@cyberismo/data-handler 0.0.7 → 0.0.9

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 (265) hide show
  1. package/dist/command-handler.d.ts +11 -2
  2. package/dist/command-handler.js +61 -18
  3. package/dist/command-handler.js.map +1 -1
  4. package/dist/command-manager.js +8 -8
  5. package/dist/command-manager.js.map +1 -1
  6. package/dist/commands/calculate.d.ts +7 -44
  7. package/dist/commands/calculate.js +8 -389
  8. package/dist/commands/calculate.js.map +1 -1
  9. package/dist/commands/create.d.ts +7 -4
  10. package/dist/commands/create.js +42 -15
  11. package/dist/commands/create.js.map +1 -1
  12. package/dist/commands/edit.d.ts +9 -3
  13. package/dist/commands/edit.js +33 -9
  14. package/dist/commands/edit.js.map +1 -1
  15. package/dist/commands/export.d.ts +13 -11
  16. package/dist/commands/export.js +80 -28
  17. package/dist/commands/export.js.map +1 -1
  18. package/dist/commands/import.d.ts +7 -0
  19. package/dist/commands/import.js +21 -2
  20. package/dist/commands/import.js.map +1 -1
  21. package/dist/commands/move.d.ts +11 -12
  22. package/dist/commands/move.js +12 -13
  23. package/dist/commands/move.js.map +1 -1
  24. package/dist/commands/remove.d.ts +2 -4
  25. package/dist/commands/remove.js +8 -16
  26. package/dist/commands/remove.js.map +1 -1
  27. package/dist/commands/rename.d.ts +1 -3
  28. package/dist/commands/rename.js +3 -6
  29. package/dist/commands/rename.js.map +1 -1
  30. package/dist/commands/show.d.ts +37 -5
  31. package/dist/commands/show.js +85 -7
  32. package/dist/commands/show.js.map +1 -1
  33. package/dist/commands/transition.d.ts +1 -3
  34. package/dist/commands/transition.js +3 -5
  35. package/dist/commands/transition.js.map +1 -1
  36. package/dist/commands/update.d.ts +5 -1
  37. package/dist/commands/update.js +7 -1
  38. package/dist/commands/update.js.map +1 -1
  39. package/dist/commands/validate.d.ts +7 -8
  40. package/dist/commands/validate.js +30 -35
  41. package/dist/commands/validate.js.map +1 -1
  42. package/dist/containers/card-container.d.ts +6 -0
  43. package/dist/containers/card-container.js +61 -0
  44. package/dist/containers/card-container.js.map +1 -1
  45. package/dist/containers/project/calculation-engine.d.ts +90 -0
  46. package/dist/containers/project/calculation-engine.js +402 -0
  47. package/dist/containers/project/calculation-engine.js.map +1 -0
  48. package/dist/containers/project/resource-collector.d.ts +2 -1
  49. package/dist/containers/project/resource-collector.js +41 -33
  50. package/dist/containers/project/resource-collector.js.map +1 -1
  51. package/dist/containers/project.d.ts +18 -6
  52. package/dist/containers/project.js +37 -72
  53. package/dist/containers/project.js.map +1 -1
  54. package/dist/containers/template.d.ts +5 -0
  55. package/dist/containers/template.js +9 -0
  56. package/dist/containers/template.js.map +1 -1
  57. package/dist/exceptions/index.d.ts +20 -0
  58. package/dist/exceptions/index.js +16 -0
  59. package/dist/exceptions/index.js.map +1 -1
  60. package/dist/index.d.ts +5 -2
  61. package/dist/index.js +5 -1
  62. package/dist/index.js.map +1 -1
  63. package/dist/interfaces/macros.d.ts +7 -2
  64. package/dist/interfaces/project-interfaces.d.ts +21 -0
  65. package/dist/interfaces/project-interfaces.js +4 -0
  66. package/dist/interfaces/project-interfaces.js.map +1 -1
  67. package/dist/interfaces/resource-interfaces.d.ts +1 -1
  68. package/dist/interfaces/resource-interfaces.js.map +1 -1
  69. package/dist/macros/base-macro.d.ts +2 -0
  70. package/dist/macros/base-macro.js +66 -19
  71. package/dist/macros/base-macro.js.map +1 -1
  72. package/dist/macros/common.d.ts +16 -9
  73. package/dist/macros/common.js +22 -9
  74. package/dist/macros/common.js.map +1 -1
  75. package/dist/macros/createCards/index.d.ts +1 -2
  76. package/dist/macros/createCards/index.js.map +1 -1
  77. package/dist/macros/graph/index.d.ts +1 -3
  78. package/dist/macros/graph/index.js +11 -9
  79. package/dist/macros/graph/index.js.map +1 -1
  80. package/dist/macros/image/index.d.ts +39 -0
  81. package/dist/macros/image/index.js +78 -0
  82. package/dist/macros/image/index.js.map +1 -0
  83. package/dist/macros/image/metadata.d.ts +18 -0
  84. package/dist/macros/image/metadata.js +22 -0
  85. package/dist/macros/image/metadata.js.map +1 -0
  86. package/dist/macros/include/index.d.ts +32 -0
  87. package/dist/macros/include/index.js +97 -0
  88. package/dist/macros/include/index.js.map +1 -0
  89. package/dist/macros/include/metadata.d.ts +15 -0
  90. package/dist/macros/include/metadata.js +19 -0
  91. package/dist/macros/include/metadata.js.map +1 -0
  92. package/dist/macros/index.d.ts +39 -31
  93. package/dist/macros/index.js +167 -73
  94. package/dist/macros/index.js.map +1 -1
  95. package/dist/macros/percentage/index.d.ts +29 -0
  96. package/dist/macros/percentage/index.js +36 -0
  97. package/dist/macros/percentage/index.js.map +1 -0
  98. package/dist/macros/percentage/metadata.d.ts +15 -0
  99. package/dist/macros/percentage/metadata.js +19 -0
  100. package/dist/macros/percentage/metadata.js.map +1 -0
  101. package/dist/macros/report/index.d.ts +2 -5
  102. package/dist/macros/report/index.js +20 -12
  103. package/dist/macros/report/index.js.map +1 -1
  104. package/dist/macros/scoreCard/index.d.ts +15 -15
  105. package/dist/macros/scoreCard/index.js +16 -17
  106. package/dist/macros/scoreCard/index.js.map +1 -1
  107. package/dist/macros/vega/index.d.ts +28 -0
  108. package/dist/macros/vega/index.js +27 -0
  109. package/dist/macros/vega/index.js.map +1 -0
  110. package/dist/macros/vega/metadata.d.ts +15 -0
  111. package/dist/macros/vega/metadata.js +7 -0
  112. package/dist/macros/vega/metadata.js.map +1 -0
  113. package/dist/macros/vegalite/index.d.ts +27 -0
  114. package/dist/macros/vegalite/index.js +27 -0
  115. package/dist/macros/vegalite/index.js.map +1 -0
  116. package/dist/macros/vegalite/metadata.d.ts +15 -0
  117. package/dist/macros/vegalite/metadata.js +7 -0
  118. package/dist/macros/vegalite/metadata.js.map +1 -0
  119. package/dist/macros/xref/index.d.ts +26 -0
  120. package/dist/macros/xref/index.js +53 -0
  121. package/dist/macros/xref/index.js.map +1 -0
  122. package/dist/macros/xref/metadata.d.ts +15 -0
  123. package/dist/macros/xref/metadata.js +19 -0
  124. package/dist/macros/xref/metadata.js.map +1 -0
  125. package/dist/module-manager.d.ts +17 -4
  126. package/dist/module-manager.js +192 -58
  127. package/dist/module-manager.js.map +1 -1
  128. package/dist/permissions/action-guard.d.ts +2 -2
  129. package/dist/permissions/action-guard.js +1 -1
  130. package/dist/permissions/action-guard.js.map +1 -1
  131. package/dist/project-settings.js +2 -8
  132. package/dist/project-settings.js.map +1 -1
  133. package/dist/resources/card-type-resource.d.ts +2 -0
  134. package/dist/resources/card-type-resource.js +63 -0
  135. package/dist/resources/card-type-resource.js.map +1 -1
  136. package/dist/resources/file-resource.d.ts +2 -0
  137. package/dist/resources/file-resource.js +12 -4
  138. package/dist/resources/file-resource.js.map +1 -1
  139. package/dist/resources/folder-resource.d.ts +19 -0
  140. package/dist/resources/folder-resource.js +51 -2
  141. package/dist/resources/folder-resource.js.map +1 -1
  142. package/dist/resources/graph-model-resource.js +1 -1
  143. package/dist/resources/graph-model-resource.js.map +1 -1
  144. package/dist/resources/graph-view-resource.js +1 -1
  145. package/dist/resources/graph-view-resource.js.map +1 -1
  146. package/dist/resources/report-resource.js +1 -1
  147. package/dist/resources/report-resource.js.map +1 -1
  148. package/dist/resources/resource-object.d.ts +8 -0
  149. package/dist/resources/resource-object.js +9 -0
  150. package/dist/resources/resource-object.js.map +1 -1
  151. package/dist/resources/template-resource.js +1 -1
  152. package/dist/resources/template-resource.js.map +1 -1
  153. package/dist/resources/workflow-resource.d.ts +2 -0
  154. package/dist/resources/workflow-resource.js +53 -9
  155. package/dist/resources/workflow-resource.js.map +1 -1
  156. package/dist/svg/index.d.ts +15 -0
  157. package/dist/svg/index.js +16 -0
  158. package/dist/svg/index.js.map +1 -0
  159. package/dist/svg/lib.d.ts +9 -0
  160. package/dist/svg/lib.js +26 -0
  161. package/dist/svg/lib.js.map +1 -0
  162. package/dist/svg/percentage.d.ts +25 -0
  163. package/dist/svg/percentage.js +90 -0
  164. package/dist/svg/percentage.js.map +1 -0
  165. package/dist/svg/scoreCard.d.ts +19 -0
  166. package/dist/svg/scoreCard.js +55 -0
  167. package/dist/svg/scoreCard.js.map +1 -0
  168. package/dist/types/queries.d.ts +8 -7
  169. package/dist/types/queries.js.map +1 -1
  170. package/dist/utils/card-utils.d.ts +6 -0
  171. package/dist/utils/card-utils.js +12 -0
  172. package/dist/utils/card-utils.js.map +1 -1
  173. package/dist/utils/clingo-facts.d.ts +2 -1
  174. package/dist/utils/clingo-facts.js +19 -2
  175. package/dist/utils/clingo-facts.js.map +1 -1
  176. package/dist/utils/clingo-parser.d.ts +1 -0
  177. package/dist/utils/clingo-parser.js +22 -100
  178. package/dist/utils/clingo-parser.js.map +1 -1
  179. package/dist/utils/constants.d.ts +7 -0
  180. package/dist/utils/constants.js +14 -0
  181. package/dist/utils/constants.js.map +1 -1
  182. package/dist/utils/csv.js.map +1 -1
  183. package/dist/utils/report.d.ts +17 -3
  184. package/dist/utils/report.js +38 -2
  185. package/dist/utils/report.js.map +1 -1
  186. package/dist/utils/resource-utils.d.ts +1 -0
  187. package/dist/utils/resource-utils.js +9 -0
  188. package/dist/utils/resource-utils.js.map +1 -1
  189. package/dist/utils/user-preferences.d.ts +1 -11
  190. package/dist/utils/user-preferences.js +30 -13
  191. package/dist/utils/user-preferences.js.map +1 -1
  192. package/dist/utils/validate.d.ts +2 -3
  193. package/dist/utils/validate.js +2 -2
  194. package/dist/utils/validate.js.map +1 -1
  195. package/package.json +8 -6
  196. package/src/command-handler.ts +96 -17
  197. package/src/command-manager.ts +8 -8
  198. package/src/commands/calculate.ts +11 -525
  199. package/src/commands/create.ts +53 -16
  200. package/src/commands/edit.ts +53 -11
  201. package/src/commands/export.ts +108 -34
  202. package/src/commands/import.ts +31 -2
  203. package/src/commands/move.ts +12 -15
  204. package/src/commands/remove.ts +10 -19
  205. package/src/commands/rename.ts +3 -12
  206. package/src/commands/show.ts +121 -8
  207. package/src/commands/transition.ts +3 -7
  208. package/src/commands/update.ts +6 -0
  209. package/src/commands/validate.ts +39 -47
  210. package/src/containers/card-container.ts +74 -0
  211. package/src/containers/project/calculation-engine.ts +535 -0
  212. package/src/containers/project/resource-collector.ts +45 -26
  213. package/src/containers/project.ts +66 -84
  214. package/src/containers/template.ts +16 -0
  215. package/src/exceptions/index.ts +36 -0
  216. package/src/index.ts +13 -2
  217. package/src/interfaces/macros.ts +7 -1
  218. package/src/interfaces/project-interfaces.ts +27 -0
  219. package/src/interfaces/resource-interfaces.ts +1 -0
  220. package/src/macros/base-macro.ts +89 -25
  221. package/src/macros/common.ts +22 -9
  222. package/src/macros/createCards/index.ts +1 -2
  223. package/src/macros/graph/index.ts +17 -12
  224. package/src/macros/image/index.ts +121 -0
  225. package/src/macros/image/metadata.ts +25 -0
  226. package/src/macros/include/index.ts +147 -0
  227. package/src/macros/include/metadata.ts +22 -0
  228. package/src/macros/index.ts +179 -100
  229. package/src/macros/percentage/index.ts +54 -0
  230. package/src/macros/percentage/metadata.ts +22 -0
  231. package/src/macros/report/index.ts +22 -17
  232. package/src/macros/scoreCard/index.ts +23 -23
  233. package/src/macros/vega/index.ts +55 -0
  234. package/src/macros/vega/metadata.ts +21 -0
  235. package/src/macros/vegalite/index.ts +50 -0
  236. package/src/macros/vegalite/metadata.ts +21 -0
  237. package/src/macros/xref/index.ts +73 -0
  238. package/src/macros/xref/metadata.ts +22 -0
  239. package/src/module-manager.ts +241 -69
  240. package/src/permissions/action-guard.ts +3 -3
  241. package/src/project-settings.ts +2 -11
  242. package/src/resources/card-type-resource.ts +100 -0
  243. package/src/resources/file-resource.ts +16 -4
  244. package/src/resources/folder-resource.ts +59 -2
  245. package/src/resources/graph-model-resource.ts +1 -1
  246. package/src/resources/graph-view-resource.ts +1 -1
  247. package/src/resources/report-resource.ts +1 -1
  248. package/src/resources/resource-object.ts +14 -0
  249. package/src/resources/template-resource.ts +1 -1
  250. package/src/resources/workflow-resource.ts +68 -13
  251. package/src/svg/index.ts +15 -0
  252. package/src/svg/lib.ts +31 -0
  253. package/src/svg/percentage.ts +97 -0
  254. package/src/svg/scoreCard.ts +88 -0
  255. package/src/types/queries.ts +8 -7
  256. package/src/types/string-pixel-width.d.ts +23 -0
  257. package/src/utils/card-utils.ts +13 -0
  258. package/src/utils/clingo-facts.ts +65 -3
  259. package/src/utils/clingo-parser.ts +31 -144
  260. package/src/utils/constants.ts +16 -0
  261. package/src/utils/csv.ts +1 -1
  262. package/src/utils/report.ts +45 -4
  263. package/src/utils/resource-utils.ts +9 -0
  264. package/src/utils/user-preferences.ts +32 -14
  265. package/src/utils/validate.ts +3 -3
@@ -1,26 +1,32 @@
1
1
  /**
2
- Cyberismo
3
- Copyright © Cyberismo Ltd and contributors 2024
4
-
5
- This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License version 3 as published by the Free Software Foundation.
6
-
7
- This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
8
-
9
- You should have received a copy of the GNU Affero General Public
10
- License along with this program. If not, see <https://www.gnu.org/licenses/>.
2
+ Cyberismo
3
+ Copyright © Cyberismo Ltd and contributors 2024
4
+ This program is free software: you can redistribute it and/or modify it under
5
+ the terms of the GNU Affero General Public License version 3 as published by
6
+ the Free Software Foundation.
7
+ This program is distributed in the hope that it will be useful, but WITHOUT
8
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9
+ FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
10
+ details. You should have received a copy of the GNU Affero General Public
11
+ License along with this program. If not, see <https://www.gnu.org/licenses/>.
11
12
  */
12
13
 
13
14
  import Handlebars from 'handlebars';
14
15
 
15
16
  import createCards from './createCards/index.js';
16
17
  import graph from './graph/index.js';
18
+ import image from './image/index.js';
19
+ import include from './include/index.js';
17
20
  import report from './report/index.js';
18
21
  import scoreCard from './scoreCard/index.js';
22
+ import xref from './xref/index.js';
23
+ import percentage from './percentage/index.js';
24
+ import vega from './vega/index.js';
25
+ import vegaLite from './vegalite/index.js';
19
26
 
20
27
  import { validateJson } from '../utils/validate.js';
21
- import { DHValidationError } from '../exceptions/index.js';
28
+ import { DHValidationError, MacroError } from '../exceptions/index.js';
22
29
  import type { AdmonitionType } from '../interfaces/adoc.js';
23
- import type { Validator } from 'jsonschema';
24
30
  import type {
25
31
  MacroGenerationContext,
26
32
  MacroMetadata,
@@ -28,9 +34,86 @@ import type {
28
34
  } from '../interfaces/macros.js';
29
35
  import type BaseMacro from './base-macro.js';
30
36
  import TaskQueue from './task-queue.js';
31
- import type { Calculate } from '../commands/index.js';
37
+ import { ClingoError } from '@cyberismo/node-clingo';
38
+ import type { Schema } from 'jsonschema';
32
39
  const CURLY_LEFT = '&#123;';
33
40
  const CURLY_RIGHT = '&#125;';
41
+ const RAW_BLOCK_OPEN = '{{#raw}}';
42
+ const RAW_BLOCK_CLOSE = '{{/raw}}';
43
+
44
+ /**
45
+ * Pre-processes the content to handle {{#raw}} blocks by escaping all handlebars syntax inside them
46
+ * @param content The template content to process
47
+ * @returns The processed content with raw blocks escaped
48
+ * @throws Error if nested raw blocks are found or if a raw block is not properly closed
49
+ */
50
+ function preprocessRawBlocks(content: string): string {
51
+ const result: string[] = [];
52
+ let i = 0;
53
+
54
+ // Helper function to check if a target string matches at a given position without creating substrings
55
+ const matchesAt = (pos: number, target: string): boolean => {
56
+ if (pos + target.length > content.length) return false;
57
+ for (let k = 0; k < target.length; k++) {
58
+ if (content[pos + k] !== target[k]) return false;
59
+ }
60
+ return true;
61
+ };
62
+
63
+ // Helper function to get line number at position
64
+ const getLineNumber = (pos: number): number => {
65
+ let lineNum = 1;
66
+ for (let k = 0; k < pos; k++) {
67
+ if (content[k] === '\n') {
68
+ lineNum++;
69
+ }
70
+ }
71
+ return lineNum;
72
+ };
73
+
74
+ while (i < content.length) {
75
+ // Check for {{#raw}}
76
+ if (matchesAt(i, RAW_BLOCK_OPEN)) {
77
+ const openingLine = getLineNumber(i);
78
+ // Find the matching {{/raw}} - no nesting allowed
79
+ let j = i + RAW_BLOCK_OPEN.length;
80
+
81
+ while (j < content.length) {
82
+ if (matchesAt(j, RAW_BLOCK_OPEN)) {
83
+ // Found nested raw block - not supported
84
+ const nestedLine = getLineNumber(j);
85
+ throw new Error(
86
+ `Nested ${RAW_BLOCK_OPEN} blocks are not supported. Found nested raw block inside another raw block on line ${nestedLine} (original raw block started on line ${openingLine}).`,
87
+ );
88
+ } else if (matchesAt(j, RAW_BLOCK_CLOSE)) {
89
+ // Found matching closing tag
90
+ const rawContent = content.slice(i + RAW_BLOCK_OPEN.length, j);
91
+ const escapedContent = rawContent
92
+ .replaceAll('{', CURLY_LEFT)
93
+ .replaceAll('}', CURLY_RIGHT);
94
+ result.push(escapedContent);
95
+ i = j + RAW_BLOCK_CLOSE.length;
96
+ break;
97
+ } else {
98
+ j++;
99
+ }
100
+ }
101
+
102
+ // If we reached the end without finding a closing tag
103
+ if (j >= content.length) {
104
+ throw new Error(
105
+ `Unclosed ${RAW_BLOCK_OPEN} block found on line ${openingLine}. Every ${RAW_BLOCK_OPEN} must have a matching ${RAW_BLOCK_CLOSE}.`,
106
+ );
107
+ }
108
+ } else {
109
+ // Not a raw block, keep as-is
110
+ result.push(content[i]);
111
+ i++;
112
+ }
113
+ }
114
+
115
+ return result.join('');
116
+ }
34
117
 
35
118
  /**
36
119
  * Constructor for all macros except report macros
@@ -43,7 +126,7 @@ export interface SimpleMacroConstructor {
43
126
  * Constructor for report macros
44
127
  */
45
128
  export interface ReportMacroConstructor {
46
- new (tasks: TaskQueue, calculate: Calculate): BaseMacro;
129
+ new (tasks: TaskQueue): BaseMacro;
47
130
  }
48
131
 
49
132
  /**
@@ -56,21 +139,26 @@ export const macros: {
56
139
  } = {
57
140
  createCards,
58
141
  graph,
142
+ image,
143
+ include,
59
144
  report,
60
145
  scoreCard,
146
+ xref,
147
+ percentage,
148
+ vega,
149
+ vegaLite,
61
150
  };
62
151
 
63
152
  /**
64
153
  * Validates the content inside a macro
65
154
  * @param macro - The macro to validate the content of
66
155
  * @param data - The data to validate
67
- * @param validator - The validator to use
68
156
  * @returns The validated data
69
157
  */
70
158
  export function validateMacroContent<T>(
71
159
  macro: MacroMetadata,
72
160
  data: unknown,
73
- validator?: Validator,
161
+ schema?: Schema,
74
162
  ): T {
75
163
  if (!macro.schema) {
76
164
  throw new Error(`Macro ${macro.name} does not have a schema`);
@@ -79,7 +167,7 @@ export function validateMacroContent<T>(
79
167
  try {
80
168
  return validateJson<T>(data, {
81
169
  schemaId: macro.schema,
82
- validator,
170
+ schema,
83
171
  });
84
172
  } catch (error) {
85
173
  let message = `Error: ${error instanceof Error ? error.message : 'Unknown error'}`;
@@ -98,33 +186,17 @@ export function registerMacros(
98
186
  instance: typeof Handlebars,
99
187
  context: MacroGenerationContext,
100
188
  tasks: TaskQueue,
101
- calculate: Calculate,
102
189
  ) {
103
190
  const macroInstances: BaseMacro[] = [];
104
191
  for (const macro of Object.keys(macros) as MacroName[]) {
105
192
  const MacroClass = macros[macro];
106
- const macroInstance = new MacroClass(tasks, calculate);
193
+ const macroInstance = new MacroClass(tasks);
107
194
  instance.registerHelper(macro, function (this: unknown, options) {
108
- if (
109
- this != null &&
110
- typeof this === 'object' &&
111
- '__isRaw' in this &&
112
- this.__isRaw
113
- ) {
114
- // we use escaped chars so that they will not be re-run
115
- return `${CURLY_LEFT}${CURLY_LEFT}#${macro}${CURLY_RIGHT}${CURLY_RIGHT}${options.fn(this)}${CURLY_LEFT}${CURLY_LEFT}/${macro}${CURLY_RIGHT}${CURLY_RIGHT}`;
116
- }
117
195
  return macroInstance.invokeMacro(context, options);
118
196
  });
119
197
  macroInstances.push(macroInstance);
120
198
  }
121
199
 
122
- instance.registerHelper('raw', function (options) {
123
- return options.fn({
124
- __isRaw: true,
125
- });
126
- });
127
-
128
200
  return macroInstances;
129
201
  }
130
202
  /**
@@ -154,25 +226,26 @@ export function registerEmptyMacros(instance: typeof Handlebars) {
154
226
  /**
155
227
  * Handle the macros in the content
156
228
  * @param content - The content to handle the macros in
157
- * @param mode - The mode to handle the macros in. Inject mode will generate injectable placeholders for the macros while static mode will generate valid adoc. Validate mode will only validate the macros syntax and throw errors in case of issues.
229
+ * @param context - The context for macro generation
230
+ * @param calculate - The calculate function
231
+ * @param preserveRawBlocks - If true, don't unescape raw blocks at the end (for nested evaluations)
158
232
  */
159
233
  export async function evaluateMacros(
160
234
  content: string,
161
235
  context: MacroGenerationContext,
162
- calculate: Calculate,
163
- maxTries: number = 10,
236
+ preserveRawBlocks: boolean = false,
164
237
  ) {
165
238
  const handlebars = Handlebars.create();
166
239
  const tasks = new TaskQueue();
167
- registerMacros(handlebars, context, tasks, calculate);
240
+ registerMacros(handlebars, context, tasks);
168
241
  let result = content;
169
- while (maxTries-- > 0) {
242
+ while ((context.maxTries ?? 10) > 0) {
170
243
  tasks.reset();
171
- const compiled = handlebars.compile(result, {
172
- strict: true,
173
- });
174
244
  try {
175
- result = compiled({});
245
+ const compiled = handlebars.compile(preprocessRawBlocks(result), {
246
+ strict: true,
247
+ });
248
+ result = compiled({ cardKey: context.cardKey });
176
249
 
177
250
  await tasks.waitAll();
178
251
 
@@ -193,7 +266,10 @@ export async function evaluateMacros(
193
266
  context,
194
267
  );
195
268
  }
196
- return result.replaceAll(CURLY_LEFT, '{').replaceAll(CURLY_RIGHT, '}');
269
+ // Only unescape raw blocks if we're not preserving them for nested evaluations
270
+ return preserveRawBlocks
271
+ ? result
272
+ : result.replaceAll(CURLY_LEFT, '{').replaceAll(CURLY_RIGHT, '}');
197
273
  }
198
274
 
199
275
  /**
@@ -211,7 +287,14 @@ export function applyMacroResults(
211
287
  context: MacroGenerationContext,
212
288
  ) {
213
289
  for (const item of tasks) {
214
- if (item.promiseResult === null) {
290
+ if (item.error) {
291
+ input = input.replace(
292
+ item.placeholder,
293
+ handleMacroError(item.error, item.macro, context),
294
+ );
295
+ } // It should not be possible that promiseResult is null if there never was an error
296
+ // Unless the function itself returns null / undefined
297
+ else if (item.promiseResult == null) {
215
298
  input = handleMacroError(
216
299
  new Error(
217
300
  `Tried to access result before it was resolved for ${item.placeholder}`,
@@ -240,7 +323,17 @@ export function handleMacroError(
240
323
  let message = `Error: ${error instanceof Error ? error.message : 'Unknown error'}`;
241
324
  if (error instanceof DHValidationError) {
242
325
  message = `Check json syntax of macro ${macro}: ${error.errors?.map((e) => e.message).join(', ')}`;
326
+ } else if (error instanceof MacroError) {
327
+ const { cardKey, macroName, dependency } = error.context;
328
+ message = `Macro error in card '${cardKey}' in macro '${macroName}':\n\n${error.message}.`;
329
+
330
+ if (dependency) {
331
+ message += `\n\nParameters:\n\n${context.mode === 'validate' ? dependency.parameters : createCodeBlock(dependency.parameters)}.\n\n${dependency.output ? `Output:\n\n${context.mode === 'validate' ? dependency.output : createCodeBlock(dependency.output)}` : ''}`;
332
+ }
333
+ } else if (error instanceof ClingoError) {
334
+ message = `Error running logic program in macro '${macro}':${error.details.errors.join('\n')}`;
243
335
  }
336
+
244
337
  if (
245
338
  typeof error === 'object' &&
246
339
  error != null &&
@@ -260,57 +353,19 @@ export function handleMacroError(
260
353
  // There might be a better way to do this
261
354
  let macroCounter = 0;
262
355
 
263
- type Value = string | number | boolean | undefined;
264
-
265
- /**
266
- * Macro options can be a flat object or a nested object
267
- * The nested object will be flattened into dot notation attributes
268
- */
269
- export type MacroOptions = {
270
- [key: string]: Value | MacroOptions;
271
- };
356
+ function objectToBase64(obj: unknown): string {
357
+ return Buffer.from(JSON.stringify(obj), 'utf-8').toString('base64');
358
+ }
272
359
  /**
273
- * Creates an injectable placeholder for a macro
274
- * @param macro - The macro to create the placeholder for
275
- * @param options - Options will be passed to the html element as attributes
360
+ * Creates a placeholder for a macro
361
+ * Options are encoded as base64
362
+ * @param macro - The macro to create a placeholder for
363
+ * @param options - The options for the macro
364
+ * @returns The placeholder for the macro
276
365
  */
277
- export function createHtmlPlaceholder(
278
- macro: MacroMetadata,
279
- options: MacroOptions,
280
- ) {
281
- // Flatten nested objects into dot notation attributes
282
- const flattenedOptions: Record<string, Value> = {};
283
-
284
- // Helper function to flatten nested objects
285
- const flatten = (obj: MacroOptions, prefix = ''): void => {
286
- Object.entries(obj).forEach(([key, value]) => {
287
- const newKey = prefix ? `${prefix}.${key}` : key;
288
-
289
- if (
290
- value !== null &&
291
- typeof value === 'object' &&
292
- !Array.isArray(value)
293
- ) {
294
- // Recursively flatten nested objects
295
- flatten(value, newKey);
296
- } else {
297
- // Add leaf values to flattened options
298
- flattenedOptions[newKey] = value as Value;
299
- }
300
- });
301
- };
302
-
303
- flatten(options);
304
-
305
- // Convert flattened options to attribute strings
306
- const attributeStrings = Object.entries(flattenedOptions)
307
- .filter(([, value]) => value !== undefined)
308
- .map(([key, value]) => `${key}="${value}"`);
309
-
310
- const optionString = attributeStrings.join(' ');
311
-
312
- // start with a line change to ensure that inline passthrough +++ is on its own line
313
- return `\n+++\n<${macro.tagName}${optionString ? ` ${optionString}` : ''} key="macro-${macroCounter++}"></${macro.tagName}>\n+++\n`;
366
+ export function createHtmlPlaceholder(macro: MacroMetadata, options: unknown) {
367
+ const optionsBase64 = objectToBase64(options);
368
+ return `\n\n++++\n<${macro.tagName} options="${optionsBase64}" key="macro-${macroCounter++}"></${macro.tagName}>\n++++\n\n`;
314
369
  }
315
370
 
316
371
  /**
@@ -328,20 +383,44 @@ export function createAdmonition(
328
383
  return `[${type}]\n.${label}\n====\n${content}\n====\n\n`;
329
384
  }
330
385
 
386
+ /**
387
+ * Creates a code block
388
+ * @param content - The content of the code block
389
+ * @returns The code block as a string
390
+ */
391
+ export function createCodeBlock(content: string) {
392
+ return `\n\n----\n${content}\n----\n\n`;
393
+ }
394
+
331
395
  /**
332
396
  * Helper function for including base64 encoded images for now
333
397
  * @param image base64 encoded image
334
398
  * @returns valid asciidoc with the image
335
399
  */
336
- export function createImage(image: string) {
400
+ export function createImage(image: string, controls: boolean = true) {
337
401
  if (process.env.EXPORT_FORMAT) {
338
402
  return `image::data:image/svg+xml;base64,${image}[]\n`;
339
403
  } else {
340
- return `++++
341
- <div class="cyberismo-svg-wrapper" data-type="cyberismo-svg-wrapper">
342
- ${Buffer.from(image, 'base64').toString('utf-8')}
343
- </div>
344
- ++++
345
- `;
404
+ const svg = Buffer.from(image, 'base64').toString('utf-8');
405
+ if (controls) {
406
+ return `++++\n<div class="cyberismo-svg-wrapper" data-type="cyberismo-svg-wrapper">\n${svg}\n</div>\n++++\n`;
407
+ } else {
408
+ return `++++\n${svg}\n++++\n`;
409
+ }
410
+ }
411
+ }
412
+
413
+ /**
414
+ * Creates a Handlebars macro block string with the given macro name and options.
415
+ *
416
+ * @param macro - The name of the macro to create (e.g., 'scoreCard', 'include').
417
+ * @param options - The options object to be stringified and inserted as macro content.
418
+ * @returns The Handlebars macro block as a string, e.g. {{#macro}}...{{/macro}}
419
+ */
420
+ export function createMacro(macro: MacroName, options: unknown) {
421
+ let optionsString = JSON.stringify(options, null, 0);
422
+ if (optionsString.length > 1) {
423
+ optionsString = optionsString.slice(1, -1);
346
424
  }
425
+ return `{{#${macro}}}${optionsString}{{/${macro}}}`;
347
426
  }
@@ -0,0 +1,54 @@
1
+ /**
2
+ Cyberismo
3
+ Copyright © Cyberismo Ltd and contributors 2025
4
+ This program is free software: you can redistribute it and/or modify it under
5
+ the terms of the GNU Affero General Public License version 3 as published by
6
+ the Free Software Foundation. This program is distributed in the hope that it
7
+ will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
8
+ of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
9
+ See the GNU Affero General Public License for more details.
10
+ You should have received a copy of the GNU Affero General Public
11
+ License along with this program. If not, see <https://www.gnu.org/licenses/>.
12
+ */
13
+
14
+ import { createImage, validateMacroContent } from '../index.js';
15
+
16
+ import type { MacroGenerationContext } from '../../interfaces/macros.js';
17
+ import macroMetadata from './metadata.js';
18
+ import BaseMacro from '../base-macro.js';
19
+ import type TaskQueue from '../task-queue.js';
20
+ import { percentage } from '../../svg/index.js';
21
+
22
+ export interface PercentageOptions {
23
+ title: string;
24
+ value: number;
25
+ legend: string;
26
+ colour?: 'blue' | 'green' | 'yellow' | 'red' | 'orange' | 'purple';
27
+ }
28
+
29
+ class PercentageMacro extends BaseMacro {
30
+ constructor(tasksQueue: TaskQueue) {
31
+ super(macroMetadata, tasksQueue);
32
+ }
33
+ handleValidate = (data: string) => {
34
+ this.validate(data);
35
+ };
36
+
37
+ handleStatic = async (_: MacroGenerationContext, input: unknown) => {
38
+ const options = this.validate(input);
39
+ return createImage(
40
+ Buffer.from(percentage(options)).toString('base64'),
41
+ false,
42
+ );
43
+ };
44
+
45
+ handleInject = async (context: MacroGenerationContext, input: unknown) => {
46
+ return this.handleStatic(context, input);
47
+ };
48
+
49
+ private validate(input: unknown): PercentageOptions {
50
+ return validateMacroContent<PercentageOptions>(this.metadata, input);
51
+ }
52
+ }
53
+
54
+ export default PercentageMacro;
@@ -0,0 +1,22 @@
1
+ /**
2
+ Cyberismo
3
+ Copyright © Cyberismo Ltd and contributors 2025
4
+ This program is free software: you can redistribute it and/or modify it under
5
+ the terms of the GNU Affero General Public License version 3 as published by
6
+ the Free Software Foundation. This program is distributed in the hope that it
7
+ will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
8
+ of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
9
+ See the GNU Affero General Public License for more details.
10
+ You should have received a copy of the GNU Affero General Public
11
+ License along with this program. If not, see <https://www.gnu.org/licenses/>.
12
+ */
13
+
14
+ import type { MacroMetadata } from '../../interfaces/macros.js';
15
+
16
+ const macroMetadata: MacroMetadata = {
17
+ name: 'percentage',
18
+ tagName: 'percentage',
19
+ schema: 'percentageMacroSchema',
20
+ };
21
+
22
+ export default macroMetadata;
@@ -12,27 +12,23 @@
12
12
  */
13
13
 
14
14
  import { validateMacroContent } from '../index.js';
15
- import type { MacroOptions } from '../index.js';
16
15
 
17
16
  import type { MacroGenerationContext } from '../../interfaces/macros.js';
18
17
  import macroMetadata from './metadata.js';
19
- import type { Calculate } from '../../commands/index.js';
20
18
  import BaseMacro from '../base-macro.js';
21
19
  import { validateJson } from '../../utils/validate.js';
22
20
  import type TaskQueue from '../task-queue.js';
23
21
  import { ReportResource } from '../../resources/report-resource.js';
24
22
  import { resourceName } from '../../utils/resource-utils.js';
25
23
  import { generateReportContent } from '../../utils/report.js';
24
+ import { ClingoError } from '@cyberismo/node-clingo';
26
25
 
27
- export interface ReportOptions extends MacroOptions {
26
+ export interface ReportOptions {
28
27
  name: string;
29
28
  }
30
29
 
31
30
  class ReportMacro extends BaseMacro {
32
- constructor(
33
- tasks: TaskQueue,
34
- private readonly calculate: Calculate,
35
- ) {
31
+ constructor(tasks: TaskQueue) {
36
32
  super(macroMetadata, tasks);
37
33
  }
38
34
  handleValidate = (input: unknown) => {
@@ -58,16 +54,25 @@ class ReportMacro extends BaseMacro {
58
54
  schema: report.schema,
59
55
  });
60
56
  }
61
-
62
- return generateReportContent({
63
- calculate: this.calculate,
64
- contentTemplate: report.contentTemplate,
65
- queryTemplate: report.queryTemplate,
66
- options: {
67
- cardKey: context.cardKey,
68
- ...options,
69
- },
70
- });
57
+ try {
58
+ return await generateReportContent({
59
+ calculate: context.project.calculationEngine,
60
+ contentTemplate: report.contentTemplate,
61
+ queryTemplate: report.queryTemplate,
62
+ options: {
63
+ cardKey: context.cardKey,
64
+ ...options,
65
+ },
66
+ context: context.context,
67
+ });
68
+ } catch (error) {
69
+ if (error instanceof ClingoError) {
70
+ throw new Error(
71
+ `Error running logic program in report '${options.name}':${error.details.errors.join('\n')}`,
72
+ );
73
+ }
74
+ throw error;
75
+ }
71
76
  };
72
77
 
73
78
  private validate(data: unknown): ReportOptions {
@@ -1,28 +1,30 @@
1
1
  /**
2
- Cyberismo
3
- Copyright © Cyberismo Ltd and contributors 2024
4
-
5
- This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License version 3 as published by the Free Software Foundation.
6
-
7
- This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
8
-
9
- You should have received a copy of the GNU Affero General Public
10
- License along with this program. If not, see <https://www.gnu.org/licenses/>.
2
+ Cyberismo
3
+ Copyright © Cyberismo Ltd and contributors 2024
4
+
5
+ This program is free software: you can redistribute it and/or modify it under
6
+ the terms of the GNU Affero General Public License version 3 as published by
7
+ the Free Software Foundation. This program is distributed in the hope that it
8
+ will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
9
+ of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10
+ See the GNU Affero General Public License for more details.
11
+ You should have received a copy of the GNU Affero General Public
12
+ License along with this program. If not, see <https://www.gnu.org/licenses/>.
11
13
  */
12
14
 
13
- import type { MacroOptions } from '../index.js';
14
- import { createHtmlPlaceholder, validateMacroContent } from '../index.js';
15
+ import { createImage, validateMacroContent } from '../index.js';
15
16
 
16
17
  import type { MacroGenerationContext } from '../../interfaces/macros.js';
17
18
  import macroMetadata from './metadata.js';
18
19
  import BaseMacro from '../base-macro.js';
19
20
  import type TaskQueue from '../task-queue.js';
21
+ import { scoreCard } from '../../svg/index.js';
20
22
 
21
- export interface ScoreCardOptions extends MacroOptions {
22
- title?: string;
23
+ export interface ScoreCardOptions {
23
24
  value: number;
24
- unit?: string;
25
25
  legend?: string;
26
+ title?: string;
27
+ unit?: string;
26
28
  }
27
29
 
28
30
  class ScoreCardMacro extends BaseMacro {
@@ -33,23 +35,21 @@ class ScoreCardMacro extends BaseMacro {
33
35
  this.validate(data);
34
36
  };
35
37
 
36
- handleStatic = async (context: MacroGenerationContext, input: unknown) => {
38
+ handleStatic = async (_: MacroGenerationContext, input: unknown) => {
37
39
  const options = this.validate(input);
38
- return this.createAsciidocElement(options);
40
+ return createImage(
41
+ Buffer.from(scoreCard(options)).toString('base64'),
42
+ false,
43
+ );
39
44
  };
40
45
 
41
- handleInject = async (_: MacroGenerationContext, input: unknown) => {
42
- const options = this.validate(input);
43
- return createHtmlPlaceholder(this.metadata, options);
46
+ handleInject = async (context: MacroGenerationContext, input: unknown) => {
47
+ return this.handleStatic(context, input);
44
48
  };
45
49
 
46
50
  private validate(input: unknown): ScoreCardOptions {
47
51
  return validateMacroContent<ScoreCardOptions>(this.metadata, input);
48
52
  }
49
-
50
- private createAsciidocElement(options: ScoreCardOptions) {
51
- return `\n----\n${options.title}: ${options.value} ${options.unit ?? ''} ${options.legend ?? ''}\n----\n`;
52
- }
53
53
  }
54
54
 
55
55
  export default ScoreCardMacro;