@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.
- package/LICENSE +21 -21
- package/MIGRATION.md +194 -194
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/docs/architecture/00-README.md +26 -26
- package/docs/architecture/01-executive-summary.md +11 -11
- package/docs/architecture/02-packages.md +110 -110
- package/docs/architecture/03-generated-types.md +172 -172
- package/docs/architecture/04-cli.md +58 -58
- package/docs/architecture/05-build.md +50 -50
- package/docs/architecture/06-incremental.md +42 -42
- package/docs/architecture/07-http-client.md +59 -59
- package/docs/architecture/08-authentication.md +18 -18
- package/docs/architecture/09-testing.md +55 -55
- package/docs/architecture/10-eslint-plugin.md +82 -82
- package/docs/architecture/11-agent-md.md +38 -38
- package/docs/architecture/12-xrm-pitfalls.md +14 -14
- package/docs/architecture/13-helpers.md +50 -50
- package/docs/architecture/14-showcases.md +21 -21
- package/docs/architecture/15-ci-cd.md +49 -49
- package/docs/architecture/16-technical-debt.md +17 -17
- package/docs/architecture/17-roadmap.md +25 -25
- package/docs/architecture/18-design-principles.md +22 -22
- package/docs/architektur/00-README.md +26 -26
- package/docs/architektur/01-zusammenfassung.md +11 -11
- package/docs/architektur/02-packages.md +110 -110
- package/docs/architektur/03-generierte-typen.md +172 -172
- package/docs/architektur/04-cli.md +58 -58
- package/docs/architektur/05-build.md +50 -50
- package/docs/architektur/06-inkrementell.md +42 -42
- package/docs/architektur/07-http-client.md +59 -59
- package/docs/architektur/08-authentifizierung.md +18 -18
- package/docs/architektur/09-testing.md +55 -55
- package/docs/architektur/10-eslint-plugin.md +82 -82
- package/docs/architektur/11-agent-md.md +38 -38
- package/docs/architektur/12-xrm-fallstricke.md +14 -14
- package/docs/architektur/13-helpers.md +50 -50
- package/docs/architektur/14-showcases.md +21 -21
- package/docs/architektur/15-ci-cd.md +49 -49
- package/docs/architektur/16-technische-schulden.md +17 -17
- package/docs/architektur/17-roadmap.md +25 -25
- package/docs/architektur/18-designprinzipien.md +22 -22
- package/package.json +1 -1
|
@@ -1,82 +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
|
-
```
|
|
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
|
+
```
|
|
@@ -1,38 +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.
|
|
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.
|
|
@@ -1,14 +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) |
|
|
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) |
|
|
@@ -1,50 +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
|
-
```
|
|
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
|
+
```
|
|
@@ -1,21 +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)
|
|
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)
|
|
@@ -1,49 +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.
|
|
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.
|
|
@@ -1,17 +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.
|
|
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.
|
|
@@ -1,25 +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)
|
|
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)
|