@gadmin2n/schematics 0.0.72 → 0.0.75

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 (173) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.js +2 -0
  3. package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/job.prisma +62 -0
  4. package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/system.prisma +0 -21
  5. package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/workflow.prisma +171 -0
  6. package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/AgendaJob.ts +60 -0
  7. package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Event.ts +1 -1
  8. package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/WorkflowEventOutbox.ts +62 -0
  9. package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/WorkflowNodeInstance.ts +62 -0
  10. package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/WorkflowNodeType.ts +62 -0
  11. package/dist/lib/application/files/gadmin2-game-angle-demo/server/.env +5 -0
  12. package/dist/lib/application/files/gadmin2-game-angle-demo/server/package.json +5 -4
  13. package/dist/lib/application/files/gadmin2-game-angle-demo/server/prisma.config.ts +14 -7
  14. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/index.ts +4 -0
  15. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/permissions.ts +49 -3
  16. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow-node-types.ts +746 -0
  17. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflows.ts +786 -0
  18. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agenda/agenda.controller.ts +6 -0
  19. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agenda/agenda.service.ts +79 -0
  20. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agendaJob/agendaJob.controller.spec.ts +20 -0
  21. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agendaJob/agendaJob.controller.ts +145 -0
  22. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agendaJob/agendaJob.module.ts +10 -0
  23. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/{canvas/canvas.service.spec.ts → agendaJob/agendaJob.service.spec.ts} +71 -65
  24. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agendaJob/agendaJob.service.ts +83 -0
  25. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/index.ts +2 -1
  26. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/temporal.module.ts +9 -0
  27. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/temporal.service.ts +100 -0
  28. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-execution.dto.ts +19 -0
  29. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-export.dto.ts +43 -0
  30. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-export.service.ts +317 -0
  31. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-node-type.controller.ts +16 -0
  32. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-node-type.service.ts +13 -0
  33. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.controller.ts +220 -0
  34. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.dto.ts +82 -0
  35. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.module.ts +16 -0
  36. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.ts +505 -0
  37. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowEventOutbox/workflowEventOutbox.controller.spec.ts +22 -0
  38. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowEventOutbox/workflowEventOutbox.controller.ts +147 -0
  39. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowEventOutbox/workflowEventOutbox.module.ts +10 -0
  40. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowEventOutbox/workflowEventOutbox.service.spec.ts +356 -0
  41. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowEventOutbox/workflowEventOutbox.service.ts +110 -0
  42. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeInstance/workflowNodeInstance.controller.spec.ts +22 -0
  43. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeInstance/workflowNodeInstance.controller.ts +216 -0
  44. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeInstance/workflowNodeInstance.module.ts +10 -0
  45. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeInstance/workflowNodeInstance.service.spec.ts +356 -0
  46. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeInstance/workflowNodeInstance.service.ts +168 -0
  47. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeType/workflowNodeType.controller.spec.ts +22 -0
  48. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeType/workflowNodeType.controller.ts +199 -0
  49. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeType/workflowNodeType.module.ts +10 -0
  50. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeType/workflowNodeType.service.spec.ts +348 -0
  51. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeType/workflowNodeType.service.ts +106 -0
  52. package/dist/lib/application/files/gadmin2-game-angle-demo/server/yarn.lock +579 -1082
  53. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/README.md +278 -0
  54. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/config/development-sql.yaml +5 -0
  55. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/docker-compose.yml +25 -0
  56. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/package.json +13 -0
  57. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/sql/create-event-trigger.sql +87 -0
  58. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/.env +7 -0
  59. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/SANDBOX.md +122 -0
  60. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/package-lock.json +4285 -0
  61. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/package.json +28 -0
  62. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/__tests__/activities/code-execute.test.ts +44 -0
  63. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/__tests__/activities/http-request.test.ts +87 -0
  64. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/__tests__/helpers.test.ts +225 -0
  65. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/__tests__/node-type-consistency.test.ts +101 -0
  66. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/code-execute.ts +51 -0
  67. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/db-execute.ts +85 -0
  68. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/db-query.ts +35 -0
  69. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/http-request.ts +54 -0
  70. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/index.ts +6 -0
  71. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/reporting.ts +62 -0
  72. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/send-notification.ts +47 -0
  73. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/config.ts +13 -0
  74. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/dsl/condition.ts +101 -0
  75. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/dsl/context.ts +58 -0
  76. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/dsl/graph.ts +184 -0
  77. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/dsl/helpers.ts +133 -0
  78. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/dsl/node-types.ts +57 -0
  79. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/dsl/types.ts +77 -0
  80. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/index.ts +36 -0
  81. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/outbox-poller.ts +226 -0
  82. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/workflows/dsl-workflow.ts +411 -0
  83. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/tsconfig.json +19 -0
  84. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/vitest.config.ts +8 -0
  85. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/yarn.lock +1905 -0
  86. package/dist/lib/application/files/gadmin2-game-angle-demo/web/package-lock.json +17555 -0
  87. package/dist/lib/application/files/gadmin2-game-angle-demo/web/package.json +5 -2
  88. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/App.tsx +1 -0
  89. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/sider.tsx +5 -1
  90. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/title.tsx +1 -1
  91. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/config/routeRegistry.tsx +63 -0
  92. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/dev-shell/DevShell.tsx +91 -2
  93. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/helpers/list.tsx +48 -2
  94. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/helpers/show.tsx +43 -2
  95. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/en/common.json +14 -9
  96. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/zh_CN/common.json +14 -9
  97. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agenda/index.tsx +309 -56
  98. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agenda/show.tsx +1 -3
  99. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agendaJob/create.tsx +108 -0
  100. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agendaJob/edit.tsx +124 -0
  101. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agendaJob/index.tsx +4 -0
  102. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agendaJob/list.tsx +245 -0
  103. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agendaJob/show.tsx +70 -0
  104. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasListPage.tsx +0 -1
  105. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasPage.tsx +160 -2
  106. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasToolbar.tsx +120 -148
  107. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CodeFloatWindow.tsx +74 -181
  108. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/LivePreview.tsx +15 -13
  109. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/canvasConfigRegistry.tsx +2 -2
  110. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/canvasContextMenuRegistry.tsx +338 -3
  111. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/canvasDefaults.ts +18 -17
  112. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/BarChartDataSourceModal.tsx +10 -4
  113. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/LineChartDataSourceModal.tsx +10 -4
  114. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/{ChartViewerConfigModal.tsx → MultiChartConfigModal.tsx} +30 -18
  115. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/MultiChartDataSourceModal.tsx +427 -0
  116. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/NumCardDataSourceModal.tsx +10 -4
  117. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/PromptModal.tsx +6 -14
  118. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/RadarChartDataSourceModal.tsx +10 -4
  119. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/TableDataSourceModal.tsx +10 -4
  120. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/canvasModalProps.ts +24 -0
  121. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/demos.ts +45 -63
  122. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/CustomNode.tsx +99 -0
  123. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/ExportModal.tsx +87 -0
  124. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/FlowRenderer.tsx +322 -0
  125. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/ImportModal.tsx +175 -0
  126. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NodeEditModal.tsx +60 -0
  127. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NodePropertyPanel.tsx +1150 -0
  128. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/RunWorkflowModal.tsx +101 -0
  129. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/StatusCards.tsx +198 -0
  130. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/VersionPanel.tsx +81 -0
  131. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/editor.tsx +566 -0
  132. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/hooks/useWorkflowAgent.ts +224 -0
  133. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/index.tsx +524 -0
  134. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/instance-detail.tsx +343 -0
  135. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/instances.tsx +243 -0
  136. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/components/CreateNodeInstanceModal.tsx +363 -0
  137. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/components/DynamicConfigForm.tsx +154 -0
  138. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/components/NodeInstanceForm.tsx +176 -0
  139. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/create.tsx +77 -0
  140. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/edit.tsx +112 -0
  141. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/index.tsx +305 -0
  142. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/show.tsx +282 -0
  143. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/show.tsx +469 -0
  144. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/types.ts +92 -0
  145. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflowEventOutbox/create.tsx +111 -0
  146. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflowEventOutbox/edit.tsx +127 -0
  147. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflowEventOutbox/index.tsx +4 -0
  148. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflowEventOutbox/list.tsx +254 -0
  149. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflowEventOutbox/show.tsx +74 -0
  150. package/dist/lib/application/files/gadmin2-game-angle-demo/web/yarn.lock +1501 -1199
  151. package/package.json +1 -1
  152. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/app.controller.spec.ts +0 -22
  153. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/BarChart/index.tsx +0 -896
  154. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/ChartSwitcher/index.tsx +0 -219
  155. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/ChartViewer/index.tsx +0 -159
  156. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/Filter/index.tsx +0 -192
  157. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/LineChart/index.tsx +0 -1034
  158. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/NumCard/NumCard.module.css +0 -8
  159. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/NumCard/index.tsx +0 -509
  160. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/NumLineCard/index.tsx +0 -66
  161. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/PieChart/index.tsx +0 -552
  162. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/RadarChart/index.tsx +0 -263
  163. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/Section/index.tsx +0 -35
  164. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/Table/index.tsx +0 -207
  165. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/TreemapChart/index.tsx +0 -382
  166. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/WorldMap/index.tsx +0 -135
  167. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/chart-constants.ts +0 -53
  168. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/icon/InfoIcon.tsx +0 -8
  169. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/icon/index.ts +0 -1
  170. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/map/config.ts +0 -31
  171. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/map/nameMap.json +0 -9
  172. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/map/world.geo.json +0 -39349
  173. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/metric-info-tooltip/index.tsx +0 -19
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "gadmin-workflow-worker",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "build": "tsc",
7
+ "dev": "ts-node src/index.ts",
8
+ "start": "node dist/index.js",
9
+ "test": "vitest run",
10
+ "test:watch": "vitest"
11
+ },
12
+ "dependencies": {
13
+ "@temporalio/activity": "^1.11.6",
14
+ "@temporalio/client": "^1.11.6",
15
+ "@temporalio/worker": "^1.11.6",
16
+ "@temporalio/workflow": "^1.11.6",
17
+ "axios": "^1.7.9",
18
+ "dotenv": "^16.4.7",
19
+ "pg": "^8.16.3"
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^22.0.0",
23
+ "@types/pg": "^8.11.0",
24
+ "ts-node": "^10.9.2",
25
+ "typescript": "^5.7.0",
26
+ "vitest": "^3.1.0"
27
+ }
28
+ }
@@ -0,0 +1,44 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { codeExecute } from '../../activities/code-execute';
3
+
4
+ describe('codeExecute activity', () => {
5
+ it('executes simple script and returns result', async () => {
6
+ const output = await codeExecute({
7
+ script: 'return 1 + 2;',
8
+ });
9
+ expect(output.result).toBe(3);
10
+ });
11
+
12
+ it('has access to data context', async () => {
13
+ const output = await codeExecute({
14
+ script: 'return data.x * 2;',
15
+ data: { x: 5 },
16
+ });
17
+ expect(output.result).toBe(10);
18
+ });
19
+
20
+ it('runOnceForEach mode processes each item', async () => {
21
+ const output = await codeExecute({
22
+ script: 'return item * 10;',
23
+ mode: 'runOnceForEach',
24
+ items: [1, 2, 3],
25
+ });
26
+ expect(output.result).toEqual([10, 20, 30]);
27
+ });
28
+
29
+ it('respects timeout', async () => {
30
+ await expect(
31
+ codeExecute({
32
+ script: 'while(true) {}',
33
+ timeout: 100,
34
+ }),
35
+ ).rejects.toThrow();
36
+ });
37
+
38
+ it('sandbox cannot access Node.js globals', async () => {
39
+ const output = await codeExecute({
40
+ script: 'return typeof require;',
41
+ });
42
+ expect(output.result).toBe('undefined');
43
+ });
44
+ });
@@ -0,0 +1,87 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import type { HttpRequestInput } from '../../activities/http-request';
3
+
4
+ // Mock axios at module level
5
+ vi.mock('axios', () => ({
6
+ default: vi.fn(),
7
+ }));
8
+
9
+ import axios from 'axios';
10
+ import { httpRequest } from '../../activities/http-request';
11
+
12
+ const mockedAxios = vi.mocked(axios);
13
+
14
+ describe('httpRequest activity', () => {
15
+ beforeEach(() => {
16
+ vi.clearAllMocks();
17
+ });
18
+
19
+ it('makes GET request and returns response', async () => {
20
+ mockedAxios.mockResolvedValueOnce({
21
+ status: 200,
22
+ data: { message: 'ok' },
23
+ headers: { 'content-type': 'application/json' },
24
+ } as any);
25
+
26
+ const result = await httpRequest({
27
+ url: 'https://api.example.com/data',
28
+ method: 'GET',
29
+ });
30
+
31
+ expect(result.status).toBe(200);
32
+ expect(result.data).toEqual({ message: 'ok' });
33
+ expect(mockedAxios).toHaveBeenCalledOnce();
34
+ });
35
+
36
+ it('sends body for POST request', async () => {
37
+ mockedAxios.mockResolvedValueOnce({
38
+ status: 201,
39
+ data: { id: 1 },
40
+ headers: {},
41
+ } as any);
42
+
43
+ await httpRequest({
44
+ url: 'https://api.example.com/items',
45
+ method: 'POST',
46
+ body: { name: 'test' },
47
+ });
48
+
49
+ const callConfig = mockedAxios.mock.calls[0][0] as any;
50
+ expect(callConfig.data).toEqual({ name: 'test' });
51
+ expect(callConfig.method).toBe('post');
52
+ });
53
+
54
+ it('applies custom timeout', async () => {
55
+ mockedAxios.mockResolvedValueOnce({
56
+ status: 200,
57
+ data: null,
58
+ headers: {},
59
+ } as any);
60
+
61
+ await httpRequest({
62
+ url: 'https://api.example.com',
63
+ method: 'GET',
64
+ timeout: 5000,
65
+ });
66
+
67
+ const callConfig = mockedAxios.mock.calls[0][0] as any;
68
+ expect(callConfig.timeout).toBe(5000);
69
+ });
70
+
71
+ it('disables redirects when followRedirects is false', async () => {
72
+ mockedAxios.mockResolvedValueOnce({
73
+ status: 302,
74
+ data: null,
75
+ headers: { location: '/new' },
76
+ } as any);
77
+
78
+ await httpRequest({
79
+ url: 'https://api.example.com/old',
80
+ method: 'GET',
81
+ followRedirects: false,
82
+ });
83
+
84
+ const callConfig = mockedAxios.mock.calls[0][0] as any;
85
+ expect(callConfig.maxRedirects).toBe(0);
86
+ });
87
+ });
@@ -0,0 +1,225 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import {
3
+ resolveTemplates,
4
+ resolveExpression,
5
+ getNestedValue,
6
+ evaluateConditions,
7
+ evaluateRule,
8
+ getSuccessorsFromNodes,
9
+ getNextNodes,
10
+ } from '../dsl/helpers';
11
+ import type { WorkflowDSL, DslNode } from '../dsl/types';
12
+
13
+ // ─── getNestedValue ──────────────────────────────────────────────────────────
14
+
15
+ describe('getNestedValue', () => {
16
+ it('resolves simple path', () => {
17
+ expect(getNestedValue({ a: 1 }, 'a')).toBe(1);
18
+ });
19
+
20
+ it('resolves nested path', () => {
21
+ expect(getNestedValue({ a: { b: { c: 42 } } }, 'a.b.c')).toBe(42);
22
+ });
23
+
24
+ it('returns undefined for missing path', () => {
25
+ expect(getNestedValue({ a: 1 }, 'b')).toBeUndefined();
26
+ });
27
+
28
+ it('returns undefined for null intermediate', () => {
29
+ expect(getNestedValue({ a: null }, 'a.b')).toBeUndefined();
30
+ });
31
+
32
+ it('resolves array index via dot notation', () => {
33
+ expect(getNestedValue({ items: ['x', 'y'] }, 'items.0')).toBe('x');
34
+ });
35
+ });
36
+
37
+ // ─── resolveTemplates ────────────────────────────────────────────────────────
38
+
39
+ describe('resolveTemplates', () => {
40
+ const ctx = { name: 'Alice', node1: { status: 200, data: { id: 5 } } };
41
+
42
+ it('replaces simple template', () => {
43
+ expect(resolveTemplates('Hello {{name}}', ctx)).toBe('Hello Alice');
44
+ });
45
+
46
+ it('replaces nested path template', () => {
47
+ expect(resolveTemplates('Status: {{node1.status}}', ctx)).toBe('Status: 200');
48
+ });
49
+
50
+ it('replaces multiple templates in one string', () => {
51
+ expect(resolveTemplates('{{name}} got {{node1.status}}', ctx)).toBe('Alice got 200');
52
+ });
53
+
54
+ it('replaces missing path with empty string', () => {
55
+ expect(resolveTemplates('{{missing}}', ctx)).toBe('');
56
+ });
57
+
58
+ it('recursively resolves objects', () => {
59
+ const obj = { url: 'https://api.com/{{node1.data.id}}', method: 'GET' };
60
+ expect(resolveTemplates(obj, ctx)).toEqual({ url: 'https://api.com/5', method: 'GET' });
61
+ });
62
+
63
+ it('recursively resolves arrays', () => {
64
+ expect(resolveTemplates(['{{name}}', '{{node1.status}}'], ctx)).toEqual(['Alice', '200']);
65
+ });
66
+
67
+ it('passes through non-string primitives', () => {
68
+ expect(resolveTemplates(42, ctx)).toBe(42);
69
+ expect(resolveTemplates(null, ctx)).toBe(null);
70
+ expect(resolveTemplates(true, ctx)).toBe(true);
71
+ });
72
+ });
73
+
74
+ // ─── resolveExpression ───────────────────────────────────────────────────────
75
+
76
+ describe('resolveExpression', () => {
77
+ it('resolves existing path', () => {
78
+ expect(resolveExpression('a.b', { a: { b: 10 } })).toBe(10);
79
+ });
80
+
81
+ it('returns raw string for unresolvable expression', () => {
82
+ expect(resolveExpression('literal_value', {})).toBe('literal_value');
83
+ });
84
+ });
85
+
86
+ // ─── evaluateRule ────────────────────────────────────────────────────────────
87
+
88
+ describe('evaluateRule', () => {
89
+ it('equals / ==', () => {
90
+ expect(evaluateRule('hello', 'equals', 'hello')).toBe(true);
91
+ expect(evaluateRule('hello', '==', 'world')).toBe(false);
92
+ });
93
+
94
+ it('notEquals / !=', () => {
95
+ expect(evaluateRule('a', 'notEquals', 'b')).toBe(true);
96
+ expect(evaluateRule('a', '!=', 'a')).toBe(false);
97
+ });
98
+
99
+ it('contains / notContains', () => {
100
+ expect(evaluateRule('hello world', 'contains', 'world')).toBe(true);
101
+ expect(evaluateRule('hello world', 'notContains', 'xyz')).toBe(true);
102
+ });
103
+
104
+ it('numeric comparisons', () => {
105
+ expect(evaluateRule(10, 'greaterThan', '5')).toBe(true);
106
+ expect(evaluateRule(10, '>', '15')).toBe(false);
107
+ expect(evaluateRule(10, 'lessThan', '15')).toBe(true);
108
+ expect(evaluateRule(10, '<', '5')).toBe(false);
109
+ expect(evaluateRule(10, '>=', '10')).toBe(true);
110
+ expect(evaluateRule(10, '<=', '10')).toBe(true);
111
+ });
112
+
113
+ it('isEmpty / isNotEmpty', () => {
114
+ expect(evaluateRule(null, 'isEmpty', '')).toBe(true);
115
+ expect(evaluateRule('', 'isEmpty', '')).toBe(true);
116
+ expect(evaluateRule('x', 'isEmpty', '')).toBe(false);
117
+ expect(evaluateRule('x', 'isNotEmpty', '')).toBe(true);
118
+ expect(evaluateRule(null, 'isNotEmpty', '')).toBe(false);
119
+ });
120
+
121
+ it('unknown operator returns false', () => {
122
+ expect(evaluateRule('a', 'unknownOp', 'b')).toBe(false);
123
+ });
124
+ });
125
+
126
+ // ─── evaluateConditions ──────────────────────────────────────────────────────
127
+
128
+ describe('evaluateConditions', () => {
129
+ const ctx = { status: 'active', count: '10' };
130
+
131
+ it('returns true for empty conditions', () => {
132
+ expect(evaluateConditions([], ctx, 'AND')).toBe(true);
133
+ });
134
+
135
+ it('AND: all must pass', () => {
136
+ const conds = [
137
+ { field: 'status', operator: 'equals', value: 'active' },
138
+ { field: 'count', operator: 'greaterThan', value: '5' },
139
+ ];
140
+ expect(evaluateConditions(conds, ctx, 'AND')).toBe(true);
141
+ });
142
+
143
+ it('AND: one fails → false', () => {
144
+ const conds = [
145
+ { field: 'status', operator: 'equals', value: 'active' },
146
+ { field: 'count', operator: 'greaterThan', value: '100' },
147
+ ];
148
+ expect(evaluateConditions(conds, ctx, 'AND')).toBe(false);
149
+ });
150
+
151
+ it('OR: one passes → true', () => {
152
+ const conds = [
153
+ { field: 'status', operator: 'equals', value: 'inactive' },
154
+ { field: 'count', operator: 'equals', value: '10' },
155
+ ];
156
+ expect(evaluateConditions(conds, ctx, 'OR')).toBe(true);
157
+ });
158
+
159
+ it('OR: all fail → false', () => {
160
+ const conds = [
161
+ { field: 'status', operator: 'equals', value: 'inactive' },
162
+ { field: 'count', operator: 'greaterThan', value: '100' },
163
+ ];
164
+ expect(evaluateConditions(conds, ctx, 'OR')).toBe(false);
165
+ });
166
+ });
167
+
168
+ // ─── Graph Navigation ────────────────────────────────────────────────────────
169
+
170
+ describe('getSuccessorsFromNodes', () => {
171
+ const dsl: WorkflowDSL = {
172
+ nodes: [
173
+ { id: 'a', type: 'manual_trigger', label: 'A', position: { x: 0, y: 0 }, config: {} },
174
+ { id: 'b', type: 'http_request', label: 'B', position: { x: 0, y: 0 }, config: {} },
175
+ { id: 'c', type: 'db_query', label: 'C', position: { x: 0, y: 0 }, config: {} },
176
+ ],
177
+ edges: [
178
+ { id: 'e1', source: 'a', target: 'b' },
179
+ { id: 'e2', source: 'a', target: 'c' },
180
+ { id: 'e3', source: 'b', target: 'c' },
181
+ ],
182
+ };
183
+
184
+ it('finds direct successors', () => {
185
+ expect(getSuccessorsFromNodes(dsl, ['a'])).toEqual(['b', 'c']);
186
+ });
187
+
188
+ it('finds successors from multiple sources', () => {
189
+ expect(getSuccessorsFromNodes(dsl, ['a', 'b'])).toEqual(['b', 'c', 'c']);
190
+ });
191
+
192
+ it('returns empty for leaf nodes', () => {
193
+ expect(getSuccessorsFromNodes(dsl, ['c'])).toEqual([]);
194
+ });
195
+ });
196
+
197
+ describe('getNextNodes', () => {
198
+ const dsl: WorkflowDSL = {
199
+ nodes: [
200
+ { id: 'cond', type: 'if_else', label: 'Condition', position: { x: 0, y: 0 }, config: {} },
201
+ { id: 'yes', type: 'http_request', label: 'Yes', position: { x: 0, y: 0 }, config: {} },
202
+ { id: 'no', type: 'send_notification', label: 'No', position: { x: 0, y: 0 }, config: {} },
203
+ ],
204
+ edges: [
205
+ { id: 'e1', source: 'cond', target: 'yes', sourceHandle: 'true' },
206
+ { id: 'e2', source: 'cond', target: 'no', sourceHandle: 'false' },
207
+ ],
208
+ };
209
+
210
+ it('follows branch edge when branch specified', () => {
211
+ const node = dsl.nodes[0];
212
+ expect(getNextNodes(dsl, node, 'true')).toEqual(['yes']);
213
+ expect(getNextNodes(dsl, node, 'false')).toEqual(['no']);
214
+ });
215
+
216
+ it('follows all edges when no branch', () => {
217
+ const node = dsl.nodes[0];
218
+ expect(getNextNodes(dsl, node, undefined)).toEqual(['yes', 'no']);
219
+ });
220
+
221
+ it('falls back to all edges if branch has no matching edge', () => {
222
+ const node = dsl.nodes[0];
223
+ expect(getNextNodes(dsl, node, 'nonexistent')).toEqual(['yes', 'no']);
224
+ });
225
+ });
@@ -0,0 +1,101 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { ALL_NODE_TYPES, ACTIVITY_NODE_TYPES, NodeType } from '../dsl/node-types';
3
+ import * as path from 'path';
4
+ import * as fs from 'fs';
5
+
6
+ // ─── Consistency with seed file ──────────────────────────────────────────────
7
+
8
+ describe('Node Type Consistency', () => {
9
+ // Read the seed file and extract types
10
+ const seedFilePath = path.resolve(__dirname, '../../../../server/seed/workflow-node-types.ts');
11
+ const seedContent = fs.readFileSync(seedFilePath, 'utf-8');
12
+
13
+ // Extract only top-level node type identifiers (at 4-space indent, not JSON Schema "type" fields)
14
+ // Pattern: lines like ` type: 'cron_trigger',` (4 spaces indent, contains underscore or known names)
15
+ const typeRegex = /^\s{4}type:\s*'([a-z][a-z_]+)'/gm;
16
+ const seedTypes: string[] = [];
17
+ let match: RegExpExecArray | null;
18
+ while ((match = typeRegex.exec(seedContent)) !== null) {
19
+ // Exclude JSON Schema type values like 'object', 'string', 'number', 'array', 'boolean'
20
+ const jsonSchemaTypes = ['object', 'string', 'number', 'array', 'boolean', 'integer'];
21
+ if (!jsonSchemaTypes.includes(match[1])) {
22
+ seedTypes.push(match[1]);
23
+ }
24
+ }
25
+
26
+ it('seed file has node types defined', () => {
27
+ expect(seedTypes.length).toBeGreaterThan(0);
28
+ });
29
+
30
+ it('every seed node type exists in ALL_NODE_TYPES', () => {
31
+ const allTypes = ALL_NODE_TYPES as readonly string[];
32
+ const missing = seedTypes.filter((t) => !allTypes.includes(t));
33
+ expect(missing).toEqual([]);
34
+ });
35
+
36
+ it('every ALL_NODE_TYPES entry exists in seed file', () => {
37
+ const missing = ALL_NODE_TYPES.filter((t) => !seedTypes.includes(t));
38
+ expect(missing).toEqual([]);
39
+ });
40
+
41
+ it('no duplicates in ALL_NODE_TYPES', () => {
42
+ const set = new Set(ALL_NODE_TYPES);
43
+ expect(set.size).toBe(ALL_NODE_TYPES.length);
44
+ });
45
+
46
+ it('ACTIVITY_NODE_TYPES is a subset of ALL_NODE_TYPES', () => {
47
+ const allTypes = ALL_NODE_TYPES as readonly string[];
48
+ for (const t of ACTIVITY_NODE_TYPES) {
49
+ expect(allTypes).toContain(t);
50
+ }
51
+ });
52
+ });
53
+
54
+ // ─── Consistency with executeNode switch ─────────────────────────────────────
55
+
56
+ describe('executeNode switch coverage', () => {
57
+ const workflowFilePath = path.resolve(__dirname, '../workflows/dsl-workflow.ts');
58
+ const workflowContent = fs.readFileSync(workflowFilePath, 'utf-8');
59
+
60
+ it('every NodeType has a case in executeNode', () => {
61
+ const missingCases: string[] = [];
62
+ for (const nodeType of ALL_NODE_TYPES) {
63
+ // Check for case 'nodeType': pattern
64
+ if (!workflowContent.includes(`case '${nodeType}'`)) {
65
+ missingCases.push(nodeType);
66
+ }
67
+ }
68
+ expect(missingCases).toEqual([]);
69
+ });
70
+ });
71
+
72
+ // ─── Consistency with activity files ─────────────────────────────────────────
73
+
74
+ describe('Activity file coverage', () => {
75
+ const activitiesIndexPath = path.resolve(__dirname, '../activities/index.ts');
76
+ const activitiesContent = fs.readFileSync(activitiesIndexPath, 'utf-8');
77
+
78
+ // Map node types to expected activity function names
79
+ const nodeTypeToActivity: Record<string, string> = {
80
+ http_request: 'httpRequest',
81
+ db_query: 'dbQuery',
82
+ db_execute: 'dbExecute',
83
+ send_notification: 'sendNotification',
84
+ code: 'codeExecute',
85
+ };
86
+
87
+ it('every ACTIVITY_NODE_TYPE has a corresponding exported activity', () => {
88
+ const missing: string[] = [];
89
+ for (const nodeType of ACTIVITY_NODE_TYPES) {
90
+ const expectedFn = nodeTypeToActivity[nodeType];
91
+ if (!expectedFn) {
92
+ missing.push(`${nodeType}: no mapping defined in test`);
93
+ continue;
94
+ }
95
+ if (!activitiesContent.includes(expectedFn)) {
96
+ missing.push(`${nodeType}: activity "${expectedFn}" not found in activities/index.ts`);
97
+ }
98
+ }
99
+ expect(missing).toEqual([]);
100
+ });
101
+ });
@@ -0,0 +1,51 @@
1
+ export interface CodeExecuteInput {
2
+ script: string;
3
+ mode?: 'runOnceForAll' | 'runOnceForEach';
4
+ timeout?: number;
5
+ data?: any;
6
+ items?: any[];
7
+ }
8
+
9
+ export interface CodeExecuteOutput {
10
+ result: any;
11
+ }
12
+
13
+ /**
14
+ * Execute custom JavaScript code in a sandboxed environment.
15
+ * Uses Node.js vm module for basic isolation.
16
+ * For production, consider isolated-vm for stronger sandboxing.
17
+ */
18
+ export async function codeExecute(input: CodeExecuteInput): Promise<CodeExecuteOutput> {
19
+ const { script, mode = 'runOnceForAll', timeout = 5000, data, items } = input;
20
+ const vm = await import('vm');
21
+
22
+ if (mode === 'runOnceForEach' && Array.isArray(items)) {
23
+ const results: any[] = [];
24
+ for (const item of items) {
25
+ const sandbox = {
26
+ item,
27
+ data,
28
+ console: { log: () => {} },
29
+ result: undefined as any,
30
+ };
31
+ const context = vm.createContext(sandbox);
32
+ const wrappedScript = new vm.Script(`result = (function() { ${script} })()`);
33
+ wrappedScript.runInContext(context, { timeout });
34
+ results.push(sandbox.result);
35
+ }
36
+ return { result: results };
37
+ }
38
+
39
+ // runOnceForAll
40
+ const sandbox = {
41
+ data,
42
+ items: items || [],
43
+ console: { log: () => {} },
44
+ result: undefined as any,
45
+ };
46
+ const context = vm.createContext(sandbox);
47
+ const wrappedScript = new vm.Script(`result = (function() { ${script} })()`);
48
+ wrappedScript.runInContext(context, { timeout });
49
+
50
+ return { result: sandbox.result };
51
+ }
@@ -0,0 +1,85 @@
1
+ import { Pool } from 'pg';
2
+ import { config } from '../config';
3
+
4
+ const pool = new Pool({ connectionString: config.database.url });
5
+
6
+ export interface DbExecuteInput {
7
+ operation: 'insert' | 'update' | 'delete' | 'raw';
8
+ table?: string;
9
+ data?: Record<string, any>;
10
+ where?: string;
11
+ rawSql?: string;
12
+ params?: any[];
13
+ datasource?: string;
14
+ timeout?: number;
15
+ }
16
+
17
+ export interface DbExecuteOutput {
18
+ affectedRows: number;
19
+ insertedId?: string;
20
+ }
21
+
22
+ /**
23
+ * Execute a database write operation (INSERT/UPDATE/DELETE).
24
+ */
25
+ export async function dbExecute(input: DbExecuteInput): Promise<DbExecuteOutput> {
26
+ const client = await pool.connect();
27
+ try {
28
+ if (input.timeout) {
29
+ await client.query(`SET statement_timeout = ${input.timeout}`);
30
+ }
31
+
32
+ let query: string;
33
+ let params: any[] = [];
34
+
35
+ switch (input.operation) {
36
+ case 'raw':
37
+ query = input.rawSql || '';
38
+ params = input.params || [];
39
+ break;
40
+
41
+ case 'insert': {
42
+ if (!input.table || !input.data) throw new Error('table and data required for insert');
43
+ const keys = Object.keys(input.data);
44
+ const values = Object.values(input.data);
45
+ const placeholders = keys.map((_, i) => `$${i + 1}`);
46
+ query = `INSERT INTO ${input.table} (${keys.join(', ')}) VALUES (${placeholders.join(', ')}) RETURNING id`;
47
+ params = values;
48
+ break;
49
+ }
50
+
51
+ case 'update': {
52
+ if (!input.table || !input.data) throw new Error('table and data required for update');
53
+ const entries = Object.entries(input.data);
54
+ const sets = entries.map(([k], i) => `${k} = $${i + 1}`);
55
+ params = entries.map(([, v]) => v);
56
+ query = `UPDATE ${input.table} SET ${sets.join(', ')}`;
57
+ if (input.where) {
58
+ query += ` WHERE ${input.where}`;
59
+ }
60
+ break;
61
+ }
62
+
63
+ case 'delete': {
64
+ if (!input.table) throw new Error('table required for delete');
65
+ query = `DELETE FROM ${input.table}`;
66
+ if (input.where) {
67
+ query += ` WHERE ${input.where}`;
68
+ }
69
+ params = input.params || [];
70
+ break;
71
+ }
72
+
73
+ default:
74
+ throw new Error(`Unknown operation: ${input.operation}`);
75
+ }
76
+
77
+ const result = await client.query(query, params);
78
+ return {
79
+ affectedRows: result.rowCount || 0,
80
+ insertedId: result.rows?.[0]?.id?.toString(),
81
+ };
82
+ } finally {
83
+ client.release();
84
+ }
85
+ }
@@ -0,0 +1,35 @@
1
+ import { Pool } from 'pg';
2
+ import { config } from '../config';
3
+
4
+ const pool = new Pool({ connectionString: config.database.url });
5
+
6
+ export interface DbQueryInput {
7
+ query: string;
8
+ params?: any[];
9
+ datasource?: string;
10
+ timeout?: number;
11
+ }
12
+
13
+ export interface DbQueryOutput {
14
+ rows: any[];
15
+ rowCount: number;
16
+ }
17
+
18
+ /**
19
+ * Execute a database SELECT query.
20
+ */
21
+ export async function dbQuery(input: DbQueryInput): Promise<DbQueryOutput> {
22
+ const client = await pool.connect();
23
+ try {
24
+ if (input.timeout) {
25
+ await client.query(`SET statement_timeout = ${input.timeout}`);
26
+ }
27
+ const result = await client.query(input.query, input.params || []);
28
+ return {
29
+ rows: result.rows,
30
+ rowCount: result.rowCount || 0,
31
+ };
32
+ } finally {
33
+ client.release();
34
+ }
35
+ }