@cyberismo/data-handler 0.0.11 → 0.0.13

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 (179) hide show
  1. package/dist/card-metadata-updater.js +1 -1
  2. package/dist/card-metadata-updater.js.map +1 -1
  3. package/dist/command-handler.d.ts +26 -39
  4. package/dist/command-handler.js +76 -31
  5. package/dist/command-handler.js.map +1 -1
  6. package/dist/command-manager.d.ts +2 -1
  7. package/dist/command-manager.js +4 -2
  8. package/dist/command-manager.js.map +1 -1
  9. package/dist/commands/calculate.d.ts +7 -0
  10. package/dist/commands/calculate.js +9 -0
  11. package/dist/commands/calculate.js.map +1 -1
  12. package/dist/commands/create.d.ts +5 -0
  13. package/dist/commands/create.js +15 -8
  14. package/dist/commands/create.js.map +1 -1
  15. package/dist/commands/fetch.d.ts +24 -0
  16. package/dist/commands/fetch.js +118 -0
  17. package/dist/commands/fetch.js.map +1 -0
  18. package/dist/commands/import.js +2 -2
  19. package/dist/commands/import.js.map +1 -1
  20. package/dist/commands/index.d.ts +2 -1
  21. package/dist/commands/index.js +2 -1
  22. package/dist/commands/index.js.map +1 -1
  23. package/dist/commands/remove.d.ts +1 -0
  24. package/dist/commands/remove.js +16 -12
  25. package/dist/commands/remove.js.map +1 -1
  26. package/dist/commands/rename.js +4 -6
  27. package/dist/commands/rename.js.map +1 -1
  28. package/dist/commands/show.d.ts +22 -1
  29. package/dist/commands/show.js +56 -0
  30. package/dist/commands/show.js.map +1 -1
  31. package/dist/commands/update.d.ts +11 -1
  32. package/dist/commands/update.js +14 -2
  33. package/dist/commands/update.js.map +1 -1
  34. package/dist/commands/validate.d.ts +2 -1
  35. package/dist/commands/validate.js +10 -10
  36. package/dist/commands/validate.js.map +1 -1
  37. package/dist/containers/card-container.js +1 -1
  38. package/dist/containers/card-container.js.map +1 -1
  39. package/dist/containers/project/calculation-engine.d.ts +8 -0
  40. package/dist/containers/project/calculation-engine.js +21 -10
  41. package/dist/containers/project/calculation-engine.js.map +1 -1
  42. package/dist/containers/project.d.ts +19 -8
  43. package/dist/containers/project.js +52 -34
  44. package/dist/containers/project.js.map +1 -1
  45. package/dist/containers/template.js +1 -1
  46. package/dist/containers/template.js.map +1 -1
  47. package/dist/index.d.ts +4 -2
  48. package/dist/index.js.map +1 -1
  49. package/dist/interfaces/command-options.d.ts +81 -0
  50. package/dist/interfaces/command-options.js +14 -0
  51. package/dist/interfaces/command-options.js.map +1 -0
  52. package/dist/interfaces/folder-content-interfaces.d.ts +50 -0
  53. package/dist/interfaces/folder-content-interfaces.js +45 -0
  54. package/dist/interfaces/folder-content-interfaces.js.map +1 -0
  55. package/dist/interfaces/project-interfaces.d.ts +13 -2
  56. package/dist/interfaces/project-interfaces.js.map +1 -1
  57. package/dist/interfaces/resource-interfaces.d.ts +28 -10
  58. package/dist/interfaces/resource-interfaces.js.map +1 -1
  59. package/dist/macros/base-macro.d.ts +1 -1
  60. package/dist/macros/base-macro.js +1 -1
  61. package/dist/macros/base-macro.js.map +1 -1
  62. package/dist/macros/createCards/index.d.ts +1 -1
  63. package/dist/macros/createCards/index.js +1 -1
  64. package/dist/macros/createCards/index.js.map +1 -1
  65. package/dist/macros/graph/index.d.ts +1 -1
  66. package/dist/macros/graph/index.js +21 -29
  67. package/dist/macros/graph/index.js.map +1 -1
  68. package/dist/macros/image/index.d.ts +1 -1
  69. package/dist/macros/image/index.js +1 -7
  70. package/dist/macros/image/index.js.map +1 -1
  71. package/dist/macros/include/index.d.ts +1 -1
  72. package/dist/macros/include/index.js +1 -1
  73. package/dist/macros/include/index.js.map +1 -1
  74. package/dist/macros/index.d.ts +12 -5
  75. package/dist/macros/index.js +19 -7
  76. package/dist/macros/index.js.map +1 -1
  77. package/dist/macros/percentage/index.d.ts +1 -1
  78. package/dist/macros/percentage/index.js +1 -1
  79. package/dist/macros/percentage/index.js.map +1 -1
  80. package/dist/macros/report/index.d.ts +1 -1
  81. package/dist/macros/report/index.js +5 -5
  82. package/dist/macros/report/index.js.map +1 -1
  83. package/dist/macros/scoreCard/index.d.ts +1 -1
  84. package/dist/macros/scoreCard/index.js +1 -1
  85. package/dist/macros/scoreCard/index.js.map +1 -1
  86. package/dist/macros/vega/index.d.ts +1 -1
  87. package/dist/macros/vega/index.js +1 -1
  88. package/dist/macros/vega/index.js.map +1 -1
  89. package/dist/macros/vegalite/index.d.ts +1 -1
  90. package/dist/macros/vegalite/index.js +1 -1
  91. package/dist/macros/vegalite/index.js.map +1 -1
  92. package/dist/macros/xref/index.d.ts +1 -1
  93. package/dist/macros/xref/index.js +1 -1
  94. package/dist/macros/xref/index.js.map +1 -1
  95. package/dist/project-settings.d.ts +14 -1
  96. package/dist/project-settings.js +51 -1
  97. package/dist/project-settings.js.map +1 -1
  98. package/dist/resources/card-type-resource.js +11 -5
  99. package/dist/resources/card-type-resource.js.map +1 -1
  100. package/dist/resources/field-type-resource.d.ts +5 -0
  101. package/dist/resources/field-type-resource.js +9 -4
  102. package/dist/resources/field-type-resource.js.map +1 -1
  103. package/dist/resources/folder-resource.d.ts +37 -9
  104. package/dist/resources/folder-resource.js +108 -12
  105. package/dist/resources/folder-resource.js.map +1 -1
  106. package/dist/resources/graph-model-resource.d.ts +7 -4
  107. package/dist/resources/graph-model-resource.js +12 -25
  108. package/dist/resources/graph-model-resource.js.map +1 -1
  109. package/dist/resources/graph-view-resource.d.ts +7 -4
  110. package/dist/resources/graph-view-resource.js +15 -31
  111. package/dist/resources/graph-view-resource.js.map +1 -1
  112. package/dist/resources/link-type-resource.js +1 -1
  113. package/dist/resources/link-type-resource.js.map +1 -1
  114. package/dist/resources/report-resource.d.ts +14 -10
  115. package/dist/resources/report-resource.js +41 -45
  116. package/dist/resources/report-resource.js.map +1 -1
  117. package/dist/resources/resource-object.d.ts +7 -0
  118. package/dist/resources/resource-object.js.map +1 -1
  119. package/dist/resources/template-resource.d.ts +5 -1
  120. package/dist/resources/template-resource.js +12 -7
  121. package/dist/resources/template-resource.js.map +1 -1
  122. package/dist/resources/workflow-resource.js +12 -5
  123. package/dist/resources/workflow-resource.js.map +1 -1
  124. package/dist/utils/log-utils.js +1 -1
  125. package/dist/utils/log-utils.js.map +1 -1
  126. package/dist/utils/report.js +6 -0
  127. package/dist/utils/report.js.map +1 -1
  128. package/dist/utils/resource-utils.d.ts +8 -0
  129. package/dist/utils/resource-utils.js +11 -0
  130. package/dist/utils/resource-utils.js.map +1 -1
  131. package/package.json +11 -11
  132. package/src/card-metadata-updater.ts +1 -1
  133. package/src/command-handler.ts +129 -61
  134. package/src/command-manager.ts +4 -1
  135. package/src/commands/calculate.ts +18 -0
  136. package/src/commands/create.ts +31 -19
  137. package/src/commands/fetch.ts +152 -0
  138. package/src/commands/import.ts +2 -0
  139. package/src/commands/index.ts +2 -0
  140. package/src/commands/remove.ts +18 -12
  141. package/src/commands/rename.ts +11 -11
  142. package/src/commands/show.ts +72 -0
  143. package/src/commands/update.ts +20 -2
  144. package/src/commands/validate.ts +13 -10
  145. package/src/containers/card-container.ts +1 -1
  146. package/src/containers/project/calculation-engine.ts +27 -11
  147. package/src/containers/project.ts +71 -61
  148. package/src/containers/template.ts +1 -1
  149. package/src/index.ts +36 -2
  150. package/src/interfaces/command-options.ts +144 -0
  151. package/src/interfaces/folder-content-interfaces.ts +69 -0
  152. package/src/interfaces/project-interfaces.ts +18 -0
  153. package/src/interfaces/resource-interfaces.ts +41 -12
  154. package/src/macros/base-macro.ts +5 -2
  155. package/src/macros/createCards/index.ts +1 -1
  156. package/src/macros/graph/index.ts +47 -51
  157. package/src/macros/image/index.ts +1 -7
  158. package/src/macros/include/index.ts +1 -1
  159. package/src/macros/index.ts +19 -7
  160. package/src/macros/percentage/index.ts +1 -1
  161. package/src/macros/report/index.ts +5 -5
  162. package/src/macros/scoreCard/index.ts +1 -1
  163. package/src/macros/vega/index.ts +1 -1
  164. package/src/macros/vegalite/index.ts +1 -1
  165. package/src/macros/xref/index.ts +1 -1
  166. package/src/project-settings.ts +62 -1
  167. package/src/resources/card-type-resource.ts +12 -6
  168. package/src/resources/field-type-resource.ts +9 -4
  169. package/src/resources/folder-resource.ts +149 -19
  170. package/src/resources/graph-model-resource.ts +16 -27
  171. package/src/resources/graph-view-resource.ts +23 -33
  172. package/src/resources/link-type-resource.ts +1 -1
  173. package/src/resources/report-resource.ts +60 -62
  174. package/src/resources/resource-object.ts +11 -0
  175. package/src/resources/template-resource.ts +12 -7
  176. package/src/resources/workflow-resource.ts +11 -6
  177. package/src/utils/log-utils.ts +1 -1
  178. package/src/utils/report.ts +6 -0
  179. package/src/utils/resource-utils.ts +16 -0
@@ -164,6 +164,7 @@ export interface ProjectMetadata {
164
164
  path: string;
165
165
  prefix: string;
166
166
  modules: string[];
167
+ hubs: HubSetting[];
167
168
  numberOfCards: number;
168
169
  }
169
170
 
@@ -172,6 +173,20 @@ export interface ProjectSettings {
172
173
  cardKeyPrefix: string;
173
174
  name: string;
174
175
  modules: ModuleSetting[];
176
+ hubs: HubSetting[];
177
+ }
178
+
179
+ // Hub configuration
180
+ export interface HubSetting {
181
+ location: string;
182
+ description?: string;
183
+ displayName?: string;
184
+ }
185
+
186
+ // Module configuration for a hub.
187
+ export interface ModuleSettingFromHub extends ModuleSetting {
188
+ documentationLocation: string;
189
+ displayName: string;
175
190
  }
176
191
 
177
192
  // Module configuration.
@@ -195,6 +210,7 @@ export type RemovableResourceTypes =
195
210
  | 'fieldType'
196
211
  | 'graphModel'
197
212
  | 'graphView'
213
+ | 'hub'
198
214
  | 'link'
199
215
  | 'linkType'
200
216
  | 'module'
@@ -229,6 +245,8 @@ export type ResourceTypes =
229
245
  | 'attachments'
230
246
  | 'calculation'
231
247
  | 'cards'
248
+ | 'hubs'
249
+ | 'importableModules'
232
250
  | 'label'
233
251
  | 'labels'
234
252
  | 'links'
@@ -11,7 +11,11 @@
11
11
  License along with this program. If not, see <https://www.gnu.org/licenses/>.
12
12
  */
13
13
 
14
- import type { Schema } from 'jsonschema';
14
+ import type {
15
+ GraphModelContent,
16
+ GraphViewContent,
17
+ ReportContent,
18
+ } from './folder-content-interfaces.js';
15
19
 
16
20
  /**
17
21
  * Each resource represents a file (or a folder in some cases) with metadata stored
@@ -32,6 +36,12 @@ export interface CardType extends ResourceBaseMetadata {
32
36
  optionallyVisibleFields: string[];
33
37
  }
34
38
 
39
+ // Base content update key interface
40
+ export interface ContentUpdateKey {
41
+ key: 'content';
42
+ subKey: string; // Resource-specific types should narrow this
43
+ }
44
+
35
45
  // Custom field
36
46
  // todo: merge with FieldType.
37
47
  export interface CustomField {
@@ -71,19 +81,29 @@ export interface FieldType extends ResourceBaseMetadata {
71
81
  export interface GraphModelMetadata extends ResourceBaseMetadata {
72
82
  category?: string;
73
83
  }
74
-
75
84
  export interface GraphModel extends GraphModelMetadata {
76
- calculationFile: string;
85
+ content: GraphModelContent;
77
86
  }
87
+ export type GraphModelContentPropertyName = 'model';
88
+ export interface GraphModelContentUpdateKey {
89
+ key: 'content';
90
+ subKey: GraphModelContentPropertyName;
91
+ }
92
+ export type GraphModelUpdateKey = string | GraphModelContentUpdateKey;
78
93
 
79
94
  // Graph view content.
80
95
  export interface GraphViewMetadata extends ResourceBaseMetadata {
81
96
  category?: string;
82
97
  }
83
-
98
+ export type GraphViewContentPropertyName = 'viewTemplate';
84
99
  export interface GraphView extends GraphViewMetadata {
85
- handleBarFile: string;
100
+ content: GraphViewContent;
86
101
  }
102
+ export interface GraphViewContentUpdateKey {
103
+ key: 'content';
104
+ subKey: GraphViewContentPropertyName;
105
+ }
106
+ export type GraphViewUpdateKey = string | GraphViewContentUpdateKey;
87
107
 
88
108
  // Link content.
89
109
  export interface Link {
@@ -103,13 +123,20 @@ export interface LinkType extends ResourceBaseMetadata {
103
123
 
104
124
  // Report resource.
105
125
  export interface Report extends ResourceBaseMetadata {
106
- name: string;
107
- metadata: ReportMetadata;
108
- contentTemplate: string;
109
- queryTemplate: string;
110
- schema?: Schema;
126
+ content: ReportContent;
111
127
  }
112
128
 
129
+ // Resource-specific content names
130
+ export type ReportContentPropertyName =
131
+ | 'contentTemplate'
132
+ | 'queryTemplate'
133
+ | 'schema';
134
+ export interface ReportContentUpdateKey {
135
+ key: 'content';
136
+ subKey: ReportContentPropertyName;
137
+ }
138
+ export type ReportUpdateKey = string | ReportContentUpdateKey;
139
+
113
140
  // Metadata for report
114
141
  export interface ReportMetadata extends ResourceBaseMetadata {
115
142
  category: string;
@@ -136,10 +163,9 @@ export type ResourceContent =
136
163
  | Workflow;
137
164
 
138
165
  // Template configuration details.
139
- export interface TemplateConfiguration extends ResourceBaseMetadata {
166
+ export interface TemplateConfiguration extends TemplateMetadata {
140
167
  path: string;
141
168
  numberOfCards: number;
142
- metadata: TemplateMetadata;
143
169
  }
144
170
 
145
171
  // Template configuration content details.
@@ -147,6 +173,9 @@ export interface TemplateMetadata extends ResourceBaseMetadata {
147
173
  category?: string;
148
174
  }
149
175
 
176
+ // Generic update key
177
+ export type UpdateKey = string | ContentUpdateKey;
178
+
150
179
  // Workflow's json file content.
151
180
  export interface Workflow extends ResourceBaseMetadata {
152
181
  states: WorkflowState[];
@@ -63,7 +63,10 @@ abstract class BaseMacro {
63
63
  input: unknown,
64
64
  ): Promise<string>;
65
65
 
66
- protected abstract handleValidate(input: unknown): void;
66
+ protected abstract handleValidate(
67
+ context: MacroGenerationContext,
68
+ input: unknown,
69
+ ): void;
67
70
 
68
71
  public get metadata() {
69
72
  return this.macroMetadata;
@@ -127,7 +130,7 @@ abstract class BaseMacro {
127
130
 
128
131
  if (context.mode === 'validate') {
129
132
  try {
130
- this.handleValidate(JSON.parse(input));
133
+ this.handleValidate(context, JSON.parse(input));
131
134
  } catch (error) {
132
135
  if (error instanceof Error) {
133
136
  const errorMessage = `From card '${context.cardKey}' a macro validation error:\n\n${error.message}.\n\nCard content:\n ${input}`;
@@ -34,7 +34,7 @@ class CreateCardsMacro extends BaseMacro {
34
34
  super(macroMetadata, tasksQueue);
35
35
  }
36
36
 
37
- handleValidate = (input: unknown) => {
37
+ handleValidate = (_: MacroGenerationContext, input: unknown) => {
38
38
  this.validate(input);
39
39
  };
40
40
 
@@ -14,16 +14,16 @@
14
14
  import BaseMacro from '../base-macro.js';
15
15
  import { createImage, validateMacroContent } from '../index.js';
16
16
  import Handlebars from 'handlebars';
17
- import { join } from 'node:path';
18
17
  import type { MacroGenerationContext } from '../../interfaces/macros.js';
19
18
  import macroMetadata from './metadata.js';
20
19
  import { pathExists } from '../../utils/file-utils.js';
21
20
  import { readFile } from 'node:fs/promises';
22
- import { resourceName } from '../../utils/resource-utils.js';
21
+ import { readFileSync } from 'node:fs';
23
22
  import type { Schema } from 'jsonschema';
24
- import { validateJson } from '../../utils/validate.js';
25
23
  import type TaskQueue from '../task-queue.js';
26
24
  import { ClingoError } from '@cyberismo/node-clingo';
25
+ import { resourceFilePath } from '../../utils/resource-utils.js';
26
+ import { resourceName } from '../../utils/resource-utils.js';
27
27
 
28
28
  export interface GraphOptions {
29
29
  model: string;
@@ -35,55 +35,23 @@ class ReportMacro extends BaseMacro {
35
35
  super(macroMetadata, tasksQueue);
36
36
  }
37
37
 
38
- handleValidate = (input: unknown) => {
39
- this.parseOptions(input);
38
+ handleValidate = (context: MacroGenerationContext, input: unknown) => {
39
+ this.parseOptions(input, context);
40
40
  };
41
41
 
42
42
  handleStatic = async (context: MacroGenerationContext, input: unknown) => {
43
- const resourceNameToPath = (name: string, fileName: string) => {
44
- const { identifier, prefix, type } = resourceName(name);
45
- if (prefix === context.project.projectPrefix) {
46
- return join(
47
- context.project.paths.resourcesFolder,
48
- type,
49
- identifier,
50
- fileName,
51
- );
52
- }
53
- return join(
54
- context.project.paths.modulesFolder,
55
- prefix,
56
- type,
57
- identifier,
58
- fileName,
59
- );
60
- };
61
-
62
- const options = this.parseOptions(input);
63
-
64
- let schema: Schema | null = null;
65
- try {
66
- schema = JSON.parse(
67
- await readFile(
68
- resourceNameToPath(options.view, 'parameterSchema.json'),
69
- { encoding: 'utf-8' },
70
- ),
71
- );
72
- } catch (err) {
73
- this.logger.trace(
74
- err,
75
- 'Graph schema not found or failed to read, skipping validation',
76
- );
77
- }
78
-
79
- if (schema) {
80
- validateJson(options, {
81
- schema,
82
- });
83
- }
84
-
85
- const modelLocation = resourceNameToPath(options.model, 'model.lp');
86
- const viewLocation = resourceNameToPath(options.view, 'view.lp.hbs');
43
+ const options = this.parseOptions(input, context);
44
+
45
+ const modelLocation = resourceFilePath(
46
+ context.project,
47
+ resourceName(options.model),
48
+ 'model.lp',
49
+ );
50
+ const viewLocation = resourceFilePath(
51
+ context.project,
52
+ resourceName(options.view),
53
+ 'view.lp.hbs',
54
+ );
87
55
 
88
56
  if (!pathExists(modelLocation)) {
89
57
  throw new Error(`Graph: Model ${options.model} does not exist`);
@@ -128,8 +96,36 @@ class ReportMacro extends BaseMacro {
128
96
  return createImage(result);
129
97
  };
130
98
 
131
- private parseOptions(input: unknown): GraphOptions {
132
- return validateMacroContent<GraphOptions>(this.metadata, input);
99
+ private parseOptions(
100
+ input: unknown,
101
+ context: MacroGenerationContext,
102
+ ): GraphOptions {
103
+ const options = validateMacroContent<GraphOptions>(this.metadata, input);
104
+
105
+ let schema: Schema | null = null;
106
+ try {
107
+ schema = JSON.parse(
108
+ readFileSync(
109
+ resourceFilePath(
110
+ context.project,
111
+ resourceName(options.view),
112
+ 'parameterSchema.json',
113
+ ),
114
+ { encoding: 'utf-8' },
115
+ ),
116
+ );
117
+ } catch (err) {
118
+ this.logger.trace(
119
+ err,
120
+ 'Graph schema not found or failed to read, skipping validation',
121
+ );
122
+ }
123
+
124
+ if (schema) {
125
+ validateMacroContent(this.metadata, input, schema);
126
+ }
127
+
128
+ return options;
133
129
  }
134
130
  }
135
131
 
@@ -44,7 +44,7 @@ export default class ImageMacro extends BaseMacro {
44
44
  super(macroMetadata, tasksQueue);
45
45
  }
46
46
 
47
- handleValidate = (input: unknown) => {
47
+ handleValidate = (_: MacroGenerationContext, input: unknown) => {
48
48
  this.validate(input);
49
49
  };
50
50
 
@@ -58,9 +58,6 @@ export default class ImageMacro extends BaseMacro {
58
58
  // Get the attachment folder path
59
59
  const attachmentFolder =
60
60
  await context.project.cardAttachmentFolder(cardKey);
61
- if (!attachmentFolder) {
62
- throw new Error(`Card '${cardKey}' not found`);
63
- }
64
61
 
65
62
  // Read the file and convert to base64
66
63
  const attachmentPath = join(attachmentFolder, options.fileName);
@@ -90,9 +87,6 @@ export default class ImageMacro extends BaseMacro {
90
87
  // Verify that the card and attachment folder exist
91
88
  const attachmentFolder =
92
89
  await context.project.cardAttachmentFolder(cardKey);
93
- if (!attachmentFolder) {
94
- throw new Error(`Card '${cardKey}' not found`);
95
- }
96
90
 
97
91
  const attachmentPath = join(attachmentFolder, options.fileName);
98
92
  if (!existsSync(attachmentPath)) {
@@ -31,7 +31,7 @@ export default class IncludeMacro extends BaseMacro {
31
31
  super(macroMetadata, tasksQueue);
32
32
  }
33
33
 
34
- handleValidate = (input: unknown) => {
34
+ handleValidate = (_: MacroGenerationContext, input: unknown) => {
35
35
  this.validate(input);
36
36
  };
37
37
 
@@ -153,6 +153,7 @@ export const macros: {
153
153
  * Validates the content inside a macro
154
154
  * @param macro - The macro to validate the content of
155
155
  * @param data - The data to validate
156
+ * @param schema - Schema to use in validation
156
157
  * @returns The validated data
157
158
  */
158
159
  export function validateMacroContent<T>(
@@ -180,7 +181,10 @@ export function validateMacroContent<T>(
180
181
 
181
182
  /**
182
183
  * Registers the macros with Handlebars
183
- * @param {Mode} mode - The mode to register the macros in
184
+ * @param instance - Handlebar instance
185
+ * @param context - The context for macro generation
186
+ * @param tasks - Tasks to register
187
+ * @returns macro instances
184
188
  */
185
189
  export function registerMacros(
186
190
  instance: typeof Handlebars,
@@ -201,7 +205,8 @@ export function registerMacros(
201
205
  }
202
206
  /**
203
207
  * Calculate amount of handlebars templates inside a string
204
- * @param input
208
+ * @param input - String to calculate templates from
209
+ * @returns number of macros
205
210
  */
206
211
  export function macroCount(input: string): number {
207
212
  const regex = /{{#[\w\s-]+?}}/g;
@@ -224,11 +229,11 @@ export function registerEmptyMacros(instance: typeof Handlebars) {
224
229
  }
225
230
 
226
231
  /**
227
- * Handle the macros in the content
232
+ * Handle the macros in the content.
228
233
  * @param content - The content to handle the macros in
229
234
  * @param context - The context for macro generation
230
- * @param calculate - The calculate function
231
235
  * @param preserveRawBlocks - If true, don't unescape raw blocks at the end (for nested evaluations)
236
+ * @returns macro result
232
237
  */
233
238
  export async function evaluateMacros(
234
239
  content: string,
@@ -240,7 +245,7 @@ export async function evaluateMacros(
240
245
  registerMacros(handlebars, context, tasks);
241
246
  let result = content;
242
247
  while ((context.maxTries ?? 10) > 0) {
243
- tasks.reset();
248
+ await tasks.reset();
244
249
  try {
245
250
  const compiled = handlebars.compile(preprocessRawBlocks(result), {
246
251
  strict: true,
@@ -313,6 +318,7 @@ export function applyMacroResults(
313
318
  * Handles errors that come when handling macros
314
319
  * @param error - The error that was thrown
315
320
  * @param macro - The macro that caused the error
321
+ * @param context - General context for the macro evaluation process
316
322
  * @returns The error message that is valid adoc
317
323
  */
318
324
  export function handleMacroError(
@@ -350,9 +356,14 @@ export function handleMacroError(
350
356
  }
351
357
 
352
358
  // This is used to generate unique keys for macros
353
- // There might be a better way to do this
359
+ // TODO: There might be a better way to do this
354
360
  let macroCounter = 0;
355
361
 
362
+ /**
363
+ * Converts object to base64
364
+ * @param obj Object to convert
365
+ * @returns base64 conversion (as a Buffer)
366
+ */
356
367
  function objectToBase64(obj: unknown): string {
357
368
  return Buffer.from(JSON.stringify(obj), 'utf-8').toString('base64');
358
369
  }
@@ -393,8 +404,9 @@ export function createCodeBlock(content: string) {
393
404
  }
394
405
 
395
406
  /**
396
- * Helper function for including base64 encoded images for now
407
+ * Helper function for including base64 encoded images
397
408
  * @param image base64 encoded image
409
+ * @param controls Add controls
398
410
  * @returns valid asciidoc with the image
399
411
  */
400
412
  export function createImage(image: string, controls: boolean = true) {
@@ -30,7 +30,7 @@ class PercentageMacro extends BaseMacro {
30
30
  constructor(tasksQueue: TaskQueue) {
31
31
  super(macroMetadata, tasksQueue);
32
32
  }
33
- handleValidate = (data: string) => {
33
+ handleValidate = (_: MacroGenerationContext, data: string) => {
34
34
  this.validate(data);
35
35
  };
36
36
 
@@ -31,7 +31,7 @@ class ReportMacro extends BaseMacro {
31
31
  constructor(tasks: TaskQueue) {
32
32
  super(macroMetadata, tasks);
33
33
  }
34
- handleValidate = (input: unknown) => {
34
+ handleValidate = (_: MacroGenerationContext, input: unknown) => {
35
35
  this.validate(input);
36
36
  };
37
37
 
@@ -45,16 +45,16 @@ class ReportMacro extends BaseMacro {
45
45
 
46
46
  if (!report) throw new Error(`Report ${options.name} does not exist`);
47
47
 
48
- if (report.schema) {
48
+ if (report.content.schema) {
49
49
  validateJson(options, {
50
- schema: report.schema,
50
+ schema: report.content.schema,
51
51
  });
52
52
  }
53
53
  try {
54
54
  return await generateReportContent({
55
55
  calculate: context.project.calculationEngine,
56
- contentTemplate: report.contentTemplate,
57
- queryTemplate: report.queryTemplate,
56
+ contentTemplate: report.content.contentTemplate,
57
+ queryTemplate: report.content.queryTemplate,
58
58
  options: {
59
59
  cardKey: context.cardKey,
60
60
  ...options,
@@ -31,7 +31,7 @@ class ScoreCardMacro extends BaseMacro {
31
31
  constructor(tasksQueue: TaskQueue) {
32
32
  super(macroMetadata, tasksQueue);
33
33
  }
34
- handleValidate = (data: string) => {
34
+ handleValidate = (_: MacroGenerationContext, data: string) => {
35
35
  this.validate(data);
36
36
  };
37
37
 
@@ -31,7 +31,7 @@ class VegaMacro extends BaseMacro {
31
31
  super(macroMetadata, tasksQueue);
32
32
  }
33
33
 
34
- handleValidate = (input: unknown) => {
34
+ handleValidate = (_: MacroGenerationContext, input: unknown) => {
35
35
  this.validate(input);
36
36
  };
37
37
 
@@ -26,7 +26,7 @@ class VegaLiteMacro extends BaseMacro {
26
26
  super(macroMetadata, tasksQueue);
27
27
  }
28
28
 
29
- handleValidate = (input: unknown) => {
29
+ handleValidate = (_: MacroGenerationContext, input: unknown) => {
30
30
  this.validate(input);
31
31
  };
32
32
 
@@ -27,7 +27,7 @@ export default class XrefMacro extends BaseMacro {
27
27
  super(macroMetadata, tasksQueue);
28
28
  }
29
29
 
30
- handleValidate = (input: unknown) => {
30
+ handleValidate = (_: MacroGenerationContext, input: unknown) => {
31
31
  this.validate(input);
32
32
  };
33
33
 
@@ -13,9 +13,11 @@
13
13
 
14
14
  import { writeJsonFile as atomicWrite } from 'write-json-file';
15
15
 
16
- import { resolve } from 'path';
16
+ import { resolve } from 'node:path';
17
+ import { URL } from 'node:url';
17
18
 
18
19
  import type {
20
+ HubSetting,
19
21
  ModuleSetting,
20
22
  ProjectSettings,
21
23
  } from './interfaces/project-interfaces.js';
@@ -31,6 +33,7 @@ export class ProjectConfiguration implements ProjectSettings {
31
33
  name: string;
32
34
  cardKeyPrefix: string;
33
35
  modules: ModuleSetting[];
36
+ hubs: HubSetting[];
34
37
  private settingPath: string;
35
38
 
36
39
  constructor(path: string) {
@@ -38,6 +41,7 @@ export class ProjectConfiguration implements ProjectSettings {
38
41
  this.settingPath = path;
39
42
  this.cardKeyPrefix = '';
40
43
  this.modules = [];
44
+ this.hubs = [];
41
45
  this.readSettings();
42
46
  }
43
47
 
@@ -70,6 +74,7 @@ export class ProjectConfiguration implements ProjectSettings {
70
74
  this.cardKeyPrefix = settings.cardKeyPrefix;
71
75
  this.name = settings.name;
72
76
  this.modules = settings.modules || [];
77
+ this.hubs = settings.hubs || [];
73
78
  } else {
74
79
  throw new Error(`Invalid configuration file '${this.settingPath}'`);
75
80
  }
@@ -81,9 +86,51 @@ export class ProjectConfiguration implements ProjectSettings {
81
86
  cardKeyPrefix: this.cardKeyPrefix,
82
87
  name: this.name,
83
88
  modules: this.modules,
89
+ hubs: this.hubs,
84
90
  };
85
91
  }
86
92
 
93
+ /**
94
+ * Adds a new hub.
95
+ * @param hubName URL of the hub to add
96
+ * @throws if hub is already in the project or URL is invalid
97
+ */
98
+ public async addHub(hubName: string) {
99
+ const trimmedHub = hubName?.trim();
100
+ if (!trimmedHub) {
101
+ throw new Error(`Cannot add empty hub to the project`);
102
+ }
103
+
104
+ const exists = this.hubs.find((item) => item.location === trimmedHub);
105
+ if (exists) {
106
+ throw new Error(
107
+ `Hub '${trimmedHub}' already exists as a hub for the project`,
108
+ );
109
+ }
110
+
111
+ try {
112
+ const hubUrl = new URL(trimmedHub);
113
+ if (!['http:', 'https:'].includes(hubUrl.protocol)) {
114
+ throw new Error(
115
+ `Invalid URL protocol '${hubUrl.protocol}'. Only HTTP and HTTPS protocols are supported for hubs.`,
116
+ );
117
+ }
118
+ if (!hubUrl.hostname) {
119
+ throw new Error('Hub URL must have a valid hostname.');
120
+ }
121
+ } catch (error) {
122
+ if (error instanceof TypeError) {
123
+ throw new Error(
124
+ `Invalid hub URL '${trimmedHub}'. Please provide a valid HTTP or HTTPS URL.`,
125
+ );
126
+ }
127
+ throw error;
128
+ }
129
+
130
+ this.hubs.push({ location: trimmedHub });
131
+ return this.save();
132
+ }
133
+
87
134
  /**
88
135
  * Adds new module to imported modules property.
89
136
  * @param module Module to add as dependency
@@ -105,6 +152,20 @@ export class ProjectConfiguration implements ProjectSettings {
105
152
  return this.save();
106
153
  }
107
154
 
155
+ /**
156
+ * Removes a hub.
157
+ * @param hubName Name of the hub to remove.
158
+ * @throws if hub is not part of the project
159
+ */
160
+ public async removeHub(hubName: string) {
161
+ const exists = this.hubs.find((item) => item.location === hubName);
162
+ if (!exists) {
163
+ throw new Error(`Hub '${hubName}' not part of the project`);
164
+ }
165
+ this.hubs = this.hubs.filter((item) => item.location !== hubName);
166
+ return this.save();
167
+ }
168
+
108
169
  /**
109
170
  * Removes module from imported modules property.
110
171
  * @param moduleName Name of the module to remove.
@@ -233,7 +233,10 @@ export class CardTypeResource extends FileResource {
233
233
  }
234
234
 
235
235
  // If value from 'customFields' is removed, remove it also from 'optionallyVisible' and 'alwaysVisible' arrays.
236
- private removeValueFromOtherArrays<Type>(op: Operation<Type>) {
236
+ private removeValueFromOtherArrays<Type>(
237
+ op: Operation<Type>,
238
+ content: CardType,
239
+ ) {
237
240
  // Update target can be a string, or an object. Of object, fetch only 'name'
238
241
  // todo: fetching 'name' or using string as name could be function in resource base class.
239
242
  const target = (op as RemoveOperation<Type>).target as Type;
@@ -242,8 +245,8 @@ export class CardTypeResource extends FileResource {
242
245
  field = { name: target['name' as keyof Type] };
243
246
  }
244
247
  const fieldName = (field ? field.name : target) as string;
245
- this.removeValue(this.data.alwaysVisibleFields, fieldName);
246
- this.removeValue(this.data.optionallyVisibleFields, fieldName);
248
+ this.removeValue(content.alwaysVisibleFields, fieldName);
249
+ this.removeValue(content.optionallyVisibleFields, fieldName);
247
250
  }
248
251
 
249
252
  // Sets content container values to be either '[]' or with proper values.
@@ -481,7 +484,7 @@ export class CardTypeResource extends FileResource {
481
484
  const existingName = this.content.name;
482
485
  await super.update(key, op);
483
486
 
484
- const content = this.content as CardType;
487
+ const content = structuredClone(this.content) as CardType;
485
488
  if (key === 'name') {
486
489
  content.name = super.handleScalar(op) as string;
487
490
  } else if (key === 'alwaysVisibleFields') {
@@ -513,12 +516,15 @@ export class CardTypeResource extends FileResource {
513
516
  content.customFields as Type[],
514
517
  ) as CustomField[];
515
518
  if (op.name === 'remove') {
516
- this.removeValueFromOtherArrays(op);
519
+ this.removeValueFromOtherArrays(op, content);
517
520
  }
521
+ } else if (key === 'description') {
522
+ content.description = super.handleScalar(op) as string;
523
+ } else if (key === 'displayName') {
524
+ content.displayName = super.handleScalar(op) as string;
518
525
  } else {
519
526
  throw new Error(`Unknown property '${key}' for CardType`);
520
527
  }
521
-
522
528
  await super.postUpdate(content, key, op);
523
529
 
524
530
  // Renaming this card type causes that references to its name must be updated.