grm-shared-library 1.1.202 → 1.1.203

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 (244) hide show
  1. package/dist/cjs/index.js +1 -0
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/cjs/kafka/topics.js +26 -0
  4. package/dist/cjs/kafka/topics.js.map +1 -1
  5. package/dist/cjs/modules/common/constants/service.const.js +1 -0
  6. package/dist/cjs/modules/common/constants/service.const.js.map +1 -1
  7. package/dist/cjs/modules/permission/data/permissions.data.js +26 -0
  8. package/dist/cjs/modules/permission/data/permissions.data.js.map +1 -1
  9. package/dist/cjs/modules/permission/enums/permission-actions.enum.js +40 -0
  10. package/dist/cjs/modules/permission/enums/permission-actions.enum.js.map +1 -1
  11. package/dist/cjs/modules/permission/enums/permissions-module.enum.js +1 -0
  12. package/dist/cjs/modules/permission/enums/permissions-module.enum.js.map +1 -1
  13. package/dist/cjs/modules/role/data/roles.data.js +83 -0
  14. package/dist/cjs/modules/role/data/roles.data.js.map +1 -1
  15. package/dist/cjs/modules/socket/enums/redis-channel-name.enum.js +1 -0
  16. package/dist/cjs/modules/socket/enums/redis-channel-name.enum.js.map +1 -1
  17. package/dist/cjs/modules/socket/enums/room-type.enum.js +8 -0
  18. package/dist/cjs/modules/socket/enums/room-type.enum.js.map +1 -1
  19. package/dist/cjs/modules/socket/enums/socket-event-name.enum.js +5 -0
  20. package/dist/cjs/modules/socket/enums/socket-event-name.enum.js.map +1 -1
  21. package/dist/cjs/modules/support/constants/support-defaults.const.js +139 -0
  22. package/dist/cjs/modules/support/constants/support-defaults.const.js.map +1 -0
  23. package/dist/cjs/modules/support/constants/support-kafka.const.js +51 -0
  24. package/dist/cjs/modules/support/constants/support-kafka.const.js.map +1 -0
  25. package/dist/cjs/modules/support/dtos/create-support-ticket-message.dto.js +82 -0
  26. package/dist/cjs/modules/support/dtos/create-support-ticket-message.dto.js.map +1 -0
  27. package/dist/cjs/modules/support/dtos/create-support-ticket.dto.js +169 -0
  28. package/dist/cjs/modules/support/dtos/create-support-ticket.dto.js.map +1 -0
  29. package/dist/cjs/modules/support/dtos/customer-feedback.dto.js +401 -0
  30. package/dist/cjs/modules/support/dtos/customer-feedback.dto.js.map +1 -0
  31. package/dist/cjs/modules/support/dtos/support-configuration.dto.js +732 -0
  32. package/dist/cjs/modules/support/dtos/support-configuration.dto.js.map +1 -0
  33. package/dist/cjs/modules/support/dtos/support-related-entity.dto.js +38 -0
  34. package/dist/cjs/modules/support/dtos/support-related-entity.dto.js.map +1 -0
  35. package/dist/cjs/modules/support/dtos/support-report-query.dto.js +127 -0
  36. package/dist/cjs/modules/support/dtos/support-report-query.dto.js.map +1 -0
  37. package/dist/cjs/modules/support/dtos/support-sync.dto.js +175 -0
  38. package/dist/cjs/modules/support/dtos/support-sync.dto.js.map +1 -0
  39. package/dist/cjs/modules/support/dtos/support-ticket-actions.dto.js +191 -0
  40. package/dist/cjs/modules/support/dtos/support-ticket-actions.dto.js.map +1 -0
  41. package/dist/cjs/modules/support/dtos/support-ticket-query.dto.js +225 -0
  42. package/dist/cjs/modules/support/dtos/support-ticket-query.dto.js.map +1 -0
  43. package/dist/cjs/modules/support/enums/support-audit-action.enum.js +87 -0
  44. package/dist/cjs/modules/support/enums/support-audit-action.enum.js.map +1 -0
  45. package/dist/cjs/modules/support/enums/support-feedback.enum.js +110 -0
  46. package/dist/cjs/modules/support/enums/support-feedback.enum.js.map +1 -0
  47. package/dist/cjs/modules/support/enums/support-provider-code.enum.js +66 -0
  48. package/dist/cjs/modules/support/enums/support-provider-code.enum.js.map +1 -0
  49. package/dist/cjs/modules/support/enums/support-related-entity-type.enum.js +50 -0
  50. package/dist/cjs/modules/support/enums/support-related-entity-type.enum.js.map +1 -0
  51. package/dist/cjs/modules/support/enums/support-routing.enum.js +58 -0
  52. package/dist/cjs/modules/support/enums/support-routing.enum.js.map +1 -0
  53. package/dist/cjs/modules/support/enums/support-sla.enum.js +55 -0
  54. package/dist/cjs/modules/support/enums/support-sla.enum.js.map +1 -0
  55. package/dist/cjs/modules/support/enums/support-sync.enum.js +133 -0
  56. package/dist/cjs/modules/support/enums/support-sync.enum.js.map +1 -0
  57. package/dist/cjs/modules/support/enums/support-ticket-channel.enum.js +36 -0
  58. package/dist/cjs/modules/support/enums/support-ticket-channel.enum.js.map +1 -0
  59. package/dist/cjs/modules/support/enums/support-ticket-message.enum.js +54 -0
  60. package/dist/cjs/modules/support/enums/support-ticket-message.enum.js.map +1 -0
  61. package/dist/cjs/modules/support/enums/support-ticket-priority.enum.js +41 -0
  62. package/dist/cjs/modules/support/enums/support-ticket-priority.enum.js.map +1 -0
  63. package/dist/cjs/modules/support/enums/support-ticket-status.enum.js +138 -0
  64. package/dist/cjs/modules/support/enums/support-ticket-status.enum.js.map +1 -0
  65. package/dist/cjs/modules/support/index.js +60 -0
  66. package/dist/cjs/modules/support/index.js.map +1 -0
  67. package/dist/cjs/modules/support/interfaces/customer-feedback.js +3 -0
  68. package/dist/cjs/modules/support/interfaces/customer-feedback.js.map +1 -0
  69. package/dist/cjs/modules/support/interfaces/support-configuration.js +3 -0
  70. package/dist/cjs/modules/support/interfaces/support-configuration.js.map +1 -0
  71. package/dist/cjs/modules/support/interfaces/support-events.js +3 -0
  72. package/dist/cjs/modules/support/interfaces/support-events.js.map +1 -0
  73. package/dist/cjs/modules/support/interfaces/support-provider-descriptor.js +3 -0
  74. package/dist/cjs/modules/support/interfaces/support-provider-descriptor.js.map +1 -0
  75. package/dist/cjs/modules/support/interfaces/support-realtime.js +18 -0
  76. package/dist/cjs/modules/support/interfaces/support-realtime.js.map +1 -0
  77. package/dist/cjs/modules/support/interfaces/support-reports.js +3 -0
  78. package/dist/cjs/modules/support/interfaces/support-reports.js.map +1 -0
  79. package/dist/cjs/modules/support/interfaces/support-sync.js +3 -0
  80. package/dist/cjs/modules/support/interfaces/support-sync.js.map +1 -0
  81. package/dist/cjs/modules/support/interfaces/support-ticket-message.js +3 -0
  82. package/dist/cjs/modules/support/interfaces/support-ticket-message.js.map +1 -0
  83. package/dist/cjs/modules/support/interfaces/support-ticket.js +3 -0
  84. package/dist/cjs/modules/support/interfaces/support-ticket.js.map +1 -0
  85. package/dist/cjs/modules/support/utils/support-feedback.util.js +66 -0
  86. package/dist/cjs/modules/support/utils/support-feedback.util.js.map +1 -0
  87. package/dist/cjs/modules/support/utils/support-redaction.util.js +173 -0
  88. package/dist/cjs/modules/support/utils/support-redaction.util.js.map +1 -0
  89. package/dist/cjs/modules/support/utils/support-routing.util.js +87 -0
  90. package/dist/cjs/modules/support/utils/support-routing.util.js.map +1 -0
  91. package/dist/cjs/modules/support/utils/support-sla.util.js +239 -0
  92. package/dist/cjs/modules/support/utils/support-sla.util.js.map +1 -0
  93. package/dist/cjs/modules/support/utils/support-status.util.js +93 -0
  94. package/dist/cjs/modules/support/utils/support-status.util.js.map +1 -0
  95. package/dist/cjs/modules/support/utils/support-sync.util.js +151 -0
  96. package/dist/cjs/modules/support/utils/support-sync.util.js.map +1 -0
  97. package/dist/cjs/modules/support/utils/support-ticket-number.util.js +61 -0
  98. package/dist/cjs/modules/support/utils/support-ticket-number.util.js.map +1 -0
  99. package/dist/esm/index.js +1 -0
  100. package/dist/esm/index.js.map +1 -1
  101. package/dist/esm/kafka/topics.js +26 -0
  102. package/dist/esm/kafka/topics.js.map +1 -1
  103. package/dist/esm/modules/common/constants/service.const.js +1 -0
  104. package/dist/esm/modules/common/constants/service.const.js.map +1 -1
  105. package/dist/esm/modules/permission/data/permissions.data.js +26 -0
  106. package/dist/esm/modules/permission/data/permissions.data.js.map +1 -1
  107. package/dist/esm/modules/permission/enums/permission-actions.enum.js +40 -0
  108. package/dist/esm/modules/permission/enums/permission-actions.enum.js.map +1 -1
  109. package/dist/esm/modules/permission/enums/permissions-module.enum.js +1 -0
  110. package/dist/esm/modules/permission/enums/permissions-module.enum.js.map +1 -1
  111. package/dist/esm/modules/role/data/roles.data.js +83 -0
  112. package/dist/esm/modules/role/data/roles.data.js.map +1 -1
  113. package/dist/esm/modules/socket/enums/redis-channel-name.enum.js +1 -0
  114. package/dist/esm/modules/socket/enums/redis-channel-name.enum.js.map +1 -1
  115. package/dist/esm/modules/socket/enums/room-type.enum.js +8 -0
  116. package/dist/esm/modules/socket/enums/room-type.enum.js.map +1 -1
  117. package/dist/esm/modules/socket/enums/socket-event-name.enum.js +5 -0
  118. package/dist/esm/modules/socket/enums/socket-event-name.enum.js.map +1 -1
  119. package/dist/esm/modules/support/constants/support-defaults.const.js +136 -0
  120. package/dist/esm/modules/support/constants/support-defaults.const.js.map +1 -0
  121. package/dist/esm/modules/support/constants/support-kafka.const.js +48 -0
  122. package/dist/esm/modules/support/constants/support-kafka.const.js.map +1 -0
  123. package/dist/esm/modules/support/dtos/create-support-ticket-message.dto.js +77 -0
  124. package/dist/esm/modules/support/dtos/create-support-ticket-message.dto.js.map +1 -0
  125. package/dist/esm/modules/support/dtos/create-support-ticket.dto.js +165 -0
  126. package/dist/esm/modules/support/dtos/create-support-ticket.dto.js.map +1 -0
  127. package/dist/esm/modules/support/dtos/customer-feedback.dto.js +392 -0
  128. package/dist/esm/modules/support/dtos/customer-feedback.dto.js.map +1 -0
  129. package/dist/esm/modules/support/dtos/support-configuration.dto.js +716 -0
  130. package/dist/esm/modules/support/dtos/support-configuration.dto.js.map +1 -0
  131. package/dist/esm/modules/support/dtos/support-related-entity.dto.js +34 -0
  132. package/dist/esm/modules/support/dtos/support-related-entity.dto.js.map +1 -0
  133. package/dist/esm/modules/support/dtos/support-report-query.dto.js +123 -0
  134. package/dist/esm/modules/support/dtos/support-report-query.dto.js.map +1 -0
  135. package/dist/esm/modules/support/dtos/support-sync.dto.js +169 -0
  136. package/dist/esm/modules/support/dtos/support-sync.dto.js.map +1 -0
  137. package/dist/esm/modules/support/dtos/support-ticket-actions.dto.js +180 -0
  138. package/dist/esm/modules/support/dtos/support-ticket-actions.dto.js.map +1 -0
  139. package/dist/esm/modules/support/dtos/support-ticket-query.dto.js +221 -0
  140. package/dist/esm/modules/support/dtos/support-ticket-query.dto.js.map +1 -0
  141. package/dist/esm/modules/support/enums/support-audit-action.enum.js +84 -0
  142. package/dist/esm/modules/support/enums/support-audit-action.enum.js.map +1 -0
  143. package/dist/esm/modules/support/enums/support-feedback.enum.js +107 -0
  144. package/dist/esm/modules/support/enums/support-feedback.enum.js.map +1 -0
  145. package/dist/esm/modules/support/enums/support-provider-code.enum.js +63 -0
  146. package/dist/esm/modules/support/enums/support-provider-code.enum.js.map +1 -0
  147. package/dist/esm/modules/support/enums/support-related-entity-type.enum.js +47 -0
  148. package/dist/esm/modules/support/enums/support-related-entity-type.enum.js.map +1 -0
  149. package/dist/esm/modules/support/enums/support-routing.enum.js +55 -0
  150. package/dist/esm/modules/support/enums/support-routing.enum.js.map +1 -0
  151. package/dist/esm/modules/support/enums/support-sla.enum.js +52 -0
  152. package/dist/esm/modules/support/enums/support-sla.enum.js.map +1 -0
  153. package/dist/esm/modules/support/enums/support-sync.enum.js +130 -0
  154. package/dist/esm/modules/support/enums/support-sync.enum.js.map +1 -0
  155. package/dist/esm/modules/support/enums/support-ticket-channel.enum.js +33 -0
  156. package/dist/esm/modules/support/enums/support-ticket-channel.enum.js.map +1 -0
  157. package/dist/esm/modules/support/enums/support-ticket-message.enum.js +51 -0
  158. package/dist/esm/modules/support/enums/support-ticket-message.enum.js.map +1 -0
  159. package/dist/esm/modules/support/enums/support-ticket-priority.enum.js +38 -0
  160. package/dist/esm/modules/support/enums/support-ticket-priority.enum.js.map +1 -0
  161. package/dist/esm/modules/support/enums/support-ticket-status.enum.js +135 -0
  162. package/dist/esm/modules/support/enums/support-ticket-status.enum.js.map +1 -0
  163. package/dist/esm/modules/support/index.js +44 -0
  164. package/dist/esm/modules/support/index.js.map +1 -0
  165. package/dist/esm/modules/support/interfaces/customer-feedback.js +2 -0
  166. package/dist/esm/modules/support/interfaces/customer-feedback.js.map +1 -0
  167. package/dist/esm/modules/support/interfaces/support-configuration.js +2 -0
  168. package/dist/esm/modules/support/interfaces/support-configuration.js.map +1 -0
  169. package/dist/esm/modules/support/interfaces/support-events.js +2 -0
  170. package/dist/esm/modules/support/interfaces/support-events.js.map +1 -0
  171. package/dist/esm/modules/support/interfaces/support-provider-descriptor.js +2 -0
  172. package/dist/esm/modules/support/interfaces/support-provider-descriptor.js.map +1 -0
  173. package/dist/esm/modules/support/interfaces/support-realtime.js +15 -0
  174. package/dist/esm/modules/support/interfaces/support-realtime.js.map +1 -0
  175. package/dist/esm/modules/support/interfaces/support-reports.js +2 -0
  176. package/dist/esm/modules/support/interfaces/support-reports.js.map +1 -0
  177. package/dist/esm/modules/support/interfaces/support-sync.js +2 -0
  178. package/dist/esm/modules/support/interfaces/support-sync.js.map +1 -0
  179. package/dist/esm/modules/support/interfaces/support-ticket-message.js +2 -0
  180. package/dist/esm/modules/support/interfaces/support-ticket-message.js.map +1 -0
  181. package/dist/esm/modules/support/interfaces/support-ticket.js +2 -0
  182. package/dist/esm/modules/support/interfaces/support-ticket.js.map +1 -0
  183. package/dist/esm/modules/support/utils/support-feedback.util.js +60 -0
  184. package/dist/esm/modules/support/utils/support-feedback.util.js.map +1 -0
  185. package/dist/esm/modules/support/utils/support-redaction.util.js +164 -0
  186. package/dist/esm/modules/support/utils/support-redaction.util.js.map +1 -0
  187. package/dist/esm/modules/support/utils/support-routing.util.js +82 -0
  188. package/dist/esm/modules/support/utils/support-routing.util.js.map +1 -0
  189. package/dist/esm/modules/support/utils/support-sla.util.js +229 -0
  190. package/dist/esm/modules/support/utils/support-sla.util.js.map +1 -0
  191. package/dist/esm/modules/support/utils/support-status.util.js +81 -0
  192. package/dist/esm/modules/support/utils/support-status.util.js.map +1 -0
  193. package/dist/esm/modules/support/utils/support-sync.util.js +142 -0
  194. package/dist/esm/modules/support/utils/support-sync.util.js.map +1 -0
  195. package/dist/esm/modules/support/utils/support-ticket-number.util.js +56 -0
  196. package/dist/esm/modules/support/utils/support-ticket-number.util.js.map +1 -0
  197. package/dist/types/index.d.ts +1 -0
  198. package/dist/types/kafka/topics.d.ts +21 -0
  199. package/dist/types/modules/common/constants/service.const.d.ts +4 -0
  200. package/dist/types/modules/permission/enums/permission-actions.enum.d.ts +21 -0
  201. package/dist/types/modules/permission/enums/permissions-module.enum.d.ts +2 -1
  202. package/dist/types/modules/socket/enums/redis-channel-name.enum.d.ts +2 -1
  203. package/dist/types/modules/socket/enums/room-type.enum.d.ts +8 -0
  204. package/dist/types/modules/socket/enums/socket-event-name.enum.d.ts +2 -1
  205. package/dist/types/modules/support/constants/support-defaults.const.d.ts +129 -0
  206. package/dist/types/modules/support/constants/support-kafka.const.d.ts +47 -0
  207. package/dist/types/modules/support/dtos/create-support-ticket-message.dto.d.ts +30 -0
  208. package/dist/types/modules/support/dtos/create-support-ticket.dto.d.ts +35 -0
  209. package/dist/types/modules/support/dtos/customer-feedback.dto.d.ts +83 -0
  210. package/dist/types/modules/support/dtos/support-configuration.dto.d.ts +130 -0
  211. package/dist/types/modules/support/dtos/support-related-entity.dto.d.ts +13 -0
  212. package/dist/types/modules/support/dtos/support-report-query.dto.d.ts +29 -0
  213. package/dist/types/modules/support/dtos/support-sync.dto.d.ts +40 -0
  214. package/dist/types/modules/support/dtos/support-ticket-actions.dto.d.ts +61 -0
  215. package/dist/types/modules/support/dtos/support-ticket-query.dto.d.ts +41 -0
  216. package/dist/types/modules/support/enums/support-audit-action.enum.d.ts +80 -0
  217. package/dist/types/modules/support/enums/support-feedback.enum.d.ts +65 -0
  218. package/dist/types/modules/support/enums/support-provider-code.enum.d.ts +57 -0
  219. package/dist/types/modules/support/enums/support-related-entity-type.enum.d.ts +41 -0
  220. package/dist/types/modules/support/enums/support-routing.enum.d.ts +48 -0
  221. package/dist/types/modules/support/enums/support-sla.enum.d.ts +47 -0
  222. package/dist/types/modules/support/enums/support-sync.enum.d.ts +108 -0
  223. package/dist/types/modules/support/enums/support-ticket-channel.enum.d.ts +30 -0
  224. package/dist/types/modules/support/enums/support-ticket-message.enum.d.ts +42 -0
  225. package/dist/types/modules/support/enums/support-ticket-priority.enum.d.ts +24 -0
  226. package/dist/types/modules/support/enums/support-ticket-status.enum.d.ts +58 -0
  227. package/dist/types/modules/support/index.d.ts +38 -0
  228. package/dist/types/modules/support/interfaces/customer-feedback.d.ts +115 -0
  229. package/dist/types/modules/support/interfaces/support-configuration.d.ts +170 -0
  230. package/dist/types/modules/support/interfaces/support-events.d.ts +126 -0
  231. package/dist/types/modules/support/interfaces/support-provider-descriptor.d.ts +92 -0
  232. package/dist/types/modules/support/interfaces/support-realtime.d.ts +65 -0
  233. package/dist/types/modules/support/interfaces/support-reports.d.ts +122 -0
  234. package/dist/types/modules/support/interfaces/support-sync.d.ts +105 -0
  235. package/dist/types/modules/support/interfaces/support-ticket-message.d.ts +66 -0
  236. package/dist/types/modules/support/interfaces/support-ticket.d.ts +180 -0
  237. package/dist/types/modules/support/utils/support-feedback.util.d.ts +23 -0
  238. package/dist/types/modules/support/utils/support-redaction.util.d.ts +48 -0
  239. package/dist/types/modules/support/utils/support-routing.util.d.ts +50 -0
  240. package/dist/types/modules/support/utils/support-sla.util.d.ts +69 -0
  241. package/dist/types/modules/support/utils/support-status.util.d.ts +44 -0
  242. package/dist/types/modules/support/utils/support-sync.util.d.ts +76 -0
  243. package/dist/types/modules/support/utils/support-ticket-number.util.d.ts +39 -0
  244. package/package.json +1 -1
@@ -0,0 +1,60 @@
1
+ import { SUPPORT_FEEDBACK_RATING } from '../constants/support-defaults.const';
2
+ import { CustomerFeedbackSentiment, CustomerFeedbackType, RATING_CUSTOMER_FEEDBACK_TYPES, } from '../enums/support-feedback.enum';
3
+ /** Whether this kind of feedback carries a star rating. */
4
+ export function feedbackTypeRequiresRating(type) {
5
+ return RATING_CUSTOMER_FEEDBACK_TYPES.includes(type);
6
+ }
7
+ /**
8
+ * Derive sentiment from the feedback TYPE and rating.
9
+ *
10
+ * Never from the message text. An automated reading of "the guard never came"
11
+ * that lands on POSITIVE is worse than no reading at all, and a support desk
12
+ * that starts distrusting one column stops trusting the dashboard. When a real
13
+ * classifier is added it writes this same field, and the rule below becomes its
14
+ * fallback rather than being replaced.
15
+ */
16
+ export function deriveFeedbackSentiment(type, rating) {
17
+ if (rating != null && Number.isFinite(rating)) {
18
+ if (rating <= SUPPORT_FEEDBACK_RATING.NEGATIVE_AT_OR_BELOW) {
19
+ return CustomerFeedbackSentiment.NEGATIVE;
20
+ }
21
+ if (rating >= SUPPORT_FEEDBACK_RATING.POSITIVE_AT_OR_ABOVE) {
22
+ return CustomerFeedbackSentiment.POSITIVE;
23
+ }
24
+ return CustomerFeedbackSentiment.NEUTRAL;
25
+ }
26
+ switch (type) {
27
+ case CustomerFeedbackType.COMPLIMENT:
28
+ return CustomerFeedbackSentiment.POSITIVE;
29
+ case CustomerFeedbackType.COMPLAINT:
30
+ case CustomerFeedbackType.BUG_REPORT:
31
+ return CustomerFeedbackSentiment.NEGATIVE;
32
+ case CustomerFeedbackType.FEATURE_REQUEST:
33
+ case CustomerFeedbackType.GENERAL:
34
+ return CustomerFeedbackSentiment.NEUTRAL;
35
+ default:
36
+ return CustomerFeedbackSentiment.UNKNOWN;
37
+ }
38
+ }
39
+ /** Whether a rating is inside the allowed range. */
40
+ export function isValidFeedbackRating(rating) {
41
+ if (typeof rating !== 'number' || !Number.isFinite(rating))
42
+ return false;
43
+ if (!Number.isInteger(rating))
44
+ return false;
45
+ return rating >= SUPPORT_FEEDBACK_RATING.MIN && rating <= SUPPORT_FEEDBACK_RATING.MAX;
46
+ }
47
+ /**
48
+ * Whether a piece of feedback is the kind an agent should probably act on.
49
+ *
50
+ * Advisory only - it drives a hint in the admin, never an automatic conversion.
51
+ * Promoting somebody's complaint into a ticket without a human deciding is how a
52
+ * queue fills with work nobody chose to take on.
53
+ */
54
+ export function feedbackWarrantsTicket(type, rating) {
55
+ if (type === CustomerFeedbackType.BUG_REPORT || type === CustomerFeedbackType.COMPLAINT) {
56
+ return true;
57
+ }
58
+ return rating != null && rating <= SUPPORT_FEEDBACK_RATING.NEGATIVE_AT_OR_BELOW;
59
+ }
60
+ //# sourceMappingURL=support-feedback.util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"support-feedback.util.js","sourceRoot":"","sources":["../../../../../src/modules/support/utils/support-feedback.util.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,qCAAqC,CAAC;AAC9E,OAAO,EACH,yBAAyB,EACzB,oBAAoB,EACpB,8BAA8B,GACjC,MAAM,gCAAgC,CAAC;AAExC,2DAA2D;AAC3D,MAAM,UAAU,0BAA0B,CAAC,IAA0B;IACjE,OAAO,8BAA8B,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AACzD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,uBAAuB,CACnC,IAA0B,EAC1B,MAAsB;IAEtB,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5C,IAAI,MAAM,IAAI,uBAAuB,CAAC,oBAAoB,EAAE,CAAC;YACzD,OAAO,yBAAyB,CAAC,QAAQ,CAAC;QAC9C,CAAC;QACD,IAAI,MAAM,IAAI,uBAAuB,CAAC,oBAAoB,EAAE,CAAC;YACzD,OAAO,yBAAyB,CAAC,QAAQ,CAAC;QAC9C,CAAC;QACD,OAAO,yBAAyB,CAAC,OAAO,CAAC;IAC7C,CAAC;IAED,QAAQ,IAAI,EAAE,CAAC;QACX,KAAK,oBAAoB,CAAC,UAAU;YAChC,OAAO,yBAAyB,CAAC,QAAQ,CAAC;QAC9C,KAAK,oBAAoB,CAAC,SAAS,CAAC;QACpC,KAAK,oBAAoB,CAAC,UAAU;YAChC,OAAO,yBAAyB,CAAC,QAAQ,CAAC;QAC9C,KAAK,oBAAoB,CAAC,eAAe,CAAC;QAC1C,KAAK,oBAAoB,CAAC,OAAO;YAC7B,OAAO,yBAAyB,CAAC,OAAO,CAAC;QAC7C;YACI,OAAO,yBAAyB,CAAC,OAAO,CAAC;IACjD,CAAC;AACL,CAAC;AAED,oDAAoD;AACpD,MAAM,UAAU,qBAAqB,CAAC,MAAe;IACjD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IACzE,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5C,OAAO,MAAM,IAAI,uBAAuB,CAAC,GAAG,IAAI,MAAM,IAAI,uBAAuB,CAAC,GAAG,CAAC;AAC1F,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CAClC,IAA0B,EAC1B,MAAsB;IAEtB,IAAI,IAAI,KAAK,oBAAoB,CAAC,UAAU,IAAI,IAAI,KAAK,oBAAoB,CAAC,SAAS,EAAE,CAAC;QACtF,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,OAAO,MAAM,IAAI,IAAI,IAAI,MAAM,IAAI,uBAAuB,CAAC,oBAAoB,CAAC;AACpF,CAAC"}
@@ -0,0 +1,164 @@
1
+ import { SUPPORT_FIELD_LIMITS } from '../constants/support-defaults.const';
2
+ /**
3
+ * Mask an identifier for logging: enough to correlate, never enough to reuse.
4
+ * `+254712345678` becomes `+254***5678`.
5
+ */
6
+ export function maskSupportIdentifier(value) {
7
+ if (value == null)
8
+ return 'unknown';
9
+ const raw = String(value).trim();
10
+ if (raw === '')
11
+ return 'unknown';
12
+ if (raw.length <= 8)
13
+ return `${raw.slice(0, 2)}***`;
14
+ return `${raw.slice(0, 4)}***${raw.slice(-4)}`;
15
+ }
16
+ /**
17
+ * Reduce a URL to scheme and host.
18
+ *
19
+ * A helpdesk URL routinely carries an API token in its query string, and a URL
20
+ * is the single most-logged value in an HTTP client.
21
+ */
22
+ export function maskSupportUrl(value) {
23
+ if (value == null)
24
+ return 'unknown';
25
+ const raw = String(value).trim();
26
+ if (raw === '')
27
+ return 'unknown';
28
+ try {
29
+ const url = new URL(raw);
30
+ return `${url.protocol}//${url.host}${url.pathname}`;
31
+ }
32
+ catch {
33
+ return raw.split('?')[0];
34
+ }
35
+ }
36
+ /**
37
+ * Replace anything credential- or PII-shaped in free text.
38
+ *
39
+ * Applied to every provider message before it is stored or logged. It is a
40
+ * belt-and-braces pass on top of key-based redaction: a helpdesk that puts a
41
+ * token in a prose error defeats key matching, and that message still ends up
42
+ * in a table an operator can read.
43
+ */
44
+ export function scrubSupportText(input) {
45
+ return String(input !== null && input !== void 0 ? input : '')
46
+ .replace(/Bearer\s+[A-Za-z0-9._-]+/gi, 'Bearer [REDACTED]')
47
+ .replace(/Token\s+token=[A-Za-z0-9._-]+/gi, 'Token token=[REDACTED]')
48
+ .replace(/\b(sk|pk)_(test|prod|live)_[A-Za-z0-9_-]+/gi, '[REDACTED]')
49
+ .replace(/\beyJ[A-Za-z0-9._-]{10,}/g, '[REDACTED]')
50
+ .replace(/\+[1-9]\d{9,14}\b/g, (match) => maskSupportIdentifier(match));
51
+ }
52
+ /** Keys whose VALUE is redacted wherever it appears in a payload, at any depth. */
53
+ const SENSITIVE_SUPPORT_KEYS = [
54
+ 'password',
55
+ 'passwd',
56
+ 'secret',
57
+ 'token',
58
+ 'apikey',
59
+ 'api_key',
60
+ 'apitoken',
61
+ 'api_token',
62
+ 'authorization',
63
+ 'auth',
64
+ 'credential',
65
+ 'accesstoken',
66
+ 'access_token',
67
+ 'refreshtoken',
68
+ 'refresh_token',
69
+ 'otp',
70
+ 'pin',
71
+ 'signature',
72
+ 'privatekey',
73
+ 'private_key',
74
+ 'sessionid',
75
+ 'session_id',
76
+ 'cookie',
77
+ 'setcookie',
78
+ 'set_cookie',
79
+ ];
80
+ function isSensitiveSupportKey(key) {
81
+ const normalized = key.toLowerCase().replace(/[^a-z_]/g, '');
82
+ return SENSITIVE_SUPPORT_KEYS.some((candidate) => normalized.includes(candidate.replace(/_/g, '')));
83
+ }
84
+ /**
85
+ * Redact a payload by key before it is persisted on a synchronization row or an
86
+ * audit entry.
87
+ *
88
+ * Depth-bounded, because a provider can return an arbitrarily nested object and
89
+ * an audit writer must not be a place a stack overflow can happen. Anything
90
+ * past the bound is replaced with a marker rather than dropped silently.
91
+ */
92
+ export function sanitizeSupportPayload(value, depth = 0) {
93
+ if (depth > 6)
94
+ return '[TRUNCATED]';
95
+ if (value == null)
96
+ return value;
97
+ if (Array.isArray(value)) {
98
+ return value.slice(0, 50).map((item) => sanitizeSupportPayload(item, depth + 1));
99
+ }
100
+ if (typeof value === 'object') {
101
+ const source = value;
102
+ const result = {};
103
+ for (const key of Object.keys(source)) {
104
+ result[key] = isSensitiveSupportKey(key)
105
+ ? '[REDACTED]'
106
+ : sanitizeSupportPayload(source[key], depth + 1);
107
+ }
108
+ return result;
109
+ }
110
+ if (typeof value === 'string')
111
+ return scrubSupportText(value);
112
+ return value;
113
+ }
114
+ /**
115
+ * Trim a value to a limit without ever throwing.
116
+ *
117
+ * A support message that runs long is truncated and still sent. Refusing to
118
+ * record what a customer said because it exceeded a column width would lose the
119
+ * thing the whole domain exists to capture.
120
+ */
121
+ export function truncateForSupport(value, maxLength) {
122
+ if (value == null)
123
+ return undefined;
124
+ const trimmed = String(value).trim();
125
+ if (trimmed === '')
126
+ return undefined;
127
+ return trimmed.length <= maxLength ? trimmed : trimmed.slice(0, maxLength);
128
+ }
129
+ /**
130
+ * A one-line preview of a longer body, for list rows.
131
+ *
132
+ * Newlines collapse to spaces so a multi-line message cannot break a table row,
133
+ * and the result is ellipsised rather than hard-cut so a reader can tell there
134
+ * is more.
135
+ */
136
+ export function previewSupportText(value, maxLength = 140) {
137
+ const collapsed = String(value !== null && value !== void 0 ? value : '')
138
+ .replace(/\s+/g, ' ')
139
+ .trim();
140
+ if (collapsed.length <= maxLength)
141
+ return collapsed;
142
+ return `${collapsed.slice(0, Math.max(0, maxLength - 1))}…`;
143
+ }
144
+ /** A provider response body reduced to something safe and small enough to keep. */
145
+ export function snapshotSupportPayload(body) {
146
+ var _a;
147
+ if (body == null || typeof body !== 'object')
148
+ return null;
149
+ const redacted = sanitizeSupportPayload(body);
150
+ const serialized = (_a = JSON.stringify(redacted)) !== null && _a !== void 0 ? _a : '';
151
+ if (serialized.length > SUPPORT_FIELD_LIMITS.SYNC_SNAPSHOT_MAX_LENGTH) {
152
+ return {
153
+ truncated: true,
154
+ preview: serialized.slice(0, SUPPORT_FIELD_LIMITS.SYNC_SNAPSHOT_MAX_LENGTH),
155
+ };
156
+ }
157
+ try {
158
+ return JSON.parse(serialized);
159
+ }
160
+ catch {
161
+ return { unparsed: serialized };
162
+ }
163
+ }
164
+ //# sourceMappingURL=support-redaction.util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"support-redaction.util.js","sourceRoot":"","sources":["../../../../../src/modules/support/utils/support-redaction.util.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAC;AAE3E;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAgC;IAClE,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,SAAS,CAAC;IACpC,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;IACjC,IAAI,GAAG,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IACjC,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC;QAAE,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC;IACpD,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACnD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,KAAgC;IAC3D,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,SAAS,CAAC;IACpC,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;IACjC,IAAI,GAAG,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IACjC,IAAI,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QACzB,OAAO,GAAG,GAAG,CAAC,QAAQ,KAAK,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;IACzD,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7B,CAAC;AACL,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAgC;IAC7D,OAAO,MAAM,CAAC,KAAK,aAAL,KAAK,cAAL,KAAK,GAAI,EAAE,CAAC;SACrB,OAAO,CAAC,4BAA4B,EAAE,mBAAmB,CAAC;SAC1D,OAAO,CAAC,iCAAiC,EAAE,wBAAwB,CAAC;SACpE,OAAO,CAAC,6CAA6C,EAAE,YAAY,CAAC;SACpE,OAAO,CAAC,2BAA2B,EAAE,YAAY,CAAC;SAClD,OAAO,CAAC,oBAAoB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC,CAAC;AAChF,CAAC;AAED,mFAAmF;AACnF,MAAM,sBAAsB,GAAG;IAC3B,UAAU;IACV,QAAQ;IACR,QAAQ;IACR,OAAO;IACP,QAAQ;IACR,SAAS;IACT,UAAU;IACV,WAAW;IACX,eAAe;IACf,MAAM;IACN,YAAY;IACZ,aAAa;IACb,cAAc;IACd,cAAc;IACd,eAAe;IACf,KAAK;IACL,KAAK;IACL,WAAW;IACX,YAAY;IACZ,aAAa;IACb,WAAW;IACX,YAAY;IACZ,QAAQ;IACR,WAAW;IACX,YAAY;CACf,CAAC;AAEF,SAAS,qBAAqB,CAAC,GAAW;IACtC,MAAM,UAAU,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC7D,OAAO,sBAAsB,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;AACxG,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAAc,EAAE,KAAK,GAAG,CAAC;IAC5D,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,aAAa,CAAC;IACpC,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,KAAK,CAAC;IAChC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,sBAAsB,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;IACrF,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,KAAgC,CAAC;QAChD,MAAM,MAAM,GAA4B,EAAE,CAAC;QAC3C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YACpC,MAAM,CAAC,GAAG,CAAC,GAAG,qBAAqB,CAAC,GAAG,CAAC;gBACpC,CAAC,CAAC,YAAY;gBACd,CAAC,CAAC,sBAAsB,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;QACzD,CAAC;QACD,OAAO,MAAM,CAAC;IAClB,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,gBAAgB,CAAC,KAAK,CAAC,CAAC;IAC9D,OAAO,KAAK,CAAC;AACjB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAC9B,KAAgC,EAChC,SAAiB;IAEjB,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,SAAS,CAAC;IACpC,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;IACrC,IAAI,OAAO,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IACrC,OAAO,OAAO,CAAC,MAAM,IAAI,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;AAC/E,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAgC,EAAE,SAAS,GAAG,GAAG;IAChF,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,aAAL,KAAK,cAAL,KAAK,GAAI,EAAE,CAAC;SAChC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE,CAAC;IACZ,IAAI,SAAS,CAAC,MAAM,IAAI,SAAS;QAAE,OAAO,SAAS,CAAC;IACpD,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;AAChE,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,sBAAsB,CAAC,IAAa;;IAChD,IAAI,IAAI,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC1D,MAAM,QAAQ,GAAG,sBAAsB,CAAC,IAAI,CAAC,CAAC;IAC9C,MAAM,UAAU,GAAG,MAAA,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,mCAAI,EAAE,CAAC;IAClD,IAAI,UAAU,CAAC,MAAM,GAAG,oBAAoB,CAAC,wBAAwB,EAAE,CAAC;QACpE,OAAO;YACH,SAAS,EAAE,IAAI;YACf,OAAO,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,oBAAoB,CAAC,wBAAwB,CAAC;SAC9E,CAAC;IACN,CAAC;IACD,IAAI,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,UAAU,CAA4B,CAAC;IAC7D,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;IACpC,CAAC;AACL,CAAC"}
@@ -0,0 +1,82 @@
1
+ import { SupportRoutingMatchType } from '../enums/support-routing.enum';
2
+ /** Whether one rule matches one ticket. */
3
+ export function supportRoutingRuleMatches(rule, subject) {
4
+ if (!rule.active)
5
+ return false;
6
+ if (rule.organizationId !== subject.organizationId)
7
+ return false;
8
+ switch (rule.matchType) {
9
+ case SupportRoutingMatchType.ANY:
10
+ return true;
11
+ case SupportRoutingMatchType.CATEGORY:
12
+ return !!rule.matchCategoryId && rule.matchCategoryId === subject.categoryId;
13
+ case SupportRoutingMatchType.PRIORITY:
14
+ return !!rule.matchPriority && rule.matchPriority === subject.priority;
15
+ case SupportRoutingMatchType.CHANNEL:
16
+ return !!rule.matchChannel && rule.matchChannel === subject.channel;
17
+ case SupportRoutingMatchType.COUNTRY:
18
+ return !!rule.matchCountryCode && rule.matchCountryCode === subject.countryCode;
19
+ case SupportRoutingMatchType.CONTROL_CENTRE:
20
+ return (rule.matchControlCentreId != null &&
21
+ rule.matchControlCentreId === subject.controlCentreId);
22
+ case SupportRoutingMatchType.APP_ID:
23
+ return !!rule.matchAppId && rule.matchAppId === subject.appId;
24
+ case SupportRoutingMatchType.RELATED_ENTITY_TYPE:
25
+ return (!!rule.matchRelatedEntityType &&
26
+ subject.relatedEntityTypes.includes(rule.matchRelatedEntityType));
27
+ default:
28
+ // An unrecognised match type never matches. A rule the running code
29
+ // does not understand must not silently become a catch-all.
30
+ return false;
31
+ }
32
+ }
33
+ /**
34
+ * Pick a queue.
35
+ *
36
+ * Rules are sorted by ascending `priority` and the FIRST match wins. No
37
+ * scoring, no weighting, no learning: an operator has to be able to answer "why
38
+ * did this land here?" by reading the table, and a routing decision nobody can
39
+ * explain is one nobody can fix at 2am.
40
+ *
41
+ * Ties on `priority` are broken by rule id, so the same inputs always produce
42
+ * the same answer - a routing engine whose output depends on row order is a
43
+ * routing engine that cannot be tested.
44
+ */
45
+ export function decideSupportQueue(rules, subject, fallbackQueueId) {
46
+ var _a;
47
+ const ordered = [...rules].sort((left, right) => {
48
+ if (left.priority !== right.priority)
49
+ return left.priority - right.priority;
50
+ return left.id.localeCompare(right.id);
51
+ });
52
+ for (const rule of ordered) {
53
+ if (supportRoutingRuleMatches(rule, subject)) {
54
+ return {
55
+ queueId: rule.targetQueueId,
56
+ ruleId: rule.id,
57
+ ruleName: rule.name,
58
+ forcedPriority: (_a = rule.forcePriority) !== null && _a !== void 0 ? _a : null,
59
+ };
60
+ }
61
+ }
62
+ return {
63
+ queueId: fallbackQueueId !== null && fallbackQueueId !== void 0 ? fallbackQueueId : null,
64
+ ruleId: null,
65
+ ruleName: null,
66
+ forcedPriority: null,
67
+ };
68
+ }
69
+ /**
70
+ * The priority a new ticket should start at.
71
+ *
72
+ * A rule may raise a priority and never lower one. A customer who says their
73
+ * panic button is broken has told us something a routing table cannot know
74
+ * better than they do, and quietly downgrading them is the failure mode this
75
+ * rule exists to prevent.
76
+ */
77
+ export function applyForcedPriority(requested, forced, rank) {
78
+ if (!forced)
79
+ return requested;
80
+ return rank[forced] < rank[requested] ? forced : requested;
81
+ }
82
+ //# sourceMappingURL=support-routing.util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"support-routing.util.js","sourceRoot":"","sources":["../../../../../src/modules/support/utils/support-routing.util.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AA2BxE,2CAA2C;AAC3C,MAAM,UAAU,yBAAyB,CACrC,IAAwB,EACxB,OAA8B;IAE9B,IAAI,CAAC,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC/B,IAAI,IAAI,CAAC,cAAc,KAAK,OAAO,CAAC,cAAc;QAAE,OAAO,KAAK,CAAC;IAEjE,QAAQ,IAAI,CAAC,SAAS,EAAE,CAAC;QACrB,KAAK,uBAAuB,CAAC,GAAG;YAC5B,OAAO,IAAI,CAAC;QAChB,KAAK,uBAAuB,CAAC,QAAQ;YACjC,OAAO,CAAC,CAAC,IAAI,CAAC,eAAe,IAAI,IAAI,CAAC,eAAe,KAAK,OAAO,CAAC,UAAU,CAAC;QACjF,KAAK,uBAAuB,CAAC,QAAQ;YACjC,OAAO,CAAC,CAAC,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,aAAa,KAAK,OAAO,CAAC,QAAQ,CAAC;QAC3E,KAAK,uBAAuB,CAAC,OAAO;YAChC,OAAO,CAAC,CAAC,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,YAAY,KAAK,OAAO,CAAC,OAAO,CAAC;QACxE,KAAK,uBAAuB,CAAC,OAAO;YAChC,OAAO,CAAC,CAAC,IAAI,CAAC,gBAAgB,IAAI,IAAI,CAAC,gBAAgB,KAAK,OAAO,CAAC,WAAW,CAAC;QACpF,KAAK,uBAAuB,CAAC,cAAc;YACvC,OAAO,CACH,IAAI,CAAC,oBAAoB,IAAI,IAAI;gBACjC,IAAI,CAAC,oBAAoB,KAAK,OAAO,CAAC,eAAe,CACxD,CAAC;QACN,KAAK,uBAAuB,CAAC,MAAM;YAC/B,OAAO,CAAC,CAAC,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,UAAU,KAAK,OAAO,CAAC,KAAK,CAAC;QAClE,KAAK,uBAAuB,CAAC,mBAAmB;YAC5C,OAAO,CACH,CAAC,CAAC,IAAI,CAAC,sBAAsB;gBAC7B,OAAO,CAAC,kBAAkB,CAAC,QAAQ,CAAC,IAAI,CAAC,sBAAsB,CAAC,CACnE,CAAC;QACN;YACI,oEAAoE;YACpE,4DAA4D;YAC5D,OAAO,KAAK,CAAC;IACrB,CAAC;AACL,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,kBAAkB,CAC9B,KAA2B,EAC3B,OAA8B,EAC9B,eAA+B;;IAE/B,MAAM,OAAO,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QAC5C,IAAI,IAAI,CAAC,QAAQ,KAAK,KAAK,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;QAC5E,OAAO,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QACzB,IAAI,yBAAyB,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC;YAC3C,OAAO;gBACH,OAAO,EAAE,IAAI,CAAC,aAAa;gBAC3B,MAAM,EAAE,IAAI,CAAC,EAAE;gBACf,QAAQ,EAAE,IAAI,CAAC,IAAI;gBACnB,cAAc,EAAE,MAAA,IAAI,CAAC,aAAa,mCAAI,IAAI;aAC7C,CAAC;QACN,CAAC;IACL,CAAC;IAED,OAAO;QACH,OAAO,EAAE,eAAe,aAAf,eAAe,cAAf,eAAe,GAAI,IAAI;QAChC,MAAM,EAAE,IAAI;QACZ,QAAQ,EAAE,IAAI;QACd,cAAc,EAAE,IAAI;KACvB,CAAC;AACN,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAC/B,SAAgC,EAChC,MAAgD,EAChD,IAAqD;IAErD,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAC9B,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AAC/D,CAAC"}
@@ -0,0 +1,229 @@
1
+ import { SUPPORTED_COUNTRIES_BY_CODE } from '../../country/constants/supported-countries.const';
2
+ import { DEFAULT_TIMEZONE } from '../../country/constants/country-defaults.const';
3
+ import { SUPPORT_BUSINESS_CALENDAR_DEFAULTS, SUPPORT_SLA_DEFAULTS, SUPPORT_SLA_WARNING_THRESHOLD_RATIO, } from '../constants/support-defaults.const';
4
+ import { SupportSlaClock, SupportSlaState } from '../enums/support-sla.enum';
5
+ /** Safety bound on the day-by-day walk, so a misconfigured calendar cannot hang a request. */
6
+ const MAX_CALENDAR_DAYS = 400;
7
+ /**
8
+ * The timezone a country's working hours are measured in.
9
+ *
10
+ * Read from the country configuration rather than from the server clock. Every
11
+ * supported country happens to sit at UTC+3 today; writing it this way is what
12
+ * stops that being an assumption baked into a deadline.
13
+ */
14
+ export function timezoneForCountry(countryCode) {
15
+ var _a;
16
+ if (!countryCode)
17
+ return DEFAULT_TIMEZONE;
18
+ const config = SUPPORTED_COUNTRIES_BY_CODE[countryCode];
19
+ return (_a = config === null || config === void 0 ? void 0 : config.timezone) !== null && _a !== void 0 ? _a : DEFAULT_TIMEZONE;
20
+ }
21
+ /**
22
+ * The zone's UTC offset, in minutes, at a given instant.
23
+ *
24
+ * Derived through Intl rather than hard-coded, so a zone that observes daylight
25
+ * saving is handled correctly away from the transition itself. At the exact
26
+ * hour of a transition the result can be off by the shift; that is a documented
27
+ * limitation, and it does not arise for KE, UG or TZ, none of which observe DST.
28
+ */
29
+ function zoneOffsetMinutes(instant, timeZone) {
30
+ const formatter = new Intl.DateTimeFormat('en-US', {
31
+ timeZone,
32
+ hourCycle: 'h23',
33
+ year: 'numeric',
34
+ month: '2-digit',
35
+ day: '2-digit',
36
+ hour: '2-digit',
37
+ minute: '2-digit',
38
+ second: '2-digit',
39
+ });
40
+ const parts = {};
41
+ for (const part of formatter.formatToParts(instant)) {
42
+ if (part.type !== 'literal')
43
+ parts[part.type] = part.value;
44
+ }
45
+ const asUtc = Date.UTC(Number(parts.year), Number(parts.month) - 1, Number(parts.day), Number(parts.hour), Number(parts.minute), Number(parts.second));
46
+ return Math.round((asUtc - instant.getTime()) / 60000);
47
+ }
48
+ function zonedParts(instant, timeZone) {
49
+ const offset = zoneOffsetMinutes(instant, timeZone);
50
+ const shifted = new Date(instant.getTime() + offset * 60000);
51
+ const isoWeekday = shifted.getUTCDay() === 0 ? 7 : shifted.getUTCDay();
52
+ const year = shifted.getUTCFullYear();
53
+ const month = shifted.getUTCMonth() + 1;
54
+ const day = shifted.getUTCDate();
55
+ return {
56
+ year,
57
+ month,
58
+ day,
59
+ minuteOfDay: shifted.getUTCHours() * 60 + shifted.getUTCMinutes(),
60
+ isoWeekday,
61
+ isoDate: `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`,
62
+ };
63
+ }
64
+ /** The instant at which a given local wall-clock time occurs in a zone. */
65
+ function instantFromZonedWallTime(year, month, day, minuteOfDay, timeZone) {
66
+ const naive = Date.UTC(year, month - 1, day, 0, minuteOfDay);
67
+ // Two passes: the first offset is read at the naive instant, the second at
68
+ // the corrected one, which settles any case where the correction crosses a
69
+ // transition boundary.
70
+ let offset = zoneOffsetMinutes(new Date(naive), timeZone);
71
+ let instant = naive - offset * 60000;
72
+ offset = zoneOffsetMinutes(new Date(instant), timeZone);
73
+ instant = naive - offset * 60000;
74
+ return new Date(instant);
75
+ }
76
+ /** The calendar in force, falling back to the platform default. */
77
+ export function resolveBusinessCalendar(calendar, countryCode) {
78
+ var _a, _b, _c, _d;
79
+ return {
80
+ workingDays: ((_a = calendar === null || calendar === void 0 ? void 0 : calendar.workingDays) === null || _a === void 0 ? void 0 : _a.length)
81
+ ? [...calendar.workingDays]
82
+ : [...SUPPORT_BUSINESS_CALENDAR_DEFAULTS.workingDays],
83
+ startMinuteOfDay: (_b = calendar === null || calendar === void 0 ? void 0 : calendar.startMinuteOfDay) !== null && _b !== void 0 ? _b : SUPPORT_BUSINESS_CALENDAR_DEFAULTS.startMinuteOfDay,
84
+ endMinuteOfDay: (_c = calendar === null || calendar === void 0 ? void 0 : calendar.endMinuteOfDay) !== null && _c !== void 0 ? _c : SUPPORT_BUSINESS_CALENDAR_DEFAULTS.endMinuteOfDay,
85
+ timezone: (_d = calendar === null || calendar === void 0 ? void 0 : calendar.timezone) !== null && _d !== void 0 ? _d : timezoneForCountry(countryCode),
86
+ holidays: (calendar === null || calendar === void 0 ? void 0 : calendar.holidays) ? [...calendar.holidays] : [],
87
+ };
88
+ }
89
+ function isWorkingDay(parts, calendar) {
90
+ if (!calendar.workingDays.includes(parts.isoWeekday))
91
+ return false;
92
+ return !calendar.holidays.includes(parts.isoDate);
93
+ }
94
+ /**
95
+ * Add working minutes to an instant.
96
+ *
97
+ * Walks day by day rather than doing arithmetic on a whole-week average,
98
+ * because holidays and a partial first day make the average wrong exactly when
99
+ * it matters - the ticket raised at 16:55 on the day before a public holiday.
100
+ *
101
+ * A calendar with no working days, or a zero-length window, degrades to
102
+ * calendar time rather than looping: a misconfigured calendar must not be able
103
+ * to hang the request that creates a ticket.
104
+ */
105
+ export function addBusinessMinutes(start, minutes, calendar) {
106
+ if (minutes <= 0)
107
+ return new Date(start.getTime());
108
+ const windowLength = calendar.endMinuteOfDay - calendar.startMinuteOfDay;
109
+ if (windowLength <= 0 || calendar.workingDays.length === 0) {
110
+ return new Date(start.getTime() + minutes * 60000);
111
+ }
112
+ let remaining = minutes;
113
+ let cursor = new Date(start.getTime());
114
+ for (let guard = 0; guard < MAX_CALENDAR_DAYS; guard += 1) {
115
+ const parts = zonedParts(cursor, calendar.timezone);
116
+ if (!isWorkingDay(parts, calendar)) {
117
+ cursor = nextDayWindowStart(parts, calendar);
118
+ continue;
119
+ }
120
+ const dayStart = instantFromZonedWallTime(parts.year, parts.month, parts.day, calendar.startMinuteOfDay, calendar.timezone);
121
+ const dayEnd = instantFromZonedWallTime(parts.year, parts.month, parts.day, calendar.endMinuteOfDay, calendar.timezone);
122
+ if (cursor.getTime() < dayStart.getTime())
123
+ cursor = dayStart;
124
+ if (cursor.getTime() >= dayEnd.getTime()) {
125
+ cursor = nextDayWindowStart(parts, calendar);
126
+ continue;
127
+ }
128
+ const availableMinutes = (dayEnd.getTime() - cursor.getTime()) / 60000;
129
+ if (remaining <= availableMinutes) {
130
+ return new Date(cursor.getTime() + remaining * 60000);
131
+ }
132
+ remaining -= availableMinutes;
133
+ cursor = nextDayWindowStart(parts, calendar);
134
+ }
135
+ // Past the safety bound the calendar is unusable; do not silently return a
136
+ // wrong deadline, return an honest calendar-time one.
137
+ return new Date(start.getTime() + minutes * 60000);
138
+ }
139
+ function nextDayWindowStart(parts, calendar) {
140
+ const nextDay = new Date(Date.UTC(parts.year, parts.month - 1, parts.day + 1));
141
+ return instantFromZonedWallTime(nextDay.getUTCFullYear(), nextDay.getUTCMonth() + 1, nextDay.getUTCDate(), calendar.startMinuteOfDay, calendar.timezone);
142
+ }
143
+ /** Working minutes between two instants. Used to account for paused time. */
144
+ export function businessMinutesBetween(from, to, calendar) {
145
+ if (to.getTime() <= from.getTime())
146
+ return 0;
147
+ const windowLength = calendar.endMinuteOfDay - calendar.startMinuteOfDay;
148
+ if (windowLength <= 0 || calendar.workingDays.length === 0) {
149
+ return (to.getTime() - from.getTime()) / 60000;
150
+ }
151
+ let total = 0;
152
+ let cursor = new Date(from.getTime());
153
+ for (let guard = 0; guard < MAX_CALENDAR_DAYS; guard += 1) {
154
+ if (cursor.getTime() >= to.getTime())
155
+ break;
156
+ const parts = zonedParts(cursor, calendar.timezone);
157
+ if (!isWorkingDay(parts, calendar)) {
158
+ cursor = nextDayWindowStart(parts, calendar);
159
+ continue;
160
+ }
161
+ const dayStart = instantFromZonedWallTime(parts.year, parts.month, parts.day, calendar.startMinuteOfDay, calendar.timezone);
162
+ const dayEnd = instantFromZonedWallTime(parts.year, parts.month, parts.day, calendar.endMinuteOfDay, calendar.timezone);
163
+ const segmentStart = Math.max(cursor.getTime(), dayStart.getTime());
164
+ const segmentEnd = Math.min(to.getTime(), dayEnd.getTime());
165
+ if (segmentEnd > segmentStart)
166
+ total += (segmentEnd - segmentStart) / 60000;
167
+ cursor = nextDayWindowStart(parts, calendar);
168
+ }
169
+ return total;
170
+ }
171
+ /** The targets in force for a priority: queue policy, then organization policy, then defaults. */
172
+ export function resolveSlaTargets(priority, policies) {
173
+ var _a;
174
+ for (const policy of policies) {
175
+ const target = (_a = policy === null || policy === void 0 ? void 0 : policy.targets) === null || _a === void 0 ? void 0 : _a.find((candidate) => candidate.priority === priority);
176
+ if (target) {
177
+ return {
178
+ firstResponseMinutes: target.firstResponseMinutes,
179
+ resolutionMinutes: target.resolutionMinutes,
180
+ clock: target.clock,
181
+ };
182
+ }
183
+ }
184
+ return SUPPORT_SLA_DEFAULTS[priority];
185
+ }
186
+ /** When a target elapses, measured on the right clock. */
187
+ export function computeSlaDueAt(start, minutes, clock, calendar) {
188
+ if (clock === SupportSlaClock.CALENDAR) {
189
+ return new Date(start.getTime() + minutes * 60000);
190
+ }
191
+ return addBusinessMinutes(start, minutes, calendar);
192
+ }
193
+ /**
194
+ * Where a clock stands right now.
195
+ *
196
+ * Pure, and takes `now` as an argument rather than reading the clock, so the
197
+ * scheduler and the read path can never disagree about what time it is and so
198
+ * the whole thing is testable without freezing time.
199
+ */
200
+ export function evaluateSlaState(input) {
201
+ var _a, _b;
202
+ const { now, dueAt, metAt, paused, stopped } = input;
203
+ if (metAt)
204
+ return SupportSlaState.MET;
205
+ if (!dueAt)
206
+ return SupportSlaState.NOT_APPLICABLE;
207
+ if (stopped) {
208
+ return now.getTime() > dueAt.getTime() ? SupportSlaState.BREACHED : SupportSlaState.MET;
209
+ }
210
+ if (paused)
211
+ return SupportSlaState.PAUSED;
212
+ if (now.getTime() > dueAt.getTime())
213
+ return SupportSlaState.BREACHED;
214
+ const targetMinutes = (_a = input.targetMinutes) !== null && _a !== void 0 ? _a : null;
215
+ const ratio = (_b = input.warningRatio) !== null && _b !== void 0 ? _b : SUPPORT_SLA_WARNING_THRESHOLD_RATIO;
216
+ if (targetMinutes && targetMinutes > 0) {
217
+ const warningAt = dueAt.getTime() - targetMinutes * (1 - ratio) * 60000;
218
+ if (now.getTime() >= warningAt)
219
+ return SupportSlaState.WARNING;
220
+ }
221
+ return SupportSlaState.ON_TRACK;
222
+ }
223
+ /** Minutes left before a deadline; negative once it has passed. */
224
+ export function minutesUntil(now, dueAt) {
225
+ if (!dueAt)
226
+ return null;
227
+ return Math.round((dueAt.getTime() - now.getTime()) / 60000);
228
+ }
229
+ //# sourceMappingURL=support-sla.util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"support-sla.util.js","sourceRoot":"","sources":["../../../../../src/modules/support/utils/support-sla.util.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ import { CUSTOMER_FEEDBACK_TRANSITIONS, } from '../enums/support-feedback.enum';
2
+ import { ACTIVE_SUPPORT_TICKET_STATUSES, SLA_PAUSED_SUPPORT_TICKET_STATUSES, SLA_STOPPED_SUPPORT_TICKET_STATUSES, SUPPORT_TICKET_TRANSITIONS, SupportTicketStatus, TERMINAL_SUPPORT_TICKET_STATUSES, } from '../enums/support-ticket-status.enum';
3
+ /**
4
+ * Whether a ticket may move from one state to another.
5
+ *
6
+ * The single authority. Controllers, services, webhook handlers and the
7
+ * reconciliation job all ask this rather than each carrying their own idea of
8
+ * what is legal - which is what stops a provider webhook quietly doing
9
+ * something no Clarion user could do.
10
+ *
11
+ * A move to the SAME state is allowed and is a no-op, because a provider that
12
+ * re-sends its current status should not be an error.
13
+ */
14
+ export function canTransitionSupportTicket(from, to) {
15
+ if (from === to)
16
+ return true;
17
+ const allowed = SUPPORT_TICKET_TRANSITIONS[from];
18
+ return Array.isArray(allowed) && allowed.includes(to);
19
+ }
20
+ /** The states a ticket may legally move to right now. Drives the admin's buttons. */
21
+ export function allowedSupportTicketTransitions(from) {
22
+ var _a;
23
+ return (_a = SUPPORT_TICKET_TRANSITIONS[from]) !== null && _a !== void 0 ? _a : [];
24
+ }
25
+ export function isTerminalSupportTicketStatus(status) {
26
+ return TERMINAL_SUPPORT_TICKET_STATUSES.includes(status);
27
+ }
28
+ export function isActiveSupportTicketStatus(status) {
29
+ return ACTIVE_SUPPORT_TICKET_STATUSES.includes(status);
30
+ }
31
+ /** True while an SLA clock should be stopped but may still resume. */
32
+ export function isSlaPausedStatus(status) {
33
+ return SLA_PAUSED_SUPPORT_TICKET_STATUSES.includes(status);
34
+ }
35
+ /** True once the clocks stop for good. */
36
+ export function isSlaStoppedStatus(status) {
37
+ return SLA_STOPPED_SUPPORT_TICKET_STATUSES.includes(status);
38
+ }
39
+ /**
40
+ * Whether a customer may reply on a ticket in this state.
41
+ *
42
+ * A CANCELLED ticket is closed to further conversation; everything else, CLOSED
43
+ * included, accepts a reply - and a reply to a closed ticket reopens it, which
44
+ * is what a customer expects and what stops them raising a duplicate.
45
+ */
46
+ export function customerMayReply(status) {
47
+ return status !== SupportTicketStatus.CANCELLED;
48
+ }
49
+ /**
50
+ * The state a customer reply should move a ticket into, or null to leave it.
51
+ *
52
+ * CLOSED becomes REOPENED, which is counted. RESOLVED goes back to IN_PROGRESS,
53
+ * which is not - the ticket was never finished from the customer's point of
54
+ * view, and calling that a reopen would inflate the reopen rate with tickets
55
+ * that simply were not fixed yet. WAITING_ON_CUSTOMER becomes IN_PROGRESS,
56
+ * because the wait is over.
57
+ */
58
+ export function statusAfterCustomerReply(status) {
59
+ switch (status) {
60
+ case SupportTicketStatus.CLOSED:
61
+ return SupportTicketStatus.REOPENED;
62
+ case SupportTicketStatus.RESOLVED:
63
+ case SupportTicketStatus.WAITING_ON_CUSTOMER:
64
+ return SupportTicketStatus.IN_PROGRESS;
65
+ default:
66
+ return null;
67
+ }
68
+ }
69
+ /** Whether a piece of feedback may move from one state to another. */
70
+ export function canTransitionCustomerFeedback(from, to) {
71
+ if (from === to)
72
+ return true;
73
+ const allowed = CUSTOMER_FEEDBACK_TRANSITIONS[from];
74
+ return Array.isArray(allowed) && allowed.includes(to);
75
+ }
76
+ /** The states a piece of feedback may legally move to right now. */
77
+ export function allowedCustomerFeedbackTransitions(from) {
78
+ var _a;
79
+ return (_a = CUSTOMER_FEEDBACK_TRANSITIONS[from]) !== null && _a !== void 0 ? _a : [];
80
+ }
81
+ //# sourceMappingURL=support-status.util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"support-status.util.js","sourceRoot":"","sources":["../../../../../src/modules/support/utils/support-status.util.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,6BAA6B,GAEhC,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACH,8BAA8B,EAC9B,kCAAkC,EAClC,mCAAmC,EACnC,0BAA0B,EAC1B,mBAAmB,EACnB,gCAAgC,GACnC,MAAM,qCAAqC,CAAC;AAE7C;;;;;;;;;;GAUG;AACH,MAAM,UAAU,0BAA0B,CACtC,IAAyB,EACzB,EAAuB;IAEvB,IAAI,IAAI,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAC7B,MAAM,OAAO,GAAG,0BAA0B,CAAC,IAAI,CAAC,CAAC;IACjD,OAAO,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AAC1D,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,+BAA+B,CAC3C,IAAyB;;IAEzB,OAAO,MAAA,0BAA0B,CAAC,IAAI,CAAC,mCAAI,EAAE,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,6BAA6B,CAAC,MAA2B;IACrE,OAAO,gCAAgC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,MAA2B;IACnE,OAAO,8BAA8B,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC3D,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,iBAAiB,CAAC,MAA2B;IACzD,OAAO,kCAAkC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC/D,CAAC;AAED,0CAA0C;AAC1C,MAAM,UAAU,kBAAkB,CAAC,MAA2B;IAC1D,OAAO,mCAAmC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAChE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAA2B;IACxD,OAAO,MAAM,KAAK,mBAAmB,CAAC,SAAS,CAAC;AACpD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,wBAAwB,CACpC,MAA2B;IAE3B,QAAQ,MAAM,EAAE,CAAC;QACb,KAAK,mBAAmB,CAAC,MAAM;YAC3B,OAAO,mBAAmB,CAAC,QAAQ,CAAC;QACxC,KAAK,mBAAmB,CAAC,QAAQ,CAAC;QAClC,KAAK,mBAAmB,CAAC,mBAAmB;YACxC,OAAO,mBAAmB,CAAC,WAAW,CAAC;QAC3C;YACI,OAAO,IAAI,CAAC;IACpB,CAAC;AACL,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,6BAA6B,CACzC,IAA4B,EAC5B,EAA0B;IAE1B,IAAI,IAAI,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAC7B,MAAM,OAAO,GAAG,6BAA6B,CAAC,IAAI,CAAC,CAAC;IACpD,OAAO,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AAC1D,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,kCAAkC,CAC9C,IAA4B;;IAE5B,OAAO,MAAA,6BAA6B,CAAC,IAAI,CAAC,mCAAI,EAAE,CAAC;AACrD,CAAC"}