@headless-lms/core 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (307) hide show
  1. package/dist/assets/events.d.ts +42 -24
  2. package/dist/assets/events.js +1 -1
  3. package/dist/assets/events.js.map +1 -1
  4. package/dist/assets/file-urls.d.ts +20 -0
  5. package/dist/assets/file-urls.js +25 -0
  6. package/dist/assets/file-urls.js.map +1 -0
  7. package/dist/assets/index.d.ts +6 -4
  8. package/dist/assets/index.js +4 -2
  9. package/dist/assets/jobs.d.ts +15 -0
  10. package/dist/assets/jobs.js +30 -0
  11. package/dist/assets/jobs.js.map +1 -0
  12. package/dist/assets/model.d.ts +2 -2
  13. package/dist/assets/ports.d.ts +46 -15
  14. package/dist/assets/service.d.ts +38 -7
  15. package/dist/assets/service.js +287 -38
  16. package/dist/assets/service.js.map +1 -1
  17. package/dist/automations/actions.d.ts +13 -24
  18. package/dist/automations/actions.js +106 -42
  19. package/dist/automations/actions.js.map +1 -1
  20. package/dist/automations/catalog.js +226 -3
  21. package/dist/automations/catalog.js.map +1 -1
  22. package/dist/automations/community-derivations.d.ts +14 -0
  23. package/dist/automations/community-derivations.js +439 -0
  24. package/dist/automations/community-derivations.js.map +1 -0
  25. package/dist/automations/community-mail.d.ts +98 -0
  26. package/dist/automations/community-mail.js +70 -0
  27. package/dist/automations/community-mail.js.map +1 -0
  28. package/dist/automations/events.d.ts +18 -0
  29. package/dist/automations/index.d.ts +13 -4
  30. package/dist/automations/index.js +10 -2
  31. package/dist/automations/model.d.ts +10 -3
  32. package/dist/automations/model.js +15 -2
  33. package/dist/automations/model.js.map +1 -1
  34. package/dist/automations/notifications/debounce.d.ts +25 -0
  35. package/dist/automations/notifications/debounce.js +93 -0
  36. package/dist/automations/notifications/debounce.js.map +1 -0
  37. package/dist/automations/notifications/defaults.d.ts +8 -0
  38. package/dist/automations/notifications/defaults.js +316 -0
  39. package/dist/automations/notifications/defaults.js.map +1 -0
  40. package/dist/automations/notifications/delivery.d.ts +28 -0
  41. package/dist/automations/notifications/delivery.js +88 -0
  42. package/dist/automations/notifications/delivery.js.map +1 -0
  43. package/dist/automations/notifications/processor.d.ts +64 -0
  44. package/dist/automations/notifications/processor.js +270 -0
  45. package/dist/automations/notifications/processor.js.map +1 -0
  46. package/dist/automations/notifications/rules/content.d.ts +6 -0
  47. package/dist/automations/notifications/rules/content.js +39 -0
  48. package/dist/automations/notifications/rules/content.js.map +1 -0
  49. package/dist/automations/notifications/rules/discussion.d.ts +7 -0
  50. package/dist/automations/notifications/rules/discussion.js +678 -0
  51. package/dist/automations/notifications/rules/discussion.js.map +1 -0
  52. package/dist/automations/notifications/rules/entitlements.d.ts +6 -0
  53. package/dist/automations/notifications/rules/entitlements.js +249 -0
  54. package/dist/automations/notifications/rules/entitlements.js.map +1 -0
  55. package/dist/automations/notifications/rules/index.d.ts +6 -0
  56. package/dist/automations/notifications/rules/index.js +15 -0
  57. package/dist/automations/notifications/rules/index.js.map +1 -0
  58. package/dist/automations/notifications/rules/organizations.d.ts +6 -0
  59. package/dist/automations/notifications/rules/organizations.js +28 -0
  60. package/dist/automations/notifications/rules/organizations.js.map +1 -0
  61. package/dist/automations/notifications/rules/recipients.d.ts +32 -0
  62. package/dist/automations/notifications/rules/recipients.js +99 -0
  63. package/dist/automations/notifications/rules/recipients.js.map +1 -0
  64. package/dist/automations/notifications/service.d.ts +27 -0
  65. package/dist/automations/notifications/service.js +45 -0
  66. package/dist/automations/notifications/service.js.map +1 -0
  67. package/dist/automations/notifications/types.d.ts +7 -0
  68. package/dist/automations/notifications/types.js +473 -0
  69. package/dist/automations/notifications/types.js.map +1 -0
  70. package/dist/automations/ports.d.ts +121 -3
  71. package/dist/automations/schedule.d.ts +6 -0
  72. package/dist/automations/schedule.js +41 -0
  73. package/dist/automations/schedule.js.map +1 -0
  74. package/dist/automations/service.d.ts +20 -4
  75. package/dist/automations/service.js +246 -45
  76. package/dist/automations/service.js.map +1 -1
  77. package/dist/content/events.d.ts +712 -50
  78. package/dist/content/events.js +55 -1
  79. package/dist/content/events.js.map +1 -1
  80. package/dist/content/index.d.ts +6 -4
  81. package/dist/content/index.js +3 -1
  82. package/dist/content/model.d.ts +2 -2
  83. package/dist/content/notification-categories.d.ts +36 -0
  84. package/dist/content/notification-categories.js +64 -0
  85. package/dist/content/notification-categories.js.map +1 -0
  86. package/dist/content/ports.d.ts +90 -18
  87. package/dist/content/service.d.ts +57 -18
  88. package/dist/content/service.js +520 -25
  89. package/dist/content/service.js.map +1 -1
  90. package/dist/content/titles.d.ts +22 -0
  91. package/dist/content/titles.js +69 -0
  92. package/dist/content/titles.js.map +1 -0
  93. package/dist/content/types.d.ts +2 -2
  94. package/dist/discussion/authors.d.ts +10 -0
  95. package/dist/discussion/authors.js +23 -0
  96. package/dist/discussion/authors.js.map +1 -0
  97. package/dist/discussion/chats.d.ts +109 -0
  98. package/dist/discussion/chats.js +831 -0
  99. package/dist/discussion/chats.js.map +1 -0
  100. package/dist/discussion/digests.d.ts +24 -0
  101. package/dist/discussion/digests.js +104 -0
  102. package/dist/discussion/digests.js.map +1 -0
  103. package/dist/discussion/events.d.ts +785 -56
  104. package/dist/discussion/events.js +165 -5
  105. package/dist/discussion/events.js.map +1 -1
  106. package/dist/discussion/index.d.ts +12 -3
  107. package/dist/discussion/index.js +9 -1
  108. package/dist/discussion/jobs.d.ts +16 -0
  109. package/dist/discussion/jobs.js +40 -0
  110. package/dist/discussion/jobs.js.map +1 -0
  111. package/dist/discussion/mentions.d.ts +11 -0
  112. package/dist/discussion/mentions.js +35 -0
  113. package/dist/discussion/mentions.js.map +1 -0
  114. package/dist/discussion/ports.d.ts +372 -28
  115. package/dist/discussion/ports.js +6 -1
  116. package/dist/discussion/ports.js.map +1 -0
  117. package/dist/discussion/posts.d.ts +113 -0
  118. package/dist/discussion/posts.js +775 -0
  119. package/dist/discussion/posts.js.map +1 -0
  120. package/dist/discussion/service.d.ts +42 -11
  121. package/dist/discussion/service.js +364 -85
  122. package/dist/discussion/service.js.map +1 -1
  123. package/dist/discussion/subscriptions.d.ts +37 -0
  124. package/dist/discussion/subscriptions.js +99 -0
  125. package/dist/discussion/subscriptions.js.map +1 -0
  126. package/dist/discussion/titles.d.ts +26 -0
  127. package/dist/discussion/titles.js +84 -0
  128. package/dist/discussion/titles.js.map +1 -0
  129. package/dist/entitlements/events.d.ts +178 -11
  130. package/dist/entitlements/events.js +28 -1
  131. package/dist/entitlements/events.js.map +1 -1
  132. package/dist/entitlements/index.d.ts +6 -4
  133. package/dist/entitlements/index.js +3 -1
  134. package/dist/entitlements/jobs.d.ts +12 -0
  135. package/dist/entitlements/jobs.js +18 -0
  136. package/dist/entitlements/jobs.js.map +1 -0
  137. package/dist/entitlements/model.d.ts +3 -2
  138. package/dist/entitlements/ports.d.ts +128 -11
  139. package/dist/entitlements/rules.d.ts +12 -0
  140. package/dist/entitlements/rules.js +17 -0
  141. package/dist/entitlements/rules.js.map +1 -0
  142. package/dist/entitlements/service.d.ts +57 -3
  143. package/dist/entitlements/service.js +492 -2
  144. package/dist/entitlements/service.js.map +1 -1
  145. package/dist/identity/access.d.ts +6 -0
  146. package/dist/identity/access.js +7 -0
  147. package/dist/identity/access.js.map +1 -0
  148. package/dist/identity/devices.d.ts +30 -0
  149. package/dist/identity/devices.js +107 -0
  150. package/dist/identity/devices.js.map +1 -0
  151. package/dist/identity/events.d.ts +2 -2
  152. package/dist/identity/index.d.ts +7 -4
  153. package/dist/identity/index.js +4 -1
  154. package/dist/identity/model.d.ts +2 -1
  155. package/dist/identity/notification-settings.d.ts +28 -0
  156. package/dist/identity/notification-settings.js +113 -0
  157. package/dist/identity/notification-settings.js.map +1 -0
  158. package/dist/identity/ports.d.ts +97 -3
  159. package/dist/identity/service.d.ts +14 -3
  160. package/dist/identity/service.js +62 -5
  161. package/dist/identity/service.js.map +1 -1
  162. package/dist/integrations/events.d.ts +3 -3
  163. package/dist/integrations/model.js +1 -1
  164. package/dist/integrations/model.js.map +1 -1
  165. package/dist/integrations/service.js +2 -2
  166. package/dist/integrations/service.js.map +1 -1
  167. package/dist/organizations/access-scopes.d.ts +19 -0
  168. package/dist/organizations/access-scopes.js +89 -0
  169. package/dist/organizations/access-scopes.js.map +1 -0
  170. package/dist/organizations/events.d.ts +75 -33
  171. package/dist/organizations/events.js +6 -0
  172. package/dist/organizations/events.js.map +1 -1
  173. package/dist/organizations/index.d.ts +8 -6
  174. package/dist/organizations/index.js +5 -3
  175. package/dist/organizations/members.d.ts +8 -5
  176. package/dist/organizations/members.js.map +1 -1
  177. package/dist/organizations/notification-categories.d.ts +32 -0
  178. package/dist/organizations/notification-categories.js +51 -0
  179. package/dist/organizations/notification-categories.js.map +1 -0
  180. package/dist/organizations/ports.d.ts +17 -11
  181. package/dist/organizations/roles.d.ts +6 -10
  182. package/dist/organizations/roles.js +19 -7
  183. package/dist/organizations/roles.js.map +1 -1
  184. package/dist/organizations/service.d.ts +22 -11
  185. package/dist/organizations/service.js +164 -47
  186. package/dist/organizations/service.js.map +1 -1
  187. package/dist/organizations/types.d.ts +1 -10
  188. package/dist/progress/events.d.ts +14 -14
  189. package/dist/progress/index.d.ts +2 -2
  190. package/dist/progress/ports.d.ts +6 -1
  191. package/dist/progress/service.d.ts +3 -1
  192. package/dist/progress/service.js +6 -2
  193. package/dist/progress/service.js.map +1 -1
  194. package/dist/reporting/admin/index.d.ts +4 -0
  195. package/dist/reporting/admin/index.js +2 -0
  196. package/dist/reporting/admin/model.d.ts +108 -0
  197. package/dist/reporting/admin/ports.d.ts +27 -0
  198. package/dist/reporting/admin/service.d.ts +22 -0
  199. package/dist/reporting/admin/service.js +38 -0
  200. package/dist/reporting/admin/service.js.map +1 -0
  201. package/dist/reporting/member/index.d.ts +5 -0
  202. package/dist/reporting/member/index.js +2 -0
  203. package/dist/reporting/member/model.d.ts +157 -0
  204. package/dist/reporting/member/ports.d.ts +48 -0
  205. package/dist/reporting/member/service.d.ts +92 -0
  206. package/dist/reporting/member/service.js +364 -0
  207. package/dist/reporting/member/service.js.map +1 -0
  208. package/dist/shared/actor.d.ts +9 -0
  209. package/dist/shared/errors.d.ts +19 -1
  210. package/dist/shared/errors.js +27 -1
  211. package/dist/shared/errors.js.map +1 -1
  212. package/dist/shared/id.d.ts +14 -0
  213. package/dist/shared/id.js +15 -1
  214. package/dist/shared/id.js.map +1 -1
  215. package/dist/shared/mailer.d.ts +15 -2
  216. package/dist/shared/mailer.js +32 -5
  217. package/dist/shared/mailer.js.map +1 -1
  218. package/dist/shared/network.d.ts +10 -0
  219. package/dist/shared/network.js +211 -0
  220. package/dist/shared/network.js.map +1 -0
  221. package/dist/shared/notification-categories.d.ts +16 -0
  222. package/dist/shared/notification-categories.js +94 -0
  223. package/dist/shared/notification-categories.js.map +1 -0
  224. package/dist/shared/notification-settings.d.ts +14 -0
  225. package/dist/shared/notification-settings.js +61 -0
  226. package/dist/shared/notification-settings.js.map +1 -0
  227. package/dist/shared/ports.d.ts +48 -2
  228. package/dist/shared/settings.d.ts +2 -1
  229. package/dist/shared/settings.js +1 -0
  230. package/dist/shared/settings.js.map +1 -1
  231. package/dist/types/assets.d.ts +2 -2
  232. package/dist/types/automations.d.ts +15 -1
  233. package/dist/types/content.d.ts +2 -2
  234. package/dist/types/discussion.d.ts +11 -3
  235. package/dist/types/email-templates.d.ts +216 -1
  236. package/dist/types/entitlements.d.ts +2 -2
  237. package/dist/types/identity.d.ts +48 -2
  238. package/dist/types/index.d.ts +9 -8
  239. package/dist/types/notifications.d.ts +157 -0
  240. package/dist/types/ports.d.ts +6 -0
  241. package/dist/types/schemas/assets.d.ts +65 -37
  242. package/dist/types/schemas/assets.js +17 -12
  243. package/dist/types/schemas/assets.js.map +1 -1
  244. package/dist/types/schemas/automations.d.ts +9 -1
  245. package/dist/types/schemas/automations.js +13 -6
  246. package/dist/types/schemas/automations.js.map +1 -1
  247. package/dist/types/schemas/content.d.ts +396 -3
  248. package/dist/types/schemas/content.js +161 -3
  249. package/dist/types/schemas/content.js.map +1 -1
  250. package/dist/types/schemas/discussion.d.ts +443 -3
  251. package/dist/types/schemas/discussion.js +233 -4
  252. package/dist/types/schemas/discussion.js.map +1 -1
  253. package/dist/types/schemas/entitlements.d.ts +36 -2
  254. package/dist/types/schemas/entitlements.js +23 -1
  255. package/dist/types/schemas/entitlements.js.map +1 -1
  256. package/dist/types/schemas/identity.d.ts +27 -1
  257. package/dist/types/schemas/identity.js +14 -2
  258. package/dist/types/schemas/identity.js.map +1 -1
  259. package/dist/types/schemas/index.d.ts +9 -8
  260. package/dist/types/schemas/index.js +9 -8
  261. package/dist/types/schemas/notifications.d.ts +95 -0
  262. package/dist/types/schemas/notifications.js +56 -0
  263. package/dist/types/schemas/notifications.js.map +1 -0
  264. package/dist/types/schemas/organizations.d.ts +8 -0
  265. package/dist/types/schemas/organizations.js +2 -0
  266. package/dist/types/schemas/organizations.js.map +1 -1
  267. package/dist/types/schemas/progress.d.ts +3 -3
  268. package/dist/types/schemas/shared.d.ts +3 -1
  269. package/dist/types/schemas/shared.js +5 -1
  270. package/dist/types/schemas/shared.js.map +1 -1
  271. package/package.json +2 -2
  272. package/dist/reporting/courses/index.d.ts +0 -4
  273. package/dist/reporting/courses/index.js +0 -2
  274. package/dist/reporting/courses/model.d.ts +0 -24
  275. package/dist/reporting/courses/ports.d.ts +0 -13
  276. package/dist/reporting/courses/service.d.ts +0 -19
  277. package/dist/reporting/courses/service.js +0 -20
  278. package/dist/reporting/courses/service.js.map +0 -1
  279. package/dist/reporting/dashboard/index.d.ts +0 -4
  280. package/dist/reporting/dashboard/index.js +0 -2
  281. package/dist/reporting/dashboard/model.d.ts +0 -14
  282. package/dist/reporting/dashboard/ports.d.ts +0 -13
  283. package/dist/reporting/dashboard/service.d.ts +0 -19
  284. package/dist/reporting/dashboard/service.js +0 -20
  285. package/dist/reporting/dashboard/service.js.map +0 -1
  286. package/dist/reporting/learn/index.d.ts +0 -5
  287. package/dist/reporting/learn/index.js +0 -2
  288. package/dist/reporting/learn/model.d.ts +0 -23
  289. package/dist/reporting/learn/ports.d.ts +0 -43
  290. package/dist/reporting/learn/service.d.ts +0 -49
  291. package/dist/reporting/learn/service.js +0 -116
  292. package/dist/reporting/learn/service.js.map +0 -1
  293. package/dist/reporting/students/index.d.ts +0 -4
  294. package/dist/reporting/students/index.js +0 -2
  295. package/dist/reporting/students/model.d.ts +0 -51
  296. package/dist/reporting/students/model.js +0 -1
  297. package/dist/reporting/students/ports.d.ts +0 -15
  298. package/dist/reporting/students/ports.js +0 -1
  299. package/dist/reporting/students/service.d.ts +0 -20
  300. package/dist/reporting/students/service.js +0 -23
  301. package/dist/reporting/students/service.js.map +0 -1
  302. /package/dist/reporting/{courses → admin}/model.js +0 -0
  303. /package/dist/reporting/{courses → admin}/ports.js +0 -0
  304. /package/dist/reporting/{dashboard → member}/model.js +0 -0
  305. /package/dist/reporting/{dashboard → member}/ports.js +0 -0
  306. /package/dist/{reporting/learn/model.js → shared/actor.js} +0 -0
  307. /package/dist/{reporting/learn/ports.js → types/notifications.js} +0 -0
@@ -0,0 +1,439 @@
1
+ import { organizationEvents } from "../organizations/events.js";
2
+ import { entitlementEvents } from "../entitlements/events.js";
3
+ import { discussionEvents } from "../discussion/events.js";
4
+ import { MESSAGE_BATCH_MS, actorOf, address, excerpt, previousStatusOf } from "./community-mail.js";
5
+ //#region src/automations/community-derivations.ts
6
+ function silence(triggers) {
7
+ return {
8
+ triggers,
9
+ derive: async () => []
10
+ };
11
+ }
12
+ function seatsLeft(capacity, active) {
13
+ if (capacity == null) return "No cap";
14
+ return String(Math.max(capacity - active, 0));
15
+ }
16
+ function muted(participant, at) {
17
+ return participant.muted && (participant.mutedUntil === null || participant.mutedUntil.getTime() > at.getTime());
18
+ }
19
+ function chatStatusOf(event) {
20
+ const value = event.metadata?.chatStatus;
21
+ return typeof value === "string" ? value : void 0;
22
+ }
23
+ async function communityTitle(deps, orgId, communityId) {
24
+ return deps.mail.communityTitle(orgId, communityId);
25
+ }
26
+ const COMMUNITY_DERIVATIONS = {
27
+ welcomeToCommunity: {
28
+ triggers: ["entitlement.created"],
29
+ derive: async (event, deps) => {
30
+ const parsed = entitlementEvents.entitlementCreated.safeParse(event);
31
+ if (!parsed.success) return;
32
+ const grant = parsed.data.data;
33
+ const communityIds = await deps.mail.communitiesOpenedBy(event.orgId, {
34
+ contentId: grant.contentId,
35
+ bundleId: grant.bundleId
36
+ });
37
+ const recipients = [];
38
+ for (const communityId of communityIds) {
39
+ const title = await communityTitle(deps, event.orgId, communityId);
40
+ if (!title) return;
41
+ const spaceNames = await deps.mail.openSpaceNames(event.orgId, grant.orgUserId, communityId);
42
+ const addressed = await address(deps, event, event.orgId, [grant.orgUserId], {
43
+ communityTitle: title,
44
+ communityId,
45
+ spaceNames
46
+ });
47
+ if (!addressed) return;
48
+ recipients.push(...addressed);
49
+ }
50
+ return recipients;
51
+ }
52
+ },
53
+ membershipEndingSoon: {
54
+ triggers: ["entitlement.ending_soon"],
55
+ derive: async (event, deps) => {
56
+ const parsed = entitlementEvents.entitlementEndingSoon.safeParse(event);
57
+ if (!parsed.success) return;
58
+ const grant = parsed.data.data;
59
+ const communityId = grant.communityId;
60
+ const title = await communityTitle(deps, event.orgId, communityId);
61
+ if (!title) return;
62
+ return address(deps, event, event.orgId, [grant.orgUserId], {
63
+ communityTitle: title,
64
+ communityId,
65
+ expiresAt: grant.expiresAt ? grant.expiresAt.toISOString() : ""
66
+ });
67
+ }
68
+ },
69
+ membershipEnded: {
70
+ triggers: ["entitlement.expired", "entitlement.deleted"],
71
+ derive: async (event, deps) => {
72
+ const parsed = event.type === "entitlement.expired" ? entitlementEvents.entitlementExpired.safeParse(event) : entitlementEvents.entitlementDeleted.safeParse(event);
73
+ if (!parsed.success) return;
74
+ const grant = parsed.data.data;
75
+ const communityIds = await deps.mail.communitiesOpenedBy(event.orgId, {
76
+ contentId: grant.contentId,
77
+ bundleId: grant.bundleId
78
+ });
79
+ const recipients = [];
80
+ for (const communityId of communityIds) {
81
+ const title = await communityTitle(deps, event.orgId, communityId);
82
+ if (!title) return;
83
+ const addressed = await address(deps, event, event.orgId, [grant.orgUserId], { communities: [{
84
+ communityId,
85
+ communityTitle: title
86
+ }] });
87
+ if (!addressed) return;
88
+ recipients.push(...addressed);
89
+ }
90
+ return recipients;
91
+ }
92
+ },
93
+ newPostInSpace: {
94
+ triggers: ["discussion.post.published"],
95
+ derive: async (event, deps) => {
96
+ const parsed = discussionEvents.postPublished.safeParse(event);
97
+ if (!parsed.success) return;
98
+ const post = parsed.data.data;
99
+ const space = await deps.mail.space(event.orgId, post.spaceId);
100
+ if (!space) return;
101
+ const openers = await deps.mail.spaceOpeners(event.orgId, post.spaceId);
102
+ return address(deps, event, event.orgId, openers.filter((id) => id !== post.orgUserId), {
103
+ spaceName: space.name,
104
+ postTitle: post.title,
105
+ authorName: await deps.mail.displayName(event.orgId, post.orgUserId),
106
+ excerpt: excerpt(post.body || post.title, 280),
107
+ postId: post.id,
108
+ communityId: post.communityId
109
+ });
110
+ }
111
+ },
112
+ yourPostWasPublished: {
113
+ triggers: ["discussion.post.published"],
114
+ derive: async (event, deps) => {
115
+ if (previousStatusOf(event) !== "pending") return [];
116
+ const parsed = discussionEvents.postPublished.safeParse(event);
117
+ if (!parsed.success) return;
118
+ const post = parsed.data.data;
119
+ const space = await deps.mail.space(event.orgId, post.spaceId);
120
+ if (!space) return;
121
+ return address(deps, event, event.orgId, [post.orgUserId], {
122
+ postTitle: post.title,
123
+ spaceName: space.name,
124
+ postId: post.id,
125
+ communityId: post.communityId
126
+ });
127
+ }
128
+ },
129
+ yourPostWasRemoved: {
130
+ triggers: ["discussion.post.removed"],
131
+ derive: async (event, deps) => {
132
+ const parsed = discussionEvents.postRemoved.safeParse(event);
133
+ if (!parsed.success) return;
134
+ const post = parsed.data.data;
135
+ if (post.removedBy === post.orgUserId) return [];
136
+ const space = await deps.mail.space(event.orgId, post.spaceId);
137
+ if (!space) return;
138
+ return address(deps, event, event.orgId, [post.orgUserId], {
139
+ postTitle: post.title,
140
+ spaceName: space.name,
141
+ spaceId: post.spaceId,
142
+ communityId: post.communityId
143
+ });
144
+ }
145
+ },
146
+ newCommentOnPost: {
147
+ triggers: ["discussion.comment.created"],
148
+ derive: async (event, deps) => {
149
+ const parsed = discussionEvents.commentCreated.safeParse(event);
150
+ if (!parsed.success) return;
151
+ const comment = parsed.data.data;
152
+ if (!comment.postId || comment.status !== "published" || !comment.communityId) return [];
153
+ const post = await deps.mail.post(event.orgId, comment.postId);
154
+ if (!post) return;
155
+ const followers = await deps.mail.followerIds(event.orgId, comment.postId);
156
+ const ids = [.../* @__PURE__ */ new Set([...followers, post.orgUserId])].filter((id) => id !== comment.orgUserId);
157
+ const openers = await deps.mail.spaceOpenersAmong(event.orgId, post.spaceId, ids);
158
+ return address(deps, event, event.orgId, openers, {
159
+ postTitle: post.title,
160
+ commenterName: await deps.mail.displayName(event.orgId, comment.orgUserId),
161
+ excerpt: excerpt(comment.body, 280),
162
+ commentId: comment.id,
163
+ postId: comment.postId,
164
+ communityId: comment.communityId
165
+ });
166
+ }
167
+ },
168
+ replyToYourComment: {
169
+ triggers: ["discussion.comment.created"],
170
+ derive: async (event, deps) => {
171
+ const parsed = discussionEvents.commentCreated.safeParse(event);
172
+ if (!parsed.success) return;
173
+ const comment = parsed.data.data;
174
+ if (!comment.parentId || !comment.postId || comment.status !== "published") return [];
175
+ const parent = await deps.mail.comment(event.orgId, comment.parentId);
176
+ const post = await deps.mail.post(event.orgId, comment.postId);
177
+ if (!parent || !post || !comment.communityId) return parent && post ? [] : void 0;
178
+ if (parent.orgUserId === comment.orgUserId) return [];
179
+ const openers = await deps.mail.spaceOpenersAmong(event.orgId, post.spaceId, [parent.orgUserId]);
180
+ return address(deps, event, event.orgId, openers, {
181
+ postTitle: post.title,
182
+ commenterName: await deps.mail.displayName(event.orgId, comment.orgUserId),
183
+ excerpt: excerpt(comment.body, 280),
184
+ commentId: comment.id,
185
+ postId: comment.postId,
186
+ communityId: comment.communityId
187
+ });
188
+ }
189
+ },
190
+ yourCommentWasRemoved: {
191
+ triggers: ["discussion.comment.removed"],
192
+ derive: async (event, deps) => {
193
+ const parsed = discussionEvents.commentRemoved.safeParse(event);
194
+ if (!parsed.success) return;
195
+ const comment = parsed.data.data;
196
+ if (!comment.postId || !comment.communityId) return [];
197
+ if (comment.removedBy === comment.orgUserId) return [];
198
+ const post = await deps.mail.post(event.orgId, comment.postId);
199
+ if (!post) return;
200
+ return address(deps, event, event.orgId, [comment.orgUserId], {
201
+ postTitle: post.title,
202
+ postId: comment.postId,
203
+ communityId: comment.communityId
204
+ });
205
+ }
206
+ },
207
+ postAwaitingReview: {
208
+ triggers: ["discussion.post.created", "discussion.post.updated"],
209
+ derive: async (event, deps) => {
210
+ const parsed = event.type === "discussion.post.created" ? discussionEvents.postCreated.safeParse(event) : discussionEvents.postUpdated.safeParse(event);
211
+ if (!parsed.success) return;
212
+ const post = parsed.data.data;
213
+ if (post.status !== "pending" || previousStatusOf(event) === "pending") return [];
214
+ const space = await deps.mail.space(event.orgId, post.spaceId);
215
+ if (!space) return;
216
+ const audience = await deps.mail.moderationAudience(event.orgId, post.communityId);
217
+ return address(deps, event, event.orgId, audience, {
218
+ spaceName: space.name,
219
+ postTitle: post.title,
220
+ authorName: await deps.mail.displayName(event.orgId, post.orgUserId),
221
+ spaceId: post.spaceId,
222
+ communityId: post.communityId
223
+ });
224
+ }
225
+ },
226
+ joinRequest: {
227
+ triggers: ["entitlement.space_member.created"],
228
+ derive: async (event, deps) => {
229
+ const parsed = entitlementEvents.spaceMemberCreated.safeParse(event);
230
+ if (!parsed.success) return;
231
+ const member = parsed.data.data;
232
+ if (member.status !== "requested") return [];
233
+ const space = await deps.mail.space(event.orgId, member.spaceId);
234
+ if (!space) return;
235
+ const active = await deps.mail.activeMemberCount(event.orgId, member.spaceId);
236
+ const audience = await deps.mail.moderationAudience(event.orgId, member.communityId);
237
+ return address(deps, event, event.orgId, audience, {
238
+ spaceName: space.name,
239
+ memberName: await deps.mail.displayName(event.orgId, member.orgUserId),
240
+ seatsLeft: seatsLeft(space.capacity, active),
241
+ spaceId: member.spaceId,
242
+ communityId: member.communityId
243
+ });
244
+ }
245
+ },
246
+ requestApproved: {
247
+ triggers: ["entitlement.space_member.updated"],
248
+ derive: async (event, deps) => {
249
+ const parsed = entitlementEvents.spaceMemberUpdated.safeParse(event);
250
+ if (!parsed.success) return;
251
+ const member = parsed.data.data;
252
+ if (member.status !== "active" || previousStatusOf(event) !== "requested") return [];
253
+ const space = await deps.mail.space(event.orgId, member.spaceId);
254
+ if (!space) return;
255
+ return address(deps, event, event.orgId, [member.orgUserId], {
256
+ spaceName: space.name,
257
+ spaceId: member.spaceId,
258
+ communityId: member.communityId
259
+ });
260
+ }
261
+ },
262
+ requestDeclined: {
263
+ triggers: ["entitlement.space_member.deleted"],
264
+ derive: async (event, deps) => {
265
+ if (previousStatusOf(event) !== "requested") return [];
266
+ const parsed = entitlementEvents.spaceMemberDeleted.safeParse(event);
267
+ if (!parsed.success) return;
268
+ const member = parsed.data.data;
269
+ const space = await deps.mail.space(event.orgId, member.spaceId);
270
+ if (!space) return;
271
+ return address(deps, event, event.orgId, [member.orgUserId], {
272
+ spaceName: space.name,
273
+ communityId: member.communityId
274
+ });
275
+ }
276
+ },
277
+ invitedToSpace: {
278
+ triggers: ["entitlement.space_member.created"],
279
+ derive: async (event, deps) => {
280
+ const parsed = entitlementEvents.spaceMemberCreated.safeParse(event);
281
+ if (!parsed.success) return;
282
+ const member = parsed.data.data;
283
+ if (member.status !== "invited") return [];
284
+ const space = await deps.mail.space(event.orgId, member.spaceId);
285
+ if (!space) return;
286
+ const actor = actorOf(event);
287
+ return address(deps, event, event.orgId, [member.orgUserId], {
288
+ spaceName: space.name,
289
+ inviterName: actor ? await deps.mail.displayName(event.orgId, actor) : "A member",
290
+ spaceId: member.spaceId,
291
+ communityId: member.communityId
292
+ });
293
+ }
294
+ },
295
+ messageRequest: {
296
+ triggers: ["discussion.chat.created"],
297
+ derive: async (event, deps) => {
298
+ const parsed = discussionEvents.chatCreated.safeParse(event);
299
+ if (!parsed.success) return;
300
+ const chat = parsed.data.data;
301
+ if (chat.kind !== "direct" || chat.status !== "pending") return [];
302
+ const recipient = chat.participantIds.find((id) => id !== chat.createdBy);
303
+ if (!recipient) return [];
304
+ const members = await deps.mail.communityMembersAmong(event.orgId, chat.communityId, [recipient]);
305
+ const body = await deps.mail.latestMessageBody(event.orgId, chat.id);
306
+ return address(deps, event, event.orgId, members, {
307
+ senderName: chat.createdBy ? await deps.mail.displayName(event.orgId, chat.createdBy) : "A member",
308
+ excerpt: excerpt(body ?? "", 140),
309
+ chatId: chat.id,
310
+ communityId: chat.communityId
311
+ });
312
+ }
313
+ },
314
+ newMessage: {
315
+ triggers: ["discussion.message.sent"],
316
+ derive: async (event, deps) => {
317
+ const parsed = discussionEvents.messageSent.safeParse(event);
318
+ if (!parsed.success) return;
319
+ const message = parsed.data.data;
320
+ const chat = await deps.mail.chat(event.orgId, message.chatId);
321
+ if (!chat) return;
322
+ if (chatStatusOf(event) === "pending") return [];
323
+ const at = new Date(event.occurredAt);
324
+ const since = new Date(at.getTime() - MESSAGE_BATCH_MS);
325
+ const recent = new Set(await deps.mail.recentMessageRecipients(event.orgId, deps.automationId, message.chatId, since));
326
+ const ids = [];
327
+ for (const participant of chat.participants) {
328
+ if (participant.leftAt || participant.orgUserId === message.orgUserId) continue;
329
+ if (muted(participant, at) || recent.has(participant.orgUserId)) continue;
330
+ if (await deps.mail.pairBlocked(event.orgId, chat.communityId, message.orgUserId, participant.orgUserId)) continue;
331
+ ids.push(participant.orgUserId);
332
+ }
333
+ const members = await deps.mail.communityMembersAmong(event.orgId, chat.communityId, ids);
334
+ const title = chat.kind === "direct" ? await deps.mail.displayName(event.orgId, message.orgUserId) : await groupTitle(deps, event.orgId, chat.participants);
335
+ return address(deps, event, event.orgId, members, {
336
+ senderName: await deps.mail.displayName(event.orgId, message.orgUserId),
337
+ chatTitle: title,
338
+ excerpt: excerpt(message.body, 140),
339
+ chatId: message.chatId,
340
+ communityId: message.communityId
341
+ });
342
+ }
343
+ },
344
+ addedToGroupChat: {
345
+ triggers: ["discussion.chat.participant.added"],
346
+ derive: async (event, deps) => {
347
+ const parsed = discussionEvents.chatParticipantAdded.safeParse(event);
348
+ if (!parsed.success) return;
349
+ const added = parsed.data.data;
350
+ const chat = await deps.mail.chat(event.orgId, added.chatId);
351
+ if (!chat) return;
352
+ return address(deps, event, event.orgId, [added.orgUserId], {
353
+ adderName: await deps.mail.displayName(event.orgId, added.addedBy),
354
+ participantNames: await participantNames(deps, event.orgId, chat.participants),
355
+ chatId: added.chatId,
356
+ communityId: added.communityId
357
+ });
358
+ }
359
+ },
360
+ youAreNowAModerator: {
361
+ triggers: ["organization.user.updated"],
362
+ derive: async (event, deps) => {
363
+ const parsed = organizationEvents.orgUserUpdated.safeParse(event);
364
+ if (!parsed.success) return;
365
+ const orgUser = parsed.data.data;
366
+ if (orgUser.role !== "moderator") return [];
367
+ const communities = await deps.mail.memberCommunityTitles(event.orgId, orgUser.id);
368
+ return address(deps, event, event.orgId, [orgUser.id], { communityTitles: communities.map((community) => community.title) });
369
+ }
370
+ },
371
+ digest: silence(["discussion.digest.due"]),
372
+ somethingWasFlagged: {
373
+ triggers: ["discussion.post.reported", "discussion.comment.reported"],
374
+ derive: async (event, deps) => {
375
+ if (event.type === "discussion.post.reported") {
376
+ const parsed = discussionEvents.postReported.safeParse(event);
377
+ if (!parsed.success) return;
378
+ const flag = parsed.data.data;
379
+ const post = await deps.mail.post(event.orgId, flag.postId);
380
+ const space = post ? await deps.mail.space(event.orgId, post.spaceId) : null;
381
+ if (!post || !space) return;
382
+ const audience = await deps.mail.moderationAudience(event.orgId, flag.communityId);
383
+ return address(deps, event, event.orgId, audience, {
384
+ spaceName: space.name,
385
+ excerpt: excerpt(post.body || post.title, 280),
386
+ reason: flag.reason,
387
+ spaceId: post.spaceId,
388
+ communityId: flag.communityId
389
+ });
390
+ }
391
+ const parsed = discussionEvents.commentReported.safeParse(event);
392
+ if (!parsed.success) return;
393
+ const flag = parsed.data.data;
394
+ if (!flag.communityId) return [];
395
+ const comment = await deps.mail.comment(event.orgId, flag.commentId);
396
+ if (!comment?.postId) return comment ? [] : void 0;
397
+ const post = await deps.mail.post(event.orgId, comment.postId);
398
+ const space = post ? await deps.mail.space(event.orgId, post.spaceId) : null;
399
+ if (!post || !space) return;
400
+ const audience = await deps.mail.moderationAudience(event.orgId, flag.communityId);
401
+ return address(deps, event, event.orgId, audience, {
402
+ spaceName: space.name,
403
+ excerpt: excerpt(comment.body, 280),
404
+ reason: flag.reason,
405
+ spaceId: post.spaceId,
406
+ communityId: flag.communityId
407
+ });
408
+ }
409
+ },
410
+ messageWasFlagged: {
411
+ triggers: ["discussion.message.reported"],
412
+ derive: async (event, deps) => {
413
+ const parsed = discussionEvents.messageReported.safeParse(event);
414
+ if (!parsed.success) return;
415
+ const flag = parsed.data.data;
416
+ const audience = await deps.mail.staffIds(event.orgId);
417
+ return address(deps, event, event.orgId, audience, {
418
+ reason: flag.reason,
419
+ communityId: flag.communityId
420
+ });
421
+ }
422
+ }
423
+ };
424
+ async function participantNames(deps, orgId, participants) {
425
+ const names = [];
426
+ for (const participant of participants) {
427
+ if (participant.leftAt) continue;
428
+ names.push(await deps.mail.displayName(orgId, participant.orgUserId));
429
+ }
430
+ return names;
431
+ }
432
+ async function groupTitle(deps, orgId, participants) {
433
+ const names = await participantNames(deps, orgId, participants);
434
+ return names.length > 0 ? names.join(", ") : "Group chat";
435
+ }
436
+ //#endregion
437
+ export { COMMUNITY_DERIVATIONS };
438
+
439
+ //# sourceMappingURL=community-derivations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"community-derivations.js","names":[],"sources":["../../src/automations/community-derivations.ts"],"sourcesContent":["import { discussionEvents } from '../discussion/index.js';\nimport { entitlementEvents } from '../entitlements/index.js';\nimport { organizationEvents } from '../organizations/index.js';\nimport type { EmailTemplateId } from '../types/index.js';\nimport type { DomainEvent } from '../shared/ports.js';\nimport {\n actorOf,\n address,\n excerpt,\n MESSAGE_BATCH_MS,\n MESSAGE_EXCERPT,\n POST_EXCERPT,\n previousStatusOf,\n type ActionDeps,\n type EmailRecipient,\n} from './community-mail.js';\n\nexport interface EmailDerivation<K extends EmailTemplateId> {\n triggers: readonly string[];\n derive(event: DomainEvent, deps: ActionDeps): Promise<EmailRecipient<K>[] | undefined>;\n}\n\nfunction silence<K extends EmailTemplateId>(triggers: readonly string[]): EmailDerivation<K> {\n return { triggers, derive: async () => [] };\n}\n\nfunction seatsLeft(capacity: number | null, active: number): string {\n if (capacity == null) {\n return 'No cap';\n }\n return String(Math.max(capacity - active, 0));\n}\n\nfunction muted(participant: { muted: boolean; mutedUntil: Date | null }, at: Date): boolean {\n return (\n participant.muted &&\n (participant.mutedUntil === null || participant.mutedUntil.getTime() > at.getTime())\n );\n}\n\nfunction chatStatusOf(event: DomainEvent): string | undefined {\n const value = event.metadata?.chatStatus;\n return typeof value === 'string' ? value : undefined;\n}\n\nasync function communityTitle(deps: ActionDeps, orgId: string, communityId: string) {\n return deps.mail.communityTitle(orgId, communityId);\n}\n\nexport const COMMUNITY_DERIVATIONS: {\n [K in EmailTemplateId]?: EmailDerivation<K>;\n} = {\n welcomeToCommunity: {\n triggers: ['entitlement.created'],\n derive: async (event, deps) => {\n const parsed = entitlementEvents.entitlementCreated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const grant = parsed.data.data;\n const communityIds = await deps.mail.communitiesOpenedBy(event.orgId, {\n contentId: grant.contentId,\n bundleId: grant.bundleId,\n });\n const recipients: EmailRecipient<'welcomeToCommunity'>[] = [];\n for (const communityId of communityIds) {\n const title = await communityTitle(deps, event.orgId, communityId);\n if (!title) {\n return undefined;\n }\n const spaceNames = await deps.mail.openSpaceNames(\n event.orgId,\n grant.orgUserId,\n communityId,\n );\n const addressed = await address<'welcomeToCommunity'>(\n deps,\n event,\n event.orgId,\n [grant.orgUserId],\n { communityTitle: title, communityId, spaceNames },\n );\n if (!addressed) {\n return undefined;\n }\n recipients.push(...addressed);\n }\n return recipients;\n },\n },\n membershipEndingSoon: {\n triggers: ['entitlement.ending_soon'],\n derive: async (event, deps) => {\n const parsed = entitlementEvents.entitlementEndingSoon.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const grant = parsed.data.data;\n const communityId = grant.communityId;\n const title = await communityTitle(deps, event.orgId, communityId);\n if (!title) {\n return undefined;\n }\n return address(deps, event, event.orgId, [grant.orgUserId], {\n communityTitle: title,\n communityId,\n expiresAt: grant.expiresAt ? grant.expiresAt.toISOString() : '',\n });\n },\n },\n membershipEnded: {\n triggers: ['entitlement.expired', 'entitlement.deleted'],\n derive: async (event, deps) => {\n const parsed =\n event.type === 'entitlement.expired'\n ? entitlementEvents.entitlementExpired.safeParse(event)\n : entitlementEvents.entitlementDeleted.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const grant = parsed.data.data;\n const communityIds = await deps.mail.communitiesOpenedBy(event.orgId, {\n contentId: grant.contentId,\n bundleId: grant.bundleId,\n });\n const recipients: EmailRecipient<'membershipEnded'>[] = [];\n for (const communityId of communityIds) {\n const title = await communityTitle(deps, event.orgId, communityId);\n if (!title) {\n return undefined;\n }\n const addressed = await address<'membershipEnded'>(\n deps,\n event,\n event.orgId,\n [grant.orgUserId],\n { communities: [{ communityId, communityTitle: title }] },\n );\n if (!addressed) {\n return undefined;\n }\n recipients.push(...addressed);\n }\n return recipients;\n },\n },\n newPostInSpace: {\n triggers: ['discussion.post.published'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.postPublished.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const post = parsed.data.data;\n const space = await deps.mail.space(event.orgId, post.spaceId);\n if (!space) {\n return undefined;\n }\n const openers = await deps.mail.spaceOpeners(event.orgId, post.spaceId);\n return address(\n deps,\n event,\n event.orgId,\n openers.filter((id) => id !== post.orgUserId),\n {\n spaceName: space.name,\n postTitle: post.title,\n authorName: await deps.mail.displayName(event.orgId, post.orgUserId),\n excerpt: excerpt(post.body || post.title, POST_EXCERPT),\n postId: post.id,\n communityId: post.communityId,\n },\n );\n },\n },\n yourPostWasPublished: {\n triggers: ['discussion.post.published'],\n derive: async (event, deps) => {\n if (previousStatusOf(event) !== 'pending') {\n return [];\n }\n const parsed = discussionEvents.postPublished.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const post = parsed.data.data;\n const space = await deps.mail.space(event.orgId, post.spaceId);\n if (!space) {\n return undefined;\n }\n return address(deps, event, event.orgId, [post.orgUserId], {\n postTitle: post.title,\n spaceName: space.name,\n postId: post.id,\n communityId: post.communityId,\n });\n },\n },\n yourPostWasRemoved: {\n triggers: ['discussion.post.removed'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.postRemoved.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const post = parsed.data.data;\n if (post.removedBy === post.orgUserId) {\n return [];\n }\n const space = await deps.mail.space(event.orgId, post.spaceId);\n if (!space) {\n return undefined;\n }\n return address(deps, event, event.orgId, [post.orgUserId], {\n postTitle: post.title,\n spaceName: space.name,\n spaceId: post.spaceId,\n communityId: post.communityId,\n });\n },\n },\n newCommentOnPost: {\n triggers: ['discussion.comment.created'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.commentCreated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const comment = parsed.data.data;\n if (!comment.postId || comment.status !== 'published' || !comment.communityId) {\n return [];\n }\n const post = await deps.mail.post(event.orgId, comment.postId);\n if (!post) {\n return undefined;\n }\n const followers = await deps.mail.followerIds(event.orgId, comment.postId);\n const ids = [...new Set([...followers, post.orgUserId])].filter(\n (id) => id !== comment.orgUserId,\n );\n const openers = await deps.mail.spaceOpenersAmong(event.orgId, post.spaceId, ids);\n return address(deps, event, event.orgId, openers, {\n postTitle: post.title,\n commenterName: await deps.mail.displayName(event.orgId, comment.orgUserId),\n excerpt: excerpt(comment.body, POST_EXCERPT),\n commentId: comment.id,\n postId: comment.postId,\n communityId: comment.communityId,\n });\n },\n },\n replyToYourComment: {\n triggers: ['discussion.comment.created'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.commentCreated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const comment = parsed.data.data;\n if (!comment.parentId || !comment.postId || comment.status !== 'published') {\n return [];\n }\n const parent = await deps.mail.comment(event.orgId, comment.parentId);\n const post = await deps.mail.post(event.orgId, comment.postId);\n if (!parent || !post || !comment.communityId) {\n return parent && post ? [] : undefined;\n }\n if (parent.orgUserId === comment.orgUserId) {\n return [];\n }\n const openers = await deps.mail.spaceOpenersAmong(event.orgId, post.spaceId, [\n parent.orgUserId,\n ]);\n return address(deps, event, event.orgId, openers, {\n postTitle: post.title,\n commenterName: await deps.mail.displayName(event.orgId, comment.orgUserId),\n excerpt: excerpt(comment.body, POST_EXCERPT),\n commentId: comment.id,\n postId: comment.postId,\n communityId: comment.communityId,\n });\n },\n },\n yourCommentWasRemoved: {\n triggers: ['discussion.comment.removed'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.commentRemoved.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const comment = parsed.data.data;\n if (!comment.postId || !comment.communityId) {\n return [];\n }\n if (comment.removedBy === comment.orgUserId) {\n return [];\n }\n const post = await deps.mail.post(event.orgId, comment.postId);\n if (!post) {\n return undefined;\n }\n return address(deps, event, event.orgId, [comment.orgUserId], {\n postTitle: post.title,\n postId: comment.postId,\n communityId: comment.communityId,\n });\n },\n },\n postAwaitingReview: {\n triggers: ['discussion.post.created', 'discussion.post.updated'],\n derive: async (event, deps) => {\n const parsed =\n event.type === 'discussion.post.created'\n ? discussionEvents.postCreated.safeParse(event)\n : discussionEvents.postUpdated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const post = parsed.data.data;\n if (post.status !== 'pending' || previousStatusOf(event) === 'pending') {\n return [];\n }\n const space = await deps.mail.space(event.orgId, post.spaceId);\n if (!space) {\n return undefined;\n }\n const audience = await deps.mail.moderationAudience(event.orgId, post.communityId);\n return address(deps, event, event.orgId, audience, {\n spaceName: space.name,\n postTitle: post.title,\n authorName: await deps.mail.displayName(event.orgId, post.orgUserId),\n spaceId: post.spaceId,\n communityId: post.communityId,\n });\n },\n },\n joinRequest: {\n triggers: ['entitlement.space_member.created'],\n derive: async (event, deps) => {\n const parsed = entitlementEvents.spaceMemberCreated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const member = parsed.data.data;\n if (member.status !== 'requested') {\n return [];\n }\n const space = await deps.mail.space(event.orgId, member.spaceId);\n if (!space) {\n return undefined;\n }\n const active = await deps.mail.activeMemberCount(event.orgId, member.spaceId);\n const audience = await deps.mail.moderationAudience(event.orgId, member.communityId);\n return address(deps, event, event.orgId, audience, {\n spaceName: space.name,\n memberName: await deps.mail.displayName(event.orgId, member.orgUserId),\n seatsLeft: seatsLeft(space.capacity, active),\n spaceId: member.spaceId,\n communityId: member.communityId,\n });\n },\n },\n requestApproved: {\n triggers: ['entitlement.space_member.updated'],\n derive: async (event, deps) => {\n const parsed = entitlementEvents.spaceMemberUpdated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const member = parsed.data.data;\n if (member.status !== 'active' || previousStatusOf(event) !== 'requested') {\n return [];\n }\n const space = await deps.mail.space(event.orgId, member.spaceId);\n if (!space) {\n return undefined;\n }\n return address(deps, event, event.orgId, [member.orgUserId], {\n spaceName: space.name,\n spaceId: member.spaceId,\n communityId: member.communityId,\n });\n },\n },\n requestDeclined: {\n triggers: ['entitlement.space_member.deleted'],\n derive: async (event, deps) => {\n if (previousStatusOf(event) !== 'requested') {\n return [];\n }\n const parsed = entitlementEvents.spaceMemberDeleted.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const member = parsed.data.data;\n const space = await deps.mail.space(event.orgId, member.spaceId);\n if (!space) {\n return undefined;\n }\n return address(deps, event, event.orgId, [member.orgUserId], {\n spaceName: space.name,\n communityId: member.communityId,\n });\n },\n },\n invitedToSpace: {\n triggers: ['entitlement.space_member.created'],\n derive: async (event, deps) => {\n const parsed = entitlementEvents.spaceMemberCreated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const member = parsed.data.data;\n if (member.status !== 'invited') {\n return [];\n }\n const space = await deps.mail.space(event.orgId, member.spaceId);\n if (!space) {\n return undefined;\n }\n const actor = actorOf(event);\n return address(deps, event, event.orgId, [member.orgUserId], {\n spaceName: space.name,\n inviterName: actor ? await deps.mail.displayName(event.orgId, actor) : 'A member',\n spaceId: member.spaceId,\n communityId: member.communityId,\n });\n },\n },\n messageRequest: {\n triggers: ['discussion.chat.created'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.chatCreated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const chat = parsed.data.data;\n if (chat.kind !== 'direct' || chat.status !== 'pending') {\n return [];\n }\n const recipient = chat.participantIds.find((id) => id !== chat.createdBy);\n if (!recipient) {\n return [];\n }\n const members = await deps.mail.communityMembersAmong(event.orgId, chat.communityId, [\n recipient,\n ]);\n const body = await deps.mail.latestMessageBody(event.orgId, chat.id);\n return address(deps, event, event.orgId, members, {\n senderName: chat.createdBy\n ? await deps.mail.displayName(event.orgId, chat.createdBy)\n : 'A member',\n excerpt: excerpt(body ?? '', MESSAGE_EXCERPT),\n chatId: chat.id,\n communityId: chat.communityId,\n });\n },\n },\n newMessage: {\n triggers: ['discussion.message.sent'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.messageSent.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const message = parsed.data.data;\n const chat = await deps.mail.chat(event.orgId, message.chatId);\n if (!chat) {\n return undefined;\n }\n if (chatStatusOf(event) === 'pending') {\n return [];\n }\n const at = new Date(event.occurredAt);\n const since = new Date(at.getTime() - MESSAGE_BATCH_MS);\n const recent = new Set(\n await deps.mail.recentMessageRecipients(\n event.orgId,\n deps.automationId,\n message.chatId,\n since,\n ),\n );\n const ids: string[] = [];\n for (const participant of chat.participants) {\n if (participant.leftAt || participant.orgUserId === message.orgUserId) {\n continue;\n }\n if (muted(participant, at) || recent.has(participant.orgUserId)) {\n continue;\n }\n if (\n await deps.mail.pairBlocked(\n event.orgId,\n chat.communityId,\n message.orgUserId,\n participant.orgUserId,\n )\n ) {\n continue;\n }\n ids.push(participant.orgUserId);\n }\n const members = await deps.mail.communityMembersAmong(event.orgId, chat.communityId, ids);\n const title =\n chat.kind === 'direct'\n ? await deps.mail.displayName(event.orgId, message.orgUserId)\n : await groupTitle(deps, event.orgId, chat.participants);\n return address(deps, event, event.orgId, members, {\n senderName: await deps.mail.displayName(event.orgId, message.orgUserId),\n chatTitle: title,\n excerpt: excerpt(message.body, MESSAGE_EXCERPT),\n chatId: message.chatId,\n communityId: message.communityId,\n });\n },\n },\n addedToGroupChat: {\n triggers: ['discussion.chat.participant.added'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.chatParticipantAdded.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const added = parsed.data.data;\n const chat = await deps.mail.chat(event.orgId, added.chatId);\n if (!chat) {\n return undefined;\n }\n return address(deps, event, event.orgId, [added.orgUserId], {\n adderName: await deps.mail.displayName(event.orgId, added.addedBy),\n participantNames: await participantNames(deps, event.orgId, chat.participants),\n chatId: added.chatId,\n communityId: added.communityId,\n });\n },\n },\n youAreNowAModerator: {\n triggers: ['organization.user.updated'],\n derive: async (event, deps) => {\n const parsed = organizationEvents.orgUserUpdated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const orgUser = parsed.data.data;\n if (orgUser.role !== 'moderator') {\n return [];\n }\n const communities = await deps.mail.memberCommunityTitles(event.orgId, orgUser.id);\n return address(deps, event, event.orgId, [orgUser.id], {\n communityTitles: communities.map((community) => community.title),\n });\n },\n },\n digest: silence(['discussion.digest.due']),\n somethingWasFlagged: {\n triggers: ['discussion.post.reported', 'discussion.comment.reported'],\n derive: async (event, deps) => {\n if (event.type === 'discussion.post.reported') {\n const parsed = discussionEvents.postReported.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const flag = parsed.data.data;\n const post = await deps.mail.post(event.orgId, flag.postId);\n const space = post ? await deps.mail.space(event.orgId, post.spaceId) : null;\n if (!post || !space) {\n return undefined;\n }\n const audience = await deps.mail.moderationAudience(event.orgId, flag.communityId);\n return address(deps, event, event.orgId, audience, {\n spaceName: space.name,\n excerpt: excerpt(post.body || post.title, POST_EXCERPT),\n reason: flag.reason,\n spaceId: post.spaceId,\n communityId: flag.communityId,\n });\n }\n const parsed = discussionEvents.commentReported.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const flag = parsed.data.data;\n if (!flag.communityId) {\n return [];\n }\n const comment = await deps.mail.comment(event.orgId, flag.commentId);\n if (!comment?.postId) {\n return comment ? [] : undefined;\n }\n const post = await deps.mail.post(event.orgId, comment.postId);\n const space = post ? await deps.mail.space(event.orgId, post.spaceId) : null;\n if (!post || !space) {\n return undefined;\n }\n const audience = await deps.mail.moderationAudience(event.orgId, flag.communityId);\n return address(deps, event, event.orgId, audience, {\n spaceName: space.name,\n excerpt: excerpt(comment.body, POST_EXCERPT),\n reason: flag.reason,\n spaceId: post.spaceId,\n communityId: flag.communityId,\n });\n },\n },\n messageWasFlagged: {\n triggers: ['discussion.message.reported'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.messageReported.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const flag = parsed.data.data;\n const audience = await deps.mail.staffIds(event.orgId);\n return address(deps, event, event.orgId, audience, {\n reason: flag.reason,\n communityId: flag.communityId,\n });\n },\n },\n};\n\nasync function participantNames(\n deps: ActionDeps,\n orgId: string,\n participants: { orgUserId: string; leftAt: Date | null }[],\n): Promise<string[]> {\n const names: string[] = [];\n for (const participant of participants) {\n if (participant.leftAt) {\n continue;\n }\n names.push(await deps.mail.displayName(orgId, participant.orgUserId));\n }\n return names;\n}\n\nasync function groupTitle(\n deps: ActionDeps,\n orgId: string,\n participants: { orgUserId: string; leftAt: Date | null }[],\n): Promise<string> {\n const names = await participantNames(deps, orgId, participants);\n return names.length > 0 ? names.join(', ') : 'Group chat';\n}\n"],"mappings":"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"}
@@ -0,0 +1,98 @@
1
+ import { DomainEvent, JsonValue } from "../types/shared.js";
2
+ import { Logger } from "../types/ports.js";
3
+ import { EmailTemplateId, EmailTemplateParams } from "../types/email-templates.js";
4
+ import { GrantEntitlementInput } from "../types/schemas/entitlements.js";
5
+ import { WebhookSender } from "../types/automations.js";
6
+ import { ChatStatus } from "../types/schemas/discussion.js";
7
+ import "../types/index.js";
8
+ import { CredentialStore } from "../shared/ports.js";
9
+ import { Mailer, MailerLookups } from "../shared/mailer.js";
10
+ import "../entitlements/index.js";
11
+ import "./ports.js";
12
+ //#region src/automations/community-mail.d.ts
13
+ declare const MESSAGE_EXCERPT = 140;
14
+ declare const POST_EXCERPT = 280;
15
+ declare const MESSAGE_BATCH_MS: number;
16
+ interface EmailRecipient<K extends EmailTemplateId = EmailTemplateId> {
17
+ to: string;
18
+ orgUserId: string;
19
+ userId: string;
20
+ params: EmailTemplateParams[K];
21
+ }
22
+ interface CommunityMailReads {
23
+ communityTitle(orgId: string, communityId: string): Promise<string | null>;
24
+ space(orgId: string, spaceId: string): Promise<{
25
+ name: string;
26
+ communityId: string;
27
+ capacity: number | null;
28
+ } | null>;
29
+ openSpaceNames(orgId: string, orgUserId: string, communityId: string): Promise<string[]>;
30
+ communitiesOpenedBy(orgId: string, grant: {
31
+ contentId: string | null;
32
+ bundleId: string | null;
33
+ }): Promise<string[]>;
34
+ communityMembersAmong(orgId: string, communityId: string, ids: string[]): Promise<string[]>;
35
+ spaceOpenersAmong(orgId: string, spaceId: string, ids: string[]): Promise<string[]>;
36
+ spaceOpeners(orgId: string, spaceId: string): Promise<string[]>;
37
+ moderationAudience(orgId: string, communityId: string): Promise<string[]>;
38
+ staffIds(orgId: string): Promise<string[]>;
39
+ displayName(orgId: string, orgUserId: string): Promise<string>;
40
+ followerIds(orgId: string, postId: string): Promise<string[]>;
41
+ comment(orgId: string, commentId: string): Promise<{
42
+ orgUserId: string;
43
+ postId: string | null;
44
+ activityId: string | null;
45
+ parentId: string | null;
46
+ body: string;
47
+ } | null>;
48
+ post(orgId: string, postId: string): Promise<{
49
+ title: string;
50
+ body: string;
51
+ spaceId: string;
52
+ orgUserId: string;
53
+ communityId: string;
54
+ } | null>;
55
+ chat(orgId: string, chatId: string): Promise<{
56
+ communityId: string;
57
+ kind: 'direct' | 'group';
58
+ status: ChatStatus;
59
+ createdBy: string | null;
60
+ participants: {
61
+ orgUserId: string;
62
+ muted: boolean;
63
+ mutedUntil: Date | null;
64
+ leftAt: Date | null;
65
+ }[];
66
+ } | null>;
67
+ latestMessageBody(orgId: string, chatId: string): Promise<string | null>;
68
+ pairBlocked(orgId: string, communityId: string, a: string, b: string): Promise<boolean>;
69
+ activeMemberCount(orgId: string, spaceId: string): Promise<number>;
70
+ memberCommunityTitles(orgId: string, orgUserId: string): Promise<{
71
+ id: string;
72
+ title: string;
73
+ }[]>;
74
+ recentMessageRecipients(orgId: string, automationId: string, chatId: string, since: Date): Promise<string[]>;
75
+ }
76
+ interface GrantWriter {
77
+ grant(orgId: string, input: GrantEntitlementInput): Promise<unknown>;
78
+ }
79
+ interface ActionDeps {
80
+ mailer: Pick<Mailer, 'send'>;
81
+ lookups: MailerLookups;
82
+ credentials: Pick<CredentialStore, 'reveal'>;
83
+ webhooks: WebhookSender;
84
+ mail: CommunityMailReads;
85
+ grants: GrantWriter;
86
+ automationId: string;
87
+ actionInput: Record<string, JsonValue>;
88
+ logger?: Logger;
89
+ }
90
+ declare const emptyCommunityMail: CommunityMailReads;
91
+ declare function actorOf(event: DomainEvent): string | undefined;
92
+ declare function previousStatusOf(event: DomainEvent): string | undefined;
93
+ declare function excerpt(value: string, max: number): string;
94
+ declare function seededCommunityId(deps: ActionDeps): string | null;
95
+ declare function address<K extends EmailTemplateId>(deps: ActionDeps, event: DomainEvent, orgId: string, orgUserIds: string[], params: EmailTemplateParams[K]): Promise<EmailRecipient<K>[] | undefined>;
96
+ //#endregion
97
+ export { ActionDeps, CommunityMailReads, EmailRecipient, GrantWriter, MESSAGE_BATCH_MS, MESSAGE_EXCERPT, POST_EXCERPT, actorOf, address, emptyCommunityMail, excerpt, previousStatusOf, seededCommunityId };
98
+ //# sourceMappingURL=community-mail.d.ts.map