@gadmin2n/schematics 0.0.139 → 0.0.141

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 (329) hide show
  1. package/dist/lib/application/files/gadmin2-game-angle-demo/Jenkinsfile +6 -34
  2. package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/approval.prisma +199 -0
  3. package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/system.prisma +9 -0
  4. package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/workflow.prisma +191 -14
  5. package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Event.ts +1 -1
  6. package/dist/lib/application/files/gadmin2-game-angle-demo/gitignore +6 -5
  7. package/dist/lib/application/files/gadmin2-game-angle-demo/readme.md +10 -3
  8. package/dist/lib/application/files/gadmin2-game-angle-demo/server/migrate-between-pg-schemas.js +6 -4
  9. package/dist/lib/application/files/gadmin2-game-angle-demo/server/package.json +5 -2
  10. package/dist/lib/application/files/gadmin2-game-angle-demo/server/prisma.config.ts +12 -3
  11. package/dist/lib/application/files/gadmin2-game-angle-demo/server/scripts/gen-jwt.ts +8 -1
  12. package/dist/lib/application/files/gadmin2-game-angle-demo/server/scripts/migrate-human-approval-configs.ts +86 -0
  13. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow-node-types.ts +710 -121
  14. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow.seed.ts +125 -10
  15. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/config/index.ts +28 -0
  16. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/json-body-parser.spec.ts +61 -0
  17. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/json-body-parser.ts +7 -0
  18. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/structured-all-exceptions.filter.spec.ts +54 -0
  19. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/structured-all-exceptions.filter.ts +86 -0
  20. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/main.ts +9 -3
  21. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/approvalChannel/approval-channel.controller.ts +89 -0
  22. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/approvalChannel/approval-channel.dto.ts +68 -0
  23. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/approvalChannel/approval-channel.service.spec.ts +177 -0
  24. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/approvalChannel/approval-channel.service.ts +283 -0
  25. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/approvalChannel/approvalChannel.module.ts +10 -0
  26. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/codeRuntime/code-executor.spec.ts +42 -0
  27. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/codeRuntime/code-executor.ts +108 -0
  28. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/codeRuntime/code-runtime.controller.ts +101 -0
  29. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/codeRuntime/code-runtime.dto.ts +29 -0
  30. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/codeRuntime/codeRuntime.module.ts +21 -0
  31. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/external-interaction-handler.registry.ts +30 -0
  32. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/external-interaction-handler.ts +15 -0
  33. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/external-interaction-runtime.controller.spec.ts +105 -0
  34. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/external-interaction-runtime.controller.ts +96 -0
  35. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/external-interaction-runtime.dto.ts +78 -0
  36. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/external-interaction-signal-outbox.service.spec.ts +56 -0
  37. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/external-interaction-signal-outbox.service.ts +111 -0
  38. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/external-interaction-token.spec.ts +23 -0
  39. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/external-interaction-token.ts +18 -0
  40. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/external-interaction.service.spec.ts +240 -0
  41. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/external-interaction.service.ts +339 -0
  42. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/external-interaction.types.ts +74 -0
  43. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/external-wait.handler.ts +47 -0
  44. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/externalInteraction.module.ts +41 -0
  45. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/human-approval.handler.spec.ts +70 -0
  46. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/human-approval.handler.ts +76 -0
  47. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/workflow-callback-url.controller.ts +12 -0
  48. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/workflow-callback-url.service.spec.ts +227 -0
  49. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/workflow-callback-url.service.ts +121 -0
  50. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/httpRequest/http-request-runtime.controller.ts +160 -0
  51. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/httpRequest/http-request-runtime.dto.ts +41 -0
  52. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/httpRequest/http-request-runtime.util.spec.ts +171 -0
  53. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/httpRequest/http-request-runtime.util.ts +245 -0
  54. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/httpRequest/httpRequest.module.ts +22 -0
  55. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/approval-rule-evaluator.spec.ts +68 -0
  56. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/approval-rule-evaluator.ts +126 -0
  57. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/approval-rule.types.ts +34 -0
  58. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/approval-signal-outbox.service.ts +115 -0
  59. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/approval-subject-source.util.spec.ts +45 -0
  60. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/approval-subject-source.util.ts +59 -0
  61. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/human-approval.controller.ts +72 -0
  62. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/human-approval.dto.ts +144 -0
  63. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/human-approval.errors.ts +19 -0
  64. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/human-approval.external-decision.spec.ts +350 -0
  65. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/human-approval.service.ts +774 -0
  66. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/humanApproval.module.ts +46 -0
  67. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/legacy-approval-config-migration.ts +227 -0
  68. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/outbox-retry.util.ts +14 -0
  69. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/runtime/human-approval-runtime.controller.ts +151 -0
  70. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/runtime/human-approval-runtime.dto.ts +88 -0
  71. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/runtime/runtime-token.guard.ts +43 -0
  72. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/index.ts +21 -1
  73. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/integrationConnection/integration-connection.controller.ts +77 -0
  74. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/integrationConnection/integration-connection.dto.ts +98 -0
  75. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/integrationConnection/integration-connection.service.ts +277 -0
  76. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/integrationConnection/integration-secret-cipher.spec.ts +14 -0
  77. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/integrationConnection/integration-secret-cipher.ts +59 -0
  78. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/integrationConnection/integrationConnection.module.ts +10 -0
  79. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/subjectProvider/actor-context.util.ts +27 -0
  80. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/subjectProvider/providers/user-group.provider.ts +208 -0
  81. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/subjectProvider/providers/user.provider.ts +168 -0
  82. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/subjectProvider/subject-provider.controller.spec.ts +157 -0
  83. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/subjectProvider/subject-provider.controller.ts +176 -0
  84. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/subjectProvider/subject-provider.dto.ts +21 -0
  85. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/subjectProvider/subject-provider.errors.ts +78 -0
  86. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/subjectProvider/subject-provider.registry.spec.ts +100 -0
  87. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/subjectProvider/subject-provider.registry.ts +115 -0
  88. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/subjectProvider/subject-provider.types.ts +97 -0
  89. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/subjectProvider/subjectProvider.module.ts +34 -0
  90. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/temporalTenantConfig/temporalTenantConfig.controller.spec.ts +89 -1
  91. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/temporalTenantConfig/temporalTenantConfig.controller.ts +4 -0
  92. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/userGroup/user-group.controller.ts +119 -0
  93. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/userGroup/user-group.dto.ts +91 -0
  94. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/userGroup/user-group.errors.ts +34 -0
  95. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/userGroup/user-group.service.spec.ts +161 -0
  96. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/userGroup/user-group.service.ts +390 -0
  97. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/userGroup/userGroup.module.ts +10 -0
  98. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/dsl-validate.util.spec.ts +17 -0
  99. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/dsl-validate.util.ts +8 -2
  100. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/parameter-binding.ts +93 -0
  101. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/temporal.service.spec.ts +34 -1
  102. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/temporal.service.ts +26 -0
  103. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-callable.spec.ts +158 -0
  104. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-callable.ts +155 -0
  105. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-compiler.service.spec.ts +512 -0
  106. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-compiler.service.ts +368 -0
  107. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-compiler.types.ts +43 -0
  108. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-data-edge.util.spec.ts +174 -0
  109. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-data-edge.util.ts +149 -0
  110. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-dsl-comparison.ts +43 -0
  111. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-execution.dto.ts +1 -1
  112. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-export.dto.ts +4 -0
  113. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-export.service.ts +70 -28
  114. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-handle-validator.ts +128 -0
  115. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-http-response.spec.ts +85 -0
  116. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-http-response.ts +157 -0
  117. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.controller.spec.ts +59 -0
  118. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.controller.ts +27 -5
  119. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.module.ts +18 -3
  120. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.spec.ts +493 -21
  121. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.ts +426 -90
  122. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowConfig/node-config-validator.service.spec.ts +79 -0
  123. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowConfig/node-config-validator.service.ts +62 -0
  124. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowConfig/schema.types.ts +10 -0
  125. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowConfig/subject-config-validator.service.spec.ts +124 -0
  126. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowConfig/subject-config-validator.service.ts +236 -0
  127. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowConfig/widget-catalog.service.spec.ts +49 -0
  128. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowConfig/widget-catalog.service.ts +69 -0
  129. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowConfig/workflowConfig.module.ts +20 -0
  130. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeInstance/node-instance-config.util.spec.ts +15 -0
  131. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeInstance/node-instance-config.util.ts +25 -0
  132. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeInstance/workflowNodeInstance.controller.ts +103 -11
  133. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeInstance/workflowNodeInstance.dto.ts +98 -0
  134. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeInstance/workflowNodeInstance.module.ts +3 -0
  135. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeInstance/workflowNodeInstance.service.spec.ts +72 -62
  136. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeInstance/workflowNodeInstance.service.ts +148 -11
  137. package/dist/lib/application/files/gadmin2-game-angle-demo/web/package.json +13 -9
  138. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/App.tsx +5 -2
  139. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/authProvider.ts +2 -2
  140. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/BulkActions.tsx +0 -5
  141. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/PageHeader.tsx +46 -0
  142. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/SearchBar.tsx +26 -18
  143. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/auditLog/components/eventList/index.tsx +2 -2
  144. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/auditLog/components/logButton/index.tsx +24 -17
  145. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/auditLog/components/logList/index.tsx +7 -18
  146. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/auditLog/components/modalDiffViewer/index.tsx +2 -2
  147. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/contexts/color-mode/index.tsx +288 -33
  148. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/contexts/color-mode/themeStyles.ts +97 -0
  149. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/index.ts +1 -0
  150. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/SettingDrawer.tsx +251 -0
  151. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/header.tsx +47 -19
  152. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/layout.tsx +18 -9
  153. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/menuRenderer.tsx +155 -0
  154. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/sider.tsx +123 -129
  155. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/config/routeRegistry.test.ts +10 -0
  156. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/config/routeRegistry.tsx +18 -0
  157. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/constants/layout.ts +32 -3
  158. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/dev-shell/AddDataModal.tsx +98 -3
  159. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/dev-shell/AddPageModal.tsx +101 -0
  160. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/dev-shell/DevShell.tsx +8 -0
  161. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/dev-shell/style.css +51 -0
  162. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/helpers/form.tsx +1 -1
  163. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/helpers/list.tsx +1 -1
  164. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/helpers/login.ts +40 -1
  165. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/helpers/show.tsx +5 -5
  166. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/hooks/useDynamicResources.tsx +16 -1
  167. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/hooks/useListPageState.ts +10 -7
  168. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/i18n.ts +4 -2
  169. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/en/common.json +26 -2
  170. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/en/workflow.json +575 -0
  171. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/zh_CN/common.json +26 -2
  172. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/zh_CN/workflow.json +575 -0
  173. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/plugins/devShellPlugin.ts +152 -2
  174. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agenda/index.tsx +9 -15
  175. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agenda/show.tsx +41 -9
  176. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agendaJob/edit.tsx +1 -1
  177. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agendaJob/list.tsx +10 -8
  178. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agendaJob/show.tsx +3 -3
  179. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/components/action-cell.css +1 -1
  180. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/edit.tsx +1 -1
  181. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/list.tsx +141 -138
  182. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/show.tsx +3 -6
  183. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasCell.css +3 -0
  184. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasCell.tsx +45 -13
  185. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasComponentLibrary.tsx +45 -22
  186. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasListPage.tsx +175 -119
  187. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasPage.tsx +248 -74
  188. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasToolbar.tsx +33 -18
  189. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CodeFloatWindow.tsx +16 -10
  190. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/PublishModal.tsx +1 -1
  191. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/canvasContextMenuRegistry.tsx +31 -0
  192. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/canvasDefaults.ts +15 -0
  193. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/BarChartDataSourceModal.tsx +16 -11
  194. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/CanvasAiModal.tsx +25 -21
  195. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/ExportModal.tsx +6 -1
  196. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/LineChartDataSourceModal.tsx +9 -10
  197. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/MultiChartConfigModal.tsx +4 -7
  198. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/MultiChartDataSourceModal.tsx +23 -9
  199. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/NumCardDataSourceModal.tsx +16 -11
  200. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/PromptModal.tsx +2 -5
  201. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/RadarChartDataSourceModal.tsx +16 -11
  202. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/TableConfigModal.tsx +2 -9
  203. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/TableDataSourceModal.tsx +23 -12
  204. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/canvasModalProps.ts +2 -17
  205. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/sectionCompactor.test.ts +149 -0
  206. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/sectionCompactor.ts +111 -17
  207. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/types.ts +1 -0
  208. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/whiteboardLayout.test.ts +83 -0
  209. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/whiteboardLayout.ts +118 -0
  210. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/whiteboardName.test.ts +26 -0
  211. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas-page/index.tsx +9 -1
  212. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/game/edit.tsx +1 -1
  213. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/game/list.tsx +10 -8
  214. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/game/show.tsx +3 -6
  215. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/human-approval/DecisionModal.tsx +71 -0
  216. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/human-approval/inbox.tsx +209 -0
  217. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/Components/AssignRolesModal.tsx +6 -1
  218. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/list.tsx +47 -150
  219. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/permissionReadme/index.tsx +21 -20
  220. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/Components/modal.tsx +1 -1
  221. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/list.tsx +115 -118
  222. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/Components/modal.tsx +1 -1
  223. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/list.tsx +61 -64
  224. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/components/show-drawer.tsx +1 -1
  225. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/list.tsx +187 -200
  226. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/approval-channels/ApprovalChannelModal.test.tsx +84 -0
  227. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/approval-channels/ApprovalChannelModal.tsx +141 -0
  228. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/approval-channels/approval-channel.types.ts +16 -0
  229. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/approval-channels/index.tsx +210 -0
  230. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/CreateNodeInstanceDrawer.tsx +154 -0
  231. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/CustomNode.tsx +194 -5
  232. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/DeletableEdge.tsx +76 -0
  233. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/DslView.tsx +1 -1
  234. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/EnhancedFlowRenderer.tsx +176 -22
  235. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/ExecutionStatusNode.test.tsx +62 -0
  236. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/ExecutionStatusNode.tsx +94 -5
  237. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/ExportModal.tsx +15 -10
  238. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/FlowRenderer.tsx +251 -70
  239. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/ImportModal.tsx +18 -24
  240. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/InstanceRequiredDialog.tsx +106 -0
  241. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/LookupConnectionDialog.test.tsx +45 -0
  242. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/LookupConnectionDialog.tsx +43 -0
  243. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NewNodeDefaults.test.tsx +283 -0
  244. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NodeEditModal.tsx +6 -7
  245. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NodePropertyPanel.test.tsx +335 -0
  246. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NodePropertyPanel.tsx +433 -237
  247. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/ParameterBindingEditor.tsx +179 -0
  248. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/RunWorkflowModal.tsx +18 -16
  249. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/StatusCards.tsx +20 -16
  250. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/VersionPanel.tsx +6 -4
  251. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/connections/ConnectionModal.test.tsx +68 -0
  252. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/connections/ConnectionModal.tsx +367 -0
  253. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/connections/connection.types.ts +21 -0
  254. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/connections/index.tsx +180 -0
  255. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/editor.tsx +38 -22
  256. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/hooks/useNodeInstanceLibrary.ts +41 -0
  257. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/hooks/useWorkflowAgent.ts +6 -0
  258. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/index.tsx +29 -35
  259. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/instance-detail.tsx +8 -11
  260. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/instances.tsx +4 -3
  261. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/components/NodeInstanceForm.tsx +56 -23
  262. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/create.tsx +21 -6
  263. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/edit.tsx +17 -20
  264. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/index.tsx +9 -17
  265. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/schema-form/SchemaField.tsx +311 -0
  266. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/schema-form/SchemaForm.test.tsx +218 -0
  267. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/schema-form/SchemaForm.tsx +83 -0
  268. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/schema-form/default-widgets.tsx +135 -0
  269. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/schema-form/schema-defaults.ts +36 -0
  270. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/schema-form/schema.types.ts +32 -0
  271. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/schema-form/widget-registry.test.tsx +23 -0
  272. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/schema-form/widget-registry.tsx +52 -0
  273. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/show.tsx +9 -13
  274. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ApprovalRequestTriggerConfigEditor.tsx +39 -0
  275. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ApprovalRuleEditor.test.tsx +20 -0
  276. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ApprovalRuleEditor.tsx +72 -0
  277. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ApproverSourceEditor.test.tsx +47 -0
  278. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ApproverSourceEditor.tsx +185 -0
  279. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/CallableInputParametersEditor.test.tsx +102 -0
  280. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/CallableInputParametersEditor.tsx +150 -0
  281. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/CodeConfigEditor.tsx +103 -0
  282. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ExternalRequestConfigEditor.test.tsx +135 -0
  283. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ExternalRequestConfigEditor.tsx +273 -0
  284. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ExternalWaitConfigEditor.tsx +303 -0
  285. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/HttpRequestConfigEditor.test.tsx +189 -0
  286. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/HttpRequestConfigEditor.tsx +706 -0
  287. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/HumanApprovalConfigEditor.test.tsx +86 -0
  288. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/HumanApprovalConfigEditor.tsx +458 -0
  289. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ReturnResultConfigEditor.test.tsx +81 -0
  290. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/ReturnResultConfigEditor.tsx +281 -0
  291. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/SubjectPicker.test.tsx +61 -0
  292. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/SubjectPicker.tsx +266 -0
  293. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/SubmitApprovalResultConfigEditor.tsx +104 -0
  294. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/SubmitExternalResultConfigEditor.tsx +98 -0
  295. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/UserGroupCreateModal.tsx +70 -0
  296. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/UserGroupEditModal.tsx +138 -0
  297. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/approver-source.test.ts +25 -0
  298. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/approver-source.ts +42 -0
  299. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/subject-provider.api.ts +95 -0
  300. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/show.tsx +15 -11
  301. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/temporal-tenant/index.test.tsx +152 -0
  302. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/temporal-tenant/index.tsx +438 -141
  303. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/types/parameter-binding.ts +49 -0
  304. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/types.ts +31 -2
  305. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/utils/lookupConnection.test.ts +208 -0
  306. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/utils/lookupConnection.ts +246 -0
  307. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/utils/newNodeConfig.ts +77 -0
  308. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/utils/removeWorkflowEdges.test.ts +34 -0
  309. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/utils/removeWorkflowEdges.ts +18 -0
  310. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/utils/resolveLookupPorts.ts +66 -0
  311. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/utils/resolveOutputs.test.ts +39 -0
  312. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/utils/resolveOutputs.ts +48 -0
  313. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/utils/resolveWorkflowDisplay.test.ts +51 -0
  314. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/utils/resolveWorkflowDisplay.ts +32 -0
  315. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflowEventOutbox/edit.tsx +1 -1
  316. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflowEventOutbox/list.tsx +10 -8
  317. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflowEventOutbox/show.tsx +3 -3
  318. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/antd-theme-vars.css +93 -0
  319. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/antd.css +8 -8
  320. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/fc.css +4 -4
  321. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/index.css +40 -5
  322. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/show-drawer.module.css +3 -3
  323. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/show-page.module.css +3 -3
  324. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/theme-styles.css +81 -0
  325. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/test/setup.ts +5 -0
  326. package/dist/lib/application/files/gadmin2-game-angle-demo/web/vite.config.ts +1 -7
  327. package/dist/lib/application/files/gadmin2-game-angle-demo/web/vitest.config.ts +12 -0
  328. package/package.json +1 -1
  329. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/components/DynamicConfigForm.tsx +0 -154
@@ -0,0 +1,774 @@
1
+ import { HttpStatus, Injectable, Optional } from '@nestjs/common';
2
+ import { Prisma } from '@prisma/client';
3
+ import { PrismaService } from 'nestjs-prisma';
4
+ import { ExternalInteractionService } from '../externalInteraction/external-interaction.service';
5
+ import type { ExternalResultInput } from '../externalInteraction/external-interaction.types';
6
+ import { SubjectProviderRegistry } from '../subjectProvider/subject-provider.registry';
7
+ import { ActorContext } from '../subjectProvider/subject-provider.types';
8
+ import { hashNodeInstanceConfig } from '../workflowNodeInstance/node-instance-config.util';
9
+ import { evaluateRules } from './approval-rule-evaluator';
10
+ import {
11
+ ApprovalSubjectSource,
12
+ materializeApprovalSubjects,
13
+ } from './approval-subject-source.util';
14
+ import {
15
+ ApprovalConfigSnapshotDto,
16
+ StartApprovalRequestDto,
17
+ SubmitApprovalDecisionDto,
18
+ SubmitExternalApprovalDecisionDto,
19
+ } from './human-approval.dto';
20
+ import { HumanApprovalDomainError } from './human-approval.errors';
21
+
22
+ type SignalResult = 'APPROVED' | 'REJECTED' | 'CANCELLED';
23
+ type SignalReason = 'RULE_SATISFIED' | 'TIMEOUT' | 'WORKFLOW_CANCELLED';
24
+ type DispatchState = 'DISPATCHING' | 'WAITING_APPROVAL' | 'DISPATCH_FAILED';
25
+
26
+ @Injectable()
27
+ export class HumanApprovalService {
28
+ constructor(
29
+ private readonly prisma: PrismaService,
30
+ private readonly providers: SubjectProviderRegistry,
31
+ @Optional()
32
+ private readonly externalInteractions?: ExternalInteractionService,
33
+ ) {}
34
+
35
+ async startRequest(input: StartApprovalRequestDto, actor: ActorContext) {
36
+ const workflowInstanceId = toBigInt(input.workflowInstanceId);
37
+ const workflowVersionId = toBigInt(input.workflowVersionId);
38
+ const snapshot = input.configSnapshot as unknown as Prisma.JsonValue;
39
+ if (hashNodeInstanceConfig(snapshot) !== input.configHash) {
40
+ throw approvalError(
41
+ 'APPROVAL_CONFIG_MISMATCH',
42
+ 'Approval configuration does not match the workflow version snapshot',
43
+ HttpStatus.CONFLICT,
44
+ );
45
+ }
46
+
47
+ const existing = await this.prisma.approvalRequest.findUnique({
48
+ where: {
49
+ workflowInstanceId_nodeId: { workflowInstanceId, nodeId: input.nodeId },
50
+ },
51
+ });
52
+ if (existing) return existing;
53
+
54
+ const channelRef = input.configSnapshot.channelRef?.trim();
55
+ if (channelRef) {
56
+ const channel = await this.prisma.approvalChannel.findUnique({
57
+ where: { code: channelRef },
58
+ select: { isEnabled: true },
59
+ });
60
+ if (!channel?.isEnabled)
61
+ throw approvalError(
62
+ 'APPROVAL_CONFIG_MISMATCH',
63
+ `Approval channel ${channelRef} was not found or is disabled`,
64
+ HttpStatus.BAD_REQUEST,
65
+ );
66
+ }
67
+
68
+ let materializedSubjects;
69
+ try {
70
+ materializedSubjects = mergeSubjectSources(
71
+ materializeApprovalSubjects(
72
+ input.configSnapshot.subjects as unknown as ApprovalSubjectSource[],
73
+ input.dynamicInputs,
74
+ ),
75
+ input.resolvedSubjects ?? [],
76
+ );
77
+ } catch (error) {
78
+ throw approvalError(
79
+ 'APPROVAL_CONFIG_MISMATCH',
80
+ error instanceof Error ? error.message : 'Invalid dynamic approvers',
81
+ HttpStatus.BAD_REQUEST,
82
+ );
83
+ }
84
+ const resolved = await this.resolveSubjects(materializedSubjects, actor);
85
+ const startedAt = new Date();
86
+ const expiresAt =
87
+ input.configSnapshot.timeout.durationSeconds > 0
88
+ ? new Date(
89
+ startedAt.getTime() +
90
+ input.configSnapshot.timeout.durationSeconds * 1000,
91
+ )
92
+ : null;
93
+ const blocked = resolved.candidates.length === 0;
94
+
95
+ return this.prisma.$transaction(async (tx) => {
96
+ const request = await tx.approvalRequest.create({
97
+ data: {
98
+ workflowInstanceId,
99
+ nodeId: input.nodeId,
100
+ workflowVersionId,
101
+ configHash: input.configHash,
102
+ configSnapshot: snapshot as Prisma.InputJsonValue,
103
+ approvalRule: input.configSnapshot
104
+ .approvalRule as unknown as Prisma.InputJsonValue,
105
+ rejectionRule: input.configSnapshot
106
+ .rejectionRule as unknown as Prisma.InputJsonValue,
107
+ status: blocked ? 'BLOCKED' : 'PENDING',
108
+ blockedReason: blocked
109
+ ? 'No eligible approval candidates resolved'
110
+ : null,
111
+ startedAt,
112
+ expiresAt,
113
+ completedAt: blocked ? startedAt : null,
114
+ creator: actor.userId,
115
+ subjects: { create: resolved.subjects },
116
+ candidates: { create: resolved.candidates },
117
+ },
118
+ });
119
+ if (channelRef && !blocked) {
120
+ const interaction =
121
+ this.externalInteractions &&
122
+ input.tenantNamespace &&
123
+ input.temporalRunId
124
+ ? await this.externalInteractions.createInteractionInTransaction(
125
+ {
126
+ tenantNamespace: input.tenantNamespace,
127
+ handlerType: 'HUMAN_APPROVAL',
128
+ handlerConfig: {
129
+ approvalRequestId: String(request.id),
130
+ allowedOutcomes: ['APPROVE', 'REJECT'],
131
+ },
132
+ workflowInstanceId: String(workflowInstanceId),
133
+ workflowVersionId: String(workflowVersionId),
134
+ temporalRunId: input.temporalRunId,
135
+ nodeId: input.nodeId,
136
+ ...(expiresAt && { expiresAt: expiresAt.toISOString() }),
137
+ },
138
+ tx,
139
+ )
140
+ : undefined;
141
+ await tx.workflowEventOutbox.create({
142
+ data: {
143
+ eventName: `approval.requested.${channelRef}`,
144
+ payload: {
145
+ workflow: input.channelInputs ?? {},
146
+ system: {
147
+ approval: {
148
+ channel: channelRef,
149
+ requestId: String(request.id),
150
+ candidates: resolved.candidates.map((candidate) => ({
151
+ approverId: candidate.principalRef,
152
+ displayName: candidate.displayName,
153
+ principalType: candidate.principalType,
154
+ })),
155
+ expiresAt: expiresAt?.toISOString() ?? null,
156
+ },
157
+ caller: {
158
+ workflowInstanceId: String(workflowInstanceId),
159
+ workflowVersionId: String(workflowVersionId),
160
+ nodeId: input.nodeId,
161
+ },
162
+ invocation: {
163
+ id: `approval:${request.id}:${channelRef}`,
164
+ },
165
+ ...(interaction && {
166
+ callback: { token: interaction.callbackToken },
167
+ }),
168
+ },
169
+ } as Prisma.InputJsonValue,
170
+ },
171
+ });
172
+ }
173
+ return request;
174
+ });
175
+ }
176
+
177
+ async submitDecision(
178
+ requestId: bigint,
179
+ input: SubmitApprovalDecisionDto,
180
+ actor: ActorContext,
181
+ ) {
182
+ return this.prisma.$transaction(async (tx) => {
183
+ const request = await tx.approvalRequest.findUnique({
184
+ where: { id: requestId },
185
+ include: {
186
+ candidates: true,
187
+ decisions: { include: { candidate: true } },
188
+ },
189
+ });
190
+ if (!request) throw notFound(requestId);
191
+
192
+ const candidate = request.candidates.find(
193
+ (item) =>
194
+ item.invalidatedAt === null && item.principalRef === actor.userId,
195
+ );
196
+ if (!candidate) {
197
+ throw approvalError(
198
+ 'APPROVAL_NOT_CANDIDATE',
199
+ 'Actor is not an active approval candidate',
200
+ HttpStatus.FORBIDDEN,
201
+ );
202
+ }
203
+ return (
204
+ await this.recordDecision(
205
+ tx,
206
+ request,
207
+ candidate,
208
+ input,
209
+ actor.userId,
210
+ input.payload,
211
+ )
212
+ ).decision;
213
+ });
214
+ }
215
+
216
+ async submitExternalDecision(
217
+ requestId: bigint,
218
+ input: SubmitExternalApprovalDecisionDto,
219
+ ) {
220
+ return this.prisma.$transaction(async (tx) => {
221
+ const request = await tx.approvalRequest.findUnique({
222
+ where: { id: requestId },
223
+ include: {
224
+ candidates: true,
225
+ decisions: { include: { candidate: true } },
226
+ },
227
+ });
228
+ if (!request) throw notFound(requestId);
229
+
230
+ const snapshot = asObject(request.configSnapshot);
231
+ if (snapshot.channelRef !== input.channelRef)
232
+ throw approvalError(
233
+ 'APPROVAL_CHANNEL_MISMATCH',
234
+ 'Callback channel does not match the channel selected by this approval request',
235
+ HttpStatus.FORBIDDEN,
236
+ );
237
+
238
+ const candidate = request.candidates.find(
239
+ (item) =>
240
+ item.invalidatedAt === null && item.principalRef === input.approverId,
241
+ );
242
+ if (!candidate)
243
+ throw approvalError(
244
+ 'APPROVAL_NOT_CANDIDATE',
245
+ 'External approver is not an active approval candidate',
246
+ HttpStatus.FORBIDDEN,
247
+ );
248
+
249
+ return (
250
+ await this.recordDecision(
251
+ tx,
252
+ request,
253
+ candidate,
254
+ input,
255
+ `external:${input.channelRef}:${input.approverId}`,
256
+ {
257
+ ...(input.payload ?? {}),
258
+ externalEventId: input.eventId,
259
+ approvalChannel: input.channelRef,
260
+ },
261
+ )
262
+ ).decision;
263
+ });
264
+ }
265
+
266
+ async handleExternalInteractionDecision(
267
+ tx: any,
268
+ requestId: bigint,
269
+ input: ExternalResultInput,
270
+ ): Promise<'PENDING' | 'APPROVED' | 'REJECTED'> {
271
+ if (input.outcome !== 'APPROVE' && input.outcome !== 'REJECT')
272
+ throw approvalError(
273
+ 'APPROVAL_CONFIG_MISMATCH',
274
+ 'External approval outcome must be APPROVE or REJECT',
275
+ HttpStatus.BAD_REQUEST,
276
+ );
277
+ if (!input.operatorId)
278
+ throw approvalError(
279
+ 'APPROVAL_NOT_CANDIDATE',
280
+ 'External approval requires an operator',
281
+ HttpStatus.FORBIDDEN,
282
+ );
283
+ const request = await tx.approvalRequest.findUnique({
284
+ where: { id: requestId },
285
+ include: {
286
+ candidates: true,
287
+ decisions: { include: { candidate: true } },
288
+ },
289
+ });
290
+ if (!request) throw notFound(requestId);
291
+ const candidate = request.candidates.find(
292
+ (item) =>
293
+ item.invalidatedAt === null && item.principalRef === input.operatorId,
294
+ );
295
+ if (!candidate)
296
+ throw approvalError(
297
+ 'APPROVAL_NOT_CANDIDATE',
298
+ 'External approver is not an active approval candidate',
299
+ HttpStatus.FORBIDDEN,
300
+ );
301
+ const data = asObject(input.data);
302
+ const result = await this.recordDecision(
303
+ tx,
304
+ request,
305
+ candidate,
306
+ {
307
+ decision: input.outcome,
308
+ ...(typeof data.comment === 'string' && { comment: data.comment }),
309
+ },
310
+ `external-token:${input.operatorId}`,
311
+ {
312
+ ...data,
313
+ externalEventId: input.eventId,
314
+ },
315
+ false,
316
+ );
317
+ return result.status;
318
+ }
319
+
320
+ private async recordDecision(
321
+ tx: any,
322
+ request: any,
323
+ candidate: { id: bigint; principalRef: string },
324
+ input: SubmitApprovalDecisionDto,
325
+ creator: string,
326
+ payload?: Record<string, unknown>,
327
+ createSignalOutbox = true,
328
+ ) {
329
+ const duplicate = request.decisions.find(
330
+ (item) => item.candidateId === candidate.id,
331
+ );
332
+ if (duplicate) {
333
+ if (duplicate.decision === input.decision)
334
+ return { decision: duplicate, status: request.status };
335
+ throw approvalError(
336
+ 'APPROVAL_DECISION_CONFLICT',
337
+ 'A different decision already exists for this eligibility revision',
338
+ HttpStatus.CONFLICT,
339
+ );
340
+ }
341
+ if (request.status !== 'PENDING') throw closed(request.id, request.status);
342
+
343
+ const decision = await tx.approvalDecision.create({
344
+ data: {
345
+ requestId: request.id,
346
+ candidateId: candidate.id,
347
+ decision: input.decision,
348
+ comment: input.comment,
349
+ ...(payload && {
350
+ payload: payload as Prisma.InputJsonValue,
351
+ }),
352
+ creator,
353
+ },
354
+ });
355
+ const activeIds = new Set(
356
+ request.candidates
357
+ .filter((item) => item.invalidatedAt === null)
358
+ .map((item) => item.id),
359
+ );
360
+ const status = evaluateRules({
361
+ candidateCount: activeIds.size,
362
+ approvalRule: request.approvalRule as any,
363
+ rejectionRule: request.rejectionRule as any,
364
+ decisions: [
365
+ ...request.decisions
366
+ .filter((item) => activeIds.has(item.candidateId))
367
+ .map((item) => ({
368
+ principalRef: item.candidate.principalRef,
369
+ decision: item.decision,
370
+ })),
371
+ { principalRef: candidate.principalRef, decision: input.decision },
372
+ ],
373
+ });
374
+ if (status === 'APPROVED' || status === 'REJECTED') {
375
+ const changed = await tx.approvalRequest.updateMany({
376
+ where: { id: request.id, status: 'PENDING' },
377
+ data: { status, completedAt: new Date() },
378
+ });
379
+ if (changed.count === 1 && createSignalOutbox)
380
+ await this.createSignal(tx, request, status, 'RULE_SATISFIED');
381
+ }
382
+ return {
383
+ decision,
384
+ status:
385
+ status === 'APPROVED' || status === 'REJECTED'
386
+ ? status
387
+ : ('PENDING' as const),
388
+ };
389
+ }
390
+
391
+ cancelRequest(requestId: bigint) {
392
+ return this.finishPending(requestId, 'CANCELLED', 'WORKFLOW_CANCELLED');
393
+ }
394
+
395
+ async expireRequest(requestId: bigint) {
396
+ const request = await this.getRequest(requestId);
397
+ if (request.status !== 'PENDING') return request;
398
+ const snapshot =
399
+ request.configSnapshot as unknown as ApprovalConfigSnapshotDto;
400
+ return this.finishPending(requestId, snapshot.timeout.action, 'TIMEOUT');
401
+ }
402
+
403
+ async expireDueRequests(now: Date) {
404
+ const due = await this.prisma.approvalRequest.findMany({
405
+ where: { status: 'PENDING', expiresAt: { not: null, lte: now } },
406
+ select: { id: true },
407
+ });
408
+ return Promise.all(due.map(({ id }) => this.expireRequest(id)));
409
+ }
410
+
411
+ async inbox(actor: ActorContext) {
412
+ const items = await this.prisma.approvalCandidate.findMany({
413
+ where: {
414
+ principalRef: actor.userId,
415
+ invalidatedAt: null,
416
+ request: { status: 'PENDING' },
417
+ },
418
+ select: {
419
+ id: true,
420
+ displayName: true,
421
+ eligibilityRevision: true,
422
+ request: true,
423
+ },
424
+ orderBy: { activeFrom: 'desc' },
425
+ });
426
+ return Promise.all(
427
+ items.map(async (item) => {
428
+ const channelRef = stringValue(
429
+ asObject(item.request.configSnapshot).channelRef,
430
+ );
431
+ return {
432
+ ...item,
433
+ request: {
434
+ ...item.request,
435
+ ...(channelRef && {
436
+ dispatch: await this.getDispatchStatus(
437
+ item.request.id,
438
+ channelRef,
439
+ ),
440
+ }),
441
+ },
442
+ };
443
+ }),
444
+ );
445
+ }
446
+
447
+ async status(requestId: bigint) {
448
+ const request = await this.prisma.approvalRequest.findUniqueOrThrow({
449
+ where: { id: requestId },
450
+ select: {
451
+ id: true,
452
+ status: true,
453
+ configSnapshot: true,
454
+ blockedReason: true,
455
+ startedAt: true,
456
+ expiresAt: true,
457
+ completedAt: true,
458
+ updatedAt: true,
459
+ },
460
+ });
461
+ const { configSnapshot, ...publicRequest } = request;
462
+ const channelRef = stringValue(asObject(configSnapshot).channelRef);
463
+ return {
464
+ ...publicRequest,
465
+ ...(channelRef && {
466
+ dispatch: await this.getDispatchStatus(requestId, channelRef),
467
+ }),
468
+ };
469
+ }
470
+
471
+ async retryDispatch(requestId: bigint) {
472
+ const request = await this.getRequest(requestId);
473
+ if (request.status !== 'PENDING') throw closed(request.id, request.status);
474
+
475
+ const channelRef = stringValue(asObject(request.configSnapshot).channelRef);
476
+ if (!channelRef)
477
+ throw approvalError(
478
+ 'APPROVAL_CONFIG_MISMATCH',
479
+ 'This approval request does not use an external channel',
480
+ HttpStatus.BAD_REQUEST,
481
+ );
482
+
483
+ const attempt = await this.findLatestDispatch(requestId, channelRef);
484
+ const dispatch = dispatchStatusFrom(attempt);
485
+ if (!attempt.outbox || dispatch.state !== 'DISPATCH_FAILED')
486
+ throw approvalError(
487
+ 'APPROVAL_REQUEST_CLOSED',
488
+ 'The approval request can only be resent after dispatch fails',
489
+ HttpStatus.CONFLICT,
490
+ );
491
+
492
+ await this.prisma.workflowEventOutbox.create({
493
+ data: {
494
+ eventName: `approval.requested.${channelRef}`,
495
+ payload: attempt.outbox.payload as Prisma.InputJsonValue,
496
+ },
497
+ });
498
+ return { success: true };
499
+ }
500
+
501
+ private async finishPending(
502
+ requestId: bigint,
503
+ status: SignalResult,
504
+ reason: SignalReason,
505
+ ) {
506
+ return this.prisma.$transaction(async (tx) => {
507
+ const request = await tx.approvalRequest.findUnique({
508
+ where: { id: requestId },
509
+ });
510
+ if (!request) throw notFound(requestId);
511
+ if (request.status !== 'PENDING') return request;
512
+ const changed = await tx.approvalRequest.updateMany({
513
+ where: { id: requestId, status: 'PENDING' },
514
+ data: { status, completedAt: new Date() },
515
+ });
516
+ if (changed.count === 1)
517
+ await this.createSignal(tx, request, status, reason);
518
+ return tx.approvalRequest.findUnique({ where: { id: requestId } });
519
+ });
520
+ }
521
+
522
+ private async getRequest(requestId: bigint) {
523
+ const request = await this.prisma.approvalRequest.findUnique({
524
+ where: { id: requestId },
525
+ });
526
+ if (!request) throw notFound(requestId);
527
+ return request;
528
+ }
529
+
530
+ private async getDispatchStatus(requestId: bigint, channelRef: string) {
531
+ return dispatchStatusFrom(
532
+ await this.findLatestDispatch(requestId, channelRef),
533
+ );
534
+ }
535
+
536
+ private async findLatestDispatch(requestId: bigint, channelRef: string) {
537
+ const outbox = await this.prisma.workflowEventOutbox.findFirst({
538
+ where: {
539
+ eventName: `approval.requested.${channelRef}`,
540
+ payload: {
541
+ path: ['requestId'],
542
+ equals: String(requestId),
543
+ },
544
+ },
545
+ orderBy: { createdAt: 'desc' },
546
+ });
547
+ const instance = outbox
548
+ ? await this.prisma.workflowInstance.findFirst({
549
+ where: { sourceOutboxId: outbox.id },
550
+ orderBy: { createdAt: 'desc' },
551
+ select: { status: true, result: true },
552
+ })
553
+ : null;
554
+ return { outbox, instance };
555
+ }
556
+
557
+ private async resolveSubjects(
558
+ configuredSubjects: Array<{ provider: string; refs: string[] }>,
559
+ actor: ActorContext,
560
+ ) {
561
+ const subjects: Array<{
562
+ provider: string;
563
+ ref: string;
564
+ resourceRevision?: number;
565
+ metadata?: Prisma.InputJsonValue;
566
+ }> = [];
567
+ const candidates = new Map<
568
+ string,
569
+ {
570
+ principalType: string;
571
+ principalRef: string;
572
+ displayName: string;
573
+ sourceRefs: Prisma.InputJsonValue;
574
+ }
575
+ >();
576
+ for (const configured of configuredSubjects) {
577
+ const provider = this.providers.get(configured.provider);
578
+ const preview = await provider.preview(configured.refs, actor);
579
+ const previewByRef = new Map(
580
+ preview.items.map((item) => [item.ref, item]),
581
+ );
582
+ for (const ref of configured.refs) {
583
+ const item = previewByRef.get(ref);
584
+ subjects.push({
585
+ provider: configured.provider,
586
+ ref,
587
+ ...(item?.revision !== undefined && {
588
+ resourceRevision: item.revision,
589
+ }),
590
+ ...(item?.metadata && {
591
+ metadata: item.metadata as Prisma.InputJsonValue,
592
+ }),
593
+ });
594
+ }
595
+ const principals = await provider.resolve(configured.refs, { actor });
596
+ for (const principal of principals) {
597
+ const key = candidateKey(principal);
598
+ const existing = candidates.get(key);
599
+ const sourceRefs = new Set<string>([
600
+ ...((existing?.sourceRefs as string[] | undefined) ?? []),
601
+ ...principal.sourceRefs,
602
+ ]);
603
+ candidates.set(key, {
604
+ principalType: principal.principalType,
605
+ principalRef: principal.principalRef,
606
+ displayName:
607
+ principal.displayName ??
608
+ existing?.displayName ??
609
+ principal.principalRef,
610
+ sourceRefs: [...sourceRefs],
611
+ });
612
+ }
613
+ }
614
+ return { subjects, candidates: [...candidates.values()] };
615
+ }
616
+
617
+ private createSignal(
618
+ tx: any,
619
+ request: { id: bigint; workflowInstanceId: bigint; nodeId: string },
620
+ result: SignalResult,
621
+ reason: SignalReason,
622
+ ) {
623
+ return tx.approvalSignalOutbox.upsert({
624
+ where: { idempotencyKey: `approval-request:${request.id}:${result}` },
625
+ update: {},
626
+ create: {
627
+ requestId: request.id,
628
+ idempotencyKey: `approval-request:${request.id}:${result}`,
629
+ payload: {
630
+ requestId: request.id.toString(),
631
+ nodeId: request.nodeId,
632
+ result,
633
+ reason,
634
+ decidedAt: new Date().toISOString(),
635
+ },
636
+ },
637
+ });
638
+ }
639
+ }
640
+
641
+ function candidateKey(value: { principalType: string; principalRef: string }) {
642
+ return `${value.principalType}\u0000${value.principalRef}`;
643
+ }
644
+
645
+ function toBigInt(value: string | number | bigint): bigint {
646
+ if (typeof value === 'number' && !Number.isSafeInteger(value)) {
647
+ throw approvalError(
648
+ 'APPROVAL_CONFIG_MISMATCH',
649
+ 'Numeric ID is not a safe integer',
650
+ HttpStatus.BAD_REQUEST,
651
+ );
652
+ }
653
+ try {
654
+ return BigInt(value);
655
+ } catch {
656
+ throw approvalError(
657
+ 'APPROVAL_CONFIG_MISMATCH',
658
+ 'Invalid integer ID',
659
+ HttpStatus.BAD_REQUEST,
660
+ );
661
+ }
662
+ }
663
+
664
+ function approvalError(code: any, message: string, status: HttpStatus) {
665
+ return new HumanApprovalDomainError(code, message, status);
666
+ }
667
+
668
+ function notFound(id: bigint) {
669
+ return approvalError(
670
+ 'APPROVAL_REQUEST_NOT_FOUND',
671
+ `Approval request not found: ${id}`,
672
+ HttpStatus.NOT_FOUND,
673
+ );
674
+ }
675
+
676
+ function closed(id: bigint, status: string) {
677
+ return approvalError(
678
+ 'APPROVAL_REQUEST_CLOSED',
679
+ `Approval request ${id} is ${status}`,
680
+ HttpStatus.CONFLICT,
681
+ );
682
+ }
683
+
684
+ function asObject(value: unknown): Record<string, unknown> {
685
+ return value && typeof value === 'object' && !Array.isArray(value)
686
+ ? (value as Record<string, unknown>)
687
+ : {};
688
+ }
689
+
690
+ function stringValue(value: unknown): string | null {
691
+ return typeof value === 'string' && value.trim() ? value.trim() : null;
692
+ }
693
+
694
+ function dispatchStatusFrom(attempt: {
695
+ outbox: {
696
+ processed: boolean;
697
+ lastError: string | null;
698
+ } | null;
699
+ instance: {
700
+ status: string;
701
+ result: unknown;
702
+ } | null;
703
+ }): {
704
+ state: DispatchState;
705
+ canRetry: boolean;
706
+ error?: string;
707
+ } {
708
+ if (!attempt.outbox)
709
+ return {
710
+ state: 'DISPATCH_FAILED',
711
+ canRetry: false,
712
+ error: 'Approval dispatch event was not created',
713
+ };
714
+
715
+ if (!attempt.outbox.processed)
716
+ return {
717
+ state: 'DISPATCHING',
718
+ canRetry: false,
719
+ ...(attempt.outbox.lastError && { error: attempt.outbox.lastError }),
720
+ };
721
+
722
+ if (attempt.outbox.lastError)
723
+ return {
724
+ state: 'DISPATCH_FAILED',
725
+ canRetry: true,
726
+ error: attempt.outbox.lastError,
727
+ };
728
+
729
+ if (!attempt.instance)
730
+ return {
731
+ state: 'DISPATCH_FAILED',
732
+ canRetry: true,
733
+ error: 'Approval dispatch Workflow did not start',
734
+ };
735
+
736
+ if (attempt.instance.status === 'COMPLETED')
737
+ return { state: 'WAITING_APPROVAL', canRetry: false };
738
+ if (
739
+ attempt.instance.status === 'FAILED' ||
740
+ attempt.instance.status === 'CANCELLED'
741
+ )
742
+ return {
743
+ state: 'DISPATCH_FAILED',
744
+ canRetry: true,
745
+ ...(workflowError(attempt.instance.result) && {
746
+ error: workflowError(attempt.instance.result),
747
+ }),
748
+ };
749
+ return { state: 'DISPATCHING', canRetry: false };
750
+ }
751
+
752
+ function workflowError(result: unknown): string | undefined {
753
+ const error = asObject(result).error;
754
+ return typeof error === 'string' && error.trim() ? error : undefined;
755
+ }
756
+
757
+ function mergeSubjectSources(
758
+ ...groups: Array<Array<{ provider: string; refs: string[] }>>
759
+ ) {
760
+ const refsByProvider = new Map<string, Set<string>>();
761
+ for (const group of groups) {
762
+ for (const source of group) {
763
+ const refs = refsByProvider.get(source.provider) ?? new Set<string>();
764
+ for (const ref of source.refs) {
765
+ if (typeof ref === 'string' && ref.trim()) refs.add(ref.trim());
766
+ }
767
+ refsByProvider.set(source.provider, refs);
768
+ }
769
+ }
770
+ return [...refsByProvider].map(([provider, refs]) => ({
771
+ provider,
772
+ refs: [...refs],
773
+ }));
774
+ }