@kelpie/server 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (584) hide show
  1. package/dist/lib/ids.d.ts +11 -0
  2. package/dist/lib/ids.d.ts.map +1 -1
  3. package/dist/lib/ids.js +11 -0
  4. package/dist/lib/ids.js.map +1 -1
  5. package/dist/modules/activities/repository.d.ts.map +1 -1
  6. package/dist/modules/activities/repository.js +19 -20
  7. package/dist/modules/activities/repository.js.map +1 -1
  8. package/dist/modules/activities/wording.d.ts +12 -0
  9. package/dist/modules/activities/wording.d.ts.map +1 -1
  10. package/dist/modules/activities/wording.js +16 -0
  11. package/dist/modules/activities/wording.js.map +1 -1
  12. package/dist/modules/agent-tasks/catalog.d.ts.map +1 -1
  13. package/dist/modules/agent-tasks/catalog.js +62 -0
  14. package/dist/modules/agent-tasks/catalog.js.map +1 -1
  15. package/dist/modules/agent-tasks/resolve.d.ts.map +1 -1
  16. package/dist/modules/agent-tasks/resolve.js +42 -9
  17. package/dist/modules/agent-tasks/resolve.js.map +1 -1
  18. package/dist/modules/agent-tasks/routes.d.ts +2 -0
  19. package/dist/modules/agent-tasks/routes.d.ts.map +1 -1
  20. package/dist/modules/attachedRecords.d.ts +6 -1
  21. package/dist/modules/attachedRecords.d.ts.map +1 -1
  22. package/dist/modules/attachedRecords.js +15 -1
  23. package/dist/modules/attachedRecords.js.map +1 -1
  24. package/dist/modules/companies/index.d.ts.map +1 -1
  25. package/dist/modules/companies/index.js +3 -1
  26. package/dist/modules/companies/index.js.map +1 -1
  27. package/dist/modules/companies/routes.d.ts +14 -0
  28. package/dist/modules/companies/routes.d.ts.map +1 -1
  29. package/dist/modules/companies/routes.js +7 -0
  30. package/dist/modules/companies/routes.js.map +1 -1
  31. package/dist/modules/companies/schema.d.ts +20 -0
  32. package/dist/modules/companies/schema.d.ts.map +1 -1
  33. package/dist/modules/companies/schema.js +6 -1
  34. package/dist/modules/companies/schema.js.map +1 -1
  35. package/dist/modules/companies/service.d.ts +5 -0
  36. package/dist/modules/companies/service.d.ts.map +1 -1
  37. package/dist/modules/companies/service.js +24 -12
  38. package/dist/modules/companies/service.js.map +1 -1
  39. package/dist/modules/consent-purposes/capabilities.d.ts +7 -0
  40. package/dist/modules/consent-purposes/capabilities.d.ts.map +1 -0
  41. package/dist/modules/consent-purposes/capabilities.js +10 -0
  42. package/dist/modules/consent-purposes/capabilities.js.map +1 -0
  43. package/dist/modules/consent-purposes/events.d.ts +38 -0
  44. package/dist/modules/consent-purposes/events.d.ts.map +1 -0
  45. package/dist/modules/consent-purposes/events.js +17 -0
  46. package/dist/modules/consent-purposes/events.js.map +1 -0
  47. package/dist/modules/consent-purposes/index.d.ts +13 -0
  48. package/dist/modules/consent-purposes/index.d.ts.map +1 -0
  49. package/dist/modules/consent-purposes/index.js +41 -0
  50. package/dist/modules/consent-purposes/index.js.map +1 -0
  51. package/dist/modules/consent-purposes/repository.d.ts +21 -0
  52. package/dist/modules/consent-purposes/repository.d.ts.map +1 -0
  53. package/dist/modules/consent-purposes/repository.js +98 -0
  54. package/dist/modules/consent-purposes/repository.js.map +1 -0
  55. package/dist/modules/consent-purposes/routes.d.ts +39 -0
  56. package/dist/modules/consent-purposes/routes.d.ts.map +1 -0
  57. package/dist/modules/consent-purposes/routes.js +79 -0
  58. package/dist/modules/consent-purposes/routes.js.map +1 -0
  59. package/dist/modules/consent-purposes/schema.d.ts +175 -0
  60. package/dist/modules/consent-purposes/schema.d.ts.map +1 -0
  61. package/dist/modules/consent-purposes/schema.js +35 -0
  62. package/dist/modules/consent-purposes/schema.js.map +1 -0
  63. package/dist/modules/consent-purposes/service.d.ts +50 -0
  64. package/dist/modules/consent-purposes/service.d.ts.map +1 -0
  65. package/dist/modules/consent-purposes/service.js +263 -0
  66. package/dist/modules/consent-purposes/service.js.map +1 -0
  67. package/dist/modules/consent-purposes/starters.d.ts +17 -0
  68. package/dist/modules/consent-purposes/starters.d.ts.map +1 -0
  69. package/dist/modules/consent-purposes/starters.js +15 -0
  70. package/dist/modules/consent-purposes/starters.js.map +1 -0
  71. package/dist/modules/consent-purposes/tools.d.ts +4 -0
  72. package/dist/modules/consent-purposes/tools.d.ts.map +1 -0
  73. package/dist/modules/consent-purposes/tools.js +29 -0
  74. package/dist/modules/consent-purposes/tools.js.map +1 -0
  75. package/dist/modules/core.d.ts.map +1 -1
  76. package/dist/modules/core.js +10 -0
  77. package/dist/modules/core.js.map +1 -1
  78. package/dist/modules/custom-fields/capabilities.d.ts +12 -0
  79. package/dist/modules/custom-fields/capabilities.d.ts.map +1 -0
  80. package/dist/modules/custom-fields/capabilities.js +15 -0
  81. package/dist/modules/custom-fields/capabilities.js.map +1 -0
  82. package/dist/modules/custom-fields/events.d.ts +49 -0
  83. package/dist/modules/custom-fields/events.d.ts.map +1 -0
  84. package/dist/modules/custom-fields/events.js +25 -0
  85. package/dist/modules/custom-fields/events.js.map +1 -0
  86. package/dist/modules/custom-fields/index.d.ts +19 -0
  87. package/dist/modules/custom-fields/index.d.ts.map +1 -0
  88. package/dist/modules/custom-fields/index.js +46 -0
  89. package/dist/modules/custom-fields/index.js.map +1 -0
  90. package/dist/modules/custom-fields/repository.d.ts +37 -0
  91. package/dist/modules/custom-fields/repository.d.ts.map +1 -0
  92. package/dist/modules/custom-fields/repository.js +144 -0
  93. package/dist/modules/custom-fields/repository.js.map +1 -0
  94. package/dist/modules/custom-fields/routes.d.ts +44 -0
  95. package/dist/modules/custom-fields/routes.d.ts.map +1 -0
  96. package/dist/modules/custom-fields/routes.js +103 -0
  97. package/dist/modules/custom-fields/routes.js.map +1 -0
  98. package/dist/modules/custom-fields/schema.d.ts +215 -0
  99. package/dist/modules/custom-fields/schema.d.ts.map +1 -0
  100. package/dist/modules/custom-fields/schema.js +43 -0
  101. package/dist/modules/custom-fields/schema.js.map +1 -0
  102. package/dist/modules/custom-fields/service.d.ts +57 -0
  103. package/dist/modules/custom-fields/service.d.ts.map +1 -0
  104. package/dist/modules/custom-fields/service.js +273 -0
  105. package/dist/modules/custom-fields/service.js.map +1 -0
  106. package/dist/modules/custom-fields/tools.d.ts +4 -0
  107. package/dist/modules/custom-fields/tools.d.ts.map +1 -0
  108. package/dist/modules/custom-fields/tools.js +38 -0
  109. package/dist/modules/custom-fields/tools.js.map +1 -0
  110. package/dist/modules/custom-fields/values.d.ts +49 -0
  111. package/dist/modules/custom-fields/values.d.ts.map +1 -0
  112. package/dist/modules/custom-fields/values.js +249 -0
  113. package/dist/modules/custom-fields/values.js.map +1 -0
  114. package/dist/modules/custom-fields/wire.d.ts +12 -0
  115. package/dist/modules/custom-fields/wire.d.ts.map +1 -0
  116. package/dist/modules/custom-fields/wire.js +30 -0
  117. package/dist/modules/custom-fields/wire.js.map +1 -0
  118. package/dist/modules/dashboard/repository.d.ts.map +1 -1
  119. package/dist/modules/dashboard/repository.js +4 -3
  120. package/dist/modules/dashboard/repository.js.map +1 -1
  121. package/dist/modules/deals/index.d.ts +2 -1
  122. package/dist/modules/deals/index.d.ts.map +1 -1
  123. package/dist/modules/deals/index.js +5 -2
  124. package/dist/modules/deals/index.js.map +1 -1
  125. package/dist/modules/deals/repository.d.ts +0 -16
  126. package/dist/modules/deals/repository.d.ts.map +1 -1
  127. package/dist/modules/deals/repository.js +6 -76
  128. package/dist/modules/deals/repository.js.map +1 -1
  129. package/dist/modules/deals/routes.d.ts +14 -0
  130. package/dist/modules/deals/routes.d.ts.map +1 -1
  131. package/dist/modules/deals/routes.js +7 -0
  132. package/dist/modules/deals/routes.js.map +1 -1
  133. package/dist/modules/deals/schema.d.ts +20 -42
  134. package/dist/modules/deals/schema.d.ts.map +1 -1
  135. package/dist/modules/deals/schema.js +6 -11
  136. package/dist/modules/deals/schema.js.map +1 -1
  137. package/dist/modules/deals/service.d.ts +13 -2
  138. package/dist/modules/deals/service.d.ts.map +1 -1
  139. package/dist/modules/deals/service.js +43 -20
  140. package/dist/modules/deals/service.js.map +1 -1
  141. package/dist/modules/decisions/repository.d.ts.map +1 -1
  142. package/dist/modules/decisions/repository.js +2 -0
  143. package/dist/modules/decisions/repository.js.map +1 -1
  144. package/dist/modules/decisions/routes.d.ts +1 -0
  145. package/dist/modules/decisions/routes.d.ts.map +1 -1
  146. package/dist/modules/enquiries/events.d.ts +44 -0
  147. package/dist/modules/enquiries/events.d.ts.map +1 -0
  148. package/dist/modules/enquiries/events.js +19 -0
  149. package/dist/modules/enquiries/events.js.map +1 -0
  150. package/dist/modules/enquiries/index.d.ts +13 -0
  151. package/dist/modules/enquiries/index.d.ts.map +1 -0
  152. package/dist/modules/enquiries/index.js +44 -0
  153. package/dist/modules/enquiries/index.js.map +1 -0
  154. package/dist/modules/enquiries/repository.d.ts +30 -0
  155. package/dist/modules/enquiries/repository.d.ts.map +1 -0
  156. package/dist/modules/enquiries/repository.js +94 -0
  157. package/dist/modules/enquiries/repository.js.map +1 -0
  158. package/dist/modules/enquiries/routes.d.ts +52 -0
  159. package/dist/modules/enquiries/routes.d.ts.map +1 -0
  160. package/dist/modules/enquiries/routes.js +127 -0
  161. package/dist/modules/enquiries/routes.js.map +1 -0
  162. package/dist/modules/enquiries/schema.d.ts +290 -0
  163. package/dist/modules/enquiries/schema.d.ts.map +1 -0
  164. package/dist/modules/enquiries/schema.js +52 -0
  165. package/dist/modules/enquiries/schema.js.map +1 -0
  166. package/dist/modules/enquiries/service.d.ts +85 -0
  167. package/dist/modules/enquiries/service.d.ts.map +1 -0
  168. package/dist/modules/enquiries/service.js +370 -0
  169. package/dist/modules/enquiries/service.js.map +1 -0
  170. package/dist/modules/enquiries/tools.d.ts +4 -0
  171. package/dist/modules/enquiries/tools.d.ts.map +1 -0
  172. package/dist/modules/enquiries/tools.js +50 -0
  173. package/dist/modules/enquiries/tools.js.map +1 -0
  174. package/dist/modules/eventCatalogs.d.ts +3 -0
  175. package/dist/modules/eventCatalogs.d.ts.map +1 -1
  176. package/dist/modules/eventCatalogs.js +3 -0
  177. package/dist/modules/eventCatalogs.js.map +1 -1
  178. package/dist/modules/forms/embed.d.ts +20 -6
  179. package/dist/modules/forms/embed.d.ts.map +1 -1
  180. package/dist/modules/forms/embed.js +359 -43
  181. package/dist/modules/forms/embed.js.map +1 -1
  182. package/dist/modules/forms/events.d.ts +20 -7
  183. package/dist/modules/forms/events.d.ts.map +1 -1
  184. package/dist/modules/forms/events.js +20 -2
  185. package/dist/modules/forms/events.js.map +1 -1
  186. package/dist/modules/forms/fields.d.ts +14 -1
  187. package/dist/modules/forms/fields.d.ts.map +1 -1
  188. package/dist/modules/forms/fields.js +87 -3
  189. package/dist/modules/forms/fields.js.map +1 -1
  190. package/dist/modules/forms/index.d.ts.map +1 -1
  191. package/dist/modules/forms/index.js +12 -1
  192. package/dist/modules/forms/index.js.map +1 -1
  193. package/dist/modules/forms/mapping.d.ts +62 -5
  194. package/dist/modules/forms/mapping.d.ts.map +1 -1
  195. package/dist/modules/forms/mapping.js +139 -5
  196. package/dist/modules/forms/mapping.js.map +1 -1
  197. package/dist/modules/forms/publicRoutes.d.ts.map +1 -1
  198. package/dist/modules/forms/publicRoutes.js +29 -1
  199. package/dist/modules/forms/publicRoutes.js.map +1 -1
  200. package/dist/modules/forms/repository.d.ts +31 -0
  201. package/dist/modules/forms/repository.d.ts.map +1 -1
  202. package/dist/modules/forms/repository.js +90 -1
  203. package/dist/modules/forms/repository.js.map +1 -1
  204. package/dist/modules/forms/routes.d.ts +83 -1
  205. package/dist/modules/forms/routes.d.ts.map +1 -1
  206. package/dist/modules/forms/routes.js +185 -2
  207. package/dist/modules/forms/routes.js.map +1 -1
  208. package/dist/modules/forms/schema.d.ts +672 -1
  209. package/dist/modules/forms/schema.d.ts.map +1 -1
  210. package/dist/modules/forms/schema.js +146 -3
  211. package/dist/modules/forms/schema.js.map +1 -1
  212. package/dist/modules/forms/service.d.ts +54 -1
  213. package/dist/modules/forms/service.d.ts.map +1 -1
  214. package/dist/modules/forms/service.js +338 -28
  215. package/dist/modules/forms/service.js.map +1 -1
  216. package/dist/modules/forms/submission.d.ts +15 -0
  217. package/dist/modules/forms/submission.d.ts.map +1 -1
  218. package/dist/modules/forms/submission.js +418 -40
  219. package/dist/modules/forms/submission.js.map +1 -1
  220. package/dist/modules/import-export/drafts.d.ts +10 -0
  221. package/dist/modules/import-export/drafts.d.ts.map +1 -1
  222. package/dist/modules/import-export/drafts.js +16 -2
  223. package/dist/modules/import-export/drafts.js.map +1 -1
  224. package/dist/modules/import-export/exportRows.d.ts +7 -4
  225. package/dist/modules/import-export/exportRows.d.ts.map +1 -1
  226. package/dist/modules/import-export/exportRows.js +20 -5
  227. package/dist/modules/import-export/exportRows.js.map +1 -1
  228. package/dist/modules/import-export/plan.d.ts +12 -1
  229. package/dist/modules/import-export/plan.d.ts.map +1 -1
  230. package/dist/modules/import-export/plan.js +29 -2
  231. package/dist/modules/import-export/plan.js.map +1 -1
  232. package/dist/modules/import-export/presets.d.ts.map +1 -1
  233. package/dist/modules/import-export/presets.js +10 -0
  234. package/dist/modules/import-export/presets.js.map +1 -1
  235. package/dist/modules/import-export/repository.d.ts +11 -1
  236. package/dist/modules/import-export/repository.d.ts.map +1 -1
  237. package/dist/modules/import-export/repository.js +42 -9
  238. package/dist/modules/import-export/repository.js.map +1 -1
  239. package/dist/modules/import-export/routes.d.ts.map +1 -1
  240. package/dist/modules/import-export/routes.js +4 -0
  241. package/dist/modules/import-export/routes.js.map +1 -1
  242. package/dist/modules/import-export/schema.d.ts +17 -0
  243. package/dist/modules/import-export/schema.d.ts.map +1 -1
  244. package/dist/modules/import-export/schema.js +11 -0
  245. package/dist/modules/import-export/schema.js.map +1 -1
  246. package/dist/modules/import-export/service.d.ts +6 -0
  247. package/dist/modules/import-export/service.d.ts.map +1 -1
  248. package/dist/modules/import-export/service.js +14 -2
  249. package/dist/modules/import-export/service.js.map +1 -1
  250. package/dist/modules/import-export/streams.d.ts.map +1 -1
  251. package/dist/modules/import-export/streams.js +3 -3
  252. package/dist/modules/import-export/streams.js.map +1 -1
  253. package/dist/modules/import-export/tools.d.ts.map +1 -1
  254. package/dist/modules/import-export/tools.js +8 -0
  255. package/dist/modules/import-export/tools.js.map +1 -1
  256. package/dist/modules/import-export/validation.d.ts.map +1 -1
  257. package/dist/modules/import-export/validation.js +22 -1
  258. package/dist/modules/import-export/validation.js.map +1 -1
  259. package/dist/modules/import-export/writes.d.ts.map +1 -1
  260. package/dist/modules/import-export/writes.js +37 -4
  261. package/dist/modules/import-export/writes.js.map +1 -1
  262. package/dist/modules/lists/repository.d.ts +11 -0
  263. package/dist/modules/lists/repository.d.ts.map +1 -1
  264. package/dist/modules/lists/repository.js +13 -0
  265. package/dist/modules/lists/repository.js.map +1 -1
  266. package/dist/modules/lists/routes.d.ts +4 -0
  267. package/dist/modules/lists/routes.d.ts.map +1 -1
  268. package/dist/modules/lists/routes.js +8 -0
  269. package/dist/modules/lists/routes.js.map +1 -1
  270. package/dist/modules/lists/schema.d.ts +17 -0
  271. package/dist/modules/lists/schema.d.ts.map +1 -1
  272. package/dist/modules/lists/schema.js +10 -0
  273. package/dist/modules/lists/schema.js.map +1 -1
  274. package/dist/modules/lists/service.d.ts +14 -2
  275. package/dist/modules/lists/service.d.ts.map +1 -1
  276. package/dist/modules/lists/service.js +55 -7
  277. package/dist/modules/lists/service.js.map +1 -1
  278. package/dist/modules/mcp/index.d.ts.map +1 -1
  279. package/dist/modules/mcp/index.js +5 -0
  280. package/dist/modules/mcp/index.js.map +1 -1
  281. package/dist/modules/notes/routes.d.ts +1 -0
  282. package/dist/modules/notes/routes.d.ts.map +1 -1
  283. package/dist/modules/opportunities/index.d.ts.map +1 -1
  284. package/dist/modules/opportunities/index.js +3 -1
  285. package/dist/modules/opportunities/index.js.map +1 -1
  286. package/dist/modules/opportunities/repository.d.ts +2 -0
  287. package/dist/modules/opportunities/repository.d.ts.map +1 -1
  288. package/dist/modules/opportunities/repository.js +4 -0
  289. package/dist/modules/opportunities/repository.js.map +1 -1
  290. package/dist/modules/opportunities/routes.d.ts +16 -0
  291. package/dist/modules/opportunities/routes.d.ts.map +1 -1
  292. package/dist/modules/opportunities/routes.js +13 -0
  293. package/dist/modules/opportunities/routes.js.map +1 -1
  294. package/dist/modules/opportunities/schema.d.ts +20 -0
  295. package/dist/modules/opportunities/schema.d.ts.map +1 -1
  296. package/dist/modules/opportunities/schema.js +6 -1
  297. package/dist/modules/opportunities/schema.js.map +1 -1
  298. package/dist/modules/opportunities/service.d.ts +20 -3
  299. package/dist/modules/opportunities/service.d.ts.map +1 -1
  300. package/dist/modules/opportunities/service.js +99 -18
  301. package/dist/modules/opportunities/service.js.map +1 -1
  302. package/dist/modules/opportunities/tools.d.ts.map +1 -1
  303. package/dist/modules/opportunities/tools.js +2 -0
  304. package/dist/modules/opportunities/tools.js.map +1 -1
  305. package/dist/modules/partnerships/index.d.ts.map +1 -1
  306. package/dist/modules/partnerships/index.js +3 -1
  307. package/dist/modules/partnerships/index.js.map +1 -1
  308. package/dist/modules/partnerships/repository.d.ts +0 -17
  309. package/dist/modules/partnerships/repository.d.ts.map +1 -1
  310. package/dist/modules/partnerships/repository.js +6 -82
  311. package/dist/modules/partnerships/repository.js.map +1 -1
  312. package/dist/modules/partnerships/routes.d.ts +14 -0
  313. package/dist/modules/partnerships/routes.d.ts.map +1 -1
  314. package/dist/modules/partnerships/routes.js +7 -0
  315. package/dist/modules/partnerships/routes.js.map +1 -1
  316. package/dist/modules/partnerships/schema.d.ts +20 -41
  317. package/dist/modules/partnerships/schema.d.ts.map +1 -1
  318. package/dist/modules/partnerships/schema.js +6 -10
  319. package/dist/modules/partnerships/schema.js.map +1 -1
  320. package/dist/modules/partnerships/service.d.ts +11 -1
  321. package/dist/modules/partnerships/service.d.ts.map +1 -1
  322. package/dist/modules/partnerships/service.js +43 -20
  323. package/dist/modules/partnerships/service.js.map +1 -1
  324. package/dist/modules/people/consentHydration.d.ts +26 -0
  325. package/dist/modules/people/consentHydration.d.ts.map +1 -0
  326. package/dist/modules/people/consentHydration.js +100 -0
  327. package/dist/modules/people/consentHydration.js.map +1 -0
  328. package/dist/modules/people/index.d.ts.map +1 -1
  329. package/dist/modules/people/index.js +3 -1
  330. package/dist/modules/people/index.js.map +1 -1
  331. package/dist/modules/people/personConsentWrites.d.ts +40 -0
  332. package/dist/modules/people/personConsentWrites.d.ts.map +1 -0
  333. package/dist/modules/people/personConsentWrites.js +98 -0
  334. package/dist/modules/people/personConsentWrites.js.map +1 -0
  335. package/dist/modules/people/repository.d.ts.map +1 -1
  336. package/dist/modules/people/repository.js +5 -1
  337. package/dist/modules/people/repository.js.map +1 -1
  338. package/dist/modules/people/routes.d.ts +57 -4
  339. package/dist/modules/people/routes.d.ts.map +1 -1
  340. package/dist/modules/people/routes.js +104 -5
  341. package/dist/modules/people/routes.js.map +1 -1
  342. package/dist/modules/people/schema.d.ts +366 -1
  343. package/dist/modules/people/schema.d.ts.map +1 -1
  344. package/dist/modules/people/schema.js +92 -2
  345. package/dist/modules/people/schema.js.map +1 -1
  346. package/dist/modules/people/service.d.ts +30 -1
  347. package/dist/modules/people/service.d.ts.map +1 -1
  348. package/dist/modules/people/service.js +115 -38
  349. package/dist/modules/people/service.js.map +1 -1
  350. package/dist/modules/people/tools.d.ts.map +1 -1
  351. package/dist/modules/people/tools.js +4 -1
  352. package/dist/modules/people/tools.js.map +1 -1
  353. package/dist/modules/personLinks.d.ts +79 -0
  354. package/dist/modules/personLinks.d.ts.map +1 -0
  355. package/dist/modules/personLinks.js +155 -0
  356. package/dist/modules/personLinks.js.map +1 -0
  357. package/dist/modules/pipelines/routes.d.ts +1 -0
  358. package/dist/modules/pipelines/routes.d.ts.map +1 -1
  359. package/dist/modules/plans/routes.d.ts +1 -0
  360. package/dist/modules/plans/routes.d.ts.map +1 -1
  361. package/dist/modules/raises/index.d.ts.map +1 -1
  362. package/dist/modules/raises/index.js +3 -1
  363. package/dist/modules/raises/index.js.map +1 -1
  364. package/dist/modules/raises/repository.d.ts +0 -17
  365. package/dist/modules/raises/repository.d.ts.map +1 -1
  366. package/dist/modules/raises/repository.js +6 -77
  367. package/dist/modules/raises/repository.js.map +1 -1
  368. package/dist/modules/raises/routes.d.ts +14 -0
  369. package/dist/modules/raises/routes.d.ts.map +1 -1
  370. package/dist/modules/raises/routes.js +7 -0
  371. package/dist/modules/raises/routes.js.map +1 -1
  372. package/dist/modules/raises/schema.d.ts +20 -41
  373. package/dist/modules/raises/schema.d.ts.map +1 -1
  374. package/dist/modules/raises/schema.js +6 -10
  375. package/dist/modules/raises/schema.js.map +1 -1
  376. package/dist/modules/raises/service.d.ts +11 -1
  377. package/dist/modules/raises/service.d.ts.map +1 -1
  378. package/dist/modules/raises/service.js +41 -18
  379. package/dist/modules/raises/service.js.map +1 -1
  380. package/dist/modules/recordTargets.d.ts.map +1 -1
  381. package/dist/modules/recordTargets.js +2 -0
  382. package/dist/modules/recordTargets.js.map +1 -1
  383. package/dist/modules/references.d.ts +10 -0
  384. package/dist/modules/references.d.ts.map +1 -1
  385. package/dist/modules/references.js +27 -4
  386. package/dist/modules/references.js.map +1 -1
  387. package/dist/modules/sample-data/fixture.d.ts +21 -0
  388. package/dist/modules/sample-data/fixture.d.ts.map +1 -1
  389. package/dist/modules/sample-data/fixture.js +40 -0
  390. package/dist/modules/sample-data/fixture.js.map +1 -1
  391. package/dist/modules/sample-data/index.d.ts.map +1 -1
  392. package/dist/modules/sample-data/index.js +1 -0
  393. package/dist/modules/sample-data/index.js.map +1 -1
  394. package/dist/modules/sample-data/routes.d.ts.map +1 -1
  395. package/dist/modules/sample-data/routes.js +1 -0
  396. package/dist/modules/sample-data/routes.js.map +1 -1
  397. package/dist/modules/sample-data/service.d.ts +1 -0
  398. package/dist/modules/sample-data/service.d.ts.map +1 -1
  399. package/dist/modules/sample-data/service.js +77 -8
  400. package/dist/modules/sample-data/service.js.map +1 -1
  401. package/dist/modules/search/index.d.ts +1 -1
  402. package/dist/modules/search/index.d.ts.map +1 -1
  403. package/dist/modules/search/index.js +2 -1
  404. package/dist/modules/search/index.js.map +1 -1
  405. package/dist/modules/search/repository.d.ts +1 -0
  406. package/dist/modules/search/repository.d.ts.map +1 -1
  407. package/dist/modules/search/repository.js +23 -0
  408. package/dist/modules/search/repository.js.map +1 -1
  409. package/dist/modules/search/service.d.ts +3 -3
  410. package/dist/modules/search/service.d.ts.map +1 -1
  411. package/dist/modules/search/service.js +5 -4
  412. package/dist/modules/search/service.js.map +1 -1
  413. package/dist/modules/stagedRecords.d.ts +3 -3
  414. package/dist/modules/stagedRecords.d.ts.map +1 -1
  415. package/dist/modules/stagedRecords.js +9 -0
  416. package/dist/modules/stagedRecords.js.map +1 -1
  417. package/dist/modules/webhooks/payloads.d.ts.map +1 -1
  418. package/dist/modules/webhooks/payloads.js +15 -2
  419. package/dist/modules/webhooks/payloads.js.map +1 -1
  420. package/dist/modules/workspace/repository.d.ts +2 -0
  421. package/dist/modules/workspace/repository.d.ts.map +1 -1
  422. package/dist/modules/workspace/repository.js +7 -0
  423. package/dist/modules/workspace/repository.js.map +1 -1
  424. package/dist/modules/workspace/service.d.ts.map +1 -1
  425. package/dist/modules/workspace/service.js +10 -0
  426. package/dist/modules/workspace/service.js.map +1 -1
  427. package/dist/modules/workspace/starters.d.ts.map +1 -1
  428. package/dist/modules/workspace/starters.js +5 -0
  429. package/dist/modules/workspace/starters.js.map +1 -1
  430. package/dist/runtime/events.d.ts +1 -1
  431. package/dist/runtime/events.d.ts.map +1 -1
  432. package/dist/runtime/events.js +1 -0
  433. package/dist/runtime/events.js.map +1 -1
  434. package/dist/schema/index.d.ts +3 -0
  435. package/dist/schema/index.d.ts.map +1 -1
  436. package/dist/schema/index.js +3 -0
  437. package/dist/schema/index.js.map +1 -1
  438. package/dist/testing/database.d.ts +5 -1
  439. package/dist/testing/database.d.ts.map +1 -1
  440. package/dist/testing/database.js +81 -10
  441. package/dist/testing/database.js.map +1 -1
  442. package/migrations/0024_third_king_cobra.sql +46 -0
  443. package/migrations/0025_worthless_chimera.sql +3 -0
  444. package/migrations/0026_late_aqueduct.sql +46 -0
  445. package/migrations/0027_brainy_silver_sable.sql +3 -0
  446. package/migrations/0028_freezing_scrambler.sql +19 -0
  447. package/migrations/0029_custom_fields.sql +26 -0
  448. package/migrations/0030_enquiries.sql +79 -0
  449. package/migrations/0031_consent.sql +45 -0
  450. package/migrations/0032_form_field_statement.sql +1 -0
  451. package/migrations/0033_form_field_consent_purpose_ids.sql +5 -0
  452. package/migrations/0034_drop_form_field_consent_purpose_id.sql +1 -0
  453. package/migrations/0035_form_field_consent_purpose_labels.sql +1 -0
  454. package/migrations/0036_form_field_notice_type.sql +2 -0
  455. package/migrations/meta/0024_snapshot.json +5516 -0
  456. package/migrations/meta/0025_snapshot.json +5336 -0
  457. package/migrations/meta/0026_snapshot.json +5710 -0
  458. package/migrations/meta/0027_snapshot.json +5716 -0
  459. package/migrations/meta/0028_snapshot.json +5740 -0
  460. package/migrations/meta/0029_snapshot.json +5935 -0
  461. package/migrations/meta/0030_snapshot.json +6245 -0
  462. package/migrations/meta/0031_snapshot.json +6572 -0
  463. package/migrations/meta/0032_snapshot.json +6578 -0
  464. package/migrations/meta/0033_snapshot.json +6572 -0
  465. package/migrations/meta/0034_snapshot.json +6566 -0
  466. package/migrations/meta/0035_snapshot.json +6573 -0
  467. package/migrations/meta/0036_snapshot.json +6573 -0
  468. package/migrations/meta/_journal.json +91 -0
  469. package/package.json +2 -2
  470. package/src/lib/ids.ts +11 -0
  471. package/src/modules/activities/repository.ts +22 -23
  472. package/src/modules/activities/wording.ts +18 -0
  473. package/src/modules/agent-tasks/catalog.ts +63 -0
  474. package/src/modules/agent-tasks/resolve.ts +64 -24
  475. package/src/modules/attachedRecords.ts +29 -2
  476. package/src/modules/companies/index.ts +3 -1
  477. package/src/modules/companies/routes.ts +7 -0
  478. package/src/modules/companies/schema.ts +7 -1
  479. package/src/modules/companies/service.ts +48 -13
  480. package/src/modules/consent-purposes/capabilities.ts +12 -0
  481. package/src/modules/consent-purposes/events.ts +38 -0
  482. package/src/modules/consent-purposes/index.ts +48 -0
  483. package/src/modules/consent-purposes/repository.ts +163 -0
  484. package/src/modules/consent-purposes/routes.ts +122 -0
  485. package/src/modules/consent-purposes/schema.ts +46 -0
  486. package/src/modules/consent-purposes/service.ts +423 -0
  487. package/src/modules/consent-purposes/starters.ts +32 -0
  488. package/src/modules/consent-purposes/tools.ts +44 -0
  489. package/src/modules/core.ts +10 -0
  490. package/src/modules/custom-fields/capabilities.ts +17 -0
  491. package/src/modules/custom-fields/events.ts +51 -0
  492. package/src/modules/custom-fields/index.ts +61 -0
  493. package/src/modules/custom-fields/repository.ts +225 -0
  494. package/src/modules/custom-fields/routes.ts +155 -0
  495. package/src/modules/custom-fields/schema.ts +61 -0
  496. package/src/modules/custom-fields/service.ts +439 -0
  497. package/src/modules/custom-fields/tools.ts +53 -0
  498. package/src/modules/custom-fields/values.ts +352 -0
  499. package/src/modules/custom-fields/wire.ts +32 -0
  500. package/src/modules/dashboard/repository.ts +4 -3
  501. package/src/modules/deals/index.ts +5 -2
  502. package/src/modules/deals/repository.ts +6 -106
  503. package/src/modules/deals/routes.ts +7 -0
  504. package/src/modules/deals/schema.ts +7 -15
  505. package/src/modules/deals/service.ts +92 -23
  506. package/src/modules/decisions/repository.ts +2 -0
  507. package/src/modules/enquiries/events.ts +45 -0
  508. package/src/modules/enquiries/index.ts +50 -0
  509. package/src/modules/enquiries/repository.ts +164 -0
  510. package/src/modules/enquiries/routes.ts +172 -0
  511. package/src/modules/enquiries/schema.ts +59 -0
  512. package/src/modules/enquiries/service.ts +644 -0
  513. package/src/modules/enquiries/tools.ts +66 -0
  514. package/src/modules/eventCatalogs.ts +3 -0
  515. package/src/modules/forms/embed.ts +391 -44
  516. package/src/modules/forms/events.ts +30 -2
  517. package/src/modules/forms/fields.ts +102 -2
  518. package/src/modules/forms/index.ts +12 -1
  519. package/src/modules/forms/mapping.ts +183 -6
  520. package/src/modules/forms/publicRoutes.ts +43 -1
  521. package/src/modules/forms/repository.ts +162 -1
  522. package/src/modules/forms/routes.ts +212 -2
  523. package/src/modules/forms/schema.ts +166 -4
  524. package/src/modules/forms/service.ts +565 -41
  525. package/src/modules/forms/submission.ts +718 -44
  526. package/src/modules/import-export/drafts.ts +20 -1
  527. package/src/modules/import-export/exportRows.ts +21 -5
  528. package/src/modules/import-export/plan.ts +47 -2
  529. package/src/modules/import-export/presets.ts +10 -0
  530. package/src/modules/import-export/repository.ts +73 -8
  531. package/src/modules/import-export/routes.ts +4 -0
  532. package/src/modules/import-export/schema.ts +11 -0
  533. package/src/modules/import-export/service.ts +28 -2
  534. package/src/modules/import-export/streams.ts +3 -1
  535. package/src/modules/import-export/tools.ts +10 -0
  536. package/src/modules/import-export/validation.ts +28 -0
  537. package/src/modules/import-export/writes.ts +70 -5
  538. package/src/modules/lists/repository.ts +30 -0
  539. package/src/modules/lists/routes.ts +8 -0
  540. package/src/modules/lists/schema.ts +10 -0
  541. package/src/modules/lists/service.ts +110 -9
  542. package/src/modules/mcp/index.ts +5 -0
  543. package/src/modules/opportunities/index.ts +3 -1
  544. package/src/modules/opportunities/repository.ts +11 -0
  545. package/src/modules/opportunities/routes.ts +13 -0
  546. package/src/modules/opportunities/schema.ts +7 -1
  547. package/src/modules/opportunities/service.ts +188 -22
  548. package/src/modules/opportunities/tools.ts +2 -0
  549. package/src/modules/partnerships/index.ts +3 -1
  550. package/src/modules/partnerships/repository.ts +6 -117
  551. package/src/modules/partnerships/routes.ts +7 -0
  552. package/src/modules/partnerships/schema.ts +7 -14
  553. package/src/modules/partnerships/service.ts +93 -22
  554. package/src/modules/people/consentHydration.ts +167 -0
  555. package/src/modules/people/index.ts +3 -1
  556. package/src/modules/people/personConsentWrites.ts +177 -0
  557. package/src/modules/people/repository.ts +5 -0
  558. package/src/modules/people/routes.ts +127 -18
  559. package/src/modules/people/schema.ts +109 -3
  560. package/src/modules/people/service.ts +186 -40
  561. package/src/modules/people/tools.ts +5 -1
  562. package/src/modules/personLinks.ts +278 -0
  563. package/src/modules/raises/index.ts +3 -1
  564. package/src/modules/raises/repository.ts +6 -107
  565. package/src/modules/raises/routes.ts +7 -0
  566. package/src/modules/raises/schema.ts +7 -14
  567. package/src/modules/raises/service.ts +91 -20
  568. package/src/modules/recordTargets.ts +2 -0
  569. package/src/modules/references.ts +43 -4
  570. package/src/modules/sample-data/fixture.ts +63 -0
  571. package/src/modules/sample-data/index.ts +1 -0
  572. package/src/modules/sample-data/routes.ts +1 -0
  573. package/src/modules/sample-data/service.ts +97 -8
  574. package/src/modules/search/index.ts +2 -1
  575. package/src/modules/search/repository.ts +33 -1
  576. package/src/modules/search/service.ts +7 -5
  577. package/src/modules/stagedRecords.ts +18 -3
  578. package/src/modules/webhooks/payloads.ts +20 -2
  579. package/src/modules/workspace/repository.ts +12 -0
  580. package/src/modules/workspace/service.ts +14 -0
  581. package/src/modules/workspace/starters.ts +5 -0
  582. package/src/runtime/events.ts +1 -0
  583. package/src/schema/index.ts +3 -0
  584. package/src/testing/database.ts +97 -11
@@ -1,3 +1,5 @@
1
+ import type { ConsentStatus, CustomFieldWireValue } from '@kelpie/schemas'
2
+
1
3
  import { changedKeys } from '../../lib/changes.ts'
2
4
  import { UNIQUE_VIOLATION, isReferenceViolation, postgresErrorCode } from '../../lib/database.ts'
3
5
  import type { Database } from '../../lib/database.ts'
@@ -19,11 +21,17 @@ import {
19
21
  deleteRecordsAttachedTo,
20
22
  deleteRecordsAttachedToCandidaciesOf,
21
23
  } from '../attachedRecords.ts'
22
- import { referencedElsewhere } from '../references.ts'
24
+ import type { CustomFieldValuesValidator } from '../custom-fields/values.ts'
25
+ import * as personLinks from '../personLinks.ts'
26
+ import { referenceViolationTable } from '../../lib/database.ts'
27
+ import { referencedByPipelineRecords, referencedElsewhere } from '../references.ts'
23
28
  import * as repository from './repository.ts'
24
29
  import { DEFAULT_PERSON_SORT, PERSON_SORTS } from './repository.ts'
25
30
  import type { PersonFilters, PersonRecord } from './repository.ts'
26
31
  import type { Influence, PreferredChannel, Relationship, SocialProfile } from './schema.ts'
32
+ import { readConsentsFor, readConsentsForMany } from './consentHydration.ts'
33
+ import type { EffectiveConsent } from './consentHydration.ts'
34
+ import { applyManualConsentWrites, consentChangedPaths } from './personConsentWrites.ts'
27
35
 
28
36
  /**
29
37
  * People: who the workspace knows.
@@ -41,6 +49,7 @@ export interface PeopleDependencies {
41
49
  readonly createId: IdFactory
42
50
  readonly now: () => Date
43
51
  readonly recordActivity: ActivityRecorder
52
+ readonly customFields: CustomFieldValuesValidator
44
53
  }
45
54
 
46
55
  /**
@@ -52,6 +61,10 @@ export interface PeopleDependencies {
52
61
  */
53
62
  const PERSON_FIELD_LABELS: FieldLabels = {
54
63
  name: 'Name',
64
+ salutation: 'Salutation',
65
+ firstName: 'First name',
66
+ lastName: 'Last name',
67
+ suffix: 'Suffix',
55
68
  email: 'Email',
56
69
  phones: 'Phone numbers',
57
70
  socialProfiles: 'Social profiles',
@@ -63,6 +76,7 @@ const PERSON_FIELD_LABELS: FieldLabels = {
63
76
  summary: 'Summary',
64
77
  tags: 'Tags',
65
78
  lastContactedAt: 'Last contacted',
79
+ doNotContact: 'Do not contact',
66
80
  }
67
81
 
68
82
  /**
@@ -71,10 +85,27 @@ const PERSON_FIELD_LABELS: FieldLabels = {
71
85
  * Deliberately an alias rather than a parallel interface. A hand-written copy of
72
86
  * fifteen fields drifts from the table the first time one is added.
73
87
  */
74
- export type PersonView = Omit<PersonRecord, 'workspaceId'>
88
+ export type PersonView = Omit<PersonRecord, 'workspaceId'> & {
89
+ /**
90
+ * The effective consent status for every workspace purpose. An entry with
91
+ * `inherited: true` has no explicit `person_consents` row and carries the
92
+ * purpose's `default_status`; the rest have their own row.
93
+ */
94
+ readonly consents: readonly EffectiveConsent[]
95
+ }
96
+
97
+ export interface PersonConsentWriteInput {
98
+ readonly purposeSlug: string
99
+ readonly status: ConsentStatus | null
100
+ }
75
101
 
76
102
  export interface CreatePersonInput {
103
+ /** Already resolved. The route composes one from the parts when the caller sent none. */
77
104
  readonly name: string
105
+ readonly salutation: string | null
106
+ readonly firstName: string | null
107
+ readonly lastName: string | null
108
+ readonly suffix: string | null
78
109
  readonly email: string | null
79
110
  readonly phones: readonly string[]
80
111
  readonly socialProfiles: readonly SocialProfile[]
@@ -86,6 +117,15 @@ export interface CreatePersonInput {
86
117
  readonly summary: string
87
118
  readonly tags: readonly string[]
88
119
  readonly lastContactedAt: Date | null
120
+ readonly doNotContact: boolean
121
+ /**
122
+ * Consent writes for named purposes. `status: null` deletes the row and
123
+ * inherits the purpose default; anything else upserts `source: manual`.
124
+ * Absent purposes are left alone.
125
+ */
126
+ readonly consents: readonly PersonConsentWriteInput[]
127
+ /** Wire shape merge patch (undefined for a create without any): sent keys change, null clears. */
128
+ readonly customFields: Readonly<Record<string, CustomFieldWireValue | null>> | undefined
89
129
  }
90
130
 
91
131
  /** PATCH semantics: an absent field is left alone, and null clears a nullable one. */
@@ -99,10 +139,10 @@ export interface PeopleService {
99
139
  remove(actor: Actor, id: string): Promise<void>
100
140
  }
101
141
 
102
- function toView(record: PersonRecord): PersonView {
142
+ function toView(record: PersonRecord, consents: readonly EffectiveConsent[]): PersonView {
103
143
  const { workspaceId: _workspaceId, ...view } = record
104
144
 
105
- return view
145
+ return { ...view, consents }
106
146
  }
107
147
 
108
148
  /**
@@ -113,6 +153,10 @@ function toView(record: PersonRecord): PersonView {
113
153
  function toStoredColumns(input: UpdatePersonInput): Partial<repository.PersonColumns> {
114
154
  return {
115
155
  ...(input.name === undefined ? {} : { name: input.name }),
156
+ ...(input.salutation === undefined ? {} : { salutation: input.salutation }),
157
+ ...(input.firstName === undefined ? {} : { firstName: input.firstName }),
158
+ ...(input.lastName === undefined ? {} : { lastName: input.lastName }),
159
+ ...(input.suffix === undefined ? {} : { suffix: input.suffix }),
116
160
  ...(input.email === undefined ? {} : { email: input.email === null ? null : normaliseEmail(input.email) }),
117
161
  ...(input.phones === undefined ? {} : { phones: input.phones }),
118
162
  ...(input.socialProfiles === undefined ? {} : { socialProfiles: input.socialProfiles }),
@@ -124,6 +168,7 @@ function toStoredColumns(input: UpdatePersonInput): Partial<repository.PersonCol
124
168
  ...(input.summary === undefined ? {} : { summary: input.summary }),
125
169
  ...(input.tags === undefined ? {} : { tags: [...input.tags] }),
126
170
  ...(input.lastContactedAt === undefined ? {} : { lastContactedAt: input.lastContactedAt }),
171
+ ...(input.doNotContact === undefined ? {} : { doNotContact: input.doNotContact }),
127
172
  }
128
173
  }
129
174
 
@@ -151,12 +196,22 @@ export function createPeopleService(dependencies: PeopleDependencies): PeopleSer
151
196
  const workspaceId = requireWorkspaceId(actor)
152
197
  const window = readListWindow(query, PERSON_SORTS, DEFAULT_PERSON_SORT)
153
198
  const rows = await repository.listPeople(dependencies.db, workspaceId, filters, window)
199
+ const consentsByPerson = await readConsentsForMany(
200
+ dependencies.db,
201
+ workspaceId,
202
+ rows.map((row) => row.id),
203
+ )
154
204
 
155
- return mapPage(toPage(rows, window, (person) => person.id), toView)
205
+ return mapPage(toPage(rows, window, (person) => person.id), (record) =>
206
+ toView(record, consentsByPerson.get(record.id) ?? []),
207
+ )
156
208
  },
157
209
 
158
210
  async get(actor, id) {
159
- return toView(await require(requireWorkspaceId(actor), id))
211
+ const workspaceId = requireWorkspaceId(actor)
212
+ const record = await require(workspaceId, id)
213
+ const consents = await readConsentsFor(dependencies.db, workspaceId, id)
214
+ return toView(record, consents)
160
215
  },
161
216
 
162
217
  async create(actor, input) {
@@ -165,6 +220,12 @@ export function createPeopleService(dependencies: PeopleDependencies): PeopleSer
165
220
 
166
221
  return dependencies.transaction(
167
222
  async ({ tx, events }) => {
223
+ const customFields = await dependencies.customFields.forCreate(
224
+ tx,
225
+ workspaceId,
226
+ 'person',
227
+ input.customFields,
228
+ )
168
229
  let created: PersonRecord
169
230
 
170
231
  try {
@@ -172,6 +233,10 @@ export function createPeopleService(dependencies: PeopleDependencies): PeopleSer
172
233
  id,
173
234
  workspaceId,
174
235
  name: input.name,
236
+ salutation: input.salutation,
237
+ firstName: input.firstName,
238
+ lastName: input.lastName,
239
+ suffix: input.suffix,
175
240
  email: input.email === null ? null : normaliseEmail(input.email),
176
241
  phones: input.phones,
177
242
  socialProfiles: input.socialProfiles,
@@ -183,6 +248,8 @@ export function createPeopleService(dependencies: PeopleDependencies): PeopleSer
183
248
  summary: input.summary,
184
249
  tags: [...input.tags],
185
250
  lastContactedAt: input.lastContactedAt,
251
+ doNotContact: input.doNotContact,
252
+ customFields,
186
253
  })
187
254
  } catch (error: unknown) {
188
255
  if (postgresErrorCode(error) === UNIQUE_VIOLATION) {
@@ -192,6 +259,16 @@ export function createPeopleService(dependencies: PeopleDependencies): PeopleSer
192
259
  throw error
193
260
  }
194
261
 
262
+ if (input.consents.length > 0) {
263
+ await applyManualConsentWrites(
264
+ tx,
265
+ workspaceId,
266
+ created.id,
267
+ input.consents,
268
+ dependencies.now(),
269
+ )
270
+ }
271
+
195
272
  await dependencies.recordActivity(tx, workspaceId, actor, {
196
273
  targetType: 'person',
197
274
  targetId: created.id,
@@ -210,7 +287,8 @@ export function createPeopleService(dependencies: PeopleDependencies): PeopleSer
210
287
  recordActivity: dependencies.recordActivity,
211
288
  })
212
289
 
213
- return toView(created)
290
+ const consents = await readConsentsFor(tx, workspaceId, created.id)
291
+ return toView(created, consents)
214
292
  },
215
293
  { workspaceId, actor: toEventActor(actor) },
216
294
  )
@@ -220,52 +298,100 @@ export function createPeopleService(dependencies: PeopleDependencies): PeopleSer
220
298
  const workspaceId = requireWorkspaceId(actor)
221
299
  const existing = await require(workspaceId, id)
222
300
  const columns = toStoredColumns(changes)
223
- const changed = changedKeys(existing, columns)
224
-
225
- // A PATCH that changes nothing is not a write. Bumping `updated_at` for it
226
- // would make the record look freshly touched to everything that sorts or
227
- // filters by it.
228
- if (changed.length === 0) {
229
- return toView(existing)
230
- }
301
+ const scalarChanged = changedKeys(existing, columns)
302
+ const consentWrites = changes.consents ?? []
231
303
 
232
304
  return dependencies.transaction(
233
305
  async ({ tx, events }) => {
306
+ const cf = await dependencies.customFields.forUpdate(
307
+ tx,
308
+ workspaceId,
309
+ 'person',
310
+ existing.customFields,
311
+ changes.customFields,
312
+ )
313
+ const customFieldsChanged = cf !== undefined && cf.changedPaths.length > 0
314
+
315
+ const consentChanges =
316
+ consentWrites.length > 0
317
+ ? await applyManualConsentWrites(
318
+ tx,
319
+ workspaceId,
320
+ id,
321
+ consentWrites,
322
+ dependencies.now(),
323
+ )
324
+ : []
325
+ const consentPaths = consentChangedPaths(consentChanges)
326
+ const consentActuallyChanged = consentPaths.length > 0
327
+
328
+ // A PATCH that changes nothing is not a write. Bumping `updated_at`
329
+ // would make the record look freshly touched to everything that sorts
330
+ // or filters by it.
331
+ if (
332
+ scalarChanged.length === 0 &&
333
+ !customFieldsChanged &&
334
+ !consentActuallyChanged
335
+ ) {
336
+ const consents = await readConsentsFor(tx, workspaceId, id)
337
+ return toView(existing, consents)
338
+ }
339
+
234
340
  let updated: PersonRecord | undefined
235
341
 
236
- try {
237
- updated = await repository.updatePerson(tx, workspaceId, id, {
238
- ...columns,
239
- updatedAt: dependencies.now(),
240
- })
241
- } catch (error: unknown) {
242
- if (postgresErrorCode(error) === UNIQUE_VIOLATION) {
243
- throw duplicateEmail()
342
+ if (scalarChanged.length > 0 || customFieldsChanged) {
343
+ try {
344
+ updated = await repository.updatePerson(tx, workspaceId, id, {
345
+ ...columns,
346
+ ...(customFieldsChanged ? { customFields: cf.merged } : {}),
347
+ updatedAt: dependencies.now(),
348
+ })
349
+ } catch (error: unknown) {
350
+ if (postgresErrorCode(error) === UNIQUE_VIOLATION) {
351
+ throw duplicateEmail()
352
+ }
353
+
354
+ throw error
244
355
  }
245
356
 
246
- throw error
357
+ if (updated === undefined) {
358
+ throw AppError.notFound('Person not found')
359
+ }
360
+ } else {
361
+ updated = existing
247
362
  }
248
363
 
249
- if (updated === undefined) {
250
- throw AppError.notFound('Person not found')
251
- }
364
+ const customFieldPaths = cf?.changedPaths ?? []
365
+ const activityChanged = [...scalarChanged, ...customFieldPaths, ...consentPaths]
252
366
 
253
- await dependencies.recordActivity(tx, workspaceId, actor, {
254
- targetType: 'person',
255
- targetId: id,
256
- kind: 'updated',
257
- ...describeUpdate(changed, PERSON_FIELD_LABELS, existing, columns),
258
- })
367
+ if (activityChanged.length > 0) {
368
+ const labels: Record<string, string> = { ...PERSON_FIELD_LABELS, ...cf?.labels }
369
+ for (const change of consentChanges) {
370
+ labels[`consents.${change.purposeSlug}`] = `Consent · ${change.purposeLabel}`
371
+ }
372
+ const before: Record<string, unknown> = { ...existing, ...cf?.flatBefore }
373
+ const after: Record<string, unknown> = { ...columns, ...cf?.flatAfter }
374
+ for (const change of consentChanges) {
375
+ before[`consents.${change.purposeSlug}`] = change.previousStatus ?? 'inherits'
376
+ after[`consents.${change.purposeSlug}`] = change.status ?? 'inherits'
377
+ }
378
+ await dependencies.recordActivity(tx, workspaceId, actor, {
379
+ targetType: 'person',
380
+ targetId: id,
381
+ kind: 'updated',
382
+ ...describeUpdate(activityChanged, labels, before, after),
383
+ })
384
+ }
259
385
 
260
386
  events.emit(
261
387
  'people.person.updated',
262
388
  { type: 'person', id },
263
- { changed },
389
+ { changed: activityChanged },
264
390
  )
265
391
 
266
392
  // Sync auto-link when the email moved. Same-transaction rationale as
267
393
  // the create path above.
268
- if (changed.includes('email')) {
394
+ if (scalarChanged.includes('email')) {
269
395
  await autoLinkPersonByEmailDomain(tx, events, workspaceId, updated, {
270
396
  createId: dependencies.createId,
271
397
  now: dependencies.now,
@@ -273,7 +399,8 @@ export function createPeopleService(dependencies: PeopleDependencies): PeopleSer
273
399
  })
274
400
  }
275
401
 
276
- return toView(updated)
402
+ const consents = await readConsentsFor(tx, workspaceId, id)
403
+ return toView(updated, consents)
277
404
  },
278
405
  { workspaceId, actor: toEventActor(actor) },
279
406
  )
@@ -295,14 +422,33 @@ export function createPeopleService(dependencies: PeopleDependencies): PeopleSer
295
422
  // notes have to be taken here: no hiring service sees that delete.
296
423
  await deleteRecordsAttachedToCandidaciesOf(tx, workspaceId, id)
297
424
 
425
+ // A person delete failing on a foreign key aborts the enclosing
426
+ // transaction, so the follow-up read that names the referring pipeline
427
+ // types would run against a dead tx. Wrap only the delete in a nested
428
+ // transaction: drizzle emits SAVEPOINT/ROLLBACK TO for it, so the
429
+ // outer tx survives the refusal and the listLinkedTargetTypes read
430
+ // below sees person_links.
431
+ let deleteError: unknown = null
432
+
298
433
  try {
299
- await repository.deletePerson(tx, workspaceId, id)
434
+ await tx.transaction(async (savepoint) => {
435
+ await repository.deletePerson(savepoint, workspaceId, id)
436
+ })
300
437
  } catch (error: unknown) {
301
- if (isReferenceViolation(error)) {
302
- throw referencedElsewhere(error, 'person')
438
+ deleteError = error
439
+ }
440
+
441
+ if (deleteError !== null) {
442
+ if (isReferenceViolation(deleteError)) {
443
+ if (referenceViolationTable(deleteError) === 'person_links') {
444
+ const types = await personLinks.listLinkedTargetTypes(tx, workspaceId, id)
445
+ throw referencedByPipelineRecords('person', types)
446
+ }
447
+
448
+ throw referencedElsewhere(deleteError, 'person')
303
449
  }
304
450
 
305
- throw error
451
+ throw deleteError
306
452
  }
307
453
 
308
454
  events.emit('people.person.deleted', { type: 'person', id }, {})
@@ -19,7 +19,11 @@ export function registerPeopleTools(mcp: McpToolRegistry, service: PeopleService
19
19
  registerCrudTools(mcp, {
20
20
  resource: 'people',
21
21
  subject: 'person',
22
- about: 'Someone the workspace knows. A job title belongs to a position, never to the person.',
22
+ about:
23
+ 'Someone the workspace knows. A job title belongs to a position, never to the person. ' +
24
+ 'name is the display name every list shows; first_name, last_name, salutation and suffix ' +
25
+ 'are optional parts beside it. A create with no name composes one from first_name and ' +
26
+ 'last_name. Changing a part never rewrites name.',
23
27
  service,
24
28
  render: personResponse,
25
29
  listArgs,
@@ -0,0 +1,278 @@
1
+ import { and, asc, eq, inArray, sql } from 'drizzle-orm'
2
+ import type { AnyPgColumn } from 'drizzle-orm/pg-core'
3
+ import type { PipelineKind } from '@kelpie/schemas'
4
+
5
+ import type { IdFactory } from '../lib/ids.ts'
6
+ import type { Queryable } from '../runtime/transaction.ts'
7
+ import { people, personLinks } from './people/schema.ts'
8
+
9
+ /**
10
+ * Shared reader/writer for `person_links`, the polymorphic table that holds a
11
+ * person's involvement in a pipeline record. Every deal/opportunity/raise/
12
+ * partnership service reaches through here rather than owning its own join
13
+ * table. `personLinks` is defined in `people/schema.ts` because the person
14
+ * side of the relation is what gives it the real foreign key.
15
+ *
16
+ * All functions take the caller's `Queryable` and `workspaceId` so every
17
+ * query rides the `(workspace_id, target_type, target_id)` index and lands in
18
+ * whatever transaction the caller opened.
19
+ */
20
+
21
+ /**
22
+ * One target the caller writes to. Kept named rather than positional so
23
+ * `linkPeople(db, ids, ws, 'deal', dealId, personIds)` cannot swap the last
24
+ * two arguments silently.
25
+ */
26
+ export interface PipelineTarget {
27
+ readonly targetType: PipelineKind
28
+ readonly targetId: string
29
+ }
30
+
31
+ /**
32
+ * The people on one pipeline record, ordered by person_id so a response is
33
+ * stable across reads. Mirrors the deals repository's old `listPersonIds`.
34
+ */
35
+ export async function listPersonIds(
36
+ db: Queryable,
37
+ workspaceId: string,
38
+ target: PipelineTarget,
39
+ ): Promise<string[]> {
40
+ const rows = await db
41
+ .select({ personId: personLinks.personId })
42
+ .from(personLinks)
43
+ .where(
44
+ and(
45
+ eq(personLinks.workspaceId, workspaceId),
46
+ eq(personLinks.targetType, target.targetType),
47
+ eq(personLinks.targetId, target.targetId),
48
+ ),
49
+ )
50
+ .orderBy(asc(personLinks.personId))
51
+
52
+ return rows.map((row) => row.personId)
53
+ }
54
+
55
+ /**
56
+ * The people on each of a page of pipeline records of one type, in one query
57
+ * rather than one per row. Ids missing from the map have no people.
58
+ */
59
+ export async function listPersonIdsFor(
60
+ db: Queryable,
61
+ workspaceId: string,
62
+ targetType: PipelineKind,
63
+ targetIds: readonly string[],
64
+ ): Promise<ReadonlyMap<string, readonly string[]>> {
65
+ if (targetIds.length === 0) {
66
+ return new Map()
67
+ }
68
+
69
+ const rows = await db
70
+ .select({ targetId: personLinks.targetId, personId: personLinks.personId })
71
+ .from(personLinks)
72
+ .where(
73
+ and(
74
+ eq(personLinks.workspaceId, workspaceId),
75
+ eq(personLinks.targetType, targetType),
76
+ inArray(personLinks.targetId, targetIds),
77
+ ),
78
+ )
79
+ .orderBy(asc(personLinks.personId))
80
+
81
+ const byTarget = new Map<string, string[]>()
82
+
83
+ for (const row of rows) {
84
+ const existing = byTarget.get(row.targetId)
85
+
86
+ if (existing === undefined) {
87
+ byTarget.set(row.targetId, [row.personId])
88
+ } else {
89
+ existing.push(row.personId)
90
+ }
91
+ }
92
+
93
+ return byTarget
94
+ }
95
+
96
+ /**
97
+ * Every pipeline record a person is on, in one query. Used by the person
98
+ * activity roll-up and the agent-tasks person case, which used to run one
99
+ * query per join table.
100
+ */
101
+ export async function listTargetsOfPerson(
102
+ db: Queryable,
103
+ workspaceId: string,
104
+ personId: string,
105
+ ): Promise<readonly PipelineTarget[]> {
106
+ const rows = await db
107
+ .select({ targetType: personLinks.targetType, targetId: personLinks.targetId })
108
+ .from(personLinks)
109
+ .where(and(eq(personLinks.workspaceId, workspaceId), eq(personLinks.personId, personId)))
110
+
111
+ return rows.map((row) => ({
112
+ targetType: row.targetType as PipelineKind,
113
+ targetId: row.targetId,
114
+ }))
115
+ }
116
+
117
+ /**
118
+ * The distinct target types still pointing at a person. Used by the 409 on
119
+ * person delete to name each pipeline the person is on, without loading every
120
+ * row.
121
+ */
122
+ export async function listLinkedTargetTypes(
123
+ db: Queryable,
124
+ workspaceId: string,
125
+ personId: string,
126
+ ): Promise<readonly PipelineKind[]> {
127
+ const rows = await db
128
+ .selectDistinct({ targetType: personLinks.targetType })
129
+ .from(personLinks)
130
+ .where(and(eq(personLinks.workspaceId, workspaceId), eq(personLinks.personId, personId)))
131
+
132
+ return rows.map((row) => row.targetType as PipelineKind)
133
+ }
134
+
135
+ /** Bulk insert; callers pre-dedupe as today. No-op on empty. */
136
+ export async function linkPeople(
137
+ db: Queryable,
138
+ createId: IdFactory,
139
+ workspaceId: string,
140
+ target: PipelineTarget,
141
+ personIds: readonly string[],
142
+ ): Promise<void> {
143
+ if (personIds.length === 0) {
144
+ return
145
+ }
146
+
147
+ await db.insert(personLinks).values(
148
+ personIds.map((personId) => ({
149
+ id: createId('personLink'),
150
+ workspaceId,
151
+ personId,
152
+ targetType: target.targetType,
153
+ targetId: target.targetId,
154
+ })),
155
+ )
156
+ }
157
+
158
+ /** Bulk delete matching a set of people. No-op on empty. */
159
+ export async function unlinkPeople(
160
+ db: Queryable,
161
+ workspaceId: string,
162
+ target: PipelineTarget,
163
+ personIds: readonly string[],
164
+ ): Promise<void> {
165
+ if (personIds.length === 0) {
166
+ return
167
+ }
168
+
169
+ await db
170
+ .delete(personLinks)
171
+ .where(
172
+ and(
173
+ eq(personLinks.workspaceId, workspaceId),
174
+ eq(personLinks.targetType, target.targetType),
175
+ eq(personLinks.targetId, target.targetId),
176
+ inArray(personLinks.personId, personIds),
177
+ ),
178
+ )
179
+ }
180
+
181
+ /**
182
+ * Link one person, skipping if the row already exists. Used by the forms
183
+ * post-submit actions: a resubmitting visitor does not double-attach.
184
+ *
185
+ * @returns `true` when a row was inserted, `false` when the link already existed.
186
+ */
187
+ export async function linkPersonIfAbsent(
188
+ db: Queryable,
189
+ createId: IdFactory,
190
+ workspaceId: string,
191
+ target: PipelineTarget,
192
+ personId: string,
193
+ ): Promise<boolean> {
194
+ const inserted = await db
195
+ .insert(personLinks)
196
+ .values({
197
+ id: createId('personLink'),
198
+ workspaceId,
199
+ personId,
200
+ targetType: target.targetType,
201
+ targetId: target.targetId,
202
+ })
203
+ .onConflictDoNothing({
204
+ target: [personLinks.personId, personLinks.targetType, personLinks.targetId],
205
+ })
206
+ .returning({ id: personLinks.id })
207
+
208
+ return inserted.length > 0
209
+ }
210
+
211
+ /**
212
+ * Target-side cleanup, called from `attachedRecords.deleteRecordsAttachedTo`
213
+ * when a pipeline record is deleted.
214
+ *
215
+ * @returns the number of rows removed.
216
+ */
217
+ export async function deleteLinksForTarget(
218
+ db: Queryable,
219
+ workspaceId: string,
220
+ target: PipelineTarget,
221
+ ): Promise<number> {
222
+ const deleted = await db
223
+ .delete(personLinks)
224
+ .where(
225
+ and(
226
+ eq(personLinks.workspaceId, workspaceId),
227
+ eq(personLinks.targetType, target.targetType),
228
+ eq(personLinks.targetId, target.targetId),
229
+ ),
230
+ )
231
+ .returning({ id: personLinks.id })
232
+
233
+ return deleted.length
234
+ }
235
+
236
+ /**
237
+ * The `?person_id=` exists-subquery SQL builder, correlated on the outer
238
+ * table's id and workspace columns. Replaces the identical `hasAnyOfPeople`
239
+ * helpers that used to live in each pipeline module's repository.
240
+ */
241
+ export function anyPersonLinked(
242
+ targetType: PipelineKind,
243
+ targetIdColumn: AnyPgColumn,
244
+ workspaceIdColumn: AnyPgColumn,
245
+ personIds: readonly string[],
246
+ ): ReturnType<typeof sql> {
247
+ return sql`exists (
248
+ select 1
249
+ from ${personLinks}
250
+ where ${personLinks.workspaceId} = ${workspaceIdColumn}
251
+ and ${personLinks.targetType} = ${targetType}
252
+ and ${personLinks.targetId} = ${targetIdColumn}
253
+ and ${inArray(personLinks.personId, personIds)}
254
+ )`
255
+ }
256
+
257
+ /**
258
+ * The names of these people in this workspace, keyed by id. Reads the
259
+ * `people` table because that is where names live; used by every pipeline
260
+ * service to validate `person_ids` and label the `linked`/`unlinked`
261
+ * activity. An id missing from the map does not exist in this workspace.
262
+ */
263
+ export async function findPeopleNamed(
264
+ db: Queryable,
265
+ workspaceId: string,
266
+ personIds: readonly string[],
267
+ ): Promise<ReadonlyMap<string, string>> {
268
+ if (personIds.length === 0) {
269
+ return new Map()
270
+ }
271
+
272
+ const rows = await db
273
+ .select({ id: people.id, name: people.name })
274
+ .from(people)
275
+ .where(and(eq(people.workspaceId, workspaceId), inArray(people.id, personIds)))
276
+
277
+ return new Map(rows.map((row) => [row.id, row.name]))
278
+ }