@lhremote/core 0.7.0 → 0.8.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 (288) hide show
  1. package/dist/db/index.d.ts +1 -1
  2. package/dist/db/index.d.ts.map +1 -1
  3. package/dist/db/index.js +1 -1
  4. package/dist/db/index.js.map +1 -1
  5. package/dist/db/repositories/action-budget.d.ts +29 -0
  6. package/dist/db/repositories/action-budget.d.ts.map +1 -0
  7. package/dist/db/repositories/action-budget.js +100 -0
  8. package/dist/db/repositories/action-budget.js.map +1 -0
  9. package/dist/db/repositories/action-budget.test.d.ts +2 -0
  10. package/dist/db/repositories/action-budget.test.d.ts.map +1 -0
  11. package/dist/db/repositories/action-budget.test.js +181 -0
  12. package/dist/db/repositories/action-budget.test.js.map +1 -0
  13. package/dist/db/repositories/campaign-hard-delete.integration.test.d.ts +2 -0
  14. package/dist/db/repositories/campaign-hard-delete.integration.test.d.ts.map +1 -0
  15. package/dist/db/repositories/campaign-hard-delete.integration.test.js +78 -0
  16. package/dist/db/repositories/campaign-hard-delete.integration.test.js.map +1 -0
  17. package/dist/db/repositories/campaign.d.ts +14 -0
  18. package/dist/db/repositories/campaign.d.ts.map +1 -1
  19. package/dist/db/repositories/campaign.js +125 -0
  20. package/dist/db/repositories/campaign.js.map +1 -1
  21. package/dist/db/repositories/index.d.ts +1 -0
  22. package/dist/db/repositories/index.d.ts.map +1 -1
  23. package/dist/db/repositories/index.js +1 -0
  24. package/dist/db/repositories/index.js.map +1 -1
  25. package/dist/index.d.ts +5 -4
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +24 -4
  28. package/dist/index.js.map +1 -1
  29. package/dist/linkedin/__tests__/dom-automation.integration.test.d.ts +2 -0
  30. package/dist/linkedin/__tests__/dom-automation.integration.test.d.ts.map +1 -0
  31. package/dist/linkedin/__tests__/dom-automation.integration.test.js +160 -0
  32. package/dist/linkedin/__tests__/dom-automation.integration.test.js.map +1 -0
  33. package/dist/linkedin/__tests__/selectors.integration.test.d.ts +2 -0
  34. package/dist/linkedin/__tests__/selectors.integration.test.d.ts.map +1 -0
  35. package/dist/linkedin/__tests__/selectors.integration.test.js +258 -0
  36. package/dist/linkedin/__tests__/selectors.integration.test.js.map +1 -0
  37. package/dist/linkedin/dom-automation.d.ts +67 -0
  38. package/dist/linkedin/dom-automation.d.ts.map +1 -0
  39. package/dist/linkedin/dom-automation.js +139 -0
  40. package/dist/linkedin/dom-automation.js.map +1 -0
  41. package/dist/linkedin/index.d.ts +3 -0
  42. package/dist/linkedin/index.d.ts.map +1 -0
  43. package/dist/linkedin/index.js +5 -0
  44. package/dist/linkedin/index.js.map +1 -0
  45. package/dist/linkedin/selectors.d.ts +67 -0
  46. package/dist/linkedin/selectors.d.ts.map +1 -0
  47. package/dist/linkedin/selectors.js +74 -0
  48. package/dist/linkedin/selectors.js.map +1 -0
  49. package/dist/operations/campaign-delete.d.ts +6 -2
  50. package/dist/operations/campaign-delete.d.ts.map +1 -1
  51. package/dist/operations/campaign-delete.js +9 -2
  52. package/dist/operations/campaign-delete.js.map +1 -1
  53. package/dist/operations/campaign-delete.test.js +41 -1
  54. package/dist/operations/campaign-delete.test.js.map +1 -1
  55. package/dist/operations/campaign-erase.d.ts +19 -0
  56. package/dist/operations/campaign-erase.d.ts.map +1 -0
  57. package/dist/operations/campaign-erase.js +22 -0
  58. package/dist/operations/campaign-erase.js.map +1 -0
  59. package/dist/operations/campaign-erase.test.d.ts +2 -0
  60. package/dist/operations/campaign-erase.test.d.ts.map +1 -0
  61. package/dist/operations/campaign-erase.test.js +101 -0
  62. package/dist/operations/campaign-erase.test.js.map +1 -0
  63. package/dist/operations/comment-on-post.d.ts +34 -0
  64. package/dist/operations/comment-on-post.d.ts.map +1 -0
  65. package/dist/operations/comment-on-post.js +108 -0
  66. package/dist/operations/comment-on-post.js.map +1 -0
  67. package/dist/operations/comment-on-post.test.d.ts +2 -0
  68. package/dist/operations/comment-on-post.test.d.ts.map +1 -0
  69. package/dist/operations/comment-on-post.test.js +240 -0
  70. package/dist/operations/comment-on-post.test.js.map +1 -0
  71. package/dist/operations/dismiss-errors.d.ts +22 -0
  72. package/dist/operations/dismiss-errors.d.ts.map +1 -0
  73. package/dist/operations/dismiss-errors.js +55 -0
  74. package/dist/operations/dismiss-errors.js.map +1 -0
  75. package/dist/operations/dismiss-errors.test.d.ts +2 -0
  76. package/dist/operations/dismiss-errors.test.d.ts.map +1 -0
  77. package/dist/operations/dismiss-errors.test.js +136 -0
  78. package/dist/operations/dismiss-errors.test.js.map +1 -0
  79. package/dist/operations/endorse-skills.d.ts +10 -0
  80. package/dist/operations/endorse-skills.d.ts.map +1 -0
  81. package/dist/operations/endorse-skills.js +12 -0
  82. package/dist/operations/endorse-skills.js.map +1 -0
  83. package/dist/operations/enrich-profile.d.ts +18 -0
  84. package/dist/operations/enrich-profile.d.ts.map +1 -0
  85. package/dist/operations/enrich-profile.js +15 -0
  86. package/dist/operations/enrich-profile.js.map +1 -0
  87. package/dist/operations/ephemeral-action.d.ts +22 -0
  88. package/dist/operations/ephemeral-action.d.ts.map +1 -0
  89. package/dist/operations/ephemeral-action.js +31 -0
  90. package/dist/operations/ephemeral-action.js.map +1 -0
  91. package/dist/operations/follow-person.d.ts +9 -0
  92. package/dist/operations/follow-person.d.ts.map +1 -0
  93. package/dist/operations/follow-person.js +11 -0
  94. package/dist/operations/follow-person.js.map +1 -0
  95. package/dist/operations/get-action-budget.d.ts +6 -0
  96. package/dist/operations/get-action-budget.d.ts.map +1 -0
  97. package/dist/operations/get-action-budget.js +22 -0
  98. package/dist/operations/get-action-budget.js.map +1 -0
  99. package/dist/operations/get-action-budget.test.d.ts +2 -0
  100. package/dist/operations/get-action-budget.test.d.ts.map +1 -0
  101. package/dist/operations/get-action-budget.test.js +79 -0
  102. package/dist/operations/get-action-budget.test.js.map +1 -0
  103. package/dist/operations/get-errors.d.ts +9 -3
  104. package/dist/operations/get-errors.d.ts.map +1 -1
  105. package/dist/operations/get-errors.js +41 -4
  106. package/dist/operations/get-errors.js.map +1 -1
  107. package/dist/operations/get-errors.test.js +108 -42
  108. package/dist/operations/get-errors.test.js.map +1 -1
  109. package/dist/operations/get-feed.d.ts +33 -0
  110. package/dist/operations/get-feed.d.ts.map +1 -0
  111. package/dist/operations/get-feed.js +220 -0
  112. package/dist/operations/get-feed.js.map +1 -0
  113. package/dist/operations/get-feed.test.d.ts +2 -0
  114. package/dist/operations/get-feed.test.d.ts.map +1 -0
  115. package/dist/operations/get-feed.test.js +276 -0
  116. package/dist/operations/get-feed.test.js.map +1 -0
  117. package/dist/operations/get-post-engagers.d.ts +40 -0
  118. package/dist/operations/get-post-engagers.d.ts.map +1 -0
  119. package/dist/operations/get-post-engagers.js +128 -0
  120. package/dist/operations/get-post-engagers.js.map +1 -0
  121. package/dist/operations/get-post-engagers.test.d.ts +2 -0
  122. package/dist/operations/get-post-engagers.test.d.ts.map +1 -0
  123. package/dist/operations/get-post-engagers.test.js +19 -0
  124. package/dist/operations/get-post-engagers.test.js.map +1 -0
  125. package/dist/operations/get-post-stats.d.ts +38 -0
  126. package/dist/operations/get-post-stats.d.ts.map +1 -0
  127. package/dist/operations/get-post-stats.js +100 -0
  128. package/dist/operations/get-post-stats.js.map +1 -0
  129. package/dist/operations/get-post-stats.test.d.ts +2 -0
  130. package/dist/operations/get-post-stats.test.d.ts.map +1 -0
  131. package/dist/operations/get-post-stats.test.js +34 -0
  132. package/dist/operations/get-post-stats.test.js.map +1 -0
  133. package/dist/operations/get-post.d.ts +183 -0
  134. package/dist/operations/get-post.d.ts.map +1 -0
  135. package/dist/operations/get-post.js +270 -0
  136. package/dist/operations/get-post.js.map +1 -0
  137. package/dist/operations/get-post.test.d.ts +2 -0
  138. package/dist/operations/get-post.test.d.ts.map +1 -0
  139. package/dist/operations/get-post.test.js +449 -0
  140. package/dist/operations/get-post.test.js.map +1 -0
  141. package/dist/operations/get-profile-activity.d.ts +129 -0
  142. package/dist/operations/get-profile-activity.d.ts.map +1 -0
  143. package/dist/operations/get-profile-activity.js +181 -0
  144. package/dist/operations/get-profile-activity.js.map +1 -0
  145. package/dist/operations/get-profile-activity.test.d.ts +2 -0
  146. package/dist/operations/get-profile-activity.test.d.ts.map +1 -0
  147. package/dist/operations/get-profile-activity.test.js +205 -0
  148. package/dist/operations/get-profile-activity.test.js.map +1 -0
  149. package/dist/operations/get-throttle-status.d.ts +6 -0
  150. package/dist/operations/get-throttle-status.d.ts.map +1 -0
  151. package/dist/operations/get-throttle-status.js +30 -0
  152. package/dist/operations/get-throttle-status.js.map +1 -0
  153. package/dist/operations/get-throttle-status.test.d.ts +2 -0
  154. package/dist/operations/get-throttle-status.test.d.ts.map +1 -0
  155. package/dist/operations/get-throttle-status.test.js +62 -0
  156. package/dist/operations/get-throttle-status.test.js.map +1 -0
  157. package/dist/operations/index.d.ts +22 -0
  158. package/dist/operations/index.d.ts.map +1 -1
  159. package/dist/operations/index.js +32 -0
  160. package/dist/operations/index.js.map +1 -1
  161. package/dist/operations/like-person-posts.d.ts +14 -0
  162. package/dist/operations/like-person-posts.d.ts.map +1 -0
  163. package/dist/operations/like-person-posts.js +28 -0
  164. package/dist/operations/like-person-posts.js.map +1 -0
  165. package/dist/operations/message-person.d.ts +11 -0
  166. package/dist/operations/message-person.d.ts.map +1 -0
  167. package/dist/operations/message-person.js +19 -0
  168. package/dist/operations/message-person.js.map +1 -0
  169. package/dist/operations/react-to-post.d.ts +38 -0
  170. package/dist/operations/react-to-post.d.ts.map +1 -0
  171. package/dist/operations/react-to-post.js +82 -0
  172. package/dist/operations/react-to-post.js.map +1 -0
  173. package/dist/operations/react-to-post.test.d.ts +2 -0
  174. package/dist/operations/react-to-post.test.d.ts.map +1 -0
  175. package/dist/operations/react-to-post.test.js +154 -0
  176. package/dist/operations/react-to-post.test.js.map +1 -0
  177. package/dist/operations/remove-connection.d.ts +6 -0
  178. package/dist/operations/remove-connection.d.ts.map +1 -0
  179. package/dist/operations/remove-connection.js +7 -0
  180. package/dist/operations/remove-connection.js.map +1 -0
  181. package/dist/operations/search-posts.d.ts +143 -0
  182. package/dist/operations/search-posts.d.ts.map +1 -0
  183. package/dist/operations/search-posts.js +220 -0
  184. package/dist/operations/search-posts.js.map +1 -0
  185. package/dist/operations/search-posts.test.d.ts +2 -0
  186. package/dist/operations/search-posts.test.d.ts.map +1 -0
  187. package/dist/operations/search-posts.test.js +261 -0
  188. package/dist/operations/search-posts.test.js.map +1 -0
  189. package/dist/operations/send-inmail.d.ts +11 -0
  190. package/dist/operations/send-inmail.d.ts.map +1 -0
  191. package/dist/operations/send-inmail.js +19 -0
  192. package/dist/operations/send-inmail.js.map +1 -0
  193. package/dist/operations/send-invite.d.ts +9 -0
  194. package/dist/operations/send-invite.d.ts.map +1 -0
  195. package/dist/operations/send-invite.js +14 -0
  196. package/dist/operations/send-invite.js.map +1 -0
  197. package/dist/operations/visit-profile.d.ts +14 -0
  198. package/dist/operations/visit-profile.d.ts.map +1 -0
  199. package/dist/operations/visit-profile.js +49 -0
  200. package/dist/operations/visit-profile.js.map +1 -0
  201. package/dist/operations/visit-profile.test.d.ts +2 -0
  202. package/dist/operations/visit-profile.test.d.ts.map +1 -0
  203. package/dist/operations/visit-profile.test.js +193 -0
  204. package/dist/operations/visit-profile.test.js.map +1 -0
  205. package/dist/services/campaign.d.ts +10 -0
  206. package/dist/services/campaign.d.ts.map +1 -1
  207. package/dist/services/campaign.js +16 -0
  208. package/dist/services/campaign.js.map +1 -1
  209. package/dist/services/collection.d.ts +5 -2
  210. package/dist/services/collection.d.ts.map +1 -1
  211. package/dist/services/collection.js +24 -5
  212. package/dist/services/collection.js.map +1 -1
  213. package/dist/services/collection.test.js +51 -9
  214. package/dist/services/collection.test.js.map +1 -1
  215. package/dist/services/ephemeral-campaign.d.ts +64 -0
  216. package/dist/services/ephemeral-campaign.d.ts.map +1 -0
  217. package/dist/services/ephemeral-campaign.js +210 -0
  218. package/dist/services/ephemeral-campaign.js.map +1 -0
  219. package/dist/services/ephemeral-campaign.test.d.ts +2 -0
  220. package/dist/services/ephemeral-campaign.test.d.ts.map +1 -0
  221. package/dist/services/ephemeral-campaign.test.js +333 -0
  222. package/dist/services/ephemeral-campaign.test.js.map +1 -0
  223. package/dist/services/errors.d.ts +10 -0
  224. package/dist/services/errors.d.ts.map +1 -1
  225. package/dist/services/errors.js +20 -0
  226. package/dist/services/errors.js.map +1 -1
  227. package/dist/services/index.d.ts +3 -1
  228. package/dist/services/index.d.ts.map +1 -1
  229. package/dist/services/index.js +3 -1
  230. package/dist/services/index.js.map +1 -1
  231. package/dist/services/instance-context.d.ts.map +1 -1
  232. package/dist/services/instance-context.js +9 -1
  233. package/dist/services/instance-context.js.map +1 -1
  234. package/dist/services/instance-context.test.js +77 -1
  235. package/dist/services/instance-context.test.js.map +1 -1
  236. package/dist/services/instance.d.ts +31 -0
  237. package/dist/services/instance.d.ts.map +1 -1
  238. package/dist/services/instance.js +123 -0
  239. package/dist/services/instance.js.map +1 -1
  240. package/dist/services/instance.test.js +70 -0
  241. package/dist/services/instance.test.js.map +1 -1
  242. package/dist/services/launcher.d.ts +7 -0
  243. package/dist/services/launcher.d.ts.map +1 -1
  244. package/dist/services/launcher.js +18 -1
  245. package/dist/services/launcher.js.map +1 -1
  246. package/dist/types/action-budget.d.ts +52 -0
  247. package/dist/types/action-budget.d.ts.map +1 -0
  248. package/dist/types/action-budget.js +4 -0
  249. package/dist/types/action-budget.js.map +1 -0
  250. package/dist/types/campaign.d.ts +24 -0
  251. package/dist/types/campaign.d.ts.map +1 -1
  252. package/dist/types/feed.d.ts +32 -0
  253. package/dist/types/feed.d.ts.map +1 -0
  254. package/dist/types/feed.js +4 -0
  255. package/dist/types/feed.js.map +1 -0
  256. package/dist/types/index.d.ts +7 -2
  257. package/dist/types/index.d.ts.map +1 -1
  258. package/dist/types/post-analytics.d.ts +40 -0
  259. package/dist/types/post-analytics.d.ts.map +1 -0
  260. package/dist/types/post-analytics.js +4 -0
  261. package/dist/types/post-analytics.js.map +1 -0
  262. package/dist/types/post.d.ts +43 -0
  263. package/dist/types/post.d.ts.map +1 -0
  264. package/dist/types/post.js +4 -0
  265. package/dist/types/post.js.map +1 -0
  266. package/dist/types/search-posts.d.ts +22 -0
  267. package/dist/types/search-posts.d.ts.map +1 -0
  268. package/dist/types/search-posts.js +4 -0
  269. package/dist/types/search-posts.js.map +1 -0
  270. package/dist/types/ui-health.d.ts +16 -0
  271. package/dist/types/ui-health.d.ts.map +1 -1
  272. package/dist/voyager/index.d.ts +2 -0
  273. package/dist/voyager/index.d.ts.map +1 -0
  274. package/dist/voyager/index.js +4 -0
  275. package/dist/voyager/index.js.map +1 -0
  276. package/dist/voyager/interceptor.d.ts +100 -0
  277. package/dist/voyager/interceptor.d.ts.map +1 -0
  278. package/dist/voyager/interceptor.integration.test.d.ts +2 -0
  279. package/dist/voyager/interceptor.integration.test.d.ts.map +1 -0
  280. package/dist/voyager/interceptor.integration.test.js +89 -0
  281. package/dist/voyager/interceptor.integration.test.js.map +1 -0
  282. package/dist/voyager/interceptor.js +235 -0
  283. package/dist/voyager/interceptor.js.map +1 -0
  284. package/dist/voyager/interceptor.test.d.ts +2 -0
  285. package/dist/voyager/interceptor.test.d.ts.map +1 -0
  286. package/dist/voyager/interceptor.test.js +372 -0
  287. package/dist/voyager/interceptor.test.js.map +1 -0
  288. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  export { DatabaseClient, type DatabaseClientOptions } from "./client.js";
2
2
  export { discoverAllDatabases, discoverDatabase } from "./discovery.js";
3
3
  export { ActionNotFoundError, CampaignNotFoundError, ChatNotFoundError, DatabaseError, DatabaseNotFoundError, ExcludeListNotFoundError, NoNextActionError, ProfileNotFoundError, } from "./errors.js";
4
- export { CampaignExcludeListRepository, CampaignRepository, CampaignStatisticsRepository, CollectionListRepository, type CollectionSummary, MessageRepository, ProfileRepository, } from "./repositories/index.js";
4
+ export { ActionBudgetRepository, CampaignExcludeListRepository, CampaignRepository, CampaignStatisticsRepository, CollectionListRepository, type CollectionSummary, MessageRepository, ProfileRepository, } from "./repositories/index.js";
5
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACxE,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,iBAAiB,EACjB,aAAa,EACb,qBAAqB,EACrB,wBAAwB,EACxB,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,6BAA6B,EAC7B,kBAAkB,EAClB,4BAA4B,EAC5B,wBAAwB,EACxB,KAAK,iBAAiB,EACtB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACxE,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,iBAAiB,EACjB,aAAa,EACb,qBAAqB,EACrB,wBAAwB,EACxB,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,sBAAsB,EACtB,6BAA6B,EAC7B,kBAAkB,EAClB,4BAA4B,EAC5B,wBAAwB,EACxB,KAAK,iBAAiB,EACtB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,yBAAyB,CAAC"}
package/dist/db/index.js CHANGED
@@ -3,5 +3,5 @@
3
3
  export { DatabaseClient } from "./client.js";
4
4
  export { discoverAllDatabases, discoverDatabase } from "./discovery.js";
5
5
  export { ActionNotFoundError, CampaignNotFoundError, ChatNotFoundError, DatabaseError, DatabaseNotFoundError, ExcludeListNotFoundError, NoNextActionError, ProfileNotFoundError, } from "./errors.js";
6
- export { CampaignExcludeListRepository, CampaignRepository, CampaignStatisticsRepository, CollectionListRepository, MessageRepository, ProfileRepository, } from "./repositories/index.js";
6
+ export { ActionBudgetRepository, CampaignExcludeListRepository, CampaignRepository, CampaignStatisticsRepository, CollectionListRepository, MessageRepository, ProfileRepository, } from "./repositories/index.js";
7
7
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,oCAAoC;AAEpC,OAAO,EAAE,cAAc,EAA8B,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACxE,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,iBAAiB,EACjB,aAAa,EACb,qBAAqB,EACrB,wBAAwB,EACxB,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,6BAA6B,EAC7B,kBAAkB,EAClB,4BAA4B,EAC5B,wBAAwB,EAExB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,oCAAoC;AAEpC,OAAO,EAAE,cAAc,EAA8B,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACxE,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,iBAAiB,EACjB,aAAa,EACb,qBAAqB,EACrB,wBAAwB,EACxB,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,sBAAsB,EACtB,6BAA6B,EAC7B,kBAAkB,EAClB,4BAA4B,EAC5B,wBAAwB,EAExB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,29 @@
1
+ import type { ActionBudgetEntry, LimitType } from "../../types/index.js";
2
+ import type { DatabaseClient } from "../client.js";
3
+ /**
4
+ * Repository for reading LinkedHelper rate limit data.
5
+ *
6
+ * Reads from LH's `limit_types`, `daily_limits`, and `action_results`
7
+ * tables to compute the current action budget.
8
+ */
9
+ export declare class ActionBudgetRepository {
10
+ private readonly stmtLimitTypes;
11
+ private readonly stmtDailyLimits;
12
+ private readonly stmtTodayCountsByType;
13
+ constructor(client: DatabaseClient);
14
+ /**
15
+ * Get all limit types defined by LinkedHelper.
16
+ */
17
+ getLimitTypes(): LimitType[];
18
+ /**
19
+ * Get the full action budget: limit types, daily limits, and today's usage.
20
+ *
21
+ * Campaign usage comes from LH's `action_results` table.
22
+ * Direct usage (CDP-direct actions like comments/reactions) is accepted
23
+ * as a parameter since it is tracked externally.
24
+ *
25
+ * @param directCounts Map of limit type ID → count of CDP-direct actions today.
26
+ */
27
+ getActionBudget(directCounts?: ReadonlyMap<number, number>): ActionBudgetEntry[];
28
+ }
29
+ //# sourceMappingURL=action-budget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action-budget.d.ts","sourceRoot":"","sources":["../../../src/db/repositories/action-budget.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACzE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAwCnD;;;;;GAKG;AACH,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;IAChC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC;IACjC,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAC;gBAE3B,MAAM,EAAE,cAAc;IAqBlC;;OAEG;IACH,aAAa,IAAI,SAAS,EAAE;IAK5B;;;;;;;;OAQG;IACH,eAAe,CACb,YAAY,GAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAa,GACpD,iBAAiB,EAAE;CAyCvB"}
@@ -0,0 +1,100 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ /**
4
+ * Maps LinkedHelper action config types to their corresponding limit type IDs.
5
+ *
6
+ * These IDs match the `limit_types` table in the LH database.
7
+ * Action types that do not consume daily limits (e.g. Waiter,
8
+ * FilterContactsOutOfMyNetwork) are intentionally omitted.
9
+ */
10
+ const ACTION_TYPE_TO_LIMIT_TYPE = new Map([
11
+ ["SaveCurrentProfile", 1],
12
+ ["InvitePerson", 8],
13
+ ["SendMessage", 9],
14
+ ["MessageToEventAttendees", 10],
15
+ ["SendInMail", 11],
16
+ ["EndorseSkills", 12],
17
+ ["InviteToGroup", 13],
18
+ ["Follow", 16],
19
+ ["Unfollow", 17],
20
+ ["LikePost", 18],
21
+ ["SendPersonToWebhook", 23],
22
+ ["GetEmailFromPAS", 24],
23
+ ["SendPersonToExternalCRM", 31],
24
+ ]);
25
+ /**
26
+ * Repository for reading LinkedHelper rate limit data.
27
+ *
28
+ * Reads from LH's `limit_types`, `daily_limits`, and `action_results`
29
+ * tables to compute the current action budget.
30
+ */
31
+ export class ActionBudgetRepository {
32
+ stmtLimitTypes;
33
+ stmtDailyLimits;
34
+ stmtTodayCountsByType;
35
+ constructor(client) {
36
+ const { db } = client;
37
+ this.stmtLimitTypes = db.prepare(`SELECT id, type FROM limit_types ORDER BY id`);
38
+ this.stmtDailyLimits = db.prepare(`SELECT id, max_limit FROM daily_limits`);
39
+ this.stmtTodayCountsByType = db.prepare(`SELECT ac.actionType AS action_type, COUNT(*) AS count_today
40
+ FROM action_results ar
41
+ JOIN action_versions av ON ar.action_version_id = av.id
42
+ JOIN action_configs ac ON av.config_id = ac.id
43
+ WHERE date(ar.created_at) = date('now', 'localtime')
44
+ GROUP BY ac.actionType`);
45
+ }
46
+ /**
47
+ * Get all limit types defined by LinkedHelper.
48
+ */
49
+ getLimitTypes() {
50
+ const rows = this.stmtLimitTypes.all();
51
+ return rows.map((r) => ({ id: r.id, type: r.type }));
52
+ }
53
+ /**
54
+ * Get the full action budget: limit types, daily limits, and today's usage.
55
+ *
56
+ * Campaign usage comes from LH's `action_results` table.
57
+ * Direct usage (CDP-direct actions like comments/reactions) is accepted
58
+ * as a parameter since it is tracked externally.
59
+ *
60
+ * @param directCounts Map of limit type ID → count of CDP-direct actions today.
61
+ */
62
+ getActionBudget(directCounts = new Map()) {
63
+ const limitTypes = this.stmtLimitTypes.all();
64
+ const dailyLimits = this.stmtDailyLimits.all();
65
+ const todayCounts = this.stmtTodayCountsByType.all();
66
+ // Build daily limit lookup: limit_type_id → max_limit
67
+ const limitMap = new Map();
68
+ for (const dl of dailyLimits) {
69
+ limitMap.set(dl.id, dl.max_limit);
70
+ }
71
+ // Build campaign usage lookup: limit_type_id → count
72
+ const campaignUsageMap = new Map();
73
+ for (const row of todayCounts) {
74
+ const limitTypeId = ACTION_TYPE_TO_LIMIT_TYPE.get(row.action_type);
75
+ if (limitTypeId !== undefined) {
76
+ const existing = campaignUsageMap.get(limitTypeId) ?? 0;
77
+ campaignUsageMap.set(limitTypeId, existing + row.count_today);
78
+ }
79
+ }
80
+ return limitTypes.map((lt) => {
81
+ const dailyLimit = limitMap.get(lt.id) ?? null;
82
+ const campaignUsed = campaignUsageMap.get(lt.id) ?? 0;
83
+ const directUsed = directCounts.get(lt.id) ?? 0;
84
+ const totalUsed = campaignUsed + directUsed;
85
+ const remaining = dailyLimit !== null
86
+ ? Math.max(0, dailyLimit - totalUsed)
87
+ : null;
88
+ return {
89
+ limitTypeId: lt.id,
90
+ limitType: lt.type,
91
+ dailyLimit,
92
+ campaignUsed,
93
+ directUsed,
94
+ totalUsed,
95
+ remaining,
96
+ };
97
+ });
98
+ }
99
+ }
100
+ //# sourceMappingURL=action-budget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action-budget.js","sourceRoot":"","sources":["../../../src/db/repositories/action-budget.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=action-budget.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action-budget.test.d.ts","sourceRoot":"","sources":["../../../src/db/repositories/action-budget.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,181 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ import { DatabaseSync } from "node:sqlite";
4
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
5
+ import { ActionBudgetRepository } from "./action-budget.js";
6
+ function createBudgetDb() {
7
+ const db = new DatabaseSync(":memory:");
8
+ db.exec(`
9
+ CREATE TABLE limit_types (
10
+ id INTEGER PRIMARY KEY,
11
+ type TEXT NOT NULL
12
+ )
13
+ `);
14
+ db.exec(`
15
+ INSERT INTO limit_types (id, type) VALUES
16
+ (8, 'Invite'),
17
+ (9, 'Message'),
18
+ (16, 'Follow'),
19
+ (18, 'PostLike')
20
+ `);
21
+ db.exec(`
22
+ CREATE TABLE daily_limits (
23
+ id INTEGER PRIMARY KEY,
24
+ max_limit INTEGER NOT NULL
25
+ )
26
+ `);
27
+ db.exec(`
28
+ INSERT INTO daily_limits (id, max_limit) VALUES
29
+ (8, 100),
30
+ (16, 150)
31
+ `);
32
+ db.exec(`
33
+ CREATE TABLE action_configs (
34
+ id INTEGER PRIMARY KEY,
35
+ actionType TEXT NOT NULL
36
+ )
37
+ `);
38
+ db.exec(`INSERT INTO action_configs (id, actionType) VALUES (1, 'InvitePerson')`);
39
+ db.exec(`INSERT INTO action_configs (id, actionType) VALUES (2, 'Follow')`);
40
+ db.exec(`
41
+ CREATE TABLE action_versions (
42
+ id INTEGER PRIMARY KEY,
43
+ action_id INTEGER NOT NULL,
44
+ config_id INTEGER NOT NULL
45
+ )
46
+ `);
47
+ db.exec(`INSERT INTO action_versions (id, action_id, config_id) VALUES (1, 1, 1)`);
48
+ db.exec(`INSERT INTO action_versions (id, action_id, config_id) VALUES (2, 2, 2)`);
49
+ db.exec(`
50
+ CREATE TABLE action_results (
51
+ id INTEGER PRIMARY KEY,
52
+ action_version_id INTEGER NOT NULL,
53
+ person_id INTEGER NOT NULL,
54
+ result INTEGER NOT NULL DEFAULT 0,
55
+ created_at DATETIME NOT NULL
56
+ )
57
+ `);
58
+ // Insert today's results
59
+ const today = new Date().toISOString().split("T")[0] ?? "";
60
+ db.exec(`
61
+ INSERT INTO action_results (action_version_id, person_id, result, created_at)
62
+ VALUES
63
+ (1, 100, 1, '${today} 10:00:00'),
64
+ (1, 101, 1, '${today} 10:05:00'),
65
+ (1, 102, -1, '${today} 10:10:00'),
66
+ (2, 200, 1, '${today} 11:00:00')
67
+ `);
68
+ // Insert yesterday's results (should NOT be counted)
69
+ const yesterday = new Date(Date.now() - 86_400_000).toISOString().split("T")[0] ?? "";
70
+ db.exec(`
71
+ INSERT INTO action_results (action_version_id, person_id, result, created_at)
72
+ VALUES
73
+ (1, 103, 1, '${yesterday} 10:00:00'),
74
+ (2, 201, 1, '${yesterday} 11:00:00')
75
+ `);
76
+ return db;
77
+ }
78
+ describe("ActionBudgetRepository", () => {
79
+ let db;
80
+ let repo;
81
+ beforeEach(() => {
82
+ db = createBudgetDb();
83
+ repo = new ActionBudgetRepository({ db });
84
+ });
85
+ afterEach(() => {
86
+ db.close();
87
+ });
88
+ describe("getLimitTypes", () => {
89
+ it("returns all limit types ordered by ID", () => {
90
+ const types = repo.getLimitTypes();
91
+ expect(types).toEqual([
92
+ { id: 8, type: "Invite" },
93
+ { id: 9, type: "Message" },
94
+ { id: 16, type: "Follow" },
95
+ { id: 18, type: "PostLike" },
96
+ ]);
97
+ });
98
+ });
99
+ describe("getActionBudget", () => {
100
+ it("combines limit types, daily limits, and today's usage", () => {
101
+ const entries = repo.getActionBudget();
102
+ // Invite: limit 100, 3 today (2 success + 1 fail — all results counted)
103
+ const invite = entries.find((e) => e.limitType === "Invite");
104
+ expect(invite).toEqual({
105
+ limitTypeId: 8,
106
+ limitType: "Invite",
107
+ dailyLimit: 100,
108
+ campaignUsed: 3,
109
+ directUsed: 0,
110
+ totalUsed: 3,
111
+ remaining: 97,
112
+ });
113
+ // Follow: limit 150, 1 today
114
+ const follow = entries.find((e) => e.limitType === "Follow");
115
+ expect(follow).toEqual({
116
+ limitTypeId: 16,
117
+ limitType: "Follow",
118
+ dailyLimit: 150,
119
+ campaignUsed: 1,
120
+ directUsed: 0,
121
+ totalUsed: 1,
122
+ remaining: 149,
123
+ });
124
+ // Message: no daily limit configured, 0 used
125
+ const message = entries.find((e) => e.limitType === "Message");
126
+ expect(message).toEqual({
127
+ limitTypeId: 9,
128
+ limitType: "Message",
129
+ dailyLimit: null,
130
+ campaignUsed: 0,
131
+ directUsed: 0,
132
+ totalUsed: 0,
133
+ remaining: null,
134
+ });
135
+ });
136
+ it("includes direct counts in budget", () => {
137
+ const directCounts = new Map([[18, 5]]);
138
+ const entries = repo.getActionBudget(directCounts);
139
+ const postLike = entries.find((e) => e.limitType === "PostLike");
140
+ expect(postLike).toEqual({
141
+ limitTypeId: 18,
142
+ limitType: "PostLike",
143
+ dailyLimit: null,
144
+ campaignUsed: 0,
145
+ directUsed: 5,
146
+ totalUsed: 5,
147
+ remaining: null,
148
+ });
149
+ });
150
+ it("clamps remaining to zero when over limit", () => {
151
+ const directCounts = new Map([[8, 200]]);
152
+ const entries = repo.getActionBudget(directCounts);
153
+ const invite = entries.find((e) => e.limitType === "Invite");
154
+ expect(invite?.remaining).toBe(0);
155
+ expect(invite?.totalUsed).toBe(203); // 3 campaign + 200 direct
156
+ });
157
+ it("returns empty entries when no limit types exist", () => {
158
+ const emptyDb = new DatabaseSync(":memory:");
159
+ emptyDb.exec(`CREATE TABLE limit_types (id INTEGER PRIMARY KEY, type TEXT NOT NULL)`);
160
+ emptyDb.exec(`CREATE TABLE daily_limits (id INTEGER PRIMARY KEY, max_limit INTEGER NOT NULL)`);
161
+ emptyDb.exec(`
162
+ CREATE TABLE action_results (
163
+ id INTEGER PRIMARY KEY, action_version_id INTEGER NOT NULL,
164
+ person_id INTEGER NOT NULL, result INTEGER NOT NULL DEFAULT 0,
165
+ created_at DATETIME NOT NULL
166
+ )
167
+ `);
168
+ emptyDb.exec(`
169
+ CREATE TABLE action_versions (
170
+ id INTEGER PRIMARY KEY, action_id INTEGER NOT NULL, config_id INTEGER NOT NULL
171
+ )
172
+ `);
173
+ emptyDb.exec(`CREATE TABLE action_configs (id INTEGER PRIMARY KEY, actionType TEXT NOT NULL)`);
174
+ const emptyRepo = new ActionBudgetRepository({ db: emptyDb });
175
+ const entries = emptyRepo.getActionBudget();
176
+ expect(entries).toEqual([]);
177
+ emptyDb.close();
178
+ });
179
+ });
180
+ });
181
+ //# sourceMappingURL=action-budget.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action-budget.test.js","sourceRoot":"","sources":["../../../src/db/repositories/action-budget.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=campaign-hard-delete.integration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"campaign-hard-delete.integration.test.d.ts","sourceRoot":"","sources":["../../../src/db/repositories/campaign-hard-delete.integration.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,78 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (C) 2026 Oleksii PELYKH
3
+ import { afterAll, beforeAll, describe, expect, it } from "vitest";
4
+ import { DatabaseClient } from "../client.js";
5
+ import { openFixture } from "../testing/open-fixture.js";
6
+ import { CampaignRepository } from "./campaign.js";
7
+ import { CampaignNotFoundError } from "../errors.js";
8
+ describe("CampaignRepository.deleteCampaign (integration)", () => {
9
+ let rawDb;
10
+ let client;
11
+ let repo;
12
+ beforeAll(() => {
13
+ // Use openFixture() for an isolated writable copy
14
+ rawDb = openFixture();
15
+ // Ensure SQLite enforces foreign key constraints so delete order is validated
16
+ rawDb.exec("PRAGMA foreign_keys = ON");
17
+ const fkPragma = rawDb
18
+ .prepare("PRAGMA foreign_keys")
19
+ .get();
20
+ expect(fkPragma.foreign_keys).toBe(1);
21
+ // Pause campaign 1 so it can be hard-deleted
22
+ // (fixture has it as active: is_paused=0, is_archived=0, is_valid=1)
23
+ rawDb.exec("UPDATE campaigns SET is_paused = 1 WHERE id = 1");
24
+ // Wrap the raw DatabaseSync in a DatabaseClient for the repo
25
+ client = { db: rawDb, close: () => rawDb.close() };
26
+ repo = new CampaignRepository(client);
27
+ });
28
+ afterAll(() => {
29
+ rawDb.close();
30
+ });
31
+ function countRows(table, where, params) {
32
+ const row = rawDb.prepare(`SELECT COUNT(*) AS cnt FROM ${table} WHERE ${where}`).get(...params);
33
+ return row.cnt;
34
+ }
35
+ it("removes campaign 1 and all related rows", () => {
36
+ // Pre-conditions: campaign 1 has data in all related tables
37
+ expect(countRows("campaigns", "id = ?", [1])).toBe(1);
38
+ expect(countRows("actions", "campaign_id = ?", [1])).toBeGreaterThan(0);
39
+ expect(countRows("action_versions", "action_id IN (SELECT id FROM actions WHERE campaign_id = ?)", [1])).toBeGreaterThan(0);
40
+ expect(countRows("action_configs", "id IN (SELECT DISTINCT av.config_id FROM action_versions av JOIN actions a ON av.action_id = a.id WHERE a.campaign_id = ?)", [1])).toBeGreaterThan(0);
41
+ expect(countRows("action_target_people", "action_id IN (SELECT id FROM actions WHERE campaign_id = ?)", [1])).toBeGreaterThan(0);
42
+ expect(countRows("action_results", "action_version_id IN (SELECT av.id FROM action_versions av JOIN actions a ON av.action_id = a.id WHERE a.campaign_id = ?)", [1])).toBeGreaterThan(0);
43
+ expect(countRows("person_in_campaigns_history", "campaign_id = ?", [1])).toBeGreaterThan(0);
44
+ expect(countRows("campaign_versions", "campaign_id = ?", [1])).toBeGreaterThan(0);
45
+ // Exclude list chain pre-conditions
46
+ const excludeListCount = countRows("collection_people_versions", "id IN (SELECT av.exclude_list_id FROM action_versions av JOIN actions a ON av.action_id = a.id WHERE a.campaign_id = ? AND av.exclude_list_id IS NOT NULL UNION SELECT cv.exclude_list_id FROM campaign_versions cv WHERE cv.campaign_id = ? AND cv.exclude_list_id IS NOT NULL)", [1, 1]);
47
+ expect(excludeListCount).toBeGreaterThan(0);
48
+ // Perform the hard delete (with FK constraints enforced)
49
+ repo.deleteCampaign(1);
50
+ // Post-conditions: all related rows are gone
51
+ expect(countRows("campaigns", "id = ?", [1])).toBe(0);
52
+ expect(countRows("actions", "campaign_id = ?", [1])).toBe(0);
53
+ expect(countRows("action_versions", "action_id IN (SELECT id FROM actions WHERE campaign_id = ?)", [1])).toBe(0);
54
+ expect(countRows("action_target_people", "action_id IN (SELECT id FROM actions WHERE campaign_id = ?)", [1])).toBe(0);
55
+ expect(countRows("action_results", "action_version_id IN (SELECT av.id FROM action_versions av JOIN actions a ON av.action_id = a.id WHERE a.campaign_id = ?)", [1])).toBe(0);
56
+ expect(countRows("action_result_flags", "action_result_id IN (SELECT ar.id FROM action_results ar JOIN action_versions av ON ar.action_version_id = av.id JOIN actions a ON av.action_id = a.id WHERE a.campaign_id = ?)", [1])).toBe(0);
57
+ expect(countRows("action_result_messages", "action_result_id IN (SELECT ar.id FROM action_results ar JOIN action_versions av ON ar.action_version_id = av.id JOIN actions a ON av.action_id = a.id WHERE a.campaign_id = ?)", [1])).toBe(0);
58
+ expect(countRows("person_in_campaigns_history", "campaign_id = ?", [1])).toBe(0);
59
+ expect(countRows("campaign_versions", "campaign_id = ?", [1])).toBe(0);
60
+ // Verify action_configs for campaign 1 are deleted
61
+ // (config ID 1 belonged to campaign 1's action)
62
+ expect(countRows("action_configs", "id = ?", [1])).toBe(0);
63
+ // Verify exclude list chain is cleaned up
64
+ expect(countRows("collection_people_versions", "id IN (1, 2)", [])).toBe(0);
65
+ expect(countRows("collections", "id IN (1, 2)", [])).toBe(0);
66
+ });
67
+ it("throws CampaignNotFoundError for non-existent campaign", () => {
68
+ expect(() => repo.deleteCampaign(9999)).toThrow(CampaignNotFoundError);
69
+ });
70
+ it("does not affect other campaigns", () => {
71
+ // Campaign 2 should still exist after deleting campaign 1
72
+ expect(countRows("campaigns", "id = ?", [2])).toBe(1);
73
+ expect(countRows("actions", "campaign_id = ?", [2])).toBeGreaterThan(0);
74
+ expect(countRows("action_versions", "action_id IN (SELECT id FROM actions WHERE campaign_id = ?)", [2])).toBeGreaterThan(0);
75
+ expect(countRows("action_configs", "id = ?", [2])).toBe(1);
76
+ });
77
+ });
78
+ //# sourceMappingURL=campaign-hard-delete.integration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"campaign-hard-delete.integration.test.js","sourceRoot":"","sources":["../../../src/db/repositories/campaign-hard-delete.integration.test.ts"],"names":[],"mappings":"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"}
@@ -63,6 +63,20 @@ export declare class CampaignRepository {
63
63
  * @throws {CampaignNotFoundError} if no campaign exists with the given ID.
64
64
  */
65
65
  updateCampaign(campaignId: number, updates: CampaignUpdateConfig): Campaign;
66
+ /**
67
+ * Hard-delete a campaign and all related rows from the database.
68
+ *
69
+ * Removes all data across: action_result_flags, action_result_messages,
70
+ * action_results, action_target_people, person_in_campaigns_history,
71
+ * campaign_versions, action_versions, actions, action_configs,
72
+ * and the exclude list chain (collection_people, collection_people_versions,
73
+ * collections).
74
+ *
75
+ * Callers must verify the campaign is not active before calling this method.
76
+ *
77
+ * @throws {CampaignNotFoundError} if no campaign exists with the given ID.
78
+ */
79
+ deleteCampaign(campaignId: number): void;
66
80
  /**
67
81
  * Prepare write statements lazily (only when needed).
68
82
  * This avoids issues when the client is opened in read-only mode.
@@ -1 +1 @@
1
- {"version":3,"file":"campaign.d.ts","sourceRoot":"","sources":["../../../src/db/repositories/campaign.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAGV,oBAAoB,EACpB,oBAAoB,EACpB,0BAA0B,EAC1B,QAAQ,EACR,cAAc,EACd,mBAAmB,EAEnB,aAAa,EACb,eAAe,EACf,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,EACzB,oBAAoB,EACrB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAuFnD;;;;;;;;;GASG;AACH,qBAAa,kBAAkB;IAsBjB,OAAO,CAAC,QAAQ,CAAC,MAAM;IArBnC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC;IACnC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAC;IACtC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC;IACjC,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAC;IACxC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;IAGhC,OAAO,CAAC,eAAe,CAYP;gBAEa,MAAM,EAAE,cAAc;IAuDnD;;OAEG;IACH,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,eAAe,EAAE;IAoBpE;;;;OAIG;IACH,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ;IAiBzC;;;;OAIG;IACH,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,EAAE;IAoCxD;;;;OAIG;IACH,UAAU,CACR,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,iBAAsB,GAC9B,oBAAoB,EAAE;IA+BzB;;;;;OAKG;IACH,UAAU,CACR,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,yBAA8B,GACtC;QAAE,MAAM,EAAE,mBAAmB,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;IAsEnD;;;;OAIG;IACH,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa;IAKnD;;;;OAIG;IACH,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB,GAAG,QAAQ;IAyB3E;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAuD1B;;;;;;OAMG;IACH,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAKpC;;;;;;;;OAQG;IACH,wBAAwB,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI;IAmCvE;;;;;;;OAOG;IACH,SAAS,CACP,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,oBAAoB,EAClC,WAAW,EAAE,MAAM,GAClB,cAAc;IA0EjB;;;;;;;;OAQG;IACH,YAAY,CACV,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,0BAA0B,GAClC,cAAc;IAuEjB;;;;;;;;;;OAUG;IACH,gBAAgB,CACd,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EAAE,GAClB;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE;CAoD5B"}
1
+ {"version":3,"file":"campaign.d.ts","sourceRoot":"","sources":["../../../src/db/repositories/campaign.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAGV,oBAAoB,EACpB,oBAAoB,EACpB,0BAA0B,EAC1B,QAAQ,EACR,cAAc,EACd,mBAAmB,EAEnB,aAAa,EACb,eAAe,EACf,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,EACzB,oBAAoB,EACrB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAuFnD;;;;;;;;;GASG;AACH,qBAAa,kBAAkB;IAqCjB,OAAO,CAAC,QAAQ,CAAC,MAAM;IApCnC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC;IACnC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAC;IACtC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC;IACjC,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAC;IACxC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;IAGhC,OAAO,CAAC,eAAe,CA2BP;gBAEa,MAAM,EAAE,cAAc;IAuDnD;;OAEG;IACH,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,eAAe,EAAE;IAoBpE;;;;OAIG;IACH,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ;IAiBzC;;;;OAIG;IACH,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,EAAE;IAoCxD;;;;OAIG;IACH,UAAU,CACR,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,iBAAsB,GAC9B,oBAAoB,EAAE;IA+BzB;;;;;OAKG;IACH,UAAU,CACR,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,yBAA8B,GACtC;QAAE,MAAM,EAAE,mBAAmB,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;IAsEnD;;;;OAIG;IACH,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa;IAKnD;;;;OAIG;IACH,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB,GAAG,QAAQ;IAyB3E;;;;;;;;;;;;OAYG;IACH,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAoExC;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAmJ1B;;;;;;OAMG;IACH,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAKpC;;;;;;;;OAQG;IACH,wBAAwB,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI;IAmCvE;;;;;;;OAOG;IACH,SAAS,CACP,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,oBAAoB,EAClC,WAAW,EAAE,MAAM,GAClB,cAAc;IA0EjB;;;;;;;;OAQG;IACH,YAAY,CACV,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,0BAA0B,GAClC,cAAc;IAuEjB;;;;;;;;;;OAUG;IACH,gBAAgB,CACd,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EAAE,GAClB;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE;CAoD5B"}
@@ -278,6 +278,69 @@ export class CampaignRepository {
278
278
  }
279
279
  return this.getCampaign(campaignId);
280
280
  }
281
+ /**
282
+ * Hard-delete a campaign and all related rows from the database.
283
+ *
284
+ * Removes all data across: action_result_flags, action_result_messages,
285
+ * action_results, action_target_people, person_in_campaigns_history,
286
+ * campaign_versions, action_versions, actions, action_configs,
287
+ * and the exclude list chain (collection_people, collection_people_versions,
288
+ * collections).
289
+ *
290
+ * Callers must verify the campaign is not active before calling this method.
291
+ *
292
+ * @throws {CampaignNotFoundError} if no campaign exists with the given ID.
293
+ */
294
+ deleteCampaign(campaignId) {
295
+ // Verify campaign exists
296
+ this.getCampaign(campaignId);
297
+ const stmts = this.getWriteStatements();
298
+ const { db } = this.client;
299
+ // Collect IDs needed after FK-referencing rows are deleted
300
+ const configRows = stmts.selectConfigIds.all(campaignId);
301
+ const configIds = configRows.map((r) => r.config_id);
302
+ const excludeRows = stmts.selectExcludeListInfo.all(campaignId, campaignId);
303
+ const excludeVersionIds = excludeRows.map((r) => r.version_id);
304
+ const excludeCollectionIds = [...new Set(excludeRows.map((r) => r.collection_id))];
305
+ db.exec("BEGIN");
306
+ try {
307
+ // 1. Delete result children (FK: action_result_flags/messages → action_results)
308
+ stmts.deleteResultFlagsByCampaign.run(campaignId);
309
+ stmts.deleteResultMessagesByCampaign.run(campaignId);
310
+ // 2. Delete results (FK: action_results → action_versions)
311
+ stmts.deleteResultsByCampaign.run(campaignId);
312
+ // 3. Delete target people (FK: action_target_people → actions)
313
+ stmts.deleteTargetPeopleByCampaign.run(campaignId);
314
+ // 4. Delete campaign history
315
+ stmts.deleteCampaignHistory.run(campaignId);
316
+ // 5. Delete collection_people for exclude lists
317
+ stmts.deleteCollectionPeopleByCampaign.run(campaignId, campaignId);
318
+ // 6. Delete campaign_versions (FK → campaigns)
319
+ stmts.deleteCampaignVersions.run(campaignId);
320
+ // 7. Delete action_versions (FK → actions)
321
+ stmts.deleteActionVersionsByCampaign.run(campaignId);
322
+ // 8. Delete actions (FK → campaigns)
323
+ stmts.deleteActionsByCampaign.run(campaignId);
324
+ // 9. Delete action_configs (collected before transaction)
325
+ for (const configId of configIds) {
326
+ stmts.deleteActionConfig.run(configId);
327
+ }
328
+ // 10. Delete exclude list versions and collections
329
+ for (const versionId of excludeVersionIds) {
330
+ stmts.deleteCollectionPeopleVersion.run(versionId);
331
+ }
332
+ for (const collectionId of excludeCollectionIds) {
333
+ stmts.deleteCollection.run(collectionId);
334
+ }
335
+ // 11. Delete the campaign itself
336
+ stmts.deleteCampaign.run(campaignId);
337
+ db.exec("COMMIT");
338
+ }
339
+ catch (e) {
340
+ db.exec("ROLLBACK");
341
+ throw e;
342
+ }
343
+ }
281
344
  /**
282
345
  * Prepare write statements lazily (only when needed).
283
346
  * This avoids issues when the client is opened in read-only mode.
@@ -309,6 +372,68 @@ export class CampaignRepository {
309
372
  VALUES (?, ?, ?, 1, ?, STRFTIME('%Y-%m-%dT%H:%M:%fZ', 'NOW'))`),
310
373
  countTarget: db.prepare(`SELECT COUNT(*) AS cnt FROM action_target_people
311
374
  WHERE action_id = ? AND person_id = ?`),
375
+ deleteResultFlagsByCampaign: db.prepare(`DELETE FROM action_result_flags
376
+ WHERE action_result_id IN (
377
+ SELECT ar.id FROM action_results ar
378
+ JOIN action_versions av ON ar.action_version_id = av.id
379
+ JOIN actions a ON av.action_id = a.id
380
+ WHERE a.campaign_id = ?
381
+ )`),
382
+ deleteResultMessagesByCampaign: db.prepare(`DELETE FROM action_result_messages
383
+ WHERE action_result_id IN (
384
+ SELECT ar.id FROM action_results ar
385
+ JOIN action_versions av ON ar.action_version_id = av.id
386
+ JOIN actions a ON av.action_id = a.id
387
+ WHERE a.campaign_id = ?
388
+ )`),
389
+ deleteResultsByCampaign: db.prepare(`DELETE FROM action_results
390
+ WHERE action_version_id IN (
391
+ SELECT av.id FROM action_versions av
392
+ JOIN actions a ON av.action_id = a.id
393
+ WHERE a.campaign_id = ?
394
+ )`),
395
+ deleteTargetPeopleByCampaign: db.prepare(`DELETE FROM action_target_people
396
+ WHERE action_id IN (
397
+ SELECT id FROM actions WHERE campaign_id = ?
398
+ )`),
399
+ deleteCampaignHistory: db.prepare(`DELETE FROM person_in_campaigns_history
400
+ WHERE campaign_id = ?`),
401
+ deleteCollectionPeopleByCampaign: db.prepare(`DELETE FROM collection_people
402
+ WHERE collection_id IN (
403
+ SELECT cpv.collection_id FROM collection_people_versions cpv
404
+ WHERE cpv.id IN (
405
+ SELECT av.exclude_list_id FROM action_versions av
406
+ JOIN actions a ON av.action_id = a.id
407
+ WHERE a.campaign_id = ? AND av.exclude_list_id IS NOT NULL
408
+ UNION
409
+ SELECT cv.exclude_list_id FROM campaign_versions cv
410
+ WHERE cv.campaign_id = ? AND cv.exclude_list_id IS NOT NULL
411
+ )
412
+ )`),
413
+ deleteCampaignVersions: db.prepare(`DELETE FROM campaign_versions WHERE campaign_id = ?`),
414
+ selectExcludeListInfo: db.prepare(`SELECT cpv.id AS version_id, cpv.collection_id
415
+ FROM collection_people_versions cpv
416
+ WHERE cpv.id IN (
417
+ SELECT av.exclude_list_id FROM action_versions av
418
+ JOIN actions a ON av.action_id = a.id
419
+ WHERE a.campaign_id = ? AND av.exclude_list_id IS NOT NULL
420
+ UNION
421
+ SELECT cv.exclude_list_id FROM campaign_versions cv
422
+ WHERE cv.campaign_id = ? AND cv.exclude_list_id IS NOT NULL
423
+ )`),
424
+ deleteActionVersionsByCampaign: db.prepare(`DELETE FROM action_versions
425
+ WHERE action_id IN (
426
+ SELECT id FROM actions WHERE campaign_id = ?
427
+ )`),
428
+ selectConfigIds: db.prepare(`SELECT DISTINCT av.config_id
429
+ FROM action_versions av
430
+ JOIN actions a ON av.action_id = a.id
431
+ WHERE a.campaign_id = ?`),
432
+ deleteActionsByCampaign: db.prepare(`DELETE FROM actions WHERE campaign_id = ?`),
433
+ deleteActionConfig: db.prepare(`DELETE FROM action_configs WHERE id = ?`),
434
+ deleteCollectionPeopleVersion: db.prepare(`DELETE FROM collection_people_versions WHERE id = ?`),
435
+ deleteCollection: db.prepare(`DELETE FROM collections WHERE id = ?`),
436
+ deleteCampaign: db.prepare(`DELETE FROM campaigns WHERE id = ?`),
312
437
  };
313
438
  return this.writeStatements;
314
439
  }