@atproto/ozone 0.1.172 → 0.1.174

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 (450) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/api/index.d.ts.map +1 -1
  3. package/dist/api/index.js +40 -0
  4. package/dist/api/index.js.map +1 -1
  5. package/dist/api/moderation/emitEvent.d.ts.map +1 -1
  6. package/dist/api/moderation/emitEvent.js +31 -0
  7. package/dist/api/moderation/emitEvent.js.map +1 -1
  8. package/dist/api/queue/assignModerator.d.ts +4 -0
  9. package/dist/api/queue/assignModerator.d.ts.map +1 -0
  10. package/dist/api/queue/assignModerator.js +28 -0
  11. package/dist/api/queue/assignModerator.js.map +1 -0
  12. package/dist/api/queue/createQueue.d.ts +4 -0
  13. package/dist/api/queue/createQueue.d.ts.map +1 -0
  14. package/dist/api/queue/createQueue.js +44 -0
  15. package/dist/api/queue/createQueue.js.map +1 -0
  16. package/dist/api/queue/deleteQueue.d.ts +4 -0
  17. package/dist/api/queue/deleteQueue.d.ts.map +1 -0
  18. package/dist/api/queue/deleteQueue.js +40 -0
  19. package/dist/api/queue/deleteQueue.js.map +1 -0
  20. package/dist/api/queue/getAssignments.d.ts +4 -0
  21. package/dist/api/queue/getAssignments.d.ts.map +1 -0
  22. package/dist/api/queue/getAssignments.js +19 -0
  23. package/dist/api/queue/getAssignments.js.map +1 -0
  24. package/dist/api/queue/listQueues.d.ts +4 -0
  25. package/dist/api/queue/listQueues.d.ts.map +1 -0
  26. package/dist/api/queue/listQueues.js +29 -0
  27. package/dist/api/queue/listQueues.js.map +1 -0
  28. package/dist/api/queue/routeReports.d.ts +4 -0
  29. package/dist/api/queue/routeReports.d.ts.map +1 -0
  30. package/dist/api/queue/routeReports.js +33 -0
  31. package/dist/api/queue/routeReports.js.map +1 -0
  32. package/dist/api/queue/unassignModerator.d.ts +4 -0
  33. package/dist/api/queue/unassignModerator.d.ts.map +1 -0
  34. package/dist/api/queue/unassignModerator.js +24 -0
  35. package/dist/api/queue/unassignModerator.js.map +1 -0
  36. package/dist/api/queue/updateQueue.d.ts +4 -0
  37. package/dist/api/queue/updateQueue.d.ts.map +1 -0
  38. package/dist/api/queue/updateQueue.js +39 -0
  39. package/dist/api/queue/updateQueue.js.map +1 -0
  40. package/dist/api/report/assignModerator.d.ts +4 -0
  41. package/dist/api/report/assignModerator.d.ts.map +1 -0
  42. package/dist/api/report/assignModerator.js +33 -0
  43. package/dist/api/report/assignModerator.js.map +1 -0
  44. package/dist/api/report/createActivity.d.ts +4 -0
  45. package/dist/api/report/createActivity.d.ts.map +1 -0
  46. package/dist/api/report/createActivity.js +44 -0
  47. package/dist/api/report/createActivity.js.map +1 -0
  48. package/dist/api/report/getAssignments.d.ts +4 -0
  49. package/dist/api/report/getAssignments.d.ts.map +1 -0
  50. package/dist/api/report/getAssignments.js +19 -0
  51. package/dist/api/report/getAssignments.js.map +1 -0
  52. package/dist/api/report/getHistoricalStats.d.ts +4 -0
  53. package/dist/api/report/getHistoricalStats.d.ts.map +1 -0
  54. package/dist/api/report/getHistoricalStats.js +32 -0
  55. package/dist/api/report/getHistoricalStats.js.map +1 -0
  56. package/dist/api/report/getLatestReport.d.ts +4 -0
  57. package/dist/api/report/getLatestReport.d.ts.map +1 -0
  58. package/dist/api/report/getLatestReport.js +31 -0
  59. package/dist/api/report/getLatestReport.js.map +1 -0
  60. package/dist/api/report/getLiveStats.d.ts +4 -0
  61. package/dist/api/report/getLiveStats.d.ts.map +1 -0
  62. package/dist/api/report/getLiveStats.js +25 -0
  63. package/dist/api/report/getLiveStats.js.map +1 -0
  64. package/dist/api/report/getReport.d.ts +4 -0
  65. package/dist/api/report/getReport.d.ts.map +1 -0
  66. package/dist/api/report/getReport.js +35 -0
  67. package/dist/api/report/getReport.js.map +1 -0
  68. package/dist/api/report/listActivities.d.ts +4 -0
  69. package/dist/api/report/listActivities.d.ts.map +1 -0
  70. package/dist/api/report/listActivities.js +25 -0
  71. package/dist/api/report/listActivities.js.map +1 -0
  72. package/dist/api/report/queryReports.d.ts +4 -0
  73. package/dist/api/report/queryReports.d.ts.map +1 -0
  74. package/dist/api/report/queryReports.js +29 -0
  75. package/dist/api/report/queryReports.js.map +1 -0
  76. package/dist/api/report/reassignQueue.d.ts +4 -0
  77. package/dist/api/report/reassignQueue.d.ts.map +1 -0
  78. package/dist/api/report/reassignQueue.js +45 -0
  79. package/dist/api/report/reassignQueue.js.map +1 -0
  80. package/dist/api/report/refreshStats.d.ts +4 -0
  81. package/dist/api/report/refreshStats.d.ts.map +1 -0
  82. package/dist/api/report/refreshStats.js +26 -0
  83. package/dist/api/report/refreshStats.js.map +1 -0
  84. package/dist/api/report/unassignModerator.d.ts +4 -0
  85. package/dist/api/report/unassignModerator.d.ts.map +1 -0
  86. package/dist/api/report/unassignModerator.js +21 -0
  87. package/dist/api/report/unassignModerator.js.map +1 -0
  88. package/dist/api/util.d.ts +2 -0
  89. package/dist/api/util.d.ts.map +1 -1
  90. package/dist/api/util.js +9 -1
  91. package/dist/api/util.js.map +1 -1
  92. package/dist/assignment/index.d.ts +89 -0
  93. package/dist/assignment/index.d.ts.map +1 -0
  94. package/dist/assignment/index.js +537 -0
  95. package/dist/assignment/index.js.map +1 -0
  96. package/dist/config/config.d.ts +14 -0
  97. package/dist/config/config.d.ts.map +1 -1
  98. package/dist/config/config.js +9 -0
  99. package/dist/config/config.js.map +1 -1
  100. package/dist/config/env.d.ts +3 -0
  101. package/dist/config/env.d.ts.map +1 -1
  102. package/dist/config/env.js +3 -0
  103. package/dist/config/env.js.map +1 -1
  104. package/dist/context.d.ts +9 -0
  105. package/dist/context.d.ts.map +1 -1
  106. package/dist/context.js +31 -10
  107. package/dist/context.js.map +1 -1
  108. package/dist/daemon/context.d.ts +6 -0
  109. package/dist/daemon/context.d.ts.map +1 -1
  110. package/dist/daemon/context.js +28 -4
  111. package/dist/daemon/context.js.map +1 -1
  112. package/dist/daemon/event-reverser.d.ts +1 -0
  113. package/dist/daemon/event-reverser.d.ts.map +1 -1
  114. package/dist/daemon/event-reverser.js +42 -1
  115. package/dist/daemon/event-reverser.js.map +1 -1
  116. package/dist/daemon/job-cursor.d.ts +5 -0
  117. package/dist/daemon/job-cursor.d.ts.map +1 -0
  118. package/dist/daemon/job-cursor.js +28 -0
  119. package/dist/daemon/job-cursor.js.map +1 -0
  120. package/dist/daemon/queue-router.d.ts +17 -0
  121. package/dist/daemon/queue-router.d.ts.map +1 -0
  122. package/dist/daemon/queue-router.js +114 -0
  123. package/dist/daemon/queue-router.js.map +1 -0
  124. package/dist/daemon/stats-computer.d.ts +51 -0
  125. package/dist/daemon/stats-computer.d.ts.map +1 -0
  126. package/dist/daemon/stats-computer.js +117 -0
  127. package/dist/daemon/stats-computer.js.map +1 -0
  128. package/dist/daemon/strike-expiry-processor.d.ts.map +1 -1
  129. package/dist/daemon/strike-expiry-processor.js +4 -19
  130. package/dist/daemon/strike-expiry-processor.js.map +1 -1
  131. package/dist/db/migrations/20260219T164523000Z-create-report-table.d.ts +4 -0
  132. package/dist/db/migrations/20260219T164523000Z-create-report-table.d.ts.map +1 -0
  133. package/dist/db/migrations/20260219T164523000Z-create-report-table.js +126 -0
  134. package/dist/db/migrations/20260219T164523000Z-create-report-table.js.map +1 -0
  135. package/dist/db/migrations/20260219T165302248Z-moderator-assignment.d.ts +4 -0
  136. package/dist/db/migrations/20260219T165302248Z-moderator-assignment.d.ts.map +1 -0
  137. package/dist/db/migrations/20260219T165302248Z-moderator-assignment.js +35 -0
  138. package/dist/db/migrations/20260219T165302248Z-moderator-assignment.js.map +1 -0
  139. package/dist/db/migrations/20260225T000000000Z-add-report-queue-table.d.ts +4 -0
  140. package/dist/db/migrations/20260225T000000000Z-add-report-queue-table.d.ts.map +1 -0
  141. package/dist/db/migrations/20260225T000000000Z-add-report-queue-table.js +36 -0
  142. package/dist/db/migrations/20260225T000000000Z-add-report-queue-table.js.map +1 -0
  143. package/dist/db/migrations/20260313T000000000Z-add-report-activity-table.d.ts +4 -0
  144. package/dist/db/migrations/20260313T000000000Z-add-report-activity-table.d.ts.map +1 -0
  145. package/dist/db/migrations/20260313T000000000Z-add-report-activity-table.js +39 -0
  146. package/dist/db/migrations/20260313T000000000Z-add-report-activity-table.js.map +1 -0
  147. package/dist/db/migrations/20260318T152058935Z-add-report-stat.d.ts +4 -0
  148. package/dist/db/migrations/20260318T152058935Z-add-report-stat.d.ts.map +1 -0
  149. package/dist/db/migrations/20260318T152058935Z-add-report-stat.js +34 -0
  150. package/dist/db/migrations/20260318T152058935Z-add-report-stat.js.map +1 -0
  151. package/dist/db/migrations/20260428T000000000Z-add-expiring-tag-table.d.ts +4 -0
  152. package/dist/db/migrations/20260428T000000000Z-add-expiring-tag-table.d.ts.map +1 -0
  153. package/dist/db/migrations/20260428T000000000Z-add-expiring-tag-table.js +32 -0
  154. package/dist/db/migrations/20260428T000000000Z-add-expiring-tag-table.js.map +1 -0
  155. package/dist/db/migrations/index.d.ts +6 -0
  156. package/dist/db/migrations/index.d.ts.map +1 -1
  157. package/dist/db/migrations/index.js +7 -1
  158. package/dist/db/migrations/index.js.map +1 -1
  159. package/dist/db/pagination.d.ts +31 -0
  160. package/dist/db/pagination.d.ts.map +1 -1
  161. package/dist/db/pagination.js +74 -1
  162. package/dist/db/pagination.js.map +1 -1
  163. package/dist/db/schema/expiring_tag.d.ts +15 -0
  164. package/dist/db/schema/expiring_tag.d.ts.map +1 -0
  165. package/dist/db/schema/expiring_tag.js +5 -0
  166. package/dist/db/schema/expiring_tag.js.map +1 -0
  167. package/dist/db/schema/index.d.ts +7 -1
  168. package/dist/db/schema/index.d.ts.map +1 -1
  169. package/dist/db/schema/index.js.map +1 -1
  170. package/dist/db/schema/moderator_assignment.d.ts +14 -0
  171. package/dist/db/schema/moderator_assignment.d.ts.map +1 -0
  172. package/dist/db/schema/moderator_assignment.js +5 -0
  173. package/dist/db/schema/moderator_assignment.js.map +1 -0
  174. package/dist/db/schema/report.d.ts +25 -0
  175. package/dist/db/schema/report.d.ts.map +1 -0
  176. package/dist/db/schema/report.js +5 -0
  177. package/dist/db/schema/report.js.map +1 -0
  178. package/dist/db/schema/report_activity.d.ts +18 -0
  179. package/dist/db/schema/report_activity.d.ts.map +1 -0
  180. package/dist/db/schema/report_activity.js +5 -0
  181. package/dist/db/schema/report_activity.js.map +1 -0
  182. package/dist/db/schema/report_queue.d.ts +19 -0
  183. package/dist/db/schema/report_queue.d.ts.map +1 -0
  184. package/dist/db/schema/report_queue.js +5 -0
  185. package/dist/db/schema/report_queue.js.map +1 -0
  186. package/dist/db/schema/report_stat.d.ts +20 -0
  187. package/dist/db/schema/report_stat.d.ts.map +1 -0
  188. package/dist/db/schema/report_stat.js +5 -0
  189. package/dist/db/schema/report_stat.js.map +1 -0
  190. package/dist/lexicon/index.d.ts +50 -0
  191. package/dist/lexicon/index.d.ts.map +1 -1
  192. package/dist/lexicon/index.js +120 -2
  193. package/dist/lexicon/index.js.map +1 -1
  194. package/dist/lexicon/lexicons.d.ts +11255 -7885
  195. package/dist/lexicon/lexicons.d.ts.map +1 -1
  196. package/dist/lexicon/lexicons.js +1900 -120
  197. package/dist/lexicon/lexicons.js.map +1 -1
  198. package/dist/lexicon/types/app/bsky/embed/external.d.ts +2 -0
  199. package/dist/lexicon/types/app/bsky/embed/external.d.ts.map +1 -1
  200. package/dist/lexicon/types/app/bsky/embed/external.js.map +1 -1
  201. package/dist/lexicon/types/chat/bsky/actor/defs.d.ts +8 -2
  202. package/dist/lexicon/types/chat/bsky/actor/defs.d.ts.map +1 -1
  203. package/dist/lexicon/types/chat/bsky/actor/defs.js +9 -0
  204. package/dist/lexicon/types/chat/bsky/actor/defs.js.map +1 -1
  205. package/dist/lexicon/types/chat/bsky/convo/defs.d.ts +37 -10
  206. package/dist/lexicon/types/chat/bsky/convo/defs.d.ts.map +1 -1
  207. package/dist/lexicon/types/chat/bsky/convo/defs.js +9 -0
  208. package/dist/lexicon/types/chat/bsky/convo/defs.js.map +1 -1
  209. package/dist/lexicon/types/chat/bsky/convo/getMessages.d.ts +3 -0
  210. package/dist/lexicon/types/chat/bsky/convo/getMessages.d.ts.map +1 -1
  211. package/dist/lexicon/types/chat/bsky/convo/getMessages.js.map +1 -1
  212. package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts +2 -0
  213. package/dist/lexicon/types/tools/ozone/moderation/defs.d.ts.map +1 -1
  214. package/dist/lexicon/types/tools/ozone/moderation/defs.js.map +1 -1
  215. package/dist/lexicon/types/tools/ozone/moderation/emitEvent.d.ts +19 -0
  216. package/dist/lexicon/types/tools/ozone/moderation/emitEvent.d.ts.map +1 -1
  217. package/dist/lexicon/types/tools/ozone/moderation/emitEvent.js +9 -0
  218. package/dist/lexicon/types/tools/ozone/moderation/emitEvent.js.map +1 -1
  219. package/dist/lexicon/types/tools/ozone/queue/assignModerator.d.ts +27 -0
  220. package/dist/lexicon/types/tools/ozone/queue/assignModerator.d.ts.map +1 -0
  221. package/dist/lexicon/types/tools/ozone/queue/assignModerator.js +7 -0
  222. package/dist/lexicon/types/tools/ozone/queue/assignModerator.js.map +1 -0
  223. package/dist/lexicon/types/tools/ozone/queue/createQueue.d.ts +35 -0
  224. package/dist/lexicon/types/tools/ozone/queue/createQueue.d.ts.map +1 -0
  225. package/dist/lexicon/types/tools/ozone/queue/createQueue.js +7 -0
  226. package/dist/lexicon/types/tools/ozone/queue/createQueue.js.map +1 -0
  227. package/dist/lexicon/types/tools/ozone/queue/defs.d.ts +62 -0
  228. package/dist/lexicon/types/tools/ozone/queue/defs.d.ts.map +1 -0
  229. package/dist/lexicon/types/tools/ozone/queue/defs.js +34 -0
  230. package/dist/lexicon/types/tools/ozone/queue/defs.js.map +1 -0
  231. package/dist/lexicon/types/tools/ozone/queue/deleteQueue.d.ts +29 -0
  232. package/dist/lexicon/types/tools/ozone/queue/deleteQueue.d.ts.map +1 -0
  233. package/dist/lexicon/types/tools/ozone/queue/deleteQueue.js +7 -0
  234. package/dist/lexicon/types/tools/ozone/queue/deleteQueue.js.map +1 -0
  235. package/dist/lexicon/types/tools/ozone/queue/getAssignments.d.ts +30 -0
  236. package/dist/lexicon/types/tools/ozone/queue/getAssignments.d.ts.map +1 -0
  237. package/dist/lexicon/types/tools/ozone/queue/getAssignments.js +7 -0
  238. package/dist/lexicon/types/tools/ozone/queue/getAssignments.js.map +1 -0
  239. package/dist/lexicon/types/tools/ozone/queue/listQueues.d.ts +32 -0
  240. package/dist/lexicon/types/tools/ozone/queue/listQueues.d.ts.map +1 -0
  241. package/dist/lexicon/types/tools/ozone/queue/listQueues.js +7 -0
  242. package/dist/lexicon/types/tools/ozone/queue/listQueues.js.map +1 -0
  243. package/dist/lexicon/types/tools/ozone/queue/routeReports.d.ts +31 -0
  244. package/dist/lexicon/types/tools/ozone/queue/routeReports.d.ts.map +1 -0
  245. package/dist/lexicon/types/tools/ozone/queue/routeReports.js +7 -0
  246. package/dist/lexicon/types/tools/ozone/queue/routeReports.js.map +1 -0
  247. package/dist/lexicon/types/tools/ozone/queue/unassignModerator.d.ts +18 -0
  248. package/dist/lexicon/types/tools/ozone/queue/unassignModerator.d.ts.map +1 -0
  249. package/dist/lexicon/types/tools/ozone/queue/unassignModerator.js +7 -0
  250. package/dist/lexicon/types/tools/ozone/queue/unassignModerator.js.map +1 -0
  251. package/dist/lexicon/types/tools/ozone/queue/updateQueue.d.ts +32 -0
  252. package/dist/lexicon/types/tools/ozone/queue/updateQueue.d.ts.map +1 -0
  253. package/dist/lexicon/types/tools/ozone/queue/updateQueue.js +7 -0
  254. package/dist/lexicon/types/tools/ozone/queue/updateQueue.js.map +1 -0
  255. package/dist/lexicon/types/tools/ozone/report/assignModerator.d.ts +31 -0
  256. package/dist/lexicon/types/tools/ozone/report/assignModerator.d.ts.map +1 -0
  257. package/dist/lexicon/types/tools/ozone/report/assignModerator.js +7 -0
  258. package/dist/lexicon/types/tools/ozone/report/assignModerator.js.map +1 -0
  259. package/dist/lexicon/types/tools/ozone/report/createActivity.d.ts +37 -0
  260. package/dist/lexicon/types/tools/ozone/report/createActivity.d.ts.map +1 -0
  261. package/dist/lexicon/types/tools/ozone/report/createActivity.js +7 -0
  262. package/dist/lexicon/types/tools/ozone/report/createActivity.js.map +1 -0
  263. package/dist/lexicon/types/tools/ozone/report/defs.d.ts +185 -0
  264. package/dist/lexicon/types/tools/ozone/report/defs.d.ts.map +1 -1
  265. package/dist/lexicon/types/tools/ozone/report/defs.js +108 -0
  266. package/dist/lexicon/types/tools/ozone/report/defs.js.map +1 -1
  267. package/dist/lexicon/types/tools/ozone/report/getAssignments.d.ts +30 -0
  268. package/dist/lexicon/types/tools/ozone/report/getAssignments.d.ts.map +1 -0
  269. package/dist/lexicon/types/tools/ozone/report/getAssignments.js +7 -0
  270. package/dist/lexicon/types/tools/ozone/report/getAssignments.js.map +1 -0
  271. package/dist/lexicon/types/tools/ozone/report/getHistoricalStats.d.ts +36 -0
  272. package/dist/lexicon/types/tools/ozone/report/getHistoricalStats.d.ts.map +1 -0
  273. package/dist/lexicon/types/tools/ozone/report/getHistoricalStats.js +7 -0
  274. package/dist/lexicon/types/tools/ozone/report/getHistoricalStats.js.map +1 -0
  275. package/dist/lexicon/types/tools/ozone/report/getLatestReport.d.ts +21 -0
  276. package/dist/lexicon/types/tools/ozone/report/getLatestReport.d.ts.map +1 -0
  277. package/dist/lexicon/types/tools/ozone/report/getLatestReport.js +7 -0
  278. package/dist/lexicon/types/tools/ozone/report/getLatestReport.js.map +1 -0
  279. package/dist/lexicon/types/tools/ozone/report/getLiveStats.d.ts +27 -0
  280. package/dist/lexicon/types/tools/ozone/report/getLiveStats.d.ts.map +1 -0
  281. package/dist/lexicon/types/tools/ozone/report/getLiveStats.js +7 -0
  282. package/dist/lexicon/types/tools/ozone/report/getLiveStats.js.map +1 -0
  283. package/dist/lexicon/types/tools/ozone/report/getReport.d.ts +22 -0
  284. package/dist/lexicon/types/tools/ozone/report/getReport.d.ts.map +1 -0
  285. package/dist/lexicon/types/tools/ozone/report/getReport.js +7 -0
  286. package/dist/lexicon/types/tools/ozone/report/getReport.js.map +1 -0
  287. package/dist/lexicon/types/tools/ozone/report/listActivities.d.ts +26 -0
  288. package/dist/lexicon/types/tools/ozone/report/listActivities.d.ts.map +1 -0
  289. package/dist/lexicon/types/tools/ozone/report/listActivities.js +7 -0
  290. package/dist/lexicon/types/tools/ozone/report/listActivities.js.map +1 -0
  291. package/dist/lexicon/types/tools/ozone/report/queryReports.d.ts +48 -0
  292. package/dist/lexicon/types/tools/ozone/report/queryReports.d.ts.map +1 -0
  293. package/dist/lexicon/types/tools/ozone/report/queryReports.js +7 -0
  294. package/dist/lexicon/types/tools/ozone/report/queryReports.js.map +1 -0
  295. package/dist/lexicon/types/tools/ozone/report/reassignQueue.d.ts +31 -0
  296. package/dist/lexicon/types/tools/ozone/report/reassignQueue.d.ts.map +1 -0
  297. package/dist/lexicon/types/tools/ozone/report/reassignQueue.js +7 -0
  298. package/dist/lexicon/types/tools/ozone/report/reassignQueue.js.map +1 -0
  299. package/dist/lexicon/types/tools/ozone/report/refreshStats.d.ts +28 -0
  300. package/dist/lexicon/types/tools/ozone/report/refreshStats.d.ts.map +1 -0
  301. package/dist/lexicon/types/tools/ozone/report/refreshStats.js +7 -0
  302. package/dist/lexicon/types/tools/ozone/report/refreshStats.js.map +1 -0
  303. package/dist/lexicon/types/tools/ozone/report/unassignModerator.d.ts +25 -0
  304. package/dist/lexicon/types/tools/ozone/report/unassignModerator.d.ts.map +1 -0
  305. package/dist/lexicon/types/tools/ozone/report/unassignModerator.js +7 -0
  306. package/dist/lexicon/types/tools/ozone/report/unassignModerator.js.map +1 -0
  307. package/dist/mod-service/expiring-tags.d.ts +27 -0
  308. package/dist/mod-service/expiring-tags.d.ts.map +1 -0
  309. package/dist/mod-service/expiring-tags.js +62 -0
  310. package/dist/mod-service/expiring-tags.js.map +1 -0
  311. package/dist/mod-service/index.d.ts +3 -1
  312. package/dist/mod-service/index.d.ts.map +1 -1
  313. package/dist/mod-service/index.js +61 -2
  314. package/dist/mod-service/index.js.map +1 -1
  315. package/dist/mod-service/report.d.ts +64 -0
  316. package/dist/mod-service/report.d.ts.map +1 -0
  317. package/dist/mod-service/report.js +282 -0
  318. package/dist/mod-service/report.js.map +1 -0
  319. package/dist/mod-service/status.d.ts +24 -0
  320. package/dist/mod-service/status.d.ts.map +1 -1
  321. package/dist/queue/service.d.ts +86 -0
  322. package/dist/queue/service.d.ts.map +1 -0
  323. package/dist/queue/service.js +430 -0
  324. package/dist/queue/service.js.map +1 -0
  325. package/dist/report/activity.d.ts +77 -0
  326. package/dist/report/activity.d.ts.map +1 -0
  327. package/dist/report/activity.js +141 -0
  328. package/dist/report/activity.js.map +1 -0
  329. package/dist/report/handle-report-update.d.ts +47 -0
  330. package/dist/report/handle-report-update.d.ts.map +1 -0
  331. package/dist/report/handle-report-update.js +178 -0
  332. package/dist/report/handle-report-update.js.map +1 -0
  333. package/dist/report/reassign.d.ts +10 -0
  334. package/dist/report/reassign.d.ts.map +1 -0
  335. package/dist/report/reassign.js +75 -0
  336. package/dist/report/reassign.js.map +1 -0
  337. package/dist/report/stats.d.ts +105 -0
  338. package/dist/report/stats.d.ts.map +1 -0
  339. package/dist/report/stats.js +619 -0
  340. package/dist/report/stats.js.map +1 -0
  341. package/dist/report/views.d.ts +111 -0
  342. package/dist/report/views.d.ts.map +1 -0
  343. package/dist/report/views.js +156 -0
  344. package/dist/report/views.js.map +1 -0
  345. package/dist/team/index.d.ts +1 -0
  346. package/dist/team/index.d.ts.map +1 -1
  347. package/dist/team/index.js +11 -0
  348. package/dist/team/index.js.map +1 -1
  349. package/package.json +3 -3
  350. package/src/api/index.ts +40 -0
  351. package/src/api/moderation/emitEvent.ts +38 -0
  352. package/src/api/queue/assignModerator.ts +31 -0
  353. package/src/api/queue/createQueue.ts +62 -0
  354. package/src/api/queue/deleteQueue.ts +56 -0
  355. package/src/api/queue/getAssignments.ts +19 -0
  356. package/src/api/queue/listQueues.ts +39 -0
  357. package/src/api/queue/routeReports.ts +44 -0
  358. package/src/api/queue/unassignModerator.ts +26 -0
  359. package/src/api/queue/updateQueue.ts +54 -0
  360. package/src/api/report/assignModerator.ts +36 -0
  361. package/src/api/report/createActivity.ts +57 -0
  362. package/src/api/report/getAssignments.ts +20 -0
  363. package/src/api/report/getHistoricalStats.ts +41 -0
  364. package/src/api/report/getLatestReport.ts +44 -0
  365. package/src/api/report/getLiveStats.ts +26 -0
  366. package/src/api/report/getReport.ts +55 -0
  367. package/src/api/report/listActivities.ts +34 -0
  368. package/src/api/report/queryReports.ts +44 -0
  369. package/src/api/report/reassignQueue.ts +68 -0
  370. package/src/api/report/refreshStats.ts +27 -0
  371. package/src/api/report/unassignModerator.ts +21 -0
  372. package/src/api/util.ts +12 -0
  373. package/src/assignment/index.ts +731 -0
  374. package/src/config/config.ts +27 -0
  375. package/src/config/env.ts +8 -0
  376. package/src/context.ts +31 -0
  377. package/src/daemon/context.ts +34 -0
  378. package/src/daemon/event-reverser.ts +50 -1
  379. package/src/daemon/job-cursor.ts +33 -0
  380. package/src/daemon/queue-router.ts +101 -0
  381. package/src/daemon/stats-computer.ts +101 -0
  382. package/src/daemon/strike-expiry-processor.ts +4 -20
  383. package/src/db/migrations/20260219T164523000Z-create-report-table.ts +155 -0
  384. package/src/db/migrations/20260219T165302248Z-moderator-assignment.ts +42 -0
  385. package/src/db/migrations/20260225T000000000Z-add-report-queue-table.ts +41 -0
  386. package/src/db/migrations/20260313T000000000Z-add-report-activity-table.ts +48 -0
  387. package/src/db/migrations/20260318T152058935Z-add-report-stat.ts +35 -0
  388. package/src/db/migrations/20260428T000000000Z-add-expiring-tag-table.ts +32 -0
  389. package/src/db/migrations/index.ts +6 -0
  390. package/src/db/pagination.ts +85 -0
  391. package/src/db/schema/expiring_tag.ts +17 -0
  392. package/src/db/schema/index.ts +13 -1
  393. package/src/db/schema/moderator_assignment.ts +16 -0
  394. package/src/db/schema/report.ts +27 -0
  395. package/src/db/schema/report_activity.ts +22 -0
  396. package/src/db/schema/report_queue.ts +21 -0
  397. package/src/db/schema/report_stat.ts +27 -0
  398. package/src/lexicon/index.ts +280 -0
  399. package/src/lexicon/lexicons.ts +2083 -214
  400. package/src/lexicon/types/app/bsky/embed/external.ts +2 -0
  401. package/src/lexicon/types/chat/bsky/actor/defs.ts +17 -1
  402. package/src/lexicon/types/chat/bsky/convo/defs.ts +50 -10
  403. package/src/lexicon/types/chat/bsky/convo/getMessages.ts +3 -0
  404. package/src/lexicon/types/tools/ozone/moderation/defs.ts +2 -0
  405. package/src/lexicon/types/tools/ozone/moderation/emitEvent.ts +24 -0
  406. package/src/lexicon/types/tools/ozone/queue/assignModerator.ts +46 -0
  407. package/src/lexicon/types/tools/ozone/queue/createQueue.ts +54 -0
  408. package/src/lexicon/types/tools/ozone/queue/defs.ts +99 -0
  409. package/src/lexicon/types/tools/ozone/queue/deleteQueue.ts +48 -0
  410. package/src/lexicon/types/tools/ozone/queue/getAssignments.ts +48 -0
  411. package/src/lexicon/types/tools/ozone/queue/listQueues.ts +50 -0
  412. package/src/lexicon/types/tools/ozone/queue/routeReports.ts +50 -0
  413. package/src/lexicon/types/tools/ozone/queue/unassignModerator.ts +37 -0
  414. package/src/lexicon/types/tools/ozone/queue/updateQueue.ts +51 -0
  415. package/src/lexicon/types/tools/ozone/report/assignModerator.ts +50 -0
  416. package/src/lexicon/types/tools/ozone/report/createActivity.ts +60 -0
  417. package/src/lexicon/types/tools/ozone/report/defs.ts +327 -0
  418. package/src/lexicon/types/tools/ozone/report/getAssignments.ts +48 -0
  419. package/src/lexicon/types/tools/ozone/report/getHistoricalStats.ts +54 -0
  420. package/src/lexicon/types/tools/ozone/report/getLatestReport.ts +39 -0
  421. package/src/lexicon/types/tools/ozone/report/getLiveStats.ts +45 -0
  422. package/src/lexicon/types/tools/ozone/report/getReport.ts +38 -0
  423. package/src/lexicon/types/tools/ozone/report/listActivities.ts +44 -0
  424. package/src/lexicon/types/tools/ozone/report/queryReports.ts +72 -0
  425. package/src/lexicon/types/tools/ozone/report/reassignQueue.ts +55 -0
  426. package/src/lexicon/types/tools/ozone/report/refreshStats.ts +46 -0
  427. package/src/lexicon/types/tools/ozone/report/unassignModerator.ts +44 -0
  428. package/src/mod-service/expiring-tags.ts +98 -0
  429. package/src/mod-service/index.ts +71 -3
  430. package/src/mod-service/report.ts +408 -0
  431. package/src/queue/service.ts +599 -0
  432. package/src/report/activity.ts +234 -0
  433. package/src/report/handle-report-update.ts +209 -0
  434. package/src/report/reassign.ts +109 -0
  435. package/src/report/stats.ts +850 -0
  436. package/src/report/views.ts +241 -0
  437. package/src/team/index.ts +11 -0
  438. package/tests/expiring-tags.test.ts +231 -0
  439. package/tests/get-report.test.ts +136 -0
  440. package/tests/query-reports.test.ts +608 -0
  441. package/tests/queue-assignment.test.ts +428 -0
  442. package/tests/queue-router.test.ts +306 -0
  443. package/tests/queues.test.ts +690 -0
  444. package/tests/report-action.test.ts +308 -0
  445. package/tests/report-activity.test.ts +567 -0
  446. package/tests/report-assignment.test.ts +517 -0
  447. package/tests/report-reassign-queue.test.ts +340 -0
  448. package/tests/report-routing.test.ts +245 -0
  449. package/tests/report-stats.test.ts +545 -0
  450. package/tsconfig.build.tsbuildinfo +1 -1
@@ -0,0 +1,234 @@
1
+ import { InvalidRequestError } from '@atproto/xrpc-server'
2
+ import { Database } from '../db'
3
+ import { Member } from '../lexicon/types/tools/ozone/team/defs'
4
+ import {
5
+ AlreadyInTargetState,
6
+ InvalidStateTransition,
7
+ handleReportUpdate,
8
+ } from './handle-report-update'
9
+
10
+ export type ActivityType =
11
+ | 'queueActivity'
12
+ | 'assignmentActivity'
13
+ | 'escalationActivity'
14
+ | 'closeActivity'
15
+ | 'reopenActivity'
16
+ | 'noteActivity'
17
+
18
+ export type CreateActivityParams = {
19
+ reportId: number
20
+ activityType: ActivityType
21
+ internalNote?: string
22
+ publicNote?: string
23
+ meta?: Record<string, unknown>
24
+ /** Set true for activities created by automated processes (e.g. queue router). */
25
+ isAutomated?: boolean
26
+ createdBy: string
27
+ }
28
+
29
+ export async function createReportActivity(
30
+ db: Database,
31
+ params: CreateActivityParams,
32
+ ) {
33
+ const {
34
+ reportId,
35
+ activityType,
36
+ internalNote,
37
+ publicNote,
38
+ meta,
39
+ isAutomated = false,
40
+ createdBy,
41
+ } = params
42
+
43
+ return db.transaction(async (dbTxn) => {
44
+ // Lock the report row for the duration of the transaction to prevent
45
+ // concurrent writes from racing on status validation + update.
46
+ const report = await dbTxn.db
47
+ .selectFrom('report')
48
+ .select(['id', 'status'])
49
+ .where('id', '=', reportId)
50
+ .forUpdate()
51
+ .executeTakeFirst()
52
+
53
+ if (!report) {
54
+ throw new InvalidRequestError(
55
+ `Report ${reportId} not found`,
56
+ 'ReportNotFound',
57
+ )
58
+ }
59
+
60
+ let result
61
+ try {
62
+ result = handleReportUpdate(report.status, {
63
+ type: 'activity',
64
+ activityType,
65
+ })
66
+ } catch (err) {
67
+ if (err instanceof AlreadyInTargetState) {
68
+ throw new InvalidRequestError(err.message, 'AlreadyInTargetState')
69
+ }
70
+ if (err instanceof InvalidStateTransition) {
71
+ throw new InvalidRequestError(err.message, 'InvalidStateTransition')
72
+ }
73
+ throw err
74
+ }
75
+
76
+ const now = new Date().toISOString()
77
+
78
+ if (result.nextStatus !== null) {
79
+ const updateSet: Record<string, string | null> = {
80
+ status: result.nextStatus,
81
+ updatedAt: now,
82
+ }
83
+ if (result.nextStatus === 'closed') {
84
+ updateSet.closedAt = now
85
+ } else if (result.nextStatus === 'open') {
86
+ updateSet.closedAt = null
87
+ }
88
+ await dbTxn.db
89
+ .updateTable('report')
90
+ .set(updateSet)
91
+ .where('id', '=', reportId)
92
+ .execute()
93
+ }
94
+
95
+ const [activity] = await dbTxn.db
96
+ .insertInto('report_activity')
97
+ .values({
98
+ reportId,
99
+ activityType,
100
+ previousStatus: result.activity?.previousStatus ?? null,
101
+ internalNote: internalNote ?? null,
102
+ publicNote: publicNote ?? null,
103
+ meta: meta ?? null,
104
+ isAutomated,
105
+ createdBy,
106
+ createdAt: now,
107
+ })
108
+ .returningAll()
109
+ .execute()
110
+
111
+ return activity
112
+ })
113
+ }
114
+
115
+ export type BulkActivityInsert = {
116
+ reportId: number
117
+ activityType: string
118
+ previousStatus: string | null
119
+ internalNote?: string
120
+ publicNote?: string
121
+ meta?: unknown
122
+ isAutomated: boolean
123
+ createdBy: string
124
+ createdAt: string
125
+ }
126
+
127
+ /**
128
+ * Insert multiple activity rows in a single query. No validation — caller is
129
+ * responsible for correctness and for being inside an appropriate transaction.
130
+ */
131
+ export async function bulkInsertReportActivities(
132
+ db: Database,
133
+ activities: BulkActivityInsert[],
134
+ ) {
135
+ if (!activities.length) return
136
+ await db.db
137
+ .insertInto('report_activity')
138
+ .values(
139
+ activities.map((a) => ({
140
+ reportId: a.reportId,
141
+ activityType: a.activityType,
142
+ previousStatus: a.previousStatus,
143
+ internalNote: a.internalNote ?? null,
144
+ publicNote: a.publicNote ?? null,
145
+ meta: a.meta ?? null,
146
+ isAutomated: a.isAutomated,
147
+ createdBy: a.createdBy,
148
+ createdAt: a.createdAt,
149
+ })),
150
+ )
151
+ .execute()
152
+ }
153
+
154
+ export type ListActivitiesParams = {
155
+ reportId: number
156
+ limit?: number
157
+ cursor?: string
158
+ }
159
+
160
+ export async function listReportActivities(
161
+ db: Database,
162
+ params: ListActivitiesParams,
163
+ ) {
164
+ const { reportId, limit = 50, cursor } = params
165
+
166
+ let builder = db.db
167
+ .selectFrom('report_activity')
168
+ .selectAll()
169
+ .where('reportId', '=', reportId)
170
+ .orderBy('createdAt', 'desc')
171
+ .orderBy('id', 'desc')
172
+ .limit(limit + 1)
173
+
174
+ if (cursor) {
175
+ const cursorId = parseInt(cursor, 10)
176
+ if (!isNaN(cursorId)) {
177
+ builder = builder.where('id', '<', cursorId)
178
+ }
179
+ }
180
+
181
+ const rows = await builder.execute()
182
+ const hasMore = rows.length > limit
183
+ const activities = hasMore ? rows.slice(0, limit) : rows
184
+
185
+ const nextCursor =
186
+ hasMore && activities.length > 0
187
+ ? String(activities[activities.length - 1].id)
188
+ : undefined
189
+
190
+ return { activities, cursor: nextCursor }
191
+ }
192
+
193
+ function buildActivityObject(
194
+ activityType: string,
195
+ previousStatus: string | null,
196
+ ): { $type: string; [k: string]: unknown } {
197
+ const $type = `tools.ozone.report.defs#${activityType}`
198
+ if (previousStatus !== null) {
199
+ return { $type, previousStatus }
200
+ }
201
+ return { $type }
202
+ }
203
+
204
+ export function formatActivityView(
205
+ activity: {
206
+ id: number
207
+ reportId: number
208
+ activityType: string
209
+ previousStatus: string | null
210
+ internalNote: string | null
211
+ publicNote: string | null
212
+ meta: unknown
213
+ isAutomated: boolean
214
+ createdBy: string
215
+ createdAt: string
216
+ },
217
+ memberViews?: Map<string, Member>,
218
+ ) {
219
+ return {
220
+ id: activity.id,
221
+ reportId: activity.reportId,
222
+ activity: buildActivityObject(
223
+ activity.activityType,
224
+ activity.previousStatus,
225
+ ),
226
+ internalNote: activity.internalNote ?? undefined,
227
+ publicNote: activity.publicNote ?? undefined,
228
+ meta: (activity.meta as Record<string, unknown>) ?? undefined,
229
+ isAutomated: activity.isAutomated,
230
+ createdBy: activity.createdBy,
231
+ moderator: memberViews?.get(activity.createdBy),
232
+ createdAt: activity.createdAt,
233
+ }
234
+ }
@@ -0,0 +1,209 @@
1
+ /**
2
+ * Pure, synchronous state-transition logic for reports.
3
+ *
4
+ * Every code path that changes a report's status or creates a report activity
5
+ * should call `handleReportUpdate` to determine the next status and the
6
+ * activity record to insert. This keeps the state machine in one place and
7
+ * decouples it from DB operations so it works for both single-row transactions
8
+ * and bulk updates.
9
+ */
10
+
11
+ // ---------------------------------------------------------------------------
12
+ // Error types — callers decide how to surface these (throw, skip, etc.)
13
+ // ---------------------------------------------------------------------------
14
+
15
+ export class AlreadyInTargetState extends Error {
16
+ constructor(
17
+ public currentStatus: string,
18
+ public targetStatus: string,
19
+ ) {
20
+ super(`Report is already in '${targetStatus}' status`)
21
+ this.name = 'AlreadyInTargetState'
22
+ }
23
+ }
24
+
25
+ export class InvalidStateTransition extends Error {
26
+ constructor(
27
+ public fromStatus: string,
28
+ public toStatus: string,
29
+ ) {
30
+ super(`Cannot transition report from '${fromStatus}' to '${toStatus}'`)
31
+ this.name = 'InvalidStateTransition'
32
+ }
33
+ }
34
+
35
+ // ---------------------------------------------------------------------------
36
+ // State machine tables
37
+ // ---------------------------------------------------------------------------
38
+
39
+ /** Valid state transitions: key = fromState, value = allowed toStates */
40
+ export const VALID_TRANSITIONS: Record<string, string[]> = {
41
+ open: ['closed', 'escalated', 'queued', 'assigned'],
42
+ closed: ['open'],
43
+ escalated: ['open', 'closed'],
44
+ queued: ['assigned', 'open'],
45
+ assigned: ['open', 'closed', 'escalated', 'queued'],
46
+ }
47
+
48
+ /** Activity types that map to a specific target status */
49
+ const ACTIVITY_TO_STATE: Record<string, string> = {
50
+ queueActivity: 'queued',
51
+ assignmentActivity: 'assigned',
52
+ escalationActivity: 'escalated',
53
+ closeActivity: 'closed',
54
+ reopenActivity: 'open',
55
+ }
56
+
57
+ /** Activity types that are only valid from specific source states */
58
+ const ACTIVITY_VALID_FROM_STATES: Record<string, string[]> = {
59
+ reopenActivity: ['closed'],
60
+ }
61
+
62
+ /** Moderation event types → target status (+ activity type) */
63
+ const EVENT_TYPE_MAP: Record<string, { status: string; activityType: string }> =
64
+ {
65
+ 'tools.ozone.moderation.defs#modEventAcknowledge': {
66
+ status: 'closed',
67
+ activityType: 'closeActivity',
68
+ },
69
+ 'tools.ozone.moderation.defs#modEventTakedown': {
70
+ status: 'closed',
71
+ activityType: 'closeActivity',
72
+ },
73
+ 'tools.ozone.moderation.defs#modEventLabel': {
74
+ status: 'closed',
75
+ activityType: 'closeActivity',
76
+ },
77
+ 'tools.ozone.moderation.defs#modEventComment': {
78
+ status: 'closed',
79
+ activityType: 'closeActivity',
80
+ },
81
+ 'tools.ozone.moderation.defs#modEventEscalate': {
82
+ status: 'escalated',
83
+ activityType: 'escalationActivity',
84
+ },
85
+ }
86
+
87
+ // ---------------------------------------------------------------------------
88
+ // Action types — the three ways a report's status can change
89
+ // ---------------------------------------------------------------------------
90
+
91
+ export type ReportUpdateAction =
92
+ | { type: 'activity'; activityType: string }
93
+ | { type: 'event'; eventType: string }
94
+ | { type: 'queue' }
95
+
96
+ // ---------------------------------------------------------------------------
97
+ // Result type
98
+ // ---------------------------------------------------------------------------
99
+
100
+ export type ActivityRecord = {
101
+ activityType: string
102
+ previousStatus: string
103
+ }
104
+
105
+ export type ReportUpdateResult = {
106
+ nextStatus: string | null
107
+ activity: ActivityRecord | null
108
+ }
109
+
110
+ // ---------------------------------------------------------------------------
111
+ // Core function
112
+ // ---------------------------------------------------------------------------
113
+
114
+ /**
115
+ * Determines the next status and activity record for a report update.
116
+ *
117
+ * @throws AlreadyInTargetState if the report is already in the target status
118
+ * @throws InvalidStateTransition if the transition is not allowed
119
+ * @returns nextStatus (null = no change) and activity (null = nothing to record)
120
+ */
121
+ export function handleReportUpdate(
122
+ currentStatus: string,
123
+ action: ReportUpdateAction,
124
+ ): ReportUpdateResult {
125
+ switch (action.type) {
126
+ case 'activity':
127
+ return handleActivityAction(currentStatus, action.activityType)
128
+ case 'event':
129
+ return handleEventAction(currentStatus, action.eventType)
130
+ case 'queue':
131
+ return handleQueueAction(currentStatus)
132
+ }
133
+ }
134
+
135
+ // ---------------------------------------------------------------------------
136
+ // Action handlers
137
+ // ---------------------------------------------------------------------------
138
+
139
+ function handleActivityAction(
140
+ currentStatus: string,
141
+ activityType: string,
142
+ ): ReportUpdateResult {
143
+ const toState = ACTIVITY_TO_STATE[activityType] ?? null
144
+
145
+ // Note-type activities — no state change, but still produce an activity record
146
+ if (toState === null) {
147
+ return { nextStatus: null, activity: null }
148
+ }
149
+
150
+ // Check activity-specific source-state constraints
151
+ const validFromStates = ACTIVITY_VALID_FROM_STATES[activityType]
152
+ if (validFromStates && !validFromStates.includes(currentStatus)) {
153
+ throw new InvalidStateTransition(currentStatus, toState)
154
+ }
155
+
156
+ validateTransition(currentStatus, toState)
157
+
158
+ return {
159
+ nextStatus: toState,
160
+ activity: { activityType, previousStatus: currentStatus },
161
+ }
162
+ }
163
+
164
+ function handleEventAction(
165
+ currentStatus: string,
166
+ eventType: string,
167
+ ): ReportUpdateResult {
168
+ const mapping = EVENT_TYPE_MAP[eventType]
169
+ if (!mapping) {
170
+ // Event type doesn't affect report status
171
+ return { nextStatus: null, activity: null }
172
+ }
173
+
174
+ validateTransition(currentStatus, mapping.status)
175
+
176
+ return {
177
+ nextStatus: mapping.status,
178
+ activity: {
179
+ activityType: mapping.activityType,
180
+ previousStatus: currentStatus,
181
+ },
182
+ }
183
+ }
184
+
185
+ function handleQueueAction(currentStatus: string): ReportUpdateResult {
186
+ // Queue routing only transitions open → queued
187
+ if (currentStatus !== 'open') {
188
+ return { nextStatus: null, activity: null }
189
+ }
190
+
191
+ return {
192
+ nextStatus: 'queued',
193
+ activity: { activityType: 'queueActivity', previousStatus: currentStatus },
194
+ }
195
+ }
196
+
197
+ // ---------------------------------------------------------------------------
198
+ // Shared validation
199
+ // ---------------------------------------------------------------------------
200
+
201
+ function validateTransition(fromStatus: string, toStatus: string): void {
202
+ if (fromStatus === toStatus) {
203
+ throw new AlreadyInTargetState(fromStatus, toStatus)
204
+ }
205
+ const allowed = VALID_TRANSITIONS[fromStatus] ?? []
206
+ if (!allowed.includes(toStatus)) {
207
+ throw new InvalidStateTransition(fromStatus, toStatus)
208
+ }
209
+ }
@@ -0,0 +1,109 @@
1
+ import { InvalidRequestError } from '@atproto/xrpc-server'
2
+ import { Database } from '../db'
3
+ import { QueueService } from '../queue/service'
4
+
5
+ export type ReassignReportQueueParams = {
6
+ reportId: number
7
+ toQueueId: number
8
+ comment?: string
9
+ createdBy: string
10
+ }
11
+
12
+ export async function reassignReportQueue(
13
+ db: Database,
14
+ queueService: QueueService,
15
+ params: ReassignReportQueueParams,
16
+ ): Promise<void> {
17
+ const { reportId, toQueueId, comment, createdBy } = params
18
+
19
+ if (toQueueId !== -1) {
20
+ const queue = await queueService.getById(toQueueId)
21
+ if (!queue) {
22
+ throw new InvalidRequestError(
23
+ `Queue ${toQueueId} not found`,
24
+ 'QueueNotFound',
25
+ )
26
+ }
27
+ if (!queue.enabled) {
28
+ throw new InvalidRequestError(
29
+ `Queue ${toQueueId} is disabled`,
30
+ 'QueueDisabled',
31
+ )
32
+ }
33
+ }
34
+
35
+ await db.transaction(async (dbTxn) => {
36
+ const report = await dbTxn.db
37
+ .selectFrom('report')
38
+ .select(['id', 'status', 'queueId'])
39
+ .where('id', '=', reportId)
40
+ .forUpdate()
41
+ .executeTakeFirst()
42
+
43
+ if (!report) {
44
+ throw new InvalidRequestError(
45
+ `Report ${reportId} not found`,
46
+ 'ReportNotFound',
47
+ )
48
+ }
49
+
50
+ if (report.status === 'closed') {
51
+ throw new InvalidRequestError(
52
+ `Report ${reportId} is closed and cannot be reassigned`,
53
+ 'ReportClosed',
54
+ )
55
+ }
56
+
57
+ // NULL and -1 both mean "unassigned" for equivalence purposes.
58
+ const currentQueueId = report.queueId ?? -1
59
+ if (currentQueueId === toQueueId) {
60
+ throw new InvalidRequestError(
61
+ `Report ${reportId} is already in queue ${toQueueId}`,
62
+ 'AlreadyInTargetQueue',
63
+ )
64
+ }
65
+
66
+ const previousStatus = report.status
67
+ let nextStatus: string = previousStatus
68
+ if (toQueueId !== -1 && previousStatus === 'open') {
69
+ nextStatus = 'queued'
70
+ } else if (toQueueId === -1 && previousStatus === 'queued') {
71
+ nextStatus = 'open'
72
+ }
73
+
74
+ const now = new Date().toISOString()
75
+
76
+ const reportUpdate: Record<string, string | number | null> = {
77
+ queueId: toQueueId,
78
+ queuedAt: toQueueId === -1 ? null : now,
79
+ updatedAt: now,
80
+ }
81
+ if (nextStatus !== previousStatus) {
82
+ reportUpdate.status = nextStatus
83
+ }
84
+
85
+ await dbTxn.db
86
+ .updateTable('report')
87
+ .set(reportUpdate)
88
+ .where('id', '=', reportId)
89
+ .execute()
90
+
91
+ await dbTxn.db
92
+ .insertInto('report_activity')
93
+ .values({
94
+ reportId,
95
+ activityType: 'queueActivity',
96
+ previousStatus,
97
+ internalNote: comment ?? null,
98
+ publicNote: null,
99
+ meta: {
100
+ fromQueueId: report.queueId ?? null,
101
+ toQueueId,
102
+ },
103
+ isAutomated: false,
104
+ createdBy,
105
+ createdAt: now,
106
+ })
107
+ .execute()
108
+ })
109
+ }