From 57f573589586d103382cf90c2edbcbca8d1494c7 Mon Sep 17 00:00:00 2001 From: raffareis Date: Wed, 12 Aug 2026 04:37:35 -0300 Subject: [PATCH 01/19] security(baileys): pin rc14 compatibility baseline (xmacna/elysium#498) --- package-lock.json | 913 +++++++++++++--------------- package.json | 15 +- test/xmacna-baileys-compat.test.cjs | 109 ++++ 3 files changed, 542 insertions(+), 495 deletions(-) create mode 100644 test/xmacna-baileys-compat.test.cjs diff --git a/package-lock.json b/package-lock.json index c45e8fef38..ae02d8bc4b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ "amqplib": "^0.10.5", "audio-decode": "^2.2.3", "axios": "^1.7.9", - "baileys": "7.0.0-rc.9", + "baileys": "7.0.0-rc14", "class-validator": "^0.14.1", "compression": "^1.7.5", "cors": "^2.8.5", @@ -98,6 +98,9 @@ "tsconfig-paths": "^4.2.0", "tsx": "^4.20.5", "typescript": "^5.7.2" + }, + "engines": { + "node": ">=24.0.0 <25" } }, "node_modules/@adiwajshing/keyed-db": { @@ -829,9 +832,9 @@ } }, "node_modules/@borewit/text-codec": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.0.tgz", - "integrity": "sha512-X999CKBxGwX8wW+4gFibsbiNdwqmdQEXmUejIWaIqdrHBgS5ARIOOeyiQbHjP9G58xVEPcuvP6VwwH3A0OFTOA==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz", + "integrity": "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==", "license": "MIT", "funding": { "type": "github", @@ -2372,17 +2375,17 @@ } }, "node_modules/@jimp/core": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/core/-/core-1.6.0.tgz", - "integrity": "sha512-EQQlKU3s9QfdJqiSrZWNTxBs3rKXgO2W+GxNXDtwchF3a4IqxDheFX1ti+Env9hdJXDiYLp2jTRjlxhPthsk8w==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/core/-/core-1.6.1.tgz", + "integrity": "sha512-+BoKC5G6hkrSy501zcJ2EpfnllP+avPevcBfRcZe/CW+EwEfY6X1EZ8QWyT7NpDIvEEJb1fdJnMMfUnFkxmw9A==", "license": "MIT", "dependencies": { - "@jimp/file-ops": "1.6.0", - "@jimp/types": "1.6.0", - "@jimp/utils": "1.6.0", + "@jimp/file-ops": "1.6.1", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", "await-to-js": "^3.0.0", "exif-parser": "^0.1.12", - "file-type": "^16.0.0", + "file-type": "^21.3.3", "mime": "3" }, "engines": { @@ -2402,14 +2405,14 @@ } }, "node_modules/@jimp/diff": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/diff/-/diff-1.6.0.tgz", - "integrity": "sha512-+yUAQ5gvRC5D1WHYxjBHZI7JBRusGGSLf8AmPRPCenTzh4PA+wZ1xv2+cYqQwTfQHU5tXYOhA0xDytfHUf1Zyw==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/diff/-/diff-1.6.1.tgz", + "integrity": "sha512-YkKDPdHjLgo1Api3+Bhc0GLAygldlpt97NfOKoNg1U6IUNXA6X2MgosCjPfSBiSvJvrrz1fsIR+/4cfYXBI/HQ==", "license": "MIT", "dependencies": { - "@jimp/plugin-resize": "1.6.0", - "@jimp/types": "1.6.0", - "@jimp/utils": "1.6.0", + "@jimp/plugin-resize": "1.6.1", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", "pixelmatch": "^5.3.0" }, "engines": { @@ -2417,23 +2420,23 @@ } }, "node_modules/@jimp/file-ops": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/file-ops/-/file-ops-1.6.0.tgz", - "integrity": "sha512-Dx/bVDmgnRe1AlniRpCKrGRm5YvGmUwbDzt+MAkgmLGf+jvBT75hmMEZ003n9HQI/aPnm/YKnXjg/hOpzNCpHQ==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/file-ops/-/file-ops-1.6.1.tgz", + "integrity": "sha512-T+gX6osHjprbDRad0/B71Evyre7ZdVY1z/gFGEG9Z8KOtZPKboWvPeP2UjbZYWQLy9UKCPQX1FNAnDiOPkJL7w==", "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/@jimp/js-bmp": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/js-bmp/-/js-bmp-1.6.0.tgz", - "integrity": "sha512-FU6Q5PC/e3yzLyBDXupR3SnL3htU7S3KEs4e6rjDP6gNEOXRFsWs6YD3hXuXd50jd8ummy+q2WSwuGkr8wi+Gw==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/js-bmp/-/js-bmp-1.6.1.tgz", + "integrity": "sha512-xzWzNT4/u5zGrTT3Tme9sGU7YzIKxi13+BCQwLqACbt5DXf9SAfdzRkopZQnmDko+6In5nqaT89Gjs43/WdnYQ==", "license": "MIT", "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/types": "1.6.0", - "@jimp/utils": "1.6.0", + "@jimp/core": "1.6.1", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", "bmp-ts": "^1.0.9" }, "engines": { @@ -2441,13 +2444,13 @@ } }, "node_modules/@jimp/js-gif": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/js-gif/-/js-gif-1.6.0.tgz", - "integrity": "sha512-N9CZPHOrJTsAUoWkWZstLPpwT5AwJ0wge+47+ix3++SdSL/H2QzyMqxbcDYNFe4MoI5MIhATfb0/dl/wmX221g==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/js-gif/-/js-gif-1.6.1.tgz", + "integrity": "sha512-YjY2W26rQa05XhanYhRZ7dingCiNN+T2Ymb1JiigIbABY0B28wHE3v3Cf1/HZPWGu0hOg36ylaKgV5KxF2M58w==", "license": "MIT", "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/types": "1.6.0", + "@jimp/core": "1.6.1", + "@jimp/types": "1.6.1", "gifwrap": "^0.10.1", "omggif": "^1.0.10" }, @@ -2456,13 +2459,13 @@ } }, "node_modules/@jimp/js-jpeg": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/js-jpeg/-/js-jpeg-1.6.0.tgz", - "integrity": "sha512-6vgFDqeusblf5Pok6B2DUiMXplH8RhIKAryj1yn+007SIAQ0khM1Uptxmpku/0MfbClx2r7pnJv9gWpAEJdMVA==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/js-jpeg/-/js-jpeg-1.6.1.tgz", + "integrity": "sha512-HT9H3yOmlOFzYmdI15IYdfy6ggQhSRIaHeA+OTJSEORXBqEo97sUZu/DsgHIcX5NJ7TkJBTgZ9BZXsV6UbsyMg==", "license": "MIT", "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/types": "1.6.0", + "@jimp/core": "1.6.1", + "@jimp/types": "1.6.1", "jpeg-js": "^0.4.4" }, "engines": { @@ -2470,13 +2473,13 @@ } }, "node_modules/@jimp/js-png": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/js-png/-/js-png-1.6.0.tgz", - "integrity": "sha512-AbQHScy3hDDgMRNfG0tPjL88AV6qKAILGReIa3ATpW5QFjBKpisvUaOqhzJ7Reic1oawx3Riyv152gaPfqsBVg==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/js-png/-/js-png-1.6.1.tgz", + "integrity": "sha512-SZ/KVhI5UjcSzzlXsXdIi/LhJ7UShf2NkMOtVrbZQcGzsqNtynAelrOXeoTxcanfVqmNhAoVHg8yR2cYoqrYjA==", "license": "MIT", "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/types": "1.6.0", + "@jimp/core": "1.6.1", + "@jimp/types": "1.6.1", "pngjs": "^7.0.0" }, "engines": { @@ -2484,13 +2487,13 @@ } }, "node_modules/@jimp/js-tiff": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/js-tiff/-/js-tiff-1.6.0.tgz", - "integrity": "sha512-zhReR8/7KO+adijj3h0ZQUOiun3mXUv79zYEAKvE0O+rP7EhgtKvWJOZfRzdZSNv0Pu1rKtgM72qgtwe2tFvyw==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/js-tiff/-/js-tiff-1.6.1.tgz", + "integrity": "sha512-jDG/eJquID1M4MBlKMmDRBmz2TpXMv7TUyu2nIRUxhlUc2ogC82T+VQUkca9GJH1BBJ9dx5sSE5dGkWNjIbZxw==", "license": "MIT", "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/types": "1.6.0", + "@jimp/core": "1.6.1", + "@jimp/types": "1.6.1", "utif2": "^4.1.0" }, "engines": { @@ -2498,13 +2501,13 @@ } }, "node_modules/@jimp/plugin-blit": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-1.6.0.tgz", - "integrity": "sha512-M+uRWl1csi7qilnSK8uxK4RJMSuVeBiO1AY0+7APnfUbQNZm6hCe0CCFv1Iyw1D/Dhb8ph8fQgm5mwM0eSxgVA==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-1.6.1.tgz", + "integrity": "sha512-MwnI7C7K81uWddY9FLw1fCOIy6SsPIUftUz36Spt7jisCn8/40DhQMlSxpxTNelnZb/2SnloFimQfRZAmHLOqQ==", "license": "MIT", "dependencies": { - "@jimp/types": "1.6.0", - "@jimp/utils": "1.6.0", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", "zod": "^3.23.8" }, "engines": { @@ -2512,25 +2515,25 @@ } }, "node_modules/@jimp/plugin-blur": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-1.6.0.tgz", - "integrity": "sha512-zrM7iic1OTwUCb0g/rN5y+UnmdEsT3IfuCXCJJNs8SZzP0MkZ1eTvuwK9ZidCuMo4+J3xkzCidRwYXB5CyGZTw==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-1.6.1.tgz", + "integrity": "sha512-lIo7Tzp5jQu30EFFSK/phXANK3citKVEjepDjQ6ljHoIFtuMRrnybnmI2Md24ulvWlDaz+hh3n6qrMb8ydwhZQ==", "license": "MIT", "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/utils": "1.6.0" + "@jimp/core": "1.6.1", + "@jimp/utils": "1.6.1" }, "engines": { "node": ">=18" } }, "node_modules/@jimp/plugin-circle": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-circle/-/plugin-circle-1.6.0.tgz", - "integrity": "sha512-xt1Gp+LtdMKAXfDp3HNaG30SPZW6AQ7dtAtTnoRKorRi+5yCJjKqXRgkewS5bvj8DEh87Ko1ydJfzqS3P2tdWw==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-circle/-/plugin-circle-1.6.1.tgz", + "integrity": "sha512-kK1PavY6cKHNNKce37vdV4Tmpc1/zDKngGoeOV3j+EMatoHFZUinV3s6F9aWryPs3A0xhCLZgdJ6Zeea1d5LCQ==", "license": "MIT", "dependencies": { - "@jimp/types": "1.6.0", + "@jimp/types": "1.6.1", "zod": "^3.23.8" }, "engines": { @@ -2538,14 +2541,14 @@ } }, "node_modules/@jimp/plugin-color": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-1.6.0.tgz", - "integrity": "sha512-J5q8IVCpkBsxIXM+45XOXTrsyfblyMZg3a9eAo0P7VPH4+CrvyNQwaYatbAIamSIN1YzxmO3DkIZXzRjFSz1SA==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-1.6.1.tgz", + "integrity": "sha512-LtUN1vAP+LRlZAtTNVhDRSiXx+26Kbz3zJaG6a5k59gQ95jgT5mknnF8lxkHcqJthM4MEk3/tPxkdJpEybyF/A==", "license": "MIT", "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/types": "1.6.0", - "@jimp/utils": "1.6.0", + "@jimp/core": "1.6.1", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", "tinycolor2": "^1.6.0", "zod": "^3.23.8" }, @@ -2554,16 +2557,16 @@ } }, "node_modules/@jimp/plugin-contain": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-1.6.0.tgz", - "integrity": "sha512-oN/n+Vdq/Qg9bB4yOBOxtY9IPAtEfES8J1n9Ddx+XhGBYT1/QTU/JYkGaAkIGoPnyYvmLEDqMz2SGihqlpqfzQ==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-1.6.1.tgz", + "integrity": "sha512-m0qhrfA8jkTqretGv4w+T/ADFR4GwBpE0sCOC2uJ0dzr44/ddOMsIdrpi89kabqYiPYIrxkgdCVCLm3zn1Vkkg==", "license": "MIT", "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/plugin-blit": "1.6.0", - "@jimp/plugin-resize": "1.6.0", - "@jimp/types": "1.6.0", - "@jimp/utils": "1.6.0", + "@jimp/core": "1.6.1", + "@jimp/plugin-blit": "1.6.1", + "@jimp/plugin-resize": "1.6.1", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", "zod": "^3.23.8" }, "engines": { @@ -2571,15 +2574,15 @@ } }, "node_modules/@jimp/plugin-cover": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-1.6.0.tgz", - "integrity": "sha512-Iow0h6yqSC269YUJ8HC3Q/MpCi2V55sMlbkkTTx4zPvd8mWZlC0ykrNDeAy9IJegrQ7v5E99rJwmQu25lygKLA==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-1.6.1.tgz", + "integrity": "sha512-hZytnsth0zoll6cPf434BrT+p/v569Wr5tyO6Dp0dH1IDPhzhB5F38sZGMLDo7bzQiN9JFVB3fxkcJ/WYCJ3Mg==", "license": "MIT", "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/plugin-crop": "1.6.0", - "@jimp/plugin-resize": "1.6.0", - "@jimp/types": "1.6.0", + "@jimp/core": "1.6.1", + "@jimp/plugin-crop": "1.6.1", + "@jimp/plugin-resize": "1.6.1", + "@jimp/types": "1.6.1", "zod": "^3.23.8" }, "engines": { @@ -2587,14 +2590,14 @@ } }, "node_modules/@jimp/plugin-crop": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-1.6.0.tgz", - "integrity": "sha512-KqZkEhvs+21USdySCUDI+GFa393eDIzbi1smBqkUPTE+pRwSWMAf01D5OC3ZWB+xZsNla93BDS9iCkLHA8wang==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-1.6.1.tgz", + "integrity": "sha512-EerRSLlclXyKDnYc/H9w/1amZW7b7v3OGi/VlerPd2M/pAu5X8TkyYWtfqYCXnNp1Ixtd8oCo9zGfY9zoXT4rg==", "license": "MIT", "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/types": "1.6.0", - "@jimp/utils": "1.6.0", + "@jimp/core": "1.6.1", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", "zod": "^3.23.8" }, "engines": { @@ -2602,13 +2605,13 @@ } }, "node_modules/@jimp/plugin-displace": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-1.6.0.tgz", - "integrity": "sha512-4Y10X9qwr5F+Bo5ME356XSACEF55485j5nGdiyJ9hYzjQP9nGgxNJaZ4SAOqpd+k5sFaIeD7SQ0Occ26uIng5Q==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-1.6.1.tgz", + "integrity": "sha512-K07QVl7xQwIfD6KfxRV/c3E9e7ZBXxUXdWuvoTWcKHL2qV48MOF5Nqbz/aJW4ThnQARIsxvYlZjPFiqkCjlU+g==", "license": "MIT", "dependencies": { - "@jimp/types": "1.6.0", - "@jimp/utils": "1.6.0", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", "zod": "^3.23.8" }, "engines": { @@ -2616,25 +2619,25 @@ } }, "node_modules/@jimp/plugin-dither": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-1.6.0.tgz", - "integrity": "sha512-600d1RxY0pKwgyU0tgMahLNKsqEcxGdbgXadCiVCoGd6V6glyCvkNrnnwC0n5aJ56Htkj88PToSdF88tNVZEEQ==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-1.6.1.tgz", + "integrity": "sha512-+2V+GCV2WycMoX1/z977TkZ8Zq/4MVSKElHYatgUqtwXMi2fDK2gKYU2g9V39IqFvTJsTIsK0+58VFz/ROBVew==", "license": "MIT", "dependencies": { - "@jimp/types": "1.6.0" + "@jimp/types": "1.6.1" }, "engines": { "node": ">=18" } }, "node_modules/@jimp/plugin-fisheye": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-fisheye/-/plugin-fisheye-1.6.0.tgz", - "integrity": "sha512-E5QHKWSCBFtpgZarlmN3Q6+rTQxjirFqo44ohoTjzYVrDI6B6beXNnPIThJgPr0Y9GwfzgyarKvQuQuqCnnfbA==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-fisheye/-/plugin-fisheye-1.6.1.tgz", + "integrity": "sha512-XtS5ZyoZ0vxZxJ6gkqI63SivhtI58vX95foMPM+cyzYkRsJXMOYCr8DScxF5bp4Xr003NjYm/P+7+08tibwzHA==", "license": "MIT", "dependencies": { - "@jimp/types": "1.6.0", - "@jimp/utils": "1.6.0", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", "zod": "^3.23.8" }, "engines": { @@ -2642,12 +2645,12 @@ } }, "node_modules/@jimp/plugin-flip": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-1.6.0.tgz", - "integrity": "sha512-/+rJVDuBIVOgwoyVkBjUFHtP+wmW0r+r5OQ2GpatQofToPVbJw1DdYWXlwviSx7hvixTWLKVgRWQ5Dw862emDg==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-1.6.1.tgz", + "integrity": "sha512-ws38W/sGj7LobNRayQ83garxiktOyWxM5vO/y4a/2cy9v65SLEUzVkrj+oeAaUSSObdz4HcCEla7XtGlnAGAaA==", "license": "MIT", "dependencies": { - "@jimp/types": "1.6.0", + "@jimp/types": "1.6.1", "zod": "^3.23.8" }, "engines": { @@ -2655,20 +2658,20 @@ } }, "node_modules/@jimp/plugin-hash": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-hash/-/plugin-hash-1.6.0.tgz", - "integrity": "sha512-wWzl0kTpDJgYVbZdajTf+4NBSKvmI3bRI8q6EH9CVeIHps9VWVsUvEyb7rpbcwVLWYuzDtP2R0lTT6WeBNQH9Q==", - "license": "MIT", - "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/js-bmp": "1.6.0", - "@jimp/js-jpeg": "1.6.0", - "@jimp/js-png": "1.6.0", - "@jimp/js-tiff": "1.6.0", - "@jimp/plugin-color": "1.6.0", - "@jimp/plugin-resize": "1.6.0", - "@jimp/types": "1.6.0", - "@jimp/utils": "1.6.0", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-hash/-/plugin-hash-1.6.1.tgz", + "integrity": "sha512-sZt6ZcMX6i8vFWb4GYnw0pR/o9++ef0dTVcboTB5B/g7nrxCODIB4wfEkJ/YqZM5wUvol77K1qeS0/rVO6z21A==", + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.1", + "@jimp/js-bmp": "1.6.1", + "@jimp/js-jpeg": "1.6.1", + "@jimp/js-png": "1.6.1", + "@jimp/js-tiff": "1.6.1", + "@jimp/plugin-color": "1.6.1", + "@jimp/plugin-resize": "1.6.1", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", "any-base": "^1.1.0" }, "engines": { @@ -2676,12 +2679,12 @@ } }, "node_modules/@jimp/plugin-mask": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-1.6.0.tgz", - "integrity": "sha512-Cwy7ExSJMZszvkad8NV8o/Z92X2kFUFM8mcDAhNVxU0Q6tA0op2UKRJY51eoK8r6eds/qak3FQkXakvNabdLnA==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-1.6.1.tgz", + "integrity": "sha512-SIG0/FcmEj3tkwFxc7fAGLO8o4uNzMpSOdQOhbCgxefQKq5wOVMk9BQx/sdMPBwtMLr9WLq0GzLA/rk6t2v20A==", "license": "MIT", "dependencies": { - "@jimp/types": "1.6.0", + "@jimp/types": "1.6.1", "zod": "^3.23.8" }, "engines": { @@ -2689,16 +2692,16 @@ } }, "node_modules/@jimp/plugin-print": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-1.6.0.tgz", - "integrity": "sha512-zarTIJi8fjoGMSI/M3Xh5yY9T65p03XJmPsuNet19K/Q7mwRU6EV2pfj+28++2PV2NJ+htDF5uecAlnGyxFN2A==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-1.6.1.tgz", + "integrity": "sha512-BYVz/X3Xzv8XYilVeDy11NOp0h7BTDjlOtu0BekIFHP1yHVd24AXNzbOy52XlzYZWQ0Dl36HOHEpl/nSNrzc6w==", "license": "MIT", "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/js-jpeg": "1.6.0", - "@jimp/js-png": "1.6.0", - "@jimp/plugin-blit": "1.6.0", - "@jimp/types": "1.6.0", + "@jimp/core": "1.6.1", + "@jimp/js-jpeg": "1.6.1", + "@jimp/js-png": "1.6.1", + "@jimp/plugin-blit": "1.6.1", + "@jimp/types": "1.6.1", "parse-bmfont-ascii": "^1.0.6", "parse-bmfont-binary": "^1.0.6", "parse-bmfont-xml": "^1.1.6", @@ -2710,9 +2713,9 @@ } }, "node_modules/@jimp/plugin-quantize": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-quantize/-/plugin-quantize-1.6.0.tgz", - "integrity": "sha512-EmzZ/s9StYQwbpG6rUGBCisc3f64JIhSH+ncTJd+iFGtGo0YvSeMdAd+zqgiHpfZoOL54dNavZNjF4otK+mvlg==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-quantize/-/plugin-quantize-1.6.1.tgz", + "integrity": "sha512-J2En9PLURfP+vwYDtuZ9T8yBW6BWYZBScydAjRiPBmJfEhTcNQqiiQODrZf7EqbbX/Sy5H6dAeRiqkgoV9N6Ww==", "license": "MIT", "dependencies": { "image-q": "^4.0.0", @@ -2723,13 +2726,13 @@ } }, "node_modules/@jimp/plugin-resize": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-1.6.0.tgz", - "integrity": "sha512-uSUD1mqXN9i1SGSz5ov3keRZ7S9L32/mAQG08wUwZiEi5FpbV0K8A8l1zkazAIZi9IJzLlTauRNU41Mi8IF9fA==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-1.6.1.tgz", + "integrity": "sha512-CLkrtJoIz2HdWnpYiN6p8KYcPc00rCH/SUu6o+lfZL05Q4uhecJlnvXuj9x+U6mDn3ldPmJj6aZqMHuUJzdVqg==", "license": "MIT", "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/types": "1.6.0", + "@jimp/core": "1.6.1", + "@jimp/types": "1.6.1", "zod": "^3.23.8" }, "engines": { @@ -2737,16 +2740,16 @@ } }, "node_modules/@jimp/plugin-rotate": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-1.6.0.tgz", - "integrity": "sha512-JagdjBLnUZGSG4xjCLkIpQOZZ3Mjbg8aGCCi4G69qR+OjNpOeGI7N2EQlfK/WE8BEHOW5vdjSyglNqcYbQBWRw==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-1.6.1.tgz", + "integrity": "sha512-nOjVjbbj705B02ksysKnh0POAwEBXZtJ9zQ5qC+X7Tavl3JNn+P3BzQovbBxLPSbUSld6XID9z5ijin4PtOAUg==", "license": "MIT", "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/plugin-crop": "1.6.0", - "@jimp/plugin-resize": "1.6.0", - "@jimp/types": "1.6.0", - "@jimp/utils": "1.6.0", + "@jimp/core": "1.6.1", + "@jimp/plugin-crop": "1.6.1", + "@jimp/plugin-resize": "1.6.1", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", "zod": "^3.23.8" }, "engines": { @@ -2754,16 +2757,16 @@ } }, "node_modules/@jimp/plugin-threshold": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/plugin-threshold/-/plugin-threshold-1.6.0.tgz", - "integrity": "sha512-M59m5dzLoHOVWdM41O8z9SyySzcDn43xHseOH0HavjsfQsT56GGCC4QzU1banJidbUrePhzoEdS42uFE8Fei8w==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/plugin-threshold/-/plugin-threshold-1.6.1.tgz", + "integrity": "sha512-JOKv9F8s6tnVLf4sB/2fF0F339EFnHvgEdFYugO6VhowKLsap0pEZmLyE/DlRnYtIj2RddHZVxVMp/eKJ04l2Q==", "license": "MIT", "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/plugin-color": "1.6.0", - "@jimp/plugin-hash": "1.6.0", - "@jimp/types": "1.6.0", - "@jimp/utils": "1.6.0", + "@jimp/core": "1.6.1", + "@jimp/plugin-color": "1.6.1", + "@jimp/plugin-hash": "1.6.1", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1", "zod": "^3.23.8" }, "engines": { @@ -2771,9 +2774,9 @@ } }, "node_modules/@jimp/types": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/types/-/types-1.6.0.tgz", - "integrity": "sha512-7UfRsiKo5GZTAATxm2qQ7jqmUXP0DxTArztllTcYdyw6Xi5oT4RaoXynVtCD4UyLK5gJgkZJcwonoijrhYFKfg==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/types/-/types-1.6.1.tgz", + "integrity": "sha512-leI7YbveTNi565m910XgIOwXyuu074H5qazAD1357HImJSv2hqxnWXpwxQbadGWZ7goZRYBDZy5lpqud0p7q5w==", "license": "MIT", "dependencies": { "zod": "^3.23.8" @@ -2783,12 +2786,12 @@ } }, "node_modules/@jimp/utils": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-1.6.0.tgz", - "integrity": "sha512-gqFTGEosKbOkYF/WFj26jMHOI5OH2jeP1MmC/zbK6BF6VJBf8rIC5898dPfSzZEbSA0wbbV5slbntWVc5PKLFA==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-1.6.1.tgz", + "integrity": "sha512-veFPRd93FCnS7AgmCkPgARVGoDRrJ9cm1ujuNyA+UfQ5VKbED2002sm5XfFLFwTsKC8j04heTrwe+tU1dluXOw==", "license": "MIT", "dependencies": { - "@jimp/types": "1.6.0", + "@jimp/types": "1.6.1", "tinycolor2": "^1.6.0" }, "engines": { @@ -2864,6 +2867,18 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/@nodable/entities": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-3.0.0.tgz", + "integrity": "sha512-8L9xFeTYKhm49xfIypoe2W5wV1m/3Z58kT+7kR9A8OyFxcPduI4VmxaUMQyKYrRjUoLLSXv6EKKID5Tvj9cUVw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/nodable" + } + ], + "license": "MIT" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2907,6 +2922,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=8.0.0" } @@ -2928,6 +2944,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.2.0.tgz", "integrity": "sha512-qRkLWiUEZNAmYapZ7KGS5C4OmBLcP/H2foXeOEaowYCR0wi89fHejrfYfbuLVCMLp/dWZXKvQusdbUEZjERfwQ==", "license": "Apache-2.0", + "peer": true, "engines": { "node": "^18.19.0 || >=20.6.0" }, @@ -2940,6 +2957,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.2.0.tgz", "integrity": "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, @@ -2955,6 +2973,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.204.0.tgz", "integrity": "sha512-vV5+WSxktzoMP8JoYWKeopChy6G3HKk4UQ2hESCRDUUTZqQ3+nM3u8noVG0LmNfRWwcFBnbZ71GKC7vaYYdJ1g==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@opentelemetry/api-logs": "0.204.0", "import-in-the-middle": "^1.8.1", @@ -3362,6 +3381,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz", "integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" @@ -3378,6 +3398,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.2.0.tgz", "integrity": "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", @@ -3395,6 +3416,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.38.0.tgz", "integrity": "sha512-kocjix+/sSggfJhwXqClZ3i9Y/MI0fp7b+g7kCRm6psy2dsf8uApTRclwG18h8Avm7C9+fnt+O36PspJ/OzoWg==", "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=14" } @@ -3578,25 +3600,24 @@ "license": "BSD-3-Clause" }, "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", + "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", + "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", + "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", "license": "BSD-3-Clause", "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" + "@protobufjs/aspromise": "^1.1.1" } }, "node_modules/@protobufjs/float": { @@ -3605,12 +3626,6 @@ "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", "license": "BSD-3-Clause" }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", - "license": "BSD-3-Clause" - }, "node_modules/@protobufjs/path": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", @@ -3624,9 +3639,9 @@ "license": "BSD-3-Clause" }, "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.2.tgz", + "integrity": "sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==", "license": "BSD-3-Clause" }, "node_modules/@redis/bloom": { @@ -3643,6 +3658,7 @@ "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz", "integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==", "license": "MIT", + "peer": true, "dependencies": { "cluster-key-slot": "1.1.2", "generic-pool": "3.9.0", @@ -4758,34 +4774,6 @@ "url": "https://github.com/sponsors/Borewit" } }, - "node_modules/@tokenizer/inflate/node_modules/@borewit/text-codec": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.1.1.tgz", - "integrity": "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/@tokenizer/inflate/node_modules/token-types": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.1.tgz", - "integrity": "sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ==", - "license": "MIT", - "dependencies": { - "@borewit/text-codec": "^0.1.0", - "@tokenizer/token": "^0.3.0", - "ieee754": "^1.2.1" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, "node_modules/@tokenizer/token": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", @@ -4895,12 +4883,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/long": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", - "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", - "license": "MIT" - }, "node_modules/@types/mime": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-4.0.0.tgz", @@ -4933,6 +4915,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -5108,6 +5091,7 @@ "integrity": "sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.47.0", "@typescript-eslint/types": "8.47.0", @@ -5411,6 +5395,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5550,6 +5535,18 @@ "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", "license": "MIT" }, + "node_modules/anynum": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/anynum/-/anynum-1.0.1.tgz", + "integrity": "sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/append-field": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", @@ -5758,6 +5755,7 @@ "resolved": "https://registry.npmjs.org/audio-decode/-/audio-decode-2.2.3.tgz", "integrity": "sha512-Z0lHvMayR/Pad9+O9ddzaBJE0DrhZkQlStrC1RwcAHF3AhQAsdwKHeLGK8fYKyp2DDU6xHxzGb4CLMui12yVrg==", "license": "MIT", + "peer": true, "dependencies": { "@wasm-audio-decoders/flac": "^0.2.4", "@wasm-audio-decoders/ogg-vorbis": "^0.1.15", @@ -5814,21 +5812,22 @@ } }, "node_modules/baileys": { - "version": "7.0.0-rc.9", - "resolved": "https://registry.npmjs.org/baileys/-/baileys-7.0.0-rc.9.tgz", - "integrity": "sha512-Txd2dZ9MHbojvsHckeuCnAKPO/bQjKxua/0tQSJwOKXffK5vpS82k4eA/Nb46K0cK0Bx+fyY0zhnQHYMBriQcw==", + "version": "7.0.0-rc14", + "resolved": "https://registry.npmjs.org/baileys/-/baileys-7.0.0-rc14.tgz", + "integrity": "sha512-pewtrljhWx5JTUBvvkXZz1fL3JPiwzjBsnhx/DWf2LWBx1cZNH7J/sF22xDehba5mySWUQU4a1Pwt7lWARUxaA==", "hasInstallScript": true, "license": "MIT", "dependencies": { "@cacheable/node-cache": "^1.4.0", "@hapi/boom": "^9.1.3", "async-mutex": "^0.5.0", - "libsignal": "git+https://github.com/whiskeysockets/libsignal-node.git", + "libsignal": "^6.0.0", "lru-cache": "^11.1.0", - "music-metadata": "^11.7.0", + "music-metadata": "^11.12.3", "p-queue": "^9.0.0", "pino": "^9.6", - "protobufjs": "^7.2.4", + "protobufjs": "^7.5.6", + "whatsapp-rust-bridge": "0.5.4", "ws": "^8.13.0" }, "engines": { @@ -5836,7 +5835,7 @@ }, "peerDependencies": { "audio-decode": "^2.1.3", - "jimp": "^1.6.0", + "jimp": "^1.6.1", "link-preview-js": "^3.0.0", "sharp": "*" }, @@ -5877,6 +5876,7 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, "funding": [ { "type": "github", @@ -6746,6 +6746,7 @@ "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", @@ -7636,6 +7637,7 @@ "integrity": "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==", "hasInstallScript": true, "license": "MIT", + "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -7706,6 +7708,7 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -7762,6 +7765,7 @@ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -8336,15 +8340,6 @@ "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", "license": "MIT" }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, "node_modules/exif-parser": { "version": "0.1.12", "resolved": "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz", @@ -8368,6 +8363,7 @@ "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "license": "MIT", + "peer": true, "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -8551,10 +8547,26 @@ ], "license": "BSD-3-Clause" }, + "node_modules/fast-xml-builder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.3.0.tgz", + "integrity": "sha512-F74cZEdCvuw9P41GAC3rod4X04jjWGM1JPEv/GWSqFTWLsdyMSBMBMlm9Hk3GLBgLBbdBNY8yee0pQh2RBVESQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "path-expression-matcher": "^1.6.2", + "xml-naming": "^0.3.0" + } + }, "node_modules/fast-xml-parser": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", - "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.10.1.tgz", + "integrity": "sha512-IEMIf7298kXuZSRFoGfMYrl7is8LpavODgbNz1cwIudv7KwVFnuU+UsMporfq6PD6aXSlawZlARiA3UywCTfMw==", "funding": [ { "type": "github", @@ -8563,7 +8575,12 @@ ], "license": "MIT", "dependencies": { - "strnum": "^2.1.0" + "@nodable/entities": "^3.0.0", + "fast-xml-builder": "^1.2.0", + "is-unsafe": "^2.0.0", + "path-expression-matcher": "^1.6.2", + "strnum": "^2.4.1", + "xml-naming": "^0.3.0" }, "bin": { "fxparser": "src/cli/cli.js" @@ -8629,17 +8646,18 @@ } }, "node_modules/file-type": { - "version": "16.5.4", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz", - "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==", + "version": "21.3.4", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.4.tgz", + "integrity": "sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==", "license": "MIT", "dependencies": { - "readable-web-to-node-stream": "^3.0.0", - "strtok3": "^6.2.4", - "token-types": "^4.1.1" + "@tokenizer/inflate": "^0.4.1", + "strtok3": "^10.3.4", + "token-types": "^6.1.1", + "uint8array-extras": "^1.4.0" }, "engines": { - "node": ">=10" + "node": ">=20" }, "funding": { "url": "https://github.com/sindresorhus/file-type?sponsor=1" @@ -10274,6 +10292,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-unsafe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-unsafe/-/is-unsafe-2.0.0.tgz", + "integrity": "sha512-2LdV822R+wmI86unXA93WCFpL6g+av8ynWk0nrHyJqGop5VoocYsSLFgN8jrfalT6iGeLNM4KXuVSsULP53kEA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", @@ -10351,38 +10381,39 @@ "license": "ISC" }, "node_modules/jimp": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/jimp/-/jimp-1.6.0.tgz", - "integrity": "sha512-YcwCHw1kiqEeI5xRpDlPPBGL2EOpBKLwO4yIBJcXWHPj5PnA5urGq0jbyhM5KoNpypQ6VboSoxc9D8HyfvngSg==", - "license": "MIT", - "dependencies": { - "@jimp/core": "1.6.0", - "@jimp/diff": "1.6.0", - "@jimp/js-bmp": "1.6.0", - "@jimp/js-gif": "1.6.0", - "@jimp/js-jpeg": "1.6.0", - "@jimp/js-png": "1.6.0", - "@jimp/js-tiff": "1.6.0", - "@jimp/plugin-blit": "1.6.0", - "@jimp/plugin-blur": "1.6.0", - "@jimp/plugin-circle": "1.6.0", - "@jimp/plugin-color": "1.6.0", - "@jimp/plugin-contain": "1.6.0", - "@jimp/plugin-cover": "1.6.0", - "@jimp/plugin-crop": "1.6.0", - "@jimp/plugin-displace": "1.6.0", - "@jimp/plugin-dither": "1.6.0", - "@jimp/plugin-fisheye": "1.6.0", - "@jimp/plugin-flip": "1.6.0", - "@jimp/plugin-hash": "1.6.0", - "@jimp/plugin-mask": "1.6.0", - "@jimp/plugin-print": "1.6.0", - "@jimp/plugin-quantize": "1.6.0", - "@jimp/plugin-resize": "1.6.0", - "@jimp/plugin-rotate": "1.6.0", - "@jimp/plugin-threshold": "1.6.0", - "@jimp/types": "1.6.0", - "@jimp/utils": "1.6.0" + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/jimp/-/jimp-1.6.1.tgz", + "integrity": "sha512-hNQh6rZtWfSVWSNVmvq87N5BPJsNH7k7I7qyrXf9DOma9xATQk3fsyHazCQe51nCjdkoWdTmh0vD7bjVSLoxxw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jimp/core": "1.6.1", + "@jimp/diff": "1.6.1", + "@jimp/js-bmp": "1.6.1", + "@jimp/js-gif": "1.6.1", + "@jimp/js-jpeg": "1.6.1", + "@jimp/js-png": "1.6.1", + "@jimp/js-tiff": "1.6.1", + "@jimp/plugin-blit": "1.6.1", + "@jimp/plugin-blur": "1.6.1", + "@jimp/plugin-circle": "1.6.1", + "@jimp/plugin-color": "1.6.1", + "@jimp/plugin-contain": "1.6.1", + "@jimp/plugin-cover": "1.6.1", + "@jimp/plugin-crop": "1.6.1", + "@jimp/plugin-displace": "1.6.1", + "@jimp/plugin-dither": "1.6.1", + "@jimp/plugin-fisheye": "1.6.1", + "@jimp/plugin-flip": "1.6.1", + "@jimp/plugin-hash": "1.6.1", + "@jimp/plugin-mask": "1.6.1", + "@jimp/plugin-print": "1.6.1", + "@jimp/plugin-quantize": "1.6.1", + "@jimp/plugin-resize": "1.6.1", + "@jimp/plugin-rotate": "1.6.1", + "@jimp/plugin-threshold": "1.6.1", + "@jimp/types": "1.6.1", + "@jimp/utils": "1.6.1" }, "engines": { "node": ">=18" @@ -10585,6 +10616,7 @@ "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.5.4.tgz", "integrity": "sha512-eohl3hKTiVyD1ilYdw9T0OiB4hnjef89e3dMYKz+mVKDzj+5IteTseASUsOB+EU9Tf6VNTCjDePcP6wkDGmLKQ==", "license": "MIT", + "peer": true, "dependencies": { "@keyv/serialize": "^1.1.1" } @@ -10610,51 +10642,13 @@ "license": "MIT" }, "node_modules/libsignal": { - "name": "@whiskeysockets/libsignal-node", - "version": "2.0.1", - "resolved": "git+ssh://git@github.com/whiskeysockets/libsignal-node.git#1c30d7d7e76a3b0aa120b04dc6a26f5a12dccf67", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/libsignal/-/libsignal-6.0.0.tgz", + "integrity": "sha512-d/5V3YFtDljbFMufz4ncyUYGYhJl+vzAe+c2EFFBQ6bz1h8Q3IOMEGXYMzlibU60I+e8GagMMpji18iez3P1hA==", "license": "GPL-3.0", "dependencies": { "curve25519-js": "^0.0.4", - "protobufjs": "6.8.8" - } - }, - "node_modules/libsignal/node_modules/@types/node": { - "version": "10.17.60", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", - "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", - "license": "MIT" - }, - "node_modules/libsignal/node_modules/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", - "license": "Apache-2.0" - }, - "node_modules/libsignal/node_modules/protobufjs": { - "version": "6.8.8", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz", - "integrity": "sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw==", - "hasInstallScript": true, - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/long": "^4.0.0", - "@types/node": "^10.1.0", - "long": "^4.0.0" - }, - "bin": { - "pbjs": "bin/pbjs", - "pbts": "bin/pbts" + "protobufjs": "^7.5.5" } }, "node_modules/lilconfig": { @@ -10680,6 +10674,7 @@ "resolved": "https://registry.npmjs.org/link-preview-js/-/link-preview-js-3.2.0.tgz", "integrity": "sha512-FvrLltjOPGbTzt+RugbzM7g8XuUNLPO2U/INSLczrYdAA32E7nZVUrVL1gr61DGOArGJA2QkPGMEvNMLLsXREA==", "license": "MIT", + "peer": true, "dependencies": { "cheerio": "1.0.0-rc.11", "url": "0.11.0" @@ -11232,12 +11227,16 @@ } }, "node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-2.0.0.tgz", + "integrity": "sha512-kOy3OxT2HH39N70UnKgu4NWDZjLOz8W/mfyvniHjRH/DrL3f2pOfvWQ4p60offbbtDAnXWp0v9LfMIqMec269Q==", "license": "MIT", "engines": { - "node": ">= 0.8" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/mediainfo.js": { @@ -11542,9 +11541,9 @@ "license": "MIT" }, "node_modules/minio/node_modules/fast-xml-parser": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz", - "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==", + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.5.tgz", + "integrity": "sha512-cK9c5I/DwIOI7/Q7AlGN3DuTdwN61gwSfL8rvuVPK+0mcCNHHGxRrpiFtaZZRfRMJL3Gl8B2AFlBG6qXf03w9A==", "funding": [ { "type": "github", @@ -11553,7 +11552,7 @@ ], "license": "MIT", "dependencies": { - "strnum": "^1.1.1" + "strnum": "^1.0.5" }, "bin": { "fxparser": "src/cli/cli.js" @@ -11656,9 +11655,9 @@ } }, "node_modules/music-metadata": { - "version": "11.10.3", - "resolved": "https://registry.npmjs.org/music-metadata/-/music-metadata-11.10.3.tgz", - "integrity": "sha512-j0g/x4cNNZW6I5gdcPAY+GFkJY9WHTpkFDMBJKQLxJQyvSfQbXm57fTE3haGFFuOzCgtsTd4Plwc49Sn9RacDQ==", + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/music-metadata/-/music-metadata-11.14.0.tgz", + "integrity": "sha512-RyOSq98kuVfXB1emJ+NjBF0av8Ph3oBuqNy+Z5sFFfLhjYrkBQEB53V8u+U0RNTVwNo20WoPUwNkfKwZfrOqmQ==", "funding": [ { "type": "github", @@ -11671,80 +11670,32 @@ ], "license": "MIT", "dependencies": { - "@borewit/text-codec": "^0.2.0", + "@borewit/text-codec": "^0.2.2", "@tokenizer/token": "^0.3.0", - "content-type": "^1.0.5", + "content-type": "^2.0.0", "debug": "^4.4.3", - "file-type": "^21.1.1", - "media-typer": "^1.1.0", - "strtok3": "^10.3.4", - "token-types": "^6.1.1", - "uint8array-extras": "^1.5.0" + "file-type": "^21.3.4", + "media-typer": "^2.0.0", + "strtok3": "^10.3.5", + "token-types": "^6.1.2", + "uint8array-extras": "^1.5.0", + "win-guid": "^0.2.1" }, "engines": { "node": ">=18" } }, - "node_modules/music-metadata/node_modules/file-type": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.1.1.tgz", - "integrity": "sha512-ifJXo8zUqbQ/bLbl9sFoqHNTNWbnPY1COImFfM6CCy7z+E+jC1eY9YfOKkx0fckIg+VljAy2/87T61fp0+eEkg==", - "license": "MIT", - "dependencies": { - "@tokenizer/inflate": "^0.4.1", - "strtok3": "^10.3.4", - "token-types": "^6.1.1", - "uint8array-extras": "^1.4.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sindresorhus/file-type?sponsor=1" - } - }, - "node_modules/music-metadata/node_modules/strtok3": { - "version": "10.3.4", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.4.tgz", - "integrity": "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==", + "node_modules/music-metadata/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", "license": "MIT", - "dependencies": { - "@tokenizer/token": "^0.3.0" - }, "engines": { "node": ">=18" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/music-metadata/node_modules/token-types": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.1.tgz", - "integrity": "sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ==", - "license": "MIT", - "dependencies": { - "@borewit/text-codec": "^0.1.0", - "@tokenizer/token": "^0.3.0", - "ieee754": "^1.2.1" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/music-metadata/node_modules/token-types/node_modules/@borewit/text-codec": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.1.1.tgz", - "integrity": "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/mute-stream": { @@ -12538,6 +12489,21 @@ "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, + "node_modules/path-expression-matcher": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.6.2.tgz", + "integrity": "sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -12576,19 +12542,6 @@ "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "license": "MIT" }, - "node_modules/peek-readable": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz", - "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, "node_modules/perfect-debounce": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", @@ -12600,6 +12553,7 @@ "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", "license": "MIT", + "peer": true, "dependencies": { "pg-connection-string": "^2.9.1", "pg-pool": "^3.10.1", @@ -12909,6 +12863,7 @@ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -12938,6 +12893,7 @@ "integrity": "sha512-F3eX7K+tWpkbhl3l4+VkFtrwJlLXbAM+f9jolgoUZbFcm1DgHZ4cq9AgVEgUym2au5Ad/TDLN8lg83D+M10ycw==", "hasInstallScript": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@prisma/config": "6.19.0", "@prisma/engines": "6.19.0" @@ -12957,15 +12913,6 @@ } } }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "license": "MIT", - "engines": { - "node": ">= 0.6.0" - } - }, "node_modules/process-warning": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", @@ -12983,24 +12930,23 @@ "license": "MIT" }, "node_modules/protobufjs": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", - "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.5.tgz", + "integrity": "sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw==", "hasInstallScript": true, "license": "BSD-3-Clause", "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", + "@protobufjs/codegen": "^2.0.5", + "@protobufjs/eventemitter": "^1.1.1", + "@protobufjs/fetch": "^1.1.1", "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", + "@protobufjs/utf8": "^1.1.1", "@types/node": ">=13.7.0", - "long": "^5.0.0" + "long": "^5.3.2" }, "engines": { "node": ">=12.0.0" @@ -13411,62 +13357,6 @@ "node": ">= 6" } }, - "node_modules/readable-web-to-node-stream": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.4.tgz", - "integrity": "sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw==", - "license": "MIT", - "dependencies": { - "readable-stream": "^4.7.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/readable-web-to-node-stream/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/readable-web-to-node-stream/node_modules/readable-stream": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", - "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", - "license": "MIT", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, "node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", @@ -14029,6 +13919,7 @@ "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", "hasInstallScript": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", @@ -14176,9 +14067,9 @@ "license": "ISC" }, "node_modules/simple-xml-to-json": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/simple-xml-to-json/-/simple-xml-to-json-1.2.3.tgz", - "integrity": "sha512-kWJDCr9EWtZ+/EYYM5MareWj2cRnZGF93YDNpH4jQiHB+hBIZnfPFSQiVMzZOdk+zXWqTZ/9fTeQNu2DqeiudA==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/simple-xml-to-json/-/simple-xml-to-json-1.2.7.tgz", + "integrity": "sha512-mz9VXphOxQWX3eQ/uXCtm6upltoN0DLx8Zb5T4TFC4FHB7S9FDPGre8CfLWqPWQQH/GrQYd2AXhhVM5LDpYx6Q==", "license": "MIT", "engines": { "node": ">=20.12.2" @@ -14628,28 +14519,30 @@ } }, "node_modules/strnum": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", - "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.1.tgz", + "integrity": "sha512-M9eUSMT2dCB2cTNPG7UYj6KuK7RJR2SN2+yCV/fTW3xzTCS6EaGZ5pSMgDIjB7r8zSfTGk+dvvn9rTjpVS9Mwg==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/NaturalIntelligence" } ], - "license": "MIT" + "license": "MIT", + "dependencies": { + "anynum": "^1.0.1" + } }, "node_modules/strtok3": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz", - "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==", + "version": "10.3.5", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.5.tgz", + "integrity": "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==", "license": "MIT", "dependencies": { - "@tokenizer/token": "^0.3.0", - "peek-readable": "^4.1.0" + "@tokenizer/token": "^0.3.0" }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "type": "github", @@ -14871,6 +14764,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -14914,16 +14808,17 @@ } }, "node_modules/token-types": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz", - "integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz", + "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==", "license": "MIT", "dependencies": { + "@borewit/text-codec": "^0.2.1", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" }, "engines": { - "node": ">=10" + "node": ">=14.16" }, "funding": { "type": "github", @@ -15059,6 +14954,7 @@ "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" @@ -15707,6 +15603,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -15944,6 +15841,12 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "license": "BSD-2-Clause" }, + "node_modules/whatsapp-rust-bridge": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/whatsapp-rust-bridge/-/whatsapp-rust-bridge-0.5.5.tgz", + "integrity": "sha512-Io1qmIEi1FTXTMhN1KmZS35BHHvwRbKHUUcN/Rn6pJUU/Mk469BcWpmFEJY0a6cYu3SHwBsqzC/0IXDj+SUvgA==", + "license": "MIT" + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -16064,6 +15967,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/win-guid": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/win-guid/-/win-guid-0.2.1.tgz", + "integrity": "sha512-gEIQU4mkgl2OPeoNrWflcJFJ3Ae2BPd4eCsHHA/XikslkIVms/nHhvnvzIZV7VLmBvtFlDOzLt9rrZT+n6D67A==", + "license": "MIT" + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -16156,6 +16065,21 @@ } } }, + "node_modules/xml-naming": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.3.0.tgz", + "integrity": "sha512-ghig2TBE/H11aOVgmahA3MhimvkBr6JIYknH/Dhdk10nXwdbIqBJsbfMxpvFPG8bAw77gN29aQWvKpmVoPlvPQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/xml-parse-from-string": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz", @@ -16222,6 +16146,7 @@ "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", "devOptional": true, "license": "ISC", + "peer": true, "bin": { "yaml": "bin.mjs" }, diff --git a/package.json b/package.json index 56e32fcc8a..d49069fe99 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,9 @@ { "name": "evolution-api", "version": "2.3.7", + "engines": { + "node": ">=24.0.0 <25" + }, "description": "Rest api for communication with WhatsApp", "main": "./dist/main.js", "type": "commonjs", @@ -10,6 +13,7 @@ "start:prod": "node dist/main", "dev:server": "tsx watch ./src/main.ts", "test": "tsx watch ./test/all.test.ts", + "test:xmacna-compat": "node --test test/xmacna-baileys-compat.test.cjs", "lint": "eslint --fix --ext .ts src", "lint:check": "eslint --ext .ts src", "commit": "cz", @@ -77,7 +81,7 @@ "amqplib": "^0.10.5", "audio-decode": "^2.2.3", "axios": "^1.7.9", - "baileys": "7.0.0-rc.9", + "baileys": "7.0.0-rc14", "class-validator": "^0.14.1", "compression": "^1.7.5", "cors": "^2.8.5", @@ -126,6 +130,15 @@ "undici": "^7.16.0", "uuid": "^13.0.0" }, + "overrides": { + "whatsapp-rust-bridge": "0.5.5", + "@aws-sdk/xml-builder": { + "fast-xml-parser": "5.10.1" + }, + "minio": { + "fast-xml-parser": "4.5.5" + } + }, "devDependencies": { "@commitlint/cli": "^19.8.1", "@commitlint/config-conventional": "^19.8.1", diff --git a/test/xmacna-baileys-compat.test.cjs b/test/xmacna-baileys-compat.test.cjs new file mode 100644 index 0000000000..b42f227c4c --- /dev/null +++ b/test/xmacna-baileys-compat.test.cjs @@ -0,0 +1,109 @@ +const assert = require('node:assert/strict'); +const fs = require('node:fs'); +const path = require('node:path'); +const test = require('node:test'); + +const root = path.resolve(__dirname, '..'); + +function read(relativePath) { + return fs.readFileSync(path.join(root, relativePath), 'utf8'); +} + +function readJson(relativePath) { + return JSON.parse(read(relativePath)); +} + +test('pins the audited Baileys and CommonJS bridge versions', () => { + const baileysPackage = readJson('node_modules/baileys/package.json'); + const bridgePackage = readJson('node_modules/whatsapp-rust-bridge/package.json'); + + assert.equal(baileysPackage.version, '7.0.0-rc14'); + assert.equal(bridgePackage.version, '0.5.5'); + + const baileys = require('baileys'); + const bridge = require('whatsapp-rust-bridge'); + assert.equal(typeof baileys.makeWASocket, 'function'); + assert.equal(typeof baileys.BufferJSON, 'object'); + assert.ok(baileys.WAProto?.Message); + assert.ok(Object.keys(bridge).length > 0); +}); + +test('round-trips typed auth material with BufferJSON', () => { + const { BufferJSON } = require('baileys'); + const encoded = JSON.stringify({ key: Uint8Array.from([0, 1, 127, 255]) }, BufferJSON.replacer); + const decoded = JSON.parse(encoded, BufferJSON.reviver); + + assert.deepEqual(Array.from(decoded.key), [0, 1, 127, 255]); +}); + +test('contains the upstream CTWA placeholder recovery path', () => { + const source = read('node_modules/baileys/lib/Socket/messages-recv.js'); + + assert.match(source, /NO_MESSAGE_FOUND_ERROR_TEXT/); + assert.match(source, /requestPlaceholderResend\(cleanKey, msgData\)/); + assert.match(source, /fall through to upsertMessage so the stub is emitted/); + assert.match(source, /messageStubParameters: \[NO_MESSAGE_FOUND_ERROR_TEXT, requestId\]/); +}); + +test('contains the GHSA self-only protocol guard without blocking legitimate cross-user events', () => { + const source = read('node_modules/baileys/lib/Utils/process-message.js'); + const guardStart = source.indexOf('const SELF_ONLY_TYPES = new Set(['); + const switchStart = source.indexOf('switch (protocolMsg.type)', guardStart); + assert.ok(guardStart >= 0 && switchStart > guardStart, 'protocol guard not found'); + + const guard = source.slice(guardStart, switchStart); + for (const type of [ + 'HISTORY_SYNC_NOTIFICATION', + 'APP_STATE_SYNC_KEY_SHARE', + 'LID_MIGRATION_MAPPING_SYNC', + 'PEER_DATA_OPERATION_REQUEST_RESPONSE_MESSAGE', + ]) { + assert.match(guard, new RegExp(`ProtocolMessage\\.Type\\.${type}`)); + } + for (const type of ['REVOKE', 'MESSAGE_EDIT', 'EPHEMERAL_SETTING', 'GROUP_MEMBER_LABEL_CHANGE']) { + assert.doesNotMatch(guard, new RegExp(`ProtocolMessage\\.Type\\.${type}[,\\n]`)); + assert.match(source.slice(switchStart), new RegExp(`case proto\\.Message\\.ProtocolMessage\\.Type\\.${type}:`)); + } + assert.match(guard, /SELF_ONLY_TYPES\.has\(protocolMsg\.type\)[\s\S]*!message\.key\.fromMe/); +}); + +test('contains the upstream privacy-token send, persistence, and recovery paths', () => { + const send = read('node_modules/baileys/lib/Socket/messages-send.js'); + const receive = read('node_modules/baileys/lib/Socket/messages-recv.js'); + + assert.match(send, /authState\.keys\.get\('tctoken'/); + assert.match(send, /issuePrivacyTokens/); + assert.match(send, /authState\.keys\.set\(\{\s*tctoken:/); + assert.match(receive, /case 'privacy_token':/); + assert.match(receive, /pruned expired tctokens/); +}); + +test('Evolution skips rc14 CIPHERTEXT placeholders before any sink effect', () => { + const source = read('src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts'); + const handlerStart = source.indexOf("'messages.upsert': async ("); + const handlerEnd = source.indexOf("'messages.update': async", handlerStart); + assert.ok(handlerStart >= 0 && handlerEnd > handlerStart, 'messages.upsert handler not found'); + + const handler = source.slice(handlerStart, handlerEnd); + const stubGuard = handler.indexOf("'Message absent from node'"); + const firstSink = Math.min( + ...['chatwootService.eventWhatsapp', 'sendDataWebhook', 'prismaRepository.message.create'] + .map((needle) => handler.indexOf(needle)) + .filter((index) => index >= 0), + ); + + assert.ok(stubGuard >= 0, 'placeholder guard not found'); + assert.ok(firstSink > stubGuard, 'a sink effect can run before the placeholder guard'); + assert.match(handler.slice(stubGuard, firstSink), /continue;/); +}); + +test('all auth-state providers persist arbitrary rc14 key categories', () => { + for (const file of [ + 'src/utils/use-multi-file-auth-state-prisma.ts', + 'src/utils/use-multi-file-auth-state-redis-db.ts', + ]) { + const source = read(file); + assert.match(source, /for \(const category in data\)/); + assert.match(source, /`\$\{category\}-\$\{id\}`/); + } +}); From fb62d859a9da853ff8cfdec6385f35d402b4bf9c Mon Sep 17 00:00:00 2001 From: raffareis Date: Wed, 12 Aug 2026 05:30:36 -0300 Subject: [PATCH 02/19] feat(inbound): add durable receipt gate foundation (xmacna/elysium#500) --- package.json | 1 + .../migration.sql | 34 ++ prisma/mysql-schema.prisma | 30 ++ .../migration.sql | 36 ++ prisma/postgresql-schema.prisma | 30 ++ prisma/psql_bouncer-schema.prisma | 30 ++ src/api/dto/instance.dto.ts | 1 + .../whatsapp/baileysMessage.processor.ts | 107 +++--- .../channel/whatsapp/inboundInbox.ts | 355 ++++++++++++++++++ .../whatsapp/whatsapp.baileys.service.ts | 186 ++++++--- .../chatwoot/services/chatwoot.service.ts | 10 +- .../event/webhook/webhook.controller.ts | 18 +- src/api/services/channel.service.ts | 1 + src/api/services/monitor.service.ts | 4 + src/api/types/wa.types.ts | 1 + src/config/env.config.ts | 17 + test/xmacna-baileys-message-processor.test.ts | 65 ++++ test/xmacna-inbound-inbox.test.ts | 65 ++++ 18 files changed, 886 insertions(+), 105 deletions(-) create mode 100644 prisma/mysql-migrations/20260812080000_add_durable_inbound_inbox/migration.sql create mode 100644 prisma/postgresql-migrations/20260812080000_add_durable_inbound_inbox/migration.sql create mode 100644 src/api/integrations/channel/whatsapp/inboundInbox.ts create mode 100644 test/xmacna-baileys-message-processor.test.ts create mode 100644 test/xmacna-inbound-inbox.test.ts diff --git a/package.json b/package.json index d49069fe99..c3e889d7c7 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "dev:server": "tsx watch ./src/main.ts", "test": "tsx watch ./test/all.test.ts", "test:xmacna-compat": "node --test test/xmacna-baileys-compat.test.cjs", + "test:xmacna-inbox": "tsx --test test/xmacna-inbound-inbox.test.ts test/xmacna-baileys-message-processor.test.ts", "lint": "eslint --fix --ext .ts src", "lint:check": "eslint --ext .ts src", "commit": "cz", diff --git a/prisma/mysql-migrations/20260812080000_add_durable_inbound_inbox/migration.sql b/prisma/mysql-migrations/20260812080000_add_durable_inbound_inbox/migration.sql new file mode 100644 index 0000000000..9710f6867c --- /dev/null +++ b/prisma/mysql-migrations/20260812080000_add_durable_inbound_inbox/migration.sql @@ -0,0 +1,34 @@ +ALTER TABLE `Instance` +ADD COLUMN `inboundInboxMode` VARCHAR(20) NOT NULL DEFAULT 'off'; + +CREATE TABLE `InboundReceipt` ( + `id` VARCHAR(191) NOT NULL, + `sourceCluster` VARCHAR(64) NOT NULL, + `instanceScope` VARCHAR(255) NOT NULL, + `messageId` VARCHAR(255) NOT NULL, + `classification` VARCHAR(20) NOT NULL, + `payloadHash` CHAR(64) NOT NULL, + `collisionHash` CHAR(64) NULL, + `state` VARCHAR(30) NOT NULL DEFAULT 'received', + `webhookState` VARCHAR(30) NOT NULL DEFAULT 'pending', + `chatwootState` VARCHAR(30) NOT NULL DEFAULT 'pending', + `chatbotState` VARCHAR(30) NOT NULL DEFAULT 'pending', + `attempts` INTEGER NOT NULL DEFAULT 0, + `availableAt` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `leaseOwner` VARCHAR(100) NULL, + `leaseToken` INTEGER NOT NULL DEFAULT 0, + `leaseExpiresAt` TIMESTAMP NULL, + `lastError` TEXT NULL, + `createdAt` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updatedAt` TIMESTAMP NOT NULL, + `messageRecordId` VARCHAR(191) NULL, + UNIQUE INDEX `InboundReceipt_sourceCluster_instanceScope_messageId_key` (`sourceCluster`, `instanceScope`, `messageId`), + UNIQUE INDEX `InboundReceipt_messageRecordId_key` (`messageRecordId`), + INDEX `InboundReceipt_state_availableAt_idx` (`state`, `availableAt`), + INDEX `InboundReceipt_instanceScope_createdAt_idx` (`instanceScope`, `createdAt`), + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +ALTER TABLE `InboundReceipt` +ADD CONSTRAINT `InboundReceipt_messageRecordId_fkey` +FOREIGN KEY (`messageRecordId`) REFERENCES `Message`(`id`) ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/mysql-schema.prisma b/prisma/mysql-schema.prisma index 71b5a743f0..54de1aa19d 100644 --- a/prisma/mysql-schema.prisma +++ b/prisma/mysql-schema.prisma @@ -111,6 +111,7 @@ model Instance { Evoai Evoai[] EvoaiSetting EvoaiSetting? Pusher Pusher? + inboundInboxMode String @default("off") @db.VarChar(20) } model Session { @@ -172,6 +173,7 @@ model Message { typebotSessionId String? MessageUpdate MessageUpdate[] Media Media? + InboundReceipt InboundReceipt? webhookUrl String? @db.VarChar(500) status String? @db.VarChar(30) @@ -181,6 +183,34 @@ model Message { @@index([instanceId]) } +model InboundReceipt { + id String @id @default(cuid()) + sourceCluster String @db.VarChar(64) + instanceScope String @db.VarChar(255) + messageId String @db.VarChar(255) + classification String @db.VarChar(20) + payloadHash String @db.Char(64) + collisionHash String? @db.Char(64) + state String @default("received") @db.VarChar(30) + webhookState String @default("pending") @db.VarChar(30) + chatwootState String @default("pending") @db.VarChar(30) + chatbotState String @default("pending") @db.VarChar(30) + attempts Int @default(0) + availableAt DateTime @default(now()) @db.Timestamp + leaseOwner String? @db.VarChar(100) + leaseToken Int @default(0) + leaseExpiresAt DateTime? @db.Timestamp + lastError String? @db.Text + createdAt DateTime @default(dbgenerated("CURRENT_TIMESTAMP")) @db.Timestamp + updatedAt DateTime @updatedAt @db.Timestamp + Message Message? @relation(fields: [messageRecordId], references: [id], onDelete: SetNull) + messageRecordId String? @unique + + @@unique([sourceCluster, instanceScope, messageId]) + @@index([state, availableAt]) + @@index([instanceScope, createdAt]) +} + model MessageUpdate { id String @id @default(cuid()) keyId String @db.VarChar(100) diff --git a/prisma/postgresql-migrations/20260812080000_add_durable_inbound_inbox/migration.sql b/prisma/postgresql-migrations/20260812080000_add_durable_inbound_inbox/migration.sql new file mode 100644 index 0000000000..c9a7a5523f --- /dev/null +++ b/prisma/postgresql-migrations/20260812080000_add_durable_inbound_inbox/migration.sql @@ -0,0 +1,36 @@ +ALTER TABLE "Instance" +ADD COLUMN "inboundInboxMode" VARCHAR(20) NOT NULL DEFAULT 'off'; + +CREATE TABLE "InboundReceipt" ( + "id" TEXT NOT NULL, + "sourceCluster" VARCHAR(64) NOT NULL, + "instanceScope" VARCHAR(255) NOT NULL, + "messageId" VARCHAR(255) NOT NULL, + "classification" VARCHAR(20) NOT NULL, + "payloadHash" CHAR(64) NOT NULL, + "collisionHash" CHAR(64), + "state" VARCHAR(30) NOT NULL DEFAULT 'received', + "webhookState" VARCHAR(30) NOT NULL DEFAULT 'pending', + "chatwootState" VARCHAR(30) NOT NULL DEFAULT 'pending', + "chatbotState" VARCHAR(30) NOT NULL DEFAULT 'pending', + "attempts" INTEGER NOT NULL DEFAULT 0, + "availableAt" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + "leaseOwner" VARCHAR(100), + "leaseToken" INTEGER NOT NULL DEFAULT 0, + "leaseExpiresAt" TIMESTAMP, + "lastError" TEXT, + "createdAt" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP NOT NULL, + "messageRecordId" TEXT, + CONSTRAINT "InboundReceipt_pkey" PRIMARY KEY ("id") +); + +CREATE UNIQUE INDEX "InboundReceipt_sourceCluster_instanceScope_messageId_key" +ON "InboundReceipt"("sourceCluster", "instanceScope", "messageId"); +CREATE UNIQUE INDEX "InboundReceipt_messageRecordId_key" ON "InboundReceipt"("messageRecordId"); +CREATE INDEX "InboundReceipt_state_availableAt_idx" ON "InboundReceipt"("state", "availableAt"); +CREATE INDEX "InboundReceipt_instanceScope_createdAt_idx" ON "InboundReceipt"("instanceScope", "createdAt"); + +ALTER TABLE "InboundReceipt" +ADD CONSTRAINT "InboundReceipt_messageRecordId_fkey" +FOREIGN KEY ("messageRecordId") REFERENCES "Message"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/postgresql-schema.prisma b/prisma/postgresql-schema.prisma index 6b98f88da4..620ae08c99 100644 --- a/prisma/postgresql-schema.prisma +++ b/prisma/postgresql-schema.prisma @@ -111,6 +111,7 @@ model Instance { N8nSetting N8nSetting[] Evoai Evoai[] EvoaiSetting EvoaiSetting? + inboundInboxMode String @default("off") @db.VarChar(20) } model Session { @@ -171,6 +172,7 @@ model Message { instanceId String MessageUpdate MessageUpdate[] Media Media? + InboundReceipt InboundReceipt? webhookUrl String? @db.VarChar(500) status String? @db.VarChar(30) @@ -180,6 +182,34 @@ model Message { @@index([instanceId]) } +model InboundReceipt { + id String @id @default(cuid()) + sourceCluster String @db.VarChar(64) + instanceScope String @db.VarChar(255) + messageId String @db.VarChar(255) + classification String @db.VarChar(20) + payloadHash String @db.Char(64) + collisionHash String? @db.Char(64) + state String @default("received") @db.VarChar(30) + webhookState String @default("pending") @db.VarChar(30) + chatwootState String @default("pending") @db.VarChar(30) + chatbotState String @default("pending") @db.VarChar(30) + attempts Int @default(0) + availableAt DateTime @default(now()) @db.Timestamp + leaseOwner String? @db.VarChar(100) + leaseToken Int @default(0) + leaseExpiresAt DateTime? @db.Timestamp + lastError String? @db.Text + createdAt DateTime @default(now()) @db.Timestamp + updatedAt DateTime @updatedAt @db.Timestamp + Message Message? @relation(fields: [messageRecordId], references: [id], onDelete: SetNull) + messageRecordId String? @unique + + @@unique([sourceCluster, instanceScope, messageId]) + @@index([state, availableAt]) + @@index([instanceScope, createdAt]) +} + model MessageUpdate { id String @id @default(cuid()) keyId String @db.VarChar(100) diff --git a/prisma/psql_bouncer-schema.prisma b/prisma/psql_bouncer-schema.prisma index a3f4dbe902..facf22fdac 100644 --- a/prisma/psql_bouncer-schema.prisma +++ b/prisma/psql_bouncer-schema.prisma @@ -112,6 +112,7 @@ model Instance { N8nSetting N8nSetting[] Evoai Evoai[] EvoaiSetting EvoaiSetting? + inboundInboxMode String @default("off") @db.VarChar(20) } model Session { @@ -171,6 +172,7 @@ model Message { instanceId String MessageUpdate MessageUpdate[] Media Media? + InboundReceipt InboundReceipt? webhookUrl String? @db.VarChar(500) status String? @db.VarChar(30) @@ -180,6 +182,34 @@ model Message { @@index([instanceId]) } +model InboundReceipt { + id String @id @default(cuid()) + sourceCluster String @db.VarChar(64) + instanceScope String @db.VarChar(255) + messageId String @db.VarChar(255) + classification String @db.VarChar(20) + payloadHash String @db.Char(64) + collisionHash String? @db.Char(64) + state String @default("received") @db.VarChar(30) + webhookState String @default("pending") @db.VarChar(30) + chatwootState String @default("pending") @db.VarChar(30) + chatbotState String @default("pending") @db.VarChar(30) + attempts Int @default(0) + availableAt DateTime @default(now()) @db.Timestamp + leaseOwner String? @db.VarChar(100) + leaseToken Int @default(0) + leaseExpiresAt DateTime? @db.Timestamp + lastError String? @db.Text + createdAt DateTime @default(now()) @db.Timestamp + updatedAt DateTime @updatedAt @db.Timestamp + Message Message? @relation(fields: [messageRecordId], references: [id], onDelete: SetNull) + messageRecordId String? @unique + + @@unique([sourceCluster, instanceScope, messageId]) + @@index([state, availableAt]) + @@index([instanceScope, createdAt]) +} + model MessageUpdate { id String @id @default(cuid()) keyId String @db.VarChar(100) diff --git a/src/api/dto/instance.dto.ts b/src/api/dto/instance.dto.ts index 85c5b69c33..24865f9673 100644 --- a/src/api/dto/instance.dto.ts +++ b/src/api/dto/instance.dto.ts @@ -15,6 +15,7 @@ export class InstanceDto extends IntegrationDto { connectionStatus?: string; profileName?: string; profilePicUrl?: string; + inboundInboxMode?: 'off' | 'shadow' | 'enforce'; // settings rejectCall?: boolean; msgCall?: string; diff --git a/src/api/integrations/channel/whatsapp/baileysMessage.processor.ts b/src/api/integrations/channel/whatsapp/baileysMessage.processor.ts index fbe1864a19..a944918c9d 100644 --- a/src/api/integrations/channel/whatsapp/baileysMessage.processor.ts +++ b/src/api/integrations/channel/whatsapp/baileysMessage.processor.ts @@ -1,75 +1,68 @@ import { Logger } from '@config/logger.config'; -import { BaileysEventMap, MessageUpsertType, WAMessage } from 'baileys'; -import { catchError, concatMap, delay, EMPTY, from, retryWhen, Subject, Subscription, take, tap } from 'rxjs'; +import { BaileysEventMap } from 'baileys'; type MessageUpsertPayload = BaileysEventMap['messages.upsert']; -type MountProps = { - onMessageReceive: (payload: MessageUpsertPayload, settings: any) => Promise; -}; +type MessageHandler = (payload: MessageUpsertPayload, settings: any) => Promise; +type MountProps = { onMessageReceive: MessageHandler }; export class BaileysMessageProcessor { - private processorLogs = new Logger('BaileysMessageProcessor'); - private subscription?: Subscription; + private readonly processorLogs = new Logger('BaileysMessageProcessor'); + private onMessageReceive?: MessageHandler; + private queue: Promise = Promise.resolve(); + private destroyed = false; - protected messageSubject = new Subject<{ - messages: WAMessage[]; - type: MessageUpsertType; - requestId?: string; - settings: any; - }>(); + constructor( + private readonly retryDelayMs = 1000, + private readonly maxRetries = 3, + ) {} mount({ onMessageReceive }: MountProps) { - // Se já existe subscription, fazer cleanup primeiro - if (this.subscription && !this.subscription.closed) { - this.subscription.unsubscribe(); - } + this.onMessageReceive = onMessageReceive; + this.destroyed = false; + } - // Se o Subject foi completado, recriar - if (this.messageSubject.closed) { - this.processorLogs.warn('MessageSubject was closed, recreating...'); - this.messageSubject = new Subject<{ - messages: WAMessage[]; - type: MessageUpsertType; - requestId?: string; - settings: any; - }>(); + processMessage(payload: MessageUpsertPayload, settings: any): Promise { + if (!this.onMessageReceive || this.destroyed) { + return Promise.reject(new Error('BaileysMessageProcessor is not mounted')); } + this.queue = this.queue + .catch((error) => { + this.processorLogs.error(`Recovering message queue after unexpected error: ${error}`); + }) + .then(() => this.processBatch(payload, settings)); + return this.queue; + } - this.subscription = this.messageSubject - .pipe( - tap(({ messages }) => { - this.processorLogs.log(`Processing batch of ${messages.length} messages`); - }), - concatMap(({ messages, type, requestId, settings }) => - from(onMessageReceive({ messages, type, requestId }, settings)).pipe( - retryWhen((errors) => - errors.pipe( - tap((error) => this.processorLogs.warn(`Retrying message batch due to error: ${error.message}`)), - delay(1000), // 1 segundo de delay - take(3), // Máximo 3 tentativas - ), - ), - ), - ), - catchError((error) => { - this.processorLogs.error(`Error processing message batch: ${error}`); - return EMPTY; - }), - ) - .subscribe({ - error: (error) => { - this.processorLogs.error(`Message stream error: ${error}`); - }, - }); + private async processBatch(payload: MessageUpsertPayload, settings: any): Promise { + this.processorLogs.log(`Processing batch of ${payload.messages.length} messages`); + for (const message of payload.messages) { + await this.processOne({ ...payload, messages: [message] }, settings); + } } - processMessage(payload: MessageUpsertPayload, settings: any) { - const { messages, type, requestId } = payload; - this.messageSubject.next({ messages, type, requestId, settings }); + private async processOne(payload: MessageUpsertPayload, settings: any): Promise { + const messageId = payload.messages[0]?.key?.id ?? 'without-id'; + for (let attempt = 0; attempt <= this.maxRetries; attempt += 1) { + try { + await this.onMessageReceive!(payload, settings); + return; + } catch (error) { + if (attempt >= this.maxRetries) { + this.processorLogs.error( + `Message ${messageId} exhausted ${this.maxRetries} retries without blocking the batch: ${error}`, + ); + return; + } + this.processorLogs.warn( + `Retrying message ${messageId} after attempt ${attempt + 1}: ${(error as Error).message}`, + ); + await new Promise((resolve) => setTimeout(resolve, this.retryDelayMs)); + } + } } onDestroy() { - this.subscription?.unsubscribe(); - this.messageSubject.complete(); + this.destroyed = true; + this.onMessageReceive = undefined; } } diff --git a/src/api/integrations/channel/whatsapp/inboundInbox.ts b/src/api/integrations/channel/whatsapp/inboundInbox.ts new file mode 100644 index 0000000000..d1d592f192 --- /dev/null +++ b/src/api/integrations/channel/whatsapp/inboundInbox.ts @@ -0,0 +1,355 @@ +import { PrismaRepository } from '@api/repository/repository.service'; +import { InboundInboxMode } from '@config/env.config'; +import { Prisma } from '@prisma/client'; +import { WAMessage } from 'baileys'; +import { createHash } from 'crypto'; + +export type InboundClassification = 'real' | 'stub' | 'control' | 'protocol'; +export type InboundClaimKind = 'claimed' | 'duplicate' | 'promoted' | 'collision'; + +export type InboundIdentity = { + sourceCluster: string; + instanceScope: string; + messageId: string; +}; + +export type InboundClaimInput = InboundIdentity & { + classification: InboundClassification; + payloadHash: string; + messageData?: Record; + leaseOwner?: string; + leaseSeconds?: number; +}; + +export type InboundClaimResult = { + kind: InboundClaimKind; + receiptId: string; + messageRecordId?: string; + shouldDispatch: boolean; + leaseToken?: number; +}; + +type ExistingReceipt = { + id: string; + classification: string; + payloadHash: string; + messageRecordId?: string | null; + state?: string; + leaseToken?: number; +}; + +const VOLATILE_KEYS = new Set(['timestamp', 'messagetimestamp', 'requestid', 'fromme']); + +function canonicalize(value: unknown): unknown { + if (Buffer.isBuffer(value)) return { type: 'Buffer', data: [...value] }; + if (Array.isArray(value)) return value.map(canonicalize); + if (value && typeof value === 'object') { + const source = value as Record; + return Object.keys(source) + .sort() + .reduce>((result, key) => { + const normalizedKey = key.toLowerCase(); + if (VOLATILE_KEYS.has(normalizedKey) || normalizedKey.endsWith('jid')) return result; + const normalizedValue = canonicalize(source[key]); + if (normalizedValue !== undefined) result[key] = normalizedValue; + return result; + }, {}); + } + if (typeof value === 'bigint') return value.toString(); + return value; +} + +export function normalizeInstanceScope(instanceName: string): string { + return instanceName.normalize('NFKC').trim().toLowerCase().replace(/\s+/g, '-'); +} + +export function classifyInboundMessage(received: WAMessage): InboundClassification { + const message = received?.message as Record | null | undefined; + const protocol = + message?.protocolMessage || + (message?.editedMessage as { message?: { protocolMessage?: unknown } } | undefined)?.message?.protocolMessage; + if (protocol) return 'protocol'; + + const text = (message?.conversation || (message?.extendedTextMessage as { text?: string } | undefined)?.text) as + | string + | undefined; + if (text === 'requestPlaceholder' || text === 'onDemandHistSync') return 'control'; + + if (!message && (received?.messageStubType !== undefined || received?.messageStubParameters?.length)) return 'stub'; + if ( + !message || + Object.keys(message).every((key) => key === 'messageContextInfo' || key === 'senderKeyDistributionMessage') + ) { + return 'control'; + } + return 'real'; +} + +export function inboundPayloadHash(received: WAMessage): string { + const payload = canonicalize({ + message: received.message, + messageStubType: received.messageStubType, + messageStubParameters: received.messageStubParameters, + }); + return createHash('sha256').update(JSON.stringify(payload)).digest('hex'); +} + +export function resolveInboundMode( + instanceMode: string | null | undefined, + fallback: InboundInboxMode, +): InboundInboxMode { + if (instanceMode === 'off' || instanceMode === 'shadow' || instanceMode === 'enforce') return instanceMode; + return fallback; +} + +export function evaluateExistingReceipt( + existing: ExistingReceipt, + incoming: Pick, +): InboundClaimKind { + if (existing.payloadHash === incoming.payloadHash) return 'duplicate'; + if (existing.classification === 'stub' && incoming.classification === 'real') return 'promoted'; + if (incoming.classification === 'stub') return 'duplicate'; + return 'collision'; +} + +function isUniqueConstraint(error: unknown): boolean { + return error instanceof Prisma.PrismaClientKnownRequestError && error.code === 'P2002'; +} + +export class PrismaInboundInbox { + constructor(private readonly repository: PrismaRepository) {} + + public async claim(input: InboundClaimInput, mode: InboundInboxMode): Promise { + if (mode === 'shadow') { + return this.observe(input); + } + try { + return await this.repository.$transaction( + async (tx) => { + const receipt = await tx.inboundReceipt.create({ + data: { + sourceCluster: input.sourceCluster, + instanceScope: input.instanceScope, + messageId: input.messageId, + classification: input.classification, + payloadHash: input.payloadHash, + state: input.classification === 'real' ? 'processing' : 'observed', + leaseOwner: input.classification === 'real' ? input.leaseOwner : undefined, + leaseToken: input.classification === 'real' ? 1 : 0, + leaseExpiresAt: + input.classification === 'real' ? new Date(Date.now() + (input.leaseSeconds ?? 60) * 1000) : undefined, + }, + }); + let messageRecordId: string | undefined; + if (input.classification === 'real' && input.messageData) { + const message = await tx.message.create({ data: input.messageData as any }); + messageRecordId = message.id; + await tx.inboundReceipt.update({ where: { id: receipt.id }, data: { messageRecordId } }); + } + return { + kind: 'claimed' as const, + receiptId: receipt.id, + messageRecordId, + shouldDispatch: input.classification === 'real', + leaseToken: input.classification === 'real' ? 1 : undefined, + }; + }, + { isolationLevel: Prisma.TransactionIsolationLevel.Serializable }, + ); + } catch (error) { + if (!isUniqueConstraint(error)) throw error; + } + + const key = { + sourceCluster: input.sourceCluster, + instanceScope: input.instanceScope, + messageId: input.messageId, + }; + const existing = await this.repository.inboundReceipt.findUnique({ + where: { sourceCluster_instanceScope_messageId: key }, + }); + if (!existing) throw new Error('Inbound receipt disappeared after unique conflict'); + + const kind = evaluateExistingReceipt(existing, input); + if (kind === 'duplicate') { + if (input.classification === 'real' && input.leaseOwner && existing.state !== 'done') { + const reclaimed = await this.repository.inboundReceipt.updateMany({ + where: { + id: existing.id, + OR: [{ state: 'failed' }, { leaseExpiresAt: { lt: new Date() } }], + }, + data: { + state: 'processing', + attempts: { increment: 1 }, + leaseOwner: input.leaseOwner, + leaseToken: { increment: 1 }, + leaseExpiresAt: new Date(Date.now() + (input.leaseSeconds ?? 60) * 1000), + lastError: null, + }, + }); + if (reclaimed.count === 1) { + const current = await this.repository.inboundReceipt.findUniqueOrThrow({ where: { id: existing.id } }); + let messageRecordId = current.messageRecordId ?? undefined; + if (!messageRecordId && input.messageData) { + const message = await this.repository.message.create({ data: input.messageData as any }); + messageRecordId = message.id; + await this.repository.inboundReceipt.update({ + where: { id: existing.id }, + data: { messageRecordId }, + }); + } + return { + kind: 'claimed', + receiptId: existing.id, + messageRecordId, + shouldDispatch: true, + leaseToken: current.leaseToken, + }; + } + } + return { + kind, + receiptId: existing.id, + messageRecordId: existing.messageRecordId ?? undefined, + shouldDispatch: false, + }; + } + if (kind === 'collision') { + await this.repository.inboundReceipt.update({ + where: { id: existing.id }, + data: { + state: 'quarantined', + collisionHash: input.payloadHash, + lastError: 'message_id_payload_hash_collision', + }, + }); + return { kind, receiptId: existing.id, shouldDispatch: false }; + } + + return this.repository.$transaction( + async (tx) => { + const current = await tx.inboundReceipt.findUnique({ + where: { sourceCluster_instanceScope_messageId: key }, + }); + if (!current) throw new Error('Inbound receipt disappeared during promotion'); + if (current.classification === 'real') { + const currentKind = current.payloadHash === input.payloadHash ? 'duplicate' : 'collision'; + return { + kind: currentKind, + receiptId: current.id, + messageRecordId: current.messageRecordId ?? undefined, + shouldDispatch: false, + } as InboundClaimResult; + } + + let messageRecordId = current.messageRecordId ?? undefined; + if (!messageRecordId && input.messageData) { + const message = await tx.message.create({ data: input.messageData as any }); + messageRecordId = message.id; + } + await tx.inboundReceipt.update({ + where: { id: current.id }, + data: { + classification: 'real', + payloadHash: input.payloadHash, + collisionHash: null, + state: 'processing', + messageRecordId, + leaseOwner: input.leaseOwner, + leaseToken: { increment: 1 }, + leaseExpiresAt: new Date(Date.now() + (input.leaseSeconds ?? 60) * 1000), + }, + }); + const promoted = await tx.inboundReceipt.findUniqueOrThrow({ where: { id: current.id } }); + return { + kind: 'promoted', + receiptId: current.id, + messageRecordId, + shouldDispatch: true, + leaseToken: promoted.leaseToken, + }; + }, + { isolationLevel: Prisma.TransactionIsolationLevel.Serializable }, + ); + } + + public async markSink( + receiptId: string, + sink: 'webhook' | 'chatwoot' | 'chatbot', + state: 'sent' | 'skipped', + ): Promise { + const field = `${sink}State` as 'webhookState' | 'chatwootState' | 'chatbotState'; + await this.repository.inboundReceipt.update({ where: { id: receiptId }, data: { [field]: state } }); + } + + public async markDone(receiptId: string, leaseOwner: string, leaseToken: number): Promise { + const updated = await this.repository.inboundReceipt.updateMany({ + where: { id: receiptId, leaseOwner, leaseToken, state: 'processing' }, + data: { state: 'done', leaseOwner: null, leaseExpiresAt: null, lastError: null }, + }); + return updated.count === 1; + } + + public async markFailed(receiptId: string, leaseOwner: string, leaseToken: number, error: unknown): Promise { + const updated = await this.repository.inboundReceipt.updateMany({ + where: { id: receiptId, leaseOwner, leaseToken, state: 'processing' }, + data: { + state: 'failed', + leaseOwner: null, + leaseExpiresAt: null, + lastError: String(error instanceof Error ? error.message : error).slice(0, 4000), + }, + }); + return updated.count === 1; + } + + private async observe(input: InboundClaimInput): Promise { + const key = { + sourceCluster: input.sourceCluster, + instanceScope: input.instanceScope, + messageId: input.messageId, + }; + const existing = await this.repository.inboundReceipt.findUnique({ + where: { sourceCluster_instanceScope_messageId: key }, + }); + if (!existing) { + try { + const created = await this.repository.inboundReceipt.create({ + data: { + ...key, + classification: input.classification, + payloadHash: input.payloadHash, + state: 'shadow_seen', + }, + }); + return { + kind: 'claimed', + receiptId: created.id, + shouldDispatch: input.classification === 'real', + }; + } catch (error) { + if (!isUniqueConstraint(error)) throw error; + return this.observe(input); + } + } + + const kind = evaluateExistingReceipt(existing, input); + if (kind === 'collision') { + await this.repository.inboundReceipt.update({ + where: { id: existing.id }, + data: { state: 'shadow_collision', collisionHash: input.payloadHash }, + }); + } else if (kind === 'promoted') { + await this.repository.inboundReceipt.update({ + where: { id: existing.id }, + data: { classification: 'real', payloadHash: input.payloadHash, state: 'shadow_promoted' }, + }); + } + return { + kind, + receiptId: existing.id, + messageRecordId: existing.messageRecordId ?? undefined, + shouldDispatch: input.classification === 'real', + }; + } +} diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 60e857fcc1..f167a23dd3 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -69,6 +69,7 @@ import { configService, ConfigSessionPhone, Database, + InboundInbox, Log, Openai, ProviderSession, @@ -153,6 +154,13 @@ import { PassThrough, Readable } from 'stream'; import { v4 } from 'uuid'; import { BaileysMessageProcessor } from './baileysMessage.processor'; +import { + classifyInboundMessage, + inboundPayloadHash, + normalizeInstanceScope, + PrismaInboundInbox, + resolveInboundMode, +} from './inboundInbox'; import { useVoiceCallsBaileys } from './voiceCalls/useVoiceCallsBaileys'; export interface ExtendedIMessageKey extends proto.IMessageKey { @@ -226,6 +234,7 @@ async function getVideoDuration(input: Buffer | string | Readable): Promise { + let activeReceipt: + | { id: string; leaseOwner: string; leaseToken: number; mode: 'off' | 'shadow' | 'enforce' } + | undefined; try { for (const received of messages) { + const inboundConfig = this.configService.get('INBOUND_INBOX'); + const inboundMode = resolveInboundMode(this.instance.inboundInboxMode, inboundConfig.MODE); + const inboundClassification = classifyInboundMessage(received); + const inboundMessageId = received.key?.id; + const inboundIdentity = inboundMessageId + ? { + sourceCluster: inboundConfig.SOURCE_CLUSTER, + instanceScope: normalizeInstanceScope(this.instance.name), + messageId: inboundMessageId, + } + : undefined; + + if (inboundMode !== 'off' && inboundIdentity && inboundClassification !== 'real') { + await this.inboundInbox.claim( + { + ...inboundIdentity, + classification: inboundClassification, + payloadHash: inboundPayloadHash(received), + }, + inboundMode, + ); + } if ( received?.messageStubParameters?.some?.((param) => [ @@ -1202,6 +1237,39 @@ export class BaileysStartupService extends ChannelStartupService { } const messageRaw = this.prepareMessage(received); + let durableMessageRecordId: string | undefined; + + if (inboundMode !== 'off' && inboundIdentity && inboundClassification === 'real') { + const messageData = { ...messageRaw }; + delete messageData.pollUpdates; + const leaseOwner = v4(); + const claim = await this.inboundInbox.claim( + { + ...inboundIdentity, + classification: inboundClassification, + payloadHash: inboundPayloadHash(received), + messageData, + leaseOwner, + leaseSeconds: inboundConfig.LEASE_SECONDS, + }, + inboundMode, + ); + durableMessageRecordId = claim.messageRecordId; + if (claim.leaseToken) { + activeReceipt = { + id: claim.receiptId, + leaseOwner, + leaseToken: claim.leaseToken, + mode: inboundMode, + }; + } + if (!claim.shouldDispatch) { + this.logger.info( + `Inbound ${claim.kind} suppressed before sinks: ${inboundIdentity.instanceScope}/${inboundMessageId}`, + ); + continue; + } + } if (messageRaw.messageType === 'pollUpdateMessage') { const pollCreationKey = messageRaw.message.pollUpdateMessage.pollCreationMessageKey; @@ -1339,6 +1407,9 @@ export class BaileysStartupService extends ChannelStartupService { messageRaw.chatwootInboxId = chatwootSentMessage.inbox_id; messageRaw.chatwootConversationId = chatwootSentMessage.conversation_id; } + if (activeReceipt) await this.inboundInbox.markSink(activeReceipt.id, 'chatwoot', 'sent'); + } else if (activeReceipt) { + await this.inboundInbox.markSink(activeReceipt.id, 'chatwoot', 'skipped'); } if (this.configService.get('OPENAI').ENABLED && received?.message?.audioMessage) { @@ -1355,7 +1426,9 @@ export class BaileysStartupService extends ChannelStartupService { if (this.configService.get('DATABASE').SAVE_DATA.NEW_MESSAGE) { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { pollUpdates, ...messageData } = messageRaw; - const msg = await this.prismaRepository.message.create({ data: messageData }); + const msg = durableMessageRecordId + ? await this.prismaRepository.message.findUniqueOrThrow({ where: { id: durableMessageRecordId } }) + : await this.prismaRepository.message.create({ data: messageData }); const { remoteJid } = received.key; const timestamp = msg.messageTimestamp; @@ -1389,50 +1462,49 @@ export class BaileysStartupService extends ChannelStartupService { try { if (isVideo && !this.configService.get('S3').SAVE_VIDEO) { this.logger.warn('Video upload is disabled. Skipping video upload.'); - // Skip video upload by returning early from this block - return; - } - - const message: any = received; - - // Verificação adicional para garantir que há conteúdo de mídia real - const hasRealMedia = this.hasValidMediaContent(message); - - if (!hasRealMedia) { - this.logger.warn('Message detected as media but contains no valid media content'); } else { - const media = await this.getBase64FromMediaMessage({ message }, true); - - if (!media) { - this.logger.verbose('No valid media to upload (messageContextInfo only), skipping MinIO'); - return; + const message: any = received; + + // Verificação adicional para garantir que há conteúdo de mídia real + const hasRealMedia = this.hasValidMediaContent(message); + + if (!hasRealMedia) { + this.logger.warn('Message detected as media but contains no valid media content'); + } else { + const media = await this.getBase64FromMediaMessage({ message }, true); + + if (!media) { + this.logger.verbose('No valid media to upload (messageContextInfo only), skipping MinIO'); + } else { + const { buffer, mediaType, fileName, size } = media; + const mimetype = mimeTypes.lookup(fileName).toString(); + const fullName = join( + `${this.instance.id}`, + received.key.remoteJid, + mediaType, + `${Date.now()}_${fileName}`, + ); + await s3Service.uploadFile(fullName, buffer, size.fileLength?.low, { + 'Content-Type': mimetype, + }); + + await this.prismaRepository.media.create({ + data: { + messageId: msg.id, + instanceId: this.instanceId, + type: mediaType, + fileName: fullName, + mimetype, + }, + }); + + const mediaUrl = await s3Service.getObjectUrl(fullName); + + messageRaw.message.mediaUrl = mediaUrl; + + await this.prismaRepository.message.update({ where: { id: msg.id }, data: messageRaw }); + } } - - const { buffer, mediaType, fileName, size } = media; - const mimetype = mimeTypes.lookup(fileName).toString(); - const fullName = join( - `${this.instance.id}`, - received.key.remoteJid, - mediaType, - `${Date.now()}_${fileName}`, - ); - await s3Service.uploadFile(fullName, buffer, size.fileLength?.low, { 'Content-Type': mimetype }); - - await this.prismaRepository.media.create({ - data: { - messageId: msg.id, - instanceId: this.instanceId, - type: mediaType, - fileName: fullName, - mimetype, - }, - }); - - const mediaUrl = await s3Service.getObjectUrl(fullName); - - messageRaw.message.mediaUrl = mediaUrl; - - await this.prismaRepository.message.update({ where: { id: msg.id }, data: messageRaw }); } } catch (error) { this.logger.error(['Error on upload file to minio', error?.message, error?.stack]); @@ -1480,7 +1552,8 @@ export class BaileysStartupService extends ChannelStartupService { } console.log(messageRaw); - this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw); + await this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw); + if (activeReceipt) await this.inboundInbox.markSink(activeReceipt.id, 'webhook', 'sent'); await chatbotController.emit({ instance: { instanceName: this.instance.name, instanceId: this.instanceId }, @@ -1488,6 +1561,7 @@ export class BaileysStartupService extends ChannelStartupService { msg: messageRaw, pushName: messageRaw.pushName, }); + if (activeReceipt) await this.inboundInbox.markSink(activeReceipt.id, 'chatbot', 'sent'); const contact = await this.prismaRepository.contact.findFirst({ where: { remoteJid: received.key.remoteJid, instanceId: this.instanceId }, @@ -1506,6 +1580,10 @@ export class BaileysStartupService extends ChannelStartupService { }; if (contactRaw.remoteJid === 'status@broadcast') { + if (activeReceipt) { + await this.inboundInbox.markDone(activeReceipt.id, activeReceipt.leaseOwner, activeReceipt.leaseToken); + activeReceipt = undefined; + } continue; } @@ -1538,6 +1616,10 @@ export class BaileysStartupService extends ChannelStartupService { update: contactRaw, }); + if (activeReceipt) { + await this.inboundInbox.markDone(activeReceipt.id, activeReceipt.leaseOwner, activeReceipt.leaseToken); + activeReceipt = undefined; + } continue; } @@ -1549,9 +1631,22 @@ export class BaileysStartupService extends ChannelStartupService { update: contactRaw, create: contactRaw, }); + if (activeReceipt) { + await this.inboundInbox.markDone(activeReceipt.id, activeReceipt.leaseOwner, activeReceipt.leaseToken); + activeReceipt = undefined; + } } } catch (error) { + if (activeReceipt) { + await this.inboundInbox.markFailed( + activeReceipt.id, + activeReceipt.leaseOwner, + activeReceipt.leaseToken, + error, + ); + } this.logger.error(error); + throw error; } }, @@ -1915,8 +2010,7 @@ export class BaileysStartupService extends ChannelStartupService { if (events['messages.upsert']) { const payload = events['messages.upsert']; - // this.messageProcessor.processMessage(payload, settings); - await this.messageHandle['messages.upsert'](payload, settings); + await this.messageProcessor.processMessage(payload, settings); } if (events['messages.update']) { diff --git a/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts b/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts index 906fff1881..11b8df2804 100644 --- a/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts +++ b/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts @@ -7,7 +7,7 @@ import { PrismaRepository } from '@api/repository/repository.service'; import { CacheService } from '@api/services/cache.service'; import { WAMonitoringService } from '@api/services/monitor.service'; import { Events } from '@api/types/wa.types'; -import { Chatwoot, ConfigService, Database, HttpServer } from '@config/env.config'; +import { Chatwoot, ConfigService, Database, HttpServer, InboundInbox } from '@config/env.config'; import { Logger } from '@config/logger.config'; import ChatwootClient, { ChatwootAPIConfig, @@ -2524,6 +2524,14 @@ export class ChatwootService { } } catch (error) { this.logger.error(error); + const waInstance = this.waMonitor.waInstances[instance.instanceName]; + const inboxDefault = this.configService.get('INBOUND_INBOX').MODE; + if ( + event === Events.MESSAGES_UPSERT && + (waInstance?.instance?.inboundInboxMode === 'enforce' || inboxDefault === 'enforce') + ) { + throw error; + } } } diff --git a/src/api/integrations/event/webhook/webhook.controller.ts b/src/api/integrations/event/webhook/webhook.controller.ts index 7f1dd8dc0e..a1ad492667 100644 --- a/src/api/integrations/event/webhook/webhook.controller.ts +++ b/src/api/integrations/event/webhook/webhook.controller.ts @@ -2,7 +2,7 @@ import { EventDto } from '@api/integrations/event/event.dto'; import { PrismaRepository } from '@api/repository/repository.service'; import { WAMonitoringService } from '@api/services/monitor.service'; import { wa } from '@api/types/wa.types'; -import { configService, Log, Webhook } from '@config/env.config'; +import { configService, InboundInbox, Log, Webhook } from '@config/env.config'; import { Logger } from '@config/logger.config'; // import { BadRequestException } from '@exceptions'; import axios, { AxiosInstance } from 'axios'; @@ -145,6 +145,14 @@ export class WebhookController extends EventController implements EventControlle url: baseURL, server_url: serverUrl, }); + const waInstance = this.monitor.waInstances[instanceName]; + const inboxDefault = configService.get('INBOUND_INBOX').MODE; + if ( + event === 'messages.upsert' && + (waInstance?.instance?.inboundInboxMode === 'enforce' || inboxDefault === 'enforce') + ) { + throw error; + } } } } @@ -195,6 +203,14 @@ export class WebhookController extends EventController implements EventControlle url: globalURL, server_url: serverUrl, }); + const waInstance = this.monitor.waInstances[instanceName]; + const inboxDefault = configService.get('INBOUND_INBOX').MODE; + if ( + event === 'messages.upsert' && + (waInstance?.instance?.inboundInboxMode === 'enforce' || inboxDefault === 'enforce') + ) { + throw error; + } } } } diff --git a/src/api/services/channel.service.ts b/src/api/services/channel.service.ts index 56bec08021..08f8b9c8a5 100644 --- a/src/api/services/channel.service.ts +++ b/src/api/services/channel.service.ts @@ -61,6 +61,7 @@ export class ChannelStartupService { this.instance.token = instance.token; this.instance.businessId = instance.businessId; this.instance.ownerJid = instance.ownerJid; + this.instance.inboundInboxMode = instance.inboundInboxMode; if (this.configService.get('CHATWOOT').ENABLED && this.localChatwoot?.enabled) { this.chatwootService.eventWhatsapp( diff --git a/src/api/services/monitor.service.ts b/src/api/services/monitor.service.ts index 438530b57e..c3b181cada 100644 --- a/src/api/services/monitor.service.ts +++ b/src/api/services/monitor.service.ts @@ -291,6 +291,7 @@ export class WAMonitoringService { number: instanceData.number, businessId: instanceData.businessId, ownerJid: instanceData.ownerJid, + inboundInboxMode: instanceData.inboundInboxMode, }); if (instanceData.connectionStatus === 'open' || instanceData.connectionStatus === 'connecting') { @@ -329,6 +330,7 @@ export class WAMonitoringService { number: instanceData.number, businessId: instanceData.businessId, connectionStatus: instanceData.connectionStatus as any, // Pass connection status + inboundInboxMode: instanceData.inboundInboxMode as 'off' | 'shadow' | 'enforce', }; this.setInstance(instance); @@ -359,6 +361,7 @@ export class WAMonitoringService { businessId: instance.businessId, ownerJid: instance.ownerJid, connectionStatus: instance.connectionStatus as any, // Pass connection status + inboundInboxMode: instance.inboundInboxMode as 'off' | 'shadow' | 'enforce', }); }), ); @@ -384,6 +387,7 @@ export class WAMonitoringService { token: instance.token, businessId: instance.businessId, connectionStatus: instance.connectionStatus as any, // Pass connection status + inboundInboxMode: instance.inboundInboxMode as 'off' | 'shadow' | 'enforce', }); }), ); diff --git a/src/api/types/wa.types.ts b/src/api/types/wa.types.ts index 8f7c6a3906..894ad371d6 100644 --- a/src/api/types/wa.types.ts +++ b/src/api/types/wa.types.ts @@ -60,6 +60,7 @@ export declare namespace wa { number?: string; integration?: string; businessId?: string; + inboundInboxMode?: 'off' | 'shadow' | 'enforce'; }; export type LocalChatwoot = { diff --git a/src/config/env.config.ts b/src/config/env.config.ts index 7c4e382e7e..9d0e6b2d75 100644 --- a/src/config/env.config.ts +++ b/src/config/env.config.ts @@ -59,6 +59,14 @@ export type Database = { DELETE_DATA: DeleteData; }; +export type InboundInboxMode = 'off' | 'shadow' | 'enforce'; +export type InboundInbox = { + MODE: InboundInboxMode; + SOURCE_CLUSTER: string; + LEASE_SECONDS: number; + MAX_ATTEMPTS: number; +}; + export type DeleteData = { LOGICAL_MESSAGE_DELETE: boolean; }; @@ -397,6 +405,7 @@ export interface Env { SSL_CONF: SslConf; PROVIDER: ProviderSession; DATABASE: Database; + INBOUND_INBOX: InboundInbox; RABBITMQ: Rabbitmq; NATS: Nats; SQS: Sqs; @@ -501,6 +510,14 @@ export class ConfigService { LOGICAL_MESSAGE_DELETE: process.env?.DATABASE_DELETE_MESSAGE === 'true', }, }, + INBOUND_INBOX: { + MODE: ['off', 'shadow', 'enforce'].includes(process.env?.INBOUND_INBOX_MODE || '') + ? (process.env.INBOUND_INBOX_MODE as InboundInboxMode) + : 'off', + SOURCE_CLUSTER: process.env?.INBOUND_INBOX_SOURCE_CLUSTER || 'default', + LEASE_SECONDS: Number.parseInt(process.env?.INBOUND_INBOX_LEASE_SECONDS || '60'), + MAX_ATTEMPTS: Number.parseInt(process.env?.INBOUND_INBOX_MAX_ATTEMPTS || '10'), + }, RABBITMQ: { ENABLED: process.env?.RABBITMQ_ENABLED === 'true', GLOBAL_ENABLED: process.env?.RABBITMQ_GLOBAL_ENABLED === 'true', diff --git a/test/xmacna-baileys-message-processor.test.ts b/test/xmacna-baileys-message-processor.test.ts new file mode 100644 index 0000000000..313ea09fe1 --- /dev/null +++ b/test/xmacna-baileys-message-processor.test.ts @@ -0,0 +1,65 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; + +import { BaileysMessageProcessor } from '../src/api/integrations/channel/whatsapp/baileysMessage.processor'; + +async function waitFor(check: () => boolean, timeoutMs = 1000): Promise { + const deadline = Date.now() + timeoutMs; + while (!check()) { + if (Date.now() >= deadline) throw new Error('condition not reached before timeout'); + await new Promise((resolve) => setTimeout(resolve, 5)); + } +} + +test('a poison message exhausts its retries without blocking the next item in the batch', async () => { + const processor = new BaileysMessageProcessor(0, 1); + const calls: string[] = []; + processor.mount({ + onMessageReceive: async ({ messages }) => { + const id = messages[0].key.id!; + calls.push(id); + if (id === 'poison') throw new Error('sink failed'); + }, + }); + + processor.processMessage( + { + type: 'notify', + messages: [ + { key: { id: 'poison' }, message: { conversation: 'A' } }, + { key: { id: 'healthy' }, message: { conversation: 'B' } }, + ], + } as any, + {}, + ); + + await waitFor(() => calls.includes('healthy')); + assert.deepEqual(calls, ['poison', 'poison', 'healthy']); + processor.onDestroy(); +}); + +test('batches remain serialized', async () => { + const processor = new BaileysMessageProcessor(0, 0); + const order: string[] = []; + processor.mount({ + onMessageReceive: async ({ messages }) => { + const id = messages[0].key.id!; + order.push(`start:${id}`); + await new Promise((resolve) => setTimeout(resolve, id === 'first' ? 10 : 0)); + order.push(`end:${id}`); + }, + }); + processor.processMessage( + { + type: 'notify', + messages: [ + { key: { id: 'first' }, message: { conversation: 'A' } }, + { key: { id: 'second' }, message: { conversation: 'B' } }, + ], + } as any, + {}, + ); + await waitFor(() => order.includes('end:second')); + assert.deepEqual(order, ['start:first', 'end:first', 'start:second', 'end:second']); + processor.onDestroy(); +}); diff --git a/test/xmacna-inbound-inbox.test.ts b/test/xmacna-inbound-inbox.test.ts new file mode 100644 index 0000000000..9418ec3e4d --- /dev/null +++ b/test/xmacna-inbound-inbox.test.ts @@ -0,0 +1,65 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; + +import { + classifyInboundMessage, + evaluateExistingReceipt, + inboundPayloadHash, + normalizeInstanceScope, + resolveInboundMode, +} from '../src/api/integrations/channel/whatsapp/inboundInbox'; + +test('normalizes stable instance scope independently from internal instance ids', () => { + assert.equal(normalizeInstanceScope(' FÁBI FontesEnergia '), 'fábi-fontesenergia'); +}); + +test('classifies real, stub, protocol and internal control payloads', () => { + assert.equal(classifyInboundMessage({ key: { id: '1' }, message: { conversation: 'oi' } } as any), 'real'); + assert.equal(classifyInboundMessage({ key: { id: '2' }, messageStubType: 2 } as any), 'stub'); + assert.equal( + classifyInboundMessage({ key: { id: '3' }, message: { protocolMessage: { type: 1 } } } as any), + 'protocol', + ); + assert.equal( + classifyInboundMessage({ key: { id: '4' }, message: { conversation: 'requestPlaceholder' } } as any), + 'control', + ); +}); + +test('canonical hash ignores volatile timestamp, jid, fromMe and requestId fields', () => { + const first = { + key: { id: 'same-id', remoteJid: '551199@s.whatsapp.net', fromMe: false }, + messageTimestamp: 100, + requestId: 'one', + message: { conversation: 'conteúdo estável' }, + }; + const replay = { + key: { id: 'same-id', remoteJid: '999999@lid', fromMe: true }, + messageTimestamp: 999, + requestId: 'two', + message: { conversation: 'conteúdo estável' }, + }; + assert.equal(inboundPayloadHash(first as any), inboundPayloadHash(replay as any)); +}); + +test('canonical hash detects divergent real content', () => { + const base = { key: { id: 'same-id' }, message: { conversation: 'A' } }; + const divergent = { key: { id: 'same-id' }, message: { conversation: 'B' } }; + assert.notEqual(inboundPayloadHash(base as any), inboundPayloadHash(divergent as any)); +}); + +test('receipt policy deduplicates exact replay, promotes stub and quarantines collision', () => { + const exact = { id: 'r1', classification: 'real', payloadHash: 'a' }; + assert.equal(evaluateExistingReceipt(exact, { classification: 'real', payloadHash: 'a' }), 'duplicate'); + assert.equal( + evaluateExistingReceipt({ ...exact, classification: 'stub' }, { classification: 'real', payloadHash: 'b' }), + 'promoted', + ); + assert.equal(evaluateExistingReceipt(exact, { classification: 'real', payloadHash: 'b' }), 'collision'); +}); + +test('invalid persisted modes fail closed to the configured fallback', () => { + assert.equal(resolveInboundMode('enforce', 'off'), 'enforce'); + assert.equal(resolveInboundMode('invalid', 'shadow'), 'shadow'); + assert.equal(resolveInboundMode(undefined, 'off'), 'off'); +}); From 8a0591e029a029e8f0306b7fc4af0939a1bea232 Mon Sep 17 00:00:00 2001 From: raffareis Date: Wed, 12 Aug 2026 05:49:52 -0300 Subject: [PATCH 03/19] feat(inbound): add durable reconciler and migration tools (xmacna/elysium#500) --- package.json | 2 + .../migration.sql | 14 + prisma/mysql-schema.prisma | 2 + .../migration.sql | 19 + prisma/postgresql-schema.prisma | 2 + prisma/psql_bouncer-schema.prisma | 2 + .../channel/whatsapp/inboundInbox.ts | 159 +++++++- .../whatsapp/whatsapp.baileys.service.ts | 191 +++++++++- .../chatbot/base-chatbot.controller.ts | 33 +- .../chatbot/chatbot.controller.ts | 69 ++-- src/cli/inboundInboxMaintenance.ts | 242 +++++++++++++ test/xmacna-inbound-inbox.postgres.test.ts | 338 ++++++++++++++++++ test/xmacna-inbound-inbox.test.ts | 2 + 13 files changed, 1018 insertions(+), 57 deletions(-) create mode 100644 prisma/mysql-migrations/20260812090000_add_inbound_contact_scope/migration.sql create mode 100644 prisma/postgresql-migrations/20260812090000_add_inbound_contact_scope/migration.sql create mode 100644 src/cli/inboundInboxMaintenance.ts create mode 100644 test/xmacna-inbound-inbox.postgres.test.ts diff --git a/package.json b/package.json index c3e889d7c7..d8673cd034 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,8 @@ "test": "tsx watch ./test/all.test.ts", "test:xmacna-compat": "node --test test/xmacna-baileys-compat.test.cjs", "test:xmacna-inbox": "tsx --test test/xmacna-inbound-inbox.test.ts test/xmacna-baileys-message-processor.test.ts", + "test:xmacna-inbox:postgres": "tsx --test test/xmacna-inbound-inbox.postgres.test.ts", + "inbound-inbox:maintenance": "tsx src/cli/inboundInboxMaintenance.ts", "lint": "eslint --fix --ext .ts src", "lint:check": "eslint --ext .ts src", "commit": "cz", diff --git a/prisma/mysql-migrations/20260812090000_add_inbound_contact_scope/migration.sql b/prisma/mysql-migrations/20260812090000_add_inbound_contact_scope/migration.sql new file mode 100644 index 0000000000..3654f0897f --- /dev/null +++ b/prisma/mysql-migrations/20260812090000_add_inbound_contact_scope/migration.sql @@ -0,0 +1,14 @@ +ALTER TABLE `InboundReceipt` ADD COLUMN `contactScope` VARCHAR(255) NULL; + +UPDATE `InboundReceipt` receipt +LEFT JOIN `Message` message ON message.id = receipt.`messageRecordId` +SET receipt.`contactScope` = COALESCE( + JSON_UNQUOTE(JSON_EXTRACT(message.`key`, '$.remoteJidAlt')), + JSON_UNQUOTE(JSON_EXTRACT(message.`key`, '$.remoteJid')), + 'historical-unknown' +); + +ALTER TABLE `InboundReceipt` MODIFY COLUMN `contactScope` VARCHAR(255) NOT NULL; + +CREATE INDEX `InboundReceipt_instanceScope_contactScope_createdAt_idx` +ON `InboundReceipt`(`instanceScope`, `contactScope`, `createdAt`); diff --git a/prisma/mysql-schema.prisma b/prisma/mysql-schema.prisma index 54de1aa19d..b56969948a 100644 --- a/prisma/mysql-schema.prisma +++ b/prisma/mysql-schema.prisma @@ -187,6 +187,7 @@ model InboundReceipt { id String @id @default(cuid()) sourceCluster String @db.VarChar(64) instanceScope String @db.VarChar(255) + contactScope String @db.VarChar(255) messageId String @db.VarChar(255) classification String @db.VarChar(20) payloadHash String @db.Char(64) @@ -209,6 +210,7 @@ model InboundReceipt { @@unique([sourceCluster, instanceScope, messageId]) @@index([state, availableAt]) @@index([instanceScope, createdAt]) + @@index([instanceScope, contactScope, createdAt]) } model MessageUpdate { diff --git a/prisma/postgresql-migrations/20260812090000_add_inbound_contact_scope/migration.sql b/prisma/postgresql-migrations/20260812090000_add_inbound_contact_scope/migration.sql new file mode 100644 index 0000000000..59616cfc61 --- /dev/null +++ b/prisma/postgresql-migrations/20260812090000_add_inbound_contact_scope/migration.sql @@ -0,0 +1,19 @@ +ALTER TABLE "InboundReceipt" ADD COLUMN "contactScope" VARCHAR(255); + +UPDATE "InboundReceipt" receipt +SET "contactScope" = COALESCE( + message."key"->>'remoteJidAlt', + message."key"->>'remoteJid', + 'historical-unknown' +) +FROM "Message" message +WHERE message.id = receipt."messageRecordId"; + +UPDATE "InboundReceipt" +SET "contactScope" = 'historical-unknown' +WHERE "contactScope" IS NULL; + +ALTER TABLE "InboundReceipt" ALTER COLUMN "contactScope" SET NOT NULL; + +CREATE INDEX "InboundReceipt_instanceScope_contactScope_createdAt_idx" +ON "InboundReceipt"("instanceScope", "contactScope", "createdAt"); diff --git a/prisma/postgresql-schema.prisma b/prisma/postgresql-schema.prisma index 620ae08c99..f66faac6df 100644 --- a/prisma/postgresql-schema.prisma +++ b/prisma/postgresql-schema.prisma @@ -186,6 +186,7 @@ model InboundReceipt { id String @id @default(cuid()) sourceCluster String @db.VarChar(64) instanceScope String @db.VarChar(255) + contactScope String @db.VarChar(255) messageId String @db.VarChar(255) classification String @db.VarChar(20) payloadHash String @db.Char(64) @@ -208,6 +209,7 @@ model InboundReceipt { @@unique([sourceCluster, instanceScope, messageId]) @@index([state, availableAt]) @@index([instanceScope, createdAt]) + @@index([instanceScope, contactScope, createdAt]) } model MessageUpdate { diff --git a/prisma/psql_bouncer-schema.prisma b/prisma/psql_bouncer-schema.prisma index facf22fdac..d4435041e0 100644 --- a/prisma/psql_bouncer-schema.prisma +++ b/prisma/psql_bouncer-schema.prisma @@ -186,6 +186,7 @@ model InboundReceipt { id String @id @default(cuid()) sourceCluster String @db.VarChar(64) instanceScope String @db.VarChar(255) + contactScope String @db.VarChar(255) messageId String @db.VarChar(255) classification String @db.VarChar(20) payloadHash String @db.Char(64) @@ -208,6 +209,7 @@ model InboundReceipt { @@unique([sourceCluster, instanceScope, messageId]) @@index([state, availableAt]) @@index([instanceScope, createdAt]) + @@index([instanceScope, contactScope, createdAt]) } model MessageUpdate { diff --git a/src/api/integrations/channel/whatsapp/inboundInbox.ts b/src/api/integrations/channel/whatsapp/inboundInbox.ts index d1d592f192..5430364591 100644 --- a/src/api/integrations/channel/whatsapp/inboundInbox.ts +++ b/src/api/integrations/channel/whatsapp/inboundInbox.ts @@ -14,6 +14,7 @@ export type InboundIdentity = { }; export type InboundClaimInput = InboundIdentity & { + contactScope: string; classification: InboundClassification; payloadHash: string; messageData?: Record; @@ -29,6 +30,16 @@ export type InboundClaimResult = { leaseToken?: number; }; +export type InboundWorkItem = { + receiptId: string; + leaseOwner: string; + leaseToken: number; + webhookState: string; + chatwootState: string; + chatbotState: string; + message: Record; +}; + type ExistingReceipt = { id: string; classification: string; @@ -63,6 +74,10 @@ export function normalizeInstanceScope(instanceName: string): string { return instanceName.normalize('NFKC').trim().toLowerCase().replace(/\s+/g, '-'); } +export function normalizeContactScope(remoteJid: string | null | undefined): string { + return (remoteJid || 'unknown').normalize('NFKC').trim().toLowerCase(); +} + export function classifyInboundMessage(received: WAMessage): InboundClassification { const message = received?.message as Record | null | undefined; const protocol = @@ -130,11 +145,13 @@ export class PrismaInboundInbox { data: { sourceCluster: input.sourceCluster, instanceScope: input.instanceScope, + contactScope: input.contactScope, messageId: input.messageId, classification: input.classification, payloadHash: input.payloadHash, state: input.classification === 'real' ? 'processing' : 'observed', leaseOwner: input.classification === 'real' ? input.leaseOwner : undefined, + attempts: input.classification === 'real' ? 1 : 0, leaseToken: input.classification === 'real' ? 1 : 0, leaseExpiresAt: input.classification === 'real' ? new Date(Date.now() + (input.leaseSeconds ?? 60) * 1000) : undefined, @@ -180,6 +197,7 @@ export class PrismaInboundInbox { }, data: { state: 'processing', + contactScope: input.contactScope, attempts: { increment: 1 }, leaseOwner: input.leaseOwner, leaseToken: { increment: 1 }, @@ -254,8 +272,10 @@ export class PrismaInboundInbox { payloadHash: input.payloadHash, collisionHash: null, state: 'processing', + contactScope: input.contactScope, messageRecordId, leaseOwner: input.leaseOwner, + attempts: 1, leaseToken: { increment: 1 }, leaseExpiresAt: new Date(Date.now() + (input.leaseSeconds ?? 60) * 1000), }, @@ -276,10 +296,123 @@ export class PrismaInboundInbox { public async markSink( receiptId: string, sink: 'webhook' | 'chatwoot' | 'chatbot', - state: 'sent' | 'skipped', - ): Promise { + state: 'sent' | 'skipped' | 'failed', + leaseOwner?: string, + leaseToken?: number, + ): Promise { const field = `${sink}State` as 'webhookState' | 'chatwootState' | 'chatbotState'; - await this.repository.inboundReceipt.update({ where: { id: receiptId }, data: { [field]: state } }); + const updated = await this.repository.inboundReceipt.updateMany({ + where: { + id: receiptId, + ...(leaseOwner && leaseToken !== undefined ? { leaseOwner, leaseToken, state: 'processing' } : {}), + }, + data: { [field]: state }, + }); + return updated.count === 1; + } + + public async heartbeat( + receiptId: string, + leaseOwner: string, + leaseToken: number, + leaseSeconds: number, + ): Promise { + const updated = await this.repository.inboundReceipt.updateMany({ + where: { id: receiptId, leaseOwner, leaseToken, state: 'processing' }, + data: { leaseExpiresAt: new Date(Date.now() + leaseSeconds * 1000) }, + }); + return updated.count === 1; + } + + public async leaseNext( + sourceCluster: string, + instanceScope: string, + leaseOwner: string, + leaseSeconds: number, + maxAttempts: number, + ): Promise { + const provider = process.env.DATABASE_PROVIDER || 'postgresql'; + return this.repository.$transaction( + async (tx) => { + const rows = + provider === 'mysql' + ? await tx.$queryRaw<{ id: string }[]>(Prisma.sql` + SELECT candidate.id + FROM InboundReceipt candidate + WHERE candidate.sourceCluster = ${sourceCluster} + AND candidate.instanceScope = ${instanceScope} + AND candidate.classification = 'real' + AND candidate.messageRecordId IS NOT NULL + AND candidate.attempts < ${maxAttempts} + AND ( + (candidate.state = 'failed' AND candidate.availableAt <= CURRENT_TIMESTAMP) + OR (candidate.state = 'processing' AND candidate.leaseExpiresAt < CURRENT_TIMESTAMP) + ) + AND NOT EXISTS ( + SELECT 1 FROM InboundReceipt previous + WHERE previous.sourceCluster = candidate.sourceCluster + AND previous.instanceScope = candidate.instanceScope + AND previous.contactScope = candidate.contactScope + AND previous.state IN ('received', 'processing', 'failed') + AND (previous.createdAt < candidate.createdAt OR + (previous.createdAt = candidate.createdAt AND previous.id < candidate.id)) + ) + ORDER BY candidate.createdAt, candidate.id + LIMIT 1 FOR UPDATE SKIP LOCKED + `) + : await tx.$queryRaw<{ id: string }[]>(Prisma.sql` + SELECT candidate.id + FROM "InboundReceipt" candidate + WHERE candidate."sourceCluster" = ${sourceCluster} + AND candidate."instanceScope" = ${instanceScope} + AND candidate.classification = 'real' + AND candidate."messageRecordId" IS NOT NULL + AND candidate.attempts < ${maxAttempts} + AND ( + (candidate.state = 'failed' AND candidate."availableAt" <= CURRENT_TIMESTAMP) + OR (candidate.state = 'processing' AND candidate."leaseExpiresAt" < CURRENT_TIMESTAMP) + ) + AND NOT EXISTS ( + SELECT 1 FROM "InboundReceipt" previous + WHERE previous."sourceCluster" = candidate."sourceCluster" + AND previous."instanceScope" = candidate."instanceScope" + AND previous."contactScope" = candidate."contactScope" + AND previous.state IN ('received', 'processing', 'failed') + AND (previous."createdAt" < candidate."createdAt" OR + (previous."createdAt" = candidate."createdAt" AND previous.id < candidate.id)) + ) + ORDER BY candidate."createdAt", candidate.id + LIMIT 1 FOR UPDATE SKIP LOCKED + `); + if (rows.length === 0) return null; + + const updated = await tx.inboundReceipt.update({ + where: { id: rows[0].id }, + data: { + state: 'processing', + attempts: { increment: 1 }, + leaseOwner, + leaseToken: { increment: 1 }, + leaseExpiresAt: new Date(Date.now() + leaseSeconds * 1000), + lastError: null, + }, + include: { Message: true }, + }); + if (!updated.Message) throw new Error(`Inbound receipt ${updated.id} has no durable Message`); + const message = { ...updated.Message } as any; + delete message.id; + return { + receiptId: updated.id, + leaseOwner, + leaseToken: updated.leaseToken, + webhookState: updated.webhookState, + chatwootState: updated.chatwootState, + chatbotState: updated.chatbotState, + message, + }; + }, + { isolationLevel: Prisma.TransactionIsolationLevel.ReadCommitted }, + ); } public async markDone(receiptId: string, leaseOwner: string, leaseToken: number): Promise { @@ -290,11 +423,26 @@ export class PrismaInboundInbox { return updated.count === 1; } - public async markFailed(receiptId: string, leaseOwner: string, leaseToken: number, error: unknown): Promise { + public async markFailed( + receiptId: string, + leaseOwner: string, + leaseToken: number, + error: unknown, + maxAttempts = 10, + baseBackoffSeconds = 2, + ): Promise { + const current = await this.repository.inboundReceipt.findFirst({ + where: { id: receiptId, leaseOwner, leaseToken, state: 'processing' }, + select: { attempts: true }, + }); + if (!current) return false; + const dead = current.attempts >= maxAttempts; + const delaySeconds = Math.min(300, baseBackoffSeconds * 2 ** Math.max(0, current.attempts - 1)); const updated = await this.repository.inboundReceipt.updateMany({ where: { id: receiptId, leaseOwner, leaseToken, state: 'processing' }, data: { - state: 'failed', + state: dead ? 'dead' : 'failed', + availableAt: dead ? new Date() : new Date(Date.now() + delaySeconds * 1000), leaseOwner: null, leaseExpiresAt: null, lastError: String(error instanceof Error ? error.message : error).slice(0, 4000), @@ -317,6 +465,7 @@ export class PrismaInboundInbox { const created = await this.repository.inboundReceipt.create({ data: { ...key, + contactScope: input.contactScope, classification: input.classification, payloadHash: input.payloadHash, state: 'shadow_seen', diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index f167a23dd3..1f59e4d845 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -157,6 +157,7 @@ import { BaileysMessageProcessor } from './baileysMessage.processor'; import { classifyInboundMessage, inboundPayloadHash, + normalizeContactScope, normalizeInstanceScope, PrismaInboundInbox, resolveInboundMode, @@ -235,6 +236,9 @@ async function getVideoDuration(input: Buffer | string | Readable): Promise { try { - return await this.createClient(this.phoneNumber); + const client = await this.createClient(this.phoneNumber); + this.startInboundInboxWorker(); + return client; } catch (error) { this.logger.error(error); throw new InternalServerErrorException(error?.toString()); } } + private startInboundInboxWorker(): void { + if (this.instance.inboundInboxMode !== 'enforce' || this.inboundWorkerTimer) return; + const run = () => void this.runInboundInboxWorker().catch((error) => this.logger.error(`Inbound worker: ${error}`)); + this.inboundWorkerTimer = setInterval(run, 1000); + this.inboundWorkerTimer.unref(); + run(); + } + + private async runInboundInboxWorker(): Promise { + if (this.inboundWorkerRunning || this.instance.inboundInboxMode !== 'enforce') return; + this.inboundWorkerRunning = true; + const config = this.configService.get('INBOUND_INBOX'); + const instanceScope = normalizeInstanceScope(this.instance.name); + try { + for (let processed = 0; processed < 25; processed += 1) { + const item = await this.inboundInbox.leaseNext( + config.SOURCE_CLUSTER, + instanceScope, + this.inboundWorkerOwner, + config.LEASE_SECONDS, + config.MAX_ATTEMPTS, + ); + if (!item) break; + await this.dispatchRecoveredInbound(item, config); + } + } finally { + this.inboundWorkerRunning = false; + } + } + + private async dispatchRecoveredInbound( + item: { + receiptId: string; + leaseOwner: string; + leaseToken: number; + webhookState: string; + chatwootState: string; + chatbotState: string; + message: Record; + }, + config: InboundInbox, + ): Promise { + const messageRaw = item.message as any; + let activeSink: 'webhook' | 'chatwoot' | 'chatbot' | undefined; + const heartbeat = setInterval( + () => { + void this.inboundInbox + .heartbeat(item.receiptId, item.leaseOwner, item.leaseToken, config.LEASE_SECONDS) + .then((renewed) => { + if (!renewed) this.logger.error(`Recovered inbound lease fencing lost for ${item.receiptId}`); + }) + .catch((error) => this.logger.error(`Recovered inbound heartbeat failed: ${error}`)); + }, + Math.max(1000, Math.floor((config.LEASE_SECONDS * 1000) / 3)), + ); + heartbeat.unref(); + + const markSink = async (sink: 'webhook' | 'chatwoot' | 'chatbot', state: 'sent' | 'skipped' | 'failed') => { + const marked = await this.inboundInbox.markSink(item.receiptId, sink, state, item.leaseOwner, item.leaseToken); + if (!marked) throw new Error(`Stale inbound lease while marking ${sink}`); + }; + + try { + if (!['sent', 'skipped'].includes(item.chatwootState)) { + activeSink = 'chatwoot'; + if ( + this.configService.get('CHATWOOT').ENABLED && + this.localChatwoot?.enabled && + !messageRaw.key?.id?.includes('@broadcast') + ) { + await this.chatwootService.eventWhatsapp( + Events.MESSAGES_UPSERT, + { instanceName: this.instance.name, instanceId: this.instanceId }, + messageRaw, + ); + await markSink('chatwoot', 'sent'); + } else { + await markSink('chatwoot', 'skipped'); + } + activeSink = undefined; + } + + if (!['sent', 'skipped'].includes(item.webhookState)) { + activeSink = 'webhook'; + await this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw); + await markSink('webhook', 'sent'); + activeSink = undefined; + } + + if (!['sent', 'skipped'].includes(item.chatbotState)) { + activeSink = 'chatbot'; + await chatbotController.emit({ + instance: { instanceName: this.instance.name, instanceId: this.instanceId }, + remoteJid: messageRaw.key.remoteJid, + msg: messageRaw, + pushName: messageRaw.pushName, + }); + await markSink('chatbot', 'sent'); + activeSink = undefined; + } + + const done = await this.inboundInbox.markDone(item.receiptId, item.leaseOwner, item.leaseToken); + if (!done) throw new Error(`Stale inbound lease while completing ${item.receiptId}`); + } catch (error) { + if (activeSink) { + await this.inboundInbox.markSink(item.receiptId, activeSink, 'failed', item.leaseOwner, item.leaseToken); + } + await this.inboundInbox.markFailed(item.receiptId, item.leaseOwner, item.leaseToken, error, config.MAX_ATTEMPTS); + } finally { + clearInterval(heartbeat); + } + } + + private async markActiveInboundSink( + receipt: { id: string; leaseOwner: string; leaseToken: number }, + sink: 'webhook' | 'chatwoot' | 'chatbot', + state: 'sent' | 'skipped' | 'failed', + ): Promise { + const marked = await this.inboundInbox.markSink(receipt.id, sink, state, receipt.leaseOwner, receipt.leaseToken); + if (!marked) throw new Error(`Stale inbound lease while marking ${sink} for ${receipt.id}`); + } + + private async completeActiveInbound(receipt: { id: string; leaseOwner: string; leaseToken: number }): Promise { + const done = await this.inboundInbox.markDone(receipt.id, receipt.leaseOwner, receipt.leaseToken); + if (!done) throw new Error(`Stale inbound lease while completing ${receipt.id}`); + } + private readonly chatHandle = { 'chats.upsert': async (chats: Chat[]) => { const existingChatIds = await this.prismaRepository.chat.findMany({ @@ -1096,12 +1233,17 @@ export class BaileysStartupService extends ChannelStartupService { let activeReceipt: | { id: string; leaseOwner: string; leaseToken: number; mode: 'off' | 'shadow' | 'enforce' } | undefined; + let activeSink: 'webhook' | 'chatwoot' | 'chatbot' | undefined; + let heartbeatTimer: NodeJS.Timeout | undefined; try { for (const received of messages) { const inboundConfig = this.configService.get('INBOUND_INBOX'); const inboundMode = resolveInboundMode(this.instance.inboundInboxMode, inboundConfig.MODE); const inboundClassification = classifyInboundMessage(received); const inboundMessageId = received.key?.id; + const inboundContactScope = normalizeContactScope( + (received.key as any)?.remoteJidAlt || received.key?.remoteJid, + ); const inboundIdentity = inboundMessageId ? { sourceCluster: inboundConfig.SOURCE_CLUSTER, @@ -1114,6 +1256,7 @@ export class BaileysStartupService extends ChannelStartupService { await this.inboundInbox.claim( { ...inboundIdentity, + contactScope: inboundContactScope, classification: inboundClassification, payloadHash: inboundPayloadHash(received), }, @@ -1246,6 +1389,7 @@ export class BaileysStartupService extends ChannelStartupService { const claim = await this.inboundInbox.claim( { ...inboundIdentity, + contactScope: inboundContactScope, classification: inboundClassification, payloadHash: inboundPayloadHash(received), messageData, @@ -1262,6 +1406,18 @@ export class BaileysStartupService extends ChannelStartupService { leaseToken: claim.leaseToken, mode: inboundMode, }; + heartbeatTimer = setInterval( + () => { + void this.inboundInbox + .heartbeat(claim.receiptId, leaseOwner, claim.leaseToken!, inboundConfig.LEASE_SECONDS) + .then((renewed) => { + if (!renewed) this.logger.error(`Inbound lease fencing lost for receipt ${claim.receiptId}`); + }) + .catch((error) => this.logger.error(`Inbound lease heartbeat failed: ${error}`)); + }, + Math.max(1000, Math.floor((inboundConfig.LEASE_SECONDS * 1000) / 3)), + ); + heartbeatTimer.unref(); } if (!claim.shouldDispatch) { this.logger.info( @@ -1396,6 +1552,7 @@ export class BaileysStartupService extends ChannelStartupService { this.localChatwoot?.enabled && !received.key.id.includes('@broadcast') ) { + activeSink = 'chatwoot'; const chatwootSentMessage = await this.chatwootService.eventWhatsapp( Events.MESSAGES_UPSERT, { instanceName: this.instance.name, instanceId: this.instanceId }, @@ -1407,9 +1564,10 @@ export class BaileysStartupService extends ChannelStartupService { messageRaw.chatwootInboxId = chatwootSentMessage.inbox_id; messageRaw.chatwootConversationId = chatwootSentMessage.conversation_id; } - if (activeReceipt) await this.inboundInbox.markSink(activeReceipt.id, 'chatwoot', 'sent'); + if (activeReceipt) await this.markActiveInboundSink(activeReceipt, 'chatwoot', 'sent'); + activeSink = undefined; } else if (activeReceipt) { - await this.inboundInbox.markSink(activeReceipt.id, 'chatwoot', 'skipped'); + await this.markActiveInboundSink(activeReceipt, 'chatwoot', 'skipped'); } if (this.configService.get('OPENAI').ENABLED && received?.message?.audioMessage) { @@ -1552,16 +1710,20 @@ export class BaileysStartupService extends ChannelStartupService { } console.log(messageRaw); + activeSink = 'webhook'; await this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw); - if (activeReceipt) await this.inboundInbox.markSink(activeReceipt.id, 'webhook', 'sent'); + if (activeReceipt) await this.markActiveInboundSink(activeReceipt, 'webhook', 'sent'); + activeSink = undefined; + activeSink = 'chatbot'; await chatbotController.emit({ instance: { instanceName: this.instance.name, instanceId: this.instanceId }, remoteJid: messageRaw.key.remoteJid, msg: messageRaw, pushName: messageRaw.pushName, }); - if (activeReceipt) await this.inboundInbox.markSink(activeReceipt.id, 'chatbot', 'sent'); + if (activeReceipt) await this.markActiveInboundSink(activeReceipt, 'chatbot', 'sent'); + activeSink = undefined; const contact = await this.prismaRepository.contact.findFirst({ where: { remoteJid: received.key.remoteJid, instanceId: this.instanceId }, @@ -1581,7 +1743,9 @@ export class BaileysStartupService extends ChannelStartupService { if (contactRaw.remoteJid === 'status@broadcast') { if (activeReceipt) { - await this.inboundInbox.markDone(activeReceipt.id, activeReceipt.leaseOwner, activeReceipt.leaseToken); + await this.completeActiveInbound(activeReceipt); + if (heartbeatTimer) clearInterval(heartbeatTimer); + heartbeatTimer = undefined; activeReceipt = undefined; } continue; @@ -1617,7 +1781,9 @@ export class BaileysStartupService extends ChannelStartupService { }); if (activeReceipt) { - await this.inboundInbox.markDone(activeReceipt.id, activeReceipt.leaseOwner, activeReceipt.leaseToken); + await this.completeActiveInbound(activeReceipt); + if (heartbeatTimer) clearInterval(heartbeatTimer); + heartbeatTimer = undefined; activeReceipt = undefined; } continue; @@ -1632,17 +1798,24 @@ export class BaileysStartupService extends ChannelStartupService { create: contactRaw, }); if (activeReceipt) { - await this.inboundInbox.markDone(activeReceipt.id, activeReceipt.leaseOwner, activeReceipt.leaseToken); + await this.completeActiveInbound(activeReceipt); + if (heartbeatTimer) clearInterval(heartbeatTimer); + heartbeatTimer = undefined; activeReceipt = undefined; } } } catch (error) { if (activeReceipt) { + if (heartbeatTimer) clearInterval(heartbeatTimer); + if (activeSink) { + await this.markActiveInboundSink(activeReceipt, activeSink, 'failed'); + } await this.inboundInbox.markFailed( activeReceipt.id, activeReceipt.leaseOwner, activeReceipt.leaseToken, error, + this.configService.get('INBOUND_INBOX').MAX_ATTEMPTS, ); } this.logger.error(error); diff --git a/src/api/integrations/chatbot/base-chatbot.controller.ts b/src/api/integrations/chatbot/base-chatbot.controller.ts index a5b83e257c..dff212ba3a 100644 --- a/src/api/integrations/chatbot/base-chatbot.controller.ts +++ b/src/api/integrations/chatbot/base-chatbot.controller.ts @@ -919,18 +919,24 @@ export abstract class BaseChatbotController 0) { - this.processDebounce(this.userMessageDebounce, content, remoteJid, debounceTime, async (debouncedContent) => { - await this.processBot( - this.waMonitor.waInstances[instance.instanceName], - remoteJid, - findBot, - session, - mergedSettings, - debouncedContent, - msg?.pushName, - msg, - ); - }); + await this.processDebounce( + this.userMessageDebounce, + content, + remoteJid, + debounceTime, + async (debouncedContent) => { + await this.processBot( + this.waMonitor.waInstances[instance.instanceName], + remoteJid, + findBot, + session, + mergedSettings, + debouncedContent, + msg?.pushName, + msg, + ); + }, + ); } else { await this.processBot( this.waMonitor.waInstances[instance.instanceName], @@ -945,6 +951,9 @@ export abstract class BaseChatbotController { - const myQuestion = userMessageDebounce[remoteJid].message; - this.logger.log('Debounce complete. Processing message: ' + myQuestion); + ): Promise { + return new Promise((resolve, reject) => { + if (userMessageDebounce[remoteJid]) { + userMessageDebounce[remoteJid].message += `\n${content}`; + userMessageDebounce[remoteJid].waiters.push({ resolve, reject }); + userMessageDebounce[remoteJid].callback = callback; + this.logger.log('message debounced: ' + userMessageDebounce[remoteJid].message); + clearTimeout(userMessageDebounce[remoteJid].timeoutId); + } else { + userMessageDebounce[remoteJid] = { + message: content, + timeoutId: null, + callback, + waiters: [{ resolve, reject }], + }; + } - delete userMessageDebounce[remoteJid]; - callback(myQuestion); - }, debounceTime * 1000); + userMessageDebounce[remoteJid].timeoutId = setTimeout(async () => { + const entry = userMessageDebounce[remoteJid]; + const myQuestion = entry.message; + this.logger.log('Debounce complete. Processing message: ' + myQuestion); + delete userMessageDebounce[remoteJid]; + try { + await entry.callback(myQuestion); + entry.waiters.forEach((waiter) => waiter.resolve()); + } catch (error) { + entry.waiters.forEach((waiter) => waiter.reject(error)); + } + }, debounceTime * 1000); + }); } public checkIgnoreJids(ignoreJids: any, remoteJid: string) { diff --git a/src/cli/inboundInboxMaintenance.ts b/src/cli/inboundInboxMaintenance.ts new file mode 100644 index 0000000000..ea5220d961 --- /dev/null +++ b/src/cli/inboundInboxMaintenance.ts @@ -0,0 +1,242 @@ +import { + inboundPayloadHash, + normalizeContactScope, + normalizeInstanceScope, +} from '@api/integrations/channel/whatsapp/inboundInbox'; +import { PrismaClient } from '@prisma/client'; +import { closeSync, openSync, readFileSync, writeFileSync } from 'fs'; + +type Command = 'backfill' | 'export' | 'import'; + +export type Options = { + command: Command; + sourceCluster?: string; + instanceName?: string; + targetCluster?: string; + targetScope?: string; + input?: string; + output?: string; + batchSize: number; + live: boolean; +}; + +type Tombstone = { + messageId: string; + classification: string; + payloadHash: string; + collisionHash?: string | null; + contactScope: string; +}; + +type TombstoneManifest = { + schemaVersion: 1; + sourceCluster: string; + sourceScope: string; + exportedAt: string; + tombstones: Tombstone[]; +}; + +function usage(): never { + console.error( + [ + 'Usage:', + ' npm run inbound-inbox:maintenance -- backfill --source-cluster NAME --instance-name NAME [--batch-size N] [--live]', + ' npm run inbound-inbox:maintenance -- export --source-cluster NAME --instance-name NAME --output FILE', + ' npm run inbound-inbox:maintenance -- import --input FILE --target-cluster NAME --target-scope NAME [--live]', + '', + 'backfill/import are dry-run by default. export refuses to overwrite FILE.', + ].join('\n'), + ); + process.exit(2); +} + +function parseArgs(argv: string[]): Options { + const command = argv.shift() as Command; + if (!['backfill', 'export', 'import'].includes(command)) usage(); + const options: Options = { command, batchSize: 500, live: false }; + while (argv.length > 0) { + const flag = argv.shift(); + if (flag === '--live') options.live = true; + else if (flag === '--source-cluster') options.sourceCluster = argv.shift(); + else if (flag === '--instance-name') options.instanceName = argv.shift(); + else if (flag === '--target-cluster') options.targetCluster = argv.shift(); + else if (flag === '--target-scope') options.targetScope = argv.shift(); + else if (flag === '--input') options.input = argv.shift(); + else if (flag === '--output') options.output = argv.shift(); + else if (flag === '--batch-size') options.batchSize = Number.parseInt(argv.shift() || '', 10); + else usage(); + } + if (!Number.isInteger(options.batchSize) || options.batchSize < 1 || options.batchSize > 5000) usage(); + if (command === 'backfill' && (!options.sourceCluster || !options.instanceName)) usage(); + if (command === 'export' && (!options.sourceCluster || !options.instanceName || !options.output)) usage(); + if (command === 'import' && (!options.input || !options.targetCluster || !options.targetScope)) usage(); + return options; +} + +function assertSafeName(value: string, label: string): string { + const normalized = value.normalize('NFKC').trim(); + if (!normalized || normalized.length > 255 || [...normalized].some((character) => character.charCodeAt(0) < 32)) { + throw new Error(`${label} is invalid`); + } + return normalized; +} + +export async function backfill(prisma: PrismaClient, options: Options): Promise { + const instanceName = assertSafeName(options.instanceName!, 'instance-name'); + const sourceCluster = assertSafeName(options.sourceCluster!, 'source-cluster'); + const instance = await prisma.instance.findUnique({ + where: { name: instanceName }, + select: { id: true, name: true }, + }); + if (!instance) throw new Error(`Instance not found: ${instanceName}`); + const instanceScope = normalizeInstanceScope(instance.name); + let cursor: string | undefined; + let scanned = 0; + let candidates = 0; + let inserted = 0; + + for (;;) { + const messages = await prisma.message.findMany({ + where: { instanceId: instance.id }, + orderBy: { id: 'asc' }, + take: options.batchSize, + ...(cursor ? { cursor: { id: cursor }, skip: 1 } : {}), + select: { id: true, key: true, message: true, messageType: true }, + }); + if (messages.length === 0) break; + cursor = messages[messages.length - 1].id; + scanned += messages.length; + const messageIds = messages.map((message) => (message.key as any)?.id).filter(Boolean) as string[]; + const existing = await prisma.inboundReceipt.findMany({ + where: { sourceCluster, instanceScope, messageId: { in: messageIds } }, + select: { messageId: true }, + }); + const existingIds = new Set(existing.map((receipt) => receipt.messageId)); + const rows = messages + .filter((message) => { + const messageId = (message.key as any)?.id; + return messageId && !existingIds.has(messageId); + }) + .map((message) => ({ + sourceCluster, + instanceScope, + contactScope: normalizeContactScope((message.key as any)?.remoteJidAlt || (message.key as any)?.remoteJid), + messageId: (message.key as any).id as string, + classification: 'real', + payloadHash: inboundPayloadHash({ key: message.key, message: message.message } as any), + state: 'historical_seen', + webhookState: 'skipped', + chatwootState: 'skipped', + chatbotState: 'skipped', + messageRecordId: message.id, + })); + candidates += rows.length; + if (options.live && rows.length > 0) { + inserted += (await prisma.inboundReceipt.createMany({ data: rows, skipDuplicates: true })).count; + } + } + console.log( + JSON.stringify({ mode: options.live ? 'live' : 'dry-run', scanned, candidates, inserted, instanceScope }), + ); +} + +export async function exportTombstones(prisma: PrismaClient, options: Options): Promise { + const sourceCluster = assertSafeName(options.sourceCluster!, 'source-cluster'); + const instanceName = assertSafeName(options.instanceName!, 'instance-name'); + const instanceScope = normalizeInstanceScope(instanceName); + const incomplete = await prisma.inboundReceipt.count({ + where: { sourceCluster, instanceScope, state: { in: ['received', 'processing', 'failed'] } }, + }); + if (incomplete > 0) throw new Error(`Refusing export: ${incomplete} receipts are incomplete`); + const receipts = await prisma.inboundReceipt.findMany({ + where: { sourceCluster, instanceScope }, + orderBy: [{ createdAt: 'asc' }, { id: 'asc' }], + select: { + messageId: true, + classification: true, + payloadHash: true, + collisionHash: true, + contactScope: true, + }, + }); + const manifest: TombstoneManifest = { + schemaVersion: 1, + sourceCluster, + sourceScope: instanceScope, + exportedAt: new Date().toISOString(), + tombstones: receipts, + }; + const fd = openSync(options.output!, 'wx', 0o600); + try { + writeFileSync(fd, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8'); + } finally { + closeSync(fd); + } + console.log(JSON.stringify({ mode: 'export', count: receipts.length, output: options.output })); +} + +export function readManifest(input: string): TombstoneManifest { + const manifest = JSON.parse(readFileSync(input, 'utf8')) as TombstoneManifest; + if (manifest.schemaVersion !== 1 || !Array.isArray(manifest.tombstones)) + throw new Error('Invalid tombstone manifest'); + for (const row of manifest.tombstones) { + if (!row.messageId || !/^[0-9a-f]{64}$/.test(row.payloadHash) || !row.contactScope) { + throw new Error('Invalid tombstone row'); + } + } + return manifest; +} + +export async function importTombstones(prisma: PrismaClient, options: Options): Promise { + const manifest = readManifest(options.input!); + const targetCluster = assertSafeName(options.targetCluster!, 'target-cluster'); + const targetScope = normalizeInstanceScope(assertSafeName(options.targetScope!, 'target-scope')); + const rows = manifest.tombstones.map((row) => ({ + sourceCluster: targetCluster, + instanceScope: targetScope, + contactScope: normalizeContactScope(row.contactScope), + messageId: row.messageId, + classification: row.classification, + payloadHash: row.payloadHash, + collisionHash: row.collisionHash, + state: 'historical_seen', + webhookState: 'skipped', + chatwootState: 'skipped', + chatbotState: 'skipped', + })); + let inserted = 0; + if (options.live && rows.length > 0) { + inserted = (await prisma.inboundReceipt.createMany({ data: rows, skipDuplicates: true })).count; + } + console.log( + JSON.stringify({ + mode: options.live ? 'live' : 'dry-run', + sourceCluster: manifest.sourceCluster, + sourceScope: manifest.sourceScope, + targetCluster, + targetScope, + candidates: rows.length, + inserted, + }), + ); +} + +async function main(): Promise { + const options = parseArgs(process.argv.slice(2)); + const prisma = new PrismaClient(); + try { + await prisma.$connect(); + if (options.command === 'backfill') await backfill(prisma, options); + else if (options.command === 'export') await exportTombstones(prisma, options); + else await importTombstones(prisma, options); + } finally { + await prisma.$disconnect(); + } +} + +if (require.main === module) { + void main().catch((error) => { + console.error(error instanceof Error ? error.message : error); + process.exitCode = 1; + }); +} diff --git a/test/xmacna-inbound-inbox.postgres.test.ts b/test/xmacna-inbound-inbox.postgres.test.ts new file mode 100644 index 0000000000..7458a2ef19 --- /dev/null +++ b/test/xmacna-inbound-inbox.postgres.test.ts @@ -0,0 +1,338 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { randomUUID } from 'node:crypto'; +import { mkdtempSync, rmSync, statSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; + +import { PrismaClient } from '@prisma/client'; + +import { PrismaInboundInbox } from '../src/api/integrations/channel/whatsapp/inboundInbox'; +import { backfill, exportTombstones, importTombstones } from '../src/cli/inboundInboxMaintenance'; + +const enabled = process.env.XMACNA_INBOX_INTEGRATION === '1'; + +test('840 concurrent deliveries create one receipt and one durable message', { skip: !enabled }, async () => { + const repository = new PrismaClient(); + const inbox = new PrismaInboundInbox(repository as any); + const suffix = randomUUID(); + const instanceId = `inbox-test-${suffix}`; + const messageId = `wamid-${suffix}`; + + try { + await repository.$connect(); + await repository.instance.create({ + data: { id: instanceId, name: `inbox-test-${suffix}`, inboundInboxMode: 'enforce' }, + }); + + const messageData = { + key: { id: messageId, remoteJid: '5511999999999@s.whatsapp.net', fromMe: false }, + pushName: 'Teste local', + messageType: 'conversation', + message: { conversation: 'mensagem concorrente' }, + source: 'unknown' as const, + messageTimestamp: 1, + instanceId, + }; + const input = { + sourceCluster: 'local-concurrency', + instanceScope: `instance-${suffix}`, + contactScope: '5511999999999@s.whatsapp.net', + messageId, + classification: 'real' as const, + payloadHash: 'a'.repeat(64), + messageData, + leaseOwner: `test-${suffix}`, + leaseSeconds: 60, + }; + + const results = await Promise.all(Array.from({ length: 840 }, () => inbox.claim(input, 'enforce'))); + assert.equal(results.filter((result) => result.shouldDispatch).length, 1); + assert.equal(results.filter((result) => result.kind === 'claimed').length, 1); + assert.equal(results.filter((result) => result.kind === 'duplicate').length, 839); + + const receipts = await repository.inboundReceipt.findMany({ + where: { + sourceCluster: input.sourceCluster, + instanceScope: input.instanceScope, + messageId, + }, + }); + assert.equal(receipts.length, 1); + assert.ok(receipts[0].messageRecordId); + assert.equal(await repository.message.count({ where: { instanceId } }), 1); + } finally { + await repository.inboundReceipt.deleteMany({ where: { instanceScope: `instance-${suffix}` } }); + await repository.instance.deleteMany({ where: { id: instanceId } }); + await repository.$disconnect(); + } +}); + +test('reconciler leases with fencing, preserves completed sinks and orders each contact', { skip: !enabled }, async () => { + const repository = new PrismaClient(); + const inbox = new PrismaInboundInbox(repository as any); + const suffix = randomUUID(); + const instanceId = `worker-test-${suffix}`; + const instanceScope = `worker-instance-${suffix}`; + const sourceCluster = 'local-worker'; + const receiptIds: string[] = []; + + const claim = async (messageId: string, contactScope: string, leaseOwner: string) => { + const result = await inbox.claim( + { + sourceCluster, + instanceScope, + contactScope, + messageId, + classification: 'real', + payloadHash: messageId.padEnd(64, '0').slice(0, 64), + leaseOwner, + leaseSeconds: 60, + messageData: { + key: { id: messageId, remoteJid: contactScope, fromMe: false }, + pushName: 'Teste local', + messageType: 'conversation', + message: { conversation: messageId }, + source: 'unknown', + messageTimestamp: 1, + instanceId, + }, + }, + 'enforce', + ); + receiptIds.push(result.receiptId); + return result; + }; + + try { + await repository.$connect(); + await repository.instance.create({ + data: { id: instanceId, name: `worker-test-${suffix}`, inboundInboxMode: 'enforce' }, + }); + + const first = await claim(`first-${suffix}`, 'contact-a@s.whatsapp.net', 'ingress-first'); + const second = await claim(`second-${suffix}`, 'contact-a@s.whatsapp.net', 'ingress-second'); + const third = await claim(`third-${suffix}`, 'contact-b@s.whatsapp.net', 'ingress-third'); + await repository.inboundReceipt.update({ + where: { id: first.receiptId }, + data: { createdAt: new Date('2026-01-01T00:00:00Z'), leaseExpiresAt: new Date(0) }, + }); + await repository.inboundReceipt.update({ + where: { id: second.receiptId }, + data: { createdAt: new Date('2026-01-01T00:00:01Z'), leaseExpiresAt: new Date(0) }, + }); + await repository.inboundReceipt.update({ + where: { id: third.receiptId }, + data: { createdAt: new Date('2026-01-01T00:00:02Z'), leaseExpiresAt: new Date(0) }, + }); + + const [leasedA, leasedB] = await Promise.all([ + inbox.leaseNext(sourceCluster, instanceScope, 'worker-a', 60, 10), + inbox.leaseNext(sourceCluster, instanceScope, 'worker-b', 60, 10), + ]); + const leased = [leasedA, leasedB].filter(Boolean); + assert.equal(leased.length, 2); + assert.deepEqual( + new Set(leased.map((item) => item!.receiptId)), + new Set([first.receiptId, third.receiptId]), + ); + + const oldest = leased.find((item) => item!.receiptId === first.receiptId)!; + assert.equal(await inbox.markSink(oldest.receiptId, 'chatwoot', 'sent', oldest.leaseOwner, oldest.leaseToken), true); + assert.equal(await inbox.markFailed(oldest.receiptId, oldest.leaseOwner, oldest.leaseToken, 'simulated kill', 10, 0), true); + const resumed = await inbox.leaseNext(sourceCluster, instanceScope, 'worker-resume', 60, 10); + assert.equal(resumed?.receiptId, first.receiptId); + assert.equal(resumed?.chatwootState, 'sent'); + assert.equal(resumed?.webhookState, 'pending'); + assert.equal(resumed?.chatbotState, 'pending'); + assert.equal(await inbox.heartbeat(resumed!.receiptId, resumed!.leaseOwner, resumed!.leaseToken, 60), true); + assert.equal(await inbox.heartbeat(resumed!.receiptId, 'stale-owner', resumed!.leaseToken, 60), false); + assert.equal(await inbox.markDone(resumed!.receiptId, resumed!.leaseOwner, resumed!.leaseToken), true); + + const nextSameContact = await inbox.leaseNext(sourceCluster, instanceScope, 'worker-next', 60, 10); + assert.equal(nextSameContact?.receiptId, second.receiptId); + assert.equal( + await inbox.markFailed( + nextSameContact!.receiptId, + nextSameContact!.leaseOwner, + nextSameContact!.leaseToken, + 'terminal failure', + 2, + 0, + ), + true, + ); + assert.equal( + (await repository.inboundReceipt.findUniqueOrThrow({ where: { id: second.receiptId } })).state, + 'dead', + ); + } finally { + await repository.inboundReceipt.deleteMany({ where: { id: { in: receiptIds } } }); + await repository.instance.deleteMany({ where: { id: instanceId } }); + await repository.$disconnect(); + } +}); + +test('stub promotes, collisions quarantine and instance recreation keeps the tombstone', { skip: !enabled }, async () => { + const repository = new PrismaClient(); + const inbox = new PrismaInboundInbox(repository as any); + const suffix = randomUUID(); + const instanceScope = `stable-name-${suffix}`; + const messageId = `stub-real-${suffix}`; + const sourceCluster = 'local-promotion'; + let instanceId = `old-${suffix}`; + let receiptId: string | undefined; + + const messageData = () => ({ + key: { id: messageId, remoteJid: 'contact@s.whatsapp.net', fromMe: false }, + pushName: 'Teste local', + messageType: 'conversation', + message: { conversation: 'conteúdo real' }, + source: 'unknown' as const, + messageTimestamp: 1, + instanceId, + }); + + try { + await repository.$connect(); + await repository.instance.create({ data: { id: instanceId, name: instanceScope, inboundInboxMode: 'enforce' } }); + const stub = await inbox.claim( + { + sourceCluster, + instanceScope, + contactScope: 'contact@s.whatsapp.net', + messageId, + classification: 'stub', + payloadHash: 'a'.repeat(64), + }, + 'enforce', + ); + receiptId = stub.receiptId; + assert.equal(stub.shouldDispatch, false); + + const promoted = await inbox.claim( + { + sourceCluster, + instanceScope, + contactScope: 'contact@s.whatsapp.net', + messageId, + classification: 'real', + payloadHash: 'b'.repeat(64), + messageData: messageData(), + leaseOwner: 'promotion-worker', + leaseSeconds: 60, + }, + 'enforce', + ); + assert.equal(promoted.kind, 'promoted'); + assert.equal(promoted.shouldDispatch, true); + assert.equal(await inbox.markDone(promoted.receiptId, 'promotion-worker', promoted.leaseToken!), true); + + const collision = await inbox.claim( + { + sourceCluster, + instanceScope, + contactScope: 'contact@s.whatsapp.net', + messageId, + classification: 'real', + payloadHash: 'c'.repeat(64), + messageData: messageData(), + leaseOwner: 'collision-worker', + }, + 'enforce', + ); + assert.equal(collision.kind, 'collision'); + assert.equal(collision.shouldDispatch, false); + assert.equal((await repository.inboundReceipt.findUniqueOrThrow({ where: { id: receiptId } })).state, 'quarantined'); + + await repository.instance.delete({ where: { id: instanceId } }); + instanceId = `new-${suffix}`; + await repository.instance.create({ data: { id: instanceId, name: instanceScope, inboundInboxMode: 'enforce' } }); + const replay = await inbox.claim( + { + sourceCluster, + instanceScope, + contactScope: 'contact@s.whatsapp.net', + messageId, + classification: 'real', + payloadHash: 'b'.repeat(64), + messageData: messageData(), + leaseOwner: 'new-instance-worker', + }, + 'enforce', + ); + assert.equal(replay.shouldDispatch, false); + assert.equal(await repository.inboundReceipt.count({ where: { id: receiptId } }), 1); + } finally { + if (receiptId) await repository.inboundReceipt.deleteMany({ where: { id: receiptId } }); + await repository.instance.deleteMany({ where: { id: instanceId } }); + await repository.$disconnect(); + } +}); + +test('historical backfill never queues replay and tombstones migrate with rewritten scope', { skip: !enabled }, async () => { + const repository = new PrismaClient(); + const suffix = randomUUID(); + const instanceId = `backfill-${suffix}`; + const instanceName = `Backfill ${suffix}`; + const sourceCluster = 'local-backfill'; + const targetCluster = 'local-target'; + const targetScope = `Migrated ${suffix}`; + const tempDir = mkdtempSync(join(tmpdir(), 'xmacna-inbox-')); + const manifestPath = join(tempDir, 'tombstones.json'); + + try { + await repository.$connect(); + await repository.instance.create({ data: { id: instanceId, name: instanceName } }); + await repository.message.createMany({ + data: [1, 2].map((index) => ({ + key: { id: `historical-${suffix}-${index}`, remoteJid: 'history@s.whatsapp.net', fromMe: false }, + pushName: 'Histórico', + messageType: 'conversation', + message: { conversation: `histórico ${index}` }, + source: 'unknown', + messageTimestamp: index, + instanceId, + })), + }); + + await backfill(repository, { + command: 'backfill', + sourceCluster, + instanceName, + batchSize: 1, + live: true, + }); + const historical = await repository.inboundReceipt.findMany({ where: { sourceCluster } }); + assert.equal(historical.length, 2); + assert.ok(historical.every((row) => row.state === 'historical_seen')); + assert.ok(historical.every((row) => row.webhookState === 'skipped')); + + await exportTombstones(repository, { + command: 'export', + sourceCluster, + instanceName, + output: manifestPath, + batchSize: 500, + live: false, + }); + assert.equal(statSync(manifestPath).mode & 0o777, 0o600); + await importTombstones(repository, { + command: 'import', + input: manifestPath, + targetCluster, + targetScope, + batchSize: 500, + live: true, + }); + const imported = await repository.inboundReceipt.findMany({ where: { sourceCluster: targetCluster } }); + assert.equal(imported.length, 2); + assert.ok(imported.every((row) => row.instanceScope === targetScope.toLowerCase().replace(/\s+/g, '-'))); + assert.ok(imported.every((row) => row.messageRecordId === null && row.state === 'historical_seen')); + } finally { + await repository.inboundReceipt.deleteMany({ where: { sourceCluster: { in: [sourceCluster, targetCluster] } } }); + await repository.instance.deleteMany({ where: { id: instanceId } }); + await repository.$disconnect(); + rmSync(tempDir, { recursive: true, force: true }); + } +}); diff --git a/test/xmacna-inbound-inbox.test.ts b/test/xmacna-inbound-inbox.test.ts index 9418ec3e4d..6b7169044e 100644 --- a/test/xmacna-inbound-inbox.test.ts +++ b/test/xmacna-inbound-inbox.test.ts @@ -5,12 +5,14 @@ import { classifyInboundMessage, evaluateExistingReceipt, inboundPayloadHash, + normalizeContactScope, normalizeInstanceScope, resolveInboundMode, } from '../src/api/integrations/channel/whatsapp/inboundInbox'; test('normalizes stable instance scope independently from internal instance ids', () => { assert.equal(normalizeInstanceScope(' FÁBI FontesEnergia '), 'fábi-fontesenergia'); + assert.equal(normalizeContactScope(' 5511999999999@S.WHATSAPP.NET '), '5511999999999@s.whatsapp.net'); }); test('classifies real, stub, protocol and internal control payloads', () => { From 808125fe6a2b47eef18817ac583b480641e0d17f Mon Sep 17 00:00:00 2001 From: raffareis Date: Wed, 12 Aug 2026 05:53:42 -0300 Subject: [PATCH 04/19] fix(connection): coalesce reconnects in source (xmacna/elysium#500) --- .../whatsapp/whatsapp.baileys.service.ts | 58 ++++++++++++------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 1f59e4d845..323bdc8ad1 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -233,6 +233,11 @@ async function getVideoDuration(input: Buffer | string | Readable): Promise }; +const xmacnaConnectStates: Map = + (globalThis as any).__xmacnaEvolutionConnectStates || + ((globalThis as any).__xmacnaEvolutionConnectStates = new Map()); + export class BaileysStartupService extends ChannelStartupService { private messageProcessor = new BaileysMessageProcessor(); private readonly inboundInbox: PrismaInboundInbox; @@ -737,35 +742,46 @@ export class BaileysStartupService extends ChannelStartupService { } public async connectToWhatsapp(number?: string): Promise { + const stateKey = normalizeInstanceScope(this.instance.name); + const state = xmacnaConnectStates.get(stateKey) || { lastStartedAt: 0 }; + xmacnaConnectStates.set(stateKey, state); + if (state.promise) { + this.logger.info('XMACNA_PATCH_DEBOUNCE: coalescing concurrent connectToWhatsapp caller'); + return state.promise; + } + const connectPromise = this.connectToWhatsappOnce(number, state); + state.promise = connectPromise; try { - this.loadChatwoot(); - this.loadSettings(); - this.loadWebhook(); - this.loadProxy(); - - // Remontar o messageProcessor para garantir que está funcionando após reconexão - this.messageProcessor.mount({ - onMessageReceive: this.messageHandle['messages.upsert'].bind(this), - }); - - const client = await this.createClient(number); - this.startInboundInboxWorker(); - return client; + return await connectPromise; } catch (error) { this.logger.error(error); throw new InternalServerErrorException(error?.toString()); + } finally { + if (state.promise === connectPromise) state.promise = undefined; } } - public async reloadConnection(): Promise { - try { - const client = await this.createClient(this.phoneNumber); - this.startInboundInboxWorker(); - return client; - } catch (error) { - this.logger.error(error); - throw new InternalServerErrorException(error?.toString()); + private async connectToWhatsappOnce(number: string | undefined, state: XmacnaConnectState): Promise { + const waitMs = Math.max(0, 10_000 - (Date.now() - state.lastStartedAt)); + if (waitMs > 0) { + this.logger.info(`XMACNA_PATCH_DEBOUNCE: delaying reconnect by ${waitMs}ms`); + await delay(waitMs); } + state.lastStartedAt = Date.now(); + this.loadChatwoot(); + this.loadSettings(); + this.loadWebhook(); + this.loadProxy(); + this.messageProcessor.mount({ + onMessageReceive: this.messageHandle['messages.upsert'].bind(this), + }); + const client = await this.createClient(number); + this.startInboundInboxWorker(); + return client; + } + + public async reloadConnection(): Promise { + return this.connectToWhatsapp(this.phoneNumber); } private startInboundInboxWorker(): void { From ba1005957ede3bdc3ebee2b7ad72f6e790c8d9fc Mon Sep 17 00:00:00 2001 From: raffareis Date: Wed, 12 Aug 2026 05:58:26 -0300 Subject: [PATCH 05/19] feat(inbound): add guarded per-instance rollout API (xmacna/elysium#500) --- src/api/controllers/instance.controller.ts | 24 ++++++++++++++++++- src/api/dto/instance.dto.ts | 4 ++++ .../whatsapp/whatsapp.baileys.service.ts | 10 ++++++++ src/api/routes/instance.router.ts | 13 ++++++++-- src/api/services/channel.service.ts | 4 ++++ src/api/services/monitor.service.ts | 2 ++ src/validate/instance.schema.ts | 10 ++++++++ test/xmacna-inbound-inbox.test.ts | 9 +++++++ 8 files changed, 73 insertions(+), 3 deletions(-) diff --git a/src/api/controllers/instance.controller.ts b/src/api/controllers/instance.controller.ts index 6a69106881..df03912b64 100644 --- a/src/api/controllers/instance.controller.ts +++ b/src/api/controllers/instance.controller.ts @@ -1,4 +1,4 @@ -import { InstanceDto, SetPresenceDto } from '@api/dto/instance.dto'; +import { InstanceDto, SetInboundInboxModeDto, SetPresenceDto } from '@api/dto/instance.dto'; import { ChatwootService } from '@api/integrations/chatbot/chatwoot/services/chatwoot.service'; import { ProviderFiles } from '@api/provider/sessions'; import { PrismaRepository } from '@api/repository/repository.service'; @@ -70,6 +70,7 @@ export class InstanceController { number: instanceData.number, businessId: instanceData.businessId, status: instanceData.status, + inboundInboxMode: instanceData.inboundInboxMode || 'off', }); instance.setInstance({ @@ -79,6 +80,7 @@ export class InstanceController { token: hash, number: instanceData.number, businessId: instanceData.businessId, + inboundInboxMode: instanceData.inboundInboxMode || 'off', }); this.waMonitor.waInstances[instance.instanceName] = instance; @@ -99,6 +101,7 @@ export class InstanceController { typeof instance.connectionStatus === 'string' ? instance.connectionStatus : instance.connectionStatus?.state || 'unknown', + inboundInboxMode: instance.instance.inboundInboxMode, }; if (instanceData.proxyHost && instanceData.proxyPort && instanceData.proxyProtocol) { @@ -161,6 +164,7 @@ export class InstanceController { instanceName: instance.instanceName, instanceId: instanceId, integration: instanceData.integration, + inboundInboxMode: instance.instance.inboundInboxMode, webhookWaBusiness, accessTokenWaBusiness, status: @@ -306,6 +310,24 @@ export class InstanceController { } } + public async setInboundInboxMode(instanceData: InstanceDto, data: SetInboundInboxModeDto) { + const persisted = await this.prismaRepository.instance.update({ + where: { name: instanceData.instanceName }, + data: { inboundInboxMode: data.mode }, + select: { name: true, inboundInboxMode: true }, + }); + const live = this.waMonitor.waInstances[instanceData.instanceName]; + if (live?.setInboundInboxMode) live.setInboundInboxMode(data.mode); + const readback = await this.prismaRepository.instance.findUniqueOrThrow({ + where: { name: instanceData.instanceName }, + select: { inboundInboxMode: true }, + }); + if (readback.inboundInboxMode !== data.mode) { + throw new InternalServerErrorException('Inbound inbox mode readback mismatch'); + } + return persisted; + } + public async connectToWhatsapp({ instanceName, number = null }: InstanceDto) { try { const instance = this.waMonitor.waInstances[instanceName]; diff --git a/src/api/dto/instance.dto.ts b/src/api/dto/instance.dto.ts index 24865f9673..e905941961 100644 --- a/src/api/dto/instance.dto.ts +++ b/src/api/dto/instance.dto.ts @@ -58,3 +58,7 @@ export class InstanceDto extends IntegrationDto { export class SetPresenceDto { presence: WAPresence; } + +export class SetInboundInboxModeDto { + mode: 'off' | 'shadow' | 'enforce'; +} diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 323bdc8ad1..d4aa7647f2 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -792,6 +792,16 @@ export class BaileysStartupService extends ChannelStartupService { run(); } + public override setInboundInboxMode(mode: 'off' | 'shadow' | 'enforce'): void { + super.setInboundInboxMode(mode); + if (mode === 'enforce') { + this.startInboundInboxWorker(); + } else if (this.inboundWorkerTimer) { + clearInterval(this.inboundWorkerTimer); + this.inboundWorkerTimer = undefined; + } + } + private async runInboundInboxWorker(): Promise { if (this.inboundWorkerRunning || this.instance.inboundInboxMode !== 'enforce') return; this.inboundWorkerRunning = true; diff --git a/src/api/routes/instance.router.ts b/src/api/routes/instance.router.ts index 3559893e68..3c35309b47 100644 --- a/src/api/routes/instance.router.ts +++ b/src/api/routes/instance.router.ts @@ -1,8 +1,8 @@ import { RouterBroker } from '@api/abstract/abstract.router'; -import { InstanceDto, SetPresenceDto } from '@api/dto/instance.dto'; +import { InstanceDto, SetInboundInboxModeDto, SetPresenceDto } from '@api/dto/instance.dto'; import { instanceController } from '@api/server.module'; import { ConfigService } from '@config/env.config'; -import { instanceSchema, presenceOnlySchema } from '@validate/validate.schema'; +import { inboundInboxModeSchema, instanceSchema, presenceOnlySchema } from '@validate/validate.schema'; import { RequestHandler, Router } from 'express'; import { HttpStatus } from './index.router'; @@ -34,6 +34,15 @@ export class InstanceRouter extends RouterBroker { return res.status(HttpStatus.OK).json(response); }) + .patch(this.routerPath('inboundInbox'), ...guards, async (req, res) => { + const response = await this.dataValidate({ + request: req, + schema: inboundInboxModeSchema, + ClassRef: SetInboundInboxModeDto, + execute: (instance, data) => instanceController.setInboundInboxMode(instance, data), + }); + return res.status(HttpStatus.OK).json(response); + }) .get(this.routerPath('connect'), ...guards, async (req, res) => { const response = await this.dataValidate({ request: req, diff --git a/src/api/services/channel.service.ts b/src/api/services/channel.service.ts index 08f8b9c8a5..de6e873ba7 100644 --- a/src/api/services/channel.service.ts +++ b/src/api/services/channel.service.ts @@ -105,6 +105,10 @@ export class ChannelStartupService { this.instance.integration = integration; } + public setInboundInboxMode(mode: 'off' | 'shadow' | 'enforce'): void { + this.instance.inboundInboxMode = mode; + } + public get integration() { return this.instance.integration; } diff --git a/src/api/services/monitor.service.ts b/src/api/services/monitor.service.ts index c3b181cada..dc6073209c 100644 --- a/src/api/services/monitor.service.ts +++ b/src/api/services/monitor.service.ts @@ -255,10 +255,12 @@ export class WAMonitoringService { token: data.hash, clientName: clientName, businessId: data.businessId, + inboundInboxMode: data.inboundInboxMode || 'off', }, }); } catch (error) { this.logger.error(error); + throw error; } } diff --git a/src/validate/instance.schema.ts b/src/validate/instance.schema.ts index a0553b6667..905e923960 100644 --- a/src/validate/instance.schema.ts +++ b/src/validate/instance.schema.ts @@ -31,6 +31,7 @@ export const instanceSchema: JSONSchema7 = { number: { type: 'string', pattern: '^\\d+[\\.@\\w-]+' }, businessId: { type: 'string' }, qrcode: { type: 'boolean' }, + inboundInboxMode: { type: 'string', enum: ['off', 'shadow', 'enforce'] }, Integration: { type: 'string', enum: Object.values(Integration), @@ -227,3 +228,12 @@ export const presenceOnlySchema: JSONSchema7 = { }, required: ['presence'], }; + +export const inboundInboxModeSchema: JSONSchema7 = { + $id: v4(), + type: 'object', + properties: { + mode: { type: 'string', enum: ['off', 'shadow', 'enforce'] }, + }, + required: ['mode'], +}; diff --git a/test/xmacna-inbound-inbox.test.ts b/test/xmacna-inbound-inbox.test.ts index 6b7169044e..69dc40342b 100644 --- a/test/xmacna-inbound-inbox.test.ts +++ b/test/xmacna-inbound-inbox.test.ts @@ -1,5 +1,6 @@ import assert from 'node:assert/strict'; import test from 'node:test'; +import { validate } from 'jsonschema'; import { classifyInboundMessage, @@ -9,6 +10,7 @@ import { normalizeInstanceScope, resolveInboundMode, } from '../src/api/integrations/channel/whatsapp/inboundInbox'; +import { inboundInboxModeSchema, instanceSchema } from '../src/validate/instance.schema'; test('normalizes stable instance scope independently from internal instance ids', () => { assert.equal(normalizeInstanceScope(' FÁBI FontesEnergia '), 'fábi-fontesenergia'); @@ -65,3 +67,10 @@ test('invalid persisted modes fail closed to the configured fallback', () => { assert.equal(resolveInboundMode('invalid', 'shadow'), 'shadow'); assert.equal(resolveInboundMode(undefined, 'off'), 'off'); }); + +test('instance API schemas accept only explicit inbox rollout modes', () => { + assert.equal(validate({ instanceName: 'pilot', inboundInboxMode: 'shadow' }, instanceSchema).valid, true); + assert.equal(validate({ instanceName: 'pilot', inboundInboxMode: 'unsafe' }, instanceSchema).valid, false); + assert.equal(validate({ mode: 'enforce' }, inboundInboxModeSchema).valid, true); + assert.equal(validate({ mode: 'unsafe' }, inboundInboxModeSchema).valid, false); +}); From 4deae55fd2d5f03f12ea24d3d532b0b5a9c1e493 Mon Sep 17 00:00:00 2001 From: raffareis Date: Wed, 12 Aug 2026 06:09:58 -0300 Subject: [PATCH 06/19] feat(inbox): expose durable inbound health metrics (#500) --- .../migration.sql | 3 + prisma/mysql-schema.prisma | 2 + .../migration.sql | 3 + prisma/postgresql-schema.prisma | 2 + prisma/psql_bouncer-schema.prisma | 2 + src/api/controllers/instance.controller.ts | 44 +++++- .../channel/whatsapp/inboundInbox.ts | 133 +++++++++++------- src/api/routes/instance.router.ts | 9 ++ test/xmacna-inbound-inbox.postgres.test.ts | 15 +- 9 files changed, 160 insertions(+), 53 deletions(-) create mode 100644 prisma/mysql-migrations/20260812100000_add_inbound_observability/migration.sql create mode 100644 prisma/postgresql-migrations/20260812100000_add_inbound_observability/migration.sql diff --git a/prisma/mysql-migrations/20260812100000_add_inbound_observability/migration.sql b/prisma/mysql-migrations/20260812100000_add_inbound_observability/migration.sql new file mode 100644 index 0000000000..31f232a54c --- /dev/null +++ b/prisma/mysql-migrations/20260812100000_add_inbound_observability/migration.sql @@ -0,0 +1,3 @@ +ALTER TABLE `InboundReceipt` +ADD COLUMN `duplicateCount` INTEGER NOT NULL DEFAULT 0, +ADD COLUMN `lastSeenAt` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP; diff --git a/prisma/mysql-schema.prisma b/prisma/mysql-schema.prisma index b56969948a..1e1d53bdbf 100644 --- a/prisma/mysql-schema.prisma +++ b/prisma/mysql-schema.prisma @@ -197,6 +197,7 @@ model InboundReceipt { chatwootState String @default("pending") @db.VarChar(30) chatbotState String @default("pending") @db.VarChar(30) attempts Int @default(0) + duplicateCount Int @default(0) availableAt DateTime @default(now()) @db.Timestamp leaseOwner String? @db.VarChar(100) leaseToken Int @default(0) @@ -204,6 +205,7 @@ model InboundReceipt { lastError String? @db.Text createdAt DateTime @default(dbgenerated("CURRENT_TIMESTAMP")) @db.Timestamp updatedAt DateTime @updatedAt @db.Timestamp + lastSeenAt DateTime @default(now()) @db.Timestamp Message Message? @relation(fields: [messageRecordId], references: [id], onDelete: SetNull) messageRecordId String? @unique diff --git a/prisma/postgresql-migrations/20260812100000_add_inbound_observability/migration.sql b/prisma/postgresql-migrations/20260812100000_add_inbound_observability/migration.sql new file mode 100644 index 0000000000..1fd2aa2f13 --- /dev/null +++ b/prisma/postgresql-migrations/20260812100000_add_inbound_observability/migration.sql @@ -0,0 +1,3 @@ +ALTER TABLE "InboundReceipt" +ADD COLUMN "duplicateCount" INTEGER NOT NULL DEFAULT 0, +ADD COLUMN "lastSeenAt" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP; diff --git a/prisma/postgresql-schema.prisma b/prisma/postgresql-schema.prisma index f66faac6df..cbba469182 100644 --- a/prisma/postgresql-schema.prisma +++ b/prisma/postgresql-schema.prisma @@ -196,6 +196,7 @@ model InboundReceipt { chatwootState String @default("pending") @db.VarChar(30) chatbotState String @default("pending") @db.VarChar(30) attempts Int @default(0) + duplicateCount Int @default(0) availableAt DateTime @default(now()) @db.Timestamp leaseOwner String? @db.VarChar(100) leaseToken Int @default(0) @@ -203,6 +204,7 @@ model InboundReceipt { lastError String? @db.Text createdAt DateTime @default(now()) @db.Timestamp updatedAt DateTime @updatedAt @db.Timestamp + lastSeenAt DateTime @default(now()) @db.Timestamp Message Message? @relation(fields: [messageRecordId], references: [id], onDelete: SetNull) messageRecordId String? @unique diff --git a/prisma/psql_bouncer-schema.prisma b/prisma/psql_bouncer-schema.prisma index d4435041e0..02d58dca98 100644 --- a/prisma/psql_bouncer-schema.prisma +++ b/prisma/psql_bouncer-schema.prisma @@ -196,6 +196,7 @@ model InboundReceipt { chatwootState String @default("pending") @db.VarChar(30) chatbotState String @default("pending") @db.VarChar(30) attempts Int @default(0) + duplicateCount Int @default(0) availableAt DateTime @default(now()) @db.Timestamp leaseOwner String? @db.VarChar(100) leaseToken Int @default(0) @@ -203,6 +204,7 @@ model InboundReceipt { lastError String? @db.Text createdAt DateTime @default(now()) @db.Timestamp updatedAt DateTime @updatedAt @db.Timestamp + lastSeenAt DateTime @default(now()) @db.Timestamp Message Message? @relation(fields: [messageRecordId], references: [id], onDelete: SetNull) messageRecordId String? @unique diff --git a/src/api/controllers/instance.controller.ts b/src/api/controllers/instance.controller.ts index df03912b64..33e19265be 100644 --- a/src/api/controllers/instance.controller.ts +++ b/src/api/controllers/instance.controller.ts @@ -1,4 +1,5 @@ import { InstanceDto, SetInboundInboxModeDto, SetPresenceDto } from '@api/dto/instance.dto'; +import { normalizeInstanceScope } from '@api/integrations/channel/whatsapp/inboundInbox'; import { ChatwootService } from '@api/integrations/chatbot/chatwoot/services/chatwoot.service'; import { ProviderFiles } from '@api/provider/sessions'; import { PrismaRepository } from '@api/repository/repository.service'; @@ -7,7 +8,7 @@ import { CacheService } from '@api/services/cache.service'; import { WAMonitoringService } from '@api/services/monitor.service'; import { SettingsService } from '@api/services/settings.service'; import { Events, Integration, wa } from '@api/types/wa.types'; -import { Auth, Chatwoot, ConfigService, HttpServer, WaBusiness } from '@config/env.config'; +import { Auth, Chatwoot, ConfigService, HttpServer, InboundInbox, WaBusiness } from '@config/env.config'; import { Logger } from '@config/logger.config'; import { BadRequestException, InternalServerErrorException, UnauthorizedException } from '@exceptions'; import { delay } from 'baileys'; @@ -328,6 +329,47 @@ export class InstanceController { return persisted; } + public async inboundInboxStats(instanceData: InstanceDto) { + const instance = await this.prismaRepository.instance.findUniqueOrThrow({ + where: { name: instanceData.instanceName }, + select: { name: true, inboundInboxMode: true }, + }); + const sourceCluster = this.configService.get('INBOUND_INBOX').SOURCE_CLUSTER; + const instanceScope = normalizeInstanceScope(instance.name); + const where = { sourceCluster, instanceScope }; + const [states, totals, oldestPending, webhookFailed, chatwootFailed, chatbotFailed] = await Promise.all([ + this.prismaRepository.inboundReceipt.groupBy({ where, by: ['state'], _count: { _all: true } }), + this.prismaRepository.inboundReceipt.aggregate({ + where, + _count: { _all: true }, + _sum: { duplicateCount: true }, + _max: { lastSeenAt: true }, + }), + this.prismaRepository.inboundReceipt.findFirst({ + where: { ...where, state: { in: ['received', 'processing', 'failed'] } }, + orderBy: { updatedAt: 'asc' }, + select: { updatedAt: true }, + }), + this.prismaRepository.inboundReceipt.count({ where: { ...where, webhookState: 'failed' } }), + this.prismaRepository.inboundReceipt.count({ where: { ...where, chatwootState: 'failed' } }), + this.prismaRepository.inboundReceipt.count({ where: { ...where, chatbotState: 'failed' } }), + ]); + return { + instance: instance.name, + mode: instance.inboundInboxMode, + sourceCluster, + instanceScope, + totals: { receipts: totals._count._all, duplicates: totals._sum.duplicateCount || 0 }, + lastInboundSeenAt: totals._max.lastSeenAt?.toISOString() || null, + states: Object.fromEntries(states.map((row) => [row.state, row._count._all])), + failedSinks: { webhook: webhookFailed, chatwoot: chatwootFailed, chatbot: chatbotFailed }, + oldestPendingAgeSeconds: oldestPending + ? Math.max(0, Math.floor((Date.now() - oldestPending.updatedAt.getTime()) / 1000)) + : 0, + sampledAt: new Date().toISOString(), + }; + } + public async connectToWhatsapp({ instanceName, number = null }: InstanceDto) { try { const instance = this.waMonitor.waInstances[instanceName]; diff --git a/src/api/integrations/channel/whatsapp/inboundInbox.ts b/src/api/integrations/channel/whatsapp/inboundInbox.ts index 5430364591..538fe9fcb3 100644 --- a/src/api/integrations/channel/whatsapp/inboundInbox.ts +++ b/src/api/integrations/channel/whatsapp/inboundInbox.ts @@ -131,6 +131,10 @@ function isUniqueConstraint(error: unknown): boolean { return error instanceof Prisma.PrismaClientKnownRequestError && error.code === 'P2002'; } +function isWriteConflict(error: unknown): boolean { + return error instanceof Prisma.PrismaClientKnownRequestError && error.code === 'P2034'; +} + export class PrismaInboundInbox { constructor(private readonly repository: PrismaRepository) {} @@ -189,6 +193,10 @@ export class PrismaInboundInbox { const kind = evaluateExistingReceipt(existing, input); if (kind === 'duplicate') { + await this.repository.inboundReceipt.update({ + where: { id: existing.id }, + data: { duplicateCount: { increment: 1 }, lastSeenAt: new Date() }, + }); if (input.classification === 'real' && input.leaseOwner && existing.state !== 'done') { const reclaimed = await this.repository.inboundReceipt.updateMany({ where: { @@ -238,59 +246,80 @@ export class PrismaInboundInbox { data: { state: 'quarantined', collisionHash: input.payloadHash, + lastSeenAt: new Date(), lastError: 'message_id_payload_hash_collision', }, }); return { kind, receiptId: existing.id, shouldDispatch: false }; } - return this.repository.$transaction( - async (tx) => { - const current = await tx.inboundReceipt.findUnique({ - where: { sourceCluster_instanceScope_messageId: key }, - }); - if (!current) throw new Error('Inbound receipt disappeared during promotion'); - if (current.classification === 'real') { - const currentKind = current.payloadHash === input.payloadHash ? 'duplicate' : 'collision'; - return { - kind: currentKind, - receiptId: current.id, - messageRecordId: current.messageRecordId ?? undefined, - shouldDispatch: false, - } as InboundClaimResult; - } + for (let attempt = 0; attempt < 3; attempt += 1) { + try { + return await this.repository.$transaction( + async (tx) => { + const current = await tx.inboundReceipt.findUnique({ + where: { sourceCluster_instanceScope_messageId: key }, + }); + if (!current) throw new Error('Inbound receipt disappeared during promotion'); + if (current.classification === 'real') { + const currentKind = current.payloadHash === input.payloadHash ? 'duplicate' : 'collision'; + await tx.inboundReceipt.update({ + where: { id: current.id }, + data: + currentKind === 'duplicate' + ? { duplicateCount: { increment: 1 }, lastSeenAt: new Date() } + : { + state: 'quarantined', + collisionHash: input.payloadHash, + lastSeenAt: new Date(), + lastError: 'message_id_payload_hash_collision', + }, + }); + return { + kind: currentKind, + receiptId: current.id, + messageRecordId: current.messageRecordId ?? undefined, + shouldDispatch: false, + } as InboundClaimResult; + } - let messageRecordId = current.messageRecordId ?? undefined; - if (!messageRecordId && input.messageData) { - const message = await tx.message.create({ data: input.messageData as any }); - messageRecordId = message.id; - } - await tx.inboundReceipt.update({ - where: { id: current.id }, - data: { - classification: 'real', - payloadHash: input.payloadHash, - collisionHash: null, - state: 'processing', - contactScope: input.contactScope, - messageRecordId, - leaseOwner: input.leaseOwner, - attempts: 1, - leaseToken: { increment: 1 }, - leaseExpiresAt: new Date(Date.now() + (input.leaseSeconds ?? 60) * 1000), + let messageRecordId = current.messageRecordId ?? undefined; + if (!messageRecordId && input.messageData) { + const message = await tx.message.create({ data: input.messageData as any }); + messageRecordId = message.id; + } + await tx.inboundReceipt.update({ + where: { id: current.id }, + data: { + classification: 'real', + payloadHash: input.payloadHash, + collisionHash: null, + state: 'processing', + contactScope: input.contactScope, + messageRecordId, + leaseOwner: input.leaseOwner, + attempts: 1, + leaseToken: { increment: 1 }, + leaseExpiresAt: new Date(Date.now() + (input.leaseSeconds ?? 60) * 1000), + lastSeenAt: new Date(), + }, + }); + const promoted = await tx.inboundReceipt.findUniqueOrThrow({ where: { id: current.id } }); + return { + kind: 'promoted', + receiptId: current.id, + messageRecordId, + shouldDispatch: true, + leaseToken: promoted.leaseToken, + }; }, - }); - const promoted = await tx.inboundReceipt.findUniqueOrThrow({ where: { id: current.id } }); - return { - kind: 'promoted', - receiptId: current.id, - messageRecordId, - shouldDispatch: true, - leaseToken: promoted.leaseToken, - }; - }, - { isolationLevel: Prisma.TransactionIsolationLevel.Serializable }, - ); + { isolationLevel: Prisma.TransactionIsolationLevel.Serializable }, + ); + } catch (error) { + if (!isWriteConflict(error) || attempt === 2) throw error; + } + } + throw new Error('Inbound receipt promotion retry exhausted'); } public async markSink( @@ -486,12 +515,22 @@ export class PrismaInboundInbox { if (kind === 'collision') { await this.repository.inboundReceipt.update({ where: { id: existing.id }, - data: { state: 'shadow_collision', collisionHash: input.payloadHash }, + data: { state: 'shadow_collision', collisionHash: input.payloadHash, lastSeenAt: new Date() }, }); } else if (kind === 'promoted') { await this.repository.inboundReceipt.update({ where: { id: existing.id }, - data: { classification: 'real', payloadHash: input.payloadHash, state: 'shadow_promoted' }, + data: { + classification: 'real', + payloadHash: input.payloadHash, + state: 'shadow_promoted', + lastSeenAt: new Date(), + }, + }); + } else { + await this.repository.inboundReceipt.update({ + where: { id: existing.id }, + data: { duplicateCount: { increment: 1 }, lastSeenAt: new Date() }, }); } return { diff --git a/src/api/routes/instance.router.ts b/src/api/routes/instance.router.ts index 3c35309b47..9cc183c965 100644 --- a/src/api/routes/instance.router.ts +++ b/src/api/routes/instance.router.ts @@ -43,6 +43,15 @@ export class InstanceRouter extends RouterBroker { }); return res.status(HttpStatus.OK).json(response); }) + .get(this.routerPath('inboundInboxStats'), ...guards, async (req, res) => { + const response = await this.dataValidate({ + request: req, + schema: null, + ClassRef: InstanceDto, + execute: (instance) => instanceController.inboundInboxStats(instance), + }); + return res.status(HttpStatus.OK).json(response); + }) .get(this.routerPath('connect'), ...guards, async (req, res) => { const response = await this.dataValidate({ request: req, diff --git a/test/xmacna-inbound-inbox.postgres.test.ts b/test/xmacna-inbound-inbox.postgres.test.ts index 7458a2ef19..0180568561 100644 --- a/test/xmacna-inbound-inbox.postgres.test.ts +++ b/test/xmacna-inbound-inbox.postgres.test.ts @@ -60,6 +60,7 @@ test('840 concurrent deliveries create one receipt and one durable message', { s }); assert.equal(receipts.length, 1); assert.ok(receipts[0].messageRecordId); + assert.equal(receipts[0].duplicateCount, 839); assert.equal(await repository.message.count({ where: { instanceId } }), 1); } finally { await repository.inboundReceipt.deleteMany({ where: { instanceScope: `instance-${suffix}` } }); @@ -210,8 +211,7 @@ test('stub promotes, collisions quarantine and instance recreation keeps the tom receiptId = stub.receiptId; assert.equal(stub.shouldDispatch, false); - const promoted = await inbox.claim( - { + const realInput = { sourceCluster, instanceScope, contactScope: 'contact@s.whatsapp.net', @@ -221,9 +221,14 @@ test('stub promotes, collisions quarantine and instance recreation keeps the tom messageData: messageData(), leaseOwner: 'promotion-worker', leaseSeconds: 60, - }, - 'enforce', - ); + } as const; + const concurrentPromotions = await Promise.all([ + inbox.claim(realInput, 'enforce'), + inbox.claim(realInput, 'enforce'), + ]); + const promoted = concurrentPromotions.find((result) => result.kind === 'promoted')!; + assert.equal(concurrentPromotions.filter((result) => result.kind === 'promoted').length, 1); + assert.equal(concurrentPromotions.filter((result) => result.kind === 'duplicate').length, 1); assert.equal(promoted.kind, 'promoted'); assert.equal(promoted.shouldDispatch, true); assert.equal(await inbox.markDone(promoted.receiptId, 'promotion-worker', promoted.leaseToken!), true); From 6a10770476a2be590a86e6502d343216b876f50e Mon Sep 17 00:00:00 2001 From: raffareis Date: Wed, 12 Aug 2026 06:34:49 -0300 Subject: [PATCH 07/19] fix(inbox): harden replay and tenant isolation (#500) --- src/api/controllers/instance.controller.ts | 6 +- .../channel/whatsapp/inboundInbox.ts | 245 +++++++++--------- test/xmacna-inbound-inbox.postgres.test.ts | 91 ++++++- test/xmacna-inbound-inbox.test.ts | 7 +- 4 files changed, 214 insertions(+), 135 deletions(-) diff --git a/src/api/controllers/instance.controller.ts b/src/api/controllers/instance.controller.ts index 33e19265be..e2b2e7f112 100644 --- a/src/api/controllers/instance.controller.ts +++ b/src/api/controllers/instance.controller.ts @@ -347,8 +347,8 @@ export class InstanceController { }), this.prismaRepository.inboundReceipt.findFirst({ where: { ...where, state: { in: ['received', 'processing', 'failed'] } }, - orderBy: { updatedAt: 'asc' }, - select: { updatedAt: true }, + orderBy: { createdAt: 'asc' }, + select: { createdAt: true }, }), this.prismaRepository.inboundReceipt.count({ where: { ...where, webhookState: 'failed' } }), this.prismaRepository.inboundReceipt.count({ where: { ...where, chatwootState: 'failed' } }), @@ -364,7 +364,7 @@ export class InstanceController { states: Object.fromEntries(states.map((row) => [row.state, row._count._all])), failedSinks: { webhook: webhookFailed, chatwoot: chatwootFailed, chatbot: chatbotFailed }, oldestPendingAgeSeconds: oldestPending - ? Math.max(0, Math.floor((Date.now() - oldestPending.updatedAt.getTime()) / 1000)) + ? Math.max(0, Math.floor((Date.now() - oldestPending.createdAt.getTime()) / 1000)) : 0, sampledAt: new Date().toISOString(), }; diff --git a/src/api/integrations/channel/whatsapp/inboundInbox.ts b/src/api/integrations/channel/whatsapp/inboundInbox.ts index 538fe9fcb3..b2f8d8f008 100644 --- a/src/api/integrations/channel/whatsapp/inboundInbox.ts +++ b/src/api/integrations/channel/whatsapp/inboundInbox.ts @@ -71,7 +71,10 @@ function canonicalize(value: unknown): unknown { } export function normalizeInstanceScope(instanceName: string): string { - return instanceName.normalize('NFKC').trim().toLowerCase().replace(/\s+/g, '-'); + // Instance.name is the stable external identity across internal-ID recreation. + // Hash the exact UTF-8 value: cosmetic normalization is not injective and could + // merge distinct tenants such as "Foo"/"foo" or "A B"/"A-B". + return `v1:${createHash('sha256').update(instanceName, 'utf8').digest('hex')}`; } export function normalizeContactScope(remoteJid: string | null | undefined): string { @@ -120,7 +123,7 @@ export function resolveInboundMode( export function evaluateExistingReceipt( existing: ExistingReceipt, incoming: Pick, -): InboundClaimKind { +): Exclude { if (existing.payloadHash === incoming.payloadHash) return 'duplicate'; if (existing.classification === 'stub' && incoming.classification === 'real') return 'promoted'; if (incoming.classification === 'stub') return 'duplicate'; @@ -142,44 +145,56 @@ export class PrismaInboundInbox { if (mode === 'shadow') { return this.observe(input); } - try { - return await this.repository.$transaction( - async (tx) => { - const receipt = await tx.inboundReceipt.create({ - data: { - sourceCluster: input.sourceCluster, - instanceScope: input.instanceScope, - contactScope: input.contactScope, - messageId: input.messageId, - classification: input.classification, - payloadHash: input.payloadHash, - state: input.classification === 'real' ? 'processing' : 'observed', - leaseOwner: input.classification === 'real' ? input.leaseOwner : undefined, - attempts: input.classification === 'real' ? 1 : 0, - leaseToken: input.classification === 'real' ? 1 : 0, - leaseExpiresAt: - input.classification === 'real' ? new Date(Date.now() + (input.leaseSeconds ?? 60) * 1000) : undefined, - }, - }); - let messageRecordId: string | undefined; - if (input.classification === 'real' && input.messageData) { - const message = await tx.message.create({ data: input.messageData as any }); - messageRecordId = message.id; - await tx.inboundReceipt.update({ where: { id: receipt.id }, data: { messageRecordId } }); - } - return { - kind: 'claimed' as const, - receiptId: receipt.id, - messageRecordId, - shouldDispatch: input.classification === 'real', - leaseToken: input.classification === 'real' ? 1 : undefined, - }; - }, - { isolationLevel: Prisma.TransactionIsolationLevel.Serializable }, - ); - } catch (error) { - if (!isUniqueConstraint(error)) throw error; + let uniqueConflict = false; + for (let attempt = 0; attempt < 8; attempt += 1) { + try { + return await this.repository.$transaction( + async (tx) => { + const receipt = await tx.inboundReceipt.create({ + data: { + sourceCluster: input.sourceCluster, + instanceScope: input.instanceScope, + contactScope: input.contactScope, + messageId: input.messageId, + classification: input.classification, + payloadHash: input.payloadHash, + state: input.classification === 'real' ? 'processing' : 'observed', + leaseOwner: input.classification === 'real' ? input.leaseOwner : undefined, + attempts: input.classification === 'real' ? 1 : 0, + leaseToken: input.classification === 'real' ? 1 : 0, + leaseExpiresAt: + input.classification === 'real' + ? new Date(Date.now() + (input.leaseSeconds ?? 60) * 1000) + : undefined, + }, + }); + let messageRecordId: string | undefined; + if (input.classification === 'real' && input.messageData) { + const message = await tx.message.create({ data: input.messageData as any }); + messageRecordId = message.id; + await tx.inboundReceipt.update({ where: { id: receipt.id }, data: { messageRecordId } }); + } + return { + kind: 'claimed' as const, + receiptId: receipt.id, + messageRecordId, + shouldDispatch: input.classification === 'real', + leaseToken: input.classification === 'real' ? 1 : undefined, + }; + }, + { isolationLevel: Prisma.TransactionIsolationLevel.Serializable }, + ); + } catch (error) { + if (isUniqueConstraint(error)) { + uniqueConflict = true; + break; + } + if (!isWriteConflict(error) || attempt === 7) throw error; + const jitterMs = Math.floor(Math.random() * 5); + await new Promise((resolve) => setTimeout(resolve, 2 ** attempt + jitterMs)); + } } + if (!uniqueConflict) throw new Error('Inbound receipt initial claim retry exhausted'); const key = { sourceCluster: input.sourceCluster, @@ -192,70 +207,15 @@ export class PrismaInboundInbox { if (!existing) throw new Error('Inbound receipt disappeared after unique conflict'); const kind = evaluateExistingReceipt(existing, input); - if (kind === 'duplicate') { - await this.repository.inboundReceipt.update({ - where: { id: existing.id }, - data: { duplicateCount: { increment: 1 }, lastSeenAt: new Date() }, - }); - if (input.classification === 'real' && input.leaseOwner && existing.state !== 'done') { - const reclaimed = await this.repository.inboundReceipt.updateMany({ - where: { - id: existing.id, - OR: [{ state: 'failed' }, { leaseExpiresAt: { lt: new Date() } }], - }, - data: { - state: 'processing', - contactScope: input.contactScope, - attempts: { increment: 1 }, - leaseOwner: input.leaseOwner, - leaseToken: { increment: 1 }, - leaseExpiresAt: new Date(Date.now() + (input.leaseSeconds ?? 60) * 1000), - lastError: null, - }, - }); - if (reclaimed.count === 1) { - const current = await this.repository.inboundReceipt.findUniqueOrThrow({ where: { id: existing.id } }); - let messageRecordId = current.messageRecordId ?? undefined; - if (!messageRecordId && input.messageData) { - const message = await this.repository.message.create({ data: input.messageData as any }); - messageRecordId = message.id; - await this.repository.inboundReceipt.update({ - where: { id: existing.id }, - data: { messageRecordId }, - }); - } - return { - kind: 'claimed', - receiptId: existing.id, - messageRecordId, - shouldDispatch: true, - leaseToken: current.leaseToken, - }; - } - } - return { - kind, - receiptId: existing.id, - messageRecordId: existing.messageRecordId ?? undefined, - shouldDispatch: false, - }; - } - if (kind === 'collision') { - await this.repository.inboundReceipt.update({ - where: { id: existing.id }, - data: { - state: 'quarantined', - collisionHash: input.payloadHash, - lastSeenAt: new Date(), - lastError: 'message_id_payload_hash_collision', - }, - }); - return { kind, receiptId: existing.id, shouldDispatch: false }; + if (kind !== 'promoted') { + // A redelivery at ingress never reopens work. Only the durable reconciler may + // lease failed/expired receipts, preserving per-sink completion state. + return this.recordSettledReplay(existing, input, kind); } - for (let attempt = 0; attempt < 3; attempt += 1) { + for (let attempt = 0; attempt < 8; attempt += 1) { try { - return await this.repository.$transaction( + const outcome = await this.repository.$transaction( async (tx) => { const current = await tx.inboundReceipt.findUnique({ where: { sourceCluster_instanceScope_messageId: key }, @@ -263,24 +223,16 @@ export class PrismaInboundInbox { if (!current) throw new Error('Inbound receipt disappeared during promotion'); if (current.classification === 'real') { const currentKind = current.payloadHash === input.payloadHash ? 'duplicate' : 'collision'; - await tx.inboundReceipt.update({ - where: { id: current.id }, - data: - currentKind === 'duplicate' - ? { duplicateCount: { increment: 1 }, lastSeenAt: new Date() } - : { - state: 'quarantined', - collisionHash: input.payloadHash, - lastSeenAt: new Date(), - lastError: 'message_id_payload_hash_collision', - }, - }); return { - kind: currentKind, - receiptId: current.id, - messageRecordId: current.messageRecordId ?? undefined, - shouldDispatch: false, - } as InboundClaimResult; + result: { + kind: currentKind, + receiptId: current.id, + messageRecordId: current.messageRecordId ?? undefined, + shouldDispatch: false, + } as InboundClaimResult, + settledReplay: current, + settledKind: currentKind, + }; } let messageRecordId = current.messageRecordId ?? undefined; @@ -306,22 +258,69 @@ export class PrismaInboundInbox { }); const promoted = await tx.inboundReceipt.findUniqueOrThrow({ where: { id: current.id } }); return { - kind: 'promoted', - receiptId: current.id, - messageRecordId, - shouldDispatch: true, - leaseToken: promoted.leaseToken, + result: { + kind: 'promoted' as const, + receiptId: current.id, + messageRecordId, + shouldDispatch: true, + leaseToken: promoted.leaseToken, + }, + settledReplay: undefined, + settledKind: undefined, }; }, { isolationLevel: Prisma.TransactionIsolationLevel.Serializable }, ); + if (outcome.settledReplay) { + return this.recordSettledReplay( + outcome.settledReplay, + input, + outcome.settledKind as 'duplicate' | 'collision', + ); + } + return outcome.result; } catch (error) { - if (!isWriteConflict(error) || attempt === 2) throw error; + if (!isWriteConflict(error)) throw error; + const raced = await this.repository.inboundReceipt.findUnique({ + where: { sourceCluster_instanceScope_messageId: key }, + }); + if (raced?.classification === 'real') { + const racedKind = raced.payloadHash === input.payloadHash ? 'duplicate' : 'collision'; + return this.recordSettledReplay(raced, input, racedKind); + } + if (attempt === 7) throw error; + const jitterMs = Math.floor(Math.random() * 5); + await new Promise((resolve) => setTimeout(resolve, 2 ** attempt + jitterMs)); } } throw new Error('Inbound receipt promotion retry exhausted'); } + private async recordSettledReplay( + existing: ExistingReceipt, + input: InboundClaimInput, + kind: 'duplicate' | 'collision', + ): Promise { + await this.repository.inboundReceipt.update({ + where: { id: existing.id }, + data: + kind === 'duplicate' + ? { duplicateCount: { increment: 1 }, lastSeenAt: new Date() } + : { + state: 'quarantined', + collisionHash: input.payloadHash, + lastSeenAt: new Date(), + lastError: 'message_id_payload_hash_collision', + }, + }); + return { + kind, + receiptId: existing.id, + messageRecordId: existing.messageRecordId ?? undefined, + shouldDispatch: false, + }; + } + public async markSink( receiptId: string, sink: 'webhook' | 'chatwoot' | 'chatbot', diff --git a/test/xmacna-inbound-inbox.postgres.test.ts b/test/xmacna-inbound-inbox.postgres.test.ts index 0180568561..418aed2a2e 100644 --- a/test/xmacna-inbound-inbox.postgres.test.ts +++ b/test/xmacna-inbound-inbox.postgres.test.ts @@ -7,11 +7,70 @@ import { join } from 'node:path'; import { PrismaClient } from '@prisma/client'; -import { PrismaInboundInbox } from '../src/api/integrations/channel/whatsapp/inboundInbox'; +import { + normalizeInstanceScope, + PrismaInboundInbox, +} from '../src/api/integrations/channel/whatsapp/inboundInbox'; import { backfill, exportTombstones, importTombstones } from '../src/cli/inboundInboxMaintenance'; const enabled = process.env.XMACNA_INBOX_INTEGRATION === '1'; +test('raw instance names that used to normalize alike remain tenant-isolated', { skip: !enabled }, async () => { + const repository = new PrismaClient(); + const inbox = new PrismaInboundInbox(repository as any); + const suffix = randomUUID(); + const names = [`Tenant ${suffix}`, `Tenant-${suffix}`]; + const instanceIds = names.map((_, index) => `scope-${index}-${suffix}`); + const sourceCluster = 'local-scope-isolation'; + const messageId = `same-wamid-${suffix}`; + + try { + await repository.$connect(); + await repository.instance.createMany({ + data: names.map((name, index) => ({ id: instanceIds[index], name, inboundInboxMode: 'enforce' })), + }); + const results = await Promise.all( + names.map((name, index) => + inbox.claim( + { + sourceCluster, + instanceScope: normalizeInstanceScope(name), + contactScope: 'contact@s.whatsapp.net', + messageId, + classification: 'real', + payloadHash: 'a'.repeat(64), + leaseOwner: `scope-worker-${index}`, + messageData: { + key: { id: messageId, remoteJid: 'contact@s.whatsapp.net', fromMe: false }, + pushName: 'Tenant isolation', + messageType: 'conversation', + message: { conversation: name }, + source: 'unknown', + messageTimestamp: 1, + instanceId: instanceIds[index], + }, + }, + 'enforce', + ), + ), + ); + assert.deepEqual( + results.map((result) => result.shouldDispatch), + [true, true], + ); + assert.equal( + await repository.inboundReceipt.count({ + where: { sourceCluster, messageId }, + }), + 2, + ); + } finally { + await repository.inboundReceipt.deleteMany({ where: { sourceCluster, messageId } }); + await repository.instance.deleteMany({ where: { id: { in: instanceIds } } }); + await repository.$disconnect(); + } +}); + test('840 concurrent deliveries create one receipt and one durable message', { skip: !enabled }, async () => { const repository = new PrismaClient(); const inbox = new PrismaInboundInbox(repository as any); @@ -62,6 +121,19 @@ test('840 concurrent deliveries create one receipt and one durable message', { s assert.ok(receipts[0].messageRecordId); assert.equal(receipts[0].duplicateCount, 839); assert.equal(await repository.message.count({ where: { instanceId } }), 1); + + const winner = results.find((result) => result.shouldDispatch)!; + assert.equal(await inbox.markSink(winner.receiptId, 'chatwoot', 'sent', input.leaseOwner, winner.leaseToken), true); + assert.equal( + await inbox.markFailed(winner.receiptId, input.leaseOwner, winner.leaseToken!, 'webhook failed', 10, 0), + true, + ); + const immediateRetry = await inbox.claim({ ...input, leaseOwner: `retry-${suffix}` }, 'enforce'); + assert.equal(immediateRetry.kind, 'duplicate'); + assert.equal(immediateRetry.shouldDispatch, false); + const failed = await repository.inboundReceipt.findUniqueOrThrow({ where: { id: winner.receiptId } }); + assert.equal(failed.state, 'failed'); + assert.equal(failed.chatwootState, 'sent'); } finally { await repository.inboundReceipt.deleteMany({ where: { instanceScope: `instance-${suffix}` } }); await repository.instance.deleteMany({ where: { id: instanceId } }); @@ -222,13 +294,12 @@ test('stub promotes, collisions quarantine and instance recreation keeps the tom leaseOwner: 'promotion-worker', leaseSeconds: 60, } as const; - const concurrentPromotions = await Promise.all([ - inbox.claim(realInput, 'enforce'), - inbox.claim(realInput, 'enforce'), - ]); + const concurrentPromotions = await Promise.all( + Array.from({ length: 100 }, () => inbox.claim(realInput, 'enforce')), + ); const promoted = concurrentPromotions.find((result) => result.kind === 'promoted')!; assert.equal(concurrentPromotions.filter((result) => result.kind === 'promoted').length, 1); - assert.equal(concurrentPromotions.filter((result) => result.kind === 'duplicate').length, 1); + assert.equal(concurrentPromotions.filter((result) => result.kind === 'duplicate').length, 99); assert.equal(promoted.kind, 'promoted'); assert.equal(promoted.shouldDispatch, true); assert.equal(await inbox.markDone(promoted.receiptId, 'promotion-worker', promoted.leaseToken!), true); @@ -249,6 +320,10 @@ test('stub promotes, collisions quarantine and instance recreation keeps the tom assert.equal(collision.kind, 'collision'); assert.equal(collision.shouldDispatch, false); assert.equal((await repository.inboundReceipt.findUniqueOrThrow({ where: { id: receiptId } })).state, 'quarantined'); + await repository.inboundReceipt.update({ + where: { id: receiptId }, + data: { leaseExpiresAt: new Date(0) }, + }); await repository.instance.delete({ where: { id: instanceId } }); instanceId = `new-${suffix}`; @@ -267,6 +342,8 @@ test('stub promotes, collisions quarantine and instance recreation keeps the tom 'enforce', ); assert.equal(replay.shouldDispatch, false); + const quarantined = await repository.inboundReceipt.findUniqueOrThrow({ where: { id: receiptId } }); + assert.equal(quarantined.state, 'quarantined'); assert.equal(await repository.inboundReceipt.count({ where: { id: receiptId } }), 1); } finally { if (receiptId) await repository.inboundReceipt.deleteMany({ where: { id: receiptId } }); @@ -332,7 +409,7 @@ test('historical backfill never queues replay and tombstones migrate with rewrit }); const imported = await repository.inboundReceipt.findMany({ where: { sourceCluster: targetCluster } }); assert.equal(imported.length, 2); - assert.ok(imported.every((row) => row.instanceScope === targetScope.toLowerCase().replace(/\s+/g, '-'))); + assert.ok(imported.every((row) => row.instanceScope === normalizeInstanceScope(targetScope))); assert.ok(imported.every((row) => row.messageRecordId === null && row.state === 'historical_seen')); } finally { await repository.inboundReceipt.deleteMany({ where: { sourceCluster: { in: [sourceCluster, targetCluster] } } }); diff --git a/test/xmacna-inbound-inbox.test.ts b/test/xmacna-inbound-inbox.test.ts index 69dc40342b..c6041f31ba 100644 --- a/test/xmacna-inbound-inbox.test.ts +++ b/test/xmacna-inbound-inbox.test.ts @@ -12,8 +12,11 @@ import { } from '../src/api/integrations/channel/whatsapp/inboundInbox'; import { inboundInboxModeSchema, instanceSchema } from '../src/validate/instance.schema'; -test('normalizes stable instance scope independently from internal instance ids', () => { - assert.equal(normalizeInstanceScope(' FÁBI FontesEnergia '), 'fábi-fontesenergia'); +test('derives stable tenant scopes without merging distinct raw instance names', () => { + assert.equal(normalizeInstanceScope('Fabi'), normalizeInstanceScope('Fabi')); + assert.notEqual(normalizeInstanceScope('Foo'), normalizeInstanceScope('foo')); + assert.notEqual(normalizeInstanceScope('A B'), normalizeInstanceScope('A-B')); + assert.match(normalizeInstanceScope(' FÁBI FontesEnergia '), /^v1:[0-9a-f]{64}$/); assert.equal(normalizeContactScope(' 5511999999999@S.WHATSAPP.NET '), '5511999999999@s.whatsapp.net'); }); From 12c975f2a81eea354f973720bf5949fd48591a4f Mon Sep 17 00:00:00 2001 From: raffareis Date: Wed, 12 Aug 2026 06:57:17 -0300 Subject: [PATCH 08/19] fix(inbox): preserve enriched payload across retries (#500) --- .../migration.sql | 1 + prisma/mysql-schema.prisma | 1 + .../migration.sql | 1 + prisma/postgresql-schema.prisma | 1 + prisma/psql_bouncer-schema.prisma | 1 + .../channel/whatsapp/inboundInbox.ts | 50 +++++++-- .../whatsapp/whatsapp.baileys.service.ts | 70 ++++++++++-- test/xmacna-inbound-inbox.postgres.test.ts | 103 ++++++++++++++++++ 8 files changed, 209 insertions(+), 19 deletions(-) create mode 100644 prisma/mysql-migrations/20260812110000_add_inbound_delivery_snapshot/migration.sql create mode 100644 prisma/postgresql-migrations/20260812110000_add_inbound_delivery_snapshot/migration.sql diff --git a/prisma/mysql-migrations/20260812110000_add_inbound_delivery_snapshot/migration.sql b/prisma/mysql-migrations/20260812110000_add_inbound_delivery_snapshot/migration.sql new file mode 100644 index 0000000000..3d46ecfd79 --- /dev/null +++ b/prisma/mysql-migrations/20260812110000_add_inbound_delivery_snapshot/migration.sql @@ -0,0 +1 @@ +ALTER TABLE `Message` ADD COLUMN `pollUpdates` JSON NULL; diff --git a/prisma/mysql-schema.prisma b/prisma/mysql-schema.prisma index 1e1d53bdbf..b6b4dc554d 100644 --- a/prisma/mysql-schema.prisma +++ b/prisma/mysql-schema.prisma @@ -160,6 +160,7 @@ model Message { participant String? @db.VarChar(100) messageType String @db.VarChar(100) message Json @db.Json + pollUpdates Json? @db.Json contextInfo Json? @db.Json source DeviceMessage messageTimestamp Int @db.Int diff --git a/prisma/postgresql-migrations/20260812110000_add_inbound_delivery_snapshot/migration.sql b/prisma/postgresql-migrations/20260812110000_add_inbound_delivery_snapshot/migration.sql new file mode 100644 index 0000000000..ab0e4c6019 --- /dev/null +++ b/prisma/postgresql-migrations/20260812110000_add_inbound_delivery_snapshot/migration.sql @@ -0,0 +1 @@ +ALTER TABLE "Message" ADD COLUMN "pollUpdates" JSONB; diff --git a/prisma/postgresql-schema.prisma b/prisma/postgresql-schema.prisma index cbba469182..590f1ed35f 100644 --- a/prisma/postgresql-schema.prisma +++ b/prisma/postgresql-schema.prisma @@ -160,6 +160,7 @@ model Message { participant String? @db.VarChar(100) messageType String @db.VarChar(100) message Json @db.JsonB + pollUpdates Json? @db.JsonB contextInfo Json? @db.JsonB source DeviceMessage messageTimestamp Int @db.Integer diff --git a/prisma/psql_bouncer-schema.prisma b/prisma/psql_bouncer-schema.prisma index 02d58dca98..6161c32a4d 100644 --- a/prisma/psql_bouncer-schema.prisma +++ b/prisma/psql_bouncer-schema.prisma @@ -160,6 +160,7 @@ model Message { participant String? @db.VarChar(100) messageType String @db.VarChar(100) message Json @db.JsonB + pollUpdates Json? @db.JsonB contextInfo Json? @db.JsonB source DeviceMessage messageTimestamp Int @db.Integer diff --git a/src/api/integrations/channel/whatsapp/inboundInbox.ts b/src/api/integrations/channel/whatsapp/inboundInbox.ts index b2f8d8f008..8d3fa9ea62 100644 --- a/src/api/integrations/channel/whatsapp/inboundInbox.ts +++ b/src/api/integrations/channel/whatsapp/inboundInbox.ts @@ -327,16 +327,52 @@ export class PrismaInboundInbox { state: 'sent' | 'skipped' | 'failed', leaseOwner?: string, leaseToken?: number, + messageData?: Record, ): Promise { const field = `${sink}State` as 'webhookState' | 'chatwootState' | 'chatbotState'; - const updated = await this.repository.inboundReceipt.updateMany({ - where: { - id: receiptId, - ...(leaseOwner && leaseToken !== undefined ? { leaseOwner, leaseToken, state: 'processing' } : {}), - }, - data: { [field]: state }, + return this.repository.$transaction(async (tx) => { + const updated = await tx.inboundReceipt.updateMany({ + where: { + id: receiptId, + ...(leaseOwner && leaseToken !== undefined ? { leaseOwner, leaseToken, state: 'processing' } : {}), + }, + data: { [field]: state }, + }); + if (updated.count !== 1) return false; + if (messageData) { + const receipt = await tx.inboundReceipt.findUnique({ + where: { id: receiptId }, + select: { messageRecordId: true }, + }); + if (!receipt?.messageRecordId) throw new Error(`Inbound receipt ${receiptId} has no durable Message`); + await tx.message.update({ where: { id: receipt.messageRecordId }, data: messageData as any }); + } + return true; + }); + } + + public async persistMessage( + receiptId: string, + leaseOwner: string, + leaseToken: number, + messageData: Record, + ): Promise { + return this.repository.$transaction(async (tx) => { + // The no-op assignment still takes the receipt row lock and proves that + // this worker owns the current fencing token before changing the payload. + const fenced = await tx.inboundReceipt.updateMany({ + where: { id: receiptId, leaseOwner, leaseToken, state: 'processing' }, + data: { leaseOwner }, + }); + if (fenced.count !== 1) return false; + const receipt = await tx.inboundReceipt.findUnique({ + where: { id: receiptId }, + select: { messageRecordId: true }, + }); + if (!receipt?.messageRecordId) throw new Error(`Inbound receipt ${receiptId} has no durable Message`); + await tx.message.update({ where: { id: receipt.messageRecordId }, data: messageData as any }); + return true; }); - return updated.count === 1; } public async heartbeat( diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index d4aa7647f2..48b76e67b8 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -851,8 +851,19 @@ export class BaileysStartupService extends ChannelStartupService { ); heartbeat.unref(); - const markSink = async (sink: 'webhook' | 'chatwoot' | 'chatbot', state: 'sent' | 'skipped' | 'failed') => { - const marked = await this.inboundInbox.markSink(item.receiptId, sink, state, item.leaseOwner, item.leaseToken); + const markSink = async ( + sink: 'webhook' | 'chatwoot' | 'chatbot', + state: 'sent' | 'skipped' | 'failed', + messageData?: Record, + ) => { + const marked = await this.inboundInbox.markSink( + item.receiptId, + sink, + state, + item.leaseOwner, + item.leaseToken, + messageData, + ); if (!marked) throw new Error(`Stale inbound lease while marking ${sink}`); }; @@ -864,14 +875,19 @@ export class BaileysStartupService extends ChannelStartupService { this.localChatwoot?.enabled && !messageRaw.key?.id?.includes('@broadcast') ) { - await this.chatwootService.eventWhatsapp( + const chatwootSentMessage = await this.chatwootService.eventWhatsapp( Events.MESSAGES_UPSERT, { instanceName: this.instance.name, instanceId: this.instanceId }, messageRaw, ); - await markSink('chatwoot', 'sent'); + if (chatwootSentMessage?.id) { + messageRaw.chatwootMessageId = chatwootSentMessage.id; + messageRaw.chatwootInboxId = chatwootSentMessage.inbox_id; + messageRaw.chatwootConversationId = chatwootSentMessage.conversation_id; + } + await markSink('chatwoot', 'sent', messageRaw); } else { - await markSink('chatwoot', 'skipped'); + await markSink('chatwoot', 'skipped', messageRaw); } activeSink = undefined; } @@ -879,7 +895,7 @@ export class BaileysStartupService extends ChannelStartupService { if (!['sent', 'skipped'].includes(item.webhookState)) { activeSink = 'webhook'; await this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw); - await markSink('webhook', 'sent'); + await markSink('webhook', 'sent', messageRaw); activeSink = undefined; } @@ -891,7 +907,7 @@ export class BaileysStartupService extends ChannelStartupService { msg: messageRaw, pushName: messageRaw.pushName, }); - await markSink('chatbot', 'sent'); + await markSink('chatbot', 'sent', messageRaw); activeSink = undefined; } @@ -911,11 +927,32 @@ export class BaileysStartupService extends ChannelStartupService { receipt: { id: string; leaseOwner: string; leaseToken: number }, sink: 'webhook' | 'chatwoot' | 'chatbot', state: 'sent' | 'skipped' | 'failed', + messageData?: Record, ): Promise { - const marked = await this.inboundInbox.markSink(receipt.id, sink, state, receipt.leaseOwner, receipt.leaseToken); + const marked = await this.inboundInbox.markSink( + receipt.id, + sink, + state, + receipt.leaseOwner, + receipt.leaseToken, + messageData, + ); if (!marked) throw new Error(`Stale inbound lease while marking ${sink} for ${receipt.id}`); } + private async persistActiveInboundMessage( + receipt: { id: string; leaseOwner: string; leaseToken: number }, + messageData: Record, + ): Promise { + const persisted = await this.inboundInbox.persistMessage( + receipt.id, + receipt.leaseOwner, + receipt.leaseToken, + messageData, + ); + if (!persisted) throw new Error(`Stale inbound lease while persisting payload for ${receipt.id}`); + } + private async completeActiveInbound(receipt: { id: string; leaseOwner: string; leaseToken: number }): Promise { const done = await this.inboundInbox.markDone(receipt.id, receipt.leaseOwner, receipt.leaseToken); if (!done) throw new Error(`Stale inbound lease while completing ${receipt.id}`); @@ -1573,6 +1610,11 @@ export class BaileysStartupService extends ChannelStartupService { await this.client.readMessages([received.key]); } + // Persist every deterministic enrichment before the first external + // sink. A recovered Chatwoot attempt must see the same poll payload + // as the original attempt, not the pre-enrichment claim snapshot. + if (activeReceipt) await this.persistActiveInboundMessage(activeReceipt, messageRaw); + if ( this.configService.get('CHATWOOT').ENABLED && this.localChatwoot?.enabled && @@ -1590,10 +1632,10 @@ export class BaileysStartupService extends ChannelStartupService { messageRaw.chatwootInboxId = chatwootSentMessage.inbox_id; messageRaw.chatwootConversationId = chatwootSentMessage.conversation_id; } - if (activeReceipt) await this.markActiveInboundSink(activeReceipt, 'chatwoot', 'sent'); + if (activeReceipt) await this.markActiveInboundSink(activeReceipt, 'chatwoot', 'sent', messageRaw); activeSink = undefined; } else if (activeReceipt) { - await this.markActiveInboundSink(activeReceipt, 'chatwoot', 'skipped'); + await this.markActiveInboundSink(activeReceipt, 'chatwoot', 'skipped', messageRaw); } if (this.configService.get('OPENAI').ENABLED && received?.message?.audioMessage) { @@ -1736,9 +1778,13 @@ export class BaileysStartupService extends ChannelStartupService { } console.log(messageRaw); + // Base64/media and PN/LID rewriting happen after Chatwoot. Snapshot + // them durably before webhook/n8n so any retry is payload-equivalent. + if (activeReceipt) await this.persistActiveInboundMessage(activeReceipt, messageRaw); + activeSink = 'webhook'; await this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw); - if (activeReceipt) await this.markActiveInboundSink(activeReceipt, 'webhook', 'sent'); + if (activeReceipt) await this.markActiveInboundSink(activeReceipt, 'webhook', 'sent', messageRaw); activeSink = undefined; activeSink = 'chatbot'; @@ -1748,7 +1794,7 @@ export class BaileysStartupService extends ChannelStartupService { msg: messageRaw, pushName: messageRaw.pushName, }); - if (activeReceipt) await this.markActiveInboundSink(activeReceipt, 'chatbot', 'sent'); + if (activeReceipt) await this.markActiveInboundSink(activeReceipt, 'chatbot', 'sent', messageRaw); activeSink = undefined; const contact = await this.prismaRepository.contact.findFirst({ diff --git a/test/xmacna-inbound-inbox.postgres.test.ts b/test/xmacna-inbound-inbox.postgres.test.ts index 418aed2a2e..79d3f02266 100644 --- a/test/xmacna-inbound-inbox.postgres.test.ts +++ b/test/xmacna-inbound-inbox.postgres.test.ts @@ -141,6 +141,109 @@ test('840 concurrent deliveries create one receipt and one durable message', { s } }); +test('Chatwoot success followed by webhook failure replays the exact enriched payload', { skip: !enabled }, async () => { + const repository = new PrismaClient(); + const inbox = new PrismaInboundInbox(repository as any); + const suffix = randomUUID(); + const instanceId = `payload-replay-${suffix}`; + const instanceScope = `payload-replay-scope-${suffix}`; + const sourceCluster = 'local-payload-replay'; + const messageId = `payload-${suffix}`; + + try { + await repository.$connect(); + await repository.instance.create({ + data: { id: instanceId, name: `payload-replay-${suffix}`, inboundInboxMode: 'enforce' }, + }); + const claimed = await inbox.claim( + { + sourceCluster, + instanceScope, + contactScope: '12345@lid', + messageId, + classification: 'real', + payloadHash: 'd'.repeat(64), + leaseOwner: 'payload-ingress', + leaseSeconds: 60, + messageData: { + key: { id: messageId, remoteJid: '12345@lid', remoteJidAlt: '5511999999999@s.whatsapp.net', fromMe: false }, + pushName: 'Payload replay', + messageType: 'pollUpdateMessage', + message: { pollUpdateMessage: { vote: { selectedOptions: ['opcao-a'] } } }, + source: 'unknown', + messageTimestamp: 1, + instanceId, + }, + }, + 'enforce', + ); + + const afterPoll = { + key: { id: messageId, remoteJid: '12345@lid', remoteJidAlt: '5511999999999@s.whatsapp.net', fromMe: false }, + pushName: 'Payload replay', + messageType: 'pollUpdateMessage', + message: { pollUpdateMessage: { vote: { selectedOptions: ['opcao-a'] } } }, + pollUpdates: [{ name: 'opcao-a', voters: ['12345@lid'] }], + source: 'unknown', + messageTimestamp: 1, + instanceId, + }; + assert.equal( + await inbox.persistMessage(claimed.receiptId, 'payload-ingress', claimed.leaseToken!, afterPoll), + true, + ); + + const afterChatwoot = { + ...afterPoll, + chatwootMessageId: 71, + chatwootInboxId: 72, + chatwootConversationId: 73, + }; + assert.equal( + await inbox.markSink( + claimed.receiptId, + 'chatwoot', + 'sent', + 'payload-ingress', + claimed.leaseToken, + afterChatwoot, + ), + true, + ); + + const beforeWebhook = { + ...afterChatwoot, + key: { ...afterChatwoot.key, remoteJid: '5511999999999@s.whatsapp.net' }, + message: { ...afterChatwoot.message, base64: 'YmluYXJ5LW1lZGlh' }, + }; + assert.equal( + await inbox.persistMessage(claimed.receiptId, 'payload-ingress', claimed.leaseToken!, beforeWebhook), + true, + ); + assert.equal( + await inbox.markSink(claimed.receiptId, 'webhook', 'failed', 'payload-ingress', claimed.leaseToken), + true, + ); + assert.equal( + await inbox.markFailed(claimed.receiptId, 'payload-ingress', claimed.leaseToken!, 'webhook failed', 10, 0), + true, + ); + + const replay = await inbox.leaseNext(sourceCluster, instanceScope, 'payload-reconciler', 60, 10); + assert.ok(replay); + assert.equal(replay.chatwootState, 'sent'); + assert.equal(replay.webhookState, 'failed'); + assert.equal((replay.message as any).chatwootConversationId, 73); + assert.equal((replay.message as any).key.remoteJid, '5511999999999@s.whatsapp.net'); + assert.equal((replay.message as any).message.base64, 'YmluYXJ5LW1lZGlh'); + assert.deepEqual((replay.message as any).pollUpdates, afterPoll.pollUpdates); + } finally { + await repository.inboundReceipt.deleteMany({ where: { sourceCluster, instanceScope } }); + await repository.instance.deleteMany({ where: { id: instanceId } }); + await repository.$disconnect(); + } +}); + test('reconciler leases with fencing, preserves completed sinks and orders each contact', { skip: !enabled }, async () => { const repository = new PrismaClient(); const inbox = new PrismaInboundInbox(repository as any); From be2db1ee06f5bdb1f6f30e3daae5f37f7c3ade4c Mon Sep 17 00:00:00 2001 From: raffareis Date: Thu, 13 Aug 2026 00:58:39 -0300 Subject: [PATCH 09/19] fix(inbox): close service-path durability gaps (#500) --- package.json | 2 +- src/api/abstract/abstract.router.ts | 14 +- src/api/abstract/requestIdentity.ts | 7 + src/api/controllers/instance.controller.ts | 27 +- .../channel/whatsapp/inboundInbox.ts | 119 ++++++-- .../channel/whatsapp/inboundSinkDispatch.ts | 31 ++ .../whatsapp/whatsapp.baileys.service.ts | 223 ++++++++++----- .../chatbot/base-chatbot.controller.ts | 6 +- .../chatbot/base-chatbot.service.ts | 8 + .../chatwoot/services/chatwoot.service.ts | 26 +- .../chatwoot/utils/chatwoot-import-helper.ts | 15 + .../chatbot/deliveryErrorPolicy.ts | 3 + .../chatbot/n8n/services/n8n.service.ts | 6 +- .../event/webhook/webhook.controller.ts | 5 +- test/xmacna-inbound-inbox.postgres.test.ts | 267 ++++++++++++++++++ test/xmacna-inbound-inbox.test.ts | 25 +- test/xmacna-inbound-service-contracts.test.ts | 47 +++ test/xmacna-inbound-sink-dispatch.test.ts | 53 ++++ 18 files changed, 776 insertions(+), 108 deletions(-) create mode 100644 src/api/abstract/requestIdentity.ts create mode 100644 src/api/integrations/channel/whatsapp/inboundSinkDispatch.ts create mode 100644 src/api/integrations/chatbot/deliveryErrorPolicy.ts create mode 100644 test/xmacna-inbound-service-contracts.test.ts create mode 100644 test/xmacna-inbound-sink-dispatch.test.ts diff --git a/package.json b/package.json index d8673cd034..49f5038cde 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "dev:server": "tsx watch ./src/main.ts", "test": "tsx watch ./test/all.test.ts", "test:xmacna-compat": "node --test test/xmacna-baileys-compat.test.cjs", - "test:xmacna-inbox": "tsx --test test/xmacna-inbound-inbox.test.ts test/xmacna-baileys-message-processor.test.ts", + "test:xmacna-inbox": "tsx --test test/xmacna-inbound-inbox.test.ts test/xmacna-inbound-sink-dispatch.test.ts test/xmacna-inbound-service-contracts.test.ts test/xmacna-baileys-message-processor.test.ts", "test:xmacna-inbox:postgres": "tsx --test test/xmacna-inbound-inbox.postgres.test.ts", "inbound-inbox:maintenance": "tsx src/cli/inboundInboxMaintenance.ts", "lint": "eslint --fix --ext .ts src", diff --git a/src/api/abstract/abstract.router.ts b/src/api/abstract/abstract.router.ts index e8449a8c8a..e2e790714d 100644 --- a/src/api/abstract/abstract.router.ts +++ b/src/api/abstract/abstract.router.ts @@ -8,6 +8,8 @@ import { Request } from 'express'; import { JSONSchema7 } from 'json-schema'; import { validate } from 'jsonschema'; +import { mergeRequestIdentity } from './requestIdentity'; + type DataValidate = { request: Request; schema: JSONSchema7; @@ -31,11 +33,13 @@ export abstract class RouterBroker { const ref = new ClassRef(); const body = request.body; - const instance = request.params as unknown as InstanceDto; - - if (request?.query && Object.keys(request.query).length > 0) { - Object.assign(instance, request.query); - } + // Route params are the authenticated resource identity and must always win. + // Query fields may carry optional operation data, but can never retarget an + // already-guarded request to another tenant via ?instanceName=... + const instance = mergeRequestIdentity( + request.params, + request?.query && Object.keys(request.query).length > 0 ? (request.query as Record) : undefined, + ) as unknown as InstanceDto; if (request.originalUrl.includes('/instance/create')) { Object.assign(instance, body); diff --git a/src/api/abstract/requestIdentity.ts b/src/api/abstract/requestIdentity.ts new file mode 100644 index 0000000000..25f9effa14 --- /dev/null +++ b/src/api/abstract/requestIdentity.ts @@ -0,0 +1,7 @@ +/** Route params are authenticated identity; query values may never override them. */ +export function mergeRequestIdentity( + params: Record, + query?: Record, +): Record { + return { ...(query || {}), ...params }; +} diff --git a/src/api/controllers/instance.controller.ts b/src/api/controllers/instance.controller.ts index e2b2e7f112..2db16cd994 100644 --- a/src/api/controllers/instance.controller.ts +++ b/src/api/controllers/instance.controller.ts @@ -1,5 +1,9 @@ import { InstanceDto, SetInboundInboxModeDto, SetPresenceDto } from '@api/dto/instance.dto'; -import { normalizeInstanceScope } from '@api/integrations/channel/whatsapp/inboundInbox'; +import { + InboundModeTransitionBlockedError, + normalizeInstanceScope, + transitionInboundMode, +} from '@api/integrations/channel/whatsapp/inboundInbox'; import { ChatwootService } from '@api/integrations/chatbot/chatwoot/services/chatwoot.service'; import { ProviderFiles } from '@api/provider/sessions'; import { PrismaRepository } from '@api/repository/repository.service'; @@ -312,11 +316,19 @@ export class InstanceController { } public async setInboundInboxMode(instanceData: InstanceDto, data: SetInboundInboxModeDto) { - const persisted = await this.prismaRepository.instance.update({ - where: { name: instanceData.instanceName }, - data: { inboundInboxMode: data.mode }, - select: { name: true, inboundInboxMode: true }, - }); + const sourceCluster = this.configService.get('INBOUND_INBOX').SOURCE_CLUSTER; + let persisted; + try { + persisted = await transitionInboundMode( + this.prismaRepository, + instanceData.instanceName, + sourceCluster, + data.mode, + ); + } catch (error) { + if (error instanceof InboundModeTransitionBlockedError) throw new BadRequestException(error.message); + throw error; + } const live = this.waMonitor.waInstances[instanceData.instanceName]; if (live?.setInboundInboxMode) live.setInboundInboxMode(data.mode); const readback = await this.prismaRepository.instance.findUniqueOrThrow({ @@ -343,7 +355,7 @@ export class InstanceController { where, _count: { _all: true }, _sum: { duplicateCount: true }, - _max: { lastSeenAt: true }, + _max: { createdAt: true, lastSeenAt: true }, }), this.prismaRepository.inboundReceipt.findFirst({ where: { ...where, state: { in: ['received', 'processing', 'failed'] } }, @@ -360,6 +372,7 @@ export class InstanceController { sourceCluster, instanceScope, totals: { receipts: totals._count._all, duplicates: totals._sum.duplicateCount || 0 }, + lastReceiptCreatedAt: totals._max.createdAt?.toISOString() || null, lastInboundSeenAt: totals._max.lastSeenAt?.toISOString() || null, states: Object.fromEntries(states.map((row) => [row.state, row._count._all])), failedSinks: { webhook: webhookFailed, chatwoot: chatwootFailed, chatbot: chatbotFailed }, diff --git a/src/api/integrations/channel/whatsapp/inboundInbox.ts b/src/api/integrations/channel/whatsapp/inboundInbox.ts index 8d3fa9ea62..b72c582612 100644 --- a/src/api/integrations/channel/whatsapp/inboundInbox.ts +++ b/src/api/integrations/channel/whatsapp/inboundInbox.ts @@ -14,6 +14,7 @@ export type InboundIdentity = { }; export type InboundClaimInput = InboundIdentity & { + instanceId: string; contactScope: string; classification: InboundClassification; payloadHash: string; @@ -117,7 +118,14 @@ export function resolveInboundMode( fallback: InboundInboxMode, ): InboundInboxMode { if (instanceMode === 'off' || instanceMode === 'shadow' || instanceMode === 'enforce') return instanceMode; - return fallback; + // A global enforce is unsafe: an instance row may still be off while sinks + // start throwing as if a durable receipt existed. Missing/invalid instance + // state therefore fails closed to off; the env may only opt into shadow. + return fallback === 'shadow' ? 'shadow' : 'off'; +} + +export function canTransitionInboundMode(current: string, target: string, incompleteReceipts: number): boolean { + return current !== 'enforce' || target === 'enforce' || incompleteReceipts === 0; } export function evaluateExistingReceipt( @@ -125,8 +133,8 @@ export function evaluateExistingReceipt( incoming: Pick, ): Exclude { if (existing.payloadHash === incoming.payloadHash) return 'duplicate'; - if (existing.classification === 'stub' && incoming.classification === 'real') return 'promoted'; - if (incoming.classification === 'stub') return 'duplicate'; + if (existing.classification !== 'real' && incoming.classification === 'real') return 'promoted'; + if (incoming.classification !== 'real') return 'duplicate'; return 'collision'; } @@ -138,6 +146,56 @@ function isWriteConflict(error: unknown): boolean { return error instanceof Prisma.PrismaClientKnownRequestError && error.code === 'P2034'; } +export class InboundModeTransitionBlockedError extends Error { + constructor(public readonly incompleteReceipts: number) { + super(`Inbound inbox downgrade blocked: ${incompleteReceipts} incomplete receipt(s) must drain first`); + } +} + +export async function transitionInboundMode( + repository: PrismaRepository, + instanceName: string, + sourceCluster: string, + targetMode: InboundInboxMode, +) { + for (let attempt = 0; attempt < 8; attempt += 1) { + try { + return await repository.$transaction( + async (tx) => { + const current = await tx.instance.findUniqueOrThrow({ + where: { name: instanceName }, + select: { name: true, inboundInboxMode: true }, + }); + const incompleteReceipts = + current.inboundInboxMode === 'enforce' && targetMode !== 'enforce' + ? await tx.inboundReceipt.count({ + where: { + sourceCluster, + instanceScope: normalizeInstanceScope(current.name), + state: { in: ['received', 'processing', 'failed'] }, + }, + }) + : 0; + if (!canTransitionInboundMode(current.inboundInboxMode, targetMode, incompleteReceipts)) { + throw new InboundModeTransitionBlockedError(incompleteReceipts); + } + return tx.instance.update({ + where: { name: instanceName }, + data: { inboundInboxMode: targetMode }, + select: { name: true, inboundInboxMode: true }, + }); + }, + { isolationLevel: Prisma.TransactionIsolationLevel.Serializable }, + ); + } catch (error) { + if (!isWriteConflict(error) || attempt === 7) throw error; + const jitterMs = Math.floor(Math.random() * 5); + await new Promise((resolve) => setTimeout(resolve, 2 ** attempt + jitterMs)); + } + } + throw new Error('Inbound inbox mode transition retry exhausted'); +} + export class PrismaInboundInbox { constructor(private readonly repository: PrismaRepository) {} @@ -150,6 +208,13 @@ export class PrismaInboundInbox { try { return await this.repository.$transaction( async (tx) => { + const instance = await tx.instance.findUnique({ + where: { id: input.instanceId }, + select: { inboundInboxMode: true }, + }); + if (instance?.inboundInboxMode !== 'enforce') { + throw new Error(`Inbound inbox mode changed before claim for instance ${input.instanceId}`); + } const receipt = await tx.inboundReceipt.create({ data: { sourceCluster: input.sourceCluster, @@ -178,7 +243,7 @@ export class PrismaInboundInbox { kind: 'claimed' as const, receiptId: receipt.id, messageRecordId, - shouldDispatch: input.classification === 'real', + shouldDispatch: input.classification === 'real' || input.classification === 'protocol', leaseToken: input.classification === 'real' ? 1 : undefined, }; }, @@ -217,6 +282,13 @@ export class PrismaInboundInbox { try { const outcome = await this.repository.$transaction( async (tx) => { + const instance = await tx.instance.findUnique({ + where: { id: input.instanceId }, + select: { inboundInboxMode: true }, + }); + if (instance?.inboundInboxMode !== 'enforce') { + throw new Error(`Inbound inbox mode changed before promotion for instance ${input.instanceId}`); + } const current = await tx.inboundReceipt.findUnique({ where: { sourceCluster_instanceScope_messageId: key }, }); @@ -301,18 +373,28 @@ export class PrismaInboundInbox { input: InboundClaimInput, kind: 'duplicate' | 'collision', ): Promise { - await this.repository.inboundReceipt.update({ - where: { id: existing.id }, - data: - kind === 'duplicate' - ? { duplicateCount: { increment: 1 }, lastSeenAt: new Date() } - : { - state: 'quarantined', - collisionHash: input.payloadHash, - lastSeenAt: new Date(), - lastError: 'message_id_payload_hash_collision', - }, - }); + if (kind === 'duplicate') { + await this.repository.inboundReceipt.update({ + where: { id: existing.id }, + data: { duplicateCount: { increment: 1 }, lastSeenAt: new Date() }, + }); + } else { + // Never revoke an active/failed worker's fencing token by quarantining its + // receipt underneath it. Terminal collisions remain visible, while an + // in-flight collision is suppressed without changing delivery state. + await this.repository.inboundReceipt.updateMany({ + where: { + id: existing.id, + state: { notIn: ['received', 'processing', 'failed'] }, + }, + data: { + state: 'quarantined', + collisionHash: input.payloadHash, + lastSeenAt: new Date(), + lastError: 'message_id_payload_hash_collision', + }, + }); + } return { kind, receiptId: existing.id, @@ -538,7 +620,7 @@ export class PrismaInboundInbox { return { kind: 'claimed', receiptId: created.id, - shouldDispatch: input.classification === 'real', + shouldDispatch: input.classification === 'real' || input.classification === 'protocol', }; } catch (error) { if (!isUniqueConstraint(error)) throw error; @@ -572,7 +654,8 @@ export class PrismaInboundInbox { kind, receiptId: existing.id, messageRecordId: existing.messageRecordId ?? undefined, - shouldDispatch: input.classification === 'real', + // Shadow observes only; it must preserve baseline delivery semantics. + shouldDispatch: input.classification === 'real' || input.classification === 'protocol', }; } } diff --git a/src/api/integrations/channel/whatsapp/inboundSinkDispatch.ts b/src/api/integrations/channel/whatsapp/inboundSinkDispatch.ts new file mode 100644 index 0000000000..487d475e3f --- /dev/null +++ b/src/api/integrations/channel/whatsapp/inboundSinkDispatch.ts @@ -0,0 +1,31 @@ +export type DurableInboundSink = 'webhook' | 'chatwoot' | 'chatbot'; +export type DurableInboundSinkState = 'sent' | 'skipped' | 'failed'; +export type DurableInboundSinkFailure = { sink: DurableInboundSink; error: Error }; + +/** + * Runs one external sink without allowing its delivery failure to block the + * remaining sinks. Persistence/fencing failures still reject because the + * caller can no longer prove ownership of the receipt. + */ +export async function attemptDurableInboundSink(options: { + sink: DurableInboundSink; + operation: () => Promise<'sent' | 'skipped'>; + mark: (sink: DurableInboundSink, state: DurableInboundSinkState) => Promise; + onDeliveryFailure?: (failure: DurableInboundSinkFailure) => void; +}): Promise { + let state: 'sent' | 'skipped'; + try { + state = await options.operation(); + } catch (cause) { + const error = cause instanceof Error ? cause : new Error(String(cause)); + // If this throws, fencing/persistence was lost and must abort the caller. + await options.mark(options.sink, 'failed'); + const failure = { sink: options.sink, error }; + options.onDeliveryFailure?.(failure); + return failure; + } + // Deliberately outside the delivery catch: a persistence/fencing failure is + // not a sink failure and must abort instead of being downgraded to retryable. + await options.mark(options.sink, state); + return undefined; +} diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 48b76e67b8..efc3cce75f 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -162,6 +162,7 @@ import { PrismaInboundInbox, resolveInboundMode, } from './inboundInbox'; +import { attemptDurableInboundSink, DurableInboundSinkFailure } from './inboundSinkDispatch'; import { useVoiceCallsBaileys } from './voiceCalls/useVoiceCallsBaileys'; export interface ExtendedIMessageKey extends proto.IMessageKey { @@ -785,7 +786,7 @@ export class BaileysStartupService extends ChannelStartupService { } private startInboundInboxWorker(): void { - if (this.instance.inboundInboxMode !== 'enforce' || this.inboundWorkerTimer) return; + if (this.currentInboundMode() !== 'enforce' || this.inboundWorkerTimer) return; const run = () => void this.runInboundInboxWorker().catch((error) => this.logger.error(`Inbound worker: ${error}`)); this.inboundWorkerTimer = setInterval(run, 1000); this.inboundWorkerTimer.unref(); @@ -803,7 +804,7 @@ export class BaileysStartupService extends ChannelStartupService { } private async runInboundInboxWorker(): Promise { - if (this.inboundWorkerRunning || this.instance.inboundInboxMode !== 'enforce') return; + if (this.inboundWorkerRunning || this.currentInboundMode() !== 'enforce') return; this.inboundWorkerRunning = true; const config = this.configService.get('INBOUND_INBOX'); const instanceScope = normalizeInstanceScope(this.instance.name); @@ -824,6 +825,11 @@ export class BaileysStartupService extends ChannelStartupService { } } + private currentInboundMode(): 'off' | 'shadow' | 'enforce' { + const fallback = this.configService.get('INBOUND_INBOX').MODE; + return resolveInboundMode(this.instance.inboundInboxMode, fallback); + } + private async dispatchRecoveredInbound( item: { receiptId: string; @@ -837,7 +843,7 @@ export class BaileysStartupService extends ChannelStartupService { config: InboundInbox, ): Promise { const messageRaw = item.message as any; - let activeSink: 'webhook' | 'chatwoot' | 'chatbot' | undefined; + const failures: DurableInboundSinkFailure[] = []; const heartbeat = setInterval( () => { void this.inboundInbox @@ -867,57 +873,82 @@ export class BaileysStartupService extends ChannelStartupService { if (!marked) throw new Error(`Stale inbound lease while marking ${sink}`); }; + const attempt = async ( + sink: 'webhook' | 'chatwoot' | 'chatbot', + currentState: string, + operation: () => Promise<'sent' | 'skipped'>, + ) => { + if (['sent', 'skipped'].includes(currentState)) return; + const failure = await attemptDurableInboundSink({ + sink, + operation, + mark: (markedSink, state) => markSink(markedSink, state, messageRaw), + onDeliveryFailure: ({ error }) => + this.logger.error(`Recovered inbound ${sink} failed for ${item.receiptId}: ${error.message}`), + }); + if (failure) failures.push(failure); + }; + try { - if (!['sent', 'skipped'].includes(item.chatwootState)) { - activeSink = 'chatwoot'; + await attempt('chatwoot', item.chatwootState, async () => { if ( - this.configService.get('CHATWOOT').ENABLED && - this.localChatwoot?.enabled && - !messageRaw.key?.id?.includes('@broadcast') + !this.configService.get('CHATWOOT').ENABLED || + !this.localChatwoot?.enabled || + messageRaw.key?.id?.includes('@broadcast') ) { - const chatwootSentMessage = await this.chatwootService.eventWhatsapp( - Events.MESSAGES_UPSERT, - { instanceName: this.instance.name, instanceId: this.instanceId }, - messageRaw, - ); - if (chatwootSentMessage?.id) { - messageRaw.chatwootMessageId = chatwootSentMessage.id; - messageRaw.chatwootInboxId = chatwootSentMessage.inbox_id; - messageRaw.chatwootConversationId = chatwootSentMessage.conversation_id; - } - await markSink('chatwoot', 'sent', messageRaw); - } else { - await markSink('chatwoot', 'skipped', messageRaw); + return 'skipped'; } - activeSink = undefined; - } + const chatwootSentMessage = await this.chatwootService.eventWhatsapp( + Events.MESSAGES_UPSERT, + { instanceName: this.instance.name, instanceId: this.instanceId }, + messageRaw, + ); + if (chatwootSentMessage?.id) { + messageRaw.chatwootMessageId = chatwootSentMessage.id; + messageRaw.chatwootInboxId = chatwootSentMessage.inbox_id; + messageRaw.chatwootConversationId = chatwootSentMessage.conversation_id; + } + return 'sent'; + }); - if (!['sent', 'skipped'].includes(item.webhookState)) { - activeSink = 'webhook'; + await attempt('webhook', item.webhookState, async () => { await this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw); - await markSink('webhook', 'sent', messageRaw); - activeSink = undefined; - } + return 'sent'; + }); - if (!['sent', 'skipped'].includes(item.chatbotState)) { - activeSink = 'chatbot'; + await attempt('chatbot', item.chatbotState, async () => { await chatbotController.emit({ instance: { instanceName: this.instance.name, instanceId: this.instanceId }, remoteJid: messageRaw.key.remoteJid, msg: messageRaw, pushName: messageRaw.pushName, }); - await markSink('chatbot', 'sent', messageRaw); - activeSink = undefined; - } + return 'sent'; + }); - const done = await this.inboundInbox.markDone(item.receiptId, item.leaseOwner, item.leaseToken); - if (!done) throw new Error(`Stale inbound lease while completing ${item.receiptId}`); - } catch (error) { - if (activeSink) { - await this.inboundInbox.markSink(item.receiptId, activeSink, 'failed', item.leaseOwner, item.leaseToken); + if (failures.length > 0) { + const summary = failures.map(({ sink, error }) => `${sink}: ${error.message}`).join('; '); + const failed = await this.inboundInbox.markFailed( + item.receiptId, + item.leaseOwner, + item.leaseToken, + summary, + config.MAX_ATTEMPTS, + ); + if (!failed) throw new Error(`Stale inbound lease while failing ${item.receiptId}`); + } else { + const done = await this.inboundInbox.markDone(item.receiptId, item.leaseOwner, item.leaseToken); + if (!done) throw new Error(`Stale inbound lease while completing ${item.receiptId}`); } - await this.inboundInbox.markFailed(item.receiptId, item.leaseOwner, item.leaseToken, error, config.MAX_ATTEMPTS); + } catch (error) { + const failed = await this.inboundInbox.markFailed( + item.receiptId, + item.leaseOwner, + item.leaseToken, + error, + config.MAX_ATTEMPTS, + ); + if (!failed) this.logger.error(`Recovered inbound fencing lost while handling ${item.receiptId}: ${error}`); } finally { clearInterval(heartbeat); } @@ -958,6 +989,23 @@ export class BaileysStartupService extends ChannelStartupService { if (!done) throw new Error(`Stale inbound lease while completing ${receipt.id}`); } + private async settleActiveInbound( + receipt: { id: string; leaseOwner: string; leaseToken: number }, + failures: Array<{ sink: 'webhook' | 'chatwoot' | 'chatbot'; error: Error }>, + maxAttempts: number, + ): Promise { + if (failures.length === 0) return this.completeActiveInbound(receipt); + const summary = failures.map(({ sink, error }) => `${sink}: ${error.message}`).join('; '); + const marked = await this.inboundInbox.markFailed( + receipt.id, + receipt.leaseOwner, + receipt.leaseToken, + summary, + maxAttempts, + ); + if (!marked) throw new Error(`Stale inbound lease while failing ${receipt.id}`); + } + private readonly chatHandle = { 'chats.upsert': async (chats: Chat[]) => { const existingChatIds = await this.prismaRepository.chat.findMany({ @@ -1297,11 +1345,15 @@ export class BaileysStartupService extends ChannelStartupService { | { id: string; leaseOwner: string; leaseToken: number; mode: 'off' | 'shadow' | 'enforce' } | undefined; let activeSink: 'webhook' | 'chatwoot' | 'chatbot' | undefined; + let activeSinkFailures: DurableInboundSinkFailure[] = []; let heartbeatTimer: NodeJS.Timeout | undefined; + let activeInboundMode: 'off' | 'shadow' | 'enforce' = 'off'; try { for (const received of messages) { + activeSinkFailures = []; const inboundConfig = this.configService.get('INBOUND_INBOX'); const inboundMode = resolveInboundMode(this.instance.inboundInboxMode, inboundConfig.MODE); + activeInboundMode = inboundMode; const inboundClassification = classifyInboundMessage(received); const inboundMessageId = received.key?.id; const inboundContactScope = normalizeContactScope( @@ -1316,15 +1368,22 @@ export class BaileysStartupService extends ChannelStartupService { : undefined; if (inboundMode !== 'off' && inboundIdentity && inboundClassification !== 'real') { - await this.inboundInbox.claim( + const observed = await this.inboundInbox.claim( { ...inboundIdentity, + instanceId: this.instanceId, contactScope: inboundContactScope, classification: inboundClassification, payloadHash: inboundPayloadHash(received), }, inboundMode, ); + if (inboundClassification === 'protocol' && !observed.shouldDispatch) { + this.logger.info( + `Inbound ${observed.kind} protocol suppressed before edit sinks: ${inboundIdentity.instanceScope}/${inboundMessageId}`, + ); + continue; + } } if ( received?.messageStubParameters?.some?.((param) => @@ -1452,6 +1511,7 @@ export class BaileysStartupService extends ChannelStartupService { const claim = await this.inboundInbox.claim( { ...inboundIdentity, + instanceId: this.instanceId, contactScope: inboundContactScope, classification: inboundClassification, payloadHash: inboundPayloadHash(received), @@ -1615,25 +1675,48 @@ export class BaileysStartupService extends ChannelStartupService { // as the original attempt, not the pre-enrichment claim snapshot. if (activeReceipt) await this.persistActiveInboundMessage(activeReceipt, messageRaw); + const attemptActiveSink = async ( + sink: 'webhook' | 'chatwoot' | 'chatbot', + operation: () => Promise<'sent' | 'skipped'>, + ) => { + activeSink = sink; + try { + if (!activeReceipt) { + await operation(); + return; + } + const failure = await attemptDurableInboundSink({ + sink, + operation, + mark: (markedSink, state) => this.markActiveInboundSink(activeReceipt!, markedSink, state, messageRaw), + onDeliveryFailure: ({ error }) => + this.logger.error(`Inbound ${sink} failed for ${activeReceipt!.id}: ${error.message}`), + }); + if (failure) activeSinkFailures.push(failure); + } finally { + activeSink = undefined; + } + }; + if ( this.configService.get('CHATWOOT').ENABLED && this.localChatwoot?.enabled && !received.key.id.includes('@broadcast') ) { - activeSink = 'chatwoot'; - const chatwootSentMessage = await this.chatwootService.eventWhatsapp( - Events.MESSAGES_UPSERT, - { instanceName: this.instance.name, instanceId: this.instanceId }, - messageRaw, - ); + await attemptActiveSink('chatwoot', async () => { + const chatwootSentMessage = await this.chatwootService.eventWhatsapp( + Events.MESSAGES_UPSERT, + { instanceName: this.instance.name, instanceId: this.instanceId }, + messageRaw, + ); - if (chatwootSentMessage?.id) { - messageRaw.chatwootMessageId = chatwootSentMessage.id; - messageRaw.chatwootInboxId = chatwootSentMessage.inbox_id; - messageRaw.chatwootConversationId = chatwootSentMessage.conversation_id; - } - if (activeReceipt) await this.markActiveInboundSink(activeReceipt, 'chatwoot', 'sent', messageRaw); - activeSink = undefined; + if (chatwootSentMessage?.id) { + messageRaw.chatwootMessageId = chatwootSentMessage.id; + messageRaw.chatwootInboxId = chatwootSentMessage.inbox_id; + messageRaw.chatwootConversationId = chatwootSentMessage.conversation_id; + } + return 'sent'; + }); } else if (activeReceipt) { await this.markActiveInboundSink(activeReceipt, 'chatwoot', 'skipped', messageRaw); } @@ -1782,20 +1865,20 @@ export class BaileysStartupService extends ChannelStartupService { // them durably before webhook/n8n so any retry is payload-equivalent. if (activeReceipt) await this.persistActiveInboundMessage(activeReceipt, messageRaw); - activeSink = 'webhook'; - await this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw); - if (activeReceipt) await this.markActiveInboundSink(activeReceipt, 'webhook', 'sent', messageRaw); - activeSink = undefined; - - activeSink = 'chatbot'; - await chatbotController.emit({ - instance: { instanceName: this.instance.name, instanceId: this.instanceId }, - remoteJid: messageRaw.key.remoteJid, - msg: messageRaw, - pushName: messageRaw.pushName, + await attemptActiveSink('webhook', async () => { + await this.sendDataWebhook(Events.MESSAGES_UPSERT, messageRaw); + return 'sent'; + }); + + await attemptActiveSink('chatbot', async () => { + await chatbotController.emit({ + instance: { instanceName: this.instance.name, instanceId: this.instanceId }, + remoteJid: messageRaw.key.remoteJid, + msg: messageRaw, + pushName: messageRaw.pushName, + }); + return 'sent'; }); - if (activeReceipt) await this.markActiveInboundSink(activeReceipt, 'chatbot', 'sent', messageRaw); - activeSink = undefined; const contact = await this.prismaRepository.contact.findFirst({ where: { remoteJid: received.key.remoteJid, instanceId: this.instanceId }, @@ -1815,7 +1898,7 @@ export class BaileysStartupService extends ChannelStartupService { if (contactRaw.remoteJid === 'status@broadcast') { if (activeReceipt) { - await this.completeActiveInbound(activeReceipt); + await this.settleActiveInbound(activeReceipt, activeSinkFailures, inboundConfig.MAX_ATTEMPTS); if (heartbeatTimer) clearInterval(heartbeatTimer); heartbeatTimer = undefined; activeReceipt = undefined; @@ -1853,7 +1936,7 @@ export class BaileysStartupService extends ChannelStartupService { }); if (activeReceipt) { - await this.completeActiveInbound(activeReceipt); + await this.settleActiveInbound(activeReceipt, activeSinkFailures, inboundConfig.MAX_ATTEMPTS); if (heartbeatTimer) clearInterval(heartbeatTimer); heartbeatTimer = undefined; activeReceipt = undefined; @@ -1870,7 +1953,7 @@ export class BaileysStartupService extends ChannelStartupService { create: contactRaw, }); if (activeReceipt) { - await this.completeActiveInbound(activeReceipt); + await this.settleActiveInbound(activeReceipt, activeSinkFailures, inboundConfig.MAX_ATTEMPTS); if (heartbeatTimer) clearInterval(heartbeatTimer); heartbeatTimer = undefined; activeReceipt = undefined; @@ -1891,7 +1974,7 @@ export class BaileysStartupService extends ChannelStartupService { ); } this.logger.error(error); - throw error; + if (activeInboundMode === 'enforce') throw error; } }, diff --git a/src/api/integrations/chatbot/base-chatbot.controller.ts b/src/api/integrations/chatbot/base-chatbot.controller.ts index dff212ba3a..471b8b7ff0 100644 --- a/src/api/integrations/chatbot/base-chatbot.controller.ts +++ b/src/api/integrations/chatbot/base-chatbot.controller.ts @@ -1,5 +1,6 @@ import { IgnoreJidDto } from '@api/dto/chatbot.dto'; import { InstanceDto } from '@api/dto/instance.dto'; +import { resolveInboundMode } from '@api/integrations/channel/whatsapp/inboundInbox'; import { PrismaRepository } from '@api/repository/repository.service'; import { WAMonitoringService } from '@api/services/monitor.service'; import { Events } from '@api/types/wa.types'; @@ -951,7 +952,10 @@ export abstract class BaseChatbotController { protected readonly prismaRepository: PrismaRepository; protected readonly configService?: ConfigService; + /** Integrations with durable delivery may opt into surfacing sink failures. */ + protected shouldPropagateDeliveryErrors(): boolean { + return false; + } + constructor( waMonitor: WAMonitoringService, prismaRepository: PrismaRepository, @@ -167,6 +174,7 @@ export abstract class BaseChatbotService { }); } catch (error) { this.logger.error(`Error in process: ${error}`); + applyDeliveryErrorPolicy(error, this.shouldPropagateDeliveryErrors()); return; } } diff --git a/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts b/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts index 11b8df2804..9d4bb09bc1 100644 --- a/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts +++ b/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts @@ -1,5 +1,6 @@ import { InstanceDto } from '@api/dto/instance.dto'; import { Options, Quoted, SendAudioDto, SendMediaDto, SendTextDto } from '@api/dto/sendMessage.dto'; +import { resolveInboundMode } from '@api/integrations/channel/whatsapp/inboundInbox'; import { ChatwootDto } from '@api/integrations/chatbot/chatwoot/dto/chatwoot.dto'; import { postgresClient } from '@api/integrations/chatbot/chatwoot/libs/postgres.client'; import { chatwootImport } from '@api/integrations/chatbot/chatwoot/utils/chatwoot-import-helper'; @@ -44,6 +45,13 @@ interface ChatwootMessage { export class ChatwootService { private readonly logger = new Logger('ChatwootService'); + private shouldSurfaceInboundFailure(instance?: InstanceDto): boolean { + if (!instance?.instanceName) return false; + const waInstance = this.waMonitor.waInstances[instance.instanceName]; + const fallback = this.configService.get('INBOUND_INBOX').MODE; + return resolveInboundMode(waInstance?.instance?.inboundInboxMode, fallback) === 'enforce'; + } + // Lock polling delay private readonly LOCK_POLLING_DELAY_MS = 300; // Delay between lock status checks @@ -932,10 +940,23 @@ export class ChatwootService { sourceId?: string, quotedMsg?: MessageModel, ) { + if (sourceId && this.isImportHistoryAvailable()) { + try { + const existing = await chatwootImport.getExistingMessageBySourceId(sourceId, conversationId); + if (existing) { + this.logger.warn(`Message ${sourceId} already saved on Chatwoot`); + return { ...existing, xmacnaDuplicate: true } as any; + } + } catch (error) { + this.logger.error(`Unable to reconcile Chatwoot source_id ${sourceId}: ${error}`); + if (this.shouldSurfaceInboundFailure(instance)) throw error; + } + } const client = await this.clientCw(instance); if (!client) { this.logger.warn('client not found'); + if (this.shouldSurfaceInboundFailure(instance)) throw new Error('Chatwoot client unavailable'); return null; } @@ -961,6 +982,7 @@ export class ChatwootService { if (!message) { this.logger.warn('message not found'); + if (this.shouldSurfaceInboundFailure(instance)) throw new Error('Chatwoot message create returned empty'); return null; } @@ -1115,6 +1137,8 @@ export class ChatwootService { return data; } catch (error) { this.logger.error(error); + if (this.shouldSurfaceInboundFailure(instance)) throw error; + return null; } } @@ -2528,7 +2552,7 @@ export class ChatwootService { const inboxDefault = this.configService.get('INBOUND_INBOX').MODE; if ( event === Events.MESSAGES_UPSERT && - (waInstance?.instance?.inboundInboxMode === 'enforce' || inboxDefault === 'enforce') + resolveInboundMode(waInstance?.instance?.inboundInboxMode, inboxDefault) === 'enforce' ) { throw error; } diff --git a/src/api/integrations/chatbot/chatwoot/utils/chatwoot-import-helper.ts b/src/api/integrations/chatbot/chatwoot/utils/chatwoot-import-helper.ts index 7d55da4163..3eee360bc2 100644 --- a/src/api/integrations/chatbot/chatwoot/utils/chatwoot-import-helper.ts +++ b/src/api/integrations/chatbot/chatwoot/utils/chatwoot-import-helper.ts @@ -206,6 +206,21 @@ class ChatwootImport { } } + public async getExistingMessageBySourceId(sourceId: string, conversationId: number) { + const formattedSourceId = `WAID:${sourceId.replace('WAID:', '')}`; + const pgClient = postgresClient.getChatwootConnection(); + const result = await pgClient.query( + `SELECT id, inbox_id, conversation_id, source_id + FROM messages + WHERE source_id = $1 AND conversation_id = $2 + ORDER BY id ASC + LIMIT 1`, + [formattedSourceId, conversationId], + ); + + return result.rows[0] ?? null; + } + public async importHistoryMessages( instance: InstanceDto, chatwootService: ChatwootService, diff --git a/src/api/integrations/chatbot/deliveryErrorPolicy.ts b/src/api/integrations/chatbot/deliveryErrorPolicy.ts new file mode 100644 index 0000000000..2779935a24 --- /dev/null +++ b/src/api/integrations/chatbot/deliveryErrorPolicy.ts @@ -0,0 +1,3 @@ +export function applyDeliveryErrorPolicy(error: unknown, propagate: boolean): void { + if (propagate) throw error; +} diff --git a/src/api/integrations/chatbot/n8n/services/n8n.service.ts b/src/api/integrations/chatbot/n8n/services/n8n.service.ts index 7da8ac28b3..6fbeb2e6ed 100644 --- a/src/api/integrations/chatbot/n8n/services/n8n.service.ts +++ b/src/api/integrations/chatbot/n8n/services/n8n.service.ts @@ -27,6 +27,10 @@ export class N8nService extends BaseChatbotService { return 'n8n'; } + protected override shouldPropagateDeliveryErrors(): boolean { + return true; + } + protected async sendMessageToBot( instance: any, session: IntegrationSession, @@ -92,7 +96,7 @@ export class N8nService extends BaseChatbotService { }); } catch (error) { this.logger.error(error.response?.data || error); - return; + throw error; } } } diff --git a/src/api/integrations/event/webhook/webhook.controller.ts b/src/api/integrations/event/webhook/webhook.controller.ts index a1ad492667..b718e4e09d 100644 --- a/src/api/integrations/event/webhook/webhook.controller.ts +++ b/src/api/integrations/event/webhook/webhook.controller.ts @@ -1,3 +1,4 @@ +import { resolveInboundMode } from '@api/integrations/channel/whatsapp/inboundInbox'; import { EventDto } from '@api/integrations/event/event.dto'; import { PrismaRepository } from '@api/repository/repository.service'; import { WAMonitoringService } from '@api/services/monitor.service'; @@ -149,7 +150,7 @@ export class WebhookController extends EventController implements EventControlle const inboxDefault = configService.get('INBOUND_INBOX').MODE; if ( event === 'messages.upsert' && - (waInstance?.instance?.inboundInboxMode === 'enforce' || inboxDefault === 'enforce') + resolveInboundMode(waInstance?.instance?.inboundInboxMode, inboxDefault) === 'enforce' ) { throw error; } @@ -207,7 +208,7 @@ export class WebhookController extends EventController implements EventControlle const inboxDefault = configService.get('INBOUND_INBOX').MODE; if ( event === 'messages.upsert' && - (waInstance?.instance?.inboundInboxMode === 'enforce' || inboxDefault === 'enforce') + resolveInboundMode(waInstance?.instance?.inboundInboxMode, inboxDefault) === 'enforce' ) { throw error; } diff --git a/test/xmacna-inbound-inbox.postgres.test.ts b/test/xmacna-inbound-inbox.postgres.test.ts index 79d3f02266..90d1c1c2d3 100644 --- a/test/xmacna-inbound-inbox.postgres.test.ts +++ b/test/xmacna-inbound-inbox.postgres.test.ts @@ -8,8 +8,10 @@ import { join } from 'node:path'; import { PrismaClient } from '@prisma/client'; import { + InboundModeTransitionBlockedError, normalizeInstanceScope, PrismaInboundInbox, + transitionInboundMode, } from '../src/api/integrations/channel/whatsapp/inboundInbox'; import { backfill, exportTombstones, importTombstones } from '../src/cli/inboundInboxMaintenance'; @@ -34,6 +36,7 @@ test('raw instance names that used to normalize alike remain tenant-isolated', { inbox.claim( { sourceCluster, + instanceId: instanceIds[index], instanceScope: normalizeInstanceScope(name), contactScope: 'contact@s.whatsapp.net', messageId, @@ -95,6 +98,7 @@ test('840 concurrent deliveries create one receipt and one durable message', { s }; const input = { sourceCluster: 'local-concurrency', + instanceId, instanceScope: `instance-${suffix}`, contactScope: '5511999999999@s.whatsapp.net', messageId, @@ -141,6 +145,70 @@ test('840 concurrent deliveries create one receipt and one durable message', { s } }); +test('enforce downgrade serializes against a concurrent ingress claim', { skip: !enabled }, async () => { + const repository = new PrismaClient(); + const inbox = new PrismaInboundInbox(repository as any); + const suffix = randomUUID(); + const instanceId = `mode-race-${suffix}`; + const instanceName = `mode-race-${suffix}`; + const instanceScope = normalizeInstanceScope(instanceName); + const sourceCluster = 'local-mode-race'; + + try { + await repository.$connect(); + await repository.instance.create({ data: { id: instanceId, name: instanceName, inboundInboxMode: 'enforce' } }); + + for (let iteration = 0; iteration < 30; iteration += 1) { + const messageId = `mode-race-${iteration}-${suffix}`; + const claim = inbox.claim( + { + sourceCluster, + instanceId, + instanceScope, + contactScope: 'contact@s.whatsapp.net', + messageId, + classification: 'real', + payloadHash: iteration.toString(16).padStart(64, '0'), + leaseOwner: `mode-race-worker-${iteration}`, + messageData: { + key: { id: messageId, remoteJid: 'contact@s.whatsapp.net', fromMe: false }, + pushName: 'Mode race', + messageType: 'conversation', + message: { conversation: `mode race ${iteration}` }, + source: 'unknown', + messageTimestamp: iteration + 1, + instanceId, + }, + }, + 'enforce', + ); + const downgrade = transitionInboundMode(repository as any, instanceName, sourceCluster, 'off'); + const [claimResult, downgradeResult] = await Promise.allSettled([claim, downgrade]); + + assert.notEqual( + claimResult.status === 'fulfilled' && downgradeResult.status === 'fulfilled', + true, + 'claim and downgrade must not both commit', + ); + if (claimResult.status === 'fulfilled') { + assert.equal(downgradeResult.status, 'rejected'); + assert.ok(downgradeResult.reason instanceof InboundModeTransitionBlockedError); + } else { + assert.equal(downgradeResult.status, 'fulfilled'); + assert.match(String(claimResult.reason), /mode changed before claim/); + } + + await repository.inboundReceipt.deleteMany({ where: { sourceCluster, instanceScope, messageId } }); + await transitionInboundMode(repository as any, instanceName, sourceCluster, 'enforce'); + } + } finally { + await repository.inboundReceipt.deleteMany({ where: { sourceCluster, instanceScope } }); + await repository.message.deleteMany({ where: { instanceId } }); + await repository.instance.deleteMany({ where: { id: instanceId } }); + await repository.$disconnect(); + } +}); + test('Chatwoot success followed by webhook failure replays the exact enriched payload', { skip: !enabled }, async () => { const repository = new PrismaClient(); const inbox = new PrismaInboundInbox(repository as any); @@ -158,6 +226,7 @@ test('Chatwoot success followed by webhook failure replays the exact enriched pa const claimed = await inbox.claim( { sourceCluster, + instanceId, instanceScope, contactScope: '12345@lid', messageId, @@ -244,6 +313,199 @@ test('Chatwoot success followed by webhook failure replays the exact enriched pa } }); +test('collision during an active lease cannot revoke fencing or quarantine the live delivery', { skip: !enabled }, async () => { + const repository = new PrismaClient(); + const inbox = new PrismaInboundInbox(repository as any); + const suffix = randomUUID(); + const instanceId = `inflight-collision-${suffix}`; + const instanceScope = `inflight-collision-scope-${suffix}`; + const sourceCluster = 'local-inflight-collision'; + const messageId = `collision-${suffix}`; + + try { + await repository.$connect(); + await repository.instance.create({ + data: { id: instanceId, name: instanceId, inboundInboxMode: 'enforce' }, + }); + const original = await inbox.claim( + { + sourceCluster, + instanceId, + instanceScope, + contactScope: 'contact@s.whatsapp.net', + messageId, + classification: 'real', + payloadHash: 'a'.repeat(64), + leaseOwner: 'live-worker', + leaseSeconds: 60, + messageData: { + key: { id: messageId, remoteJid: 'contact@s.whatsapp.net', fromMe: false }, + pushName: 'Collision', + messageType: 'conversation', + message: { conversation: 'original' }, + source: 'unknown', + messageTimestamp: 1, + instanceId, + }, + }, + 'enforce', + ); + const collision = await inbox.claim( + { + sourceCluster, + instanceId, + instanceScope, + contactScope: 'contact@s.whatsapp.net', + messageId, + classification: 'real', + payloadHash: 'b'.repeat(64), + leaseOwner: 'collision-worker', + }, + 'enforce', + ); + assert.equal(collision.kind, 'collision'); + assert.equal(collision.shouldDispatch, false); + const live = await repository.inboundReceipt.findUniqueOrThrow({ where: { id: original.receiptId } }); + assert.equal(live.state, 'processing'); + assert.equal(live.leaseOwner, 'live-worker'); + assert.equal(live.leaseToken, original.leaseToken); + assert.equal(await inbox.markSink(original.receiptId, 'webhook', 'sent', 'live-worker', original.leaseToken), true); + assert.equal(await inbox.markDone(original.receiptId, 'live-worker', original.leaseToken!), true); + } finally { + await repository.inboundReceipt.deleteMany({ where: { sourceCluster, instanceScope } }); + await repository.instance.deleteMany({ where: { id: instanceId } }); + await repository.$disconnect(); + } +}); + +test('Chatwoot failure can leave n8n/webhook sent and retries only the failed sink', { skip: !enabled }, async () => { + const repository = new PrismaClient(); + const inbox = new PrismaInboundInbox(repository as any); + const suffix = randomUUID(); + const instanceId = `chatwoot-best-effort-${suffix}`; + const instanceScope = `chatwoot-best-effort-scope-${suffix}`; + const sourceCluster = 'local-chatwoot-best-effort'; + const messageId = `best-effort-${suffix}`; + + try { + await repository.$connect(); + await repository.instance.create({ data: { id: instanceId, name: instanceId, inboundInboxMode: 'enforce' } }); + const claimed = await inbox.claim( + { + sourceCluster, + instanceId, + instanceScope, + contactScope: 'contact@s.whatsapp.net', + messageId, + classification: 'real', + payloadHash: 'f'.repeat(64), + leaseOwner: 'ingress', + leaseSeconds: 60, + messageData: { + key: { id: messageId, remoteJid: 'contact@s.whatsapp.net', fromMe: false }, + pushName: 'Best effort', + messageType: 'conversation', + message: { conversation: 'continue para n8n' }, + source: 'unknown', + messageTimestamp: 1, + instanceId, + }, + }, + 'enforce', + ); + assert.equal(await inbox.markSink(claimed.receiptId, 'chatwoot', 'failed', 'ingress', claimed.leaseToken), true); + assert.equal(await inbox.markSink(claimed.receiptId, 'webhook', 'sent', 'ingress', claimed.leaseToken), true); + assert.equal(await inbox.markSink(claimed.receiptId, 'chatbot', 'sent', 'ingress', claimed.leaseToken), true); + assert.equal(await inbox.markFailed(claimed.receiptId, 'ingress', claimed.leaseToken!, 'chatwoot unavailable', 10, 0), true); + + const replay = await inbox.leaseNext(sourceCluster, instanceScope, 'reconciler', 60, 10); + assert.ok(replay); + assert.equal(replay.chatwootState, 'failed'); + assert.equal(replay.webhookState, 'sent'); + assert.equal(replay.chatbotState, 'sent'); + assert.equal(await inbox.markSink(replay.receiptId, 'chatwoot', 'sent', replay.leaseOwner, replay.leaseToken), true); + assert.equal(await inbox.markDone(replay.receiptId, replay.leaseOwner, replay.leaseToken), true); + } finally { + await repository.inboundReceipt.deleteMany({ where: { sourceCluster, instanceScope } }); + await repository.instance.deleteMany({ where: { id: instanceId } }); + await repository.$disconnect(); + } +}); + +test('control can promote to real and protocol replay dispatches only once in enforce', { skip: !enabled }, async () => { + const repository = new PrismaClient(); + const inbox = new PrismaInboundInbox(repository as any); + const suffix = randomUUID(); + const instanceId = `classification-${suffix}`; + const instanceScope = `classification-scope-${suffix}`; + const sourceCluster = 'local-classification'; + + try { + await repository.$connect(); + await repository.instance.create({ + data: { id: instanceId, name: instanceId, inboundInboxMode: 'enforce' }, + }); + const controlId = `control-${suffix}`; + const control = await inbox.claim( + { + sourceCluster, + instanceId, + instanceScope, + contactScope: 'contact@s.whatsapp.net', + messageId: controlId, + classification: 'control', + payloadHash: 'c'.repeat(64), + }, + 'enforce', + ); + assert.equal(control.shouldDispatch, false); + const promoted = await inbox.claim( + { + sourceCluster, + instanceId, + instanceScope, + contactScope: 'contact@s.whatsapp.net', + messageId: controlId, + classification: 'real', + payloadHash: 'd'.repeat(64), + leaseOwner: 'promotion-worker', + messageData: { + key: { id: controlId, remoteJid: 'contact@s.whatsapp.net', fromMe: false }, + pushName: 'Promotion', + messageType: 'conversation', + message: { conversation: 'real' }, + source: 'unknown', + messageTimestamp: 1, + instanceId, + }, + }, + 'enforce', + ); + assert.equal(promoted.kind, 'promoted'); + assert.equal(promoted.shouldDispatch, true); + assert.equal(await inbox.markDone(promoted.receiptId, 'promotion-worker', promoted.leaseToken!), true); + + const protocolInput = { + sourceCluster, + instanceId, + instanceScope, + contactScope: 'contact@s.whatsapp.net', + messageId: `protocol-${suffix}`, + classification: 'protocol' as const, + payloadHash: 'e'.repeat(64), + }; + const firstProtocol = await inbox.claim(protocolInput, 'enforce'); + const replayedProtocol = await inbox.claim(protocolInput, 'enforce'); + assert.equal(firstProtocol.shouldDispatch, true); + assert.equal(replayedProtocol.kind, 'duplicate'); + assert.equal(replayedProtocol.shouldDispatch, false); + } finally { + await repository.inboundReceipt.deleteMany({ where: { sourceCluster, instanceScope } }); + await repository.instance.deleteMany({ where: { id: instanceId } }); + await repository.$disconnect(); + } +}); + test('reconciler leases with fencing, preserves completed sinks and orders each contact', { skip: !enabled }, async () => { const repository = new PrismaClient(); const inbox = new PrismaInboundInbox(repository as any); @@ -257,6 +519,7 @@ test('reconciler leases with fencing, preserves completed sinks and orders each const result = await inbox.claim( { sourceCluster, + instanceId, instanceScope, contactScope, messageId, @@ -375,6 +638,7 @@ test('stub promotes, collisions quarantine and instance recreation keeps the tom const stub = await inbox.claim( { sourceCluster, + instanceId, instanceScope, contactScope: 'contact@s.whatsapp.net', messageId, @@ -388,6 +652,7 @@ test('stub promotes, collisions quarantine and instance recreation keeps the tom const realInput = { sourceCluster, + instanceId, instanceScope, contactScope: 'contact@s.whatsapp.net', messageId, @@ -410,6 +675,7 @@ test('stub promotes, collisions quarantine and instance recreation keeps the tom const collision = await inbox.claim( { sourceCluster, + instanceId, instanceScope, contactScope: 'contact@s.whatsapp.net', messageId, @@ -434,6 +700,7 @@ test('stub promotes, collisions quarantine and instance recreation keeps the tom const replay = await inbox.claim( { sourceCluster, + instanceId, instanceScope, contactScope: 'contact@s.whatsapp.net', messageId, diff --git a/test/xmacna-inbound-inbox.test.ts b/test/xmacna-inbound-inbox.test.ts index c6041f31ba..ee114548b0 100644 --- a/test/xmacna-inbound-inbox.test.ts +++ b/test/xmacna-inbound-inbox.test.ts @@ -3,6 +3,7 @@ import test from 'node:test'; import { validate } from 'jsonschema'; import { + canTransitionInboundMode, classifyInboundMessage, evaluateExistingReceipt, inboundPayloadHash, @@ -55,22 +56,42 @@ test('canonical hash detects divergent real content', () => { assert.notEqual(inboundPayloadHash(base as any), inboundPayloadHash(divergent as any)); }); -test('receipt policy deduplicates exact replay, promotes stub and quarantines collision', () => { +test('receipt policy deduplicates exact replay, promotes every non-real class and quarantines real collision', () => { const exact = { id: 'r1', classification: 'real', payloadHash: 'a' }; assert.equal(evaluateExistingReceipt(exact, { classification: 'real', payloadHash: 'a' }), 'duplicate'); assert.equal( evaluateExistingReceipt({ ...exact, classification: 'stub' }, { classification: 'real', payloadHash: 'b' }), 'promoted', ); + assert.equal( + evaluateExistingReceipt({ ...exact, classification: 'control' }, { classification: 'real', payloadHash: 'b' }), + 'promoted', + ); + assert.equal( + evaluateExistingReceipt({ ...exact, classification: 'protocol' }, { classification: 'real', payloadHash: 'b' }), + 'promoted', + ); + assert.equal( + evaluateExistingReceipt(exact, { classification: 'protocol', payloadHash: 'b' }), + 'duplicate', + ); assert.equal(evaluateExistingReceipt(exact, { classification: 'real', payloadHash: 'b' }), 'collision'); }); -test('invalid persisted modes fail closed to the configured fallback', () => { +test('invalid persisted modes never enable global enforce', () => { assert.equal(resolveInboundMode('enforce', 'off'), 'enforce'); assert.equal(resolveInboundMode('invalid', 'shadow'), 'shadow'); + assert.equal(resolveInboundMode(undefined, 'enforce'), 'off'); assert.equal(resolveInboundMode(undefined, 'off'), 'off'); }); +test('enforce downgrade is blocked until incomplete receipts drain', () => { + assert.equal(canTransitionInboundMode('enforce', 'off', 1), false); + assert.equal(canTransitionInboundMode('enforce', 'shadow', 1), false); + assert.equal(canTransitionInboundMode('enforce', 'off', 0), true); + assert.equal(canTransitionInboundMode('shadow', 'off', 10), true); +}); + test('instance API schemas accept only explicit inbox rollout modes', () => { assert.equal(validate({ instanceName: 'pilot', inboundInboxMode: 'shadow' }, instanceSchema).valid, true); assert.equal(validate({ instanceName: 'pilot', inboundInboxMode: 'unsafe' }, instanceSchema).valid, false); diff --git a/test/xmacna-inbound-service-contracts.test.ts b/test/xmacna-inbound-service-contracts.test.ts new file mode 100644 index 0000000000..ef911a0062 --- /dev/null +++ b/test/xmacna-inbound-service-contracts.test.ts @@ -0,0 +1,47 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { readFileSync } from 'node:fs'; + +import { mergeRequestIdentity } from '../src/api/abstract/requestIdentity'; +import { applyDeliveryErrorPolicy } from '../src/api/integrations/chatbot/deliveryErrorPolicy'; + +test('durable delivery policy propagates errors while baseline policy preserves swallow semantics', () => { + const error = new Error('n8n unavailable'); + assert.throws(() => applyDeliveryErrorPolicy(error, true), /n8n unavailable/); + assert.doesNotThrow(() => applyDeliveryErrorPolicy(error, false)); + + const n8nSource = readFileSync('src/api/integrations/chatbot/n8n/services/n8n.service.ts', 'utf8'); + assert.match(n8nSource, /shouldPropagateDeliveryErrors\(\): boolean \{\s*return true;/); + assert.match(n8nSource, /catch \(error\) \{[\s\S]*?throw error;\s*\}/); +}); + +test('route instanceName wins over query and prevents cross-tenant retargeting', async () => { + const received = mergeRequestIdentity( + { instanceName: 'tenant-authenticated' }, + { instanceName: 'tenant-attacker', number: '5511' }, + ); + assert.equal(received.instanceName, 'tenant-authenticated'); + assert.equal(received.number, '5511'); +}); + +test('Chatwoot uses WAID preflight and surfaces media delivery failures in enforce', () => { + const source = readFileSync('src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts', 'utf8'); + const createStart = source.indexOf('public async createMessage('); + const preflight = source.indexOf('chatwootImport.getExistingMessageBySourceId(sourceId, conversationId)', createStart); + const createCall = source.indexOf('client.messages.create(', createStart); + assert.ok(createStart >= 0 && preflight > createStart && createCall > preflight); + assert.match(source, /if \(this\.shouldSurfaceInboundFailure\(instance\)\) throw error;/); + + const importSource = readFileSync( + 'src/api/integrations/chatbot/chatwoot/utils/chatwoot-import-helper.ts', + 'utf8', + ); + assert.match(importSource, /SELECT id, inbox_id, conversation_id, source_id/); +}); + +test('health stats expose fresh unique traffic separately from duplicate replays', () => { + const source = readFileSync('src/api/controllers/instance.controller.ts', 'utf8'); + assert.match(source, /_max: \{ createdAt: true, lastSeenAt: true \}/); + assert.match(source, /lastReceiptCreatedAt: totals\._max\.createdAt/); + assert.match(source, /lastInboundSeenAt: totals\._max\.lastSeenAt/); +}); diff --git a/test/xmacna-inbound-sink-dispatch.test.ts b/test/xmacna-inbound-sink-dispatch.test.ts new file mode 100644 index 0000000000..8fa28da3a4 --- /dev/null +++ b/test/xmacna-inbound-sink-dispatch.test.ts @@ -0,0 +1,53 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; + +import { + attemptDurableInboundSink, + DurableInboundSinkState, +} from '../src/api/integrations/channel/whatsapp/inboundSinkDispatch'; + +test('Chatwoot failure is persisted and does not prevent the following n8n sink', async () => { + const marks: Array<{ sink: string; state: DurableInboundSinkState }> = []; + const delivered: string[] = []; + const mark = async (sink: any, state: DurableInboundSinkState) => { + marks.push({ sink, state }); + }; + + const chatwootFailure = await attemptDurableInboundSink({ + sink: 'chatwoot', + operation: async () => { + throw new Error('chatwoot unavailable'); + }, + mark, + }); + const chatbotFailure = await attemptDurableInboundSink({ + sink: 'chatbot', + operation: async () => { + delivered.push('n8n'); + return 'sent'; + }, + mark, + }); + + assert.equal(chatwootFailure?.sink, 'chatwoot'); + assert.equal(chatbotFailure, undefined); + assert.deepEqual(delivered, ['n8n']); + assert.deepEqual(marks, [ + { sink: 'chatwoot', state: 'failed' }, + { sink: 'chatbot', state: 'sent' }, + ]); +}); + +test('fencing failure rejects instead of pretending the sink was recorded', async () => { + await assert.rejects( + () => + attemptDurableInboundSink({ + sink: 'webhook', + operation: async () => 'sent', + mark: async () => { + throw new Error('stale lease'); + }, + }), + /stale lease/, + ); +}); From 806de69a44373a224c84b283b1f8579181cb9d91 Mon Sep 17 00:00:00 2001 From: raffareis Date: Thu, 13 Aug 2026 01:38:00 -0300 Subject: [PATCH 10/19] fix(deps): remediate candidate runtime audit (#513) --- package-lock.json | 6093 +++++++---------- package.json | 35 +- .../channel/whatsapp/inboundInbox.ts | 3 +- .../whatsapp/whatsapp.baileys.service.ts | 6 +- .../chatwoot/services/chatwoot.service.ts | 6 +- test/xmacna-baileys-compat.test.cjs | 60 + 6 files changed, 2447 insertions(+), 3756 deletions(-) diff --git a/package-lock.json b/package-lock.json index ae02d8bc4b..423ab8012f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,12 +15,11 @@ "@figuro/chatwoot-sdk": "^1.1.16", "@hapi/boom": "^10.0.1", "@paralleldrive/cuid2": "^2.2.2", - "@prisma/client": "^6.16.2", - "@sentry/node": "^10.12.0", - "@types/uuid": "^10.0.0", + "@prisma/client": "^6.19.3", + "@sentry/node": "^10.70.0", "amqplib": "^0.10.5", "audio-decode": "^2.2.3", - "axios": "^1.7.9", + "axios": "^1.19.0", "baileys": "7.0.0-rc14", "class-validator": "^0.14.1", "compression": "^1.7.5", @@ -29,11 +28,11 @@ "dotenv": "^16.4.7", "emoji-regex": "^10.4.0", "eventemitter2": "^6.4.9", - "express": "^4.21.2", + "express": "^4.22.2", "express-async-errors": "^3.1.1", "fetch-socks": "^1.3.2", "fluent-ffmpeg": "^2.1.3", - "form-data": "^4.0.1", + "form-data": "^4.0.6", "https-proxy-agent": "^7.0.6", "i18next": "^23.7.19", "jimp": "^1.6.0", @@ -42,33 +41,32 @@ "jsonwebtoken": "^9.0.2", "kafkajs": "^2.2.4", "libphonenumber-js": "^1.12.25", - "link-preview-js": "^3.0.13", + "link-preview-js": "^5.0.0", "long": "^5.2.3", "mediainfo.js": "^0.3.4", "mime": "^4.0.0", "mime-types": "^2.1.35", - "minio": "^8.0.3", - "multer": "^2.0.2", + "minio": "^8.0.7", + "multer": "^2.2.0", "nats": "^2.29.1", "node-cache": "^5.1.2", - "node-cron": "^3.0.3", + "node-cron": "^4.6.0", "openai": "^4.77.3", "pg": "^8.13.1", "pino": "^9.10.0", - "prisma": "^6.1.0", + "prisma": "^6.19.3", "pusher": "^5.2.0", "qrcode": "^1.5.4", "qrcode-terminal": "^0.12.0", "redis": "^4.7.0", "rxjs": "^7.8.2", - "sharp": "^0.34.2", + "sharp": "^0.35.3", "socket.io": "^4.8.1", "socket.io-client": "^4.8.1", "socks-proxy-agent": "^8.0.5", "swagger-ui-express": "^5.0.1", - "tsup": "^8.3.5", - "undici": "^7.16.0", - "uuid": "^13.0.0" + "undici": "^7.29.0", + "uuid": "^13.0.2" }, "devDependencies": { "@commitlint/cli": "^19.8.1", @@ -83,6 +81,7 @@ "@types/node-cron": "^3.0.11", "@types/qrcode": "^1.5.5", "@types/qrcode-terminal": "^0.12.2", + "@types/uuid": "^10.0.0", "@typescript-eslint/eslint-plugin": "^8.44.0", "@typescript-eslint/parser": "^8.44.0", "commitizen": "^4.3.1", @@ -96,6 +95,7 @@ "lint-staged": "^16.1.6", "prettier": "^3.4.2", "tsconfig-paths": "^4.2.0", + "tsup": "^8.3.5", "tsx": "^4.20.5", "typescript": "^5.7.2" }, @@ -110,701 +110,344 @@ "license": "MIT" }, "node_modules/@apm-js-collab/code-transformer": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/@apm-js-collab/code-transformer/-/code-transformer-0.8.2.tgz", - "integrity": "sha512-YRjJjNq5KFSjDUoqu5pFUWrrsvGOxl6c3bu+uMFc9HNNptZ2rNU/TI2nLw4jnhQNtka972Ee2m3uqbvDQtPeCA==", - "license": "Apache-2.0" - }, - "node_modules/@apm-js-collab/tracing-hooks": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@apm-js-collab/tracing-hooks/-/tracing-hooks-0.3.1.tgz", - "integrity": "sha512-Vu1CbmPURlN5fTboVuKMoJjbO5qcq9fA5YXpskx3dXe/zTBvjODFoerw+69rVBlRLrJpwPqSDqEuJDEKIrTldw==", - "license": "Apache-2.0", - "dependencies": { - "@apm-js-collab/code-transformer": "^0.8.0", - "debug": "^4.4.1", - "module-details-from-path": "^1.0.4" - } - }, - "node_modules/@aws-crypto/sha256-browser": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", - "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-js": "^5.2.0", - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-js": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", - "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-crypto/supports-web-crypto": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", - "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/util": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", - "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.222.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/@apm-js-collab/code-transformer/-/code-transformer-0.18.1.tgz", + "integrity": "sha512-u1Hb6bHjWtkSpiprwVP6YaHC1DTN4RAU3zYkUDUe7WMnJwdyU1pwTL9dFKiSJB9IiLue/EQovmyx6xhU7FFtAQ==", "license": "Apache-2.0", "dependencies": { - "tslib": "^2.6.2" + "@types/estree": "^1.0.8", + "astring": "^1.9.0", + "esquery": "^1.7.0", + "meriyah": "^6.1.4", + "semifies": "^1.0.0", + "source-map": "^0.6.0" }, - "engines": { - "node": ">=14.0.0" + "bin": { + "code-transformer": "cli.js" } }, - "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "license": "Apache-2.0", + "node_modules/@apm-js-collab/code-transformer-bundler-plugins": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@apm-js-collab/code-transformer-bundler-plugins/-/code-transformer-bundler-plugins-0.7.4.tgz", + "integrity": "sha512-nAfOeZPSUAQvJa1iFT/5oCrTm5YQhMMrfCNthNnaXHZiOQhu1KGuLoIx7HtbAi3wfwaBYLaICPIeenIaEwcXIg==", + "license": "MIT", "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" + "@apm-js-collab/code-transformer": "^0.18.1", + "es-module-lexer": "^2.1.0", + "magic-string": "^0.30.21", + "module-details-from-path": "^1.0.4" }, "engines": { - "node": ">=14.0.0" + "node": ">=18.0.0" } }, - "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "node_modules/@apm-js-collab/tracing-hooks": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@apm-js-collab/tracing-hooks/-/tracing-hooks-0.13.0.tgz", + "integrity": "sha512-mTvWz9rnQwx1U3h0XPTHaX7bgfkpipLLTQyjlC2cdhQpQEuoLT0AGzoydeoq2NxfEVv6fWOOETcSbb2nptleyw==", "license": "Apache-2.0", "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" + "@apm-js-collab/code-transformer": "^0.18.0", + "debug": "^4.4.1", + "module-details-from-path": "^1.0.4" } }, "node_modules/@aws-sdk/client-sqs": { - "version": "3.936.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sqs/-/client-sqs-3.936.0.tgz", - "integrity": "sha512-JDYdeGV8L+zXr9Ce7gVeleQB2z0F3YWWmNvbE6cS20fR9E0XVIsYw5Grymjw9/3AG4wbOIQJ5Nayy+HgxKANjA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.936.0", - "@aws-sdk/credential-provider-node": "3.936.0", - "@aws-sdk/middleware-host-header": "3.936.0", - "@aws-sdk/middleware-logger": "3.936.0", - "@aws-sdk/middleware-recursion-detection": "3.936.0", - "@aws-sdk/middleware-sdk-sqs": "3.936.0", - "@aws-sdk/middleware-user-agent": "3.936.0", - "@aws-sdk/region-config-resolver": "3.936.0", - "@aws-sdk/types": "3.936.0", - "@aws-sdk/util-endpoints": "3.936.0", - "@aws-sdk/util-user-agent-browser": "3.936.0", - "@aws-sdk/util-user-agent-node": "3.936.0", - "@smithy/config-resolver": "^4.4.3", - "@smithy/core": "^3.18.5", - "@smithy/fetch-http-handler": "^5.3.6", - "@smithy/hash-node": "^4.2.5", - "@smithy/invalid-dependency": "^4.2.5", - "@smithy/md5-js": "^4.2.5", - "@smithy/middleware-content-length": "^4.2.5", - "@smithy/middleware-endpoint": "^4.3.12", - "@smithy/middleware-retry": "^4.4.12", - "@smithy/middleware-serde": "^4.2.6", - "@smithy/middleware-stack": "^4.2.5", - "@smithy/node-config-provider": "^4.3.5", - "@smithy/node-http-handler": "^4.4.5", - "@smithy/protocol-http": "^5.3.5", - "@smithy/smithy-client": "^4.9.8", - "@smithy/types": "^4.9.0", - "@smithy/url-parser": "^4.2.5", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-body-length-node": "^4.2.1", - "@smithy/util-defaults-mode-browser": "^4.3.11", - "@smithy/util-defaults-mode-node": "^4.2.14", - "@smithy/util-endpoints": "^3.2.5", - "@smithy/util-middleware": "^4.2.5", - "@smithy/util-retry": "^4.2.5", - "@smithy/util-utf8": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sso": { - "version": "3.936.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.936.0.tgz", - "integrity": "sha512-0G73S2cDqYwJVvqL08eakj79MZG2QRaB56Ul8/Ps9oQxllr7DMI1IQ/N3j3xjxgpq/U36pkoFZ8aK1n7Sbr3IQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.936.0", - "@aws-sdk/middleware-host-header": "3.936.0", - "@aws-sdk/middleware-logger": "3.936.0", - "@aws-sdk/middleware-recursion-detection": "3.936.0", - "@aws-sdk/middleware-user-agent": "3.936.0", - "@aws-sdk/region-config-resolver": "3.936.0", - "@aws-sdk/types": "3.936.0", - "@aws-sdk/util-endpoints": "3.936.0", - "@aws-sdk/util-user-agent-browser": "3.936.0", - "@aws-sdk/util-user-agent-node": "3.936.0", - "@smithy/config-resolver": "^4.4.3", - "@smithy/core": "^3.18.5", - "@smithy/fetch-http-handler": "^5.3.6", - "@smithy/hash-node": "^4.2.5", - "@smithy/invalid-dependency": "^4.2.5", - "@smithy/middleware-content-length": "^4.2.5", - "@smithy/middleware-endpoint": "^4.3.12", - "@smithy/middleware-retry": "^4.4.12", - "@smithy/middleware-serde": "^4.2.6", - "@smithy/middleware-stack": "^4.2.5", - "@smithy/node-config-provider": "^4.3.5", - "@smithy/node-http-handler": "^4.4.5", - "@smithy/protocol-http": "^5.3.5", - "@smithy/smithy-client": "^4.9.8", - "@smithy/types": "^4.9.0", - "@smithy/url-parser": "^4.2.5", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-body-length-node": "^4.2.1", - "@smithy/util-defaults-mode-browser": "^4.3.11", - "@smithy/util-defaults-mode-node": "^4.2.14", - "@smithy/util-endpoints": "^3.2.5", - "@smithy/util-middleware": "^4.2.5", - "@smithy/util-retry": "^4.2.5", - "@smithy/util-utf8": "^4.2.0", + "version": "3.1109.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sqs/-/client-sqs-3.1109.0.tgz", + "integrity": "sha512-+Ax4wZkgMGA7y8ZofnlMB3z/64sSFt4VjkeVZcjyRfFMugV7llkXA4qj7zHigxhYYr/kRYlYOQtFCsB0r4h41Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.7", + "@aws-sdk/credential-provider-node": "^3.972.79", + "@aws-sdk/middleware-sdk-sqs": "^3.972.40", + "@aws-sdk/types": "^3.974.3", + "@smithy/core": "^3.31.1", + "@smithy/fetch-http-handler": "^5.6.13", + "@smithy/node-http-handler": "^4.9.13", + "@smithy/types": "^4.16.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/core": { - "version": "3.936.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.936.0.tgz", - "integrity": "sha512-eGJ2ySUMvgtOziHhDRDLCrj473RJoL4J1vPjVM3NrKC/fF3/LoHjkut8AAnKmrW6a2uTzNKubigw8dEnpmpERw==", + "version": "3.977.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.977.7.tgz", + "integrity": "sha512-I88Iov89NVmjSmJLKSv7Cn9M2J+a2942OkA8nZCbz+sl4ZeY4zEOcoLOrbt1GRfQ8zEQKnjAJdXixA3J/p1fDQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.936.0", - "@aws-sdk/xml-builder": "3.930.0", - "@smithy/core": "^3.18.5", - "@smithy/node-config-provider": "^4.3.5", - "@smithy/property-provider": "^4.2.5", - "@smithy/protocol-http": "^5.3.5", - "@smithy/signature-v4": "^5.3.5", - "@smithy/smithy-client": "^4.9.8", - "@smithy/types": "^4.9.0", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-middleware": "^4.2.5", - "@smithy/util-utf8": "^4.2.0", + "@aws-sdk/types": "^3.974.3", + "@aws-sdk/xml-builder": "^3.972.38", + "@aws/lambda-invoke-store": "^0.3.0", + "@smithy/core": "^3.31.1", + "@smithy/signature-v4": "^5.6.12", + "@smithy/types": "^4.16.1", + "bowser": "^2.11.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.936.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.936.0.tgz", - "integrity": "sha512-dKajFuaugEA5i9gCKzOaVy9uTeZcApE+7Z5wdcZ6j40523fY1a56khDAUYkCfwqa7sHci4ccmxBkAo+fW1RChA==", + "version": "3.972.68", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.68.tgz", + "integrity": "sha512-2a20A/IdNOwUvaDq91iqqS7BA0XlNMfW3iLGZGZLJv0EbUqhSxB0PIx4rQQqssvWj1uXImb3/UCCdHz/+1dOiA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.936.0", - "@aws-sdk/types": "3.936.0", - "@smithy/property-provider": "^4.2.5", - "@smithy/types": "^4.9.0", + "@aws-sdk/core": "^3.977.7", + "@aws-sdk/types": "^3.974.3", + "@smithy/core": "^3.31.1", + "@smithy/types": "^4.16.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.936.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.936.0.tgz", - "integrity": "sha512-5FguODLXG1tWx/x8fBxH+GVrk7Hey2LbXV5h9SFzYCx/2h50URBm0+9hndg0Rd23+xzYe14F6SI9HA9c1sPnjg==", + "version": "3.972.70", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.70.tgz", + "integrity": "sha512-0yRem2Fs52r/Nn6UAqIlpjexfaYj8ziEozOe9tamtAVT/5bzFLKx8O2r7MaRqgS3hGKHIa1Jij9nKHSsNnb04A==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.936.0", - "@aws-sdk/types": "3.936.0", - "@smithy/fetch-http-handler": "^5.3.6", - "@smithy/node-http-handler": "^4.4.5", - "@smithy/property-provider": "^4.2.5", - "@smithy/protocol-http": "^5.3.5", - "@smithy/smithy-client": "^4.9.8", - "@smithy/types": "^4.9.0", - "@smithy/util-stream": "^4.5.6", + "@aws-sdk/core": "^3.977.7", + "@aws-sdk/types": "^3.974.3", + "@smithy/core": "^3.31.1", + "@smithy/fetch-http-handler": "^5.6.13", + "@smithy/node-http-handler": "^4.9.13", + "@smithy/types": "^4.16.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.936.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.936.0.tgz", - "integrity": "sha512-TbUv56ERQQujoHcLMcfL0Q6bVZfYF83gu/TjHkVkdSlHPOIKaG/mhE2XZSQzXv1cud6LlgeBbfzVAxJ+HPpffg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.936.0", - "@aws-sdk/credential-provider-env": "3.936.0", - "@aws-sdk/credential-provider-http": "3.936.0", - "@aws-sdk/credential-provider-login": "3.936.0", - "@aws-sdk/credential-provider-process": "3.936.0", - "@aws-sdk/credential-provider-sso": "3.936.0", - "@aws-sdk/credential-provider-web-identity": "3.936.0", - "@aws-sdk/nested-clients": "3.936.0", - "@aws-sdk/types": "3.936.0", - "@smithy/credential-provider-imds": "^4.2.5", - "@smithy/property-provider": "^4.2.5", - "@smithy/shared-ini-file-loader": "^4.4.0", - "@smithy/types": "^4.9.0", + "version": "3.973.13", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.973.13.tgz", + "integrity": "sha512-2M39DE02XpYYaSWYk/4AsImXYUU/1L2xmTMLUpMMWq7DfLv191/vCRy3baKtdr45AkJQyVgSjmuVOLm15SwrRQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.7", + "@aws-sdk/credential-provider-env": "^3.972.68", + "@aws-sdk/credential-provider-http": "^3.972.70", + "@aws-sdk/credential-provider-login": "^3.972.75", + "@aws-sdk/credential-provider-process": "^3.972.68", + "@aws-sdk/credential-provider-sso": "^3.973.12", + "@aws-sdk/credential-provider-web-identity": "^3.972.74", + "@aws-sdk/nested-clients": "^3.997.42", + "@aws-sdk/types": "^3.974.3", + "@smithy/core": "^3.31.1", + "@smithy/credential-provider-imds": "^4.4.16", + "@smithy/types": "^4.16.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-login": { - "version": "3.936.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.936.0.tgz", - "integrity": "sha512-8DVrdRqPyUU66gfV7VZNToh56ZuO5D6agWrkLQE/xbLJOm2RbeRgh6buz7CqV8ipRd6m+zCl9mM4F3osQLZn8Q==", + "version": "3.972.75", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.75.tgz", + "integrity": "sha512-jaTESuJlQsoUZ44f/i2puyPt8VlF/dMMJ9HM3cStYtk7eKX4N9UWi83OLixUkoOJH3BwWlPLCq9YIK9nfWhVBg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.936.0", - "@aws-sdk/nested-clients": "3.936.0", - "@aws-sdk/types": "3.936.0", - "@smithy/property-provider": "^4.2.5", - "@smithy/protocol-http": "^5.3.5", - "@smithy/shared-ini-file-loader": "^4.4.0", - "@smithy/types": "^4.9.0", + "@aws-sdk/core": "^3.977.7", + "@aws-sdk/nested-clients": "^3.997.42", + "@aws-sdk/types": "^3.974.3", + "@smithy/core": "^3.31.1", + "@smithy/types": "^4.16.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.936.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.936.0.tgz", - "integrity": "sha512-rk/2PCtxX9xDsQW8p5Yjoca3StqmQcSfkmD7nQ61AqAHL1YgpSQWqHE+HjfGGiHDYKG7PvE33Ku2GyA7lEIJAw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.936.0", - "@aws-sdk/credential-provider-http": "3.936.0", - "@aws-sdk/credential-provider-ini": "3.936.0", - "@aws-sdk/credential-provider-process": "3.936.0", - "@aws-sdk/credential-provider-sso": "3.936.0", - "@aws-sdk/credential-provider-web-identity": "3.936.0", - "@aws-sdk/types": "3.936.0", - "@smithy/credential-provider-imds": "^4.2.5", - "@smithy/property-provider": "^4.2.5", - "@smithy/shared-ini-file-loader": "^4.4.0", - "@smithy/types": "^4.9.0", + "version": "3.972.79", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.79.tgz", + "integrity": "sha512-RIw5dof1EHkWubrZzPC941CDtnFG1iAXsxbFgLkhdYZXHc4icU13c/uxSMI0J5eUx9bxa7LjfpdjfClBB1QsDA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "^3.972.68", + "@aws-sdk/credential-provider-http": "^3.972.70", + "@aws-sdk/credential-provider-ini": "^3.973.13", + "@aws-sdk/credential-provider-process": "^3.972.68", + "@aws-sdk/credential-provider-sso": "^3.973.12", + "@aws-sdk/credential-provider-web-identity": "^3.972.74", + "@aws-sdk/types": "^3.974.3", + "@smithy/core": "^3.31.1", + "@smithy/credential-provider-imds": "^4.4.16", + "@smithy/types": "^4.16.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.936.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.936.0.tgz", - "integrity": "sha512-GpA4AcHb96KQK2PSPUyvChvrsEKiLhQ5NWjeef2IZ3Jc8JoosiedYqp6yhZR+S8cTysuvx56WyJIJc8y8OTrLA==", + "version": "3.972.68", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.68.tgz", + "integrity": "sha512-nLP3Pda2MQTFJ25hKBMmUuB9Uv+bTZQNlufbeCwklP549Vwnkd8bRLJoCKp5k6xjmdyptrPrOfGOhN0mKuca8A==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.936.0", - "@aws-sdk/types": "3.936.0", - "@smithy/property-provider": "^4.2.5", - "@smithy/shared-ini-file-loader": "^4.4.0", - "@smithy/types": "^4.9.0", + "@aws-sdk/core": "^3.977.7", + "@aws-sdk/types": "^3.974.3", + "@smithy/core": "^3.31.1", + "@smithy/types": "^4.16.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.936.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.936.0.tgz", - "integrity": "sha512-wHlEAJJvtnSyxTfNhN98JcU4taA1ED2JvuI2eePgawqBwS/Tzi0mhED1lvNIaWOkjfLd+nHALwszGrtJwEq4yQ==", + "version": "3.973.12", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.973.12.tgz", + "integrity": "sha512-EmgyyHn+f9WCcelp3L/vci+LGbX8GigWaVphRArjVo5Pktkr9YnLy/mQ6VDkDyBD72dtfRNTgHmD2ts4rTDXKQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/client-sso": "3.936.0", - "@aws-sdk/core": "3.936.0", - "@aws-sdk/token-providers": "3.936.0", - "@aws-sdk/types": "3.936.0", - "@smithy/property-provider": "^4.2.5", - "@smithy/shared-ini-file-loader": "^4.4.0", - "@smithy/types": "^4.9.0", + "@aws-sdk/core": "^3.977.7", + "@aws-sdk/nested-clients": "^3.997.42", + "@aws-sdk/token-providers": "3.1108.0", + "@aws-sdk/types": "^3.974.3", + "@smithy/core": "^3.31.1", + "@smithy/types": "^4.16.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.936.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.936.0.tgz", - "integrity": "sha512-v3qHAuoODkoRXsAF4RG+ZVO6q2P9yYBT4GMpMEfU9wXVNn7AIfwZgTwzSUfnjNiGva5BKleWVpRpJ9DeuLFbUg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.936.0", - "@aws-sdk/nested-clients": "3.936.0", - "@aws-sdk/types": "3.936.0", - "@smithy/property-provider": "^4.2.5", - "@smithy/shared-ini-file-loader": "^4.4.0", - "@smithy/types": "^4.9.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.936.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.936.0.tgz", - "integrity": "sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.936.0", - "@smithy/protocol-http": "^5.3.5", - "@smithy/types": "^4.9.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-logger": { - "version": "3.936.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.936.0.tgz", - "integrity": "sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.936.0", - "@smithy/types": "^4.9.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.936.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.936.0.tgz", - "integrity": "sha512-l4aGbHpXM45YNgXggIux1HgsCVAvvBoqHPkqLnqMl9QVapfuSTjJHfDYDsx1Xxct6/m7qSMUzanBALhiaGO2fA==", + "version": "3.972.74", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.74.tgz", + "integrity": "sha512-0YfczxGXF3RjGj8z7QG/Ho2HnLGKDHfPSHiTs47UU1U/+mmwISDN+rvGKt2zh+3FX8NdT4xd95LGBGyhQw2dgQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.936.0", - "@aws/lambda-invoke-store": "^0.2.0", - "@smithy/protocol-http": "^5.3.5", - "@smithy/types": "^4.9.0", + "@aws-sdk/core": "^3.977.7", + "@aws-sdk/nested-clients": "^3.997.42", + "@aws-sdk/types": "^3.974.3", + "@smithy/core": "^3.31.1", + "@smithy/types": "^4.16.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/middleware-sdk-sqs": { - "version": "3.936.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sqs/-/middleware-sdk-sqs-3.936.0.tgz", - "integrity": "sha512-39WohFCCPeD6LV8zLQq7CyYbIieetEDDNLsEPeGJSh2Uv9qpY9r6zJRSTjb8hTuQbHDSEOGntHMYKpLoHdoxdQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.936.0", - "@smithy/smithy-client": "^4.9.8", - "@smithy/types": "^4.9.0", - "@smithy/util-hex-encoding": "^4.2.0", - "@smithy/util-utf8": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.936.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.936.0.tgz", - "integrity": "sha512-YB40IPa7K3iaYX0lSnV9easDOLPLh+fJyUDF3BH8doX4i1AOSsYn86L4lVldmOaSX+DwiaqKHpvk4wPBdcIPWw==", + "version": "3.972.40", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sqs/-/middleware-sdk-sqs-3.972.40.tgz", + "integrity": "sha512-inMaGeVemJEIp9Bc5Wlw0aVgVgUr/OwGqg5CLwO8TbMi6m1xFd+BG0fO/nJKVS/rkb9FSRxU/l0njDBz8OQvHQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.936.0", - "@aws-sdk/types": "3.936.0", - "@aws-sdk/util-endpoints": "3.936.0", - "@smithy/core": "^3.18.5", - "@smithy/protocol-http": "^5.3.5", - "@smithy/types": "^4.9.0", + "@aws-sdk/types": "^3.974.3", + "@smithy/core": "^3.31.1", + "@smithy/types": "^4.16.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/nested-clients": { - "version": "3.936.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.936.0.tgz", - "integrity": "sha512-eyj2tz1XmDSLSZQ5xnB7cLTVKkSJnYAEoNDSUNhzWPxrBDYeJzIbatecOKceKCU8NBf8gWWZCK/CSY0mDxMO0A==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.936.0", - "@aws-sdk/middleware-host-header": "3.936.0", - "@aws-sdk/middleware-logger": "3.936.0", - "@aws-sdk/middleware-recursion-detection": "3.936.0", - "@aws-sdk/middleware-user-agent": "3.936.0", - "@aws-sdk/region-config-resolver": "3.936.0", - "@aws-sdk/types": "3.936.0", - "@aws-sdk/util-endpoints": "3.936.0", - "@aws-sdk/util-user-agent-browser": "3.936.0", - "@aws-sdk/util-user-agent-node": "3.936.0", - "@smithy/config-resolver": "^4.4.3", - "@smithy/core": "^3.18.5", - "@smithy/fetch-http-handler": "^5.3.6", - "@smithy/hash-node": "^4.2.5", - "@smithy/invalid-dependency": "^4.2.5", - "@smithy/middleware-content-length": "^4.2.5", - "@smithy/middleware-endpoint": "^4.3.12", - "@smithy/middleware-retry": "^4.4.12", - "@smithy/middleware-serde": "^4.2.6", - "@smithy/middleware-stack": "^4.2.5", - "@smithy/node-config-provider": "^4.3.5", - "@smithy/node-http-handler": "^4.4.5", - "@smithy/protocol-http": "^5.3.5", - "@smithy/smithy-client": "^4.9.8", - "@smithy/types": "^4.9.0", - "@smithy/url-parser": "^4.2.5", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-body-length-node": "^4.2.1", - "@smithy/util-defaults-mode-browser": "^4.3.11", - "@smithy/util-defaults-mode-node": "^4.2.14", - "@smithy/util-endpoints": "^3.2.5", - "@smithy/util-middleware": "^4.2.5", - "@smithy/util-retry": "^4.2.5", - "@smithy/util-utf8": "^4.2.0", + "version": "3.997.42", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.42.tgz", + "integrity": "sha512-XWRyon2MTHXD/zMoo0Mbge6Vwf+iE0qQaM/RyGO6NfZ9WukCFiQL27nQVZjYy2JwSIg+iXZxKOX95OBXqlSM4w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.977.7", + "@aws-sdk/signature-v4-multi-region": "^3.996.44", + "@aws-sdk/types": "^3.974.3", + "@smithy/core": "^3.31.1", + "@smithy/fetch-http-handler": "^5.6.13", + "@smithy/node-http-handler": "^4.9.13", + "@smithy/types": "^4.16.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, - "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.936.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.936.0.tgz", - "integrity": "sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw==", + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.996.44", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.44.tgz", + "integrity": "sha512-ZSfQ35Qn4MhSY+A0Whyr+KBx+wJKZUyBsOrjB2pSHOafRzbFe47T8XcXM8hZqUAC69qnqIy0C9ArxTuud0CC2w==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.936.0", - "@smithy/config-resolver": "^4.4.3", - "@smithy/node-config-provider": "^4.3.5", - "@smithy/types": "^4.9.0", + "@aws-sdk/types": "^3.974.3", + "@smithy/signature-v4": "^5.6.12", + "@smithy/types": "^4.16.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.936.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.936.0.tgz", - "integrity": "sha512-vvw8+VXk0I+IsoxZw0mX9TMJawUJvEsg3EF7zcCSetwhNPAU8Xmlhv7E/sN/FgSmm7b7DsqKoW6rVtQiCs1PWQ==", + "version": "3.1108.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1108.0.tgz", + "integrity": "sha512-rI80zxDxGJ6904eC/YbjkdjY6JdaZvQ01kOmrMvw7cFQGIHo27fhnIVbMSVDS4T6foQImjxYSRoOu/uSJscXDw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.936.0", - "@aws-sdk/nested-clients": "3.936.0", - "@aws-sdk/types": "3.936.0", - "@smithy/property-provider": "^4.2.5", - "@smithy/shared-ini-file-loader": "^4.4.0", - "@smithy/types": "^4.9.0", + "@aws-sdk/core": "^3.977.7", + "@aws-sdk/nested-clients": "^3.997.42", + "@aws-sdk/types": "^3.974.3", + "@smithy/core": "^3.31.1", + "@smithy/types": "^4.16.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/types": { - "version": "3.936.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.936.0.tgz", - "integrity": "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.9.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/util-endpoints": { - "version": "3.936.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.936.0.tgz", - "integrity": "sha512-0Zx3Ntdpu+z9Wlm7JKUBOzS9EunwKAb4KdGUQQxDqh5Lc3ta5uBoub+FgmVuzwnmBu9U1Os8UuwVTH0Lgu+P5w==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.936.0", - "@smithy/types": "^4.9.0", - "@smithy/url-parser": "^4.2.5", - "@smithy/util-endpoints": "^3.2.5", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/util-locate-window": { - "version": "3.893.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.893.0.tgz", - "integrity": "sha512-T89pFfgat6c8nMmpI8eKjBcDcgJq36+m9oiXbcUzeU55MP9ZuGgBomGjGnHaEyF36jenW9gmg3NfZDm0AO2XPg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.936.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.936.0.tgz", - "integrity": "sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.936.0", - "@smithy/types": "^4.9.0", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.936.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.936.0.tgz", - "integrity": "sha512-XOEc7PF9Op00pWV2AYCGDSu5iHgYjIO53Py2VUQTIvP7SRCaCsXmA33mjBvC2Ms6FhSyWNa4aK4naUGIz0hQcw==", + "version": "3.974.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.974.3.tgz", + "integrity": "sha512-ECAqfpNsef+7MO8qtR0h9KcFIBAygaE7Cm6UOiQl+ft+uVap+1G7bNEjs4mdJE2OnA4m6k7i8peH8uGIAsOMGw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-user-agent": "3.936.0", - "@aws-sdk/types": "3.936.0", - "@smithy/node-config-provider": "^4.3.5", - "@smithy/types": "^4.9.0", + "@smithy/types": "^4.16.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } + "node": ">=20.0.0" } }, "node_modules/@aws-sdk/xml-builder": { - "version": "3.930.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.930.0.tgz", - "integrity": "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA==", + "version": "3.972.38", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.38.tgz", + "integrity": "sha512-grf7mzfVxBS5AlsuTvBN7uDpzqohFww9fRPCO+EBSUdvtsYMcPSKdz54h/7XiscqNcUM1Ae1MF7JLHmiYYuzbQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.9.0", - "fast-xml-parser": "5.2.5", + "@smithy/types": "^4.16.1", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@aws/lambda-invoke-store": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.1.tgz", - "integrity": "sha512-sIyFcoPZkTtNu9xFeEoynMef3bPJIAbOfUh+ueYcfhVl6xm2VRtMcMclSxmZCMnHHd4hlYKJeq/aggmBEWynww==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.3.0.tgz", + "integrity": "sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ==", "license": "Apache-2.0", "engines": { "node": ">=18.0.0" } }, "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", + "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -813,9 +456,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "dev": true, "license": "MIT", "engines": { @@ -823,9 +466,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", - "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -842,39 +485,39 @@ } }, "node_modules/@cacheable/memory": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@cacheable/memory/-/memory-2.0.5.tgz", - "integrity": "sha512-fkiAxCvssEyJZ5fxX4tcdZFRmW9JehSTGvvqmXn6rTzG5cH6V/3C4ad8yb01vOjp2xBydHkHrgpW0qeGtzt6VQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cacheable/memory/-/memory-2.2.0.tgz", + "integrity": "sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ==", "license": "MIT", "dependencies": { - "@cacheable/utils": "^2.3.0", - "@keyv/bigmap": "^1.1.0", - "hookified": "^1.12.2", - "keyv": "^5.5.4" + "@cacheable/utils": "^2.5.0", + "@keyv/bigmap": "^1.3.1", + "hookified": "^1.15.1", + "keyv": "^5.6.0" } }, "node_modules/@cacheable/node-cache": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/@cacheable/node-cache/-/node-cache-1.7.5.tgz", - "integrity": "sha512-awY5UuLseuR5ytutXefzyEQeVXCm1reQqahAp1Qd39+mTzOaTV3VJx9qoX7juICEfYI0Q1SFj2m2+83l6+6UbQ==", + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@cacheable/node-cache/-/node-cache-1.7.6.tgz", + "integrity": "sha512-6Omk2SgNnjtxB5f/E6bTIWIt5xhdpx39fGNRQgU9lojvRxU68v+qY+SXXLsp3ZGukqoPjsK21wZ6XABFr/Ge3A==", "license": "MIT", "dependencies": { - "cacheable": "^2.2.0", - "hookified": "^1.13.0", - "keyv": "^5.5.4" + "cacheable": "^2.3.1", + "hookified": "^1.14.0", + "keyv": "^5.5.5" }, "engines": { "node": ">=18" } }, "node_modules/@cacheable/utils": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@cacheable/utils/-/utils-2.3.1.tgz", - "integrity": "sha512-38NJXjIr4W1Sghun8ju+uYWD8h2c61B4dKwfnQHVDFpAJ9oS28RpfqZQJ6Dgd3RceGkILDY9YT+72HJR3LoeSQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@cacheable/utils/-/utils-2.5.0.tgz", + "integrity": "sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA==", "license": "MIT", "dependencies": { - "hashery": "^1.2.0", - "keyv": "^5.5.4" + "hashery": "^1.5.1", + "keyv": "^5.6.0" } }, "node_modules/@commitlint/cli": { @@ -1135,9 +778,9 @@ } }, "node_modules/@emnapi/runtime": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", - "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", "license": "MIT", "optional": true, "dependencies": { @@ -1145,12 +788,13 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.0.tgz", - "integrity": "sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1161,12 +805,13 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.0.tgz", - "integrity": "sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1177,12 +822,13 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.0.tgz", - "integrity": "sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1193,12 +839,13 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.0.tgz", - "integrity": "sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1209,12 +856,13 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.0.tgz", - "integrity": "sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1225,12 +873,13 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.0.tgz", - "integrity": "sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1241,12 +890,13 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.0.tgz", - "integrity": "sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1257,12 +907,13 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.0.tgz", - "integrity": "sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1273,12 +924,13 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.0.tgz", - "integrity": "sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1289,12 +941,13 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.0.tgz", - "integrity": "sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1305,12 +958,13 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.0.tgz", - "integrity": "sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1321,12 +975,13 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.0.tgz", - "integrity": "sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", "cpu": [ "loong64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1337,12 +992,13 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.0.tgz", - "integrity": "sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", "cpu": [ "mips64el" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1353,12 +1009,13 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.0.tgz", - "integrity": "sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1369,12 +1026,13 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.0.tgz", - "integrity": "sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1385,12 +1043,13 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.0.tgz", - "integrity": "sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", "cpu": [ "s390x" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1401,12 +1060,13 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.0.tgz", - "integrity": "sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1417,12 +1077,13 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.0.tgz", - "integrity": "sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1433,12 +1094,13 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.0.tgz", - "integrity": "sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1449,12 +1111,13 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.0.tgz", - "integrity": "sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1465,12 +1128,13 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.0.tgz", - "integrity": "sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1481,12 +1145,13 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.0.tgz", - "integrity": "sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1497,12 +1162,13 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.0.tgz", - "integrity": "sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1513,12 +1179,13 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.0.tgz", - "integrity": "sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1529,12 +1196,13 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.0.tgz", - "integrity": "sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1545,12 +1213,13 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.0.tgz", - "integrity": "sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1567,9 +1236,9 @@ "license": "Apache-2.0" }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", - "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", "dev": true, "license": "MIT", "dependencies": { @@ -1620,9 +1289,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, "license": "MIT", "dependencies": { @@ -1636,10 +1305,17 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/@eslint/eslintrc/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -1665,9 +1341,9 @@ "license": "MIT" }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -1822,16 +1498,6 @@ "axios": "^0.27.2" } }, - "node_modules/@figuro/chatwoot-sdk/node_modules/axios": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", - "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.14.9", - "form-data": "^4.0.0" - } - }, "node_modules/@hapi/boom": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/@hapi/boom/-/boom-10.0.1.tgz", @@ -1863,10 +1529,17 @@ "node": ">=10.10.0" } }, + "node_modules/@humanwhocodes/config-array/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -1875,9 +1548,9 @@ } }, "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -1910,18 +1583,18 @@ "license": "BSD-3-Clause" }, "node_modules/@img/colour": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", - "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.3.tgz", + "integrity": "sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==", "cpu": [ "arm64" ], @@ -1931,19 +1604,19 @@ "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" + "@img/sharp-libvips-darwin-arm64": "1.3.2" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.3.tgz", + "integrity": "sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==", "cpu": [ "x64" ], @@ -1953,19 +1626,38 @@ "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" + "@img/sharp-libvips-darwin-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.3.tgz", + "integrity": "sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==", + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.2.tgz", + "integrity": "sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==", "cpu": [ "arm64" ], @@ -1979,9 +1671,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.2.tgz", + "integrity": "sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==", "cpu": [ "x64" ], @@ -1995,9 +1687,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.2.tgz", + "integrity": "sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==", "cpu": [ "arm" ], @@ -2011,9 +1703,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.2.tgz", + "integrity": "sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==", "cpu": [ "arm64" ], @@ -2027,9 +1719,9 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.2.tgz", + "integrity": "sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==", "cpu": [ "ppc64" ], @@ -2043,9 +1735,9 @@ } }, "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.2.tgz", + "integrity": "sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==", "cpu": [ "riscv64" ], @@ -2059,9 +1751,9 @@ } }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.2.tgz", + "integrity": "sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==", "cpu": [ "s390x" ], @@ -2075,9 +1767,9 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.2.tgz", + "integrity": "sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==", "cpu": [ "x64" ], @@ -2091,9 +1783,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.2.tgz", + "integrity": "sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==", "cpu": [ "arm64" ], @@ -2107,9 +1799,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.2.tgz", + "integrity": "sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==", "cpu": [ "x64" ], @@ -2123,9 +1815,9 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.3.tgz", + "integrity": "sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==", "cpu": [ "arm" ], @@ -2135,19 +1827,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" + "@img/sharp-libvips-linux-arm": "1.3.2" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.3.tgz", + "integrity": "sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==", "cpu": [ "arm64" ], @@ -2157,19 +1849,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" + "@img/sharp-libvips-linux-arm64": "1.3.2" } }, "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.3.tgz", + "integrity": "sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==", "cpu": [ "ppc64" ], @@ -2179,19 +1871,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" + "@img/sharp-libvips-linux-ppc64": "1.3.2" } }, "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.3.tgz", + "integrity": "sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==", "cpu": [ "riscv64" ], @@ -2201,19 +1893,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" + "@img/sharp-libvips-linux-riscv64": "1.3.2" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.3.tgz", + "integrity": "sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==", "cpu": [ "s390x" ], @@ -2223,19 +1915,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" + "@img/sharp-libvips-linux-s390x": "1.3.2" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.3.tgz", + "integrity": "sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==", "cpu": [ "x64" ], @@ -2245,19 +1937,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" + "@img/sharp-libvips-linux-x64": "1.3.2" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.3.tgz", + "integrity": "sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==", "cpu": [ "arm64" ], @@ -2267,19 +1959,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.3.tgz", + "integrity": "sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==", "cpu": [ "x64" ], @@ -2289,38 +1981,54 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + "@img/sharp-libvips-linuxmusl-x64": "1.3.2" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.3.tgz", + "integrity": "sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==", + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.1" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.3.tgz", + "integrity": "sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==", "cpu": [ "wasm32" ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "license": "Apache-2.0", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.7.0" + "@img/sharp-wasm32": "0.35.3" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.3.tgz", + "integrity": "sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==", "cpu": [ "arm64" ], @@ -2330,16 +2038,16 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.3.tgz", + "integrity": "sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==", "cpu": [ "ia32" ], @@ -2349,16 +2057,16 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": "^20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.3.tgz", + "integrity": "sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==", "cpu": [ "x64" ], @@ -2368,12 +2076,51 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/external-editor/node_modules/iconv-lite": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/@jimp/core": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/@jimp/core/-/core-1.6.1.tgz", @@ -2802,6 +2549,7 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", @@ -2812,6 +2560,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -2827,6 +2576,7 @@ "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -2834,19 +2584,19 @@ } }, "node_modules/@keyv/bigmap": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@keyv/bigmap/-/bigmap-1.3.0.tgz", - "integrity": "sha512-KT01GjzV6AQD5+IYrcpoYLkCu1Jod3nau1Z7EsEuViO3TZGRacSbO9MfHmbJ1WaOXFtWLxPVj169cn2WNKPkIg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@keyv/bigmap/-/bigmap-1.3.1.tgz", + "integrity": "sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==", "license": "MIT", "dependencies": { - "hashery": "^1.2.0", - "hookified": "^1.13.0" + "hashery": "^1.4.0", + "hookified": "^1.15.0" }, "engines": { "node": ">= 18" }, "peerDependencies": { - "keyv": "^5.5.4" + "keyv": "^5.6.0" } }, "node_modules/@keyv/serialize": { @@ -2855,6 +2605,23 @@ "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==", "license": "MIT" }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, "node_modules/@noble/hashes": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", @@ -2918,9 +2685,9 @@ } }, "node_modules/@opentelemetry/api": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", - "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz", + "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==", "license": "Apache-2.0", "peer": true, "engines": { @@ -2928,9 +2695,9 @@ } }, "node_modules/@opentelemetry/api-logs": { - "version": "0.204.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.204.0.tgz", - "integrity": "sha512-DqxY8yoAaiBPivoJD4UtgrMS8gEmzZ5lnaxzPojzLVHBGqPxgWm4zcuvcUHZiqQ6kRX2Klel2r9y8cA2HAtqpw==", + "version": "0.220.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.220.0.tgz", + "integrity": "sha512-CmVa4ImJ+ynfrPMNaAXHET6Bhb44SwzmfyVJFq9ni2jgXJR/l7C6gfVFddNmHP+ZOkP9cf4f9DBe68qVLTHc9w==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/api": "^1.3.0" @@ -2939,23 +2706,10 @@ "node": ">=8.0.0" } }, - "node_modules/@opentelemetry/context-async-hooks": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.2.0.tgz", - "integrity": "sha512-qRkLWiUEZNAmYapZ7KGS5C4OmBLcP/H2foXeOEaowYCR0wi89fHejrfYfbuLVCMLp/dWZXKvQusdbUEZjERfwQ==", - "license": "Apache-2.0", - "peer": true, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, "node_modules/@opentelemetry/core": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.2.0.tgz", - "integrity": "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.10.0.tgz", + "integrity": "sha512-/wNZ8twnEQQA4HoHu22+vcsdru6pWPWxW+7w+FlxT6Id7PE/WIbZmVKkte+PF72e0F2dnImFeHD2syyE1Mw6MQ==", "license": "Apache-2.0", "peer": true, "dependencies": { @@ -2969,32 +2723,15 @@ } }, "node_modules/@opentelemetry/instrumentation": { - "version": "0.204.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.204.0.tgz", - "integrity": "sha512-vV5+WSxktzoMP8JoYWKeopChy6G3HKk4UQ2hESCRDUUTZqQ3+nM3u8noVG0LmNfRWwcFBnbZ71GKC7vaYYdJ1g==", + "version": "0.220.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.220.0.tgz", + "integrity": "sha512-xQx3E2WxP1mDvKzxLxX+CTCtNLa560YJZ3087qYHerl2YmiKpv7AH+dAy7vmx+eVrZ5BwhfWUAVoKOoxCNHcpw==", "license": "Apache-2.0", "peer": true, "dependencies": { - "@opentelemetry/api-logs": "0.204.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-amqplib": { - "version": "0.51.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.51.0.tgz", - "integrity": "sha512-XGmjYwjVRktD4agFnWBWQXo9SiYHKBxR6Ag3MLXwtLE4R99N3a08kGKM5SC1qOFKIELcQDGFEFT9ydXMH00Luw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.204.0", - "@opentelemetry/semantic-conventions": "^1.27.0" + "@opentelemetry/api-logs": "0.220.0", + "import-in-the-middle": "^3.0.0", + "require-in-the-middle": "^8.0.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3003,588 +2740,172 @@ "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@opentelemetry/instrumentation-connect": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.48.0.tgz", - "integrity": "sha512-OMjc3SFL4pC16PeK+tDhwP7MRvDPalYCGSvGqUhX5rASkI2H0RuxZHOWElYeXkV0WP+70Gw6JHWac/2Zqwmhdw==", + "node_modules/@opentelemetry/resources": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.10.0.tgz", + "integrity": "sha512-q6MMm2zhggzsHVNbabYwut+a6nbuQQe3URUoxaojM/8K1IBfwwPzvxIjNi2/lI1TFe+fMHMW9MWhrtDLEXEnkA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.204.0", - "@opentelemetry/semantic-conventions": "^1.27.0", - "@types/connect": "3.4.38" + "@opentelemetry/core": "2.10.0", + "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@opentelemetry/instrumentation-dataloader": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.22.0.tgz", - "integrity": "sha512-bXnTcwtngQsI1CvodFkTemrrRSQjAjZxqHVc+CJZTDnidT0T6wt3jkKhnsjU/Kkkc0lacr6VdRpCu2CUWa0OKw==", + "node_modules/@opentelemetry/sdk-trace": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace/-/sdk-trace-2.10.0.tgz", + "integrity": "sha512-MfQGq3GRmTh5fM/y+OjaO0vj6+luCB1XO2gfXCalKCfgKw0eHL++sm75DNweC6ohlp+aFvACqeE0fYayqdRaoQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.204.0" + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@opentelemetry/instrumentation-express": { - "version": "0.53.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.53.0.tgz", - "integrity": "sha512-r/PBafQmFYRjuxLYEHJ3ze1iBnP2GDA1nXOSS6E02KnYNZAVjj6WcDA1MSthtdAUUK0XnotHvvWM8/qz7DMO5A==", + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.10.0.tgz", + "integrity": "sha512-GuYQQT7QD2EeO8lcZLRQzcbOyhqAzL+6WWTKTU9mSUBYBazkEDl+VrQcXQhbB08OWM9anD1aHleVadzulpOaUQ==", "license": "Apache-2.0", + "peer": true, "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.204.0", - "@opentelemetry/semantic-conventions": "^1.27.0" + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/sdk-trace": "2.10.0", + "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, - "node_modules/@opentelemetry/instrumentation-fs": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.24.0.tgz", - "integrity": "sha512-HjIxJ6CBRD770KNVaTdMXIv29Sjz4C1kPCCK5x1Ujpc6SNnLGPqUVyJYZ3LUhhnHAqdbrl83ogVWjCgeT4Q0yw==", + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.43.0.tgz", + "integrity": "sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==", "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.204.0" - }, "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "node": ">=14" } }, - "node_modules/@opentelemetry/instrumentation-generic-pool": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.48.0.tgz", - "integrity": "sha512-TLv/On8pufynNR+pUbpkyvuESVASZZKMlqCm4bBImTpXKTpqXaJJ3o/MUDeMlM91rpen+PEv2SeyOKcHCSlgag==", - "license": "Apache-2.0", + "node_modules/@paralleldrive/cuid2": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz", + "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==", + "license": "MIT", "dependencies": { - "@opentelemetry/instrumentation": "^0.204.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@noble/hashes": "^1.1.5" } }, - "node_modules/@opentelemetry/instrumentation-graphql": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.52.0.tgz", - "integrity": "sha512-3fEJ8jOOMwopvldY16KuzHbRhPk8wSsOTSF0v2psmOCGewh6ad+ZbkTx/xyUK9rUdUMWAxRVU0tFpj4Wx1vkPA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.204.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } + "node_modules/@pinojs/redact": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", + "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", + "license": "MIT" }, - "node_modules/@opentelemetry/instrumentation-hapi": { - "version": "0.51.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.51.0.tgz", - "integrity": "sha512-qyf27DaFNL1Qhbo/da+04MSCw982B02FhuOS5/UF+PMhM61CcOiu7fPuXj8TvbqyReQuJFljXE6UirlvoT/62g==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.204.0", - "@opentelemetry/semantic-conventions": "^1.27.0" - }, + "node_modules/@pkgr/core": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.3.6.tgz", + "integrity": "sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==", + "dev": true, + "license": "MIT", "engines": { - "node": "^18.19.0 || >=20.6.0" + "node": "^14.18.0 || >=16.0.0" }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "funding": { + "url": "https://opencollective.com/pkgr" } }, - "node_modules/@opentelemetry/instrumentation-http": { - "version": "0.204.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.204.0.tgz", - "integrity": "sha512-1afJYyGRA4OmHTv0FfNTrTAzoEjPQUYgd+8ih/lX0LlZBnGio/O80vxA0lN3knsJPS7FiDrsDrWq25K7oAzbkw==", + "node_modules/@prisma/client": { + "version": "6.19.3", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.19.3.tgz", + "integrity": "sha512-mKq3jQFhjvko5LTJFHGilsuQs+W+T3Gm451NzuTDGQxwCzwXHYnIu2zGkRoW+Exq3Rob7yp2MfzSrdIiZVhrBg==", + "hasInstallScript": true, "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.1.0", - "@opentelemetry/instrumentation": "0.204.0", - "@opentelemetry/semantic-conventions": "^1.29.0", - "forwarded-parse": "2.1.2" - }, "engines": { - "node": "^18.19.0 || >=20.6.0" + "node": ">=18.18" }, "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/core": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.1.0.tgz", - "integrity": "sha512-RMEtHsxJs/GiHHxYT58IY57UXAQTuUnZVco6ymDEqTNlJKTimM4qPUPVe8InNFyBjhHBEAx4k3Q8LtNayBsbUQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" + "prisma": "*", + "typescript": ">=5.1.0" }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" + "peerDependenciesMeta": { + "prisma": { + "optional": true + }, + "typescript": { + "optional": true + } } }, - "node_modules/@opentelemetry/instrumentation-ioredis": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.52.0.tgz", - "integrity": "sha512-rUvlyZwI90HRQPYicxpDGhT8setMrlHKokCtBtZgYxQWRF5RBbG4q0pGtbZvd7kyseuHbFpA3I/5z7M8b/5ywg==", + "node_modules/@prisma/config": { + "version": "6.19.3", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.19.3.tgz", + "integrity": "sha512-CBPT44BjlQxEt8kiMEauji2WHTDoVBOKl7UlewXmUgBPnr/oPRZC3psci5chJnYmH0ivEIog2OU9PGWoki3DLQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.204.0", - "@opentelemetry/redis-common": "^0.38.0", - "@opentelemetry/semantic-conventions": "^1.27.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "c12": "3.1.0", + "deepmerge-ts": "7.1.5", + "effect": "3.21.0", + "empathic": "2.0.0" } }, - "node_modules/@opentelemetry/instrumentation-kafkajs": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.14.0.tgz", - "integrity": "sha512-kbB5yXS47dTIdO/lfbbXlzhvHFturbux4EpP0+6H78Lk0Bn4QXiZQW7rmZY1xBCY16mNcCb8Yt0mhz85hTnSVA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.204.0", - "@opentelemetry/semantic-conventions": "^1.30.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } + "node_modules/@prisma/debug": { + "version": "6.19.3", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.19.3.tgz", + "integrity": "sha512-ljkJ+SgpXNktLG0Q/n4JGYCkKf0f8oYLyjImS2I8e2q2WCfdRRtWER062ZV/ixaNP2M2VKlWXVJiGzZaUgbKZw==", + "license": "Apache-2.0" }, - "node_modules/@opentelemetry/instrumentation-knex": { - "version": "0.49.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.49.0.tgz", - "integrity": "sha512-NKsRRT27fbIYL4Ix+BjjP8h4YveyKc+2gD6DMZbr5R5rUeDqfC8+DTfIt3c3ex3BIc5Vvek4rqHnN7q34ZetLQ==", + "node_modules/@prisma/engines": { + "version": "6.19.3", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.19.3.tgz", + "integrity": "sha512-RSYxtlYFl5pJ8ZePgMv0lZ9IzVCOdTPOegrs2qcbAEFrBI1G33h6wyC9kjQvo0DnYEhEVY0X4LsuFHXLKQk88g==", + "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.204.0", - "@opentelemetry/semantic-conventions": "^1.33.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@prisma/debug": "6.19.3", + "@prisma/engines-version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", + "@prisma/fetch-engine": "6.19.3", + "@prisma/get-platform": "6.19.3" } }, - "node_modules/@opentelemetry/instrumentation-koa": { - "version": "0.52.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.52.0.tgz", - "integrity": "sha512-JJSBYLDx/mNSy8Ibi/uQixu2rH0bZODJa8/cz04hEhRaiZQoeJ5UrOhO/mS87IdgVsHrnBOsZ6vDu09znupyuA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.204.0", - "@opentelemetry/semantic-conventions": "^1.27.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } + "node_modules/@prisma/engines-version": { + "version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7.tgz", + "integrity": "sha512-03bgb1VD5gvuumNf+7fVGBzfpJPjmqV423l/WxsWk2cNQ42JD0/SsFBPhN6z8iAvdHs07/7ei77SKu7aZfq8bA==", + "license": "Apache-2.0" }, - "node_modules/@opentelemetry/instrumentation-lru-memoizer": { - "version": "0.49.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.49.0.tgz", - "integrity": "sha512-ctXu+O/1HSadAxtjoEg2w307Z5iPyLOMM8IRNwjaKrIpNAthYGSOanChbk1kqY6zU5CrpkPHGdAT6jk8dXiMqw==", + "node_modules/@prisma/fetch-engine": { + "version": "6.19.3", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.19.3.tgz", + "integrity": "sha512-tKtl/qco9Nt7LU5iKhpultD8O4vMCZcU2CHjNTnRrL1QvSUr5W/GcyFPjNL87GtRrwBc7ubXXD9xy4EvLvt8JA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.204.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@prisma/debug": "6.19.3", + "@prisma/engines-version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", + "@prisma/get-platform": "6.19.3" } }, - "node_modules/@opentelemetry/instrumentation-mongodb": { - "version": "0.57.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.57.0.tgz", - "integrity": "sha512-KD6Rg0KSHWDkik+qjIOWoksi1xqSpix8TSPfquIK1DTmd9OTFb5PHmMkzJe16TAPVEuElUW8gvgP59cacFcrMQ==", + "node_modules/@prisma/get-platform": { + "version": "6.19.3", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.19.3.tgz", + "integrity": "sha512-xFj1VcJ1N3MKooOQAGO0W5tsd0W2QzIvW7DD7c/8H14Zmp4jseeWAITm+w2LLoLrlhoHdPPh0NMZ8mfL6puoHA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.204.0", - "@opentelemetry/semantic-conventions": "^1.27.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mongoose": { - "version": "0.51.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.51.0.tgz", - "integrity": "sha512-gwWaAlhhV2By7XcbyU3DOLMvzsgeaymwP/jktDC+/uPkCmgB61zurwqOQdeiRq9KAf22Y2dtE5ZLXxytJRbEVA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.204.0", - "@opentelemetry/semantic-conventions": "^1.27.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mysql": { - "version": "0.50.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.50.0.tgz", - "integrity": "sha512-duKAvMRI3vq6u9JwzIipY9zHfikN20bX05sL7GjDeLKr2qV0LQ4ADtKST7KStdGcQ+MTN5wghWbbVdLgNcB3rA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.204.0", - "@opentelemetry/semantic-conventions": "^1.27.0", - "@types/mysql": "2.15.27" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mysql2": { - "version": "0.51.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.51.0.tgz", - "integrity": "sha512-zT2Wg22Xn43RyfU3NOUmnFtb5zlDI0fKcijCj9AcK9zuLZ4ModgtLXOyBJSSfO+hsOCZSC1v/Fxwj+nZJFdzLQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.204.0", - "@opentelemetry/semantic-conventions": "^1.27.0", - "@opentelemetry/sql-common": "^0.41.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-pg": { - "version": "0.57.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.57.0.tgz", - "integrity": "sha512-dWLGE+r5lBgm2A8SaaSYDE3OKJ/kwwy5WLyGyzor8PLhUL9VnJRiY6qhp4njwhnljiLtzeffRtG2Mf/YyWLeTw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.204.0", - "@opentelemetry/semantic-conventions": "^1.34.0", - "@opentelemetry/sql-common": "^0.41.0", - "@types/pg": "8.15.5", - "@types/pg-pool": "2.0.6" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-redis": { - "version": "0.53.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.53.0.tgz", - "integrity": "sha512-WUHV8fr+8yo5RmzyU7D5BIE1zwiaNQcTyZPwtxlfr7px6NYYx7IIpSihJK7WA60npWynfxxK1T67RAVF0Gdfjg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.204.0", - "@opentelemetry/redis-common": "^0.38.0", - "@opentelemetry/semantic-conventions": "^1.27.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-tedious": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.23.0.tgz", - "integrity": "sha512-3TMTk/9VtlRonVTaU4tCzbg4YqW+Iq/l5VnN2e5whP6JgEg/PKfrGbqQ+CxQWNLfLaQYIUgEZqAn5gk/inh1uQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.204.0", - "@opentelemetry/semantic-conventions": "^1.27.0", - "@types/tedious": "^4.0.14" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-undici": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.15.0.tgz", - "integrity": "sha512-sNFGA/iCDlVkNjzTzPRcudmI11vT/WAfAguRdZY9IspCw02N4WSC72zTuQhSMheh2a1gdeM9my1imnKRvEEvEg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.204.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.7.0" - } - }, - "node_modules/@opentelemetry/redis-common": { - "version": "0.38.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/redis-common/-/redis-common-0.38.2.tgz", - "integrity": "sha512-1BCcU93iwSRZvDAgwUxC/DV4T/406SkMfxGqu5ojc3AvNI+I9GhV7v0J1HljsczuuhcnFLYqD5VmwVXfCGHzxA==", - "license": "Apache-2.0", - "engines": { - "node": "^18.19.0 || >=20.6.0" - } - }, - "node_modules/@opentelemetry/resources": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz", - "integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@opentelemetry/core": "2.2.0", - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-trace-base": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.2.0.tgz", - "integrity": "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@opentelemetry/core": "2.2.0", - "@opentelemetry/resources": "2.2.0", - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/semantic-conventions": { - "version": "1.38.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.38.0.tgz", - "integrity": "sha512-kocjix+/sSggfJhwXqClZ3i9Y/MI0fp7b+g7kCRm6psy2dsf8uApTRclwG18h8Avm7C9+fnt+O36PspJ/OzoWg==", - "license": "Apache-2.0", - "peer": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@opentelemetry/sql-common": { - "version": "0.41.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/sql-common/-/sql-common-0.41.2.tgz", - "integrity": "sha512-4mhWm3Z8z+i508zQJ7r6Xi7y4mmoJpdvH0fZPFRkWrdp5fq7hhZ2HhYokEOLkfqSMgPR4Z9EyB3DBkbKGOqZiQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.1.0" - } - }, - "node_modules/@paralleldrive/cuid2": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz", - "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==", - "license": "MIT", - "dependencies": { - "@noble/hashes": "^1.1.5" - } - }, - "node_modules/@pinojs/redact": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", - "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", - "license": "MIT" - }, - "node_modules/@pkgr/core": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", - "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/pkgr" - } - }, - "node_modules/@prisma/client": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.19.0.tgz", - "integrity": "sha512-QXFT+N/bva/QI2qoXmjBzL7D6aliPffIwP+81AdTGq0FXDoLxLkWivGMawG8iM5B9BKfxLIXxfWWAF6wbuJU6g==", - "hasInstallScript": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "peerDependencies": { - "prisma": "*", - "typescript": ">=5.1.0" - }, - "peerDependenciesMeta": { - "prisma": { - "optional": true - }, - "typescript": { - "optional": true - } - } - }, - "node_modules/@prisma/config": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.19.0.tgz", - "integrity": "sha512-zwCayme+NzI/WfrvFEtkFhhOaZb/hI+X8TTjzjJ252VbPxAl2hWHK5NMczmnG9sXck2lsXrxIZuK524E25UNmg==", - "license": "Apache-2.0", - "dependencies": { - "c12": "3.1.0", - "deepmerge-ts": "7.1.5", - "effect": "3.18.4", - "empathic": "2.0.0" - } - }, - "node_modules/@prisma/debug": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.19.0.tgz", - "integrity": "sha512-8hAdGG7JmxrzFcTzXZajlQCidX0XNkMJkpqtfbLV54wC6LSSX6Vni25W/G+nAANwLnZ2TmwkfIuWetA7jJxJFA==", - "license": "Apache-2.0" - }, - "node_modules/@prisma/engines": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.19.0.tgz", - "integrity": "sha512-pMRJ+1S6NVdXoB8QJAPIGpKZevFjxhKt0paCkRDTZiczKb7F4yTgRP8M4JdVkpQwmaD4EoJf6qA+p61godDokw==", - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "@prisma/debug": "6.19.0", - "@prisma/engines-version": "6.19.0-26.2ba551f319ab1df4bc874a89965d8b3641056773", - "@prisma/fetch-engine": "6.19.0", - "@prisma/get-platform": "6.19.0" - } - }, - "node_modules/@prisma/engines-version": { - "version": "6.19.0-26.2ba551f319ab1df4bc874a89965d8b3641056773", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.19.0-26.2ba551f319ab1df4bc874a89965d8b3641056773.tgz", - "integrity": "sha512-gV7uOBQfAFlWDvPJdQxMT1aSRur3a0EkU/6cfbAC5isV67tKDWUrPauyaHNpB+wN1ebM4A9jn/f4gH+3iHSYSQ==", - "license": "Apache-2.0" - }, - "node_modules/@prisma/fetch-engine": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.19.0.tgz", - "integrity": "sha512-OOx2Lda0DGrZ1rodADT06ZGqHzr7HY7LNMaFE2Vp8dp146uJld58sRuasdX0OiwpHgl8SqDTUKHNUyzEq7pDdQ==", - "license": "Apache-2.0", - "dependencies": { - "@prisma/debug": "6.19.0", - "@prisma/engines-version": "6.19.0-26.2ba551f319ab1df4bc874a89965d8b3641056773", - "@prisma/get-platform": "6.19.0" - } - }, - "node_modules/@prisma/get-platform": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.19.0.tgz", - "integrity": "sha512-ym85WDO2yDhC3fIXHWYpG3kVMBA49cL1XD2GCsCF8xbwoy2OkDQY44gEbAt2X46IQ4Apq9H6g0Ex1iFfPqEkHA==", - "license": "Apache-2.0", - "dependencies": { - "@prisma/debug": "6.19.0" - } - }, - "node_modules/@prisma/instrumentation": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@prisma/instrumentation/-/instrumentation-6.15.0.tgz", - "integrity": "sha512-6TXaH6OmDkMOQvOxwLZ8XS51hU2v4A3vmE2pSijCIiGRJYyNeMcL6nMHQMyYdZRD8wl7LF3Wzc+AMPMV/9Oo7A==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.52.0 || ^0.53.0 || ^0.54.0 || ^0.55.0 || ^0.56.0 || ^0.57.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.8" - } - }, - "node_modules/@prisma/instrumentation/node_modules/@opentelemetry/api-logs": { - "version": "0.57.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.57.2.tgz", - "integrity": "sha512-uIX52NnTM0iBh84MShlpouI7UKqkZ7MrUszTmaypHBu4r7NofznSnQRfJ+uUeDtQDj6w8eFGg5KBLDAwAPz1+A==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@prisma/instrumentation/node_modules/@opentelemetry/instrumentation": { - "version": "0.57.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.57.2.tgz", - "integrity": "sha512-BdBGhQBh8IjZ2oIIX6F2/Q3LKm/FDDKi6ccYKcBTeilh6SNdNKveDOLk73BkSJjQLJk6qe4Yh+hHw1UPhCDdrg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.57.2", - "@types/shimmer": "^1.2.0", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1", - "semver": "^7.5.2", - "shimmer": "^1.2.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" + "@prisma/debug": "6.19.3" } }, "node_modules/@protobufjs/aspromise": { @@ -3705,12 +3026,13 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", - "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.4.tgz", + "integrity": "sha512-RrPokAb7dmbxFoeO3TloqHyOjgye8RkBhSqmp4aJMIex4c9r46ZstPnleDQOq1t46VOVjwIuwNogIqbodV1Vvg==", "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3718,12 +3040,13 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", - "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.4.tgz", + "integrity": "sha512-JKuJc+pnpks2pjy7L/N3v/cAkZxYlnmuZoD840ldbMI5KDbC4iO9NKwPKYdjYFCMAIIlBzYSFHxIJVYzRo2/8A==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3731,12 +3054,13 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz", - "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.4.tgz", + "integrity": "sha512-krw5uS2STmvJ02x0uTXHbqQNuz+9eZ1iw+qXk9dmW2gvV4jV7O2hEoOnuhFrpOPiel1mBFtqbxYZZtC46hXLOw==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3744,12 +3068,13 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz", - "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.4.tgz", + "integrity": "sha512-wsTxtgApb4PrOsNJIm0FZ1h3WvCC+k9uxLJ4ad75hgoS4NiRes2SoJFlDAyMwiUY8IssDqGcHbXuN0sx1tfF1A==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3757,12 +3082,13 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", - "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.4.tgz", + "integrity": "sha512-GUOnQlyZe3yAXhWOtOMsn5Qkrv5E5mZXa0thbARWi5Ei2szlVXJFQhddZ4HbAzh8q92w5twp+CQvs/eFanz9YQ==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3770,12 +3096,13 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", - "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.4.tgz", + "integrity": "sha512-/Y7f3QuxjzPKsjA/rfEDa3+0vXqyjmJ50Ln8dPpCmWkKTrUoWHG1cWhTqaAMLob2m2nESWuC7yGrREz019Ztqg==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3783,12 +3110,13 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", - "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.4.tgz", + "integrity": "sha512-81wiiX3v7aqy+T+bT61TJ78yJjRquqFFTTbAPt08imfQQzkPIW8t6aJbkTagtCCrXMNc9D66+geqlK7ydLPNqA==", "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3796,12 +3124,13 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", - "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.4.tgz", + "integrity": "sha512-9kmDIvNZqdoHOBZgNtpTBeLWYO/LVipM3H/j62P8848/l/VPEQL6N3uxU9pvP1oZAsXyC2MEnFP3ovRjo7WYNQ==", "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3809,12 +3138,13 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", - "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.4.tgz", + "integrity": "sha512-CcnXHWnXg69g+DX5VWL3FHts3qMRN2uVEHX+BZvGLdd07/gXkn3ePjYtO1LDJvxkGKVHMclKBRa1QUTH+6toYQ==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3822,12 +3152,13 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", - "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.4.tgz", + "integrity": "sha512-iFOibiHnTRuhrWLlRsOQFdZJJIa7S8OwkneJr4ocALP16u5yk6lWLINFwhHaEqBFMsKDUZofLkGos7+CPzGB3g==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3835,12 +3166,27 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", - "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.4.tgz", + "integrity": "sha512-XnWYMI7euHlb5a871xPja+Gm7DRCFU+FGRrtS2sMq9N8FvqtpagUy6gD4YOemC5MRk9xbh8+jYMEJbigFQwsgA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.4.tgz", + "integrity": "sha512-qGDAlO0U8xedCcsdRm9oaoQY8DAx/QT7uIxJWhCdx0ceIWX783UC9QSYkdpzAe29wNiVfp24+bZdQmn49o45SQ==", "cpu": [ "loong64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3848,12 +3194,27 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", - "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.4.tgz", + "integrity": "sha512-ru4H6ezD7ysA5EiEK6qkkaEb4modH8CTej6kUy/gQi20u3kB3G7Zn8snXXkeJSCOFKG/rbPPtM/+9Wgas1961w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.4.tgz", + "integrity": "sha512-2W4MO5WQVJnbJaZdvDb9rhBDuFU1nKIepPFpJUBsTh2k1YY2g+ODViaWuyOAjQ5cOP7NvrvLzt3wvHOoiAvc7w==", "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3861,12 +3222,13 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", - "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.4.tgz", + "integrity": "sha512-+fxjfuoAmVMCYV5QyjoIpu0cp5DOiOTeqYFk1AVaxGr+/ravWLX89XfQmptsoWcaVy/TGf2hexzbUOrCQIL1CQ==", "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3874,12 +3236,13 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", - "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.4.tgz", + "integrity": "sha512-jTn8JfHGL4djjFxPuM06LmNUJDsst2jeVlsd9OmIH6zc5sC9K6rIuO4YajXatLUpBmBKl6b35ro1QZocLi+tcA==", "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3887,12 +3250,13 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", - "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.4.tgz", + "integrity": "sha512-oCJCJL4pXsoDcP2QZ+JVlPTIRc6266zsIaeJJsWImmF7HO0W8nb6HuSgZlMWxJwaPf8ehbSw8yo0EUw925hKsA==", "cpu": [ "s390x" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3900,12 +3264,13 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", - "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.4.tgz", + "integrity": "sha512-W69hukhZ3KKNRCaMIEzKvcFye42hh0FE1+YoYaf5+Ikacuftoco6yO/xouz0hc5d5W/s3yBro5jRiuEE/Q5vUw==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3913,25 +3278,41 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", - "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.4.tgz", + "integrity": "sha512-qiXbGG2jkjXhzXpsFZSR2Xpb8DN/UaxYsbb/STbuR/6fpaDgRmmaq1B/LmtF2wQFOFOSsK2jdE0RZ3a0zHn4QA==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ] }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", - "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.4.tgz", + "integrity": "sha512-nWeM//hxv8mIo6jD7Hu4o48DVmV9pbV6gsKaWU+4NFyqHoPKwrkRiZGLKUhOBk8qNmDmpwFtPKg80Bo/Tn4xiQ==", "cpu": [ - "arm64" + "x64" ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.4.tgz", + "integrity": "sha512-s62SQ/vgsRSvMwDkOEfTqfgASF0f26ZNaQuTA6Aok5lrikf89yI2W0gFHvZb2Jpgc6N8JnOKZgCK2iciO3CsxQ==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3939,12 +3320,13 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz", - "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.4.tgz", + "integrity": "sha512-J6wGf8TVGbXJq+HH+ttTvrcfNKPbuZecV6KT1B8I18BC5IURUh5kl4Yl5OEP5eFIUoI5BWxCsyYMhFsDx8kekw==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3952,12 +3334,13 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", - "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.4.tgz", + "integrity": "sha512-zmfrQd/0wu6oJs8Vq8KwY/YtsKSsLtKe/HwAP4Wqy8LhWjeT55fHRAkOhYQ12wI3ayS4Tt12d5CDRD7N96SAYQ==", "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3965,12 +3348,13 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz", - "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.4.tgz", + "integrity": "sha512-qPzHqdj9rfUD+w79dtE07zi/kFwKyCJqplp5K5ygeLTp7jLpAoc16OAH39HSmRC9UpozaecsleI8uAdEj6v2yw==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3978,12 +3362,13 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", - "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.4.tgz", + "integrity": "sha512-zD6NdeWEByGE9QF9vCrlJ5YQB4oq9q91kPZS37Jwj5hOkvR1lTBSpsKhKDw4IJtbQ35LsTS1HD9DZYGKIshU1Q==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3991,703 +3376,206 @@ ] }, "node_modules/@rtsao/scc": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", - "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", - "dev": true, - "license": "MIT" - }, - "node_modules/@scarf/scarf": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz", - "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==", - "hasInstallScript": true, - "license": "Apache-2.0" - }, - "node_modules/@sentry/core": { - "version": "10.26.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.26.0.tgz", - "integrity": "sha512-TjDe5QI37SLuV0q3nMOH8JcPZhv2e85FALaQMIhRILH9Ce6G7xW5GSjmH91NUVq8yc3XtiqYlz/EenEZActc4Q==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@sentry/node": { - "version": "10.26.0", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-10.26.0.tgz", - "integrity": "sha512-VUwNoKYhRpnHQSj9lty1TgooO+1wcpS1V0K87HU8sZEas5gx3Ujiouk5ocPjlgbKreoYOApgOnEEIq5W/hfQcQ==", - "license": "MIT", - "dependencies": { - "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^2.1.0", - "@opentelemetry/core": "^2.1.0", - "@opentelemetry/instrumentation": "^0.204.0", - "@opentelemetry/instrumentation-amqplib": "0.51.0", - "@opentelemetry/instrumentation-connect": "0.48.0", - "@opentelemetry/instrumentation-dataloader": "0.22.0", - "@opentelemetry/instrumentation-express": "0.53.0", - "@opentelemetry/instrumentation-fs": "0.24.0", - "@opentelemetry/instrumentation-generic-pool": "0.48.0", - "@opentelemetry/instrumentation-graphql": "0.52.0", - "@opentelemetry/instrumentation-hapi": "0.51.0", - "@opentelemetry/instrumentation-http": "0.204.0", - "@opentelemetry/instrumentation-ioredis": "0.52.0", - "@opentelemetry/instrumentation-kafkajs": "0.14.0", - "@opentelemetry/instrumentation-knex": "0.49.0", - "@opentelemetry/instrumentation-koa": "0.52.0", - "@opentelemetry/instrumentation-lru-memoizer": "0.49.0", - "@opentelemetry/instrumentation-mongodb": "0.57.0", - "@opentelemetry/instrumentation-mongoose": "0.51.0", - "@opentelemetry/instrumentation-mysql": "0.50.0", - "@opentelemetry/instrumentation-mysql2": "0.51.0", - "@opentelemetry/instrumentation-pg": "0.57.0", - "@opentelemetry/instrumentation-redis": "0.53.0", - "@opentelemetry/instrumentation-tedious": "0.23.0", - "@opentelemetry/instrumentation-undici": "0.15.0", - "@opentelemetry/resources": "^2.1.0", - "@opentelemetry/sdk-trace-base": "^2.1.0", - "@opentelemetry/semantic-conventions": "^1.37.0", - "@prisma/instrumentation": "6.15.0", - "@sentry/core": "10.26.0", - "@sentry/node-core": "10.26.0", - "@sentry/opentelemetry": "10.26.0", - "import-in-the-middle": "^1.14.2", - "minimatch": "^9.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@sentry/node-core": { - "version": "10.26.0", - "resolved": "https://registry.npmjs.org/@sentry/node-core/-/node-core-10.26.0.tgz", - "integrity": "sha512-7OrHVn8XAsq9mMVMlpL18XTKQEVcLOJSo0n2M7QGKfFk/OfVtSFMcUWGqN1qhYtT9aMTr2bjtR5+BI33djnNTQ==", - "license": "MIT", - "dependencies": { - "@apm-js-collab/tracing-hooks": "^0.3.1", - "@sentry/core": "10.26.0", - "@sentry/opentelemetry": "10.26.0", - "import-in-the-middle": "^1.14.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^1.30.1 || ^2.1.0", - "@opentelemetry/core": "^1.30.1 || ^2.1.0", - "@opentelemetry/instrumentation": ">=0.57.1 <1", - "@opentelemetry/resources": "^1.30.1 || ^2.1.0", - "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0", - "@opentelemetry/semantic-conventions": "^1.37.0" - } - }, - "node_modules/@sentry/opentelemetry": { - "version": "10.26.0", - "resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-10.26.0.tgz", - "integrity": "sha512-ASJdOwn6NwMH2ZeBrnGJI+l/xkJp1AOiQ5FWkvTqLc/vHX+r3PDMO7c+koecY+LiZxSzZF4IV8sALXfOh6UnwA==", - "license": "MIT", - "dependencies": { - "@sentry/core": "10.26.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^1.30.1 || ^2.1.0", - "@opentelemetry/core": "^1.30.1 || ^2.1.0", - "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0", - "@opentelemetry/semantic-conventions": "^1.37.0" - } - }, - "node_modules/@smithy/abort-controller": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.5.tgz", - "integrity": "sha512-j7HwVkBw68YW8UmFRcjZOmssE77Rvk0GWAIN1oFBhsaovQmZWYCIcGa9/pwRB0ExI8Sk9MWNALTjftjHZea7VA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.9.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/config-resolver": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.3.tgz", - "integrity": "sha512-ezHLe1tKLUxDJo2LHtDuEDyWXolw8WGOR92qb4bQdWq/zKenO5BvctZGrVJBK08zjezSk7bmbKFOXIVyChvDLw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.3.5", - "@smithy/types": "^4.9.0", - "@smithy/util-config-provider": "^4.2.0", - "@smithy/util-endpoints": "^3.2.5", - "@smithy/util-middleware": "^4.2.5", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/core": { - "version": "3.18.5", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.18.5.tgz", - "integrity": "sha512-6gnIz3h+PEPQGDj8MnRSjDvKBah042jEoPgjFGJ4iJLBE78L4lY/n98x14XyPF4u3lN179Ub/ZKFY5za9GeLQw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/middleware-serde": "^4.2.6", - "@smithy/protocol-http": "^5.3.5", - "@smithy/types": "^4.9.0", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-middleware": "^4.2.5", - "@smithy/util-stream": "^4.5.6", - "@smithy/util-utf8": "^4.2.0", - "@smithy/uuid": "^1.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/credential-provider-imds": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.5.tgz", - "integrity": "sha512-BZwotjoZWn9+36nimwm/OLIcVe+KYRwzMjfhd4QT7QxPm9WY0HiOV8t/Wlh+HVUif0SBVV7ksq8//hPaBC/okQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.3.5", - "@smithy/property-provider": "^4.2.5", - "@smithy/types": "^4.9.0", - "@smithy/url-parser": "^4.2.5", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/fetch-http-handler": { - "version": "5.3.6", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.6.tgz", - "integrity": "sha512-3+RG3EA6BBJ/ofZUeTFJA7mHfSYrZtQIrDP9dI8Lf7X6Jbos2jptuLrAAteDiFVrmbEmLSuRG/bUKzfAXk7dhg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^5.3.5", - "@smithy/querystring-builder": "^4.2.5", - "@smithy/types": "^4.9.0", - "@smithy/util-base64": "^4.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/hash-node": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.5.tgz", - "integrity": "sha512-DpYX914YOfA3UDT9CN1BM787PcHfWRBB43fFGCYrZFUH0Jv+5t8yYl+Pd5PW4+QzoGEDvn5d5QIO4j2HyYZQSA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.9.0", - "@smithy/util-buffer-from": "^4.2.0", - "@smithy/util-utf8": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/invalid-dependency": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.5.tgz", - "integrity": "sha512-2L2erASEro1WC5nV+plwIMxrTXpvpfzl4e+Nre6vBVRR2HKeGGcvpJyyL3/PpiSg+cJG2KpTmZmq934Olb6e5A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.9.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/is-array-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz", - "integrity": "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/md5-js": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.2.5.tgz", - "integrity": "sha512-Bt6jpSTMWfjCtC0s79gZ/WZ1w90grfmopVOWqkI2ovhjpD5Q2XRXuecIPB9689L2+cCySMbaXDhBPU56FKNDNg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.9.0", - "@smithy/util-utf8": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-content-length": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.5.tgz", - "integrity": "sha512-Y/RabVa5vbl5FuHYV2vUCwvh/dqzrEY/K2yWPSqvhFUwIY0atLqO4TienjBXakoy4zrKAMCZwg+YEqmH7jaN7A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^5.3.5", - "@smithy/types": "^4.9.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-endpoint": { - "version": "4.3.12", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.12.tgz", - "integrity": "sha512-9pAX/H+VQPzNbouhDhkW723igBMLgrI8OtX+++M7iKJgg/zY/Ig3i1e6seCcx22FWhE6Q/S61BRdi2wXBORT+A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.18.5", - "@smithy/middleware-serde": "^4.2.6", - "@smithy/node-config-provider": "^4.3.5", - "@smithy/shared-ini-file-loader": "^4.4.0", - "@smithy/types": "^4.9.0", - "@smithy/url-parser": "^4.2.5", - "@smithy/util-middleware": "^4.2.5", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-retry": { - "version": "4.4.12", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.12.tgz", - "integrity": "sha512-S4kWNKFowYd0lID7/DBqWHOQxmxlsf0jBaos9chQZUWTVOjSW1Ogyh8/ib5tM+agFDJ/TCxuCTvrnlc+9cIBcQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.3.5", - "@smithy/protocol-http": "^5.3.5", - "@smithy/service-error-classification": "^4.2.5", - "@smithy/smithy-client": "^4.9.8", - "@smithy/types": "^4.9.0", - "@smithy/util-middleware": "^4.2.5", - "@smithy/util-retry": "^4.2.5", - "@smithy/uuid": "^1.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-serde": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.6.tgz", - "integrity": "sha512-VkLoE/z7e2g8pirwisLz8XJWedUSY8my/qrp81VmAdyrhi94T+riBfwP+AOEEFR9rFTSonC/5D2eWNmFabHyGQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^5.3.5", - "@smithy/types": "^4.9.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-stack": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.5.tgz", - "integrity": "sha512-bYrutc+neOyWxtZdbB2USbQttZN0mXaOyYLIsaTbJhFsfpXyGWUxJpEuO1rJ8IIJm2qH4+xJT0mxUSsEDTYwdQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.9.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/node-config-provider": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.5.tgz", - "integrity": "sha512-UTurh1C4qkVCtqggI36DGbLB2Kv8UlcFdMXDcWMbqVY2uRg0XmT9Pb4Vj6oSQ34eizO1fvR0RnFV4Axw4IrrAg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/property-provider": "^4.2.5", - "@smithy/shared-ini-file-loader": "^4.4.0", - "@smithy/types": "^4.9.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/node-http-handler": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.5.tgz", - "integrity": "sha512-CMnzM9R2WqlqXQGtIlsHMEZfXKJVTIrqCNoSd/QpAyp+Dw0a1Vps13l6ma1fH8g7zSPNsA59B/kWgeylFuA/lw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/abort-controller": "^4.2.5", - "@smithy/protocol-http": "^5.3.5", - "@smithy/querystring-builder": "^4.2.5", - "@smithy/types": "^4.9.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/property-provider": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.5.tgz", - "integrity": "sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.9.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/protocol-http": { - "version": "5.3.5", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.5.tgz", - "integrity": "sha512-RlaL+sA0LNMp03bf7XPbFmT5gN+w3besXSWMkA8rcmxLSVfiEXElQi4O2IWwPfxzcHkxqrwBFMbngB8yx/RvaQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.9.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/querystring-builder": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.5.tgz", - "integrity": "sha512-y98otMI1saoajeik2kLfGyRp11e5U/iJYH/wLCh3aTV/XutbGT9nziKGkgCaMD1ghK7p6htHMm6b6scl9JRUWg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.9.0", - "@smithy/util-uri-escape": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/querystring-parser": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.5.tgz", - "integrity": "sha512-031WCTdPYgiQRYNPXznHXof2YM0GwL6SeaSyTH/P72M1Vz73TvCNH2Nq8Iu2IEPq9QP2yx0/nrw5YmSeAi/AjQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.9.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/service-error-classification": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.5.tgz", - "integrity": "sha512-8fEvK+WPE3wUAcDvqDQG1Vk3ANLR8Px979te96m84CbKAjBVf25rPYSzb4xU4hlTyho7VhOGnh5i62D/JVF0JQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.9.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/shared-ini-file-loader": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.0.tgz", - "integrity": "sha512-5WmZ5+kJgJDjwXXIzr1vDTG+RhF9wzSODQBfkrQ2VVkYALKGvZX1lgVSxEkgicSAFnFhPj5rudJV0zoinqS0bA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.9.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/signature-v4": { - "version": "5.3.5", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.5.tgz", - "integrity": "sha512-xSUfMu1FT7ccfSXkoLl/QRQBi2rOvi3tiBZU2Tdy3I6cgvZ6SEi9QNey+lqps/sJRnogIS+lq+B1gxxbra2a/w==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^4.2.0", - "@smithy/protocol-http": "^5.3.5", - "@smithy/types": "^4.9.0", - "@smithy/util-hex-encoding": "^4.2.0", - "@smithy/util-middleware": "^4.2.5", - "@smithy/util-uri-escape": "^4.2.0", - "@smithy/util-utf8": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/smithy-client": { - "version": "4.9.8", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.9.8.tgz", - "integrity": "sha512-8xgq3LgKDEFoIrLWBho/oYKyWByw9/corz7vuh1upv7ZBm0ZMjGYBhbn6v643WoIqA9UTcx5A5htEp/YatUwMA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.18.5", - "@smithy/middleware-endpoint": "^4.3.12", - "@smithy/middleware-stack": "^4.2.5", - "@smithy/protocol-http": "^5.3.5", - "@smithy/types": "^4.9.0", - "@smithy/util-stream": "^4.5.6", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/types": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.9.0.tgz", - "integrity": "sha512-MvUbdnXDTwykR8cB1WZvNNwqoWVaTRA0RLlLmf/cIFNMM2cKWz01X4Ly6SMC4Kks30r8tT3Cty0jmeWfiuyHTA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/url-parser": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.5.tgz", - "integrity": "sha512-VaxMGsilqFnK1CeBX+LXnSuaMx4sTL/6znSZh2829txWieazdVxr54HmiyTsIbpOTLcf5nYpq9lpzmwRdxj6rQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/querystring-parser": "^4.2.5", - "@smithy/types": "^4.9.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" }, - "node_modules/@smithy/util-base64": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.0.tgz", - "integrity": "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^4.2.0", - "@smithy/util-utf8": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } + "node_modules/@scarf/scarf": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz", + "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==", + "hasInstallScript": true, + "license": "Apache-2.0" }, - "node_modules/@smithy/util-body-length-browser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz", - "integrity": "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, + "node_modules/@sentry/conventions": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@sentry/conventions/-/conventions-0.16.0.tgz", + "integrity": "sha512-fO9PLmHdVURcSPUpWCItWAtgKiMwGdJHbovoSEyLplX5sxs2ugvI4CBPTrkkgqhObnZOD0CnWBKDzSVQYBKEyQ==", + "license": "MIT", "engines": { - "node": ">=18.0.0" + "node": ">=14" } }, - "node_modules/@smithy/util-body-length-node": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.1.tgz", - "integrity": "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==", - "license": "Apache-2.0", + "node_modules/@sentry/core": { + "version": "10.70.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.70.0.tgz", + "integrity": "sha512-ozhCTDqg89oB4XmWfAwuHshABpvT7AkRpaPnogopPfMAaI61G1t8EKCJ4W7aum8JSBonlfyjPCyW5oYZFm0KvA==", + "license": "MIT", "dependencies": { - "tslib": "^2.6.2" + "@sentry/conventions": "^0.16.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@smithy/util-buffer-from": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz", - "integrity": "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==", - "license": "Apache-2.0", + "node_modules/@sentry/node": { + "version": "10.70.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-10.70.0.tgz", + "integrity": "sha512-SPOOVxmKTVIEtqvOKkQT163e/pOwucjS7OPsCHyRs8sFR4nfBNu0EThplyqnvqd5BWBMTPH6WTBQfo+QWHV+HA==", + "license": "MIT", "dependencies": { - "@smithy/is-array-buffer": "^4.2.0", - "tslib": "^2.6.2" + "@opentelemetry/api": "^1.9.1", + "@opentelemetry/instrumentation": "^0.220.0", + "@opentelemetry/sdk-trace-base": "^2.9.0", + "@sentry/conventions": "^0.16.0", + "@sentry/core": "10.70.0", + "@sentry/node-core": "10.70.0", + "@sentry/opentelemetry": "10.70.0", + "@sentry/server-utils": "10.70.0", + "import-in-the-middle": "^3.0.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@smithy/util-config-provider": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz", - "integrity": "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==", - "license": "Apache-2.0", + "node_modules/@sentry/node-core": { + "version": "10.70.0", + "resolved": "https://registry.npmjs.org/@sentry/node-core/-/node-core-10.70.0.tgz", + "integrity": "sha512-oPOEVVNxv5WHtckx2i06Wi9FLWyvOg/1DUeX732jZ4iqT2nupINaMH4nF4f4kSvUThFnxkFSRQxwqOxgzMKhKA==", + "license": "MIT", "dependencies": { - "tslib": "^2.6.2" + "@sentry/conventions": "^0.16.0", + "@sentry/core": "10.70.0", + "@sentry/opentelemetry": "10.70.0", + "import-in-the-middle": "^3.0.0" }, "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.3.11", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.11.tgz", - "integrity": "sha512-yHv+r6wSQXEXTPVCIQTNmXVWs7ekBTpMVErjqZoWkYN75HIFN5y9+/+sYOejfAuvxWGvgzgxbTHa/oz61YTbKw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/property-provider": "^4.2.5", - "@smithy/smithy-client": "^4.9.8", - "@smithy/types": "^4.9.0", - "tslib": "^2.6.2" + "node": ">=18" }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.2.14", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.14.tgz", - "integrity": "sha512-ljZN3iRvaJUgulfvobIuG97q1iUuCMrvXAlkZ4msY+ZuVHQHDIqn7FKZCEj+bx8omz6kF5yQXms/xhzjIO5XiA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/config-resolver": "^4.4.3", - "@smithy/credential-provider-imds": "^4.2.5", - "@smithy/node-config-provider": "^4.3.5", - "@smithy/property-provider": "^4.2.5", - "@smithy/smithy-client": "^4.9.8", - "@smithy/types": "^4.9.0", - "tslib": "^2.6.2" + "peerDependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/core": "^1.30.1 || ^2.1.0", + "@opentelemetry/exporter-trace-otlp-http": ">=0.57.0 <1", + "@opentelemetry/instrumentation": ">=0.57.1 <1", + "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0" }, - "engines": { - "node": ">=18.0.0" + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@opentelemetry/core": { + "optional": true + }, + "@opentelemetry/exporter-trace-otlp-http": { + "optional": true + }, + "@opentelemetry/instrumentation": { + "optional": true + }, + "@opentelemetry/sdk-trace-base": { + "optional": true + } } }, - "node_modules/@smithy/util-endpoints": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.5.tgz", - "integrity": "sha512-3O63AAWu2cSNQZp+ayl9I3NapW1p1rR5mlVHcF6hAB1dPZUQFfRPYtplWX/3xrzWthPGj5FqB12taJJCfH6s8A==", - "license": "Apache-2.0", + "node_modules/@sentry/opentelemetry": { + "version": "10.70.0", + "resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-10.70.0.tgz", + "integrity": "sha512-UNV/2tqypcUK6FDzerAsFJn1Km/c4VZCYkUZDNbnV5S0cwAq2BYKMo4M5vovaLDBQlxA+Wk9ovbxi5wYjjl9fw==", + "license": "MIT", "dependencies": { - "@smithy/node-config-provider": "^4.3.5", - "@smithy/types": "^4.9.0", - "tslib": "^2.6.2" + "@sentry/conventions": "^0.16.0", + "@sentry/core": "10.70.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=18" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/core": "^1.30.1 || ^2.1.0", + "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0" } }, - "node_modules/@smithy/util-hex-encoding": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz", - "integrity": "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==", - "license": "Apache-2.0", + "node_modules/@sentry/server-utils": { + "version": "10.70.0", + "resolved": "https://registry.npmjs.org/@sentry/server-utils/-/server-utils-10.70.0.tgz", + "integrity": "sha512-rzegZjMFFgCp3o+N8+XU13rfSvz4B+f8rU0ijBGrQcHdMNyfsFDTu1UTm262JofmrV2u+s+D0u0vFTnqtOGkbA==", + "license": "MIT", "dependencies": { - "tslib": "^2.6.2" + "@apm-js-collab/code-transformer-bundler-plugins": "^0.7.3", + "@apm-js-collab/tracing-hooks": "^0.13.0", + "@sentry/conventions": "^0.16.0", + "@sentry/core": "10.70.0", + "meriyah": "^6.1.4" }, "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@smithy/util-middleware": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.5.tgz", - "integrity": "sha512-6Y3+rvBF7+PZOc40ybeZMcGln6xJGVeY60E7jy9Mv5iKpMJpHgRE6dKy9ScsVxvfAYuEX4Q9a65DQX90KaQ3bA==", + "node_modules/@smithy/core": { + "version": "3.32.0", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.32.0.tgz", + "integrity": "sha512-NAiCSC78fzbNIEWoheoF74Ob5ZorLijCHpMY26Fqvqg/+9LuyIqMfHDg2p8Yk1rqOyowtiL3y7WX0AW+teL6zw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.9.0", + "@smithy/types": "^4.17.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@smithy/util-retry": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.5.tgz", - "integrity": "sha512-GBj3+EZBbN4NAqJ/7pAhsXdfzdlznOh8PydUijy6FpNIMnHPSMO2/rP4HKu+UFeikJxShERk528oy7GT79YiJg==", + "node_modules/@smithy/credential-provider-imds": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.5.0.tgz", + "integrity": "sha512-2jsPi+7Zv2hSzD9IXR9D7DTqSn7mv4XalzRm+bESh53jiaUS3NKEUbpQFTJP0HhQy9qzZvluxQ3yS24zdRrqsA==", "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^4.2.5", - "@smithy/types": "^4.9.0", + "@smithy/core": "^3.32.0", + "@smithy/types": "^4.17.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@smithy/util-stream": { - "version": "4.5.6", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.6.tgz", - "integrity": "sha512-qWw/UM59TiaFrPevefOZ8CNBKbYEP6wBAIlLqxn3VAIo9rgnTNc4ASbVrqDmhuwI87usnjhdQrxodzAGFFzbRQ==", + "node_modules/@smithy/fetch-http-handler": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.7.0.tgz", + "integrity": "sha512-W/exA8T0LEzCQtJ02w4IzaEQPIspgarqZprb7W8FwnYiDowgCrjl2fTQ6FvuSSUnJORuepBF81abmBJwqh+0XQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/fetch-http-handler": "^5.3.6", - "@smithy/node-http-handler": "^4.4.5", - "@smithy/types": "^4.9.0", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-buffer-from": "^4.2.0", - "@smithy/util-hex-encoding": "^4.2.0", - "@smithy/util-utf8": "^4.2.0", + "@smithy/core": "^3.32.0", + "@smithy/types": "^4.17.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@smithy/util-uri-escape": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz", - "integrity": "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==", + "node_modules/@smithy/node-http-handler": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.10.0.tgz", + "integrity": "sha512-nrh7VxqzPQS/ip1hS293aI/OAWDWARQvjUxCfuKhyrfHa2gTdk28066RNeWLI1uuoHXaKAkOF8IcSAHuOp0+SA==", "license": "Apache-2.0", "dependencies": { + "@smithy/core": "^3.32.0", + "@smithy/types": "^4.17.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@smithy/util-utf8": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.0.tgz", - "integrity": "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==", + "node_modules/@smithy/signature-v4": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.7.0.tgz", + "integrity": "sha512-hCynhm22wMJ8wTF9crcwu8mxggtUrSLLJgDcGUvYFBqpofxycYJCGKOMYg4xtPPFtgNiDJSYmhsWLTrcU/g59Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/util-buffer-from": "^4.2.0", + "@smithy/core": "^3.32.0", + "@smithy/types": "^4.17.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@smithy/uuid": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.0.tgz", - "integrity": "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==", + "node_modules/@smithy/types": { + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.17.0.tgz", + "integrity": "sha512-Aw4joiM0ZdErpo39lCj8phT2lxoiKZV+KZzBxnnQhWVtU2Is/WffQSL04uUWRcXUse9Ln8vXZK6V/FwqRVnQpg==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -4703,15 +3591,15 @@ "license": "MIT" }, "node_modules/@standard-schema/spec": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", - "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", "license": "MIT" }, "node_modules/@thi.ng/bitstream": { - "version": "2.4.34", - "resolved": "https://registry.npmjs.org/@thi.ng/bitstream/-/bitstream-2.4.34.tgz", - "integrity": "sha512-1bS+601Ar/D2Smpv6xzr4Mk7pJV4CIs4LbLEpi2f09sAHssAByfnOeo2Vfh0MqX9Vt4jaOPXZqEVGcRX5rsgiw==", + "version": "2.4.54", + "resolved": "https://registry.npmjs.org/@thi.ng/bitstream/-/bitstream-2.4.54.tgz", + "integrity": "sha512-uInkAJge5O0bWWEaYKrQpMccPbFg0z6eIA5NDCJXPm7l3rjlDje6RBHBXll3LiQz9Y051EdzlAEQRaB5hEifdg==", "funding": [ { "type": "github", @@ -4728,16 +3616,16 @@ ], "license": "Apache-2.0", "dependencies": { - "@thi.ng/errors": "^2.5.48" + "@thi.ng/errors": "^2.6.16" }, "engines": { "node": ">=18" } }, "node_modules/@thi.ng/errors": { - "version": "2.5.48", - "resolved": "https://registry.npmjs.org/@thi.ng/errors/-/errors-2.5.48.tgz", - "integrity": "sha512-6LXqHT3DWruK3GrH+sIQPXZUQaoRrq0ASc90wd4MSPmGR53b4DuBLL6iE1dJtrQAmGx64LEor8yDIvAL8CZBzQ==", + "version": "2.6.16", + "resolved": "https://registry.npmjs.org/@thi.ng/errors/-/errors-2.6.16.tgz", + "integrity": "sha512-a7Lv/G0La5eTNUEIyLldpeYziyFSj3rOlWNeXFu8v+ZSb8w8EnQ/L0r0sKHux7Ru6RhUkAXrcaHU7xd1ZkWovA==", "funding": [ { "type": "github", @@ -4806,6 +3694,7 @@ "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, "license": "MIT", "dependencies": { "@types/node": "*" @@ -4831,9 +3720,9 @@ } }, "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "license": "MIT" }, "node_modules/@types/express": { @@ -4850,9 +3739,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.19.7", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.7.tgz", - "integrity": "sha512-FvPtiIf1LfhzsaIXhv/PHan/2FeQBbtBDtfX2QfvPxdUelMDEckK08SM6nqo1MIZY3RUlfA+HV8+hFUSio78qg==", + "version": "4.19.9", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.9.tgz", + "integrity": "sha512-QP2ESEe/ImWY0HDwNAnK9PvEffUyhLTnWkk7KXzHfyeWAnlrDe1fN77bXl6ia8KT3wPlmA7t9/VPRpnf4Ex9sg==", "dev": true, "license": "MIT", "dependencies": { @@ -4901,23 +3790,14 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/mysql": { - "version": "2.15.27", - "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.27.tgz", - "integrity": "sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/node": { - "version": "24.10.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", - "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", "license": "MIT", "peer": true, "dependencies": { - "undici-types": "~7.16.0" + "undici-types": "~7.18.0" } }, "node_modules/@types/node-cron": { @@ -4937,26 +3817,6 @@ "form-data": "^4.0.4" } }, - "node_modules/@types/pg": { - "version": "8.15.5", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.5.tgz", - "integrity": "sha512-LF7lF6zWEKxuT3/OR8wAZGzkg4ENGXFNyiV/JeOt9z5B+0ZVwbql9McqX5c/WStFq1GaGso7H1AzP/qSzmlCKQ==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "pg-protocol": "*", - "pg-types": "^2.2.0" - } - }, - "node_modules/@types/pg-pool": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/pg-pool/-/pg-pool-2.0.6.tgz", - "integrity": "sha512-TaAUE5rq2VQYxab5Ts7WZhKNmuN78Q6PiFonTDdpbx8a1H0M1vhy3rhiMjl+e2iHmogyMw7jZF4FrE6eJUy5HQ==", - "license": "MIT", - "dependencies": { - "@types/pg": "*" - } - }, "node_modules/@types/qrcode": { "version": "1.5.6", "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.6.tgz", @@ -4975,9 +3835,9 @@ "license": "MIT" }, "node_modules/@types/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==", "dev": true, "license": "MIT" }, @@ -5028,25 +3888,11 @@ "@types/node": "*" } }, - "node_modules/@types/shimmer": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz", - "integrity": "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==", - "license": "MIT" - }, - "node_modules/@types/tedious": { - "version": "4.0.14", - "resolved": "https://registry.npmjs.org/@types/tedious/-/tedious-4.0.14.tgz", - "integrity": "sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/uuid": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", + "dev": true, "license": "MIT" }, "node_modules/@types/validator": { @@ -5055,22 +3901,30 @@ "integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==", "license": "MIT" }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.47.0.tgz", - "integrity": "sha512-fe0rz9WJQ5t2iaLfdbDc9T80GJy0AeO453q8C3YCilnGozvOyCG5t+EZtg7j7D88+c3FipfP/x+wzGnh1xp8ZA==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.67.0.tgz", + "integrity": "sha512-Un7Heoyj65NREbKAyIrFxeM143NZpExWmy1Nep4DLeQOeLlTeumPjoNKnBrU5D5moWXbPJgRa5Uwcdu0faVNGQ==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.47.0", - "@typescript-eslint/type-utils": "8.47.0", - "@typescript-eslint/utils": "8.47.0", - "@typescript-eslint/visitor-keys": "8.47.0", - "graphemer": "^1.4.0", - "ignore": "^7.0.0", + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/type-utils": "8.67.0", + "@typescript-eslint/utils": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", + "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.1.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5080,24 +3934,24 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.47.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "@typescript-eslint/parser": "^8.67.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.47.0.tgz", - "integrity": "sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.67.0.tgz", + "integrity": "sha512-fUBfTuuEulWqX6V8+O3PtScV01tzYYRUDTAirHFKoRAt7nOzoGiPt0M/bB47wWNy0coOOcgEwAMUtBpykMxl6w==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.47.0", - "@typescript-eslint/types": "8.47.0", - "@typescript-eslint/typescript-estree": "8.47.0", - "@typescript-eslint/visitor-keys": "8.47.0", - "debug": "^4.3.4" + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5107,20 +3961,20 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.47.0.tgz", - "integrity": "sha512-2X4BX8hUeB5JcA1TQJ7GjcgulXQ+5UkNb0DL8gHsHUHdFoiCTJoYLTpib3LtSDPZsRET5ygN4qqIWrHyYIKERA==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.67.0.tgz", + "integrity": "sha512-cvE8c7ulYeXN9fYuszhCeCsbzyVEXuhrRCybnBre7TUmqb5nRmBfQAwCj0O3WJFDeyAZt4VYv51vMCC9LHSdYw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.47.0", - "@typescript-eslint/types": "^8.47.0", - "debug": "^4.3.4" + "@typescript-eslint/tsconfig-utils": "^8.67.0", + "@typescript-eslint/types": "^8.67.0", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5130,18 +3984,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.47.0.tgz", - "integrity": "sha512-a0TTJk4HXMkfpFkL9/WaGTNuv7JWfFTQFJd6zS9dVAjKsojmv9HT55xzbEpnZoY+VUb+YXLMp+ihMLz/UlZfDg==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.67.0.tgz", + "integrity": "sha512-EgvsleTwS4E+WzzSvem8fAUubLwatMNF1B5hHSLQxcvs7q2dtRhGyujHwLJSYlG41niJ7GP24Aha2+0mb1b2kg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.47.0", - "@typescript-eslint/visitor-keys": "8.47.0" + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5152,9 +4006,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.47.0.tgz", - "integrity": "sha512-ybUAvjy4ZCL11uryalkKxuT3w3sXJAuWhOoGS3T/Wu+iUu1tGJmk5ytSY8gbdACNARmcYEB0COksD2j6hfGK2g==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.67.0.tgz", + "integrity": "sha512-vV+LUSv5njUWsknE71fqKTlXUva+R76SaeORd6Zojcunk/6DvKFXONU3BrAs2H49mbygUXt6gbYunzwqNwlhdg==", "dev": true, "license": "MIT", "engines": { @@ -5165,21 +4019,21 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.47.0.tgz", - "integrity": "sha512-QC9RiCmZ2HmIdCEvhd1aJELBlD93ErziOXXlHEZyuBo3tBiAZieya0HLIxp+DoDWlsQqDawyKuNEhORyku+P8A==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.67.0.tgz", + "integrity": "sha512-aVWDXbRmdXO9siTfX4ditQI1T9+zVcNazT48EJCD0v40/9RIFoUgZ05CmGEq9H2gixRpjUn/iplwvlcvutJW/Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.47.0", - "@typescript-eslint/typescript-estree": "8.47.0", - "@typescript-eslint/utils": "8.47.0", - "debug": "^4.3.4", - "ts-api-utils": "^2.1.0" + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/utils": "8.67.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5189,14 +4043,14 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.47.0.tgz", - "integrity": "sha512-nHAE6bMKsizhA2uuYZbEbmp5z2UpffNrPEqiKIeN7VsV6UY/roxanWfoRrf6x/k9+Obf+GQdkm0nPU+vnMXo9A==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.67.0.tgz", + "integrity": "sha512-sBtgslww8nsMYUjhdPBiSyUqSzT8uR6g93A2QXnQC8+cGdjz0CyaOdqHDRJb1AtORbZCNUJBBeFA/tNR2uQmww==", "dev": true, "license": "MIT", "engines": { @@ -5208,22 +4062,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.47.0.tgz", - "integrity": "sha512-k6ti9UepJf5NpzCjH31hQNLHQWupTRPhZ+KFF8WtTuTpy7uHPfeg2NM7cP27aCGajoEplxJDFVCEm9TGPYyiVg==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.67.0.tgz", + "integrity": "sha512-EKQBCE9yNlRJYm7jdTW5AhDacDUmSwQb0FAJAmK2EKYrNXIsa2vxcSZx6PvJ/dEdI6lS+Y9W+EXckLj0iPFGcw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.47.0", - "@typescript-eslint/tsconfig-utils": "8.47.0", - "@typescript-eslint/types": "8.47.0", - "@typescript-eslint/visitor-keys": "8.47.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" + "@typescript-eslint/project-service": "8.67.0", + "@typescript-eslint/tsconfig-utils": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5233,20 +4086,20 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/utils": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.47.0.tgz", - "integrity": "sha512-g7XrNf25iL4TJOiPqatNuaChyqt49a/onq5YsJ9+hXeugK+41LVg7AxikMfM02PC6jbNtZLCJj6AUcQXJS/jGQ==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.67.0.tgz", + "integrity": "sha512-U9D1FdwEWBwok3hxxSdhclMb0twvt9QnjIQ0VfQ1AiX2epnpSgv2ubVDsayOFyY8K6FX+AQ7E0FKWVG3iKsj1A==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.47.0", - "@typescript-eslint/types": "8.47.0", - "@typescript-eslint/typescript-estree": "8.47.0" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5256,19 +4109,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.47.0.tgz", - "integrity": "sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.67.0.tgz", + "integrity": "sha512-fkv8dHRDqfGtTHuJeebdrQ7cX6Ad4WAS00rgHh9UGvMycF1mjBfsxry1XsLIFhWZ6Judlh6UdzK+TYlbpCXgnA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.47.0", - "eslint-visitor-keys": "^4.2.1" + "@typescript-eslint/types": "8.67.0", + "eslint-visitor-keys": "^5.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5279,22 +4132,22 @@ } }, "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, "license": "Apache-2.0", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.3.tgz", + "integrity": "sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==", "dev": true, "license": "ISC" }, @@ -5349,13 +4202,6 @@ "url": "https://github.com/sponsors/eshaz" } }, - "node_modules/@zxing/text-encoding": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz", - "integrity": "sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==", - "license": "(Unlicense OR Apache-2.0)", - "optional": true - }, "node_modules/abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -5391,9 +4237,10 @@ } }, "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", + "dev": true, "license": "MIT", "peer": true, "bin": { @@ -5403,15 +4250,6 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", - "license": "MIT", - "peerDependencies": { - "acorn": "^8" - } - }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -5444,9 +4282,9 @@ } }, "node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "dev": true, "license": "MIT", "dependencies": { @@ -5533,6 +4371,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, "license": "MIT" }, "node_modules/anynum": { @@ -5695,6 +4534,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/astring": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", + "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", + "license": "MIT", + "bin": { + "astring": "bin/astring" + } + }, "node_modules/async": { "version": "0.2.10", "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", @@ -5754,6 +4602,7 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/audio-decode/-/audio-decode-2.2.3.tgz", "integrity": "sha512-Z0lHvMayR/Pad9+O9ddzaBJE0DrhZkQlStrC1RwcAHF3AhQAsdwKHeLGK8fYKyp2DDU6xHxzGb4CLMui12yVrg==", + "deprecated": "Renamed to @audio/decode — same API; this name remains a thin alias. npm i @audio/decode", "license": "MIT", "peer": true, "dependencies": { @@ -5768,18 +4617,22 @@ } }, "node_modules/audio-type": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/audio-type/-/audio-type-2.2.1.tgz", - "integrity": "sha512-En9AY6EG1qYqEy5L/quryzbA4akBpJrnBZNxeKTqGHC2xT9Qc4aZ8b7CcbOMFTTc/MGdoNyp+SN4zInZNKxMYA==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/audio-type/-/audio-type-2.4.2.tgz", + "integrity": "sha512-E9Bl1FGGDS81H/mKvaoPq0iQ8uT3qTBe6XWjipOo5kYrhLMAbjD7hREAVle0YJFahZOVbztxTdkwi0VBDJutSA==", "license": "MIT", "engines": { "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/audiojs" } }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, "license": "MIT", "dependencies": { "possible-typed-array-names": "^1.0.0" @@ -5801,14 +4654,40 @@ } }, "node_modules/axios": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", - "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.19.0.tgz", + "integrity": "sha512-ht/iuYZXEjFxLH/Hkezgd7m6JKlHHXEUSneaDz8uZe1Gj5QZtCnpyDsckvAiEnT89OEbCLmnte4R4sn7P0EKFw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.6", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/axios/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/axios/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" } }, "node_modules/baileys": { @@ -5867,10 +4746,14 @@ "license": "BSD-3-Clause" }, "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } }, "node_modules/base64-js": { "version": "1.5.1", @@ -5930,23 +4813,23 @@ "license": "MIT" }, "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "version": "1.20.6", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.6.tgz", + "integrity": "sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==", "license": "MIT", "dependencies": { - "bytes": "3.1.2", + "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.15.1", + "raw-body": "~2.5.3", "type-is": "~1.6.18", - "unpipe": "1.0.0" + "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8", @@ -5975,18 +4858,22 @@ "license": "ISC" }, "node_modules/bowser": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.13.0.tgz", - "integrity": "sha512-yHAbSRuT6LTeKi6k2aS40csueHqgAsFEgmrOsfRyFpJnFv5O2hl9FYmWEUZ97gZ/dG17U4IQQcTx4YAFYPuWRQ==", + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", + "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", "license": "MIT" }, "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" } }, "node_modules/braces": { @@ -6064,6 +4951,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", "integrity": "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==", + "dev": true, "license": "MIT", "dependencies": { "load-tsconfig": "^0.2.3" @@ -6127,28 +5015,29 @@ "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/cacheable": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.2.0.tgz", - "integrity": "sha512-LEJxRqfeomiiRd2t0uON6hxAtgOoWDfY3fugebbz+J3vDLO+SkdfFChQcOHTZhj9SYa9iwE9MGYNX72dKiOE4w==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.5.0.tgz", + "integrity": "sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g==", "license": "MIT", "dependencies": { - "@cacheable/memory": "^2.0.5", - "@cacheable/utils": "^2.3.0", - "hookified": "^1.13.0", - "keyv": "^5.5.4", - "qified": "^0.5.2" + "@cacheable/memory": "^2.2.0", + "@cacheable/utils": "^2.5.0", + "hookified": "^1.15.0", + "keyv": "^5.6.0", + "qified": "^0.10.1" } }, "node_modules/cachedir": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", - "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.4.0.tgz", + "integrity": "sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==", "dev": true, "license": "MIT", "engines": { @@ -6156,14 +5045,15 @@ } }, "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", + "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", "set-function-length": "^1.2.2" }, "engines": { @@ -6235,9 +5125,9 @@ } }, "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.2.0.tgz", + "integrity": "sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==", "dev": true, "license": "MIT" }, @@ -6305,20 +5195,20 @@ } }, "node_modules/cjs-module-lexer": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", - "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", + "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", "license": "MIT" }, "node_modules/class-validator": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.3.tgz", - "integrity": "sha512-rXXekcjofVN1LTOSw+u4u9WXVEUvNBVjORW154q/IdmYWy1nMbOU9aNtZB0t8m+FJQ9q91jlr2f9CwwUFdFMRA==", + "version": "0.14.4", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.4.tgz", + "integrity": "sha512-AwNusCCam51q703dW82x95tOqQp6oC9HNUl724KxJJOfnKscI8dOloXFgyez7LbTTKWuRBA37FScqVbJEoq8Yw==", "license": "MIT", "dependencies": { "@types/validator": "^13.15.3", "libphonenumber-js": "^1.11.1", - "validator": "^13.15.20" + "validator": "^13.15.22" } }, "node_modules/cli-cursor": { @@ -6348,14 +5238,14 @@ } }, "node_modules/cli-truncate": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.1.1.tgz", - "integrity": "sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.2.0.tgz", + "integrity": "sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==", "dev": true, "license": "MIT", "dependencies": { - "slice-ansi": "^7.1.0", - "string-width": "^8.0.0" + "slice-ansi": "^8.0.0", + "string-width": "^8.2.0" }, "engines": { "node": ">=20" @@ -6365,9 +5255,9 @@ } }, "node_modules/cli-truncate/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz", + "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", "dev": true, "license": "MIT", "engines": { @@ -6378,14 +5268,14 @@ } }, "node_modules/cli-truncate/node_modules/string-width": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz", - "integrity": "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.2.tgz", + "integrity": "sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==", "dev": true, "license": "MIT", "dependencies": { - "get-east-asian-width": "^1.3.0", - "strip-ansi": "^7.1.0" + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" }, "engines": { "node": ">=20" @@ -6395,13 +5285,13 @@ } }, "node_modules/cli-truncate/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "ansi-regex": "^6.2.2" }, "engines": { "node": ">=12" @@ -6431,8 +5321,42 @@ "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" }, - "engines": { - "node": ">=12" + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/clone": { @@ -6460,20 +5384,21 @@ "license": "LGPL-3.0-or-later" }, "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "license": "MIT", "dependencies": { - "color-name": "1.1.3" + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, "node_modules/colorette": { @@ -6496,9 +5421,9 @@ } }, "node_modules/commander": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", - "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", "dev": true, "license": "MIT", "engines": { @@ -6506,13 +5431,13 @@ } }, "node_modules/commitizen": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/commitizen/-/commitizen-4.3.1.tgz", - "integrity": "sha512-gwAPAVTy/j5YcOOebcCRIijn+mSjWJC+IYKivTu6aG8Ei/scoXgfsMRnuAk6b0GRste2J4NGxVdMN3ZpfNaVaw==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/commitizen/-/commitizen-4.3.2.tgz", + "integrity": "sha512-1Zs37z9JPvAcuTSSricZZwBhOPVNNxJouuY4yDEt+eD70EoxT2TU9kViG8CuB/PmVg2G4XsAGQiK4YCst97aDQ==", "dev": true, "license": "MIT", "dependencies": { - "cachedir": "2.3.0", + "cachedir": "2.4.0", "cz-conventional-changelog": "3.3.0", "dedent": "0.7.0", "detect-indent": "6.1.0", @@ -6520,10 +5445,10 @@ "find-root": "1.1.0", "fs-extra": "9.1.0", "glob": "7.2.3", - "inquirer": "8.2.5", + "inquirer": "8.2.7", "is-utf8": "^0.2.1", - "lodash": "4.17.21", - "minimist": "1.2.7", + "lodash": "4.18.1", + "minimist": "1.2.8", "strip-bom": "4.0.0", "strip-json-comments": "3.1.1" }, @@ -6533,17 +5458,7 @@ "git-cz": "bin/git-cz" }, "engines": { - "node": ">= 12" - } - }, - "node_modules/commitizen/node_modules/minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 18" } }, "node_modules/compare-func": { @@ -6625,9 +5540,9 @@ } }, "node_modules/confbox": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", - "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", + "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", "license": "MIT" }, "node_modules/consola": { @@ -6713,24 +5628,24 @@ } }, "node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", "license": "MIT" }, "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", "license": "MIT", "dependencies": { "object-assign": "^4", @@ -6738,12 +5653,16 @@ }, "engines": { "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/cosmiconfig": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", - "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.2.tgz", + "integrity": "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==", "dev": true, "license": "MIT", "peer": true, @@ -6769,13 +5688,13 @@ } }, "node_modules/cosmiconfig-typescript-loader": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.2.0.tgz", - "integrity": "sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.3.0.tgz", + "integrity": "sha512-Akr82WH1Wfqatyiqpj8HDkO2o2KmJRu1FhKfSNJP3K4IdXwHfEyL7MOb62i1AGQVLtIQM+iCE9CGOtrfhR+mmA==", "dev": true, "license": "MIT", "dependencies": { - "jiti": "^2.6.1" + "jiti": "2.6.1" }, "engines": { "node": ">=v18" @@ -6884,6 +5803,23 @@ "node": ">=4" } }, + "node_modules/cz-conventional-changelog/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/cz-conventional-changelog/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, "node_modules/cz-conventional-changelog/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -6962,9 +5898,9 @@ } }, "node_modules/dayjs": { - "version": "1.11.19", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", - "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", + "version": "1.11.21", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz", + "integrity": "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==", "license": "MIT" }, "node_modules/debug": { @@ -7052,6 +5988,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", @@ -7084,9 +6021,9 @@ } }, "node_modules/defu": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", - "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz", + "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==", "license": "MIT" }, "node_modules/delayed-stream": { @@ -7281,9 +6218,9 @@ "license": "MIT" }, "node_modules/effect": { - "version": "3.18.4", - "resolved": "https://registry.npmjs.org/effect/-/effect-3.18.4.tgz", - "integrity": "sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==", + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/effect/-/effect-3.21.0.tgz", + "integrity": "sha512-PPN80qRokCd1f015IANNhrwOnLO7GrrMQfk4/lnZRE/8j7UPWrNNjPV0uBrZutI/nHzernbW+J0hdqQysHiSnQ==", "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.0.0", @@ -7315,76 +6252,39 @@ } }, "node_modules/engine.io": { - "version": "6.6.4", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", - "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", + "version": "6.6.9", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.9.tgz", + "integrity": "sha512-clKkw4C7nJ22mGgoVcCg6V/W/TxdNyIOTr89k2ONZu81qqkddPFDF0LXcbAwhzPD8DjkiRCjzuiO6Y+fkpD4vg==", "license": "MIT", "dependencies": { "@types/cors": "^2.8.12", "@types/node": ">=10.0.0", + "@types/ws": "^8.5.12", "accepts": "~1.3.4", "base64id": "2.0.0", "cookie": "~0.7.2", "cors": "~2.8.5", - "debug": "~4.3.1", + "debug": "~4.4.1", "engine.io-parser": "~5.2.1", - "ws": "~8.17.1" + "ws": "~8.21.0" }, "engines": { "node": ">=10.2.0" } }, "node_modules/engine.io-client": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz", - "integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==", + "version": "6.6.6", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.6.tgz", + "integrity": "sha512-iY6QdftLQ9pyiPoX082bpf/u1UewnOaJrtJIF9T0++QB34lZrj0uP+Q/bj8AlUsAxqhnkTV2BS8SBZSxOmoV5Q==", "license": "MIT", "dependencies": { "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1", + "debug": "~4.4.1", "engine.io-parser": "~5.2.1", - "ws": "~8.17.1", + "ws": "~8.21.0", "xmlhttprequest-ssl": "~2.1.1" } }, - "node_modules/engine.io-client/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/engine.io-client/node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/engine.io-parser": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", @@ -7394,53 +6294,6 @@ "node": ">=10.0.0" } }, - "node_modules/engine.io/node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/engine.io/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/engine.io/node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -7487,9 +6340,9 @@ } }, "node_modules/es-abstract": { - "version": "1.24.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", - "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", + "version": "1.24.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", + "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", "dev": true, "license": "MIT", "dependencies": { @@ -7555,6 +6408,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-abstract-get": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-abstract-get/-/es-abstract-get-1.0.0.tgz", + "integrity": "sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.2", + "is-callable": "^1.2.7", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -7573,10 +6445,16 @@ "node": ">= 0.4" } }, + "node_modules/es-module-lexer": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", + "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", + "license": "MIT" + }, "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -7614,15 +6492,18 @@ } }, "node_modules/es-to-primitive": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", - "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.4.tgz", + "integrity": "sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==", "dev": true, "license": "MIT", "dependencies": { + "es-abstract-get": "^1.0.0", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", "is-callable": "^1.2.7", - "is-date-object": "^1.0.5", - "is-symbol": "^1.0.4" + "is-date-object": "^1.1.0", + "is-symbol": "^1.1.1" }, "engines": { "node": ">= 0.4" @@ -7632,9 +6513,10 @@ } }, "node_modules/esbuild": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.0.tgz", - "integrity": "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "dev": true, "hasInstallScript": true, "license": "MIT", "peer": true, @@ -7645,32 +6527,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.0", - "@esbuild/android-arm": "0.27.0", - "@esbuild/android-arm64": "0.27.0", - "@esbuild/android-x64": "0.27.0", - "@esbuild/darwin-arm64": "0.27.0", - "@esbuild/darwin-x64": "0.27.0", - "@esbuild/freebsd-arm64": "0.27.0", - "@esbuild/freebsd-x64": "0.27.0", - "@esbuild/linux-arm": "0.27.0", - "@esbuild/linux-arm64": "0.27.0", - "@esbuild/linux-ia32": "0.27.0", - "@esbuild/linux-loong64": "0.27.0", - "@esbuild/linux-mips64el": "0.27.0", - "@esbuild/linux-ppc64": "0.27.0", - "@esbuild/linux-riscv64": "0.27.0", - "@esbuild/linux-s390x": "0.27.0", - "@esbuild/linux-x64": "0.27.0", - "@esbuild/netbsd-arm64": "0.27.0", - "@esbuild/netbsd-x64": "0.27.0", - "@esbuild/openbsd-arm64": "0.27.0", - "@esbuild/openbsd-x64": "0.27.0", - "@esbuild/openharmony-arm64": "0.27.0", - "@esbuild/sunos-x64": "0.27.0", - "@esbuild/win32-arm64": "0.27.0", - "@esbuild/win32-ia32": "0.27.0", - "@esbuild/win32-x64": "0.27.0" + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" } }, "node_modules/escalade": { @@ -7777,15 +6659,15 @@ } }, "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.10.tgz", + "integrity": "sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==", "dev": true, "license": "MIT", "dependencies": { "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" + "is-core-module": "^2.16.1", + "resolve": "^2.0.0-next.6" } }, "node_modules/eslint-import-resolver-node/node_modules/debug": { @@ -7799,9 +6681,9 @@ } }, "node_modules/eslint-module-utils": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", - "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.14.0.tgz", + "integrity": "sha512-W2WCRZ9Dqntd+2u8jJcVMV2PKulc6RdLgUUoh/yQr3uB6lo/ZOeGx11sv60/8S4QFFKNslAlWhr9u0Ef7ZW6Ig==", "dev": true, "license": "MIT", "dependencies": { @@ -7860,10 +6742,17 @@ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, + "node_modules/eslint-plugin-import/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, "node_modules/eslint-plugin-import/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -7908,9 +6797,9 @@ } }, "node_modules/eslint-plugin-import/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -7954,14 +6843,14 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz", - "integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==", + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.6.tgz", + "integrity": "sha512-ifetmTcxWfz+4qRW3pH/ujdTq2jQIj59AxJMIN26K5avYgU8dxycUETQonWiW+wPrYXA0j3Try0l1CnwVQtDqQ==", "dev": true, "license": "MIT", "dependencies": { - "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.11.7" + "prettier-linter-helpers": "^1.0.1", + "synckit": "^0.11.13" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -8025,9 +6914,9 @@ } }, "node_modules/eslint/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, "license": "MIT", "dependencies": { @@ -8057,10 +6946,17 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/eslint/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -8085,26 +6981,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/eslint/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/eslint/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/eslint/node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -8166,9 +7042,9 @@ } }, "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -8265,10 +7141,9 @@ } }, "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" @@ -8294,7 +7169,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -8335,9 +7209,9 @@ "license": "MIT" }, "node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", "license": "MIT" }, "node_modules/exif-parser": { @@ -8359,40 +7233,40 @@ } }, "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", "license": "MIT", "peer": true, "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", + "body-parser": "~1.20.5", + "content-disposition": "~0.5.4", "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", - "on-finished": "2.4.1", + "on-finished": "~2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", + "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", - "qs": "6.13.0", + "qs": "~6.15.1", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", + "send": "~0.19.0", + "serve-static": "~1.16.2", "setprototypeof": "1.2.0", - "statuses": "2.0.1", + "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" @@ -8430,26 +7304,11 @@ "license": "MIT" }, "node_modules/exsolve": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", - "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.1.1.tgz", + "integrity": "sha512-9U/jZUgjnSGyntRr6y5Muu1MJcwFl6kPu7k8qLF0IMNfLqvw0NZ4nnVDq0RVoZ0RvCyumib4Ez3KYrVfilrw+g==", "license": "MIT" }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "license": "MIT", - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/fast-check": { "version": "3.23.2", "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", @@ -8486,36 +7345,6 @@ "dev": true, "license": "Apache-2.0" }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -8531,9 +7360,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz", + "integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==", "dev": true, "funding": [ { @@ -8587,23 +7416,41 @@ } }, "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", "dev": true, "license": "ISC", "dependencies": { "reusify": "^1.0.4" } }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/fetch-socks": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fetch-socks/-/fetch-socks-1.3.2.tgz", - "integrity": "sha512-vkH5+Zgj2yEbU57Cei0iyLgTZ4OkEKJj56Xu3ViB5dpsl599JgEooQ3x6NVagIFRHWnWJ+7K0MO0aIV1TMgvnw==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/fetch-socks/-/fetch-socks-1.3.3.tgz", + "integrity": "sha512-jI22jzTf7x3GAcUoZARwuxKBPotb3PFkemVWqxlF2qV55FhRytRJrn95HeLoUMq2vkIbY3aQqNYytvZKqUoMaQ==", "license": "MIT", "dependencies": { - "socks": "^2.8.2", - "undici": ">=6" + "socks": "^2.8.7", + "undici": ">=7" } }, "node_modules/figures": { @@ -8686,17 +7533,17 @@ } }, "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", "license": "MIT", "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", - "on-finished": "2.4.1", + "on-finished": "~2.4.1", "parseurl": "~1.3.3", - "statuses": "2.0.1", + "statuses": "~2.0.2", "unpipe": "~1.0.0" }, "engines": { @@ -8774,6 +7621,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz", "integrity": "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==", + "dev": true, "license": "MIT", "dependencies": { "magic-string": "^0.30.17", @@ -8807,9 +7655,9 @@ } }, "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", "dev": true, "license": "ISC" }, @@ -8840,9 +7688,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", "funding": [ { "type": "individual", @@ -8863,6 +7711,7 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, "license": "MIT", "dependencies": { "is-callable": "^1.2.7" @@ -8875,16 +7724,16 @@ } }, "node_modules/form-data": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" + "hasown": "^2.0.4", + "mime-types": "^2.1.35" }, "engines": { "node": ">= 6" @@ -8918,12 +7767,6 @@ "node": ">= 0.6" } }, - "node_modules/forwarded-parse": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/forwarded-parse/-/forwarded-parse-2.1.2.tgz", - "integrity": "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==", - "license": "MIT" - }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -8960,6 +7803,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -8980,18 +7824,21 @@ } }, "node_modules/function.prototype.name": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", - "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.2.0.tgz", + "integrity": "sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", - "hasown": "^2.0.2", - "is-callable": "^1.2.7" + "has-property-descriptors": "^1.0.2", + "hasown": "^2.0.4", + "is-callable": "^1.2.7", + "is-document.all": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -9014,6 +7861,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -9038,9 +7886,9 @@ } }, "node_modules/get-east-asian-width": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", - "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", + "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", "license": "MIT", "engines": { "node": ">=18" @@ -9104,19 +7952,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-tsconfig": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", - "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, "node_modules/gifwrap": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/gifwrap/-/gifwrap-0.10.1.tgz", @@ -9148,6 +7983,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz", "integrity": "sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==", + "deprecated": "Deprecated and no longer maintained. Use @conventional-changelog/git-client instead.", "dev": true, "license": "MIT", "dependencies": { @@ -9166,7 +8002,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, "license": "ISC", "dependencies": { @@ -9197,10 +8033,17 @@ "node": ">=10.13.0" } }, + "node_modules/glob/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -9209,9 +8052,9 @@ } }, "node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -9375,6 +8218,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, "license": "MIT", "dependencies": { "es-define-property": "^1.0.0" @@ -9427,21 +8271,21 @@ } }, "node_modules/hashery": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/hashery/-/hashery-1.2.0.tgz", - "integrity": "sha512-43XJKpwle72Ik5Zpam7MuzRWyNdwwdf6XHlh8wCj2PggvWf+v/Dm5B0dxGZOmddidgeO6Ofu9As/o231Ti/9PA==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/hashery/-/hashery-1.5.1.tgz", + "integrity": "sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ==", "license": "MIT", "dependencies": { - "hookified": "^1.13.0" + "hookified": "^1.15.0" }, "engines": { "node": ">=20" } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -9464,9 +8308,9 @@ } }, "node_modules/hookified": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.13.0.tgz", - "integrity": "sha512-6sPYUY8olshgM/1LDNW4QZQN0IqgKhtl/1C8koNZBJrKLBk3AZl6chQtNwpNztvfiApHMEwMHek5rv993PRbWw==", + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.15.1.tgz", + "integrity": "sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==", "license": "MIT" }, "node_modules/htmlparser2": { @@ -9489,19 +8333,23 @@ } }, "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/https-proxy-agent": { @@ -9598,9 +8446,9 @@ "license": "BSD-3-Clause" }, "node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz", + "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", "dev": true, "license": "MIT", "engines": { @@ -9650,15 +8498,17 @@ } }, "node_modules/import-in-the-middle": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.15.0.tgz", - "integrity": "sha512-bpQy+CrsRmYmoPMAE/0G33iwRqwW4ouqdRg8jgbH3aKuCtOc8lxgmYXg2dMM92CRiGP660EtBcymH/eVUpCSaA==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-3.3.3.tgz", + "integrity": "sha512-AiohS3H80sXO6owEltjGX+glb7qXaDhBoJb9XcQVH4UI207xu/bDLUcadVKp7Qe576reg9yr/PXZjV5qx8gfbA==", "license": "Apache-2.0", "dependencies": { - "acorn": "^8.14.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^1.2.2", - "module-details-from-path": "^1.0.3" + "cjs-module-lexer": "^2.2.0", + "es-module-lexer": "^2.2.0", + "module-details-from-path": "^1.0.4" + }, + "engines": { + "node": ">=18" } }, "node_modules/import-meta-resolve": { @@ -9711,17 +8561,17 @@ } }, "node_modules/inquirer": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", - "integrity": "sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==", + "version": "8.2.7", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.7.tgz", + "integrity": "sha512-UjOaSel/iddGZJ5xP/Eixh6dY1XghiBw4XK13rCCIJcJfyhhoul/7KhLLUGtebEj6GDYM6Vnx/mVsjx2L/mFIA==", "dev": true, "license": "MIT", "dependencies": { + "@inquirer/external-editor": "^1.0.0", "ansi-escapes": "^4.2.1", "chalk": "^4.1.1", "cli-cursor": "^3.1.0", "cli-width": "^3.0.0", - "external-editor": "^3.0.3", "figures": "^3.0.0", "lodash": "^4.17.21", "mute-stream": "0.0.8", @@ -9731,7 +8581,7 @@ "string-width": "^4.1.0", "strip-ansi": "^6.0.0", "through": "^2.3.6", - "wrap-ansi": "^7.0.0" + "wrap-ansi": "^6.0.1" }, "engines": { "node": ">=12.0.0" @@ -9770,26 +8620,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/inquirer/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/inquirer/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/inquirer/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -9829,39 +8659,23 @@ } }, "node_modules/ip-address": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", - "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.5.0.tgz", + "integrity": "sha512-R5SnVLJmgYYvf2F2ZgwSBnelz5G4q5AxIC277GDfUaNbrZKNANcBC7RHqYYePlszf4kBolVkJauG0ZjHHFh55g==", "license": "MIT", "engines": { "node": ">= 12" } }, "node_modules/ipaddr.js": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", - "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.5.0.tgz", + "integrity": "sha512-aq+t5NAc+cS6rZQQVWC2x98CPqGtKKTMDd4Gaodv0wShnItdKg/51djkGJ1hqH+Oy0ivDftCbSLCQob8zso01w==", "license": "MIT", "engines": { "node": ">= 10" } }, - "node_modules/is-arguments": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", - "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -9954,6 +8768,7 @@ "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -9963,12 +8778,13 @@ } }, "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "dev": true, "license": "MIT", "dependencies": { - "hasown": "^2.0.2" + "hasown": "^2.0.3" }, "engines": { "node": ">= 0.4" @@ -10012,6 +8828,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-document.all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-document.all/-/is-document.all-1.0.0.tgz", + "integrity": "sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -10058,6 +8890,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.4", @@ -10173,6 +9006,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -10268,6 +9102,7 @@ "version": "1.1.15", "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, "license": "MIT", "dependencies": { "which-typed-array": "^1.1.16" @@ -10432,6 +9267,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -10451,10 +9287,20 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -10511,9 +9357,9 @@ } }, "node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", "dev": true, "license": "MIT", "dependencies": { @@ -10560,12 +9406,12 @@ } }, "node_modules/jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", "license": "MIT", "dependencies": { - "jws": "^3.2.2", + "jws": "^4.0.1", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", @@ -10582,9 +9428,9 @@ } }, "node_modules/jwa": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", - "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", "license": "MIT", "dependencies": { "buffer-equal-constant-time": "^1.0.1", @@ -10593,12 +9439,12 @@ } }, "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", "license": "MIT", "dependencies": { - "jwa": "^1.4.1", + "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, @@ -10612,9 +9458,9 @@ } }, "node_modules/keyv": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.5.4.tgz", - "integrity": "sha512-eohl3hKTiVyD1ilYdw9T0OiB4hnjef89e3dMYKz+mVKDzj+5IteTseASUsOB+EU9Tf6VNTCjDePcP6wkDGmLKQ==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", + "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "license": "MIT", "peer": true, "dependencies": { @@ -10636,9 +9482,9 @@ } }, "node_modules/libphonenumber-js": { - "version": "1.12.29", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.29.tgz", - "integrity": "sha512-P2aLrbeqHbmh8+9P35LXQfXOKc7XJ0ymUKl7tyeyQjdRNfzunXWxQXGc4yl3fUf28fqLRfPY+vIVvFXK7KEBTw==", + "version": "1.13.10", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.13.10.tgz", + "integrity": "sha512-xJxrdqvbl2rtn2MaUJrUejz8J7/uZNC0V77oks2LxYrO/+ZtVpRmz+fEQMuu6VusnEB1fmpByiLS1WXecOnAnw==", "license": "MIT" }, "node_modules/libsignal": { @@ -10655,6 +9501,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, "license": "MIT", "engines": { "node": ">=14" @@ -10667,36 +9514,45 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, "license": "MIT" }, "node_modules/link-preview-js": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/link-preview-js/-/link-preview-js-3.2.0.tgz", - "integrity": "sha512-FvrLltjOPGbTzt+RugbzM7g8XuUNLPO2U/INSLczrYdAA32E7nZVUrVL1gr61DGOArGJA2QkPGMEvNMLLsXREA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/link-preview-js/-/link-preview-js-5.0.0.tgz", + "integrity": "sha512-+/R92+k6wwhkdJa7tSXp2WqLaRjyo2eTNbgxNyuZV9fKZ6zLcM9ZSd2ZdzsvrZwAvu4sk3vSfxEmj0xwfOVe7Q==", "license": "MIT", "peer": true, "dependencies": { "cheerio": "1.0.0-rc.11", - "url": "0.11.0" + "undici": "^8.6.0" }, "engines": { - "node": ">=18" + "node": ">=22" + } + }, + "node_modules/link-preview-js/node_modules/undici": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-8.10.0.tgz", + "integrity": "sha512-HvltHd7avK13QIw/oLe4qoOLyoVSoafqJ2jYOrtMRBkbYT31eiBQ8O0ehRKZiEZCMEyLFQNIADpgCWC5fALvYQ==", + "license": "MIT", + "engines": { + "node": ">=22.19.0" } }, "node_modules/lint-staged": { - "version": "16.2.7", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.2.7.tgz", - "integrity": "sha512-lDIj4RnYmK7/kXMya+qJsmkRFkGolciXjrsZ6PC25GdTfWOAWetR0ZbsNXRAj1EHHImRSalc+whZFg56F5DVow==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.4.0.tgz", + "integrity": "sha512-lBWt8hujh/Cjysw5GYVmZpFHXDCgZzhrOm8vbcUdobADZNOK/bRshr2kM3DfgrrtR1DQhfupW9gnIXOfiFi+bw==", "dev": true, "license": "MIT", "dependencies": { - "commander": "^14.0.2", + "commander": "^14.0.3", "listr2": "^9.0.5", - "micromatch": "^4.0.8", - "nano-spawn": "^2.0.0", - "pidtree": "^0.6.0", + "picomatch": "^4.0.3", "string-argv": "^0.3.2", - "yaml": "^2.8.1" + "tinyexec": "^1.0.4", + "yaml": "^2.8.2" }, "bin": { "lint-staged": "bin/lint-staged.js" @@ -10727,9 +9583,9 @@ } }, "node_modules/listr2/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz", + "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", "dev": true, "license": "MIT", "engines": { @@ -10758,13 +9614,13 @@ } }, "node_modules/listr2/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "ansi-regex": "^6.2.2" }, "engines": { "node": ">=12" @@ -10795,6 +9651,7 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "dev": true, "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" @@ -10817,9 +9674,9 @@ } }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "license": "MIT" }, "node_modules/lodash.camelcase": { @@ -10977,26 +9834,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/log-symbols/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/log-symbols/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/log-symbols/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -11041,9 +9878,9 @@ } }, "node_modules/log-update/node_modules/ansi-escapes": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.2.0.tgz", - "integrity": "sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==", + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", + "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", "dev": true, "license": "MIT", "dependencies": { @@ -11057,9 +9894,9 @@ } }, "node_modules/log-update/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz", + "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", "dev": true, "license": "MIT", "engines": { @@ -11131,6 +9968,23 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", + "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, "node_modules/log-update/node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", @@ -11150,13 +10004,13 @@ } }, "node_modules/log-update/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "ansi-regex": "^6.2.2" }, "engines": { "node": ">=12" @@ -11200,10 +10054,10 @@ } }, "node_modules/lru-cache": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", - "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", - "license": "ISC", + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } @@ -11240,9 +10094,9 @@ } }, "node_modules/mediainfo.js": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/mediainfo.js/-/mediainfo.js-0.3.6.tgz", - "integrity": "sha512-3xVRlxwlVWIZV3z1q7pb8LzFOO7iKi/DXoRiFRZdOlrUEhPyJDaaRt0uK32yQJabArQicRBeq7cRxmdZlIBTyA==", + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/mediainfo.js/-/mediainfo.js-0.3.7.tgz", + "integrity": "sha512-mgsmb2GrCTAguVcTohW7KrF+QXNaihrbljj75pLWDQcTTQyqfGoZG4Dy++AZ+FDVCT14LHvVGC9SXxcF5VePag==", "license": "BSD-2-Clause", "dependencies": { "yargs": "^18.0.0" @@ -11255,9 +10109,9 @@ } }, "node_modules/mediainfo.js/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz", + "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", "license": "MIT", "engines": { "node": ">=12" @@ -11280,7 +10134,7 @@ "node": ">=20" } }, - "node_modules/mediainfo.js/node_modules/string-width": { + "node_modules/mediainfo.js/node_modules/cliui/node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", @@ -11297,13 +10151,29 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/mediainfo.js/node_modules/string-width": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.2.tgz", + "integrity": "sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==", + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mediainfo.js/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "ansi-regex": "^6.2.2" }, "engines": { "node": ">=12" @@ -11329,16 +10199,33 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/mediainfo.js/node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mediainfo.js/node_modules/yargs": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", - "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", + "version": "18.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.1.0.tgz", + "integrity": "sha512-2rAgRKu54VsHkqI0/tYkmluGXHD4KW7yZoycuqDQ15QOTnc2VVfy0nN/1eMhnQLO00A+dwtK20xuCnc1YGeUyg==", "license": "MIT", "dependencies": { "cliui": "^9.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", - "string-width": "^7.2.0", + "string-width": "^8.2.1", "y18n": "^5.0.5", "yargs-parser": "^22.0.0" }, @@ -11384,14 +10271,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", + "node_modules/meriyah": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/meriyah/-/meriyah-6.1.4.tgz", + "integrity": "sha512-Sz8FzjzI0kN13GK/6MVEsVzMZEPvOhnmmI1lU5+/1cGOiK3QUahntrNNtdVeihrO7t9JpoH75iMNXg6R6uWflQ==", + "license": "ISC", "engines": { - "node": ">= 8" + "node": ">=18.0.0" } }, "node_modules/methods": { @@ -11417,6 +10303,19 @@ "node": ">=8.6" } }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/mime": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/mime/-/mime-4.1.0.tgz", @@ -11486,15 +10385,16 @@ } }, "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.8" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -11504,15 +10404,16 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/minio": { - "version": "8.0.6", - "resolved": "https://registry.npmjs.org/minio/-/minio-8.0.6.tgz", - "integrity": "sha512-sOeh2/b/XprRmEtYsnNRFtOqNRTPDvYtMWh+spWlfsuCV/+IdxNeKVUMKLqI7b5Dr07ZqCPuaRGU/rB9pZYVdQ==", + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/minio/-/minio-8.0.7.tgz", + "integrity": "sha512-E737MgufW8CeQAsTAtnEMrxZ9scMSf29kkhZoXzDTKj/Jszzo2SfeZUH9wbDQH2Rsq6TCtl/yQL0+XdVKZansQ==", "license": "Apache-2.0", "dependencies": { "async": "^3.2.4", @@ -11520,90 +10421,50 @@ "browser-or-node": "^2.1.1", "buffer-crc32": "^1.0.0", "eventemitter3": "^5.0.1", - "fast-xml-parser": "^4.4.1", + "fast-xml-parser": "^5.3.4", "ipaddr.js": "^2.0.1", "lodash": "^4.17.21", "mime-types": "^2.1.35", "query-string": "^7.1.3", "stream-json": "^1.8.0", "through2": "^4.0.2", - "web-encoding": "^1.1.5", "xml2js": "^0.5.0 || ^0.6.2" }, "engines": { "node": "^16 || ^18 || >=20" } }, - "node_modules/minio/node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "license": "MIT" - }, - "node_modules/minio/node_modules/fast-xml-parser": { - "version": "4.5.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.5.tgz", - "integrity": "sha512-cK9c5I/DwIOI7/Q7AlGN3DuTdwN61gwSfL8rvuVPK+0mcCNHHGxRrpiFtaZZRfRMJL3Gl8B2AFlBG6qXf03w9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "strnum": "^1.0.5" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, - "node_modules/minio/node_modules/strnum": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", - "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" - }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } + "node_modules/minio/node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" }, "node_modules/mlly": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz", - "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==", + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", + "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", + "dev": true, "license": "MIT", "dependencies": { - "acorn": "^8.15.0", + "acorn": "^8.16.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", - "ufo": "^1.6.1" + "ufo": "^1.6.3" } }, "node_modules/mlly/node_modules/confbox": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, "license": "MIT" }, "node_modules/mlly/node_modules/pkg-types": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, "license": "MIT", "dependencies": { "confbox": "^0.1.8", @@ -11637,21 +10498,22 @@ "license": "MIT" }, "node_modules/multer": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz", - "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.2.0.tgz", + "integrity": "sha512-6rdyFg2kLrMh9Jee7/BMPuV9lEAd7lLW2YUpF9/YxR7njyoUwwQ0ZPh3TaIY50Sw6vlyD2HW3wGOkTS4P79xrQ==", "license": "MIT", "dependencies": { "append-field": "^1.0.0", "busboy": "^1.6.0", "concat-stream": "^2.0.0", - "mkdirp": "^0.5.6", - "object-assign": "^4.1.1", - "type-is": "^1.6.18", - "xtend": "^4.0.2" + "type-is": "^1.6.18" }, "engines": { "node": ">= 10.16.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/music-metadata": { @@ -11709,6 +10571,7 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, "license": "MIT", "dependencies": { "any-promise": "^1.0.0", @@ -11716,23 +10579,11 @@ "thenify-all": "^1.0.0" } }, - "node_modules/nano-spawn": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-2.0.0.tgz", - "integrity": "sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=20.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/nano-spawn?sponsor=1" - } - }, "node_modules/nats": { "version": "2.29.3", "resolved": "https://registry.npmjs.org/nats/-/nats-2.29.3.tgz", "integrity": "sha512-tOQCRCwC74DgBTk4pWZ9V45sk4d7peoE2njVprMRCBXrhJ5q5cYM7i6W+Uvw2qUrcfOSnuisrX7bEx3b3Wx4QA==", + "deprecated": "Package moved. Use @nats-io/transport-node from https://github.com/nats-io/nats.js", "license": "Apache-2.0", "dependencies": { "nkeys.js": "1.1.0" @@ -11782,24 +10633,12 @@ } }, "node_modules/node-cron": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.3.tgz", - "integrity": "sha512-dOal67//nohNgYWb+nWmg5dkFdIwDm8EpeGYMekPMrngV3637lqnX0lbUcCtgibHTz6SEz7DAIjKvKDFYCnO1A==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-4.6.0.tgz", + "integrity": "sha512-Si/bzYiKRHOB8/a99T2+SDGN582ONDMSTlJr5oCkT6GtnqPjZ2s10eoQRYkW9ZHwjVxONL+W8Fb+qR0AHMQsdg==", "license": "ISC", - "dependencies": { - "uuid": "8.3.2" - }, "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/node-cron/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" + "node": ">=20" } }, "node_modules/node-domexception": { @@ -11822,6 +10661,35 @@ "node": ">=10.5.0" } }, + "node_modules/node-exports-info": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.2.tgz", + "integrity": "sha512-kXs9Go0cah0qHVV2v389IXQLdLCeE1xfFtjOAF+iobu0OIoG1pje8At2vMHyaPMiPMnG/LWP50twML21eMcAag==", + "dev": true, + "license": "MIT", + "dependencies": { + "array.prototype.flatmap": "^1.3.3", + "es-errors": "^1.3.0", + "object.entries": "^1.1.9", + "semver": "^6.3.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/node-exports-info/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -11870,24 +10738,28 @@ } }, "node_modules/nypm": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.2.tgz", - "integrity": "sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g==", + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.9.tgz", + "integrity": "sha512-zxlE2yvSWZWmHcNdT3+5zV2lrCogeE9YOklHrR3dFjqutq5wO7GFDYLFDRXLsYnJzwvy/im9fYoxePvS0VTW0w==", "license": "MIT", "dependencies": { - "citty": "^0.1.6", - "consola": "^3.4.2", + "citty": "^0.2.2", "pathe": "^2.0.3", - "pkg-types": "^2.3.0", - "tinyexec": "^1.0.1" + "tinyexec": "^1.2.4" }, "bin": { "nypm": "dist/cli.mjs" }, "engines": { - "node": "^14.16.0 || >=16.10.0" + "node": ">=18" } }, + "node_modules/nypm/node_modules/citty": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.2.2.tgz", + "integrity": "sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w==", + "license": "MIT" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -11940,6 +10812,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/object.fromentries": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", @@ -12210,26 +11098,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/ora/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/ora/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/ora/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -12253,24 +11121,15 @@ "node": ">=8" } }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/own-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", - "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.2.tgz", + "integrity": "sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg==", "dev": true, "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.6", + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" }, @@ -12314,12 +11173,12 @@ } }, "node_modules/p-queue": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.0.1.tgz", - "integrity": "sha512-RhBdVhSwJb7Ocn3e8ULk4NMwBEuOxe+1zcgphUy9c2e5aR/xbEsdVXxHJ3lynw6Qiqu7OINEyHlZkiblEpaq7w==", + "version": "9.3.3", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.3.3.tgz", + "integrity": "sha512-NXAOdnEe5FsZJfT4oK84lE1Y5cFFdWlRuOo5tww8DyNMxyRXwn39fIkUtNLKppcPC+UYU/bXujNCUGDv01y7CA==", "license": "MIT", "dependencies": { - "eventemitter3": "^5.0.1", + "eventemitter3": "^5.0.4", "p-timeout": "^7.0.0" }, "engines": { @@ -12528,12 +11387,13 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, "license": "MIT" }, "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", "license": "MIT" }, "node_modules/pathe": { @@ -12549,15 +11409,15 @@ "license": "MIT" }, "node_modules/pg": { - "version": "8.16.3", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", - "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", + "version": "8.23.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.23.0.tgz", + "integrity": "sha512-Ip2EQCngowJLGOfCwkFhPXU7/ljlhn6Rxlmy4XYfL2Y+vyRM59+8uR2xqRWKdYmbXmxCFOAmKxBuSUCdF34qLg==", "license": "MIT", "peer": true, "dependencies": { - "pg-connection-string": "^2.9.1", - "pg-pool": "^3.10.1", - "pg-protocol": "^1.10.3", + "pg-connection-string": "^2.14.0", + "pg-pool": "^3.14.0", + "pg-protocol": "^1.16.0", "pg-types": "2.2.0", "pgpass": "1.0.5" }, @@ -12565,7 +11425,7 @@ "node": ">= 16.0.0" }, "optionalDependencies": { - "pg-cloudflare": "^1.2.7" + "pg-cloudflare": "^1.4.0" }, "peerDependencies": { "pg-native": ">=3.0.1" @@ -12577,16 +11437,16 @@ } }, "node_modules/pg-cloudflare": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz", - "integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.4.0.tgz", + "integrity": "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==", "license": "MIT", "optional": true }, "node_modules/pg-connection-string": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz", - "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==", + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.14.0.tgz", + "integrity": "sha512-XwWDGcLRGCXAR8F/AM5bG7Q+A3Wm2s6QeEjlOKZLlH3UYcguiqCWKyWXVag5TLTIjR7oOJUY8kcADaZgWPyLeg==", "license": "MIT" }, "node_modules/pg-int8": { @@ -12599,18 +11459,18 @@ } }, "node_modules/pg-pool": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz", - "integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==", + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.14.0.tgz", + "integrity": "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==", "license": "MIT", "peerDependencies": { "pg": ">=8.0" } }, "node_modules/pg-protocol": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz", - "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.16.0.tgz", + "integrity": "sha512-sILXutLVjCLjcDuOmvhX5e2Z4cS5qG/6Bu3VkpFwdf/633ElGLpEh9bgmuI5I4sqKqkifQiGyiCcx1HdtrK7tg==", "license": "MIT" }, "node_modules/pg-types": { @@ -12642,34 +11502,22 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", "dev": true, "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pidtree": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", - "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", - "dev": true, - "license": "MIT", - "bin": { - "pidtree": "bin/pidtree.js" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/pino": { "version": "9.14.0", "resolved": "https://registry.npmjs.org/pino/-/pino-9.14.0.tgz", @@ -12702,15 +11550,16 @@ } }, "node_modules/pino-std-serializers": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz", - "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz", + "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==", "license": "MIT" }, "node_modules/pirates": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, "license": "MIT", "engines": { "node": ">= 6" @@ -12738,13 +11587,13 @@ } }, "node_modules/pkg-types": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", - "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.1.tgz", + "integrity": "sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==", "license": "MIT", "dependencies": { - "confbox": "^0.2.2", - "exsolve": "^1.0.7", + "confbox": "^0.2.4", + "exsolve": "^1.0.8", "pathe": "^2.0.3" } }, @@ -12761,6 +11610,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -12770,6 +11620,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, "funding": [ { "type": "opencollective", @@ -12818,9 +11669,9 @@ } }, "node_modules/postgres-bytea": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", - "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", + "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -12858,9 +11709,9 @@ } }, "node_modules/prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", + "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==", "dev": true, "license": "MIT", "peer": true, @@ -12875,9 +11726,9 @@ } }, "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz", + "integrity": "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==", "dev": true, "license": "MIT", "dependencies": { @@ -12888,15 +11739,15 @@ } }, "node_modules/prisma": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.19.0.tgz", - "integrity": "sha512-F3eX7K+tWpkbhl3l4+VkFtrwJlLXbAM+f9jolgoUZbFcm1DgHZ4cq9AgVEgUym2au5Ad/TDLN8lg83D+M10ycw==", + "version": "6.19.3", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.19.3.tgz", + "integrity": "sha512-++ZJ0ijLrDJF6hNB4t4uxg2br3fC4H9Yc9tcbjr2fcNFP3rh/SBNrAgjhsqBU4Ght8JPrVofG/ZkXfnSfnYsFg==", "hasInstallScript": true, "license": "Apache-2.0", "peer": true, "dependencies": { - "@prisma/config": "6.19.0", - "@prisma/engines": "6.19.0" + "@prisma/config": "6.19.3", + "@prisma/engines": "6.19.3" }, "bin": { "prisma": "build/index.js" @@ -12914,9 +11765,9 @@ } }, "node_modules/process-warning": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", - "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.1.0.tgz", + "integrity": "sha512-jQSaVHsPgtyw60e1rQ/A+/ArPEj/S8pS/vFnyGa/gYFXrKk/6RuDkoqVDQ5NI5MmS01698ltlAk0NoDBNLujRw==", "funding": [ { "type": "github", @@ -12975,16 +11826,23 @@ } }, "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } }, "node_modules/punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==", - "license": "MIT" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } }, "node_modules/pure-rand": { "version": "6.1.0", @@ -13003,34 +11861,40 @@ "license": "MIT" }, "node_modules/pusher": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/pusher/-/pusher-5.2.0.tgz", - "integrity": "sha512-F6LNiZyJsIkoHLz+YurjKZ1HH8V1/cMggn4k97kihjP3uTvm0P4mZzSFeHOWIy+PlJ2VInJBhUFJBYLsFR5cjg==", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/pusher/-/pusher-5.3.4.tgz", + "integrity": "sha512-gA7NpPRsQW9qpry/Ov+o9KKIPaRMziiDoEtiZ6EF0+QiSdsnT49+dH7+hf0Dvd4d+HEGQDHp6VXtrVM5DDrpmw==", "license": "MIT", "dependencies": { "@types/node-fetch": "^2.5.7", "abort-controller": "^3.0.0", "is-base64": "^1.1.0", - "node-fetch": "^2.6.1", + "node-fetch": "^2.7.0", "tweetnacl": "^1.0.0", "tweetnacl-util": "^0.15.0" }, "engines": { - "node": ">= 4.0.0" + "node": ">= 10.0.0" } }, "node_modules/qified": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/qified/-/qified-0.5.2.tgz", - "integrity": "sha512-7gJ6mxcQb9vUBOtbKm5mDevbe2uRcOEVp1g4gb/Q+oLntB3HY8eBhOYRxFI2mlDFlY1e4DOSCptzxarXRvzxCA==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/qified/-/qified-0.10.1.tgz", + "integrity": "sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==", "license": "MIT", "dependencies": { - "hookified": "^1.13.0" + "hookified": "^2.1.1" }, "engines": { "node": ">=20" } }, + "node_modules/qified/node_modules/hookified": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-2.2.0.tgz", + "integrity": "sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==", + "license": "MIT" + }, "node_modules/qoa-format": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/qoa-format/-/qoa-format-1.0.1.tgz", @@ -13065,21 +11929,6 @@ "qrcode-terminal": "bin/qrcode-terminal.js" } }, - "node_modules/qrcode/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/qrcode/node_modules/cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", @@ -13091,24 +11940,6 @@ "wrap-ansi": "^6.2.0" } }, - "node_modules/qrcode/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/qrcode/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, "node_modules/qrcode/node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -13179,20 +12010,6 @@ "node": ">=10.13.0" } }, - "node_modules/qrcode/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/qrcode/node_modules/y18n": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", @@ -13235,12 +12052,13 @@ } }, "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.6" + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" }, "engines": { "node": ">=0.6" @@ -13267,15 +12085,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", - "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", - "engines": { - "node": ">=0.4.x" - } - }, "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", @@ -13319,15 +12128,15 @@ } }, "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8" @@ -13460,17 +12269,16 @@ } }, "node_modules/require-in-the-middle": { - "version": "7.5.2", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.5.2.tgz", - "integrity": "sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", + "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", "license": "MIT", "dependencies": { "debug": "^4.3.5", - "module-details-from-path": "^1.0.3", - "resolve": "^1.22.8" + "module-details-from-path": "^1.0.3" }, "engines": { - "node": ">=8.6.0" + "node": ">=9.3.0 || >=8.10.0 <9.0.0" } }, "node_modules/require-main-filename": { @@ -13486,12 +12294,16 @@ "license": "MIT" }, "node_modules/resolve": { - "version": "1.22.11", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", - "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "version": "2.0.0-next.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.7.tgz", + "integrity": "sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==", + "dev": true, "license": "MIT", "dependencies": { - "is-core-module": "^2.16.1", + "es-errors": "^1.3.0", + "is-core-module": "^2.16.2", + "node-exports-info": "^1.6.0", + "object-keys": "^1.1.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -13523,21 +12335,12 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "devOptional": true, - "license": "MIT", - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, "node_modules/restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -13588,12 +12391,13 @@ } }, "node_modules/rollup": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", - "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.4.tgz", + "integrity": "sha512-RXOqwaPsBGjMNMa4sQjDjHieHEZDFoj/Rdr46l2MU5DfEs16wHJPC2RPTPHWhNl+M3aI472LLqFkFKut4SblOg==", + "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.8" + "@types/estree": "1.0.9" }, "bin": { "rollup": "dist/bin/rollup" @@ -13603,28 +12407,32 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.53.3", - "@rollup/rollup-android-arm64": "4.53.3", - "@rollup/rollup-darwin-arm64": "4.53.3", - "@rollup/rollup-darwin-x64": "4.53.3", - "@rollup/rollup-freebsd-arm64": "4.53.3", - "@rollup/rollup-freebsd-x64": "4.53.3", - "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", - "@rollup/rollup-linux-arm-musleabihf": "4.53.3", - "@rollup/rollup-linux-arm64-gnu": "4.53.3", - "@rollup/rollup-linux-arm64-musl": "4.53.3", - "@rollup/rollup-linux-loong64-gnu": "4.53.3", - "@rollup/rollup-linux-ppc64-gnu": "4.53.3", - "@rollup/rollup-linux-riscv64-gnu": "4.53.3", - "@rollup/rollup-linux-riscv64-musl": "4.53.3", - "@rollup/rollup-linux-s390x-gnu": "4.53.3", - "@rollup/rollup-linux-x64-gnu": "4.53.3", - "@rollup/rollup-linux-x64-musl": "4.53.3", - "@rollup/rollup-openharmony-arm64": "4.53.3", - "@rollup/rollup-win32-arm64-msvc": "4.53.3", - "@rollup/rollup-win32-ia32-msvc": "4.53.3", - "@rollup/rollup-win32-x64-gnu": "4.53.3", - "@rollup/rollup-win32-x64-msvc": "4.53.3", + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.62.4", + "@rollup/rollup-android-arm64": "4.62.4", + "@rollup/rollup-darwin-arm64": "4.62.4", + "@rollup/rollup-darwin-x64": "4.62.4", + "@rollup/rollup-freebsd-arm64": "4.62.4", + "@rollup/rollup-freebsd-x64": "4.62.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.4", + "@rollup/rollup-linux-arm-musleabihf": "4.62.4", + "@rollup/rollup-linux-arm64-gnu": "4.62.4", + "@rollup/rollup-linux-arm64-musl": "4.62.4", + "@rollup/rollup-linux-loong64-gnu": "4.62.4", + "@rollup/rollup-linux-loong64-musl": "4.62.4", + "@rollup/rollup-linux-ppc64-gnu": "4.62.4", + "@rollup/rollup-linux-ppc64-musl": "4.62.4", + "@rollup/rollup-linux-riscv64-gnu": "4.62.4", + "@rollup/rollup-linux-riscv64-musl": "4.62.4", + "@rollup/rollup-linux-s390x-gnu": "4.62.4", + "@rollup/rollup-linux-x64-gnu": "4.62.4", + "@rollup/rollup-linux-x64-musl": "4.62.4", + "@rollup/rollup-openbsd-x64": "4.62.4", + "@rollup/rollup-openharmony-arm64": "4.62.4", + "@rollup/rollup-win32-arm64-msvc": "4.62.4", + "@rollup/rollup-win32-ia32-msvc": "4.62.4", + "@rollup/rollup-win32-x64-gnu": "4.62.4", + "@rollup/rollup-win32-x64-msvc": "4.62.4", "fsevents": "~2.3.2" } }, @@ -13672,15 +12480,15 @@ } }, "node_modules/safe-array-concat": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", - "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", + "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", "has-symbols": "^1.1.0", "isarray": "^2.0.5" }, @@ -13732,6 +12540,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -13761,15 +12570,24 @@ "license": "MIT" }, "node_modules/sax": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.3.tgz", - "integrity": "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==", - "license": "BlueOak-1.0.0" + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz", + "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/semifies": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semifies/-/semifies-1.0.0.tgz", + "integrity": "sha512-xXR3KGeoxTNWPD4aBvL5NUpMTT7WMANr3EWnaS190QVkY52lqqcVRD7Q05UVbBhiWDGWMlJEUam9m7uFFGVScw==", + "license": "Apache-2.0" }, "node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -13779,24 +12597,24 @@ } }, "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", "license": "MIT", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", "mime": "1.6.0", "ms": "2.1.3", - "on-finished": "2.4.1", + "on-finished": "~2.4.1", "range-parser": "~1.2.1", - "statuses": "2.0.1" + "statuses": "~2.0.2" }, "engines": { "node": ">= 0.8.0" @@ -13811,20 +12629,11 @@ "ms": "2.0.0" } }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/send/node_modules/mime": { "version": "1.6.0", @@ -13839,15 +12648,15 @@ } }, "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", "license": "MIT", "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.19.0" + "send": "~0.19.1" }, "engines": { "node": ">= 0.8.0" @@ -13863,6 +12672,7 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", @@ -13914,48 +12724,53 @@ "license": "ISC" }, "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", - "hasInstallScript": true, + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.3.tgz", + "integrity": "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==", "license": "Apache-2.0", "peer": true, "dependencies": { - "@img/colour": "^1.0.0", + "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", - "semver": "^7.7.3" + "semver": "^7.8.5" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" + "@img/sharp-darwin-arm64": "0.35.3", + "@img/sharp-darwin-x64": "0.35.3", + "@img/sharp-freebsd-wasm32": "0.35.3", + "@img/sharp-libvips-darwin-arm64": "1.3.2", + "@img/sharp-libvips-darwin-x64": "1.3.2", + "@img/sharp-libvips-linux-arm": "1.3.2", + "@img/sharp-libvips-linux-arm64": "1.3.2", + "@img/sharp-libvips-linux-ppc64": "1.3.2", + "@img/sharp-libvips-linux-riscv64": "1.3.2", + "@img/sharp-libvips-linux-s390x": "1.3.2", + "@img/sharp-libvips-linux-x64": "1.3.2", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2", + "@img/sharp-libvips-linuxmusl-x64": "1.3.2", + "@img/sharp-linux-arm": "0.35.3", + "@img/sharp-linux-arm64": "0.35.3", + "@img/sharp-linux-ppc64": "0.35.3", + "@img/sharp-linux-riscv64": "0.35.3", + "@img/sharp-linux-s390x": "0.35.3", + "@img/sharp-linux-x64": "0.35.3", + "@img/sharp-linuxmusl-arm64": "0.35.3", + "@img/sharp-linuxmusl-x64": "0.35.3", + "@img/sharp-webcontainers-wasm32": "0.35.3", + "@img/sharp-win32-arm64": "0.35.3", + "@img/sharp-win32-ia32": "0.35.3", + "@img/sharp-win32-x64": "0.35.3" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, "node_modules/shebang-command": { @@ -13981,21 +12796,15 @@ "node": ">=8" } }, - "node_modules/shimmer": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", - "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==", - "license": "BSD-2-Clause" - }, "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" }, @@ -14007,13 +12816,13 @@ } }, "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" + "object-inspect": "^1.13.4" }, "engines": { "node": ">= 0.4" @@ -14086,17 +12895,17 @@ } }, "node_modules/slice-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", - "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-8.0.0.tgz", + "integrity": "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^6.2.1", - "is-fullwidth-code-point": "^5.0.0" + "ansi-styles": "^6.2.3", + "is-fullwidth-code-point": "^5.1.0" }, "engines": { - "node": ">=18" + "node": ">=20" }, "funding": { "url": "https://github.com/chalk/slice-ansi?sponsor=1" @@ -14113,15 +12922,15 @@ } }, "node_modules/socket.io": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", - "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.3.tgz", + "integrity": "sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A==", "license": "MIT", "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", "cors": "~2.8.5", - "debug": "~4.3.2", + "debug": "~4.4.1", "engine.io": "~6.6.0", "socket.io-adapter": "~2.5.2", "socket.io-parser": "~4.2.4" @@ -14131,61 +12940,23 @@ } }, "node_modules/socket.io-adapter": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", - "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", - "license": "MIT", - "dependencies": { - "debug": "~4.3.4", - "ws": "~8.17.1" - } - }, - "node_modules/socket.io-adapter/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "2.5.8", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.8.tgz", + "integrity": "sha512-6Oy52pbg+kvdCVvjcN+FnY7BvxZ7cIHNScbvztT/It5d0vbwoJoVZmF2gjJmnV0/4WlXRfG15zc45ySk9Ah8bw==", "license": "MIT", "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socket.io-adapter/node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } + "debug": "~4.4.1", + "ws": "~8.21.0" } }, "node_modules/socket.io-client": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", - "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==", + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.3.tgz", + "integrity": "sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==", "license": "MIT", "dependencies": { "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.2", + "debug": "~4.4.1", "engine.io-client": "~6.6.1", "socket.io-parser": "~4.2.4" }, @@ -14193,77 +12964,26 @@ "node": ">=10.0.0" } }, - "node_modules/socket.io-client/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/socket.io-parser": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", - "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.7.tgz", + "integrity": "sha512-IH/iSeO9T6gz1KkFleGDWkG9N3dl4jXVYUtMhIqH10Md0ttMer8nUNWiP1DKuNrybD2xBrixLJdCC9J6ECoYkg==", "license": "MIT", "dependencies": { "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" + "debug": "~4.4.1" }, "engines": { "node": ">=10.0.0" } }, - "node_modules/socket.io-parser/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socket.io/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/socks": { - "version": "2.8.7", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", - "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.9.tgz", + "integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==", "license": "MIT", "dependencies": { - "ip-address": "^10.0.1", + "ip-address": "^10.1.1", "smart-buffer": "^4.2.0" }, "engines": { @@ -14286,21 +13006,21 @@ } }, "node_modules/sonic-boom": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", - "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz", + "integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==", "license": "MIT", "dependencies": { "atomic-sleep": "^1.0.0" } }, "node_modules/source-map": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", - "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "license": "BSD-3-Clause", "engines": { - "node": ">= 12" + "node": ">=0.10.0" } }, "node_modules/split-on-first": { @@ -14322,9 +13042,9 @@ } }, "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -14425,19 +13145,20 @@ } }, "node_modules/string.prototype.trim": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", - "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.11.tgz", + "integrity": "sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", "define-data-property": "^1.1.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-object-atoms": "^1.0.0", - "has-property-descriptors": "^1.0.2" + "es-abstract": "^1.24.2", + "es-object-atoms": "^1.1.2", + "has-property-descriptors": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -14447,16 +13168,16 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", - "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.10.tgz", + "integrity": "sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "es-object-atoms": "^1.1.2" }, "engines": { "node": ">= 0.4" @@ -14553,6 +13274,7 @@ "version": "3.35.1", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", @@ -14575,6 +13297,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, "license": "MIT", "engines": { "node": ">= 6" @@ -14597,6 +13320,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -14606,9 +13330,9 @@ } }, "node_modules/swagger-ui-dist": { - "version": "5.30.2", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.30.2.tgz", - "integrity": "sha512-HWCg1DTNE/Nmapt+0m2EPXFwNKNeKK4PwMjkwveN/zn1cV2Kxi9SURd+m0SpdcSgWEK/O64sf8bzXdtUhigtHA==", + "version": "5.32.13", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.32.13.tgz", + "integrity": "sha512-qQobzb3DeC2LeK0j3E8812Ef4aIq1y9flJxvZkimkqUC/w4u7wS+yCc+VakqGJLweUUBrI24effhwo8OsAvNAw==", "license": "Apache-2.0", "dependencies": { "@scarf/scarf": "=1.4.0" @@ -14630,13 +13354,13 @@ } }, "node_modules/synckit": { - "version": "0.11.11", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", - "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.13.tgz", + "integrity": "sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==", "dev": true, "license": "MIT", "dependencies": { - "@pkgr/core": "^0.2.9" + "@pkgr/core": "^0.3.6" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -14669,6 +13393,7 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, "license": "MIT", "dependencies": { "any-promise": "^1.0.0" @@ -14678,6 +13403,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, "license": "MIT", "dependencies": { "thenify": ">= 3.1.0 < 4" @@ -14687,9 +13413,9 @@ } }, "node_modules/thread-stream": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", - "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.2.0.tgz", + "integrity": "sha512-zLBvqpwr4Esa0kRjcrzGU6zL25lePWaCLMx0RQFrmteozIfeNdaMLpG5U7PeHzvlFkAWaRKA9/KVW4F60iB+qw==", "license": "MIT", "dependencies": { "real-require": "^0.2.0" @@ -14718,22 +13444,23 @@ "license": "MIT" }, "node_modules/tinyexec": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", - "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.0.tgz", + "integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==", "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", - "picomatch": "^4.0.3" + "picomatch": "^4.0.4" }, "engines": { "node": ">=12.0.0" @@ -14742,49 +13469,6 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -14835,15 +13519,16 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, "license": "MIT", "bin": { "tree-kill": "cli.js" } }, "node_modules/ts-api-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", "dev": true, "license": "MIT", "engines": { @@ -14857,6 +13542,7 @@ "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, "license": "Apache-2.0" }, "node_modules/tsconfig-paths": { @@ -14894,6 +13580,7 @@ "version": "8.5.1", "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.5.1.tgz", "integrity": "sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==", + "dev": true, "license": "MIT", "dependencies": { "bundle-require": "^5.1.0", @@ -14942,22 +13629,32 @@ } } }, + "node_modules/tsup/node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, "node_modules/tsup/node_modules/tinyexec": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, "license": "MIT" }, "node_modules/tsx": { - "version": "4.20.6", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.6.tgz", - "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", - "devOptional": true, + "version": "4.23.12", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.12.tgz", + "integrity": "sha512-FDf4L4sYzKtzWYhU/Xm0AQFdTjdIxNo9ElTf2mxXM6k8YMHXzYUe4yODVaXP4V9uMFbVg8c0qyBccK2OOxb45Q==", + "dev": true, "license": "MIT", "peer": true, "dependencies": { - "esbuild": "~0.25.0", - "get-tsconfig": "^4.7.5" + "esbuild": "~0.28.0" }, "bin": { "tsx": "dist/cli.mjs" @@ -14970,9 +13667,9 @@ } }, "node_modules/tsx/node_modules/@esbuild/aix-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", - "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", "cpu": [ "ppc64" ], @@ -14987,9 +13684,9 @@ } }, "node_modules/tsx/node_modules/@esbuild/android-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", - "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", "cpu": [ "arm" ], @@ -15004,9 +13701,9 @@ } }, "node_modules/tsx/node_modules/@esbuild/android-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", - "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", "cpu": [ "arm64" ], @@ -15021,9 +13718,9 @@ } }, "node_modules/tsx/node_modules/@esbuild/android-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", - "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", "cpu": [ "x64" ], @@ -15038,9 +13735,9 @@ } }, "node_modules/tsx/node_modules/@esbuild/darwin-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", - "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", "cpu": [ "arm64" ], @@ -15055,9 +13752,9 @@ } }, "node_modules/tsx/node_modules/@esbuild/darwin-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", - "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", "cpu": [ "x64" ], @@ -15072,9 +13769,9 @@ } }, "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", - "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", "cpu": [ "arm64" ], @@ -15089,9 +13786,9 @@ } }, "node_modules/tsx/node_modules/@esbuild/freebsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", - "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", "cpu": [ "x64" ], @@ -15106,9 +13803,9 @@ } }, "node_modules/tsx/node_modules/@esbuild/linux-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", - "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", "cpu": [ "arm" ], @@ -15123,9 +13820,9 @@ } }, "node_modules/tsx/node_modules/@esbuild/linux-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", - "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", "cpu": [ "arm64" ], @@ -15140,9 +13837,9 @@ } }, "node_modules/tsx/node_modules/@esbuild/linux-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", - "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", "cpu": [ "ia32" ], @@ -15157,9 +13854,9 @@ } }, "node_modules/tsx/node_modules/@esbuild/linux-loong64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", - "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", "cpu": [ "loong64" ], @@ -15174,9 +13871,9 @@ } }, "node_modules/tsx/node_modules/@esbuild/linux-mips64el": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", - "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", "cpu": [ "mips64el" ], @@ -15191,9 +13888,9 @@ } }, "node_modules/tsx/node_modules/@esbuild/linux-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", - "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", "cpu": [ "ppc64" ], @@ -15208,9 +13905,9 @@ } }, "node_modules/tsx/node_modules/@esbuild/linux-riscv64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", - "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", "cpu": [ "riscv64" ], @@ -15225,9 +13922,9 @@ } }, "node_modules/tsx/node_modules/@esbuild/linux-s390x": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", - "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", "cpu": [ "s390x" ], @@ -15242,9 +13939,9 @@ } }, "node_modules/tsx/node_modules/@esbuild/linux-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", - "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", "cpu": [ "x64" ], @@ -15259,9 +13956,9 @@ } }, "node_modules/tsx/node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", - "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", "cpu": [ "arm64" ], @@ -15276,9 +13973,9 @@ } }, "node_modules/tsx/node_modules/@esbuild/netbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", - "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", "cpu": [ "x64" ], @@ -15293,9 +13990,9 @@ } }, "node_modules/tsx/node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", - "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", "cpu": [ "arm64" ], @@ -15310,9 +14007,9 @@ } }, "node_modules/tsx/node_modules/@esbuild/openbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", - "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", "cpu": [ "x64" ], @@ -15327,9 +14024,9 @@ } }, "node_modules/tsx/node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", - "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", "cpu": [ "arm64" ], @@ -15344,9 +14041,9 @@ } }, "node_modules/tsx/node_modules/@esbuild/sunos-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", - "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", "cpu": [ "x64" ], @@ -15361,9 +14058,9 @@ } }, "node_modules/tsx/node_modules/@esbuild/win32-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", - "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", "cpu": [ "arm64" ], @@ -15378,9 +14075,9 @@ } }, "node_modules/tsx/node_modules/@esbuild/win32-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", - "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", "cpu": [ "ia32" ], @@ -15395,9 +14092,9 @@ } }, "node_modules/tsx/node_modules/@esbuild/win32-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", - "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", "cpu": [ "x64" ], @@ -15412,10 +14109,10 @@ } }, "node_modules/tsx/node_modules/esbuild": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", - "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", - "devOptional": true, + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -15425,32 +14122,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.12", - "@esbuild/android-arm": "0.25.12", - "@esbuild/android-arm64": "0.25.12", - "@esbuild/android-x64": "0.25.12", - "@esbuild/darwin-arm64": "0.25.12", - "@esbuild/darwin-x64": "0.25.12", - "@esbuild/freebsd-arm64": "0.25.12", - "@esbuild/freebsd-x64": "0.25.12", - "@esbuild/linux-arm": "0.25.12", - "@esbuild/linux-arm64": "0.25.12", - "@esbuild/linux-ia32": "0.25.12", - "@esbuild/linux-loong64": "0.25.12", - "@esbuild/linux-mips64el": "0.25.12", - "@esbuild/linux-ppc64": "0.25.12", - "@esbuild/linux-riscv64": "0.25.12", - "@esbuild/linux-s390x": "0.25.12", - "@esbuild/linux-x64": "0.25.12", - "@esbuild/netbsd-arm64": "0.25.12", - "@esbuild/netbsd-x64": "0.25.12", - "@esbuild/openbsd-arm64": "0.25.12", - "@esbuild/openbsd-x64": "0.25.12", - "@esbuild/openharmony-arm64": "0.25.12", - "@esbuild/sunos-x64": "0.25.12", - "@esbuild/win32-arm64": "0.25.12", - "@esbuild/win32-ia32": "0.25.12", - "@esbuild/win32-x64": "0.25.12" + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" } }, "node_modules/tweetnacl": { @@ -15571,18 +14268,18 @@ } }, "node_modules/typed-array-length": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", - "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.8.tgz", + "integrity": "sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0", - "reflect.getprototypeof": "^1.0.6" + "call-bind": "^1.0.9", + "for-each": "^0.3.5", + "gopd": "^1.2.0", + "is-typed-array": "^1.1.15", + "possible-typed-array-names": "^1.1.0", + "reflect.getprototypeof": "^1.0.10" }, "engines": { "node": ">= 0.4" @@ -15613,9 +14310,10 @@ } }, "node_modules/ufo": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", - "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", + "dev": true, "license": "MIT" }, "node_modules/uint8array-extras": { @@ -15650,18 +14348,18 @@ } }, "node_modules/undici": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.16.0.tgz", - "integrity": "sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz", + "integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==", "license": "MIT", "engines": { "node": ">=20.18.1" } }, "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", "license": "MIT" }, "node_modules/unicorn-magic": { @@ -15706,26 +14404,6 @@ "punycode": "^2.1.0" } }, - "node_modules/uri-js/node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ==", - "license": "MIT", - "dependencies": { - "punycode": "1.3.2", - "querystring": "0.2.0" - } - }, "node_modules/url-parse": { "version": "1.5.10", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", @@ -15745,19 +14423,6 @@ "pako": "^1.0.11" } }, - "node_modules/util": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", - "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "which-typed-array": "^1.1.2" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -15774,9 +14439,9 @@ } }, "node_modules/uuid": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz", - "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==", + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.2.tgz", + "integrity": "sha512-vzi9uRZ926x4XV73S/4qQaTwPXM2JBj6/6lI/byHH1jOpCzb0zDbfytgA9LcN/hzb2l7WQSQnxITOVx5un/wGw==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -15787,9 +14452,9 @@ } }, "node_modules/validator": { - "version": "13.15.23", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.23.tgz", - "integrity": "sha512-4yoz1kEWqUjzi5zsPbAS/903QXSYp0UOtHsPpp7p9rHAw/W+dkInskAE386Fat3oKRROwO98d9ZB0G4cObgUyw==", + "version": "13.15.35", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.35.tgz", + "integrity": "sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==", "license": "MIT", "engines": { "node": ">= 0.10" @@ -15814,18 +14479,6 @@ "defaults": "^1.0.3" } }, - "node_modules/web-encoding": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/web-encoding/-/web-encoding-1.1.5.tgz", - "integrity": "sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==", - "license": "MIT", - "dependencies": { - "util": "^0.12.3" - }, - "optionalDependencies": { - "@zxing/text-encoding": "0.9.0" - } - }, "node_modules/web-streams-polyfill": { "version": "4.0.0-beta.3", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", @@ -15947,13 +14600,14 @@ "license": "ISC" }, "node_modules/which-typed-array": { - "version": "1.1.19", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", - "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "version": "1.1.22", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.22.tgz", + "integrity": "sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==", + "dev": true, "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", + "call-bind": "^1.0.9", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", @@ -15984,10 +14638,9 @@ } }, "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -15995,17 +14648,13 @@ "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">=8" } }, "node_modules/wrap-ansi/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -16017,26 +14666,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -16045,9 +14674,9 @@ "license": "ISC" }, "node_modules/ws": { - "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz", + "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -16141,23 +14770,25 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", - "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", - "devOptional": true, + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "dev": true, "license": "ISC", - "peer": true, "bin": { "yaml": "bin.mjs" }, "engines": { "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" } }, "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "version": "17.7.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 49f5038cde..e5c28eb674 100644 --- a/package.json +++ b/package.json @@ -78,12 +78,11 @@ "@figuro/chatwoot-sdk": "^1.1.16", "@hapi/boom": "^10.0.1", "@paralleldrive/cuid2": "^2.2.2", - "@prisma/client": "^6.16.2", - "@sentry/node": "^10.12.0", - "@types/uuid": "^10.0.0", + "@prisma/client": "^6.19.3", + "@sentry/node": "^10.70.0", "amqplib": "^0.10.5", "audio-decode": "^2.2.3", - "axios": "^1.7.9", + "axios": "^1.19.0", "baileys": "7.0.0-rc14", "class-validator": "^0.14.1", "compression": "^1.7.5", @@ -92,10 +91,10 @@ "dotenv": "^16.4.7", "emoji-regex": "^10.4.0", "eventemitter2": "^6.4.9", - "express": "^4.21.2", + "express": "^4.22.2", "express-async-errors": "^3.1.1", "fluent-ffmpeg": "^2.1.3", - "form-data": "^4.0.1", + "form-data": "^4.0.6", "https-proxy-agent": "^7.0.6", "fetch-socks": "^1.3.2", "i18next": "^23.7.19", @@ -105,41 +104,39 @@ "jsonwebtoken": "^9.0.2", "kafkajs": "^2.2.4", "libphonenumber-js": "^1.12.25", - "link-preview-js": "^3.0.13", + "link-preview-js": "^5.0.0", "long": "^5.2.3", "mediainfo.js": "^0.3.4", "mime": "^4.0.0", "mime-types": "^2.1.35", - "minio": "^8.0.3", - "multer": "^2.0.2", + "minio": "^8.0.7", + "multer": "^2.2.0", "nats": "^2.29.1", "node-cache": "^5.1.2", - "node-cron": "^3.0.3", + "node-cron": "^4.6.0", "openai": "^4.77.3", "pg": "^8.13.1", "pino": "^9.10.0", - "prisma": "^6.1.0", + "prisma": "^6.19.3", "pusher": "^5.2.0", "qrcode": "^1.5.4", "qrcode-terminal": "^0.12.0", "redis": "^4.7.0", "rxjs": "^7.8.2", - "sharp": "^0.34.2", + "sharp": "^0.35.3", "socket.io": "^4.8.1", "socket.io-client": "^4.8.1", "socks-proxy-agent": "^8.0.5", "swagger-ui-express": "^5.0.1", - "tsup": "^8.3.5", - "undici": "^7.16.0", - "uuid": "^13.0.0" + "undici": "^7.29.0", + "uuid": "^13.0.2" }, "overrides": { "whatsapp-rust-bridge": "0.5.5", + "axios": "$axios", + "link-preview-js": "$link-preview-js", "@aws-sdk/xml-builder": { "fast-xml-parser": "5.10.1" - }, - "minio": { - "fast-xml-parser": "4.5.5" } }, "devDependencies": { @@ -155,6 +152,7 @@ "@types/node-cron": "^3.0.11", "@types/qrcode": "^1.5.5", "@types/qrcode-terminal": "^0.12.2", + "@types/uuid": "^10.0.0", "@typescript-eslint/eslint-plugin": "^8.44.0", "@typescript-eslint/parser": "^8.44.0", "commitizen": "^4.3.1", @@ -168,6 +166,7 @@ "lint-staged": "^16.1.6", "prettier": "^3.4.2", "tsconfig-paths": "^4.2.0", + "tsup": "^8.3.5", "tsx": "^4.20.5", "typescript": "^5.7.2" } diff --git a/src/api/integrations/channel/whatsapp/inboundInbox.ts b/src/api/integrations/channel/whatsapp/inboundInbox.ts index b72c582612..245573a6fe 100644 --- a/src/api/integrations/channel/whatsapp/inboundInbox.ts +++ b/src/api/integrations/channel/whatsapp/inboundInbox.ts @@ -90,8 +90,7 @@ export function classifyInboundMessage(received: WAMessage): InboundClassificati if (protocol) return 'protocol'; const text = (message?.conversation || (message?.extendedTextMessage as { text?: string } | undefined)?.text) as - | string - | undefined; + string | undefined; if (text === 'requestPlaceholder' || text === 'onDemandHistSync') return 'control'; if (!message && (received?.messageStubType !== undefined || received?.messageStubParameters?.length)) return 'stub'; diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index efc3cce75f..05e08c0a47 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -1342,8 +1342,7 @@ export class BaileysStartupService extends ChannelStartupService { settings: any, ) => { let activeReceipt: - | { id: string; leaseOwner: string; leaseToken: number; mode: 'off' | 'shadow' | 'enforce' } - | undefined; + { id: string; leaseOwner: string; leaseToken: number; mode: 'off' | 'shadow' | 'enforce' } | undefined; let activeSink: 'webhook' | 'chatwoot' | 'chatbot' | undefined; let activeSinkFailures: DurableInboundSinkFailure[] = []; let heartbeatTimer: NodeJS.Timeout | undefined; @@ -3250,8 +3249,9 @@ export class BaileysStartupService extends ChannelStartupService { } const response = await axios.get(mediaMessage.media, config); + const responseContentType = response.headers['content-type']; - mimetype = response.headers['content-type']; + mimetype = typeof responseContentType === 'string' ? responseContentType : false; } } diff --git a/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts b/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts index 9d4bb09bc1..f89b3478ab 100644 --- a/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts +++ b/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts @@ -1228,7 +1228,8 @@ export class ChatwootService { const response = await axios.get(media, { responseType: 'arraybuffer', }); - mimeType = response.headers['content-type']; + const responseContentType = response.headers['content-type']; + mimeType = typeof responseContentType === 'string' ? responseContentType : ''; } let type = 'document'; @@ -2237,8 +2238,9 @@ export class ChatwootService { const isAdsMessage = (adsMessage && adsMessage.title) || adsMessage.body || adsMessage.thumbnailUrl; if (isAdsMessage) { const imgBuffer = await axios.get(adsMessage.thumbnailUrl, { responseType: 'arraybuffer' }); + const responseContentType = imgBuffer.headers['content-type']; - const extension = mimeTypes.extension(imgBuffer.headers['content-type']); + const extension = typeof responseContentType === 'string' && mimeTypes.extension(responseContentType); const mimeType = extension && mimeTypes.lookup(extension); if (!mimeType) { diff --git a/test/xmacna-baileys-compat.test.cjs b/test/xmacna-baileys-compat.test.cjs index b42f227c4c..a68c92b9ff 100644 --- a/test/xmacna-baileys-compat.test.cjs +++ b/test/xmacna-baileys-compat.test.cjs @@ -1,5 +1,6 @@ const assert = require('node:assert/strict'); const fs = require('node:fs'); +const http = require('node:http'); const path = require('node:path'); const test = require('node:test'); @@ -107,3 +108,62 @@ test('all auth-state providers persist arbitrary rc14 key categories', () => { assert.match(source, /`\$\{category\}-\$\{id\}`/); } }); + +test('security overrides resolve to the audited runtime versions', () => { + assert.equal(require('axios/package.json').version, '1.19.0'); + assert.equal(require('link-preview-js/package.json').version, '5.0.0'); + + const chatwootRoot = require.resolve('@figuro/chatwoot-sdk'); + const chatwootAxiosPackage = require.resolve('axios/package.json', { paths: [chatwootRoot] }); + assert.equal(require(chatwootAxiosPackage).version, '1.19.0'); + + const baileysRoot = require.resolve('baileys'); + const baileysLinkPreviewPackage = require.resolve('link-preview-js/package.json', { paths: [baileysRoot] }); + assert.equal(require(baileysLinkPreviewPackage).version, '5.0.0'); +}); + +test('Chatwoot SDK request remains compatible with the Axios override', async (t) => { + const server = http.createServer((request, response) => { + assert.equal(request.method, 'GET'); + assert.equal(request.url, '/api/ping?probe=inbox'); + assert.equal(request.headers.api_access_token, 'test-token'); + response.writeHead(200, { 'content-type': 'application/json' }); + response.end(JSON.stringify({ ok: true })); + }); + await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)); + t.after(() => server.close()); + + const address = server.address(); + const { request } = require('@figuro/chatwoot-sdk/dist/core/request'); + const result = await request( + { + basePath: `http://127.0.0.1:${address.port}`, + with_credentials: false, + credentials: 'omit', + token: 'test-token', + }, + { method: 'GET', url: '/api/ping', query: { probe: 'inbox' } }, + ); + + assert.deepEqual(result, { ok: true }); +}); + +test('Baileys link preview, node-cron and sharp major updates keep required APIs', async () => { + const { getLinkPreview } = await import('link-preview-js'); + assert.equal(typeof getLinkPreview, 'function'); + + const cron = require('node-cron'); + assert.equal(cron.validate('0,30 * * * *'), true); + const task = cron.schedule('0 0 1 1 *', () => {}, { timezone: 'UTC' }); + task.destroy(); + + const sharp = require('sharp'); + assert.equal(typeof sharp, 'function'); + const metadata = await sharp({ + create: { width: 1, height: 1, channels: 4, background: '#00000000' }, + }) + .png() + .metadata(); + assert.equal(metadata.width, 1); + assert.equal(metadata.height, 1); +}); From 33b730ecf502b212011917948a8e6f3c14327c55 Mon Sep 17 00:00:00 2001 From: raffareis Date: Thu, 13 Aug 2026 03:27:57 -0300 Subject: [PATCH 11/19] fix(inbox): degrade mode races and scope failure propagation to n8n (#500) Segunda visao rodada 2, regressoes 1 e 2: - claim() no longer throws when the instance leaves enforce between the in-memory mode check and the claim transaction. It degrades to the live mode (shadow -> observe, off -> bypassed passthrough), reports effectiveMode so the handler refreshes its cached mode, and never drops the message. A throw here aborted the batch during the exact emergency procedure (canary rollback) that must not lose messages. - Inbound failure propagation at the chatbot controller is now opt-in per integrator: only N8nController rethrows in enforce. A transient failure in dify/openai/typebot/evolutionBot no longer fails the aggregate 'chatbot' sink, which replayed an n8n POST that had already delivered. Proofs: tsc clean, lint clean, unit 17/17, PostgreSQL 11/11 x3 (skipped 0) including new coverage: mode-degrade claim (shadow and off), updated mode-race contract (claim never throws; blocked downgrade or clean bypass), and per-integrator propagation contract. --- .../channel/whatsapp/inboundInbox.ts | 55 ++++++++--- .../whatsapp/whatsapp.baileys.service.ts | 14 +++ .../chatbot/base-chatbot.controller.ts | 12 ++- .../chatbot/n8n/controllers/n8n.controller.ts | 4 + test/xmacna-inbound-inbox.postgres.test.ts | 91 +++++++++++++++++-- test/xmacna-inbound-service-contracts.test.ts | 24 +++++ 6 files changed, 179 insertions(+), 21 deletions(-) diff --git a/src/api/integrations/channel/whatsapp/inboundInbox.ts b/src/api/integrations/channel/whatsapp/inboundInbox.ts index 245573a6fe..3865ec0d84 100644 --- a/src/api/integrations/channel/whatsapp/inboundInbox.ts +++ b/src/api/integrations/channel/whatsapp/inboundInbox.ts @@ -5,7 +5,7 @@ import { WAMessage } from 'baileys'; import { createHash } from 'crypto'; export type InboundClassification = 'real' | 'stub' | 'control' | 'protocol'; -export type InboundClaimKind = 'claimed' | 'duplicate' | 'promoted' | 'collision'; +export type InboundClaimKind = 'claimed' | 'duplicate' | 'promoted' | 'collision' | 'bypassed'; export type InboundIdentity = { sourceCluster: string; @@ -29,6 +29,10 @@ export type InboundClaimResult = { messageRecordId?: string; shouldDispatch: boolean; leaseToken?: number; + /** Set when the instance left enforce between the in-memory check and the + * claim transaction; the caller must refresh its cached mode and continue + * without a durable receipt instead of dropping the message. */ + effectiveMode?: InboundInboxMode; }; export type InboundWorkItem = { @@ -130,7 +134,7 @@ export function canTransitionInboundMode(current: string, target: string, incomp export function evaluateExistingReceipt( existing: ExistingReceipt, incoming: Pick, -): Exclude { +): Exclude { if (existing.payloadHash === incoming.payloadHash) return 'duplicate'; if (existing.classification !== 'real' && incoming.classification === 'real') return 'promoted'; if (incoming.classification !== 'real') return 'duplicate'; @@ -205,14 +209,15 @@ export class PrismaInboundInbox { let uniqueConflict = false; for (let attempt = 0; attempt < 8; attempt += 1) { try { - return await this.repository.$transaction( + const outcome = await this.repository.$transaction( async (tx) => { const instance = await tx.instance.findUnique({ where: { id: input.instanceId }, select: { inboundInboxMode: true }, }); - if (instance?.inboundInboxMode !== 'enforce') { - throw new Error(`Inbound inbox mode changed before claim for instance ${input.instanceId}`); + const liveMode = resolveInboundMode(instance?.inboundInboxMode, 'off'); + if (liveMode !== 'enforce') { + return { divergedMode: liveMode }; } const receipt = await tx.inboundReceipt.create({ data: { @@ -239,15 +244,19 @@ export class PrismaInboundInbox { await tx.inboundReceipt.update({ where: { id: receipt.id }, data: { messageRecordId } }); } return { - kind: 'claimed' as const, - receiptId: receipt.id, - messageRecordId, - shouldDispatch: input.classification === 'real' || input.classification === 'protocol', - leaseToken: input.classification === 'real' ? 1 : undefined, + result: { + kind: 'claimed' as const, + receiptId: receipt.id, + messageRecordId, + shouldDispatch: input.classification === 'real' || input.classification === 'protocol', + leaseToken: input.classification === 'real' ? 1 : undefined, + }, }; }, { isolationLevel: Prisma.TransactionIsolationLevel.Serializable }, ); + if ('divergedMode' in outcome) return this.claimAfterModeChange(input, outcome.divergedMode); + return outcome.result; } catch (error) { if (isUniqueConstraint(error)) { uniqueConflict = true; @@ -285,8 +294,9 @@ export class PrismaInboundInbox { where: { id: input.instanceId }, select: { inboundInboxMode: true }, }); - if (instance?.inboundInboxMode !== 'enforce') { - throw new Error(`Inbound inbox mode changed before promotion for instance ${input.instanceId}`); + const liveMode = resolveInboundMode(instance?.inboundInboxMode, 'off'); + if (liveMode !== 'enforce') { + return { divergedMode: liveMode } as const; } const current = await tx.inboundReceipt.findUnique({ where: { sourceCluster_instanceScope_messageId: key }, @@ -342,6 +352,7 @@ export class PrismaInboundInbox { }, { isolationLevel: Prisma.TransactionIsolationLevel.Serializable }, ); + if ('divergedMode' in outcome) return this.claimAfterModeChange(input, outcome.divergedMode); if (outcome.settledReplay) { return this.recordSettledReplay( outcome.settledReplay, @@ -367,6 +378,26 @@ export class PrismaInboundInbox { throw new Error('Inbound receipt promotion retry exhausted'); } + // The instance left enforce between the caller's in-memory mode check and + // the claim transaction (mode transitions serialize against claims). Failing + // here would drop the message with no receipt and no reconciler; degrade to + // the live mode's semantics instead and tell the caller to refresh its cache. + private async claimAfterModeChange( + input: InboundClaimInput, + liveMode: InboundInboxMode, + ): Promise { + if (liveMode === 'shadow') { + const observed = await this.observe(input); + return { ...observed, effectiveMode: 'shadow' }; + } + return { + kind: 'bypassed', + receiptId: '', + shouldDispatch: input.classification === 'real' || input.classification === 'protocol', + effectiveMode: 'off', + }; + } + private async recordSettledReplay( existing: ExistingReceipt, input: InboundClaimInput, diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 05e08c0a47..45ecf5d5c9 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -1377,6 +1377,13 @@ export class BaileysStartupService extends ChannelStartupService { }, inboundMode, ); + if (observed.effectiveMode && observed.effectiveMode !== inboundMode) { + this.logger.warn( + `Inbound inbox mode changed to ${observed.effectiveMode} during claim; refreshing cached mode`, + ); + this.setInboundInboxMode(observed.effectiveMode); + activeInboundMode = observed.effectiveMode; + } if (inboundClassification === 'protocol' && !observed.shouldDispatch) { this.logger.info( `Inbound ${observed.kind} protocol suppressed before edit sinks: ${inboundIdentity.instanceScope}/${inboundMessageId}`, @@ -1520,6 +1527,13 @@ export class BaileysStartupService extends ChannelStartupService { }, inboundMode, ); + if (claim.effectiveMode && claim.effectiveMode !== inboundMode) { + this.logger.warn( + `Inbound inbox mode changed to ${claim.effectiveMode} during claim; continuing without enforce for ${inboundMessageId}`, + ); + this.setInboundInboxMode(claim.effectiveMode); + activeInboundMode = claim.effectiveMode; + } durableMessageRecordId = claim.messageRecordId; if (claim.leaseToken) { activeReceipt = { diff --git a/src/api/integrations/chatbot/base-chatbot.controller.ts b/src/api/integrations/chatbot/base-chatbot.controller.ts index 471b8b7ff0..a3f41f0d1b 100644 --- a/src/api/integrations/chatbot/base-chatbot.controller.ts +++ b/src/api/integrations/chatbot/base-chatbot.controller.ts @@ -953,11 +953,21 @@ export abstract class BaseChatbotController { sessionRepository: any; userMessageDebounce: { [key: string]: { message: string; timeoutId: NodeJS.Timeout } } = {}; + protected override shouldPropagateInboundFailure(): boolean { + return true; + } + protected getFallbackBotId(settings: any): string | undefined { return settings?.fallbackId; } diff --git a/test/xmacna-inbound-inbox.postgres.test.ts b/test/xmacna-inbound-inbox.postgres.test.ts index 90d1c1c2d3..ec1148264f 100644 --- a/test/xmacna-inbound-inbox.postgres.test.ts +++ b/test/xmacna-inbound-inbox.postgres.test.ts @@ -185,17 +185,27 @@ test('enforce downgrade serializes against a concurrent ingress claim', { skip: const downgrade = transitionInboundMode(repository as any, instanceName, sourceCluster, 'off'); const [claimResult, downgradeResult] = await Promise.allSettled([claim, downgrade]); - assert.notEqual( - claimResult.status === 'fulfilled' && downgradeResult.status === 'fulfilled', - true, - 'claim and downgrade must not both commit', - ); - if (claimResult.status === 'fulfilled') { - assert.equal(downgradeResult.status, 'rejected'); + if (claimResult.status !== 'fulfilled') { + assert.fail(`claim must never throw on a mode race — that used to drop the batch: ${claimResult.reason}`); + } + if (claimResult.value.kind === 'claimed') { + // Claim won the serialization: an in-flight enforce receipt must block + // the downgrade until it drains. + if (downgradeResult.status !== 'rejected') { + assert.fail('an in-flight enforce receipt must block the downgrade'); + } assert.ok(downgradeResult.reason instanceof InboundModeTransitionBlockedError); } else { + // Downgrade won: the claim must degrade to the live mode with baseline + // delivery semantics and leave no enforce receipt behind. + assert.equal(claimResult.value.kind, 'bypassed'); + assert.equal(claimResult.value.effectiveMode, 'off'); + assert.equal(claimResult.value.shouldDispatch, true); assert.equal(downgradeResult.status, 'fulfilled'); - assert.match(String(claimResult.reason), /mode changed before claim/); + const leftover = await repository.inboundReceipt.count({ + where: { sourceCluster, instanceScope, messageId }, + }); + assert.equal(leftover, 0, 'a degraded claim must not create receipts'); } await repository.inboundReceipt.deleteMany({ where: { sourceCluster, instanceScope, messageId } }); @@ -788,3 +798,68 @@ test('historical backfill never queues replay and tombstones migrate with rewrit rmSync(tempDir, { recursive: true, force: true }); } }); + +test('claim degrades to the live mode instead of dropping when the instance left enforce', { skip: !enabled }, async () => { + const repository = new PrismaClient(); + const inbox = new PrismaInboundInbox(repository as any); + const suffix = randomUUID(); + const sourceCluster = 'local-mode-degrade'; + const cases = [ + { name: `Degrade Shadow ${suffix}`, id: `degrade-shadow-${suffix}`, liveMode: 'shadow' as const }, + { name: `Degrade Off ${suffix}`, id: `degrade-off-${suffix}`, liveMode: 'off' as const }, + ]; + + try { + await repository.$connect(); + await repository.instance.createMany({ + data: cases.map((c) => ({ id: c.id, name: c.name, inboundInboxMode: c.liveMode })), + }); + + for (const c of cases) { + // The caller still believes the instance is in enforce (stale in-memory + // mode); the transaction must degrade, never throw and never drop. + const result = await inbox.claim( + { + sourceCluster, + instanceId: c.id, + instanceScope: normalizeInstanceScope(c.name), + contactScope: 'contact@s.whatsapp.net', + messageId: `degrade-${c.liveMode}-${suffix}`, + classification: 'real', + payloadHash: 'b'.repeat(64), + leaseOwner: 'degrade-worker', + messageData: { + key: { id: `degrade-${c.liveMode}-${suffix}`, remoteJid: 'contact@s.whatsapp.net', fromMe: false }, + pushName: 'Mode degrade', + messageType: 'conversation', + message: { conversation: 'hello' }, + source: 'unknown', + messageTimestamp: 1, + instanceId: c.id, + }, + }, + 'enforce', + ); + + assert.equal(result.effectiveMode, c.liveMode); + assert.equal(result.shouldDispatch, true, 'a real message must still reach the sinks in the live mode'); + assert.equal(result.leaseToken, undefined, 'no durable lease may exist outside enforce'); + + const rows = await repository.inboundReceipt.findMany({ + where: { sourceCluster, instanceScope: normalizeInstanceScope(c.name) }, + }); + if (c.liveMode === 'shadow') { + assert.equal(result.kind, 'claimed'); + assert.equal(rows.length, 1); + assert.equal(rows[0].state, 'shadow_seen'); + } else { + assert.equal(result.kind, 'bypassed'); + assert.equal(rows.length, 0, 'off must not create receipts'); + } + } + } finally { + await repository.inboundReceipt.deleteMany({ where: { sourceCluster } }); + await repository.instance.deleteMany({ where: { id: { in: cases.map((c) => c.id) } } }); + await repository.$disconnect(); + } +}); diff --git a/test/xmacna-inbound-service-contracts.test.ts b/test/xmacna-inbound-service-contracts.test.ts index ef911a0062..6e96e0aab6 100644 --- a/test/xmacna-inbound-service-contracts.test.ts +++ b/test/xmacna-inbound-service-contracts.test.ts @@ -15,6 +15,30 @@ test('durable delivery policy propagates errors while baseline policy preserves assert.match(n8nSource, /catch \(error\) \{[\s\S]*?throw error;\s*\}/); }); +test('inbound failure propagation at the controller is opt-in per integrator (n8n only)', () => { + const baseSource = readFileSync('src/api/integrations/chatbot/base-chatbot.controller.ts', 'utf8'); + assert.match(baseSource, /protected shouldPropagateInboundFailure\(\): boolean \{\s*return false;/); + assert.match(baseSource, /this\.shouldPropagateInboundFailure\(\) &&[\s\S]{0,200}throw error;/); + + const n8nSource = readFileSync('src/api/integrations/chatbot/n8n/controllers/n8n.controller.ts', 'utf8'); + assert.match(n8nSource, /protected override shouldPropagateInboundFailure\(\): boolean \{\s*return true;/); + + for (const controller of [ + 'src/api/integrations/chatbot/dify/controllers/dify.controller.ts', + 'src/api/integrations/chatbot/openai/controllers/openai.controller.ts', + 'src/api/integrations/chatbot/typebot/controllers/typebot.controller.ts', + 'src/api/integrations/chatbot/evolutionBot/controllers/evolutionBot.controller.ts', + 'src/api/integrations/chatbot/evoai/controllers/evoai.controller.ts', + 'src/api/integrations/chatbot/flowise/controllers/flowise.controller.ts', + ]) { + assert.doesNotMatch( + readFileSync(controller, 'utf8'), + /shouldPropagateInboundFailure/, + `${controller} must inherit the swallow default; a transient failure there would replay the aggregate chatbot sink`, + ); + } +}); + test('route instanceName wins over query and prevents cross-tenant retargeting', async () => { const received = mergeRequestIdentity( { instanceName: 'tenant-authenticated' }, From aa0087be60bb544be8c12ff4dfbb1285470811db Mon Sep 17 00:00:00 2001 From: raffareis Date: Thu, 13 Aug 2026 04:25:07 -0300 Subject: [PATCH 12/19] fix(inbox): isolate durable n8n chatbot sink (#500) --- .../whatsapp/whatsapp.baileys.service.ts | 40 ++++++++++++++---- .../chatbot/chatbot.controller.ts | 41 +++++++++++++++---- .../chatbot/chatbotDispatchPolicy.ts | 16 ++++++++ test/xmacna-inbound-service-contracts.test.ts | 40 ++++++++++++++++++ 4 files changed, 120 insertions(+), 17 deletions(-) create mode 100644 src/api/integrations/chatbot/chatbotDispatchPolicy.ts diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 45ecf5d5c9..6a0dda87a1 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -878,7 +878,7 @@ export class BaileysStartupService extends ChannelStartupService { currentState: string, operation: () => Promise<'sent' | 'skipped'>, ) => { - if (['sent', 'skipped'].includes(currentState)) return; + if (['sent', 'skipped'].includes(currentState)) return 'already-settled' as const; const failure = await attemptDurableInboundSink({ sink, operation, @@ -886,7 +886,11 @@ export class BaileysStartupService extends ChannelStartupService { onDeliveryFailure: ({ error }) => this.logger.error(`Recovered inbound ${sink} failed for ${item.receiptId}: ${error.message}`), }); - if (failure) failures.push(failure); + if (failure) { + failures.push(failure); + return 'failed' as const; + } + return 'succeeded' as const; }; try { @@ -916,8 +920,8 @@ export class BaileysStartupService extends ChannelStartupService { return 'sent'; }); - await attempt('chatbot', item.chatbotState, async () => { - await chatbotController.emit({ + const recoveredChatbot = await attempt('chatbot', item.chatbotState, async () => { + await chatbotController.emitDurableInbound({ instance: { instanceName: this.instance.name, instanceId: this.instanceId }, remoteJid: messageRaw.key.remoteJid, msg: messageRaw, @@ -925,6 +929,14 @@ export class BaileysStartupService extends ChannelStartupService { }); return 'sent'; }); + if (recoveredChatbot === 'succeeded') { + await chatbotController.emitBestEffortInbound({ + instance: { instanceName: this.instance.name, instanceId: this.instanceId }, + remoteJid: messageRaw.key.remoteJid, + msg: messageRaw, + pushName: messageRaw.pushName, + }); + } if (failures.length > 0) { const summary = failures.map(({ sink, error }) => `${sink}: ${error.message}`).join('; '); @@ -1696,7 +1708,7 @@ export class BaileysStartupService extends ChannelStartupService { try { if (!activeReceipt) { await operation(); - return; + return true; } const failure = await attemptDurableInboundSink({ sink, @@ -1705,7 +1717,11 @@ export class BaileysStartupService extends ChannelStartupService { onDeliveryFailure: ({ error }) => this.logger.error(`Inbound ${sink} failed for ${activeReceipt!.id}: ${error.message}`), }); - if (failure) activeSinkFailures.push(failure); + if (failure) { + activeSinkFailures.push(failure); + return false; + } + return true; } finally { activeSink = undefined; } @@ -1883,8 +1899,8 @@ export class BaileysStartupService extends ChannelStartupService { return 'sent'; }); - await attemptActiveSink('chatbot', async () => { - await chatbotController.emit({ + const chatbotDelivered = await attemptActiveSink('chatbot', async () => { + await chatbotController.emitDurableInbound({ instance: { instanceName: this.instance.name, instanceId: this.instanceId }, remoteJid: messageRaw.key.remoteJid, msg: messageRaw, @@ -1892,6 +1908,14 @@ export class BaileysStartupService extends ChannelStartupService { }); return 'sent'; }); + if (chatbotDelivered) { + await chatbotController.emitBestEffortInbound({ + instance: { instanceName: this.instance.name, instanceId: this.instanceId }, + remoteJid: messageRaw.key.remoteJid, + msg: messageRaw, + pushName: messageRaw.pushName, + }); + } const contact = await this.prismaRepository.contact.findFirst({ where: { remoteJid: received.key.remoteJid, instanceId: this.instanceId }, diff --git a/src/api/integrations/chatbot/chatbot.controller.ts b/src/api/integrations/chatbot/chatbot.controller.ts index 511cced641..bef7a6d933 100644 --- a/src/api/integrations/chatbot/chatbot.controller.ts +++ b/src/api/integrations/chatbot/chatbot.controller.ts @@ -14,6 +14,8 @@ import { Logger } from '@config/logger.config'; import { IntegrationSession } from '@prisma/client'; import { findBotByTrigger } from '@utils/findBotByTrigger'; +import { runBestEffortChatbots } from './chatbotDispatchPolicy'; + export type EmitData = { instance: InstanceDto; remoteJid: string; @@ -91,15 +93,36 @@ export class ChatbotController { pushName, isIntegration, }; - await Promise.all([ - evolutionBotController.emit(emitData), - typebotController.emit(emitData), - openaiController.emit(emitData), - difyController.emit(emitData), - n8nController.emit(emitData), - evoaiController.emit(emitData), - flowiseController.emit(emitData), - ]); + await this.emitDurableInbound(emitData); + await this.emitBestEffortInbound(emitData); + } + + /** + * The legacy receipt column is named `chatbotState`, but its durable + * contract is specifically the n8n delivery. Running n8n alone lets the + * caller persist `sent` before any non-durable integrator can have effects. + */ + public async emitDurableInbound(data: EmitData): Promise { + await n8nController.emit(data); + } + + /** + * Other chatbot integrations remain best-effort. They run only after n8n is + * durably acknowledged and are never allowed to reopen the n8n receipt. + */ + public async emitBestEffortInbound(data: EmitData): Promise { + await runBestEffortChatbots( + [ + { name: 'evolutionBot', emit: () => evolutionBotController.emit(data) }, + { name: 'typebot', emit: () => typebotController.emit(data) }, + { name: 'openai', emit: () => openaiController.emit(data) }, + { name: 'dify', emit: () => difyController.emit(data) }, + { name: 'evoai', emit: () => evoaiController.emit(data) }, + { name: 'flowise', emit: () => flowiseController.emit(data) }, + ], + (name, error) => + this.logger.error(`Best-effort chatbot ${name} failed after durable n8n delivery: ${error.message}`), + ); } public processDebounce( diff --git a/src/api/integrations/chatbot/chatbotDispatchPolicy.ts b/src/api/integrations/chatbot/chatbotDispatchPolicy.ts new file mode 100644 index 0000000000..2b92d91c35 --- /dev/null +++ b/src/api/integrations/chatbot/chatbotDispatchPolicy.ts @@ -0,0 +1,16 @@ +export type BestEffortChatbot = { + name: string; + emit: () => Promise; +}; + +export async function runBestEffortChatbots( + chatbots: BestEffortChatbot[], + onFailure: (name: string, error: Error) => void, +): Promise { + const outcomes = await Promise.allSettled(chatbots.map(({ emit }) => emit())); + outcomes.forEach((outcome, index) => { + if (outcome.status !== 'rejected') return; + const error = outcome.reason instanceof Error ? outcome.reason : new Error(String(outcome.reason)); + onFailure(chatbots[index].name, error); + }); +} diff --git a/test/xmacna-inbound-service-contracts.test.ts b/test/xmacna-inbound-service-contracts.test.ts index 6e96e0aab6..dc3ed107f5 100644 --- a/test/xmacna-inbound-service-contracts.test.ts +++ b/test/xmacna-inbound-service-contracts.test.ts @@ -3,6 +3,7 @@ import test from 'node:test'; import { readFileSync } from 'node:fs'; import { mergeRequestIdentity } from '../src/api/abstract/requestIdentity'; +import { runBestEffortChatbots } from '../src/api/integrations/chatbot/chatbotDispatchPolicy'; import { applyDeliveryErrorPolicy } from '../src/api/integrations/chatbot/deliveryErrorPolicy'; test('durable delivery policy propagates errors while baseline policy preserves swallow semantics', () => { @@ -39,6 +40,45 @@ test('inbound failure propagation at the controller is opt-in per integrator (n8 } }); +test('durable n8n delivery is isolated from best-effort chatbot effects', async () => { + const calls: string[] = []; + const failures: string[] = []; + + await assert.rejects( + async () => { + calls.push('n8n'); + throw new Error('n8n unavailable'); + }, + /n8n unavailable/, + ); + assert.deepEqual(calls, ['n8n'], 'best-effort integrations must not run before durable n8n succeeds'); + + calls.length = 0; + calls.push('n8n'); + await runBestEffortChatbots( + [ + { name: 'typebot', emit: async () => void calls.push('typebot') }, + { + name: 'dify', + emit: async () => { + calls.push('dify'); + throw new Error('dify unavailable'); + }, + }, + ], + (name, error) => failures.push(`${name}:${error.message}`), + ); + + assert.deepEqual(calls, ['n8n', 'typebot', 'dify']); + assert.deepEqual(failures, ['dify:dify unavailable']); + + const serviceSource = readFileSync('src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts', 'utf8'); + assert.match(serviceSource, /attempt\('chatbot'[\s\S]{0,500}emitDurableInbound/); + assert.match(serviceSource, /recoveredChatbot === 'succeeded'[\s\S]{0,300}emitBestEffortInbound/); + assert.match(serviceSource, /attemptActiveSink\('chatbot'[\s\S]{0,500}emitDurableInbound/); + assert.match(serviceSource, /if \(chatbotDelivered\)[\s\S]{0,300}emitBestEffortInbound/); +}); + test('route instanceName wins over query and prevents cross-tenant retargeting', async () => { const received = mergeRequestIdentity( { instanceName: 'tenant-authenticated' }, From 87a830b97be9e749822c4e285fd61c4830969e16 Mon Sep 17 00:00:00 2001 From: raffareis Date: Thu, 13 Aug 2026 04:29:27 -0300 Subject: [PATCH 13/19] fix(chatwoot): give edit revisions stable source ids (#501) --- .../chatwoot/chatwootMessageIdentity.ts | 6 ++++++ .../chatwoot/services/chatwoot.service.ts | 4 +++- test/xmacna-inbound-service-contracts.test.ts | 18 ++++++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 src/api/integrations/chatbot/chatwoot/chatwootMessageIdentity.ts diff --git a/src/api/integrations/chatbot/chatwoot/chatwootMessageIdentity.ts b/src/api/integrations/chatbot/chatwoot/chatwootMessageIdentity.ts new file mode 100644 index 0000000000..101e987506 --- /dev/null +++ b/src/api/integrations/chatbot/chatwoot/chatwootMessageIdentity.ts @@ -0,0 +1,6 @@ +import { createHash } from 'crypto'; + +export function buildChatwootEditSourceId(messageId: string, editedContent: string): string { + const revision = createHash('sha256').update(editedContent, 'utf8').digest('hex'); + return `WAID:${messageId}:EDIT:${revision}`; +} diff --git a/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts b/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts index f89b3478ab..b45e4bce15 100644 --- a/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts +++ b/src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts @@ -1,6 +1,7 @@ import { InstanceDto } from '@api/dto/instance.dto'; import { Options, Quoted, SendAudioDto, SendMediaDto, SendTextDto } from '@api/dto/sendMessage.dto'; import { resolveInboundMode } from '@api/integrations/channel/whatsapp/inboundInbox'; +import { buildChatwootEditSourceId } from '@api/integrations/chatbot/chatwoot/chatwootMessageIdentity'; import { ChatwootDto } from '@api/integrations/chatbot/chatwoot/dto/chatwoot.dto'; import { postgresClient } from '@api/integrations/chatbot/chatwoot/libs/postgres.client'; import { chatwootImport } from '@api/integrations/chatbot/chatwoot/utils/chatwoot-import-helper'; @@ -2409,6 +2410,7 @@ export class ChatwootService { if (message && message.chatwootConversationId && message.chatwootMessageId) { // Criar nova mensagem com formato: "Mensagem editada:\n\nteste1" const editedText = `\n\n\`${i18next.t('cw.message.edited')}:\`\n\n${editedMessageContent}`; + const editedSourceId = buildChatwootEditSourceId(body.key.id, editedMessageContent); const send = await this.createMessage( instance, @@ -2420,7 +2422,7 @@ export class ChatwootService { { message: { extendedTextMessage: { contextInfo: { stanzaId: key.id } } }, }, - 'WAID:' + body.key.id, + editedSourceId, null, ); if (!send) { diff --git a/test/xmacna-inbound-service-contracts.test.ts b/test/xmacna-inbound-service-contracts.test.ts index dc3ed107f5..2a3fb5b9e7 100644 --- a/test/xmacna-inbound-service-contracts.test.ts +++ b/test/xmacna-inbound-service-contracts.test.ts @@ -4,6 +4,7 @@ import { readFileSync } from 'node:fs'; import { mergeRequestIdentity } from '../src/api/abstract/requestIdentity'; import { runBestEffortChatbots } from '../src/api/integrations/chatbot/chatbotDispatchPolicy'; +import { buildChatwootEditSourceId } from '../src/api/integrations/chatbot/chatwoot/chatwootMessageIdentity'; import { applyDeliveryErrorPolicy } from '../src/api/integrations/chatbot/deliveryErrorPolicy'; test('durable delivery policy propagates errors while baseline policy preserves swallow semantics', () => { @@ -79,6 +80,23 @@ test('durable n8n delivery is isolated from best-effort chatbot effects', async assert.match(serviceSource, /if \(chatbotDelivered\)[\s\S]{0,300}emitBestEffortInbound/); }); +test('Chatwoot edit revisions have stable identities distinct from the original WAID', () => { + const first = buildChatwootEditSourceId('3EB0TEST', 'novo conteúdo'); + const retry = buildChatwootEditSourceId('3EB0TEST', 'novo conteúdo'); + const secondRevision = buildChatwootEditSourceId('3EB0TEST', 'conteúdo corrigido'); + + assert.equal(first, retry); + assert.notEqual(first, 'WAID:3EB0TEST'); + assert.notEqual(first, secondRevision); + assert.match(first, /^WAID:3EB0TEST:EDIT:[a-f0-9]{64}$/); + + const serviceSource = readFileSync( + 'src/api/integrations/chatbot/chatwoot/services/chatwoot.service.ts', + 'utf8', + ); + assert.match(serviceSource, /buildChatwootEditSourceId\(body\.key\.id, editedMessageContent\)/); +}); + test('route instanceName wins over query and prevents cross-tenant retargeting', async () => { const received = mergeRequestIdentity( { instanceName: 'tenant-authenticated' }, From c38e081669dcb46c69351aeff0972d45fb4761f2 Mon Sep 17 00:00:00 2001 From: raffareis Date: Sat, 15 Aug 2026 19:24:17 -0300 Subject: [PATCH 14/19] fix(n8n): isolate debounce per instance (#522) --- package.json | 2 +- .../chatbot/base-chatbot.controller.ts | 5 +- .../chatbot/chatbot.controller.ts | 45 +++------- .../integrations/chatbot/chatbotDebounce.ts | 59 +++++++++++++ .../dify/controllers/dify.controller.ts | 2 - .../evoai/controllers/evoai.controller.ts | 2 - .../controllers/evolutionBot.controller.ts | 2 - .../flowise/controllers/flowise.controller.ts | 2 - .../chatbot/n8n/controllers/n8n.controller.ts | 2 - .../openai/controllers/openai.controller.ts | 1 - .../typebot/controllers/typebot.controller.ts | 2 - test/xmacna-chatbot-debounce.test.ts | 83 +++++++++++++++++++ 12 files changed, 159 insertions(+), 48 deletions(-) create mode 100644 src/api/integrations/chatbot/chatbotDebounce.ts create mode 100644 test/xmacna-chatbot-debounce.test.ts diff --git a/package.json b/package.json index e5c28eb674..b22d78b7f4 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "dev:server": "tsx watch ./src/main.ts", "test": "tsx watch ./test/all.test.ts", "test:xmacna-compat": "node --test test/xmacna-baileys-compat.test.cjs", - "test:xmacna-inbox": "tsx --test test/xmacna-inbound-inbox.test.ts test/xmacna-inbound-sink-dispatch.test.ts test/xmacna-inbound-service-contracts.test.ts test/xmacna-baileys-message-processor.test.ts", + "test:xmacna-inbox": "tsx --test test/xmacna-inbound-inbox.test.ts test/xmacna-inbound-sink-dispatch.test.ts test/xmacna-inbound-service-contracts.test.ts test/xmacna-baileys-message-processor.test.ts test/xmacna-chatbot-debounce.test.ts", "test:xmacna-inbox:postgres": "tsx --test test/xmacna-inbound-inbox.postgres.test.ts", "inbound-inbox:maintenance": "tsx src/cli/inboundInboxMaintenance.ts", "lint": "eslint --fix --ext .ts src", diff --git a/src/api/integrations/chatbot/base-chatbot.controller.ts b/src/api/integrations/chatbot/base-chatbot.controller.ts index a3f41f0d1b..0961589a48 100644 --- a/src/api/integrations/chatbot/base-chatbot.controller.ts +++ b/src/api/integrations/chatbot/base-chatbot.controller.ts @@ -11,6 +11,7 @@ import { getConversationMessage } from '@utils/getConversationMessage'; import { BaseChatbotDto } from './base-chatbot.dto'; import { ChatbotController, ChatbotControllerInterface, EmitData } from './chatbot.controller'; +import { buildChatbotDebounceKey, ChatbotDebounceStore } from './chatbotDebounce'; // Common settings interface for all chatbot integrations export interface ChatbotSettings { @@ -59,7 +60,7 @@ export abstract class BaseChatbotController { await this.processBot( diff --git a/src/api/integrations/chatbot/chatbot.controller.ts b/src/api/integrations/chatbot/chatbot.controller.ts index bef7a6d933..860d8c44c6 100644 --- a/src/api/integrations/chatbot/chatbot.controller.ts +++ b/src/api/integrations/chatbot/chatbot.controller.ts @@ -14,6 +14,7 @@ import { Logger } from '@config/logger.config'; import { IntegrationSession } from '@prisma/client'; import { findBotByTrigger } from '@utils/findBotByTrigger'; +import { ChatbotDebounceStore, processChatbotDebounce } from './chatbotDebounce'; import { runBestEffortChatbots } from './chatbotDispatchPolicy'; export type EmitData = { @@ -28,7 +29,7 @@ export interface ChatbotControllerInterface { botRepository: any; settingsRepository: any; sessionRepository: any; - userMessageDebounce: { [key: string]: { message: string; timeoutId: NodeJS.Timeout } }; + userMessageDebounce: ChatbotDebounceStore; createBot(instance: InstanceDto, data: any): Promise; findBot(instance: InstanceDto): Promise; @@ -126,41 +127,21 @@ export class ChatbotController { } public processDebounce( - userMessageDebounce: any, + userMessageDebounce: ChatbotDebounceStore, content: string, - remoteJid: string, + debounceKey: string, debounceTime: number, callback: any, ): Promise { - return new Promise((resolve, reject) => { - if (userMessageDebounce[remoteJid]) { - userMessageDebounce[remoteJid].message += `\n${content}`; - userMessageDebounce[remoteJid].waiters.push({ resolve, reject }); - userMessageDebounce[remoteJid].callback = callback; - this.logger.log('message debounced: ' + userMessageDebounce[remoteJid].message); - clearTimeout(userMessageDebounce[remoteJid].timeoutId); - } else { - userMessageDebounce[remoteJid] = { - message: content, - timeoutId: null, - callback, - waiters: [{ resolve, reject }], - }; - } - - userMessageDebounce[remoteJid].timeoutId = setTimeout(async () => { - const entry = userMessageDebounce[remoteJid]; - const myQuestion = entry.message; - this.logger.log('Debounce complete. Processing message: ' + myQuestion); - delete userMessageDebounce[remoteJid]; - try { - await entry.callback(myQuestion); - entry.waiters.forEach((waiter) => waiter.resolve()); - } catch (error) { - entry.waiters.forEach((waiter) => waiter.reject(error)); - } - }, debounceTime * 1000); - }); + return processChatbotDebounce( + userMessageDebounce, + content, + debounceKey, + debounceTime, + callback, + (merged) => this.logger.log('message debounced: ' + merged), + (flushed) => this.logger.log('Debounce complete. Processing message: ' + flushed), + ); } public checkIgnoreJids(ignoreJids: any, remoteJid: string) { diff --git a/src/api/integrations/chatbot/chatbotDebounce.ts b/src/api/integrations/chatbot/chatbotDebounce.ts new file mode 100644 index 0000000000..1f5b8bf660 --- /dev/null +++ b/src/api/integrations/chatbot/chatbotDebounce.ts @@ -0,0 +1,59 @@ +export type ChatbotDebounceCallback = (content: string) => Promise; + +type DebounceWaiter = { + resolve: () => void; + reject: (error: unknown) => void; +}; + +type ChatbotDebounceEntry = { + message: string; + timeoutId: NodeJS.Timeout | null; + callback: ChatbotDebounceCallback; + waiters: DebounceWaiter[]; +}; + +export type ChatbotDebounceStore = Record; + +export function buildChatbotDebounceKey(instanceName: string, remoteJid: string): string { + return JSON.stringify([instanceName, remoteJid]); +} + +export function processChatbotDebounce( + store: ChatbotDebounceStore, + content: string, + debounceKey: string, + debounceTime: number, + callback: ChatbotDebounceCallback, + onMerged?: (content: string) => void, + onFlushed?: (content: string) => void, +): Promise { + return new Promise((resolve, reject) => { + const current = store[debounceKey]; + if (current) { + current.message += `\n${content}`; + current.waiters.push({ resolve, reject }); + current.callback = callback; + onMerged?.(current.message); + if (current.timeoutId) clearTimeout(current.timeoutId); + } else { + store[debounceKey] = { + message: content, + timeoutId: null, + callback, + waiters: [{ resolve, reject }], + }; + } + + store[debounceKey].timeoutId = setTimeout(async () => { + const entry = store[debounceKey]; + delete store[debounceKey]; + onFlushed?.(entry.message); + try { + await entry.callback(entry.message); + entry.waiters.forEach((waiter) => waiter.resolve()); + } catch (error) { + entry.waiters.forEach((waiter) => waiter.reject(error)); + } + }, debounceTime * 1000); + }); +} diff --git a/src/api/integrations/chatbot/dify/controllers/dify.controller.ts b/src/api/integrations/chatbot/dify/controllers/dify.controller.ts index ebbf2b0d16..2b6ff49117 100644 --- a/src/api/integrations/chatbot/dify/controllers/dify.controller.ts +++ b/src/api/integrations/chatbot/dify/controllers/dify.controller.ts @@ -30,8 +30,6 @@ export class DifyController extends BaseChatbotController { botRepository: any; settingsRepository: any; sessionRepository: any; - userMessageDebounce: { [key: string]: { message: string; timeoutId: NodeJS.Timeout } } = {}; - protected getFallbackBotId(settings: any): string | undefined { return settings?.fallbackId; } diff --git a/src/api/integrations/chatbot/evoai/controllers/evoai.controller.ts b/src/api/integrations/chatbot/evoai/controllers/evoai.controller.ts index 38ce433d40..346c06bfa1 100644 --- a/src/api/integrations/chatbot/evoai/controllers/evoai.controller.ts +++ b/src/api/integrations/chatbot/evoai/controllers/evoai.controller.ts @@ -30,8 +30,6 @@ export class EvoaiController extends BaseChatbotController botRepository: any; settingsRepository: any; sessionRepository: any; - userMessageDebounce: { [key: string]: { message: string; timeoutId: NodeJS.Timeout } } = {}; - protected getFallbackBotId(settings: any): string | undefined { return settings?.evoaiIdFallback; } diff --git a/src/api/integrations/chatbot/evolutionBot/controllers/evolutionBot.controller.ts b/src/api/integrations/chatbot/evolutionBot/controllers/evolutionBot.controller.ts index 13ec5330d7..a26de496e3 100644 --- a/src/api/integrations/chatbot/evolutionBot/controllers/evolutionBot.controller.ts +++ b/src/api/integrations/chatbot/evolutionBot/controllers/evolutionBot.controller.ts @@ -27,8 +27,6 @@ export class EvolutionBotController extends BaseChatbotController { botRepository: any; settingsRepository: any; sessionRepository: any; - userMessageDebounce: { [key: string]: { message: string; timeoutId: NodeJS.Timeout } } = {}; - protected override shouldPropagateInboundFailure(): boolean { return true; } diff --git a/src/api/integrations/chatbot/openai/controllers/openai.controller.ts b/src/api/integrations/chatbot/openai/controllers/openai.controller.ts index 1d9910d1f2..d2ea62388d 100644 --- a/src/api/integrations/chatbot/openai/controllers/openai.controller.ts +++ b/src/api/integrations/chatbot/openai/controllers/openai.controller.ts @@ -32,7 +32,6 @@ export class OpenaiController extends BaseChatbotController { + const store: ChatbotDebounceStore = {}; + const delivered: Array<{ instance: string; content: string }> = []; + const remoteJid = '5511999999999@s.whatsapp.net'; + + await Promise.all([ + processChatbotDebounce( + store, + 'message for instance A', + buildChatbotDebounceKey('instance-a', remoteJid), + 0.01, + async (content) => { + delivered.push({ instance: 'instance-a', content }); + }, + ), + processChatbotDebounce( + store, + 'message for instance B', + buildChatbotDebounceKey('instance-b', remoteJid), + 0.01, + async (content) => { + delivered.push({ instance: 'instance-b', content }); + }, + ), + ]); + + assert.deepEqual( + delivered.sort((left, right) => left.instance.localeCompare(right.instance)), + [ + { instance: 'instance-a', content: 'message for instance A' }, + { instance: 'instance-b', content: 'message for instance B' }, + ], + ); + assert.deepEqual(store, {}); +}); + +test('uses an injective tuple key instead of an ambiguous delimiter', () => { + assert.notEqual(buildChatbotDebounceKey('instance:a', 'contact'), buildChatbotDebounceKey('instance', 'a:contact')); +}); + +test('still coalesces consecutive messages within one instance', async () => { + const store: ChatbotDebounceStore = {}; + const delivered: string[] = []; + const debounceKey = buildChatbotDebounceKey('instance-a', '5511999999999@s.whatsapp.net'); + + await Promise.all([ + processChatbotDebounce(store, 'first', debounceKey, 0.01, async (content) => delivered.push(content)), + processChatbotDebounce(store, 'second', debounceKey, 0.01, async (content) => delivered.push(content)), + ]); + + assert.deepEqual(delivered, ['first\nsecond']); + assert.deepEqual(store, {}); +}); + +test('propagates one callback failure to every coalesced waiter', async () => { + const store: ChatbotDebounceStore = {}; + const debounceKey = buildChatbotDebounceKey('instance-a', '5511999999999@s.whatsapp.net'); + const failure = new Error('n8n unavailable'); + + const results = await Promise.allSettled([ + processChatbotDebounce(store, 'first', debounceKey, 0.01, async () => { + throw failure; + }), + processChatbotDebounce(store, 'second', debounceKey, 0.01, async () => { + throw failure; + }), + ]); + + assert.deepEqual( + results.map((result) => (result.status === 'rejected' ? result.reason : null)), + [failure, failure], + ); + assert.deepEqual(store, {}); +}); From 40f10c844ac3b050c38d351dbb8c00ea91a563ac Mon Sep 17 00:00:00 2001 From: raffareis Date: Sat, 15 Aug 2026 20:24:24 -0300 Subject: [PATCH 15/19] fix(inbox): ignore volatile device metadata (xmacna/elysium#523) --- .../channel/whatsapp/inboundInbox.ts | 12 ++++- test/xmacna-inbound-inbox.test.ts | 47 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/api/integrations/channel/whatsapp/inboundInbox.ts b/src/api/integrations/channel/whatsapp/inboundInbox.ts index 3865ec0d84..872098a136 100644 --- a/src/api/integrations/channel/whatsapp/inboundInbox.ts +++ b/src/api/integrations/channel/whatsapp/inboundInbox.ts @@ -54,7 +54,17 @@ type ExistingReceipt = { leaseToken?: number; }; -const VOLATILE_KEYS = new Set(['timestamp', 'messagetimestamp', 'requestid', 'fromme']); +const VOLATILE_KEYS = new Set([ + 'timestamp', + 'messagetimestamp', + 'requestid', + 'fromme', + // Baileys may enrich a replay of the same message with device metadata a + // few seconds later. It does not change user-visible content and must not + // turn a legitimate redelivery into a cross-payload collision. + 'devicelistmetadata', + 'devicelistmetadataversion', +]); function canonicalize(value: unknown): unknown { if (Buffer.isBuffer(value)) return { type: 'Buffer', data: [...value] }; diff --git a/test/xmacna-inbound-inbox.test.ts b/test/xmacna-inbound-inbox.test.ts index ee114548b0..9183848edd 100644 --- a/test/xmacna-inbound-inbox.test.ts +++ b/test/xmacna-inbound-inbox.test.ts @@ -56,6 +56,53 @@ test('canonical hash detects divergent real content', () => { assert.notEqual(inboundPayloadHash(base as any), inboundPayloadHash(divergent as any)); }); +test('canonical hash ignores Baileys device metadata added to a real-message replay', () => { + const first = { + key: { id: 'same-id' }, + message: { + conversation: 'mesmo conteúdo', + messageContextInfo: { messageSecret: 'stable', threadId: 'thread-1' }, + }, + }; + const enrichedReplay = { + key: { id: 'same-id' }, + message: { + conversation: 'mesmo conteúdo', + messageContextInfo: { + deviceListMetadata: { senderKeyHash: 'volatile' }, + deviceListMetadataVersion: 2, + messageSecret: 'stable', + threadId: 'thread-1', + }, + }, + }; + + assert.equal(inboundPayloadHash(first as any), inboundPayloadHash(enrichedReplay as any)); +}); + +test('canonical hash preserves material message context while ignoring device metadata', () => { + const base = { + key: { id: 'same-id' }, + message: { + extendedTextMessage: { + text: 'resposta', + contextInfo: { stanzaId: 'quoted-a', deviceListMetadataVersion: 1 }, + }, + }, + }; + const divergentQuote = { + key: { id: 'same-id' }, + message: { + extendedTextMessage: { + text: 'resposta', + contextInfo: { stanzaId: 'quoted-b', deviceListMetadataVersion: 2 }, + }, + }, + }; + + assert.notEqual(inboundPayloadHash(base as any), inboundPayloadHash(divergentQuote as any)); +}); + test('receipt policy deduplicates exact replay, promotes every non-real class and quarantines real collision', () => { const exact = { id: 'r1', classification: 'real', payloadHash: 'a' }; assert.equal(evaluateExistingReceipt(exact, { classification: 'real', payloadHash: 'a' }), 'duplicate'); From 93d137a99ba29e1bbbe5884cd1235505da03ef21 Mon Sep 17 00:00:00 2001 From: raffareis Date: Sun, 16 Aug 2026 06:14:08 -0300 Subject: [PATCH 16/19] fix(inbox): trust stable provider message identity (#526) --- .../channel/whatsapp/inboundInbox.ts | 22 ++++++-- .../whatsapp/whatsapp.baileys.service.ts | 2 +- test/xmacna-inbound-inbox.postgres.test.ts | 53 +++++++++++++++---- test/xmacna-inbound-inbox.test.ts | 38 ++++++++++++- 4 files changed, 97 insertions(+), 18 deletions(-) diff --git a/src/api/integrations/channel/whatsapp/inboundInbox.ts b/src/api/integrations/channel/whatsapp/inboundInbox.ts index 872098a136..d44a3a5c04 100644 --- a/src/api/integrations/channel/whatsapp/inboundInbox.ts +++ b/src/api/integrations/channel/whatsapp/inboundInbox.ts @@ -28,6 +28,10 @@ export type InboundClaimResult = { receiptId: string; messageRecordId?: string; shouldDispatch: boolean; + /** The provider reused its stable message id with a different transport + * envelope. This remains a duplicate, but callers may surface the variant + * for diagnostics without redispatching it. */ + payloadHashMismatch?: boolean; leaseToken?: number; /** Set when the instance left enforce between the in-memory check and the * claim transaction; the caller must refresh its cached mode and continue @@ -147,8 +151,13 @@ export function evaluateExistingReceipt( ): Exclude { if (existing.payloadHash === incoming.payloadHash) return 'duplicate'; if (existing.classification !== 'real' && incoming.classification === 'real') return 'promoted'; - if (incoming.classification !== 'real') return 'duplicate'; - return 'collision'; + // sourceCluster + instanceScope + the provider-assigned messageId is the + // durable identity. Baileys can redeliver that same message after enriching + // device, ad-referral or transport fields, so payload byte equality cannot + // decide whether it is a second user message. A different real message has a + // different provider id and receives its own receipt. Keep the hash only as + // diagnostic evidence on the duplicate result. + return 'duplicate'; } function isUniqueConstraint(error: unknown): boolean { @@ -313,13 +322,15 @@ export class PrismaInboundInbox { }); if (!current) throw new Error('Inbound receipt disappeared during promotion'); if (current.classification === 'real') { - const currentKind = current.payloadHash === input.payloadHash ? 'duplicate' : 'collision'; + const currentKind = evaluateExistingReceipt(current, input); + if (currentKind === 'promoted') throw new Error('Real receipt cannot be promoted again'); return { result: { kind: currentKind, receiptId: current.id, messageRecordId: current.messageRecordId ?? undefined, shouldDispatch: false, + payloadHashMismatch: current.payloadHash !== input.payloadHash, } as InboundClaimResult, settledReplay: current, settledKind: currentKind, @@ -377,7 +388,8 @@ export class PrismaInboundInbox { where: { sourceCluster_instanceScope_messageId: key }, }); if (raced?.classification === 'real') { - const racedKind = raced.payloadHash === input.payloadHash ? 'duplicate' : 'collision'; + const racedKind = evaluateExistingReceipt(raced, input); + if (racedKind === 'promoted') throw new Error('Real receipt cannot be promoted after a write conflict'); return this.recordSettledReplay(raced, input, racedKind); } if (attempt === 7) throw error; @@ -440,6 +452,7 @@ export class PrismaInboundInbox { receiptId: existing.id, messageRecordId: existing.messageRecordId ?? undefined, shouldDispatch: false, + payloadHashMismatch: existing.payloadHash !== input.payloadHash, }; } @@ -696,6 +709,7 @@ export class PrismaInboundInbox { messageRecordId: existing.messageRecordId ?? undefined, // Shadow observes only; it must preserve baseline delivery semantics. shouldDispatch: input.classification === 'real' || input.classification === 'protocol', + payloadHashMismatch: existing.payloadHash !== input.payloadHash, }; } } diff --git a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts index 6a0dda87a1..a5977724a8 100644 --- a/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts +++ b/src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts @@ -1569,7 +1569,7 @@ export class BaileysStartupService extends ChannelStartupService { } if (!claim.shouldDispatch) { this.logger.info( - `Inbound ${claim.kind} suppressed before sinks: ${inboundIdentity.instanceScope}/${inboundMessageId}`, + `Inbound ${claim.kind}${claim.payloadHashMismatch ? ' (payload variant)' : ''} suppressed before sinks: ${inboundIdentity.instanceScope}/${inboundMessageId}`, ); continue; } diff --git a/test/xmacna-inbound-inbox.postgres.test.ts b/test/xmacna-inbound-inbox.postgres.test.ts index ec1148264f..840c1d6638 100644 --- a/test/xmacna-inbound-inbox.postgres.test.ts +++ b/test/xmacna-inbound-inbox.postgres.test.ts @@ -323,7 +323,7 @@ test('Chatwoot success followed by webhook failure replays the exact enriched pa } }); -test('collision during an active lease cannot revoke fencing or quarantine the live delivery', { skip: !enabled }, async () => { +test('payload variant during an active lease is deduplicated without revoking fencing', { skip: !enabled }, async () => { const repository = new PrismaClient(); const inbox = new PrismaInboundInbox(repository as any); const suffix = randomUUID(); @@ -360,7 +360,7 @@ test('collision during an active lease cannot revoke fencing or quarantine the l }, 'enforce', ); - const collision = await inbox.claim( + const variant = await inbox.claim( { sourceCluster, instanceId, @@ -373,14 +373,41 @@ test('collision during an active lease cannot revoke fencing or quarantine the l }, 'enforce', ); - assert.equal(collision.kind, 'collision'); - assert.equal(collision.shouldDispatch, false); + assert.equal(variant.kind, 'duplicate'); + assert.equal(variant.shouldDispatch, false); + assert.equal(variant.payloadHashMismatch, true); const live = await repository.inboundReceipt.findUniqueOrThrow({ where: { id: original.receiptId } }); assert.equal(live.state, 'processing'); assert.equal(live.leaseOwner, 'live-worker'); assert.equal(live.leaseToken, original.leaseToken); assert.equal(await inbox.markSink(original.receiptId, 'webhook', 'sent', 'live-worker', original.leaseToken), true); assert.equal(await inbox.markDone(original.receiptId, 'live-worker', original.leaseToken!), true); + + const independent = await inbox.claim( + { + sourceCluster, + instanceId, + instanceScope, + contactScope: 'contact@s.whatsapp.net', + messageId: `${messageId}-next`, + classification: 'real', + payloadHash: 'b'.repeat(64), + leaseOwner: 'next-worker', + messageData: { + key: { id: `${messageId}-next`, remoteJid: 'contact@s.whatsapp.net', fromMe: false }, + pushName: 'Next message', + messageType: 'conversation', + message: { conversation: 'independent content' }, + source: 'unknown', + messageTimestamp: 2, + instanceId, + }, + }, + 'enforce', + ); + assert.equal(independent.kind, 'claimed'); + assert.equal(independent.shouldDispatch, true); + assert.equal(await inbox.markDone(independent.receiptId, 'next-worker', independent.leaseToken!), true); } finally { await repository.inboundReceipt.deleteMany({ where: { sourceCluster, instanceScope } }); await repository.instance.deleteMany({ where: { id: instanceId } }); @@ -622,7 +649,7 @@ test('reconciler leases with fencing, preserves completed sinks and orders each } }); -test('stub promotes, collisions quarantine and instance recreation keeps the tombstone', { skip: !enabled }, async () => { +test('stub promotes, payload variants deduplicate and instance recreation keeps the tombstone', { skip: !enabled }, async () => { const repository = new PrismaClient(); const inbox = new PrismaInboundInbox(repository as any); const suffix = randomUUID(); @@ -682,7 +709,7 @@ test('stub promotes, collisions quarantine and instance recreation keeps the tom assert.equal(promoted.shouldDispatch, true); assert.equal(await inbox.markDone(promoted.receiptId, 'promotion-worker', promoted.leaseToken!), true); - const collision = await inbox.claim( + const variant = await inbox.claim( { sourceCluster, instanceId, @@ -696,9 +723,12 @@ test('stub promotes, collisions quarantine and instance recreation keeps the tom }, 'enforce', ); - assert.equal(collision.kind, 'collision'); - assert.equal(collision.shouldDispatch, false); - assert.equal((await repository.inboundReceipt.findUniqueOrThrow({ where: { id: receiptId } })).state, 'quarantined'); + assert.equal(variant.kind, 'duplicate'); + assert.equal(variant.shouldDispatch, false); + assert.equal(variant.payloadHashMismatch, true); + const afterVariant = await repository.inboundReceipt.findUniqueOrThrow({ where: { id: receiptId } }); + assert.equal(afterVariant.state, 'done'); + assert.equal(afterVariant.collisionHash, null); await repository.inboundReceipt.update({ where: { id: receiptId }, data: { leaseExpiresAt: new Date(0) }, @@ -722,8 +752,9 @@ test('stub promotes, collisions quarantine and instance recreation keeps the tom 'enforce', ); assert.equal(replay.shouldDispatch, false); - const quarantined = await repository.inboundReceipt.findUniqueOrThrow({ where: { id: receiptId } }); - assert.equal(quarantined.state, 'quarantined'); + const deduplicated = await repository.inboundReceipt.findUniqueOrThrow({ where: { id: receiptId } }); + assert.equal(deduplicated.state, 'done'); + assert.ok(deduplicated.duplicateCount >= 100); assert.equal(await repository.inboundReceipt.count({ where: { id: receiptId } }), 1); } finally { if (receiptId) await repository.inboundReceipt.deleteMany({ where: { id: receiptId } }); diff --git a/test/xmacna-inbound-inbox.test.ts b/test/xmacna-inbound-inbox.test.ts index 9183848edd..c63faeefe8 100644 --- a/test/xmacna-inbound-inbox.test.ts +++ b/test/xmacna-inbound-inbox.test.ts @@ -103,7 +103,7 @@ test('canonical hash preserves material message context while ignoring device me assert.notEqual(inboundPayloadHash(base as any), inboundPayloadHash(divergentQuote as any)); }); -test('receipt policy deduplicates exact replay, promotes every non-real class and quarantines real collision', () => { +test('receipt policy uses the stable provider id and tolerates enriched replay envelopes', () => { const exact = { id: 'r1', classification: 'real', payloadHash: 'a' }; assert.equal(evaluateExistingReceipt(exact, { classification: 'real', payloadHash: 'a' }), 'duplicate'); assert.equal( @@ -122,7 +122,41 @@ test('receipt policy deduplicates exact replay, promotes every non-real class an evaluateExistingReceipt(exact, { classification: 'protocol', payloadHash: 'b' }), 'duplicate', ); - assert.equal(evaluateExistingReceipt(exact, { classification: 'real', payloadHash: 'b' }), 'collision'); + assert.equal(evaluateExistingReceipt(exact, { classification: 'real', payloadHash: 'b' }), 'duplicate'); +}); + +test('CTWA referral enrichment changes the diagnostic hash without changing receipt identity', () => { + const original = { + key: { id: 'ctwa-provider-id', remoteJid: 'lead@s.whatsapp.net', fromMe: false }, + message: { + extendedTextMessage: { + text: 'Quero informações', + contextInfo: { externalAdReply: { title: 'Campanha', sourceType: 'ad' } }, + }, + }, + }; + const enriched = { + ...original, + message: { + extendedTextMessage: { + text: 'Quero informações', + contextInfo: { + externalAdReply: { title: 'Campanha', sourceType: 'ad', mediaType: 2, thumbnailUrl: 'https://cdn.invalid/ad' }, + }, + }, + }, + }; + const originalHash = inboundPayloadHash(original as any); + const enrichedHash = inboundPayloadHash(enriched as any); + + assert.notEqual(originalHash, enrichedHash); + assert.equal( + evaluateExistingReceipt( + { id: 'receipt', classification: 'real', payloadHash: originalHash }, + { classification: 'real', payloadHash: enrichedHash }, + ), + 'duplicate', + ); }); test('invalid persisted modes never enable global enforce', () => { From 2d4f243e999cb37fc45017508918a1de480d8a18 Mon Sep 17 00:00:00 2001 From: raffareis Date: Fri, 11 Sep 2026 22:38:19 -0300 Subject: [PATCH 17/19] fix(n8n): detach chatbot debounce flush from the durable inbox queue (xmacna/elysium#518) Problem ------- On the Baileys route, consecutive messages from the same lead were never coalesced and every message locked the whole instance for debounceTime + n8n workflow time. processChatbotDebounce returned a Promise that only resolved on the timer flush. BaseChatbotController.emit awaited it, chatbot.controller treats n8n as the awaited durable sink (emitDurableInbound), and BaileysMessageProcessor is a single Promise chain per instance. So the 2nd message only entered after the 1st buffer had already flushed. Cloud API does not go through the queue, which is why it coalesced. Upstream v2.3.7 called n8nController.emit without await (fire-and-forget). Fix --- - processChatbotDebounce now returns a synchronous acceptance receipt { merged, metadata, flushed }. The flush (POST to n8n + WhatsApp reply) runs detached from the caller with its own failure log (onFlushError); flushed always carries an internal catch, so ignoring it can never become an unhandledRejection. - BaseChatbotController.emit no longer awaits the debounce: accepting the message into the buffer counts as delivery of the `chatbot` sink for the inbox receipt. The non-debounced branch is unchanged and still propagates failures in enforce mode. - A stale timer can no longer dispatch a buffer cycle it did not schedule. - Buffer key is unchanged (instanceName + remoteJid, #522). - The n8n payload gains messageCount, firstMessageTimestamp and lastMessageTimestamp. The buffer accumulates them; msg/keyId/ messageTimestamp keep describing the LAST coalesced message. Without debounce the three fields describe a single message. Metadata travels on a shallow copy of msg, never mutating the messageRaw persisted in the receipt. - Cloud API keeps coalescing; it only stops waiting for the flush before continuing to Chatwoot, and flush errors, which were already swallowed there outside enforce mode, now go to the dedicated log. Guarantee change vs #500 (deliberate) ------------------------------------- Between acceptance in the buffer and the flush, the message lives only in memory. If the process dies in that window, the durable receipt already marked the `chatbot` sink as delivered and there is no replay. This is the same risk as upstream, now explicit. Strict at-least-once would require persisting the buffer itself in the receipt; left as a note, not a default. Marker ------ XMACNA_DEBOUNCE_DETACHED_518 appears in code comments and in the flush failure log string, so it survives in dist/main.js for post-deploy grep. It deliberately does not start with XMACNA_PATCH: this is a fork source change, not a Dockerfile build-time patch, so it does not belong in validate-patches.sh and does not inflate its generic XMACNA_PATCH count. Tests ----- test/xmacna-chatbot-debounce.test.ts adds the serialized-arrival reproduction (await the 1st emit before the 2nd, as the Baileys queue does), which failed before this change with "aceitacao no buffer levou 201ms (janela de 200ms)", plus a slow-flush concurrency case, metadata accumulation, and timestamp normalization. The existing concurrent cases now observe acceptance.flushed. --- .../chatbot/base-chatbot.controller.ts | 35 ++- .../chatbot/chatbot.controller.ts | 37 +++- .../integrations/chatbot/chatbotDebounce.ts | 181 ++++++++++++--- .../chatbot/n8n/services/n8n.service.ts | 11 + test/xmacna-chatbot-debounce.test.ts | 208 +++++++++++++++--- 5 files changed, 391 insertions(+), 81 deletions(-) diff --git a/src/api/integrations/chatbot/base-chatbot.controller.ts b/src/api/integrations/chatbot/base-chatbot.controller.ts index 0961589a48..e7e92d5d74 100644 --- a/src/api/integrations/chatbot/base-chatbot.controller.ts +++ b/src/api/integrations/chatbot/base-chatbot.controller.ts @@ -11,7 +11,24 @@ import { getConversationMessage } from '@utils/getConversationMessage'; import { BaseChatbotDto } from './base-chatbot.dto'; import { ChatbotController, ChatbotControllerInterface, EmitData } from './chatbot.controller'; -import { buildChatbotDebounceKey, ChatbotDebounceStore } from './chatbotDebounce'; +import { + buildChatbotDebounceKey, + ChatbotDebounceMetadata, + ChatbotDebounceStore, + normalizeMessageTimestamp, +} from './chatbotDebounce'; + +/** + * XMACNA_DEBOUNCE_DETACHED_518: os metadados do buffer viajam numa copia rasa da + * mensagem, nunca mutando o `messageRaw` que o inbox ja persistiu no recibo. + * O consumidor canonico e o payload do n8n (`n8n.service.ts`). + */ +export const CHATBOT_DEBOUNCE_METADATA_FIELD = 'debounceMetadata' as const; + +export function withChatbotDebounceMetadata(msg: T, metadata: ChatbotDebounceMetadata): T { + if (!msg || typeof msg !== 'object') return msg; + return { ...(msg as Record), [CHATBOT_DEBOUNCE_METADATA_FIELD]: metadata } as T; +} // Common settings interface for all chatbot integrations export interface ChatbotSettings { @@ -921,12 +938,18 @@ export abstract class BaseChatbotController 0) { - await this.processDebounce( + // XMACNA_DEBOUNCE_DETACHED_518: SEM `await`. A aceitacao no buffer e a + // entrega do sink `chatbot`; o flush roda destacado da fila serial da + // instancia. Aguardar aqui e o que impedia o agrupamento na rota + // Baileys e prendia a instancia por `debounceTime + workflow`. + // Erro do flush nao volta por aqui: sai pelo `onFlushError` do + // `processDebounce`. + this.processDebounce( this.userMessageDebounce, content, buildChatbotDebounceKey(instance.instanceName, remoteJid), debounceTime, - async (debouncedContent) => { + async (debouncedContent, debounceMetadata) => { await this.processBot( this.waMonitor.waInstances[instance.instanceName], remoteJid, @@ -935,9 +958,10 @@ export abstract class BaseChatbotController { - return processChatbotDebounce( - userMessageDebounce, - content, - debounceKey, - debounceTime, - callback, - (merged) => this.logger.log('message debounced: ' + merged), - (flushed) => this.logger.log('Debounce complete. Processing message: ' + flushed), - ); + callback: ChatbotDebounceCallback, + messageTimestamp?: unknown, + ): ChatbotDebounceAcceptance { + return processChatbotDebounce(userMessageDebounce, content, debounceKey, debounceTime, callback, { + messageTimestamp, + onMerged: (merged) => this.logger.log('message debounced: ' + merged), + onFlushed: (flushed) => this.logger.log('Debounce complete. Processing message: ' + flushed), + onFlushError: (error) => + this.logger.error( + `XMACNA_DEBOUNCE_DETACHED_518 flush failed for ${debounceKey}: ${ + error instanceof Error ? error.message : String(error) + }`, + ), + }); } public checkIgnoreJids(ignoreJids: any, remoteJid: string) { diff --git a/src/api/integrations/chatbot/chatbotDebounce.ts b/src/api/integrations/chatbot/chatbotDebounce.ts index 1f5b8bf660..d2f89d2cc5 100644 --- a/src/api/integrations/chatbot/chatbotDebounce.ts +++ b/src/api/integrations/chatbot/chatbotDebounce.ts @@ -1,59 +1,170 @@ -export type ChatbotDebounceCallback = (content: string) => Promise; +/** + * Debounce de mensagens de entrada dos chatbots. + * + * XMACNA_DEBOUNCE_DETACHED_518 — mudanca de contrato em relacao ao upstream e a + * `xmacna/2.3.7-durable-inbox`: + * + * Antes, esta funcao devolvia uma Promise que so resolvia no flush do timer. + * O sink duravel `chatbot` do inbox (#500) faz `await` dessa Promise dentro da + * fila serial por instancia do `BaileysMessageProcessor`, entao cada mensagem + * prendia a instancia por `debounceTime + tempo do workflow n8n` e a mensagem + * seguinte do mesmo lead so entrava depois que o buffer da anterior ja tinha + * fechado — ou seja, o debounce nunca coalescia na rota Baileys (#518). + * + * Agora a chamada devolve SINCRONAMENTE um recibo de ACEITACAO no buffer. O + * flush (POST ao n8n + resposta ao WhatsApp) roda destacado do chamador, com + * log proprio de falha. Quem quiser observar o flush (testes, diagnostico) usa + * `acceptance.flushed`. + * + * Consequencia de garantia, deliberada e registrada em #518/#500: entre a + * aceitacao no buffer e o flush a mensagem vive apenas em memoria. Se o + * processo cair nessa janela, o recibo duravel ja marcou o sink `chatbot` como + * entregue e nao havera replay. E exatamente o mesmo risco do upstream (que + * chamava o emit sem await), agora consciente e documentado. A alternativa + * at-least-once estrita seria persistir o proprio buffer no recibo; ficou como + * nota, nao como padrao. + */ -type DebounceWaiter = { - resolve: () => void; - reject: (error: unknown) => void; +export type ChatbotDebounceMetadata = { + /** Quantas mensagens do lead foram coalescidas neste buffer. */ + messageCount: number; + /** `messageTimestamp` (epoch em segundos) da primeira mensagem do buffer. */ + firstMessageTimestamp?: number; + /** `messageTimestamp` (epoch em segundos) da ultima mensagem do buffer. */ + lastMessageTimestamp?: number; }; +export type ChatbotDebounceCallback = (content: string, metadata: ChatbotDebounceMetadata) => Promise; + type ChatbotDebounceEntry = { message: string; timeoutId: NodeJS.Timeout | null; callback: ChatbotDebounceCallback; - waiters: DebounceWaiter[]; + metadata: ChatbotDebounceMetadata; + flushed: Promise; + resolveFlushed: () => void; + rejectFlushed: (error: unknown) => void; }; export type ChatbotDebounceStore = Record; +export type ChatbotDebounceOptions = { + /** `messageTimestamp` normalizado da mensagem que esta sendo aceita. */ + messageTimestamp?: unknown; + onMerged?: (content: string) => void; + onFlushed?: (content: string) => void; + /** Unico lugar onde uma falha do flush destacado fica visivel. */ + onFlushError?: (error: unknown) => void; +}; + +export type ChatbotDebounceAcceptance = { + /** `true` quando a mensagem entrou num buffer que ja existia. */ + merged: boolean; + /** Snapshot dos metadados no instante da aceitacao. */ + metadata: ChatbotDebounceMetadata; + /** + * Resolve quando o flush deste buffer terminou; rejeita com o erro do + * callback. NAO deve ser aguardada no caminho de entrega da instancia — o + * ponto do #518 e justamente nao bloquear a fila. Existe para teste e + * diagnostico. Ja vem com um `catch` interno, entao ignora-la nunca vira + * `unhandledRejection`. + */ + flushed: Promise; +}; + export function buildChatbotDebounceKey(instanceName: string, remoteJid: string): string { return JSON.stringify([instanceName, remoteJid]); } +/** + * O `messageTimestamp` do Baileys chega como number, string ou Long. Só + * aceitamos o que vira um numero finito; qualquer outra coisa fica `undefined` + * em vez de virar `NaN` no payload do n8n. + */ +export function normalizeMessageTimestamp(value: unknown): number | undefined { + if (typeof value === 'number') return Number.isFinite(value) ? value : undefined; + if (typeof value === 'string' && value.trim() !== '') { + const parsed = Number(value); + return Number.isFinite(parsed) ? parsed : undefined; + } + if (typeof value === 'bigint') return Number(value); + if (value && typeof (value as { toNumber?: () => number }).toNumber === 'function') { + const parsed = (value as { toNumber: () => number }).toNumber(); + return Number.isFinite(parsed) ? parsed : undefined; + } + return undefined; +} + export function processChatbotDebounce( store: ChatbotDebounceStore, content: string, debounceKey: string, debounceTime: number, callback: ChatbotDebounceCallback, - onMerged?: (content: string) => void, - onFlushed?: (content: string) => void, -): Promise { - return new Promise((resolve, reject) => { - const current = store[debounceKey]; - if (current) { - current.message += `\n${content}`; - current.waiters.push({ resolve, reject }); - current.callback = callback; - onMerged?.(current.message); - if (current.timeoutId) clearTimeout(current.timeoutId); - } else { - store[debounceKey] = { - message: content, - timeoutId: null, - callback, - waiters: [{ resolve, reject }], - }; - } + options: ChatbotDebounceOptions = {}, +): ChatbotDebounceAcceptance { + const timestamp = normalizeMessageTimestamp(options.messageTimestamp); + const existing = store[debounceKey]; + const merged = Boolean(existing); + let entry: ChatbotDebounceEntry; - store[debounceKey].timeoutId = setTimeout(async () => { - const entry = store[debounceKey]; - delete store[debounceKey]; - onFlushed?.(entry.message); - try { - await entry.callback(entry.message); - entry.waiters.forEach((waiter) => waiter.resolve()); - } catch (error) { - entry.waiters.forEach((waiter) => waiter.reject(error)); + if (existing) { + existing.message += `\n${content}`; + existing.callback = callback; + existing.metadata.messageCount += 1; + if (timestamp !== undefined) { + if (existing.metadata.firstMessageTimestamp === undefined) { + existing.metadata.firstMessageTimestamp = timestamp; } - }, debounceTime * 1000); - }); + existing.metadata.lastMessageTimestamp = timestamp; + } + options.onMerged?.(existing.message); + if (existing.timeoutId) clearTimeout(existing.timeoutId); + entry = existing; + } else { + let resolveFlushed!: () => void; + let rejectFlushed!: (error: unknown) => void; + const flushed = new Promise((resolve, reject) => { + resolveFlushed = resolve; + rejectFlushed = reject; + }); + // O chamador nao e obrigado a observar o flush; sem este catch uma falha do + // POST ao n8n derrubaria o processo por unhandledRejection. + flushed.catch(() => undefined); + entry = { + message: content, + timeoutId: null, + callback, + metadata: { + messageCount: 1, + firstMessageTimestamp: timestamp, + lastMessageTimestamp: timestamp, + }, + flushed, + resolveFlushed, + rejectFlushed, + }; + store[debounceKey] = entry; + } + + const scheduled = entry; + scheduled.timeoutId = setTimeout(() => { + // Um flush so pode consumir o buffer que ele agendou. Sem esta guarda, um + // timer antigo poderia despachar um ciclo novo criado depois do delete. + if (store[debounceKey] !== scheduled) return; + delete store[debounceKey]; + options.onFlushed?.(scheduled.message); + const snapshot: ChatbotDebounceMetadata = { ...scheduled.metadata }; + void Promise.resolve() + .then(() => scheduled.callback(scheduled.message, snapshot)) + .then( + () => scheduled.resolveFlushed(), + (error) => { + options.onFlushError?.(error); + scheduled.rejectFlushed(error); + }, + ); + }, debounceTime * 1000); + + return { merged, metadata: { ...scheduled.metadata }, flushed: scheduled.flushed }; } diff --git a/src/api/integrations/chatbot/n8n/services/n8n.service.ts b/src/api/integrations/chatbot/n8n/services/n8n.service.ts index 6fbeb2e6ed..6d334e9641 100644 --- a/src/api/integrations/chatbot/n8n/services/n8n.service.ts +++ b/src/api/integrations/chatbot/n8n/services/n8n.service.ts @@ -5,6 +5,7 @@ import { IntegrationSession, N8n, N8nSetting } from '@prisma/client'; import axios from 'axios'; import { BaseChatbotService } from '../../base-chatbot.service'; +import { ChatbotDebounceMetadata, normalizeMessageTimestamp } from '../../chatbotDebounce'; import { OpenaiService } from '../../openai/services/openai.service'; export class N8nService extends BaseChatbotService { @@ -48,6 +49,13 @@ export class N8nService extends BaseChatbotService { } const endpoint: string = n8n.webhookUrl; + // XMACNA_DEBOUNCE_DETACHED_518: `msg` e sempre a ULTIMA mensagem coalescida + // (o callback do buffer e substituido a cada merge), entao `keyId` e + // `messageTimestamp` ja descrevem o fim da janela. Os metadados do buffer + // trazem o inicio e o tamanho, para o workflow medir a defasagem sem + // adivinhar. Sem debounce, os tres campos descrevem uma mensagem so. + const debounceMetadata = msg?.debounceMetadata as ChatbotDebounceMetadata | undefined; + const lastMessageTimestamp = normalizeMessageTimestamp(msg?.messageTimestamp); const payload: any = { chatInput: content, sessionId: session.sessionId, @@ -55,6 +63,9 @@ export class N8nService extends BaseChatbotService { pushName: pushName, keyId: msg?.key?.id, fromMe: msg?.key?.fromMe, + messageCount: debounceMetadata?.messageCount ?? 1, + firstMessageTimestamp: debounceMetadata?.firstMessageTimestamp ?? lastMessageTimestamp, + lastMessageTimestamp: debounceMetadata?.lastMessageTimestamp ?? lastMessageTimestamp, quotedMessage: msg?.contextInfo?.quotedMessage, instanceName: instance.instanceName, serverUrl: this.configService.get('SERVER').URL, diff --git a/test/xmacna-chatbot-debounce.test.ts b/test/xmacna-chatbot-debounce.test.ts index 0ce2bbb551..71ee0593d1 100644 --- a/test/xmacna-chatbot-debounce.test.ts +++ b/test/xmacna-chatbot-debounce.test.ts @@ -3,35 +3,41 @@ import test from 'node:test'; import { buildChatbotDebounceKey, + ChatbotDebounceMetadata, ChatbotDebounceStore, + normalizeMessageTimestamp, processChatbotDebounce, } from '../src/api/integrations/chatbot/chatbotDebounce'; +const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); + test('isolates the same remoteJid across instances', async () => { const store: ChatbotDebounceStore = {}; const delivered: Array<{ instance: string; content: string }> = []; const remoteJid = '5511999999999@s.whatsapp.net'; - await Promise.all([ - processChatbotDebounce( - store, - 'message for instance A', - buildChatbotDebounceKey('instance-a', remoteJid), - 0.01, - async (content) => { - delivered.push({ instance: 'instance-a', content }); - }, - ), - processChatbotDebounce( - store, - 'message for instance B', - buildChatbotDebounceKey('instance-b', remoteJid), - 0.01, - async (content) => { - delivered.push({ instance: 'instance-b', content }); - }, - ), - ]); + await Promise.all( + [ + processChatbotDebounce( + store, + 'message for instance A', + buildChatbotDebounceKey('instance-a', remoteJid), + 0.01, + async (content) => { + delivered.push({ instance: 'instance-a', content }); + }, + ), + processChatbotDebounce( + store, + 'message for instance B', + buildChatbotDebounceKey('instance-b', remoteJid), + 0.01, + async (content) => { + delivered.push({ instance: 'instance-b', content }); + }, + ), + ].map((acceptance) => acceptance.flushed), + ); assert.deepEqual( delivered.sort((left, right) => left.instance.localeCompare(right.instance)), @@ -52,32 +58,174 @@ test('still coalesces consecutive messages within one instance', async () => { const delivered: string[] = []; const debounceKey = buildChatbotDebounceKey('instance-a', '5511999999999@s.whatsapp.net'); - await Promise.all([ - processChatbotDebounce(store, 'first', debounceKey, 0.01, async (content) => delivered.push(content)), - processChatbotDebounce(store, 'second', debounceKey, 0.01, async (content) => delivered.push(content)), - ]); + const first = processChatbotDebounce(store, 'first', debounceKey, 0.01, async (content) => + void delivered.push(content), + ); + const second = processChatbotDebounce(store, 'second', debounceKey, 0.01, async (content) => + void delivered.push(content), + ); + + assert.equal(first.merged, false); + assert.equal(second.merged, true); + + await Promise.all([first.flushed, second.flushed]); assert.deepEqual(delivered, ['first\nsecond']); assert.deepEqual(store, {}); }); -test('propagates one callback failure to every coalesced waiter', async () => { +test('reports one callback failure on every coalesced acceptance receipt', async () => { const store: ChatbotDebounceStore = {}; const debounceKey = buildChatbotDebounceKey('instance-a', '5511999999999@s.whatsapp.net'); const failure = new Error('n8n unavailable'); + const reported: unknown[] = []; - const results = await Promise.allSettled([ + const acceptances = [ processChatbotDebounce(store, 'first', debounceKey, 0.01, async () => { throw failure; }), - processChatbotDebounce(store, 'second', debounceKey, 0.01, async () => { - throw failure; - }), - ]); + processChatbotDebounce( + store, + 'second', + debounceKey, + 0.01, + async () => { + throw failure; + }, + { onFlushError: (error) => void reported.push(error) }, + ), + ]; + + const results = await Promise.allSettled(acceptances.map((acceptance) => acceptance.flushed)); assert.deepEqual( results.map((result) => (result.status === 'rejected' ? result.reason : null)), [failure, failure], ); + // A falha do flush destacado precisa ter um lugar proprio para aparecer: o + // chamador na fila da instancia ja seguiu em frente. + assert.deepEqual(reported, [failure]); assert.deepEqual(store, {}); }); + +// xmacna/elysium#518 — Reproducao da chegada SERIALIZADA. +// +// `BaileysMessageProcessor` mantem uma unica cadeia de Promise por instancia +// (`this.queue = this.queue.then(...)`), e o sink duravel `chatbot` faz +// `await chatbotController.emitDurableInbound(...)`. Logo a 2a mensagem do lead +// so comeca a ser processada depois que o `emit` da 1a resolveu. Enquanto o +// debounce so resolvia no flush, isso significava (a) nunca coalescer e +// (b) prender a instancia por `debounceTime + tempo do workflow` por mensagem. +// +// Diferente dos testes acima, aqui NAO ha `Promise.all`: e exatamente o que a +// fila faz em producao. +test('coalesces messages that arrive serialized through the Baileys queue (#518)', async () => { + const store: ChatbotDebounceStore = {}; + const delivered: string[] = []; + const debounceKey = buildChatbotDebounceKey('instance-a', '5511999999999@s.whatsapp.net'); + const debounceSeconds = 0.2; + + const enqueue = async (content: string) => { + await processChatbotDebounce(store, content, debounceKey, debounceSeconds, async (merged) => { + delivered.push(merged); + }); + }; + + const startedAt = Date.now(); + await enqueue('primeira'); + const acceptanceMs = Date.now() - startedAt; + await enqueue('segunda'); + await enqueue('terceira'); + + // A instancia nao pode ficar bloqueada por uma janela de debounce por mensagem. + assert.ok( + acceptanceMs < debounceSeconds * 1000, + `aceitacao no buffer levou ${acceptanceMs}ms (janela de ${debounceSeconds * 1000}ms): a fila da instancia ficou presa`, + ); + + await sleep(debounceSeconds * 1000 + 200); + + assert.deepEqual(delivered, ['primeira\nsegunda\nterceira']); + assert.deepEqual(store, {}); +}); + +test('a slow flush never blocks the next message of the same lead (#518)', async () => { + const store: ChatbotDebounceStore = {}; + const debounceKey = buildChatbotDebounceKey('instance-a', '5511999999999@s.whatsapp.net'); + let flushStarted = 0; + + let firstFlushFinished = false; + + // Workflow lento: 300ms depois do flush. Antes do #518 isso somava ao lock. + const first = processChatbotDebounce(store, 'primeira', debounceKey, 0.05, async () => { + flushStarted += 1; + await sleep(300); + firstFlushFinished = true; + }); + + // Espera a janela fechar e o flush comecar, mas NAO terminar. + await sleep(100); + assert.equal(flushStarted, 1); + assert.equal(firstFlushFinished, false); + + // Enquanto o workflow da 1a ainda corre, a 2a precisa ser aceita na hora e + // abrir um buffer novo (a 1a ja foi despachada e saiu do store). + const startedAt = Date.now(); + const second = processChatbotDebounce(store, 'segunda', debounceKey, 0.05, async () => undefined); + assert.ok(Date.now() - startedAt < 50); + assert.equal(second.merged, false); + + await second.flushed; + assert.equal(firstFlushFinished, false, 'o flush da 2a nao pode esperar o workflow da 1a'); + await first.flushed; + assert.equal(firstFlushFinished, true); + assert.deepEqual(store, {}); +}); + +test('accumulates debounce metadata for the n8n payload (#518)', async () => { + const store: ChatbotDebounceStore = {}; + const debounceKey = buildChatbotDebounceKey('instance-a', '5511999999999@s.whatsapp.net'); + let seen: ChatbotDebounceMetadata | undefined; + + const callback = async (_content: string, metadata: ChatbotDebounceMetadata) => { + seen = metadata; + }; + + const first = processChatbotDebounce(store, 'primeira', debounceKey, 0.05, callback, { + messageTimestamp: 1757600000, + }); + assert.deepEqual(first.metadata, { + messageCount: 1, + firstMessageTimestamp: 1757600000, + lastMessageTimestamp: 1757600000, + }); + + processChatbotDebounce(store, 'segunda', debounceKey, 0.05, callback, { messageTimestamp: '1757600007' }); + const third = processChatbotDebounce(store, 'terceira', debounceKey, 0.05, callback, { + messageTimestamp: 1757600014, + }); + + assert.deepEqual(third.metadata, { + messageCount: 3, + firstMessageTimestamp: 1757600000, + lastMessageTimestamp: 1757600014, + }); + + await third.flushed; + + assert.deepEqual(seen, { + messageCount: 3, + firstMessageTimestamp: 1757600000, + lastMessageTimestamp: 1757600014, + }); +}); + +test('normalizes the Baileys messageTimestamp shapes instead of leaking NaN', () => { + assert.equal(normalizeMessageTimestamp(1757600000), 1757600000); + assert.equal(normalizeMessageTimestamp('1757600000'), 1757600000); + assert.equal(normalizeMessageTimestamp({ toNumber: () => 1757600000 }), 1757600000); + assert.equal(normalizeMessageTimestamp(undefined), undefined); + assert.equal(normalizeMessageTimestamp(''), undefined); + assert.equal(normalizeMessageTimestamp('nao-e-numero'), undefined); + assert.equal(normalizeMessageTimestamp(Number.NaN), undefined); +}); From 0697d4a0cf67d0a5bdde62913667d430851189ef Mon Sep 17 00:00:00 2001 From: raffareis Date: Fri, 11 Sep 2026 22:55:06 -0300 Subject: [PATCH 18/19] fix(deps): remediate runtime audit except minio stream-json (xmacna/elysium#518) - multer ^2.3.0 and sharp ^0.35.4 (npm audit fix, in-range). - body-parser 1.20.8 (in express 4 range) and qs override 6.16.0: express 4.22.2 pins qs ~6.15.1, the only patched qs is 6.16.0 (semver-minor). - deepmerge-ts override 8.0.0 for @prisma/config: the 8.0 break only touches deepmergeInto; prisma config uses deepmerge. - decode-uri-component override 0.5.0 under minio's query-string 7: the ESM-only fix breaks query-string.parse, but minio only calls stringify; the compat test pins that contract. - Compat tests exercise express urlencoded, multer memoryStorage, MinIO presign and deepmerge with the overridden versions. Residual: stream-json <=3.4.0 (via minio 8.0.7). The only patched release (3.5.0) drops jsonl/Parser.js, which minio requires at import. --- package-lock.json | 286 ++++++++++++++-------------- package.json | 9 +- test/xmacna-baileys-compat.test.cjs | 120 ++++++++++++ 3 files changed, 274 insertions(+), 141 deletions(-) diff --git a/package-lock.json b/package-lock.json index 423ab8012f..e6b64b1ae1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47,7 +47,7 @@ "mime": "^4.0.0", "mime-types": "^2.1.35", "minio": "^8.0.7", - "multer": "^2.2.0", + "multer": "^2.3.0", "nats": "^2.29.1", "node-cache": "^5.1.2", "node-cron": "^4.6.0", @@ -60,7 +60,7 @@ "qrcode-terminal": "^0.12.0", "redis": "^4.7.0", "rxjs": "^7.8.2", - "sharp": "^0.35.3", + "sharp": "^0.35.4", "socket.io": "^4.8.1", "socket.io-client": "^4.8.1", "socks-proxy-agent": "^8.0.5", @@ -1592,9 +1592,9 @@ } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.3.tgz", - "integrity": "sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.4.tgz", + "integrity": "sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA==", "cpu": [ "arm64" ], @@ -1610,13 +1610,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.3.2" + "@img/sharp-libvips-darwin-arm64": "1.3.3" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.3.tgz", - "integrity": "sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.4.tgz", + "integrity": "sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==", "cpu": [ "x64" ], @@ -1632,20 +1632,20 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.3.2" + "@img/sharp-libvips-darwin-x64": "1.3.3" } }, "node_modules/@img/sharp-freebsd-wasm32": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.3.tgz", - "integrity": "sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.4.tgz", + "integrity": "sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==", "license": "Apache-2.0", "optional": true, "os": [ "freebsd" ], "dependencies": { - "@img/sharp-wasm32": "0.35.3" + "@img/sharp-wasm32": "0.35.4" }, "engines": { "node": ">=20.9.0" @@ -1655,9 +1655,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.2.tgz", - "integrity": "sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.3.tgz", + "integrity": "sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg==", "cpu": [ "arm64" ], @@ -1671,9 +1671,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.2.tgz", - "integrity": "sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.3.tgz", + "integrity": "sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==", "cpu": [ "x64" ], @@ -1687,9 +1687,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.2.tgz", - "integrity": "sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.3.tgz", + "integrity": "sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==", "cpu": [ "arm" ], @@ -1703,9 +1703,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.2.tgz", - "integrity": "sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.3.tgz", + "integrity": "sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==", "cpu": [ "arm64" ], @@ -1719,9 +1719,9 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.2.tgz", - "integrity": "sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.3.tgz", + "integrity": "sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==", "cpu": [ "ppc64" ], @@ -1735,9 +1735,9 @@ } }, "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.2.tgz", - "integrity": "sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.3.tgz", + "integrity": "sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==", "cpu": [ "riscv64" ], @@ -1751,9 +1751,9 @@ } }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.2.tgz", - "integrity": "sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.3.tgz", + "integrity": "sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==", "cpu": [ "s390x" ], @@ -1767,9 +1767,9 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.2.tgz", - "integrity": "sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.3.tgz", + "integrity": "sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==", "cpu": [ "x64" ], @@ -1783,9 +1783,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.2.tgz", - "integrity": "sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.3.tgz", + "integrity": "sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==", "cpu": [ "arm64" ], @@ -1799,9 +1799,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.2.tgz", - "integrity": "sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.3.tgz", + "integrity": "sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==", "cpu": [ "x64" ], @@ -1815,9 +1815,9 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.3.tgz", - "integrity": "sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.4.tgz", + "integrity": "sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==", "cpu": [ "arm" ], @@ -1833,13 +1833,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.3.2" + "@img/sharp-libvips-linux-arm": "1.3.3" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.3.tgz", - "integrity": "sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.4.tgz", + "integrity": "sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==", "cpu": [ "arm64" ], @@ -1855,13 +1855,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.3.2" + "@img/sharp-libvips-linux-arm64": "1.3.3" } }, "node_modules/@img/sharp-linux-ppc64": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.3.tgz", - "integrity": "sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.4.tgz", + "integrity": "sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==", "cpu": [ "ppc64" ], @@ -1877,13 +1877,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.3.2" + "@img/sharp-libvips-linux-ppc64": "1.3.3" } }, "node_modules/@img/sharp-linux-riscv64": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.3.tgz", - "integrity": "sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.4.tgz", + "integrity": "sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==", "cpu": [ "riscv64" ], @@ -1899,13 +1899,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.3.2" + "@img/sharp-libvips-linux-riscv64": "1.3.3" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.3.tgz", - "integrity": "sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.4.tgz", + "integrity": "sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==", "cpu": [ "s390x" ], @@ -1921,13 +1921,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.3.2" + "@img/sharp-libvips-linux-s390x": "1.3.3" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.3.tgz", - "integrity": "sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.4.tgz", + "integrity": "sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==", "cpu": [ "x64" ], @@ -1943,13 +1943,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.3.2" + "@img/sharp-libvips-linux-x64": "1.3.3" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.3.tgz", - "integrity": "sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.4.tgz", + "integrity": "sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==", "cpu": [ "arm64" ], @@ -1965,13 +1965,13 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.3.2" + "@img/sharp-libvips-linuxmusl-arm64": "1.3.3" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.3.tgz", - "integrity": "sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.4.tgz", + "integrity": "sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==", "cpu": [ "x64" ], @@ -1987,17 +1987,17 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.3.2" + "@img/sharp-libvips-linuxmusl-x64": "1.3.3" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.3.tgz", - "integrity": "sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.4.tgz", + "integrity": "sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==", "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.11.1" + "@emnapi/runtime": "^1.11.3" }, "engines": { "node": ">=20.9.0" @@ -2007,16 +2007,16 @@ } }, "node_modules/@img/sharp-webcontainers-wasm32": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.3.tgz", - "integrity": "sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.4.tgz", + "integrity": "sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==", "cpu": [ "wasm32" ], "license": "Apache-2.0", "optional": true, "dependencies": { - "@img/sharp-wasm32": "0.35.3" + "@img/sharp-wasm32": "0.35.4" }, "engines": { "node": ">=20.9.0" @@ -2026,9 +2026,9 @@ } }, "node_modules/@img/sharp-win32-arm64": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.3.tgz", - "integrity": "sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.4.tgz", + "integrity": "sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==", "cpu": [ "arm64" ], @@ -2045,9 +2045,9 @@ } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.3.tgz", - "integrity": "sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.4.tgz", + "integrity": "sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==", "cpu": [ "ia32" ], @@ -2064,9 +2064,9 @@ } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.3.tgz", - "integrity": "sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.4.tgz", + "integrity": "sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==", "cpu": [ "x64" ], @@ -4813,9 +4813,9 @@ "license": "MIT" }, "node_modules/body-parser": { - "version": "1.20.6", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.6.tgz", - "integrity": "sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==", + "version": "1.20.8", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.8.tgz", + "integrity": "sha512-JNcyFQ64OiijEkPzUBTCe+hyPXUD/3LEldGQ6iF5LR1w00mx9o7xtDWHXBY2iItjdCFGoilOLNQbH943ut7pHA==", "license": "MIT", "dependencies": { "bytes": "~3.1.2", @@ -4826,7 +4826,7 @@ "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "on-finished": "~2.4.1", - "qs": "~6.15.1", + "qs": "~6.16.0", "raw-body": "~2.5.3", "type-is": "~1.6.18", "unpipe": "~1.0.0" @@ -5930,12 +5930,12 @@ } }, "node_modules/decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.5.0.tgz", + "integrity": "sha512-1BiQVoK8C9gUbQU6NzAtO/tkz2qOFpEObMWpcFvhx4fYnj4Oc5yzaJN/LD36ihkVUdXyh5ZekzX+yM+ty/SrPg==", "license": "MIT", "engines": { - "node": ">=0.10" + "node": ">=14.16" } }, "node_modules/dedent": { @@ -5953,9 +5953,19 @@ "license": "MIT" }, "node_modules/deepmerge-ts": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", - "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-8.0.0.tgz", + "integrity": "sha512-ICNjaP0ML+eSdEpJYQC46XiAn/UjAdwbEl0dE8p85ZTeNDinN4Kd4+9jS4OSAuH7st6eC7rQhsqTF5zIDaUm2g==", + "funding": [ + { + "type": "ko-fi", + "url": "https://ko-fi.com/rebeccastevens" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/deepmerge-ts" + } + ], "license": "BSD-3-Clause", "engines": { "node": ">=16.0.0" @@ -10498,9 +10508,9 @@ "license": "MIT" }, "node_modules/multer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/multer/-/multer-2.2.0.tgz", - "integrity": "sha512-6rdyFg2kLrMh9Jee7/BMPuV9lEAd7lLW2YUpF9/YxR7njyoUwwQ0ZPh3TaIY50Sw6vlyD2HW3wGOkTS4P79xrQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.3.0.tgz", + "integrity": "sha512-cjNbm3sttszgZeGfJR124D+jFEfkXCVAsoPBmFn9X7UxmDSFHWqE2CoEj0vrmSpuAFnqWR1Szcm9QTsiHr60Xw==", "license": "MIT", "dependencies": { "append-field": "^1.0.0", @@ -12052,9 +12062,9 @@ } }, "node_modules/qs": { - "version": "6.15.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", - "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz", + "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==", "license": "BSD-3-Clause", "dependencies": { "es-define-property": "^1.0.1", @@ -12724,9 +12734,9 @@ "license": "ISC" }, "node_modules/sharp": { - "version": "0.35.3", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.3.tgz", - "integrity": "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==", + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.4.tgz", + "integrity": "sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==", "license": "Apache-2.0", "peer": true, "dependencies": { @@ -12741,31 +12751,31 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.35.3", - "@img/sharp-darwin-x64": "0.35.3", - "@img/sharp-freebsd-wasm32": "0.35.3", - "@img/sharp-libvips-darwin-arm64": "1.3.2", - "@img/sharp-libvips-darwin-x64": "1.3.2", - "@img/sharp-libvips-linux-arm": "1.3.2", - "@img/sharp-libvips-linux-arm64": "1.3.2", - "@img/sharp-libvips-linux-ppc64": "1.3.2", - "@img/sharp-libvips-linux-riscv64": "1.3.2", - "@img/sharp-libvips-linux-s390x": "1.3.2", - "@img/sharp-libvips-linux-x64": "1.3.2", - "@img/sharp-libvips-linuxmusl-arm64": "1.3.2", - "@img/sharp-libvips-linuxmusl-x64": "1.3.2", - "@img/sharp-linux-arm": "0.35.3", - "@img/sharp-linux-arm64": "0.35.3", - "@img/sharp-linux-ppc64": "0.35.3", - "@img/sharp-linux-riscv64": "0.35.3", - "@img/sharp-linux-s390x": "0.35.3", - "@img/sharp-linux-x64": "0.35.3", - "@img/sharp-linuxmusl-arm64": "0.35.3", - "@img/sharp-linuxmusl-x64": "0.35.3", - "@img/sharp-webcontainers-wasm32": "0.35.3", - "@img/sharp-win32-arm64": "0.35.3", - "@img/sharp-win32-ia32": "0.35.3", - "@img/sharp-win32-x64": "0.35.3" + "@img/sharp-darwin-arm64": "0.35.4", + "@img/sharp-darwin-x64": "0.35.4", + "@img/sharp-freebsd-wasm32": "0.35.4", + "@img/sharp-libvips-darwin-arm64": "1.3.3", + "@img/sharp-libvips-darwin-x64": "1.3.3", + "@img/sharp-libvips-linux-arm": "1.3.3", + "@img/sharp-libvips-linux-arm64": "1.3.3", + "@img/sharp-libvips-linux-ppc64": "1.3.3", + "@img/sharp-libvips-linux-riscv64": "1.3.3", + "@img/sharp-libvips-linux-s390x": "1.3.3", + "@img/sharp-libvips-linux-x64": "1.3.3", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.3", + "@img/sharp-libvips-linuxmusl-x64": "1.3.3", + "@img/sharp-linux-arm": "0.35.4", + "@img/sharp-linux-arm64": "0.35.4", + "@img/sharp-linux-ppc64": "0.35.4", + "@img/sharp-linux-riscv64": "0.35.4", + "@img/sharp-linux-s390x": "0.35.4", + "@img/sharp-linux-x64": "0.35.4", + "@img/sharp-linuxmusl-arm64": "0.35.4", + "@img/sharp-linuxmusl-x64": "0.35.4", + "@img/sharp-webcontainers-wasm32": "0.35.4", + "@img/sharp-win32-arm64": "0.35.4", + "@img/sharp-win32-ia32": "0.35.4", + "@img/sharp-win32-x64": "0.35.4" }, "peerDependenciesMeta": { "@types/node": { diff --git a/package.json b/package.json index b22d78b7f4..feed7e0b17 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,7 @@ "mime": "^4.0.0", "mime-types": "^2.1.35", "minio": "^8.0.7", - "multer": "^2.2.0", + "multer": "^2.3.0", "nats": "^2.29.1", "node-cache": "^5.1.2", "node-cron": "^4.6.0", @@ -123,7 +123,7 @@ "qrcode-terminal": "^0.12.0", "redis": "^4.7.0", "rxjs": "^7.8.2", - "sharp": "^0.35.3", + "sharp": "^0.35.4", "socket.io": "^4.8.1", "socket.io-client": "^4.8.1", "socks-proxy-agent": "^8.0.5", @@ -137,7 +137,10 @@ "link-preview-js": "$link-preview-js", "@aws-sdk/xml-builder": { "fast-xml-parser": "5.10.1" - } + }, + "qs": "6.16.0", + "decode-uri-component": "0.5.0", + "deepmerge-ts": "8.0.0" }, "devDependencies": { "@commitlint/cli": "^19.8.1", diff --git a/test/xmacna-baileys-compat.test.cjs b/test/xmacna-baileys-compat.test.cjs index a68c92b9ff..26f793b44e 100644 --- a/test/xmacna-baileys-compat.test.cjs +++ b/test/xmacna-baileys-compat.test.cjs @@ -120,6 +120,126 @@ test('security overrides resolve to the audited runtime versions', () => { const baileysRoot = require.resolve('baileys'); const baileysLinkPreviewPackage = require.resolve('link-preview-js/package.json', { paths: [baileysRoot] }); assert.equal(require(baileysLinkPreviewPackage).version, '5.0.0'); + + assert.equal(installedPackage('multer').version, '2.3.0'); + assert.equal(installedPackage('sharp').version, '0.35.4'); + + const express = installedPackage('express'); + const bodyParser = installedPackage('body-parser', express.dir); + assert.equal(installedPackage('qs', express.dir).version, '6.16.0'); + assert.equal(installedPackage('qs', bodyParser.dir).version, '6.16.0'); + + const queryString = installedPackage('query-string', installedPackage('minio').dir); + assert.equal(installedPackage('decode-uri-component', queryString.dir).version, '0.5.0'); + + const prismaConfig = installedPackage('@prisma/config', installedPackage('prisma').dir); + assert.equal(installedPackage('deepmerge-ts', prismaConfig.dir).version, '8.0.0'); +}); + +// Several audited packages do not export ./package.json, so resolve the installed copy the way +// Node does for a parent package: walk node_modules upwards from the parent's directory. +function installedPackage(name, fromDir = root) { + for (let dir = fromDir; ; dir = path.dirname(dir)) { + const packageDir = path.join(dir, 'node_modules', name); + const manifest = path.join(packageDir, 'package.json'); + if (fs.existsSync(manifest)) return { dir: packageDir, version: JSON.parse(fs.readFileSync(manifest, 'utf8')).version }; + if (path.dirname(dir) === dir) throw new Error(`${name} is not installed above ${fromDir}`); + } +} + +async function withExpressServer(t, configure) { + const express = require('express'); + const app = express(); + configure(app); + const server = await new Promise((resolve) => { + const listening = app.listen(0, '127.0.0.1', () => resolve(listening)); + }); + t.after(() => server.close()); + return `http://127.0.0.1:${server.address().port}`; +} + +test('Express 4 urlencoded parsing keeps nested bodies with the qs override', async (t) => { + const express = require('express'); + const baseUrl = await withExpressServer(t, (app) => { + app.use(express.urlencoded({ extended: true, limit: '136mb' })); + app.post('/form', (request, response) => response.json(request.body)); + }); + + const response = await fetch(`${baseUrl}/form`, { + method: 'POST', + headers: { 'content-type': 'application/x-www-form-urlencoded' }, + body: 'number=5511999999999&options[delay]=1200&list[0]=a&list[1]=b', + }); + + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + number: '5511999999999', + options: { delay: '1200' }, + list: ['a', 'b'], + }); +}); + +test('multer memoryStorage upload remains compatible after the security update', async (t) => { + const multer = require('multer'); + const upload = multer({ storage: multer.memoryStorage() }); + const baseUrl = await withExpressServer(t, (app) => { + app.post('/media', upload.single('file'), (request, response) => { + response.json({ + number: request.body.number, + name: request.file.originalname, + mimetype: request.file.mimetype, + content: request.file.buffer.toString('utf8'), + }); + }); + }); + + const form = new FormData(); + form.append('number', '5511999999999'); + form.append('file', new Blob(['inbox-probe'], { type: 'text/plain' }), 'probe.txt'); + const response = await fetch(`${baseUrl}/media`, { method: 'POST', body: form }); + + assert.equal(response.status, 200); + assert.deepEqual(await response.json(), { + number: '5511999999999', + name: 'probe.txt', + mimetype: 'text/plain', + content: 'inbox-probe', + }); +}); + +test('MinIO client loads and signs requests with the decode-uri-component override', async () => { + // query-string 7 is CommonJS and decode-uri-component 0.5.0 is ESM-only, so query-string.parse + // cannot decode under this override. MinIO only calls query-string.stringify; this test pins + // that contract so a MinIO update that starts parsing query strings fails here, not in runtime. + const minioSources = ['node_modules/minio/dist/main/internal/client.js', 'node_modules/minio/dist/main/helpers.js']; + for (const source of minioSources) { + const calls = read(source).match(/\b(?:qs|querystring)\.[A-Za-z]+/g) ?? []; + assert.ok(calls.length > 0, `${source} no longer references query-string`); + assert.deepEqual([...new Set(calls.map((call) => call.split('.')[1]))], ['stringify'], source); + } + + const MinIo = require('minio'); + const client = new MinIo.Client({ + endPoint: '127.0.0.1', + port: 9, + useSSL: false, + accessKey: 'probe-access', + secretKey: 'probe-secret', + region: 'us-east-1', + }); + const url = new URL(await client.presignedGetObject('evolution', 'media/probe file.jpg', 60)); + assert.equal(url.pathname, '/evolution/media/probe%20file.jpg'); + assert.equal(url.searchParams.get('X-Amz-Expires'), '60'); + assert.match(url.searchParams.get('X-Amz-Signature'), /^[0-9a-f]{64}$/); +}); + +test('Prisma config deepmerge keeps plain-object merge semantics on deepmerge-ts 8', async () => { + const prismaConfigRoot = require.resolve('@prisma/config', { paths: [require.resolve('prisma/package.json')] }); + const { deepmerge } = await import(require.resolve('deepmerge-ts', { paths: [prismaConfigRoot] })); + assert.deepEqual(deepmerge({ migrations: { path: 'a' }, earlyAccess: false }, { migrations: { seed: 'b' } }), { + migrations: { path: 'a', seed: 'b' }, + earlyAccess: false, + }); }); test('Chatwoot SDK request remains compatible with the Axios override', async (t) => { From 9f9f70326e2fb524c98735398ba01e02acc4d1a3 Mon Sep 17 00:00:00 2001 From: raffareis Date: Mon, 14 Sep 2026 18:01:59 -0300 Subject: [PATCH 19/19] fix(storage): replace minio with @aws-sdk/client-s3 so the runtime audit is clean (xmacna/elysium#518) minio 8.0.7 pins stream-json <=3.4.0; the only patched release (3.5.0) drops jsonl/Parser.js and breaks minio at import, so `npm audit --omit=dev` could never be green and the inbox candidate build was blocked. The S3 wrapper (libs/minio.server.ts, name kept so the 9 importers stay untouched) now uses @aws-sdk/client-s3 + s3-request-presigner (same SDK family as @aws-sdk/client-sqs already shipped): - same exported contract: BUCKET, uploadFile, uploadTempFile, getObjectUrl, deleteFile; no-op when S3_ENABLED != true; upload/delete errors logged and returned, never thrown (caller contract); - object keys unchanged (evolution-api/); Content-Type as header, the rest as x-amz-meta; presign default 7 days (minio's default, SigV4 max); - virtual-host style on *.amazonaws.com, path style elsewhere (minio parity); - bucket bootstrap kept (HeadBucket -> CreateBucket -> public-read policy unless S3_SKIP_POLICY). Removed the decode-uri-component override (only existed for minio's query-string). Compat test now pins "minio not installed" and presigns through the SDK. New test/xmacna-s3-storage.test.ts (7 cases, injectable client) added to test:xmacna-inbox. Gates: tsc OK, eslint OK, build OK, test:xmacna-compat 14/14, test:xmacna-inbox 37/37, npm audit --omit=dev --audit-level=low: 0 vulnerabilities. --- package-lock.json | 618 ++++++------------ package.json | 8 +- .../storage/s3/libs/minio.server.ts | 297 +++++---- test/xmacna-baileys-compat.test.cjs | 40 +- test/xmacna-s3-storage.test.ts | 128 ++++ 5 files changed, 545 insertions(+), 546 deletions(-) create mode 100644 test/xmacna-s3-storage.test.ts diff --git a/package-lock.json b/package-lock.json index e6b64b1ae1..4dec792f62 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,9 @@ "license": "Apache-2.0", "dependencies": { "@adiwajshing/keyed-db": "^0.2.4", + "@aws-sdk/client-s3": "^3.1132.0", "@aws-sdk/client-sqs": "^3.891.0", + "@aws-sdk/s3-request-presigner": "^3.1132.0", "@ffmpeg-installer/ffmpeg": "^1.1.0", "@figuro/chatwoot-sdk": "^1.1.16", "@hapi/boom": "^10.0.1", @@ -46,7 +48,6 @@ "mediainfo.js": "^0.3.4", "mime": "^4.0.0", "mime-types": "^2.1.35", - "minio": "^8.0.7", "multer": "^2.3.0", "nats": "^2.29.1", "node-cache": "^5.1.2", @@ -152,6 +153,44 @@ "module-details-from-path": "^1.0.4" } }, + "node_modules/@aws-sdk/checksums": { + "version": "3.1001.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/checksums/-/checksums-3.1001.0.tgz", + "integrity": "sha512-6uTniZc87q+B5eXouGTl+7Tmc482rEeCcvxpsvREP8EfF0gvloRZ41UOA9sbSJlyy8TbqIBXb3kKfKarEArUQA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.978.0", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-s3": { + "version": "3.1132.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.1132.0.tgz", + "integrity": "sha512-ccS2utR3YpKE1KEkAlo1Oq9kUhzy/IOIbpwHAcENn/JzpzCj9l79I2o6stTzXGOKbL8+XEdH2P7c2Znp8PbBlQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/checksums": "^3.1001.0", + "@aws-sdk/core": "^3.978.0", + "@aws-sdk/credential-provider-node": "^3.972.83", + "@aws-sdk/middleware-sdk-s3": "^3.972.76", + "@aws-sdk/signature-v4-multi-region": "^3.996.46", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/fetch-http-handler": "^5.7.2", + "@smithy/node-http-handler": "^4.11.3", + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/@aws-sdk/client-sqs": { "version": "3.1109.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sqs/-/client-sqs-3.1109.0.tgz", @@ -173,17 +212,17 @@ } }, "node_modules/@aws-sdk/core": { - "version": "3.977.7", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.977.7.tgz", - "integrity": "sha512-I88Iov89NVmjSmJLKSv7Cn9M2J+a2942OkA8nZCbz+sl4ZeY4zEOcoLOrbt1GRfQ8zEQKnjAJdXixA3J/p1fDQ==", + "version": "3.978.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.978.0.tgz", + "integrity": "sha512-2yX9LUmxPklVjSGTb8dfnWRJSiFQ3TeH2nn7G1mdKHTfnabzF0+gfrS8rYfLWmZrQ8A3mEcxMJjRc51dL5KWaA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.974.3", - "@aws-sdk/xml-builder": "^3.972.38", + "@aws-sdk/types": "^3.974.5", + "@aws-sdk/xml-builder": "^3.972.40", "@aws/lambda-invoke-store": "^0.3.0", - "@smithy/core": "^3.31.1", + "@smithy/core": "^3.33.3", "@smithy/signature-v4": "^5.6.12", - "@smithy/types": "^4.16.1", + "@smithy/types": "^4.17.2", "bowser": "^2.11.0", "tslib": "^2.6.2" }, @@ -192,15 +231,15 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.972.68", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.68.tgz", - "integrity": "sha512-2a20A/IdNOwUvaDq91iqqS7BA0XlNMfW3iLGZGZLJv0EbUqhSxB0PIx4rQQqssvWj1uXImb3/UCCdHz/+1dOiA==", + "version": "3.972.71", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.71.tgz", + "integrity": "sha512-JN+JHruYZw3GUZB8YGAlDk4wTDPOEAEEdEzj5nS0xodWR4smzHsN7PnK2j6IeOsDIj2aqua5DSbhXl9Gtf90FQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.977.7", - "@aws-sdk/types": "^3.974.3", - "@smithy/core": "^3.31.1", - "@smithy/types": "^4.16.1", + "@aws-sdk/core": "^3.978.0", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { @@ -208,17 +247,17 @@ } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.972.70", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.70.tgz", - "integrity": "sha512-0yRem2Fs52r/Nn6UAqIlpjexfaYj8ziEozOe9tamtAVT/5bzFLKx8O2r7MaRqgS3hGKHIa1Jij9nKHSsNnb04A==", + "version": "3.972.73", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.73.tgz", + "integrity": "sha512-uyYYnJOnlis8uQzaYGPd7N1JoioCoNpXgnkXYixsWJXHXgXyYi8WXJSDfofxJeWfQIGWLe2Nwyq60Uc7MZdVOg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.977.7", - "@aws-sdk/types": "^3.974.3", - "@smithy/core": "^3.31.1", - "@smithy/fetch-http-handler": "^5.6.13", - "@smithy/node-http-handler": "^4.9.13", - "@smithy/types": "^4.16.1", + "@aws-sdk/core": "^3.978.0", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/fetch-http-handler": "^5.7.2", + "@smithy/node-http-handler": "^4.11.3", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { @@ -226,23 +265,23 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.973.13", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.973.13.tgz", - "integrity": "sha512-2M39DE02XpYYaSWYk/4AsImXYUU/1L2xmTMLUpMMWq7DfLv191/vCRy3baKtdr45AkJQyVgSjmuVOLm15SwrRQ==", + "version": "3.973.16", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.973.16.tgz", + "integrity": "sha512-i++ly+0Uxa+u3ebSSyr0S/3CFhFJDxCXT3+Zj+mW2bXenEx5bKGCdTIKFu39SgXBNhWDjex/8cXUx9MUTMCrTw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.977.7", - "@aws-sdk/credential-provider-env": "^3.972.68", - "@aws-sdk/credential-provider-http": "^3.972.70", - "@aws-sdk/credential-provider-login": "^3.972.75", - "@aws-sdk/credential-provider-process": "^3.972.68", - "@aws-sdk/credential-provider-sso": "^3.973.12", - "@aws-sdk/credential-provider-web-identity": "^3.972.74", - "@aws-sdk/nested-clients": "^3.997.42", - "@aws-sdk/types": "^3.974.3", - "@smithy/core": "^3.31.1", + "@aws-sdk/core": "^3.978.0", + "@aws-sdk/credential-provider-env": "^3.972.71", + "@aws-sdk/credential-provider-http": "^3.972.73", + "@aws-sdk/credential-provider-login": "^3.972.78", + "@aws-sdk/credential-provider-process": "^3.972.71", + "@aws-sdk/credential-provider-sso": "^3.973.15", + "@aws-sdk/credential-provider-web-identity": "^3.972.77", + "@aws-sdk/nested-clients": "^3.997.45", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", "@smithy/credential-provider-imds": "^4.4.16", - "@smithy/types": "^4.16.1", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { @@ -250,16 +289,16 @@ } }, "node_modules/@aws-sdk/credential-provider-login": { - "version": "3.972.75", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.75.tgz", - "integrity": "sha512-jaTESuJlQsoUZ44f/i2puyPt8VlF/dMMJ9HM3cStYtk7eKX4N9UWi83OLixUkoOJH3BwWlPLCq9YIK9nfWhVBg==", + "version": "3.972.78", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.78.tgz", + "integrity": "sha512-eUtswnXu0+Ii9ieRK+0L7aPFV3Z/dnW2VntJzjBP9xs8s+8p5nBNuymIXtXwZ+5r5+XJP3e32nMkuZ/r0HozEA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.977.7", - "@aws-sdk/nested-clients": "^3.997.42", - "@aws-sdk/types": "^3.974.3", - "@smithy/core": "^3.31.1", - "@smithy/types": "^4.16.1", + "@aws-sdk/core": "^3.978.0", + "@aws-sdk/nested-clients": "^3.997.45", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { @@ -267,21 +306,21 @@ } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.972.79", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.79.tgz", - "integrity": "sha512-RIw5dof1EHkWubrZzPC941CDtnFG1iAXsxbFgLkhdYZXHc4icU13c/uxSMI0J5eUx9bxa7LjfpdjfClBB1QsDA==", + "version": "3.972.83", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.83.tgz", + "integrity": "sha512-jdso7ejzfRnatxMUZK4S/U6KbaDPCvfIV4XL+IQAPFDBt5rj5Fq595euqlK8Le4lNCMFR9oUpt+1l0aMgaayOQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "^3.972.68", - "@aws-sdk/credential-provider-http": "^3.972.70", - "@aws-sdk/credential-provider-ini": "^3.973.13", - "@aws-sdk/credential-provider-process": "^3.972.68", - "@aws-sdk/credential-provider-sso": "^3.973.12", - "@aws-sdk/credential-provider-web-identity": "^3.972.74", - "@aws-sdk/types": "^3.974.3", - "@smithy/core": "^3.31.1", + "@aws-sdk/credential-provider-env": "^3.972.71", + "@aws-sdk/credential-provider-http": "^3.972.73", + "@aws-sdk/credential-provider-ini": "^3.973.16", + "@aws-sdk/credential-provider-process": "^3.972.71", + "@aws-sdk/credential-provider-sso": "^3.973.15", + "@aws-sdk/credential-provider-web-identity": "^3.972.77", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", "@smithy/credential-provider-imds": "^4.4.16", - "@smithy/types": "^4.16.1", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { @@ -289,15 +328,15 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.972.68", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.68.tgz", - "integrity": "sha512-nLP3Pda2MQTFJ25hKBMmUuB9Uv+bTZQNlufbeCwklP549Vwnkd8bRLJoCKp5k6xjmdyptrPrOfGOhN0mKuca8A==", + "version": "3.972.71", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.71.tgz", + "integrity": "sha512-lYmXJa4gvq4xN1lrT5NiP5vIYYKcGWAdj8y+8o6dlcateB5eF3Dn8DtmjjHKfMBrTPAMr2pebIiX/UOj8c1/UA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.977.7", - "@aws-sdk/types": "^3.974.3", - "@smithy/core": "^3.31.1", - "@smithy/types": "^4.16.1", + "@aws-sdk/core": "^3.978.0", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { @@ -305,17 +344,17 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.973.12", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.973.12.tgz", - "integrity": "sha512-EmgyyHn+f9WCcelp3L/vci+LGbX8GigWaVphRArjVo5Pktkr9YnLy/mQ6VDkDyBD72dtfRNTgHmD2ts4rTDXKQ==", + "version": "3.973.15", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.973.15.tgz", + "integrity": "sha512-6Jhcf4v0pSFdjk1EW2kvzuEBKD+UZ2uNcHUIglKKLndD20YhvkL2kdmDOV5/j4mYuWWwe/a1FQ1aomU86/Cg5Q==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.977.7", - "@aws-sdk/nested-clients": "^3.997.42", - "@aws-sdk/token-providers": "3.1108.0", - "@aws-sdk/types": "^3.974.3", - "@smithy/core": "^3.31.1", - "@smithy/types": "^4.16.1", + "@aws-sdk/core": "^3.978.0", + "@aws-sdk/nested-clients": "^3.997.45", + "@aws-sdk/token-providers": "3.1129.0", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { @@ -323,16 +362,33 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.972.74", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.74.tgz", - "integrity": "sha512-0YfczxGXF3RjGj8z7QG/Ho2HnLGKDHfPSHiTs47UU1U/+mmwISDN+rvGKt2zh+3FX8NdT4xd95LGBGyhQw2dgQ==", + "version": "3.972.77", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.77.tgz", + "integrity": "sha512-uylIQSUWpfLuH2LovxEEfwzJGM/SabLOfLMg6YXu/E8jJEKUdpdILCVCQCdFvHyu/7dLJOHPMfrSwduxO56NkQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.977.7", - "@aws-sdk/nested-clients": "^3.997.42", - "@aws-sdk/types": "^3.974.3", - "@smithy/core": "^3.31.1", - "@smithy/types": "^4.16.1", + "@aws-sdk/core": "^3.978.0", + "@aws-sdk/nested-clients": "^3.997.45", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.972.76", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.76.tgz", + "integrity": "sha512-NfnTkVUTBKTBuBgqaapFK9r3YdkKt1b2oRvgLzZq91bwNKh6ZS0S7sEcheguttREaL4iyfs/xQnqD7Z7AsWSsA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.978.0", + "@aws-sdk/signature-v4-multi-region": "^3.996.46", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { @@ -355,18 +411,35 @@ } }, "node_modules/@aws-sdk/nested-clients": { - "version": "3.997.42", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.42.tgz", - "integrity": "sha512-XWRyon2MTHXD/zMoo0Mbge6Vwf+iE0qQaM/RyGO6NfZ9WukCFiQL27nQVZjYy2JwSIg+iXZxKOX95OBXqlSM4w==", + "version": "3.997.45", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.45.tgz", + "integrity": "sha512-mooq9Q+jLa18VoM7HouczmslZU60iiB0aKc/Ztnq/luIL1ud0z4DnYprLR/ZO1gp331S9tJctM1HZr7u6YKBXQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.977.7", - "@aws-sdk/signature-v4-multi-region": "^3.996.44", - "@aws-sdk/types": "^3.974.3", - "@smithy/core": "^3.31.1", - "@smithy/fetch-http-handler": "^5.6.13", - "@smithy/node-http-handler": "^4.9.13", - "@smithy/types": "^4.16.1", + "@aws-sdk/core": "^3.978.0", + "@aws-sdk/signature-v4-multi-region": "^3.996.46", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/fetch-http-handler": "^5.7.2", + "@smithy/node-http-handler": "^4.11.3", + "@smithy/types": "^4.17.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/s3-request-presigner": { + "version": "3.1132.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.1132.0.tgz", + "integrity": "sha512-4BTMEFdxxOOADANB71G8U1PaGQo5wO9AISQw3eUyxbKcZNUGpjGJJExHWoWAfcJwPuh3E5Mj6gsIZMUFgB2W+g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.978.0", + "@aws-sdk/signature-v4-multi-region": "^3.996.46", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { @@ -374,14 +447,14 @@ } }, "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.996.44", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.44.tgz", - "integrity": "sha512-ZSfQ35Qn4MhSY+A0Whyr+KBx+wJKZUyBsOrjB2pSHOafRzbFe47T8XcXM8hZqUAC69qnqIy0C9ArxTuud0CC2w==", + "version": "3.996.46", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.46.tgz", + "integrity": "sha512-L+2xZTye/2T96f3lwCws0Zw6GG2JHZW9e8FpVgGBeeExSKyeoZ6CWRpBml/7DNiK/O26jrgPM9F+Ay8VkgzUWQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.974.3", + "@aws-sdk/types": "^3.974.5", "@smithy/signature-v4": "^5.6.12", - "@smithy/types": "^4.16.1", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { @@ -389,16 +462,16 @@ } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.1108.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1108.0.tgz", - "integrity": "sha512-rI80zxDxGJ6904eC/YbjkdjY6JdaZvQ01kOmrMvw7cFQGIHo27fhnIVbMSVDS4T6foQImjxYSRoOu/uSJscXDw==", + "version": "3.1129.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1129.0.tgz", + "integrity": "sha512-Sbl3rpzQdsG4ZK2zh0JWUYyZPKKorJlVOddA2T0DVbKJFrsW8J6wgnslxxUH04+WaBMr4A1HzJZvZX0xUvkniA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.977.7", - "@aws-sdk/nested-clients": "^3.997.42", - "@aws-sdk/types": "^3.974.3", - "@smithy/core": "^3.31.1", - "@smithy/types": "^4.16.1", + "@aws-sdk/core": "^3.978.0", + "@aws-sdk/nested-clients": "^3.997.45", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { @@ -406,12 +479,12 @@ } }, "node_modules/@aws-sdk/types": { - "version": "3.974.3", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.974.3.tgz", - "integrity": "sha512-ECAqfpNsef+7MO8qtR0h9KcFIBAygaE7Cm6UOiQl+ft+uVap+1G7bNEjs4mdJE2OnA4m6k7i8peH8uGIAsOMGw==", + "version": "3.974.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.974.5.tgz", + "integrity": "sha512-LkwLL2BLbC6wNNm4JaH9mbEqBMdOZCct6VAYqhdN4U1xrWM+fUJQEfbHwQgDypapOWTRtlk25akb5afM0P8CIQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.16.1", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { @@ -419,12 +492,12 @@ } }, "node_modules/@aws-sdk/xml-builder": { - "version": "3.972.38", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.38.tgz", - "integrity": "sha512-grf7mzfVxBS5AlsuTvBN7uDpzqohFww9fRPCO+EBSUdvtsYMcPSKdz54h/7XiscqNcUM1Ae1MF7JLHmiYYuzbQ==", + "version": "3.972.40", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.40.tgz", + "integrity": "sha512-wlFmCIGUlwF4zx/kncw+bmxTQh1HeSJq4mYV/V5cZUSJadDP3kXvGW8Rn21cimj/7y9ju+47oYWXi97vF7czaA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.16.1", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { @@ -2634,18 +2707,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@nodable/entities": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-3.0.0.tgz", - "integrity": "sha512-8L9xFeTYKhm49xfIypoe2W5wV1m/3Z58kT+7kR9A8OyFxcPduI4VmxaUMQyKYrRjUoLLSXv6EKKID5Tvj9cUVw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/nodable" - } - ], - "license": "MIT" - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -3504,12 +3565,12 @@ } }, "node_modules/@smithy/core": { - "version": "3.32.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.32.0.tgz", - "integrity": "sha512-NAiCSC78fzbNIEWoheoF74Ob5ZorLijCHpMY26Fqvqg/+9LuyIqMfHDg2p8Yk1rqOyowtiL3y7WX0AW+teL6zw==", + "version": "3.34.1", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.34.1.tgz", + "integrity": "sha512-dLcOUxz8YCv1RZUMKq6GbyUf95pLbrqh34bPvpCZ1+CByFF31BEAFewZjsGCnVsZTKdThNENfGyAgk2TJqVwSw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.17.0", + "@smithy/types": "^4.18.0", "tslib": "^2.6.2" }, "engines": { @@ -3517,13 +3578,13 @@ } }, "node_modules/@smithy/credential-provider-imds": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.5.0.tgz", - "integrity": "sha512-2jsPi+7Zv2hSzD9IXR9D7DTqSn7mv4XalzRm+bESh53jiaUS3NKEUbpQFTJP0HhQy9qzZvluxQ3yS24zdRrqsA==", + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.5.2.tgz", + "integrity": "sha512-A9uSdn72ozbRUSit0eib0TW7nXuNPlaeM0zcGkJ+nE6tFcSDbnmtwoxbTCFBukVQcszDAyvsd7+rTduPTXpygg==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.32.0", - "@smithy/types": "^4.17.0", + "@smithy/core": "^3.33.2", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { @@ -3531,13 +3592,13 @@ } }, "node_modules/@smithy/fetch-http-handler": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.7.0.tgz", - "integrity": "sha512-W/exA8T0LEzCQtJ02w4IzaEQPIspgarqZprb7W8FwnYiDowgCrjl2fTQ6FvuSSUnJORuepBF81abmBJwqh+0XQ==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.8.0.tgz", + "integrity": "sha512-ycSJu3tFAQ4v04CBB0agqFMVsSQ1iG3yw+SpgxRqKfaURpQD4CZ8Wn0zPMmSnOuTpTh65Vz+EA0rMrw089wvkA==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.32.0", - "@smithy/types": "^4.17.0", + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.18.0", "tslib": "^2.6.2" }, "engines": { @@ -3545,13 +3606,13 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.10.0.tgz", - "integrity": "sha512-nrh7VxqzPQS/ip1hS293aI/OAWDWARQvjUxCfuKhyrfHa2gTdk28066RNeWLI1uuoHXaKAkOF8IcSAHuOp0+SA==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.12.1.tgz", + "integrity": "sha512-ThMkboGeONWXAelq9FvGsuJC4rOi+qyC4/zhUF58xYpxUg5sQKx2VXZYJmtNjr4dSuBJ1HeJXETQILCz3wOHvw==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.32.0", - "@smithy/types": "^4.17.0", + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.18.0", "tslib": "^2.6.2" }, "engines": { @@ -3573,9 +3634,9 @@ } }, "node_modules/@smithy/types": { - "version": "4.17.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.17.0.tgz", - "integrity": "sha512-Aw4joiM0ZdErpo39lCj8phT2lxoiKZV+KZzBxnnQhWVtU2Is/WffQSL04uUWRcXUse9Ln8vXZK6V/FwqRVnQpg==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.18.0.tgz", + "integrity": "sha512-CgB6HHWer/vrKps24ulRIbpcpb7K4xAU7SkZ7YHzBPlwHsvsrCJFEXK421s+cJzX+ZrqtA/TuU5w1HzI7k9N8A==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -4374,18 +4435,6 @@ "dev": true, "license": "MIT" }, - "node_modules/anynum": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/anynum/-/anynum-1.0.1.tgz", - "integrity": "sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" - }, "node_modules/append-field": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", @@ -4797,15 +4846,6 @@ "readable-stream": "^3.4.0" } }, - "node_modules/block-stream2": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/block-stream2/-/block-stream2-2.1.0.tgz", - "integrity": "sha512-suhjmLI57Ewpmq00qaygS8UgEq2ly2PCItenIyhMqVjo4t4pGzqMvfgJuX8iWTeSDdfSSqS6j38fL4ToNL7Pfg==", - "license": "MIT", - "dependencies": { - "readable-stream": "^3.4.0" - } - }, "node_modules/bmp-ts": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/bmp-ts/-/bmp-ts-1.0.9.tgz", @@ -4889,12 +4929,6 @@ "node": ">=8" } }, - "node_modules/browser-or-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/browser-or-node/-/browser-or-node-2.1.1.tgz", - "integrity": "sha512-8CVjaLJGuSKMVTxJ2DpBl5XnlNDiT4cQFeuCJJrvJmts9YrTZDizTX7PjC2s6W4x+MBGZeEY6dGMrF04/6Hgqg==", - "license": "MIT" - }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -4920,15 +4954,6 @@ "ieee754": "^1.1.13" } }, - "node_modules/buffer-crc32": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", - "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -5929,15 +5954,6 @@ "node": ">=0.10.0" } }, - "node_modules/decode-uri-component": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.5.0.tgz", - "integrity": "sha512-1BiQVoK8C9gUbQU6NzAtO/tkz2qOFpEObMWpcFvhx4fYnj4Oc5yzaJN/LD36ihkVUdXyh5ZekzX+yM+ty/SrPg==", - "license": "MIT", - "engines": { - "node": ">=14.16" - } - }, "node_modules/dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -7386,45 +7402,6 @@ ], "license": "BSD-3-Clause" }, - "node_modules/fast-xml-builder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.3.0.tgz", - "integrity": "sha512-F74cZEdCvuw9P41GAC3rod4X04jjWGM1JPEv/GWSqFTWLsdyMSBMBMlm9Hk3GLBgLBbdBNY8yee0pQh2RBVESQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "path-expression-matcher": "^1.6.2", - "xml-naming": "^0.3.0" - } - }, - "node_modules/fast-xml-parser": { - "version": "5.10.1", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.10.1.tgz", - "integrity": "sha512-IEMIf7298kXuZSRFoGfMYrl7is8LpavODgbNz1cwIudv7KwVFnuU+UsMporfq6PD6aXSlawZlARiA3UywCTfMw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "@nodable/entities": "^3.0.0", - "fast-xml-builder": "^1.2.0", - "is-unsafe": "^2.0.0", - "path-expression-matcher": "^1.6.2", - "strnum": "^2.4.1", - "xml-naming": "^0.3.0" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, "node_modules/fastq": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", @@ -7533,15 +7510,6 @@ "node": ">=8" } }, - "node_modules/filter-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", - "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/finalhandler": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", @@ -8677,15 +8645,6 @@ "node": ">= 12" } }, - "node_modules/ipaddr.js": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.5.0.tgz", - "integrity": "sha512-aq+t5NAc+cS6rZQQVWC2x98CPqGtKKTMDd4Gaodv0wShnItdKg/51djkGJ1hqH+Oy0ivDftCbSLCQob8zso01w==", - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -9137,18 +9096,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-unsafe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-unsafe/-/is-unsafe-2.0.0.tgz", - "integrity": "sha512-2LdV822R+wmI86unXA93WCFpL6g+av8ynWk0nrHyJqGop5VoocYsSLFgN8jrfalT6iGeLNM4KXuVSsULP53kEA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" - }, "node_modules/is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", @@ -9687,6 +9634,7 @@ "version": "4.18.1", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true, "license": "MIT" }, "node_modules/lodash.camelcase": { @@ -10420,36 +10368,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minio": { - "version": "8.0.7", - "resolved": "https://registry.npmjs.org/minio/-/minio-8.0.7.tgz", - "integrity": "sha512-E737MgufW8CeQAsTAtnEMrxZ9scMSf29kkhZoXzDTKj/Jszzo2SfeZUH9wbDQH2Rsq6TCtl/yQL0+XdVKZansQ==", - "license": "Apache-2.0", - "dependencies": { - "async": "^3.2.4", - "block-stream2": "^2.1.0", - "browser-or-node": "^2.1.1", - "buffer-crc32": "^1.0.0", - "eventemitter3": "^5.0.1", - "fast-xml-parser": "^5.3.4", - "ipaddr.js": "^2.0.1", - "lodash": "^4.17.21", - "mime-types": "^2.1.35", - "query-string": "^7.1.3", - "stream-json": "^1.8.0", - "through2": "^4.0.2", - "xml2js": "^0.5.0 || ^0.6.2" - }, - "engines": { - "node": "^16 || ^18 || >=20" - } - }, - "node_modules/minio/node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "license": "MIT" - }, "node_modules/mlly": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", @@ -11358,21 +11276,6 @@ "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "node_modules/path-expression-matcher": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.6.2.tgz", - "integrity": "sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -12077,24 +11980,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/query-string": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", - "integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==", - "license": "MIT", - "dependencies": { - "decode-uri-component": "^0.2.2", - "filter-obj": "^1.1.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", @@ -13033,15 +12918,6 @@ "node": ">=0.10.0" } }, - "node_modules/split-on-first": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", - "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", @@ -13074,21 +12950,6 @@ "node": ">= 0.4" } }, - "node_modules/stream-chain": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/stream-chain/-/stream-chain-2.2.5.tgz", - "integrity": "sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==", - "license": "BSD-3-Clause" - }, - "node_modules/stream-json": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/stream-json/-/stream-json-1.9.1.tgz", - "integrity": "sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==", - "license": "BSD-3-Clause", - "dependencies": { - "stream-chain": "^2.2.5" - } - }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -13097,15 +12958,6 @@ "node": ">=10.0.0" } }, - "node_modules/strict-uri-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", - "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -13249,21 +13101,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/strnum": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.1.tgz", - "integrity": "sha512-M9eUSMT2dCB2cTNPG7UYj6KuK7RJR2SN2+yCV/fTW3xzTCS6EaGZ5pSMgDIjB7r8zSfTGk+dvvn9rTjpVS9Mwg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "anynum": "^1.0.1" - } - }, "node_modules/strtok3": { "version": "10.3.5", "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.5.tgz", @@ -13438,15 +13275,6 @@ "dev": true, "license": "MIT" }, - "node_modules/through2": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", - "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", - "license": "MIT", - "dependencies": { - "readable-stream": "3" - } - }, "node_modules/tinycolor2": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", @@ -14704,40 +14532,12 @@ } } }, - "node_modules/xml-naming": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.3.0.tgz", - "integrity": "sha512-ghig2TBE/H11aOVgmahA3MhimvkBr6JIYknH/Dhdk10nXwdbIqBJsbfMxpvFPG8bAw77gN29aQWvKpmVoPlvPQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "engines": { - "node": ">=16.0.0" - } - }, "node_modules/xml-parse-from-string": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz", "integrity": "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==", "license": "MIT" }, - "node_modules/xml2js": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", - "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", - "license": "MIT", - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/xmlbuilder": { "version": "11.0.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", diff --git a/package.json b/package.json index feed7e0b17..eb68fa0a70 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "dev:server": "tsx watch ./src/main.ts", "test": "tsx watch ./test/all.test.ts", "test:xmacna-compat": "node --test test/xmacna-baileys-compat.test.cjs", - "test:xmacna-inbox": "tsx --test test/xmacna-inbound-inbox.test.ts test/xmacna-inbound-sink-dispatch.test.ts test/xmacna-inbound-service-contracts.test.ts test/xmacna-baileys-message-processor.test.ts test/xmacna-chatbot-debounce.test.ts", + "test:xmacna-inbox": "tsx --test test/xmacna-s3-storage.test.ts test/xmacna-inbound-inbox.test.ts test/xmacna-inbound-sink-dispatch.test.ts test/xmacna-inbound-service-contracts.test.ts test/xmacna-baileys-message-processor.test.ts test/xmacna-chatbot-debounce.test.ts", "test:xmacna-inbox:postgres": "tsx --test test/xmacna-inbound-inbox.postgres.test.ts", "inbound-inbox:maintenance": "tsx src/cli/inboundInboxMaintenance.ts", "lint": "eslint --fix --ext .ts src", @@ -73,7 +73,9 @@ }, "dependencies": { "@adiwajshing/keyed-db": "^0.2.4", + "@aws-sdk/client-s3": "^3.1132.0", "@aws-sdk/client-sqs": "^3.891.0", + "@aws-sdk/s3-request-presigner": "^3.1132.0", "@ffmpeg-installer/ffmpeg": "^1.1.0", "@figuro/chatwoot-sdk": "^1.1.16", "@hapi/boom": "^10.0.1", @@ -93,10 +95,10 @@ "eventemitter2": "^6.4.9", "express": "^4.22.2", "express-async-errors": "^3.1.1", + "fetch-socks": "^1.3.2", "fluent-ffmpeg": "^2.1.3", "form-data": "^4.0.6", "https-proxy-agent": "^7.0.6", - "fetch-socks": "^1.3.2", "i18next": "^23.7.19", "jimp": "^1.6.0", "json-schema": "^0.4.0", @@ -109,7 +111,6 @@ "mediainfo.js": "^0.3.4", "mime": "^4.0.0", "mime-types": "^2.1.35", - "minio": "^8.0.7", "multer": "^2.3.0", "nats": "^2.29.1", "node-cache": "^5.1.2", @@ -139,7 +140,6 @@ "fast-xml-parser": "5.10.1" }, "qs": "6.16.0", - "decode-uri-component": "0.5.0", "deepmerge-ts": "8.0.0" }, "devDependencies": { diff --git a/src/api/integrations/storage/s3/libs/minio.server.ts b/src/api/integrations/storage/s3/libs/minio.server.ts index 47145a4cb3..9823d8faf0 100644 --- a/src/api/integrations/storage/s3/libs/minio.server.ts +++ b/src/api/integrations/storage/s3/libs/minio.server.ts @@ -1,139 +1,212 @@ +import { + CreateBucketCommand, + DeleteObjectCommand, + GetObjectCommand, + HeadBucketCommand, + PutBucketPolicyCommand, + PutObjectCommand, + S3Client, + S3ClientConfig, +} from '@aws-sdk/client-s3'; +import { getSignedUrl } from '@aws-sdk/s3-request-presigner'; import { ConfigService, S3 } from '@config/env.config'; import { Logger } from '@config/logger.config'; import { BadRequestException } from '@exceptions'; -import * as MinIo from 'minio'; import { join } from 'path'; import { Readable, Transform } from 'stream'; +// xmacna/elysium#518 (2026-09-14): this module used to wrap the `minio` client. The only +// patched release of its transitive `stream-json` breaks minio at import, so the runtime +// audit gate could never go green. The wrapper now speaks to S3 through the same AWS SDK +// family the fork already ships (`@aws-sdk/client-sqs`), keeping the exported contract +// (`BUCKET`, `uploadFile`, `uploadTempFile`, `getObjectUrl`, `deleteFile`) byte-compatible +// for the six callers. The file keeps its historical name so no importer changes. + const logger = new Logger('S3 Service'); const BUCKET = new ConfigService().get('S3'); -interface Metadata extends MinIo.ItemBucketMetadata { +/** Minio accepted a flat header bag; `Content-Type` is a real header, the rest is object metadata. */ +interface Metadata { 'Content-Type': string; + [key: string]: string; } -const minioClient = (() => { - if (BUCKET?.ENABLE) { - return new MinIo.Client({ - endPoint: BUCKET.ENDPOINT, - port: BUCKET.PORT, - useSSL: BUCKET.USE_SSL, - accessKey: BUCKET.ACCESS_KEY, - secretKey: BUCKET.SECRET_KEY, - region: BUCKET.REGION, - }); - } -})(); +/** minio's presignedGetObject default (7 days) — also the SigV4 maximum. */ +export const DEFAULT_PRESIGN_EXPIRY_SECONDS = 7 * 24 * 60 * 60; -const bucketName = BUCKET.BUCKET_NAME; +export function buildS3ClientConfig(bucket: S3): S3ClientConfig { + const scheme = bucket.USE_SSL ? 'https' : 'http'; + const defaultPort = bucket.USE_SSL ? 443 : 80; + const host = bucket.PORT && bucket.PORT !== defaultPort ? `${bucket.ENDPOINT}:${bucket.PORT}` : bucket.ENDPOINT; + return { + region: bucket.REGION, + endpoint: `${scheme}://${host}`, + credentials: { accessKeyId: bucket.ACCESS_KEY, secretAccessKey: bucket.SECRET_KEY }, + // minio used virtual-host style only for amazonaws endpoints and path style elsewhere + // (self-hosted MinIO). Same rule here. + forcePathStyle: !/\.amazonaws\.com$/i.test(bucket.ENDPOINT || ''), + }; +} -const bucketExists = async () => { - if (minioClient) { - try { - const list = await minioClient.listBuckets(); - return list.find((bucket) => bucket.name === bucketName); - } catch { - return false; - } - } -}; - -const setBucketPolicy = async () => { - if (minioClient) { - const policy = { - Version: '2012-10-17', - Statement: [ - { - Effect: 'Allow', - Principal: '*', - Action: ['s3:GetObject'], - Resource: [`arn:aws:s3:::${bucketName}/*`], - }, - ], - }; - await minioClient.setBucketPolicy(bucketName, JSON.stringify(policy)); +function splitMetadata(metadata: Metadata): { contentType: string; custom: Record } { + const custom: Record = {}; + let contentType = ''; + for (const [key, value] of Object.entries(metadata || {})) { + if (key.toLowerCase() === 'content-type') contentType = String(value); + else if (value !== undefined && value !== null) custom[key] = String(value); } -}; - -const createBucket = async () => { - if (minioClient) { - try { - const exists = await bucketExists(); - if (!exists) { - await minioClient.makeBucket(bucketName); - } - if (!BUCKET.SKIP_POLICY) { - await setBucketPolicy(); + return { contentType, custom }; +} + +type S3Sender = Pick; + +/** + * Builds the storage backend over an injectable client (tests pass a fake `send`). + * Every function is a no-op when the bucket is disabled, exactly like the minio wrapper. + */ +export function createS3Backend(bucket: S3, client: S3Sender | undefined) { + const bucketName = bucket?.BUCKET_NAME; + + const bucketExists = async () => { + if (client) { + try { + await client.send(new HeadBucketCommand({ Bucket: bucketName })); + return true; + } catch { + return false; } - logger.info(`S3 Bucket ${bucketName} - ON`); - return true; - } catch (error) { - logger.error('S3 ERROR:'); - logger.error(error); - return false; } - } -}; - -createBucket(); - -const uploadFile = async (fileName: string, file: Buffer | Transform | Readable, size: number, metadata: Metadata) => { - if (minioClient) { - const objectName = join('evolution-api', fileName); - try { - metadata['custom-header-application'] = 'evolution-api'; - return await minioClient.putObject(bucketName, objectName, file, size, metadata); - } catch (error) { - logger.error(error); - return error; + }; + + const setBucketPolicy = async () => { + if (client) { + const policy = { + Version: '2012-10-17', + Statement: [ + { + Effect: 'Allow', + Principal: '*', + Action: ['s3:GetObject'], + Resource: [`arn:aws:s3:::${bucketName}/*`], + }, + ], + }; + await client.send(new PutBucketPolicyCommand({ Bucket: bucketName, Policy: JSON.stringify(policy) })); } - } -}; + }; + + const createBucket = async () => { + if (client) { + try { + const exists = await bucketExists(); + if (!exists) { + await client.send(new CreateBucketCommand({ Bucket: bucketName })); + } + if (!bucket.SKIP_POLICY) { + await setBucketPolicy(); + } + logger.info(`S3 Bucket ${bucketName} - ON`); + return true; + } catch (error) { + logger.error('S3 ERROR:'); + logger.error(error); + return false; + } + } + }; + + const putObject = async ( + objectName: string, + file: Buffer | Transform | Readable, + size: number, + metadata: Metadata, + ) => { + metadata['custom-header-application'] = 'evolution-api'; + const { contentType, custom } = splitMetadata(metadata); + return client.send( + new PutObjectCommand({ + Bucket: bucketName, + Key: objectName, + Body: file, + ContentType: contentType || undefined, + ContentLength: Number.isFinite(size) && size >= 0 ? size : undefined, + Metadata: custom, + }), + ); + }; -const getObjectUrl = async (fileName: string, expiry?: number) => { - if (minioClient) { - try { + const uploadFile = async ( + fileName: string, + file: Buffer | Transform | Readable, + size: number, + metadata: Metadata, + ) => { + if (client) { const objectName = join('evolution-api', fileName); - if (expiry) { - return await minioClient.presignedGetObject(bucketName, objectName, expiry); + try { + return await putObject(objectName, file, size, metadata); + } catch (error) { + logger.error(error); + return error; } - return await minioClient.presignedGetObject(bucketName, objectName); - } catch (error) { - throw new BadRequestException(error?.message); } - } -}; - -const uploadTempFile = async ( - folder: string, - fileName: string, - file: Buffer | Transform | Readable, - size: number, - metadata: Metadata, -) => { - if (minioClient) { - const objectName = join(folder, fileName); - try { - metadata['custom-header-application'] = 'evolution-api'; - return await minioClient.putObject(bucketName, objectName, file, size, metadata); - } catch (error) { - logger.error(error); - return error; + }; + + const getObjectUrl = async (fileName: string, expiry?: number) => { + if (client) { + try { + const objectName = join('evolution-api', fileName); + const expiresIn = + expiry && expiry > 0 ? Math.min(expiry, DEFAULT_PRESIGN_EXPIRY_SECONDS) : DEFAULT_PRESIGN_EXPIRY_SECONDS; + return await getSignedUrl(client as S3Client, new GetObjectCommand({ Bucket: bucketName, Key: objectName }), { + expiresIn, + }); + } catch (error) { + throw new BadRequestException(error?.message); + } } - } -}; - -const deleteFile = async (folder: string, fileName: string) => { - if (minioClient) { - const objectName = join(folder, fileName); - try { - return await minioClient.removeObject(bucketName, objectName); - } catch (error) { - logger.error(error); - return error; + }; + + const uploadTempFile = async ( + folder: string, + fileName: string, + file: Buffer | Transform | Readable, + size: number, + metadata: Metadata, + ) => { + if (client) { + const objectName = join(folder, fileName); + try { + return await putObject(objectName, file, size, metadata); + } catch (error) { + logger.error(error); + return error; + } } - } -}; + }; + + const deleteFile = async (folder: string, fileName: string) => { + if (client) { + const objectName = join(folder, fileName); + try { + return await client.send(new DeleteObjectCommand({ Bucket: bucketName, Key: objectName })); + } catch (error) { + logger.error(error); + return error; + } + } + }; + + return { createBucket, uploadFile, uploadTempFile, getObjectUrl, deleteFile }; +} + +const s3Client = BUCKET?.ENABLE ? new S3Client(buildS3ClientConfig(BUCKET)) : undefined; + +const backend = createS3Backend(BUCKET, s3Client); + +backend.createBucket(); + +const { deleteFile, getObjectUrl, uploadFile, uploadTempFile } = backend; export { BUCKET, deleteFile, getObjectUrl, uploadFile, uploadTempFile }; diff --git a/test/xmacna-baileys-compat.test.cjs b/test/xmacna-baileys-compat.test.cjs index 26f793b44e..76cea9ce3f 100644 --- a/test/xmacna-baileys-compat.test.cjs +++ b/test/xmacna-baileys-compat.test.cjs @@ -129,8 +129,11 @@ test('security overrides resolve to the audited runtime versions', () => { assert.equal(installedPackage('qs', express.dir).version, '6.16.0'); assert.equal(installedPackage('qs', bodyParser.dir).version, '6.16.0'); - const queryString = installedPackage('query-string', installedPackage('minio').dir); - assert.equal(installedPackage('decode-uri-component', queryString.dir).version, '0.5.0'); + // xmacna/elysium#518 (2026-09-14): minio (and its unpatchable stream-json) is gone; the S3 + // wrapper speaks @aws-sdk/client-s3. Pin the swap so a merge never brings minio back. + assert.throws(() => installedPackage('minio'), /not installed/); + assert.ok(installedPackage('@aws-sdk/client-s3').version); + assert.ok(installedPackage('@aws-sdk/s3-request-presigner').version); const prismaConfig = installedPackage('@prisma/config', installedPackage('prisma').dir); assert.equal(installedPackage('deepmerge-ts', prismaConfig.dir).version, '8.0.0'); @@ -207,27 +210,22 @@ test('multer memoryStorage upload remains compatible after the security update', }); }); -test('MinIO client loads and signs requests with the decode-uri-component override', async () => { - // query-string 7 is CommonJS and decode-uri-component 0.5.0 is ESM-only, so query-string.parse - // cannot decode under this override. MinIO only calls query-string.stringify; this test pins - // that contract so a MinIO update that starts parsing query strings fails here, not in runtime. - const minioSources = ['node_modules/minio/dist/main/internal/client.js', 'node_modules/minio/dist/main/helpers.js']; - for (const source of minioSources) { - const calls = read(source).match(/\b(?:qs|querystring)\.[A-Za-z]+/g) ?? []; - assert.ok(calls.length > 0, `${source} no longer references query-string`); - assert.deepEqual([...new Set(calls.map((call) => call.split('.')[1]))], ['stringify'], source); - } - - const MinIo = require('minio'); - const client = new MinIo.Client({ - endPoint: '127.0.0.1', - port: 9, - useSSL: false, - accessKey: 'probe-access', - secretKey: 'probe-secret', +test('S3 wrapper presigns through @aws-sdk/client-s3 (minio and its stream-json are gone)', async () => { + // xmacna/elysium#518 (2026-09-14): the storage wrapper moved from minio to the AWS SDK so the + // runtime audit can be clean. Pin the presign contract the media callers rely on: path, + // expiry and a SigV4 signature, with no minio on disk. + assert.ok(!fs.existsSync(path.join(root, 'node_modules/minio')), 'minio must not be installed'); + const { S3Client, GetObjectCommand } = require('@aws-sdk/client-s3'); + const { getSignedUrl } = require('@aws-sdk/s3-request-presigner'); + const client = new S3Client({ region: 'us-east-1', + endpoint: 'http://127.0.0.1:9', + forcePathStyle: true, + credentials: { accessKeyId: 'probe-access', secretAccessKey: 'probe-secret' }, }); - const url = new URL(await client.presignedGetObject('evolution', 'media/probe file.jpg', 60)); + const url = new URL( + await getSignedUrl(client, new GetObjectCommand({ Bucket: 'evolution', Key: 'media/probe file.jpg' }), { expiresIn: 60 }), + ); assert.equal(url.pathname, '/evolution/media/probe%20file.jpg'); assert.equal(url.searchParams.get('X-Amz-Expires'), '60'); assert.match(url.searchParams.get('X-Amz-Signature'), /^[0-9a-f]{64}$/); diff --git a/test/xmacna-s3-storage.test.ts b/test/xmacna-s3-storage.test.ts new file mode 100644 index 0000000000..9ac516d77e --- /dev/null +++ b/test/xmacna-s3-storage.test.ts @@ -0,0 +1,128 @@ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import { Readable } from 'node:stream'; + +import { + CreateBucketCommand, + DeleteObjectCommand, + HeadBucketCommand, + PutBucketPolicyCommand, + PutObjectCommand, + S3Client, +} from '@aws-sdk/client-s3'; + +process.env.S3_ENABLED = process.env.S3_ENABLED ?? 'false'; + +// xmacna/elysium#518: the storage wrapper moved from `minio` to `@aws-sdk/client-s3`. These +// tests pin the contract the six callers rely on (object key layout, metadata split, +// presigned URL expiry, no-op when disabled, swallowed upload/delete errors). +import { buildS3ClientConfig, createS3Backend, DEFAULT_PRESIGN_EXPIRY_SECONDS } from '../src/api/integrations/storage/s3/libs/minio.server'; + +const bucket = { + ENABLE: true, + ACCESS_KEY: 'AKIATEST', + SECRET_KEY: 'secret', + ENDPOINT: 's3.us-east-1.amazonaws.com', + BUCKET_NAME: 'evolution-media-test', + PORT: 443, + USE_SSL: true, + REGION: 'us-east-1', + SKIP_POLICY: false, + SAVE_VIDEO: false, +} as any; + +function fakeClient(script: Record = {}) { + const calls: Array<{ name: string; input: any }> = []; + return { + calls, + send: async (command: any) => { + const name = command.constructor.name; + calls.push({ name, input: command.input }); + const outcome = script[name]; + if (outcome instanceof Error) throw outcome; + return outcome ?? {}; + }, + }; +} + +test('AWS endpoints use virtual-host style; anything else is path style (minio parity)', () => { + const aws = buildS3ClientConfig(bucket); + assert.equal(aws.endpoint, 'https://s3.us-east-1.amazonaws.com'); + assert.equal(aws.forcePathStyle, false); + assert.equal(aws.region, 'us-east-1'); + const selfHosted = buildS3ClientConfig({ ...bucket, ENDPOINT: 'minio.internal', PORT: 9000, USE_SSL: false }); + assert.equal(selfHosted.endpoint, 'http://minio.internal:9000'); + assert.equal(selfHosted.forcePathStyle, true); +}); + +test('uploadFile stores under evolution-api/ with Content-Type as header and the rest as metadata', async () => { + const client = fakeClient({ PutObjectCommand: { ETag: '"abc"' } }); + const backend = createS3Backend(bucket, client); + const body = Buffer.from('hello'); + const result = await backend.uploadFile('inst/audio/x.ogg', body, body.length, { 'Content-Type': 'audio/ogg' } as any); + assert.deepEqual(result, { ETag: '"abc"' }); + assert.equal(client.calls.length, 1); + assert.equal(client.calls[0].name, PutObjectCommand.name); + assert.equal(client.calls[0].input.Bucket, 'evolution-media-test'); + assert.equal(client.calls[0].input.Key, 'evolution-api/inst/audio/x.ogg'); + assert.equal(client.calls[0].input.ContentType, 'audio/ogg'); + assert.equal(client.calls[0].input.ContentLength, 5); + assert.deepEqual(client.calls[0].input.Metadata, { 'custom-header-application': 'evolution-api' }); +}); + +test('uploadTempFile keeps the caller folder; streams pass ContentLength through', async () => { + const client = fakeClient(); + const backend = createS3Backend(bucket, client); + await backend.uploadTempFile('tmp', 'y.bin', Readable.from([Buffer.from('ab')]), 2, { 'Content-Type': 'application/octet-stream' } as any); + assert.equal(client.calls[0].input.Key, 'tmp/y.bin'); + assert.equal(client.calls[0].input.ContentLength, 2); +}); + +test('upload and delete errors are logged and returned, never thrown (caller contract)', async () => { + const boom = new Error('AccessDenied'); + const client = fakeClient({ PutObjectCommand: boom, DeleteObjectCommand: boom }); + const backend = createS3Backend(bucket, client); + assert.equal(await backend.uploadFile('a', Buffer.alloc(1), 1, { 'Content-Type': 'x' } as any), boom); + assert.equal(await backend.deleteFile('evolution-api', 'a'), boom); + assert.equal(client.calls[1].name, DeleteObjectCommand.name); + assert.equal(client.calls[1].input.Key, 'evolution-api/a'); +}); + +test('getObjectUrl presigns evolution-api/ for 7 days by default and caps custom expiry at 7 days', async () => { + const backend = createS3Backend(bucket, new S3Client(buildS3ClientConfig(bucket))); + const url = new URL(await backend.getObjectUrl('inst/img.jpg')); + assert.equal(url.hostname, 'evolution-media-test.s3.us-east-1.amazonaws.com'); + assert.equal(url.pathname, '/evolution-api/inst/img.jpg'); + assert.equal(url.searchParams.get('X-Amz-Expires'), String(DEFAULT_PRESIGN_EXPIRY_SECONDS)); + assert.ok(url.searchParams.get('X-Amz-Signature')); + const short = new URL(await backend.getObjectUrl('inst/img.jpg', 60)); + assert.equal(short.searchParams.get('X-Amz-Expires'), '60'); + const capped = new URL(await backend.getObjectUrl('inst/img.jpg', 99 * 24 * 3600)); + assert.equal(capped.searchParams.get('X-Amz-Expires'), String(DEFAULT_PRESIGN_EXPIRY_SECONDS)); +}); + +test('createBucket: existing bucket only gets the public-read policy; missing bucket is created first', async () => { + const existing = fakeClient(); + await createS3Backend(bucket, existing).createBucket(); + assert.deepEqual( + existing.calls.map((c) => c.name), + [HeadBucketCommand.name, PutBucketPolicyCommand.name], + ); + assert.match(existing.calls[1].input.Policy, /"s3:GetObject"/); + assert.match(existing.calls[1].input.Policy, /arn:aws:s3:::evolution-media-test\/\*/); + + const missing = fakeClient({ HeadBucketCommand: new Error('NotFound') }); + await createS3Backend({ ...bucket, SKIP_POLICY: true }, missing).createBucket(); + assert.deepEqual( + missing.calls.map((c) => c.name), + [HeadBucketCommand.name, CreateBucketCommand.name], + ); +}); + +test('disabled storage: every function is a no-op returning undefined', async () => { + const backend = createS3Backend({ ...bucket, ENABLE: false }, undefined); + assert.equal(await backend.uploadFile('a', Buffer.alloc(1), 1, { 'Content-Type': 'x' } as any), undefined); + assert.equal(await backend.getObjectUrl('a'), undefined); + assert.equal(await backend.deleteFile('f', 'a'), undefined); + assert.equal(await backend.createBucket(), undefined); +});