@xrmforge/devkit 0.7.18 → 0.7.23
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
CHANGED
|
@@ -189,6 +189,45 @@ import { AccountNavigationProperties as AccountNav } from '../../generated/entit
|
|
|
189
189
|
const parent = parseLookup(apiResponse, AccountNav.ParentAccountId);
|
|
190
190
|
```
|
|
191
191
|
|
|
192
|
+
### 5b. Lookup fields: `Fields` enum is `_value`-form, `NavigationProperties` is blank
|
|
193
|
+
|
|
194
|
+
typegen emits TWO enums per entity for lookups, with DIFFERENT values. Picking the wrong one
|
|
195
|
+
compiles green but breaks at runtime (no tsc/eslint gate catches it):
|
|
196
|
+
|
|
197
|
+
| Enum | Value for a lookup (e.g. `transactioncurrencyid`) | Use for |
|
|
198
|
+
|---|---|---|
|
|
199
|
+
| `XxxFields` | `'_transactioncurrencyid_value'` (already `_value`-form) | `$select`, `$filter` |
|
|
200
|
+
| `XxxNavigationProperties` | `'transactioncurrencyid'` (blank) | `parseLookup`, `$expand`, `@odata.bind`, `$unsafe` (lookup) |
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
import { AccountFields } from '../../generated/fields/account.js';
|
|
204
|
+
import { AccountNavigationProperties as AccountNav } from '../../generated/entities/account.js';
|
|
205
|
+
|
|
206
|
+
// $select / $filter: the Fields value is ALREADY _value-form, use it directly
|
|
207
|
+
select(AccountFields.TransactionCurrencyId); // -> "_transactioncurrencyid_value"
|
|
208
|
+
|
|
209
|
+
// parseLookup: the NavigationProperties value (blank), NOT Fields
|
|
210
|
+
const currency = parseLookup(apiResponse, AccountNav.TransactionCurrencyId);
|
|
211
|
+
|
|
212
|
+
// BUG (F-LMA7-05): double _value wrap -> "__transactioncurrencyid_value_value" -> OData 400 at runtime
|
|
213
|
+
const key = `_${AccountFields.TransactionCurrencyId}_value`;
|
|
214
|
+
// BUG: parseLookup with a Fields value (already _value) -> key wrong -> always returns null
|
|
215
|
+
parseLookup(apiResponse, AccountFields.TransactionCurrencyId);
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
- **`$select`/`$filter`:** use the `XxxFields` value DIRECTLY. NEVER wrap it again as
|
|
219
|
+
`` `_${XxxFields.Lookup}_value` `` - the Fields value is already complete.
|
|
220
|
+
- **`parseLookup(response, X)`:** `X` MUST be `XxxNavigationProperties.Lookup` (blank). parseLookup
|
|
221
|
+
builds the key itself as `_${nav}_value`; a `XxxFields` value double-wraps and always returns `null`.
|
|
222
|
+
- **`$unsafe()` on an off-form LOOKUP** uses `XxxNavigationProperties.Lookup` (blank) - it takes an
|
|
223
|
+
attribute logical name, not the `_value` Web API key. `XxxFields.Lookup` (already `_value`-form) is
|
|
224
|
+
not a valid attribute name and resolves to `null` at runtime. For a non-lookup off-form field,
|
|
225
|
+
`XxxFields` is correct (F-LMA7-06).
|
|
226
|
+
- **Never write a local `lookupValue(field)` helper** that puts `_${field}_value` around a `XxxFields`
|
|
227
|
+
value (F-LMA7-05). It is plain string concatenation - green at compile time, broken at runtime.
|
|
228
|
+
- **parseLookup needs the raw response** (`Record<string, unknown>`), not a value cast to a generated
|
|
229
|
+
Entity interface (no index signature). Keep the raw response for parseLookup, cast separately.
|
|
230
|
+
|
|
192
231
|
### 6. select(), $filter, $expand, $orderby with Fields Enums
|
|
193
232
|
|
|
194
233
|
ALL OData query parts must use entity-level Fields Enums. No raw field name strings anywhere.
|
|
@@ -374,6 +413,8 @@ Xrm.Navigation.openForm({ entityName: EntityNames.Account, entityId: id }); //
|
|
|
374
413
|
- Never access WebApi response properties with `as string` casts (use generated Entity interfaces)
|
|
375
414
|
- Never `.getValue()[0].id` for lookups (use `formLookup`/`formLookupId`)
|
|
376
415
|
- Never raw strings in `parseLookup()` (use NavigationProperties enum)
|
|
416
|
+
- Never pass a `XxxFields` value to `parseLookup()` (use `XxxNavigationProperties`; a `XxxFields` value is already `_value`-form, so parseLookup double-wraps the key and always returns `null`)
|
|
417
|
+
- Never wrap a `XxxFields` lookup value again as `` `_${XxxFields.X}_value` `` (it is already `_value`-form; double-wrap -> `__..._value_value` -> OData 400). Use the Fields value directly in `$select`/`$filter`; use `XxxNavigationProperties` for `parseLookup`/`$expand`/`@odata.bind`
|
|
377
418
|
- Never raw strings in `$unsafe()` (use Entity-level Fields Enum: `form.$unsafe(AccountFields.X)`)
|
|
378
419
|
- Never manual OData annotation access (`_value`, `@OData.Community.Display.V1.FormattedValue`, `@Microsoft.Dynamics.CRM.lookuplogicalname`). Use `parseLookup()` which extracts all three.
|
|
379
420
|
|
|
@@ -598,6 +639,7 @@ each attribute to its control. `mock.getControl(Fields.Name)` works out of the b
|
|
|
598
639
|
| `Xrm.WebApi.retrieveRecord("account", id)` | `Xrm.WebApi.retrieveRecord(EntityNames.Account, id)` |
|
|
599
640
|
| `"?$select=name,revenue"` | `select(AccountFields.Name, AccountFields.Revenue)` |
|
|
600
641
|
| `value[0].id.replace("{","")` | `formLookupId(form.customerid)` |
|
|
642
|
+
| `` `_${field}_value` `` hand-built lookup key | `XxxFields.X` directly in `$select`/`$filter` (already `_value`); `XxxNavigationProperties.X` for `parseLookup` |
|
|
601
643
|
| `ExecuteFunctionCall("name", ...)` | `import { Name } from '../../generated/actions/global.js'` |
|
|
602
644
|
| `setFormNotification(msg, 'ERROR', id)` | `setFormNotification(msg, FormNotificationLevel.Error, id)` |
|
|
603
645
|
| `getValue() === 595300000` | `form.statuscode.getValue() === StatusCode.Active` |
|
|
@@ -307,6 +307,26 @@ checkPattern(
|
|
|
307
307
|
['generated/', 'node_modules'],
|
|
308
308
|
);
|
|
309
309
|
|
|
310
|
+
// ── Lookup Convention (Fields vs NavigationProperties, F-LMA7-05) ─────────────
|
|
311
|
+
|
|
312
|
+
// 3q2. Hand-built `_<field>_value` key (Fields enum is already _value-form, never wrap again).
|
|
313
|
+
// Compiles green (plain string concatenation) but produces __..._value_value -> OData 400 at runtime.
|
|
314
|
+
checkPattern(
|
|
315
|
+
'Double _value wrap on a lookup (Fields enum is already _value-form, no _${...}_value)',
|
|
316
|
+
allSrcFiles,
|
|
317
|
+
/_\$\{[^}]*\}_value/,
|
|
318
|
+
['generated/'],
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
// 3q3. parseLookup with a Fields enum value (must use the NavigationProperties enum).
|
|
322
|
+
// parseLookup builds the key itself as _${nav}_value; a Fields value (already _value) yields null.
|
|
323
|
+
checkPattern(
|
|
324
|
+
'parseLookup with a Fields enum (use the NavigationProperties enum instead)',
|
|
325
|
+
allSrcFiles,
|
|
326
|
+
/parseLookup\s*\(\s*\w+\s*,\s*\w*Fields\b/,
|
|
327
|
+
['generated/'],
|
|
328
|
+
);
|
|
329
|
+
|
|
310
330
|
// ── Legacy Helper Wrappers ───────────────────────────────────────────────────
|
|
311
331
|
|
|
312
332
|
// 3r. Forbidden legacy helper functions (must use typedForm + @xrmforge/helpers)
|