@headless-lms/core 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/events.d.ts +9 -9
- package/dist/assets/index.d.ts +3 -2
- package/dist/assets/index.js +2 -1
- package/dist/assets/jobs.d.ts +15 -0
- package/dist/assets/jobs.js +30 -0
- package/dist/assets/jobs.js.map +1 -0
- package/dist/assets/ports.d.ts +4 -3
- package/dist/assets/service.d.ts +0 -1
- package/dist/assets/service.js +0 -4
- package/dist/assets/service.js.map +1 -1
- package/dist/automations/actions.js +2 -63
- package/dist/automations/actions.js.map +1 -1
- package/dist/automations/catalog.d.ts +1 -2
- package/dist/automations/catalog.js +69 -62
- package/dist/automations/catalog.js.map +1 -1
- package/dist/automations/community-derivations.js +4 -4
- package/dist/automations/community-derivations.js.map +1 -1
- package/dist/automations/community-mail.d.ts +3 -17
- package/dist/automations/community-mail.js +1 -21
- package/dist/automations/community-mail.js.map +1 -1
- package/dist/automations/events.d.ts +0 -10
- package/dist/automations/index.d.ts +11 -4
- package/dist/automations/index.js +8 -1
- package/dist/automations/notifications/debounce.d.ts +25 -0
- package/dist/automations/notifications/debounce.js +93 -0
- package/dist/automations/notifications/debounce.js.map +1 -0
- package/dist/automations/notifications/defaults.d.ts +8 -0
- package/dist/automations/notifications/defaults.js +316 -0
- package/dist/automations/notifications/defaults.js.map +1 -0
- package/dist/automations/notifications/delivery.d.ts +28 -0
- package/dist/automations/notifications/delivery.js +88 -0
- package/dist/automations/notifications/delivery.js.map +1 -0
- package/dist/automations/notifications/processor.d.ts +64 -0
- package/dist/automations/notifications/processor.js +270 -0
- package/dist/automations/notifications/processor.js.map +1 -0
- package/dist/automations/notifications/rules/content.d.ts +6 -0
- package/dist/automations/notifications/rules/content.js +39 -0
- package/dist/automations/notifications/rules/content.js.map +1 -0
- package/dist/automations/notifications/rules/discussion.d.ts +7 -0
- package/dist/automations/notifications/rules/discussion.js +678 -0
- package/dist/automations/notifications/rules/discussion.js.map +1 -0
- package/dist/automations/notifications/rules/entitlements.d.ts +6 -0
- package/dist/automations/notifications/rules/entitlements.js +249 -0
- package/dist/automations/notifications/rules/entitlements.js.map +1 -0
- package/dist/automations/notifications/rules/index.d.ts +6 -0
- package/dist/automations/notifications/rules/index.js +15 -0
- package/dist/automations/notifications/rules/index.js.map +1 -0
- package/dist/automations/notifications/rules/organizations.d.ts +6 -0
- package/dist/automations/notifications/rules/organizations.js +28 -0
- package/dist/automations/notifications/rules/organizations.js.map +1 -0
- package/dist/automations/notifications/rules/recipients.d.ts +32 -0
- package/dist/automations/notifications/rules/recipients.js +99 -0
- package/dist/automations/notifications/rules/recipients.js.map +1 -0
- package/dist/automations/notifications/service.d.ts +27 -0
- package/dist/automations/notifications/service.js +45 -0
- package/dist/automations/notifications/service.js.map +1 -0
- package/dist/automations/notifications/types.d.ts +7 -0
- package/dist/automations/notifications/types.js +473 -0
- package/dist/automations/notifications/types.js.map +1 -0
- package/dist/automations/ports.d.ts +119 -5
- package/dist/automations/service.d.ts +10 -13
- package/dist/automations/service.js +21 -83
- package/dist/automations/service.js.map +1 -1
- package/dist/content/events.d.ts +130 -80
- package/dist/content/events.js +11 -0
- package/dist/content/events.js.map +1 -1
- package/dist/content/index.d.ts +6 -4
- package/dist/content/index.js +3 -1
- package/dist/content/model.d.ts +2 -2
- package/dist/content/notification-categories.d.ts +36 -0
- package/dist/content/notification-categories.js +64 -0
- package/dist/content/notification-categories.js.map +1 -0
- package/dist/content/ports.d.ts +25 -6
- package/dist/content/service.d.ts +4 -4
- package/dist/content/service.js +24 -8
- package/dist/content/service.js.map +1 -1
- package/dist/content/titles.d.ts +22 -0
- package/dist/content/titles.js +69 -0
- package/dist/content/titles.js.map +1 -0
- package/dist/content/types.d.ts +2 -2
- package/dist/discussion/chats.d.ts +9 -4
- package/dist/discussion/chats.js +80 -47
- package/dist/discussion/chats.js.map +1 -1
- package/dist/discussion/digests.d.ts +24 -0
- package/dist/discussion/digests.js +104 -0
- package/dist/discussion/digests.js.map +1 -0
- package/dist/discussion/events.d.ts +194 -28
- package/dist/discussion/events.js +74 -17
- package/dist/discussion/events.js.map +1 -1
- package/dist/discussion/index.d.ts +7 -2
- package/dist/discussion/index.js +6 -1
- package/dist/discussion/jobs.d.ts +16 -0
- package/dist/discussion/jobs.js +40 -0
- package/dist/discussion/jobs.js.map +1 -0
- package/dist/discussion/mentions.d.ts +11 -0
- package/dist/discussion/mentions.js +35 -0
- package/dist/discussion/mentions.js.map +1 -0
- package/dist/discussion/ports.d.ts +107 -24
- package/dist/discussion/ports.js.map +1 -1
- package/dist/discussion/posts.d.ts +18 -8
- package/dist/discussion/posts.js +207 -155
- package/dist/discussion/posts.js.map +1 -1
- package/dist/discussion/service.d.ts +17 -8
- package/dist/discussion/service.js +105 -41
- package/dist/discussion/service.js.map +1 -1
- package/dist/discussion/subscriptions.d.ts +37 -0
- package/dist/discussion/subscriptions.js +99 -0
- package/dist/discussion/subscriptions.js.map +1 -0
- package/dist/discussion/titles.d.ts +26 -0
- package/dist/discussion/titles.js +84 -0
- package/dist/discussion/titles.js.map +1 -0
- package/dist/entitlements/events.d.ts +20 -20
- package/dist/entitlements/index.d.ts +2 -1
- package/dist/entitlements/index.js +2 -1
- package/dist/entitlements/jobs.d.ts +12 -0
- package/dist/entitlements/jobs.js +18 -0
- package/dist/entitlements/jobs.js.map +1 -0
- package/dist/entitlements/ports.d.ts +7 -3
- package/dist/entitlements/service.d.ts +5 -2
- package/dist/entitlements/service.js +23 -3
- package/dist/entitlements/service.js.map +1 -1
- package/dist/identity/devices.d.ts +30 -0
- package/dist/identity/devices.js +107 -0
- package/dist/identity/devices.js.map +1 -0
- package/dist/identity/events.d.ts +2 -2
- package/dist/identity/index.d.ts +5 -4
- package/dist/identity/index.js +3 -2
- package/dist/identity/model.d.ts +2 -2
- package/dist/identity/notification-settings.d.ts +28 -0
- package/dist/identity/notification-settings.js +113 -0
- package/dist/identity/notification-settings.js.map +1 -0
- package/dist/identity/ports.d.ts +78 -54
- package/dist/integrations/events.d.ts +3 -3
- package/dist/organizations/events.d.ts +32 -32
- package/dist/organizations/index.d.ts +2 -1
- package/dist/organizations/index.js +2 -1
- package/dist/organizations/notification-categories.d.ts +32 -0
- package/dist/organizations/notification-categories.js +51 -0
- package/dist/organizations/notification-categories.js.map +1 -0
- package/dist/organizations/ports.d.ts +3 -0
- package/dist/organizations/service.d.ts +2 -0
- package/dist/organizations/service.js +7 -0
- package/dist/organizations/service.js.map +1 -1
- package/dist/progress/events.d.ts +14 -14
- package/dist/reporting/member/index.d.ts +2 -2
- package/dist/reporting/member/model.d.ts +56 -23
- package/dist/reporting/member/ports.d.ts +9 -6
- package/dist/reporting/member/service.d.ts +33 -14
- package/dist/reporting/member/service.js +138 -60
- package/dist/reporting/member/service.js.map +1 -1
- package/dist/shared/errors.d.ts +13 -1
- package/dist/shared/errors.js +18 -1
- package/dist/shared/errors.js.map +1 -1
- package/dist/shared/id.d.ts +3 -0
- package/dist/shared/id.js +4 -1
- package/dist/shared/id.js.map +1 -1
- package/dist/shared/mailer.d.ts +11 -1
- package/dist/shared/mailer.js +32 -5
- package/dist/shared/mailer.js.map +1 -1
- package/dist/shared/notification-categories.d.ts +16 -0
- package/dist/shared/notification-categories.js +94 -0
- package/dist/shared/notification-categories.js.map +1 -0
- package/dist/shared/notification-settings.d.ts +14 -0
- package/dist/shared/notification-settings.js +61 -0
- package/dist/shared/notification-settings.js.map +1 -0
- package/dist/shared/ports.d.ts +48 -2
- package/dist/shared/settings.d.ts +2 -1
- package/dist/shared/settings.js +1 -0
- package/dist/shared/settings.js.map +1 -1
- package/dist/types/automations.d.ts +8 -2
- package/dist/types/content.d.ts +2 -2
- package/dist/types/discussion.d.ts +2 -2
- package/dist/types/email-templates.d.ts +97 -3
- package/dist/types/identity.d.ts +2 -2
- package/dist/types/index.d.ts +7 -6
- package/dist/types/notifications.d.ts +157 -0
- package/dist/types/notifications.js +1 -0
- package/dist/types/schemas/automations.d.ts +1 -10
- package/dist/types/schemas/automations.js +1 -3
- package/dist/types/schemas/automations.js.map +1 -1
- package/dist/types/schemas/content.d.ts +62 -1
- package/dist/types/schemas/content.js +25 -3
- package/dist/types/schemas/content.js.map +1 -1
- package/dist/types/schemas/discussion.d.ts +85 -12
- package/dist/types/schemas/discussion.js +47 -13
- package/dist/types/schemas/discussion.js.map +1 -1
- package/dist/types/schemas/identity.d.ts +1 -85
- package/dist/types/schemas/identity.js +1 -42
- package/dist/types/schemas/identity.js.map +1 -1
- package/dist/types/schemas/index.d.ts +6 -5
- package/dist/types/schemas/index.js +6 -5
- package/dist/types/schemas/notifications.d.ts +95 -0
- package/dist/types/schemas/notifications.js +56 -0
- package/dist/types/schemas/notifications.js.map +1 -0
- package/dist/types/schemas/progress.d.ts +3 -3
- package/package.json +1 -1
- package/dist/automations/community-notifications.d.ts +0 -16
- package/dist/automations/community-notifications.js +0 -84
- package/dist/automations/community-notifications.js.map +0 -1
- package/dist/automations/seeds.d.ts +0 -17
- package/dist/automations/seeds.js +0 -235
- package/dist/automations/seeds.js.map +0 -1
- package/dist/identity/notifications.d.ts +0 -27
- package/dist/identity/notifications.js +0 -101
- package/dist/identity/notifications.js.map +0 -1
|
@@ -1,19 +1,134 @@
|
|
|
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, ScheduledAutomation, WebhookRequest, WebhookResponse, WebhookSender } from "../types/automations.js";
|
|
3
|
+
import { AutomationDispatch, AutomationEngine, AutomationExecutor, DeliveryDispatch, ScheduledAutomation, WebhookRequest, WebhookResponse, WebhookSender } from "../types/automations.js";
|
|
4
|
+
import { Delivery, NotificationCategory, NotificationFilter, RecipientList, StoredNotification, SubjectRef } from "../types/notifications.js";
|
|
4
5
|
import "../types/index.js";
|
|
5
6
|
import { CredentialStore, OutboxAppender, UnitOfWork } from "../shared/ports.js";
|
|
6
7
|
import "./model.js";
|
|
7
8
|
import "./types.js";
|
|
9
|
+
import { CommunityMailReads } from "./community-mail.js";
|
|
8
10
|
//#region src/automations/ports.d.ts
|
|
9
11
|
type NewAutomation = CreateAutomationInput & {
|
|
10
12
|
kind: AutomationKind;
|
|
11
|
-
source?: 'system' | 'user';
|
|
12
13
|
};
|
|
14
|
+
interface NotificationCategories {
|
|
15
|
+
listFor(orgId: string, communityId: string | null): Promise<NotificationCategory[]>;
|
|
16
|
+
}
|
|
17
|
+
interface NotificationReads extends Omit<CommunityMailReads, 'recentMessageRecipients'> {
|
|
18
|
+
threadParticipants(orgId: string, item: SubjectRef): Promise<string[]>;
|
|
19
|
+
mutedAmong(orgId: string, item: SubjectRef, orgUserIds: string[], at: Date): Promise<string[]>;
|
|
20
|
+
communityMembers(orgId: string, communityId: string): Promise<string[]>;
|
|
21
|
+
switchedOn(orgId: string, communityId: string, category: string, spaceId: string | null): Promise<string[]>;
|
|
22
|
+
courseHolders(orgId: string, courseId: string): Promise<string[]>;
|
|
23
|
+
contentLostBy(orgId: string, grant: {
|
|
24
|
+
orgUserId: string;
|
|
25
|
+
contentId: string | null;
|
|
26
|
+
bundleId: string | null;
|
|
27
|
+
}): Promise<string[]>;
|
|
28
|
+
activity(orgId: string, activityId: string): Promise<{
|
|
29
|
+
title: string;
|
|
30
|
+
moduleTitle: string;
|
|
31
|
+
courseId: string;
|
|
32
|
+
courseTitle: string;
|
|
33
|
+
} | null>;
|
|
34
|
+
userIdsOf(orgId: string, orgUserIds: string[]): Promise<Record<string, string>>;
|
|
35
|
+
emailOf(orgId: string, userId: string): Promise<string | null>;
|
|
36
|
+
contentInfo(orgId: string, item: {
|
|
37
|
+
contentId: string;
|
|
38
|
+
} | {
|
|
39
|
+
bundleId: string;
|
|
40
|
+
}): Promise<{
|
|
41
|
+
title: string;
|
|
42
|
+
type: string;
|
|
43
|
+
} | null>;
|
|
44
|
+
message(orgId: string, messageId: string): Promise<{
|
|
45
|
+
orgUserId: string;
|
|
46
|
+
chatId: string;
|
|
47
|
+
body: string;
|
|
48
|
+
} | null>;
|
|
49
|
+
postsPublished(orgId: string, communityId: string, from: Date, to: Date): Promise<{
|
|
50
|
+
postId: string;
|
|
51
|
+
spaceId: string;
|
|
52
|
+
title: string;
|
|
53
|
+
authorName: string;
|
|
54
|
+
spaceName: string;
|
|
55
|
+
commentCount: number;
|
|
56
|
+
}[]>;
|
|
57
|
+
unreadInChat(orgId: string, chatId: string, orgUserId: string): Promise<{
|
|
58
|
+
count: number;
|
|
59
|
+
latest: {
|
|
60
|
+
messageId: string;
|
|
61
|
+
senderOrgUserId: string;
|
|
62
|
+
body: string;
|
|
63
|
+
} | null;
|
|
64
|
+
}>;
|
|
65
|
+
}
|
|
66
|
+
type RecipientRule = (event: DomainEvent, reads: NotificationReads) => Promise<RecipientList[]>;
|
|
67
|
+
interface ChatDebounce {
|
|
68
|
+
arm(orgId: string, list: RecipientList, occurredAt: Date): Promise<void>;
|
|
69
|
+
mentioned(orgId: string, userId: string, chatId: string): Promise<void>;
|
|
70
|
+
}
|
|
71
|
+
interface NewNotification extends Omit<StoredNotification, 'orgId'> {
|
|
72
|
+
sourceEventId: string | null;
|
|
73
|
+
inApp: boolean;
|
|
74
|
+
}
|
|
75
|
+
interface NewDelivery {
|
|
76
|
+
id: string;
|
|
77
|
+
notificationId: string;
|
|
78
|
+
channel: string;
|
|
79
|
+
}
|
|
80
|
+
type ListedNotification = StoredNotification & {
|
|
81
|
+
readAt: Date | null;
|
|
82
|
+
};
|
|
83
|
+
interface NotificationPage {
|
|
84
|
+
items: ListedNotification[];
|
|
85
|
+
total: number;
|
|
86
|
+
unreadCount: number;
|
|
87
|
+
}
|
|
88
|
+
interface UnreadCounts {
|
|
89
|
+
org: number;
|
|
90
|
+
communities: {
|
|
91
|
+
communityId: string;
|
|
92
|
+
unread: number;
|
|
93
|
+
}[];
|
|
94
|
+
}
|
|
95
|
+
interface NotificationsService {
|
|
96
|
+
list(orgId: string, userId: string, filter: NotificationFilter, page: {
|
|
97
|
+
page: number;
|
|
98
|
+
pageSize: number;
|
|
99
|
+
}): Promise<NotificationPage>;
|
|
100
|
+
get(orgId: string, userId: string, notificationId: string): Promise<ListedNotification | null>;
|
|
101
|
+
unreadCounts(orgId: string, userId: string, communityIds: string[]): Promise<UnreadCounts>;
|
|
102
|
+
markRead(orgId: string, userId: string, notificationId: string): Promise<void>;
|
|
103
|
+
markAllRead(orgId: string, userId: string, filter: NotificationFilter): Promise<{
|
|
104
|
+
marked: number;
|
|
105
|
+
}>;
|
|
106
|
+
}
|
|
107
|
+
interface NotificationsRepository {
|
|
108
|
+
insert(orgId: string, rows: NewNotification[], deliveries: NewDelivery[]): Promise<void>;
|
|
109
|
+
pendingDeliveriesForEvent(orgId: string, sourceEventId: string): Promise<{
|
|
110
|
+
id: string;
|
|
111
|
+
}[]>;
|
|
112
|
+
findDelivery(orgId: string, deliveryId: string): Promise<{
|
|
113
|
+
delivery: Delivery;
|
|
114
|
+
notification: StoredNotification;
|
|
115
|
+
} | null>;
|
|
116
|
+
markSent(orgId: string, deliveryId: string, at: Date): Promise<void>;
|
|
117
|
+
recordError(orgId: string, deliveryId: string, error: string): Promise<void>;
|
|
118
|
+
markFailed(orgId: string, deliveryId: string, error: string): Promise<void>;
|
|
119
|
+
page(orgId: string, userId: string, filter: NotificationFilter, page: {
|
|
120
|
+
page: number;
|
|
121
|
+
pageSize: number;
|
|
122
|
+
}): Promise<NotificationPage>;
|
|
123
|
+
find(orgId: string, userId: string, notificationId: string): Promise<ListedNotification | null>;
|
|
124
|
+
unreadCounts(orgId: string, userId: string, communityIds: string[]): Promise<UnreadCounts>;
|
|
125
|
+
markRead(orgId: string, userId: string, notificationId: string, at: Date): Promise<boolean>;
|
|
126
|
+
markAllRead(orgId: string, userId: string, filter: NotificationFilter, at: Date): Promise<number>;
|
|
127
|
+
}
|
|
13
128
|
/** A run row before persistence assigns its id and event_id. */
|
|
14
129
|
type NewAutomationRun = Omit<AutomationRun, 'id' | 'eventId'>;
|
|
15
130
|
interface AutomationsService {
|
|
16
|
-
/**
|
|
131
|
+
/** Dispatches a run per enabled automation the event matches, then runs the notification process; throws only when the notification process fails, so the outbox redelivers. */
|
|
17
132
|
handle(event: DomainEvent): Promise<void>;
|
|
18
133
|
/** Built-in action types plus every loaded integration's declared actions. */
|
|
19
134
|
availableActions(): AvailableActions;
|
|
@@ -36,7 +151,6 @@ interface AutomationsService {
|
|
|
36
151
|
}
|
|
37
152
|
interface AutomationsRepository {
|
|
38
153
|
insert(orgId: string, input: NewAutomation): Promise<Automation>;
|
|
39
|
-
insertSeed(orgId: string, input: NewAutomation): Promise<Automation | null>;
|
|
40
154
|
update(orgId: string, id: string, patch: UpdateAutomationInput): Promise<Automation | null>;
|
|
41
155
|
/** Returns the deleted row (the event snapshot), or null if it didn't exist. */
|
|
42
156
|
delete(orgId: string, id: string): Promise<Automation | null>;
|
|
@@ -71,5 +185,5 @@ interface AutomationsTxScope {
|
|
|
71
185
|
}
|
|
72
186
|
type AutomationsUnitOfWork = UnitOfWork<AutomationsTxScope>;
|
|
73
187
|
//#endregion
|
|
74
|
-
export { type AutomationDispatch, type AutomationEngine, type AutomationExecutor, AutomationRunsRepository, AutomationsRepository, AutomationsService, AutomationsTxScope, AutomationsUnitOfWork, NewAutomation, NewAutomationRun, type WebhookRequest, type WebhookResponse, type WebhookSender };
|
|
188
|
+
export { type AutomationDispatch, type AutomationEngine, type AutomationExecutor, AutomationRunsRepository, AutomationsRepository, AutomationsService, AutomationsTxScope, AutomationsUnitOfWork, ChatDebounce, type DeliveryDispatch, ListedNotification, NewAutomation, NewAutomationRun, NewDelivery, NewNotification, NotificationCategories, NotificationPage, NotificationReads, NotificationsRepository, NotificationsService, RecipientRule, UnreadCounts, type WebhookRequest, type WebhookResponse, type WebhookSender };
|
|
75
189
|
//# sourceMappingURL=ports.d.ts.map
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { DomainEvent, Page } from "../types/shared.js";
|
|
2
2
|
import { Logger } from "../types/ports.js";
|
|
3
3
|
import { Automation, AutomationActionResult, AutomationRun, AutomationRunsQuery, AutomationsQuery, AvailableActions, AvailableTriggers, CreateAutomationInput, CreateWebhookInput, CreatedWebhook, UpdateAutomationInput, UpdateWebhookInput, Webhook, WebhookSecret } from "../types/schemas/automations.js";
|
|
4
|
-
import { AutomationDispatch, AutomationEngine, AutomationExecutor, ScheduledAutomation, WebhookSender } from "../types/automations.js";
|
|
4
|
+
import { AutomationDispatch, AutomationEngine, AutomationExecutor, DeliveryDispatch, ScheduledAutomation, WebhookSender } from "../types/automations.js";
|
|
5
5
|
import "../types/index.js";
|
|
6
6
|
import { CredentialStore } from "../shared/ports.js";
|
|
7
7
|
import "./model.js";
|
|
8
|
-
import { NotificationDesk } from "../identity/ports.js";
|
|
9
8
|
import { Mailer, MailerLookups } from "../shared/mailer.js";
|
|
10
|
-
import "../identity/index.js";
|
|
11
9
|
import "./types.js";
|
|
12
10
|
import { AutomationRunsRepository, AutomationsRepository, AutomationsService, AutomationsUnitOfWork } from "./ports.js";
|
|
13
|
-
import { CommunityMailReads, GrantWriter
|
|
11
|
+
import { CommunityMailReads, GrantWriter } from "./community-mail.js";
|
|
12
|
+
import { NotificationDelivery } from "./notifications/delivery.js";
|
|
13
|
+
import { NotificationProcessor } from "./notifications/processor.js";
|
|
14
14
|
import { IntegrationsService } from "../integrations/ports.js";
|
|
15
15
|
import "../integrations/index.js";
|
|
16
16
|
//#region src/automations/service.d.ts
|
|
@@ -28,8 +28,8 @@ type AutomationsServiceParams = {
|
|
|
28
28
|
webhooks: WebhookSender;
|
|
29
29
|
mail: Omit<CommunityMailReads, 'recentMessageRecipients'>;
|
|
30
30
|
grants: GrantWriter;
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
delivery: Pick<NotificationDelivery, 'run'>;
|
|
32
|
+
processor: Pick<NotificationProcessor, 'process'>;
|
|
33
33
|
logger?: Logger;
|
|
34
34
|
};
|
|
35
35
|
declare class AutomationsServiceImpl implements AutomationsService, AutomationExecutor {
|
|
@@ -44,25 +44,22 @@ declare class AutomationsServiceImpl implements AutomationsService, AutomationEx
|
|
|
44
44
|
private readonly webhooks;
|
|
45
45
|
private readonly mail;
|
|
46
46
|
private readonly grants;
|
|
47
|
-
private readonly
|
|
48
|
-
private readonly
|
|
47
|
+
private readonly delivery;
|
|
48
|
+
private readonly processor;
|
|
49
49
|
private readonly logger;
|
|
50
50
|
constructor(params: AutomationsServiceParams);
|
|
51
51
|
handle(event: DomainEvent): Promise<void>;
|
|
52
|
-
private seedsFor;
|
|
53
|
-
private seedFor;
|
|
54
52
|
private reconcileSchedules;
|
|
55
53
|
private safeListByTrigger;
|
|
56
54
|
private dispatchOne;
|
|
57
55
|
private openRun;
|
|
58
56
|
private dispatchFor;
|
|
59
|
-
/** Best-effort: logs and never rethrows
|
|
57
|
+
/** Best-effort: logs and never rethrows, so an org automation never fails `handle`. */
|
|
60
58
|
private recordFailure;
|
|
61
59
|
runAction(d: AutomationDispatch, index: number): Promise<AutomationActionResult>;
|
|
60
|
+
runDelivery(d: DeliveryDispatch, lastAttempt: boolean): Promise<void>;
|
|
62
61
|
listSchedules(): Promise<ScheduledAutomation[]>;
|
|
63
62
|
fireSchedule(orgId: string, automationId: string, at: Date): Promise<void>;
|
|
64
|
-
private scheduleHasWork;
|
|
65
|
-
private pendingWork;
|
|
66
63
|
finalize(d: AutomationDispatch, results: AutomationActionResult[]): Promise<void>;
|
|
67
64
|
private logOutcome;
|
|
68
65
|
create(orgId: string, input: CreateAutomationInput): Promise<Automation>;
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { deliverWebhookInputSchema } from "../types/schemas/automations.js";
|
|
2
|
-
import { ConflictError, NotFoundError } from "../shared/errors.js";
|
|
2
|
+
import { ConflictError, InvalidInputError, NotFoundError } from "../shared/errors.js";
|
|
3
3
|
import { noopLogger } from "../shared/logger.js";
|
|
4
4
|
import { ActionFailedError, AutomationKindMismatchError, InvalidTriggerError, ReservedActionError } from "./model.js";
|
|
5
5
|
import { DELIVER_WEBHOOK_ACTION, catalogActions, catalogTriggers, publicDataSchema } from "./catalog.js";
|
|
6
6
|
import { generateWebhookSecret } from "./webhook-signing.js";
|
|
7
7
|
import { executeAction } from "./actions.js";
|
|
8
8
|
import { automationEvents } from "./events.js";
|
|
9
|
-
import { communitySeeds, orgSeeds } from "./seeds.js";
|
|
10
9
|
import { cronSupported } from "./schedule.js";
|
|
11
10
|
//#region src/automations/service.ts
|
|
12
11
|
/** True only when `input` sets `enabled` and nothing else — emits automation.enabled|disabled instead of automation.updated. */
|
|
@@ -23,11 +22,6 @@ function floorMinute(at) {
|
|
|
23
22
|
function unique(values) {
|
|
24
23
|
return [...new Set(values)];
|
|
25
24
|
}
|
|
26
|
-
function recordString(value, key) {
|
|
27
|
-
if (typeof value !== "object" || value === null || !(key in value)) return null;
|
|
28
|
-
const found = value[key];
|
|
29
|
-
return typeof found === "string" ? found : null;
|
|
30
|
-
}
|
|
31
25
|
function filterMatches(filter, data) {
|
|
32
26
|
if (filter == null) return true;
|
|
33
27
|
if (typeof data !== "object" || data === null || Array.isArray(data)) return false;
|
|
@@ -48,8 +42,18 @@ function assertCatalogTriggers(triggers, schedules) {
|
|
|
48
42
|
const known = new Set(catalogTriggers().map((t) => t.type));
|
|
49
43
|
for (const trigger of triggers) if (!(schedules && trigger.startsWith("schedule:")) && !known.has(trigger)) throw new InvalidTriggerError(trigger, "unknown");
|
|
50
44
|
}
|
|
45
|
+
const REMOVED_ACTIONS = /* @__PURE__ */ new Set([
|
|
46
|
+
"notify",
|
|
47
|
+
"publishDuePosts",
|
|
48
|
+
"expireDueGrants",
|
|
49
|
+
"sendDigest",
|
|
50
|
+
"removeUnattachedUploads"
|
|
51
|
+
]);
|
|
51
52
|
function assertAuthorableActions(actions) {
|
|
52
|
-
for (const action of actions)
|
|
53
|
+
for (const action of actions) {
|
|
54
|
+
if (action.type === "deliverWebhook") throw new ReservedActionError(action.type);
|
|
55
|
+
if (REMOVED_ACTIONS.has(action.type)) throw new InvalidInputError(`The action "${action.type}" is no longer available`);
|
|
56
|
+
}
|
|
53
57
|
}
|
|
54
58
|
const SCHEDULE_FIRED = "automation.schedule.fired";
|
|
55
59
|
function publicEventData(event) {
|
|
@@ -128,8 +132,8 @@ var AutomationsServiceImpl = class {
|
|
|
128
132
|
webhooks;
|
|
129
133
|
mail;
|
|
130
134
|
grants;
|
|
131
|
-
|
|
132
|
-
|
|
135
|
+
delivery;
|
|
136
|
+
processor;
|
|
133
137
|
logger;
|
|
134
138
|
constructor(params) {
|
|
135
139
|
this.repo = params.repo;
|
|
@@ -146,13 +150,12 @@ var AutomationsServiceImpl = class {
|
|
|
146
150
|
recentMessageRecipients: (orgId, automationId, chatId, since) => this.runsRepo.recentRecipients(orgId, automationId, chatId, since)
|
|
147
151
|
};
|
|
148
152
|
this.grants = params.grants;
|
|
149
|
-
this.
|
|
150
|
-
this.
|
|
153
|
+
this.delivery = params.delivery;
|
|
154
|
+
this.processor = params.processor;
|
|
151
155
|
this.logger = params.logger ?? noopLogger;
|
|
152
156
|
}
|
|
153
157
|
async handle(event) {
|
|
154
158
|
if (event.type.startsWith("automation.")) return;
|
|
155
|
-
await this.seedFor(event);
|
|
156
159
|
const automations = await this.safeListByTrigger(event);
|
|
157
160
|
for (const automation of automations) {
|
|
158
161
|
const matches = filterMatches(automation.filter, event.data);
|
|
@@ -166,50 +169,7 @@ var AutomationsServiceImpl = class {
|
|
|
166
169
|
}
|
|
167
170
|
await this.dispatchOne(automation, event);
|
|
168
171
|
}
|
|
169
|
-
|
|
170
|
-
seedsFor(event) {
|
|
171
|
-
if (event.type === "organization.created") return orgSeeds();
|
|
172
|
-
if (event.type !== "content.community.created") return null;
|
|
173
|
-
const communityId = recordString(event.data, "id");
|
|
174
|
-
if (!communityId) {
|
|
175
|
-
this.logger.error("community seed missing id", {
|
|
176
|
-
orgId: event.orgId,
|
|
177
|
-
eventId: event.id
|
|
178
|
-
});
|
|
179
|
-
return null;
|
|
180
|
-
}
|
|
181
|
-
return communitySeeds(communityId);
|
|
182
|
-
}
|
|
183
|
-
async seedFor(event) {
|
|
184
|
-
const rows = this.seedsFor(event);
|
|
185
|
-
if (!rows) return;
|
|
186
|
-
if (await this.uow.run(async ({ automations, outbox }) => {
|
|
187
|
-
const created = [];
|
|
188
|
-
for (const row of rows) {
|
|
189
|
-
const inserted = await automations.insertSeed(event.orgId, {
|
|
190
|
-
name: row.name,
|
|
191
|
-
description: row.description,
|
|
192
|
-
triggers: row.triggers,
|
|
193
|
-
actions: row.actions,
|
|
194
|
-
filter: row.filter,
|
|
195
|
-
kind: "workflow",
|
|
196
|
-
source: "system"
|
|
197
|
-
});
|
|
198
|
-
if (inserted) created.push(automationEvents.automationCreated.make({
|
|
199
|
-
orgId: event.orgId,
|
|
200
|
-
data: inserted
|
|
201
|
-
}));
|
|
202
|
-
}
|
|
203
|
-
if (created.length > 0) {
|
|
204
|
-
await outbox.append(created);
|
|
205
|
-
this.logger.info("system automations seeded", {
|
|
206
|
-
orgId: event.orgId,
|
|
207
|
-
trigger: event.type,
|
|
208
|
-
count: created.length
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
return created.length;
|
|
212
|
-
}) > 0) await this.reconcileSchedules(event.orgId);
|
|
172
|
+
await this.processor.process(event);
|
|
213
173
|
}
|
|
214
174
|
async reconcileSchedules(orgId) {
|
|
215
175
|
try {
|
|
@@ -283,7 +243,7 @@ var AutomationsServiceImpl = class {
|
|
|
283
243
|
event
|
|
284
244
|
};
|
|
285
245
|
}
|
|
286
|
-
/** Best-effort: logs and never rethrows
|
|
246
|
+
/** Best-effort: logs and never rethrows, so an org automation never fails `handle`. */
|
|
287
247
|
async recordFailure(run, error) {
|
|
288
248
|
try {
|
|
289
249
|
return await this.uow.run(async ({ runs, outbox }) => {
|
|
@@ -321,8 +281,6 @@ var AutomationsServiceImpl = class {
|
|
|
321
281
|
webhooks: this.webhooks,
|
|
322
282
|
mail: this.mail,
|
|
323
283
|
grants: this.grants,
|
|
324
|
-
desk: this.desk,
|
|
325
|
-
scheduledWork: this.scheduledWork,
|
|
326
284
|
automationId: d.automationId,
|
|
327
285
|
actionInput: action.input,
|
|
328
286
|
logger: this.logger
|
|
@@ -356,6 +314,9 @@ var AutomationsServiceImpl = class {
|
|
|
356
314
|
...failed.length > 0 ? { error: `delivery failed for ${failed.length} recipients: ${failed.join(", ")}` } : {}
|
|
357
315
|
};
|
|
358
316
|
}
|
|
317
|
+
runDelivery(d, lastAttempt) {
|
|
318
|
+
return this.delivery.run(d, lastAttempt);
|
|
319
|
+
}
|
|
359
320
|
async listSchedules() {
|
|
360
321
|
return this.repo.listSchedules();
|
|
361
322
|
}
|
|
@@ -369,13 +330,6 @@ var AutomationsServiceImpl = class {
|
|
|
369
330
|
});
|
|
370
331
|
return;
|
|
371
332
|
}
|
|
372
|
-
if (!await this.scheduleHasWork(orgId, automation, firedAt)) {
|
|
373
|
-
this.logger.debug("schedule idle", {
|
|
374
|
-
orgId,
|
|
375
|
-
automationId
|
|
376
|
-
});
|
|
377
|
-
return;
|
|
378
|
-
}
|
|
379
333
|
const event = {
|
|
380
334
|
type: SCHEDULE_FIRED,
|
|
381
335
|
id: `schedule_${automationId}_${firedAt.toISOString()}`,
|
|
@@ -389,21 +343,6 @@ var AutomationsServiceImpl = class {
|
|
|
389
343
|
};
|
|
390
344
|
await this.dispatchOne(automation, event);
|
|
391
345
|
}
|
|
392
|
-
async scheduleHasWork(orgId, automation, firedAt) {
|
|
393
|
-
const checks = automation.actions.map((action) => this.pendingWork(action.type));
|
|
394
|
-
const known = checks.filter((check) => check !== null);
|
|
395
|
-
if (checks.length === 0 || known.length < checks.length) return true;
|
|
396
|
-
for (const check of known) if (await check(orgId, firedAt)) return true;
|
|
397
|
-
return false;
|
|
398
|
-
}
|
|
399
|
-
pendingWork(type) {
|
|
400
|
-
switch (type) {
|
|
401
|
-
case "publishDuePosts": return (orgId, now) => this.scheduledWork.hasDuePosts(orgId, now);
|
|
402
|
-
case "expireDueGrants": return (orgId, now) => this.scheduledWork.hasDueGrants(orgId, now);
|
|
403
|
-
case "removeUnattachedUploads": return (orgId, now) => this.scheduledWork.hasUnattachedUploads(orgId, now);
|
|
404
|
-
default: return null;
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
346
|
async finalize(d, results) {
|
|
408
347
|
const status = results.length === d.actions.length && results.every((r) => r.status === "completed") ? "completed" : "failed";
|
|
409
348
|
await this.uow.run(async ({ runs, outbox }) => {
|
|
@@ -508,7 +447,6 @@ var AutomationsServiceImpl = class {
|
|
|
508
447
|
const existing = await this.repo.findById(orgId, id);
|
|
509
448
|
if (!existing) return false;
|
|
510
449
|
if (existing.kind !== "workflow") throw new AutomationKindMismatchError(id, existing.kind);
|
|
511
|
-
if (existing.source === "system") throw new ConflictError("A system automation cannot be deleted");
|
|
512
450
|
const deleted = await this.uow.run(async ({ automations, outbox }) => {
|
|
513
451
|
const removed = await automations.delete(orgId, id);
|
|
514
452
|
if (!removed) return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","names":[],"sources":["../../src/automations/service.ts"],"sourcesContent":["// automations context — service implementation (inbound port) and the\n// AutomationExecutor the engine drives.\n//\n// `handle` is subscribed to all events on the EventBus: it matches an event\n// against enabled automations for its trigger, opens one run per match, and\n// hands it to the injected AutomationEngine, which calls back into\n// `runAction`/`finalize`.\nimport {\n catalogActions,\n catalogTriggers,\n DELIVER_WEBHOOK_ACTION,\n publicDataSchema,\n} from './catalog.js';\nimport { executeAction, type ActionOutcome } from './actions.js';\nimport { generateWebhookSecret } from './webhook-signing.js';\nimport type { CommunityMailReads, GrantWriter, ScheduledWork } from './community-mail.js';\nimport type { NotificationDesk } from '../identity/index.js';\nimport { communitySeeds, orgSeeds, type SeedAutomation } from './seeds.js';\nimport { cronSupported } from './schedule.js';\nimport {\n ActionFailedError,\n AutomationKindMismatchError,\n InvalidTriggerError,\n ReservedActionError,\n type Automation,\n type AutomationAction,\n type AutomationActionResult,\n type AutomationRun,\n type CreatedWebhook,\n type DeliverWebhookInput,\n type Page,\n type Webhook,\n type WebhookSecret,\n} from './model.js';\nimport type {\n AutomationRunsQuery,\n AutomationsQuery,\n AvailableActions,\n AvailableTriggers,\n CreateAutomationInput,\n CreateWebhookInput,\n UpdateAutomationInput,\n UpdateWebhookInput,\n} from './types.js';\nimport type {\n AutomationDispatch,\n AutomationEngine,\n AutomationExecutor,\n AutomationRunsRepository,\n AutomationsRepository,\n AutomationsService,\n AutomationsUnitOfWork,\n WebhookSender,\n} from './ports.js';\nimport type { CredentialStore, DomainEvent, Logger } from '../shared/ports.js';\nimport type { JsonValue, ScheduledAutomation } from '../types/index.js';\nimport { deliverWebhookInputSchema } from '../types/schemas/index.js';\nimport { ConflictError, NotFoundError } from '../shared/errors.js';\nimport { noopLogger } from '../shared/logger.js';\nimport type { Mailer, MailerLookups } from '../shared/mailer.js';\nimport type { IntegrationsService } from '../integrations/index.js';\nimport { automationEvents } from './events.js';\n\n/** True only when `input` sets `enabled` and nothing else — emits automation.enabled|disabled instead of automation.updated. */\nfunction isEnabledOnlyUpdate<T extends { enabled?: boolean }>(\n input: T,\n): input is T & { enabled: boolean } {\n if (input.enabled === undefined) {\n return false;\n }\n const { enabled: _enabled, ...rest } = input;\n return Object.values(rest).every((v) => v === undefined);\n}\n\nfunction floorMinute(at: Date): number {\n const copy = new Date(at);\n copy.setUTCSeconds(0, 0);\n return copy.getTime();\n}\n\nfunction unique(values: string[]): string[] {\n return [...new Set(values)];\n}\n\nfunction recordString(value: unknown, key: string): string | null {\n if (typeof value !== 'object' || value === null || !(key in value)) {\n return null;\n }\n const found = (value as Record<string, unknown>)[key];\n return typeof found === 'string' ? found : null;\n}\n\nfunction filterMatches(filter: Record<string, JsonValue> | null, data: unknown): boolean {\n if (filter == null) {\n return true;\n }\n if (typeof data !== 'object' || data === null || Array.isArray(data)) {\n return false;\n }\n const record = data as Record<string, unknown>;\n return Object.entries(filter).every(([key, expected]) => {\n if (!Object.prototype.hasOwnProperty.call(record, key)) {\n return false;\n }\n return JSON.stringify(record[key]) === JSON.stringify(expected);\n });\n}\n\nfunction assertAuthorableTriggers(triggers: string[]): void {\n for (const trigger of triggers) {\n if (trigger.startsWith('automation.')) {\n throw new InvalidTriggerError(trigger);\n }\n if (trigger.startsWith('schedule:') && !cronSupported(trigger.slice('schedule:'.length))) {\n throw new InvalidTriggerError(trigger, 'schedule');\n }\n }\n}\n\nfunction assertCatalogTriggers(triggers: string[], schedules: boolean): void {\n assertAuthorableTriggers(triggers);\n const known = new Set(catalogTriggers().map((t) => t.type));\n for (const trigger of triggers) {\n if (!(schedules && trigger.startsWith('schedule:')) && !known.has(trigger)) {\n throw new InvalidTriggerError(trigger, 'unknown');\n }\n }\n}\n\nfunction assertAuthorableActions(actions: AutomationAction[]): void {\n for (const action of actions) {\n if (action.type === DELIVER_WEBHOOK_ACTION) {\n throw new ReservedActionError(action.type);\n }\n }\n}\n\nconst SCHEDULE_FIRED = 'automation.schedule.fired';\n\nfunction publicEventData(event: DomainEvent): JsonValue {\n const parsed =\n event.type === SCHEDULE_FIRED\n ? { success: true as const, data: event.data }\n : publicDataSchema(event.type)?.safeParse(event.data);\n return parsed?.success ? (JSON.parse(JSON.stringify(parsed.data)) as JsonValue) : null;\n}\n\n/** A run as callers see it: its event carries only what a webhook of that event would. */\nfunction toRunView(run: AutomationRun): AutomationRun {\n const { type, version, id, orgId, occurredAt } = run.event;\n return {\n ...run,\n event: { type, version, id, orgId, occurredAt, data: publicEventData(run.event) },\n };\n}\n\n/** An automation as callers see it: a webhook's credential reference stays internal. */\nfunction toAutomationView(automation: Automation): Automation {\n return {\n ...automation,\n actions: automation.actions.map((action) => {\n if (action.type !== DELIVER_WEBHOOK_ACTION) {\n return action;\n }\n const { secretRef: _secretRef, ...input } = action.input;\n return { ...action, input };\n }),\n };\n}\n\nfunction webhookTarget(automation: Automation): DeliverWebhookInput {\n return deliverWebhookInputSchema.parse(automation.actions[0]?.input);\n}\n\nfunction toWebhook(automation: Automation): Webhook {\n return {\n orgId: automation.orgId,\n id: automation.id,\n url: webhookTarget(automation).url,\n events: automation.triggers,\n description: automation.description,\n enabled: automation.enabled,\n createdAt: automation.createdAt,\n updatedAt: automation.updatedAt,\n };\n}\n\nfunction updateEvent(orgId: string, input: { enabled?: boolean }, updated: Automation) {\n if (isEnabledOnlyUpdate(input)) {\n return input.enabled\n ? automationEvents.automationEnabled.make({ orgId, data: updated })\n : automationEvents.automationDisabled.make({ orgId, data: updated });\n }\n return automationEvents.automationUpdated.make({ orgId, data: updated });\n}\n\nexport type AutomationsServiceParams = {\n /** Read-only access (list/get/available) — runs outside any transaction. */\n repo: AutomationsRepository;\n runsRepo: AutomationRunsRepository;\n /** Atomic write scope: tx-bound repos + credential store + outbox appender. */\n uow: AutomationsUnitOfWork;\n engine: AutomationEngine;\n mailer: Pick<Mailer, 'send'>;\n lookups: MailerLookups;\n integrations: Pick<IntegrationsService, 'available'>;\n credentials: CredentialStore;\n webhooks: WebhookSender;\n mail: Omit<CommunityMailReads, 'recentMessageRecipients'>;\n grants: GrantWriter;\n desk: NotificationDesk;\n scheduledWork: ScheduledWork;\n logger?: Logger;\n};\n\nexport class AutomationsServiceImpl implements AutomationsService, AutomationExecutor {\n private readonly repo: AutomationsRepository;\n private readonly runsRepo: AutomationRunsRepository;\n private readonly uow: AutomationsUnitOfWork;\n private readonly engine: AutomationEngine;\n private readonly mailer: Pick<Mailer, 'send'>;\n private readonly lookups: MailerLookups;\n private readonly integrations: Pick<IntegrationsService, 'available'>;\n private readonly credentials: CredentialStore;\n private readonly webhooks: WebhookSender;\n private readonly mail: CommunityMailReads;\n private readonly grants: GrantWriter;\n private readonly desk: NotificationDesk;\n private readonly scheduledWork: ScheduledWork;\n private readonly logger: Logger;\n\n constructor(params: AutomationsServiceParams) {\n this.repo = params.repo;\n this.runsRepo = params.runsRepo;\n this.uow = params.uow;\n this.engine = params.engine;\n this.mailer = params.mailer;\n this.lookups = params.lookups;\n this.integrations = params.integrations;\n this.credentials = params.credentials;\n this.webhooks = params.webhooks;\n this.mail = {\n ...params.mail,\n recentMessageRecipients: (orgId, automationId, chatId, since) =>\n this.runsRepo.recentRecipients(orgId, automationId, chatId, since),\n };\n this.grants = params.grants;\n this.desk = params.desk;\n this.scheduledWork = params.scheduledWork;\n this.logger = params.logger ?? noopLogger;\n }\n\n async handle(event: DomainEvent): Promise<void> {\n // automation.* events are this service's own output — matching one as a trigger would self-loop.\n if (event.type.startsWith('automation.')) {\n return;\n }\n await this.seedFor(event);\n const automations = await this.safeListByTrigger(event);\n for (const automation of automations) {\n const matches = filterMatches(automation.filter, event.data);\n if (!automation.enabled || !matches) {\n if (automation.enabled && !matches) {\n this.logger.debug('automation filter skipped', {\n orgId: event.orgId,\n automationId: automation.id,\n trigger: event.type,\n });\n }\n continue;\n }\n await this.dispatchOne(automation, event);\n }\n }\n\n private seedsFor(event: DomainEvent): SeedAutomation[] | null {\n if (event.type === 'organization.created') {\n return orgSeeds();\n }\n if (event.type !== 'content.community.created') {\n return null;\n }\n const communityId = recordString(event.data, 'id');\n if (!communityId) {\n this.logger.error('community seed missing id', { orgId: event.orgId, eventId: event.id });\n return null;\n }\n return communitySeeds(communityId);\n }\n\n private async seedFor(event: DomainEvent): Promise<void> {\n const rows = this.seedsFor(event);\n if (!rows) {\n return;\n }\n const seeded = await this.uow.run(async ({ automations, outbox }) => {\n const created = [];\n for (const row of rows) {\n const inserted = await automations.insertSeed(event.orgId, {\n name: row.name,\n description: row.description,\n triggers: row.triggers,\n actions: row.actions,\n filter: row.filter,\n kind: 'workflow',\n source: 'system',\n });\n if (inserted) {\n created.push(\n automationEvents.automationCreated.make({ orgId: event.orgId, data: inserted }),\n );\n }\n }\n if (created.length > 0) {\n await outbox.append(created);\n this.logger.info('system automations seeded', {\n orgId: event.orgId,\n trigger: event.type,\n count: created.length,\n });\n }\n return created.length;\n });\n if (seeded > 0) {\n await this.reconcileSchedules(event.orgId);\n }\n }\n\n private async reconcileSchedules(orgId: string): Promise<void> {\n try {\n await this.engine.syncSchedules?.();\n } catch (err) {\n this.logger.error('automation schedules not reconciled', { orgId, err });\n }\n }\n\n private async safeListByTrigger(event: DomainEvent): Promise<Automation[]> {\n try {\n return await this.repo.listByTrigger(event.orgId, event.type);\n } catch (err) {\n this.logger.error('automations: failed to load trigger matches', {\n orgId: event.orgId,\n trigger: event.type,\n err,\n });\n return [];\n }\n }\n\n private async dispatchOne(automation: Automation, event: DomainEvent): Promise<void> {\n let run: AutomationRun | null = null;\n try {\n run = await this.openRun(automation, event);\n if (!run) {\n // At-least-once redelivery of a trigger event already run for this automation — no-op.\n return;\n }\n await this.engine.dispatch(this.dispatchFor(automation, run, event));\n } catch (err) {\n this.logger.error('automation run failed', {\n orgId: event.orgId,\n automationId: automation.id,\n runId: run?.id,\n trigger: event.type,\n eventId: event.id,\n err,\n });\n if (run) {\n await this.recordFailure(run, err instanceof Error ? err.message : String(err));\n }\n }\n }\n\n private openRun(automation: Automation, event: DomainEvent): Promise<AutomationRun | null> {\n const now = new Date();\n return this.uow.run(async ({ runs, outbox }) => {\n const inserted = await runs.insert(event.orgId, {\n orgId: event.orgId,\n automationId: automation.id,\n trigger: event.type,\n event,\n status: 'running',\n actionResults: [],\n startedAt: now,\n finishedAt: null,\n createdAt: now,\n updatedAt: now,\n });\n if (!inserted) {\n return null;\n }\n await outbox.append([\n automationEvents.runStarted.make({\n orgId: event.orgId,\n data: inserted,\n }),\n ]);\n return inserted;\n });\n }\n\n private dispatchFor(\n automation: Automation,\n run: AutomationRun,\n event: DomainEvent,\n ): AutomationDispatch {\n return {\n runId: run.id,\n orgId: event.orgId,\n automationId: automation.id,\n actions: automation.actions,\n event,\n };\n }\n\n /** Best-effort: logs and never rethrows — `handle` must never throw. */\n private async recordFailure(run: AutomationRun, error: string): Promise<AutomationRun | null> {\n try {\n return await this.uow.run(async ({ runs, outbox }) => {\n const failed = await runs.recordOutcome(run.orgId, run.id, {\n status: 'failed',\n actionResults: run.actionResults,\n finishedAt: new Date(),\n });\n if (failed) {\n await outbox.append([\n automationEvents.runFailed.make({\n orgId: run.orgId,\n data: failed,\n }),\n ]);\n }\n return failed;\n });\n } catch (err) {\n this.logger.error('failed to record automation run failure', {\n orgId: run.orgId,\n automationId: run.automationId,\n runId: run.id,\n err,\n originalError: error,\n });\n return null;\n }\n }\n\n async runAction(d: AutomationDispatch, index: number): Promise<AutomationActionResult> {\n const action = d.actions[index];\n if (!action) {\n throw new Error(`automation ${d.automationId} run ${d.runId}: no action at index ${index}`);\n }\n let outcome: ActionOutcome;\n try {\n outcome = await executeAction(action, d.event, {\n mailer: this.mailer,\n lookups: this.lookups,\n credentials: this.credentials,\n webhooks: this.webhooks,\n mail: this.mail,\n grants: this.grants,\n desk: this.desk,\n scheduledWork: this.scheduledWork,\n automationId: d.automationId,\n actionInput: action.input,\n logger: this.logger,\n });\n } catch (err) {\n this.logger.error('automation action failed', {\n orgId: d.orgId,\n automationId: d.automationId,\n runId: d.runId,\n trigger: d.event.type,\n index,\n type: action.type,\n err,\n });\n throw err instanceof ActionFailedError ? err : new ActionFailedError();\n }\n const { recipients, failed } = outcome;\n this.logger.info('automation action completed', {\n orgId: d.orgId,\n automationId: d.automationId,\n runId: d.runId,\n type: action.type,\n recipients: recipients.length,\n failed: failed.length,\n });\n return {\n index,\n type: action.type,\n status: 'completed',\n ...(recipients.length > 0 ? { recipients } : {}),\n ...(failed.length > 0\n ? { error: `delivery failed for ${failed.length} recipients: ${failed.join(', ')}` }\n : {}),\n };\n }\n\n async listSchedules(): Promise<ScheduledAutomation[]> {\n return this.repo.listSchedules();\n }\n\n async fireSchedule(orgId: string, automationId: string, at: Date): Promise<void> {\n const firedAt = new Date(floorMinute(at));\n const automation = await this.repo.findById(orgId, automationId);\n if (!automation?.enabled || firedAt.getTime() < floorMinute(automation.createdAt)) {\n this.logger.debug('schedule skipped', { orgId, automationId });\n return;\n }\n if (!(await this.scheduleHasWork(orgId, automation, firedAt))) {\n this.logger.debug('schedule idle', { orgId, automationId });\n return;\n }\n const event: DomainEvent = {\n type: SCHEDULE_FIRED,\n id: `schedule_${automationId}_${firedAt.toISOString()}`,\n orgId,\n version: 1,\n occurredAt: firedAt.toISOString(),\n data: { automationId, firedAt: firedAt.toISOString() },\n };\n await this.dispatchOne(automation, event);\n }\n\n private async scheduleHasWork(\n orgId: string,\n automation: Automation,\n firedAt: Date,\n ): Promise<boolean> {\n const checks = automation.actions.map((action) => this.pendingWork(action.type));\n const known = checks.filter((check) => check !== null);\n if (checks.length === 0 || known.length < checks.length) {\n return true;\n }\n for (const check of known) {\n if (await check(orgId, firedAt)) {\n return true;\n }\n }\n return false;\n }\n\n private pendingWork(type: string): ((orgId: string, now: Date) => Promise<boolean>) | null {\n switch (type) {\n case 'publishDuePosts':\n return (orgId, now) => this.scheduledWork.hasDuePosts(orgId, now);\n case 'expireDueGrants':\n return (orgId, now) => this.scheduledWork.hasDueGrants(orgId, now);\n case 'removeUnattachedUploads':\n return (orgId, now) => this.scheduledWork.hasUnattachedUploads(orgId, now);\n default:\n return null;\n }\n }\n\n async finalize(d: AutomationDispatch, results: AutomationActionResult[]): Promise<void> {\n const status: 'completed' | 'failed' =\n results.length === d.actions.length && results.every((r) => r.status === 'completed')\n ? 'completed'\n : 'failed';\n await this.uow.run(async ({ runs, outbox }) => {\n const run = await runs.recordOutcome(d.orgId, d.runId, {\n status,\n actionResults: results,\n finishedAt: new Date(),\n });\n if (!run) {\n return;\n }\n const runEvent =\n status === 'completed'\n ? automationEvents.runCompleted.make({ orgId: d.orgId, data: run })\n : automationEvents.runFailed.make({ orgId: d.orgId, data: run });\n const actionFailedEvents = results\n .filter((r) => r.status === 'failed')\n .map((result) => ({\n ...automationEvents.actionFailed.make({\n orgId: d.orgId,\n data: result,\n }),\n }));\n await outbox.append([runEvent, ...actionFailedEvents]);\n });\n this.logOutcome(d, status, results);\n }\n\n private logOutcome(\n d: AutomationDispatch,\n status: 'completed' | 'failed',\n results: AutomationActionResult[],\n ): void {\n const run = {\n orgId: d.orgId,\n automationId: d.automationId,\n runId: d.runId,\n trigger: d.event.type,\n eventId: d.event.id,\n };\n if (status === 'failed') {\n const failed = results.find((r) => r.status === 'failed');\n const index = failed?.index ?? results.length;\n this.logger.error('automation run failed', {\n ...run,\n index,\n action: failed?.type ?? d.actions[index]?.type,\n error: failed?.error ?? 'not every action ran',\n });\n return;\n }\n const partial = results.filter((r) => r.error !== undefined);\n if (partial.length > 0) {\n this.logger.warn('automation run completed with failed deliveries', {\n ...run,\n actions: partial.map(({ index, type, error }) => ({ index, type, error })),\n });\n }\n }\n\n async create(orgId: string, input: CreateAutomationInput): Promise<Automation> {\n assertCatalogTriggers(input.triggers, true);\n assertAuthorableActions(input.actions);\n const automation = await this.uow.run(async ({ automations, outbox }) => {\n const created = await automations.insert(orgId, {\n ...input,\n triggers: unique(input.triggers),\n kind: 'workflow',\n });\n await outbox.append([automationEvents.automationCreated.make({ orgId, data: created })]);\n return created;\n });\n this.logger.info('automation created', { orgId, automationId: automation.id });\n await this.reconcileSchedules(orgId);\n return automation;\n }\n\n async update(\n orgId: string,\n id: string,\n input: UpdateAutomationInput,\n ): Promise<Automation | null> {\n if (input.triggers !== undefined) {\n assertCatalogTriggers(input.triggers, true);\n }\n if (input.actions !== undefined) {\n assertAuthorableActions(input.actions);\n }\n const existing = await this.repo.findById(orgId, id);\n if (!existing) {\n return null;\n }\n if (existing.kind !== 'workflow') {\n throw new AutomationKindMismatchError(id, existing.kind);\n }\n const patch =\n input.triggers === undefined ? input : { ...input, triggers: unique(input.triggers) };\n const automation = await this.uow.run(async ({ automations, outbox }) => {\n const updated = await automations.update(orgId, id, patch);\n if (!updated) {\n return null;\n }\n await outbox.append([updateEvent(orgId, input, updated)]);\n return updated;\n });\n if (automation) {\n this.logger.info('automation updated', { orgId, automationId: id });\n await this.reconcileSchedules(orgId);\n }\n return automation;\n }\n\n async delete(orgId: string, id: string): Promise<boolean> {\n const existing = await this.repo.findById(orgId, id);\n if (!existing) {\n return false;\n }\n if (existing.kind !== 'workflow') {\n throw new AutomationKindMismatchError(id, existing.kind);\n }\n if (existing.source === 'system') {\n throw new ConflictError('A system automation cannot be deleted');\n }\n const deleted = await this.uow.run(async ({ automations, outbox }) => {\n const removed = await automations.delete(orgId, id);\n if (!removed) {\n return null;\n }\n await outbox.append([automationEvents.automationDeleted.make({ orgId, data: removed })]);\n return removed;\n });\n if (deleted) {\n this.logger.info('automation deleted', { orgId, automationId: id });\n await this.reconcileSchedules(orgId);\n }\n return deleted !== null;\n }\n\n async list(orgId: string, query?: AutomationsQuery): Promise<Automation[]> {\n const automations = await this.repo.listByOrg(orgId, query?.kind);\n return automations.map(toAutomationView);\n }\n\n async get(orgId: string, id: string): Promise<Automation | null> {\n const automation = await this.repo.findById(orgId, id);\n return automation ? toAutomationView(automation) : null;\n }\n\n async listRuns(\n orgId: string,\n automationId: string,\n query: AutomationRunsQuery,\n ): Promise<Page<AutomationRun>> {\n const page = await this.runsRepo.list(orgId, automationId, query);\n return { ...page, rows: page.rows.map(toRunView) };\n }\n\n async rerun(orgId: string, automationId: string, runId: string): Promise<AutomationRun> {\n const existing = await this.runsRepo.findById(orgId, runId);\n if (!existing || existing.automationId !== automationId) {\n throw new NotFoundError('Automation run', runId);\n }\n const automation = await this.repo.findById(orgId, automationId);\n if (!automation) {\n throw new NotFoundError('Automation', automationId);\n }\n const run = await this.uow.run(async ({ runs, outbox }) => {\n const restarted = await runs.restart(orgId, runId, new Date());\n if (!restarted) {\n return null;\n }\n await outbox.append([automationEvents.runStarted.make({ orgId, data: restarted })]);\n return restarted;\n });\n if (!run) {\n throw new ConflictError(`automation run ${runId} is still running`);\n }\n try {\n await this.engine.dispatch(this.dispatchFor(automation, run, run.event));\n } catch (err) {\n this.logger.error('automation rerun failed', {\n orgId,\n automationId,\n runId: run.id,\n trigger: run.trigger,\n err,\n });\n const failed = await this.recordFailure(\n run,\n err instanceof Error ? err.message : String(err),\n );\n return toRunView(failed ?? run);\n }\n this.logger.info('automation rerun started', { orgId, automationId, runId: run.id });\n return toRunView(run);\n }\n\n async listWebhooks(orgId: string): Promise<Webhook[]> {\n const automations = await this.repo.listByOrg(orgId, 'webhook');\n return automations.map(toWebhook);\n }\n\n async getWebhook(orgId: string, id: string): Promise<Webhook | null> {\n const automation = await this.findWebhook(orgId, id);\n return automation ? toWebhook(automation) : null;\n }\n\n async createWebhook(orgId: string, input: CreateWebhookInput): Promise<CreatedWebhook> {\n const events = unique(input.events);\n assertCatalogTriggers(events, false);\n const secret = generateWebhookSecret();\n const automation = await this.uow.run(async ({ automations, credentials, outbox }) => {\n const secretRef = await credentials.store(orgId, { secret });\n const created = await automations.insert(orgId, {\n kind: 'webhook',\n name: input.url,\n description: input.description,\n triggers: events,\n actions: [{ type: DELIVER_WEBHOOK_ACTION, input: { url: input.url, secretRef } }],\n });\n await outbox.append([automationEvents.automationCreated.make({ orgId, data: created })]);\n return created;\n });\n this.logger.info('webhook created', { orgId, automationId: automation.id });\n return { ...toWebhook(automation), secret };\n }\n\n async updateWebhook(\n orgId: string,\n id: string,\n input: UpdateWebhookInput,\n ): Promise<Webhook | null> {\n const existing = await this.findWebhook(orgId, id);\n if (!existing) {\n return null;\n }\n const events = input.events === undefined ? undefined : unique(input.events);\n if (events) {\n assertCatalogTriggers(events, false);\n }\n const { secretRef } = webhookTarget(existing);\n const patch: UpdateAutomationInput = {\n name: input.url,\n description: input.description,\n triggers: events,\n actions:\n input.url === undefined\n ? undefined\n : [{ type: DELIVER_WEBHOOK_ACTION, input: { url: input.url, secretRef } }],\n enabled: input.enabled,\n };\n const updated = await this.uow.run(async ({ automations, outbox }) => {\n const row = await automations.update(orgId, id, patch);\n if (!row) {\n return null;\n }\n await outbox.append([updateEvent(orgId, input, row)]);\n return row;\n });\n if (!updated) {\n return null;\n }\n this.logger.info('webhook updated', { orgId, automationId: id });\n return toWebhook(updated);\n }\n\n async deleteWebhook(orgId: string, id: string): Promise<boolean> {\n const existing = await this.findWebhook(orgId, id);\n if (!existing) {\n return false;\n }\n const { secretRef } = webhookTarget(existing);\n const deleted = await this.uow.run(async ({ automations, credentials, outbox }) => {\n const removed = await automations.delete(orgId, id);\n if (!removed) {\n return null;\n }\n await credentials.destroy(orgId, secretRef);\n await outbox.append([automationEvents.automationDeleted.make({ orgId, data: removed })]);\n return removed;\n });\n if (deleted) {\n this.logger.info('webhook deleted', { orgId, automationId: id });\n }\n return deleted !== null;\n }\n\n async revealWebhookSecret(orgId: string, id: string): Promise<WebhookSecret | null> {\n const existing = await this.findWebhook(orgId, id);\n if (!existing) {\n return null;\n }\n const secrets = await this.credentials.reveal(orgId, webhookTarget(existing).secretRef);\n const secret = secrets?.['secret'];\n if (typeof secret !== 'string') {\n throw new Error(`webhook ${id}: signing secret is missing`);\n }\n return { secret };\n }\n\n async rotateWebhookSecret(orgId: string, id: string): Promise<WebhookSecret | null> {\n const existing = await this.findWebhook(orgId, id);\n if (!existing) {\n return null;\n }\n const secret = generateWebhookSecret();\n await this.credentials.update(orgId, webhookTarget(existing).secretRef, { secret });\n this.logger.info('webhook secret rotated', { orgId, automationId: id });\n return { secret };\n }\n\n private async findWebhook(orgId: string, id: string): Promise<Automation | null> {\n const automation = await this.repo.findById(orgId, id);\n return automation?.kind === 'webhook' ? automation : null;\n }\n\n availableActions(): AvailableActions {\n return [\n ...catalogActions(),\n ...this.integrations.available().flatMap((integration) =>\n integration.actions.map((action) => ({\n type: `${integration.id}.${action.id}`,\n description: action.description,\n inputSchema: action.inputSchema as Record<string, JsonValue>,\n source: integration.id,\n })),\n ),\n ];\n }\n\n availableTriggers(): AvailableTriggers {\n return { triggers: catalogTriggers() };\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"service.js","names":[],"sources":["../../src/automations/service.ts"],"sourcesContent":["// automations context — service implementation (inbound port) and the\n// AutomationExecutor the engine drives.\n//\n// `handle` is subscribed to all events on the EventBus: it matches an event\n// against enabled automations for its trigger, opens one run per match, and\n// hands it to the injected AutomationEngine, which calls back into\n// `runAction`/`finalize`.\nimport {\n catalogActions,\n catalogTriggers,\n DELIVER_WEBHOOK_ACTION,\n publicDataSchema,\n} from './catalog.js';\nimport { executeAction, type ActionOutcome } from './actions.js';\nimport { generateWebhookSecret } from './webhook-signing.js';\nimport type { CommunityMailReads, GrantWriter } from './community-mail.js';\nimport type { NotificationDelivery } from './notifications/delivery.js';\nimport type { NotificationProcessor } from './notifications/processor.js';\nimport { cronSupported } from './schedule.js';\nimport {\n ActionFailedError,\n AutomationKindMismatchError,\n InvalidTriggerError,\n ReservedActionError,\n type Automation,\n type AutomationAction,\n type AutomationActionResult,\n type AutomationRun,\n type CreatedWebhook,\n type DeliverWebhookInput,\n type Page,\n type Webhook,\n type WebhookSecret,\n} from './model.js';\nimport type {\n AutomationRunsQuery,\n AutomationsQuery,\n AvailableActions,\n AvailableTriggers,\n CreateAutomationInput,\n CreateWebhookInput,\n UpdateAutomationInput,\n UpdateWebhookInput,\n} from './types.js';\nimport type {\n AutomationDispatch,\n AutomationEngine,\n AutomationExecutor,\n AutomationRunsRepository,\n AutomationsRepository,\n AutomationsService,\n AutomationsUnitOfWork,\n DeliveryDispatch,\n WebhookSender,\n} from './ports.js';\nimport type { CredentialStore, DomainEvent, Logger } from '../shared/ports.js';\nimport type { JsonValue, ScheduledAutomation } from '../types/index.js';\nimport { deliverWebhookInputSchema } from '../types/schemas/index.js';\nimport { ConflictError, InvalidInputError, NotFoundError } from '../shared/errors.js';\nimport { noopLogger } from '../shared/logger.js';\nimport type { Mailer, MailerLookups } from '../shared/mailer.js';\nimport type { IntegrationsService } from '../integrations/index.js';\nimport { automationEvents } from './events.js';\n\n/** True only when `input` sets `enabled` and nothing else — emits automation.enabled|disabled instead of automation.updated. */\nfunction isEnabledOnlyUpdate<T extends { enabled?: boolean }>(\n input: T,\n): input is T & { enabled: boolean } {\n if (input.enabled === undefined) {\n return false;\n }\n const { enabled: _enabled, ...rest } = input;\n return Object.values(rest).every((v) => v === undefined);\n}\n\nfunction floorMinute(at: Date): number {\n const copy = new Date(at);\n copy.setUTCSeconds(0, 0);\n return copy.getTime();\n}\n\nfunction unique(values: string[]): string[] {\n return [...new Set(values)];\n}\n\nfunction filterMatches(filter: Record<string, JsonValue> | null, data: unknown): boolean {\n if (filter == null) {\n return true;\n }\n if (typeof data !== 'object' || data === null || Array.isArray(data)) {\n return false;\n }\n const record = data as Record<string, unknown>;\n return Object.entries(filter).every(([key, expected]) => {\n if (!Object.prototype.hasOwnProperty.call(record, key)) {\n return false;\n }\n return JSON.stringify(record[key]) === JSON.stringify(expected);\n });\n}\n\nfunction assertAuthorableTriggers(triggers: string[]): void {\n for (const trigger of triggers) {\n if (trigger.startsWith('automation.')) {\n throw new InvalidTriggerError(trigger);\n }\n if (trigger.startsWith('schedule:') && !cronSupported(trigger.slice('schedule:'.length))) {\n throw new InvalidTriggerError(trigger, 'schedule');\n }\n }\n}\n\nfunction assertCatalogTriggers(triggers: string[], schedules: boolean): void {\n assertAuthorableTriggers(triggers);\n const known = new Set(catalogTriggers().map((t) => t.type));\n for (const trigger of triggers) {\n if (!(schedules && trigger.startsWith('schedule:')) && !known.has(trigger)) {\n throw new InvalidTriggerError(trigger, 'unknown');\n }\n }\n}\n\nconst REMOVED_ACTIONS = new Set([\n 'notify',\n 'publishDuePosts',\n 'expireDueGrants',\n 'sendDigest',\n 'removeUnattachedUploads',\n]);\n\nfunction assertAuthorableActions(actions: AutomationAction[]): void {\n for (const action of actions) {\n if (action.type === DELIVER_WEBHOOK_ACTION) {\n throw new ReservedActionError(action.type);\n }\n if (REMOVED_ACTIONS.has(action.type)) {\n throw new InvalidInputError(`The action \"${action.type}\" is no longer available`);\n }\n }\n}\n\nconst SCHEDULE_FIRED = 'automation.schedule.fired';\n\nfunction publicEventData(event: DomainEvent): JsonValue {\n const parsed =\n event.type === SCHEDULE_FIRED\n ? { success: true as const, data: event.data }\n : publicDataSchema(event.type)?.safeParse(event.data);\n return parsed?.success ? (JSON.parse(JSON.stringify(parsed.data)) as JsonValue) : null;\n}\n\n/** A run as callers see it: its event carries only what a webhook of that event would. */\nfunction toRunView(run: AutomationRun): AutomationRun {\n const { type, version, id, orgId, occurredAt } = run.event;\n return {\n ...run,\n event: { type, version, id, orgId, occurredAt, data: publicEventData(run.event) },\n };\n}\n\n/** An automation as callers see it: a webhook's credential reference stays internal. */\nfunction toAutomationView(automation: Automation): Automation {\n return {\n ...automation,\n actions: automation.actions.map((action) => {\n if (action.type !== DELIVER_WEBHOOK_ACTION) {\n return action;\n }\n const { secretRef: _secretRef, ...input } = action.input;\n return { ...action, input };\n }),\n };\n}\n\nfunction webhookTarget(automation: Automation): DeliverWebhookInput {\n return deliverWebhookInputSchema.parse(automation.actions[0]?.input);\n}\n\nfunction toWebhook(automation: Automation): Webhook {\n return {\n orgId: automation.orgId,\n id: automation.id,\n url: webhookTarget(automation).url,\n events: automation.triggers,\n description: automation.description,\n enabled: automation.enabled,\n createdAt: automation.createdAt,\n updatedAt: automation.updatedAt,\n };\n}\n\nfunction updateEvent(orgId: string, input: { enabled?: boolean }, updated: Automation) {\n if (isEnabledOnlyUpdate(input)) {\n return input.enabled\n ? automationEvents.automationEnabled.make({ orgId, data: updated })\n : automationEvents.automationDisabled.make({ orgId, data: updated });\n }\n return automationEvents.automationUpdated.make({ orgId, data: updated });\n}\n\nexport type AutomationsServiceParams = {\n /** Read-only access (list/get/available) — runs outside any transaction. */\n repo: AutomationsRepository;\n runsRepo: AutomationRunsRepository;\n /** Atomic write scope: tx-bound repos + credential store + outbox appender. */\n uow: AutomationsUnitOfWork;\n engine: AutomationEngine;\n mailer: Pick<Mailer, 'send'>;\n lookups: MailerLookups;\n integrations: Pick<IntegrationsService, 'available'>;\n credentials: CredentialStore;\n webhooks: WebhookSender;\n mail: Omit<CommunityMailReads, 'recentMessageRecipients'>;\n grants: GrantWriter;\n delivery: Pick<NotificationDelivery, 'run'>;\n processor: Pick<NotificationProcessor, 'process'>;\n logger?: Logger;\n};\n\nexport class AutomationsServiceImpl implements AutomationsService, AutomationExecutor {\n private readonly repo: AutomationsRepository;\n private readonly runsRepo: AutomationRunsRepository;\n private readonly uow: AutomationsUnitOfWork;\n private readonly engine: AutomationEngine;\n private readonly mailer: Pick<Mailer, 'send'>;\n private readonly lookups: MailerLookups;\n private readonly integrations: Pick<IntegrationsService, 'available'>;\n private readonly credentials: CredentialStore;\n private readonly webhooks: WebhookSender;\n private readonly mail: CommunityMailReads;\n private readonly grants: GrantWriter;\n private readonly delivery: Pick<NotificationDelivery, 'run'>;\n private readonly processor: Pick<NotificationProcessor, 'process'>;\n private readonly logger: Logger;\n\n constructor(params: AutomationsServiceParams) {\n this.repo = params.repo;\n this.runsRepo = params.runsRepo;\n this.uow = params.uow;\n this.engine = params.engine;\n this.mailer = params.mailer;\n this.lookups = params.lookups;\n this.integrations = params.integrations;\n this.credentials = params.credentials;\n this.webhooks = params.webhooks;\n this.mail = {\n ...params.mail,\n recentMessageRecipients: (orgId, automationId, chatId, since) =>\n this.runsRepo.recentRecipients(orgId, automationId, chatId, since),\n };\n this.grants = params.grants;\n this.delivery = params.delivery;\n this.processor = params.processor;\n this.logger = params.logger ?? noopLogger;\n }\n\n async handle(event: DomainEvent): Promise<void> {\n // automation.* events are this service's own output — matching one as a trigger would self-loop.\n if (event.type.startsWith('automation.')) {\n return;\n }\n const automations = await this.safeListByTrigger(event);\n for (const automation of automations) {\n const matches = filterMatches(automation.filter, event.data);\n if (!automation.enabled || !matches) {\n if (automation.enabled && !matches) {\n this.logger.debug('automation filter skipped', {\n orgId: event.orgId,\n automationId: automation.id,\n trigger: event.type,\n });\n }\n continue;\n }\n await this.dispatchOne(automation, event);\n }\n await this.processor.process(event);\n }\n\n private async reconcileSchedules(orgId: string): Promise<void> {\n try {\n await this.engine.syncSchedules?.();\n } catch (err) {\n this.logger.error('automation schedules not reconciled', { orgId, err });\n }\n }\n\n private async safeListByTrigger(event: DomainEvent): Promise<Automation[]> {\n try {\n return await this.repo.listByTrigger(event.orgId, event.type);\n } catch (err) {\n this.logger.error('automations: failed to load trigger matches', {\n orgId: event.orgId,\n trigger: event.type,\n err,\n });\n return [];\n }\n }\n\n private async dispatchOne(automation: Automation, event: DomainEvent): Promise<void> {\n let run: AutomationRun | null = null;\n try {\n run = await this.openRun(automation, event);\n if (!run) {\n // At-least-once redelivery of a trigger event already run for this automation — no-op.\n return;\n }\n await this.engine.dispatch(this.dispatchFor(automation, run, event));\n } catch (err) {\n this.logger.error('automation run failed', {\n orgId: event.orgId,\n automationId: automation.id,\n runId: run?.id,\n trigger: event.type,\n eventId: event.id,\n err,\n });\n if (run) {\n await this.recordFailure(run, err instanceof Error ? err.message : String(err));\n }\n }\n }\n\n private openRun(automation: Automation, event: DomainEvent): Promise<AutomationRun | null> {\n const now = new Date();\n return this.uow.run(async ({ runs, outbox }) => {\n const inserted = await runs.insert(event.orgId, {\n orgId: event.orgId,\n automationId: automation.id,\n trigger: event.type,\n event,\n status: 'running',\n actionResults: [],\n startedAt: now,\n finishedAt: null,\n createdAt: now,\n updatedAt: now,\n });\n if (!inserted) {\n return null;\n }\n await outbox.append([\n automationEvents.runStarted.make({\n orgId: event.orgId,\n data: inserted,\n }),\n ]);\n return inserted;\n });\n }\n\n private dispatchFor(\n automation: Automation,\n run: AutomationRun,\n event: DomainEvent,\n ): AutomationDispatch {\n return {\n runId: run.id,\n orgId: event.orgId,\n automationId: automation.id,\n actions: automation.actions,\n event,\n };\n }\n\n /** Best-effort: logs and never rethrows, so an org automation never fails `handle`. */\n private async recordFailure(run: AutomationRun, error: string): Promise<AutomationRun | null> {\n try {\n return await this.uow.run(async ({ runs, outbox }) => {\n const failed = await runs.recordOutcome(run.orgId, run.id, {\n status: 'failed',\n actionResults: run.actionResults,\n finishedAt: new Date(),\n });\n if (failed) {\n await outbox.append([\n automationEvents.runFailed.make({\n orgId: run.orgId,\n data: failed,\n }),\n ]);\n }\n return failed;\n });\n } catch (err) {\n this.logger.error('failed to record automation run failure', {\n orgId: run.orgId,\n automationId: run.automationId,\n runId: run.id,\n err,\n originalError: error,\n });\n return null;\n }\n }\n\n async runAction(d: AutomationDispatch, index: number): Promise<AutomationActionResult> {\n const action = d.actions[index];\n if (!action) {\n throw new Error(`automation ${d.automationId} run ${d.runId}: no action at index ${index}`);\n }\n let outcome: ActionOutcome;\n try {\n outcome = await executeAction(action, d.event, {\n mailer: this.mailer,\n lookups: this.lookups,\n credentials: this.credentials,\n webhooks: this.webhooks,\n mail: this.mail,\n grants: this.grants,\n automationId: d.automationId,\n actionInput: action.input,\n logger: this.logger,\n });\n } catch (err) {\n this.logger.error('automation action failed', {\n orgId: d.orgId,\n automationId: d.automationId,\n runId: d.runId,\n trigger: d.event.type,\n index,\n type: action.type,\n err,\n });\n throw err instanceof ActionFailedError ? err : new ActionFailedError();\n }\n const { recipients, failed } = outcome;\n this.logger.info('automation action completed', {\n orgId: d.orgId,\n automationId: d.automationId,\n runId: d.runId,\n type: action.type,\n recipients: recipients.length,\n failed: failed.length,\n });\n return {\n index,\n type: action.type,\n status: 'completed',\n ...(recipients.length > 0 ? { recipients } : {}),\n ...(failed.length > 0\n ? { error: `delivery failed for ${failed.length} recipients: ${failed.join(', ')}` }\n : {}),\n };\n }\n\n runDelivery(d: DeliveryDispatch, lastAttempt: boolean): Promise<void> {\n return this.delivery.run(d, lastAttempt);\n }\n\n async listSchedules(): Promise<ScheduledAutomation[]> {\n return this.repo.listSchedules();\n }\n\n async fireSchedule(orgId: string, automationId: string, at: Date): Promise<void> {\n const firedAt = new Date(floorMinute(at));\n const automation = await this.repo.findById(orgId, automationId);\n if (!automation?.enabled || firedAt.getTime() < floorMinute(automation.createdAt)) {\n this.logger.debug('schedule skipped', { orgId, automationId });\n return;\n }\n const event: DomainEvent = {\n type: SCHEDULE_FIRED,\n id: `schedule_${automationId}_${firedAt.toISOString()}`,\n orgId,\n version: 1,\n occurredAt: firedAt.toISOString(),\n data: { automationId, firedAt: firedAt.toISOString() },\n };\n await this.dispatchOne(automation, event);\n }\n\n async finalize(d: AutomationDispatch, results: AutomationActionResult[]): Promise<void> {\n const status: 'completed' | 'failed' =\n results.length === d.actions.length && results.every((r) => r.status === 'completed')\n ? 'completed'\n : 'failed';\n await this.uow.run(async ({ runs, outbox }) => {\n const run = await runs.recordOutcome(d.orgId, d.runId, {\n status,\n actionResults: results,\n finishedAt: new Date(),\n });\n if (!run) {\n return;\n }\n const runEvent =\n status === 'completed'\n ? automationEvents.runCompleted.make({ orgId: d.orgId, data: run })\n : automationEvents.runFailed.make({ orgId: d.orgId, data: run });\n const actionFailedEvents = results\n .filter((r) => r.status === 'failed')\n .map((result) => ({\n ...automationEvents.actionFailed.make({\n orgId: d.orgId,\n data: result,\n }),\n }));\n await outbox.append([runEvent, ...actionFailedEvents]);\n });\n this.logOutcome(d, status, results);\n }\n\n private logOutcome(\n d: AutomationDispatch,\n status: 'completed' | 'failed',\n results: AutomationActionResult[],\n ): void {\n const run = {\n orgId: d.orgId,\n automationId: d.automationId,\n runId: d.runId,\n trigger: d.event.type,\n eventId: d.event.id,\n };\n if (status === 'failed') {\n const failed = results.find((r) => r.status === 'failed');\n const index = failed?.index ?? results.length;\n this.logger.error('automation run failed', {\n ...run,\n index,\n action: failed?.type ?? d.actions[index]?.type,\n error: failed?.error ?? 'not every action ran',\n });\n return;\n }\n const partial = results.filter((r) => r.error !== undefined);\n if (partial.length > 0) {\n this.logger.warn('automation run completed with failed deliveries', {\n ...run,\n actions: partial.map(({ index, type, error }) => ({ index, type, error })),\n });\n }\n }\n\n async create(orgId: string, input: CreateAutomationInput): Promise<Automation> {\n assertCatalogTriggers(input.triggers, true);\n assertAuthorableActions(input.actions);\n const automation = await this.uow.run(async ({ automations, outbox }) => {\n const created = await automations.insert(orgId, {\n ...input,\n triggers: unique(input.triggers),\n kind: 'workflow',\n });\n await outbox.append([automationEvents.automationCreated.make({ orgId, data: created })]);\n return created;\n });\n this.logger.info('automation created', { orgId, automationId: automation.id });\n await this.reconcileSchedules(orgId);\n return automation;\n }\n\n async update(\n orgId: string,\n id: string,\n input: UpdateAutomationInput,\n ): Promise<Automation | null> {\n if (input.triggers !== undefined) {\n assertCatalogTriggers(input.triggers, true);\n }\n if (input.actions !== undefined) {\n assertAuthorableActions(input.actions);\n }\n const existing = await this.repo.findById(orgId, id);\n if (!existing) {\n return null;\n }\n if (existing.kind !== 'workflow') {\n throw new AutomationKindMismatchError(id, existing.kind);\n }\n const patch =\n input.triggers === undefined ? input : { ...input, triggers: unique(input.triggers) };\n const automation = await this.uow.run(async ({ automations, outbox }) => {\n const updated = await automations.update(orgId, id, patch);\n if (!updated) {\n return null;\n }\n await outbox.append([updateEvent(orgId, input, updated)]);\n return updated;\n });\n if (automation) {\n this.logger.info('automation updated', { orgId, automationId: id });\n await this.reconcileSchedules(orgId);\n }\n return automation;\n }\n\n async delete(orgId: string, id: string): Promise<boolean> {\n const existing = await this.repo.findById(orgId, id);\n if (!existing) {\n return false;\n }\n if (existing.kind !== 'workflow') {\n throw new AutomationKindMismatchError(id, existing.kind);\n }\n const deleted = await this.uow.run(async ({ automations, outbox }) => {\n const removed = await automations.delete(orgId, id);\n if (!removed) {\n return null;\n }\n await outbox.append([automationEvents.automationDeleted.make({ orgId, data: removed })]);\n return removed;\n });\n if (deleted) {\n this.logger.info('automation deleted', { orgId, automationId: id });\n await this.reconcileSchedules(orgId);\n }\n return deleted !== null;\n }\n\n async list(orgId: string, query?: AutomationsQuery): Promise<Automation[]> {\n const automations = await this.repo.listByOrg(orgId, query?.kind);\n return automations.map(toAutomationView);\n }\n\n async get(orgId: string, id: string): Promise<Automation | null> {\n const automation = await this.repo.findById(orgId, id);\n return automation ? toAutomationView(automation) : null;\n }\n\n async listRuns(\n orgId: string,\n automationId: string,\n query: AutomationRunsQuery,\n ): Promise<Page<AutomationRun>> {\n const page = await this.runsRepo.list(orgId, automationId, query);\n return { ...page, rows: page.rows.map(toRunView) };\n }\n\n async rerun(orgId: string, automationId: string, runId: string): Promise<AutomationRun> {\n const existing = await this.runsRepo.findById(orgId, runId);\n if (!existing || existing.automationId !== automationId) {\n throw new NotFoundError('Automation run', runId);\n }\n const automation = await this.repo.findById(orgId, automationId);\n if (!automation) {\n throw new NotFoundError('Automation', automationId);\n }\n const run = await this.uow.run(async ({ runs, outbox }) => {\n const restarted = await runs.restart(orgId, runId, new Date());\n if (!restarted) {\n return null;\n }\n await outbox.append([automationEvents.runStarted.make({ orgId, data: restarted })]);\n return restarted;\n });\n if (!run) {\n throw new ConflictError(`automation run ${runId} is still running`);\n }\n try {\n await this.engine.dispatch(this.dispatchFor(automation, run, run.event));\n } catch (err) {\n this.logger.error('automation rerun failed', {\n orgId,\n automationId,\n runId: run.id,\n trigger: run.trigger,\n err,\n });\n const failed = await this.recordFailure(\n run,\n err instanceof Error ? err.message : String(err),\n );\n return toRunView(failed ?? run);\n }\n this.logger.info('automation rerun started', { orgId, automationId, runId: run.id });\n return toRunView(run);\n }\n\n async listWebhooks(orgId: string): Promise<Webhook[]> {\n const automations = await this.repo.listByOrg(orgId, 'webhook');\n return automations.map(toWebhook);\n }\n\n async getWebhook(orgId: string, id: string): Promise<Webhook | null> {\n const automation = await this.findWebhook(orgId, id);\n return automation ? toWebhook(automation) : null;\n }\n\n async createWebhook(orgId: string, input: CreateWebhookInput): Promise<CreatedWebhook> {\n const events = unique(input.events);\n assertCatalogTriggers(events, false);\n const secret = generateWebhookSecret();\n const automation = await this.uow.run(async ({ automations, credentials, outbox }) => {\n const secretRef = await credentials.store(orgId, { secret });\n const created = await automations.insert(orgId, {\n kind: 'webhook',\n name: input.url,\n description: input.description,\n triggers: events,\n actions: [{ type: DELIVER_WEBHOOK_ACTION, input: { url: input.url, secretRef } }],\n });\n await outbox.append([automationEvents.automationCreated.make({ orgId, data: created })]);\n return created;\n });\n this.logger.info('webhook created', { orgId, automationId: automation.id });\n return { ...toWebhook(automation), secret };\n }\n\n async updateWebhook(\n orgId: string,\n id: string,\n input: UpdateWebhookInput,\n ): Promise<Webhook | null> {\n const existing = await this.findWebhook(orgId, id);\n if (!existing) {\n return null;\n }\n const events = input.events === undefined ? undefined : unique(input.events);\n if (events) {\n assertCatalogTriggers(events, false);\n }\n const { secretRef } = webhookTarget(existing);\n const patch: UpdateAutomationInput = {\n name: input.url,\n description: input.description,\n triggers: events,\n actions:\n input.url === undefined\n ? undefined\n : [{ type: DELIVER_WEBHOOK_ACTION, input: { url: input.url, secretRef } }],\n enabled: input.enabled,\n };\n const updated = await this.uow.run(async ({ automations, outbox }) => {\n const row = await automations.update(orgId, id, patch);\n if (!row) {\n return null;\n }\n await outbox.append([updateEvent(orgId, input, row)]);\n return row;\n });\n if (!updated) {\n return null;\n }\n this.logger.info('webhook updated', { orgId, automationId: id });\n return toWebhook(updated);\n }\n\n async deleteWebhook(orgId: string, id: string): Promise<boolean> {\n const existing = await this.findWebhook(orgId, id);\n if (!existing) {\n return false;\n }\n const { secretRef } = webhookTarget(existing);\n const deleted = await this.uow.run(async ({ automations, credentials, outbox }) => {\n const removed = await automations.delete(orgId, id);\n if (!removed) {\n return null;\n }\n await credentials.destroy(orgId, secretRef);\n await outbox.append([automationEvents.automationDeleted.make({ orgId, data: removed })]);\n return removed;\n });\n if (deleted) {\n this.logger.info('webhook deleted', { orgId, automationId: id });\n }\n return deleted !== null;\n }\n\n async revealWebhookSecret(orgId: string, id: string): Promise<WebhookSecret | null> {\n const existing = await this.findWebhook(orgId, id);\n if (!existing) {\n return null;\n }\n const secrets = await this.credentials.reveal(orgId, webhookTarget(existing).secretRef);\n const secret = secrets?.['secret'];\n if (typeof secret !== 'string') {\n throw new Error(`webhook ${id}: signing secret is missing`);\n }\n return { secret };\n }\n\n async rotateWebhookSecret(orgId: string, id: string): Promise<WebhookSecret | null> {\n const existing = await this.findWebhook(orgId, id);\n if (!existing) {\n return null;\n }\n const secret = generateWebhookSecret();\n await this.credentials.update(orgId, webhookTarget(existing).secretRef, { secret });\n this.logger.info('webhook secret rotated', { orgId, automationId: id });\n return { secret };\n }\n\n private async findWebhook(orgId: string, id: string): Promise<Automation | null> {\n const automation = await this.repo.findById(orgId, id);\n return automation?.kind === 'webhook' ? automation : null;\n }\n\n availableActions(): AvailableActions {\n return [\n ...catalogActions(),\n ...this.integrations.available().flatMap((integration) =>\n integration.actions.map((action) => ({\n type: `${integration.id}.${action.id}`,\n description: action.description,\n inputSchema: action.inputSchema as Record<string, JsonValue>,\n source: integration.id,\n })),\n ),\n ];\n }\n\n availableTriggers(): AvailableTriggers {\n return { triggers: catalogTriggers() };\n }\n}\n"],"mappings":"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"}
|