@headless-lms/core 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (307) hide show
  1. package/dist/assets/events.d.ts +42 -24
  2. package/dist/assets/events.js +1 -1
  3. package/dist/assets/events.js.map +1 -1
  4. package/dist/assets/file-urls.d.ts +20 -0
  5. package/dist/assets/file-urls.js +25 -0
  6. package/dist/assets/file-urls.js.map +1 -0
  7. package/dist/assets/index.d.ts +6 -4
  8. package/dist/assets/index.js +4 -2
  9. package/dist/assets/jobs.d.ts +15 -0
  10. package/dist/assets/jobs.js +30 -0
  11. package/dist/assets/jobs.js.map +1 -0
  12. package/dist/assets/model.d.ts +2 -2
  13. package/dist/assets/ports.d.ts +46 -15
  14. package/dist/assets/service.d.ts +38 -7
  15. package/dist/assets/service.js +287 -38
  16. package/dist/assets/service.js.map +1 -1
  17. package/dist/automations/actions.d.ts +13 -24
  18. package/dist/automations/actions.js +106 -42
  19. package/dist/automations/actions.js.map +1 -1
  20. package/dist/automations/catalog.js +226 -3
  21. package/dist/automations/catalog.js.map +1 -1
  22. package/dist/automations/community-derivations.d.ts +14 -0
  23. package/dist/automations/community-derivations.js +439 -0
  24. package/dist/automations/community-derivations.js.map +1 -0
  25. package/dist/automations/community-mail.d.ts +98 -0
  26. package/dist/automations/community-mail.js +70 -0
  27. package/dist/automations/community-mail.js.map +1 -0
  28. package/dist/automations/events.d.ts +18 -0
  29. package/dist/automations/index.d.ts +13 -4
  30. package/dist/automations/index.js +10 -2
  31. package/dist/automations/model.d.ts +10 -3
  32. package/dist/automations/model.js +15 -2
  33. package/dist/automations/model.js.map +1 -1
  34. package/dist/automations/notifications/debounce.d.ts +25 -0
  35. package/dist/automations/notifications/debounce.js +93 -0
  36. package/dist/automations/notifications/debounce.js.map +1 -0
  37. package/dist/automations/notifications/defaults.d.ts +8 -0
  38. package/dist/automations/notifications/defaults.js +316 -0
  39. package/dist/automations/notifications/defaults.js.map +1 -0
  40. package/dist/automations/notifications/delivery.d.ts +28 -0
  41. package/dist/automations/notifications/delivery.js +88 -0
  42. package/dist/automations/notifications/delivery.js.map +1 -0
  43. package/dist/automations/notifications/processor.d.ts +64 -0
  44. package/dist/automations/notifications/processor.js +270 -0
  45. package/dist/automations/notifications/processor.js.map +1 -0
  46. package/dist/automations/notifications/rules/content.d.ts +6 -0
  47. package/dist/automations/notifications/rules/content.js +39 -0
  48. package/dist/automations/notifications/rules/content.js.map +1 -0
  49. package/dist/automations/notifications/rules/discussion.d.ts +7 -0
  50. package/dist/automations/notifications/rules/discussion.js +678 -0
  51. package/dist/automations/notifications/rules/discussion.js.map +1 -0
  52. package/dist/automations/notifications/rules/entitlements.d.ts +6 -0
  53. package/dist/automations/notifications/rules/entitlements.js +249 -0
  54. package/dist/automations/notifications/rules/entitlements.js.map +1 -0
  55. package/dist/automations/notifications/rules/index.d.ts +6 -0
  56. package/dist/automations/notifications/rules/index.js +15 -0
  57. package/dist/automations/notifications/rules/index.js.map +1 -0
  58. package/dist/automations/notifications/rules/organizations.d.ts +6 -0
  59. package/dist/automations/notifications/rules/organizations.js +28 -0
  60. package/dist/automations/notifications/rules/organizations.js.map +1 -0
  61. package/dist/automations/notifications/rules/recipients.d.ts +32 -0
  62. package/dist/automations/notifications/rules/recipients.js +99 -0
  63. package/dist/automations/notifications/rules/recipients.js.map +1 -0
  64. package/dist/automations/notifications/service.d.ts +27 -0
  65. package/dist/automations/notifications/service.js +45 -0
  66. package/dist/automations/notifications/service.js.map +1 -0
  67. package/dist/automations/notifications/types.d.ts +7 -0
  68. package/dist/automations/notifications/types.js +473 -0
  69. package/dist/automations/notifications/types.js.map +1 -0
  70. package/dist/automations/ports.d.ts +121 -3
  71. package/dist/automations/schedule.d.ts +6 -0
  72. package/dist/automations/schedule.js +41 -0
  73. package/dist/automations/schedule.js.map +1 -0
  74. package/dist/automations/service.d.ts +20 -4
  75. package/dist/automations/service.js +246 -45
  76. package/dist/automations/service.js.map +1 -1
  77. package/dist/content/events.d.ts +712 -50
  78. package/dist/content/events.js +55 -1
  79. package/dist/content/events.js.map +1 -1
  80. package/dist/content/index.d.ts +6 -4
  81. package/dist/content/index.js +3 -1
  82. package/dist/content/model.d.ts +2 -2
  83. package/dist/content/notification-categories.d.ts +36 -0
  84. package/dist/content/notification-categories.js +64 -0
  85. package/dist/content/notification-categories.js.map +1 -0
  86. package/dist/content/ports.d.ts +90 -18
  87. package/dist/content/service.d.ts +57 -18
  88. package/dist/content/service.js +520 -25
  89. package/dist/content/service.js.map +1 -1
  90. package/dist/content/titles.d.ts +22 -0
  91. package/dist/content/titles.js +69 -0
  92. package/dist/content/titles.js.map +1 -0
  93. package/dist/content/types.d.ts +2 -2
  94. package/dist/discussion/authors.d.ts +10 -0
  95. package/dist/discussion/authors.js +23 -0
  96. package/dist/discussion/authors.js.map +1 -0
  97. package/dist/discussion/chats.d.ts +109 -0
  98. package/dist/discussion/chats.js +831 -0
  99. package/dist/discussion/chats.js.map +1 -0
  100. package/dist/discussion/digests.d.ts +24 -0
  101. package/dist/discussion/digests.js +104 -0
  102. package/dist/discussion/digests.js.map +1 -0
  103. package/dist/discussion/events.d.ts +785 -56
  104. package/dist/discussion/events.js +165 -5
  105. package/dist/discussion/events.js.map +1 -1
  106. package/dist/discussion/index.d.ts +12 -3
  107. package/dist/discussion/index.js +9 -1
  108. package/dist/discussion/jobs.d.ts +16 -0
  109. package/dist/discussion/jobs.js +40 -0
  110. package/dist/discussion/jobs.js.map +1 -0
  111. package/dist/discussion/mentions.d.ts +11 -0
  112. package/dist/discussion/mentions.js +35 -0
  113. package/dist/discussion/mentions.js.map +1 -0
  114. package/dist/discussion/ports.d.ts +372 -28
  115. package/dist/discussion/ports.js +6 -1
  116. package/dist/discussion/ports.js.map +1 -0
  117. package/dist/discussion/posts.d.ts +113 -0
  118. package/dist/discussion/posts.js +775 -0
  119. package/dist/discussion/posts.js.map +1 -0
  120. package/dist/discussion/service.d.ts +42 -11
  121. package/dist/discussion/service.js +364 -85
  122. package/dist/discussion/service.js.map +1 -1
  123. package/dist/discussion/subscriptions.d.ts +37 -0
  124. package/dist/discussion/subscriptions.js +99 -0
  125. package/dist/discussion/subscriptions.js.map +1 -0
  126. package/dist/discussion/titles.d.ts +26 -0
  127. package/dist/discussion/titles.js +84 -0
  128. package/dist/discussion/titles.js.map +1 -0
  129. package/dist/entitlements/events.d.ts +178 -11
  130. package/dist/entitlements/events.js +28 -1
  131. package/dist/entitlements/events.js.map +1 -1
  132. package/dist/entitlements/index.d.ts +6 -4
  133. package/dist/entitlements/index.js +3 -1
  134. package/dist/entitlements/jobs.d.ts +12 -0
  135. package/dist/entitlements/jobs.js +18 -0
  136. package/dist/entitlements/jobs.js.map +1 -0
  137. package/dist/entitlements/model.d.ts +3 -2
  138. package/dist/entitlements/ports.d.ts +128 -11
  139. package/dist/entitlements/rules.d.ts +12 -0
  140. package/dist/entitlements/rules.js +17 -0
  141. package/dist/entitlements/rules.js.map +1 -0
  142. package/dist/entitlements/service.d.ts +57 -3
  143. package/dist/entitlements/service.js +492 -2
  144. package/dist/entitlements/service.js.map +1 -1
  145. package/dist/identity/access.d.ts +6 -0
  146. package/dist/identity/access.js +7 -0
  147. package/dist/identity/access.js.map +1 -0
  148. package/dist/identity/devices.d.ts +30 -0
  149. package/dist/identity/devices.js +107 -0
  150. package/dist/identity/devices.js.map +1 -0
  151. package/dist/identity/events.d.ts +2 -2
  152. package/dist/identity/index.d.ts +7 -4
  153. package/dist/identity/index.js +4 -1
  154. package/dist/identity/model.d.ts +2 -1
  155. package/dist/identity/notification-settings.d.ts +28 -0
  156. package/dist/identity/notification-settings.js +113 -0
  157. package/dist/identity/notification-settings.js.map +1 -0
  158. package/dist/identity/ports.d.ts +97 -3
  159. package/dist/identity/service.d.ts +14 -3
  160. package/dist/identity/service.js +62 -5
  161. package/dist/identity/service.js.map +1 -1
  162. package/dist/integrations/events.d.ts +3 -3
  163. package/dist/integrations/model.js +1 -1
  164. package/dist/integrations/model.js.map +1 -1
  165. package/dist/integrations/service.js +2 -2
  166. package/dist/integrations/service.js.map +1 -1
  167. package/dist/organizations/access-scopes.d.ts +19 -0
  168. package/dist/organizations/access-scopes.js +89 -0
  169. package/dist/organizations/access-scopes.js.map +1 -0
  170. package/dist/organizations/events.d.ts +75 -33
  171. package/dist/organizations/events.js +6 -0
  172. package/dist/organizations/events.js.map +1 -1
  173. package/dist/organizations/index.d.ts +8 -6
  174. package/dist/organizations/index.js +5 -3
  175. package/dist/organizations/members.d.ts +8 -5
  176. package/dist/organizations/members.js.map +1 -1
  177. package/dist/organizations/notification-categories.d.ts +32 -0
  178. package/dist/organizations/notification-categories.js +51 -0
  179. package/dist/organizations/notification-categories.js.map +1 -0
  180. package/dist/organizations/ports.d.ts +17 -11
  181. package/dist/organizations/roles.d.ts +6 -10
  182. package/dist/organizations/roles.js +19 -7
  183. package/dist/organizations/roles.js.map +1 -1
  184. package/dist/organizations/service.d.ts +22 -11
  185. package/dist/organizations/service.js +164 -47
  186. package/dist/organizations/service.js.map +1 -1
  187. package/dist/organizations/types.d.ts +1 -10
  188. package/dist/progress/events.d.ts +14 -14
  189. package/dist/progress/index.d.ts +2 -2
  190. package/dist/progress/ports.d.ts +6 -1
  191. package/dist/progress/service.d.ts +3 -1
  192. package/dist/progress/service.js +6 -2
  193. package/dist/progress/service.js.map +1 -1
  194. package/dist/reporting/admin/index.d.ts +4 -0
  195. package/dist/reporting/admin/index.js +2 -0
  196. package/dist/reporting/admin/model.d.ts +108 -0
  197. package/dist/reporting/admin/ports.d.ts +27 -0
  198. package/dist/reporting/admin/service.d.ts +22 -0
  199. package/dist/reporting/admin/service.js +38 -0
  200. package/dist/reporting/admin/service.js.map +1 -0
  201. package/dist/reporting/member/index.d.ts +5 -0
  202. package/dist/reporting/member/index.js +2 -0
  203. package/dist/reporting/member/model.d.ts +157 -0
  204. package/dist/reporting/member/ports.d.ts +48 -0
  205. package/dist/reporting/member/service.d.ts +92 -0
  206. package/dist/reporting/member/service.js +364 -0
  207. package/dist/reporting/member/service.js.map +1 -0
  208. package/dist/shared/actor.d.ts +9 -0
  209. package/dist/shared/errors.d.ts +19 -1
  210. package/dist/shared/errors.js +27 -1
  211. package/dist/shared/errors.js.map +1 -1
  212. package/dist/shared/id.d.ts +14 -0
  213. package/dist/shared/id.js +15 -1
  214. package/dist/shared/id.js.map +1 -1
  215. package/dist/shared/mailer.d.ts +15 -2
  216. package/dist/shared/mailer.js +32 -5
  217. package/dist/shared/mailer.js.map +1 -1
  218. package/dist/shared/network.d.ts +10 -0
  219. package/dist/shared/network.js +211 -0
  220. package/dist/shared/network.js.map +1 -0
  221. package/dist/shared/notification-categories.d.ts +16 -0
  222. package/dist/shared/notification-categories.js +94 -0
  223. package/dist/shared/notification-categories.js.map +1 -0
  224. package/dist/shared/notification-settings.d.ts +14 -0
  225. package/dist/shared/notification-settings.js +61 -0
  226. package/dist/shared/notification-settings.js.map +1 -0
  227. package/dist/shared/ports.d.ts +48 -2
  228. package/dist/shared/settings.d.ts +2 -1
  229. package/dist/shared/settings.js +1 -0
  230. package/dist/shared/settings.js.map +1 -1
  231. package/dist/types/assets.d.ts +2 -2
  232. package/dist/types/automations.d.ts +15 -1
  233. package/dist/types/content.d.ts +2 -2
  234. package/dist/types/discussion.d.ts +11 -3
  235. package/dist/types/email-templates.d.ts +216 -1
  236. package/dist/types/entitlements.d.ts +2 -2
  237. package/dist/types/identity.d.ts +48 -2
  238. package/dist/types/index.d.ts +9 -8
  239. package/dist/types/notifications.d.ts +157 -0
  240. package/dist/types/ports.d.ts +6 -0
  241. package/dist/types/schemas/assets.d.ts +65 -37
  242. package/dist/types/schemas/assets.js +17 -12
  243. package/dist/types/schemas/assets.js.map +1 -1
  244. package/dist/types/schemas/automations.d.ts +9 -1
  245. package/dist/types/schemas/automations.js +13 -6
  246. package/dist/types/schemas/automations.js.map +1 -1
  247. package/dist/types/schemas/content.d.ts +396 -3
  248. package/dist/types/schemas/content.js +161 -3
  249. package/dist/types/schemas/content.js.map +1 -1
  250. package/dist/types/schemas/discussion.d.ts +443 -3
  251. package/dist/types/schemas/discussion.js +233 -4
  252. package/dist/types/schemas/discussion.js.map +1 -1
  253. package/dist/types/schemas/entitlements.d.ts +36 -2
  254. package/dist/types/schemas/entitlements.js +23 -1
  255. package/dist/types/schemas/entitlements.js.map +1 -1
  256. package/dist/types/schemas/identity.d.ts +27 -1
  257. package/dist/types/schemas/identity.js +14 -2
  258. package/dist/types/schemas/identity.js.map +1 -1
  259. package/dist/types/schemas/index.d.ts +9 -8
  260. package/dist/types/schemas/index.js +9 -8
  261. package/dist/types/schemas/notifications.d.ts +95 -0
  262. package/dist/types/schemas/notifications.js +56 -0
  263. package/dist/types/schemas/notifications.js.map +1 -0
  264. package/dist/types/schemas/organizations.d.ts +8 -0
  265. package/dist/types/schemas/organizations.js +2 -0
  266. package/dist/types/schemas/organizations.js.map +1 -1
  267. package/dist/types/schemas/progress.d.ts +3 -3
  268. package/dist/types/schemas/shared.d.ts +3 -1
  269. package/dist/types/schemas/shared.js +5 -1
  270. package/dist/types/schemas/shared.js.map +1 -1
  271. package/package.json +2 -2
  272. package/dist/reporting/courses/index.d.ts +0 -4
  273. package/dist/reporting/courses/index.js +0 -2
  274. package/dist/reporting/courses/model.d.ts +0 -24
  275. package/dist/reporting/courses/ports.d.ts +0 -13
  276. package/dist/reporting/courses/service.d.ts +0 -19
  277. package/dist/reporting/courses/service.js +0 -20
  278. package/dist/reporting/courses/service.js.map +0 -1
  279. package/dist/reporting/dashboard/index.d.ts +0 -4
  280. package/dist/reporting/dashboard/index.js +0 -2
  281. package/dist/reporting/dashboard/model.d.ts +0 -14
  282. package/dist/reporting/dashboard/ports.d.ts +0 -13
  283. package/dist/reporting/dashboard/service.d.ts +0 -19
  284. package/dist/reporting/dashboard/service.js +0 -20
  285. package/dist/reporting/dashboard/service.js.map +0 -1
  286. package/dist/reporting/learn/index.d.ts +0 -5
  287. package/dist/reporting/learn/index.js +0 -2
  288. package/dist/reporting/learn/model.d.ts +0 -23
  289. package/dist/reporting/learn/ports.d.ts +0 -43
  290. package/dist/reporting/learn/service.d.ts +0 -49
  291. package/dist/reporting/learn/service.js +0 -116
  292. package/dist/reporting/learn/service.js.map +0 -1
  293. package/dist/reporting/students/index.d.ts +0 -4
  294. package/dist/reporting/students/index.js +0 -2
  295. package/dist/reporting/students/model.d.ts +0 -51
  296. package/dist/reporting/students/model.js +0 -1
  297. package/dist/reporting/students/ports.d.ts +0 -15
  298. package/dist/reporting/students/ports.js +0 -1
  299. package/dist/reporting/students/service.d.ts +0 -20
  300. package/dist/reporting/students/service.js +0 -23
  301. package/dist/reporting/students/service.js.map +0 -1
  302. /package/dist/reporting/{courses → admin}/model.js +0 -0
  303. /package/dist/reporting/{courses → admin}/ports.js +0 -0
  304. /package/dist/reporting/{dashboard → member}/model.js +0 -0
  305. /package/dist/reporting/{dashboard → member}/ports.js +0 -0
  306. /package/dist/{reporting/learn/model.js → shared/actor.js} +0 -0
  307. /package/dist/{reporting/learn/ports.js → types/notifications.js} +0 -0
@@ -1,10 +1,15 @@
1
- import { Page } from "../types/shared.js";
2
- import { Activity } from "../types/schemas/content.js";
1
+ import { JsonValue, Page } from "../types/shared.js";
2
+ import { Activity, SpaceDetail } from "../types/schemas/content.js";
3
3
  import { Role } from "../types/schemas/organizations.js";
4
- import { CommentListItem, CommentView, CommentsConfig, ListCommentsQuery, ReactionCounts, ReactionEmoji } from "../types/schemas/discussion.js";
5
- import { Comment, CommentAuthor, CommentReport } from "../types/discussion.js";
4
+ import { Chat, ChatLastMessage, ChatUser, CommentListItem, CommentView, CommentsConfig, DigestRun, FlaggedComment, ListCommentsQuery, MentionItemType, Message, MessageFlag, MessageFlagResolution, MessageView, Post, PostFlag, PostFlagResolution, PostStatus, PostView, ReactionCounts, ReactionEmoji, Subscription, SubscriptionItemType, SubscriptionView } from "../types/schemas/discussion.js";
5
+ import { Comment, CommentReport } from "../types/discussion.js";
6
+ import { SubjectRef } from "../types/notifications.js";
6
7
  import "../types/index.js";
7
8
  import { OutboxAppender, UnitOfWork } from "../shared/ports.js";
9
+ import { Actor } from "../shared/actor.js";
10
+ import { CommunityDigest } from "../content/ports.js";
11
+ import "../content/index.js";
12
+ import "../organizations/index.js";
8
13
  import "./model.js";
9
14
  import "./types.js";
10
15
  //#region src/discussion/ports.d.ts
@@ -23,27 +28,40 @@ interface CommentWithContext {
23
28
  courseId: string;
24
29
  activityTitle: string;
25
30
  }
26
- /**
27
- * Who is acting, and the role they hold in this org right now.
28
- *
29
- * The role is resolved at the HTTP edge from the session's active-org
30
- * org user and handed in — read fresh on every request, never stored on a
31
- * comment. Core does not look a role up to authorise.
32
- */
33
- interface Actor {
34
- id: string;
35
- role: Role;
36
- }
37
31
  type PostCommentInput = {
38
32
  actor: Actor;
39
- activityId: string;
40
33
  parentId: string | null;
41
34
  body: string;
42
- };
35
+ } & ({
36
+ activityId: string;
37
+ postId?: undefined;
38
+ } | {
39
+ postId: string;
40
+ activityId?: undefined;
41
+ });
43
42
  /** A profile row as the repository loads it, keyed by `org_users.id`. `email`
44
43
  * is stripped before comments are served and kept only for the staff list. */
45
- interface AuthorRecord extends CommentAuthor {
44
+ interface AuthorRecord {
45
+ id: string;
46
+ firstName: string | null;
47
+ lastName: string | null;
46
48
  email: string;
49
+ image: string | null;
50
+ role: Role;
51
+ displayName: string | null;
52
+ headline: string | null;
53
+ imageAssetOrgId: string | null;
54
+ imageAssetId: string | null;
55
+ }
56
+ interface ItemTitle {
57
+ title: string;
58
+ excerpt?: string;
59
+ }
60
+ interface ItemTitles {
61
+ titlesFor(orgId: string, actor: Actor, refs: SubjectRef[]): Promise<Record<string, ItemTitle>>;
62
+ }
63
+ interface AuthorProfileReader {
64
+ authorsOf(orgId: string, orgUserIds: string[]): Promise<Record<string, AuthorRecord>>;
47
65
  }
48
66
  interface DiscussionService {
49
67
  /** Post a root comment or a reply. Lands pending where review is required and
@@ -52,15 +70,17 @@ interface DiscussionService {
52
70
  /** Author-only. Throws ForbiddenError for anyone else, staff included. */
53
71
  edit(orgId: string, commentId: string, actor: Actor, body: string): Promise<CommentView>;
54
72
  /** The author, or anyone whose role is staff. */
55
- remove(orgId: string, commentId: string, actor: Actor): Promise<Comment>;
73
+ remove(orgId: string, commentId: string, actor: Actor, communityId?: string): Promise<CommentView>;
56
74
  /** Staff only. Returns the comment to published. */
57
- restore(orgId: string, commentId: string, actor: Actor): Promise<Comment>;
75
+ restore(orgId: string, commentId: string, actor: Actor, communityId?: string): Promise<CommentView>;
58
76
  /** Staff only. Publishes a pending comment. */
59
77
  approve(orgId: string, commentId: string, actor: Actor): Promise<Comment>;
60
78
  /** Make a comment published, whichever state it is in. Staff only. */
61
79
  publish(orgId: string, commentId: string, actor: Actor): Promise<Comment>;
62
80
  /** The activity's comments as this reader may see them. */
63
81
  activityComments(orgId: string, activityId: string, actor: Actor): Promise<ActivityComments>;
82
+ /** One page of top-level comments, oldest first, each with its replies. */
83
+ postComments(orgId: string, postId: string, actor: Actor, query: PageQuery): Promise<Page<CommentView>>;
64
84
  /**
65
85
  * The org's comments, filtered. Staff-scoped: rows carry the author's email
66
86
  * and a removed comment's body, neither of which a learner may see, so the
@@ -71,42 +91,366 @@ interface DiscussionService {
71
91
  */
72
92
  listComments(orgId: string, query: ListCommentsQuery): Promise<Page<CommentListItem>>;
73
93
  setReaction(orgId: string, commentId: string, actor: Actor, emoji: ReactionEmoji | null): Promise<CommentReactions>;
74
- resolveReports(orgId: string, commentId: string, actor: Actor): Promise<void>;
94
+ resolveReports(orgId: string, commentId: string, actor: Actor, communityId?: string): Promise<void>;
75
95
  /** Accepted even on locked comments — a locked activity can still hold
76
96
  * something a moderator needs to see. */
77
97
  reportComment(orgId: string, commentId: string, actor: Actor, reason: string): Promise<CommentReport>;
78
98
  /** Plain read of one comment — no rules applied. */
79
99
  getComment(orgId: string, commentId: string): Promise<Comment | null>;
100
+ commenterIds(orgId: string, target: {
101
+ postId: string;
102
+ } | {
103
+ activityId: string;
104
+ }): Promise<string[]>;
80
105
  }
81
- interface CourseAccessReader {
82
- hasCourseAccess(orgId: string, orgUserId: string, courseId: string): Promise<boolean>;
106
+ interface ContentAccessReader {
107
+ hasAccess(orgId: string, orgUserId: string, contentId: string): Promise<boolean>;
83
108
  }
84
109
  interface ActivityGetter {
85
110
  getActivity(orgId: string, activityId: string): Promise<Activity | null>;
86
111
  }
87
- interface DiscussionRepository {
112
+ interface DiscussionRepository extends PostCommentReader, AuthorProfileReader {
88
113
  insertComment(orgId: string, comment: Comment): Promise<Comment>;
89
114
  findComment(orgId: string, id: string): Promise<Comment | null>;
115
+ commenterIds(orgId: string, target: {
116
+ postId: string;
117
+ } | {
118
+ activityId: string;
119
+ }): Promise<string[]>;
90
120
  updateComment(orgId: string, id: string, patch: Partial<Pick<Comment, 'body' | 'status' | 'removedBy' | 'updatedAt'>>): Promise<Comment | null>;
91
121
  /** Every comment on the activity, oldest first, including removed ones. */
92
122
  listByActivity(orgId: string, activityId: string): Promise<Comment[]>;
123
+ /** A page of the post's top-level threads the viewer can see, oldest first, with their replies. `total` counts threads. */
124
+ listThreadsByPost(orgId: string, postId: string, query: PostThreadsQuery): Promise<Page<Comment>>;
93
125
  reactionsOf(orgId: string, commentIds: string[], viewerId: string): Promise<Record<string, CommentReactions>>;
94
- setReaction(orgId: string, commentId: string, orgUserId: string, emoji: ReactionEmoji | null): Promise<void>;
126
+ setReaction(orgId: string, commentId: string, orgUserId: string, emoji: ReactionEmoji | null): Promise<boolean>;
95
127
  /** Returns null when this person has already reported this comment. */
96
128
  insertReport(orgId: string, report: CommentReport): Promise<CommentReport | null>;
97
129
  /** Every unresolved report against the given comments. */
98
130
  listOpenReports(orgId: string, commentIds: string[]): Promise<CommentReport[]>;
99
- resolveReportsFor(orgId: string, commentId: string, resolvedAt: Date): Promise<void>;
100
- authorsOf(orgId: string, orgUserIds: string[]): Promise<Record<string, AuthorRecord>>;
131
+ resolveReportsFor(orgId: string, commentId: string, resolved: {
132
+ resolvedAt: Date;
133
+ resolvedBy: string;
134
+ resolution: 'dismissed' | 'removed';
135
+ }): Promise<void>;
101
136
  listComments(orgId: string, query: ListCommentsQuery): Promise<Page<CommentWithContext>>;
102
137
  }
103
138
  /** Writes that emit events run through this scope so the row and the outbox
104
139
  * entry commit in one transaction. */
105
140
  interface DiscussionWriteScope {
106
141
  discussion: DiscussionRepository;
142
+ mentions: MentionsRepository;
107
143
  outbox: OutboxAppender;
108
144
  }
109
145
  type DiscussionUnitOfWork = UnitOfWork<DiscussionWriteScope>;
146
+ interface AuthorRoleReader {
147
+ roleOf(orgId: string, orgUserId: string): Promise<Role | null>;
148
+ rolesOf(orgId: string, orgUserIds: string[]): Promise<Record<string, Role>>;
149
+ }
150
+ type PostMode = 'draft' | 'submit' | 'schedule';
151
+ interface CreatePostInput {
152
+ communityId: string;
153
+ spaceId: string;
154
+ title: string;
155
+ body: string;
156
+ topicId: string | null;
157
+ extra: JsonValue;
158
+ coverAssetId: string | null;
159
+ mode: PostMode;
160
+ scheduledAt: Date | null;
161
+ }
162
+ interface UpdatePostInput {
163
+ title?: string;
164
+ body?: string;
165
+ topicId?: string | null;
166
+ extra?: JsonValue;
167
+ coverAssetId?: string | null;
168
+ }
169
+ type PostPatch = Partial<Pick<Post, 'title' | 'body' | 'topicId' | 'extra' | 'coverAssetId' | 'status' | 'scheduledAt' | 'publishedAt' | 'removedBy' | 'pinned' | 'commentsClosed' | 'reactionsEnabled'>>;
170
+ interface FeedQuery {
171
+ page: number;
172
+ pageSize: number;
173
+ topicId?: string;
174
+ }
175
+ interface PostReactions {
176
+ reactions: ReactionCounts;
177
+ viewerReaction?: ReactionEmoji;
178
+ }
179
+ type PageQuery = Pick<FeedQuery, 'page' | 'pageSize'>;
180
+ interface PostThreadsQuery extends PageQuery {
181
+ viewerId: string;
182
+ seesHeld: boolean;
183
+ }
184
+ interface BookmarkQuery extends PageQuery {
185
+ visibleStatuses: PostStatus[];
186
+ }
187
+ declare const MODERATION_QUEUE_LIMIT = 100;
188
+ interface SpaceModerationQueue {
189
+ pending: PostView[];
190
+ flags: PostFlag[];
191
+ commentFlags: FlaggedComment[];
192
+ }
193
+ interface PostCommentReader {
194
+ listOpenFlagsForSpace(orgId: string, spaceId: string, limit: number): Promise<FlaggedComment[]>;
195
+ publishedCommentCounts(orgId: string, postIds: string[]): Promise<Record<string, number>>;
196
+ }
197
+ interface PublishedPost {
198
+ postId: string;
199
+ spaceId: string;
200
+ title: string;
201
+ orgUserId: string;
202
+ commentCount: number;
203
+ }
204
+ interface PostService {
205
+ visibleTitles(orgId: string, actor: Actor, postIds: string[]): Promise<Record<string, string>>;
206
+ publishedBetween(orgId: string, communityId: string, from: Date, to: Date): Promise<PublishedPost[]>;
207
+ create(orgId: string, actor: Actor, input: CreatePostInput): Promise<PostView>;
208
+ update(orgId: string, postId: string, actor: Actor, patch: UpdatePostInput): Promise<PostView>;
209
+ submit(orgId: string, postId: string, actor: Actor): Promise<PostView>;
210
+ schedule(orgId: string, postId: string, actor: Actor, scheduledAt: Date): Promise<PostView>;
211
+ unschedule(orgId: string, postId: string, actor: Actor): Promise<PostView>;
212
+ publishScheduled(orgId: string, postId: string, now: Date): Promise<void>;
213
+ approve(orgId: string, postId: string, actor: Actor, communityId?: string): Promise<PostView>;
214
+ remove(orgId: string, postId: string, actor: Actor, communityId?: string): Promise<PostView>;
215
+ restore(orgId: string, postId: string, actor: Actor, communityId?: string): Promise<PostView>;
216
+ pin(orgId: string, postId: string, actor: Actor): Promise<PostView>;
217
+ unpin(orgId: string, postId: string, actor: Actor): Promise<PostView>;
218
+ closeComments(orgId: string, postId: string, actor: Actor): Promise<PostView>;
219
+ openComments(orgId: string, postId: string, actor: Actor): Promise<PostView>;
220
+ setReactions(orgId: string, postId: string, actor: Actor, enabled: boolean): Promise<PostView>;
221
+ setReaction(orgId: string, postId: string, actor: Actor, emoji: ReactionEmoji | null): Promise<PostReactions>;
222
+ bookmark(orgId: string, postId: string, actor: Actor): Promise<void>;
223
+ unbookmark(orgId: string, postId: string, actor: Actor): Promise<void>;
224
+ flag(orgId: string, postId: string, actor: Actor, reason: string): Promise<PostFlag>;
225
+ resolveFlags(orgId: string, postId: string, actor: Actor, resolution: Extract<PostFlagResolution, 'dismissed'>, communityId?: string): Promise<void>;
226
+ spaceFeed(orgId: string, actor: Actor, communityId: string, spaceId: string, query: FeedQuery): Promise<Page<PostView>>;
227
+ memberFeed(orgId: string, actor: Actor, communityId: string, query: Pick<FeedQuery, 'page' | 'pageSize'>): Promise<Page<PostView>>;
228
+ listOwn(orgId: string, actor: Actor, communityId: string, query: PageQuery): Promise<Page<PostView>>;
229
+ listBookmarked(orgId: string, actor: Actor, communityId: string, query: PageQuery): Promise<Page<PostView>>;
230
+ /** Signed URL of the post's cover, for whoever can read the post. */
231
+ coverFile(orgId: string, actor: Actor, postId: string): Promise<string | null>;
232
+ getPost(orgId: string, actor: Actor, postId: string): Promise<PostView>;
233
+ visible(orgId: string, actor: Actor, postId: string): Promise<{
234
+ post: Post;
235
+ space: SpaceDetail;
236
+ }>;
237
+ find(orgId: string, postId: string): Promise<Post | null>;
238
+ moderationQueue(orgId: string, actor: Actor, communityId: string, spaceId: string): Promise<SpaceModerationQueue>;
239
+ }
240
+ interface PostGuard {
241
+ status: PostStatus;
242
+ scheduledAt?: Date;
243
+ dueBy?: Date;
244
+ }
245
+ interface PostRepository {
246
+ insert(orgId: string, post: Post): Promise<Post>;
247
+ update(orgId: string, id: string, patch: PostPatch): Promise<Post | null>;
248
+ updateIf(orgId: string, id: string, guard: PostGuard, patch: PostPatch): Promise<Post | null>;
249
+ find(orgId: string, id: string): Promise<Post | null>;
250
+ findMany(orgId: string, ids: string[]): Promise<Post[]>;
251
+ publishedBetween(orgId: string, communityId: string, from: Date, to: Date): Promise<PublishedPost[]>;
252
+ listPublished(orgId: string, spaceIds: string[], query: FeedQuery): Promise<Page<Post>>;
253
+ listByAuthor(orgId: string, orgUserId: string, spaceIds: string[], query: PageQuery): Promise<Page<Post>>;
254
+ listBookmarked(orgId: string, orgUserId: string, spaceIds: string[], query: BookmarkQuery): Promise<Page<Post>>;
255
+ listPending(orgId: string, spaceId: string, limit: number): Promise<Post[]>;
256
+ reactionsOf(orgId: string, postIds: string[], viewerId: string): Promise<Record<string, PostReactions>>;
257
+ setReaction(orgId: string, postId: string, orgUserId: string, emoji: ReactionEmoji | null): Promise<boolean>;
258
+ bookmark(orgId: string, postId: string, orgUserId: string): Promise<void>;
259
+ unbookmark(orgId: string, postId: string, orgUserId: string): Promise<void>;
260
+ bookmarkedAmong(orgId: string, postIds: string[], orgUserId: string): Promise<string[]>;
261
+ insertFlag(orgId: string, flag: PostFlag): Promise<PostFlag | null>;
262
+ findFlag(orgId: string, postId: string, orgUserId: string): Promise<PostFlag | null>;
263
+ listOpenFlags(orgId: string, spaceId: string, limit: number): Promise<PostFlag[]>;
264
+ resolveFlags(orgId: string, postId: string, resolved: {
265
+ resolvedAt: Date;
266
+ resolvedBy: string;
267
+ resolution: PostFlagResolution;
268
+ }): Promise<void>;
269
+ }
270
+ interface PostWriteScope {
271
+ posts: PostRepository;
272
+ mentions: MentionsRepository;
273
+ outbox: OutboxAppender;
274
+ }
275
+ type PostUnitOfWork = UnitOfWork<PostWriteScope>;
276
+ interface DigestScheduleReader {
277
+ communitiesWithDigest(): Promise<CommunityDigest[]>;
278
+ }
279
+ interface DigestRunRepository {
280
+ listRuns(): Promise<DigestRun[]>;
281
+ advance(orgId: string, communityId: string, dueAt: Date): Promise<boolean>;
282
+ }
283
+ interface DigestWriteScope {
284
+ digests: DigestRunRepository;
285
+ outbox: OutboxAppender;
286
+ }
287
+ type DigestUnitOfWork = UnitOfWork<DigestWriteScope>;
288
+ interface DigestService {
289
+ openDue(now: Date): Promise<void>;
290
+ }
291
+ interface StartDirectInput {
292
+ communityId: string;
293
+ orgUserId: string;
294
+ body: string | null;
295
+ assetIds: string[];
296
+ }
297
+ interface StartGroupInput {
298
+ communityId: string;
299
+ orgUserIds: string[];
300
+ }
301
+ interface SendMessageInput {
302
+ body: string;
303
+ replyToId: string | null;
304
+ assetIds: string[];
305
+ }
306
+ interface ChatListItem {
307
+ chat: Chat;
308
+ participantIds: string[];
309
+ lastReadMessageId: string | null;
310
+ subscription: SubscriptionView | null;
311
+ unread: number;
312
+ lastMessage: ChatLastMessage | null;
313
+ }
314
+ type ChatInboxRow = Omit<ChatListItem, 'subscription'>;
315
+ interface ChatUnread {
316
+ count: number;
317
+ latest: {
318
+ messageId: string;
319
+ senderOrgUserId: string;
320
+ body: string;
321
+ } | null;
322
+ }
323
+ interface MessageReactions {
324
+ reactions: ReactionCounts;
325
+ viewerReaction?: ReactionEmoji;
326
+ }
327
+ interface ChatSummary {
328
+ chat: Chat;
329
+ participantIds: string[];
330
+ }
331
+ interface MessageQueueEntry {
332
+ message: MessageView;
333
+ flags: MessageFlag[];
334
+ chat: Chat;
335
+ participantIds: string[];
336
+ }
337
+ interface MessageQueue {
338
+ entries: MessageQueueEntry[];
339
+ }
340
+ interface ChatService {
341
+ startDirect(orgId: string, actor: Actor, input: StartDirectInput): Promise<Chat>;
342
+ startGroup(orgId: string, actor: Actor, input: StartGroupInput): Promise<Chat>;
343
+ addParticipant(orgId: string, actor: Actor, chatId: string, orgUserId: string): Promise<Chat>;
344
+ leave(orgId: string, actor: Actor, chatId: string): Promise<Chat>;
345
+ accept(orgId: string, actor: Actor, chatId: string): Promise<Chat>;
346
+ send(orgId: string, actor: Actor, chatId: string, input: SendMessageInput): Promise<MessageView>;
347
+ editMessage(orgId: string, actor: Actor, messageId: string, body: string): Promise<MessageView>;
348
+ deleteMessage(orgId: string, actor: Actor, messageId: string): Promise<MessageView>;
349
+ setReaction(orgId: string, actor: Actor, messageId: string, emoji: ReactionEmoji | null): Promise<MessageReactions>;
350
+ markRead(orgId: string, actor: Actor, chatId: string, messageId: string): Promise<void>;
351
+ block(orgId: string, actor: Actor, communityId: string, orgUserId: string): Promise<void>;
352
+ unblock(orgId: string, actor: Actor, communityId: string, orgUserId: string): Promise<void>;
353
+ flagMessage(orgId: string, actor: Actor, messageId: string, reason: string): Promise<MessageFlag>;
354
+ resolveMessageFlags(orgId: string, actor: Actor, messageId: string, resolution: Extract<MessageFlagResolution, 'dismissed'>, communityId?: string): Promise<void>;
355
+ lock(orgId: string, actor: Actor, chatId: string, communityId?: string): Promise<Chat>;
356
+ unlock(orgId: string, actor: Actor, chatId: string, communityId?: string): Promise<Chat>;
357
+ listChats(orgId: string, actor: Actor, communityId: string, query: PageQuery): Promise<Page<ChatListItem>>;
358
+ chatSummary(orgId: string, chatId: string): Promise<{
359
+ chat: Chat;
360
+ participants: ChatUser[];
361
+ } | null>;
362
+ latestMessageBody(orgId: string, chatId: string): Promise<string | null>;
363
+ unreadInChat(orgId: string, chatId: string, orgUserId: string): Promise<ChatUnread>;
364
+ findMessage(orgId: string, messageId: string): Promise<Message | null>;
365
+ isBlocked(orgId: string, communityId: string, a: string, b: string): Promise<boolean>;
366
+ titlesFor(orgId: string, actor: Actor, chatIds: string[]): Promise<Record<string, string>>;
367
+ messages(orgId: string, actor: Actor, chatId: string, query: Pick<FeedQuery, 'page' | 'pageSize'>): Promise<Page<MessageView>>;
368
+ /** Signed URL of a message attachment: for the chat's participants while the
369
+ * message stands (staff also after deletion), and for staff while it is flagged. */
370
+ attachmentFile(orgId: string, actor: Actor, chatId: string, messageId: string, assetId: string): Promise<string | null>;
371
+ messageQueue(orgId: string, actor: Actor, communityId: string): Promise<MessageQueue>;
372
+ }
373
+ interface ChatRepository {
374
+ lockDirectPair(orgId: string, communityId: string, orgUserId: string, otherOrgUserId: string): Promise<void>;
375
+ findDirect(orgId: string, communityId: string, orgUserId: string, otherOrgUserId: string): Promise<Chat | null>;
376
+ insertChat(orgId: string, chat: Chat): Promise<Chat>;
377
+ updateChat(orgId: string, id: string, patch: Partial<Pick<Chat, 'status' | 'lockedAt' | 'lockedBy'>>): Promise<Chat | null>;
378
+ findChat(orgId: string, id: string): Promise<Chat | null>;
379
+ listForUser(orgId: string, communityId: string, orgUserId: string, query: PageQuery): Promise<Page<ChatInboxRow>>;
380
+ participants(orgId: string, chatId: string): Promise<ChatUser[]>;
381
+ join(orgId: string, user: ChatUser): Promise<void>;
382
+ markLeft(orgId: string, chatId: string, orgUserId: string, leftAt: Date): Promise<void>;
383
+ setRead(orgId: string, chatId: string, orgUserId: string, messageId: string): Promise<void>;
384
+ unreadFor(orgId: string, chatId: string, orgUserId: string): Promise<ChatUnread>;
385
+ insertMessage(orgId: string, message: Message): Promise<Message>;
386
+ updateMessage(orgId: string, id: string, patch: Partial<Pick<Message, 'body' | 'editedAt' | 'deletedAt'>>): Promise<Message | null>;
387
+ findMessage(orgId: string, id: string): Promise<Message | null>;
388
+ listMessages(orgId: string, chatId: string, query: Pick<FeedQuery, 'page' | 'pageSize'>): Promise<Page<Message>>;
389
+ attach(orgId: string, communityId: string, messageId: string, assetIds: string[]): Promise<void>;
390
+ attachmentsOf(orgId: string, messageIds: string[]): Promise<Record<string, string[]>>;
391
+ blockedWith(orgId: string, communityId: string, orgUserId: string, otherIds: string[]): Promise<string[]>;
392
+ setReaction(orgId: string, communityId: string, messageId: string, orgUserId: string, emoji: ReactionEmoji | null): Promise<boolean>;
393
+ reactionsOf(orgId: string, messageIds: string[], viewerId: string): Promise<Record<string, MessageReactions>>;
394
+ block(orgId: string, communityId: string, blockerOrgUserId: string, blockedOrgUserId: string): Promise<void>;
395
+ unblock(orgId: string, communityId: string, blockerOrgUserId: string, blockedOrgUserId: string): Promise<void>;
396
+ isBlocked(orgId: string, communityId: string, a: string, b: string): Promise<boolean>;
397
+ anyBlockedAmong(orgId: string, communityId: string, orgUserIds: string[]): Promise<boolean>;
398
+ insertFlag(orgId: string, flag: MessageFlag): Promise<MessageFlag | null>;
399
+ findFlag(orgId: string, messageId: string, orgUserId: string): Promise<MessageFlag | null>;
400
+ listOpenFlags(orgId: string, communityId: string, limit?: number): Promise<MessageFlag[]>;
401
+ messagesByIds(orgId: string, ids: string[]): Promise<Message[]>;
402
+ chatSummaries(orgId: string, chatIds: string[]): Promise<ChatSummary[]>;
403
+ resolveFlags(orgId: string, messageId: string, resolved: {
404
+ resolvedAt: Date;
405
+ resolvedBy: string;
406
+ resolution: MessageFlagResolution;
407
+ }): Promise<void>;
408
+ resolveOpenFlagsForChat(orgId: string, chatId: string, resolved: {
409
+ resolvedAt: Date;
410
+ resolvedBy: string;
411
+ resolution: MessageFlagResolution;
412
+ }): Promise<void>;
413
+ }
414
+ interface ChatWriteScope {
415
+ chats: ChatRepository;
416
+ mentions: MentionsRepository;
417
+ outbox: OutboxAppender;
418
+ }
419
+ type ChatUnitOfWork = UnitOfWork<ChatWriteScope>;
420
+ type SubscriptionInput = {
421
+ state: 'following';
422
+ } | {
423
+ state: 'muted';
424
+ mutedUntil?: Date | null;
425
+ } | {
426
+ state: 'none';
427
+ };
428
+ type MuteInput = Exclude<SubscriptionInput, {
429
+ state: 'following';
430
+ }>;
431
+ interface SubscriptionService {
432
+ setPost(orgId: string, actor: Actor, postId: string, input: SubscriptionInput): Promise<void>;
433
+ setSpace(orgId: string, actor: Actor, spaceId: string, input: MuteInput): Promise<void>;
434
+ setChat(orgId: string, actor: Actor, chatId: string, input: MuteInput): Promise<void>;
435
+ viewerSubscriptions(orgId: string, orgUserId: string, itemType: SubscriptionItemType, itemIds: string[], at: Date): Promise<Record<string, SubscriptionView>>;
436
+ followerIds(orgId: string, postId: string): Promise<string[]>;
437
+ mutedAmong(orgId: string, item: SubjectRef, orgUserIds: string[], at: Date): Promise<string[]>;
438
+ itemSubscriptions(orgId: string, item: SubjectRef): Promise<Subscription[]>;
439
+ }
440
+ interface SubscriptionsRepository {
441
+ upsert(orgId: string, subscription: Subscription): Promise<void>;
442
+ remove(orgId: string, orgUserId: string, itemType: SubscriptionItemType, itemId: string): Promise<void>;
443
+ forViewer(orgId: string, orgUserId: string, itemType: SubscriptionItemType, itemIds: string[]): Promise<Subscription[]>;
444
+ ofItem(orgId: string, itemType: SubscriptionItemType, itemId: string): Promise<Subscription[]>;
445
+ followerIds(orgId: string, postId: string): Promise<string[]>;
446
+ mutedAmong(orgId: string, itemType: SubscriptionItemType, itemId: string, orgUserIds: string[], at: Date): Promise<string[]>;
447
+ }
448
+ interface MentionsRepository {
449
+ ofItem(orgId: string, itemType: MentionItemType, itemId: string): Promise<string[]>;
450
+ forItems(orgId: string, itemType: MentionItemType, itemIds: string[]): Promise<Record<string, string[]>>;
451
+ sync(orgId: string, itemType: MentionItemType, itemId: string, orgUserIds: string[], at: Date): Promise<string[]>;
452
+ removeItem(orgId: string, itemType: MentionItemType, itemId: string): Promise<void>;
453
+ }
110
454
  //#endregion
111
- export { ActivityComments, ActivityGetter, Actor, AuthorRecord, CommentReactions, CommentWithContext, CourseAccessReader, DiscussionRepository, DiscussionService, DiscussionUnitOfWork, DiscussionWriteScope, PostCommentInput };
455
+ export { ActivityComments, ActivityGetter, type Actor, AuthorProfileReader, AuthorRecord, AuthorRoleReader, BookmarkQuery, ChatInboxRow, ChatListItem, ChatRepository, ChatService, ChatSummary, ChatUnitOfWork, ChatUnread, ChatWriteScope, CommentReactions, CommentWithContext, ContentAccessReader, CreatePostInput, DigestRunRepository, DigestScheduleReader, DigestService, DigestUnitOfWork, DigestWriteScope, DiscussionRepository, DiscussionService, DiscussionUnitOfWork, DiscussionWriteScope, FeedQuery, ItemTitle, ItemTitles, MODERATION_QUEUE_LIMIT, MentionsRepository, MessageQueue, MessageQueueEntry, MessageReactions, MuteInput, PageQuery, PostCommentInput, PostCommentReader, PostGuard, PostMode, PostPatch, PostReactions, PostRepository, PostService, PostThreadsQuery, PostUnitOfWork, PostWriteScope, PublishedPost, SendMessageInput, SpaceModerationQueue, StartDirectInput, StartGroupInput, SubscriptionInput, SubscriptionService, SubscriptionsRepository, UpdatePostInput };
112
456
  //# sourceMappingURL=ports.d.ts.map
@@ -1 +1,6 @@
1
- export {};
1
+ //#region src/discussion/ports.ts
2
+ const MODERATION_QUEUE_LIMIT = 100;
3
+ //#endregion
4
+ export { MODERATION_QUEUE_LIMIT };
5
+
6
+ //# sourceMappingURL=ports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ports.js","names":[],"sources":["../../src/discussion/ports.ts"],"sourcesContent":["// discussion context — ports.\n// Inbound: the use cases the service implements.\n// Outbound: the persistence contract the repository fulfils.\nimport type {\n Activity,\n Chat,\n ChatLastMessage,\n ChatUser,\n DigestRun,\n FlaggedComment,\n JsonValue,\n MentionItemType,\n Message,\n MessageFlag,\n MessageFlagResolution,\n MessageView,\n Post,\n PostFlag,\n PostFlagResolution,\n PostStatus,\n PostView,\n SubjectRef,\n Subscription,\n SubscriptionItemType,\n SubscriptionView,\n} from '../types/index.js';\nimport type { CommunityDigest, SpaceDetail } from '../content/index.js';\nimport type { OutboxAppender, UnitOfWork } from '../shared/ports.js';\nimport type { Actor } from '../shared/actor.js';\nimport type { Role } from '../organizations/index.js';\nimport type { Comment, CommentReport } from './model.js';\nimport type {\n CommentsConfig,\n CommentListItem,\n CommentView,\n ListCommentsQuery,\n Page,\n ReactionCounts,\n ReactionEmoji,\n} from './types.js';\n\n/** An activity's comments as this reader may see them, with the config that\n * decided it. */\nexport interface ActivityComments {\n config: CommentsConfig;\n comments: CommentView[];\n}\n\nexport interface CommentReactions {\n reactions: ReactionCounts;\n viewerReaction?: ReactionEmoji;\n}\n\nexport interface CommentWithContext {\n comment: Comment;\n courseId: string;\n activityTitle: string;\n}\n\nexport type { Actor } from '../shared/actor.js';\nexport type PostCommentInput = {\n actor: Actor;\n parentId: string | null;\n body: string;\n} & ({ activityId: string; postId?: undefined } | { postId: string; activityId?: undefined });\n\n/** A profile row as the repository loads it, keyed by `org_users.id`. `email`\n * is stripped before comments are served and kept only for the staff list. */\nexport interface AuthorRecord {\n id: string;\n firstName: string | null;\n lastName: string | null;\n email: string;\n image: string | null;\n role: Role;\n displayName: string | null;\n headline: string | null;\n imageAssetOrgId: string | null;\n imageAssetId: string | null;\n}\n\nexport interface ItemTitle {\n title: string;\n excerpt?: string;\n}\n\nexport interface ItemTitles {\n titlesFor(orgId: string, actor: Actor, refs: SubjectRef[]): Promise<Record<string, ItemTitle>>;\n}\n\nexport interface AuthorProfileReader {\n authorsOf(orgId: string, orgUserIds: string[]): Promise<Record<string, AuthorRecord>>;\n}\n\nexport interface DiscussionService {\n /** Post a root comment or a reply. Lands pending where review is required and\n * the poster is not staff. */\n postComment(orgId: string, input: PostCommentInput): Promise<CommentView>;\n /** Author-only. Throws ForbiddenError for anyone else, staff included. */\n edit(orgId: string, commentId: string, actor: Actor, body: string): Promise<CommentView>;\n /** The author, or anyone whose role is staff. */\n remove(\n orgId: string,\n commentId: string,\n actor: Actor,\n communityId?: string,\n ): Promise<CommentView>;\n /** Staff only. Returns the comment to published. */\n restore(\n orgId: string,\n commentId: string,\n actor: Actor,\n communityId?: string,\n ): Promise<CommentView>;\n /** Staff only. Publishes a pending comment. */\n approve(orgId: string, commentId: string, actor: Actor): Promise<Comment>;\n /** Make a comment published, whichever state it is in. Staff only. */\n publish(orgId: string, commentId: string, actor: Actor): Promise<Comment>;\n\n /** The activity's comments as this reader may see them. */\n activityComments(orgId: string, activityId: string, actor: Actor): Promise<ActivityComments>;\n\n /** One page of top-level comments, oldest first, each with its replies. */\n postComments(\n orgId: string,\n postId: string,\n actor: Actor,\n query: PageQuery,\n ): Promise<Page<CommentView>>;\n\n /**\n * The org's comments, filtered. Staff-scoped: rows carry the author's email\n * and a removed comment's body, neither of which a learner may see, so the\n * edge admits only staff.\n *\n * No rule is applied beyond the filters — this is what exists, not what\n * someone is allowed to read.\n */\n listComments(orgId: string, query: ListCommentsQuery): Promise<Page<CommentListItem>>;\n\n setReaction(\n orgId: string,\n commentId: string,\n actor: Actor,\n emoji: ReactionEmoji | null,\n ): Promise<CommentReactions>;\n\n resolveReports(\n orgId: string,\n commentId: string,\n actor: Actor,\n communityId?: string,\n ): Promise<void>;\n\n /** Accepted even on locked comments — a locked activity can still hold\n * something a moderator needs to see. */\n reportComment(\n orgId: string,\n commentId: string,\n actor: Actor,\n reason: string,\n ): Promise<CommentReport>;\n\n /** Plain read of one comment — no rules applied. */\n getComment(orgId: string, commentId: string): Promise<Comment | null>;\n commenterIds(\n orgId: string,\n target: { postId: string } | { activityId: string },\n ): Promise<string[]>;\n}\n\nexport interface ContentAccessReader {\n hasAccess(orgId: string, orgUserId: string, contentId: string): Promise<boolean>;\n}\n\nexport interface ActivityGetter {\n getActivity(orgId: string, activityId: string): Promise<Activity | null>;\n}\n\nexport interface DiscussionRepository extends PostCommentReader, AuthorProfileReader {\n insertComment(orgId: string, comment: Comment): Promise<Comment>;\n findComment(orgId: string, id: string): Promise<Comment | null>;\n commenterIds(\n orgId: string,\n target: { postId: string } | { activityId: string },\n ): Promise<string[]>;\n updateComment(\n orgId: string,\n id: string,\n patch: Partial<Pick<Comment, 'body' | 'status' | 'removedBy' | 'updatedAt'>>,\n ): Promise<Comment | null>;\n /** Every comment on the activity, oldest first, including removed ones. */\n listByActivity(orgId: string, activityId: string): Promise<Comment[]>;\n /** A page of the post's top-level threads the viewer can see, oldest first, with their replies. `total` counts threads. */\n listThreadsByPost(orgId: string, postId: string, query: PostThreadsQuery): Promise<Page<Comment>>;\n\n reactionsOf(\n orgId: string,\n commentIds: string[],\n viewerId: string,\n ): Promise<Record<string, CommentReactions>>;\n setReaction(\n orgId: string,\n commentId: string,\n orgUserId: string,\n emoji: ReactionEmoji | null,\n ): Promise<boolean>;\n\n /** Returns null when this person has already reported this comment. */\n insertReport(orgId: string, report: CommentReport): Promise<CommentReport | null>;\n /** Every unresolved report against the given comments. */\n listOpenReports(orgId: string, commentIds: string[]): Promise<CommentReport[]>;\n resolveReportsFor(\n orgId: string,\n commentId: string,\n resolved: {\n resolvedAt: Date;\n resolvedBy: string;\n resolution: 'dismissed' | 'removed';\n },\n ): Promise<void>;\n\n listComments(orgId: string, query: ListCommentsQuery): Promise<Page<CommentWithContext>>;\n}\n\n/** Writes that emit events run through this scope so the row and the outbox\n * entry commit in one transaction. */\nexport interface DiscussionWriteScope {\n discussion: DiscussionRepository;\n mentions: MentionsRepository;\n outbox: OutboxAppender;\n}\nexport type DiscussionUnitOfWork = UnitOfWork<DiscussionWriteScope>;\n\nexport interface AuthorRoleReader {\n roleOf(orgId: string, orgUserId: string): Promise<Role | null>;\n rolesOf(orgId: string, orgUserIds: string[]): Promise<Record<string, Role>>;\n}\n\nexport type PostMode = 'draft' | 'submit' | 'schedule';\n\nexport interface CreatePostInput {\n communityId: string;\n spaceId: string;\n title: string;\n body: string;\n topicId: string | null;\n extra: JsonValue;\n coverAssetId: string | null;\n mode: PostMode;\n scheduledAt: Date | null;\n}\n\nexport interface UpdatePostInput {\n title?: string;\n body?: string;\n topicId?: string | null;\n extra?: JsonValue;\n coverAssetId?: string | null;\n}\n\nexport type PostPatch = Partial<\n Pick<\n Post,\n | 'title'\n | 'body'\n | 'topicId'\n | 'extra'\n | 'coverAssetId'\n | 'status'\n | 'scheduledAt'\n | 'publishedAt'\n | 'removedBy'\n | 'pinned'\n | 'commentsClosed'\n | 'reactionsEnabled'\n >\n>;\n\nexport interface FeedQuery {\n page: number;\n pageSize: number;\n topicId?: string;\n}\n\nexport interface PostReactions {\n reactions: ReactionCounts;\n viewerReaction?: ReactionEmoji;\n}\n\nexport type PageQuery = Pick<FeedQuery, 'page' | 'pageSize'>;\n\nexport interface PostThreadsQuery extends PageQuery {\n viewerId: string;\n seesHeld: boolean;\n}\n\nexport interface BookmarkQuery extends PageQuery {\n visibleStatuses: PostStatus[];\n}\n\nexport const MODERATION_QUEUE_LIMIT = 100;\n\nexport interface SpaceModerationQueue {\n pending: PostView[];\n flags: PostFlag[];\n commentFlags: FlaggedComment[];\n}\n\nexport interface PostCommentReader {\n listOpenFlagsForSpace(orgId: string, spaceId: string, limit: number): Promise<FlaggedComment[]>;\n publishedCommentCounts(orgId: string, postIds: string[]): Promise<Record<string, number>>;\n}\n\nexport interface PublishedPost {\n postId: string;\n spaceId: string;\n title: string;\n orgUserId: string;\n commentCount: number;\n}\n\nexport interface PostService {\n visibleTitles(orgId: string, actor: Actor, postIds: string[]): Promise<Record<string, string>>;\n publishedBetween(\n orgId: string,\n communityId: string,\n from: Date,\n to: Date,\n ): Promise<PublishedPost[]>;\n create(orgId: string, actor: Actor, input: CreatePostInput): Promise<PostView>;\n update(orgId: string, postId: string, actor: Actor, patch: UpdatePostInput): Promise<PostView>;\n submit(orgId: string, postId: string, actor: Actor): Promise<PostView>;\n schedule(orgId: string, postId: string, actor: Actor, scheduledAt: Date): Promise<PostView>;\n unschedule(orgId: string, postId: string, actor: Actor): Promise<PostView>;\n publishScheduled(orgId: string, postId: string, now: Date): Promise<void>;\n approve(orgId: string, postId: string, actor: Actor, communityId?: string): Promise<PostView>;\n remove(orgId: string, postId: string, actor: Actor, communityId?: string): Promise<PostView>;\n restore(orgId: string, postId: string, actor: Actor, communityId?: string): Promise<PostView>;\n pin(orgId: string, postId: string, actor: Actor): Promise<PostView>;\n unpin(orgId: string, postId: string, actor: Actor): Promise<PostView>;\n closeComments(orgId: string, postId: string, actor: Actor): Promise<PostView>;\n openComments(orgId: string, postId: string, actor: Actor): Promise<PostView>;\n setReactions(orgId: string, postId: string, actor: Actor, enabled: boolean): Promise<PostView>;\n setReaction(\n orgId: string,\n postId: string,\n actor: Actor,\n emoji: ReactionEmoji | null,\n ): Promise<PostReactions>;\n bookmark(orgId: string, postId: string, actor: Actor): Promise<void>;\n unbookmark(orgId: string, postId: string, actor: Actor): Promise<void>;\n flag(orgId: string, postId: string, actor: Actor, reason: string): Promise<PostFlag>;\n resolveFlags(\n orgId: string,\n postId: string,\n actor: Actor,\n resolution: Extract<PostFlagResolution, 'dismissed'>,\n communityId?: string,\n ): Promise<void>;\n spaceFeed(\n orgId: string,\n actor: Actor,\n communityId: string,\n spaceId: string,\n query: FeedQuery,\n ): Promise<Page<PostView>>;\n memberFeed(\n orgId: string,\n actor: Actor,\n communityId: string,\n query: Pick<FeedQuery, 'page' | 'pageSize'>,\n ): Promise<Page<PostView>>;\n listOwn(\n orgId: string,\n actor: Actor,\n communityId: string,\n query: PageQuery,\n ): Promise<Page<PostView>>;\n listBookmarked(\n orgId: string,\n actor: Actor,\n communityId: string,\n query: PageQuery,\n ): Promise<Page<PostView>>;\n /** Signed URL of the post's cover, for whoever can read the post. */\n coverFile(orgId: string, actor: Actor, postId: string): Promise<string | null>;\n getPost(orgId: string, actor: Actor, postId: string): Promise<PostView>;\n visible(orgId: string, actor: Actor, postId: string): Promise<{ post: Post; space: SpaceDetail }>;\n find(orgId: string, postId: string): Promise<Post | null>;\n moderationQueue(\n orgId: string,\n actor: Actor,\n communityId: string,\n spaceId: string,\n ): Promise<SpaceModerationQueue>;\n}\n\nexport interface PostGuard {\n status: PostStatus;\n scheduledAt?: Date;\n dueBy?: Date;\n}\n\nexport interface PostRepository {\n insert(orgId: string, post: Post): Promise<Post>;\n update(orgId: string, id: string, patch: PostPatch): Promise<Post | null>;\n updateIf(orgId: string, id: string, guard: PostGuard, patch: PostPatch): Promise<Post | null>;\n find(orgId: string, id: string): Promise<Post | null>;\n findMany(orgId: string, ids: string[]): Promise<Post[]>;\n publishedBetween(\n orgId: string,\n communityId: string,\n from: Date,\n to: Date,\n ): Promise<PublishedPost[]>;\n listPublished(orgId: string, spaceIds: string[], query: FeedQuery): Promise<Page<Post>>;\n listByAuthor(\n orgId: string,\n orgUserId: string,\n spaceIds: string[],\n query: PageQuery,\n ): Promise<Page<Post>>;\n listBookmarked(\n orgId: string,\n orgUserId: string,\n spaceIds: string[],\n query: BookmarkQuery,\n ): Promise<Page<Post>>;\n listPending(orgId: string, spaceId: string, limit: number): Promise<Post[]>;\n reactionsOf(\n orgId: string,\n postIds: string[],\n viewerId: string,\n ): Promise<Record<string, PostReactions>>;\n setReaction(\n orgId: string,\n postId: string,\n orgUserId: string,\n emoji: ReactionEmoji | null,\n ): Promise<boolean>;\n bookmark(orgId: string, postId: string, orgUserId: string): Promise<void>;\n unbookmark(orgId: string, postId: string, orgUserId: string): Promise<void>;\n bookmarkedAmong(orgId: string, postIds: string[], orgUserId: string): Promise<string[]>;\n insertFlag(orgId: string, flag: PostFlag): Promise<PostFlag | null>;\n findFlag(orgId: string, postId: string, orgUserId: string): Promise<PostFlag | null>;\n listOpenFlags(orgId: string, spaceId: string, limit: number): Promise<PostFlag[]>;\n resolveFlags(\n orgId: string,\n postId: string,\n resolved: { resolvedAt: Date; resolvedBy: string; resolution: PostFlagResolution },\n ): Promise<void>;\n}\n\nexport interface PostWriteScope {\n posts: PostRepository;\n mentions: MentionsRepository;\n outbox: OutboxAppender;\n}\nexport type PostUnitOfWork = UnitOfWork<PostWriteScope>;\n\nexport interface DigestScheduleReader {\n communitiesWithDigest(): Promise<CommunityDigest[]>;\n}\n\nexport interface DigestRunRepository {\n listRuns(): Promise<DigestRun[]>;\n advance(orgId: string, communityId: string, dueAt: Date): Promise<boolean>;\n}\n\nexport interface DigestWriteScope {\n digests: DigestRunRepository;\n outbox: OutboxAppender;\n}\nexport type DigestUnitOfWork = UnitOfWork<DigestWriteScope>;\n\nexport interface DigestService {\n openDue(now: Date): Promise<void>;\n}\n\nexport interface StartDirectInput {\n communityId: string;\n orgUserId: string;\n body: string | null;\n assetIds: string[];\n}\n\nexport interface StartGroupInput {\n communityId: string;\n orgUserIds: string[];\n}\n\nexport interface SendMessageInput {\n body: string;\n replyToId: string | null;\n assetIds: string[];\n}\n\nexport interface ChatListItem {\n chat: Chat;\n participantIds: string[];\n lastReadMessageId: string | null;\n subscription: SubscriptionView | null;\n unread: number;\n lastMessage: ChatLastMessage | null;\n}\n\nexport type ChatInboxRow = Omit<ChatListItem, 'subscription'>;\n\nexport interface ChatUnread {\n count: number;\n latest: { messageId: string; senderOrgUserId: string; body: string } | null;\n}\n\nexport interface MessageReactions {\n reactions: ReactionCounts;\n viewerReaction?: ReactionEmoji;\n}\n\nexport interface ChatSummary {\n chat: Chat;\n participantIds: string[];\n}\n\nexport interface MessageQueueEntry {\n message: MessageView;\n flags: MessageFlag[];\n chat: Chat;\n participantIds: string[];\n}\n\nexport interface MessageQueue {\n entries: MessageQueueEntry[];\n}\n\nexport interface ChatService {\n startDirect(orgId: string, actor: Actor, input: StartDirectInput): Promise<Chat>;\n startGroup(orgId: string, actor: Actor, input: StartGroupInput): Promise<Chat>;\n addParticipant(orgId: string, actor: Actor, chatId: string, orgUserId: string): Promise<Chat>;\n leave(orgId: string, actor: Actor, chatId: string): Promise<Chat>;\n accept(orgId: string, actor: Actor, chatId: string): Promise<Chat>;\n send(orgId: string, actor: Actor, chatId: string, input: SendMessageInput): Promise<MessageView>;\n editMessage(orgId: string, actor: Actor, messageId: string, body: string): Promise<MessageView>;\n deleteMessage(orgId: string, actor: Actor, messageId: string): Promise<MessageView>;\n setReaction(\n orgId: string,\n actor: Actor,\n messageId: string,\n emoji: ReactionEmoji | null,\n ): Promise<MessageReactions>;\n markRead(orgId: string, actor: Actor, chatId: string, messageId: string): Promise<void>;\n block(orgId: string, actor: Actor, communityId: string, orgUserId: string): Promise<void>;\n unblock(orgId: string, actor: Actor, communityId: string, orgUserId: string): Promise<void>;\n flagMessage(orgId: string, actor: Actor, messageId: string, reason: string): Promise<MessageFlag>;\n resolveMessageFlags(\n orgId: string,\n actor: Actor,\n messageId: string,\n resolution: Extract<MessageFlagResolution, 'dismissed'>,\n communityId?: string,\n ): Promise<void>;\n lock(orgId: string, actor: Actor, chatId: string, communityId?: string): Promise<Chat>;\n unlock(orgId: string, actor: Actor, chatId: string, communityId?: string): Promise<Chat>;\n listChats(\n orgId: string,\n actor: Actor,\n communityId: string,\n query: PageQuery,\n ): Promise<Page<ChatListItem>>;\n chatSummary(\n orgId: string,\n chatId: string,\n ): Promise<{ chat: Chat; participants: ChatUser[] } | null>;\n latestMessageBody(orgId: string, chatId: string): Promise<string | null>;\n unreadInChat(orgId: string, chatId: string, orgUserId: string): Promise<ChatUnread>;\n findMessage(orgId: string, messageId: string): Promise<Message | null>;\n isBlocked(orgId: string, communityId: string, a: string, b: string): Promise<boolean>;\n titlesFor(orgId: string, actor: Actor, chatIds: string[]): Promise<Record<string, string>>;\n messages(\n orgId: string,\n actor: Actor,\n chatId: string,\n query: Pick<FeedQuery, 'page' | 'pageSize'>,\n ): Promise<Page<MessageView>>;\n /** Signed URL of a message attachment: for the chat's participants while the\n * message stands (staff also after deletion), and for staff while it is flagged. */\n attachmentFile(\n orgId: string,\n actor: Actor,\n chatId: string,\n messageId: string,\n assetId: string,\n ): Promise<string | null>;\n messageQueue(orgId: string, actor: Actor, communityId: string): Promise<MessageQueue>;\n}\n\nexport interface ChatRepository {\n lockDirectPair(\n orgId: string,\n communityId: string,\n orgUserId: string,\n otherOrgUserId: string,\n ): Promise<void>;\n findDirect(\n orgId: string,\n communityId: string,\n orgUserId: string,\n otherOrgUserId: string,\n ): Promise<Chat | null>;\n insertChat(orgId: string, chat: Chat): Promise<Chat>;\n updateChat(\n orgId: string,\n id: string,\n patch: Partial<Pick<Chat, 'status' | 'lockedAt' | 'lockedBy'>>,\n ): Promise<Chat | null>;\n findChat(orgId: string, id: string): Promise<Chat | null>;\n listForUser(\n orgId: string,\n communityId: string,\n orgUserId: string,\n query: PageQuery,\n ): Promise<Page<ChatInboxRow>>;\n participants(orgId: string, chatId: string): Promise<ChatUser[]>;\n join(orgId: string, user: ChatUser): Promise<void>;\n markLeft(orgId: string, chatId: string, orgUserId: string, leftAt: Date): Promise<void>;\n setRead(orgId: string, chatId: string, orgUserId: string, messageId: string): Promise<void>;\n unreadFor(orgId: string, chatId: string, orgUserId: string): Promise<ChatUnread>;\n insertMessage(orgId: string, message: Message): Promise<Message>;\n updateMessage(\n orgId: string,\n id: string,\n patch: Partial<Pick<Message, 'body' | 'editedAt' | 'deletedAt'>>,\n ): Promise<Message | null>;\n findMessage(orgId: string, id: string): Promise<Message | null>;\n listMessages(\n orgId: string,\n chatId: string,\n query: Pick<FeedQuery, 'page' | 'pageSize'>,\n ): Promise<Page<Message>>;\n attach(orgId: string, communityId: string, messageId: string, assetIds: string[]): Promise<void>;\n attachmentsOf(orgId: string, messageIds: string[]): Promise<Record<string, string[]>>;\n blockedWith(\n orgId: string,\n communityId: string,\n orgUserId: string,\n otherIds: string[],\n ): Promise<string[]>;\n setReaction(\n orgId: string,\n communityId: string,\n messageId: string,\n orgUserId: string,\n emoji: ReactionEmoji | null,\n ): Promise<boolean>;\n reactionsOf(\n orgId: string,\n messageIds: string[],\n viewerId: string,\n ): Promise<Record<string, MessageReactions>>;\n block(\n orgId: string,\n communityId: string,\n blockerOrgUserId: string,\n blockedOrgUserId: string,\n ): Promise<void>;\n unblock(\n orgId: string,\n communityId: string,\n blockerOrgUserId: string,\n blockedOrgUserId: string,\n ): Promise<void>;\n isBlocked(orgId: string, communityId: string, a: string, b: string): Promise<boolean>;\n anyBlockedAmong(orgId: string, communityId: string, orgUserIds: string[]): Promise<boolean>;\n insertFlag(orgId: string, flag: MessageFlag): Promise<MessageFlag | null>;\n findFlag(orgId: string, messageId: string, orgUserId: string): Promise<MessageFlag | null>;\n listOpenFlags(orgId: string, communityId: string, limit?: number): Promise<MessageFlag[]>;\n messagesByIds(orgId: string, ids: string[]): Promise<Message[]>;\n chatSummaries(orgId: string, chatIds: string[]): Promise<ChatSummary[]>;\n resolveFlags(\n orgId: string,\n messageId: string,\n resolved: { resolvedAt: Date; resolvedBy: string; resolution: MessageFlagResolution },\n ): Promise<void>;\n resolveOpenFlagsForChat(\n orgId: string,\n chatId: string,\n resolved: { resolvedAt: Date; resolvedBy: string; resolution: MessageFlagResolution },\n ): Promise<void>;\n}\n\nexport interface ChatWriteScope {\n chats: ChatRepository;\n mentions: MentionsRepository;\n outbox: OutboxAppender;\n}\nexport type ChatUnitOfWork = UnitOfWork<ChatWriteScope>;\n\nexport type SubscriptionInput =\n { state: 'following' } | { state: 'muted'; mutedUntil?: Date | null } | { state: 'none' };\n\nexport type MuteInput = Exclude<SubscriptionInput, { state: 'following' }>;\n\nexport interface SubscriptionService {\n setPost(orgId: string, actor: Actor, postId: string, input: SubscriptionInput): Promise<void>;\n setSpace(orgId: string, actor: Actor, spaceId: string, input: MuteInput): Promise<void>;\n setChat(orgId: string, actor: Actor, chatId: string, input: MuteInput): Promise<void>;\n viewerSubscriptions(\n orgId: string,\n orgUserId: string,\n itemType: SubscriptionItemType,\n itemIds: string[],\n at: Date,\n ): Promise<Record<string, SubscriptionView>>;\n followerIds(orgId: string, postId: string): Promise<string[]>;\n mutedAmong(orgId: string, item: SubjectRef, orgUserIds: string[], at: Date): Promise<string[]>;\n itemSubscriptions(orgId: string, item: SubjectRef): Promise<Subscription[]>;\n}\n\nexport interface SubscriptionsRepository {\n upsert(orgId: string, subscription: Subscription): Promise<void>;\n remove(\n orgId: string,\n orgUserId: string,\n itemType: SubscriptionItemType,\n itemId: string,\n ): Promise<void>;\n forViewer(\n orgId: string,\n orgUserId: string,\n itemType: SubscriptionItemType,\n itemIds: string[],\n ): Promise<Subscription[]>;\n ofItem(orgId: string, itemType: SubscriptionItemType, itemId: string): Promise<Subscription[]>;\n followerIds(orgId: string, postId: string): Promise<string[]>;\n mutedAmong(\n orgId: string,\n itemType: SubscriptionItemType,\n itemId: string,\n orgUserIds: string[],\n at: Date,\n ): Promise<string[]>;\n}\n\nexport interface MentionsRepository {\n ofItem(orgId: string, itemType: MentionItemType, itemId: string): Promise<string[]>;\n forItems(\n orgId: string,\n itemType: MentionItemType,\n itemIds: string[],\n ): Promise<Record<string, string[]>>;\n sync(\n orgId: string,\n itemType: MentionItemType,\n itemId: string,\n orgUserIds: string[],\n at: Date,\n ): Promise<string[]>;\n removeItem(orgId: string, itemType: MentionItemType, itemId: string): Promise<void>;\n}\n"],"mappings":";AA6SA,MAAa,yBAAyB"}
@@ -0,0 +1,113 @@
1
+ import { Page } from "../types/shared.js";
2
+ import { Logger } from "../types/ports.js";
3
+ import { SpaceDetail } from "../types/schemas/content.js";
4
+ import { Post, PostFlag, PostFlagResolution, PostView, ReactionEmoji } from "../types/schemas/discussion.js";
5
+ import "../types/index.js";
6
+ import { Scheduler } from "../shared/ports.js";
7
+ import { FileUrls } from "../assets/file-urls.js";
8
+ import { AssetFiles, MemberImageReader } from "../assets/ports.js";
9
+ import "../assets/index.js";
10
+ import { Actor } from "../shared/actor.js";
11
+ import { CommunityReader } from "../content/ports.js";
12
+ import "../content/index.js";
13
+ import { CommunityAccessReader } from "../entitlements/ports.js";
14
+ import "../entitlements/index.js";
15
+ import "./types.js";
16
+ import { AuthorProfileReader, AuthorRoleReader, CreatePostInput, FeedQuery, MentionsRepository, PageQuery, PostCommentReader, PostReactions, PostRepository, PostService, PostUnitOfWork, PublishedPost, SpaceModerationQueue, SubscriptionsRepository, UpdatePostInput } from "./ports.js";
17
+ //#region src/discussion/posts.d.ts
18
+ type PostServiceParams = {
19
+ repo: PostRepository;
20
+ access: CommunityAccessReader;
21
+ content: CommunityReader;
22
+ authors: AuthorRoleReader;
23
+ profiles: AuthorProfileReader;
24
+ comments: PostCommentReader;
25
+ subscriptions: SubscriptionsRepository;
26
+ mentions: MentionsRepository;
27
+ images: MemberImageReader & Pick<AssetFiles, 'signFile'>;
28
+ fileUrls: FileUrls;
29
+ uow: PostUnitOfWork;
30
+ scheduler: Pick<Scheduler, 'at' | 'cancel'>;
31
+ logger?: Logger;
32
+ };
33
+ declare class PostServiceImpl implements PostService {
34
+ private readonly repo;
35
+ private readonly access;
36
+ private readonly content;
37
+ private readonly authors;
38
+ private readonly profiles;
39
+ private readonly comments;
40
+ private readonly subscriptions;
41
+ private readonly mentions;
42
+ private readonly images;
43
+ private readonly fileUrls;
44
+ private readonly uow;
45
+ private readonly scheduler;
46
+ private readonly logger;
47
+ constructor(params: PostServiceParams);
48
+ visibleTitles(orgId: string, actor: Actor, postIds: string[]): Promise<Record<string, string>>;
49
+ publishedBetween(orgId: string, communityId: string, from: Date, to: Date): Promise<PublishedPost[]>;
50
+ create(orgId: string, actor: Actor, input: CreatePostInput): Promise<PostView>;
51
+ update(orgId: string, postId: string, actor: Actor, patch: UpdatePostInput): Promise<PostView>;
52
+ submit(orgId: string, postId: string, actor: Actor): Promise<PostView>;
53
+ schedule(orgId: string, postId: string, actor: Actor, scheduledAt: Date): Promise<PostView>;
54
+ unschedule(orgId: string, postId: string, actor: Actor): Promise<PostView>;
55
+ publishScheduled(orgId: string, postId: string, now: Date): Promise<void>;
56
+ private arm;
57
+ private disarm;
58
+ approve(orgId: string, postId: string, actor: Actor, communityId?: string): Promise<PostView>;
59
+ remove(orgId: string, postId: string, actor: Actor, communityId?: string): Promise<PostView>;
60
+ restore(orgId: string, postId: string, actor: Actor, communityId?: string): Promise<PostView>;
61
+ pin(orgId: string, postId: string, actor: Actor): Promise<PostView>;
62
+ unpin(orgId: string, postId: string, actor: Actor): Promise<PostView>;
63
+ closeComments(orgId: string, postId: string, actor: Actor): Promise<PostView>;
64
+ openComments(orgId: string, postId: string, actor: Actor): Promise<PostView>;
65
+ setReactions(orgId: string, postId: string, actor: Actor, enabled: boolean): Promise<PostView>;
66
+ setReaction(orgId: string, postId: string, actor: Actor, emoji: ReactionEmoji | null): Promise<PostReactions>;
67
+ bookmark(orgId: string, postId: string, actor: Actor): Promise<void>;
68
+ unbookmark(orgId: string, postId: string, actor: Actor): Promise<void>;
69
+ flag(orgId: string, postId: string, actor: Actor, reason: string): Promise<PostFlag>;
70
+ resolveFlags(orgId: string, postId: string, actor: Actor, resolution: Extract<PostFlagResolution, 'dismissed'>, communityId?: string): Promise<void>;
71
+ spaceFeed(orgId: string, actor: Actor, communityId: string, spaceId: string, query: FeedQuery): Promise<Page<PostView>>;
72
+ memberFeed(orgId: string, actor: Actor, communityId: string, query: Pick<FeedQuery, 'page' | 'pageSize'>): Promise<Page<PostView>>;
73
+ private openSpaceIdsIn;
74
+ listOwn(orgId: string, actor: Actor, communityId: string, query: PageQuery): Promise<Page<PostView>>;
75
+ listBookmarked(orgId: string, actor: Actor, communityId: string, query: PageQuery): Promise<Page<PostView>>;
76
+ coverFile(orgId: string, actor: Actor, postId: string): Promise<string | null>;
77
+ getPost(orgId: string, actor: Actor, postId: string): Promise<PostView>;
78
+ moderationQueue(orgId: string, actor: Actor, communityId: string, spaceId: string): Promise<SpaceModerationQueue>;
79
+ visible(orgId: string, actor: Actor, postId: string): Promise<{
80
+ post: Post;
81
+ space: SpaceDetail;
82
+ }>;
83
+ find(orgId: string, postId: string): Promise<Post | null>;
84
+ private submittable;
85
+ private feedPage;
86
+ private viewOf;
87
+ private viewsOf;
88
+ private commitSubmission;
89
+ private moderate;
90
+ private mustUpdate;
91
+ private initial;
92
+ private submissionOf;
93
+ private requireOwnedImage;
94
+ private openSpace;
95
+ private requireOpen;
96
+ private loadVisible;
97
+ private canSee;
98
+ private visibleStatuses;
99
+ private canModerate;
100
+ private assertAuthor;
101
+ private topicFits;
102
+ private assertTopic;
103
+ private patchChanges;
104
+ private mentionable;
105
+ private openersOf;
106
+ private landed;
107
+ private published;
108
+ private updated;
109
+ private meta;
110
+ }
111
+ //#endregion
112
+ export { PostServiceImpl, PostServiceParams };
113
+ //# sourceMappingURL=posts.d.ts.map