@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
@@ -1,16 +1,19 @@
1
- import { and, count, eq, inArray } from 'drizzle-orm';
1
+ import { and, eq, inArray } from 'drizzle-orm';
2
2
  import { toEventActor } from '../../lib/actor.js';
3
+ import { UNIQUE_VIOLATION, postgresErrorCode } from '../../lib/database.js';
3
4
  import { AppError } from '../../lib/errors.js';
5
+ import { moduleCapabilityName } from '../../runtime/moduleConfig.js';
4
6
  import { requireWorkspaceId } from '../auth/actor.js';
5
7
  import * as authRepository from '../auth/repository.js';
6
8
  import { companies } from '../companies/schema.js';
7
9
  import { deals } from '../deals/schema.js';
8
- import { candidates, roles } from '../hiring/schema.js';
9
- import { notes } from '../notes/schema.js';
10
10
  import { enquiries } from '../enquiries/schema.js';
11
+ import { attendances, eventAssociations, events } from '../events/schema.js';
12
+ import { candidates, roles } from '../hiring/schema.js';
11
13
  import { opportunities } from '../opportunities/schema.js';
12
14
  import { partnerships } from '../partnerships/schema.js';
13
15
  import { people, personLinks } from '../people/schema.js';
16
+ import { notes } from '../notes/schema.js';
14
17
  import { pipelineStages } from '../pipelines/schema.js';
15
18
  import { planItems } from '../plans/schema.js';
16
19
  import { positions } from '../positions/schema.js';
@@ -44,6 +47,10 @@ export function createSampleDataService(dependencies) {
44
47
  throw new AppError('forbidden', 'This action needs the admin role');
45
48
  }
46
49
  }
50
+ async function moduleIsOn(workspaceId, moduleId) {
51
+ const entitlement = await dependencies.entitlements.check(workspaceId, moduleCapabilityName(moduleId));
52
+ return entitlement.kind === 'flag' ? entitlement.granted : true;
53
+ }
47
54
  return {
48
55
  async install(actor, workspaceId) {
49
56
  // The path parameter must match the actor's workspace, and it must be an
@@ -52,381 +59,473 @@ export function createSampleDataService(dependencies) {
52
59
  requireWorkspaceId(actor);
53
60
  await requireAdmin(actor, workspaceId);
54
61
  const fixture = SAMPLE_DATA_FIXTURE;
55
- return dependencies.transaction(async ({ tx }) => {
56
- const [companiesTally] = await tx
57
- .select({ value: count() })
58
- .from(companies)
59
- .where(eq(companies.workspaceId, workspaceId));
60
- const [peopleTally] = await tx
61
- .select({ value: count() })
62
- .from(people)
63
- .where(eq(people.workspaceId, workspaceId));
64
- const alreadyHasData = (companiesTally?.value ?? 0) > 0 || (peopleTally?.value ?? 0) > 0;
65
- if (alreadyHasData) {
66
- throw AppError.conflict('This workspace already has CRM data');
67
- }
68
- // Every stage this workspace carries, grouped by kind so a deal, an
69
- // opportunity, a raise and a partnership all resolve their fixture
70
- // stage slugs against the same read.
71
- const allStages = await tx
72
- .select({
73
- id: pipelineStages.id,
74
- slug: pipelineStages.slug,
75
- kind: pipelineStages.kind,
76
- })
77
- .from(pipelineStages)
78
- .where(and(eq(pipelineStages.workspaceId, workspaceId), inArray(pipelineStages.kind, [
79
- 'deal',
80
- 'opportunity',
81
- 'raise',
82
- 'partnership',
83
- 'enquiry',
84
- ])));
85
- const stageIdByKindAndSlug = new Map();
86
- for (const row of allStages) {
87
- stageIdByKindAndSlug.set(stageLookupKey(row.kind, row.slug), row.id);
88
- }
89
- const now = dependencies.now();
90
- const companyIds = new Map();
91
- const personIds = new Map();
92
- const dealIds = new Map();
93
- const opportunityIds = new Map();
94
- const raiseIds = new Map();
95
- const partnershipIds = new Map();
96
- const enquiryIds = new Map();
97
- const roleIds = new Map();
98
- const candidateIds = new Map();
99
- for (const record of fixture.companies) {
100
- const id = dependencies.createId('company');
101
- companyIds.set(record.key, id);
102
- await tx.insert(companies).values({
103
- id,
104
- workspaceId,
105
- name: record.name,
106
- domain: record.domain,
107
- industry: record.industry,
108
- description: record.description,
109
- stage: record.stage,
110
- sizeBand: record.sizeBand,
111
- hq: record.hq,
112
- website: record.website,
113
- accountType: record.accountType,
114
- icpFit: record.icpFit,
115
- techStack: [...record.techStack],
116
- summary: record.summary,
117
- tags: [...record.tags],
118
- createdAt: now,
119
- updatedAt: now,
120
- });
121
- }
122
- for (const record of fixture.people) {
123
- const id = dependencies.createId('person');
124
- personIds.set(record.key, id);
125
- await tx.insert(people).values({
126
- id,
127
- workspaceId,
128
- name: record.name,
129
- firstName: record.firstName,
130
- lastName: record.lastName,
131
- email: record.email,
132
- location: record.location,
133
- preferredChannel: record.preferredChannel,
134
- influence: record.influence,
135
- relationship: record.relationship,
136
- summary: record.summary,
137
- tags: [...record.tags],
138
- createdAt: now,
139
- updatedAt: now,
140
- });
141
- }
142
- for (const record of fixture.positions) {
143
- const personId = personIds.get(record.personKey);
144
- const companyId = companyIds.get(record.companyKey);
145
- if (personId === undefined || companyId === undefined) {
146
- throw new Error(`Sample position links unknown keys: person=${record.personKey}, company=${record.companyKey}`);
62
+ const includeDeals = await moduleIsOn(workspaceId, 'deals');
63
+ const includeOpportunities = await moduleIsOn(workspaceId, 'opportunities');
64
+ const includeRaises = await moduleIsOn(workspaceId, 'raises');
65
+ const includePartnerships = await moduleIsOn(workspaceId, 'partnerships');
66
+ const includeEvents = await moduleIsOn(workspaceId, 'events');
67
+ try {
68
+ return await dependencies.transaction(async ({ tx }) => {
69
+ // Every stage this workspace carries, grouped by kind so a deal, an
70
+ // opportunity, a raise and a partnership all resolve their fixture
71
+ // stage slugs against the same read.
72
+ const allStages = await tx
73
+ .select({
74
+ id: pipelineStages.id,
75
+ slug: pipelineStages.slug,
76
+ kind: pipelineStages.kind,
77
+ })
78
+ .from(pipelineStages)
79
+ .where(and(eq(pipelineStages.workspaceId, workspaceId), inArray(pipelineStages.kind, [
80
+ 'deal',
81
+ 'opportunity',
82
+ 'raise',
83
+ 'partnership',
84
+ 'enquiry',
85
+ ])));
86
+ const stageIdByKindAndSlug = new Map();
87
+ for (const row of allStages) {
88
+ stageIdByKindAndSlug.set(stageLookupKey(row.kind, row.slug), row.id);
147
89
  }
148
- await tx.insert(positions).values({
149
- id: dependencies.createId('position'),
150
- workspaceId,
151
- personId,
152
- companyId,
153
- title: record.title,
154
- createdAt: now,
155
- updatedAt: now,
156
- });
157
- }
158
- for (const record of fixture.deals) {
159
- const stageId = stageIdByKindAndSlug.get(stageLookupKey('deal', record.stageSlug));
160
- const companyId = companyIds.get(record.companyKey);
161
- if (stageId === undefined) {
162
- throw new Error(`Sample deal "${record.name}" names stage "${record.stageSlug}", which this workspace's deal pipeline does not carry`);
90
+ const now = dependencies.now();
91
+ const companyIds = new Map();
92
+ const personIds = new Map();
93
+ const dealIds = new Map();
94
+ const opportunityIds = new Map();
95
+ const raiseIds = new Map();
96
+ const partnershipIds = new Map();
97
+ const enquiryIds = new Map();
98
+ const roleIds = new Map();
99
+ const candidateIds = new Map();
100
+ let planItemCount = 0;
101
+ let notesWritten = 0;
102
+ let eventCount = 0;
103
+ let attendanceCount = 0;
104
+ for (const record of fixture.companies) {
105
+ const id = dependencies.createId('company');
106
+ companyIds.set(record.key, id);
107
+ await tx.insert(companies).values({
108
+ id,
109
+ workspaceId,
110
+ name: record.name,
111
+ domain: record.domain,
112
+ industry: record.industry,
113
+ description: record.description,
114
+ stage: record.stage,
115
+ sizeBand: record.sizeBand,
116
+ addresses: [...record.addresses],
117
+ accountType: record.accountType,
118
+ icpFit: record.icpFit,
119
+ techStack: [...record.techStack],
120
+ summary: record.summary,
121
+ tags: [...record.tags],
122
+ createdAt: now,
123
+ updatedAt: now,
124
+ });
163
125
  }
164
- if (companyId === undefined) {
165
- throw new Error(`Sample deal "${record.name}" names unknown company "${record.companyKey}"`);
126
+ for (const record of fixture.people) {
127
+ const id = dependencies.createId('person');
128
+ personIds.set(record.key, id);
129
+ await tx.insert(people).values({
130
+ id,
131
+ workspaceId,
132
+ name: record.name,
133
+ firstName: record.firstName,
134
+ lastName: record.lastName,
135
+ email: record.email,
136
+ addresses: [...record.addresses],
137
+ preferredChannel: record.preferredChannel,
138
+ influence: record.influence,
139
+ relationship: record.relationship,
140
+ summary: record.summary,
141
+ tags: [...record.tags],
142
+ createdAt: now,
143
+ updatedAt: now,
144
+ });
166
145
  }
167
- const id = dependencies.createId('deal');
168
- dealIds.set(record.key, id);
169
- await tx.insert(deals).values({
170
- id,
171
- workspaceId,
172
- name: record.name,
173
- companyId,
174
- stageId,
175
- valueCents: record.valueCents,
176
- currency: record.currency,
177
- expectedClose: record.expectedClose,
178
- competitors: [],
179
- risks: record.risks,
180
- whyWin: record.whyWin,
181
- summary: record.summary,
182
- tags: [...record.tags],
183
- createdAt: now,
184
- updatedAt: now,
185
- });
186
- for (const personKey of record.peopleKeys) {
187
- const personId = personIds.get(personKey);
188
- if (personId === undefined) {
189
- throw new Error(`Sample deal "${record.name}" names unknown person "${personKey}"`);
146
+ for (const record of fixture.positions) {
147
+ const personId = personIds.get(record.personKey);
148
+ const companyId = companyIds.get(record.companyKey);
149
+ if (personId === undefined || companyId === undefined) {
150
+ throw new Error(`Sample position links unknown keys: person=${record.personKey}, company=${record.companyKey}`);
190
151
  }
191
- await tx.insert(personLinks).values({
192
- id: dependencies.createId('personLink'),
152
+ await tx.insert(positions).values({
153
+ id: dependencies.createId('position'),
193
154
  workspaceId,
194
155
  personId,
195
- targetType: 'deal',
196
- targetId: id,
156
+ companyId,
157
+ title: record.title,
158
+ createdAt: now,
159
+ updatedAt: now,
197
160
  });
198
161
  }
199
- }
200
- for (const record of fixture.opportunities) {
201
- const stageId = stageIdByKindAndSlug.get(stageLookupKey('opportunity', record.stageSlug));
202
- if (stageId === undefined) {
203
- throw new Error(`Sample opportunity "${record.name}" names stage "${record.stageSlug}", which this workspace's opportunity pipeline does not carry`);
162
+ if (includeDeals) {
163
+ for (const record of fixture.deals) {
164
+ const stageId = stageIdByKindAndSlug.get(stageLookupKey('deal', record.stageSlug));
165
+ const companyId = companyIds.get(record.companyKey);
166
+ if (stageId === undefined) {
167
+ throw new Error(`Sample deal "${record.name}" names stage "${record.stageSlug}", which this workspace's deal pipeline does not carry`);
168
+ }
169
+ if (companyId === undefined) {
170
+ throw new Error(`Sample deal "${record.name}" names unknown company "${record.companyKey}"`);
171
+ }
172
+ const id = dependencies.createId('deal');
173
+ dealIds.set(record.key, id);
174
+ await tx.insert(deals).values({
175
+ id,
176
+ workspaceId,
177
+ name: record.name,
178
+ companyId,
179
+ stageId,
180
+ valueCents: record.valueCents,
181
+ currency: record.currency,
182
+ expectedClose: record.expectedClose,
183
+ competitors: [],
184
+ risks: record.risks,
185
+ whyWin: record.whyWin,
186
+ summary: record.summary,
187
+ tags: [...record.tags],
188
+ createdAt: now,
189
+ updatedAt: now,
190
+ });
191
+ for (const personKey of record.peopleKeys) {
192
+ const personId = personIds.get(personKey);
193
+ if (personId === undefined) {
194
+ throw new Error(`Sample deal "${record.name}" names unknown person "${personKey}"`);
195
+ }
196
+ await tx.insert(personLinks).values({
197
+ id: dependencies.createId('personLink'),
198
+ workspaceId,
199
+ personId,
200
+ targetType: 'deal',
201
+ targetId: id,
202
+ });
203
+ }
204
+ }
204
205
  }
205
- const companyId = record.companyKey === null ? null : (companyIds.get(record.companyKey) ?? undefined);
206
- if (companyId === undefined) {
207
- throw new Error(`Sample opportunity "${record.name}" names unknown company "${String(record.companyKey)}"`);
206
+ if (includeOpportunities) {
207
+ for (const record of fixture.opportunities) {
208
+ const stageId = stageIdByKindAndSlug.get(stageLookupKey('opportunity', record.stageSlug));
209
+ if (stageId === undefined) {
210
+ throw new Error(`Sample opportunity "${record.name}" names stage "${record.stageSlug}", which this workspace's opportunity pipeline does not carry`);
211
+ }
212
+ const companyId = record.companyKey === null ? null : (companyIds.get(record.companyKey) ?? undefined);
213
+ if (companyId === undefined) {
214
+ throw new Error(`Sample opportunity "${record.name}" names unknown company "${String(record.companyKey)}"`);
215
+ }
216
+ const id = dependencies.createId('opportunity');
217
+ opportunityIds.set(record.key, id);
218
+ await tx.insert(opportunities).values({
219
+ id,
220
+ workspaceId,
221
+ name: record.name,
222
+ kind: record.kind,
223
+ stageId,
224
+ companyId,
225
+ expectedClose: record.expectedClose,
226
+ summary: record.summary,
227
+ tags: [...record.tags],
228
+ createdAt: now,
229
+ updatedAt: now,
230
+ });
231
+ }
208
232
  }
209
- const id = dependencies.createId('opportunity');
210
- opportunityIds.set(record.key, id);
211
- await tx.insert(opportunities).values({
212
- id,
213
- workspaceId,
214
- name: record.name,
215
- kind: record.kind,
216
- stageId,
217
- companyId,
218
- expectedClose: record.expectedClose,
219
- summary: record.summary,
220
- tags: [...record.tags],
221
- createdAt: now,
222
- updatedAt: now,
223
- });
224
- }
225
- for (const record of fixture.raises) {
226
- const stageId = stageIdByKindAndSlug.get(stageLookupKey('raise', record.stageSlug));
227
- const companyId = companyIds.get(record.companyKey);
228
- if (stageId === undefined) {
229
- throw new Error(`Sample raise "${record.name}" names stage "${record.stageSlug}", which this workspace's raise pipeline does not carry`);
233
+ if (includeRaises) {
234
+ for (const record of fixture.raises) {
235
+ const stageId = stageIdByKindAndSlug.get(stageLookupKey('raise', record.stageSlug));
236
+ const companyId = companyIds.get(record.companyKey);
237
+ if (stageId === undefined) {
238
+ throw new Error(`Sample raise "${record.name}" names stage "${record.stageSlug}", which this workspace's raise pipeline does not carry`);
239
+ }
240
+ if (companyId === undefined) {
241
+ throw new Error(`Sample raise "${record.name}" names unknown company "${record.companyKey}"`);
242
+ }
243
+ const id = dependencies.createId('raise');
244
+ raiseIds.set(record.key, id);
245
+ await tx.insert(raises).values({
246
+ id,
247
+ workspaceId,
248
+ name: record.name,
249
+ companyId,
250
+ stageId,
251
+ checkSizeCents: record.checkSizeCents,
252
+ currency: record.currency,
253
+ thesisFit: record.thesisFit,
254
+ expectedClose: record.expectedClose,
255
+ summary: record.summary,
256
+ tags: [...record.tags],
257
+ createdAt: now,
258
+ updatedAt: now,
259
+ });
260
+ for (const personKey of record.peopleKeys) {
261
+ const personId = personIds.get(personKey);
262
+ if (personId === undefined) {
263
+ throw new Error(`Sample raise "${record.name}" names unknown person "${personKey}"`);
264
+ }
265
+ await tx.insert(personLinks).values({
266
+ id: dependencies.createId('personLink'),
267
+ workspaceId,
268
+ personId,
269
+ targetType: 'raise',
270
+ targetId: id,
271
+ });
272
+ }
273
+ }
230
274
  }
231
- if (companyId === undefined) {
232
- throw new Error(`Sample raise "${record.name}" names unknown company "${record.companyKey}"`);
275
+ if (includePartnerships) {
276
+ for (const record of fixture.partnerships) {
277
+ const stageId = stageIdByKindAndSlug.get(stageLookupKey('partnership', record.stageSlug));
278
+ const companyId = companyIds.get(record.companyKey);
279
+ if (stageId === undefined) {
280
+ throw new Error(`Sample partnership "${record.name}" names stage "${record.stageSlug}", which this workspace's partnership pipeline does not carry`);
281
+ }
282
+ if (companyId === undefined) {
283
+ throw new Error(`Sample partnership "${record.name}" names unknown company "${record.companyKey}"`);
284
+ }
285
+ const id = dependencies.createId('partnership');
286
+ partnershipIds.set(record.key, id);
287
+ await tx.insert(partnerships).values({
288
+ id,
289
+ workspaceId,
290
+ name: record.name,
291
+ companyId,
292
+ stageId,
293
+ kind: record.kind,
294
+ nextTouchpoint: record.nextTouchpoint,
295
+ goals: record.goals,
296
+ successLooksLike: record.successLooksLike,
297
+ summary: record.summary,
298
+ tags: [...record.tags],
299
+ createdAt: now,
300
+ updatedAt: now,
301
+ });
302
+ for (const personKey of record.peopleKeys) {
303
+ const personId = personIds.get(personKey);
304
+ if (personId === undefined) {
305
+ throw new Error(`Sample partnership "${record.name}" names unknown person "${personKey}"`);
306
+ }
307
+ await tx.insert(personLinks).values({
308
+ id: dependencies.createId('personLink'),
309
+ workspaceId,
310
+ personId,
311
+ targetType: 'partnership',
312
+ targetId: id,
313
+ });
314
+ }
315
+ }
233
316
  }
234
- const id = dependencies.createId('raise');
235
- raiseIds.set(record.key, id);
236
- await tx.insert(raises).values({
237
- id,
238
- workspaceId,
239
- name: record.name,
240
- companyId,
241
- stageId,
242
- checkSizeCents: record.checkSizeCents,
243
- currency: record.currency,
244
- thesisFit: record.thesisFit,
245
- expectedClose: record.expectedClose,
246
- summary: record.summary,
247
- tags: [...record.tags],
248
- createdAt: now,
249
- updatedAt: now,
250
- });
251
- for (const personKey of record.peopleKeys) {
252
- const personId = personIds.get(personKey);
253
- if (personId === undefined) {
254
- throw new Error(`Sample raise "${record.name}" names unknown person "${personKey}"`);
317
+ for (const record of fixture.enquiries) {
318
+ const stageId = stageIdByKindAndSlug.get(stageLookupKey('enquiry', record.stageSlug));
319
+ if (stageId === undefined) {
320
+ throw new Error(`Sample enquiry "${record.name}" names stage "${record.stageSlug}", which this workspace's enquiry pipeline does not carry`);
321
+ }
322
+ const companyId = record.companyKey === null ? null : (companyIds.get(record.companyKey) ?? undefined);
323
+ if (companyId === undefined) {
324
+ throw new Error(`Sample enquiry "${record.name}" names unknown company "${String(record.companyKey)}"`);
255
325
  }
256
- await tx.insert(personLinks).values({
257
- id: dependencies.createId('personLink'),
326
+ const id = dependencies.createId('enquiry');
327
+ enquiryIds.set(record.key, id);
328
+ await tx.insert(enquiries).values({
329
+ id,
258
330
  workspaceId,
259
- personId,
260
- targetType: 'raise',
261
- targetId: id,
331
+ name: record.name,
332
+ source: record.source,
333
+ stageId,
334
+ companyId,
335
+ summary: record.summary,
336
+ tags: [...record.tags],
337
+ createdAt: now,
338
+ updatedAt: now,
262
339
  });
340
+ for (const personKey of record.peopleKeys) {
341
+ const personId = personIds.get(personKey);
342
+ if (personId === undefined) {
343
+ throw new Error(`Sample enquiry "${record.name}" names unknown person "${personKey}"`);
344
+ }
345
+ await tx.insert(personLinks).values({
346
+ id: dependencies.createId('personLink'),
347
+ workspaceId,
348
+ personId,
349
+ targetType: 'enquiry',
350
+ targetId: id,
351
+ });
352
+ }
263
353
  }
264
- }
265
- for (const record of fixture.partnerships) {
266
- const stageId = stageIdByKindAndSlug.get(stageLookupKey('partnership', record.stageSlug));
267
- const companyId = companyIds.get(record.companyKey);
268
- if (stageId === undefined) {
269
- throw new Error(`Sample partnership "${record.name}" names stage "${record.stageSlug}", which this workspace's partnership pipeline does not carry`);
270
- }
271
- if (companyId === undefined) {
272
- throw new Error(`Sample partnership "${record.name}" names unknown company "${record.companyKey}"`);
354
+ for (const record of fixture.roles) {
355
+ const id = dependencies.createId('role');
356
+ roleIds.set(record.key, id);
357
+ await tx.insert(roles).values({
358
+ id,
359
+ workspaceId,
360
+ title: record.title,
361
+ status: record.status,
362
+ createdAt: now,
363
+ updatedAt: now,
364
+ });
273
365
  }
274
- const id = dependencies.createId('partnership');
275
- partnershipIds.set(record.key, id);
276
- await tx.insert(partnerships).values({
277
- id,
278
- workspaceId,
279
- name: record.name,
280
- companyId,
281
- stageId,
282
- kind: record.kind,
283
- nextTouchpoint: record.nextTouchpoint,
284
- goals: record.goals,
285
- successLooksLike: record.successLooksLike,
286
- summary: record.summary,
287
- tags: [...record.tags],
288
- createdAt: now,
289
- updatedAt: now,
290
- });
291
- for (const personKey of record.peopleKeys) {
292
- const personId = personIds.get(personKey);
366
+ for (const record of fixture.candidates) {
367
+ const roleId = roleIds.get(record.roleKey);
368
+ const personId = personIds.get(record.personKey);
369
+ const referrerPersonId = record.referrerPersonKey === null
370
+ ? null
371
+ : (personIds.get(record.referrerPersonKey) ?? undefined);
372
+ if (roleId === undefined) {
373
+ throw new Error(`Sample candidate "${record.key}" names unknown role "${record.roleKey}"`);
374
+ }
293
375
  if (personId === undefined) {
294
- throw new Error(`Sample partnership "${record.name}" names unknown person "${personKey}"`);
376
+ throw new Error(`Sample candidate "${record.key}" names unknown person "${record.personKey}"`);
377
+ }
378
+ if (referrerPersonId === undefined) {
379
+ throw new Error(`Sample candidate "${record.key}" names unknown referrer "${String(record.referrerPersonKey)}"`);
295
380
  }
296
- await tx.insert(personLinks).values({
297
- id: dependencies.createId('personLink'),
381
+ const id = dependencies.createId('candidate');
382
+ candidateIds.set(record.key, id);
383
+ await tx.insert(candidates).values({
384
+ id,
298
385
  workspaceId,
386
+ roleId,
299
387
  personId,
300
- targetType: 'partnership',
301
- targetId: id,
388
+ status: record.status,
389
+ // The interview_stage column carries a value only while the
390
+ // candidate is in process. Every other status clears it, which
391
+ // the service does the same way `hiring` does for a PATCH.
392
+ interviewStage: record.status === 'in_process' ? record.interviewStage : null,
393
+ referrerPersonId,
394
+ createdAt: now,
395
+ updatedAt: now,
302
396
  });
303
397
  }
304
- }
305
- for (const record of fixture.enquiries) {
306
- const stageId = stageIdByKindAndSlug.get(stageLookupKey('enquiry', record.stageSlug));
307
- if (stageId === undefined) {
308
- throw new Error(`Sample enquiry "${record.name}" names stage "${record.stageSlug}", which this workspace's enquiry pipeline does not carry`);
309
- }
310
- const companyId = record.companyKey === null ? null : (companyIds.get(record.companyKey) ?? undefined);
311
- if (companyId === undefined) {
312
- throw new Error(`Sample enquiry "${record.name}" names unknown company "${String(record.companyKey)}"`);
398
+ if (includeEvents) {
399
+ for (const record of fixture.events) {
400
+ const startsAt = new Date(now.getTime() + record.startOffsetHours * 60 * 60 * 1000);
401
+ const endsAt = new Date(startsAt.getTime() + record.durationHours * 60 * 60 * 1000);
402
+ const id = dependencies.createId('crmEvent');
403
+ eventCount += 1;
404
+ await tx.insert(events).values({
405
+ id,
406
+ workspaceId,
407
+ name: record.name,
408
+ kind: record.kind,
409
+ startsAt,
410
+ endsAt,
411
+ timezone: record.timezone,
412
+ location: record.location,
413
+ meetingUrl: record.meetingUrl,
414
+ format: record.format,
415
+ details: record.details,
416
+ status: record.status,
417
+ summary: record.summary,
418
+ tags: [...record.tags],
419
+ createdAt: now,
420
+ updatedAt: now,
421
+ });
422
+ if (record.dealKey !== null && includeDeals) {
423
+ const dealId = dealIds.get(record.dealKey);
424
+ if (dealId === undefined) {
425
+ throw new Error(`Sample event "${record.name}" names unknown deal "${record.dealKey}"`);
426
+ }
427
+ await tx.insert(eventAssociations).values({
428
+ id: dependencies.createId('eventAssociation'),
429
+ workspaceId,
430
+ eventId: id,
431
+ targetType: 'deal',
432
+ targetId: dealId,
433
+ createdAt: now,
434
+ });
435
+ }
436
+ for (const attendee of record.attendees) {
437
+ const personId = personIds.get(attendee.personKey);
438
+ if (personId === undefined) {
439
+ throw new Error(`Sample event "${record.name}" names unknown person "${attendee.personKey}"`);
440
+ }
441
+ attendanceCount += 1;
442
+ await tx.insert(attendances).values({
443
+ id: dependencies.createId('attendance'),
444
+ workspaceId,
445
+ eventId: id,
446
+ personId,
447
+ status: attendee.status,
448
+ source: attendee.source,
449
+ createdAt: now,
450
+ updatedAt: now,
451
+ });
452
+ }
453
+ }
313
454
  }
314
- const id = dependencies.createId('enquiry');
315
- enquiryIds.set(record.key, id);
316
- await tx.insert(enquiries).values({
317
- id,
318
- workspaceId,
319
- name: record.name,
320
- source: record.source,
321
- stageId,
322
- companyId,
323
- summary: record.summary,
324
- tags: [...record.tags],
325
- createdAt: now,
326
- updatedAt: now,
327
- });
328
- for (const personKey of record.peopleKeys) {
329
- const personId = personIds.get(personKey);
330
- if (personId === undefined) {
331
- throw new Error(`Sample enquiry "${record.name}" names unknown person "${personKey}"`);
455
+ for (const record of fixture.plans) {
456
+ if (!includeDeals) {
457
+ continue;
458
+ }
459
+ const dealId = dealIds.get(record.targetDealKey);
460
+ if (dealId === undefined) {
461
+ throw new Error(`Sample plan item "${record.title}" names unknown deal "${record.targetDealKey}"`);
332
462
  }
333
- await tx.insert(personLinks).values({
334
- id: dependencies.createId('personLink'),
463
+ planItemCount += 1;
464
+ await tx.insert(planItems).values({
465
+ id: dependencies.createId('planItem'),
335
466
  workspaceId,
336
- personId,
337
- targetType: 'enquiry',
338
- targetId: id,
467
+ targetType: record.targetType,
468
+ targetId: dealId,
469
+ date: record.date,
470
+ title: record.title,
471
+ status: record.status,
472
+ createdAt: now,
473
+ updatedAt: now,
339
474
  });
340
475
  }
341
- }
342
- for (const record of fixture.roles) {
343
- const id = dependencies.createId('role');
344
- roleIds.set(record.key, id);
345
- await tx.insert(roles).values({
346
- id,
347
- workspaceId,
348
- title: record.title,
349
- status: record.status,
350
- createdAt: now,
351
- updatedAt: now,
352
- });
353
- }
354
- for (const record of fixture.candidates) {
355
- const roleId = roleIds.get(record.roleKey);
356
- const personId = personIds.get(record.personKey);
357
- const referrerPersonId = record.referrerPersonKey === null
358
- ? null
359
- : (personIds.get(record.referrerPersonKey) ?? undefined);
360
- if (roleId === undefined) {
361
- throw new Error(`Sample candidate "${record.key}" names unknown role "${record.roleKey}"`);
362
- }
363
- if (personId === undefined) {
364
- throw new Error(`Sample candidate "${record.key}" names unknown person "${record.personKey}"`);
365
- }
366
- if (referrerPersonId === undefined) {
367
- throw new Error(`Sample candidate "${record.key}" names unknown referrer "${String(record.referrerPersonKey)}"`);
368
- }
369
- const id = dependencies.createId('candidate');
370
- candidateIds.set(record.key, id);
371
- await tx.insert(candidates).values({
372
- id,
373
- workspaceId,
374
- roleId,
375
- personId,
376
- status: record.status,
377
- // The interview_stage column carries a value only while the
378
- // candidate is in process. Every other status clears it, which
379
- // the service does the same way `hiring` does for a PATCH.
380
- interviewStage: record.status === 'in_process' ? record.interviewStage : null,
381
- referrerPersonId,
382
- createdAt: now,
383
- updatedAt: now,
384
- });
385
- }
386
- for (const record of fixture.plans) {
387
- const dealId = dealIds.get(record.targetDealKey);
388
- if (dealId === undefined) {
389
- throw new Error(`Sample plan item "${record.title}" names unknown deal "${record.targetDealKey}"`);
390
- }
391
- await tx.insert(planItems).values({
392
- id: dependencies.createId('planItem'),
393
- workspaceId,
394
- targetType: record.targetType,
395
- targetId: dealId,
396
- date: record.date,
397
- title: record.title,
398
- status: record.status,
399
- createdAt: now,
400
- updatedAt: now,
401
- });
402
- }
403
- for (const record of fixture.notes) {
404
- const targetId = resolveNoteTargetId(record.targetType, record.targetKey, {
405
- companyIds,
406
- personIds,
407
- dealIds,
408
- opportunityIds,
409
- raiseIds,
410
- partnershipIds,
411
- enquiryIds,
412
- candidateIds,
413
- });
414
- if (targetId === undefined) {
415
- throw new Error(`Sample note names unknown ${record.targetType} "${record.targetKey}"`);
476
+ for (const record of fixture.notes) {
477
+ const targetId = resolveNoteTargetId(record.targetType, record.targetKey, {
478
+ companyIds,
479
+ personIds,
480
+ dealIds,
481
+ opportunityIds,
482
+ raiseIds,
483
+ partnershipIds,
484
+ enquiryIds,
485
+ candidateIds,
486
+ });
487
+ if (targetId === undefined) {
488
+ if (isOptionalModuleNoteTarget(record.targetType)) {
489
+ continue;
490
+ }
491
+ throw new Error(`Sample note names unknown ${record.targetType} "${record.targetKey}"`);
492
+ }
493
+ notesWritten += 1;
494
+ await tx.insert(notes).values({
495
+ id: dependencies.createId('note'),
496
+ workspaceId,
497
+ targetType: record.targetType,
498
+ targetId,
499
+ body: record.body,
500
+ pinned: record.pinned,
501
+ createdAt: now,
502
+ updatedAt: now,
503
+ });
416
504
  }
417
- await tx.insert(notes).values({
418
- id: dependencies.createId('note'),
419
- workspaceId,
420
- targetType: record.targetType,
421
- targetId,
422
- body: record.body,
423
- pinned: record.pinned,
424
- createdAt: now,
425
- updatedAt: now,
426
- });
505
+ return {
506
+ companies: fixture.companies.length,
507
+ people: fixture.people.length,
508
+ positions: fixture.positions.length,
509
+ deals: dealIds.size,
510
+ planItems: planItemCount,
511
+ notes: notesWritten,
512
+ opportunities: opportunityIds.size,
513
+ raises: raiseIds.size,
514
+ partnerships: partnershipIds.size,
515
+ enquiries: enquiryIds.size,
516
+ roles: roleIds.size,
517
+ candidates: candidateIds.size,
518
+ events: eventCount,
519
+ attendances: attendanceCount,
520
+ };
521
+ }, { workspaceId, actor: toEventActor(actor) });
522
+ }
523
+ catch (error) {
524
+ if (postgresErrorCode(error) === UNIQUE_VIOLATION) {
525
+ throw AppError.conflict('A company domain or person email in the sample set already exists in this workspace');
427
526
  }
428
- return countsFor(fixture);
429
- }, { workspaceId, actor: toEventActor(actor) });
527
+ throw error;
528
+ }
430
529
  },
431
530
  };
432
531
  }
@@ -462,20 +561,10 @@ function resolveNoteTargetId(targetType, targetKey, maps) {
462
561
  function stageLookupKey(kind, slug) {
463
562
  return `${kind}::${slug}`;
464
563
  }
465
- function countsFor(fixture) {
466
- return {
467
- companies: fixture.companies.length,
468
- people: fixture.people.length,
469
- positions: fixture.positions.length,
470
- deals: fixture.deals.length,
471
- planItems: fixture.plans.length,
472
- notes: fixture.notes.length,
473
- opportunities: fixture.opportunities.length,
474
- raises: fixture.raises.length,
475
- partnerships: fixture.partnerships.length,
476
- enquiries: fixture.enquiries.length,
477
- roles: fixture.roles.length,
478
- candidates: fixture.candidates.length,
479
- };
564
+ function isOptionalModuleNoteTarget(targetType) {
565
+ return (targetType === 'deal' ||
566
+ targetType === 'opportunity' ||
567
+ targetType === 'raise' ||
568
+ targetType === 'partnership');
480
569
  }
481
570
  //# sourceMappingURL=service.js.map