@headless-lms/core 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. package/dist/assets/events.d.ts +90 -9
  2. package/dist/assets/events.js +19 -3
  3. package/dist/assets/events.js.map +1 -1
  4. package/dist/assets/file-urls.d.ts +20 -0
  5. package/dist/assets/file-urls.js +25 -0
  6. package/dist/assets/file-urls.js.map +1 -0
  7. package/dist/assets/index.d.ts +5 -4
  8. package/dist/assets/index.js +3 -2
  9. package/dist/assets/model.d.ts +2 -2
  10. package/dist/assets/ports.d.ts +45 -15
  11. package/dist/assets/service.d.ts +39 -7
  12. package/dist/assets/service.js +292 -37
  13. package/dist/assets/service.js.map +1 -1
  14. package/dist/automations/actions.d.ts +12 -15
  15. package/dist/automations/actions.js +188 -30
  16. package/dist/automations/actions.js.map +1 -1
  17. package/dist/automations/catalog.d.ts +5 -1
  18. package/dist/automations/catalog.js +514 -197
  19. package/dist/automations/catalog.js.map +1 -1
  20. package/dist/automations/community-derivations.d.ts +14 -0
  21. package/dist/automations/community-derivations.js +439 -0
  22. package/dist/automations/community-derivations.js.map +1 -0
  23. package/dist/automations/community-mail.d.ts +112 -0
  24. package/dist/automations/community-mail.js +90 -0
  25. package/dist/automations/community-mail.js.map +1 -0
  26. package/dist/automations/community-notifications.d.ts +16 -0
  27. package/dist/automations/community-notifications.js +84 -0
  28. package/dist/automations/community-notifications.js.map +1 -0
  29. package/dist/automations/events.d.ts +74 -24
  30. package/dist/automations/events.js +5 -5
  31. package/dist/automations/events.js.map +1 -1
  32. package/dist/automations/index.d.ts +7 -5
  33. package/dist/automations/index.js +3 -2
  34. package/dist/automations/model.d.ts +20 -3
  35. package/dist/automations/model.js +36 -3
  36. package/dist/automations/model.js.map +1 -1
  37. package/dist/automations/ports.d.ts +27 -8
  38. package/dist/automations/schedule.d.ts +6 -0
  39. package/dist/automations/schedule.js +41 -0
  40. package/dist/automations/schedule.js.map +1 -0
  41. package/dist/automations/seeds.d.ts +17 -0
  42. package/dist/automations/seeds.js +235 -0
  43. package/dist/automations/seeds.js.map +1 -0
  44. package/dist/automations/service.d.ts +41 -6
  45. package/dist/automations/service.js +542 -59
  46. package/dist/automations/service.js.map +1 -1
  47. package/dist/automations/types.d.ts +2 -2
  48. package/dist/automations/webhook-signing.d.ts +7 -0
  49. package/dist/automations/webhook-signing.js +22 -0
  50. package/dist/automations/webhook-signing.js.map +1 -0
  51. package/dist/content/events.d.ts +1013 -25
  52. package/dist/content/events.js +168 -19
  53. package/dist/content/events.js.map +1 -1
  54. package/dist/content/index.d.ts +5 -5
  55. package/dist/content/index.js +2 -2
  56. package/dist/content/model.d.ts +2 -2
  57. package/dist/content/ports.d.ts +109 -25
  58. package/dist/content/service.d.ts +68 -23
  59. package/dist/content/service.js +647 -60
  60. package/dist/content/service.js.map +1 -1
  61. package/dist/content/types.d.ts +2 -2
  62. package/dist/discussion/authors.d.ts +10 -0
  63. package/dist/discussion/authors.js +23 -0
  64. package/dist/discussion/authors.js.map +1 -0
  65. package/dist/discussion/chats.d.ts +104 -0
  66. package/dist/discussion/chats.js +798 -0
  67. package/dist/discussion/chats.js.map +1 -0
  68. package/dist/discussion/events.d.ts +633 -14
  69. package/dist/discussion/events.js +134 -5
  70. package/dist/discussion/events.js.map +1 -1
  71. package/dist/discussion/index.d.ts +7 -3
  72. package/dist/discussion/index.js +4 -1
  73. package/dist/discussion/model.d.ts +1 -2
  74. package/dist/discussion/ports.d.ts +288 -27
  75. package/dist/discussion/ports.js +6 -1
  76. package/dist/discussion/ports.js.map +1 -0
  77. package/dist/discussion/posts.d.ts +103 -0
  78. package/dist/discussion/posts.js +723 -0
  79. package/dist/discussion/posts.js.map +1 -0
  80. package/dist/discussion/service.d.ts +33 -11
  81. package/dist/discussion/service.js +297 -81
  82. package/dist/discussion/service.js.map +1 -1
  83. package/dist/entitlements/events.d.ts +264 -13
  84. package/dist/entitlements/events.js +49 -5
  85. package/dist/entitlements/events.js.map +1 -1
  86. package/dist/entitlements/index.d.ts +5 -4
  87. package/dist/entitlements/index.js +2 -1
  88. package/dist/entitlements/model.d.ts +3 -2
  89. package/dist/entitlements/ports.d.ts +124 -11
  90. package/dist/entitlements/rules.d.ts +12 -0
  91. package/dist/entitlements/rules.js +17 -0
  92. package/dist/entitlements/rules.js.map +1 -0
  93. package/dist/entitlements/service.d.ts +54 -3
  94. package/dist/entitlements/service.js +473 -2
  95. package/dist/entitlements/service.js.map +1 -1
  96. package/dist/identity/access.d.ts +6 -0
  97. package/dist/identity/access.js +7 -0
  98. package/dist/identity/access.js.map +1 -0
  99. package/dist/identity/events.d.ts +17 -3
  100. package/dist/identity/events.js +12 -2
  101. package/dist/identity/events.js.map +1 -1
  102. package/dist/identity/index.d.ts +6 -4
  103. package/dist/identity/index.js +3 -1
  104. package/dist/identity/model.d.ts +2 -1
  105. package/dist/identity/notifications.d.ts +27 -0
  106. package/dist/identity/notifications.js +101 -0
  107. package/dist/identity/notifications.js.map +1 -0
  108. package/dist/identity/ports.d.ts +76 -3
  109. package/dist/identity/service.d.ts +17 -3
  110. package/dist/identity/service.js +73 -5
  111. package/dist/identity/service.js.map +1 -1
  112. package/dist/integrations/events.d.ts +27 -3
  113. package/dist/integrations/events.js +15 -3
  114. package/dist/integrations/events.js.map +1 -1
  115. package/dist/integrations/model.js +1 -1
  116. package/dist/integrations/model.js.map +1 -1
  117. package/dist/integrations/service.js +2 -2
  118. package/dist/integrations/service.js.map +1 -1
  119. package/dist/organizations/access-scopes.d.ts +19 -0
  120. package/dist/organizations/access-scopes.js +89 -0
  121. package/dist/organizations/access-scopes.js.map +1 -0
  122. package/dist/organizations/events.d.ts +234 -25
  123. package/dist/organizations/events.js +58 -9
  124. package/dist/organizations/events.js.map +1 -1
  125. package/dist/organizations/index.d.ts +8 -7
  126. package/dist/organizations/index.js +4 -3
  127. package/dist/organizations/members.d.ts +8 -5
  128. package/dist/organizations/members.js.map +1 -1
  129. package/dist/organizations/ports.d.ts +20 -21
  130. package/dist/organizations/roles.d.ts +6 -10
  131. package/dist/organizations/roles.js +19 -7
  132. package/dist/organizations/roles.js.map +1 -1
  133. package/dist/organizations/service.d.ts +28 -14
  134. package/dist/organizations/service.js +274 -105
  135. package/dist/organizations/service.js.map +1 -1
  136. package/dist/organizations/types.d.ts +2 -11
  137. package/dist/progress/events.d.ts +34 -2
  138. package/dist/progress/events.js +15 -2
  139. package/dist/progress/events.js.map +1 -1
  140. package/dist/progress/index.d.ts +2 -2
  141. package/dist/progress/ports.d.ts +6 -1
  142. package/dist/progress/service.d.ts +5 -3
  143. package/dist/progress/service.js +9 -3
  144. package/dist/progress/service.js.map +1 -1
  145. package/dist/reporting/admin/index.d.ts +4 -0
  146. package/dist/reporting/admin/index.js +2 -0
  147. package/dist/reporting/admin/model.d.ts +108 -0
  148. package/dist/reporting/admin/ports.d.ts +27 -0
  149. package/dist/reporting/admin/service.d.ts +22 -0
  150. package/dist/reporting/admin/service.js +38 -0
  151. package/dist/reporting/admin/service.js.map +1 -0
  152. package/dist/reporting/member/index.d.ts +5 -0
  153. package/dist/reporting/member/index.js +2 -0
  154. package/dist/reporting/member/model.d.ts +124 -0
  155. package/dist/reporting/member/ports.d.ts +45 -0
  156. package/dist/reporting/member/service.d.ts +73 -0
  157. package/dist/reporting/member/service.js +286 -0
  158. package/dist/reporting/member/service.js.map +1 -0
  159. package/dist/shared/actor.d.ts +9 -0
  160. package/dist/shared/errors.d.ts +7 -1
  161. package/dist/shared/errors.js +10 -1
  162. package/dist/shared/errors.js.map +1 -1
  163. package/dist/shared/events.d.ts +5 -3
  164. package/dist/shared/events.js +1 -0
  165. package/dist/shared/events.js.map +1 -1
  166. package/dist/shared/id.d.ts +12 -0
  167. package/dist/shared/id.js +13 -1
  168. package/dist/shared/id.js.map +1 -1
  169. package/dist/shared/mailer.d.ts +6 -1
  170. package/dist/shared/mailer.js.map +1 -1
  171. package/dist/shared/network.d.ts +10 -0
  172. package/dist/shared/network.js +211 -0
  173. package/dist/shared/network.js.map +1 -0
  174. package/dist/shared/settings.js.map +1 -1
  175. package/dist/types/assets.d.ts +2 -2
  176. package/dist/types/automations.d.ts +21 -2
  177. package/dist/types/content.d.ts +2 -2
  178. package/dist/types/discussion.d.ts +12 -3
  179. package/dist/types/email-templates.d.ts +124 -1
  180. package/dist/types/entitlements.d.ts +2 -2
  181. package/dist/types/events.d.ts +3 -3
  182. package/dist/types/identity.d.ts +48 -2
  183. package/dist/types/index.d.ts +10 -10
  184. package/dist/types/organizations.d.ts +2 -2
  185. package/dist/types/ports.d.ts +7 -1
  186. package/dist/types/schemas/assets.d.ts +67 -36
  187. package/dist/types/schemas/assets.js +19 -13
  188. package/dist/types/schemas/assets.js.map +1 -1
  189. package/dist/types/schemas/automations.d.ts +88 -4
  190. package/dist/types/schemas/automations.js +46 -6
  191. package/dist/types/schemas/automations.js.map +1 -1
  192. package/dist/types/schemas/content.d.ts +385 -20
  193. package/dist/types/schemas/content.js +178 -15
  194. package/dist/types/schemas/content.js.map +1 -1
  195. package/dist/types/schemas/credentials.js.map +1 -1
  196. package/dist/types/schemas/discussion.d.ts +379 -4
  197. package/dist/types/schemas/discussion.js +209 -7
  198. package/dist/types/schemas/discussion.js.map +1 -1
  199. package/dist/types/schemas/entitlements.d.ts +44 -4
  200. package/dist/types/schemas/entitlements.js +32 -4
  201. package/dist/types/schemas/entitlements.js.map +1 -1
  202. package/dist/types/schemas/identity.d.ts +115 -5
  203. package/dist/types/schemas/identity.js +59 -6
  204. package/dist/types/schemas/identity.js.map +1 -1
  205. package/dist/types/schemas/index.d.ts +9 -9
  206. package/dist/types/schemas/index.js +9 -9
  207. package/dist/types/schemas/integrations.js.map +1 -1
  208. package/dist/types/schemas/organizations.d.ts +27 -5
  209. package/dist/types/schemas/organizations.js +14 -7
  210. package/dist/types/schemas/organizations.js.map +1 -1
  211. package/dist/types/schemas/outbox.d.ts +1 -0
  212. package/dist/types/schemas/outbox.js +1 -0
  213. package/dist/types/schemas/outbox.js.map +1 -1
  214. package/dist/types/schemas/progress.d.ts +1 -0
  215. package/dist/types/schemas/progress.js +1 -0
  216. package/dist/types/schemas/progress.js.map +1 -1
  217. package/dist/types/schemas/settings.js.map +1 -1
  218. package/dist/types/schemas/shared.d.ts +4 -1
  219. package/dist/types/schemas/shared.js +7 -1
  220. package/dist/types/schemas/shared.js.map +1 -1
  221. package/dist/types/shared.d.ts +1 -1
  222. package/package.json +2 -2
  223. package/dist/reporting/dashboard/index.d.ts +0 -4
  224. package/dist/reporting/dashboard/index.js +0 -2
  225. package/dist/reporting/dashboard/model.d.ts +0 -14
  226. package/dist/reporting/dashboard/ports.d.ts +0 -13
  227. package/dist/reporting/dashboard/service.d.ts +0 -19
  228. package/dist/reporting/dashboard/service.js +0 -20
  229. package/dist/reporting/dashboard/service.js.map +0 -1
  230. package/dist/reporting/learn/index.d.ts +0 -5
  231. package/dist/reporting/learn/index.js +0 -2
  232. package/dist/reporting/learn/model.d.ts +0 -23
  233. package/dist/reporting/learn/ports.d.ts +0 -42
  234. package/dist/reporting/learn/service.d.ts +0 -48
  235. package/dist/reporting/learn/service.js +0 -111
  236. package/dist/reporting/learn/service.js.map +0 -1
  237. package/dist/reporting/students/index.d.ts +0 -4
  238. package/dist/reporting/students/index.js +0 -2
  239. package/dist/reporting/students/model.d.ts +0 -31
  240. package/dist/reporting/students/ports.d.ts +0 -13
  241. package/dist/reporting/students/ports.js +0 -1
  242. package/dist/reporting/students/service.d.ts +0 -19
  243. package/dist/reporting/students/service.js +0 -20
  244. package/dist/reporting/students/service.js.map +0 -1
  245. /package/dist/reporting/{dashboard → admin}/model.js +0 -0
  246. /package/dist/reporting/{dashboard → admin}/ports.js +0 -0
  247. /package/dist/reporting/{learn → member}/model.js +0 -0
  248. /package/dist/reporting/{learn → member}/ports.js +0 -0
  249. /package/dist/{reporting/students/model.js → shared/actor.js} +0 -0
@@ -1,57 +1,215 @@
1
+ import { notificationKindSchema } from "../types/schemas/identity.js";
2
+ import { deliverWebhookInputSchema } from "../types/schemas/automations.js";
3
+ import { noopLogger } from "../shared/logger.js";
1
4
  import { entitlementEvents } from "../entitlements/events.js";
2
- import { ALL_EMAIL_TEMPLATE_IDS } from "./catalog.js";
5
+ import { ActionFailedError } from "./model.js";
6
+ import { ALL_EMAIL_TEMPLATE_IDS, DELIVER_WEBHOOK_ACTION, NOTIFY_ACTION, publicDataSchema } from "./catalog.js";
7
+ import { webhookHeaders } from "./webhook-signing.js";
8
+ import { address, firedAtOf } from "./community-mail.js";
9
+ import { COMMUNITY_DERIVATIONS } from "./community-derivations.js";
10
+ import { NOTIFICATION_PLACEMENTS } from "./community-notifications.js";
3
11
  //#region src/automations/actions.ts
4
12
  const SEND_EMAIL_DERIVATIONS = {
5
13
  accessGranted: {
6
- trigger: "entitlement.created",
7
- derive: async (event, lookups) => {
14
+ triggers: ["entitlement.created"],
15
+ derive: async (event, deps) => {
8
16
  const result = entitlementEvents.entitlementCreated.safeParse(event);
9
17
  if (!result.success) return;
10
18
  const entitlement = result.data.data;
11
- const [to, content] = await Promise.all([lookups.orgUserEmail(entitlement.orgId, entitlement.orgUserId), lookups.contentInfo(entitlement.orgId, entitlement.contentId)]);
12
- if (!to || !content) return;
13
- return {
14
- to,
15
- params: {
16
- contentTitle: content.title,
17
- contentId: content.id
18
- }
19
- };
19
+ if (!entitlement.contentId) return [];
20
+ const content = await deps.lookups.contentInfo(entitlement.orgId, entitlement.contentId);
21
+ if (!content) return;
22
+ return address(deps, event, entitlement.orgId, [entitlement.orgUserId], {
23
+ contentTitle: content.title,
24
+ contentId: content.id,
25
+ contentType: content.type
26
+ });
20
27
  }
21
28
  },
22
29
  accessRevoked: {
23
- trigger: "entitlement.deleted",
24
- derive: async (event, lookups) => {
30
+ triggers: ["entitlement.deleted"],
31
+ derive: async (event, deps) => {
25
32
  const result = entitlementEvents.entitlementDeleted.safeParse(event);
26
33
  if (!result.success) return;
27
34
  const entitlement = result.data.data;
28
- const [to, content] = await Promise.all([lookups.orgUserEmail(entitlement.orgId, entitlement.orgUserId), lookups.contentInfo(entitlement.orgId, entitlement.contentId)]);
29
- if (!to || !content) return;
30
- return {
31
- to,
32
- params: { contentTitle: content.title }
33
- };
35
+ if (!entitlement.contentId) return [];
36
+ const content = await deps.lookups.contentInfo(entitlement.orgId, entitlement.contentId);
37
+ if (!content) return;
38
+ return address(deps, event, entitlement.orgId, [entitlement.orgUserId], { contentTitle: content.title });
34
39
  }
35
- }
40
+ },
41
+ ...COMMUNITY_DERIVATIONS
36
42
  };
43
+ function reachedNoOne() {
44
+ return {
45
+ recipients: [],
46
+ failed: []
47
+ };
48
+ }
49
+ function assertSomeDelivered(action, outcome) {
50
+ if (outcome.recipients.length === 0 && outcome.failed.length > 0) throw new ActionFailedError(`${action}: delivery failed for all ${outcome.failed.length} recipients`);
51
+ return outcome;
52
+ }
37
53
  function isEmailTemplateId(value) {
38
54
  return typeof value === "string" && value in ALL_EMAIL_TEMPLATE_IDS;
39
55
  }
40
- /** Throws on any failure — the engine owns retry policy and failure bookkeeping. */
41
- async function executeAction(action, event, mailer, lookups) {
56
+ /** Throws on any failure but a partial fan-out — the engine owns retry policy and failure bookkeeping. */
57
+ async function executeAction(action, event, deps) {
42
58
  switch (action.type) {
43
59
  case "sendEmail": {
44
60
  const template = action.input["template"];
45
- if (!isEmailTemplateId(template)) throw new Error(`sendEmail: unknown template "${String(template)}"`);
61
+ if (!isEmailTemplateId(template)) throw new ActionFailedError(`sendEmail: unknown template "${String(template)}"`);
46
62
  const derivation = SEND_EMAIL_DERIVATIONS[template];
47
- if (!derivation || derivation.trigger !== event.type) throw new Error(`sendEmail: template "${template}" cannot be derived from event "${event.type}"`);
48
- const derived = await derivation.derive(event, lookups);
49
- if (!derived) throw new Error(`sendEmail: event "${event.type}" is missing the data required to derive template "${template}"`);
50
- await mailer.send(derived.to, template, derived.params);
51
- return;
63
+ if (!derivation || !derivation.triggers.includes(event.type)) throw new ActionFailedError(`sendEmail: template "${template}" cannot be derived from event "${event.type}"`);
64
+ const derived = await derivation.derive(event, {
65
+ ...deps,
66
+ actionInput: action.input
67
+ });
68
+ if (!derived) throw new ActionFailedError(`sendEmail: event "${event.type}" is missing the data required to derive template "${template}"`);
69
+ const outcome = reachedNoOne();
70
+ for (const recipient of derived) (await send(template, recipient, event, deps) ? outcome.recipients : outcome.failed).push(recipient.orgUserId);
71
+ return assertSomeDelivered("sendEmail", outcome);
72
+ }
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
+ case "grantAccess":
82
+ await grantAccess(action, event, deps);
83
+ 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
+ case DELIVER_WEBHOOK_ACTION:
97
+ await deliverWebhook(action, event, deps);
98
+ return reachedNoOne();
99
+ default: throw new ActionFailedError(`unknown automation action type "${action.type}"`);
100
+ }
101
+ }
102
+ async function send(template, recipient, event, deps) {
103
+ try {
104
+ await deps.mailer.send(recipient.to, template, recipient.params);
105
+ return true;
106
+ } catch (err) {
107
+ (deps.logger ?? noopLogger).warn("automation email not delivered", {
108
+ orgId: event.orgId,
109
+ automationId: deps.automationId,
110
+ trigger: event.type,
111
+ template,
112
+ orgUserId: recipient.orgUserId,
113
+ err
114
+ });
115
+ return false;
116
+ }
117
+ }
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
+ });
52
150
  }
53
- default: throw new Error(`unknown automation action type "${action.type}"`);
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
+ function grantRecipient(event) {
158
+ if (typeof event.data !== "object" || event.data === null) return;
159
+ const data = event.data;
160
+ if (event.type === "organization.student.created" && typeof data.id === "string") return data.id;
161
+ if (typeof data.orgUserId === "string") return data.orgUserId;
162
+ }
163
+ async function grantAccess(action, event, deps) {
164
+ const contentId = typeof action.input.contentId === "string" ? action.input.contentId : null;
165
+ const bundleId = typeof action.input.bundleId === "string" ? action.input.bundleId : null;
166
+ if (contentId == null === (bundleId == null)) throw new ActionFailedError("grantAccess: provide exactly one of contentId or bundleId");
167
+ const orgUserId = grantRecipient(event);
168
+ if (!orgUserId) throw new ActionFailedError(`grantAccess: event "${event.type}" has no recipient`);
169
+ const days = action.input.expiresInDays;
170
+ const expiresAt = typeof days === "number" && Number.isFinite(days) ? new Date(Date.now() + days * 864e5) : null;
171
+ await deps.grants.grant(event.orgId, {
172
+ orgUserId,
173
+ contentId,
174
+ bundleId,
175
+ expiresAt
176
+ });
177
+ }
178
+ async function deliverWebhook(action, event, deps) {
179
+ const input = deliverWebhookInputSchema.safeParse(action.input);
180
+ if (!input.success) throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: invalid input`);
181
+ const publicData = publicDataSchema(event.type);
182
+ if (!publicData) throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: event "${event.type}" is not published`);
183
+ const data = publicData.safeParse(event.data);
184
+ if (!data.success) throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: event "${event.type}" does not match its published shape`);
185
+ const secret = (await deps.credentials.reveal(event.orgId, input.data.secretRef))?.["secret"];
186
+ if (typeof secret !== "string") throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: signing secret not found`);
187
+ const body = JSON.stringify({
188
+ id: event.id,
189
+ type: event.type,
190
+ version: event.version,
191
+ orgId: event.orgId,
192
+ occurredAt: event.occurredAt,
193
+ data: data.data
194
+ });
195
+ const timestamp = Math.floor(Date.now() / 1e3);
196
+ let response;
197
+ try {
198
+ response = await deps.webhooks.send({
199
+ url: input.data.url,
200
+ headers: webhookHeaders(secret, event.id, timestamp, body),
201
+ body
202
+ });
203
+ } catch (err) {
204
+ (deps.logger ?? noopLogger).warn("webhook endpoint not reached", {
205
+ orgId: event.orgId,
206
+ automationId: deps.automationId,
207
+ eventId: event.id,
208
+ err
209
+ });
210
+ throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: the endpoint could not be reached`);
54
211
  }
212
+ if (response.status < 200 || response.status >= 300) throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: endpoint responded ${response.status}`);
55
213
  }
56
214
  //#endregion
57
215
  export { SEND_EMAIL_DERIVATIONS, executeAction };
@@ -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, EmailTemplateParams } from '../types/index.js';\nimport { entitlementEvents } from '../entitlements/index.js';\nimport type { Mailer, MailerLookups } from '../shared/mailer.js';\nimport type { DomainEvent } from '../shared/ports.js';\nimport type { AutomationAction } from './model.js';\nimport { ALL_EMAIL_TEMPLATE_IDS } from './catalog.js';\n\ninterface SendEmailDerivation<K extends EmailTemplateId> {\n /** The only event type this template's params can be derived from. */\n trigger: string;\n /** Undefined = the recipient or content behind the event's ids cannot be resolved. */\n derive(\n event: DomainEvent,\n lookups: MailerLookups,\n ): Promise<{ to: string; params: EmailTemplateParams[K] } | undefined>;\n}\n\ntype SendEmailDerivations = { [K in EmailTemplateId]?: SendEmailDerivation<K> };\n\nexport const SEND_EMAIL_DERIVATIONS: SendEmailDerivations = {\n accessGranted: {\n trigger: 'entitlement.created',\n derive: async (event, lookups) => {\n const result = entitlementEvents.entitlementCreated.safeParse(event);\n if (!result.success) {\n return undefined;\n }\n const entitlement = result.data.data;\n const [to, content] = await Promise.all([\n lookups.orgUserEmail(entitlement.orgId, entitlement.orgUserId),\n lookups.contentInfo(entitlement.orgId, entitlement.contentId),\n ]);\n if (!to || !content) {\n return undefined;\n }\n return {\n to,\n params: { contentTitle: content.title, contentId: content.id },\n };\n },\n },\n accessRevoked: {\n trigger: 'entitlement.deleted',\n derive: async (event, lookups) => {\n const result = entitlementEvents.entitlementDeleted.safeParse(event);\n if (!result.success) {\n return undefined;\n }\n const entitlement = result.data.data;\n const [to, content] = await Promise.all([\n lookups.orgUserEmail(entitlement.orgId, entitlement.orgUserId),\n lookups.contentInfo(entitlement.orgId, entitlement.contentId),\n ]);\n if (!to || !content) {\n return undefined;\n }\n return {\n to,\n params: { contentTitle: content.title },\n };\n },\n },\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 — the engine owns retry policy and failure bookkeeping. */\nexport async function executeAction(\n action: AutomationAction,\n event: DomainEvent,\n mailer: Pick<Mailer, 'send'>,\n lookups: MailerLookups,\n): Promise<void> {\n switch (action.type) {\n case 'sendEmail': {\n const template = action.input['template'];\n if (!isEmailTemplateId(template)) {\n throw new Error(`sendEmail: unknown template \"${String(template)}\"`);\n }\n const derivation = SEND_EMAIL_DERIVATIONS[template];\n if (!derivation || derivation.trigger !== event.type) {\n throw new Error(\n `sendEmail: template \"${template}\" cannot be derived from event \"${event.type}\"`,\n );\n }\n const derived = await derivation.derive(event, lookups);\n if (!derived) {\n throw new Error(\n `sendEmail: event \"${event.type}\" is missing the data required to derive template \"${template}\"`,\n );\n }\n await mailer.send(derived.to, template, derived.params);\n return;\n }\n default: {\n throw new Error(`unknown automation action type \"${action.type}\"`);\n }\n }\n}\n"],"mappings":";;;AA0BA,MAAa,yBAA+C;CAC1D,eAAe;EACb,SAAS;EACT,QAAQ,OAAO,OAAO,YAAY;GAChC,MAAM,SAAS,kBAAkB,mBAAmB,UAAU,KAAK;GACnE,IAAI,CAAC,OAAO,SACV;GAEF,MAAM,cAAc,OAAO,KAAK;GAChC,MAAM,CAAC,IAAI,WAAW,MAAM,QAAQ,IAAI,CACtC,QAAQ,aAAa,YAAY,OAAO,YAAY,SAAS,GAC7D,QAAQ,YAAY,YAAY,OAAO,YAAY,SAAS,CAC9D,CAAC;GACD,IAAI,CAAC,MAAM,CAAC,SACV;GAEF,OAAO;IACL;IACA,QAAQ;KAAE,cAAc,QAAQ;KAAO,WAAW,QAAQ;IAAG;GAC/D;EACF;CACF;CACA,eAAe;EACb,SAAS;EACT,QAAQ,OAAO,OAAO,YAAY;GAChC,MAAM,SAAS,kBAAkB,mBAAmB,UAAU,KAAK;GACnE,IAAI,CAAC,OAAO,SACV;GAEF,MAAM,cAAc,OAAO,KAAK;GAChC,MAAM,CAAC,IAAI,WAAW,MAAM,QAAQ,IAAI,CACtC,QAAQ,aAAa,YAAY,OAAO,YAAY,SAAS,GAC7D,QAAQ,YAAY,YAAY,OAAO,YAAY,SAAS,CAC9D,CAAC;GACD,IAAI,CAAC,MAAM,CAAC,SACV;GAEF,OAAO;IACL;IACA,QAAQ,EAAE,cAAc,QAAQ,MAAM;GACxC;EACF;CACF;AACF;AAEA,SAAS,kBAAkB,OAA0C;CACnE,OAAO,OAAO,UAAU,YAAY,SAAS;AAC/C;;AAGA,eAAsB,cACpB,QACA,OACA,QACA,SACe;CACf,QAAQ,OAAO,MAAf;EACE,KAAK,aAAa;GAChB,MAAM,WAAW,OAAO,MAAM;GAC9B,IAAI,CAAC,kBAAkB,QAAQ,GAC7B,MAAM,IAAI,MAAM,gCAAgC,OAAO,QAAQ,EAAE,EAAE;GAErE,MAAM,aAAa,uBAAuB;GAC1C,IAAI,CAAC,cAAc,WAAW,YAAY,MAAM,MAC9C,MAAM,IAAI,MACR,wBAAwB,SAAS,kCAAkC,MAAM,KAAK,EAChF;GAEF,MAAM,UAAU,MAAM,WAAW,OAAO,OAAO,OAAO;GACtD,IAAI,CAAC,SACH,MAAM,IAAI,MACR,qBAAqB,MAAM,KAAK,qDAAqD,SAAS,EAChG;GAEF,MAAM,OAAO,KAAK,QAAQ,IAAI,UAAU,QAAQ,MAAM;GACtD;EACF;EACA,SACE,MAAM,IAAI,MAAM,mCAAmC,OAAO,KAAK,EAAE;CAErE;AACF"}
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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"}
@@ -2,11 +2,15 @@ import { EmailTemplateId } from "../types/email-templates.js";
2
2
  import { AvailableActions, AvailableTriggers } from "../types/schemas/automations.js";
3
3
  import "../types/index.js";
4
4
  import "./types.js";
5
+ import { z } from "zod";
5
6
  //#region src/automations/catalog.d.ts
6
7
  /** Every EmailTemplateId — a missing key here is a compile error, kept exhaustive by construction. */
7
8
  declare const ALL_EMAIL_TEMPLATE_IDS: Record<EmailTemplateId, true>;
9
+ declare const DELIVER_WEBHOOK_ACTION = "deliverWebhook";
10
+ declare const NOTIFY_ACTION = "notify";
8
11
  declare function catalogActions(): AvailableActions;
9
12
  declare function catalogTriggers(): AvailableTriggers['triggers'];
13
+ declare function publicDataSchema(type: string): z.ZodType | undefined;
10
14
  //#endregion
11
- export { ALL_EMAIL_TEMPLATE_IDS, catalogActions, catalogTriggers };
15
+ export { ALL_EMAIL_TEMPLATE_IDS, DELIVER_WEBHOOK_ACTION, NOTIFY_ACTION, catalogActions, catalogTriggers, publicDataSchema };
12
16
  //# sourceMappingURL=catalog.d.ts.map