@cyberismo/data-handler 0.0.14 → 0.0.15

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 (240) hide show
  1. package/dist/card-metadata-updater.js +1 -3
  2. package/dist/card-metadata-updater.js.map +1 -1
  3. package/dist/command-handler.js +10 -16
  4. package/dist/command-handler.js.map +1 -1
  5. package/dist/command-manager.d.ts +1 -1
  6. package/dist/command-manager.js +4 -3
  7. package/dist/command-manager.js.map +1 -1
  8. package/dist/commands/create.js +13 -59
  9. package/dist/commands/create.js.map +1 -1
  10. package/dist/commands/edit.d.ts +1 -15
  11. package/dist/commands/edit.js +15 -89
  12. package/dist/commands/edit.js.map +1 -1
  13. package/dist/commands/export.js +4 -17
  14. package/dist/commands/export.js.map +1 -1
  15. package/dist/commands/import.js +3 -5
  16. package/dist/commands/import.js.map +1 -1
  17. package/dist/commands/move.d.ts +1 -2
  18. package/dist/commands/move.js +108 -146
  19. package/dist/commands/move.js.map +1 -1
  20. package/dist/commands/remove.js +9 -44
  21. package/dist/commands/remove.js.map +1 -1
  22. package/dist/commands/rename.js +2 -7
  23. package/dist/commands/rename.js.map +1 -1
  24. package/dist/commands/show.d.ts +7 -25
  25. package/dist/commands/show.js +38 -102
  26. package/dist/commands/show.js.map +1 -1
  27. package/dist/commands/transition.js +27 -30
  28. package/dist/commands/transition.js.map +1 -1
  29. package/dist/commands/update.d.ts +5 -3
  30. package/dist/commands/update.js +19 -5
  31. package/dist/commands/update.js.map +1 -1
  32. package/dist/commands/validate.d.ts +3 -3
  33. package/dist/commands/validate.js +19 -26
  34. package/dist/commands/validate.js.map +1 -1
  35. package/dist/containers/card-container.d.ts +87 -24
  36. package/dist/containers/card-container.js +183 -279
  37. package/dist/containers/card-container.js.map +1 -1
  38. package/dist/containers/project/calculation-engine.d.ts +6 -0
  39. package/dist/containers/project/calculation-engine.js +19 -12
  40. package/dist/containers/project/calculation-engine.js.map +1 -1
  41. package/dist/containers/project/card-cache.d.ts +146 -0
  42. package/dist/containers/project/card-cache.js +411 -0
  43. package/dist/containers/project/card-cache.js.map +1 -0
  44. package/dist/containers/project/resource-collector.d.ts +24 -1
  45. package/dist/containers/project/resource-collector.js +8 -1
  46. package/dist/containers/project/resource-collector.js.map +1 -1
  47. package/dist/containers/project.d.ts +117 -83
  48. package/dist/containers/project.js +418 -252
  49. package/dist/containers/project.js.map +1 -1
  50. package/dist/containers/template.d.ts +15 -31
  51. package/dist/containers/template.js +97 -104
  52. package/dist/containers/template.js.map +1 -1
  53. package/dist/index.d.ts +1 -0
  54. package/dist/index.js +1 -0
  55. package/dist/index.js.map +1 -1
  56. package/dist/interfaces/folder-content-interfaces.d.ts +2 -1
  57. package/dist/interfaces/folder-content-interfaces.js.map +1 -1
  58. package/dist/interfaces/macros.d.ts +1 -0
  59. package/dist/interfaces/macros.js +1 -1
  60. package/dist/interfaces/macros.js.map +1 -1
  61. package/dist/interfaces/project-interfaces.d.ts +5 -1
  62. package/dist/interfaces/project-interfaces.js.map +1 -1
  63. package/dist/interfaces/resource-interfaces.d.ts +11 -21
  64. package/dist/interfaces/resource-interfaces.js +3 -0
  65. package/dist/interfaces/resource-interfaces.js.map +1 -1
  66. package/dist/macros/common.d.ts +10 -10
  67. package/dist/macros/createCards/index.d.ts +0 -13
  68. package/dist/macros/createCards/index.js.map +1 -1
  69. package/dist/macros/createCards/types.d.ts +44 -0
  70. package/dist/macros/createCards/types.js +15 -0
  71. package/dist/macros/createCards/types.js.map +1 -0
  72. package/dist/macros/graph/index.d.ts +2 -6
  73. package/dist/macros/graph/index.js +2 -2
  74. package/dist/macros/graph/index.js.map +1 -1
  75. package/dist/macros/graph/types.d.ts +23 -0
  76. package/dist/macros/graph/types.js +15 -0
  77. package/dist/macros/graph/types.js.map +1 -0
  78. package/dist/macros/image/index.d.ts +8 -16
  79. package/dist/macros/image/index.js +36 -33
  80. package/dist/macros/image/index.js.map +1 -1
  81. package/dist/macros/image/types.d.ts +38 -0
  82. package/dist/macros/image/types.js +15 -0
  83. package/dist/macros/image/types.js.map +1 -0
  84. package/dist/macros/include/index.d.ts +1 -6
  85. package/dist/macros/include/index.js +4 -7
  86. package/dist/macros/include/index.js.map +1 -1
  87. package/dist/macros/include/types.d.ts +31 -0
  88. package/dist/macros/include/types.js +15 -0
  89. package/dist/macros/include/types.js.map +1 -0
  90. package/dist/macros/percentage/index.d.ts +0 -6
  91. package/dist/macros/percentage/index.js.map +1 -1
  92. package/dist/macros/percentage/types.d.ts +31 -0
  93. package/dist/macros/percentage/types.js +15 -0
  94. package/dist/macros/percentage/types.js.map +1 -0
  95. package/dist/macros/report/index.d.ts +0 -3
  96. package/dist/macros/report/index.js.map +1 -1
  97. package/dist/macros/report/types.d.ts +19 -0
  98. package/dist/macros/report/types.js +15 -0
  99. package/dist/macros/report/types.js.map +1 -0
  100. package/dist/macros/scoreCard/index.d.ts +0 -6
  101. package/dist/macros/scoreCard/index.js.map +1 -1
  102. package/dist/macros/scoreCard/types.d.ts +31 -0
  103. package/dist/macros/scoreCard/types.js +15 -0
  104. package/dist/macros/scoreCard/types.js.map +1 -0
  105. package/dist/macros/types.d.ts +25 -0
  106. package/dist/macros/types.js +2 -0
  107. package/dist/macros/types.js.map +1 -0
  108. package/dist/macros/vega/index.d.ts +0 -4
  109. package/dist/macros/vega/index.js.map +1 -1
  110. package/dist/macros/vega/types.d.ts +20 -0
  111. package/dist/macros/vega/types.js +2 -0
  112. package/dist/macros/vega/types.js.map +1 -0
  113. package/dist/macros/vegalite/index.d.ts +0 -4
  114. package/dist/macros/vegalite/index.js.map +1 -1
  115. package/dist/macros/vegalite/types.d.ts +20 -0
  116. package/dist/macros/vegalite/types.js +15 -0
  117. package/dist/macros/vegalite/types.js.map +1 -0
  118. package/dist/macros/xref/index.d.ts +0 -3
  119. package/dist/macros/xref/index.js +5 -14
  120. package/dist/macros/xref/index.js.map +1 -1
  121. package/dist/macros/xref/types.d.ts +19 -0
  122. package/dist/macros/xref/types.js +15 -0
  123. package/dist/macros/xref/types.js.map +1 -0
  124. package/dist/module-manager.js +4 -4
  125. package/dist/module-manager.js.map +1 -1
  126. package/dist/project-settings.js.map +1 -1
  127. package/dist/resources/calculation-resource.d.ts +4 -32
  128. package/dist/resources/calculation-resource.js +0 -55
  129. package/dist/resources/calculation-resource.js.map +1 -1
  130. package/dist/resources/card-type-resource.d.ts +4 -21
  131. package/dist/resources/card-type-resource.js +13 -44
  132. package/dist/resources/card-type-resource.js.map +1 -1
  133. package/dist/resources/field-type-resource.d.ts +4 -21
  134. package/dist/resources/field-type-resource.js +14 -38
  135. package/dist/resources/field-type-resource.js.map +1 -1
  136. package/dist/resources/file-resource.d.ts +12 -29
  137. package/dist/resources/file-resource.js +19 -293
  138. package/dist/resources/file-resource.js.map +1 -1
  139. package/dist/resources/folder-resource.d.ts +31 -50
  140. package/dist/resources/folder-resource.js +68 -96
  141. package/dist/resources/folder-resource.js.map +1 -1
  142. package/dist/resources/graph-model-resource.d.ts +5 -33
  143. package/dist/resources/graph-model-resource.js +8 -61
  144. package/dist/resources/graph-model-resource.js.map +1 -1
  145. package/dist/resources/graph-view-resource.d.ts +5 -28
  146. package/dist/resources/graph-view-resource.js +6 -45
  147. package/dist/resources/graph-view-resource.js.map +1 -1
  148. package/dist/resources/link-type-resource.d.ts +4 -21
  149. package/dist/resources/link-type-resource.js +6 -31
  150. package/dist/resources/link-type-resource.js.map +1 -1
  151. package/dist/resources/report-resource.d.ts +5 -17
  152. package/dist/resources/report-resource.js +6 -44
  153. package/dist/resources/report-resource.js.map +1 -1
  154. package/dist/resources/resource-object.d.ts +58 -23
  155. package/dist/resources/resource-object.js +293 -24
  156. package/dist/resources/resource-object.js.map +1 -1
  157. package/dist/resources/template-resource.d.ts +4 -15
  158. package/dist/resources/template-resource.js +10 -25
  159. package/dist/resources/template-resource.js.map +1 -1
  160. package/dist/resources/workflow-resource.d.ts +4 -23
  161. package/dist/resources/workflow-resource.js +12 -38
  162. package/dist/resources/workflow-resource.js.map +1 -1
  163. package/dist/utils/card-utils.d.ts +69 -19
  164. package/dist/utils/card-utils.js +179 -30
  165. package/dist/utils/card-utils.js.map +1 -1
  166. package/dist/utils/clingo-facts.js +11 -3
  167. package/dist/utils/clingo-facts.js.map +1 -1
  168. package/dist/utils/clingo-parser.js +1 -1
  169. package/dist/utils/clingo-parser.js.map +1 -1
  170. package/dist/utils/constants.d.ts +2 -0
  171. package/dist/utils/constants.js +4 -0
  172. package/dist/utils/constants.js.map +1 -1
  173. package/dist/utils/csv.js +1 -1
  174. package/dist/utils/csv.js.map +1 -1
  175. package/package.json +5 -5
  176. package/src/card-metadata-updater.ts +3 -5
  177. package/src/command-handler.ts +11 -18
  178. package/src/command-manager.ts +4 -3
  179. package/src/commands/create.ts +17 -83
  180. package/src/commands/edit.ts +16 -132
  181. package/src/commands/export.ts +8 -29
  182. package/src/commands/import.ts +4 -6
  183. package/src/commands/move.ts +144 -179
  184. package/src/commands/remove.ts +9 -52
  185. package/src/commands/rename.ts +2 -7
  186. package/src/commands/show.ts +50 -143
  187. package/src/commands/transition.ts +30 -33
  188. package/src/commands/update.ts +27 -9
  189. package/src/commands/validate.ts +21 -36
  190. package/src/containers/card-container.ts +200 -360
  191. package/src/containers/project/calculation-engine.ts +21 -13
  192. package/src/containers/project/card-cache.ts +497 -0
  193. package/src/containers/project/resource-collector.ts +9 -1
  194. package/src/containers/project.ts +529 -327
  195. package/src/containers/template.ts +109 -127
  196. package/src/index.ts +1 -0
  197. package/src/interfaces/folder-content-interfaces.ts +7 -1
  198. package/src/interfaces/macros.ts +2 -0
  199. package/src/interfaces/project-interfaces.ts +7 -1
  200. package/src/interfaces/resource-interfaces.ts +12 -24
  201. package/src/macros/createCards/index.ts +1 -12
  202. package/src/macros/createCards/types.ts +46 -0
  203. package/src/macros/graph/index.ts +3 -7
  204. package/src/macros/graph/types.ts +24 -0
  205. package/src/macros/image/index.ts +50 -61
  206. package/src/macros/image/types.ts +39 -0
  207. package/src/macros/include/index.ts +6 -15
  208. package/src/macros/include/types.ts +32 -0
  209. package/src/macros/percentage/index.ts +1 -7
  210. package/src/macros/percentage/types.ts +32 -0
  211. package/src/macros/report/index.ts +1 -4
  212. package/src/macros/report/types.ts +20 -0
  213. package/src/macros/scoreCard/index.ts +1 -7
  214. package/src/macros/scoreCard/types.ts +32 -0
  215. package/src/macros/types.ts +48 -0
  216. package/src/macros/vega/index.ts +1 -4
  217. package/src/macros/vega/types.ts +21 -0
  218. package/src/macros/vegalite/index.ts +1 -4
  219. package/src/macros/vegalite/types.ts +22 -0
  220. package/src/macros/xref/index.ts +6 -20
  221. package/src/macros/xref/types.ts +20 -0
  222. package/src/module-manager.ts +5 -5
  223. package/src/project-settings.ts +1 -1
  224. package/src/resources/calculation-resource.ts +6 -76
  225. package/src/resources/card-type-resource.ts +24 -59
  226. package/src/resources/field-type-resource.ts +22 -51
  227. package/src/resources/file-resource.ts +27 -409
  228. package/src/resources/folder-resource.ts +98 -124
  229. package/src/resources/graph-model-resource.ts +17 -74
  230. package/src/resources/graph-view-resource.ts +14 -54
  231. package/src/resources/link-type-resource.ts +13 -40
  232. package/src/resources/report-resource.ts +17 -57
  233. package/src/resources/resource-object.ts +435 -32
  234. package/src/resources/template-resource.ts +16 -29
  235. package/src/resources/workflow-resource.ts +26 -50
  236. package/src/utils/card-utils.ts +217 -31
  237. package/src/utils/clingo-facts.ts +13 -3
  238. package/src/utils/clingo-parser.ts +1 -1
  239. package/src/utils/constants.ts +6 -0
  240. package/src/utils/csv.ts +1 -1
@@ -14,6 +14,7 @@
14
14
 
15
15
  import type {
16
16
  CardType,
17
+ UpdateKey,
17
18
  Workflow,
18
19
  WorkflowState,
19
20
  WorkflowTransition,
@@ -39,14 +40,12 @@ import { resourceName } from './file-resource.js';
39
40
  /**
40
41
  * Workflow resource class.
41
42
  */
42
- export class WorkflowResource extends FileResource {
43
+ export class WorkflowResource extends FileResource<Workflow> {
43
44
  constructor(project: Project, name: ResourceName) {
44
45
  super(project, name, 'workflows');
45
46
 
46
47
  this.contentSchemaId = 'workflowSchema';
47
48
  this.contentSchema = super.contentSchemaContent(this.contentSchemaId);
48
-
49
- this.initialize();
50
49
  }
51
50
 
52
51
  // Collect all cards that use this workflow.
@@ -60,11 +59,10 @@ export class WorkflowResource extends FileResource {
60
59
  );
61
60
  if (
62
61
  object.data &&
63
- (object.data as CardType).workflow ===
64
- resourceNameToString(this.resourceName)
62
+ object.data.workflow === resourceNameToString(this.resourceName)
65
63
  ) {
66
64
  // fetch all cards with card type
67
- promises.push(this.collectCards({ metadata: true }, cardType.name));
65
+ promises.push(this.collectCards(cardType.name));
68
66
  }
69
67
  }
70
68
  return (await Promise.all(promises)).flat();
@@ -82,7 +80,7 @@ export class WorkflowResource extends FileResource {
82
80
 
83
81
  // Handle change of workflow state.
84
82
  private async handleStateChange(op: ChangeOperation<WorkflowState>) {
85
- const content = structuredClone(this.content) as Workflow;
83
+ const content = structuredClone(this.content);
86
84
  const stateName = this.targetName(op) as string;
87
85
  // Check that state can be changed to
88
86
  content.transitions = content.transitions.filter(
@@ -107,7 +105,7 @@ export class WorkflowResource extends FileResource {
107
105
  // Handle removal of workflow state.
108
106
  // State can be removed with or without replacement.
109
107
  private async handleStateRemoval(op: RemoveOperation<WorkflowState>) {
110
- const content = structuredClone(this.content) as Workflow;
108
+ const content = structuredClone(this.content);
111
109
  const stateName = this.targetName(op) as string;
112
110
 
113
111
  // If there is no replacement value, remove all transitions "to" and "from" this state.
@@ -153,8 +151,8 @@ export class WorkflowResource extends FileResource {
153
151
  }
154
152
 
155
153
  // Potentially updates the changed transition with current properties.
156
- private async transitionObject(op: ChangeOperation<WorkflowTransition>) {
157
- const content = structuredClone(this.content) as Workflow;
154
+ private transitionObject(op: ChangeOperation<WorkflowTransition>) {
155
+ const content = structuredClone(this.content);
158
156
  const targetTransitionName = this.targetName(op);
159
157
  const currentTransition = content.transitions.filter(
160
158
  (item) => item.name === targetTransitionName,
@@ -212,8 +210,13 @@ export class WorkflowResource extends FileResource {
212
210
  this.project,
213
211
  resourceName(cardType.name),
214
212
  );
215
- if (object.data && (object.data as CardType).workflow === oldName) {
216
- await object.update('workflow', op);
213
+ if (object.data && object.data.workflow === oldName) {
214
+ await object.update(
215
+ {
216
+ key: 'workflow',
217
+ },
218
+ op,
219
+ );
217
220
  }
218
221
  }
219
222
  }
@@ -234,21 +237,6 @@ export class WorkflowResource extends FileResource {
234
237
  return super.create(newContent);
235
238
  }
236
239
 
237
- /**
238
- * Returns content data.
239
- */
240
- public get data(): Workflow {
241
- return super.data as Workflow;
242
- }
243
-
244
- /**
245
- * Deletes file that this object is based on.
246
- * If there are card types that depended on this workflow, they are now invalid.
247
- */
248
- public async delete() {
249
- return super.delete();
250
- }
251
-
252
240
  /**
253
241
  * Renames the object and the file.
254
242
  * @param newName New name for the resource.
@@ -259,25 +247,21 @@ export class WorkflowResource extends FileResource {
259
247
  return this.handleNameChange(existingName);
260
248
  }
261
249
 
262
- /**
263
- * Shows metadata of the resource.
264
- * @returns workflow metadata.
265
- */
266
- public async show(): Promise<Workflow> {
267
- return super.show() as Promise<Workflow>;
268
- }
269
-
270
250
  /**
271
251
  * Updates workflow resource.
272
- * @param key Key to modify
252
+ * @param updateKey Key to modify
273
253
  * @param op Operation to perform on 'key'
274
254
  * @throws if key is unknown.
275
255
  */
276
- public async update<Type>(key: string, op: Operation<Type>) {
256
+ public async update<Type, K extends string>(
257
+ updateKey: UpdateKey<K>,
258
+ op: Operation<Type>,
259
+ ) {
260
+ const { key } = updateKey;
277
261
  const nameChange = key === 'name';
278
262
  const existingName = this.content.name;
279
263
 
280
- await super.update(key, op);
264
+ await super.update(updateKey, op);
281
265
 
282
266
  const content = structuredClone(this.content) as Workflow;
283
267
 
@@ -334,7 +318,7 @@ export class WorkflowResource extends FileResource {
334
318
  // If workflow state is removed, remove all transitions "to" and "from" this state.
335
319
  let removeOp: RemoveOperation<WorkflowState>;
336
320
  if (this.isStringOperation(op)) {
337
- const toBeRemovedState = (this.content as Workflow).states.find(
321
+ const toBeRemovedState = this.content.states.find(
338
322
  (state) => state.name === op.target,
339
323
  );
340
324
  removeOp = {
@@ -349,7 +333,7 @@ export class WorkflowResource extends FileResource {
349
333
  // If workflow state is renamed, replace all transitions "to" and "from" the old state with new state.
350
334
  let changeOp: ChangeOperation<WorkflowState>;
351
335
  if (this.isStringOperation(op)) {
352
- const toBeChangedState = (this.content as Workflow).states.find(
336
+ const toBeChangedState = this.content.states.find(
353
337
  (state) => state.name === op.target,
354
338
  );
355
339
  changeOp = {
@@ -363,7 +347,7 @@ export class WorkflowResource extends FileResource {
363
347
  await this.handleStateChange(changeOp);
364
348
  }
365
349
 
366
- await super.postUpdate(content, key, op);
350
+ await super.postUpdate(content, updateKey, op);
367
351
 
368
352
  // Renaming this workflow causes that references to its name must be updated.
369
353
  if (nameChange) {
@@ -381,7 +365,7 @@ export class WorkflowResource extends FileResource {
381
365
  */
382
366
  public async usage(cards?: Card[]): Promise<string[]> {
383
367
  const resourceName = resourceNameToString(this.resourceName);
384
- const allCards = cards ?? (await super.cards());
368
+ const allCards = cards ?? super.cards();
385
369
  const cardTypes = await this.project.cardTypes(ResourcesFrom.all);
386
370
  const cardTypeReferences = await Promise.all(
387
371
  cardTypes.map(async (cardType) => {
@@ -401,12 +385,4 @@ export class WorkflowResource extends FileResource {
401
385
  ...calculations,
402
386
  ];
403
387
  }
404
-
405
- /**
406
- * Validates workflow.
407
- * @throws when there are validation errors.
408
- */
409
- public validate(content?: object): Promise<void> {
410
- return super.validate(content);
411
- }
412
388
  }
@@ -1,25 +1,161 @@
1
1
  /**
2
- Cyberismo
3
- Copyright © Cyberismo Ltd and contributors 2024
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/>.
12
+ */
4
13
 
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.
14
+ import { sep } from 'node:path';
15
+ import { CARD_KEY_SEPARATOR, ROOT } from './constants.js';
6
16
 
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.
17
+ import type {
18
+ Card,
19
+ CardWithChildrenCards,
20
+ } from '../interfaces/project-interfaces.js';
21
+ import type { Project } from '../resources/folder-resource.js';
8
22
 
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/>.
11
- */
23
+ /**
24
+ * Builds card hierarchy from flat card list with nested card objects.
25
+ * This converts the cards hierarchy (where children are string[]) to
26
+ * CardWithChildrenCards[] (where children are Card[]).
27
+ * @param flatCards Cards in a flat array.
28
+ * @returns Cards in hierarchical array with nested card objects
29
+ */
30
+ export const buildCardHierarchy = (
31
+ flatCards: Card[],
32
+ ): CardWithChildrenCards[] => {
33
+ const cardMap = new Map(flatCards.map((card) => [card.key, card]));
12
34
 
13
- import { sep } from 'node:path';
35
+ // Helper to get cards as a map
36
+ function cards(flatCards: Card[]) {
37
+ const cardMap = new Map(
38
+ flatCards.map((card) => [
39
+ card.key,
40
+ { ...card, children: [] as string[] },
41
+ ]),
42
+ );
43
+
44
+ const rootCards: Card[] = [];
45
+ cardMap.forEach((card) => {
46
+ if (card.parent && cardMap.has(card.parent)) {
47
+ const parentCard = cardMap.get(card.parent);
48
+ if (parentCard) {
49
+ parentCard.children.push(card.key);
50
+ }
51
+ } else {
52
+ rootCards.push(card);
53
+ }
54
+ });
55
+
56
+ return rootCards;
57
+ }
58
+
59
+ // Helper to convert from string[] => Card[] children
60
+ function convert(card: Card): CardWithChildrenCards {
61
+ const childrenCards = card.children.map((childKey) => {
62
+ const childCard = cardMap.get(childKey)!;
63
+ return convert(childCard);
64
+ });
65
+
66
+ return {
67
+ ...card,
68
+ childrenCards,
69
+ };
70
+ }
71
+
72
+ const rootCards = cards(flatCards);
73
+ return rootCards.map(convert);
74
+ };
75
+
76
+ /**
77
+ * Breaks card path to logical parts: cardKey, list of parents, prefix and template
78
+ * If this is a root card, then parents is undefined.
79
+ * If this is not a template card, then template is undefined.
80
+ * @param prefix Project prefix.
81
+ * @param cardPath Card path to break into parts.
82
+ * @returns Card parts
83
+ * @todo: This could also return module name, if card is from a module
84
+ */
85
+ export const cardPathParts = (
86
+ prefix: string,
87
+ cardPath: string,
88
+ ): {
89
+ cardKey: string | undefined;
90
+ parents: (string | undefined)[];
91
+ prefix: string;
92
+ template: string;
93
+ } => {
94
+ const pathParts = cardPath.split(sep);
95
+ const cardKey = pathParts.at(pathParts.length - 1);
96
+ const parents = [];
97
+ let template = '';
98
+ let startIndex = -1;
99
+ let templatesNameIndex = -1;
100
+
101
+ const cardRootIndex = pathParts.indexOf('cardRoot');
102
+ const projectInternalsIndex = pathParts.indexOf('.cards');
14
103
 
15
- import type { Card } from '../interfaces/project-interfaces.js';
104
+ if (projectInternalsIndex === -1 && cardRootIndex >= 0) {
105
+ startIndex = projectInternalsIndex;
106
+ } else if (projectInternalsIndex >= 0 && cardRootIndex === -1) {
107
+ const templatesIndex = pathParts.indexOf('templates');
108
+ startIndex = templatesIndex;
109
+ if (templatesIndex === -1) {
110
+ throw new Error(
111
+ `Invalid card path. Template card must have 'templates' in path`,
112
+ );
113
+ }
114
+ const modulesIndex = pathParts.indexOf('modules');
115
+ if (modulesIndex !== -1) {
116
+ prefix = pathParts.at(modulesIndex + 1) || '';
117
+ }
118
+ templatesNameIndex = templatesIndex + 1;
119
+ template = `${prefix}/templates/${pathParts.at(templatesNameIndex)}`;
120
+ } else {
121
+ throw new Error(`Card must be either project card, or template card`);
122
+ }
123
+
124
+ // Look for parents in the path.
125
+ let previousWasParent = false;
126
+ for (let index = startIndex; index <= pathParts.length; index++) {
127
+ if (previousWasParent) {
128
+ previousWasParent = false;
129
+ parents.push(pathParts.at(index - 2));
130
+ }
131
+ const cardsSubFolder = pathParts.at(index) === 'c';
132
+ const ignoreOrNotTemplatesParent =
133
+ index - 1 !== templatesNameIndex || templatesNameIndex === -1;
134
+ if (cardsSubFolder && ignoreOrNotTemplatesParent) {
135
+ previousWasParent = true;
136
+ }
137
+ }
16
138
 
17
- // Helper function to find the parent path from a card path
139
+ return {
140
+ cardKey: cardKey,
141
+ parents: parents,
142
+ prefix: prefix,
143
+ template: template,
144
+ };
145
+ };
146
+
147
+ /**
148
+ * Find parent path from a card path
149
+ * @param cardPath Card path to find parent path from.
150
+ * @returns Parent path
151
+ */
18
152
  export const findParentPath = (cardPath: string): string | null => {
19
153
  const pathParts = cardPath.split(sep);
20
154
  const hasChildren = pathParts.lastIndexOf('c');
21
155
 
22
- if (hasChildren <= 0) return null;
156
+ if (hasChildren <= 0) {
157
+ return null;
158
+ }
23
159
 
24
160
  const parentPathParts = [...pathParts];
25
161
  parentPathParts.splice(hasChildren, 1);
@@ -28,21 +164,53 @@ export const findParentPath = (cardPath: string): string | null => {
28
164
 
29
165
  /**
30
166
  * Flattens card tree so that children are shown on same level regardless of nesting level.
31
- * @param array card tree
32
- * @returns flattened card tree.
167
+ * @param array Card tree to flatten
168
+ * @parent project Project to use
169
+ * @returns Flattened card tree.
33
170
  */
34
- export const flattenCardArray = (array: Card[]) => {
171
+ export const flattenCardArray = (array: Card[], project: Project) => {
35
172
  const result: Card[] = [];
173
+
36
174
  array.forEach((item) => {
37
175
  const { key, path, children, attachments, metadata } = item;
38
- result.push({ key, path, children, attachments, metadata });
176
+ const childCardIds = project
177
+ .cardKeysToCards(children)
178
+ .map((item) => item.key);
179
+
180
+ result.push({
181
+ key,
182
+ path,
183
+ children: [...childCardIds],
184
+ attachments,
185
+ metadata,
186
+ });
39
187
  if (children) {
40
- result.push(...flattenCardArray(children));
188
+ result.push(
189
+ ...flattenCardArray(project.cardKeysToCards(children), project),
190
+ );
41
191
  }
42
192
  });
43
193
  return result;
44
194
  };
45
195
 
196
+ /**
197
+ * Checks if given card is in some module.
198
+ * @param card Card object to check
199
+ * @returns true if card exists in a module; false otherwise
200
+ */
201
+ export const isModuleCard = (card: Card) => {
202
+ return card.path.includes(`${sep}modules${sep}`);
203
+ };
204
+
205
+ /**
206
+ * Checks if given path is from a module.
207
+ * @param path Path to check
208
+ * @returns true if path is from a module; false otherwise
209
+ */
210
+ export const isModulePath = (path: string) => {
211
+ return path.includes(`${sep}modules${sep}`);
212
+ };
213
+
46
214
  /**
47
215
  * Checks if given card is in some template.
48
216
  * @param card card object to check
@@ -55,6 +223,37 @@ export const isTemplateCard = (card: Card) => {
55
223
  );
56
224
  };
57
225
 
226
+ /**
227
+ * Returns module name from card key
228
+ * @param cardKey Card key
229
+ * @returns module name
230
+ * @todo: should be renamed to modulePrefixFromCardKey
231
+ */
232
+ export const moduleNameFromCardKey = (cardKey: string) => {
233
+ const parts = cardKey.split(CARD_KEY_SEPARATOR);
234
+ if (parts.length !== 2) {
235
+ throw new Error(`Invalid card key: ${cardKey}`);
236
+ }
237
+ return parts[0];
238
+ };
239
+
240
+ /**
241
+ * Finds parent card key
242
+ * @param cardPath Card path from which to find parent card
243
+ * @returns Parent card key
244
+ */
245
+ export function parentCard(cardPath: string) {
246
+ const pathParts = cardPath.split(sep);
247
+ if (
248
+ pathParts.at(pathParts.length - 2) === 'cardRoot' ||
249
+ (pathParts.length > 3 && pathParts.at(pathParts.length - 4) === 'templates')
250
+ ) {
251
+ return ROOT;
252
+ } else {
253
+ return pathParts.at(pathParts.length - 3);
254
+ }
255
+ }
256
+
58
257
  /**
59
258
  * Sorts array of cards first using prefix and then using ID.
60
259
  * Prefixes are returned in alphabetical order, and then in numeric order within same prefix.
@@ -64,8 +263,8 @@ export const isTemplateCard = (card: Card) => {
64
263
  * @returns Cards ordered; first by prefixes, then by ID.
65
264
  */
66
265
  export const sortCards = (a: string, b: string) => {
67
- const aParts = a.split('_');
68
- const bParts = b.split('_');
266
+ const aParts = a.split(CARD_KEY_SEPARATOR);
267
+ const bParts = b.split(CARD_KEY_SEPARATOR);
69
268
  if (aParts[0] !== bParts[0]) {
70
269
  if (aParts[0] > bParts[0]) return 1;
71
270
  if (aParts[0] < bParts[0]) return -1;
@@ -81,16 +280,3 @@ export const sortCards = (a: string, b: string) => {
81
280
  if (aParts[1] < bParts[1]) return -1;
82
281
  return 0;
83
282
  };
84
-
85
- /**
86
- * Returns module name from card key
87
- * @param cardKey card key
88
- * @returns module name
89
- */
90
- export const moduleNameFromCardKey = (cardKey: string) => {
91
- const parts = cardKey.split('_');
92
- if (parts.length !== 2) {
93
- throw new Error(`Invalid card key: ${cardKey}`);
94
- }
95
- return parts[0];
96
- };
@@ -168,6 +168,7 @@ export const createWorkflowFacts = (workflow: Workflow) => {
168
168
  */
169
169
  export const createCardFacts = async (card: Card, project: Project) => {
170
170
  // Small helper to deduce parent path
171
+ // todo: Should use card-utils
171
172
  function parentPath(cardPath: string) {
172
173
  const pathParts = cardPath.split(sep);
173
174
  if (pathParts.at(pathParts.length - 2) === 'cardRoot') {
@@ -178,6 +179,7 @@ export const createCardFacts = async (card: Card, project: Project) => {
178
179
  }
179
180
 
180
181
  // Helper to deduce template parent path.
182
+ // todo: Should use card-utils
181
183
  function parentPathFromTemplate(card: Card) {
182
184
  const cardPath = card.path;
183
185
  const pathParts = cardPath.split(sep);
@@ -201,10 +203,18 @@ export const createCardFacts = async (card: Card, project: Project) => {
201
203
  }
202
204
  }
203
205
 
204
- const parentsPath = isTemplateCard(card)
205
- ? parentPathFromTemplate(card)
206
- : parentPath(card.path);
206
+ // Use card.parent if available, otherwise fall back to path-based calculation
207
+ const parentsPath =
208
+ card.parent && card.parent !== 'root'
209
+ ? card.parent
210
+ : isTemplateCard(card)
211
+ ? parentPathFromTemplate(card)
212
+ : parentPath(card.path);
213
+
207
214
  const builder = new ClingoProgramBuilder().addComment(card.key);
215
+ if (!isTemplateCard(card)) {
216
+ builder.addCustomFact('card', (b) => b.addLiteralArgument(card.key));
217
+ }
208
218
 
209
219
  if (card.metadata) {
210
220
  for (const [field, value] of Object.entries(card.metadata)) {
@@ -103,7 +103,7 @@ class ClingoParser {
103
103
  childObject: (parentKey: string, name: string, collection: string) => {
104
104
  this.childObjectQueue.push({ parentKey, name, collection });
105
105
  },
106
- field: async (
106
+ field: (
107
107
  key: string,
108
108
  fieldName: string,
109
109
  fieldValue: string,
@@ -13,11 +13,17 @@
13
13
 
14
14
  import type { PredefinedCardMetadata } from '../interfaces/project-interfaces.js';
15
15
 
16
+ // Card key separator between project prefix and card ID
17
+ export const CARD_KEY_SEPARATOR = '_';
18
+
16
19
  export const INT32_MAX = 2147483647; // 2^31-1
17
20
 
18
21
  // Maximum level offset for includeMacro
19
22
  export const MAX_LEVEL_OFFSET = 5;
20
23
 
24
+ // Root parent name
25
+ export const ROOT = 'root';
26
+
21
27
  /**
22
28
  * These are file names that are valid for folder resources.
23
29
  * Note that the folders might still contain other files, such as .schema files,
package/src/utils/csv.ts CHANGED
@@ -10,7 +10,7 @@
10
10
  License along with this program. If not, see <https://www.gnu.org/licenses/>.
11
11
  */
12
12
 
13
- import { readFile } from 'fs/promises';
13
+ import { readFile } from 'node:fs/promises';
14
14
  import { parse } from 'csv-parse/sync';
15
15
  import type { CSVRowRaw } from '../interfaces/project-interfaces.js';
16
16