feat: Typen aus der Schema-Migration erzeugen statt aus einer Instanz

npm run generate-pocketbase-types liest das Collections-Array jetzt direkt
aus backend/pb_migrations/ (scripts/schema-to-json.mjs) und speist es über
pocketbase-typegen --json ein. Keine laufende Instanz, kein Token, kein
Netzwerkzugriff mehr nötig.

Damit stammen Schema und Typen aus derselben versionierten Quelle und können
nicht auseinanderlaufen. Vorher zeigten sie auf eine Remote-Instanz, deren
Stand niemand garantieren konnte — nach dem Neu-Deployment lieferte sie
prompt Typen ohne die fachlichen Collections.

frontend/.env enthält dadurch nur noch PUBLIC_PB_URL. PB_TYPEGEN_URL und
PB_TYPEGEN_TOKEN entfallen (letztere wurde ohnehin nirgends gelesen),
PB_SUPERUSER_TOKEN wird nicht mehr gebraucht. Die Datei enthält damit kein
Geheimnis mehr.

Verifiziert: ohne .env, ohne Token und ohne laufende Instanz entstehen alle
sechs Collections, svelte-check meldet 0 Fehler und 0 Warnungen.

Die System-Collections (_superusers, _mfas, ...) fehlen in den Typen, weil
sie bewusst nicht in der Migration stehen. Die Anwendung verwendet sie nicht.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Daniel Michelberger 2026-08-06 14:20:15 +02:00
parent 132ce04e0c
commit 2540666e1a
8 changed files with 127 additions and 137 deletions

3
.gitignore vendored
View file

@ -22,6 +22,9 @@ Thumbs.db
vite.config.js.timestamp-* vite.config.js.timestamp-*
vite.config.ts.timestamp-* vite.config.ts.timestamp-*
# Zwischendatei der Typgenerierung
.schema.tmp.json
# PocketBase-Laufzeitdaten # PocketBase-Laufzeitdaten
backend/pb_data/ backend/pb_data/
*.db *.db

View file

@ -30,7 +30,8 @@ npm run check
# Type-check with watch mode # Type-check with watch mode
npm run check:watch npm run check:watch
# Generate TypeScript types from PocketBase schema # Generate TypeScript types from the schema migration in backend/pb_migrations
# (no running instance, no token, no network access needed)
npm run generate-pocketbase-types npm run generate-pocketbase-types
``` ```
@ -99,7 +100,7 @@ The app uses Svelte 5 runes for state management with a custom store pattern:
- `$derived()` for computed values - `$derived()` for computed values
- `{@render children?.()}` for slot content - `{@render children?.()}` for slot content
**Type Generation**: After modifying PocketBase schema, run `npm run generate-pocketbase-types` to update TypeScript types. **Type Generation**: After modifying the schema migration in `backend/pb_migrations/`, run `npm run generate-pocketbase-types` to update `frontend/src/lib/types.d.ts`. The script reads the collections array straight out of the migration (`frontend/scripts/schema-to-json.mjs`) — schema and types come from the same versioned source and cannot drift apart. Commit the regenerated file.
**Async Data Loading**: Root layout (`src/routes/+layout.svelte`) shows pattern: **Async Data Loading**: Root layout (`src/routes/+layout.svelte`) shows pattern:
```svelte ```svelte

View file

@ -1,19 +1,11 @@
# Vorlage für die lokale .env — kopieren und ausfüllen: # Vorlage für die lokale .env — kopieren und ausfüllen:
# cp .env.example .env # cp .env.example .env
#
# Die echte .env steht in .gitignore und darf nicht committet werden,
# sie enthält den Superuser-Token.
# Basis-URL der PocketBase-Instanz, die das Frontend anspricht. # Basis-URL der PocketBase-Instanz, die das Frontend anspricht.
# Produktiv: https://api.stammtisch-hersbruck.de # Produktiv: https://api.stammtisch-hersbruck.de
# Lokales Backend aus ../backend: http://127.0.0.1:8090 # Lokales Backend aus ../backend: http://127.0.0.1:8090
#
# Die Variable wird zur Buildzeit eingesetzt ($env/static/public). Ein Wechsel
# der Instanz erfordert deshalb einen Neustart des Dev-Servers bzw. einen
# neuen Build.
PUBLIC_PB_URL=https://api.stammtisch-hersbruck.de PUBLIC_PB_URL=https://api.stammtisch-hersbruck.de
# Ziel für die Typgenerierung (npm run generate-pocketbase-types) und die
# Scripts unter scripts/. Zeigt üblicherweise auf dieselbe Instanz wie oben.
PB_TYPEGEN_URL=https://api.stammtisch-hersbruck.de/
# Superuser-Token für Schema-Zugriffe. Im PocketBase-Admin-UI erzeugen.
# Niemals committen.
PB_SUPERUSER_TOKEN=
PB_TYPEGEN_TOKEN=

View file

@ -35,15 +35,27 @@ Die App läuft dann auf http://stammtisch-hersbruck.de.localhost:31337
Die Variable wird von SvelteKit zur **Buildzeit** eingesetzt Die Variable wird von SvelteKit zur **Buildzeit** eingesetzt
(`$env/static/public`). Ein Wechsel der Instanz erfordert deshalb einen (`$env/static/public`). Ein Wechsel der Instanz erfordert deshalb einen
Neustart des Dev-Servers bzw. einen neuen Build — ein blosser Neustart des Neustart des Dev-Servers bzw. einen neuen Build — ein bloßer Neustart des
Containers genügt nicht. Containers genügt nicht.
`PB_TYPEGEN_URL` und `PB_SUPERUSER_TOKEN` gelten getrennt davon für Es ist die einzige Variable in `.env`; ein Token wird nicht mehr gebraucht.
`npm run generate-pocketbase-types`.
## Schema ## Typen
Das maßgebliche Schema liegt als Migration in `../backend/pb_migrations/`. Das maßgebliche Schema liegt als Migration in `../backend/pb_migrations/`.
Nach einer Schema-Änderung `npm run generate-pocketbase-types` ausführen — das Daraus entstehen auch die TypeScript-Typen:
aktualisiert `src/lib/types.d.ts`, aus dem der PocketBase-Client seine Typen
bezieht. npm run generate-pocketbase-types
Das Script liest das Collections-Array aus der Migration
(`scripts/schema-to-json.mjs`) und erzeugt daraus `src/lib/types.d.ts`. Es
braucht **keine laufende Instanz, keinen Token und keinen Netzwerkzugriff**
Schema und Typen kommen aus derselben versionierten Quelle und können deshalb
nicht auseinanderlaufen.
Nach jeder Schema-Änderung in `../backend/pb_migrations/` einmal ausführen und
die aktualisierte `types.d.ts` mitcommitten.
> Die System-Collections (`_superusers`, `_mfas`, …) stehen bewusst nicht in
> der Migration und fehlen deshalb in den Typen. Die Anwendung verwendet sie
> nicht.

View file

@ -1586,9 +1586,9 @@
} }
}, },
"node_modules/brace-expansion": { "node_modules/brace-expansion": {
"version": "1.1.12", "version": "1.1.18",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
@ -1697,6 +1697,7 @@
"version": "6.2.1", "version": "6.2.1",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
"integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
"deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
"dev": true, "dev": true,
"license": "ISC", "license": "ISC",
"optional": true, "optional": true,
@ -2107,9 +2108,9 @@
} }
}, },
"node_modules/es-object-atoms": { "node_modules/es-object-atoms": {
"version": "1.1.1", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -2228,17 +2229,17 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/form-data": { "node_modules/form-data": {
"version": "4.0.4", "version": "4.0.6",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz",
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"asynckit": "^0.4.0", "asynckit": "^0.4.0",
"combined-stream": "^1.0.8", "combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0", "es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2", "hasown": "^2.0.4",
"mime-types": "^2.1.12" "mime-types": "^2.1.35"
}, },
"engines": { "engines": {
"node": ">= 6" "node": ">= 6"
@ -2389,7 +2390,7 @@
"version": "7.2.3", "version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"deprecated": "Glob versions prior to v9 are no longer supported", "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
"dev": true, "dev": true,
"license": "ISC", "license": "ISC",
"optional": true, "optional": true,
@ -2466,9 +2467,9 @@
"optional": true "optional": true
}, },
"node_modules/hasown": { "node_modules/hasown": {
"version": "2.0.2", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -2627,9 +2628,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/ip-address": { "node_modules/ip-address": {
"version": "10.0.1", "version": "10.4.0",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.4.0.tgz",
"integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", "integrity": "sha512-oSK96Grm3aP6OrS263xVxbNDGVL7rzBtYdpGqlDG8iQdoenDoTs/nkki+DflYbAEE8Xl6o5YxhxlrKvI3nqKXQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
@ -3096,9 +3097,9 @@
} }
}, },
"node_modules/minimatch": { "node_modules/minimatch": {
"version": "3.1.2", "version": "3.1.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
"dev": true, "dev": true,
"license": "ISC", "license": "ISC",
"optional": true, "optional": true,
@ -3222,11 +3223,11 @@
"optional": true "optional": true
}, },
"node_modules/minipass-flush": { "node_modules/minipass-flush": {
"version": "1.0.5", "version": "1.0.7",
"resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.7.tgz",
"integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", "integrity": "sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA==",
"dev": true, "dev": true,
"license": "ISC", "license": "BlueOak-1.0.0",
"optional": true, "optional": true,
"dependencies": { "dependencies": {
"minipass": "^3.0.0" "minipass": "^3.0.0"
@ -3490,9 +3491,9 @@
} }
}, },
"node_modules/node-abi": { "node_modules/node-abi": {
"version": "3.78.0", "version": "3.94.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.78.0.tgz", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.94.0.tgz",
"integrity": "sha512-E2wEyrgX/CqvicaQYU3Ze1PFGjc4QYPGsjUrlYkqAE0WjHEZwgOsGMPMzkMse4LjJbDmaEuDX3CM036j5K2DSQ==", "integrity": "sha512-W5ZNO5KRPB5TkYmGVD9F6YqhsglXJzE6etpbmT+f6EQElhiX/UTG551cnsRGvLG3fyZEg9HwaDmNmj5nwJ4z9g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -3611,6 +3612,7 @@
"version": "6.2.1", "version": "6.2.1",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
"integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
"deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
"dev": true, "dev": true,
"license": "ISC", "license": "ISC",
"optional": true, "optional": true,
@ -3798,6 +3800,7 @@
"version": "7.1.3", "version": "7.1.3",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
"integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==",
"deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -3845,9 +3848,9 @@
} }
}, },
"node_modules/pump": { "node_modules/pump": {
"version": "3.0.3", "version": "3.0.4",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",
"integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -4039,9 +4042,9 @@
"optional": true "optional": true
}, },
"node_modules/semver": { "node_modules/semver": {
"version": "7.7.3", "version": "7.8.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
"dev": true, "dev": true,
"license": "ISC", "license": "ISC",
"bin": { "bin": {
@ -4149,14 +4152,14 @@
} }
}, },
"node_modules/socks": { "node_modules/socks": {
"version": "2.8.7", "version": "2.8.9",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.9.tgz",
"integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", "integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"dependencies": { "dependencies": {
"ip-address": "^10.0.1", "ip-address": "^10.1.1",
"smart-buffer": "^4.2.0" "smart-buffer": "^4.2.0"
}, },
"engines": { "engines": {
@ -4273,6 +4276,7 @@
"version": "6.2.1", "version": "6.2.1",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
"integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
"deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
"dev": true, "dev": true,
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
@ -4536,9 +4540,9 @@
} }
}, },
"node_modules/tar-fs": { "node_modules/tar-fs": {
"version": "2.1.4", "version": "2.1.5",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.5.tgz",
"integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", "integrity": "sha512-OboTd8mmMhZDNPV+UjQcK9yKAatXu2aJ+r1w4im1Otd4M4fl2hwvdoXUxIYHFTHWK/3y3FarBP70v3vwmGlOxw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {

View file

@ -10,7 +10,7 @@
"prepare": "svelte-kit sync || echo ''", "prepare": "svelte-kit sync || echo ''",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"generate-pocketbase-types": "set -a && . ./.env && set +a && pocketbase-typegen --url \"$PB_TYPEGEN_URL\" --token \"$PB_SUPERUSER_TOKEN\" --out ./src/lib/types.d.ts" "generate-pocketbase-types": "node scripts/schema-to-json.mjs > .schema.tmp.json && pocketbase-typegen --json .schema.tmp.json --out ./src/lib/types.d.ts && rm -f .schema.tmp.json"
}, },
"devDependencies": { "devDependencies": {
"@internationalized/date": "^3.12.1", "@internationalized/date": "^3.12.1",

View file

@ -0,0 +1,52 @@
/**
* Extrahiert das Collections-Array aus der PocketBase-Migration und schreibt es
* als JSON auf stdout in der Form, die `pocketbase-typegen --json` erwartet.
*
* Damit entstehen die TypeScript-Typen aus dem versionierten Schema im Repo,
* ohne laufende Instanz, ohne Token und ohne Netzwerkzugriff.
*
* Aufruf über npm run generate-pocketbase-types.
*/
import { readFileSync, readdirSync } from 'node:fs'
import { join } from 'node:path'
const MIGRATIONS_DIR = new URL('../../backend/pb_migrations/', import.meta.url).pathname
// Die Snapshot-Migration ist die einzige, die ein vollständiges
// `const collections = [...]` enthält. Bei mehreren Dateien gewinnt die
// jüngste — Migrationen sind nach Zeitstempel benannt.
const candidates = readdirSync(MIGRATIONS_DIR)
.filter((f) => f.endsWith('.js'))
.sort()
.reverse()
const MARKER = 'const collections = '
for (const file of candidates) {
const source = readFileSync(join(MIGRATIONS_DIR, file), 'utf8')
const start = source.indexOf(MARKER)
if (start === -1) continue
// Vom öffnenden [ bis zur zugehörigen schließenden Klammer zählen —
// ein Regex käme an den verschachtelten Arrays der Feldoptionen nicht vorbei.
const from = start + MARKER.length
let depth = 0
for (let i = from; i < source.length; i++) {
if (source[i] === '[') depth++
else if (source[i] === ']') {
depth--
if (depth === 0) {
process.stdout.write(source.slice(from, i + 1))
process.exit(0)
}
}
}
}
console.error(
`Keine Migration mit "${MARKER.trim()}" in ${MIGRATIONS_DIR} gefunden.\n` +
`Ohne sie lassen sich die Typen nicht aus dem Repo erzeugen.`
)
process.exit(1)

View file

@ -6,11 +6,6 @@ import type PocketBase from 'pocketbase'
import type { RecordService } from 'pocketbase' import type { RecordService } from 'pocketbase'
export enum Collections { export enum Collections {
Authorigins = "_authOrigins",
Externalauths = "_externalAuths",
Mfas = "_mfas",
Otps = "_otps",
Superusers = "_superusers",
Events = "events", Events = "events",
Riders = "riders", Riders = "riders",
Runs = "runs", Runs = "runs",
@ -46,55 +41,6 @@ export type AuthSystemFields<T = unknown> = {
// Record types for each collection // Record types for each collection
export type AuthoriginsRecord = {
collectionRef: string
created?: IsoDateString
fingerprint: string
id: string
recordRef: string
updated?: IsoDateString
}
export type ExternalauthsRecord = {
collectionRef: string
created?: IsoDateString
id: string
provider: string
providerId: string
recordRef: string
updated?: IsoDateString
}
export type MfasRecord = {
collectionRef: string
created?: IsoDateString
id: string
method: string
recordRef: string
updated?: IsoDateString
}
export type OtpsRecord = {
collectionRef: string
created?: IsoDateString
id: string
password: string
recordRef: string
sentTo?: string
updated?: IsoDateString
}
export type SuperusersRecord = {
created?: IsoDateString
email: string
emailVisibility?: boolean
id: string
password: string
tokenKey: string
updated?: IsoDateString
verified?: boolean
}
export enum EventsStatusOptions { export enum EventsStatusOptions {
"draft" = "draft", "draft" = "draft",
"active" = "active", "active" = "active",
@ -182,11 +128,6 @@ export type UsersRecord = {
} }
// Response types include system fields and match responses from the PocketBase API // Response types include system fields and match responses from the PocketBase API
export type AuthoriginsResponse<Texpand = unknown> = Required<AuthoriginsRecord> & BaseSystemFields<Texpand>
export type ExternalauthsResponse<Texpand = unknown> = Required<ExternalauthsRecord> & BaseSystemFields<Texpand>
export type MfasResponse<Texpand = unknown> = Required<MfasRecord> & BaseSystemFields<Texpand>
export type OtpsResponse<Texpand = unknown> = Required<OtpsRecord> & BaseSystemFields<Texpand>
export type SuperusersResponse<Texpand = unknown> = Required<SuperusersRecord> & AuthSystemFields<Texpand>
export type EventsResponse<Texpand = unknown> = Required<EventsRecord> & BaseSystemFields<Texpand> export type EventsResponse<Texpand = unknown> = Required<EventsRecord> & BaseSystemFields<Texpand>
export type RidersResponse<Texpand = unknown> = Required<RidersRecord> & BaseSystemFields<Texpand> export type RidersResponse<Texpand = unknown> = Required<RidersRecord> & BaseSystemFields<Texpand>
export type RunsResponse<Texpand = unknown> = Required<RunsRecord> & BaseSystemFields<Texpand> export type RunsResponse<Texpand = unknown> = Required<RunsRecord> & BaseSystemFields<Texpand>
@ -197,11 +138,6 @@ export type UsersResponse<Texpand = unknown> = Required<UsersRecord> & AuthSyste
// Types containing all Records and Responses, useful for creating typing helper functions // Types containing all Records and Responses, useful for creating typing helper functions
export type CollectionRecords = { export type CollectionRecords = {
_authOrigins: AuthoriginsRecord
_externalAuths: ExternalauthsRecord
_mfas: MfasRecord
_otps: OtpsRecord
_superusers: SuperusersRecord
events: EventsRecord events: EventsRecord
riders: RidersRecord riders: RidersRecord
runs: RunsRecord runs: RunsRecord
@ -211,11 +147,6 @@ export type CollectionRecords = {
} }
export type CollectionResponses = { export type CollectionResponses = {
_authOrigins: AuthoriginsResponse
_externalAuths: ExternalauthsResponse
_mfas: MfasResponse
_otps: OtpsResponse
_superusers: SuperusersResponse
events: EventsResponse events: EventsResponse
riders: RidersResponse riders: RidersResponse
runs: RunsResponse runs: RunsResponse
@ -228,11 +159,6 @@ export type CollectionResponses = {
// https://github.com/pocketbase/js-sdk#specify-typescript-definitions // https://github.com/pocketbase/js-sdk#specify-typescript-definitions
export type TypedPocketBase = PocketBase & { export type TypedPocketBase = PocketBase & {
collection(idOrName: '_authOrigins'): RecordService<AuthoriginsResponse>
collection(idOrName: '_externalAuths'): RecordService<ExternalauthsResponse>
collection(idOrName: '_mfas'): RecordService<MfasResponse>
collection(idOrName: '_otps'): RecordService<OtpsResponse>
collection(idOrName: '_superusers'): RecordService<SuperusersResponse>
collection(idOrName: 'events'): RecordService<EventsResponse> collection(idOrName: 'events'): RecordService<EventsResponse>
collection(idOrName: 'riders'): RecordService<RidersResponse> collection(idOrName: 'riders'): RecordService<RidersResponse>
collection(idOrName: 'runs'): RecordService<RunsResponse> collection(idOrName: 'runs'): RecordService<RunsResponse>