Run the tests on Node 22.16, and fix what a fresh setup tripped over

Node 22.16 runs TypeScript only behind --experimental-strip-types (on by
default from 22.18), so `npm test` failed on every file with
ERR_UNKNOWN_FILE_EXTENSION. The flag is harmless on newer versions.

probe and session-diagnose still imported dist/schulcloud/client.js, which
the move to core/ renamed, so both scripts died at import.

A relative MIRROR_DIR broke file serving: res.sendFile refuses a relative
path, and resolveWithin returns an absolute path only when its root is one.
The config resolves it once, at load.

package-lock.json is what `npm install` records today: the `schulcloud`
bin, and no stale peer flags. 101 tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-09-16 20:19:15 +02:00
parent 1de026ca43
commit 237395e4f4
5 changed files with 9 additions and 8 deletions

5
package-lock.json generated
View File

@@ -18,6 +18,7 @@
"zod": "^3.25.76" "zod": "^3.25.76"
}, },
"bin": { "bin": {
"schulcloud": "dist/bin/cli.js",
"schulcloud-mcp": "dist/bin/stdio.js" "schulcloud-mcp": "dist/bin/stdio.js"
}, },
"devDependencies": { "devDependencies": {
@@ -981,7 +982,6 @@
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"accepts": "^2.0.0", "accepts": "^2.0.0",
"body-parser": "^2.2.1", "body-parser": "^2.2.1",
@@ -1269,7 +1269,6 @@
"resolved": "https://registry.npmjs.org/hono/-/hono-4.13.7.tgz", "resolved": "https://registry.npmjs.org/hono/-/hono-4.13.7.tgz",
"integrity": "sha512-c8/gF9ac8Y78/agExVocyLevgR+JlpNB444Py0FSX8pJoPdYUfUzRcXtYEYGwt6l19qIlVZPN5Mfsw9jFShmQQ==", "integrity": "sha512-c8/gF9ac8Y78/agExVocyLevgR+JlpNB444Py0FSX8pJoPdYUfUzRcXtYEYGwt6l19qIlVZPN5Mfsw9jFShmQQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=16.9.0" "node": ">=16.9.0"
} }
@@ -1873,7 +1872,6 @@
"resolved": "https://registry.npmjs.org/pg/-/pg-8.23.0.tgz", "resolved": "https://registry.npmjs.org/pg/-/pg-8.23.0.tgz",
"integrity": "sha512-Ip2EQCngowJLGOfCwkFhPXU7/ljlhn6Rxlmy4XYfL2Y+vyRM59+8uR2xqRWKdYmbXmxCFOAmKxBuSUCdF34qLg==", "integrity": "sha512-Ip2EQCngowJLGOfCwkFhPXU7/ljlhn6Rxlmy4XYfL2Y+vyRM59+8uR2xqRWKdYmbXmxCFOAmKxBuSUCdF34qLg==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"pg-connection-string": "^2.14.0", "pg-connection-string": "^2.14.0",
"pg-pool": "^3.14.0", "pg-pool": "^3.14.0",
@@ -2637,7 +2635,6 @@
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"funding": { "funding": {
"url": "https://github.com/sponsors/colinhacks" "url": "https://github.com/sponsors/colinhacks"
} }

View File

@@ -17,7 +17,7 @@
"start": "node dist/bin/http.js", "start": "node dist/bin/http.js",
"stdio": "node dist/bin/stdio.js", "stdio": "node dist/bin/stdio.js",
"typecheck": "tsc -p tsconfig.json --noEmit", "typecheck": "tsc -p tsconfig.json --noEmit",
"test": "node --test test/*.test.ts", "test": "node --test --experimental-strip-types test/*.test.ts",
"probe": "node --env-file=.env scripts/probe.mjs", "probe": "node --env-file=.env scripts/probe.mjs",
"smoke": "node --env-file=.env scripts/smoke.mjs", "smoke": "node --env-file=.env scripts/smoke.mjs",
"session-diagnose": "node --env-file=.env scripts/session-diagnose.mjs", "session-diagnose": "node --env-file=.env scripts/session-diagnose.mjs",

View File

@@ -7,7 +7,7 @@
* Read-only. Usage: `node --env-file=.env scripts/probe.mjs` * Read-only. Usage: `node --env-file=.env scripts/probe.mjs`
*/ */
import { loadConfig } from '../dist/config.js'; import { loadConfig } from '../dist/config.js';
import { SchulcloudClient, SchulcloudApiError } from '../dist/schulcloud/client.js'; import { SchulcloudClient, SchulcloudApiError } from '../dist/core/client.js';
const config = loadConfig(); const config = loadConfig();
const client = new SchulcloudClient(config); const client = new SchulcloudClient(config);

View File

@@ -19,7 +19,7 @@
* (default 150 — enough to pass the ~2 h mark where an open tab would strike). * (default 150 — enough to pass the ~2 h mark where an open tab would strike).
*/ */
import { loadConfig } from '../dist/config.js'; import { loadConfig } from '../dist/config.js';
import { SchulcloudClient, SchulcloudApiError } from '../dist/schulcloud/client.js'; import { SchulcloudClient, SchulcloudApiError } from '../dist/core/client.js';
const totalMinutes = Number(process.argv[2] ?? 150); const totalMinutes = Number(process.argv[2] ?? 150);
const INTERVAL_MS = 10 * 60_000; const INTERVAL_MS = 10 * 60_000;

View File

@@ -6,6 +6,8 @@
* DevTools stays an obvious, mechanical step — see docs/AUTH.md. * DevTools stays an obvious, mechanical step — see docs/AUTH.md.
*/ */
import { resolve } from 'node:path';
export interface Config { export interface Config {
/** Instance base URL, no trailing slash, e.g. `https://schulcloud-thueringen.de`. */ /** Instance base URL, no trailing slash, e.g. `https://schulcloud-thueringen.de`. */
baseUrl: string; baseUrl: string;
@@ -76,7 +78,9 @@ export function loadConfig(): Config {
requestTimeoutMs: int('REQUEST_TIMEOUT_MS', 30_000), requestTimeoutMs: int('REQUEST_TIMEOUT_MS', 30_000),
keepaliveIntervalMs: intAllowingZero('KEEPALIVE_INTERVAL_MS', 30 * 60_000), keepaliveIntervalMs: intAllowingZero('KEEPALIVE_INTERVAL_MS', 30 * 60_000),
databaseUrl: process.env.DATABASE_URL?.trim() || undefined, databaseUrl: process.env.DATABASE_URL?.trim() || undefined,
mirrorDir: process.env.MIRROR_DIR?.trim() || '/var/lib/schulcloud-mcp/mirror', // Absolute: res.sendFile rejects a relative path, and resolveWithin only
// returns an absolute path if the root it is given is one.
mirrorDir: resolve(process.env.MIRROR_DIR?.trim() || '/var/lib/schulcloud-mcp/mirror'),
mirrorMaxBytes: int('MIRROR_MAX_BYTES', 64 * 1024 * 1024), mirrorMaxBytes: int('MIRROR_MAX_BYTES', 64 * 1024 * 1024),
crawlIntervalMs: intAllowingZero('CRAWL_INTERVAL_MS', 6 * 60 * 60_000), crawlIntervalMs: intAllowingZero('CRAWL_INTERVAL_MS', 6 * 60 * 60_000),
}; };