@atproto/ozone 0.1.173 → 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.
- package/CHANGELOG.md +9 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +40 -0
- package/dist/api/index.js.map +1 -1
- package/dist/api/moderation/emitEvent.d.ts.map +1 -1
- package/dist/api/moderation/emitEvent.js +31 -0
- package/dist/api/moderation/emitEvent.js.map +1 -1
- package/dist/api/queue/assignModerator.d.ts +4 -0
- package/dist/api/queue/assignModerator.d.ts.map +1 -0
- package/dist/api/queue/assignModerator.js +28 -0
- package/dist/api/queue/assignModerator.js.map +1 -0
- package/dist/api/queue/createQueue.d.ts +4 -0
- package/dist/api/queue/createQueue.d.ts.map +1 -0
- package/dist/api/queue/createQueue.js +44 -0
- package/dist/api/queue/createQueue.js.map +1 -0
- package/dist/api/queue/deleteQueue.d.ts +4 -0
- package/dist/api/queue/deleteQueue.d.ts.map +1 -0
- package/dist/api/queue/deleteQueue.js +40 -0
- package/dist/api/queue/deleteQueue.js.map +1 -0
- package/dist/api/queue/getAssignments.d.ts +4 -0
- package/dist/api/queue/getAssignments.d.ts.map +1 -0
- package/dist/api/queue/getAssignments.js +19 -0
- package/dist/api/queue/getAssignments.js.map +1 -0
- package/dist/api/queue/listQueues.d.ts +4 -0
- package/dist/api/queue/listQueues.d.ts.map +1 -0
- package/dist/api/queue/listQueues.js +29 -0
- package/dist/api/queue/listQueues.js.map +1 -0
- package/dist/api/queue/routeReports.d.ts +4 -0
- package/dist/api/queue/routeReports.d.ts.map +1 -0
- package/dist/api/queue/routeReports.js +33 -0
- package/dist/api/queue/routeReports.js.map +1 -0
- package/dist/api/queue/unassignModerator.d.ts +4 -0
- package/dist/api/queue/unassignModerator.d.ts.map +1 -0
- package/dist/api/queue/unassignModerator.js +24 -0
- package/dist/api/queue/unassignModerator.js.map +1 -0
- package/dist/api/queue/updateQueue.d.ts +4 -0
- package/dist/api/queue/updateQueue.d.ts.map +1 -0
- package/dist/api/queue/updateQueue.js +39 -0
- package/dist/api/queue/updateQueue.js.map +1 -0
- package/dist/api/report/assignModerator.d.ts +4 -0
- package/dist/api/report/assignModerator.d.ts.map +1 -0
- package/dist/api/report/assignModerator.js +33 -0
- package/dist/api/report/assignModerator.js.map +1 -0
- package/dist/api/report/createActivity.d.ts +4 -0
- package/dist/api/report/createActivity.d.ts.map +1 -0
- package/dist/api/report/createActivity.js +44 -0
- package/dist/api/report/createActivity.js.map +1 -0
- package/dist/api/report/getAssignments.d.ts +4 -0
- package/dist/api/report/getAssignments.d.ts.map +1 -0
- package/dist/api/report/getAssignments.js +19 -0
- package/dist/api/report/getAssignments.js.map +1 -0
- package/dist/api/report/getHistoricalStats.d.ts +4 -0
- package/dist/api/report/getHistoricalStats.d.ts.map +1 -0
- package/dist/api/report/getHistoricalStats.js +32 -0
- package/dist/api/report/getHistoricalStats.js.map +1 -0
- package/dist/api/report/getLatestReport.d.ts +4 -0
- package/dist/api/report/getLatestReport.d.ts.map +1 -0
- package/dist/api/report/getLatestReport.js +31 -0
- package/dist/api/report/getLatestReport.js.map +1 -0
- package/dist/api/report/getLiveStats.d.ts +4 -0
- package/dist/api/report/getLiveStats.d.ts.map +1 -0
- package/dist/api/report/getLiveStats.js +25 -0
- package/dist/api/report/getLiveStats.js.map +1 -0
- package/dist/api/report/getReport.d.ts +4 -0
- package/dist/api/report/getReport.d.ts.map +1 -0
- package/dist/api/report/getReport.js +35 -0
- package/dist/api/report/getReport.js.map +1 -0
- package/dist/api/report/listActivities.d.ts +4 -0
- package/dist/api/report/listActivities.d.ts.map +1 -0
- package/dist/api/report/listActivities.js +25 -0
- package/dist/api/report/listActivities.js.map +1 -0
- package/dist/api/report/queryReports.d.ts +4 -0
- package/dist/api/report/queryReports.d.ts.map +1 -0
- package/dist/api/report/queryReports.js +29 -0
- package/dist/api/report/queryReports.js.map +1 -0
- package/dist/api/report/reassignQueue.d.ts +4 -0
- package/dist/api/report/reassignQueue.d.ts.map +1 -0
- package/dist/api/report/reassignQueue.js +45 -0
- package/dist/api/report/reassignQueue.js.map +1 -0
- package/dist/api/report/refreshStats.d.ts +4 -0
- package/dist/api/report/refreshStats.d.ts.map +1 -0
- package/dist/api/report/refreshStats.js +26 -0
- package/dist/api/report/refreshStats.js.map +1 -0
- package/dist/api/report/unassignModerator.d.ts +4 -0
- package/dist/api/report/unassignModerator.d.ts.map +1 -0
- package/dist/api/report/unassignModerator.js +21 -0
- package/dist/api/report/unassignModerator.js.map +1 -0
- package/dist/api/util.d.ts +2 -0
- package/dist/api/util.d.ts.map +1 -1
- package/dist/api/util.js +9 -1
- package/dist/api/util.js.map +1 -1
- package/dist/assignment/index.d.ts +89 -0
- package/dist/assignment/index.d.ts.map +1 -0
- package/dist/assignment/index.js +537 -0
- package/dist/assignment/index.js.map +1 -0
- package/dist/config/config.d.ts +14 -0
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/config.js +9 -0
- package/dist/config/config.js.map +1 -1
- package/dist/config/env.d.ts +3 -0
- package/dist/config/env.d.ts.map +1 -1
- package/dist/config/env.js +3 -0
- package/dist/config/env.js.map +1 -1
- package/dist/context.d.ts +9 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +31 -10
- package/dist/context.js.map +1 -1
- package/dist/daemon/context.d.ts +6 -0
- package/dist/daemon/context.d.ts.map +1 -1
- package/dist/daemon/context.js +28 -4
- package/dist/daemon/context.js.map +1 -1
- package/dist/daemon/job-cursor.d.ts +5 -0
- package/dist/daemon/job-cursor.d.ts.map +1 -0
- package/dist/daemon/job-cursor.js +28 -0
- package/dist/daemon/job-cursor.js.map +1 -0
- package/dist/daemon/queue-router.d.ts +17 -0
- package/dist/daemon/queue-router.d.ts.map +1 -0
- package/dist/daemon/queue-router.js +114 -0
- package/dist/daemon/queue-router.js.map +1 -0
- package/dist/daemon/stats-computer.d.ts +51 -0
- package/dist/daemon/stats-computer.d.ts.map +1 -0
- package/dist/daemon/stats-computer.js +117 -0
- package/dist/daemon/stats-computer.js.map +1 -0
- package/dist/daemon/strike-expiry-processor.d.ts.map +1 -1
- package/dist/daemon/strike-expiry-processor.js +4 -19
- package/dist/daemon/strike-expiry-processor.js.map +1 -1
- package/dist/db/migrations/20260219T164523000Z-create-report-table.d.ts +4 -0
- package/dist/db/migrations/20260219T164523000Z-create-report-table.d.ts.map +1 -0
- package/dist/db/migrations/20260219T164523000Z-create-report-table.js +126 -0
- package/dist/db/migrations/20260219T164523000Z-create-report-table.js.map +1 -0
- package/dist/db/migrations/20260219T165302248Z-moderator-assignment.d.ts +4 -0
- package/dist/db/migrations/20260219T165302248Z-moderator-assignment.d.ts.map +1 -0
- package/dist/db/migrations/20260219T165302248Z-moderator-assignment.js +35 -0
- package/dist/db/migrations/20260219T165302248Z-moderator-assignment.js.map +1 -0
- package/dist/db/migrations/20260225T000000000Z-add-report-queue-table.d.ts +4 -0
- package/dist/db/migrations/20260225T000000000Z-add-report-queue-table.d.ts.map +1 -0
- package/dist/db/migrations/20260225T000000000Z-add-report-queue-table.js +36 -0
- package/dist/db/migrations/20260225T000000000Z-add-report-queue-table.js.map +1 -0
- package/dist/db/migrations/20260313T000000000Z-add-report-activity-table.d.ts +4 -0
- package/dist/db/migrations/20260313T000000000Z-add-report-activity-table.d.ts.map +1 -0
- package/dist/db/migrations/20260313T000000000Z-add-report-activity-table.js +39 -0
- package/dist/db/migrations/20260313T000000000Z-add-report-activity-table.js.map +1 -0
- package/dist/db/migrations/20260318T152058935Z-add-report-stat.d.ts +4 -0
- package/dist/db/migrations/20260318T152058935Z-add-report-stat.d.ts.map +1 -0
- package/dist/db/migrations/20260318T152058935Z-add-report-stat.js +34 -0
- package/dist/db/migrations/20260318T152058935Z-add-report-stat.js.map +1 -0
- package/dist/db/migrations/index.d.ts +5 -0
- package/dist/db/migrations/index.d.ts.map +1 -1
- package/dist/db/migrations/index.js +6 -1
- package/dist/db/migrations/index.js.map +1 -1
- package/dist/db/pagination.d.ts +31 -0
- package/dist/db/pagination.d.ts.map +1 -1
- package/dist/db/pagination.js +74 -1
- package/dist/db/pagination.js.map +1 -1
- package/dist/db/schema/index.d.ts +6 -1
- package/dist/db/schema/index.d.ts.map +1 -1
- package/dist/db/schema/index.js.map +1 -1
- package/dist/db/schema/moderator_assignment.d.ts +14 -0
- package/dist/db/schema/moderator_assignment.d.ts.map +1 -0
- package/dist/db/schema/moderator_assignment.js +5 -0
- package/dist/db/schema/moderator_assignment.js.map +1 -0
- package/dist/db/schema/report.d.ts +25 -0
- package/dist/db/schema/report.d.ts.map +1 -0
- package/dist/db/schema/report.js +5 -0
- package/dist/db/schema/report.js.map +1 -0
- package/dist/db/schema/report_activity.d.ts +18 -0
- package/dist/db/schema/report_activity.d.ts.map +1 -0
- package/dist/db/schema/report_activity.js +5 -0
- package/dist/db/schema/report_activity.js.map +1 -0
- package/dist/db/schema/report_queue.d.ts +19 -0
- package/dist/db/schema/report_queue.d.ts.map +1 -0
- package/dist/db/schema/report_queue.js +5 -0
- package/dist/db/schema/report_queue.js.map +1 -0
- package/dist/db/schema/report_stat.d.ts +20 -0
- package/dist/db/schema/report_stat.d.ts.map +1 -0
- package/dist/db/schema/report_stat.js +5 -0
- package/dist/db/schema/report_stat.js.map +1 -0
- package/dist/lexicon/index.d.ts +50 -0
- package/dist/lexicon/index.d.ts.map +1 -1
- package/dist/lexicon/index.js +120 -2
- package/dist/lexicon/index.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +10535 -7389
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +1789 -122
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/app/bsky/embed/external.d.ts +2 -0
- package/dist/lexicon/types/app/bsky/embed/external.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/embed/external.js.map +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/emitEvent.d.ts +19 -0
- package/dist/lexicon/types/tools/ozone/moderation/emitEvent.d.ts.map +1 -1
- package/dist/lexicon/types/tools/ozone/moderation/emitEvent.js +9 -0
- package/dist/lexicon/types/tools/ozone/moderation/emitEvent.js.map +1 -1
- package/dist/lexicon/types/tools/ozone/queue/assignModerator.d.ts +27 -0
- package/dist/lexicon/types/tools/ozone/queue/assignModerator.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/assignModerator.js +7 -0
- package/dist/lexicon/types/tools/ozone/queue/assignModerator.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/createQueue.d.ts +35 -0
- package/dist/lexicon/types/tools/ozone/queue/createQueue.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/createQueue.js +7 -0
- package/dist/lexicon/types/tools/ozone/queue/createQueue.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/defs.d.ts +62 -0
- package/dist/lexicon/types/tools/ozone/queue/defs.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/defs.js +34 -0
- package/dist/lexicon/types/tools/ozone/queue/defs.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/deleteQueue.d.ts +29 -0
- package/dist/lexicon/types/tools/ozone/queue/deleteQueue.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/deleteQueue.js +7 -0
- package/dist/lexicon/types/tools/ozone/queue/deleteQueue.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/getAssignments.d.ts +30 -0
- package/dist/lexicon/types/tools/ozone/queue/getAssignments.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/getAssignments.js +7 -0
- package/dist/lexicon/types/tools/ozone/queue/getAssignments.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/listQueues.d.ts +32 -0
- package/dist/lexicon/types/tools/ozone/queue/listQueues.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/listQueues.js +7 -0
- package/dist/lexicon/types/tools/ozone/queue/listQueues.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/routeReports.d.ts +31 -0
- package/dist/lexicon/types/tools/ozone/queue/routeReports.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/routeReports.js +7 -0
- package/dist/lexicon/types/tools/ozone/queue/routeReports.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/unassignModerator.d.ts +18 -0
- package/dist/lexicon/types/tools/ozone/queue/unassignModerator.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/unassignModerator.js +7 -0
- package/dist/lexicon/types/tools/ozone/queue/unassignModerator.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/updateQueue.d.ts +32 -0
- package/dist/lexicon/types/tools/ozone/queue/updateQueue.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/queue/updateQueue.js +7 -0
- package/dist/lexicon/types/tools/ozone/queue/updateQueue.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/assignModerator.d.ts +31 -0
- package/dist/lexicon/types/tools/ozone/report/assignModerator.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/assignModerator.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/assignModerator.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/createActivity.d.ts +37 -0
- package/dist/lexicon/types/tools/ozone/report/createActivity.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/createActivity.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/createActivity.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/defs.d.ts +185 -0
- package/dist/lexicon/types/tools/ozone/report/defs.d.ts.map +1 -1
- package/dist/lexicon/types/tools/ozone/report/defs.js +108 -0
- package/dist/lexicon/types/tools/ozone/report/defs.js.map +1 -1
- package/dist/lexicon/types/tools/ozone/report/getAssignments.d.ts +30 -0
- package/dist/lexicon/types/tools/ozone/report/getAssignments.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/getAssignments.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/getAssignments.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/getHistoricalStats.d.ts +36 -0
- package/dist/lexicon/types/tools/ozone/report/getHistoricalStats.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/getHistoricalStats.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/getHistoricalStats.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/getLatestReport.d.ts +21 -0
- package/dist/lexicon/types/tools/ozone/report/getLatestReport.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/getLatestReport.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/getLatestReport.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/getLiveStats.d.ts +27 -0
- package/dist/lexicon/types/tools/ozone/report/getLiveStats.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/getLiveStats.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/getLiveStats.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/getReport.d.ts +22 -0
- package/dist/lexicon/types/tools/ozone/report/getReport.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/getReport.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/getReport.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/listActivities.d.ts +26 -0
- package/dist/lexicon/types/tools/ozone/report/listActivities.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/listActivities.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/listActivities.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/queryReports.d.ts +48 -0
- package/dist/lexicon/types/tools/ozone/report/queryReports.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/queryReports.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/queryReports.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/reassignQueue.d.ts +31 -0
- package/dist/lexicon/types/tools/ozone/report/reassignQueue.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/reassignQueue.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/reassignQueue.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/refreshStats.d.ts +28 -0
- package/dist/lexicon/types/tools/ozone/report/refreshStats.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/refreshStats.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/refreshStats.js.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/unassignModerator.d.ts +25 -0
- package/dist/lexicon/types/tools/ozone/report/unassignModerator.d.ts.map +1 -0
- package/dist/lexicon/types/tools/ozone/report/unassignModerator.js +7 -0
- package/dist/lexicon/types/tools/ozone/report/unassignModerator.js.map +1 -0
- package/dist/mod-service/index.d.ts +3 -1
- package/dist/mod-service/index.d.ts.map +1 -1
- package/dist/mod-service/index.js +39 -2
- package/dist/mod-service/index.js.map +1 -1
- package/dist/mod-service/report.d.ts +64 -0
- package/dist/mod-service/report.d.ts.map +1 -0
- package/dist/mod-service/report.js +282 -0
- package/dist/mod-service/report.js.map +1 -0
- package/dist/mod-service/status.d.ts +20 -0
- package/dist/mod-service/status.d.ts.map +1 -1
- package/dist/queue/service.d.ts +86 -0
- package/dist/queue/service.d.ts.map +1 -0
- package/dist/queue/service.js +430 -0
- package/dist/queue/service.js.map +1 -0
- package/dist/report/activity.d.ts +77 -0
- package/dist/report/activity.d.ts.map +1 -0
- package/dist/report/activity.js +141 -0
- package/dist/report/activity.js.map +1 -0
- package/dist/report/handle-report-update.d.ts +47 -0
- package/dist/report/handle-report-update.d.ts.map +1 -0
- package/dist/report/handle-report-update.js +178 -0
- package/dist/report/handle-report-update.js.map +1 -0
- package/dist/report/reassign.d.ts +10 -0
- package/dist/report/reassign.d.ts.map +1 -0
- package/dist/report/reassign.js +75 -0
- package/dist/report/reassign.js.map +1 -0
- package/dist/report/stats.d.ts +105 -0
- package/dist/report/stats.d.ts.map +1 -0
- package/dist/report/stats.js +619 -0
- package/dist/report/stats.js.map +1 -0
- package/dist/report/views.d.ts +111 -0
- package/dist/report/views.d.ts.map +1 -0
- package/dist/report/views.js +156 -0
- package/dist/report/views.js.map +1 -0
- package/dist/team/index.d.ts +1 -0
- package/dist/team/index.d.ts.map +1 -1
- package/dist/team/index.js +11 -0
- package/dist/team/index.js.map +1 -1
- package/package.json +3 -3
- package/src/api/index.ts +40 -0
- package/src/api/moderation/emitEvent.ts +38 -0
- package/src/api/queue/assignModerator.ts +31 -0
- package/src/api/queue/createQueue.ts +62 -0
- package/src/api/queue/deleteQueue.ts +56 -0
- package/src/api/queue/getAssignments.ts +19 -0
- package/src/api/queue/listQueues.ts +39 -0
- package/src/api/queue/routeReports.ts +44 -0
- package/src/api/queue/unassignModerator.ts +26 -0
- package/src/api/queue/updateQueue.ts +54 -0
- package/src/api/report/assignModerator.ts +36 -0
- package/src/api/report/createActivity.ts +57 -0
- package/src/api/report/getAssignments.ts +20 -0
- package/src/api/report/getHistoricalStats.ts +41 -0
- package/src/api/report/getLatestReport.ts +44 -0
- package/src/api/report/getLiveStats.ts +26 -0
- package/src/api/report/getReport.ts +55 -0
- package/src/api/report/listActivities.ts +34 -0
- package/src/api/report/queryReports.ts +44 -0
- package/src/api/report/reassignQueue.ts +68 -0
- package/src/api/report/refreshStats.ts +27 -0
- package/src/api/report/unassignModerator.ts +21 -0
- package/src/api/util.ts +12 -0
- package/src/assignment/index.ts +731 -0
- package/src/config/config.ts +27 -0
- package/src/config/env.ts +8 -0
- package/src/context.ts +31 -0
- package/src/daemon/context.ts +34 -0
- package/src/daemon/job-cursor.ts +33 -0
- package/src/daemon/queue-router.ts +101 -0
- package/src/daemon/stats-computer.ts +101 -0
- package/src/daemon/strike-expiry-processor.ts +4 -20
- package/src/db/migrations/20260219T164523000Z-create-report-table.ts +155 -0
- package/src/db/migrations/20260219T165302248Z-moderator-assignment.ts +42 -0
- package/src/db/migrations/20260225T000000000Z-add-report-queue-table.ts +41 -0
- package/src/db/migrations/20260313T000000000Z-add-report-activity-table.ts +48 -0
- package/src/db/migrations/20260318T152058935Z-add-report-stat.ts +35 -0
- package/src/db/migrations/index.ts +5 -0
- package/src/db/pagination.ts +85 -0
- package/src/db/schema/index.ts +10 -0
- package/src/db/schema/moderator_assignment.ts +16 -0
- package/src/db/schema/report.ts +27 -0
- package/src/db/schema/report_activity.ts +22 -0
- package/src/db/schema/report_queue.ts +21 -0
- package/src/db/schema/report_stat.ts +27 -0
- package/src/lexicon/index.ts +280 -0
- package/src/lexicon/lexicons.ts +1910 -160
- package/src/lexicon/types/app/bsky/embed/external.ts +2 -0
- package/src/lexicon/types/tools/ozone/moderation/emitEvent.ts +24 -0
- package/src/lexicon/types/tools/ozone/queue/assignModerator.ts +46 -0
- package/src/lexicon/types/tools/ozone/queue/createQueue.ts +54 -0
- package/src/lexicon/types/tools/ozone/queue/defs.ts +99 -0
- package/src/lexicon/types/tools/ozone/queue/deleteQueue.ts +48 -0
- package/src/lexicon/types/tools/ozone/queue/getAssignments.ts +48 -0
- package/src/lexicon/types/tools/ozone/queue/listQueues.ts +50 -0
- package/src/lexicon/types/tools/ozone/queue/routeReports.ts +50 -0
- package/src/lexicon/types/tools/ozone/queue/unassignModerator.ts +37 -0
- package/src/lexicon/types/tools/ozone/queue/updateQueue.ts +51 -0
- package/src/lexicon/types/tools/ozone/report/assignModerator.ts +50 -0
- package/src/lexicon/types/tools/ozone/report/createActivity.ts +60 -0
- package/src/lexicon/types/tools/ozone/report/defs.ts +327 -0
- package/src/lexicon/types/tools/ozone/report/getAssignments.ts +48 -0
- package/src/lexicon/types/tools/ozone/report/getHistoricalStats.ts +54 -0
- package/src/lexicon/types/tools/ozone/report/getLatestReport.ts +39 -0
- package/src/lexicon/types/tools/ozone/report/getLiveStats.ts +45 -0
- package/src/lexicon/types/tools/ozone/report/getReport.ts +38 -0
- package/src/lexicon/types/tools/ozone/report/listActivities.ts +44 -0
- package/src/lexicon/types/tools/ozone/report/queryReports.ts +72 -0
- package/src/lexicon/types/tools/ozone/report/reassignQueue.ts +55 -0
- package/src/lexicon/types/tools/ozone/report/refreshStats.ts +46 -0
- package/src/lexicon/types/tools/ozone/report/unassignModerator.ts +44 -0
- package/src/mod-service/index.ts +45 -3
- package/src/mod-service/report.ts +408 -0
- package/src/queue/service.ts +599 -0
- package/src/report/activity.ts +234 -0
- package/src/report/handle-report-update.ts +209 -0
- package/src/report/reassign.ts +109 -0
- package/src/report/stats.ts +850 -0
- package/src/report/views.ts +241 -0
- package/src/team/index.ts +11 -0
- package/tests/get-report.test.ts +136 -0
- package/tests/query-reports.test.ts +608 -0
- package/tests/queue-assignment.test.ts +428 -0
- package/tests/queue-router.test.ts +306 -0
- package/tests/queues.test.ts +690 -0
- package/tests/report-action.test.ts +308 -0
- package/tests/report-activity.test.ts +567 -0
- package/tests/report-assignment.test.ts +517 -0
- package/tests/report-reassign-queue.test.ts +340 -0
- package/tests/report-routing.test.ts +245 -0
- package/tests/report-stats.test.ts +545 -0
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createReportActivity = createReportActivity;
|
|
4
|
+
exports.bulkInsertReportActivities = bulkInsertReportActivities;
|
|
5
|
+
exports.listReportActivities = listReportActivities;
|
|
6
|
+
exports.formatActivityView = formatActivityView;
|
|
7
|
+
const xrpc_server_1 = require("@atproto/xrpc-server");
|
|
8
|
+
const handle_report_update_1 = require("./handle-report-update");
|
|
9
|
+
async function createReportActivity(db, params) {
|
|
10
|
+
const { reportId, activityType, internalNote, publicNote, meta, isAutomated = false, createdBy, } = params;
|
|
11
|
+
return db.transaction(async (dbTxn) => {
|
|
12
|
+
// Lock the report row for the duration of the transaction to prevent
|
|
13
|
+
// concurrent writes from racing on status validation + update.
|
|
14
|
+
const report = await dbTxn.db
|
|
15
|
+
.selectFrom('report')
|
|
16
|
+
.select(['id', 'status'])
|
|
17
|
+
.where('id', '=', reportId)
|
|
18
|
+
.forUpdate()
|
|
19
|
+
.executeTakeFirst();
|
|
20
|
+
if (!report) {
|
|
21
|
+
throw new xrpc_server_1.InvalidRequestError(`Report ${reportId} not found`, 'ReportNotFound');
|
|
22
|
+
}
|
|
23
|
+
let result;
|
|
24
|
+
try {
|
|
25
|
+
result = (0, handle_report_update_1.handleReportUpdate)(report.status, {
|
|
26
|
+
type: 'activity',
|
|
27
|
+
activityType,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
if (err instanceof handle_report_update_1.AlreadyInTargetState) {
|
|
32
|
+
throw new xrpc_server_1.InvalidRequestError(err.message, 'AlreadyInTargetState');
|
|
33
|
+
}
|
|
34
|
+
if (err instanceof handle_report_update_1.InvalidStateTransition) {
|
|
35
|
+
throw new xrpc_server_1.InvalidRequestError(err.message, 'InvalidStateTransition');
|
|
36
|
+
}
|
|
37
|
+
throw err;
|
|
38
|
+
}
|
|
39
|
+
const now = new Date().toISOString();
|
|
40
|
+
if (result.nextStatus !== null) {
|
|
41
|
+
const updateSet = {
|
|
42
|
+
status: result.nextStatus,
|
|
43
|
+
updatedAt: now,
|
|
44
|
+
};
|
|
45
|
+
if (result.nextStatus === 'closed') {
|
|
46
|
+
updateSet.closedAt = now;
|
|
47
|
+
}
|
|
48
|
+
else if (result.nextStatus === 'open') {
|
|
49
|
+
updateSet.closedAt = null;
|
|
50
|
+
}
|
|
51
|
+
await dbTxn.db
|
|
52
|
+
.updateTable('report')
|
|
53
|
+
.set(updateSet)
|
|
54
|
+
.where('id', '=', reportId)
|
|
55
|
+
.execute();
|
|
56
|
+
}
|
|
57
|
+
const [activity] = await dbTxn.db
|
|
58
|
+
.insertInto('report_activity')
|
|
59
|
+
.values({
|
|
60
|
+
reportId,
|
|
61
|
+
activityType,
|
|
62
|
+
previousStatus: result.activity?.previousStatus ?? null,
|
|
63
|
+
internalNote: internalNote ?? null,
|
|
64
|
+
publicNote: publicNote ?? null,
|
|
65
|
+
meta: meta ?? null,
|
|
66
|
+
isAutomated,
|
|
67
|
+
createdBy,
|
|
68
|
+
createdAt: now,
|
|
69
|
+
})
|
|
70
|
+
.returningAll()
|
|
71
|
+
.execute();
|
|
72
|
+
return activity;
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Insert multiple activity rows in a single query. No validation — caller is
|
|
77
|
+
* responsible for correctness and for being inside an appropriate transaction.
|
|
78
|
+
*/
|
|
79
|
+
async function bulkInsertReportActivities(db, activities) {
|
|
80
|
+
if (!activities.length)
|
|
81
|
+
return;
|
|
82
|
+
await db.db
|
|
83
|
+
.insertInto('report_activity')
|
|
84
|
+
.values(activities.map((a) => ({
|
|
85
|
+
reportId: a.reportId,
|
|
86
|
+
activityType: a.activityType,
|
|
87
|
+
previousStatus: a.previousStatus,
|
|
88
|
+
internalNote: a.internalNote ?? null,
|
|
89
|
+
publicNote: a.publicNote ?? null,
|
|
90
|
+
meta: a.meta ?? null,
|
|
91
|
+
isAutomated: a.isAutomated,
|
|
92
|
+
createdBy: a.createdBy,
|
|
93
|
+
createdAt: a.createdAt,
|
|
94
|
+
})))
|
|
95
|
+
.execute();
|
|
96
|
+
}
|
|
97
|
+
async function listReportActivities(db, params) {
|
|
98
|
+
const { reportId, limit = 50, cursor } = params;
|
|
99
|
+
let builder = db.db
|
|
100
|
+
.selectFrom('report_activity')
|
|
101
|
+
.selectAll()
|
|
102
|
+
.where('reportId', '=', reportId)
|
|
103
|
+
.orderBy('createdAt', 'desc')
|
|
104
|
+
.orderBy('id', 'desc')
|
|
105
|
+
.limit(limit + 1);
|
|
106
|
+
if (cursor) {
|
|
107
|
+
const cursorId = parseInt(cursor, 10);
|
|
108
|
+
if (!isNaN(cursorId)) {
|
|
109
|
+
builder = builder.where('id', '<', cursorId);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const rows = await builder.execute();
|
|
113
|
+
const hasMore = rows.length > limit;
|
|
114
|
+
const activities = hasMore ? rows.slice(0, limit) : rows;
|
|
115
|
+
const nextCursor = hasMore && activities.length > 0
|
|
116
|
+
? String(activities[activities.length - 1].id)
|
|
117
|
+
: undefined;
|
|
118
|
+
return { activities, cursor: nextCursor };
|
|
119
|
+
}
|
|
120
|
+
function buildActivityObject(activityType, previousStatus) {
|
|
121
|
+
const $type = `tools.ozone.report.defs#${activityType}`;
|
|
122
|
+
if (previousStatus !== null) {
|
|
123
|
+
return { $type, previousStatus };
|
|
124
|
+
}
|
|
125
|
+
return { $type };
|
|
126
|
+
}
|
|
127
|
+
function formatActivityView(activity, memberViews) {
|
|
128
|
+
return {
|
|
129
|
+
id: activity.id,
|
|
130
|
+
reportId: activity.reportId,
|
|
131
|
+
activity: buildActivityObject(activity.activityType, activity.previousStatus),
|
|
132
|
+
internalNote: activity.internalNote ?? undefined,
|
|
133
|
+
publicNote: activity.publicNote ?? undefined,
|
|
134
|
+
meta: activity.meta ?? undefined,
|
|
135
|
+
isAutomated: activity.isAutomated,
|
|
136
|
+
createdBy: activity.createdBy,
|
|
137
|
+
moderator: memberViews?.get(activity.createdBy),
|
|
138
|
+
createdAt: activity.createdAt,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=activity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity.js","sourceRoot":"","sources":["../../src/report/activity.ts"],"names":[],"mappings":"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","sourcesContent":["import { InvalidRequestError } from '@atproto/xrpc-server'\nimport { Database } from '../db'\nimport { Member } from '../lexicon/types/tools/ozone/team/defs'\nimport {\n AlreadyInTargetState,\n InvalidStateTransition,\n handleReportUpdate,\n} from './handle-report-update'\n\nexport type ActivityType =\n | 'queueActivity'\n | 'assignmentActivity'\n | 'escalationActivity'\n | 'closeActivity'\n | 'reopenActivity'\n | 'noteActivity'\n\nexport type CreateActivityParams = {\n reportId: number\n activityType: ActivityType\n internalNote?: string\n publicNote?: string\n meta?: Record<string, unknown>\n /** Set true for activities created by automated processes (e.g. queue router). */\n isAutomated?: boolean\n createdBy: string\n}\n\nexport async function createReportActivity(\n db: Database,\n params: CreateActivityParams,\n) {\n const {\n reportId,\n activityType,\n internalNote,\n publicNote,\n meta,\n isAutomated = false,\n createdBy,\n } = params\n\n return db.transaction(async (dbTxn) => {\n // Lock the report row for the duration of the transaction to prevent\n // concurrent writes from racing on status validation + update.\n const report = await dbTxn.db\n .selectFrom('report')\n .select(['id', 'status'])\n .where('id', '=', reportId)\n .forUpdate()\n .executeTakeFirst()\n\n if (!report) {\n throw new InvalidRequestError(\n `Report ${reportId} not found`,\n 'ReportNotFound',\n )\n }\n\n let result\n try {\n result = handleReportUpdate(report.status, {\n type: 'activity',\n activityType,\n })\n } catch (err) {\n if (err instanceof AlreadyInTargetState) {\n throw new InvalidRequestError(err.message, 'AlreadyInTargetState')\n }\n if (err instanceof InvalidStateTransition) {\n throw new InvalidRequestError(err.message, 'InvalidStateTransition')\n }\n throw err\n }\n\n const now = new Date().toISOString()\n\n if (result.nextStatus !== null) {\n const updateSet: Record<string, string | null> = {\n status: result.nextStatus,\n updatedAt: now,\n }\n if (result.nextStatus === 'closed') {\n updateSet.closedAt = now\n } else if (result.nextStatus === 'open') {\n updateSet.closedAt = null\n }\n await dbTxn.db\n .updateTable('report')\n .set(updateSet)\n .where('id', '=', reportId)\n .execute()\n }\n\n const [activity] = await dbTxn.db\n .insertInto('report_activity')\n .values({\n reportId,\n activityType,\n previousStatus: result.activity?.previousStatus ?? null,\n internalNote: internalNote ?? null,\n publicNote: publicNote ?? null,\n meta: meta ?? null,\n isAutomated,\n createdBy,\n createdAt: now,\n })\n .returningAll()\n .execute()\n\n return activity\n })\n}\n\nexport type BulkActivityInsert = {\n reportId: number\n activityType: string\n previousStatus: string | null\n internalNote?: string\n publicNote?: string\n meta?: unknown\n isAutomated: boolean\n createdBy: string\n createdAt: string\n}\n\n/**\n * Insert multiple activity rows in a single query. No validation — caller is\n * responsible for correctness and for being inside an appropriate transaction.\n */\nexport async function bulkInsertReportActivities(\n db: Database,\n activities: BulkActivityInsert[],\n) {\n if (!activities.length) return\n await db.db\n .insertInto('report_activity')\n .values(\n activities.map((a) => ({\n reportId: a.reportId,\n activityType: a.activityType,\n previousStatus: a.previousStatus,\n internalNote: a.internalNote ?? null,\n publicNote: a.publicNote ?? null,\n meta: a.meta ?? null,\n isAutomated: a.isAutomated,\n createdBy: a.createdBy,\n createdAt: a.createdAt,\n })),\n )\n .execute()\n}\n\nexport type ListActivitiesParams = {\n reportId: number\n limit?: number\n cursor?: string\n}\n\nexport async function listReportActivities(\n db: Database,\n params: ListActivitiesParams,\n) {\n const { reportId, limit = 50, cursor } = params\n\n let builder = db.db\n .selectFrom('report_activity')\n .selectAll()\n .where('reportId', '=', reportId)\n .orderBy('createdAt', 'desc')\n .orderBy('id', 'desc')\n .limit(limit + 1)\n\n if (cursor) {\n const cursorId = parseInt(cursor, 10)\n if (!isNaN(cursorId)) {\n builder = builder.where('id', '<', cursorId)\n }\n }\n\n const rows = await builder.execute()\n const hasMore = rows.length > limit\n const activities = hasMore ? rows.slice(0, limit) : rows\n\n const nextCursor =\n hasMore && activities.length > 0\n ? String(activities[activities.length - 1].id)\n : undefined\n\n return { activities, cursor: nextCursor }\n}\n\nfunction buildActivityObject(\n activityType: string,\n previousStatus: string | null,\n): { $type: string; [k: string]: unknown } {\n const $type = `tools.ozone.report.defs#${activityType}`\n if (previousStatus !== null) {\n return { $type, previousStatus }\n }\n return { $type }\n}\n\nexport function formatActivityView(\n activity: {\n id: number\n reportId: number\n activityType: string\n previousStatus: string | null\n internalNote: string | null\n publicNote: string | null\n meta: unknown\n isAutomated: boolean\n createdBy: string\n createdAt: string\n },\n memberViews?: Map<string, Member>,\n) {\n return {\n id: activity.id,\n reportId: activity.reportId,\n activity: buildActivityObject(\n activity.activityType,\n activity.previousStatus,\n ),\n internalNote: activity.internalNote ?? undefined,\n publicNote: activity.publicNote ?? undefined,\n meta: (activity.meta as Record<string, unknown>) ?? undefined,\n isAutomated: activity.isAutomated,\n createdBy: activity.createdBy,\n moderator: memberViews?.get(activity.createdBy),\n createdAt: activity.createdAt,\n }\n}\n"]}
|
|
@@ -0,0 +1,47 @@
|
|
|
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
|
+
export declare class AlreadyInTargetState extends Error {
|
|
11
|
+
currentStatus: string;
|
|
12
|
+
targetStatus: string;
|
|
13
|
+
constructor(currentStatus: string, targetStatus: string);
|
|
14
|
+
}
|
|
15
|
+
export declare class InvalidStateTransition extends Error {
|
|
16
|
+
fromStatus: string;
|
|
17
|
+
toStatus: string;
|
|
18
|
+
constructor(fromStatus: string, toStatus: string);
|
|
19
|
+
}
|
|
20
|
+
/** Valid state transitions: key = fromState, value = allowed toStates */
|
|
21
|
+
export declare const VALID_TRANSITIONS: Record<string, string[]>;
|
|
22
|
+
export type ReportUpdateAction = {
|
|
23
|
+
type: 'activity';
|
|
24
|
+
activityType: string;
|
|
25
|
+
} | {
|
|
26
|
+
type: 'event';
|
|
27
|
+
eventType: string;
|
|
28
|
+
} | {
|
|
29
|
+
type: 'queue';
|
|
30
|
+
};
|
|
31
|
+
export type ActivityRecord = {
|
|
32
|
+
activityType: string;
|
|
33
|
+
previousStatus: string;
|
|
34
|
+
};
|
|
35
|
+
export type ReportUpdateResult = {
|
|
36
|
+
nextStatus: string | null;
|
|
37
|
+
activity: ActivityRecord | null;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Determines the next status and activity record for a report update.
|
|
41
|
+
*
|
|
42
|
+
* @throws AlreadyInTargetState if the report is already in the target status
|
|
43
|
+
* @throws InvalidStateTransition if the transition is not allowed
|
|
44
|
+
* @returns nextStatus (null = no change) and activity (null = nothing to record)
|
|
45
|
+
*/
|
|
46
|
+
export declare function handleReportUpdate(currentStatus: string, action: ReportUpdateAction): ReportUpdateResult;
|
|
47
|
+
//# sourceMappingURL=handle-report-update.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handle-report-update.d.ts","sourceRoot":"","sources":["../../src/report/handle-report-update.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,qBAAa,oBAAqB,SAAQ,KAAK;IAEpC,aAAa,EAAE,MAAM;IACrB,YAAY,EAAE,MAAM;gBADpB,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE,MAAM;CAK9B;AAED,qBAAa,sBAAuB,SAAQ,KAAK;IAEtC,UAAU,EAAE,MAAM;IAClB,QAAQ,EAAE,MAAM;gBADhB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM;CAK1B;AAMD,yEAAyE;AACzE,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAMtD,CAAA;AA6CD,MAAM,MAAM,kBAAkB,GAC1B;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAA;AAMrB,MAAM,MAAM,cAAc,GAAG;IAC3B,YAAY,EAAE,MAAM,CAAA;IACpB,cAAc,EAAE,MAAM,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAA;CAChC,CAAA;AAMD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,kBAAkB,GACzB,kBAAkB,CASpB"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Pure, synchronous state-transition logic for reports.
|
|
4
|
+
*
|
|
5
|
+
* Every code path that changes a report's status or creates a report activity
|
|
6
|
+
* should call `handleReportUpdate` to determine the next status and the
|
|
7
|
+
* activity record to insert. This keeps the state machine in one place and
|
|
8
|
+
* decouples it from DB operations so it works for both single-row transactions
|
|
9
|
+
* and bulk updates.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.VALID_TRANSITIONS = exports.InvalidStateTransition = exports.AlreadyInTargetState = void 0;
|
|
13
|
+
exports.handleReportUpdate = handleReportUpdate;
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Error types — callers decide how to surface these (throw, skip, etc.)
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
class AlreadyInTargetState extends Error {
|
|
18
|
+
constructor(currentStatus, targetStatus) {
|
|
19
|
+
super(`Report is already in '${targetStatus}' status`);
|
|
20
|
+
Object.defineProperty(this, "currentStatus", {
|
|
21
|
+
enumerable: true,
|
|
22
|
+
configurable: true,
|
|
23
|
+
writable: true,
|
|
24
|
+
value: currentStatus
|
|
25
|
+
});
|
|
26
|
+
Object.defineProperty(this, "targetStatus", {
|
|
27
|
+
enumerable: true,
|
|
28
|
+
configurable: true,
|
|
29
|
+
writable: true,
|
|
30
|
+
value: targetStatus
|
|
31
|
+
});
|
|
32
|
+
this.name = 'AlreadyInTargetState';
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.AlreadyInTargetState = AlreadyInTargetState;
|
|
36
|
+
class InvalidStateTransition extends Error {
|
|
37
|
+
constructor(fromStatus, toStatus) {
|
|
38
|
+
super(`Cannot transition report from '${fromStatus}' to '${toStatus}'`);
|
|
39
|
+
Object.defineProperty(this, "fromStatus", {
|
|
40
|
+
enumerable: true,
|
|
41
|
+
configurable: true,
|
|
42
|
+
writable: true,
|
|
43
|
+
value: fromStatus
|
|
44
|
+
});
|
|
45
|
+
Object.defineProperty(this, "toStatus", {
|
|
46
|
+
enumerable: true,
|
|
47
|
+
configurable: true,
|
|
48
|
+
writable: true,
|
|
49
|
+
value: toStatus
|
|
50
|
+
});
|
|
51
|
+
this.name = 'InvalidStateTransition';
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.InvalidStateTransition = InvalidStateTransition;
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
// State machine tables
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
/** Valid state transitions: key = fromState, value = allowed toStates */
|
|
59
|
+
exports.VALID_TRANSITIONS = {
|
|
60
|
+
open: ['closed', 'escalated', 'queued', 'assigned'],
|
|
61
|
+
closed: ['open'],
|
|
62
|
+
escalated: ['open', 'closed'],
|
|
63
|
+
queued: ['assigned', 'open'],
|
|
64
|
+
assigned: ['open', 'closed', 'escalated', 'queued'],
|
|
65
|
+
};
|
|
66
|
+
/** Activity types that map to a specific target status */
|
|
67
|
+
const ACTIVITY_TO_STATE = {
|
|
68
|
+
queueActivity: 'queued',
|
|
69
|
+
assignmentActivity: 'assigned',
|
|
70
|
+
escalationActivity: 'escalated',
|
|
71
|
+
closeActivity: 'closed',
|
|
72
|
+
reopenActivity: 'open',
|
|
73
|
+
};
|
|
74
|
+
/** Activity types that are only valid from specific source states */
|
|
75
|
+
const ACTIVITY_VALID_FROM_STATES = {
|
|
76
|
+
reopenActivity: ['closed'],
|
|
77
|
+
};
|
|
78
|
+
/** Moderation event types → target status (+ activity type) */
|
|
79
|
+
const EVENT_TYPE_MAP = {
|
|
80
|
+
'tools.ozone.moderation.defs#modEventAcknowledge': {
|
|
81
|
+
status: 'closed',
|
|
82
|
+
activityType: 'closeActivity',
|
|
83
|
+
},
|
|
84
|
+
'tools.ozone.moderation.defs#modEventTakedown': {
|
|
85
|
+
status: 'closed',
|
|
86
|
+
activityType: 'closeActivity',
|
|
87
|
+
},
|
|
88
|
+
'tools.ozone.moderation.defs#modEventLabel': {
|
|
89
|
+
status: 'closed',
|
|
90
|
+
activityType: 'closeActivity',
|
|
91
|
+
},
|
|
92
|
+
'tools.ozone.moderation.defs#modEventComment': {
|
|
93
|
+
status: 'closed',
|
|
94
|
+
activityType: 'closeActivity',
|
|
95
|
+
},
|
|
96
|
+
'tools.ozone.moderation.defs#modEventEscalate': {
|
|
97
|
+
status: 'escalated',
|
|
98
|
+
activityType: 'escalationActivity',
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
// ---------------------------------------------------------------------------
|
|
102
|
+
// Core function
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
/**
|
|
105
|
+
* Determines the next status and activity record for a report update.
|
|
106
|
+
*
|
|
107
|
+
* @throws AlreadyInTargetState if the report is already in the target status
|
|
108
|
+
* @throws InvalidStateTransition if the transition is not allowed
|
|
109
|
+
* @returns nextStatus (null = no change) and activity (null = nothing to record)
|
|
110
|
+
*/
|
|
111
|
+
function handleReportUpdate(currentStatus, action) {
|
|
112
|
+
switch (action.type) {
|
|
113
|
+
case 'activity':
|
|
114
|
+
return handleActivityAction(currentStatus, action.activityType);
|
|
115
|
+
case 'event':
|
|
116
|
+
return handleEventAction(currentStatus, action.eventType);
|
|
117
|
+
case 'queue':
|
|
118
|
+
return handleQueueAction(currentStatus);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
// Action handlers
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
function handleActivityAction(currentStatus, activityType) {
|
|
125
|
+
const toState = ACTIVITY_TO_STATE[activityType] ?? null;
|
|
126
|
+
// Note-type activities — no state change, but still produce an activity record
|
|
127
|
+
if (toState === null) {
|
|
128
|
+
return { nextStatus: null, activity: null };
|
|
129
|
+
}
|
|
130
|
+
// Check activity-specific source-state constraints
|
|
131
|
+
const validFromStates = ACTIVITY_VALID_FROM_STATES[activityType];
|
|
132
|
+
if (validFromStates && !validFromStates.includes(currentStatus)) {
|
|
133
|
+
throw new InvalidStateTransition(currentStatus, toState);
|
|
134
|
+
}
|
|
135
|
+
validateTransition(currentStatus, toState);
|
|
136
|
+
return {
|
|
137
|
+
nextStatus: toState,
|
|
138
|
+
activity: { activityType, previousStatus: currentStatus },
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function handleEventAction(currentStatus, eventType) {
|
|
142
|
+
const mapping = EVENT_TYPE_MAP[eventType];
|
|
143
|
+
if (!mapping) {
|
|
144
|
+
// Event type doesn't affect report status
|
|
145
|
+
return { nextStatus: null, activity: null };
|
|
146
|
+
}
|
|
147
|
+
validateTransition(currentStatus, mapping.status);
|
|
148
|
+
return {
|
|
149
|
+
nextStatus: mapping.status,
|
|
150
|
+
activity: {
|
|
151
|
+
activityType: mapping.activityType,
|
|
152
|
+
previousStatus: currentStatus,
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function handleQueueAction(currentStatus) {
|
|
157
|
+
// Queue routing only transitions open → queued
|
|
158
|
+
if (currentStatus !== 'open') {
|
|
159
|
+
return { nextStatus: null, activity: null };
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
nextStatus: 'queued',
|
|
163
|
+
activity: { activityType: 'queueActivity', previousStatus: currentStatus },
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
167
|
+
// Shared validation
|
|
168
|
+
// ---------------------------------------------------------------------------
|
|
169
|
+
function validateTransition(fromStatus, toStatus) {
|
|
170
|
+
if (fromStatus === toStatus) {
|
|
171
|
+
throw new AlreadyInTargetState(fromStatus, toStatus);
|
|
172
|
+
}
|
|
173
|
+
const allowed = exports.VALID_TRANSITIONS[fromStatus] ?? [];
|
|
174
|
+
if (!allowed.includes(toStatus)) {
|
|
175
|
+
throw new InvalidStateTransition(fromStatus, toStatus);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=handle-report-update.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handle-report-update.js","sourceRoot":"","sources":["../../src/report/handle-report-update.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Pure, synchronous state-transition logic for reports.\n *\n * Every code path that changes a report's status or creates a report activity\n * should call `handleReportUpdate` to determine the next status and the\n * activity record to insert. This keeps the state machine in one place and\n * decouples it from DB operations so it works for both single-row transactions\n * and bulk updates.\n */\n\n// ---------------------------------------------------------------------------\n// Error types — callers decide how to surface these (throw, skip, etc.)\n// ---------------------------------------------------------------------------\n\nexport class AlreadyInTargetState extends Error {\n constructor(\n public currentStatus: string,\n public targetStatus: string,\n ) {\n super(`Report is already in '${targetStatus}' status`)\n this.name = 'AlreadyInTargetState'\n }\n}\n\nexport class InvalidStateTransition extends Error {\n constructor(\n public fromStatus: string,\n public toStatus: string,\n ) {\n super(`Cannot transition report from '${fromStatus}' to '${toStatus}'`)\n this.name = 'InvalidStateTransition'\n }\n}\n\n// ---------------------------------------------------------------------------\n// State machine tables\n// ---------------------------------------------------------------------------\n\n/** Valid state transitions: key = fromState, value = allowed toStates */\nexport const VALID_TRANSITIONS: Record<string, string[]> = {\n open: ['closed', 'escalated', 'queued', 'assigned'],\n closed: ['open'],\n escalated: ['open', 'closed'],\n queued: ['assigned', 'open'],\n assigned: ['open', 'closed', 'escalated', 'queued'],\n}\n\n/** Activity types that map to a specific target status */\nconst ACTIVITY_TO_STATE: Record<string, string> = {\n queueActivity: 'queued',\n assignmentActivity: 'assigned',\n escalationActivity: 'escalated',\n closeActivity: 'closed',\n reopenActivity: 'open',\n}\n\n/** Activity types that are only valid from specific source states */\nconst ACTIVITY_VALID_FROM_STATES: Record<string, string[]> = {\n reopenActivity: ['closed'],\n}\n\n/** Moderation event types → target status (+ activity type) */\nconst EVENT_TYPE_MAP: Record<string, { status: string; activityType: string }> =\n {\n 'tools.ozone.moderation.defs#modEventAcknowledge': {\n status: 'closed',\n activityType: 'closeActivity',\n },\n 'tools.ozone.moderation.defs#modEventTakedown': {\n status: 'closed',\n activityType: 'closeActivity',\n },\n 'tools.ozone.moderation.defs#modEventLabel': {\n status: 'closed',\n activityType: 'closeActivity',\n },\n 'tools.ozone.moderation.defs#modEventComment': {\n status: 'closed',\n activityType: 'closeActivity',\n },\n 'tools.ozone.moderation.defs#modEventEscalate': {\n status: 'escalated',\n activityType: 'escalationActivity',\n },\n }\n\n// ---------------------------------------------------------------------------\n// Action types — the three ways a report's status can change\n// ---------------------------------------------------------------------------\n\nexport type ReportUpdateAction =\n | { type: 'activity'; activityType: string }\n | { type: 'event'; eventType: string }\n | { type: 'queue' }\n\n// ---------------------------------------------------------------------------\n// Result type\n// ---------------------------------------------------------------------------\n\nexport type ActivityRecord = {\n activityType: string\n previousStatus: string\n}\n\nexport type ReportUpdateResult = {\n nextStatus: string | null\n activity: ActivityRecord | null\n}\n\n// ---------------------------------------------------------------------------\n// Core function\n// ---------------------------------------------------------------------------\n\n/**\n * Determines the next status and activity record for a report update.\n *\n * @throws AlreadyInTargetState if the report is already in the target status\n * @throws InvalidStateTransition if the transition is not allowed\n * @returns nextStatus (null = no change) and activity (null = nothing to record)\n */\nexport function handleReportUpdate(\n currentStatus: string,\n action: ReportUpdateAction,\n): ReportUpdateResult {\n switch (action.type) {\n case 'activity':\n return handleActivityAction(currentStatus, action.activityType)\n case 'event':\n return handleEventAction(currentStatus, action.eventType)\n case 'queue':\n return handleQueueAction(currentStatus)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Action handlers\n// ---------------------------------------------------------------------------\n\nfunction handleActivityAction(\n currentStatus: string,\n activityType: string,\n): ReportUpdateResult {\n const toState = ACTIVITY_TO_STATE[activityType] ?? null\n\n // Note-type activities — no state change, but still produce an activity record\n if (toState === null) {\n return { nextStatus: null, activity: null }\n }\n\n // Check activity-specific source-state constraints\n const validFromStates = ACTIVITY_VALID_FROM_STATES[activityType]\n if (validFromStates && !validFromStates.includes(currentStatus)) {\n throw new InvalidStateTransition(currentStatus, toState)\n }\n\n validateTransition(currentStatus, toState)\n\n return {\n nextStatus: toState,\n activity: { activityType, previousStatus: currentStatus },\n }\n}\n\nfunction handleEventAction(\n currentStatus: string,\n eventType: string,\n): ReportUpdateResult {\n const mapping = EVENT_TYPE_MAP[eventType]\n if (!mapping) {\n // Event type doesn't affect report status\n return { nextStatus: null, activity: null }\n }\n\n validateTransition(currentStatus, mapping.status)\n\n return {\n nextStatus: mapping.status,\n activity: {\n activityType: mapping.activityType,\n previousStatus: currentStatus,\n },\n }\n}\n\nfunction handleQueueAction(currentStatus: string): ReportUpdateResult {\n // Queue routing only transitions open → queued\n if (currentStatus !== 'open') {\n return { nextStatus: null, activity: null }\n }\n\n return {\n nextStatus: 'queued',\n activity: { activityType: 'queueActivity', previousStatus: currentStatus },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Shared validation\n// ---------------------------------------------------------------------------\n\nfunction validateTransition(fromStatus: string, toStatus: string): void {\n if (fromStatus === toStatus) {\n throw new AlreadyInTargetState(fromStatus, toStatus)\n }\n const allowed = VALID_TRANSITIONS[fromStatus] ?? []\n if (!allowed.includes(toStatus)) {\n throw new InvalidStateTransition(fromStatus, toStatus)\n }\n}\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Database } from '../db';
|
|
2
|
+
import { QueueService } from '../queue/service';
|
|
3
|
+
export type ReassignReportQueueParams = {
|
|
4
|
+
reportId: number;
|
|
5
|
+
toQueueId: number;
|
|
6
|
+
comment?: string;
|
|
7
|
+
createdBy: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function reassignReportQueue(db: Database, queueService: QueueService, params: ReassignReportQueueParams): Promise<void>;
|
|
10
|
+
//# sourceMappingURL=reassign.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reassign.d.ts","sourceRoot":"","sources":["../../src/report/reassign.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAChC,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAE/C,MAAM,MAAM,yBAAyB,GAAG;IACtC,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,QAAQ,EACZ,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE,yBAAyB,GAChC,OAAO,CAAC,IAAI,CAAC,CA6Ff"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.reassignReportQueue = reassignReportQueue;
|
|
4
|
+
const xrpc_server_1 = require("@atproto/xrpc-server");
|
|
5
|
+
async function reassignReportQueue(db, queueService, params) {
|
|
6
|
+
const { reportId, toQueueId, comment, createdBy } = params;
|
|
7
|
+
if (toQueueId !== -1) {
|
|
8
|
+
const queue = await queueService.getById(toQueueId);
|
|
9
|
+
if (!queue) {
|
|
10
|
+
throw new xrpc_server_1.InvalidRequestError(`Queue ${toQueueId} not found`, 'QueueNotFound');
|
|
11
|
+
}
|
|
12
|
+
if (!queue.enabled) {
|
|
13
|
+
throw new xrpc_server_1.InvalidRequestError(`Queue ${toQueueId} is disabled`, 'QueueDisabled');
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
await db.transaction(async (dbTxn) => {
|
|
17
|
+
const report = await dbTxn.db
|
|
18
|
+
.selectFrom('report')
|
|
19
|
+
.select(['id', 'status', 'queueId'])
|
|
20
|
+
.where('id', '=', reportId)
|
|
21
|
+
.forUpdate()
|
|
22
|
+
.executeTakeFirst();
|
|
23
|
+
if (!report) {
|
|
24
|
+
throw new xrpc_server_1.InvalidRequestError(`Report ${reportId} not found`, 'ReportNotFound');
|
|
25
|
+
}
|
|
26
|
+
if (report.status === 'closed') {
|
|
27
|
+
throw new xrpc_server_1.InvalidRequestError(`Report ${reportId} is closed and cannot be reassigned`, 'ReportClosed');
|
|
28
|
+
}
|
|
29
|
+
// NULL and -1 both mean "unassigned" for equivalence purposes.
|
|
30
|
+
const currentQueueId = report.queueId ?? -1;
|
|
31
|
+
if (currentQueueId === toQueueId) {
|
|
32
|
+
throw new xrpc_server_1.InvalidRequestError(`Report ${reportId} is already in queue ${toQueueId}`, 'AlreadyInTargetQueue');
|
|
33
|
+
}
|
|
34
|
+
const previousStatus = report.status;
|
|
35
|
+
let nextStatus = previousStatus;
|
|
36
|
+
if (toQueueId !== -1 && previousStatus === 'open') {
|
|
37
|
+
nextStatus = 'queued';
|
|
38
|
+
}
|
|
39
|
+
else if (toQueueId === -1 && previousStatus === 'queued') {
|
|
40
|
+
nextStatus = 'open';
|
|
41
|
+
}
|
|
42
|
+
const now = new Date().toISOString();
|
|
43
|
+
const reportUpdate = {
|
|
44
|
+
queueId: toQueueId,
|
|
45
|
+
queuedAt: toQueueId === -1 ? null : now,
|
|
46
|
+
updatedAt: now,
|
|
47
|
+
};
|
|
48
|
+
if (nextStatus !== previousStatus) {
|
|
49
|
+
reportUpdate.status = nextStatus;
|
|
50
|
+
}
|
|
51
|
+
await dbTxn.db
|
|
52
|
+
.updateTable('report')
|
|
53
|
+
.set(reportUpdate)
|
|
54
|
+
.where('id', '=', reportId)
|
|
55
|
+
.execute();
|
|
56
|
+
await dbTxn.db
|
|
57
|
+
.insertInto('report_activity')
|
|
58
|
+
.values({
|
|
59
|
+
reportId,
|
|
60
|
+
activityType: 'queueActivity',
|
|
61
|
+
previousStatus,
|
|
62
|
+
internalNote: comment ?? null,
|
|
63
|
+
publicNote: null,
|
|
64
|
+
meta: {
|
|
65
|
+
fromQueueId: report.queueId ?? null,
|
|
66
|
+
toQueueId,
|
|
67
|
+
},
|
|
68
|
+
isAutomated: false,
|
|
69
|
+
createdBy,
|
|
70
|
+
createdAt: now,
|
|
71
|
+
})
|
|
72
|
+
.execute();
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=reassign.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reassign.js","sourceRoot":"","sources":["../../src/report/reassign.ts"],"names":[],"mappings":"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","sourcesContent":["import { InvalidRequestError } from '@atproto/xrpc-server'\nimport { Database } from '../db'\nimport { QueueService } from '../queue/service'\n\nexport type ReassignReportQueueParams = {\n reportId: number\n toQueueId: number\n comment?: string\n createdBy: string\n}\n\nexport async function reassignReportQueue(\n db: Database,\n queueService: QueueService,\n params: ReassignReportQueueParams,\n): Promise<void> {\n const { reportId, toQueueId, comment, createdBy } = params\n\n if (toQueueId !== -1) {\n const queue = await queueService.getById(toQueueId)\n if (!queue) {\n throw new InvalidRequestError(\n `Queue ${toQueueId} not found`,\n 'QueueNotFound',\n )\n }\n if (!queue.enabled) {\n throw new InvalidRequestError(\n `Queue ${toQueueId} is disabled`,\n 'QueueDisabled',\n )\n }\n }\n\n await db.transaction(async (dbTxn) => {\n const report = await dbTxn.db\n .selectFrom('report')\n .select(['id', 'status', 'queueId'])\n .where('id', '=', reportId)\n .forUpdate()\n .executeTakeFirst()\n\n if (!report) {\n throw new InvalidRequestError(\n `Report ${reportId} not found`,\n 'ReportNotFound',\n )\n }\n\n if (report.status === 'closed') {\n throw new InvalidRequestError(\n `Report ${reportId} is closed and cannot be reassigned`,\n 'ReportClosed',\n )\n }\n\n // NULL and -1 both mean \"unassigned\" for equivalence purposes.\n const currentQueueId = report.queueId ?? -1\n if (currentQueueId === toQueueId) {\n throw new InvalidRequestError(\n `Report ${reportId} is already in queue ${toQueueId}`,\n 'AlreadyInTargetQueue',\n )\n }\n\n const previousStatus = report.status\n let nextStatus: string = previousStatus\n if (toQueueId !== -1 && previousStatus === 'open') {\n nextStatus = 'queued'\n } else if (toQueueId === -1 && previousStatus === 'queued') {\n nextStatus = 'open'\n }\n\n const now = new Date().toISOString()\n\n const reportUpdate: Record<string, string | number | null> = {\n queueId: toQueueId,\n queuedAt: toQueueId === -1 ? null : now,\n updatedAt: now,\n }\n if (nextStatus !== previousStatus) {\n reportUpdate.status = nextStatus\n }\n\n await dbTxn.db\n .updateTable('report')\n .set(reportUpdate)\n .where('id', '=', reportId)\n .execute()\n\n await dbTxn.db\n .insertInto('report_activity')\n .values({\n reportId,\n activityType: 'queueActivity',\n previousStatus,\n internalNote: comment ?? null,\n publicNote: null,\n meta: {\n fromQueueId: report.queueId ?? null,\n toQueueId,\n },\n isAutomated: false,\n createdBy,\n createdAt: now,\n })\n .execute()\n })\n}\n"]}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { Selectable } from 'kysely';
|
|
2
|
+
import { Database } from '../db';
|
|
3
|
+
import { ReportStat } from '../db/schema/report_stat';
|
|
4
|
+
/**
|
|
5
|
+
* Grouped report types. Stats are computed per group rather than per individual report type.
|
|
6
|
+
*/
|
|
7
|
+
export declare const REPORT_TYPE_GROUPS: Record<string, string[]>;
|
|
8
|
+
export type ReportStatsServiceCreator = (db: Database) => ReportStatsService;
|
|
9
|
+
export type ReportStatGroup = {
|
|
10
|
+
queueId: number | null;
|
|
11
|
+
moderatorDid: string | null;
|
|
12
|
+
reportTypes: string[] | null;
|
|
13
|
+
};
|
|
14
|
+
export type AggregateStatistics = {
|
|
15
|
+
inboundCount: number;
|
|
16
|
+
pendingCount: number;
|
|
17
|
+
actionedCount: number;
|
|
18
|
+
escalatedCount: number;
|
|
19
|
+
actionRate: number;
|
|
20
|
+
avgHandlingTimeSec?: number;
|
|
21
|
+
};
|
|
22
|
+
export type QueueStatistics = {
|
|
23
|
+
inboundCount: number;
|
|
24
|
+
pendingCount: number;
|
|
25
|
+
actionedCount: number;
|
|
26
|
+
escalatedCount: number;
|
|
27
|
+
actionRate: number;
|
|
28
|
+
avgHandlingTimeSec?: number;
|
|
29
|
+
};
|
|
30
|
+
export type ModeratorStatistics = {
|
|
31
|
+
inboundCount: number;
|
|
32
|
+
actionedCount: number;
|
|
33
|
+
avgHandlingTimeSec?: number;
|
|
34
|
+
};
|
|
35
|
+
export type ReportTypeStatistics = {
|
|
36
|
+
inboundCount: number;
|
|
37
|
+
pendingCount: number;
|
|
38
|
+
actionedCount: number;
|
|
39
|
+
escalatedCount: number;
|
|
40
|
+
actionRate: number;
|
|
41
|
+
avgHandlingTimeSec?: number;
|
|
42
|
+
};
|
|
43
|
+
export type ReportStatistics = QueueStatistics | ModeratorStatistics | AggregateStatistics | ReportTypeStatistics;
|
|
44
|
+
export declare class ReportStatsService {
|
|
45
|
+
db: Database;
|
|
46
|
+
constructor(db: Database);
|
|
47
|
+
static creator(): ReportStatsServiceCreator;
|
|
48
|
+
/**
|
|
49
|
+
* Compute stats for today and finalize yesterday if needed.
|
|
50
|
+
* Called periodically by the StatsComputer daemon.
|
|
51
|
+
*/
|
|
52
|
+
materializeAll(opts?: {
|
|
53
|
+
force?: boolean;
|
|
54
|
+
}): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Compute stats for a specific date range. Used by the refreshStats endpoint.
|
|
57
|
+
*/
|
|
58
|
+
refreshDateRange(opts: {
|
|
59
|
+
startDate: string;
|
|
60
|
+
endDate: string;
|
|
61
|
+
queueIds?: number[];
|
|
62
|
+
}): Promise<void>;
|
|
63
|
+
/** Compute and write all groups for a single date. */
|
|
64
|
+
private materializeDate;
|
|
65
|
+
/** Fetch all stat rows for a date, keyed by groupKey for O(1) lookup. */
|
|
66
|
+
private fetchExistingStatsByKey;
|
|
67
|
+
/** List out the groups to compute stats for. */
|
|
68
|
+
private enumerateGroups;
|
|
69
|
+
/**
|
|
70
|
+
* Run batched GROUP BY queries for a calendar date.
|
|
71
|
+
* Returns 5 result sets covering all group types.
|
|
72
|
+
*/
|
|
73
|
+
private computeBatchedStats;
|
|
74
|
+
/** Resolve a single group's stats from batched query results (pure in-memory). */
|
|
75
|
+
private resolveGroupStats;
|
|
76
|
+
private resolveQueueStats;
|
|
77
|
+
private resolveReportTypeStats;
|
|
78
|
+
private resolveModeratorStats;
|
|
79
|
+
/** Build an upsert row from (date, group, stats). */
|
|
80
|
+
private buildUpsertRow;
|
|
81
|
+
/**
|
|
82
|
+
* Wraps a DELETE+INSERT for each row in a single transaction so we pay one
|
|
83
|
+
* commit per cycle instead of one per group. NULL-aware WHERE clauses match
|
|
84
|
+
* the existing PG <15 NULL semantics without needing a unique index.
|
|
85
|
+
*/
|
|
86
|
+
private bulkUpsert;
|
|
87
|
+
/** Get a single stat row for a date + group. */
|
|
88
|
+
private getStatForDate;
|
|
89
|
+
/** Get today's live stats for a group. */
|
|
90
|
+
getLiveStats(group: ReportStatGroup): Promise<Selectable<ReportStat> | undefined>;
|
|
91
|
+
/** Get live stats for multiple queues in a single query. */
|
|
92
|
+
getLiveStatsForQueues(queueIds: number[]): Promise<Map<number, Selectable<ReportStat>>>;
|
|
93
|
+
/** Get historical stats for a date range, paginated. */
|
|
94
|
+
getHistoricalStats(opts: {
|
|
95
|
+
group: ReportStatGroup;
|
|
96
|
+
startDate?: string;
|
|
97
|
+
endDate?: string;
|
|
98
|
+
limit: number;
|
|
99
|
+
cursor?: string;
|
|
100
|
+
}): Promise<{
|
|
101
|
+
stats: Selectable<ReportStat>[];
|
|
102
|
+
cursor?: string;
|
|
103
|
+
}>;
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=stats.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../src/report/stats.ts"],"names":[],"mappings":"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"}
|