@cyberismo/data-handler 0.0.8 → 0.0.10

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 (258) 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 +6 -3
  10. package/dist/commands/create.js +27 -5
  11. package/dist/commands/create.js.map +1 -1
  12. package/dist/commands/edit.d.ts +15 -3
  13. package/dist/commands/edit.js +52 -9
  14. package/dist/commands/edit.js.map +1 -1
  15. package/dist/commands/export.d.ts +11 -9
  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 +13 -0
  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 +1 -3
  25. package/dist/commands/remove.js +4 -6
  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 +89 -8
  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 -0
  40. package/dist/commands/validate.js +31 -4
  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.js +9 -13
  49. package/dist/containers/project/resource-collector.js.map +1 -1
  50. package/dist/containers/project.d.ts +18 -1
  51. package/dist/containers/project.js +28 -55
  52. package/dist/containers/project.js.map +1 -1
  53. package/dist/containers/template.d.ts +5 -0
  54. package/dist/containers/template.js +9 -0
  55. package/dist/containers/template.js.map +1 -1
  56. package/dist/index.d.ts +5 -2
  57. package/dist/index.js +5 -1
  58. package/dist/index.js.map +1 -1
  59. package/dist/interfaces/macros.d.ts +4 -2
  60. package/dist/interfaces/project-interfaces.d.ts +21 -0
  61. package/dist/interfaces/project-interfaces.js +4 -0
  62. package/dist/interfaces/project-interfaces.js.map +1 -1
  63. package/dist/interfaces/resource-interfaces.d.ts +4 -2
  64. package/dist/interfaces/resource-interfaces.js.map +1 -1
  65. package/dist/macros/base-macro.d.ts +2 -1
  66. package/dist/macros/base-macro.js +14 -2
  67. package/dist/macros/base-macro.js.map +1 -1
  68. package/dist/macros/common.d.ts +16 -9
  69. package/dist/macros/common.js +22 -9
  70. package/dist/macros/common.js.map +1 -1
  71. package/dist/macros/createCards/index.d.ts +2 -2
  72. package/dist/macros/createCards/index.js +4 -0
  73. package/dist/macros/createCards/index.js.map +1 -1
  74. package/dist/macros/graph/index.d.ts +1 -3
  75. package/dist/macros/graph/index.js +1 -6
  76. package/dist/macros/graph/index.js.map +1 -1
  77. package/dist/macros/image/index.d.ts +39 -0
  78. package/dist/macros/image/index.js +82 -0
  79. package/dist/macros/image/index.js.map +1 -0
  80. package/dist/macros/image/metadata.d.ts +18 -0
  81. package/dist/macros/image/metadata.js +22 -0
  82. package/dist/macros/image/metadata.js.map +1 -0
  83. package/dist/macros/include/index.d.ts +31 -0
  84. package/dist/macros/include/index.js +94 -0
  85. package/dist/macros/include/index.js.map +1 -0
  86. package/dist/macros/include/metadata.d.ts +15 -0
  87. package/dist/macros/include/metadata.js +19 -0
  88. package/dist/macros/include/metadata.js.map +1 -0
  89. package/dist/macros/index.d.ts +33 -31
  90. package/dist/macros/index.js +142 -71
  91. package/dist/macros/index.js.map +1 -1
  92. package/dist/macros/percentage/index.d.ts +28 -0
  93. package/dist/macros/percentage/index.js +33 -0
  94. package/dist/macros/percentage/index.js.map +1 -0
  95. package/dist/macros/percentage/metadata.d.ts +15 -0
  96. package/dist/macros/percentage/metadata.js +19 -0
  97. package/dist/macros/percentage/metadata.js.map +1 -0
  98. package/dist/macros/report/index.d.ts +2 -6
  99. package/dist/macros/report/index.js +3 -7
  100. package/dist/macros/report/index.js.map +1 -1
  101. package/dist/macros/scoreCard/index.d.ts +14 -15
  102. package/dist/macros/scoreCard/index.js +14 -18
  103. package/dist/macros/scoreCard/index.js.map +1 -1
  104. package/dist/macros/vega/index.d.ts +28 -0
  105. package/dist/macros/vega/index.js +27 -0
  106. package/dist/macros/vega/index.js.map +1 -0
  107. package/dist/macros/vega/metadata.d.ts +15 -0
  108. package/dist/macros/vega/metadata.js +7 -0
  109. package/dist/macros/vega/metadata.js.map +1 -0
  110. package/dist/macros/vegalite/index.d.ts +26 -0
  111. package/dist/macros/vegalite/index.js +24 -0
  112. package/dist/macros/vegalite/index.js.map +1 -0
  113. package/dist/macros/vegalite/metadata.d.ts +15 -0
  114. package/dist/macros/vegalite/metadata.js +7 -0
  115. package/dist/macros/vegalite/metadata.js.map +1 -0
  116. package/dist/macros/xref/index.d.ts +26 -0
  117. package/dist/macros/xref/index.js +53 -0
  118. package/dist/macros/xref/index.js.map +1 -0
  119. package/dist/macros/xref/metadata.d.ts +15 -0
  120. package/dist/macros/xref/metadata.js +19 -0
  121. package/dist/macros/xref/metadata.js.map +1 -0
  122. package/dist/module-manager.d.ts +1 -0
  123. package/dist/module-manager.js +14 -4
  124. package/dist/module-manager.js.map +1 -1
  125. package/dist/permissions/action-guard.d.ts +2 -2
  126. package/dist/permissions/action-guard.js +1 -1
  127. package/dist/permissions/action-guard.js.map +1 -1
  128. package/dist/resources/card-type-resource.d.ts +2 -0
  129. package/dist/resources/card-type-resource.js +63 -0
  130. package/dist/resources/card-type-resource.js.map +1 -1
  131. package/dist/resources/file-resource.d.ts +2 -0
  132. package/dist/resources/file-resource.js +12 -4
  133. package/dist/resources/file-resource.js.map +1 -1
  134. package/dist/resources/folder-resource.d.ts +19 -0
  135. package/dist/resources/folder-resource.js +51 -2
  136. package/dist/resources/folder-resource.js.map +1 -1
  137. package/dist/resources/graph-model-resource.js +1 -1
  138. package/dist/resources/graph-model-resource.js.map +1 -1
  139. package/dist/resources/graph-view-resource.js +1 -1
  140. package/dist/resources/graph-view-resource.js.map +1 -1
  141. package/dist/resources/report-resource.js +1 -1
  142. package/dist/resources/report-resource.js.map +1 -1
  143. package/dist/resources/resource-object.d.ts +8 -0
  144. package/dist/resources/resource-object.js +10 -1
  145. package/dist/resources/resource-object.js.map +1 -1
  146. package/dist/resources/template-resource.js +1 -1
  147. package/dist/resources/template-resource.js.map +1 -1
  148. package/dist/resources/workflow-resource.d.ts +2 -0
  149. package/dist/resources/workflow-resource.js +53 -9
  150. package/dist/resources/workflow-resource.js.map +1 -1
  151. package/dist/svg/index.d.ts +15 -0
  152. package/dist/svg/index.js +16 -0
  153. package/dist/svg/index.js.map +1 -0
  154. package/dist/svg/lib.d.ts +9 -0
  155. package/dist/svg/lib.js +26 -0
  156. package/dist/svg/lib.js.map +1 -0
  157. package/dist/svg/percentage.d.ts +25 -0
  158. package/dist/svg/percentage.js +90 -0
  159. package/dist/svg/percentage.js.map +1 -0
  160. package/dist/svg/scoreCard.d.ts +19 -0
  161. package/dist/svg/scoreCard.js +55 -0
  162. package/dist/svg/scoreCard.js.map +1 -0
  163. package/dist/types/queries.d.ts +8 -7
  164. package/dist/types/queries.js.map +1 -1
  165. package/dist/utils/card-utils.d.ts +6 -0
  166. package/dist/utils/card-utils.js +12 -0
  167. package/dist/utils/card-utils.js.map +1 -1
  168. package/dist/utils/clingo-facts.d.ts +2 -1
  169. package/dist/utils/clingo-facts.js +19 -2
  170. package/dist/utils/clingo-facts.js.map +1 -1
  171. package/dist/utils/clingo-parser.d.ts +1 -0
  172. package/dist/utils/clingo-parser.js +22 -100
  173. package/dist/utils/clingo-parser.js.map +1 -1
  174. package/dist/utils/constants.d.ts +7 -0
  175. package/dist/utils/constants.js +14 -0
  176. package/dist/utils/constants.js.map +1 -1
  177. package/dist/utils/csv.js.map +1 -1
  178. package/dist/utils/report.d.ts +17 -3
  179. package/dist/utils/report.js +38 -2
  180. package/dist/utils/report.js.map +1 -1
  181. package/dist/utils/resource-utils.d.ts +2 -1
  182. package/dist/utils/resource-utils.js +12 -3
  183. package/dist/utils/resource-utils.js.map +1 -1
  184. package/dist/utils/user-preferences.d.ts +1 -11
  185. package/dist/utils/user-preferences.js +30 -13
  186. package/dist/utils/user-preferences.js.map +1 -1
  187. package/dist/utils/validate.d.ts +2 -3
  188. package/dist/utils/validate.js +2 -2
  189. package/dist/utils/validate.js.map +1 -1
  190. package/package.json +8 -6
  191. package/src/command-handler.ts +96 -17
  192. package/src/command-manager.ts +8 -8
  193. package/src/commands/calculate.ts +11 -525
  194. package/src/commands/create.ts +35 -6
  195. package/src/commands/edit.ts +94 -11
  196. package/src/commands/export.ts +104 -31
  197. package/src/commands/import.ts +16 -0
  198. package/src/commands/move.ts +12 -15
  199. package/src/commands/remove.ts +4 -8
  200. package/src/commands/rename.ts +3 -12
  201. package/src/commands/show.ts +126 -9
  202. package/src/commands/transition.ts +3 -7
  203. package/src/commands/update.ts +6 -0
  204. package/src/commands/validate.ts +41 -13
  205. package/src/containers/card-container.ts +74 -0
  206. package/src/containers/project/calculation-engine.ts +535 -0
  207. package/src/containers/project/resource-collector.ts +13 -15
  208. package/src/containers/project.ts +30 -66
  209. package/src/containers/template.ts +16 -0
  210. package/src/index.ts +13 -2
  211. package/src/interfaces/macros.ts +4 -1
  212. package/src/interfaces/project-interfaces.ts +27 -0
  213. package/src/interfaces/resource-interfaces.ts +5 -2
  214. package/src/macros/base-macro.ts +19 -4
  215. package/src/macros/common.ts +22 -9
  216. package/src/macros/createCards/index.ts +6 -2
  217. package/src/macros/graph/index.ts +6 -10
  218. package/src/macros/image/index.ts +128 -0
  219. package/src/macros/image/metadata.ts +25 -0
  220. package/src/macros/include/index.ts +143 -0
  221. package/src/macros/include/metadata.ts +22 -0
  222. package/src/macros/index.ts +150 -98
  223. package/src/macros/percentage/index.ts +50 -0
  224. package/src/macros/percentage/metadata.ts +22 -0
  225. package/src/macros/report/index.ts +4 -12
  226. package/src/macros/scoreCard/index.ts +21 -25
  227. package/src/macros/vega/index.ts +55 -0
  228. package/src/macros/vega/metadata.ts +21 -0
  229. package/src/macros/vegalite/index.ts +46 -0
  230. package/src/macros/vegalite/metadata.ts +21 -0
  231. package/src/macros/xref/index.ts +73 -0
  232. package/src/macros/xref/metadata.ts +22 -0
  233. package/src/module-manager.ts +15 -5
  234. package/src/permissions/action-guard.ts +3 -3
  235. package/src/resources/card-type-resource.ts +100 -0
  236. package/src/resources/file-resource.ts +16 -4
  237. package/src/resources/folder-resource.ts +59 -2
  238. package/src/resources/graph-model-resource.ts +1 -1
  239. package/src/resources/graph-view-resource.ts +1 -1
  240. package/src/resources/report-resource.ts +1 -1
  241. package/src/resources/resource-object.ts +19 -1
  242. package/src/resources/template-resource.ts +1 -1
  243. package/src/resources/workflow-resource.ts +68 -13
  244. package/src/svg/index.ts +15 -0
  245. package/src/svg/lib.ts +31 -0
  246. package/src/svg/percentage.ts +97 -0
  247. package/src/svg/scoreCard.ts +88 -0
  248. package/src/types/queries.ts +8 -7
  249. package/src/types/string-pixel-width.d.ts +23 -0
  250. package/src/utils/card-utils.ts +13 -0
  251. package/src/utils/clingo-facts.ts +65 -3
  252. package/src/utils/clingo-parser.ts +31 -144
  253. package/src/utils/constants.ts +16 -0
  254. package/src/utils/csv.ts +1 -1
  255. package/src/utils/report.ts +45 -4
  256. package/src/utils/resource-utils.ts +12 -2
  257. package/src/utils/user-preferences.ts +32 -14
  258. package/src/utils/validate.ts +3 -3
@@ -16,7 +16,7 @@ import { existsSync, readFileSync } from 'node:fs';
16
16
  import { homedir } from 'node:os';
17
17
  import { join, resolve } from 'node:path';
18
18
  import { spawn } from 'node:child_process';
19
- import { writeFile } from 'node:fs/promises';
19
+ import { readFile, writeFile } from 'node:fs/promises';
20
20
 
21
21
  import mime from 'mime-types';
22
22
 
@@ -30,6 +30,8 @@ import type {
30
30
  ProjectMetadata,
31
31
  Resource,
32
32
  CardLocation,
33
+ Context,
34
+ ResourceFolderType,
33
35
  } from '../interfaces/project-interfaces.js';
34
36
  import type {
35
37
  CardType,
@@ -38,13 +40,19 @@ import type {
38
40
  Workflow,
39
41
  } from '../interfaces/resource-interfaces.js';
40
42
  import { Project, type ResourcesFrom } from '../containers/project.js';
41
- import { resourceName } from '../utils/resource-utils.js';
43
+ import {
44
+ type ResourceName,
45
+ resourceName,
46
+ resourceNameToPath,
47
+ resourceNameToString,
48
+ } from '../utils/resource-utils.js';
42
49
  import { TemplateResource } from '../resources/template-resource.js';
43
50
  import { UserPreferences } from '../utils/user-preferences.js';
44
51
 
45
52
  import ReportMacro from '../macros/report/index.js';
46
53
  import TaskQueue from '../macros/task-queue.js';
47
- import type { Calculate } from './calculate.js';
54
+ import { evaluateMacros } from '../macros/index.js';
55
+ import { FolderResource } from '../resources/folder-resource.js';
48
56
 
49
57
  /**
50
58
  * Show command.
@@ -54,11 +62,9 @@ export class Show {
54
62
  string,
55
63
  (from?: ResourcesFrom) => Promise<Resource[]>
56
64
  >;
57
- constructor(
58
- private project: Project,
59
- private calculate: Calculate,
60
- ) {
65
+ constructor(private project: Project) {
61
66
  this.resourceFunction = new Map([
67
+ ['calculations', this.project.calculations.bind(this.project)],
62
68
  ['cardTypes', this.project.cardTypes.bind(this.project)],
63
69
  ['fieldTypes', this.project.fieldTypes.bind(this.project)],
64
70
  ['graphModels', this.project.graphModels.bind(this.project)],
@@ -119,6 +125,27 @@ export class Show {
119
125
  }
120
126
  }
121
127
 
128
+ /**
129
+ * Shows all template cards in a project.
130
+ * @returns all template cards in a project.
131
+ */
132
+ public async showAllTemplateCards(): Promise<
133
+ { name: string; cards: Card[] }[]
134
+ > {
135
+ return Promise.all(
136
+ (await this.project.templates()).map(async (template) => {
137
+ const templateResource = new TemplateResource(
138
+ this.project,
139
+ resourceName(template.name),
140
+ );
141
+ return {
142
+ name: template.name,
143
+ cards: await templateResource.templateObject().showTemplateCards(),
144
+ };
145
+ }),
146
+ );
147
+ }
148
+
122
149
  /**
123
150
  * Shows all attachments (either template or project attachments) from a project.
124
151
  * @returns array of card attachments
@@ -264,6 +291,15 @@ export class Show {
264
291
  return this.project.listCards(cardsFrom);
265
292
  }
266
293
 
294
+ /**
295
+ * Shows the content of a logic program.
296
+ * @param cardKey The key of the card.
297
+ * @returns the content of the logic program.
298
+ */
299
+ public async showCardLogicProgram(cardKey: string) {
300
+ return this.project.calculationEngine.cardLogicProgram(cardKey);
301
+ }
302
+
267
303
  /**
268
304
  * Shows all card types in a project.
269
305
  * @returns array of card type details
@@ -282,6 +318,57 @@ export class Show {
282
318
  return results.filter((item) => item);
283
319
  }
284
320
 
321
+ /**
322
+ * Shows the content of a file in a resource.
323
+ * @param resourceName Name of the resource.
324
+ * @param fileName Name of the file to show.
325
+ * @returns the content of the file.
326
+ */
327
+ public async showFile(
328
+ resourceName: ResourceName,
329
+ fileName: string,
330
+ ): Promise<string> {
331
+ const resourceNameStr = resourceNameToString(resourceName);
332
+ if (
333
+ !(await this.project.resourceExists(
334
+ resourceName.type as ResourceFolderType,
335
+ resourceNameStr,
336
+ ))
337
+ ) {
338
+ throw new Error(
339
+ `Resource '${resourceNameStr}' does not exist in the project`,
340
+ );
341
+ }
342
+ const resource = Project.resourceObject(this.project, resourceName);
343
+ if (!(resource instanceof FolderResource)) {
344
+ throw new Error(`Resource '${resourceNameStr}' is not a folder resource`);
345
+ }
346
+ return resource.showFile(fileName);
347
+ }
348
+
349
+ /**
350
+ * Shows all file names in a folder resource.
351
+ * @param resourceName Name of the resource.
352
+ * @returns all file names in the resource.
353
+ */
354
+ public async showFileNames(resourceName: ResourceName): Promise<string[]> {
355
+ const resourceNameStr = resourceNameToString(resourceName);
356
+ if (
357
+ !(await this.project.resourceExists(
358
+ resourceName.type as ResourceFolderType,
359
+ resourceNameStr,
360
+ ))
361
+ ) {
362
+ throw new Error(
363
+ `Resource '${resourceNameStr}' does not exist in the project`,
364
+ );
365
+ }
366
+ const resource = Project.resourceObject(this.project, resourceName);
367
+ if (!(resource instanceof FolderResource)) {
368
+ throw new Error(`Resource '${resourceNameStr}' is not a folder resource`);
369
+ }
370
+ return resource.showFileNames();
371
+ }
285
372
  /**
286
373
  * Returns all unique labels in a project
287
374
  * @returns labels in a list
@@ -297,6 +384,15 @@ export class Show {
297
384
  return Array.from(new Set(labels));
298
385
  }
299
386
 
387
+ /**
388
+ * Shows the content of a logic program.
389
+ * @param resource Name of the resource.
390
+ * @returns the content of the logic program.
391
+ */
392
+ public async showLogicProgram(resource: ResourceName) {
393
+ return this.project.calculationEngine.resourceLogicProgram(resource);
394
+ }
395
+
300
396
  /**
301
397
  * Shows details of a module.
302
398
  * @param moduleName name of a module
@@ -349,6 +445,7 @@ export class Show {
349
445
  reportName: string,
350
446
  cardKey: string,
351
447
  parameters: object,
448
+ context: Context,
352
449
  outputPath?: string,
353
450
  ): Promise<string> {
354
451
  if (
@@ -359,16 +456,25 @@ export class Show {
359
456
  throw new Error(`Report '${reportName}' does not exist`);
360
457
  }
361
458
 
362
- const reportMacro = new ReportMacro(new TaskQueue(), this.calculate);
363
- const result = await reportMacro.handleInject(
459
+ await this.project.calculationEngine.generate();
460
+ const reportMacro = new ReportMacro(new TaskQueue());
461
+ let result = await reportMacro.handleStatic(
364
462
  {
365
463
  project: this.project,
366
464
  cardKey: cardKey,
367
465
  mode: 'static',
466
+ context,
368
467
  },
369
468
  { name: reportName, ...parameters },
370
469
  );
371
470
 
471
+ result = await evaluateMacros(result, {
472
+ project: this.project,
473
+ cardKey: cardKey,
474
+ mode: 'static',
475
+ context,
476
+ });
477
+
372
478
  // Show the results either in the console or write to a file.
373
479
  if (outputPath) {
374
480
  try {
@@ -393,6 +499,17 @@ export class Show {
393
499
  name: string,
394
500
  showUse: boolean = false,
395
501
  ): Promise<ResourceContent | undefined> {
502
+ // TODO: remove this workaround once calculations are implemented as a resource class
503
+ if (resourceName(name).type === 'calculations') {
504
+ const nameObj = resourceName(name);
505
+ const path = resourceNameToPath(this.project, nameObj, '.lp');
506
+ return {
507
+ name,
508
+ displayName: nameObj.identifier,
509
+ calculation: await readFile(path, 'utf-8'),
510
+ };
511
+ }
512
+
396
513
  const strictNameCheck = true;
397
514
  const resource = Project.resourceObject(
398
515
  this.project,
@@ -12,7 +12,6 @@
12
12
  */
13
13
 
14
14
  import { ActionGuard } from '../permissions/action-guard.js';
15
- import type { Calculate } from './calculate.js';
16
15
  import type {
17
16
  CardType,
18
17
  Workflow,
@@ -22,10 +21,7 @@ import { CardMetadataUpdater } from '../card-metadata-updater.js';
22
21
  import type { Project } from '../containers/project.js';
23
22
 
24
23
  export class Transition {
25
- constructor(
26
- private project: Project,
27
- private calculateCmd: Calculate,
28
- ) {}
24
+ constructor(private project: Project) {}
29
25
 
30
26
  /**
31
27
  * Transitions a card from its current state to a new state.
@@ -93,7 +89,7 @@ export class Transition {
93
89
  );
94
90
  }
95
91
 
96
- const actionGuard = new ActionGuard(this.calculateCmd);
92
+ const actionGuard = new ActionGuard(this.project.calculationEngine);
97
93
  await actionGuard.checkPermission('transition', cardKey, transition.name);
98
94
 
99
95
  details.metadata.workflowState = found.toState;
@@ -119,7 +115,7 @@ export class Transition {
119
115
  // Wrapper to run onTransition query.
120
116
  private async transitionChangesQuery(cardKey: string, transition: string) {
121
117
  if (!cardKey || !transition) return undefined;
122
- return this.calculateCmd.runQuery('onTransition', {
118
+ return this.project.calculationEngine.runQuery('onTransition', 'localApp', {
123
119
  cardKey,
124
120
  transition,
125
121
  });
@@ -34,6 +34,7 @@ export class Update {
34
34
  * @param key Property to change in resource JSON
35
35
  * @param value Value for 'key'
36
36
  * @param optionalDetail Additional detail needed for some operations. For example, 'update' needs a new value.
37
+ * @param mappingTable Optional mapping table for workflow state transitions (only used for workflow changes)
37
38
  */
38
39
  public async updateValue<Type>(
39
40
  name: string,
@@ -41,6 +42,7 @@ export class Update {
41
42
  key: string,
42
43
  value: Type,
43
44
  optionalDetail?: Type, // todo: for 'rank' it might be reasonable to accept also 'number'
45
+ mappingTable?: { stateMapping: Record<string, string> },
44
46
  ) {
45
47
  const resource = Project.resourceObject(this.project, resourceName(name));
46
48
  const op: Operation<Type> = {
@@ -60,6 +62,10 @@ export class Update {
60
62
  (op as ChangeOperation<Type>).to = optionalDetail
61
63
  ? optionalDetail
62
64
  : (value as Type);
65
+ // Add mapping table if provided (for workflow changes)
66
+ if (mappingTable) {
67
+ (op as ChangeOperation<Type>).mappingTable = mappingTable;
68
+ }
63
69
  } else if (operation === 'rank') {
64
70
  (op as RankOperation<Type>).newIndex = optionalDetail as number;
65
71
  (op as RankOperation<Type>).target = value;
@@ -42,7 +42,7 @@ import { isTemplateCard } from '../utils/card-utils.js';
42
42
  import { pathExists } from '../utils/file-utils.js';
43
43
  import { Project } from '../containers/project.js';
44
44
  import { readJsonFile } from '../utils/json.js';
45
- import { resourceName } from '../utils/resource-utils.js';
45
+ import { type ResourceName, resourceName } from '../utils/resource-utils.js';
46
46
 
47
47
  const invalidNames = new RegExp(
48
48
  '[<>:"/\\|?*\x00-\x1F]|^(?:aux|con|clock$|nul|prn|com[1-9]|lpt[1-9])$', // eslint-disable-line no-control-regex
@@ -52,7 +52,6 @@ const SHORT_TEXT_MAX_LENGTH = 80;
52
52
 
53
53
  import * as EmailValidator from 'email-validator';
54
54
  import { evaluateMacros } from '../macros/index.js';
55
- import { Calculate } from './calculate.js';
56
55
  const baseDir = dirname(fileURLToPath(import.meta.url));
57
56
  const subFoldersToValidate = ['.cards', 'cardRoot'];
58
57
 
@@ -482,7 +481,8 @@ export class Validate {
482
481
  * returns true if identifier is valid, and false otherwise.
483
482
  */
484
483
  public static isValidIdentifierName(identifier: string): boolean {
485
- const validIdentifier = new RegExp('^[A-Za-z0-9 ._-]+$');
484
+ const validIdentifier = new RegExp('^[A-Za-z0-9._-]+$');
485
+
486
486
  const contentValidated = validIdentifier.test(identifier);
487
487
  const lengthValidated = identifier.length > 0 && identifier.length < 256;
488
488
  const notInvalidIdentifier = !invalidNames.test(identifier);
@@ -547,7 +547,6 @@ export class Validate {
547
547
  } else {
548
548
  const errorMsg: string[] = [];
549
549
  const project = new Project(projectPath);
550
- const calculate = new Calculate(project);
551
550
 
552
551
  // Then, validate that each 'contentSchema' children as well.
553
552
  const result = await this.readAndValidateContentFiles(
@@ -562,7 +561,15 @@ export class Validate {
562
561
  const cards = await project.cards();
563
562
  cards.push(...(await project.allTemplateCards()));
564
563
 
564
+ const cardIds = new Map<string, number>();
565
+
565
566
  for (const card of cards) {
567
+ if (cardIds.has(card.key)) {
568
+ cardIds.set(card.key, (cardIds.get(card.key) || 0) + 1);
569
+ } else {
570
+ cardIds.set(card.key, 1);
571
+ }
572
+
566
573
  if (card.metadata) {
567
574
  // validate card's workflow
568
575
  if (!isTemplateCard(card)) {
@@ -591,15 +598,12 @@ export class Validate {
591
598
 
592
599
  // Validate macros in content
593
600
  if (card.content) {
594
- await evaluateMacros(
595
- card.content,
596
- {
597
- mode: 'validate',
598
- project,
599
- cardKey: card.key,
600
- },
601
- calculate,
602
- );
601
+ await evaluateMacros(card.content, {
602
+ context: 'localApp',
603
+ mode: 'validate',
604
+ project,
605
+ cardKey: card.key,
606
+ });
603
607
  }
604
608
  }
605
609
  if (errorMsg.length) {
@@ -607,6 +611,12 @@ export class Validate {
607
611
  .filter(this.removeDuplicateEntries)
608
612
  .join('\n');
609
613
  }
614
+ // Validate that there are no duplicate card keys
615
+ for (const [key, count] of cardIds) {
616
+ if (count > 1) {
617
+ validationErrors += `Duplicate card key '${key}' found ${count} times\n`;
618
+ }
619
+ }
610
620
  }
611
621
  } catch (error) {
612
622
  validationErrors += errorFunction(error);
@@ -919,6 +929,24 @@ export class Validate {
919
929
  return validationErrors.join('\n');
920
930
  }
921
931
 
932
+ /**
933
+ * Validates a single resource.
934
+ * @param resource Resource to validate
935
+ * @returns string containing all validation errors
936
+ */
937
+ public async validateResource(
938
+ resourceName: ResourceName,
939
+ project: Project,
940
+ ): Promise<string> {
941
+ try {
942
+ const resource = Project.resourceObject(project, resourceName);
943
+ await resource.validate();
944
+ return '';
945
+ } catch (error) {
946
+ return errorFunction(error);
947
+ }
948
+ }
949
+
922
950
  /**
923
951
  * Possibly creates (if no instance exists) and returns an instance of Validate command.
924
952
  * @returns instance of Validate command.
@@ -17,6 +17,8 @@ import { basename, join, sep } from 'node:path';
17
17
  import type { Dirent } from 'node:fs';
18
18
  import { readdir, readFile, writeFile } from 'node:fs/promises';
19
19
 
20
+ import { findParentPath } from '../utils/card-utils.js';
21
+ import { readJsonFile } from '../utils/json.js';
20
22
  import { writeJsonFile } from '../utils/json.js';
21
23
  import { getFilesSync } from '../utils/file-utils.js';
22
24
 
@@ -24,6 +26,7 @@ import { getFilesSync } from '../utils/file-utils.js';
24
26
  import {
25
27
  type CardAttachment,
26
28
  type Card,
29
+ type CardMetadata,
27
30
  CardNameRegEx,
28
31
  type FetchCardDetails,
29
32
  } from '../interfaces/project-interfaces.js';
@@ -375,4 +378,75 @@ export class CardContainer {
375
378
  await writeJsonFile(metadataFile, card.metadata);
376
379
  }
377
380
  }
381
+
382
+ /**
383
+ * Show cards with hierarchy structure from a given path.
384
+ * @param path The path to read cards from
385
+ * @returns an array of cards with proper parent-child relationships.
386
+ */
387
+ protected async showCards(path: string): Promise<Card[]> {
388
+ const cards: Card[] = [];
389
+ const cardPathMap = new Map<string, Card>();
390
+ const entries = await readdir(path, {
391
+ withFileTypes: true,
392
+ recursive: true,
393
+ });
394
+
395
+ // Checks if Dirent folder is a card folder
396
+ function cardFolder(
397
+ entry: Dirent,
398
+ cardPathMap: Map<string, Card>,
399
+ ): Card | undefined {
400
+ const fullPath = join(entry.parentPath, entry.name);
401
+ if (!cardPathMap.has(fullPath)) {
402
+ const newCard: Card = {
403
+ key: entry.name,
404
+ path: fullPath,
405
+ children: [],
406
+ attachments: [],
407
+ };
408
+ cardPathMap.set(fullPath, newCard);
409
+ return newCard;
410
+ }
411
+ }
412
+
413
+ // Process card directories first
414
+ entries
415
+ .filter((entry) => entry.isDirectory() && CardNameRegEx.test(entry.name))
416
+ .forEach((entry) => {
417
+ const card = cardFolder(entry, cardPathMap);
418
+ if (card) cards.push(card);
419
+ });
420
+
421
+ // Process metadata files in parallel
422
+ await Promise.all(
423
+ entries
424
+ .filter(
425
+ (entry) =>
426
+ entry.isFile() && entry.name === CardContainer.cardMetadataFile,
427
+ )
428
+ .map(async (entry) => {
429
+ const parentCard = cardPathMap.get(entry.parentPath);
430
+ if (!parentCard) return;
431
+ parentCard.metadata = (await readJsonFile(
432
+ join(entry.parentPath, entry.name),
433
+ )) as CardMetadata;
434
+ }),
435
+ );
436
+
437
+ // Finally, build the card hierarchy
438
+ Array.from(cardPathMap.entries()).map(([cardPath, card]) => {
439
+ const parentPath = findParentPath(cardPath);
440
+ if (!parentPath) return;
441
+ const parentCard = cardPathMap.get(parentPath);
442
+ if (!parentCard) return;
443
+
444
+ parentCard.children.push(card);
445
+ const index = cards.indexOf(card);
446
+ if (index > -1) {
447
+ cards.splice(index, 1);
448
+ }
449
+ });
450
+ return cards;
451
+ }
378
452
  }