@kelpie/server 0.12.0 → 0.13.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 (383) hide show
  1. package/dist/app.d.ts +13 -0
  2. package/dist/app.d.ts.map +1 -1
  3. package/dist/app.js +8 -1
  4. package/dist/app.js.map +1 -1
  5. package/dist/boot.d.ts.map +1 -1
  6. package/dist/boot.js +1 -0
  7. package/dist/boot.js.map +1 -1
  8. package/dist/lib/apiKeyScopes.d.ts.map +1 -1
  9. package/dist/lib/apiKeyScopes.js +6 -0
  10. package/dist/lib/apiKeyScopes.js.map +1 -1
  11. package/dist/lib/columns.d.ts +6 -0
  12. package/dist/lib/columns.d.ts.map +1 -1
  13. package/dist/lib/columns.js +5 -3
  14. package/dist/lib/columns.js.map +1 -1
  15. package/dist/lib/config.d.ts +17 -0
  16. package/dist/lib/config.d.ts.map +1 -1
  17. package/dist/lib/config.js +3 -0
  18. package/dist/lib/config.js.map +1 -1
  19. package/dist/lib/egress.d.ts.map +1 -1
  20. package/dist/lib/egress.js +8 -4
  21. package/dist/lib/egress.js.map +1 -1
  22. package/dist/lib/errors.d.ts +3 -3
  23. package/dist/lib/errors.d.ts.map +1 -1
  24. package/dist/lib/errors.js +13 -5
  25. package/dist/lib/errors.js.map +1 -1
  26. package/dist/lib/ids.d.ts +11 -0
  27. package/dist/lib/ids.d.ts.map +1 -1
  28. package/dist/lib/ids.js +11 -0
  29. package/dist/lib/ids.js.map +1 -1
  30. package/dist/lib/kelpieConfigFile.d.ts +15 -1
  31. package/dist/lib/kelpieConfigFile.d.ts.map +1 -1
  32. package/dist/lib/kelpieConfigFile.js +33 -1
  33. package/dist/lib/kelpieConfigFile.js.map +1 -1
  34. package/dist/modules/agent-tasks/catalog.d.ts.map +1 -1
  35. package/dist/modules/agent-tasks/catalog.js +32 -1
  36. package/dist/modules/agent-tasks/catalog.js.map +1 -1
  37. package/dist/modules/agent-tasks/index.d.ts.map +1 -1
  38. package/dist/modules/agent-tasks/index.js +1 -0
  39. package/dist/modules/agent-tasks/index.js.map +1 -1
  40. package/dist/modules/agent-tasks/resolve.d.ts.map +1 -1
  41. package/dist/modules/agent-tasks/resolve.js +5 -0
  42. package/dist/modules/agent-tasks/resolve.js.map +1 -1
  43. package/dist/modules/agent-tasks/routes.d.ts +2 -0
  44. package/dist/modules/agent-tasks/routes.d.ts.map +1 -1
  45. package/dist/modules/attachedRecords.d.ts +22 -8
  46. package/dist/modules/attachedRecords.d.ts.map +1 -1
  47. package/dist/modules/attachedRecords.js +42 -5
  48. package/dist/modules/attachedRecords.js.map +1 -1
  49. package/dist/modules/auth/index.d.ts.map +1 -1
  50. package/dist/modules/auth/index.js +1 -0
  51. package/dist/modules/auth/index.js.map +1 -1
  52. package/dist/modules/auth/repository.d.ts +14 -0
  53. package/dist/modules/auth/repository.d.ts.map +1 -1
  54. package/dist/modules/auth/repository.js +20 -2
  55. package/dist/modules/auth/repository.js.map +1 -1
  56. package/dist/modules/auth/routes.d.ts.map +1 -1
  57. package/dist/modules/auth/routes.js +30 -0
  58. package/dist/modules/auth/routes.js.map +1 -1
  59. package/dist/modules/auth/service.d.ts +28 -0
  60. package/dist/modules/auth/service.d.ts.map +1 -1
  61. package/dist/modules/auth/service.js +72 -4
  62. package/dist/modules/auth/service.js.map +1 -1
  63. package/dist/modules/companies/repository.d.ts.map +1 -1
  64. package/dist/modules/companies/repository.js +1 -1
  65. package/dist/modules/companies/repository.js.map +1 -1
  66. package/dist/modules/companies/routes.d.ts +2 -4
  67. package/dist/modules/companies/routes.d.ts.map +1 -1
  68. package/dist/modules/companies/routes.js +6 -11
  69. package/dist/modules/companies/routes.js.map +1 -1
  70. package/dist/modules/companies/schema.d.ts +13 -28
  71. package/dist/modules/companies/schema.d.ts.map +1 -1
  72. package/dist/modules/companies/schema.js +2 -2
  73. package/dist/modules/companies/schema.js.map +1 -1
  74. package/dist/modules/companies/service.d.ts +2 -3
  75. package/dist/modules/companies/service.d.ts.map +1 -1
  76. package/dist/modules/companies/service.js +4 -7
  77. package/dist/modules/companies/service.js.map +1 -1
  78. package/dist/modules/core.d.ts.map +1 -1
  79. package/dist/modules/core.js +2 -0
  80. package/dist/modules/core.js.map +1 -1
  81. package/dist/modules/custom-fields/repository.d.ts.map +1 -1
  82. package/dist/modules/custom-fields/repository.js +2 -0
  83. package/dist/modules/custom-fields/repository.js.map +1 -1
  84. package/dist/modules/custom-fields/routes.d.ts +1 -0
  85. package/dist/modules/custom-fields/routes.d.ts.map +1 -1
  86. package/dist/modules/dashboard/index.d.ts.map +1 -1
  87. package/dist/modules/dashboard/index.js +1 -0
  88. package/dist/modules/dashboard/index.js.map +1 -1
  89. package/dist/modules/dashboard/repository.d.ts +13 -0
  90. package/dist/modules/dashboard/repository.d.ts.map +1 -1
  91. package/dist/modules/dashboard/repository.js +36 -0
  92. package/dist/modules/dashboard/repository.js.map +1 -1
  93. package/dist/modules/dashboard/routes.d.ts.map +1 -1
  94. package/dist/modules/dashboard/routes.js +13 -0
  95. package/dist/modules/dashboard/routes.js.map +1 -1
  96. package/dist/modules/dashboard/service.d.ts +11 -0
  97. package/dist/modules/dashboard/service.d.ts.map +1 -1
  98. package/dist/modules/dashboard/service.js +24 -1
  99. package/dist/modules/dashboard/service.js.map +1 -1
  100. package/dist/modules/decisions/repository.d.ts.map +1 -1
  101. package/dist/modules/decisions/repository.js +2 -0
  102. package/dist/modules/decisions/repository.js.map +1 -1
  103. package/dist/modules/decisions/routes.d.ts +2 -0
  104. package/dist/modules/decisions/routes.d.ts.map +1 -1
  105. package/dist/modules/events/attendances.d.ts +35 -0
  106. package/dist/modules/events/attendances.d.ts.map +1 -0
  107. package/dist/modules/events/attendances.js +157 -0
  108. package/dist/modules/events/attendances.js.map +1 -0
  109. package/dist/modules/events/catalog.d.ts +35 -0
  110. package/dist/modules/events/catalog.d.ts.map +1 -0
  111. package/dist/modules/events/catalog.js +11 -0
  112. package/dist/modules/events/catalog.js.map +1 -0
  113. package/dist/modules/events/index.d.ts +10 -0
  114. package/dist/modules/events/index.d.ts.map +1 -0
  115. package/dist/modules/events/index.js +47 -0
  116. package/dist/modules/events/index.js.map +1 -0
  117. package/dist/modules/events/repository.d.ts +67 -0
  118. package/dist/modules/events/repository.d.ts.map +1 -0
  119. package/dist/modules/events/repository.js +212 -0
  120. package/dist/modules/events/repository.js.map +1 -0
  121. package/dist/modules/events/routes.d.ts +136 -0
  122. package/dist/modules/events/routes.d.ts.map +1 -0
  123. package/dist/modules/events/routes.js +280 -0
  124. package/dist/modules/events/routes.js.map +1 -0
  125. package/dist/modules/events/schema.d.ts +626 -0
  126. package/dist/modules/events/schema.d.ts.map +1 -0
  127. package/dist/modules/events/schema.js +93 -0
  128. package/dist/modules/events/schema.js.map +1 -0
  129. package/dist/modules/events/service.d.ts +68 -0
  130. package/dist/modules/events/service.d.ts.map +1 -0
  131. package/dist/modules/events/service.js +262 -0
  132. package/dist/modules/events/service.js.map +1 -0
  133. package/dist/modules/events/tools.d.ts +8 -0
  134. package/dist/modules/events/tools.d.ts.map +1 -0
  135. package/dist/modules/events/tools.js +80 -0
  136. package/dist/modules/events/tools.js.map +1 -0
  137. package/dist/modules/forms/applySubmissionFields.d.ts.map +1 -1
  138. package/dist/modules/forms/applySubmissionFields.js +7 -5
  139. package/dist/modules/forms/applySubmissionFields.js.map +1 -1
  140. package/dist/modules/forms/fillAddresses.d.ts +4 -0
  141. package/dist/modules/forms/fillAddresses.d.ts.map +1 -0
  142. package/dist/modules/forms/fillAddresses.js +93 -0
  143. package/dist/modules/forms/fillAddresses.js.map +1 -0
  144. package/dist/modules/forms/repository.d.ts +2 -2
  145. package/dist/modules/forms/repository.d.ts.map +1 -1
  146. package/dist/modules/forms/repository.js.map +1 -1
  147. package/dist/modules/forms/routes.d.ts +2 -0
  148. package/dist/modules/forms/routes.d.ts.map +1 -1
  149. package/dist/modules/forms/routes.js +2 -2
  150. package/dist/modules/forms/routes.js.map +1 -1
  151. package/dist/modules/forms/schema.js +2 -2
  152. package/dist/modules/forms/schema.js.map +1 -1
  153. package/dist/modules/forms/service.js +2 -2
  154. package/dist/modules/forms/service.js.map +1 -1
  155. package/dist/modules/forms/submission.d.ts +1 -0
  156. package/dist/modules/forms/submission.d.ts.map +1 -1
  157. package/dist/modules/forms/submission.js +27 -4
  158. package/dist/modules/forms/submission.js.map +1 -1
  159. package/dist/modules/hiring/roles.d.ts.map +1 -1
  160. package/dist/modules/hiring/roles.js +2 -1
  161. package/dist/modules/hiring/roles.js.map +1 -1
  162. package/dist/modules/import-export/addresses.d.ts +22 -0
  163. package/dist/modules/import-export/addresses.d.ts.map +1 -0
  164. package/dist/modules/import-export/addresses.js +83 -0
  165. package/dist/modules/import-export/addresses.js.map +1 -0
  166. package/dist/modules/import-export/drafts.d.ts +3 -4
  167. package/dist/modules/import-export/drafts.d.ts.map +1 -1
  168. package/dist/modules/import-export/drafts.js +4 -5
  169. package/dist/modules/import-export/drafts.js.map +1 -1
  170. package/dist/modules/import-export/exportRows.d.ts.map +1 -1
  171. package/dist/modules/import-export/exportRows.js +3 -3
  172. package/dist/modules/import-export/exportRows.js.map +1 -1
  173. package/dist/modules/import-export/plan.d.ts.map +1 -1
  174. package/dist/modules/import-export/plan.js +17 -2
  175. package/dist/modules/import-export/plan.js.map +1 -1
  176. package/dist/modules/import-export/presets.d.ts.map +1 -1
  177. package/dist/modules/import-export/presets.js +52 -7
  178. package/dist/modules/import-export/presets.js.map +1 -1
  179. package/dist/modules/import-export/repository.d.ts +3 -4
  180. package/dist/modules/import-export/repository.d.ts.map +1 -1
  181. package/dist/modules/import-export/repository.js +2 -3
  182. package/dist/modules/import-export/repository.js.map +1 -1
  183. package/dist/modules/import-export/writes.d.ts.map +1 -1
  184. package/dist/modules/import-export/writes.js +8 -4
  185. package/dist/modules/import-export/writes.js.map +1 -1
  186. package/dist/modules/lists/routes.d.ts +4 -0
  187. package/dist/modules/lists/routes.d.ts.map +1 -1
  188. package/dist/modules/notes/routes.d.ts +2 -0
  189. package/dist/modules/notes/routes.d.ts.map +1 -1
  190. package/dist/modules/people/repository.d.ts.map +1 -1
  191. package/dist/modules/people/repository.js +1 -1
  192. package/dist/modules/people/repository.js.map +1 -1
  193. package/dist/modules/people/routes.d.ts +2 -2
  194. package/dist/modules/people/routes.js +6 -6
  195. package/dist/modules/people/routes.js.map +1 -1
  196. package/dist/modules/people/schema.d.ts +17 -14
  197. package/dist/modules/people/schema.d.ts.map +1 -1
  198. package/dist/modules/people/schema.js +6 -4
  199. package/dist/modules/people/schema.js.map +1 -1
  200. package/dist/modules/people/service.d.ts +2 -2
  201. package/dist/modules/people/service.d.ts.map +1 -1
  202. package/dist/modules/people/service.js +5 -4
  203. package/dist/modules/people/service.js.map +1 -1
  204. package/dist/modules/plans/repository.d.ts +2 -2
  205. package/dist/modules/plans/repository.d.ts.map +1 -1
  206. package/dist/modules/plans/routes.d.ts +1 -0
  207. package/dist/modules/plans/routes.d.ts.map +1 -1
  208. package/dist/modules/plans/routes.js +4 -4
  209. package/dist/modules/plans/routes.js.map +1 -1
  210. package/dist/modules/plans/schema.d.ts +2 -2
  211. package/dist/modules/plans/schema.d.ts.map +1 -1
  212. package/dist/modules/plans/schema.js +3 -3
  213. package/dist/modules/plans/schema.js.map +1 -1
  214. package/dist/modules/plans/service.d.ts +2 -2
  215. package/dist/modules/plans/service.d.ts.map +1 -1
  216. package/dist/modules/plans/service.js.map +1 -1
  217. package/dist/modules/plans/tools.js +2 -2
  218. package/dist/modules/plans/tools.js.map +1 -1
  219. package/dist/modules/recordTargets.d.ts.map +1 -1
  220. package/dist/modules/recordTargets.js +11 -0
  221. package/dist/modules/recordTargets.js.map +1 -1
  222. package/dist/modules/references.d.ts.map +1 -1
  223. package/dist/modules/references.js +2 -0
  224. package/dist/modules/references.js.map +1 -1
  225. package/dist/modules/sample-data/fixture.d.ts +26 -4
  226. package/dist/modules/sample-data/fixture.d.ts.map +1 -1
  227. package/dist/modules/sample-data/fixture.js +157 -23
  228. package/dist/modules/sample-data/fixture.js.map +1 -1
  229. package/dist/modules/sample-data/index.d.ts +5 -2
  230. package/dist/modules/sample-data/index.d.ts.map +1 -1
  231. package/dist/modules/sample-data/index.js +7 -2
  232. package/dist/modules/sample-data/index.js.map +1 -1
  233. package/dist/modules/sample-data/routes.d.ts.map +1 -1
  234. package/dist/modules/sample-data/routes.js +2 -0
  235. package/dist/modules/sample-data/routes.js.map +1 -1
  236. package/dist/modules/sample-data/service.d.ts +11 -3
  237. package/dist/modules/sample-data/service.d.ts.map +1 -1
  238. package/dist/modules/sample-data/service.js +452 -363
  239. package/dist/modules/sample-data/service.js.map +1 -1
  240. package/dist/modules/sample-data/tools.d.ts.map +1 -1
  241. package/dist/modules/sample-data/tools.js +4 -3
  242. package/dist/modules/sample-data/tools.js.map +1 -1
  243. package/dist/modules/search/index.d.ts +1 -1
  244. package/dist/modules/search/index.d.ts.map +1 -1
  245. package/dist/modules/search/index.js +2 -1
  246. package/dist/modules/search/index.js.map +1 -1
  247. package/dist/modules/search/repository.d.ts +1 -0
  248. package/dist/modules/search/repository.d.ts.map +1 -1
  249. package/dist/modules/search/repository.js +22 -0
  250. package/dist/modules/search/repository.js.map +1 -1
  251. package/dist/modules/search/service.d.ts +2 -2
  252. package/dist/modules/search/service.d.ts.map +1 -1
  253. package/dist/modules/search/service.js +4 -3
  254. package/dist/modules/search/service.js.map +1 -1
  255. package/dist/modules/smtp-email/index.d.ts +12 -4
  256. package/dist/modules/smtp-email/index.d.ts.map +1 -1
  257. package/dist/modules/smtp-email/index.js +25 -5
  258. package/dist/modules/smtp-email/index.js.map +1 -1
  259. package/dist/modules/workspace/repository.d.ts +2 -0
  260. package/dist/modules/workspace/repository.d.ts.map +1 -1
  261. package/dist/modules/workspace/repository.js +12 -0
  262. package/dist/modules/workspace/repository.js.map +1 -1
  263. package/dist/modules/workspace/routes.d.ts.map +1 -1
  264. package/dist/modules/workspace/routes.js +13 -0
  265. package/dist/modules/workspace/routes.js.map +1 -1
  266. package/dist/modules/workspace/service.d.ts +10 -0
  267. package/dist/modules/workspace/service.d.ts.map +1 -1
  268. package/dist/modules/workspace/service.js +34 -10
  269. package/dist/modules/workspace/service.js.map +1 -1
  270. package/dist/modules/workspace/starters.d.ts +17 -2
  271. package/dist/modules/workspace/starters.d.ts.map +1 -1
  272. package/dist/modules/workspace/starters.js +141 -14
  273. package/dist/modules/workspace/starters.js.map +1 -1
  274. package/dist/modules/workspace/workspaceAccessMiddleware.d.ts.map +1 -1
  275. package/dist/modules/workspace/workspaceAccessMiddleware.js +3 -4
  276. package/dist/modules/workspace/workspaceAccessMiddleware.js.map +1 -1
  277. package/dist/runtime/events.d.ts +1 -1
  278. package/dist/runtime/events.d.ts.map +1 -1
  279. package/dist/runtime/events.js +2 -0
  280. package/dist/runtime/events.js.map +1 -1
  281. package/dist/runtime/module.d.ts +8 -0
  282. package/dist/runtime/module.d.ts.map +1 -1
  283. package/dist/runtime/registry.d.ts.map +1 -1
  284. package/dist/runtime/registry.js +102 -7
  285. package/dist/runtime/registry.js.map +1 -1
  286. package/dist/schema/index.d.ts +1 -0
  287. package/dist/schema/index.d.ts.map +1 -1
  288. package/dist/schema/index.js +1 -0
  289. package/dist/schema/index.js.map +1 -1
  290. package/dist/testing/app.d.ts +8 -0
  291. package/dist/testing/app.d.ts.map +1 -1
  292. package/dist/testing/app.js +6 -1
  293. package/dist/testing/app.js.map +1 -1
  294. package/dist/testing/client.d.ts.map +1 -1
  295. package/dist/testing/client.js +9 -1
  296. package/dist/testing/client.js.map +1 -1
  297. package/migrations/0044_events.sql +79 -0
  298. package/migrations/0045_addresses.sql +66 -0
  299. package/migrations/0046_drop_company_website.sql +66 -0
  300. package/migrations/meta/_journal.json +21 -0
  301. package/package.json +3 -3
  302. package/src/app.ts +25 -1
  303. package/src/boot.ts +1 -0
  304. package/src/lib/apiKeyScopes.ts +6 -0
  305. package/src/lib/columns.ts +12 -3
  306. package/src/lib/config.ts +21 -0
  307. package/src/lib/egress.ts +8 -4
  308. package/src/lib/errors.ts +16 -9
  309. package/src/lib/ids.ts +11 -0
  310. package/src/lib/kelpieConfigFile.ts +69 -1
  311. package/src/modules/agent-tasks/catalog.ts +33 -1
  312. package/src/modules/agent-tasks/index.ts +1 -0
  313. package/src/modules/agent-tasks/resolve.ts +11 -0
  314. package/src/modules/attachedRecords.ts +85 -13
  315. package/src/modules/auth/index.ts +1 -0
  316. package/src/modules/auth/repository.ts +34 -3
  317. package/src/modules/auth/routes.ts +44 -1
  318. package/src/modules/auth/service.ts +118 -4
  319. package/src/modules/companies/repository.ts +1 -0
  320. package/src/modules/companies/routes.ts +6 -11
  321. package/src/modules/companies/schema.ts +3 -3
  322. package/src/modules/companies/service.ts +6 -10
  323. package/src/modules/core.ts +2 -0
  324. package/src/modules/custom-fields/repository.ts +2 -0
  325. package/src/modules/dashboard/index.ts +1 -0
  326. package/src/modules/dashboard/repository.ts +64 -0
  327. package/src/modules/dashboard/routes.ts +15 -0
  328. package/src/modules/dashboard/service.ts +48 -0
  329. package/src/modules/decisions/repository.ts +2 -0
  330. package/src/modules/events/attendances.ts +245 -0
  331. package/src/modules/events/catalog.ts +36 -0
  332. package/src/modules/events/index.ts +54 -0
  333. package/src/modules/events/repository.ts +396 -0
  334. package/src/modules/events/routes.ts +405 -0
  335. package/src/modules/events/schema.ts +148 -0
  336. package/src/modules/events/service.ts +443 -0
  337. package/src/modules/events/tools.ts +118 -0
  338. package/src/modules/forms/applySubmissionFields.ts +7 -5
  339. package/src/modules/forms/fillAddresses.ts +185 -0
  340. package/src/modules/forms/repository.ts +3 -3
  341. package/src/modules/forms/routes.ts +2 -2
  342. package/src/modules/forms/schema.ts +2 -2
  343. package/src/modules/forms/service.ts +2 -2
  344. package/src/modules/forms/submission.ts +34 -4
  345. package/src/modules/hiring/roles.ts +2 -1
  346. package/src/modules/import-export/addresses.ts +150 -0
  347. package/src/modules/import-export/drafts.ts +7 -9
  348. package/src/modules/import-export/exportRows.ts +3 -3
  349. package/src/modules/import-export/plan.ts +30 -2
  350. package/src/modules/import-export/presets.ts +55 -7
  351. package/src/modules/import-export/repository.ts +5 -7
  352. package/src/modules/import-export/writes.ts +8 -4
  353. package/src/modules/people/repository.ts +1 -0
  354. package/src/modules/people/routes.ts +6 -6
  355. package/src/modules/people/schema.ts +7 -5
  356. package/src/modules/people/service.ts +7 -5
  357. package/src/modules/plans/repository.ts +2 -2
  358. package/src/modules/plans/routes.ts +6 -6
  359. package/src/modules/plans/schema.ts +4 -4
  360. package/src/modules/plans/service.ts +3 -3
  361. package/src/modules/plans/tools.ts +2 -2
  362. package/src/modules/recordTargets.ts +14 -0
  363. package/src/modules/references.ts +2 -0
  364. package/src/modules/sample-data/fixture.ts +208 -27
  365. package/src/modules/sample-data/index.ts +7 -2
  366. package/src/modules/sample-data/routes.ts +2 -0
  367. package/src/modules/sample-data/service.ts +166 -49
  368. package/src/modules/sample-data/tools.ts +4 -3
  369. package/src/modules/search/index.ts +2 -1
  370. package/src/modules/search/repository.ts +32 -1
  371. package/src/modules/search/service.ts +6 -4
  372. package/src/modules/smtp-email/index.ts +32 -10
  373. package/src/modules/workspace/repository.ts +18 -0
  374. package/src/modules/workspace/routes.ts +16 -0
  375. package/src/modules/workspace/service.ts +59 -14
  376. package/src/modules/workspace/starters.ts +275 -14
  377. package/src/modules/workspace/workspaceAccessMiddleware.ts +3 -4
  378. package/src/runtime/events.ts +2 -0
  379. package/src/runtime/module.ts +8 -0
  380. package/src/runtime/registry.ts +122 -8
  381. package/src/schema/index.ts +1 -0
  382. package/src/testing/app.ts +14 -1
  383. package/src/testing/client.ts +11 -1
@@ -0,0 +1,185 @@
1
+ import {
2
+ COMPANY_ADDRESS_KINDS,
3
+ PERSON_ADDRESS_KINDS,
4
+ normaliseCountry,
5
+ } from '@kelpie/schemas'
6
+ import type { CompanyAddress, PersonAddress } from '@kelpie/schemas'
7
+
8
+ import { fillBlank } from './mapping.ts'
9
+
10
+ /**
11
+ * Compose one address from `*.address.*` form answers and fill-blank it onto
12
+ * the stored list. Default kind is mailing on a Person and hq on a Company.
13
+ * An existing address of that kind keeps any part it already has.
14
+ */
15
+
16
+ const ADDRESS_PART_FIELDS = ['line1', 'line2', 'city', 'region', 'postal_code', 'country'] as const
17
+
18
+ type AddressPartField = (typeof ADDRESS_PART_FIELDS)[number]
19
+
20
+ function readPart(
21
+ standard: Readonly<Record<string, string>>,
22
+ field: AddressPartField,
23
+ ): string | undefined {
24
+ const raw = standard[`address.${field}`]
25
+
26
+ if (raw === undefined) {
27
+ return undefined
28
+ }
29
+
30
+ const trimmed = raw.trim()
31
+
32
+ return trimmed.length === 0 ? undefined : trimmed
33
+ }
34
+
35
+ function hasAddressAnswers(standard: Readonly<Record<string, string>>): boolean {
36
+ if (standard['address.kind'] !== undefined && standard['address.kind'].trim().length > 0) {
37
+ return true
38
+ }
39
+
40
+ return ADDRESS_PART_FIELDS.some((field) => readPart(standard, field) !== undefined)
41
+ }
42
+
43
+ function resolveCountry(raw: string | undefined): string | null | undefined {
44
+ if (raw === undefined) {
45
+ return undefined
46
+ }
47
+
48
+ return normaliseCountry(raw)
49
+ }
50
+
51
+ function fillPart(stored: string | null, inbound: string | undefined): string | null {
52
+ const filled = fillBlank(stored, inbound)
53
+
54
+ return filled === undefined ? stored : filled
55
+ }
56
+
57
+ function mergeAddress<Kind extends string>(
58
+ stored: { readonly line1: string | null; readonly line2: string | null; readonly city: string | null; readonly region: string | null; readonly postalCode: string | null; readonly country: string | null } | undefined,
59
+ inbound: {
60
+ readonly line1: string | undefined
61
+ readonly line2: string | undefined
62
+ readonly city: string | undefined
63
+ readonly region: string | undefined
64
+ readonly postalCode: string | undefined
65
+ readonly country: string | null | undefined
66
+ },
67
+ kind: Kind,
68
+ primary: boolean,
69
+ ): {
70
+ readonly kind: Kind
71
+ readonly line1: string | null
72
+ readonly line2: string | null
73
+ readonly city: string | null
74
+ readonly region: string | null
75
+ readonly postalCode: string | null
76
+ readonly country: string | null
77
+ readonly primary: boolean
78
+ } {
79
+ const empty = {
80
+ line1: null,
81
+ line2: null,
82
+ city: null,
83
+ region: null,
84
+ postalCode: null,
85
+ country: null,
86
+ }
87
+ const base = stored ?? empty
88
+
89
+ return {
90
+ kind,
91
+ line1: fillPart(base.line1, inbound.line1),
92
+ line2: fillPart(base.line2, inbound.line2),
93
+ city: fillPart(base.city, inbound.city),
94
+ region: fillPart(base.region, inbound.region),
95
+ postalCode: fillPart(base.postalCode, inbound.postalCode),
96
+ country: inbound.country === undefined ? base.country : fillPart(base.country, inbound.country ?? undefined),
97
+ primary,
98
+ }
99
+ }
100
+
101
+ function hasLocation(address: {
102
+ readonly line1: string | null
103
+ readonly line2: string | null
104
+ readonly city: string | null
105
+ readonly region: string | null
106
+ readonly postalCode: string | null
107
+ readonly country: string | null
108
+ }): boolean {
109
+ return (
110
+ address.line1 !== null ||
111
+ address.line2 !== null ||
112
+ address.city !== null ||
113
+ address.region !== null ||
114
+ address.postalCode !== null ||
115
+ address.country !== null
116
+ )
117
+ }
118
+
119
+ function fillTypedAddress<Kind extends string>(
120
+ stored: readonly {
121
+ readonly kind: Kind
122
+ readonly line1: string | null
123
+ readonly line2: string | null
124
+ readonly city: string | null
125
+ readonly region: string | null
126
+ readonly postalCode: string | null
127
+ readonly country: string | null
128
+ readonly primary: boolean
129
+ }[],
130
+ standard: Readonly<Record<string, string>>,
131
+ kinds: readonly Kind[],
132
+ defaultKind: Kind,
133
+ ): readonly {
134
+ readonly kind: Kind
135
+ readonly line1: string | null
136
+ readonly line2: string | null
137
+ readonly city: string | null
138
+ readonly region: string | null
139
+ readonly postalCode: string | null
140
+ readonly country: string | null
141
+ readonly primary: boolean
142
+ }[] | undefined {
143
+ if (!hasAddressAnswers(standard)) {
144
+ return undefined
145
+ }
146
+
147
+ const requested = (standard['address.kind'] ?? '').trim()
148
+ const kind = kinds.includes(requested as Kind) ? (requested as Kind) : defaultKind
149
+ const inbound = {
150
+ line1: readPart(standard, 'line1'),
151
+ line2: readPart(standard, 'line2'),
152
+ city: readPart(standard, 'city'),
153
+ region: readPart(standard, 'region'),
154
+ postalCode: readPart(standard, 'postal_code'),
155
+ country: resolveCountry(readPart(standard, 'country')),
156
+ }
157
+
158
+ const existing = stored.find((address) => address.kind === kind)
159
+ const merged = mergeAddress(existing, inbound, kind, existing?.primary ?? stored.length === 0)
160
+
161
+ if (!hasLocation(merged)) {
162
+ return undefined
163
+ }
164
+
165
+ if (existing === undefined) {
166
+ const next = stored.map((address) => (merged.primary ? { ...address, primary: false } : address))
167
+ return [...next, merged]
168
+ }
169
+
170
+ return stored.map((address) => (address.kind === kind ? merged : address))
171
+ }
172
+
173
+ export function fillPersonAddresses(
174
+ stored: readonly PersonAddress[],
175
+ standard: Readonly<Record<string, string>>,
176
+ ): readonly PersonAddress[] | undefined {
177
+ return fillTypedAddress(stored, standard, PERSON_ADDRESS_KINDS, 'mailing')
178
+ }
179
+
180
+ export function fillCompanyAddresses(
181
+ stored: readonly CompanyAddress[],
182
+ standard: Readonly<Record<string, string>>,
183
+ ): readonly CompanyAddress[] | undefined {
184
+ return fillTypedAddress(stored, standard, COMPANY_ADDRESS_KINDS, 'hq')
185
+ }
@@ -1,7 +1,7 @@
1
1
  import { and, asc, eq, ilike, inArray, or } from 'drizzle-orm'
2
2
  import type { SQL } from 'drizzle-orm'
3
3
  import type { AnyPgColumn } from 'drizzle-orm/pg-core'
4
- import type { FormSubmissionLinkTarget, PipelineKind } from '@kelpie/schemas'
4
+ import type { FormSubmissionLinkTarget, FormAttachTargetType } from '@kelpie/schemas'
5
5
 
6
6
  import { keysetCondition, orderByWindow, textSort, timestampSort } from '../../lib/pagination.ts'
7
7
  import type { ListWindow, SortableFields } from '../../lib/pagination.ts'
@@ -332,7 +332,7 @@ export async function replaceFormLists(
332
332
 
333
333
  /** One form's configured attach targets, ordered so a resent identical set is not a write. */
334
334
  export interface FormAttachTargetRow {
335
- readonly targetType: PipelineKind
335
+ readonly targetType: FormAttachTargetType
336
336
  readonly targetId: string
337
337
  }
338
338
 
@@ -347,7 +347,7 @@ export async function listAttachTargets(
347
347
  .orderBy(asc(formAttachTargets.targetType), asc(formAttachTargets.targetId))
348
348
 
349
349
  return rows.map((row) => ({
350
- targetType: row.targetType as PipelineKind,
350
+ targetType: row.targetType as FormAttachTargetType,
351
351
  targetId: row.targetId,
352
352
  }))
353
353
  }
@@ -1,6 +1,6 @@
1
1
  import type { Context, Hono } from 'hono'
2
2
  import { z } from 'zod'
3
- import { FORM_SUBMISSION_LINK_TARGETS, PIPELINE_KINDS } from '@kelpie/schemas'
3
+ import { FORM_ATTACH_TARGET_TYPES, FORM_SUBMISSION_LINK_TARGETS } from '@kelpie/schemas'
4
4
  import type { FormAttachTarget, FormSubmissionLinkTarget } from '@kelpie/schemas'
5
5
 
6
6
  import { AppError } from '../../lib/errors.ts'
@@ -59,7 +59,7 @@ const fieldBody = z.strictObject({
59
59
  })
60
60
 
61
61
  const attachTargetBody = z.strictObject({
62
- target_type: z.enum(PIPELINE_KINDS),
62
+ target_type: z.enum(FORM_ATTACH_TARGET_TYPES),
63
63
  target_id: z.string().min(1),
64
64
  })
65
65
 
@@ -1,7 +1,7 @@
1
1
  import {
2
+ FORM_ATTACH_TARGET_TYPES,
2
3
  FORM_FIELD_TYPES,
3
4
  FORM_STATUSES,
4
- PIPELINE_KINDS,
5
5
  } from '@kelpie/schemas'
6
6
  import type { FormOptionValueType, FormSubmissionActionEntry } from '@kelpie/schemas'
7
7
  import { boolean, index, integer, jsonb, pgTable, primaryKey, text } from 'drizzle-orm/pg-core'
@@ -187,7 +187,7 @@ export const formAttachTargets = pgTable(
187
187
  table.targetType,
188
188
  table.targetId,
189
189
  ),
190
- checkOneOf('form_attach_targets_target_type_check', table.targetType, PIPELINE_KINDS),
190
+ checkOneOf('form_attach_targets_target_type_check', table.targetType, FORM_ATTACH_TARGET_TYPES),
191
191
  ],
192
192
  )
193
193
 
@@ -4,7 +4,7 @@ import type {
4
4
  FormSubmissionLinkTarget,
5
5
  PipelineKind,
6
6
  } from '@kelpie/schemas'
7
- import { PIPELINE_KINDS } from '@kelpie/schemas'
7
+ import { FORM_ATTACH_TARGET_TYPES } from '@kelpie/schemas'
8
8
 
9
9
  import { changedKeys } from '../../lib/changes.ts'
10
10
  import type { Database } from '../../lib/database.ts'
@@ -422,7 +422,7 @@ export function createFormsService(dependencies: FormsDependencies): FormsServic
422
422
 
423
423
  const problems: { field: string; message: string }[] = []
424
424
 
425
- for (const kind of PIPELINE_KINDS) {
425
+ for (const kind of FORM_ATTACH_TARGET_TYPES) {
426
426
  const ofKind = targets.filter((target) => target.targetType === kind)
427
427
 
428
428
  if (ofKind.length === 0) {
@@ -17,6 +17,8 @@ import * as dealRepository from '../deals/repository.ts'
17
17
  import '../deals/events.ts'
18
18
  import * as enquiryRepository from '../enquiries/repository.ts'
19
19
  import '../enquiries/events.ts'
20
+ import '../events/catalog.ts'
21
+ import * as eventRepository from '../events/repository.ts'
20
22
  import '../lists/events.ts'
21
23
  import * as listsRepository from '../lists/repository.ts'
22
24
  import * as opportunityRepository from '../opportunities/repository.ts'
@@ -1196,18 +1198,46 @@ export function createFormSubmitService(dependencies: SubmissionDependencies): F
1196
1198
  events,
1197
1199
  actionLog,
1198
1200
  `attach:${target.targetType}:${target.targetId}`,
1199
- async (inner) => {
1201
+ async (inner, emit) => {
1202
+ const targetType = target.targetType
1203
+ const targetId = target.targetId
1204
+
1200
1205
  // Racing a target delete would fail the FK-less insert with the
1201
1206
  // same effect as an existence check: log an error, continue.
1202
- if (!(await targetExists(inner, workspaceId, target.targetType, target.targetId))) {
1203
- throw AppError.notFound(`${target.targetType} ${target.targetId} not found`)
1207
+ if (!(await targetExists(inner, workspaceId, targetType, targetId))) {
1208
+ throw AppError.notFound(`${targetType} ${targetId} not found`)
1209
+ }
1210
+
1211
+ if (targetType === 'event') {
1212
+ const { record, inserted } = await eventRepository.insertAttendanceIfAbsent(
1213
+ inner,
1214
+ {
1215
+ id: dependencies.createId('attendance'),
1216
+ workspaceId,
1217
+ eventId: targetId,
1218
+ personId: person.record.id,
1219
+ status: 'registered',
1220
+ source: 'form',
1221
+ createdAt: now,
1222
+ updatedAt: now,
1223
+ },
1224
+ )
1225
+
1226
+ if (inserted) {
1227
+ emit('events.attendance.created', { type: 'attendance', id: record.id }, {})
1228
+ }
1229
+
1230
+ return {
1231
+ status: 'ok',
1232
+ detail: inserted ? 'registered' : 'already registered',
1233
+ }
1204
1234
  }
1205
1235
 
1206
1236
  const inserted = await personLinks.linkPersonIfAbsent(
1207
1237
  inner,
1208
1238
  dependencies.createId,
1209
1239
  workspaceId,
1210
- target,
1240
+ { targetType, targetId },
1211
1241
  person.record.id,
1212
1242
  )
1213
1243
 
@@ -13,7 +13,7 @@ import { toEventActor } from '../../lib/actor.ts'
13
13
  import type { Actor } from '../auth/actor.ts'
14
14
  import { requireWorkspaceId } from '../auth/actor.ts'
15
15
  import './events.ts'
16
- import { deleteRecordsAttachedTo } from '../attachedRecords.ts'
16
+ import { deleteEventAssociationsForTarget, deleteRecordsAttachedTo } from '../attachedRecords.ts'
17
17
  import * as repository from './repository.ts'
18
18
  import { DEFAULT_ROLE_SORT, ROLE_SORTS } from './repository.ts'
19
19
  import type { RoleFilters, RoleRecord } from './repository.ts'
@@ -162,6 +162,7 @@ export function createRolesService(dependencies: RolesDependencies): RolesServic
162
162
  })
163
163
  }
164
164
 
165
+ await deleteEventAssociationsForTarget(tx, workspaceId, 'role', id)
165
166
  await repository.deleteRole(tx, workspaceId, id)
166
167
 
167
168
  events.emit('hiring.role.deleted', { type: 'role', id }, {})
@@ -0,0 +1,150 @@
1
+ import {
2
+ ADDRESS_PARTS,
3
+ addressColumnKey,
4
+ COMPANY_ADDRESS_KINDS,
5
+ PERSON_ADDRESS_KINDS,
6
+ } from '@kelpie/schemas'
7
+ import type {
8
+ AddressPart,
9
+ CompanyAddress,
10
+ CompanyAddressKind,
11
+ PersonAddress,
12
+ PersonAddressKind,
13
+ PostalAddress,
14
+ } from '@kelpie/schemas'
15
+ import { normaliseCountry } from '@kelpie/schemas'
16
+
17
+ /**
18
+ * Flattened CSV address columns → jsonb addresses, and the reverse for export.
19
+ *
20
+ * One group per kind. A group with any non-empty part becomes an address of
21
+ * that kind. The first non-empty kind in the object's kind order is primary.
22
+ * An unknown country is a warning and leaves `country` null rather than
23
+ * failing the row.
24
+ */
25
+
26
+ export interface AddressImportWarning {
27
+ readonly field: string
28
+ readonly message: string
29
+ }
30
+
31
+ export interface AddressImportResult<Kind extends string> {
32
+ readonly addresses: readonly PostalAddress<Kind>[] | undefined
33
+ readonly warnings: readonly AddressImportWarning[]
34
+ }
35
+
36
+ const CAMEL_PART: Readonly<Record<AddressPart, 'line1' | 'line2' | 'city' | 'region' | 'postalCode' | 'country'>> =
37
+ {
38
+ line1: 'line1',
39
+ line2: 'line2',
40
+ city: 'city',
41
+ region: 'region',
42
+ postal_code: 'postalCode',
43
+ country: 'country',
44
+ }
45
+
46
+ function cell(mapped: Readonly<Record<string, string>>, key: string): string | undefined {
47
+ const value = (mapped[key] ?? '').trim()
48
+
49
+ return value.length === 0 ? undefined : value
50
+ }
51
+
52
+ function addressesFromMapped<Kind extends string>(
53
+ mapped: Readonly<Record<string, string>>,
54
+ kinds: readonly Kind[],
55
+ ): AddressImportResult<Kind> {
56
+ const warnings: AddressImportWarning[] = []
57
+ const addresses: PostalAddress<Kind>[] = []
58
+
59
+ for (const kind of kinds) {
60
+ const parts: {
61
+ line1: string | null
62
+ line2: string | null
63
+ city: string | null
64
+ region: string | null
65
+ postalCode: string | null
66
+ country: string | null
67
+ } = {
68
+ line1: cell(mapped, addressColumnKey(kind, 'line1')) ?? null,
69
+ line2: cell(mapped, addressColumnKey(kind, 'line2')) ?? null,
70
+ city: cell(mapped, addressColumnKey(kind, 'city')) ?? null,
71
+ region: cell(mapped, addressColumnKey(kind, 'region')) ?? null,
72
+ postalCode: cell(mapped, addressColumnKey(kind, 'postal_code')) ?? null,
73
+ country: null,
74
+ }
75
+
76
+ const countryRaw = cell(mapped, addressColumnKey(kind, 'country'))
77
+
78
+ if (countryRaw !== undefined) {
79
+ const code = normaliseCountry(countryRaw)
80
+
81
+ if (code === null) {
82
+ warnings.push({
83
+ field: addressColumnKey(kind, 'country'),
84
+ message: `"${countryRaw}" is not a country Kelpie knows. Country left blank`,
85
+ })
86
+ } else {
87
+ parts.country = code
88
+ }
89
+ }
90
+
91
+ const hasPart = Object.values(parts).some((value) => value !== null)
92
+
93
+ if (!hasPart) {
94
+ continue
95
+ }
96
+
97
+ addresses.push({
98
+ kind,
99
+ ...parts,
100
+ primary: false,
101
+ })
102
+ }
103
+
104
+ if (addresses.length === 0) {
105
+ return { addresses: undefined, warnings }
106
+ }
107
+
108
+ return {
109
+ addresses: addresses.map((address, index) => ({ ...address, primary: index === 0 })),
110
+ warnings,
111
+ }
112
+ }
113
+
114
+ export function personAddressesFromMapped(
115
+ mapped: Readonly<Record<string, string>>,
116
+ ): AddressImportResult<PersonAddressKind> {
117
+ return addressesFromMapped(mapped, PERSON_ADDRESS_KINDS)
118
+ }
119
+
120
+ export function companyAddressesFromMapped(
121
+ mapped: Readonly<Record<string, string>>,
122
+ ): AddressImportResult<CompanyAddressKind> {
123
+ return addressesFromMapped(mapped, COMPANY_ADDRESS_KINDS)
124
+ }
125
+
126
+ function partValue(address: PostalAddress | undefined, part: AddressPart): string {
127
+ if (address === undefined) {
128
+ return ''
129
+ }
130
+
131
+ const value = address[CAMEL_PART[part]]
132
+
133
+ return value ?? ''
134
+ }
135
+
136
+ export function personAddressCells(addresses: readonly PersonAddress[]): readonly string[] {
137
+ return PERSON_ADDRESS_KINDS.flatMap((kind) => {
138
+ const address = addresses.find((entry) => entry.kind === kind)
139
+
140
+ return ADDRESS_PARTS.map((part) => partValue(address, part))
141
+ })
142
+ }
143
+
144
+ export function companyAddressCells(addresses: readonly CompanyAddress[]): readonly string[] {
145
+ return COMPANY_ADDRESS_KINDS.flatMap((kind) => {
146
+ const address = addresses.find((entry) => entry.kind === kind)
147
+
148
+ return ADDRESS_PARTS.map((part) => partValue(address, part))
149
+ })
150
+ }
@@ -9,9 +9,10 @@ import {
9
9
  composeName,
10
10
  } from '@kelpie/schemas'
11
11
 
12
- import type { CustomFieldWireValue } from '@kelpie/schemas'
12
+ import type { CompanyAddress, CustomFieldWireValue, PersonAddress } from '@kelpie/schemas'
13
13
 
14
14
  import { normaliseDomain, normaliseEmail } from '../../lib/normalisation.ts'
15
+ import { companyAddressesFromMapped, personAddressesFromMapped } from './addresses.ts'
15
16
  import { moneyToCents, splitList } from './mapping.ts'
16
17
  import { canonicalEnum } from './validation.ts'
17
18
 
@@ -36,8 +37,7 @@ export interface CompanyDraft {
36
37
  readonly description?: string
37
38
  readonly stage?: string
38
39
  readonly sizeBand?: string
39
- readonly hq?: string
40
- readonly website?: string
40
+ readonly addresses?: readonly CompanyAddress[]
41
41
  readonly accountType?: string
42
42
  readonly icpFit?: string
43
43
  readonly summary?: string
@@ -54,7 +54,7 @@ export interface PersonDraft {
54
54
  readonly email?: string
55
55
  readonly phones?: readonly string[]
56
56
  readonly timezone?: string
57
- readonly location?: string
57
+ readonly addresses?: readonly PersonAddress[]
58
58
  readonly preferredChannel?: string
59
59
  readonly influence?: string
60
60
  readonly relationship?: string
@@ -211,8 +211,7 @@ export function companyDraft(
211
211
  description: text(mapped, 'description'),
212
212
  stage: enumeration(mapped, 'stage', COMPANY_STAGES),
213
213
  sizeBand: enumeration(mapped, 'size_band', SIZE_BANDS),
214
- hq: text(mapped, 'hq'),
215
- website: text(mapped, 'website'),
214
+ addresses: companyAddressesFromMapped(mapped).addresses,
216
215
  accountType: enumeration(mapped, 'account_type', ACCOUNT_TYPES),
217
216
  icpFit: enumeration(mapped, 'icp_fit', ICP_FITS),
218
217
  summary: text(mapped, 'summary'),
@@ -246,7 +245,7 @@ export function personDraft(
246
245
  email: email === undefined ? undefined : (normaliseEmail(email) ?? undefined),
247
246
  phones: list(mapped, 'phones'),
248
247
  timezone: text(mapped, 'timezone'),
249
- location: text(mapped, 'location'),
248
+ addresses: personAddressesFromMapped(mapped).addresses,
250
249
  preferredChannel: enumeration(mapped, 'preferred_channel', PREFERRED_CHANNELS),
251
250
  influence: enumeration(mapped, 'influence', INFLUENCE_LEVELS),
252
251
  relationship: enumeration(mapped, 'relationship', RELATIONSHIP_LEVELS),
@@ -274,8 +273,7 @@ export function affiliationCompanyDraft(mapped: Readonly<Record<string, string>>
274
273
  description: undefined,
275
274
  stage: undefined,
276
275
  sizeBand: undefined,
277
- hq: undefined,
278
- website: undefined,
276
+ addresses: undefined,
279
277
  accountType: undefined,
280
278
  icpFit: undefined,
281
279
  summary: undefined,
@@ -1,6 +1,7 @@
1
1
  import { OBJECT_COLUMNS, customFieldObjectTypeForExport } from '@kelpie/schemas'
2
2
  import type { CsvColumn, CustomFieldValue, ExportObject } from '@kelpie/schemas'
3
3
 
4
+ import { companyAddressCells, personAddressCells } from './addresses.ts'
4
5
  import type { CustomFieldDefinitionRecord } from '../custom-fields/repository.ts'
5
6
  import { customFieldCells, customFieldDefinitionCells, customFieldHeaderKeys } from './customFieldCsv.ts'
6
7
  import { centsToMoney } from './mapping.ts'
@@ -101,8 +102,7 @@ export function companyCells(
101
102
  row.description,
102
103
  row.summary,
103
104
  joinList(row.tags),
104
- row.website ?? '',
105
- row.hq ?? '',
105
+ ...companyAddressCells(row.addresses),
106
106
  ],
107
107
  row.customFields,
108
108
  definitions,
@@ -122,7 +122,7 @@ export function personCells(
122
122
  row.suffix ?? '',
123
123
  row.email ?? '',
124
124
  row.timezone ?? '',
125
- row.location ?? '',
125
+ ...personAddressCells(row.addresses),
126
126
  row.preferredChannel,
127
127
  row.influence,
128
128
  row.relationship,
@@ -11,6 +11,7 @@ import { pipelineKindForImport } from '@kelpie/schemas'
11
11
 
12
12
  import { normaliseDomain, normaliseEmail } from '../../lib/normalisation.ts'
13
13
  import type { CustomFieldDefinitionRecord } from '../custom-fields/repository.ts'
14
+ import { companyAddressesFromMapped, personAddressesFromMapped } from './addresses.ts'
14
15
  import {
15
16
  customFieldWireValues,
16
17
  extractCustomFieldRaw,
@@ -628,6 +629,29 @@ function withAffiliation(
628
629
  return { ...plan, write, ...(warnings.length > 0 ? { warnings } : {}) }
629
630
  }
630
631
 
632
+ function withAddressWarnings(
633
+ context: PlanContext,
634
+ mapped: Readonly<Record<string, string>>,
635
+ plan: RowPlan,
636
+ ): RowPlan {
637
+ if (plan.action !== 'create' && plan.action !== 'update') {
638
+ return plan
639
+ }
640
+
641
+ const extra =
642
+ context.object === 'people'
643
+ ? personAddressesFromMapped(mapped).warnings
644
+ : context.object === 'companies'
645
+ ? companyAddressesFromMapped(mapped).warnings
646
+ : []
647
+
648
+ if (extra.length === 0) {
649
+ return plan
650
+ }
651
+
652
+ return { ...plan, warnings: [...(plan.warnings ?? []), ...extra] }
653
+ }
654
+
631
655
  /**
632
656
  * @param mapped The row's cells by Kelpie column, from `mapRow`.
633
657
  * @returns What this row would do. `create` when its key matches nothing;
@@ -663,7 +687,7 @@ export function planRow(context: PlanContext, mapped: Readonly<Record<string, st
663
687
  const targetId = context.lookups.existing.get(key)
664
688
 
665
689
  if (targetId === undefined) {
666
- return withAffiliation(context, mapped, { action: 'create', key, write })
690
+ return withAddressWarnings(context, mapped, withAffiliation(context, mapped, { action: 'create', key, write }))
667
691
  }
668
692
 
669
693
  // An empty string is the placeholder an in-file match carries: the record it
@@ -674,7 +698,11 @@ export function planRow(context: PlanContext, mapped: Readonly<Record<string, st
674
698
  // wants an existing person's position updated runs the job in `update` mode,
675
699
  // which is where the rename-in-place lives.
676
700
  return context.conflictMode === 'update'
677
- ? withAffiliation(context, mapped, { action: 'update', key, targetId: resolved, write })
701
+ ? withAddressWarnings(
702
+ context,
703
+ mapped,
704
+ withAffiliation(context, mapped, { action: 'update', key, targetId: resolved, write }),
705
+ )
678
706
  : { action: 'skip', key, targetId: resolved }
679
707
  }
680
708