@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,309 @@
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 { canManageOrg, CONTACT_ERASURE_REQUESTED_FIELD, normalizeContactEmail, PERSON_ERASURES_COLLECTION, personErasureConfirmationMatches, personErasureId } from "@aglyn/aglyn/server";
18
+ import { personKey } from "@aglyn/aglyn/app-utils/person-key";
19
+ import { firebaseAdmin, getOrgForHost, logHostActivity, logOrgActivity, orgDataCollectionForHost, suppressEmailForHostErasure } from "@aglyn/tenant-data-admin";
20
+ import { resolveOrgPermissions } from "@aglyn/tenant-runtime/org-permissions";
21
+ import { FieldValue } from "firebase-admin/firestore";
22
+ import { authorizeOrgCaller, orgHostIds, readCrmRouteScope } from "./org-caller.js";
23
+ /** The route key, as `registerCrmConsoleApi` registers it. */ export const CRM_ERASE_PERSON_ROUTE = 'crm/erase-person';
24
+ /**
25
+ * `POST /api/crm/erase-person` — file a privacy erasure for one person
26
+ * (AGL-2623).
27
+ *
28
+ * ## Who may
29
+ *
30
+ * A workspace admin or owner — `canManageOrg` on the caller's org role —
31
+ * and nobody else. Not a site editor with the data permission, who can
32
+ * detach a contact from their own site but has no standing to remove the
33
+ * person from every site in the workspace; and not staff acting alone,
34
+ * because the workspace is the controller of this data and the instruction
35
+ * has to come from it. Staff who are also admins of the workspace pass on
36
+ * that role, as anyone would.
37
+ *
38
+ * ## What it does, and what it does not
39
+ *
40
+ * It files the request and closes the doors; it deletes nothing. The daily
41
+ * erasure job executes the sweep, so that a workspace's person erasures and
42
+ * its own erasure run through one job, one audit trail and one queue a
43
+ * staff member can watch. What happens here, in order:
44
+ *
45
+ * 1. The record named by the body is read and its address is what the
46
+ * request is about — the body's address must match it, typed, so an
47
+ * admin who opened the wrong record is stopped by the confirmation
48
+ * rather than by luck.
49
+ * 2. The request document is written, or found already pending, in
50
+ * which case nothing is re-filed and the caller is told so.
51
+ * 3. Every site of the workspace gets its suppression row NOW, not when
52
+ * the job runs: from this moment a capture cannot rebuild the person.
53
+ * 4. The contact document and each site's lead are stamped with the
54
+ * request time, so their pages can say "erasure pending" off the
55
+ * document they already read.
56
+ * 5. The site's activity feed and the platform audit log each get a row
57
+ * that names the record by id and the person by hash, never by
58
+ * address.
59
+ *
60
+ * ## The organization variant (AGL-2634)
61
+ *
62
+ * `{ orgId, hostId?, contactId | leadId, email }` from the org-level hub:
63
+ * the same workspace admin, authorized by the org rather than through a
64
+ * site they name, so a contact no site captured can be erased from the
65
+ * page that lists it. The sweep is what it always was — every site of the
66
+ * org, found by `orgId` — and the row goes to the org's feed. A lead still
67
+ * names its site, because that is where a lead lives.
68
+ *
69
+ * ## By address alone (AGL-2839)
70
+ *
71
+ * `{ orgId, email, confirmEmail }` with no record, from the workspace's
72
+ * Privacy settings: the same workspace admin, for any person the workspace
73
+ * may hold — including one no page shows, since the CRM that lists records
74
+ * is included from Starter and a Free workspace has none of it. Nothing is
75
+ * read to file it: the sweep is keyed by the address, the markers are found
76
+ * by it, and the typed address must match its second typing, which is the
77
+ * confirmation a record's address gives the other variants. No plan is
78
+ * asked on any variant: erasure is an obligation, not a CRM feature.
79
+ */ export const crmErasePersonHandler = async (req, res)=>{
80
+ var _req_headers_authorization, _req_body, _body_contactId, _body_leadId;
81
+ if (req.method !== 'POST') {
82
+ res.setHeader('Allow', 'POST');
83
+ res.status(405).json({
84
+ error: 'Method not allowed'
85
+ });
86
+ return;
87
+ }
88
+ const authorization = String((_req_headers_authorization = req.headers.authorization) != null ? _req_headers_authorization : '');
89
+ const idToken = authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) : undefined;
90
+ if (!idToken) {
91
+ res.status(401).json({
92
+ error: 'Unauthenticated'
93
+ });
94
+ return;
95
+ }
96
+ const body = typeof req.body === 'string' ? JSON.parse(req.body) : (_req_body = req.body) != null ? _req_body : {};
97
+ const routeScope = readCrmRouteScope(body);
98
+ const contactId = String((_body_contactId = body.contactId) != null ? _body_contactId : '').trim();
99
+ const leadId = String((_body_leadId = body.leadId) != null ? _body_leadId : '').trim();
100
+ const byAddress = (routeScope == null ? void 0 : routeScope.level) === 'org' && !contactId && !leadId;
101
+ if (!routeScope || !byAddress && !contactId && !leadId || contactId && leadId) {
102
+ res.status(400).json({
103
+ error: 'Name the site and exactly one contact or lead'
104
+ });
105
+ return;
106
+ }
107
+ const { hostId } = routeScope;
108
+ if (leadId && !hostId) {
109
+ res.status(400).json({
110
+ error: 'Name the site the lead lives under'
111
+ });
112
+ return;
113
+ }
114
+ const refusal = 'Only a workspace admin can erase a person from the workspace';
115
+ try {
116
+ let orgId;
117
+ let actor;
118
+ if (routeScope.level === 'org') {
119
+ const caller = await authorizeOrgCaller(req, routeScope.orgId, {
120
+ needs: 'manage-org',
121
+ refusal
122
+ });
123
+ if (caller.ok === false) {
124
+ res.status(caller.status).json({
125
+ error: caller.error
126
+ });
127
+ return;
128
+ }
129
+ orgId = caller.orgId;
130
+ actor = {
131
+ uid: caller.uid,
132
+ email: caller.email
133
+ };
134
+ } else {
135
+ var _decoded_email;
136
+ const decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken);
137
+ const membership = await resolveOrgPermissions(decoded.uid, {
138
+ hostId
139
+ });
140
+ if (!canManageOrg(membership.role)) {
141
+ res.status(403).json({
142
+ error: refusal
143
+ });
144
+ return;
145
+ }
146
+ const resolved = await getOrgForHost(hostId);
147
+ if (!resolved || resolved.orgId !== membership.orgId) {
148
+ res.status(404).json({
149
+ error: 'Unknown site'
150
+ });
151
+ return;
152
+ }
153
+ orgId = resolved.orgId;
154
+ actor = {
155
+ uid: decoded.uid,
156
+ email: (_decoded_email = decoded.email) != null ? _decoded_email : null
157
+ };
158
+ }
159
+ const firestore = firebaseAdmin.app().firestore();
160
+ let recordEmail = null;
161
+ if (byAddress) {
162
+ recordEmail = normalizeContactEmail(body.email);
163
+ } else if (contactId) {
164
+ const contactsRef = routeScope.level === 'org' ? firestore.collection('orgs').doc(orgId).collection('contacts') : await orgDataCollectionForHost(hostId, 'contacts');
165
+ const contact = await contactsRef.doc(contactId).get();
166
+ if (!contact.exists) {
167
+ res.status(404).json({
168
+ error: 'Unknown contact'
169
+ });
170
+ return;
171
+ }
172
+ recordEmail = normalizeContactEmail(contact.get('email'));
173
+ } else {
174
+ const lead = await firestore.collection('hosts').doc(hostId).collection('leads').doc(leadId).get();
175
+ if (!lead.exists) {
176
+ res.status(404).json({
177
+ error: 'Unknown lead'
178
+ });
179
+ return;
180
+ }
181
+ recordEmail = normalizeContactEmail(lead.get('email'));
182
+ }
183
+ const key = recordEmail ? personKey(recordEmail) : null;
184
+ if (!recordEmail || !key) {
185
+ res.status(byAddress ? 400 : 422).json({
186
+ error: byAddress ? 'Enter the email address of the person to erase' : 'This record has no usable email address to erase by'
187
+ });
188
+ return;
189
+ }
190
+ if (byAddress && !personErasureConfirmationMatches(body.confirmEmail, recordEmail)) {
191
+ res.status(400).json({
192
+ error: 'Type the email address a second time, exactly, to confirm the erasure'
193
+ });
194
+ return;
195
+ }
196
+ if (!byAddress && !personErasureConfirmationMatches(body.email, recordEmail)) {
197
+ res.status(400).json({
198
+ error: 'Type the record’s email address exactly to confirm the erasure'
199
+ });
200
+ return;
201
+ }
202
+ const requestId = personErasureId(orgId, key);
203
+ const requestRef = firestore.collection(PERSON_ERASURES_COLLECTION).doc(requestId);
204
+ const existing = await requestRef.get();
205
+ if (existing.exists && existing.get('status') === 'pending') {
206
+ var _existing_get;
207
+ const answer = {
208
+ ok: true,
209
+ requestId,
210
+ pendingSinceMs: Number((_existing_get = existing.get('pendingSinceMs')) != null ? _existing_get : 0),
211
+ alreadyPending: true
212
+ };
213
+ res.status(200).json(answer);
214
+ return;
215
+ }
216
+ const now = Date.now();
217
+ const request = _extends({
218
+ orgId,
219
+ personKey: key,
220
+ status: 'pending',
221
+ email: recordEmail,
222
+ requestedAtMs: now,
223
+ requestedByUid: actor.uid
224
+ }, hostId ? {
225
+ hostId
226
+ } : {}, contactId ? {
227
+ contactId
228
+ } : {}, leadId ? {
229
+ leadId
230
+ } : {}, {
231
+ pendingSinceMs: now
232
+ });
233
+ await requestRef.set(_extends({}, request, {
234
+ // A request re-filed after a failed or completed run starts clean.
235
+ erasedAtMs: FieldValue.delete(),
236
+ result: FieldValue.delete(),
237
+ failedAtMs: FieldValue.delete(),
238
+ lastError: FieldValue.delete(),
239
+ updatedAt: FieldValue.serverTimestamp()
240
+ }), {
241
+ merge: true
242
+ });
243
+ const hostIds = await orgHostIds(firestore, orgId);
244
+ for (const siteId of hostIds){
245
+ await suppressEmailForHostErasure({
246
+ hostId: siteId,
247
+ email: recordEmail
248
+ }).catch((error)=>{
249
+ console.error('[crm] erase-person suppression write failed', siteId, error);
250
+ });
251
+ const lead = firestore.collection('hosts').doc(siteId).collection('leads').doc(key);
252
+ await lead.get().then((snapshot)=>snapshot.exists ? lead.update({
253
+ [CONTACT_ERASURE_REQUESTED_FIELD]: now
254
+ }) : undefined).catch((error)=>{
255
+ console.error('[crm] erase-person lead marker failed', siteId, error);
256
+ });
257
+ }
258
+ try {
259
+ const contacts = await firestore.collection('orgs').doc(orgId).collection('contacts').where('email', '==', recordEmail).get();
260
+ for (const contact of contacts.docs){
261
+ await contact.ref.update({
262
+ [CONTACT_ERASURE_REQUESTED_FIELD]: now
263
+ });
264
+ }
265
+ } catch (error) {
266
+ console.error('[crm] erase-person contact marker failed', orgId, error);
267
+ }
268
+ // The feed the act was performed in: the site's under a site, the
269
+ // org's at the organization level.
270
+ const target = {
271
+ type: contactId ? 'contact' : 'lead',
272
+ id: contactId || leadId
273
+ };
274
+ if (routeScope.level === 'org') {
275
+ // Filed by address, the line names no record — and never the address.
276
+ await logOrgActivity(orgId, actor, 'Requested privacy erasure', byAddress ? {
277
+ type: 'org'
278
+ } : target).catch(()=>undefined);
279
+ } else {
280
+ await logHostActivity(hostId, actor, 'Requested privacy erasure', target).catch(()=>undefined);
281
+ }
282
+ await firestore.collection('adminAudit').add({
283
+ actorUid: actor.uid,
284
+ action: 'person.erasure-requested',
285
+ target: `orgs/${orgId}/people/${key}`,
286
+ before: null,
287
+ after: {
288
+ hostId: hostId || null,
289
+ hosts: hostIds.length,
290
+ from: byAddress ? 'address' : contactId ? 'contact' : 'lead'
291
+ },
292
+ at: FieldValue.serverTimestamp()
293
+ }).catch(()=>undefined);
294
+ const answer = {
295
+ ok: true,
296
+ requestId,
297
+ pendingSinceMs: now,
298
+ alreadyPending: false
299
+ };
300
+ res.status(200).json(answer);
301
+ } catch (error) {
302
+ console.error('[crm] erase-person failed', routeScope, error);
303
+ res.status(500).json({
304
+ error: 'The erasure could not be filed'
305
+ });
306
+ }
307
+ };
308
+
309
+ //# sourceMappingURL=erase-person.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/server/erase-person.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\nimport {\n canManageOrg,\n CONTACT_ERASURE_REQUESTED_FIELD,\n normalizeContactEmail,\n PERSON_ERASURES_COLLECTION,\n personErasureConfirmationMatches,\n personErasureId,\n type PersonErasureRequest,\n type PluginApiHandler,\n} from '@aglyn/aglyn/server'\nimport { personKey } from '@aglyn/aglyn/app-utils/person-key'\nimport {\n firebaseAdmin,\n getOrgForHost,\n logHostActivity,\n logOrgActivity,\n orgDataCollectionForHost,\n suppressEmailForHostErasure,\n} from '@aglyn/tenant-data-admin'\nimport { resolveOrgPermissions } from '@aglyn/tenant-runtime/org-permissions'\nimport { FieldValue } from 'firebase-admin/firestore'\nimport { authorizeOrgCaller, orgHostIds, readCrmRouteScope } from './org-caller'\n\n/** The route key, as `registerCrmConsoleApi` registers it. */\nexport const CRM_ERASE_PERSON_ROUTE = 'crm/erase-person'\n\nexport interface ErasePersonRequestBody {\n /**\n * The site the request is filed from. At the organization level\n * (AGL-2634) the record's own site, or absent for a contact no site has\n * captured; a LEAD always names one, because a lead lives under its site.\n */\n hostId?: string\n /** The organization, at the organization level (AGL-2634). */\n orgId?: string\n /**\n * At most one of these names the record the request is filed from. Under\n * a site exactly one does; at the organization level neither may, which\n * files the request by `email` alone (AGL-2839).\n */\n contactId?: string\n leadId?: string\n /**\n * The address as the admin typed it — the confirmation of a record's\n * address, or, filed by address alone, the person the request is about.\n */\n email: string\n /** Filed by address alone: the address typed a second time. */\n confirmEmail?: string\n}\n\nexport interface ErasePersonResponse {\n ok: true\n requestId: string\n /** When the request entered the queue. */\n pendingSinceMs: number\n /** True when a request for this person was already waiting; nothing was re-filed. */\n alreadyPending: boolean\n}\n\n/**\n * `POST /api/crm/erase-person` — file a privacy erasure for one person\n * (AGL-2623).\n *\n * ## Who may\n *\n * A workspace admin or owner — `canManageOrg` on the caller's org role —\n * and nobody else. Not a site editor with the data permission, who can\n * detach a contact from their own site but has no standing to remove the\n * person from every site in the workspace; and not staff acting alone,\n * because the workspace is the controller of this data and the instruction\n * has to come from it. Staff who are also admins of the workspace pass on\n * that role, as anyone would.\n *\n * ## What it does, and what it does not\n *\n * It files the request and closes the doors; it deletes nothing. The daily\n * erasure job executes the sweep, so that a workspace's person erasures and\n * its own erasure run through one job, one audit trail and one queue a\n * staff member can watch. What happens here, in order:\n *\n * 1. The record named by the body is read and its address is what the\n * request is about — the body's address must match it, typed, so an\n * admin who opened the wrong record is stopped by the confirmation\n * rather than by luck.\n * 2. The request document is written, or found already pending, in\n * which case nothing is re-filed and the caller is told so.\n * 3. Every site of the workspace gets its suppression row NOW, not when\n * the job runs: from this moment a capture cannot rebuild the person.\n * 4. The contact document and each site's lead are stamped with the\n * request time, so their pages can say \"erasure pending\" off the\n * document they already read.\n * 5. The site's activity feed and the platform audit log each get a row\n * that names the record by id and the person by hash, never by\n * address.\n *\n * ## The organization variant (AGL-2634)\n *\n * `{ orgId, hostId?, contactId | leadId, email }` from the org-level hub:\n * the same workspace admin, authorized by the org rather than through a\n * site they name, so a contact no site captured can be erased from the\n * page that lists it. The sweep is what it always was — every site of the\n * org, found by `orgId` — and the row goes to the org's feed. A lead still\n * names its site, because that is where a lead lives.\n *\n * ## By address alone (AGL-2839)\n *\n * `{ orgId, email, confirmEmail }` with no record, from the workspace's\n * Privacy settings: the same workspace admin, for any person the workspace\n * may hold — including one no page shows, since the CRM that lists records\n * is included from Starter and a Free workspace has none of it. Nothing is\n * read to file it: the sweep is keyed by the address, the markers are found\n * by it, and the typed address must match its second typing, which is the\n * confirmation a record's address gives the other variants. No plan is\n * asked on any variant: erasure is an obligation, not a CRM feature.\n */\nexport const crmErasePersonHandler: PluginApiHandler = async (req, res) => {\n if (req.method !== 'POST') {\n res.setHeader('Allow', 'POST')\n res.status(405).json({ error: 'Method not allowed' })\n return\n }\n const authorization = String(req.headers.authorization ?? '')\n const idToken = authorization.startsWith('Bearer ')\n ? authorization.slice('Bearer '.length)\n : undefined\n if (!idToken) {\n res.status(401).json({ error: 'Unauthenticated' })\n return\n }\n const body: Partial<ErasePersonRequestBody> =\n typeof req.body === 'string' ? JSON.parse(req.body) : (req.body ?? {})\n const routeScope = readCrmRouteScope(body as Record<string, unknown>)\n const contactId = String(body.contactId ?? '').trim()\n const leadId = String(body.leadId ?? '').trim()\n const byAddress = routeScope?.level === 'org' && !contactId && !leadId\n if (!routeScope || (!byAddress && !contactId && !leadId) || (contactId && leadId)) {\n res.status(400).json({ error: 'Name the site and exactly one contact or lead' })\n return\n }\n const { hostId } = routeScope\n if (leadId && !hostId) {\n res.status(400).json({ error: 'Name the site the lead lives under' })\n return\n }\n const refusal = 'Only a workspace admin can erase a person from the workspace'\n\n try {\n let orgId: string\n let actor: { uid: string; email: string | null }\n if (routeScope.level === 'org') {\n const caller = await authorizeOrgCaller(req, routeScope.orgId, {\n needs: 'manage-org',\n refusal,\n })\n if (caller.ok === false) {\n res.status(caller.status).json({ error: caller.error })\n return\n }\n orgId = caller.orgId\n actor = { uid: caller.uid, email: caller.email }\n } else {\n const decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken)\n const membership = await resolveOrgPermissions(decoded.uid, { hostId })\n if (!canManageOrg(membership.role)) {\n res.status(403).json({ error: refusal })\n return\n }\n const resolved = await getOrgForHost(hostId)\n if (!resolved || resolved.orgId !== membership.orgId) {\n res.status(404).json({ error: 'Unknown site' })\n return\n }\n orgId = resolved.orgId\n actor = { uid: decoded.uid, email: decoded.email ?? null }\n }\n const firestore = firebaseAdmin.app().firestore()\n\n let recordEmail: string | null = null\n if (byAddress) {\n recordEmail = normalizeContactEmail(body.email)\n } else if (contactId) {\n const contactsRef =\n routeScope.level === 'org'\n ? firestore.collection('orgs').doc(orgId).collection('contacts')\n : await orgDataCollectionForHost(hostId, 'contacts')\n const contact = await contactsRef.doc(contactId).get()\n if (!contact.exists) {\n res.status(404).json({ error: 'Unknown contact' })\n return\n }\n recordEmail = normalizeContactEmail(contact.get('email'))\n } else {\n const lead = await firestore\n .collection('hosts')\n .doc(hostId)\n .collection('leads')\n .doc(leadId)\n .get()\n if (!lead.exists) {\n res.status(404).json({ error: 'Unknown lead' })\n return\n }\n recordEmail = normalizeContactEmail(lead.get('email'))\n }\n const key = recordEmail ? personKey(recordEmail) : null\n if (!recordEmail || !key) {\n res.status(byAddress ? 400 : 422).json({\n error: byAddress\n ? 'Enter the email address of the person to erase'\n : 'This record has no usable email address to erase by',\n })\n return\n }\n if (byAddress && !personErasureConfirmationMatches(body.confirmEmail, recordEmail)) {\n res.status(400).json({\n error: 'Type the email address a second time, exactly, to confirm the erasure',\n })\n return\n }\n if (!byAddress && !personErasureConfirmationMatches(body.email, recordEmail)) {\n res.status(400).json({\n error: 'Type the record’s email address exactly to confirm the erasure',\n })\n return\n }\n\n const requestId = personErasureId(orgId, key)\n const requestRef = firestore.collection(PERSON_ERASURES_COLLECTION).doc(requestId)\n const existing = await requestRef.get()\n if (existing.exists && existing.get('status') === 'pending') {\n const answer: ErasePersonResponse = {\n ok: true,\n requestId,\n pendingSinceMs: Number(existing.get('pendingSinceMs') ?? 0),\n alreadyPending: true,\n }\n res.status(200).json(answer)\n return\n }\n\n const now = Date.now()\n const request: PersonErasureRequest = {\n orgId,\n personKey: key,\n status: 'pending',\n email: recordEmail,\n requestedAtMs: now,\n requestedByUid: actor.uid,\n ...(hostId ? { hostId } : {}),\n ...(contactId ? { contactId } : {}),\n ...(leadId ? { leadId } : {}),\n pendingSinceMs: now,\n }\n await requestRef.set(\n {\n ...request,\n // A request re-filed after a failed or completed run starts clean.\n erasedAtMs: FieldValue.delete(),\n result: FieldValue.delete(),\n failedAtMs: FieldValue.delete(),\n lastError: FieldValue.delete(),\n updatedAt: FieldValue.serverTimestamp(),\n },\n { merge: true },\n )\n\n const hostIds = await orgHostIds(firestore, orgId)\n for (const siteId of hostIds) {\n await suppressEmailForHostErasure({ hostId: siteId, email: recordEmail }).catch(\n (error: unknown) => {\n console.error('[crm] erase-person suppression write failed', siteId, error)\n },\n )\n const lead = firestore.collection('hosts').doc(siteId).collection('leads').doc(key)\n await lead\n .get()\n .then((snapshot: { exists: boolean }) =>\n snapshot.exists ? lead.update({ [CONTACT_ERASURE_REQUESTED_FIELD]: now }) : undefined,\n )\n .catch((error: unknown) => {\n console.error('[crm] erase-person lead marker failed', siteId, error)\n })\n }\n try {\n const contacts = await firestore\n .collection('orgs')\n .doc(orgId)\n .collection('contacts')\n .where('email', '==', recordEmail)\n .get()\n for (const contact of contacts.docs) {\n await contact.ref.update({ [CONTACT_ERASURE_REQUESTED_FIELD]: now })\n }\n } catch (error) {\n console.error('[crm] erase-person contact marker failed', orgId, error)\n }\n\n // The feed the act was performed in: the site's under a site, the\n // org's at the organization level.\n const target = { type: contactId ? 'contact' : 'lead', id: contactId || leadId } as const\n if (routeScope.level === 'org') {\n // Filed by address, the line names no record — and never the address.\n await logOrgActivity(\n orgId,\n actor,\n 'Requested privacy erasure',\n byAddress ? { type: 'org' } : target,\n ).catch(() => undefined)\n } else {\n await logHostActivity(hostId, actor, 'Requested privacy erasure', target).catch(\n () => undefined,\n )\n }\n await firestore\n .collection('adminAudit')\n .add({\n actorUid: actor.uid,\n action: 'person.erasure-requested',\n target: `orgs/${orgId}/people/${key}`,\n before: null,\n after: {\n hostId: hostId || null,\n hosts: hostIds.length,\n from: byAddress ? 'address' : contactId ? 'contact' : 'lead',\n },\n at: FieldValue.serverTimestamp(),\n })\n .catch(() => undefined)\n\n const answer: ErasePersonResponse = {\n ok: true,\n requestId,\n pendingSinceMs: now,\n alreadyPending: false,\n }\n res.status(200).json(answer)\n } catch (error) {\n console.error('[crm] erase-person failed', routeScope, error)\n res.status(500).json({ error: 'The erasure could not be filed' })\n }\n}\n"],"names":["canManageOrg","CONTACT_ERASURE_REQUESTED_FIELD","normalizeContactEmail","PERSON_ERASURES_COLLECTION","personErasureConfirmationMatches","personErasureId","personKey","firebaseAdmin","getOrgForHost","logHostActivity","logOrgActivity","orgDataCollectionForHost","suppressEmailForHostErasure","resolveOrgPermissions","FieldValue","authorizeOrgCaller","orgHostIds","readCrmRouteScope","CRM_ERASE_PERSON_ROUTE","crmErasePersonHandler","req","res","body","method","setHeader","status","json","error","authorization","String","headers","idToken","startsWith","slice","length","undefined","JSON","parse","routeScope","contactId","trim","leadId","byAddress","level","hostId","refusal","orgId","actor","caller","needs","ok","uid","email","decoded","app","auth","verifyIdToken","membership","role","resolved","firestore","recordEmail","contactsRef","collection","doc","contact","get","exists","lead","key","confirmEmail","requestId","requestRef","existing","answer","pendingSinceMs","Number","alreadyPending","now","Date","request","requestedAtMs","requestedByUid","set","erasedAtMs","delete","result","failedAtMs","lastError","updatedAt","serverTimestamp","merge","hostIds","siteId","catch","console","then","snapshot","update","contacts","where","docs","ref","target","type","id","add","actorUid","action","before","after","hosts","from","at"],"mappings":"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"}
@@ -0,0 +1,85 @@
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
+ * WHAT EVERY CSV IMPORT ROUTE ASKS BEFORE IT READS A ROW (AGL-2602, shared
19
+ * by the companies import since AGL-2621).
20
+ *
21
+ * Who is asking and on whose behalf, whether the request is the size a
22
+ * chunk is allowed to be, and — when a row names an owner — which member
23
+ * of the org that address is. The contacts route settled each answer; the
24
+ * companies route needs the same three and must not spell them
25
+ * differently, because two gates for one permission is how one of them
26
+ * comes to be wider.
27
+ *
28
+ * ## Who may call an import
29
+ *
30
+ * The same two gates the rules put on every CRM collection: a site role
31
+ * on the host the request names, AND the organization's `data.manage`
32
+ * permission resolved through the member's custom role and overrides. The
33
+ * Admin SDK evaluates no rules, so this is the enforcement rather than an
34
+ * echo of it — and `data.manage` rather than the raw role because an owner
35
+ * who unticked "Manage data" on a custom role meant it. A staff token
36
+ * passes both gates the way it does on every console route: support
37
+ * importing a customer's file for them is the support act this exists for.
38
+ */
39
+ import { type PluginApiHandler } from '@aglyn/aglyn/server';
40
+ /** Everything an import needs about who is asking, or the refusal to send back. */
41
+ export type ImportContext = {
42
+ ok: true;
43
+ uid: string;
44
+ hostId: string;
45
+ orgId: string;
46
+ org: Record<string, unknown>;
47
+ /**
48
+ * The stamp a record CREATED by this import carries — the org, when
49
+ * the org widened its default, and otherwise the capturing group.
50
+ */
51
+ scopeTokens: string[];
52
+ /**
53
+ * The tokens a record must overlap to be SEEN from this site: the
54
+ * group's own, plus `org`, because an org-wide record is visible to
55
+ * every site in the account. The reader's set is wider than the
56
+ * creator's stamp, exactly as it is on the collection listeners.
57
+ */
58
+ readTokens: string[];
59
+ } | {
60
+ ok: false;
61
+ status: number;
62
+ body: Record<string, unknown>;
63
+ };
64
+ export type ImportRequest = Parameters<PluginApiHandler>[0];
65
+ /** Who is asking, and on whose behalf. */
66
+ export declare function resolveImportContext(req: ImportRequest): Promise<ImportContext>;
67
+ /** The rows a request may carry, or the refusal to send back. */
68
+ export declare function readImportRows<R>(req: ImportRequest, limits: {
69
+ maxBodyBytes: number;
70
+ chunkSize: number;
71
+ }): {
72
+ rows: R[];
73
+ } | {
74
+ status: number;
75
+ error: string;
76
+ };
77
+ /**
78
+ * The org's members by address, read only when a row names an owner.
79
+ *
80
+ * A file with no owner column costs no roster read; a file with one costs
81
+ * exactly one, however many rows it has.
82
+ */
83
+ export declare function ownerDirectory(orgId: string, rows: readonly {
84
+ ownerEmail?: string;
85
+ }[]): Promise<Map<string, string>>;
@@ -0,0 +1,181 @@
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
+ * WHAT EVERY CSV IMPORT ROUTE ASKS BEFORE IT READS A ROW (AGL-2602, shared
18
+ * by the companies import since AGL-2621).
19
+ *
20
+ * Who is asking and on whose behalf, whether the request is the size a
21
+ * chunk is allowed to be, and — when a row names an owner — which member
22
+ * of the org that address is. The contacts route settled each answer; the
23
+ * companies route needs the same three and must not spell them
24
+ * differently, because two gates for one permission is how one of them
25
+ * comes to be wider.
26
+ *
27
+ * ## Who may call an import
28
+ *
29
+ * The same two gates the rules put on every CRM collection: a site role
30
+ * on the host the request names, AND the organization's `data.manage`
31
+ * permission resolved through the member's custom role and overrides. The
32
+ * Admin SDK evaluates no rules, so this is the enforcement rather than an
33
+ * echo of it — and `data.manage` rather than the raw role because an owner
34
+ * who unticked "Manage data" on a custom role meant it. A staff token
35
+ * passes both gates the way it does on every console route: support
36
+ * importing a customer's file for them is the support act this exists for.
37
+ */ import { consentGroupScope, crmScopeTokens, ORG_SCOPE_TOKEN } from "@aglyn/aglyn/server";
38
+ import { consentGroupForSite, firebaseAdmin, getOrgForHost, listOrgMembers, memberHasOrgPermission, resolveOrgMembership } from "@aglyn/tenant-data-admin";
39
+ import { crmSuiteRefusal } from "./suite-gate.js";
40
+ /** Who is asking, and on whose behalf. */ export async function resolveImportContext(req) {
41
+ var _ref, _req_headers_authorization, _hostSnapshot_get, _ref1, _ref2;
42
+ var _req_body;
43
+ const hostId = String((_ref = (_req_body = req.body) == null ? void 0 : _req_body.hostId) != null ? _ref : '');
44
+ if (!hostId) {
45
+ return {
46
+ ok: false,
47
+ status: 400,
48
+ body: {
49
+ error: 'Missing hostId'
50
+ }
51
+ };
52
+ }
53
+ const authorization = String((_req_headers_authorization = req.headers.authorization) != null ? _req_headers_authorization : '');
54
+ const idToken = authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) : undefined;
55
+ if (!idToken) {
56
+ return {
57
+ ok: false,
58
+ status: 401,
59
+ body: {
60
+ error: 'Unauthenticated'
61
+ }
62
+ };
63
+ }
64
+ const decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken);
65
+ const staff = decoded['staff'] === true;
66
+ const firestore = firebaseAdmin.app().firestore();
67
+ const hostSnapshot = await firestore.collection('hosts').doc(hostId).get();
68
+ if (!hostSnapshot.exists) {
69
+ return {
70
+ ok: false,
71
+ status: 404,
72
+ body: {
73
+ error: 'Unknown site'
74
+ }
75
+ };
76
+ }
77
+ const memberRole = ((_hostSnapshot_get = hostSnapshot.get('memberRoles')) != null ? _hostSnapshot_get : {})[decoded.uid];
78
+ if (!staff && memberRole !== 'admin' && memberRole !== 'editor') {
79
+ return {
80
+ ok: false,
81
+ status: 403,
82
+ body: {
83
+ error: 'Not a site admin or editor'
84
+ }
85
+ };
86
+ }
87
+ const resolved = await getOrgForHost(hostId).catch(()=>null);
88
+ const orgId = String((_ref1 = resolved == null ? void 0 : resolved.orgId) != null ? _ref1 : '');
89
+ if (!orgId) {
90
+ return {
91
+ ok: false,
92
+ status: 404,
93
+ body: {
94
+ error: 'This site has no organization, so it has no CRM.'
95
+ }
96
+ };
97
+ }
98
+ const org = (_ref2 = resolved == null ? void 0 : resolved.org) != null ? _ref2 : {};
99
+ const membership = await resolveOrgMembership(decoded.uid, orgId).catch(()=>null);
100
+ const member = membership == null ? void 0 : membership.member;
101
+ if (!staff && (!member || member.orgSuspended === true || !await memberHasOrgPermission(orgId, member, 'data.manage'))) {
102
+ return {
103
+ ok: false,
104
+ status: 403,
105
+ body: {
106
+ error: 'Your organization role does not allow managing contacts and ' + 'companies, so it cannot import them.'
107
+ }
108
+ };
109
+ }
110
+ // Every file this context serves — contacts, companies, deals, tasks and
111
+ // leads — is the CRM's to import, whoever is asking (AGL-2787).
112
+ const suite = crmSuiteRefusal(org, 'Importing from a CSV file');
113
+ if (suite) return {
114
+ ok: false,
115
+ status: suite.status,
116
+ body: suite.body
117
+ };
118
+ const group = await consentGroupForSite(hostId);
119
+ return {
120
+ ok: true,
121
+ uid: decoded.uid,
122
+ hostId,
123
+ orgId,
124
+ org,
125
+ scopeTokens: crmScopeTokens(org, group),
126
+ readTokens: [
127
+ ORG_SCOPE_TOKEN,
128
+ ...consentGroupScope(group)
129
+ ]
130
+ };
131
+ }
132
+ /** The rows a request may carry, or the refusal to send back. */ export function readImportRows(req, limits) {
133
+ var _ref;
134
+ var _req_rawBody, _req_body;
135
+ if (((_ref = (_req_rawBody = req.rawBody) == null ? void 0 : _req_rawBody.length) != null ? _ref : 0) > limits.maxBodyBytes) {
136
+ return {
137
+ status: 413,
138
+ error: 'That request is too large. Import the file in smaller pieces.'
139
+ };
140
+ }
141
+ const rows = (_req_body = req.body) == null ? void 0 : _req_body.rows;
142
+ if (!Array.isArray(rows) || rows.length === 0) {
143
+ return {
144
+ status: 400,
145
+ error: 'No rows to import.'
146
+ };
147
+ }
148
+ if (rows.length > limits.chunkSize) {
149
+ return {
150
+ status: 400,
151
+ error: `At most ${limits.chunkSize} rows per request.`
152
+ };
153
+ }
154
+ if (!rows.every((row)=>row && typeof row === 'object' && !Array.isArray(row))) {
155
+ return {
156
+ status: 400,
157
+ error: 'Every row must be an object.'
158
+ };
159
+ }
160
+ return {
161
+ rows: rows
162
+ };
163
+ }
164
+ /**
165
+ * The org's members by address, read only when a row names an owner.
166
+ *
167
+ * A file with no owner column costs no roster read; a file with one costs
168
+ * exactly one, however many rows it has.
169
+ */ export async function ownerDirectory(orgId, rows) {
170
+ const directory = new Map();
171
+ if (!rows.some((row)=>row.ownerEmail)) return directory;
172
+ const members = await listOrgMembers(orgId);
173
+ for (const member of members){
174
+ var _member_email;
175
+ const email = String((_member_email = member.email) != null ? _member_email : '').trim().toLowerCase();
176
+ if (email && member.$id) directory.set(email, String(member.$id));
177
+ }
178
+ return directory;
179
+ }
180
+
181
+ //# sourceMappingURL=import-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/crm/src/lib/server/import-context.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 * WHAT EVERY CSV IMPORT ROUTE ASKS BEFORE IT READS A ROW (AGL-2602, shared\n * by the companies import since AGL-2621).\n *\n * Who is asking and on whose behalf, whether the request is the size a\n * chunk is allowed to be, and — when a row names an owner — which member\n * of the org that address is. The contacts route settled each answer; the\n * companies route needs the same three and must not spell them\n * differently, because two gates for one permission is how one of them\n * comes to be wider.\n *\n * ## Who may call an import\n *\n * The same two gates the rules put on every CRM collection: a site role\n * on the host the request names, AND the organization's `data.manage`\n * permission resolved through the member's custom role and overrides. The\n * Admin SDK evaluates no rules, so this is the enforcement rather than an\n * echo of it — and `data.manage` rather than the raw role because an owner\n * who unticked \"Manage data\" on a custom role meant it. A staff token\n * passes both gates the way it does on every console route: support\n * importing a customer's file for them is the support act this exists for.\n */\n\nimport {\n consentGroupScope,\n crmScopeTokens,\n ORG_SCOPE_TOKEN,\n type PluginApiHandler,\n} from '@aglyn/aglyn/server'\nimport {\n consentGroupForSite,\n firebaseAdmin,\n getOrgForHost,\n listOrgMembers,\n memberHasOrgPermission,\n resolveOrgMembership,\n} from '@aglyn/tenant-data-admin'\nimport { crmSuiteRefusal } from './suite-gate'\n\n/** Everything an import needs about who is asking, or the refusal to send back. */\nexport type ImportContext =\n | {\n ok: true\n uid: string\n hostId: string\n orgId: string\n org: Record<string, unknown>\n /**\n * The stamp a record CREATED by this import carries — the org, when\n * the org widened its default, and otherwise the capturing group.\n */\n scopeTokens: string[]\n /**\n * The tokens a record must overlap to be SEEN from this site: the\n * group's own, plus `org`, because an org-wide record is visible to\n * every site in the account. The reader's set is wider than the\n * creator's stamp, exactly as it is on the collection listeners.\n */\n readTokens: string[]\n }\n | { ok: false; status: number; body: Record<string, unknown> }\n\nexport type ImportRequest = Parameters<PluginApiHandler>[0]\n\n/** Who is asking, and on whose behalf. */\nexport async function resolveImportContext(req: ImportRequest): Promise<ImportContext> {\n const hostId = String(req.body?.hostId ?? '')\n if (!hostId) {\n return { ok: false, status: 400, body: { error: 'Missing hostId' } }\n }\n const authorization = String(req.headers.authorization ?? '')\n const idToken = authorization.startsWith('Bearer ')\n ? authorization.slice('Bearer '.length)\n : undefined\n if (!idToken) {\n return { ok: false, status: 401, body: { error: 'Unauthenticated' } }\n }\n const decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken)\n const staff = decoded['staff'] === true\n const firestore = firebaseAdmin.app().firestore()\n const hostSnapshot = await firestore.collection('hosts').doc(hostId).get()\n if (!hostSnapshot.exists) {\n return { ok: false, status: 404, body: { error: 'Unknown site' } }\n }\n const memberRole = (hostSnapshot.get('memberRoles') ?? {})[decoded.uid]\n if (!staff && memberRole !== 'admin' && memberRole !== 'editor') {\n return {\n ok: false,\n status: 403,\n body: { error: 'Not a site admin or editor' },\n }\n }\n const resolved = await getOrgForHost(hostId).catch(() => null)\n const orgId = String(resolved?.orgId ?? '')\n if (!orgId) {\n return {\n ok: false,\n status: 404,\n body: { error: 'This site has no organization, so it has no CRM.' },\n }\n }\n const org = (resolved?.org ?? {}) as Record<string, unknown>\n const membership = await resolveOrgMembership(decoded.uid, orgId).catch(\n () => null,\n )\n const member = membership?.member\n if (\n !staff &&\n (!member ||\n (member as { orgSuspended?: boolean }).orgSuspended === true ||\n !(await memberHasOrgPermission(orgId, member, 'data.manage')))\n ) {\n return {\n ok: false,\n status: 403,\n body: {\n error:\n 'Your organization role does not allow managing contacts and ' +\n 'companies, so it cannot import them.',\n },\n }\n }\n // Every file this context serves — contacts, companies, deals, tasks and\n // leads — is the CRM's to import, whoever is asking (AGL-2787).\n const suite = crmSuiteRefusal(org, 'Importing from a CSV file')\n if (suite) return { ok: false, status: suite.status, body: suite.body }\n const group = await consentGroupForSite(hostId)\n return {\n ok: true,\n uid: decoded.uid,\n hostId,\n orgId,\n org,\n scopeTokens: crmScopeTokens(org, group),\n readTokens: [ORG_SCOPE_TOKEN, ...consentGroupScope(group)],\n }\n}\n\n/** The rows a request may carry, or the refusal to send back. */\nexport function readImportRows<R>(\n req: ImportRequest,\n limits: { maxBodyBytes: number; chunkSize: number },\n): { rows: R[] } | { status: number; error: string } {\n if ((req.rawBody?.length ?? 0) > limits.maxBodyBytes) {\n return {\n status: 413,\n error: 'That request is too large. Import the file in smaller pieces.',\n }\n }\n const rows = req.body?.rows\n if (!Array.isArray(rows) || rows.length === 0) {\n return { status: 400, error: 'No rows to import.' }\n }\n if (rows.length > limits.chunkSize) {\n return {\n status: 400,\n error: `At most ${limits.chunkSize} rows per request.`,\n }\n }\n if (!rows.every((row) => row && typeof row === 'object' && !Array.isArray(row))) {\n return { status: 400, error: 'Every row must be an object.' }\n }\n return { rows: rows as R[] }\n}\n\n/**\n * The org's members by address, read only when a row names an owner.\n *\n * A file with no owner column costs no roster read; a file with one costs\n * exactly one, however many rows it has.\n */\nexport async function ownerDirectory(\n orgId: string,\n rows: readonly { ownerEmail?: string }[],\n): Promise<Map<string, string>> {\n const directory = new Map<string, string>()\n if (!rows.some((row) => row.ownerEmail)) return directory\n const members = await listOrgMembers(orgId)\n for (const member of members) {\n const email = String(member.email ?? '')\n .trim()\n .toLowerCase()\n if (email && member.$id) directory.set(email, String(member.$id))\n }\n return directory\n}\n"],"names":["consentGroupScope","crmScopeTokens","ORG_SCOPE_TOKEN","consentGroupForSite","firebaseAdmin","getOrgForHost","listOrgMembers","memberHasOrgPermission","resolveOrgMembership","crmSuiteRefusal","resolveImportContext","req","hostSnapshot","hostId","String","body","ok","status","error","authorization","headers","idToken","startsWith","slice","length","undefined","decoded","app","auth","verifyIdToken","staff","firestore","collection","doc","get","exists","memberRole","uid","resolved","catch","orgId","org","membership","member","orgSuspended","suite","group","scopeTokens","readTokens","readImportRows","limits","rawBody","maxBodyBytes","rows","Array","isArray","chunkSize","every","row","ownerDirectory","directory","Map","some","ownerEmail","members","email","trim","toLowerCase","$id","set"],"mappings":"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"}