@kelpie/server 0.8.0 → 0.10.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 (594) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +1 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/ids.d.ts +11 -0
  6. package/dist/lib/ids.d.ts.map +1 -1
  7. package/dist/lib/ids.js +11 -0
  8. package/dist/lib/ids.js.map +1 -1
  9. package/dist/modules/activities/repository.d.ts.map +1 -1
  10. package/dist/modules/activities/repository.js +19 -20
  11. package/dist/modules/activities/repository.js.map +1 -1
  12. package/dist/modules/activities/wording.d.ts +12 -0
  13. package/dist/modules/activities/wording.d.ts.map +1 -1
  14. package/dist/modules/activities/wording.js +16 -0
  15. package/dist/modules/activities/wording.js.map +1 -1
  16. package/dist/modules/agent-tasks/catalog.d.ts.map +1 -1
  17. package/dist/modules/agent-tasks/catalog.js +62 -0
  18. package/dist/modules/agent-tasks/catalog.js.map +1 -1
  19. package/dist/modules/agent-tasks/resolve.d.ts.map +1 -1
  20. package/dist/modules/agent-tasks/resolve.js +42 -9
  21. package/dist/modules/agent-tasks/resolve.js.map +1 -1
  22. package/dist/modules/agent-tasks/routes.d.ts +2 -0
  23. package/dist/modules/agent-tasks/routes.d.ts.map +1 -1
  24. package/dist/modules/attachedRecords.d.ts +6 -1
  25. package/dist/modules/attachedRecords.d.ts.map +1 -1
  26. package/dist/modules/attachedRecords.js +15 -1
  27. package/dist/modules/attachedRecords.js.map +1 -1
  28. package/dist/modules/companies/index.d.ts.map +1 -1
  29. package/dist/modules/companies/index.js +3 -1
  30. package/dist/modules/companies/index.js.map +1 -1
  31. package/dist/modules/companies/routes.d.ts +14 -0
  32. package/dist/modules/companies/routes.d.ts.map +1 -1
  33. package/dist/modules/companies/routes.js +7 -0
  34. package/dist/modules/companies/routes.js.map +1 -1
  35. package/dist/modules/companies/schema.d.ts +20 -0
  36. package/dist/modules/companies/schema.d.ts.map +1 -1
  37. package/dist/modules/companies/schema.js +6 -1
  38. package/dist/modules/companies/schema.js.map +1 -1
  39. package/dist/modules/companies/service.d.ts +5 -0
  40. package/dist/modules/companies/service.d.ts.map +1 -1
  41. package/dist/modules/companies/service.js +24 -12
  42. package/dist/modules/companies/service.js.map +1 -1
  43. package/dist/modules/consent-purposes/capabilities.d.ts +7 -0
  44. package/dist/modules/consent-purposes/capabilities.d.ts.map +1 -0
  45. package/dist/modules/consent-purposes/capabilities.js +10 -0
  46. package/dist/modules/consent-purposes/capabilities.js.map +1 -0
  47. package/dist/modules/consent-purposes/events.d.ts +38 -0
  48. package/dist/modules/consent-purposes/events.d.ts.map +1 -0
  49. package/dist/modules/consent-purposes/events.js +17 -0
  50. package/dist/modules/consent-purposes/events.js.map +1 -0
  51. package/dist/modules/consent-purposes/index.d.ts +13 -0
  52. package/dist/modules/consent-purposes/index.d.ts.map +1 -0
  53. package/dist/modules/consent-purposes/index.js +41 -0
  54. package/dist/modules/consent-purposes/index.js.map +1 -0
  55. package/dist/modules/consent-purposes/repository.d.ts +21 -0
  56. package/dist/modules/consent-purposes/repository.d.ts.map +1 -0
  57. package/dist/modules/consent-purposes/repository.js +98 -0
  58. package/dist/modules/consent-purposes/repository.js.map +1 -0
  59. package/dist/modules/consent-purposes/routes.d.ts +39 -0
  60. package/dist/modules/consent-purposes/routes.d.ts.map +1 -0
  61. package/dist/modules/consent-purposes/routes.js +79 -0
  62. package/dist/modules/consent-purposes/routes.js.map +1 -0
  63. package/dist/modules/consent-purposes/schema.d.ts +175 -0
  64. package/dist/modules/consent-purposes/schema.d.ts.map +1 -0
  65. package/dist/modules/consent-purposes/schema.js +35 -0
  66. package/dist/modules/consent-purposes/schema.js.map +1 -0
  67. package/dist/modules/consent-purposes/service.d.ts +50 -0
  68. package/dist/modules/consent-purposes/service.d.ts.map +1 -0
  69. package/dist/modules/consent-purposes/service.js +263 -0
  70. package/dist/modules/consent-purposes/service.js.map +1 -0
  71. package/dist/modules/consent-purposes/starters.d.ts +17 -0
  72. package/dist/modules/consent-purposes/starters.d.ts.map +1 -0
  73. package/dist/modules/consent-purposes/starters.js +15 -0
  74. package/dist/modules/consent-purposes/starters.js.map +1 -0
  75. package/dist/modules/consent-purposes/tools.d.ts +4 -0
  76. package/dist/modules/consent-purposes/tools.d.ts.map +1 -0
  77. package/dist/modules/consent-purposes/tools.js +29 -0
  78. package/dist/modules/consent-purposes/tools.js.map +1 -0
  79. package/dist/modules/core.d.ts.map +1 -1
  80. package/dist/modules/core.js +10 -0
  81. package/dist/modules/core.js.map +1 -1
  82. package/dist/modules/custom-fields/capabilities.d.ts +12 -0
  83. package/dist/modules/custom-fields/capabilities.d.ts.map +1 -0
  84. package/dist/modules/custom-fields/capabilities.js +15 -0
  85. package/dist/modules/custom-fields/capabilities.js.map +1 -0
  86. package/dist/modules/custom-fields/events.d.ts +49 -0
  87. package/dist/modules/custom-fields/events.d.ts.map +1 -0
  88. package/dist/modules/custom-fields/events.js +25 -0
  89. package/dist/modules/custom-fields/events.js.map +1 -0
  90. package/dist/modules/custom-fields/index.d.ts +19 -0
  91. package/dist/modules/custom-fields/index.d.ts.map +1 -0
  92. package/dist/modules/custom-fields/index.js +46 -0
  93. package/dist/modules/custom-fields/index.js.map +1 -0
  94. package/dist/modules/custom-fields/repository.d.ts +37 -0
  95. package/dist/modules/custom-fields/repository.d.ts.map +1 -0
  96. package/dist/modules/custom-fields/repository.js +144 -0
  97. package/dist/modules/custom-fields/repository.js.map +1 -0
  98. package/dist/modules/custom-fields/routes.d.ts +44 -0
  99. package/dist/modules/custom-fields/routes.d.ts.map +1 -0
  100. package/dist/modules/custom-fields/routes.js +103 -0
  101. package/dist/modules/custom-fields/routes.js.map +1 -0
  102. package/dist/modules/custom-fields/schema.d.ts +215 -0
  103. package/dist/modules/custom-fields/schema.d.ts.map +1 -0
  104. package/dist/modules/custom-fields/schema.js +43 -0
  105. package/dist/modules/custom-fields/schema.js.map +1 -0
  106. package/dist/modules/custom-fields/service.d.ts +57 -0
  107. package/dist/modules/custom-fields/service.d.ts.map +1 -0
  108. package/dist/modules/custom-fields/service.js +273 -0
  109. package/dist/modules/custom-fields/service.js.map +1 -0
  110. package/dist/modules/custom-fields/tools.d.ts +4 -0
  111. package/dist/modules/custom-fields/tools.d.ts.map +1 -0
  112. package/dist/modules/custom-fields/tools.js +38 -0
  113. package/dist/modules/custom-fields/tools.js.map +1 -0
  114. package/dist/modules/custom-fields/values.d.ts +49 -0
  115. package/dist/modules/custom-fields/values.d.ts.map +1 -0
  116. package/dist/modules/custom-fields/values.js +249 -0
  117. package/dist/modules/custom-fields/values.js.map +1 -0
  118. package/dist/modules/custom-fields/wire.d.ts +12 -0
  119. package/dist/modules/custom-fields/wire.d.ts.map +1 -0
  120. package/dist/modules/custom-fields/wire.js +30 -0
  121. package/dist/modules/custom-fields/wire.js.map +1 -0
  122. package/dist/modules/dashboard/repository.d.ts.map +1 -1
  123. package/dist/modules/dashboard/repository.js +4 -3
  124. package/dist/modules/dashboard/repository.js.map +1 -1
  125. package/dist/modules/deals/index.d.ts +2 -1
  126. package/dist/modules/deals/index.d.ts.map +1 -1
  127. package/dist/modules/deals/index.js +5 -2
  128. package/dist/modules/deals/index.js.map +1 -1
  129. package/dist/modules/deals/repository.d.ts +0 -16
  130. package/dist/modules/deals/repository.d.ts.map +1 -1
  131. package/dist/modules/deals/repository.js +6 -76
  132. package/dist/modules/deals/repository.js.map +1 -1
  133. package/dist/modules/deals/routes.d.ts +14 -0
  134. package/dist/modules/deals/routes.d.ts.map +1 -1
  135. package/dist/modules/deals/routes.js +7 -0
  136. package/dist/modules/deals/routes.js.map +1 -1
  137. package/dist/modules/deals/schema.d.ts +20 -42
  138. package/dist/modules/deals/schema.d.ts.map +1 -1
  139. package/dist/modules/deals/schema.js +6 -11
  140. package/dist/modules/deals/schema.js.map +1 -1
  141. package/dist/modules/deals/service.d.ts +13 -2
  142. package/dist/modules/deals/service.d.ts.map +1 -1
  143. package/dist/modules/deals/service.js +43 -20
  144. package/dist/modules/deals/service.js.map +1 -1
  145. package/dist/modules/decisions/repository.d.ts.map +1 -1
  146. package/dist/modules/decisions/repository.js +2 -0
  147. package/dist/modules/decisions/repository.js.map +1 -1
  148. package/dist/modules/decisions/routes.d.ts +1 -0
  149. package/dist/modules/decisions/routes.d.ts.map +1 -1
  150. package/dist/modules/enquiries/events.d.ts +44 -0
  151. package/dist/modules/enquiries/events.d.ts.map +1 -0
  152. package/dist/modules/enquiries/events.js +19 -0
  153. package/dist/modules/enquiries/events.js.map +1 -0
  154. package/dist/modules/enquiries/index.d.ts +13 -0
  155. package/dist/modules/enquiries/index.d.ts.map +1 -0
  156. package/dist/modules/enquiries/index.js +44 -0
  157. package/dist/modules/enquiries/index.js.map +1 -0
  158. package/dist/modules/enquiries/repository.d.ts +30 -0
  159. package/dist/modules/enquiries/repository.d.ts.map +1 -0
  160. package/dist/modules/enquiries/repository.js +94 -0
  161. package/dist/modules/enquiries/repository.js.map +1 -0
  162. package/dist/modules/enquiries/routes.d.ts +52 -0
  163. package/dist/modules/enquiries/routes.d.ts.map +1 -0
  164. package/dist/modules/enquiries/routes.js +127 -0
  165. package/dist/modules/enquiries/routes.js.map +1 -0
  166. package/dist/modules/enquiries/schema.d.ts +290 -0
  167. package/dist/modules/enquiries/schema.d.ts.map +1 -0
  168. package/dist/modules/enquiries/schema.js +52 -0
  169. package/dist/modules/enquiries/schema.js.map +1 -0
  170. package/dist/modules/enquiries/service.d.ts +85 -0
  171. package/dist/modules/enquiries/service.d.ts.map +1 -0
  172. package/dist/modules/enquiries/service.js +370 -0
  173. package/dist/modules/enquiries/service.js.map +1 -0
  174. package/dist/modules/enquiries/tools.d.ts +4 -0
  175. package/dist/modules/enquiries/tools.d.ts.map +1 -0
  176. package/dist/modules/enquiries/tools.js +50 -0
  177. package/dist/modules/enquiries/tools.js.map +1 -0
  178. package/dist/modules/eventCatalogs.d.ts +3 -0
  179. package/dist/modules/eventCatalogs.d.ts.map +1 -1
  180. package/dist/modules/eventCatalogs.js +3 -0
  181. package/dist/modules/eventCatalogs.js.map +1 -1
  182. package/dist/modules/forms/embed.d.ts +20 -6
  183. package/dist/modules/forms/embed.d.ts.map +1 -1
  184. package/dist/modules/forms/embed.js +359 -43
  185. package/dist/modules/forms/embed.js.map +1 -1
  186. package/dist/modules/forms/events.d.ts +20 -7
  187. package/dist/modules/forms/events.d.ts.map +1 -1
  188. package/dist/modules/forms/events.js +20 -2
  189. package/dist/modules/forms/events.js.map +1 -1
  190. package/dist/modules/forms/fields.d.ts +14 -1
  191. package/dist/modules/forms/fields.d.ts.map +1 -1
  192. package/dist/modules/forms/fields.js +87 -3
  193. package/dist/modules/forms/fields.js.map +1 -1
  194. package/dist/modules/forms/index.d.ts.map +1 -1
  195. package/dist/modules/forms/index.js +12 -1
  196. package/dist/modules/forms/index.js.map +1 -1
  197. package/dist/modules/forms/mapping.d.ts +62 -5
  198. package/dist/modules/forms/mapping.d.ts.map +1 -1
  199. package/dist/modules/forms/mapping.js +139 -5
  200. package/dist/modules/forms/mapping.js.map +1 -1
  201. package/dist/modules/forms/publicRoutes.d.ts.map +1 -1
  202. package/dist/modules/forms/publicRoutes.js +29 -1
  203. package/dist/modules/forms/publicRoutes.js.map +1 -1
  204. package/dist/modules/forms/repository.d.ts +31 -0
  205. package/dist/modules/forms/repository.d.ts.map +1 -1
  206. package/dist/modules/forms/repository.js +90 -1
  207. package/dist/modules/forms/repository.js.map +1 -1
  208. package/dist/modules/forms/routes.d.ts +83 -1
  209. package/dist/modules/forms/routes.d.ts.map +1 -1
  210. package/dist/modules/forms/routes.js +185 -2
  211. package/dist/modules/forms/routes.js.map +1 -1
  212. package/dist/modules/forms/schema.d.ts +672 -1
  213. package/dist/modules/forms/schema.d.ts.map +1 -1
  214. package/dist/modules/forms/schema.js +146 -3
  215. package/dist/modules/forms/schema.js.map +1 -1
  216. package/dist/modules/forms/service.d.ts +54 -1
  217. package/dist/modules/forms/service.d.ts.map +1 -1
  218. package/dist/modules/forms/service.js +338 -28
  219. package/dist/modules/forms/service.js.map +1 -1
  220. package/dist/modules/forms/submission.d.ts +15 -0
  221. package/dist/modules/forms/submission.d.ts.map +1 -1
  222. package/dist/modules/forms/submission.js +418 -40
  223. package/dist/modules/forms/submission.js.map +1 -1
  224. package/dist/modules/import-export/drafts.d.ts +10 -0
  225. package/dist/modules/import-export/drafts.d.ts.map +1 -1
  226. package/dist/modules/import-export/drafts.js +16 -2
  227. package/dist/modules/import-export/drafts.js.map +1 -1
  228. package/dist/modules/import-export/exportRows.d.ts +7 -4
  229. package/dist/modules/import-export/exportRows.d.ts.map +1 -1
  230. package/dist/modules/import-export/exportRows.js +20 -5
  231. package/dist/modules/import-export/exportRows.js.map +1 -1
  232. package/dist/modules/import-export/plan.d.ts +12 -1
  233. package/dist/modules/import-export/plan.d.ts.map +1 -1
  234. package/dist/modules/import-export/plan.js +29 -2
  235. package/dist/modules/import-export/plan.js.map +1 -1
  236. package/dist/modules/import-export/presets.d.ts.map +1 -1
  237. package/dist/modules/import-export/presets.js +10 -0
  238. package/dist/modules/import-export/presets.js.map +1 -1
  239. package/dist/modules/import-export/repository.d.ts +11 -1
  240. package/dist/modules/import-export/repository.d.ts.map +1 -1
  241. package/dist/modules/import-export/repository.js +42 -9
  242. package/dist/modules/import-export/repository.js.map +1 -1
  243. package/dist/modules/import-export/routes.d.ts.map +1 -1
  244. package/dist/modules/import-export/routes.js +4 -0
  245. package/dist/modules/import-export/routes.js.map +1 -1
  246. package/dist/modules/import-export/schema.d.ts +17 -0
  247. package/dist/modules/import-export/schema.d.ts.map +1 -1
  248. package/dist/modules/import-export/schema.js +11 -0
  249. package/dist/modules/import-export/schema.js.map +1 -1
  250. package/dist/modules/import-export/service.d.ts +6 -0
  251. package/dist/modules/import-export/service.d.ts.map +1 -1
  252. package/dist/modules/import-export/service.js +14 -2
  253. package/dist/modules/import-export/service.js.map +1 -1
  254. package/dist/modules/import-export/streams.d.ts.map +1 -1
  255. package/dist/modules/import-export/streams.js +3 -3
  256. package/dist/modules/import-export/streams.js.map +1 -1
  257. package/dist/modules/import-export/tools.d.ts.map +1 -1
  258. package/dist/modules/import-export/tools.js +8 -0
  259. package/dist/modules/import-export/tools.js.map +1 -1
  260. package/dist/modules/import-export/validation.d.ts.map +1 -1
  261. package/dist/modules/import-export/validation.js +22 -1
  262. package/dist/modules/import-export/validation.js.map +1 -1
  263. package/dist/modules/import-export/writes.d.ts.map +1 -1
  264. package/dist/modules/import-export/writes.js +37 -4
  265. package/dist/modules/import-export/writes.js.map +1 -1
  266. package/dist/modules/lists/index.d.ts +38 -0
  267. package/dist/modules/lists/index.d.ts.map +1 -1
  268. package/dist/modules/lists/index.js +27 -0
  269. package/dist/modules/lists/index.js.map +1 -1
  270. package/dist/modules/lists/repository.d.ts +26 -0
  271. package/dist/modules/lists/repository.d.ts.map +1 -1
  272. package/dist/modules/lists/repository.js +32 -0
  273. package/dist/modules/lists/repository.js.map +1 -1
  274. package/dist/modules/lists/routes.d.ts +4 -0
  275. package/dist/modules/lists/routes.d.ts.map +1 -1
  276. package/dist/modules/lists/routes.js +8 -0
  277. package/dist/modules/lists/routes.js.map +1 -1
  278. package/dist/modules/lists/schema.d.ts +17 -0
  279. package/dist/modules/lists/schema.d.ts.map +1 -1
  280. package/dist/modules/lists/schema.js +10 -0
  281. package/dist/modules/lists/schema.js.map +1 -1
  282. package/dist/modules/lists/service.d.ts +14 -2
  283. package/dist/modules/lists/service.d.ts.map +1 -1
  284. package/dist/modules/lists/service.js +55 -7
  285. package/dist/modules/lists/service.js.map +1 -1
  286. package/dist/modules/mcp/index.d.ts.map +1 -1
  287. package/dist/modules/mcp/index.js +5 -0
  288. package/dist/modules/mcp/index.js.map +1 -1
  289. package/dist/modules/notes/routes.d.ts +1 -0
  290. package/dist/modules/notes/routes.d.ts.map +1 -1
  291. package/dist/modules/opportunities/index.d.ts.map +1 -1
  292. package/dist/modules/opportunities/index.js +3 -1
  293. package/dist/modules/opportunities/index.js.map +1 -1
  294. package/dist/modules/opportunities/repository.d.ts +2 -0
  295. package/dist/modules/opportunities/repository.d.ts.map +1 -1
  296. package/dist/modules/opportunities/repository.js +4 -0
  297. package/dist/modules/opportunities/repository.js.map +1 -1
  298. package/dist/modules/opportunities/routes.d.ts +16 -0
  299. package/dist/modules/opportunities/routes.d.ts.map +1 -1
  300. package/dist/modules/opportunities/routes.js +13 -0
  301. package/dist/modules/opportunities/routes.js.map +1 -1
  302. package/dist/modules/opportunities/schema.d.ts +20 -0
  303. package/dist/modules/opportunities/schema.d.ts.map +1 -1
  304. package/dist/modules/opportunities/schema.js +6 -1
  305. package/dist/modules/opportunities/schema.js.map +1 -1
  306. package/dist/modules/opportunities/service.d.ts +20 -3
  307. package/dist/modules/opportunities/service.d.ts.map +1 -1
  308. package/dist/modules/opportunities/service.js +99 -18
  309. package/dist/modules/opportunities/service.js.map +1 -1
  310. package/dist/modules/opportunities/tools.d.ts.map +1 -1
  311. package/dist/modules/opportunities/tools.js +2 -0
  312. package/dist/modules/opportunities/tools.js.map +1 -1
  313. package/dist/modules/partnerships/index.d.ts.map +1 -1
  314. package/dist/modules/partnerships/index.js +3 -1
  315. package/dist/modules/partnerships/index.js.map +1 -1
  316. package/dist/modules/partnerships/repository.d.ts +0 -17
  317. package/dist/modules/partnerships/repository.d.ts.map +1 -1
  318. package/dist/modules/partnerships/repository.js +6 -82
  319. package/dist/modules/partnerships/repository.js.map +1 -1
  320. package/dist/modules/partnerships/routes.d.ts +14 -0
  321. package/dist/modules/partnerships/routes.d.ts.map +1 -1
  322. package/dist/modules/partnerships/routes.js +7 -0
  323. package/dist/modules/partnerships/routes.js.map +1 -1
  324. package/dist/modules/partnerships/schema.d.ts +20 -41
  325. package/dist/modules/partnerships/schema.d.ts.map +1 -1
  326. package/dist/modules/partnerships/schema.js +6 -10
  327. package/dist/modules/partnerships/schema.js.map +1 -1
  328. package/dist/modules/partnerships/service.d.ts +11 -1
  329. package/dist/modules/partnerships/service.d.ts.map +1 -1
  330. package/dist/modules/partnerships/service.js +43 -20
  331. package/dist/modules/partnerships/service.js.map +1 -1
  332. package/dist/modules/people/consentHydration.d.ts +26 -0
  333. package/dist/modules/people/consentHydration.d.ts.map +1 -0
  334. package/dist/modules/people/consentHydration.js +100 -0
  335. package/dist/modules/people/consentHydration.js.map +1 -0
  336. package/dist/modules/people/index.d.ts.map +1 -1
  337. package/dist/modules/people/index.js +3 -1
  338. package/dist/modules/people/index.js.map +1 -1
  339. package/dist/modules/people/personConsentWrites.d.ts +40 -0
  340. package/dist/modules/people/personConsentWrites.d.ts.map +1 -0
  341. package/dist/modules/people/personConsentWrites.js +98 -0
  342. package/dist/modules/people/personConsentWrites.js.map +1 -0
  343. package/dist/modules/people/repository.d.ts.map +1 -1
  344. package/dist/modules/people/repository.js +5 -1
  345. package/dist/modules/people/repository.js.map +1 -1
  346. package/dist/modules/people/routes.d.ts +57 -4
  347. package/dist/modules/people/routes.d.ts.map +1 -1
  348. package/dist/modules/people/routes.js +104 -5
  349. package/dist/modules/people/routes.js.map +1 -1
  350. package/dist/modules/people/schema.d.ts +366 -1
  351. package/dist/modules/people/schema.d.ts.map +1 -1
  352. package/dist/modules/people/schema.js +92 -2
  353. package/dist/modules/people/schema.js.map +1 -1
  354. package/dist/modules/people/service.d.ts +30 -1
  355. package/dist/modules/people/service.d.ts.map +1 -1
  356. package/dist/modules/people/service.js +115 -38
  357. package/dist/modules/people/service.js.map +1 -1
  358. package/dist/modules/people/tools.d.ts.map +1 -1
  359. package/dist/modules/people/tools.js +4 -1
  360. package/dist/modules/people/tools.js.map +1 -1
  361. package/dist/modules/personLinks.d.ts +79 -0
  362. package/dist/modules/personLinks.d.ts.map +1 -0
  363. package/dist/modules/personLinks.js +155 -0
  364. package/dist/modules/personLinks.js.map +1 -0
  365. package/dist/modules/pipelines/routes.d.ts +1 -0
  366. package/dist/modules/pipelines/routes.d.ts.map +1 -1
  367. package/dist/modules/plans/routes.d.ts +1 -0
  368. package/dist/modules/plans/routes.d.ts.map +1 -1
  369. package/dist/modules/raises/index.d.ts.map +1 -1
  370. package/dist/modules/raises/index.js +3 -1
  371. package/dist/modules/raises/index.js.map +1 -1
  372. package/dist/modules/raises/repository.d.ts +0 -17
  373. package/dist/modules/raises/repository.d.ts.map +1 -1
  374. package/dist/modules/raises/repository.js +6 -77
  375. package/dist/modules/raises/repository.js.map +1 -1
  376. package/dist/modules/raises/routes.d.ts +14 -0
  377. package/dist/modules/raises/routes.d.ts.map +1 -1
  378. package/dist/modules/raises/routes.js +7 -0
  379. package/dist/modules/raises/routes.js.map +1 -1
  380. package/dist/modules/raises/schema.d.ts +20 -41
  381. package/dist/modules/raises/schema.d.ts.map +1 -1
  382. package/dist/modules/raises/schema.js +6 -10
  383. package/dist/modules/raises/schema.js.map +1 -1
  384. package/dist/modules/raises/service.d.ts +11 -1
  385. package/dist/modules/raises/service.d.ts.map +1 -1
  386. package/dist/modules/raises/service.js +41 -18
  387. package/dist/modules/raises/service.js.map +1 -1
  388. package/dist/modules/recordTargets.d.ts.map +1 -1
  389. package/dist/modules/recordTargets.js +2 -0
  390. package/dist/modules/recordTargets.js.map +1 -1
  391. package/dist/modules/references.d.ts +10 -0
  392. package/dist/modules/references.d.ts.map +1 -1
  393. package/dist/modules/references.js +27 -4
  394. package/dist/modules/references.js.map +1 -1
  395. package/dist/modules/sample-data/fixture.d.ts +21 -0
  396. package/dist/modules/sample-data/fixture.d.ts.map +1 -1
  397. package/dist/modules/sample-data/fixture.js +40 -0
  398. package/dist/modules/sample-data/fixture.js.map +1 -1
  399. package/dist/modules/sample-data/index.d.ts.map +1 -1
  400. package/dist/modules/sample-data/index.js +1 -0
  401. package/dist/modules/sample-data/index.js.map +1 -1
  402. package/dist/modules/sample-data/routes.d.ts.map +1 -1
  403. package/dist/modules/sample-data/routes.js +1 -0
  404. package/dist/modules/sample-data/routes.js.map +1 -1
  405. package/dist/modules/sample-data/service.d.ts +1 -0
  406. package/dist/modules/sample-data/service.d.ts.map +1 -1
  407. package/dist/modules/sample-data/service.js +77 -8
  408. package/dist/modules/sample-data/service.js.map +1 -1
  409. package/dist/modules/search/index.d.ts +1 -1
  410. package/dist/modules/search/index.d.ts.map +1 -1
  411. package/dist/modules/search/index.js +2 -1
  412. package/dist/modules/search/index.js.map +1 -1
  413. package/dist/modules/search/repository.d.ts +1 -0
  414. package/dist/modules/search/repository.d.ts.map +1 -1
  415. package/dist/modules/search/repository.js +23 -0
  416. package/dist/modules/search/repository.js.map +1 -1
  417. package/dist/modules/search/service.d.ts +3 -3
  418. package/dist/modules/search/service.d.ts.map +1 -1
  419. package/dist/modules/search/service.js +5 -4
  420. package/dist/modules/search/service.js.map +1 -1
  421. package/dist/modules/stagedRecords.d.ts +3 -3
  422. package/dist/modules/stagedRecords.d.ts.map +1 -1
  423. package/dist/modules/stagedRecords.js +9 -0
  424. package/dist/modules/stagedRecords.js.map +1 -1
  425. package/dist/modules/webhooks/payloads.d.ts.map +1 -1
  426. package/dist/modules/webhooks/payloads.js +15 -2
  427. package/dist/modules/webhooks/payloads.js.map +1 -1
  428. package/dist/modules/workspace/repository.d.ts +2 -0
  429. package/dist/modules/workspace/repository.d.ts.map +1 -1
  430. package/dist/modules/workspace/repository.js +7 -0
  431. package/dist/modules/workspace/repository.js.map +1 -1
  432. package/dist/modules/workspace/service.d.ts.map +1 -1
  433. package/dist/modules/workspace/service.js +10 -0
  434. package/dist/modules/workspace/service.js.map +1 -1
  435. package/dist/modules/workspace/starters.d.ts.map +1 -1
  436. package/dist/modules/workspace/starters.js +5 -0
  437. package/dist/modules/workspace/starters.js.map +1 -1
  438. package/dist/runtime/events.d.ts +1 -1
  439. package/dist/runtime/events.d.ts.map +1 -1
  440. package/dist/runtime/events.js +1 -0
  441. package/dist/runtime/events.js.map +1 -1
  442. package/dist/schema/index.d.ts +3 -0
  443. package/dist/schema/index.d.ts.map +1 -1
  444. package/dist/schema/index.js +3 -0
  445. package/dist/schema/index.js.map +1 -1
  446. package/dist/testing/database.d.ts +5 -1
  447. package/dist/testing/database.d.ts.map +1 -1
  448. package/dist/testing/database.js +81 -10
  449. package/dist/testing/database.js.map +1 -1
  450. package/migrations/0024_third_king_cobra.sql +46 -0
  451. package/migrations/0025_worthless_chimera.sql +3 -0
  452. package/migrations/0026_late_aqueduct.sql +46 -0
  453. package/migrations/0027_brainy_silver_sable.sql +3 -0
  454. package/migrations/0028_freezing_scrambler.sql +19 -0
  455. package/migrations/0029_custom_fields.sql +26 -0
  456. package/migrations/0030_enquiries.sql +79 -0
  457. package/migrations/0031_consent.sql +45 -0
  458. package/migrations/0032_form_field_statement.sql +1 -0
  459. package/migrations/0033_form_field_consent_purpose_ids.sql +5 -0
  460. package/migrations/0034_drop_form_field_consent_purpose_id.sql +1 -0
  461. package/migrations/0035_form_field_consent_purpose_labels.sql +1 -0
  462. package/migrations/0036_form_field_notice_type.sql +2 -0
  463. package/migrations/meta/0024_snapshot.json +5516 -0
  464. package/migrations/meta/0025_snapshot.json +5336 -0
  465. package/migrations/meta/0026_snapshot.json +5710 -0
  466. package/migrations/meta/0027_snapshot.json +5716 -0
  467. package/migrations/meta/0028_snapshot.json +5740 -0
  468. package/migrations/meta/0029_snapshot.json +5935 -0
  469. package/migrations/meta/0030_snapshot.json +6245 -0
  470. package/migrations/meta/0031_snapshot.json +6572 -0
  471. package/migrations/meta/0032_snapshot.json +6578 -0
  472. package/migrations/meta/0033_snapshot.json +6572 -0
  473. package/migrations/meta/0034_snapshot.json +6566 -0
  474. package/migrations/meta/0035_snapshot.json +6573 -0
  475. package/migrations/meta/0036_snapshot.json +6573 -0
  476. package/migrations/meta/_journal.json +91 -0
  477. package/package.json +2 -2
  478. package/src/index.ts +6 -0
  479. package/src/lib/ids.ts +11 -0
  480. package/src/modules/activities/repository.ts +22 -23
  481. package/src/modules/activities/wording.ts +18 -0
  482. package/src/modules/agent-tasks/catalog.ts +63 -0
  483. package/src/modules/agent-tasks/resolve.ts +64 -24
  484. package/src/modules/attachedRecords.ts +29 -2
  485. package/src/modules/companies/index.ts +3 -1
  486. package/src/modules/companies/routes.ts +7 -0
  487. package/src/modules/companies/schema.ts +7 -1
  488. package/src/modules/companies/service.ts +48 -13
  489. package/src/modules/consent-purposes/capabilities.ts +12 -0
  490. package/src/modules/consent-purposes/events.ts +38 -0
  491. package/src/modules/consent-purposes/index.ts +48 -0
  492. package/src/modules/consent-purposes/repository.ts +163 -0
  493. package/src/modules/consent-purposes/routes.ts +122 -0
  494. package/src/modules/consent-purposes/schema.ts +46 -0
  495. package/src/modules/consent-purposes/service.ts +423 -0
  496. package/src/modules/consent-purposes/starters.ts +32 -0
  497. package/src/modules/consent-purposes/tools.ts +44 -0
  498. package/src/modules/core.ts +10 -0
  499. package/src/modules/custom-fields/capabilities.ts +17 -0
  500. package/src/modules/custom-fields/events.ts +51 -0
  501. package/src/modules/custom-fields/index.ts +61 -0
  502. package/src/modules/custom-fields/repository.ts +225 -0
  503. package/src/modules/custom-fields/routes.ts +155 -0
  504. package/src/modules/custom-fields/schema.ts +61 -0
  505. package/src/modules/custom-fields/service.ts +439 -0
  506. package/src/modules/custom-fields/tools.ts +53 -0
  507. package/src/modules/custom-fields/values.ts +352 -0
  508. package/src/modules/custom-fields/wire.ts +32 -0
  509. package/src/modules/dashboard/repository.ts +4 -3
  510. package/src/modules/deals/index.ts +5 -2
  511. package/src/modules/deals/repository.ts +6 -106
  512. package/src/modules/deals/routes.ts +7 -0
  513. package/src/modules/deals/schema.ts +7 -15
  514. package/src/modules/deals/service.ts +92 -23
  515. package/src/modules/decisions/repository.ts +2 -0
  516. package/src/modules/enquiries/events.ts +45 -0
  517. package/src/modules/enquiries/index.ts +50 -0
  518. package/src/modules/enquiries/repository.ts +164 -0
  519. package/src/modules/enquiries/routes.ts +172 -0
  520. package/src/modules/enquiries/schema.ts +59 -0
  521. package/src/modules/enquiries/service.ts +644 -0
  522. package/src/modules/enquiries/tools.ts +66 -0
  523. package/src/modules/eventCatalogs.ts +3 -0
  524. package/src/modules/forms/embed.ts +391 -44
  525. package/src/modules/forms/events.ts +30 -2
  526. package/src/modules/forms/fields.ts +102 -2
  527. package/src/modules/forms/index.ts +12 -1
  528. package/src/modules/forms/mapping.ts +183 -6
  529. package/src/modules/forms/publicRoutes.ts +43 -1
  530. package/src/modules/forms/repository.ts +162 -1
  531. package/src/modules/forms/routes.ts +212 -2
  532. package/src/modules/forms/schema.ts +166 -4
  533. package/src/modules/forms/service.ts +565 -41
  534. package/src/modules/forms/submission.ts +718 -44
  535. package/src/modules/import-export/drafts.ts +20 -1
  536. package/src/modules/import-export/exportRows.ts +21 -5
  537. package/src/modules/import-export/plan.ts +47 -2
  538. package/src/modules/import-export/presets.ts +10 -0
  539. package/src/modules/import-export/repository.ts +73 -8
  540. package/src/modules/import-export/routes.ts +4 -0
  541. package/src/modules/import-export/schema.ts +11 -0
  542. package/src/modules/import-export/service.ts +28 -2
  543. package/src/modules/import-export/streams.ts +3 -1
  544. package/src/modules/import-export/tools.ts +10 -0
  545. package/src/modules/import-export/validation.ts +28 -0
  546. package/src/modules/import-export/writes.ts +70 -5
  547. package/src/modules/lists/index.ts +71 -0
  548. package/src/modules/lists/repository.ts +64 -0
  549. package/src/modules/lists/routes.ts +8 -0
  550. package/src/modules/lists/schema.ts +10 -0
  551. package/src/modules/lists/service.ts +110 -9
  552. package/src/modules/mcp/index.ts +5 -0
  553. package/src/modules/opportunities/index.ts +3 -1
  554. package/src/modules/opportunities/repository.ts +11 -0
  555. package/src/modules/opportunities/routes.ts +13 -0
  556. package/src/modules/opportunities/schema.ts +7 -1
  557. package/src/modules/opportunities/service.ts +188 -22
  558. package/src/modules/opportunities/tools.ts +2 -0
  559. package/src/modules/partnerships/index.ts +3 -1
  560. package/src/modules/partnerships/repository.ts +6 -117
  561. package/src/modules/partnerships/routes.ts +7 -0
  562. package/src/modules/partnerships/schema.ts +7 -14
  563. package/src/modules/partnerships/service.ts +93 -22
  564. package/src/modules/people/consentHydration.ts +167 -0
  565. package/src/modules/people/index.ts +3 -1
  566. package/src/modules/people/personConsentWrites.ts +177 -0
  567. package/src/modules/people/repository.ts +5 -0
  568. package/src/modules/people/routes.ts +127 -18
  569. package/src/modules/people/schema.ts +109 -3
  570. package/src/modules/people/service.ts +186 -40
  571. package/src/modules/people/tools.ts +5 -1
  572. package/src/modules/personLinks.ts +278 -0
  573. package/src/modules/raises/index.ts +3 -1
  574. package/src/modules/raises/repository.ts +6 -107
  575. package/src/modules/raises/routes.ts +7 -0
  576. package/src/modules/raises/schema.ts +7 -14
  577. package/src/modules/raises/service.ts +91 -20
  578. package/src/modules/recordTargets.ts +2 -0
  579. package/src/modules/references.ts +43 -4
  580. package/src/modules/sample-data/fixture.ts +63 -0
  581. package/src/modules/sample-data/index.ts +1 -0
  582. package/src/modules/sample-data/routes.ts +1 -0
  583. package/src/modules/sample-data/service.ts +97 -8
  584. package/src/modules/search/index.ts +2 -1
  585. package/src/modules/search/repository.ts +33 -1
  586. package/src/modules/search/service.ts +7 -5
  587. package/src/modules/stagedRecords.ts +18 -3
  588. package/src/modules/webhooks/payloads.ts +20 -2
  589. package/src/modules/workspace/repository.ts +12 -0
  590. package/src/modules/workspace/service.ts +14 -0
  591. package/src/modules/workspace/starters.ts +5 -0
  592. package/src/runtime/events.ts +1 -0
  593. package/src/schema/index.ts +3 -0
  594. package/src/testing/database.ts +97 -11
@@ -0,0 +1,177 @@
1
+ import type { ConsentStatus } from '@kelpie/schemas'
2
+ import { and, eq, inArray } from 'drizzle-orm'
3
+
4
+ import { AppError } from '../../lib/errors.ts'
5
+ import type { Transaction } from '../../runtime/transaction.ts'
6
+ import { consentPurposes } from '../consent-purposes/schema.ts'
7
+ import { personConsents } from './schema.ts'
8
+
9
+ /**
10
+ * Writes to `person_consents`, shared by the manual PATCH override and every
11
+ * capture site (forms, lists, imports). Every write is idempotent — a second
12
+ * grant for the same (person, purpose) with the same source is a no-op.
13
+ *
14
+ * `source` follows the shape in `docs/privacy.md`:
15
+ * `form:<form_id>` | `list:<list_id>` | `import` | `manual`.
16
+ */
17
+
18
+ export interface ConsentWriteChange {
19
+ readonly purposeSlug: string
20
+ readonly purposeLabel: string
21
+ readonly status: ConsentStatus | null
22
+ readonly previousStatus: ConsentStatus | null
23
+ }
24
+
25
+ export interface ConsentWriteInput {
26
+ readonly purposeSlug: string
27
+ readonly status: ConsentStatus | null
28
+ }
29
+
30
+ interface PurposeRow {
31
+ readonly id: string
32
+ readonly slug: string
33
+ readonly label: string
34
+ }
35
+
36
+ async function resolvePurposesBySlug(
37
+ tx: Transaction,
38
+ workspaceId: string,
39
+ slugs: readonly string[],
40
+ ): Promise<ReadonlyMap<string, PurposeRow>> {
41
+ if (slugs.length === 0) return new Map()
42
+ const rows = await tx
43
+ .select({
44
+ id: consentPurposes.id,
45
+ slug: consentPurposes.slug,
46
+ label: consentPurposes.label,
47
+ })
48
+ .from(consentPurposes)
49
+ .where(
50
+ and(
51
+ eq(consentPurposes.workspaceId, workspaceId),
52
+ inArray(consentPurposes.slug, [...slugs]),
53
+ ),
54
+ )
55
+ return new Map(rows.map((row) => [row.slug, row]))
56
+ }
57
+
58
+ /**
59
+ * Applies one `PersonConsent` write. `status: null` deletes the row (falls
60
+ * back to the purpose default); any other status upserts it.
61
+ *
62
+ * @returns The change that landed — with the previous status so a caller can
63
+ * file a "no change" as a no-op activity.
64
+ */
65
+ export async function upsertPersonConsent(
66
+ tx: Transaction,
67
+ workspaceId: string,
68
+ personId: string,
69
+ purposeId: string,
70
+ purposeSlug: string,
71
+ purposeLabel: string,
72
+ status: ConsentStatus | null,
73
+ source: string,
74
+ now: Date,
75
+ ): Promise<ConsentWriteChange> {
76
+ const [existing] = await tx
77
+ .select({ status: personConsents.status })
78
+ .from(personConsents)
79
+ .where(
80
+ and(eq(personConsents.personId, personId), eq(personConsents.purposeId, purposeId)),
81
+ )
82
+ .limit(1)
83
+
84
+ const previousStatus = (existing?.status ?? null) as ConsentStatus | null
85
+
86
+ if (status === null) {
87
+ if (existing !== undefined) {
88
+ await tx
89
+ .delete(personConsents)
90
+ .where(
91
+ and(eq(personConsents.personId, personId), eq(personConsents.purposeId, purposeId)),
92
+ )
93
+ }
94
+ return { purposeSlug, purposeLabel, status: null, previousStatus }
95
+ }
96
+
97
+ await tx
98
+ .insert(personConsents)
99
+ .values({
100
+ workspaceId,
101
+ personId,
102
+ purposeId,
103
+ status,
104
+ notedAt: now,
105
+ source,
106
+ updatedAt: now,
107
+ })
108
+ .onConflictDoUpdate({
109
+ target: [personConsents.personId, personConsents.purposeId],
110
+ set: {
111
+ status,
112
+ notedAt: now,
113
+ source,
114
+ updatedAt: now,
115
+ },
116
+ })
117
+
118
+ return { purposeSlug, purposeLabel, status, previousStatus }
119
+ }
120
+
121
+ /**
122
+ * Reads by slug for a manual PATCH: resolves each named purpose to its id,
123
+ * refuses an unknown slug with a `422`, and applies the writes. Every write
124
+ * ridden through here uses `source: 'manual'`.
125
+ */
126
+ export async function applyManualConsentWrites(
127
+ tx: Transaction,
128
+ workspaceId: string,
129
+ personId: string,
130
+ writes: readonly ConsentWriteInput[],
131
+ now: Date,
132
+ ): Promise<readonly ConsentWriteChange[]> {
133
+ if (writes.length === 0) return []
134
+ const slugs = writes.map((write) => write.purposeSlug)
135
+ const purposesBySlug = await resolvePurposesBySlug(tx, workspaceId, slugs)
136
+
137
+ const missing = slugs.filter((slug) => !purposesBySlug.has(slug))
138
+ if (missing.length > 0) {
139
+ throw AppError.validationFailed(
140
+ 'One or more consent purposes were not found in this workspace',
141
+ missing.map((slug) => ({
142
+ field: `consents.${slug}`,
143
+ message: `Unknown consent purpose "${slug}"`,
144
+ })),
145
+ )
146
+ }
147
+
148
+ const changes: ConsentWriteChange[] = []
149
+ for (const write of writes) {
150
+ const purpose = purposesBySlug.get(write.purposeSlug)
151
+ if (purpose === undefined) continue // impossible after the check above
152
+ const change = await upsertPersonConsent(
153
+ tx,
154
+ workspaceId,
155
+ personId,
156
+ purpose.id,
157
+ purpose.slug,
158
+ purpose.label,
159
+ write.status,
160
+ 'manual',
161
+ now,
162
+ )
163
+ changes.push(change)
164
+ }
165
+ return changes
166
+ }
167
+
168
+ /**
169
+ * The `changed` paths a person update event emits for consent writes. Names
170
+ * every purpose that actually moved, shaped as `consents.<slug>`.
171
+ */
172
+ export function consentChangedPaths(changes: readonly ConsentWriteChange[]): readonly string[] {
173
+ return changes
174
+ .filter((change) => change.status !== change.previousStatus)
175
+ .map((change) => `consents.${change.purposeSlug}`)
176
+ }
177
+
@@ -66,6 +66,11 @@ function matchesTerm(term: string): SQL | undefined {
66
66
 
67
67
  return or(
68
68
  ilike(people.name, pattern),
69
+ // The same two parts `search_vector` carries, so filtering this list and
70
+ // searching the workspace find a person by their surname alike. Salutation
71
+ // and suffix are in neither: "Dr" matches half a workspace.
72
+ ilike(people.firstName, pattern),
73
+ ilike(people.lastName, pattern),
69
74
  ilike(people.email, pattern),
70
75
  ilike(people.summary, pattern),
71
76
  arrayContainsPattern(people.tags, pattern),
@@ -1,3 +1,5 @@
1
+ import { CONSENT_STATUSES, composeName, customFieldsPatchShape } from '@kelpie/schemas'
2
+ import type { NameParts } from '@kelpie/schemas'
1
3
  import type { Context, Hono } from 'hono'
2
4
  import { z } from 'zod'
3
5
 
@@ -5,6 +7,8 @@ import { pageBody, readIdFilter, readJsonBody, readListParameters } from '../../
5
7
  import type { Actor } from '../auth/actor.ts'
6
8
  import { resolveActorFrom } from '../auth/credentials.ts'
7
9
  import type { CredentialDependencies } from '../auth/credentials.ts'
10
+ import { renderCustomFieldsForWire } from '../custom-fields/wire.ts'
11
+ import type { EffectiveConsent } from './consentHydration.ts'
8
12
  import {
9
13
  INFLUENCE_LEVELS,
10
14
  PREFERRED_CHANNELS,
@@ -26,9 +30,32 @@ const socialProfileBody = z.strictObject({
26
30
  url: z.string().min(1),
27
31
  })
28
32
 
33
+ /**
34
+ * One manual consent write on a Person body. `purpose_slug` names the
35
+ * workspace purpose; `status: null` clears the row and falls back to the
36
+ * purpose default, any other value upserts `source: manual`.
37
+ */
38
+ const consentWriteBody = z.strictObject({
39
+ purpose_slug: z.string().min(1),
40
+ status: z.enum(CONSENT_STATUSES).nullable(),
41
+ })
42
+
43
+ /** The name parts of a body, under the names `composeName` reads them by. */
44
+ function toNameParts(body: {
45
+ readonly first_name?: string | null
46
+ readonly last_name?: string | null
47
+ readonly suffix?: string | null
48
+ }): NameParts {
49
+ return { firstName: body.first_name, lastName: body.last_name, suffix: body.suffix }
50
+ }
51
+
29
52
  /** The full field set, without defaults. `createBody` adds those; `updateBody` makes it partial. */
30
53
  const personShape = {
31
54
  name: z.string().min(1),
55
+ salutation: z.string().nullable(),
56
+ first_name: z.string().nullable(),
57
+ last_name: z.string().nullable(),
58
+ suffix: z.string().nullable(),
32
59
  email: z.string().nullable(),
33
60
  phones: z.array(z.string().min(1)),
34
61
  social_profiles: z.array(socialProfileBody),
@@ -40,27 +67,56 @@ const personShape = {
40
67
  summary: z.string(),
41
68
  tags: z.array(z.string().min(1)),
42
69
  last_contacted_at: z.iso.datetime().nullable(),
70
+ do_not_contact: z.boolean(),
71
+ consents: z.array(consentWriteBody),
72
+ custom_fields: customFieldsPatchShape,
43
73
  }
44
74
 
45
75
  /**
46
- * Only `name` is required. The defaults are the ones the mockup's own Add person
47
- * button writes, so creating through the API and creating through the UI produce
48
- * the same record.
76
+ * A create names its person and defaults everything else. The defaults are the
77
+ * ones the mockup's own Add person button writes, so creating through the API and
78
+ * creating through the UI produce the same record.
79
+ *
80
+ * Naming the person means `name`, or the `first_name` / `last_name` pair for one
81
+ * to be composed from — the shape a caller holding a row out of another CRM
82
+ * already has. Sending neither is the one refusal, because `name` is what every
83
+ * list, picker and timeline in Kelpie renders and a blank one is a record nobody
84
+ * can pick out again.
49
85
  */
50
- export const createBody = z.strictObject({
51
- ...personShape,
52
- email: personShape.email.default(null),
53
- phones: personShape.phones.default([]),
54
- social_profiles: personShape.social_profiles.default([]),
55
- timezone: personShape.timezone.default(null),
56
- location: personShape.location.default(null),
57
- preferred_channel: personShape.preferred_channel.default('email'),
58
- influence: personShape.influence.default('influencer'),
59
- relationship: personShape.relationship.default('cold'),
60
- summary: personShape.summary.default(''),
61
- tags: personShape.tags.default([]),
62
- last_contacted_at: personShape.last_contacted_at.default(null),
63
- })
86
+ export const createBody = z
87
+ .strictObject({
88
+ ...personShape,
89
+ name: personShape.name.optional(),
90
+ salutation: personShape.salutation.default(null),
91
+ first_name: personShape.first_name.default(null),
92
+ last_name: personShape.last_name.default(null),
93
+ suffix: personShape.suffix.default(null),
94
+ email: personShape.email.default(null),
95
+ phones: personShape.phones.default([]),
96
+ social_profiles: personShape.social_profiles.default([]),
97
+ timezone: personShape.timezone.default(null),
98
+ location: personShape.location.default(null),
99
+ preferred_channel: personShape.preferred_channel.default('email'),
100
+ influence: personShape.influence.default('influencer'),
101
+ relationship: personShape.relationship.default('cold'),
102
+ summary: personShape.summary.default(''),
103
+ tags: personShape.tags.default([]),
104
+ last_contacted_at: personShape.last_contacted_at.default(null),
105
+ do_not_contact: personShape.do_not_contact.default(false),
106
+ consents: personShape.consents.default([]),
107
+ custom_fields: personShape.custom_fields.default({}),
108
+ })
109
+ .superRefine((body, context) => {
110
+ if (body.name !== undefined || composeName(toNameParts(body)).length > 0) {
111
+ return
112
+ }
113
+
114
+ context.addIssue({
115
+ code: 'custom',
116
+ path: ['name'],
117
+ message: 'Send a name, or a first_name or last_name to compose one from',
118
+ })
119
+ })
64
120
 
65
121
  export const updateBody = z.strictObject(personShape).partial()
66
122
 
@@ -70,7 +126,13 @@ export interface PeopleRoutesDependencies extends CredentialDependencies {
70
126
 
71
127
  export function toCreateInput(body: z.infer<typeof createBody>): CreatePersonInput {
72
128
  return {
73
- name: body.name,
129
+ // Composed only here, on the way in, and only when the caller sent no name.
130
+ // `createBody` has already refused a body that would leave this blank.
131
+ name: body.name ?? composeName(toNameParts(body)),
132
+ salutation: body.salutation,
133
+ firstName: body.first_name,
134
+ lastName: body.last_name,
135
+ suffix: body.suffix,
74
136
  email: body.email,
75
137
  phones: body.phones,
76
138
  socialProfiles: body.social_profiles,
@@ -82,12 +144,31 @@ export function toCreateInput(body: z.infer<typeof createBody>): CreatePersonInp
82
144
  summary: body.summary,
83
145
  tags: body.tags,
84
146
  lastContactedAt: body.last_contacted_at === null ? null : new Date(body.last_contacted_at),
147
+ doNotContact: body.do_not_contact,
148
+ consents: body.consents.map((consent) => ({
149
+ purposeSlug: consent.purpose_slug,
150
+ status: consent.status,
151
+ })),
152
+ customFields: body.custom_fields,
85
153
  }
86
154
  }
87
155
 
156
+ /**
157
+ * A patch changes exactly the fields it names.
158
+ *
159
+ * Sending `first_name` alone does **not** recompute `name`. A display name is
160
+ * something somebody chose — "Chris" for a Christopher, a married name, a
161
+ * transliteration — and silently rewriting it from a part would undo that
162
+ * choice from across the room. Renaming is `name`, which is one more field in
163
+ * the same request for a caller that wants both.
164
+ */
88
165
  export function toUpdateInput(body: z.infer<typeof updateBody>): UpdatePersonInput {
89
166
  return {
90
167
  ...(body.name === undefined ? {} : { name: body.name }),
168
+ ...(body.salutation === undefined ? {} : { salutation: body.salutation }),
169
+ ...(body.first_name === undefined ? {} : { firstName: body.first_name }),
170
+ ...(body.last_name === undefined ? {} : { lastName: body.last_name }),
171
+ ...(body.suffix === undefined ? {} : { suffix: body.suffix }),
91
172
  ...(body.email === undefined ? {} : { email: body.email }),
92
173
  ...(body.phones === undefined ? {} : { phones: body.phones }),
93
174
  ...(body.social_profiles === undefined ? {} : { socialProfiles: body.social_profiles }),
@@ -104,6 +185,27 @@ export function toUpdateInput(body: z.infer<typeof updateBody>): UpdatePersonInp
104
185
  lastContactedAt:
105
186
  body.last_contacted_at === null ? null : new Date(body.last_contacted_at),
106
187
  }),
188
+ ...(body.do_not_contact === undefined ? {} : { doNotContact: body.do_not_contact }),
189
+ ...(body.consents === undefined
190
+ ? {}
191
+ : {
192
+ consents: body.consents.map((consent) => ({
193
+ purposeSlug: consent.purpose_slug,
194
+ status: consent.status,
195
+ })),
196
+ }),
197
+ ...(body.custom_fields === undefined ? {} : { customFields: body.custom_fields }),
198
+ }
199
+ }
200
+
201
+ function consentEntry(entry: EffectiveConsent): Record<string, unknown> {
202
+ return {
203
+ purpose_slug: entry.purposeSlug,
204
+ purpose_label: entry.purposeLabel,
205
+ status: entry.status,
206
+ source: entry.source,
207
+ noted_at: entry.notedAt === null ? null : entry.notedAt.toISOString(),
208
+ inherited: entry.inherited,
107
209
  }
108
210
  }
109
211
 
@@ -111,6 +213,10 @@ export function personResponse(person: PersonView): Record<string, unknown> {
111
213
  return {
112
214
  id: person.id,
113
215
  name: person.name,
216
+ salutation: person.salutation,
217
+ first_name: person.firstName,
218
+ last_name: person.lastName,
219
+ suffix: person.suffix,
114
220
  email: person.email,
115
221
  phones: person.phones,
116
222
  social_profiles: person.socialProfiles,
@@ -122,6 +228,9 @@ export function personResponse(person: PersonView): Record<string, unknown> {
122
228
  summary: person.summary,
123
229
  tags: person.tags,
124
230
  last_contacted_at: person.lastContactedAt === null ? null : person.lastContactedAt.toISOString(),
231
+ do_not_contact: person.doNotContact,
232
+ consents: person.consents.map(consentEntry),
233
+ custom_fields: renderCustomFieldsForWire(person.customFields),
125
234
  created_at: person.createdAt.toISOString(),
126
235
  updated_at: person.updatedAt.toISOString(),
127
236
  }
@@ -1,6 +1,12 @@
1
- import { INFLUENCE_LEVELS, PREFERRED_CHANNELS, RELATIONSHIP_LEVELS } from '@kelpie/schemas'
2
- import type { SocialProfile } from '@kelpie/schemas'
3
- import { index, jsonb, pgTable, text, unique } from 'drizzle-orm/pg-core'
1
+ import {
2
+ CONSENT_STATUSES,
3
+ INFLUENCE_LEVELS,
4
+ PIPELINE_KINDS,
5
+ PREFERRED_CHANNELS,
6
+ RELATIONSHIP_LEVELS,
7
+ } from '@kelpie/schemas'
8
+ import type { CustomFieldValue, SocialProfile } from '@kelpie/schemas'
9
+ import { boolean, index, jsonb, pgTable, primaryKey, text, unique } from 'drizzle-orm/pg-core'
4
10
 
5
11
  import {
6
12
  checkOneOf,
@@ -12,6 +18,7 @@ import {
12
18
  updatedAt,
13
19
  } from '../../lib/columns.ts'
14
20
  import type { SearchVectorPart } from '../../lib/columns.ts'
21
+ import { consentPurposes } from '../consent-purposes/schema.ts'
15
22
  import { workspaces } from '../workspace/schema.ts'
16
23
 
17
24
  /**
@@ -53,6 +60,14 @@ export const people = pgTable(
53
60
  .notNull()
54
61
  .references(() => workspaces.id, { onDelete: 'cascade' }),
55
62
  name: text('name').notNull(),
63
+ // The canonical display name is `name`, and it is the only one of these that
64
+ // is required. The parts are recorded when they are known and left null when
65
+ // they are not; nothing composes them back into `name` after the row exists,
66
+ // so an edit to one never renames the record behind the editor's back.
67
+ salutation: text('salutation'),
68
+ firstName: text('first_name'),
69
+ lastName: text('last_name'),
70
+ suffix: text('suffix'),
56
71
  email: citext('email'),
57
72
  phones: jsonb('phones').$type<readonly string[]>().notNull().default([]),
58
73
  socialProfiles: jsonb('social_profiles')
@@ -67,12 +82,31 @@ export const people = pgTable(
67
82
  summary: text('summary').notNull().default(''),
68
83
  tags: text('tags').array().notNull().default([]),
69
84
  lastContactedAt: moment('last_contacted_at'),
85
+ // The Article 21 objection. A global do-not-contact signal, independent
86
+ // of consent purposes: agents check this before outreach regardless of
87
+ // per-purpose consent state. Never touched by forms, lists, or imports —
88
+ // the workspace member owns this flag.
89
+ doNotContact: boolean('do_not_contact').notNull().default(false),
90
+ // Workspace-defined fields, keyed by definition key. The custom-fields
91
+ // service validates every key on write; the store here just carries the
92
+ // shape. Default `{}` mirrors `tags`'s default — the object is always
93
+ // present, never null.
94
+ customFields: jsonb('custom_fields')
95
+ .$type<Readonly<Record<string, CustomFieldValue>>>()
96
+ .notNull()
97
+ .default({}),
70
98
  createdAt: createdAt(),
71
99
  updatedAt: updatedAt(),
72
100
  // Their title is not here because it is not on this table. `GET /v1/search`
73
101
  // reaches it through `positions.search_vector`.
74
102
  searchVector: searchVector((): readonly SearchVectorPart[] => [
75
103
  { column: people.name, weight: 'A' },
104
+ // The parts carry the same weight as the display name: they exist so a
105
+ // person can be found by a name they are not displayed under, and finding
106
+ // them is the whole point of storing them. `salutation` and `suffix` are
107
+ // left out — "Dr" and "Jr" match half a workspace and narrow nothing.
108
+ { column: people.firstName, weight: 'A' },
109
+ { column: people.lastName, weight: 'A' },
76
110
  { column: people.email, weight: 'B' },
77
111
  { column: people.summary, weight: 'B' },
78
112
  { column: people.tags, weight: 'C', array: true },
@@ -87,3 +121,75 @@ export const people = pgTable(
87
121
  checkOneOf('people_relationship_check', table.relationship, RELATIONSHIP_LEVELS),
88
122
  ],
89
123
  )
124
+
125
+ /**
126
+ * The explicit consent decision for one (person, purpose) pair.
127
+ *
128
+ * Absence of a row means "inherits the purpose's `default_status`", so no
129
+ * `unknown` status here — a stored row always carries a decision. The pk is
130
+ * composite on `(person_id, purpose_id)` with no public id; the row is
131
+ * addressed by the pair, like `form_lists` and `form_attach_targets`.
132
+ *
133
+ * `source` records where the decision came from: `form:<form_id>`,
134
+ * `list:<list_id>`, `import`, or `manual`. Cascade with both the person and
135
+ * the purpose — erasing a person drops these with the rest of their record,
136
+ * and removing a purpose drops every row that still names it.
137
+ */
138
+ export const personConsents = pgTable(
139
+ 'person_consents',
140
+ {
141
+ workspaceId: text('workspace_id')
142
+ .notNull()
143
+ .references(() => workspaces.id, { onDelete: 'cascade' }),
144
+ personId: text('person_id')
145
+ .notNull()
146
+ .references(() => people.id, { onDelete: 'cascade' }),
147
+ purposeId: text('purpose_id')
148
+ .notNull()
149
+ .references(() => consentPurposes.id, { onDelete: 'cascade' }),
150
+ status: text('status').notNull(),
151
+ notedAt: moment('noted_at').notNull().defaultNow(),
152
+ source: text('source').notNull(),
153
+ createdAt: createdAt(),
154
+ updatedAt: updatedAt(),
155
+ },
156
+ (table) => [
157
+ primaryKey({ columns: [table.personId, table.purposeId] }),
158
+ index('person_consents_workspace_idx').on(table.workspaceId),
159
+ index('person_consents_purpose_idx').on(table.purposeId),
160
+ checkOneOf('person_consents_status_check', table.status, CONSENT_STATUSES),
161
+ ],
162
+ )
163
+
164
+ /**
165
+ * A person's involvement in a pipeline record — a deal, opportunity, raise, or
166
+ * partnership. One polymorphic table instead of a join per pipeline type; the
167
+ * person side keeps a real foreign key with **restrict**, so the database still
168
+ * blocks deleting a person who is on a deal. The target side is polymorphic per
169
+ * the convention: no FK, existence checked by the service, rows removed in the
170
+ * target's own delete transaction (`attachedRecords.ts`).
171
+ *
172
+ * `company` and `role` are deliberately absent from the check constraint: a
173
+ * link with a payload and its own lifecycle stays a typed object —
174
+ * Person↔Company is **Position** (title), Person↔Role is **Candidate** (status,
175
+ * stage, referrer).
176
+ */
177
+ export const personLinks = pgTable(
178
+ 'person_links',
179
+ {
180
+ id: primaryId(),
181
+ workspaceId: text('workspace_id')
182
+ .notNull()
183
+ .references(() => workspaces.id, { onDelete: 'cascade' }),
184
+ personId: text('person_id')
185
+ .notNull()
186
+ .references(() => people.id, { onDelete: 'restrict' }),
187
+ targetType: text('target_type').notNull(),
188
+ targetId: text('target_id').notNull(),
189
+ },
190
+ (table) => [
191
+ index('person_links_target_idx').on(table.workspaceId, table.targetType, table.targetId),
192
+ unique('person_links_person_target_key').on(table.personId, table.targetType, table.targetId),
193
+ checkOneOf('person_links_target_type_check', table.targetType, PIPELINE_KINDS),
194
+ ],
195
+ )