@khester/create-dynamics-app 1.1.0 → 2.1.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.
Files changed (210) hide show
  1. package/README.md +74 -0
  2. package/dist/artifacts/registry.d.ts +18 -0
  3. package/dist/artifacts/registry.d.ts.map +1 -0
  4. package/dist/artifacts/registry.js +340 -0
  5. package/dist/artifacts/registry.js.map +1 -0
  6. package/dist/artifacts/types.d.ts +122 -0
  7. package/dist/artifacts/types.d.ts.map +1 -0
  8. package/dist/artifacts/types.js +7 -0
  9. package/dist/artifacts/types.js.map +1 -0
  10. package/dist/artifacts/validators.d.ts +16 -0
  11. package/dist/artifacts/validators.d.ts.map +1 -0
  12. package/dist/artifacts/validators.js +45 -0
  13. package/dist/artifacts/validators.js.map +1 -0
  14. package/dist/fromDesign.d.ts +5 -0
  15. package/dist/fromDesign.d.ts.map +1 -0
  16. package/dist/fromDesign.js +98 -0
  17. package/dist/fromDesign.js.map +1 -0
  18. package/dist/index.js +129 -177
  19. package/dist/index.js.map +1 -1
  20. package/dist/injectDevTools.d.ts +28 -0
  21. package/dist/injectDevTools.d.ts.map +1 -0
  22. package/dist/injectDevTools.js +148 -0
  23. package/dist/injectDevTools.js.map +1 -0
  24. package/dist/scaffold.d.ts +48 -0
  25. package/dist/scaffold.d.ts.map +1 -0
  26. package/dist/scaffold.js +180 -0
  27. package/dist/scaffold.js.map +1 -0
  28. package/dist/templatePlan.d.ts +3 -0
  29. package/dist/templatePlan.d.ts.map +1 -0
  30. package/dist/templatePlan.js +43 -0
  31. package/dist/templatePlan.js.map +1 -0
  32. package/dist/utils/copyTemplate.d.ts +13 -1
  33. package/dist/utils/copyTemplate.d.ts.map +1 -1
  34. package/dist/utils/copyTemplate.js +98 -4
  35. package/dist/utils/copyTemplate.js.map +1 -1
  36. package/dist/utils/updatePackageJson.d.ts +11 -1
  37. package/dist/utils/updatePackageJson.d.ts.map +1 -1
  38. package/dist/utils/updatePackageJson.js +12 -10
  39. package/dist/utils/updatePackageJson.js.map +1 -1
  40. package/package.json +10 -7
  41. package/templates/_shared/dev-tools/auth/get-token.js +72 -0
  42. package/templates/_shared/dev-tools/dev/mock-xrm.js +42 -0
  43. package/templates/_shared/dev-tools/metadata-sync/index.js +152 -0
  44. package/templates/_shared/dev-tools/smoke/test-retrieve.js +44 -0
  45. package/templates/dialog-form/README.md +27 -0
  46. package/templates/dialog-form/_variants/App.v8.tsx +39 -0
  47. package/templates/dialog-form/_variants/App.v9.tsx +41 -0
  48. package/templates/dialog-form/gitignore +5 -0
  49. package/templates/dialog-form/package.json +27 -0
  50. package/templates/dialog-form/public/index.html +11 -0
  51. package/templates/dialog-form/src/index.tsx +10 -0
  52. package/templates/dialog-form/src/services/dataverse.ts +30 -0
  53. package/templates/dialog-form/tsconfig.json +15 -0
  54. package/templates/dialog-form/webpack.config.js +17 -0
  55. package/templates/grid-customizer/README.md +28 -0
  56. package/templates/grid-customizer/gitignore +4 -0
  57. package/templates/grid-customizer/package.json +25 -0
  58. package/templates/grid-customizer/src/GridCustomizer.ts +28 -0
  59. package/templates/grid-customizer/src/cell-renderers.tsx +35 -0
  60. package/templates/grid-customizer/src/index.ts +4 -0
  61. package/templates/grid-customizer/src/types/grid-types.ts +30 -0
  62. package/templates/grid-customizer/src/utils/color-utils.ts +24 -0
  63. package/templates/grid-customizer/tsconfig.json +15 -0
  64. package/templates/grid-customizer/webpack.config.js +17 -0
  65. package/templates/pcf-dataset/ControlManifest.Input.xml +16 -0
  66. package/templates/pcf-dataset/README.md +21 -0
  67. package/templates/pcf-dataset/gitignore +5 -0
  68. package/templates/pcf-dataset/index.ts +39 -0
  69. package/templates/pcf-dataset/package.json +30 -0
  70. package/templates/pcf-dataset/strings/{{componentName}}.1033.resx +47 -0
  71. package/templates/pcf-dataset/tsconfig.json +8 -0
  72. package/templates/pcf-dataset/{{componentName}}Component.tsx +39 -0
  73. package/templates/pcf-field/ControlManifest.Input.xml +17 -0
  74. package/templates/pcf-field/README.md +95 -0
  75. package/templates/pcf-field/_variants/ValueInput.boolean.tsx +24 -0
  76. package/templates/pcf-field/_variants/ValueInput.date.tsx +27 -0
  77. package/templates/pcf-field/_variants/ValueInput.number.tsx +35 -0
  78. package/templates/pcf-field/_variants/ValueInput.text.tsx +27 -0
  79. package/templates/pcf-field/gitignore +5 -0
  80. package/templates/pcf-field/index.ts +61 -0
  81. package/templates/pcf-field/package.json +30 -0
  82. package/templates/pcf-field/strings/{{componentName}}.1033.resx +47 -0
  83. package/templates/pcf-field/tsconfig.json +8 -0
  84. package/templates/pcf-field/{{componentName}}Component.tsx +35 -0
  85. package/templates/power-pages-starter/gitignore +5 -0
  86. package/templates/react-custom-page/gitignore +5 -0
  87. package/templates/{dynamics-365-starter → react-custom-page}/package.json +3 -3
  88. package/templates/react-custom-page/tools/metadata-sync/index.js +152 -0
  89. package/templates/static-web-app/README.md +36 -0
  90. package/templates/static-web-app/_variants/App.v8.tsx +32 -0
  91. package/templates/static-web-app/_variants/App.v9.tsx +31 -0
  92. package/templates/static-web-app/api/host.json +12 -0
  93. package/templates/static-web-app/api/package.json +19 -0
  94. package/templates/static-web-app/api/src/functions/hello.ts +16 -0
  95. package/templates/static-web-app/api/tsconfig.json +14 -0
  96. package/templates/static-web-app/frontend/index.html +12 -0
  97. package/templates/static-web-app/frontend/package.json +23 -0
  98. package/templates/static-web-app/frontend/src/index.tsx +8 -0
  99. package/templates/static-web-app/frontend/tsconfig.json +16 -0
  100. package/templates/static-web-app/frontend/vite.config.ts +13 -0
  101. package/templates/static-web-app/gitignore +8 -0
  102. package/templates/static-web-app/package.json +15 -0
  103. package/templates/static-web-app/staticwebapp.config.json +7 -0
  104. package/templates/teams-app/README.md +27 -0
  105. package/templates/teams-app/_variants/graph.off.ts +7 -0
  106. package/templates/teams-app/_variants/graph.on.ts +22 -0
  107. package/templates/teams-app/appPackage/manifest.json +26 -0
  108. package/templates/teams-app/gitignore +5 -0
  109. package/templates/teams-app/index.html +12 -0
  110. package/templates/teams-app/package.json +26 -0
  111. package/templates/teams-app/src/App.tsx +25 -0
  112. package/templates/teams-app/src/index.tsx +8 -0
  113. package/templates/teams-app/tsconfig.json +16 -0
  114. package/templates/teams-app/vite.config.ts +9 -0
  115. package/templates/web-resource/README.md +39 -0
  116. package/templates/web-resource/_variants/App.v8.tsx +29 -0
  117. package/templates/web-resource/_variants/App.v9.tsx +28 -0
  118. package/templates/web-resource/gitignore +5 -0
  119. package/templates/web-resource/package.json +27 -0
  120. package/templates/web-resource/public/index.html +11 -0
  121. package/templates/web-resource/src/index.tsx +10 -0
  122. package/templates/web-resource/src/services/dataverse.ts +30 -0
  123. package/templates/web-resource/tsconfig.json +15 -0
  124. package/templates/web-resource/webpack.config.js +17 -0
  125. package/dist/utils/consultingHelpers.d.ts +0 -13
  126. package/dist/utils/consultingHelpers.d.ts.map +0 -1
  127. package/dist/utils/consultingHelpers.js +0 -569
  128. package/dist/utils/consultingHelpers.js.map +0 -1
  129. package/templates/dynamics-365-starter/INTEGRATION_TEST_RESULTS.md +0 -302
  130. package/templates/dynamics-365-starter/PHASE_4_COMPLETION_SUMMARY.md +0 -305
  131. package/templates/dynamics-365-starter/deployment/QUICKSTART-MAC.md +0 -507
  132. package/templates/dynamics-365-starter/deployment/QUICKSTART-WINDOWS.md +0 -372
  133. package/templates/dynamics-365-starter/deployment/pipelines/README.md +0 -375
  134. package/templates/dynamics-365-starter/deployment/pipelines/azure-pipelines.yml +0 -330
  135. package/templates/dynamics-365-starter/deployment/pipelines/github-actions.yml +0 -422
  136. package/templates/dynamics-365-starter/deployment/pipelines/jenkins.groovy +0 -636
  137. package/templates/dynamics-365-starter/deployment/scripts/deploy.ps1 +0 -417
  138. package/templates/dynamics-365-starter/deployment/scripts/deploy.sh +0 -582
  139. package/templates/dynamics-365-starter/deployment/scripts/team-onboarding.ps1 +0 -486
  140. package/templates/dynamics-365-starter/deployment/scripts/team-onboarding.sh +0 -567
  141. package/templates/dynamics-365-starter/deployment/scripts/validate-setup.ps1 +0 -703
  142. package/templates/dynamics-365-starter/deployment/scripts/validate-setup.sh +0 -671
  143. package/templates/dynamics-365-starter/docs/team-standards/README.md +0 -273
  144. package/templates/dynamics-365-starter/docs/team-standards/client-onboarding.md +0 -577
  145. package/templates/dynamics-365-starter/docs/team-standards/code-review-checklist.md +0 -359
  146. package/templates/dynamics-365-starter/docs/team-standards/coding-standards.md +0 -700
  147. package/templates/dynamics-365-starter/docs/team-standards/cross-platform-team-guide.md +0 -736
  148. package/templates/dynamics-365-starter/docs/team-standards/development-workflows.md +0 -727
  149. package/templates/dynamics-365-starter/docs/troubleshooting/common-errors.md +0 -758
  150. package/templates/dynamics-365-starter/docs/troubleshooting/platform-specific-issues.md +0 -878
  151. package/templates/dynamics-365-starter/src/client-project-template/README.md +0 -234
  152. package/templates/dynamics-365-starter/src/client-project-template/config/client.template.json +0 -114
  153. package/templates/dynamics-365-starter/src/client-project-template/config/environments/template.json +0 -186
  154. package/templates/dynamics-365-starter/src/client-project-template/scripts/client-setup.js +0 -667
  155. package/templates/dynamics-365-starter/src/examples/README.md +0 -52
  156. package/templates/dynamics-365-starter/src/examples/component-examples/opportunity-management.tsx +0 -625
  157. package/templates/dynamics-365-starter/src/examples/entity-examples/opportunity-model.ts +0 -545
  158. package/templates/dynamics-365-starter/src/examples/integration-examples/custom-pcf-wrapper.tsx +0 -722
  159. package/templates/dynamics-365-starter/src/examples/workflow-examples/sales-workflow.ts +0 -662
  160. package/templates/dynamics-365-starter/src/page-templates/EntityDashboard.tsx +0 -519
  161. package/templates/dynamics-365-starter/src/page-templates/EntityDetailPage.tsx +0 -456
  162. package/templates/dynamics-365-starter/src/page-templates/EntityListPage.tsx +0 -406
  163. package/templates/dynamics-365-starter/src/page-templates/RelatedEntitiesPage.tsx +0 -578
  164. package/templates/dynamics-365-starter/src/page-templates/SearchPage.tsx +0 -629
  165. package/templates/dynamics-365-starter/tools/entity-generator/index.js +0 -168
  166. package/templates/dynamics-365-starter/tools/entity-generator/templates/constants.template.ts +0 -124
  167. package/templates/dynamics-365-starter/tools/entity-generator/templates/form.template.css +0 -283
  168. package/templates/dynamics-365-starter/tools/entity-generator/templates/form.template.tsx +0 -275
  169. package/templates/dynamics-365-starter/tools/entity-generator/templates/management.template.css +0 -204
  170. package/templates/dynamics-365-starter/tools/entity-generator/templates/management.template.tsx +0 -413
  171. package/templates/dynamics-365-starter/tools/entity-generator/templates/model.template.ts +0 -250
  172. package/templates/dynamics-365-starter/tools/metadata-sync/d365-client.js +0 -410
  173. package/templates/dynamics-365-starter/tools/metadata-sync/index.js +0 -512
  174. package/templates/dynamics-365-starter/tools/metadata-sync/type-generator.js +0 -675
  175. /package/templates/{dynamics-365-starter → react-custom-page}/README.md +0 -0
  176. /package/templates/{dynamics-365-starter → react-custom-page}/deployment/README.md +0 -0
  177. /package/templates/{dynamics-365-starter → react-custom-page}/docs/ARCHITECTURE_OVERVIEW.md +0 -0
  178. /package/templates/{dynamics-365-starter → react-custom-page}/docs/BEST_PRACTICES.md +0 -0
  179. /package/templates/{dynamics-365-starter → react-custom-page}/docs/MIGRATION_GUIDE.md +0 -0
  180. /package/templates/{dynamics-365-starter → react-custom-page}/public/index.html +0 -0
  181. /package/templates/{dynamics-365-starter → react-custom-page}/scripts/custom-build.js +0 -0
  182. /package/templates/{dynamics-365-starter → react-custom-page}/src/components/AccountForm.css +0 -0
  183. /package/templates/{dynamics-365-starter → react-custom-page}/src/components/AccountForm.tsx +0 -0
  184. /package/templates/{dynamics-365-starter → react-custom-page}/src/components/AccountManagement.css +0 -0
  185. /package/templates/{dynamics-365-starter → react-custom-page}/src/components/AccountManagement.tsx +0 -0
  186. /package/templates/{dynamics-365-starter → react-custom-page}/src/components/ContactForm.css +0 -0
  187. /package/templates/{dynamics-365-starter → react-custom-page}/src/components/ContactForm.tsx +0 -0
  188. /package/templates/{dynamics-365-starter → react-custom-page}/src/components/ContactManagement.css +0 -0
  189. /package/templates/{dynamics-365-starter → react-custom-page}/src/components/ContactManagement.tsx +0 -0
  190. /package/templates/{dynamics-365-starter → react-custom-page}/src/components/Logging/LogDialog.tsx +0 -0
  191. /package/templates/{dynamics-365-starter → react-custom-page}/src/components/Logging/LoggingContext.tsx +0 -0
  192. /package/templates/{dynamics-365-starter → react-custom-page}/src/components/Logging/LoggingDebugPanel.css +0 -0
  193. /package/templates/{dynamics-365-starter → react-custom-page}/src/components/Logging/LoggingDebugPanel.tsx +0 -0
  194. /package/templates/{dynamics-365-starter → react-custom-page}/src/components/Logging/LoggingProvider.tsx +0 -0
  195. /package/templates/{dynamics-365-starter → react-custom-page}/src/components/Logging/logger.ts +0 -0
  196. /package/templates/{dynamics-365-starter → react-custom-page}/src/constants/account.ts +0 -0
  197. /package/templates/{dynamics-365-starter → react-custom-page}/src/constants/contact.ts +0 -0
  198. /package/templates/{dynamics-365-starter → react-custom-page}/src/index.tsx +0 -0
  199. /package/templates/{dynamics-365-starter → react-custom-page}/src/models/Account.ts +0 -0
  200. /package/templates/{dynamics-365-starter → react-custom-page}/src/models/BaseEntity.ts +0 -0
  201. /package/templates/{dynamics-365-starter → react-custom-page}/src/models/Contact.ts +0 -0
  202. /package/templates/{dynamics-365-starter → react-custom-page}/src/pcf/ContactControlWrapper.tsx +0 -0
  203. /package/templates/{dynamics-365-starter → react-custom-page}/src/pcf/MultiEntityControlWrapper.tsx +0 -0
  204. /package/templates/{dynamics-365-starter → react-custom-page}/src/providers/DynamicsProvider.tsx +0 -0
  205. /package/templates/{dynamics-365-starter → react-custom-page}/src/services/MockApiService.ts +0 -0
  206. /package/templates/{dynamics-365-starter → react-custom-page}/src/services/ServiceFactory.ts +0 -0
  207. /package/templates/{dynamics-365-starter → react-custom-page}/src/services/XrmApiService.ts +0 -0
  208. /package/templates/{dynamics-365-starter → react-custom-page}/src/styles/index.css +0 -0
  209. /package/templates/{dynamics-365-starter → react-custom-page}/tsconfig.json +0 -0
  210. /package/templates/{dynamics-365-starter → react-custom-page}/webpack.config.js +0 -0
@@ -1,512 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Metadata Sync Tool for Dynamics 365
5
- * Pulls entity metadata from D365 environments and generates TypeScript interfaces
6
- */
7
-
8
- const { program } = require('commander');
9
- const fs = require('fs-extra');
10
- const path = require('path');
11
- const chalk = require('chalk');
12
-
13
- program
14
- .name('metadata-sync')
15
- .description(
16
- 'Sync entity metadata from Dynamics 365 and generate TypeScript interfaces'
17
- )
18
- .version('1.0.0');
19
-
20
- program
21
- .command('pull')
22
- .description('Pull entity metadata from Dynamics 365')
23
- .requiredOption('-e, --environment <env>', 'Environment (dev, test, prod)')
24
- .option(
25
- '-t, --entities <entities>',
26
- 'Comma-separated list of entities to sync'
27
- )
28
- .option('--all', 'Sync all custom entities')
29
- .option(
30
- '--output <path>',
31
- 'Output directory for metadata files',
32
- 'src/metadata'
33
- )
34
- .action(pullMetadata);
35
-
36
- program
37
- .command('generate')
38
- .description('Generate TypeScript interfaces from metadata')
39
- .option('-i, --input <path>', 'Input metadata directory', 'src/metadata')
40
- .option(
41
- '-o, --output <path>',
42
- 'Output directory for TypeScript files',
43
- 'src/types/generated'
44
- )
45
- .option('--entity <entity>', 'Generate for specific entity only')
46
- .action(generateTypes);
47
-
48
- program
49
- .command('validate')
50
- .description('Validate generated code against D365 schema')
51
- .option('-e, --environment <env>', 'Environment to validate against')
52
- .option('--fix', 'Automatically fix validation issues')
53
- .action(validateSchema);
54
-
55
- program
56
- .command('sync')
57
- .description('Pull metadata and generate types in one command')
58
- .requiredOption('-e, --environment <env>', 'Environment to sync from')
59
- .option(
60
- '-t, --entities <entities>',
61
- 'Comma-separated list of entities to sync'
62
- )
63
- .option('--all', 'Sync all custom entities')
64
- .action(syncAll);
65
-
66
- async function pullMetadata(options) {
67
- console.log(
68
- chalk.cyan(
69
- `\n🔄 Pulling metadata from ${options.environment} environment...\n`
70
- )
71
- );
72
-
73
- try {
74
- const config = await loadEnvironmentConfig(options.environment);
75
- const entities = await getEntitiesToSync(options);
76
-
77
- console.log(chalk.blue(`📋 Syncing ${entities.length} entities...`));
78
-
79
- for (const entity of entities) {
80
- await pullEntityMetadata(entity, config, options.output);
81
- }
82
-
83
- console.log(chalk.green('\n✅ Metadata sync completed successfully!'));
84
- console.log(chalk.cyan('\n📝 Next steps:'));
85
- console.log(
86
- ' npm run metadata:generate # Generate TypeScript interfaces'
87
- );
88
- console.log(' npm run metadata:validate # Validate against schema');
89
- } catch (error) {
90
- console.error(chalk.red('❌ Error pulling metadata:'), error.message);
91
- process.exit(1);
92
- }
93
- }
94
-
95
- async function generateTypes(options) {
96
- console.log(chalk.cyan('\n🔧 Generating TypeScript interfaces...\n'));
97
-
98
- try {
99
- const metadataDir = options.input;
100
- const outputDir = options.output;
101
-
102
- await fs.ensureDir(outputDir);
103
-
104
- if (options.entity) {
105
- await generateEntityTypes(options.entity, metadataDir, outputDir);
106
- } else {
107
- const metadataFiles = await fs.readdir(metadataDir);
108
- const entityFiles = metadataFiles.filter((file) =>
109
- file.endsWith('.metadata.json')
110
- );
111
-
112
- for (const file of entityFiles) {
113
- const entityName = file.replace('.metadata.json', '');
114
- await generateEntityTypes(entityName, metadataDir, outputDir);
115
- }
116
-
117
- // Generate index file
118
- await generateIndexFile(
119
- entityFiles.map((f) => f.replace('.metadata.json', '')),
120
- outputDir
121
- );
122
- }
123
-
124
- console.log(
125
- chalk.green('\n✅ TypeScript interfaces generated successfully!')
126
- );
127
- } catch (error) {
128
- console.error(chalk.red('❌ Error generating types:'), error.message);
129
- process.exit(1);
130
- }
131
- }
132
-
133
- async function validateSchema(options) {
134
- console.log(
135
- chalk.cyan(`\n🔍 Validating schema against ${options.environment}...\n`)
136
- );
137
-
138
- try {
139
- const config = await loadEnvironmentConfig(options.environment);
140
- const validationResults = await runSchemaValidation(config);
141
-
142
- if (validationResults.isValid) {
143
- console.log(chalk.green('✅ Schema validation passed!'));
144
- } else {
145
- console.log(chalk.yellow('⚠️ Schema validation issues found:'));
146
- validationResults.issues.forEach((issue) => {
147
- console.log(chalk.yellow(` • ${issue}`));
148
- });
149
-
150
- if (options.fix) {
151
- console.log(chalk.cyan('\n🔧 Attempting to fix issues...'));
152
- await fixValidationIssues(validationResults.issues);
153
- console.log(chalk.green('✅ Issues fixed automatically'));
154
- }
155
- }
156
- } catch (error) {
157
- console.error(chalk.red('❌ Error validating schema:'), error.message);
158
- process.exit(1);
159
- }
160
- }
161
-
162
- async function syncAll(options) {
163
- console.log(chalk.cyan('\n🚀 Starting full metadata sync...\n'));
164
-
165
- // Pull metadata
166
- await pullMetadata(options);
167
-
168
- // Generate types
169
- await generateTypes({ input: 'src/metadata', output: 'src/types/generated' });
170
-
171
- // Validate
172
- await validateSchema({ environment: options.environment });
173
-
174
- console.log(chalk.green('\n🎉 Complete metadata sync finished!'));
175
- }
176
-
177
- async function loadEnvironmentConfig(environment) {
178
- const configPath = path.join(
179
- process.cwd(),
180
- 'config',
181
- 'environments',
182
- `${environment}.json`
183
- );
184
-
185
- if (!(await fs.pathExists(configPath))) {
186
- throw new Error(`Environment configuration not found: ${configPath}`);
187
- }
188
-
189
- return await fs.readJson(configPath);
190
- }
191
-
192
- async function getEntitiesToSync(options) {
193
- if (options.all) {
194
- // Get all custom entities from D365
195
- return await getAllCustomEntities(options.environment);
196
- } else if (options.entities) {
197
- return options.entities.split(',').map((e) => e.trim());
198
- } else {
199
- // Default to common entities
200
- return ['account', 'contact', 'opportunity', 'quote', 'invoice'];
201
- }
202
- }
203
-
204
- async function pullEntityMetadata(entityName, config, outputDir) {
205
- console.log(chalk.blue(` 📊 Pulling metadata for ${entityName}...`));
206
-
207
- try {
208
- // Simulate D365 Web API call to get entity metadata
209
- const metadata = await fetchEntityMetadata(entityName, config);
210
-
211
- // Save metadata to file
212
- const metadataPath = path.join(outputDir, `${entityName}.metadata.json`);
213
- await fs.ensureDir(outputDir);
214
- await fs.writeJson(metadataPath, metadata, { spaces: 2 });
215
-
216
- console.log(chalk.green(` ✓ Saved ${entityName} metadata`));
217
- } catch (error) {
218
- console.log(
219
- chalk.red(` ✗ Failed to pull ${entityName}: ${error.message}`)
220
- );
221
- }
222
- }
223
-
224
- async function fetchEntityMetadata(entityName, config) {
225
- // Mock implementation - in real usage, this would make actual D365 Web API calls
226
- const mockMetadata = {
227
- LogicalName: entityName,
228
- SchemaName: entityName.charAt(0).toUpperCase() + entityName.slice(1),
229
- DisplayName: entityName.charAt(0).toUpperCase() + entityName.slice(1),
230
- PrimaryIdAttribute: `${entityName}id`,
231
- PrimaryNameAttribute: 'name',
232
- CollectionSchemaName: `${entityName}s`,
233
- EntitySetName: `${entityName}s`,
234
- IsCustomEntity: !['account', 'contact', 'opportunity'].includes(entityName),
235
- Attributes: [
236
- {
237
- LogicalName: `${entityName}id`,
238
- SchemaName: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)}Id`,
239
- DisplayName: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)} ID`,
240
- AttributeType: 'Uniqueidentifier',
241
- IsPrimaryId: true,
242
- RequiredLevel: 'SystemRequired',
243
- IsValidForCreate: false,
244
- IsValidForUpdate: false,
245
- },
246
- {
247
- LogicalName: 'name',
248
- SchemaName: 'Name',
249
- DisplayName: 'Name',
250
- AttributeType: 'String',
251
- MaxLength: 160,
252
- RequiredLevel: 'ApplicationRequired',
253
- IsValidForCreate: true,
254
- IsValidForUpdate: true,
255
- },
256
- {
257
- LogicalName: 'createdon',
258
- SchemaName: 'CreatedOn',
259
- DisplayName: 'Created On',
260
- AttributeType: 'DateTime',
261
- RequiredLevel: 'None',
262
- IsValidForCreate: false,
263
- IsValidForUpdate: false,
264
- },
265
- {
266
- LogicalName: 'modifiedon',
267
- SchemaName: 'ModifiedOn',
268
- DisplayName: 'Modified On',
269
- AttributeType: 'DateTime',
270
- RequiredLevel: 'None',
271
- IsValidForCreate: false,
272
- IsValidForUpdate: false,
273
- },
274
- ],
275
- Relationships: [],
276
- OptionSets: [],
277
- Keys: [],
278
- LastModified: new Date().toISOString(),
279
- };
280
-
281
- // Add entity-specific attributes
282
- if (entityName === 'account') {
283
- mockMetadata.Attributes.push(
284
- {
285
- LogicalName: 'revenue',
286
- SchemaName: 'Revenue',
287
- DisplayName: 'Annual Revenue',
288
- AttributeType: 'Money',
289
- RequiredLevel: 'None',
290
- IsValidForCreate: true,
291
- IsValidForUpdate: true,
292
- },
293
- {
294
- LogicalName: 'industrycode',
295
- SchemaName: 'IndustryCode',
296
- DisplayName: 'Industry',
297
- AttributeType: 'Picklist',
298
- RequiredLevel: 'None',
299
- IsValidForCreate: true,
300
- IsValidForUpdate: true,
301
- OptionSet: 'account_industrycode',
302
- }
303
- );
304
- }
305
-
306
- return mockMetadata;
307
- }
308
-
309
- async function generateEntityTypes(entityName, metadataDir, outputDir) {
310
- console.log(chalk.blue(` 🔧 Generating types for ${entityName}...`));
311
-
312
- const metadataPath = path.join(metadataDir, `${entityName}.metadata.json`);
313
-
314
- if (!(await fs.pathExists(metadataPath))) {
315
- console.log(
316
- chalk.yellow(
317
- ` ⚠ Metadata file not found: ${entityName}.metadata.json`
318
- )
319
- );
320
- return;
321
- }
322
-
323
- const metadata = await fs.readJson(metadataPath);
324
-
325
- // Generate interface
326
- const interfaceContent = generateInterfaceContent(metadata);
327
- const interfacePath = path.join(outputDir, `${entityName}.types.ts`);
328
- await fs.writeFile(interfacePath, interfaceContent);
329
-
330
- // Generate constants
331
- const constantsContent = generateConstantsContent(metadata);
332
- const constantsPath = path.join(outputDir, `${entityName}.constants.ts`);
333
- await fs.writeFile(constantsPath, constantsContent);
334
-
335
- console.log(
336
- chalk.green(` ✓ Generated ${entityName} types and constants`)
337
- );
338
- }
339
-
340
- function generateInterfaceContent(metadata) {
341
- const interfaceName = `I${metadata.SchemaName}`;
342
- const attributes = metadata.Attributes || [];
343
-
344
- let content = `// Auto-generated interface for ${metadata.DisplayName} entity
345
- // Generated on: ${new Date().toISOString()}
346
- // Source: ${metadata.LogicalName} metadata
347
-
348
- export interface ${interfaceName} {
349
- `;
350
-
351
- attributes.forEach((attr) => {
352
- const tsType = mapAttributeTypeToTypeScript(attr);
353
- const optional =
354
- attr.RequiredLevel !== 'ApplicationRequired' &&
355
- attr.RequiredLevel !== 'SystemRequired';
356
- const optionalMarker = optional ? '?' : '';
357
-
358
- content += ` /** ${attr.DisplayName} - Type: ${attr.AttributeType}${attr.MaxLength ? `, MaxLength: ${attr.MaxLength}` : ''} */\n`;
359
- content += ` ${attr.LogicalName}${optionalMarker}: ${tsType};\n\n`;
360
- });
361
-
362
- content += `}
363
-
364
- export interface ${interfaceName}Create extends Omit<${interfaceName}, '${metadata.PrimaryIdAttribute}' | 'createdon' | 'modifiedon'> {}
365
-
366
- export interface ${interfaceName}Update extends Partial<Omit<${interfaceName}, '${metadata.PrimaryIdAttribute}' | 'createdon' | 'modifiedon'>> {}
367
- `;
368
-
369
- return content;
370
- }
371
-
372
- function generateConstantsContent(metadata) {
373
- const className = `${metadata.SchemaName}Constants`;
374
- const attributes = metadata.Attributes || [];
375
-
376
- let content = `// Auto-generated constants for ${metadata.DisplayName} entity
377
- // Generated on: ${new Date().toISOString()}
378
- // Source: ${metadata.LogicalName} metadata
379
-
380
- export class ${className} {
381
- // Entity metadata
382
- public static readonly LogicalName = '${metadata.LogicalName}';
383
- public static readonly SchemaName = '${metadata.SchemaName}';
384
- public static readonly CollectionName = '${metadata.EntitySetName}';
385
- public static readonly DisplayName = '${metadata.DisplayName}';
386
- public static readonly PrimaryIdAttribute = '${metadata.PrimaryIdAttribute}';
387
- public static readonly PrimaryNameAttribute = '${metadata.PrimaryNameAttribute}';
388
-
389
- // Attribute names
390
- `;
391
-
392
- attributes.forEach((attr) => {
393
- const constantName = attr.SchemaName;
394
- content += ` /** Type: ${attr.AttributeType}, RequiredLevel: ${attr.RequiredLevel}${attr.MaxLength ? `, MaxLength: ${attr.MaxLength}` : ''} */\n`;
395
- content += ` public static readonly ${constantName} = '${attr.LogicalName}';\n\n`;
396
- });
397
-
398
- // Add option sets if any
399
- const optionSets = attributes.filter(
400
- (attr) => attr.AttributeType === 'Picklist'
401
- );
402
- if (optionSets.length > 0) {
403
- content += ` // Option Sets\n`;
404
- optionSets.forEach((attr) => {
405
- content += ` public static readonly ${attr.SchemaName}Options = {\n`;
406
- content += ` // TODO: Add actual option values from metadata\n`;
407
- content += ` };\n\n`;
408
- });
409
- }
410
-
411
- // Add display names
412
- content += ` // Display names for UI
413
- public static readonly FieldDisplayNames = {\n`;
414
- attributes.forEach((attr) => {
415
- content += ` [this.${attr.SchemaName}]: '${attr.DisplayName}',\n`;
416
- });
417
- content += ` };\n\n`;
418
-
419
- // Add required fields
420
- const requiredFields = attributes.filter(
421
- (attr) =>
422
- attr.RequiredLevel === 'ApplicationRequired' ||
423
- attr.RequiredLevel === 'SystemRequired'
424
- );
425
- content += ` // Required fields\n`;
426
- content += ` public static readonly RequiredFields = [\n`;
427
- requiredFields.forEach((attr) => {
428
- content += ` this.${attr.SchemaName},\n`;
429
- });
430
- content += ` ];\n`;
431
-
432
- content += `}\n`;
433
-
434
- return content;
435
- }
436
-
437
- function mapAttributeTypeToTypeScript(attribute) {
438
- switch (attribute.AttributeType) {
439
- case 'String':
440
- case 'Memo':
441
- return 'string';
442
- case 'Integer':
443
- case 'BigInt':
444
- case 'Double':
445
- case 'Decimal':
446
- case 'Money':
447
- return 'number';
448
- case 'Boolean':
449
- return 'boolean';
450
- case 'DateTime':
451
- return 'Date';
452
- case 'Uniqueidentifier':
453
- return 'string';
454
- case 'Picklist':
455
- case 'State':
456
- case 'Status':
457
- return 'number';
458
- case 'Lookup':
459
- case 'Customer':
460
- case 'Owner':
461
- return 'string';
462
- case 'MultiSelectPicklist':
463
- return 'number[]';
464
- default:
465
- return 'unknown';
466
- }
467
- }
468
-
469
- async function generateIndexFile(entityNames, outputDir) {
470
- let content = `// Auto-generated index file for entity types
471
- // Generated on: ${new Date().toISOString()}
472
-
473
- `;
474
-
475
- entityNames.forEach((entityName) => {
476
- const schemaName = entityName.charAt(0).toUpperCase() + entityName.slice(1);
477
- content += `export * from './${entityName}.types';\n`;
478
- content += `export * from './${entityName}.constants';\n`;
479
- });
480
-
481
- const indexPath = path.join(outputDir, 'index.ts');
482
- await fs.writeFile(indexPath, content);
483
- }
484
-
485
- async function getAllCustomEntities(environment) {
486
- // Mock implementation - would make actual D365 Web API call
487
- return ['account', 'contact', 'opportunity', 'quote', 'invoice'];
488
- }
489
-
490
- async function runSchemaValidation(config) {
491
- // Mock validation - would compare generated types with actual D365 schema
492
- return {
493
- isValid: true,
494
- issues: [],
495
- };
496
- }
497
-
498
- async function fixValidationIssues(issues) {
499
- // Mock fix implementation
500
- console.log('Fixing validation issues...');
501
- }
502
-
503
- if (require.main === module) {
504
- program.parse();
505
- }
506
-
507
- module.exports = {
508
- pullMetadata,
509
- generateTypes,
510
- validateSchema,
511
- syncAll,
512
- };