@cyberismo/data-handler 0.0.15 → 0.0.17

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 (191) hide show
  1. package/dist/card-metadata-updater.js +7 -1
  2. package/dist/card-metadata-updater.js.map +1 -1
  3. package/dist/command-handler.d.ts +4 -0
  4. package/dist/command-handler.js +22 -8
  5. package/dist/command-handler.js.map +1 -1
  6. package/dist/command-manager.d.ts +24 -1
  7. package/dist/command-manager.js +31 -7
  8. package/dist/command-manager.js.map +1 -1
  9. package/dist/commands/create.d.ts +1 -1
  10. package/dist/commands/create.js +34 -36
  11. package/dist/commands/create.js.map +1 -1
  12. package/dist/commands/export.d.ts +11 -2
  13. package/dist/commands/export.js +54 -41
  14. package/dist/commands/export.js.map +1 -1
  15. package/dist/commands/fetch.d.ts +8 -0
  16. package/dist/commands/fetch.js +101 -23
  17. package/dist/commands/fetch.js.map +1 -1
  18. package/dist/commands/import.d.ts +14 -3
  19. package/dist/commands/import.js +27 -10
  20. package/dist/commands/import.js.map +1 -1
  21. package/dist/commands/move.js +0 -1
  22. package/dist/commands/move.js.map +1 -1
  23. package/dist/commands/remove.d.ts +11 -2
  24. package/dist/commands/remove.js +15 -5
  25. package/dist/commands/remove.js.map +1 -1
  26. package/dist/commands/rename.d.ts +4 -9
  27. package/dist/commands/rename.js +37 -101
  28. package/dist/commands/rename.js.map +1 -1
  29. package/dist/commands/show.d.ts +20 -12
  30. package/dist/commands/show.js +79 -57
  31. package/dist/commands/show.js.map +1 -1
  32. package/dist/commands/transition.d.ts +9 -2
  33. package/dist/commands/transition.js +25 -17
  34. package/dist/commands/transition.js.map +1 -1
  35. package/dist/commands/update.d.ts +16 -12
  36. package/dist/commands/update.js +19 -17
  37. package/dist/commands/update.js.map +1 -1
  38. package/dist/commands/validate.d.ts +17 -9
  39. package/dist/commands/validate.js +94 -35
  40. package/dist/commands/validate.js.map +1 -1
  41. package/dist/containers/card-container.d.ts +7 -5
  42. package/dist/containers/card-container.js +30 -5
  43. package/dist/containers/card-container.js.map +1 -1
  44. package/dist/containers/project/calculation-engine.d.ts +7 -4
  45. package/dist/containers/project/calculation-engine.js +61 -66
  46. package/dist/containers/project/calculation-engine.js.map +1 -1
  47. package/dist/containers/project/project-paths.d.ts +7 -4
  48. package/dist/containers/project/project-paths.js +22 -12
  49. package/dist/containers/project/project-paths.js.map +1 -1
  50. package/dist/containers/project/resource-cache.d.ts +169 -0
  51. package/dist/containers/project/resource-cache.js +509 -0
  52. package/dist/containers/project/resource-cache.js.map +1 -0
  53. package/dist/containers/project/resource-handler.d.ts +129 -0
  54. package/dist/containers/project/resource-handler.js +206 -0
  55. package/dist/containers/project/resource-handler.js.map +1 -0
  56. package/dist/containers/project.d.ts +46 -152
  57. package/dist/containers/project.js +179 -409
  58. package/dist/containers/project.js.map +1 -1
  59. package/dist/containers/template.d.ts +8 -2
  60. package/dist/containers/template.js +24 -19
  61. package/dist/containers/template.js.map +1 -1
  62. package/dist/interfaces/command-options.d.ts +3 -1
  63. package/dist/interfaces/folder-content-interfaces.d.ts +5 -3
  64. package/dist/interfaces/folder-content-interfaces.js +3 -3
  65. package/dist/interfaces/folder-content-interfaces.js.map +1 -1
  66. package/dist/interfaces/project-interfaces.d.ts +7 -9
  67. package/dist/interfaces/project-interfaces.js.map +1 -1
  68. package/dist/interfaces/resource-interfaces.d.ts +14 -1
  69. package/dist/interfaces/resource-interfaces.js.map +1 -1
  70. package/dist/macros/graph/index.js +12 -26
  71. package/dist/macros/graph/index.js.map +1 -1
  72. package/dist/macros/index.d.ts +1 -1
  73. package/dist/macros/index.js +2 -2
  74. package/dist/macros/index.js.map +1 -1
  75. package/dist/macros/report/index.js +3 -6
  76. package/dist/macros/report/index.js.map +1 -1
  77. package/dist/module-manager.d.ts +16 -3
  78. package/dist/module-manager.js +51 -19
  79. package/dist/module-manager.js.map +1 -1
  80. package/dist/project-settings.d.ts +21 -3
  81. package/dist/project-settings.js +91 -14
  82. package/dist/project-settings.js.map +1 -1
  83. package/dist/resources/calculation-resource.d.ts +4 -3
  84. package/dist/resources/calculation-resource.js +11 -5
  85. package/dist/resources/calculation-resource.js.map +1 -1
  86. package/dist/resources/card-type-resource.d.ts +6 -1
  87. package/dist/resources/card-type-resource.js +34 -23
  88. package/dist/resources/card-type-resource.js.map +1 -1
  89. package/dist/resources/create-defaults.d.ts +3 -2
  90. package/dist/resources/create-defaults.js +3 -2
  91. package/dist/resources/create-defaults.js.map +1 -1
  92. package/dist/resources/field-type-resource.d.ts +4 -1
  93. package/dist/resources/field-type-resource.js +22 -23
  94. package/dist/resources/field-type-resource.js.map +1 -1
  95. package/dist/resources/file-resource.d.ts +5 -9
  96. package/dist/resources/file-resource.js +6 -11
  97. package/dist/resources/file-resource.js.map +1 -1
  98. package/dist/resources/folder-resource.d.ts +29 -32
  99. package/dist/resources/folder-resource.js +59 -78
  100. package/dist/resources/folder-resource.js.map +1 -1
  101. package/dist/resources/graph-model-resource.d.ts +4 -1
  102. package/dist/resources/graph-model-resource.js +11 -4
  103. package/dist/resources/graph-model-resource.js.map +1 -1
  104. package/dist/resources/graph-view-resource.d.ts +5 -2
  105. package/dist/resources/graph-view-resource.js +7 -3
  106. package/dist/resources/graph-view-resource.js.map +1 -1
  107. package/dist/resources/link-type-resource.d.ts +5 -2
  108. package/dist/resources/link-type-resource.js +5 -2
  109. package/dist/resources/link-type-resource.js.map +1 -1
  110. package/dist/resources/report-resource.d.ts +6 -7
  111. package/dist/resources/report-resource.js +14 -23
  112. package/dist/resources/report-resource.js.map +1 -1
  113. package/dist/resources/resource-object.d.ts +94 -8
  114. package/dist/resources/resource-object.js +212 -109
  115. package/dist/resources/resource-object.js.map +1 -1
  116. package/dist/resources/template-resource.d.ts +7 -3
  117. package/dist/resources/template-resource.js +10 -3
  118. package/dist/resources/template-resource.js.map +1 -1
  119. package/dist/resources/workflow-resource.d.ts +5 -2
  120. package/dist/resources/workflow-resource.js +18 -22
  121. package/dist/resources/workflow-resource.js.map +1 -1
  122. package/dist/utils/card-utils.d.ts +2 -2
  123. package/dist/utils/card-utils.js +1 -1
  124. package/dist/utils/clingo-fact-builder.d.ts +25 -14
  125. package/dist/utils/clingo-fact-builder.js +27 -5
  126. package/dist/utils/clingo-fact-builder.js.map +1 -1
  127. package/dist/utils/clingo-facts.js +3 -4
  128. package/dist/utils/clingo-facts.js.map +1 -1
  129. package/dist/utils/configuration-logger.d.ts +91 -0
  130. package/dist/utils/configuration-logger.js +151 -0
  131. package/dist/utils/configuration-logger.js.map +1 -0
  132. package/dist/utils/constants.d.ts +1 -1
  133. package/dist/utils/constants.js +5 -3
  134. package/dist/utils/constants.js.map +1 -1
  135. package/dist/utils/resource-utils.d.ts +1 -0
  136. package/dist/utils/resource-utils.js +2 -1
  137. package/dist/utils/resource-utils.js.map +1 -1
  138. package/package.json +9 -9
  139. package/src/card-metadata-updater.ts +6 -2
  140. package/src/command-handler.ts +39 -12
  141. package/src/command-manager.ts +33 -21
  142. package/src/commands/create.ts +43 -78
  143. package/src/commands/export.ts +63 -52
  144. package/src/commands/fetch.ts +143 -34
  145. package/src/commands/import.ts +37 -15
  146. package/src/commands/move.ts +0 -1
  147. package/src/commands/remove.ts +20 -7
  148. package/src/commands/rename.ts +58 -149
  149. package/src/commands/show.ts +123 -80
  150. package/src/commands/transition.ts +26 -28
  151. package/src/commands/update.ts +25 -22
  152. package/src/commands/validate.ts +104 -67
  153. package/src/containers/card-container.ts +37 -5
  154. package/src/containers/project/calculation-engine.ts +61 -93
  155. package/src/containers/project/project-paths.ts +29 -13
  156. package/src/containers/project/resource-cache.ts +651 -0
  157. package/src/containers/project/resource-handler.ts +265 -0
  158. package/src/containers/project.ts +250 -527
  159. package/src/containers/template.ts +28 -23
  160. package/src/interfaces/command-options.ts +3 -1
  161. package/src/interfaces/folder-content-interfaces.ts +7 -6
  162. package/src/interfaces/project-interfaces.ts +12 -11
  163. package/src/interfaces/resource-interfaces.ts +18 -3
  164. package/src/macros/graph/index.ts +26 -47
  165. package/src/macros/index.ts +2 -2
  166. package/src/macros/report/index.ts +3 -9
  167. package/src/module-manager.ts +74 -17
  168. package/src/project-settings.ts +96 -14
  169. package/src/resources/calculation-resource.ts +18 -18
  170. package/src/resources/card-type-resource.ts +50 -50
  171. package/src/resources/create-defaults.ts +3 -2
  172. package/src/resources/field-type-resource.ts +41 -55
  173. package/src/resources/file-resource.ts +10 -36
  174. package/src/resources/folder-resource.ts +69 -120
  175. package/src/resources/graph-model-resource.ts +20 -22
  176. package/src/resources/graph-view-resource.ts +15 -17
  177. package/src/resources/link-type-resource.ts +10 -13
  178. package/src/resources/report-resource.ts +21 -43
  179. package/src/resources/resource-object.ts +263 -149
  180. package/src/resources/template-resource.ts +17 -16
  181. package/src/resources/workflow-resource.ts +25 -44
  182. package/src/utils/card-utils.ts +2 -2
  183. package/src/utils/clingo-fact-builder.ts +28 -16
  184. package/src/utils/clingo-facts.ts +3 -4
  185. package/src/utils/configuration-logger.ts +206 -0
  186. package/src/utils/constants.ts +5 -3
  187. package/src/utils/resource-utils.ts +2 -1
  188. package/dist/containers/project/resource-collector.d.ts +0 -110
  189. package/dist/containers/project/resource-collector.js +0 -344
  190. package/dist/containers/project/resource-collector.js.map +0 -1
  191. package/src/containers/project/resource-collector.ts +0 -404
@@ -13,8 +13,10 @@
13
13
  */
14
14
  // node
15
15
  import { basename, join, resolve } from 'node:path';
16
- import { constants as fsConstants, copyFile, mkdir, readdir, unlink, writeFile, } from 'node:fs/promises';
17
- import { CardContainer } from './card-container.js'; // base class
16
+ import { constants as fsConstants, copyFile, mkdir, unlink, writeFile, } from 'node:fs/promises';
17
+ import { readdirSync } from 'node:fs';
18
+ // base class
19
+ import { CardContainer } from './card-container.js';
18
20
  import { CalculationEngine } from './project/calculation-engine.js';
19
21
  import { CardLocation, } from '../interfaces/project-interfaces.js';
20
22
  import { pathExists } from '../utils/file-utils.js';
@@ -23,73 +25,52 @@ import { cardPathParts, isModulePath, isTemplateCard, } from '../utils/card-util
23
25
  import { ProjectConfiguration } from '../project-settings.js';
24
26
  import { ProjectPaths } from './project/project-paths.js';
25
27
  import { readJsonFile } from '../utils/json.js';
26
- import { pathToResourceName, resourceName, resourceNameToString, } from '../utils/resource-utils.js';
27
- import { ResourcesFrom, ResourceCollector, } from './project/resource-collector.js';
28
+ import { ResourcesFrom } from './project/resource-cache.js';
29
+ import { ResourceHandler } from './project/resource-handler.js';
28
30
  import { Validate } from '../commands/validate.js';
29
- import { CalculationResource } from '../resources/calculation-resource.js';
30
- import { CardTypeResource } from '../resources/card-type-resource.js';
31
- import { FieldTypeResource } from '../resources/field-type-resource.js';
32
- import { GraphModelResource } from '../resources/graph-model-resource.js';
33
- import { GraphViewResource } from '../resources/graph-view-resource.js';
34
- import { LinkTypeResource } from '../resources/link-type-resource.js';
35
- import { ReportResource } from '../resources/report-resource.js';
36
- import { TemplateResource } from '../resources/template-resource.js';
37
- import { WorkflowResource } from '../resources/workflow-resource.js';
38
31
  import { ContentWatcher } from './project/project-content-watcher.js';
39
32
  import { getChildLogger } from '../utils/log-utils.js';
40
33
  import { ROOT } from '../utils/constants.js';
34
+ import { ConfigurationLogger, ConfigurationOperation, } from '../utils/configuration-logger.js';
41
35
  // Re-export this, so that classes that use Project do not need to have separate import.
42
36
  export { ResourcesFrom };
43
37
  /**
44
38
  * Represents project folder.
45
39
  */
46
40
  export class Project extends CardContainer {
47
- watchResourceChanges;
41
+ options;
48
42
  calculationEngine;
49
- // Created resources are held in a cache.
50
- // In the cache, key is resource name, and data is resource metadata (as JSON).
51
- createdResources = new Map();
52
43
  logger = getChildLogger({ module: 'Project' });
53
44
  projectPaths;
54
- resources;
45
+ resourceHandler;
55
46
  resourceWatcher;
56
47
  settings;
57
48
  validator;
58
- constructor(path, watchResourceChanges) {
59
- const settings = new ProjectConfiguration(join(path, '.cards', 'local', Project.projectConfigFileName));
60
- super(path, settings.cardKeyPrefix, '');
61
- this.watchResourceChanges = watchResourceChanges;
49
+ cachedAllModulePrefixes = [];
50
+ constructor(path, options = {
51
+ autoSave: true,
52
+ watchResourceChanges: false,
53
+ }) {
54
+ const settings = new ProjectConfiguration(join(path, '.cards', 'local', Project.projectConfigFileName), options.autoSave ?? true);
55
+ super(path, settings.cardKeyPrefix);
56
+ this.options = options;
62
57
  this.settings = settings;
63
58
  this.logger.info({ path }, 'Initializing project');
64
59
  this.calculationEngine = new CalculationEngine(this);
65
60
  this.projectPaths = new ProjectPaths(path);
66
- this.resources = new ResourceCollector(this);
67
- this.containerName = this.settings.name;
61
+ this.resourceHandler = new ResourceHandler(this);
68
62
  // todo: implement project validation
69
63
  this.validator = Validate.getInstance();
70
- this.logger.info({ resourcesFolder: this.paths.resourcesFolder }, 'Collecting local resources');
71
- this.resources.collectLocalResources();
72
- this.logger.info({ name: this.containerName }, 'Project initialization complete');
64
+ this.logger.info({ name: this.settings.name }, 'Project initialization complete');
65
+ this.refreshAllModulePrefixes();
73
66
  const ignoreRenameFileChanges = true;
74
67
  // Watch changes in .cards if there are multiple instances of Project being
75
68
  // run concurrently.
76
- if (this.watchResourceChanges) {
69
+ if (this.options.watchResourceChanges) {
77
70
  this.resourceWatcher = new ContentWatcher(ignoreRenameFileChanges, this.paths.resourcesFolder, (fileName) => {
78
71
  void (async () => {
79
- let resource;
80
- try {
81
- resource = pathToResourceName(this, join(this.paths.resourcesFolder, fileName));
82
- if (!resource) {
83
- return;
84
- }
85
- }
86
- catch {
87
- // it wasn't a resource that changed, so ignore the change
88
- return;
89
- }
90
- const resourceName = `${resource.prefix}/${resource.type}/${resource.identifier}`;
91
- await this.replaceCacheValue(resourceName);
92
- this.resources.collectLocalResources();
72
+ this.resources.handleFileSystemChange(join(this.paths.resourcesFolder, fileName));
73
+ this.resources.changed();
93
74
  })();
94
75
  });
95
76
  }
@@ -106,7 +87,19 @@ export class Project extends CardContainer {
106
87
  }
107
88
  // Finds specific module.
108
89
  async findModule(moduleName) {
109
- return (await this.resources.resources('modules')).find((item) => item.name === moduleName && item.path);
90
+ const moduleExists = this.resources.moduleNames().includes(moduleName);
91
+ if (!moduleExists) {
92
+ return undefined;
93
+ }
94
+ // For modules, we need to construct the local path where the module is stored
95
+ const moduleConfig = this.configuration.modules?.find((module) => module.name === moduleName);
96
+ if (!moduleConfig) {
97
+ return undefined;
98
+ }
99
+ return {
100
+ name: moduleName,
101
+ path: join(this.paths.modulesFolder, moduleConfig.name),
102
+ };
110
103
  }
111
104
  // Handles attachment changes after filesystem operations.
112
105
  async handleAttachmentChange(cardKey, operation, fileName) {
@@ -124,6 +117,7 @@ export class Project extends CardContainer {
124
117
  }
125
118
  }
126
119
  // Determines the parent card key from a card's filesystem path.
120
+ // todo: could be moved to card-utils
127
121
  parentFromPath(cardPath) {
128
122
  return cardPathParts(this.projectPrefix, cardPath).parents.at(-1) || 'root';
129
123
  }
@@ -135,23 +129,6 @@ export class Project extends CardContainer {
135
129
  this.cardCache.updateCard(parentCard.key, parentCard);
136
130
  }
137
131
  }
138
- // Removes current version of a resource from the resource cache.
139
- // Then re-creates the resource with current data and caches the value again.
140
- // If the value wasn't in the cache before, it will be added.
141
- async replaceCacheValue(resourceName) {
142
- if (this.createdResources.has(resourceName)) {
143
- // First, remove the old version from cache
144
- this.createdResources.delete(resourceName);
145
- }
146
- const resourceData = await this.resource(resourceName);
147
- if (resourceData) {
148
- this.createdResources.set(resourceName, resourceData);
149
- }
150
- }
151
- // Returns (local or all) resources of a given type.
152
- async resourcesOfType(type, from = ResourcesFrom.localOnly) {
153
- return this.resources.resources(type, from);
154
- }
155
132
  // Updates children in the card cache
156
133
  updateCachedChildren(parentKey, newChild) {
157
134
  const parentCard = this.cardCache.getCard(parentKey);
@@ -164,24 +141,63 @@ export class Project extends CardContainer {
164
141
  this.cardCache.updateCard(parentCard.key, parentCard);
165
142
  }
166
143
  }
144
+ // Refreshes the cached list of all module prefixes.
145
+ // This includes both direct and transient module dependencies.
146
+ refreshAllModulePrefixes() {
147
+ const prefixes = [this.projectPrefix];
148
+ try {
149
+ const modules = readdirSync(this.paths.modulesFolder, {
150
+ withFileTypes: true,
151
+ })
152
+ .filter((item) => item.isDirectory())
153
+ .map((item) => item.name);
154
+ prefixes.push(...modules);
155
+ }
156
+ catch {
157
+ // If modules folder doesn't exist, fall back to configuration modules only
158
+ const moduleNames = this.configuration.modules.map((item) => item.name);
159
+ prefixes.push(...moduleNames);
160
+ }
161
+ this.cachedAllModulePrefixes = prefixes;
162
+ }
163
+ // Validates that card's data is valid.
164
+ async validateCard(card) {
165
+ const invalidCustomData = await this.validator.validateCustomFields(this, card, this.allModulePrefixes());
166
+ const invalidWorkFlow = await this.validator.validateWorkflowState(this, card);
167
+ const invalidLabels = this.validator.validateCardLabels(card);
168
+ if (invalidCustomData.length === 0 &&
169
+ invalidWorkFlow.length === 0 &&
170
+ invalidLabels.length === 0) {
171
+ return '';
172
+ }
173
+ const errors = [];
174
+ if (invalidCustomData.length > 0) {
175
+ errors.push(invalidCustomData);
176
+ }
177
+ if (invalidWorkFlow.length > 0) {
178
+ errors.push(invalidWorkFlow);
179
+ }
180
+ if (invalidLabels.length > 0) {
181
+ errors.push(invalidLabels);
182
+ }
183
+ return errors.join('\n');
184
+ }
167
185
  /**
168
186
  * Populate template cards into the card cache.
169
187
  */
170
188
  async populateTemplateCards() {
171
189
  try {
172
- // Gets local & module templates
173
- const templateResources = await this.templates();
174
- const prefixes = await this.projectPrefixes();
190
+ const templateResources = this.resources.templates();
191
+ const prefixes = this.allModulePrefixes();
175
192
  const loadPromises = templateResources.map(async (template) => {
176
193
  try {
177
- this.validator.validResourceName('templates', template.name, prefixes);
194
+ this.validator.validResourceName('templates', template.data?.name || '', prefixes);
178
195
  }
179
196
  catch (error) {
180
- this.logger.warn({ templateName: template.name, error }, `Template name '${template.name}' does not follow required format, skipping`);
197
+ this.logger.warn({ templateName: template, error }, `Template name '${template}' does not follow required format, skipping`);
181
198
  return;
182
199
  }
183
- const templateResource = new TemplateResource(this, resourceName(template.name));
184
- const templateObject = templateResource.templateObject();
200
+ const templateObject = template.templateObject();
185
201
  const isCreated = templateObject && templateObject.isCreated();
186
202
  if (!templateObject || !isCreated) {
187
203
  return;
@@ -203,15 +219,6 @@ export class Project extends CardContainer {
203
219
  await this.cardCache.populateFromPath(this.paths.cardRootFolder);
204
220
  await this.populateTemplateCards();
205
221
  }
206
- /**
207
- * Add a given 'resource' to the local resource arrays.
208
- * @param resource Resource to add.
209
- * @param data JSON data for the resource.
210
- */
211
- addResource(resource, data) {
212
- this.resources.add(resource);
213
- this.createdResources.set(resource.name, data);
214
- }
215
222
  /**
216
223
  * Returns all template cards from the project. This includes all module templates' cards.
217
224
  * @returns all the template cards from the project
@@ -235,30 +242,6 @@ export class Project extends CardContainer {
235
242
  attachmentsByPath(path) {
236
243
  return super.attachments(path);
237
244
  }
238
- /**
239
- * Returns all the attachments in the template cards.
240
- * @returns all the attachments in the template cards.
241
- */
242
- async attachmentsFromTemplates() {
243
- const templateAttachments = [];
244
- const templates = await this.templates();
245
- for (const template of templates) {
246
- const templateResource = new TemplateResource(this, resourceName(template.name));
247
- const templateObject = templateResource.templateObject();
248
- if (templateObject) {
249
- templateAttachments.push(...templateObject.attachments());
250
- }
251
- }
252
- return templateAttachments;
253
- }
254
- /**
255
- * Returns an array of all the calculation files (*.lp) in the project.
256
- * @param from Defines where resources are collected from.
257
- * @returns array of all calculation files in the project.
258
- */
259
- async calculations(from = ResourcesFrom.all) {
260
- return this.resources.resources('calculations', from);
261
- }
262
245
  /**
263
246
  * Returns path to a card's attachment folder.
264
247
  * @param cardKey card key
@@ -298,28 +281,6 @@ export class Project extends CardContainer {
298
281
  // Update cache
299
282
  await this.handleAttachmentChange(cardKey, 'added', basename(attachmentName));
300
283
  }
301
- /**
302
- * Removes an attachment from a card.
303
- * @param cardKey The card to remove attachment from
304
- * @param fileName The name of the attachment file to remove
305
- * @throws if trying to remove module card attachment, or the attachment was not found.
306
- */
307
- async removeCardAttachment(cardKey, fileName) {
308
- const attachmentFolder = this.cardAttachmentFolder(cardKey);
309
- // Modules cannot be modified.
310
- if (isModulePath(attachmentFolder)) {
311
- throw new Error(`Cannot modify imported module`);
312
- }
313
- const attachmentPath = join(attachmentFolder, fileName);
314
- try {
315
- await unlink(attachmentPath);
316
- }
317
- catch (error) {
318
- this.logger.error({ error }, 'Removing card attachment');
319
- throw new Error(`Attachment not found: ${fileName}`);
320
- }
321
- await this.handleAttachmentChange(cardKey, 'removed', fileName);
322
- }
323
284
  /**
324
285
  * Returns path to a card's folder.
325
286
  * @param cardKey card key
@@ -330,9 +291,9 @@ export class Project extends CardContainer {
330
291
  if (found) {
331
292
  return found.path;
332
293
  }
333
- const templates = await this.templates();
294
+ const templates = this.resources.templates();
334
295
  const templatePromises = templates.map((template) => {
335
- const templateObject = new TemplateResource(this, resourceName(template.name)).templateObject();
296
+ const templateObject = template.templateObject();
336
297
  const templateCard = templateObject
337
298
  ? templateObject.findCard(cardKey)
338
299
  : undefined;
@@ -357,14 +318,6 @@ export class Project extends CardContainer {
357
318
  }
358
319
  return cards;
359
320
  }
360
- /**
361
- * Accessor for cards cache.
362
- * Used by template container (it needs to access project's cache, not their own instance).
363
- * @note Should not be used directly (other than Template).
364
- */
365
- get cardsCache() {
366
- return this.cardCache;
367
- }
368
321
  /**
369
322
  * Returns an array of all the cards in the project.
370
323
  * @note These are project cards only, by default (unless path dictates otherwise).
@@ -376,12 +329,12 @@ export class Project extends CardContainer {
376
329
  return super.cards(path, details);
377
330
  }
378
331
  /**
379
- * Returns an array of all the card types in the project.
380
- * @param from Defines where resources are collected from.
381
- * @returns array of all card types in the project.
332
+ * Accessor for cards cache.
333
+ * Used by template container (it needs to access project's cache, not their own instance).
334
+ * @note Should not be used directly (other than Template).
382
335
  */
383
- async cardTypes(from = ResourcesFrom.all) {
384
- return this.resources.resources('cardTypes', from);
336
+ get cardsCache() {
337
+ return this.cardCache;
385
338
  }
386
339
  /**
387
340
  * Returns children of a given card; as Card array
@@ -398,18 +351,6 @@ export class Project extends CardContainer {
398
351
  }
399
352
  return cards;
400
353
  }
401
- /**
402
- * Updates all local resources.
403
- */
404
- collectLocalResources() {
405
- this.resources.changed();
406
- }
407
- /**
408
- * Updates all imported module resources.
409
- */
410
- collectModuleResources() {
411
- this.resources.moduleImported();
412
- }
413
354
  /**
414
355
  * Returns project configuration.
415
356
  * @returns project configuration.
@@ -427,7 +368,8 @@ export class Project extends CardContainer {
427
368
  return undefined;
428
369
  }
429
370
  const { template } = cardPathParts(this.projectPrefix, card.path);
430
- return new TemplateResource(this, resourceName(template)).templateObject();
371
+ const templateResource = this.resources.byType(template, 'templates');
372
+ return templateResource.templateObject();
431
373
  }
432
374
  /**
433
375
  * Cleanups project when it is being closed.
@@ -439,12 +381,13 @@ export class Project extends CardContainer {
439
381
  }
440
382
  }
441
383
  /**
442
- * Returns an array of all the field types in the project.
443
- * @param from Defines where resources are collected from.
444
- * @returns array of all field types in the project.
384
+ * Returns specific card.
385
+ * @param cardToFind Card key to find
386
+ * @param details Defines which card details are included in the return values.
387
+ * @returns specific card details, or undefined if card is not part of the project.
445
388
  */
446
- async fieldTypes(from = ResourcesFrom.all) {
447
- return this.resources.resources('fieldTypes', from);
389
+ findCard(cardToFind, details) {
390
+ return super.findCard(cardToFind, details);
448
391
  }
449
392
  /**
450
393
  * Finds root of a project
@@ -462,31 +405,6 @@ export class Project extends CardContainer {
462
405
  }
463
406
  return Project.findProjectRoot(parentPath);
464
407
  }
465
- /**
466
- * Returns specific card.
467
- * @param cardToFind Card key to find
468
- * @param details Defines which card details are included in the return values.
469
- * @returns specific card details, or undefined if card is not part of the project.
470
- */
471
- findCard(cardToFind, details) {
472
- return super.findCard(cardToFind, details);
473
- }
474
- /**
475
- * Returns an array of all the graph models in the project.
476
- * @param from Defines where resources are collected from.
477
- * @returns array of all the graph models in the project.
478
- */
479
- async graphModels(from = ResourcesFrom.all) {
480
- return this.resources.resources('graphModels', from);
481
- }
482
- /**
483
- * Returns an array of all the graph views in the project.
484
- * @param from Defines where resources are collected from.
485
- * @returns array of all the graph views in the project.
486
- */
487
- async graphViews(from = ResourcesFrom.all) {
488
- return this.resources.resources('graphViews', from);
489
- }
490
408
  /**
491
409
  * When card changes.
492
410
  * @param changedCard Card that was changed.
@@ -557,12 +475,24 @@ export class Project extends CardContainer {
557
475
  /**
558
476
  * Adds a module from project.
559
477
  * @param module Module to add
478
+ * @param skipMigrationLog If true, skip logging to migration log. Used during project creation.
560
479
  */
561
- async importModule(module) {
480
+ async importModule(module, skipMigrationLog = false) {
562
481
  // Add module as a dependency.
563
482
  await this.configuration.addModule(module);
564
- this.collectModuleResources();
483
+ this.resources.changedModules();
484
+ this.refreshAllModulePrefixes();
565
485
  await this.populateTemplateCards();
486
+ // Log configuration change
487
+ if (!skipMigrationLog) {
488
+ await ConfigurationLogger.log(this.basePath, ConfigurationOperation.MODULE_ADD, module.name, {
489
+ parameters: {
490
+ location: module.location,
491
+ branch: module.branch,
492
+ private: module.private,
493
+ },
494
+ });
495
+ }
566
496
  this.logger.info(`Imported module '${module.name}'`);
567
497
  }
568
498
  /**
@@ -573,14 +503,6 @@ export class Project extends CardContainer {
573
503
  static isCreated(path) {
574
504
  return pathExists(join(path, 'cardRoot'));
575
505
  }
576
- /**
577
- * Returns an array of all the link types in the project.
578
- * @param from Defines where resources are collected from.
579
- * @returns array of all link types in the project.
580
- */
581
- async linkTypes(from = ResourcesFrom.all) {
582
- return this.resources.resources('linkTypes', from);
583
- }
584
506
  /**
585
507
  * Returns an array of cards in the project, in the templates or both.
586
508
  * Cards don't have content and nor metadata.
@@ -602,15 +524,15 @@ export class Project extends CardContainer {
602
524
  }
603
525
  if (cardsFrom === CardLocation.all ||
604
526
  cardsFrom === CardLocation.templatesOnly) {
605
- const templates = await this.templates();
527
+ const templates = this.resources.templates();
606
528
  for (const template of templates) {
607
- const templateObject = new TemplateResource(this, resourceName(template.name)).templateObject();
529
+ const templateObject = template.templateObject();
608
530
  if (templateObject) {
609
531
  // todo: optimization - do all this in parallel
610
532
  const templateCards = templateObject.listCards();
611
533
  if (templateCards.length) {
612
534
  cardListContainer.push({
613
- name: template.name,
535
+ name: template.data?.name || '',
614
536
  type: 'template',
615
537
  cards: templateCards.map((item) => item.key),
616
538
  });
@@ -643,7 +565,7 @@ export class Project extends CardContainer {
643
565
  async module(moduleName) {
644
566
  const module = await this.findModule(moduleName);
645
567
  if (module && module.path) {
646
- const modulePath = join(module.path, module.name);
568
+ const modulePath = module.path;
647
569
  const moduleConfig = await readJsonFile(join(modulePath, Project.projectConfigFileName));
648
570
  return {
649
571
  name: moduleConfig.name,
@@ -651,44 +573,19 @@ export class Project extends CardContainer {
651
573
  hubs: moduleConfig.hubs,
652
574
  path: modulePath,
653
575
  cardKeyPrefix: moduleConfig.cardKeyPrefix,
654
- calculations: [
655
- ...(await this.resources.collectResourcesFromModules('calculations', moduleName)),
656
- ],
657
- cardTypes: [
658
- ...(await this.resources.collectResourcesFromModules('cardTypes', moduleName)),
659
- ],
660
- fieldTypes: [
661
- ...(await this.resources.collectResourcesFromModules('fieldTypes', moduleName)),
662
- ],
663
- graphModels: [
664
- ...(await this.resources.collectResourcesFromModules('graphModels', moduleName)),
665
- ],
666
- graphViews: [
667
- ...(await this.resources.collectResourcesFromModules('graphViews', moduleName)),
668
- ],
669
- linkTypes: [
670
- ...(await this.resources.collectResourcesFromModules('linkTypes', moduleName)),
671
- ],
672
- reports: [
673
- ...(await this.resources.collectResourcesFromModules('reports', moduleName)),
674
- ],
675
- templates: [
676
- ...(await this.resources.collectResourcesFromModules('templates', moduleName)),
677
- ],
678
- workflows: [
679
- ...(await this.resources.collectResourcesFromModules('workflows', moduleName)),
680
- ],
576
+ calculations: this.resources.moduleResourceNames('calculations', moduleName),
577
+ cardTypes: this.resources.moduleResourceNames('cardTypes', moduleName),
578
+ fieldTypes: this.resources.moduleResourceNames('fieldTypes', moduleName),
579
+ graphModels: this.resources.moduleResourceNames('graphModels', moduleName),
580
+ graphViews: this.resources.moduleResourceNames('graphViews', moduleName),
581
+ linkTypes: this.resources.moduleResourceNames('linkTypes', moduleName),
582
+ reports: this.resources.moduleResourceNames('reports', moduleName),
583
+ templates: this.resources.moduleResourceNames('templates', moduleName),
584
+ workflows: this.resources.moduleResourceNames('workflows', moduleName),
681
585
  };
682
586
  }
683
587
  return undefined;
684
588
  }
685
- /**
686
- * Returns list of modules in the project.
687
- * @returns list of modules in the project.
688
- */
689
- async modules() {
690
- return this.resources.resources('modules');
691
- }
692
589
  /**
693
590
  * Returns a new unique card key with project prefix (e.g. test_x649it4x).
694
591
  * Random part of string will be always 8 characters in base-36 (0-9a-z)
@@ -759,6 +656,10 @@ export class Project extends CardContainer {
759
656
  */
760
657
  async populateCaches() {
761
658
  if (!this.cardCache.isPopulated) {
659
+ // Only collect modules that are registered in the project configuration
660
+ if (this.configuration.modules && this.configuration.modules.length > 0) {
661
+ this.resources.changedModules();
662
+ }
762
663
  await this.populateCardsCache();
763
664
  }
764
665
  }
@@ -775,165 +676,72 @@ export class Project extends CardContainer {
775
676
  return this.settings.cardKeyPrefix;
776
677
  }
777
678
  /**
778
- * Collects all prefixes used in the project (project's own plus all from modules).
679
+ * Returns all prefixes used in the project.
680
+ * This includes both direct dependencies and transient dependencies.
779
681
  * @returns all prefixes used in the project.
780
- * @todo - move the module prefix fetch to resource-collector.
781
- * Make it use cached value that is only changed when module is removed/imported.
782
682
  */
783
- async projectPrefixes() {
784
- const prefixes = [this.projectPrefix];
785
- let files;
786
- try {
787
- // TODO: Could be optimized so that prefixes are stored once fetched.
788
- files = await readdir(this.paths.modulesFolder, {
789
- withFileTypes: true,
790
- recursive: true,
791
- });
792
- const configurationFiles = files
793
- .filter((dirent) => dirent.isFile())
794
- .filter((dirent) => dirent.name === Project.projectConfigFileName);
795
- const configurationPromises = configurationFiles.map(async (file) => {
796
- const configuration = (await readJsonFile(join(file.parentPath, file.name)));
797
- return configuration.cardKeyPrefix;
798
- });
799
- const configurationPrefixes = await Promise.all(configurationPromises);
800
- prefixes.push(...configurationPrefixes);
801
- }
802
- catch (error) {
803
- this.logger.error({ error }, 'Failed to collect prefixes in use');
804
- }
805
- return prefixes;
683
+ allModulePrefixes() {
684
+ return this.cachedAllModulePrefixes;
806
685
  }
807
686
  /**
808
- * Removes a module from the project
809
- * @param module Module (name) to remove.
687
+ * Returns prefixes for direct module dependencies only (from cardsConfig.json).
688
+ * @returns prefixes for direct module dependencies.
810
689
  */
811
- async removeModule(moduleName) {
812
- const toBeRemovedTemplates = this.resources.moduleResources.resourceArray('templates', moduleName);
813
- // First, remove cards from the cache
814
- for (const template of toBeRemovedTemplates) {
815
- this.cardCache.deleteCardsFromTemplate(template.name);
816
- }
817
- // Then, remove module from project configuration
818
- await this.configuration.removeModule(moduleName);
819
- this.collectModuleResources();
820
- this.logger.info(`Removed module '${moduleName}'`);
821
- }
822
- /**
823
- * Removes a resource from Project.
824
- * @param resource Resource to remove.
825
- */
826
- removeResource(resource) {
827
- // Template cards must be removed from the cache when resource is removed.
828
- if (resource.path.includes('templates')) {
829
- const templateName = resourceNameToString(resourceName(resource.name));
830
- this.cardCache.deleteCardsFromTemplate(templateName);
831
- }
832
- this.resources.remove(resource);
833
- this.createdResources.delete(resource.name);
834
- }
835
- /**
836
- * Array of reports in the project.
837
- * @param from Defines where resources are collected from.
838
- * @returns array of all reports in the project.
839
- */
840
- async reports(from = ResourcesFrom.all) {
841
- return this.resources.resources('reports', from);
842
- }
843
- /**
844
- * Returns handlebar files from reports.
845
- * @param from Defines where report handlebar files are collected from.
846
- * @returns handlebar files from reports.
847
- */
848
- async reportHandlerBarFiles(from = ResourcesFrom.all) {
849
- const reports = await this.reports(from);
850
- const handleBarFiles = [];
851
- for (const reportResourceName of reports) {
852
- const name = resourceName(reportResourceName.name);
853
- const report = new ReportResource(this, name);
854
- handleBarFiles.push(...(await report.handleBarFiles()));
855
- }
856
- return handleBarFiles;
690
+ projectPrefixes() {
691
+ const prefixes = [this.projectPrefix];
692
+ const moduleNames = this.configuration.modules.map((item) => item.name);
693
+ prefixes.push(...moduleNames);
694
+ return prefixes;
857
695
  }
858
696
  /**
859
- * Returns metadata from a given resource
860
- * @param name Name of a resource
861
- * @returns Metadata from the resource.
697
+ * Removes an attachment from a card.
698
+ * @param cardKey The card to remove attachment from
699
+ * @param fileName The name of the attachment file to remove
700
+ * @throws if trying to remove module card attachment, or the attachment was not found.
862
701
  */
863
- resource(name) {
864
- const resName = resourceName(name);
865
- if (this.createdResources.has(resourceNameToString(resName))) {
866
- const value = this.createdResources.get(resourceNameToString(resName));
867
- return value;
702
+ async removeCardAttachment(cardKey, fileName) {
703
+ const attachmentFolder = this.cardAttachmentFolder(cardKey);
704
+ // Modules cannot be modified.
705
+ if (isModulePath(attachmentFolder)) {
706
+ throw new Error(`Cannot modify imported module`);
868
707
  }
869
- let resource = undefined;
708
+ const attachmentPath = join(attachmentFolder, fileName);
870
709
  try {
871
- resource = Project.resourceObject(this, resName);
872
- }
873
- catch {
874
- return undefined;
710
+ await unlink(attachmentPath);
875
711
  }
876
- const data = resource?.data;
877
- if (!data) {
878
- return undefined;
712
+ catch (error) {
713
+ this.logger.error({ error }, 'Removing card attachment');
714
+ throw new Error(`Attachment not found: ${fileName}`);
879
715
  }
880
- return data;
881
- }
882
- /**
883
- * Returns resource cache.
884
- */
885
- get resourceCache() {
886
- return this.createdResources;
716
+ await this.handleAttachmentChange(cardKey, 'removed', fileName);
887
717
  }
888
718
  /**
889
- * Checks if a given resource exists in the project already.
890
- * @param resourceType Type of resource as a string.
891
- * @param name Valid name of resource.
892
- * @returns boolean, true if resource exists; false otherwise.
719
+ * Removes a module from the project cache and configuration.
720
+ * @note that ModuleManager removes the actual files.
721
+ * @param moduleName Module name to remove.
893
722
  */
894
- async resourceExists(resourceType, name) {
895
- const resources = await this.resourcesOfType(resourceType, ResourcesFrom.all);
896
- const resource = resources.find((item) => item.name === name);
897
- return resource !== undefined;
723
+ async removeModule(moduleName) {
724
+ const toBeRemovedTemplates = this.resources.moduleResourceNames('templates', moduleName);
725
+ // First, remove template cards from the cache that are part of removed templates.
726
+ for (const templateName of toBeRemovedTemplates) {
727
+ this.cardCache.deleteCardsFromTemplate(templateName);
728
+ }
729
+ // Then, remove all module resources from cache
730
+ this.resources.removeModule(moduleName);
731
+ // Finally, remove module from project configuration
732
+ await this.configuration.removeModule(moduleName);
733
+ // Refresh cached module prefixes after removal
734
+ this.refreshAllModulePrefixes();
735
+ // Log configuration change
736
+ await ConfigurationLogger.log(this.basePath, ConfigurationOperation.MODULE_REMOVE, moduleName, {});
737
+ this.logger.info(`Removed module '${moduleName}'`);
898
738
  }
899
739
  /**
900
- * Instantiates resource object from project with a resource name.
901
- * @note that this is memory based object only.
902
- * To manipulate the resource (create files, delete files etc), use the resource object's API.
903
- * @param project Project from which resources are created from.
904
- * @param name Resource name
905
- * @throws if called with unsupported resource type.
906
- * @returns Created resource.
740
+ * Accessor for resource handler.
741
+ * @returns Resource handler instance.
907
742
  */
908
- static resourceObject(project, name) {
909
- if (name.type === 'calculations') {
910
- return new CalculationResource(project, name);
911
- }
912
- else if (name.type === 'cardTypes') {
913
- return new CardTypeResource(project, name);
914
- }
915
- else if (name.type === 'fieldTypes') {
916
- return new FieldTypeResource(project, name);
917
- }
918
- else if (name.type === 'graphModels') {
919
- return new GraphModelResource(project, name);
920
- }
921
- else if (name.type === 'graphViews') {
922
- return new GraphViewResource(project, name);
923
- }
924
- else if (name.type === 'linkTypes') {
925
- return new LinkTypeResource(project, name);
926
- }
927
- else if (name.type === 'reports') {
928
- return new ReportResource(project, name);
929
- }
930
- else if (name.type === 'templates') {
931
- return new TemplateResource(project, name);
932
- }
933
- else if (name.type === 'workflows') {
934
- return new WorkflowResource(project, name);
935
- }
936
- throw new Error(`Unsupported resource type '${resourceNameToString(name)}'`);
743
+ get resources() {
744
+ return this.resourceHandler;
937
745
  }
938
746
  /**
939
747
  * Shows details of a project.
@@ -941,11 +749,11 @@ export class Project extends CardContainer {
941
749
  */
942
750
  async show() {
943
751
  return {
944
- name: this.containerName,
752
+ name: this.settings.name,
945
753
  path: this.basePath,
946
754
  prefix: this.projectPrefix,
947
755
  hubs: this.configuration.hubs,
948
- modules: (await this.modules()).map((item) => item.name),
756
+ modules: this.resources.moduleNames(),
949
757
  numberOfCards: (await this.listCards(CardLocation.projectOnly))[0].cards
950
758
  .length,
951
759
  };
@@ -971,14 +779,6 @@ export class Project extends CardContainer {
971
779
  return cachedCard.location === templateName;
972
780
  });
973
781
  }
974
- /**
975
- * Array of templates in the project.
976
- * @param from Defines where resources are collected from.
977
- * @returns array of all templates in the project.
978
- */
979
- async templates(from = ResourcesFrom.all) {
980
- return this.resources.resources('templates', from);
981
- }
982
782
  /**
983
783
  * Update a card's content.
984
784
  * @param cardKey card key to update.
@@ -1063,35 +863,5 @@ export class Project extends CardContainer {
1063
863
  await this.handleCardChanged(card);
1064
864
  }
1065
865
  }
1066
- // Validates that card's data is valid.
1067
- async validateCard(card) {
1068
- const invalidCustomData = await this.validator.validateCustomFields(this, card);
1069
- const invalidWorkFlow = this.validator.validateWorkflowState(this, card);
1070
- const invalidLabels = this.validator.validateCardLabels(card);
1071
- if (invalidCustomData.length === 0 &&
1072
- invalidWorkFlow.length === 0 &&
1073
- invalidLabels.length === 0) {
1074
- return '';
1075
- }
1076
- const errors = [];
1077
- if (invalidCustomData.length > 0) {
1078
- errors.push(invalidCustomData);
1079
- }
1080
- if (invalidWorkFlow.length > 0) {
1081
- errors.push(invalidWorkFlow);
1082
- }
1083
- if (invalidLabels.length > 0) {
1084
- errors.push(invalidLabels);
1085
- }
1086
- return errors.join('\n');
1087
- }
1088
- /**
1089
- * Array of workflows in the project.
1090
- * @param from Defines where resources are collected from.
1091
- * @returns array of all workflows in the project.
1092
- */
1093
- async workflows(from = ResourcesFrom.all) {
1094
- return this.resources.resources('workflows', from);
1095
- }
1096
866
  }
1097
867
  //# sourceMappingURL=project.js.map