@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
@@ -1,3 +1,6 @@
1
+ import type { FormSubmissionActionEntry, PipelineKind } from '@kelpie/schemas'
2
+
3
+ import { UNIQUE_VIOLATION, postgresErrorCode } from '../../lib/database.ts'
1
4
  import type { Database } from '../../lib/database.ts'
2
5
  import { AppError } from '../../lib/errors.ts'
3
6
  import type { IdFactory } from '../../lib/ids.ts'
@@ -12,26 +15,40 @@ import * as companyRepository from '../companies/repository.ts'
12
15
  import type { CompanyRecord } from '../companies/repository.ts'
13
16
  import * as dealRepository from '../deals/repository.ts'
14
17
  import '../deals/events.ts'
18
+ import * as enquiryRepository from '../enquiries/repository.ts'
19
+ import '../enquiries/events.ts'
20
+ import '../lists/events.ts'
21
+ import * as listsRepository from '../lists/repository.ts'
22
+ import * as opportunityRepository from '../opportunities/repository.ts'
23
+ import '../opportunities/events.ts'
24
+ import * as partnershipRepository from '../partnerships/repository.ts'
25
+ import '../partnerships/events.ts'
26
+ import * as consentPurposesRepository from '../consent-purposes/repository.ts'
15
27
  import '../people/events.ts'
28
+ import { upsertPersonConsent } from '../people/personConsentWrites.ts'
16
29
  import * as peopleRepository from '../people/repository.ts'
17
30
  import type { PersonRecord } from '../people/repository.ts'
31
+ import * as personLinks from '../personLinks.ts'
18
32
  import * as pipelineRepository from '../pipelines/repository.ts'
19
33
  import '../positions/events.ts'
20
34
  import * as positionRepository from '../positions/repository.ts'
35
+ import { targetExists } from '../recordTargets.ts'
21
36
  import * as workspaceRepository from '../workspace/repository.ts'
22
37
  import './events.ts'
23
38
  import {
24
39
  DEAL_CLOSE_HORIZON_DAYS,
25
40
  companyNameFrom,
26
41
  describeAnswers,
27
- expandDealNameTemplate,
42
+ expandNameTemplate,
28
43
  expectedCloseFrom,
29
44
  fillBlank,
30
45
  findAnswerProblems,
31
46
  mapAnswers,
47
+ mergeTags,
48
+ readConsentGrants,
32
49
  readIntent,
33
50
  } from './mapping.ts'
34
- import type { Answers, SubmitIntent } from './mapping.ts'
51
+ import type { Answers, ConsentGrant, SubmitIntent } from './mapping.ts'
35
52
  import * as repository from './repository.ts'
36
53
  import type { FormFieldRecord, FormRecord } from './repository.ts'
37
54
 
@@ -105,9 +122,19 @@ export interface SubmitOutcome {
105
122
  readonly companyId: string | null
106
123
  readonly positionId: string | null
107
124
  readonly dealId: string | null
125
+ readonly opportunityId: string | null
126
+ readonly partnershipId: string | null
127
+ readonly enquiryId: string | null
108
128
  readonly submittedAt: Date
109
129
  /** Echoed so an embed can render it without a second request. */
110
130
  readonly thankYouMessage: string
131
+ /**
132
+ * Per-action outcome from the post-submit runner, in the order attempted.
133
+ * Empty for a form with no post-actions configured. Never crosses the public
134
+ * wire (see `publicRoutes.ts`); the authenticated Submissions read carries
135
+ * it in full.
136
+ */
137
+ readonly actionLog: readonly FormSubmissionActionEntry[]
111
138
  }
112
139
 
113
140
  export interface FormSubmitService {
@@ -115,6 +142,26 @@ export interface FormSubmitService {
115
142
  submit(publicKey: string, answers: Answers): Promise<SubmitOutcome>
116
143
  }
117
144
 
145
+ /**
146
+ * One post-submit action's outcome, as its `work` returns it. `skipped`
147
+ * means the action was configured but its precondition was absent (a
148
+ * company-tag merge on a submit that resolved no company); the runner logs
149
+ * it and moves on. `ok` means the action ran; the runner forwards every
150
+ * event the action emitted.
151
+ */
152
+ interface ActionResult<T> {
153
+ readonly status: 'ok' | 'skipped'
154
+ readonly detail: string
155
+ readonly value?: T
156
+ }
157
+
158
+ /** One event pending forwarding after a savepoint's `ok` return. */
159
+ interface PendingEvent {
160
+ readonly name: Parameters<BufferedEvents['emit']>[0]
161
+ readonly target: Parameters<BufferedEvents['emit']>[1]
162
+ readonly data: unknown
163
+ }
164
+
118
165
  /** A record, and whether this submit is what put it there. */
119
166
  interface Upserted<TRecord extends { readonly id: string }> {
120
167
  readonly record: TRecord
@@ -133,29 +180,26 @@ interface TouchedRecords {
133
180
  readonly person: Upserted<PersonRecord>
134
181
  readonly company: Upserted<CompanyRecord> | undefined
135
182
  readonly position: Upserted<PositionLink> | undefined
136
- readonly dealId: string | null
137
183
  }
138
184
 
139
185
  /**
140
- * One `record.created` per record this submit invented, and one
141
- * `record.updated` per record it filled a blank on.
186
+ * One `record.created` per record this submit invented (person, company,
187
+ * position), and one `record.updated` per record it filled a blank on. The
188
+ * pipeline creates (deal, opportunity, partnership) publish their own
189
+ * `*.created` events from inside their action's shim so a rolled-back
190
+ * savepoint drops the event alongside the row.
142
191
  *
143
- * A submit that matched an existing person and changed nothing about them emits
144
- * neither: a consumer mirroring the CRM has nothing to mirror, and
192
+ * A submit that matched an existing person and changed nothing about them
193
+ * emits neither: a consumer mirroring the CRM has nothing to mirror, and
145
194
  * `form.submitted` already says the submission happened.
146
- *
147
- * The Deal has no `Upserted` because it is only ever created, and its people
148
- * link rides along with it rather than as a separate `record.updated`, the same
149
- * way the deals service treats a create.
150
195
  */
151
196
  function emitRecordEvents(
152
197
  events: BufferedEvents,
153
198
  workspaceId: string,
154
199
  touched: TouchedRecords,
155
200
  ): void {
156
- if (touched.person !== undefined) {
157
- emitUpsertEvent(events, 'person', touched.person)
158
- }
201
+ emitUpsertEvent(events, 'person', touched.person)
202
+
159
203
  if (touched.company !== undefined) {
160
204
  emitUpsertEvent(events, 'company', touched.company)
161
205
  }
@@ -163,9 +207,6 @@ function emitRecordEvents(
163
207
  emitUpsertEvent(events, 'position', touched.position)
164
208
  }
165
209
 
166
- if (touched.dealId !== null) {
167
- events.emit('deals.deal.created', { type: 'deal', id: touched.dealId }, {})
168
- }
169
210
  // workspaceId is used by the transaction scope's envelope stamping; the
170
211
  // parameter stays on the signature so future call sites keep the same shape.
171
212
  void workspaceId
@@ -253,6 +294,8 @@ export function createFormSubmitService(dependencies: SubmissionDependencies): F
253
294
  id: dependencies.createId('person'),
254
295
  workspaceId,
255
296
  name: intent.personName,
297
+ firstName: intent.personFirstName ?? null,
298
+ lastName: intent.personLastName ?? null,
256
299
  email: intent.email,
257
300
  lastContactedAt: now,
258
301
  ...NEW_PERSON_DEFAULTS,
@@ -261,9 +304,19 @@ export function createFormSubmitService(dependencies: SubmissionDependencies): F
261
304
  return { record: created, created: true, filled: [] }
262
305
  }
263
306
 
264
- const name = fillBlank(existing.name, intent.personName)
307
+ // Each part fills its own blank. A stored first name is what the team has
308
+ // learned and an inbound one is what a visitor typed, so the rule that keeps
309
+ // an inbound "Alex" off a stored "Alex Rivera" covers these unchanged.
310
+ const filledName = fillBlank(existing.name, intent.personName)
311
+ const filledFirstName = fillBlank(existing.firstName, intent.personFirstName)
312
+ const filledLastName = fillBlank(existing.lastName, intent.personLastName)
313
+ const changes = {
314
+ ...(filledName === undefined ? {} : { name: filledName }),
315
+ ...(filledFirstName === undefined ? {} : { firstName: filledFirstName }),
316
+ ...(filledLastName === undefined ? {} : { lastName: filledLastName }),
317
+ }
265
318
  const updated = await peopleRepository.updatePerson(tx, workspaceId, existing.id, {
266
- ...(name === undefined ? {} : { name }),
319
+ ...changes,
267
320
  lastContactedAt: now,
268
321
  updatedAt: now,
269
322
  })
@@ -272,7 +325,7 @@ export function createFormSubmitService(dependencies: SubmissionDependencies): F
272
325
  throw new Error(`Person ${existing.id} disappeared during a form submit`)
273
326
  }
274
327
 
275
- return { record: updated, created: false, filled: name === undefined ? [] : ['name'] }
328
+ return { record: updated, created: false, filled: Object.keys(changes) }
276
329
  }
277
330
 
278
331
  /**
@@ -374,28 +427,77 @@ export function createFormSubmitService(dependencies: SubmissionDependencies): F
374
427
  return { record: created, created: true, filled: [] }
375
428
  }
376
429
 
377
- /** The stage a form's deals open in: the one it names, or the pipeline's first open one. */
430
+ /**
431
+ * The stage a form's created record opens in: the one the form names, or
432
+ * the pipeline's first open stage. Shared by the three create-triggers.
433
+ */
378
434
  async function openingStageId(
379
435
  tx: Transaction,
380
436
  workspaceId: string,
381
- form: FormRecord,
437
+ kind: PipelineKind,
438
+ configured: string | null,
382
439
  ): Promise<string> {
383
- if (form.dealStageId !== null) {
384
- return form.dealStageId
440
+ if (configured !== null) {
441
+ return configured
385
442
  }
386
443
 
387
- const stages = await pipelineRepository.listStagesOfKind(tx, workspaceId, 'deal')
444
+ const stages = await pipelineRepository.listStagesOfKind(tx, workspaceId, kind)
388
445
  const first = stages.find((stage) => stage.open) ?? stages[0]
389
446
 
390
447
  if (first === undefined) {
391
448
  // Unreachable through the API: a workspace seeds its stages at creation
392
449
  // and the last stage of a pipeline cannot be removed.
393
- throw AppError.conflict('This workspace has no deal stages')
450
+ throw AppError.conflict(`This workspace has no ${kind} stages`)
394
451
  }
395
452
 
396
453
  return first.id
397
454
  }
398
455
 
456
+ /**
457
+ * Runs `work` under a nested `tx.transaction()` — a SAVEPOINT in
458
+ * postgres-js — and captures every event the action emits into a local
459
+ * buffer. On success (`ok` or `skipped`) the events are forwarded to the
460
+ * real `BufferedEvents`; on throw the savepoint has already rolled back so
461
+ * the events are discarded and a `{status: 'error'}` entry lands on the log.
462
+ * Either way the outer transaction stays alive: the next action runs, and
463
+ * the visitor's 201 survives.
464
+ */
465
+ async function runAction<T>(
466
+ tx: Transaction,
467
+ events: BufferedEvents,
468
+ log: FormSubmissionActionEntry[],
469
+ action: string,
470
+ work: (inner: Transaction, emit: BufferedEvents['emit']) => Promise<ActionResult<T>>,
471
+ ): Promise<T | undefined> {
472
+ const pending: PendingEvent[] = []
473
+ const emit: BufferedEvents['emit'] = (name, target, data) => {
474
+ pending.push({ name, target, data })
475
+ }
476
+
477
+ try {
478
+ const result = await tx.transaction(async (inner) => work(inner, emit))
479
+ log.push({ action, status: result.status, detail: result.detail })
480
+
481
+ for (const event of pending) {
482
+ // The typed emit rejects an unrelated (name, data) pair; the shim
483
+ // captured them as one call so the pair is already validated.
484
+ events.emit(event.name, event.target, event.data as never)
485
+ }
486
+
487
+ return result.value
488
+ } catch (error: unknown) {
489
+ const detail =
490
+ error instanceof AppError
491
+ ? error.message
492
+ : error instanceof Error
493
+ ? error.message
494
+ : 'unknown error'
495
+ log.push({ action, status: 'error', detail })
496
+
497
+ return undefined
498
+ }
499
+ }
500
+
399
501
  /**
400
502
  * Creates the Deal, when the form makes them and a company was resolved.
401
503
  *
@@ -405,6 +507,7 @@ export function createFormSubmitService(dependencies: SubmissionDependencies): F
405
507
  */
406
508
  async function createDeal(
407
509
  tx: Transaction,
510
+ emit: BufferedEvents['emit'],
408
511
  workspaceId: string,
409
512
  form: FormRecord,
410
513
  intent: SubmitIntent,
@@ -419,19 +522,212 @@ export function createFormSubmitService(dependencies: SubmissionDependencies): F
419
522
  workspaceId,
420
523
  name:
421
524
  intent.dealName ??
422
- expandDealNameTemplate(form.dealNameTemplate ?? DEFAULT_DEAL_NAME_TEMPLATE, {
525
+ expandNameTemplate(form.dealNameTemplate ?? DEFAULT_DEAL_NAME_TEMPLATE, {
423
526
  companyName: company.name,
424
527
  personName: intent.personName,
425
528
  }),
426
529
  companyId: company.id,
427
- stageId: await openingStageId(tx, workspaceId, form),
530
+ stageId: await openingStageId(tx, workspaceId, 'deal', form.dealStageId),
428
531
  valueCents: 0,
429
532
  currency: null,
430
533
  ownerId: owner?.id ?? null,
431
534
  expectedClose: expectedCloseFrom(dependencies.now(), DEAL_CLOSE_HORIZON_DAYS),
432
535
  })
433
536
 
434
- await dealRepository.insertDealPeople(tx, id, [personId])
537
+ await personLinks.linkPeople(
538
+ tx,
539
+ dependencies.createId,
540
+ workspaceId,
541
+ { targetType: 'deal', targetId: id },
542
+ [personId],
543
+ )
544
+
545
+ await dependencies.recordActivity(tx, workspaceId, FORM_ACTOR, {
546
+ targetType: 'deal',
547
+ targetId: id,
548
+ kind: 'created',
549
+ ...describeCreationVia('Deal', form.name),
550
+ })
551
+
552
+ emit('deals.deal.created', { type: 'deal', id }, {})
553
+
554
+ return id
555
+ }
556
+
557
+ /**
558
+ * Creates the Opportunity, when the form makes them.
559
+ *
560
+ * `opportunities.company_id` is nullable, so a submit that resolved no
561
+ * company still creates the opportunity, with the company link null. The
562
+ * kind is required at form-write time (see `service.ts`), so it is always
563
+ * present here.
564
+ */
565
+ async function createOpportunity(
566
+ tx: Transaction,
567
+ emit: BufferedEvents['emit'],
568
+ workspaceId: string,
569
+ form: FormRecord,
570
+ intent: SubmitIntent,
571
+ company: CompanyRecord | undefined,
572
+ personId: string,
573
+ ): Promise<string> {
574
+ const id = dependencies.createId('opportunity')
575
+ const owner =
576
+ form.opportunityOwnerId === null
577
+ ? await workspaceRepository.findDefaultMember(tx, workspaceId)
578
+ : { id: form.opportunityOwnerId }
579
+
580
+ await opportunityRepository.insertOpportunity(tx, {
581
+ id,
582
+ workspaceId,
583
+ name:
584
+ intent.opportunityName ??
585
+ expandNameTemplate(form.opportunityNameTemplate ?? DEFAULT_DEAL_NAME_TEMPLATE, {
586
+ companyName: company?.name ?? '',
587
+ personName: intent.personName,
588
+ }),
589
+ // requireKindWhenCreating refuses an empty kind at write; the ?? is a
590
+ // narrowing convenience, not a runtime fallback.
591
+ kind: form.opportunityKind ?? '',
592
+ stageId: await openingStageId(tx, workspaceId, 'opportunity', form.opportunityStageId),
593
+ companyId: company?.id ?? null,
594
+ ownerId: owner?.id ?? null,
595
+ expectedClose: expectedCloseFrom(dependencies.now(), DEAL_CLOSE_HORIZON_DAYS),
596
+ })
597
+
598
+ await personLinks.linkPeople(
599
+ tx,
600
+ dependencies.createId,
601
+ workspaceId,
602
+ { targetType: 'opportunity', targetId: id },
603
+ [personId],
604
+ )
605
+
606
+ await dependencies.recordActivity(tx, workspaceId, FORM_ACTOR, {
607
+ targetType: 'opportunity',
608
+ targetId: id,
609
+ kind: 'created',
610
+ ...describeCreationVia('Opportunity', form.name),
611
+ })
612
+
613
+ emit('opportunities.opportunity.created', { type: 'opportunity', id }, {})
614
+
615
+ return id
616
+ }
617
+
618
+ /**
619
+ * Creates the Partnership, when the form makes them and a company was
620
+ * resolved. `partnerships.company_id` is `NOT NULL`, matching Deal.
621
+ */
622
+ async function createPartnership(
623
+ tx: Transaction,
624
+ emit: BufferedEvents['emit'],
625
+ workspaceId: string,
626
+ form: FormRecord,
627
+ intent: SubmitIntent,
628
+ company: CompanyRecord,
629
+ personId: string,
630
+ ): Promise<string> {
631
+ const id = dependencies.createId('partnership')
632
+ const owner =
633
+ form.partnershipOwnerId === null
634
+ ? await workspaceRepository.findDefaultMember(tx, workspaceId)
635
+ : { id: form.partnershipOwnerId }
636
+ const nextTouchpoint = expectedCloseFrom(dependencies.now(), DEAL_CLOSE_HORIZON_DAYS)
637
+
638
+ await partnershipRepository.insertPartnership(tx, {
639
+ id,
640
+ workspaceId,
641
+ name:
642
+ intent.partnershipName ??
643
+ expandNameTemplate(form.partnershipNameTemplate ?? DEFAULT_DEAL_NAME_TEMPLATE, {
644
+ companyName: company.name,
645
+ personName: intent.personName,
646
+ }),
647
+ companyId: company.id,
648
+ stageId: await openingStageId(tx, workspaceId, 'partnership', form.partnershipStageId),
649
+ kind: form.partnershipKind ?? '',
650
+ nextTouchpoint,
651
+ ownerId: owner?.id ?? null,
652
+ goals: '',
653
+ successLooksLike: '',
654
+ })
655
+
656
+ await personLinks.linkPeople(
657
+ tx,
658
+ dependencies.createId,
659
+ workspaceId,
660
+ { targetType: 'partnership', targetId: id },
661
+ [personId],
662
+ )
663
+
664
+ await dependencies.recordActivity(tx, workspaceId, FORM_ACTOR, {
665
+ targetType: 'partnership',
666
+ targetId: id,
667
+ kind: 'created',
668
+ ...describeCreationVia('Partnership', form.name),
669
+ })
670
+
671
+ emit('partnerships.partnership.created', { type: 'partnership', id }, {})
672
+
673
+ return id
674
+ }
675
+
676
+ /**
677
+ * Creates the Enquiry, when the form makes them.
678
+ *
679
+ * `enquiries.company_id` is nullable, so a submit that resolved no company
680
+ * still creates the enquiry. Enquiries have no `kind`; the form's optional
681
+ * `enquirySource` is written to the enquiry's `source` column, or empty when
682
+ * unset — an unclassified source reads as unclassified.
683
+ */
684
+ async function createEnquiry(
685
+ tx: Transaction,
686
+ emit: BufferedEvents['emit'],
687
+ workspaceId: string,
688
+ form: FormRecord,
689
+ intent: SubmitIntent,
690
+ company: CompanyRecord | undefined,
691
+ personId: string,
692
+ ): Promise<string> {
693
+ const id = dependencies.createId('enquiry')
694
+ const owner =
695
+ form.enquiryOwnerId === null
696
+ ? await workspaceRepository.findDefaultMember(tx, workspaceId)
697
+ : { id: form.enquiryOwnerId }
698
+
699
+ await enquiryRepository.insertEnquiry(tx, {
700
+ id,
701
+ workspaceId,
702
+ name:
703
+ intent.enquiryName ??
704
+ expandNameTemplate(form.enquiryNameTemplate ?? DEFAULT_DEAL_NAME_TEMPLATE, {
705
+ companyName: company?.name ?? '',
706
+ personName: intent.personName,
707
+ }),
708
+ source: form.enquirySource ?? '',
709
+ stageId: await openingStageId(tx, workspaceId, 'enquiry', form.enquiryStageId),
710
+ companyId: company?.id ?? null,
711
+ ownerId: owner?.id ?? null,
712
+ convertedDealId: null,
713
+ })
714
+
715
+ await personLinks.linkPeople(
716
+ tx,
717
+ dependencies.createId,
718
+ workspaceId,
719
+ { targetType: 'enquiry', targetId: id },
720
+ [personId],
721
+ )
722
+
723
+ await dependencies.recordActivity(tx, workspaceId, FORM_ACTOR, {
724
+ targetType: 'enquiry',
725
+ targetId: id,
726
+ kind: 'created',
727
+ ...describeCreationVia('Enquiry', form.name),
728
+ })
729
+
730
+ emit('enquiries.enquiry.created', { type: 'enquiry', id }, {})
435
731
 
436
732
  return id
437
733
  }
@@ -481,14 +777,86 @@ export function createFormSubmitService(dependencies: SubmissionDependencies): F
481
777
  return intent
482
778
  }
483
779
 
780
+ /**
781
+ * The purposes any grants name, keyed by id, so the submit transaction
782
+ * knows the slug and label without a per-grant round trip.
783
+ */
784
+ async function loadPurposesForGrants(
785
+ db: Database,
786
+ workspaceId: string,
787
+ grants: readonly ConsentGrant[],
788
+ ): Promise<ReadonlyMap<string, { readonly slug: string; readonly label: string }>> {
789
+ if (grants.length === 0) return new Map()
790
+ const ids = Array.from(new Set(grants.map((grant) => grant.purposeId)))
791
+ const rows = await consentPurposesRepository.listPurposesByIds(db, workspaceId, ids)
792
+ return new Map(rows.map((row) => [row.id, { slug: row.slug, label: row.label }]))
793
+ }
794
+
795
+ /**
796
+ * Writes each ticked grant as a `person_consents` row and appends an
797
+ * Activity to the person carrying the verbatim statement. A grant whose
798
+ * purpose has since been removed is silently skipped — the form's own
799
+ * consent field would have been left dangling anyway, and the submit
800
+ * cannot know that from the answer alone.
801
+ */
802
+ async function applyFormConsentGrants(
803
+ tx: Transaction,
804
+ workspaceId: string,
805
+ formId: string,
806
+ formName: string,
807
+ personId: string,
808
+ grants: readonly ConsentGrant[],
809
+ purposes: ReadonlyMap<string, { readonly slug: string; readonly label: string }>,
810
+ deps: SubmissionDependencies,
811
+ ): Promise<void> {
812
+ if (grants.length === 0) return
813
+ const now = deps.now()
814
+ const source = `form:${formId}`
815
+ for (const grant of grants) {
816
+ const purpose = purposes.get(grant.purposeId)
817
+ if (purpose === undefined) continue
818
+ await upsertPersonConsent(
819
+ tx,
820
+ workspaceId,
821
+ personId,
822
+ grant.purposeId,
823
+ purpose.slug,
824
+ purpose.label,
825
+ 'granted',
826
+ source,
827
+ now,
828
+ )
829
+ // The exact text the visitor ticked — the field's per-purpose override
830
+ // when set, else the workspace purpose's label. Recorded as the
831
+ // Activity detail so the timeline shows what they actually saw.
832
+ const ticked = grant.customLabel.length > 0 ? grant.customLabel : purpose.label
833
+ const detail = grant.statement.length > 0 ? `${grant.statement} — ${ticked}` : ticked
834
+ await deps.recordActivity(tx, workspaceId, FORM_ACTOR, {
835
+ targetType: 'person',
836
+ targetId: personId,
837
+ kind: 'updated',
838
+ action: `granted ${purpose.label} consent via ${formName}`,
839
+ detail,
840
+ })
841
+ }
842
+ }
843
+
484
844
  return {
485
845
  async submit(publicKey, answers) {
486
846
  const form = await requireOpenForm(publicKey)
487
847
  const { workspaceId } = form
488
848
  const fields = await repository.listFields(dependencies.db, form.id)
489
849
  const intent = readAnswers(fields, answers)
850
+ const consentGrants = readConsentGrants(fields, answers)
851
+ const [formListRows, attachTargets, consentPurposesForGrants] = await Promise.all([
852
+ repository.listFormLists(dependencies.db, form.id),
853
+ repository.listAttachTargets(dependencies.db, form.id),
854
+ loadPurposesForGrants(dependencies.db, workspaceId, consentGrants),
855
+ ])
490
856
 
491
857
  return dependencies.transaction(async ({ tx, events }) => {
858
+ // Core capture: atomic. A failure here fails the submit; every
859
+ // post-action below runs under its own savepoint and logs.
492
860
  const person = await upsertPerson(tx, workspaceId, intent)
493
861
  const company = await upsertCompany(tx, workspaceId, intent)
494
862
  const position =
@@ -501,10 +869,310 @@ export function createFormSubmitService(dependencies: SubmissionDependencies): F
501
869
  company.record.id,
502
870
  intent.positionTitle,
503
871
  )
504
- const dealId =
505
- form.createDeal && company !== undefined
506
- ? await createDeal(tx, workspaceId, form, intent, company.record, person.record.id)
507
- : null
872
+
873
+ // Consent grants (from ticked `consent` fields) are core capture, not
874
+ // post-actions: they commit with the person, and an Activity records
875
+ // the exact statement text as it was shown to the visitor.
876
+ await applyFormConsentGrants(
877
+ tx,
878
+ workspaceId,
879
+ form.id,
880
+ form.name,
881
+ person.record.id,
882
+ consentGrants,
883
+ consentPurposesForGrants,
884
+ dependencies,
885
+ )
886
+
887
+ const actionLog: FormSubmissionActionEntry[] = []
888
+
889
+ // --- Create triggers ---
890
+
891
+ const dealId = form.createDeal
892
+ ? ((await runAction<string>(tx, events, actionLog, 'create_deal', async (inner, emit) => {
893
+ if (company === undefined) {
894
+ return { status: 'skipped', detail: 'no company resolved' }
895
+ }
896
+ const id = await createDeal(
897
+ inner,
898
+ emit,
899
+ workspaceId,
900
+ form,
901
+ intent,
902
+ company.record,
903
+ person.record.id,
904
+ )
905
+
906
+ return { status: 'ok', detail: id, value: id }
907
+ })) ?? null)
908
+ : null
909
+
910
+ const opportunityId = form.createOpportunity
911
+ ? ((await runAction<string>(
912
+ tx,
913
+ events,
914
+ actionLog,
915
+ 'create_opportunity',
916
+ async (inner, emit) => {
917
+ const id = await createOpportunity(
918
+ inner,
919
+ emit,
920
+ workspaceId,
921
+ form,
922
+ intent,
923
+ company?.record,
924
+ person.record.id,
925
+ )
926
+
927
+ return { status: 'ok', detail: id, value: id }
928
+ },
929
+ )) ?? null)
930
+ : null
931
+
932
+ const partnershipId = form.createPartnership
933
+ ? ((await runAction<string>(
934
+ tx,
935
+ events,
936
+ actionLog,
937
+ 'create_partnership',
938
+ async (inner, emit) => {
939
+ if (company === undefined) {
940
+ return { status: 'skipped', detail: 'no company resolved' }
941
+ }
942
+ const id = await createPartnership(
943
+ inner,
944
+ emit,
945
+ workspaceId,
946
+ form,
947
+ intent,
948
+ company.record,
949
+ person.record.id,
950
+ )
951
+
952
+ return { status: 'ok', detail: id, value: id }
953
+ },
954
+ )) ?? null)
955
+ : null
956
+
957
+ const enquiryId = form.createEnquiry
958
+ ? ((await runAction<string>(
959
+ tx,
960
+ events,
961
+ actionLog,
962
+ 'create_enquiry',
963
+ async (inner, emit) => {
964
+ const id = await createEnquiry(
965
+ inner,
966
+ emit,
967
+ workspaceId,
968
+ form,
969
+ intent,
970
+ company?.record,
971
+ person.record.id,
972
+ )
973
+
974
+ return { status: 'ok', detail: id, value: id }
975
+ },
976
+ )) ?? null)
977
+ : null
978
+
979
+ // --- Tag merges ---
980
+
981
+ if (form.personTags.length > 0) {
982
+ await runAction<void>(tx, events, actionLog, 'tag_person', async (inner, emit) => {
983
+ const merged = mergeTags(person.record.tags, form.personTags)
984
+
985
+ if (!merged.changed) {
986
+ return { status: 'ok', detail: 'no new tags' }
987
+ }
988
+
989
+ const now = dependencies.now()
990
+ await peopleRepository.updatePerson(inner, workspaceId, person.record.id, {
991
+ tags: [...merged.next],
992
+ updatedAt: now,
993
+ })
994
+ emit(
995
+ 'people.person.updated',
996
+ { type: 'person', id: person.record.id },
997
+ { changed: ['tags'] },
998
+ )
999
+
1000
+ return { status: 'ok', detail: `merged ${String(merged.next.length - person.record.tags.length)}` }
1001
+ })
1002
+ }
1003
+
1004
+ if (form.companyTags.length > 0) {
1005
+ await runAction<void>(tx, events, actionLog, 'tag_company', async (inner, emit) => {
1006
+ if (company === undefined) {
1007
+ return { status: 'skipped', detail: 'no company resolved' }
1008
+ }
1009
+
1010
+ const merged = mergeTags(company.record.tags, form.companyTags)
1011
+
1012
+ if (!merged.changed) {
1013
+ return { status: 'ok', detail: 'no new tags' }
1014
+ }
1015
+
1016
+ const now = dependencies.now()
1017
+ await companyRepository.updateCompany(inner, workspaceId, company.record.id, {
1018
+ tags: [...merged.next],
1019
+ updatedAt: now,
1020
+ })
1021
+ emit(
1022
+ 'companies.company.updated',
1023
+ { type: 'company', id: company.record.id },
1024
+ { changed: ['tags'] },
1025
+ )
1026
+
1027
+ return { status: 'ok', detail: `merged ${String(merged.next.length - company.record.tags.length)}` }
1028
+ })
1029
+ }
1030
+
1031
+ // --- List memberships ---
1032
+
1033
+ // Every purpose named by a person list on this form, so consent
1034
+ // capture below needs no per-row round trip.
1035
+ const listConsentPurposeIds = Array.from(
1036
+ new Set(
1037
+ formListRows
1038
+ .map((row) => row.consentPurposeId)
1039
+ .filter((id): id is string => id !== null),
1040
+ ),
1041
+ )
1042
+ const listConsentPurposes =
1043
+ listConsentPurposeIds.length === 0
1044
+ ? new Map<string, { readonly slug: string; readonly label: string }>()
1045
+ : new Map(
1046
+ (
1047
+ await consentPurposesRepository.listPurposesByIds(
1048
+ tx,
1049
+ workspaceId,
1050
+ listConsentPurposeIds,
1051
+ )
1052
+ ).map((row) => [row.id, { slug: row.slug, label: row.label }]),
1053
+ )
1054
+
1055
+ for (const row of formListRows) {
1056
+ const inserted = await runAction<boolean>(
1057
+ tx,
1058
+ events,
1059
+ actionLog,
1060
+ `add_list:${row.listId}`,
1061
+ async (inner, emit) => {
1062
+ const targetId =
1063
+ row.targetType === 'person'
1064
+ ? person.record.id
1065
+ : row.targetType === 'company' && company !== undefined
1066
+ ? company.record.id
1067
+ : undefined
1068
+
1069
+ if (targetId === undefined) {
1070
+ return { status: 'skipped', detail: 'no company resolved', value: false }
1071
+ }
1072
+
1073
+ try {
1074
+ await listsRepository.insertListMember(inner, {
1075
+ id: dependencies.createId('listMember'),
1076
+ workspaceId,
1077
+ listId: row.listId,
1078
+ targetType: row.targetType,
1079
+ targetId,
1080
+ addedAt: dependencies.now(),
1081
+ })
1082
+ } catch (error: unknown) {
1083
+ if (postgresErrorCode(error) === UNIQUE_VIOLATION) {
1084
+ return { status: 'ok', detail: 'already a member', value: false }
1085
+ }
1086
+
1087
+ throw error
1088
+ }
1089
+
1090
+ emit(
1091
+ 'lists.member.added',
1092
+ { type: row.targetType, id: targetId },
1093
+ { listId: row.listId },
1094
+ )
1095
+
1096
+ return { status: 'ok', detail: targetId, value: true }
1097
+ },
1098
+ )
1099
+
1100
+ // Consent capture rides with the add — only on person lists, only
1101
+ // when the list carries a purpose, and only when we actually added
1102
+ // (or the person was already on it, since a returning submitter
1103
+ // did the same thing again). A record already on the list has
1104
+ // already been consented for its purpose; the upsert reflects
1105
+ // that a fresh submit is fresh evidence.
1106
+ if (
1107
+ row.targetType === 'person' &&
1108
+ row.consentPurposeId !== null &&
1109
+ inserted !== null
1110
+ ) {
1111
+ const purpose = listConsentPurposes.get(row.consentPurposeId)
1112
+ if (purpose !== undefined) {
1113
+ await runAction<void>(
1114
+ tx,
1115
+ events,
1116
+ actionLog,
1117
+ `consent:${row.listId}`,
1118
+ async (inner) => {
1119
+ await upsertPersonConsent(
1120
+ inner,
1121
+ workspaceId,
1122
+ person.record.id,
1123
+ row.consentPurposeId as string,
1124
+ purpose.slug,
1125
+ purpose.label,
1126
+ 'granted',
1127
+ `list:${row.listId}`,
1128
+ dependencies.now(),
1129
+ )
1130
+ await dependencies.recordActivity(inner, workspaceId, FORM_ACTOR, {
1131
+ targetType: 'person',
1132
+ targetId: person.record.id,
1133
+ kind: 'updated',
1134
+ action: `granted ${purpose.label} consent via ${form.name}`,
1135
+ detail: `Added to list membership`,
1136
+ })
1137
+ return { status: 'ok', detail: purpose.slug }
1138
+ },
1139
+ )
1140
+ }
1141
+ }
1142
+ }
1143
+
1144
+ // --- Attach the submitter to pre-existing pipeline records ---
1145
+
1146
+ for (const target of attachTargets) {
1147
+ await runAction<void>(
1148
+ tx,
1149
+ events,
1150
+ actionLog,
1151
+ `attach:${target.targetType}:${target.targetId}`,
1152
+ async (inner) => {
1153
+ // Racing a target delete would fail the FK-less insert with the
1154
+ // same effect as an existence check: log an error, continue.
1155
+ if (!(await targetExists(inner, workspaceId, target.targetType, target.targetId))) {
1156
+ throw AppError.notFound(`${target.targetType} ${target.targetId} not found`)
1157
+ }
1158
+
1159
+ const inserted = await personLinks.linkPersonIfAbsent(
1160
+ inner,
1161
+ dependencies.createId,
1162
+ workspaceId,
1163
+ target,
1164
+ person.record.id,
1165
+ )
1166
+
1167
+ return {
1168
+ status: 'ok',
1169
+ detail: inserted ? 'linked' : 'already linked',
1170
+ }
1171
+ },
1172
+ )
1173
+ }
1174
+
1175
+ // --- Persist submission + core activity + record events ---
508
1176
 
509
1177
  const submission = await repository.insertSubmission(tx, {
510
1178
  id: dependencies.createId('formSubmission'),
@@ -515,7 +1183,11 @@ export function createFormSubmitService(dependencies: SubmissionDependencies): F
515
1183
  companyId: company?.record.id ?? null,
516
1184
  positionId: position?.record.id ?? null,
517
1185
  dealId,
1186
+ opportunityId,
1187
+ partnershipId,
1188
+ enquiryId,
518
1189
  submittedAt: dependencies.now(),
1190
+ actionLog,
519
1191
  })
520
1192
 
521
1193
  await dependencies.recordActivity(tx, workspaceId, FORM_ACTOR, {
@@ -525,20 +1197,18 @@ export function createFormSubmitService(dependencies: SubmissionDependencies): F
525
1197
  ...describeFormSubmission(form.name, describeAnswers(fields, answers)),
526
1198
  })
527
1199
 
528
- if (dealId !== null) {
529
- await dependencies.recordActivity(tx, workspaceId, FORM_ACTOR, {
530
- targetType: 'deal',
531
- targetId: dealId,
532
- kind: 'created',
533
- ...describeCreationVia('Deal', form.name),
534
- })
535
- }
536
-
537
- emitRecordEvents(events, workspaceId, { person, company, position, dealId })
1200
+ emitRecordEvents(events, workspaceId, { person, company, position })
538
1201
  events.emit(
539
1202
  'forms.submission.submitted',
540
1203
  { type: 'submission', id: submission.id },
541
- { formId: form.id, submissionId: submission.id },
1204
+ {
1205
+ formId: form.id,
1206
+ submissionId: submission.id,
1207
+ opportunityId,
1208
+ partnershipId,
1209
+ enquiryId,
1210
+ actions: actionLog.map((entry) => ({ action: entry.action, status: entry.status })),
1211
+ },
542
1212
  )
543
1213
 
544
1214
  return {
@@ -548,8 +1218,12 @@ export function createFormSubmitService(dependencies: SubmissionDependencies): F
548
1218
  companyId: company?.record.id ?? null,
549
1219
  positionId: position?.record.id ?? null,
550
1220
  dealId,
1221
+ opportunityId,
1222
+ partnershipId,
1223
+ enquiryId,
551
1224
  submittedAt: submission.submittedAt,
552
1225
  thankYouMessage: form.thankYouMessage,
1226
+ actionLog,
553
1227
  }
554
1228
  }, { workspaceId })
555
1229
  },