@headless-lms/core 0.3.0 → 0.5.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 +24 -18
  2. package/dist/assets/file-urls.d.ts +11 -7
  3. package/dist/assets/file-urls.js +14 -4
  4. package/dist/assets/file-urls.js.map +1 -1
  5. package/dist/assets/index.d.ts +5 -4
  6. package/dist/assets/index.js +4 -3
  7. package/dist/assets/jobs.d.ts +15 -0
  8. package/dist/assets/jobs.js +30 -0
  9. package/dist/assets/jobs.js.map +1 -0
  10. package/dist/assets/ports.d.ts +37 -17
  11. package/dist/assets/service.d.ts +30 -15
  12. package/dist/assets/service.js +229 -59
  13. package/dist/assets/service.js.map +1 -1
  14. package/dist/automations/actions.js +2 -63
  15. package/dist/automations/actions.js.map +1 -1
  16. package/dist/automations/catalog.d.ts +1 -2
  17. package/dist/automations/catalog.js +137 -65
  18. package/dist/automations/catalog.js.map +1 -1
  19. package/dist/automations/community-derivations.js +20 -20
  20. package/dist/automations/community-derivations.js.map +1 -1
  21. package/dist/automations/community-mail.d.ts +36 -20
  22. package/dist/automations/community-mail.js +32 -24
  23. package/dist/automations/community-mail.js.map +1 -1
  24. package/dist/automations/event-permissions.d.ts +12 -0
  25. package/dist/automations/event-permissions.js +38 -0
  26. package/dist/automations/event-permissions.js.map +1 -0
  27. package/dist/automations/events.d.ts +0 -10
  28. package/dist/automations/index.d.ts +12 -4
  29. package/dist/automations/index.js +9 -1
  30. package/dist/automations/notifications/applicable.d.ts +8 -0
  31. package/dist/automations/notifications/applicable.js +12 -0
  32. package/dist/automations/notifications/applicable.js.map +1 -0
  33. package/dist/automations/notifications/debounce.d.ts +25 -0
  34. package/dist/automations/notifications/debounce.js +93 -0
  35. package/dist/automations/notifications/debounce.js.map +1 -0
  36. package/dist/automations/notifications/defaults.d.ts +8 -0
  37. package/dist/automations/notifications/defaults.js +346 -0
  38. package/dist/automations/notifications/defaults.js.map +1 -0
  39. package/dist/automations/notifications/delivery.d.ts +28 -0
  40. package/dist/automations/notifications/delivery.js +88 -0
  41. package/dist/automations/notifications/delivery.js.map +1 -0
  42. package/dist/automations/notifications/processor.d.ts +64 -0
  43. package/dist/automations/notifications/processor.js +270 -0
  44. package/dist/automations/notifications/processor.js.map +1 -0
  45. package/dist/automations/notifications/rules/content.d.ts +6 -0
  46. package/dist/automations/notifications/rules/content.js +96 -0
  47. package/dist/automations/notifications/rules/content.js.map +1 -0
  48. package/dist/automations/notifications/rules/discussion.d.ts +7 -0
  49. package/dist/automations/notifications/rules/discussion.js +678 -0
  50. package/dist/automations/notifications/rules/discussion.js.map +1 -0
  51. package/dist/automations/notifications/rules/entitlements.d.ts +6 -0
  52. package/dist/automations/notifications/rules/entitlements.js +276 -0
  53. package/dist/automations/notifications/rules/entitlements.js.map +1 -0
  54. package/dist/automations/notifications/rules/events.d.ts +9 -0
  55. package/dist/automations/notifications/rules/events.js +27 -0
  56. package/dist/automations/notifications/rules/events.js.map +1 -0
  57. package/dist/automations/notifications/rules/index.d.ts +6 -0
  58. package/dist/automations/notifications/rules/index.js +15 -0
  59. package/dist/automations/notifications/rules/index.js.map +1 -0
  60. package/dist/automations/notifications/rules/organizations.d.ts +6 -0
  61. package/dist/automations/notifications/rules/organizations.js +35 -0
  62. package/dist/automations/notifications/rules/organizations.js.map +1 -0
  63. package/dist/automations/notifications/rules/recipients.d.ts +32 -0
  64. package/dist/automations/notifications/rules/recipients.js +98 -0
  65. package/dist/automations/notifications/rules/recipients.js.map +1 -0
  66. package/dist/automations/notifications/service.d.ts +29 -0
  67. package/dist/automations/notifications/service.js +45 -0
  68. package/dist/automations/notifications/service.js.map +1 -0
  69. package/dist/automations/notifications/types.d.ts +7 -0
  70. package/dist/automations/notifications/types.js +560 -0
  71. package/dist/automations/notifications/types.js.map +1 -0
  72. package/dist/automations/ports.d.ts +134 -21
  73. package/dist/automations/service.d.ts +30 -30
  74. package/dist/automations/service.js +111 -144
  75. package/dist/automations/service.js.map +1 -1
  76. package/dist/content/event-sight.d.ts +11 -0
  77. package/dist/content/event-sight.js +11 -0
  78. package/dist/content/event-sight.js.map +1 -0
  79. package/dist/content/events.d.ts +437 -46
  80. package/dist/content/events.js +45 -1
  81. package/dist/content/events.js.map +1 -1
  82. package/dist/content/index.d.ts +8 -4
  83. package/dist/content/index.js +5 -1
  84. package/dist/content/jobs.d.ts +16 -0
  85. package/dist/content/jobs.js +37 -0
  86. package/dist/content/jobs.js.map +1 -0
  87. package/dist/content/model.d.ts +2 -2
  88. package/dist/content/notification-categories.d.ts +37 -0
  89. package/dist/content/notification-categories.js +73 -0
  90. package/dist/content/notification-categories.js.map +1 -0
  91. package/dist/content/ports.d.ts +132 -68
  92. package/dist/content/service.d.ts +100 -68
  93. package/dist/content/service.js +703 -146
  94. package/dist/content/service.js.map +1 -1
  95. package/dist/content/titles.d.ts +25 -0
  96. package/dist/content/titles.js +82 -0
  97. package/dist/content/titles.js.map +1 -0
  98. package/dist/content/types.d.ts +2 -2
  99. package/dist/discussion/authors.d.ts +4 -2
  100. package/dist/discussion/authors.js +9 -5
  101. package/dist/discussion/authors.js.map +1 -1
  102. package/dist/discussion/chats.d.ts +63 -39
  103. package/dist/discussion/chats.js +455 -190
  104. package/dist/discussion/chats.js.map +1 -1
  105. package/dist/discussion/digests.d.ts +24 -0
  106. package/dist/discussion/digests.js +104 -0
  107. package/dist/discussion/digests.js.map +1 -0
  108. package/dist/discussion/events.d.ts +227 -23
  109. package/dist/discussion/events.js +114 -19
  110. package/dist/discussion/events.js.map +1 -1
  111. package/dist/discussion/index.d.ts +12 -4
  112. package/dist/discussion/index.js +9 -2
  113. package/dist/discussion/jobs.d.ts +16 -0
  114. package/dist/discussion/jobs.js +40 -0
  115. package/dist/discussion/jobs.js.map +1 -0
  116. package/dist/discussion/live.d.ts +106 -0
  117. package/dist/discussion/live.js +642 -0
  118. package/dist/discussion/live.js.map +1 -0
  119. package/dist/discussion/mentions.d.ts +11 -0
  120. package/dist/discussion/mentions.js +35 -0
  121. package/dist/discussion/mentions.js.map +1 -0
  122. package/dist/discussion/ports.d.ts +279 -114
  123. package/dist/discussion/ports.js.map +1 -1
  124. package/dist/discussion/posts.d.ts +60 -44
  125. package/dist/discussion/posts.js +453 -287
  126. package/dist/discussion/posts.js.map +1 -1
  127. package/dist/discussion/rules.d.ts +18 -0
  128. package/dist/discussion/rules.js +22 -0
  129. package/dist/discussion/rules.js.map +1 -0
  130. package/dist/discussion/service.d.ts +50 -35
  131. package/dist/discussion/service.js +333 -205
  132. package/dist/discussion/service.js.map +1 -1
  133. package/dist/discussion/subscriptions.d.ts +37 -0
  134. package/dist/discussion/subscriptions.js +115 -0
  135. package/dist/discussion/subscriptions.js.map +1 -0
  136. package/dist/discussion/titles.d.ts +28 -0
  137. package/dist/discussion/titles.js +98 -0
  138. package/dist/discussion/titles.js.map +1 -0
  139. package/dist/entitlements/events.d.ts +20 -20
  140. package/dist/entitlements/index.d.ts +5 -4
  141. package/dist/entitlements/index.js +3 -2
  142. package/dist/entitlements/jobs.d.ts +12 -0
  143. package/dist/entitlements/jobs.js +18 -0
  144. package/dist/entitlements/jobs.js.map +1 -0
  145. package/dist/entitlements/model.d.ts +2 -2
  146. package/dist/entitlements/ports.d.ts +68 -38
  147. package/dist/entitlements/rules.d.ts +3 -6
  148. package/dist/entitlements/rules.js +5 -10
  149. package/dist/entitlements/rules.js.map +1 -1
  150. package/dist/entitlements/service.d.ts +43 -28
  151. package/dist/entitlements/service.js +387 -160
  152. package/dist/entitlements/service.js.map +1 -1
  153. package/dist/identity/devices.d.ts +30 -0
  154. package/dist/identity/devices.js +109 -0
  155. package/dist/identity/devices.js.map +1 -0
  156. package/dist/identity/index.d.ts +6 -5
  157. package/dist/identity/index.js +4 -3
  158. package/dist/identity/model.d.ts +2 -2
  159. package/dist/identity/notification-settings.d.ts +28 -0
  160. package/dist/identity/notification-settings.js +114 -0
  161. package/dist/identity/notification-settings.js.map +1 -0
  162. package/dist/identity/ports.d.ts +80 -56
  163. package/dist/identity/service.d.ts +8 -7
  164. package/dist/identity/service.js +17 -13
  165. package/dist/identity/service.js.map +1 -1
  166. package/dist/integrations/ports.d.ts +9 -8
  167. package/dist/integrations/service.d.ts +9 -8
  168. package/dist/integrations/service.js +33 -16
  169. package/dist/integrations/service.js.map +1 -1
  170. package/dist/organizations/default-roles.d.ts +22 -0
  171. package/dist/organizations/default-roles.js +89 -0
  172. package/dist/organizations/default-roles.js.map +1 -0
  173. package/dist/organizations/effective-permissions.d.ts +45 -0
  174. package/dist/organizations/effective-permissions.js +113 -0
  175. package/dist/organizations/effective-permissions.js.map +1 -0
  176. package/dist/organizations/events.d.ts +320 -181
  177. package/dist/organizations/events.js +90 -18
  178. package/dist/organizations/events.js.map +1 -1
  179. package/dist/organizations/index.d.ts +15 -10
  180. package/dist/organizations/index.js +8 -3
  181. package/dist/organizations/members.d.ts +66 -8
  182. package/dist/organizations/members.js.map +1 -1
  183. package/dist/organizations/notification-catalog.d.ts +31 -0
  184. package/dist/organizations/notification-catalog.js +32 -0
  185. package/dist/organizations/notification-catalog.js.map +1 -0
  186. package/dist/organizations/notification-categories.d.ts +34 -0
  187. package/dist/organizations/notification-categories.js +66 -0
  188. package/dist/organizations/notification-categories.js.map +1 -0
  189. package/dist/organizations/org-roles.d.ts +33 -0
  190. package/dist/organizations/org-roles.js +49 -0
  191. package/dist/organizations/org-roles.js.map +1 -0
  192. package/dist/organizations/permissions.d.ts +311 -0
  193. package/dist/organizations/permissions.js +327 -0
  194. package/dist/organizations/permissions.js.map +1 -0
  195. package/dist/organizations/ports.d.ts +187 -65
  196. package/dist/organizations/service.d.ts +104 -35
  197. package/dist/organizations/service.js +1247 -323
  198. package/dist/organizations/service.js.map +1 -1
  199. package/dist/organizations/testing.d.ts +18 -0
  200. package/dist/organizations/testing.js +37 -0
  201. package/dist/organizations/testing.js.map +1 -0
  202. package/dist/organizations/types.d.ts +5 -16
  203. package/dist/progress/index.d.ts +2 -2
  204. package/dist/progress/ports.d.ts +11 -3
  205. package/dist/progress/service.d.ts +6 -5
  206. package/dist/progress/service.js +23 -8
  207. package/dist/progress/service.js.map +1 -1
  208. package/dist/reporting/admin/index.d.ts +6 -4
  209. package/dist/reporting/admin/model.d.ts +36 -3
  210. package/dist/reporting/admin/ports.d.ts +39 -14
  211. package/dist/reporting/admin/service.d.ts +32 -11
  212. package/dist/reporting/admin/service.js +102 -18
  213. package/dist/reporting/admin/service.js.map +1 -1
  214. package/dist/reporting/events/index.d.ts +27 -0
  215. package/dist/reporting/events/index.js +20 -0
  216. package/dist/reporting/events/index.js.map +1 -0
  217. package/dist/reporting/member/calendar.d.ts +11 -0
  218. package/dist/reporting/member/calendar.js +75 -0
  219. package/dist/reporting/member/calendar.js.map +1 -0
  220. package/dist/reporting/member/index.d.ts +3 -2
  221. package/dist/reporting/member/model.d.ts +80 -26
  222. package/dist/reporting/member/ports.d.ts +50 -28
  223. package/dist/reporting/member/service.d.ts +81 -35
  224. package/dist/reporting/member/service.js +438 -145
  225. package/dist/reporting/member/service.js.map +1 -1
  226. package/dist/shared/actor.d.ts +22 -3
  227. package/dist/shared/errors.d.ts +15 -2
  228. package/dist/shared/errors.js +21 -2
  229. package/dist/shared/errors.js.map +1 -1
  230. package/dist/shared/id.d.ts +7 -0
  231. package/dist/shared/id.js +8 -1
  232. package/dist/shared/id.js.map +1 -1
  233. package/dist/shared/logger.d.ts +2 -1
  234. package/dist/shared/logger.js +10 -1
  235. package/dist/shared/logger.js.map +1 -1
  236. package/dist/shared/mailer.d.ts +11 -1
  237. package/dist/shared/mailer.js +32 -5
  238. package/dist/shared/mailer.js.map +1 -1
  239. package/dist/shared/notification-categories.d.ts +16 -0
  240. package/dist/shared/notification-categories.js +94 -0
  241. package/dist/shared/notification-categories.js.map +1 -0
  242. package/dist/shared/notification-settings.d.ts +14 -0
  243. package/dist/shared/notification-settings.js +61 -0
  244. package/dist/shared/notification-settings.js.map +1 -0
  245. package/dist/shared/ports.d.ts +68 -2
  246. package/dist/shared/settings.d.ts +2 -1
  247. package/dist/shared/settings.js +1 -0
  248. package/dist/shared/settings.js.map +1 -1
  249. package/dist/types/automations.d.ts +8 -2
  250. package/dist/types/content.d.ts +2 -2
  251. package/dist/types/discussion.d.ts +4 -5
  252. package/dist/types/email-templates.d.ts +119 -6
  253. package/dist/types/entitlements.d.ts +2 -2
  254. package/dist/types/identity.d.ts +14 -17
  255. package/dist/types/index.d.ts +13 -12
  256. package/dist/types/index.js +2 -1
  257. package/dist/types/notifications.d.ts +157 -0
  258. package/dist/types/notifications.js +1 -0
  259. package/dist/types/organizations.d.ts +3 -2
  260. package/dist/types/ports.d.ts +8 -1
  261. package/dist/types/ports.js +7 -1
  262. package/dist/types/ports.js.map +1 -0
  263. package/dist/types/schemas/assets.d.ts +5 -5
  264. package/dist/types/schemas/assets.js +4 -3
  265. package/dist/types/schemas/assets.js.map +1 -1
  266. package/dist/types/schemas/automations.d.ts +1 -10
  267. package/dist/types/schemas/automations.js +1 -3
  268. package/dist/types/schemas/automations.js.map +1 -1
  269. package/dist/types/schemas/content.d.ts +596 -11
  270. package/dist/types/schemas/content.js +225 -9
  271. package/dist/types/schemas/content.js.map +1 -1
  272. package/dist/types/schemas/discussion.d.ts +362 -66
  273. package/dist/types/schemas/discussion.js +123 -15
  274. package/dist/types/schemas/discussion.js.map +1 -1
  275. package/dist/types/schemas/entitlements.d.ts +14 -1
  276. package/dist/types/schemas/entitlements.js +11 -4
  277. package/dist/types/schemas/entitlements.js.map +1 -1
  278. package/dist/types/schemas/identity.d.ts +1 -85
  279. package/dist/types/schemas/identity.js +1 -42
  280. package/dist/types/schemas/identity.js.map +1 -1
  281. package/dist/types/schemas/index.d.ts +8 -7
  282. package/dist/types/schemas/index.js +8 -7
  283. package/dist/types/schemas/notifications.d.ts +96 -0
  284. package/dist/types/schemas/notifications.js +57 -0
  285. package/dist/types/schemas/notifications.js.map +1 -0
  286. package/dist/types/schemas/organizations.d.ts +96 -60
  287. package/dist/types/schemas/organizations.js +78 -27
  288. package/dist/types/schemas/organizations.js.map +1 -1
  289. package/dist/types/schemas/progress.d.ts +0 -1
  290. package/dist/types/schemas/progress.js +0 -1
  291. package/dist/types/schemas/progress.js.map +1 -1
  292. package/package.json +1 -1
  293. package/dist/automations/community-notifications.d.ts +0 -16
  294. package/dist/automations/community-notifications.js +0 -84
  295. package/dist/automations/community-notifications.js.map +0 -1
  296. package/dist/automations/seeds.d.ts +0 -17
  297. package/dist/automations/seeds.js +0 -235
  298. package/dist/automations/seeds.js.map +0 -1
  299. package/dist/identity/notifications.d.ts +0 -27
  300. package/dist/identity/notifications.js +0 -101
  301. package/dist/identity/notifications.js.map +0 -1
  302. package/dist/organizations/access-scopes.d.ts +0 -19
  303. package/dist/organizations/access-scopes.js +0 -89
  304. package/dist/organizations/access-scopes.js.map +0 -1
  305. package/dist/organizations/roles.d.ts +0 -19
  306. package/dist/organizations/roles.js +0 -82
  307. package/dist/organizations/roles.js.map +0 -1
@@ -1,13 +1,13 @@
1
1
  import { JsonValue, Page } from "../types/shared.js";
2
- import { Activity, SpaceDetail } from "../types/schemas/content.js";
3
- import { Role } from "../types/schemas/organizations.js";
4
- import { Chat, ChatLastMessage, ChatUser, CommentListItem, CommentView, CommentsConfig, FlaggedComment, ListCommentsQuery, Message, MessageFlag, MessageFlagResolution, MessageView, Post, PostFlag, PostFlagResolution, PostStatus, PostView, ReactionCounts, ReactionEmoji } from "../types/schemas/discussion.js";
5
- import { Comment, CommentReport } from "../types/discussion.js";
2
+ import { Activity, DmSettings, SpaceDetail } from "../types/schemas/content.js";
3
+ import { Chat, ChatLastMessage, ChatLiveEvent, ChatUnreadSummary, ChatUser, CommentListItem, CommentView, CommentsConfig, DigestRun, FlaggedComment, FlaggedCommentView, ListCommentsQuery, LiveMessage, MentionItemType, Message, MessageFlag, MessageFlagResolution, MessageView, Post, PostFlag, PostFlagResolution, PostStatus, PostView, ReactionCounts, ReactionEmoji, Subscription, SubscriptionItemType, SubscriptionView } from "../types/schemas/discussion.js";
4
+ import { Comment, CommentAuthor, CommentReport } from "../types/discussion.js";
5
+ import { SubjectRef } from "../types/notifications.js";
6
6
  import "../types/index.js";
7
+ import { Actor, EffectivePermissions } from "../shared/actor.js";
7
8
  import { OutboxAppender, UnitOfWork } from "../shared/ports.js";
8
- import { Actor } from "../shared/actor.js";
9
+ import { CommunityDigest } from "../content/ports.js";
9
10
  import "../content/index.js";
10
- import "../organizations/index.js";
11
11
  import "./model.js";
12
12
  import "./types.js";
13
13
  //#region src/discussion/ports.d.ts
@@ -27,7 +27,6 @@ interface CommentWithContext {
27
27
  activityTitle: string;
28
28
  }
29
29
  type PostCommentInput = {
30
- actor: Actor;
31
30
  parentId: string | null;
32
31
  body: string;
33
32
  } & ({
@@ -38,74 +37,109 @@ type PostCommentInput = {
38
37
  activityId?: undefined;
39
38
  });
40
39
  /** A profile row as the repository loads it, keyed by `org_users.id`. `email`
41
- * is stripped before comments are served and kept only for the staff list. */
40
+ * is stripped before comments are served and kept only for the moderation list. */
42
41
  interface AuthorRecord {
43
42
  id: string;
44
43
  firstName: string | null;
45
44
  lastName: string | null;
46
45
  email: string;
47
46
  image: string | null;
48
- role: Role;
49
47
  displayName: string | null;
50
48
  headline: string | null;
51
49
  imageAssetOrgId: string | null;
52
50
  imageAssetId: string | null;
53
51
  }
52
+ interface ItemTitle {
53
+ title: string;
54
+ excerpt?: string;
55
+ }
56
+ interface ItemTitles {
57
+ titlesFor(refs: SubjectRef[], actor: Actor): Promise<Record<string, ItemTitle>>;
58
+ }
54
59
  interface AuthorProfileReader {
55
60
  authorsOf(orgId: string, orgUserIds: string[]): Promise<Record<string, AuthorRecord>>;
56
61
  }
62
+ /** Where a comment must be for a moderation call: in one community, on a
63
+ * lesson, or anywhere the actor can reach. */
64
+ type CommentWithin = {
65
+ communityId: string;
66
+ } | 'lesson' | 'any';
57
67
  interface DiscussionService {
58
68
  /** Post a root comment or a reply. Lands pending where review is required and
59
- * the poster is not staff. */
60
- postComment(orgId: string, input: PostCommentInput): Promise<CommentView>;
61
- /** Author-only. Throws ForbiddenError for anyone else, staff included. */
62
- edit(orgId: string, commentId: string, actor: Actor, body: string): Promise<CommentView>;
63
- /** The author, or anyone whose role is staff. */
64
- remove(orgId: string, commentId: string, actor: Actor, communityId?: string): Promise<CommentView>;
65
- /** Staff only. Returns the comment to published. */
66
- restore(orgId: string, commentId: string, actor: Actor, communityId?: string): Promise<CommentView>;
67
- /** Staff only. Publishes a pending comment. */
68
- approve(orgId: string, commentId: string, actor: Actor): Promise<Comment>;
69
- /** Make a comment published, whichever state it is in. Staff only. */
70
- publish(orgId: string, commentId: string, actor: Actor): Promise<Comment>;
69
+ * the poster does not skip review. */
70
+ postComment(input: PostCommentInput, actor: Actor): Promise<CommentView>;
71
+ /** Reply to a lesson comment as a moderator of its course; a reply to a reply
72
+ * attaches to its root. */
73
+ replyToComment(commentId: string, body: string, actor: Actor): Promise<CommentView>;
74
+ /** Author-only. Throws ForbiddenError for anyone else, moderators included. */
75
+ edit(commentId: string, body: string, actor: Actor): Promise<CommentView>;
76
+ /** The author, or a moderator of where the comment is. */
77
+ remove(commentId: string, within: CommentWithin, actor: Actor): Promise<CommentView>;
78
+ /** A moderator of where the comment is. Returns the comment to published. */
79
+ restore(commentId: string, within: CommentWithin, actor: Actor): Promise<CommentView>;
80
+ /** A moderator of where the comment is. Publishes a pending comment. */
81
+ approve(commentId: string, actor: Actor): Promise<Comment>;
82
+ /** Make a comment published, whichever state it is in. A moderator of where
83
+ * the comment is. */
84
+ publish(commentId: string, within: CommentWithin, actor: Actor): Promise<Comment>;
71
85
  /** The activity's comments as this reader may see them. */
72
- activityComments(orgId: string, activityId: string, actor: Actor): Promise<ActivityComments>;
86
+ activityComments(activityId: string, actor: Actor): Promise<ActivityComments>;
73
87
  /** One page of top-level comments, oldest first, each with its replies. */
74
- postComments(orgId: string, postId: string, actor: Actor, query: PageQuery): Promise<Page<CommentView>>;
75
- /**
76
- * The org's comments, filtered. Staff-scoped: rows carry the author's email
77
- * and a removed comment's body, neither of which a learner may see, so the
78
- * edge admits only staff.
79
- *
80
- * No rule is applied beyond the filters — this is what exists, not what
81
- * someone is allowed to read.
82
- */
83
- listComments(orgId: string, query: ListCommentsQuery): Promise<Page<CommentListItem>>;
84
- setReaction(orgId: string, commentId: string, actor: Actor, emoji: ReactionEmoji | null): Promise<CommentReactions>;
85
- resolveReports(orgId: string, commentId: string, actor: Actor, communityId?: string): Promise<void>;
88
+ postComments(postId: string, query: PageQuery, actor: Actor): Promise<Page<CommentView>>;
89
+ /** The lesson comments of the courses the actor moderates, filtered. Rows
90
+ * carry the author's email and a removed comment's body. */
91
+ listComments(query: ListCommentsQuery, actor: Actor): Promise<Page<CommentListItem>>;
92
+ setReaction(commentId: string, emoji: ReactionEmoji | null, actor: Actor): Promise<CommentReactions>;
93
+ resolveReports(commentId: string, within: CommentWithin, actor: Actor): Promise<void>;
86
94
  /** Accepted even on locked comments — a locked activity can still hold
87
95
  * something a moderator needs to see. */
88
- reportComment(orgId: string, commentId: string, actor: Actor, reason: string): Promise<CommentReport>;
96
+ reportComment(commentId: string, reason: string, actor: Actor): Promise<CommentReport>;
89
97
  /** Plain read of one comment — no rules applied. */
90
98
  getComment(orgId: string, commentId: string): Promise<Comment | null>;
91
- sendDigest(orgId: string, now: Date): Promise<void>;
99
+ commenterIds(orgId: string, target: {
100
+ postId: string;
101
+ } | {
102
+ activityId: string;
103
+ }): Promise<string[]>;
92
104
  }
93
105
  interface ContentAccessReader {
94
106
  hasAccess(orgId: string, orgUserId: string, contentId: string): Promise<boolean>;
95
107
  }
96
108
  interface ActivityGetter {
97
- getActivity(orgId: string, activityId: string): Promise<Activity | null>;
109
+ getActivity(activityId: string, actor: Actor): Promise<Activity | null>;
110
+ }
111
+ interface SpaceReader {
112
+ getSpace(spaceId: string, actor: Actor): Promise<SpaceDetail | null>;
113
+ getDmSettings(communityId: string, actor: Actor): Promise<DmSettings | null>;
114
+ }
115
+ interface OpenableSpace {
116
+ id: string;
117
+ communityId: string;
118
+ accessRule: SpaceDetail['accessRule'];
119
+ }
120
+ interface CommunityAccess {
121
+ isCommunityMember(communityId: string, actor: Actor): Promise<boolean>;
122
+ canOpenSpace(spaceId: string, actor: Actor): Promise<boolean>;
123
+ canOpen(space: OpenableSpace, actor: Actor): Promise<boolean>;
124
+ openSpaceIds(communityId: string, actor: Actor): Promise<string[]>;
125
+ communityMembersAmong(orgId: string, communityId: string, orgUserIds: string[]): Promise<string[]>;
126
+ spaceOpenersAmong(orgId: string, spaceId: string, orgUserIds: string[]): Promise<string[]>;
98
127
  }
99
128
  interface DiscussionRepository extends PostCommentReader, AuthorProfileReader {
100
129
  insertComment(orgId: string, comment: Comment): Promise<Comment>;
101
130
  findComment(orgId: string, id: string): Promise<Comment | null>;
131
+ commenterIds(orgId: string, target: {
132
+ postId: string;
133
+ } | {
134
+ activityId: string;
135
+ }): Promise<string[]>;
102
136
  updateComment(orgId: string, id: string, patch: Partial<Pick<Comment, 'body' | 'status' | 'removedBy' | 'updatedAt'>>): Promise<Comment | null>;
103
137
  /** Every comment on the activity, oldest first, including removed ones. */
104
138
  listByActivity(orgId: string, activityId: string): Promise<Comment[]>;
105
139
  /** A page of the post's top-level threads the viewer can see, oldest first, with their replies. `total` counts threads. */
106
140
  listThreadsByPost(orgId: string, postId: string, query: PostThreadsQuery): Promise<Page<Comment>>;
107
141
  reactionsOf(orgId: string, commentIds: string[], viewerId: string): Promise<Record<string, CommentReactions>>;
108
- setReaction(orgId: string, commentId: string, orgUserId: string, emoji: ReactionEmoji | null): Promise<void>;
142
+ setReaction(orgId: string, commentId: string, orgUserId: string, emoji: ReactionEmoji | null): Promise<boolean>;
109
143
  /** Returns null when this person has already reported this comment. */
110
144
  insertReport(orgId: string, report: CommentReport): Promise<CommentReport | null>;
111
145
  /** Every unresolved report against the given comments. */
@@ -115,18 +149,21 @@ interface DiscussionRepository extends PostCommentReader, AuthorProfileReader {
115
149
  resolvedBy: string;
116
150
  resolution: 'dismissed' | 'removed';
117
151
  }): Promise<void>;
118
- listComments(orgId: string, query: ListCommentsQuery): Promise<Page<CommentWithContext>>;
152
+ /** `courseIds` limits the rows to those courses; undefined means every course. */
153
+ listComments(orgId: string, query: ListCommentsQuery, courseIds?: string[]): Promise<Page<CommentWithContext>>;
119
154
  }
120
155
  /** Writes that emit events run through this scope so the row and the outbox
121
156
  * entry commit in one transaction. */
122
157
  interface DiscussionWriteScope {
123
158
  discussion: DiscussionRepository;
159
+ mentions: MentionsRepository;
124
160
  outbox: OutboxAppender;
125
161
  }
126
162
  type DiscussionUnitOfWork = UnitOfWork<DiscussionWriteScope>;
127
- interface AuthorRoleReader {
128
- roleOf(orgId: string, orgUserId: string): Promise<Role | null>;
129
- rolesOf(orgId: string, orgUserIds: string[]): Promise<Record<string, Role>>;
163
+ interface AuthorPermissionReader {
164
+ permissionsOfMany(orgId: string, orgUserIds: string[]): Promise<Map<string, EffectivePermissions>>;
165
+ actorFor(orgId: string, orgUserId: string): Promise<Actor | null>;
166
+ systemActor(orgId: string): Actor;
130
167
  }
131
168
  type PostMode = 'draft' | 'submit' | 'schedule';
132
169
  interface CreatePostInput {
@@ -169,68 +206,74 @@ declare const MODERATION_QUEUE_LIMIT = 100;
169
206
  interface SpaceModerationQueue {
170
207
  pending: PostView[];
171
208
  flags: PostFlag[];
172
- commentFlags: FlaggedComment[];
209
+ commentFlags: FlaggedCommentView[];
173
210
  }
174
211
  interface PostCommentReader {
175
212
  listOpenFlagsForSpace(orgId: string, spaceId: string, limit: number): Promise<FlaggedComment[]>;
176
213
  publishedCommentCounts(orgId: string, postIds: string[]): Promise<Record<string, number>>;
177
214
  }
215
+ interface PublishedPost {
216
+ postId: string;
217
+ spaceId: string;
218
+ title: string;
219
+ orgUserId: string;
220
+ commentCount: number;
221
+ }
178
222
  interface PostService {
179
- visibleTitles(orgId: string, actor: Actor, postIds: string[]): Promise<Record<string, string>>;
180
- create(orgId: string, actor: Actor, input: CreatePostInput): Promise<PostView>;
181
- update(orgId: string, postId: string, actor: Actor, patch: UpdatePostInput): Promise<PostView>;
182
- submit(orgId: string, postId: string, actor: Actor): Promise<PostView>;
183
- schedule(orgId: string, postId: string, actor: Actor, scheduledAt: Date): Promise<PostView>;
184
- approve(orgId: string, postId: string, actor: Actor, communityId?: string): Promise<PostView>;
185
- remove(orgId: string, postId: string, actor: Actor, communityId?: string): Promise<PostView>;
186
- restore(orgId: string, postId: string, actor: Actor, communityId?: string): Promise<PostView>;
187
- pin(orgId: string, postId: string, actor: Actor): Promise<PostView>;
188
- unpin(orgId: string, postId: string, actor: Actor): Promise<PostView>;
189
- closeComments(orgId: string, postId: string, actor: Actor): Promise<PostView>;
190
- openComments(orgId: string, postId: string, actor: Actor): Promise<PostView>;
191
- setReactions(orgId: string, postId: string, actor: Actor, enabled: boolean): Promise<PostView>;
192
- setReaction(orgId: string, postId: string, actor: Actor, emoji: ReactionEmoji | null): Promise<PostReactions>;
193
- follow(orgId: string, postId: string, actor: Actor): Promise<void>;
194
- unfollow(orgId: string, postId: string, actor: Actor): Promise<void>;
195
- bookmark(orgId: string, postId: string, actor: Actor): Promise<void>;
196
- unbookmark(orgId: string, postId: string, actor: Actor): Promise<void>;
197
- flag(orgId: string, postId: string, actor: Actor, reason: string): Promise<PostFlag>;
198
- resolveFlags(orgId: string, postId: string, actor: Actor, resolution: Extract<PostFlagResolution, 'dismissed'>, communityId?: string): Promise<void>;
199
- spaceFeed(orgId: string, actor: Actor, communityId: string, spaceId: string, query: FeedQuery): Promise<Page<PostView>>;
200
- memberFeed(orgId: string, actor: Actor, communityId: string, query: Pick<FeedQuery, 'page' | 'pageSize'>): Promise<Page<PostView>>;
201
- listOwn(orgId: string, actor: Actor, communityId: string, query: PageQuery): Promise<Page<PostView>>;
202
- listBookmarked(orgId: string, actor: Actor, communityId: string, query: PageQuery): Promise<Page<PostView>>;
223
+ visibleTitles(postIds: string[], actor: Actor): Promise<Record<string, string>>;
224
+ publishedBetween(orgId: string, communityId: string, from: Date, to: Date): Promise<PublishedPost[]>;
225
+ create(input: CreatePostInput, actor: Actor): Promise<PostView>;
226
+ update(postId: string, patch: UpdatePostInput, actor: Actor): Promise<PostView>;
227
+ submit(postId: string, actor: Actor): Promise<PostView>;
228
+ schedule(postId: string, scheduledAt: Date, actor: Actor): Promise<PostView>;
229
+ unschedule(postId: string, actor: Actor): Promise<PostView>;
230
+ publishScheduled(orgId: string, postId: string, now: Date): Promise<void>;
231
+ approve(postId: string, communityId: string | null, actor: Actor): Promise<PostView>;
232
+ remove(postId: string, communityId: string | null, actor: Actor): Promise<PostView>;
233
+ restore(postId: string, communityId: string | null, actor: Actor): Promise<PostView>;
234
+ pin(postId: string, actor: Actor): Promise<PostView>;
235
+ unpin(postId: string, actor: Actor): Promise<PostView>;
236
+ closeComments(postId: string, actor: Actor): Promise<PostView>;
237
+ openComments(postId: string, actor: Actor): Promise<PostView>;
238
+ setReactions(postId: string, enabled: boolean, actor: Actor): Promise<PostView>;
239
+ setReaction(postId: string, emoji: ReactionEmoji | null, actor: Actor): Promise<PostReactions>;
240
+ bookmark(postId: string, actor: Actor): Promise<void>;
241
+ unbookmark(postId: string, actor: Actor): Promise<void>;
242
+ flag(postId: string, reason: string, actor: Actor): Promise<PostFlag>;
243
+ resolveFlags(postId: string, resolution: Extract<PostFlagResolution, 'dismissed'>, communityId: string | null, actor: Actor): Promise<void>;
244
+ spaceFeed(communityId: string, spaceId: string, query: FeedQuery, actor: Actor): Promise<Page<PostView>>;
245
+ memberFeed(communityId: string, query: Pick<FeedQuery, 'page' | 'pageSize'>, actor: Actor): Promise<Page<PostView>>;
246
+ listOwn(communityId: string, query: PageQuery, actor: Actor): Promise<Page<PostView>>;
247
+ listBookmarked(communityId: string, query: PageQuery, actor: Actor): Promise<Page<PostView>>;
203
248
  /** Signed URL of the post's cover, for whoever can read the post. */
204
- coverFile(orgId: string, actor: Actor, postId: string): Promise<string | null>;
205
- getPost(orgId: string, actor: Actor, postId: string): Promise<PostView>;
206
- visible(orgId: string, actor: Actor, postId: string): Promise<{
249
+ coverFile(postId: string, actor: Actor): Promise<string>;
250
+ assetFile(postId: string, assetId: string, actor: Actor): Promise<string>;
251
+ getPost(postId: string, actor: Actor): Promise<PostView>;
252
+ visible(postId: string, actor: Actor): Promise<{
207
253
  post: Post;
208
254
  space: SpaceDetail;
209
255
  }>;
210
256
  find(orgId: string, postId: string): Promise<Post | null>;
211
- followerIds(orgId: string, postId: string): Promise<string[]>;
212
- moderationQueue(orgId: string, actor: Actor, communityId: string, spaceId: string): Promise<SpaceModerationQueue>;
213
- publishDue(orgId: string, now: Date): Promise<void>;
214
- hasDue(orgId: string, now: Date): Promise<boolean>;
257
+ moderationQueue(communityId: string, spaceId: string, actor: Actor): Promise<SpaceModerationQueue>;
258
+ }
259
+ interface PostGuard {
260
+ status: PostStatus;
261
+ scheduledAt?: Date;
262
+ dueBy?: Date;
215
263
  }
216
264
  interface PostRepository {
217
265
  insert(orgId: string, post: Post): Promise<Post>;
218
266
  update(orgId: string, id: string, patch: PostPatch): Promise<Post | null>;
267
+ updateIf(orgId: string, id: string, guard: PostGuard, patch: PostPatch): Promise<Post | null>;
219
268
  find(orgId: string, id: string): Promise<Post | null>;
220
269
  findMany(orgId: string, ids: string[]): Promise<Post[]>;
270
+ publishedBetween(orgId: string, communityId: string, from: Date, to: Date): Promise<PublishedPost[]>;
221
271
  listPublished(orgId: string, spaceIds: string[], query: FeedQuery): Promise<Page<Post>>;
222
272
  listByAuthor(orgId: string, orgUserId: string, spaceIds: string[], query: PageQuery): Promise<Page<Post>>;
223
273
  listBookmarked(orgId: string, orgUserId: string, spaceIds: string[], query: BookmarkQuery): Promise<Page<Post>>;
224
274
  listPending(orgId: string, spaceId: string, limit: number): Promise<Post[]>;
225
- /** Due scheduled posts in id order, after `afterId` when given. */
226
- listDue(orgId: string, now: Date, limit: number, afterId?: string): Promise<Post[]>;
227
- hasDue(orgId: string, now: Date): Promise<boolean>;
228
275
  reactionsOf(orgId: string, postIds: string[], viewerId: string): Promise<Record<string, PostReactions>>;
229
- setReaction(orgId: string, postId: string, orgUserId: string, emoji: ReactionEmoji | null): Promise<void>;
230
- follow(orgId: string, postId: string, orgUserId: string): Promise<void>;
231
- unfollow(orgId: string, postId: string, orgUserId: string): Promise<void>;
232
- followerIds(orgId: string, postId: string): Promise<string[]>;
233
- followedAmong(orgId: string, postIds: string[], orgUserId: string): Promise<string[]>;
276
+ setReaction(orgId: string, postId: string, orgUserId: string, emoji: ReactionEmoji | null): Promise<boolean>;
234
277
  bookmark(orgId: string, postId: string, orgUserId: string): Promise<void>;
235
278
  unbookmark(orgId: string, postId: string, orgUserId: string): Promise<void>;
236
279
  bookmarkedAmong(orgId: string, postIds: string[], orgUserId: string): Promise<string[]>;
@@ -245,9 +288,25 @@ interface PostRepository {
245
288
  }
246
289
  interface PostWriteScope {
247
290
  posts: PostRepository;
291
+ mentions: MentionsRepository;
248
292
  outbox: OutboxAppender;
249
293
  }
250
294
  type PostUnitOfWork = UnitOfWork<PostWriteScope>;
295
+ interface DigestScheduleReader {
296
+ communitiesWithDigest(): Promise<CommunityDigest[]>;
297
+ }
298
+ interface DigestRunRepository {
299
+ listRuns(): Promise<DigestRun[]>;
300
+ advance(orgId: string, communityId: string, dueAt: Date): Promise<boolean>;
301
+ }
302
+ interface DigestWriteScope {
303
+ digests: DigestRunRepository;
304
+ outbox: OutboxAppender;
305
+ }
306
+ type DigestUnitOfWork = UnitOfWork<DigestWriteScope>;
307
+ interface DigestService {
308
+ openDue(now: Date): Promise<void>;
309
+ }
251
310
  interface StartDirectInput {
252
311
  communityId: string;
253
312
  orgUserId: string;
@@ -262,16 +321,62 @@ interface SendMessageInput {
262
321
  body: string;
263
322
  replyToId: string | null;
264
323
  assetIds: string[];
324
+ clientId?: string;
265
325
  }
266
326
  interface ChatListItem {
267
327
  chat: Chat;
268
328
  participantIds: string[];
329
+ participants: CommentAuthor[];
269
330
  lastReadMessageId: string | null;
270
- muted: boolean;
271
- mutedUntil: Date | null;
331
+ subscription: SubscriptionView | null;
272
332
  unread: number;
273
333
  lastMessage: ChatLastMessage | null;
274
334
  }
335
+ type ChatInboxRow = Omit<ChatListItem, 'subscription' | 'participants'>;
336
+ /** `requests`: true for only the pending requests to the member, false for
337
+ * everything else, omitted for every chat. */
338
+ interface ChatListQuery extends PageQuery {
339
+ requests?: boolean;
340
+ }
341
+ interface ReactionChange {
342
+ inserted: boolean;
343
+ changed: boolean;
344
+ }
345
+ interface ChatMemberState {
346
+ chatId: string;
347
+ unread: number;
348
+ lastReadMessageId: string | null;
349
+ subscription: SubscriptionView | null;
350
+ }
351
+ /** A member's state in each chat they are in in one community, with their
352
+ * unread total there. */
353
+ interface ChatMemberStates {
354
+ orgUserId: string;
355
+ unread: ChatUnreadSummary;
356
+ chats: ChatMemberState[];
357
+ }
358
+ interface LiveChat {
359
+ chat: Chat;
360
+ participantIds: string[];
361
+ participants: CommentAuthor[];
362
+ }
363
+ /** What the chat hub reads for each change, as current state. */
364
+ interface ChatLiveReads {
365
+ activeParticipants(orgId: string, chatId: string): Promise<string[]>;
366
+ messageViews(orgId: string, messageIds: string[]): Promise<LiveMessage[]>;
367
+ messageReactions(orgId: string, messageId: string): Promise<ReactionCounts>;
368
+ /** The chat with its active participants and their profiles. */
369
+ liveChat(orgId: string, chatId: string): Promise<LiveChat | null>;
370
+ memberStates(orgId: string, communityId: string, orgUserIds: string[]): Promise<ChatMemberStates[]>;
371
+ }
372
+ interface ChatUnread {
373
+ count: number;
374
+ latest: {
375
+ messageId: string;
376
+ senderOrgUserId: string;
377
+ body: string;
378
+ } | null;
379
+ }
275
380
  interface MessageReactions {
276
381
  reactions: ReactionCounts;
277
382
  viewerReaction?: ReactionEmoji;
@@ -289,38 +394,48 @@ interface MessageQueueEntry {
289
394
  interface MessageQueue {
290
395
  entries: MessageQueueEntry[];
291
396
  }
292
- interface ChatService {
293
- startDirect(orgId: string, actor: Actor, input: StartDirectInput): Promise<Chat>;
294
- startGroup(orgId: string, actor: Actor, input: StartGroupInput): Promise<Chat>;
295
- addParticipant(orgId: string, actor: Actor, chatId: string, orgUserId: string): Promise<Chat>;
296
- leave(orgId: string, actor: Actor, chatId: string): Promise<Chat>;
297
- accept(orgId: string, actor: Actor, chatId: string): Promise<Chat>;
298
- send(orgId: string, actor: Actor, chatId: string, input: SendMessageInput): Promise<MessageView>;
299
- editMessage(orgId: string, actor: Actor, messageId: string, body: string): Promise<MessageView>;
300
- deleteMessage(orgId: string, actor: Actor, messageId: string): Promise<MessageView>;
301
- setReaction(orgId: string, actor: Actor, messageId: string, emoji: ReactionEmoji | null): Promise<MessageReactions>;
302
- markRead(orgId: string, actor: Actor, chatId: string, messageId: string): Promise<void>;
303
- mute(orgId: string, actor: Actor, chatId: string, until?: Date): Promise<void>;
304
- unmute(orgId: string, actor: Actor, chatId: string): Promise<void>;
305
- block(orgId: string, actor: Actor, communityId: string, orgUserId: string): Promise<void>;
306
- unblock(orgId: string, actor: Actor, communityId: string, orgUserId: string): Promise<void>;
307
- flagMessage(orgId: string, actor: Actor, messageId: string, reason: string): Promise<MessageFlag>;
308
- resolveMessageFlags(orgId: string, actor: Actor, messageId: string, resolution: Extract<MessageFlagResolution, 'dismissed'>, communityId?: string): Promise<void>;
309
- lock(orgId: string, actor: Actor, chatId: string, communityId?: string): Promise<Chat>;
310
- unlock(orgId: string, actor: Actor, chatId: string, communityId?: string): Promise<Chat>;
311
- listChats(orgId: string, actor: Actor, communityId: string, query: PageQuery): Promise<Page<ChatListItem>>;
397
+ interface ChatService extends ChatLiveReads {
398
+ startDirect(input: StartDirectInput, actor: Actor): Promise<Chat>;
399
+ startGroup(input: StartGroupInput, actor: Actor): Promise<Chat>;
400
+ addParticipant(chatId: string, orgUserId: string, actor: Actor): Promise<Chat>;
401
+ leave(chatId: string, actor: Actor): Promise<Chat>;
402
+ accept(chatId: string, actor: Actor): Promise<Chat>;
403
+ send(chatId: string, input: SendMessageInput, actor: Actor): Promise<MessageView>;
404
+ editMessage(messageId: string, body: string, actor: Actor): Promise<MessageView>;
405
+ deleteMessage(messageId: string, actor: Actor): Promise<MessageView>;
406
+ setReaction(messageId: string, emoji: ReactionEmoji | null, actor: Actor): Promise<MessageReactions>;
407
+ markRead(chatId: string, messageId: string, actor: Actor): Promise<void>;
408
+ block(communityId: string, orgUserId: string, actor: Actor): Promise<void>;
409
+ unblock(communityId: string, orgUserId: string, actor: Actor): Promise<void>;
410
+ flagMessage(messageId: string, reason: string, actor: Actor): Promise<MessageFlag>;
411
+ resolveMessageFlags(messageId: string, resolution: Extract<MessageFlagResolution, 'dismissed'>, communityId: string | null, actor: Actor): Promise<void>;
412
+ lock(chatId: string, communityId: string | null, actor: Actor): Promise<Chat>;
413
+ unlock(chatId: string, communityId: string | null, actor: Actor): Promise<Chat>;
414
+ listChats(communityId: string, query: ChatListQuery, actor: Actor): Promise<Page<ChatListItem>>;
415
+ /** One live stream of the member's chat events in the community. */
416
+ watch(communityId: string, actor: Actor): Promise<AsyncIterable<ChatLiveEvent>>;
417
+ chat(chatId: string, actor: Actor): Promise<ChatListItem>;
418
+ directChat(communityId: string, orgUserId: string, actor: Actor): Promise<ChatListItem>;
419
+ unreadSummary(communityId: string, actor: Actor): Promise<ChatUnreadSummary>;
420
+ typing(chatId: string, actor: Actor): Promise<void>;
421
+ /** The messages as every participant sees them: a deleted message without
422
+ * its body, attachments or mentions, and no viewer reaction. */
423
+ messageViews(orgId: string, messageIds: string[]): Promise<LiveMessage[]>;
312
424
  chatSummary(orgId: string, chatId: string): Promise<{
313
425
  chat: Chat;
314
426
  participants: ChatUser[];
315
427
  } | null>;
316
428
  latestMessageBody(orgId: string, chatId: string): Promise<string | null>;
429
+ unreadInChat(orgId: string, chatId: string, orgUserId: string): Promise<ChatUnread>;
430
+ findMessage(orgId: string, messageId: string): Promise<Message | null>;
317
431
  isBlocked(orgId: string, communityId: string, a: string, b: string): Promise<boolean>;
318
- titlesFor(orgId: string, actor: Actor, chatIds: string[]): Promise<Record<string, string>>;
319
- messages(orgId: string, actor: Actor, chatId: string, query: Pick<FeedQuery, 'page' | 'pageSize'>): Promise<Page<MessageView>>;
432
+ titlesFor(chatIds: string[], actor: Actor): Promise<Record<string, string>>;
433
+ messages(chatId: string, query: Pick<FeedQuery, 'page' | 'pageSize'>, actor: Actor): Promise<Page<MessageView>>;
320
434
  /** Signed URL of a message attachment: for the chat's participants while the
321
- * message stands (staff also after deletion), and for staff while it is flagged. */
322
- attachmentFile(orgId: string, actor: Actor, chatId: string, messageId: string, assetId: string): Promise<string | null>;
323
- messageQueue(orgId: string, actor: Actor, communityId: string): Promise<MessageQueue>;
435
+ * message stands (moderators also after deletion), and for moderators while
436
+ * it is flagged. */
437
+ attachmentFile(chatId: string, messageId: string, assetId: string, actor: Actor): Promise<string>;
438
+ messageQueue(communityId: string, actor: Actor): Promise<MessageQueue>;
324
439
  }
325
440
  interface ChatRepository {
326
441
  lockDirectPair(orgId: string, communityId: string, orgUserId: string, otherOrgUserId: string): Promise<void>;
@@ -328,12 +443,22 @@ interface ChatRepository {
328
443
  insertChat(orgId: string, chat: Chat): Promise<Chat>;
329
444
  updateChat(orgId: string, id: string, patch: Partial<Pick<Chat, 'status' | 'lockedAt' | 'lockedBy'>>): Promise<Chat | null>;
330
445
  findChat(orgId: string, id: string): Promise<Chat | null>;
331
- listForUser(orgId: string, communityId: string, orgUserId: string, query: PageQuery): Promise<Page<ChatListItem>>;
446
+ listForUser(orgId: string, communityId: string, orgUserId: string, query: ChatListQuery): Promise<Page<ChatInboxRow>>;
447
+ /** The member's inbox row for one chat they are in, or null. */
448
+ inboxRow(orgId: string, chatId: string, orgUserId: string): Promise<ChatInboxRow | null>;
332
449
  participants(orgId: string, chatId: string): Promise<ChatUser[]>;
450
+ activeParticipants(orgId: string, chatId: string): Promise<string[]>;
333
451
  join(orgId: string, user: ChatUser): Promise<void>;
334
452
  markLeft(orgId: string, chatId: string, orgUserId: string, leftAt: Date): Promise<void>;
335
- setRead(orgId: string, chatId: string, orgUserId: string, messageId: string): Promise<void>;
336
- setMuted(orgId: string, chatId: string, orgUserId: string, muted: boolean, mutedUntil: Date | null): Promise<void>;
453
+ /** Moves the read mark to the message only when it is newer (by created
454
+ * time, then id) than the current one. True when it moved. */
455
+ setRead(orgId: string, chatId: string, orgUserId: string, messageId: string): Promise<boolean>;
456
+ unreadFor(orgId: string, chatId: string, orgUserId: string): Promise<ChatUnread>;
457
+ /** Unread messages across the member's chats in the community that are
458
+ * neither muted at `at` nor pending requests to them, plus those requests. */
459
+ unreadSummary(orgId: string, communityId: string, orgUserId: string, at: Date): Promise<ChatUnreadSummary>;
460
+ memberStates(orgId: string, communityId: string, orgUserIds: string[], at: Date): Promise<ChatMemberStates[]>;
461
+ findByClientId(orgId: string, chatId: string, orgUserId: string, clientId: string): Promise<Message | null>;
337
462
  insertMessage(orgId: string, message: Message): Promise<Message>;
338
463
  updateMessage(orgId: string, id: string, patch: Partial<Pick<Message, 'body' | 'editedAt' | 'deletedAt'>>): Promise<Message | null>;
339
464
  findMessage(orgId: string, id: string): Promise<Message | null>;
@@ -341,7 +466,7 @@ interface ChatRepository {
341
466
  attach(orgId: string, communityId: string, messageId: string, assetIds: string[]): Promise<void>;
342
467
  attachmentsOf(orgId: string, messageIds: string[]): Promise<Record<string, string[]>>;
343
468
  blockedWith(orgId: string, communityId: string, orgUserId: string, otherIds: string[]): Promise<string[]>;
344
- setReaction(orgId: string, communityId: string, messageId: string, orgUserId: string, emoji: ReactionEmoji | null): Promise<void>;
469
+ setReaction(orgId: string, communityId: string, messageId: string, orgUserId: string, emoji: ReactionEmoji | null): Promise<ReactionChange>;
345
470
  reactionsOf(orgId: string, messageIds: string[], viewerId: string): Promise<Record<string, MessageReactions>>;
346
471
  block(orgId: string, communityId: string, blockerOrgUserId: string, blockedOrgUserId: string): Promise<void>;
347
472
  unblock(orgId: string, communityId: string, blockerOrgUserId: string, blockedOrgUserId: string): Promise<void>;
@@ -365,9 +490,49 @@ interface ChatRepository {
365
490
  }
366
491
  interface ChatWriteScope {
367
492
  chats: ChatRepository;
493
+ mentions: MentionsRepository;
368
494
  outbox: OutboxAppender;
369
495
  }
370
496
  type ChatUnitOfWork = UnitOfWork<ChatWriteScope>;
497
+ type SubscriptionInput = {
498
+ state: 'following';
499
+ } | {
500
+ state: 'muted';
501
+ mutedUntil?: Date | null;
502
+ } | {
503
+ state: 'none';
504
+ };
505
+ type MuteInput = Exclude<SubscriptionInput, {
506
+ state: 'following';
507
+ }>;
508
+ interface SubscriptionWriteScope {
509
+ subscriptions: SubscriptionsRepository;
510
+ outbox: OutboxAppender;
511
+ }
512
+ type SubscriptionUnitOfWork = UnitOfWork<SubscriptionWriteScope>;
513
+ interface SubscriptionService {
514
+ setPost(postId: string, input: SubscriptionInput, actor: Actor): Promise<void>;
515
+ setSpace(spaceId: string, input: MuteInput, actor: Actor): Promise<void>;
516
+ setChat(chatId: string, input: MuteInput, actor: Actor): Promise<void>;
517
+ viewerSubscriptions(orgId: string, orgUserId: string, itemType: SubscriptionItemType, itemIds: string[], at: Date): Promise<Record<string, SubscriptionView>>;
518
+ followerIds(orgId: string, postId: string): Promise<string[]>;
519
+ mutedAmong(orgId: string, item: SubjectRef, orgUserIds: string[], at: Date): Promise<string[]>;
520
+ itemSubscriptions(orgId: string, item: SubjectRef): Promise<Subscription[]>;
521
+ }
522
+ interface SubscriptionsRepository {
523
+ upsert(orgId: string, subscription: Subscription): Promise<void>;
524
+ remove(orgId: string, orgUserId: string, itemType: SubscriptionItemType, itemId: string): Promise<void>;
525
+ forViewer(orgId: string, orgUserId: string, itemType: SubscriptionItemType, itemIds: string[]): Promise<Subscription[]>;
526
+ ofItem(orgId: string, itemType: SubscriptionItemType, itemId: string): Promise<Subscription[]>;
527
+ followerIds(orgId: string, postId: string): Promise<string[]>;
528
+ mutedAmong(orgId: string, itemType: SubscriptionItemType, itemId: string, orgUserIds: string[], at: Date): Promise<string[]>;
529
+ }
530
+ interface MentionsRepository {
531
+ ofItem(orgId: string, itemType: MentionItemType, itemId: string): Promise<string[]>;
532
+ forItems(orgId: string, itemType: MentionItemType, itemIds: string[]): Promise<Record<string, string[]>>;
533
+ sync(orgId: string, itemType: MentionItemType, itemId: string, orgUserIds: string[], at: Date): Promise<string[]>;
534
+ removeItem(orgId: string, itemType: MentionItemType, itemId: string): Promise<void>;
535
+ }
371
536
  //#endregion
372
- export { ActivityComments, ActivityGetter, type Actor, AuthorProfileReader, AuthorRecord, AuthorRoleReader, BookmarkQuery, ChatListItem, ChatRepository, ChatService, ChatSummary, ChatUnitOfWork, ChatWriteScope, CommentReactions, CommentWithContext, ContentAccessReader, CreatePostInput, DiscussionRepository, DiscussionService, DiscussionUnitOfWork, DiscussionWriteScope, FeedQuery, MODERATION_QUEUE_LIMIT, MessageQueue, MessageQueueEntry, MessageReactions, PageQuery, PostCommentInput, PostCommentReader, PostMode, PostPatch, PostReactions, PostRepository, PostService, PostThreadsQuery, PostUnitOfWork, PostWriteScope, SendMessageInput, SpaceModerationQueue, StartDirectInput, StartGroupInput, UpdatePostInput };
537
+ export { ActivityComments, ActivityGetter, type Actor, AuthorPermissionReader, AuthorProfileReader, AuthorRecord, BookmarkQuery, ChatInboxRow, ChatListItem, ChatListQuery, ChatLiveReads, ChatMemberState, ChatMemberStates, ChatRepository, ChatService, ChatSummary, ChatUnitOfWork, ChatUnread, ChatWriteScope, CommentReactions, CommentWithContext, CommentWithin, CommunityAccess, ContentAccessReader, CreatePostInput, DigestRunRepository, DigestScheduleReader, DigestService, DigestUnitOfWork, DigestWriteScope, DiscussionRepository, DiscussionService, DiscussionUnitOfWork, DiscussionWriteScope, FeedQuery, ItemTitle, ItemTitles, LiveChat, MODERATION_QUEUE_LIMIT, MentionsRepository, MessageQueue, MessageQueueEntry, MessageReactions, MuteInput, OpenableSpace, PageQuery, PostCommentInput, PostCommentReader, PostGuard, PostMode, PostPatch, PostReactions, PostRepository, PostService, PostThreadsQuery, PostUnitOfWork, PostWriteScope, PublishedPost, ReactionChange, SendMessageInput, SpaceModerationQueue, SpaceReader, StartDirectInput, StartGroupInput, SubscriptionInput, SubscriptionService, SubscriptionUnitOfWork, SubscriptionWriteScope, SubscriptionsRepository, UpdatePostInput };
373
538
  //# sourceMappingURL=ports.d.ts.map