@cyberismo/data-handler 0.0.2

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 (306) hide show
  1. package/LICENSE +702 -0
  2. package/dist/card-metadata-updater.d.ts +33 -0
  3. package/dist/card-metadata-updater.js +121 -0
  4. package/dist/card-metadata-updater.js.map +1 -0
  5. package/dist/command-handler.d.ts +96 -0
  6. package/dist/command-handler.js +557 -0
  7. package/dist/command-handler.js.map +1 -0
  8. package/dist/command-manager.d.ts +43 -0
  9. package/dist/command-manager.js +73 -0
  10. package/dist/command-manager.js.map +1 -0
  11. package/dist/commands/calculate.d.ts +86 -0
  12. package/dist/commands/calculate.js +444 -0
  13. package/dist/commands/calculate.js.map +1 -0
  14. package/dist/commands/create.d.ts +114 -0
  15. package/dist/commands/create.js +389 -0
  16. package/dist/commands/create.js.map +1 -0
  17. package/dist/commands/edit.d.ts +37 -0
  18. package/dist/commands/edit.js +99 -0
  19. package/dist/commands/edit.js.map +1 -0
  20. package/dist/commands/export-site.d.ts +45 -0
  21. package/dist/commands/export-site.js +301 -0
  22. package/dist/commands/export-site.js.map +1 -0
  23. package/dist/commands/export.d.ts +53 -0
  24. package/dist/commands/export.js +251 -0
  25. package/dist/commands/export.js.map +1 -0
  26. package/dist/commands/import.d.ts +53 -0
  27. package/dist/commands/import.js +133 -0
  28. package/dist/commands/import.js.map +1 -0
  29. package/dist/commands/index.d.ts +26 -0
  30. package/dist/commands/index.js +27 -0
  31. package/dist/commands/index.js.map +1 -0
  32. package/dist/commands/move.d.ts +55 -0
  33. package/dist/commands/move.js +341 -0
  34. package/dist/commands/move.js.map +1 -0
  35. package/dist/commands/remove.d.ts +38 -0
  36. package/dist/commands/remove.js +192 -0
  37. package/dist/commands/remove.js.map +1 -0
  38. package/dist/commands/rename.d.ts +46 -0
  39. package/dist/commands/rename.js +289 -0
  40. package/dist/commands/rename.js.map +1 -0
  41. package/dist/commands/show.d.ts +124 -0
  42. package/dist/commands/show.js +345 -0
  43. package/dist/commands/show.js.map +1 -0
  44. package/dist/commands/transition.d.ts +27 -0
  45. package/dist/commands/transition.js +92 -0
  46. package/dist/commands/transition.js.map +1 -0
  47. package/dist/commands/update.d.ts +29 -0
  48. package/dist/commands/update.js +64 -0
  49. package/dist/commands/update.js.map +1 -0
  50. package/dist/commands/validate.d.ts +143 -0
  51. package/dist/commands/validate.js +689 -0
  52. package/dist/commands/validate.js.map +1 -0
  53. package/dist/containers/card-container.d.ts +44 -0
  54. package/dist/containers/card-container.js +282 -0
  55. package/dist/containers/card-container.js.map +1 -0
  56. package/dist/containers/project/project-paths.d.ts +46 -0
  57. package/dist/containers/project/project-paths.js +105 -0
  58. package/dist/containers/project/project-paths.js.map +1 -0
  59. package/dist/containers/project/resource-collector.d.ts +86 -0
  60. package/dist/containers/project/resource-collector.js +331 -0
  61. package/dist/containers/project/resource-collector.js.map +1 -0
  62. package/dist/containers/project.d.ts +351 -0
  63. package/dist/containers/project.js +896 -0
  64. package/dist/containers/project.js.map +1 -0
  65. package/dist/containers/template.d.ts +108 -0
  66. package/dist/containers/template.js +433 -0
  67. package/dist/containers/template.js.map +1 -0
  68. package/dist/exceptions/index.d.ts +19 -0
  69. package/dist/exceptions/index.js +26 -0
  70. package/dist/exceptions/index.js.map +1 -0
  71. package/dist/index.d.ts +16 -0
  72. package/dist/index.js +15 -0
  73. package/dist/index.js.map +1 -0
  74. package/dist/interfaces/adoc.d.ts +12 -0
  75. package/dist/interfaces/adoc.js +13 -0
  76. package/dist/interfaces/adoc.js.map +1 -0
  77. package/dist/interfaces/macros.d.ts +45 -0
  78. package/dist/interfaces/macros.js +13 -0
  79. package/dist/interfaces/macros.js.map +1 -0
  80. package/dist/interfaces/project-interfaces.d.ts +121 -0
  81. package/dist/interfaces/project-interfaces.js +21 -0
  82. package/dist/interfaces/project-interfaces.js.map +1 -0
  83. package/dist/interfaces/request-status-interfaces.d.ts +28 -0
  84. package/dist/interfaces/request-status-interfaces.js +20 -0
  85. package/dist/interfaces/request-status-interfaces.js.map +1 -0
  86. package/dist/interfaces/resource-interfaces.d.ts +117 -0
  87. package/dist/interfaces/resource-interfaces.js +20 -0
  88. package/dist/interfaces/resource-interfaces.js.map +1 -0
  89. package/dist/macros/base-macro.d.ts +31 -0
  90. package/dist/macros/base-macro.js +126 -0
  91. package/dist/macros/base-macro.js.map +1 -0
  92. package/dist/macros/common.d.ts +17 -0
  93. package/dist/macros/common.js +23 -0
  94. package/dist/macros/common.js.map +1 -0
  95. package/dist/macros/createCards/index.d.ts +36 -0
  96. package/dist/macros/createCards/index.js +35 -0
  97. package/dist/macros/createCards/index.js.map +1 -0
  98. package/dist/macros/createCards/metadata.d.ts +14 -0
  99. package/dist/macros/createCards/metadata.js +18 -0
  100. package/dist/macros/createCards/metadata.js.map +1 -0
  101. package/dist/macros/graph/index.d.ts +29 -0
  102. package/dist/macros/graph/index.js +91 -0
  103. package/dist/macros/graph/index.js.map +1 -0
  104. package/dist/macros/graph/metadata.d.ts +14 -0
  105. package/dist/macros/graph/metadata.js +18 -0
  106. package/dist/macros/graph/metadata.js.map +1 -0
  107. package/dist/macros/index.d.ts +93 -0
  108. package/dist/macros/index.js +237 -0
  109. package/dist/macros/index.js.map +1 -0
  110. package/dist/macros/report/index.d.ts +26 -0
  111. package/dist/macros/report/index.js +70 -0
  112. package/dist/macros/report/index.js.map +1 -0
  113. package/dist/macros/report/metadata.d.ts +14 -0
  114. package/dist/macros/report/metadata.js +18 -0
  115. package/dist/macros/report/metadata.js.map +1 -0
  116. package/dist/macros/scoreCard/index.d.ts +30 -0
  117. package/dist/macros/scoreCard/index.js +38 -0
  118. package/dist/macros/scoreCard/index.js.map +1 -0
  119. package/dist/macros/scoreCard/metadata.d.ts +14 -0
  120. package/dist/macros/scoreCard/metadata.js +18 -0
  121. package/dist/macros/scoreCard/metadata.js.map +1 -0
  122. package/dist/macros/task-queue.d.ts +46 -0
  123. package/dist/macros/task-queue.js +69 -0
  124. package/dist/macros/task-queue.js.map +1 -0
  125. package/dist/module-manager.d.ts +62 -0
  126. package/dist/module-manager.js +350 -0
  127. package/dist/module-manager.js.map +1 -0
  128. package/dist/permissions/action-guard.d.ts +28 -0
  129. package/dist/permissions/action-guard.js +61 -0
  130. package/dist/permissions/action-guard.js.map +1 -0
  131. package/dist/project-settings.d.ts +42 -0
  132. package/dist/project-settings.js +120 -0
  133. package/dist/project-settings.js.map +1 -0
  134. package/dist/resources/array-handler.d.ts +28 -0
  135. package/dist/resources/array-handler.js +116 -0
  136. package/dist/resources/array-handler.js.map +1 -0
  137. package/dist/resources/card-type-resource.d.ts +72 -0
  138. package/dist/resources/card-type-resource.js +334 -0
  139. package/dist/resources/card-type-resource.js.map +1 -0
  140. package/dist/resources/create-defaults.d.ts +81 -0
  141. package/dist/resources/create-defaults.js +184 -0
  142. package/dist/resources/create-defaults.js.map +1 -0
  143. package/dist/resources/field-type-resource.d.ts +88 -0
  144. package/dist/resources/field-type-resource.js +411 -0
  145. package/dist/resources/field-type-resource.js.map +1 -0
  146. package/dist/resources/file-resource.d.ts +50 -0
  147. package/dist/resources/file-resource.js +301 -0
  148. package/dist/resources/file-resource.js.map +1 -0
  149. package/dist/resources/folder-resource.d.ts +66 -0
  150. package/dist/resources/folder-resource.js +100 -0
  151. package/dist/resources/folder-resource.js.map +1 -0
  152. package/dist/resources/graph-model-resource.d.ts +78 -0
  153. package/dist/resources/graph-model-resource.js +164 -0
  154. package/dist/resources/graph-model-resource.js.map +1 -0
  155. package/dist/resources/graph-view-resource.d.ts +78 -0
  156. package/dist/resources/graph-view-resource.js +163 -0
  157. package/dist/resources/graph-view-resource.js.map +1 -0
  158. package/dist/resources/link-type-resource.d.ts +62 -0
  159. package/dist/resources/link-type-resource.js +150 -0
  160. package/dist/resources/link-type-resource.js.map +1 -0
  161. package/dist/resources/report-resource.d.ts +77 -0
  162. package/dist/resources/report-resource.js +171 -0
  163. package/dist/resources/report-resource.js.map +1 -0
  164. package/dist/resources/resource-object.d.ts +108 -0
  165. package/dist/resources/resource-object.js +147 -0
  166. package/dist/resources/resource-object.js.map +1 -0
  167. package/dist/resources/template-resource.d.ts +82 -0
  168. package/dist/resources/template-resource.js +173 -0
  169. package/dist/resources/template-resource.js.map +1 -0
  170. package/dist/resources/workflow-resource.d.ts +67 -0
  171. package/dist/resources/workflow-resource.js +156 -0
  172. package/dist/resources/workflow-resource.js.map +1 -0
  173. package/dist/types/queries.d.ts +142 -0
  174. package/dist/types/queries.js +16 -0
  175. package/dist/types/queries.js.map +1 -0
  176. package/dist/utils/card-utils.d.ts +34 -0
  177. package/dist/utils/card-utils.js +78 -0
  178. package/dist/utils/card-utils.js.map +1 -0
  179. package/dist/utils/clingo-fact-builder.d.ts +58 -0
  180. package/dist/utils/clingo-fact-builder.js +126 -0
  181. package/dist/utils/clingo-fact-builder.js.map +1 -0
  182. package/dist/utils/clingo-facts.d.ts +97 -0
  183. package/dist/utils/clingo-facts.js +352 -0
  184. package/dist/utils/clingo-facts.js.map +1 -0
  185. package/dist/utils/clingo-parser.d.ts +59 -0
  186. package/dist/utils/clingo-parser.js +403 -0
  187. package/dist/utils/clingo-parser.js.map +1 -0
  188. package/dist/utils/clingo-program-builder.d.ts +39 -0
  189. package/dist/utils/clingo-program-builder.js +57 -0
  190. package/dist/utils/clingo-program-builder.js.map +1 -0
  191. package/dist/utils/common-utils.d.ts +24 -0
  192. package/dist/utils/common-utils.js +47 -0
  193. package/dist/utils/common-utils.js.map +1 -0
  194. package/dist/utils/constants.d.ts +18 -0
  195. package/dist/utils/constants.js +27 -0
  196. package/dist/utils/constants.js.map +1 -0
  197. package/dist/utils/csv.d.ts +18 -0
  198. package/dist/utils/csv.js +45 -0
  199. package/dist/utils/csv.js.map +1 -0
  200. package/dist/utils/file-utils.d.ts +69 -0
  201. package/dist/utils/file-utils.js +158 -0
  202. package/dist/utils/file-utils.js.map +1 -0
  203. package/dist/utils/json.d.ts +61 -0
  204. package/dist/utils/json.js +108 -0
  205. package/dist/utils/json.js.map +1 -0
  206. package/dist/utils/lexorank.d.ts +59 -0
  207. package/dist/utils/lexorank.js +159 -0
  208. package/dist/utils/lexorank.js.map +1 -0
  209. package/dist/utils/log-utils.d.ts +40 -0
  210. package/dist/utils/log-utils.js +109 -0
  211. package/dist/utils/log-utils.js.map +1 -0
  212. package/dist/utils/random.d.ts +19 -0
  213. package/dist/utils/random.js +34 -0
  214. package/dist/utils/random.js.map +1 -0
  215. package/dist/utils/resource-utils.d.ts +45 -0
  216. package/dist/utils/resource-utils.js +137 -0
  217. package/dist/utils/resource-utils.js.map +1 -0
  218. package/dist/utils/sanitize-svg.d.ts +18 -0
  219. package/dist/utils/sanitize-svg.js +38 -0
  220. package/dist/utils/sanitize-svg.js.map +1 -0
  221. package/dist/utils/user-preferences.d.ts +64 -0
  222. package/dist/utils/user-preferences.js +106 -0
  223. package/dist/utils/user-preferences.js.map +1 -0
  224. package/dist/utils/validate.d.ts +26 -0
  225. package/dist/utils/validate.js +53 -0
  226. package/dist/utils/validate.js.map +1 -0
  227. package/dist/utils/value-utils.d.ts +58 -0
  228. package/dist/utils/value-utils.js +181 -0
  229. package/dist/utils/value-utils.js.map +1 -0
  230. package/package.json +67 -0
  231. package/src/card-metadata-updater.ts +182 -0
  232. package/src/command-handler.ts +686 -0
  233. package/src/command-manager.ts +99 -0
  234. package/src/commands/calculate.ts +591 -0
  235. package/src/commands/create.ts +559 -0
  236. package/src/commands/edit.ts +123 -0
  237. package/src/commands/export-site.ts +356 -0
  238. package/src/commands/export.ts +315 -0
  239. package/src/commands/import.ts +169 -0
  240. package/src/commands/index.ts +42 -0
  241. package/src/commands/move.ts +451 -0
  242. package/src/commands/remove.ts +244 -0
  243. package/src/commands/rename.ts +378 -0
  244. package/src/commands/show.ts +442 -0
  245. package/src/commands/transition.ts +127 -0
  246. package/src/commands/update.ts +76 -0
  247. package/src/commands/validate.ts +962 -0
  248. package/src/containers/card-container.ts +378 -0
  249. package/src/containers/project/project-paths.ts +127 -0
  250. package/src/containers/project/resource-collector.ts +379 -0
  251. package/src/containers/project.ts +1135 -0
  252. package/src/containers/template.ts +573 -0
  253. package/src/exceptions/index.ts +29 -0
  254. package/src/index.ts +33 -0
  255. package/src/interfaces/adoc.ts +18 -0
  256. package/src/interfaces/macros.ts +54 -0
  257. package/src/interfaces/project-interfaces.ts +208 -0
  258. package/src/interfaces/request-status-interfaces.ts +30 -0
  259. package/src/interfaces/resource-interfaces.ts +179 -0
  260. package/src/macros/base-macro.ts +176 -0
  261. package/src/macros/common.ts +24 -0
  262. package/src/macros/createCards/index.ts +57 -0
  263. package/src/macros/createCards/metadata.ts +21 -0
  264. package/src/macros/graph/index.ts +130 -0
  265. package/src/macros/graph/metadata.ts +21 -0
  266. package/src/macros/index.ts +321 -0
  267. package/src/macros/report/index.ts +88 -0
  268. package/src/macros/report/metadata.ts +21 -0
  269. package/src/macros/scoreCard/index.ts +55 -0
  270. package/src/macros/scoreCard/metadata.ts +21 -0
  271. package/src/macros/task-queue.ts +79 -0
  272. package/src/module-manager.ts +443 -0
  273. package/src/permissions/action-guard.ts +77 -0
  274. package/src/project-settings.ts +140 -0
  275. package/src/resources/array-handler.ts +141 -0
  276. package/src/resources/card-type-resource.ts +455 -0
  277. package/src/resources/create-defaults.ts +216 -0
  278. package/src/resources/field-type-resource.ts +533 -0
  279. package/src/resources/file-resource.ts +433 -0
  280. package/src/resources/folder-resource.ts +140 -0
  281. package/src/resources/graph-model-resource.ts +205 -0
  282. package/src/resources/graph-view-resource.ts +199 -0
  283. package/src/resources/link-type-resource.ts +191 -0
  284. package/src/resources/report-resource.ts +224 -0
  285. package/src/resources/resource-object.ts +246 -0
  286. package/src/resources/template-resource.ts +210 -0
  287. package/src/resources/workflow-resource.ts +205 -0
  288. package/src/types/queries.ts +149 -0
  289. package/src/utils/card-utils.ts +83 -0
  290. package/src/utils/clingo-fact-builder.ts +167 -0
  291. package/src/utils/clingo-facts.ts +550 -0
  292. package/src/utils/clingo-parser.ts +519 -0
  293. package/src/utils/clingo-program-builder.ts +71 -0
  294. package/src/utils/common-utils.ts +54 -0
  295. package/src/utils/constants.ts +32 -0
  296. package/src/utils/csv.ts +53 -0
  297. package/src/utils/file-utils.ts +182 -0
  298. package/src/utils/json.ts +118 -0
  299. package/src/utils/lexorank.ts +180 -0
  300. package/src/utils/log-utils.ts +127 -0
  301. package/src/utils/random.ts +37 -0
  302. package/src/utils/resource-utils.ts +180 -0
  303. package/src/utils/sanitize-svg.ts +46 -0
  304. package/src/utils/user-preferences.ts +126 -0
  305. package/src/utils/validate.ts +66 -0
  306. package/src/utils/value-utils.ts +189 -0
@@ -0,0 +1,53 @@
1
+ /**
2
+ Cyberismo
3
+ Copyright © Cyberismo Ltd and contributors 2024
4
+
5
+ This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License version 3 as published by the Free Software Foundation.
6
+
7
+ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
8
+
9
+ You should have received a copy of the GNU Affero General Public
10
+ License along with this program. If not, see <https://www.gnu.org/licenses/>.
11
+ */
12
+
13
+ import { readFile } from 'fs/promises';
14
+ import { parse } from 'csv-parse/sync';
15
+ import type { CSVRowRaw } from '../interfaces/project-interfaces.js';
16
+
17
+ /**
18
+ * Reads a CSV file and returns its content as an array of objects.
19
+ * @param file Path to the CSV file.
20
+ * @returns Array of objects. Each object represents a row in the CSV file.
21
+ */
22
+ export async function readCsvFile(file: string): Promise<CSVRowRaw[]> {
23
+ const content = await readFile(file, {
24
+ encoding: 'utf-8',
25
+ });
26
+ const records = parse(content, {
27
+ bom: true,
28
+ });
29
+
30
+ if (!Array.isArray(records) || records.length < 2) {
31
+ throw new Error('CSV file must have headers');
32
+ }
33
+
34
+ const [headers, ...data] = records;
35
+
36
+ if (
37
+ !Array.isArray(headers) ||
38
+ new Set(headers).size !== headers.length ||
39
+ headers.length === 0
40
+ ) {
41
+ throw new Error('Error parsing header');
42
+ }
43
+
44
+ return data.map((row) => {
45
+ if (!Array.isArray(row)) {
46
+ throw new Error('Row is not an array');
47
+ }
48
+ return headers.reduce((acc, header, index) => {
49
+ acc[header] = row[index];
50
+ return acc;
51
+ }, {});
52
+ });
53
+ }
@@ -0,0 +1,182 @@
1
+ /**
2
+ Cyberismo
3
+ Copyright © Cyberismo Ltd and contributors 2024
4
+
5
+ This program is free software: you can redistribute it and/or modify it under
6
+ the terms of the GNU Affero General Public License version 3 as published by
7
+ the Free Software Foundation. This program is distributed in the hope that it
8
+ will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
9
+ of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10
+ See the GNU Affero General Public License for more details.
11
+ You should have received a copy of the GNU Affero General Public
12
+ License along with this program. If not, see <https://www.gnu.org/licenses/>.
13
+ */
14
+
15
+ import {
16
+ copyFile,
17
+ mkdir,
18
+ readdir,
19
+ rm,
20
+ unlink,
21
+ writeFile,
22
+ } from 'node:fs/promises';
23
+ import { existsSync, readdirSync } from 'node:fs';
24
+ import { dirname, join, sep } from 'node:path';
25
+ import { homedir } from 'node:os';
26
+
27
+ /**
28
+ * Works like the writeFile method, but ensures that the directory exists
29
+ * There is only one difference: This method only supports a string as the filePath
30
+ */
31
+ export async function writeFileSafe(
32
+ filePath: string,
33
+ data: Parameters<typeof writeFile>[1],
34
+ options?: Parameters<typeof writeFile>[2],
35
+ ) {
36
+ const dir = dirname(filePath);
37
+ await mkdir(dir, {
38
+ recursive: true,
39
+ });
40
+ return writeFile(filePath, data, options);
41
+ }
42
+
43
+ /**
44
+ * Copies directory content (subdirectories and files) to destination.
45
+ * Note that it won't create 'source', but copies all that is inside of 'source'.
46
+ * @param source path to start from
47
+ * @param destination path where to copy to
48
+ */
49
+ export async function copyDir(source: string, destination: string) {
50
+ const entries = await readdir(source, { withFileTypes: true });
51
+ for (const entry of entries) {
52
+ const sourcePath = join(source, entry.name);
53
+ const destinationPath = join(destination, entry.name);
54
+ if (entry.isDirectory()) {
55
+ await mkdir(destinationPath, { recursive: true });
56
+ await copyDir(sourcePath, destinationPath);
57
+ } else {
58
+ await mkdir(destination, { recursive: true });
59
+ await copyFile(sourcePath, destinationPath);
60
+ }
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Delete directory.
66
+ * @param path path to be deleted
67
+ */
68
+ export async function deleteDir(path: string) {
69
+ await rm(resolveTilde(path), { recursive: true, force: true });
70
+ }
71
+
72
+ /**
73
+ * Delete file.
74
+ * @param path path to file to be deleted
75
+ * @returns true, if file was deleted; false otherwise.
76
+ */
77
+ export async function deleteFile(path: string): Promise<boolean> {
78
+ if (!path) {
79
+ return false;
80
+ }
81
+ try {
82
+ await unlink(path);
83
+ } catch {
84
+ console.error(`Cannot delete file '${path}'`);
85
+ return false;
86
+ }
87
+ return true;
88
+ }
89
+
90
+ /**
91
+ * Removes extension from filename.
92
+ * @param filename Filename
93
+ * @returns filename without extension. If there was no extension, returns the original filename.
94
+ */
95
+ export function stripExtension(filename: string) {
96
+ // First handle special cases. Just return the filename in all of these cases.
97
+ // 1) If the filename ends to "."" or ".." (e.g. ".cards/local/..")
98
+ const parts = filename.split(sep);
99
+ if (
100
+ parts.at(parts.length - 1) === '..' ||
101
+ parts.at(parts.length - 1) === '.'
102
+ ) {
103
+ return filename;
104
+ }
105
+ const dotLocation = filename.lastIndexOf('.');
106
+ const sepLocation = filename.lastIndexOf(sep);
107
+ // 2) If there is a dot in the filename before sep (e.g. ".cards/local")
108
+ if (dotLocation < sepLocation) {
109
+ return filename;
110
+ }
111
+ // 3) If there is a dot in filename but it is not an actual extension (e.g. "test/.filename", or ".filename")
112
+ if (dotLocation === 0 || filename.at(dotLocation - 1) === sep) {
113
+ return filename;
114
+ }
115
+
116
+ const noExtension = filename.split('.').slice(0, -1).join('.');
117
+ // if there was no extension at all, return the original file name.
118
+ return noExtension ? noExtension : filename;
119
+ }
120
+
121
+ /**
122
+ * Lists all files from a folder.
123
+ * @param path path to folder
124
+ * @param pathPrefix relative adjustment to 'path', if any; optional; by default empty.
125
+ * @param files currently collected files; optional; by default empty.
126
+ * @returns array of filenames that are in the folder or in one of its subfolders.
127
+ * @note that 'pathPrefix' and 'files' are generally only used in internal recursion.
128
+ * When calling this from code, do not pass the parameters.
129
+ */
130
+ export function getFilesSync(
131
+ path: string,
132
+ pathPrefix: string = '',
133
+ files: string[] = [],
134
+ ): string[] {
135
+ try {
136
+ for (const entry of readdirSync(path, { withFileTypes: true })) {
137
+ const relativePath = pathPrefix
138
+ ? join(pathPrefix, entry.name)
139
+ : entry.name;
140
+
141
+ if (entry.isFile()) {
142
+ files.push(relativePath);
143
+ } else if (entry.isDirectory()) {
144
+ getFilesSync(join(path, entry.name), relativePath, files);
145
+ }
146
+ }
147
+ } catch {
148
+ // do nothing, wrong path, or no permissions to read the files
149
+ }
150
+ return files;
151
+ }
152
+
153
+ /**
154
+ * Checks if file or folder exists.
155
+ * @param path file or folder path
156
+ * @returns true if file exists, otherwise false
157
+ */
158
+ export function pathExists(path: string): boolean {
159
+ path = resolveTilde(path);
160
+ return existsSync(path);
161
+ }
162
+
163
+ /**
164
+ * Handles tilde from a path (ie. appends user home to its place).
165
+ * @param filePath Path to handle tilde from.
166
+ * @returns Path with tilde resolved.
167
+ */
168
+ export function resolveTilde(filePath: string): string {
169
+ if (!filePath || typeof filePath !== 'string') {
170
+ return '';
171
+ }
172
+ // '~/folder/path' or '~' not '~alias/folder/path'
173
+ if (filePath.startsWith('~/') || filePath === '~') {
174
+ return filePath.replace('~', homedir());
175
+ }
176
+ return filePath;
177
+ }
178
+
179
+ /**
180
+ * Path separator RE.
181
+ */
182
+ export const sepRegex = /[/\\]/;
@@ -0,0 +1,118 @@
1
+ /**
2
+ Cyberismo
3
+ Copyright © Cyberismo Ltd and contributors 2024
4
+
5
+ This program is free software: you can redistribute it and/or modify it under
6
+ the terms of the GNU Affero General Public License version 3 as published by
7
+ the Free Software Foundation. This program is distributed in the hope that it
8
+ will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
9
+ of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10
+ See the GNU Affero General Public License for more details.
11
+ You should have received a copy of the GNU Affero General Public
12
+ License along with this program. If not, see <https://www.gnu.org/licenses/>.
13
+ */
14
+
15
+ import { readFileSync } from 'node:fs';
16
+ import { type FileHandle, readFile, writeFile } from 'node:fs/promises';
17
+
18
+ /**
19
+ * Handles reading of a JSON file.
20
+ * @param file file name (and path) to read.
21
+ * @returns Parsed JSON content.
22
+ * @throws if file is not found, or file is not a JSON file.
23
+ */
24
+ export function readJsonFileSync(file: string) {
25
+ try {
26
+ const raw = readFileSync(file, { flag: 'rs', encoding: 'utf-8' });
27
+ const returnValue = JSON.parse(raw);
28
+ return returnValue;
29
+ } catch (error) {
30
+ if (error instanceof Error) {
31
+ throw new Error(
32
+ `Error while handling JSON file '${file}' : ${error.message}`,
33
+ );
34
+ }
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Handles reading of a JSON file.
40
+ * @param file file name (and path) to read.
41
+ * @returns Parsed JSON content.
42
+ * @throws if file is not found, or file is not a JSON file.
43
+ */
44
+ export async function readJsonFile(file: string) {
45
+ try {
46
+ const raw = await readFile(file, { encoding: 'utf-8' });
47
+ return JSON.parse(raw);
48
+ } catch (error) {
49
+ if (error instanceof Error) {
50
+ throw new Error(
51
+ `Error while handling JSON file '${file}' : ${error.message}`,
52
+ );
53
+ }
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Reads ADOC file.
59
+ * @param file file name (and path) to read.
60
+ * @returns ADOC file content.
61
+ * @throws if file is not found.
62
+ */
63
+ export function readADocFileSync(file: string) {
64
+ try {
65
+ const raw = readFileSync(file, { encoding: 'utf-8' });
66
+ return raw;
67
+ } catch {
68
+ throw new Error(`Adoc file '${file}' not found`);
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Removes leading or trailing whitespace
74
+ * Can be passed to JSON.stringify method
75
+ * @param _ First parameter is key of object, but it is not used
76
+ * @param value Value of the json object
77
+ * @returns JSON object with values trimmed for leading or trailing white space.
78
+ */
79
+ export function trimReplacer(_: string, value: unknown) {
80
+ if (typeof value === 'string') {
81
+ return value.trim();
82
+ }
83
+ return value;
84
+ }
85
+
86
+ /**
87
+ * Format an object with JSON.stringify
88
+ *
89
+ * The purpose of this function is to format the JSON output in a centralised function
90
+ * so that the format can be controlled in a single location.
91
+ *
92
+ * @param json JSON object to format.
93
+ * @returns Formatted JSON string
94
+ */
95
+ export function formatJson(json: object) {
96
+ return JSON.stringify(json, trimReplacer, 4);
97
+ }
98
+
99
+ /**
100
+ * Writes and formats a JSON file.
101
+ * @param filename file name (and path) to write.
102
+ * @param json JSON object to format.
103
+ * @param options Optional, write options
104
+ * @return true if write succeeded, false otherwise.
105
+ */
106
+ export async function writeJsonFile(
107
+ filename: string | FileHandle,
108
+ json: object,
109
+ options?: object,
110
+ ) {
111
+ try {
112
+ await writeFile(filename, formatJson(json), options);
113
+ return true;
114
+ } catch {
115
+ // do nothing, file didn't exist, or no permissions to write
116
+ }
117
+ return false;
118
+ }
@@ -0,0 +1,180 @@
1
+ /**
2
+ Cyberismo
3
+ Copyright © Cyberismo Ltd and contributors 2024
4
+
5
+ This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License version 3 as published by the Free Software Foundation.
6
+
7
+ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
8
+
9
+ You should have received a copy of the GNU Affero General Public
10
+ License along with this program. If not, see <https://www.gnu.org/licenses/>.
11
+ */
12
+ const ALPHABET = 'abcdefghijklmnopqrstuvwxyz';
13
+ const ALPHABET_SIZE = ALPHABET.length;
14
+
15
+ const PREFIX = '0|'; // prefix to allow easy addition of buckets later if required
16
+
17
+ export const FIRST_RANK = PREFIX + ALPHABET[0];
18
+ export const LAST_RANK = PREFIX + ALPHABET[ALPHABET_SIZE - 1];
19
+
20
+ export const EMPTY_RANK = `1|${ALPHABET[0]}`; // rank to be used when no rank is available
21
+
22
+ type Rank = string; // Rank is a string that represents a number in base 26
23
+
24
+ /**
25
+ * Convert a number to base 26
26
+ * @param num number to convert to base 26
27
+ * @returns the string representation of the number in base 26
28
+ */
29
+ export function enbase(num: number): string {
30
+ if (num === 0) return ALPHABET[0];
31
+
32
+ let result = '';
33
+ while (num > 0) {
34
+ result =
35
+ String.fromCharCode(ALPHABET.charCodeAt(0) + (num % ALPHABET_SIZE)) +
36
+ result;
37
+ num = Math.floor(num / ALPHABET_SIZE);
38
+ }
39
+ return result;
40
+ }
41
+
42
+ /**
43
+ * Convert a rank to a number
44
+ * @param str rank to convert to a number
45
+ * @returns the number representation of the rank
46
+ */
47
+ export function debase(str: string): number {
48
+ let result = 0;
49
+ for (let i = 0; i < str.length; i++) {
50
+ result +=
51
+ (str.charCodeAt(i) - ALPHABET.charCodeAt(0)) *
52
+ Math.pow(ALPHABET_SIZE, str.length - i - 1);
53
+ }
54
+ return result;
55
+ }
56
+
57
+ /**
58
+ * Returns the next available rank after the given rank
59
+ * @param rank rank to get the next rank after
60
+ * @returns the next available rank after the given rank
61
+ */
62
+ export function getRankAfter(rank: Rank): Rank {
63
+ rank = rank.replace(PREFIX, '');
64
+ let num = debase(rank);
65
+ num++;
66
+ const newRank = enbase(num);
67
+ if (newRank.length > rank.length) {
68
+ return PREFIX + rank + ALPHABET[ALPHABET_SIZE / 2];
69
+ }
70
+ return PREFIX + newRank;
71
+ }
72
+ /**
73
+ * Returns the previous available rank before the given rank
74
+ * if the rank is already the first rank, it throws an error
75
+ * @param rank rank to get the previous rank before
76
+ * @returns the previous available rank before the given rank
77
+ */
78
+ export function getRankBefore(rank: Rank): Rank {
79
+ rank = rank.replace(PREFIX, '');
80
+ let num = debase(rank);
81
+ num--;
82
+ if (num < 0) {
83
+ throw new Error('Rank cannot be negative');
84
+ }
85
+ return PREFIX + enbase(num);
86
+ }
87
+
88
+ /**
89
+ * Get the rank between two ranks
90
+ * Rank is a string that represents a number in base 26
91
+ * Rank must be a string of lowercase letters
92
+ * @param rank1 first rank
93
+ * @param rank2 second rank
94
+ * @returns the rank between the two ranks
95
+ */
96
+ export function getRankBetween(rank1: string, rank2: string): string {
97
+ // must be padded for the comparison
98
+
99
+ rank1 = rank1.replace(PREFIX, '');
100
+ rank2 = rank2.replace(PREFIX, '');
101
+
102
+ const length = Math.max(rank1.length, rank2.length);
103
+
104
+ // padding with 'a' to make the ranks equal length
105
+ // since comparison is done lexicographically
106
+ // it does not effect the comparison
107
+
108
+ const paddedRank1 = rank1.padEnd(length, ALPHABET[0]);
109
+
110
+ const paddedRank2 = rank2.padEnd(length, ALPHABET[0]);
111
+
112
+ const num1 = debase(paddedRank1);
113
+ const num2 = debase(paddedRank2);
114
+
115
+ if (num1 >= num2) {
116
+ throw new Error('Rank1 must be smaller than rank2');
117
+ }
118
+
119
+ const res = enbase(Math.floor((num1 + num2) / 2)).padStart(
120
+ length,
121
+ ALPHABET[0],
122
+ );
123
+ if (res !== paddedRank1 && res !== paddedRank2) {
124
+ return PREFIX + res;
125
+ }
126
+ return PREFIX + res + ALPHABET[ALPHABET_SIZE / 2];
127
+ }
128
+
129
+ /**
130
+ * Rebalance the ranks so that the distance between each rank is equal
131
+ * @param rankAmount number of ranks to rebalance
132
+ * @returns rebalanced ranks
133
+ */
134
+ export function rebalanceRanks(rankAmount: number): string[] {
135
+ if (rankAmount === 0) return [];
136
+ if (rankAmount === 1) return [FIRST_RANK];
137
+ const requiredLevels = Math.ceil(
138
+ Math.log(rankAmount) / Math.log(ALPHABET_SIZE),
139
+ );
140
+ // set minumum rank consisting of all 'a's and maximum rank consisting of all 'z's
141
+ const minRank = ALPHABET[0].repeat(requiredLevels);
142
+ const maxRank = ALPHABET[ALPHABET_SIZE - 1].repeat(requiredLevels);
143
+
144
+ // step size determines the distance between each rank
145
+ const stepSize = Math.floor(debase(maxRank) / (rankAmount - 1));
146
+
147
+ const ranks = [];
148
+ for (let i = 0; i < rankAmount; i++) {
149
+ if (i === 0) {
150
+ ranks.push(PREFIX + minRank);
151
+ } else if (i === rankAmount - 1) {
152
+ ranks.push(PREFIX + maxRank);
153
+ } else {
154
+ const rank = debase(minRank) + stepSize * i;
155
+ ranks.push(PREFIX + enbase(rank));
156
+ }
157
+ }
158
+ return ranks;
159
+ }
160
+
161
+ /**
162
+ * Sort items based on lexorank
163
+ * @param items items to sort
164
+ * @param rankGetter should return the lexorank of the item
165
+ * @returns sorted items
166
+ */
167
+ export function sortItems<T>(items: T[], rankGetter: (item: T) => string): T[] {
168
+ if (items.length === 0) return items;
169
+ return items.sort((a, b) => compare(rankGetter(a), rankGetter(b)));
170
+ }
171
+
172
+ /**
173
+ * default compare function
174
+ * @param a first string
175
+ * @param b second string
176
+ * @returns -1 if a < b, 0 if a === b, 1 if a > b
177
+ */
178
+ export function compare(a: string, b: string): number {
179
+ return a.localeCompare(b);
180
+ }
@@ -0,0 +1,127 @@
1
+ /**
2
+ Cyberismo
3
+ Copyright © Cyberismo Ltd and contributors 2024
4
+
5
+ This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License version 3 as published by the Free Software Foundation.
6
+
7
+ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
8
+
9
+ You should have received a copy of the GNU Affero General Public
10
+ License along with this program. If not, see <https://www.gnu.org/licenses/>.
11
+ */
12
+
13
+ import { join } from 'path';
14
+ import pino, { type TransportTargetOptions } from 'pino';
15
+ import { fileURLToPath } from 'url';
16
+ const LOG_FILE_LOCATION: string = join(
17
+ fileURLToPath(import.meta.url),
18
+ '../../../../../logs/data-handler.trace',
19
+ );
20
+
21
+ function getLogger() {
22
+ const all: TransportTargetOptions[] = [
23
+ {
24
+ target: 'pino/file',
25
+ options: { destination: LOG_FILE_LOCATION, mkdir: true }, // trace file
26
+ level: 'trace',
27
+ },
28
+ {
29
+ target: 'pino-pretty',
30
+ level: process.env.LOG_LEVEL || 'debug',
31
+ },
32
+ ];
33
+
34
+ const [file, stdout] = all;
35
+
36
+ if (process.env.NODE_ENV === 'test') {
37
+ // While testing, we simply use stdout and info level by default
38
+ return pino.default({
39
+ level: process.env.LOG_LEVEL || 'info',
40
+ transport: stdout,
41
+ });
42
+ }
43
+ if (process.env.NODE_ENV === 'development') {
44
+ // In dev mode, only stdout and debug logs by default
45
+ return pino.default({
46
+ level: 'trace',
47
+ transport: {
48
+ targets: all,
49
+ },
50
+ });
51
+ }
52
+ return pino.default({
53
+ level: 'trace',
54
+ transport: file,
55
+ });
56
+ }
57
+
58
+ export const logger = getLogger();
59
+
60
+ /**
61
+ * Returns error message string from an Error object.
62
+ * @param error Error object
63
+ * @returns Error message.
64
+ */
65
+ export function errorFunction(error: unknown): string {
66
+ if (error instanceof Error) {
67
+ const err: Error = error;
68
+ return errorMessage(`${err.message}`);
69
+ } else if (typeof error === 'string') {
70
+ return errorMessage(`${error}`);
71
+ } else {
72
+ return `${logError.name} called without an error object. Actual object is ${JSON.stringify(error)}`;
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Same as 'errorFunction' but can do automatic replacement fof the error message string.
78
+ * @param message Error message
79
+ * @param toReplace replacement substring
80
+ * @param replaceWith string that 'toReplace' is replaced with.
81
+ * @returns Modified error message.
82
+ */
83
+ export function errorMessage(
84
+ message: string,
85
+ toReplace?: string,
86
+ replaceWith?: string,
87
+ ): string {
88
+ let errorMessage = message;
89
+ if (toReplace && replaceWith) {
90
+ errorMessage = message.replace(toReplace, replaceWith);
91
+ }
92
+ return `${errorMessage}`;
93
+ }
94
+
95
+ /**
96
+ * Logs error from Error object.
97
+ * @param error potentially an Error object. When exceptions are raised, they are typically Error objects.
98
+ */
99
+ export function logError(error: unknown) {
100
+ if (error instanceof Error) {
101
+ const err: Error = error;
102
+ logErrorMessage(`${err.message}`);
103
+ } else {
104
+ console.error(
105
+ `${logError.name} called without an error object. Actual object is ${JSON.stringify(error)}`,
106
+ );
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Log error message in RED. Certain parts of messages can be replaced.
112
+ * This is useful, if including a message from external sources, and want to reduce the verbosity of the message.
113
+ * @param message Error message to log.
114
+ * @param toReplace String to look for.
115
+ * @param replaceWith Replace 'toReplace' with this. Only replaces first instance of 'toReplace'.
116
+ */
117
+ export function logErrorMessage(
118
+ message: string,
119
+ toReplace?: string,
120
+ replaceWith?: string,
121
+ ) {
122
+ let errorMessage = message;
123
+ if (toReplace && replaceWith) {
124
+ errorMessage = message.replace(toReplace, replaceWith);
125
+ }
126
+ console.error(`${errorMessage}`);
127
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ Cyberismo
3
+ Copyright © Cyberismo Ltd and contributors 2024
4
+
5
+ This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License version 3 as published by the Free Software Foundation.
6
+
7
+ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
8
+
9
+ You should have received a copy of the GNU Affero General Public
10
+ License along with this program. If not, see <https://www.gnu.org/licenses/>.
11
+ */
12
+
13
+ import { randomInt } from 'node:crypto';
14
+
15
+ // Maximum base for randomInt.
16
+ const MAX_VALUE = Math.pow(2, 48) - 1;
17
+
18
+ /**
19
+ * Returns a random string in given base with given length.
20
+ * @param base which base to be used, e.g. 36 for base-36, must be an integer
21
+ * @param length length of string, must be an integer
22
+ * @returns a random string
23
+ * @throws if parameters are not integer numbers
24
+ */
25
+ export function generateRandomString(base: number, length: number): string {
26
+ if (!Number.isInteger(base) || !Number.isInteger(length)) {
27
+ throw new Error('parameters must be integers');
28
+ }
29
+
30
+ // Generate a random number between 0 and given base max number - 1
31
+ const maxBaseNumber = Math.min(MAX_VALUE, Math.pow(base, length));
32
+ let generatedString = '';
33
+ while (generatedString.length < length) {
34
+ generatedString += randomInt(maxBaseNumber).toString(base);
35
+ }
36
+ return generatedString.substring(0, length);
37
+ }