@headless-lms/core 0.2.0 → 0.3.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 (225) hide show
  1. package/dist/assets/events.d.ts +39 -21
  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 +5 -4
  8. package/dist/assets/index.js +3 -2
  9. package/dist/assets/model.d.ts +2 -2
  10. package/dist/assets/ports.d.ts +45 -15
  11. package/dist/assets/service.d.ts +39 -7
  12. package/dist/assets/service.js +291 -38
  13. package/dist/assets/service.js.map +1 -1
  14. package/dist/automations/actions.d.ts +13 -24
  15. package/dist/automations/actions.js +168 -43
  16. package/dist/automations/actions.js.map +1 -1
  17. package/dist/automations/catalog.d.ts +2 -1
  18. package/dist/automations/catalog.js +229 -13
  19. package/dist/automations/catalog.js.map +1 -1
  20. package/dist/automations/community-derivations.d.ts +14 -0
  21. package/dist/automations/community-derivations.js +439 -0
  22. package/dist/automations/community-derivations.js.map +1 -0
  23. package/dist/automations/community-mail.d.ts +112 -0
  24. package/dist/automations/community-mail.js +90 -0
  25. package/dist/automations/community-mail.js.map +1 -0
  26. package/dist/automations/community-notifications.d.ts +16 -0
  27. package/dist/automations/community-notifications.js +84 -0
  28. package/dist/automations/community-notifications.js.map +1 -0
  29. package/dist/automations/events.d.ts +28 -0
  30. package/dist/automations/index.d.ts +4 -2
  31. package/dist/automations/index.js +3 -2
  32. package/dist/automations/model.d.ts +10 -3
  33. package/dist/automations/model.js +15 -2
  34. package/dist/automations/model.js.map +1 -1
  35. package/dist/automations/ports.d.ts +5 -1
  36. package/dist/automations/schedule.d.ts +6 -0
  37. package/dist/automations/schedule.js +41 -0
  38. package/dist/automations/schedule.js.map +1 -0
  39. package/dist/automations/seeds.d.ts +17 -0
  40. package/dist/automations/seeds.js +235 -0
  41. package/dist/automations/seeds.js.map +1 -0
  42. package/dist/automations/service.d.ts +22 -3
  43. package/dist/automations/service.js +306 -43
  44. package/dist/automations/service.js.map +1 -1
  45. package/dist/content/events.d.ts +631 -19
  46. package/dist/content/events.js +44 -1
  47. package/dist/content/events.js.map +1 -1
  48. package/dist/content/index.d.ts +4 -4
  49. package/dist/content/model.d.ts +2 -2
  50. package/dist/content/ports.d.ts +71 -18
  51. package/dist/content/service.d.ts +57 -18
  52. package/dist/content/service.js +502 -23
  53. package/dist/content/service.js.map +1 -1
  54. package/dist/content/types.d.ts +2 -2
  55. package/dist/discussion/authors.d.ts +10 -0
  56. package/dist/discussion/authors.js +23 -0
  57. package/dist/discussion/authors.js.map +1 -0
  58. package/dist/discussion/chats.d.ts +104 -0
  59. package/dist/discussion/chats.js +798 -0
  60. package/dist/discussion/chats.js.map +1 -0
  61. package/dist/discussion/events.d.ts +619 -56
  62. package/dist/discussion/events.js +106 -3
  63. package/dist/discussion/events.js.map +1 -1
  64. package/dist/discussion/index.d.ts +7 -3
  65. package/dist/discussion/index.js +4 -1
  66. package/dist/discussion/ports.d.ts +288 -27
  67. package/dist/discussion/ports.js +6 -1
  68. package/dist/discussion/ports.js.map +1 -0
  69. package/dist/discussion/posts.d.ts +103 -0
  70. package/dist/discussion/posts.js +723 -0
  71. package/dist/discussion/posts.js.map +1 -0
  72. package/dist/discussion/service.d.ts +32 -10
  73. package/dist/discussion/service.js +293 -78
  74. package/dist/discussion/service.js.map +1 -1
  75. package/dist/entitlements/events.d.ts +178 -11
  76. package/dist/entitlements/events.js +28 -1
  77. package/dist/entitlements/events.js.map +1 -1
  78. package/dist/entitlements/index.d.ts +5 -4
  79. package/dist/entitlements/index.js +2 -1
  80. package/dist/entitlements/model.d.ts +3 -2
  81. package/dist/entitlements/ports.d.ts +124 -11
  82. package/dist/entitlements/rules.d.ts +12 -0
  83. package/dist/entitlements/rules.js +17 -0
  84. package/dist/entitlements/rules.js.map +1 -0
  85. package/dist/entitlements/service.d.ts +54 -3
  86. package/dist/entitlements/service.js +472 -2
  87. package/dist/entitlements/service.js.map +1 -1
  88. package/dist/identity/access.d.ts +6 -0
  89. package/dist/identity/access.js +7 -0
  90. package/dist/identity/access.js.map +1 -0
  91. package/dist/identity/index.d.ts +6 -4
  92. package/dist/identity/index.js +3 -1
  93. package/dist/identity/model.d.ts +2 -1
  94. package/dist/identity/notifications.d.ts +27 -0
  95. package/dist/identity/notifications.js +101 -0
  96. package/dist/identity/notifications.js.map +1 -0
  97. package/dist/identity/ports.d.ts +73 -3
  98. package/dist/identity/service.d.ts +14 -3
  99. package/dist/identity/service.js +62 -5
  100. package/dist/identity/service.js.map +1 -1
  101. package/dist/integrations/model.js +1 -1
  102. package/dist/integrations/model.js.map +1 -1
  103. package/dist/integrations/service.js +2 -2
  104. package/dist/integrations/service.js.map +1 -1
  105. package/dist/organizations/access-scopes.d.ts +19 -0
  106. package/dist/organizations/access-scopes.js +89 -0
  107. package/dist/organizations/access-scopes.js.map +1 -0
  108. package/dist/organizations/events.d.ts +74 -32
  109. package/dist/organizations/events.js +6 -0
  110. package/dist/organizations/events.js.map +1 -1
  111. package/dist/organizations/index.d.ts +7 -6
  112. package/dist/organizations/index.js +4 -3
  113. package/dist/organizations/members.d.ts +8 -5
  114. package/dist/organizations/members.js.map +1 -1
  115. package/dist/organizations/ports.d.ts +14 -11
  116. package/dist/organizations/roles.d.ts +6 -10
  117. package/dist/organizations/roles.js +19 -7
  118. package/dist/organizations/roles.js.map +1 -1
  119. package/dist/organizations/service.d.ts +20 -11
  120. package/dist/organizations/service.js +157 -47
  121. package/dist/organizations/service.js.map +1 -1
  122. package/dist/organizations/types.d.ts +1 -10
  123. package/dist/progress/index.d.ts +2 -2
  124. package/dist/progress/ports.d.ts +6 -1
  125. package/dist/progress/service.d.ts +3 -1
  126. package/dist/progress/service.js +6 -2
  127. package/dist/progress/service.js.map +1 -1
  128. package/dist/reporting/admin/index.d.ts +4 -0
  129. package/dist/reporting/admin/index.js +2 -0
  130. package/dist/reporting/admin/model.d.ts +108 -0
  131. package/dist/reporting/admin/ports.d.ts +27 -0
  132. package/dist/reporting/admin/service.d.ts +22 -0
  133. package/dist/reporting/admin/service.js +38 -0
  134. package/dist/reporting/admin/service.js.map +1 -0
  135. package/dist/reporting/member/index.d.ts +5 -0
  136. package/dist/reporting/member/index.js +2 -0
  137. package/dist/reporting/member/model.d.ts +124 -0
  138. package/dist/reporting/member/ports.d.ts +45 -0
  139. package/dist/reporting/member/service.d.ts +73 -0
  140. package/dist/reporting/member/service.js +286 -0
  141. package/dist/reporting/member/service.js.map +1 -0
  142. package/dist/shared/actor.d.ts +9 -0
  143. package/dist/shared/errors.d.ts +7 -1
  144. package/dist/shared/errors.js +10 -1
  145. package/dist/shared/errors.js.map +1 -1
  146. package/dist/shared/id.d.ts +11 -0
  147. package/dist/shared/id.js +12 -1
  148. package/dist/shared/id.js.map +1 -1
  149. package/dist/shared/mailer.d.ts +4 -1
  150. package/dist/shared/mailer.js.map +1 -1
  151. package/dist/shared/network.d.ts +10 -0
  152. package/dist/shared/network.js +211 -0
  153. package/dist/shared/network.js.map +1 -0
  154. package/dist/types/assets.d.ts +2 -2
  155. package/dist/types/automations.d.ts +10 -2
  156. package/dist/types/content.d.ts +2 -2
  157. package/dist/types/discussion.d.ts +11 -3
  158. package/dist/types/email-templates.d.ts +121 -0
  159. package/dist/types/entitlements.d.ts +2 -2
  160. package/dist/types/identity.d.ts +48 -2
  161. package/dist/types/index.d.ts +9 -9
  162. package/dist/types/ports.d.ts +6 -0
  163. package/dist/types/schemas/assets.d.ts +65 -37
  164. package/dist/types/schemas/assets.js +17 -12
  165. package/dist/types/schemas/assets.js.map +1 -1
  166. package/dist/types/schemas/automations.d.ts +18 -1
  167. package/dist/types/schemas/automations.js +15 -6
  168. package/dist/types/schemas/automations.js.map +1 -1
  169. package/dist/types/schemas/content.d.ts +335 -3
  170. package/dist/types/schemas/content.js +139 -3
  171. package/dist/types/schemas/content.js.map +1 -1
  172. package/dist/types/schemas/discussion.d.ts +370 -3
  173. package/dist/types/schemas/discussion.js +199 -4
  174. package/dist/types/schemas/discussion.js.map +1 -1
  175. package/dist/types/schemas/entitlements.d.ts +36 -2
  176. package/dist/types/schemas/entitlements.js +23 -1
  177. package/dist/types/schemas/entitlements.js.map +1 -1
  178. package/dist/types/schemas/identity.d.ts +111 -1
  179. package/dist/types/schemas/identity.js +55 -2
  180. package/dist/types/schemas/identity.js.map +1 -1
  181. package/dist/types/schemas/index.d.ts +8 -8
  182. package/dist/types/schemas/index.js +8 -8
  183. package/dist/types/schemas/organizations.d.ts +8 -0
  184. package/dist/types/schemas/organizations.js +2 -0
  185. package/dist/types/schemas/organizations.js.map +1 -1
  186. package/dist/types/schemas/shared.d.ts +3 -1
  187. package/dist/types/schemas/shared.js +5 -1
  188. package/dist/types/schemas/shared.js.map +1 -1
  189. package/package.json +2 -2
  190. package/dist/reporting/courses/index.d.ts +0 -4
  191. package/dist/reporting/courses/index.js +0 -2
  192. package/dist/reporting/courses/model.d.ts +0 -24
  193. package/dist/reporting/courses/ports.d.ts +0 -13
  194. package/dist/reporting/courses/service.d.ts +0 -19
  195. package/dist/reporting/courses/service.js +0 -20
  196. package/dist/reporting/courses/service.js.map +0 -1
  197. package/dist/reporting/dashboard/index.d.ts +0 -4
  198. package/dist/reporting/dashboard/index.js +0 -2
  199. package/dist/reporting/dashboard/model.d.ts +0 -14
  200. package/dist/reporting/dashboard/ports.d.ts +0 -13
  201. package/dist/reporting/dashboard/service.d.ts +0 -19
  202. package/dist/reporting/dashboard/service.js +0 -20
  203. package/dist/reporting/dashboard/service.js.map +0 -1
  204. package/dist/reporting/learn/index.d.ts +0 -5
  205. package/dist/reporting/learn/index.js +0 -2
  206. package/dist/reporting/learn/model.d.ts +0 -23
  207. package/dist/reporting/learn/ports.d.ts +0 -43
  208. package/dist/reporting/learn/ports.js +0 -1
  209. package/dist/reporting/learn/service.d.ts +0 -49
  210. package/dist/reporting/learn/service.js +0 -116
  211. package/dist/reporting/learn/service.js.map +0 -1
  212. package/dist/reporting/students/index.d.ts +0 -4
  213. package/dist/reporting/students/index.js +0 -2
  214. package/dist/reporting/students/model.d.ts +0 -51
  215. package/dist/reporting/students/model.js +0 -1
  216. package/dist/reporting/students/ports.d.ts +0 -15
  217. package/dist/reporting/students/ports.js +0 -1
  218. package/dist/reporting/students/service.d.ts +0 -20
  219. package/dist/reporting/students/service.js +0 -23
  220. package/dist/reporting/students/service.js.map +0 -1
  221. /package/dist/reporting/{courses → admin}/model.js +0 -0
  222. /package/dist/reporting/{courses → admin}/ports.js +0 -0
  223. /package/dist/reporting/{dashboard → member}/model.js +0 -0
  224. /package/dist/reporting/{dashboard → member}/ports.js +0 -0
  225. /package/dist/{reporting/learn/model.js → shared/actor.js} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"posts.js","names":[],"sources":["../../src/discussion/posts.ts"],"sourcesContent":["import { genId } from '../shared/id.js';\nimport {\n ConflictError,\n ForbiddenError,\n InvalidInputError,\n NotFoundError,\n} from '../shared/errors.js';\nimport type { Logger } from '../shared/ports.js';\nimport { noopLogger } from '../shared/logger.js';\nimport { canModerate, isStaffRole } from '../organizations/index.js';\nimport type { Role } from '../organizations/index.js';\nimport type { CommunityReader, SpaceDetail } from '../content/index.js';\nimport type { CommunityAccessReader } from '../entitlements/index.js';\nimport type { AssetFiles, FileUrls, MemberImageReader } from '../assets/index.js';\nimport type { Post, PostFlag, PostFlagResolution, PostStatus, PostView } from '../types/index.js';\nimport type { Actor } from '../shared/actor.js';\nimport type { NewDiscussionEvent } from './events.js';\nimport { discussionEvents } from './events.js';\nimport { authorView } from './authors.js';\nimport type {\n AuthorProfileReader,\n AuthorRoleReader,\n CreatePostInput,\n FeedQuery,\n PageQuery,\n PostPatch,\n PostCommentReader,\n PostReactions,\n PostRepository,\n PostService,\n PostUnitOfWork,\n SpaceModerationQueue,\n UpdatePostInput,\n} from './ports.js';\nimport { MODERATION_QUEUE_LIMIT } from './ports.js';\nimport type { Page, ReactionEmoji } from './types.js';\n\nexport type PostServiceParams = {\n repo: PostRepository;\n access: CommunityAccessReader;\n content: CommunityReader;\n authors: AuthorRoleReader;\n profiles: AuthorProfileReader;\n comments: PostCommentReader;\n images: MemberImageReader & Pick<AssetFiles, 'signFile'>;\n fileUrls: FileUrls;\n uow: PostUnitOfWork;\n logger?: Logger;\n};\n\nconst PUBLISHED_ONLY: PostStatus[] = ['published'];\nconst DUE_BATCH_SIZE = 100;\nconst MODERATOR_VISIBLE: PostStatus[] = ['published', 'pending', 'removed'];\n\nexport class PostServiceImpl implements PostService {\n private readonly repo: PostRepository;\n private readonly access: CommunityAccessReader;\n private readonly content: CommunityReader;\n private readonly authors: AuthorRoleReader;\n private readonly profiles: AuthorProfileReader;\n private readonly comments: PostCommentReader;\n private readonly images: MemberImageReader & Pick<AssetFiles, 'signFile'>;\n private readonly fileUrls: FileUrls;\n private readonly uow: PostUnitOfWork;\n private readonly logger: Logger;\n\n constructor(params: PostServiceParams) {\n this.repo = params.repo;\n this.access = params.access;\n this.content = params.content;\n this.authors = params.authors;\n this.profiles = params.profiles;\n this.comments = params.comments;\n this.images = params.images;\n this.fileUrls = params.fileUrls;\n this.uow = params.uow;\n this.logger = params.logger ?? noopLogger;\n }\n\n async visibleTitles(\n orgId: string,\n actor: Actor,\n postIds: string[],\n ): Promise<Record<string, string>> {\n const found = await this.repo.findMany(orgId, [...new Set(postIds)]);\n const communityIds = [...new Set(found.map((post) => post.communityId))];\n const open = new Set(\n (\n await Promise.all(\n communityIds.map((communityId) => this.access.openSpaceIds(orgId, actor, communityId)),\n )\n ).flat(),\n );\n return Object.fromEntries(\n found\n .filter((post) => open.has(post.spaceId) && this.canSee(actor, post))\n .map((post) => [post.id, post.title]),\n );\n }\n\n async create(orgId: string, actor: Actor, input: CreatePostInput): Promise<PostView> {\n const space = await this.requireOpen(orgId, actor, input.spaceId, input.communityId);\n await this.requireOwnedImage(orgId, actor.id, input.coverAssetId);\n this.assertTopic(space, input.topicId);\n const now = new Date();\n const landing = this.initial(input, space, actor, now);\n const post: Post = {\n orgId,\n id: genId('post'),\n communityId: space.communityId,\n spaceId: space.id,\n orgUserId: actor.id,\n topicId: input.topicId,\n title: input.title,\n body: input.body,\n extra: input.extra,\n coverAssetId: input.coverAssetId,\n pinned: false,\n commentsClosed: null,\n reactionsEnabled: null,\n createdAt: now,\n updatedAt: now,\n ...landing,\n };\n const saved = await this.uow.run(async ({ posts, outbox }) => {\n const created = await posts.insert(orgId, post);\n const events: NewDiscussionEvent[] = [\n discussionEvents.postCreated.make({\n orgId,\n data: created,\n metadata: this.meta(actor, created.status === 'pending' ? null : undefined),\n }),\n ];\n if (created.status === 'published') {\n events.push(\n discussionEvents.postPublished.make({\n orgId,\n data: created,\n metadata: this.meta(actor, null),\n }),\n );\n }\n await outbox.append(events);\n return created;\n });\n this.logger.info('post created', {\n orgId,\n postId: saved.id,\n status: saved.status,\n actorId: actor.id,\n });\n return this.viewOf(orgId, actor, saved);\n }\n\n async update(\n orgId: string,\n postId: string,\n actor: Actor,\n patch: UpdatePostInput,\n ): Promise<PostView> {\n const { post, space } = await this.loadVisible(orgId, actor, postId);\n this.assertAuthor(actor, post);\n if (post.status === 'removed') {\n throw new ConflictError('This post was removed');\n }\n if (patch.topicId !== undefined) {\n this.assertTopic(space, patch.topicId);\n }\n if (typeof patch.coverAssetId === 'string') {\n await this.requireOwnedImage(orgId, actor.id, patch.coverAssetId);\n }\n if (!this.patchChanges(post, patch)) {\n return this.viewOf(orgId, actor, post);\n }\n const updated = await this.uow.run(async ({ posts, outbox }) => {\n const next = await this.mustUpdate(posts, orgId, postId, patch);\n await outbox.append([\n discussionEvents.postUpdated.make({\n orgId,\n data: next,\n metadata: this.meta(actor, next.status === 'pending' ? post.status : undefined),\n }),\n ]);\n return next;\n });\n this.logger.info('post updated', {\n orgId,\n postId,\n fields: Object.keys(patch),\n actorId: actor.id,\n });\n return this.viewOf(orgId, actor, updated);\n }\n\n async submit(orgId: string, postId: string, actor: Actor): Promise<PostView> {\n const { post, space } = await this.loadVisible(orgId, actor, postId);\n this.assertAuthor(actor, post);\n if (post.status !== 'draft' && post.status !== 'scheduled') {\n throw new ConflictError('This post cannot be submitted');\n }\n this.assertTopic(space, post.topicId);\n const saved = await this.commitSubmission(orgId, post, space, actor.role, actor, new Date());\n this.logger.info('post submitted', { orgId, postId, status: saved.status, actorId: actor.id });\n return this.viewOf(orgId, actor, saved);\n }\n\n async schedule(\n orgId: string,\n postId: string,\n actor: Actor,\n scheduledAt: Date,\n ): Promise<PostView> {\n const { post } = await this.loadVisible(orgId, actor, postId);\n this.assertAuthor(actor, post);\n if (post.status === 'published' || post.status === 'removed' || post.status === 'pending') {\n throw new ConflictError('This post cannot be scheduled');\n }\n if (scheduledAt <= new Date()) {\n throw new InvalidInputError('A scheduled time must be in the future');\n }\n const updated = await this.uow.run(async ({ posts, outbox }) => {\n const next = await this.mustUpdate(posts, orgId, postId, {\n status: 'scheduled',\n scheduledAt,\n publishedAt: null,\n removedBy: null,\n });\n await outbox.append([\n discussionEvents.postUpdated.make({\n orgId,\n data: next,\n metadata: this.meta(actor, post.status),\n }),\n ]);\n return next;\n });\n this.logger.info('post scheduled', {\n orgId,\n postId,\n scheduledAt: scheduledAt.toISOString(),\n actorId: actor.id,\n });\n return this.viewOf(orgId, actor, updated);\n }\n\n async approve(\n orgId: string,\n postId: string,\n actor: Actor,\n communityId?: string,\n ): Promise<PostView> {\n const { post } = await this.loadVisible(orgId, actor, postId, communityId);\n if (!this.canModerate(actor)) {\n throw new ForbiddenError('You cannot review this post');\n }\n if (post.status !== 'pending') {\n throw new ConflictError('This post is not awaiting review');\n }\n const now = new Date();\n const updated = await this.uow.run(async ({ posts, outbox }) => {\n const next = await this.mustUpdate(posts, orgId, postId, {\n status: 'published',\n publishedAt: now,\n scheduledAt: null,\n removedBy: null,\n });\n await outbox.append([\n discussionEvents.postPublished.make({\n orgId,\n data: next,\n metadata: this.meta(actor, post.status),\n }),\n ]);\n return next;\n });\n this.logger.info('post approved', { orgId, postId, actorId: actor.id });\n return this.viewOf(orgId, actor, updated);\n }\n\n async remove(\n orgId: string,\n postId: string,\n actor: Actor,\n communityId?: string,\n ): Promise<PostView> {\n const { post } = await this.loadVisible(orgId, actor, postId, communityId);\n if (post.orgUserId !== actor.id && !this.canModerate(actor)) {\n throw new ForbiddenError('You cannot remove this post');\n }\n if (post.status === 'removed') {\n throw new ConflictError('This post was removed');\n }\n if (post.status !== 'published' && post.status !== 'pending') {\n throw new ConflictError('This post cannot be removed');\n }\n const updated = await this.uow.run(async ({ posts, outbox }) => {\n const next = await this.mustUpdate(posts, orgId, postId, {\n status: 'removed',\n removedBy: actor.id,\n });\n if (post.orgUserId !== actor.id) {\n await posts.resolveFlags(orgId, postId, {\n resolvedAt: new Date(),\n resolvedBy: actor.id,\n resolution: 'removed',\n });\n }\n await outbox.append([\n discussionEvents.postRemoved.make({\n orgId,\n data: next,\n metadata: this.meta(actor, post.status),\n }),\n ]);\n return next;\n });\n this.logger.info('post removed', { orgId, postId, actorId: actor.id });\n return this.viewOf(orgId, actor, updated);\n }\n\n async restore(\n orgId: string,\n postId: string,\n actor: Actor,\n communityId?: string,\n ): Promise<PostView> {\n const { post, space } = await this.loadVisible(orgId, actor, postId, communityId);\n if (!this.canModerate(actor)) {\n throw new ForbiddenError('You cannot moderate this post');\n }\n if (post.status !== 'removed') {\n throw new ConflictError('This post was not removed');\n }\n const now = new Date();\n const role = await this.authors.roleOf(orgId, post.orgUserId);\n const updated = await this.uow.run(async ({ posts, outbox }) => {\n const next = await this.mustUpdate(\n posts,\n orgId,\n postId,\n this.submissionOf(space.settings.requireReview, role, now),\n );\n const metadata = this.meta(actor, post.status);\n const event =\n next.status === 'published'\n ? discussionEvents.postPublished.make({ orgId, data: next, metadata })\n : discussionEvents.postUpdated.make({ orgId, data: next, metadata });\n await outbox.append([event]);\n return next;\n });\n this.logger.info('post restored', {\n orgId,\n postId,\n status: updated.status,\n actorId: actor.id,\n });\n return this.viewOf(orgId, actor, updated);\n }\n\n pin(orgId: string, postId: string, actor: Actor): Promise<PostView> {\n return this.moderate(\n orgId,\n postId,\n actor,\n { pinned: true },\n (post) => post.pinned,\n 'post pinned',\n );\n }\n\n unpin(orgId: string, postId: string, actor: Actor): Promise<PostView> {\n return this.moderate(\n orgId,\n postId,\n actor,\n { pinned: false },\n (post) => !post.pinned,\n 'post unpinned',\n );\n }\n\n closeComments(orgId: string, postId: string, actor: Actor): Promise<PostView> {\n return this.moderate(\n orgId,\n postId,\n actor,\n { commentsClosed: true },\n (post) => post.commentsClosed === true,\n 'post comments closed',\n );\n }\n\n openComments(orgId: string, postId: string, actor: Actor): Promise<PostView> {\n return this.moderate(\n orgId,\n postId,\n actor,\n { commentsClosed: false },\n (post) => post.commentsClosed === false,\n 'post comments opened',\n );\n }\n\n setReactions(orgId: string, postId: string, actor: Actor, enabled: boolean): Promise<PostView> {\n return this.moderate(\n orgId,\n postId,\n actor,\n { reactionsEnabled: enabled },\n (post) => post.reactionsEnabled === enabled,\n 'post reactions changed',\n );\n }\n\n async setReaction(\n orgId: string,\n postId: string,\n actor: Actor,\n emoji: ReactionEmoji | null,\n ): Promise<PostReactions> {\n const { post, space } = await this.loadVisible(orgId, actor, postId);\n if (!(post.reactionsEnabled ?? space.settings.reactions)) {\n throw new ForbiddenError('Reactions are off on this post');\n }\n await this.repo.setReaction(orgId, postId, actor.id, emoji);\n this.logger.debug('post reaction set', { orgId, postId, orgUserId: actor.id });\n const counts = await this.repo.reactionsOf(orgId, [postId], actor.id);\n return counts[postId] ?? { reactions: {} };\n }\n\n async follow(orgId: string, postId: string, actor: Actor): Promise<void> {\n await this.loadVisible(orgId, actor, postId);\n await this.repo.follow(orgId, postId, actor.id);\n this.logger.debug('post followed', { orgId, postId, orgUserId: actor.id });\n }\n\n async unfollow(orgId: string, postId: string, actor: Actor): Promise<void> {\n await this.loadVisible(orgId, actor, postId);\n await this.repo.unfollow(orgId, postId, actor.id);\n this.logger.debug('post unfollowed', { orgId, postId, orgUserId: actor.id });\n }\n\n async bookmark(orgId: string, postId: string, actor: Actor): Promise<void> {\n await this.loadVisible(orgId, actor, postId);\n await this.repo.bookmark(orgId, postId, actor.id);\n this.logger.debug('post bookmarked', { orgId, postId, orgUserId: actor.id });\n }\n\n async unbookmark(orgId: string, postId: string, actor: Actor): Promise<void> {\n await this.loadVisible(orgId, actor, postId);\n await this.repo.unbookmark(orgId, postId, actor.id);\n this.logger.debug('post unbookmarked', { orgId, postId, orgUserId: actor.id });\n }\n\n async flag(orgId: string, postId: string, actor: Actor, reason: string): Promise<PostFlag> {\n const { post } = await this.loadVisible(orgId, actor, postId);\n const now = new Date();\n const flag: PostFlag = {\n orgId,\n id: genId('postFlag'),\n postId,\n orgUserId: actor.id,\n reason,\n resolvedAt: null,\n resolution: null,\n resolvedBy: null,\n createdAt: now,\n updatedAt: now,\n };\n const saved = await this.uow.run(async ({ posts, outbox }) => {\n const created = await posts.insertFlag(orgId, flag);\n if (!created) {\n const existing = await posts.findFlag(orgId, postId, actor.id);\n if (!existing) {\n throw new NotFoundError('Flag', postId);\n }\n return existing;\n }\n await outbox.append([\n discussionEvents.postReported.make({\n orgId,\n data: { ...created, communityId: post.communityId },\n metadata: this.meta(actor),\n }),\n ]);\n return created;\n });\n this.logger.info('post flagged', { orgId, postId, flagId: saved.id, actorId: actor.id });\n return saved;\n }\n\n async resolveFlags(\n orgId: string,\n postId: string,\n actor: Actor,\n resolution: Extract<PostFlagResolution, 'dismissed'>,\n communityId?: string,\n ): Promise<void> {\n await this.loadVisible(orgId, actor, postId, communityId);\n if (!this.canModerate(actor)) {\n throw new ForbiddenError('You cannot work this queue');\n }\n await this.repo.resolveFlags(orgId, postId, {\n resolvedAt: new Date(),\n resolvedBy: actor.id,\n resolution,\n });\n this.logger.info('post flags resolved', { orgId, postId, resolution, actorId: actor.id });\n }\n\n async spaceFeed(\n orgId: string,\n actor: Actor,\n communityId: string,\n spaceId: string,\n query: FeedQuery,\n ): Promise<Page<PostView>> {\n const space = await this.requireOpen(orgId, actor, spaceId, communityId);\n if (query.topicId && !space.topics.some((topic) => topic.id === query.topicId)) {\n throw new NotFoundError('Topic', query.topicId);\n }\n const page = await this.feedPage(orgId, actor, [spaceId], query);\n this.logger.debug('space feed read', { orgId, spaceId, rows: page.rows.length });\n return page;\n }\n\n async memberFeed(\n orgId: string,\n actor: Actor,\n communityId: string,\n query: Pick<FeedQuery, 'page' | 'pageSize'>,\n ): Promise<Page<PostView>> {\n const spaceIds = await this.openSpaceIdsIn(orgId, actor, communityId);\n const page = await this.feedPage(orgId, actor, spaceIds, query);\n this.logger.debug('member feed read', {\n orgId,\n communityId,\n spaces: spaceIds.length,\n rows: page.rows.length,\n });\n return page;\n }\n\n private async openSpaceIdsIn(\n orgId: string,\n actor: Actor,\n communityId: string,\n ): Promise<string[]> {\n if (!(await this.access.isCommunityMember(orgId, actor, communityId))) {\n throw new NotFoundError('Community', communityId);\n }\n return this.access.openSpaceIds(orgId, actor, communityId);\n }\n\n async listOwn(\n orgId: string,\n actor: Actor,\n communityId: string,\n query: PageQuery,\n ): Promise<Page<PostView>> {\n const spaceIds = await this.openSpaceIdsIn(orgId, actor, communityId);\n const page = await this.repo.listByAuthor(orgId, actor.id, spaceIds, query);\n this.logger.debug('own posts read', { orgId, communityId, rows: page.rows.length });\n return { ...page, rows: await this.viewsOf(orgId, actor, page.rows) };\n }\n\n async listBookmarked(\n orgId: string,\n actor: Actor,\n communityId: string,\n query: PageQuery,\n ): Promise<Page<PostView>> {\n const spaceIds = await this.openSpaceIdsIn(orgId, actor, communityId);\n const page = await this.repo.listBookmarked(orgId, actor.id, spaceIds, {\n ...query,\n visibleStatuses: this.visibleStatuses(actor),\n });\n this.logger.debug('bookmarked posts read', { orgId, communityId, rows: page.rows.length });\n return { ...page, rows: await this.viewsOf(orgId, actor, page.rows) };\n }\n\n async coverFile(orgId: string, actor: Actor, postId: string): Promise<string | null> {\n const post = await this.repo.find(orgId, postId);\n if (!post?.coverAssetId || !this.canSee(actor, post)) {\n return null;\n }\n const space = await this.content.getSpace(orgId, post.spaceId);\n if (!space || !(await this.access.canOpen(orgId, actor, space))) {\n return null;\n }\n return this.images.signFile(orgId, post.coverAssetId);\n }\n\n async getPost(orgId: string, actor: Actor, postId: string): Promise<PostView> {\n const { post } = await this.loadVisible(orgId, actor, postId);\n this.logger.debug('post read', { orgId, postId });\n return this.viewOf(orgId, actor, post);\n }\n\n async moderationQueue(\n orgId: string,\n actor: Actor,\n communityId: string,\n spaceId: string,\n ): Promise<SpaceModerationQueue> {\n await this.requireOpen(orgId, actor, spaceId, communityId);\n if (!this.canModerate(actor)) {\n throw new ForbiddenError('You cannot work this queue');\n }\n const [pending, flags, openCommentFlags] = await Promise.all([\n this.repo.listPending(orgId, spaceId, MODERATION_QUEUE_LIMIT),\n this.repo.listOpenFlags(orgId, spaceId, MODERATION_QUEUE_LIMIT),\n this.comments.listOpenFlagsForSpace(orgId, spaceId, MODERATION_QUEUE_LIMIT),\n ]);\n this.logger.debug('space queue read', {\n orgId,\n spaceId,\n pending: pending.length,\n flags: flags.length,\n commentFlags: openCommentFlags.length,\n });\n return {\n pending: await this.viewsOf(orgId, actor, pending),\n flags,\n commentFlags: openCommentFlags,\n };\n }\n\n visible(\n orgId: string,\n actor: Actor,\n postId: string,\n ): Promise<{ post: Post; space: SpaceDetail }> {\n return this.loadVisible(orgId, actor, postId);\n }\n\n find(orgId: string, postId: string): Promise<Post | null> {\n return this.repo.find(orgId, postId);\n }\n\n followerIds(orgId: string, postId: string): Promise<string[]> {\n return this.repo.followerIds(orgId, postId);\n }\n\n hasDue(orgId: string, now: Date): Promise<boolean> {\n return this.repo.hasDue(orgId, now);\n }\n\n async publishDue(orgId: string, now: Date): Promise<void> {\n let afterId: string | undefined;\n let count = 0;\n let refused = 0;\n for (;;) {\n const due = await this.repo.listDue(orgId, now, DUE_BATCH_SIZE, afterId);\n if (due.length === 0) {\n break;\n }\n const batch = await this.submitDue(orgId, now, due);\n count += batch.count;\n refused += batch.refused;\n if (due.length < DUE_BATCH_SIZE) {\n break;\n }\n afterId = due[due.length - 1]!.id;\n }\n if (count > 0) {\n this.logger.info('due posts submitted', { orgId, count, refused });\n } else {\n this.logger.debug('no posts due', { orgId });\n }\n }\n\n private async submitDue(\n orgId: string,\n now: Date,\n due: Post[],\n ): Promise<{ count: number; refused: number }> {\n const spaces = new Map<string, SpaceDetail | null>();\n const roles = new Map<string, Role | null>();\n const refused = new Set<string>();\n for (const post of due) {\n if (!spaces.has(post.spaceId)) {\n spaces.set(post.spaceId, await this.content.getSpace(orgId, post.spaceId));\n }\n if (!roles.has(post.orgUserId)) {\n roles.set(post.orgUserId, await this.authors.roleOf(orgId, post.orgUserId));\n }\n const space = spaces.get(post.spaceId);\n const role = roles.get(post.orgUserId) ?? null;\n if (space && !(await this.submittable(orgId, post, space, role))) {\n refused.add(post.id);\n }\n }\n const count = await this.uow.run(async ({ posts, outbox }) => {\n const events: NewDiscussionEvent[] = [];\n for (const post of due) {\n const current = await posts.find(orgId, post.id);\n if (\n !current ||\n current.status !== 'scheduled' ||\n current.scheduledAt === null ||\n current.scheduledAt > now\n ) {\n continue;\n }\n const space = spaces.get(current.spaceId);\n if (!space) {\n this.logger.error('scheduled post has no space', {\n orgId,\n postId: current.id,\n spaceId: current.spaceId,\n });\n continue;\n }\n const next = await posts.update(\n orgId,\n current.id,\n refused.has(current.id)\n ? { status: 'draft', scheduledAt: null, publishedAt: null, removedBy: null }\n : this.submissionOf(\n space.settings.requireReview,\n roles.get(current.orgUserId) ?? null,\n now,\n ),\n );\n if (!next) {\n continue;\n }\n const metadata = this.meta(null, current.status);\n events.push(\n next.status === 'published'\n ? discussionEvents.postPublished.make({ orgId, data: next, metadata })\n : discussionEvents.postUpdated.make({ orgId, data: next, metadata }),\n );\n }\n if (events.length > 0) {\n await outbox.append(events);\n }\n return events.length;\n });\n return { count, refused: refused.size };\n }\n\n private async submittable(\n orgId: string,\n post: Post,\n space: SpaceDetail,\n role: Role | null,\n ): Promise<boolean> {\n if (role === null || !this.topicFits(space, post.topicId)) {\n return false;\n }\n return this.access.canOpen(orgId, { id: post.orgUserId, role }, space);\n }\n\n private async feedPage(\n orgId: string,\n actor: Actor,\n spaceIds: string[],\n query: FeedQuery,\n ): Promise<Page<PostView>> {\n const page = await this.repo.listPublished(orgId, spaceIds, query);\n return { ...page, rows: await this.viewsOf(orgId, actor, page.rows) };\n }\n\n private async viewOf(orgId: string, actor: Actor, post: Post): Promise<PostView> {\n const [view] = await this.viewsOf(orgId, actor, [post]);\n return view!;\n }\n\n private async viewsOf(orgId: string, actor: Actor, posts: Post[]): Promise<PostView[]> {\n if (posts.length === 0) {\n return [];\n }\n const postIds = posts.map((post) => post.id);\n const [records, reactions, commentCounts, followed, bookmarked] = await Promise.all([\n this.profiles.authorsOf(orgId, [...new Set(posts.map((post) => post.orgUserId))]),\n this.repo.reactionsOf(orgId, postIds, actor.id),\n this.comments.publishedCommentCounts(orgId, postIds),\n this.repo.followedAmong(orgId, postIds, actor.id),\n this.repo.bookmarkedAmong(orgId, postIds, actor.id),\n ]);\n const following = new Set(followed);\n const saved = new Set(bookmarked);\n return posts.map((post) => {\n const record = records[post.orgUserId];\n if (!record) {\n throw new NotFoundError('Member', post.orgUserId);\n }\n const counts = reactions[post.id];\n return {\n ...post,\n coverUrl: post.coverAssetId ? this.fileUrls.postCover(post.id) : null,\n author: authorView(orgId, record, this.fileUrls),\n reactions: counts?.reactions ?? {},\n viewerReaction: counts?.viewerReaction ?? null,\n commentCount: commentCounts[post.id] ?? 0,\n following: following.has(post.id),\n bookmarked: saved.has(post.id),\n };\n });\n }\n\n private async commitSubmission(\n orgId: string,\n post: Post,\n space: SpaceDetail,\n role: Role,\n actor: Actor,\n now: Date,\n ): Promise<Post> {\n return this.uow.run(async ({ posts, outbox }) => {\n const next = await this.mustUpdate(\n posts,\n orgId,\n post.id,\n this.submissionOf(space.settings.requireReview, role, now),\n );\n const metadata = this.meta(actor, post.status);\n const event =\n next.status === 'published'\n ? discussionEvents.postPublished.make({ orgId, data: next, metadata })\n : discussionEvents.postUpdated.make({ orgId, data: next, metadata });\n await outbox.append([event]);\n return next;\n });\n }\n\n private async moderate(\n orgId: string,\n postId: string,\n actor: Actor,\n patch: PostPatch,\n unchanged: (post: Post) => boolean,\n log: string,\n ): Promise<PostView> {\n const { post } = await this.loadVisible(orgId, actor, postId);\n if (!this.canModerate(actor)) {\n throw new ForbiddenError('You cannot moderate this post');\n }\n if (post.status === 'removed') {\n throw new ConflictError('This post was removed');\n }\n if (unchanged(post)) {\n return this.viewOf(orgId, actor, post);\n }\n const updated = await this.uow.run(async ({ posts, outbox }) => {\n const next = await this.mustUpdate(posts, orgId, postId, patch);\n await outbox.append([\n discussionEvents.postUpdated.make({\n orgId,\n data: next,\n metadata: this.meta(actor, next.status === 'pending' ? post.status : undefined),\n }),\n ]);\n return next;\n });\n this.logger.info(log, { orgId, postId, actorId: actor.id });\n return this.viewOf(orgId, actor, updated);\n }\n\n private async mustUpdate(\n posts: PostRepository,\n orgId: string,\n postId: string,\n patch: PostPatch,\n ): Promise<Post> {\n const next = await posts.update(orgId, postId, patch);\n if (!next) {\n throw new NotFoundError('Post', postId);\n }\n return next;\n }\n\n private initial(input: CreatePostInput, space: SpaceDetail, actor: Actor, now: Date) {\n if (input.mode === 'draft') {\n return { status: 'draft' as const, scheduledAt: null, publishedAt: null, removedBy: null };\n }\n if (input.mode === 'schedule') {\n if (!input.scheduledAt) {\n throw new InvalidInputError('A scheduled post needs a time');\n }\n if (input.scheduledAt <= now) {\n throw new InvalidInputError('A scheduled time must be in the future');\n }\n return {\n status: 'scheduled' as const,\n scheduledAt: input.scheduledAt,\n publishedAt: null,\n removedBy: null,\n };\n }\n return this.submissionOf(space.settings.requireReview, actor.role, now);\n }\n\n private submissionOf(requireReview: boolean, role: Role | null, now: Date) {\n if (requireReview && !(role && isStaffRole(role))) {\n return { status: 'pending' as const, scheduledAt: null, publishedAt: null, removedBy: null };\n }\n return { status: 'published' as const, scheduledAt: null, publishedAt: now, removedBy: null };\n }\n\n private async requireOwnedImage(\n orgId: string,\n orgUserId: string,\n assetId: string | null,\n ): Promise<void> {\n if (assetId === null) {\n return;\n }\n if (!(await this.images.ownsImage(orgId, assetId, orgUserId))) {\n this.logger.debug('cover refused', { orgId, orgUserId, assetId });\n throw new NotFoundError('Asset', assetId);\n }\n }\n\n private async openSpace(\n orgId: string,\n actor: Actor,\n spaceId: string,\n communityId?: string,\n ): Promise<SpaceDetail | null> {\n const space = await this.content.getSpace(orgId, spaceId);\n if (!space || (communityId !== undefined && space.communityId !== communityId)) {\n return null;\n }\n return (await this.access.canOpen(orgId, actor, space)) ? space : null;\n }\n\n private async requireOpen(\n orgId: string,\n actor: Actor,\n spaceId: string,\n communityId?: string,\n ): Promise<SpaceDetail> {\n const space = await this.openSpace(orgId, actor, spaceId, communityId);\n if (!space) {\n throw new NotFoundError('Space', spaceId);\n }\n return space;\n }\n\n private async loadVisible(orgId: string, actor: Actor, postId: string, communityId?: string) {\n const post = await this.repo.find(orgId, postId);\n if (!post || (communityId !== undefined && post.communityId !== communityId)) {\n throw new NotFoundError('Post', postId);\n }\n const space = await this.openSpace(orgId, actor, post.spaceId);\n if (!space || !this.canSee(actor, post)) {\n throw new NotFoundError('Post', postId);\n }\n return { post, space };\n }\n\n private canSee(actor: Actor, post: Post): boolean {\n return post.orgUserId === actor.id || this.visibleStatuses(actor).includes(post.status);\n }\n\n private visibleStatuses(actor: Actor): PostStatus[] {\n return this.canModerate(actor) ? MODERATOR_VISIBLE : PUBLISHED_ONLY;\n }\n\n private canModerate(actor: Actor): boolean {\n return canModerate(actor.role);\n }\n\n private assertAuthor(actor: Actor, post: Post): void {\n if (post.orgUserId !== actor.id) {\n throw new ForbiddenError('You can change only your own post');\n }\n }\n\n private topicFits(space: SpaceDetail, topicId: string | null): boolean {\n if (topicId === null) {\n return space.topics.length === 0;\n }\n return space.topics.some((topic) => topic.id === topicId);\n }\n\n private assertTopic(space: SpaceDetail, topicId: string | null): void {\n if (this.topicFits(space, topicId)) {\n return;\n }\n if (topicId === null) {\n throw new InvalidInputError('This space requires a topic');\n }\n throw new NotFoundError('Topic', topicId);\n }\n\n private patchChanges(post: Post, patch: UpdatePostInput): boolean {\n return (Object.keys(patch) as (keyof UpdatePostInput)[]).some((key) => {\n const value = patch[key];\n if (value === undefined) {\n return false;\n }\n if (key === 'extra') {\n return JSON.stringify(post.extra) !== JSON.stringify(value);\n }\n return post[key] !== value;\n });\n }\n\n private meta(actor: Actor | null, previousStatus?: PostStatus | null) {\n return {\n ...(actor ? { actorOrgUserId: actor.id } : {}),\n ...(previousStatus !== undefined ? { previousStatus } : {}),\n };\n }\n}\n"],"mappings":"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"}
@@ -3,10 +3,13 @@ import { Logger } from "../types/ports.js";
3
3
  import { CommentListItem, CommentView, CommentsConfig, ListCommentsQuery, ReactionEmoji } from "../types/schemas/discussion.js";
4
4
  import { Comment, CommentReport } from "../types/discussion.js";
5
5
  import "../shared/ports.js";
6
+ import { FileUrls } from "../assets/file-urls.js";
7
+ import "../assets/index.js";
8
+ import { Actor } from "../shared/actor.js";
6
9
  import "./model.js";
7
10
  import "./types.js";
11
+ import { ActivityComments, ActivityGetter, CommentReactions, ContentAccessReader, DiscussionRepository, DiscussionService, DiscussionUnitOfWork, PageQuery, PostCommentInput, PostService } from "./ports.js";
8
12
  import { SettingsService } from "../shared/settings.js";
9
- import { ActivityComments, ActivityGetter, Actor, CommentReactions, CourseAccessReader, DiscussionRepository, DiscussionService, DiscussionUnitOfWork, PostCommentInput } from "./ports.js";
10
13
  //#region src/discussion/service.d.ts
11
14
  /** A course with no stored settings. Discussion is opt-in, so the common case
12
15
  * persists no row at all and every existing course stays silent. */
@@ -18,34 +21,52 @@ declare const DEFAULT_SETTINGS: {
18
21
  };
19
22
  type DiscussionServiceParams = {
20
23
  repo: DiscussionRepository;
21
- access: CourseAccessReader;
24
+ access: ContentAccessReader;
22
25
  content: ActivityGetter;
26
+ posts: Pick<PostService, 'visible'>;
23
27
  uow: DiscussionUnitOfWork;
24
28
  settings: SettingsService;
29
+ communities: {
30
+ listIds(orgId: string): Promise<string[]>;
31
+ };
32
+ fileUrls: FileUrls;
25
33
  logger?: Logger;
26
34
  };
27
35
  declare class DiscussionServiceImpl implements DiscussionService {
28
36
  private readonly repo;
29
37
  private readonly access;
30
38
  private readonly content;
39
+ private readonly posts;
31
40
  private readonly uow;
32
41
  private readonly settings;
42
+ private readonly communities;
43
+ private readonly fileUrls;
33
44
  private readonly logger;
34
45
  constructor(params: DiscussionServiceParams);
35
46
  resolveConfig(orgId: string, activityId: string): Promise<CommentsConfig>;
36
- /** Any org user that is not a learner moderates. The role arrives from
37
- * the edge already resolved; core never looks one up to authorise. */
38
47
  private isStaff;
39
- private toAuthor;
40
48
  private toView;
41
49
  private peopleIn;
42
50
  private renderOne;
51
+ private activityIdOf;
43
52
  private hasAccess;
44
- postComment(orgId: string, { activityId, actor, parentId, body }: PostCommentInput): Promise<CommentView>;
53
+ private requireActivityOpen;
54
+ postComment(orgId: string, input: PostCommentInput): Promise<CommentView>;
55
+ private postOnActivity;
56
+ private postOnPost;
57
+ postComments(orgId: string, postId: string, actor: Actor, query: PageQuery): Promise<Page<CommentView>>;
58
+ private thread;
59
+ private visiblePost;
60
+ private postOfComment;
61
+ private canModerate;
62
+ private inCommunity;
63
+ private commentsOpen;
45
64
  activityComments(orgId: string, activityId: string, actor: Actor): Promise<ActivityComments>;
46
65
  edit(orgId: string, commentId: string, actor: Actor, body: string): Promise<CommentView>;
47
- remove(orgId: string, commentId: string, actor: Actor): Promise<Comment>;
48
- restore(orgId: string, commentId: string, actor: Actor): Promise<Comment>;
66
+ remove(orgId: string, commentId: string, actor: Actor, communityId?: string | null): Promise<CommentView>;
67
+ private removeComment;
68
+ restore(orgId: string, commentId: string, actor: Actor, communityId?: string | null): Promise<CommentView>;
69
+ private restoreComment;
49
70
  /** Load a comment together with its activity's resolved comments config,
50
71
  * having proved the actor may reach that activity at all. Every path that
51
72
  * acts on one comment needs the pair, and none should read the row twice. */
@@ -56,11 +77,12 @@ declare class DiscussionServiceImpl implements DiscussionService {
56
77
  * caller must not decide that itself. `approve` and `restore` still enforce
57
78
  * their own staff check; the check here covers the third path, where a
58
79
  * comment is already published and neither of them would run. */
59
- publish(orgId: string, commentId: string, actor: Actor): Promise<Comment>;
60
- resolveReports(orgId: string, commentId: string, actor: Actor): Promise<void>;
80
+ publish(orgId: string, commentId: string, actor: Actor, communityId?: string | null): Promise<Comment>;
81
+ resolveReports(orgId: string, commentId: string, actor: Actor, communityId?: string | null): Promise<void>;
61
82
  reportComment(orgId: string, commentId: string, actor: Actor, reason: string): Promise<CommentReport>;
62
83
  listComments(orgId: string, query: ListCommentsQuery): Promise<Page<CommentListItem>>;
63
84
  getComment(orgId: string, commentId: string): Promise<Comment | null>;
85
+ sendDigest(orgId: string, now: Date): Promise<void>;
64
86
  }
65
87
  //#endregion
66
88
  export { DEFAULT_SETTINGS, DiscussionServiceImpl, DiscussionServiceParams };