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

View File

@@ -17,7 +17,7 @@
"start": "node dist/bin/http.js",
"stdio": "node dist/bin/stdio.js",
"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",
"smoke": "node --env-file=.env scripts/smoke.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`
*/
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 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).
*/
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 INTERVAL_MS = 10 * 60_000;

View File

@@ -6,6 +6,8 @@
* DevTools stays an obvious, mechanical step — see docs/AUTH.md.
*/
import { resolve } from 'node:path';
export interface Config {
/** Instance base URL, no trailing slash, e.g. `https://schulcloud-thueringen.de`. */
baseUrl: string;
@@ -76,7 +78,9 @@ export function loadConfig(): Config {
requestTimeoutMs: int('REQUEST_TIMEOUT_MS', 30_000),
keepaliveIntervalMs: intAllowingZero('KEEPALIVE_INTERVAL_MS', 30 * 60_000),
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),
crawlIntervalMs: intAllowingZero('CRAWL_INTERVAL_MS', 6 * 60 * 60_000),
};