@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
@@ -3,6 +3,9 @@ import type { StoredFormFieldOption } from './schema.ts'
3
3
  import { PERSON_EMAIL_TARGET } from './schema.ts'
4
4
  import type { FormFieldMapTarget, FormFieldType, FormOptionValueType } from './schema.ts'
5
5
 
6
+ /** The map target for a consent field. Repeatable, unique per purpose_id. */
7
+ const PERSON_CONSENT_TARGET = 'person.consent'
8
+
6
9
  /**
7
10
  * What a form's field list has to satisfy before it is stored.
8
11
  *
@@ -20,6 +23,12 @@ export interface FieldDraft {
20
23
  readonly mapTo: FormFieldMapTarget
21
24
  readonly options: readonly OptionDraft[]
22
25
  readonly placeholder: string | null
26
+ /** The intro sentence for a `consent` field. Ignored for every other type. */
27
+ readonly statement: string | null
28
+ /** Non-empty for `consent` fields; one checkbox per id in display order. */
29
+ readonly consentPurposeIds: readonly string[]
30
+ /** Per-purpose override for the checkbox text. Falls back to the workspace label. */
31
+ readonly consentPurposeLabels: Readonly<Record<string, string>>
23
32
  }
24
33
 
25
34
  export interface OptionDraft {
@@ -52,13 +61,19 @@ const COMPANY_TARGETS: readonly string[] = ['company.name', 'company.domain']
52
61
  * or `company.domain` answer, so a deal-creating form without either would
53
62
  * quietly never create a deal. Refused here rather than discovered later by
54
63
  * somebody wondering where their inbound pipeline went.
64
+ * @param createsPartnership Same rule for partnerships: a Partnership belongs
65
+ * to a Company. Opportunities are exempt because `opportunities.company_id`
66
+ * is nullable — a form that creates an opportunity without resolving a
67
+ * company creates an opportunity without one, which is a valid state.
55
68
  */
56
69
  export function findFieldProblems(
57
70
  fields: readonly FieldShape[],
58
71
  createsDeal: boolean,
72
+ createsPartnership = false,
59
73
  ): readonly ErrorDetail[] {
60
74
  const problems: ErrorDetail[] = []
61
75
  const seenTargets = new Set<string>()
76
+ const usedConsentPurposes = new Set<string>()
62
77
 
63
78
  if (!fields.some((field) => field.mapTo === PERSON_EMAIL_TARGET)) {
64
79
  problems.push({
@@ -67,17 +82,78 @@ export function findFieldProblems(
67
82
  })
68
83
  }
69
84
 
70
- if (createsDeal && !fields.some((field) => COMPANY_TARGETS.includes(field.mapTo))) {
85
+ const hasCompanyField = fields.some((field) => COMPANY_TARGETS.includes(field.mapTo))
86
+
87
+ if (createsDeal && !hasCompanyField) {
71
88
  problems.push({
72
89
  field: 'fields',
73
90
  message: `A form that creates deals needs a field mapped to ${COMPANY_TARGETS.join(' or ')}`,
74
91
  })
75
92
  }
76
93
 
94
+ if (createsPartnership && !hasCompanyField) {
95
+ problems.push({
96
+ field: 'fields',
97
+ message: `A form that creates partnerships needs a field mapped to ${COMPANY_TARGETS.join(' or ')}`,
98
+ })
99
+ }
100
+
77
101
  for (const [index, field] of fields.entries()) {
78
102
  const at = `fields.${String(index)}`
79
103
 
80
- if (!REPEATABLE_TARGETS.has(field.mapTo)) {
104
+ // `person.consent` is repeatable — a form may carry more than one consent
105
+ // (checkbox) or notice (implicit) field, each offering its own list of
106
+ // purposes — but no purpose may appear on two fields, whether as the
107
+ // same field's duplicate or on two fields at once. Everything else
108
+ // non-`submission` is unique per form.
109
+ if (field.mapTo === PERSON_CONSENT_TARGET) {
110
+ if (field.type !== 'consent' && field.type !== 'notice') {
111
+ problems.push({
112
+ field: `${at}.type`,
113
+ message: 'A person.consent field must be of type "consent" or "notice"',
114
+ })
115
+ }
116
+ if (field.type === 'notice' && (field.statement ?? '').trim().length === 0) {
117
+ problems.push({
118
+ field: `${at}.statement`,
119
+ message: 'A notice field needs a statement',
120
+ })
121
+ }
122
+ if (field.consentPurposeIds.length === 0) {
123
+ problems.push({
124
+ field: `${at}.consent_purpose_ids`,
125
+ message:
126
+ field.type === 'notice'
127
+ ? 'A notice field needs at least one purpose it grants implicitly'
128
+ : 'A consent field needs at least one purpose',
129
+ })
130
+ } else {
131
+ const seenLocal = new Set<string>()
132
+ for (const purposeId of field.consentPurposeIds) {
133
+ if (seenLocal.has(purposeId)) {
134
+ problems.push({
135
+ field: `${at}.consent_purpose_ids`,
136
+ message: 'A consent field lists each purpose only once',
137
+ })
138
+ break
139
+ }
140
+ seenLocal.add(purposeId)
141
+ if (usedConsentPurposes.has(purposeId)) {
142
+ problems.push({
143
+ field: `${at}.consent_purpose_ids`,
144
+ message: 'Another consent field on this form already names this purpose',
145
+ })
146
+ } else {
147
+ usedConsentPurposes.add(purposeId)
148
+ }
149
+ }
150
+ }
151
+ } else if (field.type === 'consent' || field.type === 'notice') {
152
+ problems.push({
153
+ field: `${at}.map_to`,
154
+ message: 'A consent or notice field must map to person.consent',
155
+ })
156
+ } else if (!REPEATABLE_TARGETS.has(field.mapTo)) {
81
157
  if (seenTargets.has(field.mapTo)) {
82
158
  problems.push({ field: `${at}.map_to`, message: `Another field already maps to ${field.mapTo}` })
83
159
  }
@@ -150,6 +226,9 @@ export interface FieldShape {
150
226
  readonly mapTo: string
151
227
  readonly options: readonly StoredFormFieldOption[]
152
228
  readonly placeholder: string | null
229
+ readonly statement: string | null
230
+ readonly consentPurposeIds: readonly string[]
231
+ readonly consentPurposeLabels: Readonly<Record<string, string>>
153
232
  }
154
233
 
155
234
  /**
@@ -180,11 +259,32 @@ export function fieldsDiffer(
180
259
  field.required !== draft.required ||
181
260
  field.mapTo !== draft.mapTo ||
182
261
  field.placeholder !== draft.placeholder ||
262
+ field.statement !== draft.statement ||
263
+ stringListDiffer(field.consentPurposeIds, draft.consentPurposeIds) ||
264
+ labelMapDiffer(field.consentPurposeLabels, draft.consentPurposeLabels) ||
183
265
  optionsDiffer(field.options, draft.options)
184
266
  )
185
267
  })
186
268
  }
187
269
 
270
+ function labelMapDiffer(
271
+ stored: Readonly<Record<string, string>>,
272
+ drafts: Readonly<Record<string, string>>,
273
+ ): boolean {
274
+ const storedKeys = Object.keys(stored)
275
+ const draftKeys = Object.keys(drafts)
276
+ if (storedKeys.length !== draftKeys.length) return true
277
+ return storedKeys.some((key) => stored[key] !== drafts[key])
278
+ }
279
+
280
+ function stringListDiffer(
281
+ stored: readonly string[],
282
+ drafts: readonly string[],
283
+ ): boolean {
284
+ if (stored.length !== drafts.length) return true
285
+ return stored.some((value, index) => value !== drafts[index])
286
+ }
287
+
188
288
  function optionsDiffer(
189
289
  stored: readonly StoredFormFieldOption[],
190
290
  drafts: readonly OptionDraft[],
@@ -23,7 +23,18 @@ import { registerFormsTools } from './tools.ts'
23
23
  export function createFormsModule(migrationsDirectory: string): KelpieModule {
24
24
  return {
25
25
  id: 'forms',
26
- requires: ['people', 'companies', 'positions', 'pipelines', 'deals', 'activities'],
26
+ requires: [
27
+ 'people',
28
+ 'companies',
29
+ 'positions',
30
+ 'pipelines',
31
+ 'deals',
32
+ 'opportunities',
33
+ 'partnerships',
34
+ 'raises',
35
+ 'lists',
36
+ 'activities',
37
+ ],
27
38
  events: formsEvents,
28
39
 
29
40
  register(context) {
@@ -1,3 +1,5 @@
1
+ import { composeName } from '@kelpie/schemas'
2
+
1
3
  import type { ErrorDetail } from '../../lib/errors.ts'
2
4
  import { normaliseDomain, normaliseEmail } from '../../lib/normalisation.ts'
3
5
  import type { FormFieldRecord } from './repository.ts'
@@ -29,14 +31,48 @@ export type MappedAnswers = Partial<Record<FormFieldMapTarget, string>>
29
31
  export interface SubmitIntent {
30
32
  /** Normalised. The one value a submit cannot proceed without. */
31
33
  readonly email: string
32
- /** The name given, or the part of the address before the `@` when none was. */
34
+ /**
35
+ * The name given, the one composed from the first and last name answers, or —
36
+ * when the form asked for no name at all — the part of the address before the
37
+ * `@`.
38
+ */
33
39
  readonly personName: string
40
+ /** The `person.first_name` answer only. Never split out of `personName`. */
41
+ readonly personFirstName: string | undefined
42
+ readonly personLastName: string | undefined
34
43
  readonly companyName: string | undefined
35
44
  /** The `company.domain` answer only. Never derived from the address; see `resolveDomain`. */
36
45
  readonly companyDomain: string | undefined
37
46
  readonly positionTitle: string | undefined
38
47
  /** Only meaningful when the form creates deals. */
39
48
  readonly dealName: string | undefined
49
+ /** Only meaningful when the form creates opportunities. */
50
+ readonly opportunityName: string | undefined
51
+ /** Only meaningful when the form creates partnerships. */
52
+ readonly partnershipName: string | undefined
53
+ /** Only meaningful when the form creates enquiries. */
54
+ readonly enquiryName: string | undefined
55
+ }
56
+
57
+ /** True for a consent-field answer that means "ticked". */
58
+ export function isConsentTicked(value: string | undefined): boolean {
59
+ if (value === undefined) return false
60
+ const trimmed = value.trim().toLowerCase()
61
+ return trimmed === 'true' || trimmed === 'on' || trimmed === '1' || trimmed === 'yes'
62
+ }
63
+
64
+ /** One consent grant read off the answers, for the submit transaction to apply. */
65
+ export interface ConsentGrant {
66
+ readonly fieldId: string
67
+ readonly purposeId: string
68
+ /** The field-level intro sentence, shown above every checkbox in the field. */
69
+ readonly statement: string
70
+ /**
71
+ * The field's per-purpose text override for this purpose. Empty when the
72
+ * field defers to the workspace purpose's own label; the submit resolves
73
+ * that fallback with the purpose row it loads to record the activity.
74
+ */
75
+ readonly customLabel: string
40
76
  }
41
77
 
42
78
  /**
@@ -45,12 +81,18 @@ export interface SubmitIntent {
45
81
  * Blank answers are dropped, so a submitter who cleared a prefilled field does
46
82
  * not overwrite a stored value with nothing. Two fields sharing a target is
47
83
  * refused when the form is written, except for `submission`, which writes
48
- * nothing and so cannot collide.
84
+ * nothing and so cannot collide, and `person.consent`, which is read by
85
+ * {@link readConsentGrants} instead — a form may carry several consent boxes,
86
+ * each granting a different purpose.
49
87
  */
50
88
  export function mapAnswers(fields: readonly FormFieldRecord[], answers: Answers): MappedAnswers {
51
89
  const mapped: MappedAnswers = {}
52
90
 
53
91
  for (const field of fields) {
92
+ // `person.consent` (both `consent` and `notice` types) is read separately
93
+ // — a checkbox field by ticked ids, a notice implicitly by submission.
94
+ if (field.mapTo === 'person.consent') continue
95
+
54
96
  const value = answers[field.id]?.trim()
55
97
 
56
98
  if (value === undefined || value.length === 0) {
@@ -63,6 +105,67 @@ export function mapAnswers(fields: readonly FormFieldRecord[], answers: Answers)
63
105
  return mapped
64
106
  }
65
107
 
108
+ /**
109
+ * Every ticked purpose across every consent field on this form, in field
110
+ * order then purpose order. A consent field's answer is a comma-separated
111
+ * list of the purpose ids the visitor ticked; anything not on the field's
112
+ * configured list is dropped silently — the embed offers only what the field
113
+ * declares, so an unlisted id has to be an unusual client.
114
+ *
115
+ * The statement is the field's `statement` when set, else the field's label —
116
+ * the sentence shown above the checkboxes.
117
+ */
118
+ export function readConsentGrants(
119
+ fields: readonly FormFieldRecord[],
120
+ answers: Answers,
121
+ ): readonly ConsentGrant[] {
122
+ const grants: ConsentGrant[] = []
123
+ for (const field of fields) {
124
+ if (field.consentPurposeIds.length === 0) continue
125
+ const statement = field.statement ?? field.label
126
+
127
+ // A `notice` field grants every configured purpose implicitly: the
128
+ // visitor's act of submitting the form is the consent, per the notice
129
+ // text they read. No answer to parse.
130
+ if (field.type === 'notice') {
131
+ for (const purposeId of field.consentPurposeIds) {
132
+ grants.push({
133
+ fieldId: field.id,
134
+ purposeId,
135
+ statement,
136
+ customLabel: '',
137
+ })
138
+ }
139
+ continue
140
+ }
141
+
142
+ if (field.type !== 'consent') continue
143
+ const ticked = parseConsentAnswer(answers[field.id])
144
+ if (ticked.length === 0) continue
145
+ const allowed = new Set(field.consentPurposeIds)
146
+ for (const purposeId of ticked) {
147
+ if (!allowed.has(purposeId)) continue
148
+ grants.push({
149
+ fieldId: field.id,
150
+ purposeId,
151
+ statement,
152
+ customLabel: field.consentPurposeLabels[purposeId] ?? '',
153
+ })
154
+ }
155
+ }
156
+ return grants
157
+ }
158
+
159
+ /** Splits a consent field's answer into the ticked purpose ids. */
160
+ export function parseConsentAnswer(raw: string | undefined): readonly string[] {
161
+ if (raw === undefined) return []
162
+ const parts = raw
163
+ .split(',')
164
+ .map((part) => part.trim())
165
+ .filter((part) => part.length > 0)
166
+ return Array.from(new Set(parts))
167
+ }
168
+
66
169
  /**
67
170
  * Everything wrong with an answer map, as `api.md` field details.
68
171
  *
@@ -88,6 +191,28 @@ export function findAnswerProblems(
88
191
  }
89
192
 
90
193
  for (const field of fields) {
194
+ // A consent field's answer is a comma-separated list of ticked purpose
195
+ // ids. Required means at least one must be ticked; unrequired means the
196
+ // list may be empty. Ids not on the field's own list are silently
197
+ // ignored on submit — the embed offers only what the field configures.
198
+ if (field.type === 'consent') {
199
+ const ticked = parseConsentAnswer(answers[field.id])
200
+ if (field.required && ticked.length === 0) {
201
+ problems.push({
202
+ field: `answers.${field.id}`,
203
+ message: `${field.label} needs at least one choice`,
204
+ })
205
+ }
206
+ continue
207
+ }
208
+
209
+ // A notice field carries no answer — its consent is implicit in the
210
+ // submission. `required` is meaningless on it. An answer sent for it is
211
+ // silently ignored; the mapper never reads it either.
212
+ if (field.type === 'notice') {
213
+ continue
214
+ }
215
+
91
216
  const value = answers[field.id]?.trim() ?? ''
92
217
 
93
218
  if (value.length === 0) {
@@ -144,13 +269,30 @@ export function readIntent(mapped: MappedAnswers): SubmitIntent | undefined {
144
269
  return undefined
145
270
  }
146
271
 
272
+ const firstName = mapped['person.first_name']
273
+ const lastName = mapped['person.last_name']
274
+ // A form that asks for a first and last name rather than one name box is the
275
+ // common arrangement, so the pair composes the display name. The address's
276
+ // local part stays the last resort it always was, for a form that asks for
277
+ // neither.
278
+ const composed = composeName({ firstName, lastName })
279
+
147
280
  return {
148
281
  email,
149
- personName: mapped['person.name'] ?? email.split('@')[0] ?? email,
282
+ personName:
283
+ mapped['person.name'] ??
284
+ (composed.length > 0 ? composed : undefined) ??
285
+ email.split('@')[0] ??
286
+ email,
287
+ personFirstName: firstName,
288
+ personLastName: lastName,
150
289
  companyName: mapped['company.name'],
151
290
  companyDomain: resolveDomain(mapped),
152
291
  positionTitle: mapped['position.title'],
153
292
  dealName: mapped['deal.name'],
293
+ opportunityName: mapped['opportunity.name'],
294
+ partnershipName: mapped['partnership.name'],
295
+ enquiryName: mapped['enquiry.name'],
154
296
  }
155
297
  }
156
298
 
@@ -178,13 +320,14 @@ export function companyNameFrom(intent: SubmitIntent): string | undefined {
178
320
  }
179
321
 
180
322
  /**
181
- * Expands `{{company.name}}` and `{{person.name}}` in a deal name template.
323
+ * Expands `{{company.name}}` and `{{person.name}}` in a create-trigger name
324
+ * template. Shared by the deal, opportunity, and partnership triggers.
182
325
  *
183
326
  * The fallbacks are the mockup's: a template is written once by an admin and
184
327
  * then run against whatever arrives, so a submission with no company name must
185
- * still produce a deal with a name somebody can read on a board.
328
+ * still produce a record with a name somebody can read on a board.
186
329
  */
187
- export function expandDealNameTemplate(
330
+ export function expandNameTemplate(
188
331
  template: string,
189
332
  values: { readonly companyName: string; readonly personName: string },
190
333
  ): string {
@@ -193,6 +336,40 @@ export function expandDealNameTemplate(
193
336
  .replaceAll('{{person.name}}', values.personName.length > 0 ? values.personName : 'Lead')
194
337
  }
195
338
 
339
+ /**
340
+ * The union merge for a form's tag actions. The stored order is preserved and
341
+ * new tags land at the end, so the timeline reads oldest-first. Never removes
342
+ * a tag a human set: `forms.md` §Tags is explicit on that.
343
+ *
344
+ * @returns The merged list, and `changed` = true when at least one new tag
345
+ * landed. The caller uses `changed` to decide whether to emit an update
346
+ * event, since a no-op merge should not publish `*.updated`.
347
+ */
348
+ export function mergeTags(
349
+ stored: readonly string[],
350
+ inbound: readonly string[],
351
+ ): { readonly next: readonly string[]; readonly changed: boolean } {
352
+ const known = new Set(stored)
353
+ const additions: string[] = []
354
+
355
+ for (const tag of inbound) {
356
+ const trimmed = tag.trim()
357
+
358
+ if (trimmed.length === 0 || known.has(trimmed)) {
359
+ continue
360
+ }
361
+
362
+ known.add(trimmed)
363
+ additions.push(trimmed)
364
+ }
365
+
366
+ if (additions.length === 0) {
367
+ return { next: stored, changed: false }
368
+ }
369
+
370
+ return { next: [...stored, ...additions], changed: true }
371
+ }
372
+
196
373
  /** How far ahead a deal created by a form is expected to close (`forms.md` rule 6). */
197
374
  export const DEAL_CLOSE_HORIZON_DAYS = 30
198
375
 
@@ -7,7 +7,10 @@ import { PUBLIC_ROUTE_PREFIX, readJsonBody, requestOrigin } from '../../lib/http
7
7
  import { requireCapability } from '../../runtime/entitlements.ts'
8
8
  import type { EntitlementRegistry } from '../../runtime/entitlements.ts'
9
9
  import { moduleCapabilityName } from '../../runtime/moduleConfig.ts'
10
+ import * as consentPurposesRepository from '../consent-purposes/repository.ts'
11
+ import { findWorkspace } from '../workspace/repository.ts'
10
12
  import { embedContentSecurityPolicy, renderEmbedPage } from './embed.ts'
13
+ import type { FormFieldRecord } from './repository.ts'
11
14
  import * as repository from './repository.ts'
12
15
  import type { FormSubmitService, SubmitOutcome } from './submission.ts'
13
16
 
@@ -35,6 +38,26 @@ export interface PublicFormRoutesDependencies {
35
38
  readonly entitlements: EntitlementRegistry
36
39
  }
37
40
 
41
+ /**
42
+ * The purpose labels every consent field on this form points at, keyed by id.
43
+ * Renders the embed with the display label beside each checkbox rather than
44
+ * an opaque id. A purpose that has since been removed drops out of the map;
45
+ * `renderField` falls back to the id when it does, so a stale field stays
46
+ * visible but obviously off.
47
+ */
48
+ async function loadConsentPurposeLabels(
49
+ db: Database,
50
+ workspaceId: string,
51
+ fields: readonly FormFieldRecord[],
52
+ ): Promise<ReadonlyMap<string, string>> {
53
+ const ids = Array.from(
54
+ new Set(fields.flatMap((field) => (field.type === 'consent' ? field.consentPurposeIds : []))),
55
+ )
56
+ if (ids.length === 0) return new Map()
57
+ const rows = await consentPurposesRepository.listPurposesByIds(db, workspaceId, ids)
58
+ return new Map(rows.map((row) => [row.id, row.label]))
59
+ }
60
+
38
61
  function submitResponse(outcome: SubmitOutcome): Record<string, unknown> {
39
62
  // The upserted record ids stay off this response on purpose. The caller is an
40
63
  // unauthenticated website, and a Kelpie id is a ULID whose timestamp would tell
@@ -83,12 +106,31 @@ export function mountPublicFormRoutes(
83
106
  // module off does not serve its embed either.
84
107
  await requireCapability(dependencies.entitlements, form.workspaceId, moduleCapabilityName('forms'))
85
108
 
109
+ const workspace = await findWorkspace(dependencies.db, form.workspaceId)
110
+
111
+ if (workspace === undefined) {
112
+ throw AppError.notFound('Form not found')
113
+ }
114
+
115
+ // `view=page` is the standalone hosted URL (brand chrome). The default —
116
+ // and what iframe snippets load — is the bare embed: fields only.
117
+ const layout = context.req.query('view') === 'page' ? 'page' : 'embed'
118
+
86
119
  const nonce = generateNonce()
120
+ const fields = await repository.listFields(dependencies.db, form.id)
121
+ const consentPurposeLabels = await loadConsentPurposeLabels(
122
+ dependencies.db,
123
+ form.workspaceId,
124
+ fields,
125
+ )
87
126
  const page = renderEmbedPage({
88
127
  form,
89
- fields: await repository.listFields(dependencies.db, form.id),
128
+ fields,
129
+ consentPurposeLabels,
90
130
  submitUrl: `${requestOrigin(context)}${PUBLIC_ROUTE_PREFIX}/forms/${publicKey}/submit`,
91
131
  nonce,
132
+ workspaceName: workspace.name,
133
+ layout,
92
134
  })
93
135
 
94
136
  context.header('Content-Security-Policy', embedContentSecurityPolicy(nonce))