@xrmforge/devkit 0.5.1 → 0.5.2
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/dist/templates/AGENT.md +83 -17
- package/package.json +1 -1
package/dist/templates/AGENT.md
CHANGED
|
@@ -20,40 +20,106 @@ Run `xrmforge generate` to create:
|
|
|
20
20
|
- `generated/entity-names.ts` - EntityNames const enum
|
|
21
21
|
- `generated/index.ts` - Barrel file with `export * from` re-exports
|
|
22
22
|
|
|
23
|
-
## Rules:
|
|
23
|
+
## Rules: MANDATORY (every violation is a bug)
|
|
24
24
|
|
|
25
|
-
1. **Fields Enum** for getAttribute/getControl
|
|
26
|
-
|
|
25
|
+
1. **Fields Enum** for ALL getAttribute/getControl calls. Never raw strings.
|
|
26
|
+
```typescript
|
|
27
|
+
import { AccountMainFormFieldsEnum as Fields } from '../generated/forms/account.js';
|
|
28
|
+
form.getAttribute(Fields.Name) // CORRECT
|
|
29
|
+
form.getAttribute("name") // BUG - raw string
|
|
30
|
+
```
|
|
27
31
|
|
|
28
|
-
2. **OptionSet Enum** for comparisons
|
|
29
|
-
|
|
32
|
+
2. **OptionSet Enum** for ALL value comparisons. Never magic numbers.
|
|
33
|
+
```typescript
|
|
34
|
+
import { StatusCode } from '../generated/optionsets/invoice.js';
|
|
35
|
+
if (status === StatusCode.Active) // CORRECT
|
|
36
|
+
if (status === 0) // BUG - magic number
|
|
37
|
+
```
|
|
30
38
|
|
|
31
|
-
3. **Cast
|
|
32
|
-
|
|
39
|
+
3. **FormContext Cast** to generated form interface in every onLoad:
|
|
40
|
+
```typescript
|
|
41
|
+
import type { AccountMainForm } from '../generated/forms/account.js';
|
|
42
|
+
const form = ctx.getFormContext() as AccountMainForm;
|
|
43
|
+
```
|
|
33
44
|
|
|
34
|
-
4. **EntityNames Enum**
|
|
35
|
-
|
|
45
|
+
4. **EntityNames Enum** in ALL Xrm.WebApi calls:
|
|
46
|
+
```typescript
|
|
47
|
+
import { EntityNames } from '../generated/entity-names.js';
|
|
48
|
+
Xrm.WebApi.retrieveRecord(EntityNames.Account, id)
|
|
49
|
+
```
|
|
36
50
|
|
|
37
|
-
5. **parseLookup()** from @xrmforge/helpers for lookup
|
|
51
|
+
5. **parseLookup()** from @xrmforge/helpers for ALL lookup value access:
|
|
52
|
+
```typescript
|
|
53
|
+
import { parseLookup } from '@xrmforge/helpers';
|
|
54
|
+
const customer = parseLookup(form.getAttribute(Fields.CustomerId));
|
|
55
|
+
```
|
|
38
56
|
|
|
39
|
-
6. **select()** from @xrmforge/helpers for $select queries
|
|
57
|
+
6. **select()** from @xrmforge/helpers for ALL $select queries:
|
|
58
|
+
```typescript
|
|
59
|
+
import { select } from '@xrmforge/helpers';
|
|
60
|
+
Xrm.WebApi.retrieveRecord(EntityNames.Account, id, select(Fields.Name, Fields.Revenue))
|
|
61
|
+
```
|
|
40
62
|
|
|
41
|
-
7. **
|
|
63
|
+
7. **wrapHandler()** around EVERY exported async event handler:
|
|
64
|
+
```typescript
|
|
65
|
+
import { createLogger } from '../shared/logger';
|
|
66
|
+
import { wrapHandler } from '../shared/error-handler';
|
|
67
|
+
const logger = createLogger('Namespace.Entity');
|
|
68
|
+
export const onLoad = wrapHandler('Namespace.Entity.onLoad', logger, async (ctx) => {
|
|
69
|
+
// handler code
|
|
70
|
+
});
|
|
71
|
+
```
|
|
42
72
|
|
|
43
|
-
8. **
|
|
73
|
+
8. **createFormMock()** from @xrmforge/testing for ALL form tests:
|
|
74
|
+
```typescript
|
|
75
|
+
import { createFormMock, fireOnChange, setupXrmMock } from '@xrmforge/testing';
|
|
76
|
+
```
|
|
44
77
|
|
|
45
|
-
9. **
|
|
78
|
+
9. **Module exports** (not window/global assignments). esbuild globalName handles namespacing.
|
|
46
79
|
|
|
47
|
-
10. **
|
|
80
|
+
10. **Structured Logger** instead of console.* (except in logger.ts itself):
|
|
81
|
+
```typescript
|
|
82
|
+
import { createLogger } from '../shared/logger';
|
|
83
|
+
const logger = createLogger('Namespace.Entity');
|
|
84
|
+
logger.info('Form loaded', { recordId });
|
|
85
|
+
```
|
|
48
86
|
|
|
49
|
-
## Rules:
|
|
87
|
+
## Rules: NEVER (every occurrence is a bug)
|
|
50
88
|
|
|
51
89
|
- Never `getAttribute("raw_string")` when Fields enum exists
|
|
52
|
-
- Never magic numbers for OptionSet values
|
|
90
|
+
- Never magic numbers for OptionSet values (use OptionSet enums)
|
|
53
91
|
- Never `Xrm.Page` (deprecated since D365 v9.0)
|
|
54
92
|
- Never synchronous XMLHttpRequest
|
|
55
93
|
- Never `eval()`
|
|
56
94
|
- Never `window.X = ...` (use module exports)
|
|
95
|
+
- Never `console.log/warn/error` in form scripts (use shared logger)
|
|
96
|
+
- Never export async handlers without wrapHandler()
|
|
97
|
+
- Never `Xrm.WebApi.retrieveRecord("account", ...)` with raw entity name (use EntityNames)
|
|
98
|
+
- Never `"?$select=name,revenue"` as raw string (use select() from @xrmforge/helpers)
|
|
99
|
+
- Never `.getValue()[0].id.replace(...)` for lookups (use parseLookup() from @xrmforge/helpers)
|
|
100
|
+
|
|
101
|
+
## Mandatory Shared Utilities
|
|
102
|
+
|
|
103
|
+
Every XrmForge project MUST have these in `src/shared/`:
|
|
104
|
+
|
|
105
|
+
### logger.ts
|
|
106
|
+
```typescript
|
|
107
|
+
export interface Logger { debug(msg: string, data?: unknown): void; info(...); warn(...); error(...); }
|
|
108
|
+
export function createLogger(namespace: string): Logger;
|
|
109
|
+
// Only file allowed to use console.*
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### error-handler.ts
|
|
113
|
+
```typescript
|
|
114
|
+
export function wrapHandler<T>(name: string, logger: Logger, handler: T): T;
|
|
115
|
+
// Catches sync+async errors, shows form notification, never rethrows
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### constants.ts
|
|
119
|
+
```typescript
|
|
120
|
+
export const NOTIFICATION_IDS = { ... } as const;
|
|
121
|
+
export const MESSAGES = { ... } as const;
|
|
122
|
+
```
|
|
57
123
|
|
|
58
124
|
## Before/After Examples
|
|
59
125
|
|