@salesforce/plugin-omnistudio-migration-tool 2.0.0-rc.60 → 2.0.0-rc.61

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 (65) hide show
  1. package/lib/commands/omnistudio/migration/assess.d.ts +1 -0
  2. package/lib/commands/omnistudio/migration/assess.js +27 -0
  3. package/lib/commands/omnistudio/migration/assess.js.map +1 -1
  4. package/lib/commands/omnistudio/migration/clean.js +4 -2
  5. package/lib/commands/omnistudio/migration/clean.js.map +1 -1
  6. package/lib/commands/omnistudio/migration/migrate.js +11 -0
  7. package/lib/commands/omnistudio/migration/migrate.js.map +1 -1
  8. package/lib/javascripts/reportGeneratorUtility.js +181 -0
  9. package/lib/migration/ApexNamespaceRegistry.d.ts +49 -0
  10. package/lib/migration/ApexNamespaceRegistry.js +130 -0
  11. package/lib/migration/ApexNamespaceRegistry.js.map +1 -0
  12. package/lib/migration/CustomCssRegistry.d.ts +117 -0
  13. package/lib/migration/CustomCssRegistry.js +234 -0
  14. package/lib/migration/CustomCssRegistry.js.map +1 -0
  15. package/lib/migration/base.js +1 -1
  16. package/lib/migration/base.js.map +1 -1
  17. package/lib/migration/customLabels.js +14 -3
  18. package/lib/migration/customLabels.js.map +1 -1
  19. package/lib/migration/flexcard.d.ts +98 -0
  20. package/lib/migration/flexcard.js +448 -15
  21. package/lib/migration/flexcard.js.map +1 -1
  22. package/lib/migration/interfaces.d.ts +1 -0
  23. package/lib/migration/interfaces.js.map +1 -1
  24. package/lib/migration/omniscript.d.ts +42 -1
  25. package/lib/migration/omniscript.js +249 -6
  26. package/lib/migration/omniscript.js.map +1 -1
  27. package/lib/migration/omniscriptInstance.d.ts +90 -0
  28. package/lib/migration/omniscriptInstance.js +755 -0
  29. package/lib/migration/omniscriptInstance.js.map +1 -0
  30. package/lib/styles/reportGenerator.css +25 -0
  31. package/lib/templates/assessmentReport.template +8 -0
  32. package/lib/templates/migrationReport.template +8 -0
  33. package/lib/utils/constants/documentRegistry.d.ts +6 -0
  34. package/lib/utils/constants/documentRegistry.js +6 -0
  35. package/lib/utils/constants/documentRegistry.js.map +1 -1
  36. package/lib/utils/constants/stringContants.d.ts +28 -5
  37. package/lib/utils/constants/stringContants.js +30 -5
  38. package/lib/utils/constants/stringContants.js.map +1 -1
  39. package/lib/utils/customLabels.d.ts +1 -0
  40. package/lib/utils/customLabels.js +3 -0
  41. package/lib/utils/customLabels.js.map +1 -1
  42. package/lib/utils/interfaces.d.ts +19 -0
  43. package/lib/utils/query/index.d.ts +1 -0
  44. package/lib/utils/query/index.js +17 -0
  45. package/lib/utils/query/index.js.map +1 -1
  46. package/lib/utils/resultsbuilder/FlexcardAssessmentReporter.js +4 -0
  47. package/lib/utils/resultsbuilder/FlexcardAssessmentReporter.js.map +1 -1
  48. package/lib/utils/resultsbuilder/SaveForLaterAssessmentReporter.d.ts +12 -0
  49. package/lib/utils/resultsbuilder/SaveForLaterAssessmentReporter.js +109 -0
  50. package/lib/utils/resultsbuilder/SaveForLaterAssessmentReporter.js.map +1 -0
  51. package/lib/utils/resultsbuilder/assessmentReporter.js +18 -2
  52. package/lib/utils/resultsbuilder/assessmentReporter.js.map +1 -1
  53. package/lib/utils/resultsbuilder/helpers/AssessmentReportHelper.d.ts +11 -1
  54. package/lib/utils/resultsbuilder/helpers/AssessmentReportHelper.js +64 -1
  55. package/lib/utils/resultsbuilder/helpers/AssessmentReportHelper.js.map +1 -1
  56. package/lib/utils/resultsbuilder/index.d.ts +4 -0
  57. package/lib/utils/resultsbuilder/index.js +61 -0
  58. package/lib/utils/resultsbuilder/index.js.map +1 -1
  59. package/lib/utils/stringUtils.d.ts +15 -0
  60. package/lib/utils/stringUtils.js +37 -0
  61. package/lib/utils/stringUtils.js.map +1 -1
  62. package/messages/assess.json +23 -2
  63. package/messages/migrate.json +29 -3
  64. package/oclif.manifest.json +1 -1
  65. package/package.json +5 -4
@@ -9,6 +9,7 @@ const Element_1 = __importDefault(require("../mappings/Element"));
9
9
  const OmniScriptDefinition_1 = __importDefault(require("../mappings/OmniScriptDefinition"));
10
10
  const utils_1 = require("../utils");
11
11
  const base_1 = require("./base");
12
+ const CustomCssRegistry_1 = require("./CustomCssRegistry");
12
13
  const interfaces_1 = require("./interfaces");
13
14
  const net_1 = require("../utils/net");
14
15
  const FormulaUtil_1 = require("../utils/formula/FormulaUtil");
@@ -19,10 +20,13 @@ const storageUtil_1 = require("../utils/storageUtil");
19
20
  const dataModelService_1 = require("../utils/dataModelService");
20
21
  const recordPrioritization_1 = require("../utils/recordPrioritization");
21
22
  const stringContants_1 = require("../utils/constants/stringContants");
23
+ const ApexNamespaceRegistry_1 = require("./ApexNamespaceRegistry");
22
24
  class OmniScriptMigrationTool extends base_1.BaseMigrationTool {
23
25
  constructor(exportType, namespace, connection, logger, messages, ux, allVersions) {
24
26
  super(namespace, connection, logger, messages, ux);
27
+ this.hookRegisteredClasses = new Set();
25
28
  this.IS_STANDARD_DATA_MODEL = (0, dataModelService_1.isStandardDataModel)();
29
+ this.apexNamespaceRegistry = ApexNamespaceRegistry_1.ApexNamespaceRegistry.getInstance();
26
30
  // Reserved keys that should not be used for storing output
27
31
  this.reservedKeys = new Set(['Request', 'Response']);
28
32
  // Tags to validate in PropertySet for reserved key usage
@@ -31,6 +35,36 @@ class OmniScriptMigrationTool extends base_1.BaseMigrationTool {
31
35
  this.OMNISCRIPT = 'OmniScript';
32
36
  this.exportType = exportType;
33
37
  this.allVersions = allVersions;
38
+ // Configure the shared Custom CSS registry. Idempotent — safe to call from
39
+ // both the OS and IP tool instances within a single assess run.
40
+ CustomCssRegistry_1.CustomCssRegistry.getInstance().init(connection, namespace, messages);
41
+ }
42
+ async loadHookRegistrations() {
43
+ try {
44
+ const objectName = `${this.namespacePrefix}CustomClassImplementation__c`;
45
+ const soql = `SELECT Id, Name FROM ${objectName} WHERE Name LIKE '%Hook' LIMIT 200`;
46
+ const result = await this.connection.query(soql);
47
+ const classes = new Set();
48
+ if (result.totalSize > 0) {
49
+ for (const record of result.records) {
50
+ const hookName = record.Name || '';
51
+ if (hookName.endsWith('Hook')) {
52
+ classes.add(hookName.substring(0, hookName.length - 4));
53
+ }
54
+ }
55
+ }
56
+ return classes;
57
+ }
58
+ catch (e) {
59
+ logger_1.Logger.warn('Unable to query CustomClassImplementation__c for hook registrations: ' + e);
60
+ return new Set();
61
+ }
62
+ }
63
+ hasHookForClass(remoteClass) {
64
+ if (this.hookRegisteredClasses.size === 0 || !remoteClass)
65
+ return false;
66
+ const simpleName = remoteClass.includes('.') ? remoteClass.split('.').pop() : remoteClass;
67
+ return this.hookRegisteredClasses.has(simpleName);
34
68
  }
35
69
  getName(singular = false) {
36
70
  if (this.exportType === OmniScriptExportType.IP) {
@@ -138,6 +172,9 @@ class OmniScriptMigrationTool extends base_1.BaseMigrationTool {
138
172
  try {
139
173
  const exportComponentType = this.getName();
140
174
  const omniscripts = await this.getAllOmniScripts();
175
+ if (this.exportType !== OmniScriptExportType.OS) {
176
+ this.hookRegisteredClasses = await this.loadHookRegistrations();
177
+ }
141
178
  if ((0, dataModelService_1.isStandardDataModelWithMetadataAPIEnabled)()) {
142
179
  // For the Standard Data Model Orgs, we only need to prepare the storage
143
180
  return this.handleAssessmentForStdDataModelOrgsWithMetadataAPIEnabled(omniscripts);
@@ -292,7 +329,10 @@ class OmniScriptMigrationTool extends base_1.BaseMigrationTool {
292
329
  const missingOS = [];
293
330
  const dependenciesRA = [];
294
331
  const dependenciesLWC = [];
332
+ const namespaceWarnings = [];
333
+ const namespaceErrors = [];
295
334
  //const missingRA: string[] = [];
335
+ const hookEnabledSteps = [];
296
336
  // Check for duplicate element names within the same OmniScript
297
337
  const elementNames = new Set();
298
338
  const duplicateElementNames = new Set();
@@ -307,6 +347,8 @@ class OmniScriptMigrationTool extends base_1.BaseMigrationTool {
307
347
  elementNames.add(elemName);
308
348
  }
309
349
  }
350
+ // Detect elements with corrupted parent-child level hierarchy
351
+ const corruptedParentChildElements = this.detectCorruptedParentChildElements(elements);
310
352
  for (const elem of elements) {
311
353
  const type = elem[this.getFieldKey('Type__c')];
312
354
  const elemName = `${elem['Name']}`;
@@ -364,13 +406,28 @@ class OmniScriptMigrationTool extends base_1.BaseMigrationTool {
364
406
  const nameVal = `${elemName}`;
365
407
  const className = propertySet['remoteClass'];
366
408
  const methodName = propertySet['remoteMethod'];
367
- if (className && methodName)
368
- dependenciesRA.push({ name: className + '.' + methodName, location: nameVal });
409
+ if (className && methodName) {
410
+ const status = this.apexNamespaceRegistry.resolveStatus(className);
411
+ const qualifiedClass = this.apexNamespaceRegistry.getQualifiedClassName(className);
412
+ dependenciesRA.push({ name: qualifiedClass + '.' + methodName, location: nameVal });
413
+ if (status === ApexNamespaceRegistry_1.ApexResolveStatus.NAMESPACED) {
414
+ namespaceWarnings.push(this.messages.getMessage('remoteActionNamespaceWarning', [nameVal, className, qualifiedClass]));
415
+ }
416
+ else if (status === ApexNamespaceRegistry_1.ApexResolveStatus.NOT_FOUND) {
417
+ namespaceErrors.push(this.messages.getMessage('apexClassNotFound', [className, nameVal]));
418
+ }
419
+ }
420
+ if (className && this.hasHookForClass(className)) {
421
+ hookEnabledSteps.push(nameVal);
422
+ }
369
423
  }
370
424
  // To handle radio , multiselect
371
425
  if (propertySet['optionSource'] && propertySet['optionSource']['type'] === 'Custom') {
372
426
  const nameVal = `${elemName}`;
373
- dependenciesRA.push({ name: propertySet['optionSource']['source'], location: nameVal });
427
+ const source = propertySet['optionSource']['source'];
428
+ if (source) {
429
+ dependenciesRA.push({ name: source, location: nameVal });
430
+ }
374
431
  }
375
432
  if (type === stringContants_1.Constants.CustomLightningWebComponent) {
376
433
  const nameVal = `${elemName}`;
@@ -394,8 +451,8 @@ class OmniScriptMigrationTool extends base_1.BaseMigrationTool {
394
451
  const omniScriptName = omniscript[this.getFieldKey('Name')];
395
452
  const existingOmniScriptNameVal = new stringval_1.StringVal(omniScriptName, 'name');
396
453
  let assessmentStatus = 'Ready for migration';
397
- const warnings = [];
398
- const errors = [];
454
+ const warnings = [...namespaceWarnings];
455
+ const errors = [...namespaceErrors];
399
456
  // Check for missing mandatory fields for Integration Procedures
400
457
  if (omniProcessType === 'Integration Procedure') {
401
458
  if (!existingType || existingType.trim() === '') {
@@ -516,6 +573,12 @@ class OmniScriptMigrationTool extends base_1.BaseMigrationTool {
516
573
  warnings.unshift(this.messages.getMessage('reservedKeysFoundInPropertySet', [reservedKeysList]));
517
574
  assessmentStatus = 'Needs manual intervention';
518
575
  }
576
+ // Add warning for corrupted parent-child level hierarchy (elements at same level as their parent)
577
+ if (corruptedParentChildElements.size > 0) {
578
+ const corruptedNamesList = Array.from(corruptedParentChildElements).join(', ');
579
+ warnings.push(this.messages.getMessage('corruptedParentChildLevel', [omniProcessType, corruptedNamesList, omniProcessType]));
580
+ assessmentStatus = 'Needs manual intervention';
581
+ }
519
582
  if (omniProcessType === this.OMNISCRIPT) {
520
583
  const type = omniscript[this.getFieldKey('IsLwcEnabled__c')] ? 'LWC' : 'Angular';
521
584
  if (type === 'Angular') {
@@ -523,6 +586,19 @@ class OmniScriptMigrationTool extends base_1.BaseMigrationTool {
523
586
  assessmentStatus = 'Needs manual intervention';
524
587
  }
525
588
  }
589
+ // Scan custom Lightning/Newport stylesheets for managed-package namespace references.
590
+ // Any hit forces 'Needs manual intervention' because the migrated component will
591
+ // silently lose styling until the customer rewrites the stylesheet.
592
+ const hasStylesheetNamespaceRef = await this.collectStylesheetNamespaceDependencies(omniscript, warnings);
593
+ if (hasStylesheetNamespaceRef) {
594
+ assessmentStatus = 'Needs manual intervention';
595
+ }
596
+ if (namespaceErrors.length > 0) {
597
+ assessmentStatus = 'Needs manual intervention';
598
+ }
599
+ else if (namespaceWarnings.length > 0 && assessmentStatus === 'Ready for migration') {
600
+ assessmentStatus = 'Warnings';
601
+ }
526
602
  // Deduplicate all dependency arrays to ensure no duplicates
527
603
  // For Remote Actions and LWCs, deduplicate by name property
528
604
  const uniqueRA = Array.from(new Map(dependenciesRA.map((item) => [item.name, item])).values());
@@ -534,6 +610,9 @@ class OmniScriptMigrationTool extends base_1.BaseMigrationTool {
534
610
  const uniqueMissingDR = [...new Set(missingDR)];
535
611
  const uniqueMissingIP = [...new Set(missingIP)];
536
612
  const uniqueMissingOS = [...new Set(missingOS)];
613
+ if (hookEnabledSteps.length > 0 && omniProcessType === 'Integration Procedure') {
614
+ warnings.push(this.messages.getMessage('prePostHookAutoEnabled', [hookEnabledSteps.join(', ')]));
615
+ }
537
616
  const result = {
538
617
  name: recordName,
539
618
  id: omniscript['Id'],
@@ -675,6 +754,9 @@ class OmniScriptMigrationTool extends base_1.BaseMigrationTool {
675
754
  async migrate() {
676
755
  // Get All Records from OmniScript__c (IP & OS Parent Records)
677
756
  const omniscripts = await this.getAllOmniScripts();
757
+ if (this.exportType !== OmniScriptExportType.OS) {
758
+ this.hookRegisteredClasses = await this.loadHookRegistrations();
759
+ }
678
760
  if ((0, dataModelService_1.isStandardDataModelWithMetadataAPIEnabled)()) {
679
761
  return this.handleMigrationForStdDataModelOrgsWithMetadataAPIEnabled(omniscripts);
680
762
  }
@@ -756,6 +838,30 @@ class OmniScriptMigrationTool extends base_1.BaseMigrationTool {
756
838
  originalOsRecords.set(recordId, omniscript);
757
839
  continue;
758
840
  }
841
+ // Check for corrupted parent-child level hierarchy (parent and child at same level)
842
+ const corruptedParentChildElements = this.detectCorruptedParentChildElements(elements);
843
+ if (corruptedParentChildElements.size > 0) {
844
+ const corruptedNamesList = Array.from(corruptedParentChildElements).join(', ');
845
+ const skippedResponse = {
846
+ referenceId: recordId,
847
+ id: '',
848
+ success: false,
849
+ hasErrors: false,
850
+ errors: [],
851
+ warnings: [
852
+ this.messages.getMessage('corruptedParentChildLevel', [
853
+ omniProcessType,
854
+ corruptedNamesList,
855
+ omniProcessType,
856
+ ]),
857
+ ],
858
+ newName: '',
859
+ skipped: true,
860
+ };
861
+ osUploadInfo.set(recordId, skippedResponse);
862
+ originalOsRecords.set(recordId, omniscript);
863
+ continue;
864
+ }
759
865
  if (omniscript[this.getFieldKey('IsProcedure__c')] === true) {
760
866
  // Check for missing mandatory fields for Integration Procedures
761
867
  const existingType = omniscript[this.getFieldKey('Type__c')];
@@ -1569,6 +1675,9 @@ class OmniScriptMigrationTool extends base_1.BaseMigrationTool {
1569
1675
  case stringContants_1.Constants.RemoteAction:
1570
1676
  this.processRemoteAction(propSet);
1571
1677
  break;
1678
+ case stringContants_1.Constants.NavigateAction:
1679
+ this.processNavigateAction(propSet);
1680
+ break;
1572
1681
  default:
1573
1682
  // Handle other element types if needed
1574
1683
  break;
@@ -1788,6 +1897,58 @@ class OmniScriptMigrationTool extends base_1.BaseMigrationTool {
1788
1897
  propSetMap['Sub Type'] = this.cleanName(osSubType);
1789
1898
  }
1790
1899
  }
1900
+ /**
1901
+ * Processes Navigate Action elements so the migrated parent OmniScript can launch its child
1902
+ * under the standard runtime. The standard-runtime Navigate Action LWC reads
1903
+ * `omniscript__type`, `omniscript__subType`, `omniscript__language` from the propertySet to
1904
+ * resolve the child OmniScript (it does not consume `targetLWC`). Params on the URL bound for
1905
+ * the OmniScript page must use the `omniscript__` prefix instead of the managed-package `c__`
1906
+ * prefix.
1907
+ * @param propSetMap Property set map from the element
1908
+ */
1909
+ processNavigateAction(propSetMap) {
1910
+ if (propSetMap.targetType !== 'Vlocity OmniScript') {
1911
+ return;
1912
+ }
1913
+ const lwcRef = typeof propSetMap.targetLWC === 'string' ? propSetMap.targetLWC : '';
1914
+ if (lwcRef) {
1915
+ // propertySet stores the LWC tag form ("c:foo" or "c__foo"); the ES-module form ("c/foo")
1916
+ // never appears here, so stripping a leading "<ns>:" or "c__" covers all valid inputs.
1917
+ const stripped = lwcRef
1918
+ .replace(/^[^:]+:/, '')
1919
+ .replace(/^c__/, '')
1920
+ .toLowerCase();
1921
+ const candidates = this.nameRegistry.getOmniScriptMappingKeys().filter((key) => {
1922
+ const parts = key.split('_');
1923
+ if (parts.length < 2)
1924
+ return false;
1925
+ const type = parts[0];
1926
+ const subType = parts[1];
1927
+ const language = parts[2] || 'English';
1928
+ const candidate = `${this.cleanName(type)}${this.cleanName(subType)}${language}`.toLowerCase();
1929
+ return candidate === stripped;
1930
+ });
1931
+ if (candidates.length > 1) {
1932
+ logger_1.Logger.logVerbose(`\nMultiple OmniScript registry keys collapse to the same LWC name '${lwcRef}': ${candidates.join(', ')}. Using the first match.`);
1933
+ }
1934
+ const match = candidates[0];
1935
+ if (match) {
1936
+ const cleanedFullName = this.nameRegistry.getCleanedName(match, 'OmniScript');
1937
+ const cleanedParts = cleanedFullName.split('_');
1938
+ if (cleanedParts.length >= 2) {
1939
+ propSetMap.omniscript__type = cleanedParts[0];
1940
+ propSetMap.omniscript__subType = cleanedParts[1];
1941
+ propSetMap.omniscript__language = cleanedParts[2] || match.split('_')[2] || 'English';
1942
+ }
1943
+ }
1944
+ else {
1945
+ logger_1.Logger.logVerbose(`\n${this.messages.getMessage('componentMappingNotFound', ['OmniScript', lwcRef])}`);
1946
+ }
1947
+ }
1948
+ if (typeof propSetMap.targetLWCParams === 'string' && propSetMap.targetLWCParams) {
1949
+ propSetMap.targetLWCParams = propSetMap.targetLWCParams.replace(/(^|&)c__/g, '$1omniscript__');
1950
+ }
1951
+ }
1791
1952
  /**
1792
1953
  * Processes Step elements to update reference names
1793
1954
  * @param propSetMap Property set map from the element
@@ -1897,11 +2058,20 @@ class OmniScriptMigrationTool extends base_1.BaseMigrationTool {
1897
2058
  }
1898
2059
  }
1899
2060
  /**
1900
- * Processes Remote Action elements to update transform bundle references
2061
+ * Processes Remote Action elements to update transform bundle references and qualify remoteClass with namespace
1901
2062
  * @param propSetMap Property set map from the element
1902
2063
  */
1903
2064
  processRemoteAction(propSetMap) {
1904
2065
  this.processTransformBundles(propSetMap);
2066
+ if (propSetMap.remoteClass) {
2067
+ propSetMap.remoteClass = this.apexNamespaceRegistry.getQualifiedClassName(propSetMap.remoteClass);
2068
+ }
2069
+ if (this.hasHookForClass(propSetMap.remoteClass)) {
2070
+ const remoteOptions = propSetMap['remoteOptions'] || {};
2071
+ remoteOptions['PreHook'] = true;
2072
+ remoteOptions['PostHook'] = true;
2073
+ propSetMap['remoteOptions'] = remoteOptions;
2074
+ }
1905
2075
  }
1906
2076
  /**
1907
2077
  * Processes lwcComponentOverride property to update FlexCard reference names
@@ -2137,6 +2307,79 @@ class OmniScriptMigrationTool extends base_1.BaseMigrationTool {
2137
2307
  });
2138
2308
  });
2139
2309
  }
2310
+ /**
2311
+ * Reads the OmniScript's custom Lightning/Newport stylesheet references from
2312
+ * PropertySetConfig and warns if the referenced StaticResource's CSS body
2313
+ * still contains the org's managed-package namespace string. Such references
2314
+ * won't resolve after migration and would silently break styling, so the
2315
+ * caller escalates `migrationStatus` to 'Needs manual intervention' on a hit.
2316
+ *
2317
+ * Heavy lifting (StaticResource lookup, body fetch, zip extraction, scan,
2318
+ * cache) lives in {@link CustomCssRegistry} so the same cache can later be
2319
+ * shared with FlexCard assessment without re-querying the same resources.
2320
+ *
2321
+ * @param omniscript The OmniScript/IP record being assessed.
2322
+ * @param warnings Warnings array on the in-progress OSAssessmentInfo (mutated).
2323
+ * @returns `true` if at least one namespace-referencing stylesheet
2324
+ * was found and a warning was pushed.
2325
+ */
2326
+ async collectStylesheetNamespaceDependencies(omniscript, warnings) {
2327
+ const registry = CustomCssRegistry_1.CustomCssRegistry.getInstance();
2328
+ if (!registry.isEnabled()) {
2329
+ return false;
2330
+ }
2331
+ const propertySetConfigStr = omniscript[this.getFieldKey('PropertySet__c')];
2332
+ if (!propertySetConfigStr) {
2333
+ return false;
2334
+ }
2335
+ let propertySetConfig;
2336
+ try {
2337
+ propertySetConfig = JSON.parse(propertySetConfigStr);
2338
+ }
2339
+ catch (ex) {
2340
+ logger_1.Logger.error(`Failed to parse PropertySetConfig for stylesheet scan: ${omniscript['Name']}`);
2341
+ return false;
2342
+ }
2343
+ const { stylesheetsWithNamespaceRefs } = await registry.scanOmniScriptStylesheets(propertySetConfig === null || propertySetConfig === void 0 ? void 0 : propertySetConfig.stylesheet);
2344
+ let pushed = false;
2345
+ for (const resourceName of stylesheetsWithNamespaceRefs) {
2346
+ const message = registry.buildOmniScriptNamespaceWarning(resourceName);
2347
+ if (message) {
2348
+ warnings.push(message);
2349
+ pushed = true;
2350
+ }
2351
+ }
2352
+ return pushed;
2353
+ }
2354
+ /**
2355
+ * Detects elements that have a corrupted parent-child level hierarchy.
2356
+ * In a valid OmniScript, a child element should always have a higher Level__c
2357
+ * than its parent. When both parent and child are at the same level (typically level 0),
2358
+ * it indicates data corruption that will cause elements to be lost during migration.
2359
+ *
2360
+ * @param elements - Array of element records queried from the OmniScript
2361
+ * @returns Set of element names that have corrupted parent-child levels
2362
+ */
2363
+ detectCorruptedParentChildElements(elements) {
2364
+ const corruptedElements = new Set();
2365
+ // Build a map of element IDs to their levels
2366
+ const elementLevelMap = new Map();
2367
+ for (const elem of elements) {
2368
+ elementLevelMap.set(elem['Id'], elem[this.getElementFieldKey('Level__c')]);
2369
+ }
2370
+ // Check each element: if it has a parent and both are at the same level, it's corrupted
2371
+ for (const elem of elements) {
2372
+ const parentId = elem[this.getElementFieldKey('ParentElementId__c')];
2373
+ if (parentId && elementLevelMap.has(parentId)) {
2374
+ const childLevel = elem[this.getElementFieldKey('Level__c')];
2375
+ const parentLevel = elementLevelMap.get(parentId);
2376
+ if (childLevel != null && parentLevel != null && childLevel === parentLevel) {
2377
+ corruptedElements.add(elem['Name']);
2378
+ }
2379
+ }
2380
+ }
2381
+ return corruptedElements;
2382
+ }
2140
2383
  getElementFieldKey(fieldName) {
2141
2384
  return this.IS_STANDARD_DATA_MODEL ? Element_1.default[fieldName] : this.namespacePrefix + fieldName;
2142
2385
  }