@cyberismo/data-handler 0.0.15 → 0.0.17

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 (191) hide show
  1. package/dist/card-metadata-updater.js +7 -1
  2. package/dist/card-metadata-updater.js.map +1 -1
  3. package/dist/command-handler.d.ts +4 -0
  4. package/dist/command-handler.js +22 -8
  5. package/dist/command-handler.js.map +1 -1
  6. package/dist/command-manager.d.ts +24 -1
  7. package/dist/command-manager.js +31 -7
  8. package/dist/command-manager.js.map +1 -1
  9. package/dist/commands/create.d.ts +1 -1
  10. package/dist/commands/create.js +34 -36
  11. package/dist/commands/create.js.map +1 -1
  12. package/dist/commands/export.d.ts +11 -2
  13. package/dist/commands/export.js +54 -41
  14. package/dist/commands/export.js.map +1 -1
  15. package/dist/commands/fetch.d.ts +8 -0
  16. package/dist/commands/fetch.js +101 -23
  17. package/dist/commands/fetch.js.map +1 -1
  18. package/dist/commands/import.d.ts +14 -3
  19. package/dist/commands/import.js +27 -10
  20. package/dist/commands/import.js.map +1 -1
  21. package/dist/commands/move.js +0 -1
  22. package/dist/commands/move.js.map +1 -1
  23. package/dist/commands/remove.d.ts +11 -2
  24. package/dist/commands/remove.js +15 -5
  25. package/dist/commands/remove.js.map +1 -1
  26. package/dist/commands/rename.d.ts +4 -9
  27. package/dist/commands/rename.js +37 -101
  28. package/dist/commands/rename.js.map +1 -1
  29. package/dist/commands/show.d.ts +20 -12
  30. package/dist/commands/show.js +79 -57
  31. package/dist/commands/show.js.map +1 -1
  32. package/dist/commands/transition.d.ts +9 -2
  33. package/dist/commands/transition.js +25 -17
  34. package/dist/commands/transition.js.map +1 -1
  35. package/dist/commands/update.d.ts +16 -12
  36. package/dist/commands/update.js +19 -17
  37. package/dist/commands/update.js.map +1 -1
  38. package/dist/commands/validate.d.ts +17 -9
  39. package/dist/commands/validate.js +94 -35
  40. package/dist/commands/validate.js.map +1 -1
  41. package/dist/containers/card-container.d.ts +7 -5
  42. package/dist/containers/card-container.js +30 -5
  43. package/dist/containers/card-container.js.map +1 -1
  44. package/dist/containers/project/calculation-engine.d.ts +7 -4
  45. package/dist/containers/project/calculation-engine.js +61 -66
  46. package/dist/containers/project/calculation-engine.js.map +1 -1
  47. package/dist/containers/project/project-paths.d.ts +7 -4
  48. package/dist/containers/project/project-paths.js +22 -12
  49. package/dist/containers/project/project-paths.js.map +1 -1
  50. package/dist/containers/project/resource-cache.d.ts +169 -0
  51. package/dist/containers/project/resource-cache.js +509 -0
  52. package/dist/containers/project/resource-cache.js.map +1 -0
  53. package/dist/containers/project/resource-handler.d.ts +129 -0
  54. package/dist/containers/project/resource-handler.js +206 -0
  55. package/dist/containers/project/resource-handler.js.map +1 -0
  56. package/dist/containers/project.d.ts +46 -152
  57. package/dist/containers/project.js +179 -409
  58. package/dist/containers/project.js.map +1 -1
  59. package/dist/containers/template.d.ts +8 -2
  60. package/dist/containers/template.js +24 -19
  61. package/dist/containers/template.js.map +1 -1
  62. package/dist/interfaces/command-options.d.ts +3 -1
  63. package/dist/interfaces/folder-content-interfaces.d.ts +5 -3
  64. package/dist/interfaces/folder-content-interfaces.js +3 -3
  65. package/dist/interfaces/folder-content-interfaces.js.map +1 -1
  66. package/dist/interfaces/project-interfaces.d.ts +7 -9
  67. package/dist/interfaces/project-interfaces.js.map +1 -1
  68. package/dist/interfaces/resource-interfaces.d.ts +14 -1
  69. package/dist/interfaces/resource-interfaces.js.map +1 -1
  70. package/dist/macros/graph/index.js +12 -26
  71. package/dist/macros/graph/index.js.map +1 -1
  72. package/dist/macros/index.d.ts +1 -1
  73. package/dist/macros/index.js +2 -2
  74. package/dist/macros/index.js.map +1 -1
  75. package/dist/macros/report/index.js +3 -6
  76. package/dist/macros/report/index.js.map +1 -1
  77. package/dist/module-manager.d.ts +16 -3
  78. package/dist/module-manager.js +51 -19
  79. package/dist/module-manager.js.map +1 -1
  80. package/dist/project-settings.d.ts +21 -3
  81. package/dist/project-settings.js +91 -14
  82. package/dist/project-settings.js.map +1 -1
  83. package/dist/resources/calculation-resource.d.ts +4 -3
  84. package/dist/resources/calculation-resource.js +11 -5
  85. package/dist/resources/calculation-resource.js.map +1 -1
  86. package/dist/resources/card-type-resource.d.ts +6 -1
  87. package/dist/resources/card-type-resource.js +34 -23
  88. package/dist/resources/card-type-resource.js.map +1 -1
  89. package/dist/resources/create-defaults.d.ts +3 -2
  90. package/dist/resources/create-defaults.js +3 -2
  91. package/dist/resources/create-defaults.js.map +1 -1
  92. package/dist/resources/field-type-resource.d.ts +4 -1
  93. package/dist/resources/field-type-resource.js +22 -23
  94. package/dist/resources/field-type-resource.js.map +1 -1
  95. package/dist/resources/file-resource.d.ts +5 -9
  96. package/dist/resources/file-resource.js +6 -11
  97. package/dist/resources/file-resource.js.map +1 -1
  98. package/dist/resources/folder-resource.d.ts +29 -32
  99. package/dist/resources/folder-resource.js +59 -78
  100. package/dist/resources/folder-resource.js.map +1 -1
  101. package/dist/resources/graph-model-resource.d.ts +4 -1
  102. package/dist/resources/graph-model-resource.js +11 -4
  103. package/dist/resources/graph-model-resource.js.map +1 -1
  104. package/dist/resources/graph-view-resource.d.ts +5 -2
  105. package/dist/resources/graph-view-resource.js +7 -3
  106. package/dist/resources/graph-view-resource.js.map +1 -1
  107. package/dist/resources/link-type-resource.d.ts +5 -2
  108. package/dist/resources/link-type-resource.js +5 -2
  109. package/dist/resources/link-type-resource.js.map +1 -1
  110. package/dist/resources/report-resource.d.ts +6 -7
  111. package/dist/resources/report-resource.js +14 -23
  112. package/dist/resources/report-resource.js.map +1 -1
  113. package/dist/resources/resource-object.d.ts +94 -8
  114. package/dist/resources/resource-object.js +212 -109
  115. package/dist/resources/resource-object.js.map +1 -1
  116. package/dist/resources/template-resource.d.ts +7 -3
  117. package/dist/resources/template-resource.js +10 -3
  118. package/dist/resources/template-resource.js.map +1 -1
  119. package/dist/resources/workflow-resource.d.ts +5 -2
  120. package/dist/resources/workflow-resource.js +18 -22
  121. package/dist/resources/workflow-resource.js.map +1 -1
  122. package/dist/utils/card-utils.d.ts +2 -2
  123. package/dist/utils/card-utils.js +1 -1
  124. package/dist/utils/clingo-fact-builder.d.ts +25 -14
  125. package/dist/utils/clingo-fact-builder.js +27 -5
  126. package/dist/utils/clingo-fact-builder.js.map +1 -1
  127. package/dist/utils/clingo-facts.js +3 -4
  128. package/dist/utils/clingo-facts.js.map +1 -1
  129. package/dist/utils/configuration-logger.d.ts +91 -0
  130. package/dist/utils/configuration-logger.js +151 -0
  131. package/dist/utils/configuration-logger.js.map +1 -0
  132. package/dist/utils/constants.d.ts +1 -1
  133. package/dist/utils/constants.js +5 -3
  134. package/dist/utils/constants.js.map +1 -1
  135. package/dist/utils/resource-utils.d.ts +1 -0
  136. package/dist/utils/resource-utils.js +2 -1
  137. package/dist/utils/resource-utils.js.map +1 -1
  138. package/package.json +9 -9
  139. package/src/card-metadata-updater.ts +6 -2
  140. package/src/command-handler.ts +39 -12
  141. package/src/command-manager.ts +33 -21
  142. package/src/commands/create.ts +43 -78
  143. package/src/commands/export.ts +63 -52
  144. package/src/commands/fetch.ts +143 -34
  145. package/src/commands/import.ts +37 -15
  146. package/src/commands/move.ts +0 -1
  147. package/src/commands/remove.ts +20 -7
  148. package/src/commands/rename.ts +58 -149
  149. package/src/commands/show.ts +123 -80
  150. package/src/commands/transition.ts +26 -28
  151. package/src/commands/update.ts +25 -22
  152. package/src/commands/validate.ts +104 -67
  153. package/src/containers/card-container.ts +37 -5
  154. package/src/containers/project/calculation-engine.ts +61 -93
  155. package/src/containers/project/project-paths.ts +29 -13
  156. package/src/containers/project/resource-cache.ts +651 -0
  157. package/src/containers/project/resource-handler.ts +265 -0
  158. package/src/containers/project.ts +250 -527
  159. package/src/containers/template.ts +28 -23
  160. package/src/interfaces/command-options.ts +3 -1
  161. package/src/interfaces/folder-content-interfaces.ts +7 -6
  162. package/src/interfaces/project-interfaces.ts +12 -11
  163. package/src/interfaces/resource-interfaces.ts +18 -3
  164. package/src/macros/graph/index.ts +26 -47
  165. package/src/macros/index.ts +2 -2
  166. package/src/macros/report/index.ts +3 -9
  167. package/src/module-manager.ts +74 -17
  168. package/src/project-settings.ts +96 -14
  169. package/src/resources/calculation-resource.ts +18 -18
  170. package/src/resources/card-type-resource.ts +50 -50
  171. package/src/resources/create-defaults.ts +3 -2
  172. package/src/resources/field-type-resource.ts +41 -55
  173. package/src/resources/file-resource.ts +10 -36
  174. package/src/resources/folder-resource.ts +69 -120
  175. package/src/resources/graph-model-resource.ts +20 -22
  176. package/src/resources/graph-view-resource.ts +15 -17
  177. package/src/resources/link-type-resource.ts +10 -13
  178. package/src/resources/report-resource.ts +21 -43
  179. package/src/resources/resource-object.ts +263 -149
  180. package/src/resources/template-resource.ts +17 -16
  181. package/src/resources/workflow-resource.ts +25 -44
  182. package/src/utils/card-utils.ts +2 -2
  183. package/src/utils/clingo-fact-builder.ts +28 -16
  184. package/src/utils/clingo-facts.ts +3 -4
  185. package/src/utils/configuration-logger.ts +206 -0
  186. package/src/utils/constants.ts +5 -3
  187. package/src/utils/resource-utils.ts +2 -1
  188. package/dist/containers/project/resource-collector.d.ts +0 -110
  189. package/dist/containers/project/resource-collector.js +0 -344
  190. package/dist/containers/project/resource-collector.js.map +0 -1
  191. package/src/containers/project/resource-collector.ts +0 -404
@@ -12,7 +12,7 @@
12
12
  */
13
13
 
14
14
  // node
15
- import { type Dirent } from 'node:fs';
15
+ import type { Dirent } from 'node:fs';
16
16
  import { basename, dirname, extname, join, parse, resolve } from 'node:path';
17
17
  import { readdir } from 'node:fs/promises';
18
18
 
@@ -33,13 +33,14 @@ import type {
33
33
  CustomField,
34
34
  FieldType,
35
35
  ReportMetadata,
36
- ResourceContent,
36
+ AnyResourceContent,
37
37
  Workflow,
38
38
  } from '../interfaces/resource-interfaces.js';
39
39
  import { errorFunction } from '../utils/error-utils.js';
40
40
  import { isTemplateCard } from '../utils/card-utils.js';
41
+ import { isPredefinedField } from '../utils/constants.js';
41
42
  import { pathExists } from '../utils/file-utils.js';
42
- import { Project } from '../containers/project.js';
43
+ import type { Project } from '../containers/project.js';
43
44
  import { readJsonFile } from '../utils/json.js';
44
45
  import { type ResourceName, resourceName } from '../utils/resource-utils.js';
45
46
 
@@ -51,7 +52,7 @@ const SHORT_TEXT_MAX_LENGTH = 80;
51
52
 
52
53
  import * as EmailValidator from 'email-validator';
53
54
  import { evaluateMacros } from '../macros/index.js';
54
- const baseDir = import.meta.dirname;
55
+ const baseDir = import.meta.dirname ?? new URL('.', import.meta.url).pathname;
55
56
  const subFoldersToValidate = ['.cards', 'cardRoot'];
56
57
 
57
58
  export interface LengthProvider {
@@ -82,6 +83,9 @@ export class Validate {
82
83
  static dotSchemaSchemaId = '/dotSchema';
83
84
  static parameterSchemaFile = 'parameterSchema.json';
84
85
 
86
+ /**
87
+ * Creates an instance of Validate command.
88
+ */
85
89
  constructor() {
86
90
  Validate.baseFolder = pathExists(
87
91
  join(process.cwd(), '../../schema', 'cardTreeDirectorySchema.json'),
@@ -117,7 +121,7 @@ export class Validate {
117
121
  private checkResourceName(
118
122
  file: Dirent,
119
123
  content:
120
- | ResourceContent
124
+ | AnyResourceContent
121
125
  | CustomField
122
126
  | DotSchemaContent
123
127
  | ProjectSettings
@@ -133,7 +137,7 @@ export class Validate {
133
137
  file.name !== Validate.dotSchemaSchemaId &&
134
138
  file.name !== Validate.parameterSchemaFile
135
139
  ) {
136
- const namedContent = content as ResourceContent | ReportMetadata;
140
+ const namedContent = content as AnyResourceContent | ReportMetadata;
137
141
  if (!namedContent.name) {
138
142
  errors.push(
139
143
  `File '${file.name}' does not contain 'name' property. Cannot validate resource's 'name'.`,
@@ -167,20 +171,7 @@ export class Validate {
167
171
  return join(file.parentPath, file.name);
168
172
  }
169
173
 
170
- // Puts resource to a local cache if found and returns the resource.
171
- // If value is already cached, returns from cache.
172
- private getAndCacheResource<Type>(
173
- project: Project,
174
- cachedValues: Map<string, Type>,
175
- valueName: string,
176
- ): Type | undefined {
177
- const resource = project.resource<Type>(valueName);
178
- if (resource) {
179
- cachedValues.set(valueName, resource);
180
- }
181
- return resource;
182
- }
183
-
174
+ // Parses validator messages
184
175
  private parseValidatorMessage(errorObject: object[]): string {
185
176
  let parsedErrorMessage = '';
186
177
  // todo: get schema name here?
@@ -231,7 +222,7 @@ export class Validate {
231
222
  ): Promise<string[]> {
232
223
  const message: string[] = [];
233
224
  try {
234
- const prefixes = await project.projectPrefixes();
225
+ const prefixes = project.allModulePrefixes();
235
226
  const files = await readdir(path, {
236
227
  withFileTypes: true,
237
228
  });
@@ -390,11 +381,13 @@ export class Validate {
390
381
  const errors: string[] = [];
391
382
  if (cardType && fieldArray) {
392
383
  const validationPromises = fieldArray.map(async (field) => {
393
- const fieldType = this.getAndCacheResource(
394
- project,
395
- this.validatedFieldTypes,
396
- field,
397
- );
384
+ let fieldType;
385
+ try {
386
+ fieldType = project.resources.byType(field, 'fieldTypes').show();
387
+ } catch {
388
+ fieldType = undefined;
389
+ }
390
+
398
391
  if (!fieldType) {
399
392
  return `Card type '${cardType.name}' has invalid reference to unknown ${nameOfArray} '${field}'`;
400
393
  }
@@ -471,7 +464,7 @@ export class Validate {
471
464
 
472
465
  /**
473
466
  * Validates that new identifier of a resource is according to naming convention.
474
- * @param identifier: resource identifier
467
+ * @param identifier Resource identifier
475
468
  * returns true if identifier is valid, and false otherwise.
476
469
  */
477
470
  public static isValidIdentifierName(identifier: string): boolean {
@@ -561,6 +554,7 @@ export class Validate {
561
554
  cards.push(...project.allTemplateCards());
562
555
 
563
556
  const cardIds = new Map<string, number>();
557
+ const allPrefixes = project.allModulePrefixes();
564
558
 
565
559
  for (const card of cards) {
566
560
  if (cardIds.has(card.key)) {
@@ -571,16 +565,19 @@ export class Validate {
571
565
 
572
566
  if (card.metadata) {
573
567
  if (!isTemplateCard(card)) {
574
- const validWorkflow = this.validateWorkflowState(project, card);
568
+ const validWorkflow = await this.validateWorkflowState(
569
+ project,
570
+ card,
571
+ );
575
572
  if (validWorkflow.length !== 0) {
576
573
  errorMsg.push(validWorkflow);
577
574
  }
578
575
  }
579
576
  }
580
-
581
577
  const validCustomFields = await this.validateCustomFields(
582
578
  project,
583
579
  card,
580
+ allPrefixes,
584
581
  );
585
582
  if (validCustomFields.length !== 0) {
586
583
  errorMsg.push(validCustomFields);
@@ -663,6 +660,9 @@ export class Validate {
663
660
  * @param resourceType Type of resource
664
661
  * @param name Name of resource
665
662
  * @param prefixes currently used project prefixes
663
+ * @throws when resource is not from given prefixes,
664
+ * or when actual resource does not match the type,
665
+ * or when identifier name is not valid
666
666
  * @returns resource name as valid resource name; throws in error cases.
667
667
  */
668
668
  public validResourceName(
@@ -709,11 +709,13 @@ export class Validate {
709
709
  * Validates that card's custom fields are according to schema and have correct data in them.
710
710
  * @param project currently used Project
711
711
  * @param card specific card
712
- * @returns string containing all validation errors
712
+ * @throws when card does not have metadata
713
+ * @returns validation errors, if any.
713
714
  */
714
715
  public async validateCustomFields(
715
716
  project: Project,
716
717
  card: Card,
718
+ prefixes: string[],
717
719
  ): Promise<string> {
718
720
  const validationErrors: string[] = [];
719
721
 
@@ -723,11 +725,14 @@ export class Validate {
723
725
  );
724
726
  }
725
727
 
726
- const cardType = this.getAndCacheResource(
727
- project,
728
- this.validatedCardTypes,
729
- card.metadata?.cardType,
730
- );
728
+ let cardType;
729
+ try {
730
+ cardType = project.resources
731
+ .byType(card.metadata?.cardType, 'cardTypes')
732
+ .show();
733
+ } catch {
734
+ cardType = undefined;
735
+ }
731
736
 
732
737
  if (!cardType) {
733
738
  validationErrors.push(
@@ -753,11 +758,19 @@ export class Validate {
753
758
  validationErrors.push(...fieldErrors);
754
759
 
755
760
  for (const field of cardType.customFields) {
756
- const found = await project.resourceExists('fieldTypes', field.name);
757
- if (!found) {
761
+ let fieldType;
762
+ try {
763
+ fieldType = await project.resources
764
+ .byType(field.name, 'fieldTypes')
765
+ .show();
766
+ } catch {
767
+ fieldType = undefined;
768
+ }
769
+ if (!fieldType) {
758
770
  validationErrors.push(
759
- `Custom field '${field.name}' from card type '${cardType.name}' not found from project`,
771
+ `In card '${card.key}' field '${field.name}' is missing from project\n`,
760
772
  );
773
+ continue;
761
774
  }
762
775
  if (field.isCalculated) {
763
776
  if (card.metadata[field.name] !== undefined) {
@@ -775,19 +788,6 @@ export class Validate {
775
788
  }
776
789
  }
777
790
 
778
- const fieldType = await this.getAndCacheResource(
779
- project,
780
- this.validatedFieldTypes,
781
- field.name,
782
- );
783
-
784
- if (!fieldType) {
785
- validationErrors.push(
786
- `In card '${card.key}' field '${field.name}' is missing from project\n`,
787
- );
788
- continue;
789
- }
790
-
791
791
  if (!this.validType(card.metadata[field.name], fieldType)) {
792
792
  const typeOfValue = typeof card.metadata[field.name];
793
793
  let fieldValue = card.metadata[field.name];
@@ -817,12 +817,40 @@ export class Validate {
817
817
  }
818
818
  }
819
819
 
820
+ // Validate that all metadata keys are either predefined fields or valid field type names
821
+ for (const key of Object.keys(card.metadata)) {
822
+ if (isPredefinedField(key) as boolean) {
823
+ continue;
824
+ }
825
+ try {
826
+ this.validResourceName('fieldTypes', key, prefixes);
827
+ } catch {
828
+ validationErrors.push(
829
+ `Card '${card.key}' has invalid metadata key '${key}'`,
830
+ );
831
+ continue;
832
+ }
833
+ // Check that the card's fieldType exists in the project
834
+ let fieldType;
835
+ try {
836
+ fieldType = await project.resources.byType(key, 'fieldTypes').show();
837
+ } catch {
838
+ fieldType = undefined;
839
+ }
840
+ if (!fieldType) {
841
+ validationErrors.push(
842
+ `Card '${card.key}' has field '${key}' that does not exist in the project`,
843
+ );
844
+ }
845
+ }
846
+
820
847
  return validationErrors.join('\n');
821
848
  }
822
849
 
823
850
  /**
824
851
  * Validates the labels of a card
825
852
  * @param card card to validate. Card must have metadata.
853
+ * @returns validation errors, if any.
826
854
  */
827
855
  public validateCardLabels(card: Card): string {
828
856
  const validationErrors: string[] = [];
@@ -856,9 +884,12 @@ export class Validate {
856
884
  * Template cards are expected to have empty workflow state.
857
885
  * @param project Project object.
858
886
  * @param card Card object to validate
859
- * @returns string containing all validation errors
887
+ * @returns validation errors, if any.
860
888
  */
861
- public validateWorkflowState(project: Project, card: Card): string {
889
+ public async validateWorkflowState(
890
+ project: Project,
891
+ card: Card,
892
+ ): Promise<string> {
862
893
  const validationErrors: string[] = [];
863
894
 
864
895
  if (!card.metadata) {
@@ -867,12 +898,14 @@ export class Validate {
867
898
  );
868
899
  }
869
900
 
870
- // Use caches for cardTypes and workflows, to avoid re-reading the same JSON files multiple times.
871
- const cardType = this.getAndCacheResource(
872
- project,
873
- this.validatedCardTypes,
874
- card.metadata?.cardType || '',
875
- );
901
+ let cardType;
902
+ try {
903
+ cardType = card.metadata?.cardType
904
+ ? project.resources.byType(card.metadata?.cardType, 'cardTypes').show()
905
+ : undefined;
906
+ } catch {
907
+ cardType = undefined;
908
+ }
876
909
  if (!cardType) {
877
910
  validationErrors.push(
878
911
  `Card '${card.key}' has invalid card type '${card.metadata?.cardType}'`,
@@ -886,11 +919,14 @@ export class Validate {
886
919
  return validationErrors.join('\n');
887
920
  }
888
921
 
889
- const workflow = this.getAndCacheResource(
890
- project,
891
- this.validatedWorkflows,
892
- cardType.workflow,
893
- );
922
+ let workflow;
923
+ try {
924
+ workflow = project.resources
925
+ .byType(cardType.workflow, 'workflows')
926
+ .show();
927
+ } catch {
928
+ workflow = undefined;
929
+ }
894
930
 
895
931
  if (!workflow) {
896
932
  validationErrors.push(
@@ -919,15 +955,16 @@ export class Validate {
919
955
 
920
956
  /**
921
957
  * Validates a single resource.
922
- * @param resource Resource to validate
923
- * @returns string containing all validation errors
958
+ * @param resourceName Resource to validate
959
+ * @param project Project instance to use.
960
+ * @returns validation errors, if any.
924
961
  */
925
962
  public async validateResource(
926
963
  resourceName: ResourceName,
927
964
  project: Project,
928
965
  ): Promise<string> {
929
966
  try {
930
- const resource = Project.resourceObject(project, resourceName);
967
+ const resource = project.resources.byType(resourceName);
931
968
  await resource.validate();
932
969
  return '';
933
970
  } catch (error) {
@@ -19,17 +19,19 @@ import { writeFile } from 'node:fs/promises';
19
19
  import { CardCache } from './project/card-cache.js';
20
20
  import { cardPathParts } from '../utils/card-utils.js';
21
21
  import { deleteDir } from '../utils/file-utils.js';
22
+ import { getChildLogger } from '../utils/log-utils.js';
22
23
  import { writeJsonFile } from '../utils/json.js';
23
24
 
24
25
  import type {
25
26
  CardAttachment,
26
27
  Card,
28
+ CardMetadata,
27
29
  FetchCardDetails,
28
30
  } from '../interfaces/project-interfaces.js';
29
31
 
30
32
  import asciidoctor from '@asciidoctor/core';
31
33
 
32
- import { ROOT } from '../utils/constants.js';
34
+ import { isPredefinedField, ROOT } from '../utils/constants.js';
33
35
 
34
36
  /**
35
37
  * Card container base class. Used for both Project and Template.
@@ -38,17 +40,19 @@ import { ROOT } from '../utils/constants.js';
38
40
  export class CardContainer {
39
41
  public basePath: string;
40
42
  protected cardCache: CardCache;
41
- protected containerName: string;
42
43
  protected prefix: string;
43
44
 
45
+ protected static get logger() {
46
+ return getChildLogger({ module: 'CardContainer' });
47
+ }
48
+
44
49
  static cardContentFile = 'index.adoc';
45
50
  static cardMetadataFile = 'index.json';
46
51
  static projectConfigFileName = 'cardsConfig.json';
47
52
  static schemaContentFile = '.schema';
48
53
 
49
- constructor(path: string, prefix: string, name: string) {
54
+ constructor(path: string, prefix: string) {
50
55
  this.basePath = path;
51
- this.containerName = name;
52
56
  this.prefix = prefix;
53
57
  this.cardCache = new CardCache(this.prefix);
54
58
  }
@@ -226,13 +230,41 @@ export class CardContainer {
226
230
  if (card.metadata != null) {
227
231
  const metadataFile = join(card.path, CardContainer.cardMetadataFile);
228
232
  card.metadata!.lastUpdated = new Date().toISOString();
229
- await writeJsonFile(metadataFile, card.metadata);
233
+
234
+ const sanitizedMetadata = CardContainer.sanitizeMetadata(card);
235
+ await writeJsonFile(metadataFile, sanitizedMetadata);
230
236
  return this.cardCache.updateCardMetadata(card.key, card.metadata);
231
237
  }
232
238
  return false;
233
239
  }
234
240
 
235
241
  /**
242
+ * Removes non-metadata fields that should not be persisted.
243
+ *
244
+ * @param metadata The metadata object to sanitize
245
+ * @returns Clean metadata object with only valid metadata fields
246
+ */
247
+ private static sanitizeMetadata(card: Card): CardMetadata {
248
+ const sanitized: Record<string, unknown> = {};
249
+
250
+ if (card.metadata) {
251
+ for (const [key, value] of Object.entries(card.metadata)) {
252
+ // Keys are not filtered out if they are: predefined, or field types
253
+ if (isPredefinedField(key) || key.includes('/')) {
254
+ sanitized[key] = value;
255
+ } else {
256
+ this.logger.warn(
257
+ `Card ${card.key} had extra metadata key ${key} with value ${value}. Key was removed`,
258
+ );
259
+ }
260
+ // Everything else is filtered out
261
+ }
262
+ }
263
+
264
+ return sanitized as CardMetadata;
265
+ }
266
+
267
+ /*
236
268
  * Show root cards from a given path.
237
269
  * @param path The path to get cards from
238
270
  * @returns an array of root-level cards (each with their children populated).