From be667c7c537485cfed082c37a27ecb87205f46c5 Mon Sep 17 00:00:00 2001 From: Gerald Schmittinger Date: Sat, 1 Mar 2025 23:09:42 +0100 Subject: [PATCH 01/96] =?UTF-8?q?feat(backend):=20Auth=20middleware=20toeg?= =?UTF-8?q?evoegd.=20Deze=20verifi=C3=ABert=20het=20meegegeven=20bearer=20?= =?UTF-8?q?token.=20Door=20een=20specifieke=20extra=20middleware=20per=20e?= =?UTF-8?q?ndpoint=20kan=20dan=20aangegeven=20worden=20of=20deze=20enkel?= =?UTF-8?q?=20toegankelijk=20is=20voor=20leerlingen,=20leerkrachten=20of?= =?UTF-8?q?=20allebei.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/.env.development.example | 5 + backend/package.json | 2 + backend/src/app.ts | 3 + backend/src/middleware/auth/auth.ts | 75 + .../middleware/auth/authenticated-request.ts | 6 + backend/src/routes/student.ts | 6 +- backend/src/util/envvars.ts | 8 + docker-compose.yml | 20 +- package-lock.json | 3386 +++-------------- 9 files changed, 722 insertions(+), 2789 deletions(-) create mode 100644 backend/src/middleware/auth/auth.ts create mode 100644 backend/src/middleware/auth/authenticated-request.ts diff --git a/backend/.env.development.example b/backend/.env.development.example index 58694df4..0b96f873 100644 --- a/backend/.env.development.example +++ b/backend/.env.development.example @@ -4,3 +4,8 @@ DWENGO_DB_PORT=5431 DWENGO_DB_USERNAME=postgres DWENGO_DB_PASSWORD=postgres DWENGO_DB_UPDATE=true + +DWENGO_AUTH_STUDENT_URL=http://localhost:7080/realms/student +DWENGO_AUTH_STUDENT_JWKS_ENDPOINT=http://localhost:7080/realms/student/protocol/openid-connect/certs +DWENGO_AUTH_TEACHER_URL=http://localhost:7080/realms/teacher +DWENGO_AUTH_TEACHER_JWKS_ENDPOINT=http://localhost:7080/realms/teacher/protocol/openid-connect/certs diff --git a/backend/package.json b/backend/package.json index 2b04044f..af8090f2 100644 --- a/backend/package.json +++ b/backend/package.json @@ -20,6 +20,8 @@ "@mikro-orm/reflection": "6.4.6", "dotenv": "^16.4.7", "express": "^5.0.1", + "express-jwt": "^8.5.1", + "jwks-rsa": "^3.1.0", "uuid": "^11.1.0", "js-yaml": "^4.1.0", "@types/js-yaml": "^4.0.9" diff --git a/backend/src/app.ts b/backend/src/app.ts index 7a25fa2b..521c40c5 100644 --- a/backend/src/app.ts +++ b/backend/src/app.ts @@ -11,6 +11,7 @@ import submissionRouter from './routes/submission.js'; import classRouter from './routes/class.js'; import questionRouter from './routes/question.js'; import loginRouter from './routes/login.js'; +import {authenticateUser} from "./middleware/auth/auth"; const app: Express = express(); const port: string | number = getNumericEnvVar(EnvVars.Port); @@ -23,6 +24,8 @@ app.get('/', (_, res: Response) => { }); }); +app.use(authenticateUser); + app.use('/student', studentRouter); app.use('/group', groupRouter); app.use('/assignment', assignmentRouter); diff --git a/backend/src/middleware/auth/auth.ts b/backend/src/middleware/auth/auth.ts new file mode 100644 index 00000000..82f1b23f --- /dev/null +++ b/backend/src/middleware/auth/auth.ts @@ -0,0 +1,75 @@ +import {EnvVars, getEnvVar} from "../../util/envvars.js"; +import {expressjwt} from 'express-jwt'; +import {JwtPayload} from 'jsonwebtoken' +import jwksClient from 'jwks-rsa'; +import * as express from "express"; +import * as jwt from "jsonwebtoken"; +import {AuthenticatedRequest} from "./authenticated-request.js"; + +function createJwksClient(uri: string): jwksClient.JwksClient { + return jwksClient({ + cache: true, + rateLimit: true, + jwksUri: uri, + }); +} + +const idpConfigs = { + student: { + issuer: getEnvVar(EnvVars.IdpStudentUrl), + jwksClient: createJwksClient(getEnvVar(EnvVars.IdpStudentJwksEndpoint)), + }, + teacher: { + issuer: getEnvVar(EnvVars.IdpTeacherUrl), + jwksClient: createJwksClient(getEnvVar(EnvVars.IdpTeacherJwksEndpoint)), + } +}; + +export const authenticateUser = expressjwt({ + secret: async (_: express.Request, token: jwt.Jwt | undefined) => { + if (!token?.payload || !(token.payload as JwtPayload).iss) { + throw new Error("Invalid token"); + } + + let issuer = (token.payload as JwtPayload).iss; + + let idpConfig = Object.values(idpConfigs).find(config => config.issuer === issuer); + if (!idpConfig) { + throw new Error("Issuer not accepted."); + } + + const signingKey = await idpConfig.jwksClient.getSigningKey(token.header.kid); + if (!signingKey) { + throw new Error("Signing key not found."); + } + return signingKey.getPublicKey(); + }, + audience: getEnvVar(EnvVars.IdpAudience), + algorithms: ["RS256"], + credentialsRequired: false +}); + +const authorizeRole = (studentsAllowed: boolean, teachersAllowed: boolean) => { + return (req: AuthenticatedRequest, res: express.Response, next: express.NextFunction): void => { + if (!req.auth) { + res.status(401).json({ message: "Unauthorized" }); + return; + } + + const issuer = req.auth.iss; + if (issuer === idpConfigs.student.issuer && !studentsAllowed) { + res.status(403).json({ message: "Students not allowed" }); + return; + } + if (issuer === idpConfigs.teacher.issuer && !teachersAllowed) { + res.status(403).json({ message: "Teachers not allowed" }); + return; + } + + next(); // User is allowed + }; +}; + +export const authenticatedOnly = authorizeRole(true, true); +export const studentsOnly = authorizeRole(true, false); +export const teachersOnly = authorizeRole(false, true); diff --git a/backend/src/middleware/auth/authenticated-request.ts b/backend/src/middleware/auth/authenticated-request.ts new file mode 100644 index 00000000..9b33de6f --- /dev/null +++ b/backend/src/middleware/auth/authenticated-request.ts @@ -0,0 +1,6 @@ +import { Request } from "express"; +import { JwtPayload } from "jsonwebtoken"; + +export interface AuthenticatedRequest extends Request { + auth?: JwtPayload; // Optional, as req.auth might be undefined if authentication is optional +} diff --git a/backend/src/routes/student.ts b/backend/src/routes/student.ts index a11c1fbc..f9697478 100644 --- a/backend/src/routes/student.ts +++ b/backend/src/routes/student.ts @@ -41,14 +41,14 @@ router.get('/:id/submissions', (req, res) => { }); }) - + // the list of assignments a student has router.get('/:id/assignments', (req, res) => { res.json({ assignments: [ '0' ], }); }) - + // the list of groups a student is in router.get('/:id/groups', (req, res) => { res.json({ @@ -56,4 +56,4 @@ router.get('/:id/groups', (req, res) => { }); }) -export default router \ No newline at end of file +export default router diff --git a/backend/src/util/envvars.ts b/backend/src/util/envvars.ts index 5a06ac22..2dfcf640 100644 --- a/backend/src/util/envvars.ts +++ b/backend/src/util/envvars.ts @@ -1,5 +1,8 @@ const PREFIX = 'DWENGO_'; const DB_PREFIX = PREFIX + 'DB_'; +const IDP_PREFIX = PREFIX + 'AUTH_'; +const STUDENT_IDP_PREFIX = IDP_PREFIX + 'STUDENT_'; +const TEACHER_IDP_PREFIX = IDP_PREFIX + 'TEACHER_'; type EnvVar = { key: string; required?: boolean; defaultValue?: any }; @@ -11,6 +14,11 @@ export const EnvVars: { [key: string]: EnvVar } = { DbUsername: { key: DB_PREFIX + 'USERNAME', required: true }, DbPassword: { key: DB_PREFIX + 'PASSWORD', required: true }, DbUpdate: { key: DB_PREFIX + 'UPDATE', defaultValue: false }, + IdpStudentUrl: { key: STUDENT_IDP_PREFIX + 'URL', required: true }, + IdpStudentJwksEndpoint: { key: STUDENT_IDP_PREFIX + 'JWKS_ENDPOINT', required: true }, + IdpTeacherUrl: { key: TEACHER_IDP_PREFIX + 'URL', required: true }, + IdpTeacherJwksEndpoint: { key: TEACHER_IDP_PREFIX + 'JWKS_ENDPOINT', required: true }, + IdpAudience: { key: IDP_PREFIX + 'AUDIENCE', defaultValue: 'account' } } as const; /** diff --git a/docker-compose.yml b/docker-compose.yml index c5230d7c..df906ba4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,6 +9,24 @@ services: - "5431:5432" volumes: - postgres_data:/var/lib/postgresql/data - + idp: # Bron: https://medium.com/@fingervinicius/easy-running-keycloak-with-docker-compose-b0d7a4ee2358 + image: quay.io/keycloak/keycloak:latest + environment: + KC_HOSTNAME: localhost + KC_HOSTNAME_PORT: 7080 + KC_HOSTNAME_STRICT_BACKCHANNEL: "true" + KEYCLOAK_ADMIN: admin + KEYCLOAK_ADMIN_PASSWORD: admin + KC_HEALTH_ENABLED: "true" + KC_LOG_LEVEL: info + healthcheck: + test: [ "CMD", "curl", "-f", "http://localhost:7080/health/ready" ] + interval: 15s + timeout: 2s + retries: 15 + command: ["start-dev", "--http-port", "7080", "--https-port", "7443"] + ports: + - "7080:7080" + - "7443:7443" volumes: postgres_data: diff --git a/package-lock.json b/package-lock.json index 8d47c5b5..50b2a6d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,11 +36,13 @@ "@mikro-orm/postgresql": "6.4.6", "@mikro-orm/reflection": "6.4.6", "@mikro-orm/sqlite": "6.4.6", + "@types/js-yaml": "^4.0.9", "dotenv": "^16.4.7", "express": "^5.0.1", - "uuid": "^11.1.0", + "express-jwt": "^8.5.1", "js-yaml": "^4.1.0", - "@types/js-yaml": "^4.0.9", + "jwks-rsa": "^3.1.0", + "uuid": "^11.1.0" }, "devDependencies": { "@mikro-orm/cli": "^6.4.6", @@ -55,8 +57,7 @@ }, "backend/node_modules/@mikro-orm/sqlite": { "version": "6.4.6", - "resolved": "https://registry.npmjs.org/@mikro-orm/sqlite/-/sqlite-6.4.6.tgz", - "integrity": "sha512-BvoLd6qge2N4P2w9yjPP8+Ya5dxxnZrS6W3B2xm0m8BUesWnaCg2pmGXQpzFjrpYMg40mZ+RJWRTPq4M2Nl4lw==", + "license": "MIT", "dependencies": { "@mikro-orm/knex": "6.4.6", "fs-extra": "11.3.0", @@ -72,8 +73,6 @@ }, "backend/node_modules/globals": { "version": "15.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", - "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", "dev": true, "license": "MIT", "engines": { @@ -116,8 +115,6 @@ }, "node_modules/@ampproject/remapping": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -130,8 +127,6 @@ }, "node_modules/@ampproject/remapping/node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "license": "MIT", "dependencies": { @@ -141,8 +136,6 @@ }, "node_modules/@antfu/utils": { "version": "0.7.10", - "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-0.7.10.tgz", - "integrity": "sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==", "dev": true, "license": "MIT", "funding": { @@ -151,8 +144,6 @@ }, "node_modules/@asamuzakjp/css-color": { "version": "2.8.3", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-2.8.3.tgz", - "integrity": "sha512-GIc76d9UI1hCvOATjZPyHFmE5qhRccp3/zGfMPapK3jBi+yocEzp6BBB0UnfRYP9NP4FANqUZYb0hnfs3TM3hw==", "dev": true, "license": "MIT", "dependencies": { @@ -165,8 +156,6 @@ }, "node_modules/@babel/code-frame": { "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "dev": true, "license": "MIT", "dependencies": { @@ -180,8 +169,6 @@ }, "node_modules/@babel/compat-data": { "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", - "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", "dev": true, "license": "MIT", "engines": { @@ -190,8 +177,6 @@ }, "node_modules/@babel/core": { "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.9.tgz", - "integrity": "sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==", "dev": true, "license": "MIT", "dependencies": { @@ -221,8 +206,6 @@ }, "node_modules/@babel/core/node_modules/semver": { "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "license": "ISC", "bin": { @@ -231,8 +214,6 @@ }, "node_modules/@babel/generator": { "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz", - "integrity": "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==", "dev": true, "license": "MIT", "dependencies": { @@ -248,8 +229,6 @@ }, "node_modules/@babel/generator/node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "license": "MIT", "dependencies": { @@ -259,8 +238,6 @@ }, "node_modules/@babel/helper-annotate-as-pure": { "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", - "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", "dev": true, "license": "MIT", "dependencies": { @@ -272,8 +249,6 @@ }, "node_modules/@babel/helper-compilation-targets": { "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", - "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", "dev": true, "license": "MIT", "dependencies": { @@ -289,8 +264,6 @@ }, "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "license": "ISC", "dependencies": { @@ -299,8 +272,6 @@ }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "license": "ISC", "bin": { @@ -309,8 +280,6 @@ }, "node_modules/@babel/helper-create-class-features-plugin": { "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.26.9.tgz", - "integrity": "sha512-ubbUqCofvxPRurw5L8WTsCLSkQiVpov4Qx0WMA+jUN+nXBK8ADPlJO1grkFw5CWKC5+sZSOfuGMdX1aI1iT9Sg==", "dev": true, "license": "MIT", "dependencies": { @@ -331,8 +300,6 @@ }, "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "license": "ISC", "bin": { @@ -341,8 +308,6 @@ }, "node_modules/@babel/helper-member-expression-to-functions": { "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", - "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", "dev": true, "license": "MIT", "dependencies": { @@ -355,8 +320,6 @@ }, "node_modules/@babel/helper-module-imports": { "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", - "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", "dev": true, "license": "MIT", "dependencies": { @@ -369,8 +332,6 @@ }, "node_modules/@babel/helper-module-transforms": { "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", - "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", "dev": true, "license": "MIT", "dependencies": { @@ -387,8 +348,6 @@ }, "node_modules/@babel/helper-optimise-call-expression": { "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", - "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", "dev": true, "license": "MIT", "dependencies": { @@ -400,8 +359,6 @@ }, "node_modules/@babel/helper-plugin-utils": { "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", - "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", "dev": true, "license": "MIT", "engines": { @@ -410,8 +367,6 @@ }, "node_modules/@babel/helper-replace-supers": { "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz", - "integrity": "sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==", "dev": true, "license": "MIT", "dependencies": { @@ -428,8 +383,6 @@ }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", - "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", "dev": true, "license": "MIT", "dependencies": { @@ -442,8 +395,6 @@ }, "node_modules/@babel/helper-string-parser": { "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -451,8 +402,6 @@ }, "node_modules/@babel/helper-validator-identifier": { "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -460,8 +409,6 @@ }, "node_modules/@babel/helper-validator-option": { "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", - "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", "dev": true, "license": "MIT", "engines": { @@ -470,8 +417,6 @@ }, "node_modules/@babel/helpers": { "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.9.tgz", - "integrity": "sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==", "dev": true, "license": "MIT", "dependencies": { @@ -484,8 +429,6 @@ }, "node_modules/@babel/parser": { "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz", - "integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==", "license": "MIT", "dependencies": { "@babel/types": "^7.26.9" @@ -499,8 +442,6 @@ }, "node_modules/@babel/plugin-proposal-decorators": { "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.25.9.tgz", - "integrity": "sha512-smkNLL/O1ezy9Nhy4CNosc4Va+1wo5w4gzSZeLe6y6dM4mmHfYOCPolXQPHQxonZCF+ZyebxN9vqOolkYrSn5g==", "dev": true, "license": "MIT", "dependencies": { @@ -517,8 +458,6 @@ }, "node_modules/@babel/plugin-syntax-decorators": { "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.25.9.tgz", - "integrity": "sha512-ryzI0McXUPJnRCvMo4lumIKZUzhYUO/ScI+Mz4YVaTLt04DHNSjEUjKVvbzQjZFLuod/cYEc07mJWhzl6v4DPg==", "dev": true, "license": "MIT", "dependencies": { @@ -533,8 +472,6 @@ }, "node_modules/@babel/plugin-syntax-import-attributes": { "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", - "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", "dev": true, "license": "MIT", "dependencies": { @@ -549,8 +486,6 @@ }, "node_modules/@babel/plugin-syntax-import-meta": { "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", "dev": true, "license": "MIT", "dependencies": { @@ -562,8 +497,6 @@ }, "node_modules/@babel/plugin-syntax-jsx": { "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", - "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", "dev": true, "license": "MIT", "dependencies": { @@ -578,8 +511,6 @@ }, "node_modules/@babel/plugin-syntax-typescript": { "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", - "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", "dev": true, "license": "MIT", "dependencies": { @@ -594,8 +525,6 @@ }, "node_modules/@babel/plugin-transform-typescript": { "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.26.8.tgz", - "integrity": "sha512-bME5J9AC8ChwA7aEPJ6zym3w7aObZULHhbNLU0bKUhKsAkylkzUdq+0kdymh9rzi8nlNFl2bmldFBCKNJBUpuw==", "dev": true, "license": "MIT", "dependencies": { @@ -614,8 +543,6 @@ }, "node_modules/@babel/template": { "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", - "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", "dev": true, "license": "MIT", "dependencies": { @@ -629,8 +556,6 @@ }, "node_modules/@babel/traverse": { "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.9.tgz", - "integrity": "sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==", "dev": true, "license": "MIT", "dependencies": { @@ -648,8 +573,6 @@ }, "node_modules/@babel/traverse/node_modules/globals": { "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true, "license": "MIT", "engines": { @@ -658,8 +581,6 @@ }, "node_modules/@babel/types": { "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz", - "integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", @@ -671,8 +592,6 @@ }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, "license": "MIT", "dependencies": { @@ -684,8 +603,6 @@ }, "node_modules/@csstools/color-helpers": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.1.tgz", - "integrity": "sha512-MKtmkA0BX87PKaO1NFRTFH+UnkgnmySQOvNxJubsadusqPEC2aJ9MOQiMceZJJ6oitUl/i0L6u0M1IrmAOmgBA==", "dev": true, "funding": [ { @@ -704,8 +621,6 @@ }, "node_modules/@csstools/css-calc": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.1.tgz", - "integrity": "sha512-rL7kaUnTkL9K+Cvo2pnCieqNpTKgQzy5f+N+5Iuko9HAoasP+xgprVh7KN/MaJVvVL1l0EzQq2MoqBHKSrDrag==", "dev": true, "funding": [ { @@ -728,8 +643,6 @@ }, "node_modules/@csstools/css-color-parser": { "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.7.tgz", - "integrity": "sha512-nkMp2mTICw32uE5NN+EsJ4f5N+IGFeCFu4bGpiKgb2Pq/7J/MpyLBeQ5ry4KKtRFZaYs6sTmcMYrSRIyj5DFKA==", "dev": true, "funding": [ { @@ -756,8 +669,6 @@ }, "node_modules/@csstools/css-parser-algorithms": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz", - "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==", "dev": true, "funding": [ { @@ -779,8 +690,6 @@ }, "node_modules/@csstools/css-tokenizer": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz", - "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==", "dev": true, "funding": [ { @@ -797,298 +706,8 @@ "node": ">=18" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", - "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", - "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", - "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", - "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", - "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", - "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", - "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", - "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", - "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", - "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", - "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", - "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", - "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", - "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", - "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", - "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, "node_modules/@esbuild/linux-x64": { "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", - "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", "cpu": [ "x64" ], @@ -1103,154 +722,8 @@ "node": ">=18" } }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", - "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", - "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", - "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", - "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", - "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", - "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", - "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", - "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">=18" - } - }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", - "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", "dev": true, "license": "MIT", "dependencies": { @@ -1268,8 +741,6 @@ }, "node_modules/@eslint-community/regexpp": { "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, "license": "MIT", "engines": { @@ -1278,8 +749,6 @@ }, "node_modules/@eslint/compat": { "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-1.2.6.tgz", - "integrity": "sha512-k7HNCqApoDHM6XzT30zGoETj+D+uUcZUb+IVAJmar3u6bvHf7hhHJcWx09QHj4/a2qrKZMWU0E16tvkiAdv06Q==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1296,8 +765,6 @@ }, "node_modules/@eslint/config-array": { "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", - "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1311,8 +778,6 @@ }, "node_modules/@eslint/config-array/node_modules/brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "license": "MIT", "dependencies": { @@ -1322,8 +787,6 @@ }, "node_modules/@eslint/config-array/node_modules/minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", "dependencies": { @@ -1335,8 +798,6 @@ }, "node_modules/@eslint/core": { "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.11.0.tgz", - "integrity": "sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1348,8 +809,6 @@ }, "node_modules/@eslint/eslintrc": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", - "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", "dev": true, "license": "MIT", "dependencies": { @@ -1372,8 +831,6 @@ }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "license": "MIT", "dependencies": { @@ -1383,8 +840,6 @@ }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", "dependencies": { @@ -1396,8 +851,6 @@ }, "node_modules/@eslint/js": { "version": "9.20.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.20.0.tgz", - "integrity": "sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ==", "dev": true, "license": "MIT", "engines": { @@ -1406,8 +859,6 @@ }, "node_modules/@eslint/object-schema": { "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1416,8 +867,6 @@ }, "node_modules/@eslint/plugin-kit": { "version": "0.2.6", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.6.tgz", - "integrity": "sha512-+0TjwR1eAUdZtvv/ir1mGX+v0tUoR3VEPB8Up0LLJC+whRW0GgBBtpbOkg/a/U4Dxa6l5a3l9AJ1aWIQVyoWJA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1430,14 +879,11 @@ }, "node_modules/@gar/promisify": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", - "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "license": "MIT", "optional": true }, "node_modules/@humanfs/core": { "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1446,8 +892,6 @@ }, "node_modules/@humanfs/node": { "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1460,8 +904,6 @@ }, "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1474,8 +916,6 @@ }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1488,8 +928,6 @@ }, "node_modules/@humanwhocodes/retry": { "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", - "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1502,8 +940,6 @@ }, "node_modules/@intlify/core-base": { "version": "10.0.5", - "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-10.0.5.tgz", - "integrity": "sha512-F3snDTQs0MdvnnyzTDTVkOYVAZOE/MHwRvF7mn7Jw1yuih4NrFYLNYIymGlLmq4HU2iIdzYsZ7f47bOcwY73XQ==", "license": "MIT", "dependencies": { "@intlify/message-compiler": "10.0.5", @@ -1518,8 +954,6 @@ }, "node_modules/@intlify/message-compiler": { "version": "10.0.5", - "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-10.0.5.tgz", - "integrity": "sha512-6GT1BJ852gZ0gItNZN2krX5QAmea+cmdjMvsWohArAZ3GmHdnNANEcF9JjPXAMRtQ6Ux5E269ymamg/+WU6tQA==", "license": "MIT", "dependencies": { "@intlify/shared": "10.0.5", @@ -1534,8 +968,6 @@ }, "node_modules/@intlify/shared": { "version": "10.0.5", - "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-10.0.5.tgz", - "integrity": "sha512-bmsP4L2HqBF6i6uaMqJMcFBONVjKt+siGluRq4Ca4C0q7W2eMaVZr8iCgF9dKbcVXutftkC7D6z2SaSMmLiDyA==", "license": "MIT", "engines": { "node": ">= 16" @@ -1546,8 +978,6 @@ }, "node_modules/@isaacs/cliui": { "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, "license": "ISC", "dependencies": { @@ -1562,95 +992,8 @@ "node": ">=12" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/@jercle/yargonaut": { "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@jercle/yargonaut/-/yargonaut-1.1.5.tgz", - "integrity": "sha512-zBp2myVvBHp1UaJsNTyS6q4UDKT7eRiqTS4oNTS6VQMd6mpxYOdbeK4pY279cDCdakGy6hG0J3ejoXZVsPwHqw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1661,8 +1004,6 @@ }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", "dev": true, "license": "MIT", "dependencies": { @@ -1676,8 +1017,6 @@ }, "node_modules/@jridgewell/gen-mapping/node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1687,8 +1026,6 @@ }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, "license": "MIT", "engines": { @@ -1697,8 +1034,6 @@ }, "node_modules/@jridgewell/set-array": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, "license": "MIT", "engines": { @@ -1707,14 +1042,10 @@ }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1724,8 +1055,6 @@ }, "node_modules/@mikro-orm/cli": { "version": "6.4.6", - "resolved": "https://registry.npmjs.org/@mikro-orm/cli/-/cli-6.4.6.tgz", - "integrity": "sha512-sTMoDSJrnHZBT+ZAG40OeZwR9zRTYHtaaub9OoMM2CrxfI1KeiNqL/XFB4LaM5SVRAbnoEFpMJwQ8KS+5NcN9w==", "dev": true, "license": "MIT", "dependencies": { @@ -1746,8 +1075,6 @@ }, "node_modules/@mikro-orm/core": { "version": "6.4.6", - "resolved": "https://registry.npmjs.org/@mikro-orm/core/-/core-6.4.6.tgz", - "integrity": "sha512-xVm/ALG/3vTMgh6SrvojJ6jjMa0s2hNzWN0triDB16BaNdLwWE4aAaAe+3CuoMFqJAArSOUISTEjExbzELB1ZA==", "license": "MIT", "dependencies": { "dataloader": "2.2.3", @@ -1767,8 +1094,6 @@ }, "node_modules/@mikro-orm/knex": { "version": "6.4.6", - "resolved": "https://registry.npmjs.org/@mikro-orm/knex/-/knex-6.4.6.tgz", - "integrity": "sha512-o6t67tFH/GuPZCCEtKbTTL8HDXNgB2ITjButCTZLwteL0qI9yE/f7K6K+dEUKW+hAL3KRvc2BQeumvCVWFeISg==", "license": "MIT", "dependencies": { "fs-extra": "11.3.0", @@ -1798,8 +1123,6 @@ }, "node_modules/@mikro-orm/postgresql": { "version": "6.4.6", - "resolved": "https://registry.npmjs.org/@mikro-orm/postgresql/-/postgresql-6.4.6.tgz", - "integrity": "sha512-ZcuGp6n/SPzkHPANksjdLPyeu6jT7WCg3ueNViVrxdsguCi+/grz4I+hbOQDXV8uNHCAUOw6+WP2ndcVEYkZZQ==", "license": "MIT", "dependencies": { "@mikro-orm/knex": "6.4.6", @@ -1817,8 +1140,6 @@ }, "node_modules/@mikro-orm/reflection": { "version": "6.4.6", - "resolved": "https://registry.npmjs.org/@mikro-orm/reflection/-/reflection-6.4.6.tgz", - "integrity": "sha512-7mL7HFVnaOOhDNgLjjndWyeJUtOl2wKn0spSqB8uRjS4XtwNEGVZNkW5YD1t/x7TJ99wUhe+oRDiySciiJSeBQ==", "license": "MIT", "dependencies": { "globby": "11.1.0", @@ -1833,8 +1154,6 @@ }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", @@ -1846,8 +1165,6 @@ }, "node_modules/@nodelib/fs.stat": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "license": "MIT", "engines": { "node": ">= 8" @@ -1855,8 +1172,6 @@ }, "node_modules/@nodelib/fs.walk": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", @@ -1868,8 +1183,7 @@ }, "node_modules/@npmcli/fs": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", - "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "license": "ISC", "optional": true, "dependencies": { "@gar/promisify": "^1.0.1", @@ -1878,9 +1192,7 @@ }, "node_modules/@npmcli/move-file": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", - "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", - "deprecated": "This functionality has been moved to @npmcli/fs", + "license": "MIT", "optional": true, "dependencies": { "mkdirp": "^1.0.4", @@ -1892,15 +1204,11 @@ }, "node_modules/@one-ini/wasm": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", - "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", "dev": true, "license": "MIT" }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, "license": "MIT", "optional": true, @@ -1910,8 +1218,6 @@ }, "node_modules/@pkgr/core": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", - "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", "dev": true, "license": "MIT", "engines": { @@ -1923,8 +1229,6 @@ }, "node_modules/@playwright/test": { "version": "1.50.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.50.1.tgz", - "integrity": "sha512-Jii3aBg+CEDpgnuDxEp/h7BimHcUTDlpEtce89xEumlJ5ef2hqepZ+PWp1DDpYC/VO9fmWVI1IlEaoI5fK9FXQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1939,15 +1243,11 @@ }, "node_modules/@polka/url": { "version": "1.0.0-next.28", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz", - "integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==", "dev": true, "license": "MIT" }, "node_modules/@rollup/pluginutils": { "version": "5.1.4", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", - "integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1967,17 +1267,8 @@ } } }, - "node_modules/@rollup/pluginutils/node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true, - "license": "MIT" - }, "node_modules/@rollup/pluginutils/node_modules/picomatch": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, "license": "MIT", "engines": { @@ -1987,206 +1278,8 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.8.tgz", - "integrity": "sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.8.tgz", - "integrity": "sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.8.tgz", - "integrity": "sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.8.tgz", - "integrity": "sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.8.tgz", - "integrity": "sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.8.tgz", - "integrity": "sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.8.tgz", - "integrity": "sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.8.tgz", - "integrity": "sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.8.tgz", - "integrity": "sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.8.tgz", - "integrity": "sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.8.tgz", - "integrity": "sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.8.tgz", - "integrity": "sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.8.tgz", - "integrity": "sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.8.tgz", - "integrity": "sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, "node_modules/@rollup/rollup-linux-x64-gnu": { "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.8.tgz", - "integrity": "sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA==", "cpu": [ "x64" ], @@ -2199,8 +1292,6 @@ }, "node_modules/@rollup/rollup-linux-x64-musl": { "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.8.tgz", - "integrity": "sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ==", "cpu": [ "x64" ], @@ -2211,59 +1302,13 @@ "linux" ] }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.8.tgz", - "integrity": "sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.8.tgz", - "integrity": "sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.8.tgz", - "integrity": "sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@sec-ant/readable-stream": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", - "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", "dev": true, "license": "MIT" }, "node_modules/@sindresorhus/merge-streams": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", - "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", "dev": true, "license": "MIT", "engines": { @@ -2275,8 +1320,7 @@ }, "node_modules/@tootallnate/once": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "license": "MIT", "optional": true, "engines": { "node": ">= 6" @@ -2284,8 +1328,6 @@ }, "node_modules/@ts-morph/common": { "version": "0.26.1", - "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.26.1.tgz", - "integrity": "sha512-Sn28TGl/4cFpcM+jwsH1wLncYq3FtN/BIpem+HOygfBWPT5pAeS5dB4VFVzV8FbnOKHpDLZmvAl4AjPEev5idA==", "license": "MIT", "dependencies": { "fast-glob": "^3.3.2", @@ -2295,44 +1337,31 @@ }, "node_modules/@tsconfig/node10": { "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", "dev": true, "license": "MIT" }, "node_modules/@tsconfig/node12": { "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", "dev": true, "license": "MIT" }, "node_modules/@tsconfig/node14": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", "dev": true, "license": "MIT" }, "node_modules/@tsconfig/node16": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true, "license": "MIT" }, "node_modules/@tsconfig/node22": { "version": "22.0.0", - "resolved": "https://registry.npmjs.org/@tsconfig/node22/-/node22-22.0.0.tgz", - "integrity": "sha512-twLQ77zevtxobBOD4ToAtVmuYrpeYUh3qh+TEp+08IWhpsrIflVHqQ1F1CiPxQGL7doCdBIOOCF+1Tm833faNg==", "dev": true, "license": "MIT" }, "node_modules/@types/body-parser": { "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "dev": true, "license": "MIT", "dependencies": { "@types/connect": "*", @@ -2341,9 +1370,6 @@ }, "node_modules/@types/connect": { "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, "license": "MIT", "dependencies": { "@types/node": "*" @@ -2351,22 +1377,16 @@ }, "node_modules/@types/eslint-config-prettier": { "version": "6.11.3", - "resolved": "https://registry.npmjs.org/@types/eslint-config-prettier/-/eslint-config-prettier-6.11.3.tgz", - "integrity": "sha512-3wXCiM8croUnhg9LdtZUJQwNcQYGWxxdOWDjPe1ykCqJFPVpzAKfs/2dgSoCtAvdPeaponcWPI7mPcGGp9dkKQ==", "dev": true, "license": "MIT" }, "node_modules/@types/estree": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", "dev": true, "license": "MIT" }, "node_modules/@types/express": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.0.tgz", - "integrity": "sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2378,8 +1398,6 @@ }, "node_modules/@types/express-serve-static-core": { "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", - "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", "dev": true, "license": "MIT", "dependencies": { @@ -2391,21 +1409,14 @@ }, "node_modules/@types/http-errors": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", - "dev": true, "license": "MIT" }, "node_modules/@types/js-yaml": { "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", - "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", "license": "MIT" }, "node_modules/@types/jsdom": { "version": "21.1.7", - "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.7.tgz", - "integrity": "sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==", "dev": true, "license": "MIT", "dependencies": { @@ -2416,23 +1427,31 @@ }, "node_modules/@types/json-schema": { "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true, "license": "MIT" }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.9.tgz", + "integrity": "sha512-uoe+GxEuHbvy12OUQct2X9JenKM3qAscquYymuQN4fMWG9DBQtykrQEFcAbVACF7qaLw9BePSodUL0kquqBJpQ==", + "license": "MIT", + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, "node_modules/@types/mime": { "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true, + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", "license": "MIT" }, "node_modules/@types/node": { "version": "22.13.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.4.tgz", - "integrity": "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==", - "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.20.0" @@ -2440,23 +1459,14 @@ }, "node_modules/@types/qs": { "version": "6.9.18", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", - "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==", - "dev": true, "license": "MIT" }, "node_modules/@types/range-parser": { "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true, "license": "MIT" }, "node_modules/@types/send": { "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "dev": true, "license": "MIT", "dependencies": { "@types/mime": "^1", @@ -2465,9 +1475,6 @@ }, "node_modules/@types/serve-static": { "version": "1.15.7", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", - "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", - "dev": true, "license": "MIT", "dependencies": { "@types/http-errors": "*", @@ -2477,15 +1484,11 @@ }, "node_modules/@types/tough-cookie": { "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", - "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", "dev": true, "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.24.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.24.1.tgz", - "integrity": "sha512-ll1StnKtBigWIGqvYDVuDmXJHVH4zLVot1yQ4fJtLpL7qacwkxJc1T0bptqw+miBQ/QfUbhl1TcQ4accW5KUyA==", "dev": true, "license": "MIT", "dependencies": { @@ -2514,8 +1517,6 @@ }, "node_modules/@typescript-eslint/parser": { "version": "8.24.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.24.1.tgz", - "integrity": "sha512-Tqoa05bu+t5s8CTZFaGpCH2ub3QeT9YDkXbPd3uQ4SfsLoh1/vv2GEYAioPoxCWJJNsenXlC88tRjwoHNts1oQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2539,8 +1540,6 @@ }, "node_modules/@typescript-eslint/scope-manager": { "version": "8.24.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.24.1.tgz", - "integrity": "sha512-OdQr6BNBzwRjNEXMQyaGyZzgg7wzjYKfX2ZBV3E04hUCBDv3GQCHiz9RpqdUIiVrMgJGkXm3tcEh4vFSHreS2Q==", "dev": true, "license": "MIT", "dependencies": { @@ -2557,8 +1556,6 @@ }, "node_modules/@typescript-eslint/type-utils": { "version": "8.24.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.24.1.tgz", - "integrity": "sha512-/Do9fmNgCsQ+K4rCz0STI7lYB4phTtEXqqCAs3gZW0pnK7lWNkvWd5iW545GSmApm4AzmQXmSqXPO565B4WVrw==", "dev": true, "license": "MIT", "dependencies": { @@ -2581,8 +1578,6 @@ }, "node_modules/@typescript-eslint/types": { "version": "8.24.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.24.1.tgz", - "integrity": "sha512-9kqJ+2DkUXiuhoiYIUvIYjGcwle8pcPpdlfkemGvTObzgmYfJ5d0Qm6jwb4NBXP9W1I5tss0VIAnWFumz3mC5A==", "dev": true, "license": "MIT", "engines": { @@ -2595,8 +1590,6 @@ }, "node_modules/@typescript-eslint/typescript-estree": { "version": "8.24.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.24.1.tgz", - "integrity": "sha512-UPyy4MJ/0RE648DSKQe9g0VDSehPINiejjA6ElqnFaFIhI6ZEiZAkUI0D5MCk0bQcTf/LVqZStvQ6K4lPn/BRg==", "dev": true, "license": "MIT", "dependencies": { @@ -2622,8 +1615,6 @@ }, "node_modules/@typescript-eslint/utils": { "version": "8.24.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.24.1.tgz", - "integrity": "sha512-OOcg3PMMQx9EXspId5iktsI3eMaXVwlhC8BvNnX6B5w9a4dVgpkQZuU8Hy67TolKcl+iFWq0XX+jbDGN4xWxjQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2646,8 +1637,6 @@ }, "node_modules/@typescript-eslint/visitor-keys": { "version": "8.24.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.24.1.tgz", - "integrity": "sha512-EwVHlp5l+2vp8CoqJm9KikPZgi3gbdZAtabKT9KPShGeOcJhsv4Zdo3oc8T8I0uKEmYoU4ItyxbptjF08enaxg==", "dev": true, "license": "MIT", "dependencies": { @@ -2664,8 +1653,6 @@ }, "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "dev": true, "license": "Apache-2.0", "engines": { @@ -2677,8 +1664,6 @@ }, "node_modules/@vitejs/plugin-vue": { "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.1.tgz", - "integrity": "sha512-cxh314tzaWwOLqVes2gnnCtvBDcM1UMdn+iFR+UjAn411dPT3tOmqrJjbMd7koZpMAmBM/GqeV4n9ge7JSiJJQ==", "dev": true, "license": "MIT", "engines": { @@ -2691,8 +1676,6 @@ }, "node_modules/@vitest/eslint-plugin": { "version": "1.1.31", - "resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.1.31.tgz", - "integrity": "sha512-xlsLr+e+AXZ/00eVZCtNmMeCJoJaRCoLDiAgLcxgQjSS1EertieB2MUHf8xIqPKs9lECc/UpL+y1xDcpvi02hw==", "dev": true, "license": "MIT", "peerDependencies": { @@ -2712,8 +1695,6 @@ }, "node_modules/@vitest/expect": { "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.6.tgz", - "integrity": "sha512-zBduHf/ja7/QRX4HdP1DSq5XrPgdN+jzLOwaTq/0qZjYfgETNFCKf9nOAp2j3hmom3oTbczuUzrzg9Hafh7hNg==", "dev": true, "license": "MIT", "dependencies": { @@ -2728,8 +1709,6 @@ }, "node_modules/@vitest/mocker": { "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.6.tgz", - "integrity": "sha512-KPztr4/tn7qDGZfqlSPQoF2VgJcKxnDNhmfR3VgZ6Fy1bO8T9Fc1stUiTXtqz0yG24VpD00pZP5f8EOFknjNuQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2753,10 +1732,16 @@ } } }, + "node_modules/@vitest/mocker/node_modules/estree-walker": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/@vitest/pretty-format": { "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.6.tgz", - "integrity": "sha512-Zyctv3dbNL+67qtHfRnUE/k8qxduOamRfAL1BurEIQSyOEFffoMvx2pnDSSbKAAVxY0Ej2J/GH2dQKI0W2JyVg==", "dev": true, "license": "MIT", "dependencies": { @@ -2768,8 +1753,6 @@ }, "node_modules/@vitest/runner": { "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.6.tgz", - "integrity": "sha512-JopP4m/jGoaG1+CBqubV/5VMbi7L+NQCJTu1J1Pf6YaUbk7bZtaq5CX7p+8sY64Sjn1UQ1XJparHfcvTTdu9cA==", "dev": true, "license": "MIT", "dependencies": { @@ -2782,8 +1765,6 @@ }, "node_modules/@vitest/snapshot": { "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.6.tgz", - "integrity": "sha512-qKSmxNQwT60kNwwJHMVwavvZsMGXWmngD023OHSgn873pV0lylK7dwBTfYP7e4URy5NiBCHHiQGA9DHkYkqRqg==", "dev": true, "license": "MIT", "dependencies": { @@ -2797,8 +1778,6 @@ }, "node_modules/@vitest/spy": { "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.6.tgz", - "integrity": "sha512-HfOGx/bXtjy24fDlTOpgiAEJbRfFxoX3zIGagCqACkFKKZ/TTOE6gYMKXlqecvxEndKFuNHcHqP081ggZ2yM0Q==", "dev": true, "license": "MIT", "dependencies": { @@ -2810,8 +1789,6 @@ }, "node_modules/@vitest/utils": { "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.6.tgz", - "integrity": "sha512-18ktZpf4GQFTbf9jK543uspU03Q2qya7ZGya5yiZ0Gx0nnnalBvd5ZBislbl2EhLjM8A8rt4OilqKG7QwcGkvQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2825,8 +1802,6 @@ }, "node_modules/@volar/language-core": { "version": "2.4.11", - "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.11.tgz", - "integrity": "sha512-lN2C1+ByfW9/JRPpqScuZt/4OrUUse57GLI6TbLgTIqBVemdl1wNcZ1qYGEo2+Gw8coYLgCy7SuKqn6IrQcQgg==", "dev": true, "license": "MIT", "dependencies": { @@ -2835,15 +1810,11 @@ }, "node_modules/@volar/source-map": { "version": "2.4.11", - "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.11.tgz", - "integrity": "sha512-ZQpmafIGvaZMn/8iuvCFGrW3smeqkq/IIh9F1SdSx9aUl0J4Iurzd6/FhmjNO5g2ejF3rT45dKskgXWiofqlZQ==", "dev": true, "license": "MIT" }, "node_modules/@volar/typescript": { "version": "2.4.11", - "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.11.tgz", - "integrity": "sha512-2DT+Tdh88Spp5PyPbqhyoYavYCPDsqbHLFwcUI9K1NlY1YgUJvujGdrqUp0zWxnW7KWNTr3xSpMuv2WnaTKDAw==", "dev": true, "license": "MIT", "dependencies": { @@ -2854,15 +1825,11 @@ }, "node_modules/@vue/babel-helper-vue-transform-on": { "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.2.5.tgz", - "integrity": "sha512-lOz4t39ZdmU4DJAa2hwPYmKc8EsuGa2U0L9KaZaOJUt0UwQNjNA3AZTq6uEivhOKhhG1Wvy96SvYBoFmCg3uuw==", "dev": true, "license": "MIT" }, "node_modules/@vue/babel-plugin-jsx": { "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.2.5.tgz", - "integrity": "sha512-zTrNmOd4939H9KsRIGmmzn3q2zvv1mjxkYZHgqHZgDrXz5B1Q3WyGEjO2f+JrmKghvl1JIRcvo63LgM1kH5zFg==", "dev": true, "license": "MIT", "dependencies": { @@ -2888,8 +1855,6 @@ }, "node_modules/@vue/babel-plugin-resolve-type": { "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@vue/babel-plugin-resolve-type/-/babel-plugin-resolve-type-1.2.5.tgz", - "integrity": "sha512-U/ibkQrf5sx0XXRnUZD1mo5F7PkpKyTbfXM3a3rC4YnUz6crHEz9Jg09jzzL6QYlXNto/9CePdOg/c87O4Nlfg==", "dev": true, "license": "MIT", "dependencies": { @@ -2905,8 +1870,6 @@ }, "node_modules/@vue/compiler-core": { "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz", - "integrity": "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==", "license": "MIT", "dependencies": { "@babel/parser": "^7.25.3", @@ -2916,16 +1879,8 @@ "source-map-js": "^1.2.0" } }, - "node_modules/@vue/compiler-core/node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "license": "MIT" - }, "node_modules/@vue/compiler-dom": { "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz", - "integrity": "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==", "license": "MIT", "dependencies": { "@vue/compiler-core": "3.5.13", @@ -2934,8 +1889,6 @@ }, "node_modules/@vue/compiler-sfc": { "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz", - "integrity": "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==", "license": "MIT", "dependencies": { "@babel/parser": "^7.25.3", @@ -2949,16 +1902,8 @@ "source-map-js": "^1.2.0" } }, - "node_modules/@vue/compiler-sfc/node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "license": "MIT" - }, "node_modules/@vue/compiler-ssr": { "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz", - "integrity": "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==", "license": "MIT", "dependencies": { "@vue/compiler-dom": "3.5.13", @@ -2967,8 +1912,6 @@ }, "node_modules/@vue/compiler-vue2": { "version": "2.7.16", - "resolved": "https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz", - "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==", "dev": true, "license": "MIT", "dependencies": { @@ -2978,14 +1921,10 @@ }, "node_modules/@vue/devtools-api": { "version": "6.6.4", - "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", - "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", "license": "MIT" }, "node_modules/@vue/devtools-core": { "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-7.7.2.tgz", - "integrity": "sha512-lexREWj1lKi91Tblr38ntSsy6CvI8ba7u+jmwh2yruib/ltLUcsIzEjCnrkh1yYGGIKXbAuYV2tOG10fGDB9OQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3002,8 +1941,6 @@ }, "node_modules/@vue/devtools-core/node_modules/nanoid": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.0.tgz", - "integrity": "sha512-zDAl/llz8Ue/EblwSYwdxGBYfj46IM1dhjVi8dyp9LQffoIGxJEAHj2oeZ4uNcgycSRcQ83CnfcZqEJzVDLcDw==", "dev": true, "funding": [ { @@ -3021,8 +1958,6 @@ }, "node_modules/@vue/devtools-kit": { "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.2.tgz", - "integrity": "sha512-CY0I1JH3Z8PECbn6k3TqM1Bk9ASWxeMtTCvZr7vb+CHi+X/QwQm5F1/fPagraamKMAHVfuuCbdcnNg1A4CYVWQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3037,8 +1972,6 @@ }, "node_modules/@vue/devtools-shared": { "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.2.tgz", - "integrity": "sha512-uBFxnp8gwW2vD6FrJB8JZLUzVb6PNRG0B0jBnHsOH8uKyva2qINY8PTF5Te4QlTbMDqU5K6qtJDr6cNsKWhbOA==", "dev": true, "license": "MIT", "dependencies": { @@ -3047,8 +1980,6 @@ }, "node_modules/@vue/eslint-config-prettier": { "version": "10.2.0", - "resolved": "https://registry.npmjs.org/@vue/eslint-config-prettier/-/eslint-config-prettier-10.2.0.tgz", - "integrity": "sha512-GL3YBLwv/+b86yHcNNfPJxOTtVFJ4Mbc9UU3zR+KVoG7SwGTjPT+32fXamscNumElhcpXW3mT0DgzS9w32S7Bw==", "dev": true, "license": "MIT", "dependencies": { @@ -3062,8 +1993,6 @@ }, "node_modules/@vue/eslint-config-typescript": { "version": "14.4.0", - "resolved": "https://registry.npmjs.org/@vue/eslint-config-typescript/-/eslint-config-typescript-14.4.0.tgz", - "integrity": "sha512-daU+eAekEeVz3CReE4PRW25fe+OJDKwE28jHN6LimDEnuFMbJ6H4WGogEpNof276wVP6UvzOeJQfLFjB5mW29A==", "dev": true, "license": "MIT", "dependencies": { @@ -3088,8 +2017,6 @@ }, "node_modules/@vue/language-core": { "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.2.2.tgz", - "integrity": "sha512-QotO41kurE5PLf3vrNgGTk3QswO2PdUFjBwNiOi7zMmGhwb25PSTh9hD1MCgKC06AVv+8sZQvlL3Do4TTVHSiQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3113,8 +2040,6 @@ }, "node_modules/@vue/reactivity": { "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.13.tgz", - "integrity": "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==", "license": "MIT", "dependencies": { "@vue/shared": "3.5.13" @@ -3122,8 +2047,6 @@ }, "node_modules/@vue/runtime-core": { "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.13.tgz", - "integrity": "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==", "license": "MIT", "dependencies": { "@vue/reactivity": "3.5.13", @@ -3132,8 +2055,6 @@ }, "node_modules/@vue/runtime-dom": { "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz", - "integrity": "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==", "license": "MIT", "dependencies": { "@vue/reactivity": "3.5.13", @@ -3144,8 +2065,6 @@ }, "node_modules/@vue/server-renderer": { "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.13.tgz", - "integrity": "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==", "license": "MIT", "dependencies": { "@vue/compiler-ssr": "3.5.13", @@ -3157,14 +2076,10 @@ }, "node_modules/@vue/shared": { "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.13.tgz", - "integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==", "license": "MIT" }, "node_modules/@vue/test-utils": { "version": "2.4.6", - "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.4.6.tgz", - "integrity": "sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==", "dev": true, "license": "MIT", "dependencies": { @@ -3174,8 +2089,6 @@ }, "node_modules/@vue/tsconfig": { "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.7.0.tgz", - "integrity": "sha512-ku2uNz5MaZ9IerPPUyOHzyjhXoX2kVJaVf7hL315DC17vS6IiZRmmCPfggNbU16QTvM80+uYYy3eYJB59WCtvg==", "dev": true, "license": "MIT", "peerDependencies": { @@ -3193,8 +2106,6 @@ }, "node_modules/abbrev": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.0.tgz", - "integrity": "sha512-+/kfrslGQ7TNV2ecmQwMJj/B65g5KVq1/L3SGVZ3tCYGqlzFuFCGBZJtMP99wH3NpEUyAjn0zPdPUg0D+DwrOA==", "dev": true, "license": "ISC", "engines": { @@ -3203,8 +2114,6 @@ }, "node_modules/accepts": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "license": "MIT", "dependencies": { "mime-types": "^3.0.0", @@ -3216,8 +2125,6 @@ }, "node_modules/acorn": { "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, "license": "MIT", "bin": { @@ -3229,8 +2136,6 @@ }, "node_modules/acorn-jsx": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "license": "MIT", "peerDependencies": { @@ -3239,8 +2144,6 @@ }, "node_modules/acorn-walk": { "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", "dev": true, "license": "MIT", "dependencies": { @@ -3252,8 +2155,6 @@ }, "node_modules/agent-base": { "version": "7.1.3", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", - "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", "dev": true, "license": "MIT", "engines": { @@ -3262,8 +2163,7 @@ }, "node_modules/agentkeepalive": { "version": "4.6.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", - "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "license": "MIT", "optional": true, "dependencies": { "humanize-ms": "^1.2.1" @@ -3274,8 +2174,7 @@ }, "node_modules/aggregate-error": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "license": "MIT", "optional": true, "dependencies": { "clean-stack": "^2.0.0", @@ -3287,8 +2186,6 @@ }, "node_modules/ajv": { "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", "dependencies": { @@ -3303,26 +2200,23 @@ } }, "node_modules/alien-signals": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-1.0.4.tgz", - "integrity": "sha512-DJqqQD3XcsaQcQ1s+iE2jDUZmmQpXwHiR6fCAim/w87luaW+vmLY8fMlrdkmRwzaFXhkxf3rqPCR59tKVv1MDw==", + "version": "1.0.3", "dev": true, "license": "MIT" }, "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "version": "6.1.0", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, "node_modules/ansi-styles": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "license": "MIT", "dependencies": { @@ -3337,15 +2231,12 @@ }, "node_modules/aproba": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "license": "ISC", "optional": true }, "node_modules/are-we-there-yet": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", - "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", - "deprecated": "This package is no longer supported.", + "license": "ISC", "optional": true, "dependencies": { "delegates": "^1.0.0", @@ -3357,21 +2248,15 @@ }, "node_modules/arg": { "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "dev": true, "license": "MIT" }, "node_modules/argparse": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "license": "Python-2.0" }, "node_modules/array-union": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "license": "MIT", "engines": { "node": ">=8" @@ -3379,8 +2264,6 @@ }, "node_modules/assertion-error": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, "license": "MIT", "engines": { @@ -3389,21 +2272,15 @@ }, "node_modules/asynckit": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true, "license": "MIT" }, "node_modules/balanced-match": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, "node_modules/base64-js": { "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "funding": [ { "type": "github", @@ -3417,20 +2294,18 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/bindings": { "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", "dependencies": { "file-uri-to-path": "1.0.0" } }, "node_modules/birpc": { "version": "0.2.19", - "resolved": "https://registry.npmjs.org/birpc/-/birpc-0.2.19.tgz", - "integrity": "sha512-5WeXXAvTmitV1RqJFppT5QtUiz2p1mRSYU000Jkft5ZUCLJIk4uQriYNO50HknxKwM6jd8utNc66K1qGIwwWBQ==", "dev": true, "license": "MIT", "funding": { @@ -3439,8 +2314,7 @@ }, "node_modules/bl": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", @@ -3449,8 +2323,6 @@ }, "node_modules/body-parser": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.1.0.tgz", - "integrity": "sha512-/hPxh61E+ll0Ujp24Ilm64cykicul1ypfwjVttduAiEdtnJFvLePSrIPk+HMImtNv5270wOGCb1Tns2rybMkoQ==", "license": "MIT", "dependencies": { "bytes": "^3.1.2", @@ -3469,8 +2341,6 @@ }, "node_modules/body-parser/node_modules/qs": { "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -3484,15 +2354,11 @@ }, "node_modules/boolbase": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "dev": true, "license": "ISC" }, "node_modules/brace-expansion": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -3500,8 +2366,6 @@ }, "node_modules/braces": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -3512,8 +2376,6 @@ }, "node_modules/browserslist": { "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", "dev": true, "funding": [ { @@ -3545,8 +2407,6 @@ }, "node_modules/buffer": { "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "funding": [ { "type": "github", @@ -3561,15 +2421,20 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, "node_modules/bundle-name": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", - "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", "dev": true, "license": "MIT", "dependencies": { @@ -3584,8 +2449,6 @@ }, "node_modules/bytes": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -3593,8 +2456,6 @@ }, "node_modules/cac": { "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", "dev": true, "license": "MIT", "engines": { @@ -3603,8 +2464,7 @@ }, "node_modules/cacache": { "version": "15.3.0", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", - "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "license": "ISC", "optional": true, "dependencies": { "@npmcli/fs": "^1.0.0", @@ -3632,8 +2492,7 @@ }, "node_modules/cacache/node_modules/brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", "optional": true, "dependencies": { "balanced-match": "^1.0.0", @@ -3642,9 +2501,7 @@ }, "node_modules/cacache/node_modules/glob": { "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", "optional": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -3663,8 +2520,7 @@ }, "node_modules/cacache/node_modules/lru-cache": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", "optional": true, "dependencies": { "yallist": "^4.0.0" @@ -3675,8 +2531,7 @@ }, "node_modules/cacache/node_modules/minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", "optional": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -3687,8 +2542,7 @@ }, "node_modules/cacache/node_modules/minipass": { "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", "optional": true, "dependencies": { "yallist": "^4.0.0" @@ -3699,14 +2553,11 @@ }, "node_modules/cacache/node_modules/yallist": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", "optional": true }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -3718,8 +2569,6 @@ }, "node_modules/call-bound": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", - "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -3734,8 +2583,6 @@ }, "node_modules/callsites": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, "license": "MIT", "engines": { @@ -3744,8 +2591,6 @@ }, "node_modules/caniuse-lite": { "version": "1.0.30001700", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001700.tgz", - "integrity": "sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ==", "dev": true, "funding": [ { @@ -3765,8 +2610,6 @@ }, "node_modules/chai": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", - "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", "dev": true, "license": "MIT", "dependencies": { @@ -3782,8 +2625,6 @@ }, "node_modules/chalk": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", "dependencies": { @@ -3799,8 +2640,6 @@ }, "node_modules/check-error": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", - "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", "dev": true, "license": "MIT", "engines": { @@ -3809,16 +2648,14 @@ }, "node_modules/chownr": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "license": "ISC", "engines": { "node": ">=10" } }, "node_modules/clean-stack": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "license": "MIT", "optional": true, "engines": { "node": ">=6" @@ -3826,8 +2663,6 @@ }, "node_modules/cliui": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "license": "ISC", "dependencies": { @@ -3839,16 +2674,65 @@ "node": ">=12" } }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/code-block-writer": { "version": "13.0.3", - "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.3.tgz", - "integrity": "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==", "license": "MIT" }, "node_modules/color-convert": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3860,15 +2744,12 @@ }, "node_modules/color-name": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, "license": "MIT" }, "node_modules/color-support": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "license": "ISC", "optional": true, "bin": { "color-support": "bin.js" @@ -3876,14 +2757,10 @@ }, "node_modules/colorette": { "version": "2.0.19", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", - "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", "license": "MIT" }, "node_modules/combined-stream": { "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, "license": "MIT", "dependencies": { @@ -3895,8 +2772,6 @@ }, "node_modules/commander": { "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "license": "MIT", "engines": { "node": ">=14" @@ -3904,15 +2779,11 @@ }, "node_modules/concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "devOptional": true, "license": "MIT" }, "node_modules/config-chain": { "version": "1.1.13", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", - "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3922,14 +2793,11 @@ }, "node_modules/console-control-strings": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "license": "ISC", "optional": true }, "node_modules/content-disposition": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", - "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", "license": "MIT", "dependencies": { "safe-buffer": "5.2.1" @@ -3940,8 +2808,6 @@ }, "node_modules/content-type": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -3949,15 +2815,11 @@ }, "node_modules/convert-source-map": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, "license": "MIT" }, "node_modules/cookie": { "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -3965,8 +2827,6 @@ }, "node_modules/cookie-signature": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", "license": "MIT", "engines": { "node": ">=6.6.0" @@ -3974,8 +2834,6 @@ }, "node_modules/copy-anything": { "version": "3.0.5", - "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz", - "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==", "dev": true, "license": "MIT", "dependencies": { @@ -3990,15 +2848,11 @@ }, "node_modules/create-require": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true, "license": "MIT" }, "node_modules/cross-spawn": { "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", "dependencies": { @@ -4012,8 +2866,6 @@ }, "node_modules/cssesc": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true, "license": "MIT", "bin": { @@ -4025,8 +2877,6 @@ }, "node_modules/cssstyle": { "version": "4.2.1", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.2.1.tgz", - "integrity": "sha512-9+vem03dMXG7gDmZ62uqmRiMRNtinIZ9ZyuF6BdxzfOD+FdN5hretzynkn0ReS2DO2GSw76RWHs0UmJPI2zUjw==", "dev": true, "license": "MIT", "dependencies": { @@ -4039,14 +2889,10 @@ }, "node_modules/csstype": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "license": "MIT" }, "node_modules/data-urls": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", - "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", "dev": true, "license": "MIT", "dependencies": { @@ -4059,21 +2905,15 @@ }, "node_modules/dataloader": { "version": "2.2.3", - "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.2.3.tgz", - "integrity": "sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA==", "license": "MIT" }, "node_modules/de-indent": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", - "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", "dev": true, "license": "MIT" }, "node_modules/debug": { "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -4089,15 +2929,12 @@ }, "node_modules/decimal.js": { "version": "10.5.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", - "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", "dev": true, "license": "MIT" }, "node_modules/decompress-response": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", "dependencies": { "mimic-response": "^3.1.0" }, @@ -4110,8 +2947,6 @@ }, "node_modules/deep-eql": { "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", "dev": true, "license": "MIT", "engines": { @@ -4120,23 +2955,18 @@ }, "node_modules/deep-extend": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", "engines": { "node": ">=4.0.0" } }, "node_modules/deep-is": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, "license": "MIT" }, "node_modules/default-browser": { "version": "5.2.1", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", - "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", "dev": true, "license": "MIT", "dependencies": { @@ -4152,8 +2982,6 @@ }, "node_modules/default-browser-id": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", - "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", "dev": true, "license": "MIT", "engines": { @@ -4165,8 +2993,6 @@ }, "node_modules/define-lazy-prop": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", - "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", "dev": true, "license": "MIT", "engines": { @@ -4178,8 +3004,6 @@ }, "node_modules/delayed-stream": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true, "license": "MIT", "engines": { @@ -4188,14 +3012,11 @@ }, "node_modules/delegates": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "license": "MIT", "optional": true }, "node_modules/depd": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -4203,8 +3024,6 @@ }, "node_modules/destroy": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", "license": "MIT", "engines": { "node": ">= 0.8", @@ -4213,16 +3032,13 @@ }, "node_modules/detect-libc": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", - "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "license": "Apache-2.0", "engines": { "node": ">=8" } }, "node_modules/diff": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -4231,8 +3047,6 @@ }, "node_modules/dir-glob": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "license": "MIT", "dependencies": { "path-type": "^4.0.0" @@ -4243,8 +3057,6 @@ }, "node_modules/dotenv": { "version": "16.4.7", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", - "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -4255,8 +3067,6 @@ }, "node_modules/dunder-proto": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -4277,15 +3087,20 @@ }, "node_modules/eastasianwidth": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true, "license": "MIT" }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/editorconfig": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", - "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", "dev": true, "license": "MIT", "dependencies": { @@ -4303,8 +3118,6 @@ }, "node_modules/editorconfig/node_modules/minimatch": { "version": "9.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", - "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", "dev": true, "license": "ISC", "dependencies": { @@ -4319,28 +3132,20 @@ }, "node_modules/ee-first": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, "node_modules/electron-to-chromium": { "version": "1.5.102", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.102.tgz", - "integrity": "sha512-eHhqaja8tE/FNpIiBrvBjFV/SSKpyWHLvxuR9dPTdo+3V9ppdLmFB7ZZQ98qNovcngPLYIz0oOBF9P0FfZef5Q==", "dev": true, "license": "ISC" }, "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "version": "9.2.2", "dev": true, "license": "MIT" }, "node_modules/encodeurl": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -4348,8 +3153,7 @@ }, "node_modules/encoding": { "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "license": "MIT", "optional": true, "dependencies": { "iconv-lite": "^0.6.2" @@ -4357,8 +3161,7 @@ }, "node_modules/encoding/node_modules/iconv-lite": { "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", "optional": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -4369,16 +3172,13 @@ }, "node_modules/end-of-stream": { "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "license": "MIT", "dependencies": { "once": "^1.4.0" } }, "node_modules/entities": { "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -4389,8 +3189,7 @@ }, "node_modules/env-paths": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "license": "MIT", "optional": true, "engines": { "node": ">=6" @@ -4398,14 +3197,11 @@ }, "node_modules/err-code": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "license": "MIT", "optional": true }, "node_modules/error-stack-parser-es": { "version": "0.1.5", - "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-0.1.5.tgz", - "integrity": "sha512-xHku1X40RO+fO8yJ8Wh2f2rZWVjqyhb1zgq1yZ8aZRQkv6OOKhKWRUaht3eSCUbAOBaKIgM+ykwFLE+QUxgGeg==", "dev": true, "license": "MIT", "funding": { @@ -4414,8 +3210,6 @@ }, "node_modules/es-define-property": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -4423,8 +3217,6 @@ }, "node_modules/es-errors": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -4432,15 +3224,11 @@ }, "node_modules/es-module-lexer": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", - "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", "dev": true, "license": "MIT" }, "node_modules/es-object-atoms": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -4451,8 +3239,6 @@ }, "node_modules/es-set-tostringtag": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dev": true, "license": "MIT", "dependencies": { @@ -4467,8 +3253,6 @@ }, "node_modules/esbuild": { "version": "0.25.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", - "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -4508,8 +3292,6 @@ }, "node_modules/escalade": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "license": "MIT", "engines": { "node": ">=6" @@ -4517,14 +3299,10 @@ }, "node_modules/escape-html": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "license": "MIT" }, "node_modules/escape-string-regexp": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", "engines": { @@ -4536,8 +3314,6 @@ }, "node_modules/eslint": { "version": "9.20.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.20.1.tgz", - "integrity": "sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g==", "dev": true, "license": "MIT", "dependencies": { @@ -4596,8 +3372,6 @@ }, "node_modules/eslint-config-prettier": { "version": "10.0.1", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.0.1.tgz", - "integrity": "sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw==", "dev": true, "license": "MIT", "bin": { @@ -4609,8 +3383,6 @@ }, "node_modules/eslint-plugin-playwright": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-2.2.0.tgz", - "integrity": "sha512-qSQpAw7RcSzE3zPp8FMGkthaCWovHZ/BsXtpmnGax9vQLIovlh1bsZHEa2+j2lv9DWhnyeLM/qZmp7ffQZfQvg==", "dev": true, "license": "MIT", "workspaces": [ @@ -4628,8 +3400,6 @@ }, "node_modules/eslint-plugin-playwright/node_modules/globals": { "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4644,8 +3414,6 @@ }, "node_modules/eslint-plugin-prettier": { "version": "5.2.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.3.tgz", - "integrity": "sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw==", "dev": true, "license": "MIT", "dependencies": { @@ -4675,8 +3443,6 @@ }, "node_modules/eslint-plugin-vue": { "version": "9.32.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.32.0.tgz", - "integrity": "sha512-b/Y05HYmnB/32wqVcjxjHZzNpwxj1onBOvqW89W+V+XNG1dRuaFbNd3vT9CLbr2LXjEoq+3vn8DanWf7XU22Ug==", "dev": true, "license": "MIT", "dependencies": { @@ -4698,8 +3464,6 @@ }, "node_modules/eslint-plugin-vue/node_modules/globals": { "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4714,8 +3478,6 @@ }, "node_modules/eslint-scope": { "version": "8.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", - "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -4731,8 +3493,6 @@ }, "node_modules/eslint-visitor-keys": { "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "license": "Apache-2.0", "engines": { @@ -4744,8 +3504,6 @@ }, "node_modules/eslint/node_modules/brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "license": "MIT", "dependencies": { @@ -4755,8 +3513,6 @@ }, "node_modules/eslint/node_modules/eslint-visitor-keys": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "dev": true, "license": "Apache-2.0", "engines": { @@ -4768,8 +3524,6 @@ }, "node_modules/eslint/node_modules/minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", "dependencies": { @@ -4781,8 +3535,6 @@ }, "node_modules/esm": { "version": "3.2.25", - "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", - "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", "license": "MIT", "engines": { "node": ">=6" @@ -4790,8 +3542,6 @@ }, "node_modules/espree": { "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -4808,8 +3558,6 @@ }, "node_modules/espree/node_modules/eslint-visitor-keys": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "dev": true, "license": "Apache-2.0", "engines": { @@ -4821,8 +3569,6 @@ }, "node_modules/esprima": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", @@ -4834,8 +3580,6 @@ }, "node_modules/esquery": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -4847,8 +3591,6 @@ }, "node_modules/esrecurse": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -4860,8 +3602,6 @@ }, "node_modules/estraverse": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -4869,19 +3609,11 @@ } }, "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } + "version": "2.0.2", + "license": "MIT" }, "node_modules/esutils": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -4890,8 +3622,6 @@ }, "node_modules/etag": { "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -4899,8 +3629,6 @@ }, "node_modules/execa": { "version": "9.5.2", - "resolved": "https://registry.npmjs.org/execa/-/execa-9.5.2.tgz", - "integrity": "sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q==", "dev": true, "license": "MIT", "dependencies": { @@ -4926,16 +3654,13 @@ }, "node_modules/expand-template": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", "engines": { "node": ">=6" } }, "node_modules/expect-type": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", - "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -4944,8 +3669,6 @@ }, "node_modules/express": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/express/-/express-5.0.1.tgz", - "integrity": "sha512-ORF7g6qGnD+YtUG9yx4DFoqCShNMmUKiXuT5oWMHiOvt/4WFbHC6yCwQMTSBMno7AqntNCAzzcnnjowRkTL9eQ==", "license": "MIT", "dependencies": { "accepts": "^2.0.0", @@ -4985,10 +3708,28 @@ "node": ">= 18" } }, + "node_modules/express-jwt": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/express-jwt/-/express-jwt-8.5.1.tgz", + "integrity": "sha512-Dv6QjDLpR2jmdb8M6XQXiCcpEom7mK8TOqnr0/TngDKsG2DHVkO8+XnVxkJVN7BuS1I3OrGw6N8j5DaaGgkDRQ==", + "license": "MIT", + "dependencies": { + "@types/jsonwebtoken": "^9", + "express-unless": "^2.1.3", + "jsonwebtoken": "^9.0.0" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/express-unless": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/express-unless/-/express-unless-2.1.3.tgz", + "integrity": "sha512-wj4tLMyCVYuIIKHGt0FhCtIViBcwzWejX0EjNxveAa6dG+0XBCQhMbx+PnkLkFCxLC69qoFrxds4pIyL88inaQ==", + "license": "MIT" + }, "node_modules/express/node_modules/debug": { "version": "4.3.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", - "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", "license": "MIT", "dependencies": { "ms": "2.1.2" @@ -5004,28 +3745,20 @@ }, "node_modules/express/node_modules/ms": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "license": "MIT" }, "node_modules/fast-deep-equal": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true, "license": "MIT" }, "node_modules/fast-diff": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", "dev": true, "license": "Apache-2.0" }, "node_modules/fast-glob": { "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -5040,8 +3773,6 @@ }, "node_modules/fast-glob/node_modules/glob-parent": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -5052,22 +3783,16 @@ }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true, "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true, "license": "MIT" }, "node_modules/fastq": { "version": "1.19.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", - "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==", "license": "ISC", "dependencies": { "reusify": "^1.0.4" @@ -5075,8 +3800,6 @@ }, "node_modules/figlet": { "version": "1.8.0", - "resolved": "https://registry.npmjs.org/figlet/-/figlet-1.8.0.tgz", - "integrity": "sha512-chzvGjd+Sp7KUvPHZv6EXV5Ir3Q7kYNpCr4aHrRW79qFtTefmQZNny+W1pW9kf5zeE6dikku2W50W/wAH2xWgw==", "dev": true, "license": "MIT", "bin": { @@ -5088,8 +3811,6 @@ }, "node_modules/figures": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", - "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", "dev": true, "license": "MIT", "dependencies": { @@ -5104,8 +3825,6 @@ }, "node_modules/file-entry-cache": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5117,13 +3836,10 @@ }, "node_modules/file-uri-to-path": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + "license": "MIT" }, "node_modules/fill-range": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -5134,8 +3850,6 @@ }, "node_modules/finalhandler": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.0.0.tgz", - "integrity": "sha512-MX6Zo2adDViYh+GcxxB1dpO43eypOGUOL12rLCOTMQv/DfIbpSJUy4oQIIZhVZkH9e+bZWKMon0XHFEju16tkQ==", "license": "MIT", "dependencies": { "debug": "2.6.9", @@ -5152,8 +3866,6 @@ }, "node_modules/finalhandler/node_modules/debug": { "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", "dependencies": { "ms": "2.0.0" @@ -5161,8 +3873,6 @@ }, "node_modules/finalhandler/node_modules/encodeurl": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -5170,14 +3880,10 @@ }, "node_modules/finalhandler/node_modules/ms": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, "node_modules/find-up": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "license": "MIT", "dependencies": { @@ -5193,8 +3899,6 @@ }, "node_modules/flat-cache": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "license": "MIT", "dependencies": { @@ -5207,15 +3911,11 @@ }, "node_modules/flatted": { "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true, "license": "ISC" }, "node_modules/foreground-child": { "version": "3.3.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", - "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", "dev": true, "license": "ISC", "dependencies": { @@ -5231,8 +3931,6 @@ }, "node_modules/form-data": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", "dev": true, "license": "MIT", "dependencies": { @@ -5247,8 +3945,6 @@ }, "node_modules/form-data/node_modules/mime-db": { "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, "license": "MIT", "engines": { @@ -5257,8 +3953,6 @@ }, "node_modules/form-data/node_modules/mime-types": { "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, "license": "MIT", "dependencies": { @@ -5270,8 +3964,6 @@ }, "node_modules/forwarded": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -5279,8 +3971,6 @@ }, "node_modules/fresh": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -5288,13 +3978,10 @@ }, "node_modules/fs-constants": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + "license": "MIT" }, "node_modules/fs-extra": { "version": "11.3.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", - "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", @@ -5307,8 +3994,7 @@ }, "node_modules/fs-minipass": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "license": "ISC", "dependencies": { "minipass": "^3.0.0" }, @@ -5318,8 +4004,7 @@ }, "node_modules/fs-minipass/node_modules/minipass": { "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -5329,34 +4014,15 @@ }, "node_modules/fs-minipass/node_modules/yallist": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "license": "ISC" }, "node_modules/fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC", "optional": true }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5364,9 +4030,7 @@ }, "node_modules/gauge": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", - "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", - "deprecated": "This package is no longer supported.", + "license": "ISC", "optional": true, "dependencies": { "aproba": "^1.0.3 || ^2.0.0", @@ -5384,8 +4048,7 @@ }, "node_modules/gauge/node_modules/ansi-regex": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", "optional": true, "engines": { "node": ">=8" @@ -5393,20 +4056,17 @@ }, "node_modules/gauge/node_modules/emoji-regex": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT", "optional": true }, "node_modules/gauge/node_modules/signal-exit": { "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC", "optional": true }, "node_modules/gauge/node_modules/string-width": { "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", "optional": true, "dependencies": { "emoji-regex": "^8.0.0", @@ -5419,8 +4079,7 @@ }, "node_modules/gauge/node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", "optional": true, "dependencies": { "ansi-regex": "^5.0.1" @@ -5431,8 +4090,6 @@ }, "node_modules/gensync": { "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, "license": "MIT", "engines": { @@ -5441,8 +4098,6 @@ }, "node_modules/get-caller-file": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, "license": "ISC", "engines": { @@ -5451,8 +4106,6 @@ }, "node_modules/get-intrinsic": { "version": "1.2.7", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", - "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -5475,8 +4128,6 @@ }, "node_modules/get-package-type": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "license": "MIT", "engines": { "node": ">=8.0.0" @@ -5484,8 +4135,6 @@ }, "node_modules/get-proto": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -5497,8 +4146,6 @@ }, "node_modules/get-stream": { "version": "9.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", - "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", "dev": true, "license": "MIT", "dependencies": { @@ -5514,8 +4161,6 @@ }, "node_modules/get-tsconfig": { "version": "4.10.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", - "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", "dev": true, "license": "MIT", "dependencies": { @@ -5527,19 +4172,14 @@ }, "node_modules/getopts": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/getopts/-/getopts-2.3.0.tgz", - "integrity": "sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==", "license": "MIT" }, "node_modules/github-from-package": { "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" + "license": "MIT" }, "node_modules/glob": { "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "license": "ISC", "dependencies": { @@ -5559,8 +4199,6 @@ }, "node_modules/glob-parent": { "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "license": "ISC", "dependencies": { @@ -5572,8 +4210,6 @@ }, "node_modules/globals": { "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, "license": "MIT", "engines": { @@ -5585,8 +4221,6 @@ }, "node_modules/globby": { "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "license": "MIT", "dependencies": { "array-union": "^2.1.0", @@ -5605,8 +4239,6 @@ }, "node_modules/gopd": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -5617,21 +4249,15 @@ }, "node_modules/graceful-fs": { "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, "node_modules/graphemer": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true, "license": "MIT" }, "node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", "engines": { @@ -5640,8 +4266,6 @@ }, "node_modules/has-symbols": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -5652,8 +4276,6 @@ }, "node_modules/has-tostringtag": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, "license": "MIT", "dependencies": { @@ -5668,14 +4290,11 @@ }, "node_modules/has-unicode": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "license": "ISC", "optional": true }, "node_modules/hasown": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -5686,8 +4305,6 @@ }, "node_modules/he": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true, "license": "MIT", "bin": { @@ -5696,15 +4313,11 @@ }, "node_modules/hookable": { "version": "5.5.3", - "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", - "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", "dev": true, "license": "MIT" }, "node_modules/html-encoding-sniffer": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", - "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5716,8 +4329,6 @@ }, "node_modules/html-tags": { "version": "3.3.1", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", - "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", "dev": true, "license": "MIT", "engines": { @@ -5729,14 +4340,11 @@ }, "node_modules/http-cache-semantics": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "license": "BSD-2-Clause", "optional": true }, "node_modules/http-errors": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "license": "MIT", "dependencies": { "depd": "2.0.0", @@ -5751,8 +4359,6 @@ }, "node_modules/http-proxy-agent": { "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, "license": "MIT", "dependencies": { @@ -5765,8 +4371,6 @@ }, "node_modules/https-proxy-agent": { "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dev": true, "license": "MIT", "dependencies": { @@ -5779,8 +4383,6 @@ }, "node_modules/human-signals": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.0.tgz", - "integrity": "sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -5789,8 +4391,7 @@ }, "node_modules/humanize-ms": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "license": "MIT", "optional": true, "dependencies": { "ms": "^2.0.0" @@ -5798,8 +4399,6 @@ }, "node_modules/iconv-lite": { "version": "0.5.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz", - "integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" @@ -5810,8 +4409,6 @@ }, "node_modules/ieee754": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "funding": [ { "type": "github", @@ -5825,12 +4422,11 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "BSD-3-Clause" }, "node_modules/ignore": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "license": "MIT", "engines": { "node": ">= 4" @@ -5838,8 +4434,6 @@ }, "node_modules/import-fresh": { "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5855,8 +4449,6 @@ }, "node_modules/imurmurhash": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "devOptional": true, "license": "MIT", "engines": { @@ -5865,8 +4457,7 @@ }, "node_modules/indent-string": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "license": "MIT", "optional": true, "engines": { "node": ">=8" @@ -5874,15 +4465,12 @@ }, "node_modules/infer-owner": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "license": "ISC", "optional": true }, "node_modules/inflight": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "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.", + "license": "ISC", "optional": true, "dependencies": { "once": "^1.3.0", @@ -5891,20 +4479,14 @@ }, "node_modules/inherits": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, "node_modules/ini": { "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "license": "ISC" }, "node_modules/interpret": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", - "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", "license": "MIT", "engines": { "node": ">= 0.10" @@ -5912,8 +4494,7 @@ }, "node_modules/ip-address": { "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "license": "MIT", "optional": true, "dependencies": { "jsbn": "1.1.0", @@ -5925,8 +4506,6 @@ }, "node_modules/ipaddr.js": { "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "license": "MIT", "engines": { "node": ">= 0.10" @@ -5934,8 +4513,6 @@ }, "node_modules/is-core-module": { "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "license": "MIT", "dependencies": { "hasown": "^2.0.2" @@ -5949,8 +4526,6 @@ }, "node_modules/is-docker": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", "dev": true, "license": "MIT", "bin": { @@ -5965,8 +4540,6 @@ }, "node_modules/is-extglob": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -5974,8 +4547,6 @@ }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "devOptional": true, "license": "MIT", "engines": { @@ -5984,8 +4555,6 @@ }, "node_modules/is-glob": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -5996,8 +4565,6 @@ }, "node_modules/is-inside-container": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", "dev": true, "license": "MIT", "dependencies": { @@ -6015,14 +4582,11 @@ }, "node_modules/is-lambda": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "license": "MIT", "optional": true }, "node_modules/is-number": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "license": "MIT", "engines": { "node": ">=0.12.0" @@ -6030,8 +4594,6 @@ }, "node_modules/is-plain-obj": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", "dev": true, "license": "MIT", "engines": { @@ -6043,21 +4605,15 @@ }, "node_modules/is-potential-custom-element-name": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", "dev": true, "license": "MIT" }, "node_modules/is-promise": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, "node_modules/is-stream": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", - "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", "dev": true, "license": "MIT", "engines": { @@ -6069,8 +4625,6 @@ }, "node_modules/is-unicode-supported": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", - "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", "dev": true, "license": "MIT", "engines": { @@ -6082,8 +4636,6 @@ }, "node_modules/is-what": { "version": "4.1.16", - "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", - "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", "dev": true, "license": "MIT", "engines": { @@ -6095,8 +4647,6 @@ }, "node_modules/is-wsl": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", - "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", "dev": true, "license": "MIT", "dependencies": { @@ -6111,15 +4661,11 @@ }, "node_modules/isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "devOptional": true, "license": "ISC" }, "node_modules/jackspeak": { "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -6134,18 +4680,23 @@ }, "node_modules/jiti": { "version": "2.4.2", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", - "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", "dev": true, "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/jose": { + "version": "4.15.9", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-beautify": { "version": "1.15.3", - "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.3.tgz", - "integrity": "sha512-rKKGuyTxGNlyN4EQKWzNndzXpi0bOl8Gl8YQAW1as/oMz0XhD6sHJO1hTvoBDOSzKuJb9WkwoAb34FfdkKMv2A==", "dev": true, "license": "MIT", "dependencies": { @@ -6166,8 +4717,6 @@ }, "node_modules/js-cookie": { "version": "3.0.5", - "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", - "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", "dev": true, "license": "MIT", "engines": { @@ -6176,15 +4725,11 @@ }, "node_modules/js-tokens": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true, "license": "MIT" }, "node_modules/js-yaml": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -6195,14 +4740,11 @@ }, "node_modules/jsbn": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "license": "MIT", "optional": true }, "node_modules/jsdom": { "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.0.0.tgz", - "integrity": "sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw==", "dev": true, "license": "MIT", "dependencies": { @@ -6242,8 +4784,6 @@ }, "node_modules/jsdom/node_modules/xml-name-validator": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", - "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", "dev": true, "license": "Apache-2.0", "engines": { @@ -6252,8 +4792,6 @@ }, "node_modules/jsesc": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, "license": "MIT", "bin": { @@ -6265,15 +4803,11 @@ }, "node_modules/json-buffer": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true, "license": "MIT" }, "node_modules/json-parse-even-better-errors": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz", - "integrity": "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==", "dev": true, "license": "MIT", "engines": { @@ -6282,22 +4816,16 @@ }, "node_modules/json-schema-traverse": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true, "license": "MIT" }, "node_modules/json5": { "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, "license": "MIT", "bin": { @@ -6309,8 +4837,6 @@ }, "node_modules/jsonfile": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "license": "MIT", "dependencies": { "universalify": "^2.0.0" @@ -6319,10 +4845,92 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jwks-rsa": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.1.0.tgz", + "integrity": "sha512-v7nqlfezb9YfHHzYII3ef2a2j1XnGeSE/bK3WfumaYCqONAIstJbrEGapz4kadScZzEt7zYCN7bucj8C0Mv/Rg==", + "license": "MIT", + "dependencies": { + "@types/express": "^4.17.17", + "@types/jsonwebtoken": "^9.0.2", + "debug": "^4.3.4", + "jose": "^4.14.6", + "limiter": "^1.1.5", + "lru-memoizer": "^2.2.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/jwks-rsa/node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/jwks-rsa/node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/keyv": { "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "license": "MIT", "dependencies": { @@ -6331,8 +4939,6 @@ }, "node_modules/knex": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/knex/-/knex-3.1.0.tgz", - "integrity": "sha512-GLoII6hR0c4ti243gMs5/1Rb3B+AjwMOfjYm97pu0FOQa7JH56hgBxYf5WK2525ceSbBY1cjeZ9yk99GPMB6Kw==", "license": "MIT", "dependencies": { "colorette": "2.0.19", @@ -6382,8 +4988,6 @@ }, "node_modules/knex/node_modules/debug": { "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "license": "MIT", "dependencies": { "ms": "2.1.2" @@ -6399,14 +5003,10 @@ }, "node_modules/knex/node_modules/ms": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "license": "MIT" }, "node_modules/knex/node_modules/resolve-from": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "license": "MIT", "engines": { "node": ">=8" @@ -6414,15 +5014,11 @@ }, "node_modules/kolorist": { "version": "1.8.0", - "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", - "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==", "dev": true, "license": "MIT" }, "node_modules/levn": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6433,10 +5029,13 @@ "node": ">= 0.8.0" } }, + "node_modules/limiter": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" + }, "node_modules/locate-path": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "license": "MIT", "dependencies": { @@ -6451,35 +5050,101 @@ }, "node_modules/lodash": { "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", "license": "MIT" }, "node_modules/lodash.merge": { "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true, "license": "MIT" }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, "node_modules/loupe": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", - "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", "dev": true, "license": "MIT" }, "node_modules/lru-cache": { "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, "license": "ISC" }, + "node_modules/lru-memoizer": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.3.0.tgz", + "integrity": "sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==", + "license": "MIT", + "dependencies": { + "lodash.clonedeep": "^4.5.0", + "lru-cache": "6.0.0" + } + }, + "node_modules/lru-memoizer/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lru-memoizer/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, "node_modules/magic-string": { "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" @@ -6487,15 +5152,12 @@ }, "node_modules/make-error": { "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true, "license": "ISC" }, "node_modules/make-fetch-happen": { "version": "9.1.0", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", - "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "license": "ISC", "optional": true, "dependencies": { "agentkeepalive": "^4.1.3", @@ -6521,8 +5183,7 @@ }, "node_modules/make-fetch-happen/node_modules/agent-base": { "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", "optional": true, "dependencies": { "debug": "4" @@ -6533,8 +5194,7 @@ }, "node_modules/make-fetch-happen/node_modules/http-proxy-agent": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "license": "MIT", "optional": true, "dependencies": { "@tootallnate/once": "1", @@ -6547,8 +5207,7 @@ }, "node_modules/make-fetch-happen/node_modules/https-proxy-agent": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", "optional": true, "dependencies": { "agent-base": "6", @@ -6560,8 +5219,7 @@ }, "node_modules/make-fetch-happen/node_modules/lru-cache": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", "optional": true, "dependencies": { "yallist": "^4.0.0" @@ -6572,8 +5230,7 @@ }, "node_modules/make-fetch-happen/node_modules/minipass": { "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", "optional": true, "dependencies": { "yallist": "^4.0.0" @@ -6584,8 +5241,7 @@ }, "node_modules/make-fetch-happen/node_modules/negotiator": { "version": "0.6.4", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", - "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "license": "MIT", "optional": true, "engines": { "node": ">= 0.6" @@ -6593,14 +5249,11 @@ }, "node_modules/make-fetch-happen/node_modules/yallist": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", "optional": true }, "node_modules/math-intrinsics": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -6608,8 +5261,6 @@ }, "node_modules/media-typer": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -6617,8 +5268,6 @@ }, "node_modules/memorystream": { "version": "0.3.1", - "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", "dev": true, "engines": { "node": ">= 0.10.0" @@ -6626,8 +5275,6 @@ }, "node_modules/merge-descriptors": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", - "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "license": "MIT", "engines": { "node": ">=18" @@ -6638,8 +5285,6 @@ }, "node_modules/merge2": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "license": "MIT", "engines": { "node": ">= 8" @@ -6647,8 +5292,6 @@ }, "node_modules/methods": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -6656,8 +5299,6 @@ }, "node_modules/micromatch": { "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -6669,8 +5310,6 @@ }, "node_modules/mikro-orm": { "version": "6.4.6", - "resolved": "https://registry.npmjs.org/mikro-orm/-/mikro-orm-6.4.6.tgz", - "integrity": "sha512-Lr3uFK06O/4F/AtQAsuYD6QH7DgmUooSVFVGf1y02IuiKVFKOMJ4iKimkRMyoA+ykKhgYIp8WiaEqbWJVuz4Vw==", "license": "MIT", "engines": { "node": ">= 18.12.0" @@ -6678,8 +5317,6 @@ }, "node_modules/mime-db": { "version": "1.53.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz", - "integrity": "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -6687,8 +5324,6 @@ }, "node_modules/mime-types": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.0.tgz", - "integrity": "sha512-XqoSHeCGjVClAmoGFG3lVFqQFRIrTVw2OH3axRqAcfaw+gHWIfnASS92AV+Rl/mk0MupgZTRHQOjxY6YVnzK5w==", "license": "MIT", "dependencies": { "mime-db": "^1.53.0" @@ -6699,8 +5334,7 @@ }, "node_modules/mimic-response": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -6710,8 +5344,6 @@ }, "node_modules/minimatch": { "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -6725,8 +5357,6 @@ }, "node_modules/minimist": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6734,8 +5364,6 @@ }, "node_modules/minipass": { "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, "license": "ISC", "engines": { @@ -6744,8 +5372,7 @@ }, "node_modules/minipass-collect": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "license": "ISC", "optional": true, "dependencies": { "minipass": "^3.0.0" @@ -6756,8 +5383,7 @@ }, "node_modules/minipass-collect/node_modules/minipass": { "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", "optional": true, "dependencies": { "yallist": "^4.0.0" @@ -6768,14 +5394,12 @@ }, "node_modules/minipass-collect/node_modules/yallist": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", "optional": true }, "node_modules/minipass-fetch": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", - "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "license": "MIT", "optional": true, "dependencies": { "minipass": "^3.1.0", @@ -6791,8 +5415,7 @@ }, "node_modules/minipass-fetch/node_modules/minipass": { "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", "optional": true, "dependencies": { "yallist": "^4.0.0" @@ -6803,14 +5426,12 @@ }, "node_modules/minipass-fetch/node_modules/yallist": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", "optional": true }, "node_modules/minipass-flush": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "license": "ISC", "optional": true, "dependencies": { "minipass": "^3.0.0" @@ -6821,8 +5442,7 @@ }, "node_modules/minipass-flush/node_modules/minipass": { "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", "optional": true, "dependencies": { "yallist": "^4.0.0" @@ -6833,14 +5453,12 @@ }, "node_modules/minipass-flush/node_modules/yallist": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", "optional": true }, "node_modules/minipass-pipeline": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "license": "ISC", "optional": true, "dependencies": { "minipass": "^3.0.0" @@ -6851,8 +5469,7 @@ }, "node_modules/minipass-pipeline/node_modules/minipass": { "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", "optional": true, "dependencies": { "yallist": "^4.0.0" @@ -6863,14 +5480,12 @@ }, "node_modules/minipass-pipeline/node_modules/yallist": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", "optional": true }, "node_modules/minipass-sized": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", - "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "license": "ISC", "optional": true, "dependencies": { "minipass": "^3.0.0" @@ -6881,8 +5496,7 @@ }, "node_modules/minipass-sized/node_modules/minipass": { "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", "optional": true, "dependencies": { "yallist": "^4.0.0" @@ -6893,14 +5507,12 @@ }, "node_modules/minipass-sized/node_modules/yallist": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", "optional": true }, "node_modules/minizlib": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "license": "MIT", "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" @@ -6911,8 +5523,7 @@ }, "node_modules/minizlib/node_modules/minipass": { "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -6922,20 +5533,16 @@ }, "node_modules/minizlib/node_modules/yallist": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "license": "ISC" }, "node_modules/mitt": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", - "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", "dev": true, "license": "MIT" }, "node_modules/mkdirp": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", "bin": { "mkdirp": "bin/cmd.js" }, @@ -6945,13 +5552,10 @@ }, "node_modules/mkdirp-classic": { "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + "license": "MIT" }, "node_modules/mrmime": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", - "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", "dev": true, "license": "MIT", "engines": { @@ -6960,21 +5564,15 @@ }, "node_modules/ms": { "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, "node_modules/muggle-string": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", - "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", "dev": true, "license": "MIT" }, "node_modules/nanoid": { "version": "3.3.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "funding": [ { "type": "github", @@ -6991,20 +5589,15 @@ }, "node_modules/napi-build-utils": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", - "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==" + "license": "MIT" }, "node_modules/natural-compare": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true, "license": "MIT" }, "node_modules/negotiator": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -7012,8 +5605,7 @@ }, "node_modules/node-abi": { "version": "3.74.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.74.0.tgz", - "integrity": "sha512-c5XK0MjkGBrQPGYG24GBADZud0NCbznxNx0ZkS+ebUTrmV1qTDxPxSL8zEAPURXSbLRWVexxmP4986BziahL5w==", + "license": "MIT", "dependencies": { "semver": "^7.3.5" }, @@ -7023,13 +5615,11 @@ }, "node_modules/node-addon-api": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", - "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==" + "license": "MIT" }, "node_modules/node-gyp": { "version": "8.4.1", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", - "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "license": "MIT", "optional": true, "dependencies": { "env-paths": "^2.2.0", @@ -7052,14 +5642,12 @@ }, "node_modules/node-gyp/node_modules/abbrev": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "license": "ISC", "optional": true }, "node_modules/node-gyp/node_modules/brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", "optional": true, "dependencies": { "balanced-match": "^1.0.0", @@ -7068,9 +5656,7 @@ }, "node_modules/node-gyp/node_modules/glob": { "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", "optional": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -7089,8 +5675,7 @@ }, "node_modules/node-gyp/node_modules/minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", "optional": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -7101,8 +5686,7 @@ }, "node_modules/node-gyp/node_modules/nopt": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "license": "ISC", "optional": true, "dependencies": { "abbrev": "1" @@ -7116,15 +5700,11 @@ }, "node_modules/node-releases": { "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", "dev": true, "license": "MIT" }, "node_modules/nopt": { "version": "8.1.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz", - "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==", "dev": true, "license": "ISC", "dependencies": { @@ -7139,8 +5719,6 @@ }, "node_modules/npm-normalize-package-bin": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz", - "integrity": "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==", "dev": true, "license": "ISC", "engines": { @@ -7149,8 +5727,6 @@ }, "node_modules/npm-run-all2": { "version": "7.0.2", - "resolved": "https://registry.npmjs.org/npm-run-all2/-/npm-run-all2-7.0.2.tgz", - "integrity": "sha512-7tXR+r9hzRNOPNTvXegM+QzCuMjzUIIq66VDunL6j60O4RrExx32XUhlrS7UK4VcdGw5/Wxzb3kfNcFix9JKDA==", "dev": true, "license": "MIT", "dependencies": { @@ -7176,8 +5752,6 @@ }, "node_modules/npm-run-all2/node_modules/ansi-styles": { "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, "license": "MIT", "engines": { @@ -7189,8 +5763,6 @@ }, "node_modules/npm-run-all2/node_modules/isexe": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", - "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", "dev": true, "license": "ISC", "engines": { @@ -7199,8 +5771,6 @@ }, "node_modules/npm-run-all2/node_modules/which": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", - "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", "dev": true, "license": "ISC", "dependencies": { @@ -7215,8 +5785,6 @@ }, "node_modules/npm-run-path": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", - "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", "dev": true, "license": "MIT", "dependencies": { @@ -7232,8 +5800,6 @@ }, "node_modules/npm-run-path/node_modules/path-key": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true, "license": "MIT", "engines": { @@ -7245,9 +5811,7 @@ }, "node_modules/npmlog": { "version": "6.0.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", - "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", - "deprecated": "This package is no longer supported.", + "license": "ISC", "optional": true, "dependencies": { "are-we-there-yet": "^3.0.0", @@ -7261,8 +5825,6 @@ }, "node_modules/nth-check": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -7274,15 +5836,11 @@ }, "node_modules/nwsapi": { "version": "2.2.16", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.16.tgz", - "integrity": "sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==", "dev": true, "license": "MIT" }, "node_modules/object-inspect": { "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -7293,8 +5851,6 @@ }, "node_modules/on-finished": { "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "license": "MIT", "dependencies": { "ee-first": "1.1.1" @@ -7305,8 +5861,6 @@ }, "node_modules/once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "license": "ISC", "dependencies": { "wrappy": "1" @@ -7314,8 +5868,6 @@ }, "node_modules/open": { "version": "10.1.0", - "resolved": "https://registry.npmjs.org/open/-/open-10.1.0.tgz", - "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==", "dev": true, "license": "MIT", "dependencies": { @@ -7333,8 +5885,6 @@ }, "node_modules/optionator": { "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "license": "MIT", "dependencies": { @@ -7351,8 +5901,6 @@ }, "node_modules/p-limit": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7367,8 +5915,6 @@ }, "node_modules/p-locate": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "license": "MIT", "dependencies": { @@ -7383,8 +5929,7 @@ }, "node_modules/p-map": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "license": "MIT", "optional": true, "dependencies": { "aggregate-error": "^3.0.0" @@ -7398,15 +5943,11 @@ }, "node_modules/package-json-from-dist": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true, "license": "BlueOak-1.0.0" }, "node_modules/parent-module": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "license": "MIT", "dependencies": { @@ -7418,8 +5959,6 @@ }, "node_modules/parent-require": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parent-require/-/parent-require-1.0.0.tgz", - "integrity": "sha512-2MXDNZC4aXdkkap+rBBMv0lUsfJqvX5/2FiYYnfCnorZt3Pk06/IOR5KeaoghgS2w07MLWgjbsnyaq6PdHn2LQ==", "dev": true, "engines": { "node": ">= 0.4.0" @@ -7427,8 +5966,6 @@ }, "node_modules/parse-ms": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", - "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", "dev": true, "license": "MIT", "engines": { @@ -7440,8 +5977,6 @@ }, "node_modules/parse5": { "version": "7.2.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", - "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7453,8 +5988,6 @@ }, "node_modules/parseurl": { "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -7462,14 +5995,10 @@ }, "node_modules/path-browserify": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", "license": "MIT" }, "node_modules/path-exists": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "license": "MIT", "engines": { @@ -7478,8 +6007,7 @@ }, "node_modules/path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", "optional": true, "engines": { "node": ">=0.10.0" @@ -7487,8 +6015,6 @@ }, "node_modules/path-key": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "license": "MIT", "engines": { @@ -7497,14 +6023,10 @@ }, "node_modules/path-parse": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "license": "MIT" }, "node_modules/path-scurry": { "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -7520,8 +6042,6 @@ }, "node_modules/path-to-regexp": { "version": "8.2.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", - "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", "license": "MIT", "engines": { "node": ">=16" @@ -7529,8 +6049,6 @@ }, "node_modules/path-type": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "license": "MIT", "engines": { "node": ">=8" @@ -7538,15 +6056,11 @@ }, "node_modules/pathe": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true, "license": "MIT" }, "node_modules/pathval": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", - "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", "dev": true, "license": "MIT", "engines": { @@ -7555,15 +6069,11 @@ }, "node_modules/perfect-debounce": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", - "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", "dev": true, "license": "MIT" }, "node_modules/pg": { "version": "8.13.2", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.13.2.tgz", - "integrity": "sha512-L5QkPvTjVWWHbLaFjCkOSplpb2uCiRYbg0IJ2okCy5ClYfWlSgDDnvdR6dyw3EWAH2AfS4j8E61QFI7gLfTtlw==", "license": "MIT", "dependencies": { "pg-connection-string": "^2.7.0", @@ -7589,21 +6099,15 @@ }, "node_modules/pg-cloudflare": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", - "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", "license": "MIT", "optional": true }, "node_modules/pg-connection-string": { "version": "2.6.2", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", - "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==", "license": "MIT" }, "node_modules/pg-int8": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", - "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", "license": "ISC", "engines": { "node": ">=4.0.0" @@ -7611,8 +6115,6 @@ }, "node_modules/pg-pool": { "version": "3.7.1", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.7.1.tgz", - "integrity": "sha512-xIOsFoh7Vdhojas6q3596mXFsR8nwBQBXX5JiV7p9buEVAGqYL4yFzclON5P9vFrpu1u7Zwl2oriyDa89n0wbw==", "license": "MIT", "peerDependencies": { "pg": ">=8.0" @@ -7620,14 +6122,10 @@ }, "node_modules/pg-protocol": { "version": "1.7.1", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.7.1.tgz", - "integrity": "sha512-gjTHWGYWsEgy9MsY0Gp6ZJxV24IjDqdpTW7Eh0x+WfJLFsm/TJx1MzL6T0D88mBvkpxotCQ6TwW6N+Kko7lhgQ==", "license": "MIT" }, "node_modules/pg-types": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", - "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", "license": "MIT", "dependencies": { "pg-int8": "1.0.1", @@ -7642,8 +6140,6 @@ }, "node_modules/pg-types/node_modules/postgres-array": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", - "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", "license": "MIT", "engines": { "node": ">=4" @@ -7651,8 +6147,6 @@ }, "node_modules/pg-types/node_modules/postgres-date": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", - "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -7660,8 +6154,6 @@ }, "node_modules/pg-types/node_modules/postgres-interval": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", - "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", "license": "MIT", "dependencies": { "xtend": "^4.0.0" @@ -7672,14 +6164,10 @@ }, "node_modules/pg/node_modules/pg-connection-string": { "version": "2.7.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.7.0.tgz", - "integrity": "sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==", "license": "MIT" }, "node_modules/pgpass": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", - "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", "license": "MIT", "dependencies": { "split2": "^4.1.0" @@ -7687,14 +6175,10 @@ }, "node_modules/picocolors": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "license": "MIT", "engines": { "node": ">=8.6" @@ -7705,8 +6189,6 @@ }, "node_modules/pidtree": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", - "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", "dev": true, "license": "MIT", "bin": { @@ -7718,8 +6200,6 @@ }, "node_modules/playwright": { "version": "1.50.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.50.1.tgz", - "integrity": "sha512-G8rwsOQJ63XG6BbKj2w5rHeavFjy5zynBA9zsJMMtBoe/Uf757oG12NXz6e6OirF7RCrTVAKFXbLmn1RbL7Qaw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -7737,8 +6217,6 @@ }, "node_modules/playwright-core": { "version": "1.50.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.50.1.tgz", - "integrity": "sha512-ra9fsNWayuYumt+NiM069M6OkcRb1FZSK8bgi66AtpFoWkg2+y0bJSNmkFrWhMbEBbVKC/EruAHH3g0zmtwGmQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -7748,25 +6226,8 @@ "node": ">=18" } }, - "node_modules/playwright/node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/postcss": { "version": "8.5.3", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", "funding": [ { "type": "opencollective", @@ -7793,8 +6254,6 @@ }, "node_modules/postcss-selector-parser": { "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dev": true, "license": "MIT", "dependencies": { @@ -7807,8 +6266,6 @@ }, "node_modules/postgres-array": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.2.tgz", - "integrity": "sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==", "license": "MIT", "engines": { "node": ">=12" @@ -7816,8 +6273,6 @@ }, "node_modules/postgres-bytea": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", - "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -7825,8 +6280,6 @@ }, "node_modules/postgres-date": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.1.0.tgz", - "integrity": "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==", "license": "MIT", "engines": { "node": ">=12" @@ -7834,8 +6287,6 @@ }, "node_modules/postgres-interval": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-4.0.2.tgz", - "integrity": "sha512-EMsphSQ1YkQqKZL2cuG0zHkmjCCzQqQ71l2GXITqRwjhRleCdv00bDk/ktaSi0LnlaPzAc3535KTrjXsTdtx7A==", "license": "MIT", "engines": { "node": ">=12" @@ -7843,8 +6294,7 @@ }, "node_modules/prebuild-install": { "version": "7.1.3", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", - "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "license": "MIT", "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", @@ -7868,8 +6318,6 @@ }, "node_modules/prelude-ls": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "license": "MIT", "engines": { @@ -7878,8 +6326,6 @@ }, "node_modules/prettier": { "version": "3.5.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.1.tgz", - "integrity": "sha512-hPpFQvHwL3Qv5AdRvBFMhnKo4tYxp0ReXiPn2bxkiohEX6mBeBwEpBSQTkD458RaaDKQMYSp4hX4UtfUTA5wDw==", "dev": true, "license": "MIT", "peer": true, @@ -7895,8 +6341,6 @@ }, "node_modules/prettier-linter-helpers": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", "dev": true, "license": "MIT", "dependencies": { @@ -7908,8 +6352,6 @@ }, "node_modules/pretty-ms": { "version": "9.2.0", - "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.2.0.tgz", - "integrity": "sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==", "dev": true, "license": "MIT", "dependencies": { @@ -7924,14 +6366,12 @@ }, "node_modules/promise-inflight": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "license": "ISC", "optional": true }, "node_modules/promise-retry": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", - "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "license": "MIT", "optional": true, "dependencies": { "err-code": "^2.0.2", @@ -7943,15 +6383,11 @@ }, "node_modules/proto-list": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", "dev": true, "license": "ISC" }, "node_modules/proxy-addr": { "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "license": "MIT", "dependencies": { "forwarded": "0.2.0", @@ -7963,8 +6399,7 @@ }, "node_modules/pump": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", - "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "license": "MIT", "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -7972,8 +6407,6 @@ }, "node_modules/punycode": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "license": "MIT", "engines": { @@ -7982,8 +6415,6 @@ }, "node_modules/qs": { "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.0.6" @@ -7997,8 +6428,6 @@ }, "node_modules/queue-microtask": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "funding": [ { "type": "github", @@ -8017,8 +6446,6 @@ }, "node_modules/range-parser": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -8026,8 +6453,6 @@ }, "node_modules/raw-body": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", - "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", "license": "MIT", "dependencies": { "bytes": "3.1.2", @@ -8041,8 +6466,6 @@ }, "node_modules/raw-body/node_modules/iconv-lite": { "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -8053,8 +6476,7 @@ }, "node_modules/rc": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -8067,16 +6489,13 @@ }, "node_modules/rc/node_modules/strip-json-comments": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/read-package-json-fast": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-4.0.0.tgz", - "integrity": "sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==", "dev": true, "license": "ISC", "dependencies": { @@ -8089,8 +6508,7 @@ }, "node_modules/readable-stream": { "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -8102,8 +6520,6 @@ }, "node_modules/rechoir": { "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", - "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", "license": "MIT", "dependencies": { "resolve": "^1.20.0" @@ -8114,14 +6530,10 @@ }, "node_modules/reflect-metadata": { "version": "0.2.2", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", - "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", "license": "Apache-2.0" }, "node_modules/require-directory": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, "license": "MIT", "engines": { @@ -8130,8 +6542,6 @@ }, "node_modules/resolve": { "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "license": "MIT", "dependencies": { "is-core-module": "^2.16.0", @@ -8150,8 +6560,6 @@ }, "node_modules/resolve-from": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, "license": "MIT", "engines": { @@ -8160,8 +6568,6 @@ }, "node_modules/resolve-pkg-maps": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true, "license": "MIT", "funding": { @@ -8170,8 +6576,7 @@ }, "node_modules/retry": { "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "license": "MIT", "optional": true, "engines": { "node": ">= 4" @@ -8179,8 +6584,6 @@ }, "node_modules/reusify": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "license": "MIT", "engines": { "iojs": ">=1.0.0", @@ -8189,16 +6592,12 @@ }, "node_modules/rfdc": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "dev": true, "license": "MIT" }, "node_modules/rimraf": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", "optional": true, "dependencies": { "glob": "^7.1.3" @@ -8212,8 +6611,7 @@ }, "node_modules/rimraf/node_modules/brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", "optional": true, "dependencies": { "balanced-match": "^1.0.0", @@ -8222,9 +6620,7 @@ }, "node_modules/rimraf/node_modules/glob": { "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", "optional": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -8243,8 +6639,7 @@ }, "node_modules/rimraf/node_modules/minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", "optional": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -8255,8 +6650,6 @@ }, "node_modules/rollup": { "version": "4.34.8", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.8.tgz", - "integrity": "sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8294,8 +6687,6 @@ }, "node_modules/router": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/router/-/router-2.1.0.tgz", - "integrity": "sha512-/m/NSLxeYEgWNtyC+WtNHCF7jbGxOibVWKnn+1Psff4dJGOfoXP+MuC/f2CwSmyiHdOIzYnYFp4W6GxWfekaLA==", "license": "MIT", "dependencies": { "is-promise": "^4.0.0", @@ -8308,15 +6699,11 @@ }, "node_modules/rrweb-cssom": { "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", - "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", "dev": true, "license": "MIT" }, "node_modules/run-applescript": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", - "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", "dev": true, "license": "MIT", "engines": { @@ -8328,8 +6715,6 @@ }, "node_modules/run-parallel": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "funding": [ { "type": "github", @@ -8351,8 +6736,6 @@ }, "node_modules/safe-buffer": { "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "funding": [ { "type": "github", @@ -8371,14 +6754,10 @@ }, "node_modules/safer-buffer": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, "node_modules/saxes": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", "dev": true, "license": "ISC", "dependencies": { @@ -8390,8 +6769,6 @@ }, "node_modules/semver": { "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -8402,8 +6779,6 @@ }, "node_modules/send": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/send/-/send-1.1.0.tgz", - "integrity": "sha512-v67WcEouB5GxbTWL/4NeToqcZiAWEq90N888fczVArY8A79J0L4FD7vj5hm3eUMua5EpoQ59wa/oovY6TLvRUA==", "license": "MIT", "dependencies": { "debug": "^4.3.5", @@ -8425,8 +6800,6 @@ }, "node_modules/send/node_modules/fresh": { "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -8434,8 +6807,6 @@ }, "node_modules/send/node_modules/mime-db": { "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -8443,8 +6814,6 @@ }, "node_modules/send/node_modules/mime-types": { "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "license": "MIT", "dependencies": { "mime-db": "1.52.0" @@ -8455,8 +6824,6 @@ }, "node_modules/serve-static": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.1.0.tgz", - "integrity": "sha512-A3We5UfEjG8Z7VkDv6uItWw6HY2bBSBJT1KtVESn6EOoOr2jAxNhxWCLY3jDE2WcuHXByWju74ck3ZgLwL8xmA==", "license": "MIT", "dependencies": { "encodeurl": "^2.0.0", @@ -8470,20 +6837,15 @@ }, "node_modules/set-blocking": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC", "optional": true }, "node_modules/setprototypeof": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, "node_modules/shebang-command": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "license": "MIT", "dependencies": { @@ -8495,8 +6857,6 @@ }, "node_modules/shebang-regex": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "license": "MIT", "engines": { @@ -8505,8 +6865,6 @@ }, "node_modules/shell-quote": { "version": "1.8.2", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", - "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", "dev": true, "license": "MIT", "engines": { @@ -8518,8 +6876,6 @@ }, "node_modules/side-channel": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -8537,8 +6893,6 @@ }, "node_modules/side-channel-list": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -8553,8 +6907,6 @@ }, "node_modules/side-channel-map": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -8571,8 +6923,6 @@ }, "node_modules/side-channel-weakmap": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -8590,15 +6940,11 @@ }, "node_modules/siginfo": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", "dev": true, "license": "ISC" }, "node_modules/signal-exit": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, "license": "ISC", "engines": { @@ -8610,27 +6956,6 @@ }, "node_modules/simple-concat": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", "funding": [ { "type": "github", @@ -8645,6 +6970,25 @@ "url": "https://feross.org/support" } ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", @@ -8653,8 +6997,6 @@ }, "node_modules/sirv": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.1.tgz", - "integrity": "sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==", "dev": true, "license": "MIT", "dependencies": { @@ -8668,8 +7010,6 @@ }, "node_modules/slash": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "license": "MIT", "engines": { "node": ">=8" @@ -8677,8 +7017,7 @@ }, "node_modules/smart-buffer": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", "optional": true, "engines": { "node": ">= 6.0.0", @@ -8687,8 +7026,7 @@ }, "node_modules/socks": { "version": "2.8.4", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz", - "integrity": "sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==", + "license": "MIT", "optional": true, "dependencies": { "ip-address": "^9.0.5", @@ -8701,8 +7039,7 @@ }, "node_modules/socks-proxy-agent": { "version": "6.2.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", - "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "license": "MIT", "optional": true, "dependencies": { "agent-base": "^6.0.2", @@ -8715,8 +7052,7 @@ }, "node_modules/socks-proxy-agent/node_modules/agent-base": { "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", "optional": true, "dependencies": { "debug": "4" @@ -8727,8 +7063,6 @@ }, "node_modules/source-map-js": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -8736,8 +7070,6 @@ }, "node_modules/speakingurl": { "version": "14.0.1", - "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", - "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -8746,8 +7078,6 @@ }, "node_modules/split2": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", "license": "ISC", "engines": { "node": ">= 10.x" @@ -8755,15 +7085,13 @@ }, "node_modules/sprintf-js": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "license": "BSD-3-Clause", "optional": true }, "node_modules/sqlite3": { "version": "5.1.7", - "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz", - "integrity": "sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==", "hasInstallScript": true, + "license": "BSD-3-Clause", "dependencies": { "bindings": "^1.5.0", "node-addon-api": "^7.0.0", @@ -8784,8 +7112,6 @@ }, "node_modules/sqlstring": { "version": "2.3.3", - "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", - "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -8793,16 +7119,14 @@ }, "node_modules/sqlstring-sqlite": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/sqlstring-sqlite/-/sqlstring-sqlite-0.1.1.tgz", - "integrity": "sha512-9CAYUJ0lEUPYJrswqiqdINNSfq3jqWo/bFJ7tufdoNeSK0Fy+d1kFTxjqO9PIqza0Kri+ZtYMfPVf1aZaFOvrQ==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/ssri": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", - "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "license": "ISC", "optional": true, "dependencies": { "minipass": "^3.1.1" @@ -8813,8 +7137,7 @@ }, "node_modules/ssri/node_modules/minipass": { "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", "optional": true, "dependencies": { "yallist": "^4.0.0" @@ -8825,21 +7148,16 @@ }, "node_modules/ssri/node_modules/yallist": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", "optional": true }, "node_modules/stackback": { "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", "dev": true, "license": "MIT" }, "node_modules/statuses": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -8847,23 +7165,35 @@ }, "node_modules/std-env": { "version": "3.8.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz", - "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==", "dev": true, "license": "MIT" }, "node_modules/string_decoder": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", "dependencies": { "safe-buffer": "~5.2.0" } }, "node_modules/string-width": { + "version": "5.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", "dependencies": { @@ -8875,26 +7205,47 @@ "node": ">=8" } }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", "dev": true, "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", "dependencies": { @@ -8904,24 +7255,16 @@ "node": ">=8" } }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", "dev": true, "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, "engines": { "node": ">=8" } }, "node_modules/strip-bom": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, "license": "MIT", "engines": { @@ -8930,8 +7273,6 @@ }, "node_modules/strip-final-newline": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", - "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", "dev": true, "license": "MIT", "engines": { @@ -8943,8 +7284,6 @@ }, "node_modules/strip-json-comments": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "license": "MIT", "engines": { @@ -8956,8 +7295,6 @@ }, "node_modules/superjson": { "version": "2.2.2", - "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.2.tgz", - "integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==", "dev": true, "license": "MIT", "dependencies": { @@ -8969,8 +7306,6 @@ }, "node_modules/supports-color": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { @@ -8982,8 +7317,6 @@ }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -8994,21 +7327,15 @@ }, "node_modules/svg-tags": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", - "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", "dev": true }, "node_modules/symbol-tree": { "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true, "license": "MIT" }, "node_modules/synckit": { "version": "0.9.2", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", - "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", "dev": true, "license": "MIT", "dependencies": { @@ -9024,8 +7351,7 @@ }, "node_modules/tar": { "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "license": "ISC", "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -9040,8 +7366,7 @@ }, "node_modules/tar-fs": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.2.tgz", - "integrity": "sha512-EsaAXwxmx8UB7FRKqeozqEPop69DXcmYwTQwXvyAPF352HJsPdkVhvTaDPYqfNgruveJIJy3TA2l+2zj8LJIJA==", + "license": "MIT", "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", @@ -9051,13 +7376,11 @@ }, "node_modules/tar-fs/node_modules/chownr": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + "license": "ISC" }, "node_modules/tar-stream": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", @@ -9071,21 +7394,17 @@ }, "node_modules/tar/node_modules/minipass": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "license": "ISC", "engines": { "node": ">=8" } }, "node_modules/tar/node_modules/yallist": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "license": "ISC" }, "node_modules/tarn": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.2.tgz", - "integrity": "sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==", "license": "MIT", "engines": { "node": ">=8.0.0" @@ -9093,8 +7412,6 @@ }, "node_modules/tildify": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tildify/-/tildify-2.0.0.tgz", - "integrity": "sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==", "license": "MIT", "engines": { "node": ">=8" @@ -9102,22 +7419,16 @@ }, "node_modules/tinybench": { "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", "dev": true, "license": "MIT" }, "node_modules/tinyexec": { "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", "dev": true, "license": "MIT" }, "node_modules/tinypool": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", - "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", "dev": true, "license": "MIT", "engines": { @@ -9126,8 +7437,6 @@ }, "node_modules/tinyrainbow": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", - "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", "dev": true, "license": "MIT", "engines": { @@ -9136,8 +7445,6 @@ }, "node_modules/tinyspy": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", - "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", "dev": true, "license": "MIT", "engines": { @@ -9145,29 +7452,23 @@ } }, "node_modules/tldts": { - "version": "6.1.78", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.78.tgz", - "integrity": "sha512-fSgYrW0ITH0SR/CqKMXIruYIPpNu5aDgUp22UhYoSrnUQwc7SBqifEBFNce7AAcygUPBo6a/gbtcguWdmko4RQ==", + "version": "6.1.77", "dev": true, "license": "MIT", "dependencies": { - "tldts-core": "^6.1.78" + "tldts-core": "^6.1.77" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "6.1.78", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.78.tgz", - "integrity": "sha512-jS0svNsB99jR6AJBmfmEWuKIgz91Haya91Z43PATaeHJ24BkMoNRb/jlaD37VYjb0mYf6gRL/HOnvS1zEnYBiw==", + "version": "6.1.77", "dev": true, "license": "MIT" }, "node_modules/to-regex-range": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -9178,8 +7479,6 @@ }, "node_modules/toidentifier": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "license": "MIT", "engines": { "node": ">=0.6" @@ -9187,8 +7486,6 @@ }, "node_modules/totalist": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", - "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", "dev": true, "license": "MIT", "engines": { @@ -9197,8 +7494,6 @@ }, "node_modules/tough-cookie": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.1.tgz", - "integrity": "sha512-Ek7HndSVkp10hmHP9V4qZO1u+pn1RU5sI0Fw+jCU3lyvuMZcgqsNgc6CmJJZyByK4Vm/qotGRJlfgAX8q+4JiA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -9210,8 +7505,6 @@ }, "node_modules/tr46": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", - "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", "dev": true, "license": "MIT", "dependencies": { @@ -9223,8 +7516,6 @@ }, "node_modules/ts-api-utils": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz", - "integrity": "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==", "dev": true, "license": "MIT", "engines": { @@ -9236,8 +7527,6 @@ }, "node_modules/ts-morph": { "version": "25.0.1", - "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-25.0.1.tgz", - "integrity": "sha512-QJEiTdnz1YjrB3JFhd626gX4rKHDLSjSVMvGGG4v7ONc3RBwa0Eei98G9AT9uNFDMtV54JyuXsFeC+OH0n6bXQ==", "license": "MIT", "dependencies": { "@ts-morph/common": "~0.26.0", @@ -9246,8 +7535,6 @@ }, "node_modules/ts-node": { "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9290,8 +7577,6 @@ }, "node_modules/tsconfig-paths": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", - "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", "dev": true, "license": "MIT", "dependencies": { @@ -9305,15 +7590,11 @@ }, "node_modules/tslib": { "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true, "license": "0BSD" }, "node_modules/tsx": { "version": "4.19.3", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.3.tgz", - "integrity": "sha512-4H8vUNGNjQ4V2EOoGw005+c+dGuPSnhpPBPHBtsZdGZBk/iJb4kguGlPWaZTZ3q5nMtFOEsY0nRDlh9PJyd6SQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9330,26 +7611,9 @@ "fsevents": "~2.3.3" } }, - "node_modules/tsx/node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/tunnel-agent": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", "dependencies": { "safe-buffer": "^5.0.1" }, @@ -9359,8 +7623,6 @@ }, "node_modules/type-check": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "license": "MIT", "dependencies": { @@ -9372,8 +7634,6 @@ }, "node_modules/type-fest": { "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { @@ -9385,8 +7645,6 @@ }, "node_modules/type-is": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.0.tgz", - "integrity": "sha512-gd0sGezQYCbWSbkZr75mln4YBidWUN60+devscpLF5mtRDUpiaTvKpBNrdaCvel1NdR2k6vclXybU5fBd2i+nw==", "license": "MIT", "dependencies": { "content-type": "^1.0.5", @@ -9399,8 +7657,6 @@ }, "node_modules/typescript": { "version": "5.7.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", - "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "devOptional": true, "license": "Apache-2.0", "bin": { @@ -9413,8 +7669,6 @@ }, "node_modules/typescript-eslint": { "version": "8.24.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.24.1.tgz", - "integrity": "sha512-cw3rEdzDqBs70TIcb0Gdzbt6h11BSs2pS0yaq7hDWDBtCCSei1pPSUXE9qUdQ/Wm9NgFg8mKtMt1b8fTHIl1jA==", "dev": true, "license": "MIT", "dependencies": { @@ -9436,15 +7690,10 @@ }, "node_modules/undici-types": { "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, "license": "MIT" }, "node_modules/unicorn-magic": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", - "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", "dev": true, "license": "MIT", "engines": { @@ -9456,8 +7705,7 @@ }, "node_modules/unique-filename": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "license": "ISC", "optional": true, "dependencies": { "unique-slug": "^2.0.0" @@ -9465,8 +7713,7 @@ }, "node_modules/unique-slug": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "license": "ISC", "optional": true, "dependencies": { "imurmurhash": "^0.1.4" @@ -9474,8 +7721,6 @@ }, "node_modules/universalify": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "license": "MIT", "engines": { "node": ">= 10.0.0" @@ -9483,8 +7728,6 @@ }, "node_modules/unpipe": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -9492,8 +7735,6 @@ }, "node_modules/update-browserslist-db": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", - "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", "dev": true, "funding": [ { @@ -9523,8 +7764,6 @@ }, "node_modules/uri-js": { "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -9533,14 +7772,10 @@ }, "node_modules/util-deprecate": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, "node_modules/utils-merge": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "license": "MIT", "engines": { "node": ">= 0.4.0" @@ -9548,27 +7783,22 @@ }, "node_modules/uuid": { "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], + "license": "MIT", "bin": { "uuid": "dist/esm/bin/uuid" } }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true, "license": "MIT" }, "node_modules/vary": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -9576,8 +7806,6 @@ }, "node_modules/vite": { "version": "6.1.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.1.1.tgz", - "integrity": "sha512-4GgM54XrwRfrOp297aIYspIti66k56v16ZnqHvrIM7mG+HjDlAwS7p+Srr7J6fGvEdOJ5JcQ/D9T7HhtdXDTzA==", "dev": true, "license": "MIT", "dependencies": { @@ -9648,8 +7876,6 @@ }, "node_modules/vite-hot-client": { "version": "0.2.4", - "resolved": "https://registry.npmjs.org/vite-hot-client/-/vite-hot-client-0.2.4.tgz", - "integrity": "sha512-a1nzURqO7DDmnXqabFOliz908FRmIppkBKsJthS8rbe8hBEXwEwe4C3Pp33Z1JoFCYfVL4kTOMLKk0ZZxREIeA==", "dev": true, "license": "MIT", "funding": { @@ -9661,8 +7887,6 @@ }, "node_modules/vite-node": { "version": "3.0.6", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.6.tgz", - "integrity": "sha512-s51RzrTkXKJrhNbUzQRsarjmAae7VmMPAsRT7lppVpIg6mK3zGthP9Hgz0YQQKuNcF+Ii7DfYk3Fxz40jRmePw==", "dev": true, "license": "MIT", "dependencies": { @@ -9684,8 +7908,6 @@ }, "node_modules/vite-plugin-inspect": { "version": "0.8.9", - "resolved": "https://registry.npmjs.org/vite-plugin-inspect/-/vite-plugin-inspect-0.8.9.tgz", - "integrity": "sha512-22/8qn+LYonzibb1VeFZmISdVao5kC22jmEKm24vfFE8siEn47EpVcCLYMv6iKOYMJfjSvSJfueOwcFCkUnV3A==", "dev": true, "license": "MIT", "dependencies": { @@ -9716,8 +7938,6 @@ }, "node_modules/vite-plugin-vue-devtools": { "version": "7.7.2", - "resolved": "https://registry.npmjs.org/vite-plugin-vue-devtools/-/vite-plugin-vue-devtools-7.7.2.tgz", - "integrity": "sha512-5V0UijQWiSBj32blkyPEqIbzc6HO9c1bwnBhx+ay2dzU0FakH+qMdNUT8nF9BvDE+i6I1U8CqCuJiO20vKEdQw==", "dev": true, "license": "MIT", "dependencies": { @@ -9738,8 +7958,6 @@ }, "node_modules/vite-plugin-vue-inspector": { "version": "5.3.1", - "resolved": "https://registry.npmjs.org/vite-plugin-vue-inspector/-/vite-plugin-vue-inspector-5.3.1.tgz", - "integrity": "sha512-cBk172kZKTdvGpJuzCCLg8lJ909wopwsu3Ve9FsL1XsnLBiRT9U3MePcqrgGHgCX2ZgkqZmAGR8taxw+TV6s7A==", "dev": true, "license": "MIT", "dependencies": { @@ -9757,282 +7975,8 @@ "vite": "^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0" } }, - "node_modules/vite/node_modules/@esbuild/aix-ppc64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", - "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", - "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", - "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", - "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", - "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", - "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", - "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", - "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", - "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", - "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", - "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", - "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", - "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", - "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", - "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", - "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, "node_modules/vite/node_modules/@esbuild/linux-x64": { "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", - "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", "cpu": [ "x64" ], @@ -10046,146 +7990,8 @@ "node": ">=18" } }, - "node_modules/vite/node_modules/@esbuild/netbsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz", - "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", - "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/openbsd-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", - "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", - "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", - "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", - "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", - "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", - "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, "node_modules/vite/node_modules/esbuild": { "version": "0.24.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", - "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -10225,8 +8031,6 @@ }, "node_modules/vitest": { "version": "3.0.6", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.6.tgz", - "integrity": "sha512-/iL1Sc5VeDZKPDe58oGK4HUFLhw6b5XdY1MYawjuSaDA4sEfYlY9HnS6aCEG26fX+MgUi7MwlduTBHHAI/OvMA==", "dev": true, "license": "MIT", "dependencies": { @@ -10295,15 +8099,11 @@ }, "node_modules/vscode-uri": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", - "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", "dev": true, "license": "MIT" }, "node_modules/vue": { "version": "3.5.13", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz", - "integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==", "license": "MIT", "dependencies": { "@vue/compiler-dom": "3.5.13", @@ -10323,15 +8123,11 @@ }, "node_modules/vue-component-type-helpers": { "version": "2.2.2", - "resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-2.2.2.tgz", - "integrity": "sha512-6lLY+n2xz2kCYshl59mL6gy8OUUTmkscmDFMO8i7Lj+QKwgnIFUZmM1i/iTYObtrczZVdw7UakPqDTGwVSGaRg==", "dev": true, "license": "MIT" }, "node_modules/vue-eslint-parser": { "version": "9.4.3", - "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz", - "integrity": "sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==", "dev": true, "license": "MIT", "dependencies": { @@ -10355,8 +8151,6 @@ }, "node_modules/vue-eslint-parser/node_modules/eslint-scope": { "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -10372,8 +8166,6 @@ }, "node_modules/vue-eslint-parser/node_modules/espree": { "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -10390,8 +8182,6 @@ }, "node_modules/vue-i18n": { "version": "10.0.5", - "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-10.0.5.tgz", - "integrity": "sha512-9/gmDlCblz3i8ypu/afiIc/SUIfTTE1mr0mZhb9pk70xo2csHAM9mp2gdQ3KD2O0AM3Hz/5ypb+FycTj/lHlPQ==", "license": "MIT", "dependencies": { "@intlify/core-base": "10.0.5", @@ -10410,8 +8200,6 @@ }, "node_modules/vue-router": { "version": "4.5.0", - "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.0.tgz", - "integrity": "sha512-HDuk+PuH5monfNuY+ct49mNmkCRK4xJAV9Ts4z9UFc4rzdDnxQLyCMGGc8pKhZhHTVzfanpNwB/lwqevcBwI4w==", "license": "MIT", "dependencies": { "@vue/devtools-api": "^6.6.4" @@ -10425,8 +8213,6 @@ }, "node_modules/vue-tsc": { "version": "2.2.2", - "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.2.2.tgz", - "integrity": "sha512-1icPKkxAA5KTAaSwg0wVWdE48EdsH8fgvcbAiqojP4jXKl6LEM3soiW1aG/zrWrFt8Mw1ncG2vG1PvpZpVfehA==", "dev": true, "license": "MIT", "dependencies": { @@ -10441,9 +8227,7 @@ } }, "node_modules/vuetify": { - "version": "3.7.13", - "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.7.13.tgz", - "integrity": "sha512-4+RuQU+zLtXhlN2eZUpKXums9ftzUzhMeiNEJvvJY4XdOzVwUCth2dTnEZkSF6EKdLHk3WhtRk0cIWXZxpBvcw==", + "version": "3.7.12", "license": "MIT", "engines": { "node": "^12.20 || >=14.13" @@ -10472,8 +8256,6 @@ }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", - "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", "dev": true, "license": "MIT", "dependencies": { @@ -10485,8 +8267,6 @@ }, "node_modules/w3c-xmlserializer/node_modules/xml-name-validator": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", - "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", "dev": true, "license": "Apache-2.0", "engines": { @@ -10495,8 +8275,6 @@ }, "node_modules/webidl-conversions": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -10505,8 +8283,6 @@ }, "node_modules/whatwg-encoding": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", - "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", "dev": true, "license": "MIT", "dependencies": { @@ -10518,8 +8294,6 @@ }, "node_modules/whatwg-encoding/node_modules/iconv-lite": { "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, "license": "MIT", "dependencies": { @@ -10531,8 +8305,6 @@ }, "node_modules/whatwg-mimetype": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", - "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", "dev": true, "license": "MIT", "engines": { @@ -10541,8 +8313,6 @@ }, "node_modules/whatwg-url": { "version": "14.1.1", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.1.tgz", - "integrity": "sha512-mDGf9diDad/giZ/Sm9Xi2YcyzaFpbdLpJPr+E9fSkyQ7KpQD4SdFcugkRQYzhmfI4KeV4Qpnn2sKPdo+kmsgRQ==", "dev": true, "license": "MIT", "dependencies": { @@ -10555,8 +8325,6 @@ }, "node_modules/which": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "devOptional": true, "license": "ISC", "dependencies": { @@ -10571,8 +8339,6 @@ }, "node_modules/why-is-node-running": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, "license": "MIT", "dependencies": { @@ -10588,8 +8354,7 @@ }, "node_modules/wide-align": { "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "license": "ISC", "optional": true, "dependencies": { "string-width": "^1.0.2 || 2 || 3 || 4" @@ -10597,8 +8362,7 @@ }, "node_modules/wide-align/node_modules/ansi-regex": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", "optional": true, "engines": { "node": ">=8" @@ -10606,14 +8370,12 @@ }, "node_modules/wide-align/node_modules/emoji-regex": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT", "optional": true }, "node_modules/wide-align/node_modules/string-width": { "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", "optional": true, "dependencies": { "emoji-regex": "^8.0.0", @@ -10626,8 +8388,7 @@ }, "node_modules/wide-align/node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", "optional": true, "dependencies": { "ansi-regex": "^5.0.1" @@ -10638,8 +8399,6 @@ }, "node_modules/word-wrap": { "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "license": "MIT", "engines": { @@ -10647,18 +8406,16 @@ } }, "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "version": "8.1.0", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/chalk/wrap-ansi?sponsor=1" @@ -10667,8 +8424,6 @@ "node_modules/wrap-ansi-cjs": { "name": "wrap-ansi", "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "license": "MIT", "dependencies": { @@ -10683,16 +8438,60 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, "node_modules/ws": { "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "dev": true, "license": "MIT", "engines": { @@ -10713,8 +8512,6 @@ }, "node_modules/xml-name-validator": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", - "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", "dev": true, "license": "Apache-2.0", "engines": { @@ -10723,15 +8520,11 @@ }, "node_modules/xmlchars": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true, "license": "MIT" }, "node_modules/xtend": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", "license": "MIT", "engines": { "node": ">=0.4" @@ -10739,8 +8532,6 @@ }, "node_modules/y18n": { "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, "license": "ISC", "engines": { @@ -10749,15 +8540,11 @@ }, "node_modules/yallist": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true, "license": "ISC" }, "node_modules/yargs": { "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "license": "MIT", "dependencies": { @@ -10775,18 +8562,51 @@ }, "node_modules/yargs-parser": { "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, "license": "ISC", "engines": { "node": ">=12" } }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yn": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true, "license": "MIT", "engines": { @@ -10795,8 +8615,6 @@ }, "node_modules/yocto-queue": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, "license": "MIT", "engines": { @@ -10808,8 +8626,6 @@ }, "node_modules/yoctocolors": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.1.tgz", - "integrity": "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==", "dev": true, "license": "MIT", "engines": { From 054e761baa500ac9a79e4efe58e549ccbaeb0ab9 Mon Sep 17 00:00:00 2001 From: Gerald Schmittinger Date: Sat, 1 Mar 2025 23:47:32 +0100 Subject: [PATCH 02/96] feat(backend): Generic authentication checks. Added support for deciding based on any predicate about the current AuthenticationInfo whether or not a request will be accepted. --- backend/src/middleware/auth/auth.ts | 78 ++++++++++++++----- .../middleware/auth/authentication-info.ts | 11 +++ 2 files changed, 71 insertions(+), 18 deletions(-) create mode 100644 backend/src/middleware/auth/authentication-info.ts diff --git a/backend/src/middleware/auth/auth.ts b/backend/src/middleware/auth/auth.ts index 82f1b23f..ec097fd5 100644 --- a/backend/src/middleware/auth/auth.ts +++ b/backend/src/middleware/auth/auth.ts @@ -5,6 +5,7 @@ import jwksClient from 'jwks-rsa'; import * as express from "express"; import * as jwt from "jsonwebtoken"; import {AuthenticatedRequest} from "./authenticated-request.js"; +import {AuthenticationInfo} from "./authentication-info"; function createJwksClient(uri: string): jwksClient.JwksClient { return jwksClient({ @@ -25,6 +26,9 @@ const idpConfigs = { } }; +/** + * Express middleware which verifies the JWT Bearer token if one is given in the request. + */ export const authenticateUser = expressjwt({ secret: async (_: express.Request, token: jwt.Jwt | undefined) => { if (!token?.payload || !(token.payload as JwtPayload).iss) { @@ -49,27 +53,65 @@ export const authenticateUser = expressjwt({ credentialsRequired: false }); -const authorizeRole = (studentsAllowed: boolean, teachersAllowed: boolean) => { +/** + * Get an object with information about the authenticated user from a given authenticated request. + */ +function getAuthenticationInfo(req: AuthenticatedRequest): AuthenticationInfo | undefined { + if (!req.auth) { + return; + } + + let issuer = req.auth.issuer; + let accountType: "student" | "teacher"; + + if (issuer === idpConfigs.student.issuer) { + accountType = "student"; + } else if (issuer === idpConfigs.teacher.issuer) { + accountType = "teacher"; + } else { + return; + } + + return { + accountType: accountType, + username: req.auth["preferred_username"]!, + name: req.auth["name"], + firstName: req.auth["given_name"], + lastName: req.auth["family_name"], + email: req.auth["email"], + } +} + +/** + * Middleware which rejects unauthenticated users (with HTTP 401) and authenticated users which do not fulfill + * the given access condition. + * @param accessCondition Predicate over the current AuthenticationInfo. Access is only granted when this evaluates + * to true. + */ +const authorize = (accessCondition: (auth: AuthenticationInfo) => boolean) => { return (req: AuthenticatedRequest, res: express.Response, next: express.NextFunction): void => { - if (!req.auth) { + let authInfo = getAuthenticationInfo(req); + if (!authInfo) { res.status(401).json({ message: "Unauthorized" }); - return; + } else if (!accessCondition(authInfo)) { + res.status(403).json({ message: "Forbidden" }); + } else { + next(); } + } +} - const issuer = req.auth.iss; - if (issuer === idpConfigs.student.issuer && !studentsAllowed) { - res.status(403).json({ message: "Students not allowed" }); - return; - } - if (issuer === idpConfigs.teacher.issuer && !teachersAllowed) { - res.status(403).json({ message: "Teachers not allowed" }); - return; - } +/** + * Middleware which rejects all unauthenticated users, but accepts all authenticated users. + */ +export const authenticatedOnly = authorize(_ => true); - next(); // User is allowed - }; -}; +/** + * Middleware which rejects requests from unauthenticated users or users that aren't students. + */ +export const studentsOnly = authorize(auth => auth.accountType === "student"); -export const authenticatedOnly = authorizeRole(true, true); -export const studentsOnly = authorizeRole(true, false); -export const teachersOnly = authorizeRole(false, true); +/** + * Middleware which rejects requests from unauthenticated users or users that aren't teachers. + */ +export const teachersOnly = authorize(auth => auth.accountType === "teacher"); diff --git a/backend/src/middleware/auth/authentication-info.ts b/backend/src/middleware/auth/authentication-info.ts new file mode 100644 index 00000000..6711edd0 --- /dev/null +++ b/backend/src/middleware/auth/authentication-info.ts @@ -0,0 +1,11 @@ +/** + * Object with information about the user who is currently logged in. + */ +export type AuthenticationInfo = { + accountType: "student" | "teacher", + username: string, + name?: string, + firstName?: string, + lastName?: string, + email?: string +}; From 69ba8c9567df56fc2871f4a88a416c8b0e1cc319 Mon Sep 17 00:00:00 2001 From: Gerald Schmittinger Date: Sun, 2 Mar 2025 01:24:49 +0100 Subject: [PATCH 03/96] feat(backend): Generic authentication checks. Added support for deciding based on any predicate about the current AuthenticationInfo whether or not a request will be accepted. --- backend/src/middleware/auth/auth.ts | 40 ++++++++++++------- .../middleware/auth/authenticated-request.ts | 5 ++- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/backend/src/middleware/auth/auth.ts b/backend/src/middleware/auth/auth.ts index ec097fd5..0bf63e3d 100644 --- a/backend/src/middleware/auth/auth.ts +++ b/backend/src/middleware/auth/auth.ts @@ -29,7 +29,7 @@ const idpConfigs = { /** * Express middleware which verifies the JWT Bearer token if one is given in the request. */ -export const authenticateUser = expressjwt({ +const verifyJwtToken = expressjwt({ secret: async (_: express.Request, token: jwt.Jwt | undefined) => { if (!token?.payload || !(token.payload as JwtPayload).iss) { throw new Error("Invalid token"); @@ -50,18 +50,19 @@ export const authenticateUser = expressjwt({ }, audience: getEnvVar(EnvVars.IdpAudience), algorithms: ["RS256"], - credentialsRequired: false + credentialsRequired: false, + requestProperty: "jwtPayload" }); /** * Get an object with information about the authenticated user from a given authenticated request. */ function getAuthenticationInfo(req: AuthenticatedRequest): AuthenticationInfo | undefined { - if (!req.auth) { + console.log("hi"); + if (!req.jwtPayload) { return; } - - let issuer = req.auth.issuer; + let issuer = req.jwtPayload.iss; let accountType: "student" | "teacher"; if (issuer === idpConfigs.student.issuer) { @@ -71,29 +72,38 @@ function getAuthenticationInfo(req: AuthenticatedRequest): AuthenticationInfo | } else { return; } - return { accountType: accountType, - username: req.auth["preferred_username"]!, - name: req.auth["name"], - firstName: req.auth["given_name"], - lastName: req.auth["family_name"], - email: req.auth["email"], + username: req.jwtPayload["preferred_username"]!, + name: req.jwtPayload["name"], + firstName: req.jwtPayload["given_name"], + lastName: req.jwtPayload["family_name"], + email: req.jwtPayload["email"], } } +/** + * Add the AuthenticationInfo object with the information about the current authentication to the request in order + * to avoid that the routers have to deal with the JWT token. + */ +const addAuthenticationInfo = (req: AuthenticatedRequest, res: express.Response, next: express.NextFunction) => { + req.auth = getAuthenticationInfo(req); + next(); +}; + +export const authenticateUser = [verifyJwtToken, addAuthenticationInfo]; + /** * Middleware which rejects unauthenticated users (with HTTP 401) and authenticated users which do not fulfill * the given access condition. * @param accessCondition Predicate over the current AuthenticationInfo. Access is only granted when this evaluates * to true. */ -const authorize = (accessCondition: (auth: AuthenticationInfo) => boolean) => { +export const authorize = (accessCondition: (auth: AuthenticationInfo) => boolean) => { return (req: AuthenticatedRequest, res: express.Response, next: express.NextFunction): void => { - let authInfo = getAuthenticationInfo(req); - if (!authInfo) { + if (!req.auth) { res.status(401).json({ message: "Unauthorized" }); - } else if (!accessCondition(authInfo)) { + } else if (!accessCondition(req.auth)) { res.status(403).json({ message: "Forbidden" }); } else { next(); diff --git a/backend/src/middleware/auth/authenticated-request.ts b/backend/src/middleware/auth/authenticated-request.ts index 9b33de6f..b29cfbac 100644 --- a/backend/src/middleware/auth/authenticated-request.ts +++ b/backend/src/middleware/auth/authenticated-request.ts @@ -1,6 +1,9 @@ import { Request } from "express"; import { JwtPayload } from "jsonwebtoken"; +import {AuthenticationInfo} from "./authentication-info"; export interface AuthenticatedRequest extends Request { - auth?: JwtPayload; // Optional, as req.auth might be undefined if authentication is optional + // Properties are optional since the user is not necessarily authenticated. + jwtPayload?: JwtPayload; + auth?: AuthenticationInfo; } From 4f201a12530e83aabe0ee370e3a63234622adee1 Mon Sep 17 00:00:00 2001 From: Gerald Schmittinger Date: Sun, 2 Mar 2025 01:25:57 +0100 Subject: [PATCH 04/96] chore(backend): Automatische import van testdata in Keycloack. Zie idp/README.md voor meer informatie erover wat de testdata inhoudt. --- docker-compose.yml | 15 +- idp/README.md | 7 + idp/student-realm.json | 2062 ++++++++++++++++++++++++++++++++++++++++ idp/teacher-realm.json | 2060 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 4141 insertions(+), 3 deletions(-) create mode 100644 idp/README.md create mode 100644 idp/student-realm.json create mode 100644 idp/teacher-realm.json diff --git a/docker-compose.yml b/docker-compose.yml index df906ba4..18249f52 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,12 +11,14 @@ services: - postgres_data:/var/lib/postgresql/data idp: # Bron: https://medium.com/@fingervinicius/easy-running-keycloak-with-docker-compose-b0d7a4ee2358 image: quay.io/keycloak/keycloak:latest + volumes: + - ./idp:/opt/keycloak/data/import environment: KC_HOSTNAME: localhost KC_HOSTNAME_PORT: 7080 KC_HOSTNAME_STRICT_BACKCHANNEL: "true" - KEYCLOAK_ADMIN: admin - KEYCLOAK_ADMIN_PASSWORD: admin + KC_BOOTSTRAP_ADMIN_USERNAME: admin + KC_BOOTSTRAP_ADMIN_PASSWORD: admin KC_HEALTH_ENABLED: "true" KC_LOG_LEVEL: info healthcheck: @@ -24,9 +26,16 @@ services: interval: 15s timeout: 2s retries: 15 - command: ["start-dev", "--http-port", "7080", "--https-port", "7443"] + command: [ + "start-dev", + "--http-port", "7080", + "--https-port", "7443", + "--import-realm" + ] ports: - "7080:7080" - "7443:7443" + depends_on: + - db volumes: postgres_data: diff --git a/idp/README.md b/idp/README.md new file mode 100644 index 00000000..3f3fd4ff --- /dev/null +++ b/idp/README.md @@ -0,0 +1,7 @@ +# Testdata in de IDP +De IDP in `docker-compose.yml` is zo geconfigureerd dat hij automatisch bij het starten een testconfiguratie inlaadt. Deze houdt in: +* Een realm `student` die de IDP voor leerlingen representeert. + * Hierin de gebruiker met username `testleerling1`, wachtwoord `password`. +* Een realm `teacher` die de IDP voor leerkrachten representeert. + * Hierin de gebruiker met username `testleerkracht1`, wachtwoord `password`. +* De admin-account (in de realm `master`) heeft username `admin` en wachtwoord `admin`. diff --git a/idp/student-realm.json b/idp/student-realm.json new file mode 100644 index 00000000..57ff2dd2 --- /dev/null +++ b/idp/student-realm.json @@ -0,0 +1,2062 @@ +{ + "id" : "08a7ab0a-d483-4103-a781-76013864bf50", + "realm" : "student", + "notBefore" : 0, + "defaultSignatureAlgorithm" : "RS256", + "revokeRefreshToken" : false, + "refreshTokenMaxReuse" : 0, + "accessTokenLifespan" : 300, + "accessTokenLifespanForImplicitFlow" : 900, + "ssoSessionIdleTimeout" : 1800, + "ssoSessionMaxLifespan" : 36000, + "ssoSessionIdleTimeoutRememberMe" : 0, + "ssoSessionMaxLifespanRememberMe" : 0, + "offlineSessionIdleTimeout" : 2592000, + "offlineSessionMaxLifespanEnabled" : false, + "offlineSessionMaxLifespan" : 5184000, + "clientSessionIdleTimeout" : 0, + "clientSessionMaxLifespan" : 0, + "clientOfflineSessionIdleTimeout" : 0, + "clientOfflineSessionMaxLifespan" : 0, + "accessCodeLifespan" : 60, + "accessCodeLifespanUserAction" : 300, + "accessCodeLifespanLogin" : 1800, + "actionTokenGeneratedByAdminLifespan" : 43200, + "actionTokenGeneratedByUserLifespan" : 300, + "oauth2DeviceCodeLifespan" : 600, + "oauth2DevicePollingInterval" : 5, + "enabled" : true, + "sslRequired" : "external", + "registrationAllowed" : false, + "registrationEmailAsUsername" : false, + "rememberMe" : false, + "verifyEmail" : false, + "loginWithEmailAllowed" : true, + "duplicateEmailsAllowed" : false, + "resetPasswordAllowed" : false, + "editUsernameAllowed" : false, + "bruteForceProtected" : false, + "permanentLockout" : false, + "maxTemporaryLockouts" : 0, + "bruteForceStrategy" : "MULTIPLE", + "maxFailureWaitSeconds" : 900, + "minimumQuickLoginWaitSeconds" : 60, + "waitIncrementSeconds" : 60, + "quickLoginCheckMilliSeconds" : 1000, + "maxDeltaTimeSeconds" : 43200, + "failureFactor" : 30, + "roles" : { + "realm" : [ { + "id" : "a0bb00f5-0b3a-4d57-a3fc-a3f93cbe3427", + "name" : "offline_access", + "description" : "${role_offline-access}", + "composite" : false, + "clientRole" : false, + "containerId" : "08a7ab0a-d483-4103-a781-76013864bf50", + "attributes" : { } + }, { + "id" : "b3bf9566-098c-4167-9cce-f64c720ca511", + "name" : "default-roles-student", + "description" : "${role_default-roles}", + "composite" : true, + "composites" : { + "realm" : [ "offline_access", "uma_authorization" ], + "client" : { + "account" : [ "manage-account", "view-profile" ] + } + }, + "clientRole" : false, + "containerId" : "08a7ab0a-d483-4103-a781-76013864bf50", + "attributes" : { } + }, { + "id" : "6d044f54-8ff3-4223-9e8c-771882da7a3f", + "name" : "uma_authorization", + "description" : "${role_uma_authorization}", + "composite" : false, + "clientRole" : false, + "containerId" : "08a7ab0a-d483-4103-a781-76013864bf50", + "attributes" : { } + } ], + "client" : { + "realm-management" : [ { + "id" : "f125e557-2427-4eeb-95c5-b3dadf35f9c7", + "name" : "manage-authorization", + "description" : "${role_manage-authorization}", + "composite" : false, + "clientRole" : true, + "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes" : { } + }, { + "id" : "33c7285a-7308-4752-acad-1fe59bf1c81a", + "name" : "manage-identity-providers", + "description" : "${role_manage-identity-providers}", + "composite" : false, + "clientRole" : true, + "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes" : { } + }, { + "id" : "31fb3621-62c7-43c8-af98-a4add3470fcc", + "name" : "query-clients", + "description" : "${role_query-clients}", + "composite" : false, + "clientRole" : true, + "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes" : { } + }, { + "id" : "e077c3c3-d573-494f-9cf8-34eca6603fc6", + "name" : "realm-admin", + "description" : "${role_realm-admin}", + "composite" : true, + "composites" : { + "client" : { + "realm-management" : [ "manage-authorization", "query-clients", "manage-identity-providers", "create-client", "view-users", "view-authorization", "query-users", "manage-users", "view-identity-providers", "impersonation", "manage-realm", "view-events", "view-clients", "manage-events", "manage-clients", "view-realm", "query-groups", "query-realms" ] + } + }, + "clientRole" : true, + "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes" : { } + }, { + "id" : "8bbe59b1-7693-4274-bdde-c08f94ec3187", + "name" : "create-client", + "description" : "${role_create-client}", + "composite" : false, + "clientRole" : true, + "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes" : { } + }, { + "id" : "0533162d-7dac-4ebf-87a2-7f72dad79d53", + "name" : "view-users", + "description" : "${role_view-users}", + "composite" : true, + "composites" : { + "client" : { + "realm-management" : [ "query-groups", "query-users" ] + } + }, + "clientRole" : true, + "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes" : { } + }, { + "id" : "d4b32078-67b4-4aa8-8ddf-01a820e7b64a", + "name" : "view-authorization", + "description" : "${role_view-authorization}", + "composite" : false, + "clientRole" : true, + "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes" : { } + }, { + "id" : "2a48ab18-b710-41e7-8b8c-67a5cd6af685", + "name" : "query-users", + "description" : "${role_query-users}", + "composite" : false, + "clientRole" : true, + "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes" : { } + }, { + "id" : "d71d575f-3f21-4f4a-b9e0-2628352aac8d", + "name" : "manage-users", + "description" : "${role_manage-users}", + "composite" : false, + "clientRole" : true, + "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes" : { } + }, { + "id" : "7d3cd659-4ddd-45cd-8186-210431a25bbd", + "name" : "impersonation", + "description" : "${role_impersonation}", + "composite" : false, + "clientRole" : true, + "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes" : { } + }, { + "id" : "3dbd18ca-11dc-463d-bf8e-e7d80928a90d", + "name" : "view-identity-providers", + "description" : "${role_view-identity-providers}", + "composite" : false, + "clientRole" : true, + "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes" : { } + }, { + "id" : "d4a6ef1e-bf84-4bd6-8763-1b0c9997c109", + "name" : "manage-realm", + "description" : "${role_manage-realm}", + "composite" : false, + "clientRole" : true, + "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes" : { } + }, { + "id" : "f0eab8d7-0570-44d3-94d0-2a43906d9f09", + "name" : "view-events", + "description" : "${role_view-events}", + "composite" : false, + "clientRole" : true, + "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes" : { } + }, { + "id" : "0a24b91f-ef4a-4f4b-a753-1286dd59df2b", + "name" : "view-clients", + "description" : "${role_view-clients}", + "composite" : true, + "composites" : { + "client" : { + "realm-management" : [ "query-clients" ] + } + }, + "clientRole" : true, + "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes" : { } + }, { + "id" : "b307485c-8840-4c39-ba81-fb840fa404d1", + "name" : "manage-events", + "description" : "${role_manage-events}", + "composite" : false, + "clientRole" : true, + "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes" : { } + }, { + "id" : "3719a5ed-be30-4d2c-93f5-cc6e6c0e792e", + "name" : "manage-clients", + "description" : "${role_manage-clients}", + "composite" : false, + "clientRole" : true, + "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes" : { } + }, { + "id" : "d4b13416-9f5e-42fb-bfdd-6489093922da", + "name" : "view-realm", + "description" : "${role_view-realm}", + "composite" : false, + "clientRole" : true, + "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes" : { } + }, { + "id" : "15ac861b-5440-4fe8-9f7d-857d75ec481d", + "name" : "query-groups", + "description" : "${role_query-groups}", + "composite" : false, + "clientRole" : true, + "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes" : { } + }, { + "id" : "f05a8e4d-90ea-41f6-887b-0b6b1ecb9cd9", + "name" : "query-realms", + "description" : "${role_query-realms}", + "composite" : false, + "clientRole" : true, + "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes" : { } + } ], + "dwengo" : [ ], + "security-admin-console" : [ ], + "admin-cli" : [ ], + "account-console" : [ ], + "broker" : [ { + "id" : "da1edd82-7479-4e9d-ad66-9a4cf739e828", + "name" : "read-token", + "description" : "${role_read-token}", + "composite" : false, + "clientRole" : true, + "containerId" : "befe3d72-8102-49a6-8268-bce6def58159", + "attributes" : { } + } ], + "account" : [ { + "id" : "5a3da53d-235b-4d12-b8ec-1573b13ebafc", + "name" : "view-consent", + "description" : "${role_view-consent}", + "composite" : false, + "clientRole" : true, + "containerId" : "b3a22454-d780-4093-8333-9be6f6cd5855", + "attributes" : { } + }, { + "id" : "cbc0c1d4-487b-488c-8566-1d4537212de8", + "name" : "manage-account-links", + "description" : "${role_manage-account-links}", + "composite" : false, + "clientRole" : true, + "containerId" : "b3a22454-d780-4093-8333-9be6f6cd5855", + "attributes" : { } + }, { + "id" : "79b0ed8f-bf10-4b01-bb2c-e7a58d57c798", + "name" : "delete-account", + "description" : "${role_delete-account}", + "composite" : false, + "clientRole" : true, + "containerId" : "b3a22454-d780-4093-8333-9be6f6cd5855", + "attributes" : { } + }, { + "id" : "b6aa748e-0fb0-4fa6-a0d1-3ea37c870467", + "name" : "manage-account", + "description" : "${role_manage-account}", + "composite" : true, + "composites" : { + "client" : { + "account" : [ "manage-account-links" ] + } + }, + "clientRole" : true, + "containerId" : "b3a22454-d780-4093-8333-9be6f6cd5855", + "attributes" : { } + }, { + "id" : "ddaea6cd-ede8-49f7-9746-3a3a02fdeca5", + "name" : "view-profile", + "description" : "${role_view-profile}", + "composite" : false, + "clientRole" : true, + "containerId" : "b3a22454-d780-4093-8333-9be6f6cd5855", + "attributes" : { } + }, { + "id" : "061b2038-b415-4a45-89ec-7141004c0151", + "name" : "view-applications", + "description" : "${role_view-applications}", + "composite" : false, + "clientRole" : true, + "containerId" : "b3a22454-d780-4093-8333-9be6f6cd5855", + "attributes" : { } + }, { + "id" : "95972aa1-6666-421c-8596-a91eee54b0e8", + "name" : "view-groups", + "description" : "${role_view-groups}", + "composite" : false, + "clientRole" : true, + "containerId" : "b3a22454-d780-4093-8333-9be6f6cd5855", + "attributes" : { } + }, { + "id" : "1cf27d94-d88d-42d3-b8f3-ede1f127ac45", + "name" : "manage-consent", + "description" : "${role_manage-consent}", + "composite" : true, + "composites" : { + "client" : { + "account" : [ "view-consent" ] + } + }, + "clientRole" : true, + "containerId" : "b3a22454-d780-4093-8333-9be6f6cd5855", + "attributes" : { } + } ] + } + }, + "groups" : [ ], + "defaultRole" : { + "id" : "b3bf9566-098c-4167-9cce-f64c720ca511", + "name" : "default-roles-student", + "description" : "${role_default-roles}", + "composite" : true, + "clientRole" : false, + "containerId" : "08a7ab0a-d483-4103-a781-76013864bf50" + }, + "requiredCredentials" : [ "password" ], + "otpPolicyType" : "totp", + "otpPolicyAlgorithm" : "HmacSHA1", + "otpPolicyInitialCounter" : 0, + "otpPolicyDigits" : 6, + "otpPolicyLookAheadWindow" : 1, + "otpPolicyPeriod" : 30, + "otpPolicyCodeReusable" : false, + "otpSupportedApplications" : [ "totpAppFreeOTPName", "totpAppGoogleName", "totpAppMicrosoftAuthenticatorName" ], + "localizationTexts" : { }, + "webAuthnPolicyRpEntityName" : "keycloak", + "webAuthnPolicySignatureAlgorithms" : [ "ES256", "RS256" ], + "webAuthnPolicyRpId" : "", + "webAuthnPolicyAttestationConveyancePreference" : "not specified", + "webAuthnPolicyAuthenticatorAttachment" : "not specified", + "webAuthnPolicyRequireResidentKey" : "not specified", + "webAuthnPolicyUserVerificationRequirement" : "not specified", + "webAuthnPolicyCreateTimeout" : 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister" : false, + "webAuthnPolicyAcceptableAaguids" : [ ], + "webAuthnPolicyExtraOrigins" : [ ], + "webAuthnPolicyPasswordlessRpEntityName" : "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms" : [ "ES256", "RS256" ], + "webAuthnPolicyPasswordlessRpId" : "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference" : "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment" : "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey" : "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement" : "not specified", + "webAuthnPolicyPasswordlessCreateTimeout" : 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister" : false, + "webAuthnPolicyPasswordlessAcceptableAaguids" : [ ], + "webAuthnPolicyPasswordlessExtraOrigins" : [ ], + "users" : [ { + "id" : "79e9a395-d7e4-48c9-a06e-702435bae290", + "username" : "testleerling1", + "firstName" : "Gerald", + "lastName" : "Schmittinger", + "email" : "Gerald.Schmittinger@UGent.be", + "emailVerified" : false, + "createdTimestamp" : 1740858528405, + "enabled" : true, + "totp" : false, + "credentials" : [ { + "id" : "c31a708f-8614-4144-a25f-3e976c9035ce", + "type" : "password", + "userLabel" : "My password", + "createdDate" : 1740858548515, + "secretData" : "{\"value\":\"yDKIAbZPuVXBGk4zjiqE/YFcPDm1vjXLwTrPUrvMhXY=\",\"salt\":\"tYvjd4mhV2UWeOUssK01Cw==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":5,\"algorithm\":\"argon2\",\"additionalParameters\":{\"hashLength\":[\"32\"],\"memory\":[\"7168\"],\"type\":[\"id\"],\"version\":[\"1.3\"],\"parallelism\":[\"1\"]}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-student" ], + "notBefore" : 0, + "groups" : [ ] + } ], + "scopeMappings" : [ { + "clientScope" : "offline_access", + "roles" : [ "offline_access" ] + } ], + "clientScopeMappings" : { + "account" : [ { + "client" : "account-console", + "roles" : [ "manage-account", "view-groups" ] + } ] + }, + "clients" : [ { + "id" : "b3a22454-d780-4093-8333-9be6f6cd5855", + "clientId" : "account", + "name" : "${client_account}", + "rootUrl" : "${authBaseUrl}", + "baseUrl" : "/realms/student/account/", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ "/realms/student/account/*" ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "realm_client" : "false", + "post.logout.redirect.uris" : "+" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "basic", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "organization", "microprofile-jwt" ] + }, { + "id" : "854c221b-630c-4cc3-9365-bd254246dd69", + "clientId" : "account-console", + "name" : "${client_account-console}", + "rootUrl" : "${authBaseUrl}", + "baseUrl" : "/realms/student/account/", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ "/realms/student/account/*" ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "realm_client" : "false", + "post.logout.redirect.uris" : "+", + "pkce.code.challenge.method" : "S256" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "protocolMappers" : [ { + "id" : "f33b40fe-bb9e-4254-ada9-f98dd203641b", + "name" : "audience resolve", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-audience-resolve-mapper", + "consentRequired" : false, + "config" : { } + } ], + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "basic", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "organization", "microprofile-jwt" ] + }, { + "id" : "9449aa8b-d5cc-4b9f-bb01-be1e5a896f2f", + "clientId" : "admin-cli", + "name" : "${client_admin-cli}", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : false, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : true, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "realm_client" : "false", + "client.use.lightweight.access.token.enabled" : "true" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : true, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "basic", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "organization", "microprofile-jwt" ] + }, { + "id" : "befe3d72-8102-49a6-8268-bce6def58159", + "clientId" : "broker", + "name" : "${client_broker}", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : true, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : false, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "realm_client" : "true" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "basic", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "organization", "microprofile-jwt" ] + }, { + "id" : "714243ae-72cc-4c26-842a-047357b5919a", + "clientId" : "dwengo", + "name" : "Dwengo", + "description" : "", + "rootUrl" : "http://localhost:3000", + "adminUrl" : "http://localhost:3000", + "baseUrl" : "/", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-jwt", + "redirectUris" : [ "urn:ietf:wg:oauth:2.0:oob", "http://localhost:3000/*", "http://localhost:3000" ], + "webOrigins" : [ "+" ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : true, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : true, + "protocol" : "openid-connect", + "attributes" : { + "realm_client" : "false", + "oidc.ciba.grant.enabled" : "false", + "client.secret.creation.time" : "1740860818", + "backchannel.logout.session.required" : "true", + "token.endpoint.auth.signing.alg" : "RS256", + "post.logout.redirect.uris" : "+", + "frontchannel.logout.session.required" : "true", + "oauth2.device.authorization.grant.enabled" : "false", + "display.on.consent.screen" : "false", + "backchannel.logout.revoke.offline.tokens" : "false" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : true, + "nodeReRegistrationTimeout" : -1, + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "basic", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "organization", "microprofile-jwt" ] + }, { + "id" : "0b06aaa3-717d-4a52-ab46-295a6571b642", + "clientId" : "realm-management", + "name" : "${client_realm-management}", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : true, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : false, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "realm_client" : "true" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "basic", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "organization", "microprofile-jwt" ] + }, { + "id" : "dfc7248c-3794-4e3b-aed2-3ee553cd0feb", + "clientId" : "security-admin-console", + "name" : "${client_security-admin-console}", + "rootUrl" : "${authAdminUrl}", + "baseUrl" : "/admin/student/console/", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ "/admin/student/console/*" ], + "webOrigins" : [ "+" ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "realm_client" : "false", + "client.use.lightweight.access.token.enabled" : "true", + "post.logout.redirect.uris" : "+", + "pkce.code.challenge.method" : "S256" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : true, + "nodeReRegistrationTimeout" : 0, + "protocolMappers" : [ { + "id" : "9e9ff295-30c9-43f1-a11a-773724709c07", + "name" : "locale", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "locale", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "locale", + "jsonType.label" : "String" + } + } ], + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "basic", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "organization", "microprofile-jwt" ] + } ], + "clientScopes" : [ { + "id" : "0721b27a-284f-4e6d-af70-b6f190ebdcd4", + "name" : "email", + "description" : "OpenID Connect built-in scope: email", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "consent.screen.text" : "${emailScopeConsentText}", + "display.on.consent.screen" : "true" + }, + "protocolMappers" : [ { + "id" : "d256bdc1-8983-41e0-b8fa-fcf45653045e", + "name" : "email verified", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "emailVerified", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "email_verified", + "jsonType.label" : "boolean" + } + }, { + "id" : "651c2415-db30-40ed-bdef-745b6ea744ed", + "name" : "email", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "email", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "email", + "jsonType.label" : "String" + } + } ] + }, { + "id" : "573f6eea-7626-44fe-9855-50f15c3939ba", + "name" : "web-origins", + "description" : "OpenID Connect scope for add allowed web origins to the access token", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "consent.screen.text" : "", + "display.on.consent.screen" : "false" + }, + "protocolMappers" : [ { + "id" : "3489c748-3cc7-4350-9351-2955fc7084ba", + "name" : "allowed web origins", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-allowed-origins-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "access.token.claim" : "true" + } + } ] + }, { + "id" : "00afe548-c677-4595-8478-16f752c2713a", + "name" : "offline_access", + "description" : "OpenID Connect built-in scope: offline_access", + "protocol" : "openid-connect", + "attributes" : { + "consent.screen.text" : "${offlineAccessScopeConsentText}", + "display.on.consent.screen" : "true" + } + }, { + "id" : "1448ed2b-ec1d-4bf4-a8b7-00cb85459289", + "name" : "address", + "description" : "OpenID Connect built-in scope: address", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "consent.screen.text" : "${addressScopeConsentText}", + "display.on.consent.screen" : "true" + }, + "protocolMappers" : [ { + "id" : "12d491b6-5d74-4168-ac5c-517ebc2f1de4", + "name" : "address", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-address-mapper", + "consentRequired" : false, + "config" : { + "user.attribute.formatted" : "formatted", + "user.attribute.country" : "country", + "introspection.token.claim" : "true", + "user.attribute.postal_code" : "postal_code", + "userinfo.token.claim" : "true", + "user.attribute.street" : "street", + "id.token.claim" : "true", + "user.attribute.region" : "region", + "access.token.claim" : "true", + "user.attribute.locality" : "locality" + } + } ] + }, { + "id" : "52223fb1-9651-4cdf-8317-a1301d4042f7", + "name" : "organization", + "description" : "Additional claims about the organization a subject belongs to", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "consent.screen.text" : "${organizationScopeConsentText}", + "display.on.consent.screen" : "true" + }, + "protocolMappers" : [ { + "id" : "dccc4214-ece6-4235-8119-ee8cb954c29a", + "name" : "organization", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-organization-membership-mapper", + "consentRequired" : false, + "config" : { + "id.token.claim" : "true", + "introspection.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "organization", + "jsonType.label" : "String", + "multivalued" : "true" + } + } ] + }, { + "id" : "8be22542-e327-4a25-8265-a34a29607d1b", + "name" : "service_account", + "description" : "Specific scope for a client enabled for service accounts", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "display.on.consent.screen" : "false" + }, + "protocolMappers" : [ { + "id" : "cf89064a-0af3-4a4b-a838-3528a8f4d780", + "name" : "Client IP Address", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "clientAddress", + "id.token.claim" : "true", + "introspection.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "clientAddress", + "jsonType.label" : "String" + } + }, { + "id" : "dc0f77e6-cc20-4c0a-baf3-f45046d749d1", + "name" : "Client ID", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "client_id", + "id.token.claim" : "true", + "introspection.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "client_id", + "jsonType.label" : "String" + } + }, { + "id" : "d63fd29a-3613-4529-a8e4-3a7d7e9f5802", + "name" : "Client Host", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "clientHost", + "id.token.claim" : "true", + "introspection.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "clientHost", + "jsonType.label" : "String" + } + } ] + }, { + "id" : "d9079603-62b7-4680-9d01-950daae75d6b", + "name" : "saml_organization", + "description" : "Organization Membership", + "protocol" : "saml", + "attributes" : { + "display.on.consent.screen" : "false" + }, + "protocolMappers" : [ { + "id" : "d826fc58-b006-49ad-93dc-a76700e800df", + "name" : "organization", + "protocol" : "saml", + "protocolMapper" : "saml-organization-membership-mapper", + "consentRequired" : false, + "config" : { } + } ] + }, { + "id" : "171d8267-87da-4a4b-9346-d901d470248b", + "name" : "phone", + "description" : "OpenID Connect built-in scope: phone", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "consent.screen.text" : "${phoneScopeConsentText}", + "display.on.consent.screen" : "true" + }, + "protocolMappers" : [ { + "id" : "f8bb18d4-af9d-49b0-a61f-cc81887870cd", + "name" : "phone number", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "phoneNumber", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "phone_number", + "jsonType.label" : "String" + } + }, { + "id" : "88a2c658-9b61-40a2-abd5-69c501286031", + "name" : "phone number verified", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "phoneNumberVerified", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "phone_number_verified", + "jsonType.label" : "boolean" + } + } ] + }, { + "id" : "ea3b84ac-a91f-4a3d-be4e-893e11eaf4a1", + "name" : "acr", + "description" : "OpenID Connect scope for add acr (authentication context class reference) to the token", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "display.on.consent.screen" : "false" + }, + "protocolMappers" : [ { + "id" : "152d66d4-524f-47f1-a592-be3a0c043a4f", + "name" : "acr loa level", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-acr-mapper", + "consentRequired" : false, + "config" : { + "id.token.claim" : "true", + "introspection.token.claim" : "true", + "access.token.claim" : "true" + } + } ] + }, { + "id" : "2fc1ad0d-1065-4196-8d1b-c61525c9425d", + "name" : "microprofile-jwt", + "description" : "Microprofile - JWT built-in scope", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "false" + }, + "protocolMappers" : [ { + "id" : "9d537486-f6bf-4856-91fc-ca3acaa78814", + "name" : "upn", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "username", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "upn", + "jsonType.label" : "String" + } + }, { + "id" : "55425438-4111-47a0-9a36-fec9dbbc6a8a", + "name" : "groups", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-realm-role-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "multivalued" : "true", + "user.attribute" : "foo", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "groups", + "jsonType.label" : "String" + } + } ] + }, { + "id" : "0d186f4e-ef6d-4fbc-9593-081e0d5ad171", + "name" : "profile", + "description" : "OpenID Connect built-in scope: profile", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "consent.screen.text" : "${profileScopeConsentText}", + "display.on.consent.screen" : "true" + }, + "protocolMappers" : [ { + "id" : "bb8bb550-2db6-4631-97dc-1d115d0e3034", + "name" : "given name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "firstName", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "given_name", + "jsonType.label" : "String" + } + }, { + "id" : "c942089b-2898-4052-a64d-85b61e27aaa4", + "name" : "username", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "username", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "preferred_username", + "jsonType.label" : "String" + } + }, { + "id" : "5ff3a9ca-7036-458c-b0dc-41216292d210", + "name" : "updated at", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "updatedAt", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "updated_at", + "jsonType.label" : "long" + } + }, { + "id" : "41f93d62-4074-4373-a270-9bdf1e298cb5", + "name" : "website", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "website", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "website", + "jsonType.label" : "String" + } + }, { + "id" : "ffec7d63-0f78-41ea-8023-6c7c64661b34", + "name" : "locale", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "locale", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "locale", + "jsonType.label" : "String" + } + }, { + "id" : "4a514ae7-d29f-4979-8df9-a97b36a81a96", + "name" : "profile", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "profile", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "profile", + "jsonType.label" : "String" + } + }, { + "id" : "286e349b-cb9f-41b1-b9dc-d787f13e9d99", + "name" : "nickname", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "nickname", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "nickname", + "jsonType.label" : "String" + } + }, { + "id" : "f5177603-55b1-4abe-aee6-b1e5a05e37f6", + "name" : "full name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-full-name-mapper", + "consentRequired" : false, + "config" : { + "id.token.claim" : "true", + "introspection.token.claim" : "true", + "access.token.claim" : "true", + "userinfo.token.claim" : "true" + } + }, { + "id" : "a31114d7-05fc-40c1-9ea8-6977f6f0bec5", + "name" : "zoneinfo", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "zoneinfo", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "zoneinfo", + "jsonType.label" : "String" + } + }, { + "id" : "8884be77-648d-4083-b0cf-57130162c8dc", + "name" : "gender", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "gender", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "gender", + "jsonType.label" : "String" + } + }, { + "id" : "61840434-c79f-455a-a914-117977197304", + "name" : "family name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "lastName", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "family_name", + "jsonType.label" : "String" + } + }, { + "id" : "1f40ff0b-1664-4259-846b-ab707c76d33b", + "name" : "middle name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "middleName", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "middle_name", + "jsonType.label" : "String" + } + }, { + "id" : "8534d400-8a81-4ae3-b51f-78b93e5a2045", + "name" : "picture", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "picture", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "picture", + "jsonType.label" : "String" + } + }, { + "id" : "82a0e240-0824-41b9-b6e8-856a72d1e930", + "name" : "birthdate", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "birthdate", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "birthdate", + "jsonType.label" : "String" + } + } ] + }, { + "id" : "a5cedc85-d9e9-42e1-9ea3-ff37d21d5e27", + "name" : "role_list", + "description" : "SAML role list", + "protocol" : "saml", + "attributes" : { + "consent.screen.text" : "${samlRoleListScopeConsentText}", + "display.on.consent.screen" : "true" + }, + "protocolMappers" : [ { + "id" : "19009128-590f-4bc9-80de-c9ba4aae822d", + "name" : "role list", + "protocol" : "saml", + "protocolMapper" : "saml-role-list-mapper", + "consentRequired" : false, + "config" : { + "single" : "false", + "attribute.nameformat" : "Basic", + "attribute.name" : "Role" + } + } ] + }, { + "id" : "3b6bb88b-c833-4bb5-9bd0-95831aa2ad0d", + "name" : "basic", + "description" : "OpenID Connect scope for add all basic claims to the token", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "display.on.consent.screen" : "false" + }, + "protocolMappers" : [ { + "id" : "ce925803-aec2-47cb-a3b9-4bef12c80367", + "name" : "sub", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-sub-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "access.token.claim" : "true" + } + }, { + "id" : "58729b3a-3816-460e-bf2e-d0d2206c1830", + "name" : "auth_time", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "AUTH_TIME", + "id.token.claim" : "true", + "introspection.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "auth_time", + "jsonType.label" : "long" + } + } ] + }, { + "id" : "7aa2d936-3edb-45e5-bae0-b4a618d06371", + "name" : "roles", + "description" : "OpenID Connect scope for add user roles to the access token", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "consent.screen.text" : "${rolesScopeConsentText}", + "display.on.consent.screen" : "true" + }, + "protocolMappers" : [ { + "id" : "a9d1e8e2-ca10-4904-8a42-7708b0bfdefa", + "name" : "client roles", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-client-role-mapper", + "consentRequired" : false, + "config" : { + "user.attribute" : "foo", + "introspection.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "resource_access.${client_id}.roles", + "jsonType.label" : "String", + "multivalued" : "true" + } + }, { + "id" : "1f217073-ad43-483b-b0d5-f3ca4c74282f", + "name" : "realm roles", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-realm-role-mapper", + "consentRequired" : false, + "config" : { + "user.attribute" : "foo", + "introspection.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "realm_access.roles", + "jsonType.label" : "String", + "multivalued" : "true" + } + }, { + "id" : "61b0a069-8b67-4692-bcca-66a197b230eb", + "name" : "audience resolve", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-audience-resolve-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "access.token.claim" : "true" + } + } ] + } ], + "defaultDefaultClientScopes" : [ "role_list", "saml_organization", "profile", "email", "roles", "web-origins", "acr", "basic" ], + "defaultOptionalClientScopes" : [ "offline_access", "address", "phone", "microprofile-jwt", "organization" ], + "browserSecurityHeaders" : { + "contentSecurityPolicyReportOnly" : "", + "xContentTypeOptions" : "nosniff", + "referrerPolicy" : "no-referrer", + "xRobotsTag" : "none", + "xFrameOptions" : "SAMEORIGIN", + "contentSecurityPolicy" : "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "xXSSProtection" : "1; mode=block", + "strictTransportSecurity" : "max-age=31536000; includeSubDomains" + }, + "smtpServer" : { }, + "eventsEnabled" : false, + "eventsListeners" : [ "jboss-logging" ], + "enabledEventTypes" : [ ], + "adminEventsEnabled" : false, + "adminEventsDetailsEnabled" : false, + "identityProviders" : [ ], + "identityProviderMappers" : [ ], + "components" : { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy" : [ { + "id" : "9eac5531-7f25-493f-a721-6c5e65cd34c2", + "name" : "Max Clients Limit", + "providerId" : "max-clients", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "max-clients" : [ "200" ] + } + }, { + "id" : "d9319a22-4c67-4b08-822f-4162a1ee01bc", + "name" : "Allowed Client Scopes", + "providerId" : "allowed-client-templates", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "allow-default-scopes" : [ "true" ] + } + }, { + "id" : "21456c8e-7f6b-4e49-a3e1-bea7f900e2fb", + "name" : "Consent Required", + "providerId" : "consent-required", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { } + }, { + "id" : "4872e99b-b55b-4e13-8a93-63e853289cac", + "name" : "Full Scope Disabled", + "providerId" : "scope", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { } + }, { + "id" : "a118a194-09f5-435d-9d4b-363813413167", + "name" : "Trusted Hosts", + "providerId" : "trusted-hosts", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "host-sending-registration-request-must-match" : [ "true" ], + "client-uris-must-match" : [ "true" ] + } + }, { + "id" : "e32b1e26-6571-4b0c-a205-0fbb3de44384", + "name" : "Allowed Client Scopes", + "providerId" : "allowed-client-templates", + "subType" : "authenticated", + "subComponents" : { }, + "config" : { + "allow-default-scopes" : [ "true" ] + } + }, { + "id" : "9dbe6752-9978-42a3-9210-9ec166140de2", + "name" : "Allowed Protocol Mapper Types", + "providerId" : "allowed-protocol-mappers", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "allowed-protocol-mapper-types" : [ "oidc-usermodel-attribute-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-address-mapper", "saml-role-list-mapper", "saml-user-property-mapper", "oidc-usermodel-property-mapper", "oidc-full-name-mapper", "saml-user-attribute-mapper" ] + } + }, { + "id" : "7027b3f4-d877-4814-ac78-f1edb8eb89b0", + "name" : "Allowed Protocol Mapper Types", + "providerId" : "allowed-protocol-mappers", + "subType" : "authenticated", + "subComponents" : { }, + "config" : { + "allowed-protocol-mapper-types" : [ "saml-role-list-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-address-mapper", "saml-user-property-mapper", "oidc-usermodel-attribute-mapper", "saml-user-attribute-mapper", "oidc-usermodel-property-mapper", "oidc-full-name-mapper" ] + } + } ], + "org.keycloak.keys.KeyProvider" : [ { + "id" : "819cfc66-a997-4747-9d90-a7f0c09774bf", + "name" : "aes-generated", + "providerId" : "aes-generated", + "subComponents" : { }, + "config" : { + "kid" : [ "eb74df73-3f34-457d-95c7-5ad909107703" ], + "secret" : [ "1K8IJiDODmotHJPStrXhtA" ], + "priority" : [ "100" ] + } + }, { + "id" : "299857cd-52a4-4981-8171-02e7d8f12960", + "name" : "rsa-generated", + "providerId" : "rsa-generated", + "subComponents" : { }, + "config" : { + "privateKey" : [ "MIIEpAIBAAKCAQEA1MRmAT/yImkVfPMBxC0QHdC4DQfuWUTjKeEku+gMI9jX5ChUzzzVugcvZWmxBNcvOz7p6R8EdPllZKIwFSH5WvQ1w1VIgQwIlYfpi/pknfftLd66MI2fXrQK19dRTeQRivEf39GTfBQ2Xc7y1q7zbMo5TVxATJ3DgPi13dYO7zVPpGTiQQeYiezlcBedyGe4cS1g6oBoaVif1QPY1Ni2vEjJhczNMGI408tIFws8G04Tlno814nT0ysdflUSGcRUku41NtfM9hr57LQ459sGYho8Pn11lDuiUWkomJv0y3GJ1wFBvQbDvI+6QvEdFu0GxShrlcORrNmj3BwOOLhB7wIDAQABAoIBAA6zrXq7oO8YxMfYANC97mWpBPa9jA42EN5VdNTZIXGeq7hTwxx4zynmEjPXPEih190nqUEXCBdPHl74SAtFyDWtN0PSkkp8euFePViTSj2SIpzvTX1KY+9G0JL+iVsw/bdUlwe/swm5WdJcmPIVr7NeO9xpGfZRVm+EgAieoHSN4Z7g20wLbVz1fya+6O5Hy+IGezamIA4tchk+4hyiVpSh2TcdjkJJZWOlHKPkwWU/MYQbJibuea5jLoWA39NIqV2l5GT0SoCbffGJNb9CMTTGmXoK5zNwHhG+M0a4eP1vbFDLaoDne86JySmTdv/WrTFFa3veelw2K8PHDybuB70CgYEA/gbxqLZYkJcEpqsjM/XcISFJ09icJLKl5r2l/Dm4Qq587QniQYribX/PPLfDhgVwPByQe3rccq9FoiILycTdIwgSMTsg5fzvbLJTqMAcl2r0zJgHVIDc6iXnytuE0FffKN0kSKL1C4d6n6vKoCGvOcZoXK5jxgzpY8lasvKxhCsCgYEA1mtr7CDYY3qPmTu4/Uz6cFgX8RDMZZ11AQQXNMsKHIu5C4xLeYmJMlpt0y4h52/NWRzh2svdw3SEZTCfP1WVC7StfP8KD8QdwVkQlY5EGkiz9uRtEgwk8chkOTIm2JedeRL6YWlTgnH9PIuGq84OOnEbFjVN3Lbx3N1QuQfVA00CgYEAybA1uuBcXSCqfrIuVxkD2AIYHe1DvBdjhVpaKXKii78CTSmlzKg6svnhTrIQuZ4jyHZdeMzJrvzeaqZheaemdCP6XcA2lKRIbKMBrWAq00YGa1LhrwRJYlcKPJQiVVEPS+CY6FsJ+Edu4suBK7bS6ypOvhdv/FVQEPxT2PS8YNUCgYEAxwJ+8XNuw63ud9+Zi+gVjY4F8qWPwESLYz0DuOk2YlZAknpNVumTYBvUUSxBJYh8RFhtO+D53D5Z331oYKUzJ+EzII+qLAXvRBRBMz4O8YJHHkDXBugkphBDDV8B9QeLjeNSZnUWoDziOH6bqPwf8pgl9s/Ui6V1CHSVRpcBWwUCgYA2kMgu7qS5kLtUWySPzW4nTKwhN+HFTIbRrNrECxXmxroigTEyfBFuNR5QaeYYrAtqgY1m5Lev//2GnWM7dAr7hewj6qfGszrvegHsqMs4cakVqEOtbrWxL+WtWPaIdjJ+x7ZoMnZxZDg3ysemybNHHwSyBsp1TDc+glzmMtJtLA==" ], + "keyUse" : [ "SIG" ], + "certificate" : [ "MIICnTCCAYUCBgGVUbFIeTANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdzdHVkZW50MB4XDTI1MDMwMTEyMzAyN1oXDTM1MDMwMTEyMzIwN1owEjEQMA4GA1UEAwwHc3R1ZGVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANTEZgE/8iJpFXzzAcQtEB3QuA0H7llE4ynhJLvoDCPY1+QoVM881boHL2VpsQTXLzs+6ekfBHT5ZWSiMBUh+Vr0NcNVSIEMCJWH6Yv6ZJ337S3eujCNn160CtfXUU3kEYrxH9/Rk3wUNl3O8tau82zKOU1cQEydw4D4td3WDu81T6Rk4kEHmIns5XAXnchnuHEtYOqAaGlYn9UD2NTYtrxIyYXMzTBiONPLSBcLPBtOE5Z6PNeJ09MrHX5VEhnEVJLuNTbXzPYa+ey0OOfbBmIaPD59dZQ7olFpKJib9MtxidcBQb0Gw7yPukLxHRbtBsUoa5XDkazZo9wcDji4Qe8CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAedqvKOBsz4IKKzkWHIQnN5H8dQKnuPUMdWewOwmMGIUdBU9k6aS+y+BB7mugF/Hnr8Lw5d2AHwVLj2VyP4Pq0d2My3Ihxi0vr6sSfxVHuD9y/a7FxDGVTkCvmy5DOmpF/kdNnL9xG5ZivHaucnrIHHGMcQCdbWAaac0qPZihv9pdMZFMtI3aiBO5jVJ7KP8iLNKsshg60mxCOPzauMVXi+rqqqhGAgMKAL4hjjvdIKTLWwmthnmAlGqlTk/7H82hS9aKygufXszXWdFAYhX/r8/hjyc+6zJUvkG20uRWnkR35gya7jQoZ2O6OvkQf0mgSvzgIP3xoYV2uKYD03wINg==" ], + "priority" : [ "100" ] + } + }, { + "id" : "3d6bfeeb-fa86-435e-8c39-6f547a0f4a38", + "name" : "hmac-generated-hs512", + "providerId" : "hmac-generated", + "subComponents" : { }, + "config" : { + "kid" : [ "176e970f-5915-4d27-8233-8fab6d7ad947" ], + "secret" : [ "sXeOdtyIPpH_kcZWikHFjTur9yWok0QUwKi95l8wHp6kTVX9vhoZL2siNHRoFnn8tFgT4JZbR0bMsD57qAXlmVjA830Ny_GZdhL_PFWQh7JYMEJrl-1nyLy_SReQXRtq_q9tKUafUZqeYSKBlUYZ7D4jNRJ4-uniq80Ger-4ee0" ], + "priority" : [ "100" ], + "algorithm" : [ "HS512" ] + } + }, { + "id" : "df1247b5-041e-4ae8-b7fc-26c4b6f5ff67", + "name" : "rsa-enc-generated", + "providerId" : "rsa-enc-generated", + "subComponents" : { }, + "config" : { + "privateKey" : [ "MIIEpAIBAAKCAQEAq0piY/PaZh1IX46e0G6tCPtfRx7Q8zGslOFSBLR9PNAdSlBpYiV9kOpN1kTK2Sca5j4yyO9HAFK5j+zh/cy8SsP1iyuI0sCPG7NMKV1pP5Y753wTC0lTq8z16bvXPvPYrPRKgGDmU7Ww0/WD6/P+z9Li/+ujFHzzzfoPuQvbBhma6A4oadsC+zun/mWCyiD14mB+X00BeAIsxKJZ/Sd+U4lMkkvmpoXyx61xK+j48EAZ18u7FprlvUjgGzzAmm/K6O/fHPIw5eViVly7aj5gjh67ntSuZArVtrxy/Py5V4hkSO8guXKqNz3liJvLbFCqqpfTR/0duArZR0xcnaGc6wIDAQABAoIBAFRuu5YaXxbDq2eS5RzH2Vpakin7+jJOU4wljujL0QnXagC2J2QeJ8l1fT23tieZO4yvrxfVvnFd1aMouHMC5vORqWja4jxEd6ZHWKzxIw6ZbtjZk4eWMvy18KewlFavGyiR2GF0okQ0BMBOPqNhp8JoaMWOsNnKB+GJuBNWUTWtPWNQlbaeI+uIgFywrvZOcQuWqU+9Y5rQa7oKZisufu+z0vd9XyvjXQ/Thnuu9/k1m88EMAMS63zwfIOZm35PPqh1/6aBBcRWquT1X2S8g2hwmMLZJgU91yKtQcIujHXAcvxeK72/dcm8NU6AxM+8aj+821TvcNzJi7he5SGcin0CgYEA4cuxwqRixXz6yDcTv9GpJMULbJGjA+Qf/iSfT+ftBeKbnKgZGzHwOCTu5DMkag3BPjclut4sEt3QPf1cFv5vZvdkOnPeaFxrtoMhSz8ssh8qaOsObCwicel1zdPVTmMw7YzEZV14fdIq3lkHsLy2uWa0imRH0l4xTccmsJiPtU8CgYEAwjQtspxOejCyME+M+hcU3RelD6kaMjICuWGJj8g0OpqdHM7iNVJq78fOlWjntt/ydzfOXVMMVh4AG8dAvlc86iwwsBRsJPVrrrRoSNuAwFbjKisbjlnPbqyclHfUsyQitj19tp//ExH7JaBibzKd6KhqFuQTE1iYLs2mFQAz76UCgYEAsNLu64oGm7frQP345mAPgO8aqjRHIBX3g/Q0GsR61wAGcyElQCnUgHNT7burSa5p5goT7wpsI343xUPzaUJqBY25nRj+VGYEKFL6sM3Rd9B2SuHBUq8hbmmwyraYtiFxwKZbazJO2OHMloHMRvkSc5Dd0/8CS9ld7RYH04Y2DHsCgYEAoKXTK44baP7BWC9mOjc/vgjiNQs4rU8ra7igt7zwX44o63zEKUHNTh7l6DiIfYHRrAcRAahCazaT9makSxAVRs1ZVT7/mq8d7b41Chfx8KmvbuGMAPyQGEhXmoVqAOqigEhrptfBhD/6lkyPQNcJQz2VzOvMT9OYyBa8DWFGlTUCgYAfModz6g2HsYYr37/0ByXHKL0WQQtuAlZzCY9GuDLEok7QLFI/E+bdOHos3goW72Iswo/SO7inlW4S3gojuy+zZhwCO31T9p2Z0Yn0tDK3fkUO32flOLwxCZA99pKkIul3svl6643GqSD1feybmbYRtqoPCTSKSE9vI9T9DkBTvA==" ], + "keyUse" : [ "ENC" ], + "certificate" : [ "MIICnTCCAYUCBgGVUbFItzANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdzdHVkZW50MB4XDTI1MDMwMTEyMzAyN1oXDTM1MDMwMTEyMzIwN1owEjEQMA4GA1UEAwwHc3R1ZGVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKtKYmPz2mYdSF+OntBurQj7X0ce0PMxrJThUgS0fTzQHUpQaWIlfZDqTdZEytknGuY+MsjvRwBSuY/s4f3MvErD9YsriNLAjxuzTCldaT+WO+d8EwtJU6vM9em71z7z2Kz0SoBg5lO1sNP1g+vz/s/S4v/roxR88836D7kL2wYZmugOKGnbAvs7p/5lgsog9eJgfl9NAXgCLMSiWf0nflOJTJJL5qaF8setcSvo+PBAGdfLuxaa5b1I4Bs8wJpvyujv3xzyMOXlYlZcu2o+YI4eu57UrmQK1ba8cvz8uVeIZEjvILlyqjc95Yiby2xQqqqX00f9HbgK2UdMXJ2hnOsCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAHfum0Ymw/qHTpjNeJjJyh5A1N4Z58m/PjuCXafIdDDjuAYpB3/M4bTGZRUvEvv2RuBNv3rONvMR8dRrZwio0/T0aEXnHrEAaCSfVcMy1To8TGGOzgtPMub4YCqXLMCwW5cwIbqT3P58HEqsqEbv7Zp4LtLYZBYXWDF8vM4zEn3CPYxuxRPKlrBUynRKYcwN7+/dbhJKiARpPMIZ5viGbjaTnNE/d/VFdv1q5xm3ItYnShDyJ0REGN18sWleLI6qkW0X22Gcjn38fWjiXDnF0HQYzC2UzMcEo/iLfPxTKbJnc+PPmnszfmCh7mWs5xVGfMOz/Oy8HI121x1ZSriRktA==" ], + "priority" : [ "100" ], + "algorithm" : [ "RSA-OAEP" ] + } + } ] + }, + "internationalizationEnabled" : false, + "supportedLocales" : [ ], + "authenticationFlows" : [ { + "id" : "f7d1108f-7994-47e5-81e9-1a88cdbe545c", + "alias" : "Account verification options", + "description" : "Method with which to verity the existing account", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "idp-email-verification", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "ALTERNATIVE", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "Verify Existing Account by Re-authentication", + "userSetupAllowed" : false + } ] + }, { + "id" : "cf40a5d3-bec8-4aef-9658-1b88c6cec561", + "alias" : "Browser - Conditional OTP", + "description" : "Flow to determine if the OTP is required for the authentication", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "auth-otp-form", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "6820625f-5bb5-4fa2-8539-26a8568265c1", + "alias" : "Browser - Conditional Organization", + "description" : "Flow to determine if the organization identity-first login is to be used", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "organization", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "66d5e52e-592e-4cef-bfa0-512e90b609ec", + "alias" : "Direct Grant - Conditional OTP", + "description" : "Flow to determine if the OTP is required for the authentication", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "direct-grant-validate-otp", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "b5bed405-b5f2-4839-861c-612501e4c412", + "alias" : "First Broker Login - Conditional Organization", + "description" : "Flow to determine if the authenticator that adds organization members is to be used", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "idp-add-organization-member", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "dd786e24-e822-43ec-be03-29874eb73737", + "alias" : "First broker login - Conditional OTP", + "description" : "Flow to determine if the OTP is required for the authentication", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "auth-otp-form", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "8751572f-623e-4bdc-a02c-e92c15a91143", + "alias" : "Handle Existing Account", + "description" : "Handle what to do if there is existing account with same email/username like authenticated identity provider", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "idp-confirm-link", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "Account verification options", + "userSetupAllowed" : false + } ] + }, { + "id" : "61efadf2-a54e-4071-b8c9-83e094525051", + "alias" : "Organization", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 10, + "autheticatorFlow" : true, + "flowAlias" : "Browser - Conditional Organization", + "userSetupAllowed" : false + } ] + }, { + "id" : "b99c3a7a-8ef7-46b1-b8a1-cb51f8a6e725", + "alias" : "Reset - Conditional OTP", + "description" : "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "reset-otp", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "a3bfc2e4-af67-4d3e-851f-3c58bf32be83", + "alias" : "User creation or linking", + "description" : "Flow for the existing/non-existing user alternatives", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticatorConfig" : "create unique user config", + "authenticator" : "idp-create-user-if-unique", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "ALTERNATIVE", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "Handle Existing Account", + "userSetupAllowed" : false + } ] + }, { + "id" : "4cc3bf25-d1b7-43a6-8619-5ed5f2d65aed", + "alias" : "Verify Existing Account by Re-authentication", + "description" : "Reauthentication of existing account", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "idp-username-password-form", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "First broker login - Conditional OTP", + "userSetupAllowed" : false + } ] + }, { + "id" : "4e5564ce-87da-4b25-8dcb-062216ceaa8d", + "alias" : "browser", + "description" : "Browser based authentication", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "auth-cookie", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "auth-spnego", + "authenticatorFlow" : false, + "requirement" : "DISABLED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "identity-provider-redirector", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 25, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "ALTERNATIVE", + "priority" : 26, + "autheticatorFlow" : true, + "flowAlias" : "Organization", + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "ALTERNATIVE", + "priority" : 30, + "autheticatorFlow" : true, + "flowAlias" : "forms", + "userSetupAllowed" : false + } ] + }, { + "id" : "def90462-5831-4856-b186-05df9e640bbb", + "alias" : "clients", + "description" : "Base authentication for clients", + "providerId" : "client-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "client-secret", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "client-jwt", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "client-secret-jwt", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 30, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "client-x509", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 40, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "f8c9010d-f197-417b-bda1-2993e1a73a21", + "alias" : "direct grant", + "description" : "OpenID Connect Resource Owner Grant", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "direct-grant-validate-username", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "direct-grant-validate-password", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 30, + "autheticatorFlow" : true, + "flowAlias" : "Direct Grant - Conditional OTP", + "userSetupAllowed" : false + } ] + }, { + "id" : "0fb9e2a4-ea0d-453f-a1fe-f000c849fd66", + "alias" : "docker auth", + "description" : "Used by Docker clients to authenticate against the IDP", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "docker-http-basic-authenticator", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "79a9efc4-1279-4093-8914-92f4e0b02bb4", + "alias" : "first broker login", + "description" : "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticatorConfig" : "review profile config", + "authenticator" : "idp-review-profile", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "User creation or linking", + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 50, + "autheticatorFlow" : true, + "flowAlias" : "First Broker Login - Conditional Organization", + "userSetupAllowed" : false + } ] + }, { + "id" : "f855b3a1-6612-4528-94bc-d0793bfda561", + "alias" : "forms", + "description" : "Username, password, otp and other auth forms.", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "auth-username-password-form", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "Browser - Conditional OTP", + "userSetupAllowed" : false + } ] + }, { + "id" : "fb84970b-6f04-4849-a385-792e17c1b8ce", + "alias" : "registration", + "description" : "Registration flow", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "registration-page-form", + "authenticatorFlow" : true, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : true, + "flowAlias" : "registration form", + "userSetupAllowed" : false + } ] + }, { + "id" : "fcdfd4d4-1c04-487d-aa7c-85e136814274", + "alias" : "registration form", + "description" : "Registration form", + "providerId" : "form-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "registration-user-creation", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "registration-password-action", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 50, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "registration-recaptcha-action", + "authenticatorFlow" : false, + "requirement" : "DISABLED", + "priority" : 60, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "registration-terms-and-conditions", + "authenticatorFlow" : false, + "requirement" : "DISABLED", + "priority" : 70, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "306d8f7d-c12a-46cb-9a68-c6c3f1622f57", + "alias" : "reset credentials", + "description" : "Reset credentials for a user if they forgot their password or something", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "reset-credentials-choose-user", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "reset-credential-email", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "reset-password", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 30, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 40, + "autheticatorFlow" : true, + "flowAlias" : "Reset - Conditional OTP", + "userSetupAllowed" : false + } ] + }, { + "id" : "35a54b09-ff8c-46c4-9f04-1efbb153276c", + "alias" : "saml ecp", + "description" : "SAML ECP Profile Authentication Flow", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "http-basic-authenticator", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + } ], + "authenticatorConfig" : [ { + "id" : "fc1b82d7-593d-4906-a4d9-13220b66b7ce", + "alias" : "create unique user config", + "config" : { + "require.password.update.after.registration" : "false" + } + }, { + "id" : "a90543f4-7da7-43bc-8737-7e58dd190014", + "alias" : "review profile config", + "config" : { + "update.profile.on.first.login" : "missing" + } + } ], + "requiredActions" : [ { + "alias" : "CONFIGURE_TOTP", + "name" : "Configure OTP", + "providerId" : "CONFIGURE_TOTP", + "enabled" : true, + "defaultAction" : false, + "priority" : 10, + "config" : { } + }, { + "alias" : "TERMS_AND_CONDITIONS", + "name" : "Terms and Conditions", + "providerId" : "TERMS_AND_CONDITIONS", + "enabled" : false, + "defaultAction" : false, + "priority" : 20, + "config" : { } + }, { + "alias" : "UPDATE_PASSWORD", + "name" : "Update Password", + "providerId" : "UPDATE_PASSWORD", + "enabled" : true, + "defaultAction" : false, + "priority" : 30, + "config" : { } + }, { + "alias" : "UPDATE_PROFILE", + "name" : "Update Profile", + "providerId" : "UPDATE_PROFILE", + "enabled" : true, + "defaultAction" : false, + "priority" : 40, + "config" : { } + }, { + "alias" : "VERIFY_EMAIL", + "name" : "Verify Email", + "providerId" : "VERIFY_EMAIL", + "enabled" : true, + "defaultAction" : false, + "priority" : 50, + "config" : { } + }, { + "alias" : "delete_account", + "name" : "Delete Account", + "providerId" : "delete_account", + "enabled" : false, + "defaultAction" : false, + "priority" : 60, + "config" : { } + }, { + "alias" : "webauthn-register", + "name" : "Webauthn Register", + "providerId" : "webauthn-register", + "enabled" : true, + "defaultAction" : false, + "priority" : 70, + "config" : { } + }, { + "alias" : "webauthn-register-passwordless", + "name" : "Webauthn Register Passwordless", + "providerId" : "webauthn-register-passwordless", + "enabled" : true, + "defaultAction" : false, + "priority" : 80, + "config" : { } + }, { + "alias" : "VERIFY_PROFILE", + "name" : "Verify Profile", + "providerId" : "VERIFY_PROFILE", + "enabled" : true, + "defaultAction" : false, + "priority" : 90, + "config" : { } + }, { + "alias" : "delete_credential", + "name" : "Delete Credential", + "providerId" : "delete_credential", + "enabled" : true, + "defaultAction" : false, + "priority" : 100, + "config" : { } + }, { + "alias" : "update_user_locale", + "name" : "Update User Locale", + "providerId" : "update_user_locale", + "enabled" : true, + "defaultAction" : false, + "priority" : 1000, + "config" : { } + } ], + "browserFlow" : "browser", + "registrationFlow" : "registration", + "directGrantFlow" : "direct grant", + "resetCredentialsFlow" : "reset credentials", + "clientAuthenticationFlow" : "clients", + "dockerAuthenticationFlow" : "docker auth", + "firstBrokerLoginFlow" : "first broker login", + "attributes" : { + "cibaBackchannelTokenDeliveryMode" : "poll", + "cibaExpiresIn" : "120", + "cibaAuthRequestedUserHint" : "login_hint", + "oauth2DeviceCodeLifespan" : "600", + "oauth2DevicePollingInterval" : "5", + "parRequestUriLifespan" : "60", + "cibaInterval" : "5", + "realmReusableOtpCode" : "false" + }, + "keycloakVersion" : "26.1.3", + "userManagedAccessAllowed" : false, + "organizationsEnabled" : false, + "verifiableCredentialsEnabled" : false, + "adminPermissionsEnabled" : false, + "clientProfiles" : { + "profiles" : [ ] + }, + "clientPolicies" : { + "policies" : [ ] + } +} diff --git a/idp/teacher-realm.json b/idp/teacher-realm.json new file mode 100644 index 00000000..5584f692 --- /dev/null +++ b/idp/teacher-realm.json @@ -0,0 +1,2060 @@ +{ + "id" : "02ba6887-22f5-4de4-ad9b-cb2a2060bce1", + "realm" : "teacher", + "notBefore" : 0, + "defaultSignatureAlgorithm" : "RS256", + "revokeRefreshToken" : false, + "refreshTokenMaxReuse" : 0, + "accessTokenLifespan" : 300, + "accessTokenLifespanForImplicitFlow" : 900, + "ssoSessionIdleTimeout" : 1800, + "ssoSessionMaxLifespan" : 36000, + "ssoSessionIdleTimeoutRememberMe" : 0, + "ssoSessionMaxLifespanRememberMe" : 0, + "offlineSessionIdleTimeout" : 2592000, + "offlineSessionMaxLifespanEnabled" : false, + "offlineSessionMaxLifespan" : 5184000, + "clientSessionIdleTimeout" : 0, + "clientSessionMaxLifespan" : 0, + "clientOfflineSessionIdleTimeout" : 0, + "clientOfflineSessionMaxLifespan" : 0, + "accessCodeLifespan" : 60, + "accessCodeLifespanUserAction" : 300, + "accessCodeLifespanLogin" : 1800, + "actionTokenGeneratedByAdminLifespan" : 43200, + "actionTokenGeneratedByUserLifespan" : 300, + "oauth2DeviceCodeLifespan" : 600, + "oauth2DevicePollingInterval" : 5, + "enabled" : true, + "sslRequired" : "external", + "registrationAllowed" : false, + "registrationEmailAsUsername" : false, + "rememberMe" : false, + "verifyEmail" : false, + "loginWithEmailAllowed" : true, + "duplicateEmailsAllowed" : false, + "resetPasswordAllowed" : false, + "editUsernameAllowed" : false, + "bruteForceProtected" : false, + "permanentLockout" : false, + "maxTemporaryLockouts" : 0, + "bruteForceStrategy" : "MULTIPLE", + "maxFailureWaitSeconds" : 900, + "minimumQuickLoginWaitSeconds" : 60, + "waitIncrementSeconds" : 60, + "quickLoginCheckMilliSeconds" : 1000, + "maxDeltaTimeSeconds" : 43200, + "failureFactor" : 30, + "roles" : { + "realm" : [ { + "id" : "e7f1e366-0bfc-4469-bcde-92bcd1ed5ce7", + "name" : "uma_authorization", + "description" : "${role_uma_authorization}", + "composite" : false, + "clientRole" : false, + "containerId" : "02ba6887-22f5-4de4-ad9b-cb2a2060bce1", + "attributes" : { } + }, { + "id" : "6b546a34-4ebe-4c09-b274-fc1f6bebdf93", + "name" : "default-roles-teacher", + "description" : "${role_default-roles}", + "composite" : true, + "composites" : { + "realm" : [ "offline_access", "uma_authorization" ], + "client" : { + "account" : [ "manage-account", "view-profile" ] + } + }, + "clientRole" : false, + "containerId" : "02ba6887-22f5-4de4-ad9b-cb2a2060bce1", + "attributes" : { } + }, { + "id" : "747c4433-f128-4f72-b56f-315e7779d4fd", + "name" : "offline_access", + "description" : "${role_offline-access}", + "composite" : false, + "clientRole" : false, + "containerId" : "02ba6887-22f5-4de4-ad9b-cb2a2060bce1", + "attributes" : { } + } ], + "client" : { + "realm-management" : [ { + "id" : "4c8243b1-b576-4cb2-a4f7-3ce25e408fe5", + "name" : "impersonation", + "description" : "${role_impersonation}", + "composite" : false, + "clientRole" : true, + "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes" : { } + }, { + "id" : "71fd672b-024b-4d44-b058-03320aeb1842", + "name" : "view-users", + "description" : "${role_view-users}", + "composite" : true, + "composites" : { + "client" : { + "realm-management" : [ "query-groups", "query-users" ] + } + }, + "clientRole" : true, + "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes" : { } + }, { + "id" : "fea88d42-3065-4600-a5b6-c4e2589e1304", + "name" : "view-events", + "description" : "${role_view-events}", + "composite" : false, + "clientRole" : true, + "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes" : { } + }, { + "id" : "6247b5b0-4d41-4fda-900c-3dfc725e03a2", + "name" : "manage-users", + "description" : "${role_manage-users}", + "composite" : false, + "clientRole" : true, + "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes" : { } + }, { + "id" : "a3b55a4b-b7f9-4db3-a64f-6ddf80bf74e7", + "name" : "view-authorization", + "description" : "${role_view-authorization}", + "composite" : false, + "clientRole" : true, + "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes" : { } + }, { + "id" : "d6714bc8-ff2d-4da0-98b4-2a6479e67954", + "name" : "manage-events", + "description" : "${role_manage-events}", + "composite" : false, + "clientRole" : true, + "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes" : { } + }, { + "id" : "d389da82-1730-4c66-9b43-34ac3c8d7f6c", + "name" : "query-realms", + "description" : "${role_query-realms}", + "composite" : false, + "clientRole" : true, + "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes" : { } + }, { + "id" : "4dc3905f-311b-4de0-b2e6-a3de50a078a3", + "name" : "query-users", + "description" : "${role_query-users}", + "composite" : false, + "clientRole" : true, + "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes" : { } + }, { + "id" : "28ea5d84-4e7d-484e-82fa-c9adcea4ffc0", + "name" : "manage-identity-providers", + "description" : "${role_manage-identity-providers}", + "composite" : false, + "clientRole" : true, + "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes" : { } + }, { + "id" : "e020bc9c-f2c9-4023-82eb-b62266749334", + "name" : "query-clients", + "description" : "${role_query-clients}", + "composite" : false, + "clientRole" : true, + "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes" : { } + }, { + "id" : "e7373af5-924a-4f01-b34d-55a09aac6c74", + "name" : "manage-clients", + "description" : "${role_manage-clients}", + "composite" : false, + "clientRole" : true, + "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes" : { } + }, { + "id" : "0879b6d5-7db6-4c83-8b99-e889028cb13e", + "name" : "manage-realm", + "description" : "${role_manage-realm}", + "composite" : false, + "clientRole" : true, + "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes" : { } + }, { + "id" : "ff2c82f3-7f04-4ced-9127-65097e2c16b9", + "name" : "realm-admin", + "description" : "${role_realm-admin}", + "composite" : true, + "composites" : { + "client" : { + "realm-management" : [ "impersonation", "view-users", "view-events", "manage-users", "view-authorization", "query-users", "query-realms", "manage-events", "manage-identity-providers", "query-clients", "manage-realm", "view-clients", "manage-clients", "query-groups", "create-client", "view-realm", "manage-authorization", "view-identity-providers" ] + } + }, + "clientRole" : true, + "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes" : { } + }, { + "id" : "156a28de-00d8-4828-9dc9-e09e7841312f", + "name" : "view-clients", + "description" : "${role_view-clients}", + "composite" : true, + "composites" : { + "client" : { + "realm-management" : [ "query-clients" ] + } + }, + "clientRole" : true, + "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes" : { } + }, { + "id" : "a241d7dd-b028-474a-bdf8-4d33e00c1b90", + "name" : "create-client", + "description" : "${role_create-client}", + "composite" : false, + "clientRole" : true, + "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes" : { } + }, { + "id" : "681e3f7e-bb8c-4e09-a49e-ba8c21f916ff", + "name" : "query-groups", + "description" : "${role_query-groups}", + "composite" : false, + "clientRole" : true, + "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes" : { } + }, { + "id" : "1c5886ad-b354-4246-b288-13ea7635db58", + "name" : "view-realm", + "description" : "${role_view-realm}", + "composite" : false, + "clientRole" : true, + "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes" : { } + }, { + "id" : "7dedf6ff-b715-4f14-85ac-40d0652f153d", + "name" : "manage-authorization", + "description" : "${role_manage-authorization}", + "composite" : false, + "clientRole" : true, + "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes" : { } + }, { + "id" : "694721e8-3bf3-47b5-ae38-874db0dc7740", + "name" : "view-identity-providers", + "description" : "${role_view-identity-providers}", + "composite" : false, + "clientRole" : true, + "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes" : { } + } ], + "dwengo" : [ ], + "security-admin-console" : [ ], + "admin-cli" : [ ], + "account-console" : [ ], + "broker" : [ { + "id" : "0cb1b2b5-a751-4f09-ac2f-ea26c398a857", + "name" : "read-token", + "description" : "${role_read-token}", + "composite" : false, + "clientRole" : true, + "containerId" : "cfd0202e-a6b9-4c5e-9f49-2ef17df9089b", + "attributes" : { } + } ], + "account" : [ { + "id" : "d21c51c5-353c-4d78-8c8d-8b8e9f37efa8", + "name" : "manage-account-links", + "description" : "${role_manage-account-links}", + "composite" : false, + "clientRole" : true, + "containerId" : "7ceb65eb-30da-4dc3-95bc-f06863362fd6", + "attributes" : { } + }, { + "id" : "49c8ac02-defa-41af-9e63-2fd24cfc103f", + "name" : "view-groups", + "description" : "${role_view-groups}", + "composite" : false, + "clientRole" : true, + "containerId" : "7ceb65eb-30da-4dc3-95bc-f06863362fd6", + "attributes" : { } + }, { + "id" : "3850c5cc-510a-417b-9976-a1d1d6650804", + "name" : "manage-account", + "description" : "${role_manage-account}", + "composite" : true, + "composites" : { + "client" : { + "account" : [ "manage-account-links" ] + } + }, + "clientRole" : true, + "containerId" : "7ceb65eb-30da-4dc3-95bc-f06863362fd6", + "attributes" : { } + }, { + "id" : "6554709e-304a-428f-8665-970aacd1dae8", + "name" : "view-consent", + "description" : "${role_view-consent}", + "composite" : false, + "clientRole" : true, + "containerId" : "7ceb65eb-30da-4dc3-95bc-f06863362fd6", + "attributes" : { } + }, { + "id" : "7a0c9d85-daea-4b80-93b5-095e21e5d569", + "name" : "delete-account", + "description" : "${role_delete-account}", + "composite" : false, + "clientRole" : true, + "containerId" : "7ceb65eb-30da-4dc3-95bc-f06863362fd6", + "attributes" : { } + }, { + "id" : "ee2c5cff-1b05-417f-ab3a-a796be754299", + "name" : "manage-consent", + "description" : "${role_manage-consent}", + "composite" : true, + "composites" : { + "client" : { + "account" : [ "view-consent" ] + } + }, + "clientRole" : true, + "containerId" : "7ceb65eb-30da-4dc3-95bc-f06863362fd6", + "attributes" : { } + }, { + "id" : "128fb31d-0784-4b4e-9aa5-82ceb2824fa0", + "name" : "view-profile", + "description" : "${role_view-profile}", + "composite" : false, + "clientRole" : true, + "containerId" : "7ceb65eb-30da-4dc3-95bc-f06863362fd6", + "attributes" : { } + }, { + "id" : "ca850b8d-b75b-4b04-9e42-1e4cc8ab2179", + "name" : "view-applications", + "description" : "${role_view-applications}", + "composite" : false, + "clientRole" : true, + "containerId" : "7ceb65eb-30da-4dc3-95bc-f06863362fd6", + "attributes" : { } + } ] + } + }, + "groups" : [ ], + "defaultRole" : { + "id" : "6b546a34-4ebe-4c09-b274-fc1f6bebdf93", + "name" : "default-roles-teacher", + "description" : "${role_default-roles}", + "composite" : true, + "clientRole" : false, + "containerId" : "02ba6887-22f5-4de4-ad9b-cb2a2060bce1" + }, + "requiredCredentials" : [ "password" ], + "otpPolicyType" : "totp", + "otpPolicyAlgorithm" : "HmacSHA1", + "otpPolicyInitialCounter" : 0, + "otpPolicyDigits" : 6, + "otpPolicyLookAheadWindow" : 1, + "otpPolicyPeriod" : 30, + "otpPolicyCodeReusable" : false, + "otpSupportedApplications" : [ "totpAppFreeOTPName", "totpAppGoogleName", "totpAppMicrosoftAuthenticatorName" ], + "localizationTexts" : { }, + "webAuthnPolicyRpEntityName" : "keycloak", + "webAuthnPolicySignatureAlgorithms" : [ "ES256", "RS256" ], + "webAuthnPolicyRpId" : "", + "webAuthnPolicyAttestationConveyancePreference" : "not specified", + "webAuthnPolicyAuthenticatorAttachment" : "not specified", + "webAuthnPolicyRequireResidentKey" : "not specified", + "webAuthnPolicyUserVerificationRequirement" : "not specified", + "webAuthnPolicyCreateTimeout" : 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister" : false, + "webAuthnPolicyAcceptableAaguids" : [ ], + "webAuthnPolicyExtraOrigins" : [ ], + "webAuthnPolicyPasswordlessRpEntityName" : "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms" : [ "ES256", "RS256" ], + "webAuthnPolicyPasswordlessRpId" : "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference" : "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment" : "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey" : "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement" : "not specified", + "webAuthnPolicyPasswordlessCreateTimeout" : 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister" : false, + "webAuthnPolicyPasswordlessAcceptableAaguids" : [ ], + "webAuthnPolicyPasswordlessExtraOrigins" : [ ], + "users" : [ { + "id" : "63dbbb64-c09f-4e4e-9cbf-af9e557dbb09", + "username" : "testleerkracht1", + "firstName" : "Kris", + "lastName" : "Coolsaet", + "email" : "kris.coolsaet@ugent.be", + "emailVerified" : false, + "createdTimestamp" : 1740866530658, + "enabled" : true, + "totp" : false, + "credentials" : [ { + "id" : "c5382bf7-ccc6-47de-93b9-2c11ea7b6862", + "type" : "password", + "userLabel" : "My password", + "createdDate" : 1740866544032, + "secretData" : "{\"value\":\"H2vKyHF3j/alz6CNap2uaKSRb+/wrWImVecj7dcHe1w=\",\"salt\":\"32WjW1KzFaR5RJqU0Pfq9w==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":5,\"algorithm\":\"argon2\",\"additionalParameters\":{\"hashLength\":[\"32\"],\"memory\":[\"7168\"],\"type\":[\"id\"],\"version\":[\"1.3\"],\"parallelism\":[\"1\"]}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-teacher" ], + "notBefore" : 0, + "groups" : [ ] + } ], + "scopeMappings" : [ { + "clientScope" : "offline_access", + "roles" : [ "offline_access" ] + } ], + "clientScopeMappings" : { + "account" : [ { + "client" : "account-console", + "roles" : [ "manage-account", "view-groups" ] + } ] + }, + "clients" : [ { + "id" : "7ceb65eb-30da-4dc3-95bc-f06863362fd6", + "clientId" : "account", + "name" : "${client_account}", + "rootUrl" : "${authBaseUrl}", + "baseUrl" : "/realms/teacher/account/", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ "/realms/teacher/account/*" ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "realm_client" : "false", + "post.logout.redirect.uris" : "+" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "basic", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "organization", "microprofile-jwt" ] + }, { + "id" : "920e8621-36b5-4046-b1cd-4b293668f64b", + "clientId" : "account-console", + "name" : "${client_account-console}", + "rootUrl" : "${authBaseUrl}", + "baseUrl" : "/realms/teacher/account/", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ "/realms/teacher/account/*" ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "realm_client" : "false", + "post.logout.redirect.uris" : "+", + "pkce.code.challenge.method" : "S256" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "protocolMappers" : [ { + "id" : "cd3f4ae0-3008-488b-88c5-b6d640a9edd3", + "name" : "audience resolve", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-audience-resolve-mapper", + "consentRequired" : false, + "config" : { } + } ], + "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "basic", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "organization", "microprofile-jwt" ] + }, { + "id" : "9d7b2827-b7bb-451e-ad38-8f55a69f7c9c", + "clientId" : "admin-cli", + "name" : "${client_admin-cli}", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : false, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : true, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "realm_client" : "false", + "client.use.lightweight.access.token.enabled" : "true" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : true, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "basic", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "organization", "microprofile-jwt" ] + }, { + "id" : "cfd0202e-a6b9-4c5e-9f49-2ef17df9089b", + "clientId" : "broker", + "name" : "${client_broker}", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : true, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : false, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "realm_client" : "true" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "basic", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "organization", "microprofile-jwt" ] + }, { + "id" : "abdee18a-4549-48b5-b976-4c1a42820ef9", + "clientId" : "dwengo", + "name" : "Dwengo", + "description" : "", + "rootUrl" : "http://localhost:3000", + "adminUrl" : "http://localhost:3000", + "baseUrl" : "http://localhost:3000", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ "urn:ietf:wg:oauth:2.0:oob", "http://localhost:3000/*", "http://localhost:3000" ], + "webOrigins" : [ "+" ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : true, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : true, + "protocol" : "openid-connect", + "attributes" : { + "realm_client" : "false", + "oidc.ciba.grant.enabled" : "false", + "backchannel.logout.session.required" : "true", + "post.logout.redirect.uris" : "+", + "frontchannel.logout.session.required" : "true", + "oauth2.device.authorization.grant.enabled" : "false", + "display.on.consent.screen" : "false", + "backchannel.logout.revoke.offline.tokens" : "false" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : true, + "nodeReRegistrationTimeout" : -1, + "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "basic", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "organization", "microprofile-jwt" ] + }, { + "id" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "clientId" : "realm-management", + "name" : "${client_realm-management}", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : true, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : false, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "realm_client" : "true" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "basic", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "organization", "microprofile-jwt" ] + }, { + "id" : "c421853c-5bdf-4ea9-ae97-51f5ad7b8df8", + "clientId" : "security-admin-console", + "name" : "${client_security-admin-console}", + "rootUrl" : "${authAdminUrl}", + "baseUrl" : "/admin/teacher/console/", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ "/admin/teacher/console/*" ], + "webOrigins" : [ "+" ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "realm_client" : "false", + "client.use.lightweight.access.token.enabled" : "true", + "post.logout.redirect.uris" : "+", + "pkce.code.challenge.method" : "S256" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : true, + "nodeReRegistrationTimeout" : 0, + "protocolMappers" : [ { + "id" : "a9a893af-925e-46c9-ba33-47b06101ce5f", + "name" : "locale", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "locale", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "locale", + "jsonType.label" : "String" + } + } ], + "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "basic", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "organization", "microprofile-jwt" ] + } ], + "clientScopes" : [ { + "id" : "fef4fbeb-d7e6-4474-b802-6c63df0dc9a3", + "name" : "saml_organization", + "description" : "Organization Membership", + "protocol" : "saml", + "attributes" : { + "display.on.consent.screen" : "false" + }, + "protocolMappers" : [ { + "id" : "2384b79b-5cc3-4e1c-b4b2-4bee2ceeed72", + "name" : "organization", + "protocol" : "saml", + "protocolMapper" : "saml-organization-membership-mapper", + "consentRequired" : false, + "config" : { } + } ] + }, { + "id" : "a097893c-7eed-4556-b2ed-3751c7fc3c51", + "name" : "offline_access", + "description" : "OpenID Connect built-in scope: offline_access", + "protocol" : "openid-connect", + "attributes" : { + "consent.screen.text" : "${offlineAccessScopeConsentText}", + "display.on.consent.screen" : "true" + } + }, { + "id" : "ffc38cb2-eb10-47cf-a2d6-6647fdd4da65", + "name" : "service_account", + "description" : "Specific scope for a client enabled for service accounts", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "display.on.consent.screen" : "false" + }, + "protocolMappers" : [ { + "id" : "06ed3629-1c3d-48d9-80c6-98fcd3958c48", + "name" : "Client Host", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "clientHost", + "id.token.claim" : "true", + "introspection.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "clientHost", + "jsonType.label" : "String" + } + }, { + "id" : "04eeb81e-05c0-484a-91df-9a79138bcd66", + "name" : "Client IP Address", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "clientAddress", + "id.token.claim" : "true", + "introspection.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "clientAddress", + "jsonType.label" : "String" + } + }, { + "id" : "6e673f49-ce38-4583-8040-8a2e7ec5e7c8", + "name" : "Client ID", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "client_id", + "id.token.claim" : "true", + "introspection.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "client_id", + "jsonType.label" : "String" + } + } ] + }, { + "id" : "ee188d9c-ab26-4e53-a16c-c9f77094f854", + "name" : "profile", + "description" : "OpenID Connect built-in scope: profile", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "consent.screen.text" : "${profileScopeConsentText}", + "display.on.consent.screen" : "true" + }, + "protocolMappers" : [ { + "id" : "05ff270b-6a50-4bbb-903d-9546a59f20bf", + "name" : "picture", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "picture", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "picture", + "jsonType.label" : "String" + } + }, { + "id" : "394f808d-bc7b-476e-a372-7cfece5c6db0", + "name" : "gender", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "gender", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "gender", + "jsonType.label" : "String" + } + }, { + "id" : "0371c44f-c6e0-4f88-ac8f-17a56e2b90f8", + "name" : "profile", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "profile", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "profile", + "jsonType.label" : "String" + } + }, { + "id" : "21d66073-42f2-443b-aac4-e49c9038253c", + "name" : "birthdate", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "birthdate", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "birthdate", + "jsonType.label" : "String" + } + }, { + "id" : "5cc6a97f-9d1a-4c72-b682-af6d1bd36883", + "name" : "full name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-full-name-mapper", + "consentRequired" : false, + "config" : { + "id.token.claim" : "true", + "introspection.token.claim" : "true", + "access.token.claim" : "true", + "userinfo.token.claim" : "true" + } + }, { + "id" : "d6a6d46b-80a7-4228-af07-0faae2911fed", + "name" : "nickname", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "nickname", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "nickname", + "jsonType.label" : "String" + } + }, { + "id" : "322b508a-7464-4b0f-90df-3f489975a62e", + "name" : "zoneinfo", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "zoneinfo", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "zoneinfo", + "jsonType.label" : "String" + } + }, { + "id" : "f757ae7a-3005-4899-bb4e-da1ab4b47bb0", + "name" : "locale", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "locale", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "locale", + "jsonType.label" : "String" + } + }, { + "id" : "bab8eb17-0cb0-4275-8456-aa1d65933a35", + "name" : "updated at", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "updatedAt", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "updated_at", + "jsonType.label" : "long" + } + }, { + "id" : "6ea1d43c-d4c7-4f2f-93b0-dfdb3bb584eb", + "name" : "given name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "firstName", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "given_name", + "jsonType.label" : "String" + } + }, { + "id" : "3a2ebc93-05fb-4904-996b-5e3331b72fcd", + "name" : "family name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "lastName", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "family_name", + "jsonType.label" : "String" + } + }, { + "id" : "217b417e-d4f6-4225-bf92-3bd38f6fbefb", + "name" : "username", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "username", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "preferred_username", + "jsonType.label" : "String" + } + }, { + "id" : "3dd5da51-5842-4358-a69f-f7ffffe521ac", + "name" : "website", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "website", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "website", + "jsonType.label" : "String" + } + }, { + "id" : "790bda99-1c27-4970-b3b9-4fa1c90c738c", + "name" : "middle name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "middleName", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "middle_name", + "jsonType.label" : "String" + } + } ] + }, { + "id" : "e6cf59c7-9390-4f48-ab01-79a0fa138960", + "name" : "organization", + "description" : "Additional claims about the organization a subject belongs to", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "consent.screen.text" : "${organizationScopeConsentText}", + "display.on.consent.screen" : "true" + }, + "protocolMappers" : [ { + "id" : "417ff129-6b95-4e95-9f57-a6699ca18d8d", + "name" : "organization", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-organization-membership-mapper", + "consentRequired" : false, + "config" : { + "id.token.claim" : "true", + "introspection.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "organization", + "jsonType.label" : "String", + "multivalued" : "true" + } + } ] + }, { + "id" : "43d92ef5-76d8-4df0-84b5-5f833875d345", + "name" : "email", + "description" : "OpenID Connect built-in scope: email", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "consent.screen.text" : "${emailScopeConsentText}", + "display.on.consent.screen" : "true" + }, + "protocolMappers" : [ { + "id" : "74d21718-190a-4c53-b446-b07e5f029394", + "name" : "email verified", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "emailVerified", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "email_verified", + "jsonType.label" : "boolean" + } + }, { + "id" : "949871a0-d68c-4563-a9b3-945a3148f937", + "name" : "email", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "email", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "email", + "jsonType.label" : "String" + } + } ] + }, { + "id" : "b07a2014-d07e-450f-a593-66e9f9cf4799", + "name" : "acr", + "description" : "OpenID Connect scope for add acr (authentication context class reference) to the token", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "display.on.consent.screen" : "false" + }, + "protocolMappers" : [ { + "id" : "79efdc37-0f06-43e6-a516-7bc9dc29f04d", + "name" : "acr loa level", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-acr-mapper", + "consentRequired" : false, + "config" : { + "id.token.claim" : "true", + "introspection.token.claim" : "true", + "access.token.claim" : "true" + } + } ] + }, { + "id" : "3bbbff21-0446-4813-8bdf-54c35d8fffca", + "name" : "microprofile-jwt", + "description" : "Microprofile - JWT built-in scope", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "false" + }, + "protocolMappers" : [ { + "id" : "0e996cda-fe5b-439d-ba4c-cf2129ae812f", + "name" : "upn", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "username", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "upn", + "jsonType.label" : "String" + } + }, { + "id" : "ddf1efe2-e765-475c-a4a0-d52f1f597834", + "name" : "groups", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-realm-role-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "multivalued" : "true", + "user.attribute" : "foo", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "groups", + "jsonType.label" : "String" + } + } ] + }, { + "id" : "93a40d0e-f163-42f7-a9d4-53cc2e17914e", + "name" : "basic", + "description" : "OpenID Connect scope for add all basic claims to the token", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "display.on.consent.screen" : "false" + }, + "protocolMappers" : [ { + "id" : "41eb9e93-8e04-404b-a12b-40ef5a55f640", + "name" : "sub", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-sub-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "access.token.claim" : "true" + } + }, { + "id" : "1291062a-10f6-4061-b9ea-f54ff5d8ec54", + "name" : "auth_time", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "AUTH_TIME", + "id.token.claim" : "true", + "introspection.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "auth_time", + "jsonType.label" : "long" + } + } ] + }, { + "id" : "9ea27173-e54b-42f0-8f6c-5a36c5073ede", + "name" : "role_list", + "description" : "SAML role list", + "protocol" : "saml", + "attributes" : { + "consent.screen.text" : "${samlRoleListScopeConsentText}", + "display.on.consent.screen" : "true" + }, + "protocolMappers" : [ { + "id" : "d10a6975-8aeb-4215-8d6b-23b0286d4abb", + "name" : "role list", + "protocol" : "saml", + "protocolMapper" : "saml-role-list-mapper", + "consentRequired" : false, + "config" : { + "single" : "false", + "attribute.nameformat" : "Basic", + "attribute.name" : "Role" + } + } ] + }, { + "id" : "e8a99a5a-1519-4c7d-a3f0-ac6d34c61a0b", + "name" : "phone", + "description" : "OpenID Connect built-in scope: phone", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "consent.screen.text" : "${phoneScopeConsentText}", + "display.on.consent.screen" : "true" + }, + "protocolMappers" : [ { + "id" : "b2de087f-169f-44b3-ad46-3a063ac9025f", + "name" : "phone number", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "phoneNumber", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "phone_number", + "jsonType.label" : "String" + } + }, { + "id" : "ffb8aebd-0d03-4811-8fd4-aa03bda36b2d", + "name" : "phone number verified", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "phoneNumberVerified", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "phone_number_verified", + "jsonType.label" : "boolean" + } + } ] + }, { + "id" : "30e06d84-f610-4f17-8820-6f785a510357", + "name" : "address", + "description" : "OpenID Connect built-in scope: address", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "consent.screen.text" : "${addressScopeConsentText}", + "display.on.consent.screen" : "true" + }, + "protocolMappers" : [ { + "id" : "de707a09-a895-4b67-9ac5-0ff4e69715ea", + "name" : "address", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-address-mapper", + "consentRequired" : false, + "config" : { + "user.attribute.formatted" : "formatted", + "user.attribute.country" : "country", + "introspection.token.claim" : "true", + "user.attribute.postal_code" : "postal_code", + "userinfo.token.claim" : "true", + "user.attribute.street" : "street", + "id.token.claim" : "true", + "user.attribute.region" : "region", + "access.token.claim" : "true", + "user.attribute.locality" : "locality" + } + } ] + }, { + "id" : "1762c903-9f07-451c-915d-855488e4aa42", + "name" : "web-origins", + "description" : "OpenID Connect scope for add allowed web origins to the access token", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "consent.screen.text" : "", + "display.on.consent.screen" : "false" + }, + "protocolMappers" : [ { + "id" : "0164bdc3-c79d-4467-b6bf-ca9a6889d04c", + "name" : "allowed web origins", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-allowed-origins-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "access.token.claim" : "true" + } + } ] + }, { + "id" : "91301d6d-0bb9-4da6-b8db-ee2480e25fee", + "name" : "roles", + "description" : "OpenID Connect scope for add user roles to the access token", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "consent.screen.text" : "${rolesScopeConsentText}", + "display.on.consent.screen" : "true" + }, + "protocolMappers" : [ { + "id" : "2880d772-b0da-4ee8-bf1e-3f729a945db9", + "name" : "realm roles", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-realm-role-mapper", + "consentRequired" : false, + "config" : { + "user.attribute" : "foo", + "introspection.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "realm_access.roles", + "jsonType.label" : "String", + "multivalued" : "true" + } + }, { + "id" : "535042c5-58c5-4225-94b8-0b5b3411968e", + "name" : "client roles", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-client-role-mapper", + "consentRequired" : false, + "config" : { + "user.attribute" : "foo", + "introspection.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "resource_access.${client_id}.roles", + "jsonType.label" : "String", + "multivalued" : "true" + } + }, { + "id" : "a88432f1-565f-480d-958d-a5cea1dbcf0a", + "name" : "audience resolve", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-audience-resolve-mapper", + "consentRequired" : false, + "config" : { + "introspection.token.claim" : "true", + "access.token.claim" : "true" + } + } ] + } ], + "defaultDefaultClientScopes" : [ "role_list", "saml_organization", "profile", "email", "roles", "web-origins", "acr", "basic" ], + "defaultOptionalClientScopes" : [ "offline_access", "address", "phone", "microprofile-jwt", "organization" ], + "browserSecurityHeaders" : { + "contentSecurityPolicyReportOnly" : "", + "xContentTypeOptions" : "nosniff", + "referrerPolicy" : "no-referrer", + "xRobotsTag" : "none", + "xFrameOptions" : "SAMEORIGIN", + "contentSecurityPolicy" : "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "xXSSProtection" : "1; mode=block", + "strictTransportSecurity" : "max-age=31536000; includeSubDomains" + }, + "smtpServer" : { }, + "eventsEnabled" : false, + "eventsListeners" : [ "jboss-logging" ], + "enabledEventTypes" : [ ], + "adminEventsEnabled" : false, + "adminEventsDetailsEnabled" : false, + "identityProviders" : [ ], + "identityProviderMappers" : [ ], + "components" : { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy" : [ { + "id" : "a689e06a-e440-4d94-ba54-692fba5a5486", + "name" : "Max Clients Limit", + "providerId" : "max-clients", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "max-clients" : [ "200" ] + } + }, { + "id" : "2778fda5-0a9f-40ab-ab4b-054ff8ce38e9", + "name" : "Allowed Client Scopes", + "providerId" : "allowed-client-templates", + "subType" : "authenticated", + "subComponents" : { }, + "config" : { + "allow-default-scopes" : [ "true" ] + } + }, { + "id" : "36dc0167-9c9a-4b4a-9f04-29129aecac4d", + "name" : "Allowed Protocol Mapper Types", + "providerId" : "allowed-protocol-mappers", + "subType" : "authenticated", + "subComponents" : { }, + "config" : { + "allowed-protocol-mapper-types" : [ "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-property-mapper", "saml-user-attribute-mapper", "oidc-full-name-mapper", "saml-user-property-mapper", "oidc-address-mapper", "saml-role-list-mapper", "oidc-usermodel-attribute-mapper" ] + } + }, { + "id" : "4b79c6fd-5166-4bc2-ab0b-bff0018452f6", + "name" : "Consent Required", + "providerId" : "consent-required", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { } + }, { + "id" : "2003600a-89fb-421e-9dfe-d5096ee7fd4e", + "name" : "Allowed Protocol Mapper Types", + "providerId" : "allowed-protocol-mappers", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "allowed-protocol-mapper-types" : [ "oidc-full-name-mapper", "oidc-usermodel-attribute-mapper", "oidc-address-mapper", "oidc-sha256-pairwise-sub-mapper", "saml-role-list-mapper", "saml-user-property-mapper", "saml-user-attribute-mapper", "oidc-usermodel-property-mapper" ] + } + }, { + "id" : "d62a2e93-f877-462a-bad3-93dcf91d49d2", + "name" : "Trusted Hosts", + "providerId" : "trusted-hosts", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "host-sending-registration-request-must-match" : [ "true" ], + "client-uris-must-match" : [ "true" ] + } + }, { + "id" : "6e659a80-a638-4504-b507-21b9f77586ed", + "name" : "Full Scope Disabled", + "providerId" : "scope", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { } + }, { + "id" : "9ef67c59-5c3e-40cf-90ee-516b2e35ed3d", + "name" : "Allowed Client Scopes", + "providerId" : "allowed-client-templates", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "allow-default-scopes" : [ "true" ] + } + } ], + "org.keycloak.keys.KeyProvider" : [ { + "id" : "b5365a56-e00d-4612-80bf-262a9c8dba7c", + "name" : "rsa-enc-generated", + "providerId" : "rsa-enc-generated", + "subComponents" : { }, + "config" : { + "privateKey" : [ "MIIEogIBAAKCAQEAudnbEbQij+g9JWYMuyJjF/sKe4fVEd9SrCmkFeAHZ7dEAmEKQcAlvJn1aL99pAm8KV0w9PAZugQx7ZzG6eUm4JLc+LYGJ8G8JDiVR6hIWRQ3k9HGcUwNacHKFlDj4XOeMykwLEo7jrQHAUx82vJO8bKZr2ixZqGc1UUUQNbEVYv0HzxhPKPoFYh9qQ8U6P0r/K9xIusLL6ZzmXx6uXqQuqtse05e5G9xwLjQDDrOKju4s1PJ1GZLt5Db9PGypMeA9J34tGL3O0rQom2WbO7R2GZGX044ZoNw6UnraGTmCxin1ywmwTq3JTb1IZ4DPLH/rucnPuKUb60B5ByPmXW/lQIDAQABAoIBAAlyYrGLbb9RX3xNa+O+O3m+U7nUPXcfWikwo6vN+6pgtScGzjnp3bEwxTnqE+WY7hTPLRwiMTiUmoIYuD6u3HNJW8yTmgv+y8SukJ34FpdakPmlTdg39K2VwWMxgOfWk+nHU/DIZC8chQeinu0VKICeIrQ5Ft1f5SQtEvq5v/iWIql+v2ipxdJ2dSl1NIO0/2S2Lyd7Slab50gbJ3kP0uZggN5IMtNd5GBvAbV+jaT4QWKuyXyHqOnyU/+2WU+XhmVPrX6c29sQg2CilqWf4RzEIeO/FgAiANEPyaAgW6mGtf17K1xsSrusyGMUsNGsGJSd7Q8K2o7g/Jv9V8160iECgYEA2xgKIoZ6+fT7UIDr+5insRr6PIht2WYl+JqibjzNRh+rvp1fjKkmjMOA39V6cvLaAHieSOUerSOD7nQONCedpHe6To1zOG9z5yYGgwa/2c/9eNORJq8vNw/4wXaARVf0mCNaexugPTdYvsSaqwW4+azIbB21xXUfKykLp0SjNfUCgYEA2ShJSZyTIqJ64IHi+Kj/E22ajK2DYBiFgNDmzKrW/ANO5ADumgMhahCcxW28Nw68n25vBWCK4o+6eVg1ldEdB1LxoKOYZAaA+zAiMsGI1/ndxdnlFopuJZguKhYDTmxzT0KcD9mApLKZBnCadGjG3FcdC8i14OK6S9lUIIpCvyECgYBXqWC0u7YMuPatGUhSXJwMAs1I1xWMvJBIziZbkTxY6GchV3pZn3xrKfYwmQvrXjvXoGtEo1gI0oMBL7JXL9qlabpDn9kQJZfsToygdFzi25OBerVDEykDEQLo9W8RT8Xv8YVMaJtOowyBF80CzMFcNMPkbmbCYMBd1ohxHsdm2QKBgGB9RhMvPzFkgLTBAdj7Plujl8hqULWiL6/NIsBOKLhRv/wPbfWA7pfySbZvy/Gq2qT8rNf2zb9dnb3NNAIdqIhYkoSOLGhFe4ohGRD0bZmJrMD80I3zdH2/4MNShKWUCqhtMGraeg60TMpPvlF7POEq0/0ocag7FgwdxQOwa3gBAoGASvjvVtfXyzWA9MycWLvlTPEGW5bjXrroYwulF8DkKfIaKWHfEiulTwQe4SHgS7CzWSg8KgvKIhJC/yTwfOtxZ894G9LWivwjjZCottIE+/qs6ioYSXouQr6IsWxs7U0i3gP36tsePjuSjR06kpBGfcFdynypAAq+mVBCV0Mxk9A=" ], + "keyUse" : [ "ENC" ], + "certificate" : [ "MIICnTCCAYUCBgGVU7avyzANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAd0ZWFjaGVyMB4XDTI1MDMwMTIxNTUzNloXDTM1MDMwMTIxNTcxNlowEjEQMA4GA1UEAwwHdGVhY2hlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALnZ2xG0Io/oPSVmDLsiYxf7CnuH1RHfUqwppBXgB2e3RAJhCkHAJbyZ9Wi/faQJvCldMPTwGboEMe2cxunlJuCS3Pi2BifBvCQ4lUeoSFkUN5PRxnFMDWnByhZQ4+FznjMpMCxKO460BwFMfNryTvGyma9osWahnNVFFEDWxFWL9B88YTyj6BWIfakPFOj9K/yvcSLrCy+mc5l8erl6kLqrbHtOXuRvccC40Aw6zio7uLNTydRmS7eQ2/TxsqTHgPSd+LRi9ztK0KJtlmzu0dhmRl9OOGaDcOlJ62hk5gsYp9csJsE6tyU29SGeAzyx/67nJz7ilG+tAeQcj5l1v5UCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAf3HTufrfWb0uqsEwfUETl4S4IbJOjqDdzFhUkMVtiq5I9LLUlJ7StZ6eoDCEoKUzF2lPy0qR2Om7IKC8BA7J5qUio0NSNh9j/t1Ipcjzx6SQI2cD6AjJFZndnF+OBTxdm9c6J+KMho6ZSMQEGwn2osgRBeItauxUshogQJPY/GzWMHlZyCAJcYtuflzgyw1VIQ0OiWCpCiSGeWpojxh19KR9qSBU1rETZMLokmdp84muq8aqEnNIFY5XRyUdH4gjNBx3TGsammZbvzuZdZIDvFNE19SXl/J9QcWJlRw0DuOblLcLKiamcJkQj35T9DgwtYRc/2zM3u8jNwQXKwrUWA==" ], + "priority" : [ "100" ], + "algorithm" : [ "RSA-OAEP" ] + } + }, { + "id" : "ce5dcd75-614d-453a-868c-4413b4a10c39", + "name" : "hmac-generated-hs512", + "providerId" : "hmac-generated", + "subComponents" : { }, + "config" : { + "kid" : [ "a58f2df5-d24b-4aae-9e38-d42736883c7d" ], + "secret" : [ "4sDZ4TC6Cuo0-A5Wa42n_HLCxFj6ir4enL6OmdllOTtR7f5YJN5bsPOJXOFGHeuNPe5jgNq2GfOaeqyQ19PnJMd3Ctsj7vQlx57hywXNvQ1FNuKL1uoxF2Szvw65Y4gIM7xoZpQglVhg2Zh7kA3HJEVhDvnmjNdjtm1QgdlFYws" ], + "priority" : [ "100" ], + "algorithm" : [ "HS512" ] + } + }, { + "id" : "972a70cc-5e9d-4435-8423-f4d32e18d1e7", + "name" : "rsa-generated", + "providerId" : "rsa-generated", + "subComponents" : { }, + "config" : { + "privateKey" : [ "MIIEogIBAAKCAQEAy3EHaNAy6udZMC3A2KIZhMDg0SfGN+FVOKRJfh6aJwtNPuP6EXUhCMRSCXj3EHvFYrBJwKllC8li8MC5rw0vF/P9OY1zdbNkg8Rpwa1D55AS/MlYqiKHazKIV55SXVt5MKtjGATk9D4P/UZ8iP3viBnT0Kws4lWrAv1Uk12CkeLzojeTdCr4I8xgsj5U6dWu2f9DJlsieBhefgJpIXgdWXzVyGC3dOQOrCMHxYdyL4dbBlJPk13QJGkmgH65PnRB0Zu4CtlNHWN1jbXWcRNs8iMLZ4R36F8OzbMcv51lv7+0UTP7HJ4iRjw+sSlUH1AB+3sklyoNgRnW6sisEA+UFQIDAQABAoIBAA9kbWGWQwv31g0poQoi9ZhQOZJJlps6vsZq066ppRMoLT+BYzW37Xhq1iQmVVcXbj9BxErB5kXGhmhdxI7EihgfWzzkAWTZ3lSD41aGg/k8stsSZtV0iFdpetxaO7QZjClNBlHWaPY7zdzlXN3GjL146shChqDXR3mR7ji6HftolGVnmzUXRK+gZG9IirlC+qCJ+sd6m9h83x31X5PRT6yiJ/jeNN4XpuMh61xHFckFOFCGfV2isWM9qL5kLllN1+m8nMjt0HOeEB0GRrHTMSp7QC9RI0z1C/uxdAdSyMhCUtva8jAfjtqYAo7yc63zlOvlkFuYeOQ9X8UmnavBbL8CgYEA7FjyjVr3OK139528/FJMmLk2xOCDQ08pS+ADX5Eib7R62k1ZzXiKnv/8whfdQFeJwIunSYn+y2JCaMFjARrh04SELeH6/CvQ5uCknfIkLeNBij1ye5Ruy7JpaV3oe36h8sJYv1+p5RcrxxINBxbEeKM/YQWRwcXVE54MBCB4dKsCgYEA3FufmasbB4Pna2Txlgo+XCKpf+1U+gcN6lRzsKzqtFVT7+ofUndnqTKgPrLHYytYOFIZIIP8YZBno5gUvK7bFjgAGWacayYNWAXSiEhRQ3ah95Ii/b4lcU095GN/Xu68yqlGQc0GDVD9xRejBNyYgHc2GPQ9bigjsb6pQLy0mj8CgYAN5SjVcKyqM2CjOS3cM8Z3ECSNLJnrAiNuZ4wrOTAqGxVB8lw+PUEBGhG1I4wJdVwO6ub55tgJAwzedcgpT3hJZDgVLn0ACF9uw3RKKOtBm2PGCdjKNS7SYPnbjP7XC9nfmNd44NnvMw6K1J/Zc9g3M3nNbXNlTgk57wfL0lDiowKBgEIF4cf1EGAsEUaINCo0X4LTj92YioFvY6f2LcOdy6TEfCXCDCh1RkXXuVOP1VXNQt19G7I2WYQR9Dt78Zqm+VWq6byyleM0v4LEG9Rhdpe0D8tRqdJFCorsDcNEXIFhHofKOBa3Cz0qKx7Gej2Wqsqy7S6E33MF68vxyFxxLduZAoGAcHhDa8r7EMFTEt3rZIqmblqOYvhfMKJ+Ruom11mUSHWLgyQGzK8mVPhB59J7gt0DKU6XRIwby/7c7x2wFWQ+dsy03PN49PDtewLcGtrsicJlY2mofFZpsFsYhOpyhPg4/zFiX77Ev3UEYiJJ4qXnlV5Yb+ae5D8ZNlmhIP1HQY4=" ], + "keyUse" : [ "SIG" ], + "certificate" : [ "MIICnTCCAYUCBgGVU7avAzANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAd0ZWFjaGVyMB4XDTI1MDMwMTIxNTUzNloXDTM1MDMwMTIxNTcxNlowEjEQMA4GA1UEAwwHdGVhY2hlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMtxB2jQMurnWTAtwNiiGYTA4NEnxjfhVTikSX4emicLTT7j+hF1IQjEUgl49xB7xWKwScCpZQvJYvDAua8NLxfz/TmNc3WzZIPEacGtQ+eQEvzJWKoih2syiFeeUl1beTCrYxgE5PQ+D/1GfIj974gZ09CsLOJVqwL9VJNdgpHi86I3k3Qq+CPMYLI+VOnVrtn/QyZbIngYXn4CaSF4HVl81chgt3TkDqwjB8WHci+HWwZST5Nd0CRpJoB+uT50QdGbuArZTR1jdY211nETbPIjC2eEd+hfDs2zHL+dZb+/tFEz+xyeIkY8PrEpVB9QAft7JJcqDYEZ1urIrBAPlBUCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAHkOqsY1iHqqCMDFvTh/XCNZRwdLdGY6ev+5zShrgb8MPJNgM/HIqQtwQ9wuKf5RfMF1+FQdSU1eavTeuHXp4IuMgv97DAjdZ/pBGHz5tCWMdlaf+Au/1zDoqCV91CbGuV6WHaUhDJLZfp9/phiq2BzPZO6LeWhFJLMzH+N6rPZ7Om72rjTN31TlLLgmLuKlOhMp2QpyaQB16g4ksLGIYq7IXIbCqPRuB33k3gO/+ZMYRpU2U4DQ3FZyIe4LzLXQQ7VSFz/x/rvnbF+hHBdcbszUvsQYCS21aZ6nAq4CGinU2iAOLXHmFotKs+01KZT1N3ZGlGQmHM8ywYyb9qbcfPA==" ], + "priority" : [ "100" ] + } + }, { + "id" : "24e3094f-f962-49bd-b355-ff3096bfefe8", + "name" : "aes-generated", + "providerId" : "aes-generated", + "subComponents" : { }, + "config" : { + "kid" : [ "52ac32c1-f589-4e04-9667-16d2e7bd707a" ], + "secret" : [ "ZEiWoUCZ30PSKa2rx8UXTQ" ], + "priority" : [ "100" ] + } + } ] + }, + "internationalizationEnabled" : false, + "supportedLocales" : [ ], + "authenticationFlows" : [ { + "id" : "2ac7aebb-c1ac-4fdf-9687-cedd34665024", + "alias" : "Account verification options", + "description" : "Method with which to verity the existing account", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "idp-email-verification", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "ALTERNATIVE", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "Verify Existing Account by Re-authentication", + "userSetupAllowed" : false + } ] + }, { + "id" : "2505f3dc-719b-43a1-9631-585302dd449e", + "alias" : "Browser - Conditional OTP", + "description" : "Flow to determine if the OTP is required for the authentication", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "auth-otp-form", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "5a07c120-c34b-4cf2-b38d-2e558af6853a", + "alias" : "Browser - Conditional Organization", + "description" : "Flow to determine if the organization identity-first login is to be used", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "organization", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "a3317f52-b2bc-4b4c-af14-53901d253fca", + "alias" : "Direct Grant - Conditional OTP", + "description" : "Flow to determine if the OTP is required for the authentication", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "direct-grant-validate-otp", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "2281818c-fb40-4997-a1ad-fc9ad2c3cacc", + "alias" : "First Broker Login - Conditional Organization", + "description" : "Flow to determine if the authenticator that adds organization members is to be used", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "idp-add-organization-member", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "fcab0380-ca38-4f66-aaf2-ec741ef8be8e", + "alias" : "First broker login - Conditional OTP", + "description" : "Flow to determine if the OTP is required for the authentication", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "auth-otp-form", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "ae2e214a-82b6-4d78-a7d0-f80d454e5083", + "alias" : "Handle Existing Account", + "description" : "Handle what to do if there is existing account with same email/username like authenticated identity provider", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "idp-confirm-link", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "Account verification options", + "userSetupAllowed" : false + } ] + }, { + "id" : "ad2add46-e1bb-47bf-a125-d76c517f66a4", + "alias" : "Organization", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 10, + "autheticatorFlow" : true, + "flowAlias" : "Browser - Conditional Organization", + "userSetupAllowed" : false + } ] + }, { + "id" : "74e5d429-4db2-4323-b504-005c03e530fc", + "alias" : "Reset - Conditional OTP", + "description" : "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "reset-otp", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "d11dbfe7-2472-4cda-a7f5-e9a536154028", + "alias" : "User creation or linking", + "description" : "Flow for the existing/non-existing user alternatives", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticatorConfig" : "create unique user config", + "authenticator" : "idp-create-user-if-unique", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "ALTERNATIVE", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "Handle Existing Account", + "userSetupAllowed" : false + } ] + }, { + "id" : "f1131dc8-ea34-48e1-9363-438c15f985a4", + "alias" : "Verify Existing Account by Re-authentication", + "description" : "Reauthentication of existing account", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "idp-username-password-form", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "First broker login - Conditional OTP", + "userSetupAllowed" : false + } ] + }, { + "id" : "f2880986-ef01-4199-ac31-35e0b16c989b", + "alias" : "browser", + "description" : "Browser based authentication", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "auth-cookie", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "auth-spnego", + "authenticatorFlow" : false, + "requirement" : "DISABLED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "identity-provider-redirector", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 25, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "ALTERNATIVE", + "priority" : 26, + "autheticatorFlow" : true, + "flowAlias" : "Organization", + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "ALTERNATIVE", + "priority" : 30, + "autheticatorFlow" : true, + "flowAlias" : "forms", + "userSetupAllowed" : false + } ] + }, { + "id" : "a08dca2e-d491-483f-a310-25bcfa2d89b3", + "alias" : "clients", + "description" : "Base authentication for clients", + "providerId" : "client-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "client-secret", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "client-jwt", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "client-secret-jwt", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 30, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "client-x509", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 40, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "4742ab83-03c9-417d-ba61-017d9f02afb3", + "alias" : "direct grant", + "description" : "OpenID Connect Resource Owner Grant", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "direct-grant-validate-username", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "direct-grant-validate-password", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 30, + "autheticatorFlow" : true, + "flowAlias" : "Direct Grant - Conditional OTP", + "userSetupAllowed" : false + } ] + }, { + "id" : "458f78fd-84e5-4e4d-8198-200f25942134", + "alias" : "docker auth", + "description" : "Used by Docker clients to authenticate against the IDP", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "docker-http-basic-authenticator", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "8cbdd82f-3794-4fce-9494-70279a3d1fcb", + "alias" : "first broker login", + "description" : "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticatorConfig" : "review profile config", + "authenticator" : "idp-review-profile", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "User creation or linking", + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 50, + "autheticatorFlow" : true, + "flowAlias" : "First Broker Login - Conditional Organization", + "userSetupAllowed" : false + } ] + }, { + "id" : "b64919c6-da2b-4e66-bcc6-0112d9e3132b", + "alias" : "forms", + "description" : "Username, password, otp and other auth forms.", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "auth-username-password-form", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "Browser - Conditional OTP", + "userSetupAllowed" : false + } ] + }, { + "id" : "3c8979fe-c98c-4911-b16c-510dba8fb8e3", + "alias" : "registration", + "description" : "Registration flow", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "registration-page-form", + "authenticatorFlow" : true, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : true, + "flowAlias" : "registration form", + "userSetupAllowed" : false + } ] + }, { + "id" : "6f598384-bb66-485e-8ed5-7da83c1deba1", + "alias" : "registration form", + "description" : "Registration form", + "providerId" : "form-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "registration-user-creation", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "registration-password-action", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 50, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "registration-recaptcha-action", + "authenticatorFlow" : false, + "requirement" : "DISABLED", + "priority" : 60, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "registration-terms-and-conditions", + "authenticatorFlow" : false, + "requirement" : "DISABLED", + "priority" : 70, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "086acb80-23bb-496d-a982-0d8886b2e844", + "alias" : "reset credentials", + "description" : "Reset credentials for a user if they forgot their password or something", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "reset-credentials-choose-user", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "reset-credential-email", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "reset-password", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 30, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 40, + "autheticatorFlow" : true, + "flowAlias" : "Reset - Conditional OTP", + "userSetupAllowed" : false + } ] + }, { + "id" : "2b5042d2-f5e2-456c-bd94-1f23ea0bfb20", + "alias" : "saml ecp", + "description" : "SAML ECP Profile Authentication Flow", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "http-basic-authenticator", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + } ], + "authenticatorConfig" : [ { + "id" : "3007c3b0-cdd5-4464-93f4-23e439b15253", + "alias" : "create unique user config", + "config" : { + "require.password.update.after.registration" : "false" + } + }, { + "id" : "ce14faa0-34fe-496f-bcb5-a7e72fcf3fbb", + "alias" : "review profile config", + "config" : { + "update.profile.on.first.login" : "missing" + } + } ], + "requiredActions" : [ { + "alias" : "CONFIGURE_TOTP", + "name" : "Configure OTP", + "providerId" : "CONFIGURE_TOTP", + "enabled" : true, + "defaultAction" : false, + "priority" : 10, + "config" : { } + }, { + "alias" : "TERMS_AND_CONDITIONS", + "name" : "Terms and Conditions", + "providerId" : "TERMS_AND_CONDITIONS", + "enabled" : false, + "defaultAction" : false, + "priority" : 20, + "config" : { } + }, { + "alias" : "UPDATE_PASSWORD", + "name" : "Update Password", + "providerId" : "UPDATE_PASSWORD", + "enabled" : true, + "defaultAction" : false, + "priority" : 30, + "config" : { } + }, { + "alias" : "UPDATE_PROFILE", + "name" : "Update Profile", + "providerId" : "UPDATE_PROFILE", + "enabled" : true, + "defaultAction" : false, + "priority" : 40, + "config" : { } + }, { + "alias" : "VERIFY_EMAIL", + "name" : "Verify Email", + "providerId" : "VERIFY_EMAIL", + "enabled" : true, + "defaultAction" : false, + "priority" : 50, + "config" : { } + }, { + "alias" : "delete_account", + "name" : "Delete Account", + "providerId" : "delete_account", + "enabled" : false, + "defaultAction" : false, + "priority" : 60, + "config" : { } + }, { + "alias" : "webauthn-register", + "name" : "Webauthn Register", + "providerId" : "webauthn-register", + "enabled" : true, + "defaultAction" : false, + "priority" : 70, + "config" : { } + }, { + "alias" : "webauthn-register-passwordless", + "name" : "Webauthn Register Passwordless", + "providerId" : "webauthn-register-passwordless", + "enabled" : true, + "defaultAction" : false, + "priority" : 80, + "config" : { } + }, { + "alias" : "VERIFY_PROFILE", + "name" : "Verify Profile", + "providerId" : "VERIFY_PROFILE", + "enabled" : true, + "defaultAction" : false, + "priority" : 90, + "config" : { } + }, { + "alias" : "delete_credential", + "name" : "Delete Credential", + "providerId" : "delete_credential", + "enabled" : true, + "defaultAction" : false, + "priority" : 100, + "config" : { } + }, { + "alias" : "update_user_locale", + "name" : "Update User Locale", + "providerId" : "update_user_locale", + "enabled" : true, + "defaultAction" : false, + "priority" : 1000, + "config" : { } + } ], + "browserFlow" : "browser", + "registrationFlow" : "registration", + "directGrantFlow" : "direct grant", + "resetCredentialsFlow" : "reset credentials", + "clientAuthenticationFlow" : "clients", + "dockerAuthenticationFlow" : "docker auth", + "firstBrokerLoginFlow" : "first broker login", + "attributes" : { + "cibaBackchannelTokenDeliveryMode" : "poll", + "cibaExpiresIn" : "120", + "cibaAuthRequestedUserHint" : "login_hint", + "oauth2DeviceCodeLifespan" : "600", + "oauth2DevicePollingInterval" : "5", + "parRequestUriLifespan" : "60", + "cibaInterval" : "5", + "realmReusableOtpCode" : "false" + }, + "keycloakVersion" : "26.1.3", + "userManagedAccessAllowed" : false, + "organizationsEnabled" : false, + "verifiableCredentialsEnabled" : false, + "adminPermissionsEnabled" : false, + "clientProfiles" : { + "profiles" : [ ] + }, + "clientPolicies" : { + "policies" : [ ] + } +} From a28ec22f29fdb3127a7384b34b2ec8c1f253c5c9 Mon Sep 17 00:00:00 2001 From: Gerald Schmittinger Date: Sun, 2 Mar 2025 16:33:50 +0100 Subject: [PATCH 05/96] feat(frontend): Added functionality to the frontend to log in. --- backend/.env.development.example | 5 + backend/package.json | 4 +- backend/src/app.ts | 8 +- backend/src/controllers/auth.ts | 33 ++++++ backend/src/middleware/auth/auth.ts | 1 - backend/src/middleware/cors.ts | 6 + backend/src/routes/auth.ts | 10 ++ backend/src/routes/login.ts | 14 --- backend/src/util/envvars.ts | 5 +- frontend/package.json | 4 +- frontend/src/App.vue | 2 + frontend/src/config.ts | 14 +++ frontend/src/router/index.ts | 5 + frontend/src/services/api-client.ts | 10 ++ frontend/src/services/auth-service.ts | 106 ++++++++++++++++++ frontend/src/store/auth-store.ts | 40 +++++++ frontend/src/views/HomePage.vue | 9 +- frontend/src/views/LoginPage.vue | 27 ++++- .../src/views/discussions/CallbackPage.vue | 24 ++++ package-lock.json | 101 +++++++++++++++-- 20 files changed, 395 insertions(+), 33 deletions(-) create mode 100644 backend/src/controllers/auth.ts create mode 100644 backend/src/middleware/cors.ts create mode 100644 backend/src/routes/auth.ts delete mode 100644 backend/src/routes/login.ts create mode 100644 frontend/src/config.ts create mode 100644 frontend/src/services/api-client.ts create mode 100644 frontend/src/services/auth-service.ts create mode 100644 frontend/src/store/auth-store.ts create mode 100644 frontend/src/views/discussions/CallbackPage.vue diff --git a/backend/.env.development.example b/backend/.env.development.example index 0b96f873..f809129d 100644 --- a/backend/.env.development.example +++ b/backend/.env.development.example @@ -6,6 +6,11 @@ DWENGO_DB_PASSWORD=postgres DWENGO_DB_UPDATE=true DWENGO_AUTH_STUDENT_URL=http://localhost:7080/realms/student +DWENGO_AUTH_STUDENT_CLIENT_ID=dwengo DWENGO_AUTH_STUDENT_JWKS_ENDPOINT=http://localhost:7080/realms/student/protocol/openid-connect/certs DWENGO_AUTH_TEACHER_URL=http://localhost:7080/realms/teacher +DWENGO_AUTH_TEACHER_CLIENT_ID=dwengo DWENGO_AUTH_TEACHER_JWKS_ENDPOINT=http://localhost:7080/realms/teacher/protocol/openid-connect/certs + +# Allow frontend from anywhere to access the backend (for testing purposes). Don't forget to remove this in production! +DWENGO_CORS_ALLOWED_ORIGINS=* diff --git a/backend/package.json b/backend/package.json index af8090f2..254158f1 100644 --- a/backend/package.json +++ b/backend/package.json @@ -24,7 +24,9 @@ "jwks-rsa": "^3.1.0", "uuid": "^11.1.0", "js-yaml": "^4.1.0", - "@types/js-yaml": "^4.0.9" + "@types/js-yaml": "^4.0.9", + "cors": "^2.8.5", + "@types/cors": "^2.8.17" }, "devDependencies": { "@mikro-orm/cli": "^6.4.6", diff --git a/backend/src/app.ts b/backend/src/app.ts index 521c40c5..250a6ebe 100644 --- a/backend/src/app.ts +++ b/backend/src/app.ts @@ -10,13 +10,13 @@ import assignmentRouter from './routes/assignment.js'; import submissionRouter from './routes/submission.js'; import classRouter from './routes/class.js'; import questionRouter from './routes/question.js'; -import loginRouter from './routes/login.js'; +import authRouter from './routes/auth.js'; import {authenticateUser} from "./middleware/auth/auth"; +import cors from "./middleware/cors"; const app: Express = express(); const port: string | number = getNumericEnvVar(EnvVars.Port); - // TODO Replace with Express routes app.get('/', (_, res: Response) => { res.json({ @@ -25,6 +25,7 @@ app.get('/', (_, res: Response) => { }); app.use(authenticateUser); +app.use(cors); app.use('/student', studentRouter); app.use('/group', groupRouter); @@ -32,8 +33,7 @@ app.use('/assignment', assignmentRouter); app.use('/submission', submissionRouter); app.use('/class', classRouter); app.use('/question', questionRouter); -app.use('/login', loginRouter); - +app.use('/auth', authRouter); app.use('/theme', themeRoutes); async function startServer() { diff --git a/backend/src/controllers/auth.ts b/backend/src/controllers/auth.ts new file mode 100644 index 00000000..800c3b99 --- /dev/null +++ b/backend/src/controllers/auth.ts @@ -0,0 +1,33 @@ +import {EnvVars, getEnvVar} from "../util/envvars"; + +type FrontendAuthConfig = { + student: FrontendIdpConfig, + teacher: FrontendIdpConfig +} + +type FrontendIdpConfig = { + authority: string, + clientId: string, + scope: string, + responseType: string +} + +const SCOPE = "openid profile email"; +const RESPONSE_TYPE = "code"; + +export function getFrontendAuthConfig(): FrontendAuthConfig { + return { + student: { + authority: getEnvVar(EnvVars.IdpStudentUrl), + clientId: getEnvVar(EnvVars.IdpStudentClientId), + scope: SCOPE, + responseType: RESPONSE_TYPE + }, + teacher: { + authority: getEnvVar(EnvVars.IdpTeacherUrl), + clientId: getEnvVar(EnvVars.IdpTeacherClientId), + scope: SCOPE, + responseType: RESPONSE_TYPE + }, + }; +} diff --git a/backend/src/middleware/auth/auth.ts b/backend/src/middleware/auth/auth.ts index 0bf63e3d..d06e6df2 100644 --- a/backend/src/middleware/auth/auth.ts +++ b/backend/src/middleware/auth/auth.ts @@ -58,7 +58,6 @@ const verifyJwtToken = expressjwt({ * Get an object with information about the authenticated user from a given authenticated request. */ function getAuthenticationInfo(req: AuthenticatedRequest): AuthenticationInfo | undefined { - console.log("hi"); if (!req.jwtPayload) { return; } diff --git a/backend/src/middleware/cors.ts b/backend/src/middleware/cors.ts new file mode 100644 index 00000000..e246aadf --- /dev/null +++ b/backend/src/middleware/cors.ts @@ -0,0 +1,6 @@ +import cors from "cors"; +import {EnvVars, getEnvVar} from "../util/envvars"; + +export default cors({ + origin: getEnvVar(EnvVars.CorsAllowedOrigins).split(',') +}); diff --git a/backend/src/routes/auth.ts b/backend/src/routes/auth.ts new file mode 100644 index 00000000..87c4183c --- /dev/null +++ b/backend/src/routes/auth.ts @@ -0,0 +1,10 @@ +import express from 'express' +import {getFrontendAuthConfig} from "../controllers/auth"; +const router = express.Router(); + +// returns auth configuration for frontend +router.get('/config', (req, res) => { + res.json(getFrontendAuthConfig()); +}); + +export default router; diff --git a/backend/src/routes/login.ts b/backend/src/routes/login.ts deleted file mode 100644 index 550e6d93..00000000 --- a/backend/src/routes/login.ts +++ /dev/null @@ -1,14 +0,0 @@ -import express from 'express' -const router = express.Router(); - -// returns login paths for IDP -router.get('/', (req, res) => { - res.json({ - // dummy variables, needs to be changed - // with IDP endpoints - leerkracht: '/login-leerkracht', - leerling: '/login-leerling', - }); -}) - -export default router \ No newline at end of file diff --git a/backend/src/util/envvars.ts b/backend/src/util/envvars.ts index 2dfcf640..60aa8025 100644 --- a/backend/src/util/envvars.ts +++ b/backend/src/util/envvars.ts @@ -15,10 +15,13 @@ export const EnvVars: { [key: string]: EnvVar } = { DbPassword: { key: DB_PREFIX + 'PASSWORD', required: true }, DbUpdate: { key: DB_PREFIX + 'UPDATE', defaultValue: false }, IdpStudentUrl: { key: STUDENT_IDP_PREFIX + 'URL', required: true }, + IdpStudentClientId: { key: STUDENT_IDP_PREFIX + 'CLIENT_ID', required: true }, IdpStudentJwksEndpoint: { key: STUDENT_IDP_PREFIX + 'JWKS_ENDPOINT', required: true }, IdpTeacherUrl: { key: TEACHER_IDP_PREFIX + 'URL', required: true }, + IdpTeacherClientId: { key: TEACHER_IDP_PREFIX + 'CLIENT_ID', required: true }, IdpTeacherJwksEndpoint: { key: TEACHER_IDP_PREFIX + 'JWKS_ENDPOINT', required: true }, - IdpAudience: { key: IDP_PREFIX + 'AUDIENCE', defaultValue: 'account' } + IdpAudience: { key: IDP_PREFIX + 'AUDIENCE', defaultValue: 'account' }, + CorsAllowedOrigins: { key: PREFIX + 'CORS_ALLOWED_ORIGINS', defaultValue: ''} } as const; /** diff --git a/frontend/package.json b/frontend/package.json index 2c2c2612..8c6f81d3 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -18,7 +18,9 @@ "dependencies": { "vue": "^3.5.13", "vue-router": "^4.5.0", - "vuetify": "^3.7.12" + "vuetify": "^3.7.12", + "oidc-client-ts": "^3.1.0", + "axios": "^1.8.1" }, "devDependencies": { "@playwright/test": "^1.50.1", diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 7db110de..c1eb520c 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,4 +1,6 @@ - + diff --git a/idp/README.md b/idp/README.md index 3f3fd4ff..f67d0462 100644 --- a/idp/README.md +++ b/idp/README.md @@ -1,7 +1,9 @@ # Testdata in de IDP + De IDP in `docker-compose.yml` is zo geconfigureerd dat hij automatisch bij het starten een testconfiguratie inlaadt. Deze houdt in: -* Een realm `student` die de IDP voor leerlingen representeert. - * Hierin de gebruiker met username `testleerling1`, wachtwoord `password`. -* Een realm `teacher` die de IDP voor leerkrachten representeert. - * Hierin de gebruiker met username `testleerkracht1`, wachtwoord `password`. -* De admin-account (in de realm `master`) heeft username `admin` en wachtwoord `admin`. + +- Een realm `student` die de IDP voor leerlingen representeert. + - Hierin de gebruiker met username `testleerling1`, wachtwoord `password`. +- Een realm `teacher` die de IDP voor leerkrachten representeert. + - Hierin de gebruiker met username `testleerkracht1`, wachtwoord `password`. +- De admin-account (in de realm `master`) heeft username `admin` en wachtwoord `admin`. diff --git a/idp/student-realm.json b/idp/student-realm.json index 7b6bc94b..697fda34 100644 --- a/idp/student-realm.json +++ b/idp/student-realm.json @@ -1,2062 +1,2347 @@ { - "id" : "08a7ab0a-d483-4103-a781-76013864bf50", - "realm" : "student", - "notBefore" : 0, - "defaultSignatureAlgorithm" : "RS256", - "revokeRefreshToken" : false, - "refreshTokenMaxReuse" : 0, - "accessTokenLifespan" : 300, - "accessTokenLifespanForImplicitFlow" : 900, - "ssoSessionIdleTimeout" : 1800, - "ssoSessionMaxLifespan" : 36000, - "ssoSessionIdleTimeoutRememberMe" : 0, - "ssoSessionMaxLifespanRememberMe" : 0, - "offlineSessionIdleTimeout" : 2592000, - "offlineSessionMaxLifespanEnabled" : false, - "offlineSessionMaxLifespan" : 5184000, - "clientSessionIdleTimeout" : 0, - "clientSessionMaxLifespan" : 0, - "clientOfflineSessionIdleTimeout" : 0, - "clientOfflineSessionMaxLifespan" : 0, - "accessCodeLifespan" : 60, - "accessCodeLifespanUserAction" : 300, - "accessCodeLifespanLogin" : 1800, - "actionTokenGeneratedByAdminLifespan" : 43200, - "actionTokenGeneratedByUserLifespan" : 300, - "oauth2DeviceCodeLifespan" : 600, - "oauth2DevicePollingInterval" : 5, - "enabled" : true, - "sslRequired" : "external", - "registrationAllowed" : false, - "registrationEmailAsUsername" : false, - "rememberMe" : false, - "verifyEmail" : false, - "loginWithEmailAllowed" : true, - "duplicateEmailsAllowed" : false, - "resetPasswordAllowed" : false, - "editUsernameAllowed" : false, - "bruteForceProtected" : false, - "permanentLockout" : false, - "maxTemporaryLockouts" : 0, - "bruteForceStrategy" : "MULTIPLE", - "maxFailureWaitSeconds" : 900, - "minimumQuickLoginWaitSeconds" : 60, - "waitIncrementSeconds" : 60, - "quickLoginCheckMilliSeconds" : 1000, - "maxDeltaTimeSeconds" : 43200, - "failureFactor" : 30, - "roles" : { - "realm" : [ { - "id" : "a0bb00f5-0b3a-4d57-a3fc-a3f93cbe3427", - "name" : "offline_access", - "description" : "${role_offline-access}", - "composite" : false, - "clientRole" : false, - "containerId" : "08a7ab0a-d483-4103-a781-76013864bf50", - "attributes" : { } - }, { - "id" : "b3bf9566-098c-4167-9cce-f64c720ca511", - "name" : "default-roles-student", - "description" : "${role_default-roles}", - "composite" : true, - "composites" : { - "realm" : [ "offline_access", "uma_authorization" ], - "client" : { - "account" : [ "manage-account", "view-profile" ] + "id": "08a7ab0a-d483-4103-a781-76013864bf50", + "realm": "student", + "notBefore": 0, + "defaultSignatureAlgorithm": "RS256", + "revokeRefreshToken": false, + "refreshTokenMaxReuse": 0, + "accessTokenLifespan": 300, + "accessTokenLifespanForImplicitFlow": 900, + "ssoSessionIdleTimeout": 1800, + "ssoSessionMaxLifespan": 36000, + "ssoSessionIdleTimeoutRememberMe": 0, + "ssoSessionMaxLifespanRememberMe": 0, + "offlineSessionIdleTimeout": 2592000, + "offlineSessionMaxLifespanEnabled": false, + "offlineSessionMaxLifespan": 5184000, + "clientSessionIdleTimeout": 0, + "clientSessionMaxLifespan": 0, + "clientOfflineSessionIdleTimeout": 0, + "clientOfflineSessionMaxLifespan": 0, + "accessCodeLifespan": 60, + "accessCodeLifespanUserAction": 300, + "accessCodeLifespanLogin": 1800, + "actionTokenGeneratedByAdminLifespan": 43200, + "actionTokenGeneratedByUserLifespan": 300, + "oauth2DeviceCodeLifespan": 600, + "oauth2DevicePollingInterval": 5, + "enabled": true, + "sslRequired": "external", + "registrationAllowed": false, + "registrationEmailAsUsername": false, + "rememberMe": false, + "verifyEmail": false, + "loginWithEmailAllowed": true, + "duplicateEmailsAllowed": false, + "resetPasswordAllowed": false, + "editUsernameAllowed": false, + "bruteForceProtected": false, + "permanentLockout": false, + "maxTemporaryLockouts": 0, + "bruteForceStrategy": "MULTIPLE", + "maxFailureWaitSeconds": 900, + "minimumQuickLoginWaitSeconds": 60, + "waitIncrementSeconds": 60, + "quickLoginCheckMilliSeconds": 1000, + "maxDeltaTimeSeconds": 43200, + "failureFactor": 30, + "roles": { + "realm": [ + { + "id": "a0bb00f5-0b3a-4d57-a3fc-a3f93cbe3427", + "name": "offline_access", + "description": "${role_offline-access}", + "composite": false, + "clientRole": false, + "containerId": "08a7ab0a-d483-4103-a781-76013864bf50", + "attributes": {} + }, + { + "id": "b3bf9566-098c-4167-9cce-f64c720ca511", + "name": "default-roles-student", + "description": "${role_default-roles}", + "composite": true, + "composites": { + "realm": ["offline_access", "uma_authorization"], + "client": { + "account": ["manage-account", "view-profile"] + } + }, + "clientRole": false, + "containerId": "08a7ab0a-d483-4103-a781-76013864bf50", + "attributes": {} + }, + { + "id": "6d044f54-8ff3-4223-9e8c-771882da7a3f", + "name": "uma_authorization", + "description": "${role_uma_authorization}", + "composite": false, + "clientRole": false, + "containerId": "08a7ab0a-d483-4103-a781-76013864bf50", + "attributes": {} + } + ], + "client": { + "realm-management": [ + { + "id": "f125e557-2427-4eeb-95c5-b3dadf35f9c7", + "name": "manage-authorization", + "description": "${role_manage-authorization}", + "composite": false, + "clientRole": true, + "containerId": "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes": {} + }, + { + "id": "33c7285a-7308-4752-acad-1fe59bf1c81a", + "name": "manage-identity-providers", + "description": "${role_manage-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes": {} + }, + { + "id": "31fb3621-62c7-43c8-af98-a4add3470fcc", + "name": "query-clients", + "description": "${role_query-clients}", + "composite": false, + "clientRole": true, + "containerId": "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes": {} + }, + { + "id": "e077c3c3-d573-494f-9cf8-34eca6603fc6", + "name": "realm-admin", + "description": "${role_realm-admin}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "manage-authorization", + "query-clients", + "manage-identity-providers", + "create-client", + "view-users", + "view-authorization", + "query-users", + "manage-users", + "view-identity-providers", + "impersonation", + "manage-realm", + "view-events", + "view-clients", + "manage-events", + "manage-clients", + "view-realm", + "query-groups", + "query-realms" + ] + } + }, + "clientRole": true, + "containerId": "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes": {} + }, + { + "id": "8bbe59b1-7693-4274-bdde-c08f94ec3187", + "name": "create-client", + "description": "${role_create-client}", + "composite": false, + "clientRole": true, + "containerId": "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes": {} + }, + { + "id": "0533162d-7dac-4ebf-87a2-7f72dad79d53", + "name": "view-users", + "description": "${role_view-users}", + "composite": true, + "composites": { + "client": { + "realm-management": ["query-groups", "query-users"] + } + }, + "clientRole": true, + "containerId": "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes": {} + }, + { + "id": "d4b32078-67b4-4aa8-8ddf-01a820e7b64a", + "name": "view-authorization", + "description": "${role_view-authorization}", + "composite": false, + "clientRole": true, + "containerId": "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes": {} + }, + { + "id": "2a48ab18-b710-41e7-8b8c-67a5cd6af685", + "name": "query-users", + "description": "${role_query-users}", + "composite": false, + "clientRole": true, + "containerId": "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes": {} + }, + { + "id": "d71d575f-3f21-4f4a-b9e0-2628352aac8d", + "name": "manage-users", + "description": "${role_manage-users}", + "composite": false, + "clientRole": true, + "containerId": "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes": {} + }, + { + "id": "7d3cd659-4ddd-45cd-8186-210431a25bbd", + "name": "impersonation", + "description": "${role_impersonation}", + "composite": false, + "clientRole": true, + "containerId": "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes": {} + }, + { + "id": "3dbd18ca-11dc-463d-bf8e-e7d80928a90d", + "name": "view-identity-providers", + "description": "${role_view-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes": {} + }, + { + "id": "d4a6ef1e-bf84-4bd6-8763-1b0c9997c109", + "name": "manage-realm", + "description": "${role_manage-realm}", + "composite": false, + "clientRole": true, + "containerId": "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes": {} + }, + { + "id": "f0eab8d7-0570-44d3-94d0-2a43906d9f09", + "name": "view-events", + "description": "${role_view-events}", + "composite": false, + "clientRole": true, + "containerId": "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes": {} + }, + { + "id": "0a24b91f-ef4a-4f4b-a753-1286dd59df2b", + "name": "view-clients", + "description": "${role_view-clients}", + "composite": true, + "composites": { + "client": { + "realm-management": ["query-clients"] + } + }, + "clientRole": true, + "containerId": "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes": {} + }, + { + "id": "b307485c-8840-4c39-ba81-fb840fa404d1", + "name": "manage-events", + "description": "${role_manage-events}", + "composite": false, + "clientRole": true, + "containerId": "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes": {} + }, + { + "id": "3719a5ed-be30-4d2c-93f5-cc6e6c0e792e", + "name": "manage-clients", + "description": "${role_manage-clients}", + "composite": false, + "clientRole": true, + "containerId": "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes": {} + }, + { + "id": "d4b13416-9f5e-42fb-bfdd-6489093922da", + "name": "view-realm", + "description": "${role_view-realm}", + "composite": false, + "clientRole": true, + "containerId": "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes": {} + }, + { + "id": "15ac861b-5440-4fe8-9f7d-857d75ec481d", + "name": "query-groups", + "description": "${role_query-groups}", + "composite": false, + "clientRole": true, + "containerId": "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes": {} + }, + { + "id": "f05a8e4d-90ea-41f6-887b-0b6b1ecb9cd9", + "name": "query-realms", + "description": "${role_query-realms}", + "composite": false, + "clientRole": true, + "containerId": "0b06aaa3-717d-4a52-ab46-295a6571b642", + "attributes": {} + } + ], + "dwengo": [], + "security-admin-console": [], + "admin-cli": [], + "account-console": [], + "broker": [ + { + "id": "da1edd82-7479-4e9d-ad66-9a4cf739e828", + "name": "read-token", + "description": "${role_read-token}", + "composite": false, + "clientRole": true, + "containerId": "befe3d72-8102-49a6-8268-bce6def58159", + "attributes": {} + } + ], + "account": [ + { + "id": "5a3da53d-235b-4d12-b8ec-1573b13ebafc", + "name": "view-consent", + "description": "${role_view-consent}", + "composite": false, + "clientRole": true, + "containerId": "b3a22454-d780-4093-8333-9be6f6cd5855", + "attributes": {} + }, + { + "id": "cbc0c1d4-487b-488c-8566-1d4537212de8", + "name": "manage-account-links", + "description": "${role_manage-account-links}", + "composite": false, + "clientRole": true, + "containerId": "b3a22454-d780-4093-8333-9be6f6cd5855", + "attributes": {} + }, + { + "id": "79b0ed8f-bf10-4b01-bb2c-e7a58d57c798", + "name": "delete-account", + "description": "${role_delete-account}", + "composite": false, + "clientRole": true, + "containerId": "b3a22454-d780-4093-8333-9be6f6cd5855", + "attributes": {} + }, + { + "id": "b6aa748e-0fb0-4fa6-a0d1-3ea37c870467", + "name": "manage-account", + "description": "${role_manage-account}", + "composite": true, + "composites": { + "client": { + "account": ["manage-account-links"] + } + }, + "clientRole": true, + "containerId": "b3a22454-d780-4093-8333-9be6f6cd5855", + "attributes": {} + }, + { + "id": "ddaea6cd-ede8-49f7-9746-3a3a02fdeca5", + "name": "view-profile", + "description": "${role_view-profile}", + "composite": false, + "clientRole": true, + "containerId": "b3a22454-d780-4093-8333-9be6f6cd5855", + "attributes": {} + }, + { + "id": "061b2038-b415-4a45-89ec-7141004c0151", + "name": "view-applications", + "description": "${role_view-applications}", + "composite": false, + "clientRole": true, + "containerId": "b3a22454-d780-4093-8333-9be6f6cd5855", + "attributes": {} + }, + { + "id": "95972aa1-6666-421c-8596-a91eee54b0e8", + "name": "view-groups", + "description": "${role_view-groups}", + "composite": false, + "clientRole": true, + "containerId": "b3a22454-d780-4093-8333-9be6f6cd5855", + "attributes": {} + }, + { + "id": "1cf27d94-d88d-42d3-b8f3-ede1f127ac45", + "name": "manage-consent", + "description": "${role_manage-consent}", + "composite": true, + "composites": { + "client": { + "account": ["view-consent"] + } + }, + "clientRole": true, + "containerId": "b3a22454-d780-4093-8333-9be6f6cd5855", + "attributes": {} + } + ] } - }, - "clientRole" : false, - "containerId" : "08a7ab0a-d483-4103-a781-76013864bf50", - "attributes" : { } - }, { - "id" : "6d044f54-8ff3-4223-9e8c-771882da7a3f", - "name" : "uma_authorization", - "description" : "${role_uma_authorization}", - "composite" : false, - "clientRole" : false, - "containerId" : "08a7ab0a-d483-4103-a781-76013864bf50", - "attributes" : { } - } ], - "client" : { - "realm-management" : [ { - "id" : "f125e557-2427-4eeb-95c5-b3dadf35f9c7", - "name" : "manage-authorization", - "description" : "${role_manage-authorization}", - "composite" : false, - "clientRole" : true, - "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", - "attributes" : { } - }, { - "id" : "33c7285a-7308-4752-acad-1fe59bf1c81a", - "name" : "manage-identity-providers", - "description" : "${role_manage-identity-providers}", - "composite" : false, - "clientRole" : true, - "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", - "attributes" : { } - }, { - "id" : "31fb3621-62c7-43c8-af98-a4add3470fcc", - "name" : "query-clients", - "description" : "${role_query-clients}", - "composite" : false, - "clientRole" : true, - "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", - "attributes" : { } - }, { - "id" : "e077c3c3-d573-494f-9cf8-34eca6603fc6", - "name" : "realm-admin", - "description" : "${role_realm-admin}", - "composite" : true, - "composites" : { - "client" : { - "realm-management" : [ "manage-authorization", "query-clients", "manage-identity-providers", "create-client", "view-users", "view-authorization", "query-users", "manage-users", "view-identity-providers", "impersonation", "manage-realm", "view-events", "view-clients", "manage-events", "manage-clients", "view-realm", "query-groups", "query-realms" ] - } + }, + "groups": [], + "defaultRole": { + "id": "b3bf9566-098c-4167-9cce-f64c720ca511", + "name": "default-roles-student", + "description": "${role_default-roles}", + "composite": true, + "clientRole": false, + "containerId": "08a7ab0a-d483-4103-a781-76013864bf50" + }, + "requiredCredentials": ["password"], + "otpPolicyType": "totp", + "otpPolicyAlgorithm": "HmacSHA1", + "otpPolicyInitialCounter": 0, + "otpPolicyDigits": 6, + "otpPolicyLookAheadWindow": 1, + "otpPolicyPeriod": 30, + "otpPolicyCodeReusable": false, + "otpSupportedApplications": ["totpAppFreeOTPName", "totpAppGoogleName", "totpAppMicrosoftAuthenticatorName"], + "localizationTexts": {}, + "webAuthnPolicyRpEntityName": "keycloak", + "webAuthnPolicySignatureAlgorithms": ["ES256", "RS256"], + "webAuthnPolicyRpId": "", + "webAuthnPolicyAttestationConveyancePreference": "not specified", + "webAuthnPolicyAuthenticatorAttachment": "not specified", + "webAuthnPolicyRequireResidentKey": "not specified", + "webAuthnPolicyUserVerificationRequirement": "not specified", + "webAuthnPolicyCreateTimeout": 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyAcceptableAaguids": [], + "webAuthnPolicyExtraOrigins": [], + "webAuthnPolicyPasswordlessRpEntityName": "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms": ["ES256", "RS256"], + "webAuthnPolicyPasswordlessRpId": "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified", + "webAuthnPolicyPasswordlessCreateTimeout": 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyPasswordlessAcceptableAaguids": [], + "webAuthnPolicyPasswordlessExtraOrigins": [], + "users": [ + { + "id": "79e9a395-d7e4-48c9-a06e-702435bae290", + "username": "testleerling1", + "firstName": "Gerald", + "lastName": "Schmittinger", + "email": "Gerald.Schmittinger@UGent.be", + "emailVerified": false, + "createdTimestamp": 1740858528405, + "enabled": true, + "totp": false, + "credentials": [ + { + "id": "c31a708f-8614-4144-a25f-3e976c9035ce", + "type": "password", + "userLabel": "My password", + "createdDate": 1740858548515, + "secretData": "{\"value\":\"yDKIAbZPuVXBGk4zjiqE/YFcPDm1vjXLwTrPUrvMhXY=\",\"salt\":\"tYvjd4mhV2UWeOUssK01Cw==\",\"additionalParameters\":{}}", + "credentialData": "{\"hashIterations\":5,\"algorithm\":\"argon2\",\"additionalParameters\":{\"hashLength\":[\"32\"],\"memory\":[\"7168\"],\"type\":[\"id\"],\"version\":[\"1.3\"],\"parallelism\":[\"1\"]}}" + } + ], + "disableableCredentialTypes": [], + "requiredActions": [], + "realmRoles": ["default-roles-student"], + "notBefore": 0, + "groups": [] + } + ], + "scopeMappings": [ + { + "clientScope": "offline_access", + "roles": ["offline_access"] + } + ], + "clientScopeMappings": { + "account": [ + { + "client": "account-console", + "roles": ["manage-account", "view-groups"] + } + ] + }, + "clients": [ + { + "id": "b3a22454-d780-4093-8333-9be6f6cd5855", + "clientId": "account", + "name": "${client_account}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/student/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": ["/realms/student/account/*"], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "realm_client": "false", + "post.logout.redirect.uris": "+" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": ["web-origins", "acr", "profile", "roles", "basic", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "organization", "microprofile-jwt"] }, - "clientRole" : true, - "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", - "attributes" : { } - }, { - "id" : "8bbe59b1-7693-4274-bdde-c08f94ec3187", - "name" : "create-client", - "description" : "${role_create-client}", - "composite" : false, - "clientRole" : true, - "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", - "attributes" : { } - }, { - "id" : "0533162d-7dac-4ebf-87a2-7f72dad79d53", - "name" : "view-users", - "description" : "${role_view-users}", - "composite" : true, - "composites" : { - "client" : { - "realm-management" : [ "query-groups", "query-users" ] - } + { + "id": "854c221b-630c-4cc3-9365-bd254246dd69", + "clientId": "account-console", + "name": "${client_account-console}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/student/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": ["/realms/student/account/*"], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "realm_client": "false", + "post.logout.redirect.uris": "+", + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "f33b40fe-bb9e-4254-ada9-f98dd203641b", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + } + ], + "defaultClientScopes": ["web-origins", "acr", "profile", "roles", "basic", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "organization", "microprofile-jwt"] }, - "clientRole" : true, - "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", - "attributes" : { } - }, { - "id" : "d4b32078-67b4-4aa8-8ddf-01a820e7b64a", - "name" : "view-authorization", - "description" : "${role_view-authorization}", - "composite" : false, - "clientRole" : true, - "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", - "attributes" : { } - }, { - "id" : "2a48ab18-b710-41e7-8b8c-67a5cd6af685", - "name" : "query-users", - "description" : "${role_query-users}", - "composite" : false, - "clientRole" : true, - "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", - "attributes" : { } - }, { - "id" : "d71d575f-3f21-4f4a-b9e0-2628352aac8d", - "name" : "manage-users", - "description" : "${role_manage-users}", - "composite" : false, - "clientRole" : true, - "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", - "attributes" : { } - }, { - "id" : "7d3cd659-4ddd-45cd-8186-210431a25bbd", - "name" : "impersonation", - "description" : "${role_impersonation}", - "composite" : false, - "clientRole" : true, - "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", - "attributes" : { } - }, { - "id" : "3dbd18ca-11dc-463d-bf8e-e7d80928a90d", - "name" : "view-identity-providers", - "description" : "${role_view-identity-providers}", - "composite" : false, - "clientRole" : true, - "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", - "attributes" : { } - }, { - "id" : "d4a6ef1e-bf84-4bd6-8763-1b0c9997c109", - "name" : "manage-realm", - "description" : "${role_manage-realm}", - "composite" : false, - "clientRole" : true, - "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", - "attributes" : { } - }, { - "id" : "f0eab8d7-0570-44d3-94d0-2a43906d9f09", - "name" : "view-events", - "description" : "${role_view-events}", - "composite" : false, - "clientRole" : true, - "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", - "attributes" : { } - }, { - "id" : "0a24b91f-ef4a-4f4b-a753-1286dd59df2b", - "name" : "view-clients", - "description" : "${role_view-clients}", - "composite" : true, - "composites" : { - "client" : { - "realm-management" : [ "query-clients" ] - } + { + "id": "9449aa8b-d5cc-4b9f-bb01-be1e5a896f2f", + "clientId": "admin-cli", + "name": "${client_admin-cli}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": false, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "realm_client": "false", + "client.use.lightweight.access.token.enabled": "true" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": ["web-origins", "acr", "profile", "roles", "basic", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "organization", "microprofile-jwt"] }, - "clientRole" : true, - "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", - "attributes" : { } - }, { - "id" : "b307485c-8840-4c39-ba81-fb840fa404d1", - "name" : "manage-events", - "description" : "${role_manage-events}", - "composite" : false, - "clientRole" : true, - "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", - "attributes" : { } - }, { - "id" : "3719a5ed-be30-4d2c-93f5-cc6e6c0e792e", - "name" : "manage-clients", - "description" : "${role_manage-clients}", - "composite" : false, - "clientRole" : true, - "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", - "attributes" : { } - }, { - "id" : "d4b13416-9f5e-42fb-bfdd-6489093922da", - "name" : "view-realm", - "description" : "${role_view-realm}", - "composite" : false, - "clientRole" : true, - "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", - "attributes" : { } - }, { - "id" : "15ac861b-5440-4fe8-9f7d-857d75ec481d", - "name" : "query-groups", - "description" : "${role_query-groups}", - "composite" : false, - "clientRole" : true, - "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", - "attributes" : { } - }, { - "id" : "f05a8e4d-90ea-41f6-887b-0b6b1ecb9cd9", - "name" : "query-realms", - "description" : "${role_query-realms}", - "composite" : false, - "clientRole" : true, - "containerId" : "0b06aaa3-717d-4a52-ab46-295a6571b642", - "attributes" : { } - } ], - "dwengo" : [ ], - "security-admin-console" : [ ], - "admin-cli" : [ ], - "account-console" : [ ], - "broker" : [ { - "id" : "da1edd82-7479-4e9d-ad66-9a4cf739e828", - "name" : "read-token", - "description" : "${role_read-token}", - "composite" : false, - "clientRole" : true, - "containerId" : "befe3d72-8102-49a6-8268-bce6def58159", - "attributes" : { } - } ], - "account" : [ { - "id" : "5a3da53d-235b-4d12-b8ec-1573b13ebafc", - "name" : "view-consent", - "description" : "${role_view-consent}", - "composite" : false, - "clientRole" : true, - "containerId" : "b3a22454-d780-4093-8333-9be6f6cd5855", - "attributes" : { } - }, { - "id" : "cbc0c1d4-487b-488c-8566-1d4537212de8", - "name" : "manage-account-links", - "description" : "${role_manage-account-links}", - "composite" : false, - "clientRole" : true, - "containerId" : "b3a22454-d780-4093-8333-9be6f6cd5855", - "attributes" : { } - }, { - "id" : "79b0ed8f-bf10-4b01-bb2c-e7a58d57c798", - "name" : "delete-account", - "description" : "${role_delete-account}", - "composite" : false, - "clientRole" : true, - "containerId" : "b3a22454-d780-4093-8333-9be6f6cd5855", - "attributes" : { } - }, { - "id" : "b6aa748e-0fb0-4fa6-a0d1-3ea37c870467", - "name" : "manage-account", - "description" : "${role_manage-account}", - "composite" : true, - "composites" : { - "client" : { - "account" : [ "manage-account-links" ] - } + { + "id": "befe3d72-8102-49a6-8268-bce6def58159", + "clientId": "broker", + "name": "${client_broker}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "realm_client": "true" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": ["web-origins", "acr", "profile", "roles", "basic", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "organization", "microprofile-jwt"] }, - "clientRole" : true, - "containerId" : "b3a22454-d780-4093-8333-9be6f6cd5855", - "attributes" : { } - }, { - "id" : "ddaea6cd-ede8-49f7-9746-3a3a02fdeca5", - "name" : "view-profile", - "description" : "${role_view-profile}", - "composite" : false, - "clientRole" : true, - "containerId" : "b3a22454-d780-4093-8333-9be6f6cd5855", - "attributes" : { } - }, { - "id" : "061b2038-b415-4a45-89ec-7141004c0151", - "name" : "view-applications", - "description" : "${role_view-applications}", - "composite" : false, - "clientRole" : true, - "containerId" : "b3a22454-d780-4093-8333-9be6f6cd5855", - "attributes" : { } - }, { - "id" : "95972aa1-6666-421c-8596-a91eee54b0e8", - "name" : "view-groups", - "description" : "${role_view-groups}", - "composite" : false, - "clientRole" : true, - "containerId" : "b3a22454-d780-4093-8333-9be6f6cd5855", - "attributes" : { } - }, { - "id" : "1cf27d94-d88d-42d3-b8f3-ede1f127ac45", - "name" : "manage-consent", - "description" : "${role_manage-consent}", - "composite" : true, - "composites" : { - "client" : { - "account" : [ "view-consent" ] - } + { + "id": "714243ae-72cc-4c26-842a-047357b5919a", + "clientId": "dwengo", + "name": "Dwengo", + "description": "", + "rootUrl": "http://localhost:5173", + "adminUrl": "http://localhost:5173", + "baseUrl": "/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-jwt", + "redirectUris": ["urn:ietf:wg:oauth:2.0:oob", "http://localhost:5173/*", "http://localhost:5173"], + "webOrigins": ["+"], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": true, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": true, + "protocol": "openid-connect", + "attributes": { + "realm_client": "false", + "oidc.ciba.grant.enabled": "false", + "client.secret.creation.time": "1740860818", + "backchannel.logout.session.required": "true", + "token.endpoint.auth.signing.alg": "RS256", + "post.logout.redirect.uris": "+", + "frontchannel.logout.session.required": "true", + "oauth2.device.authorization.grant.enabled": "false", + "display.on.consent.screen": "false", + "backchannel.logout.revoke.offline.tokens": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": ["web-origins", "acr", "profile", "roles", "basic", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "organization", "microprofile-jwt"] }, - "clientRole" : true, - "containerId" : "b3a22454-d780-4093-8333-9be6f6cd5855", - "attributes" : { } - } ] + { + "id": "0b06aaa3-717d-4a52-ab46-295a6571b642", + "clientId": "realm-management", + "name": "${client_realm-management}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "realm_client": "true" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": ["web-origins", "acr", "profile", "roles", "basic", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "organization", "microprofile-jwt"] + }, + { + "id": "dfc7248c-3794-4e3b-aed2-3ee553cd0feb", + "clientId": "security-admin-console", + "name": "${client_security-admin-console}", + "rootUrl": "${authAdminUrl}", + "baseUrl": "/admin/student/console/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": ["/admin/student/console/*"], + "webOrigins": ["+"], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "realm_client": "false", + "client.use.lightweight.access.token.enabled": "true", + "post.logout.redirect.uris": "+", + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "9e9ff295-30c9-43f1-a11a-773724709c07", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": ["web-origins", "acr", "profile", "roles", "basic", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "organization", "microprofile-jwt"] + } + ], + "clientScopes": [ + { + "id": "0721b27a-284f-4e6d-af70-b6f190ebdcd4", + "name": "email", + "description": "OpenID Connect built-in scope: email", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "consent.screen.text": "${emailScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "d256bdc1-8983-41e0-b8fa-fcf45653045e", + "name": "email verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "emailVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email_verified", + "jsonType.label": "boolean" + } + }, + { + "id": "651c2415-db30-40ed-bdef-745b6ea744ed", + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "573f6eea-7626-44fe-9855-50f15c3939ba", + "name": "web-origins", + "description": "OpenID Connect scope for add allowed web origins to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "consent.screen.text": "", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "3489c748-3cc7-4350-9351-2955fc7084ba", + "name": "allowed web origins", + "protocol": "openid-connect", + "protocolMapper": "oidc-allowed-origins-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "access.token.claim": "true" + } + } + ] + }, + { + "id": "00afe548-c677-4595-8478-16f752c2713a", + "name": "offline_access", + "description": "OpenID Connect built-in scope: offline_access", + "protocol": "openid-connect", + "attributes": { + "consent.screen.text": "${offlineAccessScopeConsentText}", + "display.on.consent.screen": "true" + } + }, + { + "id": "1448ed2b-ec1d-4bf4-a8b7-00cb85459289", + "name": "address", + "description": "OpenID Connect built-in scope: address", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "consent.screen.text": "${addressScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "12d491b6-5d74-4168-ac5c-517ebc2f1de4", + "name": "address", + "protocol": "openid-connect", + "protocolMapper": "oidc-address-mapper", + "consentRequired": false, + "config": { + "user.attribute.formatted": "formatted", + "user.attribute.country": "country", + "introspection.token.claim": "true", + "user.attribute.postal_code": "postal_code", + "userinfo.token.claim": "true", + "user.attribute.street": "street", + "id.token.claim": "true", + "user.attribute.region": "region", + "access.token.claim": "true", + "user.attribute.locality": "locality" + } + } + ] + }, + { + "id": "52223fb1-9651-4cdf-8317-a1301d4042f7", + "name": "organization", + "description": "Additional claims about the organization a subject belongs to", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "consent.screen.text": "${organizationScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "dccc4214-ece6-4235-8119-ee8cb954c29a", + "name": "organization", + "protocol": "openid-connect", + "protocolMapper": "oidc-organization-membership-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true", + "claim.name": "organization", + "jsonType.label": "String", + "multivalued": "true" + } + } + ] + }, + { + "id": "8be22542-e327-4a25-8265-a34a29607d1b", + "name": "service_account", + "description": "Specific scope for a client enabled for service accounts", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "cf89064a-0af3-4a4b-a838-3528a8f4d780", + "name": "Client IP Address", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientAddress", + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientAddress", + "jsonType.label": "String" + } + }, + { + "id": "dc0f77e6-cc20-4c0a-baf3-f45046d749d1", + "name": "Client ID", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "client_id", + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true", + "claim.name": "client_id", + "jsonType.label": "String" + } + }, + { + "id": "d63fd29a-3613-4529-a8e4-3a7d7e9f5802", + "name": "Client Host", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientHost", + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientHost", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "d9079603-62b7-4680-9d01-950daae75d6b", + "name": "saml_organization", + "description": "Organization Membership", + "protocol": "saml", + "attributes": { + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "d826fc58-b006-49ad-93dc-a76700e800df", + "name": "organization", + "protocol": "saml", + "protocolMapper": "saml-organization-membership-mapper", + "consentRequired": false, + "config": {} + } + ] + }, + { + "id": "171d8267-87da-4a4b-9346-d901d470248b", + "name": "phone", + "description": "OpenID Connect built-in scope: phone", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "consent.screen.text": "${phoneScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "f8bb18d4-af9d-49b0-a61f-cc81887870cd", + "name": "phone number", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "phoneNumber", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number", + "jsonType.label": "String" + } + }, + { + "id": "88a2c658-9b61-40a2-abd5-69c501286031", + "name": "phone number verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "phoneNumberVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number_verified", + "jsonType.label": "boolean" + } + } + ] + }, + { + "id": "ea3b84ac-a91f-4a3d-be4e-893e11eaf4a1", + "name": "acr", + "description": "OpenID Connect scope for add acr (authentication context class reference) to the token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "152d66d4-524f-47f1-a592-be3a0c043a4f", + "name": "acr loa level", + "protocol": "openid-connect", + "protocolMapper": "oidc-acr-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true" + } + } + ] + }, + { + "id": "2fc1ad0d-1065-4196-8d1b-c61525c9425d", + "name": "microprofile-jwt", + "description": "Microprofile - JWT built-in scope", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "9d537486-f6bf-4856-91fc-ca3acaa78814", + "name": "upn", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "upn", + "jsonType.label": "String" + } + }, + { + "id": "55425438-4111-47a0-9a36-fec9dbbc6a8a", + "name": "groups", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "multivalued": "true", + "user.attribute": "foo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "groups", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "0d186f4e-ef6d-4fbc-9593-081e0d5ad171", + "name": "profile", + "description": "OpenID Connect built-in scope: profile", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "consent.screen.text": "${profileScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "bb8bb550-2db6-4631-97dc-1d115d0e3034", + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "id": "c942089b-2898-4052-a64d-85b61e27aaa4", + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "id": "5ff3a9ca-7036-458c-b0dc-41216292d210", + "name": "updated at", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "updatedAt", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "updated_at", + "jsonType.label": "long" + } + }, + { + "id": "41f93d62-4074-4373-a270-9bdf1e298cb5", + "name": "website", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "website", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "website", + "jsonType.label": "String" + } + }, + { + "id": "ffec7d63-0f78-41ea-8023-6c7c64661b34", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + }, + { + "id": "4a514ae7-d29f-4979-8df9-a97b36a81a96", + "name": "profile", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "profile", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "profile", + "jsonType.label": "String" + } + }, + { + "id": "286e349b-cb9f-41b1-b9dc-d787f13e9d99", + "name": "nickname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "nickname", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "nickname", + "jsonType.label": "String" + } + }, + { + "id": "f5177603-55b1-4abe-aee6-b1e5a05e37f6", + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "id": "a31114d7-05fc-40c1-9ea8-6977f6f0bec5", + "name": "zoneinfo", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "zoneinfo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "zoneinfo", + "jsonType.label": "String" + } + }, + { + "id": "8884be77-648d-4083-b0cf-57130162c8dc", + "name": "gender", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "gender", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "gender", + "jsonType.label": "String" + } + }, + { + "id": "61840434-c79f-455a-a914-117977197304", + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "id": "1f40ff0b-1664-4259-846b-ab707c76d33b", + "name": "middle name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "middleName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "middle_name", + "jsonType.label": "String" + } + }, + { + "id": "8534d400-8a81-4ae3-b51f-78b93e5a2045", + "name": "picture", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "picture", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "picture", + "jsonType.label": "String" + } + }, + { + "id": "82a0e240-0824-41b9-b6e8-856a72d1e930", + "name": "birthdate", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "birthdate", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "birthdate", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "a5cedc85-d9e9-42e1-9ea3-ff37d21d5e27", + "name": "role_list", + "description": "SAML role list", + "protocol": "saml", + "attributes": { + "consent.screen.text": "${samlRoleListScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "19009128-590f-4bc9-80de-c9ba4aae822d", + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + } + ] + }, + { + "id": "3b6bb88b-c833-4bb5-9bd0-95831aa2ad0d", + "name": "basic", + "description": "OpenID Connect scope for add all basic claims to the token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "ce925803-aec2-47cb-a3b9-4bef12c80367", + "name": "sub", + "protocol": "openid-connect", + "protocolMapper": "oidc-sub-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "access.token.claim": "true" + } + }, + { + "id": "58729b3a-3816-460e-bf2e-d0d2206c1830", + "name": "auth_time", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "AUTH_TIME", + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true", + "claim.name": "auth_time", + "jsonType.label": "long" + } + } + ] + }, + { + "id": "7aa2d936-3edb-45e5-bae0-b4a618d06371", + "name": "roles", + "description": "OpenID Connect scope for add user roles to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "consent.screen.text": "${rolesScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "a9d1e8e2-ca10-4904-8a42-7708b0bfdefa", + "name": "client roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-client-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "introspection.token.claim": "true", + "access.token.claim": "true", + "claim.name": "resource_access.${client_id}.roles", + "jsonType.label": "String", + "multivalued": "true" + } + }, + { + "id": "1f217073-ad43-483b-b0d5-f3ca4c74282f", + "name": "realm roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "introspection.token.claim": "true", + "access.token.claim": "true", + "claim.name": "realm_access.roles", + "jsonType.label": "String", + "multivalued": "true" + } + }, + { + "id": "61b0a069-8b67-4692-bcca-66a197b230eb", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "access.token.claim": "true" + } + } + ] + } + ], + "defaultDefaultClientScopes": ["role_list", "saml_organization", "profile", "email", "roles", "web-origins", "acr", "basic"], + "defaultOptionalClientScopes": ["offline_access", "address", "phone", "microprofile-jwt", "organization"], + "browserSecurityHeaders": { + "contentSecurityPolicyReportOnly": "", + "xContentTypeOptions": "nosniff", + "referrerPolicy": "no-referrer", + "xRobotsTag": "none", + "xFrameOptions": "SAMEORIGIN", + "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "xXSSProtection": "1; mode=block", + "strictTransportSecurity": "max-age=31536000; includeSubDomains" + }, + "smtpServer": {}, + "eventsEnabled": false, + "eventsListeners": ["jboss-logging"], + "enabledEventTypes": [], + "adminEventsEnabled": false, + "adminEventsDetailsEnabled": false, + "identityProviders": [], + "identityProviderMappers": [], + "components": { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ + { + "id": "9eac5531-7f25-493f-a721-6c5e65cd34c2", + "name": "Max Clients Limit", + "providerId": "max-clients", + "subType": "anonymous", + "subComponents": {}, + "config": { + "max-clients": ["200"] + } + }, + { + "id": "d9319a22-4c67-4b08-822f-4162a1ee01bc", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allow-default-scopes": ["true"] + } + }, + { + "id": "21456c8e-7f6b-4e49-a3e1-bea7f900e2fb", + "name": "Consent Required", + "providerId": "consent-required", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "4872e99b-b55b-4e13-8a93-63e853289cac", + "name": "Full Scope Disabled", + "providerId": "scope", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "a118a194-09f5-435d-9d4b-363813413167", + "name": "Trusted Hosts", + "providerId": "trusted-hosts", + "subType": "anonymous", + "subComponents": {}, + "config": { + "host-sending-registration-request-must-match": ["true"], + "client-uris-must-match": ["true"] + } + }, + { + "id": "e32b1e26-6571-4b0c-a205-0fbb3de44384", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allow-default-scopes": ["true"] + } + }, + { + "id": "9dbe6752-9978-42a3-9210-9ec166140de2", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-usermodel-attribute-mapper", + "oidc-sha256-pairwise-sub-mapper", + "oidc-address-mapper", + "saml-role-list-mapper", + "saml-user-property-mapper", + "oidc-usermodel-property-mapper", + "oidc-full-name-mapper", + "saml-user-attribute-mapper" + ] + } + }, + { + "id": "7027b3f4-d877-4814-ac78-f1edb8eb89b0", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "saml-role-list-mapper", + "oidc-sha256-pairwise-sub-mapper", + "oidc-address-mapper", + "saml-user-property-mapper", + "oidc-usermodel-attribute-mapper", + "saml-user-attribute-mapper", + "oidc-usermodel-property-mapper", + "oidc-full-name-mapper" + ] + } + } + ], + "org.keycloak.keys.KeyProvider": [ + { + "id": "819cfc66-a997-4747-9d90-a7f0c09774bf", + "name": "aes-generated", + "providerId": "aes-generated", + "subComponents": {}, + "config": { + "kid": ["eb74df73-3f34-457d-95c7-5ad909107703"], + "secret": ["1K8IJiDODmotHJPStrXhtA"], + "priority": ["100"] + } + }, + { + "id": "299857cd-52a4-4981-8171-02e7d8f12960", + "name": "rsa-generated", + "providerId": "rsa-generated", + "subComponents": {}, + "config": { + "privateKey": [ + "MIIEpAIBAAKCAQEA1MRmAT/yImkVfPMBxC0QHdC4DQfuWUTjKeEku+gMI9jX5ChUzzzVugcvZWmxBNcvOz7p6R8EdPllZKIwFSH5WvQ1w1VIgQwIlYfpi/pknfftLd66MI2fXrQK19dRTeQRivEf39GTfBQ2Xc7y1q7zbMo5TVxATJ3DgPi13dYO7zVPpGTiQQeYiezlcBedyGe4cS1g6oBoaVif1QPY1Ni2vEjJhczNMGI408tIFws8G04Tlno814nT0ysdflUSGcRUku41NtfM9hr57LQ459sGYho8Pn11lDuiUWkomJv0y3GJ1wFBvQbDvI+6QvEdFu0GxShrlcORrNmj3BwOOLhB7wIDAQABAoIBAA6zrXq7oO8YxMfYANC97mWpBPa9jA42EN5VdNTZIXGeq7hTwxx4zynmEjPXPEih190nqUEXCBdPHl74SAtFyDWtN0PSkkp8euFePViTSj2SIpzvTX1KY+9G0JL+iVsw/bdUlwe/swm5WdJcmPIVr7NeO9xpGfZRVm+EgAieoHSN4Z7g20wLbVz1fya+6O5Hy+IGezamIA4tchk+4hyiVpSh2TcdjkJJZWOlHKPkwWU/MYQbJibuea5jLoWA39NIqV2l5GT0SoCbffGJNb9CMTTGmXoK5zNwHhG+M0a4eP1vbFDLaoDne86JySmTdv/WrTFFa3veelw2K8PHDybuB70CgYEA/gbxqLZYkJcEpqsjM/XcISFJ09icJLKl5r2l/Dm4Qq587QniQYribX/PPLfDhgVwPByQe3rccq9FoiILycTdIwgSMTsg5fzvbLJTqMAcl2r0zJgHVIDc6iXnytuE0FffKN0kSKL1C4d6n6vKoCGvOcZoXK5jxgzpY8lasvKxhCsCgYEA1mtr7CDYY3qPmTu4/Uz6cFgX8RDMZZ11AQQXNMsKHIu5C4xLeYmJMlpt0y4h52/NWRzh2svdw3SEZTCfP1WVC7StfP8KD8QdwVkQlY5EGkiz9uRtEgwk8chkOTIm2JedeRL6YWlTgnH9PIuGq84OOnEbFjVN3Lbx3N1QuQfVA00CgYEAybA1uuBcXSCqfrIuVxkD2AIYHe1DvBdjhVpaKXKii78CTSmlzKg6svnhTrIQuZ4jyHZdeMzJrvzeaqZheaemdCP6XcA2lKRIbKMBrWAq00YGa1LhrwRJYlcKPJQiVVEPS+CY6FsJ+Edu4suBK7bS6ypOvhdv/FVQEPxT2PS8YNUCgYEAxwJ+8XNuw63ud9+Zi+gVjY4F8qWPwESLYz0DuOk2YlZAknpNVumTYBvUUSxBJYh8RFhtO+D53D5Z331oYKUzJ+EzII+qLAXvRBRBMz4O8YJHHkDXBugkphBDDV8B9QeLjeNSZnUWoDziOH6bqPwf8pgl9s/Ui6V1CHSVRpcBWwUCgYA2kMgu7qS5kLtUWySPzW4nTKwhN+HFTIbRrNrECxXmxroigTEyfBFuNR5QaeYYrAtqgY1m5Lev//2GnWM7dAr7hewj6qfGszrvegHsqMs4cakVqEOtbrWxL+WtWPaIdjJ+x7ZoMnZxZDg3ysemybNHHwSyBsp1TDc+glzmMtJtLA==" + ], + "keyUse": ["SIG"], + "certificate": [ + "MIICnTCCAYUCBgGVUbFIeTANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdzdHVkZW50MB4XDTI1MDMwMTEyMzAyN1oXDTM1MDMwMTEyMzIwN1owEjEQMA4GA1UEAwwHc3R1ZGVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANTEZgE/8iJpFXzzAcQtEB3QuA0H7llE4ynhJLvoDCPY1+QoVM881boHL2VpsQTXLzs+6ekfBHT5ZWSiMBUh+Vr0NcNVSIEMCJWH6Yv6ZJ337S3eujCNn160CtfXUU3kEYrxH9/Rk3wUNl3O8tau82zKOU1cQEydw4D4td3WDu81T6Rk4kEHmIns5XAXnchnuHEtYOqAaGlYn9UD2NTYtrxIyYXMzTBiONPLSBcLPBtOE5Z6PNeJ09MrHX5VEhnEVJLuNTbXzPYa+ey0OOfbBmIaPD59dZQ7olFpKJib9MtxidcBQb0Gw7yPukLxHRbtBsUoa5XDkazZo9wcDji4Qe8CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAedqvKOBsz4IKKzkWHIQnN5H8dQKnuPUMdWewOwmMGIUdBU9k6aS+y+BB7mugF/Hnr8Lw5d2AHwVLj2VyP4Pq0d2My3Ihxi0vr6sSfxVHuD9y/a7FxDGVTkCvmy5DOmpF/kdNnL9xG5ZivHaucnrIHHGMcQCdbWAaac0qPZihv9pdMZFMtI3aiBO5jVJ7KP8iLNKsshg60mxCOPzauMVXi+rqqqhGAgMKAL4hjjvdIKTLWwmthnmAlGqlTk/7H82hS9aKygufXszXWdFAYhX/r8/hjyc+6zJUvkG20uRWnkR35gya7jQoZ2O6OvkQf0mgSvzgIP3xoYV2uKYD03wINg==" + ], + "priority": ["100"] + } + }, + { + "id": "3d6bfeeb-fa86-435e-8c39-6f547a0f4a38", + "name": "hmac-generated-hs512", + "providerId": "hmac-generated", + "subComponents": {}, + "config": { + "kid": ["176e970f-5915-4d27-8233-8fab6d7ad947"], + "secret": [ + "sXeOdtyIPpH_kcZWikHFjTur9yWok0QUwKi95l8wHp6kTVX9vhoZL2siNHRoFnn8tFgT4JZbR0bMsD57qAXlmVjA830Ny_GZdhL_PFWQh7JYMEJrl-1nyLy_SReQXRtq_q9tKUafUZqeYSKBlUYZ7D4jNRJ4-uniq80Ger-4ee0" + ], + "priority": ["100"], + "algorithm": ["HS512"] + } + }, + { + "id": "df1247b5-041e-4ae8-b7fc-26c4b6f5ff67", + "name": "rsa-enc-generated", + "providerId": "rsa-enc-generated", + "subComponents": {}, + "config": { + "privateKey": [ + "MIIEpAIBAAKCAQEAq0piY/PaZh1IX46e0G6tCPtfRx7Q8zGslOFSBLR9PNAdSlBpYiV9kOpN1kTK2Sca5j4yyO9HAFK5j+zh/cy8SsP1iyuI0sCPG7NMKV1pP5Y753wTC0lTq8z16bvXPvPYrPRKgGDmU7Ww0/WD6/P+z9Li/+ujFHzzzfoPuQvbBhma6A4oadsC+zun/mWCyiD14mB+X00BeAIsxKJZ/Sd+U4lMkkvmpoXyx61xK+j48EAZ18u7FprlvUjgGzzAmm/K6O/fHPIw5eViVly7aj5gjh67ntSuZArVtrxy/Py5V4hkSO8guXKqNz3liJvLbFCqqpfTR/0duArZR0xcnaGc6wIDAQABAoIBAFRuu5YaXxbDq2eS5RzH2Vpakin7+jJOU4wljujL0QnXagC2J2QeJ8l1fT23tieZO4yvrxfVvnFd1aMouHMC5vORqWja4jxEd6ZHWKzxIw6ZbtjZk4eWMvy18KewlFavGyiR2GF0okQ0BMBOPqNhp8JoaMWOsNnKB+GJuBNWUTWtPWNQlbaeI+uIgFywrvZOcQuWqU+9Y5rQa7oKZisufu+z0vd9XyvjXQ/Thnuu9/k1m88EMAMS63zwfIOZm35PPqh1/6aBBcRWquT1X2S8g2hwmMLZJgU91yKtQcIujHXAcvxeK72/dcm8NU6AxM+8aj+821TvcNzJi7he5SGcin0CgYEA4cuxwqRixXz6yDcTv9GpJMULbJGjA+Qf/iSfT+ftBeKbnKgZGzHwOCTu5DMkag3BPjclut4sEt3QPf1cFv5vZvdkOnPeaFxrtoMhSz8ssh8qaOsObCwicel1zdPVTmMw7YzEZV14fdIq3lkHsLy2uWa0imRH0l4xTccmsJiPtU8CgYEAwjQtspxOejCyME+M+hcU3RelD6kaMjICuWGJj8g0OpqdHM7iNVJq78fOlWjntt/ydzfOXVMMVh4AG8dAvlc86iwwsBRsJPVrrrRoSNuAwFbjKisbjlnPbqyclHfUsyQitj19tp//ExH7JaBibzKd6KhqFuQTE1iYLs2mFQAz76UCgYEAsNLu64oGm7frQP345mAPgO8aqjRHIBX3g/Q0GsR61wAGcyElQCnUgHNT7burSa5p5goT7wpsI343xUPzaUJqBY25nRj+VGYEKFL6sM3Rd9B2SuHBUq8hbmmwyraYtiFxwKZbazJO2OHMloHMRvkSc5Dd0/8CS9ld7RYH04Y2DHsCgYEAoKXTK44baP7BWC9mOjc/vgjiNQs4rU8ra7igt7zwX44o63zEKUHNTh7l6DiIfYHRrAcRAahCazaT9makSxAVRs1ZVT7/mq8d7b41Chfx8KmvbuGMAPyQGEhXmoVqAOqigEhrptfBhD/6lkyPQNcJQz2VzOvMT9OYyBa8DWFGlTUCgYAfModz6g2HsYYr37/0ByXHKL0WQQtuAlZzCY9GuDLEok7QLFI/E+bdOHos3goW72Iswo/SO7inlW4S3gojuy+zZhwCO31T9p2Z0Yn0tDK3fkUO32flOLwxCZA99pKkIul3svl6643GqSD1feybmbYRtqoPCTSKSE9vI9T9DkBTvA==" + ], + "keyUse": ["ENC"], + "certificate": [ + "MIICnTCCAYUCBgGVUbFItzANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdzdHVkZW50MB4XDTI1MDMwMTEyMzAyN1oXDTM1MDMwMTEyMzIwN1owEjEQMA4GA1UEAwwHc3R1ZGVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKtKYmPz2mYdSF+OntBurQj7X0ce0PMxrJThUgS0fTzQHUpQaWIlfZDqTdZEytknGuY+MsjvRwBSuY/s4f3MvErD9YsriNLAjxuzTCldaT+WO+d8EwtJU6vM9em71z7z2Kz0SoBg5lO1sNP1g+vz/s/S4v/roxR88836D7kL2wYZmugOKGnbAvs7p/5lgsog9eJgfl9NAXgCLMSiWf0nflOJTJJL5qaF8setcSvo+PBAGdfLuxaa5b1I4Bs8wJpvyujv3xzyMOXlYlZcu2o+YI4eu57UrmQK1ba8cvz8uVeIZEjvILlyqjc95Yiby2xQqqqX00f9HbgK2UdMXJ2hnOsCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAHfum0Ymw/qHTpjNeJjJyh5A1N4Z58m/PjuCXafIdDDjuAYpB3/M4bTGZRUvEvv2RuBNv3rONvMR8dRrZwio0/T0aEXnHrEAaCSfVcMy1To8TGGOzgtPMub4YCqXLMCwW5cwIbqT3P58HEqsqEbv7Zp4LtLYZBYXWDF8vM4zEn3CPYxuxRPKlrBUynRKYcwN7+/dbhJKiARpPMIZ5viGbjaTnNE/d/VFdv1q5xm3ItYnShDyJ0REGN18sWleLI6qkW0X22Gcjn38fWjiXDnF0HQYzC2UzMcEo/iLfPxTKbJnc+PPmnszfmCh7mWs5xVGfMOz/Oy8HI121x1ZSriRktA==" + ], + "priority": ["100"], + "algorithm": ["RSA-OAEP"] + } + } + ] + }, + "internationalizationEnabled": false, + "supportedLocales": [], + "authenticationFlows": [ + { + "id": "f7d1108f-7994-47e5-81e9-1a88cdbe545c", + "alias": "Account verification options", + "description": "Method with which to verity the existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-email-verification", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Verify Existing Account by Re-authentication", + "userSetupAllowed": false + } + ] + }, + { + "id": "cf40a5d3-bec8-4aef-9658-1b88c6cec561", + "alias": "Browser - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "6820625f-5bb5-4fa2-8539-26a8568265c1", + "alias": "Browser - Conditional Organization", + "description": "Flow to determine if the organization identity-first login is to be used", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "organization", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "66d5e52e-592e-4cef-bfa0-512e90b609ec", + "alias": "Direct Grant - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "direct-grant-validate-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "b5bed405-b5f2-4839-861c-612501e4c412", + "alias": "First Broker Login - Conditional Organization", + "description": "Flow to determine if the authenticator that adds organization members is to be used", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "idp-add-organization-member", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "dd786e24-e822-43ec-be03-29874eb73737", + "alias": "First broker login - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "8751572f-623e-4bdc-a02c-e92c15a91143", + "alias": "Handle Existing Account", + "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-confirm-link", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Account verification options", + "userSetupAllowed": false + } + ] + }, + { + "id": "61efadf2-a54e-4071-b8c9-83e094525051", + "alias": "Organization", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 10, + "autheticatorFlow": true, + "flowAlias": "Browser - Conditional Organization", + "userSetupAllowed": false + } + ] + }, + { + "id": "b99c3a7a-8ef7-46b1-b8a1-cb51f8a6e725", + "alias": "Reset - Conditional OTP", + "description": "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "a3bfc2e4-af67-4d3e-851f-3c58bf32be83", + "alias": "User creation or linking", + "description": "Flow for the existing/non-existing user alternatives", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "create unique user config", + "authenticator": "idp-create-user-if-unique", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Handle Existing Account", + "userSetupAllowed": false + } + ] + }, + { + "id": "4cc3bf25-d1b7-43a6-8619-5ed5f2d65aed", + "alias": "Verify Existing Account by Re-authentication", + "description": "Reauthentication of existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "First broker login - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "4e5564ce-87da-4b25-8dcb-062216ceaa8d", + "alias": "browser", + "description": "Browser based authentication", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-cookie", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-spnego", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "identity-provider-redirector", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 25, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 26, + "autheticatorFlow": true, + "flowAlias": "Organization", + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 30, + "autheticatorFlow": true, + "flowAlias": "forms", + "userSetupAllowed": false + } + ] + }, + { + "id": "def90462-5831-4856-b186-05df9e640bbb", + "alias": "clients", + "description": "Base authentication for clients", + "providerId": "client-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "client-secret", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-secret-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-x509", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 40, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "f8c9010d-f197-417b-bda1-2993e1a73a21", + "alias": "direct grant", + "description": "OpenID Connect Resource Owner Grant", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "direct-grant-validate-username", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "direct-grant-validate-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 30, + "autheticatorFlow": true, + "flowAlias": "Direct Grant - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "0fb9e2a4-ea0d-453f-a1fe-f000c849fd66", + "alias": "docker auth", + "description": "Used by Docker clients to authenticate against the IDP", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "docker-http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "79a9efc4-1279-4093-8914-92f4e0b02bb4", + "alias": "first broker login", + "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "review profile config", + "authenticator": "idp-review-profile", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "User creation or linking", + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 50, + "autheticatorFlow": true, + "flowAlias": "First Broker Login - Conditional Organization", + "userSetupAllowed": false + } + ] + }, + { + "id": "f855b3a1-6612-4528-94bc-d0793bfda561", + "alias": "forms", + "description": "Username, password, otp and other auth forms.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Browser - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "fb84970b-6f04-4849-a385-792e17c1b8ce", + "alias": "registration", + "description": "Registration flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-page-form", + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": true, + "flowAlias": "registration form", + "userSetupAllowed": false + } + ] + }, + { + "id": "fcdfd4d4-1c04-487d-aa7c-85e136814274", + "alias": "registration form", + "description": "Registration form", + "providerId": "form-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-user-creation", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-password-action", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 50, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-recaptcha-action", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 60, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-terms-and-conditions", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 70, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "306d8f7d-c12a-46cb-9a68-c6c3f1622f57", + "alias": "reset credentials", + "description": "Reset credentials for a user if they forgot their password or something", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "reset-credentials-choose-user", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-credential-email", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 40, + "autheticatorFlow": true, + "flowAlias": "Reset - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "35a54b09-ff8c-46c4-9f04-1efbb153276c", + "alias": "saml ecp", + "description": "SAML ECP Profile Authentication Flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + } + ], + "authenticatorConfig": [ + { + "id": "fc1b82d7-593d-4906-a4d9-13220b66b7ce", + "alias": "create unique user config", + "config": { + "require.password.update.after.registration": "false" + } + }, + { + "id": "a90543f4-7da7-43bc-8737-7e58dd190014", + "alias": "review profile config", + "config": { + "update.profile.on.first.login": "missing" + } + } + ], + "requiredActions": [ + { + "alias": "CONFIGURE_TOTP", + "name": "Configure OTP", + "providerId": "CONFIGURE_TOTP", + "enabled": true, + "defaultAction": false, + "priority": 10, + "config": {} + }, + { + "alias": "TERMS_AND_CONDITIONS", + "name": "Terms and Conditions", + "providerId": "TERMS_AND_CONDITIONS", + "enabled": false, + "defaultAction": false, + "priority": 20, + "config": {} + }, + { + "alias": "UPDATE_PASSWORD", + "name": "Update Password", + "providerId": "UPDATE_PASSWORD", + "enabled": true, + "defaultAction": false, + "priority": 30, + "config": {} + }, + { + "alias": "UPDATE_PROFILE", + "name": "Update Profile", + "providerId": "UPDATE_PROFILE", + "enabled": true, + "defaultAction": false, + "priority": 40, + "config": {} + }, + { + "alias": "VERIFY_EMAIL", + "name": "Verify Email", + "providerId": "VERIFY_EMAIL", + "enabled": true, + "defaultAction": false, + "priority": 50, + "config": {} + }, + { + "alias": "delete_account", + "name": "Delete Account", + "providerId": "delete_account", + "enabled": false, + "defaultAction": false, + "priority": 60, + "config": {} + }, + { + "alias": "webauthn-register", + "name": "Webauthn Register", + "providerId": "webauthn-register", + "enabled": true, + "defaultAction": false, + "priority": 70, + "config": {} + }, + { + "alias": "webauthn-register-passwordless", + "name": "Webauthn Register Passwordless", + "providerId": "webauthn-register-passwordless", + "enabled": true, + "defaultAction": false, + "priority": 80, + "config": {} + }, + { + "alias": "VERIFY_PROFILE", + "name": "Verify Profile", + "providerId": "VERIFY_PROFILE", + "enabled": true, + "defaultAction": false, + "priority": 90, + "config": {} + }, + { + "alias": "delete_credential", + "name": "Delete Credential", + "providerId": "delete_credential", + "enabled": true, + "defaultAction": false, + "priority": 100, + "config": {} + }, + { + "alias": "update_user_locale", + "name": "Update User Locale", + "providerId": "update_user_locale", + "enabled": true, + "defaultAction": false, + "priority": 1000, + "config": {} + } + ], + "browserFlow": "browser", + "registrationFlow": "registration", + "directGrantFlow": "direct grant", + "resetCredentialsFlow": "reset credentials", + "clientAuthenticationFlow": "clients", + "dockerAuthenticationFlow": "docker auth", + "firstBrokerLoginFlow": "first broker login", + "attributes": { + "cibaBackchannelTokenDeliveryMode": "poll", + "cibaExpiresIn": "120", + "cibaAuthRequestedUserHint": "login_hint", + "oauth2DeviceCodeLifespan": "600", + "oauth2DevicePollingInterval": "5", + "parRequestUriLifespan": "60", + "cibaInterval": "5", + "realmReusableOtpCode": "false" + }, + "keycloakVersion": "26.1.3", + "userManagedAccessAllowed": false, + "organizationsEnabled": false, + "verifiableCredentialsEnabled": false, + "adminPermissionsEnabled": false, + "clientProfiles": { + "profiles": [] + }, + "clientPolicies": { + "policies": [] } - }, - "groups" : [ ], - "defaultRole" : { - "id" : "b3bf9566-098c-4167-9cce-f64c720ca511", - "name" : "default-roles-student", - "description" : "${role_default-roles}", - "composite" : true, - "clientRole" : false, - "containerId" : "08a7ab0a-d483-4103-a781-76013864bf50" - }, - "requiredCredentials" : [ "password" ], - "otpPolicyType" : "totp", - "otpPolicyAlgorithm" : "HmacSHA1", - "otpPolicyInitialCounter" : 0, - "otpPolicyDigits" : 6, - "otpPolicyLookAheadWindow" : 1, - "otpPolicyPeriod" : 30, - "otpPolicyCodeReusable" : false, - "otpSupportedApplications" : [ "totpAppFreeOTPName", "totpAppGoogleName", "totpAppMicrosoftAuthenticatorName" ], - "localizationTexts" : { }, - "webAuthnPolicyRpEntityName" : "keycloak", - "webAuthnPolicySignatureAlgorithms" : [ "ES256", "RS256" ], - "webAuthnPolicyRpId" : "", - "webAuthnPolicyAttestationConveyancePreference" : "not specified", - "webAuthnPolicyAuthenticatorAttachment" : "not specified", - "webAuthnPolicyRequireResidentKey" : "not specified", - "webAuthnPolicyUserVerificationRequirement" : "not specified", - "webAuthnPolicyCreateTimeout" : 0, - "webAuthnPolicyAvoidSameAuthenticatorRegister" : false, - "webAuthnPolicyAcceptableAaguids" : [ ], - "webAuthnPolicyExtraOrigins" : [ ], - "webAuthnPolicyPasswordlessRpEntityName" : "keycloak", - "webAuthnPolicyPasswordlessSignatureAlgorithms" : [ "ES256", "RS256" ], - "webAuthnPolicyPasswordlessRpId" : "", - "webAuthnPolicyPasswordlessAttestationConveyancePreference" : "not specified", - "webAuthnPolicyPasswordlessAuthenticatorAttachment" : "not specified", - "webAuthnPolicyPasswordlessRequireResidentKey" : "not specified", - "webAuthnPolicyPasswordlessUserVerificationRequirement" : "not specified", - "webAuthnPolicyPasswordlessCreateTimeout" : 0, - "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister" : false, - "webAuthnPolicyPasswordlessAcceptableAaguids" : [ ], - "webAuthnPolicyPasswordlessExtraOrigins" : [ ], - "users" : [ { - "id" : "79e9a395-d7e4-48c9-a06e-702435bae290", - "username" : "testleerling1", - "firstName" : "Gerald", - "lastName" : "Schmittinger", - "email" : "Gerald.Schmittinger@UGent.be", - "emailVerified" : false, - "createdTimestamp" : 1740858528405, - "enabled" : true, - "totp" : false, - "credentials" : [ { - "id" : "c31a708f-8614-4144-a25f-3e976c9035ce", - "type" : "password", - "userLabel" : "My password", - "createdDate" : 1740858548515, - "secretData" : "{\"value\":\"yDKIAbZPuVXBGk4zjiqE/YFcPDm1vjXLwTrPUrvMhXY=\",\"salt\":\"tYvjd4mhV2UWeOUssK01Cw==\",\"additionalParameters\":{}}", - "credentialData" : "{\"hashIterations\":5,\"algorithm\":\"argon2\",\"additionalParameters\":{\"hashLength\":[\"32\"],\"memory\":[\"7168\"],\"type\":[\"id\"],\"version\":[\"1.3\"],\"parallelism\":[\"1\"]}}" - } ], - "disableableCredentialTypes" : [ ], - "requiredActions" : [ ], - "realmRoles" : [ "default-roles-student" ], - "notBefore" : 0, - "groups" : [ ] - } ], - "scopeMappings" : [ { - "clientScope" : "offline_access", - "roles" : [ "offline_access" ] - } ], - "clientScopeMappings" : { - "account" : [ { - "client" : "account-console", - "roles" : [ "manage-account", "view-groups" ] - } ] - }, - "clients" : [ { - "id" : "b3a22454-d780-4093-8333-9be6f6cd5855", - "clientId" : "account", - "name" : "${client_account}", - "rootUrl" : "${authBaseUrl}", - "baseUrl" : "/realms/student/account/", - "surrogateAuthRequired" : false, - "enabled" : true, - "alwaysDisplayInConsole" : false, - "clientAuthenticatorType" : "client-secret", - "redirectUris" : [ "/realms/student/account/*" ], - "webOrigins" : [ ], - "notBefore" : 0, - "bearerOnly" : false, - "consentRequired" : false, - "standardFlowEnabled" : true, - "implicitFlowEnabled" : false, - "directAccessGrantsEnabled" : false, - "serviceAccountsEnabled" : false, - "publicClient" : true, - "frontchannelLogout" : false, - "protocol" : "openid-connect", - "attributes" : { - "realm_client" : "false", - "post.logout.redirect.uris" : "+" - }, - "authenticationFlowBindingOverrides" : { }, - "fullScopeAllowed" : false, - "nodeReRegistrationTimeout" : 0, - "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "basic", "email" ], - "optionalClientScopes" : [ "address", "phone", "offline_access", "organization", "microprofile-jwt" ] - }, { - "id" : "854c221b-630c-4cc3-9365-bd254246dd69", - "clientId" : "account-console", - "name" : "${client_account-console}", - "rootUrl" : "${authBaseUrl}", - "baseUrl" : "/realms/student/account/", - "surrogateAuthRequired" : false, - "enabled" : true, - "alwaysDisplayInConsole" : false, - "clientAuthenticatorType" : "client-secret", - "redirectUris" : [ "/realms/student/account/*" ], - "webOrigins" : [ ], - "notBefore" : 0, - "bearerOnly" : false, - "consentRequired" : false, - "standardFlowEnabled" : true, - "implicitFlowEnabled" : false, - "directAccessGrantsEnabled" : false, - "serviceAccountsEnabled" : false, - "publicClient" : true, - "frontchannelLogout" : false, - "protocol" : "openid-connect", - "attributes" : { - "realm_client" : "false", - "post.logout.redirect.uris" : "+", - "pkce.code.challenge.method" : "S256" - }, - "authenticationFlowBindingOverrides" : { }, - "fullScopeAllowed" : false, - "nodeReRegistrationTimeout" : 0, - "protocolMappers" : [ { - "id" : "f33b40fe-bb9e-4254-ada9-f98dd203641b", - "name" : "audience resolve", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-audience-resolve-mapper", - "consentRequired" : false, - "config" : { } - } ], - "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "basic", "email" ], - "optionalClientScopes" : [ "address", "phone", "offline_access", "organization", "microprofile-jwt" ] - }, { - "id" : "9449aa8b-d5cc-4b9f-bb01-be1e5a896f2f", - "clientId" : "admin-cli", - "name" : "${client_admin-cli}", - "surrogateAuthRequired" : false, - "enabled" : true, - "alwaysDisplayInConsole" : false, - "clientAuthenticatorType" : "client-secret", - "redirectUris" : [ ], - "webOrigins" : [ ], - "notBefore" : 0, - "bearerOnly" : false, - "consentRequired" : false, - "standardFlowEnabled" : false, - "implicitFlowEnabled" : false, - "directAccessGrantsEnabled" : true, - "serviceAccountsEnabled" : false, - "publicClient" : true, - "frontchannelLogout" : false, - "protocol" : "openid-connect", - "attributes" : { - "realm_client" : "false", - "client.use.lightweight.access.token.enabled" : "true" - }, - "authenticationFlowBindingOverrides" : { }, - "fullScopeAllowed" : true, - "nodeReRegistrationTimeout" : 0, - "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "basic", "email" ], - "optionalClientScopes" : [ "address", "phone", "offline_access", "organization", "microprofile-jwt" ] - }, { - "id" : "befe3d72-8102-49a6-8268-bce6def58159", - "clientId" : "broker", - "name" : "${client_broker}", - "surrogateAuthRequired" : false, - "enabled" : true, - "alwaysDisplayInConsole" : false, - "clientAuthenticatorType" : "client-secret", - "redirectUris" : [ ], - "webOrigins" : [ ], - "notBefore" : 0, - "bearerOnly" : true, - "consentRequired" : false, - "standardFlowEnabled" : true, - "implicitFlowEnabled" : false, - "directAccessGrantsEnabled" : false, - "serviceAccountsEnabled" : false, - "publicClient" : false, - "frontchannelLogout" : false, - "protocol" : "openid-connect", - "attributes" : { - "realm_client" : "true" - }, - "authenticationFlowBindingOverrides" : { }, - "fullScopeAllowed" : false, - "nodeReRegistrationTimeout" : 0, - "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "basic", "email" ], - "optionalClientScopes" : [ "address", "phone", "offline_access", "organization", "microprofile-jwt" ] - }, { - "id" : "714243ae-72cc-4c26-842a-047357b5919a", - "clientId" : "dwengo", - "name" : "Dwengo", - "description" : "", - "rootUrl" : "http://localhost:5173", - "adminUrl" : "http://localhost:5173", - "baseUrl" : "/", - "surrogateAuthRequired" : false, - "enabled" : true, - "alwaysDisplayInConsole" : false, - "clientAuthenticatorType" : "client-jwt", - "redirectUris" : [ "urn:ietf:wg:oauth:2.0:oob", "http://localhost:5173/*", "http://localhost:5173" ], - "webOrigins" : [ "+" ], - "notBefore" : 0, - "bearerOnly" : false, - "consentRequired" : false, - "standardFlowEnabled" : true, - "implicitFlowEnabled" : true, - "directAccessGrantsEnabled" : false, - "serviceAccountsEnabled" : false, - "publicClient" : true, - "frontchannelLogout" : true, - "protocol" : "openid-connect", - "attributes" : { - "realm_client" : "false", - "oidc.ciba.grant.enabled" : "false", - "client.secret.creation.time" : "1740860818", - "backchannel.logout.session.required" : "true", - "token.endpoint.auth.signing.alg" : "RS256", - "post.logout.redirect.uris" : "+", - "frontchannel.logout.session.required" : "true", - "oauth2.device.authorization.grant.enabled" : "false", - "display.on.consent.screen" : "false", - "backchannel.logout.revoke.offline.tokens" : "false" - }, - "authenticationFlowBindingOverrides" : { }, - "fullScopeAllowed" : true, - "nodeReRegistrationTimeout" : -1, - "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "basic", "email" ], - "optionalClientScopes" : [ "address", "phone", "offline_access", "organization", "microprofile-jwt" ] - }, { - "id" : "0b06aaa3-717d-4a52-ab46-295a6571b642", - "clientId" : "realm-management", - "name" : "${client_realm-management}", - "surrogateAuthRequired" : false, - "enabled" : true, - "alwaysDisplayInConsole" : false, - "clientAuthenticatorType" : "client-secret", - "redirectUris" : [ ], - "webOrigins" : [ ], - "notBefore" : 0, - "bearerOnly" : true, - "consentRequired" : false, - "standardFlowEnabled" : true, - "implicitFlowEnabled" : false, - "directAccessGrantsEnabled" : false, - "serviceAccountsEnabled" : false, - "publicClient" : false, - "frontchannelLogout" : false, - "protocol" : "openid-connect", - "attributes" : { - "realm_client" : "true" - }, - "authenticationFlowBindingOverrides" : { }, - "fullScopeAllowed" : false, - "nodeReRegistrationTimeout" : 0, - "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "basic", "email" ], - "optionalClientScopes" : [ "address", "phone", "offline_access", "organization", "microprofile-jwt" ] - }, { - "id" : "dfc7248c-3794-4e3b-aed2-3ee553cd0feb", - "clientId" : "security-admin-console", - "name" : "${client_security-admin-console}", - "rootUrl" : "${authAdminUrl}", - "baseUrl" : "/admin/student/console/", - "surrogateAuthRequired" : false, - "enabled" : true, - "alwaysDisplayInConsole" : false, - "clientAuthenticatorType" : "client-secret", - "redirectUris" : [ "/admin/student/console/*" ], - "webOrigins" : [ "+" ], - "notBefore" : 0, - "bearerOnly" : false, - "consentRequired" : false, - "standardFlowEnabled" : true, - "implicitFlowEnabled" : false, - "directAccessGrantsEnabled" : false, - "serviceAccountsEnabled" : false, - "publicClient" : true, - "frontchannelLogout" : false, - "protocol" : "openid-connect", - "attributes" : { - "realm_client" : "false", - "client.use.lightweight.access.token.enabled" : "true", - "post.logout.redirect.uris" : "+", - "pkce.code.challenge.method" : "S256" - }, - "authenticationFlowBindingOverrides" : { }, - "fullScopeAllowed" : true, - "nodeReRegistrationTimeout" : 0, - "protocolMappers" : [ { - "id" : "9e9ff295-30c9-43f1-a11a-773724709c07", - "name" : "locale", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "locale", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "locale", - "jsonType.label" : "String" - } - } ], - "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "basic", "email" ], - "optionalClientScopes" : [ "address", "phone", "offline_access", "organization", "microprofile-jwt" ] - } ], - "clientScopes" : [ { - "id" : "0721b27a-284f-4e6d-af70-b6f190ebdcd4", - "name" : "email", - "description" : "OpenID Connect built-in scope: email", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "true", - "consent.screen.text" : "${emailScopeConsentText}", - "display.on.consent.screen" : "true" - }, - "protocolMappers" : [ { - "id" : "d256bdc1-8983-41e0-b8fa-fcf45653045e", - "name" : "email verified", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-property-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "emailVerified", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "email_verified", - "jsonType.label" : "boolean" - } - }, { - "id" : "651c2415-db30-40ed-bdef-745b6ea744ed", - "name" : "email", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "email", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "email", - "jsonType.label" : "String" - } - } ] - }, { - "id" : "573f6eea-7626-44fe-9855-50f15c3939ba", - "name" : "web-origins", - "description" : "OpenID Connect scope for add allowed web origins to the access token", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "false", - "consent.screen.text" : "", - "display.on.consent.screen" : "false" - }, - "protocolMappers" : [ { - "id" : "3489c748-3cc7-4350-9351-2955fc7084ba", - "name" : "allowed web origins", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-allowed-origins-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "access.token.claim" : "true" - } - } ] - }, { - "id" : "00afe548-c677-4595-8478-16f752c2713a", - "name" : "offline_access", - "description" : "OpenID Connect built-in scope: offline_access", - "protocol" : "openid-connect", - "attributes" : { - "consent.screen.text" : "${offlineAccessScopeConsentText}", - "display.on.consent.screen" : "true" - } - }, { - "id" : "1448ed2b-ec1d-4bf4-a8b7-00cb85459289", - "name" : "address", - "description" : "OpenID Connect built-in scope: address", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "true", - "consent.screen.text" : "${addressScopeConsentText}", - "display.on.consent.screen" : "true" - }, - "protocolMappers" : [ { - "id" : "12d491b6-5d74-4168-ac5c-517ebc2f1de4", - "name" : "address", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-address-mapper", - "consentRequired" : false, - "config" : { - "user.attribute.formatted" : "formatted", - "user.attribute.country" : "country", - "introspection.token.claim" : "true", - "user.attribute.postal_code" : "postal_code", - "userinfo.token.claim" : "true", - "user.attribute.street" : "street", - "id.token.claim" : "true", - "user.attribute.region" : "region", - "access.token.claim" : "true", - "user.attribute.locality" : "locality" - } - } ] - }, { - "id" : "52223fb1-9651-4cdf-8317-a1301d4042f7", - "name" : "organization", - "description" : "Additional claims about the organization a subject belongs to", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "true", - "consent.screen.text" : "${organizationScopeConsentText}", - "display.on.consent.screen" : "true" - }, - "protocolMappers" : [ { - "id" : "dccc4214-ece6-4235-8119-ee8cb954c29a", - "name" : "organization", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-organization-membership-mapper", - "consentRequired" : false, - "config" : { - "id.token.claim" : "true", - "introspection.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "organization", - "jsonType.label" : "String", - "multivalued" : "true" - } - } ] - }, { - "id" : "8be22542-e327-4a25-8265-a34a29607d1b", - "name" : "service_account", - "description" : "Specific scope for a client enabled for service accounts", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "false", - "display.on.consent.screen" : "false" - }, - "protocolMappers" : [ { - "id" : "cf89064a-0af3-4a4b-a838-3528a8f4d780", - "name" : "Client IP Address", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usersessionmodel-note-mapper", - "consentRequired" : false, - "config" : { - "user.session.note" : "clientAddress", - "id.token.claim" : "true", - "introspection.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "clientAddress", - "jsonType.label" : "String" - } - }, { - "id" : "dc0f77e6-cc20-4c0a-baf3-f45046d749d1", - "name" : "Client ID", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usersessionmodel-note-mapper", - "consentRequired" : false, - "config" : { - "user.session.note" : "client_id", - "id.token.claim" : "true", - "introspection.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "client_id", - "jsonType.label" : "String" - } - }, { - "id" : "d63fd29a-3613-4529-a8e4-3a7d7e9f5802", - "name" : "Client Host", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usersessionmodel-note-mapper", - "consentRequired" : false, - "config" : { - "user.session.note" : "clientHost", - "id.token.claim" : "true", - "introspection.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "clientHost", - "jsonType.label" : "String" - } - } ] - }, { - "id" : "d9079603-62b7-4680-9d01-950daae75d6b", - "name" : "saml_organization", - "description" : "Organization Membership", - "protocol" : "saml", - "attributes" : { - "display.on.consent.screen" : "false" - }, - "protocolMappers" : [ { - "id" : "d826fc58-b006-49ad-93dc-a76700e800df", - "name" : "organization", - "protocol" : "saml", - "protocolMapper" : "saml-organization-membership-mapper", - "consentRequired" : false, - "config" : { } - } ] - }, { - "id" : "171d8267-87da-4a4b-9346-d901d470248b", - "name" : "phone", - "description" : "OpenID Connect built-in scope: phone", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "true", - "consent.screen.text" : "${phoneScopeConsentText}", - "display.on.consent.screen" : "true" - }, - "protocolMappers" : [ { - "id" : "f8bb18d4-af9d-49b0-a61f-cc81887870cd", - "name" : "phone number", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "phoneNumber", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "phone_number", - "jsonType.label" : "String" - } - }, { - "id" : "88a2c658-9b61-40a2-abd5-69c501286031", - "name" : "phone number verified", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "phoneNumberVerified", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "phone_number_verified", - "jsonType.label" : "boolean" - } - } ] - }, { - "id" : "ea3b84ac-a91f-4a3d-be4e-893e11eaf4a1", - "name" : "acr", - "description" : "OpenID Connect scope for add acr (authentication context class reference) to the token", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "false", - "display.on.consent.screen" : "false" - }, - "protocolMappers" : [ { - "id" : "152d66d4-524f-47f1-a592-be3a0c043a4f", - "name" : "acr loa level", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-acr-mapper", - "consentRequired" : false, - "config" : { - "id.token.claim" : "true", - "introspection.token.claim" : "true", - "access.token.claim" : "true" - } - } ] - }, { - "id" : "2fc1ad0d-1065-4196-8d1b-c61525c9425d", - "name" : "microprofile-jwt", - "description" : "Microprofile - JWT built-in scope", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "true", - "display.on.consent.screen" : "false" - }, - "protocolMappers" : [ { - "id" : "9d537486-f6bf-4856-91fc-ca3acaa78814", - "name" : "upn", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "username", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "upn", - "jsonType.label" : "String" - } - }, { - "id" : "55425438-4111-47a0-9a36-fec9dbbc6a8a", - "name" : "groups", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-realm-role-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "multivalued" : "true", - "user.attribute" : "foo", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "groups", - "jsonType.label" : "String" - } - } ] - }, { - "id" : "0d186f4e-ef6d-4fbc-9593-081e0d5ad171", - "name" : "profile", - "description" : "OpenID Connect built-in scope: profile", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "true", - "consent.screen.text" : "${profileScopeConsentText}", - "display.on.consent.screen" : "true" - }, - "protocolMappers" : [ { - "id" : "bb8bb550-2db6-4631-97dc-1d115d0e3034", - "name" : "given name", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "firstName", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "given_name", - "jsonType.label" : "String" - } - }, { - "id" : "c942089b-2898-4052-a64d-85b61e27aaa4", - "name" : "username", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "username", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "preferred_username", - "jsonType.label" : "String" - } - }, { - "id" : "5ff3a9ca-7036-458c-b0dc-41216292d210", - "name" : "updated at", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "updatedAt", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "updated_at", - "jsonType.label" : "long" - } - }, { - "id" : "41f93d62-4074-4373-a270-9bdf1e298cb5", - "name" : "website", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "website", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "website", - "jsonType.label" : "String" - } - }, { - "id" : "ffec7d63-0f78-41ea-8023-6c7c64661b34", - "name" : "locale", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "locale", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "locale", - "jsonType.label" : "String" - } - }, { - "id" : "4a514ae7-d29f-4979-8df9-a97b36a81a96", - "name" : "profile", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "profile", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "profile", - "jsonType.label" : "String" - } - }, { - "id" : "286e349b-cb9f-41b1-b9dc-d787f13e9d99", - "name" : "nickname", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "nickname", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "nickname", - "jsonType.label" : "String" - } - }, { - "id" : "f5177603-55b1-4abe-aee6-b1e5a05e37f6", - "name" : "full name", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-full-name-mapper", - "consentRequired" : false, - "config" : { - "id.token.claim" : "true", - "introspection.token.claim" : "true", - "access.token.claim" : "true", - "userinfo.token.claim" : "true" - } - }, { - "id" : "a31114d7-05fc-40c1-9ea8-6977f6f0bec5", - "name" : "zoneinfo", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "zoneinfo", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "zoneinfo", - "jsonType.label" : "String" - } - }, { - "id" : "8884be77-648d-4083-b0cf-57130162c8dc", - "name" : "gender", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "gender", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "gender", - "jsonType.label" : "String" - } - }, { - "id" : "61840434-c79f-455a-a914-117977197304", - "name" : "family name", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "lastName", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "family_name", - "jsonType.label" : "String" - } - }, { - "id" : "1f40ff0b-1664-4259-846b-ab707c76d33b", - "name" : "middle name", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "middleName", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "middle_name", - "jsonType.label" : "String" - } - }, { - "id" : "8534d400-8a81-4ae3-b51f-78b93e5a2045", - "name" : "picture", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "picture", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "picture", - "jsonType.label" : "String" - } - }, { - "id" : "82a0e240-0824-41b9-b6e8-856a72d1e930", - "name" : "birthdate", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "birthdate", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "birthdate", - "jsonType.label" : "String" - } - } ] - }, { - "id" : "a5cedc85-d9e9-42e1-9ea3-ff37d21d5e27", - "name" : "role_list", - "description" : "SAML role list", - "protocol" : "saml", - "attributes" : { - "consent.screen.text" : "${samlRoleListScopeConsentText}", - "display.on.consent.screen" : "true" - }, - "protocolMappers" : [ { - "id" : "19009128-590f-4bc9-80de-c9ba4aae822d", - "name" : "role list", - "protocol" : "saml", - "protocolMapper" : "saml-role-list-mapper", - "consentRequired" : false, - "config" : { - "single" : "false", - "attribute.nameformat" : "Basic", - "attribute.name" : "Role" - } - } ] - }, { - "id" : "3b6bb88b-c833-4bb5-9bd0-95831aa2ad0d", - "name" : "basic", - "description" : "OpenID Connect scope for add all basic claims to the token", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "false", - "display.on.consent.screen" : "false" - }, - "protocolMappers" : [ { - "id" : "ce925803-aec2-47cb-a3b9-4bef12c80367", - "name" : "sub", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-sub-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "access.token.claim" : "true" - } - }, { - "id" : "58729b3a-3816-460e-bf2e-d0d2206c1830", - "name" : "auth_time", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usersessionmodel-note-mapper", - "consentRequired" : false, - "config" : { - "user.session.note" : "AUTH_TIME", - "id.token.claim" : "true", - "introspection.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "auth_time", - "jsonType.label" : "long" - } - } ] - }, { - "id" : "7aa2d936-3edb-45e5-bae0-b4a618d06371", - "name" : "roles", - "description" : "OpenID Connect scope for add user roles to the access token", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "false", - "consent.screen.text" : "${rolesScopeConsentText}", - "display.on.consent.screen" : "true" - }, - "protocolMappers" : [ { - "id" : "a9d1e8e2-ca10-4904-8a42-7708b0bfdefa", - "name" : "client roles", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-client-role-mapper", - "consentRequired" : false, - "config" : { - "user.attribute" : "foo", - "introspection.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "resource_access.${client_id}.roles", - "jsonType.label" : "String", - "multivalued" : "true" - } - }, { - "id" : "1f217073-ad43-483b-b0d5-f3ca4c74282f", - "name" : "realm roles", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-realm-role-mapper", - "consentRequired" : false, - "config" : { - "user.attribute" : "foo", - "introspection.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "realm_access.roles", - "jsonType.label" : "String", - "multivalued" : "true" - } - }, { - "id" : "61b0a069-8b67-4692-bcca-66a197b230eb", - "name" : "audience resolve", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-audience-resolve-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "access.token.claim" : "true" - } - } ] - } ], - "defaultDefaultClientScopes" : [ "role_list", "saml_organization", "profile", "email", "roles", "web-origins", "acr", "basic" ], - "defaultOptionalClientScopes" : [ "offline_access", "address", "phone", "microprofile-jwt", "organization" ], - "browserSecurityHeaders" : { - "contentSecurityPolicyReportOnly" : "", - "xContentTypeOptions" : "nosniff", - "referrerPolicy" : "no-referrer", - "xRobotsTag" : "none", - "xFrameOptions" : "SAMEORIGIN", - "contentSecurityPolicy" : "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", - "xXSSProtection" : "1; mode=block", - "strictTransportSecurity" : "max-age=31536000; includeSubDomains" - }, - "smtpServer" : { }, - "eventsEnabled" : false, - "eventsListeners" : [ "jboss-logging" ], - "enabledEventTypes" : [ ], - "adminEventsEnabled" : false, - "adminEventsDetailsEnabled" : false, - "identityProviders" : [ ], - "identityProviderMappers" : [ ], - "components" : { - "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy" : [ { - "id" : "9eac5531-7f25-493f-a721-6c5e65cd34c2", - "name" : "Max Clients Limit", - "providerId" : "max-clients", - "subType" : "anonymous", - "subComponents" : { }, - "config" : { - "max-clients" : [ "200" ] - } - }, { - "id" : "d9319a22-4c67-4b08-822f-4162a1ee01bc", - "name" : "Allowed Client Scopes", - "providerId" : "allowed-client-templates", - "subType" : "anonymous", - "subComponents" : { }, - "config" : { - "allow-default-scopes" : [ "true" ] - } - }, { - "id" : "21456c8e-7f6b-4e49-a3e1-bea7f900e2fb", - "name" : "Consent Required", - "providerId" : "consent-required", - "subType" : "anonymous", - "subComponents" : { }, - "config" : { } - }, { - "id" : "4872e99b-b55b-4e13-8a93-63e853289cac", - "name" : "Full Scope Disabled", - "providerId" : "scope", - "subType" : "anonymous", - "subComponents" : { }, - "config" : { } - }, { - "id" : "a118a194-09f5-435d-9d4b-363813413167", - "name" : "Trusted Hosts", - "providerId" : "trusted-hosts", - "subType" : "anonymous", - "subComponents" : { }, - "config" : { - "host-sending-registration-request-must-match" : [ "true" ], - "client-uris-must-match" : [ "true" ] - } - }, { - "id" : "e32b1e26-6571-4b0c-a205-0fbb3de44384", - "name" : "Allowed Client Scopes", - "providerId" : "allowed-client-templates", - "subType" : "authenticated", - "subComponents" : { }, - "config" : { - "allow-default-scopes" : [ "true" ] - } - }, { - "id" : "9dbe6752-9978-42a3-9210-9ec166140de2", - "name" : "Allowed Protocol Mapper Types", - "providerId" : "allowed-protocol-mappers", - "subType" : "anonymous", - "subComponents" : { }, - "config" : { - "allowed-protocol-mapper-types" : [ "oidc-usermodel-attribute-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-address-mapper", "saml-role-list-mapper", "saml-user-property-mapper", "oidc-usermodel-property-mapper", "oidc-full-name-mapper", "saml-user-attribute-mapper" ] - } - }, { - "id" : "7027b3f4-d877-4814-ac78-f1edb8eb89b0", - "name" : "Allowed Protocol Mapper Types", - "providerId" : "allowed-protocol-mappers", - "subType" : "authenticated", - "subComponents" : { }, - "config" : { - "allowed-protocol-mapper-types" : [ "saml-role-list-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-address-mapper", "saml-user-property-mapper", "oidc-usermodel-attribute-mapper", "saml-user-attribute-mapper", "oidc-usermodel-property-mapper", "oidc-full-name-mapper" ] - } - } ], - "org.keycloak.keys.KeyProvider" : [ { - "id" : "819cfc66-a997-4747-9d90-a7f0c09774bf", - "name" : "aes-generated", - "providerId" : "aes-generated", - "subComponents" : { }, - "config" : { - "kid" : [ "eb74df73-3f34-457d-95c7-5ad909107703" ], - "secret" : [ "1K8IJiDODmotHJPStrXhtA" ], - "priority" : [ "100" ] - } - }, { - "id" : "299857cd-52a4-4981-8171-02e7d8f12960", - "name" : "rsa-generated", - "providerId" : "rsa-generated", - "subComponents" : { }, - "config" : { - "privateKey" : [ "MIIEpAIBAAKCAQEA1MRmAT/yImkVfPMBxC0QHdC4DQfuWUTjKeEku+gMI9jX5ChUzzzVugcvZWmxBNcvOz7p6R8EdPllZKIwFSH5WvQ1w1VIgQwIlYfpi/pknfftLd66MI2fXrQK19dRTeQRivEf39GTfBQ2Xc7y1q7zbMo5TVxATJ3DgPi13dYO7zVPpGTiQQeYiezlcBedyGe4cS1g6oBoaVif1QPY1Ni2vEjJhczNMGI408tIFws8G04Tlno814nT0ysdflUSGcRUku41NtfM9hr57LQ459sGYho8Pn11lDuiUWkomJv0y3GJ1wFBvQbDvI+6QvEdFu0GxShrlcORrNmj3BwOOLhB7wIDAQABAoIBAA6zrXq7oO8YxMfYANC97mWpBPa9jA42EN5VdNTZIXGeq7hTwxx4zynmEjPXPEih190nqUEXCBdPHl74SAtFyDWtN0PSkkp8euFePViTSj2SIpzvTX1KY+9G0JL+iVsw/bdUlwe/swm5WdJcmPIVr7NeO9xpGfZRVm+EgAieoHSN4Z7g20wLbVz1fya+6O5Hy+IGezamIA4tchk+4hyiVpSh2TcdjkJJZWOlHKPkwWU/MYQbJibuea5jLoWA39NIqV2l5GT0SoCbffGJNb9CMTTGmXoK5zNwHhG+M0a4eP1vbFDLaoDne86JySmTdv/WrTFFa3veelw2K8PHDybuB70CgYEA/gbxqLZYkJcEpqsjM/XcISFJ09icJLKl5r2l/Dm4Qq587QniQYribX/PPLfDhgVwPByQe3rccq9FoiILycTdIwgSMTsg5fzvbLJTqMAcl2r0zJgHVIDc6iXnytuE0FffKN0kSKL1C4d6n6vKoCGvOcZoXK5jxgzpY8lasvKxhCsCgYEA1mtr7CDYY3qPmTu4/Uz6cFgX8RDMZZ11AQQXNMsKHIu5C4xLeYmJMlpt0y4h52/NWRzh2svdw3SEZTCfP1WVC7StfP8KD8QdwVkQlY5EGkiz9uRtEgwk8chkOTIm2JedeRL6YWlTgnH9PIuGq84OOnEbFjVN3Lbx3N1QuQfVA00CgYEAybA1uuBcXSCqfrIuVxkD2AIYHe1DvBdjhVpaKXKii78CTSmlzKg6svnhTrIQuZ4jyHZdeMzJrvzeaqZheaemdCP6XcA2lKRIbKMBrWAq00YGa1LhrwRJYlcKPJQiVVEPS+CY6FsJ+Edu4suBK7bS6ypOvhdv/FVQEPxT2PS8YNUCgYEAxwJ+8XNuw63ud9+Zi+gVjY4F8qWPwESLYz0DuOk2YlZAknpNVumTYBvUUSxBJYh8RFhtO+D53D5Z331oYKUzJ+EzII+qLAXvRBRBMz4O8YJHHkDXBugkphBDDV8B9QeLjeNSZnUWoDziOH6bqPwf8pgl9s/Ui6V1CHSVRpcBWwUCgYA2kMgu7qS5kLtUWySPzW4nTKwhN+HFTIbRrNrECxXmxroigTEyfBFuNR5QaeYYrAtqgY1m5Lev//2GnWM7dAr7hewj6qfGszrvegHsqMs4cakVqEOtbrWxL+WtWPaIdjJ+x7ZoMnZxZDg3ysemybNHHwSyBsp1TDc+glzmMtJtLA==" ], - "keyUse" : [ "SIG" ], - "certificate" : [ "MIICnTCCAYUCBgGVUbFIeTANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdzdHVkZW50MB4XDTI1MDMwMTEyMzAyN1oXDTM1MDMwMTEyMzIwN1owEjEQMA4GA1UEAwwHc3R1ZGVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANTEZgE/8iJpFXzzAcQtEB3QuA0H7llE4ynhJLvoDCPY1+QoVM881boHL2VpsQTXLzs+6ekfBHT5ZWSiMBUh+Vr0NcNVSIEMCJWH6Yv6ZJ337S3eujCNn160CtfXUU3kEYrxH9/Rk3wUNl3O8tau82zKOU1cQEydw4D4td3WDu81T6Rk4kEHmIns5XAXnchnuHEtYOqAaGlYn9UD2NTYtrxIyYXMzTBiONPLSBcLPBtOE5Z6PNeJ09MrHX5VEhnEVJLuNTbXzPYa+ey0OOfbBmIaPD59dZQ7olFpKJib9MtxidcBQb0Gw7yPukLxHRbtBsUoa5XDkazZo9wcDji4Qe8CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAedqvKOBsz4IKKzkWHIQnN5H8dQKnuPUMdWewOwmMGIUdBU9k6aS+y+BB7mugF/Hnr8Lw5d2AHwVLj2VyP4Pq0d2My3Ihxi0vr6sSfxVHuD9y/a7FxDGVTkCvmy5DOmpF/kdNnL9xG5ZivHaucnrIHHGMcQCdbWAaac0qPZihv9pdMZFMtI3aiBO5jVJ7KP8iLNKsshg60mxCOPzauMVXi+rqqqhGAgMKAL4hjjvdIKTLWwmthnmAlGqlTk/7H82hS9aKygufXszXWdFAYhX/r8/hjyc+6zJUvkG20uRWnkR35gya7jQoZ2O6OvkQf0mgSvzgIP3xoYV2uKYD03wINg==" ], - "priority" : [ "100" ] - } - }, { - "id" : "3d6bfeeb-fa86-435e-8c39-6f547a0f4a38", - "name" : "hmac-generated-hs512", - "providerId" : "hmac-generated", - "subComponents" : { }, - "config" : { - "kid" : [ "176e970f-5915-4d27-8233-8fab6d7ad947" ], - "secret" : [ "sXeOdtyIPpH_kcZWikHFjTur9yWok0QUwKi95l8wHp6kTVX9vhoZL2siNHRoFnn8tFgT4JZbR0bMsD57qAXlmVjA830Ny_GZdhL_PFWQh7JYMEJrl-1nyLy_SReQXRtq_q9tKUafUZqeYSKBlUYZ7D4jNRJ4-uniq80Ger-4ee0" ], - "priority" : [ "100" ], - "algorithm" : [ "HS512" ] - } - }, { - "id" : "df1247b5-041e-4ae8-b7fc-26c4b6f5ff67", - "name" : "rsa-enc-generated", - "providerId" : "rsa-enc-generated", - "subComponents" : { }, - "config" : { - "privateKey" : [ "MIIEpAIBAAKCAQEAq0piY/PaZh1IX46e0G6tCPtfRx7Q8zGslOFSBLR9PNAdSlBpYiV9kOpN1kTK2Sca5j4yyO9HAFK5j+zh/cy8SsP1iyuI0sCPG7NMKV1pP5Y753wTC0lTq8z16bvXPvPYrPRKgGDmU7Ww0/WD6/P+z9Li/+ujFHzzzfoPuQvbBhma6A4oadsC+zun/mWCyiD14mB+X00BeAIsxKJZ/Sd+U4lMkkvmpoXyx61xK+j48EAZ18u7FprlvUjgGzzAmm/K6O/fHPIw5eViVly7aj5gjh67ntSuZArVtrxy/Py5V4hkSO8guXKqNz3liJvLbFCqqpfTR/0duArZR0xcnaGc6wIDAQABAoIBAFRuu5YaXxbDq2eS5RzH2Vpakin7+jJOU4wljujL0QnXagC2J2QeJ8l1fT23tieZO4yvrxfVvnFd1aMouHMC5vORqWja4jxEd6ZHWKzxIw6ZbtjZk4eWMvy18KewlFavGyiR2GF0okQ0BMBOPqNhp8JoaMWOsNnKB+GJuBNWUTWtPWNQlbaeI+uIgFywrvZOcQuWqU+9Y5rQa7oKZisufu+z0vd9XyvjXQ/Thnuu9/k1m88EMAMS63zwfIOZm35PPqh1/6aBBcRWquT1X2S8g2hwmMLZJgU91yKtQcIujHXAcvxeK72/dcm8NU6AxM+8aj+821TvcNzJi7he5SGcin0CgYEA4cuxwqRixXz6yDcTv9GpJMULbJGjA+Qf/iSfT+ftBeKbnKgZGzHwOCTu5DMkag3BPjclut4sEt3QPf1cFv5vZvdkOnPeaFxrtoMhSz8ssh8qaOsObCwicel1zdPVTmMw7YzEZV14fdIq3lkHsLy2uWa0imRH0l4xTccmsJiPtU8CgYEAwjQtspxOejCyME+M+hcU3RelD6kaMjICuWGJj8g0OpqdHM7iNVJq78fOlWjntt/ydzfOXVMMVh4AG8dAvlc86iwwsBRsJPVrrrRoSNuAwFbjKisbjlnPbqyclHfUsyQitj19tp//ExH7JaBibzKd6KhqFuQTE1iYLs2mFQAz76UCgYEAsNLu64oGm7frQP345mAPgO8aqjRHIBX3g/Q0GsR61wAGcyElQCnUgHNT7burSa5p5goT7wpsI343xUPzaUJqBY25nRj+VGYEKFL6sM3Rd9B2SuHBUq8hbmmwyraYtiFxwKZbazJO2OHMloHMRvkSc5Dd0/8CS9ld7RYH04Y2DHsCgYEAoKXTK44baP7BWC9mOjc/vgjiNQs4rU8ra7igt7zwX44o63zEKUHNTh7l6DiIfYHRrAcRAahCazaT9makSxAVRs1ZVT7/mq8d7b41Chfx8KmvbuGMAPyQGEhXmoVqAOqigEhrptfBhD/6lkyPQNcJQz2VzOvMT9OYyBa8DWFGlTUCgYAfModz6g2HsYYr37/0ByXHKL0WQQtuAlZzCY9GuDLEok7QLFI/E+bdOHos3goW72Iswo/SO7inlW4S3gojuy+zZhwCO31T9p2Z0Yn0tDK3fkUO32flOLwxCZA99pKkIul3svl6643GqSD1feybmbYRtqoPCTSKSE9vI9T9DkBTvA==" ], - "keyUse" : [ "ENC" ], - "certificate" : [ "MIICnTCCAYUCBgGVUbFItzANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdzdHVkZW50MB4XDTI1MDMwMTEyMzAyN1oXDTM1MDMwMTEyMzIwN1owEjEQMA4GA1UEAwwHc3R1ZGVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKtKYmPz2mYdSF+OntBurQj7X0ce0PMxrJThUgS0fTzQHUpQaWIlfZDqTdZEytknGuY+MsjvRwBSuY/s4f3MvErD9YsriNLAjxuzTCldaT+WO+d8EwtJU6vM9em71z7z2Kz0SoBg5lO1sNP1g+vz/s/S4v/roxR88836D7kL2wYZmugOKGnbAvs7p/5lgsog9eJgfl9NAXgCLMSiWf0nflOJTJJL5qaF8setcSvo+PBAGdfLuxaa5b1I4Bs8wJpvyujv3xzyMOXlYlZcu2o+YI4eu57UrmQK1ba8cvz8uVeIZEjvILlyqjc95Yiby2xQqqqX00f9HbgK2UdMXJ2hnOsCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAHfum0Ymw/qHTpjNeJjJyh5A1N4Z58m/PjuCXafIdDDjuAYpB3/M4bTGZRUvEvv2RuBNv3rONvMR8dRrZwio0/T0aEXnHrEAaCSfVcMy1To8TGGOzgtPMub4YCqXLMCwW5cwIbqT3P58HEqsqEbv7Zp4LtLYZBYXWDF8vM4zEn3CPYxuxRPKlrBUynRKYcwN7+/dbhJKiARpPMIZ5viGbjaTnNE/d/VFdv1q5xm3ItYnShDyJ0REGN18sWleLI6qkW0X22Gcjn38fWjiXDnF0HQYzC2UzMcEo/iLfPxTKbJnc+PPmnszfmCh7mWs5xVGfMOz/Oy8HI121x1ZSriRktA==" ], - "priority" : [ "100" ], - "algorithm" : [ "RSA-OAEP" ] - } - } ] - }, - "internationalizationEnabled" : false, - "supportedLocales" : [ ], - "authenticationFlows" : [ { - "id" : "f7d1108f-7994-47e5-81e9-1a88cdbe545c", - "alias" : "Account verification options", - "description" : "Method with which to verity the existing account", - "providerId" : "basic-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "idp-email-verification", - "authenticatorFlow" : false, - "requirement" : "ALTERNATIVE", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticatorFlow" : true, - "requirement" : "ALTERNATIVE", - "priority" : 20, - "autheticatorFlow" : true, - "flowAlias" : "Verify Existing Account by Re-authentication", - "userSetupAllowed" : false - } ] - }, { - "id" : "cf40a5d3-bec8-4aef-9658-1b88c6cec561", - "alias" : "Browser - Conditional OTP", - "description" : "Flow to determine if the OTP is required for the authentication", - "providerId" : "basic-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "conditional-user-configured", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "auth-otp-form", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 20, - "autheticatorFlow" : false, - "userSetupAllowed" : false - } ] - }, { - "id" : "6820625f-5bb5-4fa2-8539-26a8568265c1", - "alias" : "Browser - Conditional Organization", - "description" : "Flow to determine if the organization identity-first login is to be used", - "providerId" : "basic-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "conditional-user-configured", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "organization", - "authenticatorFlow" : false, - "requirement" : "ALTERNATIVE", - "priority" : 20, - "autheticatorFlow" : false, - "userSetupAllowed" : false - } ] - }, { - "id" : "66d5e52e-592e-4cef-bfa0-512e90b609ec", - "alias" : "Direct Grant - Conditional OTP", - "description" : "Flow to determine if the OTP is required for the authentication", - "providerId" : "basic-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "conditional-user-configured", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "direct-grant-validate-otp", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 20, - "autheticatorFlow" : false, - "userSetupAllowed" : false - } ] - }, { - "id" : "b5bed405-b5f2-4839-861c-612501e4c412", - "alias" : "First Broker Login - Conditional Organization", - "description" : "Flow to determine if the authenticator that adds organization members is to be used", - "providerId" : "basic-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "conditional-user-configured", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "idp-add-organization-member", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 20, - "autheticatorFlow" : false, - "userSetupAllowed" : false - } ] - }, { - "id" : "dd786e24-e822-43ec-be03-29874eb73737", - "alias" : "First broker login - Conditional OTP", - "description" : "Flow to determine if the OTP is required for the authentication", - "providerId" : "basic-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "conditional-user-configured", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "auth-otp-form", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 20, - "autheticatorFlow" : false, - "userSetupAllowed" : false - } ] - }, { - "id" : "8751572f-623e-4bdc-a02c-e92c15a91143", - "alias" : "Handle Existing Account", - "description" : "Handle what to do if there is existing account with same email/username like authenticated identity provider", - "providerId" : "basic-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "idp-confirm-link", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticatorFlow" : true, - "requirement" : "REQUIRED", - "priority" : 20, - "autheticatorFlow" : true, - "flowAlias" : "Account verification options", - "userSetupAllowed" : false - } ] - }, { - "id" : "61efadf2-a54e-4071-b8c9-83e094525051", - "alias" : "Organization", - "providerId" : "basic-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticatorFlow" : true, - "requirement" : "CONDITIONAL", - "priority" : 10, - "autheticatorFlow" : true, - "flowAlias" : "Browser - Conditional Organization", - "userSetupAllowed" : false - } ] - }, { - "id" : "b99c3a7a-8ef7-46b1-b8a1-cb51f8a6e725", - "alias" : "Reset - Conditional OTP", - "description" : "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", - "providerId" : "basic-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "conditional-user-configured", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "reset-otp", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 20, - "autheticatorFlow" : false, - "userSetupAllowed" : false - } ] - }, { - "id" : "a3bfc2e4-af67-4d3e-851f-3c58bf32be83", - "alias" : "User creation or linking", - "description" : "Flow for the existing/non-existing user alternatives", - "providerId" : "basic-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticatorConfig" : "create unique user config", - "authenticator" : "idp-create-user-if-unique", - "authenticatorFlow" : false, - "requirement" : "ALTERNATIVE", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticatorFlow" : true, - "requirement" : "ALTERNATIVE", - "priority" : 20, - "autheticatorFlow" : true, - "flowAlias" : "Handle Existing Account", - "userSetupAllowed" : false - } ] - }, { - "id" : "4cc3bf25-d1b7-43a6-8619-5ed5f2d65aed", - "alias" : "Verify Existing Account by Re-authentication", - "description" : "Reauthentication of existing account", - "providerId" : "basic-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "idp-username-password-form", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticatorFlow" : true, - "requirement" : "CONDITIONAL", - "priority" : 20, - "autheticatorFlow" : true, - "flowAlias" : "First broker login - Conditional OTP", - "userSetupAllowed" : false - } ] - }, { - "id" : "4e5564ce-87da-4b25-8dcb-062216ceaa8d", - "alias" : "browser", - "description" : "Browser based authentication", - "providerId" : "basic-flow", - "topLevel" : true, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "auth-cookie", - "authenticatorFlow" : false, - "requirement" : "ALTERNATIVE", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "auth-spnego", - "authenticatorFlow" : false, - "requirement" : "DISABLED", - "priority" : 20, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "identity-provider-redirector", - "authenticatorFlow" : false, - "requirement" : "ALTERNATIVE", - "priority" : 25, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticatorFlow" : true, - "requirement" : "ALTERNATIVE", - "priority" : 26, - "autheticatorFlow" : true, - "flowAlias" : "Organization", - "userSetupAllowed" : false - }, { - "authenticatorFlow" : true, - "requirement" : "ALTERNATIVE", - "priority" : 30, - "autheticatorFlow" : true, - "flowAlias" : "forms", - "userSetupAllowed" : false - } ] - }, { - "id" : "def90462-5831-4856-b186-05df9e640bbb", - "alias" : "clients", - "description" : "Base authentication for clients", - "providerId" : "client-flow", - "topLevel" : true, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "client-secret", - "authenticatorFlow" : false, - "requirement" : "ALTERNATIVE", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "client-jwt", - "authenticatorFlow" : false, - "requirement" : "ALTERNATIVE", - "priority" : 20, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "client-secret-jwt", - "authenticatorFlow" : false, - "requirement" : "ALTERNATIVE", - "priority" : 30, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "client-x509", - "authenticatorFlow" : false, - "requirement" : "ALTERNATIVE", - "priority" : 40, - "autheticatorFlow" : false, - "userSetupAllowed" : false - } ] - }, { - "id" : "f8c9010d-f197-417b-bda1-2993e1a73a21", - "alias" : "direct grant", - "description" : "OpenID Connect Resource Owner Grant", - "providerId" : "basic-flow", - "topLevel" : true, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "direct-grant-validate-username", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "direct-grant-validate-password", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 20, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticatorFlow" : true, - "requirement" : "CONDITIONAL", - "priority" : 30, - "autheticatorFlow" : true, - "flowAlias" : "Direct Grant - Conditional OTP", - "userSetupAllowed" : false - } ] - }, { - "id" : "0fb9e2a4-ea0d-453f-a1fe-f000c849fd66", - "alias" : "docker auth", - "description" : "Used by Docker clients to authenticate against the IDP", - "providerId" : "basic-flow", - "topLevel" : true, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "docker-http-basic-authenticator", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - } ] - }, { - "id" : "79a9efc4-1279-4093-8914-92f4e0b02bb4", - "alias" : "first broker login", - "description" : "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", - "providerId" : "basic-flow", - "topLevel" : true, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticatorConfig" : "review profile config", - "authenticator" : "idp-review-profile", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticatorFlow" : true, - "requirement" : "REQUIRED", - "priority" : 20, - "autheticatorFlow" : true, - "flowAlias" : "User creation or linking", - "userSetupAllowed" : false - }, { - "authenticatorFlow" : true, - "requirement" : "CONDITIONAL", - "priority" : 50, - "autheticatorFlow" : true, - "flowAlias" : "First Broker Login - Conditional Organization", - "userSetupAllowed" : false - } ] - }, { - "id" : "f855b3a1-6612-4528-94bc-d0793bfda561", - "alias" : "forms", - "description" : "Username, password, otp and other auth forms.", - "providerId" : "basic-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "auth-username-password-form", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticatorFlow" : true, - "requirement" : "CONDITIONAL", - "priority" : 20, - "autheticatorFlow" : true, - "flowAlias" : "Browser - Conditional OTP", - "userSetupAllowed" : false - } ] - }, { - "id" : "fb84970b-6f04-4849-a385-792e17c1b8ce", - "alias" : "registration", - "description" : "Registration flow", - "providerId" : "basic-flow", - "topLevel" : true, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "registration-page-form", - "authenticatorFlow" : true, - "requirement" : "REQUIRED", - "priority" : 10, - "autheticatorFlow" : true, - "flowAlias" : "registration form", - "userSetupAllowed" : false - } ] - }, { - "id" : "fcdfd4d4-1c04-487d-aa7c-85e136814274", - "alias" : "registration form", - "description" : "Registration form", - "providerId" : "form-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "registration-user-creation", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 20, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "registration-password-action", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 50, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "registration-recaptcha-action", - "authenticatorFlow" : false, - "requirement" : "DISABLED", - "priority" : 60, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "registration-terms-and-conditions", - "authenticatorFlow" : false, - "requirement" : "DISABLED", - "priority" : 70, - "autheticatorFlow" : false, - "userSetupAllowed" : false - } ] - }, { - "id" : "306d8f7d-c12a-46cb-9a68-c6c3f1622f57", - "alias" : "reset credentials", - "description" : "Reset credentials for a user if they forgot their password or something", - "providerId" : "basic-flow", - "topLevel" : true, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "reset-credentials-choose-user", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "reset-credential-email", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 20, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "reset-password", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 30, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticatorFlow" : true, - "requirement" : "CONDITIONAL", - "priority" : 40, - "autheticatorFlow" : true, - "flowAlias" : "Reset - Conditional OTP", - "userSetupAllowed" : false - } ] - }, { - "id" : "35a54b09-ff8c-46c4-9f04-1efbb153276c", - "alias" : "saml ecp", - "description" : "SAML ECP Profile Authentication Flow", - "providerId" : "basic-flow", - "topLevel" : true, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "http-basic-authenticator", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - } ] - } ], - "authenticatorConfig" : [ { - "id" : "fc1b82d7-593d-4906-a4d9-13220b66b7ce", - "alias" : "create unique user config", - "config" : { - "require.password.update.after.registration" : "false" - } - }, { - "id" : "a90543f4-7da7-43bc-8737-7e58dd190014", - "alias" : "review profile config", - "config" : { - "update.profile.on.first.login" : "missing" - } - } ], - "requiredActions" : [ { - "alias" : "CONFIGURE_TOTP", - "name" : "Configure OTP", - "providerId" : "CONFIGURE_TOTP", - "enabled" : true, - "defaultAction" : false, - "priority" : 10, - "config" : { } - }, { - "alias" : "TERMS_AND_CONDITIONS", - "name" : "Terms and Conditions", - "providerId" : "TERMS_AND_CONDITIONS", - "enabled" : false, - "defaultAction" : false, - "priority" : 20, - "config" : { } - }, { - "alias" : "UPDATE_PASSWORD", - "name" : "Update Password", - "providerId" : "UPDATE_PASSWORD", - "enabled" : true, - "defaultAction" : false, - "priority" : 30, - "config" : { } - }, { - "alias" : "UPDATE_PROFILE", - "name" : "Update Profile", - "providerId" : "UPDATE_PROFILE", - "enabled" : true, - "defaultAction" : false, - "priority" : 40, - "config" : { } - }, { - "alias" : "VERIFY_EMAIL", - "name" : "Verify Email", - "providerId" : "VERIFY_EMAIL", - "enabled" : true, - "defaultAction" : false, - "priority" : 50, - "config" : { } - }, { - "alias" : "delete_account", - "name" : "Delete Account", - "providerId" : "delete_account", - "enabled" : false, - "defaultAction" : false, - "priority" : 60, - "config" : { } - }, { - "alias" : "webauthn-register", - "name" : "Webauthn Register", - "providerId" : "webauthn-register", - "enabled" : true, - "defaultAction" : false, - "priority" : 70, - "config" : { } - }, { - "alias" : "webauthn-register-passwordless", - "name" : "Webauthn Register Passwordless", - "providerId" : "webauthn-register-passwordless", - "enabled" : true, - "defaultAction" : false, - "priority" : 80, - "config" : { } - }, { - "alias" : "VERIFY_PROFILE", - "name" : "Verify Profile", - "providerId" : "VERIFY_PROFILE", - "enabled" : true, - "defaultAction" : false, - "priority" : 90, - "config" : { } - }, { - "alias" : "delete_credential", - "name" : "Delete Credential", - "providerId" : "delete_credential", - "enabled" : true, - "defaultAction" : false, - "priority" : 100, - "config" : { } - }, { - "alias" : "update_user_locale", - "name" : "Update User Locale", - "providerId" : "update_user_locale", - "enabled" : true, - "defaultAction" : false, - "priority" : 1000, - "config" : { } - } ], - "browserFlow" : "browser", - "registrationFlow" : "registration", - "directGrantFlow" : "direct grant", - "resetCredentialsFlow" : "reset credentials", - "clientAuthenticationFlow" : "clients", - "dockerAuthenticationFlow" : "docker auth", - "firstBrokerLoginFlow" : "first broker login", - "attributes" : { - "cibaBackchannelTokenDeliveryMode" : "poll", - "cibaExpiresIn" : "120", - "cibaAuthRequestedUserHint" : "login_hint", - "oauth2DeviceCodeLifespan" : "600", - "oauth2DevicePollingInterval" : "5", - "parRequestUriLifespan" : "60", - "cibaInterval" : "5", - "realmReusableOtpCode" : "false" - }, - "keycloakVersion" : "26.1.3", - "userManagedAccessAllowed" : false, - "organizationsEnabled" : false, - "verifiableCredentialsEnabled" : false, - "adminPermissionsEnabled" : false, - "clientProfiles" : { - "profiles" : [ ] - }, - "clientPolicies" : { - "policies" : [ ] - } } diff --git a/idp/teacher-realm.json b/idp/teacher-realm.json index f1bc513a..fd965e96 100644 --- a/idp/teacher-realm.json +++ b/idp/teacher-realm.json @@ -1,2060 +1,2345 @@ { - "id" : "02ba6887-22f5-4de4-ad9b-cb2a2060bce1", - "realm" : "teacher", - "notBefore" : 0, - "defaultSignatureAlgorithm" : "RS256", - "revokeRefreshToken" : false, - "refreshTokenMaxReuse" : 0, - "accessTokenLifespan" : 300, - "accessTokenLifespanForImplicitFlow" : 900, - "ssoSessionIdleTimeout" : 1800, - "ssoSessionMaxLifespan" : 36000, - "ssoSessionIdleTimeoutRememberMe" : 0, - "ssoSessionMaxLifespanRememberMe" : 0, - "offlineSessionIdleTimeout" : 2592000, - "offlineSessionMaxLifespanEnabled" : false, - "offlineSessionMaxLifespan" : 5184000, - "clientSessionIdleTimeout" : 0, - "clientSessionMaxLifespan" : 0, - "clientOfflineSessionIdleTimeout" : 0, - "clientOfflineSessionMaxLifespan" : 0, - "accessCodeLifespan" : 60, - "accessCodeLifespanUserAction" : 300, - "accessCodeLifespanLogin" : 1800, - "actionTokenGeneratedByAdminLifespan" : 43200, - "actionTokenGeneratedByUserLifespan" : 300, - "oauth2DeviceCodeLifespan" : 600, - "oauth2DevicePollingInterval" : 5, - "enabled" : true, - "sslRequired" : "external", - "registrationAllowed" : false, - "registrationEmailAsUsername" : false, - "rememberMe" : false, - "verifyEmail" : false, - "loginWithEmailAllowed" : true, - "duplicateEmailsAllowed" : false, - "resetPasswordAllowed" : false, - "editUsernameAllowed" : false, - "bruteForceProtected" : false, - "permanentLockout" : false, - "maxTemporaryLockouts" : 0, - "bruteForceStrategy" : "MULTIPLE", - "maxFailureWaitSeconds" : 900, - "minimumQuickLoginWaitSeconds" : 60, - "waitIncrementSeconds" : 60, - "quickLoginCheckMilliSeconds" : 1000, - "maxDeltaTimeSeconds" : 43200, - "failureFactor" : 30, - "roles" : { - "realm" : [ { - "id" : "e7f1e366-0bfc-4469-bcde-92bcd1ed5ce7", - "name" : "uma_authorization", - "description" : "${role_uma_authorization}", - "composite" : false, - "clientRole" : false, - "containerId" : "02ba6887-22f5-4de4-ad9b-cb2a2060bce1", - "attributes" : { } - }, { - "id" : "6b546a34-4ebe-4c09-b274-fc1f6bebdf93", - "name" : "default-roles-teacher", - "description" : "${role_default-roles}", - "composite" : true, - "composites" : { - "realm" : [ "offline_access", "uma_authorization" ], - "client" : { - "account" : [ "manage-account", "view-profile" ] + "id": "02ba6887-22f5-4de4-ad9b-cb2a2060bce1", + "realm": "teacher", + "notBefore": 0, + "defaultSignatureAlgorithm": "RS256", + "revokeRefreshToken": false, + "refreshTokenMaxReuse": 0, + "accessTokenLifespan": 300, + "accessTokenLifespanForImplicitFlow": 900, + "ssoSessionIdleTimeout": 1800, + "ssoSessionMaxLifespan": 36000, + "ssoSessionIdleTimeoutRememberMe": 0, + "ssoSessionMaxLifespanRememberMe": 0, + "offlineSessionIdleTimeout": 2592000, + "offlineSessionMaxLifespanEnabled": false, + "offlineSessionMaxLifespan": 5184000, + "clientSessionIdleTimeout": 0, + "clientSessionMaxLifespan": 0, + "clientOfflineSessionIdleTimeout": 0, + "clientOfflineSessionMaxLifespan": 0, + "accessCodeLifespan": 60, + "accessCodeLifespanUserAction": 300, + "accessCodeLifespanLogin": 1800, + "actionTokenGeneratedByAdminLifespan": 43200, + "actionTokenGeneratedByUserLifespan": 300, + "oauth2DeviceCodeLifespan": 600, + "oauth2DevicePollingInterval": 5, + "enabled": true, + "sslRequired": "external", + "registrationAllowed": false, + "registrationEmailAsUsername": false, + "rememberMe": false, + "verifyEmail": false, + "loginWithEmailAllowed": true, + "duplicateEmailsAllowed": false, + "resetPasswordAllowed": false, + "editUsernameAllowed": false, + "bruteForceProtected": false, + "permanentLockout": false, + "maxTemporaryLockouts": 0, + "bruteForceStrategy": "MULTIPLE", + "maxFailureWaitSeconds": 900, + "minimumQuickLoginWaitSeconds": 60, + "waitIncrementSeconds": 60, + "quickLoginCheckMilliSeconds": 1000, + "maxDeltaTimeSeconds": 43200, + "failureFactor": 30, + "roles": { + "realm": [ + { + "id": "e7f1e366-0bfc-4469-bcde-92bcd1ed5ce7", + "name": "uma_authorization", + "description": "${role_uma_authorization}", + "composite": false, + "clientRole": false, + "containerId": "02ba6887-22f5-4de4-ad9b-cb2a2060bce1", + "attributes": {} + }, + { + "id": "6b546a34-4ebe-4c09-b274-fc1f6bebdf93", + "name": "default-roles-teacher", + "description": "${role_default-roles}", + "composite": true, + "composites": { + "realm": ["offline_access", "uma_authorization"], + "client": { + "account": ["manage-account", "view-profile"] + } + }, + "clientRole": false, + "containerId": "02ba6887-22f5-4de4-ad9b-cb2a2060bce1", + "attributes": {} + }, + { + "id": "747c4433-f128-4f72-b56f-315e7779d4fd", + "name": "offline_access", + "description": "${role_offline-access}", + "composite": false, + "clientRole": false, + "containerId": "02ba6887-22f5-4de4-ad9b-cb2a2060bce1", + "attributes": {} + } + ], + "client": { + "realm-management": [ + { + "id": "4c8243b1-b576-4cb2-a4f7-3ce25e408fe5", + "name": "impersonation", + "description": "${role_impersonation}", + "composite": false, + "clientRole": true, + "containerId": "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes": {} + }, + { + "id": "71fd672b-024b-4d44-b058-03320aeb1842", + "name": "view-users", + "description": "${role_view-users}", + "composite": true, + "composites": { + "client": { + "realm-management": ["query-groups", "query-users"] + } + }, + "clientRole": true, + "containerId": "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes": {} + }, + { + "id": "fea88d42-3065-4600-a5b6-c4e2589e1304", + "name": "view-events", + "description": "${role_view-events}", + "composite": false, + "clientRole": true, + "containerId": "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes": {} + }, + { + "id": "6247b5b0-4d41-4fda-900c-3dfc725e03a2", + "name": "manage-users", + "description": "${role_manage-users}", + "composite": false, + "clientRole": true, + "containerId": "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes": {} + }, + { + "id": "a3b55a4b-b7f9-4db3-a64f-6ddf80bf74e7", + "name": "view-authorization", + "description": "${role_view-authorization}", + "composite": false, + "clientRole": true, + "containerId": "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes": {} + }, + { + "id": "d6714bc8-ff2d-4da0-98b4-2a6479e67954", + "name": "manage-events", + "description": "${role_manage-events}", + "composite": false, + "clientRole": true, + "containerId": "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes": {} + }, + { + "id": "d389da82-1730-4c66-9b43-34ac3c8d7f6c", + "name": "query-realms", + "description": "${role_query-realms}", + "composite": false, + "clientRole": true, + "containerId": "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes": {} + }, + { + "id": "4dc3905f-311b-4de0-b2e6-a3de50a078a3", + "name": "query-users", + "description": "${role_query-users}", + "composite": false, + "clientRole": true, + "containerId": "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes": {} + }, + { + "id": "28ea5d84-4e7d-484e-82fa-c9adcea4ffc0", + "name": "manage-identity-providers", + "description": "${role_manage-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes": {} + }, + { + "id": "e020bc9c-f2c9-4023-82eb-b62266749334", + "name": "query-clients", + "description": "${role_query-clients}", + "composite": false, + "clientRole": true, + "containerId": "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes": {} + }, + { + "id": "e7373af5-924a-4f01-b34d-55a09aac6c74", + "name": "manage-clients", + "description": "${role_manage-clients}", + "composite": false, + "clientRole": true, + "containerId": "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes": {} + }, + { + "id": "0879b6d5-7db6-4c83-8b99-e889028cb13e", + "name": "manage-realm", + "description": "${role_manage-realm}", + "composite": false, + "clientRole": true, + "containerId": "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes": {} + }, + { + "id": "ff2c82f3-7f04-4ced-9127-65097e2c16b9", + "name": "realm-admin", + "description": "${role_realm-admin}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "impersonation", + "view-users", + "view-events", + "manage-users", + "view-authorization", + "query-users", + "query-realms", + "manage-events", + "manage-identity-providers", + "query-clients", + "manage-realm", + "view-clients", + "manage-clients", + "query-groups", + "create-client", + "view-realm", + "manage-authorization", + "view-identity-providers" + ] + } + }, + "clientRole": true, + "containerId": "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes": {} + }, + { + "id": "156a28de-00d8-4828-9dc9-e09e7841312f", + "name": "view-clients", + "description": "${role_view-clients}", + "composite": true, + "composites": { + "client": { + "realm-management": ["query-clients"] + } + }, + "clientRole": true, + "containerId": "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes": {} + }, + { + "id": "a241d7dd-b028-474a-bdf8-4d33e00c1b90", + "name": "create-client", + "description": "${role_create-client}", + "composite": false, + "clientRole": true, + "containerId": "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes": {} + }, + { + "id": "681e3f7e-bb8c-4e09-a49e-ba8c21f916ff", + "name": "query-groups", + "description": "${role_query-groups}", + "composite": false, + "clientRole": true, + "containerId": "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes": {} + }, + { + "id": "1c5886ad-b354-4246-b288-13ea7635db58", + "name": "view-realm", + "description": "${role_view-realm}", + "composite": false, + "clientRole": true, + "containerId": "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes": {} + }, + { + "id": "7dedf6ff-b715-4f14-85ac-40d0652f153d", + "name": "manage-authorization", + "description": "${role_manage-authorization}", + "composite": false, + "clientRole": true, + "containerId": "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes": {} + }, + { + "id": "694721e8-3bf3-47b5-ae38-874db0dc7740", + "name": "view-identity-providers", + "description": "${role_view-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "attributes": {} + } + ], + "dwengo": [], + "security-admin-console": [], + "admin-cli": [], + "account-console": [], + "broker": [ + { + "id": "0cb1b2b5-a751-4f09-ac2f-ea26c398a857", + "name": "read-token", + "description": "${role_read-token}", + "composite": false, + "clientRole": true, + "containerId": "cfd0202e-a6b9-4c5e-9f49-2ef17df9089b", + "attributes": {} + } + ], + "account": [ + { + "id": "d21c51c5-353c-4d78-8c8d-8b8e9f37efa8", + "name": "manage-account-links", + "description": "${role_manage-account-links}", + "composite": false, + "clientRole": true, + "containerId": "7ceb65eb-30da-4dc3-95bc-f06863362fd6", + "attributes": {} + }, + { + "id": "49c8ac02-defa-41af-9e63-2fd24cfc103f", + "name": "view-groups", + "description": "${role_view-groups}", + "composite": false, + "clientRole": true, + "containerId": "7ceb65eb-30da-4dc3-95bc-f06863362fd6", + "attributes": {} + }, + { + "id": "3850c5cc-510a-417b-9976-a1d1d6650804", + "name": "manage-account", + "description": "${role_manage-account}", + "composite": true, + "composites": { + "client": { + "account": ["manage-account-links"] + } + }, + "clientRole": true, + "containerId": "7ceb65eb-30da-4dc3-95bc-f06863362fd6", + "attributes": {} + }, + { + "id": "6554709e-304a-428f-8665-970aacd1dae8", + "name": "view-consent", + "description": "${role_view-consent}", + "composite": false, + "clientRole": true, + "containerId": "7ceb65eb-30da-4dc3-95bc-f06863362fd6", + "attributes": {} + }, + { + "id": "7a0c9d85-daea-4b80-93b5-095e21e5d569", + "name": "delete-account", + "description": "${role_delete-account}", + "composite": false, + "clientRole": true, + "containerId": "7ceb65eb-30da-4dc3-95bc-f06863362fd6", + "attributes": {} + }, + { + "id": "ee2c5cff-1b05-417f-ab3a-a796be754299", + "name": "manage-consent", + "description": "${role_manage-consent}", + "composite": true, + "composites": { + "client": { + "account": ["view-consent"] + } + }, + "clientRole": true, + "containerId": "7ceb65eb-30da-4dc3-95bc-f06863362fd6", + "attributes": {} + }, + { + "id": "128fb31d-0784-4b4e-9aa5-82ceb2824fa0", + "name": "view-profile", + "description": "${role_view-profile}", + "composite": false, + "clientRole": true, + "containerId": "7ceb65eb-30da-4dc3-95bc-f06863362fd6", + "attributes": {} + }, + { + "id": "ca850b8d-b75b-4b04-9e42-1e4cc8ab2179", + "name": "view-applications", + "description": "${role_view-applications}", + "composite": false, + "clientRole": true, + "containerId": "7ceb65eb-30da-4dc3-95bc-f06863362fd6", + "attributes": {} + } + ] } - }, - "clientRole" : false, - "containerId" : "02ba6887-22f5-4de4-ad9b-cb2a2060bce1", - "attributes" : { } - }, { - "id" : "747c4433-f128-4f72-b56f-315e7779d4fd", - "name" : "offline_access", - "description" : "${role_offline-access}", - "composite" : false, - "clientRole" : false, - "containerId" : "02ba6887-22f5-4de4-ad9b-cb2a2060bce1", - "attributes" : { } - } ], - "client" : { - "realm-management" : [ { - "id" : "4c8243b1-b576-4cb2-a4f7-3ce25e408fe5", - "name" : "impersonation", - "description" : "${role_impersonation}", - "composite" : false, - "clientRole" : true, - "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", - "attributes" : { } - }, { - "id" : "71fd672b-024b-4d44-b058-03320aeb1842", - "name" : "view-users", - "description" : "${role_view-users}", - "composite" : true, - "composites" : { - "client" : { - "realm-management" : [ "query-groups", "query-users" ] - } + }, + "groups": [], + "defaultRole": { + "id": "6b546a34-4ebe-4c09-b274-fc1f6bebdf93", + "name": "default-roles-teacher", + "description": "${role_default-roles}", + "composite": true, + "clientRole": false, + "containerId": "02ba6887-22f5-4de4-ad9b-cb2a2060bce1" + }, + "requiredCredentials": ["password"], + "otpPolicyType": "totp", + "otpPolicyAlgorithm": "HmacSHA1", + "otpPolicyInitialCounter": 0, + "otpPolicyDigits": 6, + "otpPolicyLookAheadWindow": 1, + "otpPolicyPeriod": 30, + "otpPolicyCodeReusable": false, + "otpSupportedApplications": ["totpAppFreeOTPName", "totpAppGoogleName", "totpAppMicrosoftAuthenticatorName"], + "localizationTexts": {}, + "webAuthnPolicyRpEntityName": "keycloak", + "webAuthnPolicySignatureAlgorithms": ["ES256", "RS256"], + "webAuthnPolicyRpId": "", + "webAuthnPolicyAttestationConveyancePreference": "not specified", + "webAuthnPolicyAuthenticatorAttachment": "not specified", + "webAuthnPolicyRequireResidentKey": "not specified", + "webAuthnPolicyUserVerificationRequirement": "not specified", + "webAuthnPolicyCreateTimeout": 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyAcceptableAaguids": [], + "webAuthnPolicyExtraOrigins": [], + "webAuthnPolicyPasswordlessRpEntityName": "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms": ["ES256", "RS256"], + "webAuthnPolicyPasswordlessRpId": "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified", + "webAuthnPolicyPasswordlessCreateTimeout": 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyPasswordlessAcceptableAaguids": [], + "webAuthnPolicyPasswordlessExtraOrigins": [], + "users": [ + { + "id": "63dbbb64-c09f-4e4e-9cbf-af9e557dbb09", + "username": "testleerkracht1", + "firstName": "Kris", + "lastName": "Coolsaet", + "email": "kris.coolsaet@ugent.be", + "emailVerified": false, + "createdTimestamp": 1740866530658, + "enabled": true, + "totp": false, + "credentials": [ + { + "id": "c5382bf7-ccc6-47de-93b9-2c11ea7b6862", + "type": "password", + "userLabel": "My password", + "createdDate": 1740866544032, + "secretData": "{\"value\":\"H2vKyHF3j/alz6CNap2uaKSRb+/wrWImVecj7dcHe1w=\",\"salt\":\"32WjW1KzFaR5RJqU0Pfq9w==\",\"additionalParameters\":{}}", + "credentialData": "{\"hashIterations\":5,\"algorithm\":\"argon2\",\"additionalParameters\":{\"hashLength\":[\"32\"],\"memory\":[\"7168\"],\"type\":[\"id\"],\"version\":[\"1.3\"],\"parallelism\":[\"1\"]}}" + } + ], + "disableableCredentialTypes": [], + "requiredActions": [], + "realmRoles": ["default-roles-teacher"], + "notBefore": 0, + "groups": [] + } + ], + "scopeMappings": [ + { + "clientScope": "offline_access", + "roles": ["offline_access"] + } + ], + "clientScopeMappings": { + "account": [ + { + "client": "account-console", + "roles": ["manage-account", "view-groups"] + } + ] + }, + "clients": [ + { + "id": "7ceb65eb-30da-4dc3-95bc-f06863362fd6", + "clientId": "account", + "name": "${client_account}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/teacher/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": ["/realms/teacher/account/*"], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "realm_client": "false", + "post.logout.redirect.uris": "+" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": ["web-origins", "acr", "roles", "profile", "basic", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "organization", "microprofile-jwt"] }, - "clientRole" : true, - "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", - "attributes" : { } - }, { - "id" : "fea88d42-3065-4600-a5b6-c4e2589e1304", - "name" : "view-events", - "description" : "${role_view-events}", - "composite" : false, - "clientRole" : true, - "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", - "attributes" : { } - }, { - "id" : "6247b5b0-4d41-4fda-900c-3dfc725e03a2", - "name" : "manage-users", - "description" : "${role_manage-users}", - "composite" : false, - "clientRole" : true, - "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", - "attributes" : { } - }, { - "id" : "a3b55a4b-b7f9-4db3-a64f-6ddf80bf74e7", - "name" : "view-authorization", - "description" : "${role_view-authorization}", - "composite" : false, - "clientRole" : true, - "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", - "attributes" : { } - }, { - "id" : "d6714bc8-ff2d-4da0-98b4-2a6479e67954", - "name" : "manage-events", - "description" : "${role_manage-events}", - "composite" : false, - "clientRole" : true, - "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", - "attributes" : { } - }, { - "id" : "d389da82-1730-4c66-9b43-34ac3c8d7f6c", - "name" : "query-realms", - "description" : "${role_query-realms}", - "composite" : false, - "clientRole" : true, - "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", - "attributes" : { } - }, { - "id" : "4dc3905f-311b-4de0-b2e6-a3de50a078a3", - "name" : "query-users", - "description" : "${role_query-users}", - "composite" : false, - "clientRole" : true, - "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", - "attributes" : { } - }, { - "id" : "28ea5d84-4e7d-484e-82fa-c9adcea4ffc0", - "name" : "manage-identity-providers", - "description" : "${role_manage-identity-providers}", - "composite" : false, - "clientRole" : true, - "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", - "attributes" : { } - }, { - "id" : "e020bc9c-f2c9-4023-82eb-b62266749334", - "name" : "query-clients", - "description" : "${role_query-clients}", - "composite" : false, - "clientRole" : true, - "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", - "attributes" : { } - }, { - "id" : "e7373af5-924a-4f01-b34d-55a09aac6c74", - "name" : "manage-clients", - "description" : "${role_manage-clients}", - "composite" : false, - "clientRole" : true, - "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", - "attributes" : { } - }, { - "id" : "0879b6d5-7db6-4c83-8b99-e889028cb13e", - "name" : "manage-realm", - "description" : "${role_manage-realm}", - "composite" : false, - "clientRole" : true, - "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", - "attributes" : { } - }, { - "id" : "ff2c82f3-7f04-4ced-9127-65097e2c16b9", - "name" : "realm-admin", - "description" : "${role_realm-admin}", - "composite" : true, - "composites" : { - "client" : { - "realm-management" : [ "impersonation", "view-users", "view-events", "manage-users", "view-authorization", "query-users", "query-realms", "manage-events", "manage-identity-providers", "query-clients", "manage-realm", "view-clients", "manage-clients", "query-groups", "create-client", "view-realm", "manage-authorization", "view-identity-providers" ] - } + { + "id": "920e8621-36b5-4046-b1cd-4b293668f64b", + "clientId": "account-console", + "name": "${client_account-console}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/teacher/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": ["/realms/teacher/account/*"], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "realm_client": "false", + "post.logout.redirect.uris": "+", + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "cd3f4ae0-3008-488b-88c5-b6d640a9edd3", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + } + ], + "defaultClientScopes": ["web-origins", "acr", "roles", "profile", "basic", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "organization", "microprofile-jwt"] }, - "clientRole" : true, - "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", - "attributes" : { } - }, { - "id" : "156a28de-00d8-4828-9dc9-e09e7841312f", - "name" : "view-clients", - "description" : "${role_view-clients}", - "composite" : true, - "composites" : { - "client" : { - "realm-management" : [ "query-clients" ] - } + { + "id": "9d7b2827-b7bb-451e-ad38-8f55a69f7c9c", + "clientId": "admin-cli", + "name": "${client_admin-cli}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": false, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "realm_client": "false", + "client.use.lightweight.access.token.enabled": "true" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": ["web-origins", "acr", "roles", "profile", "basic", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "organization", "microprofile-jwt"] }, - "clientRole" : true, - "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", - "attributes" : { } - }, { - "id" : "a241d7dd-b028-474a-bdf8-4d33e00c1b90", - "name" : "create-client", - "description" : "${role_create-client}", - "composite" : false, - "clientRole" : true, - "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", - "attributes" : { } - }, { - "id" : "681e3f7e-bb8c-4e09-a49e-ba8c21f916ff", - "name" : "query-groups", - "description" : "${role_query-groups}", - "composite" : false, - "clientRole" : true, - "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", - "attributes" : { } - }, { - "id" : "1c5886ad-b354-4246-b288-13ea7635db58", - "name" : "view-realm", - "description" : "${role_view-realm}", - "composite" : false, - "clientRole" : true, - "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", - "attributes" : { } - }, { - "id" : "7dedf6ff-b715-4f14-85ac-40d0652f153d", - "name" : "manage-authorization", - "description" : "${role_manage-authorization}", - "composite" : false, - "clientRole" : true, - "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", - "attributes" : { } - }, { - "id" : "694721e8-3bf3-47b5-ae38-874db0dc7740", - "name" : "view-identity-providers", - "description" : "${role_view-identity-providers}", - "composite" : false, - "clientRole" : true, - "containerId" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", - "attributes" : { } - } ], - "dwengo" : [ ], - "security-admin-console" : [ ], - "admin-cli" : [ ], - "account-console" : [ ], - "broker" : [ { - "id" : "0cb1b2b5-a751-4f09-ac2f-ea26c398a857", - "name" : "read-token", - "description" : "${role_read-token}", - "composite" : false, - "clientRole" : true, - "containerId" : "cfd0202e-a6b9-4c5e-9f49-2ef17df9089b", - "attributes" : { } - } ], - "account" : [ { - "id" : "d21c51c5-353c-4d78-8c8d-8b8e9f37efa8", - "name" : "manage-account-links", - "description" : "${role_manage-account-links}", - "composite" : false, - "clientRole" : true, - "containerId" : "7ceb65eb-30da-4dc3-95bc-f06863362fd6", - "attributes" : { } - }, { - "id" : "49c8ac02-defa-41af-9e63-2fd24cfc103f", - "name" : "view-groups", - "description" : "${role_view-groups}", - "composite" : false, - "clientRole" : true, - "containerId" : "7ceb65eb-30da-4dc3-95bc-f06863362fd6", - "attributes" : { } - }, { - "id" : "3850c5cc-510a-417b-9976-a1d1d6650804", - "name" : "manage-account", - "description" : "${role_manage-account}", - "composite" : true, - "composites" : { - "client" : { - "account" : [ "manage-account-links" ] - } + { + "id": "cfd0202e-a6b9-4c5e-9f49-2ef17df9089b", + "clientId": "broker", + "name": "${client_broker}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "realm_client": "true" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": ["web-origins", "acr", "roles", "profile", "basic", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "organization", "microprofile-jwt"] }, - "clientRole" : true, - "containerId" : "7ceb65eb-30da-4dc3-95bc-f06863362fd6", - "attributes" : { } - }, { - "id" : "6554709e-304a-428f-8665-970aacd1dae8", - "name" : "view-consent", - "description" : "${role_view-consent}", - "composite" : false, - "clientRole" : true, - "containerId" : "7ceb65eb-30da-4dc3-95bc-f06863362fd6", - "attributes" : { } - }, { - "id" : "7a0c9d85-daea-4b80-93b5-095e21e5d569", - "name" : "delete-account", - "description" : "${role_delete-account}", - "composite" : false, - "clientRole" : true, - "containerId" : "7ceb65eb-30da-4dc3-95bc-f06863362fd6", - "attributes" : { } - }, { - "id" : "ee2c5cff-1b05-417f-ab3a-a796be754299", - "name" : "manage-consent", - "description" : "${role_manage-consent}", - "composite" : true, - "composites" : { - "client" : { - "account" : [ "view-consent" ] - } + { + "id": "abdee18a-4549-48b5-b976-4c1a42820ef9", + "clientId": "dwengo", + "name": "Dwengo", + "description": "", + "rootUrl": "http://localhost:5173", + "adminUrl": "http://localhost:5173", + "baseUrl": "http://localhost:5173", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": ["urn:ietf:wg:oauth:2.0:oob", "http://localhost:5173/*", "http://localhost:5173"], + "webOrigins": ["+"], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": true, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": true, + "protocol": "openid-connect", + "attributes": { + "realm_client": "false", + "oidc.ciba.grant.enabled": "false", + "backchannel.logout.session.required": "true", + "post.logout.redirect.uris": "+", + "frontchannel.logout.session.required": "true", + "oauth2.device.authorization.grant.enabled": "false", + "display.on.consent.screen": "false", + "backchannel.logout.revoke.offline.tokens": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": ["web-origins", "acr", "roles", "profile", "basic", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "organization", "microprofile-jwt"] }, - "clientRole" : true, - "containerId" : "7ceb65eb-30da-4dc3-95bc-f06863362fd6", - "attributes" : { } - }, { - "id" : "128fb31d-0784-4b4e-9aa5-82ceb2824fa0", - "name" : "view-profile", - "description" : "${role_view-profile}", - "composite" : false, - "clientRole" : true, - "containerId" : "7ceb65eb-30da-4dc3-95bc-f06863362fd6", - "attributes" : { } - }, { - "id" : "ca850b8d-b75b-4b04-9e42-1e4cc8ab2179", - "name" : "view-applications", - "description" : "${role_view-applications}", - "composite" : false, - "clientRole" : true, - "containerId" : "7ceb65eb-30da-4dc3-95bc-f06863362fd6", - "attributes" : { } - } ] + { + "id": "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", + "clientId": "realm-management", + "name": "${client_realm-management}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "realm_client": "true" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": ["web-origins", "acr", "roles", "profile", "basic", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "organization", "microprofile-jwt"] + }, + { + "id": "c421853c-5bdf-4ea9-ae97-51f5ad7b8df8", + "clientId": "security-admin-console", + "name": "${client_security-admin-console}", + "rootUrl": "${authAdminUrl}", + "baseUrl": "/admin/teacher/console/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": ["/admin/teacher/console/*"], + "webOrigins": ["+"], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "realm_client": "false", + "client.use.lightweight.access.token.enabled": "true", + "post.logout.redirect.uris": "+", + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "a9a893af-925e-46c9-ba33-47b06101ce5f", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": ["web-origins", "acr", "roles", "profile", "basic", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "organization", "microprofile-jwt"] + } + ], + "clientScopes": [ + { + "id": "fef4fbeb-d7e6-4474-b802-6c63df0dc9a3", + "name": "saml_organization", + "description": "Organization Membership", + "protocol": "saml", + "attributes": { + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "2384b79b-5cc3-4e1c-b4b2-4bee2ceeed72", + "name": "organization", + "protocol": "saml", + "protocolMapper": "saml-organization-membership-mapper", + "consentRequired": false, + "config": {} + } + ] + }, + { + "id": "a097893c-7eed-4556-b2ed-3751c7fc3c51", + "name": "offline_access", + "description": "OpenID Connect built-in scope: offline_access", + "protocol": "openid-connect", + "attributes": { + "consent.screen.text": "${offlineAccessScopeConsentText}", + "display.on.consent.screen": "true" + } + }, + { + "id": "ffc38cb2-eb10-47cf-a2d6-6647fdd4da65", + "name": "service_account", + "description": "Specific scope for a client enabled for service accounts", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "06ed3629-1c3d-48d9-80c6-98fcd3958c48", + "name": "Client Host", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientHost", + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientHost", + "jsonType.label": "String" + } + }, + { + "id": "04eeb81e-05c0-484a-91df-9a79138bcd66", + "name": "Client IP Address", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientAddress", + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientAddress", + "jsonType.label": "String" + } + }, + { + "id": "6e673f49-ce38-4583-8040-8a2e7ec5e7c8", + "name": "Client ID", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "client_id", + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true", + "claim.name": "client_id", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "ee188d9c-ab26-4e53-a16c-c9f77094f854", + "name": "profile", + "description": "OpenID Connect built-in scope: profile", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "consent.screen.text": "${profileScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "05ff270b-6a50-4bbb-903d-9546a59f20bf", + "name": "picture", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "picture", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "picture", + "jsonType.label": "String" + } + }, + { + "id": "394f808d-bc7b-476e-a372-7cfece5c6db0", + "name": "gender", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "gender", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "gender", + "jsonType.label": "String" + } + }, + { + "id": "0371c44f-c6e0-4f88-ac8f-17a56e2b90f8", + "name": "profile", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "profile", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "profile", + "jsonType.label": "String" + } + }, + { + "id": "21d66073-42f2-443b-aac4-e49c9038253c", + "name": "birthdate", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "birthdate", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "birthdate", + "jsonType.label": "String" + } + }, + { + "id": "5cc6a97f-9d1a-4c72-b682-af6d1bd36883", + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "id": "d6a6d46b-80a7-4228-af07-0faae2911fed", + "name": "nickname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "nickname", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "nickname", + "jsonType.label": "String" + } + }, + { + "id": "322b508a-7464-4b0f-90df-3f489975a62e", + "name": "zoneinfo", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "zoneinfo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "zoneinfo", + "jsonType.label": "String" + } + }, + { + "id": "f757ae7a-3005-4899-bb4e-da1ab4b47bb0", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + }, + { + "id": "bab8eb17-0cb0-4275-8456-aa1d65933a35", + "name": "updated at", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "updatedAt", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "updated_at", + "jsonType.label": "long" + } + }, + { + "id": "6ea1d43c-d4c7-4f2f-93b0-dfdb3bb584eb", + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "id": "3a2ebc93-05fb-4904-996b-5e3331b72fcd", + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "id": "217b417e-d4f6-4225-bf92-3bd38f6fbefb", + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "id": "3dd5da51-5842-4358-a69f-f7ffffe521ac", + "name": "website", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "website", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "website", + "jsonType.label": "String" + } + }, + { + "id": "790bda99-1c27-4970-b3b9-4fa1c90c738c", + "name": "middle name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "middleName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "middle_name", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "e6cf59c7-9390-4f48-ab01-79a0fa138960", + "name": "organization", + "description": "Additional claims about the organization a subject belongs to", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "consent.screen.text": "${organizationScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "417ff129-6b95-4e95-9f57-a6699ca18d8d", + "name": "organization", + "protocol": "openid-connect", + "protocolMapper": "oidc-organization-membership-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true", + "claim.name": "organization", + "jsonType.label": "String", + "multivalued": "true" + } + } + ] + }, + { + "id": "43d92ef5-76d8-4df0-84b5-5f833875d345", + "name": "email", + "description": "OpenID Connect built-in scope: email", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "consent.screen.text": "${emailScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "74d21718-190a-4c53-b446-b07e5f029394", + "name": "email verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "emailVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email_verified", + "jsonType.label": "boolean" + } + }, + { + "id": "949871a0-d68c-4563-a9b3-945a3148f937", + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "b07a2014-d07e-450f-a593-66e9f9cf4799", + "name": "acr", + "description": "OpenID Connect scope for add acr (authentication context class reference) to the token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "79efdc37-0f06-43e6-a516-7bc9dc29f04d", + "name": "acr loa level", + "protocol": "openid-connect", + "protocolMapper": "oidc-acr-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true" + } + } + ] + }, + { + "id": "3bbbff21-0446-4813-8bdf-54c35d8fffca", + "name": "microprofile-jwt", + "description": "Microprofile - JWT built-in scope", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "0e996cda-fe5b-439d-ba4c-cf2129ae812f", + "name": "upn", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "upn", + "jsonType.label": "String" + } + }, + { + "id": "ddf1efe2-e765-475c-a4a0-d52f1f597834", + "name": "groups", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "multivalued": "true", + "user.attribute": "foo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "groups", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "93a40d0e-f163-42f7-a9d4-53cc2e17914e", + "name": "basic", + "description": "OpenID Connect scope for add all basic claims to the token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "41eb9e93-8e04-404b-a12b-40ef5a55f640", + "name": "sub", + "protocol": "openid-connect", + "protocolMapper": "oidc-sub-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "access.token.claim": "true" + } + }, + { + "id": "1291062a-10f6-4061-b9ea-f54ff5d8ec54", + "name": "auth_time", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "AUTH_TIME", + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true", + "claim.name": "auth_time", + "jsonType.label": "long" + } + } + ] + }, + { + "id": "9ea27173-e54b-42f0-8f6c-5a36c5073ede", + "name": "role_list", + "description": "SAML role list", + "protocol": "saml", + "attributes": { + "consent.screen.text": "${samlRoleListScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "d10a6975-8aeb-4215-8d6b-23b0286d4abb", + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + } + ] + }, + { + "id": "e8a99a5a-1519-4c7d-a3f0-ac6d34c61a0b", + "name": "phone", + "description": "OpenID Connect built-in scope: phone", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "consent.screen.text": "${phoneScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "b2de087f-169f-44b3-ad46-3a063ac9025f", + "name": "phone number", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "phoneNumber", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number", + "jsonType.label": "String" + } + }, + { + "id": "ffb8aebd-0d03-4811-8fd4-aa03bda36b2d", + "name": "phone number verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "phoneNumberVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number_verified", + "jsonType.label": "boolean" + } + } + ] + }, + { + "id": "30e06d84-f610-4f17-8820-6f785a510357", + "name": "address", + "description": "OpenID Connect built-in scope: address", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "consent.screen.text": "${addressScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "de707a09-a895-4b67-9ac5-0ff4e69715ea", + "name": "address", + "protocol": "openid-connect", + "protocolMapper": "oidc-address-mapper", + "consentRequired": false, + "config": { + "user.attribute.formatted": "formatted", + "user.attribute.country": "country", + "introspection.token.claim": "true", + "user.attribute.postal_code": "postal_code", + "userinfo.token.claim": "true", + "user.attribute.street": "street", + "id.token.claim": "true", + "user.attribute.region": "region", + "access.token.claim": "true", + "user.attribute.locality": "locality" + } + } + ] + }, + { + "id": "1762c903-9f07-451c-915d-855488e4aa42", + "name": "web-origins", + "description": "OpenID Connect scope for add allowed web origins to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "consent.screen.text": "", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "0164bdc3-c79d-4467-b6bf-ca9a6889d04c", + "name": "allowed web origins", + "protocol": "openid-connect", + "protocolMapper": "oidc-allowed-origins-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "access.token.claim": "true" + } + } + ] + }, + { + "id": "91301d6d-0bb9-4da6-b8db-ee2480e25fee", + "name": "roles", + "description": "OpenID Connect scope for add user roles to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "consent.screen.text": "${rolesScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "2880d772-b0da-4ee8-bf1e-3f729a945db9", + "name": "realm roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "introspection.token.claim": "true", + "access.token.claim": "true", + "claim.name": "realm_access.roles", + "jsonType.label": "String", + "multivalued": "true" + } + }, + { + "id": "535042c5-58c5-4225-94b8-0b5b3411968e", + "name": "client roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-client-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "introspection.token.claim": "true", + "access.token.claim": "true", + "claim.name": "resource_access.${client_id}.roles", + "jsonType.label": "String", + "multivalued": "true" + } + }, + { + "id": "a88432f1-565f-480d-958d-a5cea1dbcf0a", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "access.token.claim": "true" + } + } + ] + } + ], + "defaultDefaultClientScopes": ["role_list", "saml_organization", "profile", "email", "roles", "web-origins", "acr", "basic"], + "defaultOptionalClientScopes": ["offline_access", "address", "phone", "microprofile-jwt", "organization"], + "browserSecurityHeaders": { + "contentSecurityPolicyReportOnly": "", + "xContentTypeOptions": "nosniff", + "referrerPolicy": "no-referrer", + "xRobotsTag": "none", + "xFrameOptions": "SAMEORIGIN", + "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "xXSSProtection": "1; mode=block", + "strictTransportSecurity": "max-age=31536000; includeSubDomains" + }, + "smtpServer": {}, + "eventsEnabled": false, + "eventsListeners": ["jboss-logging"], + "enabledEventTypes": [], + "adminEventsEnabled": false, + "adminEventsDetailsEnabled": false, + "identityProviders": [], + "identityProviderMappers": [], + "components": { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ + { + "id": "a689e06a-e440-4d94-ba54-692fba5a5486", + "name": "Max Clients Limit", + "providerId": "max-clients", + "subType": "anonymous", + "subComponents": {}, + "config": { + "max-clients": ["200"] + } + }, + { + "id": "2778fda5-0a9f-40ab-ab4b-054ff8ce38e9", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allow-default-scopes": ["true"] + } + }, + { + "id": "36dc0167-9c9a-4b4a-9f04-29129aecac4d", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-sha256-pairwise-sub-mapper", + "oidc-usermodel-property-mapper", + "saml-user-attribute-mapper", + "oidc-full-name-mapper", + "saml-user-property-mapper", + "oidc-address-mapper", + "saml-role-list-mapper", + "oidc-usermodel-attribute-mapper" + ] + } + }, + { + "id": "4b79c6fd-5166-4bc2-ab0b-bff0018452f6", + "name": "Consent Required", + "providerId": "consent-required", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "2003600a-89fb-421e-9dfe-d5096ee7fd4e", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-full-name-mapper", + "oidc-usermodel-attribute-mapper", + "oidc-address-mapper", + "oidc-sha256-pairwise-sub-mapper", + "saml-role-list-mapper", + "saml-user-property-mapper", + "saml-user-attribute-mapper", + "oidc-usermodel-property-mapper" + ] + } + }, + { + "id": "d62a2e93-f877-462a-bad3-93dcf91d49d2", + "name": "Trusted Hosts", + "providerId": "trusted-hosts", + "subType": "anonymous", + "subComponents": {}, + "config": { + "host-sending-registration-request-must-match": ["true"], + "client-uris-must-match": ["true"] + } + }, + { + "id": "6e659a80-a638-4504-b507-21b9f77586ed", + "name": "Full Scope Disabled", + "providerId": "scope", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "9ef67c59-5c3e-40cf-90ee-516b2e35ed3d", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allow-default-scopes": ["true"] + } + } + ], + "org.keycloak.keys.KeyProvider": [ + { + "id": "b5365a56-e00d-4612-80bf-262a9c8dba7c", + "name": "rsa-enc-generated", + "providerId": "rsa-enc-generated", + "subComponents": {}, + "config": { + "privateKey": [ + "MIIEogIBAAKCAQEAudnbEbQij+g9JWYMuyJjF/sKe4fVEd9SrCmkFeAHZ7dEAmEKQcAlvJn1aL99pAm8KV0w9PAZugQx7ZzG6eUm4JLc+LYGJ8G8JDiVR6hIWRQ3k9HGcUwNacHKFlDj4XOeMykwLEo7jrQHAUx82vJO8bKZr2ixZqGc1UUUQNbEVYv0HzxhPKPoFYh9qQ8U6P0r/K9xIusLL6ZzmXx6uXqQuqtse05e5G9xwLjQDDrOKju4s1PJ1GZLt5Db9PGypMeA9J34tGL3O0rQom2WbO7R2GZGX044ZoNw6UnraGTmCxin1ywmwTq3JTb1IZ4DPLH/rucnPuKUb60B5ByPmXW/lQIDAQABAoIBAAlyYrGLbb9RX3xNa+O+O3m+U7nUPXcfWikwo6vN+6pgtScGzjnp3bEwxTnqE+WY7hTPLRwiMTiUmoIYuD6u3HNJW8yTmgv+y8SukJ34FpdakPmlTdg39K2VwWMxgOfWk+nHU/DIZC8chQeinu0VKICeIrQ5Ft1f5SQtEvq5v/iWIql+v2ipxdJ2dSl1NIO0/2S2Lyd7Slab50gbJ3kP0uZggN5IMtNd5GBvAbV+jaT4QWKuyXyHqOnyU/+2WU+XhmVPrX6c29sQg2CilqWf4RzEIeO/FgAiANEPyaAgW6mGtf17K1xsSrusyGMUsNGsGJSd7Q8K2o7g/Jv9V8160iECgYEA2xgKIoZ6+fT7UIDr+5insRr6PIht2WYl+JqibjzNRh+rvp1fjKkmjMOA39V6cvLaAHieSOUerSOD7nQONCedpHe6To1zOG9z5yYGgwa/2c/9eNORJq8vNw/4wXaARVf0mCNaexugPTdYvsSaqwW4+azIbB21xXUfKykLp0SjNfUCgYEA2ShJSZyTIqJ64IHi+Kj/E22ajK2DYBiFgNDmzKrW/ANO5ADumgMhahCcxW28Nw68n25vBWCK4o+6eVg1ldEdB1LxoKOYZAaA+zAiMsGI1/ndxdnlFopuJZguKhYDTmxzT0KcD9mApLKZBnCadGjG3FcdC8i14OK6S9lUIIpCvyECgYBXqWC0u7YMuPatGUhSXJwMAs1I1xWMvJBIziZbkTxY6GchV3pZn3xrKfYwmQvrXjvXoGtEo1gI0oMBL7JXL9qlabpDn9kQJZfsToygdFzi25OBerVDEykDEQLo9W8RT8Xv8YVMaJtOowyBF80CzMFcNMPkbmbCYMBd1ohxHsdm2QKBgGB9RhMvPzFkgLTBAdj7Plujl8hqULWiL6/NIsBOKLhRv/wPbfWA7pfySbZvy/Gq2qT8rNf2zb9dnb3NNAIdqIhYkoSOLGhFe4ohGRD0bZmJrMD80I3zdH2/4MNShKWUCqhtMGraeg60TMpPvlF7POEq0/0ocag7FgwdxQOwa3gBAoGASvjvVtfXyzWA9MycWLvlTPEGW5bjXrroYwulF8DkKfIaKWHfEiulTwQe4SHgS7CzWSg8KgvKIhJC/yTwfOtxZ894G9LWivwjjZCottIE+/qs6ioYSXouQr6IsWxs7U0i3gP36tsePjuSjR06kpBGfcFdynypAAq+mVBCV0Mxk9A=" + ], + "keyUse": ["ENC"], + "certificate": [ + "MIICnTCCAYUCBgGVU7avyzANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAd0ZWFjaGVyMB4XDTI1MDMwMTIxNTUzNloXDTM1MDMwMTIxNTcxNlowEjEQMA4GA1UEAwwHdGVhY2hlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALnZ2xG0Io/oPSVmDLsiYxf7CnuH1RHfUqwppBXgB2e3RAJhCkHAJbyZ9Wi/faQJvCldMPTwGboEMe2cxunlJuCS3Pi2BifBvCQ4lUeoSFkUN5PRxnFMDWnByhZQ4+FznjMpMCxKO460BwFMfNryTvGyma9osWahnNVFFEDWxFWL9B88YTyj6BWIfakPFOj9K/yvcSLrCy+mc5l8erl6kLqrbHtOXuRvccC40Aw6zio7uLNTydRmS7eQ2/TxsqTHgPSd+LRi9ztK0KJtlmzu0dhmRl9OOGaDcOlJ62hk5gsYp9csJsE6tyU29SGeAzyx/67nJz7ilG+tAeQcj5l1v5UCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAf3HTufrfWb0uqsEwfUETl4S4IbJOjqDdzFhUkMVtiq5I9LLUlJ7StZ6eoDCEoKUzF2lPy0qR2Om7IKC8BA7J5qUio0NSNh9j/t1Ipcjzx6SQI2cD6AjJFZndnF+OBTxdm9c6J+KMho6ZSMQEGwn2osgRBeItauxUshogQJPY/GzWMHlZyCAJcYtuflzgyw1VIQ0OiWCpCiSGeWpojxh19KR9qSBU1rETZMLokmdp84muq8aqEnNIFY5XRyUdH4gjNBx3TGsammZbvzuZdZIDvFNE19SXl/J9QcWJlRw0DuOblLcLKiamcJkQj35T9DgwtYRc/2zM3u8jNwQXKwrUWA==" + ], + "priority": ["100"], + "algorithm": ["RSA-OAEP"] + } + }, + { + "id": "ce5dcd75-614d-453a-868c-4413b4a10c39", + "name": "hmac-generated-hs512", + "providerId": "hmac-generated", + "subComponents": {}, + "config": { + "kid": ["a58f2df5-d24b-4aae-9e38-d42736883c7d"], + "secret": [ + "4sDZ4TC6Cuo0-A5Wa42n_HLCxFj6ir4enL6OmdllOTtR7f5YJN5bsPOJXOFGHeuNPe5jgNq2GfOaeqyQ19PnJMd3Ctsj7vQlx57hywXNvQ1FNuKL1uoxF2Szvw65Y4gIM7xoZpQglVhg2Zh7kA3HJEVhDvnmjNdjtm1QgdlFYws" + ], + "priority": ["100"], + "algorithm": ["HS512"] + } + }, + { + "id": "972a70cc-5e9d-4435-8423-f4d32e18d1e7", + "name": "rsa-generated", + "providerId": "rsa-generated", + "subComponents": {}, + "config": { + "privateKey": [ + "MIIEogIBAAKCAQEAy3EHaNAy6udZMC3A2KIZhMDg0SfGN+FVOKRJfh6aJwtNPuP6EXUhCMRSCXj3EHvFYrBJwKllC8li8MC5rw0vF/P9OY1zdbNkg8Rpwa1D55AS/MlYqiKHazKIV55SXVt5MKtjGATk9D4P/UZ8iP3viBnT0Kws4lWrAv1Uk12CkeLzojeTdCr4I8xgsj5U6dWu2f9DJlsieBhefgJpIXgdWXzVyGC3dOQOrCMHxYdyL4dbBlJPk13QJGkmgH65PnRB0Zu4CtlNHWN1jbXWcRNs8iMLZ4R36F8OzbMcv51lv7+0UTP7HJ4iRjw+sSlUH1AB+3sklyoNgRnW6sisEA+UFQIDAQABAoIBAA9kbWGWQwv31g0poQoi9ZhQOZJJlps6vsZq066ppRMoLT+BYzW37Xhq1iQmVVcXbj9BxErB5kXGhmhdxI7EihgfWzzkAWTZ3lSD41aGg/k8stsSZtV0iFdpetxaO7QZjClNBlHWaPY7zdzlXN3GjL146shChqDXR3mR7ji6HftolGVnmzUXRK+gZG9IirlC+qCJ+sd6m9h83x31X5PRT6yiJ/jeNN4XpuMh61xHFckFOFCGfV2isWM9qL5kLllN1+m8nMjt0HOeEB0GRrHTMSp7QC9RI0z1C/uxdAdSyMhCUtva8jAfjtqYAo7yc63zlOvlkFuYeOQ9X8UmnavBbL8CgYEA7FjyjVr3OK139528/FJMmLk2xOCDQ08pS+ADX5Eib7R62k1ZzXiKnv/8whfdQFeJwIunSYn+y2JCaMFjARrh04SELeH6/CvQ5uCknfIkLeNBij1ye5Ruy7JpaV3oe36h8sJYv1+p5RcrxxINBxbEeKM/YQWRwcXVE54MBCB4dKsCgYEA3FufmasbB4Pna2Txlgo+XCKpf+1U+gcN6lRzsKzqtFVT7+ofUndnqTKgPrLHYytYOFIZIIP8YZBno5gUvK7bFjgAGWacayYNWAXSiEhRQ3ah95Ii/b4lcU095GN/Xu68yqlGQc0GDVD9xRejBNyYgHc2GPQ9bigjsb6pQLy0mj8CgYAN5SjVcKyqM2CjOS3cM8Z3ECSNLJnrAiNuZ4wrOTAqGxVB8lw+PUEBGhG1I4wJdVwO6ub55tgJAwzedcgpT3hJZDgVLn0ACF9uw3RKKOtBm2PGCdjKNS7SYPnbjP7XC9nfmNd44NnvMw6K1J/Zc9g3M3nNbXNlTgk57wfL0lDiowKBgEIF4cf1EGAsEUaINCo0X4LTj92YioFvY6f2LcOdy6TEfCXCDCh1RkXXuVOP1VXNQt19G7I2WYQR9Dt78Zqm+VWq6byyleM0v4LEG9Rhdpe0D8tRqdJFCorsDcNEXIFhHofKOBa3Cz0qKx7Gej2Wqsqy7S6E33MF68vxyFxxLduZAoGAcHhDa8r7EMFTEt3rZIqmblqOYvhfMKJ+Ruom11mUSHWLgyQGzK8mVPhB59J7gt0DKU6XRIwby/7c7x2wFWQ+dsy03PN49PDtewLcGtrsicJlY2mofFZpsFsYhOpyhPg4/zFiX77Ev3UEYiJJ4qXnlV5Yb+ae5D8ZNlmhIP1HQY4=" + ], + "keyUse": ["SIG"], + "certificate": [ + "MIICnTCCAYUCBgGVU7avAzANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAd0ZWFjaGVyMB4XDTI1MDMwMTIxNTUzNloXDTM1MDMwMTIxNTcxNlowEjEQMA4GA1UEAwwHdGVhY2hlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMtxB2jQMurnWTAtwNiiGYTA4NEnxjfhVTikSX4emicLTT7j+hF1IQjEUgl49xB7xWKwScCpZQvJYvDAua8NLxfz/TmNc3WzZIPEacGtQ+eQEvzJWKoih2syiFeeUl1beTCrYxgE5PQ+D/1GfIj974gZ09CsLOJVqwL9VJNdgpHi86I3k3Qq+CPMYLI+VOnVrtn/QyZbIngYXn4CaSF4HVl81chgt3TkDqwjB8WHci+HWwZST5Nd0CRpJoB+uT50QdGbuArZTR1jdY211nETbPIjC2eEd+hfDs2zHL+dZb+/tFEz+xyeIkY8PrEpVB9QAft7JJcqDYEZ1urIrBAPlBUCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAHkOqsY1iHqqCMDFvTh/XCNZRwdLdGY6ev+5zShrgb8MPJNgM/HIqQtwQ9wuKf5RfMF1+FQdSU1eavTeuHXp4IuMgv97DAjdZ/pBGHz5tCWMdlaf+Au/1zDoqCV91CbGuV6WHaUhDJLZfp9/phiq2BzPZO6LeWhFJLMzH+N6rPZ7Om72rjTN31TlLLgmLuKlOhMp2QpyaQB16g4ksLGIYq7IXIbCqPRuB33k3gO/+ZMYRpU2U4DQ3FZyIe4LzLXQQ7VSFz/x/rvnbF+hHBdcbszUvsQYCS21aZ6nAq4CGinU2iAOLXHmFotKs+01KZT1N3ZGlGQmHM8ywYyb9qbcfPA==" + ], + "priority": ["100"] + } + }, + { + "id": "24e3094f-f962-49bd-b355-ff3096bfefe8", + "name": "aes-generated", + "providerId": "aes-generated", + "subComponents": {}, + "config": { + "kid": ["52ac32c1-f589-4e04-9667-16d2e7bd707a"], + "secret": ["ZEiWoUCZ30PSKa2rx8UXTQ"], + "priority": ["100"] + } + } + ] + }, + "internationalizationEnabled": false, + "supportedLocales": [], + "authenticationFlows": [ + { + "id": "2ac7aebb-c1ac-4fdf-9687-cedd34665024", + "alias": "Account verification options", + "description": "Method with which to verity the existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-email-verification", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Verify Existing Account by Re-authentication", + "userSetupAllowed": false + } + ] + }, + { + "id": "2505f3dc-719b-43a1-9631-585302dd449e", + "alias": "Browser - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "5a07c120-c34b-4cf2-b38d-2e558af6853a", + "alias": "Browser - Conditional Organization", + "description": "Flow to determine if the organization identity-first login is to be used", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "organization", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "a3317f52-b2bc-4b4c-af14-53901d253fca", + "alias": "Direct Grant - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "direct-grant-validate-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "2281818c-fb40-4997-a1ad-fc9ad2c3cacc", + "alias": "First Broker Login - Conditional Organization", + "description": "Flow to determine if the authenticator that adds organization members is to be used", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "idp-add-organization-member", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "fcab0380-ca38-4f66-aaf2-ec741ef8be8e", + "alias": "First broker login - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "ae2e214a-82b6-4d78-a7d0-f80d454e5083", + "alias": "Handle Existing Account", + "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-confirm-link", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Account verification options", + "userSetupAllowed": false + } + ] + }, + { + "id": "ad2add46-e1bb-47bf-a125-d76c517f66a4", + "alias": "Organization", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 10, + "autheticatorFlow": true, + "flowAlias": "Browser - Conditional Organization", + "userSetupAllowed": false + } + ] + }, + { + "id": "74e5d429-4db2-4323-b504-005c03e530fc", + "alias": "Reset - Conditional OTP", + "description": "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "d11dbfe7-2472-4cda-a7f5-e9a536154028", + "alias": "User creation or linking", + "description": "Flow for the existing/non-existing user alternatives", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "create unique user config", + "authenticator": "idp-create-user-if-unique", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Handle Existing Account", + "userSetupAllowed": false + } + ] + }, + { + "id": "f1131dc8-ea34-48e1-9363-438c15f985a4", + "alias": "Verify Existing Account by Re-authentication", + "description": "Reauthentication of existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "First broker login - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "f2880986-ef01-4199-ac31-35e0b16c989b", + "alias": "browser", + "description": "Browser based authentication", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-cookie", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-spnego", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "identity-provider-redirector", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 25, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 26, + "autheticatorFlow": true, + "flowAlias": "Organization", + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 30, + "autheticatorFlow": true, + "flowAlias": "forms", + "userSetupAllowed": false + } + ] + }, + { + "id": "a08dca2e-d491-483f-a310-25bcfa2d89b3", + "alias": "clients", + "description": "Base authentication for clients", + "providerId": "client-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "client-secret", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-secret-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-x509", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 40, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "4742ab83-03c9-417d-ba61-017d9f02afb3", + "alias": "direct grant", + "description": "OpenID Connect Resource Owner Grant", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "direct-grant-validate-username", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "direct-grant-validate-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 30, + "autheticatorFlow": true, + "flowAlias": "Direct Grant - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "458f78fd-84e5-4e4d-8198-200f25942134", + "alias": "docker auth", + "description": "Used by Docker clients to authenticate against the IDP", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "docker-http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "8cbdd82f-3794-4fce-9494-70279a3d1fcb", + "alias": "first broker login", + "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "review profile config", + "authenticator": "idp-review-profile", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "User creation or linking", + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 50, + "autheticatorFlow": true, + "flowAlias": "First Broker Login - Conditional Organization", + "userSetupAllowed": false + } + ] + }, + { + "id": "b64919c6-da2b-4e66-bcc6-0112d9e3132b", + "alias": "forms", + "description": "Username, password, otp and other auth forms.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Browser - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "3c8979fe-c98c-4911-b16c-510dba8fb8e3", + "alias": "registration", + "description": "Registration flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-page-form", + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": true, + "flowAlias": "registration form", + "userSetupAllowed": false + } + ] + }, + { + "id": "6f598384-bb66-485e-8ed5-7da83c1deba1", + "alias": "registration form", + "description": "Registration form", + "providerId": "form-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-user-creation", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-password-action", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 50, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-recaptcha-action", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 60, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-terms-and-conditions", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 70, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "086acb80-23bb-496d-a982-0d8886b2e844", + "alias": "reset credentials", + "description": "Reset credentials for a user if they forgot their password or something", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "reset-credentials-choose-user", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-credential-email", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 40, + "autheticatorFlow": true, + "flowAlias": "Reset - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "2b5042d2-f5e2-456c-bd94-1f23ea0bfb20", + "alias": "saml ecp", + "description": "SAML ECP Profile Authentication Flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + } + ], + "authenticatorConfig": [ + { + "id": "3007c3b0-cdd5-4464-93f4-23e439b15253", + "alias": "create unique user config", + "config": { + "require.password.update.after.registration": "false" + } + }, + { + "id": "ce14faa0-34fe-496f-bcb5-a7e72fcf3fbb", + "alias": "review profile config", + "config": { + "update.profile.on.first.login": "missing" + } + } + ], + "requiredActions": [ + { + "alias": "CONFIGURE_TOTP", + "name": "Configure OTP", + "providerId": "CONFIGURE_TOTP", + "enabled": true, + "defaultAction": false, + "priority": 10, + "config": {} + }, + { + "alias": "TERMS_AND_CONDITIONS", + "name": "Terms and Conditions", + "providerId": "TERMS_AND_CONDITIONS", + "enabled": false, + "defaultAction": false, + "priority": 20, + "config": {} + }, + { + "alias": "UPDATE_PASSWORD", + "name": "Update Password", + "providerId": "UPDATE_PASSWORD", + "enabled": true, + "defaultAction": false, + "priority": 30, + "config": {} + }, + { + "alias": "UPDATE_PROFILE", + "name": "Update Profile", + "providerId": "UPDATE_PROFILE", + "enabled": true, + "defaultAction": false, + "priority": 40, + "config": {} + }, + { + "alias": "VERIFY_EMAIL", + "name": "Verify Email", + "providerId": "VERIFY_EMAIL", + "enabled": true, + "defaultAction": false, + "priority": 50, + "config": {} + }, + { + "alias": "delete_account", + "name": "Delete Account", + "providerId": "delete_account", + "enabled": false, + "defaultAction": false, + "priority": 60, + "config": {} + }, + { + "alias": "webauthn-register", + "name": "Webauthn Register", + "providerId": "webauthn-register", + "enabled": true, + "defaultAction": false, + "priority": 70, + "config": {} + }, + { + "alias": "webauthn-register-passwordless", + "name": "Webauthn Register Passwordless", + "providerId": "webauthn-register-passwordless", + "enabled": true, + "defaultAction": false, + "priority": 80, + "config": {} + }, + { + "alias": "VERIFY_PROFILE", + "name": "Verify Profile", + "providerId": "VERIFY_PROFILE", + "enabled": true, + "defaultAction": false, + "priority": 90, + "config": {} + }, + { + "alias": "delete_credential", + "name": "Delete Credential", + "providerId": "delete_credential", + "enabled": true, + "defaultAction": false, + "priority": 100, + "config": {} + }, + { + "alias": "update_user_locale", + "name": "Update User Locale", + "providerId": "update_user_locale", + "enabled": true, + "defaultAction": false, + "priority": 1000, + "config": {} + } + ], + "browserFlow": "browser", + "registrationFlow": "registration", + "directGrantFlow": "direct grant", + "resetCredentialsFlow": "reset credentials", + "clientAuthenticationFlow": "clients", + "dockerAuthenticationFlow": "docker auth", + "firstBrokerLoginFlow": "first broker login", + "attributes": { + "cibaBackchannelTokenDeliveryMode": "poll", + "cibaExpiresIn": "120", + "cibaAuthRequestedUserHint": "login_hint", + "oauth2DeviceCodeLifespan": "600", + "oauth2DevicePollingInterval": "5", + "parRequestUriLifespan": "60", + "cibaInterval": "5", + "realmReusableOtpCode": "false" + }, + "keycloakVersion": "26.1.3", + "userManagedAccessAllowed": false, + "organizationsEnabled": false, + "verifiableCredentialsEnabled": false, + "adminPermissionsEnabled": false, + "clientProfiles": { + "profiles": [] + }, + "clientPolicies": { + "policies": [] } - }, - "groups" : [ ], - "defaultRole" : { - "id" : "6b546a34-4ebe-4c09-b274-fc1f6bebdf93", - "name" : "default-roles-teacher", - "description" : "${role_default-roles}", - "composite" : true, - "clientRole" : false, - "containerId" : "02ba6887-22f5-4de4-ad9b-cb2a2060bce1" - }, - "requiredCredentials" : [ "password" ], - "otpPolicyType" : "totp", - "otpPolicyAlgorithm" : "HmacSHA1", - "otpPolicyInitialCounter" : 0, - "otpPolicyDigits" : 6, - "otpPolicyLookAheadWindow" : 1, - "otpPolicyPeriod" : 30, - "otpPolicyCodeReusable" : false, - "otpSupportedApplications" : [ "totpAppFreeOTPName", "totpAppGoogleName", "totpAppMicrosoftAuthenticatorName" ], - "localizationTexts" : { }, - "webAuthnPolicyRpEntityName" : "keycloak", - "webAuthnPolicySignatureAlgorithms" : [ "ES256", "RS256" ], - "webAuthnPolicyRpId" : "", - "webAuthnPolicyAttestationConveyancePreference" : "not specified", - "webAuthnPolicyAuthenticatorAttachment" : "not specified", - "webAuthnPolicyRequireResidentKey" : "not specified", - "webAuthnPolicyUserVerificationRequirement" : "not specified", - "webAuthnPolicyCreateTimeout" : 0, - "webAuthnPolicyAvoidSameAuthenticatorRegister" : false, - "webAuthnPolicyAcceptableAaguids" : [ ], - "webAuthnPolicyExtraOrigins" : [ ], - "webAuthnPolicyPasswordlessRpEntityName" : "keycloak", - "webAuthnPolicyPasswordlessSignatureAlgorithms" : [ "ES256", "RS256" ], - "webAuthnPolicyPasswordlessRpId" : "", - "webAuthnPolicyPasswordlessAttestationConveyancePreference" : "not specified", - "webAuthnPolicyPasswordlessAuthenticatorAttachment" : "not specified", - "webAuthnPolicyPasswordlessRequireResidentKey" : "not specified", - "webAuthnPolicyPasswordlessUserVerificationRequirement" : "not specified", - "webAuthnPolicyPasswordlessCreateTimeout" : 0, - "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister" : false, - "webAuthnPolicyPasswordlessAcceptableAaguids" : [ ], - "webAuthnPolicyPasswordlessExtraOrigins" : [ ], - "users" : [ { - "id" : "63dbbb64-c09f-4e4e-9cbf-af9e557dbb09", - "username" : "testleerkracht1", - "firstName" : "Kris", - "lastName" : "Coolsaet", - "email" : "kris.coolsaet@ugent.be", - "emailVerified" : false, - "createdTimestamp" : 1740866530658, - "enabled" : true, - "totp" : false, - "credentials" : [ { - "id" : "c5382bf7-ccc6-47de-93b9-2c11ea7b6862", - "type" : "password", - "userLabel" : "My password", - "createdDate" : 1740866544032, - "secretData" : "{\"value\":\"H2vKyHF3j/alz6CNap2uaKSRb+/wrWImVecj7dcHe1w=\",\"salt\":\"32WjW1KzFaR5RJqU0Pfq9w==\",\"additionalParameters\":{}}", - "credentialData" : "{\"hashIterations\":5,\"algorithm\":\"argon2\",\"additionalParameters\":{\"hashLength\":[\"32\"],\"memory\":[\"7168\"],\"type\":[\"id\"],\"version\":[\"1.3\"],\"parallelism\":[\"1\"]}}" - } ], - "disableableCredentialTypes" : [ ], - "requiredActions" : [ ], - "realmRoles" : [ "default-roles-teacher" ], - "notBefore" : 0, - "groups" : [ ] - } ], - "scopeMappings" : [ { - "clientScope" : "offline_access", - "roles" : [ "offline_access" ] - } ], - "clientScopeMappings" : { - "account" : [ { - "client" : "account-console", - "roles" : [ "manage-account", "view-groups" ] - } ] - }, - "clients" : [ { - "id" : "7ceb65eb-30da-4dc3-95bc-f06863362fd6", - "clientId" : "account", - "name" : "${client_account}", - "rootUrl" : "${authBaseUrl}", - "baseUrl" : "/realms/teacher/account/", - "surrogateAuthRequired" : false, - "enabled" : true, - "alwaysDisplayInConsole" : false, - "clientAuthenticatorType" : "client-secret", - "redirectUris" : [ "/realms/teacher/account/*" ], - "webOrigins" : [ ], - "notBefore" : 0, - "bearerOnly" : false, - "consentRequired" : false, - "standardFlowEnabled" : true, - "implicitFlowEnabled" : false, - "directAccessGrantsEnabled" : false, - "serviceAccountsEnabled" : false, - "publicClient" : true, - "frontchannelLogout" : false, - "protocol" : "openid-connect", - "attributes" : { - "realm_client" : "false", - "post.logout.redirect.uris" : "+" - }, - "authenticationFlowBindingOverrides" : { }, - "fullScopeAllowed" : false, - "nodeReRegistrationTimeout" : 0, - "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "basic", "email" ], - "optionalClientScopes" : [ "address", "phone", "offline_access", "organization", "microprofile-jwt" ] - }, { - "id" : "920e8621-36b5-4046-b1cd-4b293668f64b", - "clientId" : "account-console", - "name" : "${client_account-console}", - "rootUrl" : "${authBaseUrl}", - "baseUrl" : "/realms/teacher/account/", - "surrogateAuthRequired" : false, - "enabled" : true, - "alwaysDisplayInConsole" : false, - "clientAuthenticatorType" : "client-secret", - "redirectUris" : [ "/realms/teacher/account/*" ], - "webOrigins" : [ ], - "notBefore" : 0, - "bearerOnly" : false, - "consentRequired" : false, - "standardFlowEnabled" : true, - "implicitFlowEnabled" : false, - "directAccessGrantsEnabled" : false, - "serviceAccountsEnabled" : false, - "publicClient" : true, - "frontchannelLogout" : false, - "protocol" : "openid-connect", - "attributes" : { - "realm_client" : "false", - "post.logout.redirect.uris" : "+", - "pkce.code.challenge.method" : "S256" - }, - "authenticationFlowBindingOverrides" : { }, - "fullScopeAllowed" : false, - "nodeReRegistrationTimeout" : 0, - "protocolMappers" : [ { - "id" : "cd3f4ae0-3008-488b-88c5-b6d640a9edd3", - "name" : "audience resolve", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-audience-resolve-mapper", - "consentRequired" : false, - "config" : { } - } ], - "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "basic", "email" ], - "optionalClientScopes" : [ "address", "phone", "offline_access", "organization", "microprofile-jwt" ] - }, { - "id" : "9d7b2827-b7bb-451e-ad38-8f55a69f7c9c", - "clientId" : "admin-cli", - "name" : "${client_admin-cli}", - "surrogateAuthRequired" : false, - "enabled" : true, - "alwaysDisplayInConsole" : false, - "clientAuthenticatorType" : "client-secret", - "redirectUris" : [ ], - "webOrigins" : [ ], - "notBefore" : 0, - "bearerOnly" : false, - "consentRequired" : false, - "standardFlowEnabled" : false, - "implicitFlowEnabled" : false, - "directAccessGrantsEnabled" : true, - "serviceAccountsEnabled" : false, - "publicClient" : true, - "frontchannelLogout" : false, - "protocol" : "openid-connect", - "attributes" : { - "realm_client" : "false", - "client.use.lightweight.access.token.enabled" : "true" - }, - "authenticationFlowBindingOverrides" : { }, - "fullScopeAllowed" : true, - "nodeReRegistrationTimeout" : 0, - "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "basic", "email" ], - "optionalClientScopes" : [ "address", "phone", "offline_access", "organization", "microprofile-jwt" ] - }, { - "id" : "cfd0202e-a6b9-4c5e-9f49-2ef17df9089b", - "clientId" : "broker", - "name" : "${client_broker}", - "surrogateAuthRequired" : false, - "enabled" : true, - "alwaysDisplayInConsole" : false, - "clientAuthenticatorType" : "client-secret", - "redirectUris" : [ ], - "webOrigins" : [ ], - "notBefore" : 0, - "bearerOnly" : true, - "consentRequired" : false, - "standardFlowEnabled" : true, - "implicitFlowEnabled" : false, - "directAccessGrantsEnabled" : false, - "serviceAccountsEnabled" : false, - "publicClient" : false, - "frontchannelLogout" : false, - "protocol" : "openid-connect", - "attributes" : { - "realm_client" : "true" - }, - "authenticationFlowBindingOverrides" : { }, - "fullScopeAllowed" : false, - "nodeReRegistrationTimeout" : 0, - "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "basic", "email" ], - "optionalClientScopes" : [ "address", "phone", "offline_access", "organization", "microprofile-jwt" ] - }, { - "id" : "abdee18a-4549-48b5-b976-4c1a42820ef9", - "clientId" : "dwengo", - "name" : "Dwengo", - "description" : "", - "rootUrl" : "http://localhost:5173", - "adminUrl" : "http://localhost:5173", - "baseUrl" : "http://localhost:5173", - "surrogateAuthRequired" : false, - "enabled" : true, - "alwaysDisplayInConsole" : false, - "clientAuthenticatorType" : "client-secret", - "redirectUris" : [ "urn:ietf:wg:oauth:2.0:oob", "http://localhost:5173/*", "http://localhost:5173" ], - "webOrigins" : [ "+" ], - "notBefore" : 0, - "bearerOnly" : false, - "consentRequired" : false, - "standardFlowEnabled" : true, - "implicitFlowEnabled" : true, - "directAccessGrantsEnabled" : false, - "serviceAccountsEnabled" : false, - "publicClient" : true, - "frontchannelLogout" : true, - "protocol" : "openid-connect", - "attributes" : { - "realm_client" : "false", - "oidc.ciba.grant.enabled" : "false", - "backchannel.logout.session.required" : "true", - "post.logout.redirect.uris" : "+", - "frontchannel.logout.session.required" : "true", - "oauth2.device.authorization.grant.enabled" : "false", - "display.on.consent.screen" : "false", - "backchannel.logout.revoke.offline.tokens" : "false" - }, - "authenticationFlowBindingOverrides" : { }, - "fullScopeAllowed" : true, - "nodeReRegistrationTimeout" : -1, - "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "basic", "email" ], - "optionalClientScopes" : [ "address", "phone", "offline_access", "organization", "microprofile-jwt" ] - }, { - "id" : "112e0e97-df75-4ed7-a35f-03b7c5f9d36a", - "clientId" : "realm-management", - "name" : "${client_realm-management}", - "surrogateAuthRequired" : false, - "enabled" : true, - "alwaysDisplayInConsole" : false, - "clientAuthenticatorType" : "client-secret", - "redirectUris" : [ ], - "webOrigins" : [ ], - "notBefore" : 0, - "bearerOnly" : true, - "consentRequired" : false, - "standardFlowEnabled" : true, - "implicitFlowEnabled" : false, - "directAccessGrantsEnabled" : false, - "serviceAccountsEnabled" : false, - "publicClient" : false, - "frontchannelLogout" : false, - "protocol" : "openid-connect", - "attributes" : { - "realm_client" : "true" - }, - "authenticationFlowBindingOverrides" : { }, - "fullScopeAllowed" : false, - "nodeReRegistrationTimeout" : 0, - "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "basic", "email" ], - "optionalClientScopes" : [ "address", "phone", "offline_access", "organization", "microprofile-jwt" ] - }, { - "id" : "c421853c-5bdf-4ea9-ae97-51f5ad7b8df8", - "clientId" : "security-admin-console", - "name" : "${client_security-admin-console}", - "rootUrl" : "${authAdminUrl}", - "baseUrl" : "/admin/teacher/console/", - "surrogateAuthRequired" : false, - "enabled" : true, - "alwaysDisplayInConsole" : false, - "clientAuthenticatorType" : "client-secret", - "redirectUris" : [ "/admin/teacher/console/*" ], - "webOrigins" : [ "+" ], - "notBefore" : 0, - "bearerOnly" : false, - "consentRequired" : false, - "standardFlowEnabled" : true, - "implicitFlowEnabled" : false, - "directAccessGrantsEnabled" : false, - "serviceAccountsEnabled" : false, - "publicClient" : true, - "frontchannelLogout" : false, - "protocol" : "openid-connect", - "attributes" : { - "realm_client" : "false", - "client.use.lightweight.access.token.enabled" : "true", - "post.logout.redirect.uris" : "+", - "pkce.code.challenge.method" : "S256" - }, - "authenticationFlowBindingOverrides" : { }, - "fullScopeAllowed" : true, - "nodeReRegistrationTimeout" : 0, - "protocolMappers" : [ { - "id" : "a9a893af-925e-46c9-ba33-47b06101ce5f", - "name" : "locale", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "locale", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "locale", - "jsonType.label" : "String" - } - } ], - "defaultClientScopes" : [ "web-origins", "acr", "roles", "profile", "basic", "email" ], - "optionalClientScopes" : [ "address", "phone", "offline_access", "organization", "microprofile-jwt" ] - } ], - "clientScopes" : [ { - "id" : "fef4fbeb-d7e6-4474-b802-6c63df0dc9a3", - "name" : "saml_organization", - "description" : "Organization Membership", - "protocol" : "saml", - "attributes" : { - "display.on.consent.screen" : "false" - }, - "protocolMappers" : [ { - "id" : "2384b79b-5cc3-4e1c-b4b2-4bee2ceeed72", - "name" : "organization", - "protocol" : "saml", - "protocolMapper" : "saml-organization-membership-mapper", - "consentRequired" : false, - "config" : { } - } ] - }, { - "id" : "a097893c-7eed-4556-b2ed-3751c7fc3c51", - "name" : "offline_access", - "description" : "OpenID Connect built-in scope: offline_access", - "protocol" : "openid-connect", - "attributes" : { - "consent.screen.text" : "${offlineAccessScopeConsentText}", - "display.on.consent.screen" : "true" - } - }, { - "id" : "ffc38cb2-eb10-47cf-a2d6-6647fdd4da65", - "name" : "service_account", - "description" : "Specific scope for a client enabled for service accounts", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "false", - "display.on.consent.screen" : "false" - }, - "protocolMappers" : [ { - "id" : "06ed3629-1c3d-48d9-80c6-98fcd3958c48", - "name" : "Client Host", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usersessionmodel-note-mapper", - "consentRequired" : false, - "config" : { - "user.session.note" : "clientHost", - "id.token.claim" : "true", - "introspection.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "clientHost", - "jsonType.label" : "String" - } - }, { - "id" : "04eeb81e-05c0-484a-91df-9a79138bcd66", - "name" : "Client IP Address", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usersessionmodel-note-mapper", - "consentRequired" : false, - "config" : { - "user.session.note" : "clientAddress", - "id.token.claim" : "true", - "introspection.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "clientAddress", - "jsonType.label" : "String" - } - }, { - "id" : "6e673f49-ce38-4583-8040-8a2e7ec5e7c8", - "name" : "Client ID", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usersessionmodel-note-mapper", - "consentRequired" : false, - "config" : { - "user.session.note" : "client_id", - "id.token.claim" : "true", - "introspection.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "client_id", - "jsonType.label" : "String" - } - } ] - }, { - "id" : "ee188d9c-ab26-4e53-a16c-c9f77094f854", - "name" : "profile", - "description" : "OpenID Connect built-in scope: profile", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "true", - "consent.screen.text" : "${profileScopeConsentText}", - "display.on.consent.screen" : "true" - }, - "protocolMappers" : [ { - "id" : "05ff270b-6a50-4bbb-903d-9546a59f20bf", - "name" : "picture", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "picture", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "picture", - "jsonType.label" : "String" - } - }, { - "id" : "394f808d-bc7b-476e-a372-7cfece5c6db0", - "name" : "gender", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "gender", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "gender", - "jsonType.label" : "String" - } - }, { - "id" : "0371c44f-c6e0-4f88-ac8f-17a56e2b90f8", - "name" : "profile", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "profile", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "profile", - "jsonType.label" : "String" - } - }, { - "id" : "21d66073-42f2-443b-aac4-e49c9038253c", - "name" : "birthdate", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "birthdate", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "birthdate", - "jsonType.label" : "String" - } - }, { - "id" : "5cc6a97f-9d1a-4c72-b682-af6d1bd36883", - "name" : "full name", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-full-name-mapper", - "consentRequired" : false, - "config" : { - "id.token.claim" : "true", - "introspection.token.claim" : "true", - "access.token.claim" : "true", - "userinfo.token.claim" : "true" - } - }, { - "id" : "d6a6d46b-80a7-4228-af07-0faae2911fed", - "name" : "nickname", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "nickname", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "nickname", - "jsonType.label" : "String" - } - }, { - "id" : "322b508a-7464-4b0f-90df-3f489975a62e", - "name" : "zoneinfo", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "zoneinfo", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "zoneinfo", - "jsonType.label" : "String" - } - }, { - "id" : "f757ae7a-3005-4899-bb4e-da1ab4b47bb0", - "name" : "locale", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "locale", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "locale", - "jsonType.label" : "String" - } - }, { - "id" : "bab8eb17-0cb0-4275-8456-aa1d65933a35", - "name" : "updated at", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "updatedAt", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "updated_at", - "jsonType.label" : "long" - } - }, { - "id" : "6ea1d43c-d4c7-4f2f-93b0-dfdb3bb584eb", - "name" : "given name", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "firstName", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "given_name", - "jsonType.label" : "String" - } - }, { - "id" : "3a2ebc93-05fb-4904-996b-5e3331b72fcd", - "name" : "family name", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "lastName", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "family_name", - "jsonType.label" : "String" - } - }, { - "id" : "217b417e-d4f6-4225-bf92-3bd38f6fbefb", - "name" : "username", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "username", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "preferred_username", - "jsonType.label" : "String" - } - }, { - "id" : "3dd5da51-5842-4358-a69f-f7ffffe521ac", - "name" : "website", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "website", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "website", - "jsonType.label" : "String" - } - }, { - "id" : "790bda99-1c27-4970-b3b9-4fa1c90c738c", - "name" : "middle name", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "middleName", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "middle_name", - "jsonType.label" : "String" - } - } ] - }, { - "id" : "e6cf59c7-9390-4f48-ab01-79a0fa138960", - "name" : "organization", - "description" : "Additional claims about the organization a subject belongs to", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "true", - "consent.screen.text" : "${organizationScopeConsentText}", - "display.on.consent.screen" : "true" - }, - "protocolMappers" : [ { - "id" : "417ff129-6b95-4e95-9f57-a6699ca18d8d", - "name" : "organization", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-organization-membership-mapper", - "consentRequired" : false, - "config" : { - "id.token.claim" : "true", - "introspection.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "organization", - "jsonType.label" : "String", - "multivalued" : "true" - } - } ] - }, { - "id" : "43d92ef5-76d8-4df0-84b5-5f833875d345", - "name" : "email", - "description" : "OpenID Connect built-in scope: email", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "true", - "consent.screen.text" : "${emailScopeConsentText}", - "display.on.consent.screen" : "true" - }, - "protocolMappers" : [ { - "id" : "74d21718-190a-4c53-b446-b07e5f029394", - "name" : "email verified", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-property-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "emailVerified", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "email_verified", - "jsonType.label" : "boolean" - } - }, { - "id" : "949871a0-d68c-4563-a9b3-945a3148f937", - "name" : "email", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "email", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "email", - "jsonType.label" : "String" - } - } ] - }, { - "id" : "b07a2014-d07e-450f-a593-66e9f9cf4799", - "name" : "acr", - "description" : "OpenID Connect scope for add acr (authentication context class reference) to the token", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "false", - "display.on.consent.screen" : "false" - }, - "protocolMappers" : [ { - "id" : "79efdc37-0f06-43e6-a516-7bc9dc29f04d", - "name" : "acr loa level", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-acr-mapper", - "consentRequired" : false, - "config" : { - "id.token.claim" : "true", - "introspection.token.claim" : "true", - "access.token.claim" : "true" - } - } ] - }, { - "id" : "3bbbff21-0446-4813-8bdf-54c35d8fffca", - "name" : "microprofile-jwt", - "description" : "Microprofile - JWT built-in scope", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "true", - "display.on.consent.screen" : "false" - }, - "protocolMappers" : [ { - "id" : "0e996cda-fe5b-439d-ba4c-cf2129ae812f", - "name" : "upn", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "username", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "upn", - "jsonType.label" : "String" - } - }, { - "id" : "ddf1efe2-e765-475c-a4a0-d52f1f597834", - "name" : "groups", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-realm-role-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "multivalued" : "true", - "user.attribute" : "foo", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "groups", - "jsonType.label" : "String" - } - } ] - }, { - "id" : "93a40d0e-f163-42f7-a9d4-53cc2e17914e", - "name" : "basic", - "description" : "OpenID Connect scope for add all basic claims to the token", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "false", - "display.on.consent.screen" : "false" - }, - "protocolMappers" : [ { - "id" : "41eb9e93-8e04-404b-a12b-40ef5a55f640", - "name" : "sub", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-sub-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "access.token.claim" : "true" - } - }, { - "id" : "1291062a-10f6-4061-b9ea-f54ff5d8ec54", - "name" : "auth_time", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usersessionmodel-note-mapper", - "consentRequired" : false, - "config" : { - "user.session.note" : "AUTH_TIME", - "id.token.claim" : "true", - "introspection.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "auth_time", - "jsonType.label" : "long" - } - } ] - }, { - "id" : "9ea27173-e54b-42f0-8f6c-5a36c5073ede", - "name" : "role_list", - "description" : "SAML role list", - "protocol" : "saml", - "attributes" : { - "consent.screen.text" : "${samlRoleListScopeConsentText}", - "display.on.consent.screen" : "true" - }, - "protocolMappers" : [ { - "id" : "d10a6975-8aeb-4215-8d6b-23b0286d4abb", - "name" : "role list", - "protocol" : "saml", - "protocolMapper" : "saml-role-list-mapper", - "consentRequired" : false, - "config" : { - "single" : "false", - "attribute.nameformat" : "Basic", - "attribute.name" : "Role" - } - } ] - }, { - "id" : "e8a99a5a-1519-4c7d-a3f0-ac6d34c61a0b", - "name" : "phone", - "description" : "OpenID Connect built-in scope: phone", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "true", - "consent.screen.text" : "${phoneScopeConsentText}", - "display.on.consent.screen" : "true" - }, - "protocolMappers" : [ { - "id" : "b2de087f-169f-44b3-ad46-3a063ac9025f", - "name" : "phone number", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "phoneNumber", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "phone_number", - "jsonType.label" : "String" - } - }, { - "id" : "ffb8aebd-0d03-4811-8fd4-aa03bda36b2d", - "name" : "phone number verified", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-attribute-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "userinfo.token.claim" : "true", - "user.attribute" : "phoneNumberVerified", - "id.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "phone_number_verified", - "jsonType.label" : "boolean" - } - } ] - }, { - "id" : "30e06d84-f610-4f17-8820-6f785a510357", - "name" : "address", - "description" : "OpenID Connect built-in scope: address", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "true", - "consent.screen.text" : "${addressScopeConsentText}", - "display.on.consent.screen" : "true" - }, - "protocolMappers" : [ { - "id" : "de707a09-a895-4b67-9ac5-0ff4e69715ea", - "name" : "address", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-address-mapper", - "consentRequired" : false, - "config" : { - "user.attribute.formatted" : "formatted", - "user.attribute.country" : "country", - "introspection.token.claim" : "true", - "user.attribute.postal_code" : "postal_code", - "userinfo.token.claim" : "true", - "user.attribute.street" : "street", - "id.token.claim" : "true", - "user.attribute.region" : "region", - "access.token.claim" : "true", - "user.attribute.locality" : "locality" - } - } ] - }, { - "id" : "1762c903-9f07-451c-915d-855488e4aa42", - "name" : "web-origins", - "description" : "OpenID Connect scope for add allowed web origins to the access token", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "false", - "consent.screen.text" : "", - "display.on.consent.screen" : "false" - }, - "protocolMappers" : [ { - "id" : "0164bdc3-c79d-4467-b6bf-ca9a6889d04c", - "name" : "allowed web origins", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-allowed-origins-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "access.token.claim" : "true" - } - } ] - }, { - "id" : "91301d6d-0bb9-4da6-b8db-ee2480e25fee", - "name" : "roles", - "description" : "OpenID Connect scope for add user roles to the access token", - "protocol" : "openid-connect", - "attributes" : { - "include.in.token.scope" : "false", - "consent.screen.text" : "${rolesScopeConsentText}", - "display.on.consent.screen" : "true" - }, - "protocolMappers" : [ { - "id" : "2880d772-b0da-4ee8-bf1e-3f729a945db9", - "name" : "realm roles", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-realm-role-mapper", - "consentRequired" : false, - "config" : { - "user.attribute" : "foo", - "introspection.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "realm_access.roles", - "jsonType.label" : "String", - "multivalued" : "true" - } - }, { - "id" : "535042c5-58c5-4225-94b8-0b5b3411968e", - "name" : "client roles", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-usermodel-client-role-mapper", - "consentRequired" : false, - "config" : { - "user.attribute" : "foo", - "introspection.token.claim" : "true", - "access.token.claim" : "true", - "claim.name" : "resource_access.${client_id}.roles", - "jsonType.label" : "String", - "multivalued" : "true" - } - }, { - "id" : "a88432f1-565f-480d-958d-a5cea1dbcf0a", - "name" : "audience resolve", - "protocol" : "openid-connect", - "protocolMapper" : "oidc-audience-resolve-mapper", - "consentRequired" : false, - "config" : { - "introspection.token.claim" : "true", - "access.token.claim" : "true" - } - } ] - } ], - "defaultDefaultClientScopes" : [ "role_list", "saml_organization", "profile", "email", "roles", "web-origins", "acr", "basic" ], - "defaultOptionalClientScopes" : [ "offline_access", "address", "phone", "microprofile-jwt", "organization" ], - "browserSecurityHeaders" : { - "contentSecurityPolicyReportOnly" : "", - "xContentTypeOptions" : "nosniff", - "referrerPolicy" : "no-referrer", - "xRobotsTag" : "none", - "xFrameOptions" : "SAMEORIGIN", - "contentSecurityPolicy" : "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", - "xXSSProtection" : "1; mode=block", - "strictTransportSecurity" : "max-age=31536000; includeSubDomains" - }, - "smtpServer" : { }, - "eventsEnabled" : false, - "eventsListeners" : [ "jboss-logging" ], - "enabledEventTypes" : [ ], - "adminEventsEnabled" : false, - "adminEventsDetailsEnabled" : false, - "identityProviders" : [ ], - "identityProviderMappers" : [ ], - "components" : { - "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy" : [ { - "id" : "a689e06a-e440-4d94-ba54-692fba5a5486", - "name" : "Max Clients Limit", - "providerId" : "max-clients", - "subType" : "anonymous", - "subComponents" : { }, - "config" : { - "max-clients" : [ "200" ] - } - }, { - "id" : "2778fda5-0a9f-40ab-ab4b-054ff8ce38e9", - "name" : "Allowed Client Scopes", - "providerId" : "allowed-client-templates", - "subType" : "authenticated", - "subComponents" : { }, - "config" : { - "allow-default-scopes" : [ "true" ] - } - }, { - "id" : "36dc0167-9c9a-4b4a-9f04-29129aecac4d", - "name" : "Allowed Protocol Mapper Types", - "providerId" : "allowed-protocol-mappers", - "subType" : "authenticated", - "subComponents" : { }, - "config" : { - "allowed-protocol-mapper-types" : [ "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-property-mapper", "saml-user-attribute-mapper", "oidc-full-name-mapper", "saml-user-property-mapper", "oidc-address-mapper", "saml-role-list-mapper", "oidc-usermodel-attribute-mapper" ] - } - }, { - "id" : "4b79c6fd-5166-4bc2-ab0b-bff0018452f6", - "name" : "Consent Required", - "providerId" : "consent-required", - "subType" : "anonymous", - "subComponents" : { }, - "config" : { } - }, { - "id" : "2003600a-89fb-421e-9dfe-d5096ee7fd4e", - "name" : "Allowed Protocol Mapper Types", - "providerId" : "allowed-protocol-mappers", - "subType" : "anonymous", - "subComponents" : { }, - "config" : { - "allowed-protocol-mapper-types" : [ "oidc-full-name-mapper", "oidc-usermodel-attribute-mapper", "oidc-address-mapper", "oidc-sha256-pairwise-sub-mapper", "saml-role-list-mapper", "saml-user-property-mapper", "saml-user-attribute-mapper", "oidc-usermodel-property-mapper" ] - } - }, { - "id" : "d62a2e93-f877-462a-bad3-93dcf91d49d2", - "name" : "Trusted Hosts", - "providerId" : "trusted-hosts", - "subType" : "anonymous", - "subComponents" : { }, - "config" : { - "host-sending-registration-request-must-match" : [ "true" ], - "client-uris-must-match" : [ "true" ] - } - }, { - "id" : "6e659a80-a638-4504-b507-21b9f77586ed", - "name" : "Full Scope Disabled", - "providerId" : "scope", - "subType" : "anonymous", - "subComponents" : { }, - "config" : { } - }, { - "id" : "9ef67c59-5c3e-40cf-90ee-516b2e35ed3d", - "name" : "Allowed Client Scopes", - "providerId" : "allowed-client-templates", - "subType" : "anonymous", - "subComponents" : { }, - "config" : { - "allow-default-scopes" : [ "true" ] - } - } ], - "org.keycloak.keys.KeyProvider" : [ { - "id" : "b5365a56-e00d-4612-80bf-262a9c8dba7c", - "name" : "rsa-enc-generated", - "providerId" : "rsa-enc-generated", - "subComponents" : { }, - "config" : { - "privateKey" : [ "MIIEogIBAAKCAQEAudnbEbQij+g9JWYMuyJjF/sKe4fVEd9SrCmkFeAHZ7dEAmEKQcAlvJn1aL99pAm8KV0w9PAZugQx7ZzG6eUm4JLc+LYGJ8G8JDiVR6hIWRQ3k9HGcUwNacHKFlDj4XOeMykwLEo7jrQHAUx82vJO8bKZr2ixZqGc1UUUQNbEVYv0HzxhPKPoFYh9qQ8U6P0r/K9xIusLL6ZzmXx6uXqQuqtse05e5G9xwLjQDDrOKju4s1PJ1GZLt5Db9PGypMeA9J34tGL3O0rQom2WbO7R2GZGX044ZoNw6UnraGTmCxin1ywmwTq3JTb1IZ4DPLH/rucnPuKUb60B5ByPmXW/lQIDAQABAoIBAAlyYrGLbb9RX3xNa+O+O3m+U7nUPXcfWikwo6vN+6pgtScGzjnp3bEwxTnqE+WY7hTPLRwiMTiUmoIYuD6u3HNJW8yTmgv+y8SukJ34FpdakPmlTdg39K2VwWMxgOfWk+nHU/DIZC8chQeinu0VKICeIrQ5Ft1f5SQtEvq5v/iWIql+v2ipxdJ2dSl1NIO0/2S2Lyd7Slab50gbJ3kP0uZggN5IMtNd5GBvAbV+jaT4QWKuyXyHqOnyU/+2WU+XhmVPrX6c29sQg2CilqWf4RzEIeO/FgAiANEPyaAgW6mGtf17K1xsSrusyGMUsNGsGJSd7Q8K2o7g/Jv9V8160iECgYEA2xgKIoZ6+fT7UIDr+5insRr6PIht2WYl+JqibjzNRh+rvp1fjKkmjMOA39V6cvLaAHieSOUerSOD7nQONCedpHe6To1zOG9z5yYGgwa/2c/9eNORJq8vNw/4wXaARVf0mCNaexugPTdYvsSaqwW4+azIbB21xXUfKykLp0SjNfUCgYEA2ShJSZyTIqJ64IHi+Kj/E22ajK2DYBiFgNDmzKrW/ANO5ADumgMhahCcxW28Nw68n25vBWCK4o+6eVg1ldEdB1LxoKOYZAaA+zAiMsGI1/ndxdnlFopuJZguKhYDTmxzT0KcD9mApLKZBnCadGjG3FcdC8i14OK6S9lUIIpCvyECgYBXqWC0u7YMuPatGUhSXJwMAs1I1xWMvJBIziZbkTxY6GchV3pZn3xrKfYwmQvrXjvXoGtEo1gI0oMBL7JXL9qlabpDn9kQJZfsToygdFzi25OBerVDEykDEQLo9W8RT8Xv8YVMaJtOowyBF80CzMFcNMPkbmbCYMBd1ohxHsdm2QKBgGB9RhMvPzFkgLTBAdj7Plujl8hqULWiL6/NIsBOKLhRv/wPbfWA7pfySbZvy/Gq2qT8rNf2zb9dnb3NNAIdqIhYkoSOLGhFe4ohGRD0bZmJrMD80I3zdH2/4MNShKWUCqhtMGraeg60TMpPvlF7POEq0/0ocag7FgwdxQOwa3gBAoGASvjvVtfXyzWA9MycWLvlTPEGW5bjXrroYwulF8DkKfIaKWHfEiulTwQe4SHgS7CzWSg8KgvKIhJC/yTwfOtxZ894G9LWivwjjZCottIE+/qs6ioYSXouQr6IsWxs7U0i3gP36tsePjuSjR06kpBGfcFdynypAAq+mVBCV0Mxk9A=" ], - "keyUse" : [ "ENC" ], - "certificate" : [ "MIICnTCCAYUCBgGVU7avyzANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAd0ZWFjaGVyMB4XDTI1MDMwMTIxNTUzNloXDTM1MDMwMTIxNTcxNlowEjEQMA4GA1UEAwwHdGVhY2hlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALnZ2xG0Io/oPSVmDLsiYxf7CnuH1RHfUqwppBXgB2e3RAJhCkHAJbyZ9Wi/faQJvCldMPTwGboEMe2cxunlJuCS3Pi2BifBvCQ4lUeoSFkUN5PRxnFMDWnByhZQ4+FznjMpMCxKO460BwFMfNryTvGyma9osWahnNVFFEDWxFWL9B88YTyj6BWIfakPFOj9K/yvcSLrCy+mc5l8erl6kLqrbHtOXuRvccC40Aw6zio7uLNTydRmS7eQ2/TxsqTHgPSd+LRi9ztK0KJtlmzu0dhmRl9OOGaDcOlJ62hk5gsYp9csJsE6tyU29SGeAzyx/67nJz7ilG+tAeQcj5l1v5UCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAf3HTufrfWb0uqsEwfUETl4S4IbJOjqDdzFhUkMVtiq5I9LLUlJ7StZ6eoDCEoKUzF2lPy0qR2Om7IKC8BA7J5qUio0NSNh9j/t1Ipcjzx6SQI2cD6AjJFZndnF+OBTxdm9c6J+KMho6ZSMQEGwn2osgRBeItauxUshogQJPY/GzWMHlZyCAJcYtuflzgyw1VIQ0OiWCpCiSGeWpojxh19KR9qSBU1rETZMLokmdp84muq8aqEnNIFY5XRyUdH4gjNBx3TGsammZbvzuZdZIDvFNE19SXl/J9QcWJlRw0DuOblLcLKiamcJkQj35T9DgwtYRc/2zM3u8jNwQXKwrUWA==" ], - "priority" : [ "100" ], - "algorithm" : [ "RSA-OAEP" ] - } - }, { - "id" : "ce5dcd75-614d-453a-868c-4413b4a10c39", - "name" : "hmac-generated-hs512", - "providerId" : "hmac-generated", - "subComponents" : { }, - "config" : { - "kid" : [ "a58f2df5-d24b-4aae-9e38-d42736883c7d" ], - "secret" : [ "4sDZ4TC6Cuo0-A5Wa42n_HLCxFj6ir4enL6OmdllOTtR7f5YJN5bsPOJXOFGHeuNPe5jgNq2GfOaeqyQ19PnJMd3Ctsj7vQlx57hywXNvQ1FNuKL1uoxF2Szvw65Y4gIM7xoZpQglVhg2Zh7kA3HJEVhDvnmjNdjtm1QgdlFYws" ], - "priority" : [ "100" ], - "algorithm" : [ "HS512" ] - } - }, { - "id" : "972a70cc-5e9d-4435-8423-f4d32e18d1e7", - "name" : "rsa-generated", - "providerId" : "rsa-generated", - "subComponents" : { }, - "config" : { - "privateKey" : [ "MIIEogIBAAKCAQEAy3EHaNAy6udZMC3A2KIZhMDg0SfGN+FVOKRJfh6aJwtNPuP6EXUhCMRSCXj3EHvFYrBJwKllC8li8MC5rw0vF/P9OY1zdbNkg8Rpwa1D55AS/MlYqiKHazKIV55SXVt5MKtjGATk9D4P/UZ8iP3viBnT0Kws4lWrAv1Uk12CkeLzojeTdCr4I8xgsj5U6dWu2f9DJlsieBhefgJpIXgdWXzVyGC3dOQOrCMHxYdyL4dbBlJPk13QJGkmgH65PnRB0Zu4CtlNHWN1jbXWcRNs8iMLZ4R36F8OzbMcv51lv7+0UTP7HJ4iRjw+sSlUH1AB+3sklyoNgRnW6sisEA+UFQIDAQABAoIBAA9kbWGWQwv31g0poQoi9ZhQOZJJlps6vsZq066ppRMoLT+BYzW37Xhq1iQmVVcXbj9BxErB5kXGhmhdxI7EihgfWzzkAWTZ3lSD41aGg/k8stsSZtV0iFdpetxaO7QZjClNBlHWaPY7zdzlXN3GjL146shChqDXR3mR7ji6HftolGVnmzUXRK+gZG9IirlC+qCJ+sd6m9h83x31X5PRT6yiJ/jeNN4XpuMh61xHFckFOFCGfV2isWM9qL5kLllN1+m8nMjt0HOeEB0GRrHTMSp7QC9RI0z1C/uxdAdSyMhCUtva8jAfjtqYAo7yc63zlOvlkFuYeOQ9X8UmnavBbL8CgYEA7FjyjVr3OK139528/FJMmLk2xOCDQ08pS+ADX5Eib7R62k1ZzXiKnv/8whfdQFeJwIunSYn+y2JCaMFjARrh04SELeH6/CvQ5uCknfIkLeNBij1ye5Ruy7JpaV3oe36h8sJYv1+p5RcrxxINBxbEeKM/YQWRwcXVE54MBCB4dKsCgYEA3FufmasbB4Pna2Txlgo+XCKpf+1U+gcN6lRzsKzqtFVT7+ofUndnqTKgPrLHYytYOFIZIIP8YZBno5gUvK7bFjgAGWacayYNWAXSiEhRQ3ah95Ii/b4lcU095GN/Xu68yqlGQc0GDVD9xRejBNyYgHc2GPQ9bigjsb6pQLy0mj8CgYAN5SjVcKyqM2CjOS3cM8Z3ECSNLJnrAiNuZ4wrOTAqGxVB8lw+PUEBGhG1I4wJdVwO6ub55tgJAwzedcgpT3hJZDgVLn0ACF9uw3RKKOtBm2PGCdjKNS7SYPnbjP7XC9nfmNd44NnvMw6K1J/Zc9g3M3nNbXNlTgk57wfL0lDiowKBgEIF4cf1EGAsEUaINCo0X4LTj92YioFvY6f2LcOdy6TEfCXCDCh1RkXXuVOP1VXNQt19G7I2WYQR9Dt78Zqm+VWq6byyleM0v4LEG9Rhdpe0D8tRqdJFCorsDcNEXIFhHofKOBa3Cz0qKx7Gej2Wqsqy7S6E33MF68vxyFxxLduZAoGAcHhDa8r7EMFTEt3rZIqmblqOYvhfMKJ+Ruom11mUSHWLgyQGzK8mVPhB59J7gt0DKU6XRIwby/7c7x2wFWQ+dsy03PN49PDtewLcGtrsicJlY2mofFZpsFsYhOpyhPg4/zFiX77Ev3UEYiJJ4qXnlV5Yb+ae5D8ZNlmhIP1HQY4=" ], - "keyUse" : [ "SIG" ], - "certificate" : [ "MIICnTCCAYUCBgGVU7avAzANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAd0ZWFjaGVyMB4XDTI1MDMwMTIxNTUzNloXDTM1MDMwMTIxNTcxNlowEjEQMA4GA1UEAwwHdGVhY2hlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMtxB2jQMurnWTAtwNiiGYTA4NEnxjfhVTikSX4emicLTT7j+hF1IQjEUgl49xB7xWKwScCpZQvJYvDAua8NLxfz/TmNc3WzZIPEacGtQ+eQEvzJWKoih2syiFeeUl1beTCrYxgE5PQ+D/1GfIj974gZ09CsLOJVqwL9VJNdgpHi86I3k3Qq+CPMYLI+VOnVrtn/QyZbIngYXn4CaSF4HVl81chgt3TkDqwjB8WHci+HWwZST5Nd0CRpJoB+uT50QdGbuArZTR1jdY211nETbPIjC2eEd+hfDs2zHL+dZb+/tFEz+xyeIkY8PrEpVB9QAft7JJcqDYEZ1urIrBAPlBUCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAHkOqsY1iHqqCMDFvTh/XCNZRwdLdGY6ev+5zShrgb8MPJNgM/HIqQtwQ9wuKf5RfMF1+FQdSU1eavTeuHXp4IuMgv97DAjdZ/pBGHz5tCWMdlaf+Au/1zDoqCV91CbGuV6WHaUhDJLZfp9/phiq2BzPZO6LeWhFJLMzH+N6rPZ7Om72rjTN31TlLLgmLuKlOhMp2QpyaQB16g4ksLGIYq7IXIbCqPRuB33k3gO/+ZMYRpU2U4DQ3FZyIe4LzLXQQ7VSFz/x/rvnbF+hHBdcbszUvsQYCS21aZ6nAq4CGinU2iAOLXHmFotKs+01KZT1N3ZGlGQmHM8ywYyb9qbcfPA==" ], - "priority" : [ "100" ] - } - }, { - "id" : "24e3094f-f962-49bd-b355-ff3096bfefe8", - "name" : "aes-generated", - "providerId" : "aes-generated", - "subComponents" : { }, - "config" : { - "kid" : [ "52ac32c1-f589-4e04-9667-16d2e7bd707a" ], - "secret" : [ "ZEiWoUCZ30PSKa2rx8UXTQ" ], - "priority" : [ "100" ] - } - } ] - }, - "internationalizationEnabled" : false, - "supportedLocales" : [ ], - "authenticationFlows" : [ { - "id" : "2ac7aebb-c1ac-4fdf-9687-cedd34665024", - "alias" : "Account verification options", - "description" : "Method with which to verity the existing account", - "providerId" : "basic-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "idp-email-verification", - "authenticatorFlow" : false, - "requirement" : "ALTERNATIVE", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticatorFlow" : true, - "requirement" : "ALTERNATIVE", - "priority" : 20, - "autheticatorFlow" : true, - "flowAlias" : "Verify Existing Account by Re-authentication", - "userSetupAllowed" : false - } ] - }, { - "id" : "2505f3dc-719b-43a1-9631-585302dd449e", - "alias" : "Browser - Conditional OTP", - "description" : "Flow to determine if the OTP is required for the authentication", - "providerId" : "basic-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "conditional-user-configured", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "auth-otp-form", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 20, - "autheticatorFlow" : false, - "userSetupAllowed" : false - } ] - }, { - "id" : "5a07c120-c34b-4cf2-b38d-2e558af6853a", - "alias" : "Browser - Conditional Organization", - "description" : "Flow to determine if the organization identity-first login is to be used", - "providerId" : "basic-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "conditional-user-configured", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "organization", - "authenticatorFlow" : false, - "requirement" : "ALTERNATIVE", - "priority" : 20, - "autheticatorFlow" : false, - "userSetupAllowed" : false - } ] - }, { - "id" : "a3317f52-b2bc-4b4c-af14-53901d253fca", - "alias" : "Direct Grant - Conditional OTP", - "description" : "Flow to determine if the OTP is required for the authentication", - "providerId" : "basic-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "conditional-user-configured", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "direct-grant-validate-otp", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 20, - "autheticatorFlow" : false, - "userSetupAllowed" : false - } ] - }, { - "id" : "2281818c-fb40-4997-a1ad-fc9ad2c3cacc", - "alias" : "First Broker Login - Conditional Organization", - "description" : "Flow to determine if the authenticator that adds organization members is to be used", - "providerId" : "basic-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "conditional-user-configured", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "idp-add-organization-member", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 20, - "autheticatorFlow" : false, - "userSetupAllowed" : false - } ] - }, { - "id" : "fcab0380-ca38-4f66-aaf2-ec741ef8be8e", - "alias" : "First broker login - Conditional OTP", - "description" : "Flow to determine if the OTP is required for the authentication", - "providerId" : "basic-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "conditional-user-configured", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "auth-otp-form", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 20, - "autheticatorFlow" : false, - "userSetupAllowed" : false - } ] - }, { - "id" : "ae2e214a-82b6-4d78-a7d0-f80d454e5083", - "alias" : "Handle Existing Account", - "description" : "Handle what to do if there is existing account with same email/username like authenticated identity provider", - "providerId" : "basic-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "idp-confirm-link", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticatorFlow" : true, - "requirement" : "REQUIRED", - "priority" : 20, - "autheticatorFlow" : true, - "flowAlias" : "Account verification options", - "userSetupAllowed" : false - } ] - }, { - "id" : "ad2add46-e1bb-47bf-a125-d76c517f66a4", - "alias" : "Organization", - "providerId" : "basic-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticatorFlow" : true, - "requirement" : "CONDITIONAL", - "priority" : 10, - "autheticatorFlow" : true, - "flowAlias" : "Browser - Conditional Organization", - "userSetupAllowed" : false - } ] - }, { - "id" : "74e5d429-4db2-4323-b504-005c03e530fc", - "alias" : "Reset - Conditional OTP", - "description" : "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", - "providerId" : "basic-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "conditional-user-configured", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "reset-otp", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 20, - "autheticatorFlow" : false, - "userSetupAllowed" : false - } ] - }, { - "id" : "d11dbfe7-2472-4cda-a7f5-e9a536154028", - "alias" : "User creation or linking", - "description" : "Flow for the existing/non-existing user alternatives", - "providerId" : "basic-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticatorConfig" : "create unique user config", - "authenticator" : "idp-create-user-if-unique", - "authenticatorFlow" : false, - "requirement" : "ALTERNATIVE", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticatorFlow" : true, - "requirement" : "ALTERNATIVE", - "priority" : 20, - "autheticatorFlow" : true, - "flowAlias" : "Handle Existing Account", - "userSetupAllowed" : false - } ] - }, { - "id" : "f1131dc8-ea34-48e1-9363-438c15f985a4", - "alias" : "Verify Existing Account by Re-authentication", - "description" : "Reauthentication of existing account", - "providerId" : "basic-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "idp-username-password-form", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticatorFlow" : true, - "requirement" : "CONDITIONAL", - "priority" : 20, - "autheticatorFlow" : true, - "flowAlias" : "First broker login - Conditional OTP", - "userSetupAllowed" : false - } ] - }, { - "id" : "f2880986-ef01-4199-ac31-35e0b16c989b", - "alias" : "browser", - "description" : "Browser based authentication", - "providerId" : "basic-flow", - "topLevel" : true, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "auth-cookie", - "authenticatorFlow" : false, - "requirement" : "ALTERNATIVE", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "auth-spnego", - "authenticatorFlow" : false, - "requirement" : "DISABLED", - "priority" : 20, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "identity-provider-redirector", - "authenticatorFlow" : false, - "requirement" : "ALTERNATIVE", - "priority" : 25, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticatorFlow" : true, - "requirement" : "ALTERNATIVE", - "priority" : 26, - "autheticatorFlow" : true, - "flowAlias" : "Organization", - "userSetupAllowed" : false - }, { - "authenticatorFlow" : true, - "requirement" : "ALTERNATIVE", - "priority" : 30, - "autheticatorFlow" : true, - "flowAlias" : "forms", - "userSetupAllowed" : false - } ] - }, { - "id" : "a08dca2e-d491-483f-a310-25bcfa2d89b3", - "alias" : "clients", - "description" : "Base authentication for clients", - "providerId" : "client-flow", - "topLevel" : true, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "client-secret", - "authenticatorFlow" : false, - "requirement" : "ALTERNATIVE", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "client-jwt", - "authenticatorFlow" : false, - "requirement" : "ALTERNATIVE", - "priority" : 20, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "client-secret-jwt", - "authenticatorFlow" : false, - "requirement" : "ALTERNATIVE", - "priority" : 30, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "client-x509", - "authenticatorFlow" : false, - "requirement" : "ALTERNATIVE", - "priority" : 40, - "autheticatorFlow" : false, - "userSetupAllowed" : false - } ] - }, { - "id" : "4742ab83-03c9-417d-ba61-017d9f02afb3", - "alias" : "direct grant", - "description" : "OpenID Connect Resource Owner Grant", - "providerId" : "basic-flow", - "topLevel" : true, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "direct-grant-validate-username", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "direct-grant-validate-password", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 20, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticatorFlow" : true, - "requirement" : "CONDITIONAL", - "priority" : 30, - "autheticatorFlow" : true, - "flowAlias" : "Direct Grant - Conditional OTP", - "userSetupAllowed" : false - } ] - }, { - "id" : "458f78fd-84e5-4e4d-8198-200f25942134", - "alias" : "docker auth", - "description" : "Used by Docker clients to authenticate against the IDP", - "providerId" : "basic-flow", - "topLevel" : true, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "docker-http-basic-authenticator", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - } ] - }, { - "id" : "8cbdd82f-3794-4fce-9494-70279a3d1fcb", - "alias" : "first broker login", - "description" : "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", - "providerId" : "basic-flow", - "topLevel" : true, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticatorConfig" : "review profile config", - "authenticator" : "idp-review-profile", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticatorFlow" : true, - "requirement" : "REQUIRED", - "priority" : 20, - "autheticatorFlow" : true, - "flowAlias" : "User creation or linking", - "userSetupAllowed" : false - }, { - "authenticatorFlow" : true, - "requirement" : "CONDITIONAL", - "priority" : 50, - "autheticatorFlow" : true, - "flowAlias" : "First Broker Login - Conditional Organization", - "userSetupAllowed" : false - } ] - }, { - "id" : "b64919c6-da2b-4e66-bcc6-0112d9e3132b", - "alias" : "forms", - "description" : "Username, password, otp and other auth forms.", - "providerId" : "basic-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "auth-username-password-form", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticatorFlow" : true, - "requirement" : "CONDITIONAL", - "priority" : 20, - "autheticatorFlow" : true, - "flowAlias" : "Browser - Conditional OTP", - "userSetupAllowed" : false - } ] - }, { - "id" : "3c8979fe-c98c-4911-b16c-510dba8fb8e3", - "alias" : "registration", - "description" : "Registration flow", - "providerId" : "basic-flow", - "topLevel" : true, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "registration-page-form", - "authenticatorFlow" : true, - "requirement" : "REQUIRED", - "priority" : 10, - "autheticatorFlow" : true, - "flowAlias" : "registration form", - "userSetupAllowed" : false - } ] - }, { - "id" : "6f598384-bb66-485e-8ed5-7da83c1deba1", - "alias" : "registration form", - "description" : "Registration form", - "providerId" : "form-flow", - "topLevel" : false, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "registration-user-creation", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 20, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "registration-password-action", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 50, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "registration-recaptcha-action", - "authenticatorFlow" : false, - "requirement" : "DISABLED", - "priority" : 60, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "registration-terms-and-conditions", - "authenticatorFlow" : false, - "requirement" : "DISABLED", - "priority" : 70, - "autheticatorFlow" : false, - "userSetupAllowed" : false - } ] - }, { - "id" : "086acb80-23bb-496d-a982-0d8886b2e844", - "alias" : "reset credentials", - "description" : "Reset credentials for a user if they forgot their password or something", - "providerId" : "basic-flow", - "topLevel" : true, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "reset-credentials-choose-user", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "reset-credential-email", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 20, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticator" : "reset-password", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 30, - "autheticatorFlow" : false, - "userSetupAllowed" : false - }, { - "authenticatorFlow" : true, - "requirement" : "CONDITIONAL", - "priority" : 40, - "autheticatorFlow" : true, - "flowAlias" : "Reset - Conditional OTP", - "userSetupAllowed" : false - } ] - }, { - "id" : "2b5042d2-f5e2-456c-bd94-1f23ea0bfb20", - "alias" : "saml ecp", - "description" : "SAML ECP Profile Authentication Flow", - "providerId" : "basic-flow", - "topLevel" : true, - "builtIn" : true, - "authenticationExecutions" : [ { - "authenticator" : "http-basic-authenticator", - "authenticatorFlow" : false, - "requirement" : "REQUIRED", - "priority" : 10, - "autheticatorFlow" : false, - "userSetupAllowed" : false - } ] - } ], - "authenticatorConfig" : [ { - "id" : "3007c3b0-cdd5-4464-93f4-23e439b15253", - "alias" : "create unique user config", - "config" : { - "require.password.update.after.registration" : "false" - } - }, { - "id" : "ce14faa0-34fe-496f-bcb5-a7e72fcf3fbb", - "alias" : "review profile config", - "config" : { - "update.profile.on.first.login" : "missing" - } - } ], - "requiredActions" : [ { - "alias" : "CONFIGURE_TOTP", - "name" : "Configure OTP", - "providerId" : "CONFIGURE_TOTP", - "enabled" : true, - "defaultAction" : false, - "priority" : 10, - "config" : { } - }, { - "alias" : "TERMS_AND_CONDITIONS", - "name" : "Terms and Conditions", - "providerId" : "TERMS_AND_CONDITIONS", - "enabled" : false, - "defaultAction" : false, - "priority" : 20, - "config" : { } - }, { - "alias" : "UPDATE_PASSWORD", - "name" : "Update Password", - "providerId" : "UPDATE_PASSWORD", - "enabled" : true, - "defaultAction" : false, - "priority" : 30, - "config" : { } - }, { - "alias" : "UPDATE_PROFILE", - "name" : "Update Profile", - "providerId" : "UPDATE_PROFILE", - "enabled" : true, - "defaultAction" : false, - "priority" : 40, - "config" : { } - }, { - "alias" : "VERIFY_EMAIL", - "name" : "Verify Email", - "providerId" : "VERIFY_EMAIL", - "enabled" : true, - "defaultAction" : false, - "priority" : 50, - "config" : { } - }, { - "alias" : "delete_account", - "name" : "Delete Account", - "providerId" : "delete_account", - "enabled" : false, - "defaultAction" : false, - "priority" : 60, - "config" : { } - }, { - "alias" : "webauthn-register", - "name" : "Webauthn Register", - "providerId" : "webauthn-register", - "enabled" : true, - "defaultAction" : false, - "priority" : 70, - "config" : { } - }, { - "alias" : "webauthn-register-passwordless", - "name" : "Webauthn Register Passwordless", - "providerId" : "webauthn-register-passwordless", - "enabled" : true, - "defaultAction" : false, - "priority" : 80, - "config" : { } - }, { - "alias" : "VERIFY_PROFILE", - "name" : "Verify Profile", - "providerId" : "VERIFY_PROFILE", - "enabled" : true, - "defaultAction" : false, - "priority" : 90, - "config" : { } - }, { - "alias" : "delete_credential", - "name" : "Delete Credential", - "providerId" : "delete_credential", - "enabled" : true, - "defaultAction" : false, - "priority" : 100, - "config" : { } - }, { - "alias" : "update_user_locale", - "name" : "Update User Locale", - "providerId" : "update_user_locale", - "enabled" : true, - "defaultAction" : false, - "priority" : 1000, - "config" : { } - } ], - "browserFlow" : "browser", - "registrationFlow" : "registration", - "directGrantFlow" : "direct grant", - "resetCredentialsFlow" : "reset credentials", - "clientAuthenticationFlow" : "clients", - "dockerAuthenticationFlow" : "docker auth", - "firstBrokerLoginFlow" : "first broker login", - "attributes" : { - "cibaBackchannelTokenDeliveryMode" : "poll", - "cibaExpiresIn" : "120", - "cibaAuthRequestedUserHint" : "login_hint", - "oauth2DeviceCodeLifespan" : "600", - "oauth2DevicePollingInterval" : "5", - "parRequestUriLifespan" : "60", - "cibaInterval" : "5", - "realmReusableOtpCode" : "false" - }, - "keycloakVersion" : "26.1.3", - "userManagedAccessAllowed" : false, - "organizationsEnabled" : false, - "verifiableCredentialsEnabled" : false, - "adminPermissionsEnabled" : false, - "clientProfiles" : { - "profiles" : [ ] - }, - "clientPolicies" : { - "policies" : [ ] - } } From 8a53baa1255c6ff206b3a4d0ead1d28abbd05478 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Mar 2025 14:57:02 +0000 Subject: [PATCH 50/96] chore(deps): bump axios from 1.8.1 to 1.8.2 Bumps [axios](https://github.com/axios/axios) from 1.8.1 to 1.8.2. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.8.1...v1.8.2) --- updated-dependencies: - dependency-name: axios dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- backend/package.json | 2 +- frontend/package.json | 2 +- package-lock.json | 97 ++++++------------------------------------- 3 files changed, 14 insertions(+), 87 deletions(-) diff --git a/backend/package.json b/backend/package.json index 103f98a5..5aac8381 100644 --- a/backend/package.json +++ b/backend/package.json @@ -19,7 +19,7 @@ "@mikro-orm/reflection": "^6.4.6", "@mikro-orm/sqlite": "6.4.6", "@types/js-yaml": "^4.0.9", - "axios": "^1.8.1", + "axios": "^1.8.2", "dotenv": "^16.4.7", "express": "^5.0.1", "express-jwt": "^8.5.1", diff --git a/frontend/package.json b/frontend/package.json index 8c6f81d3..b056c9f3 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -20,7 +20,7 @@ "vue-router": "^4.5.0", "vuetify": "^3.7.12", "oidc-client-ts": "^3.1.0", - "axios": "^1.8.1" + "axios": "^1.8.2" }, "devDependencies": { "@playwright/test": "^1.50.1", diff --git a/package-lock.json b/package-lock.json index 0efae7fc..e78b2d32 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,16 +34,16 @@ "@mikro-orm/sqlite": "6.4.6", "@types/cors": "^2.8.17", "@types/js-yaml": "^4.0.9", + "axios": "^1.8.2", "cors": "^2.8.5", - "axios": "^1.8.1", "dotenv": "^16.4.7", "express": "^5.0.1", "express-jwt": "^8.5.1", "js-yaml": "^4.1.0", "jwks-rsa": "^3.1.0", - "uuid": "^11.1.0", "loki-logger-ts": "^1.0.2", "response-time": "^2.3.3", + "uuid": "^11.1.0", "winston": "^3.17.0", "winston-loki": "^6.1.3" }, @@ -90,7 +90,7 @@ "name": "dwengo-1-frontend", "version": "0.0.1", "dependencies": { - "axios": "^1.8.1", + "axios": "^1.8.2", "oidc-client-ts": "^3.1.0", "vue": "^3.5.13", "vue-router": "^4.5.0", @@ -964,44 +964,6 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@intlify/core-base": { - "version": "10.0.5", - "license": "MIT", - "dependencies": { - "@intlify/message-compiler": "10.0.5", - "@intlify/shared": "10.0.5" - }, - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/kazupon" - } - }, - "node_modules/@intlify/message-compiler": { - "version": "10.0.5", - "license": "MIT", - "dependencies": { - "@intlify/shared": "10.0.5", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/kazupon" - } - }, - "node_modules/@intlify/shared": { - "version": "10.0.5", - "license": "MIT", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/kazupon" - } - }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "dev": true, @@ -1660,7 +1622,6 @@ }, "node_modules/@types/body-parser": { "version": "1.19.5", - "dev": true, "license": "MIT", "dependencies": { "@types/connect": "*", @@ -1678,7 +1639,6 @@ "version": "2.8.17", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", - "dev": true, "license": "MIT", "dependencies": { "@types/node": "*" @@ -1718,7 +1678,6 @@ }, "node_modules/@types/http-errors": { "version": "2.0.4", - "dev": true, "license": "MIT" }, "node_modules/@types/js-yaml": { @@ -1758,7 +1717,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "dev": true, "license": "MIT" }, "node_modules/@types/node": { @@ -1770,12 +1728,10 @@ }, "node_modules/@types/qs": { "version": "6.9.18", - "dev": true, "license": "MIT" }, "node_modules/@types/range-parser": { "version": "1.2.7", - "dev": true, "license": "MIT" }, "node_modules/@types/response-time": { @@ -1791,7 +1747,6 @@ }, "node_modules/@types/send": { "version": "0.17.4", - "dev": true, "license": "MIT", "dependencies": { "@types/mime": "^1", @@ -1800,7 +1755,6 @@ }, "node_modules/@types/serve-static": { "version": "1.15.7", - "dev": true, "license": "MIT", "dependencies": { "@types/http-errors": "*", @@ -2622,9 +2576,9 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.1.tgz", - "integrity": "sha512-NN+fvwH/kV01dYUQ3PTOZns4LWtWhOFCAhQ/pHb88WQ1hNe5V/dvFwc4VJcDL11LT9xSX0QtsR8sWUuyOuOq7g==", + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.2.tgz", + "integrity": "sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -3123,7 +3077,6 @@ }, "node_modules/color-name": { "version": "1.1.4", - "dev": true, "license": "MIT" }, "node_modules/color-string": { @@ -5576,6 +5529,12 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, "node_modules/logform": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", @@ -5607,12 +5566,6 @@ "integrity": "sha512-ka87Jz3gcx/I7Hal94xaN2tZEOPoUOEVftkQqZx2EeQRN7LGdfLlI3FvZ+7WDplm+vK2Urx9ULrvSowtdCieng==", "license": "Apache-2.0" }, - "node_modules/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", - "license": "MIT" - }, "node_modules/loupe": { "version": "3.1.3", "dev": true, @@ -7909,14 +7862,6 @@ "node": ">=8" } }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/strip-bom": { "version": "3.0.0", "dev": true, @@ -8855,24 +8800,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/vue-i18n": { - "version": "10.0.5", - "license": "MIT", - "dependencies": { - "@intlify/core-base": "10.0.5", - "@intlify/shared": "10.0.5", - "@vue/devtools-api": "^6.5.0" - }, - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/kazupon" - }, - "peerDependencies": { - "vue": "^3.0.0" - } - }, "node_modules/vue-router": { "version": "4.5.0", "license": "MIT", From 7018a8822d286833e79850facc7780644c83e5d2 Mon Sep 17 00:00:00 2001 From: Gerald Schmittinger Date: Mon, 10 Mar 2025 21:14:40 +0100 Subject: [PATCH 51/96] test(backend): Testen voor DatabaseLearningPathProvider.fetchLearningPaths afgewerkt Hierbij optredende problemen opgelost. --- .../content/learning-object-repository.ts | 33 +++++++---- .../data/content/learning-path-repository.ts | 9 ++- backend/src/data/repositories.ts | 4 ++ .../content/learning-path-node.entity.ts | 3 +- .../learning-path-transition.entity.ts | 2 +- backend/src/interfaces/learning-content.ts | 4 +- .../database-learning-path-provider.test.ts | 31 +++++++++- .../learning-paths/learning-path-utils.ts | 9 ++- .../learning-paths/pn-werking-example.ts | 17 +++--- backend/tests/test-utils/expectations.ts | 59 ++++++++++++++++++- 10 files changed, 139 insertions(+), 32 deletions(-) diff --git a/backend/src/data/content/learning-object-repository.ts b/backend/src/data/content/learning-object-repository.ts index d69ae075..8fdf18fe 100644 --- a/backend/src/data/content/learning-object-repository.ts +++ b/backend/src/data/content/learning-object-repository.ts @@ -7,21 +7,30 @@ export class LearningObjectRepository extends DwengoEntityRepository { - return this.findOne({ - hruid: identifier.hruid, - language: identifier.language, - version: identifier.version, - }); + return this.findOne( + { + hruid: identifier.hruid, + language: identifier.language, + version: identifier.version, + }, + { + populate: ["keywords"] + } + ); } public findLatestByHruidAndLanguage(hruid: string, language: Language) { - return this.findOne({ - hruid: hruid, - language: language - }, { - orderBy: { - version: "DESC" + return this.findOne( + { + hruid: hruid, + language: language + }, + { + populate: ["keywords"], + orderBy: { + version: "DESC" + } } - }); + ); } } diff --git a/backend/src/data/content/learning-path-repository.ts b/backend/src/data/content/learning-path-repository.ts index 66ef54d0..347bc023 100644 --- a/backend/src/data/content/learning-path-repository.ts +++ b/backend/src/data/content/learning-path-repository.ts @@ -7,7 +7,10 @@ export class LearningPathRepository extends DwengoEntityRepository hruid: string, language: Language ): Promise { - return this.findOne({ hruid: hruid, language: language }); + return this.findOne( + { hruid: hruid, language: language }, + { populate: ["nodes", "nodes.transitions"] } + ); } /** @@ -25,8 +28,8 @@ export class LearningPathRepository extends DwengoEntityRepository { title: { $like: `%${query}%`} }, { description: { $like: `%${query}%`} } ] - } + }, + populate: ["nodes", "nodes.transitions"] }); } - // This repository is read-only for now since creating own learning object is an extension feature. } diff --git a/backend/src/data/repositories.ts b/backend/src/data/repositories.ts index 800d5485..6218bafc 100644 --- a/backend/src/data/repositories.ts +++ b/backend/src/data/repositories.ts @@ -33,6 +33,8 @@ import { LearningPath } from '../entities/content/learning-path.entity.js'; import { LearningPathRepository } from './content/learning-path-repository.js'; import { AttachmentRepository } from './content/attachment-repository.js'; import { Attachment } from '../entities/content/attachment.entity.js'; +import {LearningPathNode} from "../entities/content/learning-path-node.entity"; +import {LearningPathTransition} from "../entities/content/learning-path-transition.entity"; let entityManager: EntityManager | undefined; @@ -113,6 +115,8 @@ export const getLearningPathRepository = repositoryGetter< LearningPath, LearningPathRepository >(LearningPath); +export const getLearningPathNodeRepository = repositoryGetter(LearningPathNode); +export const getLearningPathTransitionRepository = repositoryGetter(LearningPathTransition); export const getAttachmentRepository = repositoryGetter< Attachment, AttachmentRepository diff --git a/backend/src/entities/content/learning-path-node.entity.ts b/backend/src/entities/content/learning-path-node.entity.ts index e15b033a..e2fbdbb3 100644 --- a/backend/src/entities/content/learning-path-node.entity.ts +++ b/backend/src/entities/content/learning-path-node.entity.ts @@ -5,10 +5,11 @@ import {LearningPathTransition} from "./learning-path-transition.entity"; @Entity() export class LearningPathNode { + @ManyToOne({ entity: () => LearningPath, primary: true }) learningPath!: LearningPath; - @PrimaryKey({ type: "numeric", autoincrement: true }) + @PrimaryKey({ type: "integer", autoincrement: true }) nodeNumber!: number; @Property({ type: 'string' }) diff --git a/backend/src/entities/content/learning-path-transition.entity.ts b/backend/src/entities/content/learning-path-transition.entity.ts index 6122b758..dfbe110e 100644 --- a/backend/src/entities/content/learning-path-transition.entity.ts +++ b/backend/src/entities/content/learning-path-transition.entity.ts @@ -3,7 +3,7 @@ import {LearningPathNode} from "./learning-path-node.entity"; @Entity() export class LearningPathTransition { - @ManyToOne({entity: () => LearningPathNode }) + @ManyToOne({entity: () => LearningPathNode, primary: true }) node!: LearningPathNode; @PrimaryKey({ type: 'numeric' }) diff --git a/backend/src/interfaces/learning-content.ts b/backend/src/interfaces/learning-content.ts index 861996f5..89a1340c 100644 --- a/backend/src/interfaces/learning-content.ts +++ b/backend/src/interfaces/learning-content.ts @@ -21,7 +21,7 @@ export interface LearningObjectNode { _id: string; learningobject_hruid: string; version: number; - language: string; + language: Language; start_node?: boolean; transitions: Transition[]; created_at: string; @@ -88,7 +88,7 @@ export interface FilteredLearningObject { version: number; title: string; htmlUrl: string; - language: string; + language: Language; difficulty: number; estimatedTime: number; available: boolean; diff --git a/backend/tests/services/learning-objects/database-learning-path-provider.test.ts b/backend/tests/services/learning-objects/database-learning-path-provider.test.ts index dab40a69..321fe213 100644 --- a/backend/tests/services/learning-objects/database-learning-path-provider.test.ts +++ b/backend/tests/services/learning-objects/database-learning-path-provider.test.ts @@ -6,6 +6,10 @@ import {getLearningObjectRepository, getLearningPathRepository} from "../../../s import learningObjectExample from "../../test-assets/learning-objects/pn_werkingnotebooks/pn-werkingnotebooks-example"; import learningPathExample from "../../test-assets/learning-paths/pn-werking-example" import databaseLearningPathProvider from "../../../src/services/learning-paths/database-learning-path-provider"; +import {expectToBeCorrectLearningPath} from "../../test-utils/expectations"; +import {LearningObjectRepository} from "../../../src/data/content/learning-object-repository"; +import learningObjectService from "../../../src/services/learning-objects/learning-object-service"; +import {Language} from "../../../src/entities/content/language"; async function initExampleData(): Promise<{ learningObject: LearningObject, learningPath: LearningPath }> { const learningObjectRepo = getLearningObjectRepository(); @@ -18,15 +22,17 @@ async function initExampleData(): Promise<{ learningObject: LearningObject, lear } describe("DatabaseLearningPathProvider", () => { + let learningObjectRepo: LearningObjectRepository; let example: {learningObject: LearningObject, learningPath: LearningPath}; beforeAll(async () => { await setupTestApp(); example = await initExampleData(); + learningObjectRepo = getLearningObjectRepository(); }); describe("fetchLearningPaths", () => { - it("returns the learning path correctly", () => { + it("returns the learning path correctly", async () => { const result = await databaseLearningPathProvider.fetchLearningPaths( [example.learningPath.hruid], example.learningPath.language, @@ -34,7 +40,26 @@ describe("DatabaseLearningPathProvider", () => { ); expect(result.success).toBe(true); expect(result.data?.length).toBe(1); - expect(result.data) - }) + + const learningObjectsOnPath = (await Promise.all( + example.learningPath.nodes.map(node => + learningObjectService.getLearningObjectById({ + hruid: node.learningObjectHruid, + version: node.version, + language: node.language + })) + )).filter(it => it !== null); + + expectToBeCorrectLearningPath(result.data![0], example.learningPath, learningObjectsOnPath) + }); + it("returns a non-successful response if a non-existing learning path is queried", async () => { + const result = await databaseLearningPathProvider.fetchLearningPaths( + [example.learningPath.hruid], + Language.Abkhazian, // wrong language + "the source" + ); + + expect(result.success).toBe(false); + }); }); }); diff --git a/backend/tests/test-assets/learning-paths/learning-path-utils.ts b/backend/tests/test-assets/learning-paths/learning-path-utils.ts index 31374869..68c49412 100644 --- a/backend/tests/test-assets/learning-paths/learning-path-utils.ts +++ b/backend/tests/test-assets/learning-paths/learning-path-utils.ts @@ -1,21 +1,28 @@ import {Language} from "../../../src/entities/content/language"; import {LearningPathTransition} from "../../../src/entities/content/learning-path-transition.entity"; import {LearningPathNode} from "../../../src/entities/content/learning-path-node.entity"; +import {LearningPath} from "../../../src/entities/content/learning-path.entity"; -export function createLearningPathTransition(condition: string | null, to: LearningPathNode) { +export function createLearningPathTransition(node: LearningPathNode, transitionNumber: number, condition: string | null, to: LearningPathNode) { let trans = new LearningPathTransition(); + trans.node = node; + trans.transitionNumber = transitionNumber; trans.condition = condition || "true"; trans.next = to; return trans; } export function createLearningPathNode( + learningPath: LearningPath, + nodeNumber: number, learningObjectHruid: string, version: number, language: Language, startNode: boolean ) { let node = new LearningPathNode(); + node.learningPath = learningPath; + node.nodeNumber = nodeNumber; node.learningObjectHruid = learningObjectHruid; node.version = version; node.language = language; diff --git a/backend/tests/test-assets/learning-paths/pn-werking-example.ts b/backend/tests/test-assets/learning-paths/pn-werking-example.ts index dbf86052..a96de552 100644 --- a/backend/tests/test-assets/learning-paths/pn-werking-example.ts +++ b/backend/tests/test-assets/learning-paths/pn-werking-example.ts @@ -1,16 +1,17 @@ -import {LearningPath, LearningPathNode} from "../../../src/entities/content/learning-path.entity"; +import {LearningPath} from "../../../src/entities/content/learning-path.entity"; import {Language} from "../../../src/entities/content/language"; import {EnvVars, getEnvVar} from "../../../src/util/envvars"; import {createLearningPathNode, createLearningPathTransition} from "./learning-path-utils"; +import {LearningPathNode} from "../../../src/entities/content/learning-path-node.entity"; -function createNodes(): LearningPathNode[] { +function createNodes(learningPath: LearningPath): LearningPathNode[] { let nodes = [ - createLearningPathNode("u_pn_werkingnotebooks", 3, Language.Dutch, true), - createLearningPathNode("pn_werkingnotebooks2", 3, Language.Dutch, false), - createLearningPathNode("pn_werkingnotebooks3", 3, Language.Dutch, false), + createLearningPathNode(learningPath, 0, "u_pn_werkingnotebooks", 3, Language.Dutch, true), + createLearningPathNode(learningPath, 1, "pn_werkingnotebooks2", 3, Language.Dutch, false), + createLearningPathNode(learningPath, 2, "pn_werkingnotebooks3", 3, Language.Dutch, false), ]; - nodes[0].transitions.push(createLearningPathTransition("true", nodes[1])); - nodes[1].transitions.push(createLearningPathTransition("true", nodes[2])); + nodes[0].transitions.push(createLearningPathTransition(nodes[0], 0, "true", nodes[1])); + nodes[1].transitions.push(createLearningPathTransition(nodes[1], 0, "true", nodes[2])); return nodes; } @@ -21,7 +22,7 @@ const example: LearningPathExample = { path.hruid = `${getEnvVar(EnvVars.UserContentPrefix)}pn_werking`; path.title = "Werken met notebooks"; path.description = "Een korte inleiding tot Python notebooks. Hoe ga je gemakkelijk en efficiënt met de notebooks aan de slag?"; - path.nodes = createNodes(); + path.nodes = createNodes(path); return path; } } diff --git a/backend/tests/test-utils/expectations.ts b/backend/tests/test-utils/expectations.ts index 285c001a..19c2408d 100644 --- a/backend/tests/test-utils/expectations.ts +++ b/backend/tests/test-utils/expectations.ts @@ -99,9 +99,66 @@ export function expectToBeCorrectFilteredLearningObject(filtered: FilteredLearni * * @param learningPath The learning path returned by the retriever, service or endpoint * @param expectedEntity The expected entity + * @param learningObjectsOnPath The learning objects on LearningPath. Necessary since some information in + * the learning path returned from the API endpoint */ -export function expectToBeCorrectLearningPath(learningPath: LearningPath, expectedEntity: LearningPathEntity) { +export function expectToBeCorrectLearningPath( + learningPath: LearningPath, + expectedEntity: LearningPathEntity, + learningObjectsOnPath: FilteredLearningObject[] +) { expect(learningPath.hruid).toEqual(expectedEntity.hruid); expect(learningPath.language).toEqual(expectedEntity.language); expect(learningPath.description).toEqual(expectedEntity.description); + expect(learningPath.title).toEqual(expectedEntity.title); + + const keywords = new Set(learningObjectsOnPath.flatMap(it => it.keywords || [])); + expect(new Set(learningPath.keywords.split(' '))).toEqual(keywords) + + const targetAges = new Set(learningObjectsOnPath.flatMap(it => it.targetAges || [])); + expect(new Set(learningPath.target_ages)).toEqual(targetAges); + expect(learningPath.min_age).toEqual(Math.min(...targetAges)); + expect(learningPath.max_age).toEqual(Math.max(...targetAges)); + + expect(learningPath.num_nodes).toEqual(expectedEntity.nodes.length); + expect(learningPath.image || null).toEqual(expectedEntity.image); + + let expectedLearningPathNodes = new Map( + expectedEntity.nodes.map(node => [ + {learningObjectHruid: node.learningObjectHruid, language: node.language, version: node.version}, + {startNode: node.startNode, transitions: node.transitions} + ]) + ); + + for (let node of learningPath.nodes) { + const nodeKey = { + learningObjectHruid: node.learningobject_hruid, + language: node.language, + version: node.version + }; + expect(expectedLearningPathNodes.keys()).toContainEqual(nodeKey); + let expectedNode = [...expectedLearningPathNodes.entries()] + .filter(([key, _]) => + key.learningObjectHruid === nodeKey.learningObjectHruid + && key.language === node.language + && key.version === node.version + )[0][1] + expect(node.start_node).toEqual(expectedNode?.startNode); + + expect( + new Set(node.transitions.map(it => it.next.hruid)) + ).toEqual( + new Set(expectedNode.transitions.map(it => it.next.learningObjectHruid)) + ); + expect( + new Set(node.transitions.map(it => it.next.language)) + ).toEqual( + new Set(expectedNode.transitions.map(it => it.next.language)) + ); + expect( + new Set(node.transitions.map(it => it.next.version)) + ).toEqual( + new Set(expectedNode.transitions.map(it => it.next.version)) + ); + } } From 8152306a9239db579dcbf6cb30b0ba4f7dbb9236 Mon Sep 17 00:00:00 2001 From: Gerald Schmittinger Date: Mon, 10 Mar 2025 21:59:12 +0100 Subject: [PATCH 52/96] test(backend): Testen voor DatabaseLearningPathProvider.searchLearningPath afgewerkt --- .../database-learning-path-provider.test.ts | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) rename backend/tests/services/{learning-objects => learning-path}/database-learning-path-provider.test.ts (67%) diff --git a/backend/tests/services/learning-objects/database-learning-path-provider.test.ts b/backend/tests/services/learning-path/database-learning-path-provider.test.ts similarity index 67% rename from backend/tests/services/learning-objects/database-learning-path-provider.test.ts rename to backend/tests/services/learning-path/database-learning-path-provider.test.ts index 321fe213..5d612f04 100644 --- a/backend/tests/services/learning-objects/database-learning-path-provider.test.ts +++ b/backend/tests/services/learning-path/database-learning-path-provider.test.ts @@ -62,4 +62,32 @@ describe("DatabaseLearningPathProvider", () => { expect(result.success).toBe(false); }); }); + + describe("searchLearningPaths", () => { + it("returns the correct learning path when queried with a substring of its title", async () => { + const result = await databaseLearningPathProvider.searchLearningPaths( + example.learningPath.title.substring(2, 6), + example.learningPath.language + ); + expect(result.length).toBe(1); + expect(result[0].title).toBe(example.learningPath.title); + expect(result[0].description).toBe(example.learningPath.description); + }); + it("returns the correct learning path when queried with a substring of the description", async () => { + const result = await databaseLearningPathProvider.searchLearningPaths( + example.learningPath.description.substring(5, 12), + example.learningPath.language + ); + expect(result.length).toBe(1); + expect(result[0].title).toBe(example.learningPath.title); + expect(result[0].description).toBe(example.learningPath.description); + }); + it("returns an empty result when queried with a text which is not a substring of the title or the description of a learning path", async () => { + const result = await databaseLearningPathProvider.searchLearningPaths( + "substring which does not occur in the title or the description of a learning object", + example.learningPath.language + ); + expect(result.length).toBe(0); + }); + }); }); From cb6056e9d7487ccc5ab6655e4416095c4ebc35eb Mon Sep 17 00:00:00 2001 From: Gerald Schmittinger Date: Mon, 10 Mar 2025 22:30:54 +0100 Subject: [PATCH 53/96] test(backend): Testen voor LearningObjectService toegevoegd --- backend/src/interfaces/learning-content.ts | 2 +- .../dwengo-api-learning-object-provider.ts | 2 +- .../learning-object-service.test.ts | 80 +++++++++++++++++++ 3 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 backend/tests/services/learning-objects/learning-object-service.test.ts diff --git a/backend/src/interfaces/learning-content.ts b/backend/src/interfaces/learning-content.ts index 89a1340c..5d11d6b2 100644 --- a/backend/src/interfaces/learning-content.ts +++ b/backend/src/interfaces/learning-content.ts @@ -65,7 +65,7 @@ export interface LearningObjectMetadata { uuid: string; hruid: string; version: number; - language: string; + language: Language; title: string; description: string; difficulty: number; diff --git a/backend/src/services/learning-objects/dwengo-api-learning-object-provider.ts b/backend/src/services/learning-objects/dwengo-api-learning-object-provider.ts index bef08dfe..628f616c 100644 --- a/backend/src/services/learning-objects/dwengo-api-learning-object-provider.ts +++ b/backend/src/services/learning-objects/dwengo-api-learning-object-provider.ts @@ -107,7 +107,7 @@ const dwengoApiLearningObjectProvider: LearningObjectProvider = { } ); - if (!metadata) { + if (!metadata || typeof metadata !== "object") { console.error(`⚠️ WARNING: Learning object "${id.hruid}" not found.`); return null; } diff --git a/backend/tests/services/learning-objects/learning-object-service.test.ts b/backend/tests/services/learning-objects/learning-object-service.test.ts new file mode 100644 index 00000000..2c2efd1a --- /dev/null +++ b/backend/tests/services/learning-objects/learning-object-service.test.ts @@ -0,0 +1,80 @@ +import {beforeAll, describe, expect, it} from "vitest"; +import {setupTestApp} from "../../setup-tests"; +import {LearningObject} from "../../../src/entities/content/learning-object.entity"; +import {getLearningObjectRepository} from "../../../src/data/repositories"; +import learningObjectExample from "../../test-assets/learning-objects/pn_werkingnotebooks/pn-werkingnotebooks-example"; +import learningObjectService from "../../../src/services/learning-objects/learning-object-service"; +import {LearningObjectIdentifier} from "../../../src/interfaces/learning-content"; +import {Language} from "../../../src/entities/content/language"; +import {EnvVars, getEnvVar} from "../../../src/util/envvars"; + +const TEST_LEARNING_OBJECT_TITLE = "Test title"; +const EXPECTED_DWENGO_LEARNING_OBJECT_TITLE = "Werken met notebooks"; +const DWENGO_TEST_LEARNING_OBJECT_ID: LearningObjectIdentifier = { + hruid: "pn_werkingnotebooks", + language: Language.Dutch, + version: 3 +}; + +async function initExampleData(): Promise { + const learningObjectRepo = getLearningObjectRepository(); + let learningObject = learningObjectExample.createLearningObject(); + learningObject.title = TEST_LEARNING_OBJECT_TITLE + await learningObjectRepo.save(learningObject); + return learningObject; +} + +describe("LearningObjectService", () => { + let exampleLearningObject: LearningObject; + + beforeAll(async () => { + await setupTestApp(); + exampleLearningObject = await initExampleData(); + }); + + describe("getLearningObjectById", () => { + it("returns the learning object from the Dwengo API if it does not have the user content prefix", async () => { + const result = await learningObjectService.getLearningObjectById(DWENGO_TEST_LEARNING_OBJECT_ID); + expect(result).not.toBeNull(); + expect(result?.title).toBe(EXPECTED_DWENGO_LEARNING_OBJECT_TITLE); + }); + it("returns the learning object from the database if it does have the user content prefix", async () => { + const result = await learningObjectService.getLearningObjectById(exampleLearningObject); + expect(result).not.toBeNull(); + expect(result?.title).toBe(exampleLearningObject.title); + }); + it("returns null if the hruid does not have the user content prefix and does not exist in the Dwengo repo", async () => { + const result = await learningObjectService.getLearningObjectById({ + hruid: "non-existing", + language: Language.Dutch + }); + expect(result).toBeNull(); + }); + }); + + describe("getLearningObjectHTML", () => { + it("returns the expected HTML when queried with the identifier of a learning object saved in the database", async () => { + const result = await learningObjectService.getLearningObjectHTML(exampleLearningObject); + expect(result).not.toBeNull(); + expect(result).toEqual(learningObjectExample.getHTMLRendering()); + }); + it("returns the same HTML as the Dwengo API when queried with the identifier of a learning object that does " + + "not start with the user content prefix", async () => { + const result = await learningObjectService.getLearningObjectHTML(DWENGO_TEST_LEARNING_OBJECT_ID); + expect(result).not.toBeNull(); + + const htmlFromDwengoApi = await fetch( + getEnvVar(EnvVars.LearningContentRepoApiBaseUrl) + + `/learningObject/getRaw?hruid=${DWENGO_TEST_LEARNING_OBJECT_ID.hruid}&language=${DWENGO_TEST_LEARNING_OBJECT_ID.language}&version=${DWENGO_TEST_LEARNING_OBJECT_ID.version}` + ).then(it => it.text()); + expect(result).toEqual(htmlFromDwengoApi); + }); + it("returns null when queried with a non-existing identifier", async () => { + const result = await learningObjectService.getLearningObjectHTML({ + hruid: "non_existing_hruid", + language: Language.Dutch + }); + expect(result).toBeNull(); + }); + }); +}); From 392510db8290482358bb18a51c289ad36a6dbb9b Mon Sep 17 00:00:00 2001 From: Gerald Schmittinger Date: Mon, 10 Mar 2025 23:32:22 +0100 Subject: [PATCH 54/96] test(backend): Testen voor LearningPathService toegevoegd --- .../learning-paths/learning-path-service.ts | 2 +- .../content/attachment-repository.test.ts | 2 +- .../learning-object-repository.test.ts | 2 +- .../database-learning-object-provider.test.ts | 2 +- .../learning-object-service.test.ts | 4 +- .../database-learning-path-provider.test.ts | 2 +- .../learning-path-service.test.ts | 106 ++++++++++++++++++ .../Knop.png | Bin .../content.md | 0 .../dwengo.png | Bin .../pn-werkingnotebooks-example.ts | 2 +- .../rendering.html | 0 12 files changed, 114 insertions(+), 8 deletions(-) create mode 100644 backend/tests/services/learning-path/learning-path-service.test.ts rename backend/tests/test-assets/learning-objects/{pn_werkingnotebooks => pn-werkingnotebooks}/Knop.png (100%) rename backend/tests/test-assets/learning-objects/{pn_werkingnotebooks => pn-werkingnotebooks}/content.md (100%) rename backend/tests/test-assets/learning-objects/{pn_werkingnotebooks => pn-werkingnotebooks}/dwengo.png (100%) rename backend/tests/test-assets/learning-objects/{pn_werkingnotebooks => pn-werkingnotebooks}/pn-werkingnotebooks-example.ts (98%) rename backend/tests/test-assets/learning-objects/{pn_werkingnotebooks => pn-werkingnotebooks}/rendering.html (100%) diff --git a/backend/src/services/learning-paths/learning-path-service.ts b/backend/src/services/learning-paths/learning-path-service.ts index 601790c9..a00b4633 100644 --- a/backend/src/services/learning-paths/learning-path-service.ts +++ b/backend/src/services/learning-paths/learning-path-service.ts @@ -24,7 +24,7 @@ const learningPathService = { const userContentLearningPaths = await databaseLearningPathProvider.fetchLearningPaths(userContentHruids, language, source); const nonUserContentLearningPaths - = await databaseLearningPathProvider.fetchLearningPaths(nonUserContentHruids, language, source); + = await dwengoApiLearningPathProvider.fetchLearningPaths(nonUserContentHruids, language, source); return { data: (userContentLearningPaths.data || []).concat(nonUserContentLearningPaths.data || []), diff --git a/backend/tests/data/content/attachment-repository.test.ts b/backend/tests/data/content/attachment-repository.test.ts index 02727460..3f573cdb 100644 --- a/backend/tests/data/content/attachment-repository.test.ts +++ b/backend/tests/data/content/attachment-repository.test.ts @@ -3,7 +3,7 @@ import {setupTestApp} from "../../setup-tests"; import {getAttachmentRepository, getLearningObjectRepository} from "../../../src/data/repositories"; import {AttachmentRepository} from "../../../src/data/content/attachment-repository"; import {LearningObjectRepository} from "../../../src/data/content/learning-object-repository"; -import example from "../../test-assets/learning-objects/pn_werkingnotebooks/pn-werkingnotebooks-example"; +import example from "../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example"; import {LearningObject} from "../../../src/entities/content/learning-object.entity"; import {Attachment} from "../../../src/entities/content/attachment.entity"; import {LearningObjectIdentifier} from "../../../src/entities/content/learning-object-identifier"; diff --git a/backend/tests/data/content/learning-object-repository.test.ts b/backend/tests/data/content/learning-object-repository.test.ts index 87b8511e..bcca4c83 100644 --- a/backend/tests/data/content/learning-object-repository.test.ts +++ b/backend/tests/data/content/learning-object-repository.test.ts @@ -2,7 +2,7 @@ import {beforeAll, describe, it, expect} from "vitest"; import {LearningObjectRepository} from "../../../src/data/content/learning-object-repository"; import {setupTestApp} from "../../setup-tests"; import {getLearningObjectRepository} from "../../../src/data/repositories"; -import example from "../../test-assets/learning-objects/pn_werkingnotebooks/pn-werkingnotebooks-example.js" +import example from "../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example.js" import {LearningObject} from "../../../src/entities/content/learning-object.entity"; import {expectToBeCorrectEntity} from "../../test-utils/expectations"; diff --git a/backend/tests/services/learning-objects/database-learning-object-provider.test.ts b/backend/tests/services/learning-objects/database-learning-object-provider.test.ts index c8783246..691b29ba 100644 --- a/backend/tests/services/learning-objects/database-learning-object-provider.test.ts +++ b/backend/tests/services/learning-objects/database-learning-object-provider.test.ts @@ -1,7 +1,7 @@ import {beforeAll, describe, expect, it} from "vitest"; import {setupTestApp} from "../../setup-tests"; import {getLearningObjectRepository} from "../../../src/data/repositories"; -import example from "../../test-assets/learning-objects/pn_werkingnotebooks/pn-werkingnotebooks-example"; +import example from "../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example"; import {LearningObject} from "../../../src/entities/content/learning-object.entity"; import databaseLearningObjectProvider from "../../../src/services/learning-objects/database-learning-object-provider"; import { diff --git a/backend/tests/services/learning-objects/learning-object-service.test.ts b/backend/tests/services/learning-objects/learning-object-service.test.ts index 2c2efd1a..2fac3d3e 100644 --- a/backend/tests/services/learning-objects/learning-object-service.test.ts +++ b/backend/tests/services/learning-objects/learning-object-service.test.ts @@ -2,7 +2,7 @@ import {beforeAll, describe, expect, it} from "vitest"; import {setupTestApp} from "../../setup-tests"; import {LearningObject} from "../../../src/entities/content/learning-object.entity"; import {getLearningObjectRepository} from "../../../src/data/repositories"; -import learningObjectExample from "../../test-assets/learning-objects/pn_werkingnotebooks/pn-werkingnotebooks-example"; +import learningObjectExample from "../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example"; import learningObjectService from "../../../src/services/learning-objects/learning-object-service"; import {LearningObjectIdentifier} from "../../../src/interfaces/learning-content"; import {Language} from "../../../src/entities/content/language"; @@ -11,7 +11,7 @@ import {EnvVars, getEnvVar} from "../../../src/util/envvars"; const TEST_LEARNING_OBJECT_TITLE = "Test title"; const EXPECTED_DWENGO_LEARNING_OBJECT_TITLE = "Werken met notebooks"; const DWENGO_TEST_LEARNING_OBJECT_ID: LearningObjectIdentifier = { - hruid: "pn_werkingnotebooks", + hruid: "pn-werkingnotebooks", language: Language.Dutch, version: 3 }; diff --git a/backend/tests/services/learning-path/database-learning-path-provider.test.ts b/backend/tests/services/learning-path/database-learning-path-provider.test.ts index 5d612f04..0d46c1e0 100644 --- a/backend/tests/services/learning-path/database-learning-path-provider.test.ts +++ b/backend/tests/services/learning-path/database-learning-path-provider.test.ts @@ -3,7 +3,7 @@ import {LearningObject} from "../../../src/entities/content/learning-object.enti import {setupTestApp} from "../../setup-tests"; import {LearningPath} from "../../../src/entities/content/learning-path.entity"; import {getLearningObjectRepository, getLearningPathRepository} from "../../../src/data/repositories"; -import learningObjectExample from "../../test-assets/learning-objects/pn_werkingnotebooks/pn-werkingnotebooks-example"; +import learningObjectExample from "../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example"; import learningPathExample from "../../test-assets/learning-paths/pn-werking-example" import databaseLearningPathProvider from "../../../src/services/learning-paths/database-learning-path-provider"; import {expectToBeCorrectLearningPath} from "../../test-utils/expectations"; diff --git a/backend/tests/services/learning-path/learning-path-service.test.ts b/backend/tests/services/learning-path/learning-path-service.test.ts new file mode 100644 index 00000000..83db1bef --- /dev/null +++ b/backend/tests/services/learning-path/learning-path-service.test.ts @@ -0,0 +1,106 @@ +import {beforeAll, describe, expect, it} from "vitest"; +import {setupTestApp} from "../../setup-tests"; +import {LearningObject} from "../../../src/entities/content/learning-object.entity"; +import {LearningPath} from "../../../src/entities/content/learning-path.entity"; +import {getLearningObjectRepository, getLearningPathRepository} from "../../../src/data/repositories"; +import learningObjectExample from "../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example"; +import learningPathExample from "../../test-assets/learning-paths/pn-werking-example"; +import {Language} from "../../../src/entities/content/language"; +import learningPathService from "../../../src/services/learning-paths/learning-path-service"; + +async function initExampleData(): Promise<{ learningObject: LearningObject, learningPath: LearningPath }> { + const learningObjectRepo = getLearningObjectRepository(); + const learningPathRepo = getLearningPathRepository(); + let learningObject = learningObjectExample.createLearningObject(); + let learningPath = learningPathExample.createLearningPath(); + await learningObjectRepo.save(learningObject); + await learningPathRepo.save(learningPath); + return { learningObject, learningPath }; +} + +const TEST_DWENGO_LEARNING_PATH_HRUID = "pn_werking"; +const TEST_DWENGO_LEARNING_PATH_TITLE = "Werken met notebooks"; +const TEST_DWENGO_EXCLUSIVE_LEARNING_PATH_SEARCH_QUERY = "Microscopie"; +const TEST_SEARCH_QUERY_EXPECTING_NO_MATCHES = "su$m8f9usf89ud { + let example: { learningObject: LearningObject, learningPath: LearningPath }; + beforeAll(async () => { + await setupTestApp(); + example = await initExampleData(); + }); + describe("fetchLearningPaths", () => { + it("should return learning paths both from the database and from the Dwengo API", async () => { + const result = await learningPathService.fetchLearningPaths( + [example.learningPath.hruid, TEST_DWENGO_LEARNING_PATH_HRUID], + example.learningPath.language, + "the source" + ); + expect(result.success).toBeTruthy(); + expect(result.data?.filter(it => it.hruid == TEST_DWENGO_LEARNING_PATH_HRUID).length).not.toBe(0); + expect(result.data?.filter(it => it.hruid == example.learningPath.hruid).length).not.toBe(0); + expect(result.data?.filter(it => it.hruid == TEST_DWENGO_LEARNING_PATH_HRUID)[0].title) + .toEqual(TEST_DWENGO_LEARNING_PATH_TITLE); + expect(result.data?.filter(it => it.hruid == example.learningPath.hruid)[0].title) + .toEqual(example.learningPath.title); + }); + it("should include both the learning objects from the Dwengo API and learning objects from the database in its response", async () => { + const result = await learningPathService.fetchLearningPaths( + [example.learningPath.hruid], + example.learningPath.language, + "the source" + ); + expect(result.success).toBeTruthy(); + expect(result.data?.length).toBe(1); + + // Should include all the nodes, even those pointing to foreign learning objects. + expect( + [...result.data![0].nodes.map(it => it.learningobject_hruid)].sort() + ).toEqual( + example.learningPath.nodes.map(it => it.learningObjectHruid).sort() + ); + }); + }); + describe("searchLearningPath", () => { + it("should include both the learning paths from the Dwengo API and those from the database in its response", async () => { + // This matches the learning object in the database, but definitely also some learning objects in the Dwengo API. + const result = await learningPathService.searchLearningPaths( + example.learningPath.title.substring(2, 3), + example.learningPath.language + ); + + // Should find the one from the database + expect( + result.filter(it => + it.hruid === example.learningPath.hruid && it.title === example.learningPath.title + ).length + ).toBe(1); + + // but should not only find that one. + expect(result.length).not.toBeLessThan(2); + }); + it("should still return results from the Dwengo API even though there are no matches in the database", async () => { + const result = await learningPathService.searchLearningPaths( + TEST_DWENGO_EXCLUSIVE_LEARNING_PATH_SEARCH_QUERY, + Language.Dutch + ); + + // Should find something... + expect(result.length).not.toBe(0); + + // but not the example learning path. + expect( + result.filter(it => + it.hruid === example.learningPath.hruid && it.title === example.learningPath.title + ).length + ).toBe(0); + }); + it("should return an empty list if neither the Dwengo API nor the database contains matches", async () => { + const result = await learningPathService.searchLearningPaths( + TEST_SEARCH_QUERY_EXPECTING_NO_MATCHES, + Language.Dutch + ); + expect(result.length).toBe(0); + }); + }); +}); diff --git a/backend/tests/test-assets/learning-objects/pn_werkingnotebooks/Knop.png b/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/Knop.png similarity index 100% rename from backend/tests/test-assets/learning-objects/pn_werkingnotebooks/Knop.png rename to backend/tests/test-assets/learning-objects/pn-werkingnotebooks/Knop.png diff --git a/backend/tests/test-assets/learning-objects/pn_werkingnotebooks/content.md b/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/content.md similarity index 100% rename from backend/tests/test-assets/learning-objects/pn_werkingnotebooks/content.md rename to backend/tests/test-assets/learning-objects/pn-werkingnotebooks/content.md diff --git a/backend/tests/test-assets/learning-objects/pn_werkingnotebooks/dwengo.png b/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/dwengo.png similarity index 100% rename from backend/tests/test-assets/learning-objects/pn_werkingnotebooks/dwengo.png rename to backend/tests/test-assets/learning-objects/pn-werkingnotebooks/dwengo.png diff --git a/backend/tests/test-assets/learning-objects/pn_werkingnotebooks/pn-werkingnotebooks-example.ts b/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example.ts similarity index 98% rename from backend/tests/test-assets/learning-objects/pn_werkingnotebooks/pn-werkingnotebooks-example.ts rename to backend/tests/test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example.ts index 4dff2c34..09cab0f9 100644 --- a/backend/tests/test-assets/learning-objects/pn_werkingnotebooks/pn-werkingnotebooks-example.ts +++ b/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example.ts @@ -6,7 +6,7 @@ import {EducationalGoal, LearningObject, ReturnValue} from "../../../../src/enti import {Attachment} from "../../../../src/entities/content/attachment.entity"; import {EnvVars, getEnvVar} from "../../../../src/util/envvars"; -const ASSETS_PREFIX = "learning-objects/pn_werkingnotebooks/"; +const ASSETS_PREFIX = "learning-objects/pn-werkingnotebooks/"; const example: LearningObjectExample = { createLearningObject: ()=> { diff --git a/backend/tests/test-assets/learning-objects/pn_werkingnotebooks/rendering.html b/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/rendering.html similarity index 100% rename from backend/tests/test-assets/learning-objects/pn_werkingnotebooks/rendering.html rename to backend/tests/test-assets/learning-objects/pn-werkingnotebooks/rendering.html From 385f4ae569dade14a730593603577dd1b36b14e3 Mon Sep 17 00:00:00 2001 From: Gerald Schmittinger Date: Mon, 10 Mar 2025 23:35:53 +0100 Subject: [PATCH 55/96] fix(backend): Per ongeluk gemaakte aanpassing ongedaan gemaakt. (De '_' in DWENGO_TEST_LEARNING_OBJECT_ID (learning-object-service.test.ts) moest wel degelijk een '_' blijven en geen '-' worden.) --- .../services/learning-objects/learning-object-service.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/tests/services/learning-objects/learning-object-service.test.ts b/backend/tests/services/learning-objects/learning-object-service.test.ts index 2fac3d3e..970e4675 100644 --- a/backend/tests/services/learning-objects/learning-object-service.test.ts +++ b/backend/tests/services/learning-objects/learning-object-service.test.ts @@ -11,7 +11,7 @@ import {EnvVars, getEnvVar} from "../../../src/util/envvars"; const TEST_LEARNING_OBJECT_TITLE = "Test title"; const EXPECTED_DWENGO_LEARNING_OBJECT_TITLE = "Werken met notebooks"; const DWENGO_TEST_LEARNING_OBJECT_ID: LearningObjectIdentifier = { - hruid: "pn-werkingnotebooks", + hruid: "pn_werkingnotebooks", language: Language.Dutch, version: 3 }; From 050c83ca33550d293533e29c3f3788db434aedfb Mon Sep 17 00:00:00 2001 From: Gerald Schmittinger Date: Mon, 10 Mar 2025 23:55:16 +0100 Subject: [PATCH 56/96] test(backend): Test-meerkeuzevraag toegevoegd. --- .../test-multiple-choice/content.txt | 5 +++++ .../test-multiple-choice/rendering.html | 0 .../test-multiple-choice-example.ts | 22 +++++++++++++++++++ 3 files changed, 27 insertions(+) create mode 100644 backend/tests/test-assets/learning-objects/test-multiple-choice/content.txt create mode 100644 backend/tests/test-assets/learning-objects/test-multiple-choice/rendering.html create mode 100644 backend/tests/test-assets/learning-objects/test-multiple-choice/test-multiple-choice-example.ts diff --git a/backend/tests/test-assets/learning-objects/test-multiple-choice/content.txt b/backend/tests/test-assets/learning-objects/test-multiple-choice/content.txt new file mode 100644 index 00000000..7dd5527d --- /dev/null +++ b/backend/tests/test-assets/learning-objects/test-multiple-choice/content.txt @@ -0,0 +1,5 @@ +::MC basic:: +Are you following along well with the class? { + ~No, it's very difficult to follow along. + =Yes, no problem! +} diff --git a/backend/tests/test-assets/learning-objects/test-multiple-choice/rendering.html b/backend/tests/test-assets/learning-objects/test-multiple-choice/rendering.html new file mode 100644 index 00000000..e69de29b diff --git a/backend/tests/test-assets/learning-objects/test-multiple-choice/test-multiple-choice-example.ts b/backend/tests/test-assets/learning-objects/test-multiple-choice/test-multiple-choice-example.ts new file mode 100644 index 00000000..141a0411 --- /dev/null +++ b/backend/tests/test-assets/learning-objects/test-multiple-choice/test-multiple-choice-example.ts @@ -0,0 +1,22 @@ +import {LearningObjectExample} from "../learning-object-example"; +import {LearningObject} from "../../../../src/entities/content/learning-object.entity"; +import {loadTestAsset} from "../../../test-utils/load-test-asset"; +import {EnvVars, getEnvVar} from "../../../../src/util/envvars"; +import {Language} from "../../../../src/entities/content/language"; + +const example: LearningObjectExample = { + createLearningObject: () => { + const learningObject = new LearningObject(); + learningObject.hruid = `${getEnvVar(EnvVars.UserContentPrefix)}test_multiple_choice`; + learningObject.language = Language.English; + learningObject.version = 1; + learningObject.title = "Multiple choice question for testing"; + learningObject.description = "This multiple choice question was only created for testing purposes."; + learningObject.content = loadTestAsset("learning-objects/test-multiple-choice/content.txt"); + return learningObject; + }, + createAttachment: {}, + getHTMLRendering: () => loadTestAsset("learning-objects/test-multiple-choice/rendering.html").toString() +}; + +export default example; From 164a547dd1031ebb4959d76ec80d7c82367f7714 Mon Sep 17 00:00:00 2001 From: Gerald Schmittinger Date: Tue, 11 Mar 2025 00:54:06 +0100 Subject: [PATCH 57/96] test(backend): Testdata voor voorwaardelijke overgangen toegevoegd --- .../learning-path-personalizing-service.ts | 5 ++ .../dummy/dummy-learning-object-example.ts | 25 +++++++ .../learning-objects/dummy/rendering.html | 0 .../learning-paths/test-conditions-example.ts | 74 +++++++++++++++++++ 4 files changed, 104 insertions(+) create mode 100644 backend/src/services/learning-paths/learning-path-personalizing-service.ts create mode 100644 backend/tests/test-assets/learning-objects/dummy/dummy-learning-object-example.ts create mode 100644 backend/tests/test-assets/learning-objects/dummy/rendering.html create mode 100644 backend/tests/test-assets/learning-paths/test-conditions-example.ts diff --git a/backend/src/services/learning-paths/learning-path-personalizing-service.ts b/backend/src/services/learning-paths/learning-path-personalizing-service.ts new file mode 100644 index 00000000..2341cc60 --- /dev/null +++ b/backend/src/services/learning-paths/learning-path-personalizing-service.ts @@ -0,0 +1,5 @@ +const learningPathPersonalizingService = { + +}; + +export default learningPathPersonalizingService; diff --git a/backend/tests/test-assets/learning-objects/dummy/dummy-learning-object-example.ts b/backend/tests/test-assets/learning-objects/dummy/dummy-learning-object-example.ts new file mode 100644 index 00000000..cb0f3d45 --- /dev/null +++ b/backend/tests/test-assets/learning-objects/dummy/dummy-learning-object-example.ts @@ -0,0 +1,25 @@ +import {LearningObjectExample} from "../learning-object-example"; +import {LearningObject} from "../../../../src/entities/content/learning-object.entity"; +import {Language} from "../../../../src/entities/content/language"; +import {loadTestAsset} from "../../../test-utils/load-test-asset"; + +/** + * Create a dummy learning object to be used in tests where multiple learning objects are needed (for example for use + * on a path), but where the precise contents of the learning object are not important. + */ +export function dummyLearningObject(hruid: string, language: Language, title: string): LearningObjectExample { + return { + createLearningObject: () => { + const learningObject = new LearningObject(); + learningObject.hruid = hruid; + learningObject.language = language; + learningObject.version = 1; + learningObject.title = title; + learningObject.description = "Just a dummy learning object for testing purposes"; + learningObject.content = Buffer.from("Dummy content"); + return learningObject; + }, + createAttachment: {}, + getHTMLRendering: () => loadTestAsset("learning-objects/dummy/rendering.html").toString() + } +} diff --git a/backend/tests/test-assets/learning-objects/dummy/rendering.html b/backend/tests/test-assets/learning-objects/dummy/rendering.html new file mode 100644 index 00000000..e69de29b diff --git a/backend/tests/test-assets/learning-paths/test-conditions-example.ts b/backend/tests/test-assets/learning-paths/test-conditions-example.ts new file mode 100644 index 00000000..000bb732 --- /dev/null +++ b/backend/tests/test-assets/learning-paths/test-conditions-example.ts @@ -0,0 +1,74 @@ +import {LearningPath} from "../../../src/entities/content/learning-path.entity"; +import {Language} from "../../../src/entities/content/language"; +import testMultipleChoiceExample from "../learning-objects/test-multiple-choice/test-multiple-choice-example"; +import {dummyLearningObject} from "../learning-objects/dummy/dummy-learning-object-example"; +import {createLearningPathNode, createLearningPathTransition} from "./learning-path-utils"; + +const example: LearningPathExample = { + createLearningPath: () => { + const learningPath = new LearningPath(); + learningPath.hruid = "test_conditions"; + learningPath.language = Language.English; + learningPath.title = "Example learning path with conditional transitions"; + learningPath.description = "This learning path was made for the purpose of testing conditional transitions"; + + const branchingLearningObject = testMultipleChoiceExample.createLearningObject(); + const extraExerciseLearningObject = dummyLearningObject( + "test_extra_exercise", Language.English, "Extra exercise (for students with difficulties)" + ).createLearningObject(); + const finalLearningObject = dummyLearningObject( + "test_final_learning_object", Language.English, "Final exercise (for everyone)" + ).createLearningObject(); + + const branchingNode = createLearningPathNode( + learningPath, + 0, + branchingLearningObject.hruid, + branchingLearningObject.version, + branchingLearningObject.language, + true + ); + const extraExerciseNode = createLearningPathNode( + learningPath, + 1, + extraExerciseLearningObject.hruid, + extraExerciseLearningObject.version, + extraExerciseLearningObject.language, + false + ); + const finalNode = createLearningPathNode( + learningPath, + 2, + finalLearningObject.hruid, + finalLearningObject.version, + finalLearningObject.language, + false + ); + + const transitionToExtraExercise = createLearningPathTransition( + branchingNode, + 0, + "$[?(@[0] == 0)]", // The answer to the first question was the first one, which says that it is difficult for the student to follow along. + extraExerciseNode + ); + const directTransitionToFinal = createLearningPathTransition( + branchingNode, + 1, + "$[?(@[0] == 1)]", + finalNode + ); + const transitionExtraExerciseToFinal = createLearningPathTransition( + extraExerciseNode, + 0, + "true", + finalNode + ); + + branchingNode.transitions = [transitionToExtraExercise, directTransitionToFinal]; + extraExerciseNode.transitions = [transitionExtraExerciseToFinal]; + + learningPath.nodes = [branchingNode, extraExerciseNode, finalNode]; + + return learningPath; + } +}; From bc0ac63c92d16a402174662cd7e88b91321a65da Mon Sep 17 00:00:00 2001 From: Gerald Schmittinger Date: Tue, 11 Mar 2025 02:00:27 +0100 Subject: [PATCH 58/96] feat(backend): Rendering van meerkeuzevragen en open vragen (essay) toegevoegd + getest --- .../processing/audio/audio-processor.ts | 2 ++ .../processing/extern/extern-processor.ts | 2 ++ .../processing/gift/gift-processor.ts | 14 +++++++---- .../category-question-renderer.ts | 2 +- .../description-question-renderer.ts | 2 +- .../essay-question-renderer.ts | 12 +++++++-- .../gift-question-renderer.ts | 3 ++- .../matching-question-renderer.ts | 2 +- .../multiple-choice-question-renderer.ts | 19 ++++++++++++-- .../numerical-question-renderer.ts | 2 +- .../short-question-renderer.ts | 2 +- .../true-false-question-renderer.ts | 2 +- .../processing/pdf/pdf-processor.ts | 2 ++ .../processing/processing-service.test.ts | 25 +++++++++++++++++++ .../dummy/dummy-learning-object-example.ts | 2 ++ .../learning-objects/test-essay/content.txt | 2 ++ .../test-essay/rendering.html | 7 ++++++ .../test-essay/test-essay-example.ts | 24 ++++++++++++++++++ .../test-multiple-choice/rendering.html | 14 +++++++++++ .../test-multiple-choice-example.ts | 2 ++ 20 files changed, 126 insertions(+), 16 deletions(-) create mode 100644 backend/tests/services/learning-objects/processing/processing-service.test.ts create mode 100644 backend/tests/test-assets/learning-objects/test-essay/content.txt create mode 100644 backend/tests/test-assets/learning-objects/test-essay/rendering.html create mode 100644 backend/tests/test-assets/learning-objects/test-essay/test-essay-example.ts diff --git a/backend/src/services/learning-objects/processing/audio/audio-processor.ts b/backend/src/services/learning-objects/processing/audio/audio-processor.ts index 6442e1b3..0c4dd75e 100644 --- a/backend/src/services/learning-objects/processing/audio/audio-processor.ts +++ b/backend/src/services/learning-objects/processing/audio/audio-processor.ts @@ -1,5 +1,7 @@ /** * Based on https://github.com/dwengovzw/Learning-Object-Repository/blob/main/app/processors/audio/audio_processor.js + * + * WARNING: The support for audio learning objects is currently still experimental. */ import DOMPurify from 'isomorphic-dompurify'; diff --git a/backend/src/services/learning-objects/processing/extern/extern-processor.ts b/backend/src/services/learning-objects/processing/extern/extern-processor.ts index 2aa02482..aff26d45 100644 --- a/backend/src/services/learning-objects/processing/extern/extern-processor.ts +++ b/backend/src/services/learning-objects/processing/extern/extern-processor.ts @@ -1,5 +1,7 @@ /** * Based on https://github.com/dwengovzw/Learning-Object-Repository/blob/main/app/processors/extern/extern_processor.js + * + * WARNING: The support for external content is currently still experimental. */ import DOMPurify from 'isomorphic-dompurify'; diff --git a/backend/src/services/learning-objects/processing/gift/gift-processor.ts b/backend/src/services/learning-objects/processing/gift/gift-processor.ts index 5cffcb47..5d20e99c 100644 --- a/backend/src/services/learning-objects/processing/gift/gift-processor.ts +++ b/backend/src/services/learning-objects/processing/gift/gift-processor.ts @@ -36,18 +36,22 @@ class GiftProcessor extends StringProcessor { override renderFn(giftString: string) { const quizQuestions: GIFTQuestion[] = parse(giftString); - let html = "
"; + let html = "
\n"; + let i = 1; for (let question of quizQuestions) { - html += this.renderQuestion(question); + html += `
\n`; + html += " " + this.renderQuestion(question, i).replaceAll(/\n(.+)/g, "\n $1"); // replace for indentation. + html += `
\n`; + i++; } - html += "
" + html += "
\n" return DOMPurify.sanitize(html); } - private renderQuestion(question: T): string { + private renderQuestion(question: T, questionNumber: number): string { const renderer = this.renderers[question.type] as GIFTQuestionRenderer; - return renderer.render(question); + return renderer.render(question, questionNumber); } } diff --git a/backend/src/services/learning-objects/processing/gift/question-renderers/category-question-renderer.ts b/backend/src/services/learning-objects/processing/gift/question-renderers/category-question-renderer.ts index 8329d4d6..6f299c17 100644 --- a/backend/src/services/learning-objects/processing/gift/question-renderers/category-question-renderer.ts +++ b/backend/src/services/learning-objects/processing/gift/question-renderers/category-question-renderer.ts @@ -3,7 +3,7 @@ import {Category} from "gift-pegjs"; import {ProcessingError} from "../../processing-error"; export class CategoryQuestionRenderer extends GIFTQuestionRenderer { - render(question: Category): string { + render(question: Category, questionNumber: number): string { throw new ProcessingError("The question type 'Category' is not supported yet!"); } } diff --git a/backend/src/services/learning-objects/processing/gift/question-renderers/description-question-renderer.ts b/backend/src/services/learning-objects/processing/gift/question-renderers/description-question-renderer.ts index 1f8c94be..adea25a6 100644 --- a/backend/src/services/learning-objects/processing/gift/question-renderers/description-question-renderer.ts +++ b/backend/src/services/learning-objects/processing/gift/question-renderers/description-question-renderer.ts @@ -3,7 +3,7 @@ import {Description} from "gift-pegjs"; import {ProcessingError} from "../../processing-error"; export class DescriptionQuestionRenderer extends GIFTQuestionRenderer { - render(question: Description): string { + render(question: Description, questionNumber: number): string { throw new ProcessingError("The question type 'Description' is not supported yet!"); } } diff --git a/backend/src/services/learning-objects/processing/gift/question-renderers/essay-question-renderer.ts b/backend/src/services/learning-objects/processing/gift/question-renderers/essay-question-renderer.ts index e990d000..af000c11 100644 --- a/backend/src/services/learning-objects/processing/gift/question-renderers/essay-question-renderer.ts +++ b/backend/src/services/learning-objects/processing/gift/question-renderers/essay-question-renderer.ts @@ -2,7 +2,15 @@ import {GIFTQuestionRenderer} from "./gift-question-renderer"; import {Essay} from "gift-pegjs"; export class EssayQuestionRenderer extends GIFTQuestionRenderer { - render(question: Essay): string { - return ""; + render(question: Essay, questionNumber: number): string { + let renderedHtml = ""; + if (question.title) { + renderedHtml += `

${question.title}

\n`; + } + if (question.stem) { + renderedHtml += `

${question.stem.text}

\n`; + } + renderedHtml += `\n`; + return renderedHtml; } } diff --git a/backend/src/services/learning-objects/processing/gift/question-renderers/gift-question-renderer.ts b/backend/src/services/learning-objects/processing/gift/question-renderers/gift-question-renderer.ts index 8b91185b..bd33107b 100644 --- a/backend/src/services/learning-objects/processing/gift/question-renderers/gift-question-renderer.ts +++ b/backend/src/services/learning-objects/processing/gift/question-renderers/gift-question-renderer.ts @@ -7,7 +7,8 @@ export abstract class GIFTQuestionRenderer { /** * Render the given question to HTML. * @param question The question. + * @param questionNumber The index number of the question. * @returns The question rendered as HTML. */ - abstract render(question: T): string; + abstract render(question: T, questionNumber: number): string; } diff --git a/backend/src/services/learning-objects/processing/gift/question-renderers/matching-question-renderer.ts b/backend/src/services/learning-objects/processing/gift/question-renderers/matching-question-renderer.ts index e9a2deaf..93e7511e 100644 --- a/backend/src/services/learning-objects/processing/gift/question-renderers/matching-question-renderer.ts +++ b/backend/src/services/learning-objects/processing/gift/question-renderers/matching-question-renderer.ts @@ -3,7 +3,7 @@ import {Matching} from "gift-pegjs"; import {ProcessingError} from "../../processing-error"; export class MatchingQuestionRenderer extends GIFTQuestionRenderer { - render(question: Matching): string { + render(question: Matching, questionNumber: number): string { throw new ProcessingError("The question type 'Matching' is not supported yet!"); } } diff --git a/backend/src/services/learning-objects/processing/gift/question-renderers/multiple-choice-question-renderer.ts b/backend/src/services/learning-objects/processing/gift/question-renderers/multiple-choice-question-renderer.ts index 2c40ff25..6b6d8eea 100644 --- a/backend/src/services/learning-objects/processing/gift/question-renderers/multiple-choice-question-renderer.ts +++ b/backend/src/services/learning-objects/processing/gift/question-renderers/multiple-choice-question-renderer.ts @@ -2,7 +2,22 @@ import {GIFTQuestionRenderer} from "./gift-question-renderer"; import {MultipleChoice} from "gift-pegjs"; export class MultipleChoiceQuestionRenderer extends GIFTQuestionRenderer { - render(question: MultipleChoice): string { - return ""; + render(question: MultipleChoice, questionNumber: number): string { + let renderedHtml = ""; + if (question.title) { + renderedHtml += `

${question.title}

\n`; + } + if (question.stem) { + renderedHtml += `

${question.stem.text}

\n`; + } + let i = 0; + for (let choice of question.choices) { + renderedHtml += `
\n`; + renderedHtml += ` \n`; + renderedHtml += ` \n`; + renderedHtml += `
\n`; + i++; + } + return renderedHtml; } } diff --git a/backend/src/services/learning-objects/processing/gift/question-renderers/numerical-question-renderer.ts b/backend/src/services/learning-objects/processing/gift/question-renderers/numerical-question-renderer.ts index 5da3fafd..6553add4 100644 --- a/backend/src/services/learning-objects/processing/gift/question-renderers/numerical-question-renderer.ts +++ b/backend/src/services/learning-objects/processing/gift/question-renderers/numerical-question-renderer.ts @@ -3,7 +3,7 @@ import {Numerical} from "gift-pegjs"; import {ProcessingError} from "../../processing-error"; export class NumericalQuestionRenderer extends GIFTQuestionRenderer { - render(question: Numerical): string { + render(question: Numerical, questionNumber: number): string { throw new ProcessingError("The question type 'Numerical' is not supported yet!"); } } diff --git a/backend/src/services/learning-objects/processing/gift/question-renderers/short-question-renderer.ts b/backend/src/services/learning-objects/processing/gift/question-renderers/short-question-renderer.ts index be3c9365..96196a67 100644 --- a/backend/src/services/learning-objects/processing/gift/question-renderers/short-question-renderer.ts +++ b/backend/src/services/learning-objects/processing/gift/question-renderers/short-question-renderer.ts @@ -3,7 +3,7 @@ import {ShortAnswer} from "gift-pegjs"; import {ProcessingError} from "../../processing-error"; export class ShortQuestionRenderer extends GIFTQuestionRenderer { - render(question: ShortAnswer): string { + render(question: ShortAnswer, questionNumber: number): string { throw new ProcessingError("The question type 'ShortAnswer' is not supported yet!"); } } diff --git a/backend/src/services/learning-objects/processing/gift/question-renderers/true-false-question-renderer.ts b/backend/src/services/learning-objects/processing/gift/question-renderers/true-false-question-renderer.ts index 668ee3de..0ff108f4 100644 --- a/backend/src/services/learning-objects/processing/gift/question-renderers/true-false-question-renderer.ts +++ b/backend/src/services/learning-objects/processing/gift/question-renderers/true-false-question-renderer.ts @@ -3,7 +3,7 @@ import {TrueFalse} from "gift-pegjs"; import {ProcessingError} from "../../processing-error"; export class TrueFalseQuestionRenderer extends GIFTQuestionRenderer { - render(question: TrueFalse): string { + render(question: TrueFalse, questionNumber: number): string { throw new ProcessingError("The question type 'TrueFalse' is not supported yet!"); } } diff --git a/backend/src/services/learning-objects/processing/pdf/pdf-processor.ts b/backend/src/services/learning-objects/processing/pdf/pdf-processor.ts index fe05077e..dcb08ba7 100644 --- a/backend/src/services/learning-objects/processing/pdf/pdf-processor.ts +++ b/backend/src/services/learning-objects/processing/pdf/pdf-processor.ts @@ -1,5 +1,7 @@ /** * Based on https://github.com/dwengovzw/Learning-Object-Repository/blob/main/app/processors/pdf/pdf_processor.js + * + * WARNING: The support for PDF learning objects is currently still experimental. */ import DOMPurify from 'isomorphic-dompurify'; diff --git a/backend/tests/services/learning-objects/processing/processing-service.test.ts b/backend/tests/services/learning-objects/processing/processing-service.test.ts new file mode 100644 index 00000000..f0107162 --- /dev/null +++ b/backend/tests/services/learning-objects/processing/processing-service.test.ts @@ -0,0 +1,25 @@ +import {describe, expect, it} from "vitest"; +import mdExample from "../../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example"; +import multipleChoiceExample from "../../../test-assets/learning-objects/test-multiple-choice/test-multiple-choice-example"; +import essayExample from "../../../test-assets/learning-objects/test-essay/test-essay-example"; +import processingService from "../../../../src/services/learning-objects/processing/processing-service"; + +describe("ProcessingService", () => { + it("renders a markdown learning object correctly", async () => { + const markdownLearningObject = mdExample.createLearningObject(); + const result = await processingService.render(markdownLearningObject); + expect(result).toEqual(mdExample.getHTMLRendering()); + }); + + it("renders a multiple choice question correctly", async () => { + const multipleChoiceLearningObject = multipleChoiceExample.createLearningObject(); + const result = await processingService.render(multipleChoiceLearningObject); + expect(result).toEqual(multipleChoiceExample.getHTMLRendering()); + }); + + it("renders an essay question correctly", async () => { + const essayLearningObject = essayExample.createLearningObject(); + const result = await processingService.render(essayLearningObject); + expect(result).toEqual(essayExample.getHTMLRendering()); + }); +}); diff --git a/backend/tests/test-assets/learning-objects/dummy/dummy-learning-object-example.ts b/backend/tests/test-assets/learning-objects/dummy/dummy-learning-object-example.ts index cb0f3d45..dfc15a7a 100644 --- a/backend/tests/test-assets/learning-objects/dummy/dummy-learning-object-example.ts +++ b/backend/tests/test-assets/learning-objects/dummy/dummy-learning-object-example.ts @@ -2,6 +2,7 @@ import {LearningObjectExample} from "../learning-object-example"; import {LearningObject} from "../../../../src/entities/content/learning-object.entity"; import {Language} from "../../../../src/entities/content/language"; import {loadTestAsset} from "../../../test-utils/load-test-asset"; +import {DwengoContentType} from "../../../../src/services/learning-objects/processing/content-type"; /** * Create a dummy learning object to be used in tests where multiple learning objects are needed (for example for use @@ -16,6 +17,7 @@ export function dummyLearningObject(hruid: string, language: Language, title: st learningObject.version = 1; learningObject.title = title; learningObject.description = "Just a dummy learning object for testing purposes"; + learningObject.contentType = DwengoContentType.TEXT_PLAIN; learningObject.content = Buffer.from("Dummy content"); return learningObject; }, diff --git a/backend/tests/test-assets/learning-objects/test-essay/content.txt b/backend/tests/test-assets/learning-objects/test-essay/content.txt new file mode 100644 index 00000000..dd6b5c77 --- /dev/null +++ b/backend/tests/test-assets/learning-objects/test-essay/content.txt @@ -0,0 +1,2 @@ +::MC basic:: +How are you? {} diff --git a/backend/tests/test-assets/learning-objects/test-essay/rendering.html b/backend/tests/test-assets/learning-objects/test-essay/rendering.html new file mode 100644 index 00000000..adb072a0 --- /dev/null +++ b/backend/tests/test-assets/learning-objects/test-essay/rendering.html @@ -0,0 +1,7 @@ +
+
+

MC basic

+

How are you?

+ +
+
diff --git a/backend/tests/test-assets/learning-objects/test-essay/test-essay-example.ts b/backend/tests/test-assets/learning-objects/test-essay/test-essay-example.ts new file mode 100644 index 00000000..08a818f7 --- /dev/null +++ b/backend/tests/test-assets/learning-objects/test-essay/test-essay-example.ts @@ -0,0 +1,24 @@ +import {LearningObjectExample} from "../learning-object-example"; +import {LearningObject} from "../../../../src/entities/content/learning-object.entity"; +import {loadTestAsset} from "../../../test-utils/load-test-asset"; +import {EnvVars, getEnvVar} from "../../../../src/util/envvars"; +import {Language} from "../../../../src/entities/content/language"; +import {DwengoContentType} from "../../../../src/services/learning-objects/processing/content-type"; + +const example: LearningObjectExample = { + createLearningObject: () => { + const learningObject = new LearningObject(); + learningObject.hruid = `${getEnvVar(EnvVars.UserContentPrefix)}test_essay`; + learningObject.language = Language.English; + learningObject.version = 1; + learningObject.title = "Essay question for testing"; + learningObject.description = "This essay question was only created for testing purposes."; + learningObject.contentType = DwengoContentType.GIFT; + learningObject.content = loadTestAsset("learning-objects/test-essay/content.txt"); + return learningObject; + }, + createAttachment: {}, + getHTMLRendering: () => loadTestAsset("learning-objects/test-essay/rendering.html").toString() +}; + +export default example; diff --git a/backend/tests/test-assets/learning-objects/test-multiple-choice/rendering.html b/backend/tests/test-assets/learning-objects/test-multiple-choice/rendering.html index e69de29b..c1829f24 100644 --- a/backend/tests/test-assets/learning-objects/test-multiple-choice/rendering.html +++ b/backend/tests/test-assets/learning-objects/test-multiple-choice/rendering.html @@ -0,0 +1,14 @@ +
+
+

MC basic

+

Are you following along well with the class?

+
+ + +
+
+ + +
+
+
diff --git a/backend/tests/test-assets/learning-objects/test-multiple-choice/test-multiple-choice-example.ts b/backend/tests/test-assets/learning-objects/test-multiple-choice/test-multiple-choice-example.ts index 141a0411..6abc56dd 100644 --- a/backend/tests/test-assets/learning-objects/test-multiple-choice/test-multiple-choice-example.ts +++ b/backend/tests/test-assets/learning-objects/test-multiple-choice/test-multiple-choice-example.ts @@ -3,6 +3,7 @@ import {LearningObject} from "../../../../src/entities/content/learning-object.e import {loadTestAsset} from "../../../test-utils/load-test-asset"; import {EnvVars, getEnvVar} from "../../../../src/util/envvars"; import {Language} from "../../../../src/entities/content/language"; +import {DwengoContentType} from "../../../../src/services/learning-objects/processing/content-type"; const example: LearningObjectExample = { createLearningObject: () => { @@ -12,6 +13,7 @@ const example: LearningObjectExample = { learningObject.version = 1; learningObject.title = "Multiple choice question for testing"; learningObject.description = "This multiple choice question was only created for testing purposes."; + learningObject.contentType = DwengoContentType.GIFT; learningObject.content = loadTestAsset("learning-objects/test-multiple-choice/content.txt"); return learningObject; }, From a30c4d0d32d6d69a69dcc034d4d93bca97338371 Mon Sep 17 00:00:00 2001 From: Gerald Schmittinger Date: Tue, 11 Mar 2025 04:08:32 +0100 Subject: [PATCH 59/96] =?UTF-8?q?feat(backend):=20LearningPathPersonalizin?= =?UTF-8?q?gService=20ge=C3=AFmplementeerd?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/package.json | 16 +- backend/src/controllers/learning-objects.ts | 2 +- backend/src/exceptions.ts | 1 + .../learning-path-personalizing-service.ts | 66 +++++++- package-lock.json | 160 ++++++------------ 5 files changed, 129 insertions(+), 116 deletions(-) diff --git a/backend/package.json b/backend/package.json index 2d9ea005..1e589752 100644 --- a/backend/package.json +++ b/backend/package.json @@ -18,24 +18,24 @@ "@mikro-orm/postgresql": "^6.4.6", "@mikro-orm/reflection": "^6.4.6", "@mikro-orm/sqlite": "6.4.6", + "@types/cors": "^2.8.17", "@types/js-yaml": "^4.0.9", "axios": "^1.8.2", + "cors": "^2.8.5", "dotenv": "^16.4.7", "express": "^5.0.1", + "express-jwt": "^8.5.1", "gift-pegjs": "^1.0.2", "isomorphic-dompurify": "^2.22.0", - "express-jwt": "^8.5.1", - "jwks-rsa": "^3.1.0", - "uuid": "^11.1.0", "js-yaml": "^4.1.0", - "marked": "^15.0.7", - "uuid": "^11.1.0", + "jsonpath-plus": "^10.3.0", + "jwks-rsa": "^3.1.0", "loki-logger-ts": "^1.0.2", + "marked": "^15.0.7", "response-time": "^2.3.3", + "uuid": "^11.1.0", "winston": "^3.17.0", - "winston-loki": "^6.1.3", - "cors": "^2.8.5", - "@types/cors": "^2.8.17" + "winston-loki": "^6.1.3" }, "devDependencies": { "@mikro-orm/cli": "^6.4.6", diff --git a/backend/src/controllers/learning-objects.ts b/backend/src/controllers/learning-objects.ts index 0445d9f1..ece16057 100644 --- a/backend/src/controllers/learning-objects.ts +++ b/backend/src/controllers/learning-objects.ts @@ -15,7 +15,7 @@ function getLearningObjectIdentifierFromRequest(req: Request): LearningObjectIde return { hruid: req.params.hruid as string, language: (req.query.language || getEnvVar(EnvVars.FallbackLanguage)) as Language, - version: req.query.version as string + version: parseInt(req.query.version as string) }; } diff --git a/backend/src/exceptions.ts b/backend/src/exceptions.ts index 8e2c886c..e93a6c93 100644 --- a/backend/src/exceptions.ts +++ b/backend/src/exceptions.ts @@ -24,6 +24,7 @@ export class UnauthorizedException extends Error { */ export class ForbiddenException extends Error { status = 403; + constructor(message: string = 'Forbidden') { super(message); } diff --git a/backend/src/services/learning-paths/learning-path-personalizing-service.ts b/backend/src/services/learning-paths/learning-path-personalizing-service.ts index 2341cc60..b7d1fe06 100644 --- a/backend/src/services/learning-paths/learning-path-personalizing-service.ts +++ b/backend/src/services/learning-paths/learning-path-personalizing-service.ts @@ -1,5 +1,69 @@ -const learningPathPersonalizingService = { +import {Student} from "../../entities/users/student.entity"; +import {getSubmissionRepository} from "../../data/repositories"; +import {Group} from "../../entities/assignments/group.entity"; +import {Submission} from "../../entities/assignments/submission.entity"; +import {LearningObjectIdentifier} from "../../entities/content/learning-object-identifier"; +import {LearningPathNode} from "../../entities/content/learning-path-node.entity"; +import {LearningPathTransition} from "../../entities/content/learning-path-transition.entity"; +import {JSONPath} from 'jsonpath-plus'; +/** + * Returns the last submission for the learning object associated with the given node and for the student or group + */ +async function getLastRelevantSubmission(node: LearningPathNode, pathFor: {student?: Student, group?: Group}): Promise { + const submissionRepo = getSubmissionRepository(); + const learningObjectId: LearningObjectIdentifier = { + hruid: node.learningObjectHruid, + language: node.language, + version: node.version + }; + let lastSubmission: Submission | null; + if (pathFor.group) { + lastSubmission = await submissionRepo.findMostRecentSubmissionForGroup(learningObjectId, pathFor.group); + } else if (pathFor.student) { + lastSubmission = await submissionRepo.findMostRecentSubmissionForStudent(learningObjectId, pathFor.student); + } else { + throw new Error("The path must either be created for a certain group or for a certain student!"); + } + return lastSubmission; +} + +function transitionPossible(transition: LearningPathTransition, submitted: object | null): boolean { + if (transition.condition === "true" || !transition.condition) { + return true; // If the transition is unconditional, we can go on. + } + if (submitted === null) { + return false; // If the transition is not unconditional and there was no submission, the transition is not possible. + } + return JSONPath({path: transition.condition, json: submitted}).length === 0; +} + +/** + * Service to create individual trajectories from learning paths based on the submissions of the student or group. + */ +const learningPathPersonalizingService = { + async calculatePersonalizedTrajectory(nodes: LearningPathNode[], pathFor: {student?: Student, group?: Group}): Promise { + let trajectory: LearningPathNode[] = []; + + // Always start with the start node. + let currentNode = nodes.filter(it => it.startNode)[0]; + trajectory.push(currentNode); + + while (true) { + // At every node, calculate all the possible next transitions. + let lastSubmission = await getLastRelevantSubmission(currentNode, pathFor); + let submitted = lastSubmission === null ? null : JSON.parse(lastSubmission.content); + let possibleTransitions = currentNode.transitions + .filter(it => transitionPossible(it, submitted)); + + if (possibleTransitions.length === 0) { // If there are none, the trajectory has ended. + return trajectory; + } else { // Otherwise, take the first possible transition. + currentNode = possibleTransitions[0].node; + trajectory.push(currentNode); + } + } + } }; export default learningPathPersonalizingService; diff --git a/package-lock.json b/package-lock.json index 914e100b..c8d5c035 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,17 +36,16 @@ "@types/js-yaml": "^4.0.9", "axios": "^1.8.2", "cors": "^2.8.5", - "@mikro-orm/sqlite": "6.4.6", - "@types/js-yaml": "^4.0.9", "dotenv": "^16.4.7", "express": "^5.0.1", + "express-jwt": "^8.5.1", "gift-pegjs": "^1.0.2", "isomorphic-dompurify": "^2.22.0", - "express-jwt": "^8.5.1", "js-yaml": "^4.1.0", - "marked": "^15.0.7", + "jsonpath-plus": "^10.3.0", "jwks-rsa": "^3.1.0", "loki-logger-ts": "^1.0.2", + "marked": "^15.0.7", "response-time": "^2.3.3", "uuid": "^11.1.0", "winston": "^3.17.0", @@ -155,7 +154,6 @@ }, "node_modules/@asamuzakjp/css-color": { "version": "2.8.3", - "dev": true, "license": "MIT", "dependencies": { "@csstools/css-calc": "^2.1.1", @@ -623,7 +621,6 @@ }, "node_modules/@csstools/color-helpers": { "version": "5.0.1", - "dev": true, "funding": [ { "type": "github", @@ -641,7 +638,6 @@ }, "node_modules/@csstools/css-calc": { "version": "2.1.1", - "dev": true, "funding": [ { "type": "github", @@ -663,7 +659,6 @@ }, "node_modules/@csstools/css-color-parser": { "version": "3.0.7", - "dev": true, "funding": [ { "type": "github", @@ -689,7 +684,6 @@ }, "node_modules/@csstools/css-parser-algorithms": { "version": "3.0.4", - "dev": true, "funding": [ { "type": "github", @@ -710,7 +704,6 @@ }, "node_modules/@csstools/css-tokenizer": { "version": "3.0.3", - "dev": true, "funding": [ { "type": "github", @@ -969,44 +962,6 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@intlify/core-base": { - "version": "10.0.5", - "license": "MIT", - "dependencies": { - "@intlify/message-compiler": "10.0.5", - "@intlify/shared": "10.0.5" - }, - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/kazupon" - } - }, - "node_modules/@intlify/message-compiler": { - "version": "10.0.5", - "license": "MIT", - "dependencies": { - "@intlify/shared": "10.0.5", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/kazupon" - } - }, - "node_modules/@intlify/shared": { - "version": "10.0.5", - "license": "MIT", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/kazupon" - } - }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "dev": true, @@ -1084,6 +1039,30 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@jsep-plugin/assignment": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", + "integrity": "sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==", + "license": "MIT", + "engines": { + "node": ">= 10.16.0" + }, + "peerDependencies": { + "jsep": "^0.4.0||^1.0.0" + } + }, + "node_modules/@jsep-plugin/regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@jsep-plugin/regex/-/regex-1.0.4.tgz", + "integrity": "sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==", + "license": "MIT", + "engines": { + "node": ">= 10.16.0" + }, + "peerDependencies": { + "jsep": "^0.4.0||^1.0.0" + } + }, "node_modules/@mikro-orm/cli": { "version": "6.4.6", "dev": true, @@ -1721,7 +1700,6 @@ }, "node_modules/@types/http-errors": { "version": "2.0.4", - "dev": true, "license": "MIT" }, "node_modules/@types/js-yaml": { @@ -1755,7 +1733,6 @@ }, "node_modules/@types/mime": { "version": "1.3.5", - "dev": true, "license": "MIT" }, "node_modules/@types/ms": { @@ -1766,7 +1743,6 @@ }, "node_modules/@types/node": { "version": "22.13.4", - "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.20.0" @@ -1774,12 +1750,10 @@ }, "node_modules/@types/qs": { "version": "6.9.18", - "dev": true, "license": "MIT" }, "node_modules/@types/range-parser": { "version": "1.2.7", - "dev": true, "license": "MIT" }, "node_modules/@types/response-time": { @@ -1795,7 +1769,6 @@ }, "node_modules/@types/send": { "version": "0.17.4", - "dev": true, "license": "MIT", "dependencies": { "@types/mime": "^1", @@ -1804,7 +1777,6 @@ }, "node_modules/@types/serve-static": { "version": "1.15.7", - "dev": true, "license": "MIT", "dependencies": { "@types/http-errors": "*", @@ -2498,7 +2470,6 @@ }, "node_modules/agent-base": { "version": "7.1.3", - "dev": true, "license": "MIT", "engines": { "node": ">= 14" @@ -2636,7 +2607,6 @@ "version": "1.8.2", "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.2.tgz", "integrity": "sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg==", - "version": "1.8.1", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -3135,7 +3105,6 @@ }, "node_modules/color-name": { "version": "1.1.4", - "dev": true, "license": "MIT" }, "node_modules/color-string": { @@ -3315,7 +3284,6 @@ }, "node_modules/cssstyle": { "version": "4.2.1", - "dev": true, "license": "MIT", "dependencies": { "@asamuzakjp/css-color": "^2.8.2", @@ -3331,7 +3299,6 @@ }, "node_modules/data-urls": { "version": "5.0.0", - "dev": true, "license": "MIT", "dependencies": { "whatwg-mimetype": "^4.0.0", @@ -3367,7 +3334,6 @@ }, "node_modules/decimal.js": { "version": "10.5.0", - "dev": true, "license": "MIT" }, "node_modules/decompress-response": { @@ -4804,7 +4770,6 @@ }, "node_modules/html-encoding-sniffer": { "version": "4.0.0", - "dev": true, "license": "MIT", "dependencies": { "whatwg-encoding": "^3.1.1" @@ -4845,7 +4810,6 @@ }, "node_modules/http-proxy-agent": { "version": "7.0.2", - "dev": true, "license": "MIT", "dependencies": { "agent-base": "^7.1.0", @@ -4857,7 +4821,6 @@ }, "node_modules/https-proxy-agent": { "version": "7.0.6", - "dev": true, "license": "MIT", "dependencies": { "agent-base": "^7.1.2", @@ -5097,7 +5060,6 @@ }, "node_modules/is-potential-custom-element-name": { "version": "1.0.1", - "dev": true, "license": "MIT" }, "node_modules/is-promise": { @@ -5250,7 +5212,6 @@ }, "node_modules/jsdom": { "version": "26.0.0", - "dev": true, "license": "MIT", "dependencies": { "cssstyle": "^4.2.1", @@ -5289,12 +5250,20 @@ }, "node_modules/jsdom/node_modules/xml-name-validator": { "version": "5.0.0", - "dev": true, "license": "Apache-2.0", "engines": { "node": ">=18" } }, + "node_modules/jsep": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz", + "integrity": "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==", + "license": "MIT", + "engines": { + "node": ">= 10.16.0" + } + }, "node_modules/jsesc": { "version": "3.1.0", "dev": true, @@ -5350,6 +5319,24 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonpath-plus": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-10.3.0.tgz", + "integrity": "sha512-8TNmfeTCk2Le33A3vRRwtuworG/L5RrgMvdjhKZxvyShO+mBu2fP50OWUjRLNtvw344DdDarFh9buFAZs5ujeA==", + "license": "MIT", + "dependencies": { + "@jsep-plugin/assignment": "^1.3.0", + "@jsep-plugin/regex": "^1.0.4", + "jsep": "^1.4.0" + }, + "bin": { + "jsonpath": "bin/jsonpath-cli.js", + "jsonpath-plus": "bin/jsonpath-cli.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/jsonwebtoken": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", @@ -5663,7 +5650,6 @@ }, "node_modules/lru-cache": { "version": "10.4.3", - "dev": true, "license": "ISC" }, "node_modules/lru-memoizer": { @@ -6399,7 +6385,6 @@ }, "node_modules/nwsapi": { "version": "2.2.16", - "dev": true, "license": "MIT" }, "node_modules/object-assign": { @@ -6579,7 +6564,6 @@ }, "node_modules/parse5": { "version": "7.2.1", - "dev": true, "license": "MIT", "dependencies": { "entities": "^4.5.0" @@ -7049,7 +7033,6 @@ }, "node_modules/punycode": { "version": "2.3.1", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -7354,7 +7337,6 @@ }, "node_modules/rrweb-cssom": { "version": "0.8.0", - "dev": true, "license": "MIT" }, "node_modules/run-applescript": { @@ -7422,7 +7404,6 @@ }, "node_modules/saxes": { "version": "6.0.0", - "dev": true, "license": "ISC", "dependencies": { "xmlchars": "^2.2.0" @@ -8042,7 +8023,6 @@ }, "node_modules/symbol-tree": { "version": "3.2.4", - "dev": true, "license": "MIT" }, "node_modules/synckit": { @@ -8170,7 +8150,6 @@ }, "node_modules/tldts": { "version": "6.1.77", - "dev": true, "license": "MIT", "dependencies": { "tldts-core": "^6.1.77" @@ -8181,7 +8160,6 @@ }, "node_modules/tldts-core": { "version": "6.1.77", - "dev": true, "license": "MIT" }, "node_modules/to-regex-range": { @@ -8211,7 +8189,6 @@ }, "node_modules/tough-cookie": { "version": "5.1.1", - "dev": true, "license": "BSD-3-Clause", "dependencies": { "tldts": "^6.1.32" @@ -8222,7 +8199,6 @@ }, "node_modules/tr46": { "version": "5.0.0", - "dev": true, "license": "MIT", "dependencies": { "punycode": "^2.3.1" @@ -8416,7 +8392,6 @@ }, "node_modules/undici-types": { "version": "6.20.0", - "dev": true, "license": "MIT" }, "node_modules/unicorn-magic": { @@ -8913,24 +8888,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/vue-i18n": { - "version": "10.0.5", - "license": "MIT", - "dependencies": { - "@intlify/core-base": "10.0.5", - "@intlify/shared": "10.0.5", - "@vue/devtools-api": "^6.5.0" - }, - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/kazupon" - }, - "peerDependencies": { - "vue": "^3.0.0" - } - }, "node_modules/vue-router": { "version": "4.5.0", "license": "MIT", @@ -8989,7 +8946,6 @@ }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", - "dev": true, "license": "MIT", "dependencies": { "xml-name-validator": "^5.0.0" @@ -9000,7 +8956,6 @@ }, "node_modules/w3c-xmlserializer/node_modules/xml-name-validator": { "version": "5.0.0", - "dev": true, "license": "Apache-2.0", "engines": { "node": ">=18" @@ -9008,7 +8963,6 @@ }, "node_modules/webidl-conversions": { "version": "7.0.0", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -9016,7 +8970,6 @@ }, "node_modules/whatwg-encoding": { "version": "3.1.1", - "dev": true, "license": "MIT", "dependencies": { "iconv-lite": "0.6.3" @@ -9027,7 +8980,6 @@ }, "node_modules/whatwg-encoding/node_modules/iconv-lite": { "version": "0.6.3", - "dev": true, "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -9038,7 +8990,6 @@ }, "node_modules/whatwg-mimetype": { "version": "4.0.0", - "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -9046,7 +8997,6 @@ }, "node_modules/whatwg-url": { "version": "14.1.1", - "dev": true, "license": "MIT", "dependencies": { "tr46": "^5.0.0", @@ -9289,7 +9239,6 @@ }, "node_modules/ws": { "version": "8.18.0", - "dev": true, "license": "MIT", "engines": { "node": ">=10.0.0" @@ -9317,7 +9266,6 @@ }, "node_modules/xmlchars": { "version": "2.2.0", - "dev": true, "license": "MIT" }, "node_modules/xtend": { From aa1a85e64eab8be0ba7c0d7878db8c9e29a08d51 Mon Sep 17 00:00:00 2001 From: Lint Action Date: Tue, 11 Mar 2025 03:09:08 +0000 Subject: [PATCH 60/96] style: fix linting issues met ESLint --- backend/src/controllers/learning-paths.ts | 8 ++--- .../learning-objects/attachment-service.ts | 4 +-- .../dwengo-api-learning-object-provider.ts | 18 +++------- .../learning-object-service.ts | 4 +-- .../processing/extern/extern-processor.ts | 2 +- .../processing/gift/gift-processor.ts | 4 +-- .../multiple-choice-question-renderer.ts | 2 +- .../processing/image/block-image-processor.ts | 2 +- .../markdown/dwengo-marked-renderer.ts | 34 +++++++++---------- .../processing/markdown/markdown-processor.ts | 6 ++-- .../processing/processing-service.ts | 2 +- .../database-learning-path-provider.ts | 4 +-- .../learning-path-personalizing-service.ts | 12 +++---- backend/src/util/async.ts | 2 +- .../content/attachment-repository.test.ts | 6 ++-- .../database-learning-object-provider.test.ts | 4 +-- .../learning-object-service.test.ts | 2 +- .../database-learning-path-provider.test.ts | 6 ++-- .../learning-path-service.test.ts | 8 ++--- ...xample-learning-object-with-attachments.ts | 4 +-- .../pn-werkingnotebooks-example.ts | 12 +++---- .../learning-paths/learning-path-utils.ts | 4 +-- .../learning-paths/pn-werking-example.ts | 2 +- backend/tests/test-utils/expectations.ts | 14 ++++---- 24 files changed, 76 insertions(+), 90 deletions(-) diff --git a/backend/src/controllers/learning-paths.ts b/backend/src/controllers/learning-paths.ts index cb5ae07e..6fadb10d 100644 --- a/backend/src/controllers/learning-paths.ts +++ b/backend/src/controllers/learning-paths.ts @@ -23,9 +23,7 @@ export async function getLearningPaths( ? hruids.map(String) : [String(hruids)]; } else if (themeKey) { - const theme = themes.find((t) => { - return t.title === themeKey; - }); + const theme = themes.find((t) => t.title === themeKey); if (theme) { hruidList = theme.hruids; } else { @@ -39,9 +37,7 @@ export async function getLearningPaths( res.json(searchResults); return; } else { - hruidList = themes.flatMap((theme) => { - return theme.hruids; - }); + hruidList = themes.flatMap((theme) => theme.hruids); } const learningPaths = await learningPathService.fetchLearningPaths( diff --git a/backend/src/services/learning-objects/attachment-service.ts b/backend/src/services/learning-objects/attachment-service.ts index 2285ddfe..fb2b2dd2 100644 --- a/backend/src/services/learning-objects/attachment-service.ts +++ b/backend/src/services/learning-objects/attachment-service.ts @@ -12,9 +12,9 @@ const attachmentService = { language: learningObjectId.language, version: learningObjectId.version, }, attachmentName); - } else { + } return attachmentRepo.findByMostRecentVersionOfLearningObjectAndName(learningObjectId.hruid, learningObjectId.language, attachmentName); - } + } } diff --git a/backend/src/services/learning-objects/dwengo-api-learning-object-provider.ts b/backend/src/services/learning-objects/dwengo-api-learning-object-provider.ts index 628f616c..ab914be4 100644 --- a/backend/src/services/learning-objects/dwengo-api-learning-object-provider.ts +++ b/backend/src/services/learning-objects/dwengo-api-learning-object-provider.ts @@ -68,23 +68,15 @@ async function fetchLearningObjects( const nodes: LearningObjectNode[] = learningPathResponse.data[0].nodes; if (!full) { - return nodes.map((node) => { - return node.learningobject_hruid; - }); + return nodes.map((node) => node.learningobject_hruid); } return await Promise.all( - nodes.map(async (node) => { - return dwengoApiLearningObjectProvider.getLearningObjectById({ + nodes.map(async (node) => dwengoApiLearningObjectProvider.getLearningObjectById({ hruid: node.learningobject_hruid, language: learningPathId.language - }); - }) - ).then((objects) => { - return objects.filter((obj): obj is FilteredLearningObject => { - return obj !== null; - }); - }); + })) + ).then((objects) => objects.filter((obj): obj is FilteredLearningObject => obj !== null)); } catch (error) { console.error('❌ Error fetching learning objects:', error); return []; @@ -98,7 +90,7 @@ const dwengoApiLearningObjectProvider: LearningObjectProvider = { async getLearningObjectById( id: LearningObjectIdentifier ): Promise { - let metadataUrl = `${DWENGO_API_BASE}/learningObject/getMetadata`; + const metadataUrl = `${DWENGO_API_BASE}/learningObject/getMetadata`; const metadata = await fetchWithLogging( metadataUrl, `Metadata for Learning Object HRUID "${id.hruid}" (language ${id.language})`, diff --git a/backend/src/services/learning-objects/learning-object-service.ts b/backend/src/services/learning-objects/learning-object-service.ts index 51152ec8..9dd327f0 100644 --- a/backend/src/services/learning-objects/learning-object-service.ts +++ b/backend/src/services/learning-objects/learning-object-service.ts @@ -11,9 +11,9 @@ import databaseLearningObjectProvider from "./database-learning-object-provider" function getProvider(id: LearningObjectIdentifier): LearningObjectProvider { if (id.hruid.startsWith(getEnvVar(EnvVars.UserContentPrefix))) { return databaseLearningObjectProvider; - } else { + } return dwengoApiLearningObjectProvider; - } + } /** diff --git a/backend/src/services/learning-objects/processing/extern/extern-processor.ts b/backend/src/services/learning-objects/processing/extern/extern-processor.ts index aff26d45..68ec9899 100644 --- a/backend/src/services/learning-objects/processing/extern/extern-processor.ts +++ b/backend/src/services/learning-objects/processing/extern/extern-processor.ts @@ -22,7 +22,7 @@ class ExternProcessor extends StringProcessor { // If a seperate youtube-processor would be added, this code would need to move to that processor // Converts youtube urls to youtube-embed urls - let match = /(.*youtube.com\/)watch\?v=(.*)/.exec(externURL) + const match = /(.*youtube.com\/)watch\?v=(.*)/.exec(externURL) if (match) { externURL = match[1] + "embed/" + match[2]; } diff --git a/backend/src/services/learning-objects/processing/gift/gift-processor.ts b/backend/src/services/learning-objects/processing/gift/gift-processor.ts index 5d20e99c..b6eba8ba 100644 --- a/backend/src/services/learning-objects/processing/gift/gift-processor.ts +++ b/backend/src/services/learning-objects/processing/gift/gift-processor.ts @@ -38,9 +38,9 @@ class GiftProcessor extends StringProcessor { let html = "
\n"; let i = 1; - for (let question of quizQuestions) { + for (const question of quizQuestions) { html += `
\n`; - html += " " + this.renderQuestion(question, i).replaceAll(/\n(.+)/g, "\n $1"); // replace for indentation. + html += " " + this.renderQuestion(question, i).replaceAll(/\n(.+)/g, "\n $1"); // Replace for indentation. html += `
\n`; i++; } diff --git a/backend/src/services/learning-objects/processing/gift/question-renderers/multiple-choice-question-renderer.ts b/backend/src/services/learning-objects/processing/gift/question-renderers/multiple-choice-question-renderer.ts index 6b6d8eea..bf1de8c4 100644 --- a/backend/src/services/learning-objects/processing/gift/question-renderers/multiple-choice-question-renderer.ts +++ b/backend/src/services/learning-objects/processing/gift/question-renderers/multiple-choice-question-renderer.ts @@ -11,7 +11,7 @@ export class MultipleChoiceQuestionRenderer extends GIFTQuestionRenderer${question.stem.text}

\n`; } let i = 0; - for (let choice of question.choices) { + for (const choice of question.choices) { renderedHtml += `
\n`; renderedHtml += ` \n`; renderedHtml += ` \n`; diff --git a/backend/src/services/learning-objects/processing/image/block-image-processor.ts b/backend/src/services/learning-objects/processing/image/block-image-processor.ts index cb488e92..7d578e1c 100644 --- a/backend/src/services/learning-objects/processing/image/block-image-processor.ts +++ b/backend/src/services/learning-objects/processing/image/block-image-processor.ts @@ -11,7 +11,7 @@ class BlockImageProcessor extends InlineImageProcessor { } override renderFn(imageUrl: string){ - let inlineHtml = super.render(imageUrl); + const inlineHtml = super.render(imageUrl); return DOMPurify.sanitize(`
${inlineHtml}
`); } } diff --git a/backend/src/services/learning-objects/processing/markdown/dwengo-marked-renderer.ts b/backend/src/services/learning-objects/processing/markdown/dwengo-marked-renderer.ts index 04b15b94..d5324754 100644 --- a/backend/src/services/learning-objects/processing/markdown/dwengo-marked-renderer.ts +++ b/backend/src/services/learning-objects/processing/markdown/dwengo-marked-renderer.ts @@ -56,53 +56,53 @@ function extractLearningObjectIdFromHref(href: string): LearningObjectIdentifier }, // When the syntax for a link is used => [text](href "title") - // render a custom link when the prefix for a learning object is used. + // Render a custom link when the prefix for a learning object is used. link(link: Link): string { const href = link.href; const title = link.title || ""; const text = marked.parseInline(link.text); // There could for example be an image in the link. if (href.startsWith(prefixes.learningObject)) { - // link to learning-object + // Link to learning-object const learningObjectId = extractLearningObjectIdFromHref(href); return `${text}`; - } else { - // any other link + } + // Any other link if (!isValidHttpUrl(href)) { throw new ProcessingError("Link is not a valid HTTP URL!"); } // return `${text}`; - } + }, // When the syntax for an image is used => ![text](href "title") - // render a learning object, pdf, audio or video if a prefix is used. + // Render a learning object, pdf, audio or video if a prefix is used. image(img: Image): string { const href = img.href; if (href.startsWith(prefixes.learningObject)) { - // embedded learning-object + // Embedded learning-object const learningObjectId = extractLearningObjectIdFromHref(href); return ` `; // Placeholder for the learning object since we cannot fetch its HTML here (this has to be a sync function!) } else if (href.startsWith(prefixes.pdf)) { - // embedded pdf - let proc = new PdfProcessor(); + // Embedded pdf + const proc = new PdfProcessor(); return proc.render(href.split(/\/(.+)/, 2)[1]); } else if (href.startsWith(prefixes.audio)) { - // embedded audio - let proc = new AudioProcessor(); + // Embedded audio + const proc = new AudioProcessor(); return proc.render(href.split(/\/(.+)/, 2)[1]); } else if (href.startsWith(prefixes.extern) || href.startsWith(prefixes.video) || href.startsWith(prefixes.notebook)) { - // embedded youtube video or notebook (or other extern content) - let proc = new ExternProcessor(); + // Embedded youtube video or notebook (or other extern content) + const proc = new ExternProcessor(); return proc.render(href.split(/\/(.+)/, 2)[1]); - } else { - // embedded image - let proc = new InlineImageProcessor(); + } + // Embedded image + const proc = new InlineImageProcessor(); return proc.render(href) - } + }, } diff --git a/backend/src/services/learning-objects/processing/markdown/markdown-processor.ts b/backend/src/services/learning-objects/processing/markdown/markdown-processor.ts index c20f08a3..58824c13 100644 --- a/backend/src/services/learning-objects/processing/markdown/markdown-processor.ts +++ b/backend/src/services/learning-objects/processing/markdown/markdown-processor.ts @@ -27,7 +27,7 @@ class MarkdownProcessor extends StringProcessor { } replaceLinks(html: string) { - let proc = new InlineImageProcessor(); + const proc = new InlineImageProcessor(); html = html.replace(//g, ( match: string, src: string, @@ -35,9 +35,7 @@ class MarkdownProcessor extends StringProcessor { altText: string, title: string, titleText: string - ) => { - return proc.render(src); - }); + ) => proc.render(src)); return html; } } diff --git a/backend/src/services/learning-objects/processing/processing-service.ts b/backend/src/services/learning-objects/processing/processing-service.ts index a9885718..c682de32 100644 --- a/backend/src/services/learning-objects/processing/processing-service.ts +++ b/backend/src/services/learning-objects/processing/processing-service.ts @@ -54,7 +54,7 @@ class ProcessingService { learningObject: LearningObject, fetchEmbeddedLearningObjects?: (loId: LearningObjectIdentifier) => Promise ): Promise { - let html = this.processors.get(learningObject.contentType)!.renderLearningObject(learningObject); + const html = this.processors.get(learningObject.contentType)!.renderLearningObject(learningObject); if (fetchEmbeddedLearningObjects) { // Replace all embedded learning objects. return replaceAsync( diff --git a/backend/src/services/learning-paths/database-learning-path-provider.ts b/backend/src/services/learning-paths/database-learning-path-provider.ts index b9fa9ff6..60edb98b 100644 --- a/backend/src/services/learning-paths/database-learning-path-provider.ts +++ b/backend/src/services/learning-paths/database-learning-path-provider.ts @@ -22,7 +22,7 @@ import {LearningPathTransition} from "../../entities/content/learning-path-trans */ async function getLearningObjectsForNodes(nodes: LearningPathNode[]): Promise> { // Fetching the corresponding learning object for each of the nodes and creating a map that maps each node to - // its corresponding learning object. + // Its corresponding learning object. const nullableNodesToLearningObjects = new Map( await Promise.all( nodes.map(node => @@ -58,7 +58,7 @@ async function convertLearningPath(learningPath: LearningPathEntity, order: numb const image = learningPath.image ? learningPath.image.toString("base64") : undefined; return { - _id: `${learningPath.hruid}/${learningPath.language}`, // for backwards compatibility with the original Dwengo API. + _id: `${learningPath.hruid}/${learningPath.language}`, // For backwards compatibility with the original Dwengo API. __order: order, hruid: learningPath.hruid, language: learningPath.language, diff --git a/backend/src/services/learning-paths/learning-path-personalizing-service.ts b/backend/src/services/learning-paths/learning-path-personalizing-service.ts index b7d1fe06..6cd8f610 100644 --- a/backend/src/services/learning-paths/learning-path-personalizing-service.ts +++ b/backend/src/services/learning-paths/learning-path-personalizing-service.ts @@ -43,7 +43,7 @@ function transitionPossible(transition: LearningPathTransition, submitted: objec */ const learningPathPersonalizingService = { async calculatePersonalizedTrajectory(nodes: LearningPathNode[], pathFor: {student?: Student, group?: Group}): Promise { - let trajectory: LearningPathNode[] = []; + const trajectory: LearningPathNode[] = []; // Always start with the start node. let currentNode = nodes.filter(it => it.startNode)[0]; @@ -51,17 +51,17 @@ const learningPathPersonalizingService = { while (true) { // At every node, calculate all the possible next transitions. - let lastSubmission = await getLastRelevantSubmission(currentNode, pathFor); - let submitted = lastSubmission === null ? null : JSON.parse(lastSubmission.content); - let possibleTransitions = currentNode.transitions + const lastSubmission = await getLastRelevantSubmission(currentNode, pathFor); + const submitted = lastSubmission === null ? null : JSON.parse(lastSubmission.content); + const possibleTransitions = currentNode.transitions .filter(it => transitionPossible(it, submitted)); if (possibleTransitions.length === 0) { // If there are none, the trajectory has ended. return trajectory; - } else { // Otherwise, take the first possible transition. + } // Otherwise, take the first possible transition. currentNode = possibleTransitions[0].node; trajectory.push(currentNode); - } + } } }; diff --git a/backend/src/util/async.ts b/backend/src/util/async.ts index f37f53a0..874c0b8e 100644 --- a/backend/src/util/async.ts +++ b/backend/src/util/async.ts @@ -19,5 +19,5 @@ export async function replaceAsync(str: string, regex: RegExp, replacementFn: (m const replacements: string[] = (await Promise.all(promises)); // Second run through matches: Replace them by their previously computed replacements. - return str.replace(regex, () => replacements.pop()!!); + return str.replace(regex, () => replacements.pop()!); } diff --git a/backend/tests/data/content/attachment-repository.test.ts b/backend/tests/data/content/attachment-repository.test.ts index 3f573cdb..e6c4e44a 100644 --- a/backend/tests/data/content/attachment-repository.test.ts +++ b/backend/tests/data/content/attachment-repository.test.ts @@ -11,10 +11,10 @@ import {LearningObjectIdentifier} from "../../../src/entities/content/learning-o const NEWER_TEST_SUFFIX = "nEweR"; function createTestLearningObjects(learningObjectRepo: LearningObjectRepository): {older: LearningObject, newer: LearningObject} { - let olderExample = example.createLearningObject(); + const olderExample = example.createLearningObject(); learningObjectRepo.save(olderExample); - let newerExample = example.createLearningObject(); + const newerExample = example.createLearningObject(); newerExample.title = "Newer example"; newerExample.version = 100; @@ -40,7 +40,7 @@ describe("AttachmentRepository", () => { .values(example.createAttachment) .map(fn => fn(exampleLearningObjects.older)); - for (let attachment of attachmentsOlderLearningObject) { + for (const attachment of attachmentsOlderLearningObject) { attachmentRepo.save(attachment); } }); diff --git a/backend/tests/services/learning-objects/database-learning-object-provider.test.ts b/backend/tests/services/learning-objects/database-learning-object-provider.test.ts index 691b29ba..e3960f64 100644 --- a/backend/tests/services/learning-objects/database-learning-object-provider.test.ts +++ b/backend/tests/services/learning-objects/database-learning-object-provider.test.ts @@ -12,8 +12,8 @@ import {FilteredLearningObject} from "../../../src/interfaces/learning-content"; import {Language} from "../../../src/entities/content/language"; async function initExampleData(): Promise { - let learningObjectRepo = getLearningObjectRepository(); - let exampleLearningObject = createExampleLearningObjectWithAttachments(example); + const learningObjectRepo = getLearningObjectRepository(); + const exampleLearningObject = createExampleLearningObjectWithAttachments(example); await learningObjectRepo.insert(exampleLearningObject); return exampleLearningObject; } diff --git a/backend/tests/services/learning-objects/learning-object-service.test.ts b/backend/tests/services/learning-objects/learning-object-service.test.ts index 970e4675..2a68d797 100644 --- a/backend/tests/services/learning-objects/learning-object-service.test.ts +++ b/backend/tests/services/learning-objects/learning-object-service.test.ts @@ -18,7 +18,7 @@ const DWENGO_TEST_LEARNING_OBJECT_ID: LearningObjectIdentifier = { async function initExampleData(): Promise { const learningObjectRepo = getLearningObjectRepository(); - let learningObject = learningObjectExample.createLearningObject(); + const learningObject = learningObjectExample.createLearningObject(); learningObject.title = TEST_LEARNING_OBJECT_TITLE await learningObjectRepo.save(learningObject); return learningObject; diff --git a/backend/tests/services/learning-path/database-learning-path-provider.test.ts b/backend/tests/services/learning-path/database-learning-path-provider.test.ts index 0d46c1e0..8dd3d07b 100644 --- a/backend/tests/services/learning-path/database-learning-path-provider.test.ts +++ b/backend/tests/services/learning-path/database-learning-path-provider.test.ts @@ -14,8 +14,8 @@ import {Language} from "../../../src/entities/content/language"; async function initExampleData(): Promise<{ learningObject: LearningObject, learningPath: LearningPath }> { const learningObjectRepo = getLearningObjectRepository(); const learningPathRepo = getLearningPathRepository(); - let learningObject = learningObjectExample.createLearningObject(); - let learningPath = learningPathExample.createLearningPath(); + const learningObject = learningObjectExample.createLearningObject(); + const learningPath = learningPathExample.createLearningPath(); await learningObjectRepo.save(learningObject); await learningPathRepo.save(learningPath); return { learningObject, learningPath }; @@ -55,7 +55,7 @@ describe("DatabaseLearningPathProvider", () => { it("returns a non-successful response if a non-existing learning path is queried", async () => { const result = await databaseLearningPathProvider.fetchLearningPaths( [example.learningPath.hruid], - Language.Abkhazian, // wrong language + Language.Abkhazian, // Wrong language "the source" ); diff --git a/backend/tests/services/learning-path/learning-path-service.test.ts b/backend/tests/services/learning-path/learning-path-service.test.ts index 83db1bef..b64d527a 100644 --- a/backend/tests/services/learning-path/learning-path-service.test.ts +++ b/backend/tests/services/learning-path/learning-path-service.test.ts @@ -11,8 +11,8 @@ import learningPathService from "../../../src/services/learning-paths/learning-p async function initExampleData(): Promise<{ learningObject: LearningObject, learningPath: LearningPath }> { const learningObjectRepo = getLearningObjectRepository(); const learningPathRepo = getLearningPathRepository(); - let learningObject = learningObjectExample.createLearningObject(); - let learningPath = learningPathExample.createLearningPath(); + const learningObject = learningObjectExample.createLearningObject(); + const learningPath = learningPathExample.createLearningPath(); await learningObjectRepo.save(learningObject); await learningPathRepo.save(learningPath); return { learningObject, learningPath }; @@ -76,7 +76,7 @@ describe("LearningPathService", () => { ).length ).toBe(1); - // but should not only find that one. + // But should not only find that one. expect(result.length).not.toBeLessThan(2); }); it("should still return results from the Dwengo API even though there are no matches in the database", async () => { @@ -88,7 +88,7 @@ describe("LearningPathService", () => { // Should find something... expect(result.length).not.toBe(0); - // but not the example learning path. + // But not the example learning path. expect( result.filter(it => it.hruid === example.learningPath.hruid && it.title === example.learningPath.title diff --git a/backend/tests/test-assets/learning-objects/create-example-learning-object-with-attachments.ts b/backend/tests/test-assets/learning-objects/create-example-learning-object-with-attachments.ts index 65d2b62e..c0177fbf 100644 --- a/backend/tests/test-assets/learning-objects/create-example-learning-object-with-attachments.ts +++ b/backend/tests/test-assets/learning-objects/create-example-learning-object-with-attachments.ts @@ -2,8 +2,8 @@ import {LearningObjectExample} from "./learning-object-example"; import {LearningObject} from "../../../src/entities/content/learning-object.entity"; export function createExampleLearningObjectWithAttachments(example: LearningObjectExample): LearningObject { - let learningObject = example.createLearningObject(); - for (let creationFn of Object.values(example.createAttachment)) { + const learningObject = example.createLearningObject(); + for (const creationFn of Object.values(example.createAttachment)) { learningObject.attachments.push(creationFn(learningObject)); } return learningObject; diff --git a/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example.ts b/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example.ts index 09cab0f9..9a7f340c 100644 --- a/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example.ts +++ b/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example.ts @@ -10,7 +10,7 @@ const ASSETS_PREFIX = "learning-objects/pn-werkingnotebooks/"; const example: LearningObjectExample = { createLearningObject: ()=> { - let learningObject = new LearningObject(); + const learningObject = new LearningObject(); learningObject.hruid = `${getEnvVar(EnvVars.UserContentPrefix)}pn_werkingnotebooks`; learningObject.version = 3; learningObject.language = Language.Dutch; @@ -18,11 +18,11 @@ const example: LearningObjectExample = { learningObject.description = "Leren werken met notebooks"; learningObject.keywords = ["Python", "KIKS", "Wiskunde", "STEM", "AI"] - let educationalGoal1 = new EducationalGoal(); + const educationalGoal1 = new EducationalGoal(); educationalGoal1.source = "Source"; educationalGoal1.id = "id"; - let educationalGoal2 = new EducationalGoal(); + const educationalGoal2 = new EducationalGoal(); educationalGoal2.source = "Source2"; educationalGoal2.id = "id2"; @@ -39,7 +39,7 @@ const example: LearningObjectExample = { learningObject.license = "dwengo"; learningObject.estimatedTime = 10; - let returnValue = new ReturnValue(); + const returnValue = new ReturnValue(); returnValue.callbackUrl = "callback_url_example"; returnValue.callbackSchema = '{"att": "test", "att2": "test2"}'; @@ -51,7 +51,7 @@ const example: LearningObjectExample = { }, createAttachment: { dwengoLogo: (learningObject) => { - let att = new Attachment(); + const att = new Attachment(); att.learningObject = learningObject; att.name = "dwengo.png"; att.mimeType = "image/png"; @@ -59,7 +59,7 @@ const example: LearningObjectExample = { return att; }, knop: (learningObject) => { - let att = new Attachment(); + const att = new Attachment(); att.learningObject = learningObject; att.name = "Knop.png"; att.mimeType = "image/png"; diff --git a/backend/tests/test-assets/learning-paths/learning-path-utils.ts b/backend/tests/test-assets/learning-paths/learning-path-utils.ts index 68c49412..0a09145f 100644 --- a/backend/tests/test-assets/learning-paths/learning-path-utils.ts +++ b/backend/tests/test-assets/learning-paths/learning-path-utils.ts @@ -4,7 +4,7 @@ import {LearningPathNode} from "../../../src/entities/content/learning-path-node import {LearningPath} from "../../../src/entities/content/learning-path.entity"; export function createLearningPathTransition(node: LearningPathNode, transitionNumber: number, condition: string | null, to: LearningPathNode) { - let trans = new LearningPathTransition(); + const trans = new LearningPathTransition(); trans.node = node; trans.transitionNumber = transitionNumber; trans.condition = condition || "true"; @@ -20,7 +20,7 @@ export function createLearningPathNode( language: Language, startNode: boolean ) { - let node = new LearningPathNode(); + const node = new LearningPathNode(); node.learningPath = learningPath; node.nodeNumber = nodeNumber; node.learningObjectHruid = learningObjectHruid; diff --git a/backend/tests/test-assets/learning-paths/pn-werking-example.ts b/backend/tests/test-assets/learning-paths/pn-werking-example.ts index a96de552..40a07587 100644 --- a/backend/tests/test-assets/learning-paths/pn-werking-example.ts +++ b/backend/tests/test-assets/learning-paths/pn-werking-example.ts @@ -5,7 +5,7 @@ import {createLearningPathNode, createLearningPathTransition} from "./learning-p import {LearningPathNode} from "../../../src/entities/content/learning-path-node.entity"; function createNodes(learningPath: LearningPath): LearningPathNode[] { - let nodes = [ + const nodes = [ createLearningPathNode(learningPath, 0, "u_pn_werkingnotebooks", 3, Language.Dutch, true), createLearningPathNode(learningPath, 1, "pn_werkingnotebooks2", 3, Language.Dutch, false), createLearningPathNode(learningPath, 2, "pn_werkingnotebooks3", 3, Language.Dutch, false), diff --git a/backend/tests/test-utils/expectations.ts b/backend/tests/test-utils/expectations.ts index 19c2408d..b7e9523b 100644 --- a/backend/tests/test-utils/expectations.ts +++ b/backend/tests/test-utils/expectations.ts @@ -22,7 +22,7 @@ export function expectToBeCorrectEntity( if (!expected.name) { expected.name = "expected"; } - for (let property in expected.entity) { + for (const property in expected.entity) { if ( property !in IGNORE_PROPERTIES && expected.entity[property] !== undefined // If we don't expect a certain value for a property, we assume it can be filled in by the database however it wants. @@ -34,10 +34,10 @@ export function expectToBeCorrectEntity( }); } if (typeof expected.entity[property] === "boolean") { // Sometimes, booleans get represented by numbers 0 and 1 in the objects actual from the database. - if (!!expected.entity[property] !== !!actual.entity[property]) { + if (Boolean(expected.entity[property]) !== Boolean(actual.entity[property])) { throw new AssertionError({ message: `${property} was ${expected.entity[property]} in ${expected.name}, - but ${actual.entity[property]} (${!!expected.entity[property]}) in ${actual.name}` + but ${actual.entity[property]} (${Boolean(expected.entity[property])}) in ${actual.name}` }); } } else if (typeof expected.entity[property] !== typeof actual.entity[property]) { @@ -78,7 +78,7 @@ export function expectToBeCorrectFilteredLearningObject(filtered: FilteredLearni expect(filtered.key).toEqual(original.hruid); expect(filtered.targetAges).toEqual(original.targetAges); expect(filtered.title).toEqual(original.title); - expect(!!filtered.teacherExclusive).toEqual(original.teacherExclusive) // !!: Workaround: MikroORM with SQLite returns 0 and 1 instead of booleans. + expect(Boolean(filtered.teacherExclusive)).toEqual(original.teacherExclusive) // !!: Workaround: MikroORM with SQLite returns 0 and 1 instead of booleans. expect(filtered.skosConcepts).toEqual(original.skosConcepts); expect(filtered.estimatedTime).toEqual(original.estimatedTime); expect(filtered.educationalGoals).toEqual(original.educationalGoals); @@ -123,21 +123,21 @@ export function expectToBeCorrectLearningPath( expect(learningPath.num_nodes).toEqual(expectedEntity.nodes.length); expect(learningPath.image || null).toEqual(expectedEntity.image); - let expectedLearningPathNodes = new Map( + const expectedLearningPathNodes = new Map( expectedEntity.nodes.map(node => [ {learningObjectHruid: node.learningObjectHruid, language: node.language, version: node.version}, {startNode: node.startNode, transitions: node.transitions} ]) ); - for (let node of learningPath.nodes) { + for (const node of learningPath.nodes) { const nodeKey = { learningObjectHruid: node.learningobject_hruid, language: node.language, version: node.version }; expect(expectedLearningPathNodes.keys()).toContainEqual(nodeKey); - let expectedNode = [...expectedLearningPathNodes.entries()] + const expectedNode = [...expectedLearningPathNodes.entries()] .filter(([key, _]) => key.learningObjectHruid === nodeKey.learningObjectHruid && key.language === node.language From 2a2881ec30f84f4f3f75eda14849a133e02b7de9 Mon Sep 17 00:00:00 2001 From: Lint Action Date: Tue, 11 Mar 2025 03:09:12 +0000 Subject: [PATCH 61/96] style: fix linting issues met Prettier --- backend/src/config.ts | 2 +- backend/src/controllers/learning-objects.ts | 36 ++--- backend/src/controllers/learning-paths.ts | 24 +--- backend/src/controllers/learningPaths.ts | 1 - .../src/data/content/attachment-repository.ts | 40 +++--- .../content/learning-object-repository.ts | 16 +-- .../data/content/learning-path-repository.ts | 23 +--- backend/src/data/repositories.ts | 19 +-- .../entities/assignments/assignment.entity.ts | 4 +- .../src/entities/assignments/group.entity.ts | 4 +- .../entities/assignments/submission.entity.ts | 4 +- .../classes/class-join-request.entity.ts | 4 +- backend/src/entities/classes/class.entity.ts | 4 +- .../classes/teacher-invitation.entity.ts | 4 +- .../src/entities/content/attachment.entity.ts | 4 +- backend/src/entities/content/language.ts | 2 +- .../content/learning-object.entity.ts | 10 +- .../content/learning-path-node.entity.ts | 13 +- .../learning-path-transition.entity.ts | 6 +- .../entities/content/learning-path.entity.ts | 16 +-- .../src/entities/questions/answer.entity.ts | 4 +- .../src/entities/questions/question.entity.ts | 4 +- backend/src/entities/users/teacher.entity.ts | 4 +- backend/src/interfaces/learning-content.ts | 2 +- backend/src/routes/learning-objects.ts | 5 +- .../learning-objects/attachment-service.ts | 28 ++-- .../database-learning-object-provider.ts | 58 ++++---- .../dwengo-api-learning-object-provider.ts | 70 ++++------ .../learning-object-provider.ts | 6 +- .../learning-object-service.ts | 21 ++- .../processing/audio/audio-processor.ts | 7 +- .../processing/content-type.ts | 22 +-- .../processing/extern/extern-processor.ts | 20 +-- .../processing/gift/gift-processor.ts | 35 +++-- .../category-question-renderer.ts | 6 +- .../description-question-renderer.ts | 6 +- .../essay-question-renderer.ts | 6 +- .../gift-question-renderer.ts | 2 +- .../matching-question-renderer.ts | 6 +- .../multiple-choice-question-renderer.ts | 6 +- .../numerical-question-renderer.ts | 6 +- .../short-question-renderer.ts | 6 +- .../true-false-question-renderer.ts | 6 +- .../processing/image/block-image-processor.ts | 6 +- .../image/inline-image-processor.ts | 8 +- .../markdown/dwengo-marked-renderer.ts | 66 ++++----- .../processing/markdown/markdown-processor.ts | 28 ++-- .../processing/pdf/pdf-processor.ts | 14 +- .../processing/processing-service.ts | 36 +++-- .../learning-objects/processing/processor.ts | 6 +- .../processing/string-processor.ts | 6 +- .../processing/text/text-processor.ts | 4 +- .../database-learning-path-provider.ts | 128 ++++++++---------- .../dwengo-api-learning-path-provider.ts | 32 +---- .../learning-path-personalizing-service.ts | 45 +++--- .../learning-paths/learning-path-provider.ts | 4 +- .../learning-paths/learning-path-service.ts | 37 ++--- backend/src/util/apiHelper.ts | 6 +- backend/src/util/async.ts | 2 +- backend/src/util/envvars.ts | 6 +- backend/src/util/links.ts | 6 +- .../content/attachment-repository.test.ts | 54 ++++---- .../learning-object-repository.test.ts | 56 ++++---- .../content/learning-path-repository.test.ts | 58 ++++---- .../database-learning-object-provider.test.ts | 42 +++--- .../learning-object-service.test.ts | 75 +++++----- .../processing/processing-service.test.ts | 18 +-- .../database-learning-path-provider.test.ts | 71 +++++----- .../learning-path-service.test.ts | 97 +++++-------- ...xample-learning-object-with-attachments.ts | 4 +- .../dummy/dummy-learning-object-example.ts | 18 +-- .../learning-object-example.d.ts | 10 +- .../pn-werkingnotebooks/content.md | 9 +- .../pn-werkingnotebooks-example.ts | 60 ++++---- .../pn-werkingnotebooks/rendering.html | 51 +++++-- .../test-essay/test-essay-example.ts | 20 +-- .../test-multiple-choice/rendering.html | 4 +- .../test-multiple-choice-example.ts | 20 +-- .../learning-paths/learning-path-example.d.ts | 2 +- .../learning-paths/learning-path-utils.ts | 10 +- .../learning-paths/pn-werking-example.ts | 28 ++-- .../learning-paths/test-conditions-example.ts | 42 +++--- backend/tests/test-utils/expectations.ts | 94 ++++++------- backend/tests/test-utils/load-test-asset.ts | 4 +- 84 files changed, 846 insertions(+), 1013 deletions(-) diff --git a/backend/src/config.ts b/backend/src/config.ts index 848eb07e..df8fa133 100644 --- a/backend/src/config.ts +++ b/backend/src/config.ts @@ -1,4 +1,4 @@ -import {EnvVars, getEnvVar} from "./util/envvars"; +import { EnvVars, getEnvVar } from './util/envvars'; // API export const DWENGO_API_BASE = getEnvVar(EnvVars.LearningContentRepoApiBaseUrl); diff --git a/backend/src/controllers/learning-objects.ts b/backend/src/controllers/learning-objects.ts index ece16057..cb4862e9 100644 --- a/backend/src/controllers/learning-objects.ts +++ b/backend/src/controllers/learning-objects.ts @@ -1,38 +1,35 @@ import { Request, Response } from 'express'; import { FALLBACK_LANG } from '../config.js'; -import {FilteredLearningObject, LearningObjectIdentifier, LearningPathIdentifier} from '../interfaces/learning-content'; -import learningObjectService from "../services/learning-objects/learning-object-service"; -import {EnvVars, getEnvVar} from "../util/envvars"; -import {Language} from "../entities/content/language"; -import {BadRequestException} from "../exceptions"; -import attachmentService from "../services/learning-objects/attachment-service"; -import {NotFoundError} from "@mikro-orm/core"; +import { FilteredLearningObject, LearningObjectIdentifier, LearningPathIdentifier } from '../interfaces/learning-content'; +import learningObjectService from '../services/learning-objects/learning-object-service'; +import { EnvVars, getEnvVar } from '../util/envvars'; +import { Language } from '../entities/content/language'; +import { BadRequestException } from '../exceptions'; +import attachmentService from '../services/learning-objects/attachment-service'; +import { NotFoundError } from '@mikro-orm/core'; function getLearningObjectIdentifierFromRequest(req: Request): LearningObjectIdentifier { if (!req.params.hruid) { - throw new BadRequestException("HRUID is required."); + throw new BadRequestException('HRUID is required.'); } return { hruid: req.params.hruid as string, language: (req.query.language || getEnvVar(EnvVars.FallbackLanguage)) as Language, - version: parseInt(req.query.version as string) + version: parseInt(req.query.version as string), }; } function getLearningPathIdentifierFromRequest(req: Request): LearningPathIdentifier { if (!req.query.hruid) { - throw new BadRequestException("HRUID is required."); + throw new BadRequestException('HRUID is required.'); } return { hruid: req.params.hruid as string, - language: (req.query.language as Language) || FALLBACK_LANG - } + language: (req.query.language as Language) || FALLBACK_LANG, + }; } -export async function getAllLearningObjects( - req: Request, - res: Response -): Promise { +export async function getAllLearningObjects(req: Request, res: Response): Promise { const learningPathId = getLearningPathIdentifierFromRequest(req); const full = req.query.full; @@ -46,10 +43,7 @@ export async function getAllLearningObjects( res.json(learningObjects); } -export async function getLearningObject( - req: Request, - res: Response -): Promise { +export async function getLearningObject(req: Request, res: Response): Promise { const learningObjectId = getLearningObjectIdentifierFromRequest(req); const learningObject = await learningObjectService.getLearningObjectById(learningObjectId); @@ -71,5 +65,5 @@ export async function getAttachment(req: Request, res: Response): Promise if (!attachment) { throw new NotFoundError(`Attachment ${name} not found`); } - res.setHeader("Content-Type", attachment.mimeType).send(attachment.content) + res.setHeader('Content-Type', attachment.mimeType).send(attachment.content); } diff --git a/backend/src/controllers/learning-paths.ts b/backend/src/controllers/learning-paths.ts index 6fadb10d..8e654d02 100644 --- a/backend/src/controllers/learning-paths.ts +++ b/backend/src/controllers/learning-paths.ts @@ -1,16 +1,13 @@ import { Request, Response } from 'express'; import { themes } from '../data/themes.js'; import { FALLBACK_LANG } from '../config.js'; -import learningPathService from "../services/learning-paths/learning-path-service"; -import {NotFoundException} from "../exceptions"; +import learningPathService from '../services/learning-paths/learning-path-service'; +import { NotFoundException } from '../exceptions'; /** * Fetch learning paths based on query parameters. */ -export async function getLearningPaths( - req: Request, - res: Response -): Promise { +export async function getLearningPaths(req: Request, res: Response): Promise { const hruids = req.query.hruid; const themeKey = req.query.theme as string; const searchQuery = req.query.search as string; @@ -19,9 +16,7 @@ export async function getLearningPaths( let hruidList; if (hruids) { - hruidList = Array.isArray(hruids) - ? hruids.map(String) - : [String(hruids)]; + hruidList = Array.isArray(hruids) ? hruids.map(String) : [String(hruids)]; } else if (themeKey) { const theme = themes.find((t) => t.title === themeKey); if (theme) { @@ -30,20 +25,13 @@ export async function getLearningPaths( throw new NotFoundException(`Theme "${themeKey}" not found.`); } } else if (searchQuery) { - const searchResults = await learningPathService.searchLearningPaths( - searchQuery, - language - ); + const searchResults = await learningPathService.searchLearningPaths(searchQuery, language); res.json(searchResults); return; } else { hruidList = themes.flatMap((theme) => theme.hruids); } - const learningPaths = await learningPathService.fetchLearningPaths( - hruidList, - language, - `HRUIDs: ${hruidList.join(', ')}` - ); + const learningPaths = await learningPathService.fetchLearningPaths(hruidList, language, `HRUIDs: ${hruidList.join(', ')}`); res.json(learningPaths.data); } diff --git a/backend/src/controllers/learningPaths.ts b/backend/src/controllers/learningPaths.ts index 707334b7..50299d0f 100644 --- a/backend/src/controllers/learningPaths.ts +++ b/backend/src/controllers/learningPaths.ts @@ -1,4 +1,3 @@ - import { Request, Response } from 'express'; import { themes } from '../data/themes.js'; import { FALLBACK_LANG } from '../config.js'; diff --git a/backend/src/data/content/attachment-repository.ts b/backend/src/data/content/attachment-repository.ts index c7a53c86..95c5ab1c 100644 --- a/backend/src/data/content/attachment-repository.ts +++ b/backend/src/data/content/attachment-repository.ts @@ -1,13 +1,10 @@ import { DwengoEntityRepository } from '../dwengo-entity-repository.js'; import { Attachment } from '../../entities/content/attachment.entity.js'; -import {Language} from "../../entities/content/language"; -import {LearningObjectIdentifier} from "../../entities/content/learning-object-identifier"; +import { Language } from '../../entities/content/language'; +import { LearningObjectIdentifier } from '../../entities/content/learning-object-identifier'; export class AttachmentRepository extends DwengoEntityRepository { - public findByLearningObjectIdAndName( - learningObjectId: LearningObjectIdentifier, - name: string - ): Promise { + public findByLearningObjectIdAndName(learningObjectId: LearningObjectIdentifier, name: string): Promise { return this.findOne({ learningObject: { hruid: learningObjectId.hruid, @@ -18,24 +15,23 @@ export class AttachmentRepository extends DwengoEntityRepository { }); } - public findByMostRecentVersionOfLearningObjectAndName( - hruid: string, - language: Language, - attachmentName: string - ): Promise { - return this.findOne({ - learningObject: { - hruid: hruid, - language: language - }, - name: attachmentName - }, { - orderBy: { + public findByMostRecentVersionOfLearningObjectAndName(hruid: string, language: Language, attachmentName: string): Promise { + return this.findOne( + { learningObject: { - version: 'DESC' - } + hruid: hruid, + language: language, + }, + name: attachmentName, + }, + { + orderBy: { + learningObject: { + version: 'DESC', + }, + }, } - }); + ); } // This repository is read-only for now since creating own learning object is an extension feature. } diff --git a/backend/src/data/content/learning-object-repository.ts b/backend/src/data/content/learning-object-repository.ts index 8fdf18fe..f9b6bfcb 100644 --- a/backend/src/data/content/learning-object-repository.ts +++ b/backend/src/data/content/learning-object-repository.ts @@ -1,12 +1,10 @@ import { DwengoEntityRepository } from '../dwengo-entity-repository.js'; import { LearningObject } from '../../entities/content/learning-object.entity.js'; import { LearningObjectIdentifier } from '../../entities/content/learning-object-identifier.js'; -import {Language} from "../../entities/content/language"; +import { Language } from '../../entities/content/language'; export class LearningObjectRepository extends DwengoEntityRepository { - public findByIdentifier( - identifier: LearningObjectIdentifier - ): Promise { + public findByIdentifier(identifier: LearningObjectIdentifier): Promise { return this.findOne( { hruid: identifier.hruid, @@ -14,7 +12,7 @@ export class LearningObjectRepository extends DwengoEntityRepository { - public findByHruidAndLanguage( - hruid: string, - language: Language - ): Promise { - return this.findOne( - { hruid: hruid, language: language }, - { populate: ["nodes", "nodes.transitions"] } - ); + public findByHruidAndLanguage(hruid: string, language: Language): Promise { + return this.findOne({ hruid: hruid, language: language }, { populate: ['nodes', 'nodes.transitions'] }); } /** @@ -24,12 +18,9 @@ export class LearningPathRepository extends DwengoEntityRepository return this.findAll({ where: { language: language, - $or: [ - { title: { $like: `%${query}%`} }, - { description: { $like: `%${query}%`} } - ] + $or: [{ title: { $like: `%${query}%` } }, { description: { $like: `%${query}%` } }], }, - populate: ["nodes", "nodes.transitions"] + populate: ['nodes', 'nodes.transitions'], }); } } diff --git a/backend/src/data/repositories.ts b/backend/src/data/repositories.ts index 543de6e5..7901d928 100644 --- a/backend/src/data/repositories.ts +++ b/backend/src/data/repositories.ts @@ -28,8 +28,8 @@ import { LearningPath } from '../entities/content/learning-path.entity.js'; import { LearningPathRepository } from './content/learning-path-repository.js'; import { AttachmentRepository } from './content/attachment-repository.js'; import { Attachment } from '../entities/content/attachment.entity.js'; -import {LearningPathNode} from "../entities/content/learning-path-node.entity"; -import {LearningPathTransition} from "../entities/content/learning-path-transition.entity"; +import { LearningPathNode } from '../entities/content/learning-path-node.entity'; +import { LearningPathTransition } from '../entities/content/learning-path-transition.entity'; let entityManager: EntityManager | undefined; @@ -73,17 +73,8 @@ export const getQuestionRepository = repositoryGetter(Answer); /* Learning content */ -export const getLearningObjectRepository = repositoryGetter< - LearningObject, - LearningObjectRepository ->(LearningObject); -export const getLearningPathRepository = repositoryGetter< - LearningPath, - LearningPathRepository ->(LearningPath); +export const getLearningObjectRepository = repositoryGetter(LearningObject); +export const getLearningPathRepository = repositoryGetter(LearningPath); export const getLearningPathNodeRepository = repositoryGetter(LearningPathNode); export const getLearningPathTransitionRepository = repositoryGetter(LearningPathTransition); -export const getAttachmentRepository = repositoryGetter< - Attachment, - AttachmentRepository ->(Attachment); +export const getAttachmentRepository = repositoryGetter(Attachment); diff --git a/backend/src/entities/assignments/assignment.entity.ts b/backend/src/entities/assignments/assignment.entity.ts index 44bb6810..9e3f3b03 100644 --- a/backend/src/entities/assignments/assignment.entity.ts +++ b/backend/src/entities/assignments/assignment.entity.ts @@ -2,9 +2,9 @@ import { Entity, Enum, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro import { Class } from '../classes/class.entity.js'; import { Group } from './group.entity.js'; import { Language } from '../content/language.js'; -import {AssignmentRepository} from "../../data/assignments/assignment-repository"; +import { AssignmentRepository } from '../../data/assignments/assignment-repository'; -@Entity({repository: () => AssignmentRepository}) +@Entity({ repository: () => AssignmentRepository }) export class Assignment { @ManyToOne({ entity: () => Class, primary: true }) within!: Class; diff --git a/backend/src/entities/assignments/group.entity.ts b/backend/src/entities/assignments/group.entity.ts index 0915b31b..7e01cf62 100644 --- a/backend/src/entities/assignments/group.entity.ts +++ b/backend/src/entities/assignments/group.entity.ts @@ -1,9 +1,9 @@ import { Entity, ManyToMany, ManyToOne, PrimaryKey } from '@mikro-orm/core'; import { Assignment } from './assignment.entity.js'; import { Student } from '../users/student.entity.js'; -import {GroupRepository} from "../../data/assignments/group-repository"; +import { GroupRepository } from '../../data/assignments/group-repository'; -@Entity({repository: () => GroupRepository}) +@Entity({ repository: () => GroupRepository }) export class Group { @ManyToOne({ entity: () => Assignment, diff --git a/backend/src/entities/assignments/submission.entity.ts b/backend/src/entities/assignments/submission.entity.ts index a5c2238b..34e0c1f6 100644 --- a/backend/src/entities/assignments/submission.entity.ts +++ b/backend/src/entities/assignments/submission.entity.ts @@ -2,9 +2,9 @@ import { Student } from '../users/student.entity.js'; import { Group } from './group.entity.js'; import { Entity, Enum, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'; import { Language } from '../content/language.js'; -import {SubmissionRepository} from "../../data/assignments/submission-repository"; +import { SubmissionRepository } from '../../data/assignments/submission-repository'; -@Entity({repository: () => SubmissionRepository}) +@Entity({ repository: () => SubmissionRepository }) export class Submission { @PrimaryKey({ type: 'string' }) learningObjectHruid!: string; diff --git a/backend/src/entities/classes/class-join-request.entity.ts b/backend/src/entities/classes/class-join-request.entity.ts index 5d9ef571..01197193 100644 --- a/backend/src/entities/classes/class-join-request.entity.ts +++ b/backend/src/entities/classes/class-join-request.entity.ts @@ -1,9 +1,9 @@ import { Entity, Enum, ManyToOne } from '@mikro-orm/core'; import { Student } from '../users/student.entity.js'; import { Class } from './class.entity.js'; -import {ClassJoinRequestRepository} from "../../data/classes/class-join-request-repository"; +import { ClassJoinRequestRepository } from '../../data/classes/class-join-request-repository'; -@Entity({repository: () => ClassJoinRequestRepository}) +@Entity({ repository: () => ClassJoinRequestRepository }) export class ClassJoinRequest { @ManyToOne({ entity: () => Student, diff --git a/backend/src/entities/classes/class.entity.ts b/backend/src/entities/classes/class.entity.ts index c430c9dc..610622cc 100644 --- a/backend/src/entities/classes/class.entity.ts +++ b/backend/src/entities/classes/class.entity.ts @@ -2,9 +2,9 @@ import { Collection, Entity, ManyToMany, PrimaryKey, Property } from '@mikro-orm import { v4 } from 'uuid'; import { Teacher } from '../users/teacher.entity.js'; import { Student } from '../users/student.entity.js'; -import {ClassRepository} from "../../data/classes/class-repository"; +import { ClassRepository } from '../../data/classes/class-repository'; -@Entity({repository: () => ClassRepository}) +@Entity({ repository: () => ClassRepository }) export class Class { @PrimaryKey() classId = v4(); diff --git a/backend/src/entities/classes/teacher-invitation.entity.ts b/backend/src/entities/classes/teacher-invitation.entity.ts index 597b6a41..e8bb8d01 100644 --- a/backend/src/entities/classes/teacher-invitation.entity.ts +++ b/backend/src/entities/classes/teacher-invitation.entity.ts @@ -1,12 +1,12 @@ import { Entity, ManyToOne } from '@mikro-orm/core'; import { Teacher } from '../users/teacher.entity.js'; import { Class } from './class.entity.js'; -import {TeacherInvitationRepository} from "../../data/classes/teacher-invitation-repository"; +import { TeacherInvitationRepository } from '../../data/classes/teacher-invitation-repository'; /** * Invitation of a teacher into a class (in order to teach it). */ -@Entity({repository: () => TeacherInvitationRepository}) +@Entity({ repository: () => TeacherInvitationRepository }) export class TeacherInvitation { @ManyToOne({ entity: () => Teacher, diff --git a/backend/src/entities/content/attachment.entity.ts b/backend/src/entities/content/attachment.entity.ts index 5e7000cf..f0a1b181 100644 --- a/backend/src/entities/content/attachment.entity.ts +++ b/backend/src/entities/content/attachment.entity.ts @@ -1,8 +1,8 @@ import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'; import { LearningObject } from './learning-object.entity.js'; -import {AttachmentRepository} from "../../data/content/attachment-repository"; +import { AttachmentRepository } from '../../data/content/attachment-repository'; -@Entity({repository: () => AttachmentRepository}) +@Entity({ repository: () => AttachmentRepository }) export class Attachment { @ManyToOne({ entity: () => LearningObject, diff --git a/backend/src/entities/content/language.ts b/backend/src/entities/content/language.ts index 7a106762..d7687331 100644 --- a/backend/src/entities/content/language.ts +++ b/backend/src/entities/content/language.ts @@ -182,5 +182,5 @@ export enum Language { Yiddish = 'yi', Yoruba = 'yo', Zhuang = 'za', - Zulu = 'zu' + Zulu = 'zu', } diff --git a/backend/src/entities/content/learning-object.entity.ts b/backend/src/entities/content/learning-object.entity.ts index 8f3672be..55c4a808 100644 --- a/backend/src/entities/content/learning-object.entity.ts +++ b/backend/src/entities/content/learning-object.entity.ts @@ -2,11 +2,11 @@ import { Embeddable, Embedded, Entity, Enum, ManyToMany, OneToMany, PrimaryKey, import { Language } from './language.js'; import { Attachment } from './attachment.entity.js'; import { Teacher } from '../users/teacher.entity.js'; -import {DwengoContentType} from "../../services/learning-objects/processing/content-type"; -import {v4} from "uuid"; -import {LearningObjectRepository} from "../../data/content/learning-object-repository"; +import { DwengoContentType } from '../../services/learning-objects/processing/content-type'; +import { v4 } from 'uuid'; +import { LearningObjectRepository } from '../../data/content/learning-object-repository'; -@Entity({repository: () => LearningObjectRepository}) +@Entity({ repository: () => LearningObjectRepository }) export class LearningObject { @PrimaryKey({ type: 'string' }) hruid!: string; @@ -20,7 +20,7 @@ export class LearningObject { @PrimaryKey({ type: 'number' }) version: number = 1; - @Property({type: 'uuid', unique: true}) + @Property({ type: 'uuid', unique: true }) uuid = v4(); @ManyToMany({ diff --git a/backend/src/entities/content/learning-path-node.entity.ts b/backend/src/entities/content/learning-path-node.entity.ts index e2fbdbb3..9dbb47b4 100644 --- a/backend/src/entities/content/learning-path-node.entity.ts +++ b/backend/src/entities/content/learning-path-node.entity.ts @@ -1,15 +1,14 @@ -import {Entity, Enum, ManyToOne, OneToMany, PrimaryKey, Property} from "@mikro-orm/core"; -import {Language} from "./language"; -import {LearningPath} from "./learning-path.entity"; -import {LearningPathTransition} from "./learning-path-transition.entity"; +import { Entity, Enum, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'; +import { Language } from './language'; +import { LearningPath } from './learning-path.entity'; +import { LearningPathTransition } from './learning-path-transition.entity'; @Entity() export class LearningPathNode { - @ManyToOne({ entity: () => LearningPath, primary: true }) learningPath!: LearningPath; - @PrimaryKey({ type: "integer", autoincrement: true }) + @PrimaryKey({ type: 'integer', autoincrement: true }) nodeNumber!: number; @Property({ type: 'string' }) @@ -27,7 +26,7 @@ export class LearningPathNode { @Property({ type: 'bool' }) startNode!: boolean; - @OneToMany({ entity: () => LearningPathTransition, mappedBy: "node" }) + @OneToMany({ entity: () => LearningPathTransition, mappedBy: 'node' }) transitions: LearningPathTransition[] = []; @Property({ length: 3 }) diff --git a/backend/src/entities/content/learning-path-transition.entity.ts b/backend/src/entities/content/learning-path-transition.entity.ts index dfbe110e..97bfc65f 100644 --- a/backend/src/entities/content/learning-path-transition.entity.ts +++ b/backend/src/entities/content/learning-path-transition.entity.ts @@ -1,9 +1,9 @@ -import {Entity, ManyToOne, PrimaryKey, Property} from "@mikro-orm/core"; -import {LearningPathNode} from "./learning-path-node.entity"; +import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'; +import { LearningPathNode } from './learning-path-node.entity'; @Entity() export class LearningPathTransition { - @ManyToOne({entity: () => LearningPathNode, primary: true }) + @ManyToOne({ entity: () => LearningPathNode, primary: true }) node!: LearningPathNode; @PrimaryKey({ type: 'numeric' }) diff --git a/backend/src/entities/content/learning-path.entity.ts b/backend/src/entities/content/learning-path.entity.ts index 3bb839a0..e74388aa 100644 --- a/backend/src/entities/content/learning-path.entity.ts +++ b/backend/src/entities/content/learning-path.entity.ts @@ -1,16 +1,10 @@ -import { - Entity, - Enum, - ManyToMany, OneToMany, - PrimaryKey, - Property, -} from '@mikro-orm/core'; +import { Entity, Enum, ManyToMany, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'; import { Language } from './language.js'; import { Teacher } from '../users/teacher.entity.js'; -import {LearningPathRepository} from "../../data/content/learning-path-repository"; -import {LearningPathNode} from "./learning-path-node.entity"; +import { LearningPathRepository } from '../../data/content/learning-path-repository'; +import { LearningPathNode } from './learning-path-node.entity'; -@Entity({repository: () => LearningPathRepository}) +@Entity({ repository: () => LearningPathRepository }) export class LearningPath { @PrimaryKey({ type: 'string' }) hruid!: string; @@ -30,6 +24,6 @@ export class LearningPath { @Property({ type: 'blob', nullable: true }) image: Buffer | null = null; - @OneToMany({ entity: () => LearningPathNode, mappedBy: "learningPath" }) + @OneToMany({ entity: () => LearningPathNode, mappedBy: 'learningPath' }) nodes: LearningPathNode[] = []; } diff --git a/backend/src/entities/questions/answer.entity.ts b/backend/src/entities/questions/answer.entity.ts index 81974943..2c975cc5 100644 --- a/backend/src/entities/questions/answer.entity.ts +++ b/backend/src/entities/questions/answer.entity.ts @@ -1,9 +1,9 @@ import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'; import { Question } from './question.entity.js'; import { Teacher } from '../users/teacher.entity.js'; -import {AnswerRepository} from "../../data/questions/answer-repository"; +import { AnswerRepository } from '../../data/questions/answer-repository'; -@Entity({repository: () => AnswerRepository}) +@Entity({ repository: () => AnswerRepository }) export class Answer { @ManyToOne({ entity: () => Teacher, diff --git a/backend/src/entities/questions/question.entity.ts b/backend/src/entities/questions/question.entity.ts index c03b0e8f..bfa0d7bb 100644 --- a/backend/src/entities/questions/question.entity.ts +++ b/backend/src/entities/questions/question.entity.ts @@ -1,9 +1,9 @@ import { Entity, Enum, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'; import { Language } from '../content/language.js'; import { Student } from '../users/student.entity.js'; -import {QuestionRepository} from "../../data/questions/question-repository"; +import { QuestionRepository } from '../../data/questions/question-repository'; -@Entity({repository: () => QuestionRepository}) +@Entity({ repository: () => QuestionRepository }) export class Question { @PrimaryKey({ type: 'string' }) learningObjectHruid!: string; diff --git a/backend/src/entities/users/teacher.entity.ts b/backend/src/entities/users/teacher.entity.ts index ca3290bf..8af72c37 100644 --- a/backend/src/entities/users/teacher.entity.ts +++ b/backend/src/entities/users/teacher.entity.ts @@ -1,9 +1,9 @@ import { Collection, Entity, ManyToMany } from '@mikro-orm/core'; import { User } from './user.entity.js'; import { Class } from '../classes/class.entity.js'; -import {TeacherRepository} from "../../data/users/teacher-repository"; +import { TeacherRepository } from '../../data/users/teacher-repository'; -@Entity({repository: () => TeacherRepository}) +@Entity({ repository: () => TeacherRepository }) export class Teacher extends User { @ManyToMany(() => Class) classes!: Collection; diff --git a/backend/src/interfaces/learning-content.ts b/backend/src/interfaces/learning-content.ts index 5d11d6b2..970796a2 100644 --- a/backend/src/interfaces/learning-content.ts +++ b/backend/src/interfaces/learning-content.ts @@ -1,4 +1,4 @@ -import {Language} from "../entities/content/language"; +import { Language } from '../entities/content/language'; export interface Transition { default: boolean; diff --git a/backend/src/routes/learning-objects.ts b/backend/src/routes/learning-objects.ts index 421a5f19..b731fe69 100644 --- a/backend/src/routes/learning-objects.ts +++ b/backend/src/routes/learning-objects.ts @@ -1,8 +1,5 @@ import express from 'express'; -import { - getAllLearningObjects, getAttachment, - getLearningObject, getLearningObjectHTML, -} from '../controllers/learning-objects.js'; +import { getAllLearningObjects, getAttachment, getLearningObject, getLearningObjectHTML } from '../controllers/learning-objects.js'; const router = express.Router(); diff --git a/backend/src/services/learning-objects/attachment-service.ts b/backend/src/services/learning-objects/attachment-service.ts index fb2b2dd2..7791ac04 100644 --- a/backend/src/services/learning-objects/attachment-service.ts +++ b/backend/src/services/learning-objects/attachment-service.ts @@ -1,21 +1,23 @@ -import {getAttachmentRepository} from "../../data/repositories"; -import {Attachment} from "../../entities/content/attachment.entity"; -import {LearningObjectIdentifier} from "../../interfaces/learning-content"; +import { getAttachmentRepository } from '../../data/repositories'; +import { Attachment } from '../../entities/content/attachment.entity'; +import { LearningObjectIdentifier } from '../../interfaces/learning-content'; const attachmentService = { getAttachment(learningObjectId: LearningObjectIdentifier, attachmentName: string): Promise { const attachmentRepo = getAttachmentRepository(); if (learningObjectId.version) { - return attachmentRepo.findByLearningObjectIdAndName({ - hruid: learningObjectId.hruid, - language: learningObjectId.language, - version: learningObjectId.version, - }, attachmentName); - } - return attachmentRepo.findByMostRecentVersionOfLearningObjectAndName(learningObjectId.hruid, learningObjectId.language, attachmentName); - - } -} + return attachmentRepo.findByLearningObjectIdAndName( + { + hruid: learningObjectId.hruid, + language: learningObjectId.language, + version: learningObjectId.version, + }, + attachmentName + ); + } + return attachmentRepo.findByMostRecentVersionOfLearningObjectAndName(learningObjectId.hruid, learningObjectId.language, attachmentName); + }, +}; export default attachmentService; diff --git a/backend/src/services/learning-objects/database-learning-object-provider.ts b/backend/src/services/learning-objects/database-learning-object-provider.ts index 46fc23fe..533d382f 100644 --- a/backend/src/services/learning-objects/database-learning-object-provider.ts +++ b/backend/src/services/learning-objects/database-learning-object-provider.ts @@ -1,16 +1,11 @@ -import {LearningObjectProvider} from "./learning-object-provider"; -import { - FilteredLearningObject, - LearningObjectIdentifier, - LearningPathIdentifier -} from "../../interfaces/learning-content"; -import {getLearningObjectRepository, getLearningPathRepository} from "../../data/repositories"; -import {Language} from "../../entities/content/language"; -import {LearningObject} from "../../entities/content/learning-object.entity"; -import {getUrlStringForLearningObject} from "../../util/links"; -import processingService from "./processing/processing-service"; -import {NotFoundError} from "@mikro-orm/core"; - +import { LearningObjectProvider } from './learning-object-provider'; +import { FilteredLearningObject, LearningObjectIdentifier, LearningPathIdentifier } from '../../interfaces/learning-content'; +import { getLearningObjectRepository, getLearningPathRepository } from '../../data/repositories'; +import { Language } from '../../entities/content/language'; +import { LearningObject } from '../../entities/content/learning-object.entity'; +import { getUrlStringForLearningObject } from '../../util/links'; +import processingService from './processing/processing-service'; +import { NotFoundError } from '@mikro-orm/core'; function convertLearningObject(learningObject: LearningObject | null): FilteredLearningObject | null { if (!learningObject) { @@ -34,20 +29,18 @@ function convertLearningObject(learningObject: LearningObject | null): FilteredL educationalGoals: learningObject.educationalGoals, returnValue: { callback_url: learningObject.returnValue.callbackUrl, - callback_schema: JSON.parse(learningObject.returnValue.callbackSchema) + callback_schema: JSON.parse(learningObject.returnValue.callbackSchema), }, skosConcepts: learningObject.skosConcepts, targetAges: learningObject.targetAges || [], - teacherExclusive: learningObject.teacherExclusive - } + teacherExclusive: learningObject.teacherExclusive, + }; } function findLearningObjectEntityById(id: LearningObjectIdentifier): Promise { const learningObjectRepo = getLearningObjectRepository(); - return learningObjectRepo.findLatestByHruidAndLanguage( - id.hruid, id.language as Language - ); + return learningObjectRepo.findLatestByHruidAndLanguage(id.hruid, id.language as Language); } /** @@ -68,16 +61,11 @@ const databaseLearningObjectProvider: LearningObjectProvider = { async getLearningObjectHTML(id: LearningObjectIdentifier): Promise { const learningObjectRepo = getLearningObjectRepository(); - const learningObject = await learningObjectRepo.findLatestByHruidAndLanguage( - id.hruid, id.language as Language - ); + const learningObject = await learningObjectRepo.findLatestByHruidAndLanguage(id.hruid, id.language as Language); if (!learningObject) { return null; } - return await processingService.render( - learningObject, - (id) => findLearningObjectEntityById(id) - ); + return await processingService.render(learningObject, (id) => findLearningObjectEntityById(id)); }, /** @@ -88,9 +76,9 @@ const databaseLearningObjectProvider: LearningObjectProvider = { const learningPath = await learningPathRepo.findByHruidAndLanguage(id.hruid, id.language); if (!learningPath) { - throw new NotFoundError("The learning path with the given ID could not be found."); + throw new NotFoundError('The learning path with the given ID could not be found.'); } - return learningPath.nodes.map(it => it.learningObjectHruid); // TODO: Determine this based on the submissions of the user. + return learningPath.nodes.map((it) => it.learningObjectHruid); // TODO: Determine this based on the submissions of the user. }, /** @@ -101,23 +89,23 @@ const databaseLearningObjectProvider: LearningObjectProvider = { const learningPath = await learningPathRepo.findByHruidAndLanguage(id.hruid, id.language); if (!learningPath) { - throw new NotFoundError("The learning path with the given ID could not be found."); + throw new NotFoundError('The learning path with the given ID could not be found.'); } const learningObjects = await Promise.all( - learningPath.nodes.map(it => { + learningPath.nodes.map((it) => { const learningObject = this.getLearningObjectById({ hruid: it.learningObjectHruid, language: it.language, - version: it.version - }) + version: it.version, + }); if (learningObject === null) { console.log(`WARN: Learning object corresponding with node ${it} not found!`); } return learningObject; }) ); - return learningObjects.filter(it => it !== null); // TODO: Determine this based on the submissions of the user. - } -} + return learningObjects.filter((it) => it !== null); // TODO: Determine this based on the submissions of the user. + }, +}; export default databaseLearningObjectProvider; diff --git a/backend/src/services/learning-objects/dwengo-api-learning-object-provider.ts b/backend/src/services/learning-objects/dwengo-api-learning-object-provider.ts index ab914be4..c59a3616 100644 --- a/backend/src/services/learning-objects/dwengo-api-learning-object-provider.ts +++ b/backend/src/services/learning-objects/dwengo-api-learning-object-provider.ts @@ -1,22 +1,22 @@ import { DWENGO_API_BASE } from '../../config.js'; import { fetchWithLogging } from '../../util/apiHelper.js'; import { - FilteredLearningObject, LearningObjectIdentifier, + FilteredLearningObject, + LearningObjectIdentifier, LearningObjectMetadata, - LearningObjectNode, LearningPathIdentifier, + LearningObjectNode, + LearningPathIdentifier, LearningPathResponse, } from '../../interfaces/learning-content.js'; import dwengoApiLearningPathProvider from '../learning-paths/dwengo-api-learning-path-provider.js'; -import {LearningObjectProvider} from "./learning-object-provider"; +import { LearningObjectProvider } from './learning-object-provider'; /** * Helper function to convert the learning object metadata retrieved from the API to a FilteredLearningObject which * our API should return. * @param data */ -function filterData( - data: LearningObjectMetadata -): FilteredLearningObject { +function filterData(data: LearningObjectMetadata): FilteredLearningObject { return { key: data.hruid, // Hruid learningObject (not path) _id: data._id, @@ -43,25 +43,16 @@ function filterData( /** * Generic helper function to fetch all learning objects from a given path (full data or just HRUIDs) */ -async function fetchLearningObjects( - learningPathId: LearningPathIdentifier, - full: boolean -): Promise { +async function fetchLearningObjects(learningPathId: LearningPathIdentifier, full: boolean): Promise { try { - const learningPathResponse: LearningPathResponse = - await dwengoApiLearningPathProvider.fetchLearningPaths( - [learningPathId.hruid], - learningPathId.language, - `Learning path for HRUID "${learningPathId.hruid}"` - ); + const learningPathResponse: LearningPathResponse = await dwengoApiLearningPathProvider.fetchLearningPaths( + [learningPathId.hruid], + learningPathId.language, + `Learning path for HRUID "${learningPathId.hruid}"` + ); - if ( - !learningPathResponse.success || - !learningPathResponse.data?.length - ) { - console.error( - `⚠️ WARNING: Learning path "${learningPathId.hruid}" exists but contains no learning objects.` - ); + if (!learningPathResponse.success || !learningPathResponse.data?.length) { + console.error(`⚠️ WARNING: Learning path "${learningPathId.hruid}" exists but contains no learning objects.`); return []; } @@ -72,10 +63,12 @@ async function fetchLearningObjects( } return await Promise.all( - nodes.map(async (node) => dwengoApiLearningObjectProvider.getLearningObjectById({ + nodes.map(async (node) => + dwengoApiLearningObjectProvider.getLearningObjectById({ hruid: node.learningobject_hruid, - language: learningPathId.language - })) + language: learningPathId.language, + }) + ) ).then((objects) => objects.filter((obj): obj is FilteredLearningObject => obj !== null)); } catch (error) { console.error('❌ Error fetching learning objects:', error); @@ -87,19 +80,17 @@ const dwengoApiLearningObjectProvider: LearningObjectProvider = { /** * Fetches a single learning object by its HRUID */ - async getLearningObjectById( - id: LearningObjectIdentifier - ): Promise { + async getLearningObjectById(id: LearningObjectIdentifier): Promise { const metadataUrl = `${DWENGO_API_BASE}/learningObject/getMetadata`; const metadata = await fetchWithLogging( metadataUrl, `Metadata for Learning Object HRUID "${id.hruid}" (language ${id.language})`, { - params: id + params: id, } ); - if (!metadata || typeof metadata !== "object") { + if (!metadata || typeof metadata !== 'object') { console.error(`⚠️ WARNING: Learning object "${id.hruid}" not found.`); return null; } @@ -111,10 +102,7 @@ const dwengoApiLearningObjectProvider: LearningObjectProvider = { * Fetch full learning object data (metadata) */ async getLearningObjectsFromPath(id: LearningPathIdentifier): Promise { - return (await fetchLearningObjects( - id, - true, - )) as FilteredLearningObject[]; + return (await fetchLearningObjects(id, true)) as FilteredLearningObject[]; }, /** @@ -130,13 +118,9 @@ const dwengoApiLearningObjectProvider: LearningObjectProvider = { */ async getLearningObjectHTML(id: LearningObjectIdentifier): Promise { const htmlUrl = `${DWENGO_API_BASE}/learningObject/getRaw`; - const html = await fetchWithLogging( - htmlUrl, - `Metadata for Learning Object HRUID "${id.hruid}" (language ${id.language})`, - { - params: id - } - ); + const html = await fetchWithLogging(htmlUrl, `Metadata for Learning Object HRUID "${id.hruid}" (language ${id.language})`, { + params: id, + }); if (!html) { console.error(`⚠️ WARNING: Learning object "${id.hruid}" not found.`); @@ -144,7 +128,7 @@ const dwengoApiLearningObjectProvider: LearningObjectProvider = { } return html; - } + }, }; export default dwengoApiLearningObjectProvider; diff --git a/backend/src/services/learning-objects/learning-object-provider.ts b/backend/src/services/learning-objects/learning-object-provider.ts index 70190a1a..4d443ca0 100644 --- a/backend/src/services/learning-objects/learning-object-provider.ts +++ b/backend/src/services/learning-objects/learning-object-provider.ts @@ -1,8 +1,4 @@ -import { - FilteredLearningObject, - LearningObjectIdentifier, - LearningPathIdentifier -} from "../../interfaces/learning-content"; +import { FilteredLearningObject, LearningObjectIdentifier, LearningPathIdentifier } from '../../interfaces/learning-content'; export interface LearningObjectProvider { /** diff --git a/backend/src/services/learning-objects/learning-object-service.ts b/backend/src/services/learning-objects/learning-object-service.ts index 9dd327f0..b207d3dc 100644 --- a/backend/src/services/learning-objects/learning-object-service.ts +++ b/backend/src/services/learning-objects/learning-object-service.ts @@ -1,19 +1,14 @@ -import { - FilteredLearningObject, - LearningObjectIdentifier, - LearningPathIdentifier -} from "../../interfaces/learning-content"; -import dwengoApiLearningObjectProvider from "./dwengo-api-learning-object-provider"; -import {LearningObjectProvider} from "./learning-object-provider"; -import {EnvVars, getEnvVar} from "../../util/envvars"; -import databaseLearningObjectProvider from "./database-learning-object-provider"; +import { FilteredLearningObject, LearningObjectIdentifier, LearningPathIdentifier } from '../../interfaces/learning-content'; +import dwengoApiLearningObjectProvider from './dwengo-api-learning-object-provider'; +import { LearningObjectProvider } from './learning-object-provider'; +import { EnvVars, getEnvVar } from '../../util/envvars'; +import databaseLearningObjectProvider from './database-learning-object-provider'; function getProvider(id: LearningObjectIdentifier): LearningObjectProvider { if (id.hruid.startsWith(getEnvVar(EnvVars.UserContentPrefix))) { return databaseLearningObjectProvider; - } - return dwengoApiLearningObjectProvider; - + } + return dwengoApiLearningObjectProvider; } /** @@ -46,7 +41,7 @@ const learningObjectService = { */ getLearningObjectHTML(id: LearningObjectIdentifier): Promise { return getProvider(id).getLearningObjectHTML(id); - } + }, }; export default learningObjectService; diff --git a/backend/src/services/learning-objects/processing/audio/audio-processor.ts b/backend/src/services/learning-objects/processing/audio/audio-processor.ts index 0c4dd75e..3f7a8837 100644 --- a/backend/src/services/learning-objects/processing/audio/audio-processor.ts +++ b/backend/src/services/learning-objects/processing/audio/audio-processor.ts @@ -5,12 +5,11 @@ */ import DOMPurify from 'isomorphic-dompurify'; -import {type} from "node:os"; -import {DwengoContentType} from "../content-type"; -import {StringProcessor} from "../string-processor"; +import { type } from 'node:os'; +import { DwengoContentType } from '../content-type'; +import { StringProcessor } from '../string-processor'; class AudioProcessor extends StringProcessor { - constructor() { super(DwengoContentType.AUDIO_MPEG); } diff --git a/backend/src/services/learning-objects/processing/content-type.ts b/backend/src/services/learning-objects/processing/content-type.ts index d71c97b4..2ea44246 100644 --- a/backend/src/services/learning-objects/processing/content-type.ts +++ b/backend/src/services/learning-objects/processing/content-type.ts @@ -3,16 +3,16 @@ */ enum DwengoContentType { - TEXT_PLAIN = "text/plain", - TEXT_MARKDOWN = "text/markdown", - IMAGE_BLOCK = "image/image-block", - IMAGE_INLINE = "image/image", - AUDIO_MPEG = "audio/mpeg", - APPLICATION_PDF = "application/pdf", - EXTERN = "extern", - BLOCKLY = "blockly", - GIFT = "text/gift", - CT_SCHEMA = "text/ct-schema" + TEXT_PLAIN = 'text/plain', + TEXT_MARKDOWN = 'text/markdown', + IMAGE_BLOCK = 'image/image-block', + IMAGE_INLINE = 'image/image', + AUDIO_MPEG = 'audio/mpeg', + APPLICATION_PDF = 'application/pdf', + EXTERN = 'extern', + BLOCKLY = 'blockly', + GIFT = 'text/gift', + CT_SCHEMA = 'text/ct-schema', } -export { DwengoContentType } +export { DwengoContentType }; diff --git a/backend/src/services/learning-objects/processing/extern/extern-processor.ts b/backend/src/services/learning-objects/processing/extern/extern-processor.ts index 68ec9899..d16445dc 100644 --- a/backend/src/services/learning-objects/processing/extern/extern-processor.ts +++ b/backend/src/services/learning-objects/processing/extern/extern-processor.ts @@ -5,10 +5,10 @@ */ import DOMPurify from 'isomorphic-dompurify'; -import {ProcessingError} from "../processing-error"; -import {isValidHttpUrl} from "../../../../util/links"; -import {DwengoContentType} from "../content-type"; -import {StringProcessor} from "../string-processor"; +import { ProcessingError } from '../processing-error'; +import { isValidHttpUrl } from '../../../../util/links'; +import { DwengoContentType } from '../content-type'; +import { StringProcessor } from '../string-processor'; class ExternProcessor extends StringProcessor { constructor() { @@ -17,23 +17,23 @@ class ExternProcessor extends StringProcessor { override renderFn(externURL: string) { if (!isValidHttpUrl(externURL)) { - throw new ProcessingError("The url is not valid: " + externURL); + throw new ProcessingError('The url is not valid: ' + externURL); } // If a seperate youtube-processor would be added, this code would need to move to that processor // Converts youtube urls to youtube-embed urls - const match = /(.*youtube.com\/)watch\?v=(.*)/.exec(externURL) + const match = /(.*youtube.com\/)watch\?v=(.*)/.exec(externURL); if (match) { - externURL = match[1] + "embed/" + match[2]; + externURL = match[1] + 'embed/' + match[2]; } - return DOMPurify.sanitize(` + return DOMPurify.sanitize( + `
`, - { ADD_TAGS: ["iframe"], ADD_ATTR: ['allow', 'allowfullscreen', 'frameborder', 'scrolling']} + { ADD_TAGS: ['iframe'], ADD_ATTR: ['allow', 'allowfullscreen', 'frameborder', 'scrolling'] } ); - } } diff --git a/backend/src/services/learning-objects/processing/gift/gift-processor.ts b/backend/src/services/learning-objects/processing/gift/gift-processor.ts index b6eba8ba..49f41904 100644 --- a/backend/src/services/learning-objects/processing/gift/gift-processor.ts +++ b/backend/src/services/learning-objects/processing/gift/gift-processor.ts @@ -3,21 +3,20 @@ */ import DOMPurify from 'isomorphic-dompurify'; -import {GIFTQuestion, parse} from "gift-pegjs" -import {DwengoContentType} from "../content-type"; -import {GIFTQuestionRenderer} from "./question-renderers/gift-question-renderer"; -import {MultipleChoiceQuestionRenderer} from "./question-renderers/multiple-choice-question-renderer"; -import {CategoryQuestionRenderer} from "./question-renderers/category-question-renderer"; -import {DescriptionQuestionRenderer} from "./question-renderers/description-question-renderer"; -import {EssayQuestionRenderer} from "./question-renderers/essay-question-renderer"; -import {MatchingQuestionRenderer} from "./question-renderers/matching-question-renderer"; -import {NumericalQuestionRenderer} from "./question-renderers/numerical-question-renderer"; -import {ShortQuestionRenderer} from "./question-renderers/short-question-renderer"; -import {TrueFalseQuestionRenderer} from "./question-renderers/true-false-question-renderer"; -import {StringProcessor} from "../string-processor"; +import { GIFTQuestion, parse } from 'gift-pegjs'; +import { DwengoContentType } from '../content-type'; +import { GIFTQuestionRenderer } from './question-renderers/gift-question-renderer'; +import { MultipleChoiceQuestionRenderer } from './question-renderers/multiple-choice-question-renderer'; +import { CategoryQuestionRenderer } from './question-renderers/category-question-renderer'; +import { DescriptionQuestionRenderer } from './question-renderers/description-question-renderer'; +import { EssayQuestionRenderer } from './question-renderers/essay-question-renderer'; +import { MatchingQuestionRenderer } from './question-renderers/matching-question-renderer'; +import { NumericalQuestionRenderer } from './question-renderers/numerical-question-renderer'; +import { ShortQuestionRenderer } from './question-renderers/short-question-renderer'; +import { TrueFalseQuestionRenderer } from './question-renderers/true-false-question-renderer'; +import { StringProcessor } from '../string-processor'; class GiftProcessor extends StringProcessor { - private renderers: RendererMap = { Category: new CategoryQuestionRenderer(), Description: new DescriptionQuestionRenderer(), @@ -26,8 +25,8 @@ class GiftProcessor extends StringProcessor { Numerical: new NumericalQuestionRenderer(), Short: new ShortQuestionRenderer(), TF: new TrueFalseQuestionRenderer(), - MC: new MultipleChoiceQuestionRenderer() - } + MC: new MultipleChoiceQuestionRenderer(), + }; constructor() { super(DwengoContentType.GIFT); @@ -40,11 +39,11 @@ class GiftProcessor extends StringProcessor { let i = 1; for (const question of quizQuestions) { html += `
\n`; - html += " " + this.renderQuestion(question, i).replaceAll(/\n(.+)/g, "\n $1"); // Replace for indentation. + html += ' ' + this.renderQuestion(question, i).replaceAll(/\n(.+)/g, '\n $1'); // Replace for indentation. html += `
\n`; i++; } - html += "
\n" + html += '
\n'; return DOMPurify.sanitize(html); } @@ -56,7 +55,7 @@ class GiftProcessor extends StringProcessor { } type RendererMap = { - [K in GIFTQuestion["type"]]: GIFTQuestionRenderer> + [K in GIFTQuestion['type']]: GIFTQuestionRenderer>; }; export default GiftProcessor; diff --git a/backend/src/services/learning-objects/processing/gift/question-renderers/category-question-renderer.ts b/backend/src/services/learning-objects/processing/gift/question-renderers/category-question-renderer.ts index 6f299c17..2ed79087 100644 --- a/backend/src/services/learning-objects/processing/gift/question-renderers/category-question-renderer.ts +++ b/backend/src/services/learning-objects/processing/gift/question-renderers/category-question-renderer.ts @@ -1,6 +1,6 @@ -import {GIFTQuestionRenderer} from "./gift-question-renderer"; -import {Category} from "gift-pegjs"; -import {ProcessingError} from "../../processing-error"; +import { GIFTQuestionRenderer } from './gift-question-renderer'; +import { Category } from 'gift-pegjs'; +import { ProcessingError } from '../../processing-error'; export class CategoryQuestionRenderer extends GIFTQuestionRenderer { render(question: Category, questionNumber: number): string { diff --git a/backend/src/services/learning-objects/processing/gift/question-renderers/description-question-renderer.ts b/backend/src/services/learning-objects/processing/gift/question-renderers/description-question-renderer.ts index adea25a6..c9302e5a 100644 --- a/backend/src/services/learning-objects/processing/gift/question-renderers/description-question-renderer.ts +++ b/backend/src/services/learning-objects/processing/gift/question-renderers/description-question-renderer.ts @@ -1,6 +1,6 @@ -import {GIFTQuestionRenderer} from "./gift-question-renderer"; -import {Description} from "gift-pegjs"; -import {ProcessingError} from "../../processing-error"; +import { GIFTQuestionRenderer } from './gift-question-renderer'; +import { Description } from 'gift-pegjs'; +import { ProcessingError } from '../../processing-error'; export class DescriptionQuestionRenderer extends GIFTQuestionRenderer { render(question: Description, questionNumber: number): string { diff --git a/backend/src/services/learning-objects/processing/gift/question-renderers/essay-question-renderer.ts b/backend/src/services/learning-objects/processing/gift/question-renderers/essay-question-renderer.ts index af000c11..ed4b1c50 100644 --- a/backend/src/services/learning-objects/processing/gift/question-renderers/essay-question-renderer.ts +++ b/backend/src/services/learning-objects/processing/gift/question-renderers/essay-question-renderer.ts @@ -1,9 +1,9 @@ -import {GIFTQuestionRenderer} from "./gift-question-renderer"; -import {Essay} from "gift-pegjs"; +import { GIFTQuestionRenderer } from './gift-question-renderer'; +import { Essay } from 'gift-pegjs'; export class EssayQuestionRenderer extends GIFTQuestionRenderer { render(question: Essay, questionNumber: number): string { - let renderedHtml = ""; + let renderedHtml = ''; if (question.title) { renderedHtml += `

${question.title}

\n`; } diff --git a/backend/src/services/learning-objects/processing/gift/question-renderers/gift-question-renderer.ts b/backend/src/services/learning-objects/processing/gift/question-renderers/gift-question-renderer.ts index bd33107b..41ab5ba2 100644 --- a/backend/src/services/learning-objects/processing/gift/question-renderers/gift-question-renderer.ts +++ b/backend/src/services/learning-objects/processing/gift/question-renderers/gift-question-renderer.ts @@ -1,4 +1,4 @@ -import {GIFTQuestion} from "gift-pegjs"; +import { GIFTQuestion } from 'gift-pegjs'; /** * Subclasses of this class are renderers which can render a specific type of GIFT questions to HTML. diff --git a/backend/src/services/learning-objects/processing/gift/question-renderers/matching-question-renderer.ts b/backend/src/services/learning-objects/processing/gift/question-renderers/matching-question-renderer.ts index 93e7511e..302f44d3 100644 --- a/backend/src/services/learning-objects/processing/gift/question-renderers/matching-question-renderer.ts +++ b/backend/src/services/learning-objects/processing/gift/question-renderers/matching-question-renderer.ts @@ -1,6 +1,6 @@ -import {GIFTQuestionRenderer} from "./gift-question-renderer"; -import {Matching} from "gift-pegjs"; -import {ProcessingError} from "../../processing-error"; +import { GIFTQuestionRenderer } from './gift-question-renderer'; +import { Matching } from 'gift-pegjs'; +import { ProcessingError } from '../../processing-error'; export class MatchingQuestionRenderer extends GIFTQuestionRenderer { render(question: Matching, questionNumber: number): string { diff --git a/backend/src/services/learning-objects/processing/gift/question-renderers/multiple-choice-question-renderer.ts b/backend/src/services/learning-objects/processing/gift/question-renderers/multiple-choice-question-renderer.ts index bf1de8c4..26eac034 100644 --- a/backend/src/services/learning-objects/processing/gift/question-renderers/multiple-choice-question-renderer.ts +++ b/backend/src/services/learning-objects/processing/gift/question-renderers/multiple-choice-question-renderer.ts @@ -1,9 +1,9 @@ -import {GIFTQuestionRenderer} from "./gift-question-renderer"; -import {MultipleChoice} from "gift-pegjs"; +import { GIFTQuestionRenderer } from './gift-question-renderer'; +import { MultipleChoice } from 'gift-pegjs'; export class MultipleChoiceQuestionRenderer extends GIFTQuestionRenderer { render(question: MultipleChoice, questionNumber: number): string { - let renderedHtml = ""; + let renderedHtml = ''; if (question.title) { renderedHtml += `

${question.title}

\n`; } diff --git a/backend/src/services/learning-objects/processing/gift/question-renderers/numerical-question-renderer.ts b/backend/src/services/learning-objects/processing/gift/question-renderers/numerical-question-renderer.ts index 6553add4..65352795 100644 --- a/backend/src/services/learning-objects/processing/gift/question-renderers/numerical-question-renderer.ts +++ b/backend/src/services/learning-objects/processing/gift/question-renderers/numerical-question-renderer.ts @@ -1,6 +1,6 @@ -import {GIFTQuestionRenderer} from "./gift-question-renderer"; -import {Numerical} from "gift-pegjs"; -import {ProcessingError} from "../../processing-error"; +import { GIFTQuestionRenderer } from './gift-question-renderer'; +import { Numerical } from 'gift-pegjs'; +import { ProcessingError } from '../../processing-error'; export class NumericalQuestionRenderer extends GIFTQuestionRenderer { render(question: Numerical, questionNumber: number): string { diff --git a/backend/src/services/learning-objects/processing/gift/question-renderers/short-question-renderer.ts b/backend/src/services/learning-objects/processing/gift/question-renderers/short-question-renderer.ts index 96196a67..683bbc09 100644 --- a/backend/src/services/learning-objects/processing/gift/question-renderers/short-question-renderer.ts +++ b/backend/src/services/learning-objects/processing/gift/question-renderers/short-question-renderer.ts @@ -1,6 +1,6 @@ -import {GIFTQuestionRenderer} from "./gift-question-renderer"; -import {ShortAnswer} from "gift-pegjs"; -import {ProcessingError} from "../../processing-error"; +import { GIFTQuestionRenderer } from './gift-question-renderer'; +import { ShortAnswer } from 'gift-pegjs'; +import { ProcessingError } from '../../processing-error'; export class ShortQuestionRenderer extends GIFTQuestionRenderer { render(question: ShortAnswer, questionNumber: number): string { diff --git a/backend/src/services/learning-objects/processing/gift/question-renderers/true-false-question-renderer.ts b/backend/src/services/learning-objects/processing/gift/question-renderers/true-false-question-renderer.ts index 0ff108f4..7f0fb9f8 100644 --- a/backend/src/services/learning-objects/processing/gift/question-renderers/true-false-question-renderer.ts +++ b/backend/src/services/learning-objects/processing/gift/question-renderers/true-false-question-renderer.ts @@ -1,6 +1,6 @@ -import {GIFTQuestionRenderer} from "./gift-question-renderer"; -import {TrueFalse} from "gift-pegjs"; -import {ProcessingError} from "../../processing-error"; +import { GIFTQuestionRenderer } from './gift-question-renderer'; +import { TrueFalse } from 'gift-pegjs'; +import { ProcessingError } from '../../processing-error'; export class TrueFalseQuestionRenderer extends GIFTQuestionRenderer { render(question: TrueFalse, questionNumber: number): string { diff --git a/backend/src/services/learning-objects/processing/image/block-image-processor.ts b/backend/src/services/learning-objects/processing/image/block-image-processor.ts index 7d578e1c..f4f8a773 100644 --- a/backend/src/services/learning-objects/processing/image/block-image-processor.ts +++ b/backend/src/services/learning-objects/processing/image/block-image-processor.ts @@ -2,15 +2,15 @@ * Based on https://github.com/dwengovzw/Learning-Object-Repository/blob/main/app/processors/image/block_image_processor.js */ -import InlineImageProcessor from "./inline-image-processor.js" +import InlineImageProcessor from './inline-image-processor.js'; import DOMPurify from 'isomorphic-dompurify'; class BlockImageProcessor extends InlineImageProcessor { - constructor(){ + constructor() { super(); } - override renderFn(imageUrl: string){ + override renderFn(imageUrl: string) { const inlineHtml = super.render(imageUrl); return DOMPurify.sanitize(`
${inlineHtml}
`); } diff --git a/backend/src/services/learning-objects/processing/image/inline-image-processor.ts b/backend/src/services/learning-objects/processing/image/inline-image-processor.ts index 7815fab3..8d44d1ae 100644 --- a/backend/src/services/learning-objects/processing/image/inline-image-processor.ts +++ b/backend/src/services/learning-objects/processing/image/inline-image-processor.ts @@ -3,10 +3,10 @@ */ import DOMPurify from 'isomorphic-dompurify'; -import {DwengoContentType} from "../content-type.js"; -import {ProcessingError} from "../processing-error.js"; -import {isValidHttpUrl} from "../../../../util/links"; -import {StringProcessor} from "../string-processor"; +import { DwengoContentType } from '../content-type.js'; +import { ProcessingError } from '../processing-error.js'; +import { isValidHttpUrl } from '../../../../util/links'; +import { StringProcessor } from '../string-processor'; class InlineImageProcessor extends StringProcessor { constructor(contentType: DwengoContentType = DwengoContentType.IMAGE_INLINE) { diff --git a/backend/src/services/learning-objects/processing/markdown/dwengo-marked-renderer.ts b/backend/src/services/learning-objects/processing/markdown/dwengo-marked-renderer.ts index d5324754..96710591 100644 --- a/backend/src/services/learning-objects/processing/markdown/dwengo-marked-renderer.ts +++ b/backend/src/services/learning-objects/processing/markdown/dwengo-marked-renderer.ts @@ -1,15 +1,15 @@ /** * Based on https://github.com/dwengovzw/Learning-Object-Repository/blob/main/app/processors/markdown/learing_object_markdown_renderer.js [sic!] */ -import PdfProcessor from "../pdf/pdf-processor.js"; -import AudioProcessor from "../audio/audio-processor.js"; -import ExternProcessor from "../extern/extern-processor.js"; -import InlineImageProcessor from "../image/inline-image-processor.js"; -import * as marked from "marked"; -import {getUrlStringForLearningObjectHTML, isValidHttpUrl} from "../../../../util/links"; -import {ProcessingError} from "../processing-error"; -import {LearningObjectIdentifier} from "../../../../interfaces/learning-content"; -import {Language} from "../../../../entities/content/language"; +import PdfProcessor from '../pdf/pdf-processor.js'; +import AudioProcessor from '../audio/audio-processor.js'; +import ExternProcessor from '../extern/extern-processor.js'; +import InlineImageProcessor from '../image/inline-image-processor.js'; +import * as marked from 'marked'; +import { getUrlStringForLearningObjectHTML, isValidHttpUrl } from '../../../../util/links'; +import { ProcessingError } from '../processing-error'; +import { LearningObjectIdentifier } from '../../../../interfaces/learning-content'; +import { Language } from '../../../../entities/content/language'; import Image = marked.Tokens.Image; import Heading = marked.Tokens.Heading; @@ -27,11 +27,11 @@ const prefixes = { }; function extractLearningObjectIdFromHref(href: string): LearningObjectIdentifier { - const [hruid, language, version] = href.split(/\/(.+)/, 2)[1].split("/"); + const [hruid, language, version] = href.split(/\/(.+)/, 2)[1].split('/'); return { hruid, language: language as Language, - version: parseInt(version) + version: parseInt(version), }; } @@ -41,39 +41,40 @@ function extractLearningObjectIdFromHref(href: string): LearningObjectIdentifier * - links to other learning objects, * - embeddings of other learning objects. */ - const dwengoMarkedRenderer: RendererObject = { +const dwengoMarkedRenderer: RendererObject = { heading(heading: Heading): string { const text = heading.text; const level = heading.depth; const escapedText = text.toLowerCase().replace(/[^\w]+/g, '-'); - return `\n` + - ` \n` + - ` \n` + - ` \n` + - ` ${text}\n` + - `\n` + return ( + `\n` + + ` \n` + + ` \n` + + ` \n` + + ` ${text}\n` + + `\n` + ); }, // When the syntax for a link is used => [text](href "title") // Render a custom link when the prefix for a learning object is used. link(link: Link): string { const href = link.href; - const title = link.title || ""; + const title = link.title || ''; const text = marked.parseInline(link.text); // There could for example be an image in the link. if (href.startsWith(prefixes.learningObject)) { // Link to learning-object const learningObjectId = extractLearningObjectIdFromHref(href); return `${text}`; - } - // Any other link - if (!isValidHttpUrl(href)) { - throw new ProcessingError("Link is not a valid HTTP URL!"); - } - // - return `${text}`; - + } + // Any other link + if (!isValidHttpUrl(href)) { + throw new ProcessingError('Link is not a valid HTTP URL!'); + } + // + return `${text}`; }, // When the syntax for an image is used => ![text](href "title") @@ -98,12 +99,11 @@ function extractLearningObjectIdFromHref(href: string): LearningObjectIdentifier // Embedded youtube video or notebook (or other extern content) const proc = new ExternProcessor(); return proc.render(href.split(/\/(.+)/, 2)[1]); - } - // Embedded image - const proc = new InlineImageProcessor(); - return proc.render(href) - + } + // Embedded image + const proc = new InlineImageProcessor(); + return proc.render(href); }, -} +}; export default dwengoMarkedRenderer; diff --git a/backend/src/services/learning-objects/processing/markdown/markdown-processor.ts b/backend/src/services/learning-objects/processing/markdown/markdown-processor.ts index 58824c13..5daeceec 100644 --- a/backend/src/services/learning-objects/processing/markdown/markdown-processor.ts +++ b/backend/src/services/learning-objects/processing/markdown/markdown-processor.ts @@ -2,12 +2,12 @@ * Based on https://github.com/dwengovzw/Learning-Object-Repository/blob/main/app/processors/markdown/markdown_processor.js */ -import {marked} from 'marked'; +import { marked } from 'marked'; import InlineImageProcessor from '../image/inline-image-processor.js'; -import {DwengoContentType} from "../content-type"; -import dwengoMarkedRenderer from "./dwengo-marked-renderer"; -import {StringProcessor} from "../string-processor"; -import {ProcessingError} from "../processing-error"; +import { DwengoContentType } from '../content-type'; +import dwengoMarkedRenderer from './dwengo-marked-renderer'; +import { StringProcessor } from '../string-processor'; +import { ProcessingError } from '../processing-error'; class MarkdownProcessor extends StringProcessor { constructor() { @@ -15,10 +15,10 @@ class MarkdownProcessor extends StringProcessor { } override renderFn(mdText: string) { - let html = ""; + let html = ''; try { - marked.use({renderer: dwengoMarkedRenderer}); - html = marked(mdText, {async: false}); + marked.use({ renderer: dwengoMarkedRenderer }); + html = marked(mdText, { async: false }); html = this.replaceLinks(html); // Replace html image links path } catch (e: any) { throw new ProcessingError(e.message); @@ -28,14 +28,10 @@ class MarkdownProcessor extends StringProcessor { replaceLinks(html: string) { const proc = new InlineImageProcessor(); - html = html.replace(//g, ( - match: string, - src: string, - alt: string, - altText: string, - title: string, - titleText: string - ) => proc.render(src)); + html = html.replace( + //g, + (match: string, src: string, alt: string, altText: string, title: string, titleText: string) => proc.render(src) + ); return html; } } diff --git a/backend/src/services/learning-objects/processing/pdf/pdf-processor.ts b/backend/src/services/learning-objects/processing/pdf/pdf-processor.ts index dcb08ba7..2ebed94e 100644 --- a/backend/src/services/learning-objects/processing/pdf/pdf-processor.ts +++ b/backend/src/services/learning-objects/processing/pdf/pdf-processor.ts @@ -5,10 +5,10 @@ */ import DOMPurify from 'isomorphic-dompurify'; -import {DwengoContentType} from "../content-type.js"; -import {isValidHttpUrl} from "../../../../util/links.js"; -import {ProcessingError} from "../processing-error.js"; -import {StringProcessor} from "../string-processor"; +import { DwengoContentType } from '../content-type.js'; +import { isValidHttpUrl } from '../../../../util/links.js'; +import { ProcessingError } from '../processing-error.js'; +import { StringProcessor } from '../string-processor'; class PdfProcessor extends StringProcessor { constructor() { @@ -20,9 +20,11 @@ class PdfProcessor extends StringProcessor { throw new ProcessingError(`PDF URL is invalid: ${pdfUrl}`); } - return DOMPurify.sanitize(` + return DOMPurify.sanitize( + ` - `, { ADD_TAGS: ["embed"] } + `, + { ADD_TAGS: ['embed'] } ); } } diff --git a/backend/src/services/learning-objects/processing/processing-service.ts b/backend/src/services/learning-objects/processing/processing-service.ts index c682de32..5bdbbd9c 100644 --- a/backend/src/services/learning-objects/processing/processing-service.ts +++ b/backend/src/services/learning-objects/processing/processing-service.ts @@ -2,20 +2,20 @@ * Based on https://github.com/dwengovzw/Learning-Object-Repository/blob/main/app/processors/processing_proxy.js */ -import BlockImageProcessor from "./image/block-image-processor.js"; -import InlineImageProcessor from "./image/inline-image-processor.js"; -import { MarkdownProcessor } from "./markdown/markdown-processor.js"; -import TextProcessor from "./text/text-processor.js"; -import AudioProcessor from "./audio/audio-processor.js"; -import PdfProcessor from "./pdf/pdf-processor.js"; -import ExternProcessor from "./extern/extern-processor.js"; -import GiftProcessor from "./gift/gift-processor.js"; -import {LearningObject} from "../../../entities/content/learning-object.entity"; -import Processor from "./processor"; -import {DwengoContentType} from "./content-type"; -import {LearningObjectIdentifier} from "../../../interfaces/learning-content"; -import {Language} from "../../../entities/content/language"; -import {replaceAsync} from "../../../util/async"; +import BlockImageProcessor from './image/block-image-processor.js'; +import InlineImageProcessor from './image/inline-image-processor.js'; +import { MarkdownProcessor } from './markdown/markdown-processor.js'; +import TextProcessor from './text/text-processor.js'; +import AudioProcessor from './audio/audio-processor.js'; +import PdfProcessor from './pdf/pdf-processor.js'; +import ExternProcessor from './extern/extern-processor.js'; +import GiftProcessor from './gift/gift-processor.js'; +import { LearningObject } from '../../../entities/content/learning-object.entity'; +import Processor from './processor'; +import { DwengoContentType } from './content-type'; +import { LearningObjectIdentifier } from '../../../interfaces/learning-content'; +import { Language } from '../../../entities/content/language'; +import { replaceAsync } from '../../../util/async'; const EMBEDDED_LEARNING_OBJECT_PLACEHOLDER = //g; const LEARNING_OBJECT_DOES_NOT_EXIST = "
"; @@ -32,12 +32,10 @@ class ProcessingService { new AudioProcessor(), new PdfProcessor(), new ExternProcessor(), - new GiftProcessor() + new GiftProcessor(), ]; - this.processors = new Map( - processors.map(processor => [processor.contentType, processor]) - ) + this.processors = new Map(processors.map((processor) => [processor.contentType, processor])); } /** @@ -65,7 +63,7 @@ class ProcessingService { const learningObject = await fetchEmbeddedLearningObjects({ hruid, language: language as Language, - version: parseInt(version) + version: parseInt(version), }); // If it does not exist, replace it by a placeholder. diff --git a/backend/src/services/learning-objects/processing/processor.ts b/backend/src/services/learning-objects/processing/processor.ts index 3e511cd9..85e11cee 100644 --- a/backend/src/services/learning-objects/processing/processor.ts +++ b/backend/src/services/learning-objects/processing/processor.ts @@ -1,6 +1,6 @@ -import {LearningObject} from "../../../entities/content/learning-object.entity"; -import {ProcessingError} from "./processing-error"; -import {DwengoContentType} from "./content-type"; +import { LearningObject } from '../../../entities/content/learning-object.entity'; +import { ProcessingError } from './processing-error'; +import { DwengoContentType } from './content-type'; /** * Abstract base class for all processors. diff --git a/backend/src/services/learning-objects/processing/string-processor.ts b/backend/src/services/learning-objects/processing/string-processor.ts index 2026fa93..df5e6ed3 100644 --- a/backend/src/services/learning-objects/processing/string-processor.ts +++ b/backend/src/services/learning-objects/processing/string-processor.ts @@ -1,5 +1,5 @@ -import Processor from "./processor"; -import {LearningObject} from "../../../entities/content/learning-object.entity"; +import Processor from './processor'; +import { LearningObject } from '../../../entities/content/learning-object.entity'; export abstract class StringProcessor extends Processor { /** @@ -14,6 +14,6 @@ export abstract class StringProcessor extends Processor { * @protected */ protected renderLearningObjectFn(toRender: LearningObject): string { - return this.render(toRender.content.toString("ascii")); + return this.render(toRender.content.toString('ascii')); } } diff --git a/backend/src/services/learning-objects/processing/text/text-processor.ts b/backend/src/services/learning-objects/processing/text/text-processor.ts index fb3922a7..6b4ca248 100644 --- a/backend/src/services/learning-objects/processing/text/text-processor.ts +++ b/backend/src/services/learning-objects/processing/text/text-processor.ts @@ -3,8 +3,8 @@ */ import DOMPurify from 'isomorphic-dompurify'; -import {DwengoContentType} from "../content-type.js"; -import {StringProcessor} from "../string-processor"; +import { DwengoContentType } from '../content-type.js'; +import { StringProcessor } from '../string-processor'; class TextProcessor extends StringProcessor { constructor() { diff --git a/backend/src/services/learning-paths/database-learning-path-provider.ts b/backend/src/services/learning-paths/database-learning-path-provider.ts index 60edb98b..3b3b49af 100644 --- a/backend/src/services/learning-paths/database-learning-path-provider.ts +++ b/backend/src/services/learning-paths/database-learning-path-provider.ts @@ -1,19 +1,11 @@ -import {LearningPathProvider} from "./learning-path-provider"; -import { - FilteredLearningObject, - LearningObjectNode, - LearningPath, - LearningPathResponse, - Transition -} from "../../interfaces/learning-content"; -import { - LearningPath as LearningPathEntity -} from "../../entities/content/learning-path.entity" -import {getLearningPathRepository} from "../../data/repositories"; -import {Language} from "../../entities/content/language"; -import learningObjectService from "../learning-objects/learning-object-service"; -import { LearningPathNode } from "../../entities/content/learning-path-node.entity"; -import {LearningPathTransition} from "../../entities/content/learning-path-transition.entity"; +import { LearningPathProvider } from './learning-path-provider'; +import { FilteredLearningObject, LearningObjectNode, LearningPath, LearningPathResponse, Transition } from '../../interfaces/learning-content'; +import { LearningPath as LearningPathEntity } from '../../entities/content/learning-path.entity'; +import { getLearningPathRepository } from '../../data/repositories'; +import { Language } from '../../entities/content/language'; +import learningObjectService from '../learning-objects/learning-object-service'; +import { LearningPathNode } from '../../entities/content/learning-path-node.entity'; +import { LearningPathTransition } from '../../entities/content/learning-path-transition.entity'; /** * Fetches the corresponding learning object for each of the nodes and creates a map that maps each node to its @@ -25,19 +17,19 @@ async function getLearningObjectsForNodes(nodes: LearningPathNode[]): Promise( await Promise.all( - nodes.map(node => - learningObjectService.getLearningObjectById({ - hruid: node.learningObjectHruid, - version: node.version, - language: node.language - }).then(learningObject => - <[LearningPathNode, FilteredLearningObject | null]>[node, learningObject] - ) + nodes.map((node) => + learningObjectService + .getLearningObjectById({ + hruid: node.learningObjectHruid, + version: node.version, + language: node.language, + }) + .then((learningObject) => <[LearningPathNode, FilteredLearningObject | null]>[node, learningObject]) ) ) ); - if (nullableNodesToLearningObjects.values().some(it => it === null)) { - throw new Error("At least one of the learning objects on this path could not be found.") + if (nullableNodesToLearningObjects.values().some((it) => it === null)) { + throw new Error('At least one of the learning objects on this path could not be found.'); } return nullableNodesToLearningObjects as Map; } @@ -46,16 +38,19 @@ async function getLearningObjectsForNodes(nodes: LearningPathNode[]): Promise { - const nodesToLearningObjects: Map = - await getLearningObjectsForNodes(learningPath.nodes); + const nodesToLearningObjects: Map = await getLearningObjectsForNodes(learningPath.nodes); - const targetAges = - nodesToLearningObjects.values().flatMap(it => it.targetAges || []).toArray(); + const targetAges = nodesToLearningObjects + .values() + .flatMap((it) => it.targetAges || []) + .toArray(); - const keywords = - nodesToLearningObjects.values().flatMap(it => it.keywords || []).toArray(); + const keywords = nodesToLearningObjects + .values() + .flatMap((it) => it.keywords || []) + .toArray(); - const image = learningPath.image ? learningPath.image.toString("base64") : undefined; + const image = learningPath.image ? learningPath.image.toString('base64') : undefined; return { _id: `${learningPath.hruid}/${learningPath.language}`, // For backwards compatibility with the original Dwengo API. @@ -71,8 +66,8 @@ async function convertLearningPath(learningPath: LearningPathEntity, order: numb keywords: keywords.join(' '), target_ages: targetAges, max_age: Math.max(...targetAges), - min_age: Math.min(...targetAges) - } + min_age: Math.min(...targetAges), + }; } /** @@ -80,24 +75,23 @@ async function convertLearningPath(learningPath: LearningPathEntity, order: numb * learning objects into a list of learning path nodes as they should be represented in the API. * @param nodesToLearningObjects */ -function convertNodes( - nodesToLearningObjects: Map -): LearningObjectNode[] { - return nodesToLearningObjects.entries().map((entry) => { - const [node, learningObject] = entry; - return { - _id: learningObject.uuid, - language: learningObject.language, - start_node: node.startNode, - created_at: node.createdAt.toISOString(), - updatedAt: node.updatedAt.toISOString(), - learningobject_hruid: node.learningObjectHruid, - version: learningObject.version, - transitions: node.transitions.map((trans, i) => - convertTransition(trans, i, nodesToLearningObjects) - ) - } - }).toArray(); +function convertNodes(nodesToLearningObjects: Map): LearningObjectNode[] { + return nodesToLearningObjects + .entries() + .map((entry) => { + const [node, learningObject] = entry; + return { + _id: learningObject.uuid, + language: learningObject.language, + start_node: node.startNode, + created_at: node.createdAt.toISOString(), + updatedAt: node.updatedAt.toISOString(), + learningobject_hruid: node.learningObjectHruid, + version: learningObject.version, + transitions: node.transitions.map((trans, i) => convertTransition(trans, i, nodesToLearningObjects)), + }; + }) + .toArray(); } /** @@ -115,17 +109,17 @@ function convertTransition( ): Transition { const nextNode = nodesToLearningObjects.get(transition.next); if (!nextNode) { - throw new Error(`Learning object ${transition.next.learningObjectHruid}/${transition.next.language}/${transition.next.version} not found!`) + throw new Error(`Learning object ${transition.next.learningObjectHruid}/${transition.next.language}/${transition.next.version} not found!`); } else { return { - _id: "" + index, // Retained for backwards compatibility. The index uniquely identifies the transition within the learning path. + _id: '' + index, // Retained for backwards compatibility. The index uniquely identifies the transition within the learning path. default: false, // We don't work with default transitions but retain this for backwards compatibility. next: { _id: nextNode._id + index, // Construct a unique ID for the transition for backwards compatibility. hruid: transition.next.learningObjectHruid, language: nextNode.language, - version: nextNode.version - } + version: nextNode.version, + }, }; } } @@ -140,19 +134,15 @@ const databaseLearningPathProvider: LearningPathProvider = { async fetchLearningPaths(hruids: string[], language: Language, source: string): Promise { const learningPathRepo = getLearningPathRepository(); - const learningPaths = await Promise.all( - hruids.map(hruid => learningPathRepo.findByHruidAndLanguage(hruid, language)) - ); + const learningPaths = await Promise.all(hruids.map((hruid) => learningPathRepo.findByHruidAndLanguage(hruid, language))); const filteredLearningPaths = await Promise.all( - learningPaths - .filter(learningPath => learningPath !== null) - .map((learningPath, index) => convertLearningPath(learningPath, index)) + learningPaths.filter((learningPath) => learningPath !== null).map((learningPath, index) => convertLearningPath(learningPath, index)) ); return { success: filteredLearningPaths.length > 0, data: await Promise.all(filteredLearningPaths), - source + source, }; }, @@ -163,12 +153,8 @@ const databaseLearningPathProvider: LearningPathProvider = { const learningPathRepo = getLearningPathRepository(); const searchResults = await learningPathRepo.findByQueryStringAndLanguage(query, language); - return await Promise.all( - searchResults.map((result, index) => - convertLearningPath(result, index) - ) - ); - } -} + return await Promise.all(searchResults.map((result, index) => convertLearningPath(result, index))); + }, +}; export default databaseLearningPathProvider; diff --git a/backend/src/services/learning-paths/dwengo-api-learning-path-provider.ts b/backend/src/services/learning-paths/dwengo-api-learning-path-provider.ts index ae6dff0a..037f7dd3 100644 --- a/backend/src/services/learning-paths/dwengo-api-learning-path-provider.ts +++ b/backend/src/services/learning-paths/dwengo-api-learning-path-provider.ts @@ -1,17 +1,10 @@ import { fetchWithLogging } from '../../util/apiHelper.js'; import { DWENGO_API_BASE } from '../../config.js'; -import { - LearningPath, - LearningPathResponse, -} from '../../interfaces/learning-content.js'; -import {LearningPathProvider} from "./learning-path-provider"; +import { LearningPath, LearningPathResponse } from '../../interfaces/learning-content.js'; +import { LearningPathProvider } from './learning-path-provider'; const dwengoApiLearningPathProvider: LearningPathProvider = { - async fetchLearningPaths( - hruids: string[], - language: string, - source: string - ): Promise { + async fetchLearningPaths(hruids: string[], language: string, source: string): Promise { if (hruids.length === 0) { return { success: false, @@ -24,11 +17,7 @@ const dwengoApiLearningPathProvider: LearningPathProvider = { const apiUrl = `${DWENGO_API_BASE}/learningPath/getPathsFromIdList`; const params = { pathIdList: JSON.stringify({ hruids }), language }; - const learningPaths = await fetchWithLogging( - apiUrl, - `Learning paths for ${source}`, - { params } - ); + const learningPaths = await fetchWithLogging(apiUrl, `Learning paths for ${source}`, { params }); if (!learningPaths || learningPaths.length === 0) { console.error(`⚠️ WARNING: No learning paths found for ${source}.`); @@ -46,20 +35,13 @@ const dwengoApiLearningPathProvider: LearningPathProvider = { data: learningPaths, }; }, - async searchLearningPaths( - query: string, - language: string - ): Promise { + async searchLearningPaths(query: string, language: string): Promise { const apiUrl = `${DWENGO_API_BASE}/learningPath/search`; const params = { all: query, language }; - const searchResults = await fetchWithLogging( - apiUrl, - `Search learning paths with query "${query}"`, - { params } - ); + const searchResults = await fetchWithLogging(apiUrl, `Search learning paths with query "${query}"`, { params }); return searchResults ?? []; - } + }, }; export default dwengoApiLearningPathProvider; diff --git a/backend/src/services/learning-paths/learning-path-personalizing-service.ts b/backend/src/services/learning-paths/learning-path-personalizing-service.ts index 6cd8f610..f204d742 100644 --- a/backend/src/services/learning-paths/learning-path-personalizing-service.ts +++ b/backend/src/services/learning-paths/learning-path-personalizing-service.ts @@ -1,21 +1,21 @@ -import {Student} from "../../entities/users/student.entity"; -import {getSubmissionRepository} from "../../data/repositories"; -import {Group} from "../../entities/assignments/group.entity"; -import {Submission} from "../../entities/assignments/submission.entity"; -import {LearningObjectIdentifier} from "../../entities/content/learning-object-identifier"; -import {LearningPathNode} from "../../entities/content/learning-path-node.entity"; -import {LearningPathTransition} from "../../entities/content/learning-path-transition.entity"; -import {JSONPath} from 'jsonpath-plus'; +import { Student } from '../../entities/users/student.entity'; +import { getSubmissionRepository } from '../../data/repositories'; +import { Group } from '../../entities/assignments/group.entity'; +import { Submission } from '../../entities/assignments/submission.entity'; +import { LearningObjectIdentifier } from '../../entities/content/learning-object-identifier'; +import { LearningPathNode } from '../../entities/content/learning-path-node.entity'; +import { LearningPathTransition } from '../../entities/content/learning-path-transition.entity'; +import { JSONPath } from 'jsonpath-plus'; /** * Returns the last submission for the learning object associated with the given node and for the student or group */ -async function getLastRelevantSubmission(node: LearningPathNode, pathFor: {student?: Student, group?: Group}): Promise { +async function getLastRelevantSubmission(node: LearningPathNode, pathFor: { student?: Student; group?: Group }): Promise { const submissionRepo = getSubmissionRepository(); const learningObjectId: LearningObjectIdentifier = { hruid: node.learningObjectHruid, language: node.language, - version: node.version + version: node.version, }; let lastSubmission: Submission | null; if (pathFor.group) { @@ -23,47 +23,46 @@ async function getLastRelevantSubmission(node: LearningPathNode, pathFor: {stude } else if (pathFor.student) { lastSubmission = await submissionRepo.findMostRecentSubmissionForStudent(learningObjectId, pathFor.student); } else { - throw new Error("The path must either be created for a certain group or for a certain student!"); + throw new Error('The path must either be created for a certain group or for a certain student!'); } return lastSubmission; } function transitionPossible(transition: LearningPathTransition, submitted: object | null): boolean { - if (transition.condition === "true" || !transition.condition) { + if (transition.condition === 'true' || !transition.condition) { return true; // If the transition is unconditional, we can go on. } if (submitted === null) { return false; // If the transition is not unconditional and there was no submission, the transition is not possible. } - return JSONPath({path: transition.condition, json: submitted}).length === 0; + return JSONPath({ path: transition.condition, json: submitted }).length === 0; } /** * Service to create individual trajectories from learning paths based on the submissions of the student or group. */ const learningPathPersonalizingService = { - async calculatePersonalizedTrajectory(nodes: LearningPathNode[], pathFor: {student?: Student, group?: Group}): Promise { + async calculatePersonalizedTrajectory(nodes: LearningPathNode[], pathFor: { student?: Student; group?: Group }): Promise { const trajectory: LearningPathNode[] = []; // Always start with the start node. - let currentNode = nodes.filter(it => it.startNode)[0]; + let currentNode = nodes.filter((it) => it.startNode)[0]; trajectory.push(currentNode); while (true) { // At every node, calculate all the possible next transitions. const lastSubmission = await getLastRelevantSubmission(currentNode, pathFor); const submitted = lastSubmission === null ? null : JSON.parse(lastSubmission.content); - const possibleTransitions = currentNode.transitions - .filter(it => transitionPossible(it, submitted)); + const possibleTransitions = currentNode.transitions.filter((it) => transitionPossible(it, submitted)); - if (possibleTransitions.length === 0) { // If there are none, the trajectory has ended. + if (possibleTransitions.length === 0) { + // If there are none, the trajectory has ended. return trajectory; - } // Otherwise, take the first possible transition. - currentNode = possibleTransitions[0].node; - trajectory.push(currentNode); - + } // Otherwise, take the first possible transition. + currentNode = possibleTransitions[0].node; + trajectory.push(currentNode); } - } + }, }; export default learningPathPersonalizingService; diff --git a/backend/src/services/learning-paths/learning-path-provider.ts b/backend/src/services/learning-paths/learning-path-provider.ts index f7cb1c12..baff35bf 100644 --- a/backend/src/services/learning-paths/learning-path-provider.ts +++ b/backend/src/services/learning-paths/learning-path-provider.ts @@ -1,5 +1,5 @@ -import {LearningPath, LearningPathResponse} from "../../interfaces/learning-content"; -import {Language} from "../../entities/content/language"; +import { LearningPath, LearningPathResponse } from '../../interfaces/learning-content'; +import { Language } from '../../entities/content/language'; /** * Generic interface for a service which provides access to learning paths from a data source. diff --git a/backend/src/services/learning-paths/learning-path-service.ts b/backend/src/services/learning-paths/learning-path-service.ts index a00b4633..23f0d9ba 100644 --- a/backend/src/services/learning-paths/learning-path-service.ts +++ b/backend/src/services/learning-paths/learning-path-service.ts @@ -1,14 +1,11 @@ -import { - LearningPath, - LearningPathResponse -} from "../../interfaces/learning-content"; -import dwengoApiLearningPathProvider from "./dwengo-api-learning-path-provider"; -import databaseLearningPathProvider from "./database-learning-path-provider"; -import {EnvVars, getEnvVar} from "../../util/envvars"; -import {Language} from "../../entities/content/language"; +import { LearningPath, LearningPathResponse } from '../../interfaces/learning-content'; +import dwengoApiLearningPathProvider from './dwengo-api-learning-path-provider'; +import databaseLearningPathProvider from './database-learning-path-provider'; +import { EnvVars, getEnvVar } from '../../util/envvars'; +import { Language } from '../../entities/content/language'; const userContentPrefix = getEnvVar(EnvVars.UserContentPrefix); -const allProviders = [dwengoApiLearningPathProvider, databaseLearningPathProvider] +const allProviders = [dwengoApiLearningPathProvider, databaseLearningPathProvider]; /** * Service providing access to data about learning paths from the appropriate data source (database or Dwengo-api) @@ -18,18 +15,16 @@ const learningPathService = { * Fetch the learning paths with the given hruids from the data source. */ async fetchLearningPaths(hruids: string[], language: Language, source: string): Promise { - const userContentHruids = hruids.filter(hruid => hruid.startsWith(userContentPrefix)); - const nonUserContentHruids = hruids.filter(hruid => !hruid.startsWith(userContentPrefix)); + const userContentHruids = hruids.filter((hruid) => hruid.startsWith(userContentPrefix)); + const nonUserContentHruids = hruids.filter((hruid) => !hruid.startsWith(userContentPrefix)); - const userContentLearningPaths = - await databaseLearningPathProvider.fetchLearningPaths(userContentHruids, language, source); - const nonUserContentLearningPaths - = await dwengoApiLearningPathProvider.fetchLearningPaths(nonUserContentHruids, language, source); + const userContentLearningPaths = await databaseLearningPathProvider.fetchLearningPaths(userContentHruids, language, source); + const nonUserContentLearningPaths = await dwengoApiLearningPathProvider.fetchLearningPaths(nonUserContentHruids, language, source); return { data: (userContentLearningPaths.data || []).concat(nonUserContentLearningPaths.data || []), source: source, - success: userContentLearningPaths.success || nonUserContentLearningPaths.success + success: userContentLearningPaths.success || nonUserContentLearningPaths.success, }; }, @@ -37,13 +32,9 @@ const learningPathService = { * Search learning paths in the data source using the given search string. */ async searchLearningPaths(query: string, language: Language): Promise { - const providerResponses = await Promise.all( - allProviders.map( - provider => provider.searchLearningPaths(query, language) - ) - ); + const providerResponses = await Promise.all(allProviders.map((provider) => provider.searchLearningPaths(query, language))); return providerResponses.flat(); - } -} + }, +}; export default learningPathService; diff --git a/backend/src/util/apiHelper.ts b/backend/src/util/apiHelper.ts index a7600541..fff7a6a1 100644 --- a/backend/src/util/apiHelper.ts +++ b/backend/src/util/apiHelper.ts @@ -17,9 +17,9 @@ export async function fetchWithLogging( url: string, description: string, options?: { - params?: Record, - query?: Record, - responseType?: "json" | "text", + params?: Record; + query?: Record; + responseType?: 'json' | 'text'; } ): Promise { try { diff --git a/backend/src/util/async.ts b/backend/src/util/async.ts index 874c0b8e..a5fc9b82 100644 --- a/backend/src/util/async.ts +++ b/backend/src/util/async.ts @@ -16,7 +16,7 @@ export async function replaceAsync(str: string, regex: RegExp, replacementFn: (m }); // Wait for the replacements to get loaded. Reverse them so when popping them, we work in a FIFO manner. - const replacements: string[] = (await Promise.all(promises)); + const replacements: string[] = await Promise.all(promises); // Second run through matches: Replace them by their previously computed replacements. return str.replace(regex, () => replacements.pop()!); diff --git a/backend/src/util/envvars.ts b/backend/src/util/envvars.ts index eed6dce7..115291af 100644 --- a/backend/src/util/envvars.ts +++ b/backend/src/util/envvars.ts @@ -15,9 +15,9 @@ export const EnvVars: { [key: string]: EnvVar } = { DbUsername: { key: DB_PREFIX + 'USERNAME', required: true }, DbPassword: { key: DB_PREFIX + 'PASSWORD', required: true }, DbUpdate: { key: DB_PREFIX + 'UPDATE', defaultValue: false }, - LearningContentRepoApiBaseUrl: { key: PREFIX + "LEARNING_CONTENT_REPO_API_BASE_URL", defaultValue: "https://dwengo.org/backend/api"}, - FallbackLanguage: { key: PREFIX + "FALLBACK_LANGUAGE", defaultValue: "nl" }, - UserContentPrefix: { key: DB_PREFIX + 'USER_CONTENT_PREFIX', defaultValue: "u_" }, + LearningContentRepoApiBaseUrl: { key: PREFIX + 'LEARNING_CONTENT_REPO_API_BASE_URL', defaultValue: 'https://dwengo.org/backend/api' }, + FallbackLanguage: { key: PREFIX + 'FALLBACK_LANGUAGE', defaultValue: 'nl' }, + UserContentPrefix: { key: DB_PREFIX + 'USER_CONTENT_PREFIX', defaultValue: 'u_' }, IdpStudentUrl: { key: STUDENT_IDP_PREFIX + 'URL', required: true }, IdpStudentClientId: { key: STUDENT_IDP_PREFIX + 'CLIENT_ID', required: true }, IdpStudentJwksEndpoint: { key: STUDENT_IDP_PREFIX + 'JWKS_ENDPOINT', required: true }, diff --git a/backend/src/util/links.ts b/backend/src/util/links.ts index 5deb21c2..73e27965 100644 --- a/backend/src/util/links.ts +++ b/backend/src/util/links.ts @@ -1,9 +1,9 @@ -import {LearningObjectIdentifier} from "../interfaces/learning-content"; +import { LearningObjectIdentifier } from '../interfaces/learning-content'; export function isValidHttpUrl(url: string): boolean { try { - const parsedUrl = new URL(url, "http://test.be"); - return parsedUrl.protocol === "http:" || parsedUrl.protocol === "https:"; + const parsedUrl = new URL(url, 'http://test.be'); + return parsedUrl.protocol === 'http:' || parsedUrl.protocol === 'https:'; } catch (e) { return false; } diff --git a/backend/tests/data/content/attachment-repository.test.ts b/backend/tests/data/content/attachment-repository.test.ts index e6c4e44a..b4ce9583 100644 --- a/backend/tests/data/content/attachment-repository.test.ts +++ b/backend/tests/data/content/attachment-repository.test.ts @@ -1,21 +1,21 @@ -import {beforeAll, describe, expect, it} from "vitest"; -import {setupTestApp} from "../../setup-tests"; -import {getAttachmentRepository, getLearningObjectRepository} from "../../../src/data/repositories"; -import {AttachmentRepository} from "../../../src/data/content/attachment-repository"; -import {LearningObjectRepository} from "../../../src/data/content/learning-object-repository"; -import example from "../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example"; -import {LearningObject} from "../../../src/entities/content/learning-object.entity"; -import {Attachment} from "../../../src/entities/content/attachment.entity"; -import {LearningObjectIdentifier} from "../../../src/entities/content/learning-object-identifier"; +import { beforeAll, describe, expect, it } from 'vitest'; +import { setupTestApp } from '../../setup-tests'; +import { getAttachmentRepository, getLearningObjectRepository } from '../../../src/data/repositories'; +import { AttachmentRepository } from '../../../src/data/content/attachment-repository'; +import { LearningObjectRepository } from '../../../src/data/content/learning-object-repository'; +import example from '../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example'; +import { LearningObject } from '../../../src/entities/content/learning-object.entity'; +import { Attachment } from '../../../src/entities/content/attachment.entity'; +import { LearningObjectIdentifier } from '../../../src/entities/content/learning-object-identifier'; -const NEWER_TEST_SUFFIX = "nEweR"; +const NEWER_TEST_SUFFIX = 'nEweR'; -function createTestLearningObjects(learningObjectRepo: LearningObjectRepository): {older: LearningObject, newer: LearningObject} { +function createTestLearningObjects(learningObjectRepo: LearningObjectRepository): { older: LearningObject; newer: LearningObject } { const olderExample = example.createLearningObject(); learningObjectRepo.save(olderExample); const newerExample = example.createLearningObject(); - newerExample.title = "Newer example"; + newerExample.title = 'Newer example'; newerExample.version = 100; return { @@ -24,9 +24,9 @@ function createTestLearningObjects(learningObjectRepo: LearningObjectRepository) }; } -describe("AttachmentRepository", () => { +describe('AttachmentRepository', () => { let attachmentRepo: AttachmentRepository; - let exampleLearningObjects: {older: LearningObject, newer: LearningObject}; + let exampleLearningObjects: { older: LearningObject; newer: LearningObject }; let attachmentsOlderLearningObject: Attachment[]; beforeAll(async () => { @@ -35,10 +35,8 @@ describe("AttachmentRepository", () => { exampleLearningObjects = createTestLearningObjects(getLearningObjectRepository()); }); - it("can add attachments to learning objects without throwing an error", () => { - attachmentsOlderLearningObject = Object - .values(example.createAttachment) - .map(fn => fn(exampleLearningObjects.older)); + it('can add attachments to learning objects without throwing an error', () => { + attachmentsOlderLearningObject = Object.values(example.createAttachment).map((fn) => fn(exampleLearningObjects.older)); for (const attachment of attachmentsOlderLearningObject) { attachmentRepo.save(attachment); @@ -46,7 +44,7 @@ describe("AttachmentRepository", () => { }); let attachmentOnlyNewer: Attachment; - it("allows us to add attachments with the same name to a different learning object without throwing an error", () => { + it('allows us to add attachments with the same name to a different learning object without throwing an error', () => { attachmentOnlyNewer = Object.values(example.createAttachment)[0](exampleLearningObjects.newer); attachmentOnlyNewer.content.write(NEWER_TEST_SUFFIX); @@ -54,29 +52,23 @@ describe("AttachmentRepository", () => { }); let olderLearningObjectId: LearningObjectIdentifier; - it("returns the correct attachment when queried by learningObjectId and attachment name", async () => { + it('returns the correct attachment when queried by learningObjectId and attachment name', async () => { olderLearningObjectId = { hruid: exampleLearningObjects.older.hruid, language: exampleLearningObjects.older.language, - version: exampleLearningObjects.older.version + version: exampleLearningObjects.older.version, }; - const result = await attachmentRepo.findByLearningObjectIdAndName( - olderLearningObjectId, - attachmentsOlderLearningObject[0].name - ); + const result = await attachmentRepo.findByLearningObjectIdAndName(olderLearningObjectId, attachmentsOlderLearningObject[0].name); expect(result).toBe(attachmentsOlderLearningObject[0]); }); - it("returns null when queried by learningObjectId and non-existing attachment name", async () => { - const result = await attachmentRepo.findByLearningObjectIdAndName( - olderLearningObjectId, - "non-existing name" - ); + it('returns null when queried by learningObjectId and non-existing attachment name', async () => { + const result = await attachmentRepo.findByLearningObjectIdAndName(olderLearningObjectId, 'non-existing name'); expect(result).toBe(null); }); - it("returns the newer version of the attachment when only queried by hruid, language and attachment name (but not version)", async () => { + it('returns the newer version of the attachment when only queried by hruid, language and attachment name (but not version)', async () => { const result = await attachmentRepo.findByMostRecentVersionOfLearningObjectAndName( exampleLearningObjects.older.hruid, exampleLearningObjects.older.language, diff --git a/backend/tests/data/content/learning-object-repository.test.ts b/backend/tests/data/content/learning-object-repository.test.ts index bcca4c83..48c7494f 100644 --- a/backend/tests/data/content/learning-object-repository.test.ts +++ b/backend/tests/data/content/learning-object-repository.test.ts @@ -1,12 +1,12 @@ -import {beforeAll, describe, it, expect} from "vitest"; -import {LearningObjectRepository} from "../../../src/data/content/learning-object-repository"; -import {setupTestApp} from "../../setup-tests"; -import {getLearningObjectRepository} from "../../../src/data/repositories"; -import example from "../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example.js" -import {LearningObject} from "../../../src/entities/content/learning-object.entity"; -import {expectToBeCorrectEntity} from "../../test-utils/expectations"; +import { beforeAll, describe, it, expect } from 'vitest'; +import { LearningObjectRepository } from '../../../src/data/content/learning-object-repository'; +import { setupTestApp } from '../../setup-tests'; +import { getLearningObjectRepository } from '../../../src/data/repositories'; +import example from '../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example.js'; +import { LearningObject } from '../../../src/entities/content/learning-object.entity'; +import { expectToBeCorrectEntity } from '../../test-utils/expectations'; -describe("LearningObjectRepository", () => { +describe('LearningObjectRepository', () => { let learningObjectRepository: LearningObjectRepository; let exampleLearningObject: LearningObject; @@ -16,55 +16,57 @@ describe("LearningObjectRepository", () => { learningObjectRepository = getLearningObjectRepository(); }); - it("should be able to add a learning object to it without an error", async () => { + it('should be able to add a learning object to it without an error', async () => { exampleLearningObject = example.createLearningObject(); await learningObjectRepository.insert(exampleLearningObject); }); - it("should return the learning object when queried by id", async () => { + it('should return the learning object when queried by id', async () => { const result = await learningObjectRepository.findByIdentifier({ hruid: exampleLearningObject.hruid, language: exampleLearningObject.language, - version: exampleLearningObject.version + version: exampleLearningObject.version, }); expect(result).toBeInstanceOf(LearningObject); - expectToBeCorrectEntity({ - name: "actual", - entity: result! - }, { - name: "expected", - entity: exampleLearningObject - }); + expectToBeCorrectEntity( + { + name: 'actual', + entity: result!, + }, + { + name: 'expected', + entity: exampleLearningObject, + } + ); }); - it("should return null when non-existing version is queried", async () => { + it('should return null when non-existing version is queried', async () => { const result = await learningObjectRepository.findByIdentifier({ hruid: exampleLearningObject.hruid, language: exampleLearningObject.language, - version: 100 + version: 100, }); expect(result).toBe(null); }); let newerExample: LearningObject; - it("should allow a learning object with the same id except a different version to be added", async () => { + it('should allow a learning object with the same id except a different version to be added', async () => { newerExample = example.createLearningObject(); newerExample.version = 10; - newerExample.title += " (nieuw)"; + newerExample.title += ' (nieuw)'; await learningObjectRepository.save(newerExample); }); - it("should return the newest version of the learning object when queried by only hruid and language", async () => { + it('should return the newest version of the learning object when queried by only hruid and language', async () => { const result = await learningObjectRepository.findLatestByHruidAndLanguage(newerExample.hruid, newerExample.language); expect(result).toBeInstanceOf(LearningObject); expect(result?.version).toBe(10); - expect(result?.title).toContain("(nieuw)"); + expect(result?.title).toContain('(nieuw)'); }); - it("should return null when queried by non-existing hruid or language", async () => { - const result = await learningObjectRepository.findLatestByHruidAndLanguage("something_that_does_not_exist", exampleLearningObject.language); + it('should return null when queried by non-existing hruid or language', async () => { + const result = await learningObjectRepository.findLatestByHruidAndLanguage('something_that_does_not_exist', exampleLearningObject.language); expect(result).toBe(null); }); - }); diff --git a/backend/tests/data/content/learning-path-repository.test.ts b/backend/tests/data/content/learning-path-repository.test.ts index 5298d3c4..0b8b6a90 100644 --- a/backend/tests/data/content/learning-path-repository.test.ts +++ b/backend/tests/data/content/learning-path-repository.test.ts @@ -1,24 +1,24 @@ -import {beforeAll, describe, expect, it} from "vitest"; -import {setupTestApp} from "../../setup-tests"; -import {getLearningPathRepository} from "../../../src/data/repositories"; -import {LearningPathRepository} from "../../../src/data/content/learning-path-repository"; -import example from "../../test-assets/learning-paths/pn-werking-example"; -import {LearningPath} from "../../../src/entities/content/learning-path.entity"; -import {expectToBeCorrectEntity} from "../../test-utils/expectations"; -import {Language} from "../../../src/entities/content/language"; +import { beforeAll, describe, expect, it } from 'vitest'; +import { setupTestApp } from '../../setup-tests'; +import { getLearningPathRepository } from '../../../src/data/repositories'; +import { LearningPathRepository } from '../../../src/data/content/learning-path-repository'; +import example from '../../test-assets/learning-paths/pn-werking-example'; +import { LearningPath } from '../../../src/entities/content/learning-path.entity'; +import { expectToBeCorrectEntity } from '../../test-utils/expectations'; +import { Language } from '../../../src/entities/content/language'; function expectToHaveFoundPrecisely(expected: LearningPath, result: LearningPath[]): void { - expect(result).toHaveProperty("length"); + expect(result).toHaveProperty('length'); expect(result.length).toBe(1); expectToBeCorrectEntity({ entity: result[0]! }, { entity: expected }); } function expectToHaveFoundNothing(result: LearningPath[]): void { - expect(result).toHaveProperty("length"); + expect(result).toHaveProperty('length'); expect(result.length).toBe(0); } -describe("LearningPathRepository", () => { +describe('LearningPathRepository', () => { let learningPathRepo: LearningPathRepository; beforeAll(async () => { @@ -28,51 +28,39 @@ describe("LearningPathRepository", () => { let examplePath: LearningPath; - it("should be able to add a learning path without throwing an error", async () => { + it('should be able to add a learning path without throwing an error', async () => { examplePath = example.createLearningPath(); await learningPathRepo.insert(examplePath); }); - it("should return the added path when it is queried by hruid and language", async () => { + it('should return the added path when it is queried by hruid and language', async () => { const result = await learningPathRepo.findByHruidAndLanguage(examplePath.hruid, examplePath.language); expect(result).toBeInstanceOf(LearningPath); expectToBeCorrectEntity({ entity: result! }, { entity: examplePath }); }); - it("should return null to a query on a non-existing hruid or language", async () => { - const result = await learningPathRepo.findByHruidAndLanguage("not_existing_hruid", examplePath.language); + it('should return null to a query on a non-existing hruid or language', async () => { + const result = await learningPathRepo.findByHruidAndLanguage('not_existing_hruid', examplePath.language); expect(result).toBe(null); }); - it("should return the learning path when we search for a search term occurring in its title", async () => { - const result = await learningPathRepo.findByQueryStringAndLanguage( - examplePath.title.slice(4, 9), - examplePath.language - ); + it('should return the learning path when we search for a search term occurring in its title', async () => { + const result = await learningPathRepo.findByQueryStringAndLanguage(examplePath.title.slice(4, 9), examplePath.language); expectToHaveFoundPrecisely(examplePath, result); }); - it("should return the learning path when we search for a search term occurring in its description", async () => { - const result = await learningPathRepo.findByQueryStringAndLanguage( - examplePath.description.slice(8, 15), - examplePath.language - ); + it('should return the learning path when we search for a search term occurring in its description', async () => { + const result = await learningPathRepo.findByQueryStringAndLanguage(examplePath.description.slice(8, 15), examplePath.language); expectToHaveFoundPrecisely(examplePath, result); }); - it("should return null when we search for something not occurring in its title or description", async () => { - const result = await learningPathRepo.findByQueryStringAndLanguage( - "something not occurring in the path", - examplePath.language - ); + it('should return null when we search for something not occurring in its title or description', async () => { + const result = await learningPathRepo.findByQueryStringAndLanguage('something not occurring in the path', examplePath.language); expectToHaveFoundNothing(result); }); - it("should return null when we search for something occurring in its title, but another language", async () => { - const result = await learningPathRepo.findByQueryStringAndLanguage( - examplePath.description.slice(1, 3), - Language.Kalaallisut - ); + it('should return null when we search for something occurring in its title, but another language', async () => { + const result = await learningPathRepo.findByQueryStringAndLanguage(examplePath.description.slice(1, 3), Language.Kalaallisut); expectToHaveFoundNothing(result); }); }); diff --git a/backend/tests/services/learning-objects/database-learning-object-provider.test.ts b/backend/tests/services/learning-objects/database-learning-object-provider.test.ts index e3960f64..a37704f0 100644 --- a/backend/tests/services/learning-objects/database-learning-object-provider.test.ts +++ b/backend/tests/services/learning-objects/database-learning-object-provider.test.ts @@ -1,15 +1,13 @@ -import {beforeAll, describe, expect, it} from "vitest"; -import {setupTestApp} from "../../setup-tests"; -import {getLearningObjectRepository} from "../../../src/data/repositories"; -import example from "../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example"; -import {LearningObject} from "../../../src/entities/content/learning-object.entity"; -import databaseLearningObjectProvider from "../../../src/services/learning-objects/database-learning-object-provider"; -import { - createExampleLearningObjectWithAttachments -} from "../../test-assets/learning-objects/create-example-learning-object-with-attachments"; -import {expectToBeCorrectFilteredLearningObject} from "../../test-utils/expectations"; -import {FilteredLearningObject} from "../../../src/interfaces/learning-content"; -import {Language} from "../../../src/entities/content/language"; +import { beforeAll, describe, expect, it } from 'vitest'; +import { setupTestApp } from '../../setup-tests'; +import { getLearningObjectRepository } from '../../../src/data/repositories'; +import example from '../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example'; +import { LearningObject } from '../../../src/entities/content/learning-object.entity'; +import databaseLearningObjectProvider from '../../../src/services/learning-objects/database-learning-object-provider'; +import { createExampleLearningObjectWithAttachments } from '../../test-assets/learning-objects/create-example-learning-object-with-attachments'; +import { expectToBeCorrectFilteredLearningObject } from '../../test-utils/expectations'; +import { FilteredLearningObject } from '../../../src/interfaces/learning-content'; +import { Language } from '../../../src/entities/content/language'; async function initExampleData(): Promise { const learningObjectRepo = getLearningObjectRepository(); @@ -18,39 +16,39 @@ async function initExampleData(): Promise { return exampleLearningObject; } -describe("DatabaseLearningObjectProvider", () => { +describe('DatabaseLearningObjectProvider', () => { let exampleLearningObject: LearningObject; beforeAll(async () => { await setupTestApp(); exampleLearningObject = await initExampleData(); }); - describe("getLearningObjectById", () => { - it("should return the learning object when it is queried by its id", async () => { + describe('getLearningObjectById', () => { + it('should return the learning object when it is queried by its id', async () => { const result: FilteredLearningObject | null = await databaseLearningObjectProvider.getLearningObjectById(exampleLearningObject); expect(result).toBeTruthy(); expectToBeCorrectFilteredLearningObject(result!, exampleLearningObject); }); - it("should return the learning object when it is queried by only hruid and language (but not version)", async () => { + it('should return the learning object when it is queried by only hruid and language (but not version)', async () => { const result: FilteredLearningObject | null = await databaseLearningObjectProvider.getLearningObjectById({ hruid: exampleLearningObject.hruid, - language: exampleLearningObject.language + language: exampleLearningObject.language, }); expect(result).toBeTruthy(); expectToBeCorrectFilteredLearningObject(result!, exampleLearningObject); }); - it("should return null when queried with an id that does not exist", async () => { + it('should return null when queried with an id that does not exist', async () => { const result: FilteredLearningObject | null = await databaseLearningObjectProvider.getLearningObjectById({ - hruid: "non_existing_hruid", - language: Language.Dutch + hruid: 'non_existing_hruid', + language: Language.Dutch, }); expect(result).toBeNull(); }); }); - describe("getLearningObjectHTML", () => { - it("should return the correct rendering of the learning object", async () => { + describe('getLearningObjectHTML', () => { + it('should return the correct rendering of the learning object', async () => { const result = await databaseLearningObjectProvider.getLearningObjectHTML(exampleLearningObject); expect(result).toEqual(example.getHTMLRendering()); }); diff --git a/backend/tests/services/learning-objects/learning-object-service.test.ts b/backend/tests/services/learning-objects/learning-object-service.test.ts index 2a68d797..26f03595 100644 --- a/backend/tests/services/learning-objects/learning-object-service.test.ts +++ b/backend/tests/services/learning-objects/learning-object-service.test.ts @@ -1,30 +1,30 @@ -import {beforeAll, describe, expect, it} from "vitest"; -import {setupTestApp} from "../../setup-tests"; -import {LearningObject} from "../../../src/entities/content/learning-object.entity"; -import {getLearningObjectRepository} from "../../../src/data/repositories"; -import learningObjectExample from "../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example"; -import learningObjectService from "../../../src/services/learning-objects/learning-object-service"; -import {LearningObjectIdentifier} from "../../../src/interfaces/learning-content"; -import {Language} from "../../../src/entities/content/language"; -import {EnvVars, getEnvVar} from "../../../src/util/envvars"; +import { beforeAll, describe, expect, it } from 'vitest'; +import { setupTestApp } from '../../setup-tests'; +import { LearningObject } from '../../../src/entities/content/learning-object.entity'; +import { getLearningObjectRepository } from '../../../src/data/repositories'; +import learningObjectExample from '../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example'; +import learningObjectService from '../../../src/services/learning-objects/learning-object-service'; +import { LearningObjectIdentifier } from '../../../src/interfaces/learning-content'; +import { Language } from '../../../src/entities/content/language'; +import { EnvVars, getEnvVar } from '../../../src/util/envvars'; -const TEST_LEARNING_OBJECT_TITLE = "Test title"; -const EXPECTED_DWENGO_LEARNING_OBJECT_TITLE = "Werken met notebooks"; +const TEST_LEARNING_OBJECT_TITLE = 'Test title'; +const EXPECTED_DWENGO_LEARNING_OBJECT_TITLE = 'Werken met notebooks'; const DWENGO_TEST_LEARNING_OBJECT_ID: LearningObjectIdentifier = { - hruid: "pn_werkingnotebooks", + hruid: 'pn_werkingnotebooks', language: Language.Dutch, - version: 3 + version: 3, }; async function initExampleData(): Promise { const learningObjectRepo = getLearningObjectRepository(); const learningObject = learningObjectExample.createLearningObject(); - learningObject.title = TEST_LEARNING_OBJECT_TITLE + learningObject.title = TEST_LEARNING_OBJECT_TITLE; await learningObjectRepo.save(learningObject); return learningObject; } -describe("LearningObjectService", () => { +describe('LearningObjectService', () => { let exampleLearningObject: LearningObject; beforeAll(async () => { @@ -32,47 +32,50 @@ describe("LearningObjectService", () => { exampleLearningObject = await initExampleData(); }); - describe("getLearningObjectById", () => { - it("returns the learning object from the Dwengo API if it does not have the user content prefix", async () => { + describe('getLearningObjectById', () => { + it('returns the learning object from the Dwengo API if it does not have the user content prefix', async () => { const result = await learningObjectService.getLearningObjectById(DWENGO_TEST_LEARNING_OBJECT_ID); expect(result).not.toBeNull(); expect(result?.title).toBe(EXPECTED_DWENGO_LEARNING_OBJECT_TITLE); }); - it("returns the learning object from the database if it does have the user content prefix", async () => { + it('returns the learning object from the database if it does have the user content prefix', async () => { const result = await learningObjectService.getLearningObjectById(exampleLearningObject); expect(result).not.toBeNull(); expect(result?.title).toBe(exampleLearningObject.title); }); - it("returns null if the hruid does not have the user content prefix and does not exist in the Dwengo repo", async () => { + it('returns null if the hruid does not have the user content prefix and does not exist in the Dwengo repo', async () => { const result = await learningObjectService.getLearningObjectById({ - hruid: "non-existing", - language: Language.Dutch + hruid: 'non-existing', + language: Language.Dutch, }); expect(result).toBeNull(); }); }); - describe("getLearningObjectHTML", () => { - it("returns the expected HTML when queried with the identifier of a learning object saved in the database", async () => { + describe('getLearningObjectHTML', () => { + it('returns the expected HTML when queried with the identifier of a learning object saved in the database', async () => { const result = await learningObjectService.getLearningObjectHTML(exampleLearningObject); expect(result).not.toBeNull(); expect(result).toEqual(learningObjectExample.getHTMLRendering()); }); - it("returns the same HTML as the Dwengo API when queried with the identifier of a learning object that does " + - "not start with the user content prefix", async () => { - const result = await learningObjectService.getLearningObjectHTML(DWENGO_TEST_LEARNING_OBJECT_ID); - expect(result).not.toBeNull(); + it( + 'returns the same HTML as the Dwengo API when queried with the identifier of a learning object that does ' + + 'not start with the user content prefix', + async () => { + const result = await learningObjectService.getLearningObjectHTML(DWENGO_TEST_LEARNING_OBJECT_ID); + expect(result).not.toBeNull(); - const htmlFromDwengoApi = await fetch( - getEnvVar(EnvVars.LearningContentRepoApiBaseUrl) - + `/learningObject/getRaw?hruid=${DWENGO_TEST_LEARNING_OBJECT_ID.hruid}&language=${DWENGO_TEST_LEARNING_OBJECT_ID.language}&version=${DWENGO_TEST_LEARNING_OBJECT_ID.version}` - ).then(it => it.text()); - expect(result).toEqual(htmlFromDwengoApi); - }); - it("returns null when queried with a non-existing identifier", async () => { + const htmlFromDwengoApi = await fetch( + getEnvVar(EnvVars.LearningContentRepoApiBaseUrl) + + `/learningObject/getRaw?hruid=${DWENGO_TEST_LEARNING_OBJECT_ID.hruid}&language=${DWENGO_TEST_LEARNING_OBJECT_ID.language}&version=${DWENGO_TEST_LEARNING_OBJECT_ID.version}` + ).then((it) => it.text()); + expect(result).toEqual(htmlFromDwengoApi); + } + ); + it('returns null when queried with a non-existing identifier', async () => { const result = await learningObjectService.getLearningObjectHTML({ - hruid: "non_existing_hruid", - language: Language.Dutch + hruid: 'non_existing_hruid', + language: Language.Dutch, }); expect(result).toBeNull(); }); diff --git a/backend/tests/services/learning-objects/processing/processing-service.test.ts b/backend/tests/services/learning-objects/processing/processing-service.test.ts index f0107162..27714317 100644 --- a/backend/tests/services/learning-objects/processing/processing-service.test.ts +++ b/backend/tests/services/learning-objects/processing/processing-service.test.ts @@ -1,23 +1,23 @@ -import {describe, expect, it} from "vitest"; -import mdExample from "../../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example"; -import multipleChoiceExample from "../../../test-assets/learning-objects/test-multiple-choice/test-multiple-choice-example"; -import essayExample from "../../../test-assets/learning-objects/test-essay/test-essay-example"; -import processingService from "../../../../src/services/learning-objects/processing/processing-service"; +import { describe, expect, it } from 'vitest'; +import mdExample from '../../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example'; +import multipleChoiceExample from '../../../test-assets/learning-objects/test-multiple-choice/test-multiple-choice-example'; +import essayExample from '../../../test-assets/learning-objects/test-essay/test-essay-example'; +import processingService from '../../../../src/services/learning-objects/processing/processing-service'; -describe("ProcessingService", () => { - it("renders a markdown learning object correctly", async () => { +describe('ProcessingService', () => { + it('renders a markdown learning object correctly', async () => { const markdownLearningObject = mdExample.createLearningObject(); const result = await processingService.render(markdownLearningObject); expect(result).toEqual(mdExample.getHTMLRendering()); }); - it("renders a multiple choice question correctly", async () => { + it('renders a multiple choice question correctly', async () => { const multipleChoiceLearningObject = multipleChoiceExample.createLearningObject(); const result = await processingService.render(multipleChoiceLearningObject); expect(result).toEqual(multipleChoiceExample.getHTMLRendering()); }); - it("renders an essay question correctly", async () => { + it('renders an essay question correctly', async () => { const essayLearningObject = essayExample.createLearningObject(); const result = await processingService.render(essayLearningObject); expect(result).toEqual(essayExample.getHTMLRendering()); diff --git a/backend/tests/services/learning-path/database-learning-path-provider.test.ts b/backend/tests/services/learning-path/database-learning-path-provider.test.ts index 8dd3d07b..df49ae3b 100644 --- a/backend/tests/services/learning-path/database-learning-path-provider.test.ts +++ b/backend/tests/services/learning-path/database-learning-path-provider.test.ts @@ -1,17 +1,17 @@ -import {beforeAll, describe, expect, it} from "vitest"; -import {LearningObject} from "../../../src/entities/content/learning-object.entity"; -import {setupTestApp} from "../../setup-tests"; -import {LearningPath} from "../../../src/entities/content/learning-path.entity"; -import {getLearningObjectRepository, getLearningPathRepository} from "../../../src/data/repositories"; -import learningObjectExample from "../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example"; -import learningPathExample from "../../test-assets/learning-paths/pn-werking-example" -import databaseLearningPathProvider from "../../../src/services/learning-paths/database-learning-path-provider"; -import {expectToBeCorrectLearningPath} from "../../test-utils/expectations"; -import {LearningObjectRepository} from "../../../src/data/content/learning-object-repository"; -import learningObjectService from "../../../src/services/learning-objects/learning-object-service"; -import {Language} from "../../../src/entities/content/language"; +import { beforeAll, describe, expect, it } from 'vitest'; +import { LearningObject } from '../../../src/entities/content/learning-object.entity'; +import { setupTestApp } from '../../setup-tests'; +import { LearningPath } from '../../../src/entities/content/learning-path.entity'; +import { getLearningObjectRepository, getLearningPathRepository } from '../../../src/data/repositories'; +import learningObjectExample from '../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example'; +import learningPathExample from '../../test-assets/learning-paths/pn-werking-example'; +import databaseLearningPathProvider from '../../../src/services/learning-paths/database-learning-path-provider'; +import { expectToBeCorrectLearningPath } from '../../test-utils/expectations'; +import { LearningObjectRepository } from '../../../src/data/content/learning-object-repository'; +import learningObjectService from '../../../src/services/learning-objects/learning-object-service'; +import { Language } from '../../../src/entities/content/language'; -async function initExampleData(): Promise<{ learningObject: LearningObject, learningPath: LearningPath }> { +async function initExampleData(): Promise<{ learningObject: LearningObject; learningPath: LearningPath }> { const learningObjectRepo = getLearningObjectRepository(); const learningPathRepo = getLearningPathRepository(); const learningObject = learningObjectExample.createLearningObject(); @@ -21,9 +21,9 @@ async function initExampleData(): Promise<{ learningObject: LearningObject, lear return { learningObject, learningPath }; } -describe("DatabaseLearningPathProvider", () => { +describe('DatabaseLearningPathProvider', () => { let learningObjectRepo: LearningObjectRepository; - let example: {learningObject: LearningObject, learningPath: LearningPath}; + let example: { learningObject: LearningObject; learningPath: LearningPath }; beforeAll(async () => { await setupTestApp(); @@ -31,40 +31,43 @@ describe("DatabaseLearningPathProvider", () => { learningObjectRepo = getLearningObjectRepository(); }); - describe("fetchLearningPaths", () => { - it("returns the learning path correctly", async () => { + describe('fetchLearningPaths', () => { + it('returns the learning path correctly', async () => { const result = await databaseLearningPathProvider.fetchLearningPaths( [example.learningPath.hruid], example.learningPath.language, - "the source" + 'the source' ); expect(result.success).toBe(true); expect(result.data?.length).toBe(1); - const learningObjectsOnPath = (await Promise.all( - example.learningPath.nodes.map(node => - learningObjectService.getLearningObjectById({ - hruid: node.learningObjectHruid, - version: node.version, - language: node.language - })) - )).filter(it => it !== null); + const learningObjectsOnPath = ( + await Promise.all( + example.learningPath.nodes.map((node) => + learningObjectService.getLearningObjectById({ + hruid: node.learningObjectHruid, + version: node.version, + language: node.language, + }) + ) + ) + ).filter((it) => it !== null); - expectToBeCorrectLearningPath(result.data![0], example.learningPath, learningObjectsOnPath) + expectToBeCorrectLearningPath(result.data![0], example.learningPath, learningObjectsOnPath); }); - it("returns a non-successful response if a non-existing learning path is queried", async () => { + it('returns a non-successful response if a non-existing learning path is queried', async () => { const result = await databaseLearningPathProvider.fetchLearningPaths( [example.learningPath.hruid], Language.Abkhazian, // Wrong language - "the source" + 'the source' ); expect(result.success).toBe(false); }); }); - describe("searchLearningPaths", () => { - it("returns the correct learning path when queried with a substring of its title", async () => { + describe('searchLearningPaths', () => { + it('returns the correct learning path when queried with a substring of its title', async () => { const result = await databaseLearningPathProvider.searchLearningPaths( example.learningPath.title.substring(2, 6), example.learningPath.language @@ -73,7 +76,7 @@ describe("DatabaseLearningPathProvider", () => { expect(result[0].title).toBe(example.learningPath.title); expect(result[0].description).toBe(example.learningPath.description); }); - it("returns the correct learning path when queried with a substring of the description", async () => { + it('returns the correct learning path when queried with a substring of the description', async () => { const result = await databaseLearningPathProvider.searchLearningPaths( example.learningPath.description.substring(5, 12), example.learningPath.language @@ -82,9 +85,9 @@ describe("DatabaseLearningPathProvider", () => { expect(result[0].title).toBe(example.learningPath.title); expect(result[0].description).toBe(example.learningPath.description); }); - it("returns an empty result when queried with a text which is not a substring of the title or the description of a learning path", async () => { + it('returns an empty result when queried with a text which is not a substring of the title or the description of a learning path', async () => { const result = await databaseLearningPathProvider.searchLearningPaths( - "substring which does not occur in the title or the description of a learning object", + 'substring which does not occur in the title or the description of a learning object', example.learningPath.language ); expect(result.length).toBe(0); diff --git a/backend/tests/services/learning-path/learning-path-service.test.ts b/backend/tests/services/learning-path/learning-path-service.test.ts index b64d527a..cceb19be 100644 --- a/backend/tests/services/learning-path/learning-path-service.test.ts +++ b/backend/tests/services/learning-path/learning-path-service.test.ts @@ -1,14 +1,14 @@ -import {beforeAll, describe, expect, it} from "vitest"; -import {setupTestApp} from "../../setup-tests"; -import {LearningObject} from "../../../src/entities/content/learning-object.entity"; -import {LearningPath} from "../../../src/entities/content/learning-path.entity"; -import {getLearningObjectRepository, getLearningPathRepository} from "../../../src/data/repositories"; -import learningObjectExample from "../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example"; -import learningPathExample from "../../test-assets/learning-paths/pn-werking-example"; -import {Language} from "../../../src/entities/content/language"; -import learningPathService from "../../../src/services/learning-paths/learning-path-service"; +import { beforeAll, describe, expect, it } from 'vitest'; +import { setupTestApp } from '../../setup-tests'; +import { LearningObject } from '../../../src/entities/content/learning-object.entity'; +import { LearningPath } from '../../../src/entities/content/learning-path.entity'; +import { getLearningObjectRepository, getLearningPathRepository } from '../../../src/data/repositories'; +import learningObjectExample from '../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example'; +import learningPathExample from '../../test-assets/learning-paths/pn-werking-example'; +import { Language } from '../../../src/entities/content/language'; +import learningPathService from '../../../src/services/learning-paths/learning-path-service'; -async function initExampleData(): Promise<{ learningObject: LearningObject, learningPath: LearningPath }> { +async function initExampleData(): Promise<{ learningObject: LearningObject; learningPath: LearningPath }> { const learningObjectRepo = getLearningObjectRepository(); const learningPathRepo = getLearningPathRepository(); const learningObject = learningObjectExample.createLearningObject(); @@ -18,88 +18,63 @@ async function initExampleData(): Promise<{ learningObject: LearningObject, lear return { learningObject, learningPath }; } -const TEST_DWENGO_LEARNING_PATH_HRUID = "pn_werking"; -const TEST_DWENGO_LEARNING_PATH_TITLE = "Werken met notebooks"; -const TEST_DWENGO_EXCLUSIVE_LEARNING_PATH_SEARCH_QUERY = "Microscopie"; -const TEST_SEARCH_QUERY_EXPECTING_NO_MATCHES = "su$m8f9usf89ud { - let example: { learningObject: LearningObject, learningPath: LearningPath }; +describe('LearningPathService', () => { + let example: { learningObject: LearningObject; learningPath: LearningPath }; beforeAll(async () => { await setupTestApp(); example = await initExampleData(); }); - describe("fetchLearningPaths", () => { - it("should return learning paths both from the database and from the Dwengo API", async () => { + describe('fetchLearningPaths', () => { + it('should return learning paths both from the database and from the Dwengo API', async () => { const result = await learningPathService.fetchLearningPaths( [example.learningPath.hruid, TEST_DWENGO_LEARNING_PATH_HRUID], example.learningPath.language, - "the source" + 'the source' ); expect(result.success).toBeTruthy(); - expect(result.data?.filter(it => it.hruid == TEST_DWENGO_LEARNING_PATH_HRUID).length).not.toBe(0); - expect(result.data?.filter(it => it.hruid == example.learningPath.hruid).length).not.toBe(0); - expect(result.data?.filter(it => it.hruid == TEST_DWENGO_LEARNING_PATH_HRUID)[0].title) - .toEqual(TEST_DWENGO_LEARNING_PATH_TITLE); - expect(result.data?.filter(it => it.hruid == example.learningPath.hruid)[0].title) - .toEqual(example.learningPath.title); + expect(result.data?.filter((it) => it.hruid == TEST_DWENGO_LEARNING_PATH_HRUID).length).not.toBe(0); + expect(result.data?.filter((it) => it.hruid == example.learningPath.hruid).length).not.toBe(0); + expect(result.data?.filter((it) => it.hruid == TEST_DWENGO_LEARNING_PATH_HRUID)[0].title).toEqual(TEST_DWENGO_LEARNING_PATH_TITLE); + expect(result.data?.filter((it) => it.hruid == example.learningPath.hruid)[0].title).toEqual(example.learningPath.title); }); - it("should include both the learning objects from the Dwengo API and learning objects from the database in its response", async () => { - const result = await learningPathService.fetchLearningPaths( - [example.learningPath.hruid], - example.learningPath.language, - "the source" - ); + it('should include both the learning objects from the Dwengo API and learning objects from the database in its response', async () => { + const result = await learningPathService.fetchLearningPaths([example.learningPath.hruid], example.learningPath.language, 'the source'); expect(result.success).toBeTruthy(); expect(result.data?.length).toBe(1); // Should include all the nodes, even those pointing to foreign learning objects. - expect( - [...result.data![0].nodes.map(it => it.learningobject_hruid)].sort() - ).toEqual( - example.learningPath.nodes.map(it => it.learningObjectHruid).sort() + expect([...result.data![0].nodes.map((it) => it.learningobject_hruid)].sort()).toEqual( + example.learningPath.nodes.map((it) => it.learningObjectHruid).sort() ); }); }); - describe("searchLearningPath", () => { - it("should include both the learning paths from the Dwengo API and those from the database in its response", async () => { + describe('searchLearningPath', () => { + it('should include both the learning paths from the Dwengo API and those from the database in its response', async () => { // This matches the learning object in the database, but definitely also some learning objects in the Dwengo API. - const result = await learningPathService.searchLearningPaths( - example.learningPath.title.substring(2, 3), - example.learningPath.language - ); + const result = await learningPathService.searchLearningPaths(example.learningPath.title.substring(2, 3), example.learningPath.language); // Should find the one from the database - expect( - result.filter(it => - it.hruid === example.learningPath.hruid && it.title === example.learningPath.title - ).length - ).toBe(1); + expect(result.filter((it) => it.hruid === example.learningPath.hruid && it.title === example.learningPath.title).length).toBe(1); // But should not only find that one. expect(result.length).not.toBeLessThan(2); }); - it("should still return results from the Dwengo API even though there are no matches in the database", async () => { - const result = await learningPathService.searchLearningPaths( - TEST_DWENGO_EXCLUSIVE_LEARNING_PATH_SEARCH_QUERY, - Language.Dutch - ); + it('should still return results from the Dwengo API even though there are no matches in the database', async () => { + const result = await learningPathService.searchLearningPaths(TEST_DWENGO_EXCLUSIVE_LEARNING_PATH_SEARCH_QUERY, Language.Dutch); // Should find something... expect(result.length).not.toBe(0); // But not the example learning path. - expect( - result.filter(it => - it.hruid === example.learningPath.hruid && it.title === example.learningPath.title - ).length - ).toBe(0); + expect(result.filter((it) => it.hruid === example.learningPath.hruid && it.title === example.learningPath.title).length).toBe(0); }); - it("should return an empty list if neither the Dwengo API nor the database contains matches", async () => { - const result = await learningPathService.searchLearningPaths( - TEST_SEARCH_QUERY_EXPECTING_NO_MATCHES, - Language.Dutch - ); + it('should return an empty list if neither the Dwengo API nor the database contains matches', async () => { + const result = await learningPathService.searchLearningPaths(TEST_SEARCH_QUERY_EXPECTING_NO_MATCHES, Language.Dutch); expect(result.length).toBe(0); }); }); diff --git a/backend/tests/test-assets/learning-objects/create-example-learning-object-with-attachments.ts b/backend/tests/test-assets/learning-objects/create-example-learning-object-with-attachments.ts index c0177fbf..9bd0b4c3 100644 --- a/backend/tests/test-assets/learning-objects/create-example-learning-object-with-attachments.ts +++ b/backend/tests/test-assets/learning-objects/create-example-learning-object-with-attachments.ts @@ -1,5 +1,5 @@ -import {LearningObjectExample} from "./learning-object-example"; -import {LearningObject} from "../../../src/entities/content/learning-object.entity"; +import { LearningObjectExample } from './learning-object-example'; +import { LearningObject } from '../../../src/entities/content/learning-object.entity'; export function createExampleLearningObjectWithAttachments(example: LearningObjectExample): LearningObject { const learningObject = example.createLearningObject(); diff --git a/backend/tests/test-assets/learning-objects/dummy/dummy-learning-object-example.ts b/backend/tests/test-assets/learning-objects/dummy/dummy-learning-object-example.ts index dfc15a7a..5b773d09 100644 --- a/backend/tests/test-assets/learning-objects/dummy/dummy-learning-object-example.ts +++ b/backend/tests/test-assets/learning-objects/dummy/dummy-learning-object-example.ts @@ -1,8 +1,8 @@ -import {LearningObjectExample} from "../learning-object-example"; -import {LearningObject} from "../../../../src/entities/content/learning-object.entity"; -import {Language} from "../../../../src/entities/content/language"; -import {loadTestAsset} from "../../../test-utils/load-test-asset"; -import {DwengoContentType} from "../../../../src/services/learning-objects/processing/content-type"; +import { LearningObjectExample } from '../learning-object-example'; +import { LearningObject } from '../../../../src/entities/content/learning-object.entity'; +import { Language } from '../../../../src/entities/content/language'; +import { loadTestAsset } from '../../../test-utils/load-test-asset'; +import { DwengoContentType } from '../../../../src/services/learning-objects/processing/content-type'; /** * Create a dummy learning object to be used in tests where multiple learning objects are needed (for example for use @@ -16,12 +16,12 @@ export function dummyLearningObject(hruid: string, language: Language, title: st learningObject.language = language; learningObject.version = 1; learningObject.title = title; - learningObject.description = "Just a dummy learning object for testing purposes"; + learningObject.description = 'Just a dummy learning object for testing purposes'; learningObject.contentType = DwengoContentType.TEXT_PLAIN; - learningObject.content = Buffer.from("Dummy content"); + learningObject.content = Buffer.from('Dummy content'); return learningObject; }, createAttachment: {}, - getHTMLRendering: () => loadTestAsset("learning-objects/dummy/rendering.html").toString() - } + getHTMLRendering: () => loadTestAsset('learning-objects/dummy/rendering.html').toString(), + }; } diff --git a/backend/tests/test-assets/learning-objects/learning-object-example.d.ts b/backend/tests/test-assets/learning-objects/learning-object-example.d.ts index 90808fe9..1d4009f8 100644 --- a/backend/tests/test-assets/learning-objects/learning-object-example.d.ts +++ b/backend/tests/test-assets/learning-objects/learning-object-example.d.ts @@ -1,8 +1,8 @@ -import {LearningObject} from "../../../src/entities/content/learning-object.entity"; -import {Attachment} from "../../../src/entities/content/attachment.entity"; +import { LearningObject } from '../../../src/entities/content/learning-object.entity'; +import { Attachment } from '../../../src/entities/content/attachment.entity'; type LearningObjectExample = { - createLearningObject: () => LearningObject, - createAttachment: {[key: string]: (owner: LearningObject) => Attachment}, - getHTMLRendering: () => string + createLearningObject: () => LearningObject; + createAttachment: { [key: string]: (owner: LearningObject) => Attachment }; + getHTMLRendering: () => string; }; diff --git a/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/content.md b/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/content.md index 0161bbd0..f469e46c 100644 --- a/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/content.md +++ b/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/content.md @@ -4,7 +4,7 @@ Het lesmateriaal van 'Python in wiskunde en STEM' wordt aangeboden in de vorm va _Nieuwe begrippen_ worden aangebracht via tekstuele uitleg, video en afbeeldingen. -Er zijn uitgewerkte *voorbeelden* met daarnaast ook kleine en grote *opdrachten*. In deze opdrachten zal je aangereikte code kunnen uitvoeren, maar ook zelf code opstellen. +Er zijn uitgewerkte _voorbeelden_ met daarnaast ook kleine en grote _opdrachten_. In deze opdrachten zal je aangereikte code kunnen uitvoeren, maar ook zelf code opstellen. De code die in de notebooks gebruikt wordt, is Python versie 3. We kozen voor Python omdat dit een heel toegankelijke programmeertaal is, die vaak ook intuïtief is. Python is bovendien bezig aan een opmars en wordt gebruikt door bedrijven, zoals Google, NASA, Netflix, Uber, AstraZeneca, Barco, Instagram en YouTube. @@ -12,14 +12,15 @@ Python is bovendien bezig aan een opmars en wordt gebruikt door bedrijven, zoals We kozen voor notebooks omdat daar enkele belangrijke voordelen aan verbonden zijn: leerkrachten moeten geen geavanceerde installaties doen om de notebooks te gebruiken, leerkrachten kunnen verschillende soorten van lesinhouden aanbieden via één platform, de notebooks zijn interactief, leerlingen bouwen de oplossing van een probleem stap voor stap op in de notebook waardoor dat proces zichtbaar is voor de leerkracht ([Jeroen Van der Hooft, 2023](https://libstore.ugent.be/fulltxt/RUG01/003/151/437/RUG01-003151437_2023_0001_AC.pdf)). --- -Klik je op onderstaande knop 'Open notebooks', dan word je doorgestuurd naar een andere website waar jouw persoonlijke notebooks ingeladen worden. (Dit kan even duren.) + +Klik je op onderstaande knop 'Open notebooks', dan word je doorgestuurd naar een andere website waar jouw persoonlijke notebooks ingeladen worden. (Dit kan even duren.) Links op het scherm vind je er twee bestanden met extensie _.ipynb_. Dit zijn de twee notebooks waarin je resp. een overzicht krijgt van de opbouw en mogelijkheden en hoe je er mee aan de slag kan. Dubbelklik op de bestandsnaam om een notebook te openen. -Je ziet er ook een map *images* met de afbeeldingen die in de notebooks getoond worden. +Je ziet er ook een map _images_ met de afbeeldingen die in de notebooks getoond worden. In deze eerste twee notebooks leer je hoe de notebooks zijn opgevat en hoe je ermee aan de slag kan. Na het doorlopen van beide notebooks heb je een goed idee van hoe onze Python notebooks zijn opgevat. -[![](Knop.png "Knop")](https://kiks.ilabt.imec.be/hub/tmplogin?id=0101 "Notebooks Werking") +[![](Knop.png 'Knop')](https://kiks.ilabt.imec.be/hub/tmplogin?id=0101 'Notebooks Werking') diff --git a/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example.ts b/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example.ts index 9a7f340c..5bca09a6 100644 --- a/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example.ts +++ b/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example.ts @@ -1,30 +1,30 @@ -import {LearningObjectExample} from "../learning-object-example"; -import {Language} from "../../../../src/entities/content/language"; -import {DwengoContentType} from "../../../../src/services/learning-objects/processing/content-type"; -import {loadTestAsset} from "../../../test-utils/load-test-asset"; -import {EducationalGoal, LearningObject, ReturnValue} from "../../../../src/entities/content/learning-object.entity"; -import {Attachment} from "../../../../src/entities/content/attachment.entity"; -import {EnvVars, getEnvVar} from "../../../../src/util/envvars"; +import { LearningObjectExample } from '../learning-object-example'; +import { Language } from '../../../../src/entities/content/language'; +import { DwengoContentType } from '../../../../src/services/learning-objects/processing/content-type'; +import { loadTestAsset } from '../../../test-utils/load-test-asset'; +import { EducationalGoal, LearningObject, ReturnValue } from '../../../../src/entities/content/learning-object.entity'; +import { Attachment } from '../../../../src/entities/content/attachment.entity'; +import { EnvVars, getEnvVar } from '../../../../src/util/envvars'; -const ASSETS_PREFIX = "learning-objects/pn-werkingnotebooks/"; +const ASSETS_PREFIX = 'learning-objects/pn-werkingnotebooks/'; const example: LearningObjectExample = { - createLearningObject: ()=> { + createLearningObject: () => { const learningObject = new LearningObject(); learningObject.hruid = `${getEnvVar(EnvVars.UserContentPrefix)}pn_werkingnotebooks`; learningObject.version = 3; learningObject.language = Language.Dutch; - learningObject.title = "Werken met notebooks"; - learningObject.description = "Leren werken met notebooks"; - learningObject.keywords = ["Python", "KIKS", "Wiskunde", "STEM", "AI"] + learningObject.title = 'Werken met notebooks'; + learningObject.description = 'Leren werken met notebooks'; + learningObject.keywords = ['Python', 'KIKS', 'Wiskunde', 'STEM', 'AI']; const educationalGoal1 = new EducationalGoal(); - educationalGoal1.source = "Source"; - educationalGoal1.id = "id"; + educationalGoal1.source = 'Source'; + educationalGoal1.id = 'id'; const educationalGoal2 = new EducationalGoal(); - educationalGoal2.source = "Source2"; - educationalGoal2.id = "id2"; + educationalGoal2.source = 'Source2'; + educationalGoal2.id = 'id2'; learningObject.educationalGoals = [educationalGoal1, educationalGoal2]; learningObject.admins = []; @@ -33,40 +33,40 @@ const example: LearningObjectExample = { learningObject.skosConcepts = [ 'http://ilearn.ilabt.imec.be/vocab/curr1/s-vaktaal', 'http://ilearn.ilabt.imec.be/vocab/curr1/s-digitale-media-en-toepassingen', - 'http://ilearn.ilabt.imec.be/vocab/curr1/s-computers-en-systemen' + 'http://ilearn.ilabt.imec.be/vocab/curr1/s-computers-en-systemen', ]; - learningObject.copyright = "dwengo"; - learningObject.license = "dwengo"; + learningObject.copyright = 'dwengo'; + learningObject.license = 'dwengo'; learningObject.estimatedTime = 10; const returnValue = new ReturnValue(); - returnValue.callbackUrl = "callback_url_example"; + returnValue.callbackUrl = 'callback_url_example'; returnValue.callbackSchema = '{"att": "test", "att2": "test2"}'; learningObject.returnValue = returnValue; learningObject.available = true; learningObject.content = loadTestAsset(`${ASSETS_PREFIX}/content.md`); - return learningObject + return learningObject; }, createAttachment: { dwengoLogo: (learningObject) => { const att = new Attachment(); att.learningObject = learningObject; - att.name = "dwengo.png"; - att.mimeType = "image/png"; - att.content = loadTestAsset(`${ASSETS_PREFIX}/dwengo.png`) + att.name = 'dwengo.png'; + att.mimeType = 'image/png'; + att.content = loadTestAsset(`${ASSETS_PREFIX}/dwengo.png`); return att; }, knop: (learningObject) => { const att = new Attachment(); att.learningObject = learningObject; - att.name = "Knop.png"; - att.mimeType = "image/png"; - att.content = loadTestAsset(`${ASSETS_PREFIX}/Knop.png`) + att.name = 'Knop.png'; + att.mimeType = 'image/png'; + att.content = loadTestAsset(`${ASSETS_PREFIX}/Knop.png`); return att; - } + }, }, - getHTMLRendering: () => loadTestAsset(`${ASSETS_PREFIX}/rendering.html`).toString() -} + getHTMLRendering: () => loadTestAsset(`${ASSETS_PREFIX}/rendering.html`).toString(), +}; export default example; diff --git a/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/rendering.html b/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/rendering.html index d197ebd4..ab9bd34e 100644 --- a/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/rendering.html +++ b/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/rendering.html @@ -4,17 +4,44 @@ Werken met notebooks -

Het lesmateriaal van 'Python in wiskunde en STEM' wordt aangeboden in de vorm van interactieve notebooks. Notebooks zijn digitale documenten die zowel uitvoerbare code bevatten als tekst, afbeeldingen, video, hyperlinks ...

+

+ Het lesmateriaal van 'Python in wiskunde en STEM' wordt aangeboden in de vorm van interactieve notebooks. Notebooks zijn digitale documenten die zowel uitvoerbare code bevatten als tekst, afbeeldingen, video, hyperlinks ... +

Nieuwe begrippen worden aangebracht via tekstuele uitleg, video en afbeeldingen.

-

Er zijn uitgewerkte voorbeelden met daarnaast ook kleine en grote opdrachten. In deze opdrachten zal je aangereikte code kunnen uitvoeren, maar ook zelf code opstellen.

-

De code die in de notebooks gebruikt wordt, is Python versie 3. We kozen voor Python omdat dit een heel toegankelijke programmeertaal is, die vaak ook intuC/tief is. -Python is bovendien bezig aan een opmars en wordt gebruikt door bedrijven, zoals Google, NASA, Netflix, Uber, AstraZeneca, Barco, Instagram en YouTube.

-

We kozen voor notebooks omdat daar enkele belangrijke voordelen aan verbonden zijn: leerkrachten moeten geen geavanceerde installaties doen om de notebooks te gebruiken, leerkrachten kunnen verschillende soorten van lesinhouden aanbieden via C)C)n platform, de notebooks zijn interactief, leerlingen bouwen de oplossing van een probleem stap voor stap op in de notebook waardoor dat proces zichtbaar is voor de leerkracht (Jeroen Van der Hooft, 2023).

-
-

Klik je op onderstaande knop 'Open notebooks', dan word je doorgestuurd naar een andere website waar jouw persoonlijke notebooks ingeladen worden. (Dit kan even duren.)

-

Links op het scherm vind je er twee bestanden met extensie .ipynb. -Dit zijn de twee notebooks waarin je resp. een overzicht krijgt van de opbouw en mogelijkheden en hoe je er mee aan de slag kan. Dubbelklik op de bestandsnaam om een notebook te openen.

+

+ Er zijn uitgewerkte voorbeelden met daarnaast ook kleine en grote opdrachten. In deze opdrachten zal je aangereikte code kunnen + uitvoeren, maar ook zelf code opstellen. +

+

+ De code die in de notebooks gebruikt wordt, is Python versie 3. We kozen voor Python omdat dit een heel toegankelijke programmeertaal is, die vaak + ook intuC/tief is. Python is bovendien bezig aan een opmars en wordt gebruikt door bedrijven, zoals Google, NASA, Netflix, Uber, AstraZeneca, + Barco, Instagram en YouTube. +

+

+ We kozen voor notebooks omdat daar enkele belangrijke voordelen aan verbonden zijn: leerkrachten moeten geen geavanceerde installaties doen om de + notebooks te gebruiken, leerkrachten kunnen verschillende soorten van lesinhouden aanbieden via C)C)n platform, de notebooks zijn interactief, + leerlingen bouwen de oplossing van een probleem stap voor stap op in de notebook waardoor dat proces zichtbaar is voor de leerkracht (Jeroen Van der Hooft, 2023). +

+
+

+ Klik je op onderstaande knop 'Open notebooks', dan word je doorgestuurd naar een andere website waar jouw persoonlijke notebooks ingeladen + worden. (Dit kan even duren.) +

+

+ Links op het scherm vind je er twee bestanden met extensie .ipynb. Dit zijn de twee notebooks waarin je resp. een overzicht krijgt van de + opbouw en mogelijkheden en hoe je er mee aan de slag kan. Dubbelklik op de bestandsnaam om een notebook te openen. +

Je ziet er ook een map images met de afbeeldingen die in de notebooks getoond worden.

-

In deze eerste twee notebooks leer je hoe de notebooks zijn opgevat en hoe je ermee aan de slag kan. -Na het doorlopen van beide notebooks heb je een goed idee van hoe onze Python notebooks zijn opgevat.

-

+

+ In deze eerste twee notebooks leer je hoe de notebooks zijn opgevat en hoe je ermee aan de slag kan. Na het doorlopen van beide notebooks heb je + een goed idee van hoe onze Python notebooks zijn opgevat. +

+

+ +

diff --git a/backend/tests/test-assets/learning-objects/test-essay/test-essay-example.ts b/backend/tests/test-assets/learning-objects/test-essay/test-essay-example.ts index 08a818f7..7a2f54d9 100644 --- a/backend/tests/test-assets/learning-objects/test-essay/test-essay-example.ts +++ b/backend/tests/test-assets/learning-objects/test-essay/test-essay-example.ts @@ -1,9 +1,9 @@ -import {LearningObjectExample} from "../learning-object-example"; -import {LearningObject} from "../../../../src/entities/content/learning-object.entity"; -import {loadTestAsset} from "../../../test-utils/load-test-asset"; -import {EnvVars, getEnvVar} from "../../../../src/util/envvars"; -import {Language} from "../../../../src/entities/content/language"; -import {DwengoContentType} from "../../../../src/services/learning-objects/processing/content-type"; +import { LearningObjectExample } from '../learning-object-example'; +import { LearningObject } from '../../../../src/entities/content/learning-object.entity'; +import { loadTestAsset } from '../../../test-utils/load-test-asset'; +import { EnvVars, getEnvVar } from '../../../../src/util/envvars'; +import { Language } from '../../../../src/entities/content/language'; +import { DwengoContentType } from '../../../../src/services/learning-objects/processing/content-type'; const example: LearningObjectExample = { createLearningObject: () => { @@ -11,14 +11,14 @@ const example: LearningObjectExample = { learningObject.hruid = `${getEnvVar(EnvVars.UserContentPrefix)}test_essay`; learningObject.language = Language.English; learningObject.version = 1; - learningObject.title = "Essay question for testing"; - learningObject.description = "This essay question was only created for testing purposes."; + learningObject.title = 'Essay question for testing'; + learningObject.description = 'This essay question was only created for testing purposes.'; learningObject.contentType = DwengoContentType.GIFT; - learningObject.content = loadTestAsset("learning-objects/test-essay/content.txt"); + learningObject.content = loadTestAsset('learning-objects/test-essay/content.txt'); return learningObject; }, createAttachment: {}, - getHTMLRendering: () => loadTestAsset("learning-objects/test-essay/rendering.html").toString() + getHTMLRendering: () => loadTestAsset('learning-objects/test-essay/rendering.html').toString(), }; export default example; diff --git a/backend/tests/test-assets/learning-objects/test-multiple-choice/rendering.html b/backend/tests/test-assets/learning-objects/test-multiple-choice/rendering.html index c1829f24..513a29a5 100644 --- a/backend/tests/test-assets/learning-objects/test-multiple-choice/rendering.html +++ b/backend/tests/test-assets/learning-objects/test-multiple-choice/rendering.html @@ -3,11 +3,11 @@

MC basic

Are you following along well with the class?

- +
- +
diff --git a/backend/tests/test-assets/learning-objects/test-multiple-choice/test-multiple-choice-example.ts b/backend/tests/test-assets/learning-objects/test-multiple-choice/test-multiple-choice-example.ts index 6abc56dd..52b6adbe 100644 --- a/backend/tests/test-assets/learning-objects/test-multiple-choice/test-multiple-choice-example.ts +++ b/backend/tests/test-assets/learning-objects/test-multiple-choice/test-multiple-choice-example.ts @@ -1,9 +1,9 @@ -import {LearningObjectExample} from "../learning-object-example"; -import {LearningObject} from "../../../../src/entities/content/learning-object.entity"; -import {loadTestAsset} from "../../../test-utils/load-test-asset"; -import {EnvVars, getEnvVar} from "../../../../src/util/envvars"; -import {Language} from "../../../../src/entities/content/language"; -import {DwengoContentType} from "../../../../src/services/learning-objects/processing/content-type"; +import { LearningObjectExample } from '../learning-object-example'; +import { LearningObject } from '../../../../src/entities/content/learning-object.entity'; +import { loadTestAsset } from '../../../test-utils/load-test-asset'; +import { EnvVars, getEnvVar } from '../../../../src/util/envvars'; +import { Language } from '../../../../src/entities/content/language'; +import { DwengoContentType } from '../../../../src/services/learning-objects/processing/content-type'; const example: LearningObjectExample = { createLearningObject: () => { @@ -11,14 +11,14 @@ const example: LearningObjectExample = { learningObject.hruid = `${getEnvVar(EnvVars.UserContentPrefix)}test_multiple_choice`; learningObject.language = Language.English; learningObject.version = 1; - learningObject.title = "Multiple choice question for testing"; - learningObject.description = "This multiple choice question was only created for testing purposes."; + learningObject.title = 'Multiple choice question for testing'; + learningObject.description = 'This multiple choice question was only created for testing purposes.'; learningObject.contentType = DwengoContentType.GIFT; - learningObject.content = loadTestAsset("learning-objects/test-multiple-choice/content.txt"); + learningObject.content = loadTestAsset('learning-objects/test-multiple-choice/content.txt'); return learningObject; }, createAttachment: {}, - getHTMLRendering: () => loadTestAsset("learning-objects/test-multiple-choice/rendering.html").toString() + getHTMLRendering: () => loadTestAsset('learning-objects/test-multiple-choice/rendering.html').toString(), }; export default example; diff --git a/backend/tests/test-assets/learning-paths/learning-path-example.d.ts b/backend/tests/test-assets/learning-paths/learning-path-example.d.ts index 47d0221f..9df3ba48 100644 --- a/backend/tests/test-assets/learning-paths/learning-path-example.d.ts +++ b/backend/tests/test-assets/learning-paths/learning-path-example.d.ts @@ -1,3 +1,3 @@ type LearningPathExample = { - createLearningPath: () => LearningPath + createLearningPath: () => LearningPath; }; diff --git a/backend/tests/test-assets/learning-paths/learning-path-utils.ts b/backend/tests/test-assets/learning-paths/learning-path-utils.ts index 0a09145f..c567de66 100644 --- a/backend/tests/test-assets/learning-paths/learning-path-utils.ts +++ b/backend/tests/test-assets/learning-paths/learning-path-utils.ts @@ -1,13 +1,13 @@ -import {Language} from "../../../src/entities/content/language"; -import {LearningPathTransition} from "../../../src/entities/content/learning-path-transition.entity"; -import {LearningPathNode} from "../../../src/entities/content/learning-path-node.entity"; -import {LearningPath} from "../../../src/entities/content/learning-path.entity"; +import { Language } from '../../../src/entities/content/language'; +import { LearningPathTransition } from '../../../src/entities/content/learning-path-transition.entity'; +import { LearningPathNode } from '../../../src/entities/content/learning-path-node.entity'; +import { LearningPath } from '../../../src/entities/content/learning-path.entity'; export function createLearningPathTransition(node: LearningPathNode, transitionNumber: number, condition: string | null, to: LearningPathNode) { const trans = new LearningPathTransition(); trans.node = node; trans.transitionNumber = transitionNumber; - trans.condition = condition || "true"; + trans.condition = condition || 'true'; trans.next = to; return trans; } diff --git a/backend/tests/test-assets/learning-paths/pn-werking-example.ts b/backend/tests/test-assets/learning-paths/pn-werking-example.ts index 40a07587..810b4da5 100644 --- a/backend/tests/test-assets/learning-paths/pn-werking-example.ts +++ b/backend/tests/test-assets/learning-paths/pn-werking-example.ts @@ -1,17 +1,17 @@ -import {LearningPath} from "../../../src/entities/content/learning-path.entity"; -import {Language} from "../../../src/entities/content/language"; -import {EnvVars, getEnvVar} from "../../../src/util/envvars"; -import {createLearningPathNode, createLearningPathTransition} from "./learning-path-utils"; -import {LearningPathNode} from "../../../src/entities/content/learning-path-node.entity"; +import { LearningPath } from '../../../src/entities/content/learning-path.entity'; +import { Language } from '../../../src/entities/content/language'; +import { EnvVars, getEnvVar } from '../../../src/util/envvars'; +import { createLearningPathNode, createLearningPathTransition } from './learning-path-utils'; +import { LearningPathNode } from '../../../src/entities/content/learning-path-node.entity'; function createNodes(learningPath: LearningPath): LearningPathNode[] { const nodes = [ - createLearningPathNode(learningPath, 0, "u_pn_werkingnotebooks", 3, Language.Dutch, true), - createLearningPathNode(learningPath, 1, "pn_werkingnotebooks2", 3, Language.Dutch, false), - createLearningPathNode(learningPath, 2, "pn_werkingnotebooks3", 3, Language.Dutch, false), + createLearningPathNode(learningPath, 0, 'u_pn_werkingnotebooks', 3, Language.Dutch, true), + createLearningPathNode(learningPath, 1, 'pn_werkingnotebooks2', 3, Language.Dutch, false), + createLearningPathNode(learningPath, 2, 'pn_werkingnotebooks3', 3, Language.Dutch, false), ]; - nodes[0].transitions.push(createLearningPathTransition(nodes[0], 0, "true", nodes[1])); - nodes[1].transitions.push(createLearningPathTransition(nodes[1], 0, "true", nodes[2])); + nodes[0].transitions.push(createLearningPathTransition(nodes[0], 0, 'true', nodes[1])); + nodes[1].transitions.push(createLearningPathTransition(nodes[1], 0, 'true', nodes[2])); return nodes; } @@ -20,11 +20,11 @@ const example: LearningPathExample = { const path = new LearningPath(); path.language = Language.Dutch; path.hruid = `${getEnvVar(EnvVars.UserContentPrefix)}pn_werking`; - path.title = "Werken met notebooks"; - path.description = "Een korte inleiding tot Python notebooks. Hoe ga je gemakkelijk en efficiënt met de notebooks aan de slag?"; + path.title = 'Werken met notebooks'; + path.description = 'Een korte inleiding tot Python notebooks. Hoe ga je gemakkelijk en efficiënt met de notebooks aan de slag?'; path.nodes = createNodes(path); return path; - } -} + }, +}; export default example; diff --git a/backend/tests/test-assets/learning-paths/test-conditions-example.ts b/backend/tests/test-assets/learning-paths/test-conditions-example.ts index 000bb732..b6cf3e9d 100644 --- a/backend/tests/test-assets/learning-paths/test-conditions-example.ts +++ b/backend/tests/test-assets/learning-paths/test-conditions-example.ts @@ -1,23 +1,27 @@ -import {LearningPath} from "../../../src/entities/content/learning-path.entity"; -import {Language} from "../../../src/entities/content/language"; -import testMultipleChoiceExample from "../learning-objects/test-multiple-choice/test-multiple-choice-example"; -import {dummyLearningObject} from "../learning-objects/dummy/dummy-learning-object-example"; -import {createLearningPathNode, createLearningPathTransition} from "./learning-path-utils"; +import { LearningPath } from '../../../src/entities/content/learning-path.entity'; +import { Language } from '../../../src/entities/content/language'; +import testMultipleChoiceExample from '../learning-objects/test-multiple-choice/test-multiple-choice-example'; +import { dummyLearningObject } from '../learning-objects/dummy/dummy-learning-object-example'; +import { createLearningPathNode, createLearningPathTransition } from './learning-path-utils'; const example: LearningPathExample = { createLearningPath: () => { const learningPath = new LearningPath(); - learningPath.hruid = "test_conditions"; + learningPath.hruid = 'test_conditions'; learningPath.language = Language.English; - learningPath.title = "Example learning path with conditional transitions"; - learningPath.description = "This learning path was made for the purpose of testing conditional transitions"; + learningPath.title = 'Example learning path with conditional transitions'; + learningPath.description = 'This learning path was made for the purpose of testing conditional transitions'; const branchingLearningObject = testMultipleChoiceExample.createLearningObject(); const extraExerciseLearningObject = dummyLearningObject( - "test_extra_exercise", Language.English, "Extra exercise (for students with difficulties)" + 'test_extra_exercise', + Language.English, + 'Extra exercise (for students with difficulties)' ).createLearningObject(); const finalLearningObject = dummyLearningObject( - "test_final_learning_object", Language.English, "Final exercise (for everyone)" + 'test_final_learning_object', + Language.English, + 'Final exercise (for everyone)' ).createLearningObject(); const branchingNode = createLearningPathNode( @@ -48,21 +52,11 @@ const example: LearningPathExample = { const transitionToExtraExercise = createLearningPathTransition( branchingNode, 0, - "$[?(@[0] == 0)]", // The answer to the first question was the first one, which says that it is difficult for the student to follow along. + '$[?(@[0] == 0)]', // The answer to the first question was the first one, which says that it is difficult for the student to follow along. extraExerciseNode ); - const directTransitionToFinal = createLearningPathTransition( - branchingNode, - 1, - "$[?(@[0] == 1)]", - finalNode - ); - const transitionExtraExerciseToFinal = createLearningPathTransition( - extraExerciseNode, - 0, - "true", - finalNode - ); + const directTransitionToFinal = createLearningPathTransition(branchingNode, 1, '$[?(@[0] == 1)]', finalNode); + const transitionExtraExerciseToFinal = createLearningPathTransition(extraExerciseNode, 0, 'true', finalNode); branchingNode.transitions = [transitionToExtraExercise, directTransitionToFinal]; extraExerciseNode.transitions = [transitionExtraExerciseToFinal]; @@ -70,5 +64,5 @@ const example: LearningPathExample = { learningPath.nodes = [branchingNode, extraExerciseNode, finalNode]; return learningPath; - } + }, }; diff --git a/backend/tests/test-utils/expectations.ts b/backend/tests/test-utils/expectations.ts index b7e9523b..70fc8c6f 100644 --- a/backend/tests/test-utils/expectations.ts +++ b/backend/tests/test-utils/expectations.ts @@ -1,63 +1,62 @@ -import {AssertionError} from "node:assert"; -import {LearningObject} from "../../src/entities/content/learning-object.entity"; -import {FilteredLearningObject, LearningPath} from "../../src/interfaces/learning-content"; -import {LearningPath as LearningPathEntity} from "../../src/entities/content/learning-path.entity" -import { expect } from "vitest"; +import { AssertionError } from 'node:assert'; +import { LearningObject } from '../../src/entities/content/learning-object.entity'; +import { FilteredLearningObject, LearningPath } from '../../src/interfaces/learning-content'; +import { LearningPath as LearningPathEntity } from '../../src/entities/content/learning-path.entity'; +import { expect } from 'vitest'; // Ignored properties because they belang for example to the class, not to the entity itself. -const IGNORE_PROPERTIES = ["parent"]; +const IGNORE_PROPERTIES = ['parent']; /** * Checks if the actual entity from the database conforms to the entity that was added previously. * @param actual The actual entity retrieved from the database * @param expected The (previously added) entity we would expect to retrieve */ -export function expectToBeCorrectEntity( - actual: {entity: T, name?: string}, - expected: {entity: T, name?: string} -): void { +export function expectToBeCorrectEntity(actual: { entity: T; name?: string }, expected: { entity: T; name?: string }): void { if (!actual.name) { - actual.name = "actual"; + actual.name = 'actual'; } if (!expected.name) { - expected.name = "expected"; + expected.name = 'expected'; } for (const property in expected.entity) { if ( - property !in IGNORE_PROPERTIES - && expected.entity[property] !== undefined // If we don't expect a certain value for a property, we assume it can be filled in by the database however it wants. - && typeof expected.entity[property] !== "function" // Functions obviously are not persisted via the database + property! in IGNORE_PROPERTIES && + expected.entity[property] !== undefined && // If we don't expect a certain value for a property, we assume it can be filled in by the database however it wants. + typeof expected.entity[property] !== 'function' // Functions obviously are not persisted via the database ) { if (!actual.entity.hasOwnProperty(property)) { throw new AssertionError({ - message: `${expected.name} has defined property ${property}, but ${actual.name} is missing it.` + message: `${expected.name} has defined property ${property}, but ${actual.name} is missing it.`, }); } - if (typeof expected.entity[property] === "boolean") { // Sometimes, booleans get represented by numbers 0 and 1 in the objects actual from the database. + if (typeof expected.entity[property] === 'boolean') { + // Sometimes, booleans get represented by numbers 0 and 1 in the objects actual from the database. if (Boolean(expected.entity[property]) !== Boolean(actual.entity[property])) { throw new AssertionError({ message: `${property} was ${expected.entity[property]} in ${expected.name}, - but ${actual.entity[property]} (${Boolean(expected.entity[property])}) in ${actual.name}` + but ${actual.entity[property]} (${Boolean(expected.entity[property])}) in ${actual.name}`, }); } } else if (typeof expected.entity[property] !== typeof actual.entity[property]) { throw new AssertionError({ - message: `${property} has type ${typeof expected.entity[property]} in ${expected.name}, but type ${typeof actual.entity[property]} in ${actual.name}.` + message: `${property} has type ${typeof expected.entity[property]} in ${expected.name}, but type ${typeof actual.entity[property]} in ${actual.name}.`, }); - } else if (typeof expected.entity[property] === "object") { + } else if (typeof expected.entity[property] === 'object') { expectToBeCorrectEntity( { - name: actual.name + "." + property, - entity: actual.entity[property] as object - }, { - name: expected.name + "." + property, - entity: expected.entity[property] as object + name: actual.name + '.' + property, + entity: actual.entity[property] as object, + }, + { + name: expected.name + '.' + property, + entity: expected.entity[property] as object, } ); } else { if (expected.entity[property] !== actual.entity[property]) { throw new AssertionError({ - message: `${property} was ${expected.entity[property]} in ${expected.name}, but ${actual.entity[property]} in ${actual.name}` + message: `${property} was ${expected.entity[property]} in ${expected.name}, but ${actual.entity[property]} in ${actual.name}`, }); } } @@ -78,7 +77,7 @@ export function expectToBeCorrectFilteredLearningObject(filtered: FilteredLearni expect(filtered.key).toEqual(original.hruid); expect(filtered.targetAges).toEqual(original.targetAges); expect(filtered.title).toEqual(original.title); - expect(Boolean(filtered.teacherExclusive)).toEqual(original.teacherExclusive) // !!: Workaround: MikroORM with SQLite returns 0 and 1 instead of booleans. + expect(Boolean(filtered.teacherExclusive)).toEqual(original.teacherExclusive); // !!: Workaround: MikroORM with SQLite returns 0 and 1 instead of booleans. expect(filtered.skosConcepts).toEqual(original.skosConcepts); expect(filtered.estimatedTime).toEqual(original.estimatedTime); expect(filtered.educationalGoals).toEqual(original.educationalGoals); @@ -112,10 +111,10 @@ export function expectToBeCorrectLearningPath( expect(learningPath.description).toEqual(expectedEntity.description); expect(learningPath.title).toEqual(expectedEntity.title); - const keywords = new Set(learningObjectsOnPath.flatMap(it => it.keywords || [])); - expect(new Set(learningPath.keywords.split(' '))).toEqual(keywords) + const keywords = new Set(learningObjectsOnPath.flatMap((it) => it.keywords || [])); + expect(new Set(learningPath.keywords.split(' '))).toEqual(keywords); - const targetAges = new Set(learningObjectsOnPath.flatMap(it => it.targetAges || [])); + const targetAges = new Set(learningObjectsOnPath.flatMap((it) => it.targetAges || [])); expect(new Set(learningPath.target_ages)).toEqual(targetAges); expect(learningPath.min_age).toEqual(Math.min(...targetAges)); expect(learningPath.max_age).toEqual(Math.max(...targetAges)); @@ -124,9 +123,9 @@ export function expectToBeCorrectLearningPath( expect(learningPath.image || null).toEqual(expectedEntity.image); const expectedLearningPathNodes = new Map( - expectedEntity.nodes.map(node => [ - {learningObjectHruid: node.learningObjectHruid, language: node.language, version: node.version}, - {startNode: node.startNode, transitions: node.transitions} + expectedEntity.nodes.map((node) => [ + { learningObjectHruid: node.learningObjectHruid, language: node.language, version: node.version }, + { startNode: node.startNode, transitions: node.transitions }, ]) ); @@ -134,31 +133,18 @@ export function expectToBeCorrectLearningPath( const nodeKey = { learningObjectHruid: node.learningobject_hruid, language: node.language, - version: node.version + version: node.version, }; expect(expectedLearningPathNodes.keys()).toContainEqual(nodeKey); - const expectedNode = [...expectedLearningPathNodes.entries()] - .filter(([key, _]) => - key.learningObjectHruid === nodeKey.learningObjectHruid - && key.language === node.language - && key.version === node.version - )[0][1] + const expectedNode = [...expectedLearningPathNodes.entries()].filter( + ([key, _]) => key.learningObjectHruid === nodeKey.learningObjectHruid && key.language === node.language && key.version === node.version + )[0][1]; expect(node.start_node).toEqual(expectedNode?.startNode); - expect( - new Set(node.transitions.map(it => it.next.hruid)) - ).toEqual( - new Set(expectedNode.transitions.map(it => it.next.learningObjectHruid)) - ); - expect( - new Set(node.transitions.map(it => it.next.language)) - ).toEqual( - new Set(expectedNode.transitions.map(it => it.next.language)) - ); - expect( - new Set(node.transitions.map(it => it.next.version)) - ).toEqual( - new Set(expectedNode.transitions.map(it => it.next.version)) + expect(new Set(node.transitions.map((it) => it.next.hruid))).toEqual( + new Set(expectedNode.transitions.map((it) => it.next.learningObjectHruid)) ); + expect(new Set(node.transitions.map((it) => it.next.language))).toEqual(new Set(expectedNode.transitions.map((it) => it.next.language))); + expect(new Set(node.transitions.map((it) => it.next.version))).toEqual(new Set(expectedNode.transitions.map((it) => it.next.version))); } } diff --git a/backend/tests/test-utils/load-test-asset.ts b/backend/tests/test-utils/load-test-asset.ts index effa0c73..35f6cdbf 100644 --- a/backend/tests/test-utils/load-test-asset.ts +++ b/backend/tests/test-utils/load-test-asset.ts @@ -1,5 +1,5 @@ -import fs from "fs"; -import path from "node:path"; +import fs from 'fs'; +import path from 'node:path'; /** * Load the asset at the given path. From 49f15c50d850c480de3db58e1f7f026675cc3546 Mon Sep 17 00:00:00 2001 From: Gerald Schmittinger Date: Tue, 11 Mar 2025 04:40:38 +0100 Subject: [PATCH 62/96] test(backend): Testen DatabaseLearningObjectProvider.getLearningObject[Id]sFromPath toegevoegd --- .../database-learning-object-provider.ts | 7 +- .../learning-paths/point-of-view.d.ts | 1 + .../database-learning-object-provider.test.ts | 68 ++++++++++++++++--- backend/tests/test-utils/expectations.ts | 4 +- 4 files changed, 65 insertions(+), 15 deletions(-) create mode 100644 backend/src/services/learning-paths/point-of-view.d.ts diff --git a/backend/src/services/learning-objects/database-learning-object-provider.ts b/backend/src/services/learning-objects/database-learning-object-provider.ts index 46fc23fe..56aa3a99 100644 --- a/backend/src/services/learning-objects/database-learning-object-provider.ts +++ b/backend/src/services/learning-objects/database-learning-object-provider.ts @@ -10,6 +10,7 @@ import {LearningObject} from "../../entities/content/learning-object.entity"; import {getUrlStringForLearningObject} from "../../util/links"; import processingService from "./processing/processing-service"; import {NotFoundError} from "@mikro-orm/core"; +import learningObjectService from "./learning-object-service"; function convertLearningObject(learningObject: LearningObject | null): FilteredLearningObject | null { @@ -90,7 +91,7 @@ const databaseLearningObjectProvider: LearningObjectProvider = { if (!learningPath) { throw new NotFoundError("The learning path with the given ID could not be found."); } - return learningPath.nodes.map(it => it.learningObjectHruid); // TODO: Determine this based on the submissions of the user. + return learningPath.nodes.map(it => it.learningObjectHruid); }, /** @@ -105,7 +106,7 @@ const databaseLearningObjectProvider: LearningObjectProvider = { } const learningObjects = await Promise.all( learningPath.nodes.map(it => { - const learningObject = this.getLearningObjectById({ + const learningObject = learningObjectService.getLearningObjectById({ hruid: it.learningObjectHruid, language: it.language, version: it.version @@ -116,7 +117,7 @@ const databaseLearningObjectProvider: LearningObjectProvider = { return learningObject; }) ); - return learningObjects.filter(it => it !== null); // TODO: Determine this based on the submissions of the user. + return learningObjects.filter(it => it !== null); } } diff --git a/backend/src/services/learning-paths/point-of-view.d.ts b/backend/src/services/learning-paths/point-of-view.d.ts new file mode 100644 index 00000000..e08685b8 --- /dev/null +++ b/backend/src/services/learning-paths/point-of-view.d.ts @@ -0,0 +1 @@ +type PointOfView = {type: "student", student: Student} | {type: "group", group: Group}; diff --git a/backend/tests/services/learning-objects/database-learning-object-provider.test.ts b/backend/tests/services/learning-objects/database-learning-object-provider.test.ts index 691b29ba..d7870eb9 100644 --- a/backend/tests/services/learning-objects/database-learning-object-provider.test.ts +++ b/backend/tests/services/learning-objects/database-learning-object-provider.test.ts @@ -1,29 +1,37 @@ import {beforeAll, describe, expect, it} from "vitest"; import {setupTestApp} from "../../setup-tests"; -import {getLearningObjectRepository} from "../../../src/data/repositories"; +import {getLearningObjectRepository, getLearningPathRepository} from "../../../src/data/repositories"; import example from "../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example"; import {LearningObject} from "../../../src/entities/content/learning-object.entity"; import databaseLearningObjectProvider from "../../../src/services/learning-objects/database-learning-object-provider"; -import { - createExampleLearningObjectWithAttachments -} from "../../test-assets/learning-objects/create-example-learning-object-with-attachments"; import {expectToBeCorrectFilteredLearningObject} from "../../test-utils/expectations"; import {FilteredLearningObject} from "../../../src/interfaces/learning-content"; import {Language} from "../../../src/entities/content/language"; +import learningObjectExample from "../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example"; +import learningPathExample from "../../test-assets/learning-paths/pn-werking-example"; +import {LearningPath} from "../../../src/entities/content/learning-path.entity"; -async function initExampleData(): Promise { - let learningObjectRepo = getLearningObjectRepository(); - let exampleLearningObject = createExampleLearningObjectWithAttachments(example); - await learningObjectRepo.insert(exampleLearningObject); - return exampleLearningObject; +async function initExampleData(): Promise<{learningObject: LearningObject, learningPath: LearningPath}> { + const learningObjectRepo = getLearningObjectRepository(); + const learningPathRepo = getLearningPathRepository(); + let learningObject = learningObjectExample.createLearningObject(); + let learningPath = learningPathExample.createLearningPath(); + await learningObjectRepo.save(learningObject); + await learningPathRepo.save(learningPath); + return { learningObject, learningPath }; } +const EXPECTED_TITLE_FROM_DWENGO_LEARNING_OBJECT = "Notebook opslaan"; + describe("DatabaseLearningObjectProvider", () => { let exampleLearningObject: LearningObject; + let exampleLearningPath: LearningPath; beforeAll(async () => { await setupTestApp(); - exampleLearningObject = await initExampleData(); + let exampleData = await initExampleData(); + exampleLearningObject = exampleData.learningObject; + exampleLearningPath = exampleData.learningPath; }); describe("getLearningObjectById", () => { it("should return the learning object when it is queried by its id", async () => { @@ -54,5 +62,45 @@ describe("DatabaseLearningObjectProvider", () => { const result = await databaseLearningObjectProvider.getLearningObjectHTML(exampleLearningObject); expect(result).toEqual(example.getHTMLRendering()); }); + it("should return null for a non-existing learning object", async () => { + const result = await databaseLearningObjectProvider.getLearningObjectHTML({ + hruid: "non_existing_hruid", + language: Language.Dutch + }); + expect(result).toBeNull(); + }); + }); + describe("getLearningObjectIdsFromPath", () => { + it("should return all learning object IDs from a path", async () => { + const result = await databaseLearningObjectProvider.getLearningObjectIdsFromPath(exampleLearningPath); + expect(new Set(result)) + .toEqual(new Set(exampleLearningPath.nodes.map(it => it.learningObjectHruid))); + }); + it("should throw an error if queried with a path identifier for which there is no learning path", async () => { + await expect((async () => { + await databaseLearningObjectProvider.getLearningObjectIdsFromPath({ + hruid: "non_existing_hruid", + language: Language.Dutch + }); + })()).rejects.toThrowError(); + }); + }); + describe("getLearningObjectsFromPath", () => { + it("should correctly return all learning objects which are on the path, even those who are not in the database", async () => { + const result = await databaseLearningObjectProvider.getLearningObjectsFromPath(exampleLearningPath); + expect(result.length).toBe(exampleLearningPath.nodes.length); + expect(new Set(result.map(it => it.key))) + .toEqual(new Set(exampleLearningPath.nodes.map(it => it.learningObjectHruid))); + + expect(result.map(it => it.title)).toContainEqual(EXPECTED_TITLE_FROM_DWENGO_LEARNING_OBJECT); + }); + it("should throw an error if queried with a path identifier for which there is no learning path", async () => { + await expect((async () => { + await databaseLearningObjectProvider.getLearningObjectsFromPath({ + hruid: "non_existing_hruid", + language: Language.Dutch + }); + })()).rejects.toThrowError(); + }); }); }); diff --git a/backend/tests/test-utils/expectations.ts b/backend/tests/test-utils/expectations.ts index 19c2408d..e8e70845 100644 --- a/backend/tests/test-utils/expectations.ts +++ b/backend/tests/test-utils/expectations.ts @@ -78,7 +78,7 @@ export function expectToBeCorrectFilteredLearningObject(filtered: FilteredLearni expect(filtered.key).toEqual(original.hruid); expect(filtered.targetAges).toEqual(original.targetAges); expect(filtered.title).toEqual(original.title); - expect(!!filtered.teacherExclusive).toEqual(original.teacherExclusive) // !!: Workaround: MikroORM with SQLite returns 0 and 1 instead of booleans. + expect(!!filtered.teacherExclusive).toEqual(!!original.teacherExclusive) // !!: Workaround: MikroORM with SQLite returns 0 and 1 instead of booleans. expect(filtered.skosConcepts).toEqual(original.skosConcepts); expect(filtered.estimatedTime).toEqual(original.estimatedTime); expect(filtered.educationalGoals).toEqual(original.educationalGoals); @@ -87,7 +87,7 @@ export function expectToBeCorrectFilteredLearningObject(filtered: FilteredLearni expect(filtered.returnValue?.callback_url).toEqual(original.returnValue.callbackUrl); expect(filtered.returnValue?.callback_schema).toEqual(JSON.parse(original.returnValue.callbackSchema)); expect(filtered.contentType).toEqual(original.contentType); - expect(filtered.contentLocation).toEqual(original.contentLocation || null); + expect(filtered.contentLocation || null).toEqual(original.contentLocation || null); expect(filtered.htmlUrl).toContain(`/${original.hruid}/html`); expect(filtered.htmlUrl).toContain(`language=${original.language}`); expect(filtered.htmlUrl).toContain(`version=${original.version}`); From 28e0a7b45d1fc0c4d86bf2a9cbb62a22079f44ad Mon Sep 17 00:00:00 2001 From: Lint Action Date: Tue, 11 Mar 2025 03:45:58 +0000 Subject: [PATCH 63/96] style: fix linting issues met ESLint --- .../database-learning-object-provider.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/tests/services/learning-objects/database-learning-object-provider.test.ts b/backend/tests/services/learning-objects/database-learning-object-provider.test.ts index 9bd9d36a..78d8d3a1 100644 --- a/backend/tests/services/learning-objects/database-learning-object-provider.test.ts +++ b/backend/tests/services/learning-objects/database-learning-object-provider.test.ts @@ -14,8 +14,8 @@ import {LearningPath} from "../../../src/entities/content/learning-path.entity"; async function initExampleData(): Promise<{learningObject: LearningObject, learningPath: LearningPath}> { const learningObjectRepo = getLearningObjectRepository(); const learningPathRepo = getLearningPathRepository(); - let learningObject = learningObjectExample.createLearningObject(); - let learningPath = learningPathExample.createLearningPath(); + const learningObject = learningObjectExample.createLearningObject(); + const learningPath = learningPathExample.createLearningPath(); await learningObjectRepo.save(learningObject); await learningPathRepo.save(learningPath); return { learningObject, learningPath }; @@ -29,7 +29,7 @@ describe('DatabaseLearningObjectProvider', () => { beforeAll(async () => { await setupTestApp(); - let exampleData = await initExampleData(); + const exampleData = await initExampleData(); exampleLearningObject = exampleData.learningObject; exampleLearningPath = exampleData.learningPath; }); From 75d80f65f578f081dcea96164caa277a6c4d49e2 Mon Sep 17 00:00:00 2001 From: Lint Action Date: Tue, 11 Mar 2025 03:46:02 +0000 Subject: [PATCH 64/96] style: fix linting issues met Prettier --- .../database-learning-object-provider.ts | 27 +++--- .../learning-paths/point-of-view.d.ts | 2 +- .../database-learning-object-provider.test.ts | 82 ++++++++++--------- 3 files changed, 54 insertions(+), 57 deletions(-) diff --git a/backend/src/services/learning-objects/database-learning-object-provider.ts b/backend/src/services/learning-objects/database-learning-object-provider.ts index 57ef37bb..186bdffd 100644 --- a/backend/src/services/learning-objects/database-learning-object-provider.ts +++ b/backend/src/services/learning-objects/database-learning-object-provider.ts @@ -1,17 +1,12 @@ -import {LearningObjectProvider} from "./learning-object-provider"; -import { - FilteredLearningObject, - LearningObjectIdentifier, - LearningPathIdentifier -} from "../../interfaces/learning-content"; -import {getLearningObjectRepository, getLearningPathRepository} from "../../data/repositories"; -import {Language} from "../../entities/content/language"; -import {LearningObject} from "../../entities/content/learning-object.entity"; -import {getUrlStringForLearningObject} from "../../util/links"; -import processingService from "./processing/processing-service"; -import {NotFoundError} from "@mikro-orm/core"; -import learningObjectService from "./learning-object-service"; - +import { LearningObjectProvider } from './learning-object-provider'; +import { FilteredLearningObject, LearningObjectIdentifier, LearningPathIdentifier } from '../../interfaces/learning-content'; +import { getLearningObjectRepository, getLearningPathRepository } from '../../data/repositories'; +import { Language } from '../../entities/content/language'; +import { LearningObject } from '../../entities/content/learning-object.entity'; +import { getUrlStringForLearningObject } from '../../util/links'; +import processingService from './processing/processing-service'; +import { NotFoundError } from '@mikro-orm/core'; +import learningObjectService from './learning-object-service'; function convertLearningObject(learningObject: LearningObject | null): FilteredLearningObject | null { if (!learningObject) { @@ -110,8 +105,8 @@ const databaseLearningObjectProvider: LearningObjectProvider = { return learningObject; }) ); - return learningObjects.filter(it => it !== null); - } + return learningObjects.filter((it) => it !== null); + }, }; export default databaseLearningObjectProvider; diff --git a/backend/src/services/learning-paths/point-of-view.d.ts b/backend/src/services/learning-paths/point-of-view.d.ts index e08685b8..14190090 100644 --- a/backend/src/services/learning-paths/point-of-view.d.ts +++ b/backend/src/services/learning-paths/point-of-view.d.ts @@ -1 +1 @@ -type PointOfView = {type: "student", student: Student} | {type: "group", group: Group}; +type PointOfView = { type: 'student'; student: Student } | { type: 'group'; group: Group }; diff --git a/backend/tests/services/learning-objects/database-learning-object-provider.test.ts b/backend/tests/services/learning-objects/database-learning-object-provider.test.ts index 78d8d3a1..692a72de 100644 --- a/backend/tests/services/learning-objects/database-learning-object-provider.test.ts +++ b/backend/tests/services/learning-objects/database-learning-object-provider.test.ts @@ -1,17 +1,17 @@ -import {beforeAll, describe, expect, it} from "vitest"; -import {setupTestApp} from "../../setup-tests"; -import {getLearningObjectRepository, getLearningPathRepository} from "../../../src/data/repositories"; -import example from "../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example"; -import {LearningObject} from "../../../src/entities/content/learning-object.entity"; -import databaseLearningObjectProvider from "../../../src/services/learning-objects/database-learning-object-provider"; -import {expectToBeCorrectFilteredLearningObject} from "../../test-utils/expectations"; -import {FilteredLearningObject} from "../../../src/interfaces/learning-content"; -import {Language} from "../../../src/entities/content/language"; -import learningObjectExample from "../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example"; -import learningPathExample from "../../test-assets/learning-paths/pn-werking-example"; -import {LearningPath} from "../../../src/entities/content/learning-path.entity"; +import { beforeAll, describe, expect, it } from 'vitest'; +import { setupTestApp } from '../../setup-tests'; +import { getLearningObjectRepository, getLearningPathRepository } from '../../../src/data/repositories'; +import example from '../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example'; +import { LearningObject } from '../../../src/entities/content/learning-object.entity'; +import databaseLearningObjectProvider from '../../../src/services/learning-objects/database-learning-object-provider'; +import { expectToBeCorrectFilteredLearningObject } from '../../test-utils/expectations'; +import { FilteredLearningObject } from '../../../src/interfaces/learning-content'; +import { Language } from '../../../src/entities/content/language'; +import learningObjectExample from '../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example'; +import learningPathExample from '../../test-assets/learning-paths/pn-werking-example'; +import { LearningPath } from '../../../src/entities/content/learning-path.entity'; -async function initExampleData(): Promise<{learningObject: LearningObject, learningPath: LearningPath}> { +async function initExampleData(): Promise<{ learningObject: LearningObject; learningPath: LearningPath }> { const learningObjectRepo = getLearningObjectRepository(); const learningPathRepo = getLearningPathRepository(); const learningObject = learningObjectExample.createLearningObject(); @@ -21,7 +21,7 @@ async function initExampleData(): Promise<{learningObject: LearningObject, learn return { learningObject, learningPath }; } -const EXPECTED_TITLE_FROM_DWENGO_LEARNING_OBJECT = "Notebook opslaan"; +const EXPECTED_TITLE_FROM_DWENGO_LEARNING_OBJECT = 'Notebook opslaan'; describe('DatabaseLearningObjectProvider', () => { let exampleLearningObject: LearningObject; @@ -62,45 +62,47 @@ describe('DatabaseLearningObjectProvider', () => { const result = await databaseLearningObjectProvider.getLearningObjectHTML(exampleLearningObject); expect(result).toEqual(example.getHTMLRendering()); }); - it("should return null for a non-existing learning object", async () => { + it('should return null for a non-existing learning object', async () => { const result = await databaseLearningObjectProvider.getLearningObjectHTML({ - hruid: "non_existing_hruid", - language: Language.Dutch + hruid: 'non_existing_hruid', + language: Language.Dutch, }); expect(result).toBeNull(); }); }); - describe("getLearningObjectIdsFromPath", () => { - it("should return all learning object IDs from a path", async () => { + describe('getLearningObjectIdsFromPath', () => { + it('should return all learning object IDs from a path', async () => { const result = await databaseLearningObjectProvider.getLearningObjectIdsFromPath(exampleLearningPath); - expect(new Set(result)) - .toEqual(new Set(exampleLearningPath.nodes.map(it => it.learningObjectHruid))); + expect(new Set(result)).toEqual(new Set(exampleLearningPath.nodes.map((it) => it.learningObjectHruid))); }); - it("should throw an error if queried with a path identifier for which there is no learning path", async () => { - await expect((async () => { - await databaseLearningObjectProvider.getLearningObjectIdsFromPath({ - hruid: "non_existing_hruid", - language: Language.Dutch - }); - })()).rejects.toThrowError(); + it('should throw an error if queried with a path identifier for which there is no learning path', async () => { + await expect( + (async () => { + await databaseLearningObjectProvider.getLearningObjectIdsFromPath({ + hruid: 'non_existing_hruid', + language: Language.Dutch, + }); + })() + ).rejects.toThrowError(); }); }); - describe("getLearningObjectsFromPath", () => { - it("should correctly return all learning objects which are on the path, even those who are not in the database", async () => { + describe('getLearningObjectsFromPath', () => { + it('should correctly return all learning objects which are on the path, even those who are not in the database', async () => { const result = await databaseLearningObjectProvider.getLearningObjectsFromPath(exampleLearningPath); expect(result.length).toBe(exampleLearningPath.nodes.length); - expect(new Set(result.map(it => it.key))) - .toEqual(new Set(exampleLearningPath.nodes.map(it => it.learningObjectHruid))); + expect(new Set(result.map((it) => it.key))).toEqual(new Set(exampleLearningPath.nodes.map((it) => it.learningObjectHruid))); - expect(result.map(it => it.title)).toContainEqual(EXPECTED_TITLE_FROM_DWENGO_LEARNING_OBJECT); + expect(result.map((it) => it.title)).toContainEqual(EXPECTED_TITLE_FROM_DWENGO_LEARNING_OBJECT); }); - it("should throw an error if queried with a path identifier for which there is no learning path", async () => { - await expect((async () => { - await databaseLearningObjectProvider.getLearningObjectsFromPath({ - hruid: "non_existing_hruid", - language: Language.Dutch - }); - })()).rejects.toThrowError(); + it('should throw an error if queried with a path identifier for which there is no learning path', async () => { + await expect( + (async () => { + await databaseLearningObjectProvider.getLearningObjectsFromPath({ + hruid: 'non_existing_hruid', + language: Language.Dutch, + }); + })() + ).rejects.toThrowError(); }); }); }); From 19cd9c43878a80f0fffdc7949a191528790bbb19 Mon Sep 17 00:00:00 2001 From: Gerald Schmittinger Date: Tue, 11 Mar 2025 04:54:19 +0100 Subject: [PATCH 65/96] fix(backend): Conflicten met linter opgelost --- .../dummy/dummy-learning-object-example.ts | 2 +- .../learning-objects/dummy/rendering.html | 0 .../learning-objects/dummy/rendering.txt | 1 + .../pn-werkingnotebooks-example.ts | 2 +- .../pn-werkingnotebooks/rendering.html | 47 ------------------- .../pn-werkingnotebooks/rendering.txt | 20 ++++++++ .../{rendering.html => rendering.txt} | 0 .../test-essay/test-essay-example.ts | 2 +- .../{rendering.html => rendering.txt} | 4 +- .../test-multiple-choice-example.ts | 2 +- backend/tests/test-utils/expectations.ts | 2 +- 11 files changed, 28 insertions(+), 54 deletions(-) delete mode 100644 backend/tests/test-assets/learning-objects/dummy/rendering.html create mode 100644 backend/tests/test-assets/learning-objects/dummy/rendering.txt delete mode 100644 backend/tests/test-assets/learning-objects/pn-werkingnotebooks/rendering.html create mode 100644 backend/tests/test-assets/learning-objects/pn-werkingnotebooks/rendering.txt rename backend/tests/test-assets/learning-objects/test-essay/{rendering.html => rendering.txt} (100%) rename backend/tests/test-assets/learning-objects/test-multiple-choice/{rendering.html => rendering.txt} (92%) diff --git a/backend/tests/test-assets/learning-objects/dummy/dummy-learning-object-example.ts b/backend/tests/test-assets/learning-objects/dummy/dummy-learning-object-example.ts index 5b773d09..6dbe54b0 100644 --- a/backend/tests/test-assets/learning-objects/dummy/dummy-learning-object-example.ts +++ b/backend/tests/test-assets/learning-objects/dummy/dummy-learning-object-example.ts @@ -22,6 +22,6 @@ export function dummyLearningObject(hruid: string, language: Language, title: st return learningObject; }, createAttachment: {}, - getHTMLRendering: () => loadTestAsset('learning-objects/dummy/rendering.html').toString(), + getHTMLRendering: () => loadTestAsset('learning-objects/dummy/rendering.txt').toString(), }; } diff --git a/backend/tests/test-assets/learning-objects/dummy/rendering.html b/backend/tests/test-assets/learning-objects/dummy/rendering.html deleted file mode 100644 index e69de29b..00000000 diff --git a/backend/tests/test-assets/learning-objects/dummy/rendering.txt b/backend/tests/test-assets/learning-objects/dummy/rendering.txt new file mode 100644 index 00000000..f3ae8006 --- /dev/null +++ b/backend/tests/test-assets/learning-objects/dummy/rendering.txt @@ -0,0 +1 @@ +Dummy content diff --git a/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example.ts b/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example.ts index 5bca09a6..600a4305 100644 --- a/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example.ts +++ b/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example.ts @@ -67,6 +67,6 @@ const example: LearningObjectExample = { return att; }, }, - getHTMLRendering: () => loadTestAsset(`${ASSETS_PREFIX}/rendering.html`).toString(), + getHTMLRendering: () => loadTestAsset(`${ASSETS_PREFIX}/rendering.txt`).toString(), }; export default example; diff --git a/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/rendering.html b/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/rendering.html deleted file mode 100644 index ab9bd34e..00000000 --- a/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/rendering.html +++ /dev/null @@ -1,47 +0,0 @@ -

- - - - Werken met notebooks -

-

- Het lesmateriaal van 'Python in wiskunde en STEM' wordt aangeboden in de vorm van interactieve notebooks. Notebooks zijn digitale documenten die zowel uitvoerbare code bevatten als tekst, afbeeldingen, video, hyperlinks ... -

-

Nieuwe begrippen worden aangebracht via tekstuele uitleg, video en afbeeldingen.

-

- Er zijn uitgewerkte voorbeelden met daarnaast ook kleine en grote opdrachten. In deze opdrachten zal je aangereikte code kunnen - uitvoeren, maar ook zelf code opstellen. -

-

- De code die in de notebooks gebruikt wordt, is Python versie 3. We kozen voor Python omdat dit een heel toegankelijke programmeertaal is, die vaak - ook intuC/tief is. Python is bovendien bezig aan een opmars en wordt gebruikt door bedrijven, zoals Google, NASA, Netflix, Uber, AstraZeneca, - Barco, Instagram en YouTube. -

-

- We kozen voor notebooks omdat daar enkele belangrijke voordelen aan verbonden zijn: leerkrachten moeten geen geavanceerde installaties doen om de - notebooks te gebruiken, leerkrachten kunnen verschillende soorten van lesinhouden aanbieden via C)C)n platform, de notebooks zijn interactief, - leerlingen bouwen de oplossing van een probleem stap voor stap op in de notebook waardoor dat proces zichtbaar is voor de leerkracht (Jeroen Van der Hooft, 2023). -

-
-

- Klik je op onderstaande knop 'Open notebooks', dan word je doorgestuurd naar een andere website waar jouw persoonlijke notebooks ingeladen - worden. (Dit kan even duren.) -

-

- Links op het scherm vind je er twee bestanden met extensie .ipynb. Dit zijn de twee notebooks waarin je resp. een overzicht krijgt van de - opbouw en mogelijkheden en hoe je er mee aan de slag kan. Dubbelklik op de bestandsnaam om een notebook te openen. -

-

Je ziet er ook een map images met de afbeeldingen die in de notebooks getoond worden.

-

- In deze eerste twee notebooks leer je hoe de notebooks zijn opgevat en hoe je ermee aan de slag kan. Na het doorlopen van beide notebooks heb je - een goed idee van hoe onze Python notebooks zijn opgevat. -

-

- -

diff --git a/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/rendering.txt b/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/rendering.txt new file mode 100644 index 00000000..af596243 --- /dev/null +++ b/backend/tests/test-assets/learning-objects/pn-werkingnotebooks/rendering.txt @@ -0,0 +1,20 @@ +

+ + + + Werken met notebooks +

+

Het lesmateriaal van 'Python in wiskunde en STEM' wordt aangeboden in de vorm van interactieve notebooks. Notebooks zijn digitale documenten die zowel uitvoerbare code bevatten als tekst, afbeeldingen, video, hyperlinks ...

+

Nieuwe begrippen worden aangebracht via tekstuele uitleg, video en afbeeldingen.

+

Er zijn uitgewerkte voorbeelden met daarnaast ook kleine en grote opdrachten. In deze opdrachten zal je aangereikte code kunnen uitvoeren, maar ook zelf code opstellen.

+

De code die in de notebooks gebruikt wordt, is Python versie 3. We kozen voor Python omdat dit een heel toegankelijke programmeertaal is, die vaak ook intuC/tief is. +Python is bovendien bezig aan een opmars en wordt gebruikt door bedrijven, zoals Google, NASA, Netflix, Uber, AstraZeneca, Barco, Instagram en YouTube.

+

We kozen voor notebooks omdat daar enkele belangrijke voordelen aan verbonden zijn: leerkrachten moeten geen geavanceerde installaties doen om de notebooks te gebruiken, leerkrachten kunnen verschillende soorten van lesinhouden aanbieden via C)C)n platform, de notebooks zijn interactief, leerlingen bouwen de oplossing van een probleem stap voor stap op in de notebook waardoor dat proces zichtbaar is voor de leerkracht (Jeroen Van der Hooft, 2023).

+
+

Klik je op onderstaande knop 'Open notebooks', dan word je doorgestuurd naar een andere website waar jouw persoonlijke notebooks ingeladen worden. (Dit kan even duren.)

+

Links op het scherm vind je er twee bestanden met extensie .ipynb. +Dit zijn de twee notebooks waarin je resp. een overzicht krijgt van de opbouw en mogelijkheden en hoe je er mee aan de slag kan. Dubbelklik op de bestandsnaam om een notebook te openen.

+

Je ziet er ook een map images met de afbeeldingen die in de notebooks getoond worden.

+

In deze eerste twee notebooks leer je hoe de notebooks zijn opgevat en hoe je ermee aan de slag kan. +Na het doorlopen van beide notebooks heb je een goed idee van hoe onze Python notebooks zijn opgevat.

+

diff --git a/backend/tests/test-assets/learning-objects/test-essay/rendering.html b/backend/tests/test-assets/learning-objects/test-essay/rendering.txt similarity index 100% rename from backend/tests/test-assets/learning-objects/test-essay/rendering.html rename to backend/tests/test-assets/learning-objects/test-essay/rendering.txt diff --git a/backend/tests/test-assets/learning-objects/test-essay/test-essay-example.ts b/backend/tests/test-assets/learning-objects/test-essay/test-essay-example.ts index 7a2f54d9..b1ff330e 100644 --- a/backend/tests/test-assets/learning-objects/test-essay/test-essay-example.ts +++ b/backend/tests/test-assets/learning-objects/test-essay/test-essay-example.ts @@ -18,7 +18,7 @@ const example: LearningObjectExample = { return learningObject; }, createAttachment: {}, - getHTMLRendering: () => loadTestAsset('learning-objects/test-essay/rendering.html').toString(), + getHTMLRendering: () => loadTestAsset('learning-objects/test-essay/rendering.txt').toString(), }; export default example; diff --git a/backend/tests/test-assets/learning-objects/test-multiple-choice/rendering.html b/backend/tests/test-assets/learning-objects/test-multiple-choice/rendering.txt similarity index 92% rename from backend/tests/test-assets/learning-objects/test-multiple-choice/rendering.html rename to backend/tests/test-assets/learning-objects/test-multiple-choice/rendering.txt index 513a29a5..c1829f24 100644 --- a/backend/tests/test-assets/learning-objects/test-multiple-choice/rendering.html +++ b/backend/tests/test-assets/learning-objects/test-multiple-choice/rendering.txt @@ -3,11 +3,11 @@

MC basic

Are you following along well with the class?

- +
- +
diff --git a/backend/tests/test-assets/learning-objects/test-multiple-choice/test-multiple-choice-example.ts b/backend/tests/test-assets/learning-objects/test-multiple-choice/test-multiple-choice-example.ts index 52b6adbe..cfb0b5a2 100644 --- a/backend/tests/test-assets/learning-objects/test-multiple-choice/test-multiple-choice-example.ts +++ b/backend/tests/test-assets/learning-objects/test-multiple-choice/test-multiple-choice-example.ts @@ -18,7 +18,7 @@ const example: LearningObjectExample = { return learningObject; }, createAttachment: {}, - getHTMLRendering: () => loadTestAsset('learning-objects/test-multiple-choice/rendering.html').toString(), + getHTMLRendering: () => loadTestAsset('learning-objects/test-multiple-choice/rendering.txt').toString(), }; export default example; diff --git a/backend/tests/test-utils/expectations.ts b/backend/tests/test-utils/expectations.ts index f9fe2c68..0fe63811 100644 --- a/backend/tests/test-utils/expectations.ts +++ b/backend/tests/test-utils/expectations.ts @@ -77,7 +77,7 @@ export function expectToBeCorrectFilteredLearningObject(filtered: FilteredLearni expect(filtered.key).toEqual(original.hruid); expect(filtered.targetAges).toEqual(original.targetAges); expect(filtered.title).toEqual(original.title); - expect(Boolean(filtered.teacherExclusive)).toEqual(original.teacherExclusive); // !!: Workaround: MikroORM with SQLite returns 0 and 1 instead of booleans. + expect(Boolean(filtered.teacherExclusive)).toEqual(Boolean(original.teacherExclusive)); expect(filtered.skosConcepts).toEqual(original.skosConcepts); expect(filtered.estimatedTime).toEqual(original.estimatedTime); expect(filtered.educationalGoals).toEqual(original.educationalGoals); From b630b9a45d12498958840fa52b3557c91aadaf75 Mon Sep 17 00:00:00 2001 From: Gerald Schmittinger Date: Tue, 11 Mar 2025 05:07:35 +0100 Subject: [PATCH 66/96] test(backend): Testen LearningObjectService.getLearningObject[Id]sFromPath toegevoegd --- .../learning-object-service.test.ts | 65 +++++++++++++++---- 1 file changed, 54 insertions(+), 11 deletions(-) diff --git a/backend/tests/services/learning-objects/learning-object-service.test.ts b/backend/tests/services/learning-objects/learning-object-service.test.ts index 26f03595..f4e40747 100644 --- a/backend/tests/services/learning-objects/learning-object-service.test.ts +++ b/backend/tests/services/learning-objects/learning-object-service.test.ts @@ -1,12 +1,14 @@ -import { beforeAll, describe, expect, it } from 'vitest'; -import { setupTestApp } from '../../setup-tests'; -import { LearningObject } from '../../../src/entities/content/learning-object.entity'; -import { getLearningObjectRepository } from '../../../src/data/repositories'; +import {beforeAll, describe, expect, it} from 'vitest'; +import {setupTestApp} from '../../setup-tests'; +import {LearningObject} from '../../../src/entities/content/learning-object.entity'; +import {getLearningObjectRepository, getLearningPathRepository} from '../../../src/data/repositories'; import learningObjectExample from '../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example'; import learningObjectService from '../../../src/services/learning-objects/learning-object-service'; -import { LearningObjectIdentifier } from '../../../src/interfaces/learning-content'; -import { Language } from '../../../src/entities/content/language'; -import { EnvVars, getEnvVar } from '../../../src/util/envvars'; +import {LearningObjectIdentifier, LearningPathIdentifier} from '../../../src/interfaces/learning-content'; +import {Language} from '../../../src/entities/content/language'; +import {EnvVars, getEnvVar} from '../../../src/util/envvars'; +import {LearningPath} from "../../../src/entities/content/learning-path.entity"; +import learningPathExample from "../../test-assets/learning-paths/pn-werking-example"; const TEST_LEARNING_OBJECT_TITLE = 'Test title'; const EXPECTED_DWENGO_LEARNING_OBJECT_TITLE = 'Werken met notebooks'; @@ -16,20 +18,31 @@ const DWENGO_TEST_LEARNING_OBJECT_ID: LearningObjectIdentifier = { version: 3, }; -async function initExampleData(): Promise { +const DWENGO_TEST_LEARNING_PATH_ID: LearningPathIdentifier = { + hruid: 'pn_werking', + language: Language.Dutch +}; +const DWENGO_TEST_LEARNING_PATH_HRUIDS = new Set(["pn_werkingnotebooks", "pn_werkingnotebooks2", "pn_werkingnotebooks3"]); + +async function initExampleData(): Promise<{ learningObject: LearningObject; learningPath: LearningPath }> { const learningObjectRepo = getLearningObjectRepository(); + const learningPathRepo = getLearningPathRepository(); const learningObject = learningObjectExample.createLearningObject(); - learningObject.title = TEST_LEARNING_OBJECT_TITLE; + const learningPath = learningPathExample.createLearningPath(); await learningObjectRepo.save(learningObject); - return learningObject; + await learningPathRepo.save(learningPath); + return { learningObject, learningPath }; } describe('LearningObjectService', () => { let exampleLearningObject: LearningObject; + let exampleLearningPath: LearningPath; beforeAll(async () => { await setupTestApp(); - exampleLearningObject = await initExampleData(); + const exampleData = await initExampleData(); + exampleLearningObject = exampleData.learningObject; + exampleLearningPath = exampleData.learningPath; }); describe('getLearningObjectById', () => { @@ -80,4 +93,34 @@ describe('LearningObjectService', () => { expect(result).toBeNull(); }); }); + + describe('getLearningObjectsFromPath', () => { + it("returns all learning objects when a learning path in the database is queried", async () => { + const result = await learningObjectService.getLearningObjectsFromPath(exampleLearningPath); + expect(result.map(it => it.key)).toEqual(exampleLearningPath.nodes.map(it => it.learningObjectHruid)); + }); + it("also returns all learning objects when a learning path from the Dwengo API is queried", async () => { + const result = await learningObjectService.getLearningObjectsFromPath(DWENGO_TEST_LEARNING_PATH_ID); + expect(new Set(result.map(it => it.key))).toEqual(DWENGO_TEST_LEARNING_PATH_HRUIDS); + }); + it("returns an empty list when queried with a non-existing learning path id", async () => { + const result = await learningObjectService.getLearningObjectsFromPath({hruid: "non_existing", language: Language.Dutch}); + expect(result).toEqual([]); + }); + }); + + describe('getLearningObjectIdsFromPath', () => { + it("returns all learning objects when a learning path in the database is queried", async () => { + const result = await learningObjectService.getLearningObjectIdsFromPath(exampleLearningPath); + expect(result).toEqual(exampleLearningPath.nodes.map(it => it.learningObjectHruid)); + }); + it("also returns all learning object hruids when a learning path from the Dwengo API is queried", async () => { + const result = await learningObjectService.getLearningObjectIdsFromPath(DWENGO_TEST_LEARNING_PATH_ID); + expect(new Set(result)).toEqual(DWENGO_TEST_LEARNING_PATH_HRUIDS); + }); + it("returns an empty list when queried with a non-existing learning path id", async () => { + const result = await learningObjectService.getLearningObjectIdsFromPath({hruid: "non_existing", language: Language.Dutch}); + expect(result).toEqual([]); + }); + }); }); From 466b9b8d177093e6b8c61804b82b2b2a69a81c0f Mon Sep 17 00:00:00 2001 From: Lint Action Date: Tue, 11 Mar 2025 04:08:12 +0000 Subject: [PATCH 67/96] style: fix linting issues met Prettier --- .../learning-object-service.test.ts | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/backend/tests/services/learning-objects/learning-object-service.test.ts b/backend/tests/services/learning-objects/learning-object-service.test.ts index f4e40747..f748d9f3 100644 --- a/backend/tests/services/learning-objects/learning-object-service.test.ts +++ b/backend/tests/services/learning-objects/learning-object-service.test.ts @@ -1,14 +1,14 @@ -import {beforeAll, describe, expect, it} from 'vitest'; -import {setupTestApp} from '../../setup-tests'; -import {LearningObject} from '../../../src/entities/content/learning-object.entity'; -import {getLearningObjectRepository, getLearningPathRepository} from '../../../src/data/repositories'; +import { beforeAll, describe, expect, it } from 'vitest'; +import { setupTestApp } from '../../setup-tests'; +import { LearningObject } from '../../../src/entities/content/learning-object.entity'; +import { getLearningObjectRepository, getLearningPathRepository } from '../../../src/data/repositories'; import learningObjectExample from '../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example'; import learningObjectService from '../../../src/services/learning-objects/learning-object-service'; -import {LearningObjectIdentifier, LearningPathIdentifier} from '../../../src/interfaces/learning-content'; -import {Language} from '../../../src/entities/content/language'; -import {EnvVars, getEnvVar} from '../../../src/util/envvars'; -import {LearningPath} from "../../../src/entities/content/learning-path.entity"; -import learningPathExample from "../../test-assets/learning-paths/pn-werking-example"; +import { LearningObjectIdentifier, LearningPathIdentifier } from '../../../src/interfaces/learning-content'; +import { Language } from '../../../src/entities/content/language'; +import { EnvVars, getEnvVar } from '../../../src/util/envvars'; +import { LearningPath } from '../../../src/entities/content/learning-path.entity'; +import learningPathExample from '../../test-assets/learning-paths/pn-werking-example'; const TEST_LEARNING_OBJECT_TITLE = 'Test title'; const EXPECTED_DWENGO_LEARNING_OBJECT_TITLE = 'Werken met notebooks'; @@ -20,9 +20,9 @@ const DWENGO_TEST_LEARNING_OBJECT_ID: LearningObjectIdentifier = { const DWENGO_TEST_LEARNING_PATH_ID: LearningPathIdentifier = { hruid: 'pn_werking', - language: Language.Dutch + language: Language.Dutch, }; -const DWENGO_TEST_LEARNING_PATH_HRUIDS = new Set(["pn_werkingnotebooks", "pn_werkingnotebooks2", "pn_werkingnotebooks3"]); +const DWENGO_TEST_LEARNING_PATH_HRUIDS = new Set(['pn_werkingnotebooks', 'pn_werkingnotebooks2', 'pn_werkingnotebooks3']); async function initExampleData(): Promise<{ learningObject: LearningObject; learningPath: LearningPath }> { const learningObjectRepo = getLearningObjectRepository(); @@ -95,31 +95,31 @@ describe('LearningObjectService', () => { }); describe('getLearningObjectsFromPath', () => { - it("returns all learning objects when a learning path in the database is queried", async () => { + it('returns all learning objects when a learning path in the database is queried', async () => { const result = await learningObjectService.getLearningObjectsFromPath(exampleLearningPath); - expect(result.map(it => it.key)).toEqual(exampleLearningPath.nodes.map(it => it.learningObjectHruid)); + expect(result.map((it) => it.key)).toEqual(exampleLearningPath.nodes.map((it) => it.learningObjectHruid)); }); - it("also returns all learning objects when a learning path from the Dwengo API is queried", async () => { + it('also returns all learning objects when a learning path from the Dwengo API is queried', async () => { const result = await learningObjectService.getLearningObjectsFromPath(DWENGO_TEST_LEARNING_PATH_ID); - expect(new Set(result.map(it => it.key))).toEqual(DWENGO_TEST_LEARNING_PATH_HRUIDS); + expect(new Set(result.map((it) => it.key))).toEqual(DWENGO_TEST_LEARNING_PATH_HRUIDS); }); - it("returns an empty list when queried with a non-existing learning path id", async () => { - const result = await learningObjectService.getLearningObjectsFromPath({hruid: "non_existing", language: Language.Dutch}); + it('returns an empty list when queried with a non-existing learning path id', async () => { + const result = await learningObjectService.getLearningObjectsFromPath({ hruid: 'non_existing', language: Language.Dutch }); expect(result).toEqual([]); }); }); describe('getLearningObjectIdsFromPath', () => { - it("returns all learning objects when a learning path in the database is queried", async () => { + it('returns all learning objects when a learning path in the database is queried', async () => { const result = await learningObjectService.getLearningObjectIdsFromPath(exampleLearningPath); - expect(result).toEqual(exampleLearningPath.nodes.map(it => it.learningObjectHruid)); + expect(result).toEqual(exampleLearningPath.nodes.map((it) => it.learningObjectHruid)); }); - it("also returns all learning object hruids when a learning path from the Dwengo API is queried", async () => { + it('also returns all learning object hruids when a learning path from the Dwengo API is queried', async () => { const result = await learningObjectService.getLearningObjectIdsFromPath(DWENGO_TEST_LEARNING_PATH_ID); expect(new Set(result)).toEqual(DWENGO_TEST_LEARNING_PATH_HRUIDS); }); - it("returns an empty list when queried with a non-existing learning path id", async () => { - const result = await learningObjectService.getLearningObjectIdsFromPath({hruid: "non_existing", language: Language.Dutch}); + it('returns an empty list when queried with a non-existing learning path id', async () => { + const result = await learningObjectService.getLearningObjectIdsFromPath({ hruid: 'non_existing', language: Language.Dutch }); expect(result).toEqual([]); }); }); From a69e2625afae8ac714b3f9eb8cc60907e67b3421 Mon Sep 17 00:00:00 2001 From: Gerald Schmittinger Date: Tue, 11 Mar 2025 06:13:29 +0100 Subject: [PATCH 68/96] feat(backend): Added support for customized learning paths to the database learning path provider. --- backend/src/interfaces/learning-content.ts | 1 + .../database-learning-path-provider.ts | 40 +++++++---- .../learning-path-personalization-util.ts | 42 ++++++++++++ .../learning-path-personalizing-service.ts | 68 ------------------- .../learning-paths/learning-path-provider.ts | 3 +- .../learning-paths/learning-path-service.ts | 15 ++-- .../learning-paths/point-of-view.d.ts | 1 - 7 files changed, 84 insertions(+), 86 deletions(-) create mode 100644 backend/src/services/learning-paths/learning-path-personalization-util.ts delete mode 100644 backend/src/services/learning-paths/learning-path-personalizing-service.ts delete mode 100644 backend/src/services/learning-paths/point-of-view.d.ts diff --git a/backend/src/interfaces/learning-content.ts b/backend/src/interfaces/learning-content.ts index 970796a2..a558dd9c 100644 --- a/backend/src/interfaces/learning-content.ts +++ b/backend/src/interfaces/learning-content.ts @@ -26,6 +26,7 @@ export interface LearningObjectNode { transitions: Transition[]; created_at: string; updatedAt: string; + done?: boolean; // true if a submission exists for this node by the user for whom the learning path is customized. } export interface LearningPath { diff --git a/backend/src/services/learning-paths/database-learning-path-provider.ts b/backend/src/services/learning-paths/database-learning-path-provider.ts index 3b3b49af..d804ad64 100644 --- a/backend/src/services/learning-paths/database-learning-path-provider.ts +++ b/backend/src/services/learning-paths/database-learning-path-provider.ts @@ -6,6 +6,11 @@ import { Language } from '../../entities/content/language'; import learningObjectService from '../learning-objects/learning-object-service'; import { LearningPathNode } from '../../entities/content/learning-path-node.entity'; import { LearningPathTransition } from '../../entities/content/learning-path-transition.entity'; +import { + getLastSubmissionForCustomizationTarget, + isTransitionPossible, + PersonalizationTarget +} from "./learning-path-personalization-util"; /** * Fetches the corresponding learning object for each of the nodes and creates a map that maps each node to its @@ -37,7 +42,7 @@ async function getLearningObjectsForNodes(nodes: LearningPathNode[]): Promise { +async function convertLearningPath(learningPath: LearningPathEntity, order: number, personalizedFor?: PersonalizationTarget): Promise { const nodesToLearningObjects: Map = await getLearningObjectsForNodes(learningPath.nodes); const targetAges = nodesToLearningObjects @@ -52,6 +57,8 @@ async function convertLearningPath(learningPath: LearningPathEntity, order: numb const image = learningPath.image ? learningPath.image.toString('base64') : undefined; + const convertedNodes = await convertNodes(nodesToLearningObjects, personalizedFor); + return { _id: `${learningPath.hruid}/${learningPath.language}`, // For backwards compatibility with the original Dwengo API. __order: order, @@ -60,9 +67,9 @@ async function convertLearningPath(learningPath: LearningPathEntity, order: numb description: learningPath.description, image: image, title: learningPath.title, - nodes: convertNodes(nodesToLearningObjects), + nodes: convertedNodes, num_nodes: learningPath.nodes.length, - num_nodes_left: learningPath.nodes.length, // TODO: Adjust when submissions are added. + num_nodes_left: convertedNodes.filter(it => !it.done).length, keywords: keywords.join(' '), target_ages: targetAges, max_age: Math.max(...targetAges), @@ -74,12 +81,14 @@ async function convertLearningPath(learningPath: LearningPathEntity, order: numb * Helper function converting pairs of learning path nodes (as represented in the database) and the corresponding * learning objects into a list of learning path nodes as they should be represented in the API. * @param nodesToLearningObjects + * @param personalizedFor */ -function convertNodes(nodesToLearningObjects: Map): LearningObjectNode[] { - return nodesToLearningObjects +async function convertNodes(nodesToLearningObjects: Map, personalizedFor?: PersonalizationTarget): Promise { + const nodesPromise = nodesToLearningObjects .entries() - .map((entry) => { + .map(async(entry) => { const [node, learningObject] = entry; + const lastSubmission = personalizedFor ? await getLastSubmissionForCustomizationTarget(node, personalizedFor) : null; return { _id: learningObject.uuid, language: learningObject.language, @@ -88,10 +97,13 @@ function convertNodes(nodesToLearningObjects: Map convertTransition(trans, i, nodesToLearningObjects)), + transitions: node.transitions + .filter(trans => !personalizedFor || isTransitionPossible(trans, lastSubmission)) // If we want a personalized learning path, remove all transitions that aren't possible. + .map((trans, i) => convertTransition(trans, i, nodesToLearningObjects)), // then convert all the transition }; }) .toArray(); + return await Promise.all(nodesPromise); } /** @@ -131,12 +143,16 @@ const databaseLearningPathProvider: LearningPathProvider = { /** * Fetch the learning paths with the given hruids from the database. */ - async fetchLearningPaths(hruids: string[], language: Language, source: string): Promise { + async fetchLearningPaths(hruids: string[], language: Language, source: string, personalizedFor?: PersonalizationTarget): Promise { const learningPathRepo = getLearningPathRepository(); - const learningPaths = await Promise.all(hruids.map((hruid) => learningPathRepo.findByHruidAndLanguage(hruid, language))); + const learningPaths = ( + await Promise.all( + hruids.map((hruid) => learningPathRepo.findByHruidAndLanguage(hruid, language)) + ) + ).filter((learningPath) => learningPath !== null); const filteredLearningPaths = await Promise.all( - learningPaths.filter((learningPath) => learningPath !== null).map((learningPath, index) => convertLearningPath(learningPath, index)) + learningPaths.map((learningPath, index) => convertLearningPath(learningPath, index, personalizedFor)) ); return { @@ -149,11 +165,11 @@ const databaseLearningPathProvider: LearningPathProvider = { /** * Search learning paths in the database using the given search string. */ - async searchLearningPaths(query: string, language: Language): Promise { + async searchLearningPaths(query: string, language: Language, personalizedFor?: PersonalizationTarget): Promise { const learningPathRepo = getLearningPathRepository(); const searchResults = await learningPathRepo.findByQueryStringAndLanguage(query, language); - return await Promise.all(searchResults.map((result, index) => convertLearningPath(result, index))); + return await Promise.all(searchResults.map((result, index) => convertLearningPath(result, index, personalizedFor))); }, }; diff --git a/backend/src/services/learning-paths/learning-path-personalization-util.ts b/backend/src/services/learning-paths/learning-path-personalization-util.ts new file mode 100644 index 00000000..9217fa34 --- /dev/null +++ b/backend/src/services/learning-paths/learning-path-personalization-util.ts @@ -0,0 +1,42 @@ +import {LearningPathNode} from "../../entities/content/learning-path-node.entity"; +import {Student} from "../../entities/users/student.entity"; +import {Group} from "../../entities/assignments/group.entity"; +import {Submission} from "../../entities/assignments/submission.entity"; +import {getSubmissionRepository} from "../../data/repositories"; +import {LearningObjectIdentifier} from "../../entities/content/learning-object-identifier"; +import {LearningPathTransition} from "../../entities/content/learning-path-transition.entity"; +import {JSONPath} from "jsonpath-plus"; + +export type PersonalizationTarget = { type: 'student'; student: Student } | { type: 'group'; group: Group }; +/** + * Returns the last submission for the learning object associated with the given node and for the student or group + */ +export async function getLastSubmissionForCustomizationTarget(node: LearningPathNode, pathFor: PersonalizationTarget): Promise { + const submissionRepo = getSubmissionRepository(); + const learningObjectId: LearningObjectIdentifier = { + hruid: node.learningObjectHruid, + language: node.language, + version: node.version, + }; + if (pathFor.type === 'group') { + return await submissionRepo.findMostRecentSubmissionForGroup(learningObjectId, pathFor.group); + } else { + return await submissionRepo.findMostRecentSubmissionForStudent(learningObjectId, pathFor.student); + } +} + + +/** + * Checks whether the condition of the given transaction is fulfilled by the given submission. + * @param transition + * @param submitted + */ +export function isTransitionPossible(transition: LearningPathTransition, submitted: object | null): boolean { + if (transition.condition === 'true' || !transition.condition) { + return true; // If the transition is unconditional, we can go on. + } + if (submitted === null) { + return false; // If the transition is not unconditional and there was no submission, the transition is not possible. + } + return JSONPath({ path: transition.condition, json: submitted }).length === 0; +} diff --git a/backend/src/services/learning-paths/learning-path-personalizing-service.ts b/backend/src/services/learning-paths/learning-path-personalizing-service.ts deleted file mode 100644 index f204d742..00000000 --- a/backend/src/services/learning-paths/learning-path-personalizing-service.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Student } from '../../entities/users/student.entity'; -import { getSubmissionRepository } from '../../data/repositories'; -import { Group } from '../../entities/assignments/group.entity'; -import { Submission } from '../../entities/assignments/submission.entity'; -import { LearningObjectIdentifier } from '../../entities/content/learning-object-identifier'; -import { LearningPathNode } from '../../entities/content/learning-path-node.entity'; -import { LearningPathTransition } from '../../entities/content/learning-path-transition.entity'; -import { JSONPath } from 'jsonpath-plus'; - -/** - * Returns the last submission for the learning object associated with the given node and for the student or group - */ -async function getLastRelevantSubmission(node: LearningPathNode, pathFor: { student?: Student; group?: Group }): Promise { - const submissionRepo = getSubmissionRepository(); - const learningObjectId: LearningObjectIdentifier = { - hruid: node.learningObjectHruid, - language: node.language, - version: node.version, - }; - let lastSubmission: Submission | null; - if (pathFor.group) { - lastSubmission = await submissionRepo.findMostRecentSubmissionForGroup(learningObjectId, pathFor.group); - } else if (pathFor.student) { - lastSubmission = await submissionRepo.findMostRecentSubmissionForStudent(learningObjectId, pathFor.student); - } else { - throw new Error('The path must either be created for a certain group or for a certain student!'); - } - return lastSubmission; -} - -function transitionPossible(transition: LearningPathTransition, submitted: object | null): boolean { - if (transition.condition === 'true' || !transition.condition) { - return true; // If the transition is unconditional, we can go on. - } - if (submitted === null) { - return false; // If the transition is not unconditional and there was no submission, the transition is not possible. - } - return JSONPath({ path: transition.condition, json: submitted }).length === 0; -} - -/** - * Service to create individual trajectories from learning paths based on the submissions of the student or group. - */ -const learningPathPersonalizingService = { - async calculatePersonalizedTrajectory(nodes: LearningPathNode[], pathFor: { student?: Student; group?: Group }): Promise { - const trajectory: LearningPathNode[] = []; - - // Always start with the start node. - let currentNode = nodes.filter((it) => it.startNode)[0]; - trajectory.push(currentNode); - - while (true) { - // At every node, calculate all the possible next transitions. - const lastSubmission = await getLastRelevantSubmission(currentNode, pathFor); - const submitted = lastSubmission === null ? null : JSON.parse(lastSubmission.content); - const possibleTransitions = currentNode.transitions.filter((it) => transitionPossible(it, submitted)); - - if (possibleTransitions.length === 0) { - // If there are none, the trajectory has ended. - return trajectory; - } // Otherwise, take the first possible transition. - currentNode = possibleTransitions[0].node; - trajectory.push(currentNode); - } - }, -}; - -export default learningPathPersonalizingService; diff --git a/backend/src/services/learning-paths/learning-path-provider.ts b/backend/src/services/learning-paths/learning-path-provider.ts index baff35bf..571c231a 100644 --- a/backend/src/services/learning-paths/learning-path-provider.ts +++ b/backend/src/services/learning-paths/learning-path-provider.ts @@ -1,5 +1,6 @@ import { LearningPath, LearningPathResponse } from '../../interfaces/learning-content'; import { Language } from '../../entities/content/language'; +import {PersonalizationTarget} from "./learning-path-personalizing-service"; /** * Generic interface for a service which provides access to learning paths from a data source. @@ -8,7 +9,7 @@ export interface LearningPathProvider { /** * Fetch the learning paths with the given hruids from the data source. */ - fetchLearningPaths(hruids: string[], language: Language, source: string): Promise; + fetchLearningPaths(hruids: string[], language: Language, source: string, personalizedFor?: PersonalizationTarget): Promise; /** * Search learning paths in the data source using the given search string. diff --git a/backend/src/services/learning-paths/learning-path-service.ts b/backend/src/services/learning-paths/learning-path-service.ts index 23f0d9ba..c204872c 100644 --- a/backend/src/services/learning-paths/learning-path-service.ts +++ b/backend/src/services/learning-paths/learning-path-service.ts @@ -3,6 +3,7 @@ import dwengoApiLearningPathProvider from './dwengo-api-learning-path-provider'; import databaseLearningPathProvider from './database-learning-path-provider'; import { EnvVars, getEnvVar } from '../../util/envvars'; import { Language } from '../../entities/content/language'; +import {PersonalizationTarget} from "./learning-path-personalizing-service"; const userContentPrefix = getEnvVar(EnvVars.UserContentPrefix); const allProviders = [dwengoApiLearningPathProvider, databaseLearningPathProvider]; @@ -13,16 +14,22 @@ const allProviders = [dwengoApiLearningPathProvider, databaseLearningPathProvide const learningPathService = { /** * Fetch the learning paths with the given hruids from the data source. + * @param hruids For each of the hruids, the learning path will be fetched. + * @param language This is the language each of the learning paths will use. + * @param source + * @param personalizedFor If this is set, a learning path personalized for the given group or student will be returned. */ - async fetchLearningPaths(hruids: string[], language: Language, source: string): Promise { + async fetchLearningPaths(hruids: string[], language: Language, source: string, personalizedFor?: PersonalizationTarget): Promise { const userContentHruids = hruids.filter((hruid) => hruid.startsWith(userContentPrefix)); const nonUserContentHruids = hruids.filter((hruid) => !hruid.startsWith(userContentPrefix)); - const userContentLearningPaths = await databaseLearningPathProvider.fetchLearningPaths(userContentHruids, language, source); - const nonUserContentLearningPaths = await dwengoApiLearningPathProvider.fetchLearningPaths(nonUserContentHruids, language, source); + const userContentLearningPaths = await databaseLearningPathProvider.fetchLearningPaths(userContentHruids, language, source, personalizedFor); + const nonUserContentLearningPaths = await dwengoApiLearningPathProvider.fetchLearningPaths(nonUserContentHruids, language, source, personalizedFor); + + let result = (userContentLearningPaths.data || []).concat(nonUserContentLearningPaths.data || []); return { - data: (userContentLearningPaths.data || []).concat(nonUserContentLearningPaths.data || []), + data: result, source: source, success: userContentLearningPaths.success || nonUserContentLearningPaths.success, }; diff --git a/backend/src/services/learning-paths/point-of-view.d.ts b/backend/src/services/learning-paths/point-of-view.d.ts deleted file mode 100644 index 14190090..00000000 --- a/backend/src/services/learning-paths/point-of-view.d.ts +++ /dev/null @@ -1 +0,0 @@ -type PointOfView = { type: 'student'; student: Student } | { type: 'group'; group: Group }; From bdbfe380bedf4dd97166a7425fe17dd9250aaa9a Mon Sep 17 00:00:00 2001 From: Lint Action Date: Tue, 11 Mar 2025 05:14:04 +0000 Subject: [PATCH 69/96] style: fix linting issues met ESLint --- backend/src/interfaces/learning-content.ts | 2 +- .../learning-paths/database-learning-path-provider.ts | 2 +- .../learning-paths/learning-path-personalization-util.ts | 4 ++-- backend/src/services/learning-paths/learning-path-service.ts | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/src/interfaces/learning-content.ts b/backend/src/interfaces/learning-content.ts index a558dd9c..997865ef 100644 --- a/backend/src/interfaces/learning-content.ts +++ b/backend/src/interfaces/learning-content.ts @@ -26,7 +26,7 @@ export interface LearningObjectNode { transitions: Transition[]; created_at: string; updatedAt: string; - done?: boolean; // true if a submission exists for this node by the user for whom the learning path is customized. + done?: boolean; // True if a submission exists for this node by the user for whom the learning path is customized. } export interface LearningPath { diff --git a/backend/src/services/learning-paths/database-learning-path-provider.ts b/backend/src/services/learning-paths/database-learning-path-provider.ts index d804ad64..2a784daf 100644 --- a/backend/src/services/learning-paths/database-learning-path-provider.ts +++ b/backend/src/services/learning-paths/database-learning-path-provider.ts @@ -99,7 +99,7 @@ async function convertNodes(nodesToLearningObjects: Map !personalizedFor || isTransitionPossible(trans, lastSubmission)) // If we want a personalized learning path, remove all transitions that aren't possible. - .map((trans, i) => convertTransition(trans, i, nodesToLearningObjects)), // then convert all the transition + .map((trans, i) => convertTransition(trans, i, nodesToLearningObjects)), // Then convert all the transition }; }) .toArray(); diff --git a/backend/src/services/learning-paths/learning-path-personalization-util.ts b/backend/src/services/learning-paths/learning-path-personalization-util.ts index 9217fa34..df268735 100644 --- a/backend/src/services/learning-paths/learning-path-personalization-util.ts +++ b/backend/src/services/learning-paths/learning-path-personalization-util.ts @@ -20,9 +20,9 @@ export async function getLastSubmissionForCustomizationTarget(node: LearningPath }; if (pathFor.type === 'group') { return await submissionRepo.findMostRecentSubmissionForGroup(learningObjectId, pathFor.group); - } else { + } return await submissionRepo.findMostRecentSubmissionForStudent(learningObjectId, pathFor.student); - } + } diff --git a/backend/src/services/learning-paths/learning-path-service.ts b/backend/src/services/learning-paths/learning-path-service.ts index c204872c..f2372672 100644 --- a/backend/src/services/learning-paths/learning-path-service.ts +++ b/backend/src/services/learning-paths/learning-path-service.ts @@ -26,7 +26,7 @@ const learningPathService = { const userContentLearningPaths = await databaseLearningPathProvider.fetchLearningPaths(userContentHruids, language, source, personalizedFor); const nonUserContentLearningPaths = await dwengoApiLearningPathProvider.fetchLearningPaths(nonUserContentHruids, language, source, personalizedFor); - let result = (userContentLearningPaths.data || []).concat(nonUserContentLearningPaths.data || []); + const result = (userContentLearningPaths.data || []).concat(nonUserContentLearningPaths.data || []); return { data: result, From 6624dacabda649bedee8a9796d5ab203d27c21d8 Mon Sep 17 00:00:00 2001 From: Lint Action Date: Tue, 11 Mar 2025 05:14:08 +0000 Subject: [PATCH 70/96] style: fix linting issues met Prettier --- .../database-learning-path-provider.ts | 32 ++++++++++--------- .../learning-path-personalization-util.ts | 22 ++++++------- .../learning-paths/learning-path-provider.ts | 2 +- .../learning-paths/learning-path-service.ts | 16 ++++++++-- 4 files changed, 41 insertions(+), 31 deletions(-) diff --git a/backend/src/services/learning-paths/database-learning-path-provider.ts b/backend/src/services/learning-paths/database-learning-path-provider.ts index 2a784daf..36ab58a9 100644 --- a/backend/src/services/learning-paths/database-learning-path-provider.ts +++ b/backend/src/services/learning-paths/database-learning-path-provider.ts @@ -6,11 +6,7 @@ import { Language } from '../../entities/content/language'; import learningObjectService from '../learning-objects/learning-object-service'; import { LearningPathNode } from '../../entities/content/learning-path-node.entity'; import { LearningPathTransition } from '../../entities/content/learning-path-transition.entity'; -import { - getLastSubmissionForCustomizationTarget, - isTransitionPossible, - PersonalizationTarget -} from "./learning-path-personalization-util"; +import { getLastSubmissionForCustomizationTarget, isTransitionPossible, PersonalizationTarget } from './learning-path-personalization-util'; /** * Fetches the corresponding learning object for each of the nodes and creates a map that maps each node to its @@ -69,7 +65,7 @@ async function convertLearningPath(learningPath: LearningPathEntity, order: numb title: learningPath.title, nodes: convertedNodes, num_nodes: learningPath.nodes.length, - num_nodes_left: convertedNodes.filter(it => !it.done).length, + num_nodes_left: convertedNodes.filter((it) => !it.done).length, keywords: keywords.join(' '), target_ages: targetAges, max_age: Math.max(...targetAges), @@ -83,10 +79,13 @@ async function convertLearningPath(learningPath: LearningPathEntity, order: numb * @param nodesToLearningObjects * @param personalizedFor */ -async function convertNodes(nodesToLearningObjects: Map, personalizedFor?: PersonalizationTarget): Promise { +async function convertNodes( + nodesToLearningObjects: Map, + personalizedFor?: PersonalizationTarget +): Promise { const nodesPromise = nodesToLearningObjects .entries() - .map(async(entry) => { + .map(async (entry) => { const [node, learningObject] = entry; const lastSubmission = personalizedFor ? await getLastSubmissionForCustomizationTarget(node, personalizedFor) : null; return { @@ -98,7 +97,7 @@ async function convertNodes(nodesToLearningObjects: Map !personalizedFor || isTransitionPossible(trans, lastSubmission)) // If we want a personalized learning path, remove all transitions that aren't possible. + .filter((trans) => !personalizedFor || isTransitionPossible(trans, lastSubmission)) // If we want a personalized learning path, remove all transitions that aren't possible. .map((trans, i) => convertTransition(trans, i, nodesToLearningObjects)), // Then convert all the transition }; }) @@ -143,14 +142,17 @@ const databaseLearningPathProvider: LearningPathProvider = { /** * Fetch the learning paths with the given hruids from the database. */ - async fetchLearningPaths(hruids: string[], language: Language, source: string, personalizedFor?: PersonalizationTarget): Promise { + async fetchLearningPaths( + hruids: string[], + language: Language, + source: string, + personalizedFor?: PersonalizationTarget + ): Promise { const learningPathRepo = getLearningPathRepository(); - const learningPaths = ( - await Promise.all( - hruids.map((hruid) => learningPathRepo.findByHruidAndLanguage(hruid, language)) - ) - ).filter((learningPath) => learningPath !== null); + const learningPaths = (await Promise.all(hruids.map((hruid) => learningPathRepo.findByHruidAndLanguage(hruid, language)))).filter( + (learningPath) => learningPath !== null + ); const filteredLearningPaths = await Promise.all( learningPaths.map((learningPath, index) => convertLearningPath(learningPath, index, personalizedFor)) ); diff --git a/backend/src/services/learning-paths/learning-path-personalization-util.ts b/backend/src/services/learning-paths/learning-path-personalization-util.ts index df268735..533ef15c 100644 --- a/backend/src/services/learning-paths/learning-path-personalization-util.ts +++ b/backend/src/services/learning-paths/learning-path-personalization-util.ts @@ -1,11 +1,11 @@ -import {LearningPathNode} from "../../entities/content/learning-path-node.entity"; -import {Student} from "../../entities/users/student.entity"; -import {Group} from "../../entities/assignments/group.entity"; -import {Submission} from "../../entities/assignments/submission.entity"; -import {getSubmissionRepository} from "../../data/repositories"; -import {LearningObjectIdentifier} from "../../entities/content/learning-object-identifier"; -import {LearningPathTransition} from "../../entities/content/learning-path-transition.entity"; -import {JSONPath} from "jsonpath-plus"; +import { LearningPathNode } from '../../entities/content/learning-path-node.entity'; +import { Student } from '../../entities/users/student.entity'; +import { Group } from '../../entities/assignments/group.entity'; +import { Submission } from '../../entities/assignments/submission.entity'; +import { getSubmissionRepository } from '../../data/repositories'; +import { LearningObjectIdentifier } from '../../entities/content/learning-object-identifier'; +import { LearningPathTransition } from '../../entities/content/learning-path-transition.entity'; +import { JSONPath } from 'jsonpath-plus'; export type PersonalizationTarget = { type: 'student'; student: Student } | { type: 'group'; group: Group }; /** @@ -20,12 +20,10 @@ export async function getLastSubmissionForCustomizationTarget(node: LearningPath }; if (pathFor.type === 'group') { return await submissionRepo.findMostRecentSubmissionForGroup(learningObjectId, pathFor.group); - } - return await submissionRepo.findMostRecentSubmissionForStudent(learningObjectId, pathFor.student); - + } + return await submissionRepo.findMostRecentSubmissionForStudent(learningObjectId, pathFor.student); } - /** * Checks whether the condition of the given transaction is fulfilled by the given submission. * @param transition diff --git a/backend/src/services/learning-paths/learning-path-provider.ts b/backend/src/services/learning-paths/learning-path-provider.ts index 571c231a..8736df21 100644 --- a/backend/src/services/learning-paths/learning-path-provider.ts +++ b/backend/src/services/learning-paths/learning-path-provider.ts @@ -1,6 +1,6 @@ import { LearningPath, LearningPathResponse } from '../../interfaces/learning-content'; import { Language } from '../../entities/content/language'; -import {PersonalizationTarget} from "./learning-path-personalizing-service"; +import { PersonalizationTarget } from './learning-path-personalizing-service'; /** * Generic interface for a service which provides access to learning paths from a data source. diff --git a/backend/src/services/learning-paths/learning-path-service.ts b/backend/src/services/learning-paths/learning-path-service.ts index f2372672..b7c7e794 100644 --- a/backend/src/services/learning-paths/learning-path-service.ts +++ b/backend/src/services/learning-paths/learning-path-service.ts @@ -3,7 +3,7 @@ import dwengoApiLearningPathProvider from './dwengo-api-learning-path-provider'; import databaseLearningPathProvider from './database-learning-path-provider'; import { EnvVars, getEnvVar } from '../../util/envvars'; import { Language } from '../../entities/content/language'; -import {PersonalizationTarget} from "./learning-path-personalizing-service"; +import { PersonalizationTarget } from './learning-path-personalizing-service'; const userContentPrefix = getEnvVar(EnvVars.UserContentPrefix); const allProviders = [dwengoApiLearningPathProvider, databaseLearningPathProvider]; @@ -19,12 +19,22 @@ const learningPathService = { * @param source * @param personalizedFor If this is set, a learning path personalized for the given group or student will be returned. */ - async fetchLearningPaths(hruids: string[], language: Language, source: string, personalizedFor?: PersonalizationTarget): Promise { + async fetchLearningPaths( + hruids: string[], + language: Language, + source: string, + personalizedFor?: PersonalizationTarget + ): Promise { const userContentHruids = hruids.filter((hruid) => hruid.startsWith(userContentPrefix)); const nonUserContentHruids = hruids.filter((hruid) => !hruid.startsWith(userContentPrefix)); const userContentLearningPaths = await databaseLearningPathProvider.fetchLearningPaths(userContentHruids, language, source, personalizedFor); - const nonUserContentLearningPaths = await dwengoApiLearningPathProvider.fetchLearningPaths(nonUserContentHruids, language, source, personalizedFor); + const nonUserContentLearningPaths = await dwengoApiLearningPathProvider.fetchLearningPaths( + nonUserContentHruids, + language, + source, + personalizedFor + ); const result = (userContentLearningPaths.data || []).concat(nonUserContentLearningPaths.data || []); From 64581505ef7954df4756029a76bc23cd86070601 Mon Sep 17 00:00:00 2001 From: Gerald Schmittinger Date: Tue, 11 Mar 2025 06:17:16 +0100 Subject: [PATCH 71/96] fix(backend): Resten van oude implementaties verwijderd --- .../src/services/learning-paths/learning-path-provider.ts | 4 ++-- .../src/services/learning-paths/learning-path-service.ts | 6 +++--- backend/src/services/learningObjects.ts | 0 backend/src/services/learningPaths.ts | 0 4 files changed, 5 insertions(+), 5 deletions(-) delete mode 100644 backend/src/services/learningObjects.ts delete mode 100644 backend/src/services/learningPaths.ts diff --git a/backend/src/services/learning-paths/learning-path-provider.ts b/backend/src/services/learning-paths/learning-path-provider.ts index 571c231a..137b4678 100644 --- a/backend/src/services/learning-paths/learning-path-provider.ts +++ b/backend/src/services/learning-paths/learning-path-provider.ts @@ -1,6 +1,6 @@ import { LearningPath, LearningPathResponse } from '../../interfaces/learning-content'; import { Language } from '../../entities/content/language'; -import {PersonalizationTarget} from "./learning-path-personalizing-service"; +import {PersonalizationTarget} from "./learning-path-personalization-util"; /** * Generic interface for a service which provides access to learning paths from a data source. @@ -14,5 +14,5 @@ export interface LearningPathProvider { /** * Search learning paths in the data source using the given search string. */ - searchLearningPaths(query: string, language: Language): Promise; + searchLearningPaths(query: string, language: Language, personalizedFor?: PersonalizationTarget): Promise; } diff --git a/backend/src/services/learning-paths/learning-path-service.ts b/backend/src/services/learning-paths/learning-path-service.ts index c204872c..d6662eff 100644 --- a/backend/src/services/learning-paths/learning-path-service.ts +++ b/backend/src/services/learning-paths/learning-path-service.ts @@ -3,7 +3,7 @@ import dwengoApiLearningPathProvider from './dwengo-api-learning-path-provider'; import databaseLearningPathProvider from './database-learning-path-provider'; import { EnvVars, getEnvVar } from '../../util/envvars'; import { Language } from '../../entities/content/language'; -import {PersonalizationTarget} from "./learning-path-personalizing-service"; +import {PersonalizationTarget} from "./learning-path-personalization-util"; const userContentPrefix = getEnvVar(EnvVars.UserContentPrefix); const allProviders = [dwengoApiLearningPathProvider, databaseLearningPathProvider]; @@ -38,8 +38,8 @@ const learningPathService = { /** * Search learning paths in the data source using the given search string. */ - async searchLearningPaths(query: string, language: Language): Promise { - const providerResponses = await Promise.all(allProviders.map((provider) => provider.searchLearningPaths(query, language))); + async searchLearningPaths(query: string, language: Language, personalizedFor?: PersonalizationTarget): Promise { + const providerResponses = await Promise.all(allProviders.map((provider) => provider.searchLearningPaths(query, language, personalizedFor))); return providerResponses.flat(); }, }; diff --git a/backend/src/services/learningObjects.ts b/backend/src/services/learningObjects.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/backend/src/services/learningPaths.ts b/backend/src/services/learningPaths.ts deleted file mode 100644 index e69de29b..00000000 From 009d7b803c34fdb964e9dc666191000e319f1019 Mon Sep 17 00:00:00 2001 From: Lint Action Date: Tue, 11 Mar 2025 05:19:33 +0000 Subject: [PATCH 72/96] style: fix linting issues met Prettier --- backend/src/services/learning-paths/learning-path-provider.ts | 2 +- backend/src/services/learning-paths/learning-path-service.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/services/learning-paths/learning-path-provider.ts b/backend/src/services/learning-paths/learning-path-provider.ts index 137b4678..4c8b9f21 100644 --- a/backend/src/services/learning-paths/learning-path-provider.ts +++ b/backend/src/services/learning-paths/learning-path-provider.ts @@ -1,6 +1,6 @@ import { LearningPath, LearningPathResponse } from '../../interfaces/learning-content'; import { Language } from '../../entities/content/language'; -import {PersonalizationTarget} from "./learning-path-personalization-util"; +import { PersonalizationTarget } from './learning-path-personalization-util'; /** * Generic interface for a service which provides access to learning paths from a data source. diff --git a/backend/src/services/learning-paths/learning-path-service.ts b/backend/src/services/learning-paths/learning-path-service.ts index 92a04b1a..9fb6f875 100644 --- a/backend/src/services/learning-paths/learning-path-service.ts +++ b/backend/src/services/learning-paths/learning-path-service.ts @@ -3,7 +3,7 @@ import dwengoApiLearningPathProvider from './dwengo-api-learning-path-provider'; import databaseLearningPathProvider from './database-learning-path-provider'; import { EnvVars, getEnvVar } from '../../util/envvars'; import { Language } from '../../entities/content/language'; -import {PersonalizationTarget} from "./learning-path-personalization-util"; +import { PersonalizationTarget } from './learning-path-personalization-util'; const userContentPrefix = getEnvVar(EnvVars.UserContentPrefix); const allProviders = [dwengoApiLearningPathProvider, databaseLearningPathProvider]; From fc46e79d05162a62cedcb8ac5ec1868adc93d8e0 Mon Sep 17 00:00:00 2001 From: Gerald Schmittinger Date: Tue, 11 Mar 2025 11:58:55 +0100 Subject: [PATCH 73/96] fix(backend): Fouten in isTransitionPossible en het opzetten van de testdata verbeterd. --- .../entities/assignments/submission.entity.ts | 4 +- .../content/learning-object.entity.ts | 6 +- .../database-learning-path-provider.ts | 16 +- .../learning-path-personalization-util.ts | 3 +- .../database-learning-path-provider.test.ts | 138 +++++++++++++++++- .../dummy/dummy-learning-object-example.ts | 7 +- .../test-essay/test-essay-example.ts | 4 + .../test-multiple-choice-example.ts | 4 + .../learning-paths/test-conditions-example.ts | 134 +++++++++-------- backend/vitest.config.ts | 1 + 10 files changed, 249 insertions(+), 68 deletions(-) diff --git a/backend/src/entities/assignments/submission.entity.ts b/backend/src/entities/assignments/submission.entity.ts index 34e0c1f6..920f0a71 100644 --- a/backend/src/entities/assignments/submission.entity.ts +++ b/backend/src/entities/assignments/submission.entity.ts @@ -15,8 +15,8 @@ export class Submission { }) learningObjectLanguage!: Language; - @PrimaryKey({ type: 'string' }) - learningObjectVersion: string = '1'; + @PrimaryKey({ type: 'numeric' }) + learningObjectVersion: number = 1; @PrimaryKey({ type: 'integer' }) submissionNumber!: number; diff --git a/backend/src/entities/content/learning-object.entity.ts b/backend/src/entities/content/learning-object.entity.ts index 55c4a808..c32efedc 100644 --- a/backend/src/entities/content/learning-object.entity.ts +++ b/backend/src/entities/content/learning-object.entity.ts @@ -47,7 +47,7 @@ export class LearningObject { teacherExclusive: boolean = false; @Property({ type: 'array' }) - skosConcepts!: string[]; + skosConcepts: string[] = []; @Embedded({ entity: () => EducationalGoal, @@ -64,8 +64,8 @@ export class LearningObject { @Property({ type: 'smallint', nullable: true }) difficulty?: number; - @Property({ type: 'integer' }) - estimatedTime!: number; + @Property({ type: 'integer', nullable: true }) + estimatedTime?: number; @Embedded({ entity: () => ReturnValue, diff --git a/backend/src/services/learning-paths/database-learning-path-provider.ts b/backend/src/services/learning-paths/database-learning-path-provider.ts index 36ab58a9..925f9f6b 100644 --- a/backend/src/services/learning-paths/database-learning-path-provider.ts +++ b/backend/src/services/learning-paths/database-learning-path-provider.ts @@ -97,14 +97,26 @@ async function convertNodes( learningobject_hruid: node.learningObjectHruid, version: learningObject.version, transitions: node.transitions - .filter((trans) => !personalizedFor || isTransitionPossible(trans, lastSubmission)) // If we want a personalized learning path, remove all transitions that aren't possible. - .map((trans, i) => convertTransition(trans, i, nodesToLearningObjects)), // Then convert all the transition + .filter((trans) => + !personalizedFor || isTransitionPossible(trans, optionalJsonStringToObject(lastSubmission?.content)) // If we want a personalized learning path, remove all transitions that aren't possible. + ).map((trans, i) => convertTransition(trans, i, nodesToLearningObjects)), // Then convert all the transition }; }) .toArray(); return await Promise.all(nodesPromise); } +/** + * Helper method to convert a json string to an object, or null if it is undefined. + */ +function optionalJsonStringToObject(jsonString?: string): object | null { + if (!jsonString) { + return null; + } else { + return JSON.parse(jsonString); + } +} + /** * Helper function which converts a transition in the database representation to a transition in the representation * the Dwengo API uses. diff --git a/backend/src/services/learning-paths/learning-path-personalization-util.ts b/backend/src/services/learning-paths/learning-path-personalization-util.ts index 533ef15c..648b7abc 100644 --- a/backend/src/services/learning-paths/learning-path-personalization-util.ts +++ b/backend/src/services/learning-paths/learning-path-personalization-util.ts @@ -36,5 +36,6 @@ export function isTransitionPossible(transition: LearningPathTransition, submitt if (submitted === null) { return false; // If the transition is not unconditional and there was no submission, the transition is not possible. } - return JSONPath({ path: transition.condition, json: submitted }).length === 0; + const match = JSONPath({ path: transition.condition, json: {submission: submitted} }) + return match.length === 1; } diff --git a/backend/tests/services/learning-path/database-learning-path-provider.test.ts b/backend/tests/services/learning-path/database-learning-path-provider.test.ts index df49ae3b..81d4a149 100644 --- a/backend/tests/services/learning-path/database-learning-path-provider.test.ts +++ b/backend/tests/services/learning-path/database-learning-path-provider.test.ts @@ -2,7 +2,11 @@ import { beforeAll, describe, expect, it } from 'vitest'; import { LearningObject } from '../../../src/entities/content/learning-object.entity'; import { setupTestApp } from '../../setup-tests'; import { LearningPath } from '../../../src/entities/content/learning-path.entity'; -import { getLearningObjectRepository, getLearningPathRepository } from '../../../src/data/repositories'; +import { + getLearningObjectRepository, + getLearningPathRepository, + getStudentRepository, getSubmissionRepository +} from '../../../src/data/repositories'; import learningObjectExample from '../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example'; import learningPathExample from '../../test-assets/learning-paths/pn-werking-example'; import databaseLearningPathProvider from '../../../src/services/learning-paths/database-learning-path-provider'; @@ -10,6 +14,12 @@ import { expectToBeCorrectLearningPath } from '../../test-utils/expectations'; import { LearningObjectRepository } from '../../../src/data/content/learning-object-repository'; import learningObjectService from '../../../src/services/learning-objects/learning-object-service'; import { Language } from '../../../src/entities/content/language'; +import { + ConditionTestLearningPathAndLearningObjects, + createConditionTestLearningPathAndLearningObjects +} from "../../test-assets/learning-paths/test-conditions-example"; +import {Student} from "../../../src/entities/users/student.entity"; +import {LearningObjectNode, LearningPathResponse} from "../../../src/interfaces/learning-content"; async function initExampleData(): Promise<{ learningObject: LearningObject; learningPath: LearningPath }> { const learningObjectRepo = getLearningObjectRepository(); @@ -21,13 +31,85 @@ async function initExampleData(): Promise<{ learningObject: LearningObject; lear return { learningObject, learningPath }; } +async function initPersonalizationTestData(): Promise<{ + learningContent: ConditionTestLearningPathAndLearningObjects, + studentA: Student, + studentB: Student +}> { + const studentRepo = getStudentRepository(); + const submissionRepo = getSubmissionRepository(); + const learningPathRepo = getLearningPathRepository(); + const learningObjectRepo = getLearningObjectRepository(); + const learningContent = createConditionTestLearningPathAndLearningObjects(); + await learningObjectRepo.save(learningContent.branchingObject); + await learningObjectRepo.save(learningContent.finalObject); + await learningObjectRepo.save(learningContent.extraExerciseObject); + await learningPathRepo.save(learningContent.learningPath); + + console.log(await getSubmissionRepository().findAll({})); + + const studentA = studentRepo.create({ + username: "student_a", + firstName: "Aron", + lastName: "Student" + }); + await studentRepo.save(studentA); + const submissionA = submissionRepo.create({ + learningObjectHruid: learningContent.branchingObject.hruid, + learningObjectLanguage: learningContent.branchingObject.language, + learningObjectVersion: learningContent.branchingObject.version, + submissionNumber: 0, + submitter: studentA, + submissionTime: new Date(), + content: '[0]' + }); + await submissionRepo.save(submissionA); + + const studentB = studentRepo.create({ + username: "student_b", + firstName: "Bill", + lastName: "Student" + }); + await studentRepo.save(studentB); + const submissionB = submissionRepo.create({ + learningObjectHruid: learningContent.branchingObject.hruid, + learningObjectLanguage: learningContent.branchingObject.language, + learningObjectVersion: learningContent.branchingObject.version, + submissionNumber: 1, + submitter: studentB, + submissionTime: new Date(), + content: '[1]' + }); + await submissionRepo.save(submissionB); + + return { + learningContent: learningContent, + studentA: studentA, + studentB: studentB, + } +} + +function expectBranchingObjectNode(result: LearningPathResponse, persTestData: { + learningContent: ConditionTestLearningPathAndLearningObjects; + studentA: Student; + studentB: Student +}): LearningObjectNode { + let branchingObjectMatches = result.data![0].nodes.filter( + it => it.learningobject_hruid === persTestData.learningContent.branchingObject.hruid + ); + expect(branchingObjectMatches.length).toBe(1); + return branchingObjectMatches[0]; +} + describe('DatabaseLearningPathProvider', () => { let learningObjectRepo: LearningObjectRepository; let example: { learningObject: LearningObject; learningPath: LearningPath }; + let persTestData: { learningContent: ConditionTestLearningPathAndLearningObjects, studentA: Student, studentB: Student } beforeAll(async () => { await setupTestApp(); example = await initExampleData(); + persTestData = await initPersonalizationTestData(); learningObjectRepo = getLearningObjectRepository(); }); @@ -55,6 +137,60 @@ describe('DatabaseLearningPathProvider', () => { expectToBeCorrectLearningPath(result.data![0], example.learningPath, learningObjectsOnPath); }); + it("returns the correct personalized learning path", async () => { + // For student A: + let result = await databaseLearningPathProvider.fetchLearningPaths( + [persTestData.learningContent.learningPath.hruid], + persTestData.learningContent.learningPath.language, + 'the source', + {type: 'student', student: persTestData.studentA} + ); + expect(result.success).toBeTruthy(); + expect(result.data?.length).toBe(1); + + // There should be exactly one branching object + let branchingObject = expectBranchingObjectNode(result, persTestData); + + expect( + branchingObject + .transitions + .filter(it => it.next.hruid === persTestData.learningContent.finalObject.hruid) + .length + ).toBe(0); // studentA picked the first option, therefore, there should be no direct path to the final object. + expect( + branchingObject + .transitions + .filter(it => it.next.hruid === persTestData.learningContent.extraExerciseObject.hruid) + .length + ).toBe(1); // There should however be a path to the extra exercise object. + + // For student B: + result = await databaseLearningPathProvider.fetchLearningPaths( + [persTestData.learningContent.learningPath.hruid], + persTestData.learningContent.learningPath.language, + 'the source', + {type: 'student', student: persTestData.studentB} + ); + expect(result.success).toBeTruthy(); + expect(result.data?.length).toBe(1); + + // There should still be exactly one branching object + branchingObject = expectBranchingObjectNode(result, persTestData); + + // However, now the student picks the other option. + expect( + branchingObject + .transitions + .filter(it => it.next.hruid === persTestData.learningContent.finalObject.hruid) + .length + ).toBe(1); // studentB picked the second option, therefore, there should be a direct path to the final object. + expect( + branchingObject + .transitions + .filter(it => it.next.hruid === persTestData.learningContent.extraExerciseObject.hruid) + .length + ).toBe(0); // There should not be a path anymore to the extra exercise object. + }); it('returns a non-successful response if a non-existing learning path is queried', async () => { const result = await databaseLearningPathProvider.fetchLearningPaths( [example.learningPath.hruid], diff --git a/backend/tests/test-assets/learning-objects/dummy/dummy-learning-object-example.ts b/backend/tests/test-assets/learning-objects/dummy/dummy-learning-object-example.ts index 6dbe54b0..14f62828 100644 --- a/backend/tests/test-assets/learning-objects/dummy/dummy-learning-object-example.ts +++ b/backend/tests/test-assets/learning-objects/dummy/dummy-learning-object-example.ts @@ -3,6 +3,7 @@ import { LearningObject } from '../../../../src/entities/content/learning-object import { Language } from '../../../../src/entities/content/language'; import { loadTestAsset } from '../../../test-utils/load-test-asset'; import { DwengoContentType } from '../../../../src/services/learning-objects/processing/content-type'; +import {EnvVars, getEnvVar} from "../../../../src/util/envvars"; /** * Create a dummy learning object to be used in tests where multiple learning objects are needed (for example for use @@ -12,13 +13,17 @@ export function dummyLearningObject(hruid: string, language: Language, title: st return { createLearningObject: () => { const learningObject = new LearningObject(); - learningObject.hruid = hruid; + learningObject.hruid = getEnvVar(EnvVars.UserContentPrefix) + hruid; learningObject.language = language; learningObject.version = 1; learningObject.title = title; learningObject.description = 'Just a dummy learning object for testing purposes'; learningObject.contentType = DwengoContentType.TEXT_PLAIN; learningObject.content = Buffer.from('Dummy content'); + learningObject.returnValue = { + callbackUrl: `/learningObject/${hruid}/submissions`, + callbackSchema: "[]" + } return learningObject; }, createAttachment: {}, diff --git a/backend/tests/test-assets/learning-objects/test-essay/test-essay-example.ts b/backend/tests/test-assets/learning-objects/test-essay/test-essay-example.ts index b1ff330e..c0c18c6a 100644 --- a/backend/tests/test-assets/learning-objects/test-essay/test-essay-example.ts +++ b/backend/tests/test-assets/learning-objects/test-essay/test-essay-example.ts @@ -14,6 +14,10 @@ const example: LearningObjectExample = { learningObject.title = 'Essay question for testing'; learningObject.description = 'This essay question was only created for testing purposes.'; learningObject.contentType = DwengoContentType.GIFT; + learningObject.returnValue = { + callbackUrl: `/learningObject/${learningObject.hruid}/submissions`, + callbackSchema: '["antwoord vraag 1"]' + } learningObject.content = loadTestAsset('learning-objects/test-essay/content.txt'); return learningObject; }, diff --git a/backend/tests/test-assets/learning-objects/test-multiple-choice/test-multiple-choice-example.ts b/backend/tests/test-assets/learning-objects/test-multiple-choice/test-multiple-choice-example.ts index cfb0b5a2..c5dcdc94 100644 --- a/backend/tests/test-assets/learning-objects/test-multiple-choice/test-multiple-choice-example.ts +++ b/backend/tests/test-assets/learning-objects/test-multiple-choice/test-multiple-choice-example.ts @@ -14,6 +14,10 @@ const example: LearningObjectExample = { learningObject.title = 'Multiple choice question for testing'; learningObject.description = 'This multiple choice question was only created for testing purposes.'; learningObject.contentType = DwengoContentType.GIFT; + learningObject.returnValue = { + callbackUrl: `/learningObject/${learningObject.hruid}/submissions`, + callbackSchema: '["antwoord vraag 1"]' + } learningObject.content = loadTestAsset('learning-objects/test-multiple-choice/content.txt'); return learningObject; }, diff --git a/backend/tests/test-assets/learning-paths/test-conditions-example.ts b/backend/tests/test-assets/learning-paths/test-conditions-example.ts index b6cf3e9d..f06ff904 100644 --- a/backend/tests/test-assets/learning-paths/test-conditions-example.ts +++ b/backend/tests/test-assets/learning-paths/test-conditions-example.ts @@ -3,66 +3,84 @@ import { Language } from '../../../src/entities/content/language'; import testMultipleChoiceExample from '../learning-objects/test-multiple-choice/test-multiple-choice-example'; import { dummyLearningObject } from '../learning-objects/dummy/dummy-learning-object-example'; import { createLearningPathNode, createLearningPathTransition } from './learning-path-utils'; +import {LearningObject} from "../../../src/entities/content/learning-object.entity"; +import {EnvVars, getEnvVar} from "../../../src/util/envvars"; + +export type ConditionTestLearningPathAndLearningObjects = { + branchingObject: LearningObject, + extraExerciseObject: LearningObject, + finalObject: LearningObject, + learningPath: LearningPath +}; + +export function createConditionTestLearningPathAndLearningObjects(){ + const learningPath = new LearningPath(); + learningPath.hruid = `${getEnvVar(EnvVars.UserContentPrefix)}test_conditions`; + learningPath.language = Language.English; + learningPath.title = 'Example learning path with conditional transitions'; + learningPath.description = 'This learning path was made for the purpose of testing conditional transitions'; + + const branchingLearningObject = testMultipleChoiceExample.createLearningObject(); + const extraExerciseLearningObject = dummyLearningObject( + 'test_extra_exercise', + Language.English, + 'Extra exercise (for students with difficulties)' + ).createLearningObject(); + const finalLearningObject = dummyLearningObject( + 'test_final_learning_object', + Language.English, + 'Final exercise (for everyone)' + ).createLearningObject(); + + const branchingNode = createLearningPathNode( + learningPath, + 0, + branchingLearningObject.hruid, + branchingLearningObject.version, + branchingLearningObject.language, + true + ); + const extraExerciseNode = createLearningPathNode( + learningPath, + 1, + extraExerciseLearningObject.hruid, + extraExerciseLearningObject.version, + extraExerciseLearningObject.language, + false + ); + const finalNode = createLearningPathNode( + learningPath, + 2, + finalLearningObject.hruid, + finalLearningObject.version, + finalLearningObject.language, + false + ); + + const transitionToExtraExercise = createLearningPathTransition( + branchingNode, + 0, + '$[?(@[0] == 0)]', // The answer to the first question was the first one, which says that it is difficult for the student to follow along. + extraExerciseNode + ); + const directTransitionToFinal = createLearningPathTransition(branchingNode, 1, '$[?(@[0] == 1)]', finalNode); + const transitionExtraExerciseToFinal = createLearningPathTransition(extraExerciseNode, 0, 'true', finalNode); + + branchingNode.transitions = [transitionToExtraExercise, directTransitionToFinal]; + extraExerciseNode.transitions = [transitionExtraExerciseToFinal]; + + learningPath.nodes = [branchingNode, extraExerciseNode, finalNode]; + + return { + branchingObject: branchingLearningObject, + finalObject: finalLearningObject, + extraExerciseObject: extraExerciseLearningObject, + learningPath: learningPath, + }; +} const example: LearningPathExample = { createLearningPath: () => { - const learningPath = new LearningPath(); - learningPath.hruid = 'test_conditions'; - learningPath.language = Language.English; - learningPath.title = 'Example learning path with conditional transitions'; - learningPath.description = 'This learning path was made for the purpose of testing conditional transitions'; - - const branchingLearningObject = testMultipleChoiceExample.createLearningObject(); - const extraExerciseLearningObject = dummyLearningObject( - 'test_extra_exercise', - Language.English, - 'Extra exercise (for students with difficulties)' - ).createLearningObject(); - const finalLearningObject = dummyLearningObject( - 'test_final_learning_object', - Language.English, - 'Final exercise (for everyone)' - ).createLearningObject(); - - const branchingNode = createLearningPathNode( - learningPath, - 0, - branchingLearningObject.hruid, - branchingLearningObject.version, - branchingLearningObject.language, - true - ); - const extraExerciseNode = createLearningPathNode( - learningPath, - 1, - extraExerciseLearningObject.hruid, - extraExerciseLearningObject.version, - extraExerciseLearningObject.language, - false - ); - const finalNode = createLearningPathNode( - learningPath, - 2, - finalLearningObject.hruid, - finalLearningObject.version, - finalLearningObject.language, - false - ); - - const transitionToExtraExercise = createLearningPathTransition( - branchingNode, - 0, - '$[?(@[0] == 0)]', // The answer to the first question was the first one, which says that it is difficult for the student to follow along. - extraExerciseNode - ); - const directTransitionToFinal = createLearningPathTransition(branchingNode, 1, '$[?(@[0] == 1)]', finalNode); - const transitionExtraExerciseToFinal = createLearningPathTransition(extraExerciseNode, 0, 'true', finalNode); - - branchingNode.transitions = [transitionToExtraExercise, directTransitionToFinal]; - extraExerciseNode.transitions = [transitionExtraExerciseToFinal]; - - learningPath.nodes = [branchingNode, extraExerciseNode, finalNode]; - - return learningPath; + return createConditionTestLearningPathAndLearningObjects().learningPath; }, }; diff --git a/backend/vitest.config.ts b/backend/vitest.config.ts index 302015fb..461d2018 100644 --- a/backend/vitest.config.ts +++ b/backend/vitest.config.ts @@ -4,5 +4,6 @@ export default defineConfig({ test: { environment: 'node', globals: true, + testTimeout: 10000, }, }); From dd21f46162c0b1ef4fde333659fcf866e7d7c5e4 Mon Sep 17 00:00:00 2001 From: Lint Action Date: Tue, 11 Mar 2025 10:59:51 +0000 Subject: [PATCH 74/96] style: fix linting issues met ESLint --- .../learning-paths/database-learning-path-provider.ts | 4 ++-- .../learning-path/database-learning-path-provider.test.ts | 6 +++--- .../test-assets/learning-paths/test-conditions-example.ts | 4 +--- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/backend/src/services/learning-paths/database-learning-path-provider.ts b/backend/src/services/learning-paths/database-learning-path-provider.ts index 925f9f6b..e99048f7 100644 --- a/backend/src/services/learning-paths/database-learning-path-provider.ts +++ b/backend/src/services/learning-paths/database-learning-path-provider.ts @@ -112,9 +112,9 @@ async function convertNodes( function optionalJsonStringToObject(jsonString?: string): object | null { if (!jsonString) { return null; - } else { + } return JSON.parse(jsonString); - } + } /** diff --git a/backend/tests/services/learning-path/database-learning-path-provider.test.ts b/backend/tests/services/learning-path/database-learning-path-provider.test.ts index 81d4a149..1088cb2e 100644 --- a/backend/tests/services/learning-path/database-learning-path-provider.test.ts +++ b/backend/tests/services/learning-path/database-learning-path-provider.test.ts @@ -94,7 +94,7 @@ function expectBranchingObjectNode(result: LearningPathResponse, persTestData: { studentA: Student; studentB: Student }): LearningObjectNode { - let branchingObjectMatches = result.data![0].nodes.filter( + const branchingObjectMatches = result.data![0].nodes.filter( it => it.learningobject_hruid === persTestData.learningContent.branchingObject.hruid ); expect(branchingObjectMatches.length).toBe(1); @@ -156,7 +156,7 @@ describe('DatabaseLearningPathProvider', () => { .transitions .filter(it => it.next.hruid === persTestData.learningContent.finalObject.hruid) .length - ).toBe(0); // studentA picked the first option, therefore, there should be no direct path to the final object. + ).toBe(0); // StudentA picked the first option, therefore, there should be no direct path to the final object. expect( branchingObject .transitions @@ -183,7 +183,7 @@ describe('DatabaseLearningPathProvider', () => { .transitions .filter(it => it.next.hruid === persTestData.learningContent.finalObject.hruid) .length - ).toBe(1); // studentB picked the second option, therefore, there should be a direct path to the final object. + ).toBe(1); // StudentB picked the second option, therefore, there should be a direct path to the final object. expect( branchingObject .transitions diff --git a/backend/tests/test-assets/learning-paths/test-conditions-example.ts b/backend/tests/test-assets/learning-paths/test-conditions-example.ts index f06ff904..9ac8c4d6 100644 --- a/backend/tests/test-assets/learning-paths/test-conditions-example.ts +++ b/backend/tests/test-assets/learning-paths/test-conditions-example.ts @@ -80,7 +80,5 @@ export function createConditionTestLearningPathAndLearningObjects(){ } const example: LearningPathExample = { - createLearningPath: () => { - return createConditionTestLearningPathAndLearningObjects().learningPath; - }, + createLearningPath: () => createConditionTestLearningPathAndLearningObjects().learningPath, }; From 06bc1cb8bfdd7f3df788dd30f8d36509c46adbef Mon Sep 17 00:00:00 2001 From: Lint Action Date: Tue, 11 Mar 2025 10:59:55 +0000 Subject: [PATCH 75/96] style: fix linting issues met Prettier --- .../database-learning-path-provider.ts | 12 +-- .../learning-path-personalization-util.ts | 2 +- .../database-learning-path-provider.test.ts | 90 ++++++++----------- .../dummy/dummy-learning-object-example.ts | 6 +- .../test-essay/test-essay-example.ts | 4 +- .../test-multiple-choice-example.ts | 4 +- .../learning-paths/test-conditions-example.ts | 14 +-- 7 files changed, 60 insertions(+), 72 deletions(-) diff --git a/backend/src/services/learning-paths/database-learning-path-provider.ts b/backend/src/services/learning-paths/database-learning-path-provider.ts index e99048f7..a0bd6a47 100644 --- a/backend/src/services/learning-paths/database-learning-path-provider.ts +++ b/backend/src/services/learning-paths/database-learning-path-provider.ts @@ -97,9 +97,10 @@ async function convertNodes( learningobject_hruid: node.learningObjectHruid, version: learningObject.version, transitions: node.transitions - .filter((trans) => - !personalizedFor || isTransitionPossible(trans, optionalJsonStringToObject(lastSubmission?.content)) // If we want a personalized learning path, remove all transitions that aren't possible. - ).map((trans, i) => convertTransition(trans, i, nodesToLearningObjects)), // Then convert all the transition + .filter( + (trans) => !personalizedFor || isTransitionPossible(trans, optionalJsonStringToObject(lastSubmission?.content)) // If we want a personalized learning path, remove all transitions that aren't possible. + ) + .map((trans, i) => convertTransition(trans, i, nodesToLearningObjects)), // Then convert all the transition }; }) .toArray(); @@ -112,9 +113,8 @@ async function convertNodes( function optionalJsonStringToObject(jsonString?: string): object | null { if (!jsonString) { return null; - } - return JSON.parse(jsonString); - + } + return JSON.parse(jsonString); } /** diff --git a/backend/src/services/learning-paths/learning-path-personalization-util.ts b/backend/src/services/learning-paths/learning-path-personalization-util.ts index 648b7abc..29b8c5ec 100644 --- a/backend/src/services/learning-paths/learning-path-personalization-util.ts +++ b/backend/src/services/learning-paths/learning-path-personalization-util.ts @@ -36,6 +36,6 @@ export function isTransitionPossible(transition: LearningPathTransition, submitt if (submitted === null) { return false; // If the transition is not unconditional and there was no submission, the transition is not possible. } - const match = JSONPath({ path: transition.condition, json: {submission: submitted} }) + const match = JSONPath({ path: transition.condition, json: { submission: submitted } }); return match.length === 1; } diff --git a/backend/tests/services/learning-path/database-learning-path-provider.test.ts b/backend/tests/services/learning-path/database-learning-path-provider.test.ts index 1088cb2e..7e6124a3 100644 --- a/backend/tests/services/learning-path/database-learning-path-provider.test.ts +++ b/backend/tests/services/learning-path/database-learning-path-provider.test.ts @@ -5,7 +5,8 @@ import { LearningPath } from '../../../src/entities/content/learning-path.entity import { getLearningObjectRepository, getLearningPathRepository, - getStudentRepository, getSubmissionRepository + getStudentRepository, + getSubmissionRepository, } from '../../../src/data/repositories'; import learningObjectExample from '../../test-assets/learning-objects/pn-werkingnotebooks/pn-werkingnotebooks-example'; import learningPathExample from '../../test-assets/learning-paths/pn-werking-example'; @@ -16,10 +17,10 @@ import learningObjectService from '../../../src/services/learning-objects/learni import { Language } from '../../../src/entities/content/language'; import { ConditionTestLearningPathAndLearningObjects, - createConditionTestLearningPathAndLearningObjects -} from "../../test-assets/learning-paths/test-conditions-example"; -import {Student} from "../../../src/entities/users/student.entity"; -import {LearningObjectNode, LearningPathResponse} from "../../../src/interfaces/learning-content"; + createConditionTestLearningPathAndLearningObjects, +} from '../../test-assets/learning-paths/test-conditions-example'; +import { Student } from '../../../src/entities/users/student.entity'; +import { LearningObjectNode, LearningPathResponse } from '../../../src/interfaces/learning-content'; async function initExampleData(): Promise<{ learningObject: LearningObject; learningPath: LearningPath }> { const learningObjectRepo = getLearningObjectRepository(); @@ -32,9 +33,9 @@ async function initExampleData(): Promise<{ learningObject: LearningObject; lear } async function initPersonalizationTestData(): Promise<{ - learningContent: ConditionTestLearningPathAndLearningObjects, - studentA: Student, - studentB: Student + learningContent: ConditionTestLearningPathAndLearningObjects; + studentA: Student; + studentB: Student; }> { const studentRepo = getStudentRepository(); const submissionRepo = getSubmissionRepository(); @@ -49,9 +50,9 @@ async function initPersonalizationTestData(): Promise<{ console.log(await getSubmissionRepository().findAll({})); const studentA = studentRepo.create({ - username: "student_a", - firstName: "Aron", - lastName: "Student" + username: 'student_a', + firstName: 'Aron', + lastName: 'Student', }); await studentRepo.save(studentA); const submissionA = submissionRepo.create({ @@ -61,14 +62,14 @@ async function initPersonalizationTestData(): Promise<{ submissionNumber: 0, submitter: studentA, submissionTime: new Date(), - content: '[0]' + content: '[0]', }); await submissionRepo.save(submissionA); const studentB = studentRepo.create({ - username: "student_b", - firstName: "Bill", - lastName: "Student" + username: 'student_b', + firstName: 'Bill', + lastName: 'Student', }); await studentRepo.save(studentB); const submissionB = submissionRepo.create({ @@ -78,7 +79,7 @@ async function initPersonalizationTestData(): Promise<{ submissionNumber: 1, submitter: studentB, submissionTime: new Date(), - content: '[1]' + content: '[1]', }); await submissionRepo.save(submissionB); @@ -86,16 +87,19 @@ async function initPersonalizationTestData(): Promise<{ learningContent: learningContent, studentA: studentA, studentB: studentB, - } + }; } -function expectBranchingObjectNode(result: LearningPathResponse, persTestData: { - learningContent: ConditionTestLearningPathAndLearningObjects; - studentA: Student; - studentB: Student -}): LearningObjectNode { +function expectBranchingObjectNode( + result: LearningPathResponse, + persTestData: { + learningContent: ConditionTestLearningPathAndLearningObjects; + studentA: Student; + studentB: Student; + } +): LearningObjectNode { const branchingObjectMatches = result.data![0].nodes.filter( - it => it.learningobject_hruid === persTestData.learningContent.branchingObject.hruid + (it) => it.learningobject_hruid === persTestData.learningContent.branchingObject.hruid ); expect(branchingObjectMatches.length).toBe(1); return branchingObjectMatches[0]; @@ -104,7 +108,7 @@ function expectBranchingObjectNode(result: LearningPathResponse, persTestData: { describe('DatabaseLearningPathProvider', () => { let learningObjectRepo: LearningObjectRepository; let example: { learningObject: LearningObject; learningPath: LearningPath }; - let persTestData: { learningContent: ConditionTestLearningPathAndLearningObjects, studentA: Student, studentB: Student } + let persTestData: { learningContent: ConditionTestLearningPathAndLearningObjects; studentA: Student; studentB: Student }; beforeAll(async () => { await setupTestApp(); @@ -137,13 +141,13 @@ describe('DatabaseLearningPathProvider', () => { expectToBeCorrectLearningPath(result.data![0], example.learningPath, learningObjectsOnPath); }); - it("returns the correct personalized learning path", async () => { + it('returns the correct personalized learning path', async () => { // For student A: let result = await databaseLearningPathProvider.fetchLearningPaths( [persTestData.learningContent.learningPath.hruid], persTestData.learningContent.learningPath.language, 'the source', - {type: 'student', student: persTestData.studentA} + { type: 'student', student: persTestData.studentA } ); expect(result.success).toBeTruthy(); expect(result.data?.length).toBe(1); @@ -151,25 +155,17 @@ describe('DatabaseLearningPathProvider', () => { // There should be exactly one branching object let branchingObject = expectBranchingObjectNode(result, persTestData); - expect( - branchingObject - .transitions - .filter(it => it.next.hruid === persTestData.learningContent.finalObject.hruid) - .length - ).toBe(0); // StudentA picked the first option, therefore, there should be no direct path to the final object. - expect( - branchingObject - .transitions - .filter(it => it.next.hruid === persTestData.learningContent.extraExerciseObject.hruid) - .length - ).toBe(1); // There should however be a path to the extra exercise object. + expect(branchingObject.transitions.filter((it) => it.next.hruid === persTestData.learningContent.finalObject.hruid).length).toBe(0); // StudentA picked the first option, therefore, there should be no direct path to the final object. + expect(branchingObject.transitions.filter((it) => it.next.hruid === persTestData.learningContent.extraExerciseObject.hruid).length).toBe( + 1 + ); // There should however be a path to the extra exercise object. // For student B: result = await databaseLearningPathProvider.fetchLearningPaths( [persTestData.learningContent.learningPath.hruid], persTestData.learningContent.learningPath.language, 'the source', - {type: 'student', student: persTestData.studentB} + { type: 'student', student: persTestData.studentB } ); expect(result.success).toBeTruthy(); expect(result.data?.length).toBe(1); @@ -178,18 +174,10 @@ describe('DatabaseLearningPathProvider', () => { branchingObject = expectBranchingObjectNode(result, persTestData); // However, now the student picks the other option. - expect( - branchingObject - .transitions - .filter(it => it.next.hruid === persTestData.learningContent.finalObject.hruid) - .length - ).toBe(1); // StudentB picked the second option, therefore, there should be a direct path to the final object. - expect( - branchingObject - .transitions - .filter(it => it.next.hruid === persTestData.learningContent.extraExerciseObject.hruid) - .length - ).toBe(0); // There should not be a path anymore to the extra exercise object. + expect(branchingObject.transitions.filter((it) => it.next.hruid === persTestData.learningContent.finalObject.hruid).length).toBe(1); // StudentB picked the second option, therefore, there should be a direct path to the final object. + expect(branchingObject.transitions.filter((it) => it.next.hruid === persTestData.learningContent.extraExerciseObject.hruid).length).toBe( + 0 + ); // There should not be a path anymore to the extra exercise object. }); it('returns a non-successful response if a non-existing learning path is queried', async () => { const result = await databaseLearningPathProvider.fetchLearningPaths( diff --git a/backend/tests/test-assets/learning-objects/dummy/dummy-learning-object-example.ts b/backend/tests/test-assets/learning-objects/dummy/dummy-learning-object-example.ts index 14f62828..2f2e78ad 100644 --- a/backend/tests/test-assets/learning-objects/dummy/dummy-learning-object-example.ts +++ b/backend/tests/test-assets/learning-objects/dummy/dummy-learning-object-example.ts @@ -3,7 +3,7 @@ import { LearningObject } from '../../../../src/entities/content/learning-object import { Language } from '../../../../src/entities/content/language'; import { loadTestAsset } from '../../../test-utils/load-test-asset'; import { DwengoContentType } from '../../../../src/services/learning-objects/processing/content-type'; -import {EnvVars, getEnvVar} from "../../../../src/util/envvars"; +import { EnvVars, getEnvVar } from '../../../../src/util/envvars'; /** * Create a dummy learning object to be used in tests where multiple learning objects are needed (for example for use @@ -22,8 +22,8 @@ export function dummyLearningObject(hruid: string, language: Language, title: st learningObject.content = Buffer.from('Dummy content'); learningObject.returnValue = { callbackUrl: `/learningObject/${hruid}/submissions`, - callbackSchema: "[]" - } + callbackSchema: '[]', + }; return learningObject; }, createAttachment: {}, diff --git a/backend/tests/test-assets/learning-objects/test-essay/test-essay-example.ts b/backend/tests/test-assets/learning-objects/test-essay/test-essay-example.ts index c0c18c6a..d57c7a33 100644 --- a/backend/tests/test-assets/learning-objects/test-essay/test-essay-example.ts +++ b/backend/tests/test-assets/learning-objects/test-essay/test-essay-example.ts @@ -16,8 +16,8 @@ const example: LearningObjectExample = { learningObject.contentType = DwengoContentType.GIFT; learningObject.returnValue = { callbackUrl: `/learningObject/${learningObject.hruid}/submissions`, - callbackSchema: '["antwoord vraag 1"]' - } + callbackSchema: '["antwoord vraag 1"]', + }; learningObject.content = loadTestAsset('learning-objects/test-essay/content.txt'); return learningObject; }, diff --git a/backend/tests/test-assets/learning-objects/test-multiple-choice/test-multiple-choice-example.ts b/backend/tests/test-assets/learning-objects/test-multiple-choice/test-multiple-choice-example.ts index c5dcdc94..a634878a 100644 --- a/backend/tests/test-assets/learning-objects/test-multiple-choice/test-multiple-choice-example.ts +++ b/backend/tests/test-assets/learning-objects/test-multiple-choice/test-multiple-choice-example.ts @@ -16,8 +16,8 @@ const example: LearningObjectExample = { learningObject.contentType = DwengoContentType.GIFT; learningObject.returnValue = { callbackUrl: `/learningObject/${learningObject.hruid}/submissions`, - callbackSchema: '["antwoord vraag 1"]' - } + callbackSchema: '["antwoord vraag 1"]', + }; learningObject.content = loadTestAsset('learning-objects/test-multiple-choice/content.txt'); return learningObject; }, diff --git a/backend/tests/test-assets/learning-paths/test-conditions-example.ts b/backend/tests/test-assets/learning-paths/test-conditions-example.ts index 9ac8c4d6..07857235 100644 --- a/backend/tests/test-assets/learning-paths/test-conditions-example.ts +++ b/backend/tests/test-assets/learning-paths/test-conditions-example.ts @@ -3,17 +3,17 @@ import { Language } from '../../../src/entities/content/language'; import testMultipleChoiceExample from '../learning-objects/test-multiple-choice/test-multiple-choice-example'; import { dummyLearningObject } from '../learning-objects/dummy/dummy-learning-object-example'; import { createLearningPathNode, createLearningPathTransition } from './learning-path-utils'; -import {LearningObject} from "../../../src/entities/content/learning-object.entity"; -import {EnvVars, getEnvVar} from "../../../src/util/envvars"; +import { LearningObject } from '../../../src/entities/content/learning-object.entity'; +import { EnvVars, getEnvVar } from '../../../src/util/envvars'; export type ConditionTestLearningPathAndLearningObjects = { - branchingObject: LearningObject, - extraExerciseObject: LearningObject, - finalObject: LearningObject, - learningPath: LearningPath + branchingObject: LearningObject; + extraExerciseObject: LearningObject; + finalObject: LearningObject; + learningPath: LearningPath; }; -export function createConditionTestLearningPathAndLearningObjects(){ +export function createConditionTestLearningPathAndLearningObjects() { const learningPath = new LearningPath(); learningPath.hruid = `${getEnvVar(EnvVars.UserContentPrefix)}test_conditions`; learningPath.language = Language.English; From 3f04d2fd4df6d26cb3c212a31d897a6fd8ac00c9 Mon Sep 17 00:00:00 2001 From: Gerald Schmittinger Date: Tue, 11 Mar 2025 12:18:07 +0100 Subject: [PATCH 76/96] feat(backend): Gepersonaliseerde leerpaden via API Mogelijkheid toegevoegd om via de API optioneel een gepersonaliseerde variant van een leerpad voor een student of groep aan te vragen. --- backend/src/controllers/learning-paths.ts | 27 ++++++++-- .../learning-path-personalization-util.ts | 54 ++++++++++++++++++- 2 files changed, 77 insertions(+), 4 deletions(-) diff --git a/backend/src/controllers/learning-paths.ts b/backend/src/controllers/learning-paths.ts index 8e654d02..fca86bfb 100644 --- a/backend/src/controllers/learning-paths.ts +++ b/backend/src/controllers/learning-paths.ts @@ -2,7 +2,12 @@ import { Request, Response } from 'express'; import { themes } from '../data/themes.js'; import { FALLBACK_LANG } from '../config.js'; import learningPathService from '../services/learning-paths/learning-path-service'; -import { NotFoundException } from '../exceptions'; +import {BadRequestException, NotFoundException} from '../exceptions'; +import {Language} from "../entities/content/language"; +import { + PersonalizationTarget, personalizedForGroup, + personalizedForStudent +} from "../services/learning-paths/learning-path-personalization-util"; /** * Fetch learning paths based on query parameters. @@ -13,6 +18,22 @@ export async function getLearningPaths(req: Request, res: Response): Promise theme.hruids); } - const learningPaths = await learningPathService.fetchLearningPaths(hruidList, language, `HRUIDs: ${hruidList.join(', ')}`); + const learningPaths = await learningPathService.fetchLearningPaths(hruidList, language as Language, `HRUIDs: ${hruidList.join(', ')}`, personalizationTarget); res.json(learningPaths.data); } diff --git a/backend/src/services/learning-paths/learning-path-personalization-util.ts b/backend/src/services/learning-paths/learning-path-personalization-util.ts index 648b7abc..c671e9a0 100644 --- a/backend/src/services/learning-paths/learning-path-personalization-util.ts +++ b/backend/src/services/learning-paths/learning-path-personalization-util.ts @@ -2,12 +2,64 @@ import { LearningPathNode } from '../../entities/content/learning-path-node.enti import { Student } from '../../entities/users/student.entity'; import { Group } from '../../entities/assignments/group.entity'; import { Submission } from '../../entities/assignments/submission.entity'; -import { getSubmissionRepository } from '../../data/repositories'; +import { + getClassRepository, + getGroupRepository, + getStudentRepository, + getSubmissionRepository +} from '../../data/repositories'; import { LearningObjectIdentifier } from '../../entities/content/learning-object-identifier'; import { LearningPathTransition } from '../../entities/content/learning-path-transition.entity'; import { JSONPath } from 'jsonpath-plus'; export type PersonalizationTarget = { type: 'student'; student: Student } | { type: 'group'; group: Group }; + +/** + * Shortcut function to easily create a PersonalizationTarget object for a student by his/her username. + * @param username Username of the student we want to generate a personalized learning path for. + * If there is no student with this username, return undefined. + */ +export async function personalizedForStudent(username: string): Promise { + const student = await getStudentRepository().findByUsername(username); + if (student) { + return { + type: "student", + student: student + } + } else { + return undefined; + } +} + +/** + * Shortcut function to easily create a PersonalizationTarget object for a group by class name, assignment number and + * group number. + * @param classId Id of the class in which this group was created + * @param assignmentNumber Number of the assignment for which this group was created + * @param groupNumber Number of the group for which we want to personalize the learning path. + */ +export async function personalizedForGroup(classId: string, assignmentNumber: number, groupNumber: number): Promise { + const clazz = await getClassRepository().findById(classId); + if (!clazz) { + return undefined; + } + const group = await getGroupRepository().findOne({ + assignment: { + within: clazz, + id: assignmentNumber, + }, + groupNumber: groupNumber + }) + if (group) { + return { + type: "group", + group: group + } + } else { + return undefined; + } +} + /** * Returns the last submission for the learning object associated with the given node and for the student or group */ From 78267a241657c3f54f21a20e1c779b2b5f7ea28c Mon Sep 17 00:00:00 2001 From: Lint Action Date: Tue, 11 Mar 2025 11:18:55 +0000 Subject: [PATCH 77/96] style: fix linting issues met ESLint --- .../learning-paths/learning-path-personalization-util.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/src/services/learning-paths/learning-path-personalization-util.ts b/backend/src/services/learning-paths/learning-path-personalization-util.ts index 02d7d9ab..b7145bf3 100644 --- a/backend/src/services/learning-paths/learning-path-personalization-util.ts +++ b/backend/src/services/learning-paths/learning-path-personalization-util.ts @@ -26,9 +26,9 @@ export async function personalizedForStudent(username: string): Promise Date: Tue, 11 Mar 2025 11:18:59 +0000 Subject: [PATCH 78/96] style: fix linting issues met Prettier --- backend/src/controllers/learning-paths.ts | 20 +++++----- .../learning-path-personalization-util.ts | 39 +++++++++---------- 2 files changed, 29 insertions(+), 30 deletions(-) diff --git a/backend/src/controllers/learning-paths.ts b/backend/src/controllers/learning-paths.ts index fca86bfb..7a98ab2e 100644 --- a/backend/src/controllers/learning-paths.ts +++ b/backend/src/controllers/learning-paths.ts @@ -2,12 +2,9 @@ import { Request, Response } from 'express'; import { themes } from '../data/themes.js'; import { FALLBACK_LANG } from '../config.js'; import learningPathService from '../services/learning-paths/learning-path-service'; -import {BadRequestException, NotFoundException} from '../exceptions'; -import {Language} from "../entities/content/language"; -import { - PersonalizationTarget, personalizedForGroup, - personalizedForStudent -} from "../services/learning-paths/learning-path-personalization-util"; +import { BadRequestException, NotFoundException } from '../exceptions'; +import { Language } from '../entities/content/language'; +import { PersonalizationTarget, personalizedForGroup, personalizedForStudent } from '../services/learning-paths/learning-path-personalization-util'; /** * Fetch learning paths based on query parameters. @@ -26,10 +23,10 @@ export async function getLearningPaths(req: Request, res: Response): Promise theme.hruids); } - const learningPaths = await learningPathService.fetchLearningPaths(hruidList, language as Language, `HRUIDs: ${hruidList.join(', ')}`, personalizationTarget); + const learningPaths = await learningPathService.fetchLearningPaths( + hruidList, + language as Language, + `HRUIDs: ${hruidList.join(', ')}`, + personalizationTarget + ); res.json(learningPaths.data); } diff --git a/backend/src/services/learning-paths/learning-path-personalization-util.ts b/backend/src/services/learning-paths/learning-path-personalization-util.ts index b7145bf3..df344917 100644 --- a/backend/src/services/learning-paths/learning-path-personalization-util.ts +++ b/backend/src/services/learning-paths/learning-path-personalization-util.ts @@ -2,12 +2,7 @@ import { LearningPathNode } from '../../entities/content/learning-path-node.enti import { Student } from '../../entities/users/student.entity'; import { Group } from '../../entities/assignments/group.entity'; import { Submission } from '../../entities/assignments/submission.entity'; -import { - getClassRepository, - getGroupRepository, - getStudentRepository, - getSubmissionRepository -} from '../../data/repositories'; +import { getClassRepository, getGroupRepository, getStudentRepository, getSubmissionRepository } from '../../data/repositories'; import { LearningObjectIdentifier } from '../../entities/content/learning-object-identifier'; import { LearningPathTransition } from '../../entities/content/learning-path-transition.entity'; import { JSONPath } from 'jsonpath-plus'; @@ -23,12 +18,11 @@ export async function personalizedForStudent(username: string): Promise { +export async function personalizedForGroup( + classId: string, + assignmentNumber: number, + groupNumber: number +): Promise { const clazz = await getClassRepository().findById(classId); if (!clazz) { return undefined; @@ -48,16 +46,15 @@ export async function personalizedForGroup(classId: string, assignmentNumber: nu within: clazz, id: assignmentNumber, }, - groupNumber: groupNumber - }) + groupNumber: groupNumber, + }); if (group) { return { - type: "group", - group: group - } - } - return undefined; - + type: 'group', + group: group, + }; + } + return undefined; } /** From 001bd11cca877a39615a9b42f6a1d02721ac19d4 Mon Sep 17 00:00:00 2001 From: Laure Jablonski Date: Wed, 12 Mar 2025 08:55:19 +0100 Subject: [PATCH 79/96] frontend: functionaliteit van idp werkt in login --- frontend/src/views/LoginPage.vue | 92 +++++++++++++++++++++++++++++--- 1 file changed, 86 insertions(+), 6 deletions(-) diff --git a/frontend/src/views/LoginPage.vue b/frontend/src/views/LoginPage.vue index 1cee79fb..a538a80c 100644 --- a/frontend/src/views/LoginPage.vue +++ b/frontend/src/views/LoginPage.vue @@ -1,4 +1,5 @@