From 9d6770628beccd4dd50ae98b3d06085488de7fa7 Mon Sep 17 00:00:00 2001 From: J Luetkens Date: Fri, 22 May 2026 14:41:42 +0200 Subject: [PATCH 01/11] fix: include import schema in correction prompt --- .../exams/rule-packs/default/prompt.template.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/modules/exams/rule-packs/default/prompt.template.md b/modules/exams/rule-packs/default/prompt.template.md index 54de0df..fb09b83 100644 --- a/modules/exams/rule-packs/default/prompt.template.md +++ b/modules/exams/rule-packs/default/prompt.template.md @@ -19,9 +19,9 @@ Session workflow (generic and strict): Chat reference roles: - the contract's `Session Chat Reference` identifies the session/contract only - Leistung chatRefs are internal import/export keys for submitted Leistungen and look like `chat-0001` -- the import bundle top-level `chatRef` must be the resolved Leistung chatRef from the contract's `Chat References` list +- every import bundle object must use the resolved Leistung `chatRef` from the contract's `Chat References` list as its top-level `chatRef` - never ask the user to provide a Leistung `chatRef` -- never write the `Session Chat Reference` into the import bundle top-level `chatRef` +- never write the `Session Chat Reference` into an import bundle top-level `chatRef` Matching rule: - extract the needed matching information from the submitted Leistung itself @@ -35,13 +35,15 @@ Matching rule: Control commands in this session: - `Zwischenexport`: output current result state for the active resolved Leistung `chatRef` -- `Ende Korrektur`: finish the session cleanly after current Leistung +- `Ende Korrektur`: finish the session cleanly after current Leistung and export all resolved Leistungen from this session - `Verwirf letzte Arbeit`: discard only the last processed Leistung for the active resolved Leistung `chatRef` Output format requirements: - `Zwischenexport` must return exactly one raw JSON object for the active resolved Leistung `chatRef` when a valid export can be produced -- `Ende Korrektur` must return exactly one raw JSON object for the final export when a valid export can be produced -- the returned JSON must conform to the loaded import bundle schema +- `Ende Korrektur` must return exactly one raw JSON array; each array item must be one import bundle object for one resolved Leistung `chatRef` +- every object in that array must conform to the loaded import bundle schema below +- do not invent a wrapper object for multi-Leistung export +- do not include unresolved, unprocessed, or non-matching Leistungen in the JSON array - do not output YAML, CSV, Markdown tables, prose summaries, or any substitute export format when emitting JSON - do not wrap JSON in Markdown code fences - do not prepend or append explanatory text when emitting JSON for an export command @@ -58,6 +60,10 @@ Required import bundle fields: {{contractMarkdown}} +## Import Bundle Schema + +{{importBundleSchema}} + ## Rule Pack Metadata {{rulePackManifest}} From 5d9344f86f9195414f0c92f19467225cf4b48eac Mon Sep 17 00:00:00 2001 From: J Luetkens Date: Fri, 22 May 2026 14:42:51 +0200 Subject: [PATCH 02/11] fix: render import schema into correction prompt --- .../exams/src/use-cases/export-correction-session.use-case.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/exams/src/use-cases/export-correction-session.use-case.ts b/modules/exams/src/use-cases/export-correction-session.use-case.ts index bb54fdf..bfb3c40 100644 --- a/modules/exams/src/use-cases/export-correction-session.use-case.ts +++ b/modules/exams/src/use-cases/export-correction-session.use-case.ts @@ -95,6 +95,7 @@ function buildPromptArtifacts( ): string { return renderTemplate(rulePack.templates.prompt, { contractMarkdown, + importBundleSchema: JSON.stringify(rulePack.importBundleSchema, null, 2), rulePackManifest: JSON.stringify(rulePack.manifest, null, 2), rulePackRules: JSON.stringify(rulePack.rules, null, 2), 'session.id': contract.id, From 3ca294593fe82dc8f11d28cae49b49f67cab4897 Mon Sep 17 00:00:00 2001 From: J Luetkens Date: Fri, 22 May 2026 14:44:49 +0200 Subject: [PATCH 03/11] fix: sync embedded import schema prompt --- modules/exams/src/rule-packs/default-pack.ts | 23 +++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/modules/exams/src/rule-packs/default-pack.ts b/modules/exams/src/rule-packs/default-pack.ts index c673e16..d4ad31c 100644 --- a/modules/exams/src/rule-packs/default-pack.ts +++ b/modules/exams/src/rule-packs/default-pack.ts @@ -93,9 +93,10 @@ const DEFAULT_CONTRACT_TEMPLATE = `# Correction Session Contract ## Expected Return Format -- \`Zwischenexport\` and \`Ende Korrektur\` must return raw JSON only when a valid import-bundle export can be produced. -- The returned JSON must conform to the loaded import bundle schema. -- No YAML, CSV, Markdown table, prose summary, or substitute export format is allowed when emitting JSON. +- \`Zwischenexport\` must return raw JSON only when a valid import-bundle export can be produced. +- \`Ende Korrektur\` must return one raw JSON array containing one import bundle object per resolved Leistung. +- Every import bundle object must conform to the loaded import bundle schema. +- No YAML, CSV, Markdown table, prose summary, wrapper object, or substitute export format is allowed when emitting JSON. - If a valid import-bundle JSON export cannot be produced, output exactly one short plain-text line stating the missing prerequisite, and nothing else. ## Chat References @@ -142,9 +143,9 @@ Session workflow (generic and strict): Chat reference roles: - the contract's \`Session Chat Reference\` identifies the session/contract only - Leistung chatRefs are internal import/export keys for submitted Leistungen and look like \`chat-0001\` -- the import bundle top-level \`chatRef\` must be the resolved Leistung chatRef from the contract's \`Chat References\` list +- every import bundle object must use the resolved Leistung \`chatRef\` from the contract's \`Chat References\` list as its top-level \`chatRef\` - never ask the user to provide a Leistung \`chatRef\` -- never write the \`Session Chat Reference\` into the import bundle top-level \`chatRef\` +- never write the \`Session Chat Reference\` into an import bundle top-level \`chatRef\` Matching rule: - extract the needed matching information from the submitted Leistung itself @@ -158,13 +159,15 @@ Matching rule: Control commands in this session: - \`Zwischenexport\`: output current result state for the active resolved Leistung \`chatRef\` -- \`Ende Korrektur\`: finish the session cleanly after current Leistung +- \`Ende Korrektur\`: finish the session cleanly after current Leistung and export all resolved Leistungen from this session - \`Verwirf letzte Arbeit\`: discard only the last processed Leistung for the active resolved Leistung \`chatRef\` Output format requirements: - \`Zwischenexport\` must return exactly one raw JSON object for the active resolved Leistung \`chatRef\` when a valid export can be produced -- \`Ende Korrektur\` must return exactly one raw JSON object for the final export when a valid export can be produced -- the returned JSON must conform to the loaded import bundle schema +- \`Ende Korrektur\` must return exactly one raw JSON array; each array item must be one import bundle object for one resolved Leistung \`chatRef\` +- every object in that array must conform to the loaded import bundle schema below +- do not invent a wrapper object for multi-Leistung export +- do not include unresolved, unprocessed, or non-matching Leistungen in the JSON array - do not output YAML, CSV, Markdown tables, prose summaries, or any substitute export format when emitting JSON - do not wrap JSON in Markdown code fences - do not prepend or append explanatory text when emitting JSON for an export command @@ -181,6 +184,10 @@ Required import bundle fields: {{contractMarkdown}} +## Import Bundle Schema + +{{importBundleSchema}} + ## Rule Pack Metadata {{rulePackManifest}} From 6aff05959f5814bc0cf433d58234026271a09110 Mon Sep 17 00:00:00 2001 From: J Luetkens Date: Fri, 22 May 2026 14:45:36 +0200 Subject: [PATCH 04/11] fix: define correction session final export shape --- modules/exams/rule-packs/default/contract.template.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/exams/rule-packs/default/contract.template.md b/modules/exams/rule-packs/default/contract.template.md index f4989f7..6d75b9e 100644 --- a/modules/exams/rule-packs/default/contract.template.md +++ b/modules/exams/rule-packs/default/contract.template.md @@ -25,9 +25,10 @@ ## Expected Return Format -- `Zwischenexport` and `Ende Korrektur` must return raw JSON only when a valid import-bundle export can be produced. -- The returned JSON must conform to the loaded import bundle schema. -- No YAML, CSV, Markdown table, prose summary, or substitute export format is allowed when emitting JSON. +- `Zwischenexport` must return raw JSON only when one valid import-bundle export can be produced. +- `Ende Korrektur` must return one raw JSON array containing one import bundle object per resolved Leistung. +- Every import bundle object must conform to the loaded import bundle schema. +- No YAML, CSV, Markdown table, prose summary, wrapper object, or substitute export format is allowed when emitting JSON. - If a valid import-bundle JSON export cannot be produced, output exactly one short plain-text line stating the missing prerequisite, and nothing else. ## Chat References From 179a5f1dccf374e6c41171f53e403656c68fafe2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 May 2026 13:58:38 +0000 Subject: [PATCH 05/11] chore(deps): bump the npm-production group with 3 updates Bumps the npm-production group with 3 updates: [better-sqlite3](https://github.com/WiseLibs/better-sqlite3), [vue-i18n](https://github.com/intlify/vue-i18n/tree/HEAD/packages/vue-i18n) and [vue-router](https://github.com/vuejs/router). Updates `better-sqlite3` from 12.9.0 to 12.10.0 - [Release notes](https://github.com/WiseLibs/better-sqlite3/releases) - [Commits](https://github.com/WiseLibs/better-sqlite3/compare/v12.9.0...v12.10.0) Updates `vue-i18n` from 11.4.2 to 11.4.4 - [Release notes](https://github.com/intlify/vue-i18n/releases) - [Changelog](https://github.com/intlify/vue-i18n/blob/master/CHANGELOG.md) - [Commits](https://github.com/intlify/vue-i18n/commits/v11.4.4/packages/vue-i18n) Updates `vue-router` from 5.0.6 to 5.0.7 - [Release notes](https://github.com/vuejs/router/releases) - [Commits](https://github.com/vuejs/router/compare/v5.0.6...v5.0.7) --- updated-dependencies: - dependency-name: better-sqlite3 dependency-version: 12.10.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: npm-production - dependency-name: vue-i18n dependency-version: 11.4.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: npm-production - dependency-name: vue-router dependency-version: 5.0.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: npm-production ... Signed-off-by: dependabot[bot] --- apps/teacher-ui/package.json | 4 +- package-lock.json | 158 ++++++++++++++++++++++++---------- package.json | 2 +- packages/storage/package.json | 2 +- 4 files changed, 118 insertions(+), 48 deletions(-) diff --git a/apps/teacher-ui/package.json b/apps/teacher-ui/package.json index 4714ae7..a6ac0b9 100644 --- a/apps/teacher-ui/package.json +++ b/apps/teacher-ui/package.json @@ -21,8 +21,8 @@ "primeicons": "^7.0.0", "primevue": "^4.5.5", "vue": "^3.5.34", - "vue-i18n": "^11.4.2", - "vue-router": "^5.0.6" + "vue-i18n": "^11.4.4", + "vue-router": "^5.0.7" }, "devDependencies": { "@types/jest": "^30.0.0", diff --git a/package-lock.json b/package-lock.json index 6c0d8d7..accab14 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "dependencies": { "@types/bcryptjs": "^3.0.0", "bcryptjs": "^3.0.3", - "better-sqlite3": "^12.9.0", + "better-sqlite3": "^12.10.0", "sql.js": "^1.14.1", "uuid": "^14.0.0" }, @@ -38,8 +38,8 @@ "primeicons": "^7.0.0", "primevue": "^4.5.5", "vue": "^3.5.34", - "vue-i18n": "^11.4.2", - "vue-router": "^5.0.6" + "vue-i18n": "^11.4.4", + "vue-router": "^5.0.7" }, "devDependencies": { "@types/jest": "^30.0.0", @@ -237,6 +237,7 @@ }, "node_modules/@babel/generator": { "version": "7.29.1", + "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.29.0", @@ -847,61 +848,61 @@ } }, "node_modules/@intlify/core-base": { - "version": "11.4.2", - "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-11.4.2.tgz", - "integrity": "sha512-7fpuCcVmeLv2T9qHsARqGvh8xt+sV2fH+Q+gMHFwB/rPXzo85DpbJFKn7dBH1L5p0c2cSh2DW+2h/64EKrISmA==", + "version": "11.4.4", + "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-11.4.4.tgz", + "integrity": "sha512-w/vItlylrAmhebkIbVl5YY8XMCtj8Mb2g70ttxktMYuf5AuRahgEHL2iLgLIsZBIbTSgs4hkUo7ucCL0uTJvOg==", "license": "MIT", "dependencies": { - "@intlify/devtools-types": "11.4.2", - "@intlify/message-compiler": "11.4.2", - "@intlify/shared": "11.4.2" + "@intlify/devtools-types": "11.4.4", + "@intlify/message-compiler": "11.4.4", + "@intlify/shared": "11.4.4" }, "engines": { - "node": ">= 16" + "node": ">= 22" }, "funding": { "url": "https://github.com/sponsors/kazupon" } }, "node_modules/@intlify/devtools-types": { - "version": "11.4.2", - "resolved": "https://registry.npmjs.org/@intlify/devtools-types/-/devtools-types-11.4.2.tgz", - "integrity": "sha512-3u8EN1kB6EMSi96KXs5k7a8y2X2g4+h3X6iwVZU47cP4n+mTuq//WMjG588BzSp/2XQ/dTXo2BLUXX+XS+PNfA==", + "version": "11.4.4", + "resolved": "https://registry.npmjs.org/@intlify/devtools-types/-/devtools-types-11.4.4.tgz", + "integrity": "sha512-PcBLmGmDQsTSVV911P8upzpcLJO1CNVYi/IH6bGnLR2nA+0L963+kXN1ZrisTEnbtw2ewN6HMMSldqzjronA0Q==", "license": "MIT", "dependencies": { - "@intlify/core-base": "11.4.2", - "@intlify/shared": "11.4.2" + "@intlify/core-base": "11.4.4", + "@intlify/shared": "11.4.4" }, "engines": { - "node": ">= 16" + "node": ">= 22" }, "funding": { "url": "https://github.com/sponsors/kazupon" } }, "node_modules/@intlify/message-compiler": { - "version": "11.4.2", - "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-11.4.2.tgz", - "integrity": "sha512-a6CDSGSMTGrg0BjD97x8TBYPf7qQMDlZipJ6UDfv/pd4OIym8TMlHu3MsH0bTNnRdAG2D6EFEykIgiQPqvtTkA==", + "version": "11.4.4", + "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-11.4.4.tgz", + "integrity": "sha512-vn0OAV9pYkJlPPmgnsSm5eAG3mL0+9C/oaded2JY9jmxBbhmUXT3TcAUY8WRgLY9Hte7lkUJKpXrVlYjMXBD2w==", "license": "MIT", "dependencies": { - "@intlify/shared": "11.4.2", + "@intlify/shared": "11.4.4", "source-map-js": "^1.0.2" }, "engines": { - "node": ">= 16" + "node": ">= 22" }, "funding": { "url": "https://github.com/sponsors/kazupon" } }, "node_modules/@intlify/shared": { - "version": "11.4.2", - "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-11.4.2.tgz", - "integrity": "sha512-NzpHbguRCsOHDwxmlBa9qu/imc+/QWgsYUaK6FZeNC0wK8QfAbhqrktEp/haVzxU1aikH8IX4ytD+mfFEMi/9A==", + "version": "11.4.4", + "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-11.4.4.tgz", + "integrity": "sha512-QRUCHqda1U6aR14FR0vvXD4+4gj6+fm0AhAozvSuRCw0fCvrmCugWpgiR4xH2NI6s8am6N9p5OhirplsX8ZS3g==", "license": "MIT", "engines": { - "node": ">= 16" + "node": ">= 22" }, "funding": { "url": "https://github.com/sponsors/kazupon" @@ -2118,6 +2119,12 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/@types/jsesc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@types/jsesc/-/jsesc-2.5.1.tgz", + "integrity": "sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "25.7.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-25.7.0.tgz", @@ -3135,9 +3142,9 @@ } }, "node_modules/better-sqlite3": { - "version": "12.9.0", - "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.9.0.tgz", - "integrity": "sha512-wqUv4Gm3toFpHDQmaKD4QhZm3g1DjUBI0yzS4UBl6lElUmXFYdTQmmEDpAFa5o8FiFiymURypEnfVHzILKaxqQ==", + "version": "12.10.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.10.0.tgz", + "integrity": "sha512-CyzaZRQKyHkB2ZInfTTl2nvT33EbDpjkLEbE8/Zck3Ll6O0qqvuGdrJ45HgtH+HykRg88ITY3AdreBGN70aBSQ==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -3145,7 +3152,7 @@ "prebuild-install": "^7.1.1" }, "engines": { - "node": "20.x || 22.x || 23.x || 24.x || 25.x" + "node": "20.x || 22.x || 23.x || 24.x || 25.x || 26.x" } }, "node_modules/bidi-js": { @@ -7677,18 +7684,18 @@ } }, "node_modules/vue-i18n": { - "version": "11.4.2", - "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-11.4.2.tgz", - "integrity": "sha512-sADDeKXqAGsPX6tK3t3y2ZiMpbVWN12tG+MhTiJ06rVoh58eGtM4wFyw3uWGbVkXByVp9Ne/AP+nSSzI+J9OAQ==", + "version": "11.4.4", + "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-11.4.4.tgz", + "integrity": "sha512-gIbXVSFQV4jcSJxfwdZ5zSZmZ+12CnX0K3vBkRSd6Zn+HSzCp+QwUgPwpD/uN0oKNKI9RzlUXPKVedEuMgNG0A==", "license": "MIT", "dependencies": { - "@intlify/core-base": "11.4.2", - "@intlify/devtools-types": "11.4.2", - "@intlify/shared": "11.4.2", + "@intlify/core-base": "11.4.4", + "@intlify/devtools-types": "11.4.4", + "@intlify/shared": "11.4.4", "@vue/devtools-api": "^6.5.0" }, "engines": { - "node": ">= 16" + "node": ">= 22" }, "funding": { "url": "https://github.com/sponsors/kazupon" @@ -7702,14 +7709,14 @@ "license": "MIT" }, "node_modules/vue-router": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-5.0.6.tgz", - "integrity": "sha512-9+kmUTGbKMyW9Asoy98IXXYIzrTMT7JDAdpDDeEkorHvybpUvBI2wsrSM5jFOXrFydpzRFJ9vAh+80DN2PGu9w==", + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-5.0.7.tgz", + "integrity": "sha512-dqfk8kvRbCutmCOCj/XLDqDEYxc1wBdAOGLuVy5M93ifYMsBd5fIjfaPN4tQAbxr5IprdBDIox1gr4wYyOx/SA==", "license": "MIT", "dependencies": { - "@babel/generator": "^7.28.6", + "@babel/generator": "^8.0.0-rc.4", "@vue-macros/common": "^3.1.1", - "@vue/devtools-api": "^8.0.6", + "@vue/devtools-api": "^8.1.1", "ast-walker-scope": "^0.8.3", "chokidar": "^5.0.0", "json5": "^2.2.3", @@ -7730,9 +7737,9 @@ }, "peerDependencies": { "@pinia/colada": ">=0.21.2", - "@vue/compiler-sfc": "^3.5.17", + "@vue/compiler-sfc": "^3.5.34", "pinia": "^3.0.4", - "vue": "^3.5.0" + "vue": "^3.5.34" }, "peerDependenciesMeta": { "@pinia/colada": { @@ -7746,6 +7753,69 @@ } } }, + "node_modules/vue-router/node_modules/@babel/generator": { + "version": "8.0.0-rc.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-8.0.0-rc.6.tgz", + "integrity": "sha512-6mIzgVK8DgEzvIapoQwhXTMnnkuE4STQmVv9H03i/tZ2ml8oev3TRvZJgTenK2Bsq0YWNtzOrFdTyNzCMFtjJQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^8.0.0-rc.6", + "@babel/types": "^8.0.0-rc.6", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "@types/jsesc": "^2.5.0", + "jsesc": "^3.0.2" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/vue-router/node_modules/@babel/helper-string-parser": { + "version": "8.0.0-rc.6", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.0-rc.6.tgz", + "integrity": "sha512-BCkFy+zN6kXQed3YOT7aJl93NfDSzQc3pBfsvTVPs9gU9X3V0aefEF5kwBT0E+mDWH9QgKaZstYUQN9VdQZT4g==", + "license": "MIT", + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/vue-router/node_modules/@babel/helper-validator-identifier": { + "version": "8.0.0-rc.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.0-rc.6.tgz", + "integrity": "sha512-nVJ+1JcCgntv8d78rRo++o2wuODT0Irknx2BF8Np4Ft2CRgjLqIs4qzSZ8b66yGbBdMWGmZBO9WEZv1hhNiSpg==", + "license": "MIT", + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/vue-router/node_modules/@babel/parser": { + "version": "8.0.0-rc.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-8.0.0-rc.6.tgz", + "integrity": "sha512-rOS8IpdO7mQELkTPlCsTgPejO0bFuZdEDCGQJouYbYf9e1FLTym7Fei2pEjq8q7MWbX0ravcd7QQYKs1TxOuog==", + "license": "MIT", + "dependencies": { + "@babel/types": "^8.0.0-rc.6" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/vue-router/node_modules/@babel/types": { + "version": "8.0.0-rc.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.0-rc.6.tgz", + "integrity": "sha512-p7/ABylAYlexb31wtRdIfH9L9A0Z2T/9H6zAqzqndkY2PLkvNNc580wGhp/gGKN4Sp9sQvSkhc6Oga8/O+wTyw==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^8.0.0-rc.6", + "@babel/helper-validator-identifier": "^8.0.0-rc.6" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, "node_modules/vue-router/node_modules/@vue/devtools-api": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-8.1.1.tgz", @@ -8115,7 +8185,7 @@ "@types/jest": "^30.0.0", "@types/node": "^25.7.0", "@types/uuid": "^11.0.0", - "better-sqlite3": "^12.9.0", + "better-sqlite3": "^12.10.0", "fake-indexeddb": "^6.2.0", "jest": "^30.4.2", "ts-jest": "^29.4.9", diff --git a/package.json b/package.json index 4d4db81..1c09153 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "dependencies": { "@types/bcryptjs": "^3.0.0", "bcryptjs": "^3.0.3", - "better-sqlite3": "^12.9.0", + "better-sqlite3": "^12.10.0", "sql.js": "^1.14.1", "uuid": "^14.0.0" }, diff --git a/packages/storage/package.json b/packages/storage/package.json index 8fde537..25a8f1d 100644 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -47,7 +47,7 @@ "@types/jest": "^30.0.0", "@types/node": "^25.7.0", "@types/uuid": "^11.0.0", - "better-sqlite3": "^12.9.0", + "better-sqlite3": "^12.10.0", "fake-indexeddb": "^6.2.0", "jest": "^30.4.2", "ts-jest": "^29.4.9", From f604d31341015625f77ba844fa0de1d3a855f87a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 May 2026 13:59:29 +0000 Subject: [PATCH 06/11] chore(deps-dev): bump the npm-development group with 4 updates Bumps the npm-development group with 4 updates: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node), [@vitejs/plugin-vue](https://github.com/vitejs/vite-plugin-vue/tree/HEAD/packages/plugin-vue), [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) and [vue-tsc](https://github.com/vuejs/language-tools/tree/HEAD/packages/tsc). Updates `@types/node` from 25.7.0 to 25.9.0 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `@vitejs/plugin-vue` from 6.0.6 to 6.0.7 - [Release notes](https://github.com/vitejs/vite-plugin-vue/releases) - [Changelog](https://github.com/vitejs/vite-plugin-vue/blob/main/packages/plugin-vue/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite-plugin-vue/commits/plugin-vue@6.0.7/packages/plugin-vue) Updates `vite` from 8.0.12 to 8.0.13 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v8.0.13/packages/vite) Updates `vue-tsc` from 3.2.9 to 3.3.0 - [Release notes](https://github.com/vuejs/language-tools/releases) - [Changelog](https://github.com/vuejs/language-tools/blob/master/CHANGELOG.md) - [Commits](https://github.com/vuejs/language-tools/commits/v3.3.0/packages/tsc) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 25.9.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: npm-development - dependency-name: "@vitejs/plugin-vue" dependency-version: 6.0.7 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: npm-development - dependency-name: vite dependency-version: 8.0.13 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: npm-development - dependency-name: vue-tsc dependency-version: 3.3.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: npm-development ... Signed-off-by: dependabot[bot] --- apps/teacher-ui/package.json | 8 +- modules/exams/package.json | 2 +- modules/sport/package.json | 2 +- modules/students/package.json | 2 +- package-lock.json | 215 ++++++++++++++++------------------ packages/core/package.json | 2 +- packages/plugins/package.json | 2 +- packages/storage/package.json | 2 +- 8 files changed, 114 insertions(+), 121 deletions(-) diff --git a/apps/teacher-ui/package.json b/apps/teacher-ui/package.json index 4714ae7..09aee06 100644 --- a/apps/teacher-ui/package.json +++ b/apps/teacher-ui/package.json @@ -26,8 +26,8 @@ }, "devDependencies": { "@types/jest": "^30.0.0", - "@types/node": "^25.7.0", - "@vitejs/plugin-vue": "^6.0.6", + "@types/node": "^25.9.0", + "@vitejs/plugin-vue": "^6.0.7", "@vue/tsconfig": "^0.9.1", "@vue/vue3-jest": "^29.2.6", "identity-obj-proxy": "^3.0.0", @@ -36,7 +36,7 @@ "jsdom": "^29.1.1", "ts-jest": "^29.4.9", "typescript": "^6.0.3", - "vite": "^8.0.12", - "vue-tsc": "^3.2.9" + "vite": "^8.0.13", + "vue-tsc": "^3.3.0" } } diff --git a/modules/exams/package.json b/modules/exams/package.json index 5d7bb74..aa9f521 100644 --- a/modules/exams/package.json +++ b/modules/exams/package.json @@ -31,7 +31,7 @@ }, "devDependencies": { "@types/jest": "^30.0.0", - "@types/node": "^25.7.0", + "@types/node": "^25.9.0", "@types/uuid": "^11.0.0", "jest": "^30.4.2", "ts-jest": "^29.4.9", diff --git a/modules/sport/package.json b/modules/sport/package.json index d15cf17..46d2746 100644 --- a/modules/sport/package.json +++ b/modules/sport/package.json @@ -25,7 +25,7 @@ }, "devDependencies": { "@types/jest": "^30.0.0", - "@types/node": "^25.7.0", + "@types/node": "^25.9.0", "@types/uuid": "^11.0.0", "fast-check": "^4.8.0", "jest": "^30.4.2", diff --git a/modules/students/package.json b/modules/students/package.json index eca49c8..255b4bc 100644 --- a/modules/students/package.json +++ b/modules/students/package.json @@ -22,7 +22,7 @@ }, "devDependencies": { "@types/jest": "^30.0.0", - "@types/node": "^25.7.0", + "@types/node": "^25.9.0", "jest": "^30.4.2", "ts-jest": "^29.4.9", "typescript": "^6.0.3" diff --git a/package-lock.json b/package-lock.json index 6c0d8d7..2915529 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,8 +43,8 @@ }, "devDependencies": { "@types/jest": "^30.0.0", - "@types/node": "^25.7.0", - "@vitejs/plugin-vue": "^6.0.6", + "@types/node": "^25.9.0", + "@vitejs/plugin-vue": "^6.0.7", "@vue/tsconfig": "^0.9.1", "@vue/vue3-jest": "^29.2.6", "identity-obj-proxy": "^3.0.0", @@ -53,8 +53,8 @@ "jsdom": "^29.1.1", "ts-jest": "^29.4.9", "typescript": "^6.0.3", - "vite": "^8.0.12", - "vue-tsc": "^3.2.9" + "vite": "^8.0.13", + "vue-tsc": "^3.3.0" } }, "modules/exams": { @@ -71,7 +71,7 @@ }, "devDependencies": { "@types/jest": "^30.0.0", - "@types/node": "^25.7.0", + "@types/node": "^25.9.0", "@types/uuid": "^11.0.0", "jest": "^30.4.2", "ts-jest": "^29.4.9", @@ -90,7 +90,7 @@ }, "devDependencies": { "@types/jest": "^30.0.0", - "@types/node": "^25.7.0", + "@types/node": "^25.9.0", "@types/uuid": "^11.0.0", "fast-check": "^4.8.0", "jest": "^30.4.2", @@ -107,7 +107,7 @@ }, "devDependencies": { "@types/jest": "^30.0.0", - "@types/node": "^25.7.0", + "@types/node": "^25.9.0", "jest": "^30.4.2", "ts-jest": "^29.4.9", "typescript": "^6.0.3" @@ -1519,9 +1519,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.129.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.129.0.tgz", - "integrity": "sha512-3oz8m3FGdr2nDXVqmFUw7jolKliC4MoyXYIG2c7gpjBnzUWQpUGIYcXYKxTdTi+N2jusvt610ckTMkxdwHkYEg==", + "version": "0.130.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.130.0.tgz", + "integrity": "sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q==", "dev": true, "license": "MIT", "funding": { @@ -1627,9 +1627,9 @@ } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0.tgz", - "integrity": "sha512-TWMZnRLMe63C2Lhyicviu7ZHaU4kxa6PS3rofvc9GmcvptzNN11BcfQ4Sl7MwTOsisQoa2keB/EBdNCAnUo8vA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.1.tgz", + "integrity": "sha512-fJI3I0r3C3Oj/zdBCpaCmBRZYf07xpaq4yCfDDoSFm+beWNzbIl26puW8RraUdugoJw/95zerNOn6jasAhzSmg==", "cpu": [ "arm64" ], @@ -1644,9 +1644,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0.tgz", - "integrity": "sha512-6XcD+8k0gPVItNagEw78/qqcBDwKcwDYS8V2hRmVsfUSIrd8cWe/CBvRDI5toqFyPfj+FJr6t8U6Xj2P2prEew==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.1.tgz", + "integrity": "sha512-cKnAhWEsV7TPcA/5EAteDp6KcJZBQ2G+BqE7zayMMi7kMvwRsbv7WT9aOnn0WNl4SKEIf43vjS31iUPu80nzXg==", "cpu": [ "arm64" ], @@ -1661,9 +1661,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0.tgz", - "integrity": "sha512-iN/tWVXRQDWvmZlKdceP1Dwug9GDpEymhb9p4xnEe6zvCg5lFmzVljl+1qR1NVx3yfGpr2Na+CuLmv5IU8uzfQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.1.tgz", + "integrity": "sha512-YKrVwQjIRBPo+5G/u03wGjbdy4q7pyzCe93DK9VJ7zkVmeg8LJ7GbgsiHWdR4xSoe4CAXRD7Bcjgbtr64bkXNg==", "cpu": [ "x64" ], @@ -1678,9 +1678,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0.tgz", - "integrity": "sha512-jjQMDvvwSOuhOwMszD/klSOjyWMM3zI64hWTj9KT5x4MxRbZAf+7vLQ6qouRhtsLVFHr3f0ILaJAfgENPiQdAQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.1.tgz", + "integrity": "sha512-z/oBsREo46SsFqBwYtFe0kpJeBijAT48O/WXLI4suiCLBkr03RTtTJMCzSdDd2znlh8VJizL09XVkQgk8IZonw==", "cpu": [ "x64" ], @@ -1695,9 +1695,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0.tgz", - "integrity": "sha512-d//Dtg2x6/m3mbV64yUGNnDGNZaDGRpDLLNGerHQUVObuNaIQaaDp25yUiqGXtHEXX+NP2d0wAlmKgpYgIAJ2A==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.1.tgz", + "integrity": "sha512-ik8q7GM11zxvYxFc2PeDcT6TBvhCQMaUxfph/M5l9sKuTs/Sjg3L+Byw0F7w0ZVLBZmx30P+gG0ECzzN+MFcmQ==", "cpu": [ "arm" ], @@ -1712,9 +1712,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0.tgz", - "integrity": "sha512-n7Ofp0mx+aB2cC+Sdy5YtMnXtY9lchnHbY+3Yt0uq9JsWQExf4f5Whu0tK0R8Jdc9S6RchTHjIFY7uc92puOVQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.1.tgz", + "integrity": "sha512-QoSx2EkyrrdZ6kcyE8stqZ62t0Yra8Fs5ia9lOxJrh6TMQJK7gQKmscdTHf7pOXKREKrVwOtJcQG3qVSfc866A==", "cpu": [ "arm64" ], @@ -1729,9 +1729,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0.tgz", - "integrity": "sha512-EIVjy2cgd7uuMMo94FVkBp7F6DhcZAUwNURkSG3RwUmvAXR6s0ISxM81U+IydcZByPG0pZIHsf1b6kTxoFDgJA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.1.tgz", + "integrity": "sha512-uwNwFpwKeNiZawfAWBgg0VIztPTV3ihhh1vV334h9ivnNLorxnQMU6Fz8wG1Zb4Qh9LC1/MkcyT3YlDXG3Rsgg==", "cpu": [ "arm64" ], @@ -1746,9 +1746,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0.tgz", - "integrity": "sha512-JEwwOPcwTLAcpDQlqSmjEmfs63xJnSiUNIGvLcDLUHCWK4XowpS/7c7tUsUH6uT/ct6bMUTdXKfI8967FYj6mg==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.1.tgz", + "integrity": "sha512-zY1bul7OWr7DFBiJ++wofXvnr8B45ce3QsQUhKrIhXsygAh7bTkwyeM1bi1a2g5C/yC/N8TZyGDEoMfm/l9mpg==", "cpu": [ "ppc64" ], @@ -1763,9 +1763,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0.tgz", - "integrity": "sha512-0wjCFhLrihtAubnT9iA0N++0pSV0z5Hg7tNGdNJ4RFaINceHadoF+kiFGyY1qSSNVIAZtLotG8Ju1bgDPkjnFA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.1.tgz", + "integrity": "sha512-0frlsT/f4Ft6I7SMESTKnF3cZsdicQn1dCMkF/jT9wDLE+gGoiQfv1nmT9e+s7s/fekvvy6tZM2jHvI2tkbJDQ==", "cpu": [ "s390x" ], @@ -1780,9 +1780,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0.tgz", - "integrity": "sha512-Dfn7iak9BcMMePxcoJfpSbWqnEyrp/dRF63/8qW/eHBdOZov6x5aShLLEYGYdIeSJ6vMLK/XCVB+lGIxm41bQA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.1.tgz", + "integrity": "sha512-XABVmGp9Tg0WspTVvwduTc4fpqy6JnAUrSQe6OuyqD/03nI7r0O9OWUkMIwFrjKAIqolvqoA4ZrJppgwE0Gxmw==", "cpu": [ "x64" ], @@ -1797,9 +1797,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0.tgz", - "integrity": "sha512-5/utzzDmD/pD/bmuaUcbTf/sZYy0aztwIVlfpoW1fTjCZ0BaPOMVWGZL1zvgxyi7ZIVYWlxKONHmSbHuiOh8Jw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.1.tgz", + "integrity": "sha512-bV4fzswuzVcKD90o/VM6QqKxnxlDq0g2BISDLNVmxrnhpv1DDbyPhCIjYfvzYLV+MvkKKnQt2Q6AO86SEBULUQ==", "cpu": [ "x64" ], @@ -1814,9 +1814,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0.tgz", - "integrity": "sha512-ouJs8VcUomfLfpbUECqFMRqdV4x6aeAK3MA4m6vTrJJjKyWTV5KnxZx7Jd9G+GlDaQQxubcba00x16OyJ1meig==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.1.tgz", + "integrity": "sha512-/Mh0Zhq3OP7fVs0kcQHZP6lZEthMGTaSf8UBQYSFEZDWGXXlEC+nJ6EqenaK2t4LBXMe3A+K/G2BVXXdtOr4PQ==", "cpu": [ "arm64" ], @@ -1831,9 +1831,9 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0.tgz", - "integrity": "sha512-E+oHKGiDA+lsKMmFtffDDw91EryDT7uJocrIuCHqhm6bCTM6xFK+3gaCkYOHfPwQr0cCNarSM2xaELoQDz9jJg==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.1.tgz", + "integrity": "sha512-+1xc9X45l8ufsBAm6Gjvx2qDRIY9lTVt0cgWNcJ+1gdhXvkbxePA60yRTwSTuXL09CMhyJmjpV7E3NoyxbqFQQ==", "cpu": [ "wasm32" ], @@ -1850,9 +1850,9 @@ } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0.tgz", - "integrity": "sha512-yYK02n8Rngo+gbm1y6G0+7jk1sJ/2Wt7K0me0Y7k/ErBpyf+LJ2gFpqWVTcRV1rUepBlQRmpgWkTQCiiwrK0Ow==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.1.tgz", + "integrity": "sha512-1D+UqZdfnuR+Jy1GgMJwi85bD40H21uNmOPRWQhw4oRSuolZ/B5rixZ45DK2KXOTCvmVCecauWgEhbw8bI7tOw==", "cpu": [ "arm64" ], @@ -1867,9 +1867,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0.tgz", - "integrity": "sha512-14bpChMahXRRXiTwahSl+zzHPW6qQTXtkMuJBFlbo+pqSAews2d4BdCSHfrJ/MBsCZtpmTafsY+1QhBzitcmdg==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.1.tgz", + "integrity": "sha512-INAycaWuhlOK3wk4mRHGsdgwYWmd9cChdPdE9bwWmy6rn9VqVNYNFGhOdXrofXUxwHIncSiPNb8tNm8knDVIeQ==", "cpu": [ "x64" ], @@ -1884,9 +1884,9 @@ } }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.13", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.13.tgz", - "integrity": "sha512-3ngTAv6F/Py35BsYbeeLeecvhMKdsKm4AoOETVhAA+Qc8nrA2I0kF7oa93mE9qnIurngOSpMnQ0x2nQY2FPviA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", "dev": true, "license": "MIT" }, @@ -2119,12 +2119,12 @@ } }, "node_modules/@types/node": { - "version": "25.7.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.7.0.tgz", - "integrity": "sha512-z+pdZyxE+RTQE9AcboAZCb4otwcrvgHD+GlBpPgn0emDVt0ohrTMhAwlr2Wd9nZ+nihhYFxO2pThz3C5qSu2Eg==", + "version": "25.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.0.tgz", + "integrity": "sha512-AOQwYUNolgy3VosiRqXrACUXTN8nJUtPl7FJXMqZVyxiiCLhQuG3jXKvCS1ALr+Y2OmZhzzLVlYPEqJaiqkaJQ==", "license": "MIT", "dependencies": { - "undici-types": "~7.21.0" + "undici-types": ">=7.24.0 <7.24.7" } }, "node_modules/@types/qrcode": { @@ -2536,13 +2536,13 @@ "link": true }, "node_modules/@vitejs/plugin-vue": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.6.tgz", - "integrity": "sha512-u9HHgfrq3AjXlysn0eINFnWQOJQLO9WN6VprZ8FXl7A2bYisv3Hui9Ij+7QZ41F/WYWarHjwBbXtD7dKg3uxbg==", + "version": "6.0.7", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.7.tgz", + "integrity": "sha512-km+p+XdSz9Sxm5rqUbqcSfZYaAniKxWBj1KURl+Jr7UaPvvX7BmaWMdP69I5rrFDeQGyxAG7NXdc57vz+snhWg==", "dev": true, "license": "MIT", "dependencies": { - "@rolldown/pluginutils": "1.0.0-rc.13" + "@rolldown/pluginutils": "^1.0.1" }, "engines": { "node": "^20.19.0 || >=22.12.0" @@ -2680,9 +2680,9 @@ } }, "node_modules/@vue/language-core": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.2.9.tgz", - "integrity": "sha512-ie0ojt/0fU/GfIogh+zgHbaYRPlt9S+cLOxcWwF7nTSFh897BVgnFKL2byT4kpp1mlqYWZ2psGwSniyE2xsxYw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.3.0.tgz", + "integrity": "sha512-EyUxq1b8Yoxk6hQ6X33BIRnfFLb9Rbm9w/8G8y6uMxlQu7CW7yy9JS/z54xSpIvBvVWX6Lt5v1aBGwmrqD4aJw==", "dev": true, "license": "MIT", "dependencies": { @@ -6711,14 +6711,14 @@ "license": "MIT" }, "node_modules/rolldown": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0.tgz", - "integrity": "sha512-yD986aXDESFGS95spT1LAv0jssywP4npMEjmMHyN2/5+eE8qQJUype2AaKkRiLgBgyD0LFlubwAht7VmY8rGoA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.1.tgz", + "integrity": "sha512-X0KQHljNnEkWNqqiz9zJrGunh1B0HgOxLXvnFpCOcadzcy5qohZ3tqMEUg00vncoRovXuK3ZqCT9KnnKzoInFQ==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.129.0", - "@rolldown/pluginutils": "1.0.0" + "@oxc-project/types": "=0.130.0", + "@rolldown/pluginutils": "^1.0.0" }, "bin": { "rolldown": "bin/cli.mjs" @@ -6727,29 +6727,22 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0", - "@rolldown/binding-darwin-arm64": "1.0.0", - "@rolldown/binding-darwin-x64": "1.0.0", - "@rolldown/binding-freebsd-x64": "1.0.0", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0", - "@rolldown/binding-linux-arm64-gnu": "1.0.0", - "@rolldown/binding-linux-arm64-musl": "1.0.0", - "@rolldown/binding-linux-ppc64-gnu": "1.0.0", - "@rolldown/binding-linux-s390x-gnu": "1.0.0", - "@rolldown/binding-linux-x64-gnu": "1.0.0", - "@rolldown/binding-linux-x64-musl": "1.0.0", - "@rolldown/binding-openharmony-arm64": "1.0.0", - "@rolldown/binding-wasm32-wasi": "1.0.0", - "@rolldown/binding-win32-arm64-msvc": "1.0.0", - "@rolldown/binding-win32-x64-msvc": "1.0.0" - } - }, - "node_modules/rolldown/node_modules/@rolldown/pluginutils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0.tgz", - "integrity": "sha512-aKs/3GSWyV0mrhNmt/96/Z3yczC3yvrzYATCiCXQebBsGyYzjNdUphRVLeJQ67ySKVXRfMxt2lm12pmXvbPFQQ==", - "dev": true, - "license": "MIT" + "@rolldown/binding-android-arm64": "1.0.1", + "@rolldown/binding-darwin-arm64": "1.0.1", + "@rolldown/binding-darwin-x64": "1.0.1", + "@rolldown/binding-freebsd-x64": "1.0.1", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.1", + "@rolldown/binding-linux-arm64-gnu": "1.0.1", + "@rolldown/binding-linux-arm64-musl": "1.0.1", + "@rolldown/binding-linux-ppc64-gnu": "1.0.1", + "@rolldown/binding-linux-s390x-gnu": "1.0.1", + "@rolldown/binding-linux-x64-gnu": "1.0.1", + "@rolldown/binding-linux-x64-musl": "1.0.1", + "@rolldown/binding-openharmony-arm64": "1.0.1", + "@rolldown/binding-wasm32-wasi": "1.0.1", + "@rolldown/binding-win32-arm64-msvc": "1.0.1", + "@rolldown/binding-win32-x64-msvc": "1.0.1" + } }, "node_modules/rrweb-cssom": { "version": "0.8.0", @@ -7401,9 +7394,9 @@ } }, "node_modules/undici-types": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.21.0.tgz", - "integrity": "sha512-w9IMgQrz4O0YN1LtB7K5P63vhlIOvC7opSmouCJ+ZywlPAlO9gIkJ+otk6LvGpAs2wg4econaCz3TvQ9xPoyuQ==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", "license": "MIT" }, "node_modules/unplugin": { @@ -7560,16 +7553,16 @@ } }, "node_modules/vite": { - "version": "8.0.12", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.12.tgz", - "integrity": "sha512-w2dDofOWv2QB09ZITZBsvKTVAlYvPR4IAmrY/v0ir9KvLs0xybR7i48wxhM1/oyBWO34wPns+bPGw5ZrZqDpZg==", + "version": "8.0.13", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.13.tgz", + "integrity": "sha512-MFtjBYgzmSxmgA4RAfjIyXWpGe1oALnjgUTzzV7QLx/TKxCzjtMH6Fd9/eVK+5Fg1qNoz5VAwsmMs/NofrmJvw==", "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.14", - "rolldown": "1.0.0", + "rolldown": "1.0.1", "tinyglobby": "^0.2.16" }, "bin": { @@ -7792,14 +7785,14 @@ } }, "node_modules/vue-tsc": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.2.9.tgz", - "integrity": "sha512-qm8/nbo+9eZc1SCndm9wT+gq23pM+wRIdHY0wjm83B3lIginHTwcdrLUyTrKjDWXbMVNjKegNrnymhpdqnCL3A==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.3.0.tgz", + "integrity": "sha512-kY8RcoTOENASi0P1GLPvJgA2+hoGF+t8We1UGgmnAb1r/GjTUMSE3zz+WGfjPORZNnBHdAt67sVPhBLXWunkeg==", "dev": true, "license": "MIT", "dependencies": { "@volar/typescript": "2.4.28", - "@vue/language-core": "3.2.9" + "@vue/language-core": "3.3.0" }, "bin": { "vue-tsc": "bin/vue-tsc.js" @@ -8081,7 +8074,7 @@ "version": "0.1.0", "license": "MIT", "devDependencies": { - "@types/node": "^25.7.0", + "@types/node": "^25.9.0", "typescript": "^6.0.3" } }, @@ -8093,7 +8086,7 @@ "@viccoboard/core": "*" }, "devDependencies": { - "@types/node": "^25.7.0", + "@types/node": "^25.9.0", "typescript": "^6.0.3" } }, @@ -8113,7 +8106,7 @@ "@types/better-sqlite3": "^7.6.13", "@types/crypto-js": "^4.2.2", "@types/jest": "^30.0.0", - "@types/node": "^25.7.0", + "@types/node": "^25.9.0", "@types/uuid": "^11.0.0", "better-sqlite3": "^12.9.0", "fake-indexeddb": "^6.2.0", diff --git a/packages/core/package.json b/packages/core/package.json index 63d2bfd..52a5813 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -24,7 +24,7 @@ "author": "", "license": "MIT", "devDependencies": { - "@types/node": "^25.7.0", + "@types/node": "^25.9.0", "typescript": "^6.0.3" } } diff --git a/packages/plugins/package.json b/packages/plugins/package.json index 683f2bf..22ed3fb 100644 --- a/packages/plugins/package.json +++ b/packages/plugins/package.json @@ -27,7 +27,7 @@ "@viccoboard/core": "*" }, "devDependencies": { - "@types/node": "^25.7.0", + "@types/node": "^25.9.0", "typescript": "^6.0.3" } } diff --git a/packages/storage/package.json b/packages/storage/package.json index 8fde537..4f25717 100644 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -45,7 +45,7 @@ "@types/better-sqlite3": "^7.6.13", "@types/crypto-js": "^4.2.2", "@types/jest": "^30.0.0", - "@types/node": "^25.7.0", + "@types/node": "^25.9.0", "@types/uuid": "^11.0.0", "better-sqlite3": "^12.9.0", "fake-indexeddb": "^6.2.0", From 0cc268ca01ec14324568a8d42220109b309aabff Mon Sep 17 00:00:00 2001 From: J Luetkens Date: Wed, 27 May 2026 08:55:43 +0200 Subject: [PATCH 07/11] test: require correction import schema in prompt --- .../exams/tests/export-correction-session.use-case.test.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/exams/tests/export-correction-session.use-case.test.ts b/modules/exams/tests/export-correction-session.use-case.test.ts index 7d5be2f..644c8df 100644 --- a/modules/exams/tests/export-correction-session.use-case.test.ts +++ b/modules/exams/tests/export-correction-session.use-case.test.ts @@ -208,6 +208,13 @@ describe('ExportCorrectionSessionArtifactsUseCase', () => { expect(artifact.contractFile.content).toContain('expectedHorizon'); expect(artifact.contractFile.content).toContain('Erwartungshorizont'); expect(artifact.promptFile.content).toContain('do not invent or replace them'); + expect(artifact.promptFile.content).toContain('## Import Bundle Schema'); + expect(artifact.promptFile.content).toContain('"required": ['); + expect(artifact.promptFile.content).toContain('"contract"'); + expect(artifact.promptFile.content).toContain('"chatRef"'); + expect(artifact.promptFile.content).toContain('"importedTaskScores"'); + expect(artifact.promptFile.content).toContain('must return exactly one raw JSON array'); + expect(artifact.promptFile.content).toContain('do not invent a wrapper object'); }); it('does not require users to provide Leistung chatRefs before evaluation', () => { From 7de9ea5e8eb897d51b1f4e7c153ce6dfa52089fa Mon Sep 17 00:00:00 2001 From: J Luetkens Date: Wed, 27 May 2026 09:53:58 +0200 Subject: [PATCH 08/11] fix: import correction bundle batches --- .../import-kbr-correction-bundle.use-case.ts | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/modules/exams/src/use-cases/import-kbr-correction-bundle.use-case.ts b/modules/exams/src/use-cases/import-kbr-correction-bundle.use-case.ts index 318c0dc..a5f95ec 100644 --- a/modules/exams/src/use-cases/import-kbr-correction-bundle.use-case.ts +++ b/modules/exams/src/use-cases/import-kbr-correction-bundle.use-case.ts @@ -44,6 +44,10 @@ export interface ImportKbrCorrectionBundleInput { finalizeCorrection?: boolean; } +export interface ImportKbrCorrectionBundleBatchInput extends Omit { + bundles: unknown[]; +} + export interface ImportKbrCorrectionBundleResult { correction: Exams.CorrectionEntry; candidateId: string; @@ -53,6 +57,14 @@ export interface ImportKbrCorrectionBundleResult { uncertainties: CorrectionImportUncertainty[]; } +export interface ImportKbrCorrectionBundleBatchResult { + results: ImportKbrCorrectionBundleResult[]; + importedBundleCount: number; + importedTaskScoreCount: number; + skippedTaskScoreCount: number; + uncertainties: CorrectionImportUncertainty[]; +} + function asRecord(value: unknown): Record | undefined { if (typeof value !== 'object' || value === null || Array.isArray(value)) { return undefined; @@ -209,6 +221,22 @@ export class ImportKbrCorrectionBundleUseCase { private readonly recordCorrectionUseCase: RecordCorrectionUseCase ) {} + async executeMany(input: ImportKbrCorrectionBundleBatchInput): Promise { + const results: ImportKbrCorrectionBundleResult[] = []; + + for (const bundle of input.bundles) { + results.push(await this.execute({ ...input, bundle })); + } + + return { + results, + importedBundleCount: results.length, + importedTaskScoreCount: results.reduce((sum, result) => sum + result.importedTaskScoreCount, 0), + skippedTaskScoreCount: results.reduce((sum, result) => sum + result.skippedTaskScoreCount, 0), + uncertainties: results.flatMap((result) => result.uncertainties) + }; + } + async execute(input: ImportKbrCorrectionBundleInput): Promise { const schema = input.schema ?? getDefaultCorrectionImportBundleSchema(); const { bundle } = validateCorrectionImportBundle(input.bundle, schema); From 9f20c029d192da5c20be273fa8ee049b07c3a82e Mon Sep 17 00:00:00 2001 From: J Luetkens Date: Wed, 27 May 2026 09:55:40 +0200 Subject: [PATCH 09/11] fix: route correction bundle arrays through batch import --- .../src/composables/useExamsBridge.ts | 35 ++++++------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/apps/teacher-ui/src/composables/useExamsBridge.ts b/apps/teacher-ui/src/composables/useExamsBridge.ts index 41980c4..b28bc41 100644 --- a/apps/teacher-ui/src/composables/useExamsBridge.ts +++ b/apps/teacher-ui/src/composables/useExamsBridge.ts @@ -34,13 +34,9 @@ import { } from '@viccoboard/exams'; import { getStorageAdapter } from '../services/storage.service'; -/** - * Singleton exams bridge instance - */ let examsBridgeInstance: ExamsBridge | null = null; interface ExamsBridge { - // Repositories examRepository: ExamRepository; taskNodeRepository: TaskNodeRepository; criterionRepository: CriterionRepository; @@ -49,7 +45,6 @@ interface ExamsBridge { studentLongTermNoteRepository: StudentLongTermNoteRepository; correctionSheetPresetRepository: CorrectionSheetPresetRepository; - // Use Cases createExamPayload: typeof createExamPayload; recordCorrectionUseCase: RecordCorrectionUseCase; calculateGradeUseCase: CalculateGradeUseCase; @@ -60,7 +55,6 @@ interface ExamsBridge { exportCorrectionSessionArtifactsUseCase: ExportCorrectionSessionArtifactsUseCase; importKbrCorrectionBundleUseCase: ImportKbrCorrectionBundleUseCase; - // Services gradingKeyService: typeof GradingKeyService; gradingKeyEngine: typeof GradingKeyEngine; alternativeGradingService: typeof AlternativeGradingService; @@ -122,10 +116,6 @@ interface UseExamsBridgeResult { importCorrectionBundle(input: any): Promise; } -/** - * Initialize exams bridge - * Must be called after storage is initialized - */ export function initializeExamsBridge(): ExamsBridge { if (examsBridgeInstance) { return examsBridgeInstance; @@ -133,7 +123,6 @@ export function initializeExamsBridge(): ExamsBridge { const adapter = getStorageAdapter(); - // Initialize repositories with storage adapter const examRepo = new ExamRepository(adapter); const taskNodeRepo = new TaskNodeRepository(adapter); const criterionRepo = new CriterionRepository(adapter); @@ -142,7 +131,6 @@ export function initializeExamsBridge(): ExamsBridge { const studentLongTermNoteRepo = new StudentLongTermNoteRepository(adapter); const correctionSheetPresetRepo = new CorrectionSheetPresetRepository(examRepo); - // Initialize use cases with repositories const recordCorrectionUseCase = new RecordCorrectionUseCase(correctionEntryRepo, examRepo); const calculateGradeUseCase = new CalculateGradeUseCase(); const getCorrectionSheetPresetUseCase = new GetCorrectionSheetPresetUseCase(correctionSheetPresetRepo); @@ -165,7 +153,6 @@ export function initializeExamsBridge(): ExamsBridge { ); examsBridgeInstance = { - // Repositories examRepository: examRepo, taskNodeRepository: taskNodeRepo, criterionRepository: criterionRepo, @@ -174,7 +161,6 @@ export function initializeExamsBridge(): ExamsBridge { studentLongTermNoteRepository: studentLongTermNoteRepo, correctionSheetPresetRepository: correctionSheetPresetRepo, - // Use Cases createExamPayload, recordCorrectionUseCase, calculateGradeUseCase, @@ -185,7 +171,6 @@ export function initializeExamsBridge(): ExamsBridge { exportCorrectionSessionArtifactsUseCase, importKbrCorrectionBundleUseCase, - // Services (static classes are referenced directly) gradingKeyService: GradingKeyService, gradingKeyEngine: GradingKeyEngine, alternativeGradingService: AlternativeGradingService, @@ -210,8 +195,16 @@ export function initializeExamsBridge(): ExamsBridge { exportCorrectionSheetsPdfUseCase.exportAllCandidatesPdf(examId), exportCorrectionSession: (input) => exportCorrectionSessionArtifactsUseCase.execute(input), - importCorrectionBundle: (input) => - importKbrCorrectionBundleUseCase.execute(input), + importCorrectionBundle: (input) => { + if (Array.isArray(input.bundle)) { + return importKbrCorrectionBundleUseCase.executeMany({ + ...input, + bundles: input.bundle + }); + } + + return importKbrCorrectionBundleUseCase.execute(input); + }, initialized: true }; @@ -219,9 +212,6 @@ export function initializeExamsBridge(): ExamsBridge { return examsBridgeInstance; } -/** - * Get exams bridge instance - */ export function getExamsBridge(): ExamsBridge { if (!examsBridgeInstance) { throw new Error( @@ -231,10 +221,6 @@ export function getExamsBridge(): ExamsBridge { return examsBridgeInstance; } -/** - * Vue composable for exams module access - * Provides reactive access to exams bridge - */ export function useExamsBridge(): UseExamsBridgeResult { const bridge = shallowRef(examsBridgeInstance); @@ -243,7 +229,6 @@ export function useExamsBridge(): UseExamsBridgeResult { return { examsBridge: bridge, isInitialized, - // Direct references for convenience (after initialization) get examRepository() { return bridge.value?.examRepository; }, get taskNodeRepository() { return bridge.value?.taskNodeRepository; }, get criterionRepository() { return bridge.value?.criterionRepository; }, From c055deef7fc3a87cdcdfa6dd0eae5f491e0ae42f Mon Sep 17 00:00:00 2001 From: J Luetkens Date: Wed, 27 May 2026 09:56:41 +0200 Subject: [PATCH 10/11] test: cover batch correction bundle import --- ...r-correction-bundle-batch.use-case.test.ts | 172 ++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 modules/exams/tests/import-kbr-correction-bundle-batch.use-case.test.ts diff --git a/modules/exams/tests/import-kbr-correction-bundle-batch.use-case.test.ts b/modules/exams/tests/import-kbr-correction-bundle-batch.use-case.test.ts new file mode 100644 index 0000000..a35e07d --- /dev/null +++ b/modules/exams/tests/import-kbr-correction-bundle-batch.use-case.test.ts @@ -0,0 +1,172 @@ +import { + SQLiteStorage, + InitialSchemaMigration, + GradingSchemaMigration, + ExamSchemaMigration, + CorrectionSchemaMigration +} from '@viccoboard/storage/node'; +import { Exams } from '@viccoboard/core'; + +import { ExamRepository } from '../src/repositories/exam.repository'; +import { CorrectionEntryRepository } from '../src/repositories/correction-entry.repository'; +import { RecordCorrectionUseCase } from '../src/use-cases/record-correction.use-case-v2'; +import { ImportKbrCorrectionBundleUseCase } from '../src/use-cases/import-kbr-correction-bundle.use-case'; + +function createImportRules(): Exams.CorrectionSessionRules { + return { + taskSelection: 'leaf-only', + scoring: { + aggregation: 'task', + allowPartialPoints: true, + allowAlternativeGrading: true, + allowManualScoringUnits: false + }, + evidence: { + required: false, + supportedKinds: ['text', 'structured'], + allowMultipleEvidenceItems: true + }, + deductionGovernance: { + applyWhenPointsBelowMaxPoints: false, + requireDefectStatement: false, + requireEvidenceForDeductions: false, + requireExplanationForAnyNonFullScore: false, + rejectUnjustifiedDeductions: false, + minimumDeductionStepRequiresJustification: false, + onMissingDefect: 'allow-deduction', + onMissingEvidence: 'allow-deduction' + }, + imports: { + mergeStrategy: 'merge', + allowUnmappedScores: false, + preserveManualComments: true, + preserveExistingEvidence: true + } + }; +} + +function createBundle(chatRef: string, points: number): Exams.KbrCorrectionImportBundle { + return { + contract: { + id: 'contract-session-session-42', + chatRef: 'session-session-42', + title: 'Import contract', + parts: [], + taskTree: [], + scoringUnits: [], + rules: createImportRules() + }, + chatRef, + importedTaskScores: [ + { + taskId: 'task-1', + points, + maxPoints: 10 + } + ] + }; +} + +describe('ImportKbrCorrectionBundleUseCase batch import', () => { + let storage: SQLiteStorage; + let examRepo: ExamRepository; + let correctionRepo: CorrectionEntryRepository; + let importUseCase: ImportKbrCorrectionBundleUseCase; + let exam: Exams.Exam; + + beforeEach(async () => { + storage = new SQLiteStorage({ + databasePath: ':memory:', + memory: true + }); + await storage.initialize('test-password'); + storage.registerMigration(new InitialSchemaMigration(storage)); + storage.registerMigration(new GradingSchemaMigration(storage)); + storage.registerMigration(new ExamSchemaMigration(storage)); + storage.registerMigration(new CorrectionSchemaMigration(storage)); + await storage.migrate(); + + examRepo = new ExamRepository(storage.getAdapter()); + correctionRepo = new CorrectionEntryRepository(storage.getAdapter()); + const recordUseCase = new RecordCorrectionUseCase(correctionRepo, examRepo); + importUseCase = new ImportKbrCorrectionBundleUseCase(examRepo, correctionRepo, recordUseCase); + + exam = await examRepo.create({ + title: 'Batch Import Test', + assessmentFormat: 'klausur', + mode: Exams.ExamMode.Simple, + structure: { + parts: [], + tasks: [ + { + id: 'task-internal-1', + level: 1, + order: 1, + title: 'Aufgabe 1', + points: 10, + isChoice: false, + criteria: [], + allowComments: true, + allowSupportTips: false, + commentBoxEnabled: true, + subtasks: [] + } + ], + allowsComments: true, + allowsSupportTips: false, + totalPoints: 10 + }, + gradingKey: { + id: 'grading-key', + name: 'Standard', + type: Exams.GradingKeyType.Points, + totalPoints: 10, + gradeBoundaries: [], + roundingRule: { + type: 'nearest', + decimalPlaces: 1 + }, + errorPointsToGrade: false, + customizable: true, + modifiedAfterCorrection: false + }, + printPresets: [], + candidates: [], + candidateGroups: [], + status: 'in-progress' + }); + }); + + afterEach(async () => { + await storage.close(); + }); + + it('imports an array of correction bundles sequentially', async () => { + const result = await importUseCase.executeMany({ + examId: exam.id, + sessionId: 'session-42', + sessionMap: { + examId: exam.id, + sessionId: 'session-42', + candidateIdByChatRef: { + 'chat-0001': 'candidate-1', + 'chat-0002': 'candidate-2' + }, + taskIdByRef: { + 'task-1': 'task-internal-1' + } + }, + bundles: [createBundle('chat-0001', 8), createBundle('chat-0002', 10)] + }); + + expect(result.importedBundleCount).toBe(2); + expect(result.importedTaskScoreCount).toBe(2); + expect(result.results.map((entry) => entry.chatRef)).toEqual(['chat-0001', 'chat-0002']); + + const firstCorrection = await correctionRepo.findByExamAndCandidate(exam.id, 'candidate-1'); + const secondCorrection = await correctionRepo.findByExamAndCandidate(exam.id, 'candidate-2'); + + expect(firstCorrection?.taskScores[0].points).toBe(8); + expect(secondCorrection?.taskScores[0].points).toBe(10); + }); +}); From e949a14942b118f6d1e68794838c1978213f0f3c Mon Sep 17 00:00:00 2001 From: J Luetkens Date: Wed, 27 May 2026 10:28:55 +0200 Subject: [PATCH 11/11] Reduce exams bridge batch import diff --- .../src/composables/useExamsBridge.ts | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/apps/teacher-ui/src/composables/useExamsBridge.ts b/apps/teacher-ui/src/composables/useExamsBridge.ts index b28bc41..31f98df 100644 --- a/apps/teacher-ui/src/composables/useExamsBridge.ts +++ b/apps/teacher-ui/src/composables/useExamsBridge.ts @@ -34,9 +34,13 @@ import { } from '@viccoboard/exams'; import { getStorageAdapter } from '../services/storage.service'; +/** + * Singleton exams bridge instance + */ let examsBridgeInstance: ExamsBridge | null = null; interface ExamsBridge { + // Repositories examRepository: ExamRepository; taskNodeRepository: TaskNodeRepository; criterionRepository: CriterionRepository; @@ -45,6 +49,7 @@ interface ExamsBridge { studentLongTermNoteRepository: StudentLongTermNoteRepository; correctionSheetPresetRepository: CorrectionSheetPresetRepository; + // Use Cases createExamPayload: typeof createExamPayload; recordCorrectionUseCase: RecordCorrectionUseCase; calculateGradeUseCase: CalculateGradeUseCase; @@ -55,6 +60,7 @@ interface ExamsBridge { exportCorrectionSessionArtifactsUseCase: ExportCorrectionSessionArtifactsUseCase; importKbrCorrectionBundleUseCase: ImportKbrCorrectionBundleUseCase; + // Services gradingKeyService: typeof GradingKeyService; gradingKeyEngine: typeof GradingKeyEngine; alternativeGradingService: typeof AlternativeGradingService; @@ -116,6 +122,10 @@ interface UseExamsBridgeResult { importCorrectionBundle(input: any): Promise; } +/** + * Initialize exams bridge + * Must be called after storage is initialized + */ export function initializeExamsBridge(): ExamsBridge { if (examsBridgeInstance) { return examsBridgeInstance; @@ -123,6 +133,7 @@ export function initializeExamsBridge(): ExamsBridge { const adapter = getStorageAdapter(); + // Initialize repositories with storage adapter const examRepo = new ExamRepository(adapter); const taskNodeRepo = new TaskNodeRepository(adapter); const criterionRepo = new CriterionRepository(adapter); @@ -131,6 +142,7 @@ export function initializeExamsBridge(): ExamsBridge { const studentLongTermNoteRepo = new StudentLongTermNoteRepository(adapter); const correctionSheetPresetRepo = new CorrectionSheetPresetRepository(examRepo); + // Initialize use cases with repositories const recordCorrectionUseCase = new RecordCorrectionUseCase(correctionEntryRepo, examRepo); const calculateGradeUseCase = new CalculateGradeUseCase(); const getCorrectionSheetPresetUseCase = new GetCorrectionSheetPresetUseCase(correctionSheetPresetRepo); @@ -153,6 +165,7 @@ export function initializeExamsBridge(): ExamsBridge { ); examsBridgeInstance = { + // Repositories examRepository: examRepo, taskNodeRepository: taskNodeRepo, criterionRepository: criterionRepo, @@ -161,6 +174,7 @@ export function initializeExamsBridge(): ExamsBridge { studentLongTermNoteRepository: studentLongTermNoteRepo, correctionSheetPresetRepository: correctionSheetPresetRepo, + // Use Cases createExamPayload, recordCorrectionUseCase, calculateGradeUseCase, @@ -171,6 +185,7 @@ export function initializeExamsBridge(): ExamsBridge { exportCorrectionSessionArtifactsUseCase, importKbrCorrectionBundleUseCase, + // Services (static classes are referenced directly) gradingKeyService: GradingKeyService, gradingKeyEngine: GradingKeyEngine, alternativeGradingService: AlternativeGradingService, @@ -212,6 +227,9 @@ export function initializeExamsBridge(): ExamsBridge { return examsBridgeInstance; } +/** + * Get exams bridge instance + */ export function getExamsBridge(): ExamsBridge { if (!examsBridgeInstance) { throw new Error( @@ -221,6 +239,10 @@ export function getExamsBridge(): ExamsBridge { return examsBridgeInstance; } +/** + * Vue composable for exams module access + * Provides reactive access to exams bridge + */ export function useExamsBridge(): UseExamsBridgeResult { const bridge = shallowRef(examsBridgeInstance); @@ -229,6 +251,7 @@ export function useExamsBridge(): UseExamsBridgeResult { return { examsBridge: bridge, isInitialized, + // Direct references for convenience (after initialization) get examRepository() { return bridge.value?.examRepository; }, get taskNodeRepository() { return bridge.value?.taskNodeRepository; }, get criterionRepository() { return bridge.value?.criterionRepository; }, @@ -273,4 +296,4 @@ export function useExamsBridge(): UseExamsBridgeResult { importCorrectionBundle: (input: any) => bridge.value?.importCorrectionBundle(input) ?? Promise.reject(new Error('Bridge not initialized')) }; -} +} \ No newline at end of file