@xrmforge/typegen 0.1.0 → 0.2.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.js CHANGED
@@ -1155,6 +1155,73 @@ var MetadataClient = class {
1155
1155
  log4.info(`Fetching type info for ${logicalNames.length} entities`);
1156
1156
  return Promise.all(logicalNames.map((name) => this.getEntityTypeInfo(name)));
1157
1157
  }
1158
+ // ─── Custom API Metadata ───────────────────────────────────────────────
1159
+ /**
1160
+ * Fetch all Custom APIs with their request parameters and response properties.
1161
+ *
1162
+ * Queries the customapi, customapirequestparameter, and customapiresponseproperty
1163
+ * tables and joins them into CustomApiTypeInfo objects.
1164
+ *
1165
+ * @param solutionFilter - Optional: filter by solution unique name
1166
+ * @returns Array of complete Custom API definitions
1167
+ */
1168
+ async getCustomApis(solutionFilter) {
1169
+ log4.info("Fetching Custom APIs...");
1170
+ let apiUrl = "/customapis?$select=uniquename,bindingtype,isfunction,boundentitylogicalname,displayname,description";
1171
+ if (solutionFilter) {
1172
+ const safeSolution = DataverseHttpClient.sanitizeIdentifier(solutionFilter);
1173
+ apiUrl += `&$filter=solutionid/uniquename eq '${safeSolution}'`;
1174
+ }
1175
+ const apis = await this.http.get(apiUrl);
1176
+ log4.info(`Found ${apis.value.length} Custom APIs`);
1177
+ if (apis.value.length === 0) return [];
1178
+ const params = await this.http.get(
1179
+ "/customapirequestparameters?$select=uniquename,type,isoptional,logicalentityname,description,_customapiid_value"
1180
+ );
1181
+ const props = await this.http.get(
1182
+ "/customapiresponseproperties?$select=uniquename,type,logicalentityname,description,_customapiid_value"
1183
+ );
1184
+ const paramsByApi = /* @__PURE__ */ new Map();
1185
+ for (const p of params.value) {
1186
+ const apiId = p._customapiid_value;
1187
+ if (!paramsByApi.has(apiId)) paramsByApi.set(apiId, []);
1188
+ paramsByApi.get(apiId).push({
1189
+ uniquename: p.uniquename,
1190
+ type: p.type,
1191
+ isoptional: p.isoptional,
1192
+ logicalentityname: p.logicalentityname,
1193
+ description: p.description
1194
+ });
1195
+ }
1196
+ const propsByApi = /* @__PURE__ */ new Map();
1197
+ for (const p of props.value) {
1198
+ const apiId = p._customapiid_value;
1199
+ if (!propsByApi.has(apiId)) propsByApi.set(apiId, []);
1200
+ propsByApi.get(apiId).push({
1201
+ uniquename: p.uniquename,
1202
+ type: p.type,
1203
+ logicalentityname: p.logicalentityname,
1204
+ description: p.description
1205
+ });
1206
+ }
1207
+ const result = [];
1208
+ for (const api of apis.value) {
1209
+ result.push({
1210
+ api: {
1211
+ uniquename: api.uniquename,
1212
+ bindingtype: api.bindingtype,
1213
+ isfunction: api.isfunction,
1214
+ boundentitylogicalname: api.boundentitylogicalname,
1215
+ displayname: api.displayname,
1216
+ description: api.description
1217
+ },
1218
+ requestParameters: paramsByApi.get(api.customapiid) ?? [],
1219
+ responseProperties: propsByApi.get(api.customapiid) ?? []
1220
+ });
1221
+ }
1222
+ log4.info(`Loaded ${result.length} Custom APIs with parameters and response properties`);
1223
+ return result;
1224
+ }
1158
1225
  // ─── Internal Helpers ──────────────────────────────────────────────────
1159
1226
  async getRelationships(logicalName) {
1160
1227
  const [oneToMany, manyToMany] = await Promise.all([
@@ -1487,6 +1554,37 @@ var FORM_CONTROL_TYPE_MAP = {
1487
1554
  Owner: "Xrm.Controls.LookupControl",
1488
1555
  PartyList: "Xrm.Controls.LookupControl"
1489
1556
  };
1557
+ function getFormMockValueType(attributeType) {
1558
+ const mapping = FORM_MOCK_VALUE_TYPE_MAP[attributeType];
1559
+ if (mapping) return mapping;
1560
+ return "unknown";
1561
+ }
1562
+ var FORM_MOCK_VALUE_TYPE_MAP = {
1563
+ // String types
1564
+ String: "string | null",
1565
+ Memo: "string | null",
1566
+ EntityName: "string | null",
1567
+ // Numeric types
1568
+ Integer: "number | null",
1569
+ BigInt: "number | null",
1570
+ Decimal: "number | null",
1571
+ Double: "number | null",
1572
+ Money: "number | null",
1573
+ // Boolean
1574
+ Boolean: "boolean | null",
1575
+ // OptionSet types (numeric values at runtime)
1576
+ Picklist: "number | null",
1577
+ State: "number | null",
1578
+ Status: "number | null",
1579
+ MultiSelectPicklist: "number[] | null",
1580
+ // Date/Time
1581
+ DateTime: "Date | null",
1582
+ // Lookup types
1583
+ Lookup: "Xrm.LookupValue[] | null",
1584
+ Customer: "Xrm.LookupValue[] | null",
1585
+ Owner: "Xrm.LookupValue[] | null",
1586
+ PartyList: "Xrm.LookupValue[] | null"
1587
+ };
1490
1588
  function toSafeIdentifier(logicalName) {
1491
1589
  if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(logicalName)) {
1492
1590
  return logicalName;
@@ -1531,6 +1629,42 @@ function shouldIncludeInEntityInterface(attr) {
1531
1629
  return true;
1532
1630
  }
1533
1631
 
1632
+ // src/generators/activity-party.ts
1633
+ function generateActivityPartyInterface(namespace = "XrmForge.Entities") {
1634
+ const lines = [];
1635
+ lines.push(`declare namespace ${namespace} {`);
1636
+ lines.push("");
1637
+ lines.push(" /**");
1638
+ lines.push(" * Activity Party - Teilnehmer einer Aktivit\xE4t (E-Mail, Termin, etc.)");
1639
+ lines.push(" * Wird von PartyList-Feldern (to, from, cc, bcc, requiredattendees) referenziert.");
1640
+ lines.push(" * @see https://learn.microsoft.com/en-us/power-apps/developer/data-platform/reference/entities/activityparty");
1641
+ lines.push(" */");
1642
+ lines.push(" interface ActivityParty {");
1643
+ lines.push(" /** Primary key */");
1644
+ lines.push(" activitypartyid?: string;");
1645
+ lines.push(" /** Referenz auf die zugeh\xF6rige Aktivit\xE4t */");
1646
+ lines.push(" _activityid_value?: string;");
1647
+ lines.push(" /** Referenz auf den Teilnehmer (account | contact | systemuser | queue | knowledgearticle) */");
1648
+ lines.push(" _partyid_value?: string;");
1649
+ lines.push(" /**");
1650
+ lines.push(" * Rolle des Teilnehmers:");
1651
+ lines.push(" * 1=Sender, 2=To, 3=CC, 4=BCC, 5=Required Attendee,");
1652
+ lines.push(" * 6=Optional Attendee, 7=Organizer, 8=Regarding, 9=Owner,");
1653
+ lines.push(" * 10=Resource, 11=Customer, 12=Chat Participant, 13=Related");
1654
+ lines.push(" */");
1655
+ lines.push(" participationtypemask?: number;");
1656
+ lines.push(" /** E-Mail-Adresse f\xFCr die Zustellung */");
1657
+ lines.push(" addressused?: string;");
1658
+ lines.push(" /** Aufwand des Teilnehmers (bei Serviceterminen) */");
1659
+ lines.push(" effort?: number;");
1660
+ lines.push(" /** Name des Teilnehmers (wenn nicht aufgel\xF6st) */");
1661
+ lines.push(" unresolvedpartyname?: string;");
1662
+ lines.push(" }");
1663
+ lines.push("}");
1664
+ lines.push("");
1665
+ return lines.join("\n");
1666
+ }
1667
+
1534
1668
  // src/generators/label-utils.ts
1535
1669
  function getSecondaryLabel(label, config) {
1536
1670
  if (!config.secondaryLanguage) return void 0;
@@ -1901,6 +2035,15 @@ function generateFormInterface(form, entityLogicalName, attributeMap, options =
1901
2035
  lines.push(" } & Xrm.Ui;");
1902
2036
  }
1903
2037
  lines.push(" }");
2038
+ const mockValuesName = `${interfaceName}MockValues`;
2039
+ lines.push("");
2040
+ lines.push(` /** Mock value types for "${form.name}" form (used with @xrmforge/testing) */`);
2041
+ lines.push(` type ${mockValuesName} = {`);
2042
+ for (const field of fields) {
2043
+ const mockType = getFormMockValueType(field.attributeType);
2044
+ lines.push(` ${field.logicalName}?: ${mockType};`);
2045
+ }
2046
+ lines.push(" };");
1904
2047
  lines.push("}");
1905
2048
  lines.push("");
1906
2049
  return lines.join("\n");
@@ -1939,6 +2082,569 @@ function generateEntityForms(forms, entityLogicalName, attributes, options = {})
1939
2082
  return results;
1940
2083
  }
1941
2084
 
2085
+ // src/generators/entity-fields-generator.ts
2086
+ function labelToPascalMember2(label) {
2087
+ if (!label) return "";
2088
+ const transliterated = transliterateUmlauts(label);
2089
+ const cleaned = transliterated.replace(/[^a-zA-Z0-9\s_]/g, "");
2090
+ const parts = cleaned.split(/[\s_]+/).filter((p) => p.length > 0);
2091
+ if (parts.length === 0) return "";
2092
+ const pascal = parts.map((p) => p.charAt(0).toUpperCase() + p.slice(1).toLowerCase()).join("");
2093
+ if (/^\d/.test(pascal)) return `_${pascal}`;
2094
+ return pascal;
2095
+ }
2096
+ function generateEntityFieldsEnum(info, options = {}) {
2097
+ const labelConfig = options.labelConfig || DEFAULT_LABEL_CONFIG;
2098
+ const namespace = options.namespace || "XrmForge.Entities";
2099
+ const entityName = toPascalCase(info.entity.LogicalName);
2100
+ const enumName = `${entityName}Fields`;
2101
+ const includedAttrs = info.attributes.filter(shouldIncludeInEntityInterface).sort((a, b) => a.LogicalName.localeCompare(b.LogicalName));
2102
+ const lines = [];
2103
+ lines.push(`declare namespace ${namespace} {`);
2104
+ lines.push("");
2105
+ lines.push(` /** All fields of ${entityName} (for Web API $select queries) */`);
2106
+ lines.push(` const enum ${enumName} {`);
2107
+ const usedNames = /* @__PURE__ */ new Set();
2108
+ for (const attr of includedAttrs) {
2109
+ const isLookup = isLookupType(attr.AttributeType);
2110
+ const propertyName = isLookup ? toLookupValueProperty(attr.LogicalName) : attr.LogicalName;
2111
+ const primaryLabel = getPrimaryLabel(attr.DisplayName, labelConfig);
2112
+ let memberName = labelToPascalMember2(primaryLabel);
2113
+ if (!memberName) {
2114
+ memberName = toPascalCase(attr.LogicalName);
2115
+ }
2116
+ const originalName = memberName;
2117
+ let counter = 2;
2118
+ while (usedNames.has(memberName)) {
2119
+ memberName = `${originalName}${counter}`;
2120
+ counter++;
2121
+ }
2122
+ usedNames.add(memberName);
2123
+ const dualLabel = getJSDocLabel(attr.DisplayName, labelConfig);
2124
+ if (dualLabel) {
2125
+ lines.push(` /** ${dualLabel} */`);
2126
+ }
2127
+ lines.push(` ${memberName} = '${propertyName}',`);
2128
+ }
2129
+ lines.push(" }");
2130
+ lines.push("}");
2131
+ lines.push("");
2132
+ return lines.join("\n");
2133
+ }
2134
+ function generateEntityNavigationProperties(info, options = {}) {
2135
+ const labelConfig = options.labelConfig || DEFAULT_LABEL_CONFIG;
2136
+ const namespace = options.namespace || "XrmForge.Entities";
2137
+ const entityName = toPascalCase(info.entity.LogicalName);
2138
+ const enumName = `${entityName}NavigationProperties`;
2139
+ const lookupAttrs = info.attributes.filter(shouldIncludeInEntityInterface).filter((a) => isLookupType(a.AttributeType)).sort((a, b) => a.LogicalName.localeCompare(b.LogicalName));
2140
+ if (lookupAttrs.length === 0) return "";
2141
+ const lines = [];
2142
+ lines.push(`declare namespace ${namespace} {`);
2143
+ lines.push("");
2144
+ lines.push(` /** Navigation properties of ${entityName} (for parseLookup and $expand) */`);
2145
+ lines.push(` const enum ${enumName} {`);
2146
+ const usedNames = /* @__PURE__ */ new Set();
2147
+ for (const attr of lookupAttrs) {
2148
+ const primaryLabel = getPrimaryLabel(attr.DisplayName, labelConfig);
2149
+ let memberName = labelToPascalMember2(primaryLabel);
2150
+ if (!memberName) {
2151
+ memberName = toPascalCase(attr.LogicalName);
2152
+ }
2153
+ const originalName = memberName;
2154
+ let counter = 2;
2155
+ while (usedNames.has(memberName)) {
2156
+ memberName = `${originalName}${counter}`;
2157
+ counter++;
2158
+ }
2159
+ usedNames.add(memberName);
2160
+ const dualLabel = getJSDocLabel(attr.DisplayName, labelConfig);
2161
+ if (dualLabel) {
2162
+ lines.push(` /** ${dualLabel} */`);
2163
+ }
2164
+ lines.push(` ${memberName} = '${attr.LogicalName}',`);
2165
+ }
2166
+ lines.push(" }");
2167
+ lines.push("}");
2168
+ lines.push("");
2169
+ return lines.join("\n");
2170
+ }
2171
+
2172
+ // src/generators/webapi-helpers.ts
2173
+ function select(...fields) {
2174
+ if (fields.length === 0) return "";
2175
+ return `?$select=${fields.join(",")}`;
2176
+ }
2177
+ function parseLookup(response, navigationProperty) {
2178
+ const key = `_${navigationProperty}_value`;
2179
+ const id = response[key];
2180
+ if (!id) return null;
2181
+ return {
2182
+ id,
2183
+ name: response[`${key}@OData.Community.Display.V1.FormattedValue`] ?? "",
2184
+ entityType: response[`${key}@Microsoft.Dynamics.CRM.lookuplogicalname`] ?? ""
2185
+ };
2186
+ }
2187
+ function parseLookups(response, navigationProperties) {
2188
+ const result = {};
2189
+ for (const prop of navigationProperties) {
2190
+ result[prop] = parseLookup(response, prop);
2191
+ }
2192
+ return result;
2193
+ }
2194
+ function parseFormattedValue(response, fieldName) {
2195
+ return response[`${fieldName}@OData.Community.Display.V1.FormattedValue`] ?? null;
2196
+ }
2197
+ function selectExpand(fields, expand) {
2198
+ const parts = [];
2199
+ if (fields.length > 0) parts.push(`$select=${fields.join(",")}`);
2200
+ if (expand) parts.push(`$expand=${expand}`);
2201
+ return parts.length > 0 ? `?${parts.join("&")}` : "";
2202
+ }
2203
+
2204
+ // src/generators/xrm-constants.ts
2205
+ var DisplayState = /* @__PURE__ */ ((DisplayState2) => {
2206
+ DisplayState2["Expanded"] = "expanded";
2207
+ DisplayState2["Collapsed"] = "collapsed";
2208
+ return DisplayState2;
2209
+ })(DisplayState || {});
2210
+ var FormNotificationLevel = /* @__PURE__ */ ((FormNotificationLevel2) => {
2211
+ FormNotificationLevel2["Error"] = "ERROR";
2212
+ FormNotificationLevel2["Warning"] = "WARNING";
2213
+ FormNotificationLevel2["Info"] = "INFO";
2214
+ return FormNotificationLevel2;
2215
+ })(FormNotificationLevel || {});
2216
+ var RequiredLevel = /* @__PURE__ */ ((RequiredLevel2) => {
2217
+ RequiredLevel2["None"] = "none";
2218
+ RequiredLevel2["Required"] = "required";
2219
+ RequiredLevel2["Recommended"] = "recommended";
2220
+ return RequiredLevel2;
2221
+ })(RequiredLevel || {});
2222
+ var SubmitMode = /* @__PURE__ */ ((SubmitMode2) => {
2223
+ SubmitMode2["Always"] = "always";
2224
+ SubmitMode2["Never"] = "never";
2225
+ SubmitMode2["Dirty"] = "dirty";
2226
+ return SubmitMode2;
2227
+ })(SubmitMode || {});
2228
+ var SaveMode = /* @__PURE__ */ ((SaveMode2) => {
2229
+ SaveMode2[SaveMode2["Save"] = 1] = "Save";
2230
+ SaveMode2[SaveMode2["SaveAndClose"] = 2] = "SaveAndClose";
2231
+ SaveMode2[SaveMode2["Deactivate"] = 5] = "Deactivate";
2232
+ SaveMode2[SaveMode2["Reactivate"] = 6] = "Reactivate";
2233
+ SaveMode2[SaveMode2["Send"] = 7] = "Send";
2234
+ SaveMode2[SaveMode2["Disqualify"] = 15] = "Disqualify";
2235
+ SaveMode2[SaveMode2["Qualify"] = 16] = "Qualify";
2236
+ SaveMode2[SaveMode2["Assign"] = 47] = "Assign";
2237
+ SaveMode2[SaveMode2["SaveAsCompleted"] = 58] = "SaveAsCompleted";
2238
+ SaveMode2[SaveMode2["SaveAndNew"] = 59] = "SaveAndNew";
2239
+ SaveMode2[SaveMode2["AutoSave"] = 70] = "AutoSave";
2240
+ return SaveMode2;
2241
+ })(SaveMode || {});
2242
+ var ClientType = /* @__PURE__ */ ((ClientType2) => {
2243
+ ClientType2["Web"] = "Web";
2244
+ ClientType2["Outlook"] = "Outlook";
2245
+ ClientType2["Mobile"] = "Mobile";
2246
+ return ClientType2;
2247
+ })(ClientType || {});
2248
+ var ClientState = /* @__PURE__ */ ((ClientState2) => {
2249
+ ClientState2["Online"] = "Online";
2250
+ ClientState2["Offline"] = "Offline";
2251
+ return ClientState2;
2252
+ })(ClientState || {});
2253
+ var OperationType = /* @__PURE__ */ ((OperationType2) => {
2254
+ OperationType2[OperationType2["Action"] = 0] = "Action";
2255
+ OperationType2[OperationType2["Function"] = 1] = "Function";
2256
+ OperationType2[OperationType2["CRUD"] = 2] = "CRUD";
2257
+ return OperationType2;
2258
+ })(OperationType || {});
2259
+ var StructuralProperty = /* @__PURE__ */ ((StructuralProperty2) => {
2260
+ StructuralProperty2[StructuralProperty2["Unknown"] = 0] = "Unknown";
2261
+ StructuralProperty2[StructuralProperty2["PrimitiveType"] = 1] = "PrimitiveType";
2262
+ StructuralProperty2[StructuralProperty2["ComplexType"] = 2] = "ComplexType";
2263
+ StructuralProperty2[StructuralProperty2["EnumerationType"] = 3] = "EnumerationType";
2264
+ StructuralProperty2[StructuralProperty2["Collection"] = 4] = "Collection";
2265
+ StructuralProperty2[StructuralProperty2["EntityType"] = 5] = "EntityType";
2266
+ return StructuralProperty2;
2267
+ })(StructuralProperty || {});
2268
+ var BindingType = /* @__PURE__ */ ((BindingType2) => {
2269
+ BindingType2[BindingType2["Global"] = 0] = "Global";
2270
+ BindingType2[BindingType2["Entity"] = 1] = "Entity";
2271
+ BindingType2[BindingType2["EntityCollection"] = 2] = "EntityCollection";
2272
+ return BindingType2;
2273
+ })(BindingType || {});
2274
+
2275
+ // src/generators/action-runtime.ts
2276
+ function executeRequest(request) {
2277
+ return Xrm.WebApi.online.execute(request);
2278
+ }
2279
+ function executeMultiple(requests) {
2280
+ return Xrm.WebApi.online.executeMultiple(requests);
2281
+ }
2282
+ function cleanRecordId(id) {
2283
+ return id.replace(/[{}]/g, "");
2284
+ }
2285
+ function buildBoundRequest(operationName, entityLogicalName, operationType, recordId, paramMeta, params) {
2286
+ const parameterTypes = {
2287
+ entity: {
2288
+ typeName: `mscrm.${entityLogicalName}`,
2289
+ structuralProperty: 5 /* EntityType */
2290
+ }
2291
+ };
2292
+ if (paramMeta) {
2293
+ for (const [key, meta] of Object.entries(paramMeta)) {
2294
+ parameterTypes[key] = meta;
2295
+ }
2296
+ }
2297
+ const request = {
2298
+ getMetadata: () => ({
2299
+ boundParameter: "entity",
2300
+ parameterTypes,
2301
+ operationName,
2302
+ operationType
2303
+ }),
2304
+ entity: {
2305
+ id: cleanRecordId(recordId),
2306
+ entityType: entityLogicalName
2307
+ }
2308
+ };
2309
+ if (params) {
2310
+ for (const [key, value] of Object.entries(params)) {
2311
+ request[key] = value;
2312
+ }
2313
+ }
2314
+ return request;
2315
+ }
2316
+ function buildUnboundRequest(operationName, operationType, paramMeta, params) {
2317
+ const parameterTypes = {};
2318
+ if (paramMeta) {
2319
+ for (const [key, meta] of Object.entries(paramMeta)) {
2320
+ parameterTypes[key] = meta;
2321
+ }
2322
+ }
2323
+ const request = {
2324
+ getMetadata: () => ({
2325
+ boundParameter: null,
2326
+ parameterTypes,
2327
+ operationName,
2328
+ operationType
2329
+ })
2330
+ };
2331
+ if (params) {
2332
+ for (const [key, value] of Object.entries(params)) {
2333
+ request[key] = value;
2334
+ }
2335
+ }
2336
+ return request;
2337
+ }
2338
+ function createBoundAction(operationName, entityLogicalName, paramMeta) {
2339
+ return {
2340
+ execute(recordId, params) {
2341
+ const req = buildBoundRequest(
2342
+ operationName,
2343
+ entityLogicalName,
2344
+ 0 /* Action */,
2345
+ recordId,
2346
+ paramMeta,
2347
+ params
2348
+ );
2349
+ return executeRequest(req);
2350
+ },
2351
+ request(recordId, params) {
2352
+ return buildBoundRequest(
2353
+ operationName,
2354
+ entityLogicalName,
2355
+ 0 /* Action */,
2356
+ recordId,
2357
+ paramMeta,
2358
+ params
2359
+ );
2360
+ }
2361
+ };
2362
+ }
2363
+ function createUnboundAction(operationName, paramMeta) {
2364
+ return {
2365
+ async execute(params) {
2366
+ const req = buildUnboundRequest(
2367
+ operationName,
2368
+ 0 /* Action */,
2369
+ paramMeta,
2370
+ params
2371
+ );
2372
+ const response = await executeRequest(req);
2373
+ if (!response.ok) {
2374
+ const errorText = await response.text();
2375
+ throw new Error(errorText);
2376
+ }
2377
+ if (response.status !== 204) {
2378
+ return response.json();
2379
+ }
2380
+ return response;
2381
+ },
2382
+ request(params) {
2383
+ return buildUnboundRequest(
2384
+ operationName,
2385
+ 0 /* Action */,
2386
+ paramMeta,
2387
+ params
2388
+ );
2389
+ }
2390
+ };
2391
+ }
2392
+ function createUnboundFunction(operationName) {
2393
+ return {
2394
+ async execute() {
2395
+ const req = buildUnboundRequest(operationName, 1 /* Function */);
2396
+ const response = await executeRequest(req);
2397
+ if (!response.ok) {
2398
+ const errorText = await response.text();
2399
+ throw new Error(errorText);
2400
+ }
2401
+ return response.json();
2402
+ },
2403
+ request() {
2404
+ return buildUnboundRequest(operationName, 1 /* Function */);
2405
+ }
2406
+ };
2407
+ }
2408
+ function createBoundFunction(operationName, entityLogicalName) {
2409
+ return {
2410
+ async execute(recordId) {
2411
+ const req = buildBoundRequest(
2412
+ operationName,
2413
+ entityLogicalName,
2414
+ 1 /* Function */,
2415
+ recordId
2416
+ );
2417
+ const response = await executeRequest(req);
2418
+ if (!response.ok) {
2419
+ const errorText = await response.text();
2420
+ throw new Error(errorText);
2421
+ }
2422
+ return response.json();
2423
+ },
2424
+ request(recordId) {
2425
+ return buildBoundRequest(
2426
+ operationName,
2427
+ entityLogicalName,
2428
+ 1 /* Function */,
2429
+ recordId
2430
+ );
2431
+ }
2432
+ };
2433
+ }
2434
+ async function withProgress(message, operation) {
2435
+ Xrm.Utility.showProgressIndicator(message);
2436
+ try {
2437
+ return await operation();
2438
+ } catch (error) {
2439
+ const msg = error instanceof Error ? error.message : String(error);
2440
+ Xrm.Navigation.openErrorDialog({ message: msg });
2441
+ throw error;
2442
+ } finally {
2443
+ Xrm.Utility.closeProgressIndicator();
2444
+ }
2445
+ }
2446
+
2447
+ // src/metadata/custom-api-types.ts
2448
+ function mapCustomApiParameterType(type, entityName) {
2449
+ switch (type) {
2450
+ case 0 /* Boolean */:
2451
+ return { tsType: "boolean", typeName: "Edm.Boolean", structuralProperty: 1 };
2452
+ case 1 /* DateTime */:
2453
+ return { tsType: "string", typeName: "Edm.DateTimeOffset", structuralProperty: 1 };
2454
+ case 2 /* Decimal */:
2455
+ return { tsType: "number", typeName: "Edm.Decimal", structuralProperty: 1 };
2456
+ case 3 /* Entity */:
2457
+ return {
2458
+ tsType: "Record<string, unknown>",
2459
+ typeName: `mscrm.${entityName || "crmbaseentity"}`,
2460
+ structuralProperty: 5
2461
+ };
2462
+ case 4 /* EntityCollection */:
2463
+ return {
2464
+ tsType: "Array<Record<string, unknown>>",
2465
+ typeName: "Collection(mscrm.crmbaseentity)",
2466
+ structuralProperty: 4
2467
+ };
2468
+ case 5 /* EntityReference */:
2469
+ return {
2470
+ tsType: "{ id: string; entityType: string; name?: string }",
2471
+ typeName: `mscrm.${entityName || "crmbaseentity"}`,
2472
+ structuralProperty: 5
2473
+ };
2474
+ case 6 /* Float */:
2475
+ return { tsType: "number", typeName: "Edm.Double", structuralProperty: 1 };
2476
+ case 7 /* Integer */:
2477
+ return { tsType: "number", typeName: "Edm.Int32", structuralProperty: 1 };
2478
+ case 8 /* Money */:
2479
+ return { tsType: "number", typeName: "Edm.Decimal", structuralProperty: 1 };
2480
+ case 9 /* Picklist */:
2481
+ return { tsType: "number", typeName: "Edm.Int32", structuralProperty: 1 };
2482
+ case 10 /* String */:
2483
+ return { tsType: "string", typeName: "Edm.String", structuralProperty: 1 };
2484
+ case 11 /* StringArray */:
2485
+ return { tsType: "string[]", typeName: "Collection(Edm.String)", structuralProperty: 4 };
2486
+ case 12 /* Guid */:
2487
+ return { tsType: "string", typeName: "Edm.Guid", structuralProperty: 1 };
2488
+ default:
2489
+ return { tsType: "unknown", typeName: "Edm.String", structuralProperty: 0 };
2490
+ }
2491
+ }
2492
+
2493
+ // src/generators/action-generator.ts
2494
+ function deriveActionName(uniquename) {
2495
+ const withoutPrefix = uniquename.includes("_") ? uniquename.substring(uniquename.indexOf("_") + 1) : uniquename;
2496
+ return toPascalCase(withoutPrefix);
2497
+ }
2498
+ function generateParamsInterface(name, params) {
2499
+ if (params.length === 0) return "";
2500
+ const lines = [];
2501
+ lines.push(` interface ${name}Params {`);
2502
+ for (const param of params) {
2503
+ const mapped = mapCustomApiParameterType(param.type, param.logicalentityname);
2504
+ const optional = param.isoptional ? "?" : "";
2505
+ if (param.description) {
2506
+ lines.push(` /** ${param.description} */`);
2507
+ }
2508
+ lines.push(` ${param.uniquename}${optional}: ${mapped.tsType};`);
2509
+ }
2510
+ lines.push(" }");
2511
+ return lines.join("\n");
2512
+ }
2513
+ function generateResultInterface(name, props) {
2514
+ if (props.length === 0) return "";
2515
+ const lines = [];
2516
+ lines.push(` interface ${name}Result {`);
2517
+ for (const prop of props) {
2518
+ const mapped = mapCustomApiParameterType(prop.type, prop.logicalentityname);
2519
+ if (prop.description) {
2520
+ lines.push(` /** ${prop.description} */`);
2521
+ }
2522
+ lines.push(` ${prop.uniquename}: ${mapped.tsType};`);
2523
+ }
2524
+ lines.push(" }");
2525
+ return lines.join("\n");
2526
+ }
2527
+ function generateActionDeclarations(apis, isFunction, entityName, options = {}) {
2528
+ const baseNamespace = isFunction ? options.functionsNamespace || "XrmForge.Functions" : options.actionsNamespace || "XrmForge.Actions";
2529
+ const namespace = entityName ? `${baseNamespace}.${toPascalCase(entityName)}` : baseNamespace;
2530
+ const lines = [];
2531
+ lines.push("// Auto-generated by @xrmforge/typegen - DO NOT EDIT");
2532
+ lines.push("");
2533
+ lines.push(`declare namespace ${namespace} {`);
2534
+ for (const apiInfo of apis) {
2535
+ const name = deriveActionName(apiInfo.api.uniquename);
2536
+ const hasParams = apiInfo.requestParameters.length > 0;
2537
+ const hasResult = apiInfo.responseProperties.length > 0;
2538
+ lines.push("");
2539
+ const description = apiInfo.api.description || apiInfo.api.displayname || apiInfo.api.uniquename;
2540
+ lines.push(` /** ${description} (${apiInfo.api.uniquename}) */`);
2541
+ if (hasParams) {
2542
+ lines.push(generateParamsInterface(name, apiInfo.requestParameters));
2543
+ lines.push("");
2544
+ }
2545
+ if (hasResult) {
2546
+ lines.push(generateResultInterface(name, apiInfo.responseProperties));
2547
+ lines.push("");
2548
+ }
2549
+ const importFrom = options.importPath || "@xrmforge/typegen";
2550
+ if (apiInfo.api.bindingtype === 0 /* Global */) {
2551
+ if (hasParams && hasResult) {
2552
+ lines.push(` const ${name}: import('${importFrom}').UnboundActionWithParamsExecutor<${name}Params, ${name}Result>;`);
2553
+ } else if (hasParams) {
2554
+ lines.push(` const ${name}: import('${importFrom}').UnboundActionWithParamsExecutor<${name}Params, void>;`);
2555
+ } else {
2556
+ lines.push(` const ${name}: import('${importFrom}').UnboundActionExecutor;`);
2557
+ }
2558
+ } else {
2559
+ if (hasParams) {
2560
+ lines.push(` const ${name}: import('${importFrom}').BoundActionWithParamsExecutor<${name}Params>;`);
2561
+ } else {
2562
+ lines.push(` const ${name}: import('${importFrom}').BoundActionExecutor;`);
2563
+ }
2564
+ }
2565
+ }
2566
+ lines.push("}");
2567
+ lines.push("");
2568
+ return lines.join("\n");
2569
+ }
2570
+ function generateActionModule(apis, isFunction, options = {}) {
2571
+ const importPath = options.importPath || "@xrmforge/typegen";
2572
+ const lines = [];
2573
+ lines.push("// Auto-generated by @xrmforge/typegen - DO NOT EDIT");
2574
+ lines.push("");
2575
+ const imports = /* @__PURE__ */ new Set();
2576
+ for (const apiInfo of apis) {
2577
+ if (apiInfo.api.bindingtype === 0 /* Global */) {
2578
+ if (isFunction) {
2579
+ imports.add("createUnboundFunction");
2580
+ } else {
2581
+ imports.add("createUnboundAction");
2582
+ }
2583
+ } else {
2584
+ if (isFunction) {
2585
+ imports.add("createBoundFunction");
2586
+ } else {
2587
+ imports.add("createBoundAction");
2588
+ }
2589
+ }
2590
+ }
2591
+ lines.push(`import { ${[...imports].sort().join(", ")} } from '${importPath}';`);
2592
+ lines.push("");
2593
+ for (const apiInfo of apis) {
2594
+ const name = deriveActionName(apiInfo.api.uniquename);
2595
+ const hasParams = apiInfo.requestParameters.length > 0;
2596
+ const description = apiInfo.api.description || apiInfo.api.displayname || apiInfo.api.uniquename;
2597
+ lines.push(`/** ${description} */`);
2598
+ if (apiInfo.api.bindingtype === 0 /* Global */) {
2599
+ if (isFunction) {
2600
+ lines.push(`export const ${name} = createUnboundFunction('${apiInfo.api.uniquename}');`);
2601
+ } else if (hasParams) {
2602
+ const paramMeta = generateParameterMetaMap(apiInfo.requestParameters);
2603
+ lines.push(`export const ${name} = createUnboundAction('${apiInfo.api.uniquename}', ${paramMeta});`);
2604
+ } else {
2605
+ lines.push(`export const ${name} = createUnboundAction('${apiInfo.api.uniquename}');`);
2606
+ }
2607
+ } else {
2608
+ const entity = apiInfo.api.boundentitylogicalname;
2609
+ if (isFunction) {
2610
+ lines.push(`export const ${name} = createBoundFunction('${apiInfo.api.uniquename}', '${entity}');`);
2611
+ } else if (hasParams) {
2612
+ const paramMeta = generateParameterMetaMap(apiInfo.requestParameters);
2613
+ lines.push(`export const ${name} = createBoundAction('${apiInfo.api.uniquename}', '${entity}', ${paramMeta});`);
2614
+ } else {
2615
+ lines.push(`export const ${name} = createBoundAction('${apiInfo.api.uniquename}', '${entity}');`);
2616
+ }
2617
+ }
2618
+ lines.push("");
2619
+ }
2620
+ return lines.join("\n");
2621
+ }
2622
+ function generateParameterMetaMap(params) {
2623
+ const entries = [];
2624
+ for (const param of params) {
2625
+ const mapped = mapCustomApiParameterType(param.type, param.logicalentityname);
2626
+ entries.push(
2627
+ ` ${param.uniquename}: { typeName: '${mapped.typeName}', structuralProperty: ${mapped.structuralProperty} }`
2628
+ );
2629
+ }
2630
+ return `{
2631
+ ${entries.join(",\n")},
2632
+ }`;
2633
+ }
2634
+ function groupCustomApis(apis) {
2635
+ const actions = /* @__PURE__ */ new Map();
2636
+ const functions = /* @__PURE__ */ new Map();
2637
+ for (const api of apis) {
2638
+ const target = api.api.isfunction ? functions : actions;
2639
+ const key = api.api.bindingtype === 0 /* Global */ ? "global" : api.api.boundentitylogicalname || "global";
2640
+ if (!target.has(key)) {
2641
+ target.set(key, []);
2642
+ }
2643
+ target.get(key).push(api);
2644
+ }
2645
+ return { actions, functions };
2646
+ }
2647
+
1942
2648
  // src/orchestrator/file-writer.ts
1943
2649
  import { mkdir, writeFile, readFile } from "fs/promises";
1944
2650
  import { join as join2, dirname } from "path";
@@ -2032,6 +2738,7 @@ var TypeGenerationOrchestrator = class {
2032
2738
  generateEntities: config.generateEntities ?? true,
2033
2739
  generateForms: config.generateForms ?? true,
2034
2740
  generateOptionSets: config.generateOptionSets ?? true,
2741
+ generateActions: config.generateActions ?? false,
2035
2742
  useCache: config.useCache ?? false,
2036
2743
  cacheDir: config.cacheDir ?? ".xrmforge/cache",
2037
2744
  namespacePrefix: config.namespacePrefix ?? "XrmForge"
@@ -2106,6 +2813,47 @@ var TypeGenerationOrchestrator = class {
2106
2813
  });
2107
2814
  }
2108
2815
  }
2816
+ if (this.config.generateActions && !signal?.aborted) {
2817
+ this.logger.info("Fetching Custom APIs...");
2818
+ const customApis = await metadataClient.getCustomApis();
2819
+ if (customApis.length > 0) {
2820
+ const importPath = "@xrmforge/typegen";
2821
+ const grouped = groupCustomApis(customApis);
2822
+ for (const [key, apis] of grouped.actions) {
2823
+ const entityName = key === "global" ? void 0 : key;
2824
+ const declarations = generateActionDeclarations(apis, false, entityName, { importPath });
2825
+ const module = generateActionModule(apis, false, { importPath });
2826
+ allFiles.push({
2827
+ relativePath: `actions/${key}.d.ts`,
2828
+ content: addGeneratedHeader(declarations),
2829
+ type: "action"
2830
+ });
2831
+ allFiles.push({
2832
+ relativePath: `actions/${key}.ts`,
2833
+ content: addGeneratedHeader(module),
2834
+ type: "action"
2835
+ });
2836
+ }
2837
+ for (const [key, apis] of grouped.functions) {
2838
+ const entityName = key === "global" ? void 0 : key;
2839
+ const declarations = generateActionDeclarations(apis, true, entityName, { importPath });
2840
+ const module = generateActionModule(apis, true, { importPath });
2841
+ allFiles.push({
2842
+ relativePath: `functions/${key}.d.ts`,
2843
+ content: addGeneratedHeader(declarations),
2844
+ type: "action"
2845
+ });
2846
+ allFiles.push({
2847
+ relativePath: `functions/${key}.ts`,
2848
+ content: addGeneratedHeader(module),
2849
+ type: "action"
2850
+ });
2851
+ }
2852
+ this.logger.info(`Generated ${grouped.actions.size} action groups, ${grouped.functions.size} function groups`);
2853
+ } else {
2854
+ this.logger.info("No Custom APIs found");
2855
+ }
2856
+ }
2109
2857
  if (allFiles.length > 0) {
2110
2858
  const indexContent = generateBarrelIndex(allFiles);
2111
2859
  const indexFile = {
@@ -2232,12 +2980,17 @@ var TypeGenerationOrchestrator = class {
2232
2980
  export {
2233
2981
  ApiRequestError,
2234
2982
  AuthenticationError,
2983
+ BindingType,
2984
+ ClientState,
2985
+ ClientType,
2235
2986
  ConfigError,
2236
2987
  ConsoleLogSink,
2237
2988
  DEFAULT_LABEL_CONFIG,
2238
2989
  DataverseHttpClient,
2990
+ DisplayState,
2239
2991
  ErrorCode,
2240
2992
  FastXmlParser,
2993
+ FormNotificationLevel,
2241
2994
  GenerationError,
2242
2995
  JsonLogSink,
2243
2996
  LogLevel,
@@ -2245,18 +2998,34 @@ export {
2245
2998
  MetadataCache,
2246
2999
  MetadataClient,
2247
3000
  MetadataError,
3001
+ OperationType,
3002
+ RequiredLevel,
3003
+ SaveMode,
2248
3004
  SilentLogSink,
3005
+ StructuralProperty,
3006
+ SubmitMode,
2249
3007
  TypeGenerationOrchestrator,
2250
3008
  XrmForgeError,
2251
3009
  configureLogging,
3010
+ createBoundAction,
3011
+ createBoundFunction,
2252
3012
  createCredential,
2253
3013
  createLogger,
3014
+ createUnboundAction,
3015
+ createUnboundFunction,
2254
3016
  defaultXmlParser,
2255
3017
  disambiguateEnumMembers,
3018
+ executeMultiple,
3019
+ executeRequest,
2256
3020
  extractControlFields,
2257
3021
  getJSDocLabel as formatDualLabel,
3022
+ generateActionDeclarations,
3023
+ generateActionModule,
3024
+ generateActivityPartyInterface,
3025
+ generateEntityFieldsEnum,
2258
3026
  generateEntityForms,
2259
3027
  generateEntityInterface,
3028
+ generateEntityNavigationProperties,
2260
3029
  generateEntityOptionSets,
2261
3030
  generateEnumMembers,
2262
3031
  generateFormInterface,
@@ -2264,18 +3033,27 @@ export {
2264
3033
  getEntityPropertyType,
2265
3034
  getFormAttributeType,
2266
3035
  getFormControlType,
3036
+ getFormMockValueType,
2267
3037
  getJSDocLabel,
2268
3038
  getLabelLanguagesParam,
2269
3039
  getPrimaryLabel,
2270
3040
  getSecondaryLabel,
3041
+ groupCustomApis,
2271
3042
  isLookupType,
3043
+ isPartyListType,
2272
3044
  isRateLimitError,
2273
3045
  isXrmForgeError,
2274
3046
  labelToIdentifier,
2275
3047
  parseForm,
3048
+ parseFormattedValue,
3049
+ parseLookup,
3050
+ parseLookups,
3051
+ select,
3052
+ selectExpand,
2276
3053
  shouldIncludeInEntityInterface,
2277
3054
  toLookupValueProperty,
2278
3055
  toPascalCase,
2279
- toSafeIdentifier
3056
+ toSafeIdentifier,
3057
+ withProgress
2280
3058
  };
2281
3059
  //# sourceMappingURL=index.js.map