@headless-lms/core 0.2.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 (225) hide show
  1. package/dist/assets/events.d.ts +39 -21
  2. package/dist/assets/events.js +1 -1
  3. package/dist/assets/events.js.map +1 -1
  4. package/dist/assets/file-urls.d.ts +20 -0
  5. package/dist/assets/file-urls.js +25 -0
  6. package/dist/assets/file-urls.js.map +1 -0
  7. package/dist/assets/index.d.ts +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 +291 -38
  13. package/dist/assets/service.js.map +1 -1
  14. package/dist/automations/actions.d.ts +13 -24
  15. package/dist/automations/actions.js +168 -43
  16. package/dist/automations/actions.js.map +1 -1
  17. package/dist/automations/catalog.d.ts +2 -1
  18. package/dist/automations/catalog.js +229 -13
  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 +28 -0
  30. package/dist/automations/index.d.ts +4 -2
  31. package/dist/automations/index.js +3 -2
  32. package/dist/automations/model.d.ts +10 -3
  33. package/dist/automations/model.js +15 -2
  34. package/dist/automations/model.js.map +1 -1
  35. package/dist/automations/ports.d.ts +5 -1
  36. package/dist/automations/schedule.d.ts +6 -0
  37. package/dist/automations/schedule.js +41 -0
  38. package/dist/automations/schedule.js.map +1 -0
  39. package/dist/automations/seeds.d.ts +17 -0
  40. package/dist/automations/seeds.js +235 -0
  41. package/dist/automations/seeds.js.map +1 -0
  42. package/dist/automations/service.d.ts +22 -3
  43. package/dist/automations/service.js +306 -43
  44. package/dist/automations/service.js.map +1 -1
  45. package/dist/content/events.d.ts +631 -19
  46. package/dist/content/events.js +44 -1
  47. package/dist/content/events.js.map +1 -1
  48. package/dist/content/index.d.ts +4 -4
  49. package/dist/content/model.d.ts +2 -2
  50. package/dist/content/ports.d.ts +71 -18
  51. package/dist/content/service.d.ts +57 -18
  52. package/dist/content/service.js +502 -23
  53. package/dist/content/service.js.map +1 -1
  54. package/dist/content/types.d.ts +2 -2
  55. package/dist/discussion/authors.d.ts +10 -0
  56. package/dist/discussion/authors.js +23 -0
  57. package/dist/discussion/authors.js.map +1 -0
  58. package/dist/discussion/chats.d.ts +104 -0
  59. package/dist/discussion/chats.js +798 -0
  60. package/dist/discussion/chats.js.map +1 -0
  61. package/dist/discussion/events.d.ts +619 -56
  62. package/dist/discussion/events.js +106 -3
  63. package/dist/discussion/events.js.map +1 -1
  64. package/dist/discussion/index.d.ts +7 -3
  65. package/dist/discussion/index.js +4 -1
  66. package/dist/discussion/ports.d.ts +288 -27
  67. package/dist/discussion/ports.js +6 -1
  68. package/dist/discussion/ports.js.map +1 -0
  69. package/dist/discussion/posts.d.ts +103 -0
  70. package/dist/discussion/posts.js +723 -0
  71. package/dist/discussion/posts.js.map +1 -0
  72. package/dist/discussion/service.d.ts +32 -10
  73. package/dist/discussion/service.js +293 -78
  74. package/dist/discussion/service.js.map +1 -1
  75. package/dist/entitlements/events.d.ts +178 -11
  76. package/dist/entitlements/events.js +28 -1
  77. package/dist/entitlements/events.js.map +1 -1
  78. package/dist/entitlements/index.d.ts +5 -4
  79. package/dist/entitlements/index.js +2 -1
  80. package/dist/entitlements/model.d.ts +3 -2
  81. package/dist/entitlements/ports.d.ts +124 -11
  82. package/dist/entitlements/rules.d.ts +12 -0
  83. package/dist/entitlements/rules.js +17 -0
  84. package/dist/entitlements/rules.js.map +1 -0
  85. package/dist/entitlements/service.d.ts +54 -3
  86. package/dist/entitlements/service.js +472 -2
  87. package/dist/entitlements/service.js.map +1 -1
  88. package/dist/identity/access.d.ts +6 -0
  89. package/dist/identity/access.js +7 -0
  90. package/dist/identity/access.js.map +1 -0
  91. package/dist/identity/index.d.ts +6 -4
  92. package/dist/identity/index.js +3 -1
  93. package/dist/identity/model.d.ts +2 -1
  94. package/dist/identity/notifications.d.ts +27 -0
  95. package/dist/identity/notifications.js +101 -0
  96. package/dist/identity/notifications.js.map +1 -0
  97. package/dist/identity/ports.d.ts +73 -3
  98. package/dist/identity/service.d.ts +14 -3
  99. package/dist/identity/service.js +62 -5
  100. package/dist/identity/service.js.map +1 -1
  101. package/dist/integrations/model.js +1 -1
  102. package/dist/integrations/model.js.map +1 -1
  103. package/dist/integrations/service.js +2 -2
  104. package/dist/integrations/service.js.map +1 -1
  105. package/dist/organizations/access-scopes.d.ts +19 -0
  106. package/dist/organizations/access-scopes.js +89 -0
  107. package/dist/organizations/access-scopes.js.map +1 -0
  108. package/dist/organizations/events.d.ts +74 -32
  109. package/dist/organizations/events.js +6 -0
  110. package/dist/organizations/events.js.map +1 -1
  111. package/dist/organizations/index.d.ts +7 -6
  112. package/dist/organizations/index.js +4 -3
  113. package/dist/organizations/members.d.ts +8 -5
  114. package/dist/organizations/members.js.map +1 -1
  115. package/dist/organizations/ports.d.ts +14 -11
  116. package/dist/organizations/roles.d.ts +6 -10
  117. package/dist/organizations/roles.js +19 -7
  118. package/dist/organizations/roles.js.map +1 -1
  119. package/dist/organizations/service.d.ts +20 -11
  120. package/dist/organizations/service.js +157 -47
  121. package/dist/organizations/service.js.map +1 -1
  122. package/dist/organizations/types.d.ts +1 -10
  123. package/dist/progress/index.d.ts +2 -2
  124. package/dist/progress/ports.d.ts +6 -1
  125. package/dist/progress/service.d.ts +3 -1
  126. package/dist/progress/service.js +6 -2
  127. package/dist/progress/service.js.map +1 -1
  128. package/dist/reporting/admin/index.d.ts +4 -0
  129. package/dist/reporting/admin/index.js +2 -0
  130. package/dist/reporting/admin/model.d.ts +108 -0
  131. package/dist/reporting/admin/ports.d.ts +27 -0
  132. package/dist/reporting/admin/service.d.ts +22 -0
  133. package/dist/reporting/admin/service.js +38 -0
  134. package/dist/reporting/admin/service.js.map +1 -0
  135. package/dist/reporting/member/index.d.ts +5 -0
  136. package/dist/reporting/member/index.js +2 -0
  137. package/dist/reporting/member/model.d.ts +124 -0
  138. package/dist/reporting/member/ports.d.ts +45 -0
  139. package/dist/reporting/member/service.d.ts +73 -0
  140. package/dist/reporting/member/service.js +286 -0
  141. package/dist/reporting/member/service.js.map +1 -0
  142. package/dist/shared/actor.d.ts +9 -0
  143. package/dist/shared/errors.d.ts +7 -1
  144. package/dist/shared/errors.js +10 -1
  145. package/dist/shared/errors.js.map +1 -1
  146. package/dist/shared/id.d.ts +11 -0
  147. package/dist/shared/id.js +12 -1
  148. package/dist/shared/id.js.map +1 -1
  149. package/dist/shared/mailer.d.ts +4 -1
  150. package/dist/shared/mailer.js.map +1 -1
  151. package/dist/shared/network.d.ts +10 -0
  152. package/dist/shared/network.js +211 -0
  153. package/dist/shared/network.js.map +1 -0
  154. package/dist/types/assets.d.ts +2 -2
  155. package/dist/types/automations.d.ts +10 -2
  156. package/dist/types/content.d.ts +2 -2
  157. package/dist/types/discussion.d.ts +11 -3
  158. package/dist/types/email-templates.d.ts +121 -0
  159. package/dist/types/entitlements.d.ts +2 -2
  160. package/dist/types/identity.d.ts +48 -2
  161. package/dist/types/index.d.ts +9 -9
  162. package/dist/types/ports.d.ts +6 -0
  163. package/dist/types/schemas/assets.d.ts +65 -37
  164. package/dist/types/schemas/assets.js +17 -12
  165. package/dist/types/schemas/assets.js.map +1 -1
  166. package/dist/types/schemas/automations.d.ts +18 -1
  167. package/dist/types/schemas/automations.js +15 -6
  168. package/dist/types/schemas/automations.js.map +1 -1
  169. package/dist/types/schemas/content.d.ts +335 -3
  170. package/dist/types/schemas/content.js +139 -3
  171. package/dist/types/schemas/content.js.map +1 -1
  172. package/dist/types/schemas/discussion.d.ts +370 -3
  173. package/dist/types/schemas/discussion.js +199 -4
  174. package/dist/types/schemas/discussion.js.map +1 -1
  175. package/dist/types/schemas/entitlements.d.ts +36 -2
  176. package/dist/types/schemas/entitlements.js +23 -1
  177. package/dist/types/schemas/entitlements.js.map +1 -1
  178. package/dist/types/schemas/identity.d.ts +111 -1
  179. package/dist/types/schemas/identity.js +55 -2
  180. package/dist/types/schemas/identity.js.map +1 -1
  181. package/dist/types/schemas/index.d.ts +8 -8
  182. package/dist/types/schemas/index.js +8 -8
  183. package/dist/types/schemas/organizations.d.ts +8 -0
  184. package/dist/types/schemas/organizations.js +2 -0
  185. package/dist/types/schemas/organizations.js.map +1 -1
  186. package/dist/types/schemas/shared.d.ts +3 -1
  187. package/dist/types/schemas/shared.js +5 -1
  188. package/dist/types/schemas/shared.js.map +1 -1
  189. package/package.json +2 -2
  190. package/dist/reporting/courses/index.d.ts +0 -4
  191. package/dist/reporting/courses/index.js +0 -2
  192. package/dist/reporting/courses/model.d.ts +0 -24
  193. package/dist/reporting/courses/ports.d.ts +0 -13
  194. package/dist/reporting/courses/service.d.ts +0 -19
  195. package/dist/reporting/courses/service.js +0 -20
  196. package/dist/reporting/courses/service.js.map +0 -1
  197. package/dist/reporting/dashboard/index.d.ts +0 -4
  198. package/dist/reporting/dashboard/index.js +0 -2
  199. package/dist/reporting/dashboard/model.d.ts +0 -14
  200. package/dist/reporting/dashboard/ports.d.ts +0 -13
  201. package/dist/reporting/dashboard/service.d.ts +0 -19
  202. package/dist/reporting/dashboard/service.js +0 -20
  203. package/dist/reporting/dashboard/service.js.map +0 -1
  204. package/dist/reporting/learn/index.d.ts +0 -5
  205. package/dist/reporting/learn/index.js +0 -2
  206. package/dist/reporting/learn/model.d.ts +0 -23
  207. package/dist/reporting/learn/ports.d.ts +0 -43
  208. package/dist/reporting/learn/ports.js +0 -1
  209. package/dist/reporting/learn/service.d.ts +0 -49
  210. package/dist/reporting/learn/service.js +0 -116
  211. package/dist/reporting/learn/service.js.map +0 -1
  212. package/dist/reporting/students/index.d.ts +0 -4
  213. package/dist/reporting/students/index.js +0 -2
  214. package/dist/reporting/students/model.d.ts +0 -51
  215. package/dist/reporting/students/model.js +0 -1
  216. package/dist/reporting/students/ports.d.ts +0 -15
  217. package/dist/reporting/students/ports.js +0 -1
  218. package/dist/reporting/students/service.d.ts +0 -20
  219. package/dist/reporting/students/service.js +0 -23
  220. package/dist/reporting/students/service.js.map +0 -1
  221. /package/dist/reporting/{courses → admin}/model.js +0 -0
  222. /package/dist/reporting/{courses → admin}/ports.js +0 -0
  223. /package/dist/reporting/{dashboard → member}/model.js +0 -0
  224. /package/dist/reporting/{dashboard → member}/ports.js +0 -0
  225. /package/dist/{reporting/learn/model.js → shared/actor.js} +0 -0
@@ -0,0 +1,112 @@
1
+ import { DomainEvent, JsonValue } from "../types/shared.js";
2
+ import { Logger } from "../types/ports.js";
3
+ import { EmailTemplateId, EmailTemplateParams } from "../types/email-templates.js";
4
+ import { GrantEntitlementInput } from "../types/schemas/entitlements.js";
5
+ import { WebhookSender } from "../types/automations.js";
6
+ import { ChatStatus } from "../types/schemas/discussion.js";
7
+ import "../types/index.js";
8
+ import { CredentialStore } from "../shared/ports.js";
9
+ import { NotificationDesk } from "../identity/ports.js";
10
+ import { Mailer, MailerLookups } from "../shared/mailer.js";
11
+ import "../identity/index.js";
12
+ import "../entitlements/index.js";
13
+ import "./ports.js";
14
+ //#region src/automations/community-mail.d.ts
15
+ declare const MESSAGE_EXCERPT = 140;
16
+ declare const POST_EXCERPT = 280;
17
+ declare const MESSAGE_BATCH_MS: number;
18
+ interface EmailRecipient<K extends EmailTemplateId = EmailTemplateId> {
19
+ to: string;
20
+ orgUserId: string;
21
+ userId: string;
22
+ params: EmailTemplateParams[K];
23
+ }
24
+ interface CommunityMailReads {
25
+ communityTitle(orgId: string, communityId: string): Promise<string | null>;
26
+ space(orgId: string, spaceId: string): Promise<{
27
+ name: string;
28
+ communityId: string;
29
+ capacity: number | null;
30
+ } | null>;
31
+ openSpaceNames(orgId: string, orgUserId: string, communityId: string): Promise<string[]>;
32
+ communitiesOpenedBy(orgId: string, grant: {
33
+ contentId: string | null;
34
+ bundleId: string | null;
35
+ }): Promise<string[]>;
36
+ communityMembersAmong(orgId: string, communityId: string, ids: string[]): Promise<string[]>;
37
+ spaceOpenersAmong(orgId: string, spaceId: string, ids: string[]): Promise<string[]>;
38
+ spaceOpeners(orgId: string, spaceId: string): Promise<string[]>;
39
+ moderationAudience(orgId: string, communityId: string): Promise<string[]>;
40
+ staffIds(orgId: string): Promise<string[]>;
41
+ displayName(orgId: string, orgUserId: string): Promise<string>;
42
+ followerIds(orgId: string, postId: string): Promise<string[]>;
43
+ comment(orgId: string, commentId: string): Promise<{
44
+ orgUserId: string;
45
+ postId: string | null;
46
+ body: string;
47
+ } | null>;
48
+ post(orgId: string, postId: string): Promise<{
49
+ title: string;
50
+ body: string;
51
+ spaceId: string;
52
+ orgUserId: string;
53
+ communityId: string;
54
+ } | null>;
55
+ chat(orgId: string, chatId: string): Promise<{
56
+ communityId: string;
57
+ kind: 'direct' | 'group';
58
+ status: ChatStatus;
59
+ createdBy: string | null;
60
+ participants: {
61
+ orgUserId: string;
62
+ muted: boolean;
63
+ mutedUntil: Date | null;
64
+ leftAt: Date | null;
65
+ }[];
66
+ } | null>;
67
+ latestMessageBody(orgId: string, chatId: string): Promise<string | null>;
68
+ pairBlocked(orgId: string, communityId: string, a: string, b: string): Promise<boolean>;
69
+ activeMemberCount(orgId: string, spaceId: string): Promise<number>;
70
+ memberCommunityTitles(orgId: string, orgUserId: string): Promise<{
71
+ id: string;
72
+ title: string;
73
+ }[]>;
74
+ recentMessageRecipients(orgId: string, automationId: string, chatId: string, since: Date): Promise<string[]>;
75
+ }
76
+ interface ScheduledWork {
77
+ publishDuePosts(orgId: string, now: Date): Promise<void>;
78
+ expireDueGrants(orgId: string, now: Date): Promise<void>;
79
+ sendDigest(orgId: string, now: Date): Promise<void>;
80
+ hasDuePosts(orgId: string, now: Date): Promise<boolean>;
81
+ hasDueGrants(orgId: string, now: Date): Promise<boolean>;
82
+ removeUnattachedUploads(orgId: string, now: Date): Promise<void>;
83
+ hasUnattachedUploads(orgId: string, now: Date): Promise<boolean>;
84
+ }
85
+ interface GrantWriter {
86
+ grant(orgId: string, input: GrantEntitlementInput): Promise<unknown>;
87
+ }
88
+ interface ActionDeps {
89
+ mailer: Pick<Mailer, 'send'>;
90
+ lookups: MailerLookups;
91
+ credentials: Pick<CredentialStore, 'reveal'>;
92
+ webhooks: WebhookSender;
93
+ mail: CommunityMailReads;
94
+ grants: GrantWriter;
95
+ desk: NotificationDesk;
96
+ scheduledWork: ScheduledWork;
97
+ automationId: string;
98
+ actionInput: Record<string, JsonValue>;
99
+ logger?: Logger;
100
+ }
101
+ declare const emptyCommunityMail: CommunityMailReads;
102
+ declare const emptyNotificationDesk: NotificationDesk;
103
+ declare const emptyScheduledWork: ScheduledWork;
104
+ declare function actorOf(event: DomainEvent): string | undefined;
105
+ declare function previousStatusOf(event: DomainEvent): string | undefined;
106
+ declare function excerpt(value: string, max: number): string;
107
+ declare function seededCommunityId(deps: ActionDeps): string | null;
108
+ declare function firedAtOf(event: DomainEvent): Date;
109
+ declare function address<K extends EmailTemplateId>(deps: ActionDeps, event: DomainEvent, orgId: string, orgUserIds: string[], params: EmailTemplateParams[K]): Promise<EmailRecipient<K>[] | undefined>;
110
+ //#endregion
111
+ export { ActionDeps, CommunityMailReads, EmailRecipient, GrantWriter, MESSAGE_BATCH_MS, MESSAGE_EXCERPT, POST_EXCERPT, ScheduledWork, actorOf, address, emptyCommunityMail, emptyNotificationDesk, emptyScheduledWork, excerpt, firedAtOf, previousStatusOf, seededCommunityId };
112
+ //# sourceMappingURL=community-mail.d.ts.map
@@ -0,0 +1,90 @@
1
+ import { noopLogger } from "../shared/logger.js";
2
+ //#region src/automations/community-mail.ts
3
+ const MESSAGE_EXCERPT = 140;
4
+ const POST_EXCERPT = 280;
5
+ const MESSAGE_BATCH_MS = 900 * 1e3;
6
+ const emptyCommunityMail = {
7
+ communityTitle: async () => null,
8
+ space: async () => null,
9
+ openSpaceNames: async () => [],
10
+ communitiesOpenedBy: async () => [],
11
+ communityMembersAmong: async () => [],
12
+ spaceOpenersAmong: async () => [],
13
+ spaceOpeners: async () => [],
14
+ moderationAudience: async () => [],
15
+ staffIds: async () => [],
16
+ displayName: async () => "A member",
17
+ followerIds: async () => [],
18
+ comment: async () => null,
19
+ post: async () => null,
20
+ chat: async () => null,
21
+ latestMessageBody: async () => null,
22
+ pairBlocked: async () => false,
23
+ activeMemberCount: async () => 0,
24
+ memberCommunityTitles: async () => [],
25
+ recentMessageRecipients: async () => []
26
+ };
27
+ const emptyNotificationDesk = {
28
+ notify: async () => {},
29
+ preferencesFor: async () => ({})
30
+ };
31
+ const emptyScheduledWork = {
32
+ publishDuePosts: async () => {},
33
+ expireDueGrants: async () => {},
34
+ sendDigest: async () => {},
35
+ hasDuePosts: async () => false,
36
+ hasDueGrants: async () => false,
37
+ removeUnattachedUploads: async () => {},
38
+ hasUnattachedUploads: async () => false
39
+ };
40
+ function actorOf(event) {
41
+ const value = event.metadata?.actorOrgUserId;
42
+ return typeof value === "string" ? value : void 0;
43
+ }
44
+ function previousStatusOf(event) {
45
+ const value = event.metadata?.previousStatus;
46
+ return typeof value === "string" ? value : void 0;
47
+ }
48
+ function excerpt(value, max) {
49
+ const flat = value.replace(/\s+/g, " ").trim();
50
+ return flat.length <= max ? flat : flat.slice(0, max);
51
+ }
52
+ function seededCommunityId(deps) {
53
+ const id = deps.actionInput["communityId"];
54
+ return typeof id === "string" ? id : null;
55
+ }
56
+ function firedAtOf(event) {
57
+ if (typeof event.data === "object" && event.data !== null && "firedAt" in event.data) {
58
+ const firedAt = event.data.firedAt;
59
+ if (typeof firedAt === "string") return new Date(firedAt);
60
+ }
61
+ return new Date(event.occurredAt);
62
+ }
63
+ async function address(deps, event, orgId, orgUserIds, params) {
64
+ const actor = actorOf(event);
65
+ const wanted = [...new Set(orgUserIds)].filter((id) => id !== actor);
66
+ if (wanted.length === 0) return [];
67
+ const recipients = [];
68
+ const logger = deps.logger ?? noopLogger;
69
+ for (const orgUserId of wanted) {
70
+ const person = await deps.lookups.orgUserPerson(orgId, orgUserId);
71
+ if (!person) {
72
+ logger.warn("email skipped: recipient has no address", {
73
+ orgId,
74
+ orgUserId
75
+ });
76
+ continue;
77
+ }
78
+ recipients.push({
79
+ to: person.email,
80
+ orgUserId,
81
+ userId: person.userId,
82
+ params
83
+ });
84
+ }
85
+ return recipients;
86
+ }
87
+ //#endregion
88
+ export { MESSAGE_BATCH_MS, MESSAGE_EXCERPT, POST_EXCERPT, actorOf, address, emptyCommunityMail, emptyNotificationDesk, emptyScheduledWork, excerpt, firedAtOf, previousStatusOf, seededCommunityId };
89
+
90
+ //# sourceMappingURL=community-mail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"community-mail.js","names":[],"sources":["../../src/automations/community-mail.ts"],"sourcesContent":["import type { GrantEntitlementInput } from '../entitlements/index.js';\nimport type { NotificationDesk } from '../identity/index.js';\nimport type {\n ChatStatus,\n EmailTemplateId,\n EmailTemplateParams,\n JsonValue,\n} from '../types/index.js';\nimport type { Mailer, MailerLookups } from '../shared/mailer.js';\nimport type { CredentialStore, DomainEvent, Logger } from '../shared/ports.js';\nimport { noopLogger } from '../shared/logger.js';\nimport type { WebhookSender } from './ports.js';\n\nexport const MESSAGE_EXCERPT = 140;\nexport const POST_EXCERPT = 280;\nexport const MESSAGE_BATCH_MS = 15 * 60 * 1000;\n\nexport interface EmailRecipient<K extends EmailTemplateId = EmailTemplateId> {\n to: string;\n orgUserId: string;\n userId: string;\n params: EmailTemplateParams[K];\n}\n\nexport interface CommunityMailReads {\n communityTitle(orgId: string, communityId: string): Promise<string | null>;\n space(\n orgId: string,\n spaceId: string,\n ): Promise<{ name: string; communityId: string; capacity: number | null } | null>;\n openSpaceNames(orgId: string, orgUserId: string, communityId: string): Promise<string[]>;\n communitiesOpenedBy(\n orgId: string,\n grant: { contentId: string | null; bundleId: string | null },\n ): Promise<string[]>;\n communityMembersAmong(orgId: string, communityId: string, ids: string[]): Promise<string[]>;\n spaceOpenersAmong(orgId: string, spaceId: string, ids: string[]): Promise<string[]>;\n spaceOpeners(orgId: string, spaceId: string): Promise<string[]>;\n moderationAudience(orgId: string, communityId: string): Promise<string[]>;\n staffIds(orgId: string): Promise<string[]>;\n displayName(orgId: string, orgUserId: string): Promise<string>;\n followerIds(orgId: string, postId: string): Promise<string[]>;\n comment(\n orgId: string,\n commentId: string,\n ): Promise<{ orgUserId: string; postId: string | null; body: string } | null>;\n post(\n orgId: string,\n postId: string,\n ): Promise<{\n title: string;\n body: string;\n spaceId: string;\n orgUserId: string;\n communityId: string;\n } | null>;\n chat(\n orgId: string,\n chatId: string,\n ): Promise<{\n communityId: string;\n kind: 'direct' | 'group';\n status: ChatStatus;\n createdBy: string | null;\n participants: {\n orgUserId: string;\n muted: boolean;\n mutedUntil: Date | null;\n leftAt: Date | null;\n }[];\n } | null>;\n latestMessageBody(orgId: string, chatId: string): Promise<string | null>;\n pairBlocked(orgId: string, communityId: string, a: string, b: string): Promise<boolean>;\n activeMemberCount(orgId: string, spaceId: string): Promise<number>;\n memberCommunityTitles(orgId: string, orgUserId: string): Promise<{ id: string; title: string }[]>;\n recentMessageRecipients(\n orgId: string,\n automationId: string,\n chatId: string,\n since: Date,\n ): Promise<string[]>;\n}\n\nexport interface ScheduledWork {\n publishDuePosts(orgId: string, now: Date): Promise<void>;\n expireDueGrants(orgId: string, now: Date): Promise<void>;\n sendDigest(orgId: string, now: Date): Promise<void>;\n hasDuePosts(orgId: string, now: Date): Promise<boolean>;\n hasDueGrants(orgId: string, now: Date): Promise<boolean>;\n removeUnattachedUploads(orgId: string, now: Date): Promise<void>;\n hasUnattachedUploads(orgId: string, now: Date): Promise<boolean>;\n}\n\nexport interface GrantWriter {\n grant(orgId: string, input: GrantEntitlementInput): Promise<unknown>;\n}\n\nexport interface ActionDeps {\n mailer: Pick<Mailer, 'send'>;\n lookups: MailerLookups;\n credentials: Pick<CredentialStore, 'reveal'>;\n webhooks: WebhookSender;\n mail: CommunityMailReads;\n grants: GrantWriter;\n desk: NotificationDesk;\n scheduledWork: ScheduledWork;\n automationId: string;\n actionInput: Record<string, JsonValue>;\n logger?: Logger;\n}\n\nexport const emptyCommunityMail: CommunityMailReads = {\n communityTitle: async () => null,\n space: async () => null,\n openSpaceNames: async () => [],\n communitiesOpenedBy: async () => [],\n communityMembersAmong: async () => [],\n spaceOpenersAmong: async () => [],\n spaceOpeners: async () => [],\n moderationAudience: async () => [],\n staffIds: async () => [],\n displayName: async () => 'A member',\n followerIds: async () => [],\n comment: async () => null,\n post: async () => null,\n chat: async () => null,\n latestMessageBody: async () => null,\n pairBlocked: async () => false,\n activeMemberCount: async () => 0,\n memberCommunityTitles: async () => [],\n recentMessageRecipients: async () => [],\n};\n\nexport const emptyNotificationDesk: NotificationDesk = {\n notify: async () => {},\n preferencesFor: async () => ({}),\n};\n\nexport const emptyScheduledWork: ScheduledWork = {\n publishDuePosts: async () => {},\n expireDueGrants: async () => {},\n sendDigest: async () => {},\n hasDuePosts: async () => false,\n hasDueGrants: async () => false,\n removeUnattachedUploads: async () => {},\n hasUnattachedUploads: async () => false,\n};\n\nexport function actorOf(event: DomainEvent): string | undefined {\n const value = event.metadata?.actorOrgUserId;\n return typeof value === 'string' ? value : undefined;\n}\n\nexport function previousStatusOf(event: DomainEvent): string | undefined {\n const value = event.metadata?.previousStatus;\n return typeof value === 'string' ? value : undefined;\n}\n\nexport function excerpt(value: string, max: number): string {\n const flat = value.replace(/\\s+/g, ' ').trim();\n return flat.length <= max ? flat : flat.slice(0, max);\n}\n\nexport function seededCommunityId(deps: ActionDeps): string | null {\n const id = deps.actionInput['communityId'];\n return typeof id === 'string' ? id : null;\n}\n\nexport function firedAtOf(event: DomainEvent): Date {\n if (typeof event.data === 'object' && event.data !== null && 'firedAt' in event.data) {\n const firedAt = (event.data as { firedAt?: unknown }).firedAt;\n if (typeof firedAt === 'string') {\n return new Date(firedAt);\n }\n }\n return new Date(event.occurredAt);\n}\n\nexport async function address<K extends EmailTemplateId>(\n deps: ActionDeps,\n event: DomainEvent,\n orgId: string,\n orgUserIds: string[],\n params: EmailTemplateParams[K],\n): Promise<EmailRecipient<K>[] | undefined> {\n const actor = actorOf(event);\n const wanted = [...new Set(orgUserIds)].filter((id) => id !== actor);\n if (wanted.length === 0) {\n return [];\n }\n const recipients: EmailRecipient<K>[] = [];\n const logger = deps.logger ?? noopLogger;\n for (const orgUserId of wanted) {\n const person = await deps.lookups.orgUserPerson(orgId, orgUserId);\n if (!person) {\n logger.warn('email skipped: recipient has no address', { orgId, orgUserId });\n continue;\n }\n recipients.push({ to: person.email, orgUserId, userId: person.userId, params });\n }\n return recipients;\n}\n"],"mappings":";;AAaA,MAAa,kBAAkB;AAC/B,MAAa,eAAe;AAC5B,MAAa,mBAAmB,MAAU;AAgG1C,MAAa,qBAAyC;CACpD,gBAAgB,YAAY;CAC5B,OAAO,YAAY;CACnB,gBAAgB,YAAY,CAAC;CAC7B,qBAAqB,YAAY,CAAC;CAClC,uBAAuB,YAAY,CAAC;CACpC,mBAAmB,YAAY,CAAC;CAChC,cAAc,YAAY,CAAC;CAC3B,oBAAoB,YAAY,CAAC;CACjC,UAAU,YAAY,CAAC;CACvB,aAAa,YAAY;CACzB,aAAa,YAAY,CAAC;CAC1B,SAAS,YAAY;CACrB,MAAM,YAAY;CAClB,MAAM,YAAY;CAClB,mBAAmB,YAAY;CAC/B,aAAa,YAAY;CACzB,mBAAmB,YAAY;CAC/B,uBAAuB,YAAY,CAAC;CACpC,yBAAyB,YAAY,CAAC;AACxC;AAEA,MAAa,wBAA0C;CACrD,QAAQ,YAAY,CAAC;CACrB,gBAAgB,aAAa,CAAC;AAChC;AAEA,MAAa,qBAAoC;CAC/C,iBAAiB,YAAY,CAAC;CAC9B,iBAAiB,YAAY,CAAC;CAC9B,YAAY,YAAY,CAAC;CACzB,aAAa,YAAY;CACzB,cAAc,YAAY;CAC1B,yBAAyB,YAAY,CAAC;CACtC,sBAAsB,YAAY;AACpC;AAEA,SAAgB,QAAQ,OAAwC;CAC9D,MAAM,QAAQ,MAAM,UAAU;CAC9B,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAA;AAC7C;AAEA,SAAgB,iBAAiB,OAAwC;CACvE,MAAM,QAAQ,MAAM,UAAU;CAC9B,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAA;AAC7C;AAEA,SAAgB,QAAQ,OAAe,KAAqB;CAC1D,MAAM,OAAO,MAAM,QAAQ,QAAQ,GAAG,CAAC,CAAC,KAAK;CAC7C,OAAO,KAAK,UAAU,MAAM,OAAO,KAAK,MAAM,GAAG,GAAG;AACtD;AAEA,SAAgB,kBAAkB,MAAiC;CACjE,MAAM,KAAK,KAAK,YAAY;CAC5B,OAAO,OAAO,OAAO,WAAW,KAAK;AACvC;AAEA,SAAgB,UAAU,OAA0B;CAClD,IAAI,OAAO,MAAM,SAAS,YAAY,MAAM,SAAS,QAAQ,aAAa,MAAM,MAAM;EACpF,MAAM,UAAW,MAAM,KAA+B;EACtD,IAAI,OAAO,YAAY,UACrB,OAAO,IAAI,KAAK,OAAO;CAE3B;CACA,OAAO,IAAI,KAAK,MAAM,UAAU;AAClC;AAEA,eAAsB,QACpB,MACA,OACA,OACA,YACA,QAC0C;CAC1C,MAAM,QAAQ,QAAQ,KAAK;CAC3B,MAAM,SAAS,CAAC,GAAG,IAAI,IAAI,UAAU,CAAC,CAAC,CAAC,QAAQ,OAAO,OAAO,KAAK;CACnE,IAAI,OAAO,WAAW,GACpB,OAAO,CAAC;CAEV,MAAM,aAAkC,CAAC;CACzC,MAAM,SAAS,KAAK,UAAU;CAC9B,KAAK,MAAM,aAAa,QAAQ;EAC9B,MAAM,SAAS,MAAM,KAAK,QAAQ,cAAc,OAAO,SAAS;EAChE,IAAI,CAAC,QAAQ;GACX,OAAO,KAAK,2CAA2C;IAAE;IAAO;GAAU,CAAC;GAC3E;EACF;EACA,WAAW,KAAK;GAAE,IAAI,OAAO;GAAO;GAAW,QAAQ,OAAO;GAAQ;EAAO,CAAC;CAChF;CACA,OAAO;AACT"}
@@ -0,0 +1,16 @@
1
+ import { DomainEvent } from "../types/shared.js";
2
+ import { NotificationKind } from "../types/schemas/identity.js";
3
+ import "../types/index.js";
4
+ import "../shared/ports.js";
5
+ import { NewNotification } from "../identity/ports.js";
6
+ import "../identity/index.js";
7
+ import { ActionDeps, EmailRecipient } from "./community-mail.js";
8
+ //#region src/automations/community-notifications.d.ts
9
+ type Placement = Omit<NewNotification, 'userId' | 'kind'>;
10
+ interface NotificationPlacement<K extends NotificationKind> {
11
+ place(event: DomainEvent, recipient: EmailRecipient<K>, deps: ActionDeps): Promise<Placement[]>;
12
+ }
13
+ declare const NOTIFICATION_PLACEMENTS: { [K in NotificationKind]: NotificationPlacement<K>; };
14
+ //#endregion
15
+ export { NOTIFICATION_PLACEMENTS, NotificationPlacement };
16
+ //# sourceMappingURL=community-notifications.d.ts.map
@@ -0,0 +1,84 @@
1
+ import { actorOf } from "./community-mail.js";
2
+ //#region src/automations/community-notifications.ts
3
+ function text(event, key) {
4
+ if (typeof event.data !== "object" || event.data === null) return null;
5
+ const value = event.data[key];
6
+ return typeof value === "string" ? value : null;
7
+ }
8
+ function at(communityId, actorOrgUserId, about) {
9
+ return [{
10
+ communityId,
11
+ actorOrgUserId,
12
+ spaceId: about.spaceId ?? null,
13
+ postId: about.postId ?? null,
14
+ commentId: about.commentId ?? null,
15
+ chatId: about.chatId ?? null,
16
+ messageId: about.messageId ?? null
17
+ }];
18
+ }
19
+ function actor(event) {
20
+ return actorOf(event) ?? null;
21
+ }
22
+ async function spaceOfPost(deps, event, postId) {
23
+ return postId ? (await deps.mail.post(event.orgId, postId))?.spaceId ?? null : null;
24
+ }
25
+ const NOTIFICATION_PLACEMENTS = {
26
+ newPostInSpace: { place: async (event, { params }) => at(params.communityId, text(event, "orgUserId"), {
27
+ postId: params.postId,
28
+ spaceId: text(event, "spaceId")
29
+ }) },
30
+ newCommentOnPost: { place: async (event, { params }, deps) => at(params.communityId, text(event, "orgUserId"), {
31
+ postId: params.postId,
32
+ commentId: params.commentId,
33
+ spaceId: await spaceOfPost(deps, event, params.postId)
34
+ }) },
35
+ replyToYourComment: { place: async (event, { params }, deps) => at(params.communityId, text(event, "orgUserId"), {
36
+ postId: params.postId,
37
+ commentId: params.commentId,
38
+ spaceId: await spaceOfPost(deps, event, params.postId)
39
+ }) },
40
+ yourPostWasPublished: { place: async (event, { params }) => at(params.communityId, actor(event), {
41
+ postId: params.postId,
42
+ spaceId: text(event, "spaceId")
43
+ }) },
44
+ yourPostWasRemoved: { place: async (event, { params }) => at(params.communityId, text(event, "removedBy"), {
45
+ postId: text(event, "id"),
46
+ spaceId: params.spaceId
47
+ }) },
48
+ yourCommentWasRemoved: { place: async (event, { params }, deps) => at(params.communityId, text(event, "removedBy"), {
49
+ postId: params.postId,
50
+ commentId: text(event, "id"),
51
+ spaceId: await spaceOfPost(deps, event, params.postId)
52
+ }) },
53
+ requestApproved: { place: async (event, { params }) => at(params.communityId, actor(event), { spaceId: params.spaceId }) },
54
+ requestDeclined: { place: async (event, { params }) => at(params.communityId, actor(event), { spaceId: text(event, "spaceId") }) },
55
+ invitedToSpace: { place: async (event, { params }) => at(params.communityId, actor(event), { spaceId: params.spaceId }) },
56
+ newMessage: { place: async (event, { params }) => at(params.communityId, text(event, "orgUserId"), {
57
+ chatId: params.chatId,
58
+ messageId: text(event, "id")
59
+ }) },
60
+ messageRequest: { place: async (event, { params }) => at(params.communityId, text(event, "createdBy"), { chatId: params.chatId }) },
61
+ addedToGroupChat: { place: async (event, { params }) => at(params.communityId, text(event, "addedBy"), { chatId: params.chatId }) },
62
+ youAreNowAModerator: { place: async (event) => at(null, actor(event), {}) },
63
+ membershipEndingSoon: { place: async (_event, { params }) => at(params.communityId, null, {}) },
64
+ welcomeToCommunity: { place: async (event, { params }) => at(params.communityId, actor(event), {}) },
65
+ postAwaitingReview: { place: async (event, { params }) => at(params.communityId, text(event, "orgUserId"), {
66
+ postId: text(event, "id"),
67
+ spaceId: params.spaceId
68
+ }) },
69
+ joinRequest: { place: async (event, { params }) => at(params.communityId, text(event, "orgUserId"), { spaceId: params.spaceId }) },
70
+ somethingWasFlagged: { place: async (event, { params }, deps) => {
71
+ const commentId = text(event, "commentId");
72
+ const postId = commentId ? (await deps.mail.comment(event.orgId, commentId))?.postId ?? null : text(event, "postId");
73
+ return at(params.communityId, text(event, "orgUserId"), {
74
+ spaceId: params.spaceId,
75
+ postId,
76
+ commentId
77
+ });
78
+ } },
79
+ messageWasFlagged: { place: async (event, { params }) => at(params.communityId, text(event, "orgUserId"), { messageId: text(event, "messageId") }) }
80
+ };
81
+ //#endregion
82
+ export { NOTIFICATION_PLACEMENTS };
83
+
84
+ //# sourceMappingURL=community-notifications.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"community-notifications.js","names":[],"sources":["../../src/automations/community-notifications.ts"],"sourcesContent":["import type { NewNotification } from '../identity/index.js';\nimport type { NotificationKind } from '../types/index.js';\nimport type { DomainEvent } from '../shared/ports.js';\nimport { actorOf, type ActionDeps, type EmailRecipient } from './community-mail.js';\n\ntype Placement = Omit<NewNotification, 'userId' | 'kind'>;\n\nexport interface NotificationPlacement<K extends NotificationKind> {\n place(event: DomainEvent, recipient: EmailRecipient<K>, deps: ActionDeps): Promise<Placement[]>;\n}\n\ntype About = Partial<Pick<Placement, 'spaceId' | 'postId' | 'commentId' | 'chatId' | 'messageId'>>;\n\nfunction text(event: DomainEvent, key: string): string | null {\n if (typeof event.data !== 'object' || event.data === null) {\n return null;\n }\n const value = (event.data as Record<string, unknown>)[key];\n return typeof value === 'string' ? value : null;\n}\n\nfunction at(communityId: string | null, actorOrgUserId: string | null, about: About): Placement[] {\n return [\n {\n communityId,\n actorOrgUserId,\n spaceId: about.spaceId ?? null,\n postId: about.postId ?? null,\n commentId: about.commentId ?? null,\n chatId: about.chatId ?? null,\n messageId: about.messageId ?? null,\n },\n ];\n}\n\nfunction actor(event: DomainEvent): string | null {\n return actorOf(event) ?? null;\n}\n\nasync function spaceOfPost(\n deps: ActionDeps,\n event: DomainEvent,\n postId: string | null,\n): Promise<string | null> {\n return postId ? ((await deps.mail.post(event.orgId, postId))?.spaceId ?? null) : null;\n}\n\nexport const NOTIFICATION_PLACEMENTS: {\n [K in NotificationKind]: NotificationPlacement<K>;\n} = {\n newPostInSpace: {\n place: async (event, { params }) =>\n at(params.communityId, text(event, 'orgUserId'), {\n postId: params.postId,\n spaceId: text(event, 'spaceId'),\n }),\n },\n newCommentOnPost: {\n place: async (event, { params }, deps) =>\n at(params.communityId, text(event, 'orgUserId'), {\n postId: params.postId,\n commentId: params.commentId,\n spaceId: await spaceOfPost(deps, event, params.postId),\n }),\n },\n replyToYourComment: {\n place: async (event, { params }, deps) =>\n at(params.communityId, text(event, 'orgUserId'), {\n postId: params.postId,\n commentId: params.commentId,\n spaceId: await spaceOfPost(deps, event, params.postId),\n }),\n },\n yourPostWasPublished: {\n place: async (event, { params }) =>\n at(params.communityId, actor(event), {\n postId: params.postId,\n spaceId: text(event, 'spaceId'),\n }),\n },\n yourPostWasRemoved: {\n place: async (event, { params }) =>\n at(params.communityId, text(event, 'removedBy'), {\n postId: text(event, 'id'),\n spaceId: params.spaceId,\n }),\n },\n yourCommentWasRemoved: {\n place: async (event, { params }, deps) =>\n at(params.communityId, text(event, 'removedBy'), {\n postId: params.postId,\n commentId: text(event, 'id'),\n spaceId: await spaceOfPost(deps, event, params.postId),\n }),\n },\n requestApproved: {\n place: async (event, { params }) =>\n at(params.communityId, actor(event), { spaceId: params.spaceId }),\n },\n requestDeclined: {\n place: async (event, { params }) =>\n at(params.communityId, actor(event), { spaceId: text(event, 'spaceId') }),\n },\n invitedToSpace: {\n place: async (event, { params }) =>\n at(params.communityId, actor(event), { spaceId: params.spaceId }),\n },\n newMessage: {\n place: async (event, { params }) =>\n at(params.communityId, text(event, 'orgUserId'), {\n chatId: params.chatId,\n messageId: text(event, 'id'),\n }),\n },\n messageRequest: {\n place: async (event, { params }) =>\n at(params.communityId, text(event, 'createdBy'), { chatId: params.chatId }),\n },\n addedToGroupChat: {\n place: async (event, { params }) =>\n at(params.communityId, text(event, 'addedBy'), { chatId: params.chatId }),\n },\n youAreNowAModerator: {\n place: async (event) => at(null, actor(event), {}),\n },\n membershipEndingSoon: {\n place: async (_event, { params }) => at(params.communityId, null, {}),\n },\n welcomeToCommunity: {\n place: async (event, { params }) => at(params.communityId, actor(event), {}),\n },\n postAwaitingReview: {\n place: async (event, { params }) =>\n at(params.communityId, text(event, 'orgUserId'), {\n postId: text(event, 'id'),\n spaceId: params.spaceId,\n }),\n },\n joinRequest: {\n place: async (event, { params }) =>\n at(params.communityId, text(event, 'orgUserId'), { spaceId: params.spaceId }),\n },\n somethingWasFlagged: {\n place: async (event, { params }, deps) => {\n const commentId = text(event, 'commentId');\n const postId = commentId\n ? ((await deps.mail.comment(event.orgId, commentId))?.postId ?? null)\n : text(event, 'postId');\n return at(params.communityId, text(event, 'orgUserId'), {\n spaceId: params.spaceId,\n postId,\n commentId,\n });\n },\n },\n messageWasFlagged: {\n place: async (event, { params }) =>\n at(params.communityId, text(event, 'orgUserId'), { messageId: text(event, 'messageId') }),\n },\n};\n"],"mappings":"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"}
@@ -15,6 +15,8 @@ declare const automationEvents: {
15
15
  input: Record<string, JsonValue>;
16
16
  }[];
17
17
  enabled: boolean;
18
+ filter: Record<string, JsonValue> | null;
19
+ source: "system" | "user";
18
20
  createdAt: Date;
19
21
  updatedAt: Date;
20
22
  }, "automation.created", 2>, {
@@ -29,6 +31,8 @@ declare const automationEvents: {
29
31
  input: Record<string, unknown>;
30
32
  }[];
31
33
  enabled: boolean;
34
+ filter: Record<string, unknown> | null;
35
+ source: "system" | "user";
32
36
  createdAt: unknown;
33
37
  updatedAt: unknown;
34
38
  }, undefined>;
@@ -44,6 +48,8 @@ declare const automationEvents: {
44
48
  input: Record<string, JsonValue>;
45
49
  }[];
46
50
  enabled: boolean;
51
+ filter: Record<string, JsonValue> | null;
52
+ source: "system" | "user";
47
53
  createdAt: Date;
48
54
  updatedAt: Date;
49
55
  }, "automation.updated", 2>, {
@@ -58,6 +64,8 @@ declare const automationEvents: {
58
64
  input: Record<string, unknown>;
59
65
  }[];
60
66
  enabled: boolean;
67
+ filter: Record<string, unknown> | null;
68
+ source: "system" | "user";
61
69
  createdAt: unknown;
62
70
  updatedAt: unknown;
63
71
  }, undefined>;
@@ -73,6 +81,8 @@ declare const automationEvents: {
73
81
  input: Record<string, JsonValue>;
74
82
  }[];
75
83
  enabled: boolean;
84
+ filter: Record<string, JsonValue> | null;
85
+ source: "system" | "user";
76
86
  createdAt: Date;
77
87
  updatedAt: Date;
78
88
  }, "automation.deleted", 2>, {
@@ -87,6 +97,8 @@ declare const automationEvents: {
87
97
  input: Record<string, unknown>;
88
98
  }[];
89
99
  enabled: boolean;
100
+ filter: Record<string, unknown> | null;
101
+ source: "system" | "user";
90
102
  createdAt: unknown;
91
103
  updatedAt: unknown;
92
104
  }, undefined>;
@@ -102,6 +114,8 @@ declare const automationEvents: {
102
114
  input: Record<string, JsonValue>;
103
115
  }[];
104
116
  enabled: boolean;
117
+ filter: Record<string, JsonValue> | null;
118
+ source: "system" | "user";
105
119
  createdAt: Date;
106
120
  updatedAt: Date;
107
121
  }, "automation.enabled", 2>, {
@@ -116,6 +130,8 @@ declare const automationEvents: {
116
130
  input: Record<string, unknown>;
117
131
  }[];
118
132
  enabled: boolean;
133
+ filter: Record<string, unknown> | null;
134
+ source: "system" | "user";
119
135
  createdAt: unknown;
120
136
  updatedAt: unknown;
121
137
  }, undefined>;
@@ -131,6 +147,8 @@ declare const automationEvents: {
131
147
  input: Record<string, JsonValue>;
132
148
  }[];
133
149
  enabled: boolean;
150
+ filter: Record<string, JsonValue> | null;
151
+ source: "system" | "user";
134
152
  createdAt: Date;
135
153
  updatedAt: Date;
136
154
  }, "automation.disabled", 2>, {
@@ -145,6 +163,8 @@ declare const automationEvents: {
145
163
  input: Record<string, unknown>;
146
164
  }[];
147
165
  enabled: boolean;
166
+ filter: Record<string, unknown> | null;
167
+ source: "system" | "user";
148
168
  createdAt: unknown;
149
169
  updatedAt: unknown;
150
170
  }, undefined>;
@@ -161,6 +181,7 @@ declare const automationEvents: {
161
181
  type: string;
162
182
  status: "completed" | "failed";
163
183
  error?: string | undefined;
184
+ recipients?: string[] | undefined;
164
185
  }[];
165
186
  startedAt: Date;
166
187
  finishedAt: Date | null;
@@ -179,6 +200,7 @@ declare const automationEvents: {
179
200
  type: string;
180
201
  status: "completed" | "failed";
181
202
  error?: string | undefined;
203
+ recipients?: string[] | undefined;
182
204
  }[];
183
205
  startedAt: unknown;
184
206
  finishedAt: unknown;
@@ -198,6 +220,7 @@ declare const automationEvents: {
198
220
  type: string;
199
221
  status: "completed" | "failed";
200
222
  error?: string | undefined;
223
+ recipients?: string[] | undefined;
201
224
  }[];
202
225
  startedAt: Date;
203
226
  finishedAt: Date | null;
@@ -216,6 +239,7 @@ declare const automationEvents: {
216
239
  type: string;
217
240
  status: "completed" | "failed";
218
241
  error?: string | undefined;
242
+ recipients?: string[] | undefined;
219
243
  }[];
220
244
  startedAt: unknown;
221
245
  finishedAt: unknown;
@@ -235,6 +259,7 @@ declare const automationEvents: {
235
259
  type: string;
236
260
  status: "completed" | "failed";
237
261
  error?: string | undefined;
262
+ recipients?: string[] | undefined;
238
263
  }[];
239
264
  startedAt: Date;
240
265
  finishedAt: Date | null;
@@ -253,6 +278,7 @@ declare const automationEvents: {
253
278
  type: string;
254
279
  status: "completed" | "failed";
255
280
  error?: string | undefined;
281
+ recipients?: string[] | undefined;
256
282
  }[];
257
283
  startedAt: unknown;
258
284
  finishedAt: unknown;
@@ -264,11 +290,13 @@ declare const automationEvents: {
264
290
  type: string;
265
291
  status: "completed" | "failed";
266
292
  error?: string | undefined;
293
+ recipients?: string[] | undefined;
267
294
  }, "automation.action.failed", 1>, {
268
295
  index: number;
269
296
  type: string;
270
297
  status: "completed" | "failed";
271
298
  error?: string | undefined;
299
+ recipients?: string[] | undefined;
272
300
  }, undefined>;
273
301
  };
274
302
  type AutomationCreated = EventOf<typeof automationEvents.automationCreated>;
@@ -1,9 +1,11 @@
1
1
  import { Page } from "../types/shared.js";
2
2
  import { Automation, AutomationAction, AutomationActionResult, AutomationKind, AutomationRun, AutomationRunStatus, AutomationRunsQuery, AutomationTrigger, AutomationsQuery, AvailableAction, AvailableActions, AvailableTriggers, CreateAutomationInput, CreateWebhookInput, CreatedWebhook, UpdateAutomationInput, UpdateWebhookInput, Webhook, WebhookSecret } from "../types/schemas/automations.js";
3
3
  import { AutomationDispatch, AutomationEngine, AutomationExecutor, WebhookRequest, WebhookResponse, WebhookSender } from "../types/automations.js";
4
- import { AutomationKindMismatchError, InvalidTriggerError, ReservedActionError } from "./model.js";
4
+ import { ACTION_FAILED, ActionFailedError, AutomationKindMismatchError, InvalidTriggerError, ReservedActionError, actionFailureMessage } from "./model.js";
5
5
  import "./types.js";
6
6
  import { AutomationRunsRepository, AutomationsRepository, AutomationsService, AutomationsUnitOfWork, NewAutomation, NewAutomationRun } from "./ports.js";
7
+ import { CommunityMailReads, GrantWriter, ScheduledWork } from "./community-mail.js";
7
8
  import { AutomationActionFailed, AutomationCreated, AutomationDeleted, AutomationDisabled, AutomationEnabled, AutomationEvent, AutomationRunCompleted, AutomationRunFailed, AutomationRunStarted, AutomationUpdated, automationEvents } from "./events.js";
8
9
  import { AutomationsServiceImpl } from "./service.js";
9
- export { type Automation, type AutomationAction, type AutomationActionFailed, type AutomationActionResult, type AutomationCreated, type AutomationDeleted, type AutomationDisabled, type AutomationDispatch, type AutomationEnabled, type AutomationEngine, type AutomationEvent, type AutomationExecutor, type AutomationKind, AutomationKindMismatchError, type AutomationRun, type AutomationRunCompleted, type AutomationRunFailed, type AutomationRunStarted, type AutomationRunStatus, type AutomationRunsQuery, type AutomationRunsRepository, type AutomationTrigger, type AutomationUpdated, type AutomationsQuery, type AutomationsRepository, type AutomationsService, AutomationsServiceImpl, type AutomationsUnitOfWork, type AvailableAction, type AvailableActions, type AvailableTriggers, type CreateAutomationInput, type CreateWebhookInput, type CreatedWebhook, InvalidTriggerError, type NewAutomation, type NewAutomationRun, type Page, ReservedActionError, type UpdateAutomationInput, type UpdateWebhookInput, type Webhook, type WebhookRequest, type WebhookResponse, type WebhookSecret, type WebhookSender, automationEvents };
10
+ import { cronMatches } from "./schedule.js";
11
+ export { ACTION_FAILED, ActionFailedError, type Automation, type AutomationAction, type AutomationActionFailed, type AutomationActionResult, type AutomationCreated, type AutomationDeleted, type AutomationDisabled, type AutomationDispatch, type AutomationEnabled, type AutomationEngine, type AutomationEvent, type AutomationExecutor, type AutomationKind, AutomationKindMismatchError, type AutomationRun, type AutomationRunCompleted, type AutomationRunFailed, type AutomationRunStarted, type AutomationRunStatus, type AutomationRunsQuery, type AutomationRunsRepository, type AutomationTrigger, type AutomationUpdated, type AutomationsQuery, type AutomationsRepository, type AutomationsService, AutomationsServiceImpl, type AutomationsUnitOfWork, type AvailableAction, type AvailableActions, type AvailableTriggers, type CommunityMailReads, type CreateAutomationInput, type CreateWebhookInput, type CreatedWebhook, type GrantWriter, InvalidTriggerError, type NewAutomation, type NewAutomationRun, type Page, ReservedActionError, type ScheduledWork, type UpdateAutomationInput, type UpdateWebhookInput, type Webhook, type WebhookRequest, type WebhookResponse, type WebhookSecret, type WebhookSender, actionFailureMessage, automationEvents, cronMatches };
@@ -1,4 +1,5 @@
1
+ import { ACTION_FAILED, ActionFailedError, AutomationKindMismatchError, InvalidTriggerError, ReservedActionError, actionFailureMessage } from "./model.js";
1
2
  import { automationEvents } from "./events.js";
2
- import { AutomationKindMismatchError, InvalidTriggerError, ReservedActionError } from "./model.js";
3
+ import { cronMatches } from "./schedule.js";
3
4
  import { AutomationsServiceImpl } from "./service.js";
4
- export { AutomationKindMismatchError, AutomationsServiceImpl, InvalidTriggerError, ReservedActionError, automationEvents };
5
+ export { ACTION_FAILED, ActionFailedError, AutomationKindMismatchError, AutomationsServiceImpl, InvalidTriggerError, ReservedActionError, actionFailureMessage, automationEvents, cronMatches };
@@ -5,9 +5,16 @@ import "../types/index.js";
5
5
  /** Rejected at authoring time: automation.* is reserved to avoid a self-triggering loop. */
6
6
  declare class InvalidTriggerError extends Error {
7
7
  readonly trigger: string;
8
- readonly reason: 'reserved' | 'unknown';
9
- constructor(trigger: string, reason?: 'reserved' | 'unknown');
8
+ readonly reason: 'reserved' | 'unknown' | 'schedule';
9
+ constructor(trigger: string, reason?: 'reserved' | 'unknown' | 'schedule');
10
10
  }
11
+ declare const ACTION_FAILED = "The action failed";
12
+ /** An action failure whose message is safe to record on the run. */
13
+ declare class ActionFailedError extends Error {
14
+ constructor(message?: string);
15
+ }
16
+ /** The message a run records for an action that threw `err`. */
17
+ declare function actionFailureMessage(err: unknown): string;
11
18
  declare class ReservedActionError extends Error {
12
19
  readonly actionType: string;
13
20
  constructor(actionType: string);
@@ -18,5 +25,5 @@ declare class AutomationKindMismatchError extends Error {
18
25
  constructor(automationId: string, kind: AutomationKind);
19
26
  }
20
27
  //#endregion
21
- export { type Automation, type AutomationAction, type AutomationActionResult, type AutomationKind, AutomationKindMismatchError, type AutomationRun, type AutomationRunStatus, type AutomationTrigger, type CreatedWebhook, type DeliverWebhookInput, InvalidTriggerError, type Page, ReservedActionError, type Webhook, type WebhookSecret };
28
+ export { ACTION_FAILED, ActionFailedError, type Automation, type AutomationAction, type AutomationActionResult, type AutomationKind, AutomationKindMismatchError, type AutomationRun, type AutomationRunStatus, type AutomationTrigger, type CreatedWebhook, type DeliverWebhookInput, InvalidTriggerError, type Page, ReservedActionError, type Webhook, type WebhookSecret, actionFailureMessage };
22
29
  //# sourceMappingURL=model.d.ts.map
@@ -4,12 +4,25 @@ var InvalidTriggerError = class extends Error {
4
4
  trigger;
5
5
  reason;
6
6
  constructor(trigger, reason = "reserved") {
7
- super(reason === "reserved" ? `trigger "${trigger}" is reserved: automations cannot trigger on automation.* events` : `trigger "${trigger}" is not an event the system emits`);
7
+ const message = reason === "reserved" ? `trigger "${trigger}" is reserved: automations cannot trigger on automation.* events` : reason === "unknown" ? `trigger "${trigger}" is not an event the system emits` : `trigger "${trigger}" uses a schedule this system cannot match`;
8
+ super(message);
8
9
  this.trigger = trigger;
9
10
  this.reason = reason;
10
11
  this.name = "InvalidTriggerError";
11
12
  }
12
13
  };
14
+ const ACTION_FAILED = "The action failed";
15
+ /** An action failure whose message is safe to record on the run. */
16
+ var ActionFailedError = class extends Error {
17
+ constructor(message = ACTION_FAILED) {
18
+ super(message);
19
+ this.name = "ActionFailedError";
20
+ }
21
+ };
22
+ /** The message a run records for an action that threw `err`. */
23
+ function actionFailureMessage(err) {
24
+ return err instanceof ActionFailedError ? err.message : ACTION_FAILED;
25
+ }
13
26
  var ReservedActionError = class extends Error {
14
27
  actionType;
15
28
  constructor(actionType) {
@@ -29,6 +42,6 @@ var AutomationKindMismatchError = class extends Error {
29
42
  }
30
43
  };
31
44
  //#endregion
32
- export { AutomationKindMismatchError, InvalidTriggerError, ReservedActionError };
45
+ export { ACTION_FAILED, ActionFailedError, AutomationKindMismatchError, InvalidTriggerError, ReservedActionError, actionFailureMessage };
33
46
 
34
47
  //# sourceMappingURL=model.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"model.js","names":[],"sources":["../../src/automations/model.ts"],"sourcesContent":["// automations context — domain entities, owned by @headless-lms/core/types; the\n// domain errors below are runtime code and stay in core.\nimport type { AutomationKind } from '../types/index.js';\n\nexport type {\n AutomationTrigger,\n AutomationAction,\n AutomationKind,\n Automation,\n AutomationRunStatus,\n AutomationActionResult,\n AutomationRun,\n Webhook,\n CreatedWebhook,\n WebhookSecret,\n DeliverWebhookInput,\n Page,\n} from '../types/index.js';\n\n/** Rejected at authoring time: automation.* is reserved to avoid a self-triggering loop. */\nexport class InvalidTriggerError extends Error {\n constructor(\n readonly trigger: string,\n readonly reason: 'reserved' | 'unknown' = 'reserved',\n ) {\n super(\n reason === 'reserved'\n ? `trigger \"${trigger}\" is reserved: automations cannot trigger on automation.* events`\n : `trigger \"${trigger}\" is not an event the system emits`,\n );\n this.name = 'InvalidTriggerError';\n }\n}\n\nexport class ReservedActionError extends Error {\n constructor(readonly actionType: string) {\n super(`action \"${actionType}\" is reserved to webhooks`);\n this.name = 'ReservedActionError';\n }\n}\n\nexport class AutomationKindMismatchError extends Error {\n constructor(\n readonly automationId: string,\n readonly kind: AutomationKind,\n ) {\n super(`automation \"${automationId}\" is a ${kind} and can't be changed through this operation`);\n this.name = 'AutomationKindMismatchError';\n }\n}\n"],"mappings":";;AAoBA,IAAa,sBAAb,cAAyC,MAAM;CAElC;CACA;CAFX,YACE,SACA,SAA0C,YAC1C;EACA,MACE,WAAW,aACP,YAAY,QAAQ,oEACpB,YAAY,QAAQ,mCAC1B;EAPS,KAAA,UAAA;EACA,KAAA,SAAA;EAOT,KAAK,OAAO;CACd;AACF;AAEA,IAAa,sBAAb,cAAyC,MAAM;CACxB;CAArB,YAAY,YAA6B;EACvC,MAAM,WAAW,WAAW,0BAA0B;EADnC,KAAA,aAAA;EAEnB,KAAK,OAAO;CACd;AACF;AAEA,IAAa,8BAAb,cAAiD,MAAM;CAE1C;CACA;CAFX,YACE,cACA,MACA;EACA,MAAM,eAAe,aAAa,SAAS,KAAK,6CAA6C;EAHpF,KAAA,eAAA;EACA,KAAA,OAAA;EAGT,KAAK,OAAO;CACd;AACF"}
1
+ {"version":3,"file":"model.js","names":[],"sources":["../../src/automations/model.ts"],"sourcesContent":["// automations context — domain entities, owned by @headless-lms/core/types; the\n// domain errors below are runtime code and stay in core.\nimport type { AutomationKind } from '../types/index.js';\n\nexport type {\n AutomationTrigger,\n AutomationAction,\n AutomationKind,\n Automation,\n AutomationRunStatus,\n AutomationActionResult,\n AutomationRun,\n Webhook,\n CreatedWebhook,\n WebhookSecret,\n DeliverWebhookInput,\n Page,\n} from '../types/index.js';\n\n/** Rejected at authoring time: automation.* is reserved to avoid a self-triggering loop. */\nexport class InvalidTriggerError extends Error {\n constructor(\n readonly trigger: string,\n readonly reason: 'reserved' | 'unknown' | 'schedule' = 'reserved',\n ) {\n const message =\n reason === 'reserved'\n ? `trigger \"${trigger}\" is reserved: automations cannot trigger on automation.* events`\n : reason === 'unknown'\n ? `trigger \"${trigger}\" is not an event the system emits`\n : `trigger \"${trigger}\" uses a schedule this system cannot match`;\n super(message);\n this.name = 'InvalidTriggerError';\n }\n}\n\nexport const ACTION_FAILED = 'The action failed';\n\n/** An action failure whose message is safe to record on the run. */\nexport class ActionFailedError extends Error {\n constructor(message: string = ACTION_FAILED) {\n super(message);\n this.name = 'ActionFailedError';\n }\n}\n\n/** The message a run records for an action that threw `err`. */\nexport function actionFailureMessage(err: unknown): string {\n return err instanceof ActionFailedError ? err.message : ACTION_FAILED;\n}\n\nexport class ReservedActionError extends Error {\n constructor(readonly actionType: string) {\n super(`action \"${actionType}\" is reserved to webhooks`);\n this.name = 'ReservedActionError';\n }\n}\n\nexport class AutomationKindMismatchError extends Error {\n constructor(\n readonly automationId: string,\n readonly kind: AutomationKind,\n ) {\n super(`automation \"${automationId}\" is a ${kind} and can't be changed through this operation`);\n this.name = 'AutomationKindMismatchError';\n }\n}\n"],"mappings":";;AAoBA,IAAa,sBAAb,cAAyC,MAAM;CAElC;CACA;CAFX,YACE,SACA,SAAuD,YACvD;EACA,MAAM,UACJ,WAAW,aACP,YAAY,QAAQ,oEACpB,WAAW,YACT,YAAY,QAAQ,sCACpB,YAAY,QAAQ;EAC5B,MAAM,OAAO;EATJ,KAAA,UAAA;EACA,KAAA,SAAA;EAST,KAAK,OAAO;CACd;AACF;AAEA,MAAa,gBAAgB;;AAG7B,IAAa,oBAAb,cAAuC,MAAM;CAC3C,YAAY,UAAkB,eAAe;EAC3C,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF;;AAGA,SAAgB,qBAAqB,KAAsB;CACzD,OAAO,eAAe,oBAAoB,IAAI,UAAU;AAC1D;AAEA,IAAa,sBAAb,cAAyC,MAAM;CACxB;CAArB,YAAY,YAA6B;EACvC,MAAM,WAAW,WAAW,0BAA0B;EADnC,KAAA,aAAA;EAEnB,KAAK,OAAO;CACd;AACF;AAEA,IAAa,8BAAb,cAAiD,MAAM;CAE1C;CACA;CAFX,YACE,cACA,MACA;EACA,MAAM,eAAe,aAAa,SAAS,KAAK,6CAA6C;EAHpF,KAAA,eAAA;EACA,KAAA,OAAA;EAGT,KAAK,OAAO;CACd;AACF"}
@@ -1,6 +1,6 @@
1
1
  import { DomainEvent, Page } from "../types/shared.js";
2
2
  import { Automation, AutomationKind, AutomationRun, AutomationRunsQuery, AutomationsQuery, AvailableActions, AvailableTriggers, CreateAutomationInput, CreateWebhookInput, CreatedWebhook, UpdateAutomationInput, UpdateWebhookInput, Webhook, WebhookSecret } from "../types/schemas/automations.js";
3
- import { AutomationDispatch, AutomationEngine, AutomationExecutor, WebhookRequest, WebhookResponse, WebhookSender } from "../types/automations.js";
3
+ import { AutomationDispatch, AutomationEngine, AutomationExecutor, ScheduledAutomation, WebhookRequest, WebhookResponse, WebhookSender } from "../types/automations.js";
4
4
  import "../types/index.js";
5
5
  import { CredentialStore, OutboxAppender, UnitOfWork } from "../shared/ports.js";
6
6
  import "./model.js";
@@ -8,6 +8,7 @@ import "./types.js";
8
8
  //#region src/automations/ports.d.ts
9
9
  type NewAutomation = CreateAutomationInput & {
10
10
  kind: AutomationKind;
11
+ source?: 'system' | 'user';
11
12
  };
12
13
  /** A run row before persistence assigns its id and event_id. */
13
14
  type NewAutomationRun = Omit<AutomationRun, 'id' | 'eventId'>;
@@ -35,6 +36,7 @@ interface AutomationsService {
35
36
  }
36
37
  interface AutomationsRepository {
37
38
  insert(orgId: string, input: NewAutomation): Promise<Automation>;
39
+ insertSeed(orgId: string, input: NewAutomation): Promise<Automation | null>;
38
40
  update(orgId: string, id: string, patch: UpdateAutomationInput): Promise<Automation | null>;
39
41
  /** Returns the deleted row (the event snapshot), or null if it didn't exist. */
40
42
  delete(orgId: string, id: string): Promise<Automation | null>;
@@ -42,6 +44,7 @@ interface AutomationsRepository {
42
44
  listByOrg(orgId: string, kind?: AutomationKind): Promise<Automation[]>;
43
45
  /** All rows whose triggers include `trigger`, enabled and disabled alike — the service filters to `enabled`. */
44
46
  listByTrigger(orgId: string, trigger: string): Promise<Automation[]>;
47
+ listSchedules(): Promise<ScheduledAutomation[]>;
45
48
  }
46
49
  interface AutomationRunsRepository {
47
50
  /** Keyed by (orgId, automationId, event.id); returns `null` if this event was already run for this automation (duplicate). */
@@ -55,6 +58,7 @@ interface AutomationRunsRepository {
55
58
  finishedAt: Date;
56
59
  }): Promise<AutomationRun | null>;
57
60
  list(orgId: string, automationId: string, query: AutomationRunsQuery): Promise<Page<AutomationRun>>;
61
+ recentRecipients(orgId: string, automationId: string, chatId: string, since: Date): Promise<string[]>;
58
62
  }
59
63
  /** Tx-scoped port bundle for this context's mutating use cases — every member
60
64
  * is bound to the UnitOfWork's transaction, so the write(s) and the event
@@ -0,0 +1,6 @@
1
+ //#region src/automations/schedule.d.ts
2
+ declare function cronMatches(expression: string, at: Date): boolean;
3
+ declare function cronSupported(expression: string): boolean;
4
+ //#endregion
5
+ export { cronMatches, cronSupported };
6
+ //# sourceMappingURL=schedule.d.ts.map