devchain-cli 0.21.0 → 0.22.1

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 (379) hide show
  1. package/README.md +1 -0
  2. package/dist/drizzle/0073_integration_storage.sql +27 -0
  3. package/dist/drizzle/0074_black_drax.sql +7 -0
  4. package/dist/drizzle/0075_misty_franklin_richards.sql +37 -0
  5. package/dist/drizzle/0076_epic_time_accounting.sql +48 -0
  6. package/dist/drizzle/meta/0073_snapshot.json +6247 -0
  7. package/dist/drizzle/meta/0074_snapshot.json +6299 -0
  8. package/dist/drizzle/meta/0075_snapshot.json +6563 -0
  9. package/dist/drizzle/meta/0076_snapshot.json +6913 -0
  10. package/dist/drizzle/meta/_journal.json +28 -0
  11. package/dist/node_modules/@devchain/shared/mobile-rpc-contract/index.d.ts +27 -1
  12. package/dist/node_modules/@devchain/shared/mobile-rpc-contract/index.d.ts.map +1 -1
  13. package/dist/node_modules/@devchain/shared/mobile-rpc-contract/index.js +15 -0
  14. package/dist/node_modules/@devchain/shared/mobile-rpc-contract/index.js.map +1 -1
  15. package/dist/node_modules/@devchain/shared/schemas/export-schema.d.ts +52 -52
  16. package/dist/node_modules/@devchain/shared/tsconfig.tsbuildinfo +1 -1
  17. package/dist/server/app.main.module.js +4 -0
  18. package/dist/server/app.main.module.js.map +1 -1
  19. package/dist/server/app.normal.module.js +2 -0
  20. package/dist/server/app.normal.module.js.map +1 -1
  21. package/dist/server/common/config/container-scope.d.ts +11 -0
  22. package/dist/server/common/config/container-scope.js +37 -0
  23. package/dist/server/common/config/container-scope.js.map +1 -1
  24. package/dist/server/common/constants/terminal.d.ts +1 -0
  25. package/dist/server/common/constants/terminal.js +2 -1
  26. package/dist/server/common/constants/terminal.js.map +1 -1
  27. package/dist/server/common/filters/http-exception.filter.js +3 -4
  28. package/dist/server/common/filters/http-exception.filter.js.map +1 -1
  29. package/dist/server/common/guards/integration-admission.guard.d.ts +4 -0
  30. package/dist/server/common/guards/integration-admission.guard.js +22 -0
  31. package/dist/server/common/guards/integration-admission.guard.js.map +1 -0
  32. package/dist/server/common/test/app-bootstrap.helper.js +1 -1
  33. package/dist/server/common/test/app-bootstrap.helper.js.map +1 -1
  34. package/dist/server/modules/agent-message-delivery/agent-message-delivery.service.js +4 -0
  35. package/dist/server/modules/agent-message-delivery/agent-message-delivery.service.js.map +1 -1
  36. package/dist/server/modules/cloud/cloud.module.js +5 -1
  37. package/dist/server/modules/cloud/cloud.module.js.map +1 -1
  38. package/dist/server/modules/cloud/services/cloud-egress-bridge.service.d.ts +3 -1
  39. package/dist/server/modules/cloud/services/cloud-egress-bridge.service.js +19 -3
  40. package/dist/server/modules/cloud/services/cloud-egress-bridge.service.js.map +1 -1
  41. package/dist/server/modules/cloud/services/egress-queue.service.d.ts +2 -0
  42. package/dist/server/modules/cloud/services/egress-queue.service.js +35 -17
  43. package/dist/server/modules/cloud/services/egress-queue.service.js.map +1 -1
  44. package/dist/server/modules/cloud/services/event-mapper.service.d.ts +1 -0
  45. package/dist/server/modules/cloud/services/event-mapper.service.js.map +1 -1
  46. package/dist/server/modules/cloud/services/notification-recipient-resolver.service.d.ts +10 -0
  47. package/dist/server/modules/cloud/services/notification-recipient-resolver.service.js +55 -0
  48. package/dist/server/modules/cloud/services/notification-recipient-resolver.service.js.map +1 -0
  49. package/dist/server/modules/cloud-tunnel/services/mobile-rpc-contract.generated.d.ts +31 -5
  50. package/dist/server/modules/cloud-tunnel/services/mobile-rpc-contract.generated.js +15 -0
  51. package/dist/server/modules/cloud-tunnel/services/mobile-rpc-contract.generated.js.map +1 -1
  52. package/dist/server/modules/cloud-tunnel/services/mobile-rpc-workspace-access.service.js +5 -2
  53. package/dist/server/modules/cloud-tunnel/services/mobile-rpc-workspace-access.service.js.map +1 -1
  54. package/dist/server/modules/cloud-tunnel/services/tunnel-handler.service.d.ts +1 -0
  55. package/dist/server/modules/cloud-tunnel/services/tunnel-handler.service.js +8 -0
  56. package/dist/server/modules/cloud-tunnel/services/tunnel-handler.service.js.map +1 -1
  57. package/dist/server/modules/core/controllers/runtime.controller.d.ts +1 -0
  58. package/dist/server/modules/core/controllers/runtime.controller.js +2 -0
  59. package/dist/server/modules/core/controllers/runtime.controller.js.map +1 -1
  60. package/dist/server/modules/e2ee/services/e2ee-device-store.service.d.ts +2 -0
  61. package/dist/server/modules/e2ee/services/e2ee-device-store.service.js +26 -0
  62. package/dist/server/modules/e2ee/services/e2ee-device-store.service.js.map +1 -1
  63. package/dist/server/modules/e2ee/services/e2ee-trust.service.d.ts +5 -0
  64. package/dist/server/modules/e2ee/services/e2ee-trust.service.js +11 -0
  65. package/dist/server/modules/e2ee/services/e2ee-trust.service.js.map +1 -1
  66. package/dist/server/modules/e2ee/services/paired-device-workspace-access.service.d.ts +1 -0
  67. package/dist/server/modules/e2ee/services/paired-device-workspace-access.service.js +4 -0
  68. package/dist/server/modules/e2ee/services/paired-device-workspace-access.service.js.map +1 -1
  69. package/dist/server/modules/epic-time/controllers/epic-time.controller.d.ts +8 -0
  70. package/dist/server/modules/epic-time/controllers/epic-time.controller.js +70 -0
  71. package/dist/server/modules/epic-time/controllers/epic-time.controller.js.map +1 -0
  72. package/dist/server/modules/epic-time/epic-time.module.d.ts +2 -0
  73. package/dist/server/modules/epic-time/epic-time.module.js +28 -0
  74. package/dist/server/modules/epic-time/epic-time.module.js.map +1 -0
  75. package/dist/server/modules/epic-time/models/epic-time.models.d.ts +26 -0
  76. package/dist/server/modules/epic-time/models/epic-time.models.js +3 -0
  77. package/dist/server/modules/epic-time/models/epic-time.models.js.map +1 -0
  78. package/dist/server/modules/epic-time/services/agent-time-accounting.service.d.ts +32 -0
  79. package/dist/server/modules/epic-time/services/agent-time-accounting.service.js +159 -0
  80. package/dist/server/modules/epic-time/services/agent-time-accounting.service.js.map +1 -0
  81. package/dist/server/modules/epic-time/services/epic-time.service.d.ts +15 -0
  82. package/dist/server/modules/epic-time/services/epic-time.service.js +224 -0
  83. package/dist/server/modules/epic-time/services/epic-time.service.js.map +1 -0
  84. package/dist/server/modules/epic-time/services/epic-time.store.d.ts +75 -0
  85. package/dist/server/modules/epic-time/services/epic-time.store.js +453 -0
  86. package/dist/server/modules/epic-time/services/epic-time.store.js.map +1 -0
  87. package/dist/server/modules/epics/controllers/epics.controller.d.ts +10 -0
  88. package/dist/server/modules/epics/controllers/epics.controller.js +92 -0
  89. package/dist/server/modules/epics/controllers/epics.controller.js.map +1 -1
  90. package/dist/server/modules/epics/services/epics.service.d.ts +32 -2
  91. package/dist/server/modules/epics/services/epics.service.js +243 -96
  92. package/dist/server/modules/epics/services/epics.service.js.map +1 -1
  93. package/dist/server/modules/events/catalog/agent.created.d.ts +4 -4
  94. package/dist/server/modules/events/catalog/agent.deleted.d.ts +2 -2
  95. package/dist/server/modules/events/catalog/agent.message.sent.d.ts +40 -40
  96. package/dist/server/modules/events/catalog/broadcast-registry.js +1 -0
  97. package/dist/server/modules/events/catalog/broadcast-registry.js.map +1 -1
  98. package/dist/server/modules/events/catalog/claude.hooks.ask_user_question.pending.d.ts +4 -4
  99. package/dist/server/modules/events/catalog/claude.hooks.session.started.d.ts +10 -10
  100. package/dist/server/modules/events/catalog/epic.comment.created.d.ts +6 -6
  101. package/dist/server/modules/events/catalog/epic.created.d.ts +8 -8
  102. package/dist/server/modules/events/catalog/epic.deleted.d.ts +4 -4
  103. package/dist/server/modules/events/catalog/epic.updated.d.ts +68 -42
  104. package/dist/server/modules/events/catalog/epic.updated.js +5 -0
  105. package/dist/server/modules/events/catalog/epic.updated.js.map +1 -1
  106. package/dist/server/modules/events/catalog/guest.registered.d.ts +2 -2
  107. package/dist/server/modules/events/catalog/guest.unregistered.d.ts +4 -4
  108. package/dist/server/modules/events/catalog/index.d.ts +370 -228
  109. package/dist/server/modules/events/catalog/index.js +11 -1
  110. package/dist/server/modules/events/catalog/index.js.map +1 -1
  111. package/dist/server/modules/events/catalog/integration.connection.created.d.ts +30 -0
  112. package/dist/server/modules/events/catalog/integration.connection.created.js +17 -0
  113. package/dist/server/modules/events/catalog/integration.connection.created.js.map +1 -0
  114. package/dist/server/modules/events/catalog/integration.connection.deleted.d.ts +27 -0
  115. package/dist/server/modules/events/catalog/integration.connection.deleted.js +16 -0
  116. package/dist/server/modules/events/catalog/integration.connection.deleted.js.map +1 -0
  117. package/dist/server/modules/events/catalog/integration.connection.updated.d.ts +39 -0
  118. package/dist/server/modules/events/catalog/integration.connection.updated.js +20 -0
  119. package/dist/server/modules/events/catalog/integration.connection.updated.js.map +1 -0
  120. package/dist/server/modules/events/catalog/review.comment.created.d.ts +16 -16
  121. package/dist/server/modules/events/catalog/review.comment.deleted.d.ts +2 -2
  122. package/dist/server/modules/events/catalog/review.comment.resolved.d.ts +2 -2
  123. package/dist/server/modules/events/catalog/review.comment.updated.d.ts +4 -4
  124. package/dist/server/modules/events/catalog/review.created.d.ts +12 -12
  125. package/dist/server/modules/events/catalog/review.updated.d.ts +22 -22
  126. package/dist/server/modules/events/catalog/scheduled-epic.executed.d.ts +12 -12
  127. package/dist/server/modules/events/catalog/session.human-prompt-state-changed.d.ts +77 -0
  128. package/dist/server/modules/events/catalog/session.human-prompt-state-changed.js +25 -0
  129. package/dist/server/modules/events/catalog/session.human-prompt-state-changed.js.map +1 -0
  130. package/dist/server/modules/events/catalog/session.provider-session-id.discovered.d.ts +2 -2
  131. package/dist/server/modules/events/catalog/session.recommendation.d.ts +4 -4
  132. package/dist/server/modules/events/catalog/session.restored.d.ts +2 -2
  133. package/dist/server/modules/events/catalog/session.started.d.ts +2 -2
  134. package/dist/server/modules/events/catalog/session.starting.d.ts +2 -2
  135. package/dist/server/modules/events/catalog/session.stopped.d.ts +2 -2
  136. package/dist/server/modules/events/catalog/session.transcript.discovered.d.ts +4 -4
  137. package/dist/server/modules/events/catalog/team.config.updated.d.ts +6 -6
  138. package/dist/server/modules/events/catalog/team.member.added.d.ts +2 -2
  139. package/dist/server/modules/events/catalog/team.member.removed.d.ts +2 -2
  140. package/dist/server/modules/events/catalog/terminal.watcher.triggered.d.ts +4 -4
  141. package/dist/server/modules/events/events-core.module.js +8 -1
  142. package/dist/server/modules/events/events-core.module.js.map +1 -1
  143. package/dist/server/modules/events/events-infra.module.js +6 -1
  144. package/dist/server/modules/events/events-infra.module.js.map +1 -1
  145. package/dist/server/modules/events/services/committed-event.store.d.ts +23 -0
  146. package/dist/server/modules/events/services/committed-event.store.js +169 -0
  147. package/dist/server/modules/events/services/committed-event.store.js.map +1 -0
  148. package/dist/server/modules/events/services/durable-event-dispatcher.service.d.ts +16 -0
  149. package/dist/server/modules/events/services/durable-event-dispatcher.service.js +97 -0
  150. package/dist/server/modules/events/services/durable-event-dispatcher.service.js.map +1 -0
  151. package/dist/server/modules/events/services/durable-event-registry.service.d.ts +25 -0
  152. package/dist/server/modules/events/services/durable-event-registry.service.js +59 -0
  153. package/dist/server/modules/events/services/durable-event-registry.service.js.map +1 -0
  154. package/dist/server/modules/events/services/event-log.service.d.ts +6 -1
  155. package/dist/server/modules/events/services/event-log.service.js +32 -18
  156. package/dist/server/modules/events/services/event-log.service.js.map +1 -1
  157. package/dist/server/modules/events/services/events.service.d.ts +11 -1
  158. package/dist/server/modules/events/services/events.service.js +50 -7
  159. package/dist/server/modules/events/services/events.service.js.map +1 -1
  160. package/dist/server/modules/external-integrations/adapters/clickup-external-task.provider.d.ts +97 -0
  161. package/dist/server/modules/external-integrations/adapters/clickup-external-task.provider.js +1404 -0
  162. package/dist/server/modules/external-integrations/adapters/clickup-external-task.provider.js.map +1 -0
  163. package/dist/server/modules/external-integrations/adapters/jira-external-task.provider.d.ts +113 -0
  164. package/dist/server/modules/external-integrations/adapters/jira-external-task.provider.js +1790 -0
  165. package/dist/server/modules/external-integrations/adapters/jira-external-task.provider.js.map +1 -0
  166. package/dist/server/modules/external-integrations/adapters/rich/clickup-comment-converter.d.ts +10 -0
  167. package/dist/server/modules/external-integrations/adapters/rich/clickup-comment-converter.js +97 -0
  168. package/dist/server/modules/external-integrations/adapters/rich/clickup-comment-converter.js.map +1 -0
  169. package/dist/server/modules/external-integrations/adapters/rich/clickup-markdown-converter.d.ts +16 -0
  170. package/dist/server/modules/external-integrations/adapters/rich/clickup-markdown-converter.js +386 -0
  171. package/dist/server/modules/external-integrations/adapters/rich/clickup-markdown-converter.js.map +1 -0
  172. package/dist/server/modules/external-integrations/adapters/rich/jira-adf-converter.d.ts +5 -0
  173. package/dist/server/modules/external-integrations/adapters/rich/jira-adf-converter.js +229 -0
  174. package/dist/server/modules/external-integrations/adapters/rich/jira-adf-converter.js.map +1 -0
  175. package/dist/server/modules/external-integrations/adapters/vendor-shared.d.ts +42 -0
  176. package/dist/server/modules/external-integrations/adapters/vendor-shared.js +182 -0
  177. package/dist/server/modules/external-integrations/adapters/vendor-shared.js.map +1 -0
  178. package/dist/server/modules/external-integrations/connections/integration-connections.controller.d.ts +11 -0
  179. package/dist/server/modules/external-integrations/connections/integration-connections.controller.js +121 -0
  180. package/dist/server/modules/external-integrations/connections/integration-connections.controller.js.map +1 -0
  181. package/dist/server/modules/external-integrations/connections/integration-connections.service.d.ts +46 -0
  182. package/dist/server/modules/external-integrations/connections/integration-connections.service.js +193 -0
  183. package/dist/server/modules/external-integrations/connections/integration-connections.service.js.map +1 -0
  184. package/dist/server/modules/external-integrations/connections/managed-subtask-sync.controller.d.ts +8 -0
  185. package/dist/server/modules/external-integrations/connections/managed-subtask-sync.controller.js +73 -0
  186. package/dist/server/modules/external-integrations/connections/managed-subtask-sync.controller.js.map +1 -0
  187. package/dist/server/modules/external-integrations/errors/external-provider.errors.d.ts +23 -0
  188. package/dist/server/modules/external-integrations/errors/external-provider.errors.js +105 -0
  189. package/dist/server/modules/external-integrations/errors/external-provider.errors.js.map +1 -0
  190. package/dist/server/modules/external-integrations/external-integrations.module.d.ts +2 -0
  191. package/dist/server/modules/external-integrations/external-integrations.module.js +72 -0
  192. package/dist/server/modules/external-integrations/external-integrations.module.js.map +1 -0
  193. package/dist/server/modules/external-integrations/external-task-provider.registry.d.ts +12 -0
  194. package/dist/server/modules/external-integrations/external-task-provider.registry.js +65 -0
  195. package/dist/server/modules/external-integrations/external-task-provider.registry.js.map +1 -0
  196. package/dist/server/modules/external-integrations/models/external-comment-lookup-token.d.ts +12 -0
  197. package/dist/server/modules/external-integrations/models/external-comment-lookup-token.js +49 -0
  198. package/dist/server/modules/external-integrations/models/external-comment-lookup-token.js.map +1 -0
  199. package/dist/server/modules/external-integrations/models/external-edit-session.models.d.ts +94 -0
  200. package/dist/server/modules/external-integrations/models/external-edit-session.models.js +10 -0
  201. package/dist/server/modules/external-integrations/models/external-edit-session.models.js.map +1 -0
  202. package/dist/server/modules/external-integrations/models/external-provider.models.d.ts +341 -0
  203. package/dist/server/modules/external-integrations/models/external-provider.models.js +21 -0
  204. package/dist/server/modules/external-integrations/models/external-provider.models.js.map +1 -0
  205. package/dist/server/modules/external-integrations/models/external-rich-capabilities.d.ts +6 -0
  206. package/dist/server/modules/external-integrations/models/external-rich-capabilities.js +9 -0
  207. package/dist/server/modules/external-integrations/models/external-rich-capabilities.js.map +1 -0
  208. package/dist/server/modules/external-integrations/models/external-rich-document.d.ts +59 -0
  209. package/dist/server/modules/external-integrations/models/external-rich-document.js +233 -0
  210. package/dist/server/modules/external-integrations/models/external-rich-document.js.map +1 -0
  211. package/dist/server/modules/external-integrations/models/external-task-source.d.ts +4 -0
  212. package/dist/server/modules/external-integrations/models/external-task-source.js +71 -0
  213. package/dist/server/modules/external-integrations/models/external-task-source.js.map +1 -0
  214. package/dist/server/modules/external-integrations/models/external-time-mutation.models.d.ts +74 -0
  215. package/dist/server/modules/external-integrations/models/external-time-mutation.models.js +19 -0
  216. package/dist/server/modules/external-integrations/models/external-time-mutation.models.js.map +1 -0
  217. package/dist/server/modules/external-integrations/my-work/external-edit-session.service.d.ts +57 -0
  218. package/dist/server/modules/external-integrations/my-work/external-edit-session.service.js +776 -0
  219. package/dist/server/modules/external-integrations/my-work/external-edit-session.service.js.map +1 -0
  220. package/dist/server/modules/external-integrations/my-work/external-my-work.controller.d.ts +37 -0
  221. package/dist/server/modules/external-integrations/my-work/external-my-work.controller.js +626 -0
  222. package/dist/server/modules/external-integrations/my-work/external-my-work.controller.js.map +1 -0
  223. package/dist/server/modules/external-integrations/my-work/external-my-work.openapi.d.ts +29 -0
  224. package/dist/server/modules/external-integrations/my-work/external-my-work.openapi.js +847 -0
  225. package/dist/server/modules/external-integrations/my-work/external-my-work.openapi.js.map +1 -0
  226. package/dist/server/modules/external-integrations/my-work/external-my-work.service.d.ts +34 -0
  227. package/dist/server/modules/external-integrations/my-work/external-my-work.service.js +330 -0
  228. package/dist/server/modules/external-integrations/my-work/external-my-work.service.js.map +1 -0
  229. package/dist/server/modules/external-integrations/my-work/external-time-mutation.service.d.ts +27 -0
  230. package/dist/server/modules/external-integrations/my-work/external-time-mutation.service.js +412 -0
  231. package/dist/server/modules/external-integrations/my-work/external-time-mutation.service.js.map +1 -0
  232. package/dist/server/modules/external-integrations/ports/external-task-provider.d.ts +13 -0
  233. package/dist/server/modules/external-integrations/ports/external-task-provider.js +5 -0
  234. package/dist/server/modules/external-integrations/ports/external-task-provider.js.map +1 -0
  235. package/dist/server/modules/external-integrations/request-validation.d.ts +2 -0
  236. package/dist/server/modules/external-integrations/request-validation.js +14 -0
  237. package/dist/server/modules/external-integrations/request-validation.js.map +1 -0
  238. package/dist/server/modules/external-integrations/sessions/external-edit-session.store.d.ts +59 -0
  239. package/dist/server/modules/external-integrations/sessions/external-edit-session.store.js +329 -0
  240. package/dist/server/modules/external-integrations/sessions/external-edit-session.store.js.map +1 -0
  241. package/dist/server/modules/external-integrations/sessions/external-time-mutation.store.d.ts +44 -0
  242. package/dist/server/modules/external-integrations/sessions/external-time-mutation.store.js +171 -0
  243. package/dist/server/modules/external-integrations/sessions/external-time-mutation.store.js.map +1 -0
  244. package/dist/server/modules/external-integrations/sessions/provider-operation-gate.d.ts +7 -0
  245. package/dist/server/modules/external-integrations/sessions/provider-operation-gate.js +33 -0
  246. package/dist/server/modules/external-integrations/sessions/provider-operation-gate.js.map +1 -0
  247. package/dist/server/modules/external-integrations/subscribers/external-subtask-sync.subscriber.d.ts +69 -0
  248. package/dist/server/modules/external-integrations/subscribers/external-subtask-sync.subscriber.js +1092 -0
  249. package/dist/server/modules/external-integrations/subscribers/external-subtask-sync.subscriber.js.map +1 -0
  250. package/dist/server/modules/external-integrations/subscribers/managed-subtask-content.d.ts +8 -0
  251. package/dist/server/modules/external-integrations/subscribers/managed-subtask-content.js +23 -0
  252. package/dist/server/modules/external-integrations/subscribers/managed-subtask-content.js.map +1 -0
  253. package/dist/server/modules/external-integrations/subscribers/managed-subtask-recovery-policy.d.ts +7 -0
  254. package/dist/server/modules/external-integrations/subscribers/managed-subtask-recovery-policy.js +30 -0
  255. package/dist/server/modules/external-integrations/subscribers/managed-subtask-recovery-policy.js.map +1 -0
  256. package/dist/server/modules/external-integrations/subscribers/managed-subtask-sync-health.service.d.ts +44 -0
  257. package/dist/server/modules/external-integrations/subscribers/managed-subtask-sync-health.service.js +135 -0
  258. package/dist/server/modules/external-integrations/subscribers/managed-subtask-sync-health.service.js.map +1 -0
  259. package/dist/server/modules/external-integrations/transport/safe-vendor-http-client.d.ts +47 -0
  260. package/dist/server/modules/external-integrations/transport/safe-vendor-http-client.js +294 -0
  261. package/dist/server/modules/external-integrations/transport/safe-vendor-http-client.js.map +1 -0
  262. package/dist/server/modules/hooks/dtos/hook-event.dto.d.ts +64 -64
  263. package/dist/server/modules/mcp/dtos/mcp.dto.d.ts +36 -36
  264. package/dist/server/modules/orchestrator/docker/services/seed-preparation.service.d.ts +5 -0
  265. package/dist/server/modules/orchestrator/docker/services/seed-preparation.service.js +51 -1
  266. package/dist/server/modules/orchestrator/docker/services/seed-preparation.service.js.map +1 -1
  267. package/dist/server/modules/orchestrator/sync/services/task-merge.service.d.ts +0 -2
  268. package/dist/server/modules/orchestrator/sync/services/task-merge.service.js +9 -48
  269. package/dist/server/modules/orchestrator/sync/services/task-merge.service.js.map +1 -1
  270. package/dist/server/modules/profiles/dto.d.ts +11 -11
  271. package/dist/server/modules/projects/controllers/projects.controller.d.ts +2 -2
  272. package/dist/server/modules/projects/dtos/export.dto.d.ts +6 -6
  273. package/dist/server/modules/projects/helpers/project-export.d.ts +1 -1
  274. package/dist/server/modules/projects/services/projects.service.d.ts +1 -1
  275. package/dist/server/modules/projects/template-codec/codecs/watchers.codec.d.ts +1 -1
  276. package/dist/server/modules/registry/controllers/templates.controller.d.ts +7 -7
  277. package/dist/server/modules/reviews/dtos/review.dto.d.ts +10 -10
  278. package/dist/server/modules/scheduled-epics/dtos/scheduled-epic.dto.d.ts +1 -1
  279. package/dist/server/modules/session-reader/adapters/antigravity-session-reader.adapter.d.ts +1 -1
  280. package/dist/server/modules/session-reader/adapters/opencode-session-reader.adapter.d.ts +1 -1
  281. package/dist/server/modules/session-reader/data/pricing.json +128 -39
  282. package/dist/server/modules/session-terminal-runtime/session-terminal-runtime.service.d.ts +2 -0
  283. package/dist/server/modules/session-terminal-runtime/session-terminal-runtime.service.js +18 -12
  284. package/dist/server/modules/session-terminal-runtime/session-terminal-runtime.service.js.map +1 -1
  285. package/dist/server/modules/sessions/controllers/sessions.controller.d.ts +3 -0
  286. package/dist/server/modules/sessions/controllers/sessions.controller.js +30 -0
  287. package/dist/server/modules/sessions/controllers/sessions.controller.js.map +1 -1
  288. package/dist/server/modules/sessions/dtos/sessions.dto.d.ts +2 -2
  289. package/dist/server/modules/sessions/services/message-enqueue.service.d.ts +2 -0
  290. package/dist/server/modules/sessions/services/message-enqueue.service.js +2 -0
  291. package/dist/server/modules/sessions/services/message-enqueue.service.js.map +1 -1
  292. package/dist/server/modules/sessions/services/message-pool.types.d.ts +8 -1
  293. package/dist/server/modules/sessions/services/sessions-message-pool.service.d.ts +50 -6
  294. package/dist/server/modules/sessions/services/sessions-message-pool.service.js +828 -190
  295. package/dist/server/modules/sessions/services/sessions-message-pool.service.js.map +1 -1
  296. package/dist/server/modules/settings/dtos/settings.dto.d.ts +8 -0
  297. package/dist/server/modules/settings/dtos/settings.dto.js +1 -0
  298. package/dist/server/modules/settings/dtos/settings.dto.js.map +1 -1
  299. package/dist/server/modules/settings/local/delegates/core-settings.delegate.js +12 -0
  300. package/dist/server/modules/settings/local/delegates/core-settings.delegate.js.map +1 -1
  301. package/dist/server/modules/settings/services/settings.constants.d.ts +1 -1
  302. package/dist/server/modules/settings/services/settings.constants.js +2 -1
  303. package/dist/server/modules/settings/services/settings.constants.js.map +1 -1
  304. package/dist/server/modules/settings/services/settings.service.d.ts +1 -1
  305. package/dist/server/modules/settings/services/settings.service.js +2 -1
  306. package/dist/server/modules/settings/services/settings.service.js.map +1 -1
  307. package/dist/server/modules/skills/dtos/skill.dto.d.ts +2 -2
  308. package/dist/server/modules/storage/db/schema.d.ts +2420 -958
  309. package/dist/server/modules/storage/db/schema.js +144 -2
  310. package/dist/server/modules/storage/db/schema.js.map +1 -1
  311. package/dist/server/modules/storage/interfaces/storage.interface.d.ts +34 -6
  312. package/dist/server/modules/storage/interfaces/storage.interface.js.map +1 -1
  313. package/dist/server/modules/storage/local/delegates/epic.delegate.d.ts +9 -5
  314. package/dist/server/modules/storage/local/delegates/epic.delegate.js +67 -30
  315. package/dist/server/modules/storage/local/delegates/epic.delegate.js.map +1 -1
  316. package/dist/server/modules/storage/local/delegates/external-managed-subtask.delegate.d.ts +18 -0
  317. package/dist/server/modules/storage/local/delegates/external-managed-subtask.delegate.js +320 -0
  318. package/dist/server/modules/storage/local/delegates/external-managed-subtask.delegate.js.map +1 -0
  319. package/dist/server/modules/storage/local/delegates/integration.delegate.d.ts +37 -0
  320. package/dist/server/modules/storage/local/delegates/integration.delegate.js +308 -0
  321. package/dist/server/modules/storage/local/delegates/integration.delegate.js.map +1 -0
  322. package/dist/server/modules/storage/local/integration-credential-cipher.d.ts +18 -0
  323. package/dist/server/modules/storage/local/integration-credential-cipher.js +138 -0
  324. package/dist/server/modules/storage/local/integration-credential-cipher.js.map +1 -0
  325. package/dist/server/modules/storage/local/local-storage.service.d.ts +36 -7
  326. package/dist/server/modules/storage/local/local-storage.service.js +93 -10
  327. package/dist/server/modules/storage/local/local-storage.service.js.map +1 -1
  328. package/dist/server/modules/storage/models/domain.models.d.ts +127 -0
  329. package/dist/server/modules/storage/models/domain.models.js +15 -0
  330. package/dist/server/modules/storage/models/domain.models.js.map +1 -1
  331. package/dist/server/modules/storage/storage.module.js +2 -1
  332. package/dist/server/modules/storage/storage.module.js.map +1 -1
  333. package/dist/server/modules/subscribers/dtos/subscriber.dto.d.ts +1 -1
  334. package/dist/server/modules/terminal/dtos/ws-envelope.dto.d.ts +2 -2
  335. package/dist/server/modules/terminal/gateways/terminal.gateway.d.ts +9 -1
  336. package/dist/server/modules/terminal/gateways/terminal.gateway.js +79 -3
  337. package/dist/server/modules/terminal/gateways/terminal.gateway.js.map +1 -1
  338. package/dist/server/modules/terminal/services/human-prompt-state.service.d.ts +58 -0
  339. package/dist/server/modules/terminal/services/human-prompt-state.service.js +206 -0
  340. package/dist/server/modules/terminal/services/human-prompt-state.service.js.map +1 -0
  341. package/dist/server/modules/terminal/services/terminal-activity.service.js +7 -18
  342. package/dist/server/modules/terminal/services/terminal-activity.service.js.map +1 -1
  343. package/dist/server/modules/terminal/services/terminal-io/delivery.d.ts +2 -1
  344. package/dist/server/modules/terminal/services/terminal-io/delivery.js +77 -33
  345. package/dist/server/modules/terminal/services/terminal-io/delivery.js.map +1 -1
  346. package/dist/server/modules/terminal/services/terminal-io/terminal-io.service.d.ts +14 -5
  347. package/dist/server/modules/terminal/services/terminal-io/terminal-io.service.js +64 -8
  348. package/dist/server/modules/terminal/services/terminal-io/terminal-io.service.js.map +1 -1
  349. package/dist/server/modules/terminal/services/terminal-io/types.d.ts +8 -0
  350. package/dist/server/modules/terminal/services/terminal-key-input/terminal-key-input.facade.d.ts +5 -1
  351. package/dist/server/modules/terminal/services/terminal-key-input/terminal-key-input.facade.js +39 -2
  352. package/dist/server/modules/terminal/services/terminal-key-input/terminal-key-input.facade.js.map +1 -1
  353. package/dist/server/modules/terminal/services/terminal-session/terminal-session-registry.d.ts +3 -1
  354. package/dist/server/modules/terminal/services/terminal-session/terminal-session-registry.js +7 -2
  355. package/dist/server/modules/terminal/services/terminal-session/terminal-session-registry.js.map +1 -1
  356. package/dist/server/modules/terminal/services/terminal-session/terminal-session.d.ts +4 -0
  357. package/dist/server/modules/terminal/services/terminal-session/terminal-session.js +8 -0
  358. package/dist/server/modules/terminal/services/terminal-session/terminal-session.js.map +1 -1
  359. package/dist/server/modules/terminal/terminal-delivery.module.js +13 -2
  360. package/dist/server/modules/terminal/terminal-delivery.module.js.map +1 -1
  361. package/dist/server/modules/terminal/terminal.module.js +4 -3
  362. package/dist/server/modules/terminal/terminal.module.js.map +1 -1
  363. package/dist/server/modules/terminal/utils/terminal-activity.d.ts +4 -0
  364. package/dist/server/modules/terminal/utils/terminal-activity.js +86 -0
  365. package/dist/server/modules/terminal/utils/terminal-activity.js.map +1 -0
  366. package/dist/server/modules/watchers/dtos/watcher.dto.d.ts +8 -8
  367. package/dist/server/templates/teams-dev.json +9 -9
  368. package/dist/server/tsconfig.tsbuildinfo +1 -1
  369. package/dist/server/ui/assets/ExternalRichEditor-DMtnZIMZ.js +113 -0
  370. package/dist/server/ui/assets/{ReviewDetailPage-CNffXKv8.js → ReviewDetailPage-BmyiM5IR.js} +1 -1
  371. package/dist/server/ui/assets/{ReviewsPage-DAcufZ3r.js → ReviewsPage-CQ87BMwT.js} +1 -1
  372. package/dist/server/ui/assets/index-BX6Yze-A.js +1177 -0
  373. package/dist/server/ui/assets/index-qoQEv_u4.css +32 -0
  374. package/dist/server/ui/assets/{useReviewSubscription-DD29I7uU.js → useReviewSubscription-DjbEUzt4.js} +1 -1
  375. package/dist/server/ui/index.html +2 -2
  376. package/dist/templates/teams-dev.json +9 -9
  377. package/package.json +15 -4
  378. package/dist/server/ui/assets/index-7iCs8Dxj.js +0 -1153
  379. package/dist/server/ui/assets/index-CLXZjL_B.css +0 -32
@@ -0,0 +1,1790 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.JiraExternalTaskProvider = void 0;
13
+ const common_1 = require("@nestjs/common");
14
+ const external_provider_errors_1 = require("../errors/external-provider.errors");
15
+ const external_provider_models_1 = require("../models/external-provider.models");
16
+ const jira_adf_converter_1 = require("./rich/jira-adf-converter");
17
+ const jira_adf_converter_2 = require("./rich/jira-adf-converter");
18
+ const safe_vendor_http_client_1 = require("../transport/safe-vendor-http-client");
19
+ const vendor_shared_1 = require("./vendor-shared");
20
+ const JIRA_TENANT_HOSTNAME = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.atlassian\.net$/;
21
+ const JIRA_SITE_URL = /^https:\/\/[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.atlassian\.net\/?$/i;
22
+ const JIRA_SEARCH_PAGE_SIZE = 100;
23
+ const JIRA_MAX_SEARCH_PAGES = 1_000;
24
+ const JIRA_COMMENT_PAGE_SIZE = 10;
25
+ const JIRA_COMMENT_MAX_START_AT = 1_000_000;
26
+ const JIRA_COMMENT_CURSOR_PATTERN = /^\d+$/;
27
+ const JIRA_COMMENT_AUTHOR = {
28
+ nameKey: 'displayName',
29
+ idKey: 'accountId',
30
+ idFormat: 'string',
31
+ };
32
+ const JIRA_WORKLOG_PAGE_SIZE = 100;
33
+ const JIRA_WORKLOG_MAX_PAGES = 5;
34
+ const JIRA_WORKLOG_MAX_RAW = 500;
35
+ const JIRA_MANAGED_SUBTASK_PROPERTY = 'devchain.managed-subtask';
36
+ const OTHER_ASSIGNED_WORK_AREA_ID = 'other-assigned';
37
+ const JIRA_NEUTRAL_COLOR = '#6b778c';
38
+ const JIRA_COMPLETED_COLOR = '#36b37e';
39
+ const JIRA_DETAIL_FIELDS = [
40
+ 'summary',
41
+ 'description',
42
+ 'status',
43
+ 'duedate',
44
+ 'priority',
45
+ 'project',
46
+ 'subtasks',
47
+ 'timetracking',
48
+ ];
49
+ const JIRA_SEARCH_FIELDS = [
50
+ 'summary',
51
+ 'status',
52
+ 'assignee',
53
+ 'updated',
54
+ 'duedate',
55
+ 'resolutiondate',
56
+ 'project',
57
+ 'parent',
58
+ 'issuetype',
59
+ ];
60
+ let JiraExternalTaskProvider = class JiraExternalTaskProvider {
61
+ http;
62
+ provider = 'jira';
63
+ descriptor = {
64
+ provider: this.provider,
65
+ displayName: 'Jira',
66
+ capabilities: { myWork: true },
67
+ };
68
+ myWork = {
69
+ discover: (credentials, options) => this.discoverMyWork(credentials, options),
70
+ getTaskDetail: (credentials, context, remoteTaskId) => this.getTaskDetail(credentials, context, remoteTaskId),
71
+ listComments: (credentials, context, remoteTaskId, cursor) => this.listTaskComments(credentials, context, remoteTaskId, cursor),
72
+ changeStatus: (credentials, context, remoteTaskId, input) => this.changeTaskStatus(credentials, context, remoteTaskId, input),
73
+ addComment: (credentials, context, remoteTaskId, input) => this.addTaskComment(credentials, context, remoteTaskId, input),
74
+ getTimeEntryHistory: (credentials, context, remoteTaskId) => this.getTaskTimeEntryHistory(credentials, context, remoteTaskId),
75
+ };
76
+ timeEntryMutations = {
77
+ createTimeEntry: (credentials, context, remoteTaskId, input) => this.createTimeEntry(credentials, context, remoteTaskId, input),
78
+ deleteTimeEntry: (credentials, context, remoteTaskId, remoteEntryId) => this.deleteTimeEntry(credentials, context, remoteTaskId, remoteEntryId),
79
+ readTimeEntryExact: (credentials, context, remoteTaskId, remoteEntryId) => this.readTimeEntryExact(credentials, context, remoteTaskId, remoteEntryId),
80
+ listOwnTimeEntryIdsInRange: (credentials, context, remoteTaskId, startedAfterMs, startedBeforeMs) => this.listOwnTimeEntryIdsInRange(credentials, context, remoteTaskId, startedAfterMs, startedBeforeMs),
81
+ assertTimeEntryDeletable: (credentials, context, remoteTaskId, remoteEntryId) => this.assertTimeEntryDeletable(credentials, context, remoteTaskId, remoteEntryId),
82
+ };
83
+ boardMetadataCache = new vendor_shared_1.VendorMetadataCache(vendor_shared_1.WORK_AREA_METADATA_CACHE_MAX_ENTRIES);
84
+ subtaskIssueTypeCache = new vendor_shared_1.VendorMetadataCache(vendor_shared_1.WORK_AREA_METADATA_CACHE_MAX_ENTRIES);
85
+ validate = (0, vendor_shared_1.createVendorValidators)((reason) => new external_provider_errors_1.JiraProviderError(reason));
86
+ constructor(http) {
87
+ this.http = http;
88
+ }
89
+ ownedMutations = {
90
+ getCurrentOwnerRemoteId: (credentials) => this.loadIdentity(this.resolveSite(credentials)).then((identity) => identity.accountId),
91
+ findComment: (credentials, context, remoteTaskId, commentId) => this.findComment(credentials, context, remoteTaskId, commentId),
92
+ deleteComment: (credentials, context, remoteTaskId, commentId) => this.deleteComment(credentials, context, remoteTaskId, commentId),
93
+ updateOwnedComment: (credentials, context, remoteTaskId, commentId, document) => this.updateOwnedComment(credentials, context, remoteTaskId, commentId, document),
94
+ };
95
+ descriptionEdit = {
96
+ readDescription: (credentials, context, remoteTaskId) => this.readDescription(credentials, context, remoteTaskId),
97
+ writeDescription: (credentials, context, remoteTaskId, raw) => this.writeDescription(credentials, context, remoteTaskId, raw),
98
+ };
99
+ subtaskSync = {
100
+ create: (credentials, context, input) => this.createSubtask(credentials, context, input),
101
+ readExact: (credentials, context, remoteTaskId) => this.readSubtaskExact(credentials, context, remoteTaskId),
102
+ update: (credentials, context, input) => this.updateSubtask(credentials, context, input),
103
+ delete: (credentials, context, proof) => this.deleteSubtask(credentials, context, proof),
104
+ listOwnedDirectChildren: (credentials, context, parentRemoteTaskId, ownershipToken) => this.listOwnedDirectChildren(credentials, context, parentRemoteTaskId, ownershipToken),
105
+ assertOwned: (credentials, context, proof) => this.assertOwnedSubtask(credentials, context, proof),
106
+ };
107
+ async createSubtask(credentials, context, input) {
108
+ const site = this.resolveSite(credentials);
109
+ const parentTaskId = this.validate.requiredTaskId(input.parentRemoteTaskId);
110
+ const ownershipToken = this.requireSubtaskOwnershipToken(input.ownershipToken);
111
+ const title = this.requireSubtaskTitle(input.title);
112
+ const description = this.requireSubtaskDescription(input.description);
113
+ const parent = await this.loadSubtaskParent(site, parentTaskId);
114
+ const issueTypeId = await this.loadSubtaskIssueType(site, context, parent.workAreaRemoteId);
115
+ const identity = await this.loadIdentity(site);
116
+ const propertyValue = { version: 1, ownershipToken };
117
+ const payload = await this.requestJson(site, '/rest/api/3/issue', {
118
+ method: 'POST',
119
+ body: JSON.stringify({
120
+ fields: {
121
+ project: { id: parent.workAreaRemoteId },
122
+ issuetype: { id: issueTypeId },
123
+ parent: { key: parent.remoteKey },
124
+ summary: title,
125
+ description: this.managedDescriptionAdf(description),
126
+ assignee: { id: identity.accountId },
127
+ },
128
+ properties: [{ key: JIRA_MANAGED_SUBTASK_PROPERTY, value: propertyValue }],
129
+ }),
130
+ });
131
+ try {
132
+ if (!(0, vendor_shared_1.isRecord)(payload)) {
133
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
134
+ }
135
+ this.validate.requiredIdentifier(payload.id);
136
+ const remoteKey = this.validate.requiredString(payload.key);
137
+ return {
138
+ remoteTaskId: remoteKey,
139
+ remoteKey,
140
+ parentRemoteTaskId: parent.remoteKey,
141
+ workAreaRemoteId: parent.workAreaRemoteId,
142
+ ownershipToken,
143
+ title,
144
+ description,
145
+ };
146
+ }
147
+ catch (error) {
148
+ if (error instanceof external_provider_errors_1.JiraProviderError) {
149
+ throw new external_provider_errors_1.JiraProviderError('invalid_response', undefined, { dispatched: true });
150
+ }
151
+ throw error;
152
+ }
153
+ }
154
+ async loadSubtaskParent(site, parentTaskId) {
155
+ const url = new URL(`${site.origin}/rest/api/3/issue/${encodeURIComponent(parentTaskId)}`);
156
+ url.searchParams.set('fields', 'project');
157
+ const payload = await this.requestJson(site, `${url.pathname}${url.search}`);
158
+ if (!(0, vendor_shared_1.isRecord)(payload) || !(0, vendor_shared_1.isRecord)(payload.fields) || !(0, vendor_shared_1.isRecord)(payload.fields.project)) {
159
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
160
+ }
161
+ const remoteTaskId = this.validate.requiredIdentifier(payload.id);
162
+ const remoteKey = this.validate.requiredString(payload.key);
163
+ if (remoteTaskId !== parentTaskId && remoteKey !== parentTaskId) {
164
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
165
+ }
166
+ return {
167
+ remoteTaskId,
168
+ remoteKey,
169
+ workAreaRemoteId: this.validate.requiredIdentifier(payload.fields.project.id),
170
+ };
171
+ }
172
+ async loadSubtaskIssueType(site, context, projectId) {
173
+ const cacheKey = JSON.stringify([
174
+ context.connectionId,
175
+ context.connectionGeneration,
176
+ site.hostname,
177
+ projectId,
178
+ ]);
179
+ const cached = this.subtaskIssueTypeCache.get(cacheKey);
180
+ if (cached && Date.now() - cached.fetchedAt <= vendor_shared_1.WORK_AREA_METADATA_TTL_MS) {
181
+ return cached.value;
182
+ }
183
+ const url = new URL(`${site.origin}/rest/api/3/issuetype/project`);
184
+ url.searchParams.set('projectId', projectId);
185
+ url.searchParams.set('level', '-1');
186
+ const payload = await this.requestJson(site, `${url.pathname}${url.search}`);
187
+ if (!Array.isArray(payload)) {
188
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
189
+ }
190
+ let issueTypeId = null;
191
+ for (const value of payload) {
192
+ if (!(0, vendor_shared_1.isRecord)(value)) {
193
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
194
+ }
195
+ if (value.subtask === true && value.hierarchyLevel === -1 && issueTypeId === null) {
196
+ issueTypeId = this.validate.requiredIdentifier(value.id);
197
+ }
198
+ }
199
+ if (issueTypeId === null) {
200
+ throw new external_provider_errors_1.JiraProviderError('unsupported_subtask_type');
201
+ }
202
+ this.subtaskIssueTypeCache.set(cacheKey, { value: issueTypeId, fetchedAt: Date.now() });
203
+ return issueTypeId;
204
+ }
205
+ async readSubtaskExact(credentials, _context, remoteTaskId) {
206
+ const site = this.resolveSite(credentials);
207
+ const taskId = this.validate.requiredTaskId(remoteTaskId);
208
+ const url = new URL(`${site.origin}/rest/api/3/issue/${encodeURIComponent(taskId)}`);
209
+ url.searchParams.set('fields', 'summary,description,parent,project');
210
+ url.searchParams.set('properties', JIRA_MANAGED_SUBTASK_PROPERTY);
211
+ try {
212
+ const payload = await this.requestJson(site, `${url.pathname}${url.search}`);
213
+ const snapshot = this.normalizeManagedSubtask(payload);
214
+ if (snapshot.remoteKey !== taskId) {
215
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
216
+ }
217
+ return snapshot;
218
+ }
219
+ catch (error) {
220
+ if (error instanceof external_provider_errors_1.JiraProviderError && error.details?.reason === 'not_found') {
221
+ return null;
222
+ }
223
+ throw error;
224
+ }
225
+ }
226
+ async updateSubtask(credentials, context, input) {
227
+ const current = await this.assertOwnedSubtask(credentials, context, input);
228
+ const fields = {};
229
+ if (input.title !== undefined) {
230
+ fields.summary = this.requireSubtaskTitle(input.title);
231
+ }
232
+ if (input.description !== undefined) {
233
+ fields.description = this.managedDescriptionAdf(this.requireSubtaskDescription(input.description));
234
+ }
235
+ if (Object.keys(fields).length === 0) {
236
+ throw new external_provider_errors_1.JiraProviderError('request_rejected');
237
+ }
238
+ await this.requestNoContent(this.resolveSite(credentials), `/rest/api/3/issue/${encodeURIComponent(current.remoteKey)}`, { method: 'PUT', body: JSON.stringify({ fields }) });
239
+ }
240
+ async deleteSubtask(credentials, context, proof) {
241
+ const current = await this.readSubtaskExact(credentials, context, proof.remoteTaskId);
242
+ if (current === null) {
243
+ return { outcome: 'already_absent' };
244
+ }
245
+ this.assertSubtaskProof(current, proof);
246
+ try {
247
+ await this.requestNoContent(this.resolveSite(credentials), `/rest/api/3/issue/${encodeURIComponent(current.remoteKey)}`, { method: 'DELETE' });
248
+ }
249
+ catch (error) {
250
+ if (error instanceof external_provider_errors_1.JiraProviderError && error.details?.reason === 'not_found') {
251
+ return { outcome: 'already_absent' };
252
+ }
253
+ throw error;
254
+ }
255
+ return { outcome: 'deleted' };
256
+ }
257
+ async listOwnedDirectChildren(credentials, context, parentRemoteTaskId, ownershipToken) {
258
+ const site = this.resolveSite(credentials);
259
+ const parentTaskId = this.validate.requiredTaskId(parentRemoteTaskId);
260
+ const normalizedToken = this.requireSubtaskOwnershipToken(ownershipToken);
261
+ const url = new URL(`${site.origin}/rest/api/3/issue/${encodeURIComponent(parentTaskId)}`);
262
+ url.searchParams.set('fields', 'subtasks');
263
+ const payload = await this.requestJson(site, `${url.pathname}${url.search}`);
264
+ if (!(0, vendor_shared_1.isRecord)(payload) ||
265
+ !(0, vendor_shared_1.isRecord)(payload.fields) ||
266
+ !Array.isArray(payload.fields.subtasks)) {
267
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
268
+ }
269
+ const references = payload.fields.subtasks;
270
+ const capped = references.length > external_provider_models_1.MAX_TASK_DETAIL_SUBTASKS;
271
+ const selected = references.slice(0, external_provider_models_1.MAX_TASK_DETAIL_SUBTASKS).map((value) => {
272
+ if (!(0, vendor_shared_1.isRecord)(value)) {
273
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
274
+ }
275
+ return this.validate.requiredTaskId(typeof value.key === 'string' && value.key.trim()
276
+ ? value.key
277
+ : this.validate.requiredIdentifier(value.id));
278
+ });
279
+ let staleReference = false;
280
+ const children = await (0, vendor_shared_1.mapWithConcurrency)(selected, vendor_shared_1.VENDOR_DISCOVERY_CONCURRENCY, async (childId) => {
281
+ const child = await this.readSubtaskExact(credentials, context, childId);
282
+ if (child === null) {
283
+ staleReference = true;
284
+ }
285
+ return child;
286
+ });
287
+ const items = children.filter((child) => child !== null &&
288
+ (child.parentRemoteTaskId === parentTaskId ||
289
+ child.parentRemoteTaskId === this.optionalJiraKey(payload)) &&
290
+ child.ownershipToken === normalizedToken);
291
+ return { items, complete: !capped && !staleReference };
292
+ }
293
+ optionalJiraKey(value) {
294
+ if (typeof value.key !== 'string') {
295
+ return null;
296
+ }
297
+ const key = value.key.trim();
298
+ return key.length > 0 && key.length <= external_provider_models_1.MAX_REMOTE_TASK_ID_LENGTH ? key : null;
299
+ }
300
+ async assertOwnedSubtask(credentials, context, proof) {
301
+ const current = await this.readSubtaskExact(credentials, context, proof.remoteTaskId);
302
+ if (current === null) {
303
+ throw new external_provider_errors_1.JiraProviderError('not_found');
304
+ }
305
+ this.assertSubtaskProof(current, proof);
306
+ return current;
307
+ }
308
+ assertSubtaskProof(current, proof) {
309
+ const ownershipToken = this.requireSubtaskOwnershipToken(proof.ownershipToken);
310
+ const expectedParent = this.validate.requiredTaskId(proof.expectedParentRemoteTaskId);
311
+ if (current.ownershipToken !== ownershipToken) {
312
+ throw new external_provider_errors_1.JiraProviderError('ownership_mismatch');
313
+ }
314
+ if (current.parentRemoteTaskId !== expectedParent) {
315
+ throw new external_provider_errors_1.JiraProviderError('parent_mismatch');
316
+ }
317
+ }
318
+ normalizeManagedSubtask(value) {
319
+ if (!(0, vendor_shared_1.isRecord)(value) || !(0, vendor_shared_1.isRecord)(value.fields) || !(0, vendor_shared_1.isRecord)(value.fields.project)) {
320
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
321
+ }
322
+ const parent = value.fields.parent;
323
+ if (parent !== null && parent !== undefined && !(0, vendor_shared_1.isRecord)(parent)) {
324
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
325
+ }
326
+ const parentRemoteTaskId = parent === null || parent === undefined
327
+ ? null
328
+ : typeof parent.key === 'string' && parent.key.trim()
329
+ ? parent.key.trim()
330
+ : this.validate.requiredIdentifier(parent.id);
331
+ const properties = value.properties;
332
+ if (properties !== null && properties !== undefined && !(0, vendor_shared_1.isRecord)(properties)) {
333
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
334
+ }
335
+ const propertyValue = (0, vendor_shared_1.isRecord)(properties)
336
+ ? properties[JIRA_MANAGED_SUBTASK_PROPERTY]
337
+ : undefined;
338
+ const ownershipToken = this.parseJiraOwnershipProperty(propertyValue);
339
+ this.validate.requiredIdentifier(value.id);
340
+ const remoteKey = this.validate.requiredString(value.key);
341
+ return {
342
+ remoteTaskId: remoteKey,
343
+ remoteKey,
344
+ parentRemoteTaskId,
345
+ workAreaRemoteId: this.validate.requiredIdentifier(value.fields.project.id),
346
+ ownershipToken,
347
+ title: this.validate.requiredString(value.fields.summary),
348
+ description: this.parseManagedDescriptionAdf(value.fields.description),
349
+ };
350
+ }
351
+ parseJiraOwnershipProperty(value) {
352
+ if (!(0, vendor_shared_1.isRecord)(value) || value.version !== 1 || typeof value.ownershipToken !== 'string') {
353
+ return null;
354
+ }
355
+ return this.isSubtaskOwnershipToken(value.ownershipToken) ? value.ownershipToken : null;
356
+ }
357
+ managedDescriptionAdf(description) {
358
+ const paragraphs = description === null ? [] : description.split('\n');
359
+ return {
360
+ type: 'doc',
361
+ version: 1,
362
+ content: [
363
+ ...paragraphs.map((line) => ({
364
+ type: 'paragraph',
365
+ ...(line ? { content: [{ type: 'text', text: line }] } : {}),
366
+ })),
367
+ {
368
+ type: 'paragraph',
369
+ content: [{ type: 'text', text: external_provider_models_1.EXTERNAL_SUBTASK_MANAGEMENT_NOTE }],
370
+ },
371
+ ],
372
+ };
373
+ }
374
+ parseManagedDescriptionAdf(value) {
375
+ if (value === null || value === undefined) {
376
+ return null;
377
+ }
378
+ const text = this.flattenAdfText(value);
379
+ if (text === external_provider_models_1.EXTERNAL_SUBTASK_MANAGEMENT_NOTE) {
380
+ return null;
381
+ }
382
+ const suffix = `\n${external_provider_models_1.EXTERNAL_SUBTASK_MANAGEMENT_NOTE}`;
383
+ return text.endsWith(suffix) ? text.slice(0, -suffix.length) || null : text || null;
384
+ }
385
+ requireSubtaskTitle(value) {
386
+ if (typeof value !== 'string' ||
387
+ !value.trim() ||
388
+ value.length > external_provider_models_1.MAX_EXTERNAL_SUBTASK_TITLE_LENGTH) {
389
+ throw new external_provider_errors_1.JiraProviderError('request_rejected');
390
+ }
391
+ return value.trim();
392
+ }
393
+ requireSubtaskDescription(value) {
394
+ if (value === null || value === '') {
395
+ return null;
396
+ }
397
+ if (typeof value !== 'string' || value.length > external_provider_models_1.MAX_EXTERNAL_SUBTASK_DESCRIPTION_LENGTH) {
398
+ throw new external_provider_errors_1.JiraProviderError('request_rejected');
399
+ }
400
+ return value;
401
+ }
402
+ isSubtaskOwnershipToken(value) {
403
+ return (value.length <= external_provider_models_1.MAX_EXTERNAL_SUBTASK_OWNERSHIP_TOKEN_LENGTH &&
404
+ /^[A-Za-z0-9][A-Za-z0-9._:-]*$/.test(value));
405
+ }
406
+ requireSubtaskOwnershipToken(value) {
407
+ if (typeof value !== 'string' || !this.isSubtaskOwnershipToken(value)) {
408
+ throw new external_provider_errors_1.JiraProviderError('request_rejected');
409
+ }
410
+ return value;
411
+ }
412
+ async findComment(credentials, _context, remoteTaskId, commentId) {
413
+ const site = this.resolveSite(credentials);
414
+ const taskId = this.validate.requiredTaskId(remoteTaskId);
415
+ const normalizedCommentId = this.validate.requiredIdentifier(commentId);
416
+ const payload = await this.requestJson(site, `/rest/api/3/issue/${encodeURIComponent(taskId)}/comment/${encodeURIComponent(normalizedCommentId)}`);
417
+ if (!(0, vendor_shared_1.isRecord)(payload)) {
418
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
419
+ }
420
+ const snapshot = this.normalizeComment(payload);
421
+ return {
422
+ remoteId: snapshot.remoteId,
423
+ authorRemoteId: snapshot.author.remoteId,
424
+ createdAt: snapshot.createdAt,
425
+ raw: payload.body ?? null,
426
+ metadata: { assignee: null, resolved: null, groupAssignee: null },
427
+ };
428
+ }
429
+ async deleteComment(credentials, _context, remoteTaskId, commentId) {
430
+ const site = this.resolveSite(credentials);
431
+ const taskId = this.validate.requiredTaskId(remoteTaskId);
432
+ const normalizedCommentId = this.validate.requiredIdentifier(commentId);
433
+ await this.requestNoContent(site, `/rest/api/3/issue/${encodeURIComponent(taskId)}/comment/${encodeURIComponent(normalizedCommentId)}`, { method: 'DELETE' });
434
+ }
435
+ async updateOwnedComment(credentials, _context, remoteTaskId, commentId, document) {
436
+ const site = this.resolveSite(credentials);
437
+ const taskId = this.validate.requiredTaskId(remoteTaskId);
438
+ const normalizedCommentId = this.validate.requiredIdentifier(commentId);
439
+ await this.requestJson(site, `/rest/api/3/issue/${encodeURIComponent(taskId)}/comment/${encodeURIComponent(normalizedCommentId)}`, { method: 'PUT', body: JSON.stringify({ body: (0, jira_adf_converter_2.richDocumentToAdf)(document) }) });
440
+ }
441
+ async readDescription(credentials, _context, remoteTaskId) {
442
+ const site = this.resolveSite(credentials);
443
+ const taskId = this.validate.requiredTaskId(remoteTaskId);
444
+ const url = new URL(`${site.origin}/rest/api/3/issue/${encodeURIComponent(taskId)}`);
445
+ url.searchParams.set('fields', 'description');
446
+ const payload = await this.requestJson(site, `${url.pathname}${url.search}`);
447
+ if (!(0, vendor_shared_1.isRecord)(payload) || !(0, vendor_shared_1.isRecord)(payload.fields)) {
448
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
449
+ }
450
+ return payload.fields.description ?? null;
451
+ }
452
+ async writeDescription(credentials, _context, remoteTaskId, raw) {
453
+ const site = this.resolveSite(credentials);
454
+ const taskId = this.validate.requiredTaskId(remoteTaskId);
455
+ if (!(0, vendor_shared_1.isRecord)(raw) || raw.type !== 'doc' || raw.version !== 1 || !Array.isArray(raw.content)) {
456
+ throw new external_provider_errors_1.JiraProviderError('request_rejected');
457
+ }
458
+ await this.requestNoContent(site, `/rest/api/3/issue/${encodeURIComponent(taskId)}`, {
459
+ method: 'PUT',
460
+ body: JSON.stringify({ fields: { description: raw } }),
461
+ });
462
+ }
463
+ async verifyCredentials(credentials) {
464
+ const site = this.resolveSite(credentials);
465
+ const identity = await this.loadIdentity(site);
466
+ return {
467
+ provider: this.provider,
468
+ remoteId: identity.accountId,
469
+ displayName: identity.displayName,
470
+ };
471
+ }
472
+ async getTaskDetail(credentials, _context, remoteTaskId) {
473
+ const site = this.resolveSite(credentials);
474
+ const taskId = this.validate.requiredTaskId(remoteTaskId);
475
+ const issueUrl = new URL(`${site.origin}/rest/api/3/issue/${encodeURIComponent(taskId)}`);
476
+ issueUrl.searchParams.set('fields', JIRA_DETAIL_FIELDS.join(','));
477
+ const issue = await this.requestJson(site, `${issueUrl.pathname}${issueUrl.search}`);
478
+ const [[transitionResult, configurationResult], childResult] = await Promise.all([
479
+ Promise.allSettled([
480
+ this.loadTransitions(site, taskId),
481
+ this.requestJson(site, '/rest/api/3/configuration'),
482
+ ]),
483
+ this.normalizeTaskSubtasks(issue, site, taskId),
484
+ ]);
485
+ const transitions = transitionResult.status === 'fulfilled' ? transitionResult.value : [];
486
+ const timeTrackingEnabled = configurationResult.status === 'fulfilled' &&
487
+ (0, vendor_shared_1.isRecord)(configurationResult.value) &&
488
+ configurationResult.value.timeTrackingEnabled === true;
489
+ const detail = this.normalizeTaskDetail(issue, site, taskId);
490
+ const allowedStatuses = transitions
491
+ .filter((transition) => transition.supported)
492
+ .map((transition) => transition.option);
493
+ return {
494
+ ...detail,
495
+ subtasks: childResult.items,
496
+ subtasksTruncated: childResult.truncated,
497
+ allowedStatuses,
498
+ actions: [
499
+ { action: 'change_status', supported: allowedStatuses.length > 0 },
500
+ { action: 'add_comment', supported: true },
501
+ { action: 'log_time', supported: timeTrackingEnabled },
502
+ ],
503
+ };
504
+ }
505
+ async listTaskComments(credentials, _context, remoteTaskId, cursor) {
506
+ const site = this.resolveSite(credentials);
507
+ const taskId = this.validate.requiredTaskId(remoteTaskId);
508
+ const startAt = cursor === null ? 0 : this.decodeCommentCursor(cursor);
509
+ const url = new URL(`${site.origin}/rest/api/3/issue/${encodeURIComponent(taskId)}/comment`);
510
+ url.searchParams.set('maxResults', String(JIRA_COMMENT_PAGE_SIZE));
511
+ url.searchParams.set('orderBy', '-created');
512
+ url.searchParams.set('startAt', String(startAt));
513
+ const payload = await this.requestJson(site, `${url.pathname}${url.search}`);
514
+ if (!(0, vendor_shared_1.isRecord)(payload) ||
515
+ !Array.isArray(payload.comments) ||
516
+ payload.comments.length > JIRA_COMMENT_PAGE_SIZE) {
517
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
518
+ }
519
+ const returnedStart = this.requiredCount(payload.startAt);
520
+ const total = this.requiredCount(payload.total);
521
+ const comments = payload.comments.map((value) => this.enrichComment(value));
522
+ const nextStart = returnedStart + comments.length;
523
+ return {
524
+ comments,
525
+ nextCursor: nextStart > startAt && nextStart < total && nextStart <= JIRA_COMMENT_MAX_START_AT
526
+ ? String(nextStart)
527
+ : null,
528
+ };
529
+ }
530
+ enrichComment(value) {
531
+ const comment = this.normalizeComment(value);
532
+ const rawBody = (0, vendor_shared_1.isRecord)(value) ? (value.body ?? null) : null;
533
+ if (rawBody === null) {
534
+ return { ...comment, owned: false, rich: null, lookupToken: null };
535
+ }
536
+ const parsed = (0, jira_adf_converter_1.adfToRichDocument)(rawBody);
537
+ return {
538
+ ...comment,
539
+ owned: false,
540
+ rich: parsed.supported
541
+ ? { document: parsed.document, supported: true }
542
+ : { supported: false, readOnlyReason: parsed.reason },
543
+ lookupToken: null,
544
+ };
545
+ }
546
+ requiredCount(value) {
547
+ if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 0) {
548
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
549
+ }
550
+ return value;
551
+ }
552
+ decodeCommentCursor(cursor) {
553
+ if (cursor.length > external_provider_models_1.MAX_TASK_COMMENT_CURSOR_LENGTH ||
554
+ !JIRA_COMMENT_CURSOR_PATTERN.test(cursor)) {
555
+ throw new external_provider_errors_1.JiraProviderError('request_rejected');
556
+ }
557
+ const startAt = Number(cursor);
558
+ if (!Number.isSafeInteger(startAt) || startAt > JIRA_COMMENT_MAX_START_AT) {
559
+ throw new external_provider_errors_1.JiraProviderError('request_rejected');
560
+ }
561
+ return startAt;
562
+ }
563
+ normalizeComment(value) {
564
+ if (!(0, vendor_shared_1.isRecord)(value)) {
565
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
566
+ }
567
+ const remoteId = this.validate.requiredIdentifier(value.id);
568
+ if (remoteId.length > external_provider_models_1.MAX_TASK_COMMENT_ID_LENGTH) {
569
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
570
+ }
571
+ const body = this.adfCommentBody(value.body);
572
+ return {
573
+ remoteId,
574
+ author: this.validate.commentAuthor(value.author, JIRA_COMMENT_AUTHOR),
575
+ body: body.value,
576
+ bodyTruncated: body.truncated,
577
+ createdAt: this.requiredTimestamp(value.created),
578
+ updatedAt: this.optionalTimestamp(value.updated),
579
+ };
580
+ }
581
+ async changeTaskStatus(credentials, _context, remoteTaskId, input) {
582
+ const site = this.resolveSite(credentials);
583
+ const taskId = this.validate.requiredTaskId(remoteTaskId);
584
+ const transitionId = this.validate.statusInput(input);
585
+ const transition = (await this.loadTransitions(site, taskId)).find((candidate) => candidate.id === transitionId);
586
+ if (!transition?.supported) {
587
+ throw new external_provider_errors_1.JiraProviderError('unsupported_transition', undefined, {
588
+ completeInJiraUrl: this.issueWebUrl(site, taskId),
589
+ });
590
+ }
591
+ await this.requestNoContent(site, `/rest/api/3/issue/${encodeURIComponent(taskId)}/transitions`, {
592
+ method: 'POST',
593
+ body: JSON.stringify({ transition: { id: transitionId } }),
594
+ });
595
+ }
596
+ async addTaskComment(credentials, _context, remoteTaskId, input) {
597
+ const site = this.resolveSite(credentials);
598
+ const taskId = this.validate.requiredTaskId(remoteTaskId);
599
+ this.validate.commentInput(input);
600
+ await this.requestJson(site, `/rest/api/3/issue/${encodeURIComponent(taskId)}/comment`, {
601
+ method: 'POST',
602
+ body: JSON.stringify({ body: this.plainTextAdf(input.text) }),
603
+ });
604
+ }
605
+ async createTimeEntry(credentials, _context, remoteTaskId, input) {
606
+ const site = this.resolveSite(credentials);
607
+ const taskId = this.validate.requiredTaskId(remoteTaskId);
608
+ const startedAt = this.validate.timeEntryInput(input);
609
+ if (input.durationMs % 1000 !== 0) {
610
+ throw new external_provider_errors_1.JiraProviderError('request_rejected');
611
+ }
612
+ const configuration = await this.requestJson(site, '/rest/api/3/configuration');
613
+ if (!(0, vendor_shared_1.isRecord)(configuration)) {
614
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
615
+ }
616
+ if (configuration.timeTrackingEnabled !== true) {
617
+ throw new external_provider_errors_1.JiraProviderError('time_tracking_disabled');
618
+ }
619
+ const body = {
620
+ started: new Date(startedAt).toISOString().replace('Z', '+0000'),
621
+ timeSpentSeconds: input.durationMs / 1000,
622
+ };
623
+ if (input.note !== null && input.note !== '') {
624
+ body.comment = this.plainTextAdf(input.note);
625
+ }
626
+ const payload = await this.requestJson(site, `/rest/api/3/issue/${encodeURIComponent(taskId)}/worklog?adjustEstimate=leave`, { method: 'POST', body: JSON.stringify(body) });
627
+ if (!(0, vendor_shared_1.isRecord)(payload)) {
628
+ throw new external_provider_errors_1.JiraProviderError('invalid_response', undefined, { dispatched: true });
629
+ }
630
+ const id = payload.id;
631
+ if ((typeof id !== 'string' && typeof id !== 'number') ||
632
+ !this.validate.requiredIdentifier(id)) {
633
+ throw new external_provider_errors_1.JiraProviderError('invalid_response', undefined, { dispatched: true });
634
+ }
635
+ return { remoteEntryId: this.validate.requiredIdentifier(id) };
636
+ }
637
+ async readTimeEntryExact(credentials, _context, remoteTaskId, remoteEntryId) {
638
+ const site = this.resolveSite(credentials);
639
+ const taskId = this.validate.requiredTaskId(remoteTaskId);
640
+ const entryId = this.validate.requiredIdentifier(remoteEntryId);
641
+ const [identityPayload, payload] = await Promise.all([
642
+ this.requestJson(site, '/rest/api/3/myself'),
643
+ this.requestJson(site, `/rest/api/3/issue/${encodeURIComponent(taskId)}/worklog/${encodeURIComponent(entryId)}`).catch((error) => {
644
+ if (error instanceof external_provider_errors_1.JiraProviderError && error.details?.reason === 'not_found') {
645
+ return null;
646
+ }
647
+ throw error;
648
+ }),
649
+ ]);
650
+ if (payload === null) {
651
+ return null;
652
+ }
653
+ if (!(0, vendor_shared_1.isRecord)(identityPayload)) {
654
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
655
+ }
656
+ const accountId = this.validate.requiredString(identityPayload.accountId);
657
+ const parsed = this.parseWorklog(payload);
658
+ if (parsed.remoteId !== entryId) {
659
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
660
+ }
661
+ return {
662
+ remoteId: parsed.remoteId,
663
+ startedAt: new Date(parsed.startedAtMs).toISOString(),
664
+ durationMs: parsed.durationMs,
665
+ owned: parsed.authorAccountId === accountId,
666
+ };
667
+ }
668
+ async deleteTimeEntry(credentials, _context, remoteTaskId, remoteEntryId) {
669
+ const site = this.resolveSite(credentials);
670
+ const taskId = this.validate.requiredTaskId(remoteTaskId);
671
+ const entryId = this.validate.requiredIdentifier(remoteEntryId);
672
+ await this.requestNoContent(site, `/rest/api/3/issue/${encodeURIComponent(taskId)}/worklog/${encodeURIComponent(entryId)}?adjustEstimate=leave&notifyUsers=false`, { method: 'DELETE' });
673
+ }
674
+ async listOwnTimeEntryIdsInRange(credentials, _context, remoteTaskId, startedAfterMs, startedBeforeMs) {
675
+ const site = this.resolveSite(credentials);
676
+ const taskId = this.validate.requiredTaskId(remoteTaskId);
677
+ const identity = await this.loadIdentity(site);
678
+ const ids = [];
679
+ const seen = new Set();
680
+ let nextStart = 0;
681
+ for (let page = 0; page < JIRA_WORKLOG_MAX_PAGES; page += 1) {
682
+ const page_ = await this.requestWorklogPage(site, taskId, nextStart, JIRA_WORKLOG_PAGE_SIZE, startedAfterMs, startedBeforeMs);
683
+ for (const worklog of page_.worklogs) {
684
+ const parsed = this.parseWorklog(worklog);
685
+ if (parsed.authorAccountId !== identity.accountId) {
686
+ continue;
687
+ }
688
+ if (!seen.has(parsed.remoteId)) {
689
+ seen.add(parsed.remoteId);
690
+ ids.push(parsed.remoteId);
691
+ }
692
+ }
693
+ if (page_.worklogs.length < JIRA_WORKLOG_PAGE_SIZE) {
694
+ return { ids, complete: true };
695
+ }
696
+ const stepped = page_.startAt + page_.worklogs.length;
697
+ if (stepped <= nextStart || ids.length >= JIRA_WORKLOG_MAX_RAW) {
698
+ return { ids, complete: false };
699
+ }
700
+ if (stepped >= page_.total) {
701
+ return { ids, complete: true };
702
+ }
703
+ nextStart = stepped;
704
+ }
705
+ return { ids, complete: false };
706
+ }
707
+ async assertTimeEntryDeletable(credentials, context, remoteTaskId, remoteEntryId) {
708
+ const exact = await this.readTimeEntryExact(credentials, context, remoteTaskId, remoteEntryId);
709
+ if (exact === null) {
710
+ throw new external_provider_errors_1.JiraProviderError('not_found');
711
+ }
712
+ if (!exact.owned) {
713
+ throw new external_provider_errors_1.JiraProviderError('permission_denied');
714
+ }
715
+ const site = this.resolveSite(credentials);
716
+ if (!(await this.loadDeleteOwnWorklogsPermission(site))) {
717
+ throw new external_provider_errors_1.JiraProviderError('permission_denied');
718
+ }
719
+ }
720
+ async getTaskTimeEntryHistory(credentials, _context, remoteTaskId) {
721
+ const site = this.resolveSite(credentials);
722
+ const taskId = this.validate.requiredTaskId(remoteTaskId);
723
+ const now = Date.now();
724
+ const startedAfterMs = now - external_provider_models_1.TIME_ENTRY_HISTORY_WINDOW_DAYS * 86_400_000;
725
+ const [identity, canDeleteOwn] = await Promise.all([
726
+ this.loadIdentity(site),
727
+ this.loadDeleteOwnWorklogsPermission(site),
728
+ ]);
729
+ const { raw, complete } = await this.fetchWindowedWorklogs(site, taskId, startedAfterMs, now);
730
+ const own = [];
731
+ for (const value of raw) {
732
+ const parsed = this.parseWorklog(value);
733
+ if (parsed.authorAccountId !== identity.accountId) {
734
+ continue;
735
+ }
736
+ own.push(parsed);
737
+ }
738
+ own.sort((left, right) => right.startedAtMs - left.startedAtMs || (0, vendor_shared_1.compareRemoteIds)(left.remoteId, right.remoteId));
739
+ return {
740
+ windowDays: external_provider_models_1.TIME_ENTRY_HISTORY_WINDOW_DAYS,
741
+ truncated: !complete || own.length > external_provider_models_1.MAX_TIME_ENTRY_HISTORY_ENTRIES,
742
+ hasRunningTimer: false,
743
+ entries: own.slice(0, external_provider_models_1.MAX_TIME_ENTRY_HISTORY_ENTRIES).map((parsed) => ({
744
+ remoteId: parsed.remoteId,
745
+ durationMs: parsed.durationMs,
746
+ startedAt: new Date(parsed.startedAtMs).toISOString(),
747
+ note: parsed.note,
748
+ noteTruncated: parsed.noteTruncated,
749
+ canDelete: canDeleteOwn,
750
+ })),
751
+ };
752
+ }
753
+ async loadDeleteOwnWorklogsPermission(site) {
754
+ const url = new URL(`${site.origin}/rest/api/3/mypermissions`);
755
+ url.searchParams.set('permissions', 'DELETE_OWN_WORKLOGS');
756
+ const payload = await this.requestJson(site, `${url.pathname}${url.search}`);
757
+ if (!(0, vendor_shared_1.isRecord)(payload) || !(0, vendor_shared_1.isRecord)(payload.permissions)) {
758
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
759
+ }
760
+ const permission = payload.permissions.DELETE_OWN_WORKLOGS;
761
+ if (!(0, vendor_shared_1.isRecord)(permission)) {
762
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
763
+ }
764
+ return permission.enabled === true;
765
+ }
766
+ async fetchWindowedWorklogs(site, taskId, startedAfterMs, startedBeforeMs) {
767
+ const rawById = new Map();
768
+ const collect = (worklogs) => {
769
+ for (const worklog of worklogs) {
770
+ if (!(0, vendor_shared_1.isRecord)(worklog)) {
771
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
772
+ }
773
+ rawById.set(this.validate.requiredIdentifier(worklog.id), worklog);
774
+ }
775
+ };
776
+ const raw = () => [...rawById.values()];
777
+ const probe = await this.requestWorklogPage(site, taskId, 0, 1, startedAfterMs, startedBeforeMs);
778
+ if (probe.worklogs.length > 1) {
779
+ collect(probe.worklogs);
780
+ return { raw: raw(), complete: rawById.size >= probe.total };
781
+ }
782
+ if (probe.total <= probe.worklogs.length) {
783
+ collect(probe.worklogs);
784
+ return { raw: raw(), complete: true };
785
+ }
786
+ if (probe.worklogs.length === 0) {
787
+ return this.walkWorklogsForward(site, taskId, startedAfterMs, startedBeforeMs, rawById, collect, probe.total, raw);
788
+ }
789
+ return this.walkWorklogsBackward(site, taskId, startedAfterMs, startedBeforeMs, rawById, collect, probe.total, raw);
790
+ }
791
+ async walkWorklogsBackward(site, taskId, startedAfterMs, startedBeforeMs, rawById, collect, anchorTotal, raw) {
792
+ let anchor = anchorTotal;
793
+ let nextStart = Math.max(0, anchor - JIRA_WORKLOG_PAGE_SIZE);
794
+ for (let pages = 1; pages < JIRA_WORKLOG_MAX_PAGES; pages += 1) {
795
+ if (rawById.size >= JIRA_WORKLOG_MAX_RAW) {
796
+ return { raw: raw(), complete: false };
797
+ }
798
+ const page = await this.requestWorklogPage(site, taskId, nextStart, JIRA_WORKLOG_PAGE_SIZE, startedAfterMs, startedBeforeMs);
799
+ if (page.total !== anchor) {
800
+ anchor = page.total;
801
+ }
802
+ collect(page.worklogs);
803
+ if (page.startAt === 0 || rawById.size >= anchor) {
804
+ return { raw: raw(), complete: rawById.size >= anchor || page.startAt === 0 };
805
+ }
806
+ const stepped = page.startAt - JIRA_WORKLOG_PAGE_SIZE;
807
+ if (stepped >= nextStart) {
808
+ return { raw: raw(), complete: false };
809
+ }
810
+ nextStart = Math.max(0, stepped);
811
+ }
812
+ return { raw: raw(), complete: rawById.size >= anchor };
813
+ }
814
+ async walkWorklogsForward(site, taskId, startedAfterMs, startedBeforeMs, rawById, collect, anchorTotal, raw) {
815
+ let anchor = anchorTotal;
816
+ let nextStart = 0;
817
+ for (let pages = 1; pages < JIRA_WORKLOG_MAX_PAGES; pages += 1) {
818
+ if (rawById.size >= JIRA_WORKLOG_MAX_RAW) {
819
+ return { raw: raw(), complete: false };
820
+ }
821
+ const page = await this.requestWorklogPage(site, taskId, nextStart, JIRA_WORKLOG_PAGE_SIZE, startedAfterMs, startedBeforeMs);
822
+ if (page.total !== anchor) {
823
+ anchor = page.total;
824
+ }
825
+ collect(page.worklogs);
826
+ if (page.worklogs.length < JIRA_WORKLOG_PAGE_SIZE) {
827
+ return {
828
+ raw: raw(),
829
+ complete: page.worklogs.length > 0 || rawById.size >= anchor,
830
+ };
831
+ }
832
+ if (rawById.size >= anchor) {
833
+ return { raw: raw(), complete: true };
834
+ }
835
+ const stepped = page.startAt + page.worklogs.length;
836
+ if (stepped <= nextStart) {
837
+ return { raw: raw(), complete: false };
838
+ }
839
+ nextStart = stepped;
840
+ }
841
+ return { raw: raw(), complete: rawById.size >= anchor };
842
+ }
843
+ parseWorklogPage(payload) {
844
+ if (!(0, vendor_shared_1.isRecord)(payload) || !Array.isArray(payload.worklogs)) {
845
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
846
+ }
847
+ const startAt = this.requiredCount(payload.startAt);
848
+ const maxResults = this.requiredCount(payload.maxResults);
849
+ const total = this.requiredCount(payload.total);
850
+ if (payload.worklogs.length > maxResults) {
851
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
852
+ }
853
+ if (payload.worklogs.length > 0 && startAt + payload.worklogs.length > total) {
854
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
855
+ }
856
+ return { startAt, maxResults, total, worklogs: payload.worklogs };
857
+ }
858
+ async requestWorklogPage(site, taskId, startAt, maxResults, startedAfterMs, startedBeforeMs) {
859
+ const url = new URL(`${site.origin}/rest/api/3/issue/${encodeURIComponent(taskId)}/worklog`);
860
+ url.searchParams.set('startedAfter', String(startedAfterMs));
861
+ url.searchParams.set('startedBefore', String(startedBeforeMs));
862
+ url.searchParams.set('startAt', String(startAt));
863
+ url.searchParams.set('maxResults', String(maxResults));
864
+ const payload = await this.requestJson(site, `${url.pathname}${url.search}`);
865
+ return this.parseWorklogPage(payload);
866
+ }
867
+ parseWorklog(value) {
868
+ if (!(0, vendor_shared_1.isRecord)(value)) {
869
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
870
+ }
871
+ const author = value.author;
872
+ if (author !== null && author !== undefined && !(0, vendor_shared_1.isRecord)(author)) {
873
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
874
+ }
875
+ const authorAccountId = author === null || author === undefined
876
+ ? null
877
+ : this.validate.requiredString(author.accountId);
878
+ const seconds = value.timeSpentSeconds;
879
+ if (typeof seconds !== 'number' || !Number.isSafeInteger(seconds) || seconds < 0) {
880
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
881
+ }
882
+ const note = this.worklogNote(value.comment);
883
+ return {
884
+ remoteId: this.validate.requiredIdentifier(value.id),
885
+ authorAccountId,
886
+ startedAtMs: this.parseWorklogStarted(value.started),
887
+ durationMs: seconds * 1_000,
888
+ note: note.value,
889
+ noteTruncated: note.truncated,
890
+ };
891
+ }
892
+ worklogNote(value) {
893
+ if (value === null || value === undefined) {
894
+ return { value: null, truncated: false };
895
+ }
896
+ const text = this.flattenAdfText(value);
897
+ return {
898
+ value: text ? text.slice(0, external_provider_models_1.MAX_TIME_ENTRY_NOTE_LENGTH) : null,
899
+ truncated: text.length > external_provider_models_1.MAX_TIME_ENTRY_NOTE_LENGTH,
900
+ };
901
+ }
902
+ parseWorklogStarted(value) {
903
+ if (typeof value !== 'string' || !value.trim()) {
904
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
905
+ }
906
+ const normalized = value.trim().replace(/([+-]\d{2})(\d{2})$/, '$1:$2');
907
+ const parsed = Date.parse(normalized);
908
+ if (!Number.isFinite(parsed)) {
909
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
910
+ }
911
+ return parsed;
912
+ }
913
+ taskTotalDurationMs(value) {
914
+ if (value === null || value === undefined) {
915
+ return null;
916
+ }
917
+ if (!(0, vendor_shared_1.isRecord)(value)) {
918
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
919
+ }
920
+ const seconds = value.timeSpentSeconds;
921
+ if (seconds === null || seconds === undefined) {
922
+ return null;
923
+ }
924
+ if (typeof seconds !== 'number' || !Number.isSafeInteger(seconds) || seconds < 0) {
925
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
926
+ }
927
+ return seconds * 1_000;
928
+ }
929
+ normalizeTaskDetail(value, site, taskId) {
930
+ if (!(0, vendor_shared_1.isRecord)(value) ||
931
+ !(0, vendor_shared_1.isRecord)(value.fields) ||
932
+ this.validate.requiredString(value.key) !== taskId) {
933
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
934
+ }
935
+ const fields = value.fields;
936
+ if (!(0, vendor_shared_1.isRecord)(fields.status) ||
937
+ !(0, vendor_shared_1.isRecord)(fields.status.statusCategory) ||
938
+ !(0, vendor_shared_1.isRecord)(fields.project)) {
939
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
940
+ }
941
+ const category = this.classifyStatus(this.validate.requiredString(fields.status.statusCategory.key).toLowerCase());
942
+ const description = this.adfPlainText(fields.description);
943
+ const projectId = this.validate.requiredIdentifier(fields.project.id);
944
+ const projectName = this.validate.requiredString(fields.project.name);
945
+ const statusId = this.validate.requiredIdentifier(fields.status.id);
946
+ return {
947
+ remoteId: taskId,
948
+ remoteKey: taskId,
949
+ title: this.validate.requiredString(fields.summary),
950
+ description: description.value,
951
+ descriptionTruncated: description.truncated,
952
+ status: {
953
+ remoteId: statusId,
954
+ remoteStatusIds: [statusId],
955
+ name: this.validate.requiredString(fields.status.name),
956
+ color: this.statusColor(category),
957
+ category,
958
+ position: 0,
959
+ },
960
+ dueAt: this.optionalDueDate(fields.duedate),
961
+ priority: this.normalizePriority(fields.priority),
962
+ taskTotalDurationMs: this.taskTotalDurationMs(fields.timetracking),
963
+ webUrl: this.issueWebUrl(site, taskId),
964
+ location: {
965
+ scopeKey: site.hostname,
966
+ workAreaId: projectId,
967
+ workAreaName: projectName,
968
+ },
969
+ };
970
+ }
971
+ async normalizeTaskSubtasks(value, site, parentRemoteTaskId) {
972
+ if (!(0, vendor_shared_1.isRecord)(value) || !(0, vendor_shared_1.isRecord)(value.fields) || !Array.isArray(value.fields.subtasks)) {
973
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
974
+ }
975
+ const references = value.fields.subtasks;
976
+ const selected = references.slice(0, external_provider_models_1.MAX_TASK_DETAIL_SUBTASKS);
977
+ const normalized = await (0, vendor_shared_1.mapWithConcurrency)(selected, vendor_shared_1.VENDOR_DISCOVERY_CONCURRENCY, (reference) => this.normalizeTaskSubtask(reference, site, parentRemoteTaskId));
978
+ const items = normalized
979
+ .flatMap(({ item }) => (item === null ? [] : [item]))
980
+ .sort(vendor_shared_1.compareTaskSubtaskSummaries);
981
+ return {
982
+ items,
983
+ truncated: references.length > external_provider_models_1.MAX_TASK_DETAIL_SUBTASKS ||
984
+ normalized.some((result) => result.truncated),
985
+ };
986
+ }
987
+ async normalizeTaskSubtask(reference, site, parentRemoteTaskId) {
988
+ if (!(0, vendor_shared_1.isRecord)(reference)) {
989
+ return { item: null, truncated: true };
990
+ }
991
+ const childKey = this.optionalJiraKey(reference);
992
+ if (childKey === null) {
993
+ return { item: null, truncated: true };
994
+ }
995
+ const rich = this.normalizeTaskSubtaskReference(reference, site, childKey);
996
+ if (rich !== null) {
997
+ return { item: rich, truncated: false };
998
+ }
999
+ return this.readTaskSubtaskFallback(site, childKey, parentRemoteTaskId);
1000
+ }
1001
+ normalizeTaskSubtaskReference(value, site, childKey) {
1002
+ if (!(0, vendor_shared_1.isRecord)(value.fields) ||
1003
+ !(0, vendor_shared_1.isRecord)(value.fields.status) ||
1004
+ !(0, vendor_shared_1.isRecord)(value.fields.status.statusCategory)) {
1005
+ return null;
1006
+ }
1007
+ try {
1008
+ const category = this.classifyStatus(this.validate.requiredString(value.fields.status.statusCategory.key).toLowerCase());
1009
+ return {
1010
+ remoteId: childKey,
1011
+ remoteKey: childKey,
1012
+ title: this.validate.requiredString(value.fields.summary),
1013
+ status: {
1014
+ remoteId: this.validate.requiredIdentifier(value.fields.status.id),
1015
+ name: this.validate.requiredString(value.fields.status.name),
1016
+ category,
1017
+ },
1018
+ webUrl: this.issueWebUrl(site, childKey),
1019
+ };
1020
+ }
1021
+ catch (error) {
1022
+ if (error instanceof external_provider_errors_1.JiraProviderError) {
1023
+ return null;
1024
+ }
1025
+ throw error;
1026
+ }
1027
+ }
1028
+ async readTaskSubtaskFallback(site, childKey, parentRemoteTaskId) {
1029
+ const url = new URL(`${site.origin}/rest/api/3/issue/${encodeURIComponent(childKey)}`);
1030
+ url.searchParams.set('fields', 'summary,status,parent');
1031
+ let payload;
1032
+ try {
1033
+ payload = await this.requestJson(site, `${url.pathname}${url.search}`);
1034
+ }
1035
+ catch (error) {
1036
+ if (error instanceof external_provider_errors_1.JiraProviderError && error.details?.reason === 'not_found') {
1037
+ return { item: null, truncated: true };
1038
+ }
1039
+ throw error;
1040
+ }
1041
+ if (!(0, vendor_shared_1.isRecord)(payload) ||
1042
+ this.optionalJiraKey(payload) !== childKey ||
1043
+ !(0, vendor_shared_1.isRecord)(payload.fields)) {
1044
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1045
+ }
1046
+ if (!(0, vendor_shared_1.isRecord)(payload.fields.parent) ||
1047
+ this.optionalJiraKey(payload.fields.parent) !== parentRemoteTaskId) {
1048
+ return { item: null, truncated: true };
1049
+ }
1050
+ const item = this.normalizeTaskSubtaskReference(payload, site, childKey);
1051
+ if (item === null) {
1052
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1053
+ }
1054
+ return { item, truncated: false };
1055
+ }
1056
+ async loadTransitions(site, taskId) {
1057
+ const url = new URL(`${site.origin}/rest/api/3/issue/${encodeURIComponent(taskId)}/transitions`);
1058
+ url.searchParams.set('expand', 'transitions.fields');
1059
+ const payload = await this.requestJson(site, `${url.pathname}${url.search}`);
1060
+ if (!(0, vendor_shared_1.isRecord)(payload) || !Array.isArray(payload.transitions)) {
1061
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1062
+ }
1063
+ return payload.transitions.map((value, position) => {
1064
+ if (!(0, vendor_shared_1.isRecord)(value) || !(0, vendor_shared_1.isRecord)(value.to) || !(0, vendor_shared_1.isRecord)(value.to.statusCategory)) {
1065
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1066
+ }
1067
+ if (!(0, vendor_shared_1.isRecord)(value.fields)) {
1068
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1069
+ }
1070
+ const hasRequiredFields = Object.values(value.fields).some((field) => {
1071
+ if (!(0, vendor_shared_1.isRecord)(field)) {
1072
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1073
+ }
1074
+ return field.required === true;
1075
+ });
1076
+ const category = this.classifyStatus(this.validate.requiredString(value.to.statusCategory.key).toLowerCase());
1077
+ const destinationStatusId = this.validate.requiredIdentifier(value.to.id);
1078
+ const transitionId = this.validate.requiredIdentifier(value.id);
1079
+ return {
1080
+ id: transitionId,
1081
+ supported: value.isAvailable !== false && !hasRequiredFields,
1082
+ option: {
1083
+ actionValue: transitionId,
1084
+ actionLabel: this.validate.requiredString(value.name),
1085
+ remoteId: destinationStatusId,
1086
+ remoteStatusIds: [destinationStatusId],
1087
+ name: this.validate.requiredString(value.to.name),
1088
+ color: this.statusColor(category),
1089
+ category,
1090
+ position,
1091
+ },
1092
+ };
1093
+ });
1094
+ }
1095
+ adfPlainText(value) {
1096
+ const text = this.flattenAdfText(value);
1097
+ return {
1098
+ value: text ? text.slice(0, external_provider_models_1.MAX_TASK_DETAIL_TEXT_LENGTH) : null,
1099
+ truncated: text.length > external_provider_models_1.MAX_TASK_DETAIL_TEXT_LENGTH,
1100
+ };
1101
+ }
1102
+ adfCommentBody(value) {
1103
+ const text = this.flattenAdfText(value);
1104
+ return {
1105
+ value: text.slice(0, external_provider_models_1.MAX_TASK_COMMENT_BODY_LENGTH),
1106
+ truncated: text.length > external_provider_models_1.MAX_TASK_COMMENT_BODY_LENGTH,
1107
+ };
1108
+ }
1109
+ flattenAdfText(value) {
1110
+ if (value === null || value === undefined) {
1111
+ return '';
1112
+ }
1113
+ if (!(0, vendor_shared_1.isRecord)(value) ||
1114
+ value.type !== 'doc' ||
1115
+ value.version !== 1 ||
1116
+ !Array.isArray(value.content)) {
1117
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1118
+ }
1119
+ const fragments = [];
1120
+ const visit = (node, depth) => {
1121
+ if (depth > 128) {
1122
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1123
+ }
1124
+ if (!(0, vendor_shared_1.isRecord)(node) || typeof node.type !== 'string') {
1125
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1126
+ }
1127
+ if (node.type === 'text') {
1128
+ if (typeof node.text !== 'string') {
1129
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1130
+ }
1131
+ fragments.push(node.text);
1132
+ }
1133
+ else if (node.type === 'hardBreak') {
1134
+ fragments.push('\n');
1135
+ }
1136
+ else if (node.type === 'mention' || node.type === 'status') {
1137
+ fragments.push(this.adfAttrFallback(node, 'text'));
1138
+ }
1139
+ else if (node.type === 'emoji') {
1140
+ fragments.push(this.adfAttrFallback(node, 'shortName'));
1141
+ }
1142
+ else if (node.type === 'inlineCard') {
1143
+ fragments.push(this.adfAttrFallback(node, 'url'));
1144
+ }
1145
+ else if (node.type === 'date') {
1146
+ fragments.push(this.adfDateFallback(node));
1147
+ }
1148
+ if (node.content !== undefined) {
1149
+ if (!Array.isArray(node.content)) {
1150
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1151
+ }
1152
+ node.content.forEach((child) => visit(child, depth + 1));
1153
+ }
1154
+ if (node.type === 'paragraph' || node.type === 'heading') {
1155
+ fragments.push('\n');
1156
+ }
1157
+ };
1158
+ value.content.forEach((node) => visit(node, 0));
1159
+ return fragments.join('').replace(/\n+$/, '');
1160
+ }
1161
+ adfAttrFallback(node, attr) {
1162
+ const attrs = node.attrs;
1163
+ if (attrs === null || attrs === undefined) {
1164
+ return '';
1165
+ }
1166
+ if (!(0, vendor_shared_1.isRecord)(attrs)) {
1167
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1168
+ }
1169
+ const value = attrs[attr];
1170
+ if (value === null || value === undefined) {
1171
+ return '';
1172
+ }
1173
+ if (typeof value !== 'string') {
1174
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1175
+ }
1176
+ return value.trim() ? value : '';
1177
+ }
1178
+ adfDateFallback(node) {
1179
+ const attrs = node.attrs;
1180
+ if (!(0, vendor_shared_1.isRecord)(attrs) || typeof attrs.timestamp !== 'string' || !/^\d+$/.test(attrs.timestamp)) {
1181
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1182
+ }
1183
+ try {
1184
+ return new Date(Number(attrs.timestamp)).toISOString().slice(0, 10);
1185
+ }
1186
+ catch {
1187
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1188
+ }
1189
+ }
1190
+ plainTextAdf(text) {
1191
+ return {
1192
+ type: 'doc',
1193
+ version: 1,
1194
+ content: [{ type: 'paragraph', content: [{ type: 'text', text }] }],
1195
+ };
1196
+ }
1197
+ normalizePriority(value) {
1198
+ if (value === null || value === undefined) {
1199
+ return null;
1200
+ }
1201
+ if (!(0, vendor_shared_1.isRecord)(value)) {
1202
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1203
+ }
1204
+ return { name: this.validate.requiredString(value.name), color: JIRA_NEUTRAL_COLOR };
1205
+ }
1206
+ statusColor(category) {
1207
+ return category === 'completed' ? JIRA_COMPLETED_COLOR : JIRA_NEUTRAL_COLOR;
1208
+ }
1209
+ issueWebUrl(site, taskId) {
1210
+ return `${site.origin}/browse/${encodeURIComponent(taskId)}`;
1211
+ }
1212
+ async discoverMyWork(credentials, options) {
1213
+ const site = this.resolveSite(credentials);
1214
+ const now = Date.now();
1215
+ const cutoff = now - vendor_shared_1.RECENT_COMPLETED_WINDOW_MS;
1216
+ const [identity, configuration, issues] = await Promise.all([
1217
+ this.loadIdentity(site),
1218
+ this.requestJson(site, '/rest/api/3/configuration'),
1219
+ this.searchAssignedIssues(site, options.includeCompleted),
1220
+ ]);
1221
+ if (!(0, vendor_shared_1.isRecord)(configuration)) {
1222
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1223
+ }
1224
+ const uniqueTasks = new Map();
1225
+ for (const issue of issues) {
1226
+ const normalized = this.normalizeIssue(issue, site, identity, {
1227
+ includeCompleted: options.includeCompleted,
1228
+ cutoff,
1229
+ now,
1230
+ });
1231
+ if (normalized && !uniqueTasks.has(normalized.task.remoteId)) {
1232
+ uniqueTasks.set(normalized.task.remoteId, {
1233
+ item: normalized,
1234
+ projectKey: this.issueProjectKey(issue),
1235
+ });
1236
+ }
1237
+ }
1238
+ const routed = await this.discoverRelevantBoards(site, options, [...uniqueTasks.values()], now);
1239
+ return {
1240
+ capabilities: { timeTrackingEnabled: configuration.timeTrackingEnabled === true },
1241
+ workAreas: routed.workAreas,
1242
+ tasks: routed.tasks,
1243
+ refreshedAt: new Date(now).toISOString(),
1244
+ };
1245
+ }
1246
+ async loadIdentity(site) {
1247
+ const payload = await this.requestJson(site, '/rest/api/3/myself');
1248
+ if (!(0, vendor_shared_1.isRecord)(payload)) {
1249
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1250
+ }
1251
+ return {
1252
+ accountId: this.validate.requiredString(payload.accountId),
1253
+ displayName: this.validate.requiredString(payload.displayName),
1254
+ };
1255
+ }
1256
+ async searchAssignedIssues(site, includeCompleted) {
1257
+ const jql = this.assignedJql(includeCompleted);
1258
+ const issues = [];
1259
+ const seenTokens = new Set();
1260
+ let nextPageToken;
1261
+ for (let page = 0; page < JIRA_MAX_SEARCH_PAGES; page += 1) {
1262
+ const body = {
1263
+ jql,
1264
+ fields: [...JIRA_SEARCH_FIELDS],
1265
+ maxResults: JIRA_SEARCH_PAGE_SIZE,
1266
+ };
1267
+ if (nextPageToken !== undefined) {
1268
+ body.nextPageToken = nextPageToken;
1269
+ }
1270
+ const payload = await this.requestJson(site, '/rest/api/3/search/jql', {
1271
+ method: 'POST',
1272
+ body: JSON.stringify(body),
1273
+ });
1274
+ if (!(0, vendor_shared_1.isRecord)(payload) || !Array.isArray(payload.issues)) {
1275
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1276
+ }
1277
+ issues.push(...payload.issues);
1278
+ if (payload.nextPageToken === undefined || payload.nextPageToken === null) {
1279
+ return issues;
1280
+ }
1281
+ if (typeof payload.nextPageToken !== 'string' || !payload.nextPageToken.trim()) {
1282
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1283
+ }
1284
+ nextPageToken = payload.nextPageToken.trim();
1285
+ if (seenTokens.has(nextPageToken)) {
1286
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1287
+ }
1288
+ seenTokens.add(nextPageToken);
1289
+ }
1290
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1291
+ }
1292
+ normalizeIssue(value, site, identity, filter) {
1293
+ if (!(0, vendor_shared_1.isRecord)(value) || !(0, vendor_shared_1.isRecord)(value.fields)) {
1294
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1295
+ }
1296
+ const fields = value.fields;
1297
+ if (!(0, vendor_shared_1.isRecord)(fields.status) ||
1298
+ !(0, vendor_shared_1.isRecord)(fields.status.statusCategory) ||
1299
+ !(0, vendor_shared_1.isRecord)(fields.assignee) ||
1300
+ !(0, vendor_shared_1.isRecord)(fields.project)) {
1301
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1302
+ }
1303
+ if (this.validate.requiredString(fields.assignee.accountId) !== identity.accountId) {
1304
+ return null;
1305
+ }
1306
+ const category = this.classifyStatus(this.validate.requiredString(fields.status.statusCategory.key).toLowerCase());
1307
+ const completedAt = this.optionalTimestamp(fields.resolutiondate);
1308
+ if (category === 'completed') {
1309
+ const completedTime = completedAt === null ? Number.NaN : Date.parse(completedAt);
1310
+ if (!filter.includeCompleted ||
1311
+ !Number.isFinite(completedTime) ||
1312
+ completedTime < filter.cutoff ||
1313
+ completedTime > filter.now) {
1314
+ return null;
1315
+ }
1316
+ }
1317
+ const remoteId = this.validate.requiredString(value.key);
1318
+ const projectId = this.validate.requiredIdentifier(fields.project.id);
1319
+ const projectName = this.validate.requiredString(fields.project.name);
1320
+ const workArea = {
1321
+ remoteId: projectId,
1322
+ scopeKey: site.hostname,
1323
+ name: projectName,
1324
+ kind: 'project',
1325
+ description: null,
1326
+ assignedTaskCount: 0,
1327
+ hierarchy: [
1328
+ { kind: 'workspace', remoteId: site.hostname, name: site.hostname },
1329
+ { kind: 'project', remoteId: projectId, name: projectName },
1330
+ ],
1331
+ workflow: { isOverridden: false, columns: [] },
1332
+ refresh: {
1333
+ state: 'fresh',
1334
+ refreshedAt: new Date(filter.now).toISOString(),
1335
+ retryable: false,
1336
+ retryAt: null,
1337
+ },
1338
+ };
1339
+ return {
1340
+ workArea,
1341
+ task: {
1342
+ remoteId,
1343
+ parentRemoteTaskId: this.normalizeIssueParent(fields),
1344
+ title: this.validate.requiredString(fields.summary),
1345
+ status: {
1346
+ remoteId: this.validate.requiredIdentifier(fields.status.id),
1347
+ name: this.validate.requiredString(fields.status.name),
1348
+ category,
1349
+ },
1350
+ updatedAt: this.requiredTimestamp(fields.updated),
1351
+ dueAt: this.optionalDueDate(fields.duedate),
1352
+ completedAt,
1353
+ webUrl: `${site.origin}/browse/${encodeURIComponent(remoteId)}`,
1354
+ },
1355
+ };
1356
+ }
1357
+ normalizeIssueParent(fields) {
1358
+ if (!(0, vendor_shared_1.isRecord)(fields.issuetype) || fields.issuetype.subtask !== true) {
1359
+ return null;
1360
+ }
1361
+ return (0, vendor_shared_1.isRecord)(fields.parent) ? this.optionalJiraKey(fields.parent) : null;
1362
+ }
1363
+ async discoverRelevantBoards(site, options, assignedTasks, now) {
1364
+ const projectKeys = [...new Set(assignedTasks.map(({ projectKey }) => projectKey))];
1365
+ const boards = await this.loadRelevantBoards(site, projectKeys);
1366
+ const assignedById = new Map(assignedTasks.map((assigned) => [assigned.item.task.remoteId, assigned]));
1367
+ const matchedIds = new Set();
1368
+ const workAreas = [];
1369
+ const tasks = [];
1370
+ const boardResults = await (0, vendor_shared_1.mapWithConcurrency)(boards, vendor_shared_1.VENDOR_DISCOVERY_CONCURRENCY, async (board) => {
1371
+ const boardIssueIds = await this.loadBoardIssueIds(site, board.id, options.includeCompleted);
1372
+ const boardTasks = [];
1373
+ const seenBoardTasks = new Set();
1374
+ for (const remoteId of boardIssueIds) {
1375
+ const assigned = assignedById.get(remoteId);
1376
+ if (!assigned || seenBoardTasks.has(remoteId)) {
1377
+ continue;
1378
+ }
1379
+ seenBoardTasks.add(remoteId);
1380
+ boardTasks.push(assigned);
1381
+ }
1382
+ if (boardTasks.length === 0) {
1383
+ return null;
1384
+ }
1385
+ const workArea = await this.loadBoardMetadata(site, options, board, boardTasks.length, now);
1386
+ return { workArea, boardTasks };
1387
+ });
1388
+ for (const result of boardResults) {
1389
+ if (!result) {
1390
+ continue;
1391
+ }
1392
+ workAreas.push(result.workArea);
1393
+ for (const assigned of result.boardTasks) {
1394
+ matchedIds.add(assigned.item.task.remoteId);
1395
+ tasks.push({ workArea: result.workArea, task: assigned.item.task });
1396
+ }
1397
+ }
1398
+ const unmatched = assignedTasks.filter(({ item }) => !matchedIds.has(item.task.remoteId));
1399
+ if (unmatched.length > 0) {
1400
+ const workArea = this.otherAssignedWorkArea(site, unmatched.length, now);
1401
+ workAreas.push(workArea);
1402
+ tasks.push(...unmatched.map(({ item }) => ({ workArea, task: item.task })));
1403
+ }
1404
+ return { workAreas, tasks };
1405
+ }
1406
+ async loadRelevantBoards(site, projectKeys) {
1407
+ const boardPages = await (0, vendor_shared_1.mapWithConcurrency)(projectKeys, vendor_shared_1.VENDOR_DISCOVERY_CONCURRENCY, (projectKey) => this.loadProjectBoards(site, projectKey));
1408
+ const boards = new Map();
1409
+ for (const board of boardPages.flat()) {
1410
+ if (!boards.has(board.id)) {
1411
+ boards.set(board.id, board);
1412
+ }
1413
+ }
1414
+ return [...boards.values()];
1415
+ }
1416
+ async loadProjectBoards(site, projectKey) {
1417
+ const boards = [];
1418
+ let startAt = 0;
1419
+ for (let page = 0; page < JIRA_MAX_SEARCH_PAGES; page += 1) {
1420
+ const url = new URL(`${site.origin}/rest/agile/1.0/board`);
1421
+ url.searchParams.set('projectKeyOrId', projectKey);
1422
+ url.searchParams.set('startAt', String(startAt));
1423
+ url.searchParams.set('maxResults', String(JIRA_SEARCH_PAGE_SIZE));
1424
+ const payload = await this.requestJson(site, `${url.pathname}${url.search}`);
1425
+ if (!(0, vendor_shared_1.isRecord)(payload) ||
1426
+ !Array.isArray(payload.values) ||
1427
+ typeof payload.isLast !== 'boolean') {
1428
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1429
+ }
1430
+ for (const value of payload.values) {
1431
+ if (!(0, vendor_shared_1.isRecord)(value)) {
1432
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1433
+ }
1434
+ boards.push({
1435
+ id: this.requiredBoardId(value.id),
1436
+ name: this.validate.requiredString(value.name),
1437
+ });
1438
+ }
1439
+ if (payload.isLast) {
1440
+ return boards;
1441
+ }
1442
+ if (payload.values.length === 0) {
1443
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1444
+ }
1445
+ startAt += payload.values.length;
1446
+ }
1447
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1448
+ }
1449
+ async loadBoardIssueIds(site, boardId, includeCompleted) {
1450
+ const issueIds = [];
1451
+ const seenTokens = new Set();
1452
+ let nextPageToken;
1453
+ for (let page = 0; page < JIRA_MAX_SEARCH_PAGES; page += 1) {
1454
+ const url = new URL(`${site.origin}/rest/software/1.0/board/${encodeURIComponent(boardId)}/issue`);
1455
+ url.searchParams.set('maxResults', String(JIRA_SEARCH_PAGE_SIZE));
1456
+ url.searchParams.set('fields', 'key');
1457
+ url.searchParams.set('jql', this.assignedJql(includeCompleted));
1458
+ if (nextPageToken !== undefined) {
1459
+ url.searchParams.set('nextPageToken', nextPageToken);
1460
+ }
1461
+ const payload = await this.requestJson(site, `${url.pathname}${url.search}`);
1462
+ if (!(0, vendor_shared_1.isRecord)(payload) || !Array.isArray(payload.issues)) {
1463
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1464
+ }
1465
+ for (const issue of payload.issues) {
1466
+ if (!(0, vendor_shared_1.isRecord)(issue)) {
1467
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1468
+ }
1469
+ issueIds.push(this.validate.requiredString(issue.key));
1470
+ }
1471
+ if (payload.nextPageToken === undefined || payload.nextPageToken === null) {
1472
+ return issueIds;
1473
+ }
1474
+ if (typeof payload.nextPageToken !== 'string' || !payload.nextPageToken.trim()) {
1475
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1476
+ }
1477
+ nextPageToken = payload.nextPageToken.trim();
1478
+ if (seenTokens.has(nextPageToken)) {
1479
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1480
+ }
1481
+ seenTokens.add(nextPageToken);
1482
+ }
1483
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1484
+ }
1485
+ async loadBoardMetadata(site, options, board, assignedTaskCount, now) {
1486
+ const cacheKey = JSON.stringify([
1487
+ options.connectionId,
1488
+ options.connectionGeneration,
1489
+ site.hostname,
1490
+ board.id,
1491
+ ]);
1492
+ const cached = this.boardMetadataCache.get(cacheKey);
1493
+ if (cached && now - cached.fetchedAt <= vendor_shared_1.WORK_AREA_METADATA_TTL_MS) {
1494
+ return (0, vendor_shared_1.materializeWorkArea)(cached.value, assignedTaskCount, {
1495
+ state: 'fresh',
1496
+ refreshedAt: new Date(cached.fetchedAt).toISOString(),
1497
+ retryable: false,
1498
+ retryAt: null,
1499
+ });
1500
+ }
1501
+ const base = this.baseBoardWorkArea(site, board);
1502
+ try {
1503
+ const configuration = await this.requestJson(site, `/rest/agile/1.0/board/${encodeURIComponent(board.id)}/configuration`);
1504
+ const metadataWithoutDescription = this.normalizeBoardConfiguration(configuration, base, board);
1505
+ const filterId = this.boardFilterId(configuration);
1506
+ const filter = await this.requestJson(site, `/rest/api/3/filter/${encodeURIComponent(filterId)}`);
1507
+ const metadata = {
1508
+ ...metadataWithoutDescription,
1509
+ description: this.filterDescription(filter),
1510
+ };
1511
+ this.boardMetadataCache.set(cacheKey, { value: metadata, fetchedAt: now });
1512
+ return (0, vendor_shared_1.materializeWorkArea)(metadata, assignedTaskCount, {
1513
+ state: 'fresh',
1514
+ refreshedAt: new Date(now).toISOString(),
1515
+ retryable: false,
1516
+ retryAt: null,
1517
+ });
1518
+ }
1519
+ catch (error) {
1520
+ if (!(error instanceof external_provider_errors_1.JiraProviderError)) {
1521
+ throw error;
1522
+ }
1523
+ if (cached) {
1524
+ return (0, vendor_shared_1.materializeWorkArea)(cached.value, assignedTaskCount, {
1525
+ state: 'stale',
1526
+ refreshedAt: new Date(cached.fetchedAt).toISOString(),
1527
+ retryable: true,
1528
+ retryAt: (0, vendor_shared_1.vendorRetryAt)(error),
1529
+ });
1530
+ }
1531
+ return {
1532
+ ...base,
1533
+ assignedTaskCount,
1534
+ refresh: {
1535
+ state: 'error',
1536
+ refreshedAt: null,
1537
+ retryable: true,
1538
+ retryAt: (0, vendor_shared_1.vendorRetryAt)(error),
1539
+ },
1540
+ };
1541
+ }
1542
+ }
1543
+ normalizeBoardConfiguration(value, base, board) {
1544
+ if (!(0, vendor_shared_1.isRecord)(value) ||
1545
+ this.requiredBoardId(value.id) !== board.id ||
1546
+ !(0, vendor_shared_1.isRecord)(value.columnConfig) ||
1547
+ !Array.isArray(value.columnConfig.columns)) {
1548
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1549
+ }
1550
+ const name = this.validate.requiredString(value.name);
1551
+ const columns = this.normalizeBoardColumns(value.columnConfig.columns);
1552
+ const hierarchy = [
1553
+ { kind: 'workspace', remoteId: base.scopeKey, name: base.scopeKey },
1554
+ ];
1555
+ if (value.location !== undefined && value.location !== null) {
1556
+ if (!(0, vendor_shared_1.isRecord)(value.location)) {
1557
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1558
+ }
1559
+ if (value.location.type === 'project') {
1560
+ hierarchy.push({
1561
+ kind: 'project',
1562
+ remoteId: this.validate.requiredIdentifier(value.location.id),
1563
+ name: this.validate.requiredString(value.location.name),
1564
+ });
1565
+ }
1566
+ }
1567
+ return {
1568
+ ...base,
1569
+ name,
1570
+ hierarchy,
1571
+ workflow: { isOverridden: true, columns },
1572
+ };
1573
+ }
1574
+ normalizeBoardColumns(values) {
1575
+ const mapped = values.map((value, position) => {
1576
+ if (!(0, vendor_shared_1.isRecord)(value) || !Array.isArray(value.statuses)) {
1577
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1578
+ }
1579
+ return {
1580
+ name: this.validate.requiredString(value.name),
1581
+ remoteStatusIds: value.statuses.map((status) => {
1582
+ if (!(0, vendor_shared_1.isRecord)(status)) {
1583
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1584
+ }
1585
+ return this.validate.requiredIdentifier(status.id);
1586
+ }),
1587
+ position,
1588
+ };
1589
+ });
1590
+ let lastMappedPosition = -1;
1591
+ for (const column of mapped) {
1592
+ if (column.remoteStatusIds.length > 0) {
1593
+ lastMappedPosition = column.position;
1594
+ }
1595
+ }
1596
+ return mapped.map((column) => {
1597
+ const category = column.remoteStatusIds.length === 0
1598
+ ? 'unknown'
1599
+ : column.position === lastMappedPosition
1600
+ ? 'completed'
1601
+ : 'active';
1602
+ return {
1603
+ remoteId: null,
1604
+ remoteStatusIds: column.remoteStatusIds,
1605
+ name: column.name,
1606
+ color: this.statusColor(category),
1607
+ category,
1608
+ position: column.position,
1609
+ };
1610
+ });
1611
+ }
1612
+ boardFilterId(value) {
1613
+ if (!(0, vendor_shared_1.isRecord)(value) || !(0, vendor_shared_1.isRecord)(value.filter)) {
1614
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1615
+ }
1616
+ return this.validate.requiredIdentifier(value.filter.id);
1617
+ }
1618
+ filterDescription(value) {
1619
+ if (!(0, vendor_shared_1.isRecord)(value)) {
1620
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1621
+ }
1622
+ if (value.description === undefined || value.description === null || value.description === '') {
1623
+ return null;
1624
+ }
1625
+ if (typeof value.description !== 'string') {
1626
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1627
+ }
1628
+ return value.description;
1629
+ }
1630
+ baseBoardWorkArea(site, board) {
1631
+ return {
1632
+ remoteId: board.id,
1633
+ scopeKey: site.hostname,
1634
+ name: board.name,
1635
+ kind: 'board',
1636
+ description: null,
1637
+ hierarchy: [{ kind: 'workspace', remoteId: site.hostname, name: site.hostname }],
1638
+ workflow: { isOverridden: false, columns: [] },
1639
+ };
1640
+ }
1641
+ otherAssignedWorkArea(site, count, now) {
1642
+ return {
1643
+ remoteId: OTHER_ASSIGNED_WORK_AREA_ID,
1644
+ scopeKey: site.hostname,
1645
+ name: 'Other assigned issues',
1646
+ kind: 'board',
1647
+ description: null,
1648
+ assignedTaskCount: count,
1649
+ hierarchy: [{ kind: 'workspace', remoteId: site.hostname, name: site.hostname }],
1650
+ workflow: { isOverridden: false, columns: [] },
1651
+ refresh: {
1652
+ state: 'fresh',
1653
+ refreshedAt: new Date(now).toISOString(),
1654
+ retryable: false,
1655
+ retryAt: null,
1656
+ },
1657
+ };
1658
+ }
1659
+ issueProjectKey(value) {
1660
+ if (!(0, vendor_shared_1.isRecord)(value) || !(0, vendor_shared_1.isRecord)(value.fields) || !(0, vendor_shared_1.isRecord)(value.fields.project)) {
1661
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1662
+ }
1663
+ return this.validate.requiredString(value.fields.project.key);
1664
+ }
1665
+ assignedJql(includeCompleted) {
1666
+ return includeCompleted
1667
+ ? 'assignee = currentUser() AND (statusCategory != Done OR resolutiondate >= -30d) ORDER BY updated DESC'
1668
+ : 'assignee = currentUser() AND statusCategory != Done ORDER BY updated DESC';
1669
+ }
1670
+ requiredBoardId(value) {
1671
+ if (typeof value !== 'number' || !Number.isSafeInteger(value) || value <= 0) {
1672
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1673
+ }
1674
+ return String(value);
1675
+ }
1676
+ resolveSite(credentials) {
1677
+ if (credentials.provider !== this.provider ||
1678
+ !credentials.email.trim() ||
1679
+ !credentials.token.trim()) {
1680
+ throw new external_provider_errors_1.JiraProviderError('request_rejected');
1681
+ }
1682
+ try {
1683
+ if (!JIRA_SITE_URL.test(credentials.siteUrl)) {
1684
+ throw new Error('invalid Jira site URL');
1685
+ }
1686
+ const url = new URL(credentials.siteUrl);
1687
+ const hostname = url.hostname.toLowerCase();
1688
+ if (url.protocol !== 'https:' ||
1689
+ url.username !== '' ||
1690
+ url.password !== '' ||
1691
+ url.port !== '' ||
1692
+ url.pathname !== '/' ||
1693
+ url.search !== '' ||
1694
+ url.hash !== '' ||
1695
+ !JIRA_TENANT_HOSTNAME.test(hostname)) {
1696
+ throw new Error('invalid Jira site URL');
1697
+ }
1698
+ return {
1699
+ origin: `https://${hostname}`,
1700
+ hostname,
1701
+ authorization: `Basic ${Buffer.from(`${credentials.email}:${credentials.token}`).toString('base64')}`,
1702
+ };
1703
+ }
1704
+ catch {
1705
+ throw new external_provider_errors_1.JiraProviderError('request_rejected');
1706
+ }
1707
+ }
1708
+ async requestJson(site, path, request = {}) {
1709
+ try {
1710
+ return await this.http.requestJson({
1711
+ url: `${site.origin}${path}`,
1712
+ allowedOrigins: [site.origin],
1713
+ ...request,
1714
+ headers: {
1715
+ accept: 'application/json',
1716
+ authorization: site.authorization,
1717
+ ...(request.body === undefined ? {} : { 'content-type': 'application/json' }),
1718
+ },
1719
+ });
1720
+ }
1721
+ catch (error) {
1722
+ throw (0, vendor_shared_1.mapVendorTransportError)(error, (reason, retryAt, dispatched) => new external_provider_errors_1.JiraProviderError(reason, retryAt, dispatched ? { dispatched: true } : {}));
1723
+ }
1724
+ }
1725
+ async requestNoContent(site, path, request) {
1726
+ try {
1727
+ await this.http.requestNoContent({
1728
+ url: `${site.origin}${path}`,
1729
+ allowedOrigins: [site.origin],
1730
+ ...request,
1731
+ headers: {
1732
+ accept: 'application/json',
1733
+ authorization: site.authorization,
1734
+ ...(request.body === undefined ? {} : { 'content-type': 'application/json' }),
1735
+ },
1736
+ });
1737
+ }
1738
+ catch (error) {
1739
+ throw (0, vendor_shared_1.mapVendorTransportError)(error, (reason, retryAt, dispatched) => new external_provider_errors_1.JiraProviderError(reason, retryAt, dispatched ? { dispatched: true } : {}));
1740
+ }
1741
+ }
1742
+ classifyStatus(key) {
1743
+ if (key === 'done') {
1744
+ return 'completed';
1745
+ }
1746
+ if (key === 'new' || key === 'indeterminate') {
1747
+ return 'active';
1748
+ }
1749
+ return 'unknown';
1750
+ }
1751
+ requiredTimestamp(value) {
1752
+ const timestamp = this.optionalTimestamp(value);
1753
+ if (timestamp === null) {
1754
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1755
+ }
1756
+ return timestamp;
1757
+ }
1758
+ optionalTimestamp(value) {
1759
+ if (value === null || value === undefined) {
1760
+ return null;
1761
+ }
1762
+ if (typeof value !== 'string' || !value.trim()) {
1763
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1764
+ }
1765
+ const timestamp = Date.parse(value);
1766
+ if (!Number.isFinite(timestamp)) {
1767
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1768
+ }
1769
+ return new Date(timestamp).toISOString();
1770
+ }
1771
+ optionalDueDate(value) {
1772
+ if (value === null || value === undefined) {
1773
+ return null;
1774
+ }
1775
+ if (typeof value !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(value)) {
1776
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1777
+ }
1778
+ const timestamp = Date.parse(`${value}T00:00:00.000Z`);
1779
+ if (!Number.isFinite(timestamp) || new Date(timestamp).toISOString().slice(0, 10) !== value) {
1780
+ throw new external_provider_errors_1.JiraProviderError('invalid_response');
1781
+ }
1782
+ return new Date(timestamp).toISOString();
1783
+ }
1784
+ };
1785
+ exports.JiraExternalTaskProvider = JiraExternalTaskProvider;
1786
+ exports.JiraExternalTaskProvider = JiraExternalTaskProvider = __decorate([
1787
+ (0, common_1.Injectable)(),
1788
+ __metadata("design:paramtypes", [safe_vendor_http_client_1.SafeVendorHttpClient])
1789
+ ], JiraExternalTaskProvider);
1790
+ //# sourceMappingURL=jira-external-task.provider.js.map