@taimos/radball-digital-api 0.0.5 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/llm.md +786 -0
  2. package/package.json +1 -1
package/llm.md ADDED
@@ -0,0 +1,786 @@
1
+ # Radball Digital API - LLM Documentation
2
+
3
+ This document provides comprehensive guidance for LLMs to effectively use the `@taimos/radball-digital-api` library when generating code for Radball (cycle ball) sports management applications.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @taimos/radball-digital-api
9
+ ```
10
+
11
+ ## Core Imports
12
+
13
+ ```typescript
14
+ // Import GraphQL types
15
+ import {
16
+ Association,
17
+ Season,
18
+ League,
19
+ LeagueGroup,
20
+ Club,
21
+ Team,
22
+ Person,
23
+ Gym,
24
+ Game,
25
+ MatchDay,
26
+ SaveAssociationInput,
27
+ SaveClubInput,
28
+ SaveTeamInput,
29
+ Query,
30
+ Mutation,
31
+ Scalars
32
+ } from '@taimos/radball-digital-api';
33
+
34
+ // Import validation functions
35
+ import {
36
+ validateAssociation,
37
+ validateClub,
38
+ validateTeam,
39
+ validatePerson,
40
+ checkAssociation,
41
+ checkClub,
42
+ ValidationCheckResult
43
+ } from '@taimos/radball-digital-api';
44
+
45
+ // Import REST API types
46
+ import { paths, operations, components } from '@taimos/radball-digital-api';
47
+ ```
48
+
49
+ ## Type System Overview
50
+
51
+ ### Scalar Types
52
+
53
+ The library uses AWS AppSync scalar types:
54
+
55
+ ```typescript
56
+ type Scalars = {
57
+ ID: string;
58
+ String: string;
59
+ Boolean: boolean;
60
+ Int: number;
61
+ Float: number;
62
+ AWSDate: string; // Format: YYYY-MM-DD
63
+ AWSDateTime: string; // ISO 8601 format
64
+ AWSEmail: string; // Email address
65
+ AWSURL: string; // URL string
66
+ AWSTimestamp: number; // Unix timestamp
67
+ };
68
+ ```
69
+
70
+ ### Nullable Types
71
+
72
+ ```typescript
73
+ // Maybe<T> - Can be T, null, or undefined
74
+ type Maybe<T> = T | null | undefined;
75
+
76
+ // InputMaybe<T> - For input types
77
+ type InputMaybe<T> = T | null | undefined;
78
+ ```
79
+
80
+ ## Core Entity Types
81
+
82
+ ### Association
83
+
84
+ ```typescript
85
+ // Type definition
86
+ interface Association {
87
+ __typename?: 'Association';
88
+ createdAt: Scalars['AWSDateTime'];
89
+ id: Scalars['ID'];
90
+ name: Scalars['String'];
91
+ shortName?: Maybe<Scalars['String']>;
92
+ type: Scalars['String'];
93
+ updatedAt: Scalars['AWSDateTime'];
94
+ }
95
+
96
+ // Create/Update input
97
+ interface SaveAssociationInput {
98
+ id?: InputMaybe<Scalars['ID']>;
99
+ name: Scalars['String'];
100
+ shortName?: InputMaybe<Scalars['String']>;
101
+ type: Scalars['String'];
102
+ }
103
+
104
+ // Usage example
105
+ const newAssociation: SaveAssociationInput = {
106
+ name: 'Deutscher Radball Verband',
107
+ shortName: 'DRV',
108
+ type: 'NATIONAL'
109
+ };
110
+
111
+ // Validation
112
+ try {
113
+ validateAssociation(newAssociation);
114
+ // Association is valid, proceed with save
115
+ } catch (error) {
116
+ console.error('Validation failed:', error);
117
+ }
118
+ ```
119
+
120
+ ### Club
121
+
122
+ ```typescript
123
+ // Type definition
124
+ interface Club {
125
+ __typename?: 'Club';
126
+ associationId: Scalars['ID'];
127
+ createdAt: Scalars['AWSDateTime'];
128
+ externalId?: Maybe<Scalars['String']>;
129
+ id: Scalars['ID'];
130
+ legacyId?: Maybe<Scalars['String']>;
131
+ name: Scalars['String'];
132
+ shortName?: Maybe<Scalars['String']>;
133
+ updatedAt: Scalars['AWSDateTime'];
134
+ }
135
+
136
+ // Create/Update input
137
+ interface SaveClubInput {
138
+ associationId: Scalars['ID'];
139
+ externalId?: InputMaybe<Scalars['String']>;
140
+ id?: InputMaybe<Scalars['ID']>;
141
+ legacyId?: InputMaybe<Scalars['String']>;
142
+ name: Scalars['String'];
143
+ shortName?: InputMaybe<Scalars['String']>;
144
+ }
145
+
146
+ // Usage example
147
+ const newClub: SaveClubInput = {
148
+ associationId: 'association-123',
149
+ name: 'RV Musterhausen',
150
+ shortName: 'RVM',
151
+ externalId: 'ext-456'
152
+ };
153
+ ```
154
+
155
+ ### Team
156
+
157
+ ```typescript
158
+ // Type definition
159
+ interface Team {
160
+ __typename?: 'Team';
161
+ ageClass?: Maybe<Scalars['String']>;
162
+ clubId: Scalars['ID'];
163
+ createdAt: Scalars['AWSDateTime'];
164
+ discipline: Scalars['String'];
165
+ id: Scalars['ID'];
166
+ legacyNumber?: Maybe<Scalars['Int']>;
167
+ name: Scalars['String'];
168
+ secondaryClubId?: Maybe<Scalars['ID']>;
169
+ status?: Maybe<Scalars['String']>;
170
+ updatedAt: Scalars['AWSDateTime'];
171
+ }
172
+
173
+ // Create/Update input
174
+ interface SaveTeamInput {
175
+ ageClass?: InputMaybe<Scalars['String']>;
176
+ clubId: Scalars['ID'];
177
+ discipline: Scalars['String'];
178
+ id?: InputMaybe<Scalars['ID']>;
179
+ legacyNumber?: InputMaybe<Scalars['Int']>;
180
+ name: Scalars['String'];
181
+ secondaryClubId?: InputMaybe<Scalars['ID']>;
182
+ status?: InputMaybe<Scalars['String']>;
183
+ }
184
+
185
+ // Usage example
186
+ const newTeam: SaveTeamInput = {
187
+ clubId: 'club-123',
188
+ name: 'RV Musterhausen I',
189
+ discipline: 'RADBALL',
190
+ ageClass: 'ELITE',
191
+ status: 'ACTIVE'
192
+ };
193
+ ```
194
+
195
+ ### Person
196
+
197
+ ```typescript
198
+ // Type definition
199
+ interface Person {
200
+ __typename?: 'Person';
201
+ address?: Maybe<Address>;
202
+ birthdate?: Maybe<Scalars['AWSDate']>;
203
+ clubId?: Maybe<Scalars['ID']>;
204
+ createdAt: Scalars['AWSDateTime'];
205
+ email?: Maybe<Scalars['AWSEmail']>;
206
+ firstname?: Maybe<Scalars['String']>;
207
+ gender?: Maybe<Scalars['String']>;
208
+ id: Scalars['ID'];
209
+ lastname?: Maybe<Scalars['String']>;
210
+ legacyId?: Maybe<Scalars['String']>;
211
+ phone?: Maybe<Scalars['String']>;
212
+ roles?: Maybe<Array<Scalars['String']>>;
213
+ updatedAt: Scalars['AWSDateTime'];
214
+ }
215
+
216
+ // Address sub-type
217
+ interface Address {
218
+ __typename?: 'Address';
219
+ city?: Maybe<Scalars['String']>;
220
+ country?: Maybe<Scalars['String']>;
221
+ number?: Maybe<Scalars['String']>;
222
+ street?: Maybe<Scalars['String']>;
223
+ zip?: Maybe<Scalars['String']>;
224
+ }
225
+
226
+ // Create/Update input
227
+ interface SavePersonInput {
228
+ address?: InputMaybe<AddressInput>;
229
+ birthdate?: InputMaybe<Scalars['AWSDate']>;
230
+ clubId?: InputMaybe<Scalars['ID']>;
231
+ email?: InputMaybe<Scalars['AWSEmail']>;
232
+ firstname?: InputMaybe<Scalars['String']>;
233
+ gender?: InputMaybe<Scalars['String']>;
234
+ id?: InputMaybe<Scalars['ID']>;
235
+ lastname?: InputMaybe<Scalars['String']>;
236
+ legacyId?: InputMaybe<Scalars['String']>;
237
+ phone?: InputMaybe<Scalars['String']>;
238
+ roles?: InputMaybe<Array<Scalars['String']>>;
239
+ }
240
+
241
+ // Usage example
242
+ const newPerson: SavePersonInput = {
243
+ firstname: 'Max',
244
+ lastname: 'Mustermann',
245
+ email: 'max@example.com',
246
+ birthdate: '1990-01-15',
247
+ gender: 'MALE',
248
+ roles: ['PLAYER'],
249
+ address: {
250
+ street: 'Hauptstraße',
251
+ number: '123',
252
+ zip: '12345',
253
+ city: 'Musterhausen',
254
+ country: 'DE'
255
+ }
256
+ };
257
+ ```
258
+
259
+ ### Season
260
+
261
+ ```typescript
262
+ // Type definition
263
+ interface Season {
264
+ __typename?: 'Season';
265
+ associationId: Scalars['ID'];
266
+ createdAt: Scalars['AWSDateTime'];
267
+ endDate: Scalars['AWSDate'];
268
+ id: Scalars['ID'];
269
+ minimumAgePlayerDate?: Maybe<Scalars['AWSDate']>;
270
+ minimumAgeStaffDate?: Maybe<Scalars['AWSDate']>;
271
+ name: Scalars['String'];
272
+ registrationEndDate?: Maybe<Scalars['AWSDate']>;
273
+ registrationStartDate?: Maybe<Scalars['AWSDate']>;
274
+ startDate: Scalars['AWSDate'];
275
+ updatedAt: Scalars['AWSDateTime'];
276
+ }
277
+
278
+ // Create/Update input
279
+ interface SaveSeasonInput {
280
+ associationId: Scalars['ID'];
281
+ endDate: Scalars['AWSDate'];
282
+ id?: InputMaybe<Scalars['ID']>;
283
+ minimumAgePlayerDate?: InputMaybe<Scalars['AWSDate']>;
284
+ minimumAgeStaffDate?: InputMaybe<Scalars['AWSDate']>;
285
+ name: Scalars['String'];
286
+ registrationEndDate?: InputMaybe<Scalars['AWSDate']>;
287
+ registrationStartDate?: InputMaybe<Scalars['AWSDate']>;
288
+ startDate: Scalars['AWSDate'];
289
+ }
290
+
291
+ // Usage example
292
+ const newSeason: SaveSeasonInput = {
293
+ associationId: 'association-123',
294
+ name: 'Saison 2024/2025',
295
+ startDate: '2024-09-01',
296
+ endDate: '2025-06-30',
297
+ registrationStartDate: '2024-07-01',
298
+ registrationEndDate: '2024-08-15',
299
+ minimumAgePlayerDate: '2009-01-01'
300
+ };
301
+ ```
302
+
303
+ ### League and LeagueGroup
304
+
305
+ ```typescript
306
+ // League type
307
+ interface League {
308
+ __typename?: 'League';
309
+ associationId: Scalars['ID'];
310
+ createdAt: Scalars['AWSDateTime'];
311
+ discipline: Scalars['String'];
312
+ id: Scalars['ID'];
313
+ matchDayTemplate?: Maybe<Array<MatchDayTemplateEntry>>;
314
+ matchdayBlockedDates?: Maybe<Array<Scalars['AWSDate']>>;
315
+ matchdayReferenceDate?: Maybe<Scalars['AWSDate']>;
316
+ minPlayerAge?: Maybe<Scalars['Int']>;
317
+ name: Scalars['String'];
318
+ order?: Maybe<Scalars['Int']>;
319
+ seasonId: Scalars['ID'];
320
+ updatedAt: Scalars['AWSDateTime'];
321
+ }
322
+
323
+ // LeagueGroup type
324
+ interface LeagueGroup {
325
+ __typename?: 'LeagueGroup';
326
+ createdAt: Scalars['AWSDateTime'];
327
+ id: Scalars['ID'];
328
+ leader?: Maybe<Scalars['ID']>;
329
+ leagueId: Scalars['ID'];
330
+ name: Scalars['String'];
331
+ teams?: Maybe<Array<Scalars['ID']>>;
332
+ updatedAt: Scalars['AWSDateTime'];
333
+ }
334
+
335
+ // Create inputs
336
+ interface SaveLeagueInput {
337
+ associationId: Scalars['ID'];
338
+ discipline: Scalars['String'];
339
+ id?: InputMaybe<Scalars['ID']>;
340
+ matchDayTemplate?: InputMaybe<Array<MatchDayTemplateEntryInput>>;
341
+ matchdayBlockedDates?: InputMaybe<Array<Scalars['AWSDate']>>;
342
+ matchdayReferenceDate?: InputMaybe<Scalars['AWSDate']>;
343
+ minPlayerAge?: InputMaybe<Scalars['Int']>;
344
+ name: Scalars['String'];
345
+ order?: InputMaybe<Scalars['Int']>;
346
+ seasonId: Scalars['ID'];
347
+ }
348
+
349
+ interface SaveLeagueGroupInput {
350
+ id?: InputMaybe<Scalars['ID']>;
351
+ leader?: InputMaybe<Scalars['ID']>;
352
+ leagueId: Scalars['ID'];
353
+ name: Scalars['String'];
354
+ teams?: InputMaybe<Array<Scalars['ID']>>;
355
+ }
356
+ ```
357
+
358
+ ### Game
359
+
360
+ ```typescript
361
+ // Game type
362
+ interface Game {
363
+ __typename?: 'Game';
364
+ awayScore?: Maybe<Scalars['Int']>;
365
+ awayTeamId: Scalars['ID'];
366
+ createdAt: Scalars['AWSDateTime'];
367
+ gameNumber: Scalars['Int'];
368
+ homeScore?: Maybe<Scalars['Int']>;
369
+ homeTeamId: Scalars['ID'];
370
+ id: Scalars['ID'];
371
+ matchdayId: Scalars['ID'];
372
+ refereeId?: Maybe<Scalars['ID']>;
373
+ updatedAt: Scalars['AWSDateTime'];
374
+ }
375
+
376
+ // Add game input
377
+ interface AddGameInput {
378
+ awayTeamId: Scalars['ID'];
379
+ gameNumber: Scalars['Int'];
380
+ homeTeamId: Scalars['ID'];
381
+ matchdayId: Scalars['ID'];
382
+ }
383
+ ```
384
+
385
+ ## Validation Functions
386
+
387
+ ### Using Check Functions (Non-throwing)
388
+
389
+ ```typescript
390
+ import { checkClub, checkPerson, ValidationCheckResult } from '@taimos/radball-digital-api';
391
+
392
+ // Check functions return ValidationCheckResult
393
+ const clubData: SaveClubInput = {
394
+ associationId: 'assoc-123',
395
+ name: '', // Invalid - empty name
396
+ shortName: 'ABC'
397
+ };
398
+
399
+ const result = checkClub(clubData);
400
+ if (result.hasErrors()) {
401
+ console.error('Validation errors:', result.errors);
402
+ // Output: { name: ['Name ist erforderlich'] }
403
+ }
404
+
405
+ // Check with warnings
406
+ const personData: SavePersonInput = {
407
+ firstname: 'John',
408
+ lastname: 'Doe',
409
+ email: 'invalid-email' // Invalid email format
410
+ };
411
+
412
+ const personResult = checkPerson(personData);
413
+ if (personResult.hasErrors() || personResult.hasWarnings()) {
414
+ console.log('Errors:', personResult.errors);
415
+ console.log('Warnings:', personResult.warnings);
416
+ }
417
+ ```
418
+
419
+ ### Using Validate Functions (Throwing)
420
+
421
+ ```typescript
422
+ import { validateTeam, validateSeason } from '@taimos/radball-digital-api';
423
+
424
+ // Validate functions throw on error
425
+ const teamData: SaveTeamInput = {
426
+ clubId: 'club-123',
427
+ name: 'Team A',
428
+ discipline: 'RADBALL',
429
+ status: 'ACTIVE'
430
+ };
431
+
432
+ try {
433
+ validateTeam(teamData);
434
+ // If we get here, the team is valid
435
+ console.log('Team is valid');
436
+ } catch (error) {
437
+ console.error('Validation failed:', error.message);
438
+ }
439
+
440
+ // Season validation with date checks
441
+ const seasonData: SaveSeasonInput = {
442
+ associationId: 'assoc-123',
443
+ name: 'Season 2024',
444
+ startDate: '2024-09-01',
445
+ endDate: '2024-06-01', // Invalid - end before start
446
+ registrationStartDate: '2024-07-01',
447
+ registrationEndDate: '2024-08-15'
448
+ };
449
+
450
+ try {
451
+ validateSeason(seasonData);
452
+ } catch (error) {
453
+ // Will throw: "Enddatum muss nach dem Startdatum liegen"
454
+ console.error(error.message);
455
+ }
456
+ ```
457
+
458
+ ### Custom Validation Workflow
459
+
460
+ ```typescript
461
+ import { ValidationCheckResult } from '@taimos/radball-digital-api';
462
+
463
+ // Create custom validation workflow
464
+ function validateGameData(game: AddGameInput): ValidationCheckResult {
465
+ const result = new ValidationCheckResult();
466
+
467
+ // Basic validation
468
+ if (!game.homeTeamId) {
469
+ result.addError('homeTeamId', 'Heimmannschaft ist erforderlich');
470
+ }
471
+
472
+ if (!game.awayTeamId) {
473
+ result.addError('awayTeamId', 'Gastmannschaft ist erforderlich');
474
+ }
475
+
476
+ // Business logic validation
477
+ if (game.homeTeamId === game.awayTeamId) {
478
+ result.addError('teams', 'Heim- und Gastmannschaft müssen unterschiedlich sein');
479
+ }
480
+
481
+ if (game.gameNumber < 1) {
482
+ result.addError('gameNumber', 'Spielnummer muss größer als 0 sein');
483
+ }
484
+
485
+ return result;
486
+ }
487
+
488
+ // Usage
489
+ const gameInput: AddGameInput = {
490
+ homeTeamId: 'team-1',
491
+ awayTeamId: 'team-1', // Same as home
492
+ gameNumber: 0, // Invalid
493
+ matchdayId: 'matchday-123'
494
+ };
495
+
496
+ const validation = validateGameData(gameInput);
497
+ if (validation.hasErrors()) {
498
+ // Throw aggregated error
499
+ validation.throwIfErrors();
500
+ }
501
+ ```
502
+
503
+ ## GraphQL Operations
504
+
505
+ ### Query Examples
506
+
507
+ ```typescript
508
+ // Type-safe query arguments
509
+ import { QueryGetClubByIdArgs, QueryListClubsArgs } from '@taimos/radball-digital-api';
510
+
511
+ // Single club query
512
+ const getClubArgs: QueryGetClubByIdArgs = {
513
+ id: 'club-123'
514
+ };
515
+
516
+ // List clubs with pagination
517
+ const listClubsArgs: QueryListClubsArgs = {
518
+ associationId: 'assoc-123',
519
+ limit: 20,
520
+ nextToken: undefined // For pagination
521
+ };
522
+
523
+ // Complex query with filters
524
+ const listTeamsArgs = {
525
+ clubId: 'club-123',
526
+ discipline: 'RADBALL',
527
+ status: 'ACTIVE'
528
+ };
529
+ ```
530
+
531
+ ### Mutation Examples
532
+
533
+ ```typescript
534
+ // Type-safe mutation arguments
535
+ import {
536
+ MutationAddClubArgs,
537
+ MutationRegisterTeamArgs,
538
+ MutationAddGameArgs
539
+ } from '@taimos/radball-digital-api';
540
+
541
+ // Add club mutation
542
+ const addClubArgs: MutationAddClubArgs = {
543
+ input: {
544
+ associationId: 'assoc-123',
545
+ name: 'New Club',
546
+ shortName: 'NC'
547
+ }
548
+ };
549
+
550
+ // Register team for season
551
+ const registerTeamArgs: MutationRegisterTeamArgs = {
552
+ input: {
553
+ teamId: 'team-123',
554
+ leagueGroupId: 'group-456',
555
+ players: ['person-1', 'person-2'],
556
+ preferredMatchdayDates: [
557
+ {
558
+ date: '2024-10-15',
559
+ status: 'PREFERRED'
560
+ },
561
+ {
562
+ date: '2024-10-22',
563
+ status: 'NOT_POSSIBLE'
564
+ }
565
+ ]
566
+ }
567
+ };
568
+
569
+ // Add game to matchday
570
+ const addGameArgs: MutationAddGameArgs = {
571
+ input: {
572
+ matchdayId: 'matchday-123',
573
+ homeTeamId: 'team-1',
574
+ awayTeamId: 'team-2',
575
+ gameNumber: 1
576
+ }
577
+ };
578
+ ```
579
+
580
+ ## REST API Usage
581
+
582
+ ### Import Endpoints
583
+
584
+ ```typescript
585
+ import { paths, operations } from '@taimos/radball-digital-api';
586
+
587
+ // Type definitions for import endpoints
588
+ type ClubImportRequest = paths['/import/clubs']['post']['requestBody']['content']['text/plain'];
589
+ type ClubImportResponse = paths['/import/clubs']['post']['responses']['200']['content']['application/json'];
590
+
591
+ type GymImportRequest = paths['/import/gyms']['post']['requestBody']['content']['text/plain'];
592
+ type PersonImportRequest = paths['/import/persons']['post']['requestBody']['content']['text/csv'];
593
+
594
+ // Example: Import clubs from fixed-width text
595
+ const clubsText = `
596
+ 001 RV Musterhausen RVM
597
+ 002 SC Example SCE
598
+ `;
599
+
600
+ // Example: Import persons from CSV
601
+ const personsCsv = `
602
+ firstname,lastname,email,birthdate,clubId
603
+ Max,Mustermann,max@example.com,1990-01-15,club-123
604
+ Anna,Schmidt,anna@example.com,1992-03-20,club-456
605
+ `;
606
+ ```
607
+
608
+ ## Common Patterns
609
+
610
+ ### Working with Optional Fields
611
+
612
+ ```typescript
613
+ // Using Maybe types
614
+ function getPersonFullName(person: Person): string {
615
+ const first = person.firstname ?? '';
616
+ const last = person.lastname ?? '';
617
+ return `${first} ${last}`.trim() || 'Unknown';
618
+ }
619
+
620
+ // Handling optional addresses
621
+ function formatAddress(address: Maybe<Address>): string {
622
+ if (!address) return 'No address';
623
+
624
+ const parts = [
625
+ address.street && address.number ? `${address.street} ${address.number}` : '',
626
+ address.zip && address.city ? `${address.zip} ${address.city}` : '',
627
+ address.country
628
+ ].filter(Boolean);
629
+
630
+ return parts.join(', ') || 'Incomplete address';
631
+ }
632
+ ```
633
+
634
+ ### Date Handling
635
+
636
+ ```typescript
637
+ // Working with AWSDate (YYYY-MM-DD format)
638
+ function isSeasonActive(season: Season): boolean {
639
+ const today = new Date().toISOString().split('T')[0]; // Get YYYY-MM-DD
640
+ return season.startDate <= today && today <= season.endDate;
641
+ }
642
+
643
+ // Registration period check
644
+ function isRegistrationOpen(season: Season): boolean {
645
+ if (!season.registrationStartDate || !season.registrationEndDate) {
646
+ return false;
647
+ }
648
+
649
+ const today = new Date().toISOString().split('T')[0];
650
+ return season.registrationStartDate <= today && today <= season.registrationEndDate;
651
+ }
652
+
653
+ // Age validation
654
+ function canPlayerRegister(person: Person, season: Season): boolean {
655
+ if (!person.birthdate || !season.minimumAgePlayerDate) {
656
+ return true; // No age restriction
657
+ }
658
+
659
+ return person.birthdate <= season.minimumAgePlayerDate;
660
+ }
661
+ ```
662
+
663
+ ### Error Handling
664
+
665
+ ```typescript
666
+ import { validateClub, ValidationCheckResult } from '@taimos/radball-digital-api';
667
+
668
+ // Comprehensive error handling
669
+ async function createClub(input: SaveClubInput): Promise<Club | { errors: Record<string, string[]> }> {
670
+ try {
671
+ // Validate input
672
+ validateClub(input);
673
+
674
+ // Make API call (pseudo-code)
675
+ const result = await apiClient.mutation({
676
+ addClub: {
677
+ input
678
+ }
679
+ });
680
+
681
+ return result.addClub;
682
+ } catch (error) {
683
+ if (error.message.includes('Validierung fehlgeschlagen')) {
684
+ // Parse validation errors
685
+ const errors = parseValidationErrors(error.message);
686
+ return { errors };
687
+ }
688
+
689
+ // Re-throw other errors
690
+ throw error;
691
+ }
692
+ }
693
+
694
+ function parseValidationErrors(message: string): Record<string, string[]> {
695
+ // Extract field errors from German validation messages
696
+ const errors: Record<string, string[]> = {};
697
+ const lines = message.split('\n');
698
+
699
+ for (const line of lines) {
700
+ const match = line.match(/^(\w+): (.+)$/);
701
+ if (match) {
702
+ const [, field, error] = match;
703
+ if (!errors[field]) errors[field] = [];
704
+ errors[field].push(error);
705
+ }
706
+ }
707
+
708
+ return errors;
709
+ }
710
+ ```
711
+
712
+ ### Batch Operations
713
+
714
+ ```typescript
715
+ // Register multiple teams
716
+ async function registerTeamsForSeason(
717
+ seasonId: string,
718
+ registrations: Array<{ teamId: string; leagueGroupId: string; players: string[] }>
719
+ ): Promise<void> {
720
+ // Validate all registrations first
721
+ const validationResults = registrations.map(reg => {
722
+ const result = new ValidationCheckResult();
723
+
724
+ if (!reg.teamId) result.addError('teamId', 'Team ist erforderlich');
725
+ if (!reg.leagueGroupId) result.addError('leagueGroupId', 'Liga-Gruppe ist erforderlich');
726
+ if (!reg.players || reg.players.length < 2) {
727
+ result.addError('players', 'Mindestens 2 Spieler erforderlich');
728
+ }
729
+
730
+ return { registration: reg, validation: result };
731
+ });
732
+
733
+ // Check for validation errors
734
+ const errors = validationResults.filter(r => r.validation.hasErrors());
735
+ if (errors.length > 0) {
736
+ throw new Error(`${errors.length} Registrierungen sind ungültig`);
737
+ }
738
+
739
+ // Process valid registrations
740
+ for (const { registration } of validationResults) {
741
+ await apiClient.mutation({
742
+ registerTeam: {
743
+ input: {
744
+ teamId: registration.teamId,
745
+ leagueGroupId: registration.leagueGroupId,
746
+ players: registration.players
747
+ }
748
+ }
749
+ });
750
+ }
751
+ }
752
+ ```
753
+
754
+ ## Best Practices
755
+
756
+ 1. **Always validate input data** before sending to the API
757
+ 2. **Use TypeScript types** for type safety
758
+ 3. **Handle nullable fields** appropriately with Maybe types
759
+ 4. **Check date ranges** for seasons and registration periods
760
+ 5. **Validate business rules** (e.g., team can't play against itself)
761
+ 6. **Use German error messages** as the system is designed for German users
762
+ 7. **Batch operations** when possible to reduce API calls
763
+ 8. **Cache frequently accessed data** like associations and clubs
764
+
765
+ ## Common Validation Rules
766
+
767
+ - **Names**: Required, max 255 characters
768
+ - **Short names**: Optional, max 50 characters
769
+ - **Emails**: Must be valid email format
770
+ - **Dates**: Must be in YYYY-MM-DD format
771
+ - **Season dates**: End date must be after start date
772
+ - **Registration dates**: Must be within season dates
773
+ - **Age restrictions**: Players must meet minimum age requirements
774
+ - **Team composition**: Minimum 2 players required
775
+ - **Unique constraints**: External IDs and legacy IDs must be unique
776
+
777
+ ## Error Messages (German)
778
+
779
+ All validation error messages are in German. Common messages:
780
+
781
+ - "Name ist erforderlich" - Name is required
782
+ - "Enddatum muss nach dem Startdatum liegen" - End date must be after start date
783
+ - "Ungültiges E-Mail-Format" - Invalid email format
784
+ - "Mindestens 2 Spieler erforderlich" - At least 2 players required
785
+ - "Verein ist erforderlich" - Club is required
786
+ - "Saison ist erforderlich" - Season is required
package/package.json CHANGED
@@ -73,7 +73,7 @@
73
73
  "publishConfig": {
74
74
  "access": "public"
75
75
  },
76
- "version": "0.0.5",
76
+ "version": "0.0.6",
77
77
  "jest": {
78
78
  "coverageProvider": "v8",
79
79
  "moduleNameMapper": {