@atproto/ozone 0.2.2 → 0.2.4
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 +26 -0
- package/dist/api/chat/index.d.ts.map +1 -1
- package/dist/api/chat/index.js +2 -0
- package/dist/api/chat/index.js.map +1 -1
- package/dist/api/health.d.ts.map +1 -1
- package/dist/api/moderation/util.d.ts.map +1 -1
- package/dist/api/util.d.ts +2 -2
- package/dist/api/util.d.ts.map +1 -1
- package/dist/api/well-known.d.ts.map +1 -1
- package/dist/assignment/index.d.ts.map +1 -1
- package/dist/assignment/index.js.map +1 -1
- package/dist/auth-verifier.d.ts.map +1 -1
- package/dist/background.d.ts.map +1 -1
- package/dist/background.js.map +1 -1
- package/dist/communication-service/template.d.ts.map +1 -1
- package/dist/communication-service/template.js.map +1 -1
- package/dist/communication-service/util.d.ts.map +1 -1
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/secrets.d.ts.map +1 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js.map +1 -1
- package/dist/daemon/blob-diverter.d.ts.map +1 -1
- package/dist/daemon/blob-diverter.js.map +1 -1
- package/dist/daemon/context.d.ts.map +1 -1
- package/dist/daemon/context.js.map +1 -1
- package/dist/daemon/event-pusher.d.ts +1 -1
- package/dist/daemon/event-pusher.d.ts.map +1 -1
- package/dist/daemon/event-reverser.d.ts.map +1 -1
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/materialized-view-refresher.d.ts.map +1 -1
- package/dist/daemon/queue-router.d.ts.map +1 -1
- package/dist/daemon/scheduled-action-processor.d.ts.map +1 -1
- package/dist/daemon/stats-computer.d.ts.map +1 -1
- package/dist/daemon/strike-expiry-processor.d.ts.map +1 -1
- package/dist/daemon/team-profile-synchronizer.d.ts.map +1 -1
- package/dist/daemon/verification-listener.d.ts.map +1 -1
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/migrations/provider.d.ts.map +1 -1
- package/dist/db/migrations/provider.js.map +1 -1
- package/dist/db/pagination.d.ts +1 -1
- package/dist/db/pagination.d.ts.map +1 -1
- package/dist/db/pagination.js.map +1 -1
- package/dist/db/schema/index.d.ts.map +1 -1
- package/dist/db/types.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/jetstream/service.d.ts.map +1 -1
- package/dist/lexicon/index.d.ts +2 -0
- package/dist/lexicon/index.d.ts.map +1 -1
- package/dist/lexicon/index.js +4 -0
- package/dist/lexicon/index.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +17172 -17038
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +77 -1
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/chat/bsky/convo/defs.d.ts +13 -0
- package/dist/lexicon/types/chat/bsky/convo/defs.d.ts.map +1 -1
- package/dist/lexicon/types/chat/bsky/convo/defs.js +7 -0
- package/dist/lexicon/types/chat/bsky/convo/defs.js.map +1 -1
- package/dist/lexicon/types/chat/bsky/convo/getUnreadCounts.d.ts +25 -0
- package/dist/lexicon/types/chat/bsky/convo/getUnreadCounts.d.ts.map +1 -0
- package/dist/lexicon/types/chat/bsky/convo/getUnreadCounts.js +5 -0
- package/dist/lexicon/types/chat/bsky/convo/getUnreadCounts.js.map +1 -0
- package/dist/lexicon/types/chat/bsky/convo/sendMessage.d.ts +1 -1
- package/dist/lexicon/types/chat/bsky/convo/sendMessage.d.ts.map +1 -1
- package/dist/lexicon/types/chat/bsky/convo/sendMessage.js.map +1 -1
- package/dist/lexicon/types/chat/bsky/convo/sendMessageBatch.d.ts +1 -1
- package/dist/lexicon/types/chat/bsky/convo/sendMessageBatch.d.ts.map +1 -1
- package/dist/lexicon/types/chat/bsky/convo/sendMessageBatch.js.map +1 -1
- package/dist/lexicon/types/chat/bsky/convo/unlockConvo.d.ts +1 -1
- package/dist/lexicon/types/chat/bsky/convo/unlockConvo.d.ts.map +1 -1
- package/dist/lexicon/types/chat/bsky/convo/unlockConvo.js.map +1 -1
- package/dist/lexicon/types/chat/bsky/moderation/subscribeModEvents.d.ts +1 -1
- package/dist/lexicon/types/chat/bsky/moderation/subscribeModEvents.d.ts.map +1 -1
- package/dist/lexicon/types/chat/bsky/moderation/subscribeModEvents.js.map +1 -1
- package/dist/lexicon/util.d.ts.map +1 -1
- package/dist/mod-service/index.d.ts +38 -38
- package/dist/mod-service/index.d.ts.map +1 -1
- package/dist/mod-service/profile.d.ts.map +1 -1
- package/dist/mod-service/status.d.ts +80 -80
- package/dist/mod-service/status.d.ts.map +1 -1
- package/dist/mod-service/strike.d.ts.map +1 -1
- package/dist/mod-service/strike.js.map +1 -1
- package/dist/mod-service/subject.d.ts +4 -4
- package/dist/mod-service/subject.d.ts.map +1 -1
- package/dist/mod-service/util.d.ts +1 -1
- package/dist/mod-service/util.d.ts.map +1 -1
- package/dist/mod-service/views.d.ts.map +1 -1
- package/dist/mod-service/views.js.map +1 -1
- package/dist/queue/service.d.ts.map +1 -1
- package/dist/queue/service.js.map +1 -1
- package/dist/report/activity.d.ts +5 -5
- package/dist/report/activity.d.ts.map +1 -1
- package/dist/report/handle-report-update.d.ts.map +1 -1
- package/dist/report/handle-report-update.js.map +1 -1
- package/dist/report/stats.d.ts.map +1 -1
- package/dist/report/stats.js.map +1 -1
- package/dist/report/views.d.ts +2 -2
- package/dist/report/views.d.ts.map +1 -1
- package/dist/safelink/service.d.ts +3 -3
- package/dist/safelink/service.d.ts.map +1 -1
- package/dist/safelink/service.js.map +1 -1
- package/dist/scheduled-action/service.d.ts.map +1 -1
- package/dist/scheduled-action/service.js.map +1 -1
- package/dist/sequencer/outbox.d.ts.map +1 -1
- package/dist/sequencer/outbox.js.map +1 -1
- package/dist/sequencer/sequencer.d.ts.map +1 -1
- package/dist/sequencer/sequencer.js.map +1 -1
- package/dist/set/service.d.ts +1 -1
- package/dist/set/service.d.ts.map +1 -1
- package/dist/set/service.js.map +1 -1
- package/dist/setting/service.d.ts.map +1 -1
- package/dist/setting/service.js.map +1 -1
- package/dist/tag-service/content-tagger.d.ts.map +1 -1
- package/dist/tag-service/content-tagger.js.map +1 -1
- package/dist/tag-service/embed-tagger.d.ts.map +1 -1
- package/dist/tag-service/index.d.ts.map +1 -1
- package/dist/tag-service/index.js.map +1 -1
- package/dist/tag-service/language-tagger.d.ts.map +1 -1
- package/dist/tag-service/util.d.ts.map +1 -1
- package/dist/team/index.d.ts.map +1 -1
- package/dist/team/index.js.map +1 -1
- package/dist/util.d.ts.map +1 -1
- package/dist/verification/issuer.d.ts +1 -1
- package/dist/verification/issuer.d.ts.map +1 -1
- package/dist/verification/service.d.ts +5 -5
- package/dist/verification/service.d.ts.map +1 -1
- package/dist/verification/service.js.map +1 -1
- package/dist/verification/util.d.ts.map +1 -1
- package/package.json +13 -14
- package/src/api/chat/index.ts +2 -0
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -1 +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"]}
|
|
1
|
+
{"version":3,"file":"handle-report-update.js","sourceRoot":"","sources":["../../src/report/handle-report-update.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,8EAA8E;AAC9E,wEAAwE;AACxE,8EAA8E;AAE9E,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IAC7C,YACS,aAAqB,EACrB,YAAoB;QAE3B,KAAK,CAAC,yBAAyB,YAAY,UAAU,CAAC,CAAA;6BAH/C,aAAa;4BACb,YAAY;QAGnB,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAA;IACpC,CAAC;CACF;AAED,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IAC/C,YACS,UAAkB,EAClB,QAAgB;QAEvB,KAAK,CAAC,kCAAkC,UAAU,SAAS,QAAQ,GAAG,CAAC,CAAA;0BAHhE,UAAU;wBACV,QAAQ;QAGf,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAA;IACtC,CAAC;CACF;AAED,8EAA8E;AAC9E,uBAAuB;AACvB,8EAA8E;AAE9E,yEAAyE;AACzE,MAAM,CAAC,MAAM,iBAAiB,GAA6B;IACzD,IAAI,EAAE,CAAC,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,CAAC;IACnD,MAAM,EAAE,CAAC,MAAM,CAAC;IAChB,SAAS,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC7B,MAAM,EAAE,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,CAAC;CACpD,CAAA;AAED,0DAA0D;AAC1D,MAAM,iBAAiB,GAA2B;IAChD,aAAa,EAAE,QAAQ;IACvB,kBAAkB,EAAE,UAAU;IAC9B,kBAAkB,EAAE,WAAW;IAC/B,aAAa,EAAE,QAAQ;IACvB,cAAc,EAAE,MAAM;CACvB,CAAA;AAED,qEAAqE;AACrE,MAAM,0BAA0B,GAA6B;IAC3D,cAAc,EAAE,CAAC,QAAQ,CAAC;CAC3B,CAAA;AAED,+DAA+D;AAC/D,MAAM,cAAc,GAClB;IACE,iDAAiD,EAAE;QACjD,MAAM,EAAE,QAAQ;QAChB,YAAY,EAAE,eAAe;KAC9B;IACD,8CAA8C,EAAE;QAC9C,MAAM,EAAE,QAAQ;QAChB,YAAY,EAAE,eAAe;KAC9B;IACD,2CAA2C,EAAE;QAC3C,MAAM,EAAE,QAAQ;QAChB,YAAY,EAAE,eAAe;KAC9B;IACD,6CAA6C,EAAE;QAC7C,MAAM,EAAE,QAAQ;QAChB,YAAY,EAAE,eAAe;KAC9B;IACD,8CAA8C,EAAE;QAC9C,MAAM,EAAE,WAAW;QACnB,YAAY,EAAE,oBAAoB;KACnC;CACF,CAAA;AAyBH,8EAA8E;AAC9E,gBAAgB;AAChB,8EAA8E;AAE9E;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAChC,aAAqB,EACrB,MAA0B;IAE1B,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,UAAU;YACb,OAAO,oBAAoB,CAAC,aAAa,EAAE,MAAM,CAAC,YAAY,CAAC,CAAA;QACjE,KAAK,OAAO;YACV,OAAO,iBAAiB,CAAC,aAAa,EAAE,MAAM,CAAC,SAAS,CAAC,CAAA;QAC3D,KAAK,OAAO;YACV,OAAO,iBAAiB,CAAC,aAAa,CAAC,CAAA;IAC3C,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,kBAAkB;AAClB,8EAA8E;AAE9E,SAAS,oBAAoB,CAC3B,aAAqB,EACrB,YAAoB;IAEpB,MAAM,OAAO,GAAG,iBAAiB,CAAC,YAAY,CAAC,IAAI,IAAI,CAAA;IAEvD,+EAA+E;IAC/E,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAA;IAC7C,CAAC;IAED,mDAAmD;IACnD,MAAM,eAAe,GAAG,0BAA0B,CAAC,YAAY,CAAC,CAAA;IAChE,IAAI,eAAe,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;QAChE,MAAM,IAAI,sBAAsB,CAAC,aAAa,EAAE,OAAO,CAAC,CAAA;IAC1D,CAAC;IAED,kBAAkB,CAAC,aAAa,EAAE,OAAO,CAAC,CAAA;IAE1C,OAAO;QACL,UAAU,EAAE,OAAO;QACnB,QAAQ,EAAE,EAAE,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE;KAC1D,CAAA;AACH,CAAC;AAED,SAAS,iBAAiB,CACxB,aAAqB,EACrB,SAAiB;IAEjB,MAAM,OAAO,GAAG,cAAc,CAAC,SAAS,CAAC,CAAA;IACzC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,0CAA0C;QAC1C,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAA;IAC7C,CAAC;IAED,kBAAkB,CAAC,aAAa,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;IAEjD,OAAO;QACL,UAAU,EAAE,OAAO,CAAC,MAAM;QAC1B,QAAQ,EAAE;YACR,YAAY,EAAE,OAAO,CAAC,YAAY;YAClC,cAAc,EAAE,aAAa;SAC9B;KACF,CAAA;AACH,CAAC;AAED,SAAS,iBAAiB,CAAC,aAAqB;IAC9C,+CAA+C;IAC/C,IAAI,aAAa,KAAK,MAAM,EAAE,CAAC;QAC7B,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAA;IAC7C,CAAC;IAED,OAAO;QACL,UAAU,EAAE,QAAQ;QACpB,QAAQ,EAAE,EAAE,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,aAAa,EAAE;KAC3E,CAAA;AACH,CAAC;AAED,8EAA8E;AAC9E,oBAAoB;AACpB,8EAA8E;AAE9E,SAAS,kBAAkB,CAAC,UAAkB,EAAE,QAAgB;IAC9D,IAAI,UAAU,KAAK,QAAQ,EAAE,CAAC;QAC5B,MAAM,IAAI,oBAAoB,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAA;IACtD,CAAC;IACD,MAAM,OAAO,GAAG,iBAAiB,CAAC,UAAU,CAAC,IAAI,EAAE,CAAA;IACnD,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,sBAAsB,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAA;IACxD,CAAC;AACH,CAAC","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"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../src/report/stats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAO,MAAM,QAAQ,CAAA;AAExC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAEzC,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAA;AAIxD;;GAEG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAqEvD,CAAA;AAID,MAAM,MAAM,yBAAyB,GAAG,CAAC,EAAE,EAAE,QAAQ,KAAK,kBAAkB,CAAA;AAE5E,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,WAAW,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;CAC7B,CAAA;AACD,MAAM,MAAM,mBAAmB,GAAG;IAChC,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;IACrB,cAAc,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B,CAAA;AACD,MAAM,MAAM,eAAe,GAAG;IAC5B,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;IACrB,cAAc,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B,CAAA;AACD,MAAM,MAAM,mBAAmB,GAAG;IAChC,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B,CAAA;AACD,MAAM,MAAM,oBAAoB,GAAG;IACjC,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;IACrB,cAAc,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B,CAAA;AACD,MAAM,MAAM,gBAAgB,GACxB,eAAe,GACf,mBAAmB,GACnB,mBAAmB,GACnB,oBAAoB,CAAA;AAwDxB,qBAAa,kBAAkB;IACV,EAAE,EAAE,QAAQ;
|
|
1
|
+
{"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../src/report/stats.ts"],"names":[],"mappings":"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"}
|
package/dist/report/stats.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stats.js","sourceRoot":"","sources":["../../src/report/stats.ts"],"names":[],"mappings":"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","sourcesContent":["import { Selectable, sql } from 'kysely'\nimport { MINUTE } from '@atproto/common'\nimport { Database } from '../db/index.js'\nimport { ComputedAtIdKeyset, paginate } from '../db/pagination.js'\nimport { ReportStat } from '../db/schema/report_stat.js'\nimport { jsonb } from '../db/types.js'\nimport { dbLogger } from '../logger.js'\n\n/**\n * Grouped report types. Stats are computed per group rather than per individual report type.\n */\nexport const REPORT_TYPE_GROUPS: Record<string, string[]> = {\n Legacy: [\n 'com.atproto.moderation.defs#reasonSpam',\n 'com.atproto.moderation.defs#reasonViolation',\n 'com.atproto.moderation.defs#reasonMisleading',\n 'com.atproto.moderation.defs#reasonSexual',\n 'com.atproto.moderation.defs#reasonRude',\n 'com.atproto.moderation.defs#reasonOther',\n 'com.atproto.moderation.defs#reasonAppeal',\n ],\n Appeal: ['tools.ozone.report.defs#reasonAppeal'],\n Violence: [\n 'tools.ozone.report.defs#reasonViolenceAnimalWelfare',\n 'tools.ozone.report.defs#reasonViolenceThreats',\n 'tools.ozone.report.defs#reasonViolenceGraphicContent',\n 'tools.ozone.report.defs#reasonViolenceSelfHarm',\n 'tools.ozone.report.defs#reasonViolenceGlorification',\n 'tools.ozone.report.defs#reasonViolenceExtremistContent',\n 'tools.ozone.report.defs#reasonViolenceTrafficking',\n 'tools.ozone.report.defs#reasonViolenceOther',\n ],\n Sexual: [\n 'tools.ozone.report.defs#reasonSexualAbuseContent',\n 'tools.ozone.report.defs#reasonSexualNCII',\n 'tools.ozone.report.defs#reasonSexualSextortion',\n 'tools.ozone.report.defs#reasonSexualDeepfake',\n 'tools.ozone.report.defs#reasonSexualAnimal',\n 'tools.ozone.report.defs#reasonSexualUnlabeled',\n 'tools.ozone.report.defs#reasonSexualOther',\n ],\n 'Child Safety': [\n 'tools.ozone.report.defs#reasonChildSafetyCSAM',\n 'tools.ozone.report.defs#reasonChildSafetyGroom',\n 'tools.ozone.report.defs#reasonChildSafetyMinorPrivacy',\n 'tools.ozone.report.defs#reasonChildSafetyEndangerment',\n 'tools.ozone.report.defs#reasonChildSafetyHarassment',\n 'tools.ozone.report.defs#reasonChildSafetyPromotion',\n 'tools.ozone.report.defs#reasonChildSafetyOther',\n ],\n Harassment: [\n 'tools.ozone.report.defs#reasonHarassmentTroll',\n 'tools.ozone.report.defs#reasonHarassmentTargeted',\n 'tools.ozone.report.defs#reasonHarassmentHateSpeech',\n 'tools.ozone.report.defs#reasonHarassmentDoxxing',\n 'tools.ozone.report.defs#reasonHarassmentOther',\n ],\n Misleading: [\n 'tools.ozone.report.defs#reasonMisleadingBot',\n 'tools.ozone.report.defs#reasonMisleadingImpersonation',\n 'tools.ozone.report.defs#reasonMisleadingSpam',\n 'tools.ozone.report.defs#reasonMisleadingScam',\n 'tools.ozone.report.defs#reasonMisleadingSyntheticContent',\n 'tools.ozone.report.defs#reasonMisleadingMisinformation',\n 'tools.ozone.report.defs#reasonMisleadingOther',\n ],\n 'Rule Violations': [\n 'tools.ozone.report.defs#reasonRuleSiteSecurity',\n 'tools.ozone.report.defs#reasonRuleStolenContent',\n 'tools.ozone.report.defs#reasonRuleProhibitedSales',\n 'tools.ozone.report.defs#reasonRuleBanEvasion',\n 'tools.ozone.report.defs#reasonRuleOther',\n ],\n Civic: [\n 'tools.ozone.report.defs#reasonCivicElectoralProcess',\n 'tools.ozone.report.defs#reasonCivicDisclosure',\n 'tools.ozone.report.defs#reasonCivicInterference',\n 'tools.ozone.report.defs#reasonCivicMisinformation',\n 'tools.ozone.report.defs#reasonCivicImpersonation',\n ],\n}\n\nconst REPORT_STAT_LIVE_TTL = 15 * MINUTE\n\nexport type ReportStatsServiceCreator = (db: Database) => ReportStatsService\n\nexport type ReportStatGroup = {\n queueId: number | null\n moderatorDid: string | null\n reportTypes: string[] | null\n}\nexport type AggregateStatistics = {\n inboundCount: number\n pendingCount: number\n actionedCount: number\n escalatedCount: number\n actionRate: number\n avgHandlingTimeSec?: number\n}\nexport type QueueStatistics = {\n inboundCount: number\n pendingCount: number\n actionedCount: number\n escalatedCount: number\n actionRate: number\n avgHandlingTimeSec?: number\n}\nexport type ModeratorStatistics = {\n inboundCount: number\n actionedCount: number\n avgHandlingTimeSec?: number\n}\nexport type ReportTypeStatistics = {\n inboundCount: number\n pendingCount: number\n actionedCount: number\n escalatedCount: number\n actionRate: number\n avgHandlingTimeSec?: number\n}\nexport type ReportStatistics =\n | QueueStatistics\n | ModeratorStatistics\n | AggregateStatistics\n | ReportTypeStatistics\n\n// Batched query result types\ntype QueueCountRow = {\n queueId: number | null\n count: string\n}\ntype QueueWindowRow = {\n queueId: number | null\n inboundCount: string\n actionedCount: string\n escalatedCount: string\n handlingTimeSum: string | null\n handlingTimeCount: string\n}\ntype TypeCountRow = {\n reportType: string\n count: string\n}\ntype TypeWindowRow = {\n reportType: string\n inboundCount: string\n actionedCount: string\n escalatedCount: string\n handlingTimeSum: string | null\n handlingTimeCount: string\n}\ntype ModeratorWindowRow = {\n did: string\n inboundCount: string\n actionedCount: string\n handlingTimeSum: string | null\n handlingTimeCount: string\n}\ntype BatchedStats = {\n queuePending: QueueCountRow[]\n queueWindow: QueueWindowRow[]\n typePending: TypeCountRow[]\n typeWindow: TypeWindowRow[]\n moderator: ModeratorWindowRow[]\n}\n\ntype UpsertRow = {\n date: string\n queueId: number | null\n moderatorDid: string | null\n reportTypes: string[] | null\n inboundCount: number | null\n pendingCount: number | null\n actionedCount: number | null\n escalatedCount: number | null\n actionRate: number | null\n avgHandlingTimeSec: number | null\n computedAt: string\n}\n\nexport class ReportStatsService {\n constructor(public db: Database) {}\n\n static creator(): ReportStatsServiceCreator {\n return (db: Database) => new ReportStatsService(db)\n }\n\n /**\n * Compute stats for today and finalize yesterday if needed.\n * Called periodically by the StatsComputer daemon.\n */\n async materializeAll(opts?: { force?: boolean }): Promise<void> {\n try {\n const start = Date.now()\n const today = toDateString(new Date())\n const yesterday = toDateString(new Date(Date.now() - 24 * 60 * 60 * 1000))\n\n // Always compute today's stats\n await this.materializeDate(today, opts)\n\n // Finalize yesterday if its snapshot is missing or stale\n if (!opts?.force) {\n const yesterdayRow = await this.db.db\n .selectFrom('report_stat')\n .select('computedAt')\n .where('date', '=', yesterday)\n .orderBy('computedAt', 'desc')\n .executeTakeFirst()\n const endOfYesterday = new Date(`${yesterday}T23:59:59.999Z`).getTime()\n if (\n !yesterdayRow ||\n new Date(yesterdayRow.computedAt).getTime() < endOfYesterday\n ) {\n await this.materializeDate(yesterday, { force: true })\n }\n } else {\n await this.materializeDate(yesterday, { force: true })\n }\n\n const duration = Date.now() - start\n dbLogger.info({ duration }, 'report stats materialization completed')\n } catch (err) {\n dbLogger.error({ err }, 'report stats materialization errored')\n }\n }\n\n /**\n * Compute stats for a specific date range. Used by the refreshStats endpoint.\n */\n async refreshDateRange(opts: {\n startDate: string\n endDate: string\n queueIds?: number[]\n }): Promise<void> {\n const start = new Date(opts.startDate)\n const end = new Date(opts.endDate)\n\n for (let d = new Date(start); d <= end; d.setUTCDate(d.getUTCDate() + 1)) {\n const dateStr = toDateString(d)\n if (opts.queueIds?.length) {\n // Recompute only specific queue groups for this date\n const batched = await this.computeBatchedStats(dateStr)\n const rows: UpsertRow[] = []\n for (const queueId of opts.queueIds) {\n const group: ReportStatGroup = {\n queueId,\n moderatorDid: null,\n reportTypes: null,\n }\n const stats = this.resolveGroupStats(group, batched)\n rows.push(this.buildUpsertRow(dateStr, group, stats))\n }\n await this.bulkUpsert(rows)\n } else {\n await this.materializeDate(dateStr, { force: true })\n }\n }\n }\n\n /** Compute and write all groups for a single date. */\n private async materializeDate(\n date: string,\n opts?: { force?: boolean },\n ): Promise<void> {\n const groups = await this.enumerateGroups()\n const batched = await this.computeBatchedStats(date)\n const today = toDateString(new Date())\n const isToday = date === today\n\n // Batch the cache check so we don't issue one SELECT per group.\n const existingByKey = !opts?.force\n ? await this.fetchExistingStatsByKey(date)\n : null\n\n const rows: UpsertRow[] = []\n for (const group of groups) {\n try {\n if (existingByKey) {\n const cached = existingByKey.get(groupKey(group))\n if (cached) {\n // Historical dates: never recompute. Today: recompute if stale.\n if (!isToday) continue\n const age = Date.now() - new Date(cached.computedAt).getTime()\n if (age < REPORT_STAT_LIVE_TTL) continue\n }\n }\n const stats = this.resolveGroupStats(group, batched)\n rows.push(this.buildUpsertRow(date, group, stats))\n } catch (err) {\n dbLogger.error(\n { err, group, date },\n 'error preparing report stats group',\n )\n }\n }\n\n await this.bulkUpsert(rows)\n }\n\n /** Fetch all stat rows for a date, keyed by groupKey for O(1) lookup. */\n private async fetchExistingStatsByKey(\n date: string,\n ): Promise<Map<string, Selectable<ReportStat>>> {\n const existing = await this.db.db\n .selectFrom('report_stat')\n .selectAll()\n .where('date', '=', date)\n .execute()\n const map = new Map<string, Selectable<ReportStat>>()\n for (const row of existing) {\n map.set(\n groupKey({\n queueId: row.queueId,\n moderatorDid: row.moderatorDid,\n reportTypes: row.reportTypes,\n }),\n row,\n )\n }\n return map\n }\n\n /** List out the groups to compute stats for. */\n private async enumerateGroups(): Promise<ReportStatGroup[]> {\n const groups: ReportStatGroup[] = []\n\n const queues = await this.db.db\n .selectFrom('report_queue')\n .selectAll()\n .where('enabled', '=', true)\n .where('deletedAt', 'is', null)\n .execute()\n const members = await this.db.db\n .selectFrom('member')\n .select('did')\n .where('disabled', '=', false)\n .where('role', 'in', [\n 'tools.ozone.team.defs#roleAdmin',\n 'tools.ozone.team.defs#roleModerator',\n 'tools.ozone.team.defs#roleTriage',\n ])\n .execute()\n\n // aggregate\n groups.push({ queueId: null, moderatorDid: null, reportTypes: null })\n // per queue\n for (const queue of queues) {\n groups.push({ queueId: queue.id, moderatorDid: null, reportTypes: null })\n }\n // unqueued\n groups.push({ queueId: -1, moderatorDid: null, reportTypes: null })\n // per moderator\n for (const member of members) {\n groups.push({\n queueId: null,\n moderatorDid: member.did,\n reportTypes: null,\n })\n }\n // per report type group\n for (const groupTypes of Object.values(REPORT_TYPE_GROUPS)) {\n groups.push({\n queueId: null,\n moderatorDid: null,\n reportTypes: groupTypes,\n })\n }\n\n return groups\n }\n\n /**\n * Run batched GROUP BY queries for a calendar date.\n * Returns 5 result sets covering all group types.\n */\n private async computeBatchedStats(date: string): Promise<BatchedStats> {\n const dayStart = `${date}T00:00:00.000Z`\n const dayEnd = `${nextDate(date)}T00:00:00.000Z`\n\n const [queuePending, aggregatePending] = await Promise.all([\n // Pending count is a snapshot of all non-closed reports at time of computation\n this.db.db\n .selectFrom('report')\n .select(['queueId', sql<string>`count(*)`.as('count')])\n .where('status', '!=', 'closed')\n .where('queueId', 'is not', null)\n .groupBy('queueId')\n .execute(),\n // Aggregate pending (includes all reports, even un-routed)\n this.db.db\n .selectFrom('report')\n .select(sql<string>`count(*)`.as('count'))\n .where('status', '!=', 'closed')\n .executeTakeFirst(),\n ])\n\n const queueWindow = await this.db.db\n .selectFrom('report')\n .select([\n 'queueId',\n sql<string>`count(*)`.as('inboundCount'),\n sql<string>`count(*) filter (where \"status\" = 'closed' and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'actionedCount',\n ),\n sql<string>`count(*) filter (where \"status\" = 'escalated')`.as(\n 'escalatedCount',\n ),\n sql<string>`sum(extract(epoch from (\"closedAt\"::timestamp - \"createdAt\"::timestamp))) filter (where \"status\" = 'closed' and \"closedAt\" is not null and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'handlingTimeSum',\n ),\n sql<string>`count(*) filter (where \"status\" = 'closed' and \"closedAt\" is not null and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'handlingTimeCount',\n ),\n ])\n .where('createdAt', '>=', dayStart)\n .where('createdAt', '<', dayEnd)\n .where('queueId', 'is not', null)\n .groupBy('queueId')\n .execute()\n\n // Aggregate windowed (includes all reports)\n const aggregateWindow = await this.db.db\n .selectFrom('report')\n .select([\n sql<string>`count(*)`.as('inboundCount'),\n sql<string>`count(*) filter (where \"status\" = 'closed' and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'actionedCount',\n ),\n sql<string>`count(*) filter (where \"status\" = 'escalated')`.as(\n 'escalatedCount',\n ),\n sql<string>`sum(extract(epoch from (\"closedAt\"::timestamp - \"createdAt\"::timestamp))) filter (where \"status\" = 'closed' and \"closedAt\" is not null and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'handlingTimeSum',\n ),\n sql<string>`count(*) filter (where \"status\" = 'closed' and \"closedAt\" is not null and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'handlingTimeCount',\n ),\n ])\n .where('createdAt', '>=', dayStart)\n .where('createdAt', '<', dayEnd)\n .executeTakeFirst()\n\n const typePending = await this.db.db\n .selectFrom('report')\n .select(['reportType', sql<string>`count(*)`.as('count')])\n .where('status', '!=', 'closed')\n .groupBy('reportType')\n .execute()\n\n const typeWindow = await this.db.db\n .selectFrom('report')\n .select([\n 'reportType',\n sql<string>`count(*)`.as('inboundCount'),\n sql<string>`count(*) filter (where \"status\" = 'closed' and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'actionedCount',\n ),\n sql<string>`count(*) filter (where \"status\" = 'escalated')`.as(\n 'escalatedCount',\n ),\n sql<string>`sum(extract(epoch from (\"closedAt\"::timestamp - \"createdAt\"::timestamp))) filter (where \"status\" = 'closed' and \"closedAt\" is not null and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'handlingTimeSum',\n ),\n sql<string>`count(*) filter (where \"status\" = 'closed' and \"closedAt\" is not null and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'handlingTimeCount',\n ),\n ])\n .where('createdAt', '>=', dayStart)\n .where('createdAt', '<', dayEnd)\n .groupBy('reportType')\n .execute()\n\n const moderator = await this.db.db\n .selectFrom('report as r')\n .innerJoin('moderator_assignment as ma', (join) =>\n join.onRef('ma.reportId', '=', 'r.id').on('ma.endAt', 'is', null),\n )\n .select([\n 'ma.did',\n sql<string>`count(*)`.as('inboundCount'),\n sql<string>`count(*) filter (where r.\"status\" = 'closed')`.as(\n 'actionedCount',\n ),\n sql<string>`sum(extract(epoch from (r.\"closedAt\"::timestamp - ma.\"startAt\"::timestamp))) filter (where r.\"status\" = 'closed' and r.\"closedAt\" is not null)`.as(\n 'handlingTimeSum',\n ),\n sql<string>`count(*) filter (where r.\"status\" = 'closed' and r.\"closedAt\" is not null)`.as(\n 'handlingTimeCount',\n ),\n ])\n .where('r.createdAt', '>=', dayStart)\n .where('r.createdAt', '<', dayEnd)\n .groupBy('ma.did')\n .execute()\n\n // Inject aggregate as a synthetic row with queueId=null so resolveQueueStats can find it\n const allQueuePending: QueueCountRow[] = [\n ...queuePending,\n { queueId: null, count: aggregatePending?.count ?? '0' },\n ]\n const allQueueWindow: QueueWindowRow[] = aggregateWindow\n ? [\n ...queueWindow,\n {\n queueId: null,\n inboundCount: aggregateWindow.inboundCount,\n actionedCount: aggregateWindow.actionedCount,\n escalatedCount: aggregateWindow.escalatedCount,\n handlingTimeSum: aggregateWindow.handlingTimeSum,\n handlingTimeCount: aggregateWindow.handlingTimeCount,\n },\n ]\n : queueWindow\n\n return {\n queuePending: allQueuePending,\n queueWindow: allQueueWindow,\n typePending,\n typeWindow,\n moderator,\n }\n }\n\n /** Resolve a single group's stats from batched query results (pure in-memory). */\n private resolveGroupStats(\n group: ReportStatGroup,\n batched: BatchedStats,\n ): ReportStatistics {\n if (group.moderatorDid) {\n return this.resolveModeratorStats(group.moderatorDid, batched.moderator)\n }\n if (group.reportTypes !== null) {\n return this.resolveReportTypeStats(group.reportTypes, batched)\n }\n return this.resolveQueueStats(group.queueId, batched)\n }\n\n private resolveQueueStats(\n queueId: number | null,\n batched: BatchedStats,\n ): AggregateStatistics | QueueStatistics {\n // queueId=null is the synthetic aggregate row\n const pending = batched.queuePending.find((r) => r.queueId === queueId)\n const window = batched.queueWindow.find((r) => r.queueId === queueId)\n\n const pendingCount = num(pending?.count)\n const inboundCount = num(window?.inboundCount)\n const actionedCount = num(window?.actionedCount)\n const escalatedCount = num(window?.escalatedCount)\n const handlingTimeSum = Number(window?.handlingTimeSum ?? 0)\n const handlingTimeCount = num(window?.handlingTimeCount)\n const actionRate =\n inboundCount > 0 ? Math.round((actionedCount / inboundCount) * 100) : 0\n const avgHandlingTimeSec =\n handlingTimeCount > 0\n ? Math.round(handlingTimeSum / handlingTimeCount)\n : undefined\n\n return {\n inboundCount,\n pendingCount,\n actionedCount,\n escalatedCount,\n actionRate,\n avgHandlingTimeSec,\n }\n }\n\n private resolveReportTypeStats(\n reportTypes: string[],\n batched: BatchedStats,\n ): ReportTypeStatistics {\n const types = new Set(reportTypes)\n\n const matchingPending = batched.typePending.filter((r) =>\n types.has(r.reportType),\n )\n const matchingWindow = batched.typeWindow.filter((r) =>\n types.has(r.reportType),\n )\n\n const pendingCount = sumNum(matchingPending, 'count')\n const inboundCount = sumNum(matchingWindow, 'inboundCount')\n const actionedCount = sumNum(matchingWindow, 'actionedCount')\n const escalatedCount = sumNum(matchingWindow, 'escalatedCount')\n const handlingTimeSum = matchingWindow.reduce(\n (sum, r) => sum + Number(r.handlingTimeSum ?? 0),\n 0,\n )\n const handlingTimeCount = sumNum(matchingWindow, 'handlingTimeCount')\n\n const actionRate =\n inboundCount > 0 ? Math.round((actionedCount / inboundCount) * 100) : 0\n const avgHandlingTimeSec =\n handlingTimeCount > 0\n ? Math.round(handlingTimeSum / handlingTimeCount)\n : undefined\n\n return {\n inboundCount,\n pendingCount,\n actionedCount,\n escalatedCount,\n actionRate,\n avgHandlingTimeSec,\n }\n }\n\n private resolveModeratorStats(\n moderatorDid: string,\n rows: ModeratorWindowRow[],\n ): ModeratorStatistics {\n const row = rows.find((r) => r.did === moderatorDid)\n\n const inboundCount = num(row?.inboundCount)\n const actionedCount = num(row?.actionedCount)\n const handlingTimeCount = num(row?.handlingTimeCount)\n const avgHandlingTimeSec =\n handlingTimeCount > 0 && row?.handlingTimeSum\n ? Math.round(Number(row.handlingTimeSum) / handlingTimeCount)\n : undefined\n\n return { inboundCount, actionedCount, avgHandlingTimeSec }\n }\n\n /** Build an upsert row from (date, group, stats). */\n private buildUpsertRow(\n date: string,\n group: ReportStatGroup,\n stats: ReportStatistics,\n ): UpsertRow {\n const pendingCount =\n 'pendingCount' in stats ? stats.pendingCount ?? null : null\n const escalatedCount =\n 'escalatedCount' in stats ? stats.escalatedCount ?? null : null\n const actionRate = 'actionRate' in stats ? stats.actionRate ?? null : null\n\n return {\n date,\n queueId: group.queueId,\n moderatorDid: group.moderatorDid,\n reportTypes: group.reportTypes,\n inboundCount: stats.inboundCount ?? null,\n pendingCount,\n actionedCount: stats.actionedCount ?? null,\n escalatedCount,\n actionRate,\n avgHandlingTimeSec: stats.avgHandlingTimeSec ?? null,\n computedAt: new Date().toISOString(),\n }\n }\n\n /**\n * Wraps a DELETE+INSERT for each row in a single transaction so we pay one\n * commit per cycle instead of one per group. NULL-aware WHERE clauses match\n * the existing PG <15 NULL semantics without needing a unique index.\n */\n private async bulkUpsert(rows: UpsertRow[]): Promise<void> {\n if (!rows.length) return\n\n await this.db.transaction(async (dbTxn) => {\n for (const r of rows) {\n let del = dbTxn.db.deleteFrom('report_stat').where('date', '=', r.date)\n del =\n r.queueId !== null\n ? del.where('queueId', '=', r.queueId)\n : del.where('queueId', 'is', null)\n del =\n r.moderatorDid !== null\n ? del.where('moderatorDid', '=', r.moderatorDid)\n : del.where('moderatorDid', 'is', null)\n del =\n r.reportTypes !== null\n ? del.where(\n sql`\"reportTypes\"::jsonb = ${jsonb(r.reportTypes)}::jsonb`,\n )\n : del.where('reportTypes', 'is', null)\n await del.execute()\n\n await dbTxn.db\n .insertInto('report_stat')\n .values({\n date: r.date,\n queueId: r.queueId,\n moderatorDid: r.moderatorDid,\n reportTypes: r.reportTypes !== null ? jsonb(r.reportTypes) : null,\n inboundCount: r.inboundCount,\n pendingCount: r.pendingCount,\n actionedCount: r.actionedCount,\n escalatedCount: r.escalatedCount,\n actionRate: r.actionRate,\n avgHandlingTimeSec: r.avgHandlingTimeSec,\n computedAt: r.computedAt,\n })\n .execute()\n }\n })\n }\n\n // ─── Read methods ───\n\n /** Get a single stat row for a date + group. */\n private async getStatForDate(\n date: string,\n group: ReportStatGroup,\n ): Promise<Selectable<ReportStat> | undefined> {\n let qb = this.db.db\n .selectFrom('report_stat')\n .selectAll()\n .where('date', '=', date)\n if (group.queueId !== null) {\n qb = qb.where('queueId', '=', group.queueId)\n } else {\n qb = qb.where('queueId', 'is', null)\n }\n if (group.moderatorDid) {\n qb = qb.where('moderatorDid', '=', group.moderatorDid)\n } else {\n qb = qb.where('moderatorDid', 'is', null)\n }\n if (group.reportTypes !== null) {\n qb = qb.where(\n sql`\"reportTypes\"::jsonb = ${jsonb(group.reportTypes)}::jsonb`,\n )\n } else {\n qb = qb.where('reportTypes', 'is', null)\n }\n return qb.executeTakeFirst()\n }\n\n /** Get today's live stats for a group. */\n async getLiveStats(\n group: ReportStatGroup,\n ): Promise<Selectable<ReportStat> | undefined> {\n const today = toDateString(new Date())\n return this.getStatForDate(today, group)\n }\n\n /** Get live stats for multiple queues in a single query. */\n async getLiveStatsForQueues(\n queueIds: number[],\n ): Promise<Map<number, Selectable<ReportStat>>> {\n if (!queueIds.length) return new Map()\n\n const today = toDateString(new Date())\n const rows = await this.db.db\n .selectFrom('report_stat')\n .selectAll()\n .where('date', '=', today)\n .where('queueId', 'in', queueIds)\n .where('moderatorDid', 'is', null)\n .where('reportTypes', 'is', null)\n .execute()\n\n const result = new Map<number, Selectable<ReportStat>>()\n for (const row of rows) {\n if (row.queueId !== null) {\n result.set(row.queueId, row)\n }\n }\n return result\n }\n\n /** Get historical stats for a date range, paginated. */\n async getHistoricalStats(opts: {\n group: ReportStatGroup\n startDate?: string\n endDate?: string\n limit: number\n cursor?: string\n }): Promise<{ stats: Selectable<ReportStat>[]; cursor?: string }> {\n const { group, startDate, endDate, limit } = opts\n const { queueId, moderatorDid, reportTypes } = group\n const { ref } = this.db.db.dynamic\n\n let qb = this.db.db.selectFrom('report_stat').selectAll()\n\n if (queueId !== null) {\n qb = qb.where('queueId', '=', queueId)\n } else {\n qb = qb.where('queueId', 'is', null)\n }\n if (moderatorDid) {\n qb = qb.where('moderatorDid', '=', moderatorDid)\n } else {\n qb = qb.where('moderatorDid', 'is', null)\n }\n if (reportTypes !== null) {\n qb = qb.where(sql`\"reportTypes\"::jsonb = ${jsonb(reportTypes)}::jsonb`)\n } else {\n qb = qb.where('reportTypes', 'is', null)\n }\n if (startDate) {\n qb = qb.where('date', '>=', toDateString(new Date(startDate)))\n }\n if (endDate) {\n qb = qb.where('date', '<=', toDateString(new Date(endDate)))\n }\n\n const keyset = new ComputedAtIdKeyset(ref('computedAt'), ref('id'))\n const paginatedBuilder = paginate(qb, {\n limit,\n cursor: opts.cursor,\n keyset,\n direction: 'desc',\n tryIndex: true,\n })\n\n const stats = await paginatedBuilder.execute()\n\n return { stats, cursor: keyset.packFromResult(stats) }\n }\n}\n\n// ─── Helpers ───\n\n/** Parse a pg bigint string to number, defaulting to 0. */\nfunction num(val: string | undefined | null): number {\n return val ? Number(val) : 0\n}\n\n/** Sum a numeric string field across rows. */\nfunction sumNum<T>(rows: T[], field: keyof T): number {\n return rows.reduce((sum, r) => sum + Number(r[field] ?? 0), 0)\n}\n\n/**\n * Stable cache-key for a stat group. Used to look up an existing row in the\n * batched cache map without issuing per-group SELECTs. Report types are\n * stringified in stored order, which matches REPORT_TYPE_GROUPS.\n */\nfunction groupKey(g: ReportStatGroup): string {\n return [\n g.queueId ?? 'null',\n g.moderatorDid ?? 'null',\n g.reportTypes ? JSON.stringify(g.reportTypes) : 'null',\n ].join('|')\n}\n\n/** Convert a Date to an ISO date string (YYYY-MM-DD). */\nfunction toDateString(d: Date): string {\n return d.toISOString().slice(0, 10)\n}\n\n/** Get the next calendar date string. */\nfunction nextDate(dateStr: string): string {\n const d = new Date(`${dateStr}T00:00:00.000Z`)\n d.setUTCDate(d.getUTCDate() + 1)\n return toDateString(d)\n}\n"]}
|
|
1
|
+
{"version":3,"file":"stats.js","sourceRoot":"","sources":["../../src/report/stats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,GAAG,EAAE,MAAM,QAAQ,CAAA;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAExC,OAAO,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAElE,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAEvC;;GAEG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAA6B;IAC1D,MAAM,EAAE;QACN,wCAAwC;QACxC,6CAA6C;QAC7C,8CAA8C;QAC9C,0CAA0C;QAC1C,wCAAwC;QACxC,yCAAyC;QACzC,0CAA0C;KAC3C;IACD,MAAM,EAAE,CAAC,sCAAsC,CAAC;IAChD,QAAQ,EAAE;QACR,qDAAqD;QACrD,+CAA+C;QAC/C,sDAAsD;QACtD,gDAAgD;QAChD,qDAAqD;QACrD,wDAAwD;QACxD,mDAAmD;QACnD,6CAA6C;KAC9C;IACD,MAAM,EAAE;QACN,kDAAkD;QAClD,0CAA0C;QAC1C,gDAAgD;QAChD,8CAA8C;QAC9C,4CAA4C;QAC5C,+CAA+C;QAC/C,2CAA2C;KAC5C;IACD,cAAc,EAAE;QACd,+CAA+C;QAC/C,gDAAgD;QAChD,uDAAuD;QACvD,uDAAuD;QACvD,qDAAqD;QACrD,oDAAoD;QACpD,gDAAgD;KACjD;IACD,UAAU,EAAE;QACV,+CAA+C;QAC/C,kDAAkD;QAClD,oDAAoD;QACpD,iDAAiD;QACjD,+CAA+C;KAChD;IACD,UAAU,EAAE;QACV,6CAA6C;QAC7C,uDAAuD;QACvD,8CAA8C;QAC9C,8CAA8C;QAC9C,0DAA0D;QAC1D,wDAAwD;QACxD,+CAA+C;KAChD;IACD,iBAAiB,EAAE;QACjB,gDAAgD;QAChD,iDAAiD;QACjD,mDAAmD;QACnD,8CAA8C;QAC9C,yCAAyC;KAC1C;IACD,KAAK,EAAE;QACL,qDAAqD;QACrD,+CAA+C;QAC/C,iDAAiD;QACjD,mDAAmD;QACnD,kDAAkD;KACnD;CACF,CAAA;AAED,MAAM,oBAAoB,GAAG,EAAE,GAAG,MAAM,CAAA;AAkGxC,MAAM,OAAO,kBAAkB;IAC7B,YAAmB,EAAY;kBAAZ,EAAE;IAAa,CAAC;IAEnC,MAAM,CAAC,OAAO;QACZ,OAAO,CAAC,EAAY,EAAE,EAAE,CAAC,IAAI,kBAAkB,CAAC,EAAE,CAAC,CAAA;IACrD,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,cAAc,CAAC,IAA0B;QAC7C,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;YACxB,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,IAAI,EAAE,CAAC,CAAA;YACtC,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,CAAA;YAE1E,+BAA+B;YAC/B,MAAM,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;YAEvC,yDAAyD;YACzD,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;gBACjB,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,EAAE;qBAClC,UAAU,CAAC,aAAa,CAAC;qBACzB,MAAM,CAAC,YAAY,CAAC;qBACpB,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,SAAS,CAAC;qBAC7B,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC;qBAC7B,gBAAgB,EAAE,CAAA;gBACrB,MAAM,cAAc,GAAG,IAAI,IAAI,CAAC,GAAG,SAAS,gBAAgB,CAAC,CAAC,OAAO,EAAE,CAAA;gBACvE,IACE,CAAC,YAAY;oBACb,IAAI,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,GAAG,cAAc,EAC5D,CAAC;oBACD,MAAM,IAAI,CAAC,eAAe,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;gBACxD,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,MAAM,IAAI,CAAC,eAAe,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;YACxD,CAAC;YAED,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAA;YACnC,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,EAAE,wCAAwC,CAAC,CAAA;QACvE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,EAAE,sCAAsC,CAAC,CAAA;QACjE,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,gBAAgB,CAAC,IAItB;QACC,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QACtC,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAElC,KAAK,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;YACzE,MAAM,OAAO,GAAG,YAAY,CAAC,CAAC,CAAC,CAAA;YAC/B,IAAI,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;gBAC1B,qDAAqD;gBACrD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAA;gBACvD,MAAM,IAAI,GAAgB,EAAE,CAAA;gBAC5B,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACpC,MAAM,KAAK,GAAoB;wBAC7B,OAAO;wBACP,YAAY,EAAE,IAAI;wBAClB,WAAW,EAAE,IAAI;qBAClB,CAAA;oBACD,MAAM,KAAK,GAAG,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;oBACpD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAA;gBACvD,CAAC;gBACD,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;YAC7B,CAAC;iBAAM,CAAC;gBACN,MAAM,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;YACtD,CAAC;QACH,CAAC;IACH,CAAC;IAED,sDAAsD;IAC9C,KAAK,CAAC,eAAe,CAC3B,IAAY,EACZ,IAA0B;QAE1B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,EAAE,CAAA;QAC3C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAA;QACpD,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,IAAI,EAAE,CAAC,CAAA;QACtC,MAAM,OAAO,GAAG,IAAI,KAAK,KAAK,CAAA;QAE9B,gEAAgE;QAChE,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,KAAK;YAChC,CAAC,CAAC,MAAM,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC;YAC1C,CAAC,CAAC,IAAI,CAAA;QAER,MAAM,IAAI,GAAgB,EAAE,CAAA;QAC5B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,IAAI,CAAC;gBACH,IAAI,aAAa,EAAE,CAAC;oBAClB,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAA;oBACjD,IAAI,MAAM,EAAE,CAAC;wBACX,gEAAgE;wBAChE,IAAI,CAAC,OAAO;4BAAE,SAAQ;wBACtB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAA;wBAC9D,IAAI,GAAG,GAAG,oBAAoB;4BAAE,SAAQ;oBAC1C,CAAC;gBACH,CAAC;gBACD,MAAM,KAAK,GAAG,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;gBACpD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAA;YACpD,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,QAAQ,CAAC,KAAK,CACZ,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,EACpB,oCAAoC,CACrC,CAAA;YACH,CAAC;QACH,CAAC;QAED,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;IAC7B,CAAC;IAED,yEAAyE;IACjE,KAAK,CAAC,uBAAuB,CACnC,IAAY;QAEZ,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,EAAE;aAC9B,UAAU,CAAC,aAAa,CAAC;aACzB,SAAS,EAAE;aACX,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC;aACxB,OAAO,EAAE,CAAA;QACZ,MAAM,GAAG,GAAG,IAAI,GAAG,EAAkC,CAAA;QACrD,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;YAC3B,GAAG,CAAC,GAAG,CACL,QAAQ,CAAC;gBACP,OAAO,EAAE,GAAG,CAAC,OAAO;gBACpB,YAAY,EAAE,GAAG,CAAC,YAAY;gBAC9B,WAAW,EAAE,GAAG,CAAC,WAAW;aAC7B,CAAC,EACF,GAAG,CACJ,CAAA;QACH,CAAC;QACD,OAAO,GAAG,CAAA;IACZ,CAAC;IAED,gDAAgD;IACxC,KAAK,CAAC,eAAe;QAC3B,MAAM,MAAM,GAAsB,EAAE,CAAA;QAEpC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,EAAE;aAC5B,UAAU,CAAC,cAAc,CAAC;aAC1B,SAAS,EAAE;aACX,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC;aAC3B,KAAK,CAAC,WAAW,EAAE,IAAI,EAAE,IAAI,CAAC;aAC9B,OAAO,EAAE,CAAA;QACZ,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,EAAE;aAC7B,UAAU,CAAC,QAAQ,CAAC;aACpB,MAAM,CAAC,KAAK,CAAC;aACb,KAAK,CAAC,UAAU,EAAE,GAAG,EAAE,KAAK,CAAC;aAC7B,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE;YACnB,iCAAiC;YACjC,qCAAqC;YACrC,kCAAkC;SACnC,CAAC;aACD,OAAO,EAAE,CAAA;QAEZ,YAAY;QACZ,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAA;QACrE,YAAY;QACZ,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAA;QAC3E,CAAC;QACD,WAAW;QACX,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAA;QACnE,gBAAgB;QAChB,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,MAAM,CAAC,IAAI,CAAC;gBACV,OAAO,EAAE,IAAI;gBACb,YAAY,EAAE,MAAM,CAAC,GAAG;gBACxB,WAAW,EAAE,IAAI;aAClB,CAAC,CAAA;QACJ,CAAC;QACD,wBAAwB;QACxB,KAAK,MAAM,UAAU,IAAI,MAAM,CAAC,MAAM,CAAC,kBAAkB,CAAC,EAAE,CAAC;YAC3D,MAAM,CAAC,IAAI,CAAC;gBACV,OAAO,EAAE,IAAI;gBACb,YAAY,EAAE,IAAI;gBAClB,WAAW,EAAE,UAAU;aACxB,CAAC,CAAA;QACJ,CAAC;QAED,OAAO,MAAM,CAAA;IACf,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,mBAAmB,CAAC,IAAY;QAC5C,MAAM,QAAQ,GAAG,GAAG,IAAI,gBAAgB,CAAA;QACxC,MAAM,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,gBAAgB,CAAA;QAEhD,MAAM,CAAC,YAAY,EAAE,gBAAgB,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACzD,+EAA+E;YAC/E,IAAI,CAAC,EAAE,CAAC,EAAE;iBACP,UAAU,CAAC,QAAQ,CAAC;iBACpB,MAAM,CAAC,CAAC,SAAS,EAAE,GAAG,CAAQ,UAAU,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;iBACtD,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,QAAQ,CAAC;iBAC/B,KAAK,CAAC,SAAS,EAAE,QAAQ,EAAE,IAAI,CAAC;iBAChC,OAAO,CAAC,SAAS,CAAC;iBAClB,OAAO,EAAE;YACZ,2DAA2D;YAC3D,IAAI,CAAC,EAAE,CAAC,EAAE;iBACP,UAAU,CAAC,QAAQ,CAAC;iBACpB,MAAM,CAAC,GAAG,CAAQ,UAAU,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;iBACzC,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,QAAQ,CAAC;iBAC/B,gBAAgB,EAAE;SACtB,CAAC,CAAA;QAEF,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,EAAE;aACjC,UAAU,CAAC,QAAQ,CAAC;aACpB,MAAM,CAAC;YACN,SAAS;YACT,GAAG,CAAQ,UAAU,CAAC,EAAE,CAAC,cAAc,CAAC;YACxC,GAAG,CAAQ,gEAAgE,QAAQ,qBAAqB,MAAM,GAAG,CAAC,EAAE,CAClH,eAAe,CAChB;YACD,GAAG,CAAQ,gDAAgD,CAAC,EAAE,CAC5D,gBAAgB,CACjB;YACD,GAAG,CAAQ,4JAA4J,QAAQ,qBAAqB,MAAM,GAAG,CAAC,EAAE,CAC9M,iBAAiB,CAClB;YACD,GAAG,CAAQ,2FAA2F,QAAQ,qBAAqB,MAAM,GAAG,CAAC,EAAE,CAC7I,mBAAmB,CACpB;SACF,CAAC;aACD,KAAK,CAAC,WAAW,EAAE,IAAI,EAAE,QAAQ,CAAC;aAClC,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,MAAM,CAAC;aAC/B,KAAK,CAAC,SAAS,EAAE,QAAQ,EAAE,IAAI,CAAC;aAChC,OAAO,CAAC,SAAS,CAAC;aAClB,OAAO,EAAE,CAAA;QAEZ,4CAA4C;QAC5C,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,EAAE;aACrC,UAAU,CAAC,QAAQ,CAAC;aACpB,MAAM,CAAC;YACN,GAAG,CAAQ,UAAU,CAAC,EAAE,CAAC,cAAc,CAAC;YACxC,GAAG,CAAQ,gEAAgE,QAAQ,qBAAqB,MAAM,GAAG,CAAC,EAAE,CAClH,eAAe,CAChB;YACD,GAAG,CAAQ,gDAAgD,CAAC,EAAE,CAC5D,gBAAgB,CACjB;YACD,GAAG,CAAQ,4JAA4J,QAAQ,qBAAqB,MAAM,GAAG,CAAC,EAAE,CAC9M,iBAAiB,CAClB;YACD,GAAG,CAAQ,2FAA2F,QAAQ,qBAAqB,MAAM,GAAG,CAAC,EAAE,CAC7I,mBAAmB,CACpB;SACF,CAAC;aACD,KAAK,CAAC,WAAW,EAAE,IAAI,EAAE,QAAQ,CAAC;aAClC,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,MAAM,CAAC;aAC/B,gBAAgB,EAAE,CAAA;QAErB,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,EAAE;aACjC,UAAU,CAAC,QAAQ,CAAC;aACpB,MAAM,CAAC,CAAC,YAAY,EAAE,GAAG,CAAQ,UAAU,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;aACzD,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,QAAQ,CAAC;aAC/B,OAAO,CAAC,YAAY,CAAC;aACrB,OAAO,EAAE,CAAA;QAEZ,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,EAAE;aAChC,UAAU,CAAC,QAAQ,CAAC;aACpB,MAAM,CAAC;YACN,YAAY;YACZ,GAAG,CAAQ,UAAU,CAAC,EAAE,CAAC,cAAc,CAAC;YACxC,GAAG,CAAQ,gEAAgE,QAAQ,qBAAqB,MAAM,GAAG,CAAC,EAAE,CAClH,eAAe,CAChB;YACD,GAAG,CAAQ,gDAAgD,CAAC,EAAE,CAC5D,gBAAgB,CACjB;YACD,GAAG,CAAQ,4JAA4J,QAAQ,qBAAqB,MAAM,GAAG,CAAC,EAAE,CAC9M,iBAAiB,CAClB;YACD,GAAG,CAAQ,2FAA2F,QAAQ,qBAAqB,MAAM,GAAG,CAAC,EAAE,CAC7I,mBAAmB,CACpB;SACF,CAAC;aACD,KAAK,CAAC,WAAW,EAAE,IAAI,EAAE,QAAQ,CAAC;aAClC,KAAK,CAAC,WAAW,EAAE,GAAG,EAAE,MAAM,CAAC;aAC/B,OAAO,CAAC,YAAY,CAAC;aACrB,OAAO,EAAE,CAAA;QAEZ,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,EAAE;aAC/B,UAAU,CAAC,aAAa,CAAC;aACzB,SAAS,CAAC,4BAA4B,EAAE,CAAC,IAAI,EAAE,EAAE,CAChD,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,CAClE;aACA,MAAM,CAAC;YACN,QAAQ;YACR,GAAG,CAAQ,UAAU,CAAC,EAAE,CAAC,cAAc,CAAC;YACxC,GAAG,CAAQ,+CAA+C,CAAC,EAAE,CAC3D,eAAe,CAChB;YACD,GAAG,CAAQ,gJAAgJ,CAAC,EAAE,CAC5J,iBAAiB,CAClB;YACD,GAAG,CAAQ,4EAA4E,CAAC,EAAE,CACxF,mBAAmB,CACpB;SACF,CAAC;aACD,KAAK,CAAC,aAAa,EAAE,IAAI,EAAE,QAAQ,CAAC;aACpC,KAAK,CAAC,aAAa,EAAE,GAAG,EAAE,MAAM,CAAC;aACjC,OAAO,CAAC,QAAQ,CAAC;aACjB,OAAO,EAAE,CAAA;QAEZ,yFAAyF;QACzF,MAAM,eAAe,GAAoB;YACvC,GAAG,YAAY;YACf,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,gBAAgB,EAAE,KAAK,IAAI,GAAG,EAAE;SACzD,CAAA;QACD,MAAM,cAAc,GAAqB,eAAe;YACtD,CAAC,CAAC;gBACE,GAAG,WAAW;gBACd;oBACE,OAAO,EAAE,IAAI;oBACb,YAAY,EAAE,eAAe,CAAC,YAAY;oBAC1C,aAAa,EAAE,eAAe,CAAC,aAAa;oBAC5C,cAAc,EAAE,eAAe,CAAC,cAAc;oBAC9C,eAAe,EAAE,eAAe,CAAC,eAAe;oBAChD,iBAAiB,EAAE,eAAe,CAAC,iBAAiB;iBACrD;aACF;YACH,CAAC,CAAC,WAAW,CAAA;QAEf,OAAO;YACL,YAAY,EAAE,eAAe;YAC7B,WAAW,EAAE,cAAc;YAC3B,WAAW;YACX,UAAU;YACV,SAAS;SACV,CAAA;IACH,CAAC;IAED,kFAAkF;IAC1E,iBAAiB,CACvB,KAAsB,EACtB,OAAqB;QAErB,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;YACvB,OAAO,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,YAAY,EAAE,OAAO,CAAC,SAAS,CAAC,CAAA;QAC1E,CAAC;QACD,IAAI,KAAK,CAAC,WAAW,KAAK,IAAI,EAAE,CAAC;YAC/B,OAAO,IAAI,CAAC,sBAAsB,CAAC,KAAK,CAAC,WAAW,EAAE,OAAO,CAAC,CAAA;QAChE,CAAC;QACD,OAAO,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IACvD,CAAC;IAEO,iBAAiB,CACvB,OAAsB,EACtB,OAAqB;QAErB,8CAA8C;QAC9C,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,CAAA;QACvE,MAAM,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,CAAA;QAErE,MAAM,YAAY,GAAG,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QACxC,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;QAC9C,MAAM,aAAa,GAAG,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;QAChD,MAAM,cAAc,GAAG,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAA;QAClD,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,eAAe,IAAI,CAAC,CAAC,CAAA;QAC5D,MAAM,iBAAiB,GAAG,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAA;QACxD,MAAM,UAAU,GACd,YAAY,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,aAAa,GAAG,YAAY,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QACzE,MAAM,kBAAkB,GACtB,iBAAiB,GAAG,CAAC;YACnB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,eAAe,GAAG,iBAAiB,CAAC;YACjD,CAAC,CAAC,SAAS,CAAA;QAEf,OAAO;YACL,YAAY;YACZ,YAAY;YACZ,aAAa;YACb,cAAc;YACd,UAAU;YACV,kBAAkB;SACnB,CAAA;IACH,CAAC;IAEO,sBAAsB,CAC5B,WAAqB,EACrB,OAAqB;QAErB,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,CAAA;QAElC,MAAM,eAAe,GAAG,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CACvD,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,CACxB,CAAA;QACD,MAAM,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CACrD,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,CACxB,CAAA;QAED,MAAM,YAAY,GAAG,MAAM,CAAC,eAAe,EAAE,OAAO,CAAC,CAAA;QACrD,MAAM,YAAY,GAAG,MAAM,CAAC,cAAc,EAAE,cAAc,CAAC,CAAA;QAC3D,MAAM,aAAa,GAAG,MAAM,CAAC,cAAc,EAAE,eAAe,CAAC,CAAA;QAC7D,MAAM,cAAc,GAAG,MAAM,CAAC,cAAc,EAAE,gBAAgB,CAAC,CAAA;QAC/D,MAAM,eAAe,GAAG,cAAc,CAAC,MAAM,CAC3C,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,eAAe,IAAI,CAAC,CAAC,EAChD,CAAC,CACF,CAAA;QACD,MAAM,iBAAiB,GAAG,MAAM,CAAC,cAAc,EAAE,mBAAmB,CAAC,CAAA;QAErE,MAAM,UAAU,GACd,YAAY,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,aAAa,GAAG,YAAY,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QACzE,MAAM,kBAAkB,GACtB,iBAAiB,GAAG,CAAC;YACnB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,eAAe,GAAG,iBAAiB,CAAC;YACjD,CAAC,CAAC,SAAS,CAAA;QAEf,OAAO;YACL,YAAY;YACZ,YAAY;YACZ,aAAa;YACb,cAAc;YACd,UAAU;YACV,kBAAkB;SACnB,CAAA;IACH,CAAC;IAEO,qBAAqB,CAC3B,YAAoB,EACpB,IAA0B;QAE1B,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,YAAY,CAAC,CAAA;QAEpD,MAAM,YAAY,GAAG,GAAG,CAAC,GAAG,EAAE,YAAY,CAAC,CAAA;QAC3C,MAAM,aAAa,GAAG,GAAG,CAAC,GAAG,EAAE,aAAa,CAAC,CAAA;QAC7C,MAAM,iBAAiB,GAAG,GAAG,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAA;QACrD,MAAM,kBAAkB,GACtB,iBAAiB,GAAG,CAAC,IAAI,GAAG,EAAE,eAAe;YAC3C,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,GAAG,iBAAiB,CAAC;YAC7D,CAAC,CAAC,SAAS,CAAA;QAEf,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,kBAAkB,EAAE,CAAA;IAC5D,CAAC;IAED,qDAAqD;IAC7C,cAAc,CACpB,IAAY,EACZ,KAAsB,EACtB,KAAuB;QAEvB,MAAM,YAAY,GAChB,cAAc,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;QAC7D,MAAM,cAAc,GAClB,gBAAgB,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,cAAc,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;QACjE,MAAM,UAAU,GAAG,YAAY,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;QAE1E,OAAO;YACL,IAAI;YACJ,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,YAAY,EAAE,KAAK,CAAC,YAAY,IAAI,IAAI;YACxC,YAAY;YACZ,aAAa,EAAE,KAAK,CAAC,aAAa,IAAI,IAAI;YAC1C,cAAc;YACd,UAAU;YACV,kBAAkB,EAAE,KAAK,CAAC,kBAAkB,IAAI,IAAI;YACpD,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACrC,CAAA;IACH,CAAC;IAED;;;;OAIG;IACK,KAAK,CAAC,UAAU,CAAC,IAAiB;QACxC,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAM;QAExB,MAAM,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;YACxC,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;gBACrB,IAAI,GAAG,GAAG,KAAK,CAAC,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAA;gBACvE,GAAG;oBACD,CAAC,CAAC,OAAO,KAAK,IAAI;wBAChB,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC,OAAO,CAAC;wBACtC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;gBACtC,GAAG;oBACD,CAAC,CAAC,YAAY,KAAK,IAAI;wBACrB,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,cAAc,EAAE,GAAG,EAAE,CAAC,CAAC,YAAY,CAAC;wBAChD,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,cAAc,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;gBAC3C,GAAG;oBACD,CAAC,CAAC,WAAW,KAAK,IAAI;wBACpB,CAAC,CAAC,GAAG,CAAC,KAAK,CACP,GAAG,CAAA,0BAA0B,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,SAAS,CAC3D;wBACH,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;gBAC1C,MAAM,GAAG,CAAC,OAAO,EAAE,CAAA;gBAEnB,MAAM,KAAK,CAAC,EAAE;qBACX,UAAU,CAAC,aAAa,CAAC;qBACzB,MAAM,CAAC;oBACN,IAAI,EAAE,CAAC,CAAC,IAAI;oBACZ,OAAO,EAAE,CAAC,CAAC,OAAO;oBAClB,YAAY,EAAE,CAAC,CAAC,YAAY;oBAC5B,WAAW,EAAE,CAAC,CAAC,WAAW,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI;oBACjE,YAAY,EAAE,CAAC,CAAC,YAAY;oBAC5B,YAAY,EAAE,CAAC,CAAC,YAAY;oBAC5B,aAAa,EAAE,CAAC,CAAC,aAAa;oBAC9B,cAAc,EAAE,CAAC,CAAC,cAAc;oBAChC,UAAU,EAAE,CAAC,CAAC,UAAU;oBACxB,kBAAkB,EAAE,CAAC,CAAC,kBAAkB;oBACxC,UAAU,EAAE,CAAC,CAAC,UAAU;iBACzB,CAAC;qBACD,OAAO,EAAE,CAAA;YACd,CAAC;QACH,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,uBAAuB;IAEvB,gDAAgD;IACxC,KAAK,CAAC,cAAc,CAC1B,IAAY,EACZ,KAAsB;QAEtB,IAAI,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC,EAAE;aAChB,UAAU,CAAC,aAAa,CAAC;aACzB,SAAS,EAAE;aACX,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,CAAA;QAC3B,IAAI,KAAK,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;YAC3B,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC,CAAA;QAC9C,CAAC;aAAM,CAAC;YACN,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;QACtC,CAAC;QACD,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;YACvB,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,cAAc,EAAE,GAAG,EAAE,KAAK,CAAC,YAAY,CAAC,CAAA;QACxD,CAAC;aAAM,CAAC;YACN,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,cAAc,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;QAC3C,CAAC;QACD,IAAI,KAAK,CAAC,WAAW,KAAK,IAAI,EAAE,CAAC;YAC/B,EAAE,GAAG,EAAE,CAAC,KAAK,CACX,GAAG,CAAA,0BAA0B,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,SAAS,CAC/D,CAAA;QACH,CAAC;aAAM,CAAC;YACN,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;QAC1C,CAAC;QACD,OAAO,EAAE,CAAC,gBAAgB,EAAE,CAAA;IAC9B,CAAC;IAED,0CAA0C;IAC1C,KAAK,CAAC,YAAY,CAChB,KAAsB;QAEtB,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,IAAI,EAAE,CAAC,CAAA;QACtC,OAAO,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;IAC1C,CAAC;IAED,4DAA4D;IAC5D,KAAK,CAAC,qBAAqB,CACzB,QAAkB;QAElB,IAAI,CAAC,QAAQ,CAAC,MAAM;YAAE,OAAO,IAAI,GAAG,EAAE,CAAA;QAEtC,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,IAAI,EAAE,CAAC,CAAA;QACtC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,EAAE;aAC1B,UAAU,CAAC,aAAa,CAAC;aACzB,SAAS,EAAE;aACX,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC;aACzB,KAAK,CAAC,SAAS,EAAE,IAAI,EAAE,QAAQ,CAAC;aAChC,KAAK,CAAC,cAAc,EAAE,IAAI,EAAE,IAAI,CAAC;aACjC,KAAK,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,CAAC;aAChC,OAAO,EAAE,CAAA;QAEZ,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkC,CAAA;QACxD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,GAAG,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;gBACzB,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,CAAA;YAC9B,CAAC;QACH,CAAC;QACD,OAAO,MAAM,CAAA;IACf,CAAC;IAED,wDAAwD;IACxD,KAAK,CAAC,kBAAkB,CAAC,IAMxB;QACC,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,IAAI,CAAA;QACjD,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,GAAG,KAAK,CAAA;QACpD,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,CAAA;QAElC,IAAI,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,SAAS,EAAE,CAAA;QAEzD,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,OAAO,CAAC,CAAA;QACxC,CAAC;aAAM,CAAC;YACN,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;QACtC,CAAC;QACD,IAAI,YAAY,EAAE,CAAC;YACjB,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,cAAc,EAAE,GAAG,EAAE,YAAY,CAAC,CAAA;QAClD,CAAC;aAAM,CAAC;YACN,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,cAAc,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;QAC3C,CAAC;QACD,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YACzB,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAA,0BAA0B,KAAK,CAAC,WAAW,CAAC,SAAS,CAAC,CAAA;QACzE,CAAC;aAAM,CAAC;YACN,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,aAAa,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;QAC1C,CAAC;QACD,IAAI,SAAS,EAAE,CAAC;YACd,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,YAAY,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;QAChE,CAAC;QACD,IAAI,OAAO,EAAE,CAAC;YACZ,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,YAAY,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;QAC9D,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,kBAAkB,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAA;QACnE,MAAM,gBAAgB,GAAG,QAAQ,CAAC,EAAE,EAAE;YACpC,KAAK;YACL,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,MAAM;YACN,SAAS,EAAE,MAAM;YACjB,QAAQ,EAAE,IAAI;SACf,CAAC,CAAA;QAEF,MAAM,KAAK,GAAG,MAAM,gBAAgB,CAAC,OAAO,EAAE,CAAA;QAE9C,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,CAAA;IACxD,CAAC;CACF;AAED,kBAAkB;AAElB,2DAA2D;AAC3D,SAAS,GAAG,CAAC,GAA8B;IACzC,OAAO,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AAC9B,CAAC;AAED,8CAA8C;AAC9C,SAAS,MAAM,CAAI,IAAS,EAAE,KAAc;IAC1C,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;AAChE,CAAC;AAED;;;;GAIG;AACH,SAAS,QAAQ,CAAC,CAAkB;IAClC,OAAO;QACL,CAAC,CAAC,OAAO,IAAI,MAAM;QACnB,CAAC,CAAC,YAAY,IAAI,MAAM;QACxB,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,MAAM;KACvD,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACb,CAAC;AAED,yDAAyD;AACzD,SAAS,YAAY,CAAC,CAAO;IAC3B,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;AACrC,CAAC;AAED,yCAAyC;AACzC,SAAS,QAAQ,CAAC,OAAe;IAC/B,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,OAAO,gBAAgB,CAAC,CAAA;IAC9C,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,CAAA;IAChC,OAAO,YAAY,CAAC,CAAC,CAAC,CAAA;AACxB,CAAC","sourcesContent":["import { Selectable, sql } from 'kysely'\nimport { MINUTE } from '@atproto/common'\nimport { Database } from '../db/index.js'\nimport { ComputedAtIdKeyset, paginate } from '../db/pagination.js'\nimport { ReportStat } from '../db/schema/report_stat.js'\nimport { jsonb } from '../db/types.js'\nimport { dbLogger } from '../logger.js'\n\n/**\n * Grouped report types. Stats are computed per group rather than per individual report type.\n */\nexport const REPORT_TYPE_GROUPS: Record<string, string[]> = {\n Legacy: [\n 'com.atproto.moderation.defs#reasonSpam',\n 'com.atproto.moderation.defs#reasonViolation',\n 'com.atproto.moderation.defs#reasonMisleading',\n 'com.atproto.moderation.defs#reasonSexual',\n 'com.atproto.moderation.defs#reasonRude',\n 'com.atproto.moderation.defs#reasonOther',\n 'com.atproto.moderation.defs#reasonAppeal',\n ],\n Appeal: ['tools.ozone.report.defs#reasonAppeal'],\n Violence: [\n 'tools.ozone.report.defs#reasonViolenceAnimalWelfare',\n 'tools.ozone.report.defs#reasonViolenceThreats',\n 'tools.ozone.report.defs#reasonViolenceGraphicContent',\n 'tools.ozone.report.defs#reasonViolenceSelfHarm',\n 'tools.ozone.report.defs#reasonViolenceGlorification',\n 'tools.ozone.report.defs#reasonViolenceExtremistContent',\n 'tools.ozone.report.defs#reasonViolenceTrafficking',\n 'tools.ozone.report.defs#reasonViolenceOther',\n ],\n Sexual: [\n 'tools.ozone.report.defs#reasonSexualAbuseContent',\n 'tools.ozone.report.defs#reasonSexualNCII',\n 'tools.ozone.report.defs#reasonSexualSextortion',\n 'tools.ozone.report.defs#reasonSexualDeepfake',\n 'tools.ozone.report.defs#reasonSexualAnimal',\n 'tools.ozone.report.defs#reasonSexualUnlabeled',\n 'tools.ozone.report.defs#reasonSexualOther',\n ],\n 'Child Safety': [\n 'tools.ozone.report.defs#reasonChildSafetyCSAM',\n 'tools.ozone.report.defs#reasonChildSafetyGroom',\n 'tools.ozone.report.defs#reasonChildSafetyMinorPrivacy',\n 'tools.ozone.report.defs#reasonChildSafetyEndangerment',\n 'tools.ozone.report.defs#reasonChildSafetyHarassment',\n 'tools.ozone.report.defs#reasonChildSafetyPromotion',\n 'tools.ozone.report.defs#reasonChildSafetyOther',\n ],\n Harassment: [\n 'tools.ozone.report.defs#reasonHarassmentTroll',\n 'tools.ozone.report.defs#reasonHarassmentTargeted',\n 'tools.ozone.report.defs#reasonHarassmentHateSpeech',\n 'tools.ozone.report.defs#reasonHarassmentDoxxing',\n 'tools.ozone.report.defs#reasonHarassmentOther',\n ],\n Misleading: [\n 'tools.ozone.report.defs#reasonMisleadingBot',\n 'tools.ozone.report.defs#reasonMisleadingImpersonation',\n 'tools.ozone.report.defs#reasonMisleadingSpam',\n 'tools.ozone.report.defs#reasonMisleadingScam',\n 'tools.ozone.report.defs#reasonMisleadingSyntheticContent',\n 'tools.ozone.report.defs#reasonMisleadingMisinformation',\n 'tools.ozone.report.defs#reasonMisleadingOther',\n ],\n 'Rule Violations': [\n 'tools.ozone.report.defs#reasonRuleSiteSecurity',\n 'tools.ozone.report.defs#reasonRuleStolenContent',\n 'tools.ozone.report.defs#reasonRuleProhibitedSales',\n 'tools.ozone.report.defs#reasonRuleBanEvasion',\n 'tools.ozone.report.defs#reasonRuleOther',\n ],\n Civic: [\n 'tools.ozone.report.defs#reasonCivicElectoralProcess',\n 'tools.ozone.report.defs#reasonCivicDisclosure',\n 'tools.ozone.report.defs#reasonCivicInterference',\n 'tools.ozone.report.defs#reasonCivicMisinformation',\n 'tools.ozone.report.defs#reasonCivicImpersonation',\n ],\n}\n\nconst REPORT_STAT_LIVE_TTL = 15 * MINUTE\n\nexport type ReportStatsServiceCreator = (db: Database) => ReportStatsService\n\nexport type ReportStatGroup = {\n queueId: number | null\n moderatorDid: string | null\n reportTypes: string[] | null\n}\nexport type AggregateStatistics = {\n inboundCount: number\n pendingCount: number\n actionedCount: number\n escalatedCount: number\n actionRate: number\n avgHandlingTimeSec?: number\n}\nexport type QueueStatistics = {\n inboundCount: number\n pendingCount: number\n actionedCount: number\n escalatedCount: number\n actionRate: number\n avgHandlingTimeSec?: number\n}\nexport type ModeratorStatistics = {\n inboundCount: number\n actionedCount: number\n avgHandlingTimeSec?: number\n}\nexport type ReportTypeStatistics = {\n inboundCount: number\n pendingCount: number\n actionedCount: number\n escalatedCount: number\n actionRate: number\n avgHandlingTimeSec?: number\n}\nexport type ReportStatistics =\n | QueueStatistics\n | ModeratorStatistics\n | AggregateStatistics\n | ReportTypeStatistics\n\n// Batched query result types\ntype QueueCountRow = {\n queueId: number | null\n count: string\n}\ntype QueueWindowRow = {\n queueId: number | null\n inboundCount: string\n actionedCount: string\n escalatedCount: string\n handlingTimeSum: string | null\n handlingTimeCount: string\n}\ntype TypeCountRow = {\n reportType: string\n count: string\n}\ntype TypeWindowRow = {\n reportType: string\n inboundCount: string\n actionedCount: string\n escalatedCount: string\n handlingTimeSum: string | null\n handlingTimeCount: string\n}\ntype ModeratorWindowRow = {\n did: string\n inboundCount: string\n actionedCount: string\n handlingTimeSum: string | null\n handlingTimeCount: string\n}\ntype BatchedStats = {\n queuePending: QueueCountRow[]\n queueWindow: QueueWindowRow[]\n typePending: TypeCountRow[]\n typeWindow: TypeWindowRow[]\n moderator: ModeratorWindowRow[]\n}\n\ntype UpsertRow = {\n date: string\n queueId: number | null\n moderatorDid: string | null\n reportTypes: string[] | null\n inboundCount: number | null\n pendingCount: number | null\n actionedCount: number | null\n escalatedCount: number | null\n actionRate: number | null\n avgHandlingTimeSec: number | null\n computedAt: string\n}\n\nexport class ReportStatsService {\n constructor(public db: Database) {}\n\n static creator(): ReportStatsServiceCreator {\n return (db: Database) => new ReportStatsService(db)\n }\n\n /**\n * Compute stats for today and finalize yesterday if needed.\n * Called periodically by the StatsComputer daemon.\n */\n async materializeAll(opts?: { force?: boolean }): Promise<void> {\n try {\n const start = Date.now()\n const today = toDateString(new Date())\n const yesterday = toDateString(new Date(Date.now() - 24 * 60 * 60 * 1000))\n\n // Always compute today's stats\n await this.materializeDate(today, opts)\n\n // Finalize yesterday if its snapshot is missing or stale\n if (!opts?.force) {\n const yesterdayRow = await this.db.db\n .selectFrom('report_stat')\n .select('computedAt')\n .where('date', '=', yesterday)\n .orderBy('computedAt', 'desc')\n .executeTakeFirst()\n const endOfYesterday = new Date(`${yesterday}T23:59:59.999Z`).getTime()\n if (\n !yesterdayRow ||\n new Date(yesterdayRow.computedAt).getTime() < endOfYesterday\n ) {\n await this.materializeDate(yesterday, { force: true })\n }\n } else {\n await this.materializeDate(yesterday, { force: true })\n }\n\n const duration = Date.now() - start\n dbLogger.info({ duration }, 'report stats materialization completed')\n } catch (err) {\n dbLogger.error({ err }, 'report stats materialization errored')\n }\n }\n\n /**\n * Compute stats for a specific date range. Used by the refreshStats endpoint.\n */\n async refreshDateRange(opts: {\n startDate: string\n endDate: string\n queueIds?: number[]\n }): Promise<void> {\n const start = new Date(opts.startDate)\n const end = new Date(opts.endDate)\n\n for (let d = new Date(start); d <= end; d.setUTCDate(d.getUTCDate() + 1)) {\n const dateStr = toDateString(d)\n if (opts.queueIds?.length) {\n // Recompute only specific queue groups for this date\n const batched = await this.computeBatchedStats(dateStr)\n const rows: UpsertRow[] = []\n for (const queueId of opts.queueIds) {\n const group: ReportStatGroup = {\n queueId,\n moderatorDid: null,\n reportTypes: null,\n }\n const stats = this.resolveGroupStats(group, batched)\n rows.push(this.buildUpsertRow(dateStr, group, stats))\n }\n await this.bulkUpsert(rows)\n } else {\n await this.materializeDate(dateStr, { force: true })\n }\n }\n }\n\n /** Compute and write all groups for a single date. */\n private async materializeDate(\n date: string,\n opts?: { force?: boolean },\n ): Promise<void> {\n const groups = await this.enumerateGroups()\n const batched = await this.computeBatchedStats(date)\n const today = toDateString(new Date())\n const isToday = date === today\n\n // Batch the cache check so we don't issue one SELECT per group.\n const existingByKey = !opts?.force\n ? await this.fetchExistingStatsByKey(date)\n : null\n\n const rows: UpsertRow[] = []\n for (const group of groups) {\n try {\n if (existingByKey) {\n const cached = existingByKey.get(groupKey(group))\n if (cached) {\n // Historical dates: never recompute. Today: recompute if stale.\n if (!isToday) continue\n const age = Date.now() - new Date(cached.computedAt).getTime()\n if (age < REPORT_STAT_LIVE_TTL) continue\n }\n }\n const stats = this.resolveGroupStats(group, batched)\n rows.push(this.buildUpsertRow(date, group, stats))\n } catch (err) {\n dbLogger.error(\n { err, group, date },\n 'error preparing report stats group',\n )\n }\n }\n\n await this.bulkUpsert(rows)\n }\n\n /** Fetch all stat rows for a date, keyed by groupKey for O(1) lookup. */\n private async fetchExistingStatsByKey(\n date: string,\n ): Promise<Map<string, Selectable<ReportStat>>> {\n const existing = await this.db.db\n .selectFrom('report_stat')\n .selectAll()\n .where('date', '=', date)\n .execute()\n const map = new Map<string, Selectable<ReportStat>>()\n for (const row of existing) {\n map.set(\n groupKey({\n queueId: row.queueId,\n moderatorDid: row.moderatorDid,\n reportTypes: row.reportTypes,\n }),\n row,\n )\n }\n return map\n }\n\n /** List out the groups to compute stats for. */\n private async enumerateGroups(): Promise<ReportStatGroup[]> {\n const groups: ReportStatGroup[] = []\n\n const queues = await this.db.db\n .selectFrom('report_queue')\n .selectAll()\n .where('enabled', '=', true)\n .where('deletedAt', 'is', null)\n .execute()\n const members = await this.db.db\n .selectFrom('member')\n .select('did')\n .where('disabled', '=', false)\n .where('role', 'in', [\n 'tools.ozone.team.defs#roleAdmin',\n 'tools.ozone.team.defs#roleModerator',\n 'tools.ozone.team.defs#roleTriage',\n ])\n .execute()\n\n // aggregate\n groups.push({ queueId: null, moderatorDid: null, reportTypes: null })\n // per queue\n for (const queue of queues) {\n groups.push({ queueId: queue.id, moderatorDid: null, reportTypes: null })\n }\n // unqueued\n groups.push({ queueId: -1, moderatorDid: null, reportTypes: null })\n // per moderator\n for (const member of members) {\n groups.push({\n queueId: null,\n moderatorDid: member.did,\n reportTypes: null,\n })\n }\n // per report type group\n for (const groupTypes of Object.values(REPORT_TYPE_GROUPS)) {\n groups.push({\n queueId: null,\n moderatorDid: null,\n reportTypes: groupTypes,\n })\n }\n\n return groups\n }\n\n /**\n * Run batched GROUP BY queries for a calendar date.\n * Returns 5 result sets covering all group types.\n */\n private async computeBatchedStats(date: string): Promise<BatchedStats> {\n const dayStart = `${date}T00:00:00.000Z`\n const dayEnd = `${nextDate(date)}T00:00:00.000Z`\n\n const [queuePending, aggregatePending] = await Promise.all([\n // Pending count is a snapshot of all non-closed reports at time of computation\n this.db.db\n .selectFrom('report')\n .select(['queueId', sql<string>`count(*)`.as('count')])\n .where('status', '!=', 'closed')\n .where('queueId', 'is not', null)\n .groupBy('queueId')\n .execute(),\n // Aggregate pending (includes all reports, even un-routed)\n this.db.db\n .selectFrom('report')\n .select(sql<string>`count(*)`.as('count'))\n .where('status', '!=', 'closed')\n .executeTakeFirst(),\n ])\n\n const queueWindow = await this.db.db\n .selectFrom('report')\n .select([\n 'queueId',\n sql<string>`count(*)`.as('inboundCount'),\n sql<string>`count(*) filter (where \"status\" = 'closed' and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'actionedCount',\n ),\n sql<string>`count(*) filter (where \"status\" = 'escalated')`.as(\n 'escalatedCount',\n ),\n sql<string>`sum(extract(epoch from (\"closedAt\"::timestamp - \"createdAt\"::timestamp))) filter (where \"status\" = 'closed' and \"closedAt\" is not null and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'handlingTimeSum',\n ),\n sql<string>`count(*) filter (where \"status\" = 'closed' and \"closedAt\" is not null and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'handlingTimeCount',\n ),\n ])\n .where('createdAt', '>=', dayStart)\n .where('createdAt', '<', dayEnd)\n .where('queueId', 'is not', null)\n .groupBy('queueId')\n .execute()\n\n // Aggregate windowed (includes all reports)\n const aggregateWindow = await this.db.db\n .selectFrom('report')\n .select([\n sql<string>`count(*)`.as('inboundCount'),\n sql<string>`count(*) filter (where \"status\" = 'closed' and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'actionedCount',\n ),\n sql<string>`count(*) filter (where \"status\" = 'escalated')`.as(\n 'escalatedCount',\n ),\n sql<string>`sum(extract(epoch from (\"closedAt\"::timestamp - \"createdAt\"::timestamp))) filter (where \"status\" = 'closed' and \"closedAt\" is not null and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'handlingTimeSum',\n ),\n sql<string>`count(*) filter (where \"status\" = 'closed' and \"closedAt\" is not null and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'handlingTimeCount',\n ),\n ])\n .where('createdAt', '>=', dayStart)\n .where('createdAt', '<', dayEnd)\n .executeTakeFirst()\n\n const typePending = await this.db.db\n .selectFrom('report')\n .select(['reportType', sql<string>`count(*)`.as('count')])\n .where('status', '!=', 'closed')\n .groupBy('reportType')\n .execute()\n\n const typeWindow = await this.db.db\n .selectFrom('report')\n .select([\n 'reportType',\n sql<string>`count(*)`.as('inboundCount'),\n sql<string>`count(*) filter (where \"status\" = 'closed' and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'actionedCount',\n ),\n sql<string>`count(*) filter (where \"status\" = 'escalated')`.as(\n 'escalatedCount',\n ),\n sql<string>`sum(extract(epoch from (\"closedAt\"::timestamp - \"createdAt\"::timestamp))) filter (where \"status\" = 'closed' and \"closedAt\" is not null and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'handlingTimeSum',\n ),\n sql<string>`count(*) filter (where \"status\" = 'closed' and \"closedAt\" is not null and \"closedAt\" >= ${dayStart} and \"closedAt\" < ${dayEnd})`.as(\n 'handlingTimeCount',\n ),\n ])\n .where('createdAt', '>=', dayStart)\n .where('createdAt', '<', dayEnd)\n .groupBy('reportType')\n .execute()\n\n const moderator = await this.db.db\n .selectFrom('report as r')\n .innerJoin('moderator_assignment as ma', (join) =>\n join.onRef('ma.reportId', '=', 'r.id').on('ma.endAt', 'is', null),\n )\n .select([\n 'ma.did',\n sql<string>`count(*)`.as('inboundCount'),\n sql<string>`count(*) filter (where r.\"status\" = 'closed')`.as(\n 'actionedCount',\n ),\n sql<string>`sum(extract(epoch from (r.\"closedAt\"::timestamp - ma.\"startAt\"::timestamp))) filter (where r.\"status\" = 'closed' and r.\"closedAt\" is not null)`.as(\n 'handlingTimeSum',\n ),\n sql<string>`count(*) filter (where r.\"status\" = 'closed' and r.\"closedAt\" is not null)`.as(\n 'handlingTimeCount',\n ),\n ])\n .where('r.createdAt', '>=', dayStart)\n .where('r.createdAt', '<', dayEnd)\n .groupBy('ma.did')\n .execute()\n\n // Inject aggregate as a synthetic row with queueId=null so resolveQueueStats can find it\n const allQueuePending: QueueCountRow[] = [\n ...queuePending,\n { queueId: null, count: aggregatePending?.count ?? '0' },\n ]\n const allQueueWindow: QueueWindowRow[] = aggregateWindow\n ? [\n ...queueWindow,\n {\n queueId: null,\n inboundCount: aggregateWindow.inboundCount,\n actionedCount: aggregateWindow.actionedCount,\n escalatedCount: aggregateWindow.escalatedCount,\n handlingTimeSum: aggregateWindow.handlingTimeSum,\n handlingTimeCount: aggregateWindow.handlingTimeCount,\n },\n ]\n : queueWindow\n\n return {\n queuePending: allQueuePending,\n queueWindow: allQueueWindow,\n typePending,\n typeWindow,\n moderator,\n }\n }\n\n /** Resolve a single group's stats from batched query results (pure in-memory). */\n private resolveGroupStats(\n group: ReportStatGroup,\n batched: BatchedStats,\n ): ReportStatistics {\n if (group.moderatorDid) {\n return this.resolveModeratorStats(group.moderatorDid, batched.moderator)\n }\n if (group.reportTypes !== null) {\n return this.resolveReportTypeStats(group.reportTypes, batched)\n }\n return this.resolveQueueStats(group.queueId, batched)\n }\n\n private resolveQueueStats(\n queueId: number | null,\n batched: BatchedStats,\n ): AggregateStatistics | QueueStatistics {\n // queueId=null is the synthetic aggregate row\n const pending = batched.queuePending.find((r) => r.queueId === queueId)\n const window = batched.queueWindow.find((r) => r.queueId === queueId)\n\n const pendingCount = num(pending?.count)\n const inboundCount = num(window?.inboundCount)\n const actionedCount = num(window?.actionedCount)\n const escalatedCount = num(window?.escalatedCount)\n const handlingTimeSum = Number(window?.handlingTimeSum ?? 0)\n const handlingTimeCount = num(window?.handlingTimeCount)\n const actionRate =\n inboundCount > 0 ? Math.round((actionedCount / inboundCount) * 100) : 0\n const avgHandlingTimeSec =\n handlingTimeCount > 0\n ? Math.round(handlingTimeSum / handlingTimeCount)\n : undefined\n\n return {\n inboundCount,\n pendingCount,\n actionedCount,\n escalatedCount,\n actionRate,\n avgHandlingTimeSec,\n }\n }\n\n private resolveReportTypeStats(\n reportTypes: string[],\n batched: BatchedStats,\n ): ReportTypeStatistics {\n const types = new Set(reportTypes)\n\n const matchingPending = batched.typePending.filter((r) =>\n types.has(r.reportType),\n )\n const matchingWindow = batched.typeWindow.filter((r) =>\n types.has(r.reportType),\n )\n\n const pendingCount = sumNum(matchingPending, 'count')\n const inboundCount = sumNum(matchingWindow, 'inboundCount')\n const actionedCount = sumNum(matchingWindow, 'actionedCount')\n const escalatedCount = sumNum(matchingWindow, 'escalatedCount')\n const handlingTimeSum = matchingWindow.reduce(\n (sum, r) => sum + Number(r.handlingTimeSum ?? 0),\n 0,\n )\n const handlingTimeCount = sumNum(matchingWindow, 'handlingTimeCount')\n\n const actionRate =\n inboundCount > 0 ? Math.round((actionedCount / inboundCount) * 100) : 0\n const avgHandlingTimeSec =\n handlingTimeCount > 0\n ? Math.round(handlingTimeSum / handlingTimeCount)\n : undefined\n\n return {\n inboundCount,\n pendingCount,\n actionedCount,\n escalatedCount,\n actionRate,\n avgHandlingTimeSec,\n }\n }\n\n private resolveModeratorStats(\n moderatorDid: string,\n rows: ModeratorWindowRow[],\n ): ModeratorStatistics {\n const row = rows.find((r) => r.did === moderatorDid)\n\n const inboundCount = num(row?.inboundCount)\n const actionedCount = num(row?.actionedCount)\n const handlingTimeCount = num(row?.handlingTimeCount)\n const avgHandlingTimeSec =\n handlingTimeCount > 0 && row?.handlingTimeSum\n ? Math.round(Number(row.handlingTimeSum) / handlingTimeCount)\n : undefined\n\n return { inboundCount, actionedCount, avgHandlingTimeSec }\n }\n\n /** Build an upsert row from (date, group, stats). */\n private buildUpsertRow(\n date: string,\n group: ReportStatGroup,\n stats: ReportStatistics,\n ): UpsertRow {\n const pendingCount =\n 'pendingCount' in stats ? stats.pendingCount ?? null : null\n const escalatedCount =\n 'escalatedCount' in stats ? stats.escalatedCount ?? null : null\n const actionRate = 'actionRate' in stats ? stats.actionRate ?? null : null\n\n return {\n date,\n queueId: group.queueId,\n moderatorDid: group.moderatorDid,\n reportTypes: group.reportTypes,\n inboundCount: stats.inboundCount ?? null,\n pendingCount,\n actionedCount: stats.actionedCount ?? null,\n escalatedCount,\n actionRate,\n avgHandlingTimeSec: stats.avgHandlingTimeSec ?? null,\n computedAt: new Date().toISOString(),\n }\n }\n\n /**\n * Wraps a DELETE+INSERT for each row in a single transaction so we pay one\n * commit per cycle instead of one per group. NULL-aware WHERE clauses match\n * the existing PG <15 NULL semantics without needing a unique index.\n */\n private async bulkUpsert(rows: UpsertRow[]): Promise<void> {\n if (!rows.length) return\n\n await this.db.transaction(async (dbTxn) => {\n for (const r of rows) {\n let del = dbTxn.db.deleteFrom('report_stat').where('date', '=', r.date)\n del =\n r.queueId !== null\n ? del.where('queueId', '=', r.queueId)\n : del.where('queueId', 'is', null)\n del =\n r.moderatorDid !== null\n ? del.where('moderatorDid', '=', r.moderatorDid)\n : del.where('moderatorDid', 'is', null)\n del =\n r.reportTypes !== null\n ? del.where(\n sql`\"reportTypes\"::jsonb = ${jsonb(r.reportTypes)}::jsonb`,\n )\n : del.where('reportTypes', 'is', null)\n await del.execute()\n\n await dbTxn.db\n .insertInto('report_stat')\n .values({\n date: r.date,\n queueId: r.queueId,\n moderatorDid: r.moderatorDid,\n reportTypes: r.reportTypes !== null ? jsonb(r.reportTypes) : null,\n inboundCount: r.inboundCount,\n pendingCount: r.pendingCount,\n actionedCount: r.actionedCount,\n escalatedCount: r.escalatedCount,\n actionRate: r.actionRate,\n avgHandlingTimeSec: r.avgHandlingTimeSec,\n computedAt: r.computedAt,\n })\n .execute()\n }\n })\n }\n\n // ─── Read methods ───\n\n /** Get a single stat row for a date + group. */\n private async getStatForDate(\n date: string,\n group: ReportStatGroup,\n ): Promise<Selectable<ReportStat> | undefined> {\n let qb = this.db.db\n .selectFrom('report_stat')\n .selectAll()\n .where('date', '=', date)\n if (group.queueId !== null) {\n qb = qb.where('queueId', '=', group.queueId)\n } else {\n qb = qb.where('queueId', 'is', null)\n }\n if (group.moderatorDid) {\n qb = qb.where('moderatorDid', '=', group.moderatorDid)\n } else {\n qb = qb.where('moderatorDid', 'is', null)\n }\n if (group.reportTypes !== null) {\n qb = qb.where(\n sql`\"reportTypes\"::jsonb = ${jsonb(group.reportTypes)}::jsonb`,\n )\n } else {\n qb = qb.where('reportTypes', 'is', null)\n }\n return qb.executeTakeFirst()\n }\n\n /** Get today's live stats for a group. */\n async getLiveStats(\n group: ReportStatGroup,\n ): Promise<Selectable<ReportStat> | undefined> {\n const today = toDateString(new Date())\n return this.getStatForDate(today, group)\n }\n\n /** Get live stats for multiple queues in a single query. */\n async getLiveStatsForQueues(\n queueIds: number[],\n ): Promise<Map<number, Selectable<ReportStat>>> {\n if (!queueIds.length) return new Map()\n\n const today = toDateString(new Date())\n const rows = await this.db.db\n .selectFrom('report_stat')\n .selectAll()\n .where('date', '=', today)\n .where('queueId', 'in', queueIds)\n .where('moderatorDid', 'is', null)\n .where('reportTypes', 'is', null)\n .execute()\n\n const result = new Map<number, Selectable<ReportStat>>()\n for (const row of rows) {\n if (row.queueId !== null) {\n result.set(row.queueId, row)\n }\n }\n return result\n }\n\n /** Get historical stats for a date range, paginated. */\n async getHistoricalStats(opts: {\n group: ReportStatGroup\n startDate?: string\n endDate?: string\n limit: number\n cursor?: string\n }): Promise<{ stats: Selectable<ReportStat>[]; cursor?: string }> {\n const { group, startDate, endDate, limit } = opts\n const { queueId, moderatorDid, reportTypes } = group\n const { ref } = this.db.db.dynamic\n\n let qb = this.db.db.selectFrom('report_stat').selectAll()\n\n if (queueId !== null) {\n qb = qb.where('queueId', '=', queueId)\n } else {\n qb = qb.where('queueId', 'is', null)\n }\n if (moderatorDid) {\n qb = qb.where('moderatorDid', '=', moderatorDid)\n } else {\n qb = qb.where('moderatorDid', 'is', null)\n }\n if (reportTypes !== null) {\n qb = qb.where(sql`\"reportTypes\"::jsonb = ${jsonb(reportTypes)}::jsonb`)\n } else {\n qb = qb.where('reportTypes', 'is', null)\n }\n if (startDate) {\n qb = qb.where('date', '>=', toDateString(new Date(startDate)))\n }\n if (endDate) {\n qb = qb.where('date', '<=', toDateString(new Date(endDate)))\n }\n\n const keyset = new ComputedAtIdKeyset(ref('computedAt'), ref('id'))\n const paginatedBuilder = paginate(qb, {\n limit,\n cursor: opts.cursor,\n keyset,\n direction: 'desc',\n tryIndex: true,\n })\n\n const stats = await paginatedBuilder.execute()\n\n return { stats, cursor: keyset.packFromResult(stats) }\n }\n}\n\n// ─── Helpers ───\n\n/** Parse a pg bigint string to number, defaulting to 0. */\nfunction num(val: string | undefined | null): number {\n return val ? Number(val) : 0\n}\n\n/** Sum a numeric string field across rows. */\nfunction sumNum<T>(rows: T[], field: keyof T): number {\n return rows.reduce((sum, r) => sum + Number(r[field] ?? 0), 0)\n}\n\n/**\n * Stable cache-key for a stat group. Used to look up an existing row in the\n * batched cache map without issuing per-group SELECTs. Report types are\n * stringified in stored order, which matches REPORT_TYPE_GROUPS.\n */\nfunction groupKey(g: ReportStatGroup): string {\n return [\n g.queueId ?? 'null',\n g.moderatorDid ?? 'null',\n g.reportTypes ? JSON.stringify(g.reportTypes) : 'null',\n ].join('|')\n}\n\n/** Convert a Date to an ISO date string (YYYY-MM-DD). */\nfunction toDateString(d: Date): string {\n return d.toISOString().slice(0, 10)\n}\n\n/** Get the next calendar date string. */\nfunction nextDate(dateStr: string): string {\n const d = new Date(`${dateStr}T00:00:00.000Z`)\n d.setUTCDate(d.getUTCDate() + 1)\n return toDateString(d)\n}\n"]}
|
package/dist/report/views.d.ts
CHANGED
|
@@ -35,7 +35,6 @@ export declare function buildReportView(report: ReportWithEvent, hydrated: Hydra
|
|
|
35
35
|
repo: ToolsOzoneModerationDefs.RepoViewDetail | undefined;
|
|
36
36
|
record: RecordViewDetail | undefined;
|
|
37
37
|
profile: {
|
|
38
|
-
$type: "app.bsky.actor.defs#profileViewDetailed";
|
|
39
38
|
did: string;
|
|
40
39
|
handle: string;
|
|
41
40
|
displayName?: string;
|
|
@@ -57,6 +56,7 @@ export declare function buildReportView(report: ReportWithEvent, hydrated: Hydra
|
|
|
57
56
|
verification?: AppBskyActorDefs.VerificationState;
|
|
58
57
|
status?: AppBskyActorDefs.StatusView;
|
|
59
58
|
debug?: { [_ in string]: unknown; };
|
|
59
|
+
$type: 'app.bsky.actor.defs#profileViewDetailed';
|
|
60
60
|
} | undefined;
|
|
61
61
|
status: ToolsOzoneModerationDefs.SubjectStatusView | undefined;
|
|
62
62
|
};
|
|
@@ -67,7 +67,6 @@ export declare function buildReportView(report: ReportWithEvent, hydrated: Hydra
|
|
|
67
67
|
subject: string;
|
|
68
68
|
repo: ToolsOzoneModerationDefs.RepoViewDetail | undefined;
|
|
69
69
|
profile: {
|
|
70
|
-
$type: "app.bsky.actor.defs#profileViewDetailed";
|
|
71
70
|
did: string;
|
|
72
71
|
handle: string;
|
|
73
72
|
displayName?: string;
|
|
@@ -89,6 +88,7 @@ export declare function buildReportView(report: ReportWithEvent, hydrated: Hydra
|
|
|
89
88
|
verification?: AppBskyActorDefs.VerificationState;
|
|
90
89
|
status?: AppBskyActorDefs.StatusView;
|
|
91
90
|
debug?: { [_ in string]: unknown; };
|
|
91
|
+
$type: 'app.bsky.actor.defs#profileViewDetailed';
|
|
92
92
|
} | undefined;
|
|
93
93
|
status: ToolsOzoneModerationDefs.SubjectStatusView | undefined;
|
|
94
94
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"views.d.ts","sourceRoot":"","sources":["../../src/report/views.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAEnC,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAA;AACxD,OAAO,KAAK,gBAAgB,MAAM,yCAAyC,CAAA;AAC3E,OAAO,EAAE,WAAW,EAAE,MAAM,4CAA4C,CAAA;AACxE,OAAO,EACL,gBAAgB,EAChB,QAAQ,EACT,MAAM,iDAAiD,CAAA;AACxD,OAAO,KAAK,wBAAwB,MAAM,iDAAiD,CAAA;AAC3F,OAAO,KAAK,mBAAmB,MAAM,4CAA4C,CAAA;AACjF,OAAO,KAAK,oBAAoB,MAAM,6CAA6C,CAAA;AACnF,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,2CAA2C,CAAA;AAChF,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAE3C,KAAK,WAAW,GAAG;IACjB,WAAW,CACT,IAAI,EAAE,MAAM,EAAE,EACd,QAAQ,CAAC,EAAE,cAAc,GACxB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAA;IACjC,aAAa,CACX,QAAQ,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE,EAC3B,QAAQ,CAAC,EAAE,cAAc,GACxB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAA;IACzC,WAAW,CACT,IAAI,EAAE,MAAM,EAAE,GACb,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,mBAAmB,CAAC,CAAC,CAAA;CAC9D,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;IACnC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC,CAAA;IAC5C,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;IACzC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,mBAAmB,CAAC,CAAA;IAC3D,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,SAAS,CAAC,CAAA;IAClD,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;CACrC,CAAA;AAED,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,eAAe,EAAE,EAC1B,KAAK,EAAE,WAAW,EAClB,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC,CAAC,EAC7E,SAAS,EAAE,CACT,QAAQ,EAAE,MAAM,EAAE,KACf,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,SAAS,CAAC,CAAC,EACxD,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,EACpE,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC,cAAc,CAAC,CAuCzB;AAED,wBAAgB,eAAe,CAC7B,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,cAAc,EACxB,WAAW,EAAE,OAAO,EACpB,OAAO,CAAC,EAAE,wBAAwB,CAAC,YAAY,EAAE
|
|
1
|
+
{"version":3,"file":"views.d.ts","sourceRoot":"","sources":["../../src/report/views.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAEnC,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAA;AACxD,OAAO,KAAK,gBAAgB,MAAM,yCAAyC,CAAA;AAC3E,OAAO,EAAE,WAAW,EAAE,MAAM,4CAA4C,CAAA;AACxE,OAAO,EACL,gBAAgB,EAChB,QAAQ,EACT,MAAM,iDAAiD,CAAA;AACxD,OAAO,KAAK,wBAAwB,MAAM,iDAAiD,CAAA;AAC3F,OAAO,KAAK,mBAAmB,MAAM,4CAA4C,CAAA;AACjF,OAAO,KAAK,oBAAoB,MAAM,6CAA6C,CAAA;AACnF,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,2CAA2C,CAAA;AAChF,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAE3C,KAAK,WAAW,GAAG;IACjB,WAAW,CACT,IAAI,EAAE,MAAM,EAAE,EACd,QAAQ,CAAC,EAAE,cAAc,GACxB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAA;IACjC,aAAa,CACX,QAAQ,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE,EAC3B,QAAQ,CAAC,EAAE,cAAc,GACxB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAA;IACzC,WAAW,CACT,IAAI,EAAE,MAAM,EAAE,GACb,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,mBAAmB,CAAC,CAAC,CAAA;CAC9D,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;IACnC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC,CAAA;IAC5C,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;IACzC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,mBAAmB,CAAC,CAAA;IAC3D,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,SAAS,CAAC,CAAA;IAClD,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;CACrC,CAAA;AAED,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,eAAe,EAAE,EAC1B,KAAK,EAAE,WAAW,EAClB,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC,CAAC,EAC7E,SAAS,EAAE,CACT,QAAQ,EAAE,MAAM,EAAE,KACf,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,SAAS,CAAC,CAAC,EACxD,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,EACpE,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC,cAAc,CAAC,CAuCzB;AAED,wBAAgB,eAAe,CAC7B,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,cAAc,EACxB,WAAW,EAAE,OAAO,EACpB,OAAO,CAAC,EAAE,wBAAwB,CAAC,YAAY,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBAoClC,yCAAyC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBAyBzC,yCAAyC;;;;;;;;;;;;;;;;;;EAyCzD;AAED,wBAAgB,cAAc,CAC5B,GAAG,CAAC,EAAE,UAAU,CAAC,UAAU,CAAC,GAC3B,mBAAmB,CAAC,UAAU,CAUhC;AAED,wBAAgB,aAAa,CAC3B,GAAG,CAAC,EAAE,UAAU,CAAC,UAAU,CAAC,GAC3B,oBAAoB,CAAC,SAAS,CAUhC;AAED,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,UAAU,CAAC,UAAU,CAAC,GAC1B,oBAAoB,CAAC,eAAe,CAWtC"}
|
|
@@ -32,15 +32,15 @@ export declare class SafelinkRuleService {
|
|
|
32
32
|
comment?: string;
|
|
33
33
|
}): Promise<Selectable<SafelinkEvent>>;
|
|
34
34
|
getActiveRule(url: string, pattern: SafelinkPatternType): Promise<{
|
|
35
|
-
id: number;
|
|
36
35
|
action: SafelinkActionType;
|
|
37
36
|
comment: string | null;
|
|
38
37
|
createdAt: string;
|
|
39
38
|
createdBy: string;
|
|
39
|
+
id: number;
|
|
40
|
+
pattern: SafelinkPatternType;
|
|
41
|
+
reason: SafelinkReasonType;
|
|
40
42
|
updatedAt: string;
|
|
41
43
|
url: string;
|
|
42
|
-
reason: SafelinkReasonType;
|
|
43
|
-
pattern: SafelinkPatternType;
|
|
44
44
|
} | null>;
|
|
45
45
|
getActiveRules({ cursor, limit, urls, patternType, actions, reason, createdBy, direction, }?: {
|
|
46
46
|
cursor?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/safelink/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AACnC,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA;AAErD,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EACnB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAEtE,MAAM,MAAM,0BAA0B,GAAG,CAAC,EAAE,EAAE,QAAQ,KAAK,mBAAmB,CAAA;AAE9E,qBAAa,mBAAmB;IACX,EAAE,EAAE,QAAQ;
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/safelink/service.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/safelink/service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Selectable } from 'kysely'\nimport { ToolsOzoneSafelinkDefs } from '@atproto/api'\nimport { InvalidRequestError } from '@atproto/xrpc-server'\nimport {\n SafelinkActionType,\n SafelinkPatternType,\n SafelinkReasonType,\n} from '../api/util.js'\nimport { Database } from '../db/index.js'\nimport { SafelinkEvent, SafelinkRule } from '../db/schema/safelink.js'\n\nexport type SafelinkRuleServiceCreator = (db: Database) => SafelinkRuleService\n\nexport class SafelinkRuleService {\n constructor(public db: Database) {}\n\n static creator() {\n return (db: Database) => new SafelinkRuleService(db)\n }\n\n formatEvent(event: Selectable<SafelinkEvent>): ToolsOzoneSafelinkDefs.Event {\n return {\n id: event.id,\n eventType: event.eventType,\n url: event.url,\n pattern: event.pattern,\n action: event.action,\n reason: event.reason,\n createdBy: event.createdBy,\n createdAt: new Date(event.createdAt).toISOString(),\n comment: event.comment || undefined,\n }\n }\n\n async addRule({\n url,\n pattern,\n action,\n reason,\n createdBy,\n comment,\n }: {\n url: string\n pattern: SafelinkPatternType\n action: SafelinkActionType\n reason: SafelinkReasonType\n createdBy: string\n comment?: string\n }): Promise<Selectable<SafelinkEvent>> {\n const existingRule = await this.getActiveRule(url, pattern)\n if (existingRule) {\n throw new InvalidRequestError(\n 'A rule for this URL/domain already exists',\n 'RuleAlreadyExists',\n )\n }\n\n const now = new Date().toISOString()\n const rule = {\n url,\n pattern,\n action,\n reason,\n createdBy,\n comment: comment || null,\n createdAt: now,\n }\n\n return await this.db.transaction(async (txn) => {\n const event = await txn.db\n .insertInto('safelink_event')\n .values({\n eventType: 'addRule',\n ...rule,\n })\n .returningAll()\n .executeTakeFirstOrThrow()\n\n await txn.db\n .insertInto('safelink_rule')\n .values({ ...rule, updatedAt: now })\n .execute()\n\n return event\n })\n }\n\n async updateRule({\n url,\n pattern,\n action,\n reason,\n createdBy,\n comment,\n }: {\n url: string\n pattern: SafelinkPatternType\n action: SafelinkActionType\n reason: SafelinkReasonType\n createdBy: string\n comment?: string\n }): Promise<Selectable<SafelinkEvent>> {\n const existingRule = await this.getActiveRule(url, pattern)\n if (!existingRule) {\n throw new InvalidRequestError(\n 'No active rule found for this URL/domain',\n 'RuleNotFound',\n )\n }\n\n const now = new Date().toISOString()\n const rule = {\n action,\n reason,\n createdBy,\n comment: comment || null,\n }\n\n return await this.db.transaction(async (txn) => {\n const event = await txn.db\n .insertInto('safelink_event')\n .values({\n createdAt: now,\n url: existingRule.url,\n pattern: existingRule.pattern,\n eventType: 'updateRule',\n ...rule,\n })\n .returningAll()\n .executeTakeFirstOrThrow()\n\n await txn.db\n .updateTable('safelink_rule')\n .set(rule)\n .where('url', '=', existingRule.url)\n .where('pattern', '=', existingRule.pattern)\n .execute()\n\n return event\n })\n }\n\n async removeRule({\n url,\n pattern,\n createdBy,\n comment,\n }: {\n url: string\n pattern: SafelinkPatternType\n createdBy: string\n comment?: string\n }): Promise<Selectable<SafelinkEvent>> {\n const existingRule = await this.getActiveRule(url, pattern)\n if (!existingRule) {\n throw new InvalidRequestError(\n 'No active rule found for this URL/domain',\n 'RuleNotFound',\n )\n }\n\n return await this.db.transaction(async (txn) => {\n const event = await txn.db\n .insertInto('safelink_event')\n .values({\n eventType: 'removeRule',\n url,\n pattern,\n action: existingRule.action,\n reason: existingRule.reason,\n createdBy,\n comment: comment || null,\n createdAt: new Date().toISOString(),\n })\n .returningAll()\n .executeTakeFirstOrThrow()\n\n await txn.db\n .deleteFrom('safelink_rule')\n .where('url', '=', url)\n .where('pattern', '=', pattern)\n .execute()\n\n return event\n })\n }\n\n async getActiveRule(url: string, pattern: SafelinkPatternType) {\n const rule = await this.db.db\n .selectFrom('safelink_rule')\n .selectAll()\n .where('url', '=', url)\n .where('pattern', '=', pattern)\n .executeTakeFirst()\n\n if (!rule) {\n return null\n }\n\n return rule\n }\n\n async getActiveRules({\n cursor,\n limit = 50,\n urls,\n patternType,\n actions,\n reason,\n createdBy,\n direction = 'desc',\n }: {\n cursor?: string\n limit?: number\n urls?: string[]\n patternType?: SafelinkPatternType\n actions?: SafelinkActionType[]\n reason?: SafelinkReasonType\n createdBy?: string\n direction?: 'asc' | 'desc'\n } = {}): Promise<{\n rules: Selectable<SafelinkRule>[]\n cursor?: string\n }> {\n let query = this.db.db.selectFrom('safelink_rule').selectAll()\n\n if (urls && urls.length > 0) {\n query = query.where('url', 'in', urls)\n }\n\n if (patternType) {\n query = query.where('pattern', '=', patternType)\n }\n\n if (actions && actions.length > 0) {\n query = query.where('action', 'in', actions)\n }\n\n if (reason) {\n query = query.where('reason', '=', reason)\n }\n\n if (createdBy) {\n query = query.where('createdBy', '=', createdBy)\n }\n\n if (cursor) {\n query = query.where(\n 'id',\n direction === 'asc' ? '>' : '<',\n parseInt(cursor, 10),\n )\n }\n\n const rules = await query.orderBy('id', direction).limit(limit).execute()\n\n return {\n rules,\n cursor: rules.at(-1)?.id?.toString(),\n }\n }\n\n async queryEvents({\n cursor,\n limit = 50,\n urls,\n patternType,\n direction = 'desc',\n }: {\n cursor?: string\n limit?: number\n urls?: string[]\n patternType?: SafelinkPatternType\n direction?: 'asc' | 'desc'\n } = {}): Promise<{\n events: Selectable<SafelinkEvent>[]\n cursor?: string\n }> {\n let query = this.db.db.selectFrom('safelink_event').selectAll()\n\n if (urls && urls.length > 0) {\n query = query.where('url', 'in', urls)\n }\n\n if (patternType) {\n query = query.where('pattern', '=', patternType)\n }\n\n if (cursor) {\n query = query.where(\n 'id',\n direction === 'asc' ? '>' : '<',\n parseInt(cursor, 10),\n )\n }\n\n const events = await query.orderBy('id', direction).limit(limit).execute()\n\n return {\n events,\n cursor: events.at(-1)?.id?.toString(),\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/safelink/service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Selectable } from 'kysely'\nimport { ToolsOzoneSafelinkDefs } from '@atproto/api'\nimport { InvalidRequestError } from '@atproto/xrpc-server'\nimport {\n SafelinkActionType,\n SafelinkPatternType,\n SafelinkReasonType,\n} from '../api/util.js'\nimport { Database } from '../db/index.js'\nimport { SafelinkEvent, SafelinkRule } from '../db/schema/safelink.js'\n\nexport type SafelinkRuleServiceCreator = (db: Database) => SafelinkRuleService\n\nexport class SafelinkRuleService {\n constructor(public db: Database) {}\n\n static creator() {\n return (db: Database) => new SafelinkRuleService(db)\n }\n\n formatEvent(event: Selectable<SafelinkEvent>): ToolsOzoneSafelinkDefs.Event {\n return {\n id: event.id,\n eventType: event.eventType,\n url: event.url,\n pattern: event.pattern,\n action: event.action,\n reason: event.reason,\n createdBy: event.createdBy,\n createdAt: new Date(event.createdAt).toISOString(),\n comment: event.comment || undefined,\n }\n }\n\n async addRule({\n url,\n pattern,\n action,\n reason,\n createdBy,\n comment,\n }: {\n url: string\n pattern: SafelinkPatternType\n action: SafelinkActionType\n reason: SafelinkReasonType\n createdBy: string\n comment?: string\n }): Promise<Selectable<SafelinkEvent>> {\n const existingRule = await this.getActiveRule(url, pattern)\n if (existingRule) {\n throw new InvalidRequestError(\n 'A rule for this URL/domain already exists',\n 'RuleAlreadyExists',\n )\n }\n\n const now = new Date().toISOString()\n const rule = {\n url,\n pattern,\n action,\n reason,\n createdBy,\n comment: comment || null,\n createdAt: now,\n }\n\n return await this.db.transaction(async (txn) => {\n const event = await txn.db\n .insertInto('safelink_event')\n .values({\n eventType: 'addRule',\n ...rule,\n })\n .returningAll()\n .executeTakeFirstOrThrow()\n\n await txn.db\n .insertInto('safelink_rule')\n .values({ ...rule, updatedAt: now })\n .execute()\n\n return event\n })\n }\n\n async updateRule({\n url,\n pattern,\n action,\n reason,\n createdBy,\n comment,\n }: {\n url: string\n pattern: SafelinkPatternType\n action: SafelinkActionType\n reason: SafelinkReasonType\n createdBy: string\n comment?: string\n }): Promise<Selectable<SafelinkEvent>> {\n const existingRule = await this.getActiveRule(url, pattern)\n if (!existingRule) {\n throw new InvalidRequestError(\n 'No active rule found for this URL/domain',\n 'RuleNotFound',\n )\n }\n\n const now = new Date().toISOString()\n const rule = {\n action,\n reason,\n createdBy,\n comment: comment || null,\n }\n\n return await this.db.transaction(async (txn) => {\n const event = await txn.db\n .insertInto('safelink_event')\n .values({\n createdAt: now,\n url: existingRule.url,\n pattern: existingRule.pattern,\n eventType: 'updateRule',\n ...rule,\n })\n .returningAll()\n .executeTakeFirstOrThrow()\n\n await txn.db\n .updateTable('safelink_rule')\n .set(rule)\n .where('url', '=', existingRule.url)\n .where('pattern', '=', existingRule.pattern)\n .execute()\n\n return event\n })\n }\n\n async removeRule({\n url,\n pattern,\n createdBy,\n comment,\n }: {\n url: string\n pattern: SafelinkPatternType\n createdBy: string\n comment?: string\n }): Promise<Selectable<SafelinkEvent>> {\n const existingRule = await this.getActiveRule(url, pattern)\n if (!existingRule) {\n throw new InvalidRequestError(\n 'No active rule found for this URL/domain',\n 'RuleNotFound',\n )\n }\n\n return await this.db.transaction(async (txn) => {\n const event = await txn.db\n .insertInto('safelink_event')\n .values({\n eventType: 'removeRule',\n url,\n pattern,\n action: existingRule.action,\n reason: existingRule.reason,\n createdBy,\n comment: comment || null,\n createdAt: new Date().toISOString(),\n })\n .returningAll()\n .executeTakeFirstOrThrow()\n\n await txn.db\n .deleteFrom('safelink_rule')\n .where('url', '=', url)\n .where('pattern', '=', pattern)\n .execute()\n\n return event\n })\n }\n\n async getActiveRule(url: string, pattern: SafelinkPatternType) {\n const rule = await this.db.db\n .selectFrom('safelink_rule')\n .selectAll()\n .where('url', '=', url)\n .where('pattern', '=', pattern)\n .executeTakeFirst()\n\n if (!rule) {\n return null\n }\n\n return rule\n }\n\n async getActiveRules({\n cursor,\n limit = 50,\n urls,\n patternType,\n actions,\n reason,\n createdBy,\n direction = 'desc',\n }: {\n cursor?: string\n limit?: number\n urls?: string[]\n patternType?: SafelinkPatternType\n actions?: SafelinkActionType[]\n reason?: SafelinkReasonType\n createdBy?: string\n direction?: 'asc' | 'desc'\n } = {}): Promise<{\n rules: Selectable<SafelinkRule>[]\n cursor?: string\n }> {\n let query = this.db.db.selectFrom('safelink_rule').selectAll()\n\n if (urls && urls.length > 0) {\n query = query.where('url', 'in', urls)\n }\n\n if (patternType) {\n query = query.where('pattern', '=', patternType)\n }\n\n if (actions && actions.length > 0) {\n query = query.where('action', 'in', actions)\n }\n\n if (reason) {\n query = query.where('reason', '=', reason)\n }\n\n if (createdBy) {\n query = query.where('createdBy', '=', createdBy)\n }\n\n if (cursor) {\n query = query.where(\n 'id',\n direction === 'asc' ? '>' : '<',\n parseInt(cursor, 10),\n )\n }\n\n const rules = await query.orderBy('id', direction).limit(limit).execute()\n\n return {\n rules,\n cursor: rules.at(-1)?.id?.toString(),\n }\n }\n\n async queryEvents({\n cursor,\n limit = 50,\n urls,\n patternType,\n direction = 'desc',\n }: {\n cursor?: string\n limit?: number\n urls?: string[]\n patternType?: SafelinkPatternType\n direction?: 'asc' | 'desc'\n } = {}): Promise<{\n events: Selectable<SafelinkEvent>[]\n cursor?: string\n }> {\n let query = this.db.db.selectFrom('safelink_event').selectAll()\n\n if (urls && urls.length > 0) {\n query = query.where('url', 'in', urls)\n }\n\n if (patternType) {\n query = query.where('pattern', '=', patternType)\n }\n\n if (cursor) {\n query = query.where(\n 'id',\n direction === 'asc' ? '>' : '<',\n parseInt(cursor, 10),\n )\n }\n\n const events = await query.orderBy('id', direction).limit(limit).execute()\n\n return {\n events,\n cursor: events.at(-1)?.id?.toString(),\n }\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/scheduled-action/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAEnC,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AAC3E,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAA;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,iDAAiD,CAAA;AAErF,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAE7C,MAAM,MAAM,6BAA6B,GAAG,CAC1C,EAAE,EAAE,QAAQ,KACT,sBAAsB,CAAA;AAE3B,qBAAa,sBAAsB;IACd,EAAE,EAAE,QAAQ;
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/scheduled-action/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAEnC,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AAC3E,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAA;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,iDAAiD,CAAA;AAErF,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAE7C,MAAM,MAAM,6BAA6B,GAAG,CAC1C,EAAE,EAAE,QAAQ,KACT,sBAAsB,CAAA;AAE3B,qBAAa,sBAAsB;IACd,EAAE,EAAE,QAAQ;IAA/B,YAAmB,EAAE,EAAE,QAAQ,EAAI;IAEnC,MAAM,CAAC,OAAO,SACA,QAAQ,4BACrB;IAED,qBAAqB,CACnB,MAAM,EAAE,UAAU,CAAC,eAAe,CAAC,GAClC,mBAAmB,CA0BrB;IAEK,cAAc,CAClB,gBAAgB,EAAE,gBAAgB,GACjC,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC,CAsDtC;IAEK,0BAA0B,CAC9B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,GAAG,IAAI,CAAC,CAU7C;IAEK,oBAAoB,CAAC,EACzB,MAAM,EACN,KAAU,EACV,SAAS,EACT,OAAO,EACP,QAAQ,EACR,QAAa,EACb,SAAkB,GACnB,EAAE;QACD,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,SAAS,CAAC,EAAE,IAAI,CAAA;QAChB,OAAO,CAAC,EAAE,IAAI,CAAA;QACd,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;QACnB,QAAQ,EAAE,qBAAqB,EAAE,CAAA;QACjC,SAAS,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;KAC3B,GAAG,OAAO,CAAC;QACV,OAAO,EAAE,UAAU,CAAC,eAAe,CAAC,EAAE,CAAA;QACtC,MAAM,CAAC,EAAE,MAAM,CAAA;KAChB,CAAC,CA6CD;IAEK,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QACxD,SAAS,EAAE,MAAM,EAAE,CAAA;QACnB,MAAM,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,SAAS,CAAC,EAAE,MAAM,CAAA;SAAE,EAAE,CAAA;KAC7D,CAAC,CAoCD;IAEK,0BAA0B,CAC9B,GAAG,EAAE,IAAI,GACR,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC,CAWxC;IAEK,oBAAoB,CACxB,QAAQ,EAAE,MAAM,EAChB,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,IAAI,CAAC,CAYf;IAEK,kBAAkB,CACtB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC,CAYf;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/scheduled-action/service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Selectable } from 'kysely'\nimport { InvalidRequestError } from '@atproto/xrpc-server'\nimport { ScheduledActionStatus, ScheduledActionType } from '../api/util.js'\nimport { Database } from '../db/index.js'\nimport { ScheduledAction } from '../db/schema/scheduled-action.js'\nimport { ScheduledActionView } from '../lexicon/types/tools/ozone/moderation/defs.js'\nimport { dbLogger } from '../logger.js'\nimport { SchedulingParams } from './types.js'\n\nexport type ScheduledActionServiceCreator = (\n db: Database,\n) => ScheduledActionService\n\nexport class ScheduledActionService {\n constructor(public db: Database) {}\n\n static creator() {\n return (db: Database) => new ScheduledActionService(db)\n }\n\n formatScheduledAction(\n action: Selectable<ScheduledAction>,\n ): ScheduledActionView {\n return {\n id: action.id,\n action: action.action,\n eventData: action.eventData as { [x: string]: unknown } | undefined,\n did: action.did,\n executeAt: action.executeAt\n ? new Date(action.executeAt).toISOString()\n : undefined,\n executeAfter: action.executeAfter\n ? new Date(action.executeAfter).toISOString()\n : undefined,\n executeUntil: action.executeUntil\n ? new Date(action.executeUntil).toISOString()\n : undefined,\n randomizeExecution: action.randomizeExecution,\n createdBy: action.createdBy,\n createdAt: new Date(action.createdAt).toISOString(),\n updatedAt: new Date(action.updatedAt).toISOString(),\n status: action.status,\n lastExecutedAt: action.lastExecutedAt\n ? new Date(action.lastExecutedAt).toISOString()\n : undefined,\n lastFailureReason: action.lastFailureReason || undefined,\n executionEventId: action.executionEventId || undefined,\n }\n }\n\n async scheduleAction(\n schedulingParams: SchedulingParams,\n ): Promise<Selectable<ScheduledAction>> {\n const { action, eventData, did, createdBy } = schedulingParams\n\n // Only allow one pending action at a time for a given subject and action type\n const existingAction = await this.getPendingActionForSubject(did, action)\n if (existingAction) {\n throw new InvalidRequestError(\n 'A pending scheduled action already exists for this subject',\n 'ActionAlreadyExists',\n )\n }\n\n // When a time-range for action is specified, ensure that the range is valid\n if (\n 'executeAfter' in schedulingParams &&\n schedulingParams.executeAfter &&\n schedulingParams.executeUntil &&\n schedulingParams.executeAfter >= schedulingParams.executeUntil\n ) {\n throw new InvalidRequestError(\n 'executeAfter must be before executeUntil',\n 'InvalidScheduling',\n )\n }\n\n const now = new Date().toISOString()\n const randomizeExecution =\n !('executeAt' in schedulingParams) && 'executeAfter' in schedulingParams\n\n const scheduledAction = await this.db.db\n .insertInto('scheduled_action')\n .values({\n action,\n eventData: JSON.stringify(eventData),\n did,\n executeAt: randomizeExecution\n ? null\n : schedulingParams.executeAt?.toISOString(),\n executeAfter: randomizeExecution\n ? schedulingParams.executeAfter?.toISOString()\n : null,\n executeUntil: randomizeExecution\n ? schedulingParams.executeUntil?.toISOString()\n : null,\n randomizeExecution,\n createdBy,\n createdAt: now,\n updatedAt: now,\n status: 'pending',\n })\n .returningAll()\n .executeTakeFirstOrThrow()\n\n return scheduledAction\n }\n\n async getPendingActionForSubject(\n did: string,\n action: ScheduledActionType,\n ): Promise<Selectable<ScheduledAction> | null> {\n const scheduledAction = await this.db.db\n .selectFrom('scheduled_action')\n .selectAll()\n .where('did', '=', did)\n .where('action', '=', action)\n .where('status', '=', 'pending')\n .executeTakeFirst()\n\n return scheduledAction || null\n }\n\n async listScheduledActions({\n cursor,\n limit = 50,\n startTime,\n endTime,\n subjects,\n statuses = [],\n direction = 'desc',\n }: {\n cursor?: string\n limit?: number\n startTime?: Date\n endTime?: Date\n subjects?: string[]\n statuses: ScheduledActionStatus[]\n direction?: 'asc' | 'desc'\n }): Promise<{\n actions: Selectable<ScheduledAction>[]\n cursor?: string\n }> {\n let query = this.db.db\n .selectFrom('scheduled_action')\n .where('status', 'in', statuses)\n .selectAll()\n\n if (subjects && subjects.length > 0) {\n query = query.where('did', 'in', subjects)\n }\n\n if (startTime) {\n query = query.where((qb) => {\n return qb\n .orWhere('executeAt', '>=', startTime.toISOString())\n .orWhere('executeAfter', '>=', startTime.toISOString())\n })\n }\n\n if (endTime) {\n query = query.where((qb) => {\n return qb\n .orWhere('executeAt', '<=', endTime.toISOString())\n .orWhere('executeUntil', '<=', endTime.toISOString())\n .orWhere((sqb) => {\n return sqb\n .where('executeUntil', 'is', null)\n .where('executeAfter', '<=', endTime.toISOString())\n })\n })\n }\n\n if (cursor) {\n query = query.where(\n 'id',\n direction === 'asc' ? '>' : '<',\n parseInt(cursor, 10),\n )\n }\n\n const actions = await query.orderBy('id', direction).limit(limit).execute()\n\n return {\n actions,\n cursor: actions.at(-1)?.id?.toString(),\n }\n }\n\n async cancelScheduledActions(subjects: string[]): Promise<{\n succeeded: string[]\n failed: { did: string; error: string; errorCode?: string }[]\n }> {\n const succeeded: string[] = []\n const failed: { did: string; error: string; errorCode?: string }[] = []\n\n for (const did of subjects) {\n try {\n const result = await this.db.db\n .updateTable('scheduled_action')\n .set({\n status: 'cancelled',\n updatedAt: new Date().toISOString(),\n })\n .where('did', '=', did)\n .where('status', '=', 'pending')\n .executeTakeFirst()\n\n if (result.numUpdatedRows && result.numUpdatedRows > 0) {\n succeeded.push(did)\n } else {\n failed.push({\n did,\n error: 'No pending scheduled actions found for subject',\n errorCode: 'NoPendingActions',\n })\n }\n } catch (err) {\n dbLogger.error({ err, subjects }, 'Error cancelling scheduled action')\n failed.push({\n did,\n error: 'Unknown error',\n errorCode: 'DatabaseError',\n })\n }\n }\n\n return { succeeded, failed }\n }\n\n async getPendingActionsToExecute(\n now: Date,\n ): Promise<Selectable<ScheduledAction>[]> {\n return await this.db.db\n .selectFrom('scheduled_action')\n .selectAll()\n .where('status', '=', 'pending')\n .where((qb) => {\n return qb\n .orWhere('executeAfter', '<=', now.toISOString())\n .orWhere('executeAt', '<=', now.toISOString())\n })\n .execute()\n }\n\n async markActionAsExecuted(\n actionId: number,\n executionEventId: number,\n ): Promise<void> {\n const now = new Date().toISOString()\n await this.db.db\n .updateTable('scheduled_action')\n .set({\n status: 'executed',\n lastExecutedAt: now,\n executionEventId,\n updatedAt: now,\n })\n .where('id', '=', actionId)\n .execute()\n }\n\n async markActionAsFailed(\n actionId: number,\n failureReason: string,\n ): Promise<void> {\n const now = new Date().toISOString()\n await this.db.db\n .updateTable('scheduled_action')\n .set({\n status: 'failed',\n lastExecutedAt: now,\n lastFailureReason: failureReason,\n updatedAt: now,\n })\n .where('id', '=', actionId)\n .execute()\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/scheduled-action/service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Selectable } from 'kysely'\nimport { InvalidRequestError } from '@atproto/xrpc-server'\nimport { ScheduledActionStatus, ScheduledActionType } from '../api/util.js'\nimport { Database } from '../db/index.js'\nimport { ScheduledAction } from '../db/schema/scheduled-action.js'\nimport { ScheduledActionView } from '../lexicon/types/tools/ozone/moderation/defs.js'\nimport { dbLogger } from '../logger.js'\nimport { SchedulingParams } from './types.js'\n\nexport type ScheduledActionServiceCreator = (\n db: Database,\n) => ScheduledActionService\n\nexport class ScheduledActionService {\n constructor(public db: Database) {}\n\n static creator() {\n return (db: Database) => new ScheduledActionService(db)\n }\n\n formatScheduledAction(\n action: Selectable<ScheduledAction>,\n ): ScheduledActionView {\n return {\n id: action.id,\n action: action.action,\n eventData: action.eventData as { [x: string]: unknown } | undefined,\n did: action.did,\n executeAt: action.executeAt\n ? new Date(action.executeAt).toISOString()\n : undefined,\n executeAfter: action.executeAfter\n ? new Date(action.executeAfter).toISOString()\n : undefined,\n executeUntil: action.executeUntil\n ? new Date(action.executeUntil).toISOString()\n : undefined,\n randomizeExecution: action.randomizeExecution,\n createdBy: action.createdBy,\n createdAt: new Date(action.createdAt).toISOString(),\n updatedAt: new Date(action.updatedAt).toISOString(),\n status: action.status,\n lastExecutedAt: action.lastExecutedAt\n ? new Date(action.lastExecutedAt).toISOString()\n : undefined,\n lastFailureReason: action.lastFailureReason || undefined,\n executionEventId: action.executionEventId || undefined,\n }\n }\n\n async scheduleAction(\n schedulingParams: SchedulingParams,\n ): Promise<Selectable<ScheduledAction>> {\n const { action, eventData, did, createdBy } = schedulingParams\n\n // Only allow one pending action at a time for a given subject and action type\n const existingAction = await this.getPendingActionForSubject(did, action)\n if (existingAction) {\n throw new InvalidRequestError(\n 'A pending scheduled action already exists for this subject',\n 'ActionAlreadyExists',\n )\n }\n\n // When a time-range for action is specified, ensure that the range is valid\n if (\n 'executeAfter' in schedulingParams &&\n schedulingParams.executeAfter &&\n schedulingParams.executeUntil &&\n schedulingParams.executeAfter >= schedulingParams.executeUntil\n ) {\n throw new InvalidRequestError(\n 'executeAfter must be before executeUntil',\n 'InvalidScheduling',\n )\n }\n\n const now = new Date().toISOString()\n const randomizeExecution =\n !('executeAt' in schedulingParams) && 'executeAfter' in schedulingParams\n\n const scheduledAction = await this.db.db\n .insertInto('scheduled_action')\n .values({\n action,\n eventData: JSON.stringify(eventData),\n did,\n executeAt: randomizeExecution\n ? null\n : schedulingParams.executeAt?.toISOString(),\n executeAfter: randomizeExecution\n ? schedulingParams.executeAfter?.toISOString()\n : null,\n executeUntil: randomizeExecution\n ? schedulingParams.executeUntil?.toISOString()\n : null,\n randomizeExecution,\n createdBy,\n createdAt: now,\n updatedAt: now,\n status: 'pending',\n })\n .returningAll()\n .executeTakeFirstOrThrow()\n\n return scheduledAction\n }\n\n async getPendingActionForSubject(\n did: string,\n action: ScheduledActionType,\n ): Promise<Selectable<ScheduledAction> | null> {\n const scheduledAction = await this.db.db\n .selectFrom('scheduled_action')\n .selectAll()\n .where('did', '=', did)\n .where('action', '=', action)\n .where('status', '=', 'pending')\n .executeTakeFirst()\n\n return scheduledAction || null\n }\n\n async listScheduledActions({\n cursor,\n limit = 50,\n startTime,\n endTime,\n subjects,\n statuses = [],\n direction = 'desc',\n }: {\n cursor?: string\n limit?: number\n startTime?: Date\n endTime?: Date\n subjects?: string[]\n statuses: ScheduledActionStatus[]\n direction?: 'asc' | 'desc'\n }): Promise<{\n actions: Selectable<ScheduledAction>[]\n cursor?: string\n }> {\n let query = this.db.db\n .selectFrom('scheduled_action')\n .where('status', 'in', statuses)\n .selectAll()\n\n if (subjects && subjects.length > 0) {\n query = query.where('did', 'in', subjects)\n }\n\n if (startTime) {\n query = query.where((qb) => {\n return qb\n .orWhere('executeAt', '>=', startTime.toISOString())\n .orWhere('executeAfter', '>=', startTime.toISOString())\n })\n }\n\n if (endTime) {\n query = query.where((qb) => {\n return qb\n .orWhere('executeAt', '<=', endTime.toISOString())\n .orWhere('executeUntil', '<=', endTime.toISOString())\n .orWhere((sqb) => {\n return sqb\n .where('executeUntil', 'is', null)\n .where('executeAfter', '<=', endTime.toISOString())\n })\n })\n }\n\n if (cursor) {\n query = query.where(\n 'id',\n direction === 'asc' ? '>' : '<',\n parseInt(cursor, 10),\n )\n }\n\n const actions = await query.orderBy('id', direction).limit(limit).execute()\n\n return {\n actions,\n cursor: actions.at(-1)?.id?.toString(),\n }\n }\n\n async cancelScheduledActions(subjects: string[]): Promise<{\n succeeded: string[]\n failed: { did: string; error: string; errorCode?: string }[]\n }> {\n const succeeded: string[] = []\n const failed: { did: string; error: string; errorCode?: string }[] = []\n\n for (const did of subjects) {\n try {\n const result = await this.db.db\n .updateTable('scheduled_action')\n .set({\n status: 'cancelled',\n updatedAt: new Date().toISOString(),\n })\n .where('did', '=', did)\n .where('status', '=', 'pending')\n .executeTakeFirst()\n\n if (result.numUpdatedRows && result.numUpdatedRows > 0) {\n succeeded.push(did)\n } else {\n failed.push({\n did,\n error: 'No pending scheduled actions found for subject',\n errorCode: 'NoPendingActions',\n })\n }\n } catch (err) {\n dbLogger.error({ err, subjects }, 'Error cancelling scheduled action')\n failed.push({\n did,\n error: 'Unknown error',\n errorCode: 'DatabaseError',\n })\n }\n }\n\n return { succeeded, failed }\n }\n\n async getPendingActionsToExecute(\n now: Date,\n ): Promise<Selectable<ScheduledAction>[]> {\n return await this.db.db\n .selectFrom('scheduled_action')\n .selectAll()\n .where('status', '=', 'pending')\n .where((qb) => {\n return qb\n .orWhere('executeAfter', '<=', now.toISOString())\n .orWhere('executeAt', '<=', now.toISOString())\n })\n .execute()\n }\n\n async markActionAsExecuted(\n actionId: number,\n executionEventId: number,\n ): Promise<void> {\n const now = new Date().toISOString()\n await this.db.db\n .updateTable('scheduled_action')\n .set({\n status: 'executed',\n lastExecutedAt: now,\n executionEventId,\n updatedAt: now,\n })\n .where('id', '=', actionId)\n .execute()\n }\n\n async markActionAsFailed(\n actionId: number,\n failureReason: string,\n ): Promise<void> {\n const now = new Date().toISOString()\n await this.db.db\n .updateTable('scheduled_action')\n .set({\n status: 'failed',\n lastExecutedAt: now,\n lastFailureReason: failureReason,\n updatedAt: now,\n })\n .where('id', '=', actionId)\n .execute()\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outbox.d.ts","sourceRoot":"","sources":["../../src/sequencer/outbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAwB,MAAM,iBAAiB,CAAA;AAEnE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAErD,MAAM,MAAM,UAAU,GAAG;IACvB,aAAa,EAAE,MAAM,CAAA;CACtB,CAAA;AAED,qBAAa,MAAM;IAQR,SAAS,EAAE,SAAS;IAP7B,OAAO,CAAC,QAAQ,CAAQ;IACxB,QAAQ,SAAK;IAEb,aAAa,EAAE,SAAS,EAAE,CAAA;IAC1B,SAAS,EAAE,WAAW,CAAC,SAAS,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"outbox.d.ts","sourceRoot":"","sources":["../../src/sequencer/outbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAwB,MAAM,iBAAiB,CAAA;AAEnE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAErD,MAAM,MAAM,UAAU,GAAG;IACvB,aAAa,EAAE,MAAM,CAAA;CACtB,CAAA;AAED,qBAAa,MAAM;IAQR,SAAS,EAAE,SAAS;IAP7B,OAAO,CAAC,QAAQ,CAAQ;IACxB,QAAQ,SAAK;IAEb,aAAa,EAAE,SAAS,EAAE,CAAA;IAC1B,SAAS,EAAE,WAAW,CAAC,SAAS,CAAC,CAAA;IAEjC,YACS,SAAS,EAAE,SAAS,EAC3B,IAAI,GAAE,OAAO,CAAC,UAAU,CAAM,EAK/B;IAWM,MAAM,CACX,cAAc,CAAC,EAAE,MAAM,EACvB,MAAM,CAAC,EAAE,WAAW,GACnB,cAAc,CAAC,SAAS,CAAC,CAmE3B;IAGM,WAAW,CAAC,cAAc,EAAE,MAAM,4CAexC;CACF"}
|