@headless-lms/core 0.6.0 → 0.8.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/file-urls.d.ts +5 -19
- package/dist/assets/file-urls.js +2 -23
- package/dist/assets/file-urls.js.map +1 -1
- package/dist/assets/index.d.ts +3 -3
- package/dist/assets/ports.d.ts +9 -10
- package/dist/assets/service.d.ts +10 -13
- package/dist/assets/service.js +41 -62
- package/dist/assets/service.js.map +1 -1
- package/dist/automations/index.d.ts +4 -4
- package/dist/automations/notifications/delivery.d.ts +3 -3
- package/dist/automations/notifications/delivery.js +1 -1
- package/dist/automations/notifications/delivery.js.map +1 -1
- package/dist/automations/ports.d.ts +2 -2
- package/dist/automations/service.d.ts +1 -8
- package/dist/automations/service.js +0 -8
- package/dist/automations/service.js.map +1 -1
- package/dist/content/service.d.ts +4 -1
- package/dist/content/service.js +52 -29
- package/dist/content/service.js.map +1 -1
- package/dist/discussion/chats.d.ts +1 -2
- package/dist/discussion/chats.js +8 -17
- package/dist/discussion/chats.js.map +1 -1
- package/dist/discussion/ports.d.ts +0 -6
- package/dist/discussion/ports.js.map +1 -1
- package/dist/discussion/posts.d.ts +1 -4
- package/dist/discussion/posts.js +8 -21
- package/dist/discussion/posts.js.map +1 -1
- package/dist/entitlements/events.d.ts +33 -33
- package/dist/identity/events.d.ts +2 -2
- package/dist/integrations/events.d.ts +3 -3
- package/dist/organizations/events.d.ts +2 -2
- package/dist/reporting/member/ports.d.ts +1 -10
- package/dist/reporting/member/service.d.ts +2 -11
- package/dist/reporting/member/service.js +12 -62
- package/dist/reporting/member/service.js.map +1 -1
- package/dist/types/automations.d.ts +5 -2
- package/dist/types/index.d.ts +2 -2
- package/dist/types/ports.d.ts +4 -2
- package/dist/types/ports.js.map +1 -1
- package/dist/types/schemas/content.d.ts +2 -2
- package/dist/types/schemas/discussion.d.ts +14 -14
- package/dist/types/schemas/entitlements.d.ts +8 -8
- package/dist/types/schemas/notifications.d.ts +3 -3
- package/package.json +1 -1
|
@@ -17,7 +17,7 @@ var NotificationDelivery = class {
|
|
|
17
17
|
this.devices = params.devices;
|
|
18
18
|
this.logger = params.logger ?? noopLogger;
|
|
19
19
|
}
|
|
20
|
-
async
|
|
20
|
+
async runDelivery(d, lastAttempt) {
|
|
21
21
|
const found = await this.repo.findDelivery(d.orgId, d.deliveryId);
|
|
22
22
|
if (!found || found.delivery.status !== "pending") {
|
|
23
23
|
this.logger.debug("notification delivery skipped", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delivery.js","names":[],"sources":["../../../src/automations/notifications/delivery.ts"],"sourcesContent":["import type { DevicesService } from '../../identity/index.js';\nimport { noopLogger } from '../../shared/logger.js';\nimport type { ChannelRegistry, Logger } from '../../shared/ports.js';\nimport type { ChannelRecipient, DeliveryDispatch, DeliveryResult } from '../../types/index.js';\nimport type { NotificationsRepository } from '../ports.js';\n\nexport const CHANNEL_NOT_CONFIGURED = 'channel not configured';\nexport const NO_ADDRESS = 'no address';\nexport const NOT_ACCEPTED = 'not accepted';\n\nexport type NotificationDeliveryParams = {\n repo: Pick<NotificationsRepository, 'findDelivery' | 'markSent' | 'recordError' | 'markFailed'>;\n channels: Pick<ChannelRegistry, 'get'>;\n devices: Pick<DevicesService, 'removeDead'>;\n logger?: Logger;\n};\n\nfunction messageOf(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\nexport class NotificationDelivery {\n private readonly repo: NotificationDeliveryParams['repo'];\n private readonly channels: Pick<ChannelRegistry, 'get'>;\n private readonly devices: Pick<DevicesService, 'removeDead'>;\n private readonly logger: Logger;\n\n constructor(params: NotificationDeliveryParams) {\n this.repo = params.repo;\n this.channels = params.channels;\n this.devices = params.devices;\n this.logger = params.logger ?? noopLogger;\n }\n\n async
|
|
1
|
+
{"version":3,"file":"delivery.js","names":[],"sources":["../../../src/automations/notifications/delivery.ts"],"sourcesContent":["import type { DevicesService } from '../../identity/index.js';\nimport { noopLogger } from '../../shared/logger.js';\nimport type { ChannelRegistry, Logger } from '../../shared/ports.js';\nimport type { ChannelRecipient, DeliveryDispatch, DeliveryResult } from '../../types/index.js';\nimport type { DeliveryExecutor, NotificationsRepository } from '../ports.js';\n\nexport const CHANNEL_NOT_CONFIGURED = 'channel not configured';\nexport const NO_ADDRESS = 'no address';\nexport const NOT_ACCEPTED = 'not accepted';\n\nexport type NotificationDeliveryParams = {\n repo: Pick<NotificationsRepository, 'findDelivery' | 'markSent' | 'recordError' | 'markFailed'>;\n channels: Pick<ChannelRegistry, 'get'>;\n devices: Pick<DevicesService, 'removeDead'>;\n logger?: Logger;\n};\n\nfunction messageOf(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\nexport class NotificationDelivery implements DeliveryExecutor {\n private readonly repo: NotificationDeliveryParams['repo'];\n private readonly channels: Pick<ChannelRegistry, 'get'>;\n private readonly devices: Pick<DevicesService, 'removeDead'>;\n private readonly logger: Logger;\n\n constructor(params: NotificationDeliveryParams) {\n this.repo = params.repo;\n this.channels = params.channels;\n this.devices = params.devices;\n this.logger = params.logger ?? noopLogger;\n }\n\n async runDelivery(d: DeliveryDispatch, lastAttempt: boolean): Promise<void> {\n const found = await this.repo.findDelivery(d.orgId, d.deliveryId);\n if (!found || found.delivery.status !== 'pending') {\n this.logger.debug('notification delivery skipped', {\n orgId: d.orgId,\n deliveryId: d.deliveryId,\n status: found?.delivery.status ?? null,\n });\n return;\n }\n const { delivery, notification } = found;\n const log = {\n orgId: d.orgId,\n deliveryId: d.deliveryId,\n notificationId: notification.id,\n channel: delivery.channel,\n type: notification.type,\n };\n const channel = this.channels.get(delivery.channel);\n if (!channel) {\n await this.repo.markFailed(d.orgId, d.deliveryId, CHANNEL_NOT_CONFIGURED);\n this.logger.warn('notification delivery failed: channel not configured', log);\n return;\n }\n let to: ChannelRecipient | null;\n let result: DeliveryResult;\n try {\n to = await channel.recipient(d.orgId, notification.userId);\n if (!to) {\n await this.repo.markFailed(d.orgId, d.deliveryId, NO_ADDRESS);\n this.logger.info('notification delivery failed: no address', log);\n return;\n }\n result = await channel.deliver(notification, to);\n } catch (err) {\n const error = messageOf(err);\n if (lastAttempt) {\n await this.repo.markFailed(d.orgId, d.deliveryId, error);\n this.logger.warn('notification delivery failed on its last attempt', { ...log, err });\n return;\n }\n await this.repo.recordError(d.orgId, d.deliveryId, error);\n this.logger.warn('notification delivery attempt failed', { ...log, err });\n throw err;\n }\n if (result.deadDeviceIds.length > 0) {\n await this.devices.removeDead(d.orgId, result.deadDeviceIds);\n }\n if (!result.accepted) {\n const error = result.error ?? NOT_ACCEPTED;\n await this.repo.markFailed(d.orgId, d.deliveryId, error);\n this.logger.info('notification delivery failed: not accepted', { ...log, error });\n return;\n }\n await this.repo.markSent(d.orgId, d.deliveryId, new Date());\n this.logger.info('notification delivered', log);\n }\n}\n"],"mappings":";;AAMA,MAAa,yBAAyB;AACtC,MAAa,aAAa;AAC1B,MAAa,eAAe;AAS5B,SAAS,UAAU,KAAsB;CACvC,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AACxD;AAEA,IAAa,uBAAb,MAA8D;CAC5D;CACA;CACA;CACA;CAEA,YAAY,QAAoC;EAC9C,KAAK,OAAO,OAAO;EACnB,KAAK,WAAW,OAAO;EACvB,KAAK,UAAU,OAAO;EACtB,KAAK,SAAS,OAAO,UAAU;CACjC;CAEA,MAAM,YAAY,GAAqB,aAAqC;EAC1E,MAAM,QAAQ,MAAM,KAAK,KAAK,aAAa,EAAE,OAAO,EAAE,UAAU;EAChE,IAAI,CAAC,SAAS,MAAM,SAAS,WAAW,WAAW;GACjD,KAAK,OAAO,MAAM,iCAAiC;IACjD,OAAO,EAAE;IACT,YAAY,EAAE;IACd,QAAQ,OAAO,SAAS,UAAU;GACpC,CAAC;GACD;EACF;EACA,MAAM,EAAE,UAAU,iBAAiB;EACnC,MAAM,MAAM;GACV,OAAO,EAAE;GACT,YAAY,EAAE;GACd,gBAAgB,aAAa;GAC7B,SAAS,SAAS;GAClB,MAAM,aAAa;EACrB;EACA,MAAM,UAAU,KAAK,SAAS,IAAI,SAAS,OAAO;EAClD,IAAI,CAAC,SAAS;GACZ,MAAM,KAAK,KAAK,WAAW,EAAE,OAAO,EAAE,YAAY,sBAAsB;GACxE,KAAK,OAAO,KAAK,wDAAwD,GAAG;GAC5E;EACF;EACA,IAAI;EACJ,IAAI;EACJ,IAAI;GACF,KAAK,MAAM,QAAQ,UAAU,EAAE,OAAO,aAAa,MAAM;GACzD,IAAI,CAAC,IAAI;IACP,MAAM,KAAK,KAAK,WAAW,EAAE,OAAO,EAAE,YAAY,UAAU;IAC5D,KAAK,OAAO,KAAK,4CAA4C,GAAG;IAChE;GACF;GACA,SAAS,MAAM,QAAQ,QAAQ,cAAc,EAAE;EACjD,SAAS,KAAK;GACZ,MAAM,QAAQ,UAAU,GAAG;GAC3B,IAAI,aAAa;IACf,MAAM,KAAK,KAAK,WAAW,EAAE,OAAO,EAAE,YAAY,KAAK;IACvD,KAAK,OAAO,KAAK,oDAAoD;KAAE,GAAG;KAAK;IAAI,CAAC;IACpF;GACF;GACA,MAAM,KAAK,KAAK,YAAY,EAAE,OAAO,EAAE,YAAY,KAAK;GACxD,KAAK,OAAO,KAAK,wCAAwC;IAAE,GAAG;IAAK;GAAI,CAAC;GACxE,MAAM;EACR;EACA,IAAI,OAAO,cAAc,SAAS,GAChC,MAAM,KAAK,QAAQ,WAAW,EAAE,OAAO,OAAO,aAAa;EAE7D,IAAI,CAAC,OAAO,UAAU;GACpB,MAAM,QAAQ,OAAO,SAAA;GACrB,MAAM,KAAK,KAAK,WAAW,EAAE,OAAO,EAAE,YAAY,KAAK;GACvD,KAAK,OAAO,KAAK,8CAA8C;IAAE,GAAG;IAAK;GAAM,CAAC;GAChF;EACF;EACA,MAAM,KAAK,KAAK,SAAS,EAAE,OAAO,EAAE,4BAAY,IAAI,KAAK,CAAC;EAC1D,KAAK,OAAO,KAAK,0BAA0B,GAAG;CAChD;AACF"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DomainEvent, Page } from "../types/shared.js";
|
|
2
2
|
import { Event } from "../types/schemas/content.js";
|
|
3
3
|
import { Automation, AutomationKind, AutomationRun, AutomationRunsQuery, AutomationsQuery, AvailableActions, AvailableTriggers, CreateAutomationInput, CreateWebhookInput, CreatedWebhook, UpdateAutomationInput, UpdateWebhookInput, Webhook, WebhookSecret } from "../types/schemas/automations.js";
|
|
4
|
-
import { AutomationDispatch, AutomationEngine, AutomationExecutor, DeliveryDispatch, ScheduledAutomation, WebhookRequest, WebhookResponse, WebhookSender } from "../types/automations.js";
|
|
4
|
+
import { AutomationDispatch, AutomationEngine, AutomationExecutor, DeliveryDispatch, DeliveryExecutor, ScheduledAutomation, WebhookRequest, WebhookResponse, WebhookSender } from "../types/automations.js";
|
|
5
5
|
import { Delivery, NotificationCategory, NotificationFilter, RecipientList, StoredNotification, SubjectRef } from "../types/notifications.js";
|
|
6
6
|
import "../types/index.js";
|
|
7
7
|
import { Actor } from "../shared/actor.js";
|
|
@@ -184,5 +184,5 @@ interface AutomationsTxScope {
|
|
|
184
184
|
}
|
|
185
185
|
type AutomationsUnitOfWork = UnitOfWork<AutomationsTxScope>;
|
|
186
186
|
//#endregion
|
|
187
|
-
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 };
|
|
187
|
+
export { type AutomationDispatch, type AutomationEngine, type AutomationExecutor, AutomationRunsRepository, AutomationsRepository, AutomationsService, AutomationsTxScope, AutomationsUnitOfWork, ChatDebounce, type DeliveryDispatch, type DeliveryExecutor, ListedNotification, NewAutomation, NewAutomationRun, NewDelivery, NewNotification, NotificationCategories, NotificationPage, NotificationReads, NotificationsRepository, NotificationsService, RecipientRule, UnreadCounts, type WebhookRequest, type WebhookResponse, type WebhookSender };
|
|
188
188
|
//# sourceMappingURL=ports.d.ts.map
|
|
@@ -1,7 +1,7 @@
|
|
|
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,
|
|
4
|
+
import { AutomationDispatch, AutomationEngine, AutomationExecutor, ScheduledAutomation, WebhookSender } from "../types/automations.js";
|
|
5
5
|
import "../types/index.js";
|
|
6
6
|
import { Actor } from "../shared/actor.js";
|
|
7
7
|
import { CredentialStore } from "../shared/ports.js";
|
|
@@ -10,8 +10,6 @@ import { Mailer, MailerLookups } from "../shared/mailer.js";
|
|
|
10
10
|
import "./types.js";
|
|
11
11
|
import { AutomationRunsRepository, AutomationsRepository, AutomationsService, AutomationsUnitOfWork } from "./ports.js";
|
|
12
12
|
import { CommunityMailReads, GrantWriter } from "./community-mail.js";
|
|
13
|
-
import { NotificationDelivery } from "./notifications/delivery.js";
|
|
14
|
-
import { NotificationProcessor } from "./notifications/processor.js";
|
|
15
13
|
import { IntegrationsService } from "../integrations/ports.js";
|
|
16
14
|
import "../integrations/index.js";
|
|
17
15
|
//#region src/automations/service.d.ts
|
|
@@ -30,8 +28,6 @@ type AutomationsServiceParams = {
|
|
|
30
28
|
webhooks: WebhookSender;
|
|
31
29
|
mail: Omit<CommunityMailReads, 'recentMessageRecipients'>;
|
|
32
30
|
grants: GrantWriter;
|
|
33
|
-
delivery: Pick<NotificationDelivery, 'run'>;
|
|
34
|
-
processor: Pick<NotificationProcessor, 'process'>;
|
|
35
31
|
logger?: Logger;
|
|
36
32
|
};
|
|
37
33
|
declare class AutomationsServiceImpl implements AutomationsService, AutomationExecutor {
|
|
@@ -47,8 +43,6 @@ declare class AutomationsServiceImpl implements AutomationsService, AutomationEx
|
|
|
47
43
|
private readonly webhooks;
|
|
48
44
|
private readonly mail;
|
|
49
45
|
private readonly grants;
|
|
50
|
-
private readonly delivery;
|
|
51
|
-
private readonly processor;
|
|
52
46
|
private readonly logger;
|
|
53
47
|
constructor(params: AutomationsServiceParams);
|
|
54
48
|
handle(event: DomainEvent): Promise<void>;
|
|
@@ -60,7 +54,6 @@ declare class AutomationsServiceImpl implements AutomationsService, AutomationEx
|
|
|
60
54
|
/** Best-effort: logs and never rethrows, so an org automation never fails `handle`. */
|
|
61
55
|
private recordFailure;
|
|
62
56
|
runAction(d: AutomationDispatch, index: number): Promise<AutomationActionResult>;
|
|
63
|
-
runDelivery(d: DeliveryDispatch, lastAttempt: boolean): Promise<void>;
|
|
64
57
|
listSchedules(): Promise<ScheduledAutomation[]>;
|
|
65
58
|
fireSchedule(orgId: string, automationId: string, at: Date): Promise<void>;
|
|
66
59
|
finalize(d: AutomationDispatch, results: AutomationActionResult[]): Promise<void>;
|
|
@@ -134,8 +134,6 @@ var AutomationsServiceImpl = class {
|
|
|
134
134
|
webhooks;
|
|
135
135
|
mail;
|
|
136
136
|
grants;
|
|
137
|
-
delivery;
|
|
138
|
-
processor;
|
|
139
137
|
logger;
|
|
140
138
|
constructor(params) {
|
|
141
139
|
this.repo = params.repo;
|
|
@@ -153,8 +151,6 @@ var AutomationsServiceImpl = class {
|
|
|
153
151
|
recentMessageRecipients: (orgId, automationId, chatId, since) => this.runsRepo.recentRecipients(orgId, automationId, chatId, since)
|
|
154
152
|
};
|
|
155
153
|
this.grants = params.grants;
|
|
156
|
-
this.delivery = params.delivery;
|
|
157
|
-
this.processor = params.processor;
|
|
158
154
|
this.logger = params.logger ?? noopLogger;
|
|
159
155
|
}
|
|
160
156
|
async handle(event) {
|
|
@@ -172,7 +168,6 @@ var AutomationsServiceImpl = class {
|
|
|
172
168
|
}
|
|
173
169
|
await this.dispatchOne(automation, event);
|
|
174
170
|
}
|
|
175
|
-
await this.processor.process(event);
|
|
176
171
|
}
|
|
177
172
|
async reconcileSchedules(orgId) {
|
|
178
173
|
try {
|
|
@@ -317,9 +312,6 @@ var AutomationsServiceImpl = class {
|
|
|
317
312
|
...failed.length > 0 ? { error: `delivery failed for ${failed.length} recipients: ${failed.join(", ")}` } : {}
|
|
318
313
|
};
|
|
319
314
|
}
|
|
320
|
-
runDelivery(d, lastAttempt) {
|
|
321
|
-
return this.delivery.run(d, lastAttempt);
|
|
322
|
-
}
|
|
323
315
|
async listSchedules() {
|
|
324
316
|
return this.repo.listSchedules();
|
|
325
317
|
}
|
|
@@ -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 { assertGrantsAllowed, assertReadsEvents } from './event-permissions.js';\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 type { Actor } from '../shared/actor.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 systemActor: (orgId: string) => Actor;\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 systemActor: (orgId: string) => Actor;\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.systemActor = params.systemActor;\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(input: CreateAutomationInput, actor: Actor): Promise<Automation> {\n actor.permissions.require('automations:write');\n const orgId = actor.orgId;\n assertCatalogTriggers(input.triggers, true);\n assertAuthorableActions(input.actions);\n assertGrantsAllowed(actor, 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', {\n orgId,\n automationId: automation.id,\n actorId: actor.id,\n });\n await this.reconcileSchedules(orgId);\n return automation;\n }\n\n async update(id: string, input: UpdateAutomationInput, actor: Actor): Promise<Automation> {\n actor.permissions.require('automations:write');\n const orgId = actor.orgId;\n if (input.triggers !== undefined) {\n assertCatalogTriggers(input.triggers, true);\n }\n if (input.actions !== undefined) {\n assertAuthorableActions(input.actions);\n assertGrantsAllowed(actor, input.actions);\n }\n const existing = await this.repo.findById(orgId, id);\n if (!existing) {\n throw new NotFoundError('Automation', id);\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 throw new NotFoundError('Automation', id);\n }\n this.logger.info('automation updated', { orgId, automationId: id });\n await this.reconcileSchedules(orgId);\n return automation;\n }\n\n async delete(id: string, actor: Actor): Promise<void> {\n actor.permissions.require('automations:delete');\n const orgId = actor.orgId;\n const existing = await this.repo.findById(orgId, id);\n if (!existing) {\n throw new NotFoundError('Automation', id);\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 throw new NotFoundError('Automation', id);\n }\n this.logger.info('automation deleted', { orgId, automationId: id });\n await this.reconcileSchedules(orgId);\n }\n\n async list(query: AutomationsQuery | undefined, actor: Actor): Promise<Automation[]> {\n actor.permissions.require('automations:read');\n const automations = await this.repo.listByOrg(actor.orgId, query?.kind);\n return automations.map(toAutomationView);\n }\n\n async get(id: string, actor: Actor): Promise<Automation> {\n actor.permissions.require('automations:read');\n const automation = await this.repo.findById(actor.orgId, id);\n if (!automation) {\n throw new NotFoundError('Automation', id);\n }\n return toAutomationView(automation);\n }\n\n async listRuns(\n automationId: string,\n query: AutomationRunsQuery,\n actor: Actor,\n ): Promise<Page<AutomationRun>> {\n actor.permissions.require('automations:read');\n const page = await this.runsRepo.list(actor.orgId, automationId, query);\n return { ...page, rows: page.rows.map(toRunView) };\n }\n\n async rerun(automationId: string, runId: string, actor: Actor): Promise<AutomationRun> {\n actor.permissions.require('automations:write');\n const orgId = actor.orgId;\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 assertGrantsAllowed(actor, automation.actions);\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(actor: Actor): Promise<Webhook[]> {\n actor.permissions.require('automations:read');\n const automations = await this.repo.listByOrg(actor.orgId, 'webhook');\n return automations.map(toWebhook);\n }\n\n async getWebhook(id: string, actor: Actor): Promise<Webhook> {\n actor.permissions.require('automations:read');\n return toWebhook(await this.requireWebhook(actor.orgId, id));\n }\n\n async createWebhook(input: CreateWebhookInput, actor: Actor): Promise<CreatedWebhook> {\n actor.permissions.require('automations:write');\n const orgId = actor.orgId;\n const events = unique(input.events);\n assertCatalogTriggers(events, false);\n assertReadsEvents(actor, events);\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(id: string, input: UpdateWebhookInput, actor: Actor): Promise<Webhook> {\n actor.permissions.require('automations:write');\n const orgId = actor.orgId;\n const existing = await this.requireWebhook(orgId, id);\n const events = input.events === undefined ? undefined : unique(input.events);\n if (events) {\n assertCatalogTriggers(events, false);\n assertReadsEvents(actor, events);\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 throw new NotFoundError('Webhook', id);\n }\n this.logger.info('webhook updated', { orgId, automationId: id });\n return toWebhook(updated);\n }\n\n async deleteWebhook(id: string, actor: Actor): Promise<void> {\n actor.permissions.require('automations:delete');\n const orgId = actor.orgId;\n const existing = await this.requireWebhook(orgId, id);\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 throw new NotFoundError('Webhook', id);\n }\n this.logger.info('webhook deleted', { orgId, automationId: id });\n }\n\n async revealWebhookSecret(id: string, actor: Actor): Promise<WebhookSecret> {\n actor.permissions.require('automations:write');\n const orgId = actor.orgId;\n const existing = await this.requireWebhook(orgId, id);\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(id: string, actor: Actor): Promise<WebhookSecret> {\n actor.permissions.require('automations:write');\n const orgId = actor.orgId;\n const existing = await this.requireWebhook(orgId, id);\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 requireWebhook(orgId: string, id: string): Promise<Automation> {\n const automation = await this.repo.findById(orgId, id);\n if (automation?.kind !== 'webhook') {\n throw new NotFoundError('Webhook', id);\n }\n return automation;\n }\n\n availableActions(actor: Actor): AvailableActions {\n actor.permissions.require('automations:read');\n return [\n ...catalogActions(),\n ...this.integrations.available(this.systemActor(actor.orgId)).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(actor: Actor): AvailableTriggers {\n actor.permissions.require('automations:read');\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 { assertGrantsAllowed, assertReadsEvents } from './event-permissions.js';\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 { 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, 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 type { Actor } from '../shared/actor.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 systemActor: (orgId: string) => Actor;\n credentials: CredentialStore;\n webhooks: WebhookSender;\n mail: Omit<CommunityMailReads, 'recentMessageRecipients'>;\n grants: GrantWriter;\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 systemActor: (orgId: string) => Actor;\n private readonly credentials: CredentialStore;\n private readonly webhooks: WebhookSender;\n private readonly mail: CommunityMailReads;\n private readonly grants: GrantWriter;\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.systemActor = params.systemActor;\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.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 }\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 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(input: CreateAutomationInput, actor: Actor): Promise<Automation> {\n actor.permissions.require('automations:write');\n const orgId = actor.orgId;\n assertCatalogTriggers(input.triggers, true);\n assertAuthorableActions(input.actions);\n assertGrantsAllowed(actor, 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', {\n orgId,\n automationId: automation.id,\n actorId: actor.id,\n });\n await this.reconcileSchedules(orgId);\n return automation;\n }\n\n async update(id: string, input: UpdateAutomationInput, actor: Actor): Promise<Automation> {\n actor.permissions.require('automations:write');\n const orgId = actor.orgId;\n if (input.triggers !== undefined) {\n assertCatalogTriggers(input.triggers, true);\n }\n if (input.actions !== undefined) {\n assertAuthorableActions(input.actions);\n assertGrantsAllowed(actor, input.actions);\n }\n const existing = await this.repo.findById(orgId, id);\n if (!existing) {\n throw new NotFoundError('Automation', id);\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 throw new NotFoundError('Automation', id);\n }\n this.logger.info('automation updated', { orgId, automationId: id });\n await this.reconcileSchedules(orgId);\n return automation;\n }\n\n async delete(id: string, actor: Actor): Promise<void> {\n actor.permissions.require('automations:delete');\n const orgId = actor.orgId;\n const existing = await this.repo.findById(orgId, id);\n if (!existing) {\n throw new NotFoundError('Automation', id);\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 throw new NotFoundError('Automation', id);\n }\n this.logger.info('automation deleted', { orgId, automationId: id });\n await this.reconcileSchedules(orgId);\n }\n\n async list(query: AutomationsQuery | undefined, actor: Actor): Promise<Automation[]> {\n actor.permissions.require('automations:read');\n const automations = await this.repo.listByOrg(actor.orgId, query?.kind);\n return automations.map(toAutomationView);\n }\n\n async get(id: string, actor: Actor): Promise<Automation> {\n actor.permissions.require('automations:read');\n const automation = await this.repo.findById(actor.orgId, id);\n if (!automation) {\n throw new NotFoundError('Automation', id);\n }\n return toAutomationView(automation);\n }\n\n async listRuns(\n automationId: string,\n query: AutomationRunsQuery,\n actor: Actor,\n ): Promise<Page<AutomationRun>> {\n actor.permissions.require('automations:read');\n const page = await this.runsRepo.list(actor.orgId, automationId, query);\n return { ...page, rows: page.rows.map(toRunView) };\n }\n\n async rerun(automationId: string, runId: string, actor: Actor): Promise<AutomationRun> {\n actor.permissions.require('automations:write');\n const orgId = actor.orgId;\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 assertGrantsAllowed(actor, automation.actions);\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(actor: Actor): Promise<Webhook[]> {\n actor.permissions.require('automations:read');\n const automations = await this.repo.listByOrg(actor.orgId, 'webhook');\n return automations.map(toWebhook);\n }\n\n async getWebhook(id: string, actor: Actor): Promise<Webhook> {\n actor.permissions.require('automations:read');\n return toWebhook(await this.requireWebhook(actor.orgId, id));\n }\n\n async createWebhook(input: CreateWebhookInput, actor: Actor): Promise<CreatedWebhook> {\n actor.permissions.require('automations:write');\n const orgId = actor.orgId;\n const events = unique(input.events);\n assertCatalogTriggers(events, false);\n assertReadsEvents(actor, events);\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(id: string, input: UpdateWebhookInput, actor: Actor): Promise<Webhook> {\n actor.permissions.require('automations:write');\n const orgId = actor.orgId;\n const existing = await this.requireWebhook(orgId, id);\n const events = input.events === undefined ? undefined : unique(input.events);\n if (events) {\n assertCatalogTriggers(events, false);\n assertReadsEvents(actor, events);\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 throw new NotFoundError('Webhook', id);\n }\n this.logger.info('webhook updated', { orgId, automationId: id });\n return toWebhook(updated);\n }\n\n async deleteWebhook(id: string, actor: Actor): Promise<void> {\n actor.permissions.require('automations:delete');\n const orgId = actor.orgId;\n const existing = await this.requireWebhook(orgId, id);\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 throw new NotFoundError('Webhook', id);\n }\n this.logger.info('webhook deleted', { orgId, automationId: id });\n }\n\n async revealWebhookSecret(id: string, actor: Actor): Promise<WebhookSecret> {\n actor.permissions.require('automations:write');\n const orgId = actor.orgId;\n const existing = await this.requireWebhook(orgId, id);\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(id: string, actor: Actor): Promise<WebhookSecret> {\n actor.permissions.require('automations:write');\n const orgId = actor.orgId;\n const existing = await this.requireWebhook(orgId, id);\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 requireWebhook(orgId: string, id: string): Promise<Automation> {\n const automation = await this.repo.findById(orgId, id);\n if (automation?.kind !== 'webhook') {\n throw new NotFoundError('Webhook', id);\n }\n return automation;\n }\n\n availableActions(actor: Actor): AvailableActions {\n actor.permissions.require('automations:read');\n return [\n ...catalogActions(),\n ...this.integrations.available(this.systemActor(actor.orgId)).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(actor: Actor): AvailableTriggers {\n actor.permissions.require('automations:read');\n return { triggers: catalogTriggers() };\n }\n}\n"],"mappings":"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"}
|
|
@@ -14,7 +14,7 @@ type ContentServiceParams = {
|
|
|
14
14
|
repo: ContentRepository;
|
|
15
15
|
uow: ContentUnitOfWork;
|
|
16
16
|
libraryAssets: ContentAssetReader;
|
|
17
|
-
files: Pick<AssetFiles, 'readyFiles' | 'syncPublicImages'>;
|
|
17
|
+
files: Pick<AssetFiles, 'readyFiles' | 'fileLinks' | 'syncPublicImages'>;
|
|
18
18
|
fileUrls: FileUrls;
|
|
19
19
|
scheduler: Pick<Scheduler, 'at' | 'cancel'>;
|
|
20
20
|
logger?: Logger;
|
|
@@ -32,15 +32,18 @@ declare class ContentService implements CourseManagementService, DownloadablesSe
|
|
|
32
32
|
private requireAny;
|
|
33
33
|
private visibleIds;
|
|
34
34
|
private withThumbnail;
|
|
35
|
+
private withThumbnails;
|
|
35
36
|
private syncThumbnail;
|
|
36
37
|
private withdrawThumbnail;
|
|
37
38
|
private spaceView;
|
|
39
|
+
private spaceViews;
|
|
38
40
|
private communityPublished;
|
|
39
41
|
private oneSpaceView;
|
|
40
42
|
private syncSpaceImages;
|
|
41
43
|
private pageWithThumbnails;
|
|
42
44
|
private downloadFiles;
|
|
43
45
|
private eventFiles;
|
|
46
|
+
private listedFiles;
|
|
44
47
|
private assertAttachable;
|
|
45
48
|
private courseAssetIds;
|
|
46
49
|
private downloadAssetIds;
|
package/dist/content/service.js
CHANGED
|
@@ -93,11 +93,17 @@ var ContentService = class {
|
|
|
93
93
|
}
|
|
94
94
|
return ids;
|
|
95
95
|
}
|
|
96
|
-
withThumbnail(item) {
|
|
97
|
-
|
|
96
|
+
async withThumbnail(item) {
|
|
97
|
+
const [view] = await this.withThumbnails([item]);
|
|
98
|
+
return view;
|
|
99
|
+
}
|
|
100
|
+
async withThumbnails(items) {
|
|
101
|
+
const gated = items.flatMap((item) => item.thumbnailAssetId && !shownPublicly(item) ? [{ assetId: item.thumbnailAssetId }] : []);
|
|
102
|
+
const links = gated.length > 0 ? await this.files.fileLinks(items[0].orgId, gated) : {};
|
|
103
|
+
return items.map((item) => ({
|
|
98
104
|
...item,
|
|
99
|
-
thumbnailUrl: !item.thumbnailAssetId ? null : shownPublicly(item) ? this.fileUrls.publicImage(item.orgId, item.id, item.thumbnailAssetId) :
|
|
100
|
-
};
|
|
105
|
+
thumbnailUrl: !item.thumbnailAssetId ? null : shownPublicly(item) ? this.fileUrls.publicImage(item.orgId, item.id, item.thumbnailAssetId) : links[item.thumbnailAssetId] ?? null
|
|
106
|
+
}));
|
|
101
107
|
}
|
|
102
108
|
syncThumbnail(item) {
|
|
103
109
|
return this.files.syncPublicImages(item.orgId, item.id, shownPublicly(item) && item.thumbnailAssetId ? [item.thumbnailAssetId] : []);
|
|
@@ -105,14 +111,22 @@ var ContentService = class {
|
|
|
105
111
|
withdrawThumbnail(orgId, id) {
|
|
106
112
|
return this.files.syncPublicImages(orgId, id, []);
|
|
107
113
|
}
|
|
108
|
-
spaceView(space, published) {
|
|
109
|
-
const
|
|
110
|
-
return
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
114
|
+
async spaceView(space, published) {
|
|
115
|
+
const [view] = await this.spaceViews([space], published);
|
|
116
|
+
return view;
|
|
117
|
+
}
|
|
118
|
+
async spaceViews(spaces, published) {
|
|
119
|
+
const gated = published ? [] : spaces.flatMap((space) => spaceImageIds(space));
|
|
120
|
+
const links = gated.length > 0 ? await this.files.fileLinks(spaces[0].orgId, gated.map((assetId) => ({ assetId }))) : {};
|
|
121
|
+
return spaces.map((space) => {
|
|
122
|
+
const url = (assetId) => !assetId ? null : published ? this.fileUrls.publicImage(space.orgId, space.id, assetId) : links[assetId] ?? null;
|
|
123
|
+
return {
|
|
124
|
+
...space,
|
|
125
|
+
coverUrl: url(space.coverAssetId),
|
|
126
|
+
backgroundUrl: url(space.backgroundAssetId),
|
|
127
|
+
iconUrl: url(space.iconAssetId)
|
|
128
|
+
};
|
|
129
|
+
});
|
|
116
130
|
}
|
|
117
131
|
async communityPublished(orgId, communityId) {
|
|
118
132
|
return (await this.repo.findCommunityById(orgId, communityId))?.status === "published";
|
|
@@ -123,31 +137,34 @@ var ContentService = class {
|
|
|
123
137
|
syncSpaceImages(space, published) {
|
|
124
138
|
return this.files.syncPublicImages(space.orgId, space.id, published ? spaceImageIds(space) : []);
|
|
125
139
|
}
|
|
126
|
-
pageWithThumbnails(page) {
|
|
140
|
+
async pageWithThumbnails(page) {
|
|
127
141
|
return {
|
|
128
142
|
...page,
|
|
129
|
-
rows:
|
|
143
|
+
rows: await this.withThumbnails(page.rows)
|
|
130
144
|
};
|
|
131
145
|
}
|
|
132
|
-
|
|
146
|
+
downloadFiles(orgId, links) {
|
|
147
|
+
return this.listedFiles(orgId, links);
|
|
148
|
+
}
|
|
149
|
+
eventFiles(orgId, links) {
|
|
150
|
+
return this.listedFiles(orgId, links);
|
|
151
|
+
}
|
|
152
|
+
async listedFiles(orgId, links) {
|
|
133
153
|
const ready = await this.files.readyFiles(orgId, links.map((link) => link.assetId));
|
|
134
|
-
|
|
154
|
+
const urls = await this.files.fileLinks(orgId, links.flatMap((link) => {
|
|
135
155
|
const file = ready[link.assetId];
|
|
136
156
|
return file ? [{
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
url: this.fileUrls.downloadFile(link.downloadId, link.assetId)
|
|
157
|
+
assetId: link.assetId,
|
|
158
|
+
saveAs: link.displayName ?? file.filename
|
|
140
159
|
}] : [];
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
async eventFiles(orgId, links) {
|
|
144
|
-
const ready = await this.files.readyFiles(orgId, links.map((link) => link.assetId));
|
|
160
|
+
}));
|
|
145
161
|
return links.flatMap((link) => {
|
|
146
162
|
const file = ready[link.assetId];
|
|
147
|
-
|
|
163
|
+
const url = urls[link.assetId];
|
|
164
|
+
return file && url ? [{
|
|
148
165
|
...link,
|
|
149
166
|
...file,
|
|
150
|
-
url
|
|
167
|
+
url
|
|
151
168
|
}] : [];
|
|
152
169
|
});
|
|
153
170
|
}
|
|
@@ -321,9 +338,13 @@ var ContentService = class {
|
|
|
321
338
|
async listCourseActivityViews(courseId, actor) {
|
|
322
339
|
this.require(actor, "courses:read", courseId);
|
|
323
340
|
const [activities, links] = await Promise.all([this.repo.listActivitiesForCourse(actor.orgId, courseId), this.repo.listActivityAssetsForCourse(actor.orgId, courseId)]);
|
|
341
|
+
const urls = await this.files.fileLinks(actor.orgId, links.map((link) => ({ assetId: link.assetId })));
|
|
324
342
|
return activities.map((activity) => ({
|
|
325
343
|
...activity,
|
|
326
|
-
assetUrls: Object.fromEntries(links.
|
|
344
|
+
assetUrls: Object.fromEntries(links.flatMap((link) => {
|
|
345
|
+
const url = urls[link.assetId];
|
|
346
|
+
return link.activityId === activity.id && url ? [[link.assetId, url]] : [];
|
|
347
|
+
}))
|
|
327
348
|
}));
|
|
328
349
|
}
|
|
329
350
|
async listCourseActivityAssets(courseId, actor) {
|
|
@@ -763,7 +784,8 @@ var ContentService = class {
|
|
|
763
784
|
async findEvents(ids, actor) {
|
|
764
785
|
for (const id of ids) this.require(actor, "events:read", id);
|
|
765
786
|
if (ids.length === 0) return [];
|
|
766
|
-
|
|
787
|
+
const found = await this.repo.findEventsByIds(actor.orgId, ids);
|
|
788
|
+
return this.withThumbnails(found);
|
|
767
789
|
}
|
|
768
790
|
async createEvent(input, actor) {
|
|
769
791
|
this.require(actor, "events:write");
|
|
@@ -1269,7 +1291,8 @@ var ContentService = class {
|
|
|
1269
1291
|
this.requireAny(actor, COMMUNITY_READERS, communityId);
|
|
1270
1292
|
const community = await this.repo.findCommunityById(actor.orgId, communityId);
|
|
1271
1293
|
if (!community) throw new NotFoundError("Community", communityId);
|
|
1272
|
-
|
|
1294
|
+
const spaces = await this.repo.listSpaces(actor.orgId, communityId);
|
|
1295
|
+
return this.spaceViews(spaces, community.status === "published");
|
|
1273
1296
|
}
|
|
1274
1297
|
async getDmSettings(communityId, actor) {
|
|
1275
1298
|
this.require(actor, "communities:read", communityId);
|
|
@@ -1529,7 +1552,7 @@ var ContentService = class {
|
|
|
1529
1552
|
actorId: actor.id
|
|
1530
1553
|
});
|
|
1531
1554
|
const published = await this.communityPublished(orgId, communityId);
|
|
1532
|
-
return
|
|
1555
|
+
return this.spaceViews(spaces, published);
|
|
1533
1556
|
}
|
|
1534
1557
|
async setSpaceTopics(communityId, spaceId, input, actor) {
|
|
1535
1558
|
this.require(actor, "communities:write", communityId);
|