@xrmforge/typegen 0.8.3 → 0.8.5

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 (43) hide show
  1. package/LICENSE +21 -21
  2. package/MIGRATION.md +194 -194
  3. package/dist/index.js +4 -4
  4. package/dist/index.js.map +1 -1
  5. package/docs/architecture/00-README.md +26 -26
  6. package/docs/architecture/01-executive-summary.md +11 -11
  7. package/docs/architecture/02-packages.md +110 -110
  8. package/docs/architecture/03-generated-types.md +172 -172
  9. package/docs/architecture/04-cli.md +58 -58
  10. package/docs/architecture/05-build.md +50 -50
  11. package/docs/architecture/06-incremental.md +42 -42
  12. package/docs/architecture/07-http-client.md +59 -59
  13. package/docs/architecture/08-authentication.md +18 -18
  14. package/docs/architecture/09-testing.md +55 -55
  15. package/docs/architecture/10-eslint-plugin.md +82 -82
  16. package/docs/architecture/11-agent-md.md +38 -38
  17. package/docs/architecture/12-xrm-pitfalls.md +14 -14
  18. package/docs/architecture/13-helpers.md +50 -50
  19. package/docs/architecture/14-showcases.md +21 -21
  20. package/docs/architecture/15-ci-cd.md +49 -49
  21. package/docs/architecture/16-technical-debt.md +17 -17
  22. package/docs/architecture/17-roadmap.md +25 -25
  23. package/docs/architecture/18-design-principles.md +22 -22
  24. package/docs/architektur/00-README.md +26 -26
  25. package/docs/architektur/01-zusammenfassung.md +11 -11
  26. package/docs/architektur/02-packages.md +110 -110
  27. package/docs/architektur/03-generierte-typen.md +172 -172
  28. package/docs/architektur/04-cli.md +58 -58
  29. package/docs/architektur/05-build.md +50 -50
  30. package/docs/architektur/06-inkrementell.md +42 -42
  31. package/docs/architektur/07-http-client.md +59 -59
  32. package/docs/architektur/08-authentifizierung.md +18 -18
  33. package/docs/architektur/09-testing.md +55 -55
  34. package/docs/architektur/10-eslint-plugin.md +82 -82
  35. package/docs/architektur/11-agent-md.md +38 -38
  36. package/docs/architektur/12-xrm-fallstricke.md +14 -14
  37. package/docs/architektur/13-helpers.md +50 -50
  38. package/docs/architektur/14-showcases.md +21 -21
  39. package/docs/architektur/15-ci-cd.md +49 -49
  40. package/docs/architektur/16-technische-schulden.md +17 -17
  41. package/docs/architektur/17-roadmap.md +25 -25
  42. package/docs/architektur/18-designprinzipien.md +22 -22
  43. package/package.json +1 -1
@@ -1,58 +1,58 @@
1
- # 4. CLI-Befehle
2
-
3
- ## 4.1 `xrmforge generate`
4
-
5
- Generiert TypeScript-Deklarationen aus einer Dataverse-Umgebung.
6
-
7
- | Flag | Typ | Standard | Beschreibung |
8
- |------|-----|----------|--------------|
9
- | `--url <url>` | string | erforderlich | Dataverse-Umgebungs-URL |
10
- | `--auth <method>` | string | erforderlich | Authentifizierungsmethode: client-credentials, interactive, device-code, token |
11
- | `--tenant-id <id>` | string | variiert | Azure AD Tenant-ID |
12
- | `--client-id <id>` | string | variiert | Azure AD Application-ID |
13
- | `--client-secret <s>` | string | variiert | Client Secret (nur client-credentials) |
14
- | `--token <token>` | string | variiert | Vorab erworbenes Bearer-Token (nur Token-Auth) |
15
- | `--entities <list>` | string | - | Kommagetrennte logische Entitätsnamen |
16
- | `--solutions <list>` | string | - | Kommagetrennte eindeutige Lösungsnamen |
17
- | `--output <dir>` | string | ./generated | Ausgabeverzeichnis |
18
- | `--label-language <n>` | string | 1033 | Primäre Label-Sprache (LCID) |
19
- | `--secondary-language <n>` | string | - | Sekundäre Label-Sprache für JSDoc |
20
- | `--no-forms` | flag | - | Formular-Interface-Generierung überspringen |
21
- | `--no-optionsets` | flag | - | OptionSet-Enum-Generierung überspringen |
22
- | `--actions` | flag | false | Custom-API-Executors generieren |
23
- | `--actions-filter <prefix>` | string | - | Custom APIs nach Uniquename-Präfix filtern |
24
- | `--cache` | flag | false | Metadaten-Caching für inkrementelle Generierung aktivieren |
25
- | `--no-cache` | flag | - | Vollständige Metadaten-Aktualisierung erzwingen |
26
- | `--cache-dir <dir>` | string | .xrmforge/cache | Cache-Verzeichnis |
27
- | `-v, --verbose` | flag | false | Debug-Logging |
28
-
29
- Mindestens eines von `--entities` oder `--solutions` ist erforderlich.
30
-
31
- ## 4.2 `xrmforge build`
32
-
33
- Baut WebResources als IIFE-Bundles mit esbuild (über @xrmforge/devkit).
34
-
35
- | Flag | Typ | Standard | Beschreibung |
36
- |------|-----|----------|--------------|
37
- | `--watch` | flag | false | Watch-Modus mit inkrementellen Rebuilds |
38
- | `--minify` | flag | aus Konfiguration | Minifizierungseinstellung überschreiben |
39
- | `--no-sourcemap` | flag | - | Source Maps deaktivieren |
40
- | `--out-dir <dir>` | string | aus Konfiguration | Ausgabeverzeichnis überschreiben |
41
- | `-v, --verbose` | flag | false | Fehlerstacks anzeigen |
42
-
43
- Liest die Konfiguration aus `xrmforge.config.json`.
44
-
45
- ## 4.3 `xrmforge init`
46
-
47
- Erstellt ein neues D365-Formularskript-Projekt.
48
-
49
- | Flag | Typ | Standard | Beschreibung |
50
- |------|-----|----------|--------------|
51
- | `[dir]` | positional | . | Zielverzeichnis |
52
- | `--name <name>` | string | Verzeichnisname | Projektname für package.json |
53
- | `--prefix <prefix>` | string | contoso | Publisher-Präfix |
54
- | `--namespace <ns>` | string | PascalCase(prefix) | Basis-Namespace für Skripte |
55
- | `--skip-install` | flag | false | npm install überspringen |
56
- | `--force` | flag | false | Nicht-leere Verzeichnisse erlauben |
57
-
58
- Generiert 11 Dateien: package.json, tsconfig.json, xrmforge.config.json, vitest.config.ts, .gitignore, AGENT.md, example-form.ts, example-form.test.ts, generated/.gitkeep, GitHub Actions CI, Azure DevOps Pipeline.
1
+ # 4. CLI-Befehle
2
+
3
+ ## 4.1 `xrmforge generate`
4
+
5
+ Generiert TypeScript-Deklarationen aus einer Dataverse-Umgebung.
6
+
7
+ | Flag | Typ | Standard | Beschreibung |
8
+ |------|-----|----------|--------------|
9
+ | `--url <url>` | string | erforderlich | Dataverse-Umgebungs-URL |
10
+ | `--auth <method>` | string | erforderlich | Authentifizierungsmethode: client-credentials, interactive, device-code, token |
11
+ | `--tenant-id <id>` | string | variiert | Azure AD Tenant-ID |
12
+ | `--client-id <id>` | string | variiert | Azure AD Application-ID |
13
+ | `--client-secret <s>` | string | variiert | Client Secret (nur client-credentials) |
14
+ | `--token <token>` | string | variiert | Vorab erworbenes Bearer-Token (nur Token-Auth) |
15
+ | `--entities <list>` | string | - | Kommagetrennte logische Entitätsnamen |
16
+ | `--solutions <list>` | string | - | Kommagetrennte eindeutige Lösungsnamen |
17
+ | `--output <dir>` | string | ./generated | Ausgabeverzeichnis |
18
+ | `--label-language <n>` | string | 1033 | Primäre Label-Sprache (LCID) |
19
+ | `--secondary-language <n>` | string | - | Sekundäre Label-Sprache für JSDoc |
20
+ | `--no-forms` | flag | - | Formular-Interface-Generierung überspringen |
21
+ | `--no-optionsets` | flag | - | OptionSet-Enum-Generierung überspringen |
22
+ | `--actions` | flag | false | Custom-API-Executors generieren |
23
+ | `--actions-filter <prefix>` | string | - | Custom APIs nach Uniquename-Präfix filtern |
24
+ | `--cache` | flag | false | Metadaten-Caching für inkrementelle Generierung aktivieren |
25
+ | `--no-cache` | flag | - | Vollständige Metadaten-Aktualisierung erzwingen |
26
+ | `--cache-dir <dir>` | string | .xrmforge/cache | Cache-Verzeichnis |
27
+ | `-v, --verbose` | flag | false | Debug-Logging |
28
+
29
+ Mindestens eines von `--entities` oder `--solutions` ist erforderlich.
30
+
31
+ ## 4.2 `xrmforge build`
32
+
33
+ Baut WebResources als IIFE-Bundles mit esbuild (über @xrmforge/devkit).
34
+
35
+ | Flag | Typ | Standard | Beschreibung |
36
+ |------|-----|----------|--------------|
37
+ | `--watch` | flag | false | Watch-Modus mit inkrementellen Rebuilds |
38
+ | `--minify` | flag | aus Konfiguration | Minifizierungseinstellung überschreiben |
39
+ | `--no-sourcemap` | flag | - | Source Maps deaktivieren |
40
+ | `--out-dir <dir>` | string | aus Konfiguration | Ausgabeverzeichnis überschreiben |
41
+ | `-v, --verbose` | flag | false | Fehlerstacks anzeigen |
42
+
43
+ Liest die Konfiguration aus `xrmforge.config.json`.
44
+
45
+ ## 4.3 `xrmforge init`
46
+
47
+ Erstellt ein neues D365-Formularskript-Projekt.
48
+
49
+ | Flag | Typ | Standard | Beschreibung |
50
+ |------|-----|----------|--------------|
51
+ | `[dir]` | positional | . | Zielverzeichnis |
52
+ | `--name <name>` | string | Verzeichnisname | Projektname für package.json |
53
+ | `--prefix <prefix>` | string | contoso | Publisher-Präfix |
54
+ | `--namespace <ns>` | string | PascalCase(prefix) | Basis-Namespace für Skripte |
55
+ | `--skip-install` | flag | false | npm install überspringen |
56
+ | `--force` | flag | false | Nicht-leere Verzeichnisse erlauben |
57
+
58
+ Generiert 11 Dateien: package.json, tsconfig.json, xrmforge.config.json, vitest.config.ts, .gitignore, AGENT.md, example-form.ts, example-form.test.ts, generated/.gitkeep, GitHub Actions CI, Azure DevOps Pipeline.
@@ -1,50 +1,50 @@
1
- # 5. Build-Architektur
2
-
3
- ## 5.1 esbuild IIFE-Bundles
4
-
5
- XrmForge verwendet esbuild, um IIFE-Bundles (Immediately Invoked Function Expression) für Dynamics 365 zu erstellen. D365 erfordert, dass Skripte als namespace.function registriert werden (z.B. `Contoso.Account.onLoad`).
6
-
7
- **esbuild-Konfiguration pro Eintrag:**
8
- ```
9
- format: 'iife'
10
- bundle: true
11
- globalName: entry.namespace // z.B. 'Contoso.Account'
12
- target: config.target // Standard: 'es2020'
13
- minify: config.minify
14
- sourcemap: config.sourcemap
15
- external: config.external // z.B. ['fs', 'path'] für Node.js-Abhängigkeiten
16
- ```
17
-
18
- Alle Einträge werden parallel mit `Promise.allSettled()` gebaut, was Teilerfolge ermöglicht.
19
-
20
- ## 5.2 xrmforge.config.json Schema
21
-
22
- ```json
23
- {
24
- "build": {
25
- "outDir": "./dist/prefix_/JS",
26
- "target": "es2020",
27
- "sourcemap": true,
28
- "minify": true,
29
- "external": [],
30
- "entries": {
31
- "entry_name": {
32
- "input": "./src/forms/account-form.ts",
33
- "namespace": "Contoso.Account",
34
- "out": "Account/OnLoad.js"
35
- }
36
- }
37
- }
38
- }
39
- ```
40
-
41
- ## 5.3 globalName-Behandlung
42
-
43
- esbuild erstellt automatisch verschachtelte Globals aus gepunkteten Namespaces:
44
- ```javascript
45
- // namespace: "Contoso.Account" erzeugt:
46
- var Contoso = Contoso || {};
47
- Contoso.Account = (() => { return { onLoad, onSave }; })();
48
- ```
49
-
50
- D365-Event-Registrierung: `Contoso.Account.onLoad`.
1
+ # 5. Build-Architektur
2
+
3
+ ## 5.1 esbuild IIFE-Bundles
4
+
5
+ XrmForge verwendet esbuild, um IIFE-Bundles (Immediately Invoked Function Expression) für Dynamics 365 zu erstellen. D365 erfordert, dass Skripte als namespace.function registriert werden (z.B. `Contoso.Account.onLoad`).
6
+
7
+ **esbuild-Konfiguration pro Eintrag:**
8
+ ```
9
+ format: 'iife'
10
+ bundle: true
11
+ globalName: entry.namespace // z.B. 'Contoso.Account'
12
+ target: config.target // Standard: 'es2020'
13
+ minify: config.minify
14
+ sourcemap: config.sourcemap
15
+ external: config.external // z.B. ['fs', 'path'] für Node.js-Abhängigkeiten
16
+ ```
17
+
18
+ Alle Einträge werden parallel mit `Promise.allSettled()` gebaut, was Teilerfolge ermöglicht.
19
+
20
+ ## 5.2 xrmforge.config.json Schema
21
+
22
+ ```json
23
+ {
24
+ "build": {
25
+ "outDir": "./dist/prefix_/JS",
26
+ "target": "es2020",
27
+ "sourcemap": true,
28
+ "minify": true,
29
+ "external": [],
30
+ "entries": {
31
+ "entry_name": {
32
+ "input": "./src/forms/account-form.ts",
33
+ "namespace": "Contoso.Account",
34
+ "out": "Account/OnLoad.js"
35
+ }
36
+ }
37
+ }
38
+ }
39
+ ```
40
+
41
+ ## 5.3 globalName-Behandlung
42
+
43
+ esbuild erstellt automatisch verschachtelte Globals aus gepunkteten Namespaces:
44
+ ```javascript
45
+ // namespace: "Contoso.Account" erzeugt:
46
+ var Contoso = Contoso || {};
47
+ Contoso.Account = (() => { return { onLoad, onSave }; })();
48
+ ```
49
+
50
+ D365-Event-Registrierung: `Contoso.Account.onLoad`.
@@ -1,42 +1,42 @@
1
- # 6. Inkrementelle Generierung
2
-
3
- ## 6.1 Übersicht
4
-
5
- Die inkrementelle Generierung verwendet die Dataverse-Funktion `RetrieveMetadataChanges`, um zu erkennen, welche Entitäten sich seit der letzten Generierung geändert haben. Dies reduziert die Generierungszeit von Sekunden auf Millisekunden (gemessen: 4720ms auf 473ms, 10-fache Verbesserung).
6
-
7
- ## 6.2 Komponenten
8
-
9
- **ChangeDetector** (`src/metadata/change-detector.ts`):
10
- - `getInitialVersionStamp()` - Erster Lauf: holt den initialen ServerVersionStamp
11
- - `detectChanges(clientVersionStamp)` - Folgeläufe: gibt changedEntityNames, deletedEntityNames, newVersionStamp zurück
12
-
13
- **MetadataCache** (`src/metadata/cache.ts`):
14
- - Dateisystem-basiert: `.xrmforge/cache/metadata.json`
15
- - Speichert: Manifest (Version, Umgebungs-URL, ServerVersionStamp, letzter Refresh, Entitätsliste) + entityTypeInfos pro Entität
16
- - Validierung: prüft Cache-Version, Umgebungs-URL-Übereinstimmung, Dateiexistenz
17
-
18
- ## 6.3 Ablauf
19
-
20
- ```
21
- Erster Lauf (kein Cache):
22
- 1. Alle Entitäts-Metadaten abrufen
23
- 2. getInitialVersionStamp()
24
- 3. Cache mit ServerVersionStamp speichern
25
-
26
- Folgelauf (Cache vorhanden):
27
- 1. Cache laden, Umgebungs-URL validieren
28
- 2. detectChanges(cachedVersionStamp)
29
- 3. Nur geänderte Entitäten abrufen
30
- 4. Gelöschte Entitäten aus Cache entfernen
31
- 5. Cache mit neuem ServerVersionStamp speichern
32
-
33
- Abgelaufener Stempel (>90 Tage):
34
- Fehlercode 0x80044352 erkannt
35
- Rückfall auf vollständigen Refresh
36
- ```
37
-
38
- ## 6.4 RetrieveMetadataChanges API
39
-
40
- - **Typ:** OData-Funktion (GET, nicht POST)
41
- - **URL:** `/RetrieveMetadataChanges(Query=@q,ClientVersionStamp=@s)?@q={...}&@s='...'`
42
- - **Antwort:** EntityMetadata[] mit HasChanged-Flag, ServerVersionStamp, DeletedMetadata
1
+ # 6. Inkrementelle Generierung
2
+
3
+ ## 6.1 Übersicht
4
+
5
+ Die inkrementelle Generierung verwendet die Dataverse-Funktion `RetrieveMetadataChanges`, um zu erkennen, welche Entitäten sich seit der letzten Generierung geändert haben. Dies reduziert die Generierungszeit von Sekunden auf Millisekunden (gemessen: 4720ms auf 473ms, 10-fache Verbesserung).
6
+
7
+ ## 6.2 Komponenten
8
+
9
+ **ChangeDetector** (`src/metadata/change-detector.ts`):
10
+ - `getInitialVersionStamp()` - Erster Lauf: holt den initialen ServerVersionStamp
11
+ - `detectChanges(clientVersionStamp)` - Folgeläufe: gibt changedEntityNames, deletedEntityNames, newVersionStamp zurück
12
+
13
+ **MetadataCache** (`src/metadata/cache.ts`):
14
+ - Dateisystem-basiert: `.xrmforge/cache/metadata.json`
15
+ - Speichert: Manifest (Version, Umgebungs-URL, ServerVersionStamp, letzter Refresh, Entitätsliste) + entityTypeInfos pro Entität
16
+ - Validierung: prüft Cache-Version, Umgebungs-URL-Übereinstimmung, Dateiexistenz
17
+
18
+ ## 6.3 Ablauf
19
+
20
+ ```
21
+ Erster Lauf (kein Cache):
22
+ 1. Alle Entitäts-Metadaten abrufen
23
+ 2. getInitialVersionStamp()
24
+ 3. Cache mit ServerVersionStamp speichern
25
+
26
+ Folgelauf (Cache vorhanden):
27
+ 1. Cache laden, Umgebungs-URL validieren
28
+ 2. detectChanges(cachedVersionStamp)
29
+ 3. Nur geänderte Entitäten abrufen
30
+ 4. Gelöschte Entitäten aus Cache entfernen
31
+ 5. Cache mit neuem ServerVersionStamp speichern
32
+
33
+ Abgelaufener Stempel (>90 Tage):
34
+ Fehlercode 0x80044352 erkannt
35
+ Rückfall auf vollständigen Refresh
36
+ ```
37
+
38
+ ## 6.4 RetrieveMetadataChanges API
39
+
40
+ - **Typ:** OData-Funktion (GET, nicht POST)
41
+ - **URL:** `/RetrieveMetadataChanges(Query=@q,ClientVersionStamp=@s)?@q={...}&@s='...'`
42
+ - **Antwort:** EntityMetadata[] mit HasChanged-Flag, ServerVersionStamp, DeletedMetadata
@@ -1,59 +1,59 @@
1
- # 7. HTTP-Client
2
-
3
- ## 7.1 DataverseHttpClient
4
-
5
- Der zentrale HTTP-Client (`src/http/client.ts`) bietet belastbare Kommunikation mit der Dataverse Web API.
6
-
7
- **Kernmethoden:**
8
- - `get<T>(path, signal?)` - Einzelne GET-Anfrage mit Retry
9
- - `getAll<T>(path, signal?)` - GET mit automatischer @odata.nextLink-Paginierung (max 100 Seiten)
10
-
11
- ## 7.2 Nur-Lesen-Standard
12
-
13
- Der Client ist standardmäßig auf `readOnly: true` eingestellt und blockiert POST/PATCH/PUT/DELETE-Anfragen. Dies verhindert versehentliche Datenänderungen während der Typgenerierung. Schreibzugriff erfordert explizites `readOnly: false`.
14
-
15
- ## 7.3 Retry mit exponentiellem Backoff
16
-
17
- - **Basisverzögerung:** 1000ms (konfigurierbar)
18
- - **Maximaler Backoff:** 60 Sekunden
19
- - **Jitter:** Zufällige Verzögerung bis zur Basisverzögerung
20
- - **Formel:** `min(baseDelay * 2^(attempt-1) + jitter, 60000)`
21
- - **Maximale Retries:** konfigurierbar (Standard: 3)
22
-
23
- ## 7.4 Rate Limiting (HTTP 429)
24
-
25
- - **Separater Zähler** von Standard-Retries (nicht vermischt)
26
- - **Retry-After-Header** wird respektiert (Sekunden in Millisekunden umgerechnet)
27
- - **Maximal 10 aufeinanderfolgende 429-Retries** (DEFAULT_MAX_RATE_LIMIT_RETRIES)
28
- - 429-Antworten inkrementieren den Standard-Retry-Zähler NICHT
29
-
30
- ## 7.5 Nebenläufigkeitssteuerung
31
-
32
- Nicht-rekursives Semaphor-Muster:
33
- - **maxConcurrency:** 5 (Standard)
34
- - Warteschlange mit FIFO-Reihenfolge
35
- - Alle Retries erfolgen innerhalb eines einzelnen Slots (verhindert Slot-Erschöpfung)
36
-
37
- ## 7.6 Token-Caching
38
-
39
- - Nur im Speicher (wird niemals auf die Festplatte persistiert)
40
- - 5-Minuten-Puffer vor Ablauf (TOKEN_BUFFER_MS = 300000)
41
- - Ausstehende Token-Refresh-Promise verhindert gleichzeitige Token-Anfragen
42
-
43
- ## 7.7 Eingabe-Sanitisierung
44
-
45
- OData-Injection-Prävention:
46
- - `sanitizeIdentifier()` - Regex `[a-zA-Z_][a-zA-Z0-9_]*`
47
- - `sanitizeGuid()` - GUID-Format-Validierung
48
- - `escapeODataString()` - Verdopplung einfacher Anführungszeichen
49
-
50
- ## 7.8 Fehlerbehandlung
51
-
52
- | HTTP-Status | Verhalten | Retry |
53
- |-------------|-----------|-------|
54
- | 2xx | Erfolg | Nein |
55
- | 401 | Token-Cache leeren, einmal wiederholen | Ja (1x) |
56
- | 429 | Retry-After respektieren, separater Zähler | Ja (bis zu 10x) |
57
- | 5xx | Exponentieller Backoff | Ja (bis zu maxRetries) |
58
- | 404, 403 | Nicht wiederholbar | Nein |
59
- | Netzwerkfehler | Exponentieller Backoff | Ja |
1
+ # 7. HTTP-Client
2
+
3
+ ## 7.1 DataverseHttpClient
4
+
5
+ Der zentrale HTTP-Client (`src/http/client.ts`) bietet belastbare Kommunikation mit der Dataverse Web API.
6
+
7
+ **Kernmethoden:**
8
+ - `get<T>(path, signal?)` - Einzelne GET-Anfrage mit Retry
9
+ - `getAll<T>(path, signal?)` - GET mit automatischer @odata.nextLink-Paginierung (max 100 Seiten)
10
+
11
+ ## 7.2 Nur-Lesen-Standard
12
+
13
+ Der Client ist standardmäßig auf `readOnly: true` eingestellt und blockiert POST/PATCH/PUT/DELETE-Anfragen. Dies verhindert versehentliche Datenänderungen während der Typgenerierung. Schreibzugriff erfordert explizites `readOnly: false`.
14
+
15
+ ## 7.3 Retry mit exponentiellem Backoff
16
+
17
+ - **Basisverzögerung:** 1000ms (konfigurierbar)
18
+ - **Maximaler Backoff:** 60 Sekunden
19
+ - **Jitter:** Zufällige Verzögerung bis zur Basisverzögerung
20
+ - **Formel:** `min(baseDelay * 2^(attempt-1) + jitter, 60000)`
21
+ - **Maximale Retries:** konfigurierbar (Standard: 3)
22
+
23
+ ## 7.4 Rate Limiting (HTTP 429)
24
+
25
+ - **Separater Zähler** von Standard-Retries (nicht vermischt)
26
+ - **Retry-After-Header** wird respektiert (Sekunden in Millisekunden umgerechnet)
27
+ - **Maximal 10 aufeinanderfolgende 429-Retries** (DEFAULT_MAX_RATE_LIMIT_RETRIES)
28
+ - 429-Antworten inkrementieren den Standard-Retry-Zähler NICHT
29
+
30
+ ## 7.5 Nebenläufigkeitssteuerung
31
+
32
+ Nicht-rekursives Semaphor-Muster:
33
+ - **maxConcurrency:** 5 (Standard)
34
+ - Warteschlange mit FIFO-Reihenfolge
35
+ - Alle Retries erfolgen innerhalb eines einzelnen Slots (verhindert Slot-Erschöpfung)
36
+
37
+ ## 7.6 Token-Caching
38
+
39
+ - Nur im Speicher (wird niemals auf die Festplatte persistiert)
40
+ - 5-Minuten-Puffer vor Ablauf (TOKEN_BUFFER_MS = 300000)
41
+ - Ausstehende Token-Refresh-Promise verhindert gleichzeitige Token-Anfragen
42
+
43
+ ## 7.7 Eingabe-Sanitisierung
44
+
45
+ OData-Injection-Prävention:
46
+ - `sanitizeIdentifier()` - Regex `[a-zA-Z_][a-zA-Z0-9_]*`
47
+ - `sanitizeGuid()` - GUID-Format-Validierung
48
+ - `escapeODataString()` - Verdopplung einfacher Anführungszeichen
49
+
50
+ ## 7.8 Fehlerbehandlung
51
+
52
+ | HTTP-Status | Verhalten | Retry |
53
+ |-------------|-----------|-------|
54
+ | 2xx | Erfolg | Nein |
55
+ | 401 | Token-Cache leeren, einmal wiederholen | Ja (1x) |
56
+ | 429 | Retry-After respektieren, separater Zähler | Ja (bis zu 10x) |
57
+ | 5xx | Exponentieller Backoff | Ja (bis zu maxRetries) |
58
+ | 404, 403 | Nicht wiederholbar | Nein |
59
+ | Netzwerkfehler | Exponentieller Backoff | Ja |
@@ -1,18 +1,18 @@
1
- # 8. Authentifizierung
2
-
3
- ## 8.1 Credential Factory
4
-
5
- `createCredential(config: AuthConfig)` gibt ein `TokenCredential` (aus @azure/identity) basierend auf der Authentifizierungsmethode zurück:
6
-
7
- ## 8.2 Vier Authentifizierungsabläufe
8
-
9
- | Methode | Konfiguration | @azure/identity-Klasse | Anwendungsfall |
10
- |---------|---------------|------------------------|----------------|
11
- | client-credentials | tenantId, clientId, clientSecret | ClientSecretCredential | CI/CD, automatisierte Pipelines |
12
- | interactive | tenantId, clientId? | InteractiveBrowserCredential | Entwickler-Arbeitsplatz |
13
- | device-code | tenantId, clientId? | DeviceCodeCredential | Headless-CLI-Umgebungen |
14
- | token | token (string) | StaticTokenCredential | Vorab erworbene Tokens (z.B. aus TokenVault) |
15
-
16
- ## 8.3 Token-Scope
17
-
18
- Alle Authentifizierungsabläufe fordern den Scope an: `{environmentUrl}/.default`
1
+ # 8. Authentifizierung
2
+
3
+ ## 8.1 Credential Factory
4
+
5
+ `createCredential(config: AuthConfig)` gibt ein `TokenCredential` (aus @azure/identity) basierend auf der Authentifizierungsmethode zurück:
6
+
7
+ ## 8.2 Vier Authentifizierungsabläufe
8
+
9
+ | Methode | Konfiguration | @azure/identity-Klasse | Anwendungsfall |
10
+ |---------|---------------|------------------------|----------------|
11
+ | client-credentials | tenantId, clientId, clientSecret | ClientSecretCredential | CI/CD, automatisierte Pipelines |
12
+ | interactive | tenantId, clientId? | InteractiveBrowserCredential | Entwickler-Arbeitsplatz |
13
+ | device-code | tenantId, clientId? | DeviceCodeCredential | Headless-CLI-Umgebungen |
14
+ | token | token (string) | StaticTokenCredential | Vorab erworbene Tokens (z.B. aus TokenVault) |
15
+
16
+ ## 8.3 Token-Scope
17
+
18
+ Alle Authentifizierungsabläufe fordern den Scope an: `{environmentUrl}/.default`
@@ -1,55 +1,55 @@
1
- # 9. Test-Framework
2
-
3
- ## 9.1 createFormMock
4
-
5
- ```typescript
6
- import { createFormMock } from '@xrmforge/testing';
7
- import type { AccountMainForm, AccountMainFormMockValues } from '../generated/forms/account';
8
-
9
- const mock = createFormMock<AccountMainForm, AccountMainFormMockValues>({
10
- name: 'Contoso Ltd',
11
- statuscode: 0,
12
- revenue: 1000000,
13
- });
14
-
15
- // Verwendung in Tests:
16
- onLoad(mock.executionContext);
17
- expect(mock.formContext.getControl('revenue').getVisible()).toBe(true);
18
- ```
19
-
20
- **Was gemockt wird:**
21
- - Attribute: MockAttribute-Instanzen mit getValue/setValue, Dirty-Tracking, onChange-Handlern, Required Level, Submit Mode
22
- - Steuerelemente: MockControl-Instanzen mit Sichtbarkeits-/Deaktiviert-/Label-/Benachrichtigungszustand
23
- - UI: Formular-Benachrichtigungen, Tab/Section-Stubs
24
- - Entität: ID, Entitätsname, Primärattribut
25
- - Daten: refresh(), save() Stubs, die Promise-ähnliches zurückgeben
26
- - Navigation: openForm/openAlertDialog Stubs
27
-
28
- **Lazy-Initialisierung:** Attribute, die über `getAttribute()` angesprochen werden und nicht in den initialen Werten enthalten waren, werden spontan mit null-Wert erstellt.
29
-
30
- ## 9.2 fireOnChange
31
-
32
- ```typescript
33
- mock.fireOnChange('statuscode');
34
- // Löst alle über getAttribute('statuscode').addOnChange(handler) registrierten Handler aus
35
- ```
36
-
37
- Erstellt einen MockEventContext mit dem Attribut als Eventquelle.
38
-
39
- ## 9.3 setupXrmMock / teardownXrmMock
40
-
41
- ```typescript
42
- import { setupXrmMock, teardownXrmMock } from '@xrmforge/testing';
43
-
44
- beforeEach(() => setupXrmMock());
45
- afterEach(() => teardownXrmMock());
46
-
47
- // Mit WebApi-Überschreibungen:
48
- setupXrmMock({
49
- webApiOverrides: {
50
- retrieveMultipleRecords: async () => ({ entities: [{ name: 'Test' }] }),
51
- },
52
- });
53
- ```
54
-
55
- Richtet ein globales `Xrm`-Objekt auf `globalThis` ein mit minimalen WebApi-, Navigation- und Utility-Stubs.
1
+ # 9. Test-Framework
2
+
3
+ ## 9.1 createFormMock
4
+
5
+ ```typescript
6
+ import { createFormMock } from '@xrmforge/testing';
7
+ import type { AccountMainForm, AccountMainFormMockValues } from '../generated/forms/account';
8
+
9
+ const mock = createFormMock<AccountMainForm, AccountMainFormMockValues>({
10
+ name: 'Contoso Ltd',
11
+ statuscode: 0,
12
+ revenue: 1000000,
13
+ });
14
+
15
+ // Verwendung in Tests:
16
+ onLoad(mock.executionContext);
17
+ expect(mock.formContext.getControl('revenue').getVisible()).toBe(true);
18
+ ```
19
+
20
+ **Was gemockt wird:**
21
+ - Attribute: MockAttribute-Instanzen mit getValue/setValue, Dirty-Tracking, onChange-Handlern, Required Level, Submit Mode
22
+ - Steuerelemente: MockControl-Instanzen mit Sichtbarkeits-/Deaktiviert-/Label-/Benachrichtigungszustand
23
+ - UI: Formular-Benachrichtigungen, Tab/Section-Stubs
24
+ - Entität: ID, Entitätsname, Primärattribut
25
+ - Daten: refresh(), save() Stubs, die Promise-ähnliches zurückgeben
26
+ - Navigation: openForm/openAlertDialog Stubs
27
+
28
+ **Lazy-Initialisierung:** Attribute, die über `getAttribute()` angesprochen werden und nicht in den initialen Werten enthalten waren, werden spontan mit null-Wert erstellt.
29
+
30
+ ## 9.2 fireOnChange
31
+
32
+ ```typescript
33
+ mock.fireOnChange('statuscode');
34
+ // Löst alle über getAttribute('statuscode').addOnChange(handler) registrierten Handler aus
35
+ ```
36
+
37
+ Erstellt einen MockEventContext mit dem Attribut als Eventquelle.
38
+
39
+ ## 9.3 setupXrmMock / teardownXrmMock
40
+
41
+ ```typescript
42
+ import { setupXrmMock, teardownXrmMock } from '@xrmforge/testing';
43
+
44
+ beforeEach(() => setupXrmMock());
45
+ afterEach(() => teardownXrmMock());
46
+
47
+ // Mit WebApi-Überschreibungen:
48
+ setupXrmMock({
49
+ webApiOverrides: {
50
+ retrieveMultipleRecords: async () => ({ entities: [{ name: 'Test' }] }),
51
+ },
52
+ });
53
+ ```
54
+
55
+ Richtet ein globales `Xrm`-Objekt auf `globalThis` ein mit minimalen WebApi-, Navigation- und Utility-Stubs.