@xrmforge/typegen 0.4.0 → 0.5.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.
package/dist/index.d.ts CHANGED
@@ -330,16 +330,6 @@ declare class DataverseHttpClient {
330
330
  * @param signal - Optional AbortSignal to cancel the request
331
331
  */
332
332
  getAll<T>(path: string, signal?: AbortSignal): Promise<T[]>;
333
- /**
334
- * Execute a POST request that is semantically a read operation.
335
- * Used for Dataverse actions like RetrieveMetadataChanges that require POST
336
- * but do not modify data. Allowed even in read-only mode.
337
- *
338
- * @param path - API path (relative to apiUrl)
339
- * @param body - JSON body to send
340
- * @param signal - Optional AbortSignal to cancel the request
341
- */
342
- postReadOnly<T>(path: string, body: unknown, signal?: AbortSignal): Promise<T>;
343
333
  /**
344
334
  * Returns true if this client is in read-only mode (the safe default).
345
335
  */
package/dist/index.js CHANGED
@@ -404,28 +404,6 @@ var DataverseHttpClient = class {
404
404
  }
405
405
  return allResults;
406
406
  }
407
- /**
408
- * Execute a POST request that is semantically a read operation.
409
- * Used for Dataverse actions like RetrieveMetadataChanges that require POST
410
- * but do not modify data. Allowed even in read-only mode.
411
- *
412
- * @param path - API path (relative to apiUrl)
413
- * @param body - JSON body to send
414
- * @param signal - Optional AbortSignal to cancel the request
415
- */
416
- async postReadOnly(path2, body, signal) {
417
- const ALLOWED_PATHS = ["/RetrieveMetadataChanges"];
418
- const normalizedPath = path2.startsWith("/") ? path2 : `/${path2}`;
419
- if (!ALLOWED_PATHS.some((p) => normalizedPath.startsWith(p))) {
420
- throw new ApiRequestError(
421
- "API_2001" /* API_REQUEST_FAILED */,
422
- `postReadOnly is only allowed for safe read operations: ${ALLOWED_PATHS.join(", ")}. Got: "${normalizedPath}"`,
423
- { path: normalizedPath }
424
- );
425
- }
426
- const url = this.resolveUrl(path2);
427
- return this.executeWithConcurrency(url, signal, "POST", body);
428
- }
429
407
  // ─── Read-Only Enforcement ─────────────────────────────────────────────
430
408
  /**
431
409
  * Returns true if this client is in read-only mode (the safe default).
@@ -514,12 +492,14 @@ var DataverseHttpClient = class {
514
492
  try {
515
493
  tokenResponse = await this.credential.getToken(scope);
516
494
  } catch (error) {
495
+ const cause = error instanceof Error ? error.message : String(error);
517
496
  throw new AuthenticationError(
518
497
  "AUTH_1003" /* AUTH_TOKEN_FAILED */,
519
- `Failed to acquire access token for ${this.baseUrl}. Verify your authentication configuration.`,
498
+ `Failed to acquire access token for ${this.baseUrl}. Verify your authentication configuration.
499
+ Cause: ${cause}`,
520
500
  {
521
501
  environmentUrl: this.baseUrl,
522
- originalError: error instanceof Error ? error.message : String(error)
502
+ originalError: cause
523
503
  }
524
504
  );
525
505
  }
@@ -1421,26 +1401,22 @@ var ChangeDetector = class {
1421
1401
  */
1422
1402
  async detectChanges(clientVersionStamp) {
1423
1403
  log6.info("Detecting metadata changes since last run");
1424
- const requestBody = {
1425
- Query: {
1426
- Criteria: {
1427
- FilterOperator: "And",
1428
- Conditions: []
1429
- },
1430
- Properties: {
1431
- AllProperties: false,
1432
- PropertyNames: ["LogicalName"]
1433
- }
1404
+ const query = {
1405
+ Criteria: {
1406
+ FilterOperator: "And",
1407
+ Conditions: []
1434
1408
  },
1435
- ClientVersionStamp: clientVersionStamp,
1436
- DeletedMetadataFilters: "Entity"
1409
+ Properties: {
1410
+ AllProperties: false,
1411
+ PropertyNames: ["LogicalName"]
1412
+ }
1437
1413
  };
1414
+ const queryJson = encodeURIComponent(JSON.stringify(query));
1415
+ const deletedFilter = `Microsoft.Dynamics.CRM.DeletedMetadataFilters'Default'`;
1416
+ const path2 = `/RetrieveMetadataChanges(Query=@q,ClientVersionStamp=@s,DeletedMetadataFilters=@d)?@q=${queryJson}&@s='${clientVersionStamp}'&@d=${deletedFilter}`;
1438
1417
  let response;
1439
1418
  try {
1440
- response = await this.http.postReadOnly(
1441
- "/RetrieveMetadataChanges",
1442
- requestBody
1443
- );
1419
+ response = await this.http.get(path2);
1444
1420
  } catch (error) {
1445
1421
  if (this.isExpiredVersionStampError(error)) {
1446
1422
  throw new MetadataError(
@@ -1475,22 +1451,19 @@ var ChangeDetector = class {
1475
1451
  */
1476
1452
  async getInitialVersionStamp() {
1477
1453
  log6.info("Fetching initial server version stamp");
1478
- const requestBody = {
1479
- Query: {
1480
- Criteria: {
1481
- FilterOperator: "And",
1482
- Conditions: []
1483
- },
1484
- Properties: {
1485
- AllProperties: false,
1486
- PropertyNames: ["LogicalName"]
1487
- }
1454
+ const query = {
1455
+ Criteria: {
1456
+ FilterOperator: "And",
1457
+ Conditions: []
1458
+ },
1459
+ Properties: {
1460
+ AllProperties: false,
1461
+ PropertyNames: ["LogicalName"]
1488
1462
  }
1489
1463
  };
1490
- const response = await this.http.postReadOnly(
1491
- "/RetrieveMetadataChanges",
1492
- requestBody
1493
- );
1464
+ const queryParam = encodeURIComponent(JSON.stringify(query));
1465
+ const path2 = `/RetrieveMetadataChanges(Query=@q)?@q=${queryParam}`;
1466
+ const response = await this.http.get(path2);
1494
1467
  log6.info("Initial version stamp acquired");
1495
1468
  return response.ServerVersionStamp;
1496
1469
  }
@@ -2135,6 +2108,50 @@ function generateFormInterface(form, entityLogicalName, attributeMap, options =
2135
2108
  lines.push("");
2136
2109
  }
2137
2110
  }
2111
+ const specialControls = form.allSpecialControls || [];
2112
+ const subgrids = specialControls.filter((sc) => sc.controlType === "subgrid" || sc.controlType === "editablegrid");
2113
+ const quickViews = specialControls.filter((sc) => sc.controlType === "quickview");
2114
+ if (subgrids.length > 0) {
2115
+ const subgridsEnumName = `${baseName}FormSubgrids`;
2116
+ lines.push(` /** Subgrid constants for "${form.name}" (compile-time only, zero runtime) */`);
2117
+ lines.push(` const enum ${subgridsEnumName} {`);
2118
+ const usedMembers = /* @__PURE__ */ new Set();
2119
+ for (const sg of subgrids) {
2120
+ let member = toSafeFormName(sg.id) || toPascalCase(sg.id);
2121
+ const original = member;
2122
+ let counter = 2;
2123
+ while (usedMembers.has(member)) {
2124
+ member = `${original}${counter}`;
2125
+ counter++;
2126
+ }
2127
+ usedMembers.add(member);
2128
+ const label = sg.targetEntityType ? `Subgrid: ${sg.targetEntityType}` : `Subgrid`;
2129
+ lines.push(` /** ${label} */`);
2130
+ lines.push(` ${member} = '${sg.id}',`);
2131
+ }
2132
+ lines.push(" }");
2133
+ lines.push("");
2134
+ }
2135
+ if (quickViews.length > 0) {
2136
+ const qvEnumName = `${baseName}FormQuickViews`;
2137
+ lines.push(` /** Quick View constants for "${form.name}" (compile-time only, zero runtime) */`);
2138
+ lines.push(` const enum ${qvEnumName} {`);
2139
+ const usedMembers = /* @__PURE__ */ new Set();
2140
+ for (const qv of quickViews) {
2141
+ let member = toSafeFormName(qv.id) || toPascalCase(qv.id);
2142
+ const original = member;
2143
+ let counter = 2;
2144
+ while (usedMembers.has(member)) {
2145
+ member = `${original}${counter}`;
2146
+ counter++;
2147
+ }
2148
+ usedMembers.add(member);
2149
+ lines.push(` /** Quick View */`);
2150
+ lines.push(` ${member} = '${qv.id}',`);
2151
+ }
2152
+ lines.push(" }");
2153
+ lines.push("");
2154
+ }
2138
2155
  lines.push(` /** ${form.name} */`);
2139
2156
  lines.push(` interface ${interfaceName} extends Omit<Xrm.FormContext, 'getAttribute' | 'getControl'> {`);
2140
2157
  lines.push(` /** Typisierter Feldzugriff: nur Felder die auf diesem Formular existieren */`);
@@ -2144,7 +2161,6 @@ function generateFormInterface(form, entityLogicalName, attributeMap, options =
2144
2161
  lines.push("");
2145
2162
  lines.push(` /** Typisierter Control-Zugriff: nur Controls die auf diesem Formular existieren */`);
2146
2163
  lines.push(` getControl<K extends ${fieldsTypeName}>(name: K): ${ctrlMapName}[K];`);
2147
- const specialControls = form.allSpecialControls || [];
2148
2164
  for (const sc of specialControls) {
2149
2165
  const xrmType = specialControlToXrmType(sc.controlType);
2150
2166
  if (xrmType) {