@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,73 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ /**
18
+ * `POST /api/crm/contacts-import` — one chunk of a contact file, written
19
+ * (AGL-2602).
20
+ *
21
+ * The browser has already read the file and applied the operator's column
22
+ * mapping; what arrives here is up to {@link CONTACT_IMPORT_CHUNK_SIZE} raw
23
+ * rows in the field vocabulary `crm-import.ts` defines. This route judges
24
+ * each one and writes it, and it is the ONLY place either happens: the
25
+ * drawer shows a preview from the same mapper but stores nothing, so a
26
+ * stale tab cannot store something the server would have refused.
27
+ *
28
+ * ## Every row goes through the door every capture goes through
29
+ *
30
+ * `captureHostContact` — the runtime's wrapper over `upsertHostContact`,
31
+ * the one writer of the contacts collection for every door that is not the
32
+ * v1 API: forms, checkout, bookings, the newsletter box — is the writer
33
+ * here too, so a row this file creates raises `contactCreated` like any
34
+ * other capture (AGL-2605). That is what makes an import
35
+ * dedupe on the address the way a second form submission does, land in the
36
+ * capturing group's facet the way a form capture does, record consent
37
+ * against the capturing site, and stop at a hard band where a form
38
+ * capture stops. A bulk path with its own `add()` would be the fastest way
39
+ * to grow an unscoped, unbanded, unconsented copy of the address book.
40
+ *
41
+ * What the door did not know how to write until now — a phone, a title, a
42
+ * company, an owner, a stage, custom values — it now takes as `facet`, and
43
+ * what it did not say until now — created or merged or refused — it now
44
+ * returns as a verdict. Both changes are the door's, so the v1 API and the
45
+ * manual add can reach them next.
46
+ *
47
+ * ## Two lookups paid once per request, not once per row
48
+ *
49
+ * An owner is named by email in the file and stored by uid on the record,
50
+ * so the org's roster is read once and every row resolves against it. A
51
+ * company is named by name and stored by id, so each distinct name is
52
+ * looked up once, created once when missing, and remembered for the rest
53
+ * of the request — two hundred rows at one company is one read and at most
54
+ * one write, not two hundred of each.
55
+ *
56
+ * ## Who may call it
57
+ *
58
+ * `resolveImportContext` (`import-context.ts`) — the two gates every CSV
59
+ * import route asks, shared with the companies import so one permission
60
+ * has one spelling.
61
+ */
62
+ import { type PluginApiHandler } from '@aglyn/aglyn/server';
63
+ /** The one sentence every imported contact's timeline opens with. */
64
+ export declare const CONTACT_IMPORT_INTERACTION_SUMMARY = "Imported from CSV";
65
+ /**
66
+ * `POST crm/contacts-import` — `{ hostId, rows }` → a {@link ContactImportChunkResult}.
67
+ *
68
+ * Rows are written one after another rather than in parallel, because the
69
+ * door's create path counts the collection before it adds: two hundred
70
+ * creates racing one another would each count the same audience and each
71
+ * pass a band the last of them should have hit.
72
+ */
73
+ export declare const crmContactsImportHandler: PluginApiHandler;
@@ -0,0 +1,263 @@
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
+ */ /**
18
+ * `POST /api/crm/contacts-import` — one chunk of a contact file, written
19
+ * (AGL-2602).
20
+ *
21
+ * The browser has already read the file and applied the operator's column
22
+ * mapping; what arrives here is up to {@link CONTACT_IMPORT_CHUNK_SIZE} raw
23
+ * rows in the field vocabulary `crm-import.ts` defines. This route judges
24
+ * each one and writes it, and it is the ONLY place either happens: the
25
+ * drawer shows a preview from the same mapper but stores nothing, so a
26
+ * stale tab cannot store something the server would have refused.
27
+ *
28
+ * ## Every row goes through the door every capture goes through
29
+ *
30
+ * `captureHostContact` — the runtime's wrapper over `upsertHostContact`,
31
+ * the one writer of the contacts collection for every door that is not the
32
+ * v1 API: forms, checkout, bookings, the newsletter box — is the writer
33
+ * here too, so a row this file creates raises `contactCreated` like any
34
+ * other capture (AGL-2605). That is what makes an import
35
+ * dedupe on the address the way a second form submission does, land in the
36
+ * capturing group's facet the way a form capture does, record consent
37
+ * against the capturing site, and stop at a hard band where a form
38
+ * capture stops. A bulk path with its own `add()` would be the fastest way
39
+ * to grow an unscoped, unbanded, unconsented copy of the address book.
40
+ *
41
+ * What the door did not know how to write until now — a phone, a title, a
42
+ * company, an owner, a stage, custom values — it now takes as `facet`, and
43
+ * what it did not say until now — created or merged or refused — it now
44
+ * returns as a verdict. Both changes are the door's, so the v1 API and the
45
+ * manual add can reach them next.
46
+ *
47
+ * ## Two lookups paid once per request, not once per row
48
+ *
49
+ * An owner is named by email in the file and stored by uid on the record,
50
+ * so the org's roster is read once and every row resolves against it. A
51
+ * company is named by name and stored by id, so each distinct name is
52
+ * looked up once, created once when missing, and remembered for the rest
53
+ * of the request — two hundred rows at one company is one read and at most
54
+ * one write, not two hundred of each.
55
+ *
56
+ * ## Who may call it
57
+ *
58
+ * `resolveImportContext` (`import-context.ts`) — the two gates every CSV
59
+ * import route asks, shared with the companies import so one permission
60
+ * has one spelling.
61
+ */ import { CRM_COLLECTIONS, nameSearchFields, visibleToTokens } from "@aglyn/aglyn/server";
62
+ import { CONTACT_IMPORT_CHUNK_SIZE, CONTACT_IMPORT_MAX_BODY_BYTES, normalizeContactImportRow } from "../model/crm-import.js";
63
+ import { crmRecordsQuotaForOrg, firebaseAdmin } from "@aglyn/tenant-data-admin";
64
+ import { captureHostContact } from "@aglyn/tenant-runtime";
65
+ import { FieldValue } from "firebase-admin/firestore";
66
+ import { ownerDirectory, readImportRows, resolveImportContext } from "./import-context.js";
67
+ /** The one sentence every imported contact's timeline opens with. */ export const CONTACT_IMPORT_INTERACTION_SUMMARY = 'Imported from CSV';
68
+ /**
69
+ * The holder's live custom-field definitions.
70
+ *
71
+ * Read in full and filtered in memory: a holder has a handful of fields,
72
+ * and a `where` on `retiredAt` would need an index for a collection that
73
+ * fits in one page. Definitions the caller's scope cannot see are left out
74
+ * for the same reason a value under an undefined key is — a value written
75
+ * under a field the holder's own form will never show is a value nobody
76
+ * can edit.
77
+ */ async function loadFieldDefinitions(orgId, readTokens) {
78
+ const snapshot = await firebaseAdmin.app().firestore().collection('orgs').doc(orgId).collection(CRM_COLLECTIONS.contactFields).limit(200).get();
79
+ return snapshot.docs.map((doc)=>doc.data()).filter((field)=>!!field.key && !field.retiredAt && visibleToTokens(field.visibleTo, readTokens));
80
+ }
81
+ /**
82
+ * The company a name refers to in this scope, created when there is none.
83
+ *
84
+ * Looked up by `nameLower`, the search twin `nameSearchFields` writes on
85
+ * every company, and narrowed in memory to the ones the caller's scope can
86
+ * see — two clients of one agency may each have an "Acme" and must each get
87
+ * their own. Created with the same scope stamp every CRM creator uses, so
88
+ * the company lands exactly where a contact captured on this site would.
89
+ */ async function resolveCompanyId(context, name, cache, tally) {
90
+ const fields = nameSearchFields(name);
91
+ const cached = cache.get(fields.nameLower);
92
+ if (cached) return cached;
93
+ const orgRef = firebaseAdmin.app().firestore().collection('orgs').doc(context.orgId);
94
+ const companies = orgRef.collection(CRM_COLLECTIONS.companies);
95
+ const matches = await companies.where('nameLower', '==', fields.nameLower).limit(10).get();
96
+ const seen = matches.docs.find((doc)=>visibleToTokens(doc.get('visibleTo'), context.readTokens));
97
+ if (seen) {
98
+ cache.set(fields.nameLower, seen.id);
99
+ return seen.id;
100
+ }
101
+ /*
102
+ * THE RECORDS BAND (AGL-2611). A company is a record of the same band
103
+ * the contact behind this row will meet at its own door, so on an org
104
+ * at a full hard band the row gets no company rather than a company it
105
+ * was not allowed to hold. Nothing is reported here: the contact is
106
+ * refused `audience-band` a few lines on and the row is listed skipped,
107
+ * which is the one message the operator needs. A plan with an overage
108
+ * rate never reaches this branch — a rate is what makes the band meter
109
+ * instead of refuse.
110
+ */ const room = await crmRecordsQuotaForOrg(context.org, orgRef);
111
+ if (!room.allowed) return '';
112
+ const created = await companies.add(_extends({}, fields, {
113
+ hostId: context.hostId,
114
+ visibleTo: context.scopeTokens,
115
+ createdByUid: context.uid,
116
+ createdAt: FieldValue.serverTimestamp(),
117
+ updatedAt: FieldValue.serverTimestamp()
118
+ }));
119
+ tally.created += 1;
120
+ cache.set(fields.nameLower, created.id);
121
+ return created.id;
122
+ }
123
+ /**
124
+ * `POST crm/contacts-import` — `{ hostId, rows }` → a {@link ContactImportChunkResult}.
125
+ *
126
+ * Rows are written one after another rather than in parallel, because the
127
+ * door's create path counts the collection before it adds: two hundred
128
+ * creates racing one another would each count the same audience and each
129
+ * pass a band the last of them should have hit.
130
+ */ export const crmContactsImportHandler = async (req, res)=>{
131
+ if (req.method !== 'POST') {
132
+ res.setHeader('Allow', 'POST');
133
+ return res.status(405).json({
134
+ error: 'Method not allowed'
135
+ });
136
+ }
137
+ const read = readImportRows(req, {
138
+ maxBodyBytes: CONTACT_IMPORT_MAX_BODY_BYTES,
139
+ chunkSize: CONTACT_IMPORT_CHUNK_SIZE
140
+ });
141
+ if ('error' in read) return res.status(read.status).json({
142
+ error: read.error
143
+ });
144
+ try {
145
+ const context = await resolveImportContext(req);
146
+ if (context.ok === false) return res.status(context.status).json(context.body);
147
+ const fields = await loadFieldDefinitions(context.orgId, context.readTokens);
148
+ const skipped = [];
149
+ const dropped = {};
150
+ const normalized = [];
151
+ /*
152
+ * A duplicate WITHIN the request is skipped rather than merged: the
153
+ * second row would merge onto the first's record and the count would
154
+ * read one created and one merged for one person in one file, which is
155
+ * a number the operator cannot reconcile against the rows they sent.
156
+ * Across requests the door's own dedupe answers, and reports a merge.
157
+ */ const seen = new Set();
158
+ read.rows.forEach((raw, index)=>{
159
+ const verdict = normalizeContactImportRow(raw, fields);
160
+ if (verdict.ok === false) {
161
+ skipped.push({
162
+ index,
163
+ email: verdict.input,
164
+ reason: verdict.reason
165
+ });
166
+ return;
167
+ }
168
+ if (seen.has(verdict.row.email)) {
169
+ skipped.push({
170
+ index,
171
+ email: verdict.row.email,
172
+ reason: 'duplicate'
173
+ });
174
+ return;
175
+ }
176
+ seen.add(verdict.row.email);
177
+ for (const entry of verdict.row.dropped){
178
+ var _dropped_entry_field;
179
+ dropped[entry.field] = ((_dropped_entry_field = dropped[entry.field]) != null ? _dropped_entry_field : 0) + 1;
180
+ }
181
+ normalized.push({
182
+ index,
183
+ row: verdict.row
184
+ });
185
+ });
186
+ const owners = await ownerDirectory(context.orgId, normalized.map((entry)=>entry.row));
187
+ const ownersUnresolved = new Set();
188
+ const companies = new Map();
189
+ const companyTally = {
190
+ created: 0
191
+ };
192
+ let created = 0;
193
+ let merged = 0;
194
+ for (const { index, row } of normalized){
195
+ let ownerUid;
196
+ if (row.ownerEmail) {
197
+ ownerUid = owners.get(row.ownerEmail);
198
+ if (!ownerUid) ownersUnresolved.add(row.ownerEmail);
199
+ }
200
+ const companyId = row.companyName ? await resolveCompanyId(context, row.companyName, companies, companyTally) : undefined;
201
+ const verdict = await captureHostContact(_extends({
202
+ hostId: context.hostId,
203
+ email: row.email
204
+ }, row.name ? {
205
+ name: row.name
206
+ } : {}, {
207
+ source: 'import',
208
+ interaction: {
209
+ summary: CONTACT_IMPORT_INTERACTION_SUMMARY
210
+ },
211
+ marketingConsent: row.marketingConsent,
212
+ tags: row.tags,
213
+ facet: _extends({}, row.phone ? {
214
+ phone: row.phone
215
+ } : {}, row.jobTitle ? {
216
+ jobTitle: row.jobTitle
217
+ } : {}, companyId ? {
218
+ companyId
219
+ } : {}, row.address ? {
220
+ address: row.address
221
+ } : {}, ownerUid ? {
222
+ ownerUid
223
+ } : {}, row.lifecycleStage ? {
224
+ lifecycleStage: row.lifecycleStage
225
+ } : {}, Object.keys(row.custom).length ? {
226
+ custom: row.custom
227
+ } : {}),
228
+ // An import is the merchant's own act and files nobody under a
229
+ // campaign; the picker on the profile is where that happens.
230
+ campaignIds: []
231
+ }));
232
+ if ('refused' in verdict) {
233
+ skipped.push({
234
+ index,
235
+ email: row.email,
236
+ reason: verdict.refused === 'band' ? 'audience-band' : verdict.refused === 'invalid-email' ? 'invalid-email' : verdict.refused === 'erased' ? 'erased' : 'write-failed'
237
+ });
238
+ continue;
239
+ }
240
+ if (verdict.created) created += 1;
241
+ else merged += 1;
242
+ }
243
+ const result = {
244
+ received: read.rows.length,
245
+ created,
246
+ merged,
247
+ skipped: skipped.sort((a, b)=>a.index - b.index),
248
+ dropped,
249
+ companiesCreated: companyTally.created,
250
+ ownersUnresolved: [
251
+ ...ownersUnresolved
252
+ ]
253
+ };
254
+ return res.status(200).json(result);
255
+ } catch (error) {
256
+ console.error('crm/contacts-import failed', error);
257
+ return res.status(500).json({
258
+ error: 'The import could not continue.'
259
+ });
260
+ }
261
+ };
262
+
263
+ //# sourceMappingURL=contacts-import.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/server/contacts-import.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * `POST /api/crm/contacts-import` — one chunk of a contact file, written\n * (AGL-2602).\n *\n * The browser has already read the file and applied the operator's column\n * mapping; what arrives here is up to {@link CONTACT_IMPORT_CHUNK_SIZE} raw\n * rows in the field vocabulary `crm-import.ts` defines. This route judges\n * each one and writes it, and it is the ONLY place either happens: the\n * drawer shows a preview from the same mapper but stores nothing, so a\n * stale tab cannot store something the server would have refused.\n *\n * ## Every row goes through the door every capture goes through\n *\n * `captureHostContact` — the runtime's wrapper over `upsertHostContact`,\n * the one writer of the contacts collection for every door that is not the\n * v1 API: forms, checkout, bookings, the newsletter box — is the writer\n * here too, so a row this file creates raises `contactCreated` like any\n * other capture (AGL-2605). That is what makes an import\n * dedupe on the address the way a second form submission does, land in the\n * capturing group's facet the way a form capture does, record consent\n * against the capturing site, and stop at a hard band where a form\n * capture stops. A bulk path with its own `add()` would be the fastest way\n * to grow an unscoped, unbanded, unconsented copy of the address book.\n *\n * What the door did not know how to write until now — a phone, a title, a\n * company, an owner, a stage, custom values — it now takes as `facet`, and\n * what it did not say until now — created or merged or refused — it now\n * returns as a verdict. Both changes are the door's, so the v1 API and the\n * manual add can reach them next.\n *\n * ## Two lookups paid once per request, not once per row\n *\n * An owner is named by email in the file and stored by uid on the record,\n * so the org's roster is read once and every row resolves against it. A\n * company is named by name and stored by id, so each distinct name is\n * looked up once, created once when missing, and remembered for the rest\n * of the request — two hundred rows at one company is one read and at most\n * one write, not two hundred of each.\n *\n * ## Who may call it\n *\n * `resolveImportContext` (`import-context.ts`) — the two gates every CSV\n * import route asks, shared with the companies import so one permission\n * has one spelling.\n */\n\nimport {\n type ContactFieldDefinition,\n CRM_COLLECTIONS,\n nameSearchFields,\n type PluginApiHandler,\n visibleToTokens,\n} from '@aglyn/aglyn/server'\nimport {\n CONTACT_IMPORT_CHUNK_SIZE,\n CONTACT_IMPORT_MAX_BODY_BYTES,\n type ContactImportChunkResult,\n type ContactImportRawRow,\n type ContactImportRow,\n type ContactImportSkippedRow,\n normalizeContactImportRow,\n} from '../model/crm-import'\nimport { crmRecordsQuotaForOrg, firebaseAdmin } from '@aglyn/tenant-data-admin'\nimport { captureHostContact } from '@aglyn/tenant-runtime'\nimport { FieldValue } from 'firebase-admin/firestore'\nimport {\n type ImportContext,\n ownerDirectory,\n readImportRows,\n resolveImportContext,\n} from './import-context'\n\n/** The one sentence every imported contact's timeline opens with. */\nexport const CONTACT_IMPORT_INTERACTION_SUMMARY = 'Imported from CSV'\n\n/**\n * The holder's live custom-field definitions.\n *\n * Read in full and filtered in memory: a holder has a handful of fields,\n * and a `where` on `retiredAt` would need an index for a collection that\n * fits in one page. Definitions the caller's scope cannot see are left out\n * for the same reason a value under an undefined key is — a value written\n * under a field the holder's own form will never show is a value nobody\n * can edit.\n */\nasync function loadFieldDefinitions(\n orgId: string,\n readTokens: readonly string[],\n): Promise<ContactFieldDefinition[]> {\n const snapshot = await firebaseAdmin\n .app()\n .firestore()\n .collection('orgs')\n .doc(orgId)\n .collection(CRM_COLLECTIONS.contactFields)\n .limit(200)\n .get()\n return snapshot.docs\n .map((doc) => doc.data() as ContactFieldDefinition)\n .filter(\n (field) =>\n !!field.key &&\n !field.retiredAt &&\n visibleToTokens(field.visibleTo, readTokens),\n )\n}\n\n/**\n * The company a name refers to in this scope, created when there is none.\n *\n * Looked up by `nameLower`, the search twin `nameSearchFields` writes on\n * every company, and narrowed in memory to the ones the caller's scope can\n * see — two clients of one agency may each have an \"Acme\" and must each get\n * their own. Created with the same scope stamp every CRM creator uses, so\n * the company lands exactly where a contact captured on this site would.\n */\nasync function resolveCompanyId(\n context: Extract<ImportContext, { ok: true }>,\n name: string,\n cache: Map<string, string>,\n tally: { created: number },\n): Promise<string> {\n const fields = nameSearchFields(name)\n const cached = cache.get(fields.nameLower)\n if (cached) return cached\n const orgRef = firebaseAdmin\n .app()\n .firestore()\n .collection('orgs')\n .doc(context.orgId)\n const companies = orgRef.collection(CRM_COLLECTIONS.companies)\n const matches = await companies\n .where('nameLower', '==', fields.nameLower)\n .limit(10)\n .get()\n const seen = matches.docs.find((doc) =>\n visibleToTokens(doc.get('visibleTo'), context.readTokens),\n )\n if (seen) {\n cache.set(fields.nameLower, seen.id)\n return seen.id\n }\n /*\n * THE RECORDS BAND (AGL-2611). A company is a record of the same band\n * the contact behind this row will meet at its own door, so on an org\n * at a full hard band the row gets no company rather than a company it\n * was not allowed to hold. Nothing is reported here: the contact is\n * refused `audience-band` a few lines on and the row is listed skipped,\n * which is the one message the operator needs. A plan with an overage\n * rate never reaches this branch — a rate is what makes the band meter\n * instead of refuse.\n */\n const room = await crmRecordsQuotaForOrg(context.org as never, orgRef)\n if (!room.allowed) return ''\n const created = await companies.add({\n ...fields,\n hostId: context.hostId,\n visibleTo: context.scopeTokens,\n createdByUid: context.uid,\n createdAt: FieldValue.serverTimestamp(),\n updatedAt: FieldValue.serverTimestamp(),\n })\n tally.created += 1\n cache.set(fields.nameLower, created.id)\n return created.id\n}\n\n/**\n * `POST crm/contacts-import` — `{ hostId, rows }` → a {@link ContactImportChunkResult}.\n *\n * Rows are written one after another rather than in parallel, because the\n * door's create path counts the collection before it adds: two hundred\n * creates racing one another would each count the same audience and each\n * pass a band the last of them should have hit.\n */\nexport const crmContactsImportHandler: PluginApiHandler = async (req, res) => {\n if (req.method !== 'POST') {\n res.setHeader('Allow', 'POST')\n return res.status(405).json({ error: 'Method not allowed' })\n }\n const read = readImportRows<ContactImportRawRow>(req, {\n maxBodyBytes: CONTACT_IMPORT_MAX_BODY_BYTES,\n chunkSize: CONTACT_IMPORT_CHUNK_SIZE,\n })\n if ('error' in read) return res.status(read.status).json({ error: read.error })\n try {\n const context = await resolveImportContext(req)\n if (context.ok === false) return res.status(context.status).json(context.body)\n\n const fields = await loadFieldDefinitions(context.orgId, context.readTokens)\n const skipped: ContactImportSkippedRow[] = []\n const dropped: Record<string, number> = {}\n const normalized: { index: number; row: ContactImportRow }[] = []\n /*\n * A duplicate WITHIN the request is skipped rather than merged: the\n * second row would merge onto the first's record and the count would\n * read one created and one merged for one person in one file, which is\n * a number the operator cannot reconcile against the rows they sent.\n * Across requests the door's own dedupe answers, and reports a merge.\n */\n const seen = new Set<string>()\n read.rows.forEach((raw, index) => {\n const verdict = normalizeContactImportRow(raw, fields)\n if (verdict.ok === false) {\n skipped.push({ index, email: verdict.input, reason: verdict.reason })\n return\n }\n if (seen.has(verdict.row.email)) {\n skipped.push({ index, email: verdict.row.email, reason: 'duplicate' })\n return\n }\n seen.add(verdict.row.email)\n for (const entry of verdict.row.dropped) {\n dropped[entry.field] = (dropped[entry.field] ?? 0) + 1\n }\n normalized.push({ index, row: verdict.row })\n })\n\n const owners = await ownerDirectory(\n context.orgId,\n normalized.map((entry) => entry.row),\n )\n const ownersUnresolved = new Set<string>()\n const companies = new Map<string, string>()\n const companyTally = { created: 0 }\n let created = 0\n let merged = 0\n\n for (const { index, row } of normalized) {\n let ownerUid: string | undefined\n if (row.ownerEmail) {\n ownerUid = owners.get(row.ownerEmail)\n if (!ownerUid) ownersUnresolved.add(row.ownerEmail)\n }\n const companyId = row.companyName\n ? await resolveCompanyId(context, row.companyName, companies, companyTally)\n : undefined\n const verdict = await captureHostContact({\n hostId: context.hostId,\n email: row.email,\n ...(row.name ? { name: row.name } : {}),\n source: 'import',\n interaction: { summary: CONTACT_IMPORT_INTERACTION_SUMMARY },\n marketingConsent: row.marketingConsent,\n tags: row.tags,\n facet: {\n ...(row.phone ? { phone: row.phone } : {}),\n ...(row.jobTitle ? { jobTitle: row.jobTitle } : {}),\n ...(companyId ? { companyId } : {}),\n ...(row.address ? { address: row.address } : {}),\n ...(ownerUid ? { ownerUid } : {}),\n ...(row.lifecycleStage ? { lifecycleStage: row.lifecycleStage } : {}),\n ...(Object.keys(row.custom).length ? { custom: row.custom } : {}),\n },\n // An import is the merchant's own act and files nobody under a\n // campaign; the picker on the profile is where that happens.\n campaignIds: [],\n })\n if ('refused' in verdict) {\n skipped.push({\n index,\n email: row.email,\n reason:\n verdict.refused === 'band'\n ? 'audience-band'\n : verdict.refused === 'invalid-email'\n ? 'invalid-email'\n : verdict.refused === 'erased'\n ? 'erased'\n : 'write-failed',\n })\n continue\n }\n if (verdict.created) created += 1\n else merged += 1\n }\n\n const result: ContactImportChunkResult = {\n received: read.rows.length,\n created,\n merged,\n skipped: skipped.sort((a, b) => a.index - b.index),\n dropped,\n companiesCreated: companyTally.created,\n ownersUnresolved: [...ownersUnresolved],\n }\n return res.status(200).json(result)\n } catch (error) {\n console.error('crm/contacts-import failed', error)\n return res.status(500).json({ error: 'The import could not continue.' })\n }\n}\n"],"names":["CRM_COLLECTIONS","nameSearchFields","visibleToTokens","CONTACT_IMPORT_CHUNK_SIZE","CONTACT_IMPORT_MAX_BODY_BYTES","normalizeContactImportRow","crmRecordsQuotaForOrg","firebaseAdmin","captureHostContact","FieldValue","ownerDirectory","readImportRows","resolveImportContext","CONTACT_IMPORT_INTERACTION_SUMMARY","loadFieldDefinitions","orgId","readTokens","snapshot","app","firestore","collection","doc","contactFields","limit","get","docs","map","data","filter","field","key","retiredAt","visibleTo","resolveCompanyId","context","name","cache","tally","fields","cached","nameLower","orgRef","companies","matches","where","seen","find","set","id","room","org","allowed","created","add","hostId","scopeTokens","createdByUid","uid","createdAt","serverTimestamp","updatedAt","crmContactsImportHandler","req","res","method","setHeader","status","json","error","read","maxBodyBytes","chunkSize","ok","body","skipped","dropped","normalized","Set","rows","forEach","raw","index","verdict","push","email","input","reason","has","row","entry","owners","ownersUnresolved","Map","companyTally","merged","ownerUid","ownerEmail","companyId","companyName","undefined","source","interaction","summary","marketingConsent","tags","facet","phone","jobTitle","address","lifecycleStage","Object","keys","custom","length","campaignIds","refused","result","received","sort","a","b","companiesCreated","console"],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ /**
18
+ * `POST /api/crm/contacts-merge` — two records for one person become one
19
+ * (AGL-2625).
20
+ *
21
+ * Body: `{ hostId, survivorId, mergedId }`. The survivor keeps its address
22
+ * as the identity; the merged record's address becomes an alternate on it,
23
+ * every deal, task, activity and lead naming the merged record is repointed,
24
+ * and the merged document is deleted. `mergeContacts` in the data library
25
+ * is the whole of the work, so the REST door performs the same merge.
26
+ *
27
+ * ## Who may call it
28
+ *
29
+ * An ORG-WIDE member holding `data.manage`. Narrower than the other CRM
30
+ * routes, which admit a site-scoped member for the site they reach, and
31
+ * deliberately: a merge is org-scoped by nature. Contacts are org documents
32
+ * shared by every site that captured the person, and folding two of them
33
+ * together touches every holder's facet, every site's consent entry and
34
+ * every deal in the org that named the merged record — a decision about
35
+ * records a site-scoped member cannot read, made on their behalf. The
36
+ * console offers the action to org-wide members only, and this is the door
37
+ * that makes that a rule rather than a hint.
38
+ *
39
+ * ## Why a server route
40
+ *
41
+ * A browser could write the survivor and delete the merged record under the
42
+ * rules, and could not do the rest: a scoped `array-contains-any` listener
43
+ * cannot repoint deals it may not list, the address index is closed to
44
+ * clients, and the transaction over both documents is what keeps two merges
45
+ * of one pair from both succeeding.
46
+ *
47
+ * ## The organization variant (AGL-2634)
48
+ *
49
+ * `{ orgId, hostId?, survivorId, mergedId }` from the org-level hub. The
50
+ * same org-wide caller, authorized by the org directly rather than through
51
+ * a site it names — so a record no site captured can be merged there — and
52
+ * the act logged in the org's feed. A site beside the org is the record's
53
+ * own, and is where the data library files the timeline note and the
54
+ * site's feed line when there is one.
55
+ */
56
+ import type { PluginApiHandler } from '@aglyn/aglyn/server';
57
+ export declare const CONTACTS_MERGE_ROUTE = "crm/contacts-merge";
58
+ /** The body the console's merge dialog posts. */
59
+ export interface ContactsMergeRequest {
60
+ /** The site the merge runs as; at the organization level the record's own, or absent. */
61
+ hostId?: string;
62
+ /** The organization, at the organization level (AGL-2634). */
63
+ orgId?: string;
64
+ /** `orgs/{orgId}/contacts/{id}` — the record that stays. */
65
+ survivorId: string;
66
+ /** The record folded into it and deleted. */
67
+ mergedId: string;
68
+ }
69
+ /** What the route answers on success. */
70
+ export interface ContactsMergeResponse {
71
+ ok: true;
72
+ survivorId: string;
73
+ survivorEmail: string;
74
+ mergedId: string;
75
+ mergedEmail: string;
76
+ /** Every address the survivor answers to now, primary first. */
77
+ emails: string[];
78
+ repointed: {
79
+ deals: number;
80
+ tasks: number;
81
+ activities: number;
82
+ leads: number;
83
+ };
84
+ }
85
+ export declare const contactsMergeHandler: PluginApiHandler;
86
+ export default contactsMergeHandler;
@@ -0,0 +1,206 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ /**
17
+ * `POST /api/crm/contacts-merge` — two records for one person become one
18
+ * (AGL-2625).
19
+ *
20
+ * Body: `{ hostId, survivorId, mergedId }`. The survivor keeps its address
21
+ * as the identity; the merged record's address becomes an alternate on it,
22
+ * every deal, task, activity and lead naming the merged record is repointed,
23
+ * and the merged document is deleted. `mergeContacts` in the data library
24
+ * is the whole of the work, so the REST door performs the same merge.
25
+ *
26
+ * ## Who may call it
27
+ *
28
+ * An ORG-WIDE member holding `data.manage`. Narrower than the other CRM
29
+ * routes, which admit a site-scoped member for the site they reach, and
30
+ * deliberately: a merge is org-scoped by nature. Contacts are org documents
31
+ * shared by every site that captured the person, and folding two of them
32
+ * together touches every holder's facet, every site's consent entry and
33
+ * every deal in the org that named the merged record — a decision about
34
+ * records a site-scoped member cannot read, made on their behalf. The
35
+ * console offers the action to org-wide members only, and this is the door
36
+ * that makes that a rule rather than a hint.
37
+ *
38
+ * ## Why a server route
39
+ *
40
+ * A browser could write the survivor and delete the merged record under the
41
+ * rules, and could not do the rest: a scoped `array-contains-any` listener
42
+ * cannot repoint deals it may not list, the address index is closed to
43
+ * clients, and the transaction over both documents is what keeps two merges
44
+ * of one pair from both succeeding.
45
+ *
46
+ * ## The organization variant (AGL-2634)
47
+ *
48
+ * `{ orgId, hostId?, survivorId, mergedId }` from the org-level hub. The
49
+ * same org-wide caller, authorized by the org directly rather than through
50
+ * a site it names — so a record no site captured can be merged there — and
51
+ * the act logged in the org's feed. A site beside the org is the record's
52
+ * own, and is where the data library files the timeline note and the
53
+ * site's feed line when there is one.
54
+ */ import { firebaseAdmin, getOrgForHost, logOrgActivity, mergeContacts } from "@aglyn/tenant-data-admin";
55
+ import { resolveOrgPermissions } from "@aglyn/tenant-runtime/org-permissions";
56
+ import { authorizeOrgCaller, holdsDataManage, readCrmRouteScope } from "./org-caller.js";
57
+ import { crmSuiteRefusal } from "./suite-gate.js";
58
+ export const CONTACTS_MERGE_ROUTE = 'crm/contacts-merge';
59
+ function typed(value, max) {
60
+ return String(value != null ? value : '').trim().slice(0, max);
61
+ }
62
+ export const contactsMergeHandler = async (req, res)=>{
63
+ var _req_body;
64
+ if (req.method !== 'POST') {
65
+ res.setHeader('Allow', 'POST');
66
+ res.status(405).json({
67
+ error: 'Method not allowed'
68
+ });
69
+ return;
70
+ }
71
+ const body = typeof req.body === 'string' ? JSON.parse(req.body) : (_req_body = req.body) != null ? _req_body : {};
72
+ const routeScope = readCrmRouteScope(body);
73
+ const survivorId = typed(body.survivorId, 200);
74
+ const mergedId = typed(body.mergedId, 200);
75
+ if (!routeScope || !survivorId || !mergedId) {
76
+ res.status(400).json({
77
+ error: 'Missing hostId, survivorId or mergedId'
78
+ });
79
+ return;
80
+ }
81
+ if (survivorId === mergedId) {
82
+ res.status(400).json({
83
+ error: 'Pick two different contacts to merge'
84
+ });
85
+ return;
86
+ }
87
+ const { hostId } = routeScope;
88
+ const refusal = 'Merging contacts requires the data permission across the whole workspace';
89
+ try {
90
+ let orgId;
91
+ let org;
92
+ let actor;
93
+ let actorName;
94
+ let orgActor = null;
95
+ if (routeScope.level === 'org') {
96
+ const caller = await authorizeOrgCaller(req, routeScope.orgId, {
97
+ needs: 'data.manage',
98
+ refusal
99
+ });
100
+ if (caller.ok === false) {
101
+ res.status(caller.status).json({
102
+ error: caller.error
103
+ });
104
+ return;
105
+ }
106
+ orgId = caller.orgId;
107
+ org = caller.org;
108
+ actor = {
109
+ uid: caller.uid,
110
+ email: caller.email
111
+ };
112
+ actorName = caller.name || null;
113
+ orgActor = actor;
114
+ } else {
115
+ var _req_headers_authorization, _decoded_email;
116
+ const authorization = String((_req_headers_authorization = req.headers.authorization) != null ? _req_headers_authorization : '');
117
+ const idToken = authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) : undefined;
118
+ if (!idToken) {
119
+ res.status(401).json({
120
+ error: 'Unauthenticated'
121
+ });
122
+ return;
123
+ }
124
+ const decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken);
125
+ const staff = decoded['staff'] === true;
126
+ const membership = await resolveOrgPermissions(decoded.uid, {
127
+ hostId
128
+ });
129
+ if (!staff && !(membership.orgWide && await holdsDataManage(membership.orgId, decoded.uid))) {
130
+ res.status(403).json({
131
+ error: refusal
132
+ });
133
+ return;
134
+ }
135
+ const resolved = await getOrgForHost(hostId);
136
+ if (!resolved) {
137
+ res.status(404).json({
138
+ error: 'Unknown site'
139
+ });
140
+ return;
141
+ }
142
+ orgId = resolved.orgId;
143
+ org = resolved.org;
144
+ actor = {
145
+ uid: decoded.uid,
146
+ email: (_decoded_email = decoded.email) != null ? _decoded_email : null
147
+ };
148
+ actorName = typeof decoded['name'] === 'string' ? decoded['name'] : null;
149
+ }
150
+ // Merging is the CRM's at either level (AGL-2787).
151
+ const suite = crmSuiteRefusal(org, 'Merging two contacts');
152
+ if (suite) {
153
+ res.status(suite.status).json(suite.body);
154
+ return;
155
+ }
156
+ const firestore = firebaseAdmin.app().firestore();
157
+ const result = await mergeContacts({
158
+ firestore,
159
+ orgRef: firestore.collection('orgs').doc(orgId),
160
+ survivorId,
161
+ mergedId,
162
+ actor,
163
+ hostId: hostId || null,
164
+ actorName
165
+ });
166
+ if (result.ok === false) {
167
+ if (result.reason === 'same-record') {
168
+ res.status(400).json({
169
+ error: 'Pick two different contacts to merge'
170
+ });
171
+ return;
172
+ }
173
+ res.status(404).json({
174
+ error: result.reason === 'survivor-missing' ? 'The contact to keep could not be found' : 'The contact to merge could not be found — it may already have been merged'
175
+ });
176
+ return;
177
+ }
178
+ // The org-level act, in the org's feed: the data library wrote the
179
+ // site's line when a site was named, and the org's is this door's.
180
+ if (orgActor) {
181
+ await logOrgActivity(orgId, orgActor, `Merged with ${result.mergedEmail}`, {
182
+ type: 'contact',
183
+ id: result.survivorId,
184
+ name: result.survivorEmail
185
+ });
186
+ }
187
+ const answer = {
188
+ ok: true,
189
+ survivorId: result.survivorId,
190
+ survivorEmail: result.survivorEmail,
191
+ mergedId: result.mergedId,
192
+ mergedEmail: result.mergedEmail,
193
+ emails: result.emails,
194
+ repointed: result.repointed
195
+ };
196
+ res.status(200).json(answer);
197
+ } catch (error) {
198
+ console.error('[crm] contacts-merge failed', routeScope, survivorId, mergedId, error);
199
+ res.status(500).json({
200
+ error: 'The contacts could not be merged.'
201
+ });
202
+ }
203
+ };
204
+ export default contactsMergeHandler;
205
+
206
+ //# sourceMappingURL=contacts-merge.js.map