@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,70 @@
1
+ import { noopLogger } from "../shared/logger.js";
2
+ //#region src/automations/community-mail.ts
3
+ const MESSAGE_EXCERPT = 140;
4
+ const POST_EXCERPT = 280;
5
+ const MESSAGE_BATCH_MS = 900 * 1e3;
6
+ const emptyCommunityMail = {
7
+ communityTitle: async () => null,
8
+ space: async () => null,
9
+ openSpaceNames: async () => [],
10
+ communitiesOpenedBy: async () => [],
11
+ communityMembersAmong: async () => [],
12
+ spaceOpenersAmong: async () => [],
13
+ spaceOpeners: async () => [],
14
+ moderationAudience: async () => [],
15
+ staffIds: async () => [],
16
+ displayName: async () => "A member",
17
+ followerIds: async () => [],
18
+ comment: async () => null,
19
+ post: async () => null,
20
+ chat: async () => null,
21
+ latestMessageBody: async () => null,
22
+ pairBlocked: async () => false,
23
+ activeMemberCount: async () => 0,
24
+ memberCommunityTitles: async () => [],
25
+ recentMessageRecipients: async () => []
26
+ };
27
+ function actorOf(event) {
28
+ const value = event.metadata?.actorOrgUserId;
29
+ return typeof value === "string" ? value : void 0;
30
+ }
31
+ function previousStatusOf(event) {
32
+ const value = event.metadata?.previousStatus;
33
+ return typeof value === "string" ? value : void 0;
34
+ }
35
+ function excerpt(value, max) {
36
+ const flat = value.replace(/\s+/g, " ").trim();
37
+ return flat.length <= max ? flat : flat.slice(0, max);
38
+ }
39
+ function seededCommunityId(deps) {
40
+ const id = deps.actionInput["communityId"];
41
+ return typeof id === "string" ? id : null;
42
+ }
43
+ async function address(deps, event, orgId, orgUserIds, params) {
44
+ const actor = actorOf(event);
45
+ const wanted = [...new Set(orgUserIds)].filter((id) => id !== actor);
46
+ if (wanted.length === 0) return [];
47
+ const recipients = [];
48
+ const logger = deps.logger ?? noopLogger;
49
+ for (const orgUserId of wanted) {
50
+ const person = await deps.lookups.orgUserPerson(orgId, orgUserId);
51
+ if (!person) {
52
+ logger.warn("email skipped: recipient has no address", {
53
+ orgId,
54
+ orgUserId
55
+ });
56
+ continue;
57
+ }
58
+ recipients.push({
59
+ to: person.email,
60
+ orgUserId,
61
+ userId: person.userId,
62
+ params
63
+ });
64
+ }
65
+ return recipients;
66
+ }
67
+ //#endregion
68
+ export { MESSAGE_BATCH_MS, MESSAGE_EXCERPT, POST_EXCERPT, actorOf, address, emptyCommunityMail, excerpt, previousStatusOf, seededCommunityId };
69
+
70
+ //# sourceMappingURL=community-mail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"community-mail.js","names":[],"sources":["../../src/automations/community-mail.ts"],"sourcesContent":["import type { GrantEntitlementInput } from '../entitlements/index.js';\nimport type {\n ChatStatus,\n EmailTemplateId,\n EmailTemplateParams,\n JsonValue,\n} from '../types/index.js';\nimport type { Mailer, MailerLookups } from '../shared/mailer.js';\nimport type { CredentialStore, DomainEvent, Logger } from '../shared/ports.js';\nimport { noopLogger } from '../shared/logger.js';\nimport type { WebhookSender } from './ports.js';\n\nexport const MESSAGE_EXCERPT = 140;\nexport const POST_EXCERPT = 280;\nexport const MESSAGE_BATCH_MS = 15 * 60 * 1000;\n\nexport interface EmailRecipient<K extends EmailTemplateId = EmailTemplateId> {\n to: string;\n orgUserId: string;\n userId: string;\n params: EmailTemplateParams[K];\n}\n\nexport interface CommunityMailReads {\n communityTitle(orgId: string, communityId: string): Promise<string | null>;\n space(\n orgId: string,\n spaceId: string,\n ): Promise<{ name: string; communityId: string; capacity: number | null } | null>;\n openSpaceNames(orgId: string, orgUserId: string, communityId: string): Promise<string[]>;\n communitiesOpenedBy(\n orgId: string,\n grant: { contentId: string | null; bundleId: string | null },\n ): Promise<string[]>;\n communityMembersAmong(orgId: string, communityId: string, ids: string[]): Promise<string[]>;\n spaceOpenersAmong(orgId: string, spaceId: string, ids: string[]): Promise<string[]>;\n spaceOpeners(orgId: string, spaceId: string): Promise<string[]>;\n moderationAudience(orgId: string, communityId: string): Promise<string[]>;\n staffIds(orgId: string): Promise<string[]>;\n displayName(orgId: string, orgUserId: string): Promise<string>;\n followerIds(orgId: string, postId: string): Promise<string[]>;\n comment(\n orgId: string,\n commentId: string,\n ): Promise<{\n orgUserId: string;\n postId: string | null;\n activityId: string | null;\n parentId: string | null;\n body: string;\n } | null>;\n post(\n orgId: string,\n postId: string,\n ): Promise<{\n title: string;\n body: string;\n spaceId: string;\n orgUserId: string;\n communityId: string;\n } | null>;\n chat(\n orgId: string,\n chatId: string,\n ): Promise<{\n communityId: string;\n kind: 'direct' | 'group';\n status: ChatStatus;\n createdBy: string | null;\n participants: {\n orgUserId: string;\n muted: boolean;\n mutedUntil: Date | null;\n leftAt: Date | null;\n }[];\n } | null>;\n latestMessageBody(orgId: string, chatId: string): Promise<string | null>;\n pairBlocked(orgId: string, communityId: string, a: string, b: string): Promise<boolean>;\n activeMemberCount(orgId: string, spaceId: string): Promise<number>;\n memberCommunityTitles(orgId: string, orgUserId: string): Promise<{ id: string; title: string }[]>;\n recentMessageRecipients(\n orgId: string,\n automationId: string,\n chatId: string,\n since: Date,\n ): Promise<string[]>;\n}\n\nexport interface GrantWriter {\n grant(orgId: string, input: GrantEntitlementInput): Promise<unknown>;\n}\n\nexport interface ActionDeps {\n mailer: Pick<Mailer, 'send'>;\n lookups: MailerLookups;\n credentials: Pick<CredentialStore, 'reveal'>;\n webhooks: WebhookSender;\n mail: CommunityMailReads;\n grants: GrantWriter;\n automationId: string;\n actionInput: Record<string, JsonValue>;\n logger?: Logger;\n}\n\nexport const emptyCommunityMail: CommunityMailReads = {\n communityTitle: async () => null,\n space: async () => null,\n openSpaceNames: async () => [],\n communitiesOpenedBy: async () => [],\n communityMembersAmong: async () => [],\n spaceOpenersAmong: async () => [],\n spaceOpeners: async () => [],\n moderationAudience: async () => [],\n staffIds: async () => [],\n displayName: async () => 'A member',\n followerIds: async () => [],\n comment: async () => null,\n post: async () => null,\n chat: async () => null,\n latestMessageBody: async () => null,\n pairBlocked: async () => false,\n activeMemberCount: async () => 0,\n memberCommunityTitles: async () => [],\n recentMessageRecipients: async () => [],\n};\n\nexport function actorOf(event: DomainEvent): string | undefined {\n const value = event.metadata?.actorOrgUserId;\n return typeof value === 'string' ? value : undefined;\n}\n\nexport function previousStatusOf(event: DomainEvent): string | undefined {\n const value = event.metadata?.previousStatus;\n return typeof value === 'string' ? value : undefined;\n}\n\nexport function excerpt(value: string, max: number): string {\n const flat = value.replace(/\\s+/g, ' ').trim();\n return flat.length <= max ? flat : flat.slice(0, max);\n}\n\nexport function seededCommunityId(deps: ActionDeps): string | null {\n const id = deps.actionInput['communityId'];\n return typeof id === 'string' ? id : null;\n}\n\nexport async function address<K extends EmailTemplateId>(\n deps: ActionDeps,\n event: DomainEvent,\n orgId: string,\n orgUserIds: string[],\n params: EmailTemplateParams[K],\n): Promise<EmailRecipient<K>[] | undefined> {\n const actor = actorOf(event);\n const wanted = [...new Set(orgUserIds)].filter((id) => id !== actor);\n if (wanted.length === 0) {\n return [];\n }\n const recipients: EmailRecipient<K>[] = [];\n const logger = deps.logger ?? noopLogger;\n for (const orgUserId of wanted) {\n const person = await deps.lookups.orgUserPerson(orgId, orgUserId);\n if (!person) {\n logger.warn('email skipped: recipient has no address', { orgId, orgUserId });\n continue;\n }\n recipients.push({ to: person.email, orgUserId, userId: person.userId, params });\n }\n return recipients;\n}\n"],"mappings":";;AAYA,MAAa,kBAAkB;AAC/B,MAAa,eAAe;AAC5B,MAAa,mBAAmB,MAAU;AA0F1C,MAAa,qBAAyC;CACpD,gBAAgB,YAAY;CAC5B,OAAO,YAAY;CACnB,gBAAgB,YAAY,CAAC;CAC7B,qBAAqB,YAAY,CAAC;CAClC,uBAAuB,YAAY,CAAC;CACpC,mBAAmB,YAAY,CAAC;CAChC,cAAc,YAAY,CAAC;CAC3B,oBAAoB,YAAY,CAAC;CACjC,UAAU,YAAY,CAAC;CACvB,aAAa,YAAY;CACzB,aAAa,YAAY,CAAC;CAC1B,SAAS,YAAY;CACrB,MAAM,YAAY;CAClB,MAAM,YAAY;CAClB,mBAAmB,YAAY;CAC/B,aAAa,YAAY;CACzB,mBAAmB,YAAY;CAC/B,uBAAuB,YAAY,CAAC;CACpC,yBAAyB,YAAY,CAAC;AACxC;AAEA,SAAgB,QAAQ,OAAwC;CAC9D,MAAM,QAAQ,MAAM,UAAU;CAC9B,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAA;AAC7C;AAEA,SAAgB,iBAAiB,OAAwC;CACvE,MAAM,QAAQ,MAAM,UAAU;CAC9B,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAA;AAC7C;AAEA,SAAgB,QAAQ,OAAe,KAAqB;CAC1D,MAAM,OAAO,MAAM,QAAQ,QAAQ,GAAG,CAAC,CAAC,KAAK;CAC7C,OAAO,KAAK,UAAU,MAAM,OAAO,KAAK,MAAM,GAAG,GAAG;AACtD;AAEA,SAAgB,kBAAkB,MAAiC;CACjE,MAAM,KAAK,KAAK,YAAY;CAC5B,OAAO,OAAO,OAAO,WAAW,KAAK;AACvC;AAEA,eAAsB,QACpB,MACA,OACA,OACA,YACA,QAC0C;CAC1C,MAAM,QAAQ,QAAQ,KAAK;CAC3B,MAAM,SAAS,CAAC,GAAG,IAAI,IAAI,UAAU,CAAC,CAAC,CAAC,QAAQ,OAAO,OAAO,KAAK;CACnE,IAAI,OAAO,WAAW,GACpB,OAAO,CAAC;CAEV,MAAM,aAAkC,CAAC;CACzC,MAAM,SAAS,KAAK,UAAU;CAC9B,KAAK,MAAM,aAAa,QAAQ;EAC9B,MAAM,SAAS,MAAM,KAAK,QAAQ,cAAc,OAAO,SAAS;EAChE,IAAI,CAAC,QAAQ;GACX,OAAO,KAAK,2CAA2C;IAAE;IAAO;GAAU,CAAC;GAC3E;EACF;EACA,WAAW,KAAK;GAAE,IAAI,OAAO;GAAO;GAAW,QAAQ,OAAO;GAAQ;EAAO,CAAC;CAChF;CACA,OAAO;AACT"}
@@ -15,6 +15,7 @@ declare const automationEvents: {
15
15
  input: Record<string, JsonValue>;
16
16
  }[];
17
17
  enabled: boolean;
18
+ filter: Record<string, JsonValue> | null;
18
19
  createdAt: Date;
19
20
  updatedAt: Date;
20
21
  }, "automation.created", 2>, {
@@ -29,6 +30,7 @@ declare const automationEvents: {
29
30
  input: Record<string, unknown>;
30
31
  }[];
31
32
  enabled: boolean;
33
+ filter: Record<string, unknown> | null;
32
34
  createdAt: unknown;
33
35
  updatedAt: unknown;
34
36
  }, undefined>;
@@ -44,6 +46,7 @@ declare const automationEvents: {
44
46
  input: Record<string, JsonValue>;
45
47
  }[];
46
48
  enabled: boolean;
49
+ filter: Record<string, JsonValue> | null;
47
50
  createdAt: Date;
48
51
  updatedAt: Date;
49
52
  }, "automation.updated", 2>, {
@@ -58,6 +61,7 @@ declare const automationEvents: {
58
61
  input: Record<string, unknown>;
59
62
  }[];
60
63
  enabled: boolean;
64
+ filter: Record<string, unknown> | null;
61
65
  createdAt: unknown;
62
66
  updatedAt: unknown;
63
67
  }, undefined>;
@@ -73,6 +77,7 @@ declare const automationEvents: {
73
77
  input: Record<string, JsonValue>;
74
78
  }[];
75
79
  enabled: boolean;
80
+ filter: Record<string, JsonValue> | null;
76
81
  createdAt: Date;
77
82
  updatedAt: Date;
78
83
  }, "automation.deleted", 2>, {
@@ -87,6 +92,7 @@ declare const automationEvents: {
87
92
  input: Record<string, unknown>;
88
93
  }[];
89
94
  enabled: boolean;
95
+ filter: Record<string, unknown> | null;
90
96
  createdAt: unknown;
91
97
  updatedAt: unknown;
92
98
  }, undefined>;
@@ -102,6 +108,7 @@ declare const automationEvents: {
102
108
  input: Record<string, JsonValue>;
103
109
  }[];
104
110
  enabled: boolean;
111
+ filter: Record<string, JsonValue> | null;
105
112
  createdAt: Date;
106
113
  updatedAt: Date;
107
114
  }, "automation.enabled", 2>, {
@@ -116,6 +123,7 @@ declare const automationEvents: {
116
123
  input: Record<string, unknown>;
117
124
  }[];
118
125
  enabled: boolean;
126
+ filter: Record<string, unknown> | null;
119
127
  createdAt: unknown;
120
128
  updatedAt: unknown;
121
129
  }, undefined>;
@@ -131,6 +139,7 @@ declare const automationEvents: {
131
139
  input: Record<string, JsonValue>;
132
140
  }[];
133
141
  enabled: boolean;
142
+ filter: Record<string, JsonValue> | null;
134
143
  createdAt: Date;
135
144
  updatedAt: Date;
136
145
  }, "automation.disabled", 2>, {
@@ -145,6 +154,7 @@ declare const automationEvents: {
145
154
  input: Record<string, unknown>;
146
155
  }[];
147
156
  enabled: boolean;
157
+ filter: Record<string, unknown> | null;
148
158
  createdAt: unknown;
149
159
  updatedAt: unknown;
150
160
  }, undefined>;
@@ -161,6 +171,7 @@ declare const automationEvents: {
161
171
  type: string;
162
172
  status: "completed" | "failed";
163
173
  error?: string | undefined;
174
+ recipients?: string[] | undefined;
164
175
  }[];
165
176
  startedAt: Date;
166
177
  finishedAt: Date | null;
@@ -179,6 +190,7 @@ declare const automationEvents: {
179
190
  type: string;
180
191
  status: "completed" | "failed";
181
192
  error?: string | undefined;
193
+ recipients?: string[] | undefined;
182
194
  }[];
183
195
  startedAt: unknown;
184
196
  finishedAt: unknown;
@@ -198,6 +210,7 @@ declare const automationEvents: {
198
210
  type: string;
199
211
  status: "completed" | "failed";
200
212
  error?: string | undefined;
213
+ recipients?: string[] | undefined;
201
214
  }[];
202
215
  startedAt: Date;
203
216
  finishedAt: Date | null;
@@ -216,6 +229,7 @@ declare const automationEvents: {
216
229
  type: string;
217
230
  status: "completed" | "failed";
218
231
  error?: string | undefined;
232
+ recipients?: string[] | undefined;
219
233
  }[];
220
234
  startedAt: unknown;
221
235
  finishedAt: unknown;
@@ -235,6 +249,7 @@ declare const automationEvents: {
235
249
  type: string;
236
250
  status: "completed" | "failed";
237
251
  error?: string | undefined;
252
+ recipients?: string[] | undefined;
238
253
  }[];
239
254
  startedAt: Date;
240
255
  finishedAt: Date | null;
@@ -253,6 +268,7 @@ declare const automationEvents: {
253
268
  type: string;
254
269
  status: "completed" | "failed";
255
270
  error?: string | undefined;
271
+ recipients?: string[] | undefined;
256
272
  }[];
257
273
  startedAt: unknown;
258
274
  finishedAt: unknown;
@@ -264,11 +280,13 @@ declare const automationEvents: {
264
280
  type: string;
265
281
  status: "completed" | "failed";
266
282
  error?: string | undefined;
283
+ recipients?: string[] | undefined;
267
284
  }, "automation.action.failed", 1>, {
268
285
  index: number;
269
286
  type: string;
270
287
  status: "completed" | "failed";
271
288
  error?: string | undefined;
289
+ recipients?: string[] | undefined;
272
290
  }, undefined>;
273
291
  };
274
292
  type AutomationCreated = EventOf<typeof automationEvents.automationCreated>;
@@ -1,9 +1,18 @@
1
1
  import { Page } from "../types/shared.js";
2
2
  import { Automation, AutomationAction, AutomationActionResult, AutomationKind, AutomationRun, AutomationRunStatus, AutomationRunsQuery, AutomationTrigger, AutomationsQuery, AvailableAction, AvailableActions, AvailableTriggers, CreateAutomationInput, CreateWebhookInput, CreatedWebhook, UpdateAutomationInput, UpdateWebhookInput, Webhook, WebhookSecret } from "../types/schemas/automations.js";
3
- import { AutomationDispatch, AutomationEngine, AutomationExecutor, WebhookRequest, WebhookResponse, WebhookSender } from "../types/automations.js";
4
- import { AutomationKindMismatchError, InvalidTriggerError, ReservedActionError } from "./model.js";
3
+ import { AutomationDispatch, AutomationEngine, AutomationExecutor, DeliveryDispatch, WebhookRequest, WebhookResponse, WebhookSender } from "../types/automations.js";
4
+ import { ACTION_FAILED, ActionFailedError, AutomationKindMismatchError, InvalidTriggerError, ReservedActionError, actionFailureMessage } from "./model.js";
5
5
  import "./types.js";
6
- import { AutomationRunsRepository, AutomationsRepository, AutomationsService, AutomationsUnitOfWork, NewAutomation, NewAutomationRun } from "./ports.js";
6
+ import { AutomationRunsRepository, AutomationsRepository, AutomationsService, AutomationsUnitOfWork, ChatDebounce, ListedNotification, NewAutomation, NewAutomationRun, NewDelivery, NewNotification, NotificationCategories, NotificationPage, NotificationReads, NotificationsRepository, NotificationsService, RecipientRule, UnreadCounts } from "./ports.js";
7
+ import { CommunityMailReads, GrantWriter } from "./community-mail.js";
7
8
  import { AutomationActionFailed, AutomationCreated, AutomationDeleted, AutomationDisabled, AutomationEnabled, AutomationEvent, AutomationRunCompleted, AutomationRunFailed, AutomationRunStarted, AutomationUpdated, automationEvents } from "./events.js";
9
+ import { CHANNEL_NOT_CONFIGURED, NOT_ACCEPTED, NO_ADDRESS, NotificationDelivery } from "./notifications/delivery.js";
10
+ import { DEVICE_WINDOW_MS, IN_APP, MESSAGE_SENT, NotificationProcessor, PUSH } from "./notifications/processor.js";
8
11
  import { AutomationsServiceImpl } from "./service.js";
9
- export { type Automation, type AutomationAction, type AutomationActionFailed, type AutomationActionResult, type AutomationCreated, type AutomationDeleted, type AutomationDisabled, type AutomationDispatch, type AutomationEnabled, type AutomationEngine, type AutomationEvent, type AutomationExecutor, type AutomationKind, AutomationKindMismatchError, type AutomationRun, type AutomationRunCompleted, type AutomationRunFailed, type AutomationRunStarted, type AutomationRunStatus, type AutomationRunsQuery, type AutomationRunsRepository, type AutomationTrigger, type AutomationUpdated, type AutomationsQuery, type AutomationsRepository, type AutomationsService, AutomationsServiceImpl, type AutomationsUnitOfWork, type AvailableAction, type AvailableActions, type AvailableTriggers, type CreateAutomationInput, type CreateWebhookInput, type CreatedWebhook, InvalidTriggerError, type NewAutomation, type NewAutomationRun, type Page, ReservedActionError, type UpdateAutomationInput, type UpdateWebhookInput, type Webhook, type WebhookRequest, type WebhookResponse, type WebhookSecret, type WebhookSender, automationEvents };
12
+ import { cronMatches } from "./schedule.js";
13
+ import { NotificationsServiceImpl } from "./notifications/service.js";
14
+ import { CHAT_QUIET_MS, FLUSH_JOB, SchedulerChatDebounce, flushKey, notificationJobs } from "./notifications/debounce.js";
15
+ import { RECIPIENT_RULES } from "./notifications/rules/index.js";
16
+ import { NOTIFICATION_TYPES } from "./notifications/types.js";
17
+ import { DEFAULT_COMMUNITY_CATEGORIES, DEFAULT_ORG_CATEGORIES } from "./notifications/defaults.js";
18
+ export { ACTION_FAILED, ActionFailedError, type Automation, type AutomationAction, type AutomationActionFailed, type AutomationActionResult, type AutomationCreated, type AutomationDeleted, type AutomationDisabled, type AutomationDispatch, type AutomationEnabled, type AutomationEngine, type AutomationEvent, type AutomationExecutor, type AutomationKind, AutomationKindMismatchError, type AutomationRun, type AutomationRunCompleted, type AutomationRunFailed, type AutomationRunStarted, type AutomationRunStatus, type AutomationRunsQuery, type AutomationRunsRepository, type AutomationTrigger, type AutomationUpdated, type AutomationsQuery, type AutomationsRepository, type AutomationsService, AutomationsServiceImpl, type AutomationsUnitOfWork, type AvailableAction, type AvailableActions, type AvailableTriggers, CHANNEL_NOT_CONFIGURED, CHAT_QUIET_MS, type ChatDebounce, type CommunityMailReads, type CreateAutomationInput, type CreateWebhookInput, type CreatedWebhook, DEFAULT_COMMUNITY_CATEGORIES, DEFAULT_ORG_CATEGORIES, DEVICE_WINDOW_MS, type DeliveryDispatch, FLUSH_JOB, type GrantWriter, IN_APP, InvalidTriggerError, type ListedNotification, MESSAGE_SENT, NOTIFICATION_TYPES, NOT_ACCEPTED, NO_ADDRESS, type NewAutomation, type NewAutomationRun, type NewDelivery, type NewNotification, type NotificationCategories, NotificationDelivery, type NotificationPage, NotificationProcessor, type NotificationReads, type NotificationsRepository, type NotificationsService, NotificationsServiceImpl, PUSH, type Page, RECIPIENT_RULES, type RecipientRule, ReservedActionError, SchedulerChatDebounce, type UnreadCounts, type UpdateAutomationInput, type UpdateWebhookInput, type Webhook, type WebhookRequest, type WebhookResponse, type WebhookSecret, type WebhookSender, actionFailureMessage, automationEvents, cronMatches, flushKey, notificationJobs };
@@ -1,4 +1,12 @@
1
+ import { ACTION_FAILED, ActionFailedError, AutomationKindMismatchError, InvalidTriggerError, ReservedActionError, actionFailureMessage } from "./model.js";
1
2
  import { automationEvents } from "./events.js";
2
- import { AutomationKindMismatchError, InvalidTriggerError, ReservedActionError } from "./model.js";
3
+ import { cronMatches } from "./schedule.js";
3
4
  import { AutomationsServiceImpl } from "./service.js";
4
- export { AutomationKindMismatchError, AutomationsServiceImpl, InvalidTriggerError, ReservedActionError, automationEvents };
5
+ import { NotificationsServiceImpl } from "./notifications/service.js";
6
+ import { CHANNEL_NOT_CONFIGURED, NOT_ACCEPTED, NO_ADDRESS, NotificationDelivery } from "./notifications/delivery.js";
7
+ import { DEVICE_WINDOW_MS, IN_APP, MESSAGE_SENT, NotificationProcessor, PUSH } from "./notifications/processor.js";
8
+ import { CHAT_QUIET_MS, FLUSH_JOB, SchedulerChatDebounce, flushKey, notificationJobs } from "./notifications/debounce.js";
9
+ import { RECIPIENT_RULES } from "./notifications/rules/index.js";
10
+ import { NOTIFICATION_TYPES } from "./notifications/types.js";
11
+ import { DEFAULT_COMMUNITY_CATEGORIES, DEFAULT_ORG_CATEGORIES } from "./notifications/defaults.js";
12
+ export { ACTION_FAILED, ActionFailedError, AutomationKindMismatchError, AutomationsServiceImpl, CHANNEL_NOT_CONFIGURED, CHAT_QUIET_MS, DEFAULT_COMMUNITY_CATEGORIES, DEFAULT_ORG_CATEGORIES, DEVICE_WINDOW_MS, FLUSH_JOB, IN_APP, InvalidTriggerError, MESSAGE_SENT, NOTIFICATION_TYPES, NOT_ACCEPTED, NO_ADDRESS, NotificationDelivery, NotificationProcessor, NotificationsServiceImpl, PUSH, RECIPIENT_RULES, ReservedActionError, SchedulerChatDebounce, actionFailureMessage, automationEvents, cronMatches, flushKey, notificationJobs };
@@ -5,9 +5,16 @@ import "../types/index.js";
5
5
  /** Rejected at authoring time: automation.* is reserved to avoid a self-triggering loop. */
6
6
  declare class InvalidTriggerError extends Error {
7
7
  readonly trigger: string;
8
- readonly reason: 'reserved' | 'unknown';
9
- constructor(trigger: string, reason?: 'reserved' | 'unknown');
8
+ readonly reason: 'reserved' | 'unknown' | 'schedule';
9
+ constructor(trigger: string, reason?: 'reserved' | 'unknown' | 'schedule');
10
10
  }
11
+ declare const ACTION_FAILED = "The action failed";
12
+ /** An action failure whose message is safe to record on the run. */
13
+ declare class ActionFailedError extends Error {
14
+ constructor(message?: string);
15
+ }
16
+ /** The message a run records for an action that threw `err`. */
17
+ declare function actionFailureMessage(err: unknown): string;
11
18
  declare class ReservedActionError extends Error {
12
19
  readonly actionType: string;
13
20
  constructor(actionType: string);
@@ -18,5 +25,5 @@ declare class AutomationKindMismatchError extends Error {
18
25
  constructor(automationId: string, kind: AutomationKind);
19
26
  }
20
27
  //#endregion
21
- export { type Automation, type AutomationAction, type AutomationActionResult, type AutomationKind, AutomationKindMismatchError, type AutomationRun, type AutomationRunStatus, type AutomationTrigger, type CreatedWebhook, type DeliverWebhookInput, InvalidTriggerError, type Page, ReservedActionError, type Webhook, type WebhookSecret };
28
+ export { ACTION_FAILED, ActionFailedError, type Automation, type AutomationAction, type AutomationActionResult, type AutomationKind, AutomationKindMismatchError, type AutomationRun, type AutomationRunStatus, type AutomationTrigger, type CreatedWebhook, type DeliverWebhookInput, InvalidTriggerError, type Page, ReservedActionError, type Webhook, type WebhookSecret, actionFailureMessage };
22
29
  //# sourceMappingURL=model.d.ts.map
@@ -4,12 +4,25 @@ var InvalidTriggerError = class extends Error {
4
4
  trigger;
5
5
  reason;
6
6
  constructor(trigger, reason = "reserved") {
7
- super(reason === "reserved" ? `trigger "${trigger}" is reserved: automations cannot trigger on automation.* events` : `trigger "${trigger}" is not an event the system emits`);
7
+ const message = reason === "reserved" ? `trigger "${trigger}" is reserved: automations cannot trigger on automation.* events` : reason === "unknown" ? `trigger "${trigger}" is not an event the system emits` : `trigger "${trigger}" uses a schedule this system cannot match`;
8
+ super(message);
8
9
  this.trigger = trigger;
9
10
  this.reason = reason;
10
11
  this.name = "InvalidTriggerError";
11
12
  }
12
13
  };
14
+ const ACTION_FAILED = "The action failed";
15
+ /** An action failure whose message is safe to record on the run. */
16
+ var ActionFailedError = class extends Error {
17
+ constructor(message = ACTION_FAILED) {
18
+ super(message);
19
+ this.name = "ActionFailedError";
20
+ }
21
+ };
22
+ /** The message a run records for an action that threw `err`. */
23
+ function actionFailureMessage(err) {
24
+ return err instanceof ActionFailedError ? err.message : ACTION_FAILED;
25
+ }
13
26
  var ReservedActionError = class extends Error {
14
27
  actionType;
15
28
  constructor(actionType) {
@@ -29,6 +42,6 @@ var AutomationKindMismatchError = class extends Error {
29
42
  }
30
43
  };
31
44
  //#endregion
32
- export { AutomationKindMismatchError, InvalidTriggerError, ReservedActionError };
45
+ export { ACTION_FAILED, ActionFailedError, AutomationKindMismatchError, InvalidTriggerError, ReservedActionError, actionFailureMessage };
33
46
 
34
47
  //# sourceMappingURL=model.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"model.js","names":[],"sources":["../../src/automations/model.ts"],"sourcesContent":["// automations context — domain entities, owned by @headless-lms/core/types; the\n// domain errors below are runtime code and stay in core.\nimport type { AutomationKind } from '../types/index.js';\n\nexport type {\n AutomationTrigger,\n AutomationAction,\n AutomationKind,\n Automation,\n AutomationRunStatus,\n AutomationActionResult,\n AutomationRun,\n Webhook,\n CreatedWebhook,\n WebhookSecret,\n DeliverWebhookInput,\n Page,\n} from '../types/index.js';\n\n/** Rejected at authoring time: automation.* is reserved to avoid a self-triggering loop. */\nexport class InvalidTriggerError extends Error {\n constructor(\n readonly trigger: string,\n readonly reason: 'reserved' | 'unknown' = 'reserved',\n ) {\n super(\n reason === 'reserved'\n ? `trigger \"${trigger}\" is reserved: automations cannot trigger on automation.* events`\n : `trigger \"${trigger}\" is not an event the system emits`,\n );\n this.name = 'InvalidTriggerError';\n }\n}\n\nexport class ReservedActionError extends Error {\n constructor(readonly actionType: string) {\n super(`action \"${actionType}\" is reserved to webhooks`);\n this.name = 'ReservedActionError';\n }\n}\n\nexport class AutomationKindMismatchError extends Error {\n constructor(\n readonly automationId: string,\n readonly kind: AutomationKind,\n ) {\n super(`automation \"${automationId}\" is a ${kind} and can't be changed through this operation`);\n this.name = 'AutomationKindMismatchError';\n }\n}\n"],"mappings":";;AAoBA,IAAa,sBAAb,cAAyC,MAAM;CAElC;CACA;CAFX,YACE,SACA,SAA0C,YAC1C;EACA,MACE,WAAW,aACP,YAAY,QAAQ,oEACpB,YAAY,QAAQ,mCAC1B;EAPS,KAAA,UAAA;EACA,KAAA,SAAA;EAOT,KAAK,OAAO;CACd;AACF;AAEA,IAAa,sBAAb,cAAyC,MAAM;CACxB;CAArB,YAAY,YAA6B;EACvC,MAAM,WAAW,WAAW,0BAA0B;EADnC,KAAA,aAAA;EAEnB,KAAK,OAAO;CACd;AACF;AAEA,IAAa,8BAAb,cAAiD,MAAM;CAE1C;CACA;CAFX,YACE,cACA,MACA;EACA,MAAM,eAAe,aAAa,SAAS,KAAK,6CAA6C;EAHpF,KAAA,eAAA;EACA,KAAA,OAAA;EAGT,KAAK,OAAO;CACd;AACF"}
1
+ {"version":3,"file":"model.js","names":[],"sources":["../../src/automations/model.ts"],"sourcesContent":["// automations context — domain entities, owned by @headless-lms/core/types; the\n// domain errors below are runtime code and stay in core.\nimport type { AutomationKind } from '../types/index.js';\n\nexport type {\n AutomationTrigger,\n AutomationAction,\n AutomationKind,\n Automation,\n AutomationRunStatus,\n AutomationActionResult,\n AutomationRun,\n Webhook,\n CreatedWebhook,\n WebhookSecret,\n DeliverWebhookInput,\n Page,\n} from '../types/index.js';\n\n/** Rejected at authoring time: automation.* is reserved to avoid a self-triggering loop. */\nexport class InvalidTriggerError extends Error {\n constructor(\n readonly trigger: string,\n readonly reason: 'reserved' | 'unknown' | 'schedule' = 'reserved',\n ) {\n const message =\n reason === 'reserved'\n ? `trigger \"${trigger}\" is reserved: automations cannot trigger on automation.* events`\n : reason === 'unknown'\n ? `trigger \"${trigger}\" is not an event the system emits`\n : `trigger \"${trigger}\" uses a schedule this system cannot match`;\n super(message);\n this.name = 'InvalidTriggerError';\n }\n}\n\nexport const ACTION_FAILED = 'The action failed';\n\n/** An action failure whose message is safe to record on the run. */\nexport class ActionFailedError extends Error {\n constructor(message: string = ACTION_FAILED) {\n super(message);\n this.name = 'ActionFailedError';\n }\n}\n\n/** The message a run records for an action that threw `err`. */\nexport function actionFailureMessage(err: unknown): string {\n return err instanceof ActionFailedError ? err.message : ACTION_FAILED;\n}\n\nexport class ReservedActionError extends Error {\n constructor(readonly actionType: string) {\n super(`action \"${actionType}\" is reserved to webhooks`);\n this.name = 'ReservedActionError';\n }\n}\n\nexport class AutomationKindMismatchError extends Error {\n constructor(\n readonly automationId: string,\n readonly kind: AutomationKind,\n ) {\n super(`automation \"${automationId}\" is a ${kind} and can't be changed through this operation`);\n this.name = 'AutomationKindMismatchError';\n }\n}\n"],"mappings":";;AAoBA,IAAa,sBAAb,cAAyC,MAAM;CAElC;CACA;CAFX,YACE,SACA,SAAuD,YACvD;EACA,MAAM,UACJ,WAAW,aACP,YAAY,QAAQ,oEACpB,WAAW,YACT,YAAY,QAAQ,sCACpB,YAAY,QAAQ;EAC5B,MAAM,OAAO;EATJ,KAAA,UAAA;EACA,KAAA,SAAA;EAST,KAAK,OAAO;CACd;AACF;AAEA,MAAa,gBAAgB;;AAG7B,IAAa,oBAAb,cAAuC,MAAM;CAC3C,YAAY,UAAkB,eAAe;EAC3C,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF;;AAGA,SAAgB,qBAAqB,KAAsB;CACzD,OAAO,eAAe,oBAAoB,IAAI,UAAU;AAC1D;AAEA,IAAa,sBAAb,cAAyC,MAAM;CACxB;CAArB,YAAY,YAA6B;EACvC,MAAM,WAAW,WAAW,0BAA0B;EADnC,KAAA,aAAA;EAEnB,KAAK,OAAO;CACd;AACF;AAEA,IAAa,8BAAb,cAAiD,MAAM;CAE1C;CACA;CAFX,YACE,cACA,MACA;EACA,MAAM,eAAe,aAAa,SAAS,KAAK,6CAA6C;EAHpF,KAAA,eAAA;EACA,KAAA,OAAA;EAGT,KAAK,OAAO;CACd;AACF"}
@@ -0,0 +1,25 @@
1
+ import { Logger } from "../../types/ports.js";
2
+ import { RecipientList } from "../../types/notifications.js";
3
+ import "../../types/index.js";
4
+ import { ScheduledJob, Scheduler } from "../../shared/ports.js";
5
+ import { ChatDebounce, NotificationReads } from "../ports.js";
6
+ import { NotificationProcessor } from "./processor.js";
7
+ //#region src/automations/notifications/debounce.d.ts
8
+ declare const FLUSH_JOB = "automations.notifications.flush";
9
+ declare const CHAT_QUIET_MS: number;
10
+ declare function flushKey(userId: string, chatId: string): string;
11
+ declare class SchedulerChatDebounce implements ChatDebounce {
12
+ private readonly scheduler;
13
+ constructor(scheduler: Pick<Scheduler, 'at' | 'cancel'>);
14
+ arm(orgId: string, list: RecipientList, occurredAt: Date): Promise<void>;
15
+ mentioned(_orgId: string, userId: string, chatId: string): Promise<void>;
16
+ }
17
+ interface NotificationJobDeps {
18
+ processor: Pick<NotificationProcessor, 'file'>;
19
+ reads: NotificationReads;
20
+ logger?: Logger;
21
+ }
22
+ declare function notificationJobs(deps: NotificationJobDeps): ScheduledJob[];
23
+ //#endregion
24
+ export { CHAT_QUIET_MS, FLUSH_JOB, NotificationJobDeps, SchedulerChatDebounce, flushKey, notificationJobs };
25
+ //# sourceMappingURL=debounce.d.ts.map
@@ -0,0 +1,93 @@
1
+ import { idSchema } from "../../types/schemas/shared.js";
2
+ import { noopLogger } from "../../shared/logger.js";
3
+ import "../community-mail.js";
4
+ import { chatOf } from "./processor.js";
5
+ import { Reasons, chatReach, chatTitle, recipientsOf, textExcerpt } from "./rules/recipients.js";
6
+ import { messageSummary } from "./rules/discussion.js";
7
+ import { z } from "zod";
8
+ //#region src/automations/notifications/debounce.ts
9
+ const FLUSH_JOB = "automations.notifications.flush";
10
+ const CHAT_QUIET_MS = 120 * 1e3;
11
+ const flushPayload = z.object({
12
+ orgId: idSchema,
13
+ userId: idSchema,
14
+ orgUserId: idSchema,
15
+ chatId: idSchema
16
+ }).strict();
17
+ function flushKey(userId, chatId) {
18
+ return `${userId}:${chatId}`;
19
+ }
20
+ var SchedulerChatDebounce = class {
21
+ scheduler;
22
+ constructor(scheduler) {
23
+ this.scheduler = scheduler;
24
+ }
25
+ async arm(orgId, list, occurredAt) {
26
+ const chatId = chatOf(list);
27
+ if (!chatId) return;
28
+ const runAt = new Date(occurredAt.getTime() + CHAT_QUIET_MS);
29
+ for (const recipient of list.recipients ?? []) await this.scheduler.at(FLUSH_JOB, runAt, flushKey(recipient.userId, chatId), {
30
+ orgId,
31
+ userId: recipient.userId,
32
+ orgUserId: recipient.orgUserId,
33
+ chatId
34
+ });
35
+ }
36
+ async mentioned(_orgId, userId, chatId) {
37
+ await this.scheduler.cancel(FLUSH_JOB, flushKey(userId, chatId));
38
+ }
39
+ };
40
+ function notificationJobs(deps) {
41
+ const logger = deps.logger ?? noopLogger;
42
+ return [{
43
+ job: FLUSH_JOB,
44
+ async run(payload, fire) {
45
+ const parsed = flushPayload.safeParse(payload);
46
+ if (!parsed.success) {
47
+ logger.error("chat flush payload refused", {
48
+ job: FLUSH_JOB,
49
+ fireId: fire.id
50
+ });
51
+ return;
52
+ }
53
+ const { orgId, orgUserId, chatId } = parsed.data;
54
+ const unread = await deps.reads.unreadInChat(orgId, chatId, orgUserId);
55
+ if (unread.count === 0 || !unread.latest) return;
56
+ const chat = await deps.reads.chat(orgId, chatId);
57
+ if (!chat) return;
58
+ const latest = unread.latest;
59
+ const recipients = await recipientsOf(deps.reads, orgId, new Reasons().add([orgUserId], "participant"), chatReach(deps.reads, orgId, chat, chatId, latest.senderOrgUserId, fire.startedAt));
60
+ if (recipients.length === 0) return;
61
+ await deps.processor.file(orgId, `chat-flush:${chatId}:${latest.messageId}`, [{
62
+ type: "message.sent",
63
+ scope: "chat",
64
+ actorOrgUserId: latest.senderOrgUserId,
65
+ communityId: chat.communityId,
66
+ spaceId: null,
67
+ subject: {
68
+ type: "message",
69
+ id: latest.messageId
70
+ },
71
+ parent: {
72
+ type: "chat",
73
+ id: chatId
74
+ },
75
+ display: {
76
+ senderName: await deps.reads.displayName(orgId, latest.senderOrgUserId),
77
+ chatTitle: await chatTitle(deps.reads, orgId, chat, latest.senderOrgUserId),
78
+ excerpt: await textExcerpt(deps.reads, orgId, latest.body, 140),
79
+ count: String(unread.count),
80
+ summary: messageSummary(unread.count)
81
+ },
82
+ recipients
83
+ }], {
84
+ occurredAt: fire.startedAt,
85
+ debounce: false
86
+ });
87
+ }
88
+ }];
89
+ }
90
+ //#endregion
91
+ export { CHAT_QUIET_MS, FLUSH_JOB, SchedulerChatDebounce, flushKey, notificationJobs };
92
+
93
+ //# sourceMappingURL=debounce.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"debounce.js","names":[],"sources":["../../../src/automations/notifications/debounce.ts"],"sourcesContent":["import { z } from 'zod';\nimport { idSchema } from '../../types/schemas/index.js';\nimport type { RecipientList } from '../../types/index.js';\nimport { noopLogger } from '../../shared/logger.js';\nimport type { Logger, ScheduledJob, Scheduler } from '../../shared/ports.js';\nimport { MESSAGE_EXCERPT } from '../community-mail.js';\nimport type { ChatDebounce, NotificationReads } from '../ports.js';\nimport { chatOf, type NotificationProcessor } from './processor.js';\nimport { messageSummary } from './rules/discussion.js';\nimport { chatReach, chatTitle, Reasons, recipientsOf, textExcerpt } from './rules/recipients.js';\n\nexport const FLUSH_JOB = 'automations.notifications.flush';\nexport const CHAT_QUIET_MS = 2 * 60 * 1000;\n\nconst flushPayload = z\n .object({ orgId: idSchema, userId: idSchema, orgUserId: idSchema, chatId: idSchema })\n .strict();\n\nexport function flushKey(userId: string, chatId: string): string {\n return `${userId}:${chatId}`;\n}\n\nexport class SchedulerChatDebounce implements ChatDebounce {\n constructor(private readonly scheduler: Pick<Scheduler, 'at' | 'cancel'>) {}\n\n async arm(orgId: string, list: RecipientList, occurredAt: Date): Promise<void> {\n const chatId = chatOf(list);\n if (!chatId) {\n return;\n }\n const runAt = new Date(occurredAt.getTime() + CHAT_QUIET_MS);\n for (const recipient of list.recipients ?? []) {\n await this.scheduler.at(FLUSH_JOB, runAt, flushKey(recipient.userId, chatId), {\n orgId,\n userId: recipient.userId,\n orgUserId: recipient.orgUserId,\n chatId,\n });\n }\n }\n\n async mentioned(_orgId: string, userId: string, chatId: string): Promise<void> {\n await this.scheduler.cancel(FLUSH_JOB, flushKey(userId, chatId));\n }\n}\n\nexport interface NotificationJobDeps {\n processor: Pick<NotificationProcessor, 'file'>;\n reads: NotificationReads;\n logger?: Logger;\n}\n\nexport function notificationJobs(deps: NotificationJobDeps): ScheduledJob[] {\n const logger = deps.logger ?? noopLogger;\n return [\n {\n job: FLUSH_JOB,\n async run(payload, fire) {\n const parsed = flushPayload.safeParse(payload);\n if (!parsed.success) {\n logger.error('chat flush payload refused', { job: FLUSH_JOB, fireId: fire.id });\n return;\n }\n const { orgId, orgUserId, chatId } = parsed.data;\n const unread = await deps.reads.unreadInChat(orgId, chatId, orgUserId);\n if (unread.count === 0 || !unread.latest) {\n return;\n }\n const chat = await deps.reads.chat(orgId, chatId);\n if (!chat) {\n return;\n }\n const latest = unread.latest;\n const recipients = await recipientsOf(\n deps.reads,\n orgId,\n new Reasons().add([orgUserId], 'participant'),\n chatReach(deps.reads, orgId, chat, chatId, latest.senderOrgUserId, fire.startedAt),\n );\n if (recipients.length === 0) {\n return;\n }\n await deps.processor.file(\n orgId,\n `chat-flush:${chatId}:${latest.messageId}`,\n [\n {\n type: 'message.sent',\n scope: 'chat',\n actorOrgUserId: latest.senderOrgUserId,\n communityId: chat.communityId,\n spaceId: null,\n subject: { type: 'message', id: latest.messageId },\n parent: { type: 'chat', id: chatId },\n display: {\n senderName: await deps.reads.displayName(orgId, latest.senderOrgUserId),\n chatTitle: await chatTitle(deps.reads, orgId, chat, latest.senderOrgUserId),\n excerpt: await textExcerpt(deps.reads, orgId, latest.body, MESSAGE_EXCERPT),\n count: String(unread.count),\n summary: messageSummary(unread.count),\n },\n recipients,\n },\n ],\n { occurredAt: fire.startedAt, debounce: false },\n );\n },\n },\n ];\n}\n"],"mappings":";;;;;;;;AAWA,MAAa,YAAY;AACzB,MAAa,gBAAgB,MAAS;AAEtC,MAAM,eAAe,EAClB,OAAO;CAAE,OAAO;CAAU,QAAQ;CAAU,WAAW;CAAU,QAAQ;AAAS,CAAC,CAAC,CACpF,OAAO;AAEV,SAAgB,SAAS,QAAgB,QAAwB;CAC/D,OAAO,GAAG,OAAO,GAAG;AACtB;AAEA,IAAa,wBAAb,MAA2D;CAC5B;CAA7B,YAAY,WAA8D;EAA7C,KAAA,YAAA;CAA8C;CAE3E,MAAM,IAAI,OAAe,MAAqB,YAAiC;EAC7E,MAAM,SAAS,OAAO,IAAI;EAC1B,IAAI,CAAC,QACH;EAEF,MAAM,QAAQ,IAAI,KAAK,WAAW,QAAQ,IAAI,aAAa;EAC3D,KAAK,MAAM,aAAa,KAAK,cAAc,CAAC,GAC1C,MAAM,KAAK,UAAU,GAAG,WAAW,OAAO,SAAS,UAAU,QAAQ,MAAM,GAAG;GAC5E;GACA,QAAQ,UAAU;GAClB,WAAW,UAAU;GACrB;EACF,CAAC;CAEL;CAEA,MAAM,UAAU,QAAgB,QAAgB,QAA+B;EAC7E,MAAM,KAAK,UAAU,OAAO,WAAW,SAAS,QAAQ,MAAM,CAAC;CACjE;AACF;AAQA,SAAgB,iBAAiB,MAA2C;CAC1E,MAAM,SAAS,KAAK,UAAU;CAC9B,OAAO,CACL;EACE,KAAK;EACL,MAAM,IAAI,SAAS,MAAM;GACvB,MAAM,SAAS,aAAa,UAAU,OAAO;GAC7C,IAAI,CAAC,OAAO,SAAS;IACnB,OAAO,MAAM,8BAA8B;KAAE,KAAK;KAAW,QAAQ,KAAK;IAAG,CAAC;IAC9E;GACF;GACA,MAAM,EAAE,OAAO,WAAW,WAAW,OAAO;GAC5C,MAAM,SAAS,MAAM,KAAK,MAAM,aAAa,OAAO,QAAQ,SAAS;GACrE,IAAI,OAAO,UAAU,KAAK,CAAC,OAAO,QAChC;GAEF,MAAM,OAAO,MAAM,KAAK,MAAM,KAAK,OAAO,MAAM;GAChD,IAAI,CAAC,MACH;GAEF,MAAM,SAAS,OAAO;GACtB,MAAM,aAAa,MAAM,aACvB,KAAK,OACL,OACA,IAAI,QAAQ,CAAC,CAAC,IAAI,CAAC,SAAS,GAAG,aAAa,GAC5C,UAAU,KAAK,OAAO,OAAO,MAAM,QAAQ,OAAO,iBAAiB,KAAK,SAAS,CACnF;GACA,IAAI,WAAW,WAAW,GACxB;GAEF,MAAM,KAAK,UAAU,KACnB,OACA,cAAc,OAAO,GAAG,OAAO,aAC/B,CACE;IACE,MAAM;IACN,OAAO;IACP,gBAAgB,OAAO;IACvB,aAAa,KAAK;IAClB,SAAS;IACT,SAAS;KAAE,MAAM;KAAW,IAAI,OAAO;IAAU;IACjD,QAAQ;KAAE,MAAM;KAAQ,IAAI;IAAO;IACnC,SAAS;KACP,YAAY,MAAM,KAAK,MAAM,YAAY,OAAO,OAAO,eAAe;KACtE,WAAW,MAAM,UAAU,KAAK,OAAO,OAAO,MAAM,OAAO,eAAe;KAC1E,SAAS,MAAM,YAAY,KAAK,OAAO,OAAO,OAAO,MAAA,GAAqB;KAC1E,OAAO,OAAO,OAAO,KAAK;KAC1B,SAAS,eAAe,OAAO,KAAK;IACtC;IACA;GACF,CACF,GACA;IAAE,YAAY,KAAK;IAAW,UAAU;GAAM,CAChD;EACF;CACF,CACF;AACF"}
@@ -0,0 +1,8 @@
1
+ import { NotificationCategory } from "../../types/notifications.js";
2
+ import "../../types/index.js";
3
+ //#region src/automations/notifications/defaults.d.ts
4
+ declare const DEFAULT_COMMUNITY_CATEGORIES: NotificationCategory[];
5
+ declare const DEFAULT_ORG_CATEGORIES: NotificationCategory[];
6
+ //#endregion
7
+ export { DEFAULT_COMMUNITY_CATEGORIES, DEFAULT_ORG_CATEGORIES };
8
+ //# sourceMappingURL=defaults.d.ts.map