@cyberismo/backend 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 (108) hide show
  1. package/dist/app.d.ts +21 -0
  2. package/dist/app.js +11 -3
  3. package/dist/app.js.map +1 -1
  4. package/dist/common/validationSchemas.d.ts +48 -0
  5. package/dist/domain/calculations/index.d.ts +15 -0
  6. package/dist/domain/calculations/schema.d.ts +16 -0
  7. package/dist/domain/calculations/service.d.ts +14 -0
  8. package/dist/domain/cardTypes/index.d.ts +15 -0
  9. package/dist/domain/cardTypes/schema.d.ts +17 -0
  10. package/dist/domain/cardTypes/service.d.ts +15 -0
  11. package/dist/domain/cards/index.d.ts +15 -0
  12. package/dist/domain/cards/index.js +22 -6
  13. package/dist/domain/cards/index.js.map +1 -1
  14. package/dist/domain/cards/lib.d.ts +29 -0
  15. package/dist/domain/cards/lib.js +73 -2
  16. package/dist/domain/cards/lib.js.map +1 -1
  17. package/dist/domain/cards/service.d.ts +61 -0
  18. package/dist/domain/cards/service.js +20 -12
  19. package/dist/domain/cards/service.js.map +1 -1
  20. package/dist/domain/fieldTypes/index.d.ts +15 -0
  21. package/dist/domain/fieldTypes/schema.d.ts +28 -0
  22. package/dist/domain/fieldTypes/service.d.ts +16 -0
  23. package/dist/domain/graphModels/index.d.ts +15 -0
  24. package/dist/domain/graphModels/schema.d.ts +16 -0
  25. package/dist/domain/graphModels/service.d.ts +14 -0
  26. package/dist/domain/graphViews/index.d.ts +15 -0
  27. package/dist/domain/graphViews/schema.d.ts +4 -0
  28. package/dist/domain/graphViews/service.d.ts +14 -0
  29. package/dist/domain/labels/index.d.ts +15 -0
  30. package/dist/domain/labels/index.js +33 -0
  31. package/dist/domain/labels/index.js.map +1 -0
  32. package/dist/domain/labels/service.d.ts +19 -0
  33. package/dist/domain/labels/service.js +21 -0
  34. package/dist/domain/labels/service.js.map +1 -0
  35. package/dist/domain/linkTypes/index.d.ts +15 -0
  36. package/dist/domain/linkTypes/schema.d.ts +16 -0
  37. package/dist/domain/linkTypes/service.d.ts +15 -0
  38. package/dist/domain/logicPrograms/index.d.ts +15 -0
  39. package/dist/domain/logicPrograms/service.d.ts +15 -0
  40. package/dist/domain/project/index.d.ts +15 -0
  41. package/dist/domain/project/index.js +42 -0
  42. package/dist/domain/project/index.js.map +1 -0
  43. package/dist/domain/project/schema.d.ts +20 -0
  44. package/dist/domain/project/schema.js +21 -0
  45. package/dist/domain/project/schema.js.map +1 -0
  46. package/dist/domain/project/service.d.ts +30 -0
  47. package/dist/domain/project/service.js +54 -0
  48. package/dist/domain/project/service.js.map +1 -0
  49. package/dist/domain/reports/index.d.ts +15 -0
  50. package/dist/domain/reports/schema.d.ts +16 -0
  51. package/dist/domain/reports/service.d.ts +14 -0
  52. package/dist/domain/resources/index.d.ts +15 -0
  53. package/dist/domain/resources/schema.d.ts +60 -0
  54. package/dist/domain/resources/service.d.ts +36 -0
  55. package/dist/domain/resources/service.js +60 -31
  56. package/dist/domain/resources/service.js.map +1 -1
  57. package/dist/domain/templates/index.d.ts +15 -0
  58. package/dist/domain/templates/index.js +1 -1
  59. package/dist/domain/templates/index.js.map +1 -1
  60. package/dist/domain/templates/schema.d.ts +22 -0
  61. package/dist/domain/templates/service.d.ts +16 -0
  62. package/dist/domain/tree/index.d.ts +15 -0
  63. package/dist/domain/tree/index.js +3 -2
  64. package/dist/domain/tree/index.js.map +1 -1
  65. package/dist/domain/tree/service.d.ts +22 -0
  66. package/dist/domain/tree/service.js +10 -2
  67. package/dist/domain/tree/service.js.map +1 -1
  68. package/dist/domain/workflows/index.d.ts +15 -0
  69. package/dist/domain/workflows/schema.d.ts +16 -0
  70. package/dist/domain/workflows/service.d.ts +14 -0
  71. package/dist/export.d.ts +42 -0
  72. package/dist/export.js +48 -152
  73. package/dist/export.js.map +1 -1
  74. package/dist/index.d.ts +13 -0
  75. package/dist/index.js.map +1 -1
  76. package/dist/main.d.ts +1 -0
  77. package/dist/middleware/commandManager.d.ts +20 -0
  78. package/dist/middleware/tree.d.ts +9 -0
  79. package/dist/middleware/tree.js +13 -0
  80. package/dist/middleware/tree.js.map +1 -0
  81. package/dist/middleware/zvalidator.d.ts +9 -0
  82. package/dist/public/THIRD-PARTY.txt +77 -83
  83. package/dist/public/assets/index-BrNy_4vV.js +163198 -0
  84. package/dist/public/index.html +1 -1
  85. package/dist/types.d.ts +29 -0
  86. package/dist/utils.d.ts +11 -0
  87. package/dist/utils.js +0 -32
  88. package/dist/utils.js.map +1 -1
  89. package/package.json +14 -8
  90. package/src/app.ts +13 -4
  91. package/src/domain/cards/index.ts +31 -6
  92. package/src/domain/cards/lib.ts +93 -3
  93. package/src/domain/cards/service.ts +36 -24
  94. package/src/domain/labels/index.ts +36 -0
  95. package/src/domain/labels/service.ts +23 -0
  96. package/src/domain/project/index.ts +58 -0
  97. package/src/domain/project/schema.ts +23 -0
  98. package/src/domain/project/service.ts +88 -0
  99. package/src/domain/resources/service.ts +87 -41
  100. package/src/domain/templates/index.ts +1 -1
  101. package/src/domain/tree/index.ts +10 -3
  102. package/src/domain/tree/service.ts +15 -1
  103. package/src/export.ts +59 -192
  104. package/src/index.ts +5 -1
  105. package/src/middleware/tree.ts +17 -0
  106. package/src/types.ts +13 -0
  107. package/src/utils.ts +0 -39
  108. package/dist/public/assets/index-Dtn1rQ-9.js +0 -163921
@@ -11,7 +11,7 @@
11
11
  name="msapplication-TileImage"
12
12
  content="/cropped-favicon-270x270.png"
13
13
  />
14
- <script type="module" crossorigin src="/assets/index-Dtn1rQ-9.js"></script>
14
+ <script type="module" crossorigin src="/assets/index-BrNy_4vV.js"></script>
15
15
  <link rel="stylesheet" crossorigin href="/assets/index-DnK7MBer.css">
16
16
  </head>
17
17
  <body>
@@ -0,0 +1,29 @@
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
+ import type { Context } from 'hono';
14
+ export interface ResourceFileContentResponse {
15
+ content: string;
16
+ }
17
+ export interface ResourceValidationResponse {
18
+ errors: string[];
19
+ }
20
+ export interface TreeOptions {
21
+ recursive?: boolean;
22
+ cardKey?: string;
23
+ }
24
+ export interface AppVars {
25
+ tree?: TreeOptions;
26
+ }
27
+ export type AppContext = Context<{
28
+ Variables: AppVars;
29
+ }>;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The relative path to the static frontend directory.
3
+ */
4
+ export declare const staticFrontendDirRelative: string;
5
+ /**
6
+ * Finds a free port.
7
+ * @param port - The port to start looking for.
8
+ * @param maxAttempts - The maximum number of attempts to find a free port.
9
+ * @returns The free port.
10
+ */
11
+ export declare function findFreePort(minPort: number, maxPort: number): Promise<number>;
package/dist/utils.js CHANGED
@@ -12,38 +12,6 @@
12
12
  */
13
13
  import path from 'node:path';
14
14
  import { createServer } from 'node:net';
15
- /**
16
- * Runs promises in parallel, but only maxConcurrent at a time.
17
- * @param promises - Array of promises to run in parallel
18
- * @param maxConcurrent - Maximum number of promises to run at a time
19
- * @returns - Promise that resolves when all promises have resolved
20
- */
21
- export async function runInParallel(promises, maxConcurrent = 2) {
22
- const waitingPromises = [];
23
- const wrappedPromises = promises.map((fn) => async () => {
24
- await fn();
25
- const next = waitingPromises.shift();
26
- if (next) {
27
- await next();
28
- }
29
- });
30
- const runningPromises = wrappedPromises.slice(0, maxConcurrent);
31
- waitingPromises.push(...wrappedPromises.slice(maxConcurrent));
32
- return Promise.all(runningPromises.map((p) => p()));
33
- }
34
- /**
35
- * Runs a callback and returns the result or undefined if it throws.
36
- * @param cb - The callback to run.
37
- * @returns The result of the callback or undefined if it throws.
38
- */
39
- export async function runCbSafely(cb) {
40
- try {
41
- return await cb();
42
- }
43
- catch {
44
- // All exceptions are ignored.
45
- }
46
- }
47
15
  /**
48
16
  * The relative path to the static frontend directory.
49
17
  */
package/dist/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;EAWE;AACF,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAExC;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,QAAoC,EACpC,gBAAwB,CAAC;IAEzB,MAAM,eAAe,GAA+B,EAAE,CAAC;IACvD,MAAM,eAAe,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,IAAI,EAAE;QACtD,MAAM,EAAE,EAAE,CAAC;QACX,MAAM,IAAI,GAAG,eAAe,CAAC,KAAK,EAAE,CAAC;QACrC,IAAI,IAAI,EAAE,CAAC;YACT,MAAM,IAAI,EAAE,CAAC;QACf,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,eAAe,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC;IAChE,eAAe,CAAC,IAAI,CAAC,GAAG,eAAe,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC;IAE9D,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACtD,CAAC;AACD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,EAAwB;IAExB,IAAI,CAAC;QACH,OAAO,MAAM,EAAE,EAAE,CAAC;IACpB,CAAC;IAAC,MAAM,CAAC;QACP,8BAA8B;IAChC,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,IAAI,CAAC,QAAQ,CACpD,OAAO,CAAC,GAAG,EAAE,EACb,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAC5C,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,OAAe,EACf,OAAe;IAEf,KAAK,IAAI,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,IAAI,CAAC;YACH,MAAM,QAAQ,CAAC,CAAC,CAAC,CAAC;YAClB,OAAO,CAAC,CAAC;QACX,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;gBAC/D,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,yCAAyC,CAAC,CAAC;YAClE,CAAC;iBAAM,CAAC;gBACN,MAAM,GAAG,CAAC;YACZ,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;AAC9C,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY;IAC5B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;QAC9B,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;YACvB,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CAAC,CAAC;QAChB,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACzB,MAAM,CAAC,GAAG,CAAC,CAAC;QACd,CAAC,CAAC,CAAC;QACH,UAAU,CAAC,GAAG,EAAE;YACd,MAAM,CAAC,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC,CAAC;QAC7D,CAAC,EAAE,IAAI,CAAC,CAAC;IACX,CAAC,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;EAWE;AACF,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAExC;;GAEG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,IAAI,CAAC,QAAQ,CACpD,OAAO,CAAC,GAAG,EAAE,EACb,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAC5C,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,OAAe,EACf,OAAe;IAEf,KAAK,IAAI,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,IAAI,CAAC;YACH,MAAM,QAAQ,CAAC,CAAC,CAAC,CAAC;YAClB,OAAO,CAAC,CAAC;QACX,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;gBAC/D,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,yCAAyC,CAAC,CAAC;YAClE,CAAC;iBAAM,CAAC;gBACN,MAAM,GAAG,CAAC;YACZ,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;AAC9C,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY;IAC5B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;QAC9B,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;YACvB,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CAAC,CAAC;QAChB,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACzB,MAAM,CAAC,GAAG,CAAC,CAAC;QACd,CAAC,CAAC,CAAC;QACH,UAAU,CAAC,GAAG,EAAE;YACd,MAAM,CAAC,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC,CAAC;QAC7D,CAAC,EAAE,IAAI,CAAC,CAAC;IACX,CAAC,CAAC,CAAC;AACL,CAAC"}
package/package.json CHANGED
@@ -1,21 +1,27 @@
1
1
  {
2
2
  "name": "@cyberismo/backend",
3
- "version": "0.0.15",
3
+ "version": "0.0.17",
4
4
  "description": "Express backend for Cyberismo",
5
5
  "main": "dist/index.js",
6
6
  "keywords": [],
7
- "author": "",
7
+ "author": "sami.merila@cyberismo.com",
8
8
  "license": "AGPL-3.0",
9
+ "homepage": "https://github.com/CyberismoCom/cyberismo",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/CyberismoCom/cyberismo.git"
13
+ },
14
+ "bugs": "https://github.com/CyberismoCom/cyberismo/issues",
9
15
  "dependencies": {
10
16
  "@asciidoctor/core": "^3.0.4",
11
17
  "@hono/node-server": "^1.19.2",
12
- "@hono/zod-validator": "^0.7.4",
18
+ "@hono/zod-validator": "^0.7.5",
13
19
  "@types/mime-types": "^3.0.1",
14
20
  "dotenv": "^17.2.3",
15
- "hono": "^4.10.3",
16
- "mime-types": "^3.0.1",
17
- "zod": "^4.1.12",
18
- "@cyberismo/data-handler": "0.0.15"
21
+ "hono": "^4.10.7",
22
+ "mime-types": "^3.0.2",
23
+ "zod": "^4.1.13",
24
+ "@cyberismo/data-handler": "0.0.17"
19
25
  },
20
26
  "devDependencies": {
21
27
  "@cyberismo/app": "0.0.2"
@@ -36,7 +42,7 @@
36
42
  "debug": "tsx --inspect-brk src/main.ts",
37
43
  "export": "pnpm build && node dist/main.js --export",
38
44
  "build": "tsc -p tsconfig.build.json && shx rm -rf ./dist/public && shx cp -r ../app/dist ./dist/public",
39
- "test": "vitest run",
45
+ "test": "vitest run --silent",
40
46
  "lint": "eslint ."
41
47
  }
42
48
  }
package/src/app.ts CHANGED
@@ -26,18 +26,22 @@ import reportsRouter from './domain/reports/index.js';
26
26
  import templatesRouter from './domain/templates/index.js';
27
27
  import treeRouter from './domain/tree/index.js';
28
28
  import workflowsRouter from './domain/workflows/index.js';
29
+ import labelsRouter from './domain/labels/index.js';
29
30
  import { readFile } from 'node:fs/promises';
30
31
  import path from 'node:path';
31
- import { isSSGContext } from './export.js';
32
32
  import resourcesRouter from './domain/resources/index.js';
33
33
  import logicProgramsRouter from './domain/logicPrograms/index.js';
34
+ import { isSSGContext } from 'hono/ssg';
35
+ import type { AppVars, TreeOptions } from './types.js';
36
+ import treeMiddleware from './middleware/tree.js';
37
+ import projectRouter from './domain/project/index.js';
34
38
 
35
39
  /**
36
40
  * Create the Hono app for the backend
37
41
  * @param projectPath - Path to the project
38
42
  */
39
- export function createApp(projectPath?: string) {
40
- const app = new Hono();
43
+ export function createApp(projectPath?: string, opts?: TreeOptions) {
44
+ const app = new Hono<{ Variables: AppVars }>();
41
45
 
42
46
  app.use('/api', cors());
43
47
 
@@ -48,6 +52,7 @@ export function createApp(projectPath?: string) {
48
52
  }),
49
53
  );
50
54
 
55
+ app.use(treeMiddleware(opts));
51
56
  // Attach CommandManager to all requests
52
57
  app.use(attachCommandManager(projectPath));
53
58
 
@@ -65,6 +70,8 @@ export function createApp(projectPath?: string) {
65
70
  app.route('/api/workflows', workflowsRouter);
66
71
  app.route('/api/resources', resourcesRouter);
67
72
  app.route('/api/logicPrograms', logicProgramsRouter);
73
+ app.route('/api/labels', labelsRouter);
74
+ app.route('/api/project', projectRouter);
68
75
 
69
76
  // serve index.html for all other routes
70
77
  app.notFound(async (c) => {
@@ -79,7 +86,9 @@ export function createApp(projectPath?: string) {
79
86
  // Error handling
80
87
  app.onError((err, c) => {
81
88
  if (!isSSGContext(c)) {
82
- console.error(err.stack);
89
+ if (process.env.NODE_ENV !== 'test') {
90
+ console.error(err.stack);
91
+ }
83
92
  }
84
93
  return c.json(
85
94
  {
@@ -13,9 +13,10 @@
13
13
 
14
14
  import { type Context, Hono } from 'hono';
15
15
  import type { ContentfulStatusCode } from 'hono/utils/http-status';
16
- import { ssgParams, isSSGContext } from '../../export.js';
17
16
  import { getCardDetails } from './lib.js';
18
17
  import * as cardService from './service.js';
18
+ import { isSSGContext, ssgParams } from 'hono/ssg';
19
+ import type { AppContext } from '../../types.js';
19
20
 
20
21
  const router = new Hono();
21
22
 
@@ -58,6 +59,12 @@ router.get('/', async (c) => {
58
59
  * in: path
59
60
  * required: true
60
61
  * description: Card key (string)
62
+ * - name: raw
63
+ * in: query
64
+ * required: false
65
+ * schema:
66
+ * type: boolean
67
+ * description: When true, returns the raw card data without triggering calculations
61
68
  * responses:
62
69
  * 200:
63
70
  * description: Object containing card details. See lib/api/types.ts/CardResponse for the structure.
@@ -68,10 +75,11 @@ router.get('/', async (c) => {
68
75
  */
69
76
  router.get(
70
77
  '/:key',
71
- ssgParams(async (c: Context) => {
78
+ ssgParams(async (c: AppContext) => {
72
79
  const commands = c.get('commands');
73
- const cards = await cardService.getAllCards(commands);
74
- return cards.map((key) => ({ key }));
80
+ const opts = c.get('tree');
81
+ const cards = await cardService.findAllCards(commands, opts);
82
+ return cards.map((card) => ({ key: card.key }));
75
83
  }),
76
84
  async (c) => {
77
85
  const key = c.req.param('key');
@@ -79,10 +87,13 @@ router.get(
79
87
  return c.text('No search key', 400);
80
88
  }
81
89
 
90
+ const raw = c.req.query('raw');
91
+
82
92
  const result = await getCardDetails(
83
93
  c.get('commands'),
84
94
  key,
85
95
  isSSGContext(c),
96
+ raw?.toLowerCase() === 'true' ? true : false,
86
97
  );
87
98
  if (result.status === 200) {
88
99
  return c.json(result.data);
@@ -117,6 +128,12 @@ router.get(
117
128
  * type: object
118
129
  * required: false
119
130
  * description: New metadata for the card. Must be an object with key-value pairs.
131
+ * - name: raw
132
+ * in: query
133
+ * required: false
134
+ * schema:
135
+ * type: boolean
136
+ * description: When true, returns the raw card data without triggering calculations
120
137
  * responses:
121
138
  * 200:
122
139
  * description: Object containing card details, same as GET. See definitions.ts/CardDetails for the structure.
@@ -134,10 +151,18 @@ router.patch('/:key', async (c) => {
134
151
  return c.text('No search key', 400);
135
152
  }
136
153
 
154
+ const raw = c.req.query('raw');
155
+
137
156
  const body = await c.req.json();
138
157
 
139
158
  try {
140
- const result = await cardService.updateCard(commands, key, body);
159
+ await cardService.updateCard(commands, key, body);
160
+ const result = await getCardDetails(
161
+ c.get('commands'),
162
+ key,
163
+ isSSGContext(c),
164
+ raw?.toLowerCase() === 'true' ? true : false,
165
+ );
141
166
  if (result.status === 200) {
142
167
  return c.json(result.data);
143
168
  } else {
@@ -587,7 +612,7 @@ router.get(
587
612
  '/:key/a/:attachment',
588
613
  ssgParams(async (c: Context) => {
589
614
  const commands = c.get('commands');
590
- return await cardService.getAllAttachments(commands);
615
+ return await cardService.findRelevantAttachments(commands, c.get('tree'));
591
616
  }),
592
617
  (c) => {
593
618
  const commands = c.get('commands');
@@ -15,6 +15,8 @@ import Processor from '@asciidoctor/core';
15
15
  import type { Card } from '@cyberismo/data-handler/interfaces/project-interfaces';
16
16
  import { type CommandManager, evaluateMacros } from '@cyberismo/data-handler';
17
17
  import { getCardQueryResult } from '../../export.js';
18
+ import type { TreeOptions } from '../../types.js';
19
+ import type { QueryResult } from '@cyberismo/data-handler/types/queries';
18
20
 
19
21
  interface result {
20
22
  status: number;
@@ -25,9 +27,10 @@ interface result {
25
27
  export async function getCardDetails(
26
28
  commands: CommandManager,
27
29
  key: string,
28
- staticMode?: boolean,
30
+ staticMode: boolean,
31
+ raw: boolean,
29
32
  ): Promise<result> {
30
- let cardDetailsResponse: Card | undefined;
33
+ let cardDetailsResponse: Card;
31
34
  try {
32
35
  cardDetailsResponse = commands.showCmd.showCardDetails(key);
33
36
  } catch {
@@ -39,7 +42,7 @@ export async function getCardDetails(
39
42
  }
40
43
 
41
44
  // always parse for now if not in export mode
42
- if (!staticMode) {
45
+ if (!staticMode && !raw) {
43
46
  await commands.calculateCmd.generate();
44
47
  }
45
48
 
@@ -65,6 +68,66 @@ export async function getCardDetails(
65
68
  })
66
69
  .toString();
67
70
 
71
+ if (raw) {
72
+ if (!cardDetailsResponse.metadata) {
73
+ throw new Error('Card has no metadata');
74
+ }
75
+ const cardType = await commands.showCmd.showResource(
76
+ cardDetailsResponse.metadata.cardType,
77
+ 'cardTypes',
78
+ );
79
+
80
+ const fields = [];
81
+ let i = 0;
82
+ for (const customField of cardType.customFields) {
83
+ const fieldType = await commands.showCmd.showResource(
84
+ customField.name,
85
+ 'fieldTypes',
86
+ );
87
+ fields.push({
88
+ key: customField.name,
89
+ visibility: 'always',
90
+ index: i++,
91
+ fieldDisplayName: fieldType.displayName,
92
+ fieldDescription: fieldType.description,
93
+ dataType: fieldType.dataType,
94
+ isCalculated: customField.isCalculated,
95
+ value: cardDetailsResponse.metadata[customField.name],
96
+ enumValues: fieldType.enumValues ?? [],
97
+ });
98
+ }
99
+ return {
100
+ status: 200,
101
+ data: {
102
+ key: cardDetailsResponse.key,
103
+ rank: cardDetailsResponse.metadata?.rank,
104
+ title: cardDetailsResponse.metadata?.title || '',
105
+ cardType: cardDetailsResponse.metadata?.cardType || '',
106
+ cardTypeDisplayName: cardDetailsResponse.metadata.cardType,
107
+ workflowState: '',
108
+ lastUpdated: cardDetailsResponse.metadata.lastUpdated,
109
+ fields,
110
+ labels: cardDetailsResponse.metadata?.labels || [],
111
+ links: [],
112
+ notifications: [],
113
+ policyChecks: {
114
+ successes: [],
115
+ failures: [],
116
+ },
117
+ deniedOperations: {
118
+ transition: [],
119
+ move: [],
120
+ delete: [],
121
+ editField: [],
122
+ editContent: [],
123
+ },
124
+ rawContent: cardDetailsResponse.content || '',
125
+ parsedContent: htmlContent,
126
+ attachments: cardDetailsResponse.attachments,
127
+ },
128
+ };
129
+ }
130
+
68
131
  const card = staticMode
69
132
  ? await getCardQueryResult(commands.project.basePath, key)
70
133
  : await commands.calculateCmd.runQuery('card', 'localApp', {
@@ -84,3 +147,30 @@ export async function getCardDetails(
84
147
  },
85
148
  };
86
149
  }
150
+ /**
151
+ * Returns all cards from a tree query, flattened.
152
+ * @param commands the command manager used for the query
153
+ * @param options optional tree query options
154
+ * @returns a promise that resolves to an array of all cards
155
+ */
156
+ export async function allCards(
157
+ commands: CommandManager,
158
+ options?: TreeOptions,
159
+ ): Promise<QueryResult<'tree'>[]> {
160
+ const fetchedCards = await commands.calculateCmd.runQuery(
161
+ 'tree',
162
+ 'exportedSite',
163
+ options || {},
164
+ );
165
+
166
+ function flattenCards(cards: QueryResult<'tree'>[]): QueryResult<'tree'>[] {
167
+ return cards.reduce<QueryResult<'tree'>[]>((acc, curr) => {
168
+ acc.push(curr);
169
+ if (curr.children && curr.children.length > 0) {
170
+ acc.push(...flattenCards(curr.children));
171
+ }
172
+ return acc;
173
+ }, []);
174
+ }
175
+ return flattenCards(fetchedCards);
176
+ }
@@ -12,12 +12,10 @@
12
12
  */
13
13
 
14
14
  import Processor from '@asciidoctor/core';
15
- import {
16
- CardLocation,
17
- type MetadataContent,
18
- } from '@cyberismo/data-handler/interfaces/project-interfaces';
15
+ import { type MetadataContent } from '@cyberismo/data-handler/interfaces/project-interfaces';
19
16
  import { type CommandManager, evaluateMacros } from '@cyberismo/data-handler';
20
- import { getCardDetails } from './lib.js';
17
+ import { allCards } from './lib.js';
18
+ import type { TreeOptions } from '../../types.js';
21
19
 
22
20
  export async function getProjectInfo(commands: CommandManager) {
23
21
  const projectResponse = await commands.showCmd.showProject();
@@ -94,8 +92,6 @@ export async function updateCard(
94
92
  if (errors.length > 0) {
95
93
  throw new Error(errors.join('\n'));
96
94
  }
97
-
98
- return await getCardDetails(commands, key);
99
95
  }
100
96
 
101
97
  export async function deleteCard(commands: CommandManager, key: string) {
@@ -221,23 +217,39 @@ export function getAttachment(
221
217
  return commands.showCmd.showAttachment(key, filename);
222
218
  }
223
219
 
224
- export async function getAllCards(commands: CommandManager) {
225
- const fetchedCards = await commands.showCmd.showCards(
226
- CardLocation.projectOnly,
227
- );
228
- const projectCards = fetchedCards.find(
229
- (cardContainer) => cardContainer.type === 'project',
230
- );
231
- if (!projectCards) {
232
- throw new Error('Data handler did not return project cards');
233
- }
234
- return projectCards.cards;
220
+ /**
221
+ * Used for exporting cards, thus static mode is assumed
222
+ * @param commandsthe command manager used for the query
223
+ * @param options optional tree query options
224
+ * @returns all cards in a flattened array
225
+ */
226
+ export async function findAllCards(
227
+ commands: CommandManager,
228
+ options?: TreeOptions,
229
+ ): ReturnType<typeof allCards> {
230
+ return allCards(commands, options);
235
231
  }
236
-
237
- export async function getAllAttachments(commands: CommandManager) {
232
+ /**
233
+ * Gets all attachments that are required for rendering the wanted cards
234
+ * @param commands the command manager used for the query
235
+ * @param options optional tree query options
236
+ * @returns all attachments for cards returned by the tree query
237
+ */
238
+ export async function findRelevantAttachments(
239
+ commands: CommandManager,
240
+ options?: TreeOptions,
241
+ ) {
242
+ const cards = new Set<string>(
243
+ (await allCards(commands, options)).map((c) => c.key),
244
+ );
238
245
  const attachments = await commands.showCmd.showAttachments();
239
- return attachments.map((attachment) => ({
240
- key: attachment.card,
241
- attachment: attachment.fileName,
242
- }));
246
+ return attachments
247
+ .filter(
248
+ (attachment) =>
249
+ cards.has(attachment.card) && attachment.mimeType?.startsWith('image/'),
250
+ )
251
+ .map((attachment) => ({
252
+ key: attachment.card,
253
+ attachment: attachment.fileName,
254
+ }));
243
255
  }
@@ -0,0 +1,36 @@
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 { Hono } from 'hono';
15
+ import * as labelsService from './service.js';
16
+
17
+ const router = new Hono();
18
+
19
+ /**
20
+ * @swagger
21
+ * /api/labels:
22
+ * get:
23
+ * summary: Returns all unique labels defined in the project.
24
+ * responses:
25
+ * 200:
26
+ * description: List of label strings.
27
+ * 500:
28
+ * description: Internal server error
29
+ */
30
+ router.get('/', (c) => {
31
+ const commands = c.get('commands');
32
+ const labels = labelsService.getLabels(commands);
33
+ return c.json(labels);
34
+ });
35
+
36
+ export default router;
@@ -0,0 +1,23 @@
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 type { CommandManager } from '@cyberismo/data-handler';
15
+
16
+ /**
17
+ * Returns all unique labels available in the project.
18
+ * @param commands command manager used for the query
19
+ * @returns a list of labels
20
+ */
21
+ export function getLabels(commands: CommandManager): string[] {
22
+ return commands.showCmd.showLabels().sort();
23
+ }
@@ -0,0 +1,58 @@
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 { Hono } from 'hono';
15
+ import { zValidator } from '../../middleware/zvalidator.js';
16
+ import { moduleParamSchema, updateProjectSchema } from './schema.js';
17
+ import * as projectService from './service.js';
18
+
19
+ const router = new Hono();
20
+
21
+ router.get('/', async (c) => {
22
+ const commands = c.get('commands');
23
+
24
+ const project = await projectService.getProject(commands);
25
+ return c.json(project);
26
+ });
27
+
28
+ router.patch('/', zValidator('json', updateProjectSchema), async (c) => {
29
+ const commands = c.get('commands');
30
+ const updates = c.req.valid('json');
31
+
32
+ const project = await projectService.updateProject(commands, updates);
33
+ return c.json(project);
34
+ });
35
+
36
+ router.post(
37
+ '/modules/:module/update',
38
+ zValidator('param', moduleParamSchema),
39
+ async (c) => {
40
+ const commands = c.get('commands');
41
+ const { module } = c.req.valid('param');
42
+ await projectService.updateModule(commands, module);
43
+ return c.json({ message: 'Module updated' });
44
+ },
45
+ );
46
+
47
+ router.delete(
48
+ '/modules/:module',
49
+ zValidator('param', moduleParamSchema),
50
+ async (c) => {
51
+ const commands = c.get('commands');
52
+ const { module } = c.req.valid('param');
53
+ await projectService.deleteModule(commands, module);
54
+ return c.json({ message: 'Module removed' });
55
+ },
56
+ );
57
+
58
+ export default router;
@@ -0,0 +1,23 @@
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 { z } from 'zod';
15
+
16
+ export const moduleParamSchema = z.object({
17
+ module: z.string().min(1),
18
+ });
19
+
20
+ export const updateProjectSchema = z.object({
21
+ name: z.string().optional(),
22
+ cardKeyPrefix: z.string().optional(),
23
+ });