specrails-desktop 2.3.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (323) hide show
  1. package/client/dist/assets/ActivityFeedPage-DJJlZ3mF.js +1 -0
  2. package/client/dist/assets/AgentsPage-49JaEDjR.js +86 -0
  3. package/client/dist/assets/{AnalyticsPage-Dyyz1ht3.js → AnalyticsPage-BUd3gWYC.js} +1 -1
  4. package/client/dist/assets/{BarChart-CMdLa6Es.js → BarChart-HDe_YoUD.js} +2 -2
  5. package/client/dist/assets/CodePage-CqPPND47.js +2 -0
  6. package/client/dist/assets/{DesktopAnalyticsPage-CTNwb639.js → DesktopAnalyticsPage-CgvmSvF0.js} +1 -1
  7. package/client/dist/assets/DocsDialog-hHFd3Ejs.js +11 -0
  8. package/client/dist/assets/DocsPage-B4R1aksg.js +11 -0
  9. package/client/dist/assets/{ExportDropdown-DuoZcdYN.js → ExportDropdown-f4dwQjlT.js} +1 -1
  10. package/client/dist/assets/IntegrationsPage-CX2Ybxx0.js +3 -0
  11. package/client/dist/assets/JobDetailPage-DN2Jc8Ti.js +16 -0
  12. package/client/dist/assets/JobsPage-DmdpqijT.js +1 -0
  13. package/client/dist/assets/{cssMode-Cc6ozl-J.js → cssMode-DzNPAYFh.js} +1 -1
  14. package/client/dist/assets/{dist-js-H6hyhSuv.js → dist-js-COfIfLRE.js} +1 -1
  15. package/client/dist/assets/{dist-js-4UEGaKhD.js → dist-js-CvScGQU_.js} +1 -1
  16. package/client/dist/assets/{editor.main-CfXxHimg.js → editor.main-C7Rmw-hR.js} +2 -2
  17. package/client/dist/assets/{freemarker2-DP7J1gG3.js → freemarker2-Cszs4SVo.js} +1 -1
  18. package/client/dist/assets/{handlebars-BjRlucw6.js → handlebars-Dp7Lsuym.js} +1 -1
  19. package/client/dist/assets/{html-OumBQJ-U.js → html-BURidrEm.js} +1 -1
  20. package/client/dist/assets/{htmlMode-CStc3zXM.js → htmlMode--k5M7GjZ.js} +1 -1
  21. package/client/dist/assets/index-DBpvYrDK.css +2 -0
  22. package/client/dist/assets/index-DGIXKRHE.js +142 -0
  23. package/client/dist/assets/{integrations-Cublz3m6.js → integrations-2C7MkGT0.js} +1 -1
  24. package/client/dist/assets/{integrations-HIlUxXVs.js → integrations-BDC670cg.js} +1 -1
  25. package/client/dist/assets/integrations-BqUmRUef.js +1 -0
  26. package/client/dist/assets/{integrations-DmQYCUvN.js → integrations-C2jQtv-s.js} +1 -1
  27. package/client/dist/assets/{integrations-DRdbki5W.js → integrations-CB98NeH5.js} +1 -1
  28. package/client/dist/assets/{integrations-C3p12Ms6.js → integrations-CX4p_bij.js} +1 -1
  29. package/client/dist/assets/{integrations-DaC4SzzL.js → integrations-_SuVeQIG.js} +1 -1
  30. package/client/dist/assets/{integrations-Cr6hH7XR.js → integrations-eQPHAYsE.js} +1 -1
  31. package/client/dist/assets/{javascript-CMk--e7g.js → javascript-kJQz__44.js} +1 -1
  32. package/client/dist/assets/jira-C-ATCti0.js +1 -0
  33. package/client/dist/assets/jira-CmVfRM-b.js +1 -0
  34. package/client/dist/assets/jira-D7bkKAX8.js +1 -0
  35. package/client/dist/assets/jira-DKImM1YH.js +1 -0
  36. package/client/dist/assets/jira-DOw8bkIR.js +1 -0
  37. package/client/dist/assets/jira-DlA-wGp-.js +1 -0
  38. package/client/dist/assets/jira-Fob8EGxN.js +1 -0
  39. package/client/dist/assets/jira-xZA2lixb.js +1 -0
  40. package/client/dist/assets/{jsonMode-C2h3ZcjZ.js → jsonMode-v5JYPpnz.js} +1 -1
  41. package/client/dist/assets/{lib-Cs5FrUJI.js → lib-Bro9Z0gp.js} +1 -1
  42. package/client/dist/assets/{liquid-mI3KJrBE.js → liquid-Dl9I6gWt.js} +1 -1
  43. package/client/dist/assets/{lspLanguageFeatures-DU09ggWi.js → lspLanguageFeatures-CPlEe0NK.js} +1 -1
  44. package/client/dist/assets/{mdx-C41VDTR_.js → mdx-Byl7TtzQ.js} +1 -1
  45. package/client/dist/assets/{monaco.contribution-CPObAXMC.js → monaco.contribution-YMAkHQcQ.js} +2 -2
  46. package/client/dist/assets/{python-Y27rKQtk.js → python-jWQwT6j2.js} +1 -1
  47. package/client/dist/assets/{razor-Cd5-q9Bp.js → razor-BWS3sP-E.js} +1 -1
  48. package/client/dist/assets/setup-C0dzw8j4.js +1 -0
  49. package/client/dist/assets/setup-C1IA-9YS.js +1 -0
  50. package/client/dist/assets/setup-CpfjaNut.js +1 -0
  51. package/client/dist/assets/setup-D3rNZA9A.js +1 -0
  52. package/client/dist/assets/setup-UD2aanGs.js +1 -0
  53. package/client/dist/assets/setup-WP6WOYQh.js +1 -0
  54. package/client/dist/assets/setup-gzLG8T6F.js +1 -0
  55. package/client/dist/assets/setup-pjgmYHx6.js +1 -0
  56. package/client/dist/assets/specs-4lA_u79w.js +1 -0
  57. package/client/dist/assets/{specs-D2FzlLn9.js → specs-BHjxcjOf.js} +1 -1
  58. package/client/dist/assets/{specs-CZ1PsXsC.js → specs-CXNQzPk9.js} +1 -1
  59. package/client/dist/assets/{specs-Dyc5hYeE.js → specs-DFnc2Huj.js} +1 -1
  60. package/client/dist/assets/{specs-BFfu3u-a.js → specs-DZCLH2-l.js} +1 -1
  61. package/client/dist/assets/{specs-B__C8-8a.js → specs-DgmyAE3N.js} +1 -1
  62. package/client/dist/assets/{specs-DaUTrNF9.js → specs-DicWhvwi.js} +1 -1
  63. package/client/dist/assets/{specs-k0PyLDVt.js → specs-dkro6lSM.js} +1 -1
  64. package/client/dist/assets/{tsMode-B0y_xEci.js → tsMode-BbOGOuSV.js} +1 -1
  65. package/client/dist/assets/{typescript-BzK0OgwW.js → typescript-eBtFQJLs.js} +1 -1
  66. package/client/dist/assets/{useProjectCache-BZWYV-w-.js → useProjectCache-D9juBhsO.js} +1 -1
  67. package/client/dist/assets/{workers-rt--R2Qy.js → workers-BvicOoDf.js} +1 -1
  68. package/client/dist/assets/{xml-eX9QXAmI.js → xml-BJepAPyM.js} +1 -1
  69. package/client/dist/assets/{yaml-fcsNkpOt.js → yaml-DabgV-eA.js} +1 -1
  70. package/client/dist/index.html +12 -11
  71. package/docs/jira-integration-plan.md +321 -0
  72. package/package.json +1 -1
  73. package/server/dist/db.js +80 -0
  74. package/server/dist/feature-flags.js +11 -0
  75. package/server/dist/jira/jira-adf.js +113 -0
  76. package/server/dist/jira/jira-backlog-config.js +58 -0
  77. package/server/dist/jira/jira-client.js +279 -0
  78. package/server/dist/jira/jira-credential-store.js +103 -0
  79. package/server/dist/jira/jira-db.js +341 -0
  80. package/server/dist/jira/jira-issue-fields.js +428 -0
  81. package/server/dist/jira/jira-materializer.js +250 -0
  82. package/server/dist/jira/jira-status-resolver.js +211 -0
  83. package/server/dist/jira/jira-sync-manager.js +1014 -0
  84. package/server/dist/jira/types.js +9 -0
  85. package/server/dist/jira-router.js +304 -0
  86. package/server/dist/project-registry.js +43 -1
  87. package/server/dist/project-router-tickets.js +49 -1
  88. package/server/dist/project-router.js +4 -0
  89. package/server/dist/rails-router.js +12 -0
  90. package/client/dist/assets/ActivityFeedPage-3Veccrvk.js +0 -1
  91. package/client/dist/assets/AgentsPage-2mFPghP4.js +0 -86
  92. package/client/dist/assets/CodePage-D7Xwjhut.js +0 -2
  93. package/client/dist/assets/DocsDialog-D8yoyZDD.js +0 -11
  94. package/client/dist/assets/DocsPage-CeO-fAxy.js +0 -11
  95. package/client/dist/assets/IntegrationsPage-iIZ0UEzf.js +0 -3
  96. package/client/dist/assets/JobDetailPage-DgJHAH2m.js +0 -16
  97. package/client/dist/assets/JobsPage-Bv_RpRAE.js +0 -1
  98. package/client/dist/assets/index-CGHKpC-N.js +0 -142
  99. package/client/dist/assets/index-D17R4Cjc.css +0 -2
  100. package/client/dist/assets/integrations-D28q1kF6.js +0 -1
  101. package/client/dist/assets/setup--FMCsnQS.js +0 -1
  102. package/client/dist/assets/setup-B19ZpBNi.js +0 -1
  103. package/client/dist/assets/setup-BZPmkjSN.js +0 -1
  104. package/client/dist/assets/setup-BqYA02rS.js +0 -1
  105. package/client/dist/assets/setup-ChKQDHN9.js +0 -1
  106. package/client/dist/assets/setup-D2n9jMfM.js +0 -1
  107. package/client/dist/assets/setup-P3r6YP1D.js +0 -1
  108. package/client/dist/assets/setup-fnfEbwlv.js +0 -1
  109. package/client/dist/assets/specs-cKEh2LXt.js +0 -1
  110. /package/client/dist/assets/{abap-Bw6f2wDG.js → abap-s65oMlhi.js} +0 -0
  111. /package/client/dist/assets/{activity-BdrPln96.js → activity-BqqwnH_h.js} +0 -0
  112. /package/client/dist/assets/{activity-BEIp_Y1A.js → activity-C8qqEIoP.js} +0 -0
  113. /package/client/dist/assets/{activity-CpkRS8Sx.js → activity-CZVM4nlJ.js} +0 -0
  114. /package/client/dist/assets/{activity-DOUVEjJi.js → activity-Cyy07Tgo.js} +0 -0
  115. /package/client/dist/assets/{activity-DRwkql_y.js → activity-DlbWCa4y.js} +0 -0
  116. /package/client/dist/assets/{activity-DKCpESPt.js → activity-Dwq0heud.js} +0 -0
  117. /package/client/dist/assets/{activity-DcDQ7tjw.js → activity-qFTcMyW9.js} +0 -0
  118. /package/client/dist/assets/{addon-image-3WCl5Vhd.js → addon-image-CpF0L0jM.js} +0 -0
  119. /package/client/dist/assets/{addon-ligatures-C5OdliKs.js → addon-ligatures-hXysGZrA.js} +0 -0
  120. /package/client/dist/assets/{addon-webgl-BbX6pSjl.js → addon-webgl-Cn1slavz.js} +0 -0
  121. /package/client/dist/assets/{addspec-D33ocMxf.js → addspec-B1FTtI2a.js} +0 -0
  122. /package/client/dist/assets/{addspec-DFswZ0jK.js → addspec-BCT9vm_c.js} +0 -0
  123. /package/client/dist/assets/{addspec-DVZ15Jp8.js → addspec-DeDOztDr.js} +0 -0
  124. /package/client/dist/assets/{addspec-Fkv91Opc.js → addspec-DpRgmfmx.js} +0 -0
  125. /package/client/dist/assets/{addspec-BEeF5-zc.js → addspec-Dw-0Dg-4.js} +0 -0
  126. /package/client/dist/assets/{addspec-B5yl4Loj.js → addspec-rp496P_F.js} +0 -0
  127. /package/client/dist/assets/{addspec-DRE-jZv7.js → addspec-v8j6A7CD.js} +0 -0
  128. /package/client/dist/assets/{agents-DK-Dlc0i.js → agents-23iPejcA.js} +0 -0
  129. /package/client/dist/assets/{agents-Q6Ldfpxx.js → agents-BDx1RXcl.js} +0 -0
  130. /package/client/dist/assets/{agents-TeOSy-ax.js → agents-BFr3kUhK.js} +0 -0
  131. /package/client/dist/assets/{agents-Bm9rPqnt.js → agents-B_1L9xRg.js} +0 -0
  132. /package/client/dist/assets/{agents-1nCDWRmP.js → agents-BlPnx-mz.js} +0 -0
  133. /package/client/dist/assets/{agents-iTqjRajS.js → agents-DcxZHzNr.js} +0 -0
  134. /package/client/dist/assets/{agents-s87sMGzL.js → agents-G3shOewU.js} +0 -0
  135. /package/client/dist/assets/{agentstudio-B6Wb59E7.js → agentstudio-B-CMAQqy.js} +0 -0
  136. /package/client/dist/assets/{agentstudio-D3I62TLJ.js → agentstudio-Bk1eZcv4.js} +0 -0
  137. /package/client/dist/assets/{agentstudio-DuH9TogZ.js → agentstudio-ChxNuGAu.js} +0 -0
  138. /package/client/dist/assets/{agentstudio-Kw88_dUF.js → agentstudio-DNlme601.js} +0 -0
  139. /package/client/dist/assets/{agentstudio-BdidyBzZ.js → agentstudio-DpP9caEE.js} +0 -0
  140. /package/client/dist/assets/{agentstudio-BSnWLR63.js → agentstudio-Y3G0ddJ2.js} +0 -0
  141. /package/client/dist/assets/{agentstudio-BADhZ41e.js → agentstudio-kk9RB7Se.js} +0 -0
  142. /package/client/dist/assets/{aiedit-DJMny-D5.js → aiedit-5ETerMK1.js} +0 -0
  143. /package/client/dist/assets/{aiedit-D2ji6Qy0.js → aiedit-BBCrOpHq.js} +0 -0
  144. /package/client/dist/assets/{aiedit-DAhZTvtk.js → aiedit-BMtcGYNE.js} +0 -0
  145. /package/client/dist/assets/{aiedit-DvrcbwGv.js → aiedit-D9ddlgkM.js} +0 -0
  146. /package/client/dist/assets/{aiedit-WBSjT_C1.js → aiedit-De0SOH6S.js} +0 -0
  147. /package/client/dist/assets/{aiedit-BWxHGsYA.js → aiedit-DrfzQroF.js} +0 -0
  148. /package/client/dist/assets/{aiedit-DOcxERkU.js → aiedit-fMltW101.js} +0 -0
  149. /package/client/dist/assets/{analytics-C9Zc-rkM.js → analytics-BeTyviO8.js} +0 -0
  150. /package/client/dist/assets/{analytics-CrPCZRJ-.js → analytics-C4eEO260.js} +0 -0
  151. /package/client/dist/assets/{analytics-CYj0tfj7.js → analytics-C67cIA1b.js} +0 -0
  152. /package/client/dist/assets/{analytics-C6EzgtdE.js → analytics-CAguvW28.js} +0 -0
  153. /package/client/dist/assets/{analytics-CVx3YOc0.js → analytics-DBtt8Mgk.js} +0 -0
  154. /package/client/dist/assets/{analytics-CnY4kNG3.js → analytics-DUPtODxX.js} +0 -0
  155. /package/client/dist/assets/{analytics-BIdr0YfL.js → analytics-YIpQvPAc.js} +0 -0
  156. /package/client/dist/assets/{apex-Cw8_REBo.js → apex-BLUBIldB.js} +0 -0
  157. /package/client/dist/assets/{attachments-DYHGA2Dj.js → attachments-CCWasu-P.js} +0 -0
  158. /package/client/dist/assets/{attachments-Dd92KpUH.js → attachments-CHaDUfjB.js} +0 -0
  159. /package/client/dist/assets/{attachments-DzdU6DV6.js → attachments-CVSAbGNl.js} +0 -0
  160. /package/client/dist/assets/{attachments-Bcf6BG6V.js → attachments-Chg5poG1.js} +0 -0
  161. /package/client/dist/assets/{attachments-BW4L3l2L.js → attachments-DazTVJbH.js} +0 -0
  162. /package/client/dist/assets/{attachments-COcrGRFz.js → attachments-Dn-JImAK.js} +0 -0
  163. /package/client/dist/assets/{attachments-Bke8sCU4.js → attachments-LDA9kp2X.js} +0 -0
  164. /package/client/dist/assets/{azcli-Cz6HAoOw.js → azcli-DuWxh9mO.js} +0 -0
  165. /package/client/dist/assets/{bat-CcJ-xyqL.js → bat-UKoTejQm.js} +0 -0
  166. /package/client/dist/assets/{bicep-z1WDCKYz.js → bicep-4sTT4B3D.js} +0 -0
  167. /package/client/dist/assets/{browser-DGITz3fC.js → browser-BDd1dbFa.js} +0 -0
  168. /package/client/dist/assets/{browser-JsAIGCEW.js → browser-BWSgbfdX.js} +0 -0
  169. /package/client/dist/assets/{browser-M5-rbPlw.js → browser-D2Y_UAKA.js} +0 -0
  170. /package/client/dist/assets/{browser-BlYF4OOq.js → browser-DH9SGVfM.js} +0 -0
  171. /package/client/dist/assets/{browser-Bc-YdlVg.js → browser-DWOVYMlg.js} +0 -0
  172. /package/client/dist/assets/{browser-CT-ReZGt.js → browser-Dxc_VIRK.js} +0 -0
  173. /package/client/dist/assets/{browser-5ErDlJoR.js → browser-lTQwcDCI.js} +0 -0
  174. /package/client/dist/assets/{cameligo-BRewOpfa.js → cameligo-CAAryRYO.js} +0 -0
  175. /package/client/dist/assets/{chat-DwUm6W9z.js → chat-BO9MvVID.js} +0 -0
  176. /package/client/dist/assets/{chat-BEGuC03z.js → chat-CPgmgZOj.js} +0 -0
  177. /package/client/dist/assets/{chat-CboQguCi.js → chat-CUrG1eVg.js} +0 -0
  178. /package/client/dist/assets/{chat-DRCa9pOt.js → chat-CvOOKB2s.js} +0 -0
  179. /package/client/dist/assets/{chat-BEW60P_u.js → chat-DIh3hr6y.js} +0 -0
  180. /package/client/dist/assets/{chat-yoXwguQu.js → chat-UVVZqA57.js} +0 -0
  181. /package/client/dist/assets/{chat-BQNMD0PL.js → chat-mPn3UlMl.js} +0 -0
  182. /package/client/dist/assets/{clojure-DBjRWN6g.js → clojure-BlMERO1w.js} +0 -0
  183. /package/client/dist/assets/{clsx-DnqN-uhr.js → clsx-CnH-HMk3.js} +0 -0
  184. /package/client/dist/assets/{code-zCwBt3Uu.js → code-BwIz8agY.js} +0 -0
  185. /package/client/dist/assets/{code-g0qFMzyg.js → code-CD7yNSK0.js} +0 -0
  186. /package/client/dist/assets/{code-DDU0CRS0.js → code-CDFlxUFC.js} +0 -0
  187. /package/client/dist/assets/{code-L35Loak_.js → code-Cp3Fdng-.js} +0 -0
  188. /package/client/dist/assets/{code-D1z-YDt-.js → code-D24e1Crx.js} +0 -0
  189. /package/client/dist/assets/{code-BtsmPQLV.js → code-DtZBQTi9.js} +0 -0
  190. /package/client/dist/assets/{code-Coa8f2Sh.js → code-nKa0fkm_.js} +0 -0
  191. /package/client/dist/assets/{coffee-Cfk_XHGR.js → coffee-Cj8D-Wl1.js} +0 -0
  192. /package/client/dist/assets/{commands-sqrqsxyE.js → commands-B-MVT-2F.js} +0 -0
  193. /package/client/dist/assets/{commands-UD1NzmwX.js → commands-B0yFTp7e.js} +0 -0
  194. /package/client/dist/assets/{commands-DLrvnPNg.js → commands-BR1kDkHQ.js} +0 -0
  195. /package/client/dist/assets/{commands-CJxCry-o.js → commands-Cb21pDlG.js} +0 -0
  196. /package/client/dist/assets/{commands-CfgY-_of.js → commands-DWgp-8W1.js} +0 -0
  197. /package/client/dist/assets/{commands-B772IyDa.js → commands-ddsl1V91.js} +0 -0
  198. /package/client/dist/assets/{commands-BDDp6xFG.js → commands-t4frzhB0.js} +0 -0
  199. /package/client/dist/assets/{common-Dmm1GhdD.js → common-5ilvMOcH.js} +0 -0
  200. /package/client/dist/assets/{common-DltqHaAe.js → common-B4sqsKp7.js} +0 -0
  201. /package/client/dist/assets/{common-GbpxfPG8.js → common-BKpVwUIf.js} +0 -0
  202. /package/client/dist/assets/{common-DeDELLZJ.js → common-BzEC3kJU.js} +0 -0
  203. /package/client/dist/assets/{common-DnjcgkPH.js → common-CALKUpYm.js} +0 -0
  204. /package/client/dist/assets/{common-Dard9UNH.js → common-CTEbWVZS.js} +0 -0
  205. /package/client/dist/assets/{common-DCr6VzJ7.js → common-DQiza2Xp.js} +0 -0
  206. /package/client/dist/assets/{cpp-BVob6BaP.js → cpp-BPfKnaj_.js} +0 -0
  207. /package/client/dist/assets/{csharp-C4fbRuOu.js → csharp-gX-x5uD6.js} +0 -0
  208. /package/client/dist/assets/{csp-DthFP_vT.js → csp-DKGVt8SM.js} +0 -0
  209. /package/client/dist/assets/{css-CGMH0hcW.js → css-CPMdnAVq.js} +0 -0
  210. /package/client/dist/assets/{cypher-Pnf68BRV.js → cypher-ClMDrj9S.js} +0 -0
  211. /package/client/dist/assets/{dart-PMMOtxZX.js → dart-C4zbzpVv.js} +0 -0
  212. /package/client/dist/assets/{dashboard-BZBADHSj.js → dashboard--Y6yzMlf.js} +0 -0
  213. /package/client/dist/assets/{dashboard-I19DXBxw.js → dashboard--a4-6oYE.js} +0 -0
  214. /package/client/dist/assets/{dashboard-CB6Le1yN.js → dashboard-BiJ3CDTG.js} +0 -0
  215. /package/client/dist/assets/{dashboard-B4ixDVk8.js → dashboard-CiXjk63Z.js} +0 -0
  216. /package/client/dist/assets/{dashboard-C1MfeUHs.js → dashboard-Cx5VjCea.js} +0 -0
  217. /package/client/dist/assets/{dashboard-C7SK6xu5.js → dashboard-D7jg25XR.js} +0 -0
  218. /package/client/dist/assets/{dashboard-CoTpMOBM.js → dashboard-DpGYK2s1.js} +0 -0
  219. /package/client/dist/assets/{dockerfile-di1nsJCc.js → dockerfile-D9xw73D1.js} +0 -0
  220. /package/client/dist/assets/{ecl-D_WVcB5M.js → ecl-gqO8tIR9.js} +0 -0
  221. /package/client/dist/assets/{editor.api2-XLGzZfbc.js → editor.api2-BPnIxMjz.js} +0 -0
  222. /package/client/dist/assets/{elixir-OAdJEMOn.js → elixir-DSAhVF3_.js} +0 -0
  223. /package/client/dist/assets/{explore-D2EFgt8J.js → explore-BE5UmlbD.js} +0 -0
  224. /package/client/dist/assets/{explore-BV5Xxlsn.js → explore-BmTaI8dX.js} +0 -0
  225. /package/client/dist/assets/{explore-A8Ltoblq.js → explore-CCwkqoWq.js} +0 -0
  226. /package/client/dist/assets/{explore-4mFpnrKU.js → explore-CMdEoPDx.js} +0 -0
  227. /package/client/dist/assets/{explore-C3FSE42C.js → explore-CtdCL4QU.js} +0 -0
  228. /package/client/dist/assets/{explore-B9A3iN2W.js → explore-DHjxSkqQ.js} +0 -0
  229. /package/client/dist/assets/{explore-BrBJvfjP.js → explore-__BeALjE.js} +0 -0
  230. /package/client/dist/assets/{flow9-D3QEZjgn.js → flow9-DeQCSPOd.js} +0 -0
  231. /package/client/dist/assets/{format-command-CwGuwzGA.js → format-command-2VNoNnMv.js} +0 -0
  232. /package/client/dist/assets/{fsharp-BF0k_8N8.js → fsharp-CEfaXL-S.js} +0 -0
  233. /package/client/dist/assets/{go-BAQO5Jsz.js → go-Xp1OkZCh.js} +0 -0
  234. /package/client/dist/assets/{graphql-hdFVFkiV.js → graphql-BwRXrUwe.js} +0 -0
  235. /package/client/dist/assets/{hcl-DWnl1o-X.js → hcl-u06DtVFk.js} +0 -0
  236. /package/client/dist/assets/{ini-CB-6OVu3.js → ini-AmeIpFND.js} +0 -0
  237. /package/client/dist/assets/{java-d1CmfiHX.js → java-CyDbRQjX.js} +0 -0
  238. /package/client/dist/assets/{jobs-DPPT6bV6.js → jobs-8viuHLDV.js} +0 -0
  239. /package/client/dist/assets/{jobs-3j3_npyo.js → jobs-AW2eB5D-.js} +0 -0
  240. /package/client/dist/assets/{jobs-2N3RXDAM.js → jobs-BSm89DL5.js} +0 -0
  241. /package/client/dist/assets/{jobs-BqEbCCxD.js → jobs-BZ3sQHjZ.js} +0 -0
  242. /package/client/dist/assets/{jobs-cHYInoau.js → jobs-Bd8AdOTb.js} +0 -0
  243. /package/client/dist/assets/{jobs-DRzjWI9u.js → jobs-CRtsq_u0.js} +0 -0
  244. /package/client/dist/assets/{jobs-2f6Hdc72.js → jobs-CSRwFQ6K.js} +0 -0
  245. /package/client/dist/assets/{jobs-vGfzIDQa.js → jobs-CbEl7WMI.js} +0 -0
  246. /package/client/dist/assets/{julia-Bgv08lKa.js → julia-BqialFRG.js} +0 -0
  247. /package/client/dist/assets/{kotlin-u98kaVTf.js → kotlin-Dzz8TWAt.js} +0 -0
  248. /package/client/dist/assets/{less-CjYwpgg5.js → less-DHRJD3TR.js} +0 -0
  249. /package/client/dist/assets/{lexon-YTjaAFBB.js → lexon-5Y3QgTmT.js} +0 -0
  250. /package/client/dist/assets/{lua-BzmkWv27.js → lua-sKvhfPn5.js} +0 -0
  251. /package/client/dist/assets/{m3-CFwk9fw0.js → m3-DWDVwkFG.js} +0 -0
  252. /package/client/dist/assets/{markdown-CR5iMpSZ.js → markdown-CD_aSBxW.js} +0 -0
  253. /package/client/dist/assets/{mips-CcEalc17.js → mips-687T03hg.js} +0 -0
  254. /package/client/dist/assets/{msdax-BQbkawnr.js → msdax-C1St-dIV.js} +0 -0
  255. /package/client/dist/assets/{mysql-GTlaaW_P.js → mysql-BG7r8oBS.js} +0 -0
  256. /package/client/dist/assets/{nav-C2YXcbZS.js → nav-B05EYB0b.js} +0 -0
  257. /package/client/dist/assets/{nav-D2bOGSEg.js → nav-BNGCq-0y.js} +0 -0
  258. /package/client/dist/assets/{nav-BEL3MTwK.js → nav-BRInPX8a.js} +0 -0
  259. /package/client/dist/assets/{nav-CtYwmMgu.js → nav-Bf87DRHD.js} +0 -0
  260. /package/client/dist/assets/{nav-iH1V5j6o.js → nav-BkVzzFpc.js} +0 -0
  261. /package/client/dist/assets/{nav-0fwkrgHt.js → nav-BzFLtS1W.js} +0 -0
  262. /package/client/dist/assets/{nav-ClzOE4mA.js → nav-DBDbQOYn.js} +0 -0
  263. /package/client/dist/assets/{nav-B_G-TJDW.js → nav-X9sVtUWC.js} +0 -0
  264. /package/client/dist/assets/{objective-c-Byu1T5if.js → objective-c-Ds1-m05L.js} +0 -0
  265. /package/client/dist/assets/{pascal-BrfzBfRm.js → pascal-BKK9FpIi.js} +0 -0
  266. /package/client/dist/assets/{pascaligo-BXXKFUeo.js → pascaligo-SRS3nwtO.js} +0 -0
  267. /package/client/dist/assets/{perl-B3OikKq-.js → perl-B2hTOlrF.js} +0 -0
  268. /package/client/dist/assets/{pgsql-CTsa0Acc.js → pgsql-DIQJYNpL.js} +0 -0
  269. /package/client/dist/assets/{php-DiQh3FUW.js → php-BEaVe8X2.js} +0 -0
  270. /package/client/dist/assets/{pla-92uH8Fzm.js → pla-oPLHpZ-Q.js} +0 -0
  271. /package/client/dist/assets/{postiats-BbeWkKUr.js → postiats-D_vzrAzD.js} +0 -0
  272. /package/client/dist/assets/{powerquery-DgDMzpsm.js → powerquery-BKG6w-FH.js} +0 -0
  273. /package/client/dist/assets/{powershell-BfdUUzaG.js → powershell-B3dLhDt4.js} +0 -0
  274. /package/client/dist/assets/{protobuf-BojW2ftW.js → protobuf-DC8SGjcl.js} +0 -0
  275. /package/client/dist/assets/{pug-BxqTg3IU.js → pug-D5E-4fI0.js} +0 -0
  276. /package/client/dist/assets/{qsharp-BX_A-MW9.js → qsharp-6vJAWv0x.js} +0 -0
  277. /package/client/dist/assets/{r-D9BMnxvJ.js → r-CDwsEcbM.js} +0 -0
  278. /package/client/dist/assets/{redis-5cJqEQJJ.js → redis-CuQbbESS.js} +0 -0
  279. /package/client/dist/assets/{redshift-d8BBqiwb.js → redshift-B9e1k-qI.js} +0 -0
  280. /package/client/dist/assets/{restructuredtext-C8a6yIcZ.js → restructuredtext-BiJ5IwaU.js} +0 -0
  281. /package/client/dist/assets/{ruby-egeh-6KX.js → ruby-B0UAHY9b.js} +0 -0
  282. /package/client/dist/assets/{rust-a3r9IInB.js → rust-Dg_spmFr.js} +0 -0
  283. /package/client/dist/assets/{sb-y8iRIDei.js → sb-DjU66I8Q.js} +0 -0
  284. /package/client/dist/assets/{scala-BPDK2AmK.js → scala-qvStIdfG.js} +0 -0
  285. /package/client/dist/assets/{scheme-BIWUEoOs.js → scheme-FstEk5Rh.js} +0 -0
  286. /package/client/dist/assets/{scss-CA-PSzwg.js → scss-w0U3rQLK.js} +0 -0
  287. /package/client/dist/assets/{settings-CTcwN9RE.js → settings-5tzo0Rn3.js} +0 -0
  288. /package/client/dist/assets/{settings-D_dujJZI.js → settings-BDAW3trC.js} +0 -0
  289. /package/client/dist/assets/{settings-Bg0A3zoS.js → settings-BEWv3VEu.js} +0 -0
  290. /package/client/dist/assets/{settings-BgPqg2nv.js → settings-BORg56um.js} +0 -0
  291. /package/client/dist/assets/{settings-BSze3_9q.js → settings-D3LurcR5.js} +0 -0
  292. /package/client/dist/assets/{settings-CSJ0ahZ8.js → settings-DcqWIEM6.js} +0 -0
  293. /package/client/dist/assets/{settings-DYIV89nV.js → settings-Dfz8QbZS.js} +0 -0
  294. /package/client/dist/assets/{settings-DDcfx_ca.js → settings-yMubjqYw.js} +0 -0
  295. /package/client/dist/assets/{shell--LiT1Bja.js → shell-DJ78wREd.js} +0 -0
  296. /package/client/dist/assets/{solidity-DdqZccZg.js → solidity-1aGIVsdX.js} +0 -0
  297. /package/client/dist/assets/{sophia-S6-YxNG_.js → sophia-40LqcGjB.js} +0 -0
  298. /package/client/dist/assets/{sparql-BSf5kMp2.js → sparql-Cz5dqG_g.js} +0 -0
  299. /package/client/dist/assets/{sql-D7KgjR8G.js → sql-64f62Ni4.js} +0 -0
  300. /package/client/dist/assets/{st-BnoDa-Ml.js → st-gJe2yG8J.js} +0 -0
  301. /package/client/dist/assets/{swift-DEUHTkUX.js → swift-C6ME22mv.js} +0 -0
  302. /package/client/dist/assets/{systemverilog-Tqb_KPnW.js → systemverilog-CEWz259w.js} +0 -0
  303. /package/client/dist/assets/{tcl-BmBFS2qq.js → tcl-CcLVIi3m.js} +0 -0
  304. /package/client/dist/assets/{terminal-Bje4ziIa.js → terminal-BYtreaaF.js} +0 -0
  305. /package/client/dist/assets/{terminal-CSONJOex.js → terminal-C0xx0SjA.js} +0 -0
  306. /package/client/dist/assets/{terminal-DeWzh6ys.js → terminal-CPpK58RC.js} +0 -0
  307. /package/client/dist/assets/{terminal-C2WYcFHF.js → terminal-CdxkpafL.js} +0 -0
  308. /package/client/dist/assets/{terminal-DEqzGtcr.js → terminal-Ciia0wh2.js} +0 -0
  309. /package/client/dist/assets/{terminal-80yDMgMF.js → terminal-DHIkiWcs.js} +0 -0
  310. /package/client/dist/assets/{terminal-lkZYR4wJ.js → terminal-DY42QANg.js} +0 -0
  311. /package/client/dist/assets/{terminal-YOlsJCQj.js → terminal-DoxtVdma.js} +0 -0
  312. /package/client/dist/assets/{tickets-DYvafSaY.js → tickets-0rM0lIXd.js} +0 -0
  313. /package/client/dist/assets/{tickets-DNOANUXr.js → tickets-1UIGf_oA.js} +0 -0
  314. /package/client/dist/assets/{tickets-DlpC_iTg.js → tickets-9kdPXInd.js} +0 -0
  315. /package/client/dist/assets/{tickets-CF2PYelu.js → tickets-C6pwZwt4.js} +0 -0
  316. /package/client/dist/assets/{tickets-CB7N30gm.js → tickets-DAjtxAVb.js} +0 -0
  317. /package/client/dist/assets/{tickets-DU1aqsbr.js → tickets-DNmXcAwu.js} +0 -0
  318. /package/client/dist/assets/{tickets-clefmXLv.js → tickets-n23kDqJT.js} +0 -0
  319. /package/client/dist/assets/{tickets-DucYgtdl.js → tickets-tGx5AR5b.js} +0 -0
  320. /package/client/dist/assets/{twig-BQV8igWC.js → twig-DvsO-WjW.js} +0 -0
  321. /package/client/dist/assets/{typespec-DlFroUGY.js → typespec-Brkt3IAA.js} +0 -0
  322. /package/client/dist/assets/{vb-BlrJpIMX.js → vb-r121Uzxt.js} +0 -0
  323. /package/client/dist/assets/{wgsl-BWgIc6FZ.js → wgsl-BRX8uYh4.js} +0 -0
@@ -0,0 +1,428 @@
1
+ "use strict";
2
+ // Pure, I/O-free renderer for the read-only "Jira details" panel: turns a raw
3
+ // issue field map (fields=*all) + /field metadata into an ordered list of
4
+ // populated, non-skipped { label, value, href? } rows. Also normalizes the
5
+ // dev-status (PR/branch/commit) payloads. NEVER throws — bad shapes are dropped.
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.SKIP_SYSTEM_FIELDS = void 0;
8
+ exports.indexFieldMeta = indexFieldMeta;
9
+ exports.resolveCustomFieldId = resolveCustomFieldId;
10
+ exports.humanizeKey = humanizeKey;
11
+ exports.formatIssueFields = formatIssueFields;
12
+ exports.normalizePullRequests = normalizePullRequests;
13
+ exports.normalizeBranches = normalizeBranches;
14
+ exports.normalizeRepositoryCommits = normalizeRepositoryCommits;
15
+ const jira_adf_1 = require("./jira-adf");
16
+ const jira_materializer_1 = require("./jira-materializer");
17
+ /** System fields the modal already shows OR that are noise — never rendered. */
18
+ exports.SKIP_SYSTEM_FIELDS = new Set([
19
+ // already shown by the modal / card
20
+ 'summary', 'description', 'status', 'priority', 'labels', 'assignee',
21
+ // identity / housekeeping
22
+ 'project', 'key', 'id', 'self', 'expand', 'thumbnail', 'lastViewed',
23
+ 'statuscategorychangedate', 'workratio', 'issuerestriction', 'issuekey',
24
+ // aggregates / progress (covered by timetracking)
25
+ 'aggregatetimespent', 'aggregatetimeestimate', 'aggregatetimeoriginalestimate',
26
+ 'aggregateprogress', 'progress', 'timeoriginalestimate', 'timeestimate', 'timespent',
27
+ // heavy / out-of-scope content blocks
28
+ 'comment', 'worklog', 'attachment',
29
+ // footer already shows local created/updated
30
+ 'created', 'updated',
31
+ ]);
32
+ // Custom-field schema.custom suffixes we recognise (never hardcode customfield_NNN).
33
+ const STORY_POINTS = [':float', ':jsw-story-points', ':story-point-estimate'];
34
+ const EPIC_LINK = 'com.pyxis.greenhopper.jira:gh-epic-link';
35
+ const EPIC_NAME = 'com.pyxis.greenhopper.jira:gh-epic-label';
36
+ const FLAGGED = [':gh-jira-flag', ':greenhopper-flagged-field'];
37
+ const TEAM = [':atlassian-team', ':rm-teams-custom-field-team'];
38
+ const SPRINT = 'com.pyxis.greenhopper.jira:gh-sprint';
39
+ const RANK = [':gh-lexo-rank', ':lexorank'];
40
+ /**
41
+ * Custom-field schema.custom substrings whose VALUE is an internal serialized
42
+ * blob (not human content) — must never render as a "field". The Development
43
+ * summary field is the prime case: its branches/PRs/commits are surfaced in the
44
+ * dedicated Development section via the dev-status API, not as a raw blob row.
45
+ */
46
+ const SKIP_CUSTOM_SCHEMA = [
47
+ 'jira-development-integration-plugin', // Development summary (devsummary / devsummarycf)
48
+ ':devsummary',
49
+ ':global-rank',
50
+ ':aozoraplugin', // misc internal aggregates seen on some instances
51
+ ];
52
+ function isSkippedCustomSchema(custom) {
53
+ return !!custom && SKIP_CUSTOM_SCHEMA.some((s) => custom.includes(s));
54
+ }
55
+ /** A populated string value that is actually a serialized object/map blob. */
56
+ function looksLikeSerializedBlob(value) {
57
+ return /^\s*[{[]/.test(value);
58
+ }
59
+ function indexFieldMeta(meta) {
60
+ const m = new Map();
61
+ for (const f of meta)
62
+ if (f && typeof f.id === 'string')
63
+ m.set(f.id, f);
64
+ return m;
65
+ }
66
+ /** Resolve a custom field id by schema.custom suffix OR exact name. null when absent. */
67
+ function resolveCustomFieldId(meta, opts) {
68
+ const names = (opts.names ?? []).map((n) => n.toLowerCase());
69
+ for (const f of meta.values()) {
70
+ const custom = f.schema?.custom;
71
+ if (custom && (opts.customEndsWith ?? []).some((s) => custom.endsWith(s) || custom === s))
72
+ return f.id;
73
+ if (f.name && names.includes(f.name.toLowerCase()))
74
+ return f.id;
75
+ }
76
+ return null;
77
+ }
78
+ function isPlainObject(v) {
79
+ return !!v && typeof v === 'object' && !Array.isArray(v);
80
+ }
81
+ /** Title-case a field key for the label fallback when /field metadata is missing. */
82
+ function humanizeKey(key) {
83
+ const spaced = key.replace(/([a-z])([A-Z])/g, '$1 $2').replace(/[_-]+/g, ' ').trim();
84
+ return spaced.charAt(0).toUpperCase() + spaced.slice(1);
85
+ }
86
+ const ISSUE_KEY_RE = /^[A-Z][A-Z0-9]+-\d+$/;
87
+ /** Render a scalar/object value by schema, or by shape when schema is absent. Returns
88
+ * the display string, or null when the value is empty/unpopulated. */
89
+ function scalarDisplay(raw, schemaType) {
90
+ if (raw == null)
91
+ return null;
92
+ switch (schemaType) {
93
+ case 'user':
94
+ return userName(raw);
95
+ case 'priority':
96
+ case 'status':
97
+ case 'resolution':
98
+ case 'issuetype':
99
+ case 'securitylevel':
100
+ case 'component':
101
+ case 'version':
102
+ return isPlainObject(raw) ? str(raw.name) : null;
103
+ case 'option':
104
+ case 'option-with-child': {
105
+ if (!isPlainObject(raw))
106
+ return null;
107
+ const base = str(raw.value) ?? str(raw.name);
108
+ const child = isPlainObject(raw.child) ? str(raw.child.value) ?? str(raw.child.name) : null;
109
+ return base ? (child ? `${base} / ${child}` : base) : null;
110
+ }
111
+ case 'votes':
112
+ return isPlainObject(raw) && typeof raw.votes === 'number' && raw.votes > 0 ? String(raw.votes) : null;
113
+ case 'watches':
114
+ return isPlainObject(raw) && typeof raw.watchCount === 'number' && raw.watchCount > 0 ? String(raw.watchCount) : null;
115
+ case 'timetracking':
116
+ return timetracking(raw);
117
+ case 'number':
118
+ return typeof raw === 'number' ? String(raw) : null;
119
+ case 'date':
120
+ case 'datetime':
121
+ return typeof raw === 'string' && raw.trim() ? raw : null;
122
+ case 'string':
123
+ return text(raw);
124
+ default:
125
+ return sniff(raw);
126
+ }
127
+ }
128
+ /** Shape-sniff a value with no usable schema (covers many custom fields). */
129
+ function sniff(raw) {
130
+ if (typeof raw === 'string')
131
+ return text(raw);
132
+ if (typeof raw === 'number')
133
+ return String(raw);
134
+ if (typeof raw === 'boolean')
135
+ return raw ? 'Yes' : null;
136
+ if (Array.isArray(raw)) {
137
+ const parts = raw.map((x) => sniff(x)).filter((x) => !!x);
138
+ return parts.length ? parts.join(', ') : null;
139
+ }
140
+ if (isPlainObject(raw)) {
141
+ return str(raw.value) ?? str(raw.name) ?? str(raw.displayName) ?? userName(raw);
142
+ }
143
+ return null;
144
+ }
145
+ function userName(raw) {
146
+ if (!isPlainObject(raw))
147
+ return null;
148
+ return str(raw.displayName) ?? str(raw.emailAddress) ?? str(raw.name);
149
+ }
150
+ function text(raw) {
151
+ if (typeof raw !== 'string') {
152
+ // ADF rich text (e.g. environment on Cloud).
153
+ if (isPlainObject(raw) && raw.type === 'doc') {
154
+ const t = (0, jira_adf_1.adfToText)(raw).trim();
155
+ return t || null;
156
+ }
157
+ return null;
158
+ }
159
+ const t = raw.trim();
160
+ return t || null;
161
+ }
162
+ function str(v) {
163
+ return typeof v === 'string' && v.trim() ? v.trim() : null;
164
+ }
165
+ function timetracking(raw) {
166
+ if (!isPlainObject(raw))
167
+ return null;
168
+ const lines = [];
169
+ if (str(raw.originalEstimate))
170
+ lines.push(`Original: ${raw.originalEstimate}`);
171
+ if (str(raw.remainingEstimate))
172
+ lines.push(`Remaining: ${raw.remainingEstimate}`);
173
+ if (str(raw.timeSpent))
174
+ lines.push(`Spent: ${raw.timeSpent}`);
175
+ return lines.length ? lines.join('\n') : null;
176
+ }
177
+ function arrayDisplay(raw, itemType) {
178
+ if (raw.length === 0)
179
+ return null;
180
+ let parts;
181
+ switch (itemType) {
182
+ case 'string':
183
+ parts = raw.filter((x) => typeof x === 'string' && x.trim().length > 0);
184
+ break;
185
+ case 'user':
186
+ parts = raw.map(userName).filter((x) => !!x);
187
+ break;
188
+ case 'component':
189
+ case 'version':
190
+ parts = raw.map((x) => (isPlainObject(x) ? str(x.name) : null)).filter((x) => !!x);
191
+ break;
192
+ case 'option':
193
+ parts = raw.map((x) => (isPlainObject(x) ? str(x.value) ?? str(x.name) : null)).filter((x) => !!x);
194
+ break;
195
+ default:
196
+ parts = raw.map((x) => sniff(x)).filter((x) => !!x);
197
+ }
198
+ return parts.length ? parts.join(', ') : null;
199
+ }
200
+ function labelFor(key, ctx) {
201
+ return ctx.meta.get(key)?.name ?? humanizeKey(key);
202
+ }
203
+ /** PURE. Ordered, populated, non-skipped detail rows for the read-only panel. */
204
+ function formatIssueFields(input) {
205
+ const f = input.fields ?? {};
206
+ const ctx = { meta: indexFieldMeta(input.fieldMeta ?? []), baseUrl: input.baseUrl };
207
+ const rows = [];
208
+ const handled = new Set();
209
+ const push = (key, schemaType, opts) => {
210
+ handled.add(key);
211
+ const value = scalarDisplay(f[key], schemaType);
212
+ if (value != null)
213
+ rows.push({ label: opts?.label ?? labelFor(key, ctx), value });
214
+ };
215
+ const pushArray = (key, itemType, opts) => {
216
+ handled.add(key);
217
+ const v = f[key];
218
+ if (!Array.isArray(v))
219
+ return;
220
+ const value = arrayDisplay(v, itemType);
221
+ if (value != null)
222
+ rows.push({ label: opts?.label ?? labelFor(key, ctx), value });
223
+ };
224
+ // ── Ordered system fields ──────────────────────────────────────────────────
225
+ push('issuetype', 'issuetype');
226
+ push('resolution', 'resolution');
227
+ push('reporter', 'user');
228
+ // creator: suppress when identical to reporter.
229
+ handled.add('creator');
230
+ {
231
+ const creator = userName(f.creator);
232
+ const reporter = userName(f.reporter);
233
+ if (creator && creator !== reporter)
234
+ rows.push({ label: labelFor('creator', ctx), value: creator });
235
+ }
236
+ pushArray('components', 'component');
237
+ pushArray('fixVersions', 'version');
238
+ pushArray('versions', 'version');
239
+ push('environment', 'string');
240
+ // parent: only when it is NOT an epic (epic parent is shown as jira_epic_key).
241
+ handled.add('parent');
242
+ {
243
+ const p = f.parent;
244
+ if (isPlainObject(p)) {
245
+ const itype = isPlainObject(p.fields) && isPlainObject(p.fields.issuetype) ? str(p.fields.issuetype.name) : null;
246
+ const key = str(p.key);
247
+ if (key && (itype ?? '').toLowerCase() !== 'epic') {
248
+ const summary = isPlainObject(p.fields) ? str(p.fields.summary) : null;
249
+ rows.push({ label: labelFor('parent', ctx), value: summary ? `${key} — ${summary}` : key, href: (0, jira_materializer_1.issueUrl)(ctx.baseUrl, key) });
250
+ }
251
+ }
252
+ }
253
+ // subtasks: headline count + one linked row each.
254
+ handled.add('subtasks');
255
+ if (Array.isArray(f.subtasks) && f.subtasks.length > 0) {
256
+ rows.push({ label: labelFor('subtasks', ctx), value: `${f.subtasks.length} sub-tasks` });
257
+ for (const s of f.subtasks) {
258
+ if (!isPlainObject(s))
259
+ continue;
260
+ const key = str(s.key);
261
+ if (!key)
262
+ continue;
263
+ const summary = isPlainObject(s.fields) ? str(s.fields.summary) : null;
264
+ rows.push({ label: key, value: summary ?? key, href: (0, jira_materializer_1.issueUrl)(ctx.baseUrl, key) });
265
+ }
266
+ }
267
+ // issuelinks: one row per linked issue, labelled by the relationship.
268
+ handled.add('issuelinks');
269
+ if (Array.isArray(f.issuelinks)) {
270
+ for (const link of f.issuelinks) {
271
+ if (!isPlainObject(link) || !isPlainObject(link.type))
272
+ continue;
273
+ const out = isPlainObject(link.outwardIssue) ? link.outwardIssue : null;
274
+ const inw = isPlainObject(link.inwardIssue) ? link.inwardIssue : null;
275
+ const rel = out ? str(link.type.outward) : str(link.type.inward);
276
+ const issue = out ?? inw;
277
+ if (!issue)
278
+ continue;
279
+ const key = str(issue.key);
280
+ if (!key)
281
+ continue;
282
+ const summary = isPlainObject(issue.fields) ? str(issue.fields.summary) : null;
283
+ rows.push({ label: rel ?? labelFor('issuelinks', ctx), value: summary ? `${key} — ${summary}` : key, href: (0, jira_materializer_1.issueUrl)(ctx.baseUrl, key) });
284
+ }
285
+ }
286
+ push('duedate', 'date');
287
+ push('resolutiondate', 'datetime');
288
+ push('security', 'securitylevel', { label: labelFor('security', ctx) });
289
+ push('votes', 'votes');
290
+ push('watches', 'watches');
291
+ push('timetracking', 'timetracking');
292
+ // ── Resolved custom fields ──────────────────────────────────────────────────
293
+ const sp = resolveCustomFieldId(ctx.meta, { customEndsWith: STORY_POINTS, names: ['Story Points', 'Story point estimate'] });
294
+ if (sp && typeof f[sp] === 'number') {
295
+ handled.add(sp);
296
+ rows.push({ label: ctx.meta.get(sp)?.name ?? 'Story Points', value: String(f[sp]) });
297
+ }
298
+ if (!input.alreadyShown.hasEpicKey) {
299
+ const el = resolveCustomFieldId(ctx.meta, { customEndsWith: [EPIC_LINK] });
300
+ if (el && str(f[el])) {
301
+ handled.add(el);
302
+ const key = str(f[el]);
303
+ rows.push({ label: ctx.meta.get(el)?.name ?? 'Epic Link', value: key, href: (0, jira_materializer_1.issueUrl)(ctx.baseUrl, key) });
304
+ }
305
+ const en = resolveCustomFieldId(ctx.meta, { customEndsWith: [EPIC_NAME] });
306
+ if (en && str(f[en])) {
307
+ handled.add(en);
308
+ rows.push({ label: ctx.meta.get(en)?.name ?? 'Epic Name', value: str(f[en]) });
309
+ }
310
+ }
311
+ const flag = resolveCustomFieldId(ctx.meta, { customEndsWith: FLAGGED, names: ['Flagged'] });
312
+ if (flag && Array.isArray(f[flag]) && f[flag].length > 0) {
313
+ handled.add(flag);
314
+ rows.push({ label: ctx.meta.get(flag)?.name ?? 'Flagged', value: 'Impediment' });
315
+ }
316
+ const team = resolveCustomFieldId(ctx.meta, { customEndsWith: TEAM, names: ['Team'] });
317
+ if (team) {
318
+ handled.add(team);
319
+ const v = f[team];
320
+ const name = isPlainObject(v) ? str(v.name) ?? str(v.title) ?? str(v.value) : str(v);
321
+ if (name)
322
+ rows.push({ label: ctx.meta.get(team)?.name ?? 'Team', value: name });
323
+ }
324
+ // Mark sprint + rank custom fields handled so the generic sweep skips them.
325
+ for (const id of [
326
+ resolveCustomFieldId(ctx.meta, { customEndsWith: [SPRINT] }),
327
+ resolveCustomFieldId(ctx.meta, { customEndsWith: RANK }),
328
+ ])
329
+ if (id)
330
+ handled.add(id);
331
+ // ── Generic sweep: any remaining populated custom field ─────────────────────
332
+ const sweep = [];
333
+ for (const key of Object.keys(f)) {
334
+ if (handled.has(key) || exports.SKIP_SYSTEM_FIELDS.has(key) || !key.startsWith('customfield_'))
335
+ continue;
336
+ const meta = ctx.meta.get(key);
337
+ const schema = meta?.schema;
338
+ // Skip internal serialized-blob custom fields (e.g. the Development summary —
339
+ // its PRs/branches/commits are surfaced in the Development section instead).
340
+ if (isSkippedCustomSchema(schema?.custom))
341
+ continue;
342
+ const value = Array.isArray(f[key]) ? arrayDisplay(f[key], schema?.items)
343
+ : scalarDisplay(f[key], schema?.type);
344
+ // Defensive: a value that is a serialized object/map blob is not human content.
345
+ if (value != null && !looksLikeSerializedBlob(value))
346
+ sweep.push({ label: labelFor(key, ctx), value });
347
+ }
348
+ sweep.sort((a, b) => a.label.localeCompare(b.label));
349
+ rows.push(...sweep);
350
+ return rows;
351
+ }
352
+ // ─── Development (dev-status) normalizers ──────────────────────────────────────
353
+ function mapCommit(raw) {
354
+ if (!isPlainObject(raw))
355
+ return null;
356
+ const url = str(raw.url);
357
+ if (!url)
358
+ return null;
359
+ return {
360
+ id: str(raw.id) ?? '',
361
+ displayId: str(raw.displayId) ?? (str(raw.id) ?? '').slice(0, 7),
362
+ message: str(raw.message) ?? '',
363
+ url,
364
+ author: isPlainObject(raw.author) ? str(raw.author.name) : null,
365
+ timestamp: str(raw.authorTimestamp) ?? str(raw.timestamp) ?? null,
366
+ };
367
+ }
368
+ function normalizePullRequests(detail) {
369
+ const out = [];
370
+ for (const d of detail?.detail ?? []) {
371
+ for (const pr of (isPlainObject(d) && Array.isArray(d.pullRequests) ? d.pullRequests : [])) {
372
+ if (!isPlainObject(pr))
373
+ continue;
374
+ const url = str(pr.url);
375
+ if (!url)
376
+ continue;
377
+ out.push({
378
+ id: str(pr.id) ?? '',
379
+ title: str(pr.name) ?? str(pr.id) ?? '',
380
+ url,
381
+ status: str(pr.status) ?? 'UNKNOWN',
382
+ sourceBranch: isPlainObject(pr.source) ? str(pr.source.branch) : null,
383
+ destBranch: isPlainObject(pr.destination) ? str(pr.destination.branch) : null,
384
+ author: isPlainObject(pr.author) ? str(pr.author.name) : null,
385
+ lastUpdate: str(pr.lastUpdate) ?? null,
386
+ });
387
+ }
388
+ }
389
+ return out;
390
+ }
391
+ function normalizeBranches(detail) {
392
+ const out = [];
393
+ for (const d of detail?.detail ?? []) {
394
+ for (const b of (isPlainObject(d) && Array.isArray(d.branches) ? d.branches : [])) {
395
+ if (!isPlainObject(b))
396
+ continue;
397
+ const url = str(b.url);
398
+ if (!url)
399
+ continue;
400
+ out.push({
401
+ name: str(b.name) ?? '',
402
+ url,
403
+ createPullRequestUrl: str(b.createPullRequestUrl) ?? null,
404
+ repo: isPlainObject(b.repository) ? str(b.repository.name) : null,
405
+ repoUrl: isPlainObject(b.repository) ? str(b.repository.url) : null,
406
+ lastCommit: mapCommit(b.lastCommit),
407
+ });
408
+ }
409
+ }
410
+ return out;
411
+ }
412
+ function normalizeRepositoryCommits(detail) {
413
+ const out = [];
414
+ for (const d of detail?.detail ?? []) {
415
+ for (const repo of (isPlainObject(d) && Array.isArray(d.repositories) ? d.repositories : [])) {
416
+ if (!isPlainObject(repo) || !Array.isArray(repo.commits))
417
+ continue;
418
+ for (const c of repo.commits) {
419
+ const mapped = mapCommit(c);
420
+ if (mapped)
421
+ out.push(mapped);
422
+ if (out.length >= 50)
423
+ return out;
424
+ }
425
+ }
426
+ }
427
+ return out;
428
+ }
@@ -0,0 +1,250 @@
1
+ "use strict";
2
+ // Inbound materializer: Jira issues → `.specrails/local-tickets.json`.
3
+ //
4
+ // The local store stays the canonical read cache that specrails-core reads
5
+ // unchanged. This module performs a SURGICAL merge (never a wholesale rewrite):
6
+ // it only upserts tickets that have a Jira link and preserves every locally-
7
+ // created ticket (no jira link) untouched. It also:
8
+ // - allocates Jira `#id`s from the UNION of the store's next_id and the
9
+ // jira_links max, so Jira ids never collide with local specs;
10
+ // - skips the `status` field for any ticket with a pending outbox op
11
+ // ("frozen" ids) so an inbound poll never reverts a status we just wrote.
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.issueStatusCategory = issueStatusCategory;
14
+ exports.mapStatus = mapStatus;
15
+ exports.mapPriority = mapPriority;
16
+ exports.issueUrl = issueUrl;
17
+ exports.extractEpic = extractEpic;
18
+ exports.extractSprint = extractSprint;
19
+ exports.mapIssueToTicket = mapIssueToTicket;
20
+ exports.upsertIssuesIntoStore = upsertIssuesIntoStore;
21
+ const ticket_store_1 = require("../ticket-store");
22
+ const jira_adf_1 = require("./jira-adf");
23
+ const jira_db_1 = require("./jira-db");
24
+ const CANCEL_NAMES = ['won\'t do', 'wont do', 'cancelled', 'canceled', 'rejected', 'abandoned', 'invalid', 'duplicate', 'declined'];
25
+ function issueStatusCategory(issue) {
26
+ const k = issue.fields.status?.statusCategory?.key;
27
+ return k === 'new' || k === 'done' ? k : 'indeterminate';
28
+ }
29
+ /** Map a Jira issue's status to a Specrails ticket status. */
30
+ function mapStatus(issue) {
31
+ const cat = issueStatusCategory(issue);
32
+ if (cat === 'new')
33
+ return 'todo';
34
+ if (cat === 'indeterminate')
35
+ return 'in_progress';
36
+ // done category: distinguish a cancelled/rejected resolution from a real ship.
37
+ const name = (issue.fields.status?.name ?? '').toLowerCase();
38
+ return CANCEL_NAMES.some((w) => name.includes(w)) ? 'cancelled' : 'done';
39
+ }
40
+ /** Map a Jira priority name to a Specrails priority (best-effort, defaults medium). */
41
+ function mapPriority(name) {
42
+ switch ((name ?? '').toLowerCase()) {
43
+ case 'highest':
44
+ case 'blocker':
45
+ case 'critical':
46
+ return 'critical';
47
+ case 'high':
48
+ return 'high';
49
+ case 'low':
50
+ case 'lowest':
51
+ case 'trivial':
52
+ case 'minor':
53
+ return 'low';
54
+ default:
55
+ return 'medium';
56
+ }
57
+ }
58
+ /** Build the issue's browser URL from the base URL + key. */
59
+ function issueUrl(baseUrl, key) {
60
+ return `${baseUrl.replace(/\/+$/, '')}/browse/${key}`;
61
+ }
62
+ /**
63
+ * Extract the parent epic from an issue. In modern Jira (company- and
64
+ * team-managed) the epic is the issue's `parent`. We only treat the parent as an
65
+ * epic when its issue type is Epic (when that info is present); otherwise we keep
66
+ * the parent reference anyway (most spec-issues' parent is the epic).
67
+ */
68
+ function extractEpic(issue) {
69
+ const parent = issue.fields.parent;
70
+ if (!parent?.key)
71
+ return { key: null, name: null };
72
+ const typeName = parent.fields?.issuetype?.name;
73
+ if (typeName && typeName.toLowerCase() !== 'epic')
74
+ return { key: null, name: null };
75
+ return { key: parent.key, name: parent.fields?.summary ?? parent.key };
76
+ }
77
+ /**
78
+ * Extract the issue's sprint from the (instance-specific) sprint custom field.
79
+ * The value is an array of sprint objects `[{ id, name, state }]`; we prefer the
80
+ * ACTIVE sprint, else the last one listed (Jira orders past→future). Returns
81
+ * null/null when there's no sprint field or no sprint. Tolerant of the value
82
+ * being absent, a non-array, or legacy string entries.
83
+ */
84
+ function extractSprint(issue, sprintFieldId) {
85
+ if (!sprintFieldId || sprintFieldId === 'none')
86
+ return { id: null, name: null, state: null };
87
+ const raw = issue.fields[sprintFieldId];
88
+ if (!Array.isArray(raw) || raw.length === 0)
89
+ return { id: null, name: null, state: null };
90
+ const objs = raw.filter((s) => !!s && typeof s === 'object');
91
+ if (objs.length === 0)
92
+ return { id: null, name: null, state: null };
93
+ const active = objs.find((s) => typeof s.state === 'string' && s.state.toLowerCase() === 'active');
94
+ const chosen = active ?? objs[objs.length - 1];
95
+ const id = chosen.id != null ? String(chosen.id) : null;
96
+ const name = typeof chosen.name === 'string' ? chosen.name : id;
97
+ const state = typeof chosen.state === 'string' ? chosen.state.toLowerCase() : null;
98
+ return { id, name, state };
99
+ }
100
+ /** Map a Jira issue to a full Ticket. `existing` preserves created_at and local-only fields. */
101
+ function mapIssueToTicket(issue, localId, conn, existing) {
102
+ const now = new Date().toISOString();
103
+ const sprint = extractSprint(issue, conn.sprintFieldId);
104
+ return {
105
+ id: localId,
106
+ title: issue.fields.summary || `(${issue.key})`,
107
+ description: (0, jira_adf_1.adfToText)(issue.fields.description),
108
+ status: mapStatus(issue),
109
+ priority: mapPriority(issue.fields.priority?.name),
110
+ labels: issue.fields.labels ?? [],
111
+ assignee: issue.fields.assignee?.displayName ?? issue.fields.assignee?.emailAddress ?? null,
112
+ prerequisites: existing?.prerequisites ?? [],
113
+ metadata: existing?.metadata ?? {},
114
+ comments: existing?.comments ?? [],
115
+ attachments: existing?.attachments,
116
+ origin_conversation_id: existing?.origin_conversation_id ?? null,
117
+ is_epic: existing?.is_epic ?? false,
118
+ parent_epic_id: existing?.parent_epic_id ?? null,
119
+ execution_order: existing?.execution_order ?? null,
120
+ short_summary: existing?.short_summary ?? null,
121
+ created_at: existing?.created_at ?? now,
122
+ updated_at: now,
123
+ created_by: existing?.created_by ?? 'jira',
124
+ source: 'jira',
125
+ jira_key: issue.key,
126
+ jira_url: issueUrl(conn.baseUrl, issue.key),
127
+ jira_epic_key: extractEpic(issue).key,
128
+ jira_epic_name: extractEpic(issue).name,
129
+ jira_sprint_id: sprint.id,
130
+ jira_sprint_name: sprint.name,
131
+ jira_sprint_state: sprint.state,
132
+ };
133
+ }
134
+ /** Compare the Jira-derived content of two tickets, ignoring updated_at. */
135
+ function sameJiraContent(a, b) {
136
+ return (a.title === b.title &&
137
+ a.description === b.description &&
138
+ a.status === b.status &&
139
+ a.priority === b.priority &&
140
+ a.assignee === b.assignee &&
141
+ (a.jira_key ?? null) === (b.jira_key ?? null) &&
142
+ (a.jira_url ?? null) === (b.jira_url ?? null) &&
143
+ (a.jira_epic_key ?? null) === (b.jira_epic_key ?? null) &&
144
+ (a.jira_epic_name ?? null) === (b.jira_epic_name ?? null) &&
145
+ (a.jira_sprint_id ?? null) === (b.jira_sprint_id ?? null) &&
146
+ (a.jira_sprint_name ?? null) === (b.jira_sprint_name ?? null) &&
147
+ (a.jira_sprint_state ?? null) === (b.jira_sprint_state ?? null) &&
148
+ a.labels.length === b.labels.length &&
149
+ a.labels.every((l, i) => l === b.labels[i]));
150
+ }
151
+ /**
152
+ * Apply the frozen-id guard to a freshly mapped ticket. A frozen id has a pending
153
+ * outbound write (a status transition OR a field write-back), so inbound Jira
154
+ * data must NOT clobber the locally-edited fields before that write lands — the
155
+ * poll would otherwise revert the user's edit. We preserve the editable fields
156
+ * (status/priority/title/description/labels) and let Jira-owned fields
157
+ * (key/url/epic/sprint/assignee) flow through.
158
+ */
159
+ function applyFrozen(mapped, existing, frozen) {
160
+ if (existing && frozen) {
161
+ mapped.status = existing.status;
162
+ mapped.priority = existing.priority ?? mapped.priority;
163
+ mapped.title = existing.title;
164
+ mapped.description = existing.description;
165
+ mapped.labels = existing.labels;
166
+ }
167
+ return mapped;
168
+ }
169
+ /**
170
+ * Upsert a batch of Jira issues into the local store. `frozenLocalIds` is the set
171
+ * of local ids whose `status` must NOT be overwritten by inbound data (they have
172
+ * a pending outbox op or are on an active rail). Returns the changed local ids
173
+ * and the max Jira `updated` timestamp seen (drives the high-water mark — derived
174
+ * from Jira's own server timestamps, never the local clock).
175
+ */
176
+ function upsertIssuesIntoStore(db, projectPath, conn, issues, frozenLocalIds = new Set()) {
177
+ const ticketFile = (0, ticket_store_1.resolveTicketStoragePath)(projectPath);
178
+ let maxUpdatedMs = conn.highWaterMs ?? 0;
179
+ // ── Pre-pass (no lock): decide whether ANYTHING actually changed ──────────
180
+ // The high-water + 2-min overlap means most polls return issues that are
181
+ // byte-identical to what we already cached. Writing the store anyway bumps its
182
+ // revision and makes the file-watcher fire a full-board refresh → the every-60s
183
+ // flicker. So when nothing changed we skip the write (and the broadcasts)
184
+ // entirely and return the unchanged revision.
185
+ const preStore = (0, ticket_store_1.readStore)(ticketFile);
186
+ let anyChange = false;
187
+ for (const issue of issues) {
188
+ const u = issue.fields.updated ? Date.parse(issue.fields.updated) : 0;
189
+ if (!Number.isNaN(u) && u > maxUpdatedMs)
190
+ maxUpdatedMs = u;
191
+ const link = (0, jira_db_1.getLinkByIssueId)(db, issue.id);
192
+ if (!link) {
193
+ anyChange = true;
194
+ continue;
195
+ }
196
+ if (issue.key && issue.key !== link.jiraKey) {
197
+ anyChange = true;
198
+ continue;
199
+ }
200
+ const existing = preStore.tickets[String(link.localId)];
201
+ if (!existing) {
202
+ anyChange = true;
203
+ continue;
204
+ }
205
+ const mapped = applyFrozen(mapIssueToTicket(issue, link.localId, conn, existing), existing, frozenLocalIds.has(link.localId));
206
+ if (!sameJiraContent(existing, mapped))
207
+ anyChange = true;
208
+ }
209
+ if (!anyChange) {
210
+ return { changedLocalIds: [], maxUpdatedMs, upserted: 0, revision: preStore.revision, wrote: false };
211
+ }
212
+ // ── Write pass (locked): persist only the tickets that actually changed ────
213
+ const changedLocalIds = [];
214
+ const store = (0, ticket_store_1.mutateStore)(ticketFile, (s) => {
215
+ for (const issue of issues) {
216
+ let link = (0, jira_db_1.getLinkByIssueId)(db, issue.id);
217
+ if (!link) {
218
+ const localId = Math.max(s.next_id, nextJiraLocalId(db));
219
+ link = (0, jira_db_1.insertLinkWithId)(db, {
220
+ localId,
221
+ jiraIssueId: issue.id,
222
+ jiraKey: issue.key,
223
+ jiraProjectId: conn.jiraProjectId,
224
+ deployment: conn.deployment,
225
+ });
226
+ if (localId >= s.next_id)
227
+ s.next_id = localId + 1;
228
+ }
229
+ else if (issue.key && issue.key !== link.jiraKey) {
230
+ // Issue moved/renamed — refresh the display key (link keyed on immutable id).
231
+ db.prepare('UPDATE jira_links SET jira_key = ?, updated_at = ? WHERE jira_issue_id = ?').run(issue.key, new Date().toISOString(), issue.id);
232
+ }
233
+ const localId = link.localId;
234
+ const existing = s.tickets[String(localId)];
235
+ const mapped = applyFrozen(mapIssueToTicket(issue, localId, conn, existing), existing, frozenLocalIds.has(localId));
236
+ // Unchanged tickets are left exactly as-is — no reassignment (keeps their
237
+ // updated_at) and no ticket_updated broadcast.
238
+ if (existing && sameJiraContent(existing, mapped))
239
+ continue;
240
+ s.tickets[String(localId)] = mapped;
241
+ changedLocalIds.push(localId);
242
+ (0, jira_db_1.updateLinkStatusCategory)(db, issue.id, issueStatusCategory(issue));
243
+ }
244
+ });
245
+ return { changedLocalIds, maxUpdatedMs, upserted: changedLocalIds.length, revision: store.revision, wrote: true };
246
+ }
247
+ function nextJiraLocalId(db) {
248
+ const row = db.prepare('SELECT MAX(local_id) AS maxId FROM jira_links').get();
249
+ return (row.maxId ?? 0) + 1;
250
+ }