@xrmforge/typegen 0.7.1 → 0.8.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
@@ -1295,7 +1295,7 @@ var MetadataCache = class {
1295
1295
  log5.info(`Loaded metadata cache: ${data.manifest.entities.length} entities, last refreshed ${data.manifest.lastRefreshed}`);
1296
1296
  return data;
1297
1297
  } catch (error) {
1298
- if (error.code === "ENOENT") {
1298
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
1299
1299
  log5.info("No metadata cache found, will do full refresh");
1300
1300
  } else {
1301
1301
  log5.warn("Failed to read metadata cache, will do full refresh", {
@@ -1367,7 +1367,7 @@ var MetadataCache = class {
1367
1367
  await fs.unlink(this.cacheFilePath);
1368
1368
  log5.info("Metadata cache cleared");
1369
1369
  } catch (error) {
1370
- if (error.code !== "ENOENT") {
1370
+ if (!(error instanceof Error && "code" in error && error.code === "ENOENT")) {
1371
1371
  throw error;
1372
1372
  }
1373
1373
  }
@@ -1748,36 +1748,33 @@ function shouldIncludeInEntityInterface(attr) {
1748
1748
  }
1749
1749
 
1750
1750
  // src/generators/activity-party.ts
1751
- function generateActivityPartyInterface(namespace = "XrmForge.Entities") {
1751
+ function generateActivityPartyInterface() {
1752
1752
  const lines = [];
1753
- lines.push(`declare namespace ${namespace} {`);
1754
- lines.push("");
1753
+ lines.push("/**");
1754
+ lines.push(" * Activity Party - Teilnehmer einer Aktivit\xE4t (E-Mail, Termin, etc.)");
1755
+ lines.push(" * Wird von PartyList-Feldern (to, from, cc, bcc, requiredattendees) referenziert.");
1756
+ lines.push(" * @see https://learn.microsoft.com/en-us/power-apps/developer/data-platform/reference/entities/activityparty");
1757
+ lines.push(" */");
1758
+ lines.push("export interface ActivityParty {");
1759
+ lines.push(" /** Primary key */");
1760
+ lines.push(" activitypartyid?: string;");
1761
+ lines.push(" /** Referenz auf die zugeh\xF6rige Aktivit\xE4t */");
1762
+ lines.push(" _activityid_value?: string;");
1763
+ lines.push(" /** Referenz auf den Teilnehmer (account | contact | systemuser | queue | knowledgearticle) */");
1764
+ lines.push(" _partyid_value?: string;");
1755
1765
  lines.push(" /**");
1756
- lines.push(" * Activity Party - Teilnehmer einer Aktivit\xE4t (E-Mail, Termin, etc.)");
1757
- lines.push(" * Wird von PartyList-Feldern (to, from, cc, bcc, requiredattendees) referenziert.");
1758
- lines.push(" * @see https://learn.microsoft.com/en-us/power-apps/developer/data-platform/reference/entities/activityparty");
1766
+ lines.push(" * Rolle des Teilnehmers:");
1767
+ lines.push(" * 1=Sender, 2=To, 3=CC, 4=BCC, 5=Required Attendee,");
1768
+ lines.push(" * 6=Optional Attendee, 7=Organizer, 8=Regarding, 9=Owner,");
1769
+ lines.push(" * 10=Resource, 11=Customer, 12=Chat Participant, 13=Related");
1759
1770
  lines.push(" */");
1760
- lines.push(" interface ActivityParty {");
1761
- lines.push(" /** Primary key */");
1762
- lines.push(" activitypartyid?: string;");
1763
- lines.push(" /** Referenz auf die zugeh\xF6rige Aktivit\xE4t */");
1764
- lines.push(" _activityid_value?: string;");
1765
- lines.push(" /** Referenz auf den Teilnehmer (account | contact | systemuser | queue | knowledgearticle) */");
1766
- lines.push(" _partyid_value?: string;");
1767
- lines.push(" /**");
1768
- lines.push(" * Rolle des Teilnehmers:");
1769
- lines.push(" * 1=Sender, 2=To, 3=CC, 4=BCC, 5=Required Attendee,");
1770
- lines.push(" * 6=Optional Attendee, 7=Organizer, 8=Regarding, 9=Owner,");
1771
- lines.push(" * 10=Resource, 11=Customer, 12=Chat Participant, 13=Related");
1772
- lines.push(" */");
1773
- lines.push(" participationtypemask?: number;");
1774
- lines.push(" /** E-Mail-Adresse f\xFCr die Zustellung */");
1775
- lines.push(" addressused?: string;");
1776
- lines.push(" /** Aufwand des Teilnehmers (bei Serviceterminen) */");
1777
- lines.push(" effort?: number;");
1778
- lines.push(" /** Name des Teilnehmers (wenn nicht aufgel\xF6st) */");
1779
- lines.push(" unresolvedpartyname?: string;");
1780
- lines.push(" }");
1771
+ lines.push(" participationtypemask?: number;");
1772
+ lines.push(" /** E-Mail-Adresse f\xFCr die Zustellung */");
1773
+ lines.push(" addressused?: string;");
1774
+ lines.push(" /** Aufwand des Teilnehmers (bei Serviceterminen) */");
1775
+ lines.push(" effort?: number;");
1776
+ lines.push(" /** Name des Teilnehmers (wenn nicht aufgel\xF6st) */");
1777
+ lines.push(" unresolvedpartyname?: string;");
1781
1778
  lines.push("}");
1782
1779
  lines.push("");
1783
1780
  return lines.join("\n");
@@ -1824,16 +1821,18 @@ function disambiguateEnumMembers(members) {
1824
1821
  // src/generators/entity-generator.ts
1825
1822
  function generateEntityInterface(info, options = {}) {
1826
1823
  const labelConfig = options.labelConfig || DEFAULT_LABEL_CONFIG;
1827
- const namespace = options.namespace || "XrmForge.Entities";
1828
1824
  const entityName = toPascalCase(info.entity.LogicalName);
1829
1825
  const entityLabel = getJSDocLabel(info.entity.DisplayName, labelConfig);
1830
1826
  const lines = [];
1831
- lines.push(`declare namespace ${namespace} {`);
1832
- lines.push("");
1827
+ const partyListAttrs = info.attributes.filter((a) => isPartyListType(a.AttributeType));
1828
+ if (partyListAttrs.length > 0) {
1829
+ lines.push("import type { ActivityParty } from './_activity-party.js';");
1830
+ lines.push("");
1831
+ }
1833
1832
  if (entityLabel) {
1834
- lines.push(` /** ${entityLabel} */`);
1833
+ lines.push(`/** ${entityLabel} */`);
1835
1834
  }
1836
- lines.push(` interface ${entityName} {`);
1835
+ lines.push(`export interface ${entityName} {`);
1837
1836
  const includedAttrs = info.attributes.filter(shouldIncludeInEntityInterface).sort((a, b) => a.LogicalName.localeCompare(b.LogicalName));
1838
1837
  const lookupTargets = /* @__PURE__ */ new Map();
1839
1838
  for (const la of info.lookupAttributes) {
@@ -1858,21 +1857,19 @@ function generateEntityInterface(info, options = {}) {
1858
1857
  jsdocParts.push("read-only");
1859
1858
  }
1860
1859
  if (jsdocParts.length > 0) {
1861
- lines.push(` /** ${jsdocParts.join(" - ")} */`);
1860
+ lines.push(` /** ${jsdocParts.join(" - ")} */`);
1862
1861
  }
1863
- lines.push(` ${propertyName}: ${tsType} | null;`);
1862
+ lines.push(` ${propertyName}: ${tsType} | null;`);
1864
1863
  }
1865
- const partyListAttrs = info.attributes.filter((a) => isPartyListType(a.AttributeType));
1866
1864
  if (partyListAttrs.length > 0) {
1867
1865
  const relationship = info.oneToManyRelationships.find(
1868
1866
  (r) => r.ReferencingEntity === "activityparty"
1869
1867
  );
1870
1868
  const navPropName = relationship ? relationship.SchemaName.charAt(0).toLowerCase() + relationship.SchemaName.slice(1) : `${info.entity.LogicalName}_activity_parties`;
1871
1869
  lines.push("");
1872
- lines.push(` /** ActivityParty collection (${partyListAttrs.length} PartyList-Felder: ${partyListAttrs.map((a) => a.LogicalName).join(", ")}) */`);
1873
- lines.push(` ${navPropName}: ActivityParty[] | null;`);
1870
+ lines.push(` /** ActivityParty collection (${partyListAttrs.length} PartyList-Felder: ${partyListAttrs.map((a) => a.LogicalName).join(", ")}) */`);
1871
+ lines.push(` ${navPropName}: ActivityParty[] | null;`);
1874
1872
  }
1875
- lines.push(" }");
1876
1873
  lines.push("}");
1877
1874
  lines.push("");
1878
1875
  return lines.join("\n");
@@ -1881,7 +1878,6 @@ function generateEntityInterface(info, options = {}) {
1881
1878
  // src/generators/optionset-generator.ts
1882
1879
  function generateOptionSetEnum(optionSet, _entityLogicalName, attributeSchemaName, options = {}) {
1883
1880
  const labelConfig = options.labelConfig || DEFAULT_LABEL_CONFIG;
1884
- const namespace = options.namespace || "XrmForge.OptionSets";
1885
1881
  const enumName = optionSet.IsGlobal ? toPascalCase(optionSet.Name) : toPascalCase(attributeSchemaName);
1886
1882
  const rawMembers = optionSet.Options.map((opt) => {
1887
1883
  const label = getPrimaryLabel(opt.Label, labelConfig);
@@ -1897,24 +1893,21 @@ function generateOptionSetEnum(optionSet, _entityLogicalName, attributeSchemaNam
1897
1893
  rawMembers.map((m) => ({ name: m.name, value: m.value }))
1898
1894
  );
1899
1895
  const lines = [];
1900
- lines.push(`declare namespace ${namespace} {`);
1901
- lines.push("");
1902
1896
  const enumLabel = getJSDocLabel(optionSet.DisplayName, labelConfig);
1903
1897
  if (enumLabel) {
1904
- lines.push(` /** ${enumLabel} (${optionSet.Name}) */`);
1898
+ lines.push(`/** ${enumLabel} (${optionSet.Name}) */`);
1905
1899
  }
1906
- lines.push(` const enum ${enumName} {`);
1900
+ lines.push(`export const enum ${enumName} {`);
1907
1901
  for (let i = 0; i < disambiguated.length; i++) {
1908
1902
  const member = disambiguated[i];
1909
1903
  const rawMember = rawMembers[i];
1910
1904
  if (!rawMember) continue;
1911
1905
  const memberLabel = getJSDocLabel(rawMember.option.Label, labelConfig);
1912
1906
  if (memberLabel) {
1913
- lines.push(` /** ${memberLabel} */`);
1907
+ lines.push(` /** ${memberLabel} */`);
1914
1908
  }
1915
- lines.push(` ${member.name} = ${member.value},`);
1909
+ lines.push(` ${member.name} = ${member.value},`);
1916
1910
  }
1917
- lines.push(" }");
1918
1911
  lines.push("}");
1919
1912
  lines.push("");
1920
1913
  return lines.join("\n");
@@ -1979,9 +1972,7 @@ function labelToPascalMember(label) {
1979
1972
  }
1980
1973
  function generateFormInterface(form, entityLogicalName, attributeMap, options = {}, baseNameOverride) {
1981
1974
  const labelConfig = options.labelConfig || DEFAULT_LABEL_CONFIG;
1982
- const namespacePrefix = options.namespacePrefix || "XrmForge.Forms";
1983
1975
  const entityPascal = toPascalCase(entityLogicalName);
1984
- const namespace = `${namespacePrefix}.${entityPascal}`;
1985
1976
  const baseName = baseNameOverride || buildFormBaseName(entityPascal, toSafeFormName(form.name));
1986
1977
  const interfaceName = `${baseName}Form`;
1987
1978
  const fieldsTypeName = `${baseName}FormFields`;
@@ -2021,38 +2012,36 @@ function generateFormInterface(form, entityLogicalName, attributeMap, options =
2021
2012
  });
2022
2013
  }
2023
2014
  const lines = [];
2024
- lines.push(`declare namespace ${namespace} {`);
2025
- lines.push("");
2026
- lines.push(` /** Valid field names for the "${form.name}" form */`);
2027
- lines.push(` type ${fieldsTypeName} =`);
2015
+ lines.push(`/** Valid field names for the "${form.name}" form */`);
2016
+ lines.push(`export type ${fieldsTypeName} =`);
2028
2017
  for (let i = 0; i < fields.length; i++) {
2029
2018
  const separator = i === fields.length - 1 ? ";" : "";
2030
- lines.push(` | "${fields[i].logicalName}"${separator}`);
2019
+ lines.push(` | "${fields[i].logicalName}"${separator}`);
2031
2020
  }
2032
2021
  lines.push("");
2033
- lines.push(` /** Attribute type map for "${form.name}" */`);
2034
- lines.push(` type ${attrMapName} = {`);
2022
+ lines.push(`/** Attribute type map for "${form.name}" */`);
2023
+ lines.push(`export type ${attrMapName} = {`);
2035
2024
  for (const field of fields) {
2036
- lines.push(` ${field.logicalName}: ${field.formAttributeType};`);
2025
+ lines.push(` ${field.logicalName}: ${field.formAttributeType};`);
2037
2026
  }
2038
- lines.push(" };");
2027
+ lines.push("};");
2039
2028
  lines.push("");
2040
- lines.push(` /** Control type map for "${form.name}" */`);
2041
- lines.push(` type ${ctrlMapName} = {`);
2029
+ lines.push(`/** Control type map for "${form.name}" */`);
2030
+ lines.push(`export type ${ctrlMapName} = {`);
2042
2031
  for (const field of fields) {
2043
- lines.push(` ${field.logicalName}: ${field.formControlType};`);
2032
+ lines.push(` ${field.logicalName}: ${field.formControlType};`);
2044
2033
  }
2045
- lines.push(" };");
2034
+ lines.push("};");
2046
2035
  lines.push("");
2047
- lines.push(` /** Field constants for "${form.name}" (compile-time only, zero runtime) */`);
2048
- lines.push(` const enum ${fieldsTypeName}Enum {`);
2036
+ lines.push(`/** Field constants for "${form.name}" (compile-time only, zero runtime) */`);
2037
+ lines.push(`export const enum ${fieldsTypeName}Enum {`);
2049
2038
  for (const field of fields) {
2050
2039
  if (field.label) {
2051
- lines.push(` /** ${field.label} */`);
2040
+ lines.push(` /** ${field.label} */`);
2052
2041
  }
2053
- lines.push(` ${field.enumMemberName} = '${field.logicalName}',`);
2042
+ lines.push(` ${field.enumMemberName} = '${field.logicalName}',`);
2054
2043
  }
2055
- lines.push(" }");
2044
+ lines.push("}");
2056
2045
  lines.push("");
2057
2046
  const namedTabs = form.tabs.filter((t) => t.name);
2058
2047
  if (namedTabs.length > 0) {
@@ -2070,16 +2059,16 @@ function generateFormInterface(form, entityLogicalName, attributeMap, options =
2070
2059
  usedTabMembers.add(memberName);
2071
2060
  tabMemberNames.push(memberName);
2072
2061
  }
2073
- lines.push(` /** Tab constants for "${form.name}" (compile-time only, zero runtime) */`);
2074
- lines.push(` const enum ${tabsEnumName} {`);
2062
+ lines.push(`/** Tab constants for "${form.name}" (compile-time only, zero runtime) */`);
2063
+ lines.push(`export const enum ${tabsEnumName} {`);
2075
2064
  for (let i = 0; i < namedTabs.length; i++) {
2076
2065
  const tab = namedTabs[i];
2077
2066
  if (tab.label) {
2078
- lines.push(` /** ${tab.label} */`);
2067
+ lines.push(` /** ${tab.label} */`);
2079
2068
  }
2080
- lines.push(` ${tabMemberNames[i]} = '${tab.name}',`);
2069
+ lines.push(` ${tabMemberNames[i]} = '${tab.name}',`);
2081
2070
  }
2082
- lines.push(" }");
2071
+ lines.push("}");
2083
2072
  lines.push("");
2084
2073
  for (let i = 0; i < namedTabs.length; i++) {
2085
2074
  const tab = namedTabs[i];
@@ -2087,12 +2076,12 @@ function generateFormInterface(form, entityLogicalName, attributeMap, options =
2087
2076
  if (namedSections.length === 0) continue;
2088
2077
  const tabMemberName = tabMemberNames[i];
2089
2078
  const sectionsEnumName = `${baseName}Form${tabMemberName}Sections`;
2090
- lines.push(` /** Section constants for tab "${tab.name}" (compile-time only, zero runtime) */`);
2091
- lines.push(` const enum ${sectionsEnumName} {`);
2079
+ lines.push(`/** Section constants for tab "${tab.name}" (compile-time only, zero runtime) */`);
2080
+ lines.push(`export const enum ${sectionsEnumName} {`);
2092
2081
  const usedSectionMembers = /* @__PURE__ */ new Set();
2093
2082
  for (const section of namedSections) {
2094
2083
  if (section.label) {
2095
- lines.push(` /** ${section.label} */`);
2084
+ lines.push(` /** ${section.label} */`);
2096
2085
  }
2097
2086
  let sectionMember = toSafeFormName(section.name) || toPascalCase(section.name);
2098
2087
  const originalSectionMember = sectionMember;
@@ -2102,9 +2091,9 @@ function generateFormInterface(form, entityLogicalName, attributeMap, options =
2102
2091
  sCounter++;
2103
2092
  }
2104
2093
  usedSectionMembers.add(sectionMember);
2105
- lines.push(` ${sectionMember} = '${section.name}',`);
2094
+ lines.push(` ${sectionMember} = '${section.name}',`);
2106
2095
  }
2107
- lines.push(" }");
2096
+ lines.push("}");
2108
2097
  lines.push("");
2109
2098
  }
2110
2099
  }
@@ -2113,8 +2102,8 @@ function generateFormInterface(form, entityLogicalName, attributeMap, options =
2113
2102
  const quickViews = specialControls.filter((sc) => sc.controlType === "quickview");
2114
2103
  if (subgrids.length > 0) {
2115
2104
  const subgridsEnumName = `${baseName}FormSubgrids`;
2116
- lines.push(` /** Subgrid constants for "${form.name}" (compile-time only, zero runtime) */`);
2117
- lines.push(` const enum ${subgridsEnumName} {`);
2105
+ lines.push(`/** Subgrid constants for "${form.name}" (compile-time only, zero runtime) */`);
2106
+ lines.push(`export const enum ${subgridsEnumName} {`);
2118
2107
  const usedMembers = /* @__PURE__ */ new Set();
2119
2108
  for (const sg of subgrids) {
2120
2109
  let member = toSafeFormName(sg.id) || toPascalCase(sg.id);
@@ -2126,16 +2115,16 @@ function generateFormInterface(form, entityLogicalName, attributeMap, options =
2126
2115
  }
2127
2116
  usedMembers.add(member);
2128
2117
  const label = sg.targetEntityType ? `Subgrid: ${sg.targetEntityType}` : `Subgrid`;
2129
- lines.push(` /** ${label} */`);
2130
- lines.push(` ${member} = '${sg.id}',`);
2118
+ lines.push(` /** ${label} */`);
2119
+ lines.push(` ${member} = '${sg.id}',`);
2131
2120
  }
2132
- lines.push(" }");
2121
+ lines.push("}");
2133
2122
  lines.push("");
2134
2123
  }
2135
2124
  if (quickViews.length > 0) {
2136
2125
  const qvEnumName = `${baseName}FormQuickViews`;
2137
- lines.push(` /** Quick View constants for "${form.name}" (compile-time only, zero runtime) */`);
2138
- lines.push(` const enum ${qvEnumName} {`);
2126
+ lines.push(`/** Quick View constants for "${form.name}" (compile-time only, zero runtime) */`);
2127
+ lines.push(`export const enum ${qvEnumName} {`);
2139
2128
  const usedMembers = /* @__PURE__ */ new Set();
2140
2129
  for (const qv of quickViews) {
2141
2130
  let member = toSafeFormName(qv.id) || toPascalCase(qv.id);
@@ -2146,66 +2135,65 @@ function generateFormInterface(form, entityLogicalName, attributeMap, options =
2146
2135
  counter++;
2147
2136
  }
2148
2137
  usedMembers.add(member);
2149
- lines.push(` /** Quick View */`);
2150
- lines.push(` ${member} = '${qv.id}',`);
2138
+ lines.push(` /** Quick View */`);
2139
+ lines.push(` ${member} = '${qv.id}',`);
2151
2140
  }
2152
- lines.push(" }");
2141
+ lines.push("}");
2153
2142
  lines.push("");
2154
2143
  }
2155
- lines.push(` /** ${form.name} */`);
2156
- lines.push(` interface ${interfaceName} extends Omit<Xrm.FormContext, 'getAttribute' | 'getControl'> {`);
2157
- lines.push(` /** Typisierter Feldzugriff: nur Felder die auf diesem Formular existieren */`);
2158
- lines.push(` getAttribute<K extends ${fieldsTypeName}>(name: K): ${attrMapName}[K];`);
2159
- lines.push(" getAttribute(index: number): Xrm.Attributes.Attribute;");
2160
- lines.push(" getAttribute(): Xrm.Attributes.Attribute[];");
2144
+ lines.push(`/** ${form.name} */`);
2145
+ lines.push(`export interface ${interfaceName} extends Omit<Xrm.FormContext, 'getAttribute' | 'getControl'> {`);
2146
+ lines.push(` /** Typisierter Feldzugriff: nur Felder die auf diesem Formular existieren */`);
2147
+ lines.push(` getAttribute<K extends ${fieldsTypeName}>(name: K): ${attrMapName}[K];`);
2148
+ lines.push(" getAttribute(index: number): Xrm.Attributes.Attribute;");
2149
+ lines.push(" getAttribute(): Xrm.Attributes.Attribute[];");
2161
2150
  lines.push("");
2162
- lines.push(` /** Typisierter Control-Zugriff: nur Controls die auf diesem Formular existieren */`);
2163
- lines.push(` getControl<K extends ${fieldsTypeName}>(name: K): ${ctrlMapName}[K];`);
2151
+ lines.push(` /** Typisierter Control-Zugriff: nur Controls die auf diesem Formular existieren */`);
2152
+ lines.push(` getControl<K extends ${fieldsTypeName}>(name: K): ${ctrlMapName}[K];`);
2164
2153
  for (const sc of specialControls) {
2165
2154
  const xrmType = specialControlToXrmType(sc.controlType);
2166
2155
  if (xrmType) {
2167
- lines.push(` getControl(name: "${sc.id}"): ${xrmType};`);
2156
+ lines.push(` getControl(name: "${sc.id}"): ${xrmType};`);
2168
2157
  }
2169
2158
  }
2170
- lines.push(" getControl(index: number): Xrm.Controls.Control;");
2171
- lines.push(" getControl(): Xrm.Controls.Control[];");
2159
+ lines.push(" getControl(index: number): Xrm.Controls.Control;");
2160
+ lines.push(" getControl(): Xrm.Controls.Control[];");
2172
2161
  if (form.tabs.length > 0) {
2173
2162
  lines.push("");
2174
- lines.push(" /** Typisierter Tab-Zugriff */");
2175
- lines.push(" ui: {");
2176
- lines.push(" tabs: {");
2163
+ lines.push(" /** Typisierter Tab-Zugriff */");
2164
+ lines.push(" ui: {");
2165
+ lines.push(" tabs: {");
2177
2166
  for (const tab of form.tabs) {
2178
2167
  if (tab.name) {
2179
2168
  const sectionNames = tab.sections.filter((s) => s.name).map((s) => s.name);
2180
2169
  if (sectionNames.length > 0) {
2181
- lines.push(` get(name: "${tab.name}"): Xrm.Controls.Tab & {`);
2182
- lines.push(" sections: {");
2170
+ lines.push(` get(name: "${tab.name}"): Xrm.Controls.Tab & {`);
2171
+ lines.push(" sections: {");
2183
2172
  for (const sectionName of sectionNames) {
2184
- lines.push(` get(name: "${sectionName}"): Xrm.Controls.Section;`);
2173
+ lines.push(` get(name: "${sectionName}"): Xrm.Controls.Section;`);
2185
2174
  }
2186
- lines.push(" get(name: string): Xrm.Controls.Section;");
2187
- lines.push(" };");
2175
+ lines.push(" get(name: string): Xrm.Controls.Section;");
2188
2176
  lines.push(" };");
2177
+ lines.push(" };");
2189
2178
  } else {
2190
- lines.push(` get(name: "${tab.name}"): Xrm.Controls.Tab;`);
2179
+ lines.push(` get(name: "${tab.name}"): Xrm.Controls.Tab;`);
2191
2180
  }
2192
2181
  }
2193
2182
  }
2194
- lines.push(" get(name: string): Xrm.Controls.Tab;");
2195
- lines.push(" };");
2196
- lines.push(" } & Xrm.Ui;");
2183
+ lines.push(" get(name: string): Xrm.Controls.Tab;");
2184
+ lines.push(" };");
2185
+ lines.push(" } & Xrm.Ui;");
2197
2186
  }
2198
- lines.push(" }");
2187
+ lines.push("}");
2199
2188
  const mockValuesName = `${interfaceName}MockValues`;
2200
2189
  lines.push("");
2201
- lines.push(` /** Mock value types for "${form.name}" form (used with @xrmforge/testing) */`);
2202
- lines.push(` type ${mockValuesName} = {`);
2190
+ lines.push(`/** Mock value types for "${form.name}" form (used with @xrmforge/testing) */`);
2191
+ lines.push(`export type ${mockValuesName} = {`);
2203
2192
  for (const field of fields) {
2204
2193
  const mockType = getFormMockValueType(field.attributeType);
2205
- lines.push(` ${field.logicalName}?: ${mockType};`);
2194
+ lines.push(` ${field.logicalName}?: ${mockType};`);
2206
2195
  }
2207
- lines.push(" };");
2208
- lines.push("}");
2196
+ lines.push("};");
2209
2197
  lines.push("");
2210
2198
  return lines.join("\n");
2211
2199
  }
@@ -2256,15 +2244,12 @@ function labelToPascalMember2(label) {
2256
2244
  }
2257
2245
  function generateEntityFieldsEnum(info, options = {}) {
2258
2246
  const labelConfig = options.labelConfig || DEFAULT_LABEL_CONFIG;
2259
- const namespace = options.namespace || "XrmForge.Entities";
2260
2247
  const entityName = toPascalCase(info.entity.LogicalName);
2261
2248
  const enumName = `${entityName}Fields`;
2262
2249
  const includedAttrs = info.attributes.filter(shouldIncludeInEntityInterface).sort((a, b) => a.LogicalName.localeCompare(b.LogicalName));
2263
2250
  const lines = [];
2264
- lines.push(`declare namespace ${namespace} {`);
2265
- lines.push("");
2266
- lines.push(` /** All fields of ${entityName} (for Web API $select queries) */`);
2267
- lines.push(` const enum ${enumName} {`);
2251
+ lines.push(`/** All fields of ${entityName} (for Web API $select queries) */`);
2252
+ lines.push(`export const enum ${enumName} {`);
2268
2253
  const usedNames = /* @__PURE__ */ new Set();
2269
2254
  for (const attr of includedAttrs) {
2270
2255
  const isLookup = isLookupType(attr.AttributeType);
@@ -2283,27 +2268,23 @@ function generateEntityFieldsEnum(info, options = {}) {
2283
2268
  usedNames.add(memberName);
2284
2269
  const dualLabel = getJSDocLabel(attr.DisplayName, labelConfig);
2285
2270
  if (dualLabel) {
2286
- lines.push(` /** ${dualLabel} */`);
2271
+ lines.push(` /** ${dualLabel} */`);
2287
2272
  }
2288
- lines.push(` ${memberName} = '${propertyName}',`);
2273
+ lines.push(` ${memberName} = '${propertyName}',`);
2289
2274
  }
2290
- lines.push(" }");
2291
2275
  lines.push("}");
2292
2276
  lines.push("");
2293
2277
  return lines.join("\n");
2294
2278
  }
2295
2279
  function generateEntityNavigationProperties(info, options = {}) {
2296
2280
  const labelConfig = options.labelConfig || DEFAULT_LABEL_CONFIG;
2297
- const namespace = options.namespace || "XrmForge.Entities";
2298
2281
  const entityName = toPascalCase(info.entity.LogicalName);
2299
2282
  const enumName = `${entityName}NavigationProperties`;
2300
2283
  const lookupAttrs = info.attributes.filter(shouldIncludeInEntityInterface).filter((a) => isLookupType(a.AttributeType)).sort((a, b) => a.LogicalName.localeCompare(b.LogicalName));
2301
2284
  if (lookupAttrs.length === 0) return "";
2302
2285
  const lines = [];
2303
- lines.push(`declare namespace ${namespace} {`);
2304
- lines.push("");
2305
- lines.push(` /** Navigation properties of ${entityName} (for parseLookup and $expand) */`);
2306
- lines.push(` const enum ${enumName} {`);
2286
+ lines.push(`/** Navigation properties of ${entityName} (for parseLookup and $expand) */`);
2287
+ lines.push(`export const enum ${enumName} {`);
2307
2288
  const usedNames = /* @__PURE__ */ new Set();
2308
2289
  for (const attr of lookupAttrs) {
2309
2290
  const primaryLabel = getPrimaryLabel(attr.DisplayName, labelConfig);
@@ -2320,29 +2301,25 @@ function generateEntityNavigationProperties(info, options = {}) {
2320
2301
  usedNames.add(memberName);
2321
2302
  const dualLabel = getJSDocLabel(attr.DisplayName, labelConfig);
2322
2303
  if (dualLabel) {
2323
- lines.push(` /** ${dualLabel} */`);
2304
+ lines.push(` /** ${dualLabel} */`);
2324
2305
  }
2325
- lines.push(` ${memberName} = '${attr.LogicalName}',`);
2306
+ lines.push(` ${memberName} = '${attr.LogicalName}',`);
2326
2307
  }
2327
- lines.push(" }");
2328
2308
  lines.push("}");
2329
2309
  lines.push("");
2330
2310
  return lines.join("\n");
2331
2311
  }
2332
2312
 
2333
2313
  // src/generators/entity-names-generator.ts
2334
- function generateEntityNamesEnum(entityNames, options = {}) {
2335
- const namespace = options.namespace ?? "XrmForge";
2314
+ function generateEntityNamesEnum(entityNames, _options = {}) {
2336
2315
  const sorted = [...entityNames].sort();
2337
2316
  const lines = [];
2338
- lines.push(`declare namespace ${namespace} {`);
2339
- lines.push(" /** Entity logical names for Xrm.WebApi calls (compile-time only, zero runtime) */");
2340
- lines.push(" const enum EntityNames {");
2317
+ lines.push("/** Entity logical names for Xrm.WebApi calls (compile-time only, zero runtime) */");
2318
+ lines.push("export const enum EntityNames {");
2341
2319
  for (const name of sorted) {
2342
2320
  const pascal = toPascalCase(name);
2343
- lines.push(` ${pascal} = '${name}',`);
2321
+ lines.push(` ${pascal} = '${name}',`);
2344
2322
  }
2345
- lines.push(" }");
2346
2323
  lines.push("}");
2347
2324
  lines.push("");
2348
2325
  return lines.join("\n");
@@ -2541,7 +2518,6 @@ function createBoundAction(operationName, entityLogicalName, paramMeta) {
2541
2518
  }
2542
2519
  function createUnboundAction(operationName, paramMeta) {
2543
2520
  return {
2544
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- return type varies (Response or parsed JSON)
2545
2521
  async execute(params) {
2546
2522
  const req = buildUnboundRequest(
2547
2523
  operationName,
@@ -2678,46 +2654,42 @@ function deriveActionName(uniquename) {
2678
2654
  function generateParamsInterface(name, params) {
2679
2655
  if (params.length === 0) return "";
2680
2656
  const lines = [];
2681
- lines.push(` interface ${name}Params {`);
2657
+ lines.push(`export interface ${name}Params {`);
2682
2658
  for (const param of params) {
2683
2659
  const mapped = mapCustomApiParameterType(param.type, param.logicalentityname);
2684
2660
  const optional = param.isoptional ? "?" : "";
2685
2661
  if (param.description) {
2686
- lines.push(` /** ${param.description} */`);
2662
+ lines.push(` /** ${param.description} */`);
2687
2663
  }
2688
- lines.push(` ${param.uniquename}${optional}: ${mapped.tsType};`);
2664
+ lines.push(` ${param.uniquename}${optional}: ${mapped.tsType};`);
2689
2665
  }
2690
- lines.push(" }");
2666
+ lines.push("}");
2691
2667
  return lines.join("\n");
2692
2668
  }
2693
2669
  function generateResultInterface(name, props) {
2694
2670
  if (props.length === 0) return "";
2695
2671
  const lines = [];
2696
- lines.push(` interface ${name}Result {`);
2672
+ lines.push(`export interface ${name}Result {`);
2697
2673
  for (const prop of props) {
2698
2674
  const mapped = mapCustomApiParameterType(prop.type, prop.logicalentityname);
2699
2675
  if (prop.description) {
2700
- lines.push(` /** ${prop.description} */`);
2676
+ lines.push(` /** ${prop.description} */`);
2701
2677
  }
2702
- lines.push(` ${prop.uniquename}: ${mapped.tsType};`);
2678
+ lines.push(` ${prop.uniquename}: ${mapped.tsType};`);
2703
2679
  }
2704
- lines.push(" }");
2680
+ lines.push("}");
2705
2681
  return lines.join("\n");
2706
2682
  }
2707
- function generateActionDeclarations(apis, isFunction, entityName, options = {}) {
2708
- const baseNamespace = isFunction ? options.functionsNamespace || "XrmForge.Functions" : options.actionsNamespace || "XrmForge.Actions";
2709
- const namespace = entityName ? `${baseNamespace}.${toPascalCase(entityName)}` : baseNamespace;
2683
+ function generateActionDeclarations(apis, _isFunction, _entityName, _options = {}) {
2710
2684
  const lines = [];
2711
2685
  lines.push("// Auto-generated by @xrmforge/typegen - DO NOT EDIT");
2712
2686
  lines.push("");
2713
- lines.push(`declare namespace ${namespace} {`);
2714
2687
  for (const apiInfo of apis) {
2715
2688
  const name = deriveActionName(apiInfo.api.uniquename);
2716
2689
  const hasParams = apiInfo.requestParameters.length > 0;
2717
2690
  const hasResult = apiInfo.responseProperties.length > 0;
2718
- lines.push("");
2719
2691
  const description = apiInfo.api.description || apiInfo.api.displayname || apiInfo.api.uniquename;
2720
- lines.push(` /** ${description} (${apiInfo.api.uniquename}) */`);
2692
+ lines.push(`/** ${description} (${apiInfo.api.uniquename}) */`);
2721
2693
  if (hasParams) {
2722
2694
  lines.push(generateParamsInterface(name, apiInfo.requestParameters));
2723
2695
  lines.push("");
@@ -2726,29 +2698,11 @@ function generateActionDeclarations(apis, isFunction, entityName, options = {})
2726
2698
  lines.push(generateResultInterface(name, apiInfo.responseProperties));
2727
2699
  lines.push("");
2728
2700
  }
2729
- const importFrom = options.importPath || "@xrmforge/typegen";
2730
- if (apiInfo.api.bindingtype === 0 /* Global */) {
2731
- if (hasParams && hasResult) {
2732
- lines.push(` const ${name}: import('${importFrom}').UnboundActionWithParamsExecutor<${name}Params, ${name}Result>;`);
2733
- } else if (hasParams) {
2734
- lines.push(` const ${name}: import('${importFrom}').UnboundActionWithParamsExecutor<${name}Params, void>;`);
2735
- } else {
2736
- lines.push(` const ${name}: import('${importFrom}').UnboundActionExecutor;`);
2737
- }
2738
- } else {
2739
- if (hasParams) {
2740
- lines.push(` const ${name}: import('${importFrom}').BoundActionWithParamsExecutor<${name}Params>;`);
2741
- } else {
2742
- lines.push(` const ${name}: import('${importFrom}').BoundActionExecutor;`);
2743
- }
2744
- }
2745
2701
  }
2746
- lines.push("}");
2747
- lines.push("");
2748
2702
  return lines.join("\n");
2749
2703
  }
2750
2704
  function generateActionModule(apis, isFunction, options = {}) {
2751
- const importPath = options.importPath || "@xrmforge/typegen";
2705
+ const importPath = options.importPath || "@xrmforge/helpers";
2752
2706
  const lines = [];
2753
2707
  lines.push("// Auto-generated by @xrmforge/typegen - DO NOT EDIT");
2754
2708
  lines.push("");
@@ -2773,8 +2727,17 @@ function generateActionModule(apis, isFunction, options = {}) {
2773
2727
  for (const apiInfo of apis) {
2774
2728
  const name = deriveActionName(apiInfo.api.uniquename);
2775
2729
  const hasParams = apiInfo.requestParameters.length > 0;
2730
+ const hasResult = apiInfo.responseProperties.length > 0;
2776
2731
  const description = apiInfo.api.description || apiInfo.api.displayname || apiInfo.api.uniquename;
2777
- lines.push(`/** ${description} */`);
2732
+ lines.push(`/** ${description} (${apiInfo.api.uniquename}) */`);
2733
+ if (hasParams) {
2734
+ lines.push(generateParamsInterface(name, apiInfo.requestParameters));
2735
+ lines.push("");
2736
+ }
2737
+ if (hasResult) {
2738
+ lines.push(generateResultInterface(name, apiInfo.responseProperties));
2739
+ lines.push("");
2740
+ }
2778
2741
  if (apiInfo.api.bindingtype === 0 /* Global */) {
2779
2742
  if (isFunction) {
2780
2743
  lines.push(`export const ${name} = createUnboundFunction('${apiInfo.api.uniquename}');`);
@@ -2860,10 +2823,10 @@ async function writeAllFiles(outputDir, files) {
2860
2823
  }
2861
2824
  async function deleteOrphanedFiles(outputDir, deletedEntityNames) {
2862
2825
  let deleted = 0;
2863
- const subdirs = ["entities", "optionsets", "forms"];
2826
+ const subdirs = ["entities", "optionsets", "forms", "fields"];
2864
2827
  for (const entityName of deletedEntityNames) {
2865
2828
  for (const subdir of subdirs) {
2866
- const filePath = join2(outputDir, subdir, `${entityName}.d.ts`);
2829
+ const filePath = join2(outputDir, subdir, `${entityName}.ts`);
2867
2830
  try {
2868
2831
  await unlink(filePath);
2869
2832
  deleted++;
@@ -2885,29 +2848,49 @@ var GENERATED_HEADER = `// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u250
2885
2848
  function addGeneratedHeader(content) {
2886
2849
  return GENERATED_HEADER + content;
2887
2850
  }
2851
+ function toImportSpecifier(relativePath) {
2852
+ const withoutExt = relativePath.replace(/\.ts$/, "");
2853
+ return `./${withoutExt}.js`;
2854
+ }
2888
2855
  function generateBarrelIndex(files) {
2889
2856
  const lines = [GENERATED_HEADER];
2890
2857
  const entities = files.filter((f) => f.type === "entity");
2891
2858
  const optionsets = files.filter((f) => f.type === "optionset");
2892
2859
  const forms = files.filter((f) => f.type === "form");
2860
+ const fields = files.filter((f) => f.type === "fields");
2861
+ const actions = files.filter((f) => f.type === "action");
2893
2862
  if (entities.length > 0) {
2894
2863
  lines.push("// Entity Interfaces");
2895
2864
  for (const f of entities) {
2896
- lines.push(`/// <reference path="${f.relativePath}" />`);
2865
+ lines.push(`export * from '${toImportSpecifier(f.relativePath)}';`);
2897
2866
  }
2898
2867
  lines.push("");
2899
2868
  }
2900
2869
  if (optionsets.length > 0) {
2901
2870
  lines.push("// OptionSet Enums");
2902
2871
  for (const f of optionsets) {
2903
- lines.push(`/// <reference path="${f.relativePath}" />`);
2872
+ lines.push(`export * from '${toImportSpecifier(f.relativePath)}';`);
2904
2873
  }
2905
2874
  lines.push("");
2906
2875
  }
2907
2876
  if (forms.length > 0) {
2908
2877
  lines.push("// Form Interfaces");
2909
2878
  for (const f of forms) {
2910
- lines.push(`/// <reference path="${f.relativePath}" />`);
2879
+ lines.push(`export * from '${toImportSpecifier(f.relativePath)}';`);
2880
+ }
2881
+ lines.push("");
2882
+ }
2883
+ if (fields.length > 0) {
2884
+ lines.push("// Entity Fields & Navigation Properties");
2885
+ for (const f of fields) {
2886
+ lines.push(`export * from '${toImportSpecifier(f.relativePath)}';`);
2887
+ }
2888
+ lines.push("");
2889
+ }
2890
+ if (actions.length > 0) {
2891
+ lines.push("// Custom API Actions & Functions");
2892
+ for (const f of actions) {
2893
+ lines.push(`export * from '${toImportSpecifier(f.relativePath)}';`);
2911
2894
  }
2912
2895
  lines.push("");
2913
2896
  }
@@ -3047,11 +3030,9 @@ var TypeGenerationOrchestrator = class {
3047
3030
  allFiles.push(...actionFiles);
3048
3031
  }
3049
3032
  if (this.config.entities.length > 0) {
3050
- const entityNamesContent = generateEntityNamesEnum(this.config.entities, {
3051
- namespace: this.config.namespacePrefix
3052
- });
3033
+ const entityNamesContent = generateEntityNamesEnum(this.config.entities);
3053
3034
  allFiles.push({
3054
- relativePath: "entity-names.d.ts",
3035
+ relativePath: "entity-names.ts",
3055
3036
  content: addGeneratedHeader(entityNamesContent),
3056
3037
  type: "entity"
3057
3038
  });
@@ -3059,7 +3040,7 @@ var TypeGenerationOrchestrator = class {
3059
3040
  if (allFiles.length > 0) {
3060
3041
  const indexContent = generateBarrelIndex(allFiles);
3061
3042
  const indexFile = {
3062
- relativePath: "index.d.ts",
3043
+ relativePath: "index.ts",
3063
3044
  content: indexContent,
3064
3045
  type: "entity"
3065
3046
  };
@@ -3212,11 +3193,10 @@ var TypeGenerationOrchestrator = class {
3212
3193
  const files = [];
3213
3194
  if (this.config.generateEntities) {
3214
3195
  const entityContent = generateEntityInterface(entityInfo, {
3215
- labelConfig: this.config.labelConfig,
3216
- namespace: `${this.config.namespacePrefix}.Entities`
3196
+ labelConfig: this.config.labelConfig
3217
3197
  });
3218
3198
  files.push({
3219
- relativePath: `entities/${entityName}.d.ts`,
3199
+ relativePath: `entities/${entityName}.ts`,
3220
3200
  content: addGeneratedHeader(entityContent),
3221
3201
  type: "entity"
3222
3202
  });
@@ -3225,13 +3205,12 @@ var TypeGenerationOrchestrator = class {
3225
3205
  const picklistAttrs = this.getPicklistAttributes(entityInfo);
3226
3206
  if (picklistAttrs.length > 0) {
3227
3207
  const optionSets = generateEntityOptionSets(picklistAttrs, entityName, {
3228
- labelConfig: this.config.labelConfig,
3229
- namespace: `${this.config.namespacePrefix}.OptionSets`
3208
+ labelConfig: this.config.labelConfig
3230
3209
  });
3231
3210
  if (optionSets.length > 0) {
3232
3211
  const combinedContent = optionSets.map((os) => os.content).join("\n");
3233
3212
  files.push({
3234
- relativePath: `optionsets/${entityName}.d.ts`,
3213
+ relativePath: `optionsets/${entityName}.ts`,
3235
3214
  content: addGeneratedHeader(combinedContent),
3236
3215
  type: "optionset"
3237
3216
  });
@@ -3247,14 +3226,13 @@ var TypeGenerationOrchestrator = class {
3247
3226
  entityName,
3248
3227
  entityInfo.attributes,
3249
3228
  {
3250
- labelConfig: this.config.labelConfig,
3251
- namespacePrefix: `${this.config.namespacePrefix}.Forms`
3229
+ labelConfig: this.config.labelConfig
3252
3230
  }
3253
3231
  );
3254
3232
  if (formResults.length > 0) {
3255
3233
  const combinedContent = formResults.map((f) => f.content).join("\n");
3256
3234
  files.push({
3257
- relativePath: `forms/${entityName}.d.ts`,
3235
+ relativePath: `forms/${entityName}.ts`,
3258
3236
  content: addGeneratedHeader(combinedContent),
3259
3237
  type: "form"
3260
3238
  });
@@ -3263,6 +3241,21 @@ var TypeGenerationOrchestrator = class {
3263
3241
  warnings.push(`No forms found for ${entityName}`);
3264
3242
  }
3265
3243
  }
3244
+ if (this.config.generateEntities) {
3245
+ const fieldsEnumContent = generateEntityFieldsEnum(entityInfo, {
3246
+ labelConfig: this.config.labelConfig
3247
+ });
3248
+ const navPropsContent = generateEntityNavigationProperties(entityInfo, {
3249
+ labelConfig: this.config.labelConfig
3250
+ });
3251
+ const combinedFieldsContent = navPropsContent ? `${fieldsEnumContent}
3252
+ ${navPropsContent}` : fieldsEnumContent;
3253
+ files.push({
3254
+ relativePath: `fields/${entityName}.ts`,
3255
+ content: addGeneratedHeader(combinedFieldsContent),
3256
+ type: "fields"
3257
+ });
3258
+ }
3266
3259
  return { entityLogicalName: entityName, files, warnings };
3267
3260
  }
3268
3261
  /**
@@ -3285,14 +3278,10 @@ var TypeGenerationOrchestrator = class {
3285
3278
  const entityName = key === "global" ? void 0 : key;
3286
3279
  const declarations = generateActionDeclarations(apis, false, entityName, { importPath });
3287
3280
  const module = generateActionModule(apis, false, { importPath });
3288
- files.push({
3289
- relativePath: `actions/${key}.d.ts`,
3290
- content: addGeneratedHeader(declarations),
3291
- type: "action"
3292
- });
3293
3281
  files.push({
3294
3282
  relativePath: `actions/${key}.ts`,
3295
- content: addGeneratedHeader(module),
3283
+ content: addGeneratedHeader(`${declarations}
3284
+ ${module}`),
3296
3285
  type: "action"
3297
3286
  });
3298
3287
  }
@@ -3300,14 +3289,10 @@ var TypeGenerationOrchestrator = class {
3300
3289
  const entityName = key === "global" ? void 0 : key;
3301
3290
  const declarations = generateActionDeclarations(apis, true, entityName, { importPath });
3302
3291
  const module = generateActionModule(apis, true, { importPath });
3303
- files.push({
3304
- relativePath: `functions/${key}.d.ts`,
3305
- content: addGeneratedHeader(declarations),
3306
- type: "action"
3307
- });
3308
3292
  files.push({
3309
3293
  relativePath: `functions/${key}.ts`,
3310
- content: addGeneratedHeader(module),
3294
+ content: addGeneratedHeader(`${declarations}
3295
+ ${module}`),
3311
3296
  type: "action"
3312
3297
  });
3313
3298
  }