@aglyn/plugins-crm 1.0.0-beta.143

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 (669) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +7 -0
  3. package/package.json +57 -0
  4. package/src/index.d.ts +21 -0
  5. package/src/index.js +29 -0
  6. package/src/index.js.map +1 -0
  7. package/src/lib/components/activity-list.d.ts +98 -0
  8. package/src/lib/components/activity-list.js +321 -0
  9. package/src/lib/components/activity-list.js.map +1 -0
  10. package/src/lib/components/activity-queries.d.ts +117 -0
  11. package/src/lib/components/activity-queries.js +198 -0
  12. package/src/lib/components/activity-queries.js.map +1 -0
  13. package/src/lib/components/add-to-list-button.d.ts +25 -0
  14. package/src/lib/components/add-to-list-button.js +64 -0
  15. package/src/lib/components/add-to-list-button.js.map +1 -0
  16. package/src/lib/components/add-to-list-dialog.d.ts +24 -0
  17. package/src/lib/components/add-to-list-dialog.js +327 -0
  18. package/src/lib/components/add-to-list-dialog.js.map +1 -0
  19. package/src/lib/components/assignment-rule-drawer.d.ts +43 -0
  20. package/src/lib/components/assignment-rule-drawer.js +293 -0
  21. package/src/lib/components/assignment-rule-drawer.js.map +1 -0
  22. package/src/lib/components/companies-bulk-bar.d.ts +29 -0
  23. package/src/lib/components/companies-bulk-bar.js +274 -0
  24. package/src/lib/components/companies-bulk-bar.js.map +1 -0
  25. package/src/lib/components/companies-section.d.ts +54 -0
  26. package/src/lib/components/companies-section.js +522 -0
  27. package/src/lib/components/companies-section.js.map +1 -0
  28. package/src/lib/components/company-contacts-card.d.ts +49 -0
  29. package/src/lib/components/company-contacts-card.js +435 -0
  30. package/src/lib/components/company-contacts-card.js.map +1 -0
  31. package/src/lib/components/company-deals-card.d.ts +20 -0
  32. package/src/lib/components/company-deals-card.js +38 -0
  33. package/src/lib/components/company-deals-card.js.map +1 -0
  34. package/src/lib/components/company-detail-page.d.ts +20 -0
  35. package/src/lib/components/company-detail-page.js +165 -0
  36. package/src/lib/components/company-detail-page.js.map +1 -0
  37. package/src/lib/components/company-edit-drawer.d.ts +65 -0
  38. package/src/lib/components/company-edit-drawer.js +433 -0
  39. package/src/lib/components/company-edit-drawer.js.map +1 -0
  40. package/src/lib/components/company-import-drawer.d.ts +42 -0
  41. package/src/lib/components/company-import-drawer.js +99 -0
  42. package/src/lib/components/company-import-drawer.js.map +1 -0
  43. package/src/lib/components/company-picker.d.ts +127 -0
  44. package/src/lib/components/company-picker.js +323 -0
  45. package/src/lib/components/company-picker.js.map +1 -0
  46. package/src/lib/components/company-properties-card.d.ts +47 -0
  47. package/src/lib/components/company-properties-card.js +289 -0
  48. package/src/lib/components/company-properties-card.js.map +1 -0
  49. package/src/lib/components/contact-address-fields.d.ts +29 -0
  50. package/src/lib/components/contact-address-fields.js +149 -0
  51. package/src/lib/components/contact-address-fields.js.map +1 -0
  52. package/src/lib/components/contact-associations-card.d.ts +72 -0
  53. package/src/lib/components/contact-associations-card.js +296 -0
  54. package/src/lib/components/contact-associations-card.js.map +1 -0
  55. package/src/lib/components/contact-custom-columns.d.ts +38 -0
  56. package/src/lib/components/contact-custom-columns.js +102 -0
  57. package/src/lib/components/contact-custom-columns.js.map +1 -0
  58. package/src/lib/components/contact-custom-fields-card.d.ts +39 -0
  59. package/src/lib/components/contact-custom-fields-card.js +204 -0
  60. package/src/lib/components/contact-custom-fields-card.js.map +1 -0
  61. package/src/lib/components/contact-deals-card.d.ts +20 -0
  62. package/src/lib/components/contact-deals-card.js +38 -0
  63. package/src/lib/components/contact-deals-card.js.map +1 -0
  64. package/src/lib/components/contact-detail-page.d.ts +46 -0
  65. package/src/lib/components/contact-detail-page.js +535 -0
  66. package/src/lib/components/contact-detail-page.js.map +1 -0
  67. package/src/lib/components/contact-duplicates-card.d.ts +42 -0
  68. package/src/lib/components/contact-duplicates-card.js +167 -0
  69. package/src/lib/components/contact-duplicates-card.js.map +1 -0
  70. package/src/lib/components/contact-field-drawer.d.ts +64 -0
  71. package/src/lib/components/contact-field-drawer.js +251 -0
  72. package/src/lib/components/contact-field-drawer.js.map +1 -0
  73. package/src/lib/components/contact-import-drawer.d.ts +46 -0
  74. package/src/lib/components/contact-import-drawer.js +149 -0
  75. package/src/lib/components/contact-import-drawer.js.map +1 -0
  76. package/src/lib/components/contact-known-by-card.d.ts +39 -0
  77. package/src/lib/components/contact-known-by-card.js +142 -0
  78. package/src/lib/components/contact-known-by-card.js.map +1 -0
  79. package/src/lib/components/contact-list-columns.d.ts +53 -0
  80. package/src/lib/components/contact-list-columns.js +288 -0
  81. package/src/lib/components/contact-list-columns.js.map +1 -0
  82. package/src/lib/components/contact-merge-dialog.d.ts +79 -0
  83. package/src/lib/components/contact-merge-dialog.js +405 -0
  84. package/src/lib/components/contact-merge-dialog.js.map +1 -0
  85. package/src/lib/components/contact-properties-card.d.ts +89 -0
  86. package/src/lib/components/contact-properties-card.js +492 -0
  87. package/src/lib/components/contact-properties-card.js.map +1 -0
  88. package/src/lib/components/contact-timeline-card.d.ts +68 -0
  89. package/src/lib/components/contact-timeline-card.js +435 -0
  90. package/src/lib/components/contact-timeline-card.js.map +1 -0
  91. package/src/lib/components/contacts-bulk-bar.d.ts +91 -0
  92. package/src/lib/components/contacts-bulk-bar.js +493 -0
  93. package/src/lib/components/contacts-bulk-bar.js.map +1 -0
  94. package/src/lib/components/contacts-section.d.ts +26 -0
  95. package/src/lib/components/contacts-section.js +1025 -0
  96. package/src/lib/components/contacts-section.js.map +1 -0
  97. package/src/lib/components/copy-capture-address-button.d.ts +33 -0
  98. package/src/lib/components/copy-capture-address-button.js +60 -0
  99. package/src/lib/components/copy-capture-address-button.js.map +1 -0
  100. package/src/lib/components/crm-attribution-zone.d.ts +38 -0
  101. package/src/lib/components/crm-attribution-zone.js +36 -0
  102. package/src/lib/components/crm-attribution-zone.js.map +1 -0
  103. package/src/lib/components/crm-booking-zone.d.ts +61 -0
  104. package/src/lib/components/crm-booking-zone.js +50 -0
  105. package/src/lib/components/crm-booking-zone.js.map +1 -0
  106. package/src/lib/components/crm-bulk-bar-frame.d.ts +47 -0
  107. package/src/lib/components/crm-bulk-bar-frame.js +137 -0
  108. package/src/lib/components/crm-bulk-bar-frame.js.map +1 -0
  109. package/src/lib/components/crm-call-actions.d.ts +65 -0
  110. package/src/lib/components/crm-call-actions.js +110 -0
  111. package/src/lib/components/crm-call-actions.js.map +1 -0
  112. package/src/lib/components/crm-column-menu.d.ts +21 -0
  113. package/src/lib/components/crm-column-menu.js +103 -0
  114. package/src/lib/components/crm-column-menu.js.map +1 -0
  115. package/src/lib/components/crm-console-page.d.ts +19 -0
  116. package/src/lib/components/crm-console-page.js +151 -0
  117. package/src/lib/components/crm-console-page.js.map +1 -0
  118. package/src/lib/components/crm-console-sections.d.ts +52 -0
  119. package/src/lib/components/crm-console-sections.js +83 -0
  120. package/src/lib/components/crm-console-sections.js.map +1 -0
  121. package/src/lib/components/crm-custom-field-control.d.ts +26 -0
  122. package/src/lib/components/crm-custom-field-control.js +131 -0
  123. package/src/lib/components/crm-custom-field-control.js.map +1 -0
  124. package/src/lib/components/crm-email-template-tools.d.ts +69 -0
  125. package/src/lib/components/crm-email-template-tools.js +240 -0
  126. package/src/lib/components/crm-email-template-tools.js.map +1 -0
  127. package/src/lib/components/crm-export-all-button.d.ts +39 -0
  128. package/src/lib/components/crm-export-all-button.js +98 -0
  129. package/src/lib/components/crm-export-all-button.js.map +1 -0
  130. package/src/lib/components/crm-filter-bar.d.ts +53 -0
  131. package/src/lib/components/crm-filter-bar.js +294 -0
  132. package/src/lib/components/crm-filter-bar.js.map +1 -0
  133. package/src/lib/components/crm-glance-card.d.ts +54 -0
  134. package/src/lib/components/crm-glance-card.js +219 -0
  135. package/src/lib/components/crm-glance-card.js.map +1 -0
  136. package/src/lib/components/crm-next-activity-column.d.ts +35 -0
  137. package/src/lib/components/crm-next-activity-column.js +100 -0
  138. package/src/lib/components/crm-next-activity-column.js.map +1 -0
  139. package/src/lib/components/crm-record-header.d.ts +97 -0
  140. package/src/lib/components/crm-record-header.js +160 -0
  141. package/src/lib/components/crm-record-header.js.map +1 -0
  142. package/src/lib/components/crm-record-insights-zone.d.ts +50 -0
  143. package/src/lib/components/crm-record-insights-zone.js +169 -0
  144. package/src/lib/components/crm-record-insights-zone.js.map +1 -0
  145. package/src/lib/components/crm-record-picker.d.ts +42 -0
  146. package/src/lib/components/crm-record-picker.js +104 -0
  147. package/src/lib/components/crm-record-picker.js.map +1 -0
  148. package/src/lib/components/crm-send-email-button.d.ts +39 -0
  149. package/src/lib/components/crm-send-email-button.js +81 -0
  150. package/src/lib/components/crm-send-email-button.js.map +1 -0
  151. package/src/lib/components/crm-send-email-dialog.d.ts +61 -0
  152. package/src/lib/components/crm-send-email-dialog.js +851 -0
  153. package/src/lib/components/crm-send-email-dialog.js.map +1 -0
  154. package/src/lib/components/crm-site-picker.d.ts +61 -0
  155. package/src/lib/components/crm-site-picker.js +99 -0
  156. package/src/lib/components/crm-site-picker.js.map +1 -0
  157. package/src/lib/components/crm-suite-lock.d.ts +29 -0
  158. package/src/lib/components/crm-suite-lock.js +114 -0
  159. package/src/lib/components/crm-suite-lock.js.map +1 -0
  160. package/src/lib/components/crm-tasks-due-card.d.ts +42 -0
  161. package/src/lib/components/crm-tasks-due-card.js +225 -0
  162. package/src/lib/components/crm-tasks-due-card.js.map +1 -0
  163. package/src/lib/components/crm-views-control.d.ts +65 -0
  164. package/src/lib/components/crm-views-control.js +518 -0
  165. package/src/lib/components/crm-views-control.js.map +1 -0
  166. package/src/lib/components/csv-import-drawer.d.ts +132 -0
  167. package/src/lib/components/csv-import-drawer.js +746 -0
  168. package/src/lib/components/csv-import-drawer.js.map +1 -0
  169. package/src/lib/components/deal-board.d.ts +70 -0
  170. package/src/lib/components/deal-board.js +478 -0
  171. package/src/lib/components/deal-board.js.map +1 -0
  172. package/src/lib/components/deal-detail-page.d.ts +18 -0
  173. package/src/lib/components/deal-detail-page.js +323 -0
  174. package/src/lib/components/deal-detail-page.js.map +1 -0
  175. package/src/lib/components/deal-edit-drawer.d.ts +59 -0
  176. package/src/lib/components/deal-edit-drawer.js +640 -0
  177. package/src/lib/components/deal-edit-drawer.js.map +1 -0
  178. package/src/lib/components/deal-import-drawer.d.ts +24 -0
  179. package/src/lib/components/deal-import-drawer.js +83 -0
  180. package/src/lib/components/deal-import-drawer.js.map +1 -0
  181. package/src/lib/components/deal-products-card.d.ts +48 -0
  182. package/src/lib/components/deal-products-card.js +614 -0
  183. package/src/lib/components/deal-products-card.js.map +1 -0
  184. package/src/lib/components/deal-properties-card.d.ts +29 -0
  185. package/src/lib/components/deal-properties-card.js +144 -0
  186. package/src/lib/components/deal-properties-card.js.map +1 -0
  187. package/src/lib/components/deal-stage-card.d.ts +24 -0
  188. package/src/lib/components/deal-stage-card.js +199 -0
  189. package/src/lib/components/deal-stage-card.js.map +1 -0
  190. package/src/lib/components/deals-bulk-bar.d.ts +31 -0
  191. package/src/lib/components/deals-bulk-bar.js +340 -0
  192. package/src/lib/components/deals-bulk-bar.js.map +1 -0
  193. package/src/lib/components/deals-section.d.ts +49 -0
  194. package/src/lib/components/deals-section.js +767 -0
  195. package/src/lib/components/deals-section.js.map +1 -0
  196. package/src/lib/components/email-capture-card.d.ts +42 -0
  197. package/src/lib/components/email-capture-card.js +134 -0
  198. package/src/lib/components/email-capture-card.js.map +1 -0
  199. package/src/lib/components/email-template-drawer.d.ts +33 -0
  200. package/src/lib/components/email-template-drawer.js +209 -0
  201. package/src/lib/components/email-template-drawer.js.map +1 -0
  202. package/src/lib/components/email-templates-card.d.ts +33 -0
  203. package/src/lib/components/email-templates-card.js +370 -0
  204. package/src/lib/components/email-templates-card.js.map +1 -0
  205. package/src/lib/components/erase-person-action.d.ts +92 -0
  206. package/src/lib/components/erase-person-action.js +261 -0
  207. package/src/lib/components/erase-person-action.js.map +1 -0
  208. package/src/lib/components/fields-section.d.ts +37 -0
  209. package/src/lib/components/fields-section.js +641 -0
  210. package/src/lib/components/fields-section.js.map +1 -0
  211. package/src/lib/components/form-contact-fields-card.d.ts +46 -0
  212. package/src/lib/components/form-contact-fields-card.js +253 -0
  213. package/src/lib/components/form-contact-fields-card.js.map +1 -0
  214. package/src/lib/components/insert-link-at-caret.d.ts +28 -0
  215. package/src/lib/components/insert-link-at-caret.js +37 -0
  216. package/src/lib/components/insert-link-at-caret.js.map +1 -0
  217. package/src/lib/components/lead-convert-dialog.d.ts +32 -0
  218. package/src/lib/components/lead-convert-dialog.js +493 -0
  219. package/src/lib/components/lead-convert-dialog.js.map +1 -0
  220. package/src/lib/components/lead-detail-page.d.ts +16 -0
  221. package/src/lib/components/lead-detail-page.js +163 -0
  222. package/src/lib/components/lead-detail-page.js.map +1 -0
  223. package/src/lib/components/lead-history-card.d.ts +32 -0
  224. package/src/lib/components/lead-history-card.js +165 -0
  225. package/src/lib/components/lead-history-card.js.map +1 -0
  226. package/src/lib/components/lead-import-drawer.d.ts +24 -0
  227. package/src/lib/components/lead-import-drawer.js +95 -0
  228. package/src/lib/components/lead-import-drawer.js.map +1 -0
  229. package/src/lib/components/lead-owner-select.d.ts +35 -0
  230. package/src/lib/components/lead-owner-select.js +91 -0
  231. package/src/lib/components/lead-owner-select.js.map +1 -0
  232. package/src/lib/components/lead-properties-card.d.ts +61 -0
  233. package/src/lib/components/lead-properties-card.js +395 -0
  234. package/src/lib/components/lead-properties-card.js.map +1 -0
  235. package/src/lib/components/lead-status-chip.d.ts +12 -0
  236. package/src/lib/components/lead-status-chip.js +43 -0
  237. package/src/lib/components/lead-status-chip.js.map +1 -0
  238. package/src/lib/components/lead-surfaces-note.d.ts +126 -0
  239. package/src/lib/components/lead-surfaces-note.js +262 -0
  240. package/src/lib/components/lead-surfaces-note.js.map +1 -0
  241. package/src/lib/components/lead-unqualify-dialog.d.ts +24 -0
  242. package/src/lib/components/lead-unqualify-dialog.js +125 -0
  243. package/src/lib/components/lead-unqualify-dialog.js.map +1 -0
  244. package/src/lib/components/leads-bulk-bar.d.ts +58 -0
  245. package/src/lib/components/leads-bulk-bar.js +307 -0
  246. package/src/lib/components/leads-bulk-bar.js.map +1 -0
  247. package/src/lib/components/leads-section.d.ts +23 -0
  248. package/src/lib/components/leads-section.js +664 -0
  249. package/src/lib/components/leads-section.js.map +1 -0
  250. package/src/lib/components/linked-deals-card.d.ts +32 -0
  251. package/src/lib/components/linked-deals-card.js +163 -0
  252. package/src/lib/components/linked-deals-card.js.map +1 -0
  253. package/src/lib/components/log-activity-dialog.d.ts +60 -0
  254. package/src/lib/components/log-activity-dialog.js +368 -0
  255. package/src/lib/components/log-activity-dialog.js.map +1 -0
  256. package/src/lib/components/lost-reason-dialog.d.ts +26 -0
  257. package/src/lib/components/lost-reason-dialog.js +95 -0
  258. package/src/lib/components/lost-reason-dialog.js.map +1 -0
  259. package/src/lib/components/new-contact-drawer.d.ts +78 -0
  260. package/src/lib/components/new-contact-drawer.js +343 -0
  261. package/src/lib/components/new-contact-drawer.js.map +1 -0
  262. package/src/lib/components/org-lead-surfaces-note.d.ts +57 -0
  263. package/src/lib/components/org-lead-surfaces-note.js +172 -0
  264. package/src/lib/components/org-lead-surfaces-note.js.map +1 -0
  265. package/src/lib/components/owner-avatar.d.ts +18 -0
  266. package/src/lib/components/owner-avatar.js +61 -0
  267. package/src/lib/components/owner-avatar.js.map +1 -0
  268. package/src/lib/components/pipeline-stages-dialog.d.ts +43 -0
  269. package/src/lib/components/pipeline-stages-dialog.js +339 -0
  270. package/src/lib/components/pipeline-stages-dialog.js.map +1 -0
  271. package/src/lib/components/pipelines-dialog.d.ts +55 -0
  272. package/src/lib/components/pipelines-dialog.js +517 -0
  273. package/src/lib/components/pipelines-dialog.js.map +1 -0
  274. package/src/lib/components/recent-activity-feed.d.ts +39 -0
  275. package/src/lib/components/recent-activity-feed.js +103 -0
  276. package/src/lib/components/recent-activity-feed.js.map +1 -0
  277. package/src/lib/components/recipes-card.d.ts +39 -0
  278. package/src/lib/components/recipes-card.js +485 -0
  279. package/src/lib/components/recipes-card.js.map +1 -0
  280. package/src/lib/components/record-activity-card.d.ts +26 -0
  281. package/src/lib/components/record-activity-card.js +121 -0
  282. package/src/lib/components/record-activity-card.js.map +1 -0
  283. package/src/lib/components/record-files-card.d.ts +67 -0
  284. package/src/lib/components/record-files-card.js +290 -0
  285. package/src/lib/components/record-files-card.js.map +1 -0
  286. package/src/lib/components/record-tasks-card.d.ts +32 -0
  287. package/src/lib/components/record-tasks-card.js +306 -0
  288. package/src/lib/components/record-tasks-card.js.map +1 -0
  289. package/src/lib/components/reports/activity-card.d.ts +57 -0
  290. package/src/lib/components/reports/activity-card.js +281 -0
  291. package/src/lib/components/reports/activity-card.js.map +1 -0
  292. package/src/lib/components/reports/closed-deals-card.d.ts +20 -0
  293. package/src/lib/components/reports/closed-deals-card.js +194 -0
  294. package/src/lib/components/reports/closed-deals-card.js.map +1 -0
  295. package/src/lib/components/reports/contacts-mix-card.d.ts +27 -0
  296. package/src/lib/components/reports/contacts-mix-card.js +161 -0
  297. package/src/lib/components/reports/contacts-mix-card.js.map +1 -0
  298. package/src/lib/components/reports/contacts-trend-card.d.ts +31 -0
  299. package/src/lib/components/reports/contacts-trend-card.js +144 -0
  300. package/src/lib/components/reports/contacts-trend-card.js.map +1 -0
  301. package/src/lib/components/reports/forecast-card.d.ts +22 -0
  302. package/src/lib/components/reports/forecast-card.js +251 -0
  303. package/src/lib/components/reports/forecast-card.js.map +1 -0
  304. package/src/lib/components/reports/lead-counts.d.ts +32 -0
  305. package/src/lib/components/reports/lead-counts.js +44 -0
  306. package/src/lib/components/reports/lead-counts.js.map +1 -0
  307. package/src/lib/components/reports/lead-funnel-card.d.ts +38 -0
  308. package/src/lib/components/reports/lead-funnel-card.js +375 -0
  309. package/src/lib/components/reports/lead-funnel-card.js.map +1 -0
  310. package/src/lib/components/reports/pipeline-card.d.ts +19 -0
  311. package/src/lib/components/reports/pipeline-card.js +338 -0
  312. package/src/lib/components/reports/pipeline-card.js.map +1 -0
  313. package/src/lib/components/reports/report-bar-chart.d.ts +39 -0
  314. package/src/lib/components/reports/report-bar-chart.js +133 -0
  315. package/src/lib/components/reports/report-bar-chart.js.map +1 -0
  316. package/src/lib/components/reports/report-breakdown.d.ts +32 -0
  317. package/src/lib/components/reports/report-breakdown.js +105 -0
  318. package/src/lib/components/reports/report-breakdown.js.map +1 -0
  319. package/src/lib/components/reports/report-export.d.ts +37 -0
  320. package/src/lib/components/reports/report-export.js +66 -0
  321. package/src/lib/components/reports/report-export.js.map +1 -0
  322. package/src/lib/components/reports/report-format.d.ts +36 -0
  323. package/src/lib/components/reports/report-format.js +43 -0
  324. package/src/lib/components/reports/report-format.js.map +1 -0
  325. package/src/lib/components/reports/report-scope.d.ts +81 -0
  326. package/src/lib/components/reports/report-scope.js +58 -0
  327. package/src/lib/components/reports/report-scope.js.map +1 -0
  328. package/src/lib/components/reports/report-stat-tile.d.ts +47 -0
  329. package/src/lib/components/reports/report-stat-tile.js +96 -0
  330. package/src/lib/components/reports/report-stat-tile.js.map +1 -0
  331. package/src/lib/components/reports/source-conversion-card.d.ts +28 -0
  332. package/src/lib/components/reports/source-conversion-card.js +205 -0
  333. package/src/lib/components/reports/source-conversion-card.js.map +1 -0
  334. package/src/lib/components/reports/tasks-card.d.ts +26 -0
  335. package/src/lib/components/reports/tasks-card.js +279 -0
  336. package/src/lib/components/reports/tasks-card.js.map +1 -0
  337. package/src/lib/components/reports/use-aggregate-read.d.ts +99 -0
  338. package/src/lib/components/reports/use-aggregate-read.js +195 -0
  339. package/src/lib/components/reports/use-aggregate-read.js.map +1 -0
  340. package/src/lib/components/reports/won-lost-by-owner-card.d.ts +30 -0
  341. package/src/lib/components/reports/won-lost-by-owner-card.js +168 -0
  342. package/src/lib/components/reports/won-lost-by-owner-card.js.map +1 -0
  343. package/src/lib/components/reports-section.d.ts +42 -0
  344. package/src/lib/components/reports-section.js +308 -0
  345. package/src/lib/components/reports-section.js.map +1 -0
  346. package/src/lib/components/sending-addresses-card.d.ts +49 -0
  347. package/src/lib/components/sending-addresses-card.js +262 -0
  348. package/src/lib/components/sending-addresses-card.js.map +1 -0
  349. package/src/lib/components/settings-section.d.ts +151 -0
  350. package/src/lib/components/settings-section.js +783 -0
  351. package/src/lib/components/settings-section.js.map +1 -0
  352. package/src/lib/components/task-cells.d.ts +51 -0
  353. package/src/lib/components/task-cells.js +139 -0
  354. package/src/lib/components/task-cells.js.map +1 -0
  355. package/src/lib/components/task-edit-drawer.d.ts +53 -0
  356. package/src/lib/components/task-edit-drawer.js +483 -0
  357. package/src/lib/components/task-edit-drawer.js.map +1 -0
  358. package/src/lib/components/task-import-drawer.d.ts +24 -0
  359. package/src/lib/components/task-import-drawer.js +82 -0
  360. package/src/lib/components/task-import-drawer.js.map +1 -0
  361. package/src/lib/components/task-snooze-menu.d.ts +65 -0
  362. package/src/lib/components/task-snooze-menu.js +207 -0
  363. package/src/lib/components/task-snooze-menu.js.map +1 -0
  364. package/src/lib/components/tasks-bulk-bar.d.ts +25 -0
  365. package/src/lib/components/tasks-bulk-bar.js +348 -0
  366. package/src/lib/components/tasks-bulk-bar.js.map +1 -0
  367. package/src/lib/components/tasks-calendar.d.ts +38 -0
  368. package/src/lib/components/tasks-calendar.js +228 -0
  369. package/src/lib/components/tasks-calendar.js.map +1 -0
  370. package/src/lib/components/tasks-section.d.ts +27 -0
  371. package/src/lib/components/tasks-section.js +579 -0
  372. package/src/lib/components/tasks-section.js.map +1 -0
  373. package/src/lib/components/use-contact-campaign-emails.d.ts +32 -0
  374. package/src/lib/components/use-contact-campaign-emails.js +83 -0
  375. package/src/lib/components/use-contact-campaign-emails.js.map +1 -0
  376. package/src/lib/components/use-crm-api.d.ts +44 -0
  377. package/src/lib/components/use-crm-api.js +84 -0
  378. package/src/lib/components/use-crm-api.js.map +1 -0
  379. package/src/lib/components/use-crm-inbound-address.d.ts +60 -0
  380. package/src/lib/components/use-crm-inbound-address.js +124 -0
  381. package/src/lib/components/use-crm-inbound-address.js.map +1 -0
  382. package/src/lib/components/use-emails-hub-path.d.ts +24 -0
  383. package/src/lib/components/use-emails-hub-path.js +59 -0
  384. package/src/lib/components/use-emails-hub-path.js.map +1 -0
  385. package/src/lib/components/use-member-email-aliases.d.ts +54 -0
  386. package/src/lib/components/use-member-email-aliases.js +186 -0
  387. package/src/lib/components/use-member-email-aliases.js.map +1 -0
  388. package/src/lib/components/use-org-members.d.ts +40 -0
  389. package/src/lib/components/use-org-members.js +100 -0
  390. package/src/lib/components/use-org-members.js.map +1 -0
  391. package/src/lib/constants/api-routes.d.ts +109 -0
  392. package/src/lib/constants/api-routes.js +74 -0
  393. package/src/lib/constants/api-routes.js.map +1 -0
  394. package/src/lib/constants/bundle-common.d.ts +14 -0
  395. package/src/lib/constants/bundle-common.js +14 -0
  396. package/src/lib/constants/bundle-common.js.map +1 -0
  397. package/src/lib/constants/company-filters.d.ts +18 -0
  398. package/src/lib/constants/company-filters.js +51 -0
  399. package/src/lib/constants/company-filters.js.map +1 -0
  400. package/src/lib/constants/contact-filters.d.ts +38 -0
  401. package/src/lib/constants/contact-filters.js +294 -0
  402. package/src/lib/constants/contact-filters.js.map +1 -0
  403. package/src/lib/hooks/use-contact-field-definitions.d.ts +48 -0
  404. package/src/lib/hooks/use-contact-field-definitions.js +72 -0
  405. package/src/lib/hooks/use-contact-field-definitions.js.map +1 -0
  406. package/src/lib/hooks/use-contact-update.d.ts +25 -0
  407. package/src/lib/hooks/use-contact-update.js +80 -0
  408. package/src/lib/hooks/use-contact-update.js.map +1 -0
  409. package/src/lib/hooks/use-crm-activity-logger.d.ts +73 -0
  410. package/src/lib/hooks/use-crm-activity-logger.js +101 -0
  411. package/src/lib/hooks/use-crm-activity-logger.js.map +1 -0
  412. package/src/lib/hooks/use-crm-bulk-apply.d.ts +52 -0
  413. package/src/lib/hooks/use-crm-bulk-apply.js +112 -0
  414. package/src/lib/hooks/use-crm-bulk-apply.js.map +1 -0
  415. package/src/lib/hooks/use-crm-default-view.d.ts +24 -0
  416. package/src/lib/hooks/use-crm-default-view.js +64 -0
  417. package/src/lib/hooks/use-crm-default-view.js.map +1 -0
  418. package/src/lib/hooks/use-crm-email-templates.d.ts +59 -0
  419. package/src/lib/hooks/use-crm-email-templates.js +103 -0
  420. package/src/lib/hooks/use-crm-email-templates.js.map +1 -0
  421. package/src/lib/hooks/use-crm-hub-path.d.ts +29 -0
  422. package/src/lib/hooks/use-crm-hub-path.js +41 -0
  423. package/src/lib/hooks/use-crm-hub-path.js.map +1 -0
  424. package/src/lib/hooks/use-crm-org-mount.d.ts +97 -0
  425. package/src/lib/hooks/use-crm-org-mount.js +215 -0
  426. package/src/lib/hooks/use-crm-org-mount.js.map +1 -0
  427. package/src/lib/hooks/use-crm-record-names.d.ts +52 -0
  428. package/src/lib/hooks/use-crm-record-names.js +126 -0
  429. package/src/lib/hooks/use-crm-record-names.js.map +1 -0
  430. package/src/lib/hooks/use-crm-records-quota.d.ts +68 -0
  431. package/src/lib/hooks/use-crm-records-quota.js +118 -0
  432. package/src/lib/hooks/use-crm-records-quota.js.map +1 -0
  433. package/src/lib/hooks/use-crm-saved-view.d.ts +96 -0
  434. package/src/lib/hooks/use-crm-saved-view.js +329 -0
  435. package/src/lib/hooks/use-crm-saved-view.js.map +1 -0
  436. package/src/lib/hooks/use-crm-scope.d.ts +117 -0
  437. package/src/lib/hooks/use-crm-scope.js +147 -0
  438. package/src/lib/hooks/use-crm-scope.js.map +1 -0
  439. package/src/lib/hooks/use-crm-tasks.d.ts +85 -0
  440. package/src/lib/hooks/use-crm-tasks.js +153 -0
  441. package/src/lib/hooks/use-crm-tasks.js.map +1 -0
  442. package/src/lib/hooks/use-crm-view-grid.d.ts +55 -0
  443. package/src/lib/hooks/use-crm-view-grid.js +170 -0
  444. package/src/lib/hooks/use-crm-view-grid.js.map +1 -0
  445. package/src/lib/hooks/use-crm-views.d.ts +42 -0
  446. package/src/lib/hooks/use-crm-views.js +84 -0
  447. package/src/lib/hooks/use-crm-views.js.map +1 -0
  448. package/src/lib/hooks/use-deal-stage-api.d.ts +69 -0
  449. package/src/lib/hooks/use-deal-stage-api.js +78 -0
  450. package/src/lib/hooks/use-deal-stage-api.js.map +1 -0
  451. package/src/lib/hooks/use-deals.d.ts +47 -0
  452. package/src/lib/hooks/use-deals.js +105 -0
  453. package/src/lib/hooks/use-deals.js.map +1 -0
  454. package/src/lib/hooks/use-org-leads.d.ts +53 -0
  455. package/src/lib/hooks/use-org-leads.js +114 -0
  456. package/src/lib/hooks/use-org-leads.js.map +1 -0
  457. package/src/lib/hooks/use-org-member-directory.d.ts +36 -0
  458. package/src/lib/hooks/use-org-member-directory.js +116 -0
  459. package/src/lib/hooks/use-org-member-directory.js.map +1 -0
  460. package/src/lib/hooks/use-org-member-options.d.ts +38 -0
  461. package/src/lib/hooks/use-org-member-options.js +131 -0
  462. package/src/lib/hooks/use-org-member-options.js.map +1 -0
  463. package/src/lib/hooks/use-pipeline.d.ts +71 -0
  464. package/src/lib/hooks/use-pipeline.js +159 -0
  465. package/src/lib/hooks/use-pipeline.js.map +1 -0
  466. package/src/lib/model/companies-bulk-writes.d.ts +40 -0
  467. package/src/lib/model/companies-bulk-writes.js +103 -0
  468. package/src/lib/model/companies-bulk-writes.js.map +1 -0
  469. package/src/lib/model/companies-csv.d.ts +36 -0
  470. package/src/lib/model/companies-csv.js +38 -0
  471. package/src/lib/model/companies-csv.js.map +1 -0
  472. package/src/lib/model/companies.d.ts +99 -0
  473. package/src/lib/model/companies.js +168 -0
  474. package/src/lib/model/companies.js.map +1 -0
  475. package/src/lib/model/company-delete-route.d.ts +43 -0
  476. package/src/lib/model/company-delete-route.js +31 -0
  477. package/src/lib/model/company-delete-route.js.map +1 -0
  478. package/src/lib/model/company-delete.d.ts +42 -0
  479. package/src/lib/model/company-delete.js +52 -0
  480. package/src/lib/model/company-delete.js.map +1 -0
  481. package/src/lib/model/contact-duplicates.d.ts +58 -0
  482. package/src/lib/model/contact-duplicates.js +67 -0
  483. package/src/lib/model/contact-duplicates.js.map +1 -0
  484. package/src/lib/model/contact-record.d.ts +113 -0
  485. package/src/lib/model/contact-record.js +70 -0
  486. package/src/lib/model/contact-record.js.map +1 -0
  487. package/src/lib/model/contact-update.d.ts +93 -0
  488. package/src/lib/model/contact-update.js +50 -0
  489. package/src/lib/model/contact-update.js.map +1 -0
  490. package/src/lib/model/contacts-bulk-writes.d.ts +173 -0
  491. package/src/lib/model/contacts-bulk-writes.js +196 -0
  492. package/src/lib/model/contacts-bulk-writes.js.map +1 -0
  493. package/src/lib/model/contacts-csv.d.ts +54 -0
  494. package/src/lib/model/contacts-csv.js +67 -0
  495. package/src/lib/model/contacts-csv.js.map +1 -0
  496. package/src/lib/model/contacts-list-seed.d.ts +51 -0
  497. package/src/lib/model/contacts-list-seed.js +63 -0
  498. package/src/lib/model/contacts-list-seed.js.map +1 -0
  499. package/src/lib/model/crm-activity-report.d.ts +27 -0
  500. package/src/lib/model/crm-activity-report.js +56 -0
  501. package/src/lib/model/crm-activity-report.js.map +1 -0
  502. package/src/lib/model/crm-api.d.ts +49 -0
  503. package/src/lib/model/crm-api.js +47 -0
  504. package/src/lib/model/crm-api.js.map +1 -0
  505. package/src/lib/model/crm-bulk-writes.d.ts +147 -0
  506. package/src/lib/model/crm-bulk-writes.js +207 -0
  507. package/src/lib/model/crm-bulk-writes.js.map +1 -0
  508. package/src/lib/model/crm-company-import.d.ts +154 -0
  509. package/src/lib/model/crm-company-import.js +331 -0
  510. package/src/lib/model/crm-company-import.js.map +1 -0
  511. package/src/lib/model/crm-custom-draft.d.ts +62 -0
  512. package/src/lib/model/crm-custom-draft.js +67 -0
  513. package/src/lib/model/crm-custom-draft.js.map +1 -0
  514. package/src/lib/model/crm-deal-import.d.ts +112 -0
  515. package/src/lib/model/crm-deal-import.js +263 -0
  516. package/src/lib/model/crm-deal-import.js.map +1 -0
  517. package/src/lib/model/crm-import.d.ts +244 -0
  518. package/src/lib/model/crm-import.js +466 -0
  519. package/src/lib/model/crm-import.js.map +1 -0
  520. package/src/lib/model/crm-lead-import.d.ts +105 -0
  521. package/src/lib/model/crm-lead-import.js +255 -0
  522. package/src/lib/model/crm-lead-import.js.map +1 -0
  523. package/src/lib/model/crm-record-routes.d.ts +21 -0
  524. package/src/lib/model/crm-record-routes.js +70 -0
  525. package/src/lib/model/crm-record-routes.js.map +1 -0
  526. package/src/lib/model/crm-routes.d.ts +101 -0
  527. package/src/lib/model/crm-routes.js +92 -0
  528. package/src/lib/model/crm-routes.js.map +1 -0
  529. package/src/lib/model/crm-task-import.d.ts +94 -0
  530. package/src/lib/model/crm-task-import.js +275 -0
  531. package/src/lib/model/crm-task-import.js.map +1 -0
  532. package/src/lib/model/crm-view-param.d.ts +43 -0
  533. package/src/lib/model/crm-view-param.js +49 -0
  534. package/src/lib/model/crm-view-param.js.map +1 -0
  535. package/src/lib/model/deal-board-model.d.ts +241 -0
  536. package/src/lib/model/deal-board-model.js +439 -0
  537. package/src/lib/model/deal-board-model.js.map +1 -0
  538. package/src/lib/model/deal-form-model.d.ts +114 -0
  539. package/src/lib/model/deal-form-model.js +218 -0
  540. package/src/lib/model/deal-form-model.js.map +1 -0
  541. package/src/lib/model/deals-csv.d.ts +32 -0
  542. package/src/lib/model/deals-csv.js +37 -0
  543. package/src/lib/model/deals-csv.js.map +1 -0
  544. package/src/lib/model/import-column-shapes.d.ts +36 -0
  545. package/src/lib/model/import-column-shapes.js +76 -0
  546. package/src/lib/model/import-column-shapes.js.map +1 -0
  547. package/src/lib/model/lead-company-suggestion.d.ts +61 -0
  548. package/src/lib/model/lead-company-suggestion.js +65 -0
  549. package/src/lib/model/lead-company-suggestion.js.map +1 -0
  550. package/src/lib/model/lead-filters.d.ts +29 -0
  551. package/src/lib/model/lead-filters.js +39 -0
  552. package/src/lib/model/lead-filters.js.map +1 -0
  553. package/src/lib/model/lead-surfaces.d.ts +58 -0
  554. package/src/lib/model/lead-surfaces.js +67 -0
  555. package/src/lib/model/lead-surfaces.js.map +1 -0
  556. package/src/lib/model/leads-csv.d.ts +40 -0
  557. package/src/lib/model/leads-csv.js +40 -0
  558. package/src/lib/model/leads-csv.js.map +1 -0
  559. package/src/lib/model/next-activity-api.d.ts +31 -0
  560. package/src/lib/model/next-activity-api.js +57 -0
  561. package/src/lib/model/next-activity-api.js.map +1 -0
  562. package/src/lib/model/next-activity.d.ts +64 -0
  563. package/src/lib/model/next-activity.js +47 -0
  564. package/src/lib/model/next-activity.js.map +1 -0
  565. package/src/lib/model/person-key-web.d.ts +43 -0
  566. package/src/lib/model/person-key-web.js +52 -0
  567. package/src/lib/model/person-key-web.js.map +1 -0
  568. package/src/lib/model/record-facts.d.ts +282 -0
  569. package/src/lib/model/record-facts.js +434 -0
  570. package/src/lib/model/record-facts.js.map +1 -0
  571. package/src/lib/model/task-api.d.ts +28 -0
  572. package/src/lib/model/task-api.js +57 -0
  573. package/src/lib/model/task-api.js.map +1 -0
  574. package/src/lib/model/task-calendar.d.ts +108 -0
  575. package/src/lib/model/task-calendar.js +139 -0
  576. package/src/lib/model/task-calendar.js.map +1 -0
  577. package/src/lib/model/task-routes.d.ts +177 -0
  578. package/src/lib/model/task-routes.js +197 -0
  579. package/src/lib/model/task-routes.js.map +1 -0
  580. package/src/lib/model/task-scope.d.ts +45 -0
  581. package/src/lib/model/task-scope.js +51 -0
  582. package/src/lib/model/task-scope.js.map +1 -0
  583. package/src/lib/model/task-views.d.ts +149 -0
  584. package/src/lib/model/task-views.js +286 -0
  585. package/src/lib/model/task-views.js.map +1 -0
  586. package/src/lib/model/tasks-csv.d.ts +28 -0
  587. package/src/lib/model/tasks-csv.js +28 -0
  588. package/src/lib/model/tasks-csv.js.map +1 -0
  589. package/src/lib/plugin.d.ts +32 -0
  590. package/src/lib/plugin.js +208 -0
  591. package/src/lib/plugin.js.map +1 -0
  592. package/src/lib/server/companies-import.d.ts +57 -0
  593. package/src/lib/server/companies-import.js +242 -0
  594. package/src/lib/server/companies-import.js.map +1 -0
  595. package/src/lib/server/company-delete.d.ts +59 -0
  596. package/src/lib/server/company-delete.js +146 -0
  597. package/src/lib/server/company-delete.js.map +1 -0
  598. package/src/lib/server/contact-email-history.d.ts +82 -0
  599. package/src/lib/server/contact-email-history.js +264 -0
  600. package/src/lib/server/contact-email-history.js.map +1 -0
  601. package/src/lib/server/contact-profile.d.ts +41 -0
  602. package/src/lib/server/contact-profile.js +45 -0
  603. package/src/lib/server/contact-profile.js.map +1 -0
  604. package/src/lib/server/contact-update.d.ts +92 -0
  605. package/src/lib/server/contact-update.js +512 -0
  606. package/src/lib/server/contact-update.js.map +1 -0
  607. package/src/lib/server/contacts-import.d.ts +73 -0
  608. package/src/lib/server/contacts-import.js +263 -0
  609. package/src/lib/server/contacts-import.js.map +1 -0
  610. package/src/lib/server/contacts-merge.d.ts +86 -0
  611. package/src/lib/server/contacts-merge.js +206 -0
  612. package/src/lib/server/contacts-merge.js.map +1 -0
  613. package/src/lib/server/deals-import.d.ts +76 -0
  614. package/src/lib/server/deals-import.js +246 -0
  615. package/src/lib/server/deals-import.js.map +1 -0
  616. package/src/lib/server/email-send.d.ts +119 -0
  617. package/src/lib/server/email-send.js +667 -0
  618. package/src/lib/server/email-send.js.map +1 -0
  619. package/src/lib/server/email-template-duplicate.d.ts +19 -0
  620. package/src/lib/server/email-template-duplicate.js +206 -0
  621. package/src/lib/server/email-template-duplicate.js.map +1 -0
  622. package/src/lib/server/erase-person.d.ts +108 -0
  623. package/src/lib/server/erase-person.js +309 -0
  624. package/src/lib/server/erase-person.js.map +1 -0
  625. package/src/lib/server/import-context.d.ts +85 -0
  626. package/src/lib/server/import-context.js +181 -0
  627. package/src/lib/server/import-context.js.map +1 -0
  628. package/src/lib/server/inbound-address.d.ts +61 -0
  629. package/src/lib/server/inbound-address.js +188 -0
  630. package/src/lib/server/inbound-address.js.map +1 -0
  631. package/src/lib/server/lead-convert.d.ts +78 -0
  632. package/src/lib/server/lead-convert.js +250 -0
  633. package/src/lib/server/lead-convert.js.map +1 -0
  634. package/src/lib/server/leads-import.d.ts +84 -0
  635. package/src/lib/server/leads-import.js +261 -0
  636. package/src/lib/server/leads-import.js.map +1 -0
  637. package/src/lib/server/next-activity-routes.d.ts +33 -0
  638. package/src/lib/server/next-activity-routes.js +110 -0
  639. package/src/lib/server/next-activity-routes.js.map +1 -0
  640. package/src/lib/server/org-activity.d.ts +61 -0
  641. package/src/lib/server/org-activity.js +120 -0
  642. package/src/lib/server/org-activity.js.map +1 -0
  643. package/src/lib/server/org-caller.d.ts +110 -0
  644. package/src/lib/server/org-caller.js +154 -0
  645. package/src/lib/server/org-caller.js.map +1 -0
  646. package/src/lib/server/recipe-routes.d.ts +75 -0
  647. package/src/lib/server/recipe-routes.js +442 -0
  648. package/src/lib/server/recipe-routes.js.map +1 -0
  649. package/src/lib/server/record-facts.d.ts +65 -0
  650. package/src/lib/server/record-facts.js +337 -0
  651. package/src/lib/server/record-facts.js.map +1 -0
  652. package/src/lib/server/record-timeline.d.ts +51 -0
  653. package/src/lib/server/record-timeline.js +215 -0
  654. package/src/lib/server/record-timeline.js.map +1 -0
  655. package/src/lib/server/suite-gate.d.ts +42 -0
  656. package/src/lib/server/suite-gate.js +87 -0
  657. package/src/lib/server/suite-gate.js.map +1 -0
  658. package/src/lib/server/task-routes.d.ts +132 -0
  659. package/src/lib/server/task-routes.js +699 -0
  660. package/src/lib/server/task-routes.js.map +1 -0
  661. package/src/lib/server/tasks-import.d.ts +54 -0
  662. package/src/lib/server/tasks-import.js +157 -0
  663. package/src/lib/server/tasks-import.js.map +1 -0
  664. package/src/lib/server-deal-stage.d.ts +88 -0
  665. package/src/lib/server-deal-stage.js +316 -0
  666. package/src/lib/server-deal-stage.js.map +1 -0
  667. package/src/lib/server.d.ts +149 -0
  668. package/src/lib/server.js +615 -0
  669. package/src/lib/server.js.map +1 -0
@@ -0,0 +1,667 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ /**
3
+ * @license
4
+ * Copyright 2026 Aglyn LLC
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */ import { buildCrmEmailActivity, contactCaptureHostIds, CRM_ACTIVITY_LOG_FULL_MESSAGE, CRM_COLLECTIONS, CRM_EMAIL_BODY_MAX, CRM_EMAIL_CONTEXT, CRM_EMAIL_SUBJECT_MAX, crmActivityLogHasRoom, crmEmailDeliveryTags, crmMergeFieldsIn, crmScopeTokens, isOrgWideMember, normalizeContactEmail, readContactFacet, readMarketingBasis, renderCrmMergeFields, resolveOrgEntitlements, visibleToHost } from "@aglyn/aglyn/server";
18
+ import { isEmailConfigured, sendEmail, sendFailureReason } from "@aglyn/shared-util-email";
19
+ import { consentGroupForSite, consumeRateLimit, countCrmActivitiesForRecord, filterSendableForHost, firebaseAdmin, getHostDocAdmin, getOrgForHost, hostSendingIdentity, logOrgActivity, memberHasOrgPermission, newCrmActivityRef, orgDataCollectionForHost, readOrgEmailRamp, recordEmailSends, releaseCrmEmailSend, reserveCrmEmailSend, resolveOrgMembership, writeCrmEmailActivity } from "@aglyn/tenant-data-admin";
20
+ import { isRefusedIdToken } from "@aglyn/tenant-data-admin/server/id-token-refusal";
21
+ import { authorizeOrgCaller, orgHostIds, readCrmRouteScope } from "./org-caller.js";
22
+ import { crmSuiteRefusal } from "./suite-gate.js";
23
+ /**
24
+ * `POST /api/crm/email-send` — one email to one person, from their record
25
+ * (AGL-2615).
26
+ *
27
+ * Body: `{ hostId, contactId? | leadId? | dealId?, subject, body }`. Answers
28
+ * `{ ok: true, activityId, to, from, logged }` once the provider accepted
29
+ * the message, and a refusal with a `reason` the dialog can act on
30
+ * otherwise. The subject and the body may carry merge fields —
31
+ * `{{contact.firstName}}`, `{{deal.amount}}` (AGL-2658) — which this route
32
+ * fills from the record before the send, so the timeline row is the letter
33
+ * as it left; see `crm-email-templates.ts` for the grammar.
34
+ *
35
+ * ## What it is, and what it is not
36
+ *
37
+ * A message a teammate wrote to one person they have a relationship with —
38
+ * a reply to a lead, a follow-up on a deal. It is not marketing: no
39
+ * campaign audience, no unsubscribe pair, no frequency window, no consent
40
+ * split. What it does owe is everything a bounce or a complaint owes the
41
+ * shared sending domain, which is why BOTH suppression lists are consulted
42
+ * and why a person's own stated refusal — a `declined` basis — is a hard
43
+ * stop even though the message is not the kind of mail that basis governs.
44
+ * The recipient asked this site not to write; the kind of writing is not
45
+ * the point.
46
+ *
47
+ * ## Why the recipient is resolved here and not taken from the body
48
+ *
49
+ * The dialog shows the address the record carries, and this route reads it
50
+ * again off the record the caller named, through the same visibility check
51
+ * every other CRM route makes. A `to` in the body would make this a route
52
+ * that sends the site's mail to any address a member types, which is a
53
+ * different product with a different abuse surface.
54
+ *
55
+ * ## The organization variant (AGL-2634)
56
+ *
57
+ * `{ orgId, hostId?, … }` from the org-level hub. The caller is authorized
58
+ * by the org — an org-wide member holding `data.manage` — and the record is
59
+ * read with no site's visibility to check, because an org-wide member reads
60
+ * every row. What the org level cannot do away with is the SITE the message
61
+ * leaves from: the sending identity, the suppression list and the person's
62
+ * stated refusal are each a site's. So the org variant still names one —
63
+ * the record's own capturing site or the site the reader picked, in the
64
+ * body; failing that the record's own site read off the document; failing
65
+ * that the org's only site — and refuses, with a `site` reason, when the
66
+ * org has several and none was named. The row it writes is visible to
67
+ * whoever may see the RECORD, so an email to a person another site
68
+ * captured shows on their timeline under that site's hub; the act is
69
+ * logged in the org's feed.
70
+ *
71
+ * ## The order of the gates
72
+ *
73
+ * Cheap and certain first. A malformed body and a missing token cost
74
+ * nothing; the per-user pace is one counter; the record is one read and
75
+ * decides everything after it; the ceiling one aggregate; the two
76
+ * suppression lists two keyed reads; the sending identity next, because it
77
+ * is the most expensive and the one a workspace fixes once. The daily cap
78
+ * comes LAST and is the one gate that writes before the provider is called
79
+ * (AGL-2645): the send is reserved on today's counter inside a transaction,
80
+ * so two reps at the last slot cannot both pass one reading of it, and a
81
+ * message the provider refuses gives the slot back. After the provider has
82
+ * accepted, two more things are written, neither able to fail the send:
83
+ * the activity row and the org's cost meter.
84
+ *
85
+ * ## The new-workspace ramp is part of that cap, not a gate beside it
86
+ *
87
+ * A message from a workspace created this morning lands on the sending
88
+ * domain every other tenant's receipts leave on, so the plan's day is held
89
+ * to the share of it the workspace has earned — the same ramp the campaign
90
+ * path is paced by, `readOrgEmailRamp`, taken to the allowance this surface
91
+ * meters instead of the campaign ceiling. It is handed to the reservation
92
+ * rather than checked before it, so the ceiling and the count it is judged
93
+ * against come out of the one transactional read; a ramp consulted
94
+ * separately would be a second ceiling and two reps could pass it at once.
95
+ * A workspace past its first week is not ramped and pays nothing to find
96
+ * that out.
97
+ */ /** How many one-to-one emails one person may send in one minute. */ export const CRM_EMAIL_SENDS_PER_MINUTE = 20;
98
+ const CRM_EMAIL_RATE_WINDOW_MS = 60000;
99
+ /** What the route says when the plan carries no one-to-one email at all. */ export const CRM_EMAIL_NOT_INCLUDED_MESSAGE = 'Your plan does not include one-to-one email. Upgrade in Billing to email ' + 'people from their records.';
100
+ /** What the route says at the daily cap; `included` is the plan's figure. */ export function crmEmailCapReachedMessage(included) {
101
+ return `Today's one-to-one email limit (${included.toLocaleString('en-US')}) is ` + 'reached. It resets at midnight UTC — see Billing for the count.';
102
+ }
103
+ /**
104
+ * What the route says when the NEW-WORKSPACE RAMP is the ceiling that was
105
+ * reached, rather than the plan's own.
106
+ *
107
+ * The campaign path's wording, pointed at this surface: the number, what was
108
+ * spent against it, that nothing was sent or counted, and when it lifts. A
109
+ * workspace told only "limit reached" against a figure far below the one its
110
+ * plan page advertises would read it as a billing fault.
111
+ */ export function crmEmailRampReachedMessage(ceiling, used) {
112
+ return `This workspace may send ${ceiling.toLocaleString('en-US')} one-to-one ` + `email${ceiling === 1 ? '' : 's'} a day while it establishes a sending ` + `history, and has sent ${used.toLocaleString('en-US')} today. Nothing ` + 'has been sent and nothing has been counted — the allowance grows as ' + "the workspace sends, and is the plan's in full after its first week. " + 'It resets at midnight UTC.';
113
+ }
114
+ /** What the route says when the ramp's own history could not be read. */ export const CRM_EMAIL_RAMP_UNAVAILABLE_MESSAGE = "This workspace's sending history could not be read just now, so how much " + 'of its first-week email allowance it has earned is not known. Nothing has ' + 'been sent and nothing has been counted — try again in a moment.';
115
+ export const CRM_EMAIL_SUPPRESSED_MESSAGE = 'This address has bounced or reported a message as spam, so it cannot be ' + 'emailed.';
116
+ export const CRM_EMAIL_DECLINED_MESSAGE = 'This person asked not to be emailed by this site.';
117
+ export const CRM_EMAIL_RATE_MESSAGE = 'You have sent too many emails in the last minute. Wait a moment and try again.';
118
+ /** What the org variant says when no site was named and the org has several. */ export const CRM_EMAIL_PICK_SITE_MESSAGE = 'Pick the site this email leaves from — its sending address is what the ' + 'message is sent as.';
119
+ const refuse = (status, error, extra = {})=>({
120
+ ok: false,
121
+ status,
122
+ body: _extends({
123
+ error
124
+ }, extra)
125
+ });
126
+ /** One typed field, trimmed and bounded. */ function typed(value, max) {
127
+ return String(value != null ? value : '').trim().slice(0, max);
128
+ }
129
+ /**
130
+ * Who is sending, and whether they may: a verified ID token, then
131
+ * `data.manage` on the site's org through the same three-layer read the
132
+ * create route makes, admitted only for a site the member reaches. Staff
133
+ * pass the permission check as they do on every CRM route, and send as
134
+ * themselves.
135
+ */ async function authorizeSender(req, hostId) {
136
+ var _req_headers_authorization, _resolved_org;
137
+ const authorization = String((_req_headers_authorization = req.headers.authorization) != null ? _req_headers_authorization : '');
138
+ const idToken = authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) : undefined;
139
+ if (!idToken) return refuse(401, 'Unauthenticated');
140
+ let decoded;
141
+ try {
142
+ decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken);
143
+ } catch (error) {
144
+ // A refused credential is the caller's 401; a failure to check one is
145
+ // ours and keeps a 5xx (AGL-2852).
146
+ if (isRefusedIdToken(error)) return refuse(401, 'Unauthenticated');
147
+ console.error('[crm] email-send could not verify the sender', error);
148
+ return refuse(500, 'The sign-in could not be checked. Try again.');
149
+ }
150
+ const resolved = await getOrgForHost(hostId).catch(()=>null);
151
+ if (!resolved) return refuse(404, 'Unknown site');
152
+ const { orgId } = resolved;
153
+ const org = (_resolved_org = resolved.org) != null ? _resolved_org : {};
154
+ if (decoded.staff !== true) {
155
+ var _ref;
156
+ var _member_hostAccess;
157
+ const membership = await resolveOrgMembership(decoded.uid, orgId).catch(()=>null);
158
+ const member = (_ref = membership == null ? void 0 : membership.member) != null ? _ref : null;
159
+ const reaches = isOrgWideMember(member) || Boolean(member == null ? void 0 : (_member_hostAccess = member.hostAccess) == null ? void 0 : _member_hostAccess[hostId]);
160
+ const allowed = member && reaches && await memberHasOrgPermission(orgId, member, 'data.manage');
161
+ if (!allowed) {
162
+ return refuse(403, 'Sending email from a record requires the data.manage permission on this site');
163
+ }
164
+ }
165
+ const email = normalizeContactEmail(decoded.email);
166
+ if (!email) {
167
+ // A reply-to is owed and the token is the only place it can honestly
168
+ // come from; an account with no address cannot be replied to.
169
+ return refuse(403, 'Your account has no email address to receive replies at.');
170
+ }
171
+ return {
172
+ ok: true,
173
+ uid: decoded.uid,
174
+ email,
175
+ name: typed(decoded.name, 120),
176
+ orgId,
177
+ org
178
+ };
179
+ }
180
+ /**
181
+ * The org variant's sender (AGL-2634): authorized by the org for an
182
+ * org-wide member holding `data.manage`, with the same reply-to rule.
183
+ */ async function authorizeOrgSender(req, orgId) {
184
+ const caller = await authorizeOrgCaller(req, orgId, {
185
+ needs: 'data.manage',
186
+ refusal: 'Sending email at the organization level requires the data.manage ' + 'permission across the whole workspace'
187
+ });
188
+ if (caller.ok === false) return refuse(caller.status, caller.error);
189
+ const email = normalizeContactEmail(caller.email);
190
+ if (!email) {
191
+ return refuse(403, 'Your account has no email address to receive replies at.');
192
+ }
193
+ return {
194
+ ok: true,
195
+ uid: caller.uid,
196
+ email,
197
+ name: caller.name,
198
+ orgId,
199
+ org: caller.org
200
+ };
201
+ }
202
+ /**
203
+ * The person the named record is about, as THIS site may see them.
204
+ *
205
+ * A deal names its contact; a lead carries its own address; a contact is
206
+ * itself. Each is looked up by id and then checked against what the site
207
+ * may read — the Admin SDK evaluates no rules, and a member of site A must
208
+ * not be able to mail site B's contact by guessing an id. The links the
209
+ * activity is filed under are whatever resolved: a deal's email is also the
210
+ * contact's, so it lands on both timelines.
211
+ *
212
+ * At the organization level there is no site to check against and none is
213
+ * checked: the caller was admitted as an org-wide member, who reads every
214
+ * row. A lead still needs its site named — a lead lives under one.
215
+ */ async function resolveRecipient(firestore, scope, orgId, ids) {
216
+ const link = {};
217
+ const { hostId } = scope;
218
+ const scoped = scope.level === 'site';
219
+ const visible = (tokens)=>!scoped || visibleToHost(tokens, hostId);
220
+ let contactId = ids.contactId;
221
+ let siteHint = '';
222
+ let dealRecord = null;
223
+ if (ids.dealId) {
224
+ var _deal_data, _deal_get;
225
+ const deal = await firestore.collection('orgs').doc(orgId).collection(CRM_COLLECTIONS.deals).doc(ids.dealId).get();
226
+ if (!deal.exists || !visible(deal.get('visibleTo'))) {
227
+ return refuse(404, 'Unknown deal');
228
+ }
229
+ link.dealId = deal.id;
230
+ dealRecord = (_deal_data = deal.data()) != null ? _deal_data : {};
231
+ siteHint = typed(deal.get('hostId'), 128);
232
+ contactId = contactId || String((_deal_get = deal.get('contactId')) != null ? _deal_get : '').trim();
233
+ if (!contactId) {
234
+ return refuse(400, 'This deal names no contact to email.');
235
+ }
236
+ }
237
+ if (contactId) {
238
+ var _contact_data;
239
+ const contacts = scoped ? await orgDataCollectionForHost(hostId, 'contacts') : firestore.collection('orgs').doc(orgId).collection('contacts');
240
+ const contact = await contacts.doc(contactId).get();
241
+ if (!contact.exists || !visible(contact.get('visibleTo'))) {
242
+ return refuse(404, 'Unknown contact');
243
+ }
244
+ const email = normalizeContactEmail(contact.get('email'));
245
+ if (!email) return refuse(400, 'This contact has no email address.');
246
+ const record = (_contact_data = contact.data()) != null ? _contact_data : {};
247
+ link.contactId = contact.id;
248
+ if (ids.leadId) link.leadId = ids.leadId;
249
+ siteHint = siteHint || typed(record['hostId'], 128) || contactCaptureHostIds(record)[0] || '';
250
+ return {
251
+ ok: true,
252
+ email,
253
+ record,
254
+ contact: record,
255
+ link,
256
+ siteHint,
257
+ deal: dealRecord,
258
+ lead: null
259
+ };
260
+ }
261
+ if (ids.leadId) {
262
+ var _lead_data, _lead_get;
263
+ if (!hostId) return refuse(400, 'Name the site the lead lives under.');
264
+ const lead = await firestore.collection('hosts').doc(hostId).collection('leads').doc(ids.leadId).get();
265
+ if (!lead.exists) return refuse(404, 'Unknown lead');
266
+ const email = normalizeContactEmail(lead.get('email'));
267
+ if (!email) return refuse(400, 'This lead has no email address.');
268
+ const record = (_lead_data = lead.data()) != null ? _lead_data : {};
269
+ link.leadId = lead.id;
270
+ // A converted lead's email belongs on the contact it became as well.
271
+ const converted = String((_lead_get = lead.get('convertedContactId')) != null ? _lead_get : '').trim();
272
+ if (converted) link.contactId = converted;
273
+ return {
274
+ ok: true,
275
+ email,
276
+ record,
277
+ contact: null,
278
+ link,
279
+ siteHint: hostId,
280
+ deal: null,
281
+ lead: record
282
+ };
283
+ }
284
+ return refuse(400, 'Name a contact, lead or deal to email.');
285
+ }
286
+ /**
287
+ * The site an org-level send leaves from (AGL-2634): the body's, else the
288
+ * record's own, else the org's only site — and a refusal the dialog turns
289
+ * into a Site picker when the org has several and none was named.
290
+ */ async function resolveSendingSite(firestore, scope, orgId, siteHint) {
291
+ if (scope.hostId) return {
292
+ ok: true,
293
+ hostId: scope.hostId
294
+ };
295
+ if (siteHint) return {
296
+ ok: true,
297
+ hostId: siteHint
298
+ };
299
+ const hosts = await orgHostIds(firestore, orgId);
300
+ if (hosts.length === 1) return {
301
+ ok: true,
302
+ hostId: hosts[0]
303
+ };
304
+ return refuse(409, CRM_EMAIL_PICK_SITE_MESSAGE, {
305
+ reason: 'site'
306
+ });
307
+ }
308
+ /** The HTTP shape of a send the provider did not accept. */ function sendRefusal(result) {
309
+ var _result_detail;
310
+ const reason = sendFailureReason(result);
311
+ const detail = String((_result_detail = result.detail) != null ? _result_detail : '');
312
+ if (reason === 'unverified-domain') {
313
+ return refuse(409, detail || 'This site has no verified sending identity.', {
314
+ reason: 'sending-identity'
315
+ });
316
+ }
317
+ if (reason === 'suppressed' || reason === 'unengaged') {
318
+ return refuse(409, CRM_EMAIL_SUPPRESSED_MESSAGE, {
319
+ reason: 'suppressed'
320
+ });
321
+ }
322
+ if (reason === 'rate-limited') {
323
+ const retryAtMs = Number(result.retryAtMs);
324
+ return refuse(503, 'The mail provider asked us to slow down. Try again in a minute.', {
325
+ reason: 'provider-rate',
326
+ retryAfterSeconds: Number.isFinite(retryAtMs) ? Math.max(1, Math.ceil((retryAtMs - Date.now()) / 1000)) : 60
327
+ });
328
+ }
329
+ if (reason === 'unconfigured') {
330
+ return refuse(501, 'Email is not configured on this deployment.', {
331
+ reason: 'unconfigured'
332
+ });
333
+ }
334
+ return refuse(502, 'The email could not be sent.', {
335
+ reason: 'send-failed'
336
+ });
337
+ }
338
+ /** What the route says when a subject or a message is nothing once its fields are filled. */ export const CRM_EMAIL_EMPTY_AFTER_MERGE_MESSAGE = 'The subject and the message are empty once their merge fields are filled ' + 'in. Check the fields against this record.';
339
+ /**
340
+ * What `{{…}}` reads from, for this send (AGL-2658): the documents already
341
+ * in hand — the contact, the deal, the lead — the sender off their token,
342
+ * and the site's name, read only when a field asks for it. The contact's
343
+ * facet is the SENDING site's group's, because a name, a job title and a
344
+ * company are one holder's knowledge of a person.
345
+ */ async function mergeContextFor(options) {
346
+ const { recipient, sender, hostId, groupId, fields } = options;
347
+ const wantsSite = fields.some((field)=>field.startsWith('site.'));
348
+ const host = wantsSite ? await getHostDocAdmin(hostId).catch(()=>null) : null;
349
+ return {
350
+ contact: recipient.contact,
351
+ contactGroupId: groupId,
352
+ lead: recipient.lead,
353
+ deal: recipient.deal,
354
+ sender: {
355
+ name: sender.name,
356
+ email: sender.email
357
+ },
358
+ site: {
359
+ name: typed(host == null ? void 0 : host['displayName'], 200)
360
+ }
361
+ };
362
+ }
363
+ function answer(res, refusal) {
364
+ const retry = refusal.body['retryAfterSeconds'];
365
+ if (typeof retry === 'number') res.setHeader('Retry-After', String(retry));
366
+ res.status(refusal.status).json(refusal.body);
367
+ }
368
+ export const crmEmailSendHandler = async (req, res)=>{
369
+ var _req_body, _body_body;
370
+ if (req.method !== 'POST') {
371
+ res.setHeader('Allow', 'POST');
372
+ res.status(405).json({
373
+ error: 'Method not allowed'
374
+ });
375
+ return;
376
+ }
377
+ const body = (_req_body = req.body) != null ? _req_body : {};
378
+ const routeScope = readCrmRouteScope(body);
379
+ if (!routeScope) {
380
+ res.status(400).json({
381
+ error: 'Missing hostId'
382
+ });
383
+ return;
384
+ }
385
+ const ids = {
386
+ contactId: typed(body['contactId'], 128),
387
+ leadId: typed(body['leadId'], 128),
388
+ dealId: typed(body['dealId'], 128)
389
+ };
390
+ if (!ids.contactId && !ids.leadId && !ids.dealId) {
391
+ res.status(400).json({
392
+ error: 'Name a contact, lead or deal to email.'
393
+ });
394
+ return;
395
+ }
396
+ const draftSubject = typed(body['subject'], CRM_EMAIL_SUBJECT_MAX);
397
+ if (!draftSubject) {
398
+ res.status(400).json({
399
+ error: 'Enter a subject.'
400
+ });
401
+ return;
402
+ }
403
+ // Paragraphs are what the dialog offers, and a plain-text part is what a
404
+ // paragraph is; line endings are normalized so the stored body and the
405
+ // sent body are one string.
406
+ const draftText = String((_body_body = body['body']) != null ? _body_body : '').replace(/\r\n?/g, '\n').trim().slice(0, CRM_EMAIL_BODY_MAX);
407
+ if (!draftText) {
408
+ res.status(400).json({
409
+ error: 'Write the message.'
410
+ });
411
+ return;
412
+ }
413
+ try {
414
+ const sender = routeScope.level === 'org' ? await authorizeOrgSender(req, routeScope.orgId) : await authorizeSender(req, routeScope.hostId);
415
+ if (sender.ok === false) return answer(res, sender);
416
+ const { orgId, org } = sender;
417
+ /*
418
+ * THE PLAN, before the pace and the cap (AGL-2787). One-to-one email is
419
+ * the suite's; a plan without the suite is refused as such, rather than
420
+ * reaching the daily cap and being told about a zero it has no way to
421
+ * raise short of the upgrade this names.
422
+ */ const suite = crmSuiteRefusal(org, 'Emailing one person from their record');
423
+ if (suite) return answer(res, {
424
+ ok: false,
425
+ status: suite.status,
426
+ body: suite.body
427
+ });
428
+ /*
429
+ * THE PER-USER PACE, on its own key. The console dispatcher already
430
+ * counts this write against the person's general console budget; this
431
+ * is the narrower bucket the feature owes — twenty a minute is a rep
432
+ * writing, and past it is a script — keyed by the uid the token proved
433
+ * so one person cannot spend the bucket of everyone on their address.
434
+ */ const rate = await consumeRateLimit(`crm-email-send:uid:${sender.uid}`, {
435
+ limit: CRM_EMAIL_SENDS_PER_MINUTE,
436
+ windowMs: CRM_EMAIL_RATE_WINDOW_MS
437
+ });
438
+ if (!rate.allowed) {
439
+ return answer(res, refuse(429, CRM_EMAIL_RATE_MESSAGE, {
440
+ reason: 'rate',
441
+ retryAfterSeconds: Math.max(1, Math.ceil((rate.resetMs - Date.now()) / 1000))
442
+ }));
443
+ }
444
+ const firestore = firebaseAdmin.app().firestore();
445
+ const recipient = await resolveRecipient(firestore, routeScope, orgId, ids);
446
+ if (recipient.ok === false) return answer(res, recipient);
447
+ // The site the message leaves from — the mounted one under a site, and
448
+ // at the organization level whatever the body, the record or the org
449
+ // can answer.
450
+ const site = await resolveSendingSite(firestore, routeScope, orgId, recipient.siteHint);
451
+ if (site.ok === false) return answer(res, site);
452
+ const { hostId } = site;
453
+ const group = await consentGroupForSite(hostId, org);
454
+ // The company the sending site files this person under, so the email
455
+ // shows on the company's log as the automation step's rows do.
456
+ if (recipient.contact) {
457
+ const facet = readContactFacet(recipient.contact, group.groupId);
458
+ if (facet.companyId) recipient.link.companyId = facet.companyId;
459
+ }
460
+ /*
461
+ * MERGE FIELDS (AGL-2658), filled here rather than in the dialog: the
462
+ * dialog previews with the same resolver, but what leaves and what is
463
+ * logged is rendered by the route, off the documents it read to find
464
+ * the recipient, so a preview the rep did not look at cannot differ
465
+ * from the letter. A subject or a message that is nothing once its
466
+ * fields are filled is refused before any gate is spent on it.
467
+ */ const fields = crmMergeFieldsIn(`${draftSubject}\n${draftText}`);
468
+ const merge = fields.length ? await mergeContextFor({
469
+ recipient,
470
+ sender,
471
+ hostId,
472
+ groupId: group.groupId,
473
+ fields
474
+ }) : null;
475
+ const subject = merge ? renderCrmMergeFields(draftSubject, merge).trim().slice(0, CRM_EMAIL_SUBJECT_MAX) : draftSubject;
476
+ const text = merge ? renderCrmMergeFields(draftText, merge).trim().slice(0, CRM_EMAIL_BODY_MAX) : draftText;
477
+ if (!subject || !text) {
478
+ return answer(res, refuse(400, CRM_EMAIL_EMPTY_AFTER_MERGE_MESSAGE, {
479
+ reason: 'merge'
480
+ }));
481
+ }
482
+ // The per-record ceiling, before the send: an email that could not be
483
+ // logged would be a message the timeline never shows, and the timeline
484
+ // is the reason the send lives on the record.
485
+ const orgRef = firestore.collection('orgs').doc(orgId);
486
+ if (!crmActivityLogHasRoom(await countCrmActivitiesForRecord(orgRef, recipient.link))) {
487
+ return answer(res, refuse(409, CRM_ACTIVITY_LOG_FULL_MESSAGE, {
488
+ reason: 'ceiling'
489
+ }));
490
+ }
491
+ // Both suppression lists, then the person's own refusal.
492
+ const sendable = await filterSendableForHost(hostId, [
493
+ recipient.email
494
+ ]);
495
+ if (!sendable.length) {
496
+ return answer(res, refuse(409, CRM_EMAIL_SUPPRESSED_MESSAGE, {
497
+ reason: 'suppressed'
498
+ }));
499
+ }
500
+ if (readMarketingBasis(recipient.record, group).basis === 'declined') {
501
+ return answer(res, refuse(409, CRM_EMAIL_DECLINED_MESSAGE, {
502
+ reason: 'declined'
503
+ }));
504
+ }
505
+ if (!isEmailConfigured()) {
506
+ return answer(res, refuse(501, 'Email is not configured on this deployment.', {
507
+ reason: 'unconfigured'
508
+ }));
509
+ }
510
+ const identity = await hostSendingIdentity(hostId);
511
+ if (identity.refusal) {
512
+ return answer(res, refuse(409, identity.refusal.message, {
513
+ reason: 'sending-identity'
514
+ }));
515
+ }
516
+ const now = new Date();
517
+ /*
518
+ * THE NEW-WORKSPACE RAMP, resolved for the ceiling below.
519
+ *
520
+ * A workspace past its first week graduates off the org record alone and
521
+ * pays no read for this. One that is still on the ramp needs its
522
+ * delivery history to know which step it has earned, and when that
523
+ * history is unreadable the step is a floor rather than an answer.
524
+ *
525
+ * A floor is enough for a campaign, which defers what it cannot send
526
+ * today and delivers it tomorrow. It is not enough here: this route
527
+ * admits or refuses ONE message and has no third answer, so a ramp that
528
+ * cannot say what a workspace has earned refuses and says so. The
529
+ * refusal is transient by construction — it lasts as long as the window
530
+ * read is failing — and it reaches only workspaces inside their first
531
+ * week, since every other one graduated before the window was consulted.
532
+ */ const { ramp, degraded } = await readOrgEmailRamp({
533
+ orgId,
534
+ createdAt: org['createdAt'],
535
+ now: now.getTime()
536
+ });
537
+ if (degraded) {
538
+ return answer(res, refuse(503, CRM_EMAIL_RAMP_UNAVAILABLE_MESSAGE, {
539
+ reason: 'ramp-unavailable'
540
+ }));
541
+ }
542
+ // THE DAILY CAP (AGL-2611), taken as a slot rather than read as a
543
+ // figure (AGL-2645): the transaction reads today's counter, judges it
544
+ // with `checkCrmEmailQuota` against the ramp's share of the plan's day
545
+ // and raises it in one step, so the count the cap enforces is the count
546
+ // this send was admitted on.
547
+ const claim = await reserveCrmEmailSend(firestore, orgId, org, now, ramp);
548
+ if (!claim.ok) {
549
+ const { quota } = claim;
550
+ // WHICH ceiling was reached, in the org's own terms. The reservation
551
+ // answers with the number it judged, which is the plan's day unless
552
+ // the ramp held this workspace below it — and a workspace shown a
553
+ // figure under the one its plan page advertises is owed the reason.
554
+ const rampBound = quota.included < resolveOrgEntitlements(org).crmEmailsPerDay;
555
+ return answer(res, refuse(409, quota.included <= 0 ? CRM_EMAIL_NOT_INCLUDED_MESSAGE : rampBound ? crmEmailRampReachedMessage(quota.included, quota.used) : crmEmailCapReachedMessage(quota.included), {
556
+ reason: rampBound ? 'ramp' : 'quota',
557
+ included: quota.included,
558
+ used: quota.used,
559
+ resetsAtMs: quota.resetsAt.getTime()
560
+ }));
561
+ }
562
+ // The row's id, minted before the send so the delivery webhook can find
563
+ // it from the tags alone; written only once the message has left.
564
+ const activityRef = newCrmActivityRef(firestore, orgId);
565
+ let result;
566
+ try {
567
+ result = await sendEmail(_extends({
568
+ to: recipient.email,
569
+ subject,
570
+ text,
571
+ sendingIdentity: identity,
572
+ audience: 'tenant',
573
+ context: CRM_EMAIL_CONTEXT,
574
+ replyTo: sender.email
575
+ }, sender.name ? {
576
+ fromName: sender.name
577
+ } : {}, {
578
+ tags: crmEmailDeliveryTags({
579
+ orgId,
580
+ hostId,
581
+ activityId: activityRef.id
582
+ })
583
+ }));
584
+ } catch (error) {
585
+ // The provider never answered, so no message left: the slot goes
586
+ // back before the failure is reported.
587
+ await releaseCrmEmailSend(firestore, claim.reservation);
588
+ throw error;
589
+ }
590
+ if (!result.sent) {
591
+ await releaseCrmEmailSend(firestore, claim.reservation);
592
+ return answer(res, sendRefusal(result));
593
+ }
594
+ const sentAtMs = Date.now();
595
+ let logged = true;
596
+ /*
597
+ * Who may list the row. Under a site, what a record created there is
598
+ * stamped with. At the organization level the RECORD's own tokens, so
599
+ * an email to a person another site captured shows on their timeline
600
+ * under that site's hub — a row stamped with the sending site alone
601
+ * would be invisible where the person is read.
602
+ */ const recordTokens = recipient.record['visibleTo'];
603
+ const visibleTo = routeScope.level === 'org' && Array.isArray(recordTokens) && recordTokens.length ? recordTokens.map(String) : crmScopeTokens(org, group);
604
+ try {
605
+ await writeCrmEmailActivity(activityRef, buildCrmEmailActivity(_extends({
606
+ subject,
607
+ body: text,
608
+ to: recipient.email,
609
+ atMs: sentAtMs,
610
+ byUid: sender.uid
611
+ }, sender.name ? {
612
+ byName: sender.name
613
+ } : {}, {
614
+ link: recipient.link,
615
+ hostId,
616
+ visibleTo
617
+ })));
618
+ } catch (error) {
619
+ // The message has left. A row that could not be written is a gap on
620
+ // the timeline, not a failed send, and it is said so in the answer.
621
+ console.error('[crm] email activity write failed', orgId, activityRef.id, error);
622
+ logged = false;
623
+ }
624
+ // The cap's count was taken with the reservation; the org's cost meter
625
+ // for COGS is stamped after delivery, unable to fail the send.
626
+ await recordEmailSends({
627
+ scope: {
628
+ kind: 'org',
629
+ orgId
630
+ },
631
+ count: 1,
632
+ sendClass: 'transactional',
633
+ firestore
634
+ });
635
+ // The org-level act, in the org's feed: the site variant's is the
636
+ // timeline row itself, under the site the record page is on.
637
+ if (routeScope.level === 'org') {
638
+ await logOrgActivity(orgId, {
639
+ uid: sender.uid,
640
+ email: sender.email
641
+ }, 'Sent email', recipient.link.contactId ? {
642
+ type: 'contact',
643
+ id: recipient.link.contactId,
644
+ name: recipient.email
645
+ } : {
646
+ type: 'lead',
647
+ id: recipient.link.leadId,
648
+ name: recipient.email
649
+ });
650
+ }
651
+ res.status(200).json({
652
+ ok: true,
653
+ activityId: activityRef.id,
654
+ to: recipient.email,
655
+ from: identity.from,
656
+ logged
657
+ });
658
+ } catch (error) {
659
+ console.error('[crm] email-send failed', routeScope, error);
660
+ res.status(500).json({
661
+ error: 'The email could not be sent.'
662
+ });
663
+ }
664
+ };
665
+ export default crmEmailSendHandler;
666
+
667
+ //# sourceMappingURL=email-send.js.map