@xrmforge/typegen 0.7.0 → 0.8.0

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/MIGRATION.md +40 -11
  2. package/dist/index.d.ts +15 -27
  3. package/dist/index.js +191 -206
  4. package/dist/index.js.map +1 -1
  5. package/docs/architecture/00-README.md +26 -0
  6. package/docs/architecture/01-executive-summary.md +11 -0
  7. package/docs/architecture/02-packages.md +110 -0
  8. package/docs/architecture/03-generated-types.md +172 -0
  9. package/docs/architecture/04-cli.md +58 -0
  10. package/docs/architecture/05-build.md +50 -0
  11. package/docs/architecture/06-incremental.md +42 -0
  12. package/docs/architecture/07-http-client.md +59 -0
  13. package/docs/architecture/08-authentication.md +18 -0
  14. package/docs/architecture/09-testing.md +55 -0
  15. package/docs/architecture/10-eslint-plugin.md +82 -0
  16. package/docs/architecture/11-agent-md.md +38 -0
  17. package/docs/architecture/12-xrm-pitfalls.md +14 -0
  18. package/docs/architecture/13-helpers.md +50 -0
  19. package/docs/architecture/14-showcases.md +21 -0
  20. package/docs/architecture/15-ci-cd.md +49 -0
  21. package/docs/architecture/16-technical-debt.md +17 -0
  22. package/docs/architecture/17-roadmap.md +25 -0
  23. package/docs/architecture/18-design-principles.md +22 -0
  24. package/docs/architektur/00-README.md +26 -0
  25. package/docs/architektur/01-zusammenfassung.md +11 -0
  26. package/docs/architektur/02-packages.md +110 -0
  27. package/docs/architektur/03-generierte-typen.md +172 -0
  28. package/docs/architektur/04-cli.md +58 -0
  29. package/docs/architektur/05-build.md +50 -0
  30. package/docs/architektur/06-inkrementell.md +42 -0
  31. package/docs/architektur/07-http-client.md +59 -0
  32. package/docs/architektur/08-authentifizierung.md +18 -0
  33. package/docs/architektur/09-testing.md +55 -0
  34. package/docs/architektur/10-eslint-plugin.md +82 -0
  35. package/docs/architektur/11-agent-md.md +38 -0
  36. package/docs/architektur/12-xrm-fallstricke.md +14 -0
  37. package/docs/architektur/13-helpers.md +50 -0
  38. package/docs/architektur/14-showcases.md +21 -0
  39. package/docs/architektur/15-ci-cd.md +49 -0
  40. package/docs/architektur/16-technische-schulden.md +17 -0
  41. package/docs/architektur/17-roadmap.md +25 -0
  42. package/docs/architektur/18-designprinzipien.md +22 -0
  43. package/package.json +4 -3
@@ -0,0 +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.
@@ -0,0 +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`.
@@ -0,0 +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
@@ -0,0 +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 |
@@ -0,0 +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`
@@ -0,0 +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.
@@ -0,0 +1,82 @@
1
+ # 10. ESLint Plugin
2
+
3
+ ## 10.1 Installation
4
+
5
+ ```javascript
6
+ // eslint.config.js (Flat Config, ESLint v9)
7
+ import xrmforge from '@xrmforge/eslint-plugin';
8
+
9
+ export default [
10
+ xrmforge.configs.recommended,
11
+ // oder einzelne Regeln auswählen
12
+ ];
13
+ ```
14
+
15
+ ## 10.2 Regeln
16
+
17
+ ### no-xrm-page (error)
18
+
19
+ Verbietet die veraltete `Xrm.Page`-API (entfernt in D365 v9.0+).
20
+
21
+ ```typescript
22
+ // Falsch
23
+ Xrm.Page.getAttribute("name");
24
+
25
+ // Richtig
26
+ const form = executionContext.getFormContext();
27
+ form.getAttribute("name");
28
+ ```
29
+
30
+ ### no-magic-optionset (warn)
31
+
32
+ Verbietet rohe Zahlen (>= 2) in Vergleichen mit `.getValue()`.
33
+
34
+ ```typescript
35
+ // Falsch
36
+ if (attr.getValue() === 595300000) { }
37
+
38
+ // Richtig
39
+ import { StatusCode } from '../generated/optionsets/account';
40
+ if (attr.getValue() === StatusCode.Active) { }
41
+ ```
42
+
43
+ ### no-sync-webapi (error)
44
+
45
+ Verbietet synchrone XMLHttpRequest (`new XMLHttpRequest()` und `.open()` mit `async=false`).
46
+
47
+ ```typescript
48
+ // Falsch
49
+ xhr.open("GET", url, false);
50
+
51
+ // Richtig
52
+ const data = await Xrm.WebApi.retrieveRecord("account", id);
53
+ ```
54
+
55
+ ### require-error-handling (warn)
56
+
57
+ Verlangt try/catch in exportierten async-Funktionen, die mit "on" beginnen (Event-Handler).
58
+
59
+ ```typescript
60
+ // Falsch
61
+ export async function onLoad(ctx) {
62
+ await fetch("/api"); // keine Fehlerbehandlung
63
+ }
64
+
65
+ // Richtig
66
+ export async function onLoad(ctx) {
67
+ try { await fetch("/api"); }
68
+ catch (error) { console.error(error); }
69
+ }
70
+ ```
71
+
72
+ ### require-namespace (warn)
73
+
74
+ Verbietet direkte `window.X = ...` oder `globalThis.X = ...` Zuweisungen. Stattdessen sollen Modul-Exports mit esbuild globalName verwendet werden.
75
+
76
+ ```typescript
77
+ // Falsch
78
+ window.Contoso = { onLoad: function() {} };
79
+
80
+ // Richtig
81
+ export function onLoad(ctx: Xrm.Events.EventContext) {}
82
+ ```
@@ -0,0 +1,38 @@
1
+ # 11. AGENT.md System
2
+
3
+ ## 11.1 Zweck
4
+
5
+ Die AGENT.md ist eine generierte Datei, die KI-Coding-Assistenten (Claude, ChatGPT, Copilot, Cursor) beibringt, wie sie optimale D365-Formularskripte mit XrmForge schreiben. Sie wird von `xrmforge init` erzeugt und im Projektstamm abgelegt.
6
+
7
+ ## 11.2 Inhaltsstruktur
8
+
9
+ 1. **Package-Übersicht** - Was jedes @xrmforge-Package tut
10
+ 2. **10 Regeln: Immer** - Fields Enum, OptionSet Enum, FormContext-Cast, EntityNames, parseLookup, select, createFormMock, Modul-Exports, Tabs/Sections Enums, Fehlerbehandlung
11
+ 3. **Regeln: Niemals** - Rohe Strings, Magic Numbers, Xrm.Page, synchrone XHR, eval, window-Zuweisungen
12
+ 4. **Vorher/Nachher-Beispiele** - Feldzugriff, OptionSet-Vergleich, Testen
13
+ 5. **Pattern-Erkennungstabelle** - Zuordnung von Legacy-Pattern zu XrmForge-Ersetzung
14
+ 6. **OptionSet-Enum-Erstellungsanleitung** - Wie man Enums aus Magic Numbers in Legacy-Code erstellt
15
+ 7. **Testen mit setupXrmMock** - Globales Xrm-Mock-Pattern
16
+ 8. **Build-Befehle** - xrmforge build, Watch-Modus
17
+ 9. **@types/xrm-Fallstricke** - Bekannte Probleme und Workarounds
18
+ 10. **Dateistruktur** - Erwartetes Projektlayout
19
+
20
+ ## 11.3 Template-System
21
+
22
+ Die AGENT.md ist als `src/scaffold/templates/AGENT.md` im devkit-Package gespeichert und wird zur Scaffolding-Zeit über `template-loader.ts` geladen. Es ist keine Variablenersetzung nötig (die Datei ist statisch).
23
+
24
+ ## 11.4 Ergebnisse des KI-Vergleichstests
25
+
26
+ Fünf KI-Modelle wurden beim Konvertieren von Legacy-D365-JavaScript (account.js + lm_helper.js, 1.288 Zeilen) zu TypeScript mit XrmForge getestet:
27
+
28
+ | Rang | Modell | Punkte | Werkzeug | Stärke |
29
+ |------|--------|--------|----------|--------|
30
+ | 1 | Claude Opus 4.6 | 42/50 | Claude Code | Meiste Tests (62), beste Codestruktur |
31
+ | 2 | Claude Sonnet 4.6 | 41/50 | Claude Code | Meiste Bugs gefunden (5), bester DI-Ansatz |
32
+ | 3 | Cursor Composer 2 | 35/50 | Cursor IDE | Hat select() Node-API-Problem erkannt |
33
+ | 4 | ChatGPT GPT-4o | 30/50 | ChatGPT Web | Funktional, aber weniger XrmForge-spezifisch |
34
+ | 5 | MS Copilot | 12/50 | Browser-Chat | Kein Workspace-Zugriff, hat AGENT.md nie gesehen |
35
+
36
+ **Kriterien (11, maximal 5 Punkte je = 55 max):** Fields-Enum-Nutzung, OptionSet-Enums, FormContext-Typisierung, XrmForge-Helpers, Modul-Exports, Tests vorhanden, Testqualität, Fehlerbehandlung, Codequalität, gefundene Bugs, Dokumentation.
37
+
38
+ **Zentrale Erkenntnis:** Keine KI hat konsistent `@xrmforge/helpers`-Imports (select, parseLookup) verwendet. Dies bleibt die grösste Adoptionslücke.
@@ -0,0 +1,14 @@
1
+ # 12. @types/xrm-Fallstricke
2
+
3
+ Bekannte Probleme bei der Arbeit mit `@types/xrm`:
4
+
5
+ | Problem | Falsch | Richtig |
6
+ |---------|--------|---------|
7
+ | Form-Interface | `interface extends Xrm.FormContext` | `extends Omit<Xrm.FormContext, 'getAttribute' \| 'getControl'>` |
8
+ | AlertDialogResponse | `Xrm.Navigation.AlertDialogResponse` | `Xrm.Async.PromiseLike<void>` (Typ existiert nicht) |
9
+ | ConfirmDialogResponse | `Xrm.Navigation.ConfirmDialogResponse` | `Xrm.Navigation.ConfirmResult` (Typ existiert nicht) |
10
+ | setNotification | `setNotification(message)` | `setNotification(message, uniqueId)` (erfordert 2 Argumente) |
11
+ | openFile | `openFile({ fileName, ... })` | Muss `fileSize`-Eigenschaft in FileDetails enthalten |
12
+ | SubmitMode | `Xrm.Attributes.SubmitMode` | `Xrm.SubmitMode` |
13
+ | const enum in .d.ts | `const enum` in `.d.ts`-Dateien | `const enum` in `.ts`-ES-Modulen verwenden (typegen 0.8.0+ generiert `.ts`-Dateien, wodurch dieses Problem gelöst ist) |
14
+ | Grid.refresh() | `grid.refresh()` | `(grid as any).refresh()` (nicht typisiert in @types/xrm) |
@@ -0,0 +1,50 @@
1
+ # 13. @xrmforge/helpers Package
2
+
3
+ ## 13.1 Problem
4
+
5
+ Der bisherige Ansatz nutzte einen `/helpers`-Subpath-Export auf `@xrmforge/typegen`. Das war verwirrend, weil typegen ein Node.js-Codegenerierungstool ist, während die Helpers browsersichere Laufzeitfunktionen sind. Der Subpath `@xrmforge/typegen/helpers` war nicht intuitiv und KI-Coding-Assistenten haben ihn durchgängig nicht gefunden.
6
+
7
+ ## 13.2 Lösung
8
+
9
+ Ein eigenständiges `@xrmforge/helpers`-Package bündelt allen browsersicheren Laufzeitcode. Keine Node.js-Abhängigkeiten. Klarer, auffindbarer Import-Pfad:
10
+
11
+ ```typescript
12
+ // Import vom dedizierten helpers-Package
13
+ import { select, parseLookup, typedForm } from '@xrmforge/helpers';
14
+ ```
15
+
16
+ ## 13.3 Exports
17
+
18
+ **Web-API-Helpers:**
19
+ - `select(...fields: string[]): string` - Erstellt `?$select=field1,field2`
20
+ - `selectExpand(fields: string[], expand: string): string` - Erstellt `?$select=...&$expand=...`
21
+ - `parseLookup(response: Record<string, unknown>, fieldName: string): LookupValue | null` - Parst `_fieldname_value` mit OData-Annotationen
22
+ - `parseLookups(response: Record<string, unknown>, fieldName: string): LookupValue[]` - Multi-Value-Lookup-Parsing
23
+ - `parseFormattedValue(response: Record<string, unknown>, fieldName: string): string | null` - Extrahiert `@OData.Community.Display.V1.FormattedValue`
24
+
25
+ **Xrm-Konstanten (8 const enums):**
26
+ - DisplayState, FormNotificationLevel, RequiredLevel, SubmitMode, SaveMode, ClientType, ClientState, OperationType
27
+
28
+ **typedForm()-Proxy:**
29
+ - `typedForm<TForm>(formContext)` - Gibt einen Proxy zurück, bei dem `form.name` an `getAttribute('name')` delegiert
30
+ - GET-Trap: Property-Zugriff delegiert an getAttribute(); `$context` gibt den rohen FormContext zurück; `$control(name)` gibt getControl() zurück
31
+ - SET-Trap: Wirft TypeError und erzwingt die Verwendung von `.setValue()`
32
+ - HAS-Trap: Prüft, ob ein Attribut auf dem Formular existiert
33
+
34
+ **Action/Function-Laufzeit:**
35
+ - `createBoundAction(entityName, actionName)` - Erstellt einen gebundenen Action-Executor
36
+ - `executeRequest(request)` - Führt einen Organization Request über Xrm.WebApi.online.execute aus
37
+ - `withProgress(message, fn)` - Umhüllt eine asynchrone Operation mit Xrm.Utility.showProgressIndicator
38
+
39
+ ## 13.4 Migration
40
+
41
+ Der alte Import-Pfad `@xrmforge/typegen/helpers` wurde entfernt. Alle Imports aktualisieren:
42
+
43
+ ```typescript
44
+ // Alt (entfernt)
45
+ import { select } from '@xrmforge/typegen/helpers';
46
+ import { typedForm } from '@xrmforge/formhelpers';
47
+
48
+ // Neu
49
+ import { select, typedForm } from '@xrmforge/helpers';
50
+ ```
@@ -0,0 +1,21 @@
1
+ # 14. Showcases
2
+
3
+ ## 14.1 Markant WebResources (Produktions-Showcase)
4
+
5
+ Befindet sich im XrmForge-Workspace-Repository unter `docs/07_showcase/markant-webresources/`.
6
+
7
+ - **30 WebResources** in `src/forms/` (Account, Contact, Opportunity, Lead, Quote, Email, Task usw.)
8
+ - **1 gemeinsame Bibliothek** (DSGVO-Aufbewahrungsfristen-UI)
9
+ - **9 Testdateien** mit 59 Tests
10
+ - **79 generierte Typings:** 25 Form-Interfaces, 28 Entity-Interfaces, 22 OptionSet-Dateien, 4 Action-Executors
11
+ - **esbuild-Build** über xrmforge.config.json (32 Einträge)
12
+ - **Deploy-Skript** (deploy.mjs) mit @azure/identity-Authentifizierung, inkrementellem Deployment, Hash-basierter Änderungserkennung
13
+ - **27 Entitäten, 236 OptionSet-Enums, 95 Form-Interfaces, 7 Custom-API-Executors**
14
+
15
+ ## 14.2 LMApp WebResources (KI-Vergleichs-Showcase)
16
+
17
+ Erstellt während der KI-Vergleichstests (Session 9). 18 Legacy-JavaScript-Formularskripte (~8.400 Zeilen) zu TypeScript mit XrmForge-Patterns konvertiert.
18
+
19
+ - **19 WebResources** mit Fields Enums, EntityNames, OptionSet Enums
20
+ - **84 Tests** in 8 Testdateien
21
+ - **XrmForge-optimiert:** Alle 10 AGENT.md-Regeln angewendet (FormContext-Cast, Fields Enum, EntityNames, OptionSet Enums, gemeinsames getLookupObject, Tab Enums)
@@ -0,0 +1,49 @@
1
+ # 15. CI/CD
2
+
3
+ ## 15.1 GitHub Actions CI (`.github/workflows/ci.yml`)
4
+
5
+ **Auslöser:** Push auf main, Pull Requests gegen main.
6
+
7
+ **Matrix:** Node 20, Node 22 auf ubuntu-latest.
8
+
9
+ **Schritte:**
10
+ 1. Checkout
11
+ 2. pnpm einrichten (aus packageManager-Feld)
12
+ 3. Node.js einrichten (Matrix-Version)
13
+ 4. `pnpm install --frozen-lockfile`
14
+ 5. `pnpm lint`
15
+ 6. `pnpm -r exec tsc --noEmit` (Typecheck aller Packages)
16
+ 7. `pnpm build`
17
+ 8. `pnpm test`
18
+ 9. Coverage (nur Node 22): `npx vitest run --coverage` in typegen
19
+
20
+ ## 15.2 Release-Workflow (`.github/workflows/release.yml`)
21
+
22
+ **Auslöser:** Nach erfolgreicher CI bei Push auf main.
23
+
24
+ **Schritte:**
25
+ 1. Checkout, pnpm einrichten, Node 22 einrichten
26
+ 2. `pnpm install --frozen-lockfile`
27
+ 3. `pnpm build`
28
+ 4. Changesets-Action: erstellt Release-PR oder veröffentlicht auf npm
29
+
30
+ **Publish-Befehl:** `pnpm release` = `turbo run build && changeset publish`
31
+
32
+ ## 15.3 Turbo-Pipeline
33
+
34
+ ```
35
+ build: dependsOn: [^build], outputs: [dist/**]
36
+ test: dependsOn: [build]
37
+ typecheck: dependsOn: [^build]
38
+ lint: (keine Abhängigkeiten)
39
+ dev: cache: false, persistent: true
40
+ clean: cache: false
41
+ ```
42
+
43
+ ## 15.4 Changesets
44
+
45
+ Konfiguriert für öffentlichen npm-Zugriff, automatische Aktualisierung interner Abhängigkeiten auf Patch-Ebene. Veröffentlichung erfordert NPM_TOKEN Secret.
46
+
47
+ ## 15.5 Veröffentlichungsreihenfolge
48
+
49
+ Aufgrund interner Abhängigkeiten: zuerst typegen, dann devkit, dann cli. Es muss `pnpm publish` verwendet werden (nicht `npm publish`), um `workspace:*`-Referenzen in echte Versionen aufzulösen.
@@ -0,0 +1,17 @@
1
+ # 16. Technische Schulden
2
+
3
+ ## 16.1 Bekannte Probleme
4
+
5
+ | Problem | Status | Priorität |
6
+ |---------|--------|-----------|
7
+ | parseLookup/select wird von KI-Assistenten nicht übernommen | Offen | Hoch |
8
+ | release.yml doppelte Ausführungen (CI löst Release aus, Release löst CI erneut aus) | Offen | Niedrig |
9
+ | Keine Integrationstests gegen Live-Dataverse | Offen (OE-4) | Mittel |
10
+ | @xrmforge/webapi hat keine Action/Function-Unterstützung | Akzeptiert | Niedrig |
11
+ | devDependency-Versionen im generierten package.json sind auf alte Versionen fixiert | Offen | Niedrig |
12
+
13
+ ## 16.2 Akzeptierte Einschränkungen
14
+
15
+ - **const-enum-Einschränkung:** Gelöst in typegen 0.8.0. Generierter Output sind jetzt `.ts`-ES-Module, sodass `const enum` direkt mit vitest und anderen Test-Frameworks funktioniert.
16
+ - **Grid.refresh() erfordert `as any`:** Nicht typisiert in @types/xrm.
17
+ - **Eine Solution pro Entität:** Wenn eine Entität in mehreren Solutions vorkommt, wird sie nur einmal generiert.
@@ -0,0 +1,25 @@
1
+ # 17. Roadmap
2
+
3
+ ## 17.1 Nächste Schritte (Prioritätsreihenfolge)
4
+
5
+ 1. **parseLookup/select-Adoption** - AGENT.md-Beispiele verbessern, damit KI-Assistenten konsistent `/helpers`-Imports verwenden
6
+ 2. **LMApp-Showcase-Neugenerierung** - Mit aktuellen Releases (testing@0.2.0, devkit@0.4.0 mit verbesserter AGENT.md)
7
+ 3. **KI-Battle Runde 3** - Sonnet vs. Opus erneut testen nach Verbesserungen, um Fortschritt zu messen
8
+ 4. **Dokumentationswebsite** - xrmforge.dev oder xrmforge.io (OE-3)
9
+
10
+ ## 17.2 Offene Entscheidungen
11
+
12
+ | ID | Entscheidung | Status |
13
+ |----|--------------|--------|
14
+ | OE-1 | npm-Scope-Verfügbarkeit (@xrmforge) | Offen |
15
+ | OE-2 | GitHub-Org vs. persönliches Repo | Entschieden: persönlich (juergenbeck/XrmForge) |
16
+ | OE-3 | Dokumentationsdomain (xrmforge.dev oder .io) | Offen |
17
+ | OE-4 | Dataverse-Testumgebung für Integrationstests | Offen |
18
+ | OE-5 | Publisher-Prefix und Solution-Name für PCF/WebResource-Tests | Offen |
19
+
20
+ ## 17.3 Zukünftige Möglichkeiten
21
+
22
+ - Relationship-Names const enum (OE-7, niedrige Priorität)
23
+ - @xrmforge/webapi mit Action/Function-Unterstützung (DataverseHttpClient wiederverwenden)
24
+ - Plugin-System für benutzerdefinierte Generatoren und Typ-Zuordnungen
25
+ - Serverseitige Generierung (Custom API in Dataverse)
@@ -0,0 +1,22 @@
1
+ # 18. Designprinzipien
2
+
3
+ Die 18 Designprinzipien, die die gesamte XrmForge-Entwicklung bestimmen:
4
+
5
+ 1. **Erweitern, nicht ersetzen** - Typen bauen auf @types/xrm auf, überschreiben sie nie.
6
+ 2. **TypeScript durchgängig** - 100% TypeScript-nativ. Kein .NET, kein ADAL.
7
+ 3. **Code muss bauen** - Jeder Arbeitsschritt endet mit grünem Build + Tests.
8
+ 4. **Recherche vor Geschwindigkeit** - Untersuchen, vergleichen, entscheiden, dann implementieren. Niemals raten.
9
+ 5. **Kein Modul ohne Grundlagen** - Fehlerbehandlung, Logging, Unit-Tests, JSDoc auf allen öffentlichen APIs.
10
+ 6. **Monorepo-Disziplin** - Jedes Package eigenständig, keine zirkulären Abhängigkeiten, Barrel-Exports.
11
+ 7. **Enterprise-Resilienz** - Retry + exponentielles Backoff, Rate-Limit-Erkennung, Token-Caching, Read-only als Standard.
12
+ 8. **esbuild-first, webpack-kompatibel** - Standard: esbuild (schnell). webpack bleibt unterstützt. IIFE-Output für D365.
13
+ 9. **Nur MSAL-Authentifizierung** - Ausschliesslich @azure/identity (kein Legacy-ADAL). Drei Flows: Client Credentials, Browser, Device Code.
14
+ 10. **Review erforderlich** - Nach jedem Schritt sofortiges kritisches Review (6 Dimensionen). Nicht fragen, ob Review gewünscht ist.
15
+ 11. **Session-State erforderlich** - session-state.md aktualisiert, Changelog geschrieben, offene Fragen erfasst.
16
+ 12. **Keine halben Sachen** - Jeder Schritt vollständig abgeschlossen: grüner Build + Tests + Review vor dem nächsten Schritt.
17
+ 13. **Fundierte Architekturentscheidungen** - Recherchieren, vergleichen, mit Pro/Contra empfehlen, Entscheidung einholen, persistieren.
18
+ 14. **Abstraktion statt Vendor-Lock-in** - Externe Abhängigkeiten hinter Interfaces (Parser, Auth, Bundler).
19
+ 15. **Zweisprachige Labels** - Primärsprache (1033/Englisch) für Bezeichner, Sekundärsprache in JSDoc. Deutsche Umlaute transliteriert.
20
+ 16. **Review mit Recherche und Live-Verifikation** - Internetrecherche, Live-D365-Verifikation, Produktionscode-Prüfungen, Quellen zitieren.
21
+ 17. **Aufschub hinterfragen** - "Später"-Check: Wird es schwieriger? API-Vertrag? Echter Aufwand? Technische Gründe?
22
+ 18. **Read-only als Standard für Dataverse-Zugriff** - DataverseHttpClient ist standardmässig readOnly: true. Schreibzugriff ist ein explizites Opt-in.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xrmforge/typegen",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "TypeScript declaration generator for Dynamics 365 / Dataverse",
5
5
  "keywords": [
6
6
  "dynamics-365",
@@ -33,6 +33,7 @@
33
33
  },
34
34
  "files": [
35
35
  "dist",
36
+ "docs",
36
37
  "MIGRATION.md"
37
38
  ],
38
39
  "sideEffects": false,
@@ -54,12 +55,12 @@
54
55
  "node": ">=20.0.0"
55
56
  },
56
57
  "scripts": {
57
- "build": "tsup",
58
+ "build": "tsup && node scripts/copy-docs.mjs",
58
59
  "dev": "tsup --watch",
59
60
  "test": "vitest run",
60
61
  "test:watch": "vitest",
61
62
  "typecheck": "tsc --noEmit",
62
63
  "lint": "eslint src/",
63
- "clean": "rm -rf dist"
64
+ "clean": "rm -rf dist docs"
64
65
  }
65
66
  }