@rytass/bpm-core-nestjs-module 0.1.3 → 0.1.5

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 (612) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/LICENSE +21 -0
  3. package/README.md +143 -0
  4. package/package.json +3 -3
  5. package/src/index.js +18 -0
  6. package/src/index.js.map +1 -0
  7. package/src/lib/attachment/attachment-options.d.ts +44 -0
  8. package/src/lib/attachment/attachment-options.js +62 -0
  9. package/src/lib/attachment/attachment-options.js.map +1 -0
  10. package/src/lib/attachment/attachment-storage.provider.d.ts +4 -0
  11. package/src/lib/attachment/attachment-storage.provider.js +35 -0
  12. package/src/lib/attachment/attachment-storage.provider.js.map +1 -0
  13. package/src/lib/attachment/{attachment-storage.token.ts → attachment-storage.token.d.ts} +1 -3
  14. package/src/lib/attachment/attachment-storage.token.js +5 -0
  15. package/src/lib/attachment/attachment-storage.token.js.map +1 -0
  16. package/src/lib/attachment/attachment.controller.d.ts +8 -0
  17. package/src/lib/attachment/attachment.controller.js +38 -0
  18. package/src/lib/attachment/attachment.controller.js.map +1 -0
  19. package/src/lib/attachment/attachment.entity.d.ts +14 -0
  20. package/src/lib/attachment/attachment.entity.js +74 -0
  21. package/src/lib/attachment/attachment.entity.js.map +1 -0
  22. package/src/lib/attachment/attachment.module.d.ts +27 -0
  23. package/src/lib/attachment/attachment.module.js +103 -0
  24. package/src/lib/attachment/attachment.module.js.map +1 -0
  25. package/src/lib/attachment/attachment.mutations.d.ts +8 -0
  26. package/src/lib/attachment/attachment.mutations.js +35 -0
  27. package/src/lib/attachment/attachment.mutations.js.map +1 -0
  28. package/src/lib/attachment/attachment.queries.d.ts +9 -0
  29. package/src/lib/attachment/attachment.queries.js +78 -0
  30. package/src/lib/attachment/attachment.queries.js.map +1 -0
  31. package/src/lib/attachment/attachment.service.d.ts +49 -0
  32. package/src/lib/attachment/attachment.service.js +276 -0
  33. package/src/lib/attachment/attachment.service.js.map +1 -0
  34. package/src/lib/attachment/dto/upload-attachment.input.d.ts +11 -0
  35. package/src/lib/attachment/dto/upload-attachment.input.js +68 -0
  36. package/src/lib/attachment/dto/upload-attachment.input.js.map +1 -0
  37. package/src/lib/attachment/index.js +9 -0
  38. package/src/lib/attachment/index.js.map +1 -0
  39. package/src/lib/bpm/bpm-root.module.d.ts +155 -0
  40. package/src/lib/bpm/bpm-root.module.js +170 -0
  41. package/src/lib/bpm/bpm-root.module.js.map +1 -0
  42. package/src/lib/bpm/index.js +5 -0
  43. package/src/lib/bpm/index.js.map +1 -0
  44. package/src/lib/bpm-auth/bpm-auth-context.d.ts +24 -0
  45. package/src/lib/bpm-auth/bpm-auth-context.extractor.d.ts +4 -0
  46. package/src/lib/bpm-auth/bpm-auth-context.extractor.js +69 -0
  47. package/src/lib/bpm-auth/bpm-auth-context.extractor.js.map +1 -0
  48. package/src/lib/bpm-auth/bpm-auth-context.js +15 -0
  49. package/src/lib/bpm-auth/bpm-auth-context.js.map +1 -0
  50. package/src/lib/bpm-auth/bpm-auth.authorization.d.ts +12 -0
  51. package/src/lib/bpm-auth/bpm-auth.authorization.js +67 -0
  52. package/src/lib/bpm-auth/bpm-auth.authorization.js.map +1 -0
  53. package/src/lib/bpm-auth/bpm-auth.decorators.d.ts +3 -0
  54. package/src/lib/bpm-auth/bpm-auth.decorators.js +11 -0
  55. package/src/lib/bpm-auth/bpm-auth.decorators.js.map +1 -0
  56. package/src/lib/bpm-auth/bpm-auth.guard.d.ts +7 -0
  57. package/src/lib/bpm-auth/bpm-auth.guard.js +24 -0
  58. package/src/lib/bpm-auth/bpm-auth.guard.js.map +1 -0
  59. package/src/lib/bpm-auth/bpm-auth.module.d.ts +6 -0
  60. package/src/lib/bpm-auth/bpm-auth.module.js +69 -0
  61. package/src/lib/bpm-auth/bpm-auth.module.js.map +1 -0
  62. package/src/lib/bpm-auth/bpm-auth.options.d.ts +11 -0
  63. package/src/lib/bpm-auth/bpm-auth.options.js +3 -0
  64. package/src/lib/bpm-auth/bpm-auth.options.js.map +1 -0
  65. package/src/lib/bpm-auth/configurable-bpm-auth-context.accessor.d.ts +10 -0
  66. package/src/lib/bpm-auth/configurable-bpm-auth-context.accessor.js +40 -0
  67. package/src/lib/bpm-auth/configurable-bpm-auth-context.accessor.js.map +1 -0
  68. package/src/lib/bpm-auth/index.js +10 -0
  69. package/src/lib/bpm-auth/index.js.map +1 -0
  70. package/src/lib/common/filters/all-exceptions.filter.d.ts +25 -0
  71. package/src/lib/common/filters/all-exceptions.filter.js +93 -0
  72. package/src/lib/common/filters/all-exceptions.filter.js.map +1 -0
  73. package/src/lib/common/index.js +5 -0
  74. package/src/lib/common/index.js.map +1 -0
  75. package/src/lib/common/iso-duration.d.ts +1 -0
  76. package/src/lib/common/iso-duration.js +27 -0
  77. package/src/lib/common/iso-duration.js.map +1 -0
  78. package/src/lib/condition/condition.module.d.ts +2 -0
  79. package/src/lib/condition/condition.module.js +16 -0
  80. package/src/lib/condition/condition.module.js.map +1 -0
  81. package/src/lib/condition/condition.service.d.ts +16 -0
  82. package/src/lib/condition/condition.service.js +82 -0
  83. package/src/lib/condition/condition.service.js.map +1 -0
  84. package/src/lib/condition/index.js +6 -0
  85. package/src/lib/condition/index.js.map +1 -0
  86. package/src/lib/database/data-source.d.ts +4 -0
  87. package/src/lib/database/data-source.js +7 -0
  88. package/src/lib/database/data-source.js.map +1 -0
  89. package/src/lib/database/index.js +6 -0
  90. package/src/lib/database/index.js.map +1 -0
  91. package/src/lib/database/migration-runner.d.ts +1 -0
  92. package/src/lib/database/migration-runner.js +23 -0
  93. package/src/lib/database/migration-runner.js.map +1 -0
  94. package/src/lib/database/typeorm.config.d.ts +14 -0
  95. package/src/lib/database/typeorm.config.js +135 -0
  96. package/src/lib/database/typeorm.config.js.map +1 -0
  97. package/src/lib/delegation/delegation-rule.entity.d.ts +19 -0
  98. package/src/lib/delegation/delegation-rule.entity.js +99 -0
  99. package/src/lib/delegation/delegation-rule.entity.js.map +1 -0
  100. package/src/lib/delegation/delegation.enums.d.ts +10 -0
  101. package/src/lib/delegation/delegation.enums.js +23 -0
  102. package/src/lib/delegation/delegation.enums.js.map +1 -0
  103. package/src/lib/delegation/delegation.module.d.ts +2 -0
  104. package/src/lib/delegation/delegation.module.js +22 -0
  105. package/src/lib/delegation/delegation.module.js.map +1 -0
  106. package/src/lib/delegation/delegation.mutations.d.ts +11 -0
  107. package/src/lib/delegation/delegation.mutations.js +87 -0
  108. package/src/lib/delegation/delegation.mutations.js.map +1 -0
  109. package/src/lib/delegation/delegation.queries.d.ts +10 -0
  110. package/src/lib/delegation/delegation.queries.js +91 -0
  111. package/src/lib/delegation/delegation.queries.js.map +1 -0
  112. package/src/lib/delegation/delegation.service.d.ts +56 -0
  113. package/src/lib/delegation/delegation.service.js +268 -0
  114. package/src/lib/delegation/delegation.service.js.map +1 -0
  115. package/src/lib/delegation/dto/delegation-rule.input.d.ts +24 -0
  116. package/src/lib/delegation/dto/delegation-rule.input.js +143 -0
  117. package/src/lib/delegation/dto/delegation-rule.input.js.map +1 -0
  118. package/src/lib/delegation/index.js +9 -0
  119. package/src/lib/delegation/index.js.map +1 -0
  120. package/src/lib/form/dto/form-definition.input.d.ts +21 -0
  121. package/src/lib/form/dto/form-definition.input.js +103 -0
  122. package/src/lib/form/dto/form-definition.input.js.map +1 -0
  123. package/src/lib/form/form-definition-version.entity.d.ts +17 -0
  124. package/src/lib/form/form-definition-version.entity.js +84 -0
  125. package/src/lib/form/form-definition-version.entity.js.map +1 -0
  126. package/src/lib/form/form-definition.entity.d.ts +13 -0
  127. package/src/lib/form/form-definition.entity.js +66 -0
  128. package/src/lib/form/form-definition.entity.js.map +1 -0
  129. package/src/lib/form/form-schema-lint.object.d.ts +4 -0
  130. package/src/lib/form/form-schema-lint.object.js +20 -0
  131. package/src/lib/form/form-schema-lint.object.js.map +1 -0
  132. package/src/lib/form/form-schema.validator.d.ts +13 -0
  133. package/src/lib/form/form-schema.validator.js +354 -0
  134. package/src/lib/form/form-schema.validator.js.map +1 -0
  135. package/src/lib/form/form.enums.d.ts +9 -0
  136. package/src/lib/form/form.enums.js +22 -0
  137. package/src/lib/form/form.enums.js.map +1 -0
  138. package/src/lib/form/form.module.d.ts +2 -0
  139. package/src/lib/form/form.module.js +27 -0
  140. package/src/lib/form/form.module.js.map +1 -0
  141. package/src/lib/form/form.mutations.d.ts +14 -0
  142. package/src/lib/form/form.mutations.js +88 -0
  143. package/src/lib/form/form.mutations.js.map +1 -0
  144. package/src/lib/form/form.queries.d.ts +16 -0
  145. package/src/lib/form/form.queries.js +96 -0
  146. package/src/lib/form/form.queries.js.map +1 -0
  147. package/src/lib/form/form.service.d.ts +35 -0
  148. package/src/lib/form/form.service.js +297 -0
  149. package/src/lib/form/form.service.js.map +1 -0
  150. package/src/lib/form/index.js +12 -0
  151. package/src/lib/form/index.js.map +1 -0
  152. package/src/lib/identity/identity-options.d.ts +13 -0
  153. package/src/lib/identity/identity-options.js +20 -0
  154. package/src/lib/identity/identity-options.js.map +1 -0
  155. package/src/lib/identity/identity.module.d.ts +17 -0
  156. package/src/lib/identity/identity.module.js +65 -0
  157. package/src/lib/identity/identity.module.js.map +1 -0
  158. package/src/lib/identity/identity.queries.d.ts +12 -0
  159. package/src/lib/identity/identity.queries.js +79 -0
  160. package/src/lib/identity/identity.queries.js.map +1 -0
  161. package/src/lib/identity/identity.service.d.ts +22 -0
  162. package/src/lib/identity/identity.service.js +141 -0
  163. package/src/lib/identity/identity.service.js.map +1 -0
  164. package/src/lib/identity/index.js +7 -0
  165. package/src/lib/identity/index.js.map +1 -0
  166. package/src/lib/identity/member-base.adapter.d.ts +95 -0
  167. package/src/lib/identity/member-base.adapter.js +137 -0
  168. package/src/lib/identity/member-base.adapter.js.map +1 -0
  169. package/src/lib/identity/member-metadata-cache.entity.d.ts +8 -0
  170. package/src/lib/identity/member-metadata-cache.entity.js +39 -0
  171. package/src/lib/identity/member-metadata-cache.entity.js.map +1 -0
  172. package/src/lib/identity/member-profile.object.d.ts +8 -0
  173. package/src/lib/identity/member-profile.object.js +37 -0
  174. package/src/lib/identity/member-profile.object.js.map +1 -0
  175. package/src/lib/identity/member-resolver.interface.d.ts +79 -0
  176. package/src/lib/identity/member-resolver.interface.js +19 -0
  177. package/src/lib/identity/member-resolver.interface.js.map +1 -0
  178. package/src/lib/migrations/0000000000001-enable-postgres-extensions.d.ts +6 -0
  179. package/src/lib/migrations/0000000000001-enable-postgres-extensions.js +17 -0
  180. package/src/lib/migrations/0000000000001-enable-postgres-extensions.js.map +1 -0
  181. package/src/lib/migrations/0000000001000-identity-organization-foundation.d.ts +6 -0
  182. package/src/lib/migrations/{0000000001000-identity-organization-foundation.ts → 0000000001000-identity-organization-foundation.js} +29 -38
  183. package/src/lib/migrations/0000000001000-identity-organization-foundation.js.map +1 -0
  184. package/src/lib/migrations/0000000002000-form-builder-foundation.d.ts +6 -0
  185. package/src/lib/migrations/{0000000002000-form-builder-foundation.ts → 0000000002000-form-builder-foundation.js} +21 -22
  186. package/src/lib/migrations/0000000002000-form-builder-foundation.js.map +1 -0
  187. package/src/lib/migrations/0000000003000-approval-template-foundation.d.ts +6 -0
  188. package/src/lib/migrations/{0000000003000-approval-template-foundation.ts → 0000000003000-approval-template-foundation.js} +21 -24
  189. package/src/lib/migrations/0000000003000-approval-template-foundation.js.map +1 -0
  190. package/src/lib/migrations/0000000004000-workflow-engine-foundation.d.ts +6 -0
  191. package/src/lib/migrations/{0000000004000-workflow-engine-foundation.ts → 0000000004000-workflow-engine-foundation.js} +34 -46
  192. package/src/lib/migrations/0000000004000-workflow-engine-foundation.js.map +1 -0
  193. package/src/lib/migrations/0000000005000-delegation-rules.d.ts +6 -0
  194. package/src/lib/migrations/{0000000005000-delegation-rules.ts → 0000000005000-delegation-rules.js} +19 -20
  195. package/src/lib/migrations/0000000005000-delegation-rules.js.map +1 -0
  196. package/src/lib/migrations/0000000006000-notifications-sla.d.ts +6 -0
  197. package/src/lib/migrations/{0000000006000-notifications-sla.ts → 0000000006000-notifications-sla.js} +23 -26
  198. package/src/lib/migrations/0000000006000-notifications-sla.js.map +1 -0
  199. package/src/lib/migrations/0000000007000-signatures-attachments.d.ts +6 -0
  200. package/src/lib/migrations/{0000000007000-signatures-attachments.ts → 0000000007000-signatures-attachments.js} +27 -28
  201. package/src/lib/migrations/0000000007000-signatures-attachments.js.map +1 -0
  202. package/src/lib/migrations/0000000008000-approval-template-categories.d.ts +6 -0
  203. package/src/lib/migrations/{0000000008000-approval-template-categories.ts → 0000000008000-approval-template-categories.js} +25 -28
  204. package/src/lib/migrations/0000000008000-approval-template-categories.js.map +1 -0
  205. package/src/lib/migrations/0000000009000-task-candidates.d.ts +6 -0
  206. package/src/lib/migrations/{0000000009000-task-candidates.ts → 0000000009000-task-candidates.js} +23 -26
  207. package/src/lib/migrations/0000000009000-task-candidates.js.map +1 -0
  208. package/src/lib/migrations/0000000010000-notification-delivery-state.d.ts +6 -0
  209. package/src/lib/migrations/{0000000010000-notification-delivery-state.ts → 0000000010000-notification-delivery-state.js} +17 -16
  210. package/src/lib/migrations/0000000010000-notification-delivery-state.js.map +1 -0
  211. package/src/lib/migrations/0000000011000-remove-attachment-encryption-key.d.ts +6 -0
  212. package/src/lib/migrations/0000000011000-remove-attachment-encryption-key.js +22 -0
  213. package/src/lib/migrations/0000000011000-remove-attachment-encryption-key.js.map +1 -0
  214. package/src/lib/migrations/0000000012000-notification-sla-idempotency.d.ts +6 -0
  215. package/src/lib/migrations/0000000012000-notification-sla-idempotency.js +21 -0
  216. package/src/lib/migrations/0000000012000-notification-sla-idempotency.js.map +1 -0
  217. package/src/lib/migrations/0000000013000-workflow-query-indexes.d.ts +6 -0
  218. package/src/lib/migrations/0000000013000-workflow-query-indexes.js +80 -0
  219. package/src/lib/migrations/0000000013000-workflow-query-indexes.js.map +1 -0
  220. package/src/lib/migrations/index.d.ts +16 -0
  221. package/src/lib/migrations/index.js +49 -0
  222. package/src/lib/migrations/index.js.map +1 -0
  223. package/src/lib/notification/dto/notification-preference.input.d.ts +9 -0
  224. package/src/lib/notification/dto/notification-preference.input.js +53 -0
  225. package/src/lib/notification/dto/notification-preference.input.js.map +1 -0
  226. package/src/lib/notification/index.js +11 -0
  227. package/src/lib/notification/index.js.map +1 -0
  228. package/src/lib/notification/notification-delivery-scheduler.service.d.ts +13 -0
  229. package/src/lib/notification/notification-delivery-scheduler.service.js +53 -0
  230. package/src/lib/notification/notification-delivery-scheduler.service.js.map +1 -0
  231. package/src/lib/notification/notification-delivery.service.d.ts +23 -0
  232. package/src/lib/notification/notification-delivery.service.js +245 -0
  233. package/src/lib/notification/notification-delivery.service.js.map +1 -0
  234. package/src/lib/notification/notification-dispatcher.token.d.ts +7 -0
  235. package/src/lib/notification/notification-dispatcher.token.js +5 -0
  236. package/src/lib/notification/notification-dispatcher.token.js.map +1 -0
  237. package/src/lib/notification/notification-options.d.ts +215 -0
  238. package/src/lib/notification/notification-options.js +128 -0
  239. package/src/lib/notification/notification-options.js.map +1 -0
  240. package/src/lib/notification/notification-options.module.d.ts +23 -0
  241. package/src/lib/notification/notification-options.module.js +41 -0
  242. package/src/lib/notification/notification-options.module.js.map +1 -0
  243. package/src/lib/notification/notification-preference.entity.d.ts +10 -0
  244. package/src/lib/notification/notification-preference.entity.js +50 -0
  245. package/src/lib/notification/notification-preference.entity.js.map +1 -0
  246. package/src/lib/notification/notification-sla-scheduler.service.d.ts +13 -0
  247. package/src/lib/notification/notification-sla-scheduler.service.js +51 -0
  248. package/src/lib/notification/notification-sla-scheduler.service.js.map +1 -0
  249. package/src/lib/notification/notification-template.d.ts +13 -0
  250. package/src/lib/notification/notification-template.js +61 -0
  251. package/src/lib/notification/notification-template.js.map +1 -0
  252. package/src/lib/notification/notification.entity.d.ts +23 -0
  253. package/src/lib/notification/notification.entity.js +122 -0
  254. package/src/lib/notification/notification.entity.js.map +1 -0
  255. package/src/lib/notification/notification.enums.d.ts +24 -0
  256. package/src/lib/notification/notification.enums.js +45 -0
  257. package/src/lib/notification/notification.enums.js.map +1 -0
  258. package/src/lib/notification/notification.module.d.ts +2 -0
  259. package/src/lib/notification/notification.module.js +45 -0
  260. package/src/lib/notification/notification.module.js.map +1 -0
  261. package/src/lib/notification/notification.mutations.d.ts +11 -0
  262. package/src/lib/notification/notification.mutations.js +64 -0
  263. package/src/lib/notification/notification.mutations.js.map +1 -0
  264. package/src/lib/notification/notification.queries.d.ts +11 -0
  265. package/src/lib/notification/notification.queries.js +77 -0
  266. package/src/lib/notification/notification.queries.js.map +1 -0
  267. package/src/lib/notification/notification.service.d.ts +81 -0
  268. package/src/lib/notification/notification.service.js +621 -0
  269. package/src/lib/notification/notification.service.js.map +1 -0
  270. package/src/lib/organization/dto/manager-resolution.input.d.ts +18 -0
  271. package/src/lib/organization/dto/manager-resolution.input.js +107 -0
  272. package/src/lib/organization/dto/manager-resolution.input.js.map +1 -0
  273. package/src/lib/organization/dto/membership.input.d.ts +16 -0
  274. package/src/lib/organization/dto/membership.input.js +99 -0
  275. package/src/lib/organization/dto/membership.input.js.map +1 -0
  276. package/src/lib/organization/dto/org-unit.input.d.ts +24 -0
  277. package/src/lib/organization/dto/org-unit.input.js +119 -0
  278. package/src/lib/organization/dto/org-unit.input.js.map +1 -0
  279. package/src/lib/organization/dto/position.input.d.ts +13 -0
  280. package/src/lib/organization/dto/position.input.js +70 -0
  281. package/src/lib/organization/dto/position.input.js.map +1 -0
  282. package/src/lib/organization/index.js +16 -0
  283. package/src/lib/organization/index.js.map +1 -0
  284. package/src/lib/organization/json-metadata.d.ts +1 -0
  285. package/src/lib/organization/json-metadata.js +14 -0
  286. package/src/lib/organization/json-metadata.js.map +1 -0
  287. package/src/lib/organization/manager-resolution.entity.d.ts +11 -0
  288. package/src/lib/organization/manager-resolution.entity.js +55 -0
  289. package/src/lib/organization/manager-resolution.entity.js.map +1 -0
  290. package/src/lib/organization/membership.entity.d.ts +11 -0
  291. package/src/lib/organization/membership.entity.js +60 -0
  292. package/src/lib/organization/membership.entity.js.map +1 -0
  293. package/src/lib/organization/org-unit-tree-commit-result.object.d.ts +4 -0
  294. package/src/lib/organization/org-unit-tree-commit-result.object.js +17 -0
  295. package/src/lib/organization/org-unit-tree-commit-result.object.js.map +1 -0
  296. package/src/lib/organization/org-unit.entity.d.ts +13 -0
  297. package/src/lib/organization/org-unit.entity.js +64 -0
  298. package/src/lib/organization/org-unit.entity.js.map +1 -0
  299. package/src/lib/organization/organization-summary.object.d.ts +6 -0
  300. package/src/lib/organization/organization-summary.object.js +28 -0
  301. package/src/lib/organization/organization-summary.object.js.map +1 -0
  302. package/src/lib/organization/organization.enums.d.ts +11 -0
  303. package/src/lib/organization/organization.enums.js +22 -0
  304. package/src/lib/organization/organization.enums.js.map +1 -0
  305. package/src/lib/organization/organization.module.d.ts +2 -0
  306. package/src/lib/organization/organization.module.js +35 -0
  307. package/src/lib/organization/organization.module.js.map +1 -0
  308. package/src/lib/organization/organization.mutations.d.ts +26 -0
  309. package/src/lib/organization/organization.mutations.js +148 -0
  310. package/src/lib/organization/organization.mutations.js.map +1 -0
  311. package/src/lib/organization/organization.queries.d.ts +23 -0
  312. package/src/lib/organization/organization.queries.js +202 -0
  313. package/src/lib/organization/organization.queries.js.map +1 -0
  314. package/src/lib/organization/organization.service.d.ts +102 -0
  315. package/src/lib/organization/organization.service.js +751 -0
  316. package/src/lib/organization/organization.service.js.map +1 -0
  317. package/src/lib/organization/position.entity.d.ts +9 -0
  318. package/src/lib/organization/position.entity.js +48 -0
  319. package/src/lib/organization/position.entity.js.map +1 -0
  320. package/src/lib/organization/resolved-manager.object.d.ts +4 -0
  321. package/src/lib/organization/resolved-manager.object.js +20 -0
  322. package/src/lib/organization/resolved-manager.object.js.map +1 -0
  323. package/src/lib/signature/index.js +8 -0
  324. package/src/lib/signature/index.js.map +1 -0
  325. package/src/lib/signature/signature-options.d.ts +33 -0
  326. package/src/lib/signature/signature-options.js +51 -0
  327. package/src/lib/signature/signature-options.js.map +1 -0
  328. package/src/lib/signature/signature-verification.object.d.ts +6 -0
  329. package/src/lib/signature/signature-verification.object.js +28 -0
  330. package/src/lib/signature/signature-verification.object.js.map +1 -0
  331. package/src/lib/signature/signature.entity.d.ts +16 -0
  332. package/src/lib/signature/signature.entity.js +88 -0
  333. package/src/lib/signature/signature.entity.js.map +1 -0
  334. package/src/lib/signature/signature.module.d.ts +11 -0
  335. package/src/lib/signature/signature.module.js +60 -0
  336. package/src/lib/signature/signature.module.js.map +1 -0
  337. package/src/lib/signature/signature.queries.d.ts +11 -0
  338. package/src/lib/signature/signature.queries.js +46 -0
  339. package/src/lib/signature/signature.queries.js.map +1 -0
  340. package/src/lib/signature/signature.service.d.ts +25 -0
  341. package/src/lib/signature/signature.service.js +124 -0
  342. package/src/lib/signature/signature.service.js.map +1 -0
  343. package/src/lib/template/approval-template-category.entity.d.ts +9 -0
  344. package/src/lib/template/approval-template-category.entity.js +49 -0
  345. package/src/lib/template/approval-template-category.entity.js.map +1 -0
  346. package/src/lib/template/approval-template-version.entity.d.ts +21 -0
  347. package/src/lib/template/approval-template-version.entity.js +106 -0
  348. package/src/lib/template/approval-template-version.entity.js.map +1 -0
  349. package/src/lib/template/approval-template.entity.d.ts +14 -0
  350. package/src/lib/template/approval-template.entity.js +74 -0
  351. package/src/lib/template/approval-template.entity.js.map +1 -0
  352. package/src/lib/template/dto/approval-template.input.d.ts +36 -0
  353. package/src/lib/template/dto/approval-template.input.js +187 -0
  354. package/src/lib/template/dto/approval-template.input.js.map +1 -0
  355. package/src/lib/template/index.js +12 -0
  356. package/src/lib/template/index.js.map +1 -0
  357. package/src/lib/template/template.enums.d.ts +14 -0
  358. package/src/lib/template/template.enums.js +31 -0
  359. package/src/lib/template/template.enums.js.map +1 -0
  360. package/src/lib/template/template.module.d.ts +2 -0
  361. package/src/lib/template/template.module.js +33 -0
  362. package/src/lib/template/template.module.js.map +1 -0
  363. package/src/lib/template/template.mutations.d.ts +20 -0
  364. package/src/lib/template/template.mutations.js +139 -0
  365. package/src/lib/template/template.mutations.js.map +1 -0
  366. package/src/lib/template/template.queries.d.ts +16 -0
  367. package/src/lib/template/template.queries.js +133 -0
  368. package/src/lib/template/template.queries.js.map +1 -0
  369. package/src/lib/template/template.service.d.ts +64 -0
  370. package/src/lib/template/template.service.js +521 -0
  371. package/src/lib/template/template.service.js.map +1 -0
  372. package/src/lib/template/workflow-definition.validator.d.ts +9 -0
  373. package/src/lib/template/workflow-definition.validator.js +362 -0
  374. package/src/lib/template/workflow-definition.validator.js.map +1 -0
  375. package/src/lib/testing/cel-js.jest.d.ts +15 -0
  376. package/src/lib/testing/cel-js.jest.js +27 -0
  377. package/src/lib/testing/cel-js.jest.js.map +1 -0
  378. package/src/lib/workflow-engine/activity-log.entity.d.ts +12 -0
  379. package/src/lib/workflow-engine/activity-log.entity.js +62 -0
  380. package/src/lib/workflow-engine/activity-log.entity.js.map +1 -0
  381. package/src/lib/workflow-engine/approval-instance-page-info.object.d.ts +8 -0
  382. package/src/lib/workflow-engine/approval-instance-page-info.object.js +36 -0
  383. package/src/lib/workflow-engine/approval-instance-page-info.object.js.map +1 -0
  384. package/src/lib/workflow-engine/approval-instance.entity.d.ts +22 -0
  385. package/src/lib/workflow-engine/approval-instance.entity.js +113 -0
  386. package/src/lib/workflow-engine/approval-instance.entity.js.map +1 -0
  387. package/src/lib/workflow-engine/dto/cancel-approval-instance.input.d.ts +5 -0
  388. package/src/lib/workflow-engine/dto/cancel-approval-instance.input.js +29 -0
  389. package/src/lib/workflow-engine/dto/cancel-approval-instance.input.js.map +1 -0
  390. package/src/lib/workflow-engine/dto/decide-task.input.d.ts +9 -0
  391. package/src/lib/workflow-engine/dto/decide-task.input.js +47 -0
  392. package/src/lib/workflow-engine/dto/decide-task.input.js.map +1 -0
  393. package/src/lib/workflow-engine/dto/dry-run-approval-workflow.input.d.ts +6 -0
  394. package/src/lib/workflow-engine/dto/dry-run-approval-workflow.input.js +34 -0
  395. package/src/lib/workflow-engine/dto/dry-run-approval-workflow.input.js.map +1 -0
  396. package/src/lib/workflow-engine/dto/resubmit-approval-instance.input.d.ts +6 -0
  397. package/src/lib/workflow-engine/dto/resubmit-approval-instance.input.js +34 -0
  398. package/src/lib/workflow-engine/dto/resubmit-approval-instance.input.js.map +1 -0
  399. package/src/lib/workflow-engine/dto/submit-approval-instance.input.d.ts +7 -0
  400. package/src/lib/workflow-engine/dto/submit-approval-instance.input.js +40 -0
  401. package/src/lib/workflow-engine/dto/submit-approval-instance.input.js.map +1 -0
  402. package/src/lib/workflow-engine/index.js +24 -0
  403. package/src/lib/workflow-engine/index.js.map +1 -0
  404. package/src/lib/workflow-engine/task-candidate.entity.d.ts +14 -0
  405. package/src/lib/workflow-engine/task-candidate.entity.js +72 -0
  406. package/src/lib/workflow-engine/task-candidate.entity.js.map +1 -0
  407. package/src/lib/workflow-engine/task-decision.entity.d.ts +12 -0
  408. package/src/lib/workflow-engine/task-decision.entity.js +60 -0
  409. package/src/lib/workflow-engine/task-decision.entity.js.map +1 -0
  410. package/src/lib/workflow-engine/task.entity.d.ts +20 -0
  411. package/src/lib/workflow-engine/task.entity.js +109 -0
  412. package/src/lib/workflow-engine/task.entity.js.map +1 -0
  413. package/src/lib/workflow-engine/workflow-condition-evaluator.d.ts +5 -0
  414. package/src/lib/workflow-engine/workflow-condition-evaluator.js +107 -0
  415. package/src/lib/workflow-engine/workflow-condition-evaluator.js.map +1 -0
  416. package/src/lib/workflow-engine/workflow-dashboard-summary.object.d.ts +8 -0
  417. package/src/lib/workflow-engine/workflow-dashboard-summary.object.js +36 -0
  418. package/src/lib/workflow-engine/workflow-dashboard-summary.object.js.map +1 -0
  419. package/src/lib/workflow-engine/workflow-dry-run.object.d.ts +21 -0
  420. package/src/lib/workflow-engine/workflow-dry-run.object.js +86 -0
  421. package/src/lib/workflow-engine/workflow-dry-run.object.js.map +1 -0
  422. package/src/lib/workflow-engine/workflow-engine.enums.d.ts +58 -0
  423. package/src/lib/workflow-engine/workflow-engine.enums.js +95 -0
  424. package/src/lib/workflow-engine/workflow-engine.enums.js.map +1 -0
  425. package/src/lib/workflow-engine/workflow-engine.module.d.ts +9 -0
  426. package/src/lib/workflow-engine/workflow-engine.module.js +76 -0
  427. package/src/lib/workflow-engine/workflow-engine.module.js.map +1 -0
  428. package/src/lib/workflow-engine/workflow-engine.mutations.d.ts +19 -0
  429. package/src/lib/workflow-engine/workflow-engine.mutations.js +106 -0
  430. package/src/lib/workflow-engine/workflow-engine.mutations.js.map +1 -0
  431. package/src/lib/workflow-engine/workflow-engine.queries.d.ts +29 -0
  432. package/src/lib/workflow-engine/workflow-engine.queries.js +225 -0
  433. package/src/lib/workflow-engine/workflow-engine.queries.js.map +1 -0
  434. package/src/lib/workflow-engine/workflow-engine.service.d.ts +145 -0
  435. package/src/lib/workflow-engine/workflow-engine.service.js +2785 -0
  436. package/src/lib/workflow-engine/workflow-engine.service.js.map +1 -0
  437. package/src/lib/workflow-engine/{workflow-engine.tokens.ts → workflow-engine.tokens.d.ts} +3 -9
  438. package/src/lib/workflow-engine/workflow-engine.tokens.js +5 -0
  439. package/src/lib/workflow-engine/workflow-engine.tokens.js.map +1 -0
  440. package/src/lib/workflow-engine/workflow-service-task-dispatcher.token.d.ts +18 -0
  441. package/src/lib/workflow-engine/workflow-service-task-dispatcher.token.js +39 -0
  442. package/src/lib/workflow-engine/workflow-service-task-dispatcher.token.js.map +1 -0
  443. package/src/lib/workflow-engine/workflow-token.entity.d.ts +10 -0
  444. package/src/lib/workflow-engine/workflow-token.entity.js +50 -0
  445. package/src/lib/workflow-engine/workflow-token.entity.js.map +1 -0
  446. package/eslint.config.mjs +0 -20
  447. package/jest.config.cts +0 -15
  448. package/project.json +0 -58
  449. package/src/lib/attachment/attachment-options.spec.ts +0 -45
  450. package/src/lib/attachment/attachment-options.ts +0 -130
  451. package/src/lib/attachment/attachment-storage.provider.spec.ts +0 -79
  452. package/src/lib/attachment/attachment-storage.provider.ts +0 -60
  453. package/src/lib/attachment/attachment.controller.ts +0 -39
  454. package/src/lib/attachment/attachment.entity.ts +0 -59
  455. package/src/lib/attachment/attachment.module.ts +0 -149
  456. package/src/lib/attachment/attachment.mutations.ts +0 -22
  457. package/src/lib/attachment/attachment.queries.ts +0 -65
  458. package/src/lib/attachment/attachment.service.spec.ts +0 -319
  459. package/src/lib/attachment/attachment.service.ts +0 -477
  460. package/src/lib/attachment/dto/upload-attachment.input.ts +0 -60
  461. package/src/lib/bpm/bpm-root.module.spec.ts +0 -138
  462. package/src/lib/bpm/bpm-root.module.ts +0 -320
  463. package/src/lib/bpm-auth/bpm-auth-context.extractor.ts +0 -106
  464. package/src/lib/bpm-auth/bpm-auth-context.ts +0 -40
  465. package/src/lib/bpm-auth/bpm-auth.authorization.ts +0 -80
  466. package/src/lib/bpm-auth/bpm-auth.decorators.ts +0 -19
  467. package/src/lib/bpm-auth/bpm-auth.guard.ts +0 -31
  468. package/src/lib/bpm-auth/bpm-auth.module.spec.ts +0 -106
  469. package/src/lib/bpm-auth/bpm-auth.module.ts +0 -69
  470. package/src/lib/bpm-auth/bpm-auth.options.ts +0 -21
  471. package/src/lib/bpm-auth/configurable-bpm-auth-context.accessor.ts +0 -55
  472. package/src/lib/common/filters/all-exceptions.filter.ts +0 -138
  473. package/src/lib/common/iso-duration.ts +0 -31
  474. package/src/lib/condition/condition.module.ts +0 -8
  475. package/src/lib/condition/condition.service.spec.ts +0 -50
  476. package/src/lib/condition/condition.service.ts +0 -141
  477. package/src/lib/database/data-source.ts +0 -7
  478. package/src/lib/database/migration-runner.ts +0 -24
  479. package/src/lib/database/typeorm.config.ts +0 -217
  480. package/src/lib/delegation/delegation-rule.entity.ts +0 -84
  481. package/src/lib/delegation/delegation.enums.ts +0 -21
  482. package/src/lib/delegation/delegation.module.ts +0 -14
  483. package/src/lib/delegation/delegation.mutations.ts +0 -92
  484. package/src/lib/delegation/delegation.queries.ts +0 -111
  485. package/src/lib/delegation/delegation.service.spec.ts +0 -295
  486. package/src/lib/delegation/delegation.service.ts +0 -482
  487. package/src/lib/delegation/dto/delegation-rule.input.ts +0 -125
  488. package/src/lib/form/dto/form-definition.input.ts +0 -73
  489. package/src/lib/form/form-definition-version.entity.ts +0 -66
  490. package/src/lib/form/form-definition.entity.ts +0 -54
  491. package/src/lib/form/form-schema-lint.object.ts +0 -10
  492. package/src/lib/form/form-schema.validator.spec.ts +0 -180
  493. package/src/lib/form/form-schema.validator.ts +0 -538
  494. package/src/lib/form/form.enums.ts +0 -20
  495. package/src/lib/form/form.module.ts +0 -19
  496. package/src/lib/form/form.mutations.ts +0 -68
  497. package/src/lib/form/form.queries.ts +0 -72
  498. package/src/lib/form/form.service.spec.ts +0 -144
  499. package/src/lib/form/form.service.ts +0 -490
  500. package/src/lib/identity/identity-options.ts +0 -41
  501. package/src/lib/identity/identity.module.ts +0 -96
  502. package/src/lib/identity/identity.queries.ts +0 -61
  503. package/src/lib/identity/identity.service.spec.ts +0 -105
  504. package/src/lib/identity/identity.service.ts +0 -200
  505. package/src/lib/identity/member-base.adapter.spec.ts +0 -77
  506. package/src/lib/identity/member-base.adapter.ts +0 -244
  507. package/src/lib/identity/member-metadata-cache.entity.ts +0 -27
  508. package/src/lib/identity/member-profile.object.ts +0 -28
  509. package/src/lib/identity/member-resolver.interface.ts +0 -47
  510. package/src/lib/migrations/0000000000001-enable-postgres-extensions.ts +0 -14
  511. package/src/lib/migrations/0000000011000-remove-attachment-encryption-key.ts +0 -19
  512. package/src/lib/migrations/0000000012000-notification-sla-idempotency.ts +0 -22
  513. package/src/lib/migrations/0000000013000-workflow-query-indexes.ts +0 -113
  514. package/src/lib/migrations/index.ts +0 -47
  515. package/src/lib/notification/dto/notification-preference.input.ts +0 -46
  516. package/src/lib/notification/notification-delivery-scheduler.service.ts +0 -69
  517. package/src/lib/notification/notification-delivery.service.spec.ts +0 -315
  518. package/src/lib/notification/notification-delivery.service.ts +0 -339
  519. package/src/lib/notification/notification-dispatcher.token.ts +0 -13
  520. package/src/lib/notification/notification-options.module.ts +0 -67
  521. package/src/lib/notification/notification-options.spec.ts +0 -93
  522. package/src/lib/notification/notification-options.ts +0 -452
  523. package/src/lib/notification/notification-preference.entity.ts +0 -35
  524. package/src/lib/notification/notification-sla-scheduler.service.ts +0 -66
  525. package/src/lib/notification/notification-template.spec.ts +0 -33
  526. package/src/lib/notification/notification-template.ts +0 -97
  527. package/src/lib/notification/notification.entity.ts +0 -102
  528. package/src/lib/notification/notification.enums.ts +0 -45
  529. package/src/lib/notification/notification.module.ts +0 -37
  530. package/src/lib/notification/notification.mutations.ts +0 -47
  531. package/src/lib/notification/notification.queries.ts +0 -64
  532. package/src/lib/notification/notification.service.spec.ts +0 -412
  533. package/src/lib/notification/notification.service.ts +0 -974
  534. package/src/lib/organization/dto/manager-resolution.input.ts +0 -91
  535. package/src/lib/organization/dto/membership.input.ts +0 -77
  536. package/src/lib/organization/dto/org-unit.input.ts +0 -96
  537. package/src/lib/organization/dto/position.input.ts +0 -50
  538. package/src/lib/organization/json-metadata.ts +0 -15
  539. package/src/lib/organization/manager-resolution.entity.ts +0 -44
  540. package/src/lib/organization/membership.entity.ts +0 -50
  541. package/src/lib/organization/org-unit-tree-commit-result.object.ts +0 -8
  542. package/src/lib/organization/org-unit.entity.ts +0 -53
  543. package/src/lib/organization/organization-summary.object.ts +0 -16
  544. package/src/lib/organization/organization.enums.ts +0 -19
  545. package/src/lib/organization/organization.module.ts +0 -27
  546. package/src/lib/organization/organization.mutations.ts +0 -115
  547. package/src/lib/organization/organization.queries.ts +0 -188
  548. package/src/lib/organization/organization.service.spec.ts +0 -760
  549. package/src/lib/organization/organization.service.ts +0 -1216
  550. package/src/lib/organization/position.entity.ts +0 -39
  551. package/src/lib/organization/resolved-manager.object.ts +0 -10
  552. package/src/lib/signature/signature-options.spec.ts +0 -60
  553. package/src/lib/signature/signature-options.ts +0 -120
  554. package/src/lib/signature/signature-verification.object.ts +0 -16
  555. package/src/lib/signature/signature.entity.ts +0 -62
  556. package/src/lib/signature/signature.module.ts +0 -80
  557. package/src/lib/signature/signature.queries.ts +0 -37
  558. package/src/lib/signature/signature.service.spec.ts +0 -134
  559. package/src/lib/signature/signature.service.ts +0 -205
  560. package/src/lib/template/approval-template-category.entity.ts +0 -40
  561. package/src/lib/template/approval-template-version.entity.ts +0 -82
  562. package/src/lib/template/approval-template.entity.ts +0 -64
  563. package/src/lib/template/dto/approval-template.input.ts +0 -147
  564. package/src/lib/template/template.enums.ts +0 -30
  565. package/src/lib/template/template.module.ts +0 -25
  566. package/src/lib/template/template.mutations.ts +0 -106
  567. package/src/lib/template/template.queries.ts +0 -119
  568. package/src/lib/template/template.service.spec.ts +0 -476
  569. package/src/lib/template/template.service.ts +0 -820
  570. package/src/lib/template/workflow-definition.validator.spec.ts +0 -595
  571. package/src/lib/template/workflow-definition.validator.ts +0 -539
  572. package/src/lib/testing/cel-js.jest.ts +0 -46
  573. package/src/lib/workflow-engine/activity-log.entity.ts +0 -48
  574. package/src/lib/workflow-engine/approval-instance-page-info.object.ts +0 -22
  575. package/src/lib/workflow-engine/approval-instance.entity.ts +0 -86
  576. package/src/lib/workflow-engine/dto/cancel-approval-instance.input.ts +0 -18
  577. package/src/lib/workflow-engine/dto/decide-task.input.ts +0 -33
  578. package/src/lib/workflow-engine/dto/dry-run-approval-workflow.input.ts +0 -22
  579. package/src/lib/workflow-engine/dto/resubmit-approval-instance.input.ts +0 -22
  580. package/src/lib/workflow-engine/dto/submit-approval-instance.input.ts +0 -27
  581. package/src/lib/workflow-engine/task-candidate.entity.ts +0 -56
  582. package/src/lib/workflow-engine/task-decision.entity.ts +0 -43
  583. package/src/lib/workflow-engine/task.entity.ts +0 -85
  584. package/src/lib/workflow-engine/workflow-condition-evaluator.ts +0 -168
  585. package/src/lib/workflow-engine/workflow-dashboard-summary.object.ts +0 -22
  586. package/src/lib/workflow-engine/workflow-dry-run.object.ts +0 -58
  587. package/src/lib/workflow-engine/workflow-engine.enums.ts +0 -99
  588. package/src/lib/workflow-engine/workflow-engine.module.ts +0 -75
  589. package/src/lib/workflow-engine/workflow-engine.mutations.ts +0 -85
  590. package/src/lib/workflow-engine/workflow-engine.queries.ts +0 -229
  591. package/src/lib/workflow-engine/workflow-engine.service.spec.ts +0 -2689
  592. package/src/lib/workflow-engine/workflow-engine.service.ts +0 -4707
  593. package/src/lib/workflow-engine/workflow-service-task-dispatcher.token.ts +0 -69
  594. package/src/lib/workflow-engine/workflow-token.entity.ts +0 -40
  595. package/tsconfig.json +0 -24
  596. package/tsconfig.lib.json +0 -16
  597. package/tsconfig.spec.json +0 -16
  598. /package/src/{index.ts → index.d.ts} +0 -0
  599. /package/src/lib/attachment/{index.ts → index.d.ts} +0 -0
  600. /package/src/lib/bpm/{index.ts → index.d.ts} +0 -0
  601. /package/src/lib/bpm-auth/{index.ts → index.d.ts} +0 -0
  602. /package/src/lib/common/{index.ts → index.d.ts} +0 -0
  603. /package/src/lib/condition/{index.ts → index.d.ts} +0 -0
  604. /package/src/lib/database/{index.ts → index.d.ts} +0 -0
  605. /package/src/lib/delegation/{index.ts → index.d.ts} +0 -0
  606. /package/src/lib/form/{index.ts → index.d.ts} +0 -0
  607. /package/src/lib/identity/{index.ts → index.d.ts} +0 -0
  608. /package/src/lib/notification/{index.ts → index.d.ts} +0 -0
  609. /package/src/lib/organization/{index.ts → index.d.ts} +0 -0
  610. /package/src/lib/signature/{index.ts → index.d.ts} +0 -0
  611. /package/src/lib/template/{index.ts → index.d.ts} +0 -0
  612. /package/src/lib/workflow-engine/{index.ts → index.d.ts} +0 -0
@@ -0,0 +1,2785 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WorkflowEngineService = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const common_1 = require("@nestjs/common");
6
+ const typeorm_1 = require("@nestjs/typeorm");
7
+ const typeorm_2 = require("typeorm");
8
+ const attachment_service_1 = require("../attachment/attachment.service");
9
+ const delegation_service_1 = require("../delegation/delegation.service");
10
+ const form_definition_version_entity_1 = require("../form/form-definition-version.entity");
11
+ const form_enums_1 = require("../form/form.enums");
12
+ const notification_service_1 = require("../notification/notification.service");
13
+ const notification_entity_1 = require("../notification/notification.entity");
14
+ const signature_service_1 = require("../signature/signature.service");
15
+ const condition_service_1 = require("../condition/condition.service");
16
+ const manager_resolution_entity_1 = require("../organization/manager-resolution.entity");
17
+ const membership_entity_1 = require("../organization/membership.entity");
18
+ const org_unit_entity_1 = require("../organization/org-unit.entity");
19
+ const organization_enums_1 = require("../organization/organization.enums");
20
+ const approval_template_version_entity_1 = require("../template/approval-template-version.entity");
21
+ const approval_template_entity_1 = require("../template/approval-template.entity");
22
+ const template_enums_1 = require("../template/template.enums");
23
+ const activity_log_entity_1 = require("./activity-log.entity");
24
+ const approval_instance_entity_1 = require("./approval-instance.entity");
25
+ const task_decision_entity_1 = require("./task-decision.entity");
26
+ const task_candidate_entity_1 = require("./task-candidate.entity");
27
+ const task_entity_1 = require("./task.entity");
28
+ const workflow_engine_enums_1 = require("./workflow-engine.enums");
29
+ const workflow_condition_evaluator_1 = require("./workflow-condition-evaluator");
30
+ const workflow_dry_run_object_1 = require("./workflow-dry-run.object");
31
+ const workflow_service_task_dispatcher_token_1 = require("./workflow-service-task-dispatcher.token");
32
+ const workflow_token_entity_1 = require("./workflow-token.entity");
33
+ const MAX_PROCESSING_STEPS = 500;
34
+ const DEFAULT_APPROVAL_HISTORY_TASK_LIMIT = 50;
35
+ const SYSTEM_DRY_RUN_INSTANCE_ID = 'dry-run-instance';
36
+ const WORKFLOW_READ_ALL_PERMISSIONS = new Set([
37
+ 'bpm:*',
38
+ 'bpm.workflow.read_all',
39
+ 'bpm:workflow:read_all',
40
+ 'workflow.read_all',
41
+ ]);
42
+ let WorkflowEngineService = class WorkflowEngineService {
43
+ constructor(approvalInstanceRepository, workflowTokenRepository, taskRepository, taskCandidateRepository, taskDecisionRepository, notificationRepository, activityLogRepository, approvalTemplateRepository, approvalTemplateVersionRepository, formDefinitionVersionRepository, attachmentService, conditionService, delegationService, notificationService, signatureService, serviceTaskDispatcher = new workflow_service_task_dispatcher_token_1.DefaultWorkflowServiceTaskDispatcher()) {
44
+ this.approvalInstanceRepository = approvalInstanceRepository;
45
+ this.workflowTokenRepository = workflowTokenRepository;
46
+ this.taskRepository = taskRepository;
47
+ this.taskCandidateRepository = taskCandidateRepository;
48
+ this.taskDecisionRepository = taskDecisionRepository;
49
+ this.notificationRepository = notificationRepository;
50
+ this.activityLogRepository = activityLogRepository;
51
+ this.approvalTemplateRepository = approvalTemplateRepository;
52
+ this.approvalTemplateVersionRepository = approvalTemplateVersionRepository;
53
+ this.formDefinitionVersionRepository = formDefinitionVersionRepository;
54
+ this.attachmentService = attachmentService;
55
+ this.conditionService = conditionService;
56
+ this.delegationService = delegationService;
57
+ this.notificationService = notificationService;
58
+ this.signatureService = signatureService;
59
+ this.serviceTaskDispatcher = serviceTaskDispatcher;
60
+ }
61
+ async submitApprovalInstance(input) {
62
+ const formData = parseJsonObject(input.formDataJson, 'formDataJson');
63
+ const template = await this.getTemplateOrThrow(input.templateId);
64
+ if (!template.currentVersionId) {
65
+ throw new common_1.ConflictException('Approval template does not have a published version');
66
+ }
67
+ const templateVersion = await this.getPublishedTemplateVersionOrThrow(template.currentVersionId);
68
+ const formDefinitionVersion = await this.getPublishedFormVersionOrThrow(templateVersion.formDefinitionVersionId);
69
+ validateSubmittedFormData(formDefinitionVersion.schema, formData);
70
+ const initiatorMetadataSnapshot = input.initiatorMetadataSnapshotJson
71
+ ? parseJsonObject(input.initiatorMetadataSnapshotJson, 'initiatorMetadataSnapshotJson')
72
+ : await this.readDefaultInitiatorMetadataSnapshot(input.initiatorMemberId);
73
+ if (!this.conditionService.evaluateBoolean(templateVersion.initiatorPolicyCel, buildInitiatorPolicyContext(initiatorMetadataSnapshot), 'initiatorPolicyCel')) {
74
+ throw new common_1.ConflictException('Current member is not allowed to start this approval template');
75
+ }
76
+ const startedAt = new Date();
77
+ const startNode = templateVersion.workflowDefinition.nodes.find((node) => node.type === 'startEvent');
78
+ if (!startNode) {
79
+ throw new common_1.BadRequestException('Published workflow does not include a startEvent');
80
+ }
81
+ return this.approvalInstanceRepository.manager.transaction(async (manager) => {
82
+ const instanceRepository = manager.getRepository(approval_instance_entity_1.ApprovalInstanceEntity);
83
+ const tokenRepository = manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity);
84
+ const activityRepository = manager.getRepository(activity_log_entity_1.ActivityLogEntity);
85
+ const instance = await instanceRepository.save(instanceRepository.create({
86
+ completedAt: null,
87
+ formData,
88
+ formDefinitionSnapshot: {
89
+ formDefinitionVersionId: formDefinitionVersion.id,
90
+ schema: formDefinitionVersion.schema,
91
+ uiSchema: formDefinitionVersion.uiSchema,
92
+ version: formDefinitionVersion.version,
93
+ },
94
+ initiatorMemberId: input.initiatorMemberId,
95
+ initiatorMetadataSnapshot,
96
+ startedAt,
97
+ state: workflow_engine_enums_1.ApprovalInstanceStateEnum.RUNNING,
98
+ templateId: template.id,
99
+ templateVersionId: templateVersion.id,
100
+ title: input.title?.trim() || template.name,
101
+ workflowSnapshot: templateVersion.workflowDefinition,
102
+ }));
103
+ await this.attachmentService.bindFormDataAttachmentsToInstance(manager, {
104
+ formData,
105
+ instanceId: instance.id,
106
+ });
107
+ const token = await tokenRepository.save(tokenRepository.create({
108
+ consumedAt: null,
109
+ currentNodeId: startNode.id,
110
+ instanceId: instance.id,
111
+ parentTokenId: null,
112
+ status: workflow_engine_enums_1.WorkflowTokenStatusEnum.ACTIVE,
113
+ }));
114
+ await activityRepository.save([
115
+ activityRepository.create({
116
+ actorMemberId: input.initiatorMemberId,
117
+ eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.INSTANCE_STARTED,
118
+ instanceId: instance.id,
119
+ nodeId: startNode.id,
120
+ payload: {
121
+ templateId: template.id,
122
+ templateVersionId: templateVersion.id,
123
+ },
124
+ taskId: null,
125
+ }),
126
+ activityRepository.create({
127
+ actorMemberId: null,
128
+ eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TOKEN_CREATED,
129
+ instanceId: instance.id,
130
+ nodeId: startNode.id,
131
+ payload: {
132
+ tokenId: token.id,
133
+ },
134
+ taskId: null,
135
+ }),
136
+ ]);
137
+ await this.processRunningInstance(manager, instance);
138
+ return instance;
139
+ });
140
+ }
141
+ async processInstance(instanceId) {
142
+ await this.approvalInstanceRepository.manager.transaction(async (manager) => {
143
+ await manager.query('SELECT pg_advisory_xact_lock(hashtext($1))', [
144
+ instanceId,
145
+ ]);
146
+ const instance = await manager
147
+ .getRepository(approval_instance_entity_1.ApprovalInstanceEntity)
148
+ .findOne({ where: { id: instanceId } });
149
+ if (!instance) {
150
+ throw new common_1.NotFoundException(`Approval instance ${instanceId} was not found`);
151
+ }
152
+ await manager.getRepository(activity_log_entity_1.ActivityLogEntity).save(manager.getRepository(activity_log_entity_1.ActivityLogEntity).create({
153
+ actorMemberId: null,
154
+ eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.ENGINE_PROCESS_REQUESTED,
155
+ instanceId,
156
+ nodeId: null,
157
+ payload: {
158
+ state: instance.state,
159
+ },
160
+ taskId: null,
161
+ }));
162
+ if (instance.state !== workflow_engine_enums_1.ApprovalInstanceStateEnum.RUNNING) {
163
+ return;
164
+ }
165
+ await this.processRunningInstance(manager, instance);
166
+ });
167
+ }
168
+ async decideTask(input) {
169
+ return this.approvalInstanceRepository.manager.transaction(async (manager) => {
170
+ const taskRepository = manager.getRepository(task_entity_1.TaskEntity);
171
+ const taskCandidateRepository = manager.getRepository(task_candidate_entity_1.TaskCandidateEntity);
172
+ const taskDecisionRepository = manager.getRepository(task_decision_entity_1.TaskDecisionEntity);
173
+ const activityRepository = manager.getRepository(activity_log_entity_1.ActivityLogEntity);
174
+ const tokenRepository = manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity);
175
+ const instanceRepository = manager.getRepository(approval_instance_entity_1.ApprovalInstanceEntity);
176
+ const task = await taskRepository.findOne({
177
+ where: { id: input.taskId },
178
+ });
179
+ if (!task) {
180
+ throw new common_1.NotFoundException(`Task ${input.taskId} was not found`);
181
+ }
182
+ await manager.query('SELECT pg_advisory_xact_lock(hashtext($1))', [
183
+ task.instanceId,
184
+ ]);
185
+ if (task.status !== workflow_engine_enums_1.TaskStatusEnum.PENDING &&
186
+ task.status !== workflow_engine_enums_1.TaskStatusEnum.IN_PROGRESS) {
187
+ throw new common_1.ConflictException(`Task ${task.id} is not pending`);
188
+ }
189
+ const taskCandidates = await taskCandidateRepository.find({
190
+ where: { taskId: task.id },
191
+ });
192
+ const actorCandidate = taskCandidates.find((candidate) => candidate.memberId === input.decidedByMemberId);
193
+ const isDirectAssignee = task.assigneeMemberId === input.decidedByMemberId;
194
+ if (!isDirectAssignee && !actorCandidate) {
195
+ throw new common_1.ConflictException(`Task ${task.id} is assigned to another member`);
196
+ }
197
+ if (actorCandidate &&
198
+ actorCandidate.status !== workflow_engine_enums_1.TaskCandidateStatusEnum.PENDING &&
199
+ actorCandidate.status !== workflow_engine_enums_1.TaskCandidateStatusEnum.CLAIMED) {
200
+ throw new common_1.ConflictException(`Task ${task.id} was already decided by this member`);
201
+ }
202
+ if (input.action !== workflow_engine_enums_1.TaskDecisionActionEnum.APPROVED &&
203
+ input.action !== workflow_engine_enums_1.TaskDecisionActionEnum.REJECTED &&
204
+ input.action !== workflow_engine_enums_1.TaskDecisionActionEnum.RETURNED &&
205
+ input.action !== workflow_engine_enums_1.TaskDecisionActionEnum.TRANSFERRED) {
206
+ throw new common_1.ConflictException(`Task decision action ${input.action} is not supported yet`);
207
+ }
208
+ const decisionComment = input.comment?.trim() || null;
209
+ if (input.action === workflow_engine_enums_1.TaskDecisionActionEnum.REJECTED &&
210
+ !decisionComment) {
211
+ throw new common_1.BadRequestException('Reject decision comment is required');
212
+ }
213
+ const transferToMemberId = input.transferToMemberId?.trim() || null;
214
+ if (input.action === workflow_engine_enums_1.TaskDecisionActionEnum.TRANSFERRED &&
215
+ !transferToMemberId) {
216
+ throw new common_1.BadRequestException('Transfer target member is required');
217
+ }
218
+ if (input.action === workflow_engine_enums_1.TaskDecisionActionEnum.TRANSFERRED &&
219
+ transferToMemberId === input.decidedByMemberId) {
220
+ throw new common_1.ConflictException('Transfer target must be a different member');
221
+ }
222
+ const instance = await instanceRepository.findOne({
223
+ where: { id: task.instanceId },
224
+ });
225
+ if (!instance) {
226
+ throw new common_1.NotFoundException(`Approval instance ${task.instanceId} was not found`);
227
+ }
228
+ if (instance.state !== workflow_engine_enums_1.ApprovalInstanceStateEnum.RUNNING) {
229
+ throw new common_1.ConflictException(`Approval instance ${instance.id} is not running`);
230
+ }
231
+ const returnToNodeId = input.action === workflow_engine_enums_1.TaskDecisionActionEnum.RETURNED
232
+ ? readReturnTargetNodeId(instance.workflowSnapshot, task.nodeId, input.returnToNodeId)
233
+ : null;
234
+ const decidedAt = new Date();
235
+ const claimedTask = await taskRepository.save({
236
+ ...task,
237
+ assigneeMemberId: task.assigneeMemberId ?? input.decidedByMemberId,
238
+ openedAt: task.openedAt ?? decidedAt,
239
+ originalAssigneeMemberId: task.originalAssigneeMemberId ??
240
+ actorCandidate?.originalMemberId ??
241
+ input.decidedByMemberId,
242
+ status: workflow_engine_enums_1.TaskStatusEnum.IN_PROGRESS,
243
+ });
244
+ const claimedCandidate = actorCandidate
245
+ ? await taskCandidateRepository.save({
246
+ ...actorCandidate,
247
+ claimedAt: actorCandidate.claimedAt ?? decidedAt,
248
+ status: workflow_engine_enums_1.TaskCandidateStatusEnum.CLAIMED,
249
+ })
250
+ : null;
251
+ const signature = await this.signatureService.signTaskDecision(manager, {
252
+ action: input.action,
253
+ comment: decisionComment,
254
+ decidedAt,
255
+ instance,
256
+ returnToNodeId,
257
+ signerMemberId: input.decidedByMemberId,
258
+ task: claimedTask,
259
+ transferToMemberId,
260
+ });
261
+ const decision = await taskDecisionRepository.save(taskDecisionRepository.create({
262
+ action: input.action,
263
+ comment: decisionComment,
264
+ decidedAt,
265
+ decidedByMemberId: input.decidedByMemberId,
266
+ returnToNodeId,
267
+ signatureId: signature.id,
268
+ taskId: task.id,
269
+ transferToMemberId,
270
+ }));
271
+ if (input.action === workflow_engine_enums_1.TaskDecisionActionEnum.TRANSFERRED) {
272
+ await this.transferTask(manager, instance, claimedTask, decision, signature, transferToMemberId, decisionComment, decidedAt, claimedCandidate);
273
+ return decision;
274
+ }
275
+ const nextCandidateRows = await this.markTaskCandidateDecision({
276
+ action: input.action,
277
+ candidate: claimedCandidate,
278
+ candidates: taskCandidates,
279
+ decidedAt,
280
+ manager,
281
+ task: claimedTask,
282
+ });
283
+ const shouldCompleteTask = shouldCompleteTaskAfterDecision({
284
+ action: input.action,
285
+ candidates: nextCandidateRows,
286
+ decisionPolicy: readDecisionPolicySnapshot(claimedTask),
287
+ });
288
+ const completedTask = shouldCompleteTask
289
+ ? await taskRepository.save({
290
+ ...claimedTask,
291
+ completedAt: decidedAt,
292
+ status: workflow_engine_enums_1.TaskStatusEnum.COMPLETED,
293
+ })
294
+ : await taskRepository.save({
295
+ ...claimedTask,
296
+ status: workflow_engine_enums_1.TaskStatusEnum.IN_PROGRESS,
297
+ });
298
+ await activityRepository.save(activityRepository.create({
299
+ actorMemberId: input.decidedByMemberId,
300
+ eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TASK_DECIDED,
301
+ instanceId: instance.id,
302
+ nodeId: claimedTask.nodeId,
303
+ payload: {
304
+ action: input.action,
305
+ comment: decisionComment,
306
+ decisionId: decision.id,
307
+ signatureId: signature.id,
308
+ signedPayloadHash: signature.signedPayloadHash,
309
+ },
310
+ taskId: completedTask.id,
311
+ }));
312
+ if (input.action === workflow_engine_enums_1.TaskDecisionActionEnum.APPROVED &&
313
+ shouldCompleteTask) {
314
+ const token = await tokenRepository.findOne({
315
+ where: { id: claimedTask.tokenId },
316
+ });
317
+ if (!token) {
318
+ throw new common_1.NotFoundException(`Workflow token ${claimedTask.tokenId} was not found`);
319
+ }
320
+ if (token.status !== workflow_engine_enums_1.WorkflowTokenStatusEnum.WAITING) {
321
+ throw new common_1.ConflictException(`Workflow token ${token.id} is not waiting for a task decision`);
322
+ }
323
+ const activeToken = await tokenRepository.save({
324
+ ...token,
325
+ consumedAt: null,
326
+ status: workflow_engine_enums_1.WorkflowTokenStatusEnum.ACTIVE,
327
+ });
328
+ await this.advanceTokenToOutgoingNodes(manager, instance, activeToken, readWorkflowNodeOrThrow(instance.workflowSnapshot, claimedTask.nodeId));
329
+ await this.processRunningInstance(manager, instance);
330
+ return decision;
331
+ }
332
+ if (input.action === workflow_engine_enums_1.TaskDecisionActionEnum.APPROVED) {
333
+ return decision;
334
+ }
335
+ if (input.action === workflow_engine_enums_1.TaskDecisionActionEnum.RETURNED) {
336
+ await this.returnInstanceToNode(manager, instance, completedTask, returnToNodeId, decidedAt);
337
+ return decision;
338
+ }
339
+ await this.rejectInstance(manager, instance, completedTask, decidedAt);
340
+ return decision;
341
+ });
342
+ }
343
+ async cancelApprovalInstance(input) {
344
+ return this.approvalInstanceRepository.manager.transaction(async (manager) => {
345
+ await manager.query('SELECT pg_advisory_xact_lock(hashtext($1))', [
346
+ input.instanceId,
347
+ ]);
348
+ const instanceRepository = manager.getRepository(approval_instance_entity_1.ApprovalInstanceEntity);
349
+ const instance = await instanceRepository.findOne({
350
+ where: { id: input.instanceId },
351
+ });
352
+ if (!instance) {
353
+ throw new common_1.NotFoundException(`Approval instance ${input.instanceId} was not found`);
354
+ }
355
+ if (instance.initiatorMemberId !== input.cancelledByMemberId) {
356
+ throw new common_1.ConflictException(`Approval instance ${instance.id} can only be cancelled by its initiator`);
357
+ }
358
+ if (instance.state !== workflow_engine_enums_1.ApprovalInstanceStateEnum.RUNNING &&
359
+ instance.state !== workflow_engine_enums_1.ApprovalInstanceStateEnum.RETURNED) {
360
+ throw new common_1.ConflictException(`Approval instance ${instance.id} is not cancellable`);
361
+ }
362
+ const cancelledAt = new Date();
363
+ await this.consumeOpenRuntimeState(manager, instance, cancelledAt, workflow_engine_enums_1.TaskStatusEnum.CANCELLED);
364
+ const cancelledInstance = await instanceRepository.save({
365
+ ...instance,
366
+ completedAt: cancelledAt,
367
+ state: workflow_engine_enums_1.ApprovalInstanceStateEnum.CANCELLED,
368
+ });
369
+ await manager.getRepository(activity_log_entity_1.ActivityLogEntity).save(manager.getRepository(activity_log_entity_1.ActivityLogEntity).create({
370
+ actorMemberId: input.cancelledByMemberId,
371
+ eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.INSTANCE_CANCELLED,
372
+ instanceId: instance.id,
373
+ nodeId: null,
374
+ payload: {
375
+ comment: input.comment?.trim() || null,
376
+ },
377
+ taskId: null,
378
+ }));
379
+ return cancelledInstance;
380
+ });
381
+ }
382
+ async resubmitApprovalInstance(input) {
383
+ const formData = parseJsonObject(input.formDataJson, 'formDataJson');
384
+ return this.approvalInstanceRepository.manager.transaction(async (manager) => {
385
+ await manager.query('SELECT pg_advisory_xact_lock(hashtext($1))', [
386
+ input.instanceId,
387
+ ]);
388
+ const instanceRepository = manager.getRepository(approval_instance_entity_1.ApprovalInstanceEntity);
389
+ const instance = await instanceRepository.findOne({
390
+ where: { id: input.instanceId },
391
+ });
392
+ if (!instance) {
393
+ throw new common_1.NotFoundException(`Approval instance ${input.instanceId} was not found`);
394
+ }
395
+ if (instance.initiatorMemberId !== input.initiatorMemberId) {
396
+ throw new common_1.ConflictException(`Approval instance ${instance.id} can only be resubmitted by its initiator`);
397
+ }
398
+ if (instance.state !== workflow_engine_enums_1.ApprovalInstanceStateEnum.RETURNED) {
399
+ throw new common_1.ConflictException(`Approval instance ${instance.id} is not returned`);
400
+ }
401
+ validateSubmittedFormData(readFormDefinitionSnapshotSchema(instance.formDefinitionSnapshot), formData);
402
+ const resubmittedInstance = await instanceRepository.save({
403
+ ...instance,
404
+ completedAt: null,
405
+ formData,
406
+ state: workflow_engine_enums_1.ApprovalInstanceStateEnum.RUNNING,
407
+ title: input.title?.trim() || instance.title,
408
+ });
409
+ await this.attachmentService.bindFormDataAttachmentsToInstance(manager, {
410
+ formData,
411
+ instanceId: instance.id,
412
+ });
413
+ const returnedContext = await this.readLatestReturnedInstanceContext(manager, instance.id);
414
+ const runtimeInstance = returnedContext?.resubmitStrategy === 'FROM_RETURN_POINT'
415
+ ? await this.resumeReturnedInstanceFromReturnPoint(manager, resubmittedInstance, returnedContext, new Date())
416
+ : resubmittedInstance;
417
+ await manager.getRepository(activity_log_entity_1.ActivityLogEntity).save(manager.getRepository(activity_log_entity_1.ActivityLogEntity).create({
418
+ actorMemberId: input.initiatorMemberId,
419
+ eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.INSTANCE_RESUBMITTED,
420
+ instanceId: instance.id,
421
+ nodeId: null,
422
+ payload: {
423
+ resubmitStrategy: returnedContext?.resubmitStrategy ?? 'RESTART',
424
+ },
425
+ taskId: null,
426
+ }));
427
+ await this.processRunningInstance(manager, runtimeInstance);
428
+ return runtimeInstance;
429
+ });
430
+ }
431
+ dryRunApprovalWorkflow(input) {
432
+ const workflowDefinition = parseWorkflowDefinition(input.workflowDefinitionJson);
433
+ const formData = parseJsonObject(input.formDataJson, 'formDataJson');
434
+ const initiatorMetadataSnapshot = input.initiatorMetadataSnapshotJson
435
+ ? parseJsonObject(input.initiatorMetadataSnapshotJson, 'initiatorMetadataSnapshotJson')
436
+ : readDefaultInitiatorMetadataSnapshot(input.initiatorMemberId);
437
+ try {
438
+ return Object.assign(new workflow_dry_run_object_1.WorkflowDryRunResultObject(), {
439
+ errors: [],
440
+ steps: this.simulateWorkflow({
441
+ formData,
442
+ initiatorMemberId: input.initiatorMemberId,
443
+ initiatorMetadataSnapshot,
444
+ workflowDefinition,
445
+ }),
446
+ valid: true,
447
+ });
448
+ }
449
+ catch (error) {
450
+ return Object.assign(new workflow_dry_run_object_1.WorkflowDryRunResultObject(), {
451
+ errors: [error instanceof Error ? error.message : 'Dry run failed'],
452
+ steps: [],
453
+ valid: false,
454
+ });
455
+ }
456
+ }
457
+ simulateWorkflow(input) {
458
+ const startNode = input.workflowDefinition.nodes.find((node) => node.type === 'startEvent');
459
+ if (!startNode) {
460
+ throw new common_1.BadRequestException('Workflow does not include a startEvent');
461
+ }
462
+ return this.simulateWorkflowPath({
463
+ ...input,
464
+ depth: 0,
465
+ incomingEdge: null,
466
+ lastDecision: null,
467
+ nodeId: startNode.id,
468
+ steps: [],
469
+ visitedNodeIds: new Set(),
470
+ });
471
+ }
472
+ simulateWorkflowPath(input) {
473
+ if (input.depth > MAX_PROCESSING_STEPS) {
474
+ throw new common_1.ConflictException('Dry run exceeded maximum processing steps');
475
+ }
476
+ const node = readWorkflowNodeOrThrow(input.workflowDefinition, input.nodeId);
477
+ const visitKey = `${input.nodeId}:${input.incomingEdge?.id ?? 'start'}`;
478
+ const entryCondition = readNodeEntryCondition(node);
479
+ if (input.visitedNodeIds.has(visitKey)) {
480
+ return [
481
+ ...input.steps,
482
+ createDryRunStep({
483
+ assigneeMemberId: null,
484
+ edge: input.incomingEdge,
485
+ edgeMatched: null,
486
+ edgeReason: null,
487
+ entryCondition,
488
+ entryConditionMet: null,
489
+ message: '偵測到循環路徑,已停止此分支。',
490
+ node,
491
+ status: 'STOPPED',
492
+ stepIndex: input.steps.length,
493
+ }),
494
+ ];
495
+ }
496
+ const nextVisitedNodeIds = new Set([...input.visitedNodeIds, visitKey]);
497
+ const entryConditionMet = this.conditionService.evaluateBoolean(entryCondition, buildDryRunExpressionContext(input, input.lastDecision), `workflow.nodes.${node.id}.data.entryCondition`);
498
+ const assigneeMemberId = entryConditionMet && node.type === 'userTask'
499
+ ? this.resolveDryRunAssigneeMemberId(input, node.data.approverResolver, `workflow.nodes.${node.id}.data.approverResolver`)
500
+ : null;
501
+ const nextLastDecision = entryConditionMet && node.type === 'userTask'
502
+ ? {
503
+ action: workflow_engine_enums_1.TaskDecisionActionEnum.APPROVED,
504
+ assigneeMemberId,
505
+ nodeId: node.id,
506
+ }
507
+ : input.lastDecision;
508
+ const nextSteps = [
509
+ ...input.steps,
510
+ createDryRunStep({
511
+ assigneeMemberId,
512
+ edge: input.incomingEdge,
513
+ edgeMatched: input.incomingEdge ? true : null,
514
+ edgeReason: readDryRunEdgeReason(input.incomingEdge),
515
+ entryCondition,
516
+ entryConditionMet,
517
+ message: readDryRunStepMessage(node, entryConditionMet),
518
+ node,
519
+ status: readDryRunStepStatus(node, entryConditionMet),
520
+ stepIndex: input.steps.length,
521
+ }),
522
+ ];
523
+ if (!entryConditionMet || node.type === 'endEvent') {
524
+ if (node.type === 'endEvent') {
525
+ return nextSteps;
526
+ }
527
+ return readOutgoingEdgesOrThrow(input.workflowDefinition, node.id).reduce((steps, edge) => this.simulateWorkflowPath({
528
+ ...input,
529
+ depth: input.depth + 1,
530
+ incomingEdge: edge,
531
+ lastDecision: nextLastDecision,
532
+ nodeId: edge.target,
533
+ steps,
534
+ visitedNodeIds: nextVisitedNodeIds,
535
+ }), nextSteps);
536
+ }
537
+ const outgoingEdges = this.readDryRunAvailableOutgoingEdges(input, node, nextLastDecision);
538
+ return outgoingEdges.reduce((steps, edge) => this.simulateWorkflowPath({
539
+ ...input,
540
+ depth: input.depth + 1,
541
+ incomingEdge: edge,
542
+ lastDecision: nextLastDecision,
543
+ nodeId: edge.target,
544
+ steps,
545
+ visitedNodeIds: nextVisitedNodeIds,
546
+ }), nextSteps);
547
+ }
548
+ readDryRunAvailableOutgoingEdges(input, node, lastDecision) {
549
+ if (node.type === 'endEvent') {
550
+ return [];
551
+ }
552
+ const outgoingEdges = readOutgoingEdgesOrThrow(input.workflowDefinition, node.id);
553
+ const conditionalEdges = outgoingEdges.filter((edge) => !edge.data.isDefault && edgeHasCondition(edge));
554
+ if (conditionalEdges.length === 0) {
555
+ return outgoingEdges;
556
+ }
557
+ const matchingEdges = conditionalEdges.filter((edge) => this.evaluateDryRunEdge(input, edge, lastDecision));
558
+ if (matchingEdges.length > 0) {
559
+ return node.type === 'exclusiveGateway'
560
+ ? [matchingEdges[0]]
561
+ : matchingEdges;
562
+ }
563
+ const defaultEdges = outgoingEdges.filter((edge) => edge.data.isDefault);
564
+ if (defaultEdges.length > 0) {
565
+ return node.type === 'exclusiveGateway'
566
+ ? [defaultEdges[0]]
567
+ : defaultEdges;
568
+ }
569
+ throw new common_1.ConflictException(`Workflow node ${node.id} has no matching outgoing edge`);
570
+ }
571
+ evaluateDryRunEdge(input, edge, lastDecision) {
572
+ const expression = edge.data.condition?.trim();
573
+ if (expression) {
574
+ return this.conditionService.evaluateBoolean(expression, buildDryRunExpressionContext(input, lastDecision), `workflow.edges.${edge.id}.data.condition`);
575
+ }
576
+ return (0, workflow_condition_evaluator_1.evaluateWorkflowEdgeCondition)(edge, { formData: input.formData });
577
+ }
578
+ resolveDryRunAssigneeMemberId(input, resolver, label) {
579
+ if (resolver.type === 'DIRECT') {
580
+ return readMemberIdFromValue(resolver.memberIds, label);
581
+ }
582
+ if (resolver.type === 'DYNAMIC_FORM') {
583
+ return readMemberIdFromValue(readValueAtPath(input.formData, resolver.formPath), `${label}.formPath`);
584
+ }
585
+ if (resolver.type === 'EXPRESSION') {
586
+ return readMemberIdFromValue(this.conditionService.evaluateValue(resolver.expression, buildDryRunExpressionContext(input, null), `${label}.expression`), `${label}.expression`);
587
+ }
588
+ if (resolver.type === 'POSITION') {
589
+ return `position:${resolver.positionId}`;
590
+ }
591
+ if (resolver.type === 'ORG_UNIT_MEMBER') {
592
+ return `orgUnitMember:${resolver.orgUnitId}`;
593
+ }
594
+ if (resolver.type === 'ORG_UNIT_POSITION') {
595
+ return `orgUnitPosition:${resolver.orgUnitId}:${resolver.positionId}`;
596
+ }
597
+ if (resolver.type === 'ORG_UNIT_MANAGER') {
598
+ return `orgUnitManager:${resolver.orgUnitId}`;
599
+ }
600
+ return readMemberIdFromValue(readManagerMemberIdFromInitiatorSnapshot(input.initiatorMetadataSnapshot, resolver.levelsUp), label);
601
+ }
602
+ async getApprovalInstance(id, scope) {
603
+ const instance = await this.approvalInstanceRepository.findOne({
604
+ where: { id },
605
+ });
606
+ if (!instance) {
607
+ throw new common_1.NotFoundException(`Approval instance ${id} was not found`);
608
+ }
609
+ if (scope &&
610
+ !canReadAllWorkflows(scope) &&
611
+ !(await this.isApprovalInstanceReadableByMember(instance, scope.memberId))) {
612
+ throw new common_1.NotFoundException(`Approval instance ${id} was not found`);
613
+ }
614
+ return instance;
615
+ }
616
+ async listApprovalInstances(scope, options = {}) {
617
+ const queryBuilder = await this.createFilteredApprovalInstanceQueryBuilder(scope, options);
618
+ if (!queryBuilder) {
619
+ return [];
620
+ }
621
+ queryBuilder.orderBy('"approvalInstance"."created_at"', 'DESC');
622
+ if (shouldPaginateList(options)) {
623
+ queryBuilder
624
+ .skip(readPageOffset(options.page, options.pageSize))
625
+ .take(normalizePageSize(options.pageSize));
626
+ }
627
+ return queryBuilder.getMany();
628
+ }
629
+ async countApprovalInstances(scope, options = {}) {
630
+ const queryBuilder = await this.createFilteredApprovalInstanceQueryBuilder(scope, options);
631
+ return queryBuilder ? queryBuilder.getCount() : 0;
632
+ }
633
+ async readApprovalInstancePageInfo(scope, options = {}) {
634
+ const totalCount = await this.countApprovalInstances(scope, options);
635
+ const page = normalizePage(options.page);
636
+ const pageSize = normalizePageSize(options.pageSize);
637
+ const totalPages = Math.max(Math.ceil(totalCount / pageSize), 1);
638
+ return {
639
+ hasNextPage: page < totalPages,
640
+ hasPreviousPage: page > 1,
641
+ page,
642
+ pageSize,
643
+ totalCount,
644
+ totalPages,
645
+ };
646
+ }
647
+ async createFilteredApprovalInstanceQueryBuilder(scope, options = {}) {
648
+ const queryBuilder = this.approvalInstanceRepository.createQueryBuilder('approvalInstance');
649
+ const hasReadableInstances = this.applyReadableInstanceFilter(queryBuilder, scope, options);
650
+ if (!hasReadableInstances) {
651
+ return null;
652
+ }
653
+ const templateId = normalizeText(options.templateId);
654
+ if (options.state?.length) {
655
+ queryBuilder.andWhere('"approvalInstance"."state" IN (:...states)', {
656
+ states: [...options.state],
657
+ });
658
+ }
659
+ if (templateId) {
660
+ queryBuilder.andWhere('"approvalInstance"."template_id" = :templateId', {
661
+ templateId,
662
+ });
663
+ }
664
+ applyApprovalInstanceSearchFilter(queryBuilder, options.searchText);
665
+ return queryBuilder;
666
+ }
667
+ applyReadableInstanceFilter(queryBuilder, scope, options) {
668
+ if (!scope) {
669
+ return true;
670
+ }
671
+ const view = options.view ?? workflow_engine_enums_1.ApprovalInstanceListViewEnum.ALL;
672
+ if (view === workflow_engine_enums_1.ApprovalInstanceListViewEnum.SENT) {
673
+ queryBuilder.andWhere('"approvalInstance"."initiator_member_id" = :readableMemberId', {
674
+ readableMemberId: scope.memberId,
675
+ });
676
+ return true;
677
+ }
678
+ if (view === workflow_engine_enums_1.ApprovalInstanceListViewEnum.CC) {
679
+ queryBuilder.andWhere(`
680
+ EXISTS (
681
+ SELECT 1
682
+ FROM "notifications" "readableNotification"
683
+ WHERE "readableNotification"."instance_id" = "approvalInstance"."id"
684
+ AND "readableNotification"."recipient_member_id" = :readableMemberId
685
+ )
686
+ `, {
687
+ readableMemberId: scope.memberId,
688
+ });
689
+ return true;
690
+ }
691
+ if (canReadAllWorkflows(scope)) {
692
+ return true;
693
+ }
694
+ queryBuilder.andWhere(`
695
+ (
696
+ "approvalInstance"."initiator_member_id" = :readableMemberId
697
+ OR EXISTS (
698
+ SELECT 1
699
+ FROM "tasks" "readableTask"
700
+ WHERE "readableTask"."instance_id" = "approvalInstance"."id"
701
+ AND (
702
+ "readableTask"."assignee_member_id" = :readableMemberId
703
+ OR "readableTask"."original_assignee_member_id" = :readableMemberId
704
+ )
705
+ )
706
+ OR EXISTS (
707
+ SELECT 1
708
+ FROM "task_candidates" "readableCandidate"
709
+ INNER JOIN "tasks" "readableCandidateTask"
710
+ ON "readableCandidateTask"."id" = "readableCandidate"."task_id"
711
+ WHERE "readableCandidateTask"."instance_id" = "approvalInstance"."id"
712
+ AND (
713
+ "readableCandidate"."member_id" = :readableMemberId
714
+ OR "readableCandidate"."original_member_id" = :readableMemberId
715
+ )
716
+ )
717
+ OR EXISTS (
718
+ SELECT 1
719
+ FROM "task_decisions" "readableDecision"
720
+ INNER JOIN "tasks" "readableDecisionTask"
721
+ ON "readableDecisionTask"."id" = "readableDecision"."task_id"
722
+ WHERE "readableDecisionTask"."instance_id" = "approvalInstance"."id"
723
+ AND "readableDecision"."decided_by_member_id" = :readableMemberId
724
+ )
725
+ )
726
+ `, {
727
+ readableMemberId: scope.memberId,
728
+ });
729
+ return true;
730
+ }
731
+ async readWorkflowDashboardSummary(scope, options = {}) {
732
+ const instances = (await this.listApprovalInstances(scope, {
733
+ view: workflow_engine_enums_1.ApprovalInstanceListViewEnum.ALL,
734
+ })).filter((instance) => isInstanceWithinRange(instance, options));
735
+ const instanceIds = instances.map((instance) => instance.id);
736
+ const openTasks = instanceIds.length
737
+ ? await this.taskRepository.find({
738
+ where: {
739
+ instanceId: (0, typeorm_2.In)(instanceIds),
740
+ status: (0, typeorm_2.In)([workflow_engine_enums_1.TaskStatusEnum.PENDING, workflow_engine_enums_1.TaskStatusEnum.IN_PROGRESS]),
741
+ },
742
+ })
743
+ : [];
744
+ const now = Date.now();
745
+ return {
746
+ activeInstanceCount: instances.filter((instance) => instance.state === workflow_engine_enums_1.ApprovalInstanceStateEnum.RUNNING).length,
747
+ completedInstanceCount: instances.filter((instance) => instance.state === workflow_engine_enums_1.ApprovalInstanceStateEnum.APPROVED).length,
748
+ overdueTaskCount: openTasks.filter((task) => task.slaDueAt && task.slaDueAt.getTime() <= now).length,
749
+ pendingTaskCount: openTasks.length,
750
+ rejectedInstanceCount: instances.filter((instance) => instance.state === workflow_engine_enums_1.ApprovalInstanceStateEnum.REJECTED).length,
751
+ totalInstanceCount: instances.length,
752
+ };
753
+ }
754
+ async listLaunchableApprovalTemplates(memberId) {
755
+ const templates = await this.approvalTemplateRepository.find({
756
+ order: { updatedAt: 'DESC' },
757
+ });
758
+ const currentVersionIds = templates
759
+ .map((template) => template.currentVersionId)
760
+ .filter((versionId) => Boolean(versionId));
761
+ if (!currentVersionIds.length) {
762
+ return [];
763
+ }
764
+ const versions = await this.approvalTemplateVersionRepository.find({
765
+ where: {
766
+ id: (0, typeorm_2.In)(currentVersionIds),
767
+ status: template_enums_1.ApprovalTemplateVersionStatusEnum.PUBLISHED,
768
+ },
769
+ });
770
+ const versionById = new Map(versions.map((version) => [
771
+ version.id,
772
+ version,
773
+ ]));
774
+ const initiatorMetadataSnapshot = await this.readDefaultInitiatorMetadataSnapshot(memberId);
775
+ return templates.filter((template) => {
776
+ const currentVersion = template.currentVersionId
777
+ ? (versionById.get(template.currentVersionId) ?? null)
778
+ : null;
779
+ if (!currentVersion?.formDefinitionVersionId) {
780
+ return false;
781
+ }
782
+ return this.conditionService.evaluateBoolean(currentVersion.initiatorPolicyCel, buildInitiatorPolicyContext(initiatorMetadataSnapshot), 'initiatorPolicyCel');
783
+ });
784
+ }
785
+ async listWorkflowTokens(instanceId, scope) {
786
+ await this.getApprovalInstance(instanceId, scope);
787
+ return this.workflowTokenRepository.find({
788
+ order: { createdAt: 'ASC' },
789
+ where: { instanceId },
790
+ });
791
+ }
792
+ async listTasks(instanceId, scope) {
793
+ await this.getApprovalInstance(instanceId, scope);
794
+ return this.attachTaskCandidateSummaries(await this.taskRepository.find({
795
+ order: { createdAt: 'ASC' },
796
+ where: { instanceId },
797
+ }));
798
+ }
799
+ async listInboxTasks(assigneeMemberId) {
800
+ const directTasks = await this.taskRepository.find({
801
+ order: { createdAt: 'DESC' },
802
+ where: [
803
+ {
804
+ assigneeMemberId,
805
+ status: (0, typeorm_2.In)([workflow_engine_enums_1.TaskStatusEnum.PENDING, workflow_engine_enums_1.TaskStatusEnum.IN_PROGRESS]),
806
+ },
807
+ ],
808
+ });
809
+ const candidateRows = await this.taskCandidateRepository.find({
810
+ where: {
811
+ memberId: assigneeMemberId,
812
+ status: (0, typeorm_2.In)([
813
+ workflow_engine_enums_1.TaskCandidateStatusEnum.PENDING,
814
+ workflow_engine_enums_1.TaskCandidateStatusEnum.CLAIMED,
815
+ ]),
816
+ },
817
+ });
818
+ const candidateTasks = candidateRows.length
819
+ ? await this.taskRepository.find({
820
+ order: { createdAt: 'DESC' },
821
+ where: {
822
+ id: (0, typeorm_2.In)(candidateRows.map((candidate) => candidate.taskId)),
823
+ status: (0, typeorm_2.In)([workflow_engine_enums_1.TaskStatusEnum.PENDING, workflow_engine_enums_1.TaskStatusEnum.IN_PROGRESS]),
824
+ },
825
+ })
826
+ : [];
827
+ return this.attachTaskCandidateSummaries(uniqueTasksById([...directTasks, ...candidateTasks]));
828
+ }
829
+ async listApprovalHistoryTasks(assigneeMemberId) {
830
+ const directTasks = await this.taskRepository.find({
831
+ order: { completedAt: 'DESC', createdAt: 'DESC' },
832
+ take: DEFAULT_APPROVAL_HISTORY_TASK_LIMIT,
833
+ where: { assigneeMemberId, status: workflow_engine_enums_1.TaskStatusEnum.COMPLETED },
834
+ });
835
+ const candidateRows = await this.taskCandidateRepository.find({
836
+ where: {
837
+ memberId: assigneeMemberId,
838
+ status: workflow_engine_enums_1.TaskCandidateStatusEnum.COMPLETED,
839
+ },
840
+ });
841
+ const candidateTasks = candidateRows.length
842
+ ? await this.taskRepository.find({
843
+ order: { completedAt: 'DESC', createdAt: 'DESC' },
844
+ take: DEFAULT_APPROVAL_HISTORY_TASK_LIMIT,
845
+ where: {
846
+ id: (0, typeorm_2.In)(candidateRows.map((candidate) => candidate.taskId)),
847
+ status: workflow_engine_enums_1.TaskStatusEnum.COMPLETED,
848
+ },
849
+ })
850
+ : [];
851
+ return this.attachTaskCandidateSummaries(uniqueTasksById([...directTasks, ...candidateTasks]));
852
+ }
853
+ async listTaskDecisions(taskId, scope) {
854
+ await this.assertTaskReadable(taskId, scope);
855
+ return this.taskDecisionRepository.find({
856
+ order: { decidedAt: 'ASC' },
857
+ where: { taskId },
858
+ });
859
+ }
860
+ async listTaskCandidates(taskId, scope) {
861
+ await this.assertTaskReadable(taskId, scope);
862
+ return this.taskCandidateRepository.find({
863
+ order: { createdAt: 'ASC', id: 'ASC' },
864
+ where: { taskId },
865
+ });
866
+ }
867
+ async attachTaskCandidateSummaries(tasks) {
868
+ if (!tasks.length) {
869
+ return tasks;
870
+ }
871
+ const candidates = await this.taskCandidateRepository.find({
872
+ order: { createdAt: 'ASC', id: 'ASC' },
873
+ where: { taskId: (0, typeorm_2.In)(tasks.map((task) => task.id)) },
874
+ });
875
+ const candidatesByTaskId = candidates.reduce((currentMap, candidate) => {
876
+ const nextMap = new Map(currentMap);
877
+ const memberIds = nextMap.get(candidate.taskId) ?? [];
878
+ nextMap.set(candidate.taskId, [...memberIds, candidate.memberId]);
879
+ return nextMap;
880
+ }, new Map());
881
+ return tasks.map((task) => Object.assign(new task_entity_1.TaskEntity(), task, {
882
+ candidateMemberIds: candidatesByTaskId.get(task.id) ?? [],
883
+ }));
884
+ }
885
+ async readDefaultInitiatorMetadataSnapshot(memberId) {
886
+ const today = toDateOnlyString(new Date());
887
+ const memberships = (await this.approvalInstanceRepository.manager
888
+ .getRepository(membership_entity_1.MembershipEntity)
889
+ .find({ where: { memberId } }))
890
+ .filter((membership) => isDateRangeActive(membership, today))
891
+ .sort(compareMembership);
892
+ const primaryMembership = memberships[0] ?? null;
893
+ const positionIds = uniqueTexts(memberships
894
+ .map((membership) => membership.positionId)
895
+ .filter((positionId) => Boolean(positionId)));
896
+ return {
897
+ customFields: {},
898
+ memberId,
899
+ orgUnitIds: uniqueTexts(memberships.map((membership) => membership.orgUnitId)),
900
+ positionId: primaryMembership?.positionId ?? null,
901
+ positionIds,
902
+ primaryOrgUnitId: primaryMembership?.orgUnitId ?? null,
903
+ };
904
+ }
905
+ async listActivityLogs(instanceId, scope) {
906
+ await this.getApprovalInstance(instanceId, scope);
907
+ return this.activityLogRepository.find({
908
+ order: { createdAt: 'ASC' },
909
+ where: { instanceId },
910
+ });
911
+ }
912
+ async assertTaskReadable(taskId, scope) {
913
+ const task = await this.taskRepository.findOne({ where: { id: taskId } });
914
+ if (!task) {
915
+ throw new common_1.NotFoundException(`Task ${taskId} was not found`);
916
+ }
917
+ await this.getApprovalInstance(task.instanceId, scope);
918
+ return task;
919
+ }
920
+ async isApprovalInstanceReadableByMember(instance, memberId) {
921
+ if (instance.initiatorMemberId === memberId) {
922
+ return true;
923
+ }
924
+ const tasks = await this.taskRepository.find({
925
+ where: { instanceId: instance.id },
926
+ });
927
+ if (tasks.some((task) => isTaskRelatedToMember(task, memberId))) {
928
+ return true;
929
+ }
930
+ const taskIds = tasks.map((task) => task.id);
931
+ if (!taskIds.length) {
932
+ return false;
933
+ }
934
+ const candidates = await this.taskCandidateRepository.find({
935
+ where: { taskId: (0, typeorm_2.In)(taskIds) },
936
+ });
937
+ if (candidates.some((candidate) => candidate.memberId === memberId ||
938
+ candidate.originalMemberId === memberId)) {
939
+ return true;
940
+ }
941
+ const decisions = await this.taskDecisionRepository.find({
942
+ where: { taskId: (0, typeorm_2.In)(taskIds) },
943
+ });
944
+ return decisions.some((decision) => decision.decidedByMemberId === memberId);
945
+ }
946
+ async processActiveToken(manager, instance, token) {
947
+ const node = readWorkflowNodeOrThrow(instance.workflowSnapshot, token.currentNodeId);
948
+ if (!(await this.isNodeEntryReady(manager, instance, token, node))) {
949
+ return;
950
+ }
951
+ if (!this.isNodeEntryConditionMet(instance, node)) {
952
+ await this.skipNodeByEntryCondition(manager, instance, token, node);
953
+ return;
954
+ }
955
+ if (node.type === 'startEvent') {
956
+ await this.advanceTokenToOutgoingNodes(manager, instance, token, node);
957
+ return;
958
+ }
959
+ if (node.type === 'endEvent') {
960
+ await this.completeTokenAtEndNode(manager, instance, token, node);
961
+ return;
962
+ }
963
+ if (node.type === 'userTask') {
964
+ await this.createUserTaskForToken(manager, instance, token, node);
965
+ return;
966
+ }
967
+ if (node.type === 'serviceTask') {
968
+ await this.executeServiceTask(manager, instance, token, node);
969
+ return;
970
+ }
971
+ if (node.type === 'exclusiveGateway') {
972
+ await this.processExclusiveGateway(manager, instance, token, node);
973
+ return;
974
+ }
975
+ if (node.type === 'parallelGateway') {
976
+ await this.advanceTokenToOutgoingNodes(manager, instance, token, node);
977
+ return;
978
+ }
979
+ return;
980
+ }
981
+ async processRunningInstance(manager, instance) {
982
+ for (let step = 0; step < MAX_PROCESSING_STEPS; step += 1) {
983
+ const token = await manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity).findOne({
984
+ order: { createdAt: 'ASC' },
985
+ where: {
986
+ instanceId: instance.id,
987
+ status: workflow_engine_enums_1.WorkflowTokenStatusEnum.ACTIVE,
988
+ },
989
+ });
990
+ if (!token) {
991
+ await this.completeInstanceIfNoOpenRuntimeState(manager, instance);
992
+ return;
993
+ }
994
+ await this.processActiveToken(manager, instance, token);
995
+ }
996
+ throw new common_1.ConflictException(`Approval instance ${instance.id} exceeded maximum processing steps`);
997
+ }
998
+ async completeInstanceIfNoOpenRuntimeState(manager, instance) {
999
+ if (instance.state !== workflow_engine_enums_1.ApprovalInstanceStateEnum.RUNNING) {
1000
+ return;
1001
+ }
1002
+ const waitingTokens = await manager
1003
+ .getRepository(workflow_token_entity_1.WorkflowTokenEntity)
1004
+ .find({
1005
+ where: {
1006
+ instanceId: instance.id,
1007
+ status: workflow_engine_enums_1.WorkflowTokenStatusEnum.WAITING,
1008
+ },
1009
+ });
1010
+ if (waitingTokens.length > 0) {
1011
+ return;
1012
+ }
1013
+ const completedAt = new Date();
1014
+ const completedInstance = await manager
1015
+ .getRepository(approval_instance_entity_1.ApprovalInstanceEntity)
1016
+ .save({
1017
+ ...instance,
1018
+ completedAt,
1019
+ state: workflow_engine_enums_1.ApprovalInstanceStateEnum.APPROVED,
1020
+ });
1021
+ await this.notificationService.createInstanceCompletedNotification({
1022
+ instance: completedInstance,
1023
+ manager,
1024
+ });
1025
+ }
1026
+ async isNodeEntryReady(manager, instance, token, node) {
1027
+ if (node.type === 'startEvent') {
1028
+ return true;
1029
+ }
1030
+ const incomingEdges = readIncomingEdges(instance.workflowSnapshot, node.id);
1031
+ if (incomingEdges.length < 2) {
1032
+ return true;
1033
+ }
1034
+ const tokenRepository = manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity);
1035
+ const activityRepository = manager.getRepository(activity_log_entity_1.ActivityLogEntity);
1036
+ const activeOrWaitingTokens = (await tokenRepository.find({
1037
+ where: { instanceId: instance.id },
1038
+ })).filter((candidate) => candidate.currentNodeId === node.id &&
1039
+ (candidate.status === workflow_engine_enums_1.WorkflowTokenStatusEnum.ACTIVE ||
1040
+ candidate.status === workflow_engine_enums_1.WorkflowTokenStatusEnum.WAITING));
1041
+ const arrivedTokens = activeOrWaitingTokens.some((candidate) => candidate.id === token.id)
1042
+ ? activeOrWaitingTokens
1043
+ : [...activeOrWaitingTokens, token];
1044
+ if ((node.data.triggerMode ?? 'AND') === 'OR') {
1045
+ await this.cancelAlternativeTokensForOrTrigger(manager, instance, token, node.id);
1046
+ await this.consumeJoinedTokens(manager, arrivedTokens.filter((candidate) => candidate.id !== token.id));
1047
+ return true;
1048
+ }
1049
+ const pendingTokensThatMayReachNode = (await tokenRepository.find({
1050
+ where: { instanceId: instance.id },
1051
+ })).filter((candidate) => candidate.id !== token.id &&
1052
+ !arrivedTokens.some((arrivedToken) => arrivedToken.id === candidate.id) &&
1053
+ (candidate.status === workflow_engine_enums_1.WorkflowTokenStatusEnum.ACTIVE ||
1054
+ candidate.status === workflow_engine_enums_1.WorkflowTokenStatusEnum.WAITING) &&
1055
+ hasPathToNode(instance.workflowSnapshot, candidate.currentNodeId, node.id));
1056
+ if (arrivedTokens.length < incomingEdges.length &&
1057
+ pendingTokensThatMayReachNode.length > 0) {
1058
+ await tokenRepository.save({
1059
+ ...token,
1060
+ status: workflow_engine_enums_1.WorkflowTokenStatusEnum.WAITING,
1061
+ });
1062
+ await activityRepository.save(activityRepository.create({
1063
+ actorMemberId: null,
1064
+ eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TOKEN_ADVANCED,
1065
+ instanceId: instance.id,
1066
+ nodeId: node.id,
1067
+ payload: {
1068
+ arrivedCount: arrivedTokens.length,
1069
+ requiredCount: incomingEdges.length,
1070
+ tokenId: token.id,
1071
+ triggerMode: 'AND',
1072
+ },
1073
+ taskId: null,
1074
+ }));
1075
+ return false;
1076
+ }
1077
+ await this.consumeJoinedTokens(manager, arrivedTokens.filter((candidate) => candidate.id !== token.id));
1078
+ return true;
1079
+ }
1080
+ async advanceTokenToOutgoingNodes(manager, instance, token, node) {
1081
+ const edges = await this.readAvailableOutgoingEdges(manager, instance, node);
1082
+ if (edges.length === 1) {
1083
+ await this.advanceTokenAlongEdge(manager, instance, token, node, edges[0]);
1084
+ return;
1085
+ }
1086
+ await this.forkTokenAlongEdges(manager, instance, token, node, edges);
1087
+ }
1088
+ isNodeEntryConditionMet(instance, node) {
1089
+ const entryCondition = readNodeEntryCondition(node);
1090
+ return this.conditionService.evaluateBoolean(entryCondition, buildWorkflowExpressionContext(instance), `workflow.nodes.${node.id}.data.entryCondition`);
1091
+ }
1092
+ async skipNodeByEntryCondition(manager, instance, token, node) {
1093
+ await manager.getRepository(activity_log_entity_1.ActivityLogEntity).save(manager.getRepository(activity_log_entity_1.ActivityLogEntity).create({
1094
+ actorMemberId: null,
1095
+ eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TOKEN_ADVANCED,
1096
+ instanceId: instance.id,
1097
+ nodeId: node.id,
1098
+ payload: {
1099
+ skippedByEntryCondition: true,
1100
+ tokenId: token.id,
1101
+ },
1102
+ taskId: null,
1103
+ }));
1104
+ await this.advanceTokenToOutgoingNodes(manager, instance, token, node);
1105
+ }
1106
+ async readAvailableOutgoingEdges(manager, instance, node) {
1107
+ const outgoingEdges = readOutgoingEdgesOrThrow(instance.workflowSnapshot, node.id);
1108
+ const conditionalEdges = outgoingEdges.filter((edge) => !edge.data.isDefault && edgeHasCondition(edge));
1109
+ if (conditionalEdges.length === 0) {
1110
+ return outgoingEdges;
1111
+ }
1112
+ const matchingEdges = (await Promise.all(conditionalEdges.map(async (edge) => (await this.evaluateWorkflowEdge(manager, instance, node, edge))
1113
+ ? edge
1114
+ : null))).filter((edge) => edge !== null);
1115
+ if (matchingEdges.length > 0) {
1116
+ return node.type === 'exclusiveGateway'
1117
+ ? [matchingEdges[0]]
1118
+ : matchingEdges;
1119
+ }
1120
+ const defaultEdges = outgoingEdges.filter((edge) => edge.data.isDefault);
1121
+ if (defaultEdges.length > 0) {
1122
+ return node.type === 'exclusiveGateway'
1123
+ ? [defaultEdges[0]]
1124
+ : defaultEdges;
1125
+ }
1126
+ throw new common_1.ConflictException(`Workflow node ${node.id} has no matching outgoing edge`);
1127
+ }
1128
+ async evaluateWorkflowEdge(manager, instance, node, edge) {
1129
+ const expression = edge.data.condition?.trim();
1130
+ if (expression) {
1131
+ return this.conditionService.evaluateBoolean(expression, {
1132
+ ...buildWorkflowExpressionContext(instance),
1133
+ lastDecision: await this.readLastDecisionContext(manager, instance.id, node.id),
1134
+ }, `workflow.edges.${edge.id}.data.condition`);
1135
+ }
1136
+ return (0, workflow_condition_evaluator_1.evaluateWorkflowEdgeCondition)(edge, { formData: instance.formData });
1137
+ }
1138
+ async readLastDecisionContext(manager, instanceId, nodeId) {
1139
+ const tasks = await manager.getRepository(task_entity_1.TaskEntity).find({
1140
+ where: { instanceId, nodeId },
1141
+ });
1142
+ const taskIds = tasks.map((task) => task.id);
1143
+ if (taskIds.length === 0) {
1144
+ return null;
1145
+ }
1146
+ const decision = (await manager.getRepository(task_decision_entity_1.TaskDecisionEntity).find({
1147
+ order: { decidedAt: 'DESC' },
1148
+ where: { taskId: (0, typeorm_2.In)(taskIds) },
1149
+ }))[0];
1150
+ if (!decision) {
1151
+ return null;
1152
+ }
1153
+ return {
1154
+ action: decision.action,
1155
+ comment: decision.comment,
1156
+ decidedAt: decision.decidedAt.toISOString(),
1157
+ decidedByMemberId: decision.decidedByMemberId,
1158
+ returnToNodeId: decision.returnToNodeId,
1159
+ taskId: decision.taskId,
1160
+ transferToMemberId: decision.transferToMemberId,
1161
+ };
1162
+ }
1163
+ async advanceTokenAlongEdge(manager, instance, token, node, edge) {
1164
+ const tokenRepository = manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity);
1165
+ const activityRepository = manager.getRepository(activity_log_entity_1.ActivityLogEntity);
1166
+ const nextToken = await tokenRepository.save({
1167
+ ...token,
1168
+ currentNodeId: edge.target,
1169
+ });
1170
+ await activityRepository.save(activityRepository.create({
1171
+ actorMemberId: null,
1172
+ eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TOKEN_ADVANCED,
1173
+ instanceId: instance.id,
1174
+ nodeId: edge.target,
1175
+ payload: {
1176
+ edgeId: edge.id,
1177
+ fromNodeId: node.id,
1178
+ tokenId: nextToken.id,
1179
+ toNodeId: edge.target,
1180
+ },
1181
+ taskId: null,
1182
+ }));
1183
+ }
1184
+ async forkTokenAlongEdges(manager, instance, token, node, edges) {
1185
+ const forkedAt = new Date();
1186
+ const tokenRepository = manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity);
1187
+ const activityRepository = manager.getRepository(activity_log_entity_1.ActivityLogEntity);
1188
+ await tokenRepository.save({
1189
+ ...token,
1190
+ consumedAt: forkedAt,
1191
+ status: workflow_engine_enums_1.WorkflowTokenStatusEnum.CONSUMED,
1192
+ });
1193
+ const childTokens = await tokenRepository.save(edges.map((edge) => tokenRepository.create({
1194
+ consumedAt: null,
1195
+ currentNodeId: edge.target,
1196
+ instanceId: instance.id,
1197
+ parentTokenId: token.id,
1198
+ status: workflow_engine_enums_1.WorkflowTokenStatusEnum.ACTIVE,
1199
+ })));
1200
+ await activityRepository.save([
1201
+ activityRepository.create({
1202
+ actorMemberId: null,
1203
+ eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TOKEN_ADVANCED,
1204
+ instanceId: instance.id,
1205
+ nodeId: node.id,
1206
+ payload: {
1207
+ edgeIds: edges.map((edge) => edge.id),
1208
+ fromNodeId: node.id,
1209
+ tokenId: token.id,
1210
+ toNodeIds: edges.map((edge) => edge.target),
1211
+ },
1212
+ taskId: null,
1213
+ }),
1214
+ ...childTokens.map((childToken, index) => activityRepository.create({
1215
+ actorMemberId: null,
1216
+ eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TOKEN_CREATED,
1217
+ instanceId: instance.id,
1218
+ nodeId: childToken.currentNodeId,
1219
+ payload: {
1220
+ edgeId: edges[index]?.id,
1221
+ parentTokenId: token.id,
1222
+ tokenId: childToken.id,
1223
+ },
1224
+ taskId: null,
1225
+ })),
1226
+ ]);
1227
+ }
1228
+ async completeTokenAtEndNode(manager, instance, token, node) {
1229
+ const completedAt = new Date();
1230
+ const tokenRepository = manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity);
1231
+ const instanceRepository = manager.getRepository(approval_instance_entity_1.ApprovalInstanceEntity);
1232
+ const activityRepository = manager.getRepository(activity_log_entity_1.ActivityLogEntity);
1233
+ const endState = node.data.endState ?? 'APPROVED';
1234
+ const instanceState = endState === 'REJECTED'
1235
+ ? workflow_engine_enums_1.ApprovalInstanceStateEnum.REJECTED
1236
+ : workflow_engine_enums_1.ApprovalInstanceStateEnum.APPROVED;
1237
+ await tokenRepository.save({
1238
+ ...token,
1239
+ consumedAt: completedAt,
1240
+ status: workflow_engine_enums_1.WorkflowTokenStatusEnum.CONSUMED,
1241
+ });
1242
+ const hasOpenToken = (await tokenRepository.find({
1243
+ where: { instanceId: instance.id },
1244
+ })).some((candidate) => candidate.id !== token.id &&
1245
+ (candidate.status === workflow_engine_enums_1.WorkflowTokenStatusEnum.ACTIVE ||
1246
+ candidate.status === workflow_engine_enums_1.WorkflowTokenStatusEnum.WAITING));
1247
+ if (!hasOpenToken) {
1248
+ const completedInstance = await instanceRepository.save({
1249
+ ...instance,
1250
+ completedAt,
1251
+ state: instanceState,
1252
+ });
1253
+ if (instanceState === workflow_engine_enums_1.ApprovalInstanceStateEnum.APPROVED) {
1254
+ await this.notificationService.createInstanceCompletedNotification({
1255
+ instance: completedInstance,
1256
+ manager,
1257
+ });
1258
+ }
1259
+ }
1260
+ await activityRepository.save(activityRepository.create({
1261
+ actorMemberId: null,
1262
+ eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TOKEN_ADVANCED,
1263
+ instanceId: instance.id,
1264
+ nodeId: node.id,
1265
+ payload: {
1266
+ endState,
1267
+ instanceState,
1268
+ tokenId: token.id,
1269
+ },
1270
+ taskId: null,
1271
+ }));
1272
+ }
1273
+ async createUserTaskForToken(manager, instance, token, node) {
1274
+ const taskRepository = manager.getRepository(task_entity_1.TaskEntity);
1275
+ const taskCandidateRepository = manager.getRepository(task_candidate_entity_1.TaskCandidateEntity);
1276
+ const tokenRepository = manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity);
1277
+ const activityRepository = manager.getRepository(activity_log_entity_1.ActivityLogEntity);
1278
+ const existingTask = await taskRepository.findOne({
1279
+ where: {
1280
+ nodeId: node.id,
1281
+ tokenId: token.id,
1282
+ },
1283
+ });
1284
+ if (existingTask) {
1285
+ await tokenRepository.save({
1286
+ ...token,
1287
+ status: workflow_engine_enums_1.WorkflowTokenStatusEnum.WAITING,
1288
+ });
1289
+ return;
1290
+ }
1291
+ const candidates = await this.resolveRuntimeTaskCandidates(manager, instance, node);
1292
+ const primaryCandidate = candidates[0];
1293
+ const now = new Date();
1294
+ const task = await taskRepository.save(taskRepository.create({
1295
+ assigneeMemberId: candidates.length === 1 ? primaryCandidate.memberId : null,
1296
+ assignmentType: candidates.length === 1
1297
+ ? workflow_engine_enums_1.TaskAssignmentTypeEnum.DIRECT_MEMBER
1298
+ : workflow_engine_enums_1.TaskAssignmentTypeEnum.CANDIDATE_GROUP,
1299
+ completedAt: null,
1300
+ createdAt: now,
1301
+ decisionPolicySnapshot: node.data.decisionPolicy,
1302
+ delegationChain: candidates.length === 1 ? primaryCandidate.delegationChain : [],
1303
+ instanceId: instance.id,
1304
+ nodeId: node.id,
1305
+ openedAt: null,
1306
+ originalAssigneeMemberId: candidates.length === 1 ? primaryCandidate.originalMemberId : null,
1307
+ slaDueAt: (0, notification_service_1.calculateTaskSlaDueAt)({ node, now }),
1308
+ status: workflow_engine_enums_1.TaskStatusEnum.PENDING,
1309
+ tokenId: token.id,
1310
+ }));
1311
+ const savedCandidates = await taskCandidateRepository.save(candidates.map((candidate) => taskCandidateRepository.create({
1312
+ claimedAt: null,
1313
+ createdAt: now,
1314
+ decidedAt: null,
1315
+ delegationChain: candidate.delegationChain,
1316
+ memberId: candidate.memberId,
1317
+ originalMemberId: candidate.originalMemberId,
1318
+ sourceType: candidate.sourceType,
1319
+ status: workflow_engine_enums_1.TaskCandidateStatusEnum.PENDING,
1320
+ taskId: task.id,
1321
+ })));
1322
+ task.candidateMemberIds = savedCandidates.map((candidate) => candidate.memberId);
1323
+ const singleCandidate = savedCandidates.length === 1 ? savedCandidates[0] : null;
1324
+ await tokenRepository.save({
1325
+ ...token,
1326
+ status: workflow_engine_enums_1.WorkflowTokenStatusEnum.WAITING,
1327
+ });
1328
+ await activityRepository.save(activityRepository.create({
1329
+ actorMemberId: null,
1330
+ eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TASK_CREATED,
1331
+ instanceId: instance.id,
1332
+ nodeId: node.id,
1333
+ payload: {
1334
+ assignmentType: task.assignmentType,
1335
+ assigneeMemberId: singleCandidate?.memberId ?? null,
1336
+ candidateMemberIds: savedCandidates.map((candidate) => candidate.memberId),
1337
+ originalAssigneeMemberId: singleCandidate?.originalMemberId ?? null,
1338
+ tokenId: token.id,
1339
+ },
1340
+ taskId: task.id,
1341
+ }));
1342
+ await savedCandidates.reduce(async (previous, candidate) => {
1343
+ await previous;
1344
+ await this.notificationService.createTaskAssignedNotification({
1345
+ instance,
1346
+ manager,
1347
+ node,
1348
+ task: Object.assign(new task_entity_1.TaskEntity(), task, {
1349
+ assigneeMemberId: candidate.memberId,
1350
+ delegationChain: candidate.delegationChain,
1351
+ originalAssigneeMemberId: candidate.originalMemberId,
1352
+ }),
1353
+ });
1354
+ }, Promise.resolve());
1355
+ }
1356
+ async markTaskCandidateDecision({ action, candidate, candidates, decidedAt, manager, task, }) {
1357
+ const taskCandidateRepository = manager.getRepository(task_candidate_entity_1.TaskCandidateEntity);
1358
+ if (!candidate && candidates.length === 0 && task.assigneeMemberId) {
1359
+ const legacyCandidate = await taskCandidateRepository.save(taskCandidateRepository.create({
1360
+ claimedAt: task.openedAt ?? decidedAt,
1361
+ createdAt: task.createdAt,
1362
+ decidedAt,
1363
+ delegationChain: task.delegationChain,
1364
+ memberId: task.assigneeMemberId,
1365
+ originalMemberId: task.originalAssigneeMemberId ?? task.assigneeMemberId,
1366
+ sourceType: 'DIRECT',
1367
+ status: workflow_engine_enums_1.TaskCandidateStatusEnum.COMPLETED,
1368
+ taskId: task.id,
1369
+ }));
1370
+ return [legacyCandidate];
1371
+ }
1372
+ const completedCandidate = candidate
1373
+ ? Object.assign(new task_candidate_entity_1.TaskCandidateEntity(), candidate, {
1374
+ claimedAt: candidate.claimedAt ?? decidedAt,
1375
+ decidedAt,
1376
+ status: workflow_engine_enums_1.TaskCandidateStatusEnum.COMPLETED,
1377
+ })
1378
+ : null;
1379
+ const decisionPolicy = readDecisionPolicySnapshot(task);
1380
+ const shouldCancelOpenCandidates = action !== workflow_engine_enums_1.TaskDecisionActionEnum.APPROVED ||
1381
+ decisionPolicy.type === 'SINGLE' ||
1382
+ decisionPolicy.type === 'PARALLEL_ANY';
1383
+ const nextCandidates = candidates.map((currentCandidate) => {
1384
+ if (currentCandidate.id === completedCandidate?.id) {
1385
+ return completedCandidate;
1386
+ }
1387
+ if (shouldCancelOpenCandidates &&
1388
+ (currentCandidate.status === workflow_engine_enums_1.TaskCandidateStatusEnum.PENDING ||
1389
+ currentCandidate.status === workflow_engine_enums_1.TaskCandidateStatusEnum.CLAIMED)) {
1390
+ return Object.assign(new task_candidate_entity_1.TaskCandidateEntity(), currentCandidate, {
1391
+ decidedAt,
1392
+ status: workflow_engine_enums_1.TaskCandidateStatusEnum.CANCELLED,
1393
+ });
1394
+ }
1395
+ return currentCandidate;
1396
+ });
1397
+ if (nextCandidates.length > 0) {
1398
+ await taskCandidateRepository.save(nextCandidates);
1399
+ }
1400
+ return nextCandidates;
1401
+ }
1402
+ async transferTask(manager, instance, task, decision, signature, transferToMemberId, decisionComment, decidedAt, actorCandidate) {
1403
+ if (!transferToMemberId) {
1404
+ throw new common_1.BadRequestException('Transfer target member is required');
1405
+ }
1406
+ const taskRepository = manager.getRepository(task_entity_1.TaskEntity);
1407
+ const taskCandidateRepository = manager.getRepository(task_candidate_entity_1.TaskCandidateEntity);
1408
+ const activityRepository = manager.getRepository(activity_log_entity_1.ActivityLogEntity);
1409
+ const transferringMemberId = task.assigneeMemberId;
1410
+ if (!transferringMemberId) {
1411
+ throw new common_1.ConflictException('Task must be claimed before transfer');
1412
+ }
1413
+ const nextDelegationChain = [
1414
+ ...readDelegationSteps(task.delegationChain),
1415
+ {
1416
+ from: transferringMemberId,
1417
+ reason: 'MANUAL_TRANSFER',
1418
+ ruleId: null,
1419
+ to: transferToMemberId,
1420
+ },
1421
+ ];
1422
+ const transferredTask = await taskRepository.save({
1423
+ ...task,
1424
+ completedAt: decidedAt,
1425
+ delegationChain: nextDelegationChain,
1426
+ status: workflow_engine_enums_1.TaskStatusEnum.TRANSFERRED,
1427
+ });
1428
+ const nextTask = await taskRepository.save(taskRepository.create({
1429
+ assigneeMemberId: transferToMemberId,
1430
+ assignmentType: workflow_engine_enums_1.TaskAssignmentTypeEnum.DIRECT_MEMBER,
1431
+ completedAt: null,
1432
+ decisionPolicySnapshot: { type: 'SINGLE' },
1433
+ delegationChain: nextDelegationChain,
1434
+ instanceId: task.instanceId,
1435
+ nodeId: task.nodeId,
1436
+ openedAt: null,
1437
+ originalAssigneeMemberId: task.originalAssigneeMemberId,
1438
+ slaDueAt: task.slaDueAt,
1439
+ status: workflow_engine_enums_1.TaskStatusEnum.PENDING,
1440
+ tokenId: task.tokenId,
1441
+ }));
1442
+ await taskCandidateRepository.save([
1443
+ ...(actorCandidate
1444
+ ? [
1445
+ taskCandidateRepository.create({
1446
+ ...actorCandidate,
1447
+ decidedAt,
1448
+ status: workflow_engine_enums_1.TaskCandidateStatusEnum.TRANSFERRED,
1449
+ }),
1450
+ ]
1451
+ : []),
1452
+ taskCandidateRepository.create({
1453
+ claimedAt: null,
1454
+ createdAt: decidedAt,
1455
+ decidedAt: null,
1456
+ delegationChain: nextDelegationChain,
1457
+ memberId: transferToMemberId,
1458
+ originalMemberId: task.originalAssigneeMemberId ?? transferToMemberId,
1459
+ sourceType: 'DIRECT',
1460
+ status: workflow_engine_enums_1.TaskCandidateStatusEnum.PENDING,
1461
+ taskId: nextTask.id,
1462
+ }),
1463
+ ]);
1464
+ await activityRepository.save([
1465
+ activityRepository.create({
1466
+ actorMemberId: task.assigneeMemberId,
1467
+ eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TASK_DECIDED,
1468
+ instanceId: instance.id,
1469
+ nodeId: task.nodeId,
1470
+ payload: {
1471
+ action: workflow_engine_enums_1.TaskDecisionActionEnum.TRANSFERRED,
1472
+ comment: decisionComment,
1473
+ decisionId: decision.id,
1474
+ signatureId: signature.id,
1475
+ signedPayloadHash: signature.signedPayloadHash,
1476
+ transferToMemberId,
1477
+ },
1478
+ taskId: transferredTask.id,
1479
+ }),
1480
+ activityRepository.create({
1481
+ actorMemberId: transferringMemberId,
1482
+ eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TASK_CREATED,
1483
+ instanceId: instance.id,
1484
+ nodeId: task.nodeId,
1485
+ payload: {
1486
+ assigneeMemberId: transferToMemberId,
1487
+ delegationChain: nextDelegationChain,
1488
+ originalAssigneeMemberId: task.originalAssigneeMemberId,
1489
+ tokenId: task.tokenId,
1490
+ transferredFromTaskId: task.id,
1491
+ },
1492
+ taskId: nextTask.id,
1493
+ }),
1494
+ ]);
1495
+ const node = readWorkflowNodeOrThrow(instance.workflowSnapshot, task.nodeId);
1496
+ if (node.type === 'userTask') {
1497
+ await this.notificationService.createTaskAssignedNotification({
1498
+ instance,
1499
+ manager,
1500
+ node,
1501
+ task: nextTask,
1502
+ transferred: true,
1503
+ });
1504
+ }
1505
+ }
1506
+ async resolveRuntimeTaskCandidates(manager, instance, node) {
1507
+ const resolvedCandidates = await this.resolveApproverResolver(manager, instance, node.data.approverResolver, `簽核節點「${node.data.label}」`);
1508
+ const context = {
1509
+ formData: instance.formData,
1510
+ initiatorMemberId: instance.initiatorMemberId,
1511
+ initiatorMetadataSnapshot: instance.initiatorMetadataSnapshot,
1512
+ instanceId: instance.id,
1513
+ nodeId: node.id,
1514
+ state: instance.state,
1515
+ templateId: instance.templateId,
1516
+ templateVersionId: instance.templateVersionId,
1517
+ title: instance.title,
1518
+ };
1519
+ const delegatedCandidates = await Promise.all(resolvedCandidates.map(async (candidate) => {
1520
+ const delegationResolution = await this.delegationService.resolveAssignee(candidate.memberId, context);
1521
+ return {
1522
+ delegationChain: delegationResolution.delegationChain,
1523
+ memberId: delegationResolution.finalAssigneeMemberId,
1524
+ originalMemberId: candidate.memberId,
1525
+ sourceType: candidate.sourceType,
1526
+ };
1527
+ }));
1528
+ const uniqueCandidates = uniqueRuntimeCandidates(delegatedCandidates);
1529
+ if (uniqueCandidates.length === 0) {
1530
+ throw new common_1.ConflictException(`簽核節點「${node.data.label}」 did not resolve to any member id`);
1531
+ }
1532
+ return uniqueCandidates;
1533
+ }
1534
+ async resolveApproverResolver(manager, instance, resolver, label) {
1535
+ if (resolver.type === 'DIRECT') {
1536
+ return readMemberIdsFromValue(resolver.memberIds, label).map((memberId) => ({ memberId, sourceType: resolver.type }));
1537
+ }
1538
+ if (resolver.type === 'DYNAMIC_FORM') {
1539
+ return readMemberIdsFromValue(readValueAtPath(instance.formData, resolver.formPath), `${label}.formPath`).map((memberId) => ({ memberId, sourceType: resolver.type }));
1540
+ }
1541
+ if (resolver.type === 'EXPRESSION') {
1542
+ return readMemberIdsFromValue(this.conditionService.evaluateValue(resolver.expression, buildWorkflowExpressionContext(instance), `${label}.expression`), `${label}.expression`).map((memberId) => ({ memberId, sourceType: resolver.type }));
1543
+ }
1544
+ if (resolver.type === 'POSITION') {
1545
+ return this.resolvePositionCandidates(manager, resolver.positionId, label).then((memberIds) => memberIds.map((memberId) => ({ memberId, sourceType: resolver.type })));
1546
+ }
1547
+ if (resolver.type === 'ORG_UNIT_MEMBER') {
1548
+ return this.resolveOrgUnitMemberCandidates(manager, resolver.orgUnitId, Boolean(resolver.includeDescendants), label).then((memberIds) => memberIds.map((memberId) => ({ memberId, sourceType: resolver.type })));
1549
+ }
1550
+ if (resolver.type === 'ORG_UNIT_POSITION') {
1551
+ return this.resolveOrgUnitPositionCandidates(manager, resolver.orgUnitId, resolver.positionId, Boolean(resolver.includeDescendants), label).then((memberIds) => memberIds.map((memberId) => ({ memberId, sourceType: resolver.type })));
1552
+ }
1553
+ if (resolver.type === 'ORG_UNIT_MANAGER') {
1554
+ return this.resolveOrgUnitManagerCandidates(manager, instance, resolver.orgUnitId, resolver.fallback, label).then((memberIds) => memberIds.map((memberId) => ({ memberId, sourceType: resolver.type })));
1555
+ }
1556
+ return this.resolveInitiatorManagerCandidates(manager, instance, resolver.levelsUp, resolver.fallback, label).then((memberIds) => memberIds.map((memberId) => ({ memberId, sourceType: resolver.type })));
1557
+ }
1558
+ async resolvePositionCandidates(manager, positionId, label) {
1559
+ const today = toDateOnlyString(new Date());
1560
+ const memberships = await manager.getRepository(membership_entity_1.MembershipEntity).find({
1561
+ where: { positionId },
1562
+ });
1563
+ const activeMemberships = memberships
1564
+ .filter((membership) => membership.effectiveFrom <= today &&
1565
+ (!membership.effectiveTo || membership.effectiveTo >= today))
1566
+ .sort((first, second) => {
1567
+ if (first.isPrimary !== second.isPrimary) {
1568
+ return first.isPrimary ? -1 : 1;
1569
+ }
1570
+ return second.effectiveFrom.localeCompare(first.effectiveFrom);
1571
+ });
1572
+ if (!activeMemberships.length) {
1573
+ throw new common_1.ConflictException(`${label} position ${positionId} has no active membership`);
1574
+ }
1575
+ return uniqueTexts(activeMemberships.map((membership) => membership.memberId));
1576
+ }
1577
+ async resolveInitiatorManagerCandidates(manager, instance, levelsUp, fallback, label) {
1578
+ const normalizedLevelsUp = Math.max(Math.trunc(levelsUp), 1);
1579
+ const resolvedManagerMemberIds = await this.resolveManagerMemberIdsFromOrganization(manager, instance.initiatorMemberId, normalizedLevelsUp);
1580
+ if (resolvedManagerMemberIds.length) {
1581
+ return resolvedManagerMemberIds;
1582
+ }
1583
+ return [
1584
+ this.resolveFallbackAssignee(instance, fallback, label, `找不到發起人的第 ${normalizedLevelsUp} 層主管`),
1585
+ ];
1586
+ }
1587
+ resolveFallbackAssignee(instance, fallback, label, failureReason) {
1588
+ if (!fallback || fallback.type === 'NONE') {
1589
+ throw new common_1.ConflictException(`${label} 無法建立待簽任務:${failureReason},且未設定改派固定人。請聯絡流程管理員調整主管規則或模板 fallback。`);
1590
+ }
1591
+ if (!fallback.memberId.trim()) {
1592
+ throw new common_1.ConflictException(`${label} 無法建立待簽任務:改派固定人未設定。`);
1593
+ }
1594
+ if (fallback.memberId === instance.initiatorMemberId &&
1595
+ !fallback.allowInitiatorSelfApproval) {
1596
+ throw new common_1.ConflictException(`${label} 無法建立待簽任務:改派固定人是申請人本人,且此節點未允許申請人自簽。`);
1597
+ }
1598
+ return fallback.memberId;
1599
+ }
1600
+ async resolveManagerMemberIdsFromOrganization(manager, memberId, levelsUp) {
1601
+ return this.resolveManagerMemberIdsAtLevel(manager, memberId, toDateOnlyString(new Date()), levelsUp);
1602
+ }
1603
+ async resolveManagerMemberIdsAtLevel(manager, memberId, date, remainingLevels) {
1604
+ const directManagerMemberIds = await this.resolveDirectManagerMemberIds(manager, memberId, date);
1605
+ if (remainingLevels <= 1 || directManagerMemberIds.length === 0) {
1606
+ return directManagerMemberIds;
1607
+ }
1608
+ const nextLevelGroups = await Promise.all(directManagerMemberIds.map((managerMemberId) => this.resolveManagerMemberIdsAtLevel(manager, managerMemberId, date, remainingLevels - 1)));
1609
+ return uniqueTexts(nextLevelGroups.flat());
1610
+ }
1611
+ async resolveDirectManagerMemberIds(manager, memberId, date) {
1612
+ const memberships = await manager.getRepository(membership_entity_1.MembershipEntity).find({
1613
+ where: { memberId },
1614
+ });
1615
+ const activeMemberships = memberships.filter((membership) => isDateRangeActive(membership, date));
1616
+ const directOrgUnitIds = activeMemberships.map((membership) => membership.orgUnitId);
1617
+ const positionIds = activeMemberships
1618
+ .map((membership) => membership.positionId)
1619
+ .filter((positionId) => Boolean(positionId));
1620
+ const orgUnitIds = await this.readOrgUnitAndAncestorIds(manager, directOrgUnitIds);
1621
+ const candidatePairs = [
1622
+ { scopeId: memberId, scopeType: organization_enums_1.ManagerResolutionScopeTypeEnum.MEMBER },
1623
+ ...orgUnitIds.map((scopeId) => ({
1624
+ scopeId,
1625
+ scopeType: organization_enums_1.ManagerResolutionScopeTypeEnum.ORG_UNIT,
1626
+ })),
1627
+ ...positionIds.map((scopeId) => ({
1628
+ scopeId,
1629
+ scopeType: organization_enums_1.ManagerResolutionScopeTypeEnum.POSITION,
1630
+ })),
1631
+ ];
1632
+ return this.resolveManagerResolutionCandidates(manager, candidatePairs, date);
1633
+ }
1634
+ async resolveOrgUnitManagerCandidates(manager, instance, orgUnitId, fallback, label) {
1635
+ const date = toDateOnlyString(new Date());
1636
+ const orgUnitIds = await this.readOrgUnitAndAncestorIds(manager, [
1637
+ orgUnitId,
1638
+ ]);
1639
+ const managerMemberIds = await this.resolveManagerResolutionCandidates(manager, orgUnitIds.map((scopeId) => ({
1640
+ scopeId,
1641
+ scopeType: organization_enums_1.ManagerResolutionScopeTypeEnum.ORG_UNIT,
1642
+ })), date);
1643
+ if (!managerMemberIds.length) {
1644
+ return [
1645
+ this.resolveFallbackAssignee(instance, fallback, label, `找不到指定組織的主管規則`),
1646
+ ];
1647
+ }
1648
+ return managerMemberIds;
1649
+ }
1650
+ async readOrgUnitAndAncestorIds(manager, orgUnitIds) {
1651
+ if (!orgUnitIds.length) {
1652
+ return [];
1653
+ }
1654
+ const orgUnitRepository = manager.getRepository(org_unit_entity_1.OrgUnitEntity);
1655
+ const directOrgUnits = await orgUnitRepository.find({
1656
+ where: { deletedAt: (0, typeorm_2.IsNull)(), id: (0, typeorm_2.In)([...orgUnitIds]) },
1657
+ });
1658
+ const ancestorGroups = await Promise.all(directOrgUnits.map((orgUnit) => orgUnitRepository
1659
+ .createQueryBuilder('orgUnit')
1660
+ .where('orgUnit.deleted_at IS NULL')
1661
+ .andWhere('orgUnit.path @> :path', { path: orgUnit.path })
1662
+ .getMany()));
1663
+ const byId = new Map([...directOrgUnits, ...ancestorGroups.flat()].map((orgUnit) => [orgUnit.id, orgUnit]));
1664
+ return [...byId.keys()];
1665
+ }
1666
+ async resolveManagerResolutionCandidates(manager, candidatePairs, date) {
1667
+ const scopeIds = candidatePairs.map((pair) => pair.scopeId);
1668
+ if (!scopeIds.length) {
1669
+ return [];
1670
+ }
1671
+ const resolutions = await manager
1672
+ .getRepository(manager_resolution_entity_1.ManagerResolutionEntity)
1673
+ .find({
1674
+ where: {
1675
+ scopeId: (0, typeorm_2.In)([...scopeIds]),
1676
+ scopeType: (0, typeorm_2.In)([
1677
+ organization_enums_1.ManagerResolutionScopeTypeEnum.MEMBER,
1678
+ organization_enums_1.ManagerResolutionScopeTypeEnum.ORG_UNIT,
1679
+ organization_enums_1.ManagerResolutionScopeTypeEnum.POSITION,
1680
+ ]),
1681
+ },
1682
+ });
1683
+ const active = resolutions
1684
+ .filter((resolution) => candidatePairs.some((pair) => pair.scopeId === resolution.scopeId &&
1685
+ pair.scopeType === resolution.scopeType))
1686
+ .filter((resolution) => isDateRangeActive(resolution, date))
1687
+ .sort(compareManagerResolution);
1688
+ return uniqueTexts(active.map((resolution) => resolution.managerMemberId));
1689
+ }
1690
+ async resolveOrgUnitMemberCandidates(manager, orgUnitId, includeDescendants, label) {
1691
+ const orgUnitIds = await this.readOrgUnitScopeIds(manager, orgUnitId, includeDescendants);
1692
+ const today = toDateOnlyString(new Date());
1693
+ const memberships = await manager.getRepository(membership_entity_1.MembershipEntity).find({
1694
+ where: { orgUnitId: (0, typeorm_2.In)([...orgUnitIds]) },
1695
+ });
1696
+ const memberIds = memberships
1697
+ .filter((membership) => isDateRangeActive(membership, today))
1698
+ .sort(compareMembership)
1699
+ .map((membership) => membership.memberId);
1700
+ if (memberIds.length === 0) {
1701
+ throw new common_1.ConflictException(`${label} orgUnit ${orgUnitId} has no active member`);
1702
+ }
1703
+ return uniqueTexts(memberIds);
1704
+ }
1705
+ async resolveOrgUnitPositionCandidates(manager, orgUnitId, positionId, includeDescendants, label) {
1706
+ const orgUnitIds = await this.readOrgUnitScopeIds(manager, orgUnitId, includeDescendants);
1707
+ const today = toDateOnlyString(new Date());
1708
+ const memberships = await manager.getRepository(membership_entity_1.MembershipEntity).find({
1709
+ where: { orgUnitId: (0, typeorm_2.In)([...orgUnitIds]), positionId },
1710
+ });
1711
+ const memberIds = memberships
1712
+ .filter((membership) => isDateRangeActive(membership, today))
1713
+ .sort(compareMembership)
1714
+ .map((membership) => membership.memberId);
1715
+ if (memberIds.length === 0) {
1716
+ throw new common_1.ConflictException(`${label} orgUnit ${orgUnitId} and position ${positionId} has no active membership`);
1717
+ }
1718
+ return uniqueTexts(memberIds);
1719
+ }
1720
+ async readOrgUnitScopeIds(manager, orgUnitId, includeDescendants) {
1721
+ const orgUnitRepository = manager.getRepository(org_unit_entity_1.OrgUnitEntity);
1722
+ const orgUnit = await orgUnitRepository.findOne({
1723
+ where: { deletedAt: (0, typeorm_2.IsNull)(), id: orgUnitId },
1724
+ });
1725
+ if (!orgUnit) {
1726
+ throw new common_1.ConflictException(`Org unit ${orgUnitId} was not found`);
1727
+ }
1728
+ if (!includeDescendants) {
1729
+ return [orgUnit.id];
1730
+ }
1731
+ const descendants = await orgUnitRepository
1732
+ .createQueryBuilder('orgUnit')
1733
+ .where('orgUnit.deleted_at IS NULL')
1734
+ .andWhere('orgUnit.path <@ :path', { path: orgUnit.path })
1735
+ .getMany();
1736
+ return uniqueTexts([orgUnit.id, ...descendants.map((unit) => unit.id)]);
1737
+ }
1738
+ async processExclusiveGateway(manager, instance, token, node) {
1739
+ if (node.data.direction !== 'split') {
1740
+ await this.advanceTokenToOutgoingNodes(manager, instance, token, node);
1741
+ return;
1742
+ }
1743
+ const edge = (await this.readAvailableOutgoingEdges(manager, instance, node))[0];
1744
+ await this.advanceTokenAlongEdge(manager, instance, token, node, edge);
1745
+ }
1746
+ async executeServiceTask(manager, instance, token, node) {
1747
+ const activityRepository = manager.getRepository(activity_log_entity_1.ActivityLogEntity);
1748
+ const action = node.data.action;
1749
+ if (action.type === 'WEBHOOK') {
1750
+ const payload = typeof action.payload === 'string' && action.payload.trim()
1751
+ ? this.conditionService.evaluateValue(action.payload, buildWorkflowExpressionContext(instance), `workflow.nodes.${node.id}.data.action.payload`)
1752
+ : {
1753
+ instance: buildWorkflowExpressionContext(instance).instance,
1754
+ triggeredAt: new Date().toISOString(),
1755
+ };
1756
+ const result = await executeWebhookServiceAction(this.serviceTaskDispatcher, action, payload);
1757
+ await activityRepository.save(activityRepository.create({
1758
+ actorMemberId: null,
1759
+ eventType: result.ok
1760
+ ? workflow_engine_enums_1.ActivityLogEventTypeEnum.SERVICE_TASK_EXECUTED
1761
+ : workflow_engine_enums_1.ActivityLogEventTypeEnum.SERVICE_TASK_FAILED,
1762
+ instanceId: instance.id,
1763
+ nodeId: node.id,
1764
+ payload: {
1765
+ action: 'WEBHOOK',
1766
+ error: result.error,
1767
+ ok: result.ok,
1768
+ status: result.status,
1769
+ tokenId: token.id,
1770
+ url: action.url,
1771
+ },
1772
+ taskId: null,
1773
+ }));
1774
+ await this.advanceTokenToOutgoingNodes(manager, instance, token, node);
1775
+ return;
1776
+ }
1777
+ if (action.type === 'SET_FORM_FIELD') {
1778
+ const value = this.conditionService.evaluateValue(action.value, buildWorkflowExpressionContext(instance), `workflow.nodes.${node.id}.data.action.value`);
1779
+ const updatedFormData = writeValueAtPath(instance.formData, action.fieldPath, value);
1780
+ const updatedInstance = await manager
1781
+ .getRepository(approval_instance_entity_1.ApprovalInstanceEntity)
1782
+ .save({
1783
+ ...instance,
1784
+ formData: updatedFormData,
1785
+ });
1786
+ Object.assign(instance, updatedInstance);
1787
+ await activityRepository.save(activityRepository.create({
1788
+ actorMemberId: null,
1789
+ eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.SERVICE_TASK_EXECUTED,
1790
+ instanceId: instance.id,
1791
+ nodeId: node.id,
1792
+ payload: {
1793
+ action: 'SET_FORM_FIELD',
1794
+ fieldPath: action.fieldPath,
1795
+ tokenId: token.id,
1796
+ },
1797
+ taskId: null,
1798
+ }));
1799
+ await this.advanceTokenToOutgoingNodes(manager, updatedInstance, token, node);
1800
+ return;
1801
+ }
1802
+ const recipients = await this.resolveApproverResolver(manager, instance, action.recipients, `知會節點「${node.data.label}」`);
1803
+ const recipientMemberIds = uniqueTexts(recipients.map((recipient) => recipient.memberId));
1804
+ await this.notificationService.createServiceTaskNotifications({
1805
+ instance,
1806
+ manager,
1807
+ node,
1808
+ recipientMemberIds,
1809
+ });
1810
+ await manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity).save({
1811
+ ...token,
1812
+ consumedAt: new Date(),
1813
+ status: workflow_engine_enums_1.WorkflowTokenStatusEnum.CONSUMED,
1814
+ });
1815
+ await activityRepository.save(activityRepository.create({
1816
+ actorMemberId: null,
1817
+ eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TOKEN_ADVANCED,
1818
+ instanceId: instance.id,
1819
+ nodeId: node.id,
1820
+ payload: {
1821
+ action: 'NOTIFY',
1822
+ recipientMemberIds,
1823
+ tokenId: token.id,
1824
+ },
1825
+ taskId: null,
1826
+ }));
1827
+ }
1828
+ async rejectInstance(manager, instance, task, rejectedAt) {
1829
+ const tokenRepository = manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity);
1830
+ const instanceRepository = manager.getRepository(approval_instance_entity_1.ApprovalInstanceEntity);
1831
+ const activeTokens = await tokenRepository.find({
1832
+ where: { instanceId: instance.id },
1833
+ });
1834
+ const consumedTokens = activeTokens
1835
+ .filter((token) => token.status === workflow_engine_enums_1.WorkflowTokenStatusEnum.ACTIVE ||
1836
+ token.status === workflow_engine_enums_1.WorkflowTokenStatusEnum.WAITING)
1837
+ .map((token) => ({
1838
+ ...token,
1839
+ consumedAt: rejectedAt,
1840
+ status: workflow_engine_enums_1.WorkflowTokenStatusEnum.CONSUMED,
1841
+ }));
1842
+ if (consumedTokens.length) {
1843
+ await tokenRepository.save(consumedTokens);
1844
+ }
1845
+ await instanceRepository.save({
1846
+ ...instance,
1847
+ completedAt: rejectedAt,
1848
+ state: workflow_engine_enums_1.ApprovalInstanceStateEnum.REJECTED,
1849
+ });
1850
+ await manager.getRepository(activity_log_entity_1.ActivityLogEntity).save(manager.getRepository(activity_log_entity_1.ActivityLogEntity).create({
1851
+ actorMemberId: task.assigneeMemberId,
1852
+ eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TOKEN_ADVANCED,
1853
+ instanceId: instance.id,
1854
+ nodeId: task.nodeId,
1855
+ payload: {
1856
+ action: 'REJECTED',
1857
+ taskId: task.id,
1858
+ },
1859
+ taskId: task.id,
1860
+ }));
1861
+ }
1862
+ async returnInstanceToNode(manager, instance, task, returnToNodeId, returnedAt) {
1863
+ if (!returnToNodeId) {
1864
+ throw new common_1.ConflictException('Return target node is required');
1865
+ }
1866
+ const targetNode = readWorkflowNodeOrThrow(instance.workflowSnapshot, returnToNodeId);
1867
+ const tokenRepository = manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity);
1868
+ const instanceRepository = manager.getRepository(approval_instance_entity_1.ApprovalInstanceEntity);
1869
+ const activityRepository = manager.getRepository(activity_log_entity_1.ActivityLogEntity);
1870
+ await this.consumeOpenRuntimeState(manager, instance, returnedAt, workflow_engine_enums_1.TaskStatusEnum.CANCELLED);
1871
+ const returnedInstance = await instanceRepository.save({
1872
+ ...instance,
1873
+ completedAt: null,
1874
+ state: targetNode.type === 'startEvent'
1875
+ ? workflow_engine_enums_1.ApprovalInstanceStateEnum.RETURNED
1876
+ : workflow_engine_enums_1.ApprovalInstanceStateEnum.RUNNING,
1877
+ });
1878
+ const resubmitStrategy = readReturnResubmitStrategy(task, instance);
1879
+ const returnToken = await tokenRepository.save(tokenRepository.create({
1880
+ consumedAt: null,
1881
+ currentNodeId: targetNode.id,
1882
+ instanceId: instance.id,
1883
+ parentTokenId: null,
1884
+ status: workflow_engine_enums_1.WorkflowTokenStatusEnum.ACTIVE,
1885
+ }));
1886
+ await activityRepository.save([
1887
+ activityRepository.create({
1888
+ actorMemberId: task.assigneeMemberId,
1889
+ eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.INSTANCE_RETURNED,
1890
+ instanceId: instance.id,
1891
+ nodeId: task.nodeId,
1892
+ payload: {
1893
+ resubmitStrategy,
1894
+ returnedFromNodeId: task.nodeId,
1895
+ returnToNodeId: targetNode.id,
1896
+ taskId: task.id,
1897
+ },
1898
+ taskId: task.id,
1899
+ }),
1900
+ activityRepository.create({
1901
+ actorMemberId: null,
1902
+ eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TOKEN_CREATED,
1903
+ instanceId: instance.id,
1904
+ nodeId: targetNode.id,
1905
+ payload: {
1906
+ returnedFromNodeId: task.nodeId,
1907
+ tokenId: returnToken.id,
1908
+ },
1909
+ taskId: null,
1910
+ }),
1911
+ ]);
1912
+ if (returnedInstance.state === workflow_engine_enums_1.ApprovalInstanceStateEnum.RUNNING) {
1913
+ await this.processRunningInstance(manager, returnedInstance);
1914
+ }
1915
+ }
1916
+ async readLatestReturnedInstanceContext(manager, instanceId) {
1917
+ const activity = await manager.getRepository(activity_log_entity_1.ActivityLogEntity).findOne({
1918
+ order: { createdAt: 'DESC' },
1919
+ where: {
1920
+ eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.INSTANCE_RETURNED,
1921
+ instanceId,
1922
+ },
1923
+ });
1924
+ if (!activity) {
1925
+ return null;
1926
+ }
1927
+ const returnToNodeId = readStringPayloadValue(activity.payload, 'returnToNodeId');
1928
+ const returnedFromNodeId = readStringPayloadValue(activity.payload, 'returnedFromNodeId') ??
1929
+ activity.nodeId;
1930
+ if (!returnToNodeId || !returnedFromNodeId) {
1931
+ return null;
1932
+ }
1933
+ return {
1934
+ resubmitStrategy: readReturnResubmitStrategyFromPayload(activity.payload),
1935
+ returnedFromNodeId,
1936
+ returnToNodeId,
1937
+ };
1938
+ }
1939
+ async resumeReturnedInstanceFromReturnPoint(manager, instance, context, resubmittedAt) {
1940
+ const targetNode = readWorkflowNodeOrThrow(instance.workflowSnapshot, context.returnToNodeId);
1941
+ if (targetNode.type !== 'startEvent') {
1942
+ return instance;
1943
+ }
1944
+ readWorkflowNodeOrThrow(instance.workflowSnapshot, context.returnedFromNodeId);
1945
+ await this.consumeOpenRuntimeState(manager, instance, resubmittedAt, workflow_engine_enums_1.TaskStatusEnum.CANCELLED);
1946
+ const tokenRepository = manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity);
1947
+ const returnPointToken = await tokenRepository.save(tokenRepository.create({
1948
+ consumedAt: null,
1949
+ currentNodeId: context.returnedFromNodeId,
1950
+ instanceId: instance.id,
1951
+ parentTokenId: null,
1952
+ status: workflow_engine_enums_1.WorkflowTokenStatusEnum.ACTIVE,
1953
+ }));
1954
+ await manager.getRepository(activity_log_entity_1.ActivityLogEntity).save(manager.getRepository(activity_log_entity_1.ActivityLogEntity).create({
1955
+ actorMemberId: null,
1956
+ eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TOKEN_CREATED,
1957
+ instanceId: instance.id,
1958
+ nodeId: context.returnedFromNodeId,
1959
+ payload: {
1960
+ resubmitStrategy: context.resubmitStrategy,
1961
+ resumedFromNodeId: context.returnToNodeId,
1962
+ tokenId: returnPointToken.id,
1963
+ },
1964
+ taskId: null,
1965
+ }));
1966
+ return instance;
1967
+ }
1968
+ async consumeOpenRuntimeState(manager, instance, consumedAt, taskStatus) {
1969
+ const tokenRepository = manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity);
1970
+ const taskRepository = manager.getRepository(task_entity_1.TaskEntity);
1971
+ const tokens = await tokenRepository.find({
1972
+ where: { instanceId: instance.id },
1973
+ });
1974
+ const openTokens = tokens.filter((token) => token.status === workflow_engine_enums_1.WorkflowTokenStatusEnum.ACTIVE ||
1975
+ token.status === workflow_engine_enums_1.WorkflowTokenStatusEnum.WAITING);
1976
+ const tasks = await taskRepository.find({
1977
+ where: { instanceId: instance.id },
1978
+ });
1979
+ const openTasks = tasks.filter((task) => task.status === workflow_engine_enums_1.TaskStatusEnum.PENDING ||
1980
+ task.status === workflow_engine_enums_1.TaskStatusEnum.IN_PROGRESS);
1981
+ if (openTokens.length > 0) {
1982
+ await tokenRepository.save(openTokens.map((token) => ({
1983
+ ...token,
1984
+ consumedAt,
1985
+ status: workflow_engine_enums_1.WorkflowTokenStatusEnum.CONSUMED,
1986
+ })));
1987
+ }
1988
+ if (openTasks.length > 0) {
1989
+ await taskRepository.save(openTasks.map((openTask) => Object.assign(new task_entity_1.TaskEntity(), openTask, {
1990
+ completedAt: consumedAt,
1991
+ status: taskStatus,
1992
+ })));
1993
+ await this.closeTaskCandidates(manager, openTasks.map((task) => task.id), readCandidateStatusForClosedTask(taskStatus));
1994
+ }
1995
+ }
1996
+ async consumeJoinedTokens(manager, tokens) {
1997
+ if (tokens.length === 0) {
1998
+ return;
1999
+ }
2000
+ const consumedAt = new Date();
2001
+ await manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity).save(tokens.map((token) => ({
2002
+ ...token,
2003
+ consumedAt,
2004
+ status: workflow_engine_enums_1.WorkflowTokenStatusEnum.CONSUMED,
2005
+ })));
2006
+ }
2007
+ async cancelAlternativeTokensForOrTrigger(manager, instance, token, targetNodeId) {
2008
+ const tokenRepository = manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity);
2009
+ const taskRepository = manager.getRepository(task_entity_1.TaskEntity);
2010
+ const consumedAt = new Date();
2011
+ const tokens = await tokenRepository.find({
2012
+ where: { instanceId: instance.id },
2013
+ });
2014
+ const alternativeTokens = tokens.filter((candidate) => candidate.id !== token.id &&
2015
+ (candidate.status === workflow_engine_enums_1.WorkflowTokenStatusEnum.ACTIVE ||
2016
+ candidate.status === workflow_engine_enums_1.WorkflowTokenStatusEnum.WAITING) &&
2017
+ hasPathToNode(instance.workflowSnapshot, candidate.currentNodeId, targetNodeId));
2018
+ if (alternativeTokens.length === 0) {
2019
+ return;
2020
+ }
2021
+ const alternativeTokenIds = alternativeTokens.map((candidate) => candidate.id);
2022
+ const cancellableTasks = (await taskRepository.find({
2023
+ where: { instanceId: instance.id },
2024
+ })).filter((task) => alternativeTokenIds.includes(task.tokenId) &&
2025
+ (task.status === workflow_engine_enums_1.TaskStatusEnum.PENDING ||
2026
+ task.status === workflow_engine_enums_1.TaskStatusEnum.IN_PROGRESS));
2027
+ await tokenRepository.save(alternativeTokens.map((candidate) => ({
2028
+ ...candidate,
2029
+ consumedAt,
2030
+ status: workflow_engine_enums_1.WorkflowTokenStatusEnum.CONSUMED,
2031
+ })));
2032
+ if (cancellableTasks.length) {
2033
+ await taskRepository.save(cancellableTasks.map((task) => Object.assign(new task_entity_1.TaskEntity(), task, {
2034
+ completedAt: consumedAt,
2035
+ status: workflow_engine_enums_1.TaskStatusEnum.CANCELLED,
2036
+ })));
2037
+ await this.closeTaskCandidates(manager, cancellableTasks.map((task) => task.id), workflow_engine_enums_1.TaskCandidateStatusEnum.SUPERSEDED);
2038
+ }
2039
+ }
2040
+ async closeTaskCandidates(manager, taskIds, status) {
2041
+ if (taskIds.length === 0) {
2042
+ return;
2043
+ }
2044
+ const candidateRepository = manager.getRepository(task_candidate_entity_1.TaskCandidateEntity);
2045
+ const candidates = (await candidateRepository.find({ where: { taskId: (0, typeorm_2.In)([...taskIds]) } })).filter((candidate) => candidate.status === workflow_engine_enums_1.TaskCandidateStatusEnum.PENDING ||
2046
+ candidate.status === workflow_engine_enums_1.TaskCandidateStatusEnum.CLAIMED);
2047
+ if (candidates.length === 0) {
2048
+ return;
2049
+ }
2050
+ await candidateRepository.save(candidates.map((candidate) => Object.assign(new task_candidate_entity_1.TaskCandidateEntity(), candidate, { status })));
2051
+ }
2052
+ async getTemplateOrThrow(templateId) {
2053
+ const template = await this.approvalTemplateRepository.findOne({
2054
+ where: { deletedAt: (0, typeorm_2.IsNull)(), id: templateId },
2055
+ });
2056
+ if (!template) {
2057
+ throw new common_1.NotFoundException(`Approval template ${templateId} was not found`);
2058
+ }
2059
+ return template;
2060
+ }
2061
+ async getPublishedTemplateVersionOrThrow(versionId) {
2062
+ const version = await this.approvalTemplateVersionRepository.findOne({
2063
+ where: { id: versionId },
2064
+ });
2065
+ if (!version) {
2066
+ throw new common_1.NotFoundException(`Approval template version ${versionId} was not found`);
2067
+ }
2068
+ if (version.status !== template_enums_1.ApprovalTemplateVersionStatusEnum.PUBLISHED) {
2069
+ throw new common_1.ConflictException('Approval template current version must be published');
2070
+ }
2071
+ return version;
2072
+ }
2073
+ async getPublishedFormVersionOrThrow(versionId) {
2074
+ if (!versionId) {
2075
+ throw new common_1.ConflictException('Approval template version does not bind a form version');
2076
+ }
2077
+ const version = await this.formDefinitionVersionRepository.findOne({
2078
+ where: { id: versionId },
2079
+ });
2080
+ if (!version) {
2081
+ throw new common_1.NotFoundException(`Form definition version ${versionId} was not found`);
2082
+ }
2083
+ if (version.status !== form_enums_1.FormDefinitionVersionStatusEnum.PUBLISHED) {
2084
+ throw new common_1.ConflictException('Approval template must bind a published form version');
2085
+ }
2086
+ return version;
2087
+ }
2088
+ };
2089
+ exports.WorkflowEngineService = WorkflowEngineService;
2090
+ exports.WorkflowEngineService = WorkflowEngineService = tslib_1.__decorate([
2091
+ (0, common_1.Injectable)(),
2092
+ tslib_1.__param(0, (0, typeorm_1.InjectRepository)(approval_instance_entity_1.ApprovalInstanceEntity)),
2093
+ tslib_1.__param(1, (0, typeorm_1.InjectRepository)(workflow_token_entity_1.WorkflowTokenEntity)),
2094
+ tslib_1.__param(2, (0, typeorm_1.InjectRepository)(task_entity_1.TaskEntity)),
2095
+ tslib_1.__param(3, (0, typeorm_1.InjectRepository)(task_candidate_entity_1.TaskCandidateEntity)),
2096
+ tslib_1.__param(4, (0, typeorm_1.InjectRepository)(task_decision_entity_1.TaskDecisionEntity)),
2097
+ tslib_1.__param(5, (0, typeorm_1.InjectRepository)(notification_entity_1.NotificationEntity)),
2098
+ tslib_1.__param(6, (0, typeorm_1.InjectRepository)(activity_log_entity_1.ActivityLogEntity)),
2099
+ tslib_1.__param(7, (0, typeorm_1.InjectRepository)(approval_template_entity_1.ApprovalTemplateEntity)),
2100
+ tslib_1.__param(8, (0, typeorm_1.InjectRepository)(approval_template_version_entity_1.ApprovalTemplateVersionEntity)),
2101
+ tslib_1.__param(9, (0, typeorm_1.InjectRepository)(form_definition_version_entity_1.FormDefinitionVersionEntity)),
2102
+ tslib_1.__param(15, (0, common_1.Optional)()),
2103
+ tslib_1.__param(15, (0, common_1.Inject)(workflow_service_task_dispatcher_token_1.BPM_WORKFLOW_SERVICE_TASK_DISPATCHER)),
2104
+ tslib_1.__metadata("design:paramtypes", [typeorm_2.Repository,
2105
+ typeorm_2.Repository,
2106
+ typeorm_2.Repository,
2107
+ typeorm_2.Repository,
2108
+ typeorm_2.Repository,
2109
+ typeorm_2.Repository,
2110
+ typeorm_2.Repository,
2111
+ typeorm_2.Repository,
2112
+ typeorm_2.Repository,
2113
+ typeorm_2.Repository,
2114
+ attachment_service_1.AttachmentService,
2115
+ condition_service_1.ConditionService,
2116
+ delegation_service_1.DelegationService,
2117
+ notification_service_1.NotificationService,
2118
+ signature_service_1.SignatureService, Object])
2119
+ ], WorkflowEngineService);
2120
+ function parseJsonObject(value, label) {
2121
+ try {
2122
+ const parsedValue = JSON.parse(value);
2123
+ if (isRecord(parsedValue)) {
2124
+ return parsedValue;
2125
+ }
2126
+ throw new common_1.BadRequestException(`${label} must be a JSON object`);
2127
+ }
2128
+ catch (error) {
2129
+ if (error instanceof common_1.BadRequestException) {
2130
+ throw error;
2131
+ }
2132
+ throw new common_1.BadRequestException(error instanceof Error ? error.message : `${label} is invalid JSON`);
2133
+ }
2134
+ }
2135
+ function readDefaultInitiatorMetadataSnapshot(memberId) {
2136
+ return {
2137
+ customFields: {},
2138
+ memberId,
2139
+ };
2140
+ }
2141
+ function parseWorkflowDefinition(value) {
2142
+ try {
2143
+ const parsedValue = JSON.parse(value);
2144
+ if (isRecord(parsedValue) &&
2145
+ Array.isArray(parsedValue.nodes) &&
2146
+ Array.isArray(parsedValue.edges) &&
2147
+ isRecord(parsedValue.meta)) {
2148
+ return parsedValue;
2149
+ }
2150
+ throw new common_1.BadRequestException('workflowDefinitionJson must be a workflow definition object');
2151
+ }
2152
+ catch (error) {
2153
+ if (error instanceof common_1.BadRequestException) {
2154
+ throw error;
2155
+ }
2156
+ throw new common_1.BadRequestException(error instanceof Error
2157
+ ? error.message
2158
+ : 'workflowDefinitionJson is invalid JSON');
2159
+ }
2160
+ }
2161
+ function buildInitiatorPolicyContext(initiatorMetadataSnapshot) {
2162
+ return {
2163
+ env: {
2164
+ now: new Date().toISOString(),
2165
+ },
2166
+ initiator: initiatorMetadataSnapshot,
2167
+ subject: initiatorMetadataSnapshot,
2168
+ };
2169
+ }
2170
+ function buildWorkflowExpressionContext(instance) {
2171
+ return {
2172
+ env: {
2173
+ now: new Date().toISOString(),
2174
+ },
2175
+ form: instance.formData,
2176
+ formData: instance.formData,
2177
+ initiator: {
2178
+ ...instance.initiatorMetadataSnapshot,
2179
+ memberId: instance.initiatorMemberId,
2180
+ },
2181
+ instance: {
2182
+ id: instance.id,
2183
+ state: instance.state,
2184
+ templateId: instance.templateId,
2185
+ templateVersionId: instance.templateVersionId,
2186
+ title: instance.title,
2187
+ },
2188
+ };
2189
+ }
2190
+ function readDelegationSteps(value) {
2191
+ return value
2192
+ .map((step) => {
2193
+ const from = readStringValue(step.from);
2194
+ const to = readStringValue(step.to);
2195
+ const reason = readStringValue(step.reason);
2196
+ if (!from || !to || !reason) {
2197
+ return null;
2198
+ }
2199
+ return {
2200
+ from,
2201
+ reason,
2202
+ ruleId: readStringValue(step.ruleId),
2203
+ to,
2204
+ };
2205
+ })
2206
+ .filter((step) => step !== null);
2207
+ }
2208
+ function readStringValue(value) {
2209
+ return typeof value === 'string' && value.trim() ? value : null;
2210
+ }
2211
+ function buildDryRunExpressionContext(input, lastDecision) {
2212
+ return {
2213
+ env: {
2214
+ now: new Date().toISOString(),
2215
+ },
2216
+ form: input.formData,
2217
+ formData: input.formData,
2218
+ initiator: {
2219
+ ...input.initiatorMetadataSnapshot,
2220
+ memberId: input.initiatorMemberId,
2221
+ },
2222
+ instance: {
2223
+ id: SYSTEM_DRY_RUN_INSTANCE_ID,
2224
+ state: workflow_engine_enums_1.ApprovalInstanceStateEnum.RUNNING,
2225
+ title: 'Dry Run',
2226
+ },
2227
+ lastDecision,
2228
+ };
2229
+ }
2230
+ function isRecord(value) {
2231
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
2232
+ }
2233
+ function readWorkflowNodeOrThrow(workflow, nodeId) {
2234
+ const node = workflow.nodes.find((candidate) => candidate.id === nodeId);
2235
+ if (!node) {
2236
+ throw new common_1.ConflictException(`Workflow node ${nodeId} was not found`);
2237
+ }
2238
+ return node;
2239
+ }
2240
+ function readOutgoingEdgesOrThrow(workflow, nodeId) {
2241
+ const edges = workflow.edges.filter((edge) => edge.source === nodeId);
2242
+ if (edges.length === 0) {
2243
+ throw new common_1.ConflictException(`Workflow node ${nodeId} has no outgoing edge`);
2244
+ }
2245
+ return edges;
2246
+ }
2247
+ function readIncomingEdges(workflow, nodeId) {
2248
+ return workflow.edges.filter((edge) => edge.target === nodeId);
2249
+ }
2250
+ function hasPathToNode(workflow, sourceNodeId, targetNodeId) {
2251
+ return visitPathToNode(workflow, sourceNodeId, targetNodeId, new Set());
2252
+ }
2253
+ function visitPathToNode(workflow, sourceNodeId, targetNodeId, visitedNodeIds) {
2254
+ if (sourceNodeId === targetNodeId) {
2255
+ return true;
2256
+ }
2257
+ if (visitedNodeIds.has(sourceNodeId)) {
2258
+ return false;
2259
+ }
2260
+ const nextVisitedNodeIds = new Set([...visitedNodeIds, sourceNodeId]);
2261
+ return workflow.edges
2262
+ .filter((edge) => edge.source === sourceNodeId)
2263
+ .some((edge) => visitPathToNode(workflow, edge.target, targetNodeId, nextVisitedNodeIds));
2264
+ }
2265
+ function readReturnTargetNodeId(workflow, currentNodeId, requestedNodeId) {
2266
+ const currentNode = readWorkflowNodeOrThrow(workflow, currentNodeId);
2267
+ if (currentNode.type !== 'userTask') {
2268
+ throw new common_1.ConflictException(`Workflow node ${currentNodeId} does not support return decisions`);
2269
+ }
2270
+ if (!currentNode.data.returnBehavior.allowReturn) {
2271
+ throw new common_1.ConflictException(`User task ${currentNodeId} does not allow return decisions`);
2272
+ }
2273
+ const trimmedRequestedNodeId = requestedNodeId?.trim() || null;
2274
+ const defaultTargetNodeId = readDefaultReturnTargetNodeId(workflow, currentNode);
2275
+ if (currentNode.data.returnBehavior.allowedTargets === 'ANY') {
2276
+ if (!trimmedRequestedNodeId) {
2277
+ throw new common_1.BadRequestException('returnToNodeId is required');
2278
+ }
2279
+ readWorkflowNodeOrThrow(workflow, trimmedRequestedNodeId);
2280
+ return trimmedRequestedNodeId;
2281
+ }
2282
+ if (trimmedRequestedNodeId &&
2283
+ trimmedRequestedNodeId !== defaultTargetNodeId) {
2284
+ throw new common_1.ConflictException(`User task ${currentNodeId} can only return to ${defaultTargetNodeId}`);
2285
+ }
2286
+ return defaultTargetNodeId;
2287
+ }
2288
+ function readDefaultReturnTargetNodeId(workflow, currentNode) {
2289
+ if (currentNode.data.returnBehavior.allowedTargets === 'INITIATOR') {
2290
+ const startNode = workflow.nodes.find((node) => node.type === 'startEvent');
2291
+ if (!startNode) {
2292
+ throw new common_1.ConflictException('Workflow does not include a startEvent');
2293
+ }
2294
+ return startNode.id;
2295
+ }
2296
+ const previousNodeId = readIncomingEdges(workflow, currentNode.id)[0]?.source;
2297
+ if (!previousNodeId) {
2298
+ throw new common_1.ConflictException(`User task ${currentNode.id} does not have a previous node to return to`);
2299
+ }
2300
+ return previousNodeId;
2301
+ }
2302
+ function readReturnResubmitStrategy(task, instance) {
2303
+ const node = readWorkflowNodeOrThrow(instance.workflowSnapshot, task.nodeId);
2304
+ if (node.type !== 'userTask') {
2305
+ return 'RESTART';
2306
+ }
2307
+ return node.data.returnBehavior.resubmitStrategy ?? 'RESTART';
2308
+ }
2309
+ function readReturnResubmitStrategyFromPayload(payload) {
2310
+ return payload.resubmitStrategy === 'FROM_RETURN_POINT'
2311
+ ? 'FROM_RETURN_POINT'
2312
+ : 'RESTART';
2313
+ }
2314
+ function readStringPayloadValue(payload, key) {
2315
+ const value = payload[key];
2316
+ return typeof value === 'string' && value.trim() ? value.trim() : null;
2317
+ }
2318
+ function readNodeEntryCondition(node) {
2319
+ if (node.type === 'userTask' || node.type === 'serviceTask') {
2320
+ return node.data.entryCondition?.trim() || null;
2321
+ }
2322
+ return null;
2323
+ }
2324
+ function edgeHasCondition(edge) {
2325
+ return Boolean(edge.data.condition?.trim() ||
2326
+ (edge.data.conditionFieldKey && edge.data.conditionOperator));
2327
+ }
2328
+ function readMemberIdFromValue(value, label) {
2329
+ return readMemberIdsFromValue(value, label)[0] ?? '';
2330
+ }
2331
+ function readMemberIdsFromValue(value, label) {
2332
+ if (typeof value === 'string' && value.trim()) {
2333
+ return [value.trim()];
2334
+ }
2335
+ if (Array.isArray(value)) {
2336
+ const memberIds = value
2337
+ .filter((candidate) => typeof candidate === 'string' && Boolean(candidate.trim()))
2338
+ .map((candidate) => candidate.trim());
2339
+ if (memberIds.length > 0) {
2340
+ return uniqueTexts(memberIds);
2341
+ }
2342
+ }
2343
+ throw new common_1.ConflictException(`${label} did not resolve to a member id`);
2344
+ }
2345
+ function uniqueTexts(values) {
2346
+ return [...new Set(values.filter((value) => value.trim()))];
2347
+ }
2348
+ function uniqueRuntimeCandidates(candidates) {
2349
+ const seen = new Set();
2350
+ return candidates.filter((candidate) => {
2351
+ if (seen.has(candidate.memberId)) {
2352
+ return false;
2353
+ }
2354
+ seen.add(candidate.memberId);
2355
+ return true;
2356
+ });
2357
+ }
2358
+ function uniqueTasksById(tasks) {
2359
+ const seen = new Set();
2360
+ return tasks.filter((task) => {
2361
+ if (seen.has(task.id)) {
2362
+ return false;
2363
+ }
2364
+ seen.add(task.id);
2365
+ return true;
2366
+ });
2367
+ }
2368
+ function normalizeText(value) {
2369
+ return typeof value === 'string' && value.trim() ? value.trim() : null;
2370
+ }
2371
+ function readPageOffset(page, pageSize) {
2372
+ const normalizedPageSize = normalizePageSize(pageSize);
2373
+ return (normalizePage(page) - 1) * normalizedPageSize;
2374
+ }
2375
+ function shouldPaginateList(options) {
2376
+ return (typeof options.page !== 'undefined' ||
2377
+ typeof options.pageSize !== 'undefined');
2378
+ }
2379
+ function normalizePage(value) {
2380
+ return Number.isInteger(value) && Number(value) > 0 ? Number(value) : 1;
2381
+ }
2382
+ function normalizePageSize(value) {
2383
+ if (!Number.isInteger(value) || Number(value) <= 0) {
2384
+ return 20;
2385
+ }
2386
+ return Math.min(Number(value), 100);
2387
+ }
2388
+ function applyApprovalInstanceSearchFilter(queryBuilder, searchTextValue) {
2389
+ const searchText = normalizeText(searchTextValue)?.toLocaleLowerCase();
2390
+ if (!searchText) {
2391
+ return;
2392
+ }
2393
+ queryBuilder.andWhere([
2394
+ '(',
2395
+ 'LOWER("approvalInstance"."title") LIKE :approvalInstanceSearchText',
2396
+ 'OR LOWER("approvalInstance"."initiator_member_id") LIKE :approvalInstanceSearchText',
2397
+ 'OR LOWER(CAST("approvalInstance"."id" AS text)) LIKE :approvalInstanceSearchText',
2398
+ ')',
2399
+ ].join(' '), {
2400
+ approvalInstanceSearchText: `%${escapeLikePattern(searchText)}%`,
2401
+ });
2402
+ }
2403
+ function escapeLikePattern(value) {
2404
+ return value
2405
+ .replace(/\\/gu, '\\\\')
2406
+ .replace(/%/gu, '\\%')
2407
+ .replace(/_/gu, '\\_');
2408
+ }
2409
+ function isInstanceWithinRange(instance, options) {
2410
+ const fromTime = options.from?.getTime() ?? null;
2411
+ const toTime = options.to?.getTime() ?? null;
2412
+ const candidateTime = (instance.completedAt ??
2413
+ instance.startedAt ??
2414
+ instance.createdAt).getTime();
2415
+ return ((fromTime === null || candidateTime >= fromTime) &&
2416
+ (toTime === null || candidateTime <= toTime));
2417
+ }
2418
+ function readDecisionPolicySnapshot(task) {
2419
+ const value = task.decisionPolicySnapshot;
2420
+ if (isRecord(value) &&
2421
+ typeof value.type === 'string' &&
2422
+ (value.type === 'SINGLE' ||
2423
+ value.type === 'SEQUENTIAL' ||
2424
+ value.type === 'PARALLEL_ALL' ||
2425
+ value.type === 'PARALLEL_ANY')) {
2426
+ return { type: value.type };
2427
+ }
2428
+ if (isRecord(value) &&
2429
+ value.type === 'QUORUM' &&
2430
+ typeof value.threshold === 'number' &&
2431
+ (value.thresholdType === 'COUNT' || value.thresholdType === 'PERCENTAGE')) {
2432
+ return {
2433
+ threshold: value.threshold,
2434
+ thresholdType: value.thresholdType,
2435
+ type: 'QUORUM',
2436
+ };
2437
+ }
2438
+ return { type: 'SINGLE' };
2439
+ }
2440
+ function shouldCompleteTaskAfterDecision({ action, candidates, decisionPolicy, }) {
2441
+ if (action !== workflow_engine_enums_1.TaskDecisionActionEnum.APPROVED) {
2442
+ return true;
2443
+ }
2444
+ if (decisionPolicy.type === 'SINGLE' ||
2445
+ decisionPolicy.type === 'PARALLEL_ANY') {
2446
+ return true;
2447
+ }
2448
+ const completedCount = candidates.filter((candidate) => candidate.status === workflow_engine_enums_1.TaskCandidateStatusEnum.COMPLETED).length;
2449
+ const totalCount = Math.max(candidates.length, 1);
2450
+ if (decisionPolicy.type === 'PARALLEL_ALL' ||
2451
+ decisionPolicy.type === 'SEQUENTIAL') {
2452
+ return completedCount >= totalCount;
2453
+ }
2454
+ const threshold = decisionPolicy.thresholdType === 'PERCENTAGE'
2455
+ ? Math.ceil((totalCount * decisionPolicy.threshold) / 100)
2456
+ : decisionPolicy.threshold;
2457
+ return completedCount >= Math.max(threshold, 1);
2458
+ }
2459
+ function readValueAtPath(value, path) {
2460
+ return path
2461
+ .split('.')
2462
+ .filter((segment) => segment.trim())
2463
+ .reduce((currentValue, segment) => {
2464
+ if (!isRecord(currentValue)) {
2465
+ return undefined;
2466
+ }
2467
+ return currentValue[segment];
2468
+ }, value);
2469
+ }
2470
+ function writeValueAtPath(value, path, nextValue) {
2471
+ const segments = normalizeFormFieldPath(path);
2472
+ if (segments.length === 0) {
2473
+ return value;
2474
+ }
2475
+ return writeNestedValue(value, segments, nextValue);
2476
+ }
2477
+ function writeNestedValue(value, segments, nextValue) {
2478
+ const [currentSegment, ...remainingSegments] = segments;
2479
+ if (!currentSegment) {
2480
+ return value;
2481
+ }
2482
+ if (remainingSegments.length === 0) {
2483
+ return { ...value, [currentSegment]: nextValue };
2484
+ }
2485
+ const currentValue = value[currentSegment];
2486
+ const nestedValue = isRecord(currentValue) ? currentValue : {};
2487
+ return {
2488
+ ...value,
2489
+ [currentSegment]: writeNestedValue(nestedValue, remainingSegments, nextValue),
2490
+ };
2491
+ }
2492
+ function normalizeFormFieldPath(path) {
2493
+ const segments = path
2494
+ .split('.')
2495
+ .map((segment) => segment.trim())
2496
+ .filter(Boolean);
2497
+ if (segments[0] === 'form' || segments[0] === 'formData') {
2498
+ return segments.slice(1);
2499
+ }
2500
+ return segments;
2501
+ }
2502
+ async function executeWebhookServiceAction(serviceTaskDispatcher, action, payload) {
2503
+ try {
2504
+ return await serviceTaskDispatcher.dispatchWebhook({
2505
+ headers: action.headers,
2506
+ payload,
2507
+ url: action.url,
2508
+ });
2509
+ }
2510
+ catch (error) {
2511
+ return {
2512
+ error: error instanceof Error ? error.message : 'Unknown webhook error',
2513
+ ok: false,
2514
+ status: null,
2515
+ };
2516
+ }
2517
+ }
2518
+ function readManagerMemberIdFromInitiatorSnapshot(initiatorMetadataSnapshot, levelsUp) {
2519
+ const managerChain = initiatorMetadataSnapshot.managerChain;
2520
+ if (Array.isArray(managerChain)) {
2521
+ return managerChain[Math.max(levelsUp - 1, 0)];
2522
+ }
2523
+ const managerMemberIds = initiatorMetadataSnapshot.managerMemberIds;
2524
+ if (Array.isArray(managerMemberIds)) {
2525
+ return managerMemberIds[Math.max(levelsUp - 1, 0)];
2526
+ }
2527
+ const customFields = initiatorMetadataSnapshot.customFields;
2528
+ if (isRecord(customFields)) {
2529
+ return customFields.managerMemberId;
2530
+ }
2531
+ return initiatorMetadataSnapshot.managerMemberId;
2532
+ }
2533
+ function isDateRangeActive(value, date) {
2534
+ return (value.effectiveFrom <= date &&
2535
+ (!value.effectiveTo || value.effectiveTo >= date));
2536
+ }
2537
+ function compareManagerResolution(left, right) {
2538
+ const priorityDiff = right.priority - left.priority;
2539
+ if (priorityDiff !== 0) {
2540
+ return priorityDiff;
2541
+ }
2542
+ return (readManagerResolutionScopeRank(right.scopeType) -
2543
+ readManagerResolutionScopeRank(left.scopeType) ||
2544
+ right.effectiveFrom.localeCompare(left.effectiveFrom));
2545
+ }
2546
+ function readManagerResolutionScopeRank(scopeType) {
2547
+ const ranks = {
2548
+ [organization_enums_1.ManagerResolutionScopeTypeEnum.MEMBER]: 3,
2549
+ [organization_enums_1.ManagerResolutionScopeTypeEnum.ORG_UNIT]: 2,
2550
+ [organization_enums_1.ManagerResolutionScopeTypeEnum.POSITION]: 1,
2551
+ };
2552
+ return ranks[scopeType];
2553
+ }
2554
+ function compareMembership(left, right) {
2555
+ if (left.isPrimary !== right.isPrimary) {
2556
+ return left.isPrimary ? -1 : 1;
2557
+ }
2558
+ return right.effectiveFrom.localeCompare(left.effectiveFrom);
2559
+ }
2560
+ function toDateOnlyString(value) {
2561
+ return value.toISOString().slice(0, 10);
2562
+ }
2563
+ function canReadAllWorkflows(scope) {
2564
+ return (scope.roles.includes('BPM_ADMIN') ||
2565
+ scope.permissions.some((permission) => WORKFLOW_READ_ALL_PERMISSIONS.has(permission)));
2566
+ }
2567
+ function isTaskRelatedToMember(task, memberId) {
2568
+ return (task.assigneeMemberId === memberId ||
2569
+ task.originalAssigneeMemberId === memberId);
2570
+ }
2571
+ function readCandidateStatusForClosedTask(taskStatus) {
2572
+ if (taskStatus === workflow_engine_enums_1.TaskStatusEnum.TRANSFERRED) {
2573
+ return workflow_engine_enums_1.TaskCandidateStatusEnum.TRANSFERRED;
2574
+ }
2575
+ if (taskStatus === workflow_engine_enums_1.TaskStatusEnum.COMPLETED) {
2576
+ return workflow_engine_enums_1.TaskCandidateStatusEnum.COMPLETED;
2577
+ }
2578
+ return workflow_engine_enums_1.TaskCandidateStatusEnum.CANCELLED;
2579
+ }
2580
+ function validateSubmittedFormData(schema, formData) {
2581
+ const missingFields = schema.fields.filter((field) => {
2582
+ const fieldValue = formData[field.fieldKey];
2583
+ return (isSubmittedFormFieldVisible(field, schema.fields, formData) &&
2584
+ isSubmittedFormFieldRequired(field, schema.fields, formData) &&
2585
+ !isSubmittedFieldValuePresent(fieldValue));
2586
+ });
2587
+ if (missingFields.length > 0) {
2588
+ throw new common_1.BadRequestException(`Form data is missing required fields: ${missingFields
2589
+ .map((field) => field.label || field.fieldKey)
2590
+ .join(', ')}`);
2591
+ }
2592
+ }
2593
+ function readFormDefinitionSnapshotSchema(snapshot) {
2594
+ const schema = snapshot.schema;
2595
+ if (isFormDefinitionSchema(schema)) {
2596
+ return schema;
2597
+ }
2598
+ throw new common_1.BadRequestException('Approval instance form schema is invalid');
2599
+ }
2600
+ function isFormDefinitionSchema(value) {
2601
+ return (isRecord(value) && value.schemaVersion === 1 && Array.isArray(value.fields));
2602
+ }
2603
+ function isSubmittedFormFieldVisible(field, fields, values) {
2604
+ return field.visibleWhen
2605
+ ? evaluateFormConditionExpression(field.visibleWhen, fields, values, true)
2606
+ : true;
2607
+ }
2608
+ function isSubmittedFormFieldRequired(field, fields, values) {
2609
+ return (field.required ||
2610
+ Boolean(field.requiredWhen
2611
+ ? evaluateFormConditionExpression(field.requiredWhen, fields, values, false)
2612
+ : false));
2613
+ }
2614
+ function evaluateFormConditionExpression(expression, fields, values, fallback) {
2615
+ const rule = parseFormConditionRule(expression);
2616
+ if (!rule) {
2617
+ return fallback;
2618
+ }
2619
+ const field = fields.find((candidate) => candidate.fieldKey === rule.fieldKey);
2620
+ if (!field) {
2621
+ return fallback;
2622
+ }
2623
+ return evaluateFormConditionRule(rule, field, values[field.fieldKey]);
2624
+ }
2625
+ function parseFormConditionRule(expression) {
2626
+ const match = expression
2627
+ .trim()
2628
+ .match(/^form\.([A-Za-z][A-Za-z0-9_]*)\s*(==|!=|>=|<=|>|<)\s*(.+)$/u);
2629
+ if (!match) {
2630
+ return null;
2631
+ }
2632
+ const operator = readFormConditionOperatorFromSymbol(match[2]);
2633
+ if (!operator) {
2634
+ return null;
2635
+ }
2636
+ return {
2637
+ fieldKey: match[1],
2638
+ operator,
2639
+ value: parseFormConditionLiteral(match[3]),
2640
+ };
2641
+ }
2642
+ function evaluateFormConditionRule(rule, field, value) {
2643
+ if (Array.isArray(value)) {
2644
+ return evaluateArrayFormCondition(value, rule);
2645
+ }
2646
+ if (field.type === 'boolean') {
2647
+ return compareFormConditionValues(value === true ? 'true' : 'false', rule.value, rule.operator);
2648
+ }
2649
+ if (isNumberFormField(field)) {
2650
+ return compareNumericFormCondition(value, rule);
2651
+ }
2652
+ return compareFormConditionValues(typeof value === 'undefined' || value === null ? '' : String(value), rule.value, rule.operator);
2653
+ }
2654
+ function evaluateArrayFormCondition(value, rule) {
2655
+ const stringValues = value.filter((entry) => typeof entry === 'string');
2656
+ if (rule.operator === 'equals') {
2657
+ return stringValues.includes(rule.value);
2658
+ }
2659
+ if (rule.operator === 'notEquals') {
2660
+ return !stringValues.includes(rule.value);
2661
+ }
2662
+ return false;
2663
+ }
2664
+ function compareNumericFormCondition(value, rule) {
2665
+ const actualValue = typeof value === 'number' ? value : Number(value);
2666
+ const expectedValue = Number(rule.value);
2667
+ if (!Number.isFinite(actualValue) || !Number.isFinite(expectedValue)) {
2668
+ return false;
2669
+ }
2670
+ return compareFormConditionValues(actualValue, expectedValue, rule.operator);
2671
+ }
2672
+ function compareFormConditionValues(actualValue, expectedValue, operator) {
2673
+ if (operator === 'equals') {
2674
+ return actualValue === expectedValue;
2675
+ }
2676
+ if (operator === 'notEquals') {
2677
+ return actualValue !== expectedValue;
2678
+ }
2679
+ if (operator === 'greaterThan') {
2680
+ return actualValue > expectedValue;
2681
+ }
2682
+ if (operator === 'greaterThanOrEqual') {
2683
+ return actualValue >= expectedValue;
2684
+ }
2685
+ if (operator === 'lessThan') {
2686
+ return actualValue < expectedValue;
2687
+ }
2688
+ return actualValue <= expectedValue;
2689
+ }
2690
+ function readFormConditionOperatorFromSymbol(symbol) {
2691
+ const operators = {
2692
+ '!=': 'notEquals',
2693
+ '<': 'lessThan',
2694
+ '<=': 'lessThanOrEqual',
2695
+ '==': 'equals',
2696
+ '>': 'greaterThan',
2697
+ '>=': 'greaterThanOrEqual',
2698
+ };
2699
+ return symbol ? (operators[symbol] ?? null) : null;
2700
+ }
2701
+ function parseFormConditionLiteral(value) {
2702
+ const trimmedValue = value.trim();
2703
+ if ((trimmedValue.startsWith('"') && trimmedValue.endsWith('"')) ||
2704
+ (trimmedValue.startsWith("'") && trimmedValue.endsWith("'"))) {
2705
+ return trimmedValue.slice(1, -1);
2706
+ }
2707
+ return trimmedValue;
2708
+ }
2709
+ function isSubmittedFieldValuePresent(value) {
2710
+ if (typeof value === 'undefined' || value === null) {
2711
+ return false;
2712
+ }
2713
+ if (typeof value === 'string') {
2714
+ return value.trim().length > 0;
2715
+ }
2716
+ if (Array.isArray(value)) {
2717
+ return value.length > 0;
2718
+ }
2719
+ return true;
2720
+ }
2721
+ function isNumberFormField(field) {
2722
+ return field.type === 'number' || field.type === 'money';
2723
+ }
2724
+ function createDryRunStep(input) {
2725
+ return Object.assign(new workflow_dry_run_object_1.WorkflowDryRunStepObject(), {
2726
+ assigneeMemberId: input.assigneeMemberId,
2727
+ edgeDefault: input.edge ? Boolean(input.edge.data.isDefault) : null,
2728
+ edgeId: input.edge?.id ?? null,
2729
+ edgeLabel: input.edge ? readDryRunEdgeLabel(input.edge) : null,
2730
+ edgeMatched: input.edgeMatched,
2731
+ edgeReason: input.edgeReason,
2732
+ entryCondition: input.entryCondition,
2733
+ entryConditionMatched: input.entryConditionMet,
2734
+ id: `dry-run-step-${input.stepIndex + 1}`,
2735
+ message: input.message,
2736
+ nodeId: input.node.id,
2737
+ nodeLabel: input.node.data.label,
2738
+ nodeType: input.node.type,
2739
+ status: input.status,
2740
+ });
2741
+ }
2742
+ function readDryRunEdgeLabel(edge) {
2743
+ return edge.data.label?.trim() || edge.id;
2744
+ }
2745
+ function readDryRunEdgeReason(edge) {
2746
+ if (!edge) {
2747
+ return null;
2748
+ }
2749
+ if (edge.data.isDefault) {
2750
+ return '其他條件不符合時採用預設路徑。';
2751
+ }
2752
+ const expression = edge.data.condition?.trim();
2753
+ if (expression) {
2754
+ return `條件成立:${expression}`;
2755
+ }
2756
+ if (edge.data.conditionFieldKey && edge.data.conditionOperator) {
2757
+ return `條件成立:${edge.data.conditionFieldKey} ${edge.data.conditionOperator}${edge.data.conditionValue ? ` ${edge.data.conditionValue}` : ''}`;
2758
+ }
2759
+ return '無條件路徑。';
2760
+ }
2761
+ function readDryRunStepStatus(node, entryConditionMet) {
2762
+ if (!entryConditionMet) {
2763
+ return 'SKIPPED';
2764
+ }
2765
+ if (node.type === 'userTask') {
2766
+ return 'WAITING';
2767
+ }
2768
+ if (node.type === 'endEvent') {
2769
+ return 'COMPLETED';
2770
+ }
2771
+ return 'PASSED';
2772
+ }
2773
+ function readDryRunStepMessage(node, entryConditionMet) {
2774
+ if (!entryConditionMet) {
2775
+ return '進入條件不符合,略過節點。';
2776
+ }
2777
+ if (node.type === 'userTask') {
2778
+ return '將建立待簽任務。';
2779
+ }
2780
+ if (node.type === 'endEvent') {
2781
+ return `流程結束:${node.data.endState ?? 'APPROVED'}`;
2782
+ }
2783
+ return '節點條件通過。';
2784
+ }
2785
+ //# sourceMappingURL=workflow-engine.service.js.map