@jterrats/smart-deployment 1.0.3 → 1.0.4

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 (257) hide show
  1. package/LICENSE +29 -0
  2. package/README.md +6 -0
  3. package/lib/ai/wave-validation-service.d.ts +21 -0
  4. package/lib/ai/wave-validation-service.js +189 -114
  5. package/lib/ai/wave-validation-service.js.map +1 -1
  6. package/lib/analysis/analysis-reporter.d.ts +12 -0
  7. package/lib/analysis/analysis-reporter.js +61 -0
  8. package/lib/analysis/analysis-reporter.js.map +1 -1
  9. package/lib/analysis/analyze-artifact-service.d.ts +25 -0
  10. package/lib/analysis/analyze-artifact-service.js +50 -0
  11. package/lib/analysis/analyze-artifact-service.js.map +1 -0
  12. package/lib/analysis/analyze-context-service.d.ts +25 -0
  13. package/lib/analysis/analyze-context-service.js +28 -0
  14. package/lib/analysis/analyze-context-service.js.map +1 -0
  15. package/lib/analysis/project-analysis-service.d.ts +48 -0
  16. package/lib/analysis/project-analysis-service.js +134 -0
  17. package/lib/analysis/project-analysis-service.js.map +1 -0
  18. package/lib/commands/analyze.d.ts +0 -2
  19. package/lib/commands/analyze.js +31 -162
  20. package/lib/commands/analyze.js.map +1 -1
  21. package/lib/commands/config.d.ts +0 -4
  22. package/lib/commands/config.js +25 -95
  23. package/lib/commands/config.js.map +1 -1
  24. package/lib/commands/resume.js +6 -16
  25. package/lib/commands/resume.js.map +1 -1
  26. package/lib/commands/start.d.ts +0 -11
  27. package/lib/commands/start.js +38 -550
  28. package/lib/commands/start.js.map +1 -1
  29. package/lib/commands/status.js +36 -12
  30. package/lib/commands/status.js.map +1 -1
  31. package/lib/commands/validate.d.ts +3 -0
  32. package/lib/commands/validate.js +5 -7
  33. package/lib/commands/validate.js.map +1 -1
  34. package/lib/config/config-mutation-service.d.ts +21 -0
  35. package/lib/config/config-mutation-service.js +61 -0
  36. package/lib/config/config-mutation-service.js.map +1 -0
  37. package/lib/dependencies/circular-dependency-detector.d.ts +5 -0
  38. package/lib/dependencies/circular-dependency-detector.js +67 -72
  39. package/lib/dependencies/circular-dependency-detector.js.map +1 -1
  40. package/lib/dependencies/dependency-depth-calculator.d.ts +28 -0
  41. package/lib/dependencies/dependency-depth-calculator.js +86 -39
  42. package/lib/dependencies/dependency-depth-calculator.js.map +1 -1
  43. package/lib/dependencies/dependency-graph-builder.d.ts +45 -13
  44. package/lib/dependencies/dependency-graph-builder.js +228 -123
  45. package/lib/dependencies/dependency-graph-builder.js.map +1 -1
  46. package/lib/dependencies/dependency-impact-analyzer.d.ts +13 -0
  47. package/lib/dependencies/dependency-impact-analyzer.js +146 -102
  48. package/lib/dependencies/dependency-impact-analyzer.js.map +1 -1
  49. package/lib/dependencies/dependency-merger.d.ts +10 -0
  50. package/lib/dependencies/dependency-merger.js +123 -61
  51. package/lib/dependencies/dependency-merger.js.map +1 -1
  52. package/lib/dependencies/dependency-resolver.d.ts +34 -9
  53. package/lib/dependencies/dependency-resolver.js +181 -112
  54. package/lib/dependencies/dependency-resolver.js.map +1 -1
  55. package/lib/dependencies/dependency-semantics.d.ts +20 -0
  56. package/lib/dependencies/dependency-semantics.js +55 -0
  57. package/lib/dependencies/dependency-semantics.js.map +1 -0
  58. package/lib/dependencies/graph-visualizer.d.ts +6 -1
  59. package/lib/dependencies/graph-visualizer.js +38 -4
  60. package/lib/dependencies/graph-visualizer.js.map +1 -1
  61. package/lib/dependencies/heuristic-inference.d.ts +2 -53
  62. package/lib/dependencies/heuristic-inference.js +8 -317
  63. package/lib/dependencies/heuristic-inference.js.map +1 -1
  64. package/lib/dependencies/heuristic-patterns.d.ts +14 -0
  65. package/lib/dependencies/heuristic-patterns.js +247 -0
  66. package/lib/dependencies/heuristic-patterns.js.map +1 -0
  67. package/lib/deployment/cycle-remediation-runner.d.ts +36 -0
  68. package/lib/deployment/cycle-remediation-runner.js +262 -0
  69. package/lib/deployment/cycle-remediation-runner.js.map +1 -0
  70. package/lib/deployment/deployment-context-service.d.ts +24 -0
  71. package/lib/deployment/deployment-context-service.js +17 -0
  72. package/lib/deployment/deployment-context-service.js.map +1 -0
  73. package/lib/deployment/deployment-runner.d.ts +37 -0
  74. package/lib/deployment/deployment-runner.js +94 -0
  75. package/lib/deployment/deployment-runner.js.map +1 -0
  76. package/lib/deployment/deployment-status-service.d.ts +5 -0
  77. package/lib/deployment/deployment-status-service.js +39 -18
  78. package/lib/deployment/deployment-status-service.js.map +1 -1
  79. package/lib/deployment/deployment-validation-service.d.ts +5 -2
  80. package/lib/deployment/deployment-validation-service.js +48 -45
  81. package/lib/deployment/deployment-validation-service.js.map +1 -1
  82. package/lib/deployment/resume-deployment-service.d.ts +14 -0
  83. package/lib/deployment/resume-deployment-service.js +25 -0
  84. package/lib/deployment/resume-deployment-service.js.map +1 -0
  85. package/lib/deployment/start-execution-service.d.ts +44 -0
  86. package/lib/deployment/start-execution-service.js +94 -0
  87. package/lib/deployment/start-execution-service.js.map +1 -0
  88. package/lib/deployment/test-executor.d.ts +60 -1
  89. package/lib/deployment/test-executor.js +180 -12
  90. package/lib/deployment/test-executor.js.map +1 -1
  91. package/lib/deployment/test-plan-service.d.ts +8 -0
  92. package/lib/deployment/test-plan-service.js +26 -0
  93. package/lib/deployment/test-plan-service.js.map +1 -0
  94. package/lib/deployment/wave-manifest-service.d.ts +11 -0
  95. package/lib/deployment/wave-manifest-service.js +41 -0
  96. package/lib/deployment/wave-manifest-service.js.map +1 -0
  97. package/lib/parsers/apex-class-parser.js +180 -98
  98. package/lib/parsers/apex-class-parser.js.map +1 -1
  99. package/lib/parsers/apex-trigger-parser.js +56 -7
  100. package/lib/parsers/apex-trigger-parser.js.map +1 -1
  101. package/lib/parsers/aura-parser.js +2 -2
  102. package/lib/parsers/aura-parser.js.map +1 -1
  103. package/lib/parsers/bot-parser.js +31 -4
  104. package/lib/parsers/bot-parser.js.map +1 -1
  105. package/lib/parsers/custom-metadata-parser.js +37 -3
  106. package/lib/parsers/custom-metadata-parser.js.map +1 -1
  107. package/lib/parsers/custom-object-parser.js +284 -150
  108. package/lib/parsers/custom-object-parser.js.map +1 -1
  109. package/lib/parsers/email-template-parser.js +115 -58
  110. package/lib/parsers/email-template-parser.js.map +1 -1
  111. package/lib/parsers/flexipage-parser.js +41 -16
  112. package/lib/parsers/flexipage-parser.js.map +1 -1
  113. package/lib/parsers/flow-parser.js +73 -63
  114. package/lib/parsers/flow-parser.js.map +1 -1
  115. package/lib/parsers/genai-prompt-parser.js +16 -4
  116. package/lib/parsers/genai-prompt-parser.js.map +1 -1
  117. package/lib/parsers/layout-parser.d.ts +11 -0
  118. package/lib/parsers/layout-parser.js +143 -68
  119. package/lib/parsers/layout-parser.js.map +1 -1
  120. package/lib/parsers/lwc-parser.d.ts +0 -9
  121. package/lib/parsers/lwc-parser.js +245 -100
  122. package/lib/parsers/lwc-parser.js.map +1 -1
  123. package/lib/parsers/parser-utils.d.ts +3 -0
  124. package/lib/parsers/parser-utils.js +16 -0
  125. package/lib/parsers/parser-utils.js.map +1 -0
  126. package/lib/parsers/permission-set-parser.d.ts +6 -0
  127. package/lib/parsers/permission-set-parser.js +31 -40
  128. package/lib/parsers/permission-set-parser.js.map +1 -1
  129. package/lib/parsers/profile-parser.d.ts +13 -0
  130. package/lib/parsers/profile-parser.js +39 -34
  131. package/lib/parsers/profile-parser.js.map +1 -1
  132. package/lib/parsers/visualforce-parser.js +2 -1
  133. package/lib/parsers/visualforce-parser.js.map +1 -1
  134. package/lib/presentation/analyze-command-presenter.d.ts +10 -0
  135. package/lib/presentation/analyze-command-presenter.js +30 -0
  136. package/lib/presentation/analyze-command-presenter.js.map +1 -0
  137. package/lib/presentation/config-command-presenter.d.ts +15 -0
  138. package/lib/presentation/config-command-presenter.js +50 -0
  139. package/lib/presentation/config-command-presenter.js.map +1 -0
  140. package/lib/presentation/project-analysis-presenter.d.ts +9 -0
  141. package/lib/presentation/project-analysis-presenter.js +9 -0
  142. package/lib/presentation/project-analysis-presenter.js.map +1 -0
  143. package/lib/presentation/resume-command-presenter.d.ts +7 -0
  144. package/lib/presentation/resume-command-presenter.js +12 -0
  145. package/lib/presentation/resume-command-presenter.js.map +1 -0
  146. package/lib/presentation/start-command-presenter.d.ts +15 -0
  147. package/lib/presentation/start-command-presenter.js +29 -0
  148. package/lib/presentation/start-command-presenter.js.map +1 -0
  149. package/lib/presentation/status-command-presenter.d.ts +7 -0
  150. package/lib/presentation/status-command-presenter.js +16 -0
  151. package/lib/presentation/status-command-presenter.js.map +1 -0
  152. package/lib/presentation/validate-command-presenter.d.ts +9 -0
  153. package/lib/presentation/validate-command-presenter.js +50 -0
  154. package/lib/presentation/validate-command-presenter.js.map +1 -0
  155. package/lib/services/metadata-scanner-service.d.ts +8 -12
  156. package/lib/services/metadata-scanner-service.js +55 -533
  157. package/lib/services/metadata-scanner-service.js.map +1 -1
  158. package/lib/services/scanners/automation-ai-metadata-scanner.d.ts +4 -0
  159. package/lib/services/scanners/automation-ai-metadata-scanner.js +61 -0
  160. package/lib/services/scanners/automation-ai-metadata-scanner.js.map +1 -0
  161. package/lib/services/scanners/code-metadata-scanner.d.ts +23 -0
  162. package/lib/services/scanners/code-metadata-scanner.js +149 -0
  163. package/lib/services/scanners/code-metadata-scanner.js.map +1 -0
  164. package/lib/services/scanners/data-metadata-scanner.d.ts +3 -0
  165. package/lib/services/scanners/data-metadata-scanner.js +82 -0
  166. package/lib/services/scanners/data-metadata-scanner.js.map +1 -0
  167. package/lib/services/scanners/experience-metadata-scanner.d.ts +5 -0
  168. package/lib/services/scanners/experience-metadata-scanner.js +123 -0
  169. package/lib/services/scanners/experience-metadata-scanner.js.map +1 -0
  170. package/lib/services/scanners/scanner-runtime.d.ts +11 -0
  171. package/lib/services/scanners/scanner-runtime.js +115 -0
  172. package/lib/services/scanners/scanner-runtime.js.map +1 -0
  173. package/lib/services/scanners/security-metadata-scanner.d.ts +3 -0
  174. package/lib/services/scanners/security-metadata-scanner.js +71 -0
  175. package/lib/services/scanners/security-metadata-scanner.js.map +1 -0
  176. package/lib/types/dependency.d.ts +11 -1
  177. package/lib/types/metadata.d.ts +17 -0
  178. package/lib/types/salesforce/flow/actions.d.ts +108 -0
  179. package/lib/types/salesforce/flow/actions.js +2 -0
  180. package/lib/types/salesforce/flow/actions.js.map +1 -0
  181. package/lib/types/salesforce/flow/common.d.ts +36 -0
  182. package/lib/types/salesforce/flow/common.js +5 -0
  183. package/lib/types/salesforce/flow/common.js.map +1 -0
  184. package/lib/types/salesforce/flow/metadata.d.ts +37 -0
  185. package/lib/types/salesforce/flow/metadata.js +2 -0
  186. package/lib/types/salesforce/flow/metadata.js.map +1 -0
  187. package/lib/types/salesforce/flow/records.d.ts +38 -0
  188. package/lib/types/salesforce/flow/records.js +2 -0
  189. package/lib/types/salesforce/flow/records.js.map +1 -0
  190. package/lib/types/salesforce/flow/resources.d.ts +44 -0
  191. package/lib/types/salesforce/flow/resources.js +2 -0
  192. package/lib/types/salesforce/flow/resources.js.map +1 -0
  193. package/lib/types/salesforce/flow/screens.d.ts +56 -0
  194. package/lib/types/salesforce/flow/screens.js +2 -0
  195. package/lib/types/salesforce/flow/screens.js.map +1 -0
  196. package/lib/types/salesforce/flow/start.d.ts +24 -0
  197. package/lib/types/salesforce/flow/start.js +2 -0
  198. package/lib/types/salesforce/flow/start.js.map +1 -0
  199. package/lib/types/salesforce/flow.d.ts +9 -514
  200. package/lib/types/salesforce/flow.js +1 -1
  201. package/lib/types/salesforce/object/core.d.ts +68 -0
  202. package/lib/types/salesforce/object/core.js +5 -0
  203. package/lib/types/salesforce/object/core.js.map +1 -0
  204. package/lib/types/salesforce/object/field.d.ts +155 -0
  205. package/lib/types/salesforce/object/field.js +5 -0
  206. package/lib/types/salesforce/object/field.js.map +1 -0
  207. package/lib/types/salesforce/object/filter.d.ts +16 -0
  208. package/lib/types/salesforce/object/filter.js +5 -0
  209. package/lib/types/salesforce/object/filter.js.map +1 -0
  210. package/lib/types/salesforce/object/policy.d.ts +27 -0
  211. package/lib/types/salesforce/object/policy.js +5 -0
  212. package/lib/types/salesforce/object/policy.js.map +1 -0
  213. package/lib/types/salesforce/object/record.d.ts +44 -0
  214. package/lib/types/salesforce/object/record.js +5 -0
  215. package/lib/types/salesforce/object/record.js.map +1 -0
  216. package/lib/types/salesforce/object/view.d.ts +71 -0
  217. package/lib/types/salesforce/object/view.js +5 -0
  218. package/lib/types/salesforce/object/view.js.map +1 -0
  219. package/lib/types/salesforce/object/weblink.d.ts +70 -0
  220. package/lib/types/salesforce/object/weblink.js +5 -0
  221. package/lib/types/salesforce/object/weblink.js.map +1 -0
  222. package/lib/types/salesforce/object.d.ts +7 -423
  223. package/lib/types/salesforce/object.js +7 -1
  224. package/lib/types/salesforce/object.js.map +1 -1
  225. package/lib/utils/cache-manager.d.ts +16 -15
  226. package/lib/utils/cache-manager.js +290 -188
  227. package/lib/utils/cache-manager.js.map +1 -1
  228. package/lib/utils/logger.d.ts +8 -0
  229. package/lib/utils/logger.js +82 -69
  230. package/lib/utils/logger.js.map +1 -1
  231. package/lib/validators/xml/xml-reference-rules.d.ts +3 -0
  232. package/lib/validators/xml/xml-reference-rules.js +173 -0
  233. package/lib/validators/xml/xml-reference-rules.js.map +1 -0
  234. package/lib/validators/xml/xml-report-formatter.d.ts +2 -0
  235. package/lib/validators/xml/xml-report-formatter.js +47 -0
  236. package/lib/validators/xml/xml-report-formatter.js.map +1 -0
  237. package/lib/validators/xml/xml-schema-rules.d.ts +3 -0
  238. package/lib/validators/xml/xml-schema-rules.js +75 -0
  239. package/lib/validators/xml/xml-schema-rules.js.map +1 -0
  240. package/lib/validators/xml/xml-validation-types.d.ts +25 -0
  241. package/lib/validators/xml/xml-validation-types.js +2 -0
  242. package/lib/validators/xml/xml-validation-types.js.map +1 -0
  243. package/lib/validators/xml-metadata-validator.d.ts +2 -63
  244. package/lib/validators/xml-metadata-validator.js +7 -338
  245. package/lib/validators/xml-metadata-validator.js.map +1 -1
  246. package/lib/waves/test-optimizer.d.ts +13 -0
  247. package/lib/waves/test-optimizer.js +124 -63
  248. package/lib/waves/test-optimizer.js.map +1 -1
  249. package/lib/waves/wave-builder.d.ts +17 -21
  250. package/lib/waves/wave-builder.js +199 -135
  251. package/lib/waves/wave-builder.js.map +1 -1
  252. package/lib/waves/wave-splitter.d.ts +12 -8
  253. package/lib/waves/wave-splitter.js +156 -115
  254. package/lib/waves/wave-splitter.js.map +1 -1
  255. package/npm-shrinkwrap.json +2 -2
  256. package/oclif.manifest.json +1 -1
  257. package/package.json +1 -1
@@ -8,35 +8,18 @@
8
8
  * - Metadata parsers (all types)
9
9
  * - DependencyGraphBuilder
10
10
  */
11
- import * as fs from 'node:fs/promises';
12
11
  import * as path from 'node:path';
13
- import { glob as globAsync } from 'glob';
14
12
  import { DependencyGraphBuilder } from '../dependencies/dependency-graph-builder.js';
15
- import { parseAura } from '../parsers/aura-parser.js';
16
- import { parseApexClass } from '../parsers/apex-class-parser.js';
17
- import { parseApexTrigger } from '../parsers/apex-trigger-parser.js';
18
- import { parseBot } from '../parsers/bot-parser.js';
19
- import { parseCustomMetadataType } from '../parsers/custom-metadata-parser.js';
20
- import { parseCustomObject } from '../parsers/custom-object-parser.js';
21
- import { parseEmailTemplate } from '../parsers/email-template-parser.js';
22
- import { parseFlexiPage } from '../parsers/flexipage-parser.js';
23
- import { parseFlow } from '../parsers/flow-parser.js';
24
- import { parseGenAiPrompt } from '../parsers/genai-prompt-parser.js';
25
- import { parseLayout } from '../parsers/layout-parser.js';
26
- import { parseLWC } from '../parsers/lwc-parser.js';
27
- import { parsePermissionSet } from '../parsers/permission-set-parser.js';
28
- import { parseProfile } from '../parsers/profile-parser.js';
29
- import { parseVisualforce } from '../parsers/visualforce-parser.js';
30
13
  import { ForceIgnoreParser } from '../scanner/forceignore-parser.js';
31
14
  import { SfdxProjectDetector } from '../scanner/sfdx-project-detector.js';
32
15
  import { getLogger } from '../utils/logger.js';
16
+ import { parseBotComponent, parseFlowComponent, parseGenAiPromptComponent, } from './scanners/automation-ai-metadata-scanner.js';
17
+ import { CODE_DIRECTORY_SCANNERS, CODE_FILE_SCANNERS } from './scanners/code-metadata-scanner.js';
18
+ import { parseCustomMetadataComponents, parseCustomObjectComponent } from './scanners/data-metadata-scanner.js';
19
+ import { parseEmailTemplateComponent, parseFlexiPageComponent, parseLayoutComponent, parseVisualforceComponent, } from './scanners/experience-metadata-scanner.js';
20
+ import { fileExists, scanMetadataDirectories, scanMetadataFiles, scanRegisteredDirectoryMetadata, scanRegisteredFileMetadata, } from './scanners/scanner-runtime.js';
21
+ import { parsePermissionSetComponent, parseProfileComponent } from './scanners/security-metadata-scanner.js';
33
22
  const logger = getLogger('MetadataScannerService');
34
- function toNodeIds(dependencies, defaultType) {
35
- return new Set([...dependencies].map((dependency) => (dependency.includes(':') ? dependency : `${defaultType}:${dependency}`)));
36
- }
37
- function isDefined(value) {
38
- return value !== undefined;
39
- }
40
23
  /**
41
24
  * Metadata file pattern matcher
42
25
  * TODO: Use when implementing pattern-based metadata detection
@@ -53,6 +36,7 @@ function isDefined(value) {
53
36
  */
54
37
  export class MetadataScannerService {
55
38
  forceIgnoreParser;
39
+ shouldIgnorePath = (filePath) => this.shouldIgnore(filePath);
56
40
  /**
57
41
  * Scan project and analyze dependencies
58
42
  */
@@ -72,7 +56,7 @@ export class MetadataScannerService {
72
56
  });
73
57
  // Parse .forceignore
74
58
  const forceIgnorePath = path.join(projectInfo.projectRoot, '.forceignore');
75
- if (await this.fileExists(forceIgnorePath)) {
59
+ if (await fileExists(forceIgnorePath)) {
76
60
  this.forceIgnoreParser = new ForceIgnoreParser();
77
61
  const loadResult = await this.forceIgnoreParser.load(projectInfo.projectRoot);
78
62
  logger.info('Loaded .forceignore', {
@@ -110,7 +94,7 @@ export class MetadataScannerService {
110
94
  async scanMetadata(projectRoot, packageDirs, errors, warnings) {
111
95
  const scannedPackages = await Promise.all(packageDirs.map(async (packageDir) => {
112
96
  const packagePath = path.isAbsolute(packageDir) ? packageDir : path.join(projectRoot, packageDir);
113
- if (!(await this.fileExists(packagePath))) {
97
+ if (!(await fileExists(packagePath))) {
114
98
  logger.warn('Package directory not found', { packagePath });
115
99
  warnings.push(`Package directory not found: ${packagePath}`);
116
100
  return [];
@@ -124,507 +108,57 @@ export class MetadataScannerService {
124
108
  */
125
109
  async scanPackageDirectory(packagePath, errors, warnings) {
126
110
  void warnings;
127
- const components = [];
128
- // Scan Apex Classes
129
- const apexFiles = await this.findFiles(packagePath, '**/classes/**/*.cls');
130
- const apexComponents = await Promise.all(apexFiles
131
- .filter((filePath) => !this.shouldIgnore(filePath) && !filePath.endsWith('.cls-meta.xml'))
132
- .map(async (filePath) => {
133
- try {
134
- const content = await fs.readFile(filePath, 'utf-8');
135
- const parsed = parseApexClass(filePath, content);
136
- return {
137
- name: parsed.className,
138
- type: 'ApexClass',
139
- filePath,
140
- dependencies: toNodeIds(parsed.dependencies.map((d) => d.className), 'ApexClass'),
141
- dependents: new Set(),
142
- priorityBoost: 0,
143
- };
144
- }
145
- catch (error) {
146
- const errorMsg = `Failed to parse Apex class ${filePath}: ${error instanceof Error ? error.message : String(error)}`;
147
- logger.warn(errorMsg);
148
- errors.push(errorMsg);
149
- return undefined;
150
- }
151
- }));
152
- components.push(...apexComponents.filter(isDefined));
153
- // Scan Apex Triggers
154
- const triggerFiles = await this.findFiles(packagePath, '**/triggers/**/*.trigger');
155
- const triggerComponents = await Promise.all(triggerFiles
156
- .filter((filePath) => !this.shouldIgnore(filePath) && !filePath.endsWith('.trigger-meta.xml'))
157
- .map(async (filePath) => {
158
- try {
159
- const content = await fs.readFile(filePath, 'utf-8');
160
- const parsed = parseApexTrigger(filePath, content);
161
- return {
162
- name: parsed.triggerName,
163
- type: 'ApexTrigger',
164
- filePath,
165
- dependencies: toNodeIds(parsed.dependencies.map((d) => d.className), 'ApexClass'),
166
- dependents: new Set(),
167
- priorityBoost: 0,
168
- };
169
- }
170
- catch (error) {
171
- const errorMsg = `Failed to parse Apex trigger ${filePath}: ${error instanceof Error ? error.message : String(error)}`;
172
- logger.warn(errorMsg);
173
- errors.push(errorMsg);
174
- return undefined;
175
- }
176
- }));
177
- components.push(...triggerComponents.filter(isDefined));
178
- // Scan LWC Components (container-based)
179
- const lwcDirs = await this.findDirectories(packagePath, '**/lwc/*');
180
- const lwcComponents = await Promise.all(lwcDirs
181
- .filter((lwcDir) => !this.shouldIgnore(lwcDir))
182
- .map(async (lwcDir) => {
183
- const componentName = path.basename(lwcDir);
184
- const jsFile = path.join(lwcDir, `${componentName}.js`);
185
- const tsFile = path.join(lwcDir, `${componentName}.ts`);
186
- const metaFile = path.join(lwcDir, `${componentName}.js-meta.xml`);
187
- const jsExists = await this.fileExists(jsFile);
188
- const tsExists = jsExists ? false : await this.fileExists(tsFile);
189
- const codeFile = jsExists ? jsFile : tsExists ? tsFile : null;
190
- if (!codeFile) {
191
- return undefined;
192
- }
193
- try {
194
- const jsContent = await fs.readFile(codeFile, 'utf-8');
195
- const metaContent = (await this.fileExists(metaFile)) ? await fs.readFile(metaFile, 'utf-8') : undefined;
196
- const parsed = parseLWC(componentName, jsContent, metaContent);
197
- return {
198
- name: componentName,
199
- type: 'LightningComponentBundle',
200
- filePath: codeFile,
201
- dependencies: new Set([...parsed.apexImports, ...parsed.lwcImports.map((imp) => `c:${imp}`)]),
202
- dependents: new Set(),
203
- priorityBoost: 0,
204
- };
205
- }
206
- catch (error) {
207
- const errorMsg = `Failed to parse LWC ${componentName}: ${error instanceof Error ? error.message : String(error)}`;
208
- logger.warn(errorMsg);
209
- errors.push(errorMsg);
210
- return undefined;
211
- }
212
- }));
213
- components.push(...lwcComponents.filter(isDefined));
214
- // Scan Aura Components (container-based)
215
- const auraDirs = await this.findDirectories(packagePath, '**/aura/*');
216
- const auraComponents = await Promise.all(auraDirs
217
- .filter((auraDir) => !this.shouldIgnore(auraDir))
218
- .map(async (auraDir) => {
219
- const componentName = path.basename(auraDir);
220
- const cmpFile = path.join(auraDir, `${componentName}.cmp`);
221
- if (!(await this.fileExists(cmpFile))) {
222
- return undefined;
223
- }
224
- try {
225
- const cmpContent = await fs.readFile(cmpFile, 'utf-8');
226
- const parsed = parseAura(componentName, cmpContent);
227
- const deps = new Set();
228
- if (parsed.apexController)
229
- deps.add(parsed.apexController);
230
- if (parsed.extendsComponent)
231
- deps.add(parsed.extendsComponent);
232
- parsed.implementsInterfaces.forEach((i) => deps.add(i));
233
- parsed.childComponents.forEach((c) => deps.add(`c:${c}`));
234
- return {
235
- name: componentName,
236
- type: 'AuraDefinitionBundle',
237
- filePath: cmpFile,
238
- dependencies: deps,
239
- dependents: new Set(),
240
- priorityBoost: 0,
241
- };
242
- }
243
- catch (error) {
244
- const errorMsg = `Failed to parse Aura component ${componentName}: ${error instanceof Error ? error.message : String(error)}`;
245
- logger.warn(errorMsg);
246
- errors.push(errorMsg);
247
- return undefined;
248
- }
249
- }));
250
- components.push(...auraComponents.filter(isDefined));
251
- // Scan Flows
252
- const flowFiles = await this.findFiles(packagePath, '**/flows/**/*.flow-meta.xml');
253
- const flowComponents = await Promise.all(flowFiles
254
- .filter((filePath) => !this.shouldIgnore(filePath))
255
- .map(async (filePath) => {
256
- try {
257
- const content = await fs.readFile(filePath, 'utf-8');
258
- const parsed = parseFlow(filePath, content);
259
- const deps = new Set();
260
- parsed.dependencies.forEach((d) => {
261
- if (d.type === 'apex_action' || d.type === 'subflow') {
262
- deps.add(d.name);
263
- }
264
- });
265
- return {
266
- name: parsed.flowName,
267
- type: 'Flow',
268
- filePath,
269
- dependencies: deps,
270
- dependents: new Set(),
271
- priorityBoost: 0,
272
- };
273
- }
274
- catch (error) {
275
- const errorMsg = `Failed to parse Flow ${filePath}: ${error instanceof Error ? error.message : String(error)}`;
276
- logger.warn(errorMsg);
277
- errors.push(errorMsg);
278
- return undefined;
279
- }
280
- }));
281
- components.push(...flowComponents.filter(isDefined));
282
- // Scan Custom Objects
283
- const objectDirs = await this.findDirectories(packagePath, '**/objects/*');
284
- const objectComponents = await Promise.all(objectDirs.map(async (objectDir) => {
285
- if (this.shouldIgnore(objectDir)) {
286
- return undefined;
287
- }
288
- const objectName = path.basename(objectDir);
289
- const objectFile = path.join(objectDir, `${objectName}.object-meta.xml`);
290
- if (!(await this.fileExists(objectFile))) {
291
- return undefined;
292
- }
293
- try {
294
- const content = await fs.readFile(objectFile, 'utf-8');
295
- const parsed = await parseCustomObject(objectName, content);
296
- const deps = new Set();
297
- parsed.dependencies.forEach((dependency) => {
298
- if ((dependency.type === 'lookup_field' || dependency.type === 'master_detail_field') &&
299
- dependency.referencedObject) {
300
- deps.add(dependency.referencedObject);
301
- }
302
- });
303
- return {
304
- name: objectName,
305
- type: 'CustomObject',
306
- filePath: objectFile,
307
- dependencies: deps,
308
- dependents: new Set(),
309
- priorityBoost: 0,
310
- };
311
- }
312
- catch (error) {
313
- const errorMsg = `Failed to parse Custom Object ${objectName}: ${error instanceof Error ? error.message : String(error)}`;
314
- logger.warn(errorMsg);
315
- errors.push(errorMsg);
316
- return undefined;
317
- }
318
- }));
319
- components.push(...objectComponents.filter(isDefined));
320
- // Scan Custom Metadata Types
321
- const cmtDirs = await this.findDirectories(packagePath, '**/customMetadata/*');
322
- const customMetadataComponents = await Promise.all(cmtDirs.map(async (cmtDir) => {
323
- if (this.shouldIgnore(cmtDir)) {
324
- return undefined;
325
- }
111
+ const componentGroups = await Promise.all([
112
+ this.scanRegisteredFileMetadata(packagePath, errors),
113
+ this.scanRegisteredDirectoryMetadata(packagePath, errors),
114
+ this.scanAutomationMetadata(packagePath, errors),
115
+ this.scanDataMetadata(packagePath, errors),
116
+ this.scanSecurityMetadata(packagePath, errors),
117
+ this.scanExperienceMetadata(packagePath, errors),
118
+ this.scanAIMetadata(packagePath, errors),
119
+ ]);
120
+ return componentGroups.flat();
121
+ }
122
+ async scanAutomationMetadata(packagePath, errors) {
123
+ return scanMetadataFiles(packagePath, '**/flows/**/*.flow-meta.xml', errors, 'Flow', this.shouldIgnorePath, parseFlowComponent);
124
+ }
125
+ async scanDataMetadata(packagePath, errors) {
126
+ const objectComponents = await scanMetadataDirectories(packagePath, '**/objects/*', errors, 'Custom Object', this.shouldIgnorePath, parseCustomObjectComponent);
127
+ const customMetadataComponents = await scanMetadataDirectories(packagePath, '**/customMetadata/*', errors, 'Custom Metadata Type', this.shouldIgnorePath, async (cmtDir) => {
326
128
  const typeName = path.basename(cmtDir);
327
129
  const typeFile = path.join(cmtDir, `${typeName}.md-meta.xml`);
328
- if (!(await this.fileExists(typeFile))) {
329
- return undefined;
330
- }
331
- try {
332
- const content = await fs.readFile(typeFile, 'utf-8');
333
- await parseCustomMetadataType(typeName, content);
334
- return {
335
- name: typeName,
336
- type: 'CustomMetadata',
337
- filePath: typeFile,
338
- dependencies: new Set(),
339
- dependents: new Set(),
340
- priorityBoost: 0,
341
- };
342
- }
343
- catch (error) {
344
- const errorMsg = `Failed to parse Custom Metadata Type ${typeName}: ${error instanceof Error ? error.message : String(error)}`;
345
- logger.warn(errorMsg);
346
- errors.push(errorMsg);
347
- return undefined;
348
- }
349
- }));
350
- components.push(...customMetadataComponents.filter(isDefined));
351
- // Scan Profiles
352
- const profileFiles = await this.findFiles(packagePath, '**/profiles/**/*.profile-meta.xml');
353
- const profileComponents = await Promise.all(profileFiles.map(async (filePath) => {
354
- if (this.shouldIgnore(filePath)) {
355
- return undefined;
356
- }
357
- try {
358
- const profileName = path.basename(filePath, '.profile-meta.xml');
359
- const parsed = await parseProfile(filePath, profileName);
360
- const deps = new Set();
361
- parsed.objectPermissions.forEach((permission) => deps.add(permission));
362
- parsed.apexClassAccesses.forEach((classAccess) => deps.add(classAccess));
363
- parsed.layoutAssignments.forEach((layoutAssignment) => deps.add(layoutAssignment));
364
- return {
365
- name: profileName,
366
- type: 'Profile',
367
- filePath,
368
- dependencies: deps,
369
- dependents: new Set(),
370
- priorityBoost: 0,
371
- };
372
- }
373
- catch (error) {
374
- const errorMsg = `Failed to parse Profile ${filePath}: ${error instanceof Error ? error.message : String(error)}`;
375
- logger.warn(errorMsg);
376
- errors.push(errorMsg);
377
- return undefined;
378
- }
379
- }));
380
- components.push(...profileComponents.filter(isDefined));
381
- // Scan Permission Sets
382
- const permSetFiles = await this.findFiles(packagePath, '**/permissionsets/**/*.permissionset-meta.xml');
383
- const permissionSetComponents = await Promise.all(permSetFiles.map(async (filePath) => {
384
- if (this.shouldIgnore(filePath)) {
385
- return undefined;
386
- }
387
- try {
388
- const permSetName = path.basename(filePath, '.permissionset-meta.xml');
389
- const parsed = await parsePermissionSet(filePath, permSetName);
390
- const deps = new Set();
391
- parsed.objectPermissions.forEach((permission) => deps.add(permission));
392
- parsed.apexClassAccesses.forEach((classAccess) => deps.add(classAccess));
393
- parsed.customPermissions.forEach((customPermission) => deps.add(customPermission));
394
- return {
395
- name: permSetName,
396
- type: 'PermissionSet',
397
- filePath,
398
- dependencies: deps,
399
- dependents: new Set(),
400
- priorityBoost: 0,
401
- };
402
- }
403
- catch (error) {
404
- const errorMsg = `Failed to parse Permission Set ${filePath}: ${error instanceof Error ? error.message : String(error)}`;
405
- logger.warn(errorMsg);
406
- errors.push(errorMsg);
407
- return undefined;
408
- }
409
- }));
410
- components.push(...permissionSetComponents.filter(isDefined));
411
- // Scan FlexiPages
412
- const flexipageFiles = await this.findFiles(packagePath, '**/flexipages/**/*.flexipage-meta.xml');
413
- const flexipageComponents = await Promise.all(flexipageFiles.map(async (filePath) => {
414
- if (this.shouldIgnore(filePath)) {
415
- return undefined;
416
- }
417
- try {
418
- const flexipageName = path.basename(filePath, '.flexipage-meta.xml');
419
- const parsed = await parseFlexiPage(filePath, flexipageName);
420
- const deps = new Set();
421
- parsed.lwcComponents.forEach((componentName) => deps.add(`c:${componentName}`));
422
- parsed.auraComponents.forEach((componentName) => deps.add(`c:${componentName}`));
423
- parsed.objects.forEach((objectName) => deps.add(objectName));
424
- return {
425
- name: flexipageName,
426
- type: 'FlexiPage',
427
- filePath,
428
- dependencies: deps,
429
- dependents: new Set(),
430
- priorityBoost: 0,
431
- };
432
- }
433
- catch (error) {
434
- const errorMsg = `Failed to parse FlexiPage ${filePath}: ${error instanceof Error ? error.message : String(error)}`;
435
- logger.warn(errorMsg);
436
- errors.push(errorMsg);
437
- return undefined;
438
- }
439
- }));
440
- components.push(...flexipageComponents.filter(isDefined));
441
- // Scan Layouts
442
- const layoutFiles = await this.findFiles(packagePath, '**/layouts/**/*.layout-meta.xml');
443
- const layoutComponents = await Promise.all(layoutFiles.map(async (filePath) => {
444
- if (this.shouldIgnore(filePath)) {
445
- return undefined;
446
- }
447
- try {
448
- const layoutName = path.basename(filePath, '.layout-meta.xml');
449
- const parsed = await parseLayout(filePath, layoutName);
450
- const deps = new Set();
451
- deps.add(parsed.object);
452
- parsed.customButtons.forEach((buttonName) => deps.add(buttonName));
453
- parsed.visualforcePages.forEach((pageName) => deps.add(pageName));
454
- return {
455
- name: layoutName,
456
- type: 'Layout',
457
- filePath,
458
- dependencies: deps,
459
- dependents: new Set(),
460
- priorityBoost: 0,
461
- };
462
- }
463
- catch (error) {
464
- const errorMsg = `Failed to parse Layout ${filePath}: ${error instanceof Error ? error.message : String(error)}`;
465
- logger.warn(errorMsg);
466
- errors.push(errorMsg);
467
- return undefined;
468
- }
469
- }));
470
- components.push(...layoutComponents.filter(isDefined));
471
- // Scan Email Templates
472
- const emailTemplateFiles = await this.findFiles(packagePath, '**/email/**/*.email-meta.xml');
473
- const emailTemplateComponents = await Promise.all(emailTemplateFiles.map(async (filePath) => {
474
- if (this.shouldIgnore(filePath)) {
475
- return undefined;
476
- }
477
- try {
478
- const templateName = path.basename(filePath, '.email-meta.xml');
479
- const content = await fs.readFile(filePath, 'utf-8');
480
- const parsed = await parseEmailTemplate(templateName, content, content);
481
- const deps = new Set();
482
- parsed.dependencies.forEach((dependency) => {
483
- if (dependency.type === 'visualforce_page') {
484
- deps.add(dependency.name);
485
- }
486
- });
487
- return {
488
- name: templateName,
489
- type: 'EmailTemplate',
490
- filePath,
491
- dependencies: deps,
492
- dependents: new Set(),
493
- priorityBoost: 0,
494
- };
495
- }
496
- catch (error) {
497
- const errorMsg = `Failed to parse Email Template ${filePath}: ${error instanceof Error ? error.message : String(error)}`;
498
- logger.warn(errorMsg);
499
- errors.push(errorMsg);
500
- return undefined;
501
- }
502
- }));
503
- components.push(...emailTemplateComponents.filter(isDefined));
504
- // Scan Bots
505
- const botFiles = await this.findFiles(packagePath, '**/bots/**/*.bot-meta.xml');
506
- const botComponents = await Promise.all(botFiles.map(async (filePath) => {
507
- if (this.shouldIgnore(filePath)) {
508
- return undefined;
509
- }
510
- try {
511
- const botName = path.basename(filePath, '.bot-meta.xml');
512
- const parsed = await parseBot(filePath, botName);
513
- const deps = new Set();
514
- parsed.flows.forEach((flowName) => deps.add(flowName));
515
- parsed.apexActions.forEach((actionName) => deps.add(actionName));
516
- parsed.genAiPrompts.forEach((promptName) => deps.add(promptName));
517
- return {
518
- name: botName,
519
- type: 'Bot',
520
- filePath,
521
- dependencies: deps,
522
- dependents: new Set(),
523
- priorityBoost: 0,
524
- };
525
- }
526
- catch (error) {
527
- const errorMsg = `Failed to parse Bot ${filePath}: ${error instanceof Error ? error.message : String(error)}`;
528
- logger.warn(errorMsg);
529
- errors.push(errorMsg);
530
- return undefined;
531
- }
532
- }));
533
- components.push(...botComponents.filter(isDefined));
534
- // Scan GenAI Prompts
535
- const genaiFiles = await this.findFiles(packagePath, '**/genaiPromptTemplates/**/*.genAiPromptTemplate-meta.xml');
536
- const genAiPromptComponents = await Promise.all(genaiFiles.map(async (filePath) => {
537
- if (this.shouldIgnore(filePath)) {
538
- return undefined;
539
- }
540
- try {
541
- const promptName = path.basename(filePath, '.genAiPromptTemplate-meta.xml');
542
- const parsed = await parseGenAiPrompt(filePath, promptName);
543
- const deps = new Set();
544
- parsed.sobjects.forEach((sObjectName) => deps.add(sObjectName));
545
- parsed.dependencies.sobjects.forEach((sObjectName) => deps.add(sObjectName));
546
- return {
547
- name: promptName,
548
- type: 'GenAiPromptTemplate',
549
- filePath,
550
- dependencies: deps,
551
- dependents: new Set(),
552
- priorityBoost: 0,
553
- };
554
- }
555
- catch (error) {
556
- const errorMsg = `Failed to parse GenAI Prompt ${filePath}: ${error instanceof Error ? error.message : String(error)}`;
557
- logger.warn(errorMsg);
558
- errors.push(errorMsg);
559
- return undefined;
560
- }
561
- }));
562
- components.push(...genAiPromptComponents.filter(isDefined));
563
- // Scan Visualforce Pages/Components
564
- const vfPageFiles = await this.findFiles(packagePath, '**/pages/**/*.page');
565
- const vfComponentFiles = await this.findFiles(packagePath, '**/components/**/*.component');
566
- const visualforceComponents = await Promise.all([...vfPageFiles, ...vfComponentFiles].map(async (filePath) => {
567
- if (this.shouldIgnore(filePath)) {
568
- return undefined;
569
- }
570
- try {
571
- const content = await fs.readFile(filePath, 'utf-8');
572
- const fileName = path.basename(filePath);
573
- const parsed = parseVisualforce(fileName, content);
574
- const deps = new Set();
575
- parsed.dependencies.forEach((dependency) => {
576
- if (dependency.type === 'apex_controller' || dependency.type === 'apex_extension') {
577
- deps.add(dependency.name);
578
- }
579
- });
580
- const componentType = parsed.type === 'page' ? 'VisualforcePage' : 'VisualforceComponent';
581
- return {
582
- name: parsed.name,
583
- type: componentType,
584
- filePath,
585
- dependencies: deps,
586
- dependents: new Set(),
587
- priorityBoost: 0,
588
- };
589
- }
590
- catch (error) {
591
- const errorMsg = `Failed to parse Visualforce ${filePath}: ${error instanceof Error ? error.message : String(error)}`;
592
- logger.warn(errorMsg);
593
- errors.push(errorMsg);
594
- return undefined;
130
+ if (!(await fileExists(typeFile))) {
131
+ return [];
595
132
  }
596
- }));
597
- components.push(...visualforceComponents.filter(isDefined));
598
- return components;
133
+ return parseCustomMetadataComponents(cmtDir);
134
+ }, (directoryPath) => path.basename(directoryPath));
135
+ return [...objectComponents, ...customMetadataComponents];
599
136
  }
600
- /**
601
- * Find files matching a glob pattern
602
- */
603
- async findFiles(rootPath, pattern) {
604
- const fullPattern = path.join(rootPath, pattern);
605
- const files = await globAsync(fullPattern, {
606
- ignore: ['**/node_modules/**', '**/.git/**'],
607
- });
608
- return files;
137
+ async scanSecurityMetadata(packagePath, errors) {
138
+ const profileComponents = await scanMetadataFiles(packagePath, '**/profiles/**/*.profile-meta.xml', errors, 'Profile', this.shouldIgnorePath, parseProfileComponent);
139
+ const permissionSetComponents = await scanMetadataFiles(packagePath, '**/permissionsets/**/*.permissionset-meta.xml', errors, 'Permission Set', this.shouldIgnorePath, parsePermissionSetComponent);
140
+ return [...profileComponents, ...permissionSetComponents];
609
141
  }
610
- /**
611
- * Find directories matching a glob pattern
612
- */
613
- async findDirectories(rootPath, pattern) {
614
- const fullPattern = path.join(rootPath, pattern);
615
- const allMatches = await globAsync(fullPattern, {
616
- ignore: ['**/node_modules/**', '**/.git/**'],
617
- });
618
- const directoryMatches = await Promise.all(allMatches.map(async (match) => {
619
- try {
620
- const stat = await fs.stat(match);
621
- return stat.isDirectory() ? match : undefined;
622
- }
623
- catch {
624
- return undefined;
625
- }
626
- }));
627
- return directoryMatches.filter((match) => match !== undefined);
142
+ async scanExperienceMetadata(packagePath, errors) {
143
+ const flexipageComponents = await scanMetadataFiles(packagePath, '**/flexipages/**/*.flexipage-meta.xml', errors, 'FlexiPage', this.shouldIgnorePath, parseFlexiPageComponent);
144
+ const layoutComponents = await scanMetadataFiles(packagePath, '**/layouts/**/*.layout-meta.xml', errors, 'Layout', this.shouldIgnorePath, parseLayoutComponent);
145
+ const emailTemplateComponents = await scanMetadataFiles(packagePath, '**/email/**/*.email-meta.xml', errors, 'Email Template', this.shouldIgnorePath, (filePath) => parseEmailTemplateComponent(filePath, fileExists));
146
+ const visualforceComponents = await Promise.all([
147
+ scanMetadataFiles(packagePath, '**/pages/**/*.page', errors, 'Visualforce', this.shouldIgnorePath, parseVisualforceComponent),
148
+ scanMetadataFiles(packagePath, '**/components/**/*.component', errors, 'Visualforce', this.shouldIgnorePath, parseVisualforceComponent),
149
+ ]);
150
+ return [...flexipageComponents, ...layoutComponents, ...emailTemplateComponents, ...visualforceComponents.flat()];
151
+ }
152
+ async scanAIMetadata(packagePath, errors) {
153
+ const botComponents = await scanMetadataFiles(packagePath, '**/bots/**/*.bot-meta.xml', errors, 'Bot', this.shouldIgnorePath, parseBotComponent);
154
+ const genAiPromptComponents = await scanMetadataFiles(packagePath, '**/genaiPromptTemplates/**/*.genAiPromptTemplate-meta.xml', errors, 'GenAI Prompt', this.shouldIgnorePath, parseGenAiPromptComponent);
155
+ return [...botComponents, ...genAiPromptComponents];
156
+ }
157
+ async scanRegisteredFileMetadata(packagePath, errors) {
158
+ return scanRegisteredFileMetadata(packagePath, errors, this.shouldIgnorePath, CODE_FILE_SCANNERS);
159
+ }
160
+ async scanRegisteredDirectoryMetadata(packagePath, errors) {
161
+ return scanRegisteredDirectoryMetadata(packagePath, errors, this.shouldIgnorePath, CODE_DIRECTORY_SCANNERS);
628
162
  }
629
163
  /**
630
164
  * Check if file should be ignored by .forceignore
@@ -635,17 +169,5 @@ export class MetadataScannerService {
635
169
  }
636
170
  return this.forceIgnoreParser.isIgnored(filePath);
637
171
  }
638
- /**
639
- * Check if file/directory exists
640
- */
641
- async fileExists(filePath) {
642
- try {
643
- await fs.access(filePath);
644
- return true;
645
- }
646
- catch {
647
- return false;
648
- }
649
- }
650
172
  }
651
173
  //# sourceMappingURL=metadata-scanner-service.js.map