@kelpie/server 0.5.1 → 0.6.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 (347) hide show
  1. package/README.md +5 -2
  2. package/dist/index.d.ts +17 -8
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +8 -4
  5. package/dist/index.js.map +1 -1
  6. package/dist/lib/actor.d.ts +9 -1
  7. package/dist/lib/actor.d.ts.map +1 -1
  8. package/dist/lib/actor.js +13 -0
  9. package/dist/lib/actor.js.map +1 -1
  10. package/dist/lib/config.d.ts +44 -6
  11. package/dist/lib/config.d.ts.map +1 -1
  12. package/dist/lib/config.js +9 -5
  13. package/dist/lib/config.js.map +1 -1
  14. package/dist/lib/email.d.ts +15 -63
  15. package/dist/lib/email.d.ts.map +1 -1
  16. package/dist/lib/email.js +13 -65
  17. package/dist/lib/email.js.map +1 -1
  18. package/dist/lib/fromEnv.d.ts +60 -0
  19. package/dist/lib/fromEnv.d.ts.map +1 -0
  20. package/dist/lib/fromEnv.js +94 -0
  21. package/dist/lib/fromEnv.js.map +1 -0
  22. package/dist/lib/ids.d.ts +6 -0
  23. package/dist/lib/ids.d.ts.map +1 -1
  24. package/dist/lib/ids.js +6 -0
  25. package/dist/lib/ids.js.map +1 -1
  26. package/dist/lib/kelpieConfigFile.d.ts +123 -0
  27. package/dist/lib/kelpieConfigFile.d.ts.map +1 -0
  28. package/dist/lib/kelpieConfigFile.js +111 -0
  29. package/dist/lib/kelpieConfigFile.js.map +1 -0
  30. package/dist/lib/logger.d.ts +38 -7
  31. package/dist/lib/logger.d.ts.map +1 -1
  32. package/dist/lib/logger.js +58 -20
  33. package/dist/lib/logger.js.map +1 -1
  34. package/dist/modules/agent-tasks/index.d.ts.map +1 -1
  35. package/dist/modules/agent-tasks/index.js +4 -1
  36. package/dist/modules/agent-tasks/index.js.map +1 -1
  37. package/dist/modules/agent-tasks/prompt.d.ts +36 -6
  38. package/dist/modules/agent-tasks/prompt.d.ts.map +1 -1
  39. package/dist/modules/agent-tasks/prompt.js +33 -4
  40. package/dist/modules/agent-tasks/prompt.js.map +1 -1
  41. package/dist/modules/agent-tasks/service.d.ts.map +1 -1
  42. package/dist/modules/agent-tasks/service.js +3 -2
  43. package/dist/modules/agent-tasks/service.js.map +1 -1
  44. package/dist/modules/agent-tasks/wire.d.ts +11 -0
  45. package/dist/modules/agent-tasks/wire.d.ts.map +1 -1
  46. package/dist/modules/agent-tasks/wire.js +1 -0
  47. package/dist/modules/agent-tasks/wire.js.map +1 -1
  48. package/dist/modules/auth/index.d.ts.map +1 -1
  49. package/dist/modules/auth/index.js +8 -7
  50. package/dist/modules/auth/index.js.map +1 -1
  51. package/dist/modules/companies/events.d.ts +22 -0
  52. package/dist/modules/companies/events.d.ts.map +1 -0
  53. package/dist/modules/companies/events.js +8 -0
  54. package/dist/modules/companies/events.js.map +1 -0
  55. package/dist/modules/companies/index.d.ts.map +1 -1
  56. package/dist/modules/companies/index.js +2 -0
  57. package/dist/modules/companies/index.js.map +1 -1
  58. package/dist/modules/companies/service.d.ts +1 -0
  59. package/dist/modules/companies/service.d.ts.map +1 -1
  60. package/dist/modules/companies/service.js +8 -11
  61. package/dist/modules/companies/service.js.map +1 -1
  62. package/dist/modules/core.d.ts.map +1 -1
  63. package/dist/modules/core.js +4 -0
  64. package/dist/modules/core.js.map +1 -1
  65. package/dist/modules/deals/events.d.ts +31 -0
  66. package/dist/modules/deals/events.d.ts.map +1 -0
  67. package/dist/modules/deals/events.js +12 -0
  68. package/dist/modules/deals/events.js.map +1 -0
  69. package/dist/modules/deals/index.d.ts.map +1 -1
  70. package/dist/modules/deals/index.js +2 -0
  71. package/dist/modules/deals/index.js.map +1 -1
  72. package/dist/modules/deals/service.d.ts +1 -0
  73. package/dist/modules/deals/service.d.ts.map +1 -1
  74. package/dist/modules/deals/service.js +9 -18
  75. package/dist/modules/deals/service.js.map +1 -1
  76. package/dist/modules/decisions/events.d.ts +21 -0
  77. package/dist/modules/decisions/events.d.ts.map +1 -0
  78. package/dist/modules/decisions/events.js +11 -0
  79. package/dist/modules/decisions/events.js.map +1 -0
  80. package/dist/modules/decisions/index.d.ts.map +1 -1
  81. package/dist/modules/decisions/index.js +2 -0
  82. package/dist/modules/decisions/index.js.map +1 -1
  83. package/dist/modules/decisions/service.d.ts +1 -0
  84. package/dist/modules/decisions/service.d.ts.map +1 -1
  85. package/dist/modules/decisions/service.js +4 -7
  86. package/dist/modules/decisions/service.js.map +1 -1
  87. package/dist/modules/forms/events.d.ts +37 -0
  88. package/dist/modules/forms/events.d.ts.map +1 -0
  89. package/dist/modules/forms/events.js +15 -0
  90. package/dist/modules/forms/events.js.map +1 -0
  91. package/dist/modules/forms/index.d.ts.map +1 -1
  92. package/dist/modules/forms/index.js +2 -0
  93. package/dist/modules/forms/index.js.map +1 -1
  94. package/dist/modules/forms/service.d.ts +1 -0
  95. package/dist/modules/forms/service.d.ts.map +1 -1
  96. package/dist/modules/forms/service.js +8 -11
  97. package/dist/modules/forms/service.js.map +1 -1
  98. package/dist/modules/forms/submission.d.ts +5 -0
  99. package/dist/modules/forms/submission.d.ts.map +1 -1
  100. package/dist/modules/forms/submission.js +54 -28
  101. package/dist/modules/forms/submission.js.map +1 -1
  102. package/dist/modules/handbook/events.d.ts +28 -0
  103. package/dist/modules/handbook/events.d.ts.map +1 -0
  104. package/dist/modules/handbook/events.js +14 -0
  105. package/dist/modules/handbook/events.js.map +1 -0
  106. package/dist/modules/handbook/index.d.ts.map +1 -1
  107. package/dist/modules/handbook/index.js +2 -0
  108. package/dist/modules/handbook/index.js.map +1 -1
  109. package/dist/modules/handbook/service.d.ts +1 -0
  110. package/dist/modules/handbook/service.d.ts.map +1 -1
  111. package/dist/modules/handbook/service.js +8 -15
  112. package/dist/modules/handbook/service.js.map +1 -1
  113. package/dist/modules/hiring/candidates.d.ts +1 -0
  114. package/dist/modules/hiring/candidates.d.ts.map +1 -1
  115. package/dist/modules/hiring/candidates.js +8 -11
  116. package/dist/modules/hiring/candidates.js.map +1 -1
  117. package/dist/modules/hiring/events.d.ts +35 -0
  118. package/dist/modules/hiring/events.d.ts.map +1 -0
  119. package/dist/modules/hiring/events.js +11 -0
  120. package/dist/modules/hiring/events.js.map +1 -0
  121. package/dist/modules/hiring/index.d.ts.map +1 -1
  122. package/dist/modules/hiring/index.js +2 -0
  123. package/dist/modules/hiring/index.js.map +1 -1
  124. package/dist/modules/hiring/roles.d.ts +1 -0
  125. package/dist/modules/hiring/roles.d.ts.map +1 -1
  126. package/dist/modules/hiring/roles.js +8 -11
  127. package/dist/modules/hiring/roles.js.map +1 -1
  128. package/dist/modules/import-export/events.d.ts +22 -0
  129. package/dist/modules/import-export/events.d.ts.map +1 -0
  130. package/dist/modules/import-export/events.js +12 -0
  131. package/dist/modules/import-export/events.js.map +1 -0
  132. package/dist/modules/import-export/index.d.ts.map +1 -1
  133. package/dist/modules/import-export/index.js +2 -0
  134. package/dist/modules/import-export/index.js.map +1 -1
  135. package/dist/modules/import-export/service.d.ts +5 -0
  136. package/dist/modules/import-export/service.d.ts.map +1 -1
  137. package/dist/modules/import-export/service.js +53 -14
  138. package/dist/modules/import-export/service.js.map +1 -1
  139. package/dist/modules/notes/events.d.ts +22 -0
  140. package/dist/modules/notes/events.d.ts.map +1 -0
  141. package/dist/modules/notes/events.js +12 -0
  142. package/dist/modules/notes/events.js.map +1 -0
  143. package/dist/modules/notes/index.d.ts.map +1 -1
  144. package/dist/modules/notes/index.js +2 -0
  145. package/dist/modules/notes/index.js.map +1 -1
  146. package/dist/modules/notes/service.d.ts +1 -0
  147. package/dist/modules/notes/service.d.ts.map +1 -1
  148. package/dist/modules/notes/service.js +4 -7
  149. package/dist/modules/notes/service.js.map +1 -1
  150. package/dist/modules/opportunities/events.d.ts +31 -0
  151. package/dist/modules/opportunities/events.d.ts.map +1 -0
  152. package/dist/modules/opportunities/events.js +12 -0
  153. package/dist/modules/opportunities/events.js.map +1 -0
  154. package/dist/modules/opportunities/index.d.ts.map +1 -1
  155. package/dist/modules/opportunities/index.js +2 -0
  156. package/dist/modules/opportunities/index.js.map +1 -1
  157. package/dist/modules/opportunities/service.d.ts +1 -0
  158. package/dist/modules/opportunities/service.d.ts.map +1 -1
  159. package/dist/modules/opportunities/service.js +9 -18
  160. package/dist/modules/opportunities/service.js.map +1 -1
  161. package/dist/modules/partnerships/events.d.ts +31 -0
  162. package/dist/modules/partnerships/events.d.ts.map +1 -0
  163. package/dist/modules/partnerships/events.js +12 -0
  164. package/dist/modules/partnerships/events.js.map +1 -0
  165. package/dist/modules/partnerships/index.d.ts.map +1 -1
  166. package/dist/modules/partnerships/index.js +2 -0
  167. package/dist/modules/partnerships/index.js.map +1 -1
  168. package/dist/modules/partnerships/service.d.ts +1 -0
  169. package/dist/modules/partnerships/service.d.ts.map +1 -1
  170. package/dist/modules/partnerships/service.js +9 -18
  171. package/dist/modules/partnerships/service.js.map +1 -1
  172. package/dist/modules/people/events.d.ts +28 -0
  173. package/dist/modules/people/events.d.ts.map +1 -0
  174. package/dist/modules/people/events.js +18 -0
  175. package/dist/modules/people/events.js.map +1 -0
  176. package/dist/modules/people/index.d.ts.map +1 -1
  177. package/dist/modules/people/index.js +2 -0
  178. package/dist/modules/people/index.js.map +1 -1
  179. package/dist/modules/people/service.d.ts +1 -0
  180. package/dist/modules/people/service.d.ts.map +1 -1
  181. package/dist/modules/people/service.js +8 -11
  182. package/dist/modules/people/service.js.map +1 -1
  183. package/dist/modules/pipelines/service.d.ts +4 -0
  184. package/dist/modules/pipelines/service.d.ts.map +1 -1
  185. package/dist/modules/pipelines/service.js +31 -8
  186. package/dist/modules/pipelines/service.js.map +1 -1
  187. package/dist/modules/plans/events.d.ts +21 -0
  188. package/dist/modules/plans/events.d.ts.map +1 -0
  189. package/dist/modules/plans/events.js +11 -0
  190. package/dist/modules/plans/events.js.map +1 -0
  191. package/dist/modules/plans/index.d.ts.map +1 -1
  192. package/dist/modules/plans/index.js +2 -0
  193. package/dist/modules/plans/index.js.map +1 -1
  194. package/dist/modules/plans/service.d.ts +1 -0
  195. package/dist/modules/plans/service.d.ts.map +1 -1
  196. package/dist/modules/plans/service.js +6 -14
  197. package/dist/modules/plans/service.js.map +1 -1
  198. package/dist/modules/positions/events.d.ts +22 -0
  199. package/dist/modules/positions/events.d.ts.map +1 -0
  200. package/dist/modules/positions/events.js +8 -0
  201. package/dist/modules/positions/events.js.map +1 -0
  202. package/dist/modules/positions/index.d.ts.map +1 -1
  203. package/dist/modules/positions/index.js +2 -0
  204. package/dist/modules/positions/index.js.map +1 -1
  205. package/dist/modules/positions/service.d.ts +1 -0
  206. package/dist/modules/positions/service.d.ts.map +1 -1
  207. package/dist/modules/positions/service.js +8 -11
  208. package/dist/modules/positions/service.js.map +1 -1
  209. package/dist/modules/raises/events.d.ts +31 -0
  210. package/dist/modules/raises/events.d.ts.map +1 -0
  211. package/dist/modules/raises/events.js +12 -0
  212. package/dist/modules/raises/events.js.map +1 -0
  213. package/dist/modules/raises/index.d.ts.map +1 -1
  214. package/dist/modules/raises/index.js +2 -0
  215. package/dist/modules/raises/index.js.map +1 -1
  216. package/dist/modules/raises/service.d.ts +1 -0
  217. package/dist/modules/raises/service.d.ts.map +1 -1
  218. package/dist/modules/raises/service.js +9 -18
  219. package/dist/modules/raises/service.js.map +1 -1
  220. package/dist/modules/reseal.d.ts +55 -0
  221. package/dist/modules/reseal.d.ts.map +1 -1
  222. package/dist/modules/reseal.js +90 -1
  223. package/dist/modules/reseal.js.map +1 -1
  224. package/dist/modules/smtp-email/index.d.ts +69 -0
  225. package/dist/modules/smtp-email/index.d.ts.map +1 -0
  226. package/dist/modules/smtp-email/index.js +82 -0
  227. package/dist/modules/smtp-email/index.js.map +1 -0
  228. package/dist/modules/webhooks/index.d.ts.map +1 -1
  229. package/dist/modules/webhooks/index.js +5 -1
  230. package/dist/modules/webhooks/index.js.map +1 -1
  231. package/dist/modules/webhooks/payloads.d.ts +22 -7
  232. package/dist/modules/webhooks/payloads.d.ts.map +1 -1
  233. package/dist/modules/webhooks/payloads.js +63 -44
  234. package/dist/modules/webhooks/payloads.js.map +1 -1
  235. package/dist/modules/workspace/events.d.ts +52 -0
  236. package/dist/modules/workspace/events.d.ts.map +1 -0
  237. package/dist/modules/workspace/events.js +16 -0
  238. package/dist/modules/workspace/events.js.map +1 -0
  239. package/dist/modules/workspace/index.d.ts.map +1 -1
  240. package/dist/modules/workspace/index.js +6 -2
  241. package/dist/modules/workspace/index.js.map +1 -1
  242. package/dist/modules/workspace/service.d.ts +1 -0
  243. package/dist/modules/workspace/service.d.ts.map +1 -1
  244. package/dist/modules/workspace/service.js +13 -15
  245. package/dist/modules/workspace/service.js.map +1 -1
  246. package/dist/modules/workspace/workspaceAccessMiddleware.d.ts.map +1 -1
  247. package/dist/modules/workspace/workspaceAccessMiddleware.js +11 -3
  248. package/dist/modules/workspace/workspaceAccessMiddleware.js.map +1 -1
  249. package/dist/runtime/events.d.ts +86 -108
  250. package/dist/runtime/events.d.ts.map +1 -1
  251. package/dist/runtime/events.js +123 -52
  252. package/dist/runtime/events.js.map +1 -1
  253. package/dist/runtime/module.d.ts +54 -2
  254. package/dist/runtime/module.d.ts.map +1 -1
  255. package/dist/runtime/registry.d.ts +23 -0
  256. package/dist/runtime/registry.d.ts.map +1 -1
  257. package/dist/runtime/registry.js +102 -2
  258. package/dist/runtime/registry.js.map +1 -1
  259. package/dist/runtime/transaction.d.ts +29 -4
  260. package/dist/runtime/transaction.d.ts.map +1 -1
  261. package/dist/runtime/transaction.js +48 -7
  262. package/dist/runtime/transaction.js.map +1 -1
  263. package/dist/testing/app.d.ts.map +1 -1
  264. package/dist/testing/app.js +11 -3
  265. package/dist/testing/app.js.map +1 -1
  266. package/dist/testing/database.d.ts.map +1 -1
  267. package/dist/testing/database.js +6 -3
  268. package/dist/testing/database.js.map +1 -1
  269. package/dist/testing/services.d.ts +21 -1
  270. package/dist/testing/services.d.ts.map +1 -1
  271. package/dist/testing/services.js +20 -5
  272. package/dist/testing/services.js.map +1 -1
  273. package/package.json +5 -2
  274. package/src/index.ts +44 -9
  275. package/src/lib/actor.ts +15 -1
  276. package/src/lib/config.ts +56 -7
  277. package/src/lib/email.ts +22 -92
  278. package/src/lib/fromEnv.ts +143 -0
  279. package/src/lib/ids.ts +6 -0
  280. package/src/lib/kelpieConfigFile.ts +283 -0
  281. package/src/lib/logger.ts +92 -34
  282. package/src/modules/agent-tasks/index.ts +4 -1
  283. package/src/modules/agent-tasks/prompt.ts +57 -9
  284. package/src/modules/agent-tasks/service.ts +3 -2
  285. package/src/modules/agent-tasks/wire.ts +12 -0
  286. package/src/modules/auth/index.ts +11 -9
  287. package/src/modules/companies/events.ts +25 -0
  288. package/src/modules/companies/index.ts +2 -0
  289. package/src/modules/companies/service.ts +8 -11
  290. package/src/modules/core.ts +4 -0
  291. package/src/modules/deals/events.ts +34 -0
  292. package/src/modules/deals/index.ts +2 -0
  293. package/src/modules/deals/service.ts +17 -18
  294. package/src/modules/decisions/events.ts +24 -0
  295. package/src/modules/decisions/index.ts +2 -0
  296. package/src/modules/decisions/service.ts +8 -7
  297. package/src/modules/forms/events.ts +37 -0
  298. package/src/modules/forms/index.ts +2 -0
  299. package/src/modules/forms/service.ts +12 -11
  300. package/src/modules/forms/submission.ts +80 -29
  301. package/src/modules/handbook/events.ts +31 -0
  302. package/src/modules/handbook/index.ts +2 -0
  303. package/src/modules/handbook/service.ts +8 -15
  304. package/src/modules/hiring/candidates.ts +8 -11
  305. package/src/modules/hiring/events.ts +36 -0
  306. package/src/modules/hiring/index.ts +2 -0
  307. package/src/modules/hiring/roles.ts +8 -11
  308. package/src/modules/import-export/events.ts +25 -0
  309. package/src/modules/import-export/index.ts +2 -0
  310. package/src/modules/import-export/service.ts +83 -15
  311. package/src/modules/notes/events.ts +25 -0
  312. package/src/modules/notes/index.ts +2 -0
  313. package/src/modules/notes/service.ts +8 -7
  314. package/src/modules/opportunities/events.ts +34 -0
  315. package/src/modules/opportunities/index.ts +2 -0
  316. package/src/modules/opportunities/service.ts +17 -18
  317. package/src/modules/partnerships/events.ts +34 -0
  318. package/src/modules/partnerships/index.ts +2 -0
  319. package/src/modules/partnerships/service.ts +17 -18
  320. package/src/modules/people/events.ts +36 -0
  321. package/src/modules/people/index.ts +2 -0
  322. package/src/modules/people/service.ts +97 -87
  323. package/src/modules/pipelines/service.ts +47 -8
  324. package/src/modules/plans/events.ts +24 -0
  325. package/src/modules/plans/index.ts +2 -0
  326. package/src/modules/plans/service.ts +14 -14
  327. package/src/modules/positions/events.ts +25 -0
  328. package/src/modules/positions/index.ts +2 -0
  329. package/src/modules/positions/service.ts +8 -11
  330. package/src/modules/raises/events.ts +34 -0
  331. package/src/modules/raises/index.ts +2 -0
  332. package/src/modules/raises/service.ts +17 -18
  333. package/src/modules/reseal.ts +144 -1
  334. package/src/modules/smtp-email/index.ts +119 -0
  335. package/src/modules/webhooks/index.ts +5 -1
  336. package/src/modules/webhooks/payloads.ts +94 -48
  337. package/src/modules/workspace/events.ts +46 -0
  338. package/src/modules/workspace/index.ts +6 -2
  339. package/src/modules/workspace/service.ts +37 -15
  340. package/src/modules/workspace/workspaceAccessMiddleware.ts +12 -3
  341. package/src/runtime/events.ts +268 -107
  342. package/src/runtime/module.ts +55 -2
  343. package/src/runtime/registry.ts +169 -1
  344. package/src/runtime/transaction.ts +94 -17
  345. package/src/testing/app.ts +11 -3
  346. package/src/testing/database.ts +6 -3
  347. package/src/testing/services.ts +34 -5
@@ -12,8 +12,10 @@ import type { TransactionScope } from '../../runtime/transaction.ts'
12
12
  import type { ActivityRecorder } from '../activities/recorder.ts'
13
13
  import { describeLink, describeUnlink, describeUpdate } from '../activities/wording.ts'
14
14
  import type { FieldLabels } from '../activities/wording.ts'
15
+ import { toEventActor } from '../../lib/actor.ts'
15
16
  import type { Actor } from '../auth/actor.ts'
16
17
  import { requireWorkspaceId } from '../auth/actor.ts'
18
+ import './events.ts'
17
19
  import { deleteRecordsAttachedTo } from '../attachedRecords.ts'
18
20
  import * as repository from './repository.ts'
19
21
  import { DEFAULT_CANDIDATE_SORT, CANDIDATE_SORTS } from './repository.ts'
@@ -287,10 +289,10 @@ export function createCandidatesService(dependencies: CandidatesDependencies): C
287
289
  ...describeLink('role', roleTitle),
288
290
  })
289
291
 
290
- events.emit('record.created', { workspaceId, objectType: 'candidate', recordId: id })
292
+ events.emit('hiring.candidate.created', { type: 'candidate', id }, {})
291
293
 
292
294
  return toView(created)
293
- })
295
+ }, { workspaceId, actor: toEventActor(actor) })
294
296
  },
295
297
 
296
298
  async update(actor, id, changes) {
@@ -354,15 +356,10 @@ export function createCandidatesService(dependencies: CandidatesDependencies): C
354
356
  ),
355
357
  })
356
358
 
357
- events.emit('record.updated', {
358
- workspaceId,
359
- objectType: 'candidate',
360
- recordId: id,
361
- changedFields: changed,
362
- })
359
+ events.emit('hiring.candidate.updated', { type: 'candidate', id }, { changed })
363
360
 
364
361
  return toView(updated)
365
- })
362
+ }, { workspaceId, actor: toEventActor(actor) })
366
363
  },
367
364
 
368
365
  async remove(actor, id) {
@@ -384,8 +381,8 @@ export function createCandidatesService(dependencies: CandidatesDependencies): C
384
381
  ...describeUnlink('role', roleTitle),
385
382
  })
386
383
 
387
- events.emit('record.deleted', { workspaceId, objectType: 'candidate', recordId: id })
388
- })
384
+ events.emit('hiring.candidate.deleted', { type: 'candidate', id }, {})
385
+ }, { workspaceId, actor: toEventActor(actor) })
389
386
  },
390
387
  }
391
388
  }
@@ -0,0 +1,36 @@
1
+ import { z } from 'zod'
2
+
3
+ import type { ModuleEventCatalog } from '../../runtime/module.ts'
4
+
5
+ /** Domain events published by the hiring module (roles + candidates). */
6
+
7
+ export const hiringEvents = {
8
+ 'hiring.role.created': z.object({}).strict(),
9
+ 'hiring.role.updated': z.object({ changed: z.array(z.string()).readonly() }),
10
+ 'hiring.role.deleted': z.object({}).strict(),
11
+ 'hiring.candidate.created': z.object({}).strict(),
12
+ 'hiring.candidate.updated': z.object({ changed: z.array(z.string()).readonly() }),
13
+ 'hiring.candidate.deleted': z.object({}).strict(),
14
+ } satisfies ModuleEventCatalog
15
+
16
+ export type RoleCreatedData = Record<string, never>
17
+ export interface RoleUpdatedData {
18
+ readonly changed: readonly string[]
19
+ }
20
+ export type RoleDeletedData = Record<string, never>
21
+ export type CandidateCreatedData = Record<string, never>
22
+ export interface CandidateUpdatedData {
23
+ readonly changed: readonly string[]
24
+ }
25
+ export type CandidateDeletedData = Record<string, never>
26
+
27
+ declare module '../../runtime/events.ts' {
28
+ interface KelpieEventMap {
29
+ 'hiring.role.created': RoleCreatedData
30
+ 'hiring.role.updated': RoleUpdatedData
31
+ 'hiring.role.deleted': RoleDeletedData
32
+ 'hiring.candidate.created': CandidateCreatedData
33
+ 'hiring.candidate.updated': CandidateUpdatedData
34
+ 'hiring.candidate.deleted': CandidateDeletedData
35
+ }
36
+ }
@@ -1,6 +1,7 @@
1
1
  import type { KelpieModule } from '../../runtime/module.ts'
2
2
  import { createActivityRecorder } from '../activities/index.ts'
3
3
  import { createCandidatesService } from './candidates.ts'
4
+ import { hiringEvents } from './events.ts'
4
5
  import { createRolesService } from './roles.ts'
5
6
  import { mountHiringRoutes } from './routes.ts'
6
7
  import * as schema from './schema.ts'
@@ -22,6 +23,7 @@ export function createHiringModule(migrationsDirectory: string): KelpieModule {
22
23
  return {
23
24
  id: 'hiring',
24
25
  requires: ['people', 'activities'],
26
+ events: hiringEvents,
25
27
 
26
28
  register(context) {
27
29
  const recordActivity = createActivityRecorder({
@@ -9,8 +9,10 @@ import type { ListQueryParameters, Page } from '../../lib/pagination.ts'
9
9
  import type { TransactionScope } from '../../runtime/transaction.ts'
10
10
  import type { ActivityRecorder } from '../activities/recorder.ts'
11
11
  import { describeUnlink } from '../activities/wording.ts'
12
+ import { toEventActor } from '../../lib/actor.ts'
12
13
  import type { Actor } from '../auth/actor.ts'
13
14
  import { requireWorkspaceId } from '../auth/actor.ts'
15
+ import './events.ts'
14
16
  import { deleteRecordsAttachedTo } from '../attachedRecords.ts'
15
17
  import * as repository from './repository.ts'
16
18
  import { DEFAULT_ROLE_SORT, ROLE_SORTS } from './repository.ts'
@@ -103,10 +105,10 @@ export function createRolesService(dependencies: RolesDependencies): RolesServic
103
105
  status: input.status,
104
106
  })
105
107
 
106
- events.emit('record.created', { workspaceId, objectType: 'role', recordId: id })
108
+ events.emit('hiring.role.created', { type: 'role', id }, {})
107
109
 
108
110
  return toView(created)
109
- })
111
+ }, { workspaceId, actor: toEventActor(actor) })
110
112
  },
111
113
 
112
114
  async update(actor, id, changes) {
@@ -129,15 +131,10 @@ export function createRolesService(dependencies: RolesDependencies): RolesServic
129
131
  throw AppError.notFound('Role not found')
130
132
  }
131
133
 
132
- events.emit('record.updated', {
133
- workspaceId,
134
- objectType: 'role',
135
- recordId: id,
136
- changedFields: changed,
137
- })
134
+ events.emit('hiring.role.updated', { type: 'role', id }, { changed })
138
135
 
139
136
  return toView(updated)
140
- })
137
+ }, { workspaceId, actor: toEventActor(actor) })
141
138
  },
142
139
 
143
140
  /**
@@ -167,8 +164,8 @@ export function createRolesService(dependencies: RolesDependencies): RolesServic
167
164
 
168
165
  await repository.deleteRole(tx, workspaceId, id)
169
166
 
170
- events.emit('record.deleted', { workspaceId, objectType: 'role', recordId: id })
171
- })
167
+ events.emit('hiring.role.deleted', { type: 'role', id }, {})
168
+ }, { workspaceId, actor: toEventActor(actor) })
172
169
  },
173
170
  }
174
171
  }
@@ -0,0 +1,25 @@
1
+ import { z } from 'zod'
2
+
3
+ import type { ModuleEventCatalog } from '../../runtime/module.ts'
4
+
5
+ /**
6
+ * Domain events published by the import-export module.
7
+ *
8
+ * Record events (`people.person.created`, etc.) fire from this module during
9
+ * an import, but they are owned by the record's module and declared in its
10
+ * catalog. `imports.job.completed` is what this module owns.
11
+ */
12
+
13
+ export const importExportEvents = {
14
+ 'imports.job.completed': z.object({ object: z.string() }),
15
+ } satisfies ModuleEventCatalog
16
+
17
+ export interface ImportCompletedData {
18
+ readonly object: string
19
+ }
20
+
21
+ declare module '../../runtime/events.ts' {
22
+ interface KelpieEventMap {
23
+ 'imports.job.completed': ImportCompletedData
24
+ }
25
+ }
@@ -1,5 +1,6 @@
1
1
  import type { KelpieModule } from '../../runtime/module.ts'
2
2
  import { createActivityRecorder } from '../activities/index.ts'
3
+ import { importExportEvents } from './events.ts'
3
4
  import { mountImportExportRoutes } from './routes.ts'
4
5
  import * as schema from './schema.ts'
5
6
  import { createImportExportService } from './service.ts'
@@ -21,6 +22,7 @@ export function createImportExportModule(migrationsDirectory: string): KelpieMod
21
22
  return {
22
23
  id: 'import-export',
23
24
  requires: ['people', 'companies', 'positions', 'pipelines', 'deals', 'activities'],
25
+ events: importExportEvents,
24
26
 
25
27
  register(context) {
26
28
  const service = createImportExportService({
@@ -24,10 +24,78 @@ import { AppError, describeThrown } from '../../lib/errors.ts'
24
24
  import type { IdFactory } from '../../lib/ids.ts'
25
25
  import type { Logger } from '../../lib/logger.ts'
26
26
  import { normaliseDomain, normaliseEmail } from '../../lib/normalisation.ts'
27
- import type { Queryable, TransactionScope } from '../../runtime/transaction.ts'
27
+ import type { BufferedEvents, Queryable, TransactionScope } from '../../runtime/transaction.ts'
28
28
  import type { ActivityRecorder } from '../activities/recorder.ts'
29
+ import { toEventActor } from '../../lib/actor.ts'
30
+ import type { RecordObjectType } from '../../runtime/events.ts'
29
31
  import type { Actor } from '../auth/actor.ts'
30
32
  import { requireWorkspaceId } from '../auth/actor.ts'
33
+ import '../companies/events.ts'
34
+ import '../deals/events.ts'
35
+ import '../people/events.ts'
36
+ import '../positions/events.ts'
37
+ import './events.ts'
38
+
39
+ /**
40
+ * Fires the created event owned by the record's module for one imported row.
41
+ * Split from the loop so TypeScript can correlate the object type with its
42
+ * typed event name at each call site.
43
+ */
44
+ function emitImportedRecordCreated(
45
+ events: BufferedEvents,
46
+ objectType: RecordObjectType,
47
+ recordId: string,
48
+ ): void {
49
+ switch (objectType) {
50
+ case 'person':
51
+ events.emit('people.person.created', { type: 'person', id: recordId }, {})
52
+ return
53
+ case 'company':
54
+ events.emit('companies.company.created', { type: 'company', id: recordId }, {})
55
+ return
56
+ case 'position':
57
+ events.emit('positions.position.created', { type: 'position', id: recordId }, {})
58
+ return
59
+ case 'deal':
60
+ events.emit('deals.deal.created', { type: 'deal', id: recordId }, {})
61
+ return
62
+ // The remaining `RecordObjectType` values are not produced by an import.
63
+ default:
64
+ throw new Error(`import produced an unexpected object type: ${objectType}`)
65
+ }
66
+ }
67
+
68
+ function emitImportedRecordUpdated(
69
+ events: BufferedEvents,
70
+ objectType: RecordObjectType,
71
+ recordId: string,
72
+ changed: readonly string[],
73
+ ): void {
74
+ switch (objectType) {
75
+ case 'person':
76
+ events.emit('people.person.updated', { type: 'person', id: recordId }, { changed })
77
+ return
78
+ case 'company':
79
+ events.emit(
80
+ 'companies.company.updated',
81
+ { type: 'company', id: recordId },
82
+ { changed },
83
+ )
84
+ return
85
+ case 'position':
86
+ events.emit(
87
+ 'positions.position.updated',
88
+ { type: 'position', id: recordId },
89
+ { changed },
90
+ )
91
+ return
92
+ case 'deal':
93
+ events.emit('deals.deal.updated', { type: 'deal', id: recordId }, { changed })
94
+ return
95
+ default:
96
+ throw new Error(`import produced an unexpected object type: ${objectType}`)
97
+ }
98
+ }
31
99
  import { CsvFormatError, csvLine, fileDigest, parseCsv } from './csv.ts'
32
100
  import type { CsvRow, ParsedCsv } from './csv.ts'
33
101
  import { headersFor } from './exportRows.ts'
@@ -550,20 +618,16 @@ export function createImportExportService(
550
618
  )
551
619
 
552
620
  if (plan.action === 'create') {
553
- events.emit('record.created', {
554
- workspaceId,
555
- objectType: written.objectType,
556
- recordId: written.recordId,
557
- })
621
+ emitImportedRecordCreated(events, written.objectType, written.recordId)
558
622
  } else if (written.changedFields.length > 0) {
559
623
  // An update that moved nothing publishes nothing. Re-running the same
560
624
  // file must not wake every consumer watching for a change.
561
- events.emit('record.updated', {
562
- workspaceId,
563
- objectType: written.objectType,
564
- recordId: written.recordId,
565
- changedFields: written.changedFields,
566
- })
625
+ emitImportedRecordUpdated(
626
+ events,
627
+ written.objectType,
628
+ written.recordId,
629
+ written.changedFields,
630
+ )
567
631
  }
568
632
  }
569
633
 
@@ -579,7 +643,7 @@ export function createImportExportService(
579
643
  )
580
644
 
581
645
  return outcome
582
- })
646
+ }, { workspaceId, actor: toEventActor(actor) })
583
647
  }
584
648
 
585
649
  /**
@@ -658,10 +722,14 @@ export function createImportExportService(
658
722
  })
659
723
 
660
724
  await dependencies.transaction(({ events }) => {
661
- events.emit('import.completed', { workspaceId, importJobId: jobId, object: job.object })
725
+ events.emit(
726
+ 'imports.job.completed',
727
+ { type: 'import_job', id: jobId },
728
+ { object: job.object },
729
+ )
662
730
 
663
731
  return Promise.resolve()
664
- })
732
+ }, { workspaceId, actor: toEventActor(actor) })
665
733
  }
666
734
 
667
735
  /**
@@ -0,0 +1,25 @@
1
+ import { z } from 'zod'
2
+
3
+ import type { ModuleEventCatalog } from '../../runtime/module.ts'
4
+
5
+ /**
6
+ * Domain events published by the notes module.
7
+ *
8
+ * `target` is the record the note attaches to (a person, company, deal, ...).
9
+ * The note's own id lives in `data` so a consumer that watches records does
10
+ * not need a second lookup to know which record the note landed on.
11
+ */
12
+
13
+ export const notesEvents = {
14
+ 'notes.note.added': z.object({ noteId: z.string() }),
15
+ } satisfies ModuleEventCatalog
16
+
17
+ export interface NoteAddedData {
18
+ readonly noteId: string
19
+ }
20
+
21
+ declare module '../../runtime/events.ts' {
22
+ interface KelpieEventMap {
23
+ 'notes.note.added': NoteAddedData
24
+ }
25
+ }
@@ -1,5 +1,6 @@
1
1
  import type { KelpieModule } from '../../runtime/module.ts'
2
2
  import { createActivityRecorder } from '../activities/index.ts'
3
+ import { notesEvents } from './events.ts'
3
4
  import { mountNotesRoutes } from './routes.ts'
4
5
  import * as schema from './schema.ts'
5
6
  import { createNotesService } from './service.ts'
@@ -16,6 +17,7 @@ export function createNotesModule(migrationsDirectory: string): KelpieModule {
16
17
  id: 'notes',
17
18
  requires: ['workspace', 'activities'],
18
19
  structural: true,
20
+ events: notesEvents,
19
21
 
20
22
  register(context) {
21
23
  const service = createNotesService({
@@ -7,8 +7,10 @@ import type { ListQueryParameters, Page } from '../../lib/pagination.ts'
7
7
  import type { TransactionScope } from '../../runtime/transaction.ts'
8
8
  import { describeNote } from '../activities/wording.ts'
9
9
  import type { ActivityRecorder } from '../activities/recorder.ts'
10
+ import { toEventActor } from '../../lib/actor.ts'
10
11
  import type { Actor } from '../auth/actor.ts'
11
12
  import { actorMemberId, requireWorkspaceId } from '../auth/actor.ts'
13
+ import './events.ts'
12
14
  import { missingTargets } from '../recordTargets.ts'
13
15
  import type { RecordTargetType } from '../recordTargets.ts'
14
16
  import * as repository from './repository.ts'
@@ -145,15 +147,14 @@ export function createNotesService(dependencies: NotesDependencies): NotesServic
145
147
  // their own event, carrying the target, because a consumer watching a
146
148
  // record wants the note without a second lookup to find out what it is
147
149
  // attached to.
148
- events.emit('note.added', {
149
- workspaceId,
150
- noteId: created.id,
151
- targetType: created.targetType,
152
- targetId: created.targetId,
153
- })
150
+ events.emit(
151
+ 'notes.note.added',
152
+ { type: created.targetType, id: created.targetId },
153
+ { noteId: created.id },
154
+ )
154
155
 
155
156
  return toView(created)
156
- })
157
+ }, { workspaceId, actor: toEventActor(actor) })
157
158
  },
158
159
 
159
160
  async update(actor, id, changes) {
@@ -0,0 +1,34 @@
1
+ import { z } from 'zod'
2
+
3
+ import type { ModuleEventCatalog } from '../../runtime/module.ts'
4
+
5
+ /** Domain events published by the opportunities module. */
6
+
7
+ export const opportunitiesEvents = {
8
+ 'opportunities.opportunity.created': z.object({}).strict(),
9
+ 'opportunities.opportunity.updated': z.object({ changed: z.array(z.string()).readonly() }),
10
+ 'opportunities.opportunity.deleted': z.object({}).strict(),
11
+ 'opportunities.opportunity.stage_changed': z.object({
12
+ fromStageId: z.string().nullable(),
13
+ toStageId: z.string(),
14
+ }),
15
+ } satisfies ModuleEventCatalog
16
+
17
+ export type OpportunityCreatedData = Record<string, never>
18
+ export interface OpportunityUpdatedData {
19
+ readonly changed: readonly string[]
20
+ }
21
+ export type OpportunityDeletedData = Record<string, never>
22
+ export interface OpportunityStageChangedData {
23
+ readonly fromStageId: string | null
24
+ readonly toStageId: string
25
+ }
26
+
27
+ declare module '../../runtime/events.ts' {
28
+ interface KelpieEventMap {
29
+ 'opportunities.opportunity.created': OpportunityCreatedData
30
+ 'opportunities.opportunity.updated': OpportunityUpdatedData
31
+ 'opportunities.opportunity.deleted': OpportunityDeletedData
32
+ 'opportunities.opportunity.stage_changed': OpportunityStageChangedData
33
+ }
34
+ }
@@ -1,5 +1,6 @@
1
1
  import type { KelpieModule } from '../../runtime/module.ts'
2
2
  import { createActivityRecorder } from '../activities/index.ts'
3
+ import { opportunitiesEvents } from './events.ts'
3
4
  import { mountOpportunitiesRoutes } from './routes.ts'
4
5
  import * as schema from './schema.ts'
5
6
  import { createOpportunitiesService } from './service.ts'
@@ -17,6 +18,7 @@ export function createOpportunitiesModule(migrationsDirectory: string): KelpieMo
17
18
  return {
18
19
  id: 'opportunities',
19
20
  requires: ['companies', 'pipelines', 'activities'],
21
+ events: opportunitiesEvents,
20
22
 
21
23
  register(context) {
22
24
  const service = createOpportunitiesService({
@@ -8,8 +8,10 @@ import type { TransactionScope } from '../../runtime/transaction.ts'
8
8
  import type { ActivityRecorder } from '../activities/recorder.ts'
9
9
  import { describeCreation, describeStageChange, describeUpdate } from '../activities/wording.ts'
10
10
  import type { FieldLabels } from '../activities/wording.ts'
11
+ import { toEventActor } from '../../lib/actor.ts'
11
12
  import type { Actor } from '../auth/actor.ts'
12
13
  import { actorMemberId, requireWorkspaceId } from '../auth/actor.ts'
14
+ import './events.ts'
13
15
  import { deleteRecordsAttachedTo } from '../attachedRecords.ts'
14
16
  import * as companyRepository from '../companies/repository.ts'
15
17
  import * as pipelineRepository from '../pipelines/repository.ts'
@@ -236,10 +238,10 @@ export function createOpportunitiesService(
236
238
  ...describeCreation('Opportunity'),
237
239
  })
238
240
 
239
- events.emit('record.created', { workspaceId, objectType: 'opportunity', recordId: id })
241
+ events.emit('opportunities.opportunity.created', { type: 'opportunity', id }, {})
240
242
 
241
243
  return toView(created)
242
- })
244
+ }, { workspaceId, actor: toEventActor(actor) })
243
245
  },
244
246
 
245
247
  async update(actor, id, changes) {
@@ -302,25 +304,22 @@ export function createOpportunitiesService(
302
304
  })
303
305
  }
304
306
 
305
- events.emit('record.updated', {
306
- workspaceId,
307
- objectType: 'opportunity',
308
- recordId: id,
309
- changedFields: changed,
310
- })
307
+ events.emit(
308
+ 'opportunities.opportunity.updated',
309
+ { type: 'opportunity', id },
310
+ { changed },
311
+ )
311
312
 
312
313
  if (stageMove !== undefined) {
313
- events.emit('stage.changed', {
314
- workspaceId,
315
- objectType: 'opportunity',
316
- recordId: id,
317
- fromStageId: stageMove.from.id,
318
- toStageId: stageMove.to.id,
319
- })
314
+ events.emit(
315
+ 'opportunities.opportunity.stage_changed',
316
+ { type: 'opportunity', id },
317
+ { fromStageId: stageMove.from.id, toStageId: stageMove.to.id },
318
+ )
320
319
  }
321
320
 
322
321
  return toView(updated)
323
- })
322
+ }, { workspaceId, actor: toEventActor(actor) })
324
323
  },
325
324
 
326
325
  async remove(actor, id) {
@@ -335,8 +334,8 @@ export function createOpportunitiesService(
335
334
  await deleteRecordsAttachedTo(tx, workspaceId, 'opportunity', id)
336
335
  await repository.deleteOpportunity(tx, workspaceId, id)
337
336
 
338
- events.emit('record.deleted', { workspaceId, objectType: 'opportunity', recordId: id })
339
- })
337
+ events.emit('opportunities.opportunity.deleted', { type: 'opportunity', id }, {})
338
+ }, { workspaceId, actor: toEventActor(actor) })
340
339
  },
341
340
  }
342
341
  }
@@ -0,0 +1,34 @@
1
+ import { z } from 'zod'
2
+
3
+ import type { ModuleEventCatalog } from '../../runtime/module.ts'
4
+
5
+ /** Domain events published by the partnerships module. */
6
+
7
+ export const partnershipsEvents = {
8
+ 'partnerships.partnership.created': z.object({}).strict(),
9
+ 'partnerships.partnership.updated': z.object({ changed: z.array(z.string()).readonly() }),
10
+ 'partnerships.partnership.deleted': z.object({}).strict(),
11
+ 'partnerships.partnership.stage_changed': z.object({
12
+ fromStageId: z.string().nullable(),
13
+ toStageId: z.string(),
14
+ }),
15
+ } satisfies ModuleEventCatalog
16
+
17
+ export type PartnershipCreatedData = Record<string, never>
18
+ export interface PartnershipUpdatedData {
19
+ readonly changed: readonly string[]
20
+ }
21
+ export type PartnershipDeletedData = Record<string, never>
22
+ export interface PartnershipStageChangedData {
23
+ readonly fromStageId: string | null
24
+ readonly toStageId: string
25
+ }
26
+
27
+ declare module '../../runtime/events.ts' {
28
+ interface KelpieEventMap {
29
+ 'partnerships.partnership.created': PartnershipCreatedData
30
+ 'partnerships.partnership.updated': PartnershipUpdatedData
31
+ 'partnerships.partnership.deleted': PartnershipDeletedData
32
+ 'partnerships.partnership.stage_changed': PartnershipStageChangedData
33
+ }
34
+ }
@@ -1,5 +1,6 @@
1
1
  import type { KelpieModule } from '../../runtime/module.ts'
2
2
  import { createActivityRecorder } from '../activities/index.ts'
3
+ import { partnershipsEvents } from './events.ts'
3
4
  import { mountPartnershipsRoutes } from './routes.ts'
4
5
  import * as schema from './schema.ts'
5
6
  import { createPartnershipsService } from './service.ts'
@@ -17,6 +18,7 @@ export function createPartnershipsModule(migrationsDirectory: string): KelpieMod
17
18
  return {
18
19
  id: 'partnerships',
19
20
  requires: ['companies', 'people', 'pipelines', 'activities'],
21
+ events: partnershipsEvents,
20
22
 
21
23
  register(context) {
22
24
  const service = createPartnershipsService({
@@ -14,8 +14,10 @@ import {
14
14
  describeUpdate,
15
15
  } from '../activities/wording.ts'
16
16
  import type { FieldLabels } from '../activities/wording.ts'
17
+ import { toEventActor } from '../../lib/actor.ts'
17
18
  import type { Actor } from '../auth/actor.ts'
18
19
  import { actorMemberId, requireWorkspaceId } from '../auth/actor.ts'
20
+ import './events.ts'
19
21
  import { deleteRecordsAttachedTo } from '../attachedRecords.ts'
20
22
  import * as companyRepository from '../companies/repository.ts'
21
23
  import * as pipelineRepository from '../pipelines/repository.ts'
@@ -301,10 +303,10 @@ export function createPartnershipsService(
301
303
  })
302
304
  }
303
305
 
304
- events.emit('record.created', { workspaceId, objectType: 'partnership', recordId: id })
306
+ events.emit('partnerships.partnership.created', { type: 'partnership', id }, {})
305
307
 
306
308
  return toView(created, personIds)
307
- })
309
+ }, { workspaceId, actor: toEventActor(actor) })
308
310
  },
309
311
 
310
312
  async update(actor, id, changes) {
@@ -402,25 +404,22 @@ export function createPartnershipsService(
402
404
  })
403
405
  }
404
406
 
405
- events.emit('record.updated', {
406
- workspaceId,
407
- objectType: 'partnership',
408
- recordId: id,
409
- changedFields: linksChanged ? [...changed, 'personIds'] : changed,
410
- })
407
+ events.emit(
408
+ 'partnerships.partnership.updated',
409
+ { type: 'partnership', id },
410
+ { changed: linksChanged ? [...changed, 'personIds'] : changed },
411
+ )
411
412
 
412
413
  if (stageMove !== undefined) {
413
- events.emit('stage.changed', {
414
- workspaceId,
415
- objectType: 'partnership',
416
- recordId: id,
417
- fromStageId: stageMove.from.id,
418
- toStageId: stageMove.to.id,
419
- })
414
+ events.emit(
415
+ 'partnerships.partnership.stage_changed',
416
+ { type: 'partnership', id },
417
+ { fromStageId: stageMove.from.id, toStageId: stageMove.to.id },
418
+ )
420
419
  }
421
420
 
422
421
  return toView(updated, nextPeople ?? currentPeople)
423
- })
422
+ }, { workspaceId, actor: toEventActor(actor) })
424
423
  },
425
424
 
426
425
  async remove(actor, id) {
@@ -435,8 +434,8 @@ export function createPartnershipsService(
435
434
  await deleteRecordsAttachedTo(tx, workspaceId, 'partnership', id)
436
435
  await repository.deletePartnership(tx, workspaceId, id)
437
436
 
438
- events.emit('record.deleted', { workspaceId, objectType: 'partnership', recordId: id })
439
- })
437
+ events.emit('partnerships.partnership.deleted', { type: 'partnership', id }, {})
438
+ }, { workspaceId, actor: toEventActor(actor) })
440
439
  },
441
440
  }
442
441
  }