@xrmforge/typegen 0.8.4 → 0.8.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/LICENSE +21 -21
  2. package/MIGRATION.md +194 -194
  3. package/dist/index.d.ts +7 -0
  4. package/dist/index.js +46 -4
  5. package/dist/index.js.map +1 -1
  6. package/docs/architecture/00-README.md +26 -26
  7. package/docs/architecture/01-executive-summary.md +11 -11
  8. package/docs/architecture/02-packages.md +110 -110
  9. package/docs/architecture/03-generated-types.md +172 -172
  10. package/docs/architecture/04-cli.md +58 -58
  11. package/docs/architecture/05-build.md +50 -50
  12. package/docs/architecture/06-incremental.md +42 -42
  13. package/docs/architecture/07-http-client.md +59 -59
  14. package/docs/architecture/08-authentication.md +18 -18
  15. package/docs/architecture/09-testing.md +55 -55
  16. package/docs/architecture/10-eslint-plugin.md +82 -82
  17. package/docs/architecture/11-agent-md.md +38 -38
  18. package/docs/architecture/12-xrm-pitfalls.md +14 -14
  19. package/docs/architecture/13-helpers.md +50 -50
  20. package/docs/architecture/14-showcases.md +21 -21
  21. package/docs/architecture/15-ci-cd.md +49 -49
  22. package/docs/architecture/16-technical-debt.md +17 -17
  23. package/docs/architecture/17-roadmap.md +25 -25
  24. package/docs/architecture/18-design-principles.md +22 -22
  25. package/docs/architektur/00-README.md +26 -26
  26. package/docs/architektur/01-zusammenfassung.md +11 -11
  27. package/docs/architektur/02-packages.md +110 -110
  28. package/docs/architektur/03-generierte-typen.md +172 -172
  29. package/docs/architektur/04-cli.md +58 -58
  30. package/docs/architektur/05-build.md +50 -50
  31. package/docs/architektur/06-inkrementell.md +42 -42
  32. package/docs/architektur/07-http-client.md +59 -59
  33. package/docs/architektur/08-authentifizierung.md +18 -18
  34. package/docs/architektur/09-testing.md +55 -55
  35. package/docs/architektur/10-eslint-plugin.md +82 -82
  36. package/docs/architektur/11-agent-md.md +38 -38
  37. package/docs/architektur/12-xrm-fallstricke.md +14 -14
  38. package/docs/architektur/13-helpers.md +50 -50
  39. package/docs/architektur/14-showcases.md +21 -21
  40. package/docs/architektur/15-ci-cd.md +49 -49
  41. package/docs/architektur/16-technische-schulden.md +17 -17
  42. package/docs/architektur/17-roadmap.md +25 -25
  43. package/docs/architektur/18-designprinzipien.md +22 -22
  44. package/package.json +1 -1
@@ -1,82 +1,82 @@
1
- # 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
- // or pick individual rules
12
- ];
13
- ```
14
-
15
- ### 10.2 Rules
16
-
17
- #### no-xrm-page (error)
18
-
19
- Forbids the deprecated `Xrm.Page` API (removed in D365 v9.0+).
20
-
21
- ```typescript
22
- // Bad
23
- Xrm.Page.getAttribute("name");
24
-
25
- // Good
26
- const form = executionContext.getFormContext();
27
- form.getAttribute("name");
28
- ```
29
-
30
- #### no-magic-optionset (warn)
31
-
32
- Forbids raw numbers (>= 2) in comparisons with `.getValue()`.
33
-
34
- ```typescript
35
- // Bad
36
- if (attr.getValue() === 595300000) { }
37
-
38
- // Good
39
- import { StatusCode } from '../generated/optionsets/account';
40
- if (attr.getValue() === StatusCode.Active) { }
41
- ```
42
-
43
- #### no-sync-webapi (error)
44
-
45
- Forbids synchronous XMLHttpRequest (`new XMLHttpRequest()` and `.open()` with `async=false`).
46
-
47
- ```typescript
48
- // Bad
49
- xhr.open("GET", url, false);
50
-
51
- // Good
52
- const data = await Xrm.WebApi.retrieveRecord("account", id);
53
- ```
54
-
55
- #### require-error-handling (warn)
56
-
57
- Requires try/catch in exported async functions starting with "on" (event handlers).
58
-
59
- ```typescript
60
- // Bad
61
- export async function onLoad(ctx) {
62
- await fetch("/api"); // no error handling
63
- }
64
-
65
- // Good
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
- Forbids direct `window.X = ...` or `globalThis.X = ...` assignments. Module exports with esbuild globalName should be used instead.
75
-
76
- ```typescript
77
- // Bad
78
- window.Contoso = { onLoad: function() {} };
79
-
80
- // Good
81
- export function onLoad(ctx: Xrm.Events.EventContext) {}
82
- ```
1
+ # 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
+ // or pick individual rules
12
+ ];
13
+ ```
14
+
15
+ ### 10.2 Rules
16
+
17
+ #### no-xrm-page (error)
18
+
19
+ Forbids the deprecated `Xrm.Page` API (removed in D365 v9.0+).
20
+
21
+ ```typescript
22
+ // Bad
23
+ Xrm.Page.getAttribute("name");
24
+
25
+ // Good
26
+ const form = executionContext.getFormContext();
27
+ form.getAttribute("name");
28
+ ```
29
+
30
+ #### no-magic-optionset (warn)
31
+
32
+ Forbids raw numbers (>= 2) in comparisons with `.getValue()`.
33
+
34
+ ```typescript
35
+ // Bad
36
+ if (attr.getValue() === 595300000) { }
37
+
38
+ // Good
39
+ import { StatusCode } from '../generated/optionsets/account';
40
+ if (attr.getValue() === StatusCode.Active) { }
41
+ ```
42
+
43
+ #### no-sync-webapi (error)
44
+
45
+ Forbids synchronous XMLHttpRequest (`new XMLHttpRequest()` and `.open()` with `async=false`).
46
+
47
+ ```typescript
48
+ // Bad
49
+ xhr.open("GET", url, false);
50
+
51
+ // Good
52
+ const data = await Xrm.WebApi.retrieveRecord("account", id);
53
+ ```
54
+
55
+ #### require-error-handling (warn)
56
+
57
+ Requires try/catch in exported async functions starting with "on" (event handlers).
58
+
59
+ ```typescript
60
+ // Bad
61
+ export async function onLoad(ctx) {
62
+ await fetch("/api"); // no error handling
63
+ }
64
+
65
+ // Good
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
+ Forbids direct `window.X = ...` or `globalThis.X = ...` assignments. Module exports with esbuild globalName should be used instead.
75
+
76
+ ```typescript
77
+ // Bad
78
+ window.Contoso = { onLoad: function() {} };
79
+
80
+ // Good
81
+ export function onLoad(ctx: Xrm.Events.EventContext) {}
82
+ ```
@@ -1,38 +1,38 @@
1
- # AGENT.md System
2
-
3
- ### 11.1 Purpose
4
-
5
- The AGENT.md is a scaffolded file that teaches AI coding assistants (Claude, ChatGPT, Copilot, Cursor) how to write optimal D365 form scripts using XrmForge. It is generated by `xrmforge init` and placed in the project root.
6
-
7
- ### 11.2 Content Structure
8
-
9
- 1. **Package overview** - What each @xrmforge package does
10
- 2. **10 Rules: Always** - Fields Enum, OptionSet Enum, FormContext cast, EntityNames, parseLookup, select, createFormMock, module exports, Tabs/Sections enums, error handling
11
- 3. **Rules: Never** - Raw strings, magic numbers, Xrm.Page, sync XHR, eval, window assignments
12
- 4. **Before/After examples** - Field access, OptionSet comparison, testing
13
- 5. **Pattern Recognition table** - Legacy pattern to XrmForge replacement mapping
14
- 6. **OptionSet Enum creation guide** - How to create enums from magic numbers in legacy code
15
- 7. **Testing with setupXrmMock** - Global Xrm mock pattern
16
- 8. **Build commands** - xrmforge build, watch mode
17
- 9. **@types/xrm Pitfalls** - Known issues and workarounds
18
- 10. **File structure** - Expected project layout
19
-
20
- ### 11.3 Template System
21
-
22
- The AGENT.md is stored as `src/scaffold/templates/AGENT.md` in the devkit package and loaded via `template-loader.ts` at scaffold time. No variable substitution needed (the file is static).
23
-
24
- ### 11.4 KI Comparison Test Results
25
-
26
- Five AI models were tested converting legacy D365 JavaScript (account.js + lm_helper.js, 1,288 lines) to TypeScript with XrmForge:
27
-
28
- | Rank | Model | Score | Tool | Strength |
29
- |------|-------|-------|------|----------|
30
- | 1 | Claude Opus 4.6 | 42/50 | Claude Code | Most tests (62), best code structure |
31
- | 2 | Claude Sonnet 4.6 | 41/50 | Claude Code | Most bugs found (5), best DI approach |
32
- | 3 | Cursor Composer 2 | 35/50 | Cursor IDE | Recognized select() Node API issue |
33
- | 4 | ChatGPT GPT-4o | 30/50 | ChatGPT Web | Functional but less XrmForge-specific |
34
- | 5 | MS Copilot | 12/50 | Browser Chat | No workspace access, never saw AGENT.md |
35
-
36
- **Criteria (11, max 5 points each = 55 max):** Fields Enum usage, OptionSet Enums, FormContext typing, XrmForge helpers, module exports, tests present, test quality, error handling, code quality, bugs found, documentation.
37
-
38
- **Key finding:** No AI consistently used `@xrmforge/helpers` imports (select, parseLookup). This remains the biggest adoption gap.
1
+ # AGENT.md System
2
+
3
+ ### 11.1 Purpose
4
+
5
+ The AGENT.md is a scaffolded file that teaches AI coding assistants (Claude, ChatGPT, Copilot, Cursor) how to write optimal D365 form scripts using XrmForge. It is generated by `xrmforge init` and placed in the project root.
6
+
7
+ ### 11.2 Content Structure
8
+
9
+ 1. **Package overview** - What each @xrmforge package does
10
+ 2. **10 Rules: Always** - Fields Enum, OptionSet Enum, FormContext cast, EntityNames, parseLookup, select, createFormMock, module exports, Tabs/Sections enums, error handling
11
+ 3. **Rules: Never** - Raw strings, magic numbers, Xrm.Page, sync XHR, eval, window assignments
12
+ 4. **Before/After examples** - Field access, OptionSet comparison, testing
13
+ 5. **Pattern Recognition table** - Legacy pattern to XrmForge replacement mapping
14
+ 6. **OptionSet Enum creation guide** - How to create enums from magic numbers in legacy code
15
+ 7. **Testing with setupXrmMock** - Global Xrm mock pattern
16
+ 8. **Build commands** - xrmforge build, watch mode
17
+ 9. **@types/xrm Pitfalls** - Known issues and workarounds
18
+ 10. **File structure** - Expected project layout
19
+
20
+ ### 11.3 Template System
21
+
22
+ The AGENT.md is stored as `src/scaffold/templates/AGENT.md` in the devkit package and loaded via `template-loader.ts` at scaffold time. No variable substitution needed (the file is static).
23
+
24
+ ### 11.4 KI Comparison Test Results
25
+
26
+ Five AI models were tested converting legacy D365 JavaScript (account.js + lm_helper.js, 1,288 lines) to TypeScript with XrmForge:
27
+
28
+ | Rank | Model | Score | Tool | Strength |
29
+ |------|-------|-------|------|----------|
30
+ | 1 | Claude Opus 4.6 | 42/50 | Claude Code | Most tests (62), best code structure |
31
+ | 2 | Claude Sonnet 4.6 | 41/50 | Claude Code | Most bugs found (5), best DI approach |
32
+ | 3 | Cursor Composer 2 | 35/50 | Cursor IDE | Recognized select() Node API issue |
33
+ | 4 | ChatGPT GPT-4o | 30/50 | ChatGPT Web | Functional but less XrmForge-specific |
34
+ | 5 | MS Copilot | 12/50 | Browser Chat | No workspace access, never saw AGENT.md |
35
+
36
+ **Criteria (11, max 5 points each = 55 max):** Fields Enum usage, OptionSet Enums, FormContext typing, XrmForge helpers, module exports, tests present, test quality, error handling, code quality, bugs found, documentation.
37
+
38
+ **Key finding:** No AI consistently used `@xrmforge/helpers` imports (select, parseLookup). This remains the biggest adoption gap.
@@ -1,14 +1,14 @@
1
- # @types/xrm Pitfalls
2
-
3
- Known issues when working with `@types/xrm`:
4
-
5
- | Issue | Wrong | Correct |
6
- |-------|-------|---------|
7
- | Form interface | `interface extends Xrm.FormContext` | `extends Omit<Xrm.FormContext, 'getAttribute' \| 'getControl'>` |
8
- | AlertDialogResponse | `Xrm.Navigation.AlertDialogResponse` | `Xrm.Async.PromiseLike<void>` (type does not exist) |
9
- | ConfirmDialogResponse | `Xrm.Navigation.ConfirmDialogResponse` | `Xrm.Navigation.ConfirmResult` (type does not exist) |
10
- | setNotification | `setNotification(message)` | `setNotification(message, uniqueId)` (requires 2 args) |
11
- | openFile | `openFile({ fileName, ... })` | Must include `fileSize` property in FileDetails |
12
- | SubmitMode | `Xrm.Attributes.SubmitMode` | `Xrm.SubmitMode` |
13
- | const enum in .d.ts | `const enum` in `.d.ts` files | Use `const enum` in `.ts` ES modules (typegen 0.8.0+ generates `.ts` files, resolving this issue) |
14
- | Grid.refresh() | `grid.refresh()` | `(grid as any).refresh()` (not typed in @types/xrm) |
1
+ # @types/xrm Pitfalls
2
+
3
+ Known issues when working with `@types/xrm`:
4
+
5
+ | Issue | Wrong | Correct |
6
+ |-------|-------|---------|
7
+ | Form interface | `interface extends Xrm.FormContext` | `extends Omit<Xrm.FormContext, 'getAttribute' \| 'getControl'>` |
8
+ | AlertDialogResponse | `Xrm.Navigation.AlertDialogResponse` | `Xrm.Async.PromiseLike<void>` (type does not exist) |
9
+ | ConfirmDialogResponse | `Xrm.Navigation.ConfirmDialogResponse` | `Xrm.Navigation.ConfirmResult` (type does not exist) |
10
+ | setNotification | `setNotification(message)` | `setNotification(message, uniqueId)` (requires 2 args) |
11
+ | openFile | `openFile({ fileName, ... })` | Must include `fileSize` property in FileDetails |
12
+ | SubmitMode | `Xrm.Attributes.SubmitMode` | `Xrm.SubmitMode` |
13
+ | const enum in .d.ts | `const enum` in `.d.ts` files | Use `const enum` in `.ts` ES modules (typegen 0.8.0+ generates `.ts` files, resolving this issue) |
14
+ | Grid.refresh() | `grid.refresh()` | `(grid as any).refresh()` (not typed in @types/xrm) |
@@ -1,50 +1,50 @@
1
- # @xrmforge/helpers Package
2
-
3
- ### 13.1 Problem
4
-
5
- The previous approach used a `/helpers` subpath export on `@xrmforge/typegen`. This was confusing because typegen is a Node.js code generation tool, while helpers are browser-safe runtime utilities. The subpath `@xrmforge/typegen/helpers` was non-obvious and AI coding assistants consistently failed to discover it.
6
-
7
- ### 13.2 Solution
8
-
9
- A standalone `@xrmforge/helpers` package consolidates all browser-safe runtime code. Zero Node.js dependencies. Clean, discoverable import path:
10
-
11
- ```typescript
12
- // Import from the dedicated 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` - Builds `?$select=field1,field2`
20
- - `selectExpand(fields: string[], expand: string): string` - Builds `?$select=...&$expand=...`
21
- - `parseLookup(response: Record<string, unknown>, fieldName: string): LookupValue | null` - Parses `_fieldname_value` with OData annotations
22
- - `parseLookups(response: Record<string, unknown>, fieldName: string): LookupValue[]` - Multi-value lookup parsing
23
- - `parseFormattedValue(response: Record<string, unknown>, fieldName: string): string | null` - Extracts `@OData.Community.Display.V1.FormattedValue`
24
-
25
- **Xrm Constants (8 const enums):**
26
- - DisplayState, FormNotificationLevel, RequiredLevel, SubmitMode, SaveMode, ClientType, ClientState, OperationType
27
-
28
- **typedForm() Proxy:**
29
- - `typedForm<TForm>(formContext)` - Returns a proxy where `form.name` delegates to `getAttribute('name')`
30
- - GET trap: Property access delegates to getAttribute(); `$context` returns raw FormContext; `$control(name)` returns getControl()
31
- - SET trap: Throws TypeError forcing `.setValue()` usage
32
- - HAS trap: Checks if attribute exists on the form
33
-
34
- **Action/Function Runtime:**
35
- - `createBoundAction(entityName, actionName)` - Creates a bound action executor
36
- - `executeRequest(request)` - Executes an Organization Request via Xrm.WebApi.online.execute
37
- - `withProgress(message, fn)` - Wraps an async operation with Xrm.Utility.showProgressIndicator
38
-
39
- ### 13.4 Migration
40
-
41
- The old import path `@xrmforge/typegen/helpers` has been removed. Update all imports:
42
-
43
- ```typescript
44
- // Old (removed)
45
- import { select } from '@xrmforge/typegen/helpers';
46
- import { typedForm } from '@xrmforge/formhelpers';
47
-
48
- // New
49
- import { select, typedForm } from '@xrmforge/helpers';
50
- ```
1
+ # @xrmforge/helpers Package
2
+
3
+ ### 13.1 Problem
4
+
5
+ The previous approach used a `/helpers` subpath export on `@xrmforge/typegen`. This was confusing because typegen is a Node.js code generation tool, while helpers are browser-safe runtime utilities. The subpath `@xrmforge/typegen/helpers` was non-obvious and AI coding assistants consistently failed to discover it.
6
+
7
+ ### 13.2 Solution
8
+
9
+ A standalone `@xrmforge/helpers` package consolidates all browser-safe runtime code. Zero Node.js dependencies. Clean, discoverable import path:
10
+
11
+ ```typescript
12
+ // Import from the dedicated 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` - Builds `?$select=field1,field2`
20
+ - `selectExpand(fields: string[], expand: string): string` - Builds `?$select=...&$expand=...`
21
+ - `parseLookup(response: Record<string, unknown>, fieldName: string): LookupValue | null` - Parses `_fieldname_value` with OData annotations
22
+ - `parseLookups(response: Record<string, unknown>, fieldName: string): LookupValue[]` - Multi-value lookup parsing
23
+ - `parseFormattedValue(response: Record<string, unknown>, fieldName: string): string | null` - Extracts `@OData.Community.Display.V1.FormattedValue`
24
+
25
+ **Xrm Constants (8 const enums):**
26
+ - DisplayState, FormNotificationLevel, RequiredLevel, SubmitMode, SaveMode, ClientType, ClientState, OperationType
27
+
28
+ **typedForm() Proxy:**
29
+ - `typedForm<TForm>(formContext)` - Returns a proxy where `form.name` delegates to `getAttribute('name')`
30
+ - GET trap: Property access delegates to getAttribute(); `$context` returns raw FormContext; `$control(name)` returns getControl()
31
+ - SET trap: Throws TypeError forcing `.setValue()` usage
32
+ - HAS trap: Checks if attribute exists on the form
33
+
34
+ **Action/Function Runtime:**
35
+ - `createBoundAction(entityName, actionName)` - Creates a bound action executor
36
+ - `executeRequest(request)` - Executes an Organization Request via Xrm.WebApi.online.execute
37
+ - `withProgress(message, fn)` - Wraps an async operation with Xrm.Utility.showProgressIndicator
38
+
39
+ ### 13.4 Migration
40
+
41
+ The old import path `@xrmforge/typegen/helpers` has been removed. Update all imports:
42
+
43
+ ```typescript
44
+ // Old (removed)
45
+ import { select } from '@xrmforge/typegen/helpers';
46
+ import { typedForm } from '@xrmforge/formhelpers';
47
+
48
+ // New
49
+ import { select, typedForm } from '@xrmforge/helpers';
50
+ ```
@@ -1,21 +1,21 @@
1
- # Showcases
2
-
3
- ### 14.1 Markant WebResources (Production Showcase)
4
-
5
- Located in the XrmForge-Workspace repository under `docs/07_showcase/markant-webresources/`.
6
-
7
- - **30 WebResources** in `src/forms/` (account, contact, opportunity, lead, quote, email, task, etc.)
8
- - **1 shared library** (GDPR retention UI)
9
- - **9 test files** with 59 tests
10
- - **79 generated typings:** 25 form interfaces, 28 entity interfaces, 22 OptionSet files, 4 action executors
11
- - **esbuild build** via xrmforge.config.json (32 entries)
12
- - **Deploy script** (deploy.mjs) with @azure/identity auth, incremental deployment, hash-based change detection
13
- - **27 entities, 236 OptionSet enums, 95 form interfaces, 7 Custom API executors**
14
-
15
- ### 14.2 LMApp WebResources (KI Comparison Showcase)
16
-
17
- Created during the KI comparison tests (Session 9). 18 legacy JavaScript form scripts (~8,400 lines) converted to TypeScript with XrmForge patterns.
18
-
19
- - **19 WebResources** with Fields Enums, EntityNames, OptionSet Enums
20
- - **84 tests** in 8 test files
21
- - **XrmForge-optimized:** All 10 AGENT.md rules applied (FormContext cast, Fields Enum, EntityNames, OptionSet Enums, shared getLookupObject, Tab Enums)
1
+ # Showcases
2
+
3
+ ### 14.1 Markant WebResources (Production Showcase)
4
+
5
+ Located in the XrmForge-Workspace repository under `docs/07_showcase/markant-webresources/`.
6
+
7
+ - **30 WebResources** in `src/forms/` (account, contact, opportunity, lead, quote, email, task, etc.)
8
+ - **1 shared library** (GDPR retention UI)
9
+ - **9 test files** with 59 tests
10
+ - **79 generated typings:** 25 form interfaces, 28 entity interfaces, 22 OptionSet files, 4 action executors
11
+ - **esbuild build** via xrmforge.config.json (32 entries)
12
+ - **Deploy script** (deploy.mjs) with @azure/identity auth, incremental deployment, hash-based change detection
13
+ - **27 entities, 236 OptionSet enums, 95 form interfaces, 7 Custom API executors**
14
+
15
+ ### 14.2 LMApp WebResources (KI Comparison Showcase)
16
+
17
+ Created during the KI comparison tests (Session 9). 18 legacy JavaScript form scripts (~8,400 lines) converted to TypeScript with XrmForge patterns.
18
+
19
+ - **19 WebResources** with Fields Enums, EntityNames, OptionSet Enums
20
+ - **84 tests** in 8 test files
21
+ - **XrmForge-optimized:** All 10 AGENT.md rules applied (FormContext cast, Fields Enum, EntityNames, OptionSet Enums, shared getLookupObject, Tab Enums)
@@ -1,49 +1,49 @@
1
- # CI/CD
2
-
3
- ### 15.1 GitHub Actions CI (`.github/workflows/ci.yml`)
4
-
5
- **Triggers:** Push to main, Pull Requests against main.
6
-
7
- **Matrix:** Node 20, Node 22 on ubuntu-latest.
8
-
9
- **Steps:**
10
- 1. Checkout
11
- 2. Setup pnpm (from packageManager field)
12
- 3. Setup Node.js (matrix version)
13
- 4. `pnpm install --frozen-lockfile`
14
- 5. `pnpm lint`
15
- 6. `pnpm -r exec tsc --noEmit` (typecheck all packages)
16
- 7. `pnpm build`
17
- 8. `pnpm test`
18
- 9. Coverage (Node 22 only): `npx vitest run --coverage` in typegen
19
-
20
- ### 15.2 Release Workflow (`.github/workflows/release.yml`)
21
-
22
- **Triggers:** After successful CI on push to main.
23
-
24
- **Steps:**
25
- 1. Checkout, setup pnpm, setup Node 22
26
- 2. `pnpm install --frozen-lockfile`
27
- 3. `pnpm build`
28
- 4. Changesets action: creates Release PR or publishes to npm
29
-
30
- **Publish command:** `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: (no dependencies)
39
- dev: cache: false, persistent: true
40
- clean: cache: false
41
- ```
42
-
43
- ### 15.4 Changesets
44
-
45
- Configured for public npm access, auto-update internal dependencies on patch level. Publish requires NPM_TOKEN secret.
46
-
47
- ### 15.5 Publishing Order
48
-
49
- Due to internal dependencies: typegen first, then devkit, then cli. Must use `pnpm publish` (not `npm publish`) to resolve `workspace:*` references to real versions.
1
+ # CI/CD
2
+
3
+ ### 15.1 GitHub Actions CI (`.github/workflows/ci.yml`)
4
+
5
+ **Triggers:** Push to main, Pull Requests against main.
6
+
7
+ **Matrix:** Node 20, Node 22 on ubuntu-latest.
8
+
9
+ **Steps:**
10
+ 1. Checkout
11
+ 2. Setup pnpm (from packageManager field)
12
+ 3. Setup Node.js (matrix version)
13
+ 4. `pnpm install --frozen-lockfile`
14
+ 5. `pnpm lint`
15
+ 6. `pnpm -r exec tsc --noEmit` (typecheck all packages)
16
+ 7. `pnpm build`
17
+ 8. `pnpm test`
18
+ 9. Coverage (Node 22 only): `npx vitest run --coverage` in typegen
19
+
20
+ ### 15.2 Release Workflow (`.github/workflows/release.yml`)
21
+
22
+ **Triggers:** After successful CI on push to main.
23
+
24
+ **Steps:**
25
+ 1. Checkout, setup pnpm, setup Node 22
26
+ 2. `pnpm install --frozen-lockfile`
27
+ 3. `pnpm build`
28
+ 4. Changesets action: creates Release PR or publishes to npm
29
+
30
+ **Publish command:** `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: (no dependencies)
39
+ dev: cache: false, persistent: true
40
+ clean: cache: false
41
+ ```
42
+
43
+ ### 15.4 Changesets
44
+
45
+ Configured for public npm access, auto-update internal dependencies on patch level. Publish requires NPM_TOKEN secret.
46
+
47
+ ### 15.5 Publishing Order
48
+
49
+ Due to internal dependencies: typegen first, then devkit, then cli. Must use `pnpm publish` (not `npm publish`) to resolve `workspace:*` references to real versions.
@@ -1,17 +1,17 @@
1
- # Technical Debt
2
-
3
- ### 16.1 Known Issues
4
-
5
- | Issue | Status | Priority |
6
- |-------|--------|----------|
7
- | parseLookup/select not adopted by AI assistants | Open | High |
8
- | release.yml double runs (CI triggers release, release re-triggers CI) | Open | Low |
9
- | No integration tests against live Dataverse | Open (OE-4) | Medium |
10
- | @xrmforge/webapi has no Action/Function support | Accepted | Low |
11
- | devDependency versions in scaffolded package.json are pinned to old versions | Open | Low |
12
-
13
- ### 16.2 Accepted Limitations
14
-
15
- - **const enum limitation:** Resolved in typegen 0.8.0. Generated output is now `.ts` ES modules, so `const enum` works directly with vitest and other test frameworks.
16
- - **Grid.refresh() requires `as any`:** Not typed in @types/xrm.
17
- - **Single solution per entity:** If an entity appears in multiple solutions, it is only generated once.
1
+ # Technical Debt
2
+
3
+ ### 16.1 Known Issues
4
+
5
+ | Issue | Status | Priority |
6
+ |-------|--------|----------|
7
+ | parseLookup/select not adopted by AI assistants | Open | High |
8
+ | release.yml double runs (CI triggers release, release re-triggers CI) | Open | Low |
9
+ | No integration tests against live Dataverse | Open (OE-4) | Medium |
10
+ | @xrmforge/webapi has no Action/Function support | Accepted | Low |
11
+ | devDependency versions in scaffolded package.json are pinned to old versions | Open | Low |
12
+
13
+ ### 16.2 Accepted Limitations
14
+
15
+ - **const enum limitation:** Resolved in typegen 0.8.0. Generated output is now `.ts` ES modules, so `const enum` works directly with vitest and other test frameworks.
16
+ - **Grid.refresh() requires `as any`:** Not typed in @types/xrm.
17
+ - **Single solution per entity:** If an entity appears in multiple solutions, it is only generated once.
@@ -1,25 +1,25 @@
1
- # Roadmap
2
-
3
- ### 17.1 Next Steps (Priority Order)
4
-
5
- 1. **parseLookup/select Adoption** - Improve AGENT.md examples so AI assistants consistently use `/helpers` imports
6
- 2. **LMApp Showcase regeneration** - With latest releases (testing@0.2.0, devkit@0.4.0 with improved AGENT.md)
7
- 3. **KI Battle Round 3** - Re-test Sonnet vs Opus after improvements to measure progress
8
- 4. **Documentation website** - xrmforge.dev or xrmforge.io (OE-3)
9
-
10
- ### 17.2 Open Decisions
11
-
12
- | ID | Decision | Status |
13
- |----|----------|--------|
14
- | OE-1 | npm scope availability (@xrmforge) | Open |
15
- | OE-2 | GitHub org vs personal repo | Decided: personal (juergenbeck/XrmForge) |
16
- | OE-3 | Documentation domain (xrmforge.dev or .io) | Open |
17
- | OE-4 | Dataverse test environment for integration tests | Open |
18
- | OE-5 | Publisher prefix and solution name for PCF/WebResource tests | Open |
19
-
20
- ### 17.3 Future Possibilities
21
-
22
- - Relationship Names const enum (OE-7, low priority)
23
- - @xrmforge/webapi with Action/Function support (reuse DataverseHttpClient)
24
- - Plugin system for custom generators and type mappings
25
- - Server-side generation (Custom API in Dataverse)
1
+ # Roadmap
2
+
3
+ ### 17.1 Next Steps (Priority Order)
4
+
5
+ 1. **parseLookup/select Adoption** - Improve AGENT.md examples so AI assistants consistently use `/helpers` imports
6
+ 2. **LMApp Showcase regeneration** - With latest releases (testing@0.2.0, devkit@0.4.0 with improved AGENT.md)
7
+ 3. **KI Battle Round 3** - Re-test Sonnet vs Opus after improvements to measure progress
8
+ 4. **Documentation website** - xrmforge.dev or xrmforge.io (OE-3)
9
+
10
+ ### 17.2 Open Decisions
11
+
12
+ | ID | Decision | Status |
13
+ |----|----------|--------|
14
+ | OE-1 | npm scope availability (@xrmforge) | Open |
15
+ | OE-2 | GitHub org vs personal repo | Decided: personal (juergenbeck/XrmForge) |
16
+ | OE-3 | Documentation domain (xrmforge.dev or .io) | Open |
17
+ | OE-4 | Dataverse test environment for integration tests | Open |
18
+ | OE-5 | Publisher prefix and solution name for PCF/WebResource tests | Open |
19
+
20
+ ### 17.3 Future Possibilities
21
+
22
+ - Relationship Names const enum (OE-7, low priority)
23
+ - @xrmforge/webapi with Action/Function support (reuse DataverseHttpClient)
24
+ - Plugin system for custom generators and type mappings
25
+ - Server-side generation (Custom API in Dataverse)
@@ -1,22 +1,22 @@
1
- # Design Principles
2
-
3
- The 18 design principles that govern all XrmForge development:
4
-
5
- 1. **Extend, don't replace** - Types build on @types/xrm, never override them.
6
- 2. **TypeScript all the way** - 100% TypeScript-native. No .NET, no ADAL.
7
- 3. **Code must build** - Every work step ends with green build + tests.
8
- 4. **Research before speed** - Investigate, compare, decide, then implement. Never guess.
9
- 5. **No module without basics** - Error handling, logging, unit tests, JSDoc on all public APIs.
10
- 6. **Monorepo discipline** - Each package standalone, no circular deps, barrel exports.
11
- 7. **Enterprise resilience** - Retry + exponential backoff, rate-limit awareness, token caching, read-only default.
12
- 8. **esbuild-first, webpack-compatible** - Default: esbuild (fast). webpack stays supported. IIFE output for D365.
13
- 9. **MSAL-only authentication** - Only @azure/identity (no legacy ADAL). Three flows: client credentials, browser, device code.
14
- 10. **Review required** - After every step, immediate critical review (6 dimensions). No asking if review is wanted.
15
- 11. **Session state required** - session-state.md updated, changelog written, open questions tracked.
16
- 12. **No half measures** - Every step completed fully: green build + tests + review before next step.
17
- 13. **Informed architecture decisions** - Research, compare, recommend with pros/cons, get decision, persist.
18
- 14. **Abstraction over vendor lock-in** - External dependencies behind interfaces (parser, auth, bundler).
19
- 15. **Dual-language labels** - Primary language (1033/English) for identifiers, secondary in JSDoc. German umlauts transliterated.
20
- 16. **Review with research and live verification** - Internet research, live D365 verification, production code checks, cite sources.
21
- 17. **Challenge postponement** - "Later" check: Will it get harder? API contract? Real effort? Technical reasons?
22
- 18. **Read-only default for Dataverse access** - DataverseHttpClient defaults to readOnly: true. Write access is an explicit opt-in.
1
+ # Design Principles
2
+
3
+ The 18 design principles that govern all XrmForge development:
4
+
5
+ 1. **Extend, don't replace** - Types build on @types/xrm, never override them.
6
+ 2. **TypeScript all the way** - 100% TypeScript-native. No .NET, no ADAL.
7
+ 3. **Code must build** - Every work step ends with green build + tests.
8
+ 4. **Research before speed** - Investigate, compare, decide, then implement. Never guess.
9
+ 5. **No module without basics** - Error handling, logging, unit tests, JSDoc on all public APIs.
10
+ 6. **Monorepo discipline** - Each package standalone, no circular deps, barrel exports.
11
+ 7. **Enterprise resilience** - Retry + exponential backoff, rate-limit awareness, token caching, read-only default.
12
+ 8. **esbuild-first, webpack-compatible** - Default: esbuild (fast). webpack stays supported. IIFE output for D365.
13
+ 9. **MSAL-only authentication** - Only @azure/identity (no legacy ADAL). Three flows: client credentials, browser, device code.
14
+ 10. **Review required** - After every step, immediate critical review (6 dimensions). No asking if review is wanted.
15
+ 11. **Session state required** - session-state.md updated, changelog written, open questions tracked.
16
+ 12. **No half measures** - Every step completed fully: green build + tests + review before next step.
17
+ 13. **Informed architecture decisions** - Research, compare, recommend with pros/cons, get decision, persist.
18
+ 14. **Abstraction over vendor lock-in** - External dependencies behind interfaces (parser, auth, bundler).
19
+ 15. **Dual-language labels** - Primary language (1033/English) for identifiers, secondary in JSDoc. German umlauts transliterated.
20
+ 16. **Review with research and live verification** - Internet research, live D365 verification, production code checks, cite sources.
21
+ 17. **Challenge postponement** - "Later" check: Will it get harder? API contract? Real effort? Technical reasons?
22
+ 18. **Read-only default for Dataverse access** - DataverseHttpClient defaults to readOnly: true. Write access is an explicit opt-in.