@headless-lms/core 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (307) hide show
  1. package/dist/assets/events.d.ts +24 -18
  2. package/dist/assets/file-urls.d.ts +11 -7
  3. package/dist/assets/file-urls.js +14 -4
  4. package/dist/assets/file-urls.js.map +1 -1
  5. package/dist/assets/index.d.ts +5 -4
  6. package/dist/assets/index.js +4 -3
  7. package/dist/assets/jobs.d.ts +15 -0
  8. package/dist/assets/jobs.js +30 -0
  9. package/dist/assets/jobs.js.map +1 -0
  10. package/dist/assets/ports.d.ts +37 -17
  11. package/dist/assets/service.d.ts +30 -15
  12. package/dist/assets/service.js +229 -59
  13. package/dist/assets/service.js.map +1 -1
  14. package/dist/automations/actions.js +2 -63
  15. package/dist/automations/actions.js.map +1 -1
  16. package/dist/automations/catalog.d.ts +1 -2
  17. package/dist/automations/catalog.js +137 -65
  18. package/dist/automations/catalog.js.map +1 -1
  19. package/dist/automations/community-derivations.js +20 -20
  20. package/dist/automations/community-derivations.js.map +1 -1
  21. package/dist/automations/community-mail.d.ts +36 -20
  22. package/dist/automations/community-mail.js +32 -24
  23. package/dist/automations/community-mail.js.map +1 -1
  24. package/dist/automations/event-permissions.d.ts +12 -0
  25. package/dist/automations/event-permissions.js +38 -0
  26. package/dist/automations/event-permissions.js.map +1 -0
  27. package/dist/automations/events.d.ts +0 -10
  28. package/dist/automations/index.d.ts +12 -4
  29. package/dist/automations/index.js +9 -1
  30. package/dist/automations/notifications/applicable.d.ts +8 -0
  31. package/dist/automations/notifications/applicable.js +12 -0
  32. package/dist/automations/notifications/applicable.js.map +1 -0
  33. package/dist/automations/notifications/debounce.d.ts +25 -0
  34. package/dist/automations/notifications/debounce.js +93 -0
  35. package/dist/automations/notifications/debounce.js.map +1 -0
  36. package/dist/automations/notifications/defaults.d.ts +8 -0
  37. package/dist/automations/notifications/defaults.js +346 -0
  38. package/dist/automations/notifications/defaults.js.map +1 -0
  39. package/dist/automations/notifications/delivery.d.ts +28 -0
  40. package/dist/automations/notifications/delivery.js +88 -0
  41. package/dist/automations/notifications/delivery.js.map +1 -0
  42. package/dist/automations/notifications/processor.d.ts +64 -0
  43. package/dist/automations/notifications/processor.js +270 -0
  44. package/dist/automations/notifications/processor.js.map +1 -0
  45. package/dist/automations/notifications/rules/content.d.ts +6 -0
  46. package/dist/automations/notifications/rules/content.js +96 -0
  47. package/dist/automations/notifications/rules/content.js.map +1 -0
  48. package/dist/automations/notifications/rules/discussion.d.ts +7 -0
  49. package/dist/automations/notifications/rules/discussion.js +678 -0
  50. package/dist/automations/notifications/rules/discussion.js.map +1 -0
  51. package/dist/automations/notifications/rules/entitlements.d.ts +6 -0
  52. package/dist/automations/notifications/rules/entitlements.js +276 -0
  53. package/dist/automations/notifications/rules/entitlements.js.map +1 -0
  54. package/dist/automations/notifications/rules/events.d.ts +9 -0
  55. package/dist/automations/notifications/rules/events.js +27 -0
  56. package/dist/automations/notifications/rules/events.js.map +1 -0
  57. package/dist/automations/notifications/rules/index.d.ts +6 -0
  58. package/dist/automations/notifications/rules/index.js +15 -0
  59. package/dist/automations/notifications/rules/index.js.map +1 -0
  60. package/dist/automations/notifications/rules/organizations.d.ts +6 -0
  61. package/dist/automations/notifications/rules/organizations.js +35 -0
  62. package/dist/automations/notifications/rules/organizations.js.map +1 -0
  63. package/dist/automations/notifications/rules/recipients.d.ts +32 -0
  64. package/dist/automations/notifications/rules/recipients.js +98 -0
  65. package/dist/automations/notifications/rules/recipients.js.map +1 -0
  66. package/dist/automations/notifications/service.d.ts +29 -0
  67. package/dist/automations/notifications/service.js +45 -0
  68. package/dist/automations/notifications/service.js.map +1 -0
  69. package/dist/automations/notifications/types.d.ts +7 -0
  70. package/dist/automations/notifications/types.js +560 -0
  71. package/dist/automations/notifications/types.js.map +1 -0
  72. package/dist/automations/ports.d.ts +134 -21
  73. package/dist/automations/service.d.ts +30 -30
  74. package/dist/automations/service.js +111 -144
  75. package/dist/automations/service.js.map +1 -1
  76. package/dist/content/event-sight.d.ts +11 -0
  77. package/dist/content/event-sight.js +11 -0
  78. package/dist/content/event-sight.js.map +1 -0
  79. package/dist/content/events.d.ts +437 -46
  80. package/dist/content/events.js +45 -1
  81. package/dist/content/events.js.map +1 -1
  82. package/dist/content/index.d.ts +8 -4
  83. package/dist/content/index.js +5 -1
  84. package/dist/content/jobs.d.ts +16 -0
  85. package/dist/content/jobs.js +37 -0
  86. package/dist/content/jobs.js.map +1 -0
  87. package/dist/content/model.d.ts +2 -2
  88. package/dist/content/notification-categories.d.ts +37 -0
  89. package/dist/content/notification-categories.js +73 -0
  90. package/dist/content/notification-categories.js.map +1 -0
  91. package/dist/content/ports.d.ts +132 -68
  92. package/dist/content/service.d.ts +100 -68
  93. package/dist/content/service.js +703 -146
  94. package/dist/content/service.js.map +1 -1
  95. package/dist/content/titles.d.ts +25 -0
  96. package/dist/content/titles.js +82 -0
  97. package/dist/content/titles.js.map +1 -0
  98. package/dist/content/types.d.ts +2 -2
  99. package/dist/discussion/authors.d.ts +4 -2
  100. package/dist/discussion/authors.js +9 -5
  101. package/dist/discussion/authors.js.map +1 -1
  102. package/dist/discussion/chats.d.ts +63 -39
  103. package/dist/discussion/chats.js +455 -190
  104. package/dist/discussion/chats.js.map +1 -1
  105. package/dist/discussion/digests.d.ts +24 -0
  106. package/dist/discussion/digests.js +104 -0
  107. package/dist/discussion/digests.js.map +1 -0
  108. package/dist/discussion/events.d.ts +227 -23
  109. package/dist/discussion/events.js +114 -19
  110. package/dist/discussion/events.js.map +1 -1
  111. package/dist/discussion/index.d.ts +12 -4
  112. package/dist/discussion/index.js +9 -2
  113. package/dist/discussion/jobs.d.ts +16 -0
  114. package/dist/discussion/jobs.js +40 -0
  115. package/dist/discussion/jobs.js.map +1 -0
  116. package/dist/discussion/live.d.ts +106 -0
  117. package/dist/discussion/live.js +642 -0
  118. package/dist/discussion/live.js.map +1 -0
  119. package/dist/discussion/mentions.d.ts +11 -0
  120. package/dist/discussion/mentions.js +35 -0
  121. package/dist/discussion/mentions.js.map +1 -0
  122. package/dist/discussion/ports.d.ts +279 -114
  123. package/dist/discussion/ports.js.map +1 -1
  124. package/dist/discussion/posts.d.ts +60 -44
  125. package/dist/discussion/posts.js +453 -287
  126. package/dist/discussion/posts.js.map +1 -1
  127. package/dist/discussion/rules.d.ts +18 -0
  128. package/dist/discussion/rules.js +22 -0
  129. package/dist/discussion/rules.js.map +1 -0
  130. package/dist/discussion/service.d.ts +50 -35
  131. package/dist/discussion/service.js +333 -205
  132. package/dist/discussion/service.js.map +1 -1
  133. package/dist/discussion/subscriptions.d.ts +37 -0
  134. package/dist/discussion/subscriptions.js +115 -0
  135. package/dist/discussion/subscriptions.js.map +1 -0
  136. package/dist/discussion/titles.d.ts +28 -0
  137. package/dist/discussion/titles.js +98 -0
  138. package/dist/discussion/titles.js.map +1 -0
  139. package/dist/entitlements/events.d.ts +20 -20
  140. package/dist/entitlements/index.d.ts +5 -4
  141. package/dist/entitlements/index.js +3 -2
  142. package/dist/entitlements/jobs.d.ts +12 -0
  143. package/dist/entitlements/jobs.js +18 -0
  144. package/dist/entitlements/jobs.js.map +1 -0
  145. package/dist/entitlements/model.d.ts +2 -2
  146. package/dist/entitlements/ports.d.ts +68 -38
  147. package/dist/entitlements/rules.d.ts +3 -6
  148. package/dist/entitlements/rules.js +5 -10
  149. package/dist/entitlements/rules.js.map +1 -1
  150. package/dist/entitlements/service.d.ts +43 -28
  151. package/dist/entitlements/service.js +387 -160
  152. package/dist/entitlements/service.js.map +1 -1
  153. package/dist/identity/devices.d.ts +30 -0
  154. package/dist/identity/devices.js +109 -0
  155. package/dist/identity/devices.js.map +1 -0
  156. package/dist/identity/index.d.ts +6 -5
  157. package/dist/identity/index.js +4 -3
  158. package/dist/identity/model.d.ts +2 -2
  159. package/dist/identity/notification-settings.d.ts +28 -0
  160. package/dist/identity/notification-settings.js +114 -0
  161. package/dist/identity/notification-settings.js.map +1 -0
  162. package/dist/identity/ports.d.ts +80 -56
  163. package/dist/identity/service.d.ts +8 -7
  164. package/dist/identity/service.js +17 -13
  165. package/dist/identity/service.js.map +1 -1
  166. package/dist/integrations/ports.d.ts +9 -8
  167. package/dist/integrations/service.d.ts +9 -8
  168. package/dist/integrations/service.js +33 -16
  169. package/dist/integrations/service.js.map +1 -1
  170. package/dist/organizations/default-roles.d.ts +22 -0
  171. package/dist/organizations/default-roles.js +89 -0
  172. package/dist/organizations/default-roles.js.map +1 -0
  173. package/dist/organizations/effective-permissions.d.ts +45 -0
  174. package/dist/organizations/effective-permissions.js +113 -0
  175. package/dist/organizations/effective-permissions.js.map +1 -0
  176. package/dist/organizations/events.d.ts +320 -181
  177. package/dist/organizations/events.js +90 -18
  178. package/dist/organizations/events.js.map +1 -1
  179. package/dist/organizations/index.d.ts +15 -10
  180. package/dist/organizations/index.js +8 -3
  181. package/dist/organizations/members.d.ts +66 -8
  182. package/dist/organizations/members.js.map +1 -1
  183. package/dist/organizations/notification-catalog.d.ts +31 -0
  184. package/dist/organizations/notification-catalog.js +32 -0
  185. package/dist/organizations/notification-catalog.js.map +1 -0
  186. package/dist/organizations/notification-categories.d.ts +34 -0
  187. package/dist/organizations/notification-categories.js +66 -0
  188. package/dist/organizations/notification-categories.js.map +1 -0
  189. package/dist/organizations/org-roles.d.ts +33 -0
  190. package/dist/organizations/org-roles.js +49 -0
  191. package/dist/organizations/org-roles.js.map +1 -0
  192. package/dist/organizations/permissions.d.ts +311 -0
  193. package/dist/organizations/permissions.js +327 -0
  194. package/dist/organizations/permissions.js.map +1 -0
  195. package/dist/organizations/ports.d.ts +187 -65
  196. package/dist/organizations/service.d.ts +104 -35
  197. package/dist/organizations/service.js +1247 -323
  198. package/dist/organizations/service.js.map +1 -1
  199. package/dist/organizations/testing.d.ts +18 -0
  200. package/dist/organizations/testing.js +37 -0
  201. package/dist/organizations/testing.js.map +1 -0
  202. package/dist/organizations/types.d.ts +5 -16
  203. package/dist/progress/index.d.ts +2 -2
  204. package/dist/progress/ports.d.ts +11 -3
  205. package/dist/progress/service.d.ts +6 -5
  206. package/dist/progress/service.js +23 -8
  207. package/dist/progress/service.js.map +1 -1
  208. package/dist/reporting/admin/index.d.ts +6 -4
  209. package/dist/reporting/admin/model.d.ts +36 -3
  210. package/dist/reporting/admin/ports.d.ts +39 -14
  211. package/dist/reporting/admin/service.d.ts +32 -11
  212. package/dist/reporting/admin/service.js +102 -18
  213. package/dist/reporting/admin/service.js.map +1 -1
  214. package/dist/reporting/events/index.d.ts +27 -0
  215. package/dist/reporting/events/index.js +20 -0
  216. package/dist/reporting/events/index.js.map +1 -0
  217. package/dist/reporting/member/calendar.d.ts +11 -0
  218. package/dist/reporting/member/calendar.js +75 -0
  219. package/dist/reporting/member/calendar.js.map +1 -0
  220. package/dist/reporting/member/index.d.ts +3 -2
  221. package/dist/reporting/member/model.d.ts +80 -26
  222. package/dist/reporting/member/ports.d.ts +50 -28
  223. package/dist/reporting/member/service.d.ts +81 -35
  224. package/dist/reporting/member/service.js +438 -145
  225. package/dist/reporting/member/service.js.map +1 -1
  226. package/dist/shared/actor.d.ts +22 -3
  227. package/dist/shared/errors.d.ts +15 -2
  228. package/dist/shared/errors.js +21 -2
  229. package/dist/shared/errors.js.map +1 -1
  230. package/dist/shared/id.d.ts +7 -0
  231. package/dist/shared/id.js +8 -1
  232. package/dist/shared/id.js.map +1 -1
  233. package/dist/shared/logger.d.ts +2 -1
  234. package/dist/shared/logger.js +10 -1
  235. package/dist/shared/logger.js.map +1 -1
  236. package/dist/shared/mailer.d.ts +11 -1
  237. package/dist/shared/mailer.js +32 -5
  238. package/dist/shared/mailer.js.map +1 -1
  239. package/dist/shared/notification-categories.d.ts +16 -0
  240. package/dist/shared/notification-categories.js +94 -0
  241. package/dist/shared/notification-categories.js.map +1 -0
  242. package/dist/shared/notification-settings.d.ts +14 -0
  243. package/dist/shared/notification-settings.js +61 -0
  244. package/dist/shared/notification-settings.js.map +1 -0
  245. package/dist/shared/ports.d.ts +68 -2
  246. package/dist/shared/settings.d.ts +2 -1
  247. package/dist/shared/settings.js +1 -0
  248. package/dist/shared/settings.js.map +1 -1
  249. package/dist/types/automations.d.ts +8 -2
  250. package/dist/types/content.d.ts +2 -2
  251. package/dist/types/discussion.d.ts +4 -5
  252. package/dist/types/email-templates.d.ts +119 -6
  253. package/dist/types/entitlements.d.ts +2 -2
  254. package/dist/types/identity.d.ts +14 -17
  255. package/dist/types/index.d.ts +13 -12
  256. package/dist/types/index.js +2 -1
  257. package/dist/types/notifications.d.ts +157 -0
  258. package/dist/types/notifications.js +1 -0
  259. package/dist/types/organizations.d.ts +3 -2
  260. package/dist/types/ports.d.ts +8 -1
  261. package/dist/types/ports.js +7 -1
  262. package/dist/types/ports.js.map +1 -0
  263. package/dist/types/schemas/assets.d.ts +5 -5
  264. package/dist/types/schemas/assets.js +4 -3
  265. package/dist/types/schemas/assets.js.map +1 -1
  266. package/dist/types/schemas/automations.d.ts +1 -10
  267. package/dist/types/schemas/automations.js +1 -3
  268. package/dist/types/schemas/automations.js.map +1 -1
  269. package/dist/types/schemas/content.d.ts +596 -11
  270. package/dist/types/schemas/content.js +225 -9
  271. package/dist/types/schemas/content.js.map +1 -1
  272. package/dist/types/schemas/discussion.d.ts +362 -66
  273. package/dist/types/schemas/discussion.js +123 -15
  274. package/dist/types/schemas/discussion.js.map +1 -1
  275. package/dist/types/schemas/entitlements.d.ts +14 -1
  276. package/dist/types/schemas/entitlements.js +11 -4
  277. package/dist/types/schemas/entitlements.js.map +1 -1
  278. package/dist/types/schemas/identity.d.ts +1 -85
  279. package/dist/types/schemas/identity.js +1 -42
  280. package/dist/types/schemas/identity.js.map +1 -1
  281. package/dist/types/schemas/index.d.ts +8 -7
  282. package/dist/types/schemas/index.js +8 -7
  283. package/dist/types/schemas/notifications.d.ts +96 -0
  284. package/dist/types/schemas/notifications.js +57 -0
  285. package/dist/types/schemas/notifications.js.map +1 -0
  286. package/dist/types/schemas/organizations.d.ts +96 -60
  287. package/dist/types/schemas/organizations.js +78 -27
  288. package/dist/types/schemas/organizations.js.map +1 -1
  289. package/dist/types/schemas/progress.d.ts +0 -1
  290. package/dist/types/schemas/progress.js +0 -1
  291. package/dist/types/schemas/progress.js.map +1 -1
  292. package/package.json +1 -1
  293. package/dist/automations/community-notifications.d.ts +0 -16
  294. package/dist/automations/community-notifications.js +0 -84
  295. package/dist/automations/community-notifications.js.map +0 -1
  296. package/dist/automations/seeds.d.ts +0 -17
  297. package/dist/automations/seeds.js +0 -235
  298. package/dist/automations/seeds.js.map +0 -1
  299. package/dist/identity/notifications.d.ts +0 -27
  300. package/dist/identity/notifications.js +0 -101
  301. package/dist/identity/notifications.js.map +0 -1
  302. package/dist/organizations/access-scopes.d.ts +0 -19
  303. package/dist/organizations/access-scopes.js +0 -89
  304. package/dist/organizations/access-scopes.js.map +0 -1
  305. package/dist/organizations/roles.d.ts +0 -19
  306. package/dist/organizations/roles.js +0 -82
  307. package/dist/organizations/roles.js.map +0 -1
@@ -1,13 +1,11 @@
1
- import { notificationKindSchema } from "../types/schemas/identity.js";
2
1
  import { deliverWebhookInputSchema } from "../types/schemas/automations.js";
3
2
  import { noopLogger } from "../shared/logger.js";
4
3
  import { entitlementEvents } from "../entitlements/events.js";
5
4
  import { ActionFailedError } from "./model.js";
6
- import { ALL_EMAIL_TEMPLATE_IDS, DELIVER_WEBHOOK_ACTION, NOTIFY_ACTION, publicDataSchema } from "./catalog.js";
5
+ import { ALL_EMAIL_TEMPLATE_IDS, DELIVER_WEBHOOK_ACTION, publicDataSchema } from "./catalog.js";
7
6
  import { webhookHeaders } from "./webhook-signing.js";
8
- import { address, firedAtOf } from "./community-mail.js";
7
+ import { address } from "./community-mail.js";
9
8
  import { COMMUNITY_DERIVATIONS } from "./community-derivations.js";
10
- import { NOTIFICATION_PLACEMENTS } from "./community-notifications.js";
11
9
  //#region src/automations/actions.ts
12
10
  const SEND_EMAIL_DERIVATIONS = {
13
11
  accessGranted: {
@@ -70,29 +68,9 @@ async function executeAction(action, event, deps) {
70
68
  for (const recipient of derived) (await send(template, recipient, event, deps) ? outcome.recipients : outcome.failed).push(recipient.orgUserId);
71
69
  return assertSomeDelivered("sendEmail", outcome);
72
70
  }
73
- case NOTIFY_ACTION: {
74
- const kind = notificationKindSchema.safeParse(action.input["kind"]);
75
- if (!kind.success) throw new ActionFailedError(`${NOTIFY_ACTION}: unknown kind "${String(action.input["kind"])}"`);
76
- return notify(kind.data, event, {
77
- ...deps,
78
- actionInput: action.input
79
- });
80
- }
81
71
  case "grantAccess":
82
72
  await grantAccess(action, event, deps);
83
73
  return reachedNoOne();
84
- case "publishDuePosts":
85
- await deps.scheduledWork.publishDuePosts(event.orgId, firedAtOf(event));
86
- return reachedNoOne();
87
- case "expireDueGrants":
88
- await deps.scheduledWork.expireDueGrants(event.orgId, firedAtOf(event));
89
- return reachedNoOne();
90
- case "sendDigest":
91
- await deps.scheduledWork.sendDigest(event.orgId, firedAtOf(event));
92
- return reachedNoOne();
93
- case "removeUnattachedUploads":
94
- await deps.scheduledWork.removeUnattachedUploads(event.orgId, firedAtOf(event));
95
- return reachedNoOne();
96
74
  case DELIVER_WEBHOOK_ACTION:
97
75
  await deliverWebhook(action, event, deps);
98
76
  return reachedNoOne();
@@ -115,45 +93,6 @@ async function send(template, recipient, event, deps) {
115
93
  return false;
116
94
  }
117
95
  }
118
- async function notify(kind, event, deps) {
119
- const derivation = SEND_EMAIL_DERIVATIONS[kind];
120
- if (!derivation || !derivation.triggers.includes(event.type)) throw new ActionFailedError(`${NOTIFY_ACTION}: kind "${kind}" cannot be derived from event "${event.type}"`);
121
- const recipients = await derivation.derive(event, deps);
122
- if (!recipients) throw new ActionFailedError(`${NOTIFY_ACTION}: event "${event.type}" is missing the data required to derive kind "${kind}"`);
123
- const preferences = await deps.desk.preferencesFor(event.orgId, recipients.map((recipient) => recipient.userId), kind);
124
- const placement = NOTIFICATION_PLACEMENTS[kind];
125
- const notifications = [];
126
- const outcome = reachedNoOne();
127
- for (const recipient of recipients) {
128
- const preference = preferences[recipient.userId];
129
- const email = preference?.email !== false;
130
- const inApp = preference?.inApp !== false;
131
- const emailed = email && await send(kind, recipient, event, deps);
132
- let placed = false;
133
- if (inApp) try {
134
- const placements = await placement.place(event, recipient, deps);
135
- notifications.push(...placements.map((spot) => ({
136
- ...spot,
137
- userId: recipient.userId,
138
- kind
139
- })));
140
- placed = true;
141
- } catch (err) {
142
- (deps.logger ?? noopLogger).warn("automation notification not placed", {
143
- orgId: event.orgId,
144
- automationId: deps.automationId,
145
- trigger: event.type,
146
- kind,
147
- orgUserId: recipient.orgUserId,
148
- err
149
- });
150
- }
151
- if (emailed || placed) outcome.recipients.push(recipient.orgUserId);
152
- if (email && !emailed || inApp && !placed) outcome.failed.push(recipient.orgUserId);
153
- }
154
- await deps.desk.notify(event.orgId, notifications);
155
- return assertSomeDelivered(NOTIFY_ACTION, outcome);
156
- }
157
96
  function grantRecipient(event) {
158
97
  if (typeof event.data !== "object" || event.data === null) return;
159
98
  const data = event.data;
@@ -1 +1 @@
1
- {"version":3,"file":"actions.js","names":[],"sources":["../../src/automations/actions.ts"],"sourcesContent":["// automations context — action runners. `executeAction` maps one\n// AutomationAction against the DomainEvent that triggered its run.\n//\n// SEND_EMAIL_DERIVATIONS is the sendEmail runner's internal derivation table:\n// which (trigger, template) pairings are derivable. A template with no entry\n// (e.g. courseCompleted) makes `executeAction` throw a named error, recorded\n// by the engine as a failed action.\nimport type { EmailTemplateId, NotificationKind } from '../types/index.js';\nimport { deliverWebhookInputSchema, notificationKindSchema } from '../types/schemas/index.js';\nimport { entitlementEvents } from '../entitlements/index.js';\nimport type { NewNotification } from '../identity/index.js';\nimport type { DomainEvent } from '../shared/ports.js';\nimport { noopLogger } from '../shared/logger.js';\nimport { ActionFailedError, type AutomationAction } from './model.js';\nimport type { WebhookResponse } from './ports.js';\nimport {\n ALL_EMAIL_TEMPLATE_IDS,\n DELIVER_WEBHOOK_ACTION,\n NOTIFY_ACTION,\n publicDataSchema,\n} from './catalog.js';\nimport { webhookHeaders } from './webhook-signing.js';\nimport { address, firedAtOf, type ActionDeps, type EmailRecipient } from './community-mail.js';\nimport { COMMUNITY_DERIVATIONS, type EmailDerivation } from './community-derivations.js';\nimport { NOTIFICATION_PLACEMENTS } from './community-notifications.js';\n\ntype SendEmailDerivations = { [K in EmailTemplateId]?: EmailDerivation<K> };\n\nexport const SEND_EMAIL_DERIVATIONS: SendEmailDerivations = {\n accessGranted: {\n triggers: ['entitlement.created'],\n derive: async (event, deps) => {\n const result = entitlementEvents.entitlementCreated.safeParse(event);\n if (!result.success) {\n return undefined;\n }\n const entitlement = result.data.data;\n if (!entitlement.contentId) {\n return [];\n }\n const content = await deps.lookups.contentInfo(entitlement.orgId, entitlement.contentId);\n if (!content) {\n return undefined;\n }\n return address(deps, event, entitlement.orgId, [entitlement.orgUserId], {\n contentTitle: content.title,\n contentId: content.id,\n contentType: content.type,\n });\n },\n },\n accessRevoked: {\n triggers: ['entitlement.deleted'],\n derive: async (event, deps) => {\n const result = entitlementEvents.entitlementDeleted.safeParse(event);\n if (!result.success) {\n return undefined;\n }\n const entitlement = result.data.data;\n if (!entitlement.contentId) {\n return [];\n }\n const content = await deps.lookups.contentInfo(entitlement.orgId, entitlement.contentId);\n if (!content) {\n return undefined;\n }\n return address(deps, event, entitlement.orgId, [entitlement.orgUserId], {\n contentTitle: content.title,\n });\n },\n },\n ...COMMUNITY_DERIVATIONS,\n};\n\nexport type { ActionDeps, EmailRecipient };\n\n/** Who an action reached, and who a delivery to failed for (a partial failure does not throw). */\nexport interface ActionOutcome {\n recipients: string[];\n failed: string[];\n}\n\nfunction reachedNoOne(): ActionOutcome {\n return { recipients: [], failed: [] };\n}\n\nfunction assertSomeDelivered(action: string, outcome: ActionOutcome): ActionOutcome {\n if (outcome.recipients.length === 0 && outcome.failed.length > 0) {\n throw new ActionFailedError(\n `${action}: delivery failed for all ${outcome.failed.length} recipients`,\n );\n }\n return outcome;\n}\n\nfunction isEmailTemplateId(value: unknown): value is EmailTemplateId {\n return typeof value === 'string' && value in ALL_EMAIL_TEMPLATE_IDS;\n}\n\n/** Throws on any failure but a partial fan-out — the engine owns retry policy and failure bookkeeping. */\nexport async function executeAction(\n action: AutomationAction,\n event: DomainEvent,\n deps: ActionDeps,\n): Promise<ActionOutcome> {\n switch (action.type) {\n case 'sendEmail': {\n const template = action.input['template'];\n if (!isEmailTemplateId(template)) {\n throw new ActionFailedError(`sendEmail: unknown template \"${String(template)}\"`);\n }\n const derivation = SEND_EMAIL_DERIVATIONS[template];\n if (!derivation || !derivation.triggers.includes(event.type)) {\n throw new ActionFailedError(\n `sendEmail: template \"${template}\" cannot be derived from event \"${event.type}\"`,\n );\n }\n const derived = await derivation.derive(event, { ...deps, actionInput: action.input });\n if (!derived) {\n throw new ActionFailedError(\n `sendEmail: event \"${event.type}\" is missing the data required to derive template \"${template}\"`,\n );\n }\n const outcome = reachedNoOne();\n for (const recipient of derived) {\n const list = (await send(template, recipient, event, deps))\n ? outcome.recipients\n : outcome.failed;\n list.push(recipient.orgUserId);\n }\n return assertSomeDelivered('sendEmail', outcome);\n }\n case NOTIFY_ACTION: {\n const kind = notificationKindSchema.safeParse(action.input['kind']);\n if (!kind.success) {\n throw new ActionFailedError(\n `${NOTIFY_ACTION}: unknown kind \"${String(action.input['kind'])}\"`,\n );\n }\n return notify(kind.data, event, { ...deps, actionInput: action.input });\n }\n case 'grantAccess': {\n await grantAccess(action, event, deps);\n return reachedNoOne();\n }\n case 'publishDuePosts': {\n await deps.scheduledWork.publishDuePosts(event.orgId, firedAtOf(event));\n return reachedNoOne();\n }\n case 'expireDueGrants': {\n await deps.scheduledWork.expireDueGrants(event.orgId, firedAtOf(event));\n return reachedNoOne();\n }\n case 'sendDigest': {\n await deps.scheduledWork.sendDigest(event.orgId, firedAtOf(event));\n return reachedNoOne();\n }\n case 'removeUnattachedUploads': {\n await deps.scheduledWork.removeUnattachedUploads(event.orgId, firedAtOf(event));\n return reachedNoOne();\n }\n case DELIVER_WEBHOOK_ACTION: {\n await deliverWebhook(action, event, deps);\n return reachedNoOne();\n }\n default: {\n throw new ActionFailedError(`unknown automation action type \"${action.type}\"`);\n }\n }\n}\n\nasync function send<K extends EmailTemplateId>(\n template: K,\n recipient: EmailRecipient<K>,\n event: DomainEvent,\n deps: ActionDeps,\n): Promise<boolean> {\n try {\n await deps.mailer.send(recipient.to, template, recipient.params);\n return true;\n } catch (err) {\n (deps.logger ?? noopLogger).warn('automation email not delivered', {\n orgId: event.orgId,\n automationId: deps.automationId,\n trigger: event.type,\n template,\n orgUserId: recipient.orgUserId,\n err,\n });\n return false;\n }\n}\n\nasync function notify<K extends NotificationKind>(\n kind: K,\n event: DomainEvent,\n deps: ActionDeps,\n): Promise<ActionOutcome> {\n const derivation = SEND_EMAIL_DERIVATIONS[kind] as EmailDerivation<K> | undefined;\n if (!derivation || !derivation.triggers.includes(event.type)) {\n throw new ActionFailedError(\n `${NOTIFY_ACTION}: kind \"${kind}\" cannot be derived from event \"${event.type}\"`,\n );\n }\n const recipients = await derivation.derive(event, deps);\n if (!recipients) {\n throw new ActionFailedError(\n `${NOTIFY_ACTION}: event \"${event.type}\" is missing the data required to derive kind \"${kind}\"`,\n );\n }\n const preferences = await deps.desk.preferencesFor(\n event.orgId,\n recipients.map((recipient) => recipient.userId),\n kind,\n );\n const placement = NOTIFICATION_PLACEMENTS[kind];\n const notifications: NewNotification[] = [];\n const outcome = reachedNoOne();\n for (const recipient of recipients) {\n const preference = preferences[recipient.userId];\n const email = preference?.email !== false;\n const inApp = preference?.inApp !== false;\n const emailed = email && (await send(kind, recipient, event, deps));\n let placed = false;\n if (inApp) {\n try {\n const placements = await placement.place(event, recipient, deps);\n notifications.push(\n ...placements.map((spot) => ({ ...spot, userId: recipient.userId, kind })),\n );\n placed = true;\n } catch (err) {\n (deps.logger ?? noopLogger).warn('automation notification not placed', {\n orgId: event.orgId,\n automationId: deps.automationId,\n trigger: event.type,\n kind,\n orgUserId: recipient.orgUserId,\n err,\n });\n }\n }\n if (emailed || placed) {\n outcome.recipients.push(recipient.orgUserId);\n }\n if ((email && !emailed) || (inApp && !placed)) {\n outcome.failed.push(recipient.orgUserId);\n }\n }\n await deps.desk.notify(event.orgId, notifications);\n return assertSomeDelivered(NOTIFY_ACTION, outcome);\n}\n\nfunction grantRecipient(event: DomainEvent): string | undefined {\n if (typeof event.data !== 'object' || event.data === null) {\n return undefined;\n }\n const data = event.data as Record<string, unknown>;\n if (event.type === 'organization.student.created' && typeof data.id === 'string') {\n return data.id;\n }\n if (typeof data.orgUserId === 'string') {\n return data.orgUserId;\n }\n return undefined;\n}\n\nasync function grantAccess(\n action: AutomationAction,\n event: DomainEvent,\n deps: ActionDeps,\n): Promise<void> {\n const contentId = typeof action.input.contentId === 'string' ? action.input.contentId : null;\n const bundleId = typeof action.input.bundleId === 'string' ? action.input.bundleId : null;\n if ((contentId == null) === (bundleId == null)) {\n throw new ActionFailedError('grantAccess: provide exactly one of contentId or bundleId');\n }\n const orgUserId = grantRecipient(event);\n if (!orgUserId) {\n throw new ActionFailedError(`grantAccess: event \"${event.type}\" has no recipient`);\n }\n const days = action.input.expiresInDays;\n const expiresAt =\n typeof days === 'number' && Number.isFinite(days)\n ? new Date(Date.now() + days * 86_400_000)\n : null;\n await deps.grants.grant(event.orgId, { orgUserId, contentId, bundleId, expiresAt });\n}\n\nasync function deliverWebhook(\n action: AutomationAction,\n event: DomainEvent,\n deps: ActionDeps,\n): Promise<void> {\n const input = deliverWebhookInputSchema.safeParse(action.input);\n if (!input.success) {\n throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: invalid input`);\n }\n const publicData = publicDataSchema(event.type);\n if (!publicData) {\n throw new ActionFailedError(\n `${DELIVER_WEBHOOK_ACTION}: event \"${event.type}\" is not published`,\n );\n }\n const data = publicData.safeParse(event.data);\n if (!data.success) {\n throw new ActionFailedError(\n `${DELIVER_WEBHOOK_ACTION}: event \"${event.type}\" does not match its published shape`,\n );\n }\n const secrets = await deps.credentials.reveal(event.orgId, input.data.secretRef);\n const secret = secrets?.['secret'];\n if (typeof secret !== 'string') {\n throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: signing secret not found`);\n }\n const body = JSON.stringify({\n id: event.id,\n type: event.type,\n version: event.version,\n orgId: event.orgId,\n occurredAt: event.occurredAt,\n data: data.data,\n });\n const timestamp = Math.floor(Date.now() / 1000);\n let response: WebhookResponse;\n try {\n response = await deps.webhooks.send({\n url: input.data.url,\n headers: webhookHeaders(secret, event.id, timestamp, body),\n body,\n });\n } catch (err) {\n (deps.logger ?? noopLogger).warn('webhook endpoint not reached', {\n orgId: event.orgId,\n automationId: deps.automationId,\n eventId: event.id,\n err,\n });\n throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: the endpoint could not be reached`);\n }\n if (response.status < 200 || response.status >= 300) {\n throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: endpoint responded ${response.status}`);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"actions.js","names":[],"sources":["../../src/automations/actions.ts"],"sourcesContent":["// automations context — action runners. `executeAction` maps one\n// AutomationAction against the DomainEvent that triggered its run.\n//\n// SEND_EMAIL_DERIVATIONS is the sendEmail runner's internal derivation table:\n// which (trigger, template) pairings are derivable. A template with no entry\n// (e.g. courseCompleted) makes `executeAction` throw a named error, recorded\n// by the engine as a failed action.\nimport type { EmailTemplateId } from '../types/index.js';\nimport { deliverWebhookInputSchema } from '../types/schemas/index.js';\nimport { entitlementEvents } from '../entitlements/index.js';\nimport type { DomainEvent } from '../shared/ports.js';\nimport { noopLogger } from '../shared/logger.js';\nimport { ActionFailedError, type AutomationAction } from './model.js';\nimport type { WebhookResponse } from './ports.js';\nimport { ALL_EMAIL_TEMPLATE_IDS, DELIVER_WEBHOOK_ACTION, publicDataSchema } from './catalog.js';\nimport { webhookHeaders } from './webhook-signing.js';\nimport { address, type ActionDeps, type EmailRecipient } from './community-mail.js';\nimport { COMMUNITY_DERIVATIONS, type EmailDerivation } from './community-derivations.js';\n\ntype SendEmailDerivations = { [K in EmailTemplateId]?: EmailDerivation<K> };\n\nexport const SEND_EMAIL_DERIVATIONS: SendEmailDerivations = {\n accessGranted: {\n triggers: ['entitlement.created'],\n derive: async (event, deps) => {\n const result = entitlementEvents.entitlementCreated.safeParse(event);\n if (!result.success) {\n return undefined;\n }\n const entitlement = result.data.data;\n if (!entitlement.contentId) {\n return [];\n }\n const content = await deps.lookups.contentInfo(entitlement.orgId, entitlement.contentId);\n if (!content) {\n return undefined;\n }\n return address(deps, event, entitlement.orgId, [entitlement.orgUserId], {\n contentTitle: content.title,\n contentId: content.id,\n contentType: content.type,\n });\n },\n },\n accessRevoked: {\n triggers: ['entitlement.deleted'],\n derive: async (event, deps) => {\n const result = entitlementEvents.entitlementDeleted.safeParse(event);\n if (!result.success) {\n return undefined;\n }\n const entitlement = result.data.data;\n if (!entitlement.contentId) {\n return [];\n }\n const content = await deps.lookups.contentInfo(entitlement.orgId, entitlement.contentId);\n if (!content) {\n return undefined;\n }\n return address(deps, event, entitlement.orgId, [entitlement.orgUserId], {\n contentTitle: content.title,\n });\n },\n },\n ...COMMUNITY_DERIVATIONS,\n};\n\nexport type { ActionDeps, EmailRecipient };\n\n/** Who an action reached, and who a delivery to failed for (a partial failure does not throw). */\nexport interface ActionOutcome {\n recipients: string[];\n failed: string[];\n}\n\nfunction reachedNoOne(): ActionOutcome {\n return { recipients: [], failed: [] };\n}\n\nfunction assertSomeDelivered(action: string, outcome: ActionOutcome): ActionOutcome {\n if (outcome.recipients.length === 0 && outcome.failed.length > 0) {\n throw new ActionFailedError(\n `${action}: delivery failed for all ${outcome.failed.length} recipients`,\n );\n }\n return outcome;\n}\n\nfunction isEmailTemplateId(value: unknown): value is EmailTemplateId {\n return typeof value === 'string' && value in ALL_EMAIL_TEMPLATE_IDS;\n}\n\n/** Throws on any failure but a partial fan-out — the engine owns retry policy and failure bookkeeping. */\nexport async function executeAction(\n action: AutomationAction,\n event: DomainEvent,\n deps: ActionDeps,\n): Promise<ActionOutcome> {\n switch (action.type) {\n case 'sendEmail': {\n const template = action.input['template'];\n if (!isEmailTemplateId(template)) {\n throw new ActionFailedError(`sendEmail: unknown template \"${String(template)}\"`);\n }\n const derivation = SEND_EMAIL_DERIVATIONS[template];\n if (!derivation || !derivation.triggers.includes(event.type)) {\n throw new ActionFailedError(\n `sendEmail: template \"${template}\" cannot be derived from event \"${event.type}\"`,\n );\n }\n const derived = await derivation.derive(event, { ...deps, actionInput: action.input });\n if (!derived) {\n throw new ActionFailedError(\n `sendEmail: event \"${event.type}\" is missing the data required to derive template \"${template}\"`,\n );\n }\n const outcome = reachedNoOne();\n for (const recipient of derived) {\n const list = (await send(template, recipient, event, deps))\n ? outcome.recipients\n : outcome.failed;\n list.push(recipient.orgUserId);\n }\n return assertSomeDelivered('sendEmail', outcome);\n }\n case 'grantAccess': {\n await grantAccess(action, event, deps);\n return reachedNoOne();\n }\n case DELIVER_WEBHOOK_ACTION: {\n await deliverWebhook(action, event, deps);\n return reachedNoOne();\n }\n default: {\n throw new ActionFailedError(`unknown automation action type \"${action.type}\"`);\n }\n }\n}\n\nasync function send<K extends EmailTemplateId>(\n template: K,\n recipient: EmailRecipient<K>,\n event: DomainEvent,\n deps: ActionDeps,\n): Promise<boolean> {\n try {\n await deps.mailer.send(recipient.to, template, recipient.params);\n return true;\n } catch (err) {\n (deps.logger ?? noopLogger).warn('automation email not delivered', {\n orgId: event.orgId,\n automationId: deps.automationId,\n trigger: event.type,\n template,\n orgUserId: recipient.orgUserId,\n err,\n });\n return false;\n }\n}\n\nfunction grantRecipient(event: DomainEvent): string | undefined {\n if (typeof event.data !== 'object' || event.data === null) {\n return undefined;\n }\n const data = event.data as Record<string, unknown>;\n if (event.type === 'organization.student.created' && typeof data.id === 'string') {\n return data.id;\n }\n if (typeof data.orgUserId === 'string') {\n return data.orgUserId;\n }\n return undefined;\n}\n\nasync function grantAccess(\n action: AutomationAction,\n event: DomainEvent,\n deps: ActionDeps,\n): Promise<void> {\n const contentId = typeof action.input.contentId === 'string' ? action.input.contentId : null;\n const bundleId = typeof action.input.bundleId === 'string' ? action.input.bundleId : null;\n if ((contentId == null) === (bundleId == null)) {\n throw new ActionFailedError('grantAccess: provide exactly one of contentId or bundleId');\n }\n const orgUserId = grantRecipient(event);\n if (!orgUserId) {\n throw new ActionFailedError(`grantAccess: event \"${event.type}\" has no recipient`);\n }\n const days = action.input.expiresInDays;\n const expiresAt =\n typeof days === 'number' && Number.isFinite(days)\n ? new Date(Date.now() + days * 86_400_000)\n : null;\n await deps.grants.grant(event.orgId, {\n orgUserId,\n contentId,\n bundleId,\n expiresAt,\n });\n}\n\nasync function deliverWebhook(\n action: AutomationAction,\n event: DomainEvent,\n deps: ActionDeps,\n): Promise<void> {\n const input = deliverWebhookInputSchema.safeParse(action.input);\n if (!input.success) {\n throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: invalid input`);\n }\n const publicData = publicDataSchema(event.type);\n if (!publicData) {\n throw new ActionFailedError(\n `${DELIVER_WEBHOOK_ACTION}: event \"${event.type}\" is not published`,\n );\n }\n const data = publicData.safeParse(event.data);\n if (!data.success) {\n throw new ActionFailedError(\n `${DELIVER_WEBHOOK_ACTION}: event \"${event.type}\" does not match its published shape`,\n );\n }\n const secrets = await deps.credentials.reveal(event.orgId, input.data.secretRef);\n const secret = secrets?.['secret'];\n if (typeof secret !== 'string') {\n throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: signing secret not found`);\n }\n const body = JSON.stringify({\n id: event.id,\n type: event.type,\n version: event.version,\n orgId: event.orgId,\n occurredAt: event.occurredAt,\n data: data.data,\n });\n const timestamp = Math.floor(Date.now() / 1000);\n let response: WebhookResponse;\n try {\n response = await deps.webhooks.send({\n url: input.data.url,\n headers: webhookHeaders(secret, event.id, timestamp, body),\n body,\n });\n } catch (err) {\n (deps.logger ?? noopLogger).warn('webhook endpoint not reached', {\n orgId: event.orgId,\n automationId: deps.automationId,\n eventId: event.id,\n err,\n });\n throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: the endpoint could not be reached`);\n }\n if (response.status < 200 || response.status >= 300) {\n throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: endpoint responded ${response.status}`);\n }\n}\n"],"mappings":";;;;;;;;;AAqBA,MAAa,yBAA+C;CAC1D,eAAe;EACb,UAAU,CAAC,qBAAqB;EAChC,QAAQ,OAAO,OAAO,SAAS;GAC7B,MAAM,SAAS,kBAAkB,mBAAmB,UAAU,KAAK;GACnE,IAAI,CAAC,OAAO,SACV;GAEF,MAAM,cAAc,OAAO,KAAK;GAChC,IAAI,CAAC,YAAY,WACf,OAAO,CAAC;GAEV,MAAM,UAAU,MAAM,KAAK,QAAQ,YAAY,YAAY,OAAO,YAAY,SAAS;GACvF,IAAI,CAAC,SACH;GAEF,OAAO,QAAQ,MAAM,OAAO,YAAY,OAAO,CAAC,YAAY,SAAS,GAAG;IACtE,cAAc,QAAQ;IACtB,WAAW,QAAQ;IACnB,aAAa,QAAQ;GACvB,CAAC;EACH;CACF;CACA,eAAe;EACb,UAAU,CAAC,qBAAqB;EAChC,QAAQ,OAAO,OAAO,SAAS;GAC7B,MAAM,SAAS,kBAAkB,mBAAmB,UAAU,KAAK;GACnE,IAAI,CAAC,OAAO,SACV;GAEF,MAAM,cAAc,OAAO,KAAK;GAChC,IAAI,CAAC,YAAY,WACf,OAAO,CAAC;GAEV,MAAM,UAAU,MAAM,KAAK,QAAQ,YAAY,YAAY,OAAO,YAAY,SAAS;GACvF,IAAI,CAAC,SACH;GAEF,OAAO,QAAQ,MAAM,OAAO,YAAY,OAAO,CAAC,YAAY,SAAS,GAAG,EACtE,cAAc,QAAQ,MACxB,CAAC;EACH;CACF;CACA,GAAG;AACL;AAUA,SAAS,eAA8B;CACrC,OAAO;EAAE,YAAY,CAAC;EAAG,QAAQ,CAAC;CAAE;AACtC;AAEA,SAAS,oBAAoB,QAAgB,SAAuC;CAClF,IAAI,QAAQ,WAAW,WAAW,KAAK,QAAQ,OAAO,SAAS,GAC7D,MAAM,IAAI,kBACR,GAAG,OAAO,4BAA4B,QAAQ,OAAO,OAAO,YAC9D;CAEF,OAAO;AACT;AAEA,SAAS,kBAAkB,OAA0C;CACnE,OAAO,OAAO,UAAU,YAAY,SAAS;AAC/C;;AAGA,eAAsB,cACpB,QACA,OACA,MACwB;CACxB,QAAQ,OAAO,MAAf;EACE,KAAK,aAAa;GAChB,MAAM,WAAW,OAAO,MAAM;GAC9B,IAAI,CAAC,kBAAkB,QAAQ,GAC7B,MAAM,IAAI,kBAAkB,gCAAgC,OAAO,QAAQ,EAAE,EAAE;GAEjF,MAAM,aAAa,uBAAuB;GAC1C,IAAI,CAAC,cAAc,CAAC,WAAW,SAAS,SAAS,MAAM,IAAI,GACzD,MAAM,IAAI,kBACR,wBAAwB,SAAS,kCAAkC,MAAM,KAAK,EAChF;GAEF,MAAM,UAAU,MAAM,WAAW,OAAO,OAAO;IAAE,GAAG;IAAM,aAAa,OAAO;GAAM,CAAC;GACrF,IAAI,CAAC,SACH,MAAM,IAAI,kBACR,qBAAqB,MAAM,KAAK,qDAAqD,SAAS,EAChG;GAEF,MAAM,UAAU,aAAa;GAC7B,KAAK,MAAM,aAAa,SAItB,CAHc,MAAM,KAAK,UAAU,WAAW,OAAO,IAAI,IACrD,QAAQ,aACR,QAAQ,OAAA,CACP,KAAK,UAAU,SAAS;GAE/B,OAAO,oBAAoB,aAAa,OAAO;EACjD;EACA,KAAK;GACH,MAAM,YAAY,QAAQ,OAAO,IAAI;GACrC,OAAO,aAAa;EAEtB,KAAK;GACH,MAAM,eAAe,QAAQ,OAAO,IAAI;GACxC,OAAO,aAAa;EAEtB,SACE,MAAM,IAAI,kBAAkB,mCAAmC,OAAO,KAAK,EAAE;CAEjF;AACF;AAEA,eAAe,KACb,UACA,WACA,OACA,MACkB;CAClB,IAAI;EACF,MAAM,KAAK,OAAO,KAAK,UAAU,IAAI,UAAU,UAAU,MAAM;EAC/D,OAAO;CACT,SAAS,KAAK;EACZ,CAAC,KAAK,UAAU,WAAA,CAAY,KAAK,kCAAkC;GACjE,OAAO,MAAM;GACb,cAAc,KAAK;GACnB,SAAS,MAAM;GACf;GACA,WAAW,UAAU;GACrB;EACF,CAAC;EACD,OAAO;CACT;AACF;AAEA,SAAS,eAAe,OAAwC;CAC9D,IAAI,OAAO,MAAM,SAAS,YAAY,MAAM,SAAS,MACnD;CAEF,MAAM,OAAO,MAAM;CACnB,IAAI,MAAM,SAAS,kCAAkC,OAAO,KAAK,OAAO,UACtE,OAAO,KAAK;CAEd,IAAI,OAAO,KAAK,cAAc,UAC5B,OAAO,KAAK;AAGhB;AAEA,eAAe,YACb,QACA,OACA,MACe;CACf,MAAM,YAAY,OAAO,OAAO,MAAM,cAAc,WAAW,OAAO,MAAM,YAAY;CACxF,MAAM,WAAW,OAAO,OAAO,MAAM,aAAa,WAAW,OAAO,MAAM,WAAW;CACrF,IAAK,aAAa,UAAW,YAAY,OACvC,MAAM,IAAI,kBAAkB,2DAA2D;CAEzF,MAAM,YAAY,eAAe,KAAK;CACtC,IAAI,CAAC,WACH,MAAM,IAAI,kBAAkB,uBAAuB,MAAM,KAAK,mBAAmB;CAEnF,MAAM,OAAO,OAAO,MAAM;CAC1B,MAAM,YACJ,OAAO,SAAS,YAAY,OAAO,SAAS,IAAI,IAC5C,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAU,IACvC;CACN,MAAM,KAAK,OAAO,MAAM,MAAM,OAAO;EACnC;EACA;EACA;EACA;CACF,CAAC;AACH;AAEA,eAAe,eACb,QACA,OACA,MACe;CACf,MAAM,QAAQ,0BAA0B,UAAU,OAAO,KAAK;CAC9D,IAAI,CAAC,MAAM,SACT,MAAM,IAAI,kBAAkB,GAAG,uBAAuB,gBAAgB;CAExE,MAAM,aAAa,iBAAiB,MAAM,IAAI;CAC9C,IAAI,CAAC,YACH,MAAM,IAAI,kBACR,GAAG,uBAAuB,WAAW,MAAM,KAAK,mBAClD;CAEF,MAAM,OAAO,WAAW,UAAU,MAAM,IAAI;CAC5C,IAAI,CAAC,KAAK,SACR,MAAM,IAAI,kBACR,GAAG,uBAAuB,WAAW,MAAM,KAAK,qCAClD;CAGF,MAAM,UAAS,MADO,KAAK,YAAY,OAAO,MAAM,OAAO,MAAM,KAAK,SAAS,EAAA,GACtD;CACzB,IAAI,OAAO,WAAW,UACpB,MAAM,IAAI,kBAAkB,GAAG,uBAAuB,2BAA2B;CAEnF,MAAM,OAAO,KAAK,UAAU;EAC1B,IAAI,MAAM;EACV,MAAM,MAAM;EACZ,SAAS,MAAM;EACf,OAAO,MAAM;EACb,YAAY,MAAM;EAClB,MAAM,KAAK;CACb,CAAC;CACD,MAAM,YAAY,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;CAC9C,IAAI;CACJ,IAAI;EACF,WAAW,MAAM,KAAK,SAAS,KAAK;GAClC,KAAK,MAAM,KAAK;GAChB,SAAS,eAAe,QAAQ,MAAM,IAAI,WAAW,IAAI;GACzD;EACF,CAAC;CACH,SAAS,KAAK;EACZ,CAAC,KAAK,UAAU,WAAA,CAAY,KAAK,gCAAgC;GAC/D,OAAO,MAAM;GACb,cAAc,KAAK;GACnB,SAAS,MAAM;GACf;EACF,CAAC;EACD,MAAM,IAAI,kBAAkB,GAAG,uBAAuB,oCAAoC;CAC5F;CACA,IAAI,SAAS,SAAS,OAAO,SAAS,UAAU,KAC9C,MAAM,IAAI,kBAAkB,GAAG,uBAAuB,uBAAuB,SAAS,QAAQ;AAElG"}
@@ -7,10 +7,9 @@ import { z } from "zod";
7
7
  /** Every EmailTemplateId — a missing key here is a compile error, kept exhaustive by construction. */
8
8
  declare const ALL_EMAIL_TEMPLATE_IDS: Record<EmailTemplateId, true>;
9
9
  declare const DELIVER_WEBHOOK_ACTION = "deliverWebhook";
10
- declare const NOTIFY_ACTION = "notify";
11
10
  declare function catalogActions(): AvailableActions;
12
11
  declare function catalogTriggers(): AvailableTriggers['triggers'];
13
12
  declare function publicDataSchema(type: string): z.ZodType | undefined;
14
13
  //#endregion
15
- export { ALL_EMAIL_TEMPLATE_IDS, DELIVER_WEBHOOK_ACTION, NOTIFY_ACTION, catalogActions, catalogTriggers, publicDataSchema };
14
+ export { ALL_EMAIL_TEMPLATE_IDS, DELIVER_WEBHOOK_ACTION, catalogActions, catalogTriggers, publicDataSchema };
16
15
  //# sourceMappingURL=catalog.d.ts.map
@@ -1,11 +1,10 @@
1
- import { notificationKindSchema } from "../types/schemas/identity.js";
2
- import { assetEvents } from "../assets/events.js";
3
- import { organizationEvents } from "../organizations/events.js";
4
1
  import { entitlementEvents } from "../entitlements/events.js";
2
+ import { assetEvents } from "../assets/events.js";
5
3
  import { contentEvents } from "../content/events.js";
6
4
  import { discussionEvents } from "../discussion/events.js";
7
5
  import { identityEvents } from "../identity/events.js";
8
6
  import { integrationEvents } from "../integrations/events.js";
7
+ import { organizationEvents } from "../organizations/events.js";
9
8
  import { progressEvents } from "../progress/events.js";
10
9
  //#region src/automations/catalog.ts
11
10
  /** Every EmailTemplateId — a missing key here is a compile error, kept exhaustive by construction. */
@@ -34,75 +33,52 @@ const ALL_EMAIL_TEMPLATE_IDS = {
34
33
  messageRequest: true,
35
34
  newMessage: true,
36
35
  addedToGroupChat: true,
37
- youAreNowAModerator: true,
36
+ roleAssigned: true,
38
37
  digest: true,
39
38
  postAwaitingReview: true,
40
39
  joinRequest: true,
41
40
  somethingWasFlagged: true,
42
- messageWasFlagged: true
41
+ messageWasFlagged: true,
42
+ postReacted: true,
43
+ commentReacted: true,
44
+ mention: true,
45
+ messageReacted: true,
46
+ courseActivityAdded: true,
47
+ activityCommentCreated: true,
48
+ activityCommentRemoved: true,
49
+ activityCommentReacted: true,
50
+ activityCommentFlagged: true,
51
+ activityMention: true,
52
+ eventGranted: true,
53
+ eventStartingSoon: true,
54
+ eventChanged: true,
55
+ eventCanceled: true,
56
+ eventRevoked: true
43
57
  };
44
58
  const DELIVER_WEBHOOK_ACTION = "deliverWebhook";
45
- const NOTIFY_ACTION = "notify";
46
59
  function catalogActions() {
47
- return [
48
- {
49
- type: "sendEmail",
50
- description: "Send a transactional email using a built-in template.",
51
- inputSchema: {
52
- type: "object",
53
- required: ["template"],
54
- properties: { template: { enum: Object.keys(ALL_EMAIL_TEMPLATE_IDS) } }
55
- },
56
- source: "system"
57
- },
58
- {
59
- type: NOTIFY_ACTION,
60
- description: "Tell each person concerned, in the app and by email, as they chose.",
61
- inputSchema: {
62
- type: "object",
63
- required: ["kind"],
64
- properties: { kind: { enum: [...notificationKindSchema.options] } }
65
- },
66
- source: "system"
67
- },
68
- {
69
- type: "grantAccess",
70
- description: "Grant a content item or bundle to the person on the event.",
71
- inputSchema: {
72
- type: "object",
73
- properties: {
74
- contentId: { type: "string" },
75
- bundleId: { type: "string" },
76
- expiresInDays: { type: "number" }
77
- }
78
- },
79
- source: "system"
80
- },
81
- {
82
- type: "publishDuePosts",
83
- description: "Submit scheduled posts whose time has passed.",
84
- inputSchema: { type: "object" },
85
- source: "system"
60
+ return [{
61
+ type: "sendEmail",
62
+ description: "Send a transactional email using a built-in template.",
63
+ inputSchema: {
64
+ type: "object",
65
+ required: ["template"],
66
+ properties: { template: { enum: Object.keys(ALL_EMAIL_TEMPLATE_IDS) } }
86
67
  },
87
- {
88
- type: "expireDueGrants",
89
- description: "Expire grants whose time has passed.",
90
- inputSchema: { type: "object" },
91
- source: "system"
68
+ source: "system"
69
+ }, {
70
+ type: "grantAccess",
71
+ description: "Grant a content item or bundle to the person on the event.",
72
+ inputSchema: {
73
+ type: "object",
74
+ properties: {
75
+ contentId: { type: "string" },
76
+ bundleId: { type: "string" },
77
+ expiresInDays: { type: "number" }
78
+ }
92
79
  },
93
- {
94
- type: "sendDigest",
95
- description: "Open the daily digest for every community.",
96
- inputSchema: { type: "object" },
97
- source: "system"
98
- },
99
- {
100
- type: "removeUnattachedUploads",
101
- description: "Remove member uploads older than an hour that nothing references.",
102
- inputSchema: { type: "object" },
103
- source: "system"
104
- }
105
- ];
80
+ source: "system"
81
+ }];
106
82
  }
107
83
  const TRIGGERS = [
108
84
  {
@@ -153,6 +129,42 @@ const TRIGGERS = [
153
129
  category: "Organization",
154
130
  description: "an organization member was updated"
155
131
  },
132
+ {
133
+ event: organizationEvents.roleAssigned,
134
+ label: "Role assigned",
135
+ category: "Organization",
136
+ description: "a person was given a role on the organization or on a content item"
137
+ },
138
+ {
139
+ event: organizationEvents.roleUnassigned,
140
+ label: "Role removed",
141
+ category: "Organization",
142
+ description: "a role was removed from a person on a content item"
143
+ },
144
+ {
145
+ event: organizationEvents.roleCreated,
146
+ label: "Role created",
147
+ category: "Organization",
148
+ description: "the organization added a role"
149
+ },
150
+ {
151
+ event: organizationEvents.roleUpdated,
152
+ label: "Role updated",
153
+ category: "Organization",
154
+ description: "a role was renamed, changed or reset"
155
+ },
156
+ {
157
+ event: organizationEvents.roleDeleted,
158
+ label: "Role deleted",
159
+ category: "Organization",
160
+ description: "the organization deleted a role it added"
161
+ },
162
+ {
163
+ event: organizationEvents.ownershipTransferred,
164
+ label: "Ownership transferred",
165
+ category: "Organization",
166
+ description: "ownership of the organization moved to another person"
167
+ },
156
168
  {
157
169
  event: organizationEvents.inviteCreated,
158
170
  label: "Invite sent",
@@ -243,6 +255,12 @@ const TRIGGERS = [
243
255
  category: "Courses",
244
256
  description: "a course activity was deleted"
245
257
  },
258
+ {
259
+ event: contentEvents.activityPublished,
260
+ label: "Activity published",
261
+ category: "Courses",
262
+ description: "an activity of a published course was published"
263
+ },
246
264
  {
247
265
  event: contentEvents.activitiesReordered,
248
266
  label: "Activities reordered",
@@ -291,6 +309,30 @@ const TRIGGERS = [
291
309
  category: "Downloads",
292
310
  description: "download assets were reordered"
293
311
  },
312
+ {
313
+ event: contentEvents.eventCreated,
314
+ label: "Event created",
315
+ category: "Events",
316
+ description: "an event was created"
317
+ },
318
+ {
319
+ event: contentEvents.eventUpdated,
320
+ label: "Event updated",
321
+ category: "Events",
322
+ description: "an event was updated, published, unpublished or canceled"
323
+ },
324
+ {
325
+ event: contentEvents.eventDeleted,
326
+ label: "Event deleted",
327
+ category: "Events",
328
+ description: "an event was deleted"
329
+ },
330
+ {
331
+ event: contentEvents.eventStartingSoon,
332
+ label: "Event starting soon",
333
+ category: "Events",
334
+ description: "an event starts in 1 day or in 1 hour"
335
+ },
294
336
  {
295
337
  event: contentEvents.communityCreated,
296
338
  label: "Community created",
@@ -423,6 +465,18 @@ const TRIGGERS = [
423
465
  category: "Discussion",
424
466
  description: "a comment was published"
425
467
  },
468
+ {
469
+ event: discussionEvents.commentUpdated,
470
+ label: "Comment updated",
471
+ category: "Discussion",
472
+ description: "a comment was edited"
473
+ },
474
+ {
475
+ event: discussionEvents.commentReactionAdded,
476
+ label: "Comment reaction added",
477
+ category: "Discussion",
478
+ description: "a person reacted to a comment for the first time"
479
+ },
426
480
  {
427
481
  event: discussionEvents.commentRemoved,
428
482
  label: "Comment removed",
@@ -453,6 +507,12 @@ const TRIGGERS = [
453
507
  category: "Discussion",
454
508
  description: "a post was published"
455
509
  },
510
+ {
511
+ event: discussionEvents.postReactionAdded,
512
+ label: "Post reaction added",
513
+ category: "Discussion",
514
+ description: "a person reacted to a post for the first time"
515
+ },
456
516
  {
457
517
  event: discussionEvents.postRemoved,
458
518
  label: "Post removed",
@@ -489,6 +549,18 @@ const TRIGGERS = [
489
549
  category: "Discussion",
490
550
  description: "a message was sent"
491
551
  },
552
+ {
553
+ event: discussionEvents.messageUpdated,
554
+ label: "Message updated",
555
+ category: "Discussion",
556
+ description: "a message was edited"
557
+ },
558
+ {
559
+ event: discussionEvents.messageReactionAdded,
560
+ label: "Message reaction added",
561
+ category: "Discussion",
562
+ description: "a person reacted to a message for the first time"
563
+ },
492
564
  {
493
565
  event: discussionEvents.messageDeleted,
494
566
  label: "Message deleted",
@@ -505,7 +577,7 @@ const TRIGGERS = [
505
577
  event: discussionEvents.digestDue,
506
578
  label: "Digest due",
507
579
  category: "Discussion",
508
- description: "a community digest period opened"
580
+ description: "a community's digest came due on its schedule"
509
581
  },
510
582
  {
511
583
  event: assetEvents.assetCreated,
@@ -536,6 +608,6 @@ function publicDataSchema(type) {
536
608
  return TRIGGERS.find((trigger) => trigger.event.type === type)?.event.publicData;
537
609
  }
538
610
  //#endregion
539
- export { ALL_EMAIL_TEMPLATE_IDS, DELIVER_WEBHOOK_ACTION, NOTIFY_ACTION, catalogActions, catalogTriggers, publicDataSchema };
611
+ export { ALL_EMAIL_TEMPLATE_IDS, DELIVER_WEBHOOK_ACTION, catalogActions, catalogTriggers, publicDataSchema };
540
612
 
541
613
  //# sourceMappingURL=catalog.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"catalog.js","names":[],"sources":["../../src/automations/catalog.ts"],"sourcesContent":["// automations context — the code-owned catalogs `availableActions()` and\n// `availableTriggers()` serve: built-in action definitions and the domain\n// event types automations may react to.\nimport type { z } from 'zod';\nimport type { EmailTemplateId } from '../types/index.js';\nimport { notificationKindSchema } from '../types/schemas/index.js';\nimport { assetEvents } from '../assets/index.js';\nimport { contentEvents } from '../content/index.js';\nimport { discussionEvents } from '../discussion/index.js';\nimport { entitlementEvents } from '../entitlements/index.js';\nimport { identityEvents } from '../identity/index.js';\nimport { integrationEvents } from '../integrations/index.js';\nimport { organizationEvents } from '../organizations/index.js';\nimport { progressEvents } from '../progress/index.js';\nimport type { AvailableActions, AvailableTriggers } from './types.js';\n\n/** Every EmailTemplateId — a missing key here is a compile error, kept exhaustive by construction. */\nexport const ALL_EMAIL_TEMPLATE_IDS: Record<EmailTemplateId, true> = {\n magicLink: true,\n studentInvite: true,\n memberInvite: true,\n passwordReset: true,\n passwordChanged: true,\n emailVerification: true,\n accessGranted: true,\n accessRevoked: true,\n courseCompleted: true,\n welcomeToCommunity: true,\n membershipEndingSoon: true,\n membershipEnded: true,\n newPostInSpace: true,\n yourPostWasPublished: true,\n yourPostWasRemoved: true,\n newCommentOnPost: true,\n replyToYourComment: true,\n yourCommentWasRemoved: true,\n requestApproved: true,\n requestDeclined: true,\n invitedToSpace: true,\n messageRequest: true,\n newMessage: true,\n addedToGroupChat: true,\n youAreNowAModerator: true,\n digest: true,\n postAwaitingReview: true,\n joinRequest: true,\n somethingWasFlagged: true,\n messageWasFlagged: true,\n};\n\nexport const DELIVER_WEBHOOK_ACTION = 'deliverWebhook';\nexport const NOTIFY_ACTION = 'notify';\n\nexport function catalogActions(): AvailableActions {\n return [\n {\n type: 'sendEmail',\n description: 'Send a transactional email using a built-in template.',\n inputSchema: {\n type: 'object',\n required: ['template'],\n properties: {\n template: { enum: Object.keys(ALL_EMAIL_TEMPLATE_IDS) },\n },\n },\n source: 'system',\n },\n {\n type: NOTIFY_ACTION,\n description: 'Tell each person concerned, in the app and by email, as they chose.',\n inputSchema: {\n type: 'object',\n required: ['kind'],\n properties: {\n kind: { enum: [...notificationKindSchema.options] },\n },\n },\n source: 'system',\n },\n {\n type: 'grantAccess',\n description: 'Grant a content item or bundle to the person on the event.',\n inputSchema: {\n type: 'object',\n properties: {\n contentId: { type: 'string' },\n bundleId: { type: 'string' },\n expiresInDays: { type: 'number' },\n },\n },\n source: 'system',\n },\n {\n type: 'publishDuePosts',\n description: 'Submit scheduled posts whose time has passed.',\n inputSchema: { type: 'object' },\n source: 'system',\n },\n {\n type: 'expireDueGrants',\n description: 'Expire grants whose time has passed.',\n inputSchema: { type: 'object' },\n source: 'system',\n },\n {\n type: 'sendDigest',\n description: 'Open the daily digest for every community.',\n inputSchema: { type: 'object' },\n source: 'system',\n },\n {\n type: 'removeUnattachedUploads',\n description: 'Remove member uploads older than an hour that nothing references.',\n inputSchema: { type: 'object' },\n source: 'system',\n },\n ];\n}\n\ninterface CatalogTrigger {\n readonly event: { readonly type: string; readonly publicData: z.ZodType };\n readonly label: string;\n readonly category: string;\n readonly description: string;\n}\n\nconst TRIGGERS: CatalogTrigger[] = [\n {\n event: identityEvents.userCreated,\n label: 'User created',\n category: 'Users',\n description: 'a user was created',\n },\n {\n event: identityEvents.userUpdated,\n label: 'User updated',\n category: 'Users',\n description: 'a user was updated',\n },\n {\n event: organizationEvents.organizationCreated,\n label: 'Organization created',\n category: 'Organization',\n description: 'an organization was created',\n },\n {\n event: organizationEvents.organizationUpdated,\n label: 'Organization updated',\n category: 'Organization',\n description: 'an organization was updated',\n },\n {\n event: organizationEvents.organizationDeleted,\n label: 'Organization deleted',\n category: 'Organization',\n description: 'an organization was deleted',\n },\n {\n event: organizationEvents.orgUserLinked,\n label: 'Member joined',\n category: 'Organization',\n description: 'a user was linked to an organization',\n },\n {\n event: organizationEvents.orgUserDeleted,\n label: 'Member removed',\n category: 'Organization',\n description: 'a user was removed from an organization',\n },\n {\n event: organizationEvents.orgUserUpdated,\n label: 'Member updated',\n category: 'Organization',\n description: 'an organization member was updated',\n },\n {\n event: organizationEvents.inviteCreated,\n label: 'Invite sent',\n category: 'Organization',\n description: 'an invite was created or re-issued',\n },\n {\n event: organizationEvents.inviteAccepted,\n label: 'Invite accepted',\n category: 'Organization',\n description: 'an invite was accepted',\n },\n {\n event: organizationEvents.studentCreated,\n label: 'Student created',\n category: 'Students',\n description: 'a student was created',\n },\n {\n event: organizationEvents.studentDeleted,\n label: 'Student deleted',\n category: 'Students',\n description: 'a student was deleted',\n },\n {\n event: organizationEvents.studentLinked,\n label: 'Student linked to account',\n category: 'Students',\n description: 'a pending student was linked to an auth account',\n },\n {\n event: contentEvents.courseCreated,\n label: 'Course created',\n category: 'Courses',\n description: 'a course was created',\n },\n {\n event: contentEvents.courseUpdated,\n label: 'Course updated',\n category: 'Courses',\n description: 'a course was updated',\n },\n {\n event: contentEvents.courseDeleted,\n label: 'Course deleted',\n category: 'Courses',\n description: 'a course was deleted',\n },\n {\n event: contentEvents.moduleCreated,\n label: 'Module created',\n category: 'Courses',\n description: 'a course module was created',\n },\n {\n event: contentEvents.moduleUpdated,\n label: 'Module updated',\n category: 'Courses',\n description: 'a course module was updated',\n },\n {\n event: contentEvents.moduleDeleted,\n label: 'Module deleted',\n category: 'Courses',\n description: 'a course module was deleted',\n },\n {\n event: contentEvents.modulesReordered,\n label: 'Modules reordered',\n category: 'Courses',\n description: 'course modules were reordered',\n },\n {\n event: contentEvents.activityCreated,\n label: 'Activity created',\n category: 'Courses',\n description: 'a course activity was created',\n },\n {\n event: contentEvents.activityUpdated,\n label: 'Activity updated',\n category: 'Courses',\n description: 'a course activity was updated',\n },\n {\n event: contentEvents.activityDeleted,\n label: 'Activity deleted',\n category: 'Courses',\n description: 'a course activity was deleted',\n },\n {\n event: contentEvents.activitiesReordered,\n label: 'Activities reordered',\n category: 'Courses',\n description: 'course activities were reordered',\n },\n {\n event: contentEvents.downloadCreated,\n label: 'Download created',\n category: 'Downloads',\n description: 'a download was created',\n },\n {\n event: contentEvents.downloadUpdated,\n label: 'Download updated',\n category: 'Downloads',\n description: 'a download was updated',\n },\n {\n event: contentEvents.downloadDeleted,\n label: 'Download deleted',\n category: 'Downloads',\n description: 'a download was deleted',\n },\n {\n event: contentEvents.downloadAssetAdded,\n label: 'Download file added',\n category: 'Downloads',\n description: 'a download asset was added',\n },\n {\n event: contentEvents.downloadAssetRemoved,\n label: 'Download file removed',\n category: 'Downloads',\n description: 'a download asset was removed',\n },\n {\n event: contentEvents.downloadAssetRenamed,\n label: 'Download file renamed',\n category: 'Downloads',\n description: 'a download asset was renamed',\n },\n {\n event: contentEvents.downloadAssetsReordered,\n label: 'Download files reordered',\n category: 'Downloads',\n description: 'download assets were reordered',\n },\n {\n event: contentEvents.communityCreated,\n label: 'Community created',\n category: 'Communities',\n description: 'a community was created',\n },\n {\n event: contentEvents.communityUpdated,\n label: 'Community updated',\n category: 'Communities',\n description: 'a community was updated',\n },\n {\n event: contentEvents.communityDeleted,\n label: 'Community deleted',\n category: 'Communities',\n description: 'a community was deleted',\n },\n {\n event: contentEvents.spaceCreated,\n label: 'Space created',\n category: 'Communities',\n description: 'a space was created',\n },\n {\n event: contentEvents.spaceUpdated,\n label: 'Space updated',\n category: 'Communities',\n description: 'a space was updated',\n },\n {\n event: contentEvents.spaceDeleted,\n label: 'Space deleted',\n category: 'Communities',\n description: 'a space was deleted',\n },\n {\n event: contentEvents.spacesReordered,\n label: 'Spaces reordered',\n category: 'Communities',\n description: 'spaces were reordered',\n },\n {\n event: entitlementEvents.entitlementCreated,\n label: 'Access granted',\n category: 'Access',\n description: 'a student was granted access to content',\n },\n {\n event: entitlementEvents.entitlementUpdated,\n label: 'Access updated',\n category: 'Access',\n description: \"an entitlement's status or expiry changed\",\n },\n {\n event: entitlementEvents.entitlementDeleted,\n label: 'Access revoked',\n category: 'Access',\n description: \"a student's access to content was revoked\",\n },\n {\n event: entitlementEvents.entitlementExpired,\n label: 'Access expired',\n category: 'Access',\n description: 'an entitlement passed its expiry',\n },\n {\n event: entitlementEvents.entitlementEndingSoon,\n label: 'Access ending soon',\n category: 'Access',\n description: 'an entitlement expires within a week',\n },\n {\n event: entitlementEvents.spaceMemberCreated,\n label: 'Space member created',\n category: 'Access',\n description: 'a person was added to a space',\n },\n {\n event: entitlementEvents.spaceMemberUpdated,\n label: 'Space member updated',\n category: 'Access',\n description: 'a space membership changed',\n },\n {\n event: entitlementEvents.spaceMemberDeleted,\n label: 'Space member removed',\n category: 'Access',\n description: 'a space membership was removed',\n },\n {\n event: progressEvents.progressStarted,\n label: 'Progress started',\n category: 'Progress',\n description: 'a student started a progress record',\n },\n {\n event: progressEvents.progressCompleted,\n label: 'Progress completed',\n category: 'Progress',\n description: 'a student completed a progress record',\n },\n {\n event: integrationEvents.connectionCreated,\n label: 'Integration connected',\n category: 'Integrations',\n description: 'an integration connection was established',\n },\n {\n event: integrationEvents.connectionUpdated,\n label: 'Integration updated',\n category: 'Integrations',\n description: 'an integration connection changed',\n },\n {\n event: integrationEvents.connectionRemoved,\n label: 'Integration disconnected',\n category: 'Integrations',\n description: 'an integration connection was removed',\n },\n {\n event: discussionEvents.commentCreated,\n label: 'Comment created',\n category: 'Discussion',\n description: 'a comment was created',\n },\n {\n event: discussionEvents.commentPublished,\n label: 'Comment published',\n category: 'Discussion',\n description: 'a comment was published',\n },\n {\n event: discussionEvents.commentRemoved,\n label: 'Comment removed',\n category: 'Discussion',\n description: 'a comment was removed',\n },\n {\n event: discussionEvents.commentReported,\n label: 'Comment reported',\n category: 'Discussion',\n description: 'a comment was reported',\n },\n {\n event: discussionEvents.postCreated,\n label: 'Post created',\n category: 'Discussion',\n description: 'a post was created',\n },\n {\n event: discussionEvents.postUpdated,\n label: 'Post updated',\n category: 'Discussion',\n description: 'a post was updated',\n },\n {\n event: discussionEvents.postPublished,\n label: 'Post published',\n category: 'Discussion',\n description: 'a post was published',\n },\n {\n event: discussionEvents.postRemoved,\n label: 'Post removed',\n category: 'Discussion',\n description: 'a post was removed',\n },\n {\n event: discussionEvents.postReported,\n label: 'Post reported',\n category: 'Discussion',\n description: 'a post was reported',\n },\n {\n event: discussionEvents.chatCreated,\n label: 'Chat created',\n category: 'Discussion',\n description: 'a chat was created',\n },\n {\n event: discussionEvents.chatUpdated,\n label: 'Chat updated',\n category: 'Discussion',\n description: 'a chat was updated',\n },\n {\n event: discussionEvents.chatParticipantAdded,\n label: 'Chat participant added',\n category: 'Discussion',\n description: 'a person was added to a chat',\n },\n {\n event: discussionEvents.messageSent,\n label: 'Message sent',\n category: 'Discussion',\n description: 'a message was sent',\n },\n {\n event: discussionEvents.messageDeleted,\n label: 'Message deleted',\n category: 'Discussion',\n description: 'a message was deleted',\n },\n {\n event: discussionEvents.messageReported,\n label: 'Message reported',\n category: 'Discussion',\n description: 'a message was reported',\n },\n {\n event: discussionEvents.digestDue,\n label: 'Digest due',\n category: 'Discussion',\n description: 'a community digest period opened',\n },\n {\n event: assetEvents.assetCreated,\n label: 'Upload requested',\n category: 'Assets',\n description: 'an asset upload was requested',\n },\n {\n event: assetEvents.assetReady,\n label: 'Upload ready',\n category: 'Assets',\n description: 'an asset was confirmed ready',\n },\n {\n event: assetEvents.assetDeleted,\n label: 'Asset deleted',\n category: 'Assets',\n description: 'an asset was deleted',\n },\n];\n\nexport function catalogTriggers(): AvailableTriggers['triggers'] {\n return TRIGGERS.map(({ event, ...trigger }) => ({ type: event.type, ...trigger }));\n}\n\nexport function publicDataSchema(type: string): z.ZodType | undefined {\n return TRIGGERS.find((trigger) => trigger.event.type === type)?.event.publicData;\n}\n"],"mappings":";;;;;;;;;;;AAiBA,MAAa,yBAAwD;CACnE,WAAW;CACX,eAAe;CACf,cAAc;CACd,eAAe;CACf,iBAAiB;CACjB,mBAAmB;CACnB,eAAe;CACf,eAAe;CACf,iBAAiB;CACjB,oBAAoB;CACpB,sBAAsB;CACtB,iBAAiB;CACjB,gBAAgB;CAChB,sBAAsB;CACtB,oBAAoB;CACpB,kBAAkB;CAClB,oBAAoB;CACpB,uBAAuB;CACvB,iBAAiB;CACjB,iBAAiB;CACjB,gBAAgB;CAChB,gBAAgB;CAChB,YAAY;CACZ,kBAAkB;CAClB,qBAAqB;CACrB,QAAQ;CACR,oBAAoB;CACpB,aAAa;CACb,qBAAqB;CACrB,mBAAmB;AACrB;AAEA,MAAa,yBAAyB;AACtC,MAAa,gBAAgB;AAE7B,SAAgB,iBAAmC;CACjD,OAAO;EACL;GACE,MAAM;GACN,aAAa;GACb,aAAa;IACX,MAAM;IACN,UAAU,CAAC,UAAU;IACrB,YAAY,EACV,UAAU,EAAE,MAAM,OAAO,KAAK,sBAAsB,EAAE,EACxD;GACF;GACA,QAAQ;EACV;EACA;GACE,MAAM;GACN,aAAa;GACb,aAAa;IACX,MAAM;IACN,UAAU,CAAC,MAAM;IACjB,YAAY,EACV,MAAM,EAAE,MAAM,CAAC,GAAG,uBAAuB,OAAO,EAAE,EACpD;GACF;GACA,QAAQ;EACV;EACA;GACE,MAAM;GACN,aAAa;GACb,aAAa;IACX,MAAM;IACN,YAAY;KACV,WAAW,EAAE,MAAM,SAAS;KAC5B,UAAU,EAAE,MAAM,SAAS;KAC3B,eAAe,EAAE,MAAM,SAAS;IAClC;GACF;GACA,QAAQ;EACV;EACA;GACE,MAAM;GACN,aAAa;GACb,aAAa,EAAE,MAAM,SAAS;GAC9B,QAAQ;EACV;EACA;GACE,MAAM;GACN,aAAa;GACb,aAAa,EAAE,MAAM,SAAS;GAC9B,QAAQ;EACV;EACA;GACE,MAAM;GACN,aAAa;GACb,aAAa,EAAE,MAAM,SAAS;GAC9B,QAAQ;EACV;EACA;GACE,MAAM;GACN,aAAa;GACb,aAAa,EAAE,MAAM,SAAS;GAC9B,QAAQ;EACV;CACF;AACF;AASA,MAAM,WAA6B;CACjC;EACE,OAAO,eAAe;EACtB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,eAAe;EACtB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,eAAe;EACtB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,eAAe;EACtB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,YAAY;EACnB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,YAAY;EACnB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,YAAY;EACnB,OAAO;EACP,UAAU;EACV,aAAa;CACf;AACF;AAEA,SAAgB,kBAAiD;CAC/D,OAAO,SAAS,KAAK,EAAE,OAAO,GAAG,eAAe;EAAE,MAAM,MAAM;EAAM,GAAG;CAAQ,EAAE;AACnF;AAEA,SAAgB,iBAAiB,MAAqC;CACpE,OAAO,SAAS,MAAM,YAAY,QAAQ,MAAM,SAAS,IAAI,CAAC,EAAE,MAAM;AACxE"}
1
+ {"version":3,"file":"catalog.js","names":[],"sources":["../../src/automations/catalog.ts"],"sourcesContent":["// automations context — the code-owned catalogs `availableActions()` and\n// `availableTriggers()` serve: built-in action definitions and the domain\n// event types automations may react to.\nimport type { z } from 'zod';\nimport type { EmailTemplateId } from '../types/index.js';\nimport { assetEvents } from '../assets/index.js';\nimport { contentEvents } from '../content/index.js';\nimport { discussionEvents } from '../discussion/index.js';\nimport { entitlementEvents } from '../entitlements/index.js';\nimport { identityEvents } from '../identity/index.js';\nimport { integrationEvents } from '../integrations/index.js';\nimport { organizationEvents } from '../organizations/index.js';\nimport { progressEvents } from '../progress/index.js';\nimport type { AvailableActions, AvailableTriggers } from './types.js';\n\n/** Every EmailTemplateId — a missing key here is a compile error, kept exhaustive by construction. */\nexport const ALL_EMAIL_TEMPLATE_IDS: Record<EmailTemplateId, true> = {\n magicLink: true,\n studentInvite: true,\n memberInvite: true,\n passwordReset: true,\n passwordChanged: true,\n emailVerification: true,\n accessGranted: true,\n accessRevoked: true,\n courseCompleted: true,\n welcomeToCommunity: true,\n membershipEndingSoon: true,\n membershipEnded: true,\n newPostInSpace: true,\n yourPostWasPublished: true,\n yourPostWasRemoved: true,\n newCommentOnPost: true,\n replyToYourComment: true,\n yourCommentWasRemoved: true,\n requestApproved: true,\n requestDeclined: true,\n invitedToSpace: true,\n messageRequest: true,\n newMessage: true,\n addedToGroupChat: true,\n roleAssigned: true,\n digest: true,\n postAwaitingReview: true,\n joinRequest: true,\n somethingWasFlagged: true,\n messageWasFlagged: true,\n postReacted: true,\n commentReacted: true,\n mention: true,\n messageReacted: true,\n courseActivityAdded: true,\n activityCommentCreated: true,\n activityCommentRemoved: true,\n activityCommentReacted: true,\n activityCommentFlagged: true,\n activityMention: true,\n eventGranted: true,\n eventStartingSoon: true,\n eventChanged: true,\n eventCanceled: true,\n eventRevoked: true,\n};\n\nexport const DELIVER_WEBHOOK_ACTION = 'deliverWebhook';\n\nexport function catalogActions(): AvailableActions {\n return [\n {\n type: 'sendEmail',\n description: 'Send a transactional email using a built-in template.',\n inputSchema: {\n type: 'object',\n required: ['template'],\n properties: {\n template: { enum: Object.keys(ALL_EMAIL_TEMPLATE_IDS) },\n },\n },\n source: 'system',\n },\n {\n type: 'grantAccess',\n description: 'Grant a content item or bundle to the person on the event.',\n inputSchema: {\n type: 'object',\n properties: {\n contentId: { type: 'string' },\n bundleId: { type: 'string' },\n expiresInDays: { type: 'number' },\n },\n },\n source: 'system',\n },\n ];\n}\n\ninterface CatalogTrigger {\n readonly event: { readonly type: string; readonly publicData: z.ZodType };\n readonly label: string;\n readonly category: string;\n readonly description: string;\n}\n\nconst TRIGGERS: CatalogTrigger[] = [\n {\n event: identityEvents.userCreated,\n label: 'User created',\n category: 'Users',\n description: 'a user was created',\n },\n {\n event: identityEvents.userUpdated,\n label: 'User updated',\n category: 'Users',\n description: 'a user was updated',\n },\n {\n event: organizationEvents.organizationCreated,\n label: 'Organization created',\n category: 'Organization',\n description: 'an organization was created',\n },\n {\n event: organizationEvents.organizationUpdated,\n label: 'Organization updated',\n category: 'Organization',\n description: 'an organization was updated',\n },\n {\n event: organizationEvents.organizationDeleted,\n label: 'Organization deleted',\n category: 'Organization',\n description: 'an organization was deleted',\n },\n {\n event: organizationEvents.orgUserLinked,\n label: 'Member joined',\n category: 'Organization',\n description: 'a user was linked to an organization',\n },\n {\n event: organizationEvents.orgUserDeleted,\n label: 'Member removed',\n category: 'Organization',\n description: 'a user was removed from an organization',\n },\n {\n event: organizationEvents.orgUserUpdated,\n label: 'Member updated',\n category: 'Organization',\n description: 'an organization member was updated',\n },\n {\n event: organizationEvents.roleAssigned,\n label: 'Role assigned',\n category: 'Organization',\n description: 'a person was given a role on the organization or on a content item',\n },\n {\n event: organizationEvents.roleUnassigned,\n label: 'Role removed',\n category: 'Organization',\n description: 'a role was removed from a person on a content item',\n },\n {\n event: organizationEvents.roleCreated,\n label: 'Role created',\n category: 'Organization',\n description: 'the organization added a role',\n },\n {\n event: organizationEvents.roleUpdated,\n label: 'Role updated',\n category: 'Organization',\n description: 'a role was renamed, changed or reset',\n },\n {\n event: organizationEvents.roleDeleted,\n label: 'Role deleted',\n category: 'Organization',\n description: 'the organization deleted a role it added',\n },\n {\n event: organizationEvents.ownershipTransferred,\n label: 'Ownership transferred',\n category: 'Organization',\n description: 'ownership of the organization moved to another person',\n },\n {\n event: organizationEvents.inviteCreated,\n label: 'Invite sent',\n category: 'Organization',\n description: 'an invite was created or re-issued',\n },\n {\n event: organizationEvents.inviteAccepted,\n label: 'Invite accepted',\n category: 'Organization',\n description: 'an invite was accepted',\n },\n {\n event: organizationEvents.studentCreated,\n label: 'Student created',\n category: 'Students',\n description: 'a student was created',\n },\n {\n event: organizationEvents.studentDeleted,\n label: 'Student deleted',\n category: 'Students',\n description: 'a student was deleted',\n },\n {\n event: organizationEvents.studentLinked,\n label: 'Student linked to account',\n category: 'Students',\n description: 'a pending student was linked to an auth account',\n },\n {\n event: contentEvents.courseCreated,\n label: 'Course created',\n category: 'Courses',\n description: 'a course was created',\n },\n {\n event: contentEvents.courseUpdated,\n label: 'Course updated',\n category: 'Courses',\n description: 'a course was updated',\n },\n {\n event: contentEvents.courseDeleted,\n label: 'Course deleted',\n category: 'Courses',\n description: 'a course was deleted',\n },\n {\n event: contentEvents.moduleCreated,\n label: 'Module created',\n category: 'Courses',\n description: 'a course module was created',\n },\n {\n event: contentEvents.moduleUpdated,\n label: 'Module updated',\n category: 'Courses',\n description: 'a course module was updated',\n },\n {\n event: contentEvents.moduleDeleted,\n label: 'Module deleted',\n category: 'Courses',\n description: 'a course module was deleted',\n },\n {\n event: contentEvents.modulesReordered,\n label: 'Modules reordered',\n category: 'Courses',\n description: 'course modules were reordered',\n },\n {\n event: contentEvents.activityCreated,\n label: 'Activity created',\n category: 'Courses',\n description: 'a course activity was created',\n },\n {\n event: contentEvents.activityUpdated,\n label: 'Activity updated',\n category: 'Courses',\n description: 'a course activity was updated',\n },\n {\n event: contentEvents.activityDeleted,\n label: 'Activity deleted',\n category: 'Courses',\n description: 'a course activity was deleted',\n },\n {\n event: contentEvents.activityPublished,\n label: 'Activity published',\n category: 'Courses',\n description: 'an activity of a published course was published',\n },\n {\n event: contentEvents.activitiesReordered,\n label: 'Activities reordered',\n category: 'Courses',\n description: 'course activities were reordered',\n },\n {\n event: contentEvents.downloadCreated,\n label: 'Download created',\n category: 'Downloads',\n description: 'a download was created',\n },\n {\n event: contentEvents.downloadUpdated,\n label: 'Download updated',\n category: 'Downloads',\n description: 'a download was updated',\n },\n {\n event: contentEvents.downloadDeleted,\n label: 'Download deleted',\n category: 'Downloads',\n description: 'a download was deleted',\n },\n {\n event: contentEvents.downloadAssetAdded,\n label: 'Download file added',\n category: 'Downloads',\n description: 'a download asset was added',\n },\n {\n event: contentEvents.downloadAssetRemoved,\n label: 'Download file removed',\n category: 'Downloads',\n description: 'a download asset was removed',\n },\n {\n event: contentEvents.downloadAssetRenamed,\n label: 'Download file renamed',\n category: 'Downloads',\n description: 'a download asset was renamed',\n },\n {\n event: contentEvents.downloadAssetsReordered,\n label: 'Download files reordered',\n category: 'Downloads',\n description: 'download assets were reordered',\n },\n {\n event: contentEvents.eventCreated,\n label: 'Event created',\n category: 'Events',\n description: 'an event was created',\n },\n {\n event: contentEvents.eventUpdated,\n label: 'Event updated',\n category: 'Events',\n description: 'an event was updated, published, unpublished or canceled',\n },\n {\n event: contentEvents.eventDeleted,\n label: 'Event deleted',\n category: 'Events',\n description: 'an event was deleted',\n },\n {\n event: contentEvents.eventStartingSoon,\n label: 'Event starting soon',\n category: 'Events',\n description: 'an event starts in 1 day or in 1 hour',\n },\n {\n event: contentEvents.communityCreated,\n label: 'Community created',\n category: 'Communities',\n description: 'a community was created',\n },\n {\n event: contentEvents.communityUpdated,\n label: 'Community updated',\n category: 'Communities',\n description: 'a community was updated',\n },\n {\n event: contentEvents.communityDeleted,\n label: 'Community deleted',\n category: 'Communities',\n description: 'a community was deleted',\n },\n {\n event: contentEvents.spaceCreated,\n label: 'Space created',\n category: 'Communities',\n description: 'a space was created',\n },\n {\n event: contentEvents.spaceUpdated,\n label: 'Space updated',\n category: 'Communities',\n description: 'a space was updated',\n },\n {\n event: contentEvents.spaceDeleted,\n label: 'Space deleted',\n category: 'Communities',\n description: 'a space was deleted',\n },\n {\n event: contentEvents.spacesReordered,\n label: 'Spaces reordered',\n category: 'Communities',\n description: 'spaces were reordered',\n },\n {\n event: entitlementEvents.entitlementCreated,\n label: 'Access granted',\n category: 'Access',\n description: 'a student was granted access to content',\n },\n {\n event: entitlementEvents.entitlementUpdated,\n label: 'Access updated',\n category: 'Access',\n description: \"an entitlement's status or expiry changed\",\n },\n {\n event: entitlementEvents.entitlementDeleted,\n label: 'Access revoked',\n category: 'Access',\n description: \"a student's access to content was revoked\",\n },\n {\n event: entitlementEvents.entitlementExpired,\n label: 'Access expired',\n category: 'Access',\n description: 'an entitlement passed its expiry',\n },\n {\n event: entitlementEvents.entitlementEndingSoon,\n label: 'Access ending soon',\n category: 'Access',\n description: 'an entitlement expires within a week',\n },\n {\n event: entitlementEvents.spaceMemberCreated,\n label: 'Space member created',\n category: 'Access',\n description: 'a person was added to a space',\n },\n {\n event: entitlementEvents.spaceMemberUpdated,\n label: 'Space member updated',\n category: 'Access',\n description: 'a space membership changed',\n },\n {\n event: entitlementEvents.spaceMemberDeleted,\n label: 'Space member removed',\n category: 'Access',\n description: 'a space membership was removed',\n },\n {\n event: progressEvents.progressStarted,\n label: 'Progress started',\n category: 'Progress',\n description: 'a student started a progress record',\n },\n {\n event: progressEvents.progressCompleted,\n label: 'Progress completed',\n category: 'Progress',\n description: 'a student completed a progress record',\n },\n {\n event: integrationEvents.connectionCreated,\n label: 'Integration connected',\n category: 'Integrations',\n description: 'an integration connection was established',\n },\n {\n event: integrationEvents.connectionUpdated,\n label: 'Integration updated',\n category: 'Integrations',\n description: 'an integration connection changed',\n },\n {\n event: integrationEvents.connectionRemoved,\n label: 'Integration disconnected',\n category: 'Integrations',\n description: 'an integration connection was removed',\n },\n {\n event: discussionEvents.commentCreated,\n label: 'Comment created',\n category: 'Discussion',\n description: 'a comment was created',\n },\n {\n event: discussionEvents.commentPublished,\n label: 'Comment published',\n category: 'Discussion',\n description: 'a comment was published',\n },\n {\n event: discussionEvents.commentUpdated,\n label: 'Comment updated',\n category: 'Discussion',\n description: 'a comment was edited',\n },\n {\n event: discussionEvents.commentReactionAdded,\n label: 'Comment reaction added',\n category: 'Discussion',\n description: 'a person reacted to a comment for the first time',\n },\n {\n event: discussionEvents.commentRemoved,\n label: 'Comment removed',\n category: 'Discussion',\n description: 'a comment was removed',\n },\n {\n event: discussionEvents.commentReported,\n label: 'Comment reported',\n category: 'Discussion',\n description: 'a comment was reported',\n },\n {\n event: discussionEvents.postCreated,\n label: 'Post created',\n category: 'Discussion',\n description: 'a post was created',\n },\n {\n event: discussionEvents.postUpdated,\n label: 'Post updated',\n category: 'Discussion',\n description: 'a post was updated',\n },\n {\n event: discussionEvents.postPublished,\n label: 'Post published',\n category: 'Discussion',\n description: 'a post was published',\n },\n {\n event: discussionEvents.postReactionAdded,\n label: 'Post reaction added',\n category: 'Discussion',\n description: 'a person reacted to a post for the first time',\n },\n {\n event: discussionEvents.postRemoved,\n label: 'Post removed',\n category: 'Discussion',\n description: 'a post was removed',\n },\n {\n event: discussionEvents.postReported,\n label: 'Post reported',\n category: 'Discussion',\n description: 'a post was reported',\n },\n {\n event: discussionEvents.chatCreated,\n label: 'Chat created',\n category: 'Discussion',\n description: 'a chat was created',\n },\n {\n event: discussionEvents.chatUpdated,\n label: 'Chat updated',\n category: 'Discussion',\n description: 'a chat was updated',\n },\n {\n event: discussionEvents.chatParticipantAdded,\n label: 'Chat participant added',\n category: 'Discussion',\n description: 'a person was added to a chat',\n },\n {\n event: discussionEvents.messageSent,\n label: 'Message sent',\n category: 'Discussion',\n description: 'a message was sent',\n },\n {\n event: discussionEvents.messageUpdated,\n label: 'Message updated',\n category: 'Discussion',\n description: 'a message was edited',\n },\n {\n event: discussionEvents.messageReactionAdded,\n label: 'Message reaction added',\n category: 'Discussion',\n description: 'a person reacted to a message for the first time',\n },\n {\n event: discussionEvents.messageDeleted,\n label: 'Message deleted',\n category: 'Discussion',\n description: 'a message was deleted',\n },\n {\n event: discussionEvents.messageReported,\n label: 'Message reported',\n category: 'Discussion',\n description: 'a message was reported',\n },\n {\n event: discussionEvents.digestDue,\n label: 'Digest due',\n category: 'Discussion',\n description: \"a community's digest came due on its schedule\",\n },\n {\n event: assetEvents.assetCreated,\n label: 'Upload requested',\n category: 'Assets',\n description: 'an asset upload was requested',\n },\n {\n event: assetEvents.assetReady,\n label: 'Upload ready',\n category: 'Assets',\n description: 'an asset was confirmed ready',\n },\n {\n event: assetEvents.assetDeleted,\n label: 'Asset deleted',\n category: 'Assets',\n description: 'an asset was deleted',\n },\n];\n\nexport function catalogTriggers(): AvailableTriggers['triggers'] {\n return TRIGGERS.map(({ event, ...trigger }) => ({ type: event.type, ...trigger }));\n}\n\nexport function publicDataSchema(type: string): z.ZodType | undefined {\n return TRIGGERS.find((trigger) => trigger.event.type === type)?.event.publicData;\n}\n"],"mappings":";;;;;;;;;;AAgBA,MAAa,yBAAwD;CACnE,WAAW;CACX,eAAe;CACf,cAAc;CACd,eAAe;CACf,iBAAiB;CACjB,mBAAmB;CACnB,eAAe;CACf,eAAe;CACf,iBAAiB;CACjB,oBAAoB;CACpB,sBAAsB;CACtB,iBAAiB;CACjB,gBAAgB;CAChB,sBAAsB;CACtB,oBAAoB;CACpB,kBAAkB;CAClB,oBAAoB;CACpB,uBAAuB;CACvB,iBAAiB;CACjB,iBAAiB;CACjB,gBAAgB;CAChB,gBAAgB;CAChB,YAAY;CACZ,kBAAkB;CAClB,cAAc;CACd,QAAQ;CACR,oBAAoB;CACpB,aAAa;CACb,qBAAqB;CACrB,mBAAmB;CACnB,aAAa;CACb,gBAAgB;CAChB,SAAS;CACT,gBAAgB;CAChB,qBAAqB;CACrB,wBAAwB;CACxB,wBAAwB;CACxB,wBAAwB;CACxB,wBAAwB;CACxB,iBAAiB;CACjB,cAAc;CACd,mBAAmB;CACnB,cAAc;CACd,eAAe;CACf,cAAc;AAChB;AAEA,MAAa,yBAAyB;AAEtC,SAAgB,iBAAmC;CACjD,OAAO,CACL;EACE,MAAM;EACN,aAAa;EACb,aAAa;GACX,MAAM;GACN,UAAU,CAAC,UAAU;GACrB,YAAY,EACV,UAAU,EAAE,MAAM,OAAO,KAAK,sBAAsB,EAAE,EACxD;EACF;EACA,QAAQ;CACV,GACA;EACE,MAAM;EACN,aAAa;EACb,aAAa;GACX,MAAM;GACN,YAAY;IACV,WAAW,EAAE,MAAM,SAAS;IAC5B,UAAU,EAAE,MAAM,SAAS;IAC3B,eAAe,EAAE,MAAM,SAAS;GAClC;EACF;EACA,QAAQ;CACV,CACF;AACF;AASA,MAAM,WAA6B;CACjC;EACE,OAAO,eAAe;EACtB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,eAAe;EACtB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,eAAe;EACtB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,eAAe;EACtB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,YAAY;EACnB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,YAAY;EACnB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,YAAY;EACnB,OAAO;EACP,UAAU;EACV,aAAa;CACf;AACF;AAEA,SAAgB,kBAAiD;CAC/D,OAAO,SAAS,KAAK,EAAE,OAAO,GAAG,eAAe;EAAE,MAAM,MAAM;EAAM,GAAG;CAAQ,EAAE;AACnF;AAEA,SAAgB,iBAAiB,MAAqC;CACpE,OAAO,SAAS,MAAM,YAAY,QAAQ,MAAM,SAAS,IAAI,CAAC,EAAE,MAAM;AACxE"}