@baasix/baasix 0.1.0

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 (666) hide show
  1. package/LICENSE.MD +85 -0
  2. package/README.md +526 -0
  3. package/assets/banner.jpg +0 -0
  4. package/assets/banner_small.jpg +0 -0
  5. package/assets/logo_icon.svg +20 -0
  6. package/assets/logo_icon_rounded.svg +20 -0
  7. package/dist/LICENSE.MD +85 -0
  8. package/dist/README.md +526 -0
  9. package/dist/app/404/index.html +1 -0
  10. package/dist/app/404.html +1 -0
  11. package/dist/app/_next/static/chunks/041e1f03-56ae8a902a7f2fe6.js +24 -0
  12. package/dist/app/_next/static/chunks/1117-05479929a8da73e3.js +1 -0
  13. package/dist/app/_next/static/chunks/1299.77cc7b7b76b75cba.js +1 -0
  14. package/dist/app/_next/static/chunks/1303-35a96e9c9cdeab9d.js +1 -0
  15. package/dist/app/_next/static/chunks/1509-56ac00cdaaecdf53.js +1 -0
  16. package/dist/app/_next/static/chunks/1668-e3eabd0f6753c780.js +1 -0
  17. package/dist/app/_next/static/chunks/1783-d9fb550fd324300c.js +1 -0
  18. package/dist/app/_next/static/chunks/2117-29b5fa47421595ad.js +2 -0
  19. package/dist/app/_next/static/chunks/2344.35b46d2179a765b5.js +1 -0
  20. package/dist/app/_next/static/chunks/257.990da16794a31292.js +1 -0
  21. package/dist/app/_next/static/chunks/2676-73b0ee7c80073a84.js +1 -0
  22. package/dist/app/_next/static/chunks/3563-b8842744384391fe.js +1 -0
  23. package/dist/app/_next/static/chunks/363642f4-933b579ed3c85f60.js +1 -0
  24. package/dist/app/_next/static/chunks/3817-e20c8f0a0810fc95.js +1 -0
  25. package/dist/app/_next/static/chunks/3834.84944e390d902509.js +2 -0
  26. package/dist/app/_next/static/chunks/4043-3a30c8a75896f241.js +1 -0
  27. package/dist/app/_next/static/chunks/4225-14090c7c0cd9dec6.js +1 -0
  28. package/dist/app/_next/static/chunks/4438-c9a12ca15b6e9160.js +1 -0
  29. package/dist/app/_next/static/chunks/4458-679fd0c6884f456a.js +1 -0
  30. package/dist/app/_next/static/chunks/4475-8bdfbd536fba8c48.js +1 -0
  31. package/dist/app/_next/static/chunks/4883-8a924721bb21b3b0.js +1 -0
  32. package/dist/app/_next/static/chunks/489-683ab07188f9df2b.js +1 -0
  33. package/dist/app/_next/static/chunks/4952-1b97320cf61f3f21.js +1 -0
  34. package/dist/app/_next/static/chunks/5094-8d53e403235d4ca6.js +1 -0
  35. package/dist/app/_next/static/chunks/5101-3a146e0625747ad1.js +1 -0
  36. package/dist/app/_next/static/chunks/54a60aa6-d9747982e0a81f58.js +79 -0
  37. package/dist/app/_next/static/chunks/5650-f096291df402bfc2.js +1 -0
  38. package/dist/app/_next/static/chunks/600-539045311240f579.js +1 -0
  39. package/dist/app/_next/static/chunks/6170-803b82e19d3ade6d.js +89 -0
  40. package/dist/app/_next/static/chunks/6241-30d7169d1010e5a4.js +1 -0
  41. package/dist/app/_next/static/chunks/6530-a91e10cffa4200c4.js +1 -0
  42. package/dist/app/_next/static/chunks/6547-4bbbdb5c399aef1e.js +1 -0
  43. package/dist/app/_next/static/chunks/6712-781937c53a2c49da.js +1 -0
  44. package/dist/app/_next/static/chunks/6fcbdc68-90be1a5480b8d353.js +1 -0
  45. package/dist/app/_next/static/chunks/70e0d97a-aeaf0cdc26ba1a58.js +1 -0
  46. package/dist/app/_next/static/chunks/7214-5154a89d08d24dde.js +1 -0
  47. package/dist/app/_next/static/chunks/7324-b53229c59a640880.js +10 -0
  48. package/dist/app/_next/static/chunks/7636-66424f0b51d350e9.js +1 -0
  49. package/dist/app/_next/static/chunks/7874-39a3f2541165a675.js +1 -0
  50. package/dist/app/_next/static/chunks/7982-9da12b83f11e3f5f.js +1 -0
  51. package/dist/app/_next/static/chunks/8213a2eb-da25a3b3c5521b2b.js +1 -0
  52. package/dist/app/_next/static/chunks/8473-6598318371eca31b.js +1 -0
  53. package/dist/app/_next/static/chunks/8640fa6b-72e43370f68e5587.js +1 -0
  54. package/dist/app/_next/static/chunks/9090-3ef676f29c95f1c7.js +1 -0
  55. package/dist/app/_next/static/chunks/9124-a02f9e209e6e3cce.js +1 -0
  56. package/dist/app/_next/static/chunks/926-156f32067d111d6b.js +1 -0
  57. package/dist/app/_next/static/chunks/9487-b17481605e513b83.js +1 -0
  58. package/dist/app/_next/static/chunks/9599-a7e572bb88c3392b.js +1 -0
  59. package/dist/app/_next/static/chunks/9881-419697138376e755.js +1 -0
  60. package/dist/app/_next/static/chunks/app/(authenticated)/activity-log/all-activity/page-8917930b4d663405.js +1 -0
  61. package/dist/app/_next/static/chunks/app/(authenticated)/activity-log/email-log/page-b27a6ee32782d7df.js +1 -0
  62. package/dist/app/_next/static/chunks/app/(authenticated)/activity-log/notifications/page-b7eda523ede2702c.js +1 -0
  63. package/dist/app/_next/static/chunks/app/(authenticated)/activity-log/page-1cfa62d1caedaed0.js +1 -0
  64. package/dist/app/_next/static/chunks/app/(authenticated)/activity-log/sessions/page-3e21e20db90aeff7.js +1 -0
  65. package/dist/app/_next/static/chunks/app/(authenticated)/activity-log/workflow-executions/page-27bcc26b747fb29b.js +1 -0
  66. package/dist/app/_next/static/chunks/app/(authenticated)/activity-log/workflow-logs/page-9f9e9e952aef436e.js +1 -0
  67. package/dist/app/_next/static/chunks/app/(authenticated)/change-password/page-8d61aa499eabb127.js +1 -0
  68. package/dist/app/_next/static/chunks/app/(authenticated)/dashboard/page-1ceeac9e72997a8a.js +1 -0
  69. package/dist/app/_next/static/chunks/app/(authenticated)/data-browser/page-8cda2b57759dd670.js +1 -0
  70. package/dist/app/_next/static/chunks/app/(authenticated)/file-manager/page-8c6f1b1da66ad7e4.js +1 -0
  71. package/dist/app/_next/static/chunks/app/(authenticated)/layout-f70d225b2759c998.js +1 -0
  72. package/dist/app/_next/static/chunks/app/(authenticated)/settings/migrations/page-aacec8f7cfb40ab2.js +1 -0
  73. package/dist/app/_next/static/chunks/app/(authenticated)/settings/permissions/page-828110cfcde429c6.js +1 -0
  74. package/dist/app/_next/static/chunks/app/(authenticated)/settings/project/page-420e794bb76bd204.js +1 -0
  75. package/dist/app/_next/static/chunks/app/(authenticated)/settings/roles/page-9001d02b28f70708.js +1 -0
  76. package/dist/app/_next/static/chunks/app/(authenticated)/settings/schema/page-899574f35091dd58.js +1 -0
  77. package/dist/app/_next/static/chunks/app/(authenticated)/settings/tasks/page-ad7ab3e27c83f44f.js +1 -0
  78. package/dist/app/_next/static/chunks/app/(authenticated)/settings/templates/edit/page-bd83414cb8c4cb04.js +1 -0
  79. package/dist/app/_next/static/chunks/app/(authenticated)/settings/templates/page-3181447f8772b1d3.js +1 -0
  80. package/dist/app/_next/static/chunks/app/(authenticated)/settings/tenants/page-ef9bfbacef5a1d73.js +1 -0
  81. package/dist/app/_next/static/chunks/app/(authenticated)/users/invites/page-480306b7b2bbac7e.js +1 -0
  82. package/dist/app/_next/static/chunks/app/(authenticated)/users/list/page-74da51254c2606b3.js +1 -0
  83. package/dist/app/_next/static/chunks/app/(authenticated)/users/page-e99c6f0b915001b2.js +1 -0
  84. package/dist/app/_next/static/chunks/app/(authenticated)/users/preferences/page-1a935630ce8f2b12.js +1 -0
  85. package/dist/app/_next/static/chunks/app/(authenticated)/users/user-roles/page-901dfb8ea1f39ca8.js +1 -0
  86. package/dist/app/_next/static/chunks/app/(authenticated)/workflows/detail/page-9a6b839aea688ca4.js +1 -0
  87. package/dist/app/_next/static/chunks/app/(authenticated)/workflows/edit/page-11774efbc2fecae2.js +1 -0
  88. package/dist/app/_next/static/chunks/app/(authenticated)/workflows/execution/page-8ec1aea90412c03d.js +1 -0
  89. package/dist/app/_next/static/chunks/app/(authenticated)/workflows/page-88bc5b36ccb0a1f7.js +1 -0
  90. package/dist/app/_next/static/chunks/app/(public)/forgot-password/page-ed263fd46ef81c20.js +1 -0
  91. package/dist/app/_next/static/chunks/app/(public)/layout-f538977545844af8.js +1 -0
  92. package/dist/app/_next/static/chunks/app/(public)/login/page-c0a10b137f346096.js +1 -0
  93. package/dist/app/_next/static/chunks/app/(public)/register/page-4cb7644893efd9b3.js +1 -0
  94. package/dist/app/_next/static/chunks/app/_not-found/page-653f8815b78256cc.js +1 -0
  95. package/dist/app/_next/static/chunks/app/layout-591ca7a3e16528a1.js +1 -0
  96. package/dist/app/_next/static/chunks/app/page-dd19d124b5fa2577.js +1 -0
  97. package/dist/app/_next/static/chunks/c37d3baf.c2ff165f5b02c692.js +1 -0
  98. package/dist/app/_next/static/chunks/d0deef33.0379166a4ec23470.js +1 -0
  99. package/dist/app/_next/static/chunks/fd9d1056-54169f07cd680d6c.js +1 -0
  100. package/dist/app/_next/static/chunks/framework-8e0e0f4a6b83a956.js +1 -0
  101. package/dist/app/_next/static/chunks/main-324e91f5a430cddf.js +1 -0
  102. package/dist/app/_next/static/chunks/main-app-55bcae20c77aaf0e.js +1 -0
  103. package/dist/app/_next/static/chunks/pages/_app-3c9ca398d360b709.js +1 -0
  104. package/dist/app/_next/static/chunks/pages/_error-cf5ca766ac8f493f.js +1 -0
  105. package/dist/app/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
  106. package/dist/app/_next/static/chunks/webpack-2c306566f7ee1b63.js +1 -0
  107. package/dist/app/_next/static/css/6c4002bae4e236b2.css +3 -0
  108. package/dist/app/_next/static/css/a275cc2b185e04f8.css +1 -0
  109. package/dist/app/_next/static/eCWhKA8XHqmB1zgFcEtN2/_buildManifest.js +1 -0
  110. package/dist/app/_next/static/eCWhKA8XHqmB1zgFcEtN2/_ssgManifest.js +1 -0
  111. package/dist/app/activity-log/all-activity/index.html +1 -0
  112. package/dist/app/activity-log/all-activity/index.txt +14 -0
  113. package/dist/app/activity-log/email-log/index.html +1 -0
  114. package/dist/app/activity-log/email-log/index.txt +14 -0
  115. package/dist/app/activity-log/index.html +1 -0
  116. package/dist/app/activity-log/index.txt +14 -0
  117. package/dist/app/activity-log/notifications/index.html +1 -0
  118. package/dist/app/activity-log/notifications/index.txt +14 -0
  119. package/dist/app/activity-log/sessions/index.html +1 -0
  120. package/dist/app/activity-log/sessions/index.txt +14 -0
  121. package/dist/app/activity-log/workflow-executions/index.html +1 -0
  122. package/dist/app/activity-log/workflow-executions/index.txt +14 -0
  123. package/dist/app/activity-log/workflow-logs/index.html +1 -0
  124. package/dist/app/activity-log/workflow-logs/index.txt +14 -0
  125. package/dist/app/change-password/index.html +1 -0
  126. package/dist/app/change-password/index.txt +14 -0
  127. package/dist/app/dashboard/index.html +1 -0
  128. package/dist/app/dashboard/index.txt +14 -0
  129. package/dist/app/data-browser/index.html +1 -0
  130. package/dist/app/data-browser/index.txt +14 -0
  131. package/dist/app/file-manager/index.html +1 -0
  132. package/dist/app/file-manager/index.txt +14 -0
  133. package/dist/app/forgot-password/index.html +1 -0
  134. package/dist/app/forgot-password/index.txt +13 -0
  135. package/dist/app/index.html +1 -0
  136. package/dist/app/index.txt +9 -0
  137. package/dist/app/login/index.html +1 -0
  138. package/dist/app/login/index.txt +13 -0
  139. package/dist/app/logo-dark.png +0 -0
  140. package/dist/app/logo-icon.svg +81 -0
  141. package/dist/app/logo-light.png +0 -0
  142. package/dist/app/register/index.html +1 -0
  143. package/dist/app/register/index.txt +13 -0
  144. package/dist/app/settings/migrations/index.html +1 -0
  145. package/dist/app/settings/migrations/index.txt +14 -0
  146. package/dist/app/settings/permissions/index.html +1 -0
  147. package/dist/app/settings/permissions/index.txt +14 -0
  148. package/dist/app/settings/project/index.html +1 -0
  149. package/dist/app/settings/project/index.txt +14 -0
  150. package/dist/app/settings/roles/index.html +1 -0
  151. package/dist/app/settings/roles/index.txt +14 -0
  152. package/dist/app/settings/schema/index.html +1 -0
  153. package/dist/app/settings/schema/index.txt +14 -0
  154. package/dist/app/settings/tasks/index.html +1 -0
  155. package/dist/app/settings/tasks/index.txt +14 -0
  156. package/dist/app/settings/templates/edit/index.html +1 -0
  157. package/dist/app/settings/templates/edit/index.txt +14 -0
  158. package/dist/app/settings/templates/index.html +1 -0
  159. package/dist/app/settings/templates/index.txt +14 -0
  160. package/dist/app/settings/tenants/index.html +1 -0
  161. package/dist/app/settings/tenants/index.txt +14 -0
  162. package/dist/app/users/index.html +1 -0
  163. package/dist/app/users/index.txt +14 -0
  164. package/dist/app/users/invites/index.html +1 -0
  165. package/dist/app/users/invites/index.txt +14 -0
  166. package/dist/app/users/list/index.html +1 -0
  167. package/dist/app/users/list/index.txt +14 -0
  168. package/dist/app/users/preferences/index.html +1 -0
  169. package/dist/app/users/preferences/index.txt +14 -0
  170. package/dist/app/users/user-roles/index.html +1 -0
  171. package/dist/app/users/user-roles/index.txt +14 -0
  172. package/dist/app/workflows/detail/index.html +1 -0
  173. package/dist/app/workflows/detail/index.txt +14 -0
  174. package/dist/app/workflows/edit/index.html +1 -0
  175. package/dist/app/workflows/edit/index.txt +14 -0
  176. package/dist/app/workflows/execution/index.html +1 -0
  177. package/dist/app/workflows/execution/index.txt +14 -0
  178. package/dist/app/workflows/index.html +1 -0
  179. package/dist/app/workflows/index.txt +14 -0
  180. package/dist/app.d.ts +36 -0
  181. package/dist/app.d.ts.map +1 -0
  182. package/dist/app.js +546 -0
  183. package/dist/app.js.map +1 -0
  184. package/dist/auth/adapters/baasix-adapter.d.ts +12 -0
  185. package/dist/auth/adapters/baasix-adapter.d.ts.map +1 -0
  186. package/dist/auth/adapters/baasix-adapter.js +318 -0
  187. package/dist/auth/adapters/baasix-adapter.js.map +1 -0
  188. package/dist/auth/adapters/index.d.ts +6 -0
  189. package/dist/auth/adapters/index.d.ts.map +1 -0
  190. package/dist/auth/adapters/index.js +5 -0
  191. package/dist/auth/adapters/index.js.map +1 -0
  192. package/dist/auth/core.d.ts +73 -0
  193. package/dist/auth/core.d.ts.map +1 -0
  194. package/dist/auth/core.js +528 -0
  195. package/dist/auth/core.js.map +1 -0
  196. package/dist/auth/index.d.ts +56 -0
  197. package/dist/auth/index.d.ts.map +1 -0
  198. package/dist/auth/index.js +58 -0
  199. package/dist/auth/index.js.map +1 -0
  200. package/dist/auth/oauth2/index.d.ts +5 -0
  201. package/dist/auth/oauth2/index.d.ts.map +1 -0
  202. package/dist/auth/oauth2/index.js +5 -0
  203. package/dist/auth/oauth2/index.js.map +1 -0
  204. package/dist/auth/oauth2/utils.d.ts +90 -0
  205. package/dist/auth/oauth2/utils.d.ts.map +1 -0
  206. package/dist/auth/oauth2/utils.js +167 -0
  207. package/dist/auth/oauth2/utils.js.map +1 -0
  208. package/dist/auth/providers/apple.d.ts +28 -0
  209. package/dist/auth/providers/apple.d.ts.map +1 -0
  210. package/dist/auth/providers/apple.js +192 -0
  211. package/dist/auth/providers/apple.js.map +1 -0
  212. package/dist/auth/providers/credential.d.ts +87 -0
  213. package/dist/auth/providers/credential.d.ts.map +1 -0
  214. package/dist/auth/providers/credential.js +162 -0
  215. package/dist/auth/providers/credential.js.map +1 -0
  216. package/dist/auth/providers/facebook.d.ts +26 -0
  217. package/dist/auth/providers/facebook.d.ts.map +1 -0
  218. package/dist/auth/providers/facebook.js +112 -0
  219. package/dist/auth/providers/facebook.js.map +1 -0
  220. package/dist/auth/providers/github.d.ts +29 -0
  221. package/dist/auth/providers/github.d.ts.map +1 -0
  222. package/dist/auth/providers/github.js +144 -0
  223. package/dist/auth/providers/github.js.map +1 -0
  224. package/dist/auth/providers/google.d.ts +32 -0
  225. package/dist/auth/providers/google.d.ts.map +1 -0
  226. package/dist/auth/providers/google.js +145 -0
  227. package/dist/auth/providers/google.js.map +1 -0
  228. package/dist/auth/providers/index.d.ts +22 -0
  229. package/dist/auth/providers/index.d.ts.map +1 -0
  230. package/dist/auth/providers/index.js +17 -0
  231. package/dist/auth/providers/index.js.map +1 -0
  232. package/dist/auth/routes.d.ts +63 -0
  233. package/dist/auth/routes.d.ts.map +1 -0
  234. package/dist/auth/routes.js +827 -0
  235. package/dist/auth/routes.js.map +1 -0
  236. package/dist/auth/services/index.d.ts +10 -0
  237. package/dist/auth/services/index.d.ts.map +1 -0
  238. package/dist/auth/services/index.js +7 -0
  239. package/dist/auth/services/index.js.map +1 -0
  240. package/dist/auth/services/session.d.ts +81 -0
  241. package/dist/auth/services/session.d.ts.map +1 -0
  242. package/dist/auth/services/session.js +186 -0
  243. package/dist/auth/services/session.js.map +1 -0
  244. package/dist/auth/services/token.d.ts +41 -0
  245. package/dist/auth/services/token.d.ts.map +1 -0
  246. package/dist/auth/services/token.js +44 -0
  247. package/dist/auth/services/token.js.map +1 -0
  248. package/dist/auth/services/verification.d.ts +77 -0
  249. package/dist/auth/services/verification.d.ts.map +1 -0
  250. package/dist/auth/services/verification.js +143 -0
  251. package/dist/auth/services/verification.js.map +1 -0
  252. package/dist/auth/types.d.ts +318 -0
  253. package/dist/auth/types.d.ts.map +1 -0
  254. package/dist/auth/types.js +6 -0
  255. package/dist/auth/types.js.map +1 -0
  256. package/dist/customTypes/arrays.d.ts +200 -0
  257. package/dist/customTypes/arrays.d.ts.map +1 -0
  258. package/dist/customTypes/arrays.js +309 -0
  259. package/dist/customTypes/arrays.js.map +1 -0
  260. package/dist/customTypes/index.d.ts +8 -0
  261. package/dist/customTypes/index.d.ts.map +1 -0
  262. package/dist/customTypes/index.js +11 -0
  263. package/dist/customTypes/index.js.map +1 -0
  264. package/dist/customTypes/postgis.d.ts +146 -0
  265. package/dist/customTypes/postgis.d.ts.map +1 -0
  266. package/dist/customTypes/postgis.js +315 -0
  267. package/dist/customTypes/postgis.js.map +1 -0
  268. package/dist/customTypes/ranges.d.ts +128 -0
  269. package/dist/customTypes/ranges.d.ts.map +1 -0
  270. package/dist/customTypes/ranges.js +257 -0
  271. package/dist/customTypes/ranges.js.map +1 -0
  272. package/dist/index.d.ts +37 -0
  273. package/dist/index.d.ts.map +1 -0
  274. package/dist/index.js +42 -0
  275. package/dist/index.js.map +1 -0
  276. package/dist/migrations/0.1.0-alpha.0_initial_setup.d.ts +29 -0
  277. package/dist/migrations/0.1.0-alpha.0_initial_setup.d.ts.map +1 -0
  278. package/dist/migrations/0.1.0-alpha.0_initial_setup.js +72 -0
  279. package/dist/migrations/0.1.0-alpha.0_initial_setup.js.map +1 -0
  280. package/dist/migrations/_example_migration.d.ts +31 -0
  281. package/dist/migrations/_example_migration.d.ts.map +1 -0
  282. package/dist/migrations/_example_migration.js +75 -0
  283. package/dist/migrations/_example_migration.js.map +1 -0
  284. package/dist/plugins/definePlugin.d.ts +49 -0
  285. package/dist/plugins/definePlugin.d.ts.map +1 -0
  286. package/dist/plugins/definePlugin.js +131 -0
  287. package/dist/plugins/definePlugin.js.map +1 -0
  288. package/dist/plugins/softDelete.d.ts +179 -0
  289. package/dist/plugins/softDelete.d.ts.map +1 -0
  290. package/dist/plugins/softDelete.js +235 -0
  291. package/dist/plugins/softDelete.js.map +1 -0
  292. package/dist/routes/auth.route.d.ts +14 -0
  293. package/dist/routes/auth.route.d.ts.map +1 -0
  294. package/dist/routes/auth.route.js +421 -0
  295. package/dist/routes/auth.route.js.map +1 -0
  296. package/dist/routes/file.route.d.ts +7 -0
  297. package/dist/routes/file.route.d.ts.map +1 -0
  298. package/dist/routes/file.route.js +274 -0
  299. package/dist/routes/file.route.js.map +1 -0
  300. package/dist/routes/items.route.d.ts +7 -0
  301. package/dist/routes/items.route.d.ts.map +1 -0
  302. package/dist/routes/items.route.js +369 -0
  303. package/dist/routes/items.route.js.map +1 -0
  304. package/dist/routes/migration.route.d.ts +7 -0
  305. package/dist/routes/migration.route.d.ts.map +1 -0
  306. package/dist/routes/migration.route.js +225 -0
  307. package/dist/routes/migration.route.js.map +1 -0
  308. package/dist/routes/notification.route.d.ts +7 -0
  309. package/dist/routes/notification.route.d.ts.map +1 -0
  310. package/dist/routes/notification.route.js +124 -0
  311. package/dist/routes/notification.route.js.map +1 -0
  312. package/dist/routes/openapi.route.d.ts +7 -0
  313. package/dist/routes/openapi.route.d.ts.map +1 -0
  314. package/dist/routes/openapi.route.js +2169 -0
  315. package/dist/routes/openapi.route.js.map +1 -0
  316. package/dist/routes/permission.route.d.ts +7 -0
  317. package/dist/routes/permission.route.d.ts.map +1 -0
  318. package/dist/routes/permission.route.js +158 -0
  319. package/dist/routes/permission.route.js.map +1 -0
  320. package/dist/routes/realtime.route.d.ts +21 -0
  321. package/dist/routes/realtime.route.d.ts.map +1 -0
  322. package/dist/routes/realtime.route.js +243 -0
  323. package/dist/routes/realtime.route.js.map +1 -0
  324. package/dist/routes/reports.route.d.ts +7 -0
  325. package/dist/routes/reports.route.d.ts.map +1 -0
  326. package/dist/routes/reports.route.js +95 -0
  327. package/dist/routes/reports.route.js.map +1 -0
  328. package/dist/routes/schema.route.d.ts +7 -0
  329. package/dist/routes/schema.route.d.ts.map +1 -0
  330. package/dist/routes/schema.route.js +1780 -0
  331. package/dist/routes/schema.route.js.map +1 -0
  332. package/dist/routes/settings.route.d.ts +7 -0
  333. package/dist/routes/settings.route.d.ts.map +1 -0
  334. package/dist/routes/settings.route.js +154 -0
  335. package/dist/routes/settings.route.js.map +1 -0
  336. package/dist/routes/templates.route.d.ts +7 -0
  337. package/dist/routes/templates.route.d.ts.map +1 -0
  338. package/dist/routes/templates.route.js +91 -0
  339. package/dist/routes/templates.route.js.map +1 -0
  340. package/dist/routes/utils.route.d.ts +7 -0
  341. package/dist/routes/utils.route.d.ts.map +1 -0
  342. package/dist/routes/utils.route.js +33 -0
  343. package/dist/routes/utils.route.js.map +1 -0
  344. package/dist/routes/workflow.route.d.ts +7 -0
  345. package/dist/routes/workflow.route.d.ts.map +1 -0
  346. package/dist/routes/workflow.route.js +787 -0
  347. package/dist/routes/workflow.route.js.map +1 -0
  348. package/dist/services/AssetsService.d.ts +39 -0
  349. package/dist/services/AssetsService.d.ts.map +1 -0
  350. package/dist/services/AssetsService.js +255 -0
  351. package/dist/services/AssetsService.js.map +1 -0
  352. package/dist/services/CacheService.d.ts +169 -0
  353. package/dist/services/CacheService.d.ts.map +1 -0
  354. package/dist/services/CacheService.js +722 -0
  355. package/dist/services/CacheService.js.map +1 -0
  356. package/dist/services/FilesService.d.ts +30 -0
  357. package/dist/services/FilesService.d.ts.map +1 -0
  358. package/dist/services/FilesService.js +268 -0
  359. package/dist/services/FilesService.js.map +1 -0
  360. package/dist/services/HooksManager.d.ts +38 -0
  361. package/dist/services/HooksManager.d.ts.map +1 -0
  362. package/dist/services/HooksManager.js +165 -0
  363. package/dist/services/HooksManager.js.map +1 -0
  364. package/dist/services/ItemsService.d.ts +273 -0
  365. package/dist/services/ItemsService.d.ts.map +1 -0
  366. package/dist/services/ItemsService.js +2458 -0
  367. package/dist/services/ItemsService.js.map +1 -0
  368. package/dist/services/MailService.d.ts +76 -0
  369. package/dist/services/MailService.d.ts.map +1 -0
  370. package/dist/services/MailService.js +585 -0
  371. package/dist/services/MailService.js.map +1 -0
  372. package/dist/services/MigrationService.d.ts +243 -0
  373. package/dist/services/MigrationService.d.ts.map +1 -0
  374. package/dist/services/MigrationService.js +914 -0
  375. package/dist/services/MigrationService.js.map +1 -0
  376. package/dist/services/NotificationService.d.ts +35 -0
  377. package/dist/services/NotificationService.d.ts.map +1 -0
  378. package/dist/services/NotificationService.js +159 -0
  379. package/dist/services/NotificationService.js.map +1 -0
  380. package/dist/services/PermissionService.d.ts +128 -0
  381. package/dist/services/PermissionService.d.ts.map +1 -0
  382. package/dist/services/PermissionService.js +373 -0
  383. package/dist/services/PermissionService.js.map +1 -0
  384. package/dist/services/PluginManager.d.ts +138 -0
  385. package/dist/services/PluginManager.d.ts.map +1 -0
  386. package/dist/services/PluginManager.js +463 -0
  387. package/dist/services/PluginManager.js.map +1 -0
  388. package/dist/services/RealtimeService.d.ts +209 -0
  389. package/dist/services/RealtimeService.d.ts.map +1 -0
  390. package/dist/services/RealtimeService.js +978 -0
  391. package/dist/services/RealtimeService.js.map +1 -0
  392. package/dist/services/ReportService.d.ts +13 -0
  393. package/dist/services/ReportService.d.ts.map +1 -0
  394. package/dist/services/ReportService.js +91 -0
  395. package/dist/services/ReportService.js.map +1 -0
  396. package/dist/services/SettingsService.d.ts +60 -0
  397. package/dist/services/SettingsService.d.ts.map +1 -0
  398. package/dist/services/SettingsService.js +474 -0
  399. package/dist/services/SettingsService.js.map +1 -0
  400. package/dist/services/SocketService.d.ts +129 -0
  401. package/dist/services/SocketService.d.ts.map +1 -0
  402. package/dist/services/SocketService.js +600 -0
  403. package/dist/services/SocketService.js.map +1 -0
  404. package/dist/services/StatsService.d.ts +10 -0
  405. package/dist/services/StatsService.d.ts.map +1 -0
  406. package/dist/services/StatsService.js +40 -0
  407. package/dist/services/StatsService.js.map +1 -0
  408. package/dist/services/StorageService.d.ts +20 -0
  409. package/dist/services/StorageService.d.ts.map +1 -0
  410. package/dist/services/StorageService.js +164 -0
  411. package/dist/services/StorageService.js.map +1 -0
  412. package/dist/services/TasksService.d.ts +74 -0
  413. package/dist/services/TasksService.d.ts.map +1 -0
  414. package/dist/services/TasksService.js +404 -0
  415. package/dist/services/TasksService.js.map +1 -0
  416. package/dist/services/WorkflowService.d.ts +305 -0
  417. package/dist/services/WorkflowService.d.ts.map +1 -0
  418. package/dist/services/WorkflowService.js +1811 -0
  419. package/dist/services/WorkflowService.js.map +1 -0
  420. package/dist/templates/logo/logo.png +0 -0
  421. package/dist/templates/mails/default.liquid +23 -0
  422. package/dist/types/aggregation.d.ts +40 -0
  423. package/dist/types/aggregation.d.ts.map +1 -0
  424. package/dist/types/aggregation.js +6 -0
  425. package/dist/types/aggregation.js.map +1 -0
  426. package/dist/types/assets.d.ts +32 -0
  427. package/dist/types/assets.d.ts.map +1 -0
  428. package/dist/types/assets.js +6 -0
  429. package/dist/types/assets.js.map +1 -0
  430. package/dist/types/auth.d.ts +50 -0
  431. package/dist/types/auth.d.ts.map +1 -0
  432. package/dist/types/auth.js +6 -0
  433. package/dist/types/auth.js.map +1 -0
  434. package/dist/types/cache.d.ts +47 -0
  435. package/dist/types/cache.d.ts.map +1 -0
  436. package/dist/types/cache.js +6 -0
  437. package/dist/types/cache.js.map +1 -0
  438. package/dist/types/database.d.ts +16 -0
  439. package/dist/types/database.d.ts.map +1 -0
  440. package/dist/types/database.js +6 -0
  441. package/dist/types/database.js.map +1 -0
  442. package/dist/types/fields.d.ts +71 -0
  443. package/dist/types/fields.d.ts.map +1 -0
  444. package/dist/types/fields.js +6 -0
  445. package/dist/types/fields.js.map +1 -0
  446. package/dist/types/files.d.ts +33 -0
  447. package/dist/types/files.d.ts.map +1 -0
  448. package/dist/types/files.js +6 -0
  449. package/dist/types/files.js.map +1 -0
  450. package/dist/types/hooks.d.ts +29 -0
  451. package/dist/types/hooks.d.ts.map +1 -0
  452. package/dist/types/hooks.js +6 -0
  453. package/dist/types/hooks.js.map +1 -0
  454. package/dist/types/import-export.d.ts +62 -0
  455. package/dist/types/import-export.d.ts.map +1 -0
  456. package/dist/types/import-export.js +6 -0
  457. package/dist/types/import-export.js.map +1 -0
  458. package/dist/types/index.d.ts +31 -0
  459. package/dist/types/index.d.ts.map +1 -0
  460. package/dist/types/index.js +58 -0
  461. package/dist/types/index.js.map +1 -0
  462. package/dist/types/mail.d.ts +34 -0
  463. package/dist/types/mail.d.ts.map +1 -0
  464. package/dist/types/mail.js +6 -0
  465. package/dist/types/mail.js.map +1 -0
  466. package/dist/types/notifications.d.ts +16 -0
  467. package/dist/types/notifications.d.ts.map +1 -0
  468. package/dist/types/notifications.js +6 -0
  469. package/dist/types/notifications.js.map +1 -0
  470. package/dist/types/plugin.d.ts +351 -0
  471. package/dist/types/plugin.d.ts.map +1 -0
  472. package/dist/types/plugin.js +8 -0
  473. package/dist/types/plugin.js.map +1 -0
  474. package/dist/types/query.d.ts +71 -0
  475. package/dist/types/query.d.ts.map +1 -0
  476. package/dist/types/query.js +6 -0
  477. package/dist/types/query.js.map +1 -0
  478. package/dist/types/relations.d.ts +111 -0
  479. package/dist/types/relations.d.ts.map +1 -0
  480. package/dist/types/relations.js +6 -0
  481. package/dist/types/relations.js.map +1 -0
  482. package/dist/types/reports.d.ts +17 -0
  483. package/dist/types/reports.d.ts.map +1 -0
  484. package/dist/types/reports.js +6 -0
  485. package/dist/types/reports.js.map +1 -0
  486. package/dist/types/schema.d.ts +26 -0
  487. package/dist/types/schema.d.ts.map +1 -0
  488. package/dist/types/schema.js +6 -0
  489. package/dist/types/schema.js.map +1 -0
  490. package/dist/types/seed.d.ts +27 -0
  491. package/dist/types/seed.d.ts.map +1 -0
  492. package/dist/types/seed.js +6 -0
  493. package/dist/types/seed.js.map +1 -0
  494. package/dist/types/services.d.ts +68 -0
  495. package/dist/types/services.d.ts.map +1 -0
  496. package/dist/types/services.js +6 -0
  497. package/dist/types/services.js.map +1 -0
  498. package/dist/types/settings.d.ts +36 -0
  499. package/dist/types/settings.d.ts.map +1 -0
  500. package/dist/types/settings.js +6 -0
  501. package/dist/types/settings.js.map +1 -0
  502. package/dist/types/sockets.d.ts +26 -0
  503. package/dist/types/sockets.d.ts.map +1 -0
  504. package/dist/types/sockets.js +6 -0
  505. package/dist/types/sockets.js.map +1 -0
  506. package/dist/types/sort.d.ts +25 -0
  507. package/dist/types/sort.d.ts.map +1 -0
  508. package/dist/types/sort.js +6 -0
  509. package/dist/types/sort.js.map +1 -0
  510. package/dist/types/spatial.d.ts +19 -0
  511. package/dist/types/spatial.d.ts.map +1 -0
  512. package/dist/types/spatial.js +6 -0
  513. package/dist/types/spatial.js.map +1 -0
  514. package/dist/types/stats.d.ts +21 -0
  515. package/dist/types/stats.d.ts.map +1 -0
  516. package/dist/types/stats.js +6 -0
  517. package/dist/types/stats.js.map +1 -0
  518. package/dist/types/storage.d.ts +19 -0
  519. package/dist/types/storage.d.ts.map +1 -0
  520. package/dist/types/storage.js +6 -0
  521. package/dist/types/storage.js.map +1 -0
  522. package/dist/types/tasks.d.ts +14 -0
  523. package/dist/types/tasks.d.ts.map +1 -0
  524. package/dist/types/tasks.js +6 -0
  525. package/dist/types/tasks.js.map +1 -0
  526. package/dist/types/utils.d.ts +54 -0
  527. package/dist/types/utils.d.ts.map +1 -0
  528. package/dist/types/utils.js +6 -0
  529. package/dist/types/utils.js.map +1 -0
  530. package/dist/types/workflow.d.ts +17 -0
  531. package/dist/types/workflow.d.ts.map +1 -0
  532. package/dist/types/workflow.js +6 -0
  533. package/dist/types/workflow.js.map +1 -0
  534. package/dist/utils/aggregationUtils.d.ts +192 -0
  535. package/dist/utils/aggregationUtils.d.ts.map +1 -0
  536. package/dist/utils/aggregationUtils.js +450 -0
  537. package/dist/utils/aggregationUtils.js.map +1 -0
  538. package/dist/utils/auth.d.ts +93 -0
  539. package/dist/utils/auth.d.ts.map +1 -0
  540. package/dist/utils/auth.js +557 -0
  541. package/dist/utils/auth.js.map +1 -0
  542. package/dist/utils/cache.d.ts +64 -0
  543. package/dist/utils/cache.d.ts.map +1 -0
  544. package/dist/utils/cache.js +464 -0
  545. package/dist/utils/cache.js.map +1 -0
  546. package/dist/utils/common.d.ts +53 -0
  547. package/dist/utils/common.d.ts.map +1 -0
  548. package/dist/utils/common.js +162 -0
  549. package/dist/utils/common.js.map +1 -0
  550. package/dist/utils/db.d.ts +101 -0
  551. package/dist/utils/db.d.ts.map +1 -0
  552. package/dist/utils/db.js +413 -0
  553. package/dist/utils/db.js.map +1 -0
  554. package/dist/utils/dirname.d.ts +30 -0
  555. package/dist/utils/dirname.d.ts.map +1 -0
  556. package/dist/utils/dirname.js +95 -0
  557. package/dist/utils/dirname.js.map +1 -0
  558. package/dist/utils/dynamicVariableResolver.d.ts +17 -0
  559. package/dist/utils/dynamicVariableResolver.d.ts.map +1 -0
  560. package/dist/utils/dynamicVariableResolver.js +262 -0
  561. package/dist/utils/dynamicVariableResolver.js.map +1 -0
  562. package/dist/utils/env.d.ts +38 -0
  563. package/dist/utils/env.d.ts.map +1 -0
  564. package/dist/utils/env.js +80 -0
  565. package/dist/utils/env.js.map +1 -0
  566. package/dist/utils/errorHandler.d.ts +14 -0
  567. package/dist/utils/errorHandler.d.ts.map +1 -0
  568. package/dist/utils/errorHandler.js +79 -0
  569. package/dist/utils/errorHandler.js.map +1 -0
  570. package/dist/utils/fieldExpansion.d.ts +30 -0
  571. package/dist/utils/fieldExpansion.d.ts.map +1 -0
  572. package/dist/utils/fieldExpansion.js +145 -0
  573. package/dist/utils/fieldExpansion.js.map +1 -0
  574. package/dist/utils/fieldUtils.d.ts +179 -0
  575. package/dist/utils/fieldUtils.d.ts.map +1 -0
  576. package/dist/utils/fieldUtils.js +424 -0
  577. package/dist/utils/fieldUtils.js.map +1 -0
  578. package/dist/utils/filterOperators.d.ts +472 -0
  579. package/dist/utils/filterOperators.d.ts.map +1 -0
  580. package/dist/utils/filterOperators.js +1229 -0
  581. package/dist/utils/filterOperators.js.map +1 -0
  582. package/dist/utils/importUtils.d.ts +127 -0
  583. package/dist/utils/importUtils.d.ts.map +1 -0
  584. package/dist/utils/importUtils.js +437 -0
  585. package/dist/utils/importUtils.js.map +1 -0
  586. package/dist/utils/index.d.ts +75 -0
  587. package/dist/utils/index.d.ts.map +1 -0
  588. package/dist/utils/index.js +101 -0
  589. package/dist/utils/index.js.map +1 -0
  590. package/dist/utils/logger.d.ts +41 -0
  591. package/dist/utils/logger.d.ts.map +1 -0
  592. package/dist/utils/logger.js +217 -0
  593. package/dist/utils/logger.js.map +1 -0
  594. package/dist/utils/orderUtils.d.ts +117 -0
  595. package/dist/utils/orderUtils.d.ts.map +1 -0
  596. package/dist/utils/orderUtils.js +249 -0
  597. package/dist/utils/orderUtils.js.map +1 -0
  598. package/dist/utils/queryBuilder.d.ts +118 -0
  599. package/dist/utils/queryBuilder.d.ts.map +1 -0
  600. package/dist/utils/queryBuilder.js +489 -0
  601. package/dist/utils/queryBuilder.js.map +1 -0
  602. package/dist/utils/relationLoader.d.ts +65 -0
  603. package/dist/utils/relationLoader.d.ts.map +1 -0
  604. package/dist/utils/relationLoader.js +1081 -0
  605. package/dist/utils/relationLoader.js.map +1 -0
  606. package/dist/utils/relationPathResolver.d.ts +30 -0
  607. package/dist/utils/relationPathResolver.d.ts.map +1 -0
  608. package/dist/utils/relationPathResolver.js +173 -0
  609. package/dist/utils/relationPathResolver.js.map +1 -0
  610. package/dist/utils/relationUtils.d.ts +139 -0
  611. package/dist/utils/relationUtils.d.ts.map +1 -0
  612. package/dist/utils/relationUtils.js +711 -0
  613. package/dist/utils/relationUtils.js.map +1 -0
  614. package/dist/utils/router.d.ts +6 -0
  615. package/dist/utils/router.d.ts.map +1 -0
  616. package/dist/utils/router.js +95 -0
  617. package/dist/utils/router.js.map +1 -0
  618. package/dist/utils/schema.d.ts +88 -0
  619. package/dist/utils/schema.d.ts.map +1 -0
  620. package/dist/utils/schema.js +24 -0
  621. package/dist/utils/schema.js.map +1 -0
  622. package/dist/utils/schemaManager.d.ts +238 -0
  623. package/dist/utils/schemaManager.d.ts.map +1 -0
  624. package/dist/utils/schemaManager.js +1992 -0
  625. package/dist/utils/schemaManager.js.map +1 -0
  626. package/dist/utils/schemaValidator.d.ts +83 -0
  627. package/dist/utils/schemaValidator.d.ts.map +1 -0
  628. package/dist/utils/schemaValidator.js +491 -0
  629. package/dist/utils/schemaValidator.js.map +1 -0
  630. package/dist/utils/seed.d.ts +45 -0
  631. package/dist/utils/seed.d.ts.map +1 -0
  632. package/dist/utils/seed.js +248 -0
  633. package/dist/utils/seed.js.map +1 -0
  634. package/dist/utils/sessionCleanup.d.ts +10 -0
  635. package/dist/utils/sessionCleanup.d.ts.map +1 -0
  636. package/dist/utils/sessionCleanup.js +49 -0
  637. package/dist/utils/sessionCleanup.js.map +1 -0
  638. package/dist/utils/sortUtils.d.ts +117 -0
  639. package/dist/utils/sortUtils.d.ts.map +1 -0
  640. package/dist/utils/sortUtils.js +232 -0
  641. package/dist/utils/sortUtils.js.map +1 -0
  642. package/dist/utils/spatialUtils.d.ts +244 -0
  643. package/dist/utils/spatialUtils.d.ts.map +1 -0
  644. package/dist/utils/spatialUtils.js +359 -0
  645. package/dist/utils/spatialUtils.js.map +1 -0
  646. package/dist/utils/systemschema.d.ts +11040 -0
  647. package/dist/utils/systemschema.d.ts.map +1 -0
  648. package/dist/utils/systemschema.js +1777 -0
  649. package/dist/utils/systemschema.js.map +1 -0
  650. package/dist/utils/tenantUtils.d.ts +34 -0
  651. package/dist/utils/tenantUtils.d.ts.map +1 -0
  652. package/dist/utils/tenantUtils.js +124 -0
  653. package/dist/utils/tenantUtils.js.map +1 -0
  654. package/dist/utils/typeMapper.d.ts +25 -0
  655. package/dist/utils/typeMapper.d.ts.map +1 -0
  656. package/dist/utils/typeMapper.js +282 -0
  657. package/dist/utils/typeMapper.js.map +1 -0
  658. package/dist/utils/valueValidator.d.ts +60 -0
  659. package/dist/utils/valueValidator.d.ts.map +1 -0
  660. package/dist/utils/valueValidator.js +303 -0
  661. package/dist/utils/valueValidator.js.map +1 -0
  662. package/dist/utils/workflow.d.ts +87 -0
  663. package/dist/utils/workflow.d.ts.map +1 -0
  664. package/dist/utils/workflow.js +205 -0
  665. package/dist/utils/workflow.js.map +1 -0
  666. package/package.json +115 -0
@@ -0,0 +1,1811 @@
1
+ import { createRequire } from 'module';
2
+ import { getCache } from "../utils/cache.js";
3
+ import ItemsService from "./ItemsService.js";
4
+ import { hooksManager } from "./HooksManager.js";
5
+ import socketService from "./SocketService.js";
6
+ import mailService from "./MailService.js";
7
+ import statsService from "./StatsService.js";
8
+ import schedule from "node-schedule";
9
+ import { schemaManager } from "../utils/schemaManager.js";
10
+ import { getProjectPath } from "../utils/dirname.js";
11
+ /**
12
+ * WorkflowService - Comprehensive workflow execution engine
13
+ * Supports: HTTP requests, data transformation, conditions, loops, service operations
14
+ */
15
+ class WorkflowService {
16
+ initialized = false;
17
+ scheduledJobs = new Map();
18
+ customModules = new Map(); // Registry for custom modules
19
+ stepProcessors;
20
+ constructor() {
21
+ this.stepProcessors = {
22
+ trigger: this.processTriggerNode.bind(this),
23
+ http: this.processHttpNode.bind(this),
24
+ transform: this.processTransformNode.bind(this),
25
+ condition: this.processConditionNode.bind(this),
26
+ service: this.processServiceNode.bind(this),
27
+ loop: this.processLoopNode.bind(this),
28
+ filter: this.processFilterNode.bind(this),
29
+ aggregate: this.processAggregateNode.bind(this),
30
+ delay: this.processDelayNode.bind(this),
31
+ notification: this.processNotificationNode.bind(this),
32
+ email: this.processEmailNode.bind(this),
33
+ workflow: this.processWorkflowNode.bind(this),
34
+ stats: this.processStatsNode.bind(this),
35
+ file: this.processFileNode.bind(this),
36
+ variable: this.processVariableNode.bind(this),
37
+ script: this.processScriptNode.bind(this),
38
+ try: this.processTryNode.bind(this),
39
+ };
40
+ }
41
+ /**
42
+ * Register a custom module that can be used in script nodes
43
+ * @param moduleName - Name of the module (used in require())
44
+ * @param moduleExport - The module export (function, object, or class)
45
+ * @param options - Optional configuration
46
+ */
47
+ registerCustomModule(moduleName, moduleExport, options = {}) {
48
+ if (!moduleName || typeof moduleName !== "string") {
49
+ throw new Error("Module name must be a non-empty string");
50
+ }
51
+ if (!moduleExport) {
52
+ throw new Error("Module export is required");
53
+ }
54
+ this.customModules.set(moduleName, {
55
+ name: moduleName,
56
+ export: moduleExport,
57
+ description: options.description || "",
58
+ allowRequire: options.allowRequire !== false, // Default to true
59
+ registeredAt: new Date(),
60
+ });
61
+ console.info(`WorkflowService: Registered custom module "${moduleName}"`);
62
+ }
63
+ /**
64
+ * Unregister a custom module
65
+ * @param moduleName - Name of the module to unregister
66
+ */
67
+ unregisterCustomModule(moduleName) {
68
+ if (this.customModules.has(moduleName)) {
69
+ this.customModules.delete(moduleName);
70
+ console.info(`WorkflowService: Unregistered custom module "${moduleName}"`);
71
+ return true;
72
+ }
73
+ return false;
74
+ }
75
+ /**
76
+ * Get all registered custom modules
77
+ * @returns List of registered modules with metadata
78
+ */
79
+ getRegisteredModules() {
80
+ return Array.from(this.customModules.values()).map((module) => ({
81
+ name: module.name,
82
+ description: module.description,
83
+ allowRequire: module.allowRequire,
84
+ registeredAt: module.registeredAt,
85
+ }));
86
+ }
87
+ /**
88
+ * Check if a module is available (built-in or custom)
89
+ * @param moduleName - Name of the module
90
+ * @returns boolean
91
+ */
92
+ isModuleAvailable(moduleName) {
93
+ // Check built-in modules
94
+ const builtInModules = [
95
+ "lodash",
96
+ "_",
97
+ "dayjs",
98
+ "axios",
99
+ "crypto",
100
+ "uuid",
101
+ "joi",
102
+ "validator",
103
+ "bcrypt",
104
+ "jsonwebtoken",
105
+ ];
106
+ if (builtInModules.includes(moduleName)) {
107
+ return true;
108
+ }
109
+ // Check custom modules
110
+ return this.customModules.has(moduleName);
111
+ }
112
+ async init() {
113
+ if (this.initialized) {
114
+ return;
115
+ }
116
+ try {
117
+ // Register hooks for workflow triggers
118
+ this.registerWorkflowHooks();
119
+ // Initialize scheduled workflows
120
+ await this.initializeScheduledWorkflows();
121
+ this.initialized = true;
122
+ console.info("WorkflowService initialized successfully");
123
+ }
124
+ catch (error) {
125
+ console.warn("WorkflowService: Initialization failed:", error.message);
126
+ }
127
+ }
128
+ async ensureInitialized() {
129
+ if (!this.initialized) {
130
+ await this.init();
131
+ }
132
+ }
133
+ /**
134
+ * Create execution record (without starting execution)
135
+ */
136
+ async createExecutionRecord(workflowId, userId = null, tenantId = null) {
137
+ await this.ensureInitialized();
138
+ const workflowService = new ItemsService("baasix_Workflow");
139
+ const executionService = new ItemsService("baasix_WorkflowExecution");
140
+ // Fetch workflow
141
+ const workflowResult = await workflowService.readOne(workflowId);
142
+ if (!workflowResult) {
143
+ throw new Error(`Workflow ${workflowId} not found`);
144
+ }
145
+ if (workflowResult.status !== "active") {
146
+ throw new Error(`Workflow ${workflowId} is not active`);
147
+ }
148
+ // Create execution record
149
+ const executionId = await executionService.createOne({
150
+ workflow_Id: workflowId,
151
+ status: "queued",
152
+ trigger_data: {},
153
+ context_data: {
154
+ variables: { ...workflowResult.variables },
155
+ trigger: {},
156
+ },
157
+ tenant_Id: tenantId || workflowResult.tenant_Id,
158
+ triggered_by_Id: userId,
159
+ });
160
+ // Read the full execution record to return
161
+ const execution = await executionService.readOne(executionId);
162
+ // Ensure execution record has 'id' property (primary key might have different name)
163
+ execution.id = executionId;
164
+ return execution;
165
+ }
166
+ /**
167
+ * Execute workflow asynchronously using existing execution record
168
+ */
169
+ async executeWorkflowAsync(workflowId, triggerData = {}, userId = null, tenantId = null, executionId = null) {
170
+ await this.ensureInitialized();
171
+ const workflowService = new ItemsService("baasix_Workflow");
172
+ try {
173
+ // Fetch workflow
174
+ const workflow = await workflowService.readOne(workflowId);
175
+ if (!workflow) {
176
+ throw new Error(`Workflow ${workflowId} not found`);
177
+ }
178
+ // Update trigger data in execution record
179
+ if (executionId) {
180
+ const executionService = new ItemsService("baasix_WorkflowExecution");
181
+ await executionService.updateOne(executionId, { trigger_data: triggerData });
182
+ }
183
+ // Execute asynchronously
184
+ await this.runWorkflowExecution(executionId, workflow, triggerData);
185
+ }
186
+ catch (error) {
187
+ console.error("Error executing workflow:", error);
188
+ throw error;
189
+ }
190
+ }
191
+ /**
192
+ * Execute a workflow (legacy method - creates record and executes)
193
+ */
194
+ async executeWorkflow(workflowId, triggerData = {}, userId = null, tenantId = null) {
195
+ await this.ensureInitialized();
196
+ const workflowService = new ItemsService("baasix_Workflow");
197
+ const executionService = new ItemsService("baasix_WorkflowExecution");
198
+ try {
199
+ // Fetch workflow
200
+ const workflow = await workflowService.readOne(workflowId);
201
+ if (!workflow) {
202
+ throw new Error(`Workflow ${workflowId} not found`);
203
+ }
204
+ if (workflow.status !== "active") {
205
+ throw new Error(`Workflow ${workflowId} is not active`);
206
+ }
207
+ // Create execution record
208
+ const executionId = await executionService.createOne({
209
+ workflow_Id: workflowId,
210
+ status: "queued",
211
+ trigger_data: triggerData,
212
+ context_data: {
213
+ variables: { ...workflow.variables },
214
+ trigger: triggerData,
215
+ },
216
+ tenant_Id: tenantId || workflow.tenant_Id,
217
+ triggered_by_Id: userId,
218
+ });
219
+ // Read the full execution record to return
220
+ const execution = await executionService.readOne(executionId);
221
+ // Ensure execution record has 'id' property (primary key might have different name)
222
+ execution.id = executionId;
223
+ // Execute asynchronously
224
+ this.runWorkflowExecution(executionId, workflow, triggerData).catch((error) => {
225
+ console.error(`Workflow execution ${executionId} failed:`, error);
226
+ });
227
+ return execution;
228
+ }
229
+ catch (error) {
230
+ console.error("Error executing workflow:", error);
231
+ throw error;
232
+ }
233
+ }
234
+ /**
235
+ * Run workflow execution
236
+ */
237
+ async runWorkflowExecution(executionId, workflow, triggerData) {
238
+ const executionService = new ItemsService("baasix_WorkflowExecution");
239
+ const logService = new ItemsService("baasix_WorkflowExecutionLog");
240
+ const startTime = Date.now();
241
+ try {
242
+ // Update execution status
243
+ await executionService.updateOne(executionId, {
244
+ status: "running",
245
+ startedAt: new Date(),
246
+ });
247
+ // Emit execution started event
248
+ socketService.emitWorkflowExecutionUpdate(executionId, {
249
+ status: "running",
250
+ });
251
+ const { nodes, edges } = workflow.flow_data;
252
+ // Initialize context with workflow variables and trigger data
253
+ const context = {
254
+ variables: { ...workflow.variables },
255
+ trigger: triggerData,
256
+ outputs: {}, // Store outputs from each node
257
+ };
258
+ // Find start node (trigger node)
259
+ const startNode = nodes.find((n) => n.type === "trigger");
260
+ if (!startNode) {
261
+ throw new Error("No trigger node found in workflow");
262
+ }
263
+ // Execute workflow graph
264
+ await this.executeNode(startNode, nodes, edges, context, executionId, logService);
265
+ // Mark execution as completed
266
+ const duration = Date.now() - startTime;
267
+ await executionService.updateOne(executionId, {
268
+ status: "completed",
269
+ completedAt: new Date(),
270
+ durationMs: duration,
271
+ result_data: context.outputs,
272
+ });
273
+ // Emit execution completed event
274
+ socketService.emitWorkflowExecutionComplete(executionId, {
275
+ status: "completed",
276
+ duration,
277
+ });
278
+ console.info(`Workflow execution ${executionId} completed in ${duration}ms`);
279
+ }
280
+ catch (error) {
281
+ const duration = Date.now() - startTime;
282
+ await executionService.updateOne(executionId, {
283
+ status: "failed",
284
+ completedAt: new Date(),
285
+ durationMs: duration,
286
+ errorMessage: error.message,
287
+ });
288
+ // Emit execution failed event
289
+ socketService.emitWorkflowExecutionComplete(executionId, {
290
+ status: "failed",
291
+ error: error.message,
292
+ });
293
+ console.error(`Workflow execution ${executionId} failed:`, error);
294
+ throw error;
295
+ }
296
+ }
297
+ /**
298
+ * Execute a single node and its descendants
299
+ */
300
+ async executeNode(node, allNodes, edges, context, executionId, LogService) {
301
+ const nodeStartTime = Date.now();
302
+ let logId;
303
+ try {
304
+ // Emit node started event
305
+ socketService.emitWorkflowExecutionUpdate(executionId, {
306
+ currentNodeId: node.id,
307
+ nodeId: node.id,
308
+ nodeStatus: "running",
309
+ inputData: {
310
+ trigger: context.trigger,
311
+ variables: context.variables,
312
+ loop: context.loop,
313
+ error: context.error,
314
+ },
315
+ });
316
+ // Create log entry
317
+ logId = await LogService.createOne({
318
+ execution_Id: executionId,
319
+ nodeId: node.id,
320
+ nodeType: node.type,
321
+ nodeLabel: node.data?.label || node.type,
322
+ status: "running",
323
+ inputData: {
324
+ trigger: context.trigger,
325
+ variables: context.variables,
326
+ loop: context.loop,
327
+ error: context.error,
328
+ },
329
+ });
330
+ // Process node based on type
331
+ const processor = this.stepProcessors[node.type];
332
+ if (!processor) {
333
+ throw new Error(`Unknown node type: ${node.type}`);
334
+ }
335
+ const result = await processor(node, context, allNodes, edges, executionId, LogService);
336
+ // Store node output in context
337
+ context.outputs[node.id] = result;
338
+ // Update log with success - but only if not already in a final state
339
+ const nodeDuration = Date.now() - nodeStartTime;
340
+ const currentLog = await LogService.readOne(logId);
341
+ if (currentLog && currentLog.status !== "success" && currentLog.status !== "failed") {
342
+ await LogService.updateOne(logId, {
343
+ status: "success",
344
+ outputData: result,
345
+ durationMs: nodeDuration,
346
+ });
347
+ // Only emit node completed event if we actually updated the status
348
+ socketService.emitWorkflowExecutionUpdate(executionId, {
349
+ nodeId: node.id,
350
+ nodeStatus: "completed",
351
+ outputData: result,
352
+ });
353
+ }
354
+ // For try/loop/condition nodes, edges are handled internally by the processor
355
+ // Don't auto-execute their edges
356
+ if (node.type === "try" || node.type === "loop" || node.type === "condition") {
357
+ return result;
358
+ }
359
+ // Find and execute next nodes
360
+ const nextEdges = edges.filter((e) => e.source === node.id);
361
+ for (const edge of nextEdges) {
362
+ // For condition nodes, check the sourceHandle (true/false branch)
363
+ if (node.type === "condition" && edge.sourceHandle) {
364
+ const conditionResult = result.conditionMet;
365
+ // Only follow the edge that matches the condition result
366
+ if (edge.sourceHandle === "true" && !conditionResult) {
367
+ continue; // Skip true branch if condition is false
368
+ }
369
+ if (edge.sourceHandle === "false" && conditionResult) {
370
+ continue; // Skip false branch if condition is true
371
+ }
372
+ }
373
+ // Check if edge has a custom condition
374
+ if (edge.data?.condition) {
375
+ const conditionMet = this.evaluateCondition(edge.data.condition, context);
376
+ if (!conditionMet) {
377
+ continue; // Skip this edge
378
+ }
379
+ }
380
+ const nextNode = allNodes.find((n) => n.id === edge.target);
381
+ if (nextNode) {
382
+ await this.executeNode(nextNode, allNodes, edges, context, executionId, LogService);
383
+ }
384
+ }
385
+ return result;
386
+ }
387
+ catch (error) {
388
+ const nodeDuration = Date.now() - nodeStartTime;
389
+ // Update the log we created earlier (using logId from creation)
390
+ try {
391
+ if (logId) {
392
+ const currentLog = await LogService.readOne(logId);
393
+ // Only update if not already in a final state (success or failed)
394
+ if (currentLog && currentLog.status !== "success" && currentLog.status !== "failed") {
395
+ await LogService.updateOne(logId, {
396
+ status: "failed",
397
+ errorMessage: error.message,
398
+ durationMs: nodeDuration,
399
+ });
400
+ }
401
+ }
402
+ }
403
+ catch (logError) {
404
+ // If log update fails, just log it but don't fail the workflow
405
+ console.warn(`Failed to update log for node ${node.id}:`, logError);
406
+ }
407
+ // Emit node failed event
408
+ socketService.emitWorkflowExecutionUpdate(executionId, {
409
+ nodeId: node.id,
410
+ nodeStatus: "failed",
411
+ error: error.message,
412
+ });
413
+ throw error;
414
+ }
415
+ }
416
+ /**
417
+ * Process trigger node
418
+ */
419
+ async processTriggerNode(_node, context) {
420
+ // Trigger node just passes data through
421
+ return context.trigger;
422
+ }
423
+ /**
424
+ * Process HTTP request node
425
+ */
426
+ async processHttpNode(node, context) {
427
+ const { url, method = "GET", headers = {}, body, timeout = 30000 } = node.data;
428
+ // Replace template variables in URL and body
429
+ const processedUrl = this.replaceTemplateVariables(url, context);
430
+ const processedHeaders = this.replaceTemplateVariables(headers, context);
431
+ const processedBody = body ? this.replaceTemplateVariables(body, context) : undefined;
432
+ try {
433
+ const controller = new AbortController();
434
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
435
+ const options = {
436
+ method: method.toUpperCase(),
437
+ headers: {
438
+ "Content-Type": "application/json",
439
+ ...processedHeaders,
440
+ },
441
+ signal: controller.signal,
442
+ };
443
+ if (processedBody && ["POST", "PUT", "PATCH"].includes(options.method)) {
444
+ options.body = typeof processedBody === "string" ? processedBody : JSON.stringify(processedBody);
445
+ }
446
+ const response = await fetch(processedUrl, options);
447
+ clearTimeout(timeoutId);
448
+ const contentType = response.headers.get("content-type");
449
+ let responseData;
450
+ if (contentType && contentType.includes("application/json")) {
451
+ responseData = await response.json();
452
+ }
453
+ else {
454
+ responseData = await response.text();
455
+ }
456
+ return {
457
+ status: response.status,
458
+ statusText: response.statusText,
459
+ headers: Object.fromEntries(response.headers.entries()),
460
+ data: responseData,
461
+ };
462
+ }
463
+ catch (error) {
464
+ throw new Error(`HTTP request failed: ${error.message}`);
465
+ }
466
+ }
467
+ /**
468
+ * Process transformation node
469
+ */
470
+ async processTransformNode(node, context) {
471
+ const { transformType, script, mapping } = node.data;
472
+ try {
473
+ if (transformType === "script") {
474
+ // Execute JavaScript transformation
475
+ return this.executeTransformScript(script, context);
476
+ }
477
+ else if (transformType === "mapping") {
478
+ // Apply field mapping
479
+ return this.applyFieldMapping(mapping, context);
480
+ }
481
+ throw new Error(`Unknown transform type: ${transformType}`);
482
+ }
483
+ catch (error) {
484
+ throw new Error(`Transform failed: ${error.message}`);
485
+ }
486
+ }
487
+ /**
488
+ * Execute JavaScript transformation script
489
+ */
490
+ executeTransformScript(script, context) {
491
+ try {
492
+ // Create a safe execution context
493
+ const func = new Function("context", "data", "trigger", "outputs", "loop", "variables", "error", script);
494
+ return func(context, context.outputs[Object.keys(context.outputs).pop() || ""], // Last output
495
+ context.trigger, context.outputs, context.loop || null, // Loop context (null if not in a loop)
496
+ context.variables || {}, context.error || null // Error context (null if not in catch block)
497
+ );
498
+ }
499
+ catch (error) {
500
+ throw new Error(`Script execution failed: ${error.message}`);
501
+ }
502
+ }
503
+ /**
504
+ * Apply field mapping transformation
505
+ */
506
+ applyFieldMapping(mapping, context) {
507
+ const result = {};
508
+ const lastOutput = context.outputs[Object.keys(context.outputs).pop() || ""];
509
+ for (const [targetField, sourceExpression] of Object.entries(mapping)) {
510
+ result[targetField] = this.evaluateExpression(sourceExpression, context, lastOutput);
511
+ }
512
+ return result;
513
+ }
514
+ /**
515
+ * Process condition node
516
+ */
517
+ async processConditionNode(node, context, allNodes, edges, executionId, LogService) {
518
+ const { conditions, operator = "AND" } = node.data;
519
+ const results = conditions.map((condition) => {
520
+ return this.evaluateCondition(condition, context);
521
+ });
522
+ let conditionMet = false;
523
+ if (operator === "AND") {
524
+ conditionMet = results.every((r) => r);
525
+ }
526
+ else if (operator === "OR") {
527
+ conditionMet = results.some((r) => r);
528
+ }
529
+ // Find edges for true and false branches
530
+ const trueEdges = edges.filter((e) => e.source === node.id && e.sourceHandle === "true");
531
+ const falseEdges = edges.filter((e) => e.source === node.id && e.sourceHandle === "false");
532
+ // Execute the appropriate branch based on condition
533
+ const branchEdges = conditionMet ? trueEdges : falseEdges;
534
+ for (const edge of branchEdges) {
535
+ const nextNode = allNodes.find((n) => n.id === edge.target);
536
+ if (nextNode) {
537
+ await this.executeConditionBranch(node.id, nextNode, allNodes, edges, context, executionId, LogService);
538
+ }
539
+ }
540
+ // After branch completes (via condition-end), execute "done" handle nodes
541
+ const doneEdges = edges.filter((e) => e.source === node.id && e.sourceHandle === "done");
542
+ for (const edge of doneEdges) {
543
+ const nextNode = allNodes.find((n) => n.id === edge.target);
544
+ if (nextNode) {
545
+ await this.executeNode(nextNode, allNodes, edges, context, executionId, LogService);
546
+ }
547
+ }
548
+ return { conditionMet, results, branch: conditionMet ? "true" : "false" };
549
+ }
550
+ /**
551
+ * Execute a condition branch
552
+ * Executes nodes until they connect back to the condition's "condition-end" handle
553
+ */
554
+ async executeConditionBranch(conditionNodeId, startNode, allNodes, edges, context, executionId, LogService) {
555
+ const executedInBranch = new Set();
556
+ const executeNodeInBranch = async (currentNode) => {
557
+ if (executedInBranch.has(currentNode.id)) {
558
+ return;
559
+ }
560
+ executedInBranch.add(currentNode.id);
561
+ // Execute the node
562
+ const result = await this.executeSingleNode(currentNode, context, executionId, LogService, allNodes, edges);
563
+ context.outputs[currentNode.id] = result;
564
+ // Find outgoing edges
565
+ const outgoingEdges = edges.filter((e) => e.source === currentNode.id);
566
+ for (const edge of outgoingEdges) {
567
+ // Check if this edge connects back to the condition node's "condition-end" handle
568
+ if (edge.target === conditionNodeId && edge.targetHandle === "condition-end") {
569
+ // Branch complete, return to condition node
570
+ return;
571
+ }
572
+ // Continue executing the branch
573
+ const nextNode = allNodes.find((n) => n.id === edge.target);
574
+ if (nextNode) {
575
+ await executeNodeInBranch(nextNode);
576
+ }
577
+ }
578
+ };
579
+ await executeNodeInBranch(startNode);
580
+ }
581
+ /**
582
+ * Evaluate a single condition
583
+ */
584
+ evaluateCondition(condition, context) {
585
+ const { field, operator, value } = condition;
586
+ const fieldValue = this.evaluateExpression(field, context);
587
+ switch (operator) {
588
+ case "equals":
589
+ case "==":
590
+ return fieldValue == value;
591
+ case "not_equals":
592
+ case "!=":
593
+ return fieldValue != value;
594
+ case "greater_than":
595
+ case ">":
596
+ return fieldValue > value;
597
+ case "less_than":
598
+ case "<":
599
+ return fieldValue < value;
600
+ case "greater_or_equal":
601
+ case ">=":
602
+ return fieldValue >= value;
603
+ case "less_or_equal":
604
+ case "<=":
605
+ return fieldValue <= value;
606
+ case "contains":
607
+ return String(fieldValue).includes(value);
608
+ case "not_contains":
609
+ return !String(fieldValue).includes(value);
610
+ case "starts_with":
611
+ return String(fieldValue).startsWith(value);
612
+ case "ends_with":
613
+ return String(fieldValue).endsWith(value);
614
+ case "is_empty":
615
+ return !fieldValue || fieldValue.length === 0;
616
+ case "is_not_empty":
617
+ return fieldValue && fieldValue.length > 0;
618
+ case "in":
619
+ return Array.isArray(value) && value.includes(fieldValue);
620
+ case "not_in":
621
+ return Array.isArray(value) && !value.includes(fieldValue);
622
+ default:
623
+ return false;
624
+ }
625
+ }
626
+ /**
627
+ * Process service operation node
628
+ */
629
+ async processServiceNode(node, context) {
630
+ const { operation, collection, itemId, data, filter, sort, limit, bypassPermissions = false, executeAsAnonymous = false, } = node.data;
631
+ const itemsService = new ItemsService(collection, {
632
+ accountability: executeAsAnonymous ? undefined : context.accountability,
633
+ });
634
+ // Parse data if it's a JSON string
635
+ let parsedData = data;
636
+ if (typeof data === "string") {
637
+ try {
638
+ parsedData = JSON.parse(data);
639
+ }
640
+ catch (e) {
641
+ // If parsing fails, keep as string
642
+ }
643
+ }
644
+ const processedData = parsedData ? this.replaceTemplateVariables(parsedData, context) : undefined;
645
+ // Parse filter if it's a JSON string
646
+ let parsedFilter = filter;
647
+ if (typeof filter === "string") {
648
+ try {
649
+ parsedFilter = JSON.parse(filter);
650
+ }
651
+ catch (e) {
652
+ // If parsing fails, keep as string
653
+ }
654
+ }
655
+ const processedFilter = parsedFilter ? this.replaceTemplateVariables(parsedFilter, context) : undefined;
656
+ try {
657
+ switch (operation) {
658
+ case "create": {
659
+ const created = await itemsService.createOne(processedData, { bypassPermissions });
660
+ return created;
661
+ }
662
+ case "read": {
663
+ if (itemId) {
664
+ const item = await itemsService.readOne(this.evaluateExpression(itemId, context), {}, bypassPermissions);
665
+ return item;
666
+ }
667
+ else {
668
+ const items = await itemsService.readByQuery({
669
+ filter: processedFilter,
670
+ sort,
671
+ limit,
672
+ }, bypassPermissions);
673
+ return items;
674
+ }
675
+ }
676
+ case "update": {
677
+ const updated = await itemsService.updateOne(this.evaluateExpression(itemId, context), processedData, { bypassPermissions });
678
+ return updated;
679
+ }
680
+ case "delete": {
681
+ const deleted = await itemsService.deleteOne(this.evaluateExpression(itemId, context), { bypassPermissions });
682
+ return deleted;
683
+ }
684
+ default:
685
+ throw new Error(`Unknown service operation: ${operation}`);
686
+ }
687
+ }
688
+ catch (error) {
689
+ throw new Error(`Service operation failed: ${error.message}`);
690
+ }
691
+ }
692
+ /**
693
+ * Process loop node
694
+ */
695
+ async processLoopNode(node, context, allNodes, edges, executionId, LogService) {
696
+ const { loopType, arraySource, startIndex = 0, endIndex, maxIterations = 1000 } = node.data;
697
+ let items = [];
698
+ if (loopType === "array") {
699
+ // Get array from context
700
+ items = this.evaluateExpression(arraySource, context);
701
+ if (!Array.isArray(items)) {
702
+ throw new Error("Loop source is not an array");
703
+ }
704
+ // Apply slice if indices specified
705
+ if (endIndex !== undefined) {
706
+ items = items.slice(startIndex, endIndex);
707
+ }
708
+ else if (startIndex > 0) {
709
+ items = items.slice(startIndex);
710
+ }
711
+ }
712
+ else if (loopType === "count") {
713
+ // Create array of indices
714
+ const count = Math.min(endIndex || maxIterations, maxIterations);
715
+ items = Array.from({ length: count }, (_, i) => i + startIndex);
716
+ }
717
+ // Limit iterations
718
+ items = items.slice(0, maxIterations);
719
+ const results = [];
720
+ // Find loop body entry edges (from "loop" source handle)
721
+ const loopBodyEdges = edges.filter((e) => e.source === node.id && e.sourceHandle === "loop");
722
+ // Execute loop iterations
723
+ for (let i = 0; i < items.length; i++) {
724
+ // Create loop iteration context
725
+ const loopContext = {
726
+ ...context,
727
+ loop: {
728
+ index: i,
729
+ item: items[i],
730
+ total: items.length,
731
+ },
732
+ };
733
+ // Execute loop body for each item
734
+ const iterationResult = await this.executeLoopIteration(node.id, loopBodyEdges, allNodes, edges, loopContext, executionId, LogService);
735
+ results.push(iterationResult);
736
+ }
737
+ // After all iterations complete, execute "done" handle nodes
738
+ const doneEdges = edges.filter((e) => e.source === node.id && e.sourceHandle === "done");
739
+ for (const edge of doneEdges) {
740
+ const nextNode = allNodes.find((n) => n.id === edge.target);
741
+ if (nextNode) {
742
+ await this.executeNode(nextNode, allNodes, edges, context, executionId, LogService);
743
+ }
744
+ }
745
+ return { items: results, count: results.length };
746
+ }
747
+ /**
748
+ * Execute a single loop iteration
749
+ * Executes nodes until they connect back to the loop's "loop-end" handle
750
+ */
751
+ async executeLoopIteration(loopNodeId, loopBodyEdges, allNodes, edges, context, executionId, LogService) {
752
+ const iterationResults = {};
753
+ // Track which nodes have been executed in this iteration
754
+ const executedInIteration = new Set();
755
+ // Internal function to execute a node and follow its chain
756
+ const executeNodeInLoop = async (currentNode) => {
757
+ // Prevent re-executing the same node in this iteration
758
+ if (executedInIteration.has(currentNode.id)) {
759
+ return;
760
+ }
761
+ executedInIteration.add(currentNode.id);
762
+ // Execute the node
763
+ const result = await this.executeSingleNode(currentNode, context, executionId, LogService, allNodes, edges);
764
+ // Store result
765
+ iterationResults[currentNode.id] = result;
766
+ context.outputs[currentNode.id] = result;
767
+ // Find outgoing edges from this node
768
+ const outgoingEdges = edges.filter((e) => e.source === currentNode.id);
769
+ for (const edge of outgoingEdges) {
770
+ // Check if this edge connects back to the loop node's "loop-end" handle
771
+ if (edge.target === loopNodeId && edge.targetHandle === "loop-end") {
772
+ // This marks the end of the loop body - don't continue execution
773
+ return;
774
+ }
775
+ // For condition nodes, check sourceHandle
776
+ if (currentNode.type === "condition" && edge.sourceHandle) {
777
+ const conditionResult = result.conditionMet;
778
+ if (edge.sourceHandle === "true" && !conditionResult) {
779
+ continue;
780
+ }
781
+ if (edge.sourceHandle === "false" && conditionResult) {
782
+ continue;
783
+ }
784
+ }
785
+ // For try-catch nodes, handle based on sourceHandle
786
+ if (currentNode.type === "try" && edge.sourceHandle) {
787
+ // Try-catch logic is handled internally by processTryNode
788
+ // We shouldn't auto-execute edges here for try nodes
789
+ continue;
790
+ }
791
+ // Continue executing the chain
792
+ const nextNode = allNodes.find((n) => n.id === edge.target);
793
+ if (nextNode && nextNode.id !== loopNodeId) {
794
+ await executeNodeInLoop(nextNode);
795
+ }
796
+ }
797
+ };
798
+ // Start execution from each loop body entry point
799
+ for (const edge of loopBodyEdges) {
800
+ const startNode = allNodes.find((n) => n.id === edge.target);
801
+ if (startNode) {
802
+ await executeNodeInLoop(startNode);
803
+ }
804
+ }
805
+ return iterationResults;
806
+ }
807
+ /**
808
+ * Execute a single node without following its edges
809
+ * Used by loop iteration to have finer control over execution flow
810
+ */
811
+ async executeSingleNode(node, context, executionId, LogService, allNodes, edges) {
812
+ const nodeStartTime = Date.now();
813
+ // Create execution log
814
+ const logId = await LogService.createOne({
815
+ execution_Id: executionId,
816
+ nodeId: node.id,
817
+ nodeType: node.type,
818
+ nodeLabel: node.data?.label || node.type,
819
+ status: "running",
820
+ inputData: {
821
+ trigger: context.trigger,
822
+ variables: context.variables,
823
+ loop: context.loop,
824
+ error: context.error,
825
+ },
826
+ });
827
+ try {
828
+ // Get the processor for this node type
829
+ const processor = this.stepProcessors[node.type];
830
+ if (!processor) {
831
+ throw new Error(`Unknown node type: ${node.type}`);
832
+ }
833
+ // Execute the node processor
834
+ // For branching nodes (condition, loop, try), pass all parameters
835
+ // For other nodes, just pass node and context
836
+ const branchingNodeTypes = ['condition', 'loop', 'try'];
837
+ let result;
838
+ if (branchingNodeTypes.includes(node.type) && allNodes && edges) {
839
+ result = await processor(node, context, allNodes, edges, executionId, LogService);
840
+ }
841
+ else {
842
+ result = await processor(node, context);
843
+ }
844
+ // Update log with success - but only if not already in a final state
845
+ const nodeDuration = Date.now() - nodeStartTime;
846
+ const currentLog = await LogService.readOne(logId);
847
+ if (currentLog && currentLog.status !== "success" && currentLog.status !== "failed") {
848
+ await LogService.updateOne(logId, {
849
+ status: "success",
850
+ outputData: result,
851
+ durationMs: nodeDuration,
852
+ });
853
+ }
854
+ return result;
855
+ }
856
+ catch (error) {
857
+ const nodeDuration = Date.now() - nodeStartTime;
858
+ // Only update if not already in a final state (success or failed)
859
+ const currentLog = await LogService.readOne(logId);
860
+ if (currentLog && currentLog.status !== "success" && currentLog.status !== "failed") {
861
+ await LogService.updateOne(logId, {
862
+ status: "failed",
863
+ errorMessage: error.message,
864
+ durationMs: nodeDuration,
865
+ });
866
+ }
867
+ throw error;
868
+ }
869
+ }
870
+ /**
871
+ * Process filter node
872
+ */
873
+ async processFilterNode(node, context) {
874
+ const { arraySource, conditions, operator = "AND" } = node.data;
875
+ const array = this.evaluateExpression(arraySource, context);
876
+ if (!Array.isArray(array)) {
877
+ throw new Error("Filter source is not an array");
878
+ }
879
+ const filtered = array.filter((item) => {
880
+ const itemContext = { ...context, current: item };
881
+ const results = conditions.map((cond) => this.evaluateCondition(cond, itemContext));
882
+ if (operator === "AND") {
883
+ return results.every((r) => r);
884
+ }
885
+ else {
886
+ return results.some((r) => r);
887
+ }
888
+ });
889
+ return { items: filtered, count: filtered.length };
890
+ }
891
+ /**
892
+ * Process aggregate node
893
+ */
894
+ async processAggregateNode(node, context) {
895
+ const { arraySource, operation, field } = node.data;
896
+ const array = this.evaluateExpression(arraySource, context);
897
+ if (!Array.isArray(array)) {
898
+ throw new Error("Aggregate source is not an array");
899
+ }
900
+ const values = field ? array.map((item) => item[field]) : array;
901
+ switch (operation) {
902
+ case "count":
903
+ return { result: array.length };
904
+ case "sum":
905
+ return { result: values.reduce((a, b) => a + (Number(b) || 0), 0) };
906
+ case "average":
907
+ return { result: values.reduce((a, b) => a + (Number(b) || 0), 0) / values.length };
908
+ case "min":
909
+ return { result: Math.min(...values) };
910
+ case "max":
911
+ return { result: Math.max(...values) };
912
+ case "first":
913
+ return { result: array[0] };
914
+ case "last":
915
+ return { result: array[array.length - 1] };
916
+ default:
917
+ throw new Error(`Unknown aggregate operation: ${operation}`);
918
+ }
919
+ }
920
+ /**
921
+ * Process delay node
922
+ */
923
+ async processDelayNode(node, context) {
924
+ const { delay = 1000 } = node.data;
925
+ const processedDelay = this.evaluateExpression(delay, context);
926
+ await new Promise((resolve) => setTimeout(resolve, Number(processedDelay)));
927
+ return { delayed: processedDelay };
928
+ }
929
+ /**
930
+ * Process notification node
931
+ */
932
+ async processNotificationNode(node, context) {
933
+ const { userId, title, message, type = "info", data, bypassPermissions = false, executeAsAnonymous = false, } = node.data;
934
+ const itemsService = new ItemsService("baasix_Notification", {
935
+ accountability: executeAsAnonymous ? undefined : context.accountability,
936
+ });
937
+ const processedUserId = this.evaluateExpression(userId, context);
938
+ const processedTitle = this.replaceTemplateVariables(title, context);
939
+ const processedMessage = this.replaceTemplateVariables(message, context);
940
+ const processedData = data ? this.replaceTemplateVariables(data, context) : {};
941
+ try {
942
+ const notification = await itemsService.createOne({
943
+ userId: processedUserId,
944
+ title: processedTitle,
945
+ message: processedMessage,
946
+ type,
947
+ data: processedData,
948
+ seen: false,
949
+ }, { bypassPermissions });
950
+ return notification;
951
+ }
952
+ catch (error) {
953
+ throw new Error(`Notification failed: ${error.message}`);
954
+ }
955
+ }
956
+ /**
957
+ * Replace template variables in a value (string, object, or array)
958
+ */
959
+ replaceTemplateVariables(value, context) {
960
+ if (typeof value === "string") {
961
+ // Replace {{variable}} syntax
962
+ return value.replace(/\{\{(.+?)\}\}/g, (match, expression) => {
963
+ const result = this.evaluateExpression(expression.trim(), context);
964
+ return result !== undefined ? result : match;
965
+ });
966
+ }
967
+ else if (Array.isArray(value)) {
968
+ return value.map((item) => this.replaceTemplateVariables(item, context));
969
+ }
970
+ else if (typeof value === "object" && value !== null) {
971
+ const result = {};
972
+ for (const [key, val] of Object.entries(value)) {
973
+ result[key] = this.replaceTemplateVariables(val, context);
974
+ }
975
+ return result;
976
+ }
977
+ return value;
978
+ }
979
+ /**
980
+ * Evaluate expression to get value from context
981
+ */
982
+ evaluateExpression(expression, context, defaultData = null) {
983
+ if (!expression)
984
+ return defaultData;
985
+ // Handle direct context references
986
+ if (typeof expression === "string") {
987
+ // Parse expression to handle both dot and bracket notation
988
+ // e.g., "outputs['service-read'].data" or "outputs.someKey.data"
989
+ const tokens = [];
990
+ let currentToken = "";
991
+ let inBracket = false;
992
+ for (let i = 0; i < expression.length; i++) {
993
+ const char = expression[i];
994
+ if (char === "[") {
995
+ if (currentToken) {
996
+ tokens.push(currentToken);
997
+ currentToken = "";
998
+ }
999
+ inBracket = true;
1000
+ }
1001
+ else if (char === "]") {
1002
+ inBracket = false;
1003
+ }
1004
+ else if (char === "." && !inBracket) {
1005
+ if (currentToken) {
1006
+ tokens.push(currentToken);
1007
+ currentToken = "";
1008
+ }
1009
+ }
1010
+ else if (char !== "'" && char !== '"') {
1011
+ currentToken += char;
1012
+ }
1013
+ }
1014
+ if (currentToken) {
1015
+ tokens.push(currentToken);
1016
+ }
1017
+ let current = tokens[0] === "trigger"
1018
+ ? context.trigger
1019
+ : tokens[0] === "outputs"
1020
+ ? context.outputs
1021
+ : tokens[0] === "variables"
1022
+ ? context.variables
1023
+ : tokens[0] === "loop"
1024
+ ? context.loop
1025
+ : tokens[0] === "current"
1026
+ ? context.current
1027
+ : defaultData;
1028
+ for (let i = 1; i < tokens.length && current; i++) {
1029
+ current = current[tokens[i]];
1030
+ }
1031
+ return current !== undefined ? current : defaultData;
1032
+ }
1033
+ return expression;
1034
+ }
1035
+ /**
1036
+ * Register hooks for workflow triggers
1037
+ */
1038
+ registerWorkflowHooks() {
1039
+ const hookAfterActions = [
1040
+ "items.read.after",
1041
+ "items.read.one.after",
1042
+ "items.create.after",
1043
+ "items.update.after",
1044
+ "items.delete.after",
1045
+ ];
1046
+ for (const action of hookAfterActions) {
1047
+ hooksManager.registerHook("*", action, async (hookData) => {
1048
+ const { collection, accountability, document, id, result, query, data, previousDocument } = hookData;
1049
+ // Trigger workflows with the hook data
1050
+ const workflowResult = await this.triggerWorkflowsByHook(collection, action, {
1051
+ collection,
1052
+ itemId: id,
1053
+ data: document || data,
1054
+ result,
1055
+ query,
1056
+ previousDocument,
1057
+ action,
1058
+ user: accountability?.user,
1059
+ tenant: accountability?.tenant,
1060
+ accountability,
1061
+ });
1062
+ // If workflow modified the data, return the modified hookData
1063
+ // Otherwise return the original hookData
1064
+ if (workflowResult && workflowResult.modifiedData) {
1065
+ return {
1066
+ ...hookData,
1067
+ ...workflowResult.modifiedData,
1068
+ };
1069
+ }
1070
+ return hookData;
1071
+ });
1072
+ }
1073
+ const hookBeforeActions = ["items.read", "items.read.one", "items.create", "items.update", "items.delete"];
1074
+ for (const action of hookBeforeActions) {
1075
+ hooksManager.registerHook("*", action, async (hookData) => {
1076
+ const { collection, accountability, data, id, query } = hookData;
1077
+ // Trigger workflows with the hook data
1078
+ const workflowResult = await this.triggerWorkflowsByHook(collection, action, {
1079
+ collection,
1080
+ itemId: id,
1081
+ data,
1082
+ query,
1083
+ action,
1084
+ user: accountability?.user,
1085
+ tenant: accountability?.tenant,
1086
+ accountability,
1087
+ });
1088
+ // If workflow modified the data, return the modified hookData
1089
+ // Otherwise return the original hookData
1090
+ if (workflowResult && workflowResult.modifiedData) {
1091
+ return {
1092
+ ...hookData,
1093
+ ...workflowResult.modifiedData,
1094
+ };
1095
+ }
1096
+ return hookData;
1097
+ });
1098
+ }
1099
+ console.info("WorkflowService: Registered workflow trigger hooks");
1100
+ }
1101
+ /**
1102
+ * Trigger workflows by hook event
1103
+ * @returns Object with modifiedData if workflows modified the data, null otherwise
1104
+ */
1105
+ async triggerWorkflowsByHook(collection, action, data) {
1106
+ try {
1107
+ // Skip workflow triggers for system tables to prevent infinite recursion
1108
+ // When workflow hooks try to read baasix_Workflow table, it would trigger hooks again
1109
+ if (collection.startsWith("baasix_")) {
1110
+ return null;
1111
+ }
1112
+ const workflowService = new ItemsService("baasix_Workflow");
1113
+ const workflowsResult = await workflowService.readByQuery({
1114
+ filter: {
1115
+ status: "active",
1116
+ trigger_type: "hook",
1117
+ trigger_hook_collection: collection,
1118
+ trigger_hook_action: action,
1119
+ },
1120
+ });
1121
+ const workflows = workflowsResult.data || [];
1122
+ // Track if any workflow modified the data
1123
+ let modifiedData = null;
1124
+ for (const workflow of workflows) {
1125
+ // Check role-based access for hook workflows
1126
+ if (workflow.allowed_roles && workflow.allowed_roles.length > 0) {
1127
+ // Get user's role from accountability
1128
+ const userRole = data.accountability?.role;
1129
+ // If no user role, skip this workflow
1130
+ if (!userRole) {
1131
+ console.log(`⏭️ Skipping workflow ${workflow.name} (${workflow.id}) - no user role and role restrictions apply`);
1132
+ continue;
1133
+ }
1134
+ // Check if user's role is in the allowed_roles array
1135
+ const hasAccess = workflow.allowed_roles.includes(userRole);
1136
+ if (!hasAccess) {
1137
+ console.log(`⏭️ Skipping workflow ${workflow.name} (${workflow.id}) - user does not have required role`);
1138
+ continue;
1139
+ }
1140
+ }
1141
+ console.log(`🚀 Triggering workflow: ${workflow.name} (${workflow.id})`);
1142
+ // For "before" hooks, execute synchronously to allow data modification
1143
+ // For "after" hooks, execute asynchronously
1144
+ if (action.indexOf(".after") === -1) {
1145
+ // This is a "before" hook - execute synchronously
1146
+ try {
1147
+ const executionService = new ItemsService("baasix_WorkflowExecution");
1148
+ // Create execution record
1149
+ const executionId = await executionService.createOne({
1150
+ workflow_Id: workflow.id,
1151
+ status: "queued",
1152
+ trigger_data: data,
1153
+ context_data: {
1154
+ variables: { ...workflow.variables },
1155
+ trigger: data,
1156
+ },
1157
+ tenant_Id: data.tenant?.id || workflow.tenant_Id,
1158
+ triggered_by_Id: data.user?.id,
1159
+ });
1160
+ // Execute synchronously and wait for result
1161
+ await this.runWorkflowExecution(executionId, workflow, data);
1162
+ // Get the completed execution with results
1163
+ const completedExecution = await executionService.readOne(executionId);
1164
+ if (completedExecution.status === "completed" && completedExecution.result_data) {
1165
+ modifiedData = modifiedData || {};
1166
+ // Iterate through all node outputs to find hook modifications
1167
+ // Priority: later nodes override earlier ones
1168
+ const nodeEntries = Object.entries(completedExecution.result_data);
1169
+ for (const [nodeId, nodeOutput] of nodeEntries) {
1170
+ // Skip trigger nodes as they just pass through original data
1171
+ if (nodeId.startsWith("trigger-"))
1172
+ continue;
1173
+ if (!nodeOutput || typeof nodeOutput !== "object")
1174
+ continue;
1175
+ // Script nodes wrap their return value in a 'result' key
1176
+ // So if nodeOutput has a 'result' key, extract modifications from it
1177
+ const output = nodeOutput.result || nodeOutput;
1178
+ // Check for specific modification keys in the output
1179
+ // Workflows can return: document, data, query to modify hook data
1180
+ if (output.document !== undefined) {
1181
+ modifiedData.document = output.document;
1182
+ }
1183
+ if (output.data !== undefined) {
1184
+ modifiedData.data = output.data;
1185
+ }
1186
+ if (output.query !== undefined) {
1187
+ modifiedData.query = output.query;
1188
+ }
1189
+ }
1190
+ }
1191
+ else if (completedExecution.status === "failed") {
1192
+ console.error(`❌ Workflow ${workflow.name} failed:`, completedExecution.errorMessage);
1193
+ // For before hooks, throw the error to prevent the operation
1194
+ throw new Error(completedExecution.errorMessage || "Workflow execution failed");
1195
+ }
1196
+ }
1197
+ catch (error) {
1198
+ console.error(`Error executing before hook workflow ${workflow.name}:`, error);
1199
+ // Re-throw the error so it propagates to the operation
1200
+ throw error;
1201
+ }
1202
+ }
1203
+ else {
1204
+ // This is an "after" hook - execute asynchronously (fire and forget)
1205
+ this.executeWorkflow(workflow.id, data, data.user?.id, data.tenant?.id).catch((error) => {
1206
+ console.error(`Error executing after hook workflow ${workflow.name}:`, error);
1207
+ });
1208
+ }
1209
+ }
1210
+ // Return modified data if any workflow made changes
1211
+ return modifiedData ? { modifiedData } : null;
1212
+ }
1213
+ catch (error) {
1214
+ console.error("Error triggering workflows by hook:", error);
1215
+ return null;
1216
+ }
1217
+ }
1218
+ /**
1219
+ * Initialize scheduled workflows
1220
+ */
1221
+ async initializeScheduledWorkflows() {
1222
+ try {
1223
+ // Check if baasix_Workflow table exists before trying to query it
1224
+ // This prevents errors during initial setup or testing
1225
+ try {
1226
+ schemaManager.getTable("baasix_Workflow");
1227
+ }
1228
+ catch (error) {
1229
+ console.info("WorkflowService: baasix_Workflow table not found, skipping scheduled workflow initialization");
1230
+ return;
1231
+ }
1232
+ const workflowService = new ItemsService("baasix_Workflow");
1233
+ const scheduledWorkflowsResult = await workflowService.readByQuery({
1234
+ filter: {
1235
+ status: "active",
1236
+ trigger_type: "schedule",
1237
+ },
1238
+ });
1239
+ const scheduledWorkflows = scheduledWorkflowsResult.data || [];
1240
+ for (const workflow of scheduledWorkflows) {
1241
+ this.scheduleWorkflow(workflow);
1242
+ }
1243
+ console.info(`WorkflowService: Initialized ${scheduledWorkflows.length} scheduled workflows`);
1244
+ }
1245
+ catch (error) {
1246
+ console.error("Error initializing scheduled workflows:", error);
1247
+ }
1248
+ }
1249
+ /**
1250
+ * Try to acquire distributed lock for scheduled workflow execution
1251
+ * @param workflowId - Workflow ID
1252
+ * @param lockTimeout - Lock timeout in seconds (default: 300 = 5 minutes)
1253
+ * @returns True if lock acquired, false otherwise
1254
+ */
1255
+ async tryAcquireScheduledWorkflowLock(workflowId, lockTimeout = 300) {
1256
+ const cache = getCache();
1257
+ const lockKey = `workflow:schedule:lock:${workflowId}`;
1258
+ const lockValue = `${process.pid}-${Date.now()}`;
1259
+ try {
1260
+ const acquired = await cache.setIfNotExists(lockKey, lockValue, lockTimeout);
1261
+ if (acquired) {
1262
+ console.info(`✓ Acquired scheduled workflow lock for: ${workflowId}`);
1263
+ }
1264
+ return acquired;
1265
+ }
1266
+ catch (error) {
1267
+ console.error(`Error acquiring scheduled workflow lock for ${workflowId}:`, error);
1268
+ return false;
1269
+ }
1270
+ }
1271
+ /**
1272
+ * Release distributed lock for scheduled workflow
1273
+ * @param workflowId - Workflow ID
1274
+ */
1275
+ async releaseScheduledWorkflowLock(workflowId) {
1276
+ const cache = getCache();
1277
+ const lockKey = `workflow:schedule:lock:${workflowId}`;
1278
+ try {
1279
+ await cache.delete(lockKey);
1280
+ console.info(`✓ Released scheduled workflow lock for: ${workflowId}`);
1281
+ }
1282
+ catch (error) {
1283
+ console.error(`Error releasing scheduled workflow lock for ${workflowId}:`, error);
1284
+ }
1285
+ }
1286
+ /**
1287
+ * Schedule a workflow
1288
+ */
1289
+ scheduleWorkflow(workflow) {
1290
+ const cron = workflow.trigger_cron;
1291
+ if (!cron) {
1292
+ console.warn(`Workflow ${workflow.id} has no cron configuration`);
1293
+ return;
1294
+ }
1295
+ try {
1296
+ // Cancel existing job if any
1297
+ if (this.scheduledJobs.has(workflow.id)) {
1298
+ this.scheduledJobs.get(workflow.id)?.cancel();
1299
+ }
1300
+ // Schedule new job with distributed locking
1301
+ const job = schedule.scheduleJob(cron, async () => {
1302
+ console.info(`⏰ Cron triggered for workflow ${workflow.id}`);
1303
+ // Try to acquire distributed lock (multi-instance safe)
1304
+ const lockAcquired = await this.tryAcquireScheduledWorkflowLock(workflow.id);
1305
+ if (!lockAcquired) {
1306
+ console.info(`⏭️ Skipping workflow ${workflow.id} - another instance is processing it`);
1307
+ return;
1308
+ }
1309
+ try {
1310
+ console.info(`🚀 Executing scheduled workflow ${workflow.id}`);
1311
+ await this.executeWorkflow(workflow.id, { scheduledAt: new Date() });
1312
+ }
1313
+ finally {
1314
+ // Always release lock when done
1315
+ await this.releaseScheduledWorkflowLock(workflow.id);
1316
+ }
1317
+ });
1318
+ if (job) {
1319
+ this.scheduledJobs.set(workflow.id, job);
1320
+ console.info(`Scheduled workflow ${workflow.id} with cron: ${cron}`);
1321
+ }
1322
+ }
1323
+ catch (error) {
1324
+ console.error(`Error scheduling workflow ${workflow.id}:`, error);
1325
+ }
1326
+ }
1327
+ /**
1328
+ * Cancel a scheduled workflow
1329
+ */
1330
+ cancelScheduledWorkflow(workflowId) {
1331
+ if (this.scheduledJobs.has(workflowId)) {
1332
+ this.scheduledJobs.get(workflowId)?.cancel();
1333
+ this.scheduledJobs.delete(workflowId);
1334
+ console.info(`Cancelled scheduled workflow ${workflowId}`);
1335
+ }
1336
+ }
1337
+ /**
1338
+ * Process email node - Send email using MailService
1339
+ */
1340
+ async processEmailNode(node, context) {
1341
+ const config = node.data || {};
1342
+ // Replace template variables
1343
+ const to = this.replaceTemplateVariables(config.to || "", context);
1344
+ const subject = this.replaceTemplateVariables(config.subject || "", context);
1345
+ const body = this.replaceTemplateVariables(config.body || "", context);
1346
+ const from = this.replaceTemplateVariables(config.from || "", context);
1347
+ // Parse recipients (can be comma-separated)
1348
+ const recipients = to
1349
+ .split(",")
1350
+ .map((email) => email.trim())
1351
+ .filter(Boolean);
1352
+ if (recipients.length === 0) {
1353
+ throw new Error("Email node: No recipients specified");
1354
+ }
1355
+ if (!subject || !body) {
1356
+ throw new Error("Email node: Subject and body are required");
1357
+ }
1358
+ // Send email to each recipient
1359
+ const results = [];
1360
+ for (const recipient of recipients) {
1361
+ try {
1362
+ await mailService.sendMail({
1363
+ to: recipient,
1364
+ from: from || undefined,
1365
+ subject: subject,
1366
+ html: body,
1367
+ });
1368
+ results.push({ recipient, status: "sent" });
1369
+ }
1370
+ catch (error) {
1371
+ results.push({ recipient, status: "failed", error: error.message });
1372
+ }
1373
+ }
1374
+ return {
1375
+ success: true,
1376
+ sent: results.filter((r) => r.status === "sent").length,
1377
+ failed: results.filter((r) => r.status === "failed").length,
1378
+ results,
1379
+ };
1380
+ }
1381
+ /**
1382
+ * Process workflow node - Execute another workflow and return its result
1383
+ */
1384
+ async processWorkflowNode(node, context) {
1385
+ const config = node.data || {};
1386
+ if (!config.workflowId) {
1387
+ throw new Error("Workflow node: workflowId is required");
1388
+ }
1389
+ // Prepare trigger data for the child workflow
1390
+ let triggerData = {};
1391
+ if (config.passData) {
1392
+ // Pass specific data or entire context
1393
+ if (config.dataMapping) {
1394
+ // Map specific fields
1395
+ for (const [key, value] of Object.entries(config.dataMapping)) {
1396
+ triggerData[key] = this.replaceTemplateVariables(value, context);
1397
+ }
1398
+ }
1399
+ else {
1400
+ // Pass entire context
1401
+ triggerData = { ...context };
1402
+ }
1403
+ }
1404
+ // Execute the child workflow
1405
+ const childExecution = await this.executeWorkflow(config.workflowId, triggerData, context.accountability?.user?.id, context.accountability?.tenant?.id);
1406
+ // Wait for completion if configured
1407
+ if (config.waitForCompletion !== false) {
1408
+ // Poll for completion (with timeout)
1409
+ const maxWaitTime = config.timeout || 300000; // 5 minutes default
1410
+ const startTime = Date.now();
1411
+ const pollInterval = 1000; // 1 second
1412
+ const executionService = new ItemsService("baasix_WorkflowExecution");
1413
+ while (Date.now() - startTime < maxWaitTime) {
1414
+ const execution = await executionService.readOne(childExecution.id);
1415
+ if (execution.status === "completed") {
1416
+ return {
1417
+ success: true,
1418
+ executionId: childExecution.id,
1419
+ status: "completed",
1420
+ result: execution.result,
1421
+ };
1422
+ }
1423
+ else if (execution.status === "failed") {
1424
+ throw new Error(`Child workflow failed: ${execution.errorMessage}`);
1425
+ }
1426
+ else if (execution.status === "cancelled") {
1427
+ throw new Error("Child workflow was cancelled");
1428
+ }
1429
+ // Wait before next poll
1430
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
1431
+ }
1432
+ throw new Error("Child workflow execution timed out");
1433
+ }
1434
+ // Don't wait - return execution ID
1435
+ return {
1436
+ success: true,
1437
+ executionId: childExecution.id,
1438
+ status: "started",
1439
+ async: true,
1440
+ };
1441
+ }
1442
+ /**
1443
+ * Process stats node - Generate statistics using StatsService
1444
+ */
1445
+ async processStatsNode(node, context) {
1446
+ const config = node.data || {};
1447
+ if (!config.collection) {
1448
+ throw new Error("Stats node: collection is required");
1449
+ }
1450
+ // Use StatsService
1451
+ const collection = this.replaceTemplateVariables(config.collection, context);
1452
+ const groupBy = config.groupBy ? this.replaceTemplateVariables(config.groupBy, context) : null;
1453
+ const operation = config.operation || "count"; // count, sum, avg, min, max
1454
+ // Build filter from template variables
1455
+ let filter = {};
1456
+ if (config.filter) {
1457
+ filter = JSON.parse(this.replaceTemplateVariables(JSON.stringify(config.filter), context));
1458
+ }
1459
+ // Get stats
1460
+ const stats = await statsService.getStats({
1461
+ collection,
1462
+ groupBy: groupBy ? [groupBy] : [],
1463
+ aggregate: [
1464
+ {
1465
+ operation,
1466
+ field: config.field || "id",
1467
+ },
1468
+ ],
1469
+ filter,
1470
+ });
1471
+ return {
1472
+ success: true,
1473
+ collection,
1474
+ operation,
1475
+ stats,
1476
+ };
1477
+ }
1478
+ /**
1479
+ * Process file node - File operations using FilesService
1480
+ */
1481
+ async processFileNode(node, context) {
1482
+ const config = node.data || {};
1483
+ const operation = config.operation;
1484
+ const bypassPermissions = config.bypassPermissions || false;
1485
+ const executeAsAnonymous = config.executeAsAnonymous || false;
1486
+ if (!operation) {
1487
+ throw new Error("File node: operation is required");
1488
+ }
1489
+ const itemsService = new ItemsService("baasix_File", {
1490
+ accountability: executeAsAnonymous ? undefined : context.accountability,
1491
+ });
1492
+ switch (operation) {
1493
+ case "info": {
1494
+ const fileId = this.replaceTemplateVariables(config.fileId || "", context);
1495
+ if (!fileId) {
1496
+ throw new Error("File node: fileId is required for info operation");
1497
+ }
1498
+ const fileInfo = await itemsService.readOne(fileId, {}, bypassPermissions);
1499
+ return {
1500
+ success: true,
1501
+ operation: "info",
1502
+ file: fileInfo,
1503
+ };
1504
+ }
1505
+ case "delete": {
1506
+ const deleteFileId = this.replaceTemplateVariables(config.fileId || "", context);
1507
+ if (!deleteFileId) {
1508
+ throw new Error("File node: fileId is required for delete operation");
1509
+ }
1510
+ await itemsService.deleteOne(deleteFileId, { bypassPermissions });
1511
+ return {
1512
+ success: true,
1513
+ operation: "delete",
1514
+ fileId: deleteFileId,
1515
+ };
1516
+ }
1517
+ case "list": {
1518
+ const query = {
1519
+ limit: config.limit || 100,
1520
+ };
1521
+ if (config.filter) {
1522
+ query.filter = JSON.parse(this.replaceTemplateVariables(JSON.stringify(config.filter), context));
1523
+ }
1524
+ const files = await itemsService.readByQuery(query, bypassPermissions);
1525
+ return {
1526
+ success: true,
1527
+ operation: "list",
1528
+ files: files.data,
1529
+ total: files.meta?.total || files.data.length,
1530
+ };
1531
+ }
1532
+ default:
1533
+ throw new Error(`File node: unknown operation "${operation}"`);
1534
+ }
1535
+ }
1536
+ /**
1537
+ * Process variable node - Set workflow variables
1538
+ */
1539
+ async processVariableNode(node, context) {
1540
+ const config = node.data || {};
1541
+ if (!config.variables || typeof config.variables !== "object") {
1542
+ throw new Error("Variable node: variables object is required");
1543
+ }
1544
+ // Set variables in context
1545
+ if (!context.variables) {
1546
+ context.variables = {};
1547
+ }
1548
+ // Process each variable with template replacement
1549
+ const setVariables = {};
1550
+ for (const [key, value] of Object.entries(config.variables)) {
1551
+ const resolvedValue = this.replaceTemplateVariables(String(value), context);
1552
+ context.variables[key] = resolvedValue;
1553
+ setVariables[key] = resolvedValue;
1554
+ }
1555
+ return {
1556
+ success: true,
1557
+ variables: setVariables,
1558
+ };
1559
+ }
1560
+ /**
1561
+ * Process script node - Execute custom JavaScript
1562
+ */
1563
+ async processScriptNode(node, context) {
1564
+ const config = node.data || {};
1565
+ if (!config.script) {
1566
+ throw new Error("Script node: script code is required");
1567
+ }
1568
+ // Create require function for ES module compatibility
1569
+ // Use a file path that works in both Jest and production
1570
+ const require = createRequire(getProjectPath('package.json'));
1571
+ // Import commonly used libraries
1572
+ const lodash = require("lodash");
1573
+ const dayjs = require("dayjs");
1574
+ const axios = require("axios");
1575
+ // Create safe execution context with available libraries
1576
+ const sandbox = {
1577
+ // Workflow context
1578
+ context: context,
1579
+ trigger: context.trigger,
1580
+ outputs: context.outputs,
1581
+ variables: context.variables,
1582
+ loop: context.loop, // Loop iteration context (if inside a loop)
1583
+ // Built-in JavaScript
1584
+ console: console,
1585
+ JSON: JSON,
1586
+ Math: Math,
1587
+ Date: Date,
1588
+ String: String,
1589
+ Number: Number,
1590
+ Boolean: Boolean,
1591
+ Array: Array,
1592
+ Object: Object,
1593
+ Promise: Promise,
1594
+ setTimeout: setTimeout,
1595
+ setInterval: setInterval,
1596
+ clearTimeout: clearTimeout,
1597
+ clearInterval: clearInterval,
1598
+ // Utility libraries
1599
+ _: lodash, // Lodash for array/object manipulation
1600
+ lodash: lodash, // Alternative name
1601
+ dayjs: dayjs, // Date manipulation
1602
+ axios: axios, // HTTP requests
1603
+ // Helper to dynamically require other modules
1604
+ require: (moduleName) => {
1605
+ // Check if it's a custom registered module
1606
+ if (this.customModules.has(moduleName)) {
1607
+ const customModule = this.customModules.get(moduleName);
1608
+ if (customModule.allowRequire) {
1609
+ return customModule.export;
1610
+ }
1611
+ else {
1612
+ throw new Error(`Custom module "${moduleName}" is registered but require() is disabled for it`);
1613
+ }
1614
+ }
1615
+ // Whitelist of allowed built-in modules for security
1616
+ const allowedModules = [
1617
+ "lodash",
1618
+ "dayjs",
1619
+ "axios",
1620
+ "crypto",
1621
+ "uuid",
1622
+ "joi",
1623
+ "validator",
1624
+ "bcrypt",
1625
+ "jsonwebtoken",
1626
+ ];
1627
+ if (!allowedModules.includes(moduleName)) {
1628
+ const customModuleNames = Array.from(this.customModules.keys());
1629
+ const availableModules = [...allowedModules, ...customModuleNames];
1630
+ throw new Error(`Module "${moduleName}" is not allowed. Available modules: ${availableModules.join(", ")}`);
1631
+ }
1632
+ try {
1633
+ return require(moduleName);
1634
+ }
1635
+ catch (error) {
1636
+ throw new Error(`Failed to require module "${moduleName}": ${error.message}`);
1637
+ }
1638
+ },
1639
+ };
1640
+ // Add custom modules directly to sandbox if allowRequire is true
1641
+ for (const [moduleName, moduleInfo] of this.customModules.entries()) {
1642
+ if (moduleInfo.allowRequire) {
1643
+ // Make custom modules available as direct variables in the sandbox
1644
+ // e.g., if module name is "myUtils", it's available as both myUtils and via require('myUtils')
1645
+ sandbox[moduleName] = moduleInfo.export;
1646
+ }
1647
+ }
1648
+ try {
1649
+ // Execute script in sandboxed context
1650
+ const AsyncFunction = Object.getPrototypeOf(async function () { }).constructor;
1651
+ const func = new AsyncFunction(...Object.keys(sandbox), config.script);
1652
+ const result = await func(...Object.values(sandbox));
1653
+ return {
1654
+ success: true,
1655
+ result: result,
1656
+ };
1657
+ }
1658
+ catch (error) {
1659
+ throw new Error(`Script execution error: ${error.message}`);
1660
+ }
1661
+ }
1662
+ /**
1663
+ * Process try-catch node - Error handling
1664
+ */
1665
+ async processTryNode(node, context, allNodes, edges, executionId, LogService) {
1666
+ // Find try and catch edges
1667
+ const tryEdges = edges.filter((e) => e.source === node.id && e.sourceHandle === "try");
1668
+ const catchEdges = edges.filter((e) => e.source === node.id && e.sourceHandle === "catch");
1669
+ let executedBranch = "try";
1670
+ try {
1671
+ // Execute try branch
1672
+ for (const edge of tryEdges) {
1673
+ const nextNode = allNodes.find((n) => n.id === edge.target);
1674
+ if (nextNode) {
1675
+ await this.executeTryBranch(node.id, nextNode, allNodes, edges, context, executionId, LogService);
1676
+ }
1677
+ }
1678
+ }
1679
+ catch (error) {
1680
+ executedBranch = "catch";
1681
+ // Store error in context for catch branch
1682
+ context.error = {
1683
+ message: error.message,
1684
+ stack: error.stack,
1685
+ nodeId: node.id,
1686
+ };
1687
+ // Execute catch branch
1688
+ for (const edge of catchEdges) {
1689
+ const nextNode = allNodes.find((n) => n.id === edge.target);
1690
+ if (nextNode) {
1691
+ await this.executeTryBranch(node.id, nextNode, allNodes, edges, context, executionId, LogService);
1692
+ }
1693
+ }
1694
+ }
1695
+ // After branch completes (via try-end), execute "done" handle nodes
1696
+ const doneEdges = edges.filter((e) => e.source === node.id && e.sourceHandle === "done");
1697
+ for (const edge of doneEdges) {
1698
+ const nextNode = allNodes.find((n) => n.id === edge.target);
1699
+ if (nextNode) {
1700
+ await this.executeNode(nextNode, allNodes, edges, context, executionId, LogService);
1701
+ }
1702
+ }
1703
+ return {
1704
+ success: true,
1705
+ branch: executedBranch,
1706
+ error: context.error || null,
1707
+ };
1708
+ }
1709
+ /**
1710
+ * Execute a try/catch branch
1711
+ * Executes nodes until they connect back to the try node's "try-end" handle
1712
+ */
1713
+ async executeTryBranch(tryNodeId, startNode, allNodes, edges, context, executionId, LogService) {
1714
+ const executedInBranch = new Set();
1715
+ const executeNodeInBranch = async (currentNode) => {
1716
+ if (executedInBranch.has(currentNode.id)) {
1717
+ return;
1718
+ }
1719
+ executedInBranch.add(currentNode.id);
1720
+ // Execute the node
1721
+ const result = await this.executeSingleNode(currentNode, context, executionId, LogService, allNodes, edges);
1722
+ context.outputs[currentNode.id] = result;
1723
+ // Find outgoing edges
1724
+ const outgoingEdges = edges.filter((e) => e.source === currentNode.id);
1725
+ for (const edge of outgoingEdges) {
1726
+ // Check if this edge connects back to the try node's "try-end" handle
1727
+ if (edge.target === tryNodeId && edge.targetHandle === "try-end") {
1728
+ // Branch complete, return to try node
1729
+ return;
1730
+ }
1731
+ // Continue executing the branch
1732
+ const nextNode = allNodes.find((n) => n.id === edge.target);
1733
+ if (nextNode) {
1734
+ await executeNodeInBranch(nextNode);
1735
+ }
1736
+ }
1737
+ };
1738
+ await executeNodeInBranch(startNode);
1739
+ }
1740
+ /**
1741
+ * Execute a single node (for testing individual steps)
1742
+ */
1743
+ async executeSingleNodeFromAPI(workflowId, nodeId, inputData, userId, tenantId, workflowData = null) {
1744
+ await this.ensureInitialized();
1745
+ const workflowService = new ItemsService("baasix_Workflow");
1746
+ try {
1747
+ // Use provided workflow data or fetch it
1748
+ const workflow = workflowData || (await workflowService.readOne(workflowId));
1749
+ if (!workflow) {
1750
+ throw new Error(`Workflow ${workflowId} not found`);
1751
+ }
1752
+ // Find the node to execute
1753
+ const { nodes, edges } = workflow.flow_data;
1754
+ const node = nodes.find((n) => n.id === nodeId);
1755
+ if (!node) {
1756
+ throw new Error(`Node ${nodeId} not found in workflow`);
1757
+ }
1758
+ // Create minimal context with provided input data
1759
+ const context = {
1760
+ variables: { ...workflow.variables },
1761
+ trigger: inputData.trigger || {},
1762
+ outputs: inputData.outputs || {},
1763
+ loop: inputData.loop || null,
1764
+ error: inputData.error || null,
1765
+ };
1766
+ // Process the node
1767
+ const processor = this.stepProcessors[node.type];
1768
+ if (!processor) {
1769
+ throw new Error(`Unknown node type: ${node.type}`);
1770
+ }
1771
+ const result = await processor(node, context, nodes, edges, null, null);
1772
+ return {
1773
+ success: true,
1774
+ nodeId,
1775
+ nodeType: node.type,
1776
+ input: {
1777
+ trigger: context.trigger,
1778
+ variables: context.variables,
1779
+ loop: context.loop,
1780
+ error: context.error,
1781
+ },
1782
+ output: result,
1783
+ workflow: workflow, // Return workflow for route to use
1784
+ };
1785
+ }
1786
+ catch (error) {
1787
+ console.error(`Single node execution ${nodeId} failed:`, error);
1788
+ throw error;
1789
+ }
1790
+ }
1791
+ /**
1792
+ * Graceful shutdown
1793
+ */
1794
+ async shutdown() {
1795
+ console.info("WorkflowService: Starting graceful shutdown...");
1796
+ // Cancel all scheduled jobs
1797
+ for (const [workflowId, job] of this.scheduledJobs) {
1798
+ job.cancel();
1799
+ console.info(`Cancelled scheduled workflow ${workflowId}`);
1800
+ }
1801
+ this.scheduledJobs.clear();
1802
+ console.info("WorkflowService: Shutdown completed");
1803
+ }
1804
+ }
1805
+ // Create singleton instance only if it doesn't exist
1806
+ if (!globalThis.__baasix_workflowService) {
1807
+ globalThis.__baasix_workflowService = new WorkflowService();
1808
+ }
1809
+ const workflowService = globalThis.__baasix_workflowService;
1810
+ export default workflowService;
1811
+ //# sourceMappingURL=WorkflowService.js.map