@cyberismo/data-handler 0.0.13 → 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 (262) 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 +13 -17
  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.d.ts +3 -3
  9. package/dist/commands/create.js +20 -81
  10. package/dist/commands/create.js.map +1 -1
  11. package/dist/commands/edit.d.ts +12 -25
  12. package/dist/commands/edit.js +25 -74
  13. package/dist/commands/edit.js.map +1 -1
  14. package/dist/commands/export.js +4 -17
  15. package/dist/commands/export.js.map +1 -1
  16. package/dist/commands/fetch.js +2 -1
  17. package/dist/commands/fetch.js.map +1 -1
  18. package/dist/commands/import.js +3 -5
  19. package/dist/commands/import.js.map +1 -1
  20. package/dist/commands/move.d.ts +1 -2
  21. package/dist/commands/move.js +108 -146
  22. package/dist/commands/move.js.map +1 -1
  23. package/dist/commands/remove.js +15 -49
  24. package/dist/commands/remove.js.map +1 -1
  25. package/dist/commands/rename.d.ts +1 -0
  26. package/dist/commands/rename.js +13 -7
  27. package/dist/commands/rename.js.map +1 -1
  28. package/dist/commands/show.d.ts +7 -25
  29. package/dist/commands/show.js +39 -113
  30. package/dist/commands/show.js.map +1 -1
  31. package/dist/commands/transition.js +27 -30
  32. package/dist/commands/transition.js.map +1 -1
  33. package/dist/commands/update.d.ts +5 -3
  34. package/dist/commands/update.js +19 -5
  35. package/dist/commands/update.js.map +1 -1
  36. package/dist/commands/validate.d.ts +3 -3
  37. package/dist/commands/validate.js +20 -27
  38. package/dist/commands/validate.js.map +1 -1
  39. package/dist/containers/card-container.d.ts +87 -24
  40. package/dist/containers/card-container.js +183 -279
  41. package/dist/containers/card-container.js.map +1 -1
  42. package/dist/containers/project/calculation-engine.d.ts +6 -0
  43. package/dist/containers/project/calculation-engine.js +36 -29
  44. package/dist/containers/project/calculation-engine.js.map +1 -1
  45. package/dist/containers/project/card-cache.d.ts +146 -0
  46. package/dist/containers/project/card-cache.js +411 -0
  47. package/dist/containers/project/card-cache.js.map +1 -0
  48. package/dist/containers/project/resource-collector.d.ts +24 -1
  49. package/dist/containers/project/resource-collector.js +8 -1
  50. package/dist/containers/project/resource-collector.js.map +1 -1
  51. package/dist/containers/project.d.ts +119 -84
  52. package/dist/containers/project.js +423 -253
  53. package/dist/containers/project.js.map +1 -1
  54. package/dist/containers/template.d.ts +15 -31
  55. package/dist/containers/template.js +97 -104
  56. package/dist/containers/template.js.map +1 -1
  57. package/dist/index.d.ts +1 -0
  58. package/dist/index.js +1 -0
  59. package/dist/index.js.map +1 -1
  60. package/dist/interfaces/folder-content-interfaces.d.ts +12 -5
  61. package/dist/interfaces/folder-content-interfaces.js +5 -3
  62. package/dist/interfaces/folder-content-interfaces.js.map +1 -1
  63. package/dist/interfaces/macros.d.ts +1 -0
  64. package/dist/interfaces/macros.js +1 -1
  65. package/dist/interfaces/macros.js.map +1 -1
  66. package/dist/interfaces/project-interfaces.d.ts +16 -10
  67. package/dist/interfaces/project-interfaces.js +10 -8
  68. package/dist/interfaces/project-interfaces.js.map +1 -1
  69. package/dist/interfaces/resource-interfaces.d.ts +21 -22
  70. package/dist/interfaces/resource-interfaces.js +3 -0
  71. package/dist/interfaces/resource-interfaces.js.map +1 -1
  72. package/dist/macros/common.d.ts +10 -10
  73. package/dist/macros/createCards/index.d.ts +0 -13
  74. package/dist/macros/createCards/index.js.map +1 -1
  75. package/dist/macros/createCards/types.d.ts +44 -0
  76. package/dist/macros/createCards/types.js +15 -0
  77. package/dist/macros/createCards/types.js.map +1 -0
  78. package/dist/macros/graph/index.d.ts +2 -6
  79. package/dist/macros/graph/index.js +2 -2
  80. package/dist/macros/graph/index.js.map +1 -1
  81. package/dist/macros/graph/types.d.ts +23 -0
  82. package/dist/macros/graph/types.js +15 -0
  83. package/dist/macros/graph/types.js.map +1 -0
  84. package/dist/macros/image/index.d.ts +8 -16
  85. package/dist/macros/image/index.js +36 -33
  86. package/dist/macros/image/index.js.map +1 -1
  87. package/dist/macros/image/types.d.ts +38 -0
  88. package/dist/macros/image/types.js +15 -0
  89. package/dist/macros/image/types.js.map +1 -0
  90. package/dist/macros/include/index.d.ts +1 -6
  91. package/dist/macros/include/index.js +4 -7
  92. package/dist/macros/include/index.js.map +1 -1
  93. package/dist/macros/include/types.d.ts +31 -0
  94. package/dist/macros/include/types.js +15 -0
  95. package/dist/macros/include/types.js.map +1 -0
  96. package/dist/macros/percentage/index.d.ts +0 -6
  97. package/dist/macros/percentage/index.js.map +1 -1
  98. package/dist/macros/percentage/types.d.ts +31 -0
  99. package/dist/macros/percentage/types.js +15 -0
  100. package/dist/macros/percentage/types.js.map +1 -0
  101. package/dist/macros/report/index.d.ts +0 -3
  102. package/dist/macros/report/index.js.map +1 -1
  103. package/dist/macros/report/types.d.ts +19 -0
  104. package/dist/macros/report/types.js +15 -0
  105. package/dist/macros/report/types.js.map +1 -0
  106. package/dist/macros/scoreCard/index.d.ts +0 -6
  107. package/dist/macros/scoreCard/index.js.map +1 -1
  108. package/dist/macros/scoreCard/types.d.ts +31 -0
  109. package/dist/macros/scoreCard/types.js +15 -0
  110. package/dist/macros/scoreCard/types.js.map +1 -0
  111. package/dist/macros/types.d.ts +25 -0
  112. package/dist/macros/types.js +2 -0
  113. package/dist/macros/types.js.map +1 -0
  114. package/dist/macros/vega/index.d.ts +0 -4
  115. package/dist/macros/vega/index.js.map +1 -1
  116. package/dist/macros/vega/types.d.ts +20 -0
  117. package/dist/macros/vega/types.js +2 -0
  118. package/dist/macros/vega/types.js.map +1 -0
  119. package/dist/macros/vegalite/index.d.ts +0 -4
  120. package/dist/macros/vegalite/index.js.map +1 -1
  121. package/dist/macros/vegalite/types.d.ts +20 -0
  122. package/dist/macros/vegalite/types.js +15 -0
  123. package/dist/macros/vegalite/types.js.map +1 -0
  124. package/dist/macros/xref/index.d.ts +0 -3
  125. package/dist/macros/xref/index.js +5 -14
  126. package/dist/macros/xref/index.js.map +1 -1
  127. package/dist/macros/xref/types.d.ts +19 -0
  128. package/dist/macros/xref/types.js +15 -0
  129. package/dist/macros/xref/types.js.map +1 -0
  130. package/dist/module-manager.js +4 -4
  131. package/dist/module-manager.js.map +1 -1
  132. package/dist/project-settings.js.map +1 -1
  133. package/dist/resources/calculation-resource.d.ts +43 -0
  134. package/dist/resources/calculation-resource.js +75 -0
  135. package/dist/resources/calculation-resource.js.map +1 -0
  136. package/dist/resources/card-type-resource.d.ts +4 -21
  137. package/dist/resources/card-type-resource.js +13 -44
  138. package/dist/resources/card-type-resource.js.map +1 -1
  139. package/dist/resources/create-defaults.d.ts +13 -6
  140. package/dist/resources/create-defaults.js +19 -5
  141. package/dist/resources/create-defaults.js.map +1 -1
  142. package/dist/resources/field-type-resource.d.ts +4 -21
  143. package/dist/resources/field-type-resource.js +14 -38
  144. package/dist/resources/field-type-resource.js.map +1 -1
  145. package/dist/resources/file-resource.d.ts +12 -29
  146. package/dist/resources/file-resource.js +19 -287
  147. package/dist/resources/file-resource.js.map +1 -1
  148. package/dist/resources/folder-resource.d.ts +32 -51
  149. package/dist/resources/folder-resource.js +68 -96
  150. package/dist/resources/folder-resource.js.map +1 -1
  151. package/dist/resources/graph-model-resource.d.ts +5 -33
  152. package/dist/resources/graph-model-resource.js +8 -61
  153. package/dist/resources/graph-model-resource.js.map +1 -1
  154. package/dist/resources/graph-view-resource.d.ts +5 -28
  155. package/dist/resources/graph-view-resource.js +6 -45
  156. package/dist/resources/graph-view-resource.js.map +1 -1
  157. package/dist/resources/link-type-resource.d.ts +4 -21
  158. package/dist/resources/link-type-resource.js +6 -31
  159. package/dist/resources/link-type-resource.js.map +1 -1
  160. package/dist/resources/report-resource.d.ts +5 -17
  161. package/dist/resources/report-resource.js +6 -44
  162. package/dist/resources/report-resource.js.map +1 -1
  163. package/dist/resources/resource-object.d.ts +58 -23
  164. package/dist/resources/resource-object.js +307 -26
  165. package/dist/resources/resource-object.js.map +1 -1
  166. package/dist/resources/template-resource.d.ts +4 -15
  167. package/dist/resources/template-resource.js +10 -25
  168. package/dist/resources/template-resource.js.map +1 -1
  169. package/dist/resources/workflow-resource.d.ts +4 -23
  170. package/dist/resources/workflow-resource.js +12 -38
  171. package/dist/resources/workflow-resource.js.map +1 -1
  172. package/dist/utils/card-utils.d.ts +69 -19
  173. package/dist/utils/card-utils.js +179 -30
  174. package/dist/utils/card-utils.js.map +1 -1
  175. package/dist/utils/clingo-facts.js +11 -3
  176. package/dist/utils/clingo-facts.js.map +1 -1
  177. package/dist/utils/clingo-parser.js +1 -1
  178. package/dist/utils/clingo-parser.js.map +1 -1
  179. package/dist/utils/constants.d.ts +2 -0
  180. package/dist/utils/constants.js +5 -0
  181. package/dist/utils/constants.js.map +1 -1
  182. package/dist/utils/csv.js +1 -1
  183. package/dist/utils/csv.js.map +1 -1
  184. package/dist/utils/error-utils.d.ts +34 -0
  185. package/dist/utils/error-utils.js +56 -0
  186. package/dist/utils/error-utils.js.map +1 -0
  187. package/dist/utils/log-utils.d.ts +0 -27
  188. package/dist/utils/log-utils.js +0 -58
  189. package/dist/utils/log-utils.js.map +1 -1
  190. package/dist/utils/user-preferences.js +6 -3
  191. package/dist/utils/user-preferences.js.map +1 -1
  192. package/package.json +5 -5
  193. package/src/card-metadata-updater.ts +3 -5
  194. package/src/command-handler.ts +14 -19
  195. package/src/command-manager.ts +4 -3
  196. package/src/commands/create.ts +28 -112
  197. package/src/commands/edit.ts +27 -118
  198. package/src/commands/export.ts +8 -29
  199. package/src/commands/fetch.ts +2 -1
  200. package/src/commands/import.ts +4 -6
  201. package/src/commands/move.ts +144 -179
  202. package/src/commands/remove.ts +12 -54
  203. package/src/commands/rename.ts +22 -7
  204. package/src/commands/show.ts +51 -156
  205. package/src/commands/transition.ts +30 -33
  206. package/src/commands/update.ts +27 -9
  207. package/src/commands/validate.ts +22 -37
  208. package/src/containers/card-container.ts +200 -360
  209. package/src/containers/project/calculation-engine.ts +43 -33
  210. package/src/containers/project/card-cache.ts +497 -0
  211. package/src/containers/project/resource-collector.ts +9 -1
  212. package/src/containers/project.ts +533 -328
  213. package/src/containers/template.ts +109 -127
  214. package/src/index.ts +1 -0
  215. package/src/interfaces/folder-content-interfaces.ts +23 -5
  216. package/src/interfaces/macros.ts +2 -0
  217. package/src/interfaces/project-interfaces.ts +19 -10
  218. package/src/interfaces/resource-interfaces.ts +22 -24
  219. package/src/macros/createCards/index.ts +1 -12
  220. package/src/macros/createCards/types.ts +46 -0
  221. package/src/macros/graph/index.ts +3 -7
  222. package/src/macros/graph/types.ts +24 -0
  223. package/src/macros/image/index.ts +50 -61
  224. package/src/macros/image/types.ts +39 -0
  225. package/src/macros/include/index.ts +6 -15
  226. package/src/macros/include/types.ts +32 -0
  227. package/src/macros/percentage/index.ts +1 -7
  228. package/src/macros/percentage/types.ts +32 -0
  229. package/src/macros/report/index.ts +1 -4
  230. package/src/macros/report/types.ts +20 -0
  231. package/src/macros/scoreCard/index.ts +1 -7
  232. package/src/macros/scoreCard/types.ts +32 -0
  233. package/src/macros/types.ts +48 -0
  234. package/src/macros/vega/index.ts +1 -4
  235. package/src/macros/vega/types.ts +21 -0
  236. package/src/macros/vegalite/index.ts +1 -4
  237. package/src/macros/vegalite/types.ts +22 -0
  238. package/src/macros/xref/index.ts +6 -20
  239. package/src/macros/xref/types.ts +20 -0
  240. package/src/module-manager.ts +5 -5
  241. package/src/project-settings.ts +1 -1
  242. package/src/resources/calculation-resource.ts +101 -0
  243. package/src/resources/card-type-resource.ts +24 -59
  244. package/src/resources/create-defaults.ts +21 -5
  245. package/src/resources/field-type-resource.ts +22 -51
  246. package/src/resources/file-resource.ts +27 -403
  247. package/src/resources/folder-resource.ts +99 -125
  248. package/src/resources/graph-model-resource.ts +17 -74
  249. package/src/resources/graph-view-resource.ts +14 -54
  250. package/src/resources/link-type-resource.ts +13 -40
  251. package/src/resources/report-resource.ts +17 -57
  252. package/src/resources/resource-object.ts +454 -39
  253. package/src/resources/template-resource.ts +16 -29
  254. package/src/resources/workflow-resource.ts +26 -50
  255. package/src/utils/card-utils.ts +217 -31
  256. package/src/utils/clingo-facts.ts +13 -3
  257. package/src/utils/clingo-parser.ts +1 -1
  258. package/src/utils/constants.ts +7 -0
  259. package/src/utils/csv.ts +1 -1
  260. package/src/utils/error-utils.ts +62 -0
  261. package/src/utils/log-utils.ts +0 -68
  262. package/src/utils/user-preferences.ts +7 -3
@@ -13,28 +13,23 @@
13
13
  */
14
14
 
15
15
  // node
16
- import { basename, join, sep } from 'node:path';
17
- import type { Dirent } from 'node:fs';
18
- import { readdir, readFile, writeFile } from 'node:fs/promises';
16
+ import { join } from 'node:path';
17
+ import { writeFile } from 'node:fs/promises';
19
18
 
20
- import { findParentPath } from '../utils/card-utils.js';
21
- import { getFilesSync } from '../utils/file-utils.js';
22
- import { readJsonFile } from '../utils/json.js';
19
+ import { CardCache } from './project/card-cache.js';
20
+ import { cardPathParts } from '../utils/card-utils.js';
21
+ import { deleteDir } from '../utils/file-utils.js';
23
22
  import { writeJsonFile } from '../utils/json.js';
24
23
 
25
- // interfaces
26
- import {
27
- type CardAttachment,
28
- type Card,
29
- type CardMetadata,
30
- CardNameRegEx,
31
- type FetchCardDetails,
24
+ import type {
25
+ CardAttachment,
26
+ Card,
27
+ FetchCardDetails,
32
28
  } from '../interfaces/project-interfaces.js';
33
29
 
34
- // asciidoctor
35
30
  import asciidoctor from '@asciidoctor/core';
36
31
 
37
- import mime from 'mime-types';
32
+ import { ROOT } from '../utils/constants.js';
38
33
 
39
34
  /**
40
35
  * Card container base class. Used for both Project and Template.
@@ -42,411 +37,256 @@ import mime from 'mime-types';
42
37
  */
43
38
  export class CardContainer {
44
39
  public basePath: string;
40
+ protected cardCache: CardCache;
45
41
  protected containerName: string;
42
+ protected prefix: string;
46
43
 
47
- static projectConfigFileName = 'cardsConfig.json';
48
- static cardMetadataFile = 'index.json';
49
44
  static cardContentFile = 'index.adoc';
45
+ static cardMetadataFile = 'index.json';
46
+ static projectConfigFileName = 'cardsConfig.json';
50
47
  static schemaContentFile = '.schema';
51
48
 
52
- constructor(path: string, name: string) {
49
+ constructor(path: string, prefix: string, name: string) {
53
50
  this.basePath = path;
54
51
  this.containerName = name;
52
+ this.prefix = prefix;
53
+ this.cardCache = new CardCache(this.prefix);
55
54
  }
56
55
 
57
- // Lists all direct children.
58
- private async childrenCards(cardPath: string, details?: FetchCardDetails) {
59
- const containerCards: Card[] = [];
60
- await this.doCollectCards(cardPath, containerCards, details, true);
61
- return containerCards;
62
- }
63
-
64
- // Function collects attachments from all cards in one folder.
65
- private async doCollectAttachments(
66
- folder: string,
67
- attachments: CardAttachment[],
68
- ): Promise<CardAttachment[]> {
69
- const currentPaths: string[] = [];
70
- let entries: Dirent[] = [];
71
- try {
72
- entries = (await readdir(folder, { withFileTypes: true })).filter(
73
- (item) => item.isDirectory(),
74
- );
75
- } catch {
76
- // ignore throws, if currentPaths does not have more values, recursion will stop
56
+ // Filters one card to only include the details requested.
57
+ private filterCardDetails(
58
+ card: Card,
59
+ details: FetchCardDetails = {
60
+ attachments: true,
61
+ children: true,
62
+ content: true,
63
+ metadata: true,
64
+ parent: true,
65
+ },
66
+ ): Card {
67
+ const filteredCard: Card = {
68
+ key: card.key,
69
+ path: card.path,
70
+ children: details.children ? card.children : [],
71
+ attachments: details.attachments ? card.attachments : [],
72
+ };
73
+
74
+ if (details.content) {
75
+ filteredCard.content = card.content;
77
76
  }
78
- for (const entry of entries) {
79
- // Investigate the content of card folders' attachment folders, but do not continue to children cards.
80
- // For each attachment folder, collect all files.
81
- if (CardNameRegEx.test(entry.name)) {
82
- currentPaths.push(join(entry.parentPath, entry.name));
83
- } else if (entry.name === 'c') {
84
- continue;
85
- } else if (entry.name === 'a') {
86
- const attachmentFolder = join(entry.parentPath, entry.name);
87
- const cardItem = basename(entry.parentPath) || '';
88
- let entryAttachments: Dirent[] = [];
89
- try {
90
- entryAttachments = await readdir(attachmentFolder, {
91
- withFileTypes: true,
92
- });
93
- } catch {
94
- // ignore readdir errors
95
- }
96
- entryAttachments.forEach((attachment) =>
97
- attachments.push({
98
- card: cardItem,
99
- fileName: attachment.name,
100
- path: attachment.parentPath,
101
- mimeType: mime.lookup(attachment.name) || null,
102
- }),
103
- );
104
- }
77
+ if (details.metadata) {
78
+ filteredCard.metadata = structuredClone(card.metadata);
105
79
  }
106
-
107
- if (currentPaths) {
108
- const promises = currentPaths.map((item) =>
109
- this.doCollectAttachments(item, attachments),
110
- );
111
- await Promise.all(promises);
112
- }
113
- return attachments;
114
- }
115
-
116
- // Collects all cards from container.
117
- private async doCollectCards(
118
- path: string,
119
- cards: Card[],
120
- details: FetchCardDetails = {},
121
- directChildrenOnly: boolean = false,
122
- ): Promise<Card[]> {
123
- let entries = [];
124
- try {
125
- entries = await readdir(path, { withFileTypes: true });
126
- } catch {
127
- return cards;
80
+ if (details.parent) {
81
+ filteredCard.parent = card.parent;
128
82
  }
129
- let finish = false;
130
- const currentPaths: string[] = [];
131
-
132
- for (const entry of entries) {
133
- if (entry.isDirectory()) {
134
- const currentPath = join(entry.parentPath, entry.name);
135
- currentPaths.push(currentPath);
136
- if (CardNameRegEx.test(entry.name)) {
137
- if (directChildrenOnly) {
138
- // Make recursion stop on the level where first children cards are found.
139
- finish = true;
140
- }
141
-
142
- const attachmentFiles: CardAttachment[] = [];
143
- const promiseContainer = [
144
- this.getContent(currentPath, details.content),
145
- this.getMetadata(currentPath, details.metadata),
146
- this.getChildren(currentPath, details),
147
- this.getAttachments(
148
- currentPath,
149
- attachmentFiles,
150
- details.attachments,
151
- ),
152
- ];
153
- const [cardContent, cardMetadata, cardChildren] =
154
- await Promise.all(promiseContainer);
155
-
156
- cards.push({
157
- key: entry.name,
158
- path: currentPath,
159
- children: details.children ? (cardChildren as Card[]) : [],
160
- attachments: details.attachments ? [...attachmentFiles] : [],
161
- ...(details.content && { content: cardContent as string }),
162
- ...(details.metadata && {
163
- metadata: JSON.parse(cardMetadata as string),
164
- }),
165
- ...(details.parent && { parent: this.parentCard(currentPath) }),
166
- });
167
- }
168
- }
83
+ if (details.calculations) {
84
+ filteredCard.calculations = card.calculations;
169
85
  }
170
86
 
171
- // Continue collecting cards from children
172
- if (!finish && currentPaths) {
173
- const promises = currentPaths.map((item) =>
174
- this.doCollectCards(item, cards, details, directChildrenOnly),
175
- );
176
- await Promise.all(promises);
177
- }
178
- return cards;
87
+ return filteredCard;
179
88
  }
180
89
 
181
- // Find specific card
182
- private async doFindCard(
183
- path: string,
184
- cardKey: string,
185
- details: FetchCardDetails = {},
186
- foundCards: Card[],
187
- ): Promise<Card[]> {
188
- const entries = await readdir(path, { withFileTypes: true });
189
- let asciiDocProcessor;
190
- // optimization: do not create AsciiDoctor Processor, unless it is needed.
191
- if (details.contentType && details.contentType === 'html') {
192
- asciiDocProcessor = asciidoctor();
193
- }
194
-
195
- for (const entry of entries) {
196
- if (entry.isDirectory()) {
197
- const currentPath = join(entry.parentPath, entry.name);
198
- if (entry.name === cardKey) {
199
- const attachmentFiles: CardAttachment[] = [];
200
- const promiseContainer = [
201
- this.getContent(currentPath, details.content),
202
- this.getMetadata(currentPath, details.metadata),
203
- this.getChildren(currentPath, details),
204
- this.getAttachments(
205
- currentPath,
206
- attachmentFiles,
207
- details.attachments,
208
- ),
209
- ];
210
- const [cardContent, cardMetadata, cardChildren] =
211
- await Promise.all(promiseContainer);
212
-
213
- const content =
214
- details.contentType && details.contentType === 'html'
215
- ? asciiDocProcessor?.convert(cardContent as string)
216
- : cardContent;
217
-
218
- foundCards.push({
219
- key: entry.name,
220
- path: currentPath,
221
- children: details.children ? (cardChildren as Card[]) : [],
222
- attachments: details.attachments ? [...attachmentFiles] : [],
223
- ...(details.content && { content: content as string }),
224
- ...(details.metadata && {
225
- metadata: JSON.parse(cardMetadata as string),
226
- }),
227
- ...(details.parent && { parent: this.parentCard(currentPath) }),
228
- ...(details.calculations && { calculations: [] }),
229
- });
230
- break; //optimization - there can only be one.
231
- }
232
- // Only continue, if the card has not been found.
233
- if (foundCards.length === 0) {
234
- await this.doFindCard(currentPath, cardKey, details, foundCards);
235
- }
236
- }
237
- }
238
- return foundCards;
239
- }
240
-
241
- // Gets conditionally attachments
242
- private async getAttachments(
243
- currentPath: string,
244
- files: CardAttachment[],
245
- include?: boolean,
246
- ): Promise<string | CardAttachment[] | Card[]> {
247
- return include ? this.doCollectAttachments(currentPath, files) : [];
248
- }
249
-
250
- // Gets conditionally children
251
- private async getChildren(
252
- currentPath: string,
253
- details: FetchCardDetails = {},
254
- ): Promise<string | CardAttachment[] | Card[]> {
255
- return details.children ? this.childrenCards(currentPath, details) : [];
256
- }
257
-
258
- // Gets conditionally content
259
- private async getContent(
260
- currentPath: string,
261
- include?: boolean,
262
- ): Promise<string | CardAttachment[] | Card[]> {
263
- return include
264
- ? readFile(join(currentPath, CardContainer.cardContentFile), {
265
- encoding: 'utf-8',
266
- })
267
- : '';
90
+ // Filters cards to only include the details requested.
91
+ private filterCardsDetails(
92
+ cards: Card[],
93
+ details?: FetchCardDetails,
94
+ ): Card[] {
95
+ return cards.map((card) => {
96
+ return this.filterCardDetails(card, details);
97
+ });
268
98
  }
269
99
 
270
- // Gets conditionally metadata
271
- private async getMetadata(
272
- currentPath: string,
273
- include?: boolean,
274
- ): Promise<string> {
275
- let metadata = include
276
- ? await readFile(join(currentPath, CardContainer.cardMetadataFile), {
277
- encoding: 'utf-8',
278
- })
279
- : '';
280
- metadata = this.injectLinksIfMissing(metadata);
281
- return metadata;
100
+ /**
101
+ * Determines the container from a given path.
102
+ * @param path The filesystem path to analyze
103
+ * @returns Location string: 'project' for project cards, template name for template cards
104
+ */
105
+ protected determineContainer(path: string): string {
106
+ return cardPathParts(this.prefix, path).template || 'project';
282
107
  }
283
108
 
284
- // Injects 'links' member - if it is missing - to a string representation of a card.
285
- private injectLinksIfMissing(metadata: string): string {
286
- if (metadata !== '' && !metadata.includes('"links":')) {
287
- const end = metadata.lastIndexOf('}');
288
- metadata = metadata.slice(0, end - 1) + ',\n "links": []\n' + '}';
289
- }
290
- return metadata;
291
- }
109
+ /**
110
+ * Populates the card cache with all cards from all locations.
111
+ */
112
+ protected async populateCardsCache(): Promise<void> {}
292
113
 
293
- // Finds parent
294
- private parentCard(cardPath: string) {
295
- const pathParts = cardPath.split(sep);
296
- if (
297
- pathParts.at(pathParts.length - 2) === 'cardRoot' ||
298
- (pathParts.length > 3 &&
299
- pathParts.at(pathParts.length - 4) === 'templates')
300
- ) {
301
- return 'root';
302
- } else {
303
- return pathParts.at(pathParts.length - 3);
304
- }
305
- }
114
+ /**
115
+ * Populates template cards into the cache.
116
+ */
117
+ protected async populateTemplateCards(): Promise<void> {}
306
118
 
307
- // Lists all attachments from container.
308
- protected async attachments(path: string): Promise<CardAttachment[]> {
119
+ /**
120
+ * Lists all attachments from the container.
121
+ * @param path Path where attachments should be collected.
122
+ * @returns attachments from the container.
123
+ */
124
+ protected attachments(path: string): CardAttachment[] {
309
125
  const attachments: CardAttachment[] = [];
310
- const cards: Card[] = [];
311
- await this.doCollectCards(path, cards, { attachments: true });
312
126
 
313
- cards.forEach((card) => {
314
- if (card.attachments) {
315
- attachments.push(...card.attachments.flat());
127
+ const targetLocation = this.determineContainer(path);
128
+ const cards = [...this.cardCache.getCards()];
129
+ const filteredCards = cards.filter((card) => {
130
+ if (card.attachments.length === 0) {
131
+ return false;
316
132
  }
133
+ return card.location === targetLocation;
317
134
  });
318
135
 
136
+ filteredCards.forEach((item) => attachments.push(...item.attachments));
319
137
  return attachments;
320
138
  }
321
139
 
322
- // Lists all cards from container.
323
- protected async cards(
324
- path: string,
325
- details: FetchCardDetails = {},
326
- directChildrenOnly: boolean = false,
327
- ): Promise<Card[]> {
328
- const containerCards: Card[] = [];
329
- await this.doCollectCards(
330
- path,
331
- containerCards,
332
- details,
333
- directChildrenOnly,
334
- );
335
- return containerCards;
140
+ /**
141
+ * Shows all cards from the container with the given details (by default all of them).
142
+ * @param path Path where cards should be listed.
143
+ * @param details Which details of the card should be included
144
+ * @returns all cards from the container
145
+ */
146
+ protected cards(path: string, details?: FetchCardDetails): Card[] {
147
+ if (!this.cardCache.isPopulated) {
148
+ throw new Error('Cards cache is not populated!');
149
+ }
150
+
151
+ const targetLocation = this.determineContainer(path);
152
+ const relevantCards = this.cardCache
153
+ .getCards()
154
+ .filter((cachedCard) => cachedCard.location === targetLocation);
155
+ return this.filterCardsDetails(relevantCards, details);
336
156
  }
337
157
 
338
- // Finds a specific card.
339
- protected async findCard(
340
- path: string,
341
- cardKey: string,
342
- details: FetchCardDetails = {},
343
- ): Promise<Card | undefined> {
344
- const foundCards: Card[] = [];
345
- await this.doFindCard(path, cardKey, details, foundCards);
346
- return foundCards.at(0);
158
+ /**
159
+ * Finds a specific card.
160
+ * @param cardKey Card key to find
161
+ * @param details Card details to be included in the card
162
+ * @throws if card does not exist in the container
163
+ */
164
+ protected findCard(cardKey: string, details?: FetchCardDetails): Card {
165
+ const cachedCard = this.cardCache.getCard(cardKey);
166
+ if (cachedCard) {
167
+ // Apply content type transformation if needed
168
+ const content = cachedCard.content;
169
+ if (details?.contentType === 'html' && content) {
170
+ const processor = asciidoctor();
171
+ processor.convert(content) as string;
172
+ }
173
+ return this.filterCardDetails(cachedCard, details);
174
+ }
175
+ throw new Error(`Card '${cardKey}' does not exist in the project`);
347
176
  }
348
177
 
349
- // Checks if container has the specified card.
350
- protected hasCard(cardKey: string, path: string): boolean {
351
- const allFiles = getFilesSync(path);
352
- const cardIndexJsonFile = join(cardKey, CardContainer.cardMetadataFile);
353
- const found = allFiles.findIndex((file) =>
354
- file.includes(cardIndexJsonFile),
355
- );
356
- return found !== -1;
178
+ /**
179
+ * Removes a card. If card has children, they are removed as well.
180
+ * @param cardKey Card key to remove.
181
+ * @returns true, if card was removed; false otherwise
182
+ */
183
+ protected async removeCard(cardKey: string): Promise<boolean> {
184
+ const card = this.cardCache.getCard(cardKey);
185
+ if (card) {
186
+ // Children must removed first
187
+ const children = card.children;
188
+ for (const child of children) {
189
+ await this.removeCard(child);
190
+ }
191
+ await deleteDir(card.path);
192
+ return this.cardCache.deleteCard(cardKey);
193
+ }
194
+ return false;
357
195
  }
358
196
 
359
- // Persists card content.
197
+ /**
198
+ * Persists the whole card.
199
+ * @param card Card to persist
200
+ */
360
201
  protected async saveCard(card: Card) {
361
202
  await this.saveCardContent(card);
362
203
  await this.saveCardMetadata(card);
363
204
  }
364
205
 
365
- // Persists card metadata.
366
- protected async saveCardContent(card: Card) {
206
+ /**
207
+ * Persists card content.
208
+ * @param card Card to persist.
209
+ * @returns true if card was updated; false otherwise.
210
+ */
211
+ protected async saveCardContent(card: Card): Promise<boolean> {
367
212
  if (card.content != null) {
368
213
  const contentFile = join(card.path, CardContainer.cardContentFile);
369
214
  await writeFile(contentFile, card.content);
215
+ return this.cardCache.updateCardContent(card.key, card.content);
370
216
  }
217
+ return false;
371
218
  }
372
219
 
373
- // Persists card metadata.
374
- protected async saveCardMetadata(card: Card) {
220
+ /**
221
+ * Persists card metadata.
222
+ * @param card Card to persist
223
+ * @returns true if card was updated; false otherwise.
224
+ */
225
+ protected async saveCardMetadata(card: Card): Promise<boolean> {
375
226
  if (card.metadata != null) {
376
227
  const metadataFile = join(card.path, CardContainer.cardMetadataFile);
377
228
  card.metadata!.lastUpdated = new Date().toISOString();
378
229
  await writeJsonFile(metadataFile, card.metadata);
230
+ return this.cardCache.updateCardMetadata(card.key, card.metadata);
379
231
  }
232
+ return false;
380
233
  }
381
234
 
382
235
  /**
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.
236
+ * Show root cards from a given path.
237
+ * @param path The path to get cards from
238
+ * @returns an array of root-level cards (each with their children populated).
386
239
  */
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
- });
240
+ protected showCards(path: string): Card[] {
241
+ const container = this.determineContainer(path);
242
+ const rootCards: Card[] = [];
243
+ const relevantCards = Array.from(this.cardCache.getCards()).filter(
244
+ (cachedCard) => cachedCard.location === container,
245
+ );
394
246
 
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: [],
247
+ relevantCards.forEach((card) => {
248
+ if (
249
+ card.parent === ROOT ||
250
+ !card.parent ||
251
+ !relevantCards.find((cachedCard) => cachedCard.key === card.parent)
252
+ ) {
253
+ const cardWithChildren: Card = {
254
+ ...card,
255
+ children: card.children,
407
256
  };
408
- cardPathMap.set(fullPath, newCard);
409
- return newCard;
257
+ rootCards.push(cardWithChildren);
410
258
  }
411
- }
259
+ });
412
260
 
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
- );
261
+ return rootCards;
262
+ }
436
263
 
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;
264
+ /**
265
+ * Checks if container has the specified card.
266
+ * @param cardKey Card key to check
267
+ * @return true, if card is in the container
268
+ */
269
+ public hasCard(cardKey: string): boolean {
270
+ return this.cardCache.hasCard(cardKey);
271
+ }
272
+
273
+ /**
274
+ * Checks if container has the specified project card.
275
+ * @param cardKey Card key to check
276
+ * @return true, if card is in the container
277
+ */
278
+ public hasProjectCard(cardKey: string): boolean {
279
+ const cachedCard = this.cardCache.getCard(cardKey);
280
+ return cachedCard ? cachedCard.location === 'project' : false;
281
+ }
282
+
283
+ /**
284
+ * Checks if container has the specified template card.
285
+ * @param cardKey Card key to check
286
+ * @return true, if card is in the container
287
+ */
288
+ public hasTemplateCard(cardKey: string): boolean {
289
+ const cachedCard = this.cardCache.getCard(cardKey);
290
+ return cachedCard ? cachedCard.location !== 'project' : false;
451
291
  }
452
292
  }