@cyberismo/data-handler 0.0.10 → 0.0.12
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.
- package/dist/card-metadata-updater.js +1 -1
- package/dist/card-metadata-updater.js.map +1 -1
- package/dist/command-handler.d.ts +13 -8
- package/dist/command-handler.js +47 -7
- package/dist/command-handler.js.map +1 -1
- package/dist/command-manager.d.ts +2 -1
- package/dist/command-manager.js +4 -2
- package/dist/command-manager.js.map +1 -1
- package/dist/commands/calculate.d.ts +7 -0
- package/dist/commands/calculate.js +9 -0
- package/dist/commands/calculate.js.map +1 -1
- package/dist/commands/create.d.ts +5 -0
- package/dist/commands/create.js +15 -8
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/fetch.d.ts +24 -0
- package/dist/commands/fetch.js +118 -0
- package/dist/commands/fetch.js.map +1 -0
- package/dist/commands/index.d.ts +2 -1
- package/dist/commands/index.js +2 -1
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/remove.d.ts +1 -0
- package/dist/commands/remove.js +16 -12
- package/dist/commands/remove.js.map +1 -1
- package/dist/commands/rename.js +4 -6
- package/dist/commands/rename.js.map +1 -1
- package/dist/commands/show.d.ts +18 -1
- package/dist/commands/show.js +52 -0
- package/dist/commands/show.js.map +1 -1
- package/dist/commands/validate.js +7 -8
- package/dist/commands/validate.js.map +1 -1
- package/dist/containers/project/calculation-engine.d.ts +8 -0
- package/dist/containers/project/calculation-engine.js +20 -9
- package/dist/containers/project/calculation-engine.js.map +1 -1
- package/dist/containers/project.d.ts +19 -8
- package/dist/containers/project.js +52 -34
- package/dist/containers/project.js.map +1 -1
- package/dist/containers/template.js +1 -1
- package/dist/containers/template.js.map +1 -1
- package/dist/interfaces/project-interfaces.d.ts +13 -2
- package/dist/interfaces/project-interfaces.js.map +1 -1
- package/dist/macros/base-macro.d.ts +1 -1
- package/dist/macros/base-macro.js +1 -1
- package/dist/macros/base-macro.js.map +1 -1
- package/dist/macros/createCards/index.d.ts +1 -1
- package/dist/macros/createCards/index.js +1 -1
- package/dist/macros/createCards/index.js.map +1 -1
- package/dist/macros/graph/index.d.ts +1 -1
- package/dist/macros/graph/index.js +21 -29
- package/dist/macros/graph/index.js.map +1 -1
- package/dist/macros/image/index.d.ts +1 -1
- package/dist/macros/image/index.js +1 -7
- package/dist/macros/image/index.js.map +1 -1
- package/dist/macros/include/index.d.ts +1 -1
- package/dist/macros/include/index.js +1 -1
- package/dist/macros/include/index.js.map +1 -1
- package/dist/macros/index.d.ts +12 -5
- package/dist/macros/index.js +19 -7
- package/dist/macros/index.js.map +1 -1
- package/dist/macros/percentage/index.d.ts +1 -1
- package/dist/macros/percentage/index.js +1 -1
- package/dist/macros/percentage/index.js.map +1 -1
- package/dist/macros/report/index.d.ts +1 -1
- package/dist/macros/report/index.js +1 -1
- package/dist/macros/report/index.js.map +1 -1
- package/dist/macros/scoreCard/index.d.ts +1 -1
- package/dist/macros/scoreCard/index.js +1 -1
- package/dist/macros/scoreCard/index.js.map +1 -1
- package/dist/macros/vega/index.d.ts +1 -1
- package/dist/macros/vega/index.js +1 -1
- package/dist/macros/vega/index.js.map +1 -1
- package/dist/macros/vegalite/index.d.ts +1 -1
- package/dist/macros/vegalite/index.js +1 -1
- package/dist/macros/vegalite/index.js.map +1 -1
- package/dist/macros/xref/index.d.ts +1 -1
- package/dist/macros/xref/index.js +1 -1
- package/dist/macros/xref/index.js.map +1 -1
- package/dist/project-settings.d.ts +14 -1
- package/dist/project-settings.js +51 -1
- package/dist/project-settings.js.map +1 -1
- package/dist/resources/field-type-resource.d.ts +5 -0
- package/dist/resources/field-type-resource.js +8 -3
- package/dist/resources/field-type-resource.js.map +1 -1
- package/dist/resources/graph-view-resource.js +3 -5
- package/dist/resources/graph-view-resource.js.map +1 -1
- package/dist/resources/workflow-resource.js +6 -5
- package/dist/resources/workflow-resource.js.map +1 -1
- package/dist/utils/log-utils.js +1 -1
- package/dist/utils/log-utils.js.map +1 -1
- package/dist/utils/report.js +6 -0
- package/dist/utils/report.js.map +1 -1
- package/dist/utils/resource-utils.d.ts +8 -0
- package/dist/utils/resource-utils.js +11 -0
- package/dist/utils/resource-utils.js.map +1 -1
- package/package.json +7 -9
- package/src/card-metadata-updater.ts +1 -1
- package/src/command-handler.ts +70 -15
- package/src/command-manager.ts +4 -1
- package/src/commands/calculate.ts +18 -0
- package/src/commands/create.ts +31 -19
- package/src/commands/fetch.ts +152 -0
- package/src/commands/index.ts +2 -0
- package/src/commands/remove.ts +18 -12
- package/src/commands/rename.ts +11 -11
- package/src/commands/show.ts +68 -0
- package/src/commands/validate.ts +7 -8
- package/src/containers/project/calculation-engine.ts +26 -8
- package/src/containers/project.ts +71 -61
- package/src/containers/template.ts +1 -1
- package/src/interfaces/project-interfaces.ts +18 -0
- package/src/macros/base-macro.ts +5 -2
- package/src/macros/createCards/index.ts +1 -1
- package/src/macros/graph/index.ts +47 -51
- package/src/macros/image/index.ts +1 -7
- package/src/macros/include/index.ts +1 -1
- package/src/macros/index.ts +19 -7
- package/src/macros/percentage/index.ts +1 -1
- package/src/macros/report/index.ts +1 -1
- package/src/macros/scoreCard/index.ts +1 -1
- package/src/macros/vega/index.ts +1 -1
- package/src/macros/vegalite/index.ts +1 -1
- package/src/macros/xref/index.ts +1 -1
- package/src/project-settings.ts +62 -1
- package/src/resources/field-type-resource.ts +8 -3
- package/src/resources/graph-view-resource.ts +7 -5
- package/src/resources/workflow-resource.ts +7 -6
- package/src/utils/log-utils.ts +1 -1
- package/src/utils/report.ts +6 -0
- package/src/utils/resource-utils.ts +16 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
Cyberismo
|
|
3
|
+
Copyright © Cyberismo Ltd and contributors 2025
|
|
4
|
+
This program is free software: you can redistribute it and/or modify it under
|
|
5
|
+
the terms of the GNU Affero General Public License version 3 as published by
|
|
6
|
+
the Free Software Foundation.
|
|
7
|
+
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
8
|
+
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
9
|
+
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
10
|
+
details. You should have received a copy of the GNU Affero General Public
|
|
11
|
+
License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { mkdir } from 'node:fs/promises';
|
|
15
|
+
import { resolve, sep } from 'node:path';
|
|
16
|
+
import type { Project } from '../containers/project.js';
|
|
17
|
+
|
|
18
|
+
import { writeJsonFile } from '../utils/json.js';
|
|
19
|
+
import { validateJson } from '../utils/validate.js';
|
|
20
|
+
import { type ModuleSetting } from '../interfaces/project-interfaces.js';
|
|
21
|
+
import { errorFunction, getChildLogger } from '../utils/log-utils.js';
|
|
22
|
+
|
|
23
|
+
const FETCH_TIMEOUT = 30000; // 30s timeout for fetching a hub file.
|
|
24
|
+
const MAX_RESPONSE_SIZE = 1024 * 1024; // 1MB limit for safety
|
|
25
|
+
const HUB_SCHEMA = 'hubSchema';
|
|
26
|
+
const MODULE_LIST_FILE = 'moduleList.json';
|
|
27
|
+
const TEMP_FOLDER = `.temp`;
|
|
28
|
+
|
|
29
|
+
export const MODULE_LIST_FULL_PATH = `${TEMP_FOLDER}/${MODULE_LIST_FILE}`;
|
|
30
|
+
|
|
31
|
+
export class Fetch {
|
|
32
|
+
constructor(private project: Project) {}
|
|
33
|
+
|
|
34
|
+
private get logger() {
|
|
35
|
+
return getChildLogger({
|
|
36
|
+
module: 'fetch',
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private async fetchJSON(location: string, schemaId: string) {
|
|
41
|
+
try {
|
|
42
|
+
const url = new URL(`${location}/${MODULE_LIST_FILE}`);
|
|
43
|
+
if (!['http:', 'https:'].includes(url.protocol)) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
`Invalid protocol: ${url.protocol}. Only HTTP and HTTPS are supported.`,
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
this.logger.info(`Fetching module list from: ${url.toString()}`);
|
|
50
|
+
const response = await fetch(url.toString(), {
|
|
51
|
+
method: 'GET',
|
|
52
|
+
headers: {
|
|
53
|
+
Accept: 'application/json',
|
|
54
|
+
'User-Agent': 'Cyberismo/1.0',
|
|
55
|
+
},
|
|
56
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT),
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
if (!response.ok) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
`HTTP ${response.status}: ${response.statusText} when fetching from ${url.toString()}`,
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Check content length before downloading
|
|
66
|
+
const contentLength = response.headers.get('content-length');
|
|
67
|
+
if (contentLength && parseInt(contentLength) > MAX_RESPONSE_SIZE) {
|
|
68
|
+
throw new Error(
|
|
69
|
+
`Response too large: ${contentLength} bytes (max: ${MAX_RESPONSE_SIZE})`,
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const contentType = response.headers.get('content-type');
|
|
74
|
+
if (!contentType?.includes('application/json')) {
|
|
75
|
+
this.logger.warn(`Expected JSON response, got: ${contentType}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const json = await response.json();
|
|
79
|
+
// Validate the incoming JSON before saving it into a file.
|
|
80
|
+
await validateJson(json, { schemaId: schemaId });
|
|
81
|
+
|
|
82
|
+
// Validate JSON structure and prevent prototype pollution
|
|
83
|
+
if (typeof json !== 'object' || json === null || Array.isArray(json)) {
|
|
84
|
+
throw new Error('Response must be a JSON object');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Additional size check after JSON parsing
|
|
88
|
+
if (JSON.stringify(json).length > MAX_RESPONSE_SIZE) {
|
|
89
|
+
throw new Error('JSON content too large after parsing');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return json;
|
|
93
|
+
} catch (error) {
|
|
94
|
+
this.logger.error(
|
|
95
|
+
error,
|
|
96
|
+
`Failed to fetch module list from ${location}: ${errorFunction(error)}`,
|
|
97
|
+
);
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Fetches modules from modules hub(s) and writes them to a file.
|
|
104
|
+
*/
|
|
105
|
+
public async fetchHubs() {
|
|
106
|
+
const hubs = this.project.configuration.hubs;
|
|
107
|
+
|
|
108
|
+
const moduleMap: Map<string, ModuleSetting> = new Map([]);
|
|
109
|
+
|
|
110
|
+
for (const hub of hubs) {
|
|
111
|
+
const json = await this.fetchJSON(hub.location, HUB_SCHEMA);
|
|
112
|
+
json.modules.forEach((module: ModuleSetting) => {
|
|
113
|
+
if (!moduleMap.has(module.name)) {
|
|
114
|
+
moduleMap.set(module.name, module);
|
|
115
|
+
} else {
|
|
116
|
+
this.logger.info(
|
|
117
|
+
`Skipping module '${module.name}' since it was already listed.`,
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
const fullPath = resolve(this.project.basePath, MODULE_LIST_FULL_PATH);
|
|
125
|
+
const normalizedBasePath = resolve(this.project.basePath);
|
|
126
|
+
|
|
127
|
+
// Ensure the file is written within the project directory (prevent path traversal)
|
|
128
|
+
if (
|
|
129
|
+
!fullPath.startsWith(normalizedBasePath + sep) &&
|
|
130
|
+
fullPath !== normalizedBasePath
|
|
131
|
+
) {
|
|
132
|
+
throw new Error(
|
|
133
|
+
'Invalid file path: attempting to write outside project directory',
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
await mkdir(resolve(this.project.basePath, TEMP_FOLDER), {
|
|
138
|
+
recursive: true,
|
|
139
|
+
});
|
|
140
|
+
await writeJsonFile(fullPath, {
|
|
141
|
+
modules: Array.from(moduleMap.values()),
|
|
142
|
+
});
|
|
143
|
+
this.logger.info(`Module list written to: ${fullPath}`);
|
|
144
|
+
} catch (error) {
|
|
145
|
+
this.logger.error(
|
|
146
|
+
error,
|
|
147
|
+
`Failed to write module list to local file: ${errorFunction(error)}`,
|
|
148
|
+
);
|
|
149
|
+
throw error;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
package/src/commands/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { Calculate } from './calculate.js';
|
|
|
15
15
|
import { Create } from './create.js';
|
|
16
16
|
import { Edit } from './edit.js';
|
|
17
17
|
import { Export } from './export.js';
|
|
18
|
+
import { Fetch } from './fetch.js';
|
|
18
19
|
import { Import } from './import.js';
|
|
19
20
|
import { Move } from './move.js';
|
|
20
21
|
import { Remove } from './remove.js';
|
|
@@ -29,6 +30,7 @@ export {
|
|
|
29
30
|
Create,
|
|
30
31
|
Edit,
|
|
31
32
|
Export,
|
|
33
|
+
Fetch,
|
|
32
34
|
Import,
|
|
33
35
|
Move,
|
|
34
36
|
Remove,
|
package/src/commands/remove.ts
CHANGED
|
@@ -53,9 +53,6 @@ export class Remove {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
const attachmentFolder = await this.project.cardAttachmentFolder(cardKey);
|
|
56
|
-
if (!attachmentFolder) {
|
|
57
|
-
throw new Error(`Card '${cardKey}' not found`);
|
|
58
|
-
}
|
|
59
56
|
|
|
60
57
|
// Imported templates cannot be modified.
|
|
61
58
|
if (attachmentFolder.includes(MODULES_PATH)) {
|
|
@@ -95,13 +92,16 @@ export class Remove {
|
|
|
95
92
|
},
|
|
96
93
|
);
|
|
97
94
|
const promiseContainer: Promise<void>[] = [];
|
|
98
|
-
|
|
99
|
-
|
|
95
|
+
|
|
96
|
+
for (const item of allCards) {
|
|
97
|
+
const links = item.metadata?.links ?? [];
|
|
98
|
+
for (const link of links) {
|
|
100
99
|
if (link.cardKey === cardKey) {
|
|
101
100
|
promiseContainer.push(this.removeLink(item.key, link.cardKey));
|
|
102
101
|
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
105
|
await Promise.all(promiseContainer);
|
|
106
106
|
|
|
107
107
|
// Calculations need to be updated before card is removed.
|
|
@@ -182,6 +182,11 @@ export class Remove {
|
|
|
182
182
|
await this.project.updateCardMetadataKey(sourceCardKey, 'links', newLinks);
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
+
// Remove a hub from project.
|
|
186
|
+
private async removeHubLocation(name: string) {
|
|
187
|
+
await this.project.configuration.removeHub(name);
|
|
188
|
+
}
|
|
189
|
+
|
|
185
190
|
/**
|
|
186
191
|
* Removes either attachment, card, imported module, link or resource from project.
|
|
187
192
|
* @param type Type of resource
|
|
@@ -221,14 +226,15 @@ export class Remove {
|
|
|
221
226
|
return resource?.delete();
|
|
222
227
|
} else {
|
|
223
228
|
// Something else than resources...
|
|
224
|
-
if (type
|
|
229
|
+
if (type === 'attachment')
|
|
225
230
|
return this.removeAttachment(targetName, rest[0]);
|
|
226
|
-
else if (type
|
|
227
|
-
else if (type
|
|
231
|
+
else if (type === 'card') return this.removeCard(targetName);
|
|
232
|
+
else if (type === 'link')
|
|
228
233
|
return this.removeLink(targetName, rest[0], rest[1], rest.at(2));
|
|
229
|
-
else if (type
|
|
234
|
+
else if (type === 'module')
|
|
230
235
|
return this.moduleManager.removeModule(targetName);
|
|
231
|
-
else if (type
|
|
236
|
+
else if (type === 'label') return this.removeLabel(targetName, rest[0]);
|
|
237
|
+
else if (type === 'hub') return this.removeHubLocation(targetName);
|
|
232
238
|
}
|
|
233
239
|
throw new Error(`Unknown resource type '${type}'`);
|
|
234
240
|
}
|
package/src/commands/rename.ts
CHANGED
|
@@ -160,17 +160,17 @@ export class Rename {
|
|
|
160
160
|
);
|
|
161
161
|
|
|
162
162
|
// Then replace all values that match in the conversion map.
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
encoding: 'utf-8'
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
163
|
+
await Promise.all(
|
|
164
|
+
files.map(async (item) => {
|
|
165
|
+
const target = join(item.parentPath, item.name);
|
|
166
|
+
let fileContent = await readFile(target, { encoding: 'utf-8' });
|
|
167
|
+
for (const [key, value] of conversionMap) {
|
|
168
|
+
const re = new RegExp(key, 'g');
|
|
169
|
+
fileContent = fileContent.replace(re, value);
|
|
170
|
+
}
|
|
171
|
+
await writeFile(target, fileContent);
|
|
172
|
+
}),
|
|
173
|
+
);
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
// Changes the name of a resource to match the new prefix.
|
package/src/commands/show.ts
CHANGED
|
@@ -18,6 +18,8 @@ import { join, resolve } from 'node:path';
|
|
|
18
18
|
import { spawn } from 'node:child_process';
|
|
19
19
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
20
20
|
|
|
21
|
+
import { MODULE_LIST_FULL_PATH } from './fetch.js';
|
|
22
|
+
|
|
21
23
|
import mime from 'mime-types';
|
|
22
24
|
|
|
23
25
|
import type { attachmentPayload } from '../interfaces/request-status-interfaces.js';
|
|
@@ -26,6 +28,8 @@ import type {
|
|
|
26
28
|
Card,
|
|
27
29
|
CardListContainer,
|
|
28
30
|
ModuleContent,
|
|
31
|
+
HubSetting,
|
|
32
|
+
ModuleSettingFromHub,
|
|
29
33
|
ProjectFetchCardDetails,
|
|
30
34
|
ProjectMetadata,
|
|
31
35
|
Resource,
|
|
@@ -53,6 +57,8 @@ import ReportMacro from '../macros/report/index.js';
|
|
|
53
57
|
import TaskQueue from '../macros/task-queue.js';
|
|
54
58
|
import { evaluateMacros } from '../macros/index.js';
|
|
55
59
|
import { FolderResource } from '../resources/folder-resource.js';
|
|
60
|
+
import { readJsonFile } from '../utils/json.js';
|
|
61
|
+
import { getChildLogger } from '../utils/log-utils.js';
|
|
56
62
|
|
|
57
63
|
/**
|
|
58
64
|
* Show command.
|
|
@@ -76,6 +82,12 @@ export class Show {
|
|
|
76
82
|
]);
|
|
77
83
|
}
|
|
78
84
|
|
|
85
|
+
private get logger() {
|
|
86
|
+
return getChildLogger({
|
|
87
|
+
module: 'show',
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
79
91
|
// Collect all labels from cards.
|
|
80
92
|
private collectLabels = (cards: Card[]): string[] => {
|
|
81
93
|
return cards.reduce<string[]>((labels, card) => {
|
|
@@ -369,6 +381,54 @@ export class Show {
|
|
|
369
381
|
}
|
|
370
382
|
return resource.showFileNames();
|
|
371
383
|
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Shows importable modules.
|
|
387
|
+
* @param showAll - When true, shows all importable modules, even if they have already been imported
|
|
388
|
+
* @param showDetails - When true, shows all properties of modules, not just name.
|
|
389
|
+
* @returns list of modules; the list content depends on the parameters provided
|
|
390
|
+
* by default it is a list of module names that could be imported into the project,
|
|
391
|
+
* with 'showDetails' true, instead of name, the list consists of full details of the modules
|
|
392
|
+
* with 'showAll' true, the list consists of all modules in the hubs, even if they have already been imported
|
|
393
|
+
* Note that the two boolean options can be combined.
|
|
394
|
+
*/
|
|
395
|
+
public async showImportableModules(
|
|
396
|
+
showAll?: boolean,
|
|
397
|
+
showDetails?: boolean,
|
|
398
|
+
): Promise<ModuleSettingFromHub[]> {
|
|
399
|
+
try {
|
|
400
|
+
const moduleList = (
|
|
401
|
+
await readJsonFile(
|
|
402
|
+
resolve(this.project.basePath, MODULE_LIST_FULL_PATH),
|
|
403
|
+
)
|
|
404
|
+
).modules;
|
|
405
|
+
const currentModules = await this.project.modules();
|
|
406
|
+
const nonImportedModules = moduleList.filter(
|
|
407
|
+
(item: ModuleSettingFromHub) => {
|
|
408
|
+
return !currentModules.some((module) => item.name === module.name);
|
|
409
|
+
},
|
|
410
|
+
);
|
|
411
|
+
|
|
412
|
+
if (showAll && showDetails) {
|
|
413
|
+
return moduleList;
|
|
414
|
+
}
|
|
415
|
+
if (showAll) {
|
|
416
|
+
return moduleList?.map((item: ModuleSettingFromHub) => item?.name);
|
|
417
|
+
}
|
|
418
|
+
if (showDetails) {
|
|
419
|
+
return nonImportedModules;
|
|
420
|
+
}
|
|
421
|
+
// By default return the non-imported modules
|
|
422
|
+
return nonImportedModules.map((item: ModuleSettingFromHub) => item?.name);
|
|
423
|
+
} catch (error) {
|
|
424
|
+
if (error instanceof Error) {
|
|
425
|
+
this.logger.error(error.message);
|
|
426
|
+
}
|
|
427
|
+
// Module list doesn't exist, return empty list
|
|
428
|
+
return [];
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
372
432
|
/**
|
|
373
433
|
* Returns all unique labels in a project
|
|
374
434
|
* @returns labels in a list
|
|
@@ -406,6 +466,14 @@ export class Show {
|
|
|
406
466
|
return moduleDetails;
|
|
407
467
|
}
|
|
408
468
|
|
|
469
|
+
/**
|
|
470
|
+
* Shows hubs of the project.
|
|
471
|
+
* @returns list of hubs.
|
|
472
|
+
*/
|
|
473
|
+
public showHubs(): HubSetting[] {
|
|
474
|
+
return this.project.configuration.hubs;
|
|
475
|
+
}
|
|
476
|
+
|
|
409
477
|
/**
|
|
410
478
|
* Returns all project cards in the project. Cards don't have content and nor metadata.
|
|
411
479
|
* @note AppUi uses this method.
|
package/src/commands/validate.ts
CHANGED
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
// node
|
|
15
15
|
import { type Dirent } from 'node:fs';
|
|
16
16
|
import { basename, dirname, extname, join, parse, resolve } from 'node:path';
|
|
17
|
-
import { fileURLToPath } from 'node:url';
|
|
18
17
|
import { readdir } from 'node:fs/promises';
|
|
19
18
|
|
|
20
19
|
// dependencies
|
|
@@ -52,7 +51,7 @@ const SHORT_TEXT_MAX_LENGTH = 80;
|
|
|
52
51
|
|
|
53
52
|
import * as EmailValidator from 'email-validator';
|
|
54
53
|
import { evaluateMacros } from '../macros/index.js';
|
|
55
|
-
const baseDir =
|
|
54
|
+
const baseDir = import.meta.dirname;
|
|
56
55
|
const subFoldersToValidate = ['.cards', 'cardRoot'];
|
|
57
56
|
|
|
58
57
|
export interface LengthProvider {
|
|
@@ -606,17 +605,17 @@ export class Validate {
|
|
|
606
605
|
});
|
|
607
606
|
}
|
|
608
607
|
}
|
|
609
|
-
if (errorMsg.length) {
|
|
610
|
-
validationErrors += errorMsg
|
|
611
|
-
.filter(this.removeDuplicateEntries)
|
|
612
|
-
.join('\n');
|
|
613
|
-
}
|
|
614
608
|
// Validate that there are no duplicate card keys
|
|
615
609
|
for (const [key, count] of cardIds) {
|
|
616
610
|
if (count > 1) {
|
|
617
|
-
|
|
611
|
+
errorMsg.push(`Duplicate card key '${key}' found ${count} times`);
|
|
618
612
|
}
|
|
619
613
|
}
|
|
614
|
+
if (errorMsg.length) {
|
|
615
|
+
validationErrors += errorMsg
|
|
616
|
+
.filter(this.removeDuplicateEntries)
|
|
617
|
+
.join('\n');
|
|
618
|
+
}
|
|
620
619
|
}
|
|
621
620
|
} catch (error) {
|
|
622
621
|
validationErrors += errorFunction(error);
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
// node
|
|
15
15
|
import { basename, join, resolve } from 'node:path';
|
|
16
|
-
import { readFile } from 'node:fs/promises';
|
|
16
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
17
17
|
|
|
18
18
|
import { sanitizeSvgBase64 } from '../../utils/sanitize-svg.js';
|
|
19
19
|
import { instance } from '@viz-js/viz';
|
|
@@ -57,6 +57,7 @@ import {
|
|
|
57
57
|
solve,
|
|
58
58
|
setProgram,
|
|
59
59
|
removeProgram,
|
|
60
|
+
buildProgram,
|
|
60
61
|
} from '@cyberismo/node-clingo';
|
|
61
62
|
import { generateReportContent } from '../../utils/report.js';
|
|
62
63
|
import { lpFiles, graphvizReport } from '@cyberismo/assets';
|
|
@@ -110,6 +111,22 @@ export class CalculationEngine {
|
|
|
110
111
|
return createCardFacts(card, this.project);
|
|
111
112
|
}
|
|
112
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Exports logic program to a given file
|
|
116
|
+
* @param destination Destination file path
|
|
117
|
+
* @param programs Programs or categories to export
|
|
118
|
+
* @param query Query to export, if not provided, all programs will be exported
|
|
119
|
+
*/
|
|
120
|
+
public async exportLogicProgram(
|
|
121
|
+
destination: string,
|
|
122
|
+
programs: string[],
|
|
123
|
+
query?: QueryName,
|
|
124
|
+
) {
|
|
125
|
+
let logicProgram = query ? this.queryContent(query) : '';
|
|
126
|
+
logicProgram += await buildProgram('', programs);
|
|
127
|
+
await writeFile(destination, logicProgram);
|
|
128
|
+
}
|
|
129
|
+
|
|
113
130
|
// // Wrapper to run onCreation query.
|
|
114
131
|
private async creationQuery(cardKeys: string[], context: Context) {
|
|
115
132
|
if (!cardKeys) return undefined;
|
|
@@ -503,6 +520,13 @@ export class CalculationEngine {
|
|
|
503
520
|
return this.parseClingoResult(clingoOutput);
|
|
504
521
|
}
|
|
505
522
|
|
|
523
|
+
private queryContent(queryName: QueryName, options?: unknown) {
|
|
524
|
+
const content = lpFiles.queries[queryName];
|
|
525
|
+
const handlebars = Handlebars.create();
|
|
526
|
+
const compiled = handlebars.compile(content);
|
|
527
|
+
return compiled(options || {});
|
|
528
|
+
}
|
|
529
|
+
|
|
506
530
|
/**
|
|
507
531
|
* Runs a pre-defined query.
|
|
508
532
|
* @param queryName Name of the query file without extension
|
|
@@ -514,13 +538,7 @@ export class CalculationEngine {
|
|
|
514
538
|
context: Context = 'localApp',
|
|
515
539
|
options?: unknown,
|
|
516
540
|
): Promise<QueryResult<T>[]> {
|
|
517
|
-
|
|
518
|
-
const handlebars = Handlebars.create();
|
|
519
|
-
const compiled = handlebars.compile(content);
|
|
520
|
-
content = compiled(options || {});
|
|
521
|
-
if (!content) {
|
|
522
|
-
throw new Error(`Query file ${queryName} not found`);
|
|
523
|
-
}
|
|
541
|
+
const content = this.queryContent(queryName, options);
|
|
524
542
|
|
|
525
543
|
this.logger.trace({ queryName }, 'Running query');
|
|
526
544
|
const clingoOutput = await this.run(content, context);
|