@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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discussion.js","names":[],"sources":["../../../../src/automations/notifications/rules/discussion.ts"],"sourcesContent":["import { discussionEvents } from '../../../discussion/index.js';\nimport type { DomainEvent } from '../../../shared/ports.js';\nimport type { JsonValue, RecipientList, SubjectRef } from '../../../types/index.js';\nimport {\n communityModerators,\n MESSAGE_EXCERPT,\n POST_EXCERPT,\n previousStatusOf,\n} from '../../community-mail.js';\nimport type { NotificationReads, RecipientRule } from '../../ports.js';\nimport {\n actorOr,\n chatReach,\n chatTitle,\n eventAt,\n listOf,\n participantNames,\n Reasons,\n recipientsOf,\n textExcerpt,\n type ListBase,\n type Reach,\n} from './recipients.js';\n\ntype Post = NonNullable<Awaited<ReturnType<NotificationReads['post']>>>;\ntype Activity = NonNullable<Awaited<ReturnType<NotificationReads['activity']>>>;\n\ntype Place =\n | { kind: 'post'; postId: string; post: Post; spaceName: string }\n | { kind: 'activity'; activityId: string; activity: Activity };\n\nconst CHAT_STATUS = 'chatStatus';\n\nfunction postReach(\n reads: NotificationReads,\n orgId: string,\n post: { id: string; spaceId: string },\n exclude: (string | null)[],\n at: Date,\n): Reach {\n return {\n exclude,\n at,\n canOpen: (orgUserIds) => reads.spaceOpenersAmong(orgId, post.spaceId, orgUserIds),\n mutedOn: [\n { type: 'post', id: post.id },\n { type: 'space', id: post.spaceId },\n ],\n };\n}\n\nfunction courseReach(\n reads: NotificationReads,\n orgId: string,\n courseId: string,\n exclude: (string | null)[],\n at: Date,\n): Reach {\n return {\n exclude,\n at,\n canOpen: async (orgUserIds) => {\n const [holders, readers] = await Promise.all([\n reads.courseMembers(orgId, courseId),\n reads.holders(orgId, 'courses:read', courseId),\n ]);\n const open = new Set([...holders, ...readers]);\n return orgUserIds.filter((orgUserId) => open.has(orgUserId));\n },\n };\n}\n\nasync function placeOf(\n reads: NotificationReads,\n orgId: string,\n comment: { postId: string | null; activityId: string | null },\n): Promise<Place | null> {\n if (comment.postId) {\n const post = await reads.post(orgId, comment.postId);\n const space = post ? await reads.space(orgId, post.spaceId) : null;\n return post && space\n ? { kind: 'post', postId: comment.postId, post, spaceName: space.name }\n : null;\n }\n if (comment.activityId) {\n const activity = await reads.activity(orgId, comment.activityId);\n return activity ? { kind: 'activity', activityId: comment.activityId, activity } : null;\n }\n return null;\n}\n\nfunction placeReach(\n reads: NotificationReads,\n orgId: string,\n place: Place,\n exclude: (string | null)[],\n at: Date,\n): Reach {\n return place.kind === 'post'\n ? postReach(reads, orgId, { id: place.postId, spaceId: place.post.spaceId }, exclude, at)\n : courseReach(reads, orgId, place.activity.courseId, exclude, at);\n}\n\nfunction commentBase(\n type: string,\n place: Place,\n commentId: string,\n actorId: string | null,\n display: Record<string, JsonValue>,\n): ListBase {\n const subject: SubjectRef = { type: 'comment', id: commentId };\n if (place.kind === 'post') {\n return {\n type,\n scope: 'space',\n actorId,\n communityId: place.post.communityId,\n spaceId: place.post.spaceId,\n subject,\n parent: { type: 'post', id: place.postId },\n display: { postTitle: place.post.title, ...display },\n };\n }\n return {\n type,\n scope: 'org',\n actorId,\n communityId: null,\n spaceId: null,\n subject,\n parent: { type: 'activity', id: place.activityId },\n display: {\n activityTitle: place.activity.title,\n courseTitle: place.activity.courseTitle,\n courseId: place.activity.courseId,\n ...display,\n },\n };\n}\n\nasync function mentionsOf(\n reads: NotificationReads,\n orgId: string,\n base: Omit<ListBase, 'type'>,\n mentioned: string[],\n reach: Reach,\n): Promise<RecipientList[]> {\n if (mentioned.length === 0) {\n return [];\n }\n const recipients = await recipientsOf(\n reads,\n orgId,\n new Reasons().add(mentioned, 'mentioned'),\n reach,\n );\n return listOf({ ...base, type: 'mention' }, recipients);\n}\n\nasync function commentLists(\n event: DomainEvent,\n reads: NotificationReads,\n comment: {\n id: string;\n postId: string | null;\n activityId: string | null;\n parentId: string | null;\n orgUserId: string;\n body: string;\n mentionedOrgUserIds: string[];\n },\n): Promise<RecipientList[]> {\n const orgId = event.orgId;\n const place = await placeOf(reads, orgId, comment);\n if (!place) {\n return [];\n }\n const at = eventAt(event);\n const exclude = [comment.orgUserId, actorOr(event, comment.orgUserId)];\n const reasons = new Reasons();\n if (place.kind === 'post') {\n reasons.add([place.post.orgUserId], 'post_author');\n }\n const parent = comment.parentId ? await reads.comment(orgId, comment.parentId) : null;\n reasons.add([parent?.orgUserId], 'comment_author');\n if (place.kind === 'post') {\n reasons.add(await reads.followerIds(orgId, place.postId), 'follower');\n }\n reasons.add(\n await reads.threadParticipants(\n orgId,\n place.kind === 'post'\n ? { type: 'post', id: place.postId }\n : { type: 'activity', id: place.activityId },\n ),\n 'thread_participant',\n );\n const name = await reads.displayName(orgId, comment.orgUserId);\n const text = await textExcerpt(reads, orgId, comment.body, POST_EXCERPT);\n const created = listOf(\n commentBase('comment.created', place, comment.id, comment.orgUserId, {\n commenterName: name,\n excerpt: text,\n }),\n await recipientsOf(reads, orgId, reasons, placeReach(reads, orgId, place, exclude, at)),\n );\n const mentions = await mentionsOf(\n reads,\n orgId,\n commentBase('mention', place, comment.id, comment.orgUserId, {\n authorName: name,\n excerpt: text,\n }),\n comment.mentionedOrgUserIds,\n placeReach(reads, orgId, place, exclude, at),\n );\n return [...created, ...mentions];\n}\n\nconst postPublished: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.postPublished.safeParse(event);\n if (!parsed.success || previousStatusOf(event) === 'removed') {\n return [];\n }\n const post = parsed.data.data;\n const orgId = event.orgId;\n const space = await reads.space(orgId, post.spaceId);\n if (!space) {\n return [];\n }\n const at = eventAt(event);\n const actor = actorOr(event, null);\n const exclude = [...new Set([post.orgUserId, ...(actor ? [actor] : [])])];\n const reach = postReach(reads, orgId, post, exclude, at);\n const base = {\n scope: 'space' as const,\n communityId: post.communityId,\n spaceId: post.spaceId,\n subject: { type: 'post' as const, id: post.id },\n parent: null,\n };\n const lists: RecipientList[] = [];\n if (previousStatusOf(event) === 'pending') {\n lists.push(\n ...listOf(\n {\n ...base,\n type: 'post.approved',\n actorId: actor,\n display: { postTitle: post.title, spaceName: space.name },\n },\n await recipientsOf(reads, orgId, new Reasons().add([post.orgUserId], 'subject'), {\n ...reach,\n exclude: [actor],\n }),\n ),\n );\n }\n const authorName = await reads.displayName(orgId, post.orgUserId);\n const text = await textExcerpt(\n reads,\n orgId,\n reads.postText(post.body) || post.title,\n POST_EXCERPT,\n );\n lists.push(\n ...(await mentionsOf(\n reads,\n orgId,\n {\n ...base,\n actorId: post.orgUserId,\n display: { authorName, excerpt: text, postTitle: post.title },\n },\n post.mentionedOrgUserIds,\n reach,\n )),\n );\n lists.push({\n ...base,\n type: 'post.published',\n actorId: post.orgUserId,\n display: {\n spaceName: space.name,\n postTitle: post.title,\n authorName,\n excerpt: text,\n },\n audience: { reason: 'space_member', spaceId: post.spaceId, exclude },\n });\n return lists;\n};\n\nasync function awaitingReview(\n event: DomainEvent,\n reads: NotificationReads,\n post: {\n id: string;\n communityId: string;\n spaceId: string;\n orgUserId: string;\n title: string;\n status: string;\n },\n): Promise<RecipientList[]> {\n if (post.status !== 'pending' || previousStatusOf(event) === 'pending') {\n return [];\n }\n const orgId = event.orgId;\n const space = await reads.space(orgId, post.spaceId);\n if (!space) {\n return [];\n }\n const actor = actorOr(event, post.orgUserId);\n return listOf(\n {\n type: 'post.awaiting_review',\n scope: 'space',\n actorId: post.orgUserId,\n communityId: post.communityId,\n spaceId: post.spaceId,\n subject: { type: 'post', id: post.id },\n parent: null,\n display: {\n spaceName: space.name,\n postTitle: post.title,\n authorName: await reads.displayName(orgId, post.orgUserId),\n },\n },\n await recipientsOf(\n reads,\n orgId,\n new Reasons().add(await communityModerators(reads, orgId, post.communityId), 'moderator'),\n {\n exclude: [actor, post.orgUserId],\n at: eventAt(event),\n canOpen: (orgUserIds) => reads.spaceOpenersAmong(orgId, post.spaceId, orgUserIds),\n },\n ),\n );\n}\n\nconst postCreated: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.postCreated.safeParse(event);\n return parsed.success ? awaitingReview(event, reads, parsed.data.data) : [];\n};\n\nconst postUpdated: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.postUpdated.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const post = parsed.data.data;\n const orgId = event.orgId;\n const lists = await awaitingReview(event, reads, post);\n if (post.mentionedOrgUserIds.length === 0) {\n return lists;\n }\n const actor = actorOr(event, post.orgUserId);\n lists.push(\n ...(await mentionsOf(\n reads,\n orgId,\n {\n scope: 'space',\n actorId: post.orgUserId,\n communityId: post.communityId,\n spaceId: post.spaceId,\n subject: { type: 'post', id: post.id },\n parent: null,\n display: {\n authorName: await reads.displayName(orgId, post.orgUserId),\n excerpt: await textExcerpt(\n reads,\n orgId,\n reads.postText(post.body) || post.title,\n POST_EXCERPT,\n ),\n postTitle: post.title,\n },\n },\n post.mentionedOrgUserIds,\n postReach(reads, orgId, post, [actor, post.orgUserId], eventAt(event)),\n )),\n );\n return lists;\n};\n\nconst postRemoved: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.postRemoved.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const post = parsed.data.data;\n const remover = post.removedBy ?? actorOr(event, null);\n if (!remover || remover === post.orgUserId) {\n return [];\n }\n const orgId = event.orgId;\n const space = await reads.space(orgId, post.spaceId);\n if (!space) {\n return [];\n }\n return listOf(\n {\n type: 'post.removed',\n scope: 'space',\n actorId: remover,\n communityId: post.communityId,\n spaceId: post.spaceId,\n subject: { type: 'post', id: post.id },\n parent: null,\n display: { postTitle: post.title, spaceName: space.name },\n },\n await recipientsOf(\n reads,\n orgId,\n new Reasons().add([post.orgUserId], 'subject'),\n postReach(reads, orgId, post, [remover], eventAt(event)),\n ),\n );\n};\n\nconst postReacted: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.postReactionAdded.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const reaction = parsed.data.data;\n const orgId = event.orgId;\n const post = await reads.post(orgId, reaction.postId);\n if (!post) {\n return [];\n }\n return listOf(\n {\n type: 'post.reacted',\n scope: 'space',\n actorId: reaction.orgUserId,\n communityId: post.communityId,\n spaceId: post.spaceId,\n subject: { type: 'post', id: reaction.postId },\n parent: null,\n display: {\n postTitle: post.title,\n reactorName: await reads.displayName(orgId, reaction.orgUserId),\n reaction: reaction.reaction,\n },\n },\n await recipientsOf(\n reads,\n orgId,\n new Reasons().add([post.orgUserId], 'post_author'),\n postReach(\n reads,\n orgId,\n { id: reaction.postId, spaceId: post.spaceId },\n [reaction.orgUserId, actorOr(event, null)],\n eventAt(event),\n ),\n ),\n );\n};\n\nconst commentCreated: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.commentCreated.safeParse(event);\n if (!parsed.success || parsed.data.data.status !== 'published') {\n return [];\n }\n return commentLists(event, reads, parsed.data.data);\n};\n\nconst commentPublished: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.commentPublished.safeParse(event);\n return parsed.success ? commentLists(event, reads, parsed.data.data) : [];\n};\n\nconst commentUpdated: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.commentUpdated.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const comment = parsed.data.data;\n if (comment.status !== 'published' || comment.mentionedOrgUserIds.length === 0) {\n return [];\n }\n const orgId = event.orgId;\n const place = await placeOf(reads, orgId, comment);\n if (!place) {\n return [];\n }\n return mentionsOf(\n reads,\n orgId,\n commentBase('mention', place, comment.id, comment.orgUserId, {\n authorName: await reads.displayName(orgId, comment.orgUserId),\n excerpt: await textExcerpt(reads, orgId, comment.body, POST_EXCERPT),\n }),\n comment.mentionedOrgUserIds,\n placeReach(\n reads,\n orgId,\n place,\n [comment.orgUserId, actorOr(event, comment.orgUserId)],\n eventAt(event),\n ),\n );\n};\n\nconst commentRemoved: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.commentRemoved.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const comment = parsed.data.data;\n const remover = comment.removedBy ?? actorOr(event, null);\n if (!remover || remover === comment.orgUserId) {\n return [];\n }\n const orgId = event.orgId;\n const place = await placeOf(reads, orgId, comment);\n if (!place) {\n return [];\n }\n return listOf(\n commentBase('comment.removed', place, comment.id, remover, {}),\n await recipientsOf(\n reads,\n orgId,\n new Reasons().add([comment.orgUserId], 'subject'),\n placeReach(reads, orgId, place, [remover], eventAt(event)),\n ),\n );\n};\n\nconst commentReacted: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.commentReactionAdded.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const reaction = parsed.data.data;\n const orgId = event.orgId;\n const comment = await reads.comment(orgId, reaction.commentId);\n const place = comment ? await placeOf(reads, orgId, comment) : null;\n if (!comment || !place) {\n return [];\n }\n return listOf(\n commentBase('comment.reacted', place, reaction.commentId, reaction.orgUserId, {\n excerpt: await textExcerpt(reads, orgId, comment.body, POST_EXCERPT),\n reactorName: await reads.displayName(orgId, reaction.orgUserId),\n reaction: reaction.reaction,\n }),\n await recipientsOf(\n reads,\n orgId,\n new Reasons().add([comment.orgUserId], 'comment_author'),\n placeReach(reads, orgId, place, [reaction.orgUserId, actorOr(event, null)], eventAt(event)),\n ),\n );\n};\n\nconst postReported: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.postReported.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const flag = parsed.data.data;\n const orgId = event.orgId;\n const post = await reads.post(orgId, flag.postId);\n const space = post ? await reads.space(orgId, post.spaceId) : null;\n if (!post || !space) {\n return [];\n }\n return listOf(\n {\n type: 'content.flagged',\n scope: 'space',\n actorId: flag.orgUserId,\n communityId: flag.communityId,\n spaceId: post.spaceId,\n subject: { type: 'post', id: flag.postId },\n parent: null,\n display: {\n spaceName: space.name,\n excerpt: await textExcerpt(\n reads,\n orgId,\n reads.postText(post.body) || post.title,\n POST_EXCERPT,\n ),\n reason: flag.reason,\n },\n },\n await recipientsOf(\n reads,\n orgId,\n new Reasons().add(await communityModerators(reads, orgId, flag.communityId), 'moderator'),\n {\n exclude: [actorOr(event, flag.orgUserId)],\n at: eventAt(event),\n canOpen: (orgUserIds) => reads.spaceOpenersAmong(orgId, post.spaceId, orgUserIds),\n },\n ),\n );\n};\n\nconst commentReported: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.commentReported.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const flag = parsed.data.data;\n const orgId = event.orgId;\n const comment = await reads.comment(orgId, flag.commentId);\n const place = comment ? await placeOf(reads, orgId, comment) : null;\n if (!comment || !place) {\n return [];\n }\n const actor = actorOr(event, flag.orgUserId);\n const text = await textExcerpt(reads, orgId, comment.body, POST_EXCERPT);\n if (place.kind === 'post') {\n return listOf(\n {\n ...commentBase('content.flagged', place, flag.commentId, actor, {}),\n display: { spaceName: place.spaceName, excerpt: text, reason: flag.reason },\n },\n await recipientsOf(\n reads,\n orgId,\n new Reasons().add(\n await communityModerators(reads, orgId, place.post.communityId),\n 'moderator',\n ),\n {\n exclude: [actor],\n at: eventAt(event),\n canOpen: (orgUserIds) => reads.spaceOpenersAmong(orgId, place.post.spaceId, orgUserIds),\n },\n ),\n );\n }\n return listOf(\n commentBase('content.flagged', place, flag.commentId, actor, {\n excerpt: text,\n reason: flag.reason,\n }),\n await recipientsOf(\n reads,\n orgId,\n new Reasons().add(\n await reads.holders(orgId, 'courses:moderate', place.activity.courseId),\n 'moderator',\n ),\n { exclude: [actor], at: eventAt(event) },\n ),\n );\n};\n\nfunction chatPending(event: DomainEvent, status: string): boolean {\n const value = event.metadata?.[CHAT_STATUS];\n return (typeof value === 'string' ? value : status) === 'pending';\n}\n\nasync function chatMentions(\n event: DomainEvent,\n reads: NotificationReads,\n message: { id: string; chatId: string; orgUserId: string; body: string },\n mentioned: string[],\n): Promise<RecipientList[]> {\n const orgId = event.orgId;\n const chat = await reads.chat(orgId, message.chatId);\n if (!chat || chatPending(event, chat.status)) {\n return [];\n }\n return mentionsOf(\n reads,\n orgId,\n {\n scope: 'chat',\n actorId: message.orgUserId,\n communityId: chat.communityId,\n spaceId: null,\n subject: { type: 'message', id: message.id },\n parent: { type: 'chat', id: message.chatId },\n display: {\n authorName: await reads.displayName(orgId, message.orgUserId),\n excerpt: await textExcerpt(reads, orgId, message.body, MESSAGE_EXCERPT),\n chatTitle: await chatTitle(reads, orgId, chat, message.orgUserId),\n },\n },\n mentioned,\n chatReach(reads, orgId, chat, message.chatId, message.orgUserId, eventAt(event)),\n );\n}\n\nexport function messageSummary(count: number): string {\n return count === 1 ? '1 new message' : `${count} new messages`;\n}\n\nconst messageSent: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.messageSent.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const message = parsed.data.data;\n const orgId = event.orgId;\n const chat = await reads.chat(orgId, message.chatId);\n if (!chat || chatPending(event, chat.status)) {\n return [];\n }\n const senderName = await reads.displayName(orgId, message.orgUserId);\n const sent = listOf(\n {\n type: 'message.sent',\n scope: 'chat',\n actorId: message.orgUserId,\n communityId: chat.communityId,\n spaceId: null,\n subject: { type: 'message', id: message.id },\n parent: { type: 'chat', id: message.chatId },\n display: {\n senderName,\n chatTitle: await chatTitle(reads, orgId, chat, message.orgUserId),\n excerpt: await textExcerpt(reads, orgId, message.body, MESSAGE_EXCERPT),\n count: '1',\n summary: messageSummary(1),\n },\n },\n await recipientsOf(\n reads,\n orgId,\n new Reasons().add(\n chat.participants.map((participant) => participant.orgUserId),\n 'participant',\n ),\n chatReach(reads, orgId, chat, message.chatId, message.orgUserId, eventAt(event)),\n ),\n );\n return [...sent, ...(await chatMentions(event, reads, message, message.mentionedOrgUserIds))];\n};\n\nconst messageUpdated: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.messageUpdated.safeParse(event);\n if (!parsed.success || parsed.data.data.mentionedOrgUserIds.length === 0) {\n return [];\n }\n const message = parsed.data.data;\n return chatMentions(event, reads, message, message.mentionedOrgUserIds);\n};\n\nconst messageReacted: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.messageReactionAdded.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const reaction = parsed.data.data;\n const orgId = event.orgId;\n const [message, chat] = await Promise.all([\n reads.message(orgId, reaction.messageId),\n reads.chat(orgId, reaction.chatId),\n ]);\n if (!message || !chat) {\n return [];\n }\n return listOf(\n {\n type: 'message.reacted',\n scope: 'chat',\n actorId: reaction.orgUserId,\n communityId: chat.communityId,\n spaceId: null,\n subject: { type: 'message', id: reaction.messageId },\n parent: { type: 'chat', id: reaction.chatId },\n display: {\n excerpt: await textExcerpt(reads, orgId, message.body, MESSAGE_EXCERPT),\n reactorName: await reads.displayName(orgId, reaction.orgUserId),\n reaction: reaction.reaction,\n },\n },\n await recipientsOf(\n reads,\n orgId,\n new Reasons().add([message.orgUserId], 'message_author'),\n chatReach(reads, orgId, chat, reaction.chatId, reaction.orgUserId, eventAt(event)),\n ),\n );\n};\n\nconst messageReported: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.messageReported.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const flag = parsed.data.data;\n const orgId = event.orgId;\n const message = await reads.message(orgId, flag.messageId);\n return listOf(\n {\n type: 'message.flagged',\n scope: 'chat',\n actorId: flag.orgUserId,\n communityId: flag.communityId,\n spaceId: null,\n subject: { type: 'message', id: flag.messageId },\n parent: message ? { type: 'chat', id: message.chatId } : null,\n display: { reason: flag.reason },\n },\n await recipientsOf(\n reads,\n orgId,\n new Reasons().add(await communityModerators(reads, orgId, flag.communityId), 'moderator'),\n { exclude: [actorOr(event, flag.orgUserId)], at: eventAt(event) },\n ),\n );\n};\n\nconst chatCreated: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.chatCreated.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const created = parsed.data.data;\n if (created.kind !== 'direct' || created.status !== 'pending' || !created.createdBy) {\n return [];\n }\n const orgId = event.orgId;\n const chat = await reads.chat(orgId, created.id);\n if (!chat) {\n return [];\n }\n const body = await reads.latestMessageBody(orgId, created.id);\n return listOf(\n {\n type: 'chat.request',\n scope: 'chat',\n actorId: created.createdBy,\n communityId: created.communityId,\n spaceId: null,\n subject: { type: 'chat', id: created.id },\n parent: null,\n display: {\n senderName: await reads.displayName(orgId, created.createdBy),\n excerpt: await textExcerpt(reads, orgId, body ?? '', MESSAGE_EXCERPT),\n },\n },\n await recipientsOf(\n reads,\n orgId,\n new Reasons().add(created.participantIds, 'participant'),\n chatReach(reads, orgId, chat, created.id, created.createdBy, eventAt(event)),\n ),\n );\n};\n\nconst chatParticipantAdded: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.chatParticipantAdded.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const added = parsed.data.data;\n const orgId = event.orgId;\n const chat = await reads.chat(orgId, added.chatId);\n if (!chat) {\n return [];\n }\n return listOf(\n {\n type: 'chat.added',\n scope: 'chat',\n actorId: added.addedBy,\n communityId: added.communityId,\n spaceId: null,\n subject: { type: 'chat', id: added.chatId },\n parent: null,\n display: {\n adderName: await reads.displayName(orgId, added.addedBy),\n participantNames: await participantNames(reads, orgId, chat),\n },\n },\n await recipientsOf(\n reads,\n orgId,\n new Reasons().add([added.orgUserId], 'subject'),\n chatReach(reads, orgId, chat, added.chatId, added.addedBy, eventAt(event)),\n ),\n );\n};\n\nconst digestDue: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.digestDue.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const { communityId, from, to } = parsed.data.data;\n const orgId = event.orgId;\n const posts = await reads.postsPublished(orgId, communityId, from, to);\n if (posts.length === 0) {\n return [];\n }\n const title = await reads.communityTitle(orgId, communityId);\n if (title === null) {\n return [];\n }\n const spaceIds = [...new Set(posts.map((post) => post.spaceId))];\n const openers = new Map<string, Set<string>>();\n for (const spaceId of spaceIds) {\n const open = await reads.spaceOpeners(orgId, spaceId);\n const muted = new Set(\n open.length > 0\n ? await reads.mutedAmong(orgId, { type: 'space', id: spaceId }, open, eventAt(event))\n : [],\n );\n openers.set(spaceId, new Set(open.filter((orgUserId) => !muted.has(orgUserId))));\n }\n const groups = new Map<string, { spaceIds: string[]; members: Reasons }>();\n for (const orgUserId of await reads.communityMembers(orgId, communityId)) {\n const open = spaceIds.filter((spaceId) => openers.get(spaceId)?.has(orgUserId));\n if (open.length === 0) {\n continue;\n }\n const key = open.join(' ');\n const group = groups.get(key) ?? { spaceIds: open, members: new Reasons() };\n group.members.add([orgUserId], 'community_member');\n groups.set(key, group);\n }\n const lists: RecipientList[] = [];\n for (const group of groups.values()) {\n const shown = posts.filter((post) => group.spaceIds.includes(post.spaceId));\n lists.push(\n ...listOf(\n {\n type: 'digest',\n scope: 'community',\n actorId: null,\n communityId,\n spaceId: null,\n subject: { type: 'community', id: communityId },\n parent: null,\n display: {\n communityTitle: title,\n from: from.toISOString(),\n to: to.toISOString(),\n posts: shown.map((post) => ({\n postId: post.postId,\n title: post.title,\n authorName: post.authorName,\n spaceName: post.spaceName,\n commentCount: post.commentCount,\n })),\n },\n },\n await recipientsOf(reads, orgId, group.members, { exclude: [], at: eventAt(event) }),\n ),\n );\n }\n return lists;\n};\n\nexport const DISCUSSION_RULES: Record<string, RecipientRule> = {\n 'discussion.post.published': postPublished,\n 'discussion.post.created': postCreated,\n 'discussion.post.updated': postUpdated,\n 'discussion.post.removed': postRemoved,\n 'discussion.post.reaction.added': postReacted,\n 'discussion.comment.created': commentCreated,\n 'discussion.comment.published': commentPublished,\n 'discussion.comment.updated': commentUpdated,\n 'discussion.comment.removed': commentRemoved,\n 'discussion.comment.reaction.added': commentReacted,\n 'discussion.post.reported': postReported,\n 'discussion.comment.reported': commentReported,\n 'discussion.message.sent': messageSent,\n 'discussion.message.updated': messageUpdated,\n 'discussion.message.reaction.added': messageReacted,\n 'discussion.message.reported': messageReported,\n 'discussion.chat.created': chatCreated,\n 'discussion.chat.participant.added': chatParticipantAdded,\n 'discussion.digest.due': digestDue,\n};\n"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { RecipientRule } from "../../ports.js";
2
+ //#region src/automations/notifications/rules/entitlements.d.ts
3
+ declare const ENTITLEMENT_RULES: Record<string, RecipientRule>;
4
+ //#endregion
5
+ export { ENTITLEMENT_RULES };
6
+ //# sourceMappingURL=entitlements.d.ts.map
@@ -0,0 +1,276 @@
1
+ import { entitlementEvents } from "../../../entitlements/events.js";
2
+ import { communityModerators, previousStatusOf } from "../../community-mail.js";
3
+ import { Reasons, actorOr, eventAt, listOf, recipientsOf } from "./recipients.js";
4
+ import { eventBase, eventDisplay } from "./events.js";
5
+ //#region src/automations/notifications/rules/entitlements.ts
6
+ function seatsLeft(capacity, active) {
7
+ return capacity == null ? "No cap" : String(Math.max(capacity - active, 0));
8
+ }
9
+ function subjectOnly(event, reads, orgUserId) {
10
+ return recipientsOf(reads, event.orgId, new Reasons().add([orgUserId], "subject"), {
11
+ exclude: [actorOr(event, null)],
12
+ at: eventAt(event)
13
+ });
14
+ }
15
+ async function grantSubject(reads, orgId, grant) {
16
+ if (grant.bundleId) {
17
+ const info = await reads.contentInfo(orgId, { bundleId: grant.bundleId });
18
+ return info ? {
19
+ ref: {
20
+ type: "bundle",
21
+ id: grant.bundleId
22
+ },
23
+ ...info
24
+ } : null;
25
+ }
26
+ if (!grant.contentId) return null;
27
+ const info = await reads.contentInfo(orgId, { contentId: grant.contentId });
28
+ if (!info) return null;
29
+ return {
30
+ ref: {
31
+ type: info.type === "download" ? "download" : info.type === "community" ? "community" : "course",
32
+ id: grant.contentId
33
+ },
34
+ ...info
35
+ };
36
+ }
37
+ async function membershipEnded(event, reads, grant, ref) {
38
+ const orgId = event.orgId;
39
+ const communityIds = await reads.communitiesOpenedBy(orgId, grant);
40
+ if (communityIds.length === 0) return null;
41
+ const held = new Set((await reads.memberCommunityTitles(orgId, grant.orgUserId)).map((community) => community.id));
42
+ const communities = [];
43
+ for (const communityId of communityIds.filter((id) => !held.has(id))) {
44
+ const title = await reads.communityTitle(orgId, communityId);
45
+ if (title !== null) communities.push({
46
+ communityId,
47
+ communityTitle: title
48
+ });
49
+ }
50
+ if (communities.length === 0) return null;
51
+ return listOf({
52
+ type: "community.membership_ended",
53
+ scope: "org",
54
+ actorId: actorOr(event, null),
55
+ communityId: null,
56
+ spaceId: null,
57
+ subject: ref,
58
+ parent: null,
59
+ display: { communities }
60
+ }, await subjectOnly(event, reads, grant.orgUserId));
61
+ }
62
+ async function spaceBase(reads, orgId, member, type, actorId) {
63
+ const space = await reads.space(orgId, member.spaceId);
64
+ return space ? {
65
+ space,
66
+ base: {
67
+ type,
68
+ scope: "space",
69
+ actorId,
70
+ communityId: member.communityId,
71
+ spaceId: member.spaceId,
72
+ subject: {
73
+ type: "space",
74
+ id: member.spaceId
75
+ },
76
+ parent: null
77
+ }
78
+ } : null;
79
+ }
80
+ const spaceMemberCreated = async (event, reads) => {
81
+ const parsed = entitlementEvents.spaceMemberCreated.safeParse(event);
82
+ if (!parsed.success) return [];
83
+ const member = parsed.data.data;
84
+ const orgId = event.orgId;
85
+ if (member.status === "requested") {
86
+ const found = await spaceBase(reads, orgId, member, "space.join_requested", member.orgUserId);
87
+ if (!found) return [];
88
+ const active = await reads.activeMemberCount(orgId, member.spaceId);
89
+ return listOf({
90
+ ...found.base,
91
+ display: {
92
+ spaceName: found.space.name,
93
+ spaceId: member.spaceId,
94
+ memberName: await reads.displayName(orgId, member.orgUserId),
95
+ seatsLeft: seatsLeft(found.space.capacity, active)
96
+ }
97
+ }, await recipientsOf(reads, orgId, new Reasons().add(await communityModerators(reads, orgId, member.communityId), "moderator"), {
98
+ exclude: [member.orgUserId, actorOr(event, null)],
99
+ at: eventAt(event),
100
+ canOpen: (orgUserIds) => reads.spaceOpenersAmong(orgId, member.spaceId, orgUserIds)
101
+ }));
102
+ }
103
+ if (member.status === "invited") {
104
+ const inviter = actorOr(event, null);
105
+ const found = await spaceBase(reads, orgId, member, "space.invited", inviter);
106
+ if (!found) return [];
107
+ return listOf({
108
+ ...found.base,
109
+ display: {
110
+ spaceName: found.space.name,
111
+ spaceId: member.spaceId,
112
+ inviterName: inviter ? await reads.displayName(orgId, inviter) : "A member"
113
+ }
114
+ }, await subjectOnly(event, reads, member.orgUserId));
115
+ }
116
+ return [];
117
+ };
118
+ const spaceMemberUpdated = async (event, reads) => {
119
+ const parsed = entitlementEvents.spaceMemberUpdated.safeParse(event);
120
+ if (!parsed.success) return [];
121
+ const member = parsed.data.data;
122
+ if (member.status !== "active" || previousStatusOf(event) !== "requested") return [];
123
+ const found = await spaceBase(reads, event.orgId, member, "space.request_approved", actorOr(event, null));
124
+ return found ? listOf({
125
+ ...found.base,
126
+ display: {
127
+ spaceName: found.space.name,
128
+ spaceId: member.spaceId
129
+ }
130
+ }, await subjectOnly(event, reads, member.orgUserId)) : [];
131
+ };
132
+ const spaceMemberDeleted = async (event, reads) => {
133
+ const parsed = entitlementEvents.spaceMemberDeleted.safeParse(event);
134
+ if (!parsed.success || previousStatusOf(event) !== "requested") return [];
135
+ const member = parsed.data.data;
136
+ const found = await spaceBase(reads, event.orgId, member, "space.request_declined", actorOr(event, null));
137
+ return found ? listOf({
138
+ ...found.base,
139
+ display: {
140
+ spaceName: found.space.name,
141
+ spaceId: member.spaceId
142
+ }
143
+ }, await subjectOnly(event, reads, member.orgUserId)) : [];
144
+ };
145
+ async function grantedEvent(reads, orgId, grant) {
146
+ return grant.contentId ? reads.event(orgId, grant.contentId) : null;
147
+ }
148
+ const entitlementCreated = async (event, reads) => {
149
+ const parsed = entitlementEvents.entitlementCreated.safeParse(event);
150
+ if (!parsed.success) return [];
151
+ const grant = parsed.data.data;
152
+ const orgId = event.orgId;
153
+ const actor = actorOr(event, null);
154
+ const granted = await grantedEvent(reads, orgId, grant);
155
+ if (granted) {
156
+ if (!granted.settings.notifications) return [];
157
+ return listOf({
158
+ ...eventBase("event.granted", granted, actor),
159
+ display: eventDisplay(granted)
160
+ }, await recipientsOf(reads, orgId, new Reasons().add([grant.orgUserId], "event_member"), {
161
+ exclude: [],
162
+ at: eventAt(event)
163
+ }));
164
+ }
165
+ const subject = await subjectOnly(event, reads, grant.orgUserId);
166
+ if (subject.length === 0) return [];
167
+ const lists = [];
168
+ const target = await grantSubject(reads, orgId, grant);
169
+ if (target) lists.push(...listOf({
170
+ type: "access.granted",
171
+ scope: "org",
172
+ actorId: actor,
173
+ communityId: null,
174
+ spaceId: null,
175
+ subject: target.ref,
176
+ parent: null,
177
+ display: {
178
+ contentTitle: target.title,
179
+ contentType: target.type
180
+ }
181
+ }, subject));
182
+ for (const communityId of await reads.communitiesOpenedBy(orgId, grant)) {
183
+ const title = await reads.communityTitle(orgId, communityId);
184
+ if (title === null) continue;
185
+ lists.push(...listOf({
186
+ type: "community.welcome",
187
+ scope: "community",
188
+ actorId: actor,
189
+ communityId,
190
+ spaceId: null,
191
+ subject: {
192
+ type: "community",
193
+ id: communityId
194
+ },
195
+ parent: null,
196
+ display: {
197
+ communityTitle: title,
198
+ spaceNames: await reads.openSpaceNames(orgId, grant.orgUserId, communityId)
199
+ }
200
+ }, subject));
201
+ }
202
+ return lists;
203
+ };
204
+ const entitlementEndingSoon = async (event, reads) => {
205
+ const parsed = entitlementEvents.entitlementEndingSoon.safeParse(event);
206
+ if (!parsed.success) return [];
207
+ const grant = parsed.data.data;
208
+ const title = await reads.communityTitle(event.orgId, grant.communityId);
209
+ if (title === null) return [];
210
+ return listOf({
211
+ type: "community.ending_soon",
212
+ scope: "community",
213
+ actorId: null,
214
+ communityId: grant.communityId,
215
+ spaceId: null,
216
+ subject: {
217
+ type: "community",
218
+ id: grant.communityId
219
+ },
220
+ parent: null,
221
+ display: {
222
+ communityTitle: title,
223
+ expiresAt: grant.expiresAt ? grant.expiresAt.toISOString() : ""
224
+ }
225
+ }, await subjectOnly(event, reads, grant.orgUserId));
226
+ };
227
+ const entitlementDeleted = async (event, reads) => {
228
+ const parsed = entitlementEvents.entitlementDeleted.safeParse(event);
229
+ if (!parsed.success) return [];
230
+ const grant = parsed.data.data;
231
+ const revokedFrom = await grantedEvent(reads, event.orgId, grant);
232
+ if (revokedFrom) {
233
+ if (!revokedFrom.settings.notifications || !(await reads.contentLostBy(event.orgId, grant)).includes(revokedFrom.id)) return [];
234
+ const actor = actorOr(event, null);
235
+ return listOf({
236
+ ...eventBase("event.revoked", revokedFrom, actor),
237
+ display: eventDisplay(revokedFrom)
238
+ }, await recipientsOf(reads, event.orgId, new Reasons().add([grant.orgUserId], "event_member"), {
239
+ exclude: [actor],
240
+ at: eventAt(event)
241
+ }));
242
+ }
243
+ const target = await grantSubject(reads, event.orgId, grant);
244
+ const ended = await membershipEnded(event, reads, grant, target?.ref ?? null);
245
+ if (ended) return ended;
246
+ if (!target || (await reads.contentLostBy(event.orgId, grant)).length === 0) return [];
247
+ return listOf({
248
+ type: "access.revoked",
249
+ scope: "org",
250
+ actorId: actorOr(event, null),
251
+ communityId: null,
252
+ spaceId: null,
253
+ subject: target.ref,
254
+ parent: null,
255
+ display: { contentTitle: target.title }
256
+ }, await subjectOnly(event, reads, grant.orgUserId));
257
+ };
258
+ const entitlementExpired = async (event, reads) => {
259
+ const parsed = entitlementEvents.entitlementExpired.safeParse(event);
260
+ if (!parsed.success) return [];
261
+ const grant = parsed.data.data;
262
+ return await membershipEnded(event, reads, grant, (await grantSubject(reads, event.orgId, grant))?.ref ?? null) ?? [];
263
+ };
264
+ const ENTITLEMENT_RULES = {
265
+ "entitlement.space_member.created": spaceMemberCreated,
266
+ "entitlement.space_member.updated": spaceMemberUpdated,
267
+ "entitlement.space_member.deleted": spaceMemberDeleted,
268
+ "entitlement.created": entitlementCreated,
269
+ "entitlement.ending_soon": entitlementEndingSoon,
270
+ "entitlement.deleted": entitlementDeleted,
271
+ "entitlement.expired": entitlementExpired
272
+ };
273
+ //#endregion
274
+ export { ENTITLEMENT_RULES };
275
+
276
+ //# sourceMappingURL=entitlements.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entitlements.js","names":[],"sources":["../../../../src/automations/notifications/rules/entitlements.ts"],"sourcesContent":["import { entitlementEvents } from '../../../entitlements/index.js';\nimport type { DomainEvent } from '../../../shared/ports.js';\nimport type { RecipientList, SubjectRef } from '../../../types/index.js';\nimport { communityModerators, previousStatusOf } from '../../community-mail.js';\nimport type { NotificationReads, RecipientRule } from '../../ports.js';\nimport { actorOr, eventAt, listOf, Reasons, recipientsOf } from './recipients.js';\nimport { eventBase, eventDisplay } from './events.js';\n\ntype Grant = { orgUserId: string; contentId: string | null; bundleId: string | null };\ntype SpaceMember = { spaceId: string; communityId: string; orgUserId: string; status: string };\n\nfunction seatsLeft(capacity: number | null, active: number): string {\n return capacity == null ? 'No cap' : String(Math.max(capacity - active, 0));\n}\n\nfunction subjectOnly(event: DomainEvent, reads: NotificationReads, orgUserId: string) {\n return recipientsOf(reads, event.orgId, new Reasons().add([orgUserId], 'subject'), {\n exclude: [actorOr(event, null)],\n at: eventAt(event),\n });\n}\n\nasync function grantSubject(\n reads: NotificationReads,\n orgId: string,\n grant: Grant,\n): Promise<{ ref: SubjectRef; title: string; type: string } | null> {\n if (grant.bundleId) {\n const info = await reads.contentInfo(orgId, { bundleId: grant.bundleId });\n return info ? { ref: { type: 'bundle', id: grant.bundleId }, ...info } : null;\n }\n if (!grant.contentId) {\n return null;\n }\n const info = await reads.contentInfo(orgId, { contentId: grant.contentId });\n if (!info) {\n return null;\n }\n const type: SubjectRef['type'] =\n info.type === 'download' ? 'download' : info.type === 'community' ? 'community' : 'course';\n return { ref: { type, id: grant.contentId }, ...info };\n}\n\nasync function membershipEnded(\n event: DomainEvent,\n reads: NotificationReads,\n grant: Grant,\n ref: SubjectRef | null,\n): Promise<RecipientList[] | null> {\n const orgId = event.orgId;\n const communityIds = await reads.communitiesOpenedBy(orgId, grant);\n if (communityIds.length === 0) {\n return null;\n }\n const held = new Set(\n (await reads.memberCommunityTitles(orgId, grant.orgUserId)).map((community) => community.id),\n );\n const communities: { communityId: string; communityTitle: string }[] = [];\n for (const communityId of communityIds.filter((id) => !held.has(id))) {\n const title = await reads.communityTitle(orgId, communityId);\n if (title !== null) {\n communities.push({ communityId, communityTitle: title });\n }\n }\n if (communities.length === 0) {\n return null;\n }\n return listOf(\n {\n type: 'community.membership_ended',\n scope: 'org',\n actorId: actorOr(event, null),\n communityId: null,\n spaceId: null,\n subject: ref,\n parent: null,\n display: { communities },\n },\n await subjectOnly(event, reads, grant.orgUserId),\n );\n}\n\nasync function spaceBase(\n reads: NotificationReads,\n orgId: string,\n member: SpaceMember,\n type: string,\n actorId: string | null,\n) {\n const space = await reads.space(orgId, member.spaceId);\n return space\n ? {\n space,\n base: {\n type,\n scope: 'space' as const,\n actorId,\n communityId: member.communityId,\n spaceId: member.spaceId,\n subject: { type: 'space' as const, id: member.spaceId },\n parent: null,\n },\n }\n : null;\n}\n\nconst spaceMemberCreated: RecipientRule = async (event, reads) => {\n const parsed = entitlementEvents.spaceMemberCreated.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const member = parsed.data.data;\n const orgId = event.orgId;\n if (member.status === 'requested') {\n const found = await spaceBase(reads, orgId, member, 'space.join_requested', member.orgUserId);\n if (!found) {\n return [];\n }\n const active = await reads.activeMemberCount(orgId, member.spaceId);\n return listOf(\n {\n ...found.base,\n display: {\n spaceName: found.space.name,\n spaceId: member.spaceId,\n memberName: await reads.displayName(orgId, member.orgUserId),\n seatsLeft: seatsLeft(found.space.capacity, active),\n },\n },\n await recipientsOf(\n reads,\n orgId,\n new Reasons().add(await communityModerators(reads, orgId, member.communityId), 'moderator'),\n {\n exclude: [member.orgUserId, actorOr(event, null)],\n at: eventAt(event),\n canOpen: (orgUserIds) => reads.spaceOpenersAmong(orgId, member.spaceId, orgUserIds),\n },\n ),\n );\n }\n if (member.status === 'invited') {\n const inviter = actorOr(event, null);\n const found = await spaceBase(reads, orgId, member, 'space.invited', inviter);\n if (!found) {\n return [];\n }\n return listOf(\n {\n ...found.base,\n display: {\n spaceName: found.space.name,\n spaceId: member.spaceId,\n inviterName: inviter ? await reads.displayName(orgId, inviter) : 'A member',\n },\n },\n await subjectOnly(event, reads, member.orgUserId),\n );\n }\n return [];\n};\n\nconst spaceMemberUpdated: RecipientRule = async (event, reads) => {\n const parsed = entitlementEvents.spaceMemberUpdated.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const member = parsed.data.data;\n if (member.status !== 'active' || previousStatusOf(event) !== 'requested') {\n return [];\n }\n const found = await spaceBase(\n reads,\n event.orgId,\n member,\n 'space.request_approved',\n actorOr(event, null),\n );\n return found\n ? listOf(\n { ...found.base, display: { spaceName: found.space.name, spaceId: member.spaceId } },\n await subjectOnly(event, reads, member.orgUserId),\n )\n : [];\n};\n\nconst spaceMemberDeleted: RecipientRule = async (event, reads) => {\n const parsed = entitlementEvents.spaceMemberDeleted.safeParse(event);\n if (!parsed.success || previousStatusOf(event) !== 'requested') {\n return [];\n }\n const member = parsed.data.data;\n const found = await spaceBase(\n reads,\n event.orgId,\n member,\n 'space.request_declined',\n actorOr(event, null),\n );\n return found\n ? listOf(\n { ...found.base, display: { spaceName: found.space.name, spaceId: member.spaceId } },\n await subjectOnly(event, reads, member.orgUserId),\n )\n : [];\n};\n\nasync function grantedEvent(reads: NotificationReads, orgId: string, grant: Grant) {\n return grant.contentId ? reads.event(orgId, grant.contentId) : null;\n}\n\nconst entitlementCreated: RecipientRule = async (event, reads) => {\n const parsed = entitlementEvents.entitlementCreated.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const grant = parsed.data.data;\n const orgId = event.orgId;\n const actor = actorOr(event, null);\n const granted = await grantedEvent(reads, orgId, grant);\n if (granted) {\n if (!granted.settings.notifications) {\n return [];\n }\n return listOf(\n { ...eventBase('event.granted', granted, actor), display: eventDisplay(granted) },\n await recipientsOf(reads, orgId, new Reasons().add([grant.orgUserId], 'event_member'), {\n exclude: [],\n at: eventAt(event),\n }),\n );\n }\n const subject = await subjectOnly(event, reads, grant.orgUserId);\n if (subject.length === 0) {\n return [];\n }\n const lists: RecipientList[] = [];\n const target = await grantSubject(reads, orgId, grant);\n if (target) {\n lists.push(\n ...listOf(\n {\n type: 'access.granted',\n scope: 'org',\n actorId: actor,\n communityId: null,\n spaceId: null,\n subject: target.ref,\n parent: null,\n display: { contentTitle: target.title, contentType: target.type },\n },\n subject,\n ),\n );\n }\n for (const communityId of await reads.communitiesOpenedBy(orgId, grant)) {\n const title = await reads.communityTitle(orgId, communityId);\n if (title === null) {\n continue;\n }\n lists.push(\n ...listOf(\n {\n type: 'community.welcome',\n scope: 'community',\n actorId: actor,\n communityId,\n spaceId: null,\n subject: { type: 'community', id: communityId },\n parent: null,\n display: {\n communityTitle: title,\n spaceNames: await reads.openSpaceNames(orgId, grant.orgUserId, communityId),\n },\n },\n subject,\n ),\n );\n }\n return lists;\n};\n\nconst entitlementEndingSoon: RecipientRule = async (event, reads) => {\n const parsed = entitlementEvents.entitlementEndingSoon.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const grant = parsed.data.data;\n const title = await reads.communityTitle(event.orgId, grant.communityId);\n if (title === null) {\n return [];\n }\n return listOf(\n {\n type: 'community.ending_soon',\n scope: 'community',\n actorId: null,\n communityId: grant.communityId,\n spaceId: null,\n subject: { type: 'community', id: grant.communityId },\n parent: null,\n display: {\n communityTitle: title,\n expiresAt: grant.expiresAt ? grant.expiresAt.toISOString() : '',\n },\n },\n await subjectOnly(event, reads, grant.orgUserId),\n );\n};\n\nconst entitlementDeleted: RecipientRule = async (event, reads) => {\n const parsed = entitlementEvents.entitlementDeleted.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const grant = parsed.data.data;\n const revokedFrom = await grantedEvent(reads, event.orgId, grant);\n if (revokedFrom) {\n if (\n !revokedFrom.settings.notifications ||\n !(await reads.contentLostBy(event.orgId, grant)).includes(revokedFrom.id)\n ) {\n return [];\n }\n const actor = actorOr(event, null);\n return listOf(\n { ...eventBase('event.revoked', revokedFrom, actor), display: eventDisplay(revokedFrom) },\n await recipientsOf(reads, event.orgId, new Reasons().add([grant.orgUserId], 'event_member'), {\n exclude: [actor],\n at: eventAt(event),\n }),\n );\n }\n const target = await grantSubject(reads, event.orgId, grant);\n const ended = await membershipEnded(event, reads, grant, target?.ref ?? null);\n if (ended) {\n return ended;\n }\n if (!target || (await reads.contentLostBy(event.orgId, grant)).length === 0) {\n return [];\n }\n return listOf(\n {\n type: 'access.revoked',\n scope: 'org',\n actorId: actorOr(event, null),\n communityId: null,\n spaceId: null,\n subject: target.ref,\n parent: null,\n display: { contentTitle: target.title },\n },\n await subjectOnly(event, reads, grant.orgUserId),\n );\n};\n\nconst entitlementExpired: RecipientRule = async (event, reads) => {\n const parsed = entitlementEvents.entitlementExpired.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const grant = parsed.data.data;\n const target = await grantSubject(reads, event.orgId, grant);\n return (await membershipEnded(event, reads, grant, target?.ref ?? null)) ?? [];\n};\n\nexport const ENTITLEMENT_RULES: Record<string, RecipientRule> = {\n 'entitlement.space_member.created': spaceMemberCreated,\n 'entitlement.space_member.updated': spaceMemberUpdated,\n 'entitlement.space_member.deleted': spaceMemberDeleted,\n 'entitlement.created': entitlementCreated,\n 'entitlement.ending_soon': entitlementEndingSoon,\n 'entitlement.deleted': entitlementDeleted,\n 'entitlement.expired': entitlementExpired,\n};\n"],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { Event } from "../../../types/schemas/content.js";
2
+ import "../../../content/index.js";
3
+ import { ListBase } from "./recipients.js";
4
+ //#region src/automations/notifications/rules/events.d.ts
5
+ declare function eventBase(type: string, event: Event, actorId: string | null): Omit<ListBase, 'display'>;
6
+ declare function eventDisplay(event: Event): Record<string, string>;
7
+ //#endregion
8
+ export { eventBase, eventDisplay };
9
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1,27 @@
1
+ //#region src/automations/notifications/rules/events.ts
2
+ function eventBase(type, event, actorId) {
3
+ return {
4
+ type,
5
+ scope: "org",
6
+ actorId,
7
+ communityId: null,
8
+ spaceId: null,
9
+ subject: {
10
+ type: "event",
11
+ id: event.id
12
+ },
13
+ parent: null
14
+ };
15
+ }
16
+ function eventDisplay(event) {
17
+ return {
18
+ eventTitle: event.title,
19
+ startsAt: event.startsAt.toISOString(),
20
+ endsAt: event.endsAt.toISOString(),
21
+ timeZone: event.timeZone
22
+ };
23
+ }
24
+ //#endregion
25
+ export { eventBase, eventDisplay };
26
+
27
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","names":[],"sources":["../../../../src/automations/notifications/rules/events.ts"],"sourcesContent":["import type { Event } from '../../../content/index.js';\nimport type { ListBase } from './recipients.js';\n\nexport function eventBase(\n type: string,\n event: Event,\n actorId: string | null,\n): Omit<ListBase, 'display'> {\n return {\n type,\n scope: 'org',\n actorId,\n communityId: null,\n spaceId: null,\n subject: { type: 'event', id: event.id },\n parent: null,\n };\n}\n\nexport function eventDisplay(event: Event): Record<string, string> {\n return {\n eventTitle: event.title,\n startsAt: event.startsAt.toISOString(),\n endsAt: event.endsAt.toISOString(),\n timeZone: event.timeZone,\n };\n}\n"],"mappings":";AAGA,SAAgB,UACd,MACA,OACA,SAC2B;CAC3B,OAAO;EACL;EACA,OAAO;EACP;EACA,aAAa;EACb,SAAS;EACT,SAAS;GAAE,MAAM;GAAS,IAAI,MAAM;EAAG;EACvC,QAAQ;CACV;AACF;AAEA,SAAgB,aAAa,OAAsC;CACjE,OAAO;EACL,YAAY,MAAM;EAClB,UAAU,MAAM,SAAS,YAAY;EACrC,QAAQ,MAAM,OAAO,YAAY;EACjC,UAAU,MAAM;CAClB;AACF"}
@@ -0,0 +1,6 @@
1
+ import { RecipientRule } from "../../ports.js";
2
+ //#region src/automations/notifications/rules/index.d.ts
3
+ declare const RECIPIENT_RULES: Record<string, RecipientRule>;
4
+ //#endregion
5
+ export { RECIPIENT_RULES };
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,15 @@
1
+ import { DISCUSSION_RULES } from "./discussion.js";
2
+ import { CONTENT_RULES } from "./content.js";
3
+ import { ENTITLEMENT_RULES } from "./entitlements.js";
4
+ import { ORGANIZATION_RULES } from "./organizations.js";
5
+ //#region src/automations/notifications/rules/index.ts
6
+ const RECIPIENT_RULES = {
7
+ ...DISCUSSION_RULES,
8
+ ...ENTITLEMENT_RULES,
9
+ ...CONTENT_RULES,
10
+ ...ORGANIZATION_RULES
11
+ };
12
+ //#endregion
13
+ export { RECIPIENT_RULES };
14
+
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../src/automations/notifications/rules/index.ts"],"sourcesContent":["import type { RecipientRule } from '../../ports.js';\nimport { CONTENT_RULES } from './content.js';\nimport { DISCUSSION_RULES } from './discussion.js';\nimport { ENTITLEMENT_RULES } from './entitlements.js';\nimport { ORGANIZATION_RULES } from './organizations.js';\n\nexport const RECIPIENT_RULES: Record<string, RecipientRule> = {\n ...DISCUSSION_RULES,\n ...ENTITLEMENT_RULES,\n ...CONTENT_RULES,\n ...ORGANIZATION_RULES,\n};\n"],"mappings":";;;;;AAMA,MAAa,kBAAiD;CAC5D,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;AACL"}
@@ -0,0 +1,6 @@
1
+ import { RecipientRule } from "../../ports.js";
2
+ //#region src/automations/notifications/rules/organizations.d.ts
3
+ declare const ORGANIZATION_RULES: Record<string, RecipientRule>;
4
+ //#endregion
5
+ export { ORGANIZATION_RULES };
6
+ //# sourceMappingURL=organizations.d.ts.map
@@ -0,0 +1,35 @@
1
+ import { organizationEvents } from "../../../organizations/events.js";
2
+ import { roleGain } from "../../community-mail.js";
3
+ import { Reasons, actorOr, eventAt, listOf, recipientsOf } from "./recipients.js";
4
+ //#region src/automations/notifications/rules/organizations.ts
5
+ const roleAssigned = async (event, reads) => {
6
+ const parsed = organizationEvents.roleAssigned.safeParse(event);
7
+ if (!parsed.success) return [];
8
+ const assigned = parsed.data.data;
9
+ const orgId = event.orgId;
10
+ const gain = await roleGain(reads, orgId, assigned);
11
+ if (!gain) return [];
12
+ return listOf({
13
+ type: "role.assigned",
14
+ scope: "org",
15
+ actorId: actorOr(event, null),
16
+ communityId: null,
17
+ spaceId: null,
18
+ subject: null,
19
+ parent: null,
20
+ display: {
21
+ roleName: gain.roleName,
22
+ place: gain.place,
23
+ contentId: gain.contentId,
24
+ contentType: gain.contentType
25
+ }
26
+ }, await recipientsOf(reads, orgId, new Reasons().add([assigned.orgUserId], "subject"), {
27
+ exclude: [actorOr(event, null)],
28
+ at: eventAt(event)
29
+ }));
30
+ };
31
+ const ORGANIZATION_RULES = { "organization.role.assigned": roleAssigned };
32
+ //#endregion
33
+ export { ORGANIZATION_RULES };
34
+
35
+ //# sourceMappingURL=organizations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"organizations.js","names":[],"sources":["../../../../src/automations/notifications/rules/organizations.ts"],"sourcesContent":["import { organizationEvents } from '../../../organizations/index.js';\nimport { roleGain } from '../../community-mail.js';\nimport type { RecipientRule } from '../../ports.js';\nimport { actorOr, eventAt, listOf, Reasons, recipientsOf } from './recipients.js';\n\nconst roleAssigned: RecipientRule = async (event, reads) => {\n const parsed = organizationEvents.roleAssigned.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const assigned = parsed.data.data;\n const orgId = event.orgId;\n const gain = await roleGain(reads, orgId, assigned);\n if (!gain) {\n return [];\n }\n return listOf(\n {\n type: 'role.assigned',\n scope: 'org',\n actorId: actorOr(event, null),\n communityId: null,\n spaceId: null,\n subject: null,\n parent: null,\n display: {\n roleName: gain.roleName,\n place: gain.place,\n contentId: gain.contentId,\n contentType: gain.contentType,\n },\n },\n await recipientsOf(reads, orgId, new Reasons().add([assigned.orgUserId], 'subject'), {\n exclude: [actorOr(event, null)],\n at: eventAt(event),\n }),\n );\n};\n\nexport const ORGANIZATION_RULES: Record<string, RecipientRule> = {\n 'organization.role.assigned': roleAssigned,\n};\n"],"mappings":";;;;AAKA,MAAM,eAA8B,OAAO,OAAO,UAAU;CAC1D,MAAM,SAAS,mBAAmB,aAAa,UAAU,KAAK;CAC9D,IAAI,CAAC,OAAO,SACV,OAAO,CAAC;CAEV,MAAM,WAAW,OAAO,KAAK;CAC7B,MAAM,QAAQ,MAAM;CACpB,MAAM,OAAO,MAAM,SAAS,OAAO,OAAO,QAAQ;CAClD,IAAI,CAAC,MACH,OAAO,CAAC;CAEV,OAAO,OACL;EACE,MAAM;EACN,OAAO;EACP,SAAS,QAAQ,OAAO,IAAI;EAC5B,aAAa;EACb,SAAS;EACT,SAAS;EACT,QAAQ;EACR,SAAS;GACP,UAAU,KAAK;GACf,OAAO,KAAK;GACZ,WAAW,KAAK;GAChB,aAAa,KAAK;EACpB;CACF,GACA,MAAM,aAAa,OAAO,OAAO,IAAI,QAAQ,CAAC,CAAC,IAAI,CAAC,SAAS,SAAS,GAAG,SAAS,GAAG;EACnF,SAAS,CAAC,QAAQ,OAAO,IAAI,CAAC;EAC9B,IAAI,QAAQ,KAAK;CACnB,CAAC,CACH;AACF;AAEA,MAAa,qBAAoD,EAC/D,8BAA8B,aAChC"}
@@ -0,0 +1,32 @@
1
+ import { DomainEvent } from "../../../types/shared.js";
2
+ import { Reason, RecipientList, SubjectRef } from "../../../types/notifications.js";
3
+ import "../../../types/index.js";
4
+ import "../../../shared/ports.js";
5
+ import { NotificationReads } from "../../ports.js";
6
+ //#region src/automations/notifications/rules/recipients.d.ts
7
+ type Recipient = NonNullable<RecipientList['recipients']>[number];
8
+ type ListBase = Omit<RecipientList, 'recipients' | 'audience'>;
9
+ type Chat = NonNullable<Awaited<ReturnType<NotificationReads['chat']>>>;
10
+ declare class Reasons {
11
+ private readonly byPerson;
12
+ add(orgUserIds: (string | null | undefined)[], reason: Reason): this;
13
+ ids(): string[];
14
+ entries(): [string, Reason[]][];
15
+ }
16
+ interface Reach {
17
+ exclude: (string | null | undefined)[];
18
+ canOpen?: (orgUserIds: string[]) => Promise<string[]>;
19
+ mutedOn?: SubjectRef[];
20
+ at: Date;
21
+ }
22
+ declare function recipientsOf(reads: NotificationReads, orgId: string, reasons: Reasons, reach: Reach): Promise<Recipient[]>;
23
+ declare function listOf(base: ListBase, recipients: Recipient[]): RecipientList[];
24
+ declare function eventAt(event: DomainEvent): Date;
25
+ declare function actorOr(event: DomainEvent, fallback: string | null): string | null;
26
+ declare function textExcerpt(reads: NotificationReads, orgId: string, text: string, max: number): Promise<string>;
27
+ declare function participantNames(reads: NotificationReads, orgId: string, chat: Chat): Promise<string[]>;
28
+ declare function chatTitle(reads: NotificationReads, orgId: string, chat: Chat, senderOrgUserId: string): Promise<string>;
29
+ declare function chatReach(reads: NotificationReads, orgId: string, chat: Chat, chatId: string, actor: string, at: Date): Reach;
30
+ //#endregion
31
+ export { Chat, ListBase, Reach, Reasons, Recipient, actorOr, chatReach, chatTitle, eventAt, listOf, participantNames, recipientsOf, textExcerpt };
32
+ //# sourceMappingURL=recipients.d.ts.map