diff --git a/package-lock.json b/package-lock.json index 7b746ed2..b51eb5cb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,9 +39,9 @@ "conventional-changelog-conventionalcommits": "^5.0.0", "crypto-js": "^4.2.0", "dotenv": "^16.0.0", - "fastify": "^4.8.1", + "fastify": "^4.28.1", "fastify-metrics": "^10.2.0", - "fastify-plugin": "^4.0.0", + "fastify-plugin": "^4.5.1", "fastify-xml-body-parser": "^2.2.0", "fs-extra": "^10.0.1", "fs-xattr": "0.3.1", @@ -100,7 +100,7 @@ "ts-node-dev": "^1.1.8", "tsx": "^4.16.0", "tus-js-client": "^3.1.0", - "typescript": "^4.5.5" + "typescript": "^5.6.2" }, "engines": { "node": ">= 14.0.0" @@ -2987,16 +2987,24 @@ "integrity": "sha512-J8TOSBq3SoZbDhM9+R/u77hP93gz/rajSA+K2kGyijPpORPWUXHUpTaleoj+92As0S9uPRP7Oi8IqMf0u+ro6A==" }, "node_modules/@fastify/error": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.0.0.tgz", - "integrity": "sha512-dPRyT40GiHRzSCll3/Jn2nPe25+E1VXc9tDwRAIKwFCxd5Np5wzgz1tmooWG3sV0qKgrBibihVoCna2ru4SEFg==" + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.4.1.tgz", + "integrity": "sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==" }, "node_modules/@fastify/fast-json-stringify-compiler": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.2.0.tgz", - "integrity": "sha512-ypZynRvXA3dibfPykQN3RB5wBdEUgSGgny8Qc6k163wYPLD4mEGEDkACp+00YmqkGvIm8D/xYoHajwyEdWD/eg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.3.0.tgz", + "integrity": "sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==", + "dependencies": { + "fast-json-stringify": "^5.7.0" + } + }, + "node_modules/@fastify/merge-json-schemas": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.1.1.tgz", + "integrity": "sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==", "dependencies": { - "fast-json-stringify": "^5.0.0" + "fast-deep-equal": "^3.1.3" } }, "node_modules/@fastify/multipart": { @@ -7030,11 +7038,6 @@ "node": ">= 8" } }, - "node_modules/archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==" - }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -7083,13 +7086,12 @@ } }, "node_modules/avvio": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/avvio/-/avvio-8.2.0.tgz", - "integrity": "sha512-bbCQdg7bpEv6kGH41RO/3B2/GMMmJSo2iBK+X8AWN9mujtfUipMDfIjsgHCfpnKqoGEQrrmCDKSa5OQ19+fDmg==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/avvio/-/avvio-8.4.0.tgz", + "integrity": "sha512-CDSwaxINFy59iNwhYnkvALBwZiTydGkOecZyPkqBpABYR1KqGEsET0VOOYDwtleZSUIdeY36DC2bSZ24CO1igA==", "dependencies": { - "archy": "^1.0.0", - "debug": "^4.0.0", - "fastq": "^1.6.1" + "@fastify/error": "^3.3.0", + "fastq": "^1.17.1" } }, "node_modules/axios": { @@ -7634,9 +7636,9 @@ "dev": true }, "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "engines": { "node": ">= 0.6" } @@ -8333,9 +8335,9 @@ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, "node_modules/fast-content-type-parse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-1.0.0.tgz", - "integrity": "sha512-Xbc4XcysUXcsP5aHUU7Nq3OwvHq97C+WnbkeIefpeYLX+ryzFJlU6OStFJhs6Ol0LkUGpcK+wL0JwfM+FCU5IA==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-1.1.0.tgz", + "integrity": "sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==" }, "node_modules/fast-copy": { "version": "2.1.3", @@ -8387,18 +8389,35 @@ "dev": true }, "node_modules/fast-json-stringify": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.6.2.tgz", - "integrity": "sha512-F6xkRrXvtGbAiDSEI5Rk7qk2P63Y9kc8bO6Dnsd3Rt6sBNr2QxNFWs0JbKftgiyOfGxnJaRoHe4SizCTqeAyrA==", + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.16.1.tgz", + "integrity": "sha512-KAdnLvy1yu/XrRtP+LJnxbBGrhN+xXu+gt3EUvZhYGKCr3lFHq/7UFJHHFgmJKoqlh6B40bZLEv7w46B0mqn1g==", "dependencies": { - "@fastify/deepmerge": "^1.0.0", + "@fastify/merge-json-schemas": "^0.1.0", "ajv": "^8.10.0", - "ajv-formats": "^2.1.1", + "ajv-formats": "^3.0.1", "fast-deep-equal": "^3.1.3", "fast-uri": "^2.1.0", + "json-schema-ref-resolver": "^1.0.1", "rfdc": "^1.2.0" } }, + "node_modules/fast-json-stringify/node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", @@ -8406,9 +8425,9 @@ "dev": true }, "node_modules/fast-querystring": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.1.tgz", - "integrity": "sha512-qR2r+e3HvhEFmpdHMv//U8FnFlnYjaC6QKDuaXALDkw2kvHO8WDjxH+f/rHGR4Me4pnk8p9JAkRNTjYHAKRn2Q==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz", + "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", "dependencies": { "fast-decode-uri-component": "^1.0.1" } @@ -8453,25 +8472,36 @@ } }, "node_modules/fastify": { - "version": "4.15.0", - "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.15.0.tgz", - "integrity": "sha512-m/CaRN8nf5uyYdrDe2qqq+0z3oGyE+A++qlKQoLJTI4WI0nWK9D6R3FxXQ3MVwt/md977GMR4F43pE9oqrS2zw==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.28.1.tgz", + "integrity": "sha512-kFWUtpNr4i7t5vY2EJPCN2KgMVpuqfU4NjnJNCgiNB900oiDeYqaNDRcAfeBbOF5hGixixxcKnOU4KN9z6QncQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "dependencies": { "@fastify/ajv-compiler": "^3.5.0", - "@fastify/error": "^3.0.0", - "@fastify/fast-json-stringify-compiler": "^4.2.0", + "@fastify/error": "^3.4.0", + "@fastify/fast-json-stringify-compiler": "^4.3.0", "abstract-logging": "^2.0.1", - "avvio": "^8.2.0", - "fast-content-type-parse": "^1.0.0", - "find-my-way": "^7.6.0", - "light-my-request": "^5.6.1", - "pino": "^8.5.0", - "process-warning": "^2.0.0", + "avvio": "^8.3.0", + "fast-content-type-parse": "^1.1.0", + "fast-json-stringify": "^5.8.0", + "find-my-way": "^8.0.0", + "light-my-request": "^5.11.0", + "pino": "^9.0.0", + "process-warning": "^3.0.0", "proxy-addr": "^2.0.7", "rfdc": "^1.3.0", - "secure-json-parse": "^2.5.0", - "semver": "^7.3.7", - "tiny-lru": "^10.0.0" + "secure-json-parse": "^2.7.0", + "semver": "^7.5.4", + "toad-cache": "^3.3.0" } }, "node_modules/fastify-metrics": { @@ -8505,10 +8535,112 @@ "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-3.0.1.tgz", "integrity": "sha512-qKcDXmuZadJqdTm6vlCqioEbyewF60b/0LOFCcYN1B6BIZGlYJumWWOYs70SFYLDAH4YqdE1cxH/RKMG7rFxgA==" }, + "node_modules/fastify/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" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/fastify/node_modules/pino": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.4.0.tgz", + "integrity": "sha512-nbkQb5+9YPhQRz/BeQmrWpEknAaqjpAqRK8NwJpmrX/JHu7JuZC5G1CeAwJDJfGes4h+YihC6in3Q2nGb+Y09w==", + "dependencies": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.1.1", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^1.2.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^4.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/fastify/node_modules/pino-abstract-transport": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz", + "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==", + "dependencies": { + "readable-stream": "^4.0.0", + "split2": "^4.0.0" + } + }, + "node_modules/fastify/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==" + }, + "node_modules/fastify/node_modules/pino/node_modules/process-warning": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.0.tgz", + "integrity": "sha512-/MyYDxttz7DfGMMHiysAsFE4qF+pQYAA8ziO/3NcRVrQ5fSk+Mns4QZA/oRPFzvcqNoVJXQNWNAsdwBXLUkQKw==" + }, "node_modules/fastify/node_modules/process-warning": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.0.0.tgz", - "integrity": "sha512-+MmoAXoUX+VTHAlwns0h+kFUWFs/3FZy+ZuchkgjyOu3oioLAo2LB5aCfKPh2+P9O18i3m43tUEv3YqttSy0Ww==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", + "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==" + }, + "node_modules/fastify/node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "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/fastify/node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/fastify/node_modules/sonic-boom": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.1.0.tgz", + "integrity": "sha512-NGipjjRicyJJ03rPiZCJYjwlsuP2d1/5QUviozRXC7S3WdVWNK5e3Ojieb9CCyfhq2UC+3+SRd9nG3I2lPRvUw==", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, + "node_modules/fastify/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==", + "dependencies": { + "real-require": "^0.2.0" + } }, "node_modules/fastq": { "version": "1.17.1", @@ -8552,13 +8684,13 @@ } }, "node_modules/find-my-way": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-7.6.0.tgz", - "integrity": "sha512-H7berWdHJ+5CNVr4ilLWPai4ml7Y2qAsxjw3pfeBxPigZmaDTzF0wjJLj90xRCmGcWYcyt050yN+34OZDJm1eQ==", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-8.2.2.tgz", + "integrity": "sha512-Dobi7gcTEq8yszimcfp/R7+owiT4WncAJ7VTTgFH1jYJ5GaG1FbhjwDG820hptN0QDFvzVY3RfCzdInvGPGzjA==", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-querystring": "^1.0.0", - "safe-regex2": "^2.0.0" + "safe-regex2": "^3.1.0" }, "engines": { "node": ">=14" @@ -10008,6 +10140,14 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, + "node_modules/json-schema-ref-resolver": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-1.0.1.tgz", + "integrity": "sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + } + }, "node_modules/json-schema-resolver": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/json-schema-resolver/-/json-schema-resolver-2.0.0.tgz", @@ -10193,19 +10333,19 @@ } }, "node_modules/light-my-request": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.6.1.tgz", - "integrity": "sha512-sbJnC1UBRivi9L1kICr3CESb82pNiPNB3TvtdIrZZqW0Qh8uDXvoywMmWKZlihDcmw952CMICCzM+54LDf+E+g==", + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.13.0.tgz", + "integrity": "sha512-9IjUN9ZyCS9pTG+KqTDEQo68Sui2lHsYBrfMyVUTTZ3XhH8PMZq7xO94Kr+eP9dhi/kcKsx4N41p2IXEBil1pQ==", "dependencies": { - "cookie": "^0.5.0", - "process-warning": "^2.0.0", + "cookie": "^0.6.0", + "process-warning": "^3.0.0", "set-cookie-parser": "^2.4.1" } }, "node_modules/light-my-request/node_modules/process-warning": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.0.0.tgz", - "integrity": "sha512-+MmoAXoUX+VTHAlwns0h+kFUWFs/3FZy+ZuchkgjyOu3oioLAo2LB5aCfKPh2+P9O18i3m43tUEv3YqttSy0Ww==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", + "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==" }, "node_modules/lines-and-columns": { "version": "1.2.4", @@ -11388,6 +11528,14 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "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==", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/process-warning": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-1.0.0.tgz", @@ -11777,11 +11925,11 @@ } }, "node_modules/ret": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", - "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.4.3.tgz", + "integrity": "sha512-0f4Memo5QP7WQyUEAYUO3esD/XjOc3Zjjg5CPsAq1p8sIu0XPeMbHJemKA0BO7tV0X7+A0FoEpbmHXWxPyD3wQ==", "engines": { - "node": ">=4" + "node": ">=10" } }, "node_modules/retry": { @@ -11885,11 +12033,11 @@ ] }, "node_modules/safe-regex2": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-2.0.0.tgz", - "integrity": "sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-3.1.0.tgz", + "integrity": "sha512-RAAZAGbap2kBfbVhvmnTFv73NWLMvDGOITFYTZBAaY8eR+Ir4ef7Up/e7amo+y1+AH+3PtLkrt9mvcTsG9LXug==", "dependencies": { - "ret": "~0.2.0" + "ret": "~0.4.0" } }, "node_modules/safe-stable-stringify": { @@ -11950,9 +12098,9 @@ } }, "node_modules/set-cookie-parser": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.5.1.tgz", - "integrity": "sha512-1jeBGaKNGdEq4FgIrORu/N570dwoPYio8lSoYLWmX7sQ//0JY08Xh9o5pBcgmHQ/MbsYp/aZnOe1s1lIsbLprQ==" + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.0.tgz", + "integrity": "sha512-lXLOiqpkUumhRdFF3k1osNXCy9akgx/dyPZ5p8qAg9seJzXr5ZrlqZuWIMuY6ejOsVLE6flJ5/h3lsn57fQ/PQ==" }, "node_modules/setprototypeof": { "version": "1.2.0", @@ -12355,6 +12503,14 @@ "node": ">=8.0" } }, + "node_modules/toad-cache": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz", + "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==", + "engines": { + "node": ">=12" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -12692,16 +12848,16 @@ "integrity": "sha512-Tfay0l6gJMP5rkil8CzGbLthukn+9BN/VXWcABVFPjOoelJ+koW8BuPZYk+h/L+lEeIp1fSzVRiWRPIjKVjPdg==" }, "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", + "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/undici-types": { @@ -15257,16 +15413,24 @@ "integrity": "sha512-J8TOSBq3SoZbDhM9+R/u77hP93gz/rajSA+K2kGyijPpORPWUXHUpTaleoj+92As0S9uPRP7Oi8IqMf0u+ro6A==" }, "@fastify/error": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.0.0.tgz", - "integrity": "sha512-dPRyT40GiHRzSCll3/Jn2nPe25+E1VXc9tDwRAIKwFCxd5Np5wzgz1tmooWG3sV0qKgrBibihVoCna2ru4SEFg==" + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.4.1.tgz", + "integrity": "sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==" }, "@fastify/fast-json-stringify-compiler": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.2.0.tgz", - "integrity": "sha512-ypZynRvXA3dibfPykQN3RB5wBdEUgSGgny8Qc6k163wYPLD4mEGEDkACp+00YmqkGvIm8D/xYoHajwyEdWD/eg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.3.0.tgz", + "integrity": "sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==", + "requires": { + "fast-json-stringify": "^5.7.0" + } + }, + "@fastify/merge-json-schemas": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.1.1.tgz", + "integrity": "sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==", "requires": { - "fast-json-stringify": "^5.0.0" + "fast-deep-equal": "^3.1.3" } }, "@fastify/multipart": { @@ -18334,11 +18498,6 @@ "picomatch": "^2.0.4" } }, - "archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==" - }, "arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -18381,13 +18540,12 @@ "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==" }, "avvio": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/avvio/-/avvio-8.2.0.tgz", - "integrity": "sha512-bbCQdg7bpEv6kGH41RO/3B2/GMMmJSo2iBK+X8AWN9mujtfUipMDfIjsgHCfpnKqoGEQrrmCDKSa5OQ19+fDmg==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/avvio/-/avvio-8.4.0.tgz", + "integrity": "sha512-CDSwaxINFy59iNwhYnkvALBwZiTydGkOecZyPkqBpABYR1KqGEsET0VOOYDwtleZSUIdeY36DC2bSZ24CO1igA==", "requires": { - "archy": "^1.0.0", - "debug": "^4.0.0", - "fastq": "^1.6.1" + "@fastify/error": "^3.3.0", + "fastq": "^1.17.1" } }, "axios": { @@ -18794,9 +18952,9 @@ "dev": true }, "cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==" }, "create-require": { "version": "1.1.1", @@ -19311,9 +19469,9 @@ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, "fast-content-type-parse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-1.0.0.tgz", - "integrity": "sha512-Xbc4XcysUXcsP5aHUU7Nq3OwvHq97C+WnbkeIefpeYLX+ryzFJlU6OStFJhs6Ol0LkUGpcK+wL0JwfM+FCU5IA==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-1.1.0.tgz", + "integrity": "sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==" }, "fast-copy": { "version": "2.1.3", @@ -19362,16 +19520,27 @@ "dev": true }, "fast-json-stringify": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.6.2.tgz", - "integrity": "sha512-F6xkRrXvtGbAiDSEI5Rk7qk2P63Y9kc8bO6Dnsd3Rt6sBNr2QxNFWs0JbKftgiyOfGxnJaRoHe4SizCTqeAyrA==", + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.16.1.tgz", + "integrity": "sha512-KAdnLvy1yu/XrRtP+LJnxbBGrhN+xXu+gt3EUvZhYGKCr3lFHq/7UFJHHFgmJKoqlh6B40bZLEv7w46B0mqn1g==", "requires": { - "@fastify/deepmerge": "^1.0.0", + "@fastify/merge-json-schemas": "^0.1.0", "ajv": "^8.10.0", - "ajv-formats": "^2.1.1", + "ajv-formats": "^3.0.1", "fast-deep-equal": "^3.1.3", "fast-uri": "^2.1.0", + "json-schema-ref-resolver": "^1.0.1", "rfdc": "^1.2.0" + }, + "dependencies": { + "ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "requires": { + "ajv": "^8.0.0" + } + } } }, "fast-levenshtein": { @@ -19381,9 +19550,9 @@ "dev": true }, "fast-querystring": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.1.tgz", - "integrity": "sha512-qR2r+e3HvhEFmpdHMv//U8FnFlnYjaC6QKDuaXALDkw2kvHO8WDjxH+f/rHGR4Me4pnk8p9JAkRNTjYHAKRn2Q==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz", + "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", "requires": { "fast-decode-uri-component": "^1.0.1" } @@ -19412,31 +19581,113 @@ } }, "fastify": { - "version": "4.15.0", - "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.15.0.tgz", - "integrity": "sha512-m/CaRN8nf5uyYdrDe2qqq+0z3oGyE+A++qlKQoLJTI4WI0nWK9D6R3FxXQ3MVwt/md977GMR4F43pE9oqrS2zw==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.28.1.tgz", + "integrity": "sha512-kFWUtpNr4i7t5vY2EJPCN2KgMVpuqfU4NjnJNCgiNB900oiDeYqaNDRcAfeBbOF5hGixixxcKnOU4KN9z6QncQ==", "requires": { "@fastify/ajv-compiler": "^3.5.0", - "@fastify/error": "^3.0.0", - "@fastify/fast-json-stringify-compiler": "^4.2.0", + "@fastify/error": "^3.4.0", + "@fastify/fast-json-stringify-compiler": "^4.3.0", "abstract-logging": "^2.0.1", - "avvio": "^8.2.0", - "fast-content-type-parse": "^1.0.0", - "find-my-way": "^7.6.0", - "light-my-request": "^5.6.1", - "pino": "^8.5.0", - "process-warning": "^2.0.0", + "avvio": "^8.3.0", + "fast-content-type-parse": "^1.1.0", + "fast-json-stringify": "^5.8.0", + "find-my-way": "^8.0.0", + "light-my-request": "^5.11.0", + "pino": "^9.0.0", + "process-warning": "^3.0.0", "proxy-addr": "^2.0.7", "rfdc": "^1.3.0", - "secure-json-parse": "^2.5.0", - "semver": "^7.3.7", - "tiny-lru": "^10.0.0" + "secure-json-parse": "^2.7.0", + "semver": "^7.5.4", + "toad-cache": "^3.3.0" }, "dependencies": { + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "pino": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.4.0.tgz", + "integrity": "sha512-nbkQb5+9YPhQRz/BeQmrWpEknAaqjpAqRK8NwJpmrX/JHu7JuZC5G1CeAwJDJfGes4h+YihC6in3Q2nGb+Y09w==", + "requires": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.1.1", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^1.2.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^4.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" + }, + "dependencies": { + "process-warning": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.0.tgz", + "integrity": "sha512-/MyYDxttz7DfGMMHiysAsFE4qF+pQYAA8ziO/3NcRVrQ5fSk+Mns4QZA/oRPFzvcqNoVJXQNWNAsdwBXLUkQKw==" + } + } + }, + "pino-abstract-transport": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz", + "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==", + "requires": { + "readable-stream": "^4.0.0", + "split2": "^4.0.0" + } + }, + "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==" + }, "process-warning": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.0.0.tgz", - "integrity": "sha512-+MmoAXoUX+VTHAlwns0h+kFUWFs/3FZy+ZuchkgjyOu3oioLAo2LB5aCfKPh2+P9O18i3m43tUEv3YqttSy0Ww==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", + "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==" + }, + "readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "requires": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + } + }, + "real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==" + }, + "sonic-boom": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.1.0.tgz", + "integrity": "sha512-NGipjjRicyJJ03rPiZCJYjwlsuP2d1/5QUviozRXC7S3WdVWNK5e3Ojieb9CCyfhq2UC+3+SRd9nG3I2lPRvUw==", + "requires": { + "atomic-sleep": "^1.0.0" + } + }, + "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==", + "requires": { + "real-require": "^0.2.0" + } } } }, @@ -19506,13 +19757,13 @@ } }, "find-my-way": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-7.6.0.tgz", - "integrity": "sha512-H7berWdHJ+5CNVr4ilLWPai4ml7Y2qAsxjw3pfeBxPigZmaDTzF0wjJLj90xRCmGcWYcyt050yN+34OZDJm1eQ==", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-8.2.2.tgz", + "integrity": "sha512-Dobi7gcTEq8yszimcfp/R7+owiT4WncAJ7VTTgFH1jYJ5GaG1FbhjwDG820hptN0QDFvzVY3RfCzdInvGPGzjA==", "requires": { "fast-deep-equal": "^3.1.3", "fast-querystring": "^1.0.0", - "safe-regex2": "^2.0.0" + "safe-regex2": "^3.1.0" } }, "find-up": { @@ -20570,6 +20821,14 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, + "json-schema-ref-resolver": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-1.0.1.tgz", + "integrity": "sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, "json-schema-resolver": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/json-schema-resolver/-/json-schema-resolver-2.0.0.tgz", @@ -20698,19 +20957,19 @@ } }, "light-my-request": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.6.1.tgz", - "integrity": "sha512-sbJnC1UBRivi9L1kICr3CESb82pNiPNB3TvtdIrZZqW0Qh8uDXvoywMmWKZlihDcmw952CMICCzM+54LDf+E+g==", + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.13.0.tgz", + "integrity": "sha512-9IjUN9ZyCS9pTG+KqTDEQo68Sui2lHsYBrfMyVUTTZ3XhH8PMZq7xO94Kr+eP9dhi/kcKsx4N41p2IXEBil1pQ==", "requires": { - "cookie": "^0.5.0", - "process-warning": "^2.0.0", + "cookie": "^0.6.0", + "process-warning": "^3.0.0", "set-cookie-parser": "^2.4.1" }, "dependencies": { "process-warning": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.0.0.tgz", - "integrity": "sha512-+MmoAXoUX+VTHAlwns0h+kFUWFs/3FZy+ZuchkgjyOu3oioLAo2LB5aCfKPh2+P9O18i3m43tUEv3YqttSy0Ww==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", + "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==" } } }, @@ -21627,6 +21886,11 @@ "react-is": "^18.0.0" } }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" + }, "process-warning": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-1.0.0.tgz", @@ -21916,9 +22180,9 @@ "dev": true }, "ret": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", - "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==" + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.4.3.tgz", + "integrity": "sha512-0f4Memo5QP7WQyUEAYUO3esD/XjOc3Zjjg5CPsAq1p8sIu0XPeMbHJemKA0BO7tV0X7+A0FoEpbmHXWxPyD3wQ==" }, "retry": { "version": "0.13.1", @@ -21975,11 +22239,11 @@ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, "safe-regex2": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-2.0.0.tgz", - "integrity": "sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-3.1.0.tgz", + "integrity": "sha512-RAAZAGbap2kBfbVhvmnTFv73NWLMvDGOITFYTZBAaY8eR+Ir4ef7Up/e7amo+y1+AH+3PtLkrt9mvcTsG9LXug==", "requires": { - "ret": "~0.2.0" + "ret": "~0.4.0" } }, "safe-stable-stringify": { @@ -22024,9 +22288,9 @@ } }, "set-cookie-parser": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.5.1.tgz", - "integrity": "sha512-1jeBGaKNGdEq4FgIrORu/N570dwoPYio8lSoYLWmX7sQ//0JY08Xh9o5pBcgmHQ/MbsYp/aZnOe1s1lIsbLprQ==" + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.0.tgz", + "integrity": "sha512-lXLOiqpkUumhRdFF3k1osNXCy9akgx/dyPZ5p8qAg9seJzXr5ZrlqZuWIMuY6ejOsVLE6flJ5/h3lsn57fQ/PQ==" }, "setprototypeof": { "version": "1.2.0", @@ -22338,6 +22602,11 @@ "is-number": "^7.0.0" } }, + "toad-cache": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz", + "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==" + }, "toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -22556,9 +22825,9 @@ "integrity": "sha512-Tfay0l6gJMP5rkil8CzGbLthukn+9BN/VXWcABVFPjOoelJ+koW8BuPZYk+h/L+lEeIp1fSzVRiWRPIjKVjPdg==" }, "typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", + "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", "dev": true }, "undici-types": { diff --git a/package.json b/package.json index 8d0f1bc1..6a1eb9ed 100644 --- a/package.json +++ b/package.json @@ -55,9 +55,9 @@ "conventional-changelog-conventionalcommits": "^5.0.0", "crypto-js": "^4.2.0", "dotenv": "^16.0.0", - "fastify": "^4.8.1", + "fastify": "^4.28.1", "fastify-metrics": "^10.2.0", - "fastify-plugin": "^4.0.0", + "fastify-plugin": "^4.5.1", "fastify-xml-body-parser": "^2.2.0", "fs-extra": "^10.0.1", "fs-xattr": "0.3.1", @@ -113,7 +113,7 @@ "ts-node-dev": "^1.1.8", "tsx": "^4.16.0", "tus-js-client": "^3.1.0", - "typescript": "^4.5.5" + "typescript": "^5.6.2" }, "bin": "./dist/server.js" } diff --git a/src/http/plugins/storage.ts b/src/http/plugins/storage.ts index 27670ae0..8e286941 100644 --- a/src/http/plugins/storage.ts +++ b/src/http/plugins/storage.ts @@ -17,7 +17,7 @@ const storageBackend = createStorageBackend(storageBackendType) export const storage = fastifyPlugin( async function storagePlugin(fastify) { - fastify.decorateRequest('storage', undefined) + fastify.decorateRequest('storage', null) fastify.addHook('preHandler', async (request) => { const database = new StorageKnexDB(request.db, { tenantId: request.tenantId, diff --git a/src/http/plugins/tracing.ts b/src/http/plugins/tracing.ts index 25bb364e..eadf5f75 100644 --- a/src/http/plugins/tracing.ts +++ b/src/http/plugins/tracing.ts @@ -42,11 +42,11 @@ export const tracing = fastifyPlugin( const span = trace.getSpan(context.active()) if (span) { - // We collect logs only in full and logs mode + // We collect logs only in full,logs,debug mode if ( tracingEnabled && request.tracingMode && - !['full', 'logs'].includes(request.tracingMode) + !['full', 'logs', 'debug'].includes(request.tracingMode) ) { traceCollector.clearTrace(span.spanContext().traceId) } @@ -68,7 +68,7 @@ export const traceServerTime = fastifyPlugin( const spans = traceCollector.getSpansForTrace(traceId) if (spans) { try { - const serverTimingHeaders = spansToServerTimings(spans) + const serverTimingHeaders = spansToServerTimings(spans, reply.statusCode >= 500) request.serverTimings = serverTimingHeaders @@ -94,12 +94,15 @@ export const traceServerTime = fastifyPlugin( }) fastify.addHook('onRequestAbort', async (req) => { - const traceId = trace.getSpan(context.active())?.spanContext().traceId + const span = trace.getSpan(context.active()) + const traceId = span?.spanContext().traceId + + span?.setAttribute('req_aborted', true) if (traceId) { const spans = traceCollector.getSpansForTrace(traceId) if (spans) { - req.serverTimings = spansToServerTimings(spans) + req.serverTimings = spansToServerTimings(spans, true) } traceCollector.clearTrace(traceId) } @@ -155,6 +158,8 @@ function spansToServerTimings( spanName, duration, action: span.item.attributes['db.statement'], + error: span.item.attributes.error, + status: span.item.status, host: hostName ? isIP(hostName) ? hostName diff --git a/src/http/routes/object/deleteObject.ts b/src/http/routes/object/deleteObject.ts index 3ed06570..370a436f 100644 --- a/src/http/routes/object/deleteObject.ts +++ b/src/http/routes/object/deleteObject.ts @@ -1,5 +1,5 @@ import { FastifyInstance } from 'fastify' -import { FromSchema, JSONSchema } from 'json-schema-to-ts' +import { FromSchema } from 'json-schema-to-ts' import { createDefaultSchema, createResponse } from '../../routes-helper' import { AuthenticatedRequest } from '../../types' import { ROUTE_OPERATIONS } from '../operations' diff --git a/src/http/routes/object/updateObject.ts b/src/http/routes/object/updateObject.ts index 81511540..4b7474ac 100644 --- a/src/http/routes/object/updateObject.ts +++ b/src/http/routes/object/updateObject.ts @@ -77,6 +77,7 @@ export default async function routes(fastify: FastifyInstance) { .uploadOverridingObject(request, { owner, objectName: objectName, + signal: request.signals.body.signal, }) return response.status(objectMetadata?.httpStatusCode ?? 200).send({ diff --git a/src/http/routes/object/uploadSignedObject.ts b/src/http/routes/object/uploadSignedObject.ts index 4390b5aa..d4cdc110 100644 --- a/src/http/routes/object/uploadSignedObject.ts +++ b/src/http/routes/object/uploadSignedObject.ts @@ -94,6 +94,7 @@ export default async function routes(fastify: FastifyInstance) { owner, objectName, isUpsert: upsert, + signal: request.signals.body.signal, }) return response.status(objectMetadata?.httpStatusCode ?? 200).send({ diff --git a/src/http/routes/s3/commands/upload-part.ts b/src/http/routes/s3/commands/upload-part.ts index 76ef2705..2774caf4 100644 --- a/src/http/routes/s3/commands/upload-part.ts +++ b/src/http/routes/s3/commands/upload-part.ts @@ -97,16 +97,19 @@ export default function UploadPart(s3Router: S3Router) { const metadata = s3Protocol.parseMetadataHeaders(req.Headers) - return s3Protocol.putObject({ - Body: ctx.req as any, - Bucket: req.Params.Bucket, - Key: req.Params['*'], - CacheControl: req.Headers?.['cache-control'], - ContentType: req.Headers?.['content-type'], - Expires: req.Headers?.['expires'] ? new Date(req.Headers?.['expires']) : undefined, - ContentEncoding: req.Headers?.['content-encoding'], - Metadata: metadata, - }) + return s3Protocol.putObject( + { + Body: ctx.req as any, + Bucket: req.Params.Bucket, + Key: req.Params['*'], + CacheControl: req.Headers?.['cache-control'], + ContentType: req.Headers?.['content-type'], + Expires: req.Headers?.['expires'] ? new Date(req.Headers?.['expires']) : undefined, + ContentEncoding: req.Headers?.['content-encoding'], + Metadata: metadata, + }, + ctx.signals.body + ) } ) } diff --git a/src/http/routes/tus/index.ts b/src/http/routes/tus/index.ts index 6347ff6d..5967720a 100644 --- a/src/http/routes/tus/index.ts +++ b/src/http/routes/tus/index.ts @@ -59,7 +59,7 @@ type MultiPartRequest = http.IncomingMessage & { function createTusStore() { if (storageBackendType === 's3') { - const agent = createAgent(storageS3Endpoint?.includes('http://') ? 'http' : 'https') + const agent = createAgent('s3_tus') return new S3Store({ partSize: tusPartSize * 1024 * 1024, // Each uploaded part will have ${tusPartSize}MB, expirationPeriodInMilliseconds: tusUrlExpiryMs, diff --git a/src/internal/monitoring/metrics.ts b/src/internal/monitoring/metrics.ts index 4aecf22a..e71808ac 100644 --- a/src/internal/monitoring/metrics.ts +++ b/src/internal/monitoring/metrics.ts @@ -68,3 +68,28 @@ export const DbActiveConnection = new client.Gauge({ help: 'Number of database connections', labelNames: ['region', 'is_external'], }) + +// Create Prometheus metrics +export const HttpPoolSocketsGauge = new client.Gauge({ + name: 'storage_api_http_pool_busy_sockets', + help: 'Number of busy sockets currently in use', + labelNames: ['name', 'region', 'protocol'], +}) + +export const HttpPoolFreeSocketsGauge = new client.Gauge({ + name: 'storage_api_http_pool_free_sockets', + help: 'Number of free sockets available for reuse', + labelNames: ['name', 'region', 'protocol'], +}) + +export const HttpPoolPendingRequestsGauge = new client.Gauge({ + name: 'storage_api_http_pool_requests', + help: 'Number of pending requests waiting for a socket', + labelNames: ['name', 'region', 'protocol'], +}) + +export const HttpPoolErrorGauge = new client.Gauge({ + name: 'storage_api_http_pool_errors', + help: 'Number of pending requests waiting for a socket', + labelNames: ['name', 'region', 'type', 'protocol'], +}) diff --git a/src/internal/monitoring/otel-instrumentation.ts b/src/internal/monitoring/otel-instrumentation.ts index b79d0cdc..e5bf9bbd 100644 --- a/src/internal/monitoring/otel-instrumentation.ts +++ b/src/internal/monitoring/otel-instrumentation.ts @@ -108,6 +108,13 @@ class ClassInstrumentation implements Instrumentation { span.setStatus({ code: SpanStatusCode.OK }) return result } catch (error) { + if (error instanceof Error) { + span.setAttributes({ + error: JSON.stringify({ message: error.message, stack: error.stack }), + stack: error.stack, + }) + } + span.setStatus({ code: SpanStatusCode.ERROR, message: error instanceof Error ? error.message : String(error), diff --git a/src/internal/monitoring/otel.ts b/src/internal/monitoring/otel.ts index cb18d9b0..68e188cb 100644 --- a/src/internal/monitoring/otel.ts +++ b/src/internal/monitoring/otel.ts @@ -31,6 +31,7 @@ import { S3Backend } from '@storage/backend' import { StorageKnexDB } from '@storage/database' import { TenantConnection } from '@internal/database' import { S3Store } from '@tus/s3-store' +import { Upload } from '@aws-sdk/lib-storage' const tracingEnabled = process.env.TRACING_ENABLED === 'true' const headersEnv = process.env.OTEL_EXPORTER_OTLP_TRACES_HEADERS || '' @@ -239,6 +240,11 @@ const sdk = new NodeSDK({ }, setName: (name, attrs) => 'S3.' + attrs.operation, }), + new ClassInstrumentation({ + targetClass: Upload, + enabled: true, + methodsToInstrument: ['done', '__notifyProgress'], + }), getNodeAutoInstrumentations({ '@opentelemetry/instrumentation-http': { enabled: false, diff --git a/src/storage/backend/adapter.ts b/src/storage/backend/adapter.ts index 94fbfc5e..0aa3f109 100644 --- a/src/storage/backend/adapter.ts +++ b/src/storage/backend/adapter.ts @@ -82,7 +82,8 @@ export abstract class StorageBackendAdapter { version: string | undefined, body: NodeJS.ReadableStream, contentType: string, - cacheControl: string + cacheControl: string, + signal?: AbortSignal ): Promise { throw new Error('uploadObject not implemented') } @@ -172,7 +173,8 @@ export abstract class StorageBackendAdapter { uploadId: string, partNumber: number, body?: string | Uint8Array | Buffer | Readable, - length?: number + length?: number, + signal?: AbortSignal ): Promise<{ ETag?: string }> { throw new Error('not implemented') } diff --git a/src/storage/backend/s3.ts b/src/storage/backend/s3.ts index 53f8bbba..1663e486 100644 --- a/src/storage/backend/s3.ts +++ b/src/storage/backend/s3.ts @@ -30,23 +30,91 @@ import { ERRORS, StorageBackendError } from '@internal/errors' import { getConfig } from '../../config' import Agent, { HttpsAgent } from 'agentkeepalive' import { Readable } from 'stream' +import { + HttpPoolErrorGauge, + HttpPoolFreeSocketsGauge, + HttpPoolPendingRequestsGauge, + HttpPoolSocketsGauge, +} from '@internal/monitoring/metrics' + +const { storageS3MaxSockets, region } = getConfig() + +const watchers: NodeJS.Timeout[] = [] -const { storageS3MaxSockets } = getConfig() +process.once('SIGTERM', () => { + watchers.forEach((watcher) => { + clearInterval(watcher) + }) +}) /** * Creates an agent for the given protocol - * @param protocol + * @param name */ -export function createAgent(protocol: 'http' | 'https') { +export function createAgent(name: string) { const agentOptions = { maxSockets: storageS3MaxSockets, keepAlive: true, keepAliveMsecs: 1000, + freeSocketTimeout: 1000 * 15, + } + + const httpAgent = new Agent(agentOptions) + const httpsAgent = new HttpsAgent(agentOptions) + + if (httpsAgent) { + const watcher = setInterval(() => { + const httpStatus = httpAgent.getCurrentStatus() + const httpsStatus = httpsAgent.getCurrentStatus() + updateHttpPoolMetrics(name, 'http', httpStatus) + updateHttpPoolMetrics(name, 'https', httpsStatus) + }, 5000) + + watchers.push(watcher) + } + + return { httpAgent, httpsAgent } +} + +// Function to update Prometheus metrics based on the current status of the agent +function updateHttpPoolMetrics(name: string, protocol: string, status: Agent.AgentStatus): void { + // Calculate the number of busy sockets by iterating over the `sockets` object + let busySocketCount = 0 + for (const host in status.sockets) { + if (status.sockets.hasOwnProperty(host)) { + busySocketCount += status.sockets[host] + } } - return protocol === 'http' - ? { httpAgent: new Agent(agentOptions) } - : { httpsAgent: new HttpsAgent(agentOptions) } + // Calculate the number of free sockets by iterating over the `freeSockets` object + let freeSocketCount = 0 + for (const host in status.freeSockets) { + if (status.freeSockets.hasOwnProperty(host)) { + freeSocketCount += status.freeSockets[host] + } + } + + // Calculate the number of pending requests by iterating over the `requests` object + let pendingRequestCount = 0 + for (const host in status.requests) { + if (status.requests.hasOwnProperty(host)) { + pendingRequestCount += status.requests[host] + } + } + + // Update the metrics with calculated values + HttpPoolSocketsGauge.set({ name, region, protocol }, busySocketCount) + HttpPoolFreeSocketsGauge.set({ name, region, protocol }, freeSocketCount) + HttpPoolPendingRequestsGauge.set({ name, region }, pendingRequestCount) + HttpPoolErrorGauge.set({ name, region, type: 'socket_error', protocol }, status.errorSocketCount) + HttpPoolErrorGauge.set( + { name, region, type: 'timeout_socket_error', protocol }, + status.timeoutSocketCount + ) + HttpPoolErrorGauge.set( + { name, region, type: 'create_socket_error', protocol }, + status.createSocketErrorCount + ) } export interface S3ClientOptions { @@ -56,7 +124,7 @@ export interface S3ClientOptions { accessKey?: string secretKey?: string role?: string - httpAgent?: { httpAgent: Agent } | { httpsAgent: HttpsAgent } + httpAgent?: { httpAgent: Agent; httpsAgent: HttpsAgent } requestTimeout?: number downloadTimeout?: number uploadTimeout?: number @@ -75,18 +143,21 @@ export class S3Backend implements StorageBackendAdapter { // Default client for API operations this.client = this.createS3Client({ ...options, + name: 's3_default', requestTimeout: options.requestTimeout, }) // Upload client exclusively for upload operations this.uploadClient = this.createS3Client({ ...options, + name: 's3_upload', requestTimeout: options.uploadTimeout, }) // Download client exclusively for download operations this.downloadClient = this.createS3Client({ ...options, + name: 's3_download', requestTimeout: options.downloadTimeout, }) } @@ -144,6 +215,7 @@ export class S3Backend implements StorageBackendAdapter { * @param body * @param contentType * @param cacheControl + * @param signal */ async uploadObject( bucketName: string, @@ -151,7 +223,8 @@ export class S3Backend implements StorageBackendAdapter { version: string | undefined, body: NodeJS.ReadableStream, contentType: string, - cacheControl: string + cacheControl: string, + signal?: AbortSignal ): Promise { try { const paralellUploadS3 = new Upload({ @@ -166,6 +239,14 @@ export class S3Backend implements StorageBackendAdapter { }, }) + signal?.addEventListener( + 'abort', + () => { + paralellUploadS3.abort() + }, + { once: true } + ) + const data = (await paralellUploadS3.done()) as CompleteMultipartUploadCommandOutput const metadata = await this.headObject(bucketName, key, version) @@ -451,10 +532,8 @@ export class S3Backend implements StorageBackendAdapter { } } - protected createS3Client(options: S3ClientOptions) { - const storageS3Protocol = options.endpoint?.includes('http://') ? 'http' : 'https' - - const agent = options.httpAgent ? options.httpAgent : createAgent(storageS3Protocol) + protected createS3Client(options: S3ClientOptions & { name: string }) { + const agent = options.httpAgent ?? createAgent(options.name) const params: S3ClientConfig = { region: options.region, diff --git a/src/storage/events/base-event.ts b/src/storage/events/base-event.ts index 0c31b5ee..6051b8ca 100644 --- a/src/storage/events/base-event.ts +++ b/src/storage/events/base-event.ts @@ -5,10 +5,11 @@ import { createAgent, createStorageBackend } from '../backend' import { Storage } from '../storage' import { getConfig } from '../../config' import { logger } from '@internal/monitoring' +import Agent, { HttpsAgent } from 'agentkeepalive' -const { storageBackendType, storageS3Endpoint, region } = getConfig() -const storageS3Protocol = storageS3Endpoint?.includes('http://') ? 'http' : 'https' -const httpAgent = createAgent(storageS3Protocol) +const { storageBackendType, region } = getConfig() + +let httpAgent: { httpAgent: Agent; httpsAgent: HttpsAgent } | undefined export abstract class BaseEvent> extends QueueBaseEvent { /** @@ -51,6 +52,14 @@ export abstract class BaseEvent> extends } } + protected static getAgent() { + if (httpAgent) { + return httpAgent + } + httpAgent = createAgent('s3_worker') + return httpAgent + } + protected static async createStorage(payload: BasePayload) { const adminUser = await getServiceKeyUser(payload.tenant.ref) @@ -68,7 +77,7 @@ export abstract class BaseEvent> extends }) const storageBackend = createStorageBackend(storageBackendType, { - httpAgent, + httpAgent: BaseEvent.getAgent(), }) return new Storage(storageBackend, db) diff --git a/src/storage/object.ts b/src/storage/object.ts index 1948da64..290ec040 100644 --- a/src/storage/object.ts +++ b/src/storage/object.ts @@ -23,6 +23,7 @@ export interface UploadObjectOptions { owner?: string isUpsert?: boolean version?: string + signal?: AbortSignal } const { requestUrlLengthLimit, storageS3Bucket } = getConfig() diff --git a/src/storage/protocols/s3/s3-handler.ts b/src/storage/protocols/s3/s3-handler.ts index f164b32d..c3ad826c 100644 --- a/src/storage/protocols/s3/s3-handler.ts +++ b/src/storage/protocols/s3/s3-handler.ts @@ -579,8 +579,13 @@ export class S3ProtocolHandler { * * Reference: https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPart.html * @param command + * @param signal */ - async uploadPart(command: UploadPartCommandInput) { + async uploadPart(command: UploadPartCommandInput, signal?: AbortSignal) { + if (signal?.aborted) { + throw ERRORS.Aborted('UploadPart aborted') + } + const { Bucket, PartNumber, UploadId, Key, Body, ContentLength } = command if (!UploadId) { @@ -608,6 +613,10 @@ export class S3ProtocolHandler { const multipart = await this.shouldAllowPartUpload(UploadId, ContentLength, maxFileSize) + if (signal?.aborted) { + throw ERRORS.Aborted('UploadPart aborted') + } + const proxy = new PassThrough() if (Body instanceof Readable) { @@ -636,7 +645,8 @@ export class S3ProtocolHandler { UploadId, PartNumber || 0, stream as Readable, - ContentLength + ContentLength, + signal ) } ) @@ -677,8 +687,9 @@ export class S3ProtocolHandler { * Reference: https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html * * @param command + * @param signal */ - async putObject(command: PutObjectCommandInput) { + async putObject(command: PutObjectCommandInput, signal?: AbortSignal) { const uploader = new Uploader(this.storage.backend, this.storage.db) mustBeValidBucketName(command.Bucket) @@ -705,6 +716,7 @@ export class S3ProtocolHandler { fileSizeLimit: bucket.file_size_limit, allowedMimeTypes: bucket.allowed_mime_types, metadata: command.Metadata, + signal, }) return { diff --git a/src/storage/uploader.ts b/src/storage/uploader.ts index 776c0dd6..aff4ea4e 100644 --- a/src/storage/uploader.ts +++ b/src/storage/uploader.ts @@ -15,6 +15,7 @@ interface UploaderOptions extends UploadObjectOptions { fileSizeLimit?: number | null allowedMimeTypes?: string[] | null metadata?: Record + signal?: AbortSignal } const { storageS3Bucket, uploadFileSizeLimitStandard } = getConfig() @@ -106,7 +107,8 @@ export class Uploader { version, file.body, file.mimeType, - file.cacheControl + file.cacheControl, + options.signal ) if (file.isTruncated()) { diff --git a/src/test/object.test.ts b/src/test/object.test.ts index c7094c13..bb941a80 100644 --- a/src/test/object.test.ts +++ b/src/test/object.test.ts @@ -109,7 +109,7 @@ describe('testing GET object', () => { expect(response.statusCode).toBe(200) expect(response.headers['etag']).toBe('abc') expect(response.headers['last-modified']).toBe('Wed, 12 Oct 2022 11:17:02 GMT') - expect(response.headers['content-length']).toBe(3746) + expect(response.headers['content-length']).toBe('3746') expect(response.headers['cache-control']).toBe('no-cache') }) @@ -124,7 +124,7 @@ describe('testing GET object', () => { expect(response.statusCode).toBe(200) expect(response.headers['etag']).toBe('abc') expect(response.headers['last-modified']).toBe('Wed, 12 Oct 2022 11:17:02 GMT') - expect(response.headers['content-length']).toBe(3746) + expect(response.headers['content-length']).toBe('3746') expect(response.headers['cache-control']).toBe('no-cache') }) @@ -147,7 +147,7 @@ describe('testing GET object', () => { expect(response.statusCode).toBe(200) expect(response.headers['etag']).toBe('abc') expect(response.headers['last-modified']).toBe('Wed, 12 Oct 2022 11:17:02 GMT') - expect(response.headers['content-length']).toBe(3746) + expect(response.headers['content-length']).toBe('3746') expect(response.headers['cache-control']).toBe('no-cache') }) @@ -162,7 +162,7 @@ describe('testing GET object', () => { expect(response.statusCode).toBe(200) expect(response.headers['etag']).toBe('abc') expect(response.headers['last-modified']).toBe('Wed, 12 Oct 2022 11:17:02 GMT') - expect(response.headers['content-length']).toBe(3746) + expect(response.headers['content-length']).toBe('3746') expect(response.headers['cache-control']).toBe('no-cache') })