cassproject 5.0.20 → 5.0.23

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 (107) hide show
  1. package/package.json +49 -52
  2. package/src/com/eduworks/ec/array/EcArray.js +1 -1
  3. package/src/com/eduworks/ec/array/EcAsyncHelper.js +1 -1
  4. package/src/com/eduworks/ec/array/EcObject.js +1 -1
  5. package/src/com/eduworks/ec/crypto/EcAes.js +1 -1
  6. package/src/com/eduworks/ec/crypto/EcAesCtrAsync.js +20 -22
  7. package/src/com/eduworks/ec/crypto/EcCrypto.js +8 -4
  8. package/src/com/eduworks/ec/crypto/EcPk.js +7 -7
  9. package/src/com/eduworks/ec/crypto/EcPpk.js +7 -7
  10. package/src/com/eduworks/ec/crypto/EcRsaOaepAsync.js +20 -19
  11. package/src/com/eduworks/ec/crypto/EcRsaOaepAsyncWorker.js +7 -6
  12. package/src/com/eduworks/ec/crypto/forgeAsync.js +1 -9
  13. package/src/com/eduworks/ec/crypto/forgeAsyncNode.js +8 -8
  14. package/src/com/eduworks/ec/graph/EcDirectedGraph.js +1 -2
  15. package/src/com/eduworks/ec/graph/EcFrameworkGraph.js +18 -24
  16. package/src/com/eduworks/ec/promises/helpers.js +1 -3
  17. package/src/com/eduworks/ec/remote/EcRemote.js +9 -9
  18. package/src/com/eduworks/ec/task/EcAsyncTask.js +5 -20
  19. package/src/com/eduworks/ec/task/EcAsyncTaskManager.js +3 -4
  20. package/src/com/eduworks/ec/task/EcAsyncTaskWorker.js +9 -11
  21. package/src/com/eduworks/schema/cfd/competency/CfdFramework.js +36 -37
  22. package/src/com/eduworks/schema/ebac/EbacContact.js +3 -9
  23. package/src/com/eduworks/schema/ebac/EbacContactGrant.js +3 -9
  24. package/src/com/eduworks/schema/ebac/EbacCredential.js +3 -9
  25. package/src/com/eduworks/schema/ebac/EbacCredentialCommit.js +3 -9
  26. package/src/com/eduworks/schema/ebac/EbacCredentialRequest.js +3 -9
  27. package/src/com/eduworks/schema/ebac/EbacCredentials.js +3 -9
  28. package/src/com/eduworks/schema/ebac/EbacEncryptedSecret.js +3 -9
  29. package/src/com/eduworks/schema/ebac/EbacEncryptedValue.js +5 -12
  30. package/src/com/eduworks/schema/ebac/EbacSignature.js +1 -6
  31. package/src/index/env.js +6 -3
  32. package/src/index/polyfills.js +13 -27
  33. package/src/org/angles/schema/angles/VideoStory.js +1 -1
  34. package/src/org/cass/competency/EcCompetency.js +1 -2
  35. package/src/org/cass/competency/EcDirectory.js +1 -2
  36. package/src/org/cass/competency/EcFramework.js +9 -12
  37. package/src/org/cass/exporter/CSVExport.js +12 -13
  38. package/src/org/cass/importer/ASNImport.js +3 -6
  39. package/src/org/cass/importer/CSVImport.js +9 -17
  40. package/src/org/cass/importer/CTDLASNCSVConceptImport.js +25 -121
  41. package/src/org/cass/importer/CTDLASNCSVImport.js +88 -133
  42. package/src/org/cass/importer/FrameworkImport.js +16 -30
  43. package/src/org/cass/importer/MedbiqImport.js +3 -4
  44. package/src/org/cass/importer/TabStructuredImport.js +6 -6
  45. package/src/org/cass/profile/EcAssertion.js +40 -51
  46. package/src/org/cassproject/ebac/identity/EcContactGrant.js +2 -2
  47. package/src/org/cassproject/ebac/identity/EcIdentityManager.js +56 -62
  48. package/src/org/cassproject/ebac/identity/EcRekeyRequest.js +3 -2
  49. package/src/org/cassproject/ebac/identity/remote/EcRemoteIdentityManager.js +51 -62
  50. package/src/org/cassproject/ebac/identity/remote/OAuth2FileBasedRemoteIdentityManager.js +56 -70
  51. package/src/org/cassproject/ebac/identity/remote/RemoteIdentityManagerInterface.js +3 -3
  52. package/src/org/cassproject/ebac/repository/EcEncryptedValue.js +51 -44
  53. package/src/org/cassproject/ebac/repository/EcRepository.js +228 -301
  54. package/src/org/cassproject/general/repository/GeneralFile.js +8 -9
  55. package/src/org/cassproject/schema/cass/competency/Competency.js +10 -11
  56. package/src/org/cassproject/schema/cass/competency/Framework.js +10 -11
  57. package/src/org/cassproject/schema/cass/competency/Level.js +10 -11
  58. package/src/org/cassproject/schema/cass/competency/Relation.js +10 -11
  59. package/src/org/cassproject/schema/cass/competency/RollupRule.js +7 -7
  60. package/src/org/cassproject/schema/cass/profile/Assertion.js +20 -27
  61. package/src/org/cassproject/schema/cass/profile/AssertionEnvelope.js +3 -5
  62. package/src/org/cassproject/schema/general/EcRemoteLinkedData.js +41 -44
  63. package/src/org/json/ld/EcLinkedData.js +10 -12
  64. package/src/org/schema/EcOrganization.js +10 -10
  65. package/src/org/schema/EcQuestion.js +8 -8
  66. package/src/org/w3/skos/Concept.js +6 -6
  67. package/src/org/w3/skos/ConceptScheme.js +6 -6
  68. package/src/org/w3/skos/EcConcept.js +1 -2
  69. package/src/org/w3/skos/EcConceptScheme.js +1 -2
  70. package/src/test/0.start.test.js +1 -1
  71. package/src/test/0.testLibraries.test.js +172 -0
  72. package/src/test/1.ASNImport.test.js +122 -0
  73. package/src/test/1.CSVExport.test.js +57 -0
  74. package/src/test/1.CSVImport.test.js +185 -0
  75. package/src/test/1.CTDLASNCSVConceptImport.test.js +348 -0
  76. package/src/test/1.CTDLASNCSVImport.import.test.js +497 -0
  77. package/src/test/1.Ebac.schema.test.js +107 -0
  78. package/src/test/1.EcAssertion.decrypt.test.js +98 -0
  79. package/src/test/1.EcAssertion.pinned.test.js +160 -0
  80. package/src/test/1.EcCrypto.formats.test.js +293 -0
  81. package/src/test/1.EcCrypto.generateUUID.test.js +47 -0
  82. package/src/test/1.EcEncryptedValue.helpers.test.js +236 -0
  83. package/src/test/1.EcHelpers.misc.test.js +259 -0
  84. package/src/test/1.EcIdentityManager.keys.test.js +439 -0
  85. package/src/test/1.EcOrganization.rekey.test.js +68 -0
  86. package/src/test/1.EcRemote.responses.test.js +155 -0
  87. package/src/test/1.EcRemoteIdentityManager.flows.test.js +326 -0
  88. package/src/test/1.EcRemoteLinkedData.pinned.test.js +473 -0
  89. package/src/test/1.EcRepository.l0.test.js +14 -5
  90. package/src/test/1.EcRepository.l1.test.js +16 -7
  91. package/src/test/1.EcRepository.l2.test.js +20 -11
  92. package/src/test/1.EcRepository.unit.test.js +961 -0
  93. package/src/test/1.EcRsaOaep.test.js +2 -12
  94. package/src/test/1.EcRsaOaepAsync.test.js +2 -12
  95. package/src/test/1.EcRsaOaepAsyncWorker.test.js +2 -12
  96. package/src/test/1.EcRsaOaepAsyncWorker.workers.test.js +147 -0
  97. package/src/test/1.FrameworkImport.test.js +170 -0
  98. package/src/test/1.MedbiqImport.test.js +92 -0
  99. package/src/test/1.OAuth2FileBasedRemoteIdentityManager.test.js +399 -0
  100. package/src/test/1.TabStructuredImport.test.js +61 -0
  101. package/src/test/1.polyfills.test.js +89 -0
  102. package/src/test/1.schema.competency.pinned.test.js +384 -0
  103. package/src/test/3.EcFrameworkGraph.test.js +3 -2
  104. package/src/test/4.schema.test.js +33 -28
  105. package/src/test/4.skyId.test.js +4 -2
  106. package/src/test/cypressValue.js +9 -0
  107. package/src/test/2.EcCompetency.test.js +0 -17
@@ -5,7 +5,7 @@ const jsonld = require("jsonld");
5
5
  let isNode = false;
6
6
  if (typeof process === 'object') {
7
7
  if (typeof process.versions === 'object') {
8
- if (typeof process.versions.node !== 'undefined') {
8
+ if (process.versions.node !== undefined) {
9
9
  isNode = true;
10
10
  }
11
11
  }
@@ -24,7 +24,7 @@ const customDocLoader = async (url, options) => {
24
24
  // call the default documentLoader
25
25
  let result = docLoader(url);
26
26
  result.then((x) => {
27
- if (x && x.document) {
27
+ if (x?.document) {
28
28
  jsonldCache[url] = x.document;
29
29
  }
30
30
  })
@@ -48,7 +48,7 @@ function setVersionIdentifier(f) {
48
48
  },
49
49
  "ceterms:identifierType": parts.length > 2 ? parts[2] : undefined
50
50
  };
51
- } catch (e) {
51
+ } catch (e) { // NOSONAR - A value that cannot be split (not a string) is dropped from the list.
52
52
  delete f["ceterms:versionIdentifier"][i];
53
53
  }
54
54
  }
@@ -153,11 +153,10 @@ module.exports = class CTDLASNCSVConceptImport {
153
153
  errors.push(hierarchyError);
154
154
  }
155
155
  }
156
- const terms = JSON.parse(JSON.stringify((await EcRemote.getExpectingObject("https://schema.cassproject.org/0.4/jsonld1.1/ceasn2cassConceptsTerms"))));
156
+ const terms = structuredClone(await EcRemote.getExpectingObject("https://schema.cassproject.org/0.4/jsonld1.1/ceasn2cassConceptsTerms"));
157
157
  let schemeArray = [];
158
158
  let concepts = [];
159
159
  let conceptCtids = {};
160
- let rowsWithErrors = new Set(); // Track rows that have errors
161
160
  for (let each = 0; each < tabularData.length; each++) {
162
161
  let pretranslatedE = tabularData[each];
163
162
  // Skip extra lines if found in file
@@ -180,7 +179,6 @@ module.exports = class CTDLASNCSVConceptImport {
180
179
  rowHasError = true;
181
180
  }
182
181
  if (rowHasError) {
183
- rowsWithErrors.add(each);
184
182
  continue;
185
183
  }
186
184
  if (
@@ -190,7 +188,7 @@ module.exports = class CTDLASNCSVConceptImport {
190
188
  }
191
189
  if (pretranslatedE["@type"] == "skos:ConceptScheme") {
192
190
  // Validate required properties (only if validation rules provided)
193
- if (validationRules && validationRules.requiredProps) {
191
+ if (validationRules?.requiredProps) {
194
192
  const validationErrors = CTDLASNCSVConceptImport.validateRequiredProperties(
195
193
  pretranslatedE,
196
194
  "skos:ConceptScheme",
@@ -203,12 +201,11 @@ module.exports = class CTDLASNCSVConceptImport {
203
201
  } else {
204
202
  errors.push(validationErrors);
205
203
  }
206
- rowsWithErrors.add(each);
207
204
  continue;
208
205
  }
209
206
  }
210
207
  // Validate registry URL fields (only if url rules provided)
211
- if (validationRules && validationRules.urlRules) {
208
+ if (validationRules?.urlRules) {
212
209
  const urlErrors = CTDLASNCSVImport.validateRegistryUrls(
213
210
  pretranslatedE,
214
211
  "skos:ConceptScheme",
@@ -217,7 +214,6 @@ module.exports = class CTDLASNCSVConceptImport {
217
214
  );
218
215
  if (urlErrors) {
219
216
  errors.push(...urlErrors);
220
- rowsWithErrors.add(each);
221
217
  continue;
222
218
  }
223
219
  }
@@ -280,10 +276,7 @@ module.exports = class CTDLASNCSVConceptImport {
280
276
  "schema:dateModified"
281
277
  ] = new Date().toISOString();
282
278
  if (
283
- EcConceptScheme.template != null &&
284
- EcConceptScheme.template[
285
- "schema:dateCreated"
286
- ] != null
279
+ EcConceptScheme.template?.["schema:dateCreated"] != null
287
280
  ) {
288
281
  CTDLASNCSVImport.setDateCreated(e, f);
289
282
  }
@@ -291,7 +284,7 @@ module.exports = class CTDLASNCSVConceptImport {
291
284
  schemeArray.push(f);
292
285
  } else if (pretranslatedE["@type"] == "skos:Concept") {
293
286
  // Validate required properties (only if validation rules provided)
294
- if (validationRules && validationRules.requiredProps) {
287
+ if (validationRules?.requiredProps) {
295
288
  const validationErrors = CTDLASNCSVConceptImport.validateRequiredProperties(
296
289
  pretranslatedE,
297
290
  "skos:Concept",
@@ -304,12 +297,11 @@ module.exports = class CTDLASNCSVConceptImport {
304
297
  } else {
305
298
  errors.push(validationErrors);
306
299
  }
307
- rowsWithErrors.add(each);
308
300
  continue;
309
301
  }
310
302
  }
311
303
  // Validate registry URL fields (only if url rules provided)
312
- if (validationRules && validationRules.urlRules) {
304
+ if (validationRules?.urlRules) {
313
305
  const urlErrors = CTDLASNCSVImport.validateRegistryUrls(
314
306
  pretranslatedE,
315
307
  "skos:Concept",
@@ -318,7 +310,6 @@ module.exports = class CTDLASNCSVConceptImport {
318
310
  );
319
311
  if (urlErrors) {
320
312
  errors.push(...urlErrors);
321
- rowsWithErrors.add(each);
322
313
  continue;
323
314
  }
324
315
  }
@@ -351,12 +342,10 @@ module.exports = class CTDLASNCSVConceptImport {
351
342
  f.copyFrom(e);
352
343
  if (e["id"] == null) {
353
344
  errors.push(`Row ${each + 2}: Concept is missing an id`);
354
- rowsWithErrors.add(each);
355
345
  continue;
356
346
  }
357
347
  if (
358
- EcConcept.template != null &&
359
- EcConcept.template["@owner"] != null
348
+ EcConcept.template?.["@owner"] != null
360
349
  ) {
361
350
  f["owner"] =
362
351
  EcConcept.template["@owner"];
@@ -372,10 +361,7 @@ module.exports = class CTDLASNCSVConceptImport {
372
361
  }
373
362
  if (ceo != null) f.addOwner(ceo.ppk.toPk());
374
363
  if (
375
- EcConcept.template != null &&
376
- EcConcept.template[
377
- "schema:dateCreated"
378
- ] != null
364
+ EcConcept.template?.["schema:dateCreated"] != null
379
365
  ) {
380
366
  CTDLASNCSVImport.setDateCreated(e, f);
381
367
  }
@@ -430,12 +416,7 @@ module.exports = class CTDLASNCSVConceptImport {
430
416
  }
431
417
  if (e["skos:topConceptOf"] != null) {
432
418
  let scheme = e["skos:topConceptOf"];
433
- for (
434
- let i = 0;
435
- i < schemeArray.length;
436
- i++
437
- ) {
438
- let schemeObj = schemeArray[i];
419
+ for (let schemeObj of schemeArray) {
439
420
  if (scheme == schemeObj["id"]) {
440
421
  if (
441
422
  schemeObj[
@@ -460,12 +441,7 @@ module.exports = class CTDLASNCSVConceptImport {
460
441
  if (e["skos:inScheme"] != null) {
461
442
  let scheme = e["skos:inScheme"];
462
443
  console.log('scheme', scheme);
463
- for (
464
- let i = 0;
465
- i < schemeArray.length;
466
- i++
467
- ) {
468
- let schemeObj = schemeArray[i];
444
+ for (let schemeObj of schemeArray) {
469
445
  if (scheme == schemeObj["id"]) {
470
446
  if (
471
447
  schemeObj[
@@ -497,13 +473,11 @@ module.exports = class CTDLASNCSVConceptImport {
497
473
  pretranslatedE["@type"] == ""
498
474
  ) {
499
475
  errors.push(`Row ${each + 2}: Missing or empty @type`);
500
- rowsWithErrors.add(each);
501
476
  continue;
502
477
  } else {
503
478
  errors.push(
504
479
  `Row ${each + 2}: Found unknown type: ` + pretranslatedE["@type"]
505
480
  );
506
- rowsWithErrors.add(each);
507
481
  continue;
508
482
  }
509
483
  }
@@ -561,11 +535,10 @@ module.exports = class CTDLASNCSVConceptImport {
561
535
  errors.push(hierarchyError);
562
536
  }
563
537
  }
564
- const terms = JSON.parse(JSON.stringify((await EcRemote.getExpectingObject("https://schema.cassproject.org/0.4/jsonld1.1/ceasn2cassConceptsTerms"))));
538
+ const terms = structuredClone(await EcRemote.getExpectingObject("https://schema.cassproject.org/0.4/jsonld1.1/ceasn2cassConceptsTerms"));
565
539
  let schemeArray = [];
566
540
  let concepts = [];
567
541
  let conceptCtids = {};
568
- let rowsWithErrors = new Set(); // Track rows that have errors
569
542
  for (let each = 0; each < tabularData.length; each++) {
570
543
  let pretranslatedE = tabularData[each];
571
544
  // Skip extra lines if found in file
@@ -584,7 +557,6 @@ module.exports = class CTDLASNCSVConceptImport {
584
557
  rowHasError = true;
585
558
  }
586
559
  if (rowHasError) {
587
- rowsWithErrors.add(each);
588
560
  continue;
589
561
  }
590
562
  if (
@@ -594,7 +566,7 @@ module.exports = class CTDLASNCSVConceptImport {
594
566
  }
595
567
  if (pretranslatedE["@type"] == "asn:ProgressionModel") {
596
568
  // Validate required properties (only if validation rules provided)
597
- if (validationRules && validationRules.requiredProps) {
569
+ if (validationRules?.requiredProps) {
598
570
  const validationErrors = CTDLASNCSVConceptImport.validateRequiredProperties(
599
571
  pretranslatedE,
600
572
  "asn:ProgressionModel",
@@ -607,12 +579,11 @@ module.exports = class CTDLASNCSVConceptImport {
607
579
  } else {
608
580
  errors.push(validationErrors);
609
581
  }
610
- rowsWithErrors.add(each);
611
582
  continue;
612
583
  }
613
584
  }
614
585
  // Validate registry URL fields (only if url rules provided)
615
- if (validationRules && validationRules.urlRules) {
586
+ if (validationRules?.urlRules) {
616
587
  const urlErrors = CTDLASNCSVImport.validateRegistryUrls(
617
588
  pretranslatedE,
618
589
  "asn:ProgressionModel",
@@ -621,7 +592,6 @@ module.exports = class CTDLASNCSVConceptImport {
621
592
  );
622
593
  if (urlErrors) {
623
594
  errors.push(...urlErrors);
624
- rowsWithErrors.add(each);
625
595
  continue;
626
596
  }
627
597
  }
@@ -680,10 +650,7 @@ module.exports = class CTDLASNCSVConceptImport {
680
650
  "schema:dateModified"
681
651
  ] = new Date().toISOString();
682
652
  if (
683
- EcConceptScheme.template != null &&
684
- EcConceptScheme.template[
685
- "schema:dateCreated"
686
- ] != null
653
+ EcConceptScheme.template?.["schema:dateCreated"] != null
687
654
  ) {
688
655
  CTDLASNCSVImport.setDateCreated(e, f);
689
656
  }
@@ -691,7 +658,7 @@ module.exports = class CTDLASNCSVConceptImport {
691
658
  schemeArray.push(f);
692
659
  } else if (pretranslatedE["@type"] == "asn:ProgressionLevel") {
693
660
  // Validate required properties (only if validation rules provided)
694
- if (validationRules && validationRules.requiredProps) {
661
+ if (validationRules?.requiredProps) {
695
662
  const validationErrors = CTDLASNCSVConceptImport.validateRequiredProperties(
696
663
  pretranslatedE,
697
664
  "asn:ProgressionLevel",
@@ -704,12 +671,11 @@ module.exports = class CTDLASNCSVConceptImport {
704
671
  } else {
705
672
  errors.push(validationErrors);
706
673
  }
707
- rowsWithErrors.add(each);
708
674
  continue;
709
675
  }
710
676
  }
711
677
  // Validate registry URL fields (only if url rules provided)
712
- if (validationRules && validationRules.urlRules) {
678
+ if (validationRules?.urlRules) {
713
679
  const urlErrors = CTDLASNCSVImport.validateRegistryUrls(
714
680
  pretranslatedE,
715
681
  "asn:ProgressionLevel",
@@ -718,7 +684,6 @@ module.exports = class CTDLASNCSVConceptImport {
718
684
  );
719
685
  if (urlErrors) {
720
686
  errors.push(...urlErrors);
721
- rowsWithErrors.add(each);
722
687
  continue;
723
688
  }
724
689
  }
@@ -751,12 +716,10 @@ module.exports = class CTDLASNCSVConceptImport {
751
716
  f.copyFrom(e);
752
717
  if (e["id"] == null) {
753
718
  errors.push(`Row ${each + 2}: Concept is missing an id`);
754
- rowsWithErrors.add(each);
755
719
  continue;
756
720
  }
757
721
  if (
758
- EcConcept.template != null &&
759
- EcConcept.template["@owner"] != null
722
+ EcConcept.template?.["@owner"] != null
760
723
  ) {
761
724
  f["owner"] =
762
725
  EcConcept.template["@owner"];
@@ -772,10 +735,7 @@ module.exports = class CTDLASNCSVConceptImport {
772
735
  }
773
736
  if (ceo != null) f.addOwner(ceo.ppk.toPk());
774
737
  if (
775
- EcConcept.template != null &&
776
- EcConcept.template[
777
- "schema:dateCreated"
778
- ] != null
738
+ EcConcept.template?.["schema:dateCreated"] != null
779
739
  ) {
780
740
  CTDLASNCSVImport.setDateCreated(e, f);
781
741
  }
@@ -795,12 +755,7 @@ module.exports = class CTDLASNCSVConceptImport {
795
755
  }
796
756
  if (e["skos:topConceptOf"] != null) {
797
757
  let scheme = e["skos:topConceptOf"];
798
- for (
799
- let i = 0;
800
- i < schemeArray.length;
801
- i++
802
- ) {
803
- let schemeObj = schemeArray[i];
758
+ for (let schemeObj of schemeArray) {
804
759
  if (scheme == schemeObj["id"]) {
805
760
  if (
806
761
  schemeObj[
@@ -824,12 +779,7 @@ module.exports = class CTDLASNCSVConceptImport {
824
779
  }
825
780
  if (e["skos:inScheme"] != null) {
826
781
  let scheme = e["skos:inScheme"];
827
- for (
828
- let i = 0;
829
- i < schemeArray.length;
830
- i++
831
- ) {
832
- let schemeObj = schemeArray[i];
782
+ for (let schemeObj of schemeArray) {
833
783
  if (scheme == schemeObj["id"]) {
834
784
  if (
835
785
  schemeObj[
@@ -862,13 +812,11 @@ module.exports = class CTDLASNCSVConceptImport {
862
812
  pretranslatedE["@type"] == ""
863
813
  ) {
864
814
  errors.push(`Row ${each + 2}: Missing or empty @type`);
865
- rowsWithErrors.add(each);
866
815
  continue;
867
816
  } else {
868
817
  errors.push(
869
818
  `Row ${each + 2}: Found unknown type: ` + pretranslatedE["@type"]
870
819
  );
871
- rowsWithErrors.add(each);
872
820
  continue;
873
821
  }
874
822
  }
@@ -913,51 +861,7 @@ module.exports = class CTDLASNCSVConceptImport {
913
861
  }
914
862
 
915
863
  static validateConceptHierarchy(tabularData, hierarchyRules) {
916
- // Use provided hierarchy rules or fall back to defaults
917
- if (!hierarchyRules) {
918
- return 'Hierarchy rules not defined.'
919
- }
920
-
921
- // Check each type defined in hierarchy rules
922
- for (const [type, rules] of Object.entries(hierarchyRules)) {
923
- const hasType = tabularData.some(r => r && r["@type"] === type);
924
- if (!hasType) continue;
925
-
926
- let hasRequiredProp = false;
927
- let hasChildProp = false;
928
-
929
- for (const row of tabularData) {
930
- if (!row || !row["@type"]) continue;
931
-
932
- // Check if this row has any of the required properties
933
- if (row["@type"] === type && rules.requiredProperties) {
934
- for (const prop of rules.requiredProperties) {
935
- if (row[prop]) {
936
- hasRequiredProp = true;
937
- break;
938
- }
939
- }
940
- }
941
-
942
- // Check if any row has child properties
943
- if (rules.childProperties) {
944
- for (const prop of rules.childProperties) {
945
- if (row[prop]) {
946
- hasChildProp = true;
947
- break;
948
- }
949
- }
950
- }
951
-
952
- if (hasRequiredProp && hasChildProp) break;
953
- }
954
-
955
- // Validate that at least one of the hierarchy properties exists
956
- if (!hasRequiredProp && !hasChildProp) {
957
- return rules.errorMessage || `CSV must contain hierarchy properties for ${type}`;
958
- }
959
- }
960
-
961
- return null;
864
+ // Same rules as for frameworks.
865
+ return CTDLASNCSVImport.validateFrameworkHierarchy(tabularData, hierarchyRules);
962
866
  }
963
867
  };