From eca52db3815267552b86023f071645c6f310b801 Mon Sep 17 00:00:00 2001 From: AlbanSdl Date: Fri, 19 Jul 2024 23:31:36 +0200 Subject: [PATCH 01/14] fix: update ue seeding in order to use csv source --- .gitignore | 2 + package.json | 43 +- pnpm-lock.yaml | 2456 ++++++++++++++++++++---------------------- prisma/schema.prisma | 2 +- scripts/seed/base.ts | 46 + scripts/seed/ue.ts | 126 +++ 6 files changed, 1376 insertions(+), 1299 deletions(-) create mode 100644 scripts/seed/base.ts create mode 100644 scripts/seed/ue.ts diff --git a/.gitignore b/.gitignore index d46849a..98b445c 100644 --- a/.gitignore +++ b/.gitignore @@ -113,3 +113,5 @@ docs/build/ # OLD DATABASE, DO NOT PUSH, IT CONTAINS SENSITIVE DATA migration/etuutt_old/etuutt_old.sql +# UE Data +scripts/**/*.csv \ No newline at end of file diff --git a/package.json b/package.json index e36f961..747942a 100644 --- a/package.json +++ b/package.json @@ -25,39 +25,44 @@ "test:cov": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest --coverage --config ./test/jest.json --runInBand", "test:instantfail": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest --config test/jest.json --runInBand --testNamePattern --bail", "test:db:reset": "env-cmd -f .env.test --use-shell \"pnpm prisma migrate reset --force && pnpm prisma db push\"", - "test:db:editor": "env-cmd -f .env.test -- pnpm prisma studio" + "test:db:editor": "env-cmd -f .env.test -- pnpm prisma studio", + "seed:base": "env-cmd -f .env.dev -- ts-node scripts/seed/base.ts", + "seed:ue": "env-cmd -f .env.dev -- ts-node scripts/seed/ue.ts", + "seed:base:prod": "node scripts/seed/base.js", + "seed:ue:prod": "node scripts/seed/ue.js" }, "dependencies": { + "@fast-csv/parse": "^5.0.0", "@nestjs/axios": "^3.0.2", - "@nestjs/common": "^10.3.0", - "@nestjs/config": "^3.1.1", - "@nestjs/core": "^10.3.0", + "@nestjs/common": "^10.3.10", + "@nestjs/config": "^3.2.3", + "@nestjs/core": "^10.3.10", "@nestjs/jwt": "^10.2.0", "@nestjs/passport": "^10.0.3", - "@nestjs/platform-express": "^10.3.0", - "@nestjs/swagger": "^7.2.0", - "@prisma/client": "^5.8.1", - "axios": "^1.6.7", + "@nestjs/platform-express": "^10.3.10", + "@nestjs/swagger": "^7.4.0", + "@prisma/client": "^5.17.0", + "axios": "^1.7.2", "bcryptjs": "^2.4.3", "class-transformer": "^0.5.1", "class-validator": "^0.14.1", - "fast-xml-parser": "^4.3.6", - "file-type": "^19.0.0", - "ldapts": "^7.0.10", + "fast-xml-parser": "^4.4.0", + "file-type": "^19.2.0", + "ldapts": "^7.1.0", "multer": "1.4.5-lts.1", - "pactum-matchers": "^1.1.6", + "pactum-matchers": "^1.1.7", "passport-jwt": "^4.0.1", "pdfkit": "^0.14.0", - "prisma": "^5.8.1", + "prisma": "^5.17.0", "reflect-metadata": "^0.1.14", "rxjs": "^7.8.1", - "sharp": "^0.33.2" + "sharp": "^0.33.4" }, "devDependencies": { "@faker-js/faker": "^7.6.0", - "@nestjs/cli": "^10.3.0", - "@nestjs/schematics": "^10.1.0", - "@nestjs/testing": "^10.3.0", + "@nestjs/cli": "^10.4.2", + "@nestjs/schematics": "^10.1.2", + "@nestjs/testing": "^10.3.10", "@types/bcryptjs": "^2.4.6", "@types/express": "^4.17.21", "@types/jest": "29.5.0", @@ -70,14 +75,14 @@ "@typescript-eslint/parser": "^5.62.0", "cz-emoji": "1.3.2-canary.2", "env-cmd": "^10.1.0", - "eslint": "^8.56.0", + "eslint": "^8.57.0", "eslint-config-prettier": "^8.10.0", "eslint-plugin-prettier": "^4.2.1", "jest": "29.5.0", "ldap-server-mock": "^6.0.1", "mysql": "^2.18.1", "nock": "^13.5.4", - "pactum": "^3.6.0", + "pactum": "^3.7.0", "prettier": "^2.8.8", "source-map-support": "^0.5.21", "ts-jest": "29.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5b63b9f..39ed627 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,36 +8,39 @@ importers: .: dependencies: + '@fast-csv/parse': + specifier: ^5.0.0 + version: 5.0.0 '@nestjs/axios': specifier: ^3.0.2 - version: 3.0.2(@nestjs/common@10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(axios@1.6.7)(rxjs@7.8.1) + version: 3.0.2(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(axios@1.7.2)(rxjs@7.8.1) '@nestjs/common': - specifier: ^10.3.0 - version: 10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + specifier: ^10.3.10 + version: 10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) '@nestjs/config': - specifier: ^3.1.1 - version: 3.1.1(@nestjs/common@10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(reflect-metadata@0.1.14) + specifier: ^3.2.3 + version: 3.2.3(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(rxjs@7.8.1) '@nestjs/core': - specifier: ^10.3.0 - version: 10.3.1(@nestjs/common@10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@10.3.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + specifier: ^10.3.10 + version: 10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10)(reflect-metadata@0.1.14)(rxjs@7.8.1) '@nestjs/jwt': specifier: ^10.2.0 - version: 10.2.0(@nestjs/common@10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1)) + version: 10.2.0(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1)) '@nestjs/passport': specifier: ^10.0.3 - version: 10.0.3(@nestjs/common@10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(passport@0.6.0) + version: 10.0.3(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(passport@0.7.0) '@nestjs/platform-express': - specifier: ^10.3.0 - version: 10.3.1(@nestjs/common@10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@10.3.1) + specifier: ^10.3.10 + version: 10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@10.3.10) '@nestjs/swagger': - specifier: ^7.2.0 - version: 7.2.0(@nestjs/common@10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@10.3.1(@nestjs/common@10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@10.3.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14) + specifier: ^7.4.0 + version: 7.4.0(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10)(reflect-metadata@0.1.14)(rxjs@7.8.1))(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14) '@prisma/client': - specifier: ^5.8.1 - version: 5.8.1(prisma@5.8.1) + specifier: ^5.17.0 + version: 5.17.0(prisma@5.17.0) axios: - specifier: ^1.6.7 - version: 1.6.7 + specifier: ^1.7.2 + version: 1.7.2 bcryptjs: specifier: ^2.4.3 version: 2.4.3 @@ -48,20 +51,20 @@ importers: specifier: ^0.14.1 version: 0.14.1 fast-xml-parser: - specifier: ^4.3.6 - version: 4.3.6 + specifier: ^4.4.0 + version: 4.4.0 file-type: - specifier: ^19.0.0 - version: 19.0.0 + specifier: ^19.2.0 + version: 19.2.0 ldapts: - specifier: ^7.0.10 - version: 7.0.12 + specifier: ^7.1.0 + version: 7.1.0 multer: specifier: 1.4.5-lts.1 version: 1.4.5-lts.1 pactum-matchers: - specifier: ^1.1.6 - version: 1.1.6 + specifier: ^1.1.7 + version: 1.1.7 passport-jwt: specifier: ^4.0.1 version: 4.0.1 @@ -69,8 +72,8 @@ importers: specifier: ^0.14.0 version: 0.14.0 prisma: - specifier: ^5.8.1 - version: 5.8.1 + specifier: ^5.17.0 + version: 5.17.0 reflect-metadata: specifier: ^0.1.14 version: 0.1.14 @@ -78,21 +81,21 @@ importers: specifier: ^7.8.1 version: 7.8.1 sharp: - specifier: ^0.33.2 - version: 0.33.2 + specifier: ^0.33.4 + version: 0.33.4 devDependencies: '@faker-js/faker': specifier: ^7.6.0 version: 7.6.0 '@nestjs/cli': - specifier: ^10.3.0 - version: 10.3.0 + specifier: ^10.4.2 + version: 10.4.2 '@nestjs/schematics': - specifier: ^10.1.0 - version: 10.1.0(chokidar@3.5.3)(typescript@4.9.5) + specifier: ^10.1.2 + version: 10.1.2(chokidar@3.6.0)(typescript@4.9.5) '@nestjs/testing': - specifier: ^10.3.0 - version: 10.3.1(@nestjs/common@10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@10.3.1(@nestjs/common@10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@10.3.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@10.3.1(@nestjs/common@10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@10.3.1)) + specifier: ^10.3.10 + version: 10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@10.3.10)) '@types/bcryptjs': specifier: ^2.4.6 version: 2.4.6 @@ -119,10 +122,10 @@ importers: version: 0.13.4 '@typescript-eslint/eslint-plugin': specifier: ^5.62.0 - version: 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.56.0)(typescript@4.9.5))(eslint@8.56.0)(typescript@4.9.5) + version: 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@4.9.5))(eslint@8.57.0)(typescript@4.9.5) '@typescript-eslint/parser': specifier: ^5.62.0 - version: 5.62.0(eslint@8.56.0)(typescript@4.9.5) + version: 5.62.0(eslint@8.57.0)(typescript@4.9.5) cz-emoji: specifier: 1.3.2-canary.2 version: 1.3.2-canary.2 @@ -130,14 +133,14 @@ importers: specifier: ^10.1.0 version: 10.1.0 eslint: - specifier: ^8.56.0 - version: 8.56.0 + specifier: ^8.57.0 + version: 8.57.0 eslint-config-prettier: specifier: ^8.10.0 - version: 8.10.0(eslint@8.56.0) + version: 8.10.0(eslint@8.57.0) eslint-plugin-prettier: specifier: ^4.2.1 - version: 4.2.1(eslint-config-prettier@8.10.0(eslint@8.56.0))(eslint@8.56.0)(prettier@2.8.8) + version: 4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8) jest: specifier: 29.5.0 version: 29.5.0(@types/node@18.15.11)(ts-node@10.9.2(@types/node@18.15.11)(typescript@4.9.5)) @@ -151,8 +154,8 @@ importers: specifier: ^13.5.4 version: 13.5.4 pactum: - specifier: ^3.6.0 - version: 3.6.0 + specifier: ^3.7.0 + version: 3.7.0 prettier: specifier: ^2.8.8 version: 2.8.8 @@ -161,10 +164,10 @@ importers: version: 0.5.21 ts-jest: specifier: 29.1.0 - version: 29.1.0(@babel/core@7.23.6)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.6))(jest@29.5.0(@types/node@18.15.11)(ts-node@10.9.2(@types/node@18.15.11)(typescript@4.9.5)))(typescript@4.9.5) + version: 29.1.0(@babel/core@7.24.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.9))(jest@29.5.0(@types/node@18.15.11)(ts-node@10.9.2(@types/node@18.15.11)(typescript@4.9.5)))(typescript@4.9.5) ts-loader: specifier: ^9.5.1 - version: 9.5.1(typescript@4.9.5)(webpack@5.89.0) + version: 9.5.1(typescript@4.9.5)(webpack@5.92.1) ts-node: specifier: ^10.9.2 version: 10.9.2(@types/node@18.15.11)(typescript@4.9.5) @@ -177,16 +180,12 @@ importers: packages: - '@aashutoshrathi/word-wrap@1.2.6': - resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} - engines: {node: '>=0.10.0'} - - '@ampproject/remapping@2.2.1': - resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@angular-devkit/core@17.0.9': - resolution: {integrity: sha512-r5jqwpWOgowqe9KSDqJ3iSbmsEt2XPjSvRG4DSI2T9s31bReoMtreo8b7wkRa2B3hbcDnstFbn8q27VvJDqRaQ==} + '@angular-devkit/core@17.3.8': + resolution: {integrity: sha512-Q8q0voCGudbdCgJ7lXdnyaxKHbNQBARH68zPQV72WT8NWy+Gw/tys870i6L58NWbBaCJEUcIj/kb6KoakSRu+Q==} engines: {node: ^18.13.0 || >=20.9.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: chokidar: ^3.5.2 @@ -194,95 +193,95 @@ packages: chokidar: optional: true - '@angular-devkit/schematics-cli@17.0.9': - resolution: {integrity: sha512-tznzzB26sy8jVUlV9HhXcbFYZcIIFMAiDMOuyLko2LZFjfoqW+OPvwa1mwAQwvVVSQZVAKvdndFhzwyl/axwFQ==} + '@angular-devkit/schematics-cli@17.3.8': + resolution: {integrity: sha512-TjmiwWJarX7oqvNiRAroQ5/LeKUatxBOCNEuKXO/PV8e7pn/Hr/BqfFm+UcYrQoFdZplmtNAfqmbqgVziKvCpA==} engines: {node: ^18.13.0 || >=20.9.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} hasBin: true - '@angular-devkit/schematics@17.0.9': - resolution: {integrity: sha512-5ti7g45F2KjDJS0DbgnOGI1GyKxGpn4XsKTYJFJrSAWj6VpuvPy/DINRrXNuRVo09VPEkqA+IW7QwaG9icptQg==} + '@angular-devkit/schematics@17.3.8': + resolution: {integrity: sha512-QRVEYpIfgkprNHc916JlPuNbLzOgrm9DZalHasnLUz4P6g7pR21olb8YCyM2OTJjombNhya9ZpckcADU5Qyvlg==} engines: {node: ^18.13.0 || >=20.9.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} '@arr/every@1.0.1': resolution: {integrity: sha512-UQFQ6SgyJ6LX42W8rHCs8KVc0JS0tzVL9ct4XYedJukskYVWTo49tNiMEK9C2HTyarbNiT/RVIRSY82vH+6sTg==} engines: {node: '>=4'} - '@babel/code-frame@7.23.5': - resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==} + '@babel/code-frame@7.24.7': + resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.23.5': - resolution: {integrity: sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==} + '@babel/compat-data@7.24.9': + resolution: {integrity: sha512-e701mcfApCJqMMueQI0Fb68Amflj83+dvAvHawoBpAz+GDjCIyGHzNwnefjsWJ3xiYAqqiQFoWbspGYBdb2/ng==} engines: {node: '>=6.9.0'} - '@babel/core@7.23.6': - resolution: {integrity: sha512-FxpRyGjrMJXh7X3wGLGhNDCRiwpWEF74sKjTLDJSG5Kyvow3QZaG0Adbqzi9ZrVjTWpsX+2cxWXD71NMg93kdw==} + '@babel/core@7.24.9': + resolution: {integrity: sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg==} engines: {node: '>=6.9.0'} - '@babel/generator@7.23.6': - resolution: {integrity: sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==} + '@babel/generator@7.24.10': + resolution: {integrity: sha512-o9HBZL1G2129luEUlG1hB4N/nlYNWHnpwlND9eOMclRqqu1YDy2sSYVCFUZwl8I1Gxh+QSRrP2vD7EpUmFVXxg==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.23.6': - resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==} + '@babel/helper-compilation-targets@7.24.8': + resolution: {integrity: sha512-oU+UoqCHdp+nWVDkpldqIQL/i/bvAv53tRqLG/s+cOXxe66zOYLU7ar/Xs3LdmBihrUMEUhwu6dMZwbNOYDwvw==} engines: {node: '>=6.9.0'} - '@babel/helper-environment-visitor@7.22.20': - resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} + '@babel/helper-environment-visitor@7.24.7': + resolution: {integrity: sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==} engines: {node: '>=6.9.0'} - '@babel/helper-function-name@7.23.0': - resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} + '@babel/helper-function-name@7.24.7': + resolution: {integrity: sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==} engines: {node: '>=6.9.0'} - '@babel/helper-hoist-variables@7.22.5': - resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} + '@babel/helper-hoist-variables@7.24.7': + resolution: {integrity: sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==} engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.22.15': - resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} + '@babel/helper-module-imports@7.24.7': + resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} engines: {node: '>=6.9.0'} - '@babel/helper-module-transforms@7.23.3': - resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} + '@babel/helper-module-transforms@7.24.9': + resolution: {integrity: sha512-oYbh+rtFKj/HwBQkFlUzvcybzklmVdVV3UU+mN7n2t/q3yGHbuVdNxyFvSBO1tfvjyArpHNcWMAzsSPdyI46hw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-plugin-utils@7.22.5': - resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} + '@babel/helper-plugin-utils@7.24.8': + resolution: {integrity: sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==} engines: {node: '>=6.9.0'} - '@babel/helper-simple-access@7.22.5': - resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} + '@babel/helper-simple-access@7.24.7': + resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} engines: {node: '>=6.9.0'} - '@babel/helper-split-export-declaration@7.22.6': - resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} + '@babel/helper-split-export-declaration@7.24.7': + resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==} engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.23.4': - resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==} + '@babel/helper-string-parser@7.24.8': + resolution: {integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.22.20': - resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} + '@babel/helper-validator-identifier@7.24.7': + resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.23.5': - resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==} + '@babel/helper-validator-option@7.24.8': + resolution: {integrity: sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.23.6': - resolution: {integrity: sha512-wCfsbN4nBidDRhpDhvcKlzHWCTlgJYUUdSJfzXb2NuBssDSIjc3xcb+znA7l+zYsFljAcGM0aFkN40cR3lXiGA==} + '@babel/helpers@7.24.8': + resolution: {integrity: sha512-gV2265Nkcz7weJJfvDoAEVzC1e2OTDpkGbEsebse8koXUJUXPsCMi7sRo/+SPMuMZ9MtUPnGwITTnQnU5YjyaQ==} engines: {node: '>=6.9.0'} - '@babel/highlight@7.23.4': - resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==} + '@babel/highlight@7.24.7': + resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} engines: {node: '>=6.9.0'} - '@babel/parser@7.23.6': - resolution: {integrity: sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==} + '@babel/parser@7.24.8': + resolution: {integrity: sha512-WzfbgXOkGzZiXXCqk43kKwZjzwx4oulxZi3nq2TYL9mOjQv6kYwul9mz6ID36njuL7Xkp6nJEfok848Zj10j/w==} engines: {node: '>=6.0.0'} hasBin: true @@ -311,8 +310,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-jsx@7.23.3': - resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==} + '@babel/plugin-syntax-jsx@7.24.7': + resolution: {integrity: sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -353,22 +352,22 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-typescript@7.23.3': - resolution: {integrity: sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==} + '@babel/plugin-syntax-typescript@7.24.7': + resolution: {integrity: sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/template@7.22.15': - resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} + '@babel/template@7.24.7': + resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.23.6': - resolution: {integrity: sha512-czastdK1e8YByZqezMPFiZ8ahwVMh/ESl9vPgvgdB9AmFMGP5jfpFax74AQgl5zj4XHzqeYAg2l8PuUeRS1MgQ==} + '@babel/traverse@7.24.8': + resolution: {integrity: sha512-t0P1xxAPzEDcEPmjprAQq19NWum4K0EQPjMwZQZbHt+GiZqvjCHjj755Weq1YRPVzBI+3zSfvScfpnuIecVFJQ==} engines: {node: '>=6.9.0'} - '@babel/types@7.23.6': - resolution: {integrity: sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==} + '@babel/types@7.24.9': + resolution: {integrity: sha512-xm8XrMKz0IlUdocVbYJe0Z9xEgidU7msskG8BbhnTPK/HZ2z/7FP7ykqPgrUH+C+r414mNfNWam1f2vqOjqjYQ==} engines: {node: '>=6.9.0'} '@bcoe/v8-coverage@0.2.3': @@ -382,8 +381,8 @@ packages: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} - '@emnapi/runtime@0.45.0': - resolution: {integrity: sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==} + '@emnapi/runtime@1.2.0': + resolution: {integrity: sha512-bV21/9LQmcQeCPEg3BDFtvwL6cwiTMksYNWQQ4KOxCZikEGalWtenoZ0wCiukJINlGCIi2KXx01g4FoH/LxpzQ==} '@eslint-community/eslint-utils@4.4.0': resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} @@ -391,16 +390,16 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/regexpp@4.10.0': - resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} + '@eslint-community/regexpp@4.11.0': + resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} '@eslint/eslintrc@2.1.4': resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - '@eslint/js@8.56.0': - resolution: {integrity: sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==} + '@eslint/js@8.57.0': + resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} '@exodus/schemasafe@1.3.0': @@ -410,126 +409,131 @@ packages: resolution: {integrity: sha512-XK6BTq1NDMo9Xqw/YkYyGjSsg44fbNwYRx7QK2CuoQgyy+f1rrTDHoExVM5PsyXCtfl2vs2vVJ0MN0yN6LppRw==} engines: {node: '>=14.0.0', npm: '>=6.0.0'} - '@humanwhocodes/config-array@0.11.13': - resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==} + '@fast-csv/parse@5.0.0': + resolution: {integrity: sha512-ecF8tCm3jVxeRjEB6VPzmA+1wGaJ5JgaUX2uesOXdXD6qQp0B3EdshOIed4yT1Xlj/F2f8v4zHSo0Oi31L697g==} + + '@humanwhocodes/config-array@0.11.14': + resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} - '@humanwhocodes/object-schema@2.0.1': - resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==} + '@humanwhocodes/object-schema@2.0.3': + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + deprecated: Use @eslint/object-schema instead - '@img/sharp-darwin-arm64@0.33.2': - resolution: {integrity: sha512-itHBs1rPmsmGF9p4qRe++CzCgd+kFYktnsoR1sbIAfsRMrJZau0Tt1AH9KVnufc2/tU02Gf6Ibujx+15qRE03w==} + '@img/sharp-darwin-arm64@0.33.4': + resolution: {integrity: sha512-p0suNqXufJs9t3RqLBO6vvrgr5OhgbWp76s5gTRvdmxmuv9E1rcaqGUsl3l4mKVmXPkTkTErXediAui4x+8PSA==} engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [arm64] os: [darwin] - '@img/sharp-darwin-x64@0.33.2': - resolution: {integrity: sha512-/rK/69Rrp9x5kaWBjVN07KixZanRr+W1OiyKdXcbjQD6KbW+obaTeBBtLUAtbBsnlTTmWthw99xqoOS7SsySDg==} + '@img/sharp-darwin-x64@0.33.4': + resolution: {integrity: sha512-0l7yRObwtTi82Z6ebVI2PnHT8EB2NxBgpK2MiKJZJ7cz32R4lxd001ecMhzzsZig3Yv9oclvqqdV93jo9hy+Dw==} engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [x64] os: [darwin] - '@img/sharp-libvips-darwin-arm64@1.0.1': - resolution: {integrity: sha512-kQyrSNd6lmBV7O0BUiyu/OEw9yeNGFbQhbxswS1i6rMDwBBSX+e+rPzu3S+MwAiGU3HdLze3PanQ4Xkfemgzcw==} + '@img/sharp-libvips-darwin-arm64@1.0.2': + resolution: {integrity: sha512-tcK/41Rq8IKlSaKRCCAuuY3lDJjQnYIW1UXU1kxcEKrfL8WR7N6+rzNoOxoQRJWTAECuKwgAHnPvqXGN8XfkHA==} engines: {macos: '>=11', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [arm64] os: [darwin] - '@img/sharp-libvips-darwin-x64@1.0.1': - resolution: {integrity: sha512-eVU/JYLPVjhhrd8Tk6gosl5pVlvsqiFlt50wotCvdkFGf+mDNBJxMh+bvav+Wt3EBnNZWq8Sp2I7XfSjm8siog==} + '@img/sharp-libvips-darwin-x64@1.0.2': + resolution: {integrity: sha512-Ofw+7oaWa0HiiMiKWqqaZbaYV3/UGL2wAPeLuJTx+9cXpCRdvQhCLG0IH8YGwM0yGWGLpsF4Su9vM1o6aer+Fw==} engines: {macos: '>=10.13', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [x64] os: [darwin] - '@img/sharp-libvips-linux-arm64@1.0.1': - resolution: {integrity: sha512-bnGG+MJjdX70mAQcSLxgeJco11G+MxTz+ebxlz8Y3dxyeb3Nkl7LgLI0mXupoO+u1wRNx/iRj5yHtzA4sde1yA==} + '@img/sharp-libvips-linux-arm64@1.0.2': + resolution: {integrity: sha512-x7kCt3N00ofFmmkkdshwj3vGPCnmiDh7Gwnd4nUwZln2YjqPxV1NlTyZOvoDWdKQVDL911487HOueBvrpflagw==} engines: {glibc: '>=2.26', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [arm64] os: [linux] - '@img/sharp-libvips-linux-arm@1.0.1': - resolution: {integrity: sha512-FtdMvR4R99FTsD53IA3LxYGghQ82t3yt0ZQ93WMZ2xV3dqrb0E8zq4VHaTOuLEAuA83oDawHV3fd+BsAPadHIQ==} + '@img/sharp-libvips-linux-arm@1.0.2': + resolution: {integrity: sha512-iLWCvrKgeFoglQxdEwzu1eQV04o8YeYGFXtfWU26Zr2wWT3q3MTzC+QTCO3ZQfWd3doKHT4Pm2kRmLbupT+sZw==} engines: {glibc: '>=2.28', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [arm] os: [linux] - '@img/sharp-libvips-linux-s390x@1.0.1': - resolution: {integrity: sha512-3+rzfAR1YpMOeA2zZNp+aYEzGNWK4zF3+sdMxuCS3ey9HhDbJ66w6hDSHDMoap32DueFwhhs3vwooAB2MaK4XQ==} + '@img/sharp-libvips-linux-s390x@1.0.2': + resolution: {integrity: sha512-cmhQ1J4qVhfmS6szYW7RT+gLJq9dH2i4maq+qyXayUSn9/3iY2ZeWpbAgSpSVbV2E1JUL2Gg7pwnYQ1h8rQIog==} engines: {glibc: '>=2.28', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [s390x] os: [linux] - '@img/sharp-libvips-linux-x64@1.0.1': - resolution: {integrity: sha512-3NR1mxFsaSgMMzz1bAnnKbSAI+lHXVTqAHgc1bgzjHuXjo4hlscpUxc0vFSAPKI3yuzdzcZOkq7nDPrP2F8Jgw==} + '@img/sharp-libvips-linux-x64@1.0.2': + resolution: {integrity: sha512-E441q4Qdb+7yuyiADVi5J+44x8ctlrqn8XgkDTwr4qPJzWkaHwD489iZ4nGDgcuya4iMN3ULV6NwbhRZJ9Z7SQ==} engines: {glibc: '>=2.26', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [x64] os: [linux] - '@img/sharp-libvips-linuxmusl-arm64@1.0.1': - resolution: {integrity: sha512-5aBRcjHDG/T6jwC3Edl3lP8nl9U2Yo8+oTl5drd1dh9Z1EBfzUKAJFUDTDisDjUwc7N4AjnPGfCA3jl3hY8uDg==} + '@img/sharp-libvips-linuxmusl-arm64@1.0.2': + resolution: {integrity: sha512-3CAkndNpYUrlDqkCM5qhksfE+qSIREVpyoeHIU6jd48SJZViAmznoQQLAv4hVXF7xyUB9zf+G++e2v1ABjCbEQ==} engines: {musl: '>=1.2.2', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [arm64] os: [linux] - '@img/sharp-libvips-linuxmusl-x64@1.0.1': - resolution: {integrity: sha512-dcT7inI9DBFK6ovfeWRe3hG30h51cBAP5JXlZfx6pzc/Mnf9HFCQDLtYf4MCBjxaaTfjCCjkBxcy3XzOAo5txw==} + '@img/sharp-libvips-linuxmusl-x64@1.0.2': + resolution: {integrity: sha512-VI94Q6khIHqHWNOh6LLdm9s2Ry4zdjWJwH56WoiJU7NTeDwyApdZZ8c+SADC8OH98KWNQXnE01UdJ9CSfZvwZw==} engines: {musl: '>=1.2.2', npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [x64] os: [linux] - '@img/sharp-linux-arm64@0.33.2': - resolution: {integrity: sha512-pz0NNo882vVfqJ0yNInuG9YH71smP4gRSdeL09ukC2YLE6ZyZePAlWKEHgAzJGTiOh8Qkaov6mMIMlEhmLdKew==} + '@img/sharp-linux-arm64@0.33.4': + resolution: {integrity: sha512-2800clwVg1ZQtxwSoTlHvtm9ObgAax7V6MTAB/hDT945Tfyy3hVkmiHpeLPCKYqYR1Gcmv1uDZ3a4OFwkdBL7Q==} engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [arm64] os: [linux] - '@img/sharp-linux-arm@0.33.2': - resolution: {integrity: sha512-Fndk/4Zq3vAc4G/qyfXASbS3HBZbKrlnKZLEJzPLrXoJuipFNNwTes71+Ki1hwYW5lch26niRYoZFAtZVf3EGA==} + '@img/sharp-linux-arm@0.33.4': + resolution: {integrity: sha512-RUgBD1c0+gCYZGCCe6mMdTiOFS0Zc/XrN0fYd6hISIKcDUbAW5NtSQW9g/powkrXYm6Vzwd6y+fqmExDuCdHNQ==} engines: {glibc: '>=2.28', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [arm] os: [linux] - '@img/sharp-linux-s390x@0.33.2': - resolution: {integrity: sha512-MBoInDXDppMfhSzbMmOQtGfloVAflS2rP1qPcUIiITMi36Mm5YR7r0ASND99razjQUpHTzjrU1flO76hKvP5RA==} - engines: {glibc: '>=2.28', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} + '@img/sharp-linux-s390x@0.33.4': + resolution: {integrity: sha512-h3RAL3siQoyzSoH36tUeS0PDmb5wINKGYzcLB5C6DIiAn2F3udeFAum+gj8IbA/82+8RGCTn7XW8WTFnqag4tQ==} + engines: {glibc: '>=2.31', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [s390x] os: [linux] - '@img/sharp-linux-x64@0.33.2': - resolution: {integrity: sha512-xUT82H5IbXewKkeF5aiooajoO1tQV4PnKfS/OZtb5DDdxS/FCI/uXTVZ35GQ97RZXsycojz/AJ0asoz6p2/H/A==} + '@img/sharp-linux-x64@0.33.4': + resolution: {integrity: sha512-GoR++s0XW9DGVi8SUGQ/U4AeIzLdNjHka6jidVwapQ/JebGVQIpi52OdyxCNVRE++n1FCLzjDovJNozif7w/Aw==} engines: {glibc: '>=2.26', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [x64] os: [linux] - '@img/sharp-linuxmusl-arm64@0.33.2': - resolution: {integrity: sha512-F+0z8JCu/UnMzg8IYW1TMeiViIWBVg7IWP6nE0p5S5EPQxlLd76c8jYemG21X99UzFwgkRo5yz2DS+zbrnxZeA==} + '@img/sharp-linuxmusl-arm64@0.33.4': + resolution: {integrity: sha512-nhr1yC3BlVrKDTl6cO12gTpXMl4ITBUZieehFvMntlCXFzH2bvKG76tBL2Y/OqhupZt81pR7R+Q5YhJxW0rGgQ==} engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [arm64] os: [linux] - '@img/sharp-linuxmusl-x64@0.33.2': - resolution: {integrity: sha512-+ZLE3SQmSL+Fn1gmSaM8uFusW5Y3J9VOf+wMGNnTtJUMUxFhv+P4UPaYEYT8tqnyYVaOVGgMN/zsOxn9pSsO2A==} + '@img/sharp-linuxmusl-x64@0.33.4': + resolution: {integrity: sha512-uCPTku0zwqDmZEOi4ILyGdmW76tH7dm8kKlOIV1XC5cLyJ71ENAAqarOHQh0RLfpIpbV5KOpXzdU6XkJtS0daw==} engines: {musl: '>=1.2.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [x64] os: [linux] - '@img/sharp-wasm32@0.33.2': - resolution: {integrity: sha512-fLbTaESVKuQcpm8ffgBD7jLb/CQLcATju/jxtTXR1XCLwbOQt+OL5zPHSDMmp2JZIeq82e18yE0Vv7zh6+6BfQ==} + '@img/sharp-wasm32@0.33.4': + resolution: {integrity: sha512-Bmmauh4sXUsUqkleQahpdNXKvo+wa1V9KhT2pDA4VJGKwnKMJXiSTGphn0gnJrlooda0QxCtXc6RX1XAU6hMnQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [wasm32] - '@img/sharp-win32-ia32@0.33.2': - resolution: {integrity: sha512-okBpql96hIGuZ4lN3+nsAjGeggxKm7hIRu9zyec0lnfB8E7Z6p95BuRZzDDXZOl2e8UmR4RhYt631i7mfmKU8g==} + '@img/sharp-win32-ia32@0.33.4': + resolution: {integrity: sha512-99SJ91XzUhYHbx7uhK3+9Lf7+LjwMGQZMDlO/E/YVJ7Nc3lyDFZPGhjwiYdctoH2BOzW9+TnfqcaMKt0jHLdqw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [ia32] os: [win32] - '@img/sharp-win32-x64@0.33.2': - resolution: {integrity: sha512-E4magOks77DK47FwHUIGH0RYWSgRBfGdK56kIHSVeB9uIS4pPFr4N2kIVsXdQQo4LzOsENKV5KAhRlRL7eMAdg==} + '@img/sharp-win32-x64@0.33.4': + resolution: {integrity: sha512-3QLocdTRVIrFNye5YocZl+KKpYKP+fksi1QhmOArgx7GyhIbQp/WrJRu176jm8IxromS7RIkzMiMINVdBtC8Aw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0', yarn: '>=3.2.0'} cpu: [x64] os: [win32] @@ -612,38 +616,41 @@ packages: resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jridgewell/gen-mapping@0.3.3': - resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} + '@jridgewell/gen-mapping@0.3.5': + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} engines: {node: '>=6.0.0'} - '@jridgewell/resolve-uri@3.1.1': - resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/set-array@1.1.2': - resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} engines: {node: '>=6.0.0'} - '@jridgewell/source-map@0.3.5': - resolution: {integrity: sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==} + '@jridgewell/source-map@0.3.6': + resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} - '@jridgewell/sourcemap-codec@1.4.15': - resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} - '@jridgewell/trace-mapping@0.3.20': - resolution: {integrity: sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==} + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - '@ljharb/through@2.3.11': - resolution: {integrity: sha512-ccfcIDlogiXNq5KcbAwbaO7lMh3Tm1i3khMPYpxlK8hH/W53zN81KM9coerRLOnTGu3nfXIniAmQbRI9OxbC0w==} + '@ljharb/through@2.3.13': + resolution: {integrity: sha512-/gKJun8NNiWGZJkGzI/Ragc53cOdcLNdzjLaIa+GEjguQs0ulsurx8WN0jijdK9yPqDvziX995sMRLyLt1uZMQ==} engines: {node: '>= 0.4'} '@lukeed/csprng@1.1.0': resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} engines: {node: '>=8'} + '@microsoft/tsdoc@0.15.0': + resolution: {integrity: sha512-HZpPoABogPvjeJOdzCOSJsXeL/SMCBgBZMVC3X3d7YYp2gf31MfxhUoYUNwf1ERPJOnQc0wkFn9trqI6ZEdZuA==} + '@nestjs/axios@3.0.2': resolution: {integrity: sha512-Z6GuOUdNQjP7FX+OuV2Ybyamse+/e0BFdTWBX5JxpBDKA+YkdLynDgG6HTF04zy6e9zPa19UX0WA2VDoehwhXQ==} peerDependencies: @@ -651,12 +658,12 @@ packages: axios: ^1.3.1 rxjs: ^6.0.0 || ^7.0.0 - '@nestjs/cli@10.3.0': - resolution: {integrity: sha512-37h+wSDItY0NE/x3a/M9yb2cXzfsD4qoE26rHgFn592XXLelDN12wdnfn7dTIaiRZT7WOCdQ+BYP9mQikR4AsA==} + '@nestjs/cli@10.4.2': + resolution: {integrity: sha512-fQexIfLHfp6GUgX+CO4fOg+AEwV5ox/LHotQhyZi9wXUQDyIqS0NTTbumr//62EcX35qV4nU0359nYnuEdzG+A==} engines: {node: '>= 16.14'} hasBin: true peerDependencies: - '@swc/cli': ^0.1.62 + '@swc/cli': ^0.1.62 || ^0.3.0 || ^0.4.0 '@swc/core': ^1.3.62 peerDependenciesMeta: '@swc/cli': @@ -664,12 +671,12 @@ packages: '@swc/core': optional: true - '@nestjs/common@10.3.1': - resolution: {integrity: sha512-YuxeIlVemVQCuXMkNbBpNlmwZgp/Cu6dwCOjki63mhyYHEFX48GNNA4zZn5MFRjF4h7VSceABsScROuzsxs9LA==} + '@nestjs/common@10.3.10': + resolution: {integrity: sha512-H8k0jZtxk1IdtErGDmxFRy0PfcOAUg41Prrqpx76DQusGGJjsaovs1zjXVD1rZWaVYchfT1uczJ6L4Kio10VNg==} peerDependencies: class-transformer: '*' class-validator: '*' - reflect-metadata: ^0.1.12 + reflect-metadata: ^0.1.12 || ^0.2.0 rxjs: ^7.1.0 peerDependenciesMeta: class-transformer: @@ -677,20 +684,20 @@ packages: class-validator: optional: true - '@nestjs/config@3.1.1': - resolution: {integrity: sha512-qu5QlNiJdqQtOsnB6lx4JCXPQ96jkKUsOGd+JXfXwqJqZcOSAq6heNFg0opW4pq4J/VZoNwoo87TNnx9wthnqQ==} + '@nestjs/config@3.2.3': + resolution: {integrity: sha512-p6yv/CvoBewJ72mBq4NXgOAi2rSQNWx3a+IMJLVKS2uiwFCOQQuiIatGwq6MRjXV3Jr+B41iUO8FIf4xBrZ4/w==} peerDependencies: '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 - reflect-metadata: ^0.1.13 + rxjs: ^7.1.0 - '@nestjs/core@10.3.1': - resolution: {integrity: sha512-mh6FwTKh2R3CmLRuB50BF5q/lzc+Mz+7qAlEvpgCiTSIfSXzbQ47vWpfgLirwkL3SlCvtFS8onxOeI69RpxvXA==} + '@nestjs/core@10.3.10': + resolution: {integrity: sha512-ZbQ4jovQyzHtCGCrzK5NdtW1SYO2fHSsgSY1+/9WdruYCUra+JDkWEXgZ4M3Hv480Dl3OXehAmY1wCOojeMyMQ==} peerDependencies: '@nestjs/common': ^10.0.0 '@nestjs/microservices': ^10.0.0 '@nestjs/platform-express': ^10.0.0 '@nestjs/websockets': ^10.0.0 - reflect-metadata: ^0.1.12 + reflect-metadata: ^0.1.12 || ^0.2.0 rxjs: ^7.1.0 peerDependenciesMeta: '@nestjs/microservices': @@ -705,13 +712,13 @@ packages: peerDependencies: '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 - '@nestjs/mapped-types@2.0.4': - resolution: {integrity: sha512-xl+gUSp0B+ln1VSNoUftlglk8dfpUes3DHGxKZ5knuBxS5g2H/8p9/DSBOYWUfO5f4u9s6ffBPZ71WO+tbe5SA==} + '@nestjs/mapped-types@2.0.5': + resolution: {integrity: sha512-bSJv4pd6EY99NX9CjBIyn4TVDoSit82DUZlL4I3bqNfy5Gt+gXTa86i3I/i0iIV9P4hntcGM5GyO+FhZAhxtyg==} peerDependencies: '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 class-transformer: ^0.4.0 || ^0.5.0 class-validator: ^0.13.0 || ^0.14.0 - reflect-metadata: ^0.1.12 + reflect-metadata: ^0.1.12 || ^0.2.0 peerDependenciesMeta: class-transformer: optional: true @@ -724,26 +731,26 @@ packages: '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 passport: ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0 - '@nestjs/platform-express@10.3.1': - resolution: {integrity: sha512-Rj21quI5h4Lry7q9an+nO4ADQiQUy9A6XK74o5aTUHo3Ysm25ujqh2NgU4XbT3M2oXU9qzhE59OfhkQ7ZUvTAg==} + '@nestjs/platform-express@10.3.10': + resolution: {integrity: sha512-wK2ow3CZI2KFqWeEpPmoR300OB6BcBLxARV1EiClJLCj4S1mZsoCmS0YWgpk3j1j6mo0SI8vNLi/cC2iZPEPQA==} peerDependencies: '@nestjs/common': ^10.0.0 '@nestjs/core': ^10.0.0 - '@nestjs/schematics@10.1.0': - resolution: {integrity: sha512-HQWvD3F7O0Sv3qHS2jineWxPLmBTLlyjT6VdSw2EAIXulitmV+ErxB3TCVQQORlNkl5p5cwRYWyBaOblDbNFIQ==} + '@nestjs/schematics@10.1.2': + resolution: {integrity: sha512-S0bMtZM5U4mAiqkhRyZkXgjmOHBS5P/lp/vEydgMR4F7csOShc3jFeKVs1Eghd9xCFezGKy3SHy7hFT6dpPhWQ==} peerDependencies: typescript: '>=4.8.2' - '@nestjs/swagger@7.2.0': - resolution: {integrity: sha512-W7WPq561/79w27ZEgViXS7c5hqPwT7QXhsLsSeu2jeBROUhMM825QKDFKbMmtb643IW5dznJ4PjherlZZgtMvg==} + '@nestjs/swagger@7.4.0': + resolution: {integrity: sha512-dCiwKkRxcR7dZs5jtrGspBAe/nqJd1AYzOBTzw9iCdbq3BGrLpwokelk6lFZPe4twpTsPQqzNKBwKzVbI6AR/g==} peerDependencies: - '@fastify/static': ^6.0.0 + '@fastify/static': ^6.0.0 || ^7.0.0 '@nestjs/common': ^9.0.0 || ^10.0.0 '@nestjs/core': ^9.0.0 || ^10.0.0 class-transformer: '*' class-validator: '*' - reflect-metadata: ^0.1.12 + reflect-metadata: ^0.1.12 || ^0.2.0 peerDependenciesMeta: '@fastify/static': optional: true @@ -752,8 +759,8 @@ packages: class-validator: optional: true - '@nestjs/testing@10.3.1': - resolution: {integrity: sha512-74aSAugWT31jSPnStyRWDXgjHXWO3GYaUfAZ2T7Dml88UGkGy95iwaWgYy7aYM8/xVFKcDYkfL5FAYqZYce/yg==} + '@nestjs/testing@10.3.10': + resolution: {integrity: sha512-i3HAtVQJijxNxJq1k39aelyJlyEIBRONys7IipH/4r8W0J+M1V+y5EKDOyi4j1SdNSb/vmNyWpZ2/ewZjl3kRA==} peerDependencies: '@nestjs/common': ^10.0.0 '@nestjs/core': ^10.0.0 @@ -789,8 +796,8 @@ packages: '@polka/url@0.5.0': resolution: {integrity: sha512-oZLYFEAzUKyi3SKnXvj32ZCEGH6RDnao7COuCVhDydMS9NrCSVXhM79VaKyP5+Zc33m0QXEd2DN3UkU7OsHcfw==} - '@prisma/client@5.8.1': - resolution: {integrity: sha512-xQtMPfbIwLlbm0VVIVQY2yqQVOxPwRQhvIp7Z3m2900g1bu/zRHKhYZJQWELqmjl6d8YwBy0K2NvMqh47v1ubw==} + '@prisma/client@5.17.0': + resolution: {integrity: sha512-N2tnyKayT0Zf7mHjwEyE8iG7FwTmXDHFZ1GnNhQp0pJUObsuel4ZZ1XwfuAYkq5mRIiC/Kot0kt0tGCfLJ70Jw==} engines: {node: '>=16.13'} peerDependencies: prisma: '*' @@ -798,26 +805,26 @@ packages: prisma: optional: true - '@prisma/debug@5.8.1': - resolution: {integrity: sha512-tjuw7eA0Us3T42jx9AmAgL58rzwzpFGYc3R7Y4Ip75EBYrKMBA1YihuWMcBC92ILmjlQ/u3p8VxcIE0hr+fZfg==} + '@prisma/debug@5.17.0': + resolution: {integrity: sha512-l7+AteR3P8FXiYyo496zkuoiJ5r9jLQEdUuxIxNCN1ud8rdbH3GTxm+f+dCyaSv9l9WY+29L9czaVRXz9mULfg==} - '@prisma/engines-version@5.8.1-1.78caf6feeaed953168c64e15a249c3e9a033ebe2': - resolution: {integrity: sha512-f5C3JM3l9yhGr3cr4FMqWloFaSCpNpMi58Om22rjD2DOz3owci2mFdFXMgnAGazFPKrCbbEhcxdsRfspEYRoFQ==} + '@prisma/engines-version@5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053': + resolution: {integrity: sha512-tUuxZZysZDcrk5oaNOdrBnnkoTtmNQPkzINFDjz7eG6vcs9AVDmA/F6K5Plsb2aQc/l5M2EnFqn3htng9FA4hg==} - '@prisma/engines@5.8.1': - resolution: {integrity: sha512-TJgYLRrZr56uhqcXO4GmP5be+zjCIHtLDK20Cnfg+o9d905hsN065QOL+3Z0zQAy6YD31Ol4u2kzSfRmbJv/uA==} + '@prisma/engines@5.17.0': + resolution: {integrity: sha512-+r+Nf+JP210Jur+/X8SIPLtz+uW9YA4QO5IXA+KcSOBe/shT47bCcRMTYCbOESw3FFYFTwe7vU6KTWHKPiwvtg==} - '@prisma/fetch-engine@5.8.1': - resolution: {integrity: sha512-+bgjjoSFa6uYEbAPlklfoVSStOEfcpheOjoBoNsNNSQdSzcwE2nM4Q0prun0+P8/0sCHo18JZ9xqa8gObvgOUw==} + '@prisma/fetch-engine@5.17.0': + resolution: {integrity: sha512-ESxiOaHuC488ilLPnrv/tM2KrPhQB5TRris/IeIV4ZvUuKeaicCl4Xj/JCQeG9IlxqOgf1cCg5h5vAzlewN91Q==} - '@prisma/get-platform@5.8.1': - resolution: {integrity: sha512-wnA+6HTFcY+tkykMokix9GiAkaauPC5W/gg0O5JB0J8tCTNWrqpnQ7AsaGRfkYUbeOIioh6woDjQrGTTRf1Zag==} + '@prisma/get-platform@5.17.0': + resolution: {integrity: sha512-UlDgbRozCP1rfJ5Tlkf3Cnftb6srGrEQ4Nm3og+1Se2gWmCZ0hmPIi+tQikGDUVLlvOWx3Gyi9LzgRP+HTXV9w==} '@sinclair/typebox@0.27.8': resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - '@sinonjs/commons@3.0.0': - resolution: {integrity: sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==} + '@sinonjs/commons@3.0.1': + resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} '@sinonjs/fake-timers@10.3.0': resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} @@ -828,8 +835,8 @@ packages: '@tokenizer/token@0.3.0': resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} - '@tsconfig/node10@1.0.9': - resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} + '@tsconfig/node10@1.0.11': + resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} '@tsconfig/node12@1.0.11': resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} @@ -846,14 +853,14 @@ packages: '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} - '@types/babel__generator@7.6.7': - resolution: {integrity: sha512-6Sfsq+EaaLrw4RmdFWE9Onp63TOUue71AWb4Gpa6JxzgTYtimbM086WnYTy2U67AofR++QKCo08ZP6pwx8YFHQ==} + '@types/babel__generator@7.6.8': + resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} '@types/babel__template@7.4.4': resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} - '@types/babel__traverse@7.20.4': - resolution: {integrity: sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA==} + '@types/babel__traverse@7.20.6': + resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} '@types/bcryptjs@2.4.6': resolution: {integrity: sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==} @@ -867,14 +874,14 @@ packages: '@types/eslint-scope@3.7.7': resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} - '@types/eslint@8.44.9': - resolution: {integrity: sha512-6yBxcvwnnYoYT1Uk2d+jvIfsuP4mb2EdIxFnrPABj5a/838qe5bGkNLFOiipX4ULQ7XVQvTxOh7jO+BTAiqsEw==} + '@types/eslint@8.56.10': + resolution: {integrity: sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==} '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} - '@types/express-serve-static-core@4.17.41': - resolution: {integrity: sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==} + '@types/express-serve-static-core@4.19.5': + resolution: {integrity: sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==} '@types/express@4.17.21': resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} @@ -903,12 +910,12 @@ packages: '@types/jsonwebtoken@9.0.5': resolution: {integrity: sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==} + '@types/jsonwebtoken@9.0.6': + resolution: {integrity: sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw==} + '@types/mime@1.3.5': resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} - '@types/mime@3.0.4': - resolution: {integrity: sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw==} - '@types/multer@1.4.11': resolution: {integrity: sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w==} @@ -933,29 +940,26 @@ packages: '@types/pdfkit@0.13.4': resolution: {integrity: sha512-ixGNDHYJCCKuamY305wbfYSphZ2WPe8FPkjn8oF4fHV+PgPV4V+hecPh2VOS2h4RNtpSB3zQcR4sCpNvvrEb1A==} - '@types/qs@6.9.10': - resolution: {integrity: sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==} + '@types/qs@6.9.15': + resolution: {integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==} '@types/range-parser@1.2.7': resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} - '@types/semver@7.5.6': - resolution: {integrity: sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==} + '@types/semver@7.5.8': + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} '@types/send@0.17.4': resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} - '@types/serve-static@1.15.5': - resolution: {integrity: sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==} + '@types/serve-static@1.15.7': + resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} '@types/stack-utils@2.0.3': resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} - '@types/uuid@9.0.8': - resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} - - '@types/validator@13.11.8': - resolution: {integrity: sha512-c/hzNDBh7eRF+KbCf+OoZxKbnkpaK/cKp9iLQWqB7muXtM+MtL9SUUH8vCFcLn6dH1Qm05jiexK0ofWY7TfOhQ==} + '@types/validator@13.12.0': + resolution: {integrity: sha512-nH45Lk7oPIJ1RVOF6JgFI6Dy0QpHEzq4QecZhvguxYPDwT8c93prCMqAtiIttm39voZ+DDR+qkNnMpJmMBRqag==} '@types/yargs-parser@21.0.3': resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} @@ -1024,8 +1028,8 @@ packages: '@ungap/structured-clone@1.2.0': resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} - '@webassemblyjs/ast@1.11.6': - resolution: {integrity: sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==} + '@webassemblyjs/ast@1.12.1': + resolution: {integrity: sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==} '@webassemblyjs/floating-point-hex-parser@1.11.6': resolution: {integrity: sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==} @@ -1033,8 +1037,8 @@ packages: '@webassemblyjs/helper-api-error@1.11.6': resolution: {integrity: sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==} - '@webassemblyjs/helper-buffer@1.11.6': - resolution: {integrity: sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==} + '@webassemblyjs/helper-buffer@1.12.1': + resolution: {integrity: sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==} '@webassemblyjs/helper-numbers@1.11.6': resolution: {integrity: sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==} @@ -1042,8 +1046,8 @@ packages: '@webassemblyjs/helper-wasm-bytecode@1.11.6': resolution: {integrity: sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==} - '@webassemblyjs/helper-wasm-section@1.11.6': - resolution: {integrity: sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==} + '@webassemblyjs/helper-wasm-section@1.12.1': + resolution: {integrity: sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==} '@webassemblyjs/ieee754@1.11.6': resolution: {integrity: sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==} @@ -1054,20 +1058,20 @@ packages: '@webassemblyjs/utf8@1.11.6': resolution: {integrity: sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==} - '@webassemblyjs/wasm-edit@1.11.6': - resolution: {integrity: sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==} + '@webassemblyjs/wasm-edit@1.12.1': + resolution: {integrity: sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==} - '@webassemblyjs/wasm-gen@1.11.6': - resolution: {integrity: sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==} + '@webassemblyjs/wasm-gen@1.12.1': + resolution: {integrity: sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==} - '@webassemblyjs/wasm-opt@1.11.6': - resolution: {integrity: sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==} + '@webassemblyjs/wasm-opt@1.12.1': + resolution: {integrity: sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==} - '@webassemblyjs/wasm-parser@1.11.6': - resolution: {integrity: sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==} + '@webassemblyjs/wasm-parser@1.12.1': + resolution: {integrity: sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==} - '@webassemblyjs/wast-printer@1.11.6': - resolution: {integrity: sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==} + '@webassemblyjs/wast-printer@1.12.1': + resolution: {integrity: sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==} '@xtuc/ieee754@1.2.0': resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} @@ -1082,8 +1086,8 @@ packages: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} - acorn-import-assertions@1.9.0: - resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==} + acorn-import-attributes@1.9.5: + resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} peerDependencies: acorn: ^8 @@ -1092,12 +1096,12 @@ packages: peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn-walk@8.3.1: - resolution: {integrity: sha512-TgUZgYvqZprrl7YldZNoa9OciCAyZR+Ejm9eXzKCmjsF5IKp/wgQ7Z/ZpjpGTIUPwrHQIcYeI8qDh4PsEwxMbw==} + acorn-walk@8.3.3: + resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==} engines: {node: '>=0.4.0'} - acorn@8.11.2: - resolution: {integrity: sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==} + acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} engines: {node: '>=0.4.0'} hasBin: true @@ -1208,8 +1212,8 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - axios@1.6.7: - resolution: {integrity: sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==} + axios@1.7.2: + resolution: {integrity: sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==} babel-jest@29.7.0: resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} @@ -1256,17 +1260,13 @@ packages: bignumber.js@9.0.0: resolution: {integrity: sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==} - binary-extensions@2.2.0: - resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - body-parser@1.20.1: - resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - body-parser@1.20.2: resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} @@ -1277,15 +1277,15 @@ packages: brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} - braces@3.0.2: - resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} brotli@1.3.3: resolution: {integrity: sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==} - browserslist@4.22.2: - resolution: {integrity: sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==} + browserslist@4.23.2: + resolution: {integrity: sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -1313,9 +1313,6 @@ packages: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} - call-bind@1.0.5: - resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==} - call-bind@1.0.7: resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} engines: {node: '>= 0.4'} @@ -1332,11 +1329,11 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001570: - resolution: {integrity: sha512-+3e0ASu4sw1SWaoCtvPeyXp+5PsjigkSt8OXZbF9StH5pQWbxEjLAZE3n8Aup5udop1uRiKA7a4utUk/uoSpUw==} + caniuse-lite@1.0.30001642: + resolution: {integrity: sha512-3XQ0DoRgLijXJErLSl+bLnJ+Et4KqV1PY6JJBGAFlsNsz31zeAIncyeZfLCabHK/jtSh+671RM9YMldxjUPZtA==} - centra@2.6.0: - resolution: {integrity: sha512-dgh+YleemrT8u85QL11Z6tYhegAs3MMxsaWAq/oXeAmYJ7VxL3SI9TZtnfaEvNDMAPolj25FXIb3S+HCI4wQaQ==} + centra@2.7.0: + resolution: {integrity: sha512-PbFMgMSrmgx6uxCdm57RUos9Tc3fclMvhLSATYN39XsDV29B89zZ3KA89jmY0vwSGazyU+uerqwa6t+KaodPcg==} chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} @@ -1360,20 +1357,20 @@ packages: chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} - chokidar@3.5.3: - resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} - chrome-trace-event@1.0.3: - resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==} + chrome-trace-event@1.0.4: + resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} engines: {node: '>=6.0'} ci-info@3.9.0: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} - cjs-module-lexer@1.2.3: - resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==} + cjs-module-lexer@1.3.1: + resolution: {integrity: sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==} class-transformer@0.5.1: resolution: {integrity: sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==} @@ -1393,8 +1390,8 @@ packages: resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} engines: {node: '>=6'} - cli-table3@0.6.3: - resolution: {integrity: sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==} + cli-table3@0.6.5: + resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} engines: {node: 10.* || >= 12.*} cli-truncate@1.1.0: @@ -1490,8 +1487,8 @@ packages: cookie-signature@1.0.6: resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} - cookie@0.5.0: - resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} + cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} engines: {node: '>= 0.6'} core-util-is@1.0.2: @@ -1540,8 +1537,8 @@ packages: supports-color: optional: true - debug@4.3.4: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + debug@4.3.5: + resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -1549,8 +1546,8 @@ packages: supports-color: optional: true - dedent@1.5.1: - resolution: {integrity: sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==} + dedent@1.5.3: + resolution: {integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==} peerDependencies: babel-plugin-macros: ^3.1.0 peerDependenciesMeta: @@ -1574,10 +1571,6 @@ packages: defaults@1.0.4: resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} - define-data-property@1.1.1: - resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==} - engines: {node: '>= 0.4'} - define-data-property@1.1.4: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} @@ -1598,8 +1591,8 @@ packages: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - detect-libc@2.0.2: - resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==} + detect-libc@2.0.3: + resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} engines: {node: '>=8'} detect-newline@3.1.0: @@ -1629,8 +1622,8 @@ packages: resolution: {integrity: sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==} engines: {node: '>=12'} - dotenv@16.3.1: - resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==} + dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} engines: {node: '>=12'} eastasianwidth@0.2.0: @@ -1642,8 +1635,8 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.4.614: - resolution: {integrity: sha512-X4ze/9Sc3QWs6h92yerwqv7aB/uU8vCjZcrMjA8N9R1pjMFRe44dLsck5FzLilOYvcXuDn93B+bpGYyufc70gQ==} + electron-to-chromium@1.4.830: + resolution: {integrity: sha512-TrPKKH20HeN0J1LHzsYLs2qwXrp8TF4nHdu4sq61ozGbzMpWhI7iIOPYPPkxeq1azMT9PZ8enPFcftbs/Npcjg==} emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} @@ -1659,8 +1652,8 @@ packages: resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} engines: {node: '>= 0.8'} - enhanced-resolve@5.15.0: - resolution: {integrity: sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==} + enhanced-resolve@5.17.0: + resolution: {integrity: sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==} engines: {node: '>=10.13.0'} env-cmd@10.1.0: @@ -1682,11 +1675,11 @@ packages: es-get-iterator@1.1.3: resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} - es-module-lexer@1.4.1: - resolution: {integrity: sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==} + es-module-lexer@1.5.4: + resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==} - escalade@3.1.1: - resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + escalade@3.1.2: + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} engines: {node: '>=6'} escape-html@1.0.3: @@ -1704,10 +1697,6 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - escape-string-regexp@5.0.0: - resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} - engines: {node: '>=12'} - eslint-config-prettier@8.10.0: resolution: {integrity: sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==} hasBin: true @@ -1737,8 +1726,8 @@ packages: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint@8.56.0: - resolution: {integrity: sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==} + eslint@8.57.0: + resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true @@ -1751,8 +1740,8 @@ packages: engines: {node: '>=4'} hasBin: true - esquery@1.5.0: - resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} engines: {node: '>=0.10'} esrecurse@4.3.0: @@ -1791,8 +1780,8 @@ packages: resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - express@4.18.2: - resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} + express@4.19.2: + resolution: {integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==} engines: {node: '>= 0.10.0'} external-editor@2.2.0: @@ -1826,12 +1815,12 @@ packages: fast-safe-stringify@2.1.1: resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} - fast-xml-parser@4.3.6: - resolution: {integrity: sha512-M2SovcRxD4+vC493Uc2GZVcZaj66CCJhWurC4viynVSTvrpErCShNcDz1lAho6n9REQKvL/ll4A4/fw6Y9z8nw==} + fast-xml-parser@4.4.0: + resolution: {integrity: sha512-kLY3jFlwIYwBNDojclKsNAC12sfD6NwW74QB2CoNGPvtVxjliYehVunB3HYyNi+n4Tt1dAcgwYvmKF/Z18flqg==} hasBin: true - fastq@1.16.0: - resolution: {integrity: sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==} + fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} @@ -1844,20 +1833,16 @@ packages: resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} engines: {node: '>=8'} - figures@5.0.0: - resolution: {integrity: sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==} - engines: {node: '>=14'} - file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} - file-type@19.0.0: - resolution: {integrity: sha512-s7cxa7/leUWLiXO78DVVfBVse+milos9FitauDLG1pI7lNaJ2+5lzPnr2N24ym+84HVwJL6hVuGfgVE+ALvU8Q==} + file-type@19.2.0: + resolution: {integrity: sha512-tUgGaVcM7enfk+toLsBbbCP71iIThjBiMWLk1gYUprcY21/uRNnui0Py9oJ5AIXE7EJYfBeJRAl8pNHBmujboQ==} engines: {node: '>=18'} - fill-range@7.0.1: - resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} finalhandler@1.2.0: @@ -1876,8 +1861,8 @@ packages: resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} engines: {node: ^10.12.0 || >=12.0.0} - flatted@3.2.9: - resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==} + flatted@3.3.1: + resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} follow-redirects@1.15.6: resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} @@ -1894,8 +1879,8 @@ packages: for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} - foreground-child@3.1.1: - resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} + foreground-child@3.2.1: + resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} engines: {node: '>=14'} fork-ts-checker-webpack-plugin@9.0.2: @@ -1924,8 +1909,8 @@ packages: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} - fs-monkey@1.0.5: - resolution: {integrity: sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==} + fs-monkey@1.0.6: + resolution: {integrity: sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==} fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -1953,9 +1938,6 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - get-intrinsic@1.2.2: - resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==} - get-intrinsic@1.2.4: resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} engines: {node: '>= 0.4'} @@ -1979,17 +1961,14 @@ packages: glob-to-regexp@0.4.1: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} - glob@10.3.10: - resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==} - engines: {node: '>=16 || 14 >=14.17'} + glob@10.4.2: + resolution: {integrity: sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==} + engines: {node: '>=16 || 14 >=14.18'} hasBin: true glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - - glob@9.3.5: - resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==} - engines: {node: '>=16 || 14 >=14.17'} + deprecated: Glob versions prior to v9 are no longer supported globals@11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} @@ -2027,14 +2006,11 @@ packages: resolution: {integrity: sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==} engines: {node: '>=8'} - has-property-descriptors@1.0.1: - resolution: {integrity: sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==} - has-property-descriptors@1.0.2: resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - has-proto@1.0.1: - resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} engines: {node: '>= 0.4'} has-symbols@1.0.3: @@ -2045,8 +2021,8 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} - hasown@2.0.0: - resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==} + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} homedir@0.6.0: @@ -2073,8 +2049,8 @@ packages: ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - ignore@5.3.0: - resolution: {integrity: sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==} + ignore@5.3.1: + resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} engines: {node: '>= 4'} import-fresh@3.3.0: @@ -2092,6 +2068,7 @@ packages: inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -2114,18 +2091,14 @@ packages: resolution: {integrity: sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==} engines: {node: '>=12.0.0'} - inquirer@9.2.11: - resolution: {integrity: sha512-B2LafrnnhbRzCWfAdOXisUzL89Kg8cVJlYmhqoi3flSiV/TveO+nsXwgKr9h9PIo+J1hz7nBSk6gegRIMBBf7g==} - engines: {node: '>=14.18.0'} + inquirer@9.2.15: + resolution: {integrity: sha512-vI2w4zl/mDluHt9YEQ/543VTCwPKWiHzKtm9dM2V0NdFcqEexDAjUHzO1oA60HRNaVifGXXM1tRRNluLVHa0Kg==} + engines: {node: '>=18'} internal-slot@1.0.7: resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} engines: {node: '>= 0.4'} - interpret@1.4.0: - resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} - engines: {node: '>= 0.10'} - ipaddr.js@1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} @@ -2159,8 +2132,9 @@ packages: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} - is-core-module@2.13.1: - resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + is-core-module@2.15.0: + resolution: {integrity: sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==} + engines: {node: '>= 0.4'} is-date-object@1.0.5: resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} @@ -2190,8 +2164,9 @@ packages: resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} engines: {node: '>=8'} - is-map@2.0.2: - resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==} + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} is-number-object@1.0.7: resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} @@ -2209,8 +2184,9 @@ packages: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} - is-set@2.0.2: - resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==} + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} is-shared-array-buffer@1.0.3: resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} @@ -2232,15 +2208,13 @@ packages: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} - is-unicode-supported@1.3.0: - resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} - engines: {node: '>=12'} - - is-weakmap@2.0.1: - resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==} + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} - is-weakset@2.0.2: - resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==} + is-weakset@2.0.3: + resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==} + engines: {node: '>= 0.4'} isarray@1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} @@ -2259,8 +2233,8 @@ packages: resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} engines: {node: '>=8'} - istanbul-lib-instrument@6.0.1: - resolution: {integrity: sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==} + istanbul-lib-instrument@6.0.3: + resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} engines: {node: '>=10'} istanbul-lib-report@3.0.1: @@ -2271,17 +2245,16 @@ packages: resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} engines: {node: '>=10'} - istanbul-reports@3.1.6: - resolution: {integrity: sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==} + istanbul-reports@3.1.7: + resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} engines: {node: '>=8'} iterare@1.2.1: resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==} engines: {node: '>=6'} - jackspeak@2.3.6: - resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} - engines: {node: '>=14'} + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} jest-changed-files@29.7.0: resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} @@ -2458,8 +2431,11 @@ packages: engines: {node: '>=6'} hasBin: true - jsonc-parser@3.2.0: - resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} + jsonc-parser@3.2.1: + resolution: {integrity: sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==} + + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} @@ -2499,8 +2475,8 @@ packages: engines: {node: '>=10.13.0'} deprecated: This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md - ldapts@7.0.12: - resolution: {integrity: sha512-orwgIejUi/ZyGah9y8jWZmFUg8Ci5M8WAv0oZjSf3MVuk1sRBdor9Qy1ttGHbYpWj96HXKFunQ8AYZ8WWGp17g==} + ldapts@7.1.0: + resolution: {integrity: sha512-EGHJC1L9xFd9Qxevkq4hTi4I8KQ9Eh3F8Uzv7m1dviu5D8Ryq2Q4a52ddb49bDOv40UZuc37tuV94YPf+Ub/1g==} engines: {node: '>=18'} leven@3.1.0: @@ -2511,11 +2487,12 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} - libphonenumber-js@1.10.54: - resolution: {integrity: sha512-P+38dUgJsmh0gzoRDoM4F5jLbyfztkU6PY6eSK6S5HwTi/LPvnwXqVCQZlAy1FxZ5c48q25QhxGQ0pq+WQcSlQ==} + libphonenumber-js@1.11.4: + resolution: {integrity: sha512-F/R50HQuWWYcmU/esP5jrH5LiWYaN7DpN0a/99U8+mnGGtnx8kmRE+649dQh3v+CowXXZc8vpkf5AmYkO0AQ7Q==} lightcookie@1.0.25: resolution: {integrity: sha512-SrY/+eBPaKAMnsn7mCsoOMZzoQyCyHHHZlFCu2fjo28XxSyCLjlooKiTxyrXTg8NPaHp1YzWi0lcGG1gDi6KHw==} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. linebreak@1.1.0: resolution: {integrity: sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==} @@ -2535,6 +2512,12 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash.escaperegexp@4.1.2: + resolution: {integrity: sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==} + + lodash.groupby@4.6.0: + resolution: {integrity: sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==} + lodash.includes@4.3.0: resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} @@ -2550,6 +2533,9 @@ packages: lodash.isinteger@4.0.4: resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + lodash.isnil@4.0.0: + resolution: {integrity: sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==} + lodash.isnumber@3.0.3: resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} @@ -2559,6 +2545,9 @@ packages: lodash.isstring@4.0.1: resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + lodash.isundefined@3.0.1: + resolution: {integrity: sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==} + lodash.memoize@4.1.2: resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} @@ -2571,6 +2560,9 @@ packages: lodash.partialright@4.2.1: resolution: {integrity: sha512-yebmPMQZH7i4El6SdJTW9rn8irWl8VTcsmiWqm/I4sY8/ZjbSo0Z512HL6soeAu3mh5rhx5uIIo6kYJOQXbCxw==} + lodash.uniq@4.5.0: + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} @@ -2578,19 +2570,14 @@ packages: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} engines: {node: '>=10'} - lru-cache@10.1.0: - resolution: {integrity: sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==} - engines: {node: 14 || >=16.14} + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - lru-cache@6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} - - magic-string@0.30.5: - resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==} + magic-string@0.30.8: + resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==} engines: {node: '>=12'} make-dir@4.0.0: @@ -2629,8 +2616,8 @@ packages: resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} engines: {node: '>= 0.6'} - micromatch@4.0.5: - resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + micromatch@4.0.7: + resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} engines: {node: '>=8.6'} mime-db@1.52.0: @@ -2660,23 +2647,15 @@ packages: minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - minimatch@8.0.4: - resolution: {integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==} - engines: {node: '>=16 || 14 >=14.17'} - - minimatch@9.0.3: - resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - minipass@4.2.8: - resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==} - engines: {node: '>=8'} - - minipass@7.0.4: - resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==} + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} mkdirp@0.5.6: @@ -2749,8 +2728,8 @@ packages: node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - node-releases@2.0.14: - resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + node-releases@2.0.17: + resolution: {integrity: sha512-Ww6ZlOiEQfPfXM45v17oabk77Z7mg5bOt7AjDyzy7RjK9OrLrLC8dyZQoAPEOtFX9SaNf1Tdvr5gRJWdTJj7GA==} normalize-package-data@2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} @@ -2767,11 +2746,12 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - object-inspect@1.13.1: - resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} + object-inspect@1.13.2: + resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} + engines: {node: '>= 0.4'} - object-is@1.1.5: - resolution: {integrity: sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==} + object-is@1.1.6: + resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} engines: {node: '>= 0.4'} object-keys@1.1.1: @@ -2800,8 +2780,8 @@ packages: openapi-fuzzer-core@1.0.6: resolution: {integrity: sha512-FJNJIfgUFuv4NmVGq9MYdoKra2GrkDy2uhIjE2YGlw30UA1glf4SXLMhI4UwdcJ8jisKdIxi7lXrfej8GvNW5w==} - optionator@0.9.3: - resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} ora@5.4.1: @@ -2832,11 +2812,14 @@ packages: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} - pactum-matchers@1.1.6: - resolution: {integrity: sha512-55io32NeOKbLpHKKPzYDOr+N2dseTzMbj1Gj1y+zvOkKK6NDf5BT5pxglfqLN/ra3ig5zvbrKFUqZIWjAWboog==} + package-json-from-dist@1.0.0: + resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} + + pactum-matchers@1.1.7: + resolution: {integrity: sha512-RqwewcUje6vhcYQGbPfdSXkcp/Vtwn4WmmTWLSmqp0CGxBroCEqRg3JMIjkjQTZCd2VmG+tTcQw+n4P/iuqv3Q==} - pactum@3.6.0: - resolution: {integrity: sha512-pR3OzFXi7K3oNADMoEiC8Err51aCJS3FTS0MXHqqr3YDlSCb/NcdJjcoTGh443W0WilvnHFL4OsaGtO0sWv76A==} + pactum@3.7.0: + resolution: {integrity: sha512-zYw+84hb/rQZ2/mEw2xla/qowLnOvlIvbTYeAhQS2pu3ZAyx+pXJLntcCqZ0QI9PUWwx8HqCzm2qBclZTtZUzw==} engines: {node: '>=10'} pad@2.3.0: @@ -2868,8 +2851,8 @@ packages: resolution: {integrity: sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==} engines: {node: '>= 0.4.0'} - passport@0.6.0: - resolution: {integrity: sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==} + passport@0.7.0: + resolution: {integrity: sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==} engines: {node: '>= 0.4.0'} path-exists@4.0.0: @@ -2887,9 +2870,9 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-scurry@1.10.1: - resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==} - engines: {node: '>=16 || 14 >=14.17'} + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} path-to-regexp@0.1.7: resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} @@ -2907,24 +2890,24 @@ packages: pdfkit@0.14.0: resolution: {integrity: sha512-Hnor8/78jhHm6ONrxWhrqOwAVALlBnFyWOF8sstBZMiqHZgZ5A6RU+Q3yahhw82plxpT7LOfH3b3qcOX6rzMQg==} - peek-readable@5.0.0: - resolution: {integrity: sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==} + peek-readable@5.1.3: + resolution: {integrity: sha512-kCsc9HwH5RgVA3H3VqkWFyGQwsxUxLdiSX1d5nqAm7hnMFjNFX1VhBLmJoUY0hZNc8gmDNgBkLjfhiWPsziXWA==} engines: {node: '>=14.16'} - phin@3.7.0: - resolution: {integrity: sha512-DqnVNrpYhKGBZppNKprD+UJylMeEKOZxHgPB+ZP6mGzf3uA2uox4Ep9tUm+rUc8WLIdHT3HcAE4X8fhwQA9JKg==} + phin@3.7.1: + resolution: {integrity: sha512-GEazpTWwTZaEQ9RhL7Nyz0WwqilbqgLahDM3D0hxWwmVDI52nXEybHqiN6/elwpkJBhcuj+WbBu+QfT0uhPGfQ==} engines: {node: '>= 8'} - picocolors@1.0.0: - resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + picocolors@1.0.1: + resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - picomatch@3.0.1: - resolution: {integrity: sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==} - engines: {node: '>=10'} + picomatch@4.0.1: + resolution: {integrity: sha512-xUXwsxNjwTQ8K3GnT4pCJm+xq3RUPQbmkYJTP5aFIfNIvbcc/4MUxgBaaRSZJ6yGJZiGSyYlM6MzwTsRk8SYCg==} + engines: {node: '>=12'} pirates@4.0.6: resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} @@ -2974,8 +2957,8 @@ packages: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - prisma@5.8.1: - resolution: {integrity: sha512-N6CpjzECnUHZ5beeYpDzkt2rYpEdAeqXX2dweu6BoQaeYkNZrC/WJHM+5MO/uidFHTak8QhkPKBWck1o/4MD4A==} + prisma@5.17.0: + resolution: {integrity: sha512-m4UWkN5lBE6yevqeOxEvmepnL5cNPEjzMw2IqDB59AcEV6w7D8vGljDLd1gPFH+W6gUxw9x7/RmN5dCS/WTPxA==} engines: {node: '>=16.13'} hasBin: true @@ -3001,8 +2984,8 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - pure-rand@6.0.4: - resolution: {integrity: sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==} + pure-rand@6.1.0: + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} qs@6.11.0: resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} @@ -3018,16 +3001,12 @@ packages: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} - raw-body@2.5.1: - resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==} - engines: {node: '>= 0.8'} - raw-body@2.5.2: resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} engines: {node: '>= 0.8'} - react-is@18.2.0: - resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} read-pkg-up@7.0.1: resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} @@ -3047,18 +3026,10 @@ packages: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} - readable-web-to-node-stream@3.0.2: - resolution: {integrity: sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==} - engines: {node: '>=8'} - readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} - rechoir@0.6.2: - resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} - engines: {node: '>= 0.10'} - reflect-metadata@0.1.14: resolution: {integrity: sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==} @@ -3115,11 +3086,7 @@ packages: rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - hasBin: true - - rimraf@4.4.1: - resolution: {integrity: sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==} - engines: {node: '>=14'} + deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true run-async@2.4.1: @@ -3167,8 +3134,8 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.5.4: - resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} engines: {node: '>=10'} hasBin: true @@ -3176,19 +3143,15 @@ packages: resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} engines: {node: '>= 0.8.0'} - serialize-javascript@6.0.1: - resolution: {integrity: sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==} + serialize-javascript@6.0.2: + resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} serve-static@1.15.0: resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} engines: {node: '>= 0.8.0'} - set-function-length@1.1.1: - resolution: {integrity: sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==} - engines: {node: '>= 0.4'} - - set-function-length@1.2.1: - resolution: {integrity: sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==} + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} set-function-name@2.0.2: @@ -3198,9 +3161,9 @@ packages: setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - sharp@0.33.2: - resolution: {integrity: sha512-WlYOPyyPDiiM07j/UO+E720ju6gtNtHjEGg5vovUk1Lgxyjm2LFO+37Nt/UI3MMh2l6hxTWQWi7qk3cXJTutcQ==} - engines: {libvips: '>=8.15.1', node: ^18.17.0 || ^20.3.0 || >=21.0.0} + sharp@0.33.4: + resolution: {integrity: sha512-7i/dt5kGl7qR4gwPRD2biwD2/SvBn3O04J77XKFgL2OnZtQw+AG9wnuS/csmu80nPRHLYE9E41fyEiG8nhH6/Q==} + engines: {libvips: '>=8.15.2', node: ^18.17.0 || ^20.3.0 || >=21.0.0} shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} @@ -3210,13 +3173,9 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shelljs@0.8.5: - resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==} - engines: {node: '>=4'} - hasBin: true - - side-channel@1.0.4: - resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + side-channel@1.0.6: + resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + engines: {node: '>= 0.4'} signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} @@ -3256,14 +3215,14 @@ packages: spdx-correct@3.2.0: resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} - spdx-exceptions@2.3.0: - resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==} + spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} spdx-expression-parse@3.0.1: resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} - spdx-license-ids@3.0.16: - resolution: {integrity: sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==} + spdx-license-ids@3.0.18: + resolution: {integrity: sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==} sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} @@ -3344,9 +3303,9 @@ packages: strnum@1.0.5: resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} - strtok3@7.0.0: - resolution: {integrity: sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==} - engines: {node: '>=14.16'} + strtok3@8.0.0: + resolution: {integrity: sha512-YzsSP+kli3q1tTA04HsfY1GqIapi3vEMN38jJ+aLpFyoev0onI/RuZWBGkQgc7ORynb3LW4cSOP3XtsKV21X6Q==} + engines: {node: '>=16'} supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} @@ -3364,8 +3323,8 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - swagger-ui-dist@5.11.0: - resolution: {integrity: sha512-j0PIATqQSEFGOLmiJOJZj1X1Jt6bFIur3JpY7+ghliUnfZs0fpWDdHEkn9q7QUlBtKbkn6TepvSxTqnE8l3s0A==} + swagger-ui-dist@5.17.14: + resolution: {integrity: sha512-CVbSfaLpstV65OnSjbXfVd6Sta3q3F7Cj/yYuvHMp1P90LztOLs6PfUnKEVAeiIVQt9u2SaPwv0LiH/OyMjHRw==} symbol-observable@1.0.1: resolution: {integrity: sha512-Kb3PrPYz4HanVF1LVGuAdW6LoVgIwjUYJGzFe7NDrBLCN4lsV/5J0MFurV+ygS4bRVwrCEt2c7MQ1R2a72oJDw==} @@ -3379,8 +3338,8 @@ packages: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} engines: {node: '>=6'} - terser-webpack-plugin@5.3.9: - resolution: {integrity: sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==} + terser-webpack-plugin@5.3.10: + resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==} engines: {node: '>= 10.13.0'} peerDependencies: '@swc/core': '*' @@ -3395,8 +3354,8 @@ packages: uglify-js: optional: true - terser@5.26.0: - resolution: {integrity: sha512-dytTGoE2oHgbNV9nTzgBEPaqAWvcJNl66VZ0BkJqlvp71IjO8CxdBx/ykCNb47cLnCmCvRZ6ZR0tLkqvZCdVBQ==} + terser@5.31.3: + resolution: {integrity: sha512-pAfYn3NIZLyZpa83ZKigvj6Rn9c/vd5KfYGX7cN1mnzqgDcxWvrU5ZtAfIKhEXz9nRecw4z3LXkjaq96/qZqAA==} engines: {node: '>=10'} hasBin: true @@ -3432,8 +3391,8 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} - token-types@5.0.1: - resolution: {integrity: sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==} + token-types@6.0.0: + resolution: {integrity: sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA==} engines: {node: '>=14.16'} tr46@0.0.3: @@ -3500,8 +3459,8 @@ packages: tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} - tslib@2.6.2: - resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + tslib@2.6.3: + resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} tsutils@3.21.0: resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} @@ -3554,6 +3513,10 @@ packages: resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==} engines: {node: '>=8'} + uint8array-extras@1.4.0: + resolution: {integrity: sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ==} + engines: {node: '>=18'} + unicode-properties@1.4.1: resolution: {integrity: sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==} @@ -3568,8 +3531,8 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} - update-browserslist-db@1.0.13: - resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} + update-browserslist-db@1.1.0: + resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -3584,26 +3547,22 @@ packages: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} - uuid@9.0.0: - resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==} - hasBin: true - - uuid@9.0.1: - resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + uuid@10.0.0: + resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} hasBin: true v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - v8-to-istanbul@9.2.0: - resolution: {integrity: sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==} + v8-to-istanbul@9.3.0: + resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} engines: {node: '>=10.12.0'} validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} - validator@13.11.0: - resolution: {integrity: sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==} + validator@13.12.0: + resolution: {integrity: sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==} engines: {node: '>= 0.10'} vary@1.1.2: @@ -3625,8 +3584,8 @@ packages: walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} - watchpack@2.4.0: - resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==} + watchpack@2.4.1: + resolution: {integrity: sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==} engines: {node: '>=10.13.0'} wcwidth@1.0.1: @@ -3643,8 +3602,8 @@ packages: resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==} engines: {node: '>=10.13.0'} - webpack@5.89.0: - resolution: {integrity: sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==} + webpack@5.92.1: + resolution: {integrity: sha512-JECQ7IwJb+7fgUFBlrJzbyu3GEuNBcdqr1LD7IbSzwkSmIevTm8PF+wej3Oxuz/JFBUZ6O1o43zsPkwm1C4TmA==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -3659,11 +3618,12 @@ packages: which-boxed-primitive@1.0.2: resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} - which-collection@1.0.1: - resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==} + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} - which-typed-array@1.1.14: - resolution: {integrity: sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==} + which-typed-array@1.1.15: + resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} engines: {node: '>= 0.4'} which@2.0.2: @@ -3671,6 +3631,10 @@ packages: engines: {node: '>= 8'} hasBin: true + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + wrap-ansi@6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} @@ -3701,9 +3665,6 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} @@ -3722,40 +3683,38 @@ packages: snapshots: - '@aashutoshrathi/word-wrap@1.2.6': {} - - '@ampproject/remapping@2.2.1': + '@ampproject/remapping@2.3.0': dependencies: - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.20 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 - '@angular-devkit/core@17.0.9(chokidar@3.5.3)': + '@angular-devkit/core@17.3.8(chokidar@3.6.0)': dependencies: ajv: 8.12.0 ajv-formats: 2.1.1(ajv@8.12.0) - jsonc-parser: 3.2.0 - picomatch: 3.0.1 + jsonc-parser: 3.2.1 + picomatch: 4.0.1 rxjs: 7.8.1 source-map: 0.7.4 optionalDependencies: - chokidar: 3.5.3 + chokidar: 3.6.0 - '@angular-devkit/schematics-cli@17.0.9(chokidar@3.5.3)': + '@angular-devkit/schematics-cli@17.3.8(chokidar@3.6.0)': dependencies: - '@angular-devkit/core': 17.0.9(chokidar@3.5.3) - '@angular-devkit/schematics': 17.0.9(chokidar@3.5.3) + '@angular-devkit/core': 17.3.8(chokidar@3.6.0) + '@angular-devkit/schematics': 17.3.8(chokidar@3.6.0) ansi-colors: 4.1.3 - inquirer: 9.2.11 + inquirer: 9.2.15 symbol-observable: 4.0.0 yargs-parser: 21.1.1 transitivePeerDependencies: - chokidar - '@angular-devkit/schematics@17.0.9(chokidar@3.5.3)': + '@angular-devkit/schematics@17.3.8(chokidar@3.6.0)': dependencies: - '@angular-devkit/core': 17.0.9(chokidar@3.5.3) - jsonc-parser: 3.2.0 - magic-string: 0.30.5 + '@angular-devkit/core': 17.3.8(chokidar@3.6.0) + jsonc-parser: 3.2.1 + magic-string: 0.30.8 ora: 5.4.1 rxjs: 7.8.1 transitivePeerDependencies: @@ -3763,201 +3722,209 @@ snapshots: '@arr/every@1.0.1': {} - '@babel/code-frame@7.23.5': + '@babel/code-frame@7.24.7': dependencies: - '@babel/highlight': 7.23.4 - chalk: 2.4.2 + '@babel/highlight': 7.24.7 + picocolors: 1.0.1 - '@babel/compat-data@7.23.5': {} + '@babel/compat-data@7.24.9': {} - '@babel/core@7.23.6': + '@babel/core@7.24.9': dependencies: - '@ampproject/remapping': 2.2.1 - '@babel/code-frame': 7.23.5 - '@babel/generator': 7.23.6 - '@babel/helper-compilation-targets': 7.23.6 - '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.6) - '@babel/helpers': 7.23.6 - '@babel/parser': 7.23.6 - '@babel/template': 7.22.15 - '@babel/traverse': 7.23.6 - '@babel/types': 7.23.6 + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.24.10 + '@babel/helper-compilation-targets': 7.24.8 + '@babel/helper-module-transforms': 7.24.9(@babel/core@7.24.9) + '@babel/helpers': 7.24.8 + '@babel/parser': 7.24.8 + '@babel/template': 7.24.7 + '@babel/traverse': 7.24.8 + '@babel/types': 7.24.9 convert-source-map: 2.0.0 - debug: 4.3.4 + debug: 4.3.5 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/generator@7.23.6': + '@babel/generator@7.24.10': dependencies: - '@babel/types': 7.23.6 - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.20 + '@babel/types': 7.24.9 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 jsesc: 2.5.2 - '@babel/helper-compilation-targets@7.23.6': + '@babel/helper-compilation-targets@7.24.8': dependencies: - '@babel/compat-data': 7.23.5 - '@babel/helper-validator-option': 7.23.5 - browserslist: 4.22.2 + '@babel/compat-data': 7.24.9 + '@babel/helper-validator-option': 7.24.8 + browserslist: 4.23.2 lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-environment-visitor@7.22.20': {} + '@babel/helper-environment-visitor@7.24.7': + dependencies: + '@babel/types': 7.24.9 - '@babel/helper-function-name@7.23.0': + '@babel/helper-function-name@7.24.7': dependencies: - '@babel/template': 7.22.15 - '@babel/types': 7.23.6 + '@babel/template': 7.24.7 + '@babel/types': 7.24.9 - '@babel/helper-hoist-variables@7.22.5': + '@babel/helper-hoist-variables@7.24.7': dependencies: - '@babel/types': 7.23.6 + '@babel/types': 7.24.9 - '@babel/helper-module-imports@7.22.15': + '@babel/helper-module-imports@7.24.7': dependencies: - '@babel/types': 7.23.6 + '@babel/traverse': 7.24.8 + '@babel/types': 7.24.9 + transitivePeerDependencies: + - supports-color - '@babel/helper-module-transforms@7.23.3(@babel/core@7.23.6)': + '@babel/helper-module-transforms@7.24.9(@babel/core@7.24.9)': dependencies: - '@babel/core': 7.23.6 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-module-imports': 7.22.15 - '@babel/helper-simple-access': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 - '@babel/helper-validator-identifier': 7.22.20 + '@babel/core': 7.24.9 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-simple-access': 7.24.7 + '@babel/helper-split-export-declaration': 7.24.7 + '@babel/helper-validator-identifier': 7.24.7 + transitivePeerDependencies: + - supports-color - '@babel/helper-plugin-utils@7.22.5': {} + '@babel/helper-plugin-utils@7.24.8': {} - '@babel/helper-simple-access@7.22.5': + '@babel/helper-simple-access@7.24.7': dependencies: - '@babel/types': 7.23.6 + '@babel/traverse': 7.24.8 + '@babel/types': 7.24.9 + transitivePeerDependencies: + - supports-color - '@babel/helper-split-export-declaration@7.22.6': + '@babel/helper-split-export-declaration@7.24.7': dependencies: - '@babel/types': 7.23.6 + '@babel/types': 7.24.9 - '@babel/helper-string-parser@7.23.4': {} + '@babel/helper-string-parser@7.24.8': {} - '@babel/helper-validator-identifier@7.22.20': {} + '@babel/helper-validator-identifier@7.24.7': {} - '@babel/helper-validator-option@7.23.5': {} + '@babel/helper-validator-option@7.24.8': {} - '@babel/helpers@7.23.6': + '@babel/helpers@7.24.8': dependencies: - '@babel/template': 7.22.15 - '@babel/traverse': 7.23.6 - '@babel/types': 7.23.6 - transitivePeerDependencies: - - supports-color + '@babel/template': 7.24.7 + '@babel/types': 7.24.9 - '@babel/highlight@7.23.4': + '@babel/highlight@7.24.7': dependencies: - '@babel/helper-validator-identifier': 7.22.20 + '@babel/helper-validator-identifier': 7.24.7 chalk: 2.4.2 js-tokens: 4.0.0 + picocolors: 1.0.1 - '@babel/parser@7.23.6': + '@babel/parser@7.24.8': dependencies: - '@babel/types': 7.23.6 + '@babel/types': 7.24.9 - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.23.6)': + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.9)': dependencies: - '@babel/core': 7.23.6 - '@babel/helper-plugin-utils': 7.22.5 + '@babel/core': 7.24.9 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.23.6)': + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.24.9)': dependencies: - '@babel/core': 7.23.6 - '@babel/helper-plugin-utils': 7.22.5 + '@babel/core': 7.24.9 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.23.6)': + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.9)': dependencies: - '@babel/core': 7.23.6 - '@babel/helper-plugin-utils': 7.22.5 + '@babel/core': 7.24.9 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.23.6)': + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.9)': dependencies: - '@babel/core': 7.23.6 - '@babel/helper-plugin-utils': 7.22.5 + '@babel/core': 7.24.9 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.6)': + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.9)': dependencies: - '@babel/core': 7.23.6 - '@babel/helper-plugin-utils': 7.22.5 + '@babel/core': 7.24.9 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.6)': + '@babel/plugin-syntax-jsx@7.24.7(@babel/core@7.24.9)': dependencies: - '@babel/core': 7.23.6 - '@babel/helper-plugin-utils': 7.22.5 + '@babel/core': 7.24.9 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.23.6)': + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.9)': dependencies: - '@babel/core': 7.23.6 - '@babel/helper-plugin-utils': 7.22.5 + '@babel/core': 7.24.9 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.6)': + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.9)': dependencies: - '@babel/core': 7.23.6 - '@babel/helper-plugin-utils': 7.22.5 + '@babel/core': 7.24.9 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.6)': + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.9)': dependencies: - '@babel/core': 7.23.6 - '@babel/helper-plugin-utils': 7.22.5 + '@babel/core': 7.24.9 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.6)': + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.9)': dependencies: - '@babel/core': 7.23.6 - '@babel/helper-plugin-utils': 7.22.5 + '@babel/core': 7.24.9 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.6)': + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.9)': dependencies: - '@babel/core': 7.23.6 - '@babel/helper-plugin-utils': 7.22.5 + '@babel/core': 7.24.9 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.6)': + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.9)': dependencies: - '@babel/core': 7.23.6 - '@babel/helper-plugin-utils': 7.22.5 + '@babel/core': 7.24.9 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.23.6)': + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.9)': dependencies: - '@babel/core': 7.23.6 - '@babel/helper-plugin-utils': 7.22.5 + '@babel/core': 7.24.9 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.23.6)': + '@babel/plugin-syntax-typescript@7.24.7(@babel/core@7.24.9)': dependencies: - '@babel/core': 7.23.6 - '@babel/helper-plugin-utils': 7.22.5 + '@babel/core': 7.24.9 + '@babel/helper-plugin-utils': 7.24.8 - '@babel/template@7.22.15': + '@babel/template@7.24.7': dependencies: - '@babel/code-frame': 7.23.5 - '@babel/parser': 7.23.6 - '@babel/types': 7.23.6 + '@babel/code-frame': 7.24.7 + '@babel/parser': 7.24.8 + '@babel/types': 7.24.9 - '@babel/traverse@7.23.6': + '@babel/traverse@7.24.8': dependencies: - '@babel/code-frame': 7.23.5 - '@babel/generator': 7.23.6 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-function-name': 7.23.0 - '@babel/helper-hoist-variables': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 - '@babel/parser': 7.23.6 - '@babel/types': 7.23.6 - debug: 4.3.4 + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.24.10 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-function-name': 7.24.7 + '@babel/helper-hoist-variables': 7.24.7 + '@babel/helper-split-export-declaration': 7.24.7 + '@babel/parser': 7.24.8 + '@babel/types': 7.24.9 + debug: 4.3.5 globals: 11.12.0 transitivePeerDependencies: - supports-color - '@babel/types@7.23.6': + '@babel/types@7.24.9': dependencies: - '@babel/helper-string-parser': 7.23.4 - '@babel/helper-validator-identifier': 7.22.20 + '@babel/helper-string-parser': 7.24.8 + '@babel/helper-validator-identifier': 7.24.7 to-fast-properties: 2.0.0 '@bcoe/v8-coverage@0.2.3': {} @@ -3969,25 +3936,25 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.9 - '@emnapi/runtime@0.45.0': + '@emnapi/runtime@1.2.0': dependencies: - tslib: 2.6.2 + tslib: 2.6.3 optional: true - '@eslint-community/eslint-utils@4.4.0(eslint@8.56.0)': + '@eslint-community/eslint-utils@4.4.0(eslint@8.57.0)': dependencies: - eslint: 8.56.0 + eslint: 8.57.0 eslint-visitor-keys: 3.4.3 - '@eslint-community/regexpp@4.10.0': {} + '@eslint-community/regexpp@4.11.0': {} '@eslint/eslintrc@2.1.4': dependencies: ajv: 6.12.6 - debug: 4.3.4 + debug: 4.3.5 espree: 9.6.1 globals: 13.24.0 - ignore: 5.3.0 + ignore: 5.3.1 import-fresh: 3.3.0 js-yaml: 4.1.0 minimatch: 3.1.2 @@ -3995,97 +3962,106 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@8.56.0': {} + '@eslint/js@8.57.0': {} '@exodus/schemasafe@1.3.0': {} '@faker-js/faker@7.6.0': {} - '@humanwhocodes/config-array@0.11.13': + '@fast-csv/parse@5.0.0': + dependencies: + lodash.escaperegexp: 4.1.2 + lodash.groupby: 4.6.0 + lodash.isfunction: 3.0.9 + lodash.isnil: 4.0.0 + lodash.isundefined: 3.0.1 + lodash.uniq: 4.5.0 + + '@humanwhocodes/config-array@0.11.14': dependencies: - '@humanwhocodes/object-schema': 2.0.1 - debug: 4.3.4 + '@humanwhocodes/object-schema': 2.0.3 + debug: 4.3.5 minimatch: 3.1.2 transitivePeerDependencies: - supports-color '@humanwhocodes/module-importer@1.0.1': {} - '@humanwhocodes/object-schema@2.0.1': {} + '@humanwhocodes/object-schema@2.0.3': {} - '@img/sharp-darwin-arm64@0.33.2': + '@img/sharp-darwin-arm64@0.33.4': optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.0.1 + '@img/sharp-libvips-darwin-arm64': 1.0.2 optional: true - '@img/sharp-darwin-x64@0.33.2': + '@img/sharp-darwin-x64@0.33.4': optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.0.1 + '@img/sharp-libvips-darwin-x64': 1.0.2 optional: true - '@img/sharp-libvips-darwin-arm64@1.0.1': + '@img/sharp-libvips-darwin-arm64@1.0.2': optional: true - '@img/sharp-libvips-darwin-x64@1.0.1': + '@img/sharp-libvips-darwin-x64@1.0.2': optional: true - '@img/sharp-libvips-linux-arm64@1.0.1': + '@img/sharp-libvips-linux-arm64@1.0.2': optional: true - '@img/sharp-libvips-linux-arm@1.0.1': + '@img/sharp-libvips-linux-arm@1.0.2': optional: true - '@img/sharp-libvips-linux-s390x@1.0.1': + '@img/sharp-libvips-linux-s390x@1.0.2': optional: true - '@img/sharp-libvips-linux-x64@1.0.1': + '@img/sharp-libvips-linux-x64@1.0.2': optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.0.1': + '@img/sharp-libvips-linuxmusl-arm64@1.0.2': optional: true - '@img/sharp-libvips-linuxmusl-x64@1.0.1': + '@img/sharp-libvips-linuxmusl-x64@1.0.2': optional: true - '@img/sharp-linux-arm64@0.33.2': + '@img/sharp-linux-arm64@0.33.4': optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.0.1 + '@img/sharp-libvips-linux-arm64': 1.0.2 optional: true - '@img/sharp-linux-arm@0.33.2': + '@img/sharp-linux-arm@0.33.4': optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.0.1 + '@img/sharp-libvips-linux-arm': 1.0.2 optional: true - '@img/sharp-linux-s390x@0.33.2': + '@img/sharp-linux-s390x@0.33.4': optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.0.1 + '@img/sharp-libvips-linux-s390x': 1.0.2 optional: true - '@img/sharp-linux-x64@0.33.2': + '@img/sharp-linux-x64@0.33.4': optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.0.1 + '@img/sharp-libvips-linux-x64': 1.0.2 optional: true - '@img/sharp-linuxmusl-arm64@0.33.2': + '@img/sharp-linuxmusl-arm64@0.33.4': optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.0.1 + '@img/sharp-libvips-linuxmusl-arm64': 1.0.2 optional: true - '@img/sharp-linuxmusl-x64@0.33.2': + '@img/sharp-linuxmusl-x64@0.33.4': optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.0.1 + '@img/sharp-libvips-linuxmusl-x64': 1.0.2 optional: true - '@img/sharp-wasm32@0.33.2': + '@img/sharp-wasm32@0.33.4': dependencies: - '@emnapi/runtime': 0.45.0 + '@emnapi/runtime': 1.2.0 optional: true - '@img/sharp-win32-ia32@0.33.2': + '@img/sharp-win32-ia32@0.33.4': optional: true - '@img/sharp-win32-x64@0.33.2': + '@img/sharp-win32-x64@0.33.4': optional: true '@isaacs/cliui@8.0.2': @@ -4142,7 +4118,7 @@ snapshots: jest-util: 29.7.0 jest-validate: 29.7.0 jest-watcher: 29.7.0 - micromatch: 4.0.5 + micromatch: 4.0.7 pretty-format: 29.7.0 slash: 3.0.0 strip-ansi: 6.0.1 @@ -4194,7 +4170,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.20 + '@jridgewell/trace-mapping': 0.3.25 '@types/node': 18.15.11 chalk: 4.1.2 collect-v8-coverage: 1.0.2 @@ -4202,17 +4178,17 @@ snapshots: glob: 7.2.3 graceful-fs: 4.2.11 istanbul-lib-coverage: 3.2.2 - istanbul-lib-instrument: 6.0.1 + istanbul-lib-instrument: 6.0.3 istanbul-lib-report: 3.0.1 istanbul-lib-source-maps: 4.0.1 - istanbul-reports: 3.1.6 + istanbul-reports: 3.1.7 jest-message-util: 29.7.0 jest-util: 29.7.0 jest-worker: 29.7.0 slash: 3.0.0 string-length: 4.0.2 strip-ansi: 6.0.1 - v8-to-istanbul: 9.2.0 + v8-to-istanbul: 9.3.0 transitivePeerDependencies: - supports-color @@ -4222,7 +4198,7 @@ snapshots: '@jest/source-map@29.6.3': dependencies: - '@jridgewell/trace-mapping': 0.3.20 + '@jridgewell/trace-mapping': 0.3.25 callsites: 3.1.0 graceful-fs: 4.2.11 @@ -4242,9 +4218,9 @@ snapshots: '@jest/transform@29.7.0': dependencies: - '@babel/core': 7.23.6 + '@babel/core': 7.24.9 '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.20 + '@jridgewell/trace-mapping': 0.3.25 babel-plugin-istanbul: 6.1.1 chalk: 4.1.2 convert-source-map: 2.0.0 @@ -4253,7 +4229,7 @@ snapshots: jest-haste-map: 29.7.0 jest-regex-util: 29.6.3 jest-util: 29.7.0 - micromatch: 4.0.5 + micromatch: 4.0.7 pirates: 4.0.6 slash: 3.0.0 write-file-atomic: 4.0.2 @@ -4269,184 +4245,183 @@ snapshots: '@types/yargs': 17.0.32 chalk: 4.1.2 - '@jridgewell/gen-mapping@0.3.3': + '@jridgewell/gen-mapping@0.3.5': dependencies: - '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 - '@jridgewell/trace-mapping': 0.3.20 + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 - '@jridgewell/resolve-uri@3.1.1': {} + '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/set-array@1.1.2': {} + '@jridgewell/set-array@1.2.1': {} - '@jridgewell/source-map@0.3.5': + '@jridgewell/source-map@0.3.6': dependencies: - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.20 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 - '@jridgewell/sourcemap-codec@1.4.15': {} + '@jridgewell/sourcemap-codec@1.5.0': {} - '@jridgewell/trace-mapping@0.3.20': + '@jridgewell/trace-mapping@0.3.25': dependencies: - '@jridgewell/resolve-uri': 3.1.1 - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 '@jridgewell/trace-mapping@0.3.9': dependencies: - '@jridgewell/resolve-uri': 3.1.1 - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 - '@ljharb/through@2.3.11': + '@ljharb/through@2.3.13': dependencies: - call-bind: 1.0.5 + call-bind: 1.0.7 '@lukeed/csprng@1.1.0': {} - '@nestjs/axios@3.0.2(@nestjs/common@10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(axios@1.6.7)(rxjs@7.8.1)': + '@microsoft/tsdoc@0.15.0': {} + + '@nestjs/axios@3.0.2(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(axios@1.7.2)(rxjs@7.8.1)': dependencies: - '@nestjs/common': 10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) - axios: 1.6.7 + '@nestjs/common': 10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + axios: 1.7.2 rxjs: 7.8.1 - '@nestjs/cli@10.3.0': + '@nestjs/cli@10.4.2': dependencies: - '@angular-devkit/core': 17.0.9(chokidar@3.5.3) - '@angular-devkit/schematics': 17.0.9(chokidar@3.5.3) - '@angular-devkit/schematics-cli': 17.0.9(chokidar@3.5.3) - '@nestjs/schematics': 10.1.0(chokidar@3.5.3)(typescript@5.3.3) + '@angular-devkit/core': 17.3.8(chokidar@3.6.0) + '@angular-devkit/schematics': 17.3.8(chokidar@3.6.0) + '@angular-devkit/schematics-cli': 17.3.8(chokidar@3.6.0) + '@nestjs/schematics': 10.1.2(chokidar@3.6.0)(typescript@5.3.3) chalk: 4.1.2 - chokidar: 3.5.3 - cli-table3: 0.6.3 + chokidar: 3.6.0 + cli-table3: 0.6.5 commander: 4.1.1 - fork-ts-checker-webpack-plugin: 9.0.2(typescript@5.3.3)(webpack@5.89.0) - glob: 10.3.10 + fork-ts-checker-webpack-plugin: 9.0.2(typescript@5.3.3)(webpack@5.92.1) + glob: 10.4.2 inquirer: 8.2.6 node-emoji: 1.11.0 ora: 5.4.1 - rimraf: 4.4.1 - shelljs: 0.8.5 - source-map-support: 0.5.21 tree-kill: 1.2.2 tsconfig-paths: 4.2.0 tsconfig-paths-webpack-plugin: 4.1.0 typescript: 5.3.3 - webpack: 5.89.0 + webpack: 5.92.1 webpack-node-externals: 3.0.0 transitivePeerDependencies: - esbuild - uglify-js - webpack-cli - '@nestjs/common@10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1)': + '@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1)': dependencies: iterare: 1.2.1 reflect-metadata: 0.1.14 rxjs: 7.8.1 - tslib: 2.6.2 + tslib: 2.6.3 uid: 2.0.2 optionalDependencies: class-transformer: 0.5.1 class-validator: 0.14.1 - '@nestjs/config@3.1.1(@nestjs/common@10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(reflect-metadata@0.1.14)': + '@nestjs/config@3.2.3(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(rxjs@7.8.1)': dependencies: - '@nestjs/common': 10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) - dotenv: 16.3.1 + '@nestjs/common': 10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + dotenv: 16.4.5 dotenv-expand: 10.0.0 lodash: 4.17.21 - reflect-metadata: 0.1.14 - uuid: 9.0.0 + rxjs: 7.8.1 - '@nestjs/core@10.3.1(@nestjs/common@10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@10.3.1)(reflect-metadata@0.1.14)(rxjs@7.8.1)': + '@nestjs/core@10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10)(reflect-metadata@0.1.14)(rxjs@7.8.1)': dependencies: - '@nestjs/common': 10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/common': 10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) '@nuxtjs/opencollective': 0.3.2 fast-safe-stringify: 2.1.1 iterare: 1.2.1 path-to-regexp: 3.2.0 reflect-metadata: 0.1.14 rxjs: 7.8.1 - tslib: 2.6.2 + tslib: 2.6.3 uid: 2.0.2 optionalDependencies: - '@nestjs/platform-express': 10.3.1(@nestjs/common@10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@10.3.1) + '@nestjs/platform-express': 10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@10.3.10) transitivePeerDependencies: - encoding - '@nestjs/jwt@10.2.0(@nestjs/common@10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))': + '@nestjs/jwt@10.2.0(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))': dependencies: - '@nestjs/common': 10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/common': 10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) '@types/jsonwebtoken': 9.0.5 jsonwebtoken: 9.0.2 - '@nestjs/mapped-types@2.0.4(@nestjs/common@10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)': + '@nestjs/mapped-types@2.0.5(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)': dependencies: - '@nestjs/common': 10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/common': 10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) reflect-metadata: 0.1.14 optionalDependencies: class-transformer: 0.5.1 class-validator: 0.14.1 - '@nestjs/passport@10.0.3(@nestjs/common@10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(passport@0.6.0)': + '@nestjs/passport@10.0.3(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(passport@0.7.0)': dependencies: - '@nestjs/common': 10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) - passport: 0.6.0 + '@nestjs/common': 10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + passport: 0.7.0 - '@nestjs/platform-express@10.3.1(@nestjs/common@10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@10.3.1)': + '@nestjs/platform-express@10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@10.3.10)': dependencies: - '@nestjs/common': 10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) - '@nestjs/core': 10.3.1(@nestjs/common@10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@10.3.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/common': 10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/core': 10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10)(reflect-metadata@0.1.14)(rxjs@7.8.1) body-parser: 1.20.2 cors: 2.8.5 - express: 4.18.2 + express: 4.19.2 multer: 1.4.4-lts.1 - tslib: 2.6.2 + tslib: 2.6.3 transitivePeerDependencies: - supports-color - '@nestjs/schematics@10.1.0(chokidar@3.5.3)(typescript@4.9.5)': + '@nestjs/schematics@10.1.2(chokidar@3.6.0)(typescript@4.9.5)': dependencies: - '@angular-devkit/core': 17.0.9(chokidar@3.5.3) - '@angular-devkit/schematics': 17.0.9(chokidar@3.5.3) + '@angular-devkit/core': 17.3.8(chokidar@3.6.0) + '@angular-devkit/schematics': 17.3.8(chokidar@3.6.0) comment-json: 4.2.3 - jsonc-parser: 3.2.0 + jsonc-parser: 3.3.1 pluralize: 8.0.0 typescript: 4.9.5 transitivePeerDependencies: - chokidar - '@nestjs/schematics@10.1.0(chokidar@3.5.3)(typescript@5.3.3)': + '@nestjs/schematics@10.1.2(chokidar@3.6.0)(typescript@5.3.3)': dependencies: - '@angular-devkit/core': 17.0.9(chokidar@3.5.3) - '@angular-devkit/schematics': 17.0.9(chokidar@3.5.3) + '@angular-devkit/core': 17.3.8(chokidar@3.6.0) + '@angular-devkit/schematics': 17.3.8(chokidar@3.6.0) comment-json: 4.2.3 - jsonc-parser: 3.2.0 + jsonc-parser: 3.3.1 pluralize: 8.0.0 typescript: 5.3.3 transitivePeerDependencies: - chokidar - '@nestjs/swagger@7.2.0(@nestjs/common@10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@10.3.1(@nestjs/common@10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@10.3.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)': + '@nestjs/swagger@7.4.0(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10)(reflect-metadata@0.1.14)(rxjs@7.8.1))(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)': dependencies: - '@nestjs/common': 10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) - '@nestjs/core': 10.3.1(@nestjs/common@10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@10.3.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) - '@nestjs/mapped-types': 2.0.4(@nestjs/common@10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14) + '@microsoft/tsdoc': 0.15.0 + '@nestjs/common': 10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/core': 10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/mapped-types': 2.0.5(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14) js-yaml: 4.1.0 lodash: 4.17.21 path-to-regexp: 3.2.0 reflect-metadata: 0.1.14 - swagger-ui-dist: 5.11.0 + swagger-ui-dist: 5.17.14 optionalDependencies: class-transformer: 0.5.1 class-validator: 0.14.1 - '@nestjs/testing@10.3.1(@nestjs/common@10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@10.3.1(@nestjs/common@10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@10.3.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@10.3.1(@nestjs/common@10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@10.3.1))': + '@nestjs/testing@10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@10.3.10))': dependencies: - '@nestjs/common': 10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) - '@nestjs/core': 10.3.1(@nestjs/common@10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@10.3.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) - tslib: 2.6.2 + '@nestjs/common': 10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/core': 10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10)(reflect-metadata@0.1.14)(rxjs@7.8.1) + tslib: 2.6.3 optionalDependencies: - '@nestjs/platform-express': 10.3.1(@nestjs/common@10.3.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@10.3.1) + '@nestjs/platform-express': 10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@10.3.10) '@nodelib/fs.scandir@2.1.5': dependencies: @@ -4458,7 +4433,7 @@ snapshots: '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 - fastq: 1.16.0 + fastq: 1.17.1 '@nuxtjs/opencollective@0.3.2': dependencies: @@ -4473,48 +4448,48 @@ snapshots: '@polka/url@0.5.0': {} - '@prisma/client@5.8.1(prisma@5.8.1)': + '@prisma/client@5.17.0(prisma@5.17.0)': optionalDependencies: - prisma: 5.8.1 + prisma: 5.17.0 - '@prisma/debug@5.8.1': {} + '@prisma/debug@5.17.0': {} - '@prisma/engines-version@5.8.1-1.78caf6feeaed953168c64e15a249c3e9a033ebe2': {} + '@prisma/engines-version@5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053': {} - '@prisma/engines@5.8.1': + '@prisma/engines@5.17.0': dependencies: - '@prisma/debug': 5.8.1 - '@prisma/engines-version': 5.8.1-1.78caf6feeaed953168c64e15a249c3e9a033ebe2 - '@prisma/fetch-engine': 5.8.1 - '@prisma/get-platform': 5.8.1 + '@prisma/debug': 5.17.0 + '@prisma/engines-version': 5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053 + '@prisma/fetch-engine': 5.17.0 + '@prisma/get-platform': 5.17.0 - '@prisma/fetch-engine@5.8.1': + '@prisma/fetch-engine@5.17.0': dependencies: - '@prisma/debug': 5.8.1 - '@prisma/engines-version': 5.8.1-1.78caf6feeaed953168c64e15a249c3e9a033ebe2 - '@prisma/get-platform': 5.8.1 + '@prisma/debug': 5.17.0 + '@prisma/engines-version': 5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053 + '@prisma/get-platform': 5.17.0 - '@prisma/get-platform@5.8.1': + '@prisma/get-platform@5.17.0': dependencies: - '@prisma/debug': 5.8.1 + '@prisma/debug': 5.17.0 '@sinclair/typebox@0.27.8': {} - '@sinonjs/commons@3.0.0': + '@sinonjs/commons@3.0.1': dependencies: type-detect: 4.0.8 '@sinonjs/fake-timers@10.3.0': dependencies: - '@sinonjs/commons': 3.0.0 + '@sinonjs/commons': 3.0.1 '@swc/helpers@0.3.17': dependencies: - tslib: 2.6.2 + tslib: 2.6.3 '@tokenizer/token@0.3.0': {} - '@tsconfig/node10@1.0.9': {} + '@tsconfig/node10@1.0.11': {} '@tsconfig/node12@1.0.11': {} @@ -4528,24 +4503,24 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.23.6 - '@babel/types': 7.23.6 - '@types/babel__generator': 7.6.7 + '@babel/parser': 7.24.8 + '@babel/types': 7.24.9 + '@types/babel__generator': 7.6.8 '@types/babel__template': 7.4.4 - '@types/babel__traverse': 7.20.4 + '@types/babel__traverse': 7.20.6 - '@types/babel__generator@7.6.7': + '@types/babel__generator@7.6.8': dependencies: - '@babel/types': 7.23.6 + '@babel/types': 7.24.9 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.23.6 - '@babel/types': 7.23.6 + '@babel/parser': 7.24.8 + '@babel/types': 7.24.9 - '@types/babel__traverse@7.20.4': + '@types/babel__traverse@7.20.6': dependencies: - '@babel/types': 7.23.6 + '@babel/types': 7.24.9 '@types/bcryptjs@2.4.6': {} @@ -4560,29 +4535,29 @@ snapshots: '@types/eslint-scope@3.7.7': dependencies: - '@types/eslint': 8.44.9 + '@types/eslint': 8.56.10 '@types/estree': 1.0.5 - '@types/eslint@8.44.9': + '@types/eslint@8.56.10': dependencies: '@types/estree': 1.0.5 '@types/json-schema': 7.0.15 '@types/estree@1.0.5': {} - '@types/express-serve-static-core@4.17.41': + '@types/express-serve-static-core@4.19.5': dependencies: '@types/node': 18.15.11 - '@types/qs': 6.9.10 + '@types/qs': 6.9.15 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 '@types/express@4.17.21': dependencies: '@types/body-parser': 1.19.5 - '@types/express-serve-static-core': 4.17.41 - '@types/qs': 6.9.10 - '@types/serve-static': 1.15.5 + '@types/express-serve-static-core': 4.19.5 + '@types/qs': 6.9.15 + '@types/serve-static': 1.15.7 '@types/graceful-fs@4.1.9': dependencies: @@ -4611,9 +4586,11 @@ snapshots: dependencies: '@types/node': 18.15.11 - '@types/mime@1.3.5': {} + '@types/jsonwebtoken@9.0.6': + dependencies: + '@types/node': 18.15.11 - '@types/mime@3.0.4': {} + '@types/mime@1.3.5': {} '@types/multer@1.4.11': dependencies: @@ -4630,7 +4607,7 @@ snapshots: '@types/passport-jwt@3.0.13': dependencies: '@types/express': 4.17.21 - '@types/jsonwebtoken': 9.0.5 + '@types/jsonwebtoken': 9.0.6 '@types/passport-strategy': 0.2.38 '@types/passport-strategy@0.2.38': @@ -4646,28 +4623,26 @@ snapshots: dependencies: '@types/node': 18.15.11 - '@types/qs@6.9.10': {} + '@types/qs@6.9.15': {} '@types/range-parser@1.2.7': {} - '@types/semver@7.5.6': {} + '@types/semver@7.5.8': {} '@types/send@0.17.4': dependencies: '@types/mime': 1.3.5 '@types/node': 18.15.11 - '@types/serve-static@1.15.5': + '@types/serve-static@1.15.7': dependencies: '@types/http-errors': 2.0.4 - '@types/mime': 3.0.4 '@types/node': 18.15.11 + '@types/send': 0.17.4 '@types/stack-utils@2.0.3': {} - '@types/uuid@9.0.8': {} - - '@types/validator@13.11.8': {} + '@types/validator@13.12.0': {} '@types/yargs-parser@21.0.3': {} @@ -4675,32 +4650,32 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.56.0)(typescript@4.9.5))(eslint@8.56.0)(typescript@4.9.5)': + '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@4.9.5))(eslint@8.57.0)(typescript@4.9.5)': dependencies: - '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 5.62.0(eslint@8.56.0)(typescript@4.9.5) + '@eslint-community/regexpp': 4.11.0 + '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@4.9.5) '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/type-utils': 5.62.0(eslint@8.56.0)(typescript@4.9.5) - '@typescript-eslint/utils': 5.62.0(eslint@8.56.0)(typescript@4.9.5) - debug: 4.3.4 - eslint: 8.56.0 + '@typescript-eslint/type-utils': 5.62.0(eslint@8.57.0)(typescript@4.9.5) + '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@4.9.5) + debug: 4.3.5 + eslint: 8.57.0 graphemer: 1.4.0 - ignore: 5.3.0 + ignore: 5.3.1 natural-compare-lite: 1.4.0 - semver: 7.5.4 + semver: 7.6.3 tsutils: 3.21.0(typescript@4.9.5) optionalDependencies: typescript: 4.9.5 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@5.62.0(eslint@8.56.0)(typescript@4.9.5)': + '@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@4.9.5)': dependencies: '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 '@typescript-eslint/typescript-estree': 5.62.0(typescript@4.9.5) - debug: 4.3.4 - eslint: 8.56.0 + debug: 4.3.5 + eslint: 8.57.0 optionalDependencies: typescript: 4.9.5 transitivePeerDependencies: @@ -4711,12 +4686,12 @@ snapshots: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/visitor-keys': 5.62.0 - '@typescript-eslint/type-utils@5.62.0(eslint@8.56.0)(typescript@4.9.5)': + '@typescript-eslint/type-utils@5.62.0(eslint@8.57.0)(typescript@4.9.5)': dependencies: '@typescript-eslint/typescript-estree': 5.62.0(typescript@4.9.5) - '@typescript-eslint/utils': 5.62.0(eslint@8.56.0)(typescript@4.9.5) - debug: 4.3.4 - eslint: 8.56.0 + '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@4.9.5) + debug: 4.3.5 + eslint: 8.57.0 tsutils: 3.21.0(typescript@4.9.5) optionalDependencies: typescript: 4.9.5 @@ -4729,27 +4704,27 @@ snapshots: dependencies: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/visitor-keys': 5.62.0 - debug: 4.3.4 + debug: 4.3.5 globby: 11.1.0 is-glob: 4.0.3 - semver: 7.5.4 + semver: 7.6.3 tsutils: 3.21.0(typescript@4.9.5) optionalDependencies: typescript: 4.9.5 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@5.62.0(eslint@8.56.0)(typescript@4.9.5)': + '@typescript-eslint/utils@5.62.0(eslint@8.57.0)(typescript@4.9.5)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@types/json-schema': 7.0.15 - '@types/semver': 7.5.6 + '@types/semver': 7.5.8 '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 '@typescript-eslint/typescript-estree': 5.62.0(typescript@4.9.5) - eslint: 8.56.0 + eslint: 8.57.0 eslint-scope: 5.1.1 - semver: 7.5.4 + semver: 7.6.3 transitivePeerDependencies: - supports-color - typescript @@ -4761,7 +4736,7 @@ snapshots: '@ungap/structured-clone@1.2.0': {} - '@webassemblyjs/ast@1.11.6': + '@webassemblyjs/ast@1.12.1': dependencies: '@webassemblyjs/helper-numbers': 1.11.6 '@webassemblyjs/helper-wasm-bytecode': 1.11.6 @@ -4770,7 +4745,7 @@ snapshots: '@webassemblyjs/helper-api-error@1.11.6': {} - '@webassemblyjs/helper-buffer@1.11.6': {} + '@webassemblyjs/helper-buffer@1.12.1': {} '@webassemblyjs/helper-numbers@1.11.6': dependencies: @@ -4780,12 +4755,12 @@ snapshots: '@webassemblyjs/helper-wasm-bytecode@1.11.6': {} - '@webassemblyjs/helper-wasm-section@1.11.6': + '@webassemblyjs/helper-wasm-section@1.12.1': dependencies: - '@webassemblyjs/ast': 1.11.6 - '@webassemblyjs/helper-buffer': 1.11.6 + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/helper-buffer': 1.12.1 '@webassemblyjs/helper-wasm-bytecode': 1.11.6 - '@webassemblyjs/wasm-gen': 1.11.6 + '@webassemblyjs/wasm-gen': 1.12.1 '@webassemblyjs/ieee754@1.11.6': dependencies: @@ -4797,44 +4772,44 @@ snapshots: '@webassemblyjs/utf8@1.11.6': {} - '@webassemblyjs/wasm-edit@1.11.6': + '@webassemblyjs/wasm-edit@1.12.1': dependencies: - '@webassemblyjs/ast': 1.11.6 - '@webassemblyjs/helper-buffer': 1.11.6 + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/helper-buffer': 1.12.1 '@webassemblyjs/helper-wasm-bytecode': 1.11.6 - '@webassemblyjs/helper-wasm-section': 1.11.6 - '@webassemblyjs/wasm-gen': 1.11.6 - '@webassemblyjs/wasm-opt': 1.11.6 - '@webassemblyjs/wasm-parser': 1.11.6 - '@webassemblyjs/wast-printer': 1.11.6 + '@webassemblyjs/helper-wasm-section': 1.12.1 + '@webassemblyjs/wasm-gen': 1.12.1 + '@webassemblyjs/wasm-opt': 1.12.1 + '@webassemblyjs/wasm-parser': 1.12.1 + '@webassemblyjs/wast-printer': 1.12.1 - '@webassemblyjs/wasm-gen@1.11.6': + '@webassemblyjs/wasm-gen@1.12.1': dependencies: - '@webassemblyjs/ast': 1.11.6 + '@webassemblyjs/ast': 1.12.1 '@webassemblyjs/helper-wasm-bytecode': 1.11.6 '@webassemblyjs/ieee754': 1.11.6 '@webassemblyjs/leb128': 1.11.6 '@webassemblyjs/utf8': 1.11.6 - '@webassemblyjs/wasm-opt@1.11.6': + '@webassemblyjs/wasm-opt@1.12.1': dependencies: - '@webassemblyjs/ast': 1.11.6 - '@webassemblyjs/helper-buffer': 1.11.6 - '@webassemblyjs/wasm-gen': 1.11.6 - '@webassemblyjs/wasm-parser': 1.11.6 + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/helper-buffer': 1.12.1 + '@webassemblyjs/wasm-gen': 1.12.1 + '@webassemblyjs/wasm-parser': 1.12.1 - '@webassemblyjs/wasm-parser@1.11.6': + '@webassemblyjs/wasm-parser@1.12.1': dependencies: - '@webassemblyjs/ast': 1.11.6 + '@webassemblyjs/ast': 1.12.1 '@webassemblyjs/helper-api-error': 1.11.6 '@webassemblyjs/helper-wasm-bytecode': 1.11.6 '@webassemblyjs/ieee754': 1.11.6 '@webassemblyjs/leb128': 1.11.6 '@webassemblyjs/utf8': 1.11.6 - '@webassemblyjs/wast-printer@1.11.6': + '@webassemblyjs/wast-printer@1.12.1': dependencies: - '@webassemblyjs/ast': 1.11.6 + '@webassemblyjs/ast': 1.12.1 '@xtuc/long': 4.2.2 '@xtuc/ieee754@1.2.0': {} @@ -4848,17 +4823,19 @@ snapshots: mime-types: 2.1.35 negotiator: 0.6.3 - acorn-import-assertions@1.9.0(acorn@8.11.2): + acorn-import-attributes@1.9.5(acorn@8.12.1): dependencies: - acorn: 8.11.2 + acorn: 8.12.1 - acorn-jsx@5.3.2(acorn@8.11.2): + acorn-jsx@5.3.2(acorn@8.12.1): dependencies: - acorn: 8.11.2 + acorn: 8.12.1 - acorn-walk@8.3.1: {} + acorn-walk@8.3.3: + dependencies: + acorn: 8.12.1 - acorn@8.11.2: {} + acorn@8.12.1: {} ajv-formats@2.1.1(ajv@8.12.0): optionalDependencies: @@ -4927,7 +4904,7 @@ snapshots: array-buffer-byte-length@1.0.1: dependencies: - call-bind: 1.0.5 + call-bind: 1.0.7 is-array-buffer: 3.0.4 array-flatten@1.1.1: {} @@ -4948,7 +4925,7 @@ snapshots: dependencies: possible-typed-array-names: 1.0.0 - axios@1.6.7: + axios@1.7.2: dependencies: follow-redirects: 1.15.6 form-data: 4.0.0 @@ -4956,13 +4933,13 @@ snapshots: transitivePeerDependencies: - debug - babel-jest@29.7.0(@babel/core@7.23.6): + babel-jest@29.7.0(@babel/core@7.24.9): dependencies: - '@babel/core': 7.23.6 + '@babel/core': 7.24.9 '@jest/transform': 29.7.0 '@types/babel__core': 7.20.5 babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.6.3(@babel/core@7.23.6) + babel-preset-jest: 29.6.3(@babel/core@7.24.9) chalk: 4.1.2 graceful-fs: 4.2.11 slash: 3.0.0 @@ -4971,7 +4948,7 @@ snapshots: babel-plugin-istanbul@6.1.1: dependencies: - '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-plugin-utils': 7.24.8 '@istanbuljs/load-nyc-config': 1.1.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-instrument: 5.2.1 @@ -4981,32 +4958,32 @@ snapshots: babel-plugin-jest-hoist@29.6.3: dependencies: - '@babel/template': 7.22.15 - '@babel/types': 7.23.6 + '@babel/template': 7.24.7 + '@babel/types': 7.24.9 '@types/babel__core': 7.20.5 - '@types/babel__traverse': 7.20.4 - - babel-preset-current-node-syntax@1.0.1(@babel/core@7.23.6): - dependencies: - '@babel/core': 7.23.6 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.6) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.23.6) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.6) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.23.6) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.6) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.6) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.6) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.6) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.6) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.6) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.6) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.6) - - babel-preset-jest@29.6.3(@babel/core@7.23.6): - dependencies: - '@babel/core': 7.23.6 + '@types/babel__traverse': 7.20.6 + + babel-preset-current-node-syntax@1.0.1(@babel/core@7.24.9): + dependencies: + '@babel/core': 7.24.9 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.9) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.24.9) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.9) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.9) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.9) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.9) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.9) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.9) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.9) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.9) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.9) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.9) + + babel-preset-jest@29.6.3(@babel/core@7.24.9): + dependencies: + '@babel/core': 7.24.9 babel-plugin-jest-hoist: 29.6.3 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.6) + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.9) backoff@2.5.0: dependencies: @@ -5022,7 +4999,7 @@ snapshots: bignumber.js@9.0.0: {} - binary-extensions@2.2.0: {} + binary-extensions@2.3.0: {} bl@4.1.0: dependencies: @@ -5030,23 +5007,6 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 - body-parser@1.20.1: - dependencies: - 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.11.0 - raw-body: 2.5.1 - type-is: 1.6.18 - unpipe: 1.0.0 - transitivePeerDependencies: - - supports-color - body-parser@1.20.2: dependencies: bytes: 3.1.2 @@ -5073,20 +5033,20 @@ snapshots: dependencies: balanced-match: 1.0.2 - braces@3.0.2: + braces@3.0.3: dependencies: - fill-range: 7.0.1 + fill-range: 7.1.1 brotli@1.3.3: dependencies: base64-js: 1.5.1 - browserslist@4.22.2: + browserslist@4.23.2: dependencies: - caniuse-lite: 1.0.30001570 - electron-to-chromium: 1.4.614 - node-releases: 2.0.14 - update-browserslist-db: 1.0.13(browserslist@4.22.2) + caniuse-lite: 1.0.30001642 + electron-to-chromium: 1.4.830 + node-releases: 2.0.17 + update-browserslist-db: 1.1.0(browserslist@4.23.2) bs-logger@0.2.6: dependencies: @@ -5111,19 +5071,13 @@ snapshots: bytes@3.1.2: {} - call-bind@1.0.5: - dependencies: - function-bind: 1.1.2 - get-intrinsic: 1.2.2 - set-function-length: 1.1.1 - call-bind@1.0.7: dependencies: es-define-property: 1.0.0 es-errors: 1.3.0 function-bind: 1.1.2 get-intrinsic: 1.2.4 - set-function-length: 1.2.1 + set-function-length: 1.2.2 callsites@3.1.0: {} @@ -5131,9 +5085,13 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001570: {} + caniuse-lite@1.0.30001642: {} - centra@2.6.0: {} + centra@2.7.0: + dependencies: + follow-redirects: 1.15.6 + transitivePeerDependencies: + - debug chalk@2.4.2: dependencies: @@ -5154,10 +5112,10 @@ snapshots: chardet@0.7.0: {} - chokidar@3.5.3: + chokidar@3.6.0: dependencies: anymatch: 3.1.3 - braces: 3.0.2 + braces: 3.0.3 glob-parent: 5.1.2 is-binary-path: 2.1.0 is-glob: 4.0.3 @@ -5166,19 +5124,19 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - chrome-trace-event@1.0.3: {} + chrome-trace-event@1.0.4: {} ci-info@3.9.0: {} - cjs-module-lexer@1.2.3: {} + cjs-module-lexer@1.3.1: {} class-transformer@0.5.1: {} class-validator@0.14.1: dependencies: - '@types/validator': 13.11.8 - libphonenumber-js: 1.10.54 - validator: 13.11.0 + '@types/validator': 13.12.0 + libphonenumber-js: 1.11.4 + validator: 13.12.0 cli-cursor@2.1.0: dependencies: @@ -5190,7 +5148,7 @@ snapshots: cli-spinners@2.9.2: {} - cli-table3@0.6.3: + cli-table3@0.6.5: dependencies: string-width: 4.2.3 optionalDependencies: @@ -5280,7 +5238,7 @@ snapshots: cookie-signature@1.0.6: {} - cookie@0.5.0: {} + cookie@0.6.0: {} core-util-is@1.0.2: {} @@ -5342,32 +5300,32 @@ snapshots: dependencies: ms: 2.0.0 - debug@4.3.4: + debug@4.3.5: dependencies: ms: 2.1.2 - dedent@1.5.1: {} + dedent@1.5.3: {} deep-equal@2.2.3: dependencies: array-buffer-byte-length: 1.0.1 - call-bind: 1.0.5 + call-bind: 1.0.7 es-get-iterator: 1.1.3 - get-intrinsic: 1.2.2 + get-intrinsic: 1.2.4 is-arguments: 1.1.1 is-array-buffer: 3.0.4 is-date-object: 1.0.5 is-regex: 1.1.4 is-shared-array-buffer: 1.0.3 isarray: 2.0.5 - object-is: 1.1.5 + object-is: 1.1.6 object-keys: 1.1.1 object.assign: 4.1.5 regexp.prototype.flags: 1.5.2 - side-channel: 1.0.4 + side-channel: 1.0.6 which-boxed-primitive: 1.0.2 - which-collection: 1.0.1 - which-typed-array: 1.1.14 + which-collection: 1.0.2 + which-typed-array: 1.1.15 deep-is@0.1.4: {} @@ -5379,12 +5337,6 @@ snapshots: dependencies: clone: 1.0.4 - define-data-property@1.1.1: - dependencies: - get-intrinsic: 1.2.2 - gopd: 1.0.1 - has-property-descriptors: 1.0.1 - define-data-property@1.1.4: dependencies: es-define-property: 1.0.0 @@ -5393,8 +5345,8 @@ snapshots: define-properties@1.2.1: dependencies: - define-data-property: 1.1.1 - has-property-descriptors: 1.0.1 + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 object-keys: 1.1.1 delayed-stream@1.0.0: {} @@ -5403,7 +5355,7 @@ snapshots: destroy@1.2.0: {} - detect-libc@2.0.2: {} + detect-libc@2.0.3: {} detect-newline@3.1.0: {} @@ -5423,7 +5375,7 @@ snapshots: dotenv-expand@10.0.0: {} - dotenv@16.3.1: {} + dotenv@16.4.5: {} eastasianwidth@0.2.0: {} @@ -5433,7 +5385,7 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.4.614: {} + electron-to-chromium@1.4.830: {} emittery@0.13.1: {} @@ -5443,7 +5395,7 @@ snapshots: encodeurl@1.0.2: {} - enhanced-resolve@5.15.0: + enhanced-resolve@5.17.0: dependencies: graceful-fs: 4.2.11 tapable: 2.2.1 @@ -5465,19 +5417,19 @@ snapshots: es-get-iterator@1.1.3: dependencies: - call-bind: 1.0.5 - get-intrinsic: 1.2.2 + call-bind: 1.0.7 + get-intrinsic: 1.2.4 has-symbols: 1.0.3 is-arguments: 1.1.1 - is-map: 2.0.2 - is-set: 2.0.2 + is-map: 2.0.3 + is-set: 2.0.3 is-string: 1.0.7 isarray: 2.0.5 stop-iteration-iterator: 1.0.0 - es-module-lexer@1.4.1: {} + es-module-lexer@1.5.4: {} - escalade@3.1.1: {} + escalade@3.1.2: {} escape-html@1.0.3: {} @@ -5487,19 +5439,17 @@ snapshots: escape-string-regexp@4.0.0: {} - escape-string-regexp@5.0.0: {} - - eslint-config-prettier@8.10.0(eslint@8.56.0): + eslint-config-prettier@8.10.0(eslint@8.57.0): dependencies: - eslint: 8.56.0 + eslint: 8.57.0 - eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.10.0(eslint@8.56.0))(eslint@8.56.0)(prettier@2.8.8): + eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8): dependencies: - eslint: 8.56.0 + eslint: 8.57.0 prettier: 2.8.8 prettier-linter-helpers: 1.0.0 optionalDependencies: - eslint-config-prettier: 8.10.0(eslint@8.56.0) + eslint-config-prettier: 8.10.0(eslint@8.57.0) eslint-scope@5.1.1: dependencies: @@ -5513,26 +5463,26 @@ snapshots: eslint-visitor-keys@3.4.3: {} - eslint@8.56.0: + eslint@8.57.0: dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) - '@eslint-community/regexpp': 4.10.0 + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@eslint-community/regexpp': 4.11.0 '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.56.0 - '@humanwhocodes/config-array': 0.11.13 + '@eslint/js': 8.57.0 + '@humanwhocodes/config-array': 0.11.14 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 '@ungap/structured-clone': 1.2.0 ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.4 + debug: 4.3.5 doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 eslint-visitor-keys: 3.4.3 espree: 9.6.1 - esquery: 1.5.0 + esquery: 1.6.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 file-entry-cache: 6.0.1 @@ -5540,7 +5490,7 @@ snapshots: glob-parent: 6.0.2 globals: 13.24.0 graphemer: 1.4.0 - ignore: 5.3.0 + ignore: 5.3.1 imurmurhash: 0.1.4 is-glob: 4.0.3 is-path-inside: 3.0.3 @@ -5550,7 +5500,7 @@ snapshots: lodash.merge: 4.6.2 minimatch: 3.1.2 natural-compare: 1.4.0 - optionator: 0.9.3 + optionator: 0.9.4 strip-ansi: 6.0.1 text-table: 0.2.0 transitivePeerDependencies: @@ -5558,13 +5508,13 @@ snapshots: espree@9.6.1: dependencies: - acorn: 8.11.2 - acorn-jsx: 5.3.2(acorn@8.11.2) + acorn: 8.12.1 + acorn-jsx: 5.3.2(acorn@8.12.1) eslint-visitor-keys: 3.4.3 esprima@4.0.1: {} - esquery@1.5.0: + esquery@1.6.0: dependencies: estraverse: 5.3.0 @@ -5604,14 +5554,14 @@ snapshots: jest-message-util: 29.7.0 jest-util: 29.7.0 - express@4.18.2: + express@4.19.2: dependencies: accepts: 1.3.8 array-flatten: 1.1.1 - body-parser: 1.20.1 + body-parser: 1.20.2 content-disposition: 0.5.4 content-type: 1.0.5 - cookie: 0.5.0 + cookie: 0.6.0 cookie-signature: 1.0.6 debug: 2.6.9 depd: 2.0.0 @@ -5664,7 +5614,7 @@ snapshots: '@nodelib/fs.walk': 1.2.8 glob-parent: 5.1.2 merge2: 1.4.1 - micromatch: 4.0.5 + micromatch: 4.0.7 fast-json-stable-stringify@2.1.0: {} @@ -5672,11 +5622,11 @@ snapshots: fast-safe-stringify@2.1.1: {} - fast-xml-parser@4.3.6: + fast-xml-parser@4.4.0: dependencies: strnum: 1.0.5 - fastq@1.16.0: + fastq@1.17.1: dependencies: reusify: 1.0.4 @@ -5692,22 +5642,17 @@ snapshots: dependencies: escape-string-regexp: 1.0.5 - figures@5.0.0: - dependencies: - escape-string-regexp: 5.0.0 - is-unicode-supported: 1.3.0 - file-entry-cache@6.0.1: dependencies: flat-cache: 3.2.0 - file-type@19.0.0: + file-type@19.2.0: dependencies: - readable-web-to-node-stream: 3.0.2 - strtok3: 7.0.0 - token-types: 5.0.1 + strtok3: 8.0.0 + token-types: 6.0.0 + uint8array-extras: 1.4.0 - fill-range@7.0.1: + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -5735,11 +5680,11 @@ snapshots: flat-cache@3.2.0: dependencies: - flatted: 3.2.9 + flatted: 3.3.1 keyv: 4.5.4 rimraf: 3.0.2 - flatted@3.2.9: {} + flatted@3.3.1: {} follow-redirects@1.15.6: {} @@ -5759,16 +5704,16 @@ snapshots: dependencies: is-callable: 1.2.7 - foreground-child@3.1.1: + foreground-child@3.2.1: dependencies: cross-spawn: 7.0.3 signal-exit: 4.1.0 - fork-ts-checker-webpack-plugin@9.0.2(typescript@5.3.3)(webpack@5.89.0): + fork-ts-checker-webpack-plugin@9.0.2(typescript@5.3.3)(webpack@5.92.1): dependencies: - '@babel/code-frame': 7.23.5 + '@babel/code-frame': 7.24.7 chalk: 4.1.2 - chokidar: 3.5.3 + chokidar: 3.6.0 cosmiconfig: 8.3.6(typescript@5.3.3) deepmerge: 4.3.1 fs-extra: 10.1.0 @@ -5776,10 +5721,10 @@ snapshots: minimatch: 3.1.2 node-abort-controller: 3.1.1 schema-utils: 3.3.0 - semver: 7.5.4 + semver: 7.6.3 tapable: 2.2.1 typescript: 5.3.3 - webpack: 5.89.0 + webpack: 5.92.1 form-data-lite@1.0.3: dependencies: @@ -5803,7 +5748,7 @@ snapshots: jsonfile: 6.1.0 universalify: 2.0.1 - fs-monkey@1.0.5: {} + fs-monkey@1.0.6: {} fs.realpath@1.0.0: {} @@ -5820,20 +5765,13 @@ snapshots: get-caller-file@2.0.5: {} - get-intrinsic@1.2.2: - dependencies: - function-bind: 1.1.2 - has-proto: 1.0.1 - has-symbols: 1.0.3 - hasown: 2.0.0 - get-intrinsic@1.2.4: dependencies: es-errors: 1.3.0 function-bind: 1.1.2 - has-proto: 1.0.1 + has-proto: 1.0.3 has-symbols: 1.0.3 - hasown: 2.0.0 + hasown: 2.0.2 get-package-type@0.1.0: {} @@ -5849,13 +5787,14 @@ snapshots: glob-to-regexp@0.4.1: {} - glob@10.3.10: + glob@10.4.2: dependencies: - foreground-child: 3.1.1 - jackspeak: 2.3.6 - minimatch: 9.0.3 - minipass: 7.0.4 - path-scurry: 1.10.1 + foreground-child: 3.2.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.0 + path-scurry: 1.11.1 glob@7.2.3: dependencies: @@ -5866,13 +5805,6 @@ snapshots: once: 1.4.0 path-is-absolute: 1.0.1 - glob@9.3.5: - dependencies: - fs.realpath: 1.0.0 - minimatch: 8.0.4 - minipass: 4.2.8 - path-scurry: 1.10.1 - globals@11.12.0: {} globals@13.24.0: @@ -5884,13 +5816,13 @@ snapshots: array-union: 2.1.0 dir-glob: 3.0.1 fast-glob: 3.3.2 - ignore: 5.3.0 + ignore: 5.3.1 merge2: 1.4.1 slash: 3.0.0 gopd@1.0.1: dependencies: - get-intrinsic: 1.2.2 + get-intrinsic: 1.2.4 graceful-fs@4.2.11: {} @@ -5904,15 +5836,11 @@ snapshots: has-own-prop@2.0.0: {} - has-property-descriptors@1.0.1: - dependencies: - get-intrinsic: 1.2.2 - has-property-descriptors@1.0.2: dependencies: es-define-property: 1.0.0 - has-proto@1.0.1: {} + has-proto@1.0.3: {} has-symbols@1.0.3: {} @@ -5920,7 +5848,7 @@ snapshots: dependencies: has-symbols: 1.0.3 - hasown@2.0.0: + hasown@2.0.2: dependencies: function-bind: 1.1.2 @@ -5946,7 +5874,7 @@ snapshots: ieee754@1.2.1: {} - ignore@5.3.0: {} + ignore@5.3.1: {} import-fresh@3.3.0: dependencies: @@ -6034,15 +5962,15 @@ snapshots: through: 2.3.8 wrap-ansi: 6.2.0 - inquirer@9.2.11: + inquirer@9.2.15: dependencies: - '@ljharb/through': 2.3.11 + '@ljharb/through': 2.3.13 ansi-escapes: 4.3.2 chalk: 5.3.0 cli-cursor: 3.1.0 cli-width: 4.1.0 external-editor: 3.1.0 - figures: 5.0.0 + figures: 3.2.0 lodash: 4.17.21 mute-stream: 1.0.0 ora: 5.4.1 @@ -6055,22 +5983,20 @@ snapshots: internal-slot@1.0.7: dependencies: es-errors: 1.3.0 - hasown: 2.0.0 - side-channel: 1.0.4 - - interpret@1.4.0: {} + hasown: 2.0.2 + side-channel: 1.0.6 ipaddr.js@1.9.1: {} is-arguments@1.1.1: dependencies: - call-bind: 1.0.5 + call-bind: 1.0.7 has-tostringtag: 1.0.2 is-array-buffer@3.0.4: dependencies: - call-bind: 1.0.5 - get-intrinsic: 1.2.2 + call-bind: 1.0.7 + get-intrinsic: 1.2.4 is-arrayish@0.2.1: {} @@ -6082,18 +6008,18 @@ snapshots: is-binary-path@2.1.0: dependencies: - binary-extensions: 2.2.0 + binary-extensions: 2.3.0 is-boolean-object@1.1.2: dependencies: - call-bind: 1.0.5 + call-bind: 1.0.7 has-tostringtag: 1.0.2 is-callable@1.2.7: {} - is-core-module@2.13.1: + is-core-module@2.15.0: dependencies: - hasown: 2.0.0 + hasown: 2.0.2 is-date-object@1.0.5: dependencies: @@ -6113,7 +6039,7 @@ snapshots: is-interactive@1.0.0: {} - is-map@2.0.2: {} + is-map@2.0.3: {} is-number-object@1.0.7: dependencies: @@ -6125,10 +6051,10 @@ snapshots: is-regex@1.1.4: dependencies: - call-bind: 1.0.5 + call-bind: 1.0.7 has-tostringtag: 1.0.2 - is-set@2.0.2: {} + is-set@2.0.3: {} is-shared-array-buffer@1.0.3: dependencies: @@ -6146,14 +6072,12 @@ snapshots: is-unicode-supported@0.1.0: {} - is-unicode-supported@1.3.0: {} + is-weakmap@2.0.2: {} - is-weakmap@2.0.1: {} - - is-weakset@2.0.2: + is-weakset@2.0.3: dependencies: - call-bind: 1.0.5 - get-intrinsic: 1.2.2 + call-bind: 1.0.7 + get-intrinsic: 1.2.4 isarray@1.0.0: {} @@ -6165,21 +6089,21 @@ snapshots: istanbul-lib-instrument@5.2.1: dependencies: - '@babel/core': 7.23.6 - '@babel/parser': 7.23.6 + '@babel/core': 7.24.9 + '@babel/parser': 7.24.8 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 6.3.1 transitivePeerDependencies: - supports-color - istanbul-lib-instrument@6.0.1: + istanbul-lib-instrument@6.0.3: dependencies: - '@babel/core': 7.23.6 - '@babel/parser': 7.23.6 + '@babel/core': 7.24.9 + '@babel/parser': 7.24.8 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 - semver: 7.5.4 + semver: 7.6.3 transitivePeerDependencies: - supports-color @@ -6191,20 +6115,20 @@ snapshots: istanbul-lib-source-maps@4.0.1: dependencies: - debug: 4.3.4 + debug: 4.3.5 istanbul-lib-coverage: 3.2.2 source-map: 0.6.1 transitivePeerDependencies: - supports-color - istanbul-reports@3.1.6: + istanbul-reports@3.1.7: dependencies: html-escaper: 2.0.2 istanbul-lib-report: 3.0.1 iterare@1.2.1: {} - jackspeak@2.3.6: + jackspeak@3.4.3: dependencies: '@isaacs/cliui': 8.0.2 optionalDependencies: @@ -6225,7 +6149,7 @@ snapshots: '@types/node': 18.15.11 chalk: 4.1.2 co: 4.6.0 - dedent: 1.5.1 + dedent: 1.5.3 is-generator-fn: 2.1.0 jest-each: 29.7.0 jest-matcher-utils: 29.7.0 @@ -6235,7 +6159,7 @@ snapshots: jest-util: 29.7.0 p-limit: 3.1.0 pretty-format: 29.7.0 - pure-rand: 6.0.4 + pure-rand: 6.1.0 slash: 3.0.0 stack-utils: 2.0.6 transitivePeerDependencies: @@ -6263,10 +6187,10 @@ snapshots: jest-config@29.7.0(@types/node@18.15.11)(ts-node@10.9.2(@types/node@18.15.11)(typescript@4.9.5)): dependencies: - '@babel/core': 7.23.6 + '@babel/core': 7.24.9 '@jest/test-sequencer': 29.7.0 '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.23.6) + babel-jest: 29.7.0(@babel/core@7.24.9) chalk: 4.1.2 ci-info: 3.9.0 deepmerge: 4.3.1 @@ -6280,7 +6204,7 @@ snapshots: jest-runner: 29.7.0 jest-util: 29.7.0 jest-validate: 29.7.0 - micromatch: 4.0.5 + micromatch: 4.0.7 parse-json: 5.2.0 pretty-format: 29.7.0 slash: 3.0.0 @@ -6333,7 +6257,7 @@ snapshots: jest-regex-util: 29.6.3 jest-util: 29.7.0 jest-worker: 29.7.0 - micromatch: 4.0.5 + micromatch: 4.0.7 walker: 1.0.8 optionalDependencies: fsevents: 2.3.3 @@ -6352,12 +6276,12 @@ snapshots: jest-message-util@29.7.0: dependencies: - '@babel/code-frame': 7.23.5 + '@babel/code-frame': 7.24.7 '@jest/types': 29.6.3 '@types/stack-utils': 2.0.3 chalk: 4.1.2 graceful-fs: 4.2.11 - micromatch: 4.0.5 + micromatch: 4.0.7 pretty-format: 29.7.0 slash: 3.0.0 stack-utils: 2.0.6 @@ -6430,7 +6354,7 @@ snapshots: '@jest/types': 29.6.3 '@types/node': 18.15.11 chalk: 4.1.2 - cjs-module-lexer: 1.2.3 + cjs-module-lexer: 1.3.1 collect-v8-coverage: 1.0.2 glob: 7.2.3 graceful-fs: 4.2.11 @@ -6448,15 +6372,15 @@ snapshots: jest-snapshot@29.7.0: dependencies: - '@babel/core': 7.23.6 - '@babel/generator': 7.23.6 - '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.6) - '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.23.6) - '@babel/types': 7.23.6 + '@babel/core': 7.24.9 + '@babel/generator': 7.24.10 + '@babel/plugin-syntax-jsx': 7.24.7(@babel/core@7.24.9) + '@babel/plugin-syntax-typescript': 7.24.7(@babel/core@7.24.9) + '@babel/types': 7.24.9 '@jest/expect-utils': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.6) + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.9) chalk: 4.1.2 expect: 29.7.0 graceful-fs: 4.2.11 @@ -6467,7 +6391,7 @@ snapshots: jest-util: 29.7.0 natural-compare: 1.4.0 pretty-format: 29.7.0 - semver: 7.5.4 + semver: 7.6.3 transitivePeerDependencies: - supports-color @@ -6554,7 +6478,9 @@ snapshots: json5@2.2.3: {} - jsonc-parser@3.2.0: {} + jsonc-parser@3.2.1: {} + + jsonc-parser@3.3.1: {} jsonfile@6.1.0: dependencies: @@ -6573,7 +6499,7 @@ snapshots: lodash.isstring: 4.0.1 lodash.once: 4.1.1 ms: 2.1.3 - semver: 7.5.4 + semver: 7.6.3 jwa@1.4.1: dependencies: @@ -6601,7 +6527,7 @@ snapshots: ldap-server-mock@6.0.1: dependencies: ldapjs: 2.3.3 - tslib: 2.6.2 + tslib: 2.6.3 ldapjs@2.3.3: dependencies: @@ -6614,14 +6540,13 @@ snapshots: vasync: 2.2.1 verror: 1.10.1 - ldapts@7.0.12: + ldapts@7.1.0: dependencies: '@types/asn1': 0.2.4 - '@types/uuid': 9.0.8 asn1: 0.2.6 - debug: 4.3.4 + debug: 4.3.5 strict-event-emitter-types: 2.0.0 - uuid: 9.0.1 + uuid: 10.0.0 transitivePeerDependencies: - supports-color @@ -6632,7 +6557,7 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - libphonenumber-js@1.10.54: {} + libphonenumber-js@1.11.4: {} lightcookie@1.0.25: {} @@ -6653,6 +6578,10 @@ snapshots: dependencies: p-locate: 5.0.0 + lodash.escaperegexp@4.1.2: {} + + lodash.groupby@4.6.0: {} + lodash.includes@4.3.0: {} lodash.invoke@4.5.2: {} @@ -6663,12 +6592,16 @@ snapshots: lodash.isinteger@4.0.4: {} + lodash.isnil@4.0.0: {} + lodash.isnumber@3.0.3: {} lodash.isplainobject@4.0.6: {} lodash.isstring@4.0.1: {} + lodash.isundefined@3.0.1: {} + lodash.memoize@4.1.2: {} lodash.merge@4.6.2: {} @@ -6677,6 +6610,8 @@ snapshots: lodash.partialright@4.2.1: {} + lodash.uniq@4.5.0: {} + lodash@4.17.21: {} log-symbols@4.1.0: @@ -6684,23 +6619,19 @@ snapshots: chalk: 4.1.2 is-unicode-supported: 0.1.0 - lru-cache@10.1.0: {} + lru-cache@10.4.3: {} lru-cache@5.1.1: dependencies: yallist: 3.1.1 - lru-cache@6.0.0: - dependencies: - yallist: 4.0.0 - - magic-string@0.30.5: + magic-string@0.30.8: dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.0 make-dir@4.0.0: dependencies: - semver: 7.5.4 + semver: 7.6.3 make-error@1.3.6: {} @@ -6716,7 +6647,7 @@ snapshots: memfs@3.5.3: dependencies: - fs-monkey: 1.0.5 + fs-monkey: 1.0.6 merge-descriptors@1.0.1: {} @@ -6726,9 +6657,9 @@ snapshots: methods@1.1.2: {} - micromatch@4.0.5: + micromatch@4.0.7: dependencies: - braces: 3.0.2 + braces: 3.0.3 picomatch: 2.3.1 mime-db@1.52.0: {} @@ -6749,19 +6680,13 @@ snapshots: dependencies: brace-expansion: 1.1.11 - minimatch@8.0.4: - dependencies: - brace-expansion: 2.0.1 - - minimatch@9.0.3: + minimatch@9.0.5: dependencies: brace-expansion: 2.0.1 minimist@1.2.8: {} - minipass@4.2.8: {} - - minipass@7.0.4: {} + minipass@7.1.2: {} mkdirp@0.5.6: dependencies: @@ -6816,7 +6741,7 @@ snapshots: nock@13.5.4: dependencies: - debug: 4.3.4 + debug: 4.3.5 json-stringify-safe: 5.0.1 propagate: 2.0.1 transitivePeerDependencies: @@ -6834,7 +6759,7 @@ snapshots: node-int64@0.4.0: {} - node-releases@2.0.14: {} + node-releases@2.0.17: {} normalize-package-data@2.5.0: dependencies: @@ -6851,18 +6776,18 @@ snapshots: object-assign@4.1.1: {} - object-inspect@1.13.1: {} + object-inspect@1.13.2: {} - object-is@1.1.5: + object-is@1.1.6: dependencies: - call-bind: 1.0.5 + call-bind: 1.0.7 define-properties: 1.2.1 object-keys@1.1.1: {} object.assign@4.1.5: dependencies: - call-bind: 1.0.5 + call-bind: 1.0.7 define-properties: 1.2.1 has-symbols: 1.0.3 object-keys: 1.1.1 @@ -6887,14 +6812,14 @@ snapshots: dependencies: klona: 2.0.6 - optionator@0.9.3: + optionator@0.9.4: dependencies: - '@aashutoshrathi/word-wrap': 1.2.6 deep-is: 0.1.4 fast-levenshtein: 2.0.6 levn: 0.4.1 prelude-ls: 1.2.1 type-check: 0.4.0 + word-wrap: 1.2.5 ora@5.4.1: dependencies: @@ -6928,9 +6853,11 @@ snapshots: p-try@2.2.0: {} - pactum-matchers@1.1.6: {} + package-json-from-dist@1.0.0: {} - pactum@3.6.0: + pactum-matchers@1.1.7: {} + + pactum@3.7.0: dependencies: '@exodus/schemasafe': 1.3.0 deep-override: 1.0.2 @@ -6939,10 +6866,12 @@ snapshots: klona: 2.0.6 lightcookie: 1.0.25 openapi-fuzzer-core: 1.0.6 - pactum-matchers: 1.1.6 + pactum-matchers: 1.1.7 parse-graphql: 1.0.0 - phin: 3.7.0 + phin: 3.7.1 polka: 0.5.2 + transitivePeerDependencies: + - debug pad@2.3.0: dependencies: @@ -6958,7 +6887,7 @@ snapshots: parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.23.5 + '@babel/code-frame': 7.24.7 error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -6972,7 +6901,7 @@ snapshots: passport-strategy@1.0.0: {} - passport@0.6.0: + passport@0.7.0: dependencies: passport-strategy: 1.0.0 pause: 0.0.1 @@ -6986,10 +6915,10 @@ snapshots: path-parse@1.0.7: {} - path-scurry@1.10.1: + path-scurry@1.11.1: dependencies: - lru-cache: 10.1.0 - minipass: 7.0.4 + lru-cache: 10.4.3 + minipass: 7.1.2 path-to-regexp@0.1.7: {} @@ -7006,17 +6935,19 @@ snapshots: linebreak: 1.1.0 png-js: 1.0.0 - peek-readable@5.0.0: {} + peek-readable@5.1.3: {} - phin@3.7.0: + phin@3.7.1: dependencies: - centra: 2.6.0 + centra: 2.7.0 + transitivePeerDependencies: + - debug - picocolors@1.0.0: {} + picocolors@1.0.1: {} picomatch@2.3.1: {} - picomatch@3.0.1: {} + picomatch@4.0.1: {} pirates@4.0.6: {} @@ -7051,11 +6982,11 @@ snapshots: dependencies: '@jest/schemas': 29.6.3 ansi-styles: 5.2.0 - react-is: 18.2.0 + react-is: 18.3.1 - prisma@5.8.1: + prisma@5.17.0: dependencies: - '@prisma/engines': 5.8.1 + '@prisma/engines': 5.17.0 process-nextick-args@2.0.1: {} @@ -7075,11 +7006,11 @@ snapshots: punycode@2.3.1: {} - pure-rand@6.0.4: {} + pure-rand@6.1.0: {} qs@6.11.0: dependencies: - side-channel: 1.0.4 + side-channel: 1.0.6 queue-microtask@1.2.3: {} @@ -7089,13 +7020,6 @@ snapshots: range-parser@1.2.1: {} - raw-body@2.5.1: - dependencies: - bytes: 3.1.2 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - unpipe: 1.0.0 - raw-body@2.5.2: dependencies: bytes: 3.1.2 @@ -7103,7 +7027,7 @@ snapshots: iconv-lite: 0.4.24 unpipe: 1.0.0 - react-is@18.2.0: {} + react-is@18.3.1: {} read-pkg-up@7.0.1: dependencies: @@ -7144,18 +7068,10 @@ snapshots: string_decoder: 1.3.0 util-deprecate: 1.0.2 - readable-web-to-node-stream@3.0.2: - dependencies: - readable-stream: 3.6.2 - readdirp@3.6.0: dependencies: picomatch: 2.3.1 - rechoir@0.6.2: - dependencies: - resolve: 1.22.8 - reflect-metadata@0.1.14: {} regexp.prototype.flags@1.5.2: @@ -7183,7 +7099,7 @@ snapshots: resolve@1.22.8: dependencies: - is-core-module: 2.13.1 + is-core-module: 2.15.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 @@ -7205,10 +7121,6 @@ snapshots: dependencies: glob: 7.2.3 - rimraf@4.4.1: - dependencies: - glob: 9.3.5 - run-async@2.4.1: {} run-async@3.0.0: {} @@ -7229,7 +7141,7 @@ snapshots: rxjs@7.8.1: dependencies: - tslib: 2.6.2 + tslib: 2.6.3 safe-buffer@5.1.2: {} @@ -7247,9 +7159,7 @@ snapshots: semver@6.3.1: {} - semver@7.5.4: - dependencies: - lru-cache: 6.0.0 + semver@7.6.3: {} send@0.18.0: dependencies: @@ -7269,7 +7179,7 @@ snapshots: transitivePeerDependencies: - supports-color - serialize-javascript@6.0.1: + serialize-javascript@6.0.2: dependencies: randombytes: 2.1.0 @@ -7282,21 +7192,14 @@ snapshots: transitivePeerDependencies: - supports-color - set-function-length@1.1.1: - dependencies: - define-data-property: 1.1.1 - get-intrinsic: 1.2.2 - gopd: 1.0.1 - has-property-descriptors: 1.0.1 - - set-function-length@1.2.1: + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 es-errors: 1.3.0 function-bind: 1.1.2 get-intrinsic: 1.2.4 gopd: 1.0.1 - has-property-descriptors: 1.0.1 + has-property-descriptors: 1.0.2 set-function-name@2.0.2: dependencies: @@ -7307,31 +7210,31 @@ snapshots: setprototypeof@1.2.0: {} - sharp@0.33.2: + sharp@0.33.4: dependencies: color: 4.2.3 - detect-libc: 2.0.2 - semver: 7.5.4 + detect-libc: 2.0.3 + semver: 7.6.3 optionalDependencies: - '@img/sharp-darwin-arm64': 0.33.2 - '@img/sharp-darwin-x64': 0.33.2 - '@img/sharp-libvips-darwin-arm64': 1.0.1 - '@img/sharp-libvips-darwin-x64': 1.0.1 - '@img/sharp-libvips-linux-arm': 1.0.1 - '@img/sharp-libvips-linux-arm64': 1.0.1 - '@img/sharp-libvips-linux-s390x': 1.0.1 - '@img/sharp-libvips-linux-x64': 1.0.1 - '@img/sharp-libvips-linuxmusl-arm64': 1.0.1 - '@img/sharp-libvips-linuxmusl-x64': 1.0.1 - '@img/sharp-linux-arm': 0.33.2 - '@img/sharp-linux-arm64': 0.33.2 - '@img/sharp-linux-s390x': 0.33.2 - '@img/sharp-linux-x64': 0.33.2 - '@img/sharp-linuxmusl-arm64': 0.33.2 - '@img/sharp-linuxmusl-x64': 0.33.2 - '@img/sharp-wasm32': 0.33.2 - '@img/sharp-win32-ia32': 0.33.2 - '@img/sharp-win32-x64': 0.33.2 + '@img/sharp-darwin-arm64': 0.33.4 + '@img/sharp-darwin-x64': 0.33.4 + '@img/sharp-libvips-darwin-arm64': 1.0.2 + '@img/sharp-libvips-darwin-x64': 1.0.2 + '@img/sharp-libvips-linux-arm': 1.0.2 + '@img/sharp-libvips-linux-arm64': 1.0.2 + '@img/sharp-libvips-linux-s390x': 1.0.2 + '@img/sharp-libvips-linux-x64': 1.0.2 + '@img/sharp-libvips-linuxmusl-arm64': 1.0.2 + '@img/sharp-libvips-linuxmusl-x64': 1.0.2 + '@img/sharp-linux-arm': 0.33.4 + '@img/sharp-linux-arm64': 0.33.4 + '@img/sharp-linux-s390x': 0.33.4 + '@img/sharp-linux-x64': 0.33.4 + '@img/sharp-linuxmusl-arm64': 0.33.4 + '@img/sharp-linuxmusl-x64': 0.33.4 + '@img/sharp-wasm32': 0.33.4 + '@img/sharp-win32-ia32': 0.33.4 + '@img/sharp-win32-x64': 0.33.4 shebang-command@2.0.0: dependencies: @@ -7339,17 +7242,12 @@ snapshots: shebang-regex@3.0.0: {} - shelljs@0.8.5: + side-channel@1.0.6: dependencies: - glob: 7.2.3 - interpret: 1.4.0 - rechoir: 0.6.2 - - side-channel@1.0.4: - dependencies: - call-bind: 1.0.5 - get-intrinsic: 1.2.2 - object-inspect: 1.13.1 + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + object-inspect: 1.13.2 signal-exit@3.0.7: {} @@ -7384,16 +7282,16 @@ snapshots: spdx-correct@3.2.0: dependencies: spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.16 + spdx-license-ids: 3.0.18 - spdx-exceptions@2.3.0: {} + spdx-exceptions@2.5.0: {} spdx-expression-parse@3.0.1: dependencies: - spdx-exceptions: 2.3.0 - spdx-license-ids: 3.0.16 + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.18 - spdx-license-ids@3.0.16: {} + spdx-license-ids@3.0.18: {} sprintf-js@1.0.3: {} @@ -7465,10 +7363,10 @@ snapshots: strnum@1.0.5: {} - strtok3@7.0.0: + strtok3@8.0.0: dependencies: '@tokenizer/token': 0.3.0 - peek-readable: 5.0.0 + peek-readable: 5.1.3 supports-color@5.5.0: dependencies: @@ -7484,7 +7382,7 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - swagger-ui-dist@5.11.0: {} + swagger-ui-dist@5.17.14: {} symbol-observable@1.0.1: {} @@ -7492,19 +7390,19 @@ snapshots: tapable@2.2.1: {} - terser-webpack-plugin@5.3.9(webpack@5.89.0): + terser-webpack-plugin@5.3.10(webpack@5.92.1): dependencies: - '@jridgewell/trace-mapping': 0.3.20 + '@jridgewell/trace-mapping': 0.3.25 jest-worker: 27.5.1 schema-utils: 3.3.0 - serialize-javascript: 6.0.1 - terser: 5.26.0 - webpack: 5.89.0 + serialize-javascript: 6.0.2 + terser: 5.31.3 + webpack: 5.92.1 - terser@5.26.0: + terser@5.31.3: dependencies: - '@jridgewell/source-map': 0.3.5 - acorn: 8.11.2 + '@jridgewell/source-map': 0.3.6 + acorn: 8.12.1 commander: 2.20.3 source-map-support: 0.5.21 @@ -7534,7 +7432,7 @@ snapshots: toidentifier@1.0.1: {} - token-types@5.0.1: + token-types@6.0.0: dependencies: '@tokenizer/token': 0.3.0 ieee754: 1.2.1 @@ -7547,7 +7445,7 @@ snapshots: dependencies: matchit: 1.1.0 - ts-jest@29.1.0(@babel/core@7.23.6)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.6))(jest@29.5.0(@types/node@18.15.11)(ts-node@10.9.2(@types/node@18.15.11)(typescript@4.9.5)))(typescript@4.9.5): + ts-jest@29.1.0(@babel/core@7.24.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.9))(jest@29.5.0(@types/node@18.15.11)(ts-node@10.9.2(@types/node@18.15.11)(typescript@4.9.5)))(typescript@4.9.5): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 @@ -7556,34 +7454,34 @@ snapshots: json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 - semver: 7.5.4 + semver: 7.6.3 typescript: 4.9.5 yargs-parser: 21.1.1 optionalDependencies: - '@babel/core': 7.23.6 + '@babel/core': 7.24.9 '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.23.6) + babel-jest: 29.7.0(@babel/core@7.24.9) - ts-loader@9.5.1(typescript@4.9.5)(webpack@5.89.0): + ts-loader@9.5.1(typescript@4.9.5)(webpack@5.92.1): dependencies: chalk: 4.1.2 - enhanced-resolve: 5.15.0 - micromatch: 4.0.5 - semver: 7.5.4 + enhanced-resolve: 5.17.0 + micromatch: 4.0.7 + semver: 7.6.3 source-map: 0.7.4 typescript: 4.9.5 - webpack: 5.89.0 + webpack: 5.92.1 ts-node@10.9.2(@types/node@18.15.11)(typescript@4.9.5): dependencies: '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.9 + '@tsconfig/node10': 1.0.11 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 '@types/node': 18.15.11 - acorn: 8.11.2 - acorn-walk: 8.3.1 + acorn: 8.12.1 + acorn-walk: 8.3.3 arg: 4.1.3 create-require: 1.1.1 diff: 4.0.2 @@ -7595,7 +7493,7 @@ snapshots: tsconfig-paths-webpack-plugin@4.1.0: dependencies: chalk: 4.1.2 - enhanced-resolve: 5.15.0 + enhanced-resolve: 5.17.0 tsconfig-paths: 4.2.0 tsconfig-paths@4.2.0: @@ -7606,7 +7504,7 @@ snapshots: tslib@1.14.1: {} - tslib@2.6.2: {} + tslib@2.6.3: {} tsutils@3.21.0(typescript@4.9.5): dependencies: @@ -7642,6 +7540,8 @@ snapshots: dependencies: '@lukeed/csprng': 1.1.0 + uint8array-extras@1.4.0: {} + unicode-properties@1.4.1: dependencies: base64-js: 1.5.1 @@ -7656,11 +7556,11 @@ snapshots: unpipe@1.0.0: {} - update-browserslist-db@1.0.13(browserslist@4.22.2): + update-browserslist-db@1.1.0(browserslist@4.23.2): dependencies: - browserslist: 4.22.2 - escalade: 3.1.1 - picocolors: 1.0.0 + browserslist: 4.23.2 + escalade: 3.1.2 + picocolors: 1.0.1 uri-js@4.4.1: dependencies: @@ -7670,15 +7570,13 @@ snapshots: utils-merge@1.0.1: {} - uuid@9.0.0: {} - - uuid@9.0.1: {} + uuid@10.0.0: {} v8-compile-cache-lib@3.0.1: {} - v8-to-istanbul@9.2.0: + v8-to-istanbul@9.3.0: dependencies: - '@jridgewell/trace-mapping': 0.3.20 + '@jridgewell/trace-mapping': 0.3.25 '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 2.0.0 @@ -7687,7 +7585,7 @@ snapshots: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - validator@13.11.0: {} + validator@13.12.0: {} vary@1.1.2: {} @@ -7711,7 +7609,7 @@ snapshots: dependencies: makeerror: 1.0.12 - watchpack@2.4.0: + watchpack@2.4.1: dependencies: glob-to-regexp: 0.4.1 graceful-fs: 4.2.11 @@ -7726,19 +7624,19 @@ snapshots: webpack-sources@3.2.3: {} - webpack@5.89.0: + webpack@5.92.1: dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.5 - '@webassemblyjs/ast': 1.11.6 - '@webassemblyjs/wasm-edit': 1.11.6 - '@webassemblyjs/wasm-parser': 1.11.6 - acorn: 8.11.2 - acorn-import-assertions: 1.9.0(acorn@8.11.2) - browserslist: 4.22.2 - chrome-trace-event: 1.0.3 - enhanced-resolve: 5.15.0 - es-module-lexer: 1.4.1 + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/wasm-edit': 1.12.1 + '@webassemblyjs/wasm-parser': 1.12.1 + acorn: 8.12.1 + acorn-import-attributes: 1.9.5(acorn@8.12.1) + browserslist: 4.23.2 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.17.0 + es-module-lexer: 1.5.4 eslint-scope: 5.1.1 events: 3.3.0 glob-to-regexp: 0.4.1 @@ -7749,8 +7647,8 @@ snapshots: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.2.1 - terser-webpack-plugin: 5.3.9(webpack@5.89.0) - watchpack: 2.4.0 + terser-webpack-plugin: 5.3.10(webpack@5.92.1) + watchpack: 2.4.1 webpack-sources: 3.2.3 transitivePeerDependencies: - '@swc/core' @@ -7770,17 +7668,17 @@ snapshots: is-string: 1.0.7 is-symbol: 1.0.4 - which-collection@1.0.1: + which-collection@1.0.2: dependencies: - is-map: 2.0.2 - is-set: 2.0.2 - is-weakmap: 2.0.1 - is-weakset: 2.0.2 + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.3 - which-typed-array@1.1.14: + which-typed-array@1.1.15: dependencies: available-typed-arrays: 1.0.7 - call-bind: 1.0.5 + call-bind: 1.0.7 for-each: 0.3.3 gopd: 1.0.1 has-tostringtag: 1.0.2 @@ -7789,6 +7687,8 @@ snapshots: dependencies: isexe: 2.0.0 + word-wrap@1.2.5: {} + wrap-ansi@6.2.0: dependencies: ansi-styles: 4.3.0 @@ -7820,14 +7720,12 @@ snapshots: yallist@3.1.1: {} - yallist@4.0.0: {} - yargs-parser@21.1.1: {} yargs@17.7.2: dependencies: cliui: 8.0.1 - escalade: 3.1.1 + escalade: 3.1.2 get-caller-file: 2.0.5 require-directory: 2.1.1 string-width: 4.2.3 diff --git a/prisma/schema.prisma b/prisma/schema.prisma index ddc9f4b..48def3a 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -483,7 +483,7 @@ model UeWorkTime { td Int? tp Int? the Int? - project Int? + project Boolean? internship Int? ueId String @unique diff --git a/scripts/seed/base.ts b/scripts/seed/base.ts new file mode 100644 index 0000000..a20f947 --- /dev/null +++ b/scripts/seed/base.ts @@ -0,0 +1,46 @@ +import { PrismaClient } from '@prisma/client'; + +const prisma = new PrismaClient(); + +async function main() { + // SEMESTERS // + await prisma.semester.deleteMany({}); + await prisma.semester.create({ + data: { + code: 'P24', + start: new Date('2024-02-01'), + end: new Date('2024-09-01'), + }, + }); + await prisma.semester.create({ + data: { + code: 'A24', + start: new Date('2024-08-31'), + end: new Date('2025-02-01'), + }, + }); + + // UE CATEGORIES // + console.log('Updating UE Categories'); + const categories = [ + { code: 'CS', name: 'Connaissances scientifiques' }, + { code: 'TM', name: 'Techniques et méthodes' }, + { code: 'ST', name: 'Stage' }, + { code: 'HT', name: 'Humanités et technologies' }, + { code: 'ME', name: 'Mise en situation' }, + { code: 'EC', name: 'Expression et communication' }, + ]; + await Promise.all( + categories.map((category) => + prisma.ueCreditCategory.upsert({ + where: { + code: category.code, + }, + update: category, + create: category, + }), + ), + ); +} + +main(); diff --git a/scripts/seed/ue.ts b/scripts/seed/ue.ts new file mode 100644 index 0000000..5e05d2f --- /dev/null +++ b/scripts/seed/ue.ts @@ -0,0 +1,126 @@ +import { createReadStream } from 'fs'; +import { parse } from '@fast-csv/parse'; +import { PrismaClient } from '@prisma/client'; + +const prisma = new PrismaClient(); + +type UEOF = { + entry_nb: string; + shortname: string; + fullname: string; + idnumber: string; + category_idnumber: string; + startdate: string; + enddate: string; + objectives: string; + program: string; + cm_hours: string; + td_hours: string; + tp_hours: string; + the_hours: string; + internship_hours: string; + summary: string; + lang: string; +}; +type DataHeader = keyof UEOF; + +async function parseDocument(document: string) { + return new Promise((resolve, reject) => { + const ues: UEOF[] = []; + createReadStream(document) + .pipe( + parse({ + delimiter: ';', + skipLines: 5, + headers: [ + 'entry_nb', + 'shortname', + 'fullname', + 'idnumber', + 'category_idnumber', + 'startdate', + 'enddate', + 'objectives', + 'program', + 'cm_hours', + 'td_hours', + 'tp_hours', + 'the_hours', + 'internship_hours', + 'summary', + 'lang', + ] satisfies DataHeader[], + renameHeaders: true, + }), + ) + .on('error', (error) => { + console.error('\x1b[41;30mAn error occurred while parsing UE datasource\x1b[0m'); + console.error(error); + reject(); + }) + .on('data', ues.push) + .on('end', () => resolve(ues)); + }); +} + +async function main() { + const ues = await parseDocument('scripts/seed/ues.csv'); + await prisma.ue.deleteMany({}); + const promises = ues.map(async (ue) => { + await prisma.ue.create({ + data: { + code: ue.shortname, + inscriptionCode: ue.idnumber, + name: { + create: { + fr: ue.fullname, + }, + }, + info: { + create: { + comment: { + create: { + fr: ue.summary, + }, + }, + objectives: { + create: { + fr: ue.objectives, + }, + }, + program: { + create: { + fr: ue.program, + }, + }, + languages: ue.lang, + }, + }, + branchOption: {}, + subsequentUes: {}, + workTime: { + create: { + cm: Number(ue.cm_hours), + td: Number(ue.td_hours), + tp: Number(ue.tp_hours), + the: Number(ue.the_hours), + internship: Number(ue.internship_hours), + }, + }, + credits: { + create: { + credits: 0, + category: { + connect: { + code: 'CS', + }, + }, + }, + }, + }, + }); + }); + await Promise.all(promises); +} + +main(); From 78aaf9fcbc0eb00018999daced7dcd7008faf001 Mon Sep 17 00:00:00 2001 From: AlbanSdl Date: Sat, 20 Jul 2024 00:17:23 +0200 Subject: [PATCH 02/14] fix: update database structure to reflect siep changes --- prisma/schema.prisma | 5 +++-- scripts/seed/ue.ts | 13 +++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 48def3a..36418b0 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -213,8 +213,9 @@ model Translation { model Ue { id String @id @default(uuid()) - code String @unique @db.VarChar(10) - inscriptionCode String @unique @db.Char(4) + siepId Int @unique + code String @unique @db.VarChar(15) + legacyCode String @unique @db.VarChar(6) nameTranslationId String @unique validationRate Float? ueInfoId String @unique diff --git a/scripts/seed/ue.ts b/scripts/seed/ue.ts index 5e05d2f..1418c4a 100644 --- a/scripts/seed/ue.ts +++ b/scripts/seed/ue.ts @@ -58,7 +58,7 @@ async function parseDocument(document: string) { console.error(error); reject(); }) - .on('data', ues.push) + .on('data', (ue) => ues.push(ue)) .on('end', () => resolve(ues)); }); } @@ -66,11 +66,12 @@ async function parseDocument(document: string) { async function main() { const ues = await parseDocument('scripts/seed/ues.csv'); await prisma.ue.deleteMany({}); - const promises = ues.map(async (ue) => { - await prisma.ue.create({ + const promises = ues.map((ue) => + prisma.ue.create({ data: { code: ue.shortname, - inscriptionCode: ue.idnumber, + legacyCode: ue.idnumber, + siepId: Number(ue.entry_nb), name: { create: { fr: ue.fullname, @@ -118,8 +119,8 @@ async function main() { }, }, }, - }); - }); + }), + ); await Promise.all(promises); } From 3551b03425906993dac94af1b13dc771d281320b Mon Sep 17 00:00:00 2001 From: AlbanSdl Date: Sat, 27 Jul 2024 19:25:13 +0200 Subject: [PATCH 03/14] fix(ue): database changes and data import --- package.json | 4 +- prisma/schema.prisma | 166 +++++++++++++---------- scripts/seed/aliases.ts | 26 ++++ scripts/seed/base.ts | 146 +++++++++++++++++++- scripts/seed/ue.ts | 293 +++++++++++++++++++++++++++++++++------- 5 files changed, 503 insertions(+), 132 deletions(-) create mode 100644 scripts/seed/aliases.ts diff --git a/package.json b/package.json index 747942a..61753a6 100644 --- a/package.json +++ b/package.json @@ -28,8 +28,10 @@ "test:db:editor": "env-cmd -f .env.test -- pnpm prisma studio", "seed:base": "env-cmd -f .env.dev -- ts-node scripts/seed/base.ts", "seed:ue": "env-cmd -f .env.dev -- ts-node scripts/seed/ue.ts", + "seed:ue:aliases": "env-cmd -f .env.dev -- ts-node scripts/seed/aliases.ts", "seed:base:prod": "node scripts/seed/base.js", - "seed:ue:prod": "node scripts/seed/ue.js" + "seed:ue:prod": "node scripts/seed/ue.js", + "seed:ue:aliases:prod": "node scripts/seed/aliases.js" }, "dependencies": { "@fast-csv/parse": "^5.0.0", diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 36418b0..a2d6515 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -21,12 +21,12 @@ model Asso { descriptionShortTranslationId String @unique descriptionTranslationId String @unique - descriptionShortTranslation Translation @relation(name: "descriptionShortTranslation", fields: [descriptionShortTranslationId], references: [id], onDelete: Cascade) - descriptionTranslation Translation @relation(name: "descriptionTranslation", fields: [descriptionTranslationId], references: [id], onDelete: Cascade) + descriptionShortTranslation Translation @relation(name: "descriptionShortTranslation", fields: [descriptionShortTranslationId], references: [id], onDelete: Cascade) + descriptionTranslation Translation @relation(name: "descriptionTranslation", fields: [descriptionTranslationId], references: [id], onDelete: Cascade) assoMemberships AssoMembership[] assoMessages AssoMessage[] events Event[] - assoMembershipRoles AssoMembershipRole[] + assoMembershipRoles AssoMembershipRole[] } model AssoMembership { @@ -55,10 +55,10 @@ model AssoMembershipRole { name String position Int isPresident Boolean - assoId String + assoId String assoMembership AssoMembership[] - asso Asso @relation(fields: [assoId], references: [id]) + asso Asso @relation(fields: [assoId], references: [id]) } model AssoMessage { @@ -127,7 +127,7 @@ model Semester { start DateTime @db.Date end DateTime @db.Date - openedUes Ue[] + openedUes Ueof[] annals UeAnnal[] comments UeComment[] courses UeCourse[] @@ -205,35 +205,53 @@ model Translation { branchOptionDescriptions UTTBranchOption? formationDescriptions UTTFormation? formationFollowingMethodDescriptions UTTFormationFollowingMethod? - ueNames Ue? - ueInfoComments UeInfo? @relation("ueInfoCommentsTranslation") + ueNames Ueof? ueInfo UeInfo? @relation("ueInfoObjectivesTranslation") ueInfoPrograms UeInfo? @relation("ueInfoProgramTranslation") } model Ue { - id String @id @default(uuid()) + code String @id @db.VarChar(8) + createdAt DateTime @default(now()) + creationYear Int + updateYear Int + comments UeComment[] + annals UeAnnal[] + starVotes UeStarVote[] + ueofs Ueof[] + subsequentUes Ueof[] @relation("ueRequirements") + aliases UeAlias[] +} + +model UeAlias { + code String @id @db.VarChar(8) + standsFor String? @db.VarChar(8) + + alias Ue? @relation(fields: [standsFor], references: [code]) +} + +model Ueof { + code String @id @db.VarChar(20) siepId Int @unique - code String @unique @db.VarChar(15) - legacyCode String @unique @db.VarChar(6) + ueId String nameTranslationId String @unique - validationRate Float? ueInfoId String @unique createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + available Boolean @default(false) - name Translation @relation(fields: [nameTranslationId], references: [id], onDelete: Cascade) - usersSubscriptions UserUeSubscription[] + ue Ue @relation(fields: [ueId], references: [code]) credits UeCredit[] - starVotes UeStarVote[] + branchOption UTTBranchOption[] + name Translation @relation(fields: [nameTranslationId], references: [id], onDelete: Cascade) + info UeInfo @relation(fields: [ueInfoId], references: [id], onDelete: Cascade) + requirements Ue[] @relation(name: "ueRequirements") openSemester Semester[] workTime UeWorkTime? - info UeInfo @relation(fields: [ueInfoId], references: [id], onDelete: Cascade) - subsequentUes UeInfo[] @relation("ueRequirements") - annals UeAnnal[] - comments UeComment[] courses UeCourse[] - branchOption UTTBranchOption[] + usersSubscriptions UserUeSubscription[] + comments UeComment[] + annals UeAnnal[] } model UeAnnal { @@ -245,11 +263,13 @@ model UeAnnal { deletedAt DateTime? validatedAt DateTime? ueId String + ueofId String senderId String? semesterId String typeId String - ue Ue @relation(fields: [ueId], references: [id], onDelete: Cascade) + ue Ue @relation(fields: [ueId], references: [code], onDelete: Cascade) + ueof Ueof @relation(fields: [ueofId], references: [code], onDelete: Cascade) sender User? @relation(fields: [senderId], references: [id], onDelete: SetNull) type UeAnnalType @relation(fields: [typeId], references: [id]) semester Semester @relation(fields: [semesterId], references: [code]) @@ -298,10 +318,12 @@ model UeComment { validatedAt DateTime? lastValidatedBody String? ueId String + ueofId String authorId String? semesterId String - ue Ue @relation(fields: [ueId], references: [id], onDelete: Cascade) + ue Ue @relation(fields: [ueId], references: [code], onDelete: Cascade) + ueof Ueof @relation(fields: [ueofId], references: [code], onDelete: Cascade) author User? @relation(fields: [authorId], references: [id], onDelete: SetNull) semester Semester @relation(fields: [semesterId], references: [code]) answers UeCommentReply[] @@ -380,11 +402,11 @@ model UeCourse { type CourseType room String @db.VarChar(50) createdAt DateTime @default(now()) - ueId String + ueofId String semesterId String students User[] - ue Ue @relation(fields: [ueId], references: [id]) + ueof Ueof @relation(fields: [ueofId], references: [code]) semester Semester @relation(fields: [semesterId], references: [code]) courseExchangesFrom UeCourseExchange[] @relation(name: "courseFrom") courseExchangesTo UeCourseExchange[] @relation(name: "courseTo") @@ -422,36 +444,32 @@ model UeCourseExchangeReply { } model UeCredit { - id String @id @default(uuid()) - credits Int @db.SmallInt - ueId String - categoryId String + id String @id @default(uuid()) + credits Int @db.SmallInt + ueofId String + categoryId String? - ue Ue @relation(fields: [ueId], references: [id], onDelete: Cascade) - category UeCreditCategory @relation(fields: [categoryId], references: [code]) + ueof Ueof @relation(fields: [ueofId], references: [code], onDelete: Cascade) + category UeCreditCategory? @relation(fields: [categoryId], references: [code]) } model UeCreditCategory { - code String @id @db.VarChar(10) + code String @id @db.Char(2) name String @db.VarChar(255) - credits UeCredit[] + ueCredits UeCredit[] } model UeInfo { id String @id @default(uuid()) - degree String? @db.Text minors String? @db.Text - languages String? @db.Text - commentTranslationId String? @unique + language String? @db.Text objectivesTranslationId String? @unique programTranslationId String? @unique - ue Ue? - comment Translation? @relation("ueInfoCommentsTranslation", fields: [commentTranslationId], references: [id], onDelete: Cascade) - objectives Translation? @relation("ueInfoObjectivesTranslation", fields: [objectivesTranslationId], references: [id], onDelete: Cascade) - program Translation? @relation("ueInfoProgramTranslation", fields: [programTranslationId], references: [id], onDelete: Cascade) - requirements Ue[] @relation(name: "ueRequirements") + ueof Ueof? + objectives Translation? @relation("ueInfoObjectivesTranslation", fields: [objectivesTranslationId], references: [id], onDelete: Cascade) + program Translation? @relation("ueInfoProgramTranslation", fields: [programTranslationId], references: [id], onDelete: Cascade) } model UeStarCriterion { @@ -471,7 +489,7 @@ model UeStarVote { criterionId String userId String? - ue Ue @relation(fields: [ueId], references: [id], onDelete: Cascade) + ue Ue @relation(fields: [ueId], references: [code], onDelete: Cascade) criterion UeStarCriterion @relation(fields: [criterionId], references: [id], onDelete: Cascade) user User? @relation(fields: [userId], references: [id], onDelete: SetNull) @@ -479,16 +497,16 @@ model UeStarVote { } model UeWorkTime { - id String @id @default(uuid()) + id String @id @default(uuid()) cm Int? td Int? tp Int? the Int? project Boolean? internship Int? - ueId String @unique + ueofId String @unique - ue Ue @relation(fields: [ueId], references: [id], onDelete: Cascade) + ueof Ueof @relation(fields: [ueofId], references: [code], onDelete: Cascade) } model UserPermission { @@ -526,7 +544,7 @@ model User { infosId String @unique mailsPhonesId String @unique socialNetworkId String @unique - privacyId String @unique + privacyId String @unique userType UserType timestamps UserTimestamps? @@ -600,15 +618,15 @@ model UserBDEContribution { } model UserBranchSubscription { - id String @id @default(uuid()) - userId String - semesterNumber Int @db.SmallInt - createdAt DateTime @default(now()) - branchOptionId String - semesterCode String + id String @id @default(uuid()) + userId String + semesterNumber Int @db.SmallInt + createdAt DateTime @default(now()) + branchOptionId String + semesterCode String user User @relation(fields: [userId], references: [id]) - branchOption UTTBranchOption @relation(fields: [branchOptionId], references: [id]) + branchOption UTTBranchOption @relation(fields: [branchOptionId], references: [code]) semester Semester @relation(fields: [semesterCode], references: [code]) } @@ -702,11 +720,11 @@ model UserHomepageWidget { } model UserPreference { - id String @id @default(uuid()) - language Language @default(fr) - wantDaymail Boolean @default(false) - wantDayNotif Boolean @default(false) - wantDiscordUtt Boolean @default(false) + id String @id @default(uuid()) + language Language @default(fr) + wantDaymail Boolean @default(false) + wantDayNotif Boolean @default(false) + wantDiscordUtt Boolean @default(false) user User? } @@ -720,14 +738,14 @@ model UserRGPD { } model UserSocialNetwork { - id String @id @default(uuid()) - facebook String? @db.VarChar(255) - twitter String? @db.VarChar(255) - instagram String? @db.VarChar(255) - linkedin String? @db.VarChar(255) - twitch String? @db.VarChar(255) - spotify String? @db.VarChar(255) - discord String? @db.VarChar(255) + id String @id @default(uuid()) + facebook String? @db.VarChar(255) + twitter String? @db.VarChar(255) + instagram String? @db.VarChar(255) + linkedin String? @db.VarChar(255) + twitch String? @db.VarChar(255) + spotify String? @db.VarChar(255) + discord String? @db.VarChar(255) user User? } @@ -759,39 +777,39 @@ model UserUeSubscription { id String @id @default(uuid()) createdAt DateTime @default(now()) userId String - ueId String + ueofId String semesterId String user User @relation(fields: [userId], references: [id]) - ue Ue @relation(fields: [ueId], references: [id]) + ueof Ueof @relation(fields: [ueofId], references: [code]) semester Semester @relation(fields: [semesterId], references: [code]) - @@unique([userId, ueId, semesterId]) + @@unique([userId, ueofId, semesterId]) } model UTTBranch { - code String @id @db.VarChar(10) - name String @db.VarChar(255) + code String @id @db.VarChar(10) + name String @db.VarChar(255) + isMaster Boolean @default(false) exitSalary Int? employmentRate Float? CDIRate Float? abroadEmploymentRate Float? - descriptionTranslationId String @unique + descriptionTranslationId String @unique descriptionTranslation Translation @relation(fields: [descriptionTranslationId], references: [id], onDelete: Cascade) branchOptions UTTBranchOption[] } model UTTBranchOption { - id String @id @default(uuid()) - code String @db.VarChar(10) + code String @id @db.VarChar(10) name String @db.VarChar(255) branchCode String descriptionTranslationId String @unique branch UTTBranch @relation(fields: [branchCode], references: [code]) descriptionTranslation Translation @relation(fields: [descriptionTranslationId], references: [id], onDelete: Cascade) - ues Ue[] + ueofs Ueof[] branchSubscriptions UserBranchSubscription[] @@unique([code, branchCode]) diff --git a/scripts/seed/aliases.ts b/scripts/seed/aliases.ts new file mode 100644 index 0000000..0865560 --- /dev/null +++ b/scripts/seed/aliases.ts @@ -0,0 +1,26 @@ +import { PrismaClient } from '@prisma/client'; + +const prisma = new PrismaClient(); + +async function main() { + console.info('\x1b[42;30mSeeding UE aliases\x1b[0m'); + await prisma.ueAlias.deleteMany(); + await prisma.ueAlias.createMany({ + data: [ + { + code: 'EC01', + }, + { + code: 'MATH03', + standsFor: 'MT03', + }, + { + code: 'NF05', + standsFor: 'NF06', + }, + ], + }); + console.info('\x1b[42;30m✅ Aliases have been created\x1b[0m'); +} + +main(); diff --git a/scripts/seed/base.ts b/scripts/seed/base.ts index a20f947..c274660 100644 --- a/scripts/seed/base.ts +++ b/scripts/seed/base.ts @@ -7,16 +7,37 @@ async function main() { await prisma.semester.deleteMany({}); await prisma.semester.create({ data: { - code: 'P24', - start: new Date('2024-02-01'), - end: new Date('2024-09-01'), + code: 'A24', + start: new Date('2024-08-31'), + end: new Date('2025-01-20'), }, }); await prisma.semester.create({ data: { - code: 'A24', + code: 'H24', + start: new Date('2025-01-21'), + end: new Date('2025-02-14'), + }, + }); + await prisma.semester.create({ + data: { + code: 'P25', + start: new Date('2025-02-15'), + end: new Date('2025-06-29'), + }, + }); + await prisma.semester.create({ + data: { + code: 'E25', + start: new Date('2025-07-01'), + end: new Date('2025-08-30'), + }, + }); + await prisma.semester.create({ + data: { + code: 'U24', start: new Date('2024-08-31'), - end: new Date('2025-02-01'), + end: new Date('2025-06-29'), }, }); @@ -29,6 +50,8 @@ async function main() { { code: 'HT', name: 'Humanités et technologies' }, { code: 'ME', name: 'Mise en situation' }, { code: 'EC', name: 'Expression et communication' }, + { code: 'AC', name: 'Autres Crédits' }, + { code: 'MA', name: 'Master' }, ]; await Promise.all( categories.map((category) => @@ -41,6 +64,119 @@ async function main() { }), ), ); + + // BRANCHES // + console.log('Updating Branches'); + const branches = [ + { code: 'TC', name: 'Tronc commun', isMaster: false }, + { code: 'RT', name: 'Réseaux et télécommunications', isMaster: false }, + { code: 'GM', name: 'Génie mécanique', isMaster: false }, + { code: 'GM_APPR', name: 'Génie mécanique - Apprentissage', isMaster: false }, + { code: 'A2I', name: 'Automatique et informatique industrielle', isMaster: false }, + { code: 'MTE', name: 'Matériaux: Technologie et Economie', isMaster: false }, + { code: 'ISI', name: "Informatique et Systèmes d'Information", isMaster: false }, + { code: 'SN_APPR', name: 'Systèmes Numériques - Apprentissage', isMaster: false }, + { code: 'MM', name: 'Matériaux et Mécanique', isMaster: false }, + { code: 'GI', name: 'Génie Industriel', isMaster: false }, + { code: 'GI_APPR', name: 'Génie Industriel - Apprentissage', isMaster: false }, + { code: 'RE', name: 'Risques et Environnement', isMaster: true }, + { code: 'PAIP', name: 'Physique Appliquée et Ingénierie Physique', isMaster: true }, + { code: 'ISC', name: 'Ingénierie des Systèmes Complexes', isMaster: true }, + { code: 'IC', name: 'Ingénierie de Conception', isMaster: true }, + ]; + await Promise.all( + branches.map((branch) => + prisma.uTTBranch.upsert({ + where: { + code: branch.code, + }, + update: branch, + create: { + ...branch, + descriptionTranslation: { + create: {}, + }, + }, + }), + ), + ); + + // BRANCH OPTIONS // + console.log('Updating branch options'); + const branch_options = [ + { code: 'TC', name: 'Tronc commun', branch: 'TC' }, + { code: 'GM', name: 'Tronc commun de Génie Mécanique', branch: 'GM' }, + { code: 'A2I', name: "Tronc commun d'Automatique et informatique industrielle", branch: 'A2I' }, + { code: 'MTE', name: 'Tronc commun de Matériaux: Technologie et Economie', branch: 'MTE' }, + { code: 'GM_APPR', name: 'Tronc commun de Génie mécanique - Apprentissage', branch: 'GM_APPR' }, + { code: 'ISI', name: "Tronc commun d'Informatique et Systèmes d'Information", branch: 'ISI' }, + { code: 'MM', name: 'Tronc commun de Matériaux et Mécanique', branch: 'MM' }, + { code: 'GI_APPR', name: 'Tronc commun de Génie Industriel - Apprentissage', branch: 'GI_APPR' }, + { code: 'SN_APPR', name: 'Tronc commun de Systèmes Numériques - Apprentissage', branch: 'SN_APPR' }, + { code: 'RT', name: 'Tronc commun de Réseaux et télécommunications', branch: 'RT' }, + { code: 'GI', name: 'Tronc commun de Génie Industriel', branch: 'GI' }, + { code: 'MDPI_APPR', name: 'Management Digital des Produits et Infrastructures - Apprentissage', branch: 'GM' }, + { code: 'LIP_APPR', name: 'Logistique interne et production - Apprentissage', branch: 'GI' }, + { code: 'LET_APPR', name: 'Logistique externe et transport - Apprentissage', branch: 'GI' }, + { code: 'TQM', name: 'Transformation et qualité des matériaux', branch: 'MTE' }, + { code: 'TCMC', name: 'Technologie et commerce des matériaux et des composants', branch: 'MTE' }, + { code: 'TEI', name: 'Technologie embarquée et interopérabilité', branch: 'A2I' }, + { code: 'SPI', name: 'Systèmes de production intelligents', branch: 'A2I' }, + { + code: 'CEISME', + name: "Conception et industrialisation des systèmes mécaniques, en lien avec l'environnement", + branch: 'GM', + }, + { code: 'EME', name: 'Énergie, matériaux et environnement', branch: 'MTE' }, + { code: 'SSC', name: 'Sécurité des systèmes et des communications', branch: 'RT' }, + { code: 'IPL', name: 'Innovation par le logiciel', branch: 'ISI' }, + { code: 'ATN', name: 'Accompagnement de la Transformation Numérique', branch: 'ISI' }, + { code: 'VDC', name: 'Valorisation des données et des connaissances', branch: 'ISI' }, + { code: 'TMOC', name: 'Technologies mobiles et objets connectés', branch: 'RT' }, + { code: 'MDPI', name: 'Management Digital des Produits et infrastructures', branch: 'GM' }, + { code: 'SNM', name: 'Simulation numérique en mécanique', branch: 'GM' }, + { code: 'CSR', name: 'Convergence service réseaux', branch: 'RT' }, + { code: 'RAMS', name: 'Fiabilité, Maintenance, Disponibilité et Sûreté', branch: 'GI' }, + { code: 'RAMS_APPR', name: 'Fiabilité, Maintenance, Disponibilité et Sûreté - Apprentissage', branch: 'GI' }, + { code: 'SN', name: 'Tronc commun de Systèmes Numériques', branch: 'SN_APPR' }, + { code: 'LIP', name: 'Logistique interne et production', branch: 'GI' }, + { code: 'LET', name: 'Logistique externe et transport', branch: 'GI' }, + { code: 'RE', name: 'Mention Risques et Environnement', branch: 'RE' }, + { code: 'PAIP', name: 'Mention Physique Appliquée et Ingénierie Physique', branch: 'PAIP' }, + { code: 'ISC', name: 'Mention Ingénierie des Systèmes Complexes', branch: 'ISC' }, + { code: 'IC', name: 'Mention Ingénierie de Conception', branch: 'IC' }, + { code: 'IMEDD', name: "Ingénierie et Management de l'Environnement et du Développement Durable", branch: 'RE' }, + { code: 'SSI', name: "Sécurité des Systèmes d'Information", branch: 'ISC' }, + { code: 'NPHOT', name: 'Nano-optics and Nanophotonics', branch: 'PAIP' }, + { code: 'MPSMP', name: 'Mécanique et Performance en Service de Matériaux et Produits', branch: 'IC' }, + { code: 'OSS', name: 'Optimisation et Sûreté des Systèmes', branch: 'ISC' }, + { code: 'IMSGA', name: 'Ingénierie et Management en Sécurité Globale Appliquée', branch: 'RE' }, + ]; + await Promise.all( + branch_options.map((option) => + prisma.uTTBranchOption.upsert({ + where: { + code: option.code, + }, + update: { + branchCode: option.branch, + name: option.name, + }, + create: { + code: option.code, + name: option.name, + branch: { + connect: { + code: option.branch, + }, + }, + descriptionTranslation: { + create: {}, + }, + }, + }), + ), + ); } main(); diff --git a/scripts/seed/ue.ts b/scripts/seed/ue.ts index 1418c4a..d96ba8d 100644 --- a/scripts/seed/ue.ts +++ b/scripts/seed/ue.ts @@ -1,4 +1,5 @@ import { createReadStream } from 'fs'; +import { createInterface } from 'readline/promises'; import { parse } from '@fast-csv/parse'; import { PrismaClient } from '@prisma/client'; @@ -6,40 +7,64 @@ const prisma = new PrismaClient(); type UEOF = { entry_nb: string; - shortname: string; - fullname: string; - idnumber: string; - category_idnumber: string; - startdate: string; - enddate: string; + siep_id: number; + ueof_code: string; + code: string; + name: string; objectives: string; program: string; - cm_hours: string; - td_hours: string; - tp_hours: string; - the_hours: string; - internship_hours: string; - summary: string; lang: string; + cm_hours: number; + td_hours: number; + tp_hours: number; + the_hours: number; + has_project: boolean; + internship_hours: number; + credit_count: number; + engineer_credit_type?: string; + master_credit_type?: string; + semesters: string[]; + minors: string[]; + requirements: string[]; + engineer_branch: string[]; + engineer_branch_option: string[]; + master_branch: string[]; + master_branch_option: string[]; }; type DataHeader = keyof UEOF; +async function findPadding(document: string) { + return new Promise((resolve, reject) => { + const readable = createReadStream(document); + const reader = createInterface({ input: readable }); + let count = 0; + reader + .on('line', (line) => { + count++; + if (!line) { + resolve(count); + readable.destroy(); + } + }) + .on('close', () => reject()); + }); +} + async function parseDocument(document: string) { - return new Promise((resolve, reject) => { + return new Promise(async (resolve, reject) => { const ues: UEOF[] = []; + const padding = await findPadding(document); createReadStream(document) .pipe( parse({ delimiter: ';', - skipLines: 5, + skipLines: padding, headers: [ 'entry_nb', - 'shortname', - 'fullname', - 'idnumber', - 'category_idnumber', - 'startdate', - 'enddate', + 'siep_id', + 'ueof_code', + 'code', + 'name', 'objectives', 'program', 'cm_hours', @@ -47,8 +72,18 @@ async function parseDocument(document: string) { 'tp_hours', 'the_hours', 'internship_hours', - 'summary', + 'credit_count', 'lang', + 'minors', + 'semesters', + 'has_project', + 'engineer_credit_type', + 'master_credit_type', + 'requirements', + 'engineer_branch', + 'engineer_branch_option', + 'master_branch_option', + 'master_branch', ] satisfies DataHeader[], renameHeaders: true, }), @@ -58,32 +93,82 @@ async function parseDocument(document: string) { console.error(error); reject(); }) - .on('data', (ue) => ues.push(ue)) - .on('end', () => resolve(ues)); + .on('data', (ue) => + ues.push({ + ...ue, + siep_id: Number(ue.siep_id), + cm_hours: Number(ue.cm_hours) || 0, + td_hours: Number(ue.td_hours) || 0, + tp_hours: Number(ue.tp_hours) || 0, + the_hours: Number(ue.the_hours) || 0, + has_project: ue.has_project === 'OUI', + internship_hours: Number(ue.internship_hours) || 0, + credit_count: Number(ue.credit_count) || 0, + requirements: ue.requirements ? [ue.requirements as unknown as string] : [], + semesters: ue.semesters ? (ue.semesters as unknown as string)?.split(/\s\/\s/g) : [], + minors: ue.minors ? (ue.minors as unknown as string)?.split(/\s\/\s/g) : [], + engineer_branch: ue.engineer_branch ? (ue.engineer_branch as unknown as string)?.split(/\s\/\s/g) : [], + engineer_branch_option: ue.engineer_branch_option + ? (ue.engineer_branch_option as unknown as string)?.split(/\s\/\s/g) + : [], + master_branch: ue.master_branch ? (ue.master_branch as unknown as string)?.split(/\s\/\s/g) : [], + master_branch_option: ue.master_branch_option + ? (ue.master_branch_option as unknown as string)?.split(/\s\/\s/g) + : [], + }), + ) + .on('end', () => + resolve( + ues.reduce((prev, ueof) => { + // Data has not been merged in the CSV for the field `requirements`, we need to merge it manually + const existing = prev.find((ue) => ue.ueof_code === ueof.ueof_code); + if (!existing) return [...prev, ueof]; + existing.requirements.push(...ueof.requirements); + return prev; + }, []), + ), + ); }); } async function main() { + const importYear = new Date().getFullYear(); // Can be changed to the year of the import if done with web interface + + console.info('\x1b[42;30mFetching UE list\x1b[0m'); const ues = await parseDocument('scripts/seed/ues.csv'); - await prisma.ue.deleteMany({}); - const promises = ues.map((ue) => - prisma.ue.create({ - data: { - code: ue.shortname, - legacyCode: ue.idnumber, - siepId: Number(ue.entry_nb), + await prisma.ueof.updateMany({ + data: { available: false }, + }); + + console.info('\x1b[42;30mUpdating UEs\x1b[0m'); + for (const ue of ues) { + await prisma.ueof.upsert({ + where: { + code: ue.ueof_code, + }, + create: { + code: ue.ueof_code, + siepId: ue.siep_id, + ue: { + connectOrCreate: { + where: { code: ue.code }, + create: { + code: ue.code, + creationYear: 2000 + Number(ue.ueof_code.slice(-2)), + updateYear: 2000 + Number(ue.ueof_code.slice(-2)), + }, + }, + }, name: { create: { - fr: ue.fullname, + fr: ue.name, }, }, + available: true, info: { create: { - comment: { - create: { - fr: ue.summary, - }, - }, + language: ue.lang, + minors: ue.minors.join(), objectives: { create: { fr: ue.objectives, @@ -94,34 +179,138 @@ async function main() { fr: ue.program, }, }, - languages: ue.lang, }, }, - branchOption: {}, - subsequentUes: {}, workTime: { create: { - cm: Number(ue.cm_hours), - td: Number(ue.td_hours), - tp: Number(ue.tp_hours), - the: Number(ue.the_hours), - internship: Number(ue.internship_hours), + cm: ue.cm_hours, + td: ue.td_hours, + tp: ue.tp_hours, + the: ue.the_hours, + internship: ue.internship_hours, + project: ue.has_project, }, }, credits: { - create: { - credits: 0, - category: { - connect: { - code: 'CS', + create: (ue.code === 'PE00' + ? ['CS', 'TM', 'EC', 'ME', 'HT', 'AC'] + : [...new Set([ue.engineer_credit_type, ue.master_credit_type])] + ) + .filter((type) => type) + .map((type) => ({ credits: ue.credit_count, categoryId: type })), + }, + branchOption: { + connect: [ + ...(ue.engineer_branch_option.length ? ue.engineer_branch_option : ue.engineer_branch), + ...(ue.master_branch_option.length ? ue.master_branch_option : ue.master_branch), + ].map((code) => ({ + code, + })), + }, + openSemester: { + connect: ue.semesters.map((code) => ({ + code: `${code}${(importYear + Number(code === 'P')) % 100}`, + })), + }, + }, + update: { + siepId: ue.siep_id, + name: { + update: { + fr: ue.name, + }, + }, + available: true, + info: { + update: { + language: ue.lang, + minors: ue.minors.join(), + objectives: { + update: { + fr: ue.objectives, + }, + }, + program: { + update: { + fr: ue.program, }, }, }, }, + workTime: { + update: { + cm: ue.cm_hours, + td: ue.td_hours, + tp: ue.tp_hours, + the: ue.the_hours, + internship: ue.internship_hours, + project: ue.has_project, + }, + }, + credits: { + deleteMany: {}, + create: (ue.code === 'PE00' + ? ['CS', 'TM', 'EC', 'ME', 'HT', 'AC'] + : [...new Set([ue.engineer_credit_type, ue.master_credit_type])] + ) + .filter((type) => type) + .map((type) => ({ categoryId: type, credits: ue.credit_count })), + }, + branchOption: { + set: [ + ...(ue.engineer_branch_option.length ? ue.engineer_branch_option : ue.engineer_branch), + ...(ue.master_branch_option.length ? ue.master_branch_option : ue.master_branch), + ].map((code) => ({ + code, + })), + }, + openSemester: { + connect: ue.semesters.map((code) => ({ + code: `${code}${(importYear + Number(code === 'P')) % 100}`, + })), + }, }, - }), - ); - await Promise.all(promises); + }); + } + + const aliases = await prisma.ueAlias.findMany(); + + console.info('\x1b[42;30mImporting UE requirements...\x1b[0m'); + + try { + await Promise.all( + ues.map((ueof) => + prisma.ueof.update({ + where: { + code: ueof.ueof_code, + }, + data: { + ue: { + update: { + updateYear: 2000 + Number(ueof.ueof_code.slice(-2)), + }, + }, + requirements: { + connect: ueof.requirements + .map((code) => { + const result = aliases.find((alias) => alias.code === code); + return result ? result.standsFor : code; + }) + .filter((code) => code) + .map((code) => ({ + code: code, + })), + }, + }, + }), + ), + ); + console.info('\x1b[42;30m✅ Import complete\x1b[0m'); + } catch (error) { + console.error( + '\x1b[41;30mAn error occurred while importing UE requirements. Try `$ pnpm seed:ue:aliases` first.\x1b[0m', + ); + } } main(); From 84f4f8e5b3be3ec193038b9a3e6454098b407241 Mon Sep 17 00:00:00 2001 From: AlbanSdl Date: Sun, 28 Jul 2024 02:51:08 +0200 Subject: [PATCH 04/14] fix(ue): structural update part 1 --- src/auth/auth.service.ts | 8 +- src/exceptions.ts | 5 + src/ldap/ldap.module.ts | 2 +- src/semester/semester.service.ts | 5 + src/ue/annals/annals.controller.ts | 14 +- src/ue/annals/annals.service.ts | 33 ++- src/ue/annals/dto/create-annal.dto.ts | 18 +- src/ue/annals/interfaces/annal.interface.ts | 1 + src/ue/comments/comments.service.ts | 16 +- src/ue/dto/ue-search.dto.ts | 10 +- src/ue/interfaces/ue.interface.ts | 95 +++++---- src/ue/ue.controller.ts | 225 +++++++++++--------- src/ue/ue.service.ts | 143 ++++++------- 13 files changed, 325 insertions(+), 250 deletions(-) diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index 04b1667..9376b94 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -51,7 +51,7 @@ export class AuthService { dto.studentId = Number(ldapUser.supannEtuId); type = UserType.STUDENT; branch.push(...(Array.isArray(ldapUser.niveau) ? ldapUser.niveau : [ldapUser.niveau])); - ues.push(...(Array.isArray(ldapUser.uv) ? ldapUser.uv : [ldapUser.uv])); + ues.push(...(Array.isArray(ldapUser.uv) ? ldapUser.uv : [ldapUser.uv])); // TODO : check what is done by the admin : are they UEOF or UE codes ? branchOption.push(...(Array.isArray(ldapUser.filiere) ? ldapUser.filiere : [ldapUser.filiere])); [formation] = Array.isArray(ldapUser.formation) ? ldapUser.formation : [ldapUser.formation]; // TODO: this is wrong, students can have multiple formations ! } else if (ldapUser.gidNumber === LdapAccountGroup.EMPLOYEES) { @@ -113,10 +113,8 @@ export class AuthService { UesSubscriptions: currentSemester ? { createMany: { - data: ( - await this.ueService.getIdFromCode(ues) - ).map((id) => ({ - ueId: id, + data: ues.map((id) => ({ + ueofId: id, semesterId: currentSemester.code, })), }, diff --git a/src/exceptions.ts b/src/exceptions.ts index b5a5afa..5404e8f 100644 --- a/src/exceptions.ts +++ b/src/exceptions.ts @@ -65,6 +65,7 @@ export const enum ERROR_CODE { NO_SUCH_ANNAL = 4408, NO_SUCH_ANNAL_TYPE = 4409, NO_SUCH_ASSO = 4410, + NO_SUCH_UEOF = 4411, ANNAL_ALREADY_UPLOADED = 4901, CREDENTIALS_ALREADY_TAKEN = 5001, } @@ -283,6 +284,10 @@ export const ErrorData = Object.freeze({ message: 'The asso % does no exist', httpCode: HttpStatus.NOT_FOUND, }, + [ERROR_CODE.NO_SUCH_UEOF]: { + message: 'UEOF % does no exist', + httpCode: HttpStatus.NOT_FOUND, + }, [ERROR_CODE.ANNAL_ALREADY_UPLOADED]: { message: 'A file has alreay been uploaded for this annal', httpCode: HttpStatus.CONFLICT, diff --git a/src/ldap/ldap.module.ts b/src/ldap/ldap.module.ts index a5ba249..a27b2c7 100644 --- a/src/ldap/ldap.module.ts +++ b/src/ldap/ldap.module.ts @@ -1,7 +1,7 @@ import { Injectable, Module } from '@nestjs/common'; import { Client as LdapClient } from 'ldapts'; import { ConfigModule } from '../config/config.module'; -import { LdapAccountGroup, LdapUser } from './ldap.interface'; +import { LdapUser } from './ldap.interface'; @Module({ exports: [LdapModule], diff --git a/src/semester/semester.service.ts b/src/semester/semester.service.ts index 13396cf..47d99c3 100644 --- a/src/semester/semester.service.ts +++ b/src/semester/semester.service.ts @@ -55,6 +55,11 @@ export class SemesterService { start: { lte: new Date(), }, + code: { + not: { + contains: 'U', + }, + }, }, }); } diff --git a/src/ue/annals/annals.controller.ts b/src/ue/annals/annals.controller.ts index 5e0c16f..9dd60be 100644 --- a/src/ue/annals/annals.controller.ts +++ b/src/ue/annals/annals.controller.ts @@ -26,15 +26,17 @@ export class AnnalsController { @Post() @RequireUserType('STUDENT') - async createUeAnnal(@Body() { ueCode, semester, typeId }: CreateAnnal, @GetUser() user: User) { + async createUeAnnal(@Body() { ueCode, semester, typeId, ueof }: CreateAnnal, @GetUser() user: User) { + if (ueof && !user.permissions.includes('annalUploader')) + throw new AppException(ERROR_CODE.PARAM_DOES_NOT_EXIST, 'ueof'); if (!(await this.ueService.doesUeExist(ueCode))) throw new AppException(ERROR_CODE.NO_SUCH_UE, ueCode); if (!(await this.annalsService.doesAnnalTypeExist(typeId))) throw new AppException(ERROR_CODE.NO_SUCH_ANNAL_TYPE); - if ( - !(await this.ueService.hasDoneThisUeInSemester(user.id, ueCode, semester)) && - !user.permissions.includes('annalUploader') - ) + try { + return this.annalsService.createAnnalFile(user, { ueCode, semester, typeId }, ueof); + } catch (error) { + if (user.permissions.includes('annalUploader')) throw new AppException(ERROR_CODE.NO_SUCH_UEOF, ueof); throw new AppException(ERROR_CODE.NOT_DONE_UE_IN_SEMESTER, ueCode, semester); - return this.annalsService.createAnnalFile(user, { ueCode, semester, typeId }); + } } @Get('metadata') diff --git a/src/ue/annals/annals.service.ts b/src/ue/annals/annals.service.ts index 1427d90..494050e 100644 --- a/src/ue/annals/annals.service.ts +++ b/src/ue/annals/annals.service.ts @@ -9,15 +9,20 @@ import { CreateAnnal } from './dto/create-annal.dto'; import { UpdateAnnalDto } from './dto/update-annal.dto'; import { ConfigModule } from '../../config/config.module'; import { User } from '../../users/interfaces/user.interface'; +import { Semester } from '@prisma/client'; @Injectable() export class AnnalsService { constructor(readonly prisma: PrismaService, readonly config: ConfigModule) {} async getUEAnnalMetadata(user: User, ueCode: string, isModerator: boolean) { - const ue = await this.prisma.ue.findUnique({ + const ueof = await this.prisma.ueof.findMany({ where: { - code: ueCode, + ueId: ueCode, + available: true, + }, + include: { + openSemester: true, }, }); const semesters = !isModerator @@ -25,11 +30,15 @@ export class AnnalsService { await this.prisma.userUeSubscription.findMany({ where: { userId: user.id, - ueId: ue.id, + ueof: { + ueId: ueCode, + }, }, }) ).map((subscription) => subscription.semesterId) - : ue.openSemester.map((semester) => semester.code); + : [...ueof.reduce((prev, nxt) => new Set([...prev, ...nxt.openSemester]), new Set())].map( + (semester) => semester.code, + ); const annalType = await this.prisma.ueAnnalType.findMany(); return { types: annalType, @@ -47,7 +56,16 @@ export class AnnalsService { ); } - async createAnnalFile(user: User, params: CreateAnnal) { + async createAnnalFile(user: User, params: CreateAnnal, ueof?: string) { + const subscription = await this.prisma.userUeSubscription.findFirst({ + where: { + semesterId: params.semester, + userId: user.id, + ueof: { + ueId: params.ueCode, + }, + }, + }); // Create upload/file entry return this.prisma.ueAnnal.create({ data: { @@ -71,6 +89,11 @@ export class AnnalsService { code: params.ueCode, }, }, + ueof: { + connect: { + code: subscription.ueofId ?? ueof, + }, + }, }, }); } diff --git a/src/ue/annals/dto/create-annal.dto.ts b/src/ue/annals/dto/create-annal.dto.ts index 2b0c04d..dff1275 100644 --- a/src/ue/annals/dto/create-annal.dto.ts +++ b/src/ue/annals/dto/create-annal.dto.ts @@ -1,4 +1,13 @@ -import { IsAlphanumeric, IsNotEmpty, IsString, IsUUID, Length, MaxLength, MinLength } from 'class-validator'; +import { + IsAlphanumeric, + IsNotEmpty, + IsOptional, + IsString, + IsUUID, + Length, + MaxLength, + MinLength, +} from 'class-validator'; export class CreateAnnal { @IsString() @@ -17,4 +26,11 @@ export class CreateAnnal { @MinLength(3) @MaxLength(5) ueCode: string; + + @IsOptional() + @IsString() + @IsNotEmpty() + @MaxLength(20) + @MinLength(12) + ueof?: string; } diff --git a/src/ue/annals/interfaces/annal.interface.ts b/src/ue/annals/interfaces/annal.interface.ts index 3d42619..5735540 100644 --- a/src/ue/annals/interfaces/annal.interface.ts +++ b/src/ue/annals/interfaces/annal.interface.ts @@ -26,6 +26,7 @@ const UE_ANNAL_SELECT_FILTER = { }, validatedAt: true, ue: { select: { code: true } }, + ueof: { select: { code: true, info: { select: { language: true } } } }, }, } satisfies Prisma.UeAnnalFindManyArgs; diff --git a/src/ue/comments/comments.service.ts b/src/ue/comments/comments.service.ts index 7d30604..71f4ac5 100644 --- a/src/ue/comments/comments.service.ts +++ b/src/ue/comments/comments.service.ts @@ -146,8 +146,8 @@ export class CommentsService { async getLastSemesterDoneByUser(userId: string, ueCode: string): Promise { return this.prisma.userUeSubscription.findFirst({ where: { - ue: { - code: ueCode, + ueof: { + ueId: ueCode, }, userId, }, @@ -182,7 +182,7 @@ export class CommentsService { }, where: { authorId: userId, - ueId: ue.id, + ueId: ue.code, }, }); return comment.length > 0; @@ -196,6 +196,8 @@ export class CommentsService { * @returns the created {@link UeComment} */ async createComment(body: UeCommentPostDto, userId: string): Promise { + // Use last semester done when creating the comment + const lastSemester = await this.getLastSemesterDoneByUser(userId, body.ueCode); return this.prisma.ueComment.create( { args: { @@ -217,10 +219,14 @@ export class CommentsService { code: body.ueCode, }, }, + ueof: { + connect: { + code: lastSemester.ueofId, + }, + }, semester: { connect: { - // Use last semester done when creating the comment - code: (await this.getLastSemesterDoneByUser(userId, body.ueCode)).semesterId, + code: lastSemester.semesterId, }, }, }, diff --git a/src/ue/dto/ue-search.dto.ts b/src/ue/dto/ue-search.dto.ts index 361ea3f..fd041f2 100644 --- a/src/ue/dto/ue-search.dto.ts +++ b/src/ue/dto/ue-search.dto.ts @@ -1,5 +1,5 @@ import { Type } from 'class-transformer'; -import { IsNumber, IsPositive, IsString, MaxLength, MinLength, IsOptional } from 'class-validator'; +import { IsNumber, IsPositive, IsString, IsOptional, Length } from 'class-validator'; /** * Query parameters of the request to search UEs. @@ -28,8 +28,7 @@ export class UeSearchDto { creditType?: string; @IsString() - @MaxLength(3) - @MinLength(3) + @Length(3) @IsOptional() availableAtSemester?: string; @@ -38,4 +37,9 @@ export class UeSearchDto { @IsPositive() @IsOptional() page?: number; + + @IsString() + @Length(2) + @IsOptional() + preferredLang?: string; } diff --git a/src/ue/interfaces/ue.interface.ts b/src/ue/interfaces/ue.interface.ts index af93ae1..d217f4b 100644 --- a/src/ue/interfaces/ue.interface.ts +++ b/src/ue/interfaces/ue.interface.ts @@ -4,64 +4,71 @@ import { translationSelect } from '../../utils'; const UE_SELECT_FILTER = { select: { - id: true, code: true, - inscriptionCode: true, - name: translationSelect, - info: { + updateYear: true, + creationYear: true, + subsequentUes: true, + ueofs: { + where: { + available: true, + }, select: { + name: translationSelect, + siepId: true, requirements: { select: { code: true, }, }, - comment: translationSelect, - degree: true, - languages: true, - minors: true, - objectives: translationSelect, - program: translationSelect, - }, - }, - openSemester: { - select: { - code: true, - start: true, - end: true, - }, - orderBy: { - start: 'asc', - }, - }, - workTime: { - select: { - cm: true, - td: true, - tp: true, - the: true, - project: true, - internship: true, - }, - }, - credits: { - select: { - credits: true, - category: { + info: { + select: { + language: true, + minors: true, + objectives: translationSelect, + program: translationSelect, + }, + }, + openSemester: { select: { code: true, - name: true, + start: true, + end: true, + }, + orderBy: { + start: 'asc', }, }, - }, - }, - branchOption: { - select: { - code: true, - name: true, - branch: { + workTime: { + select: { + cm: true, + td: true, + tp: true, + the: true, + project: true, + internship: true, + }, + }, + credits: { + select: { + credits: true, + category: { + select: { + code: true, + name: true, + }, + }, + }, + }, + branchOption: { select: { code: true, name: true, + branch: { + select: { + code: true, + name: true, + }, + }, }, }, }, diff --git a/src/ue/ue.controller.ts b/src/ue/ue.controller.ts index 574d7c8..4999786 100644 --- a/src/ue/ue.controller.ts +++ b/src/ue/ue.controller.ts @@ -9,6 +9,7 @@ import { UeRateDto } from './dto/ue-rate.dto'; import { Ue } from './interfaces/ue.interface'; import { Language, UserType } from '@prisma/client'; import { Translation } from '../prisma/types'; +import { omit } from 'src/utils'; @Controller('ue') export class UeController { @@ -17,21 +18,30 @@ export class UeController { @Get() @IsPublic() async searchUe( + @GetUser() user: User, @Headers('language') language: Language, @Query() queryParams: UeSearchDto, ): Promise> { const res = await this.ueService.searchUes(queryParams, language); return { ...res, - items: res.items.map((ue) => this.formatUeOverview(ue)), + items: res.items.map((ue) => + this.formatUeOverview( + ue, + queryParams.preferredLang ? [queryParams.preferredLang, language] : [language], + user?.branchSubscriptions.map((sub) => [sub.branchOption.code, sub.branchOption.branch.code]).flat() ?? [], // TODO : add more filters + ), + ), }; } @Get('/:ueCode') @IsPublic() - async getUe(@Param('ueCode') ueCode: string, @GetUser() user: User): Promise { + async getUe(@GetUser() user: User, @Param('ueCode') ueCode: string): Promise> { if (!(await this.ueService.doesUeExist(ueCode))) throw new AppException(ERROR_CODE.NO_SUCH_UE, ueCode); - return this.formatDetailedUe(await this.ueService.getUe(ueCode.toUpperCase()), user); + const result = this.formatDetailedUe(await this.ueService.getUe(ueCode.toUpperCase())); + if (user.userType === UserType.STUDENT || user.userType === UserType.FORMER_STUDENT) return result; + return omit(result, 'starVotes'); } @Get('/rate/criteria') @@ -73,23 +83,36 @@ export class UeController { @Get('/of/me') @RequireUserType('STUDENT') - async getMyUes(@GetUser() user: User): Promise { - return (await this.ueService.getUesOfUser(user.id)).map((ue) => this.formatUeOverview(ue)); + async getMyUes(@GetUser() user: User, @Headers('language') language: Language): Promise { + return (await this.ueService.getUesOfUser(user.id)).map((ue) => + this.formatUeOverview( + ue, + [language], + user.branchSubscriptions.map((sub) => [sub.branchOption.code, sub.branchOption.branch.code]).flat(), + ), + ); } - private formatUeOverview(ue: Ue): UeOverview { + /** This method chooses an UEOF and displays its basic data */ + private formatUeOverview(ue: Ue, langPref: string[], branchOptionPref: string[]): UeOverview { + const lowerCasePref = langPref.map((lang) => lang.toLocaleLowerCase()); + const availableOf = ue.ueofs.filter((ueof) => + branchOptionPref.some((optionPref) => ueof.branchOption.some((option) => option.code === optionPref)), + ); + const chosenOf = availableOf.length + ? availableOf.find((ueof) => lowerCasePref.includes(ueof.info.language)) + : ue.ueofs.find((ueof) => lowerCasePref.includes(ueof.info.language)) ?? ue.ueofs[0]; return { code: ue.code, - inscriptionCode: ue.inscriptionCode, - name: ue.name, - credits: ue.credits.map((c) => ({ + name: chosenOf.name, + credits: chosenOf.credits.map((c) => ({ credits: c.credits, category: { code: c.category.code, name: c.category.name, }, })), - branchOption: ue.branchOption.map((branchOption) => ({ + branchOption: chosenOf.branchOption.map((branchOption) => ({ code: branchOption.code, name: branchOption.name, branch: { @@ -98,74 +121,73 @@ export class UeController { }, })), info: { - requirements: ue.info.requirements.map((r) => r.code), - comment: ue.info.comment, - degree: ue.info.degree, - languages: ue.info.languages, - minors: ue.info.minors, - objectives: ue.info.objectives, - program: ue.info.program, + requirements: chosenOf.requirements.map((r) => r.code), + languages: [...new Set(ue.ueofs.map((ueof) => ueof.info.language))], + minors: chosenOf.info.minors, + objectives: chosenOf.info.objectives, + program: chosenOf.info.program, }, - openSemester: ue.openSemester.map((semester) => ({ - code: semester.code, - start: semester.start, - end: semester.end, - })), + openSemester: ue.ueofs + .map((ueof) => ueof.openSemester) + .reduce((acc, val) => [...acc, ...val.filter((sem) => acc.every((has) => has.code !== sem.code))], []) + .map((semester) => ({ + code: semester.code, + start: semester.start, + end: semester.end, + })), }; } - private formatDetailedUe(ue: Ue, user?: User): UeDetail { - const format = { + private formatDetailedUe(ue: Ue): UeDetail { + return { code: ue.code, - inscriptionCode: ue.inscriptionCode, - name: ue.name, - credits: ue.credits.map((c) => ({ - credits: c.credits, - category: { - code: c.category.code, - name: c.category.name, + creationYear: ue.creationYear, + updateYear: ue.updateYear, + ofs: ue.ueofs.map((ueof) => ({ + name: ueof.name, + credits: ueof.credits.map((c) => ({ + credits: c.credits, + category: { + code: c.category.code, + name: c.category.name, + }, + })), + branchOption: ueof.branchOption.map((branchOption) => ({ + code: branchOption.code, + name: branchOption.name, + branch: { + code: branchOption.branch.code, + name: branchOption.branch.name, + }, + })), + info: { + requirements: ueof.requirements.map((r) => r.code), + language: ueof.info.language, + minors: ueof.info.minors, + objectives: ueof.info.objectives, + program: ueof.info.program, }, - })), - branchOption: ue.branchOption.map((branchOption) => ({ - code: branchOption.code, - name: branchOption.name, - branch: { - code: branchOption.branch.code, - name: branchOption.branch.name, + openSemester: ueof.openSemester.map((semester) => ({ + code: semester.code, + start: semester.start, + end: semester.end, + })), + workTime: { + cm: ueof.workTime.cm, + td: ueof.workTime.td, + tp: ueof.workTime.tp, + the: ueof.workTime.the, + project: ueof.workTime.project, + internship: ueof.workTime.internship, }, })), - info: { - requirements: ue.info.requirements.map((r) => r.code), - comment: ue.info.comment, - degree: ue.info.degree, - languages: ue.info.languages, - minors: ue.info.minors, - objectives: ue.info.objectives, - program: ue.info.program, - }, - openSemester: ue.openSemester.map((semester) => ({ - code: semester.code, - start: semester.start, - end: semester.end, - })), - workTime: { - cm: ue.workTime.cm, - td: ue.workTime.td, - tp: ue.workTime.tp, - the: ue.workTime.the, - project: ue.workTime.project, - internship: ue.workTime.internship, - }, - } as UeDetail; - if (user?.userType === UserType.STUDENT || user?.userType === UserType.FORMER_STUDENT) - format.starVotes = ue.starVotes; - return format; + starVotes: ue.starVotes, + }; } } export type UeOverview = { code: string; - inscriptionCode: string; name: Translation; credits: Array<{ credits: number; @@ -184,9 +206,7 @@ export type UeOverview = { }>; info: { requirements: string[]; - comment: Translation; - degree: string; - languages: string; + languages: string[]; minors: string; objectives: Translation; program: Translation; @@ -200,44 +220,45 @@ export type UeOverview = { export type UeDetail = { code: string; - inscriptionCode: string; - name: Translation; - credits: Array<{ - credits: number; - category: { + creationYear: number; + updateYear: number; + ofs: { + name: Translation; + credits: Array<{ + credits: number; + category: { + code: string; + name: string; + }; + }>; + branchOption: Array<{ + branch: { + code: string; + name: string; + }; code: string; name: string; + }>; + info: { + requirements: string[]; + language: string; + minors: string; + objectives: Translation; + program: Translation; }; - }>; - branchOption: Array<{ - branch: { + openSemester: Array<{ code: string; - name: string; + start: Date; + end: Date; + }>; + workTime: { + cm: number; + td: number; + tp: number; + the: number; + project: boolean; + internship: number; }; - code: string; - name: string; - }>; - info: { - requirements: string[]; - comment: Translation; - degree: string; - languages: string; - minors: string; - objectives: Translation; - program: Translation; - }; - openSemester: Array<{ - code: string; - start: Date; - end: Date; - }>; - workTime: { - cm: number; - td: number; - tp: number; - the: number; - project: number; - internship: number; - }; + }[]; starVotes?: { [criterionId: string]: number }; }; diff --git a/src/ue/ue.service.ts b/src/ue/ue.service.ts index 273f316..4b1633e 100644 --- a/src/ue/ue.service.ts +++ b/src/ue/ue.service.ts @@ -18,25 +18,9 @@ export class UeService { readonly semesterService: SemesterService, ) {} - async getIdFromCode(ueCode: string): Promise; - async getIdFromCode(ueCodes: string[]): Promise; - async getIdFromCode(ueCodes?: string | string[]) { - const values = ( - await this.prisma.ue.findMany({ - where: { - code: { - in: Array.isArray(ueCodes) ? ueCodes : [ueCodes], - }, - }, - }) - ).map((ue) => ue.id); - if (!Array.isArray(ueCodes)) return values[0]; - return values; - } - /** * Retrieves a page of {@link Ue} matching the user query. This query searchs for a text in - * the ue code, name, comment, objectives and program. The user can restrict his research to a branch, + * the ue code, name, objectives and program. The user can restrict his research to a branch, * a branch option, a credit type or a semester. * @param query the query parameters of this route * @param language the language in which to search for text @@ -58,24 +42,33 @@ export class UeService { }, }, { - inscriptionCode: { - contains: query.q, + ueofs: { + some: { + code: { contains: query.q }, + }, }, }, { - name: { - [language]: { - contains: query.q, + ueofs: { + some: { + name: { + [language]: { + contains: query.q, + }, + }, }, }, }, { - info: { - OR: [ - { comment: { [language]: { contains: query.q } } }, - { objectives: { [language]: { contains: query.q } } }, - { program: { [language]: { contains: query.q } } }, - ], + ueofs: { + some: { + info: { + OR: [ + { objectives: { [language]: { contains: query.q } } }, + { program: { [language]: { contains: query.q } } }, + ], + }, + }, }, }, ], @@ -84,16 +77,20 @@ export class UeService { // Filter per branch option and branch if such a filter is present ...(query.branchOption || query.branch ? { - branchOption: { + ueofs: { some: { - OR: [ - { code: query.branchOption }, - { - branch: { - code: query.branch, - }, + branchOption: { + some: { + OR: [ + { code: query.branchOption }, + { + branch: { + code: query.branch, + }, + }, + ], }, - ], + }, }, }, } @@ -101,10 +98,14 @@ export class UeService { // Filter per credit type ...(query.creditType ? { - credits: { + ueofs: { some: { - category: { - code: query.creditType, + credits: { + some: { + category: { + code: query.creditType, + }, + }, }, }, }, @@ -113,9 +114,13 @@ export class UeService { // Filter per semester ...(query.availableAtSemester ? { - openSemester: { + ueofs: { some: { - code: query.availableAtSemester?.toUpperCase(), + openSemester: { + some: { + code: query.availableAtSemester?.toUpperCase(), + }, + }, }, }, } @@ -161,8 +166,8 @@ export class UeService { async getLastSemesterDoneByUser(userId: string, ueCode: string): Promise { return this.prisma.userUeSubscription.findFirst({ where: { - ue: { - code: ueCode, + ueof: { + ueId: ueCode, }, userId, }, @@ -180,13 +185,11 @@ export class UeService { * @returns whether the ue exists */ async doesUeExist(ueCode: string) { - return ( - (await this.prisma.ue.count({ - where: { - code: ueCode, - }, - })) != 0 - ); + return !!(await this.prisma.ue.findUnique({ + where: { + code: ueCode, + }, + })); } /** @@ -200,20 +203,6 @@ export class UeService { return (await this.getLastSemesterDoneByUser(userId, ueCode)) != null; } - async hasDoneThisUeInSemester(userId: string, ueCode: string, semesterCode: string) { - return ( - (await this.prisma.userUeSubscription.count({ - where: { - semesterId: semesterCode, - ue: { - code: ueCode, - }, - userId, - }, - })) != 0 - ); - } - /** * Checks whether a criterion exists * @param criterionId the id of the criterion to check @@ -267,7 +256,7 @@ export class UeService { return this.prisma.ueStarVote.findMany({ where: { userId: userId, - ueId: ue.id, + ueId: ue.code, }, }); } @@ -281,11 +270,10 @@ export class UeService { * @returns the new rate of the {@link ueCode | ue} for the {@link user} */ async doRateUe(userId: string, ueCode: string, dto: UeRateDto): Promise { - const ueId = await this.getUeIdFromCode(ueCode); return this.prisma.ueStarVote.upsert({ where: { ueId_userId_criterionId: { - ueId, + ueId: ueCode, userId, criterionId: dto.criterion, }, @@ -293,7 +281,7 @@ export class UeService { create: { value: dto.value, criterionId: dto.criterion, - ueId, + ueId: ueCode, userId, }, update: { @@ -303,11 +291,10 @@ export class UeService { } async unRateUe(userId: string, ueCode: string, criterionId: string): Promise { - const ueId = await this.getUeIdFromCode(ueCode); return this.prisma.ueStarVote.delete({ where: { ueId_userId_criterionId: { - ueId, + ueId: ueCode, userId, criterionId, }, @@ -320,19 +307,19 @@ export class UeService { if (currentSemester === null) return []; return this.prisma.ue.findMany({ where: { - usersSubscriptions: { + ueofs: { some: { - userId, - semester: { - code: currentSemester.code, + usersSubscriptions: { + some: { + userId, + semester: { + code: currentSemester.code, + }, + }, }, }, }, }, }); } - - private async getUeIdFromCode(ueCode: string): Promise { - return (await this.prisma.withDefaultBehaviour.ue.findUnique({ where: { code: ueCode }, select: { id: true } })).id; - } } From 856a26b955295784e22ad067ba3706ad5c70e257 Mon Sep 17 00:00:00 2001 From: AlbanSdl Date: Sun, 28 Jul 2024 03:14:26 +0200 Subject: [PATCH 05/14] feat(ue): add support for ue aliases --- src/ue/ue.controller.ts | 13 ++++++++++--- src/ue/ue.service.ts | 8 ++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/ue/ue.controller.ts b/src/ue/ue.controller.ts index 4999786..76edad4 100644 --- a/src/ue/ue.controller.ts +++ b/src/ue/ue.controller.ts @@ -1,4 +1,6 @@ -import { Body, Controller, Delete, Get, Headers, Param, Put, Query } from '@nestjs/common'; +import { Body, Controller, Delete, Get, Headers, Param, Put, Query, Res } from '@nestjs/common'; +import { HttpStatusCode } from 'axios'; +import type { Response } from 'express'; import { UeSearchDto } from './dto/ue-search.dto'; import { UeService } from './ue.service'; import { GetUser, IsPublic, RequireUserType } from '../auth/decorator'; @@ -37,8 +39,13 @@ export class UeController { @Get('/:ueCode') @IsPublic() - async getUe(@GetUser() user: User, @Param('ueCode') ueCode: string): Promise> { - if (!(await this.ueService.doesUeExist(ueCode))) throw new AppException(ERROR_CODE.NO_SUCH_UE, ueCode); + async getUe(@GetUser() user: User, @Param('ueCode') ueCode: string, @Res() res: Response): Promise { + if (!(await this.ueService.doesUeExist(ueCode))) { + // Check for aliases or throw an error + const alias = await this.ueService.findAlias(ueCode); + if (alias?.standsFor) return res.redirect(HttpStatusCode.MovedPermanently, `/ue/${alias.standsFor}`); + throw new AppException(ERROR_CODE.NO_SUCH_UE, ueCode); + } const result = this.formatDetailedUe(await this.ueService.getUe(ueCode.toUpperCase())); if (user.userType === UserType.STUDENT || user.userType === UserType.FORMER_STUDENT) return result; return omit(result, 'starVotes'); diff --git a/src/ue/ue.service.ts b/src/ue/ue.service.ts index 4b1633e..d4bd317 100644 --- a/src/ue/ue.service.ts +++ b/src/ue/ue.service.ts @@ -192,6 +192,14 @@ export class UeService { })); } + async findAlias(aliasCode: string) { + return this.prisma.ueAlias.findUnique({ + where: { + code: aliasCode, + }, + }); + } + /** * Checks whether a user has already done an ue * @remarks The user must not be null From 689d0eee60b6cea27a09e93e9e0a630b2a504934 Mon Sep 17 00:00:00 2001 From: AlbanSdl Date: Sun, 28 Jul 2024 17:09:58 +0200 Subject: [PATCH 06/14] fix(ue): update typings part 2 --- migration/etuutt_old/modules/user.ts | 2 +- prisma/seed/modules/ue.seed.ts | 85 ++++++----- prisma/seed/modules/ueComment.seed.ts | 8 +- prisma/seed/modules/ueStarVotes.seed.ts | 3 +- prisma/seed/modules/ueSubscription.seed.ts | 3 +- src/ue/ue.controller.ts | 4 +- test/declarations.ts | 62 ++++---- test/utils/fakedb.ts | 161 +++++++++++---------- 8 files changed, 181 insertions(+), 147 deletions(-) diff --git a/migration/etuutt_old/modules/user.ts b/migration/etuutt_old/modules/user.ts index 1cb6211..4450823 100644 --- a/migration/etuutt_old/modules/user.ts +++ b/migration/etuutt_old/modules/user.ts @@ -89,7 +89,7 @@ export async function migrateUsers( UesSubscriptions: { createMany: { data: user.uvs.split('|').map((code) => ({ - ueId: ues.find((ue) => ue.code === code).id, + ueId: ues.find((ue) => ue.code === code).code, semesterId: currentSemester.code, })), }, diff --git a/prisma/seed/modules/ue.seed.ts b/prisma/seed/modules/ue.seed.ts index 1bf3f08..250b111 100644 --- a/prisma/seed/modules/ue.seed.ts +++ b/prisma/seed/modules/ue.seed.ts @@ -5,6 +5,8 @@ import { generateTranslation } from '../utils'; const FAKER_ROUNDS = 20; +export const OF_SUFFIX = '_FR_TRO_U23'; + export default function ueSeed( prisma: PrismaClient, semesters: RawSemester[], @@ -22,49 +24,54 @@ export default function ueSeed( prisma.ue.create({ data: { code, - inscriptionCode: code.length === 3 ? `${code}X` : code.length === 4 ? code : code.substring(0, 4), - name: generateTranslation(faker.name.jobTitle), - validationRate: faker.datatype.number({ min: 0, max: 100 }), - createdAt: date, - updatedAt: date, - info: { + creationYear: faker.datatype.number({ min: 2000, max: 2024 }), + updateYear: faker.datatype.number({ min: 2001, max: 2024 }), + ueofs: { create: { - comment: generateTranslation(), - program: generateTranslation(), - objectives: generateTranslation(), - languages: faker.random.word(), - }, - }, - credits: { - create: [ - { - category: { - connect: { - code: faker.helpers.arrayElement(creditCategory).code, + code: `${code}${OF_SUFFIX}`, + siepId: faker.datatype.number({ min: 100000, max: 999999 }), + name: generateTranslation(faker.name.jobTitle), + createdAt: date, + updatedAt: date, + info: { + create: { + program: generateTranslation(), + objectives: generateTranslation(), + language: faker.random.word(), + }, + }, + credits: { + create: [ + { + category: { + connect: { + code: faker.helpers.arrayElement(creditCategory).code, + }, + }, + credits: faker.datatype.number({ min: 1, max: 6 }), }, + ], + }, + openSemester: { + connect: faker.helpers + .arrayElements(semesters, faker.datatype.number({ min: 1, max: 50 })) + .map((semester) => ({ code: semester.code })), + }, + branchOption: { + connect: faker.helpers + .arrayElements(branchOptions, faker.datatype.number({ min: 1, max: 3 })) + .map((branchOption) => ({ code: branchOption.code })), + }, + workTime: { + create: { + cm: faker.datatype.number({ min: 6, max: 32 }), + td: faker.datatype.number({ min: 6, max: 32 }), + tp: faker.datatype.number({ min: 6, max: 16 }), + the: faker.datatype.number({ min: 32, max: 64 }), + project: faker.datatype.boolean(), + internship: 0, }, - credits: faker.datatype.number({ min: 1, max: 6 }), }, - ], - }, - openSemester: { - connect: faker.helpers - .arrayElements(semesters, faker.datatype.number({ min: 1, max: 50 })) - .map((semester) => ({ code: semester.code })), - }, - branchOption: { - connect: faker.helpers - .arrayElements(branchOptions, faker.datatype.number({ min: 1, max: 3 })) - .map((branchOption) => ({ id: branchOption.id })), - }, - workTime: { - create: { - cm: faker.datatype.number({ min: 6, max: 32 }), - td: faker.datatype.number({ min: 6, max: 32 }), - tp: faker.datatype.number({ min: 6, max: 16 }), - the: faker.datatype.number({ min: 32, max: 64 }), - project: faker.datatype.number({ min: 16, max: 48 }), - internship: 0, }, }, }, diff --git a/prisma/seed/modules/ueComment.seed.ts b/prisma/seed/modules/ueComment.seed.ts index 3c6dcf7..1a106f4 100644 --- a/prisma/seed/modules/ueComment.seed.ts +++ b/prisma/seed/modules/ueComment.seed.ts @@ -1,6 +1,7 @@ import { RawSemester, RawUe, RawUeComment, RawUser, RawUserUeSubscription } from '../../../src/prisma/types'; import { Prisma, PrismaClient } from '@prisma/client'; import { faker } from '@faker-js/faker'; +import { OF_SUFFIX } from './ue.seed'; const FAKER_ROUNDS = 100; @@ -40,7 +41,12 @@ export default function ueCommentSeed( }, ue: { connect: { - id: subscription.ueId, + code: subscription.ueofId.slice(0, -OF_SUFFIX.length), + }, + }, + ueof: { + connect: { + code: subscription.ueofId, }, }, semester: { diff --git a/prisma/seed/modules/ueStarVotes.seed.ts b/prisma/seed/modules/ueStarVotes.seed.ts index 84db589..28e577b 100644 --- a/prisma/seed/modules/ueStarVotes.seed.ts +++ b/prisma/seed/modules/ueStarVotes.seed.ts @@ -1,4 +1,5 @@ import { RawUeStarVote, RawUeStarCriterion, RawUserUeSubscription } from '../../../src/prisma/types'; +import { OF_SUFFIX } from './ue.seed'; import { PrismaClient } from '@prisma/client'; import { faker } from '@faker-js/faker'; @@ -17,7 +18,7 @@ export default function ueStarVotesSeed( prisma.ueStarVote.create({ data: { user: { connect: { id: subscription.userId } }, - ue: { connect: { id: subscription.ueId } }, + ue: { connect: { code: subscription.ueofId.slice(0, -OF_SUFFIX.length) } }, criterion: { connect: { id: criterion.id } }, value: faker.datatype.number({ min: 1, max: 5 }), }, diff --git a/prisma/seed/modules/ueSubscription.seed.ts b/prisma/seed/modules/ueSubscription.seed.ts index 5190257..97108a4 100644 --- a/prisma/seed/modules/ueSubscription.seed.ts +++ b/prisma/seed/modules/ueSubscription.seed.ts @@ -1,6 +1,7 @@ import { RawSemester, RawUe, RawUser, RawUserUeSubscription } from '../../../src/prisma/types'; import { PrismaClient } from '@prisma/client'; import { faker } from '@faker-js/faker'; +import { OF_SUFFIX } from './ue.seed'; export default function ueSubscriptionSeed( prisma: PrismaClient, @@ -16,7 +17,7 @@ export default function ueSubscriptionSeed( subscriptions.push( prisma.userUeSubscription.create({ data: { - ue: { connect: { id: ue.id } }, + ueof: { connect: { code: `${ue.code}$${OF_SUFFIX}` } }, user: { connect: { id: user.id } }, semester: { connect: { code: faker.helpers.arrayElement(semesters).code } }, }, diff --git a/src/ue/ue.controller.ts b/src/ue/ue.controller.ts index 76edad4..4c6a9e2 100644 --- a/src/ue/ue.controller.ts +++ b/src/ue/ue.controller.ts @@ -11,7 +11,7 @@ import { UeRateDto } from './dto/ue-rate.dto'; import { Ue } from './interfaces/ue.interface'; import { Language, UserType } from '@prisma/client'; import { Translation } from '../prisma/types'; -import { omit } from 'src/utils'; +import { omit } from '../utils'; @Controller('ue') export class UeController { @@ -43,7 +43,7 @@ export class UeController { if (!(await this.ueService.doesUeExist(ueCode))) { // Check for aliases or throw an error const alias = await this.ueService.findAlias(ueCode); - if (alias?.standsFor) return res.redirect(HttpStatusCode.MovedPermanently, `/ue/${alias.standsFor}`); + if (alias?.standsFor) return res.redirect(HttpStatusCode.MovedPermanently, `./${alias.standsFor}`); throw new AppException(ERROR_CODE.NO_SUCH_UE, ueCode); } const result = this.formatDetailedUe(await this.ueService.getUe(ueCode.toUpperCase())); diff --git a/test/declarations.ts b/test/declarations.ts index 075f9fd..7ec8a12 100644 --- a/test/declarations.ts +++ b/test/declarations.ts @@ -34,19 +34,18 @@ export function deepDateToString(obj: T): JsonLikeVariant { function ueOverviewExpectation(ue: FakeUe, spec: Spec) { return { - ...omit(ue, 'id', 'validationRate', 'createdAt', 'updatedAt', 'openSemesters', 'workTime'), + code: ue.code, name: getTranslation(ue.name, spec.language), - info: { - ...omit(ue.info, 'id', 'comment', 'program', 'objectives'), - comment: getTranslation(ue.info.comment, spec.language), - program: getTranslation(ue.info.program, spec.language), - objectives: getTranslation(ue.info.objectives, spec.language), - }, - credits: ue.credits.map((credit) => omit(credit, 'id', 'ueId', 'categoryId')), + credits: ue.credits.map((credit) => omit(credit, 'id', 'ueofId', 'categoryId')), branchOption: ue.branchOption.map((branchOption) => ({ ...pick(branchOption, 'code', 'name'), branch: pick(branchOption.branch, 'code', 'name'), })), + info: { + ...omit(ue.info, 'id', 'program', 'objectives'), + program: getTranslation(ue.info.program, spec.language), + objectives: getTranslation(ue.info.objectives, spec.language), + }, openSemester: ue.openSemesters.map((semester) => ({ ...semester, start: semester.start.toISOString(), @@ -72,28 +71,33 @@ Spec.prototype.expectAppError = function ( Spec.prototype.expectUe = function (ue: FakeUe, rates: Array<{ criterionId: string; value: number }> = []) { return (this).expectStatus(HttpStatus.OK).expectJsonMatchStrict( deepDateToString({ - ...omit(ue, 'id', 'validationRate', 'createdAt', 'updatedAt', 'openSemesters'), - name: getTranslation(ue.name, this.language), - info: { - ...omit(ue.info, 'id'), - objectives: getTranslation(ue.info.objectives, this.language), - comment: getTranslation(ue.info.comment, this.language), - program: getTranslation(ue.info.program, this.language), - }, - workTime: omit(ue.workTime, 'id', 'ueId'), - credits: ue.credits.map((credit) => omit(credit, 'id', 'ueId', 'categoryId')), - branchOption: ue.branchOption.map((branchOption) => ({ - ...pick(branchOption, 'code', 'name'), - branch: pick(branchOption.branch, 'code', 'name'), - })), - openSemester: ue.openSemesters - .mappedSort((semester) => semester.start.toISOString()) - .map((semester) => ({ - ...semester, - start: semester.start.toISOString(), - end: semester.end.toISOString(), - })), + code: ue.code, + creationYear: ue.creationYear, + updateYear: ue.updateYear, starVotes: Object.fromEntries(rates.map((rate) => [rate.criterionId, rate.value])), + ofs: [ + { + name: getTranslation(ue.name, this.language), + credits: ue.credits.map((credit) => omit(credit, 'id', 'ueofId', 'categoryId')), + branchOption: ue.branchOption.map((branchOption) => ({ + ...pick(branchOption, 'code', 'name'), + branch: pick(branchOption.branch, 'code', 'name'), + })), + info: { + ...omit(ue.info, 'id'), + objectives: getTranslation(ue.info.objectives, this.language), + program: getTranslation(ue.info.program, this.language), + }, + openSemester: ue.openSemesters + .mappedSort((semester) => semester.start.toISOString()) + .map((semester) => ({ + ...semester, + start: semester.start.toISOString(), + end: semester.end.toISOString(), + })), + workTime: omit(ue.workTime, 'id', 'ueofId'), + }, + ], }), ); }; diff --git a/test/utils/fakedb.ts b/test/utils/fakedb.ts index 6e13b0c..8f6a47b 100644 --- a/test/utils/fakedb.ts +++ b/test/utils/fakedb.ts @@ -79,11 +79,12 @@ export type FakeAsso = Partial< >; export type FakeSemester = Partial; export type FakeUe = Partial> & { + ueofCode?: string; name?: Partial; + siepId?: number; credits?: (Partial & { category: RawCreditCategory })[]; info?: Partial< - Omit & { - comment: Partial; + Omit & { objectives: Partial; program: Partial; requirements: { code: string }[]; @@ -317,7 +318,7 @@ export const createUser = entityFaker( semesterNumber: branch.semesterNumber, semesterCode: branch.semester.code, branchCode: branch.branch.code, - branchOptionId: branch.branchOption.id, + branchOptionId: branch.branchOption.code, })), }, }, @@ -660,7 +661,8 @@ export const createAnnal = entityFaker( semesterId: semester.code, senderId: sender.id, typeId: type.id, - ueId: ue.id, + ueId: ue.code, + ueofId: ue.ueofCode, }, }), ); @@ -670,6 +672,9 @@ export const createUe = entityFaker( 'ue', { code: faker.db.ue.code, + siepId: () => faker.datatype.number({ min: 100000, max: 999999 }), + creationYear: () => faker.datatype.number({ min: 2000, max: 2024 }), + updateYear: () => faker.datatype.number({ min: 2001, max: 2024 }), name: () => faker.db.translation(faker.name.jobTitle), credits: [ { @@ -689,7 +694,7 @@ export const createUe = entityFaker( td: () => faker.datatype.number({ min: 0, max: 100 }), tp: () => faker.datatype.number({ min: 0, max: 100 }), the: () => faker.datatype.number({ min: 0, max: 100 }), - project: () => faker.datatype.number({ min: 0, max: 100 }), + project: () => faker.datatype.boolean(), internship: () => faker.datatype.number({ min: 0, max: 100 }), }, branchOption: [], @@ -700,94 +705,99 @@ export const createUe = entityFaker( .get(PrismaService) .withDefaultBehaviour.ue.create({ data: { - ...omit(params, 'name', 'credits', 'info', 'workTime', 'inscriptionCode', 'openSemesters'), - name: { + ...omit(params, 'siepId', 'branchOption', 'name', 'credits', 'info', 'workTime', 'openSemesters'), + ueofs: { create: { - fr: 'TODO : implement this value', - ...params.name, - }, - }, - inscriptionCode: params.inscriptionCode ?? params.code, - credits: { - create: params.credits.map((credit) => ({ - category: { - connectOrCreate: { - create: credit.category, - where: { code: credit.category.code }, - }, - }, - credits: credit.credits, - })), - }, - info: { - create: { - ...omit(params.info, 'requirements', 'comment', 'objectives', 'program'), - comment: { + code: params.ueofCode || `${params.code}_FR_TRO_U23`, + siepId: params.siepId, + available: true, + name: { create: { fr: 'TODO : implement this value', - ...params.info.comment, + ...params.name, }, }, - objectives: { - create: { - fr: 'TODO : implement this value', - ...params.info.objectives, - }, + credits: { + create: params.credits.map((credit) => ({ + category: { + connectOrCreate: { + create: credit.category, + where: { code: credit.category.code }, + }, + }, + credits: credit.credits, + })), }, - program: { + info: { create: { - fr: 'TODO : implement this value', - ...params.info.program, + ...omit(params.info, 'objectives', 'program'), + objectives: { + create: { + fr: 'TODO : implement this value', + ...params.info.objectives, + }, + }, + program: { + create: { + fr: 'TODO : implement this value', + ...params.info.program, + }, + }, }, }, + workTime: { + create: params.workTime, + }, + branchOption: { + connect: params.branchOption.map((branchOption) => ({ + code: branchOption.code, + })), + }, + openSemester: { + connect: params.openSemesters.map((semester) => ({ + code: semester.code, + })), + }, }, }, - workTime: { - create: params.workTime, - }, - branchOption: { - connect: params.branchOption.map((branchOption) => ({ - id: branchOption.id, - })), - }, - openSemester: { - connect: params.openSemesters.map((semester) => ({ - code: semester.code, - })), - }, }, include: { - name: translationSelect, - info: { + ueofs: { include: { + name: translationSelect, requirements: { select: { code: true, }, }, - comment: translationSelect, - objectives: translationSelect, - program: translationSelect, - }, - }, - workTime: true, - credits: { - include: { - category: true, - }, - }, - openSemester: true, - branchOption: { - include: { - branch: true, + info: { + include: { + objectives: translationSelect, + program: translationSelect, + }, + }, + workTime: true, + credits: { + include: { + category: true, + }, + }, + openSemester: true, + branchOption: { + include: { + branch: true, + }, + }, }, }, }, }) .then((ue) => ({ - ...omit(ue, 'openSemester', 'ueInfoId', 'nameTranslationId'), - info: omit(ue.info, 'commentTranslationId', 'objectivesTranslationId', 'programTranslationId'), - openSemesters: ue.openSemester, + ...omit(ue, 'ueofs'), + ueofCode: ue.ueofs[0].code, + ...omit(ue.ueofs[0], 'code', 'ueId', 'ueInfoId', 'openSemester', 'nameTranslationId'), + info: omit(ue.ueofs[0].info, 'objectivesTranslationId', 'programTranslationId'), + openSemesters: ue.ueofs[0].openSemester, })), ); @@ -797,15 +807,15 @@ export const createUeSubscription = entityFaker('userUeSubscription', {}, async .get(PrismaService) .userUeSubscription.create({ data: { - ...omit(params, 'semesterId', 'ueId', 'userId'), + ...omit(params, 'semesterId', 'ueofId', 'userId'), semester: { connect: { code: dependencies.semester.code, }, }, - ue: { + ueof: { connect: { - code: dependencies.ue.code, + code: dependencies.ue.ueofCode, }, }, user: { @@ -884,7 +894,7 @@ export const createComment = entityFaker( .get(PrismaService) .withDefaultBehaviour.ueComment.create({ data: { - ...omit(params, 'ueId', 'authorId', 'semesterId', 'status'), + ...omit(params, 'ueId', 'ueofId', 'authorId', 'semesterId', 'status'), validatedAt: params.status & CommentStatus.VALIDATED ? new Date() : undefined, deletedAt: params.status & CommentStatus.DELETED ? new Date() : undefined, ue: { @@ -892,6 +902,11 @@ export const createComment = entityFaker( code: dependencies.ue.code, }, }, + ueof: { + connect: { + code: dependencies.ue.ueofCode, + }, + }, author: { connect: { id: dependencies.user.id, From 3dcb22ba2bad25cdc7ca466cc2242ae6f71e565f Mon Sep 17 00:00:00 2001 From: AlbanSdl Date: Sun, 28 Jul 2024 20:58:54 +0200 Subject: [PATCH 07/14] fix(ue): add branchOption to UeCredit entries --- prisma/schema.prisma | 16 ++-- prisma/seed/modules/ue.seed.ts | 10 +-- scripts/seed/ue.ts | 87 +++++++++++++------ src/ue/interfaces/ue.interface.ts | 14 +-- src/ue/ue.controller.ts | 53 +++++------ src/ue/ue.service.ts | 20 +++-- test/declarations.ts | 20 +++-- test/e2e/auth/cas-sign-up.e2e-spec.ts | 2 +- test/e2e/ue/annals/delete-annal.e2e-spec.ts | 2 +- test/e2e/ue/annals/get-annal-file.e2e-spec.ts | 2 +- .../ue/annals/get-annal-metadata.e2e-spec.ts | 2 +- test/e2e/ue/annals/get-annals.e2e-spec.ts | 2 +- test/e2e/ue/annals/patch-annal.e2e-spec.ts | 2 +- test/e2e/ue/annals/upload-annal.e2e-spec.ts | 2 +- .../ue/comments/delete-comment.e2e-spec.ts | 2 +- test/e2e/ue/comments/delete-reply.e2e-spec.ts | 2 +- .../e2e/ue/comments/delete-upvote.e2e-spec.ts | 2 +- .../comments/get-comment-from-id.e2e-spec.ts | 4 +- test/e2e/ue/comments/get-comment.e2e-spec.ts | 2 +- test/e2e/ue/comments/post-comment.e2e-spec.ts | 2 +- test/e2e/ue/comments/post-reply.e2e-spec.ts | 2 +- test/e2e/ue/comments/post-upvote.e2e-spec.ts | 2 +- .../ue/comments/update-comment.e2e-spec.ts | 2 +- test/e2e/ue/comments/update-reply.e2e-spec.ts | 2 +- test/e2e/ue/delete-rate.e2e-spec.ts | 2 +- test/e2e/ue/get-my-ues.e2e-spec.ts | 17 +++- test/e2e/ue/get-ue-rate.e2e-spec.ts | 2 +- test/e2e/ue/get.e2e-spec.ts | 42 +++++---- test/e2e/ue/put-rate.e2e-spec.ts | 2 +- test/e2e/ue/search.e2e-spec.ts | 37 ++++---- test/utils/fakedb.ts | 36 ++++---- 31 files changed, 230 insertions(+), 164 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index a2d6515..56e754c 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -224,8 +224,8 @@ model Ue { } model UeAlias { - code String @id @db.VarChar(8) - standsFor String? @db.VarChar(8) + code String @id @db.VarChar(8) + standsFor String? @db.VarChar(8) alias Ue? @relation(fields: [standsFor], references: [code]) } @@ -242,7 +242,6 @@ model Ueof { ue Ue @relation(fields: [ueId], references: [code]) credits UeCredit[] - branchOption UTTBranchOption[] name Translation @relation(fields: [nameTranslationId], references: [id], onDelete: Cascade) info UeInfo @relation(fields: [ueInfoId], references: [id], onDelete: Cascade) requirements Ue[] @relation(name: "ueRequirements") @@ -444,10 +443,11 @@ model UeCourseExchangeReply { } model UeCredit { - id String @id @default(uuid()) - credits Int @db.SmallInt - ueofId String - categoryId String? + id String @id @default(uuid()) + credits Int @db.SmallInt + ueofId String + categoryId String? + branchOptions UTTBranchOption[] ueof Ueof @relation(fields: [ueofId], references: [code], onDelete: Cascade) category UeCreditCategory? @relation(fields: [categoryId], references: [code]) @@ -809,7 +809,7 @@ model UTTBranchOption { branch UTTBranch @relation(fields: [branchCode], references: [code]) descriptionTranslation Translation @relation(fields: [descriptionTranslationId], references: [id], onDelete: Cascade) - ueofs Ueof[] + uecredits UeCredit[] branchSubscriptions UserBranchSubscription[] @@unique([code, branchCode]) diff --git a/prisma/seed/modules/ue.seed.ts b/prisma/seed/modules/ue.seed.ts index 250b111..cde4ad5 100644 --- a/prisma/seed/modules/ue.seed.ts +++ b/prisma/seed/modules/ue.seed.ts @@ -49,6 +49,11 @@ export default function ueSeed( }, }, credits: faker.datatype.number({ min: 1, max: 6 }), + branchOptions: { + connect: faker.helpers + .arrayElements(branchOptions, faker.datatype.number({ min: 1, max: 3 })) + .map((branchOption) => ({ code: branchOption.code })), + }, }, ], }, @@ -57,11 +62,6 @@ export default function ueSeed( .arrayElements(semesters, faker.datatype.number({ min: 1, max: 50 })) .map((semester) => ({ code: semester.code })), }, - branchOption: { - connect: faker.helpers - .arrayElements(branchOptions, faker.datatype.number({ min: 1, max: 3 })) - .map((branchOption) => ({ code: branchOption.code })), - }, workTime: { create: { cm: faker.datatype.number({ min: 6, max: 32 }), diff --git a/scripts/seed/ue.ts b/scripts/seed/ue.ts index d96ba8d..ab69c0e 100644 --- a/scripts/seed/ue.ts +++ b/scripts/seed/ue.ts @@ -141,7 +141,44 @@ async function main() { }); console.info('\x1b[42;30mUpdating UEs\x1b[0m'); + const branches = await prisma.uTTBranch.findMany({ + select: { + isMaster: true, + code: true, + }, + }); for (const ue of ues) { + // Generating ue credits (includes branch and branch options) + const credits: { category: string; credits: number; branchOptions: string[] }[] = []; + if (ue.engineer_credit_type) + credits.push({ + category: ue.engineer_credit_type, + credits: ue.credit_count, + branchOptions: ue.engineer_branch_option.length + ? ue.engineer_branch_option + : ue.engineer_branch.length + ? ue.engineer_branch + : branches.filter((branch) => !branch.isMaster).map((branch) => branch.code), + }); + if (ue.master_credit_type) + credits.push({ + category: ue.master_credit_type, + credits: ue.credit_count, + branchOptions: ue.master_branch_option.length + ? ue.master_branch_option + : ue.master_branch.length + ? ue.master_branch + : branches.filter((branch) => branch.isMaster).map((branch) => branch.code), + }); + if (ue.code === 'PE00') + credits.push( + ...['CS', 'TM', 'EC', 'ME', 'HT', 'AC'].map((code) => ({ + category: code, + credits: ue.credit_count, + branchOptions: branches.filter((branch) => !branch.isMaster).map((branch) => branch.code), + })), + ); + // Update database ue data await prisma.ueof.upsert({ where: { code: ue.ueof_code, @@ -192,19 +229,18 @@ async function main() { }, }, credits: { - create: (ue.code === 'PE00' - ? ['CS', 'TM', 'EC', 'ME', 'HT', 'AC'] - : [...new Set([ue.engineer_credit_type, ue.master_credit_type])] - ) - .filter((type) => type) - .map((type) => ({ credits: ue.credit_count, categoryId: type })), - }, - branchOption: { - connect: [ - ...(ue.engineer_branch_option.length ? ue.engineer_branch_option : ue.engineer_branch), - ...(ue.master_branch_option.length ? ue.master_branch_option : ue.master_branch), - ].map((code) => ({ - code, + create: credits.map((credit) => ({ + credits: credit.credits, + category: { + connect: { + code: credit.category, + }, + }, + branchOptions: { + connect: credit.branchOptions.map((code) => ({ + code: code, + })), + }, })), }, openSemester: { @@ -249,19 +285,18 @@ async function main() { }, credits: { deleteMany: {}, - create: (ue.code === 'PE00' - ? ['CS', 'TM', 'EC', 'ME', 'HT', 'AC'] - : [...new Set([ue.engineer_credit_type, ue.master_credit_type])] - ) - .filter((type) => type) - .map((type) => ({ categoryId: type, credits: ue.credit_count })), - }, - branchOption: { - set: [ - ...(ue.engineer_branch_option.length ? ue.engineer_branch_option : ue.engineer_branch), - ...(ue.master_branch_option.length ? ue.master_branch_option : ue.master_branch), - ].map((code) => ({ - code, + create: credits.map((credit) => ({ + credits: credit.credits, + category: { + connect: { + code: credit.category, + }, + }, + branchOptions: { + connect: credit.branchOptions.map((code) => ({ + code: code, + })), + }, })), }, openSemester: { diff --git a/src/ue/interfaces/ue.interface.ts b/src/ue/interfaces/ue.interface.ts index d217f4b..3cb9631 100644 --- a/src/ue/interfaces/ue.interface.ts +++ b/src/ue/interfaces/ue.interface.ts @@ -57,16 +57,16 @@ const UE_SELECT_FILTER = { name: true, }, }, - }, - }, - branchOption: { - select: { - code: true, - name: true, - branch: { + branchOptions: { select: { code: true, name: true, + branch: { + select: { + code: true, + name: true, + }, + }, }, }, }, diff --git a/src/ue/ue.controller.ts b/src/ue/ue.controller.ts index 4c6a9e2..c6c63f9 100644 --- a/src/ue/ue.controller.ts +++ b/src/ue/ue.controller.ts @@ -104,7 +104,9 @@ export class UeController { private formatUeOverview(ue: Ue, langPref: string[], branchOptionPref: string[]): UeOverview { const lowerCasePref = langPref.map((lang) => lang.toLocaleLowerCase()); const availableOf = ue.ueofs.filter((ueof) => - branchOptionPref.some((optionPref) => ueof.branchOption.some((option) => option.code === optionPref)), + branchOptionPref.some((optionPref) => + ueof.credits.some((credit) => credit.branchOptions.some((option) => option.code === optionPref)), + ), ); const chosenOf = availableOf.length ? availableOf.find((ueof) => lowerCasePref.includes(ueof.info.language)) @@ -118,14 +120,7 @@ export class UeController { code: c.category.code, name: c.category.name, }, - })), - branchOption: chosenOf.branchOption.map((branchOption) => ({ - code: branchOption.code, - name: branchOption.name, - branch: { - code: branchOption.branch.code, - name: branchOption.branch.name, - }, + branchOptions: c.branchOptions, })), info: { requirements: chosenOf.requirements.map((r) => r.code), @@ -158,14 +153,14 @@ export class UeController { code: c.category.code, name: c.category.name, }, - })), - branchOption: ueof.branchOption.map((branchOption) => ({ - code: branchOption.code, - name: branchOption.name, - branch: { - code: branchOption.branch.code, - name: branchOption.branch.name, - }, + branchOptions: c.branchOptions.map((branchOption) => ({ + code: branchOption.code, + name: branchOption.name, + branch: { + code: branchOption.branch.code, + name: branchOption.branch.name, + }, + })), })), info: { requirements: ueof.requirements.map((r) => r.code), @@ -202,14 +197,14 @@ export type UeOverview = { code: string; name: string; }; - }>; - branchOption: Array<{ - branch: { + branchOptions: Array<{ + branch: { + code: string; + name: string; + }; code: string; name: string; - }; - code: string; - name: string; + }>; }>; info: { requirements: string[]; @@ -237,14 +232,14 @@ export type UeDetail = { code: string; name: string; }; - }>; - branchOption: Array<{ - branch: { + branchOptions: Array<{ + branch: { + code: string; + name: string; + }; code: string; name: string; - }; - code: string; - name: string; + }>; }>; info: { requirements: string[]; diff --git a/src/ue/ue.service.ts b/src/ue/ue.service.ts index d4bd317..8af3d35 100644 --- a/src/ue/ue.service.ts +++ b/src/ue/ue.service.ts @@ -79,16 +79,20 @@ export class UeService { ? { ueofs: { some: { - branchOption: { + credits: { some: { - OR: [ - { code: query.branchOption }, - { - branch: { - code: query.branch, - }, + branchOptions: { + some: { + OR: [ + { code: query.branchOption }, + { + branch: { + code: query.branch, + }, + }, + ], }, - ], + }, }, }, }, diff --git a/test/declarations.ts b/test/declarations.ts index 7ec8a12..1d70f57 100644 --- a/test/declarations.ts +++ b/test/declarations.ts @@ -36,10 +36,12 @@ function ueOverviewExpectation(ue: FakeUe, spec: Spec) { return { code: ue.code, name: getTranslation(ue.name, spec.language), - credits: ue.credits.map((credit) => omit(credit, 'id', 'ueofId', 'categoryId')), - branchOption: ue.branchOption.map((branchOption) => ({ - ...pick(branchOption, 'code', 'name'), - branch: pick(branchOption.branch, 'code', 'name'), + credits: ue.credits.map((credit) => ({ + ...omit(credit, 'id', 'ueofId', 'categoryId', 'branchOptions'), + branchOptions: credit.branchOptions.map((branchOption) => ({ + ...pick(branchOption, 'code', 'name'), + branch: pick(branchOption.branch, 'code', 'name'), + })), })), info: { ...omit(ue.info, 'id', 'program', 'objectives'), @@ -78,10 +80,12 @@ Spec.prototype.expectUe = function (ue: FakeUe, rates: Array<{ criterionId: stri ofs: [ { name: getTranslation(ue.name, this.language), - credits: ue.credits.map((credit) => omit(credit, 'id', 'ueofId', 'categoryId')), - branchOption: ue.branchOption.map((branchOption) => ({ - ...pick(branchOption, 'code', 'name'), - branch: pick(branchOption.branch, 'code', 'name'), + credits: ue.credits.map((credit) => ({ + ...omit(credit, 'id', 'ueofId', 'categoryId', 'branchOptions'), + branchOptions: credit.branchOptions.map((branchOption) => ({ + ...pick(branchOption, 'code', 'name'), + branch: pick(branchOption.branch, 'code', 'name'), + })), })), info: { ...omit(ue.info, 'id'), diff --git a/test/e2e/auth/cas-sign-up.e2e-spec.ts b/test/e2e/auth/cas-sign-up.e2e-spec.ts index 189c8f8..1990565 100644 --- a/test/e2e/auth/cas-sign-up.e2e-spec.ts +++ b/test/e2e/auth/cas-sign-up.e2e-spec.ts @@ -34,7 +34,7 @@ const CasSignUpE2ESpec = e2eSuite('/auth/signup/cas', (app) => { start: new Date(), end: new Date(), }); - const ue = fakedb.createUe(app); + const ue = fakedb.createUe(app, { branchOptions: [branchOption] }); beforeAll(() => ldapServer.start()); afterAll(() => ldapServer.stop()); diff --git a/test/e2e/ue/annals/delete-annal.e2e-spec.ts b/test/e2e/ue/annals/delete-annal.e2e-spec.ts index 8261cce..07231c5 100644 --- a/test/e2e/ue/annals/delete-annal.e2e-spec.ts +++ b/test/e2e/ue/annals/delete-annal.e2e-spec.ts @@ -23,7 +23,7 @@ const DeleteAnnal = e2eSuite('DELETE /ue/annals/{annalId}', (app) => { const semester = createSemester(app); const branch = createBranch(app); const branchOption = createBranchOption(app, { branch }); - const ue = createUe(app, { openSemesters: [semester], branchOption: [branchOption] }); + const ue = createUe(app, { branchOptions: [branchOption] }, { openSemesters: [semester] }); createUeSubscription(app, { user: senderUser, ue, semester }); const annal_validated = createAnnal(app, { semester, sender: senderUser, type: annalType, ue }); diff --git a/test/e2e/ue/annals/get-annal-file.e2e-spec.ts b/test/e2e/ue/annals/get-annal-file.e2e-spec.ts index 82f81be..81e1a51 100644 --- a/test/e2e/ue/annals/get-annal-file.e2e-spec.ts +++ b/test/e2e/ue/annals/get-annal-file.e2e-spec.ts @@ -22,7 +22,7 @@ const GetAnnalFile = e2eSuite('GET /ue/annals/{annalId}', (app) => { const semester = createSemester(app); const branch = createBranch(app); const branchOption = createBranchOption(app, { branch }); - const ue = createUe(app, { openSemesters: [semester], branchOption: [branchOption] }); + const ue = createUe(app, { branchOptions: [branchOption] }, { openSemesters: [semester] }); createUeSubscription(app, { user: senderUser, ue, semester }); const annal_not_validated = createAnnal( app, diff --git a/test/e2e/ue/annals/get-annal-metadata.e2e-spec.ts b/test/e2e/ue/annals/get-annal-metadata.e2e-spec.ts index 558d10e..6201439 100644 --- a/test/e2e/ue/annals/get-annal-metadata.e2e-spec.ts +++ b/test/e2e/ue/annals/get-annal-metadata.e2e-spec.ts @@ -19,7 +19,7 @@ const GetAnnalMetadata = e2eSuite('GET /ue/annals/metadata', (app) => { const semester = createSemester(app); const branch = createBranch(app); const branchOption = createBranchOption(app, { branch }); - const ue = createUe(app, { openSemesters: [semester], branchOption: [branchOption] }); + const ue = createUe(app, { branchOptions: [branchOption] }, { openSemesters: [semester] }); createUeSubscription(app, { user: ueUser, ue, semester }); it('should return a 401 as user is not authenticated', () => { diff --git a/test/e2e/ue/annals/get-annals.e2e-spec.ts b/test/e2e/ue/annals/get-annals.e2e-spec.ts index eda55c7..7bd9fb9 100644 --- a/test/e2e/ue/annals/get-annals.e2e-spec.ts +++ b/test/e2e/ue/annals/get-annals.e2e-spec.ts @@ -25,7 +25,7 @@ const GetAnnal = e2eSuite('GET /ue/annals', (app) => { const semester = createSemester(app); const branch = createBranch(app); const branchOption = createBranchOption(app, { branch }); - const ue = createUe(app, { openSemesters: [semester], branchOption: [branchOption] }); + const ue = createUe(app, { branchOptions: [branchOption] }, { openSemesters: [semester] }); createUeSubscription(app, { user: senderUser, ue, semester }); const annal_not_validated = createAnnal( app, diff --git a/test/e2e/ue/annals/patch-annal.e2e-spec.ts b/test/e2e/ue/annals/patch-annal.e2e-spec.ts index 1e88364..3c94f11 100644 --- a/test/e2e/ue/annals/patch-annal.e2e-spec.ts +++ b/test/e2e/ue/annals/patch-annal.e2e-spec.ts @@ -22,7 +22,7 @@ const EditAnnal = e2eSuite('PATCH /ue/annals/{annalId}', (app) => { const semester = createSemester(app); const branch = createBranch(app); const branchOption = createBranchOption(app, { branch }); - const ue = createUe(app, { openSemesters: [semester], branchOption: [branchOption] }); + const ue = createUe(app, { branchOptions: [branchOption] }, { openSemesters: [semester] }); createUeSubscription(app, { user: senderUser, ue, semester }); const annal_validated = createAnnal(app, { semester, sender: senderUser, type: annalType, ue }); const annal_not_uploaded = createAnnal( diff --git a/test/e2e/ue/annals/upload-annal.e2e-spec.ts b/test/e2e/ue/annals/upload-annal.e2e-spec.ts index 518990b..480cac6 100644 --- a/test/e2e/ue/annals/upload-annal.e2e-spec.ts +++ b/test/e2e/ue/annals/upload-annal.e2e-spec.ts @@ -23,7 +23,7 @@ const PostAnnal = e2eSuite('POST-PUT /ue/annals', (app) => { const semester = createSemester(app); const branch = createBranch(app); const branchOption = createBranchOption(app, { branch }); - const ue = createUe(app, { openSemesters: [semester], branchOption: [branchOption] }); + const ue = createUe(app, { branchOptions: [branchOption] }, { openSemesters: [semester] }); createUeSubscription(app, { user: senderUser, ue, semester }); it('should return a 401 as user is not authenticated', () => { diff --git a/test/e2e/ue/comments/delete-comment.e2e-spec.ts b/test/e2e/ue/comments/delete-comment.e2e-spec.ts index 9dfd6f5..30b0829 100644 --- a/test/e2e/ue/comments/delete-comment.e2e-spec.ts +++ b/test/e2e/ue/comments/delete-comment.e2e-spec.ts @@ -19,7 +19,7 @@ const DeleteComment = e2eSuite('DELETE /ue/comments/{commentId}', (app) => { const semester = createSemester(app); const branch = createBranch(app); const branchOption = createBranchOption(app, { branch }); - const ue = createUe(app, { openSemesters: [semester], branchOption: [branchOption] }); + const ue = createUe(app, { branchOptions: [branchOption] }, { openSemesters: [semester] }); const comment1 = createComment(app, { user, ue, semester }); createCommentUpvote(app, { user, comment: comment1 }); diff --git a/test/e2e/ue/comments/delete-reply.e2e-spec.ts b/test/e2e/ue/comments/delete-reply.e2e-spec.ts index c7257f7..50dec1f 100644 --- a/test/e2e/ue/comments/delete-reply.e2e-spec.ts +++ b/test/e2e/ue/comments/delete-reply.e2e-spec.ts @@ -19,7 +19,7 @@ const DeleteCommentReply = e2eSuite('DELETE /ue/comments/reply/{replyId}', (app) const semester = createSemester(app); const branch = createBranch(app); const branchOption = createBranchOption(app, { branch }); - const ue = createUe(app, { openSemesters: [semester], branchOption: [branchOption] }); + const ue = createUe(app, { branchOptions: [branchOption] }, { openSemesters: [semester] }); const comment1 = createComment(app, { user, ue, semester }); const reply = createCommentReply(app, { user, comment: comment1 }); diff --git a/test/e2e/ue/comments/delete-upvote.e2e-spec.ts b/test/e2e/ue/comments/delete-upvote.e2e-spec.ts index 3615c45..cf781f4 100644 --- a/test/e2e/ue/comments/delete-upvote.e2e-spec.ts +++ b/test/e2e/ue/comments/delete-upvote.e2e-spec.ts @@ -19,7 +19,7 @@ const DeleteUpvote = e2eSuite('DELETE /ue/comments/{commentId}/upvote', (app) => const semester = createSemester(app); const branch = createBranch(app); const branchOption = createBranchOption(app, { branch }); - const ue = createUe(app, { openSemesters: [semester], branchOption: [branchOption] }); + const ue = createUe(app, { branchOptions: [branchOption] }, { openSemesters: [semester] }); const comment1 = createComment(app, { user, ue, semester }); const upvote = createCommentUpvote(app, { user: user2, comment: comment1 }); diff --git a/test/e2e/ue/comments/get-comment-from-id.e2e-spec.ts b/test/e2e/ue/comments/get-comment-from-id.e2e-spec.ts index af8a6f9..28af1dc 100644 --- a/test/e2e/ue/comments/get-comment-from-id.e2e-spec.ts +++ b/test/e2e/ue/comments/get-comment-from-id.e2e-spec.ts @@ -10,7 +10,9 @@ const GetCommentFromIdE2ESpec = e2eSuite('GET /ue/comments/:commentId', (app) => const user = fakedb.createUser(app); const user2 = fakedb.createUser(app, { login: 'user2', studentId: 3 }); const semester = fakedb.createSemester(app); - const ue = fakedb.createUe(app, { code: `XX01`, openSemesters: [semester] }); + const branch = fakedb.createBranch(app); + const branchOption = fakedb.createBranchOption(app, { branch }); + const ue = fakedb.createUe(app, { branchOptions: [branchOption] }, { code: `XX01`, openSemesters: [semester] }); const comment = fakedb.createComment(app, { user, ue, semester }); fakedb.createCommentUpvote(app, { user: user2, comment }); const reply = fakedb.createCommentReply(app, { user, comment }, { body: 'HelloWorld' }); diff --git a/test/e2e/ue/comments/get-comment.e2e-spec.ts b/test/e2e/ue/comments/get-comment.e2e-spec.ts index d11d766..690a305 100644 --- a/test/e2e/ue/comments/get-comment.e2e-spec.ts +++ b/test/e2e/ue/comments/get-comment.e2e-spec.ts @@ -22,7 +22,7 @@ const GetCommentsE2ESpec = e2eSuite('GET /ue/comments', (app) => { const semester = createSemester(app); const branch = createBranch(app); const branchOption = createBranchOption(app, { branch }); - const ue = createUe(app, { openSemesters: [semester], branchOption: [branchOption] }); + const ue = createUe(app, { branchOptions: [branchOption] }, { openSemesters: [semester] }); const comments: FakeComment[] = []; comments.push( createComment( diff --git a/test/e2e/ue/comments/post-comment.e2e-spec.ts b/test/e2e/ue/comments/post-comment.e2e-spec.ts index 156d39e..b3e4d7a 100644 --- a/test/e2e/ue/comments/post-comment.e2e-spec.ts +++ b/test/e2e/ue/comments/post-comment.e2e-spec.ts @@ -19,7 +19,7 @@ const PostCommment = e2eSuite('POST /ue/comments', (app) => { const semester = createSemester(app); const branch = createBranch(app); const branchOption = createBranchOption(app, { branch }); - const ue = createUe(app, { openSemesters: [semester], branchOption: [branchOption] }); + const ue = createUe(app, { branchOptions: [branchOption] }, { openSemesters: [semester] }); createUeSubscription(app, { user: user2, ue, semester }); it('should return a 401 as user is not authenticated', () => { diff --git a/test/e2e/ue/comments/post-reply.e2e-spec.ts b/test/e2e/ue/comments/post-reply.e2e-spec.ts index 03e35c7..610a0bc 100644 --- a/test/e2e/ue/comments/post-reply.e2e-spec.ts +++ b/test/e2e/ue/comments/post-reply.e2e-spec.ts @@ -18,7 +18,7 @@ const PostCommmentReply = e2eSuite('POST /ue/comments/{commentId}/reply', (app) const semester = createSemester(app); const branch = createBranch(app); const branchOption = createBranchOption(app, { branch }); - const ue = createUe(app, { openSemesters: [semester], branchOption: [branchOption] }); + const ue = createUe(app, { branchOptions: [branchOption] }, { openSemesters: [semester] }); const comment = createComment(app, { ue, user, semester }); createUeSubscription(app, { user, ue, semester }); diff --git a/test/e2e/ue/comments/post-upvote.e2e-spec.ts b/test/e2e/ue/comments/post-upvote.e2e-spec.ts index 968f018..f1cfc52 100644 --- a/test/e2e/ue/comments/post-upvote.e2e-spec.ts +++ b/test/e2e/ue/comments/post-upvote.e2e-spec.ts @@ -19,7 +19,7 @@ const PostUpvote = e2eSuite('POST /ue/comments/{commentId}/upvote', (app) => { const semester = createSemester(app); const branch = createBranch(app); const branchOption = createBranchOption(app, { branch }); - const ue = createUe(app, { openSemesters: [semester], branchOption: [branchOption] }); + const ue = createUe(app, { branchOptions: [branchOption] }, { openSemesters: [semester] }); const comment = createComment(app, { ue, user, semester }); it('should return a 401 as user is not authenticated', () => { diff --git a/test/e2e/ue/comments/update-comment.e2e-spec.ts b/test/e2e/ue/comments/update-comment.e2e-spec.ts index c0cb281..8871c34 100644 --- a/test/e2e/ue/comments/update-comment.e2e-spec.ts +++ b/test/e2e/ue/comments/update-comment.e2e-spec.ts @@ -19,7 +19,7 @@ const UpdateComment = e2eSuite('PATCH /ue/comments/{commentId}', (app) => { const semester = createSemester(app); const branch = createBranch(app); const branchOption = createBranchOption(app, { branch }); - const ue = createUe(app, { openSemesters: [semester], branchOption: [branchOption] }); + const ue = createUe(app, { branchOptions: [branchOption] }, { openSemesters: [semester] }); const comment = createComment(app, { ue, user, semester }); createCommentUpvote(app, { user: user2, comment }); diff --git a/test/e2e/ue/comments/update-reply.e2e-spec.ts b/test/e2e/ue/comments/update-reply.e2e-spec.ts index 019e1db..93a0e72 100644 --- a/test/e2e/ue/comments/update-reply.e2e-spec.ts +++ b/test/e2e/ue/comments/update-reply.e2e-spec.ts @@ -19,7 +19,7 @@ const UpdateCommentReply = e2eSuite('PATCH /ue/comments/reply/{replyId}', (app) const semester = createSemester(app); const branch = createBranch(app); const branchOption = createBranchOption(app, { branch }); - const ue = createUe(app, { openSemesters: [semester], branchOption: [branchOption] }); + const ue = createUe(app, { branchOptions: [branchOption] }, { openSemesters: [semester] }); const comment = createComment(app, { ue, user, semester }); const reply = createCommentReply(app, { user, comment }); diff --git a/test/e2e/ue/delete-rate.e2e-spec.ts b/test/e2e/ue/delete-rate.e2e-spec.ts index 01bd7c3..03cca7a 100644 --- a/test/e2e/ue/delete-rate.e2e-spec.ts +++ b/test/e2e/ue/delete-rate.e2e-spec.ts @@ -19,7 +19,7 @@ const DeleteRate = e2eSuite('DELETE /ue/{ueCode}/rate/{critetionId}', (app) => { const semester = createSemester(app); const branch = createBranch(app); const branchOption = createBranchOption(app, { branch }); - const ue = createUe(app, { openSemesters: [semester], branchOption: [branchOption] }); + const ue = createUe(app, { branchOptions: [branchOption] }, { openSemesters: [semester] }); const criterion = createCriterion(app); createUeSubscription(app, { user, ue, semester }); const rating = createUeRating(app, { user, ue, criterion }); diff --git a/test/e2e/ue/get-my-ues.e2e-spec.ts b/test/e2e/ue/get-my-ues.e2e-spec.ts index 52cfada..0370fdd 100644 --- a/test/e2e/ue/get-my-ues.e2e-spec.ts +++ b/test/e2e/ue/get-my-ues.e2e-spec.ts @@ -1,24 +1,33 @@ import { e2eSuite } from '../../utils/test_utils'; import * as pactum from 'pactum'; -import { createSemester, createUe, createUeSubscription, createUser } from '../../utils/fakedb'; +import { + createBranch, + createBranchOption, + createSemester, + createUe, + createUeSubscription, + createUser, +} from '../../utils/fakedb'; import { ERROR_CODE } from '../../../src/exceptions'; const GetMyUesE2ESpec = e2eSuite('GET ue/of/me', (app) => { const user = createUser(app); - const ue = createUe(app); + const branch = createBranch(app); + const branchOption = createBranchOption(app, { branch }); + const ue = createUe(app, { branchOptions: [branchOption] }); const semester = createSemester(app, { start: new Date(Date.now() - 30 * 24 * 3_600_000), end: new Date(Date.now() + 30 * 24 * 3_600_000), }); createUeSubscription(app, { user, ue, semester }); - const ue2 = createUe(app); + const ue2 = createUe(app, { branchOptions: [branchOption] }); const semester2 = createSemester(app, { start: new Date(Date.now() - 90 * 24 * 3_600_000), end: new Date(Date.now() - 30 * 24 * 3_600_000), }); createUeSubscription(app, { user, ue: ue2, semester: semester2 }); const user2 = createUser(app); - const ue3 = createUe(app); + const ue3 = createUe(app, { branchOptions: [branchOption] }); createUeSubscription(app, { user: user2, ue: ue3, semester }); const formerStudent = createUser(app, { userType: 'FORMER_STUDENT' }); diff --git a/test/e2e/ue/get-ue-rate.e2e-spec.ts b/test/e2e/ue/get-ue-rate.e2e-spec.ts index fb7b520..8a91f4e 100644 --- a/test/e2e/ue/get-ue-rate.e2e-spec.ts +++ b/test/e2e/ue/get-ue-rate.e2e-spec.ts @@ -17,7 +17,7 @@ const GetRateE2ESpec = e2eSuite('GET /ue/{ueCode}/rate', (app) => { const semester = createSemester(app); const branch = createBranch(app); const branchOption = createBranchOption(app, { branch }); - const ue = createUe(app, { openSemesters: [semester], branchOption: [branchOption] }); + const ue = createUe(app, { branchOptions: [branchOption] }, { openSemesters: [semester] }); const c1 = createCriterion(app); const c2 = createCriterion(app); createUeRating(app, { ue, criterion: c1, user }, { value: 1 }); diff --git a/test/e2e/ue/get.e2e-spec.ts b/test/e2e/ue/get.e2e-spec.ts index 57327ca..49fa5d1 100644 --- a/test/e2e/ue/get.e2e-spec.ts +++ b/test/e2e/ue/get.e2e-spec.ts @@ -40,26 +40,32 @@ const GetE2ESpec = e2eSuite('GET /ue/{ueCode}', (app) => { const ues: FakeUe[] = []; for (let i = 0; i < 30; i++) ues.push( - createUe(app, { - code: `XX${`${i}`.padStart(2, '0')}`, - credits: [ - { - category: { - code: i % 3 == 0 ? 'CS' : 'TM', - name: i % 3 == 0 ? 'CS' : 'TM', + createUe( + app, + { branchOptions: [branchOptions[(i * 3) % 4]] }, + { + code: `XX${`${i}`.padStart(2, '0')}`, + credits: [ + { + category: { + code: i % 3 == 0 ? 'CS' : 'TM', + name: i % 3 == 0 ? 'CS' : 'TM', + }, + credits: 6, }, - credits: 6, - }, - ], - openSemesters: [semesters[i % 2]], - branchOption: [branchOptions[(i * 3) % 4]], - }), + ], + openSemesters: [semesters[i % 2]], + }, + ), ); - const ueWithRating = createUe(app, { - code: `XX30`, - openSemesters: semesters, - branchOption: [branchOptions[0]], - }); + const ueWithRating = createUe( + app, + { branchOptions: [branchOptions[0]] }, + { + code: `XX30`, + openSemesters: semesters, + }, + ); const criterion = createCriterion(app); createUeSubscription(app, { user, ue: ueWithRating, semester: semesters[0] }); createUeSubscription(app, { user: user2, ue: ueWithRating, semester: semesters[0] }); diff --git a/test/e2e/ue/put-rate.e2e-spec.ts b/test/e2e/ue/put-rate.e2e-spec.ts index a7c085a..1867526 100644 --- a/test/e2e/ue/put-rate.e2e-spec.ts +++ b/test/e2e/ue/put-rate.e2e-spec.ts @@ -19,7 +19,7 @@ const PutRate = e2eSuite('PUT /ue/{ueCode}/rate', (app) => { const semester = createSemester(app); const branch = createBranch(app); const branchOption = createBranchOption(app, { branch }); - const ue = createUe(app, { openSemesters: [semester], branchOption: [branchOption] }); + const ue = createUe(app, { branchOptions: [branchOption] }, { openSemesters: [semester] }); const criterion = createCriterion(app); createUeSubscription(app, { user, ue, semester }); diff --git a/test/e2e/ue/search.e2e-spec.ts b/test/e2e/ue/search.e2e-spec.ts index c6c992b..d01f183 100644 --- a/test/e2e/ue/search.e2e-spec.ts +++ b/test/e2e/ue/search.e2e-spec.ts @@ -29,20 +29,23 @@ const SearchE2ESpec = e2eSuite('GET /ue', (app) => { const ues: FakeUe[] = []; for (let i = 0; i < 30; i++) ues.push( - createUe(app, { - code: `XX${`${i}`.padStart(2, '0')}`, - credits: [ - { - category: { - code: i % 3 == 0 ? 'CS' : 'TM', - name: i % 3 == 0 ? 'CS' : 'TM', + createUe( + app, + { branchOptions: [branchOptions[i % 4]] }, + { + code: `XX${`${i}`.padStart(2, '0')}`, + credits: [ + { + category: { + code: i % 3 == 0 ? 'CS' : 'TM', + name: i % 3 == 0 ? 'CS' : 'TM', + }, + credits: 6, }, - credits: 6, - }, - ], - openSemesters: [semesters[i % 2]], - branchOption: [branchOptions[i % 4]], - }), + ], + openSemesters: [semesters[i % 2]], + }, + ), ); it('should return a 400 as semester is in a wrong format', () => { @@ -106,7 +109,9 @@ const SearchE2ESpec = e2eSuite('GET /ue', (app) => { }); it('should return a list of ues filtered by branch option', () => { - const expectedUes = ues.filter((ue) => ue.branchOption.some((branchOption) => branchOption.code === 'T1')); + const expectedUes = ues.filter((ue) => + ue.credits.some((credit) => credit.branchOptions.some((branchOption) => branchOption.code === 'T1')), + ); return pactum .spec() .withBearerToken(user.token) @@ -116,7 +121,9 @@ const SearchE2ESpec = e2eSuite('GET /ue', (app) => { }); it('should return a list of ues filtered by branch', () => { - const expectedUes = ues.filter((ue) => ue.branchOption.some((branchOption) => branchOption.branch.code === 'B1')); + const expectedUes = ues.filter((ue) => + ue.credits.some((credit) => credit.branchOptions.some((branchOption) => branchOption.branch.code === 'B1')), + ); return pactum .spec() .withBearerToken(user.token) diff --git a/test/utils/fakedb.ts b/test/utils/fakedb.ts index 8f6a47b..f654f5c 100644 --- a/test/utils/fakedb.ts +++ b/test/utils/fakedb.ts @@ -82,7 +82,10 @@ export type FakeUe = Partial> & { ueofCode?: string; name?: Partial; siepId?: number; - credits?: (Partial & { category: RawCreditCategory })[]; + credits?: (Partial & { + category: RawCreditCategory; + branchOptions?: Partial[]; + })[]; info?: Partial< Omit & { objectives: Partial; @@ -92,7 +95,6 @@ export type FakeUe = Partial> & { >; workTime?: Partial; openSemesters?: Partial[]; - branchOption?: Partial[]; }; export type FakeUserUeSubscription = Partial; export type FakeUeStarCriterion = Partial; @@ -155,6 +157,7 @@ export interface FakeEntityMap { ue: { entity: FakeUe; params: CreateUeParameters; + deps: { branchOptions: FakeBranchOption[] }; }; userUeSubscription: { entity: FakeUserUeSubscription; @@ -667,7 +670,9 @@ export const createAnnal = entityFaker( }), ); -export type CreateUeParameters = FakeUe; +export type CreateUeParameters = Omit & { + credits: (FakeUe['credits'] extends Array ? Omit : never)[]; +}; export const createUe = entityFaker( 'ue', { @@ -697,15 +702,14 @@ export const createUe = entityFaker( project: () => faker.datatype.boolean(), internship: () => faker.datatype.number({ min: 0, max: 100 }), }, - branchOption: [], openSemesters: [], }, - async (app, params) => + async (app, { branchOptions }, params) => app() .get(PrismaService) .withDefaultBehaviour.ue.create({ data: { - ...omit(params, 'siepId', 'branchOption', 'name', 'credits', 'info', 'workTime', 'openSemesters'), + ...omit(params, 'siepId', 'name', 'credits', 'info', 'workTime', 'openSemesters'), ueofs: { create: { code: params.ueofCode || `${params.code}_FR_TRO_U23`, @@ -726,6 +730,11 @@ export const createUe = entityFaker( }, }, credits: credit.credits, + branchOptions: { + connect: branchOptions.map((branchOption) => ({ + code: branchOption.code, + })), + }, })), }, info: { @@ -748,11 +757,6 @@ export const createUe = entityFaker( workTime: { create: params.workTime, }, - branchOption: { - connect: params.branchOption.map((branchOption) => ({ - code: branchOption.code, - })), - }, openSemester: { connect: params.openSemesters.map((semester) => ({ code: semester.code, @@ -780,14 +784,14 @@ export const createUe = entityFaker( credits: { include: { category: true, + branchOptions: { + include: { + branch: true, + }, + }, }, }, openSemester: true, - branchOption: { - include: { - branch: true, - }, - }, }, }, }, From f9b1478cfc924514b43d5b9e383deafb78d41819 Mon Sep 17 00:00:00 2001 From: AlbanSdl Date: Sun, 8 Sep 2024 23:15:31 +0200 Subject: [PATCH 08/14] fix(tests): tests are now up to date --- prisma/schema.prisma | 6 ---- prisma/seed/modules/ueComment.seed.ts | 9 +---- prisma/seed/seed.ts | 2 +- scripts/seed/ue.ts | 2 +- src/array.ts | 8 +++++ src/exceptions.ts | 25 ++++++++------ src/ue/annals/annals.controller.ts | 4 +-- src/ue/annals/annals.service.ts | 13 +++----- src/ue/annals/interfaces/annal.interface.ts | 3 +- src/ue/comments/comments.service.ts | 17 +++++----- .../comments/interfaces/comment.interface.ts | 10 ++++++ src/ue/dto/ue-search.dto.ts | 2 +- src/ue/ue.controller.ts | 10 ++++-- src/validation.ts | 1 + test/declarations.d.ts | 6 ++-- test/declarations.ts | 33 ++++++++++++++----- test/e2e/ue/annals/delete-annal.e2e-spec.ts | 3 -- test/e2e/ue/annals/get-annals.e2e-spec.ts | 2 +- test/e2e/ue/annals/patch-annal.e2e-spec.ts | 3 -- .../ue/comments/delete-comment.e2e-spec.ts | 1 + .../comments/get-comment-from-id.e2e-spec.ts | 4 +-- test/e2e/ue/comments/get-comment.e2e-spec.ts | 5 +-- test/e2e/ue/comments/post-comment.e2e-spec.ts | 2 ++ .../ue/comments/update-comment.e2e-spec.ts | 2 ++ test/e2e/ue/search.e2e-spec.ts | 2 +- test/utils/fakedb.ts | 15 ++++----- 26 files changed, 107 insertions(+), 83 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 56e754c..8032ce8 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -215,8 +215,6 @@ model Ue { createdAt DateTime @default(now()) creationYear Int updateYear Int - comments UeComment[] - annals UeAnnal[] starVotes UeStarVote[] ueofs Ueof[] subsequentUes Ueof[] @relation("ueRequirements") @@ -261,13 +259,11 @@ model UeAnnal { updatedAt DateTime @updatedAt deletedAt DateTime? validatedAt DateTime? - ueId String ueofId String senderId String? semesterId String typeId String - ue Ue @relation(fields: [ueId], references: [code], onDelete: Cascade) ueof Ueof @relation(fields: [ueofId], references: [code], onDelete: Cascade) sender User? @relation(fields: [senderId], references: [id], onDelete: SetNull) type UeAnnalType @relation(fields: [typeId], references: [id]) @@ -316,12 +312,10 @@ model UeComment { deletedAt DateTime? validatedAt DateTime? lastValidatedBody String? - ueId String ueofId String authorId String? semesterId String - ue Ue @relation(fields: [ueId], references: [code], onDelete: Cascade) ueof Ueof @relation(fields: [ueofId], references: [code], onDelete: Cascade) author User? @relation(fields: [authorId], references: [id], onDelete: SetNull) semester Semester @relation(fields: [semesterId], references: [code]) diff --git a/prisma/seed/modules/ueComment.seed.ts b/prisma/seed/modules/ueComment.seed.ts index 1a106f4..c3d91ba 100644 --- a/prisma/seed/modules/ueComment.seed.ts +++ b/prisma/seed/modules/ueComment.seed.ts @@ -1,14 +1,12 @@ -import { RawSemester, RawUe, RawUeComment, RawUser, RawUserUeSubscription } from '../../../src/prisma/types'; +import { RawSemester, RawUeComment, RawUser, RawUserUeSubscription } from '../../../src/prisma/types'; import { Prisma, PrismaClient } from '@prisma/client'; import { faker } from '@faker-js/faker'; -import { OF_SUFFIX } from './ue.seed'; const FAKER_ROUNDS = 100; export default function ueCommentSeed( prisma: PrismaClient, users: RawUser[], - ues: RawUe[], semester: RawSemester[], ueSubscriptions: RawUserUeSubscription[], ): Promise { @@ -39,11 +37,6 @@ export default function ueCommentSeed( id: subscription.userId, }, }, - ue: { - connect: { - code: subscription.ueofId.slice(0, -OF_SUFFIX.length), - }, - }, ueof: { connect: { code: subscription.ueofId, diff --git a/prisma/seed/seed.ts b/prisma/seed/seed.ts index 130d02d..548e025 100644 --- a/prisma/seed/seed.ts +++ b/prisma/seed/seed.ts @@ -29,7 +29,7 @@ async function main() { const ues = await ueSeed(prisma, semesters, branchOptions, creditCategories); const users = await userSeed(prisma); const ueSubscriptions = await ueSubscriptionSeed(prisma, users, ues, semesters); - await ueCommentSeed(prisma, users, ues, semesters, ueSubscriptions); + await ueCommentSeed(prisma, users, semesters, ueSubscriptions); const ueStarCriterions = await ueStarCriterionSeed(prisma); await ueStarVotesSeed(prisma, ueStarCriterions, ueSubscriptions); const assos = await assoSeed(prisma); diff --git a/scripts/seed/ue.ts b/scripts/seed/ue.ts index ab69c0e..efef3fc 100644 --- a/scripts/seed/ue.ts +++ b/scripts/seed/ue.ts @@ -135,7 +135,7 @@ async function main() { const importYear = new Date().getFullYear(); // Can be changed to the year of the import if done with web interface console.info('\x1b[42;30mFetching UE list\x1b[0m'); - const ues = await parseDocument('scripts/seed/ues.csv'); + const ues = await parseDocument('scripts/seed/Export UE_OF pour UNG 20240727.csv'); await prisma.ueof.updateMany({ data: { available: false }, }); diff --git a/src/array.ts b/src/array.ts index a6948ed..a33b088 100644 --- a/src/array.ts +++ b/src/array.ts @@ -20,6 +20,8 @@ declare global { * The length of the array should be fixed, not dependent on the value to map. */ mappedSort(mapper: (e: T) => any[] | any): this; + /** Retrieves all unique values of this array, skipping all duplicates. Does not alter original array */ + readonly uniqueValues: this; } } @@ -41,4 +43,10 @@ Array.prototype.mappedSort = function (this: Array, mapper: (e: T) => any[ }); }; +Object.defineProperty(Array.prototype, 'uniqueValues', { + get: function (this: Array) { + return this.filter((value, index) => this.indexOf(value) === index); + }, +}); + export {}; diff --git a/src/exceptions.ts b/src/exceptions.ts index 5404e8f..7b48f03 100644 --- a/src/exceptions.ts +++ b/src/exceptions.ts @@ -23,16 +23,17 @@ export const enum ERROR_CODE { PARAM_NOT_ENUM = 2007, PARAM_NOT_DATE = 2008, PARAM_NOT_UUID = 2009, - PARAM_TOO_LONG = 20010, - PARAM_TOO_SHORT = 2011, - PARAM_SIZE_TOO_SMALL = 2012, - PARAM_SIZE_TOO_BIG = 2013, - PARAM_IS_EMPTY = 2014, - PARAM_NOT_POSITIVE = 2015, - PARAM_TOO_LOW = 2016, - PARAM_TOO_HIGH = 2017, - PARAM_NOT_INT = 2018, - NO_FILE_PROVIDED = 2019, + PARAM_INVALID_SIZE = 2010, + PARAM_TOO_LONG = 2011, + PARAM_TOO_SHORT = 2012, + PARAM_SIZE_TOO_SMALL = 2013, + PARAM_SIZE_TOO_BIG = 2014, + PARAM_IS_EMPTY = 2015, + PARAM_NOT_POSITIVE = 2016, + PARAM_TOO_LOW = 2017, + PARAM_TOO_HIGH = 2018, + PARAM_NOT_INT = 2019, + NO_FILE_PROVIDED = 2020, PARAM_DOES_NOT_MATCH_REGEX = 2102, NO_FIELD_PROVIDED = 2201, WIDGET_OVERLAPPING = 2301, @@ -116,6 +117,10 @@ export const ErrorData = Object.freeze({ message: 'The following parameters must be a valid UUID: %', httpCode: HttpStatus.BAD_REQUEST, }, + [ERROR_CODE.PARAM_INVALID_SIZE]: { + message: 'The following parameters do not match required size: %', + httpCode: HttpStatus.BAD_REQUEST, + }, [ERROR_CODE.PARAM_TOO_LONG]: { message: 'The following parameters are too long: %', httpCode: HttpStatus.BAD_REQUEST, diff --git a/src/ue/annals/annals.controller.ts b/src/ue/annals/annals.controller.ts index 9dd60be..61a9b60 100644 --- a/src/ue/annals/annals.controller.ts +++ b/src/ue/annals/annals.controller.ts @@ -32,7 +32,7 @@ export class AnnalsController { if (!(await this.ueService.doesUeExist(ueCode))) throw new AppException(ERROR_CODE.NO_SUCH_UE, ueCode); if (!(await this.annalsService.doesAnnalTypeExist(typeId))) throw new AppException(ERROR_CODE.NO_SUCH_ANNAL_TYPE); try { - return this.annalsService.createAnnalFile(user, { ueCode, semester, typeId }, ueof); + return await this.annalsService.createAnnalFile(user, { ueCode, semester, typeId }, ueof); } catch (error) { if (user.permissions.includes('annalUploader')) throw new AppException(ERROR_CODE.NO_SUCH_UEOF, ueof); throw new AppException(ERROR_CODE.NOT_DONE_UE_IN_SEMESTER, ueCode, semester); @@ -89,7 +89,7 @@ export class AnnalsController { response.setHeader('Content-Type', 'application/pdf'); response.setHeader( 'Content-Disposition', - `attachment; filename=${annalFile.metadata.type.name} ${annalFile.metadata.ue.code} - ${annalFile.metadata.semesterId}`, + `attachment; filename=${annalFile.metadata.type.name} ${annalFile.metadata.ueof.code} - ${annalFile.metadata.semesterId}`, ); annalFile.stream.pipe(response); } diff --git a/src/ue/annals/annals.service.ts b/src/ue/annals/annals.service.ts index 494050e..c356b09 100644 --- a/src/ue/annals/annals.service.ts +++ b/src/ue/annals/annals.service.ts @@ -84,11 +84,6 @@ export class AnnalsService { id: user.id, }, }, - ue: { - connect: { - code: params.ueCode, - }, - }, ueof: { connect: { code: subscription.ueofId ?? ueof, @@ -151,7 +146,7 @@ export class AnnalsService { size, compress: true, info: { - Title: `${fileEntry.type.name} ${fileEntry.ue.code} - ${fileEntry.semesterId}`, + Title: `${fileEntry.type.name} ${fileEntry.ueof.code} - ${fileEntry.semesterId}`, Creator: 'EtuUTT', Producer: 'EtuUTT', }, @@ -187,8 +182,10 @@ export class AnnalsService { return ( await this.prisma.ueAnnal.findMany({ where: { - ue: { - code: ueCode, + ueof: { + ue: { + code: ueCode, + }, }, deletedAt: includeAll ? undefined : null, ...(includeAll diff --git a/src/ue/annals/interfaces/annal.interface.ts b/src/ue/annals/interfaces/annal.interface.ts index 5735540..0ef61ee 100644 --- a/src/ue/annals/interfaces/annal.interface.ts +++ b/src/ue/annals/interfaces/annal.interface.ts @@ -25,8 +25,7 @@ const UE_ANNAL_SELECT_FILTER = { }, }, validatedAt: true, - ue: { select: { code: true } }, - ueof: { select: { code: true, info: { select: { language: true } } } }, + ueof: { select: { code: true, info: { select: { language: true } }, ue: { select: { code: true } } } }, }, } satisfies Prisma.UeAnnalFindManyArgs; diff --git a/src/ue/comments/comments.service.ts b/src/ue/comments/comments.service.ts index 71f4ac5..24775ec 100644 --- a/src/ue/comments/comments.service.ts +++ b/src/ue/comments/comments.service.ts @@ -35,8 +35,10 @@ export class CommentsService { includeDeletedReplied: bypassAnonymousData, }, where: { - ue: { - code: dto.ueCode, + ueof: { + ue: { + code: dto.ueCode, + }, }, }, take: this.config.PAGINATION_PAGE_SIZE, @@ -45,7 +47,7 @@ export class CommentsService { userId, ); const commentCount = await this.prisma.ueComment.count({ - where: { ue: { code: dto.ueCode } }, + where: { ueof: { ue: { code: dto.ueCode } } }, }); // If the user is neither a moderator or the comment author, and the comment is anonymous, // we remove the author from the response @@ -182,7 +184,9 @@ export class CommentsService { }, where: { authorId: userId, - ueId: ue.code, + ueof: { + ueId: ue.code, + }, }, }); return comment.length > 0; @@ -214,11 +218,6 @@ export class CommentsService { id: userId, }, }, - ue: { - connect: { - code: body.ueCode, - }, - }, ueof: { connect: { code: lastSemester.ueofId, diff --git a/src/ue/comments/interfaces/comment.interface.ts b/src/ue/comments/interfaces/comment.interface.ts index 5d6a670..def47c2 100644 --- a/src/ue/comments/interfaces/comment.interface.ts +++ b/src/ue/comments/interfaces/comment.interface.ts @@ -25,6 +25,16 @@ const COMMENT_SELECT_FILTER = { }, isAnonymous: true, body: true, + ueof: { + select: { + code: true, + info: { + select: { + language: true, + }, + }, + }, + }, answers: { select: { id: true, diff --git a/src/ue/dto/ue-search.dto.ts b/src/ue/dto/ue-search.dto.ts index fd041f2..6ecc6ac 100644 --- a/src/ue/dto/ue-search.dto.ts +++ b/src/ue/dto/ue-search.dto.ts @@ -28,7 +28,7 @@ export class UeSearchDto { creditType?: string; @IsString() - @Length(3) + @Length(3, 3) @IsOptional() availableAtSemester?: string; diff --git a/src/ue/ue.controller.ts b/src/ue/ue.controller.ts index c6c63f9..3ad0241 100644 --- a/src/ue/ue.controller.ts +++ b/src/ue/ue.controller.ts @@ -39,7 +39,11 @@ export class UeController { @Get('/:ueCode') @IsPublic() - async getUe(@GetUser() user: User, @Param('ueCode') ueCode: string, @Res() res: Response): Promise { + async getUe( + @GetUser() user: User, + @Param('ueCode') ueCode: string, + @Res({ passthrough: true }) res: Response, + ): Promise { if (!(await this.ueService.doesUeExist(ueCode))) { // Check for aliases or throw an error const alias = await this.ueService.findAlias(ueCode); @@ -47,7 +51,7 @@ export class UeController { throw new AppException(ERROR_CODE.NO_SUCH_UE, ueCode); } const result = this.formatDetailedUe(await this.ueService.getUe(ueCode.toUpperCase())); - if (user.userType === UserType.STUDENT || user.userType === UserType.FORMER_STUDENT) return result; + if (user?.userType === UserType.STUDENT || user?.userType === UserType.FORMER_STUDENT) return result; return omit(result, 'starVotes'); } @@ -124,7 +128,7 @@ export class UeController { })), info: { requirements: chosenOf.requirements.map((r) => r.code), - languages: [...new Set(ue.ueofs.map((ueof) => ueof.info.language))], + languages: ue.ueofs.map((ueof) => ueof.info.language).uniqueValues, minors: chosenOf.info.minors, objectives: chosenOf.info.objectives, program: chosenOf.info.program, diff --git a/src/validation.ts b/src/validation.ts index b4a89a6..24c6cab 100644 --- a/src/validation.ts +++ b/src/validation.ts @@ -19,6 +19,7 @@ const mappedErrors = { isEnum: ERROR_CODE.PARAM_NOT_ENUM, isDate: ERROR_CODE.PARAM_NOT_DATE, isUuid: ERROR_CODE.PARAM_NOT_UUID, + isLength: ERROR_CODE.PARAM_INVALID_SIZE, maxLength: ERROR_CODE.PARAM_TOO_LONG, minLength: ERROR_CODE.PARAM_TOO_SHORT, arrayMinSize: ERROR_CODE.PARAM_SIZE_TOO_SMALL, diff --git a/test/declarations.d.ts b/test/declarations.d.ts index 3ef3a8a..e33fc2c 100644 --- a/test/declarations.d.ts +++ b/test/declarations.d.ts @@ -1,5 +1,5 @@ import { ERROR_CODE, ErrorData, ExtrasTypeBuilder } from '../src/exceptions'; -import { UeComment } from 'src/ue/interfaces/comment.interface'; +import { UeComment } from 'src/ue/comments/interfaces/comment.interface'; import { UeCommentReply } from 'src/ue/comments/interfaces/comment-reply.interface'; import { UeRating } from 'src/ue/interfaces/rate.interface'; import { FakeUeAnnalType } from './utils/fakedb'; @@ -48,11 +48,11 @@ declare module './declarations' { * depending on the {@link created} property. */ expectUeComment( - comment: JsonLikeVariant>, + comment: JsonLikeVariant> & { ue: FakeUe }, created = false, ): this; /** expects to return the given {@link commentPage | page of comments} */ - expectUeComments(commentPage: JsonLikeVariant>): this; + expectUeComments(commentPage: Pagination): this; /** * expects to return the given {@link reply} * The HTTP Status code may be 200 or 204, depending on the {@link created} property. diff --git a/test/declarations.ts b/test/declarations.ts index 1d70f57..50639e2 100644 --- a/test/declarations.ts +++ b/test/declarations.ts @@ -15,12 +15,12 @@ import { isArray } from 'class-validator'; import { Language } from '@prisma/client'; /** Shortcut function for `this.expectStatus(200).expectJsonLike` */ -function expect(obj: JsonLikeVariant) { - return (this).expectStatus(HttpStatus.OK).expectJsonMatchStrict(obj); +function expect(this: Spec, obj: JsonLikeVariant) { + return this.expectStatus(HttpStatus.OK).expectJsonMatchStrict(obj); } /** Shortcut function for `this.expectStatus(200|204).expectJsonLike` */ -function expectOkOrCreate(obj: JsonLikeVariant, created = false) { - return (this).expectStatus(created ? HttpStatus.CREATED : HttpStatus.OK).expectJsonLike(obj); +function expectOkOrCreate(this: Spec, obj: JsonLikeVariant, created = false) { + return this.expectStatus(created ? HttpStatus.CREATED : HttpStatus.OK).expectJsonLike(obj); } export function deepDateToString(obj: T): JsonLikeVariant { @@ -44,7 +44,8 @@ function ueOverviewExpectation(ue: FakeUe, spec: Spec) { })), })), info: { - ...omit(ue.info, 'id', 'program', 'objectives'), + ...omit(ue.info, 'id', 'program', 'objectives', 'language'), + languages: [ue.info.language], program: getTranslation(ue.info.program, spec.language), objectives: getTranslation(ue.info.objectives, spec.language), }, @@ -76,7 +77,7 @@ Spec.prototype.expectUe = function (ue: FakeUe, rates: Array<{ criterionId: stri code: ue.code, creationYear: ue.creationYear, updateYear: ue.updateYear, - starVotes: Object.fromEntries(rates.map((rate) => [rate.criterionId, rate.value])), + ...(rates.length ? { starVotes: Object.fromEntries(rates.map((rate) => [rate.criterionId, rate.value])) } : {}), ofs: [ { name: getTranslation(ue.name, this.language), @@ -131,8 +132,18 @@ Spec.prototype.expectUesWithPagination = function (app: AppProvider, ues: FakeUe itemsPerPage: app().get(ConfigModule).PAGINATION_PAGE_SIZE, }); }; -Spec.prototype.expectUeComment = expectOkOrCreate>; -Spec.prototype.expectUeComments = function expect(obj: Pagination) { +Spec.prototype.expectUeComment = function expect(this: Spec, obj, created = false) { + return this.expectStatus(created ? HttpStatus.CREATED : HttpStatus.OK).expectJsonLike({ + ...omit(obj as any, 'ue', 'ueofId'), + ueof: { + code: obj.ue.ueofCode, + info: { + language: obj.ue.info.language, + }, + }, + }); +}; +Spec.prototype.expectUeComments = function expect(obj) { return (this).expectStatus(HttpStatus.OK).expectJsonMatchStrict({ itemCount: obj.itemCount, itemsPerPage: obj.itemsPerPage, @@ -149,6 +160,12 @@ Spec.prototype.expectUeComments = function expect(obj: Pagination) { 'upvoted', 'upvotes', ), + ueof: { + code: comment.ue.ueofCode, + info: { + language: comment.ue.info.language, + }, + }, createdAt: comment.createdAt.toISOString(), updatedAt: comment.updatedAt.toISOString(), answers: comment.answers.map((answer) => ({ diff --git a/test/e2e/ue/annals/delete-annal.e2e-spec.ts b/test/e2e/ue/annals/delete-annal.e2e-spec.ts index 07231c5..23e9798 100644 --- a/test/e2e/ue/annals/delete-annal.e2e-spec.ts +++ b/test/e2e/ue/annals/delete-annal.e2e-spec.ts @@ -67,9 +67,6 @@ const DeleteAnnal = e2eSuite('DELETE /ue/annals/{annalId}', (app) => { sender: pick(senderUser, 'id', 'firstName', 'lastName'), createdAt: annal_validated.createdAt.toISOString(), updatedAt: JsonLike.ANY_DATE, - ue: { - code: ue.code, - }, }); return app() .get(PrismaService) diff --git a/test/e2e/ue/annals/get-annals.e2e-spec.ts b/test/e2e/ue/annals/get-annals.e2e-spec.ts index 7bd9fb9..40e3e83 100644 --- a/test/e2e/ue/annals/get-annals.e2e-spec.ts +++ b/test/e2e/ue/annals/get-annals.e2e-spec.ts @@ -105,7 +105,7 @@ const GetAnnal = e2eSuite('GET /ue/annals', (app) => { const formatAnnalFile = (from: Partial): JsonLikeVariant => { return { - ...pick(from, 'id', 'semesterId', 'status', 'sender', 'type', 'ue'), + ...pick(from, 'id', 'semesterId', 'status', 'sender', 'type', 'ueof'), createdAt: from.createdAt?.toISOString(), updatedAt: from.updatedAt?.toISOString(), }; diff --git a/test/e2e/ue/annals/patch-annal.e2e-spec.ts b/test/e2e/ue/annals/patch-annal.e2e-spec.ts index 3c94f11..d5695bf 100644 --- a/test/e2e/ue/annals/patch-annal.e2e-spec.ts +++ b/test/e2e/ue/annals/patch-annal.e2e-spec.ts @@ -106,9 +106,6 @@ const EditAnnal = e2eSuite('PATCH /ue/annals/{annalId}', (app) => { id: annal_validated.id, createdAt: annal_validated.createdAt.toISOString(), updatedAt: JsonLike.ANY_DATE, - ue: { - code: ue.code, - }, }); }); }); diff --git a/test/e2e/ue/comments/delete-comment.e2e-spec.ts b/test/e2e/ue/comments/delete-comment.e2e-spec.ts index 30b0829..bc83595 100644 --- a/test/e2e/ue/comments/delete-comment.e2e-spec.ts +++ b/test/e2e/ue/comments/delete-comment.e2e-spec.ts @@ -57,6 +57,7 @@ const DeleteComment = e2eSuite('DELETE /ue/comments/{commentId}', (app) => { .withBearerToken(user.token) .delete(`/ue/comments/${comment1.id}`) .expectUeComment({ + ue, id: comment1.id, author: { id: comment1.authorId, diff --git a/test/e2e/ue/comments/get-comment-from-id.e2e-spec.ts b/test/e2e/ue/comments/get-comment-from-id.e2e-spec.ts index 28af1dc..91cf636 100644 --- a/test/e2e/ue/comments/get-comment-from-id.e2e-spec.ts +++ b/test/e2e/ue/comments/get-comment-from-id.e2e-spec.ts @@ -43,13 +43,13 @@ const GetCommentFromIdE2ESpec = e2eSuite('GET /ue/comments/:commentId', (app) => .withBearerToken(user.token) .get(`/ue/comments/${comment.id}`) .expectUeComment({ + ue, ...(omit( comment, 'semesterId', 'authorId', 'deletedAt', 'validatedAt', - 'ueId', 'lastValidatedBody', ) as Required), answers: [ @@ -78,13 +78,13 @@ const GetCommentFromIdE2ESpec = e2eSuite('GET /ue/comments/:commentId', (app) => .withBearerToken(user2.token) .get(`/ue/comments/${comment.id}`) .expectUeComment({ + ue, ...(omit( comment, 'semesterId', 'authorId', 'deletedAt', 'validatedAt', - 'ueId', 'lastValidatedBody', ) as Required), answers: [ diff --git a/test/e2e/ue/comments/get-comment.e2e-spec.ts b/test/e2e/ue/comments/get-comment.e2e-spec.ts index 690a305..c59a974 100644 --- a/test/e2e/ue/comments/get-comment.e2e-spec.ts +++ b/test/e2e/ue/comments/get-comment.e2e-spec.ts @@ -108,7 +108,7 @@ const GetCommentsE2ESpec = e2eSuite('GET /ue/comments', (app) => { .slice(0, app().get(ConfigModule).PAGINATION_PAGE_SIZE) .map((comment) => { if (comment.isAnonymous && comment.author.id !== user.id) delete comment.author; - return comment; + return { ...comment, ue }; }), itemCount: comments.length, itemsPerPage: app().get(ConfigModule).PAGINATION_PAGE_SIZE, @@ -154,7 +154,7 @@ const GetCommentsE2ESpec = e2eSuite('GET /ue/comments', (app) => { .slice(app().get(ConfigModule).PAGINATION_PAGE_SIZE, app().get(ConfigModule).PAGINATION_PAGE_SIZE * 2) .map((comment) => { if (comment.isAnonymous && comment.author.id !== user.id) delete comment.author; - return comment; + return { ...comment, ue }; }), itemCount: comments.length, itemsPerPage: app().get(ConfigModule).PAGINATION_PAGE_SIZE, @@ -188,6 +188,7 @@ const GetCommentsE2ESpec = e2eSuite('GET /ue/comments', (app) => { ? (b.createdAt).getTime() - (a.createdAt).getTime() : b.upvotes - a.upvotes, ) + .map((comment) => ({ ...comment, ue })) .slice(0, app().get(ConfigModule).PAGINATION_PAGE_SIZE), itemCount: comments.length, itemsPerPage: app().get(ConfigModule).PAGINATION_PAGE_SIZE, diff --git a/test/e2e/ue/comments/post-comment.e2e-spec.ts b/test/e2e/ue/comments/post-comment.e2e-spec.ts index b3e4d7a..e4fdfac 100644 --- a/test/e2e/ue/comments/post-comment.e2e-spec.ts +++ b/test/e2e/ue/comments/post-comment.e2e-spec.ts @@ -96,6 +96,7 @@ const PostCommment = e2eSuite('POST /ue/comments', (app) => { .expectUeComment( { id: JsonLike.ANY_UUID, + ue, author: { id: user2.id, firstName: user2.firstName, @@ -144,6 +145,7 @@ const PostCommment = e2eSuite('POST /ue/comments', (app) => { }) .expectUeComment( { + ue, id: JsonLike.ANY_UUID, author: { id: user2.id, diff --git a/test/e2e/ue/comments/update-comment.e2e-spec.ts b/test/e2e/ue/comments/update-comment.e2e-spec.ts index 8871c34..b4a7573 100644 --- a/test/e2e/ue/comments/update-comment.e2e-spec.ts +++ b/test/e2e/ue/comments/update-comment.e2e-spec.ts @@ -100,6 +100,7 @@ const UpdateComment = e2eSuite('PATCH /ue/comments/{commentId}', (app) => { isAnonymous: true, }) .expectUeComment({ + ue, id: JsonLike.ANY_UUID, author: { id: user.id, @@ -133,6 +134,7 @@ const UpdateComment = e2eSuite('PATCH /ue/comments/{commentId}', (app) => { isAnonymous: false, }) .expectUeComment({ + ue, id: JsonLike.ANY_UUID, author: { id: user.id, diff --git a/test/e2e/ue/search.e2e-spec.ts b/test/e2e/ue/search.e2e-spec.ts index d01f183..cf4c1a7 100644 --- a/test/e2e/ue/search.e2e-spec.ts +++ b/test/e2e/ue/search.e2e-spec.ts @@ -53,7 +53,7 @@ const SearchE2ESpec = e2eSuite('GET /ue', (app) => { .spec() .withBearerToken(user.token) .get('/ue?q=XX01&availableAtSemester=AP28') - .expectAppError(ERROR_CODE.PARAM_TOO_LONG, 'availableAtSemester'); + .expectAppError(ERROR_CODE.PARAM_INVALID_SIZE, 'availableAtSemester'); }); it('should return a 400 as page is negative', () => { diff --git a/test/utils/fakedb.ts b/test/utils/fakedb.ts index f654f5c..8de8aef 100644 --- a/test/utils/fakedb.ts +++ b/test/utils/fakedb.ts @@ -664,7 +664,6 @@ export const createAnnal = entityFaker( semesterId: semester.code, senderId: sender.id, typeId: type.id, - ueId: ue.code, ueofId: ue.ueofCode, }, }), @@ -799,8 +798,11 @@ export const createUe = entityFaker( .then((ue) => ({ ...omit(ue, 'ueofs'), ueofCode: ue.ueofs[0].code, - ...omit(ue.ueofs[0], 'code', 'ueId', 'ueInfoId', 'openSemester', 'nameTranslationId'), - info: omit(ue.ueofs[0].info, 'objectivesTranslationId', 'programTranslationId'), + ...omit(ue.ueofs[0], 'code', 'ueId', 'ueInfoId', 'openSemester', 'nameTranslationId', 'requirements'), + info: { + ...omit(ue.ueofs[0].info, 'objectivesTranslationId', 'programTranslationId'), + requirements: ue.ueofs[0].requirements, + }, openSemesters: ue.ueofs[0].openSemester, })), ); @@ -898,14 +900,9 @@ export const createComment = entityFaker( .get(PrismaService) .withDefaultBehaviour.ueComment.create({ data: { - ...omit(params, 'ueId', 'ueofId', 'authorId', 'semesterId', 'status'), + ...omit(params, 'ueofId', 'authorId', 'semesterId', 'status'), validatedAt: params.status & CommentStatus.VALIDATED ? new Date() : undefined, deletedAt: params.status & CommentStatus.DELETED ? new Date() : undefined, - ue: { - connect: { - code: dependencies.ue.code, - }, - }, ueof: { connect: { code: dependencies.ue.ueofCode, From ca7355aea7a8bf281890d85701bbb9beff10a522 Mon Sep 17 00:00:00 2001 From: AlbanSdl Date: Sun, 8 Sep 2024 23:35:07 +0200 Subject: [PATCH 09/14] fix(tests): all tests are now working --- test/declarations.ts | 4 ++-- test/e2e/ue/get.e2e-spec.ts | 12 +++++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/test/declarations.ts b/test/declarations.ts index 50639e2..8a5c1ce 100644 --- a/test/declarations.ts +++ b/test/declarations.ts @@ -71,13 +71,13 @@ Spec.prototype.expectAppError = function ( error: (args as string[]).reduce((arg, extra) => arg.replaceAll('%', extra), ErrorData[errorCode].message), }); }; -Spec.prototype.expectUe = function (ue: FakeUe, rates: Array<{ criterionId: string; value: number }> = []) { +Spec.prototype.expectUe = function (ue: FakeUe, rates: Array<{ criterionId: string; value: number }>) { return (this).expectStatus(HttpStatus.OK).expectJsonMatchStrict( deepDateToString({ code: ue.code, creationYear: ue.creationYear, updateYear: ue.updateYear, - ...(rates.length ? { starVotes: Object.fromEntries(rates.map((rate) => [rate.criterionId, rate.value])) } : {}), + ...(rates ? { starVotes: Object.fromEntries(rates.map((rate) => [rate.criterionId, rate.value])) } : {}), ofs: [ { name: getTranslation(ue.name, this.language), diff --git a/test/e2e/ue/get.e2e-spec.ts b/test/e2e/ue/get.e2e-spec.ts index 49fa5d1..5c3f5c3 100644 --- a/test/e2e/ue/get.e2e-spec.ts +++ b/test/e2e/ue/get.e2e-spec.ts @@ -14,6 +14,7 @@ import { e2eSuite } from '../../utils/test_utils'; import * as pactum from 'pactum'; import { ERROR_CODE } from '../../../src/exceptions'; import { computeRate } from '../../../src/ue/interfaces/ue.interface'; +import { UserType } from '@prisma/client'; const GetE2ESpec = e2eSuite('GET /ue/{ueCode}', (app) => { const user = createUser(app); @@ -21,6 +22,11 @@ const GetE2ESpec = e2eSuite('GET /ue/{ueCode}', (app) => { login: 'user2', studentId: 2, }); + const user3 = createUser(app, { + login: 'user3', + studentId: 3, + userType: UserType.EMPLOYEE, + }); const semesters = [createSemester(app), createSemester(app)]; const branches = [createBranch(app), createBranch(app)]; const branchOptions = [ @@ -77,7 +83,11 @@ const GetE2ESpec = e2eSuite('GET /ue/{ueCode}', (app) => { }); it('should return the UE XX01', () => { - return pactum.spec().withBearerToken(user.token).get('/ue/XX01').expectUe(ues[1]); + return pactum.spec().withBearerToken(user.token).get('/ue/XX01').expectUe(ues[1], []); + }); + + it('should return the UE XX01 without ratings', () => { + return pactum.spec().withBearerToken(user3.token).get('/ue/XX01').expectUe(ues[1]); }); it('should return the UE XX30 with rating', () => { From 55307bc9ab8518a7161ce4905c1f6e8610ed4a02 Mon Sep 17 00:00:00 2001 From: AlbanSdl Date: Mon, 9 Sep 2024 02:54:32 +0200 Subject: [PATCH 10/14] fix(ue): import script works with the new db structure --- migration/etuutt_old/make-migration.ts | 139 +++++---- migration/etuutt_old/modules/branch.ts | 265 +++++++++++++++--- .../etuutt_old/modules/creditCategory.ts | 44 +-- migration/etuutt_old/modules/ue.ts | 67 +++-- 4 files changed, 372 insertions(+), 143 deletions(-) diff --git a/migration/etuutt_old/make-migration.ts b/migration/etuutt_old/make-migration.ts index c5657b9..c279afd 100644 --- a/migration/etuutt_old/make-migration.ts +++ b/migration/etuutt_old/make-migration.ts @@ -1,7 +1,7 @@ import { PrismaClient as _PrismaClient } from '@prisma/client'; import { createConnection } from 'mysql'; import { cleanDb } from '../../test/utils/test_utils'; -import { migrateUEs } from './modules/ue'; +import { findLegacyUeofName, migrateUEs } from './modules/ue'; import { createCreditCategories } from './modules/creditCategory'; import { createSemesters } from './modules/semester'; import { migrateUeComments } from './modules/ueComment'; @@ -15,7 +15,7 @@ import { RawUeComment, } from '../../src/prisma/types'; import { stringToTranslation } from './utils'; -import { omit, pick } from '../../src/utils'; +import { omit } from '../../src/utils'; type MayBePromise = Promise | T; @@ -33,6 +33,7 @@ export async function getOperationResults(operations: MayBePromise> { - const ue = await _prisma.ue.findUnique({ where: { code: params.code } }); + // Fix credit category types to match new categories + if (params.credits.category === 'HP' || params.credits.category === 'OTHER') params.credits.category = 'AC'; + if (params.credits.category === 'MASTER') params.credits.category = 'MA'; + const { ue: ueCode, ueof: ueofCode } = findLegacyUeofName(params.code, params.info.comment); + const ue = await _prisma.ue.findUnique({ where: { code: ueCode } }); if (!ue) { return { data: await _prisma.ue.create({ data: { - ...pick(params, 'code', 'inscriptionCode'), - name: stringToTranslation(params.name), - workTime: { - create: params.workTime, - }, - info: { - create: { - program: stringToTranslation(params.info.program), - objectives: stringToTranslation(params.info.objectives), - languages: params.info.languages, - minors: params.info.minors, - requirements: { - connect: params.info.requirements.map((value) => ({ id: value })), + code: ueCode, + creationYear: new Date('2023-01-01').getTime() / 1000, + updateYear: new Date('2023-01-01').getTime() / 1000, + ueofs: { + connectOrCreate: { + where: { + code: ueofCode, }, - comment: stringToTranslation(params.info.comment), - }, - }, - credits: { - create: { - credits: params.credits.credits, - category: { - connect: { - code: params.credits.category, + create: { + code: ueofCode, + siepId: ueId--, + available: false, + workTime: { + create: params.workTime, + }, + credits: { + create: { + credits: params.credits.credits, + category: { + connect: { + code: params.credits.category, + }, + }, + }, + }, + name: stringToTranslation(params.name), + info: { + create: { + program: stringToTranslation(params.info.program), + objectives: stringToTranslation(params.info.objectives), + language: params.info.languages, + minors: params.info.minors, + }, }, }, }, @@ -125,32 +138,38 @@ const prisma = _prisma.$extends({ // Ok that would be crazy to check every field, let's not do that return { data: await _prisma.ue.update({ - where: { code: params.code }, + where: { code: ueCode }, data: { - ...pick(params, 'code', 'inscriptionCode'), - name: stringToTranslation(params.name), - workTime: { - update: params.workTime, - }, - info: { - update: { - program: stringToTranslation(params.info.program), - objectives: stringToTranslation(params.info.objectives), - languages: params.info.languages, - minors: params.info.minors, - requirements: { - connect: params.info.requirements.map((value) => ({ id: value })), + ueofs: { + connectOrCreate: { + where: { + code: ueofCode, }, - comment: stringToTranslation(params.info.comment), - }, - }, - credits: { - deleteMany: {}, - create: { - credits: params.credits.credits, - category: { - connect: { - code: params.credits.category, + create: { + code: ueofCode, + siepId: ueId--, + available: false, + workTime: { + create: params.workTime, + }, + credits: { + create: { + credits: params.credits.credits, + category: { + connect: { + code: params.credits.category, + }, + }, + }, + }, + name: stringToTranslation(params.name), + info: { + create: { + program: stringToTranslation(params.info.program), + objectives: stringToTranslation(params.info.objectives), + language: params.info.languages, + minors: params.info.minors, + }, }, }, }, @@ -186,10 +205,12 @@ const prisma = _prisma.$extends({ async create({ code, name, + isMaster, description, }: { code: string; name: string; + isMaster: boolean; description: string; }): Promise> { const branch = await _prisma.uTTBranch.findUnique({ @@ -199,7 +220,7 @@ const prisma = _prisma.$extends({ if (!branch) { return { data: await _prisma.uTTBranch.create({ - data: { code, name, descriptionTranslation: stringToTranslation(description) }, + data: { code, name, isMaster, descriptionTranslation: stringToTranslation(description) }, }), operation: 'created', }; @@ -208,7 +229,7 @@ const prisma = _prisma.$extends({ return { data: await _prisma.uTTBranch.update({ where: { code }, - data: { name, descriptionTranslation: stringToTranslation(description) }, + data: { name, isMaster, descriptionTranslation: stringToTranslation(description) }, }), operation: 'updated', }; @@ -282,7 +303,13 @@ const prisma = _prisma.$extends({ ue: string; semesterCode: string; }): Promise> { - const comment = await _prisma.ueComment.findFirst({ where: { ue: { code: ue }, createdAt } }); + const codes = findLegacyUeofName(ue, ''); + // check this is the correct ueof : + let ueof = await _prisma.ueof.findFirst({ where: { code: codes.ueof } }); + if (!ueof) ueof = await _prisma.ueof.findFirst({ where: { code: codes.ueof.replace('TRO', 'REI') } }); + if (ueof) codes.ueof = codes.ueof.replace('TRO', 'REI'); + else console.error(`No such ueof for this code : ${codes.ueof}`); + const comment = await _prisma.ueComment.findFirst({ where: { ueof: { ue: { code: codes.ue } }, createdAt } }); if (!comment) { return { data: await _prisma.ueComment.create({ @@ -292,7 +319,7 @@ const prisma = _prisma.$extends({ createdAt, updatedAt, validatedAt: isValid ? updatedAt : null, - ue: { connect: { code: ue } }, + ueof: { connect: { code: codes.ueof } }, semester: { connect: { code: semesterCode } }, }, }), diff --git a/migration/etuutt_old/modules/branch.ts b/migration/etuutt_old/modules/branch.ts index 8cc3a89..b5280b2 100644 --- a/migration/etuutt_old/modules/branch.ts +++ b/migration/etuutt_old/modules/branch.ts @@ -9,12 +9,18 @@ export async function createBranches(prisma: PrismaClient) { TC(prisma), A2I(prisma), GI(prisma), + GI_APPR(prisma), GM(prisma), + GM_APPR(prisma), ISI(prisma), MTE(prisma), MM(prisma), RT(prisma), - SN(prisma), + SN_APPR(prisma), + RE(prisma), + PAIP(prisma), + ISC(prisma), + IC(prisma), ]; const operationsAwaited = await Promise.all(operations); const branchOptions = await getOperationResults(operationsAwaited.map(({ branchOptions }) => branchOptions).flat(1)); @@ -30,16 +36,17 @@ export async function createBranches(prisma: PrismaClient) { function TC(prisma: PrismaClient) { return prisma.uTTBranch .create({ - code: 'TCBR', - name: 'TCBR', + code: 'TC', + name: 'Tronc commun', + isMaster: false, description: '', }) .then((branch) => ({ branch, branchOptions: [ prisma.uTTBranchOption.create({ - code: 'TCBR_TC', - name: 'TCBR', + code: 'TC', + name: 'Tronc commun', description: '', branchCode: branch.data.code, }), @@ -52,20 +59,15 @@ function A2I(prisma: PrismaClient) { .create({ code: 'A2I', name: 'Automatique et Informatique Industrielle', + isMaster: false, description: '', }) .then((branch) => ({ branch, branchOptions: [ prisma.uTTBranchOption.create({ - code: 'TCBR', - name: 'TCBR', - description: '', - branchCode: branch.data.code, - }), - prisma.uTTBranchOption.create({ - code: 'LIBRE', - name: 'Filière LIBRE A2I', + code: 'A2I', + name: "Tronc commun d'Automatique et informatique industrielle", description: '', branchCode: branch.data.code, }), @@ -90,14 +92,15 @@ function GI(prisma: PrismaClient) { .create({ code: 'GI', name: 'Génie Industriel', + isMaster: false, description: '', }) .then((branch) => ({ branch, branchOptions: [ prisma.uTTBranchOption.create({ - code: 'TCBR', - name: 'Tronc commun de branche', + code: 'GI', + name: 'Tronc commun de Génie Industriel', description: '', branchCode: branch.data.code, }), @@ -119,6 +122,45 @@ function GI(prisma: PrismaClient) { description: '', branchCode: branch.data.code, }), + prisma.uTTBranchOption.create({ + code: 'LET_APPR', + name: 'Logistique Externe et Transport - Apprentissage', + description: '', + branchCode: branch.data.code, + }), + prisma.uTTBranchOption.create({ + code: 'LIP_APPR', + name: 'Logistique Interne et Production - Apprentissage', + description: '', + branchCode: branch.data.code, + }), + prisma.uTTBranchOption.create({ + code: 'RAMS_APPR', + name: 'Reliability, Availability, Maintenance and Safety - Apprentissage', + description: '', + branchCode: branch.data.code, + }), + ], + })); +} + +function GI_APPR(prisma: PrismaClient) { + return prisma.uTTBranch + .create({ + code: 'GI_APPR', + name: 'Génie Industriel - Apprentissage', + isMaster: false, + description: '', + }) + .then((branch) => ({ + branch, + branchOptions: [ + prisma.uTTBranchOption.create({ + code: 'GI_APPR', + name: 'Tronc commun de Génie Industriel - Apprentissage', + description: '', + branchCode: branch.data.code, + }), ], })); } @@ -127,15 +169,16 @@ function GM(prisma: PrismaClient) { return prisma.uTTBranch .create({ code: 'GM', - name: 'Génie Mécanique', + name: 'Génie Mécanique - Apprentissage', + isMaster: false, description: '', }) .then((branch) => ({ branch, branchOptions: [ prisma.uTTBranchOption.create({ - code: 'TCBR', - name: 'Tronc commun de branche', + code: 'GM', + name: 'Tronc commun de Génie Mécanique', description: '', branchCode: branch.data.code, }), @@ -151,6 +194,12 @@ function GM(prisma: PrismaClient) { description: '', branchCode: branch.data.code, }), + prisma.uTTBranchOption.create({ + code: 'MDPI_APPR', + name: 'Management Digital des Produits et Infrastructures - Apprentissage', + description: '', + branchCode: branch.data.code, + }), prisma.uTTBranchOption.create({ code: 'SNM', name: 'Simulation Numérique en Mécanique', @@ -161,19 +210,41 @@ function GM(prisma: PrismaClient) { })); } +function GM_APPR(prisma: PrismaClient) { + return prisma.uTTBranch + .create({ + code: 'GM_APPR', + name: 'Génie Mécanique', + isMaster: false, + description: '', + }) + .then((branch) => ({ + branch, + branchOptions: [ + prisma.uTTBranchOption.create({ + code: 'GM_APPR', + name: 'Tronc commun de Génie mécanique - Apprentissage', + description: '', + branchCode: branch.data.code, + }), + ], + })); +} + function ISI(prisma: PrismaClient) { return prisma.uTTBranch .create({ code: 'ISI', name: "Informatique et Systèmes d'Information", + isMaster: false, description: '', }) .then((branch) => ({ branch, branchOptions: [ prisma.uTTBranchOption.create({ - code: 'TCBR', - name: 'Tronc commun de branche', + code: 'ISI', + name: "Tronc commun d'Informatique et Systèmes d'Information", description: '', branchCode: branch.data.code, }), @@ -204,14 +275,15 @@ function MTE(prisma: PrismaClient) { .create({ code: 'MTE', name: 'Matériaux : Technologie et Economie', + isMaster: false, description: '', }) .then((branch) => ({ branch, branchOptions: [ prisma.uTTBranchOption.create({ - code: 'TCBR', - name: 'Tronc commun de branche', + code: 'MTE', + name: 'Tronc commun de Matériaux: Technologie et Economie', description: '', branchCode: branch.data.code, }), @@ -222,7 +294,7 @@ function MTE(prisma: PrismaClient) { branchCode: branch.data.code, }), prisma.uTTBranchOption.create({ - code: 'AUTO', + code: 'TCMC', name: 'Technologie et Commerce des Matériaux et des Composants', description: '', branchCode: branch.data.code, @@ -242,14 +314,15 @@ function MM(prisma: PrismaClient) { .create({ code: 'MM', name: 'Matériaux et Mécanique', + isMaster: false, description: '', }) .then((branch) => ({ branch, branchOptions: [ prisma.uTTBranchOption.create({ - code: 'TCBR', - name: 'Tronc commun de branche', + code: 'MM', + name: 'Tronc commun de Matériaux et Mécanique', description: '', branchCode: branch.data.code, }), @@ -263,13 +336,14 @@ function RT(prisma: PrismaClient) { code: 'RT', name: 'Réseaux et Télécommunications', description: '', + isMaster: false, }) .then((branch) => ({ branch, branchOptions: [ prisma.uTTBranchOption.create({ - code: 'TCBR', - name: 'Tronc commun de branche', + code: 'RT', + name: 'Tronc commun de Réseaux et télécommunications', description: '', branchCode: branch.data.code, }), @@ -295,19 +369,146 @@ function RT(prisma: PrismaClient) { })); } -function SN(prisma: PrismaClient) { +function SN_APPR(prisma: PrismaClient) { + return prisma.uTTBranch + .create({ + code: 'SN_APPR', + name: 'Systèmes Numériques - Apprentissage', + isMaster: false, + description: '', + }) + .then((branch) => ({ + branch, + branchOptions: [ + prisma.uTTBranchOption.create({ + code: 'SN_APPR', + name: 'Tronc commun de Systèmes Numériques - Apprentissage', + description: '', + branchCode: branch.data.code, + }), + prisma.uTTBranchOption.create({ + code: 'SN', + name: 'Tronc commun de Systèmes Numériques', + description: '', + branchCode: branch.data.code, + }), + ], + })); +} + +function RE(prisma: PrismaClient) { + return prisma.uTTBranch + .create({ + code: 'RE', + name: 'Risques et Environnement', + isMaster: true, + description: '', + }) + .then((branch) => ({ + branch, + branchOptions: [ + prisma.uTTBranchOption.create({ + code: 'RE', + name: 'Mention Risques et Environnement', + description: '', + branchCode: branch.data.code, + }), + prisma.uTTBranchOption.create({ + code: 'IMEDD', + name: "Ingénierie et Management de l'Environnement et du Développement Durable", + description: '', + branchCode: branch.data.code, + }), + prisma.uTTBranchOption.create({ + code: 'IMSGA', + name: 'Ingénierie et Management en Sécurité Globale Appliquée', + description: '', + branchCode: branch.data.code, + }), + ], + })); +} + +function PAIP(prisma: PrismaClient) { + return prisma.uTTBranch + .create({ + code: 'PAIP', + name: 'Physique Appliquée et Ingénierie Physique', + isMaster: true, + description: '', + }) + .then((branch) => ({ + branch, + branchOptions: [ + prisma.uTTBranchOption.create({ + code: 'PAIP', + name: 'Mention Physique Appliquée et Ingénierie Physique', + description: '', + branchCode: branch.data.code, + }), + prisma.uTTBranchOption.create({ + code: 'NPHOT', + name: 'Nano-optics and Nanophotonics', + description: '', + branchCode: branch.data.code, + }), + ], + })); +} + +function ISC(prisma: PrismaClient) { return prisma.uTTBranch .create({ - code: 'SN', - name: 'Systèmes Numériques', + code: 'ISC', + name: 'Ingénierie des Systèmes Complexes', + isMaster: true, description: '', }) .then((branch) => ({ branch, branchOptions: [ prisma.uTTBranchOption.create({ - code: 'TCBR', - name: 'Tronc commun de branche', + code: 'ISC', + name: 'Mention Ingénierie des Systèmes Complexes', + description: '', + branchCode: branch.data.code, + }), + prisma.uTTBranchOption.create({ + code: 'SSI', + name: "Sécurité des Systèmes d'Information", + description: '', + branchCode: branch.data.code, + }), + prisma.uTTBranchOption.create({ + code: 'OSS', + name: 'Optimisation et Sûreté des Systèmes', + description: '', + branchCode: branch.data.code, + }), + ], + })); +} + +function IC(prisma: PrismaClient) { + return prisma.uTTBranch + .create({ + code: 'IC', + name: 'Ingénierie de Conception', + isMaster: true, + description: '', + }) + .then((branch) => ({ + branch, + branchOptions: [ + prisma.uTTBranchOption.create({ + code: 'IC', + name: 'Mention Ingénierie de Conception', + description: '', + branchCode: branch.data.code, + }), + prisma.uTTBranchOption.create({ + code: 'MPSMP', + name: 'Mécanique et Performance en Service de Matériaux et Produits', description: '', branchCode: branch.data.code, }), diff --git a/migration/etuutt_old/modules/creditCategory.ts b/migration/etuutt_old/modules/creditCategory.ts index 31193a7..c798550 100644 --- a/migration/etuutt_old/modules/creditCategory.ts +++ b/migration/etuutt_old/modules/creditCategory.ts @@ -3,42 +3,14 @@ import { RawCreditCategory } from '../../../src/prisma/types'; export async function createCreditCategories(prisma: PrismaClient) { const creditCategoriesData = [ - { - code: 'CS', - name: 'Connaissances scientifiques', - }, - { - code: 'TM', - name: 'Techniques et méthodes', - }, - { - code: 'EC', - name: 'Expression et communication', - }, - { - code: 'ME', - name: "Management de l'entreprise", - }, - { - code: 'HT', - name: 'Humanité et technologie', - }, - { - code: 'ST', - name: 'Stage', - }, - { - code: 'HP', - name: 'Hors profil', - }, - { - code: 'MASTER', - name: 'Master', - }, - { - code: 'OTHER', - name: 'Autre', - }, + { code: 'CS', name: 'Connaissances scientifiques' }, + { code: 'TM', name: 'Techniques et méthodes' }, + { code: 'ST', name: 'Stage' }, + { code: 'HT', name: 'Humanités et technologies' }, + { code: 'ME', name: 'Mise en situation' }, + { code: 'EC', name: 'Expression et communication' }, + { code: 'AC', name: 'Autres Crédits' }, + { code: 'MA', name: 'Master' }, ]; const operations: Promise>[] = []; for (const creditCategoryData of creditCategoriesData) { diff --git a/migration/etuutt_old/modules/ue.ts b/migration/etuutt_old/modules/ue.ts index 66c02bc..2f1ff3f 100644 --- a/migration/etuutt_old/modules/ue.ts +++ b/migration/etuutt_old/modules/ue.ts @@ -2,6 +2,26 @@ import { getOperationResults, PrismaOperationResult, QueryFunction } from '../ma import { PrismaClient } from '../make-migration'; import { RawUe } from '../../../src/prisma/types'; +export function findLegacyUeofName(ueCode: string, commentaire: string) { + let LOCATION = 'TRO'; + if (commentaire.match(/UE réalisée à Reims/)) LOCATION = 'REI'; + let UECODE = ueCode; + let LANG = 'FR'; + if (ueCode.length > 4 && ueCode.slice(-1).match(/[APR]/)) { + const modifier = ueCode.slice(-1); + UECODE = ueCode.slice(0, -1); + if (modifier === 'A') LANG = 'EN'; + if (modifier === 'R') LOCATION = 'REI'; + } + if (ueCode.startsWith('LG')) LANG = 'GE'; + if (ueCode.startsWith('IT')) LANG = 'IT'; + if (ueCode.startsWith('KO')) LANG = 'KO'; + if (ueCode.startsWith('LC')) LANG = 'CH'; + if (ueCode.startsWith('LP')) LANG = 'PO'; + if (ueCode.startsWith('LS')) LANG = 'SP'; + return { ue: UECODE, ueof: `${UECODE}_${LANG}_${LOCATION}_LEG` }; +} + export async function migrateUEs(query: QueryFunction, prisma: PrismaClient) { const ues = await query('SELECT * FROM etu_uvs WHERE isOld = 0'); const operations: PrismaOperationResult[] = []; @@ -12,34 +32,17 @@ export async function migrateUEs(query: QueryFunction, prisma: PrismaClient) { ? -1 : 0, ); - const inscriptionCodes: string[] = (await prisma.ue.findMany({ select: { inscriptionCode: true } })).map( - ({ inscriptionCode }) => inscriptionCode, - ); for (const ue of ues) { - let inscriptionCode = ue.code.slice(0, 4); - while (inscriptionCodes.includes(inscriptionCode)) { - inscriptionCode = - inscriptionCode.slice(0, 3) + - Math.floor(Math.random() * 36) - .toString(36) - .toUpperCase(); - } - inscriptionCodes.push(inscriptionCode); - const requirements = operations - .filter((u) => new RegExp(`(^|\\W)${u.data.code}($|\\W)`).test(ue.antecedents)) - .map((u) => u.data.id); - //console.log(ue.code, inscriptionCode); operations.push( await prisma.ue.create({ code: ue.code, name: ue.name, - inscriptionCode, workTime: { cm: ue.cm, td: ue.td, tp: ue.tp, the: ue.the, - project: ue.projet, + project: ue.projet > 0, internship: ue.stage, }, info: { @@ -47,7 +50,6 @@ export async function migrateUEs(query: QueryFunction, prisma: PrismaClient) { objectives: ue.objectifs, languages: ue.languages, minors: ue.mineurs, - requirements, comment: ue.commentaire, }, credits: { @@ -59,5 +61,32 @@ export async function migrateUEs(query: QueryFunction, prisma: PrismaClient) { } const results = await getOperationResults(operations); console.log(`UEs : created ${results.created}, updated ${results.updated}, not changed ${results.notChanged}`); + let linkingOperations = 0; + for (const ue of ues) { + const { ue: ueCode, ueof: ueofCode } = findLegacyUeofName(ue.code, ue.commentaire); + const requirements = operations + .filter((u) => new RegExp(`(^|\\W)${u.data.code}($|\\W)`).test(ue.antecedents)) + .map((u) => u.data.code); + if (requirements.length === 0) continue; + linkingOperations++; + prisma.ue.update({ + where: { code: ueCode }, + data: { + ueofs: { + update: { + where: { + code: ueofCode, + }, + data: { + requirements: { + connect: requirements.map((value) => ({ code: value })), + }, + }, + }, + }, + }, + }); + } + console.log(`UEs : linked ${linkingOperations}`); return results.data; } From 43c6db98cd8c33d4e131ed01b670ec30509a1b60 Mon Sep 17 00:00:00 2001 From: AlbanSdl Date: Mon, 9 Sep 2024 21:27:58 +0200 Subject: [PATCH 11/14] doc(ue): introduction --- docs/doc_developers/api/index.md | 2 +- docs/doc_developers/api/ues.md | 60 ++++++++++++++++++++++++++++++++ scripts/seed/ue.ts | 2 +- 3 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 docs/doc_developers/api/ues.md diff --git a/docs/doc_developers/api/index.md b/docs/doc_developers/api/index.md index 1458b47..f2a6a36 100644 --- a/docs/doc_developers/api/index.md +++ b/docs/doc_developers/api/index.md @@ -10,6 +10,6 @@ setup.md technologies.md conventions.md test.md +ues.md timetables.md ``` - diff --git a/docs/doc_developers/api/ues.md b/docs/doc_developers/api/ues.md new file mode 100644 index 0000000..bdd3f6a --- /dev/null +++ b/docs/doc_developers/api/ues.md @@ -0,0 +1,60 @@ +# Le Guide des UEs + +_Avant la rentrée A24, la refonte du SIEP et par la même occasion la création du nouveau site étudiant, les UEs à l'UTT correspondaient à tous les différents cours donnés à l'UTT et dans ses antennes à Reims et Nogent. Ainsi, MT03 et MT03A (algèbre linéaire et algèbre linéaire en anglais) étaient deux UEs différentes. Avec cette refonte, a été introduite une distinction entre les cours et leurs variantes. Par exemple MT03A est en réalité une variante de MT03 et un étudiant n'est pas *censé* faire les deux. \ +Cette distinction entre les UEs (unité d'enseignement) et les UEOFs (offre de formation d'UE) permet ainsi de regrouper sous le même code d'UE deux cours différents sur la forme mais pas sur le fond. Pour s'adapter au mieux, le site étudiant a également adopté cette distinction, pour présenter au mieux les différentes opportunités en terme de cours disponibles tout en agrégeant les données utilisateur (commentaires, annales, notations, etc)_ + +## Glossaire : Les sigles made in UTT™ + +Avant tout, revenons rapidement sur les différentes abréviations qui figurent dans cette documentation et qui sont issues de l'admin de l'UTT : + +- **UE _(anciennement UV)_ :** unité d'enseignement. Un cours qui, une fois validé, certifie l'acquisition de commpétences particulières. Le code d'une UE est généralement composé d'un panachage de 4 chiffres et lettres, mais cette longueur n'est pas fixe. \ + Exemples : `NF10`, `MT01`, `APPTC` + > Certains codes d'UE ont changé pendant la refonte du SIEP, par exemple `PIX` est devenu `NF10`, `CTC1` est devenu `AST03`, ... +- **UEOF :** offre de formation d'UE. Il s'agit d'une version précise d'un cours avec un programme, une langue et un emplacement **fixes**. Ainsi en cas de modification de l'UE, de changement de programme ou encore de modification de la langue ou du lieu d'enseignement, une nouvelle UEOF sera crée. _Ces nouvelles considérations permettront au site étudiant d'évaluer l'ancienneté des programmes des UEs et de mieux pondérer les commentaires et notations._ Le code d'une UEOF est composé du code de l'UE, de la langue d'enseignement, du lieu d'enseignement ainsi que de son année de création. + Exemples : `NF10_FR_TRO_U24`, `MT01_FR_TRO_U23`, `APPTC_FR_TRO_U23` + > Attention, le concept d'UEOF n'existait pas avant 2023, donc les formations plus anciennes afficheront `U23` pour date de création ! +- **UEOE :** offre d'enseignement d'UE. L'enseignement d'une UEOF pendant un semestre précis. Ces codes apparaissent principalement sur le dossier étudiant et les emplois du temps. Ils sont constitués du début du code des UEOF avec le code de la période d'enseignement. Dans le site étudiant, les `UserUeSubscription` joueront le même rôle que les UEOE à la différence près qu'ils sont également rattachés à l'étudiant. \ + Exemples :`A24_NF10_FR_TRO`, `P25_MT01_FR_TRO`, `A24_APPTC_FR_TRO` +- **SIEP :** système informatique de l'enseignement et de la pédagogie. Le nouvel ERP de l'administration pour gérer les étudiants, leurs études et leurs données personnelles. Permet de centraliser tous les différents services qui existaient jusqu'à présent. \ + Cette nouvelle plateforme remplace ainsi de manière non-exhaustive : le guide des ues, le dossier étudiant, le suivi des stages et des alternances, les inscriptions et réinscriptions, les certificats de langue +- **DFP :** direction de la formation et de la pédagogie. L'équipe de l'UTT en charge de la formation et des outils administratifs (dont le SIEP fait partie) qui sont liés à la formation des étudiants. +- **ERP :** _en anglais, enterprise resource planning_. [Logiciel de gestion](https://fr.wikipedia.org/wiki/Progiciel_de_gestion_int%C3%A9gr%C3%A9) des processus et des ressources d'une entreprise. On peut retenir que c'est avant tout un logiciel de gestion, qui se décline dans le cadre du SIEP en une version incluant des pages web (celles du site du siep). Il faut bien retenir que le siep est avant tout un outil interne avant d'être un site internet ! Ainsi il ne faut pas compter sur de belles APIs propres... + +## Les UEs : La vision EtuUTT + +Depuis 2013 et avant, EtuUTT centralise les informations du guide des UEs, _qui rappelons-le était papier à ses débuts,_ et les enrichit de données supplémentaires : des ressentis d'anciens étudiants qui ont déjà suivi l'UE et des anciens sujets d'exams. Cela permet aux étudiants, aussi bien de choisir au mieux leurs prochains cours que de s'entrainer et réviser pour leurs examens. + +C'est dans cet état d'esprit que nous menons la refonte du site étudiant : + +- les commentaires sont toujours présents et permettent aux étudiants de s'exprimer librement sur leurs cours passés. + > Ces commentaires sont bien entendu **modérés** et ne sont visibles que par les étudiants (et anciens étudiants). \ + > ⚠️ Il faudra impérativement **avoir fait l'UE** pour pouvoir laisser un commentaire. Si un étudiant crée sont compte EtuUTT après le semestre où il a suivi l'UE1, il ne pourra pas s'exprimer sur cette UE. +- les utilisateurs peuvent désormais réagir à un commentaire d'UE en y répondant. Cela peut permettre de nuancer certains propos spécifiques ou au contraire de les développer par un argument supplémentaire ! +- les étudiants peuvent désormais _upvote_ les commentaires qui leurs semblent les plus pertinents. Ils remontront dans la liste des commentaires. + > ❓ Les commentaires sont triés en fonction de leur nombre d'upvotes mais aussi de leur ancienneté et de l'UEOF à laquelle ils sont liés +- Pour que les étudiants donnent plus leur avis sur leurs cours, il est désormais possible de noter avec une note sur 5 les UEs déjà suivies1 sur des critères prédéfinis par les administrateurs. Il s'agira principalement d'évaluer la qualité des contenus et de l'enseignement. +- la recherche d'une UE affichera toutes les différentes version de l'UE (dans toutes ses langues d'enseignement et les différents semestres où elle est disponible). Les UEs sont désormais rattachées à aux branches au profil desquelles elles comptent. Cela permettra d'identifier facilement les UEs hors profil. +- les étudiants pourront consulter les anciens examens des UEs et partager ceux des semestres qu'ils ont suivi1. + +1 : il est possible qu'une importation du profil étudiant soit ajoutée dans le futur et qu'il soit ainsi possible de donner son avis sur des UEs suivies avant la création du compte EtuUTT. + +Les objectifs de la refonte du guide des UEs étant présentés, nous allons pouvoir aborder la partie technique. + +## Les structures + +Pour définir aux mieux les UEs, nous utilisont plusieurs structures de données. Passons-les en revue afin de comprendre l'usage de chacune d'elles : + +- **`Ue`**, il s'agit d'une UE au sens de la refonte du SIEP. C'est à dire qu'elle ne contient elle même que peu d'informations : sa date de création et de dernière modification ainsi que les notes des utilisateurs. Ce sont les `Ueof` liées à cette UE qui contiendront la majorité des détails du guide des UEs. +- **`UeAlias`**. Certaines UEs n'existent plus. C'est le cas de `MATH03` ou de `EC01` par exemple. Il faut donc gérer les cas où certaines UEs auraient des prérequis qui n'existent plus ! Pour des UEs qui ont changé de nom comme `MT03` il suffit de mettre en place une "redirection" et pour les UEs qui n'existent plus du tout comme `EC01` il faut également le notifier pour faire disparaître la référence. C'est tout l'intérêt de cette table. +- **`Ueof`**, c'est la structure qui définit une UEOF. Elle va donc être liée à toutes les informations du guide des UEs la concernant : temps de travail, crédits, branche associée, prérequis, commentaires, annales... Pour un soucis de lisibilité, cette structure n'inclut pas les données telles que la langue d'enseignement, les mineurs, le programme et les objectifs. On utilisera `UeInfo` à la place. +- **`UeAnnal`**, il s'agit de la structure qui représente un sujet d'examen. Fait référence à l'utilisateur qui a envoyé le sujet, à l'UEOF, au semestre ainsi qu'au type d'examen (médian, final, etc) +- **`UeAnnalType`**, c'est le type d'examen d'une annale. Ces valeurs sont rentrées par les administrateurs du site qui seront dans la possibilité d'ajouter de nouveaux types d'examens s'ils se rendent compte que les valeurs précédentes ne sont pas assez précises. +- **`UeComment`**. Le commentaire d'un étudiant au sujet du cours qu'il a suivi. Ce commentaire est rattaché à l'UEOF correspondante afin d'afficher quelle variante de l'UE a été suivie mais sera affiché pour toutes les UEOFs de l'UE. Le commentaire peut être posté en anonyme mais l'auteur sera tout de même visible pour la modération. Egalement, les commentaires ne sont pas supprimés directement lorsque l'utilisateur choisit de les supprimer. Il y aura un petit délai permettant d'annuler la suppression ou de procéder à des actions de modération si nécessaire. +- **`UeCommentReply`**, c'est la réponse d'un utilisateur à un commentaire d'une UE. +- **`UeCommentUpvote`** il s'agit d'un upvote donné par n'importe quel étudiant à un commentaire de n'importe quelle UE s'il la trouvé instructif et/ou pertinent. +- **`UeCredit`**. C'est le nombre de crédits obtenus par un étudiant qui réalise l'UE. C'est ici qu'apparaît les branches et filières auxquelles cette UE est liée. En effet, il arrive qu'une même UE donne des crédits d'un type différent selon si l'étudiant est en master ou en cycle ingénieur. +- **`UeCreditCategory`**. Il s'agit des différents types de crédits ECTS: `CS`, `TM`, etc +- **`UeInfo`**. C'est ici que figurent toutes les informations détaillées des UEOFs : leur programme, les objectifs, la langue d'enseignement, les mineurs, etc. Cela permet de ne pas surcharger la structure des UEOFs. +- **`UeStarCriterion`**. Les critères sur lesquels les utilisateurs peuvent évaluer une UE. Ces critères ne sont pas fixes et pourront être modifiés par les administrateurs du site s'ils se rendent compte qu'ils sont mal définis. +- **`UeStarVote`**, c'est le vote d'un utilisateur pour une UE donnée et un critère donné. Il ne peut y avoir qu'un seul vote par Utilisateur & Ue & Critère. +- **`UeWorkTime`**. Cette structure rassemble les heures de travail estimé pour effectuer l'UE. Il n'y a plus de nombre d'heures pour la réalisation d'un projet éventuel mais l'indication porte désormais sur le fait qu'il y ait un projet ou non. diff --git a/scripts/seed/ue.ts b/scripts/seed/ue.ts index efef3fc..996a957 100644 --- a/scripts/seed/ue.ts +++ b/scripts/seed/ue.ts @@ -135,7 +135,7 @@ async function main() { const importYear = new Date().getFullYear(); // Can be changed to the year of the import if done with web interface console.info('\x1b[42;30mFetching UE list\x1b[0m'); - const ues = await parseDocument('scripts/seed/Export UE_OF pour UNG 20240727.csv'); + const ues = await parseDocument('scripts/seed/dfp_data.csv'); await prisma.ueof.updateMany({ data: { available: false }, }); From 624040fed354960c96846d1517621f62e1814fbb Mon Sep 17 00:00:00 2001 From: AlbanSdl Date: Mon, 9 Sep 2024 21:32:08 +0200 Subject: [PATCH 12/14] doc(ue): add semester codes to doc --- docs/doc_developers/api/ues.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/doc_developers/api/ues.md b/docs/doc_developers/api/ues.md index bdd3f6a..df3053e 100644 --- a/docs/doc_developers/api/ues.md +++ b/docs/doc_developers/api/ues.md @@ -58,3 +58,15 @@ Pour définir aux mieux les UEs, nous utilisont plusieurs structures de données - **`UeStarCriterion`**. Les critères sur lesquels les utilisateurs peuvent évaluer une UE. Ces critères ne sont pas fixes et pourront être modifiés par les administrateurs du site s'ils se rendent compte qu'ils sont mal définis. - **`UeStarVote`**, c'est le vote d'un utilisateur pour une UE donnée et un critère donné. Il ne peut y avoir qu'un seul vote par Utilisateur & Ue & Critère. - **`UeWorkTime`**. Cette structure rassemble les heures de travail estimé pour effectuer l'UE. Il n'y a plus de nombre d'heures pour la réalisation d'un projet éventuel mais l'indication porte désormais sur le fait qu'il y ait un projet ou non. + +## Les Semestres + +La DFP a intégré certaines formations au guide des UEs qui n'ont pas lieu sur les semestres classiques d'automne et de printemps. Il a donc fallu intégrer de nouveaux laps de temps au site étudiant. Voici une correspondance entre les codes et leur signification : + +| Sigle | Signification | +| ----- | ----------------------------- | +| A24 | Semestre d'automne 2024 | +| P25 | Semestre de printemps 2025 | +| H25 | Intersemestre d'hiver 2025 | +| E25 | Intersemestre d'été 2025 | +| U24 | Année universitaire 2024-2025 | From fda50561647b8db3c4e6aec3845361c9cd876313 Mon Sep 17 00:00:00 2001 From: AlbanSdl Date: Thu, 3 Oct 2024 00:53:28 +0200 Subject: [PATCH 13/14] fix(ues): move starvote relation from ue to ueof --- docs/doc_developers/api/ues.md | 10 +-- migration/etuutt_old/make-migration.ts | 2 - prisma/schema.prisma | 88 +++++++++++----------- prisma/seed/modules/ue.seed.ts | 2 - scripts/seed/ue.ts | 7 -- src/array.ts | 16 ++++ src/prisma/types.ts | 2 +- src/ue/interfaces/ue.interface.ts | 100 +++++++++++++------------ src/ue/ue.controller.ts | 56 +++++++++++++- test/declarations.ts | 4 +- test/utils/fakedb.ts | 10 +-- 11 files changed, 176 insertions(+), 121 deletions(-) diff --git a/docs/doc_developers/api/ues.md b/docs/doc_developers/api/ues.md index df3053e..1ca9cef 100644 --- a/docs/doc_developers/api/ues.md +++ b/docs/doc_developers/api/ues.md @@ -30,7 +30,7 @@ C'est dans cet état d'esprit que nous menons la refonte du site étudiant : > Ces commentaires sont bien entendu **modérés** et ne sont visibles que par les étudiants (et anciens étudiants). \ > ⚠️ Il faudra impérativement **avoir fait l'UE** pour pouvoir laisser un commentaire. Si un étudiant crée sont compte EtuUTT après le semestre où il a suivi l'UE1, il ne pourra pas s'exprimer sur cette UE. - les utilisateurs peuvent désormais réagir à un commentaire d'UE en y répondant. Cela peut permettre de nuancer certains propos spécifiques ou au contraire de les développer par un argument supplémentaire ! -- les étudiants peuvent désormais _upvote_ les commentaires qui leurs semblent les plus pertinents. Ils remontront dans la liste des commentaires. +- les étudiants peuvent désormais _upvote_ les commentaires qui leurs semblent les plus pertinents. Ils remonteront dans la liste des commentaires. > ❓ Les commentaires sont triés en fonction de leur nombre d'upvotes mais aussi de leur ancienneté et de l'UEOF à laquelle ils sont liés - Pour que les étudiants donnent plus leur avis sur leurs cours, il est désormais possible de noter avec une note sur 5 les UEs déjà suivies1 sur des critères prédéfinis par les administrateurs. Il s'agira principalement d'évaluer la qualité des contenus et de l'enseignement. - la recherche d'une UE affichera toutes les différentes version de l'UE (dans toutes ses langues d'enseignement et les différents semestres où elle est disponible). Les UEs sont désormais rattachées à aux branches au profil desquelles elles comptent. Cela permettra d'identifier facilement les UEs hors profil. @@ -42,19 +42,19 @@ Les objectifs de la refonte du guide des UEs étant présentés, nous allons pou ## Les structures -Pour définir aux mieux les UEs, nous utilisont plusieurs structures de données. Passons-les en revue afin de comprendre l'usage de chacune d'elles : +Pour définir aux mieux les UEs, nous utilisons plusieurs structures de données. Passons-les en revue afin de comprendre l'usage de chacune d'elles : - **`Ue`**, il s'agit d'une UE au sens de la refonte du SIEP. C'est à dire qu'elle ne contient elle même que peu d'informations : sa date de création et de dernière modification ainsi que les notes des utilisateurs. Ce sont les `Ueof` liées à cette UE qui contiendront la majorité des détails du guide des UEs. - **`UeAlias`**. Certaines UEs n'existent plus. C'est le cas de `MATH03` ou de `EC01` par exemple. Il faut donc gérer les cas où certaines UEs auraient des prérequis qui n'existent plus ! Pour des UEs qui ont changé de nom comme `MT03` il suffit de mettre en place une "redirection" et pour les UEs qui n'existent plus du tout comme `EC01` il faut également le notifier pour faire disparaître la référence. C'est tout l'intérêt de cette table. -- **`Ueof`**, c'est la structure qui définit une UEOF. Elle va donc être liée à toutes les informations du guide des UEs la concernant : temps de travail, crédits, branche associée, prérequis, commentaires, annales... Pour un soucis de lisibilité, cette structure n'inclut pas les données telles que la langue d'enseignement, les mineurs, le programme et les objectifs. On utilisera `UeInfo` à la place. +- **`Ueof`**, c'est la structure qui définit une UEOF. Elle va donc être liée à toutes les informations du guide des UEs la concernant : temps de travail, crédits, branche associée, prérequis, commentaires, annales... Pour un soucis de lisibilité, cette structure n'inclut pas les données telles que la langue d'enseignement, les mineurs, le programme et les objectifs. On utilisera `UeofInfo` à la place. - **`UeAnnal`**, il s'agit de la structure qui représente un sujet d'examen. Fait référence à l'utilisateur qui a envoyé le sujet, à l'UEOF, au semestre ainsi qu'au type d'examen (médian, final, etc) - **`UeAnnalType`**, c'est le type d'examen d'une annale. Ces valeurs sont rentrées par les administrateurs du site qui seront dans la possibilité d'ajouter de nouveaux types d'examens s'ils se rendent compte que les valeurs précédentes ne sont pas assez précises. - **`UeComment`**. Le commentaire d'un étudiant au sujet du cours qu'il a suivi. Ce commentaire est rattaché à l'UEOF correspondante afin d'afficher quelle variante de l'UE a été suivie mais sera affiché pour toutes les UEOFs de l'UE. Le commentaire peut être posté en anonyme mais l'auteur sera tout de même visible pour la modération. Egalement, les commentaires ne sont pas supprimés directement lorsque l'utilisateur choisit de les supprimer. Il y aura un petit délai permettant d'annuler la suppression ou de procéder à des actions de modération si nécessaire. - **`UeCommentReply`**, c'est la réponse d'un utilisateur à un commentaire d'une UE. -- **`UeCommentUpvote`** il s'agit d'un upvote donné par n'importe quel étudiant à un commentaire de n'importe quelle UE s'il la trouvé instructif et/ou pertinent. +- **`UeCommentUpvote`** il s'agit d'un upvote donné par n'importe quel étudiant à un commentaire de n'importe quelle UE s'il l'a trouvé instructif et/ou pertinent. - **`UeCredit`**. C'est le nombre de crédits obtenus par un étudiant qui réalise l'UE. C'est ici qu'apparaît les branches et filières auxquelles cette UE est liée. En effet, il arrive qu'une même UE donne des crédits d'un type différent selon si l'étudiant est en master ou en cycle ingénieur. - **`UeCreditCategory`**. Il s'agit des différents types de crédits ECTS: `CS`, `TM`, etc -- **`UeInfo`**. C'est ici que figurent toutes les informations détaillées des UEOFs : leur programme, les objectifs, la langue d'enseignement, les mineurs, etc. Cela permet de ne pas surcharger la structure des UEOFs. +- **`UeofInfo`**. C'est ici que figurent toutes les informations détaillées des UEOFs : leur programme, les objectifs, la langue d'enseignement, les mineurs, etc. Cela permet de ne pas surcharger la structure des UEOFs. - **`UeStarCriterion`**. Les critères sur lesquels les utilisateurs peuvent évaluer une UE. Ces critères ne sont pas fixes et pourront être modifiés par les administrateurs du site s'ils se rendent compte qu'ils sont mal définis. - **`UeStarVote`**, c'est le vote d'un utilisateur pour une UE donnée et un critère donné. Il ne peut y avoir qu'un seul vote par Utilisateur & Ue & Critère. - **`UeWorkTime`**. Cette structure rassemble les heures de travail estimé pour effectuer l'UE. Il n'y a plus de nombre d'heures pour la réalisation d'un projet éventuel mais l'indication porte désormais sur le fait qu'il y ait un projet ou non. diff --git a/migration/etuutt_old/make-migration.ts b/migration/etuutt_old/make-migration.ts index c279afd..28a8159 100644 --- a/migration/etuutt_old/make-migration.ts +++ b/migration/etuutt_old/make-migration.ts @@ -94,8 +94,6 @@ const prisma = _prisma.$extends({ data: await _prisma.ue.create({ data: { code: ueCode, - creationYear: new Date('2023-01-01').getTime() / 1000, - updateYear: new Date('2023-01-01').getTime() / 1000, ueofs: { connectOrCreate: { where: { diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 8032ce8..54ea5e5 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -190,35 +190,33 @@ model Translation { de String? @db.Text zh String? @db.Text - assoDescriptionShort Asso? @relation("descriptionShortTranslation") assoDescription Asso? @relation("descriptionTranslation") + assoDescriptionShort Asso? @relation("descriptionShortTranslation") assoMessageTitle AssoMessage? @relation("titleTranslation") assoMessageTitleBody AssoMessage? @relation("bodyTranslation") eventDescription Event? @relation("descriptionTranslation") eventTitle Event? @relation("titleTranslation") - userPermissionName UserPermission? @relation("userPermissionName") + ueofInfo UeofInfo? @relation("ueofInfoObjectivesTranslation") + ueofInfoPrograms UeofInfo? @relation("ueofInfoProgramTranslation") userPermissionDescription UserPermission? @relation("userPermissionDescription") + userPermissionName UserPermission? @relation("userPermissionName") annalReportReasonDescriptions UeAnnalReportReason? - commentReportReasonDescriptions UeCommentReportReason? - starCriterionDescriptions UeStarCriterion? branchDescriptions UTTBranch? branchOptionDescriptions UTTBranchOption? + commentReportReasonDescriptions UeCommentReportReason? formationDescriptions UTTFormation? formationFollowingMethodDescriptions UTTFormationFollowingMethod? + starCriterionDescriptions UeStarCriterion? ueNames Ueof? - ueInfo UeInfo? @relation("ueInfoObjectivesTranslation") - ueInfoPrograms UeInfo? @relation("ueInfoProgramTranslation") } model Ue { - code String @id @db.VarChar(8) - createdAt DateTime @default(now()) - creationYear Int - updateYear Int - starVotes UeStarVote[] - ueofs Ueof[] - subsequentUes Ueof[] @relation("ueRequirements") + code String @id @db.VarChar(8) + createdAt DateTime @default(now()) + + subsequentUes Ueof[] @relation("ueRequirements") aliases UeAlias[] + ueofs Ueof[] } model UeAlias { @@ -229,26 +227,28 @@ model UeAlias { } model Ueof { - code String @id @db.VarChar(20) - siepId Int @unique - ueId String - nameTranslationId String @unique - ueInfoId String @unique + code String @id @db.VarChar(20) + siepId Int @unique + + available Boolean @default(false) createdAt DateTime @default(now()) + nameTranslationId String @unique + ueId String + ueofInfoId String @unique updatedAt DateTime @updatedAt - available Boolean @default(false) - ue Ue @relation(fields: [ueId], references: [code]) - credits UeCredit[] + info UeofInfo @relation(fields: [ueofInfoId], references: [id], onDelete: Cascade) name Translation @relation(fields: [nameTranslationId], references: [id], onDelete: Cascade) - info UeInfo @relation(fields: [ueInfoId], references: [id], onDelete: Cascade) requirements Ue[] @relation(name: "ueRequirements") - openSemester Semester[] - workTime UeWorkTime? + ue Ue @relation(fields: [ueId], references: [code]) + annals UeAnnal[] + comments UeComment[] courses UeCourse[] + credits UeCredit[] + openSemester Semester[] + starVotes UeStarVote[] usersSubscriptions UserUeSubscription[] - comments UeComment[] - annals UeAnnal[] + workTime UeWorkTime? } model UeAnnal { @@ -258,16 +258,16 @@ model UeAnnal { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt deletedAt DateTime? - validatedAt DateTime? - ueofId String - senderId String? semesterId String + senderId String? typeId String + ueofId String + validatedAt DateTime? ueof Ueof @relation(fields: [ueofId], references: [code], onDelete: Cascade) + semester Semester @relation(fields: [semesterId], references: [code]) sender User? @relation(fields: [senderId], references: [id], onDelete: SetNull) type UeAnnalType @relation(fields: [typeId], references: [id]) - semester Semester @relation(fields: [semesterId], references: [code]) reports UeAnnalReport[] } @@ -284,12 +284,12 @@ model UeAnnalReport { createdAt DateTime @default(now()) mitigated Boolean @default(false) annalId String - userId String? reasonId String + userId String? annal UeAnnal @relation(fields: [annalId], references: [id], onDelete: Cascade) - user User? @relation(fields: [userId], references: [id], onDelete: SetNull) reason UeAnnalReportReason @relation(fields: [reasonId], references: [name]) + user User? @relation(fields: [userId], references: [id], onDelete: SetNull) @@unique([userId, annalId, reasonId]) // Prevent from spams } @@ -316,9 +316,9 @@ model UeComment { authorId String? semesterId String - ueof Ueof @relation(fields: [ueofId], references: [code], onDelete: Cascade) author User? @relation(fields: [authorId], references: [id], onDelete: SetNull) semester Semester @relation(fields: [semesterId], references: [code]) + ueof Ueof @relation(fields: [ueofId], references: [code], onDelete: Cascade) answers UeCommentReply[] reports UeCommentReport[] upvotes UeCommentUpvote[] @@ -454,16 +454,16 @@ model UeCreditCategory { ueCredits UeCredit[] } -model UeInfo { +model UeofInfo { id String @id @default(uuid()) minors String? @db.Text language String? @db.Text objectivesTranslationId String? @unique programTranslationId String? @unique + objectives Translation? @relation("ueofInfoObjectivesTranslation", fields: [objectivesTranslationId], references: [id], onDelete: Cascade) + program Translation? @relation("ueofInfoProgramTranslation", fields: [programTranslationId], references: [id], onDelete: Cascade) ueof Ueof? - objectives Translation? @relation("ueInfoObjectivesTranslation", fields: [objectivesTranslationId], references: [id], onDelete: Cascade) - program Translation? @relation("ueInfoProgramTranslation", fields: [programTranslationId], references: [id], onDelete: Cascade) } model UeStarCriterion { @@ -479,15 +479,15 @@ model UeStarVote { id String @id @default(uuid()) value Int @db.SmallInt createdAt DateTime @default(now()) - ueId String criterionId String + ueofId String userId String? - ue Ue @relation(fields: [ueId], references: [code], onDelete: Cascade) criterion UeStarCriterion @relation(fields: [criterionId], references: [id], onDelete: Cascade) + ueof Ueof @relation(fields: [ueofId], references: [code], onDelete: Cascade) user User? @relation(fields: [userId], references: [id], onDelete: SetNull) - @@unique([ueId, userId, criterionId]) + @@unique([ueofId, userId, criterionId]) } model UeWorkTime { @@ -540,19 +540,19 @@ model User { socialNetworkId String @unique privacyId String @unique + socialNetwork UserSocialNetwork @relation(fields: [socialNetworkId], references: [id]) + rgpd UserRGPD @relation(fields: [rgpdId], references: [id]) + preference UserPreference @relation(fields: [preferenceId], references: [id]) + infos UserInfos @relation(fields: [infosId], references: [id]) + mailsPhones UserMailsPhones @relation(fields: [mailsPhonesId], references: [id]) userType UserType timestamps UserTimestamps? - socialNetwork UserSocialNetwork @relation(fields: [socialNetworkId], references: [id]) bans UserBan[] - rgpd UserRGPD @relation(fields: [rgpdId], references: [id]) bdeContributions UserBDEContribution[] assoMembership AssoMembership[] branchSubscriptions UserBranchSubscription[] formation UserFormation? - preference UserPreference @relation(fields: [preferenceId], references: [id]) - infos UserInfos @relation(fields: [infosId], references: [id]) addresses UserAddress[] - mailsPhones UserMailsPhones @relation(fields: [mailsPhonesId], references: [id]) otherAttributes UserOtherAttributValue[] UesSubscriptions UserUeSubscription[] UeStarVotes UeStarVote[] diff --git a/prisma/seed/modules/ue.seed.ts b/prisma/seed/modules/ue.seed.ts index cde4ad5..3efc1fe 100644 --- a/prisma/seed/modules/ue.seed.ts +++ b/prisma/seed/modules/ue.seed.ts @@ -24,8 +24,6 @@ export default function ueSeed( prisma.ue.create({ data: { code, - creationYear: faker.datatype.number({ min: 2000, max: 2024 }), - updateYear: faker.datatype.number({ min: 2001, max: 2024 }), ueofs: { create: { code: `${code}${OF_SUFFIX}`, diff --git a/scripts/seed/ue.ts b/scripts/seed/ue.ts index 996a957..b8402db 100644 --- a/scripts/seed/ue.ts +++ b/scripts/seed/ue.ts @@ -191,8 +191,6 @@ async function main() { where: { code: ue.code }, create: { code: ue.code, - creationYear: 2000 + Number(ue.ueof_code.slice(-2)), - updateYear: 2000 + Number(ue.ueof_code.slice(-2)), }, }, }, @@ -320,11 +318,6 @@ async function main() { code: ueof.ueof_code, }, data: { - ue: { - update: { - updateYear: 2000 + Number(ueof.ueof_code.slice(-2)), - }, - }, requirements: { connect: ueof.requirements .map((code) => { diff --git a/src/array.ts b/src/array.ts index a33b088..78780bd 100644 --- a/src/array.ts +++ b/src/array.ts @@ -1,5 +1,9 @@ declare global { interface Array { + /** + * Groups the current array by a key, using a mapper function. + */ + groupyBy(keyMapper: (entity: T) => K): { [key in K]: T[] }; /** * Sorts the current array and returns it. * Array is sorted based on a mapper function, that returns in order the values by which to sort the array. @@ -25,6 +29,18 @@ declare global { } } +Array.prototype.groupyBy = function ( + this: Array, + keyMapper: (entity: T) => K, +) { + return this.reduce((acc, entity) => { + const key = keyMapper(entity); + if (!acc[key]) acc[key] = []; + acc[key].push(entity); + return acc; + }, {} as { [key in K]: T[] }); +}; + Array.prototype.mappedSort = function (this: Array, mapper: (e: T) => any[] | any) { return this.sort((a, b) => { const aMapped = mapper(a); diff --git a/src/prisma/types.ts b/src/prisma/types.ts index 6021916..6f2ec33 100644 --- a/src/prisma/types.ts +++ b/src/prisma/types.ts @@ -13,7 +13,7 @@ export { Ue as RawUe, UeCredit as RawUeCredit, UeCreditCategory as RawCreditCategory, - UeInfo as RawUeInfo, + UeofInfo as RawUeofInfo, UeWorkTime as RawUeWorkTime, UeStarVote as RawUeStarVote, UeStarCriterion as RawUeStarCriterion, diff --git a/src/ue/interfaces/ue.interface.ts b/src/ue/interfaces/ue.interface.ts index 3cb9631..6c8e50b 100644 --- a/src/ue/interfaces/ue.interface.ts +++ b/src/ue/interfaces/ue.interface.ts @@ -1,19 +1,16 @@ import { Prisma, PrismaClient } from '@prisma/client'; import { generateCustomModel } from '../../prisma/prisma.service'; -import { translationSelect } from '../../utils'; +import { omit, translationSelect } from '../../utils'; const UE_SELECT_FILTER = { select: { code: true, - updateYear: true, - creationYear: true, subsequentUes: true, ueofs: { - where: { - available: true, - }, select: { + code: true, name: translationSelect, + available: true, siepId: true, requirements: { select: { @@ -71,16 +68,16 @@ const UE_SELECT_FILTER = { }, }, }, - }, - }, - starVotes: { - select: { - criterionId: true, - createdAt: true, - value: true, - }, - orderBy: { - criterionId: 'asc', + starVotes: { + select: { + criterionId: true, + createdAt: true, + value: true, + }, + orderBy: { + criterionId: 'asc', + }, + }, }, }, }, @@ -90,8 +87,18 @@ const UE_SELECT_FILTER = { } as const satisfies Prisma.UeFindManyArgs; export type UnformattedUe = Prisma.UeGetPayload; -export type Ue = Omit & { - starVotes: { [key: string]: number }; +export type Ue = Omit & { + ueofs: Omit[]; + starVotes: { + [key: string]: UeStarVoteEntry[]; + }; + creationYear: number; + updateYear: number; +}; +export type UeStarVoteEntry = { + createdAt: Date; + ueofCode: string; + value: number; }; export function generateCustomUeModel(prisma: PrismaClient) { @@ -101,42 +108,37 @@ export function generateCustomUeModel(prisma: PrismaClient) { function formatUe(_: PrismaClient, ue: UnformattedUe): Ue { // We store rates in a object where the key is the criterion id and the value is a list ratings const starVoteCriteria: { - [key: string]: { - createdAt: Date; - value: number; - }[]; + [key: string]: UeStarVoteEntry[]; } = {}; - for (const starVote of ue.starVotes) { - if (starVote.criterionId in starVoteCriteria) - starVoteCriteria[starVote.criterionId].push({ - createdAt: starVote.createdAt, - value: starVote.value, - }); - else - starVoteCriteria[starVote.criterionId] = [ - { + for (const ueof of ue.ueofs) { + for (const starVote of ueof.starVotes) { + if (starVote.criterionId in starVoteCriteria) + starVoteCriteria[starVote.criterionId].push({ createdAt: starVote.createdAt, + ueofCode: ueof.code, value: starVote.value, - }, - ]; + }); + else + starVoteCriteria[starVote.criterionId] = [ + { + createdAt: starVote.createdAt, + ueofCode: ueof.code, + value: starVote.value, + }, + ]; + } } - // Compute ratings for each criterion, using an exponential decay function - // And turn semester into their respective code. + // Compute creationYear and updateYear + const ueofYears = ue.ueofs + .map((ueof) => Number(ueof.code.match(/\d+$/)?.[0])) + .filter((ueof) => ueof) + .sort(); + return { ...ue, - starVotes: Object.fromEntries(Object.entries(starVoteCriteria).map(([key, entry]) => [key, computeRate(entry)])), + ueofs: ue.ueofs.filter((ueof) => ueof.available).map(omit('available')), + starVotes: starVoteCriteria, + creationYear: 2000 + ueofYears[0], + updateYear: 2000 + ueofYears[ueofYears.length - 1], }; } - -export function computeRate(rates: Array<{ createdAt: Date; value: number }>) { - let coefficients = 0; - let ponderation = 0; - const newestCreationTimestamp = rates.reduce((acc, rate) => Math.max(rate.createdAt.getTime(), acc), 0); - for (const { value, createdAt } of rates) { - const dt = (newestCreationTimestamp - createdAt.getTime()) / 1000; - const dp = Math.exp(-dt / 10e7); - ponderation += dp * value; - coefficients += dp; - } - return Math.round((ponderation / coefficients) * 10) / 10; -} diff --git a/src/ue/ue.controller.ts b/src/ue/ue.controller.ts index 3ad0241..95b3a97 100644 --- a/src/ue/ue.controller.ts +++ b/src/ue/ue.controller.ts @@ -8,7 +8,7 @@ import { User } from '../users/interfaces/user.interface'; import { UUIDParam } from '../app.pipe'; import { AppException, ERROR_CODE } from '../exceptions'; import { UeRateDto } from './dto/ue-rate.dto'; -import { Ue } from './interfaces/ue.interface'; +import { Ue, UeStarVoteEntry } from './interfaces/ue.interface'; import { Language, UserType } from '@prisma/client'; import { Translation } from '../prisma/types'; import { omit } from '../utils'; @@ -150,6 +150,7 @@ export class UeController { creationYear: ue.creationYear, updateYear: ue.updateYear, ofs: ue.ueofs.map((ueof) => ({ + code: ueof.code, name: ueof.name, credits: ueof.credits.map((c) => ({ credits: c.credits, @@ -186,10 +187,55 @@ export class UeController { project: ueof.workTime.project, internship: ueof.workTime.internship, }, + starVotes: { + // Compute ratings for each criterion, using an exponential decay function + ...Object.fromEntries( + Object.entries(ue.starVotes).map(([criterion, rates]) => [criterion, this.computeRate(rates, ueof.code)]), + ), + voteCount: Math.max(...Object.values(ue.starVotes).map((rates) => rates.length)), + }, })), - starVotes: ue.starVotes, }; } + + private computeRate(rates: UeStarVoteEntry[], targetUeofCode: string) { + function aggregate( + entities: T[], + mapper: (entity: T) => [key: number, value: number], + dtModifier = 1, + decay = 10, + ponderationMultiplier: (entity: T) => number = () => 1, + ) { + let coefficients = 0; + let ponderation = 0; + const latestKey = Math.max(...entities.map((entity) => mapper(entity)[0])); + for (const entity of entities) { + const [key, value] = mapper(entity); + const dt = (latestKey - key) / dtModifier; + const dp = Math.exp(-dt / decay) * ponderationMultiplier(entity); + ponderation += dp * value; + coefficients += dp; + } + return Math.round((ponderation / coefficients) * 10) / 10; + } + // Ponderate the rates of each ueof + const ueofRates = Object.entries(rates.groupyBy((rate) => rate.ueofCode)).map( + ([ueofCode, rates]) => + [ + ueofCode, + Number(ueofCode.slice(0, -2)) || 0, + aggregate(rates, (ent) => [ent.createdAt.getTime(), ent.value], 1000, 10e7), + ] as const, + ); + // Ponderate the rates depending on the ueof + return aggregate( + ueofRates, + (ent) => [ent[1], ent[2]], + 1, + 10, + ([ueofCode]) => (ueofCode === targetUeofCode ? 2 : ueofCode.startsWith(targetUeofCode.slice(0, -3)) ? 1 : 0.5), + ); + } } export type UeOverview = { @@ -229,6 +275,7 @@ export type UeDetail = { creationYear: number; updateYear: number; ofs: { + code: string; name: Translation; credits: Array<{ credits: number; @@ -266,5 +313,8 @@ export type UeDetail = { internship: number; }; }[]; - starVotes?: { [criterionId: string]: number }; + starVotes?: { + [criterionId: string]: number; + voteCount: number; + }; }; diff --git a/test/declarations.ts b/test/declarations.ts index 8a5c1ce..ded6233 100644 --- a/test/declarations.ts +++ b/test/declarations.ts @@ -75,8 +75,8 @@ Spec.prototype.expectUe = function (ue: FakeUe, rates: Array<{ criterionId: stri return (this).expectStatus(HttpStatus.OK).expectJsonMatchStrict( deepDateToString({ code: ue.code, - creationYear: ue.creationYear, - updateYear: ue.updateYear, + creationYear: 2000 + Number(ue.ueofCode.match(/\d+$/)?.[0] ?? 23), + updateYear: 2000 + Number(ue.ueofCode.match(/\d+$/)?.[0] ?? 23), ...(rates ? { starVotes: Object.fromEntries(rates.map((rate) => [rate.criterionId, rate.value])) } : {}), ofs: [ { diff --git a/test/utils/fakedb.ts b/test/utils/fakedb.ts index 8de8aef..28a3dbf 100644 --- a/test/utils/fakedb.ts +++ b/test/utils/fakedb.ts @@ -16,7 +16,7 @@ import { RawUeCommentReply, RawUeCommentUpvote, RawUeCredit, - RawUeInfo, + RawUeofInfo, RawUeStarCriterion, RawUeStarVote, RawUeWorkTime, @@ -78,7 +78,7 @@ export type FakeAsso = Partial< } >; export type FakeSemester = Partial; -export type FakeUe = Partial> & { +export type FakeUe = Partial> & { ueofCode?: string; name?: Partial; siepId?: number; @@ -87,7 +87,7 @@ export type FakeUe = Partial> & { branchOptions?: Partial[]; })[]; info?: Partial< - Omit & { + Omit & { objectives: Partial; program: Partial; requirements: { code: string }[]; @@ -677,8 +677,6 @@ export const createUe = entityFaker( { code: faker.db.ue.code, siepId: () => faker.datatype.number({ min: 100000, max: 999999 }), - creationYear: () => faker.datatype.number({ min: 2000, max: 2024 }), - updateYear: () => faker.datatype.number({ min: 2001, max: 2024 }), name: () => faker.db.translation(faker.name.jobTitle), credits: [ { @@ -798,7 +796,7 @@ export const createUe = entityFaker( .then((ue) => ({ ...omit(ue, 'ueofs'), ueofCode: ue.ueofs[0].code, - ...omit(ue.ueofs[0], 'code', 'ueId', 'ueInfoId', 'openSemester', 'nameTranslationId', 'requirements'), + ...omit(ue.ueofs[0], 'code', 'ueId', 'ueofInfoId', 'openSemester', 'nameTranslationId', 'requirements'), info: { ...omit(ue.ueofs[0].info, 'objectivesTranslationId', 'programTranslationId'), requirements: ue.ueofs[0].requirements, From e3688518d059504c3803017493ee088b067a3aac Mon Sep 17 00:00:00 2001 From: AlbanSdl Date: Wed, 9 Oct 2024 21:26:47 +0200 Subject: [PATCH 14/14] fix: update rating behaviour --- migration/etuutt_old/modules/ue.ts | 4 +- prisma/schema.prisma | 64 ++++++------ prisma/seed/modules/ueStarVotes.seed.ts | 3 +- scripts/seed/ue.ts | 27 +++-- src/exceptions.ts | 9 +- src/types.d.ts | 3 + src/ue/annals/annals.controller.ts | 31 +++--- src/ue/annals/annals.service.ts | 14 +-- src/ue/annals/dto/create-annal.dto.ts | 2 + src/ue/comments/comments.controller.ts | 23 +++-- src/ue/comments/comments.service.ts | 4 +- src/ue/ue.controller.ts | 69 ++++++------- src/ue/ue.service.ts | 130 ++++++++++++++---------- test/declarations.ts | 2 +- test/e2e/ue/delete-rate.e2e-spec.ts | 2 +- test/e2e/ue/get.e2e-spec.ts | 12 ++- test/utils/fakedb.ts | 10 +- 17 files changed, 224 insertions(+), 185 deletions(-) diff --git a/migration/etuutt_old/modules/ue.ts b/migration/etuutt_old/modules/ue.ts index 2f1ff3f..4dbdb53 100644 --- a/migration/etuutt_old/modules/ue.ts +++ b/migration/etuutt_old/modules/ue.ts @@ -2,9 +2,9 @@ import { getOperationResults, PrismaOperationResult, QueryFunction } from '../ma import { PrismaClient } from '../make-migration'; import { RawUe } from '../../../src/prisma/types'; -export function findLegacyUeofName(ueCode: string, commentaire: string) { +export function findLegacyUeofName(ueCode: string, comment: string) { let LOCATION = 'TRO'; - if (commentaire.match(/UE réalisée à Reims/)) LOCATION = 'REI'; + if (comment.match(/UE réalisée à Reims/)) LOCATION = 'REI'; let UECODE = ueCode; let LANG = 'FR'; if (ueCode.length > 4 && ueCode.slice(-1).match(/[APR]/)) { diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 54ea5e5..0c0a70b 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -264,10 +264,10 @@ model UeAnnal { ueofId String validatedAt DateTime? - ueof Ueof @relation(fields: [ueofId], references: [code], onDelete: Cascade) semester Semester @relation(fields: [semesterId], references: [code]) sender User? @relation(fields: [senderId], references: [id], onDelete: SetNull) type UeAnnalType @relation(fields: [typeId], references: [id]) + ueof Ueof @relation(fields: [ueofId], references: [code], onDelete: Cascade) reports UeAnnalReport[] } @@ -280,10 +280,10 @@ model UeAnnalType { model UeAnnalReport { id String @id @default(uuid()) + annalId String body String? @db.Text createdAt DateTime @default(now()) mitigated Boolean @default(false) - annalId String reasonId String userId String? @@ -311,10 +311,10 @@ model UeComment { // Removed @updatedAt because the property is used to display the last datetime the content of the comment was altered on deletedAt DateTime? validatedAt DateTime? - lastValidatedBody String? - ueofId String authorId String? + lastValidatedBody String? semesterId String + ueofId String author User? @relation(fields: [authorId], references: [id], onDelete: SetNull) semester Semester @relation(fields: [semesterId], references: [code]) @@ -334,8 +334,8 @@ model UeCommentReply { commentId String authorId String? - comment UeComment @relation(fields: [commentId], references: [id], onDelete: Cascade) author User? @relation(fields: [authorId], references: [id], onDelete: SetNull) + comment UeComment @relation(fields: [commentId], references: [id], onDelete: Cascade) reports UeCommentReplyReport[] } @@ -345,12 +345,12 @@ model UeCommentReport { createdAt DateTime @default(now()) mitigated Boolean @default(false) commentId String - userId String reasonId String + userId String comment UeComment @relation(fields: [commentId], references: [id], onDelete: Cascade) - user User @relation(fields: [userId], references: [id], onDelete: Cascade) reason UeCommentReportReason @relation(fields: [reasonId], references: [name], onDelete: Cascade) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([userId, commentId, reasonId]) // Prevent from spam } @@ -360,13 +360,13 @@ model UeCommentReplyReport { body String @db.Text createdAt DateTime @default(now()) mitigated Boolean @default(false) + reasonId String replyId String userId String - reasonId String + reason UeCommentReportReason @relation(fields: [reasonId], references: [name], onDelete: Cascade) reply UeCommentReply @relation(fields: [replyId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) - reason UeCommentReportReason @relation(fields: [reasonId], references: [name], onDelete: Cascade) @@unique([userId, replyId, reasonId]) // Prevent from spam } @@ -376,46 +376,46 @@ model UeCommentReportReason { descriptionTranslationId String @unique descriptionTranslation Translation @relation(fields: [descriptionTranslationId], references: [id], onDelete: Cascade) - reports UeCommentReport[] replyReports UeCommentReplyReport[] + reports UeCommentReport[] } model UeCommentUpvote { id String @id @default(uuid()) - userId String? commentId String createdAt DateTime @default(now()) + userId String? - user User? @relation(fields: [userId], references: [id], onDelete: SetNull) comment UeComment @relation(fields: [commentId], references: [id], onDelete: Cascade) + user User? @relation(fields: [userId], references: [id], onDelete: SetNull) } model UeCourse { id String @id @default(uuid()) - type CourseType - room String @db.VarChar(50) createdAt DateTime @default(now()) - ueofId String + room String @db.VarChar(50) semesterId String + type CourseType + ueofId String - students User[] - ueof Ueof @relation(fields: [ueofId], references: [code]) - semester Semester @relation(fields: [semesterId], references: [code]) courseExchangesFrom UeCourseExchange[] @relation(name: "courseFrom") courseExchangesTo UeCourseExchange[] @relation(name: "courseTo") + semester Semester @relation(fields: [semesterId], references: [code]) + ueof Ueof @relation(fields: [ueofId], references: [code]) + students User[] timetableEntries TimetableEntry[] } model UeCourseExchange { id String @id @default(uuid()) - stillAvailable Boolean - body String? @db.Text - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - deletedAt DateTime? authorId String + body String? @db.Text courseFromId String courseToId String + createdAt DateTime @default(now()) + deletedAt DateTime? + stillAvailable Boolean + updatedAt DateTime @updatedAt author User @relation(fields: [authorId], references: [id]) courseFrom UeCourse @relation(name: "courseFrom", fields: [courseFromId], references: [id]) @@ -425,26 +425,26 @@ model UeCourseExchange { model UeCourseExchangeReply { id String @id @default(uuid()) + authorId String body String @db.Text createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt deletedAt DateTime? - authorId String exchangeId String + updatedAt DateTime @updatedAt author User @relation(fields: [authorId], references: [id]) exchange UeCourseExchange @relation(fields: [exchangeId], references: [id]) } model UeCredit { - id String @id @default(uuid()) - credits Int @db.SmallInt - ueofId String - categoryId String? - branchOptions UTTBranchOption[] + id String @id @default(uuid()) + categoryId String? + credits Int @db.SmallInt + ueofId String - ueof Ueof @relation(fields: [ueofId], references: [code], onDelete: Cascade) - category UeCreditCategory? @relation(fields: [categoryId], references: [code]) + category UeCreditCategory? @relation(fields: [categoryId], references: [code]) + ueof Ueof @relation(fields: [ueofId], references: [code], onDelete: Cascade) + branchOptions UTTBranchOption[] } model UeCreditCategory { diff --git a/prisma/seed/modules/ueStarVotes.seed.ts b/prisma/seed/modules/ueStarVotes.seed.ts index 28e577b..47174d6 100644 --- a/prisma/seed/modules/ueStarVotes.seed.ts +++ b/prisma/seed/modules/ueStarVotes.seed.ts @@ -1,5 +1,4 @@ import { RawUeStarVote, RawUeStarCriterion, RawUserUeSubscription } from '../../../src/prisma/types'; -import { OF_SUFFIX } from './ue.seed'; import { PrismaClient } from '@prisma/client'; import { faker } from '@faker-js/faker'; @@ -18,7 +17,7 @@ export default function ueStarVotesSeed( prisma.ueStarVote.create({ data: { user: { connect: { id: subscription.userId } }, - ue: { connect: { code: subscription.ueofId.slice(0, -OF_SUFFIX.length) } }, + ueof: { connect: { code: subscription.ueofId } }, criterion: { connect: { id: criterion.id } }, value: faker.datatype.number({ min: 1, max: 5 }), }, diff --git a/scripts/seed/ue.ts b/scripts/seed/ue.ts index b8402db..45c308e 100644 --- a/scripts/seed/ue.ts +++ b/scripts/seed/ue.ts @@ -52,7 +52,7 @@ async function findPadding(document: string) { async function parseDocument(document: string) { return new Promise(async (resolve, reject) => { - const ues: UEOF[] = []; + const ueofs: UEOF[] = []; const padding = await findPadding(document); createReadStream(document) .pipe( @@ -94,7 +94,7 @@ async function parseDocument(document: string) { reject(); }) .on('data', (ue) => - ues.push({ + ueofs.push({ ...ue, siep_id: Number(ue.siep_id), cm_hours: Number(ue.cm_hours) || 0, @@ -105,27 +105,26 @@ async function parseDocument(document: string) { internship_hours: Number(ue.internship_hours) || 0, credit_count: Number(ue.credit_count) || 0, requirements: ue.requirements ? [ue.requirements as unknown as string] : [], - semesters: ue.semesters ? (ue.semesters as unknown as string)?.split(/\s\/\s/g) : [], - minors: ue.minors ? (ue.minors as unknown as string)?.split(/\s\/\s/g) : [], - engineer_branch: ue.engineer_branch ? (ue.engineer_branch as unknown as string)?.split(/\s\/\s/g) : [], + semesters: ue.semesters ? (ue.semesters as unknown as string).split(/\s\/\s/g) : [], + minors: ue.minors ? (ue.minors as unknown as string).split(/\s\/\s/g) : [], + engineer_branch: ue.engineer_branch ? (ue.engineer_branch as unknown as string).split(/\s\/\s/g) : [], engineer_branch_option: ue.engineer_branch_option ? (ue.engineer_branch_option as unknown as string)?.split(/\s\/\s/g) : [], - master_branch: ue.master_branch ? (ue.master_branch as unknown as string)?.split(/\s\/\s/g) : [], + master_branch: ue.master_branch ? (ue.master_branch as unknown as string).split(/\s\/\s/g) : [], master_branch_option: ue.master_branch_option - ? (ue.master_branch_option as unknown as string)?.split(/\s\/\s/g) + ? (ue.master_branch_option as unknown as string).split(/\s\/\s/g) : [], }), ) .on('end', () => resolve( - ues.reduce((prev, ueof) => { - // Data has not been merged in the CSV for the field `requirements`, we need to merge it manually - const existing = prev.find((ue) => ue.ueof_code === ueof.ueof_code); - if (!existing) return [...prev, ueof]; - existing.requirements.push(...ueof.requirements); - return prev; - }, []), + // A single UEOF can appear multiple times in the CSV with a different requirement + // We group the UEOFs by their code and merge the requirements + Object.values(ueofs.groupyBy((ueof) => ueof.ueof_code)).map((duplicates) => ({ + ...duplicates[0], + requirements: duplicates.flatMap((ueof) => ueof.requirements), + })), ), ); }); diff --git a/src/exceptions.ts b/src/exceptions.ts index 7b48f03..cad5d1b 100644 --- a/src/exceptions.ts +++ b/src/exceptions.ts @@ -53,9 +53,10 @@ export const enum ERROR_CODE { NOT_REPLY_AUTHOR = 4223, IS_COMMENT_AUTHOR = 4224, GROUP_NOT_PART_OF_ENTRY = 4225, - NOT_ALREADY_RATED_UE = 4226, + NOT_ALREADY_RATED_UEOF = 4226, NOT_DONE_UE_IN_SEMESTER = 4227, NOT_ANNAL_SENDER = 4228, + NOT_ALREADY_DONE_UEOF = 4229, NO_SUCH_UE = 4401, NO_SUCH_COMMENT = 4402, NO_SUCH_REPLY = 4403, @@ -237,7 +238,7 @@ export const ErrorData = Object.freeze({ message: 'The group % is not part of the timetable entry %', httpCode: HttpStatus.FORBIDDEN, }, - [ERROR_CODE.NOT_ALREADY_RATED_UE]: { + [ERROR_CODE.NOT_ALREADY_RATED_UEOF]: { message: 'You must have rated the UE % (on criterion %) before deleting your rating', httpCode: HttpStatus.FORBIDDEN, }, @@ -249,6 +250,10 @@ export const ErrorData = Object.freeze({ message: 'You are not the sender of this annal', httpCode: HttpStatus.FORBIDDEN, }, + [ERROR_CODE.NOT_ALREADY_DONE_UEOF]: { + message: 'You must have done this UEOF before to perform this action', + httpCode: HttpStatus.FORBIDDEN, + }, [ERROR_CODE.NO_SUCH_UE]: { message: 'The UE % does not exist', httpCode: HttpStatus.NOT_FOUND, diff --git a/src/types.d.ts b/src/types.d.ts index aaa6df3..f6dce5f 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -19,3 +19,6 @@ declare type SetPartial = Omit & Partial> declare type RecursivelySetPartial = K extends `${infer K1}.${infer K2}` ? Omit & RecursivelySetPartial : SetPartial; + +/** Retrieves the type of items of an {@link Array} */ +declare type ItemType = T extends Array ? U : T; diff --git a/src/ue/annals/annals.controller.ts b/src/ue/annals/annals.controller.ts index 61a9b60..3283078 100644 --- a/src/ue/annals/annals.controller.ts +++ b/src/ue/annals/annals.controller.ts @@ -21,31 +21,32 @@ export class AnnalsController { @RequireUserType('STUDENT', 'FORMER_STUDENT') async getUeAnnalList(@Query() { ueCode }: GetFromUeCodeDto, @GetUser() user: User) { if (!(await this.ueService.doesUeExist(ueCode))) throw new AppException(ERROR_CODE.NO_SUCH_UE, ueCode); - return this.annalsService.getUEAnnalsList(user, ueCode, user.permissions.includes('annalModerator')); + return this.annalsService.getUeAnnalsList(user, ueCode, user.permissions.includes('annalModerator')); } @Post() @RequireUserType('STUDENT') async createUeAnnal(@Body() { ueCode, semester, typeId, ueof }: CreateAnnal, @GetUser() user: User) { if (ueof && !user.permissions.includes('annalUploader')) - throw new AppException(ERROR_CODE.PARAM_DOES_NOT_EXIST, 'ueof'); - if (!(await this.ueService.doesUeExist(ueCode))) throw new AppException(ERROR_CODE.NO_SUCH_UE, ueCode); + throw new AppException(ERROR_CODE.FORBIDDEN_NOT_ENOUGH_PERMISSIONS, 'annalUploader'); + if (!ueof && !(await this.ueService.doesUeExist(ueCode))) throw new AppException(ERROR_CODE.NO_SUCH_UE, ueCode); + if (ueof && !(await this.ueService.doesUeofExist(ueof))) throw new AppException(ERROR_CODE.NO_SUCH_UEOF, ueof); if (!(await this.annalsService.doesAnnalTypeExist(typeId))) throw new AppException(ERROR_CODE.NO_SUCH_ANNAL_TYPE); - try { - return await this.annalsService.createAnnalFile(user, { ueCode, semester, typeId }, ueof); - } catch (error) { - if (user.permissions.includes('annalUploader')) throw new AppException(ERROR_CODE.NO_SUCH_UEOF, ueof); + if ( + !(await this.ueService.hasUserAttended(ueCode, user.id, semester)) && + !user.permissions.includes('annalUploader') + ) throw new AppException(ERROR_CODE.NOT_DONE_UE_IN_SEMESTER, ueCode, semester); - } + return await this.annalsService.createAnnalFile(user, { ueCode, semester, typeId, ueof }); } @Get('metadata') @RequireUserType('STUDENT', 'FORMER_STUDENT') async getUeAnnalMetadata(@Query() { ueCode }: GetFromUeCodeDto, @GetUser() user: User) { if (!(await this.ueService.doesUeExist(ueCode))) throw new AppException(ERROR_CODE.NO_SUCH_UE, ueCode); - if (!(await this.ueService.hasAlreadyDoneThisUe(user.id, ueCode)) && !user.permissions.includes('annalUploader')) + if (!(await this.ueService.hasUserAttended(ueCode, user.id)) && !user.permissions.includes('annalUploader')) throw new AppException(ERROR_CODE.NOT_ALREADY_DONE_UE); - return this.annalsService.getUEAnnalMetadata(user, ueCode, user.permissions.includes('annalUploader')); + return this.annalsService.getUeAnnalMetadata(user, ueCode, user.permissions.includes('annalUploader')); } @Put(':annalId') @@ -61,10 +62,10 @@ export class AnnalsController { @Query() { rotate }: UploadAnnalDto, @GetUser() user: User, ) { - if (!(await this.annalsService.isUEAnnalSender(user.id, annalId))) + if (!(await this.annalsService.isUeAnnalSender(user.id, annalId))) throw new AppException(ERROR_CODE.NOT_ANNAL_SENDER); if ( - (await this.annalsService.getUEAnnal(annalId, user.id, user.permissions.includes('annalModerator'))).status !== + (await this.annalsService.getUeAnnal(annalId, user.id, user.permissions.includes('annalModerator'))).status !== CommentStatus.PROCESSING ) throw new AppException(ERROR_CODE.ANNAL_ALREADY_UPLOADED); @@ -80,7 +81,7 @@ export class AnnalsController { ) { if (!(await this.annalsService.isAnnalAccessible(user.id, annalId, user.permissions.includes('annalModerator')))) throw new AppException(ERROR_CODE.NO_SUCH_ANNAL, annalId); - const annalFile = await this.annalsService.getUEAnnalFile( + const annalFile = await this.annalsService.getUeAnnalFile( annalId, user.id, user.permissions.includes('annalModerator'), @@ -99,7 +100,7 @@ export class AnnalsController { async updateUeAnnal(@UUIDParam('annalId') annalId: string, @Body() body: UpdateAnnalDto, @GetUser() user: User) { if (!(await this.annalsService.isAnnalAccessible(user.id, annalId, user.permissions.includes('annalModerator')))) throw new AppException(ERROR_CODE.NO_SUCH_ANNAL, annalId); - if (!(await this.annalsService.isUEAnnalSender(user.id, annalId)) && !user.permissions.includes('annalModerator')) + if (!(await this.annalsService.isUeAnnalSender(user.id, annalId)) && !user.permissions.includes('annalModerator')) throw new AppException(ERROR_CODE.NOT_ANNAL_SENDER); return this.annalsService.updateAnnalMetadata(annalId, body); } @@ -109,7 +110,7 @@ export class AnnalsController { async deleteUeAnnal(@UUIDParam('annalId') annalId: string, @GetUser() user: User) { if (!(await this.annalsService.isAnnalAccessible(user.id, annalId, user.permissions.includes('annalModerator')))) throw new AppException(ERROR_CODE.NO_SUCH_ANNAL, annalId); - if (!(await this.annalsService.isUEAnnalSender(user.id, annalId)) && !user.permissions.includes('annalModerator')) + if (!(await this.annalsService.isUeAnnalSender(user.id, annalId)) && !user.permissions.includes('annalModerator')) throw new AppException(ERROR_CODE.NOT_ANNAL_SENDER); return this.annalsService.deleteAnnal(annalId); } diff --git a/src/ue/annals/annals.service.ts b/src/ue/annals/annals.service.ts index c356b09..91f002d 100644 --- a/src/ue/annals/annals.service.ts +++ b/src/ue/annals/annals.service.ts @@ -15,7 +15,7 @@ import { Semester } from '@prisma/client'; export class AnnalsService { constructor(readonly prisma: PrismaService, readonly config: ConfigModule) {} - async getUEAnnalMetadata(user: User, ueCode: string, isModerator: boolean) { + async getUeAnnalMetadata(user: User, ueCode: string, isModerator: boolean) { const ueof = await this.prisma.ueof.findMany({ where: { ueId: ueCode, @@ -56,7 +56,7 @@ export class AnnalsService { ); } - async createAnnalFile(user: User, params: CreateAnnal, ueof?: string) { + async createAnnalFile(user: User, params: CreateAnnal) { const subscription = await this.prisma.userUeSubscription.findFirst({ where: { semesterId: params.semester, @@ -86,7 +86,7 @@ export class AnnalsService { }, ueof: { connect: { - code: subscription.ueofId ?? ueof, + code: subscription.ueofId ?? params.ueof, }, }, }, @@ -178,7 +178,7 @@ export class AnnalsService { return fileEntry; } - async getUEAnnalsList(user: User, ueCode: string, includeAll: boolean) { + async getUeAnnalsList(user: User, ueCode: string, includeAll: boolean) { return ( await this.prisma.ueAnnal.findMany({ where: { @@ -253,7 +253,7 @@ export class AnnalsService { ); } - async getUEAnnal(annalId: string, userId: string, includeAll: boolean) { + async getUeAnnal(annalId: string, userId: string, includeAll: boolean) { return this.prisma.ueAnnal.findUnique({ where: { id: annalId, @@ -284,7 +284,7 @@ export class AnnalsService { }); } - async getUEAnnalFile(annalId: string, userId: string, includeAll: boolean) { + async getUeAnnalFile(annalId: string, userId: string, includeAll: boolean) { const metadata = await this.prisma.ueAnnal.findUnique({ where: { id: annalId, @@ -320,7 +320,7 @@ export class AnnalsService { }; } - async isUEAnnalSender(userId: string, annalId: string) { + async isUeAnnalSender(userId: string, annalId: string) { return ( (await this.prisma.ueAnnal.count({ where: { diff --git a/src/ue/annals/dto/create-annal.dto.ts b/src/ue/annals/dto/create-annal.dto.ts index dff1275..a9c2987 100644 --- a/src/ue/annals/dto/create-annal.dto.ts +++ b/src/ue/annals/dto/create-annal.dto.ts @@ -7,6 +7,7 @@ import { Length, MaxLength, MinLength, + ValidateIf, } from 'class-validator'; export class CreateAnnal { @@ -21,6 +22,7 @@ export class CreateAnnal { typeId: string; @IsString() + @ValidateIf((obj: CreateAnnal) => !!obj.ueCode || !!obj.ueof) @IsNotEmpty() @IsAlphanumeric() @MinLength(3) diff --git a/src/ue/comments/comments.controller.ts b/src/ue/comments/comments.controller.ts index 218dd49..db098b6 100644 --- a/src/ue/comments/comments.controller.ts +++ b/src/ue/comments/comments.controller.ts @@ -16,16 +16,17 @@ export class CommentsController { @Get() @RequireUserType('STUDENT', 'FORMER_STUDENT') - async getUEComments(@GetUser() user: User, @Query() dto: GetUeCommentsDto) { + async getUeComments(@GetUser() user: User, @Query() dto: GetUeCommentsDto) { if (!(await this.ueService.doesUeExist(dto.ueCode))) throw new AppException(ERROR_CODE.NO_SUCH_UE, dto.ueCode); return this.commentsService.getComments(user.id, dto, user.permissions.includes('commentModerator')); } @Post() @RequireUserType('STUDENT') - async PostUEComment(@GetUser() user: User, @Body() body: UeCommentPostDto) { + async PostUeComment(@GetUser() user: User, @Body() body: UeCommentPostDto) { + // FIXME : a user can only post one comment per ue (among all ueofs) if (!(await this.ueService.doesUeExist(body.ueCode))) throw new AppException(ERROR_CODE.NO_SUCH_UE, body.ueCode); - if (!(await this.ueService.hasAlreadyDoneThisUe(user.id, body.ueCode))) + if (!(await this.ueService.hasUserAttended(body.ueCode, user.id))) throw new AppException(ERROR_CODE.NOT_ALREADY_DONE_UE); if (await this.commentsService.hasAlreadyPostedAComment(user.id, body.ueCode)) throw new AppException(ERROR_CODE.FORBIDDEN_ALREADY_COMMENTED); @@ -34,7 +35,7 @@ export class CommentsController { @Get(':commentId') @RequireUserType('STUDENT', 'FORMER_STUDENT') - async getUECommentFromId(@UUIDParam('commentId') commentId: string, @GetUser() user: User) { + async getUeCommentFromId(@UUIDParam('commentId') commentId: string, @GetUser() user: User) { const comment = await this.commentsService.getCommentFromId( commentId, user.id, @@ -46,7 +47,7 @@ export class CommentsController { @Patch(':commentId') @RequireUserType('STUDENT', 'FORMER_STUDENT') - async EditUEComment( + async editUeComment( @UUIDParam('commentId') commentId: string, @GetUser() user: User, @Body() body: UeCommentUpdateDto, @@ -79,7 +80,7 @@ export class CommentsController { @Delete(':commentId') @RequireUserType('STUDENT', 'FORMER_STUDENT') - async DiscardUEComment(@UUIDParam('commentId') commentId: string, @GetUser() user: User) { + async discardUeComment(@UUIDParam('commentId') commentId: string, @GetUser() user: User) { if ( !(await this.commentsService.doesCommentExist(commentId, user.id, user.permissions.includes('commentModerator'))) ) @@ -99,7 +100,7 @@ export class CommentsController { @Post(':commentId/upvote') @RequireUserType('STUDENT') @HttpCode(HttpStatus.OK) - async UpvoteUEComment(@UUIDParam('commentId') commentId: string, @GetUser() user: User) { + async UpvoteUeComment(@UUIDParam('commentId') commentId: string, @GetUser() user: User) { if ( !(await this.commentsService.doesCommentExist( commentId, @@ -122,7 +123,7 @@ export class CommentsController { @Delete(':commentId/upvote') @RequireUserType('STUDENT', 'FORMER_STUDENT') @HttpCode(HttpStatus.OK) - async UnUpvoteUEComment(@UUIDParam('commentId') commentId: string, @GetUser() user: User) { + async UnUpvoteUeComment(@UUIDParam('commentId') commentId: string, @GetUser() user: User) { if ( !(await this.commentsService.doesCommentExist( commentId, @@ -144,7 +145,7 @@ export class CommentsController { @Post(':commentId/reply') @RequireUserType('STUDENT') - async CreateReplyComment( + async createReplyComment( @GetUser() user: User, @UUIDParam('commentId') commentId: string, @Body() body: CommentReplyDto, @@ -163,7 +164,7 @@ export class CommentsController { @Patch('reply/:replyId') @RequireUserType('STUDENT', 'FORMER_STUDENT') - async EditReplyComment(@GetUser() user: User, @UUIDParam('replyId') replyId: string, @Body() body: CommentReplyDto) { + async editReplyComment(@GetUser() user: User, @UUIDParam('replyId') replyId: string, @Body() body: CommentReplyDto) { if (!(await this.commentsService.doesReplyExist(replyId))) throw new AppException(ERROR_CODE.NO_SUCH_REPLY); if ( (await this.commentsService.isUserCommentReplyAuthor(user.id, replyId)) || @@ -175,7 +176,7 @@ export class CommentsController { @Delete('reply/:replyId') @RequireUserType('STUDENT', 'FORMER_STUDENT') - async DeleteReplyComment(@GetUser() user: User, @UUIDParam('replyId') replyId: string) { + async deleteReplyComment(@GetUser() user: User, @UUIDParam('replyId') replyId: string) { if (!(await this.commentsService.doesReplyExist(replyId))) throw new AppException(ERROR_CODE.NO_SUCH_REPLY); if ( (await this.commentsService.isUserCommentReplyAuthor(user.id, replyId)) || diff --git a/src/ue/comments/comments.service.ts b/src/ue/comments/comments.service.ts index 24775ec..3900684 100644 --- a/src/ue/comments/comments.service.ts +++ b/src/ue/comments/comments.service.ts @@ -145,7 +145,7 @@ export class CommentsService { * @param ueCode the code of the UE * @returns the last semester done by the {@link user} for the {@link ueCode | ue} */ - async getLastSemesterDoneByUser(userId: string, ueCode: string): Promise { + private async getLastUserSubscription(userId: string, ueCode: string): Promise { return this.prisma.userUeSubscription.findFirst({ where: { ueof: { @@ -201,7 +201,7 @@ export class CommentsService { */ async createComment(body: UeCommentPostDto, userId: string): Promise { // Use last semester done when creating the comment - const lastSemester = await this.getLastSemesterDoneByUser(userId, body.ueCode); + const lastSemester = await this.getLastUserSubscription(userId, body.ueCode); return this.prisma.ueComment.create( { args: { diff --git a/src/ue/ue.controller.ts b/src/ue/ue.controller.ts index 95b3a97..9efdc38 100644 --- a/src/ue/ue.controller.ts +++ b/src/ue/ue.controller.ts @@ -11,7 +11,6 @@ import { UeRateDto } from './dto/ue-rate.dto'; import { Ue, UeStarVoteEntry } from './interfaces/ue.interface'; import { Language, UserType } from '@prisma/client'; import { Translation } from '../prisma/types'; -import { omit } from '../utils'; @Controller('ue') export class UeController { @@ -50,46 +49,44 @@ export class UeController { if (alias?.standsFor) return res.redirect(HttpStatusCode.MovedPermanently, `./${alias.standsFor}`); throw new AppException(ERROR_CODE.NO_SUCH_UE, ueCode); } - const result = this.formatDetailedUe(await this.ueService.getUe(ueCode.toUpperCase())); - if (user?.userType === UserType.STUDENT || user?.userType === UserType.FORMER_STUDENT) return result; - return omit(result, 'starVotes'); + return this.formatDetailedUe(await this.ueService.getUe(ueCode.toUpperCase()), user?.userType); } @Get('/rate/criteria') @RequireUserType('STUDENT', 'FORMER_STUDENT') - async GetRateCriteria() { + async getRateCriteria() { return this.ueService.getRateCriteria(); } @Get('/:ueCode/rate') @RequireUserType('STUDENT', 'FORMER_STUDENT') - async GetRateUe(@Param('ueCode') ueCode: string, @GetUser() user: User) { + async getUeRate(@Param('ueCode') ueCode: string, @GetUser() user: User) { if (!(await this.ueService.doesUeExist(ueCode))) throw new AppException(ERROR_CODE.NO_SUCH_UE, ueCode); return this.ueService.getRateUe(user.id, ueCode); } - @Put('/:ueCode/rate') + @Put('/ueof/:ueofCode/rate') @RequireUserType('STUDENT') - async RateUe(@Param('ueCode') ueCode: string, @GetUser() user: User, @Body() dto: UeRateDto) { - if (!(await this.ueService.doesUeExist(ueCode))) throw new AppException(ERROR_CODE.NO_SUCH_UE, ueCode); + async rateUe(@Param('ueofCode') ueofCode: string, @GetUser() user: User, @Body() dto: UeRateDto) { + if (!(await this.ueService.doesUeofExist(ueofCode))) throw new AppException(ERROR_CODE.NO_SUCH_UEOF, ueofCode); if (!(await this.ueService.doesCriterionExist(dto.criterion))) throw new AppException(ERROR_CODE.NO_SUCH_CRITERION); - if (!(await this.ueService.hasAlreadyDoneThisUe(user.id, ueCode))) - throw new AppException(ERROR_CODE.NOT_ALREADY_DONE_UE); - return this.ueService.doRateUe(user.id, ueCode, dto); + if (!(await this.ueService.hasUserAttended(ueofCode, user.id))) + throw new AppException(ERROR_CODE.NOT_ALREADY_DONE_UEOF); + return this.ueService.rateUeof(user.id, ueofCode, dto); } - @Delete('/:ueCode/rate/:criterionId') + @Delete('/ueof/:ueofCode/rate/:criterionId') @RequireUserType('STUDENT', 'FORMER_STUDENT') - async UnRateUe( - @Param('ueCode') ueCode: string, + async unRateUe( + @Param('ueofCode') ueofCode: string, @UUIDParam('criterionId') criterionId: string, @GetUser() user: User, ) { - if (!(await this.ueService.doesUeExist(ueCode))) throw new AppException(ERROR_CODE.NO_SUCH_UE, ueCode); + if (!(await this.ueService.doesUeofExist(ueofCode))) throw new AppException(ERROR_CODE.NO_SUCH_UEOF, ueofCode); if (!(await this.ueService.doesCriterionExist(criterionId))) throw new AppException(ERROR_CODE.NO_SUCH_CRITERION); - if (!(await this.ueService.hasAlreadyRated(user.id, ueCode, criterionId))) - throw new AppException(ERROR_CODE.NOT_ALREADY_RATED_UE, ueCode, criterionId); - return this.ueService.unRateUe(user.id, ueCode, criterionId); + if (!(await this.ueService.hasAlreadyRated(user.id, ueofCode, criterionId))) + throw new AppException(ERROR_CODE.NOT_ALREADY_RATED_UEOF, ueofCode, criterionId); + return this.ueService.unRateUeof(user.id, ueofCode, criterionId); } @Get('/of/me') @@ -129,9 +126,6 @@ export class UeController { info: { requirements: chosenOf.requirements.map((r) => r.code), languages: ue.ueofs.map((ueof) => ueof.info.language).uniqueValues, - minors: chosenOf.info.minors, - objectives: chosenOf.info.objectives, - program: chosenOf.info.program, }, openSemester: ue.ueofs .map((ueof) => ueof.openSemester) @@ -144,12 +138,12 @@ export class UeController { }; } - private formatDetailedUe(ue: Ue): UeDetail { + private formatDetailedUe(ue: Ue, userType: UserType): UeDetail { return { code: ue.code, creationYear: ue.creationYear, updateYear: ue.updateYear, - ofs: ue.ueofs.map((ueof) => ({ + ueofs: ue.ueofs.map((ueof) => ({ code: ueof.code, name: ueof.name, credits: ueof.credits.map((c) => ({ @@ -187,18 +181,24 @@ export class UeController { project: ueof.workTime.project, internship: ueof.workTime.internship, }, - starVotes: { - // Compute ratings for each criterion, using an exponential decay function - ...Object.fromEntries( - Object.entries(ue.starVotes).map(([criterion, rates]) => [criterion, this.computeRate(rates, ueof.code)]), - ), - voteCount: Math.max(...Object.values(ue.starVotes).map((rates) => rates.length)), - }, + starVotes: + userType === UserType.STUDENT || userType === UserType.FORMER_STUDENT + ? { + // Compute ratings for each criterion, using an exponential decay function + ...Object.fromEntries( + Object.entries(ue.starVotes).map(([criterion, rates]) => [ + criterion, + this.computeRate(rates, ueof.code), + ]), + ), + voteCount: Math.max(...Object.values(ue.starVotes).map((rates) => rates.length)), + } + : undefined, })), }; } - private computeRate(rates: UeStarVoteEntry[], targetUeofCode: string) { + public computeRate(rates: UeStarVoteEntry[], targetUeofCode: string) { function aggregate( entities: T[], mapper: (entity: T) => [key: number, value: number], @@ -259,9 +259,6 @@ export type UeOverview = { info: { requirements: string[]; languages: string[]; - minors: string; - objectives: Translation; - program: Translation; }; openSemester: Array<{ code: string; @@ -274,7 +271,7 @@ export type UeDetail = { code: string; creationYear: number; updateYear: number; - ofs: { + ueofs: { code: string; name: Translation; credits: Array<{ diff --git a/src/ue/ue.service.ts b/src/ue/ue.service.ts index 8af3d35..1f5f70e 100644 --- a/src/ue/ue.service.ts +++ b/src/ue/ue.service.ts @@ -5,7 +5,6 @@ import { UeRateDto } from './dto/ue-rate.dto'; import { Ue } from './interfaces/ue.interface'; import { Criterion } from './interfaces/criterion.interface'; import { UeRating } from './interfaces/rate.interface'; -import { RawUserUeSubscription } from '../prisma/types'; import { ConfigModule } from '../config/config.module'; import { Language, Prisma } from '@prisma/client'; import { SemesterService } from '../semester/semester.service'; @@ -122,7 +121,7 @@ export class UeService { some: { openSemester: { some: { - code: query.availableAtSemester?.toUpperCase(), + code: query.availableAtSemester.toUpperCase(), }, }, }, @@ -160,36 +159,13 @@ export class UeService { }); } - /** - * Retrieves the last semester done by a user for a given ue - * @remarks The user must not be null - * @param userId the user to retrieve semesters of - * @param ueCode the code of the UE - * @returns the last semester done by the {@link user} for the {@link ueCode | ue} - */ - async getLastSemesterDoneByUser(userId: string, ueCode: string): Promise { - return this.prisma.userUeSubscription.findFirst({ - where: { - ueof: { - ueId: ueCode, - }, - userId, - }, - orderBy: { - semester: { - end: 'desc', - }, - }, - }); - } - /** * Checks whether an ue exists * @param ueCode the code of the ue to check * @returns whether the ue exists */ async doesUeExist(ueCode: string) { - return !!(await this.prisma.ue.findUnique({ + return !!(await this.prisma.ue.count({ where: { code: ueCode, }, @@ -204,15 +180,46 @@ export class UeService { }); } + async doesUeofExist(ueofCode: string) { + return !!(await this.prisma.ueof.count({ + where: { + code: ueofCode, + }, + })); + } + /** - * Checks whether a user has already done an ue - * @remarks The user must not be null + * Retrieves the amount of UEOF the given {@link userId | user} has attended. + * Not only this method can be used to retrieve the precise count of matching ueofs done by the user, + * but also in order to check if a user has attended a specific UEOF. + * The key feature of this function is that UEOF matching is done on both UEOF and UE codes, + * allowing to check if a user has attended a specific UEOF or a specific UE. + * + * @param ueCodeOrUeofCode the code of the ue or the ueof to check * @param userId the user to check - * @param ueCode the code of the ue to check - * @returns whether the {@link user} has already done the {@link ueCode | ue} + * @param semester provide a semester code to filter the ueofs by semester + * @returns the amount of UEOF the {@link userId | user} has attended + * + * @example + * ```typescript + * const user: User = ...; + * const ueCode: string = 'MT01'; + * // Check user has already done MT01 + * if (!(await ueService.hasUserAttended(ueCode, user.id))) { + * throw new AppException(ERROR_CODE.DUMMY_ERROR, 'This feature is only available for students who have attended MT01'); + * } + * ``` */ - async hasAlreadyDoneThisUe(userId: string, ueCode: string) { - return (await this.getLastSemesterDoneByUser(userId, ueCode)) != null; + async hasUserAttended(ueCodeOrUeofCode: string, userId: string, semester?: string): Promise { + return this.prisma.userUeSubscription.count({ + where: { + userId: userId ?? null, // We want the filter to be applied in every query + semesterId: semester || undefined, // This filter should be applied only if semester is not null + ueof: { + OR: [{ code: ueCodeOrUeofCode }, { ueId: ueCodeOrUeofCode }], + }, + }, + }); } /** @@ -230,12 +237,12 @@ export class UeService { ); } - async hasAlreadyRated(userId: string, ueCode: string, criterionId: string) { + async hasAlreadyRated(userId: string, ueofCode: string, criterionId: string) { return ( (await this.prisma.ueStarVote.count({ where: { - ue: { - code: ueCode, + ueof: { + code: ueofCode, }, userId, criterionId, @@ -259,33 +266,50 @@ export class UeService { * @param ueCode the code of the ue to fetch the rates of * @returns the rates of the {@link ueCode | ue} for the {@link user} */ - async getRateUe(userId: string, ueCode: string): Promise { - const ue = await this.prisma.ue.findUnique({ + async getRateUe( + userId: string, + ueCode: string, + ): Promise<{ + [ueofCode: string]: UeRating[]; + }> { + const ueofs = await this.prisma.ueof.findMany({ where: { - code: ueCode, - }, - }); - return this.prisma.ueStarVote.findMany({ - where: { - userId: userId, - ueId: ue.code, + ue: { + code: ueCode, + }, }, }); + return Object.fromEntries( + await Promise.all( + ueofs.map( + async (ueof) => + [ + ueof.code, + await this.prisma.ueStarVote.findMany({ + where: { + userId: userId, + ueofId: ueof.code, + }, + }), + ] as const, + ), + ), + ); } /** * Creates or updates a rating for a given ue * @remarks The user must not be null and the ue must exist * @param userId the user rating the ue - * @param ueCode the code of the ue to rate + * @param ueofCode the code of the ue to rate * @param dto the rating to apply - * @returns the new rate of the {@link ueCode | ue} for the {@link user} + * @returns the new rate of the {@link ueofCode | ue} for the {@link user} */ - async doRateUe(userId: string, ueCode: string, dto: UeRateDto): Promise { + async rateUeof(userId: string, ueofCode: string, dto: UeRateDto): Promise { return this.prisma.ueStarVote.upsert({ where: { - ueId_userId_criterionId: { - ueId: ueCode, + ueofId_userId_criterionId: { + ueofId: ueofCode, userId, criterionId: dto.criterion, }, @@ -293,7 +317,7 @@ export class UeService { create: { value: dto.value, criterionId: dto.criterion, - ueId: ueCode, + ueofId: ueofCode, userId, }, update: { @@ -302,11 +326,11 @@ export class UeService { }); } - async unRateUe(userId: string, ueCode: string, criterionId: string): Promise { + async unRateUeof(userId: string, ueofCode: string, criterionId: string): Promise { return this.prisma.ueStarVote.delete({ where: { - ueId_userId_criterionId: { - ueId: ueCode, + ueofId_userId_criterionId: { + ueofId: ueofCode, userId, criterionId, }, diff --git a/test/declarations.ts b/test/declarations.ts index ded6233..5209d8d 100644 --- a/test/declarations.ts +++ b/test/declarations.ts @@ -78,7 +78,7 @@ Spec.prototype.expectUe = function (ue: FakeUe, rates: Array<{ criterionId: stri creationYear: 2000 + Number(ue.ueofCode.match(/\d+$/)?.[0] ?? 23), updateYear: 2000 + Number(ue.ueofCode.match(/\d+$/)?.[0] ?? 23), ...(rates ? { starVotes: Object.fromEntries(rates.map((rate) => [rate.criterionId, rate.value])) } : {}), - ofs: [ + ueofs: [ { name: getTranslation(ue.name, this.language), credits: ue.credits.map((credit) => ({ diff --git a/test/e2e/ue/delete-rate.e2e-spec.ts b/test/e2e/ue/delete-rate.e2e-spec.ts index 03cca7a..a639da4 100644 --- a/test/e2e/ue/delete-rate.e2e-spec.ts +++ b/test/e2e/ue/delete-rate.e2e-spec.ts @@ -33,7 +33,7 @@ const DeleteRate = e2eSuite('DELETE /ue/{ueCode}/rate/{critetionId}', (app) => { .spec() .withBearerToken(user2.token) .delete(`/ue/${ue.code}/rate/${criterion.id}`) - .expectAppError(ERROR_CODE.NOT_ALREADY_RATED_UE, ue.code, criterion.id); + .expectAppError(ERROR_CODE.NOT_ALREADY_RATED_UEOF, ue.code, criterion.id); }); it('should return a 404 as the UE does not exist', () => { diff --git a/test/e2e/ue/get.e2e-spec.ts b/test/e2e/ue/get.e2e-spec.ts index 5c3f5c3..9dfe226 100644 --- a/test/e2e/ue/get.e2e-spec.ts +++ b/test/e2e/ue/get.e2e-spec.ts @@ -11,9 +11,9 @@ import { FakeUeStarVote, } from '../../utils/fakedb'; import { e2eSuite } from '../../utils/test_utils'; +import { UeController } from '../../../src/ue/ue.controller'; import * as pactum from 'pactum'; import { ERROR_CODE } from '../../../src/exceptions'; -import { computeRate } from '../../../src/ue/interfaces/ue.interface'; import { UserType } from '@prisma/client'; const GetE2ESpec = e2eSuite('GET /ue/{ueCode}', (app) => { @@ -98,7 +98,15 @@ const GetE2ESpec = e2eSuite('GET /ue/{ueCode}', (app) => { .expectUe(ueWithRating, [ { criterionId: criterion.id, - value: computeRate([rate2 as Required, rate1 as Required]), + value: app() + .get(UeController) + .computeRate( + [ + { ...(rate2 as Required), ueofCode: ueWithRating.ueofCode }, + { ...(rate1 as Required), ueofCode: ueWithRating.ueofCode }, + ], + ueWithRating.ueofCode, + ), }, ]); }); diff --git a/test/utils/fakedb.ts b/test/utils/fakedb.ts index 28a3dbf..af7ff46 100644 --- a/test/utils/fakedb.ts +++ b/test/utils/fakedb.ts @@ -670,7 +670,7 @@ export const createAnnal = entityFaker( ); export type CreateUeParameters = Omit & { - credits: (FakeUe['credits'] extends Array ? Omit : never)[]; + credits: Omit, 'branchOptions'>[]; }; export const createUe = entityFaker( 'ue', @@ -696,7 +696,7 @@ export const createUe = entityFaker( td: () => faker.datatype.number({ min: 0, max: 100 }), tp: () => faker.datatype.number({ min: 0, max: 100 }), the: () => faker.datatype.number({ min: 0, max: 100 }), - project: () => faker.datatype.boolean(), + project: faker.datatype.boolean, internship: () => faker.datatype.number({ min: 0, max: 100 }), }, openSemesters: [], @@ -864,15 +864,15 @@ export const createUeRating = entityFaker( .get(PrismaService) .withDefaultBehaviour.ueStarVote.create({ data: { - ...omit(params, 'criterionId', 'ueId', 'userId'), + ...omit(params, 'criterionId', 'ueofId', 'userId'), criterion: { connect: { id: dependencies.criterion.id, }, }, - ue: { + ueof: { connect: { - code: dependencies.ue.code, + code: dependencies.ue.ueofCode, }, }, user: {