@headless-lms/core 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/events.d.ts +27 -21
- package/dist/assets/file-urls.d.ts +11 -7
- package/dist/assets/file-urls.js +14 -4
- package/dist/assets/file-urls.js.map +1 -1
- package/dist/assets/index.d.ts +4 -4
- package/dist/assets/index.js +3 -3
- package/dist/assets/ports.d.ts +34 -15
- package/dist/assets/service.d.ts +30 -14
- package/dist/assets/service.js +230 -56
- package/dist/assets/service.js.map +1 -1
- package/dist/automations/actions.js.map +1 -1
- package/dist/automations/catalog.js +69 -4
- package/dist/automations/catalog.js.map +1 -1
- package/dist/automations/community-derivations.js +16 -16
- package/dist/automations/community-derivations.js.map +1 -1
- package/dist/automations/community-mail.d.ts +34 -4
- package/dist/automations/community-mail.js +32 -4
- package/dist/automations/community-mail.js.map +1 -1
- package/dist/automations/event-permissions.d.ts +12 -0
- package/dist/automations/event-permissions.js +38 -0
- package/dist/automations/event-permissions.js.map +1 -0
- package/dist/automations/index.d.ts +2 -1
- package/dist/automations/index.js +2 -1
- package/dist/automations/notifications/applicable.d.ts +8 -0
- package/dist/automations/notifications/applicable.js +12 -0
- package/dist/automations/notifications/applicable.js.map +1 -0
- package/dist/automations/notifications/debounce.js +1 -1
- package/dist/automations/notifications/debounce.js.map +1 -1
- package/dist/automations/notifications/defaults.js +33 -3
- package/dist/automations/notifications/defaults.js.map +1 -1
- package/dist/automations/notifications/processor.js +3 -3
- package/dist/automations/notifications/processor.js.map +1 -1
- package/dist/automations/notifications/rules/content.js +60 -3
- package/dist/automations/notifications/rules/content.js.map +1 -1
- package/dist/automations/notifications/rules/discussion.js +29 -29
- package/dist/automations/notifications/rules/discussion.js.map +1 -1
- package/dist/automations/notifications/rules/entitlements.js +36 -9
- package/dist/automations/notifications/rules/entitlements.js.map +1 -1
- package/dist/automations/notifications/rules/events.d.ts +9 -0
- package/dist/automations/notifications/rules/events.js +27 -0
- package/dist/automations/notifications/rules/events.js.map +1 -0
- package/dist/automations/notifications/rules/organizations.js +17 -10
- package/dist/automations/notifications/rules/organizations.js.map +1 -1
- package/dist/automations/notifications/rules/recipients.js +0 -1
- package/dist/automations/notifications/rules/recipients.js.map +1 -1
- package/dist/automations/notifications/service.d.ts +8 -6
- package/dist/automations/notifications/service.js +13 -13
- package/dist/automations/notifications/service.js.map +1 -1
- package/dist/automations/notifications/types.js +94 -7
- package/dist/automations/notifications/types.js.map +1 -1
- package/dist/automations/ports.d.ts +30 -31
- package/dist/automations/service.d.ts +20 -17
- package/dist/automations/service.js +91 -62
- package/dist/automations/service.js.map +1 -1
- package/dist/content/event-sight.d.ts +11 -0
- package/dist/content/event-sight.js +11 -0
- package/dist/content/event-sight.js.map +1 -0
- package/dist/content/events.d.ts +413 -72
- package/dist/content/events.js +34 -1
- 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/jobs.d.ts +16 -0
- package/dist/content/jobs.js +37 -0
- package/dist/content/jobs.js.map +1 -0
- package/dist/content/model.d.ts +2 -2
- package/dist/content/notification-categories.d.ts +5 -4
- package/dist/content/notification-categories.js +15 -6
- package/dist/content/notification-categories.js.map +1 -1
- package/dist/content/ports.d.ts +113 -68
- package/dist/content/service.d.ts +99 -67
- package/dist/content/service.js +680 -139
- package/dist/content/service.js.map +1 -1
- package/dist/content/titles.d.ts +6 -3
- package/dist/content/titles.js +37 -24
- package/dist/content/titles.js.map +1 -1
- package/dist/content/types.d.ts +2 -2
- package/dist/discussion/authors.d.ts +4 -2
- package/dist/discussion/authors.js +9 -5
- package/dist/discussion/authors.js.map +1 -1
- package/dist/discussion/chats.d.ts +56 -37
- package/dist/discussion/chats.js +393 -161
- package/dist/discussion/chats.js.map +1 -1
- package/dist/discussion/events.d.ts +77 -39
- package/dist/discussion/events.js +41 -3
- package/dist/discussion/events.js.map +1 -1
- package/dist/discussion/index.d.ts +8 -5
- package/dist/discussion/index.js +5 -3
- package/dist/discussion/live.d.ts +106 -0
- package/dist/discussion/live.js +642 -0
- package/dist/discussion/live.js.map +1 -0
- package/dist/discussion/mentions.js +2 -2
- package/dist/discussion/mentions.js.map +1 -1
- package/dist/discussion/ports.d.ts +182 -100
- package/dist/discussion/ports.js.map +1 -1
- package/dist/discussion/posts.d.ts +45 -39
- package/dist/discussion/posts.js +268 -154
- package/dist/discussion/posts.js.map +1 -1
- package/dist/discussion/rules.d.ts +18 -0
- package/dist/discussion/rules.js +22 -0
- package/dist/discussion/rules.js.map +1 -0
- package/dist/discussion/service.d.ts +37 -31
- package/dist/discussion/service.js +252 -188
- package/dist/discussion/service.js.map +1 -1
- package/dist/discussion/subscriptions.d.ts +8 -8
- package/dist/discussion/subscriptions.js +29 -13
- package/dist/discussion/subscriptions.js.map +1 -1
- package/dist/discussion/titles.d.ts +4 -2
- package/dist/discussion/titles.js +27 -13
- package/dist/discussion/titles.js.map +1 -1
- package/dist/entitlements/events.d.ts +20 -20
- package/dist/entitlements/index.d.ts +4 -4
- package/dist/entitlements/index.js +2 -2
- package/dist/entitlements/model.d.ts +2 -2
- package/dist/entitlements/ports.d.ts +65 -39
- package/dist/entitlements/rules.d.ts +3 -6
- package/dist/entitlements/rules.js +5 -10
- package/dist/entitlements/rules.js.map +1 -1
- package/dist/entitlements/service.d.ts +40 -28
- package/dist/entitlements/service.js +370 -163
- package/dist/entitlements/service.js.map +1 -1
- package/dist/identity/devices.d.ts +3 -3
- package/dist/identity/devices.js +7 -5
- package/dist/identity/devices.js.map +1 -1
- package/dist/identity/events.d.ts +2 -2
- package/dist/identity/index.d.ts +2 -2
- package/dist/identity/index.js +2 -2
- package/dist/identity/notification-settings.d.ts +1 -1
- package/dist/identity/notification-settings.js +3 -2
- package/dist/identity/notification-settings.js.map +1 -1
- package/dist/identity/ports.d.ts +7 -7
- package/dist/identity/service.d.ts +8 -7
- package/dist/identity/service.js +17 -13
- package/dist/identity/service.js.map +1 -1
- package/dist/integrations/events.d.ts +3 -3
- package/dist/integrations/ports.d.ts +9 -8
- package/dist/integrations/service.d.ts +9 -8
- package/dist/integrations/service.js +33 -16
- package/dist/integrations/service.js.map +1 -1
- package/dist/organizations/default-roles.d.ts +22 -0
- package/dist/organizations/default-roles.js +89 -0
- package/dist/organizations/default-roles.js.map +1 -0
- package/dist/organizations/effective-permissions.d.ts +45 -0
- package/dist/organizations/effective-permissions.js +113 -0
- package/dist/organizations/effective-permissions.js.map +1 -0
- package/dist/organizations/events.d.ts +320 -181
- package/dist/organizations/events.js +90 -18
- package/dist/organizations/events.js.map +1 -1
- package/dist/organizations/index.d.ts +14 -10
- package/dist/organizations/index.js +7 -3
- package/dist/organizations/members.d.ts +66 -8
- package/dist/organizations/members.js.map +1 -1
- package/dist/organizations/notification-catalog.d.ts +31 -0
- package/dist/organizations/notification-catalog.js +32 -0
- package/dist/organizations/notification-catalog.js.map +1 -0
- package/dist/organizations/notification-categories.d.ts +6 -4
- package/dist/organizations/notification-categories.js +25 -10
- package/dist/organizations/notification-categories.js.map +1 -1
- package/dist/organizations/org-roles.d.ts +33 -0
- package/dist/organizations/org-roles.js +49 -0
- package/dist/organizations/org-roles.js.map +1 -0
- package/dist/organizations/permissions.d.ts +311 -0
- package/dist/organizations/permissions.js +327 -0
- package/dist/organizations/permissions.js.map +1 -0
- package/dist/organizations/ports.d.ts +184 -65
- package/dist/organizations/service.d.ts +102 -35
- package/dist/organizations/service.js +1245 -328
- package/dist/organizations/service.js.map +1 -1
- package/dist/organizations/testing.d.ts +18 -0
- package/dist/organizations/testing.js +37 -0
- package/dist/organizations/testing.js.map +1 -0
- package/dist/organizations/types.d.ts +5 -16
- package/dist/progress/events.d.ts +14 -14
- package/dist/progress/index.d.ts +2 -2
- package/dist/progress/ports.d.ts +11 -3
- package/dist/progress/service.d.ts +6 -5
- package/dist/progress/service.js +23 -8
- package/dist/progress/service.js.map +1 -1
- package/dist/reporting/admin/index.d.ts +6 -4
- package/dist/reporting/admin/model.d.ts +36 -3
- package/dist/reporting/admin/ports.d.ts +39 -14
- package/dist/reporting/admin/service.d.ts +32 -11
- package/dist/reporting/admin/service.js +102 -18
- package/dist/reporting/admin/service.js.map +1 -1
- package/dist/reporting/events/index.d.ts +27 -0
- package/dist/reporting/events/index.js +20 -0
- package/dist/reporting/events/index.js.map +1 -0
- package/dist/reporting/member/calendar.d.ts +11 -0
- package/dist/reporting/member/calendar.js +75 -0
- package/dist/reporting/member/calendar.js.map +1 -0
- package/dist/reporting/member/index.d.ts +3 -2
- package/dist/reporting/member/model.d.ts +26 -5
- package/dist/reporting/member/ports.d.ts +48 -29
- package/dist/reporting/member/service.d.ts +56 -29
- package/dist/reporting/member/service.js +328 -113
- package/dist/reporting/member/service.js.map +1 -1
- package/dist/shared/actor.d.ts +22 -3
- package/dist/shared/errors.d.ts +2 -1
- package/dist/shared/errors.js +3 -1
- package/dist/shared/errors.js.map +1 -1
- package/dist/shared/id.d.ts +4 -0
- package/dist/shared/id.js +4 -0
- package/dist/shared/id.js.map +1 -1
- package/dist/shared/logger.d.ts +2 -1
- package/dist/shared/logger.js +10 -1
- package/dist/shared/logger.js.map +1 -1
- package/dist/shared/ports.d.ts +25 -5
- package/dist/types/content.d.ts +2 -2
- package/dist/types/discussion.d.ts +4 -5
- package/dist/types/email-templates.d.ts +22 -3
- package/dist/types/entitlements.d.ts +2 -2
- package/dist/types/identity.d.ts +13 -16
- package/dist/types/index.d.ts +9 -9
- package/dist/types/index.js +2 -1
- package/dist/types/notifications.d.ts +4 -4
- package/dist/types/organizations.d.ts +3 -2
- package/dist/types/ports.d.ts +8 -1
- package/dist/types/ports.js +7 -1
- package/dist/types/ports.js.map +1 -0
- package/dist/types/schemas/assets.d.ts +5 -5
- package/dist/types/schemas/assets.js +4 -3
- package/dist/types/schemas/assets.js.map +1 -1
- package/dist/types/schemas/content.d.ts +537 -13
- package/dist/types/schemas/content.js +201 -7
- package/dist/types/schemas/content.js.map +1 -1
- package/dist/types/schemas/discussion.d.ts +282 -59
- package/dist/types/schemas/discussion.js +77 -3
- package/dist/types/schemas/discussion.js.map +1 -1
- package/dist/types/schemas/entitlements.d.ts +14 -1
- package/dist/types/schemas/entitlements.js +11 -4
- package/dist/types/schemas/entitlements.js.map +1 -1
- package/dist/types/schemas/index.d.ts +5 -5
- package/dist/types/schemas/index.js +5 -5
- package/dist/types/schemas/notifications.d.ts +13 -12
- package/dist/types/schemas/notifications.js +3 -2
- package/dist/types/schemas/notifications.js.map +1 -1
- package/dist/types/schemas/organizations.d.ts +96 -60
- package/dist/types/schemas/organizations.js +78 -27
- package/dist/types/schemas/organizations.js.map +1 -1
- package/dist/types/schemas/progress.d.ts +3 -4
- package/dist/types/schemas/progress.js +0 -1
- package/dist/types/schemas/progress.js.map +1 -1
- package/package.json +1 -1
- package/dist/organizations/access-scopes.d.ts +0 -19
- package/dist/organizations/access-scopes.js +0 -89
- package/dist/organizations/access-scopes.js.map +0 -1
- package/dist/organizations/roles.d.ts +0 -19
- package/dist/organizations/roles.js +0 -82
- package/dist/organizations/roles.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actions.js","names":[],"sources":["../../src/automations/actions.ts"],"sourcesContent":["// automations context — action runners. `executeAction` maps one\n// AutomationAction against the DomainEvent that triggered its run.\n//\n// SEND_EMAIL_DERIVATIONS is the sendEmail runner's internal derivation table:\n// which (trigger, template) pairings are derivable. A template with no entry\n// (e.g. courseCompleted) makes `executeAction` throw a named error, recorded\n// by the engine as a failed action.\nimport type { EmailTemplateId } from '../types/index.js';\nimport { deliverWebhookInputSchema } from '../types/schemas/index.js';\nimport { entitlementEvents } from '../entitlements/index.js';\nimport type { DomainEvent } from '../shared/ports.js';\nimport { noopLogger } from '../shared/logger.js';\nimport { ActionFailedError, type AutomationAction } from './model.js';\nimport type { WebhookResponse } from './ports.js';\nimport { ALL_EMAIL_TEMPLATE_IDS, DELIVER_WEBHOOK_ACTION, publicDataSchema } from './catalog.js';\nimport { webhookHeaders } from './webhook-signing.js';\nimport { address, type ActionDeps, type EmailRecipient } from './community-mail.js';\nimport { COMMUNITY_DERIVATIONS, type EmailDerivation } from './community-derivations.js';\n\ntype SendEmailDerivations = { [K in EmailTemplateId]?: EmailDerivation<K> };\n\nexport const SEND_EMAIL_DERIVATIONS: SendEmailDerivations = {\n accessGranted: {\n triggers: ['entitlement.created'],\n derive: async (event, deps) => {\n const result = entitlementEvents.entitlementCreated.safeParse(event);\n if (!result.success) {\n return undefined;\n }\n const entitlement = result.data.data;\n if (!entitlement.contentId) {\n return [];\n }\n const content = await deps.lookups.contentInfo(entitlement.orgId, entitlement.contentId);\n if (!content) {\n return undefined;\n }\n return address(deps, event, entitlement.orgId, [entitlement.orgUserId], {\n contentTitle: content.title,\n contentId: content.id,\n contentType: content.type,\n });\n },\n },\n accessRevoked: {\n triggers: ['entitlement.deleted'],\n derive: async (event, deps) => {\n const result = entitlementEvents.entitlementDeleted.safeParse(event);\n if (!result.success) {\n return undefined;\n }\n const entitlement = result.data.data;\n if (!entitlement.contentId) {\n return [];\n }\n const content = await deps.lookups.contentInfo(entitlement.orgId, entitlement.contentId);\n if (!content) {\n return undefined;\n }\n return address(deps, event, entitlement.orgId, [entitlement.orgUserId], {\n contentTitle: content.title,\n });\n },\n },\n ...COMMUNITY_DERIVATIONS,\n};\n\nexport type { ActionDeps, EmailRecipient };\n\n/** Who an action reached, and who a delivery to failed for (a partial failure does not throw). */\nexport interface ActionOutcome {\n recipients: string[];\n failed: string[];\n}\n\nfunction reachedNoOne(): ActionOutcome {\n return { recipients: [], failed: [] };\n}\n\nfunction assertSomeDelivered(action: string, outcome: ActionOutcome): ActionOutcome {\n if (outcome.recipients.length === 0 && outcome.failed.length > 0) {\n throw new ActionFailedError(\n `${action}: delivery failed for all ${outcome.failed.length} recipients`,\n );\n }\n return outcome;\n}\n\nfunction isEmailTemplateId(value: unknown): value is EmailTemplateId {\n return typeof value === 'string' && value in ALL_EMAIL_TEMPLATE_IDS;\n}\n\n/** Throws on any failure but a partial fan-out — the engine owns retry policy and failure bookkeeping. */\nexport async function executeAction(\n action: AutomationAction,\n event: DomainEvent,\n deps: ActionDeps,\n): Promise<ActionOutcome> {\n switch (action.type) {\n case 'sendEmail': {\n const template = action.input['template'];\n if (!isEmailTemplateId(template)) {\n throw new ActionFailedError(`sendEmail: unknown template \"${String(template)}\"`);\n }\n const derivation = SEND_EMAIL_DERIVATIONS[template];\n if (!derivation || !derivation.triggers.includes(event.type)) {\n throw new ActionFailedError(\n `sendEmail: template \"${template}\" cannot be derived from event \"${event.type}\"`,\n );\n }\n const derived = await derivation.derive(event, { ...deps, actionInput: action.input });\n if (!derived) {\n throw new ActionFailedError(\n `sendEmail: event \"${event.type}\" is missing the data required to derive template \"${template}\"`,\n );\n }\n const outcome = reachedNoOne();\n for (const recipient of derived) {\n const list = (await send(template, recipient, event, deps))\n ? outcome.recipients\n : outcome.failed;\n list.push(recipient.orgUserId);\n }\n return assertSomeDelivered('sendEmail', outcome);\n }\n case 'grantAccess': {\n await grantAccess(action, event, deps);\n return reachedNoOne();\n }\n case DELIVER_WEBHOOK_ACTION: {\n await deliverWebhook(action, event, deps);\n return reachedNoOne();\n }\n default: {\n throw new ActionFailedError(`unknown automation action type \"${action.type}\"`);\n }\n }\n}\n\nasync function send<K extends EmailTemplateId>(\n template: K,\n recipient: EmailRecipient<K>,\n event: DomainEvent,\n deps: ActionDeps,\n): Promise<boolean> {\n try {\n await deps.mailer.send(recipient.to, template, recipient.params);\n return true;\n } catch (err) {\n (deps.logger ?? noopLogger).warn('automation email not delivered', {\n orgId: event.orgId,\n automationId: deps.automationId,\n trigger: event.type,\n template,\n orgUserId: recipient.orgUserId,\n err,\n });\n return false;\n }\n}\n\nfunction grantRecipient(event: DomainEvent): string | undefined {\n if (typeof event.data !== 'object' || event.data === null) {\n return undefined;\n }\n const data = event.data as Record<string, unknown>;\n if (event.type === 'organization.student.created' && typeof data.id === 'string') {\n return data.id;\n }\n if (typeof data.orgUserId === 'string') {\n return data.orgUserId;\n }\n return undefined;\n}\n\nasync function grantAccess(\n action: AutomationAction,\n event: DomainEvent,\n deps: ActionDeps,\n): Promise<void> {\n const contentId = typeof action.input.contentId === 'string' ? action.input.contentId : null;\n const bundleId = typeof action.input.bundleId === 'string' ? action.input.bundleId : null;\n if ((contentId == null) === (bundleId == null)) {\n throw new ActionFailedError('grantAccess: provide exactly one of contentId or bundleId');\n }\n const orgUserId = grantRecipient(event);\n if (!orgUserId) {\n throw new ActionFailedError(`grantAccess: event \"${event.type}\" has no recipient`);\n }\n const days = action.input.expiresInDays;\n const expiresAt =\n typeof days === 'number' && Number.isFinite(days)\n ? new Date(Date.now() + days * 86_400_000)\n : null;\n await deps.grants.grant(event.orgId, { orgUserId, contentId, bundleId, expiresAt });\n}\n\nasync function deliverWebhook(\n action: AutomationAction,\n event: DomainEvent,\n deps: ActionDeps,\n): Promise<void> {\n const input = deliverWebhookInputSchema.safeParse(action.input);\n if (!input.success) {\n throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: invalid input`);\n }\n const publicData = publicDataSchema(event.type);\n if (!publicData) {\n throw new ActionFailedError(\n `${DELIVER_WEBHOOK_ACTION}: event \"${event.type}\" is not published`,\n );\n }\n const data = publicData.safeParse(event.data);\n if (!data.success) {\n throw new ActionFailedError(\n `${DELIVER_WEBHOOK_ACTION}: event \"${event.type}\" does not match its published shape`,\n );\n }\n const secrets = await deps.credentials.reveal(event.orgId, input.data.secretRef);\n const secret = secrets?.['secret'];\n if (typeof secret !== 'string') {\n throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: signing secret not found`);\n }\n const body = JSON.stringify({\n id: event.id,\n type: event.type,\n version: event.version,\n orgId: event.orgId,\n occurredAt: event.occurredAt,\n data: data.data,\n });\n const timestamp = Math.floor(Date.now() / 1000);\n let response: WebhookResponse;\n try {\n response = await deps.webhooks.send({\n url: input.data.url,\n headers: webhookHeaders(secret, event.id, timestamp, body),\n body,\n });\n } catch (err) {\n (deps.logger ?? noopLogger).warn('webhook endpoint not reached', {\n orgId: event.orgId,\n automationId: deps.automationId,\n eventId: event.id,\n err,\n });\n throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: the endpoint could not be reached`);\n }\n if (response.status < 200 || response.status >= 300) {\n throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: endpoint responded ${response.status}`);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"actions.js","names":[],"sources":["../../src/automations/actions.ts"],"sourcesContent":["// automations context — action runners. `executeAction` maps one\n// AutomationAction against the DomainEvent that triggered its run.\n//\n// SEND_EMAIL_DERIVATIONS is the sendEmail runner's internal derivation table:\n// which (trigger, template) pairings are derivable. A template with no entry\n// (e.g. courseCompleted) makes `executeAction` throw a named error, recorded\n// by the engine as a failed action.\nimport type { EmailTemplateId } from '../types/index.js';\nimport { deliverWebhookInputSchema } from '../types/schemas/index.js';\nimport { entitlementEvents } from '../entitlements/index.js';\nimport type { DomainEvent } from '../shared/ports.js';\nimport { noopLogger } from '../shared/logger.js';\nimport { ActionFailedError, type AutomationAction } from './model.js';\nimport type { WebhookResponse } from './ports.js';\nimport { ALL_EMAIL_TEMPLATE_IDS, DELIVER_WEBHOOK_ACTION, publicDataSchema } from './catalog.js';\nimport { webhookHeaders } from './webhook-signing.js';\nimport { address, type ActionDeps, type EmailRecipient } from './community-mail.js';\nimport { COMMUNITY_DERIVATIONS, type EmailDerivation } from './community-derivations.js';\n\ntype SendEmailDerivations = { [K in EmailTemplateId]?: EmailDerivation<K> };\n\nexport const SEND_EMAIL_DERIVATIONS: SendEmailDerivations = {\n accessGranted: {\n triggers: ['entitlement.created'],\n derive: async (event, deps) => {\n const result = entitlementEvents.entitlementCreated.safeParse(event);\n if (!result.success) {\n return undefined;\n }\n const entitlement = result.data.data;\n if (!entitlement.contentId) {\n return [];\n }\n const content = await deps.lookups.contentInfo(entitlement.orgId, entitlement.contentId);\n if (!content) {\n return undefined;\n }\n return address(deps, event, entitlement.orgId, [entitlement.orgUserId], {\n contentTitle: content.title,\n contentId: content.id,\n contentType: content.type,\n });\n },\n },\n accessRevoked: {\n triggers: ['entitlement.deleted'],\n derive: async (event, deps) => {\n const result = entitlementEvents.entitlementDeleted.safeParse(event);\n if (!result.success) {\n return undefined;\n }\n const entitlement = result.data.data;\n if (!entitlement.contentId) {\n return [];\n }\n const content = await deps.lookups.contentInfo(entitlement.orgId, entitlement.contentId);\n if (!content) {\n return undefined;\n }\n return address(deps, event, entitlement.orgId, [entitlement.orgUserId], {\n contentTitle: content.title,\n });\n },\n },\n ...COMMUNITY_DERIVATIONS,\n};\n\nexport type { ActionDeps, EmailRecipient };\n\n/** Who an action reached, and who a delivery to failed for (a partial failure does not throw). */\nexport interface ActionOutcome {\n recipients: string[];\n failed: string[];\n}\n\nfunction reachedNoOne(): ActionOutcome {\n return { recipients: [], failed: [] };\n}\n\nfunction assertSomeDelivered(action: string, outcome: ActionOutcome): ActionOutcome {\n if (outcome.recipients.length === 0 && outcome.failed.length > 0) {\n throw new ActionFailedError(\n `${action}: delivery failed for all ${outcome.failed.length} recipients`,\n );\n }\n return outcome;\n}\n\nfunction isEmailTemplateId(value: unknown): value is EmailTemplateId {\n return typeof value === 'string' && value in ALL_EMAIL_TEMPLATE_IDS;\n}\n\n/** Throws on any failure but a partial fan-out — the engine owns retry policy and failure bookkeeping. */\nexport async function executeAction(\n action: AutomationAction,\n event: DomainEvent,\n deps: ActionDeps,\n): Promise<ActionOutcome> {\n switch (action.type) {\n case 'sendEmail': {\n const template = action.input['template'];\n if (!isEmailTemplateId(template)) {\n throw new ActionFailedError(`sendEmail: unknown template \"${String(template)}\"`);\n }\n const derivation = SEND_EMAIL_DERIVATIONS[template];\n if (!derivation || !derivation.triggers.includes(event.type)) {\n throw new ActionFailedError(\n `sendEmail: template \"${template}\" cannot be derived from event \"${event.type}\"`,\n );\n }\n const derived = await derivation.derive(event, { ...deps, actionInput: action.input });\n if (!derived) {\n throw new ActionFailedError(\n `sendEmail: event \"${event.type}\" is missing the data required to derive template \"${template}\"`,\n );\n }\n const outcome = reachedNoOne();\n for (const recipient of derived) {\n const list = (await send(template, recipient, event, deps))\n ? outcome.recipients\n : outcome.failed;\n list.push(recipient.orgUserId);\n }\n return assertSomeDelivered('sendEmail', outcome);\n }\n case 'grantAccess': {\n await grantAccess(action, event, deps);\n return reachedNoOne();\n }\n case DELIVER_WEBHOOK_ACTION: {\n await deliverWebhook(action, event, deps);\n return reachedNoOne();\n }\n default: {\n throw new ActionFailedError(`unknown automation action type \"${action.type}\"`);\n }\n }\n}\n\nasync function send<K extends EmailTemplateId>(\n template: K,\n recipient: EmailRecipient<K>,\n event: DomainEvent,\n deps: ActionDeps,\n): Promise<boolean> {\n try {\n await deps.mailer.send(recipient.to, template, recipient.params);\n return true;\n } catch (err) {\n (deps.logger ?? noopLogger).warn('automation email not delivered', {\n orgId: event.orgId,\n automationId: deps.automationId,\n trigger: event.type,\n template,\n orgUserId: recipient.orgUserId,\n err,\n });\n return false;\n }\n}\n\nfunction grantRecipient(event: DomainEvent): string | undefined {\n if (typeof event.data !== 'object' || event.data === null) {\n return undefined;\n }\n const data = event.data as Record<string, unknown>;\n if (event.type === 'organization.student.created' && typeof data.id === 'string') {\n return data.id;\n }\n if (typeof data.orgUserId === 'string') {\n return data.orgUserId;\n }\n return undefined;\n}\n\nasync function grantAccess(\n action: AutomationAction,\n event: DomainEvent,\n deps: ActionDeps,\n): Promise<void> {\n const contentId = typeof action.input.contentId === 'string' ? action.input.contentId : null;\n const bundleId = typeof action.input.bundleId === 'string' ? action.input.bundleId : null;\n if ((contentId == null) === (bundleId == null)) {\n throw new ActionFailedError('grantAccess: provide exactly one of contentId or bundleId');\n }\n const orgUserId = grantRecipient(event);\n if (!orgUserId) {\n throw new ActionFailedError(`grantAccess: event \"${event.type}\" has no recipient`);\n }\n const days = action.input.expiresInDays;\n const expiresAt =\n typeof days === 'number' && Number.isFinite(days)\n ? new Date(Date.now() + days * 86_400_000)\n : null;\n await deps.grants.grant(event.orgId, {\n orgUserId,\n contentId,\n bundleId,\n expiresAt,\n });\n}\n\nasync function deliverWebhook(\n action: AutomationAction,\n event: DomainEvent,\n deps: ActionDeps,\n): Promise<void> {\n const input = deliverWebhookInputSchema.safeParse(action.input);\n if (!input.success) {\n throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: invalid input`);\n }\n const publicData = publicDataSchema(event.type);\n if (!publicData) {\n throw new ActionFailedError(\n `${DELIVER_WEBHOOK_ACTION}: event \"${event.type}\" is not published`,\n );\n }\n const data = publicData.safeParse(event.data);\n if (!data.success) {\n throw new ActionFailedError(\n `${DELIVER_WEBHOOK_ACTION}: event \"${event.type}\" does not match its published shape`,\n );\n }\n const secrets = await deps.credentials.reveal(event.orgId, input.data.secretRef);\n const secret = secrets?.['secret'];\n if (typeof secret !== 'string') {\n throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: signing secret not found`);\n }\n const body = JSON.stringify({\n id: event.id,\n type: event.type,\n version: event.version,\n orgId: event.orgId,\n occurredAt: event.occurredAt,\n data: data.data,\n });\n const timestamp = Math.floor(Date.now() / 1000);\n let response: WebhookResponse;\n try {\n response = await deps.webhooks.send({\n url: input.data.url,\n headers: webhookHeaders(secret, event.id, timestamp, body),\n body,\n });\n } catch (err) {\n (deps.logger ?? noopLogger).warn('webhook endpoint not reached', {\n orgId: event.orgId,\n automationId: deps.automationId,\n eventId: event.id,\n err,\n });\n throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: the endpoint could not be reached`);\n }\n if (response.status < 200 || response.status >= 300) {\n throw new ActionFailedError(`${DELIVER_WEBHOOK_ACTION}: endpoint responded ${response.status}`);\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { assetEvents } from "../assets/events.js";
|
|
2
|
-
import { organizationEvents } from "../organizations/events.js";
|
|
3
1
|
import { entitlementEvents } from "../entitlements/events.js";
|
|
2
|
+
import { assetEvents } from "../assets/events.js";
|
|
4
3
|
import { contentEvents } from "../content/events.js";
|
|
5
4
|
import { discussionEvents } from "../discussion/events.js";
|
|
6
5
|
import { identityEvents } from "../identity/events.js";
|
|
7
6
|
import { integrationEvents } from "../integrations/events.js";
|
|
7
|
+
import { organizationEvents } from "../organizations/events.js";
|
|
8
8
|
import { progressEvents } from "../progress/events.js";
|
|
9
9
|
//#region src/automations/catalog.ts
|
|
10
10
|
/** Every EmailTemplateId — a missing key here is a compile error, kept exhaustive by construction. */
|
|
@@ -33,7 +33,7 @@ const ALL_EMAIL_TEMPLATE_IDS = {
|
|
|
33
33
|
messageRequest: true,
|
|
34
34
|
newMessage: true,
|
|
35
35
|
addedToGroupChat: true,
|
|
36
|
-
|
|
36
|
+
roleAssigned: true,
|
|
37
37
|
digest: true,
|
|
38
38
|
postAwaitingReview: true,
|
|
39
39
|
joinRequest: true,
|
|
@@ -48,7 +48,12 @@ const ALL_EMAIL_TEMPLATE_IDS = {
|
|
|
48
48
|
activityCommentRemoved: true,
|
|
49
49
|
activityCommentReacted: true,
|
|
50
50
|
activityCommentFlagged: true,
|
|
51
|
-
activityMention: true
|
|
51
|
+
activityMention: true,
|
|
52
|
+
eventGranted: true,
|
|
53
|
+
eventStartingSoon: true,
|
|
54
|
+
eventChanged: true,
|
|
55
|
+
eventCanceled: true,
|
|
56
|
+
eventRevoked: true
|
|
52
57
|
};
|
|
53
58
|
const DELIVER_WEBHOOK_ACTION = "deliverWebhook";
|
|
54
59
|
function catalogActions() {
|
|
@@ -124,6 +129,42 @@ const TRIGGERS = [
|
|
|
124
129
|
category: "Organization",
|
|
125
130
|
description: "an organization member was updated"
|
|
126
131
|
},
|
|
132
|
+
{
|
|
133
|
+
event: organizationEvents.roleAssigned,
|
|
134
|
+
label: "Role assigned",
|
|
135
|
+
category: "Organization",
|
|
136
|
+
description: "a person was given a role on the organization or on a content item"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
event: organizationEvents.roleUnassigned,
|
|
140
|
+
label: "Role removed",
|
|
141
|
+
category: "Organization",
|
|
142
|
+
description: "a role was removed from a person on a content item"
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
event: organizationEvents.roleCreated,
|
|
146
|
+
label: "Role created",
|
|
147
|
+
category: "Organization",
|
|
148
|
+
description: "the organization added a role"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
event: organizationEvents.roleUpdated,
|
|
152
|
+
label: "Role updated",
|
|
153
|
+
category: "Organization",
|
|
154
|
+
description: "a role was renamed, changed or reset"
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
event: organizationEvents.roleDeleted,
|
|
158
|
+
label: "Role deleted",
|
|
159
|
+
category: "Organization",
|
|
160
|
+
description: "the organization deleted a role it added"
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
event: organizationEvents.ownershipTransferred,
|
|
164
|
+
label: "Ownership transferred",
|
|
165
|
+
category: "Organization",
|
|
166
|
+
description: "ownership of the organization moved to another person"
|
|
167
|
+
},
|
|
127
168
|
{
|
|
128
169
|
event: organizationEvents.inviteCreated,
|
|
129
170
|
label: "Invite sent",
|
|
@@ -268,6 +309,30 @@ const TRIGGERS = [
|
|
|
268
309
|
category: "Downloads",
|
|
269
310
|
description: "download assets were reordered"
|
|
270
311
|
},
|
|
312
|
+
{
|
|
313
|
+
event: contentEvents.eventCreated,
|
|
314
|
+
label: "Event created",
|
|
315
|
+
category: "Events",
|
|
316
|
+
description: "an event was created"
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
event: contentEvents.eventUpdated,
|
|
320
|
+
label: "Event updated",
|
|
321
|
+
category: "Events",
|
|
322
|
+
description: "an event was updated, published, unpublished or canceled"
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
event: contentEvents.eventDeleted,
|
|
326
|
+
label: "Event deleted",
|
|
327
|
+
category: "Events",
|
|
328
|
+
description: "an event was deleted"
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
event: contentEvents.eventStartingSoon,
|
|
332
|
+
label: "Event starting soon",
|
|
333
|
+
category: "Events",
|
|
334
|
+
description: "an event starts in 1 day or in 1 hour"
|
|
335
|
+
},
|
|
271
336
|
{
|
|
272
337
|
event: contentEvents.communityCreated,
|
|
273
338
|
label: "Community created",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog.js","names":[],"sources":["../../src/automations/catalog.ts"],"sourcesContent":["// automations context — the code-owned catalogs `availableActions()` and\n// `availableTriggers()` serve: built-in action definitions and the domain\n// event types automations may react to.\nimport type { z } from 'zod';\nimport type { EmailTemplateId } from '../types/index.js';\nimport { assetEvents } from '../assets/index.js';\nimport { contentEvents } from '../content/index.js';\nimport { discussionEvents } from '../discussion/index.js';\nimport { entitlementEvents } from '../entitlements/index.js';\nimport { identityEvents } from '../identity/index.js';\nimport { integrationEvents } from '../integrations/index.js';\nimport { organizationEvents } from '../organizations/index.js';\nimport { progressEvents } from '../progress/index.js';\nimport type { AvailableActions, AvailableTriggers } from './types.js';\n\n/** Every EmailTemplateId — a missing key here is a compile error, kept exhaustive by construction. */\nexport const ALL_EMAIL_TEMPLATE_IDS: Record<EmailTemplateId, true> = {\n magicLink: true,\n studentInvite: true,\n memberInvite: true,\n passwordReset: true,\n passwordChanged: true,\n emailVerification: true,\n accessGranted: true,\n accessRevoked: true,\n courseCompleted: true,\n welcomeToCommunity: true,\n membershipEndingSoon: true,\n membershipEnded: true,\n newPostInSpace: true,\n yourPostWasPublished: true,\n yourPostWasRemoved: true,\n newCommentOnPost: true,\n replyToYourComment: true,\n yourCommentWasRemoved: true,\n requestApproved: true,\n requestDeclined: true,\n invitedToSpace: true,\n messageRequest: true,\n newMessage: true,\n addedToGroupChat: true,\n youAreNowAModerator: true,\n digest: true,\n postAwaitingReview: true,\n joinRequest: true,\n somethingWasFlagged: true,\n messageWasFlagged: true,\n postReacted: true,\n commentReacted: true,\n mention: true,\n messageReacted: true,\n courseActivityAdded: true,\n activityCommentCreated: true,\n activityCommentRemoved: true,\n activityCommentReacted: true,\n activityCommentFlagged: true,\n activityMention: true,\n};\n\nexport const DELIVER_WEBHOOK_ACTION = 'deliverWebhook';\n\nexport function catalogActions(): AvailableActions {\n return [\n {\n type: 'sendEmail',\n description: 'Send a transactional email using a built-in template.',\n inputSchema: {\n type: 'object',\n required: ['template'],\n properties: {\n template: { enum: Object.keys(ALL_EMAIL_TEMPLATE_IDS) },\n },\n },\n source: 'system',\n },\n {\n type: 'grantAccess',\n description: 'Grant a content item or bundle to the person on the event.',\n inputSchema: {\n type: 'object',\n properties: {\n contentId: { type: 'string' },\n bundleId: { type: 'string' },\n expiresInDays: { type: 'number' },\n },\n },\n source: 'system',\n },\n ];\n}\n\ninterface CatalogTrigger {\n readonly event: { readonly type: string; readonly publicData: z.ZodType };\n readonly label: string;\n readonly category: string;\n readonly description: string;\n}\n\nconst TRIGGERS: CatalogTrigger[] = [\n {\n event: identityEvents.userCreated,\n label: 'User created',\n category: 'Users',\n description: 'a user was created',\n },\n {\n event: identityEvents.userUpdated,\n label: 'User updated',\n category: 'Users',\n description: 'a user was updated',\n },\n {\n event: organizationEvents.organizationCreated,\n label: 'Organization created',\n category: 'Organization',\n description: 'an organization was created',\n },\n {\n event: organizationEvents.organizationUpdated,\n label: 'Organization updated',\n category: 'Organization',\n description: 'an organization was updated',\n },\n {\n event: organizationEvents.organizationDeleted,\n label: 'Organization deleted',\n category: 'Organization',\n description: 'an organization was deleted',\n },\n {\n event: organizationEvents.orgUserLinked,\n label: 'Member joined',\n category: 'Organization',\n description: 'a user was linked to an organization',\n },\n {\n event: organizationEvents.orgUserDeleted,\n label: 'Member removed',\n category: 'Organization',\n description: 'a user was removed from an organization',\n },\n {\n event: organizationEvents.orgUserUpdated,\n label: 'Member updated',\n category: 'Organization',\n description: 'an organization member was updated',\n },\n {\n event: organizationEvents.inviteCreated,\n label: 'Invite sent',\n category: 'Organization',\n description: 'an invite was created or re-issued',\n },\n {\n event: organizationEvents.inviteAccepted,\n label: 'Invite accepted',\n category: 'Organization',\n description: 'an invite was accepted',\n },\n {\n event: organizationEvents.studentCreated,\n label: 'Student created',\n category: 'Students',\n description: 'a student was created',\n },\n {\n event: organizationEvents.studentDeleted,\n label: 'Student deleted',\n category: 'Students',\n description: 'a student was deleted',\n },\n {\n event: organizationEvents.studentLinked,\n label: 'Student linked to account',\n category: 'Students',\n description: 'a pending student was linked to an auth account',\n },\n {\n event: contentEvents.courseCreated,\n label: 'Course created',\n category: 'Courses',\n description: 'a course was created',\n },\n {\n event: contentEvents.courseUpdated,\n label: 'Course updated',\n category: 'Courses',\n description: 'a course was updated',\n },\n {\n event: contentEvents.courseDeleted,\n label: 'Course deleted',\n category: 'Courses',\n description: 'a course was deleted',\n },\n {\n event: contentEvents.moduleCreated,\n label: 'Module created',\n category: 'Courses',\n description: 'a course module was created',\n },\n {\n event: contentEvents.moduleUpdated,\n label: 'Module updated',\n category: 'Courses',\n description: 'a course module was updated',\n },\n {\n event: contentEvents.moduleDeleted,\n label: 'Module deleted',\n category: 'Courses',\n description: 'a course module was deleted',\n },\n {\n event: contentEvents.modulesReordered,\n label: 'Modules reordered',\n category: 'Courses',\n description: 'course modules were reordered',\n },\n {\n event: contentEvents.activityCreated,\n label: 'Activity created',\n category: 'Courses',\n description: 'a course activity was created',\n },\n {\n event: contentEvents.activityUpdated,\n label: 'Activity updated',\n category: 'Courses',\n description: 'a course activity was updated',\n },\n {\n event: contentEvents.activityDeleted,\n label: 'Activity deleted',\n category: 'Courses',\n description: 'a course activity was deleted',\n },\n {\n event: contentEvents.activityPublished,\n label: 'Activity published',\n category: 'Courses',\n description: 'an activity of a published course was published',\n },\n {\n event: contentEvents.activitiesReordered,\n label: 'Activities reordered',\n category: 'Courses',\n description: 'course activities were reordered',\n },\n {\n event: contentEvents.downloadCreated,\n label: 'Download created',\n category: 'Downloads',\n description: 'a download was created',\n },\n {\n event: contentEvents.downloadUpdated,\n label: 'Download updated',\n category: 'Downloads',\n description: 'a download was updated',\n },\n {\n event: contentEvents.downloadDeleted,\n label: 'Download deleted',\n category: 'Downloads',\n description: 'a download was deleted',\n },\n {\n event: contentEvents.downloadAssetAdded,\n label: 'Download file added',\n category: 'Downloads',\n description: 'a download asset was added',\n },\n {\n event: contentEvents.downloadAssetRemoved,\n label: 'Download file removed',\n category: 'Downloads',\n description: 'a download asset was removed',\n },\n {\n event: contentEvents.downloadAssetRenamed,\n label: 'Download file renamed',\n category: 'Downloads',\n description: 'a download asset was renamed',\n },\n {\n event: contentEvents.downloadAssetsReordered,\n label: 'Download files reordered',\n category: 'Downloads',\n description: 'download assets were reordered',\n },\n {\n event: contentEvents.communityCreated,\n label: 'Community created',\n category: 'Communities',\n description: 'a community was created',\n },\n {\n event: contentEvents.communityUpdated,\n label: 'Community updated',\n category: 'Communities',\n description: 'a community was updated',\n },\n {\n event: contentEvents.communityDeleted,\n label: 'Community deleted',\n category: 'Communities',\n description: 'a community was deleted',\n },\n {\n event: contentEvents.spaceCreated,\n label: 'Space created',\n category: 'Communities',\n description: 'a space was created',\n },\n {\n event: contentEvents.spaceUpdated,\n label: 'Space updated',\n category: 'Communities',\n description: 'a space was updated',\n },\n {\n event: contentEvents.spaceDeleted,\n label: 'Space deleted',\n category: 'Communities',\n description: 'a space was deleted',\n },\n {\n event: contentEvents.spacesReordered,\n label: 'Spaces reordered',\n category: 'Communities',\n description: 'spaces were reordered',\n },\n {\n event: entitlementEvents.entitlementCreated,\n label: 'Access granted',\n category: 'Access',\n description: 'a student was granted access to content',\n },\n {\n event: entitlementEvents.entitlementUpdated,\n label: 'Access updated',\n category: 'Access',\n description: \"an entitlement's status or expiry changed\",\n },\n {\n event: entitlementEvents.entitlementDeleted,\n label: 'Access revoked',\n category: 'Access',\n description: \"a student's access to content was revoked\",\n },\n {\n event: entitlementEvents.entitlementExpired,\n label: 'Access expired',\n category: 'Access',\n description: 'an entitlement passed its expiry',\n },\n {\n event: entitlementEvents.entitlementEndingSoon,\n label: 'Access ending soon',\n category: 'Access',\n description: 'an entitlement expires within a week',\n },\n {\n event: entitlementEvents.spaceMemberCreated,\n label: 'Space member created',\n category: 'Access',\n description: 'a person was added to a space',\n },\n {\n event: entitlementEvents.spaceMemberUpdated,\n label: 'Space member updated',\n category: 'Access',\n description: 'a space membership changed',\n },\n {\n event: entitlementEvents.spaceMemberDeleted,\n label: 'Space member removed',\n category: 'Access',\n description: 'a space membership was removed',\n },\n {\n event: progressEvents.progressStarted,\n label: 'Progress started',\n category: 'Progress',\n description: 'a student started a progress record',\n },\n {\n event: progressEvents.progressCompleted,\n label: 'Progress completed',\n category: 'Progress',\n description: 'a student completed a progress record',\n },\n {\n event: integrationEvents.connectionCreated,\n label: 'Integration connected',\n category: 'Integrations',\n description: 'an integration connection was established',\n },\n {\n event: integrationEvents.connectionUpdated,\n label: 'Integration updated',\n category: 'Integrations',\n description: 'an integration connection changed',\n },\n {\n event: integrationEvents.connectionRemoved,\n label: 'Integration disconnected',\n category: 'Integrations',\n description: 'an integration connection was removed',\n },\n {\n event: discussionEvents.commentCreated,\n label: 'Comment created',\n category: 'Discussion',\n description: 'a comment was created',\n },\n {\n event: discussionEvents.commentPublished,\n label: 'Comment published',\n category: 'Discussion',\n description: 'a comment was published',\n },\n {\n event: discussionEvents.commentUpdated,\n label: 'Comment updated',\n category: 'Discussion',\n description: 'a comment was edited',\n },\n {\n event: discussionEvents.commentReactionAdded,\n label: 'Comment reaction added',\n category: 'Discussion',\n description: 'a person reacted to a comment for the first time',\n },\n {\n event: discussionEvents.commentRemoved,\n label: 'Comment removed',\n category: 'Discussion',\n description: 'a comment was removed',\n },\n {\n event: discussionEvents.commentReported,\n label: 'Comment reported',\n category: 'Discussion',\n description: 'a comment was reported',\n },\n {\n event: discussionEvents.postCreated,\n label: 'Post created',\n category: 'Discussion',\n description: 'a post was created',\n },\n {\n event: discussionEvents.postUpdated,\n label: 'Post updated',\n category: 'Discussion',\n description: 'a post was updated',\n },\n {\n event: discussionEvents.postPublished,\n label: 'Post published',\n category: 'Discussion',\n description: 'a post was published',\n },\n {\n event: discussionEvents.postReactionAdded,\n label: 'Post reaction added',\n category: 'Discussion',\n description: 'a person reacted to a post for the first time',\n },\n {\n event: discussionEvents.postRemoved,\n label: 'Post removed',\n category: 'Discussion',\n description: 'a post was removed',\n },\n {\n event: discussionEvents.postReported,\n label: 'Post reported',\n category: 'Discussion',\n description: 'a post was reported',\n },\n {\n event: discussionEvents.chatCreated,\n label: 'Chat created',\n category: 'Discussion',\n description: 'a chat was created',\n },\n {\n event: discussionEvents.chatUpdated,\n label: 'Chat updated',\n category: 'Discussion',\n description: 'a chat was updated',\n },\n {\n event: discussionEvents.chatParticipantAdded,\n label: 'Chat participant added',\n category: 'Discussion',\n description: 'a person was added to a chat',\n },\n {\n event: discussionEvents.messageSent,\n label: 'Message sent',\n category: 'Discussion',\n description: 'a message was sent',\n },\n {\n event: discussionEvents.messageUpdated,\n label: 'Message updated',\n category: 'Discussion',\n description: 'a message was edited',\n },\n {\n event: discussionEvents.messageReactionAdded,\n label: 'Message reaction added',\n category: 'Discussion',\n description: 'a person reacted to a message for the first time',\n },\n {\n event: discussionEvents.messageDeleted,\n label: 'Message deleted',\n category: 'Discussion',\n description: 'a message was deleted',\n },\n {\n event: discussionEvents.messageReported,\n label: 'Message reported',\n category: 'Discussion',\n description: 'a message was reported',\n },\n {\n event: discussionEvents.digestDue,\n label: 'Digest due',\n category: 'Discussion',\n description: \"a community's digest came due on its schedule\",\n },\n {\n event: assetEvents.assetCreated,\n label: 'Upload requested',\n category: 'Assets',\n description: 'an asset upload was requested',\n },\n {\n event: assetEvents.assetReady,\n label: 'Upload ready',\n category: 'Assets',\n description: 'an asset was confirmed ready',\n },\n {\n event: assetEvents.assetDeleted,\n label: 'Asset deleted',\n category: 'Assets',\n description: 'an asset was deleted',\n },\n];\n\nexport function catalogTriggers(): AvailableTriggers['triggers'] {\n return TRIGGERS.map(({ event, ...trigger }) => ({ type: event.type, ...trigger }));\n}\n\nexport function publicDataSchema(type: string): z.ZodType | undefined {\n return TRIGGERS.find((trigger) => trigger.event.type === type)?.event.publicData;\n}\n"],"mappings":";;;;;;;;;;AAgBA,MAAa,yBAAwD;CACnE,WAAW;CACX,eAAe;CACf,cAAc;CACd,eAAe;CACf,iBAAiB;CACjB,mBAAmB;CACnB,eAAe;CACf,eAAe;CACf,iBAAiB;CACjB,oBAAoB;CACpB,sBAAsB;CACtB,iBAAiB;CACjB,gBAAgB;CAChB,sBAAsB;CACtB,oBAAoB;CACpB,kBAAkB;CAClB,oBAAoB;CACpB,uBAAuB;CACvB,iBAAiB;CACjB,iBAAiB;CACjB,gBAAgB;CAChB,gBAAgB;CAChB,YAAY;CACZ,kBAAkB;CAClB,qBAAqB;CACrB,QAAQ;CACR,oBAAoB;CACpB,aAAa;CACb,qBAAqB;CACrB,mBAAmB;CACnB,aAAa;CACb,gBAAgB;CAChB,SAAS;CACT,gBAAgB;CAChB,qBAAqB;CACrB,wBAAwB;CACxB,wBAAwB;CACxB,wBAAwB;CACxB,wBAAwB;CACxB,iBAAiB;AACnB;AAEA,MAAa,yBAAyB;AAEtC,SAAgB,iBAAmC;CACjD,OAAO,CACL;EACE,MAAM;EACN,aAAa;EACb,aAAa;GACX,MAAM;GACN,UAAU,CAAC,UAAU;GACrB,YAAY,EACV,UAAU,EAAE,MAAM,OAAO,KAAK,sBAAsB,EAAE,EACxD;EACF;EACA,QAAQ;CACV,GACA;EACE,MAAM;EACN,aAAa;EACb,aAAa;GACX,MAAM;GACN,YAAY;IACV,WAAW,EAAE,MAAM,SAAS;IAC5B,UAAU,EAAE,MAAM,SAAS;IAC3B,eAAe,EAAE,MAAM,SAAS;GAClC;EACF;EACA,QAAQ;CACV,CACF;AACF;AASA,MAAM,WAA6B;CACjC;EACE,OAAO,eAAe;EACtB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,eAAe;EACtB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,eAAe;EACtB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,eAAe;EACtB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,YAAY;EACnB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,YAAY;EACnB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,YAAY;EACnB,OAAO;EACP,UAAU;EACV,aAAa;CACf;AACF;AAEA,SAAgB,kBAAiD;CAC/D,OAAO,SAAS,KAAK,EAAE,OAAO,GAAG,eAAe;EAAE,MAAM,MAAM;EAAM,GAAG;CAAQ,EAAE;AACnF;AAEA,SAAgB,iBAAiB,MAAqC;CACpE,OAAO,SAAS,MAAM,YAAY,QAAQ,MAAM,SAAS,IAAI,CAAC,EAAE,MAAM;AACxE"}
|
|
1
|
+
{"version":3,"file":"catalog.js","names":[],"sources":["../../src/automations/catalog.ts"],"sourcesContent":["// automations context — the code-owned catalogs `availableActions()` and\n// `availableTriggers()` serve: built-in action definitions and the domain\n// event types automations may react to.\nimport type { z } from 'zod';\nimport type { EmailTemplateId } from '../types/index.js';\nimport { assetEvents } from '../assets/index.js';\nimport { contentEvents } from '../content/index.js';\nimport { discussionEvents } from '../discussion/index.js';\nimport { entitlementEvents } from '../entitlements/index.js';\nimport { identityEvents } from '../identity/index.js';\nimport { integrationEvents } from '../integrations/index.js';\nimport { organizationEvents } from '../organizations/index.js';\nimport { progressEvents } from '../progress/index.js';\nimport type { AvailableActions, AvailableTriggers } from './types.js';\n\n/** Every EmailTemplateId — a missing key here is a compile error, kept exhaustive by construction. */\nexport const ALL_EMAIL_TEMPLATE_IDS: Record<EmailTemplateId, true> = {\n magicLink: true,\n studentInvite: true,\n memberInvite: true,\n passwordReset: true,\n passwordChanged: true,\n emailVerification: true,\n accessGranted: true,\n accessRevoked: true,\n courseCompleted: true,\n welcomeToCommunity: true,\n membershipEndingSoon: true,\n membershipEnded: true,\n newPostInSpace: true,\n yourPostWasPublished: true,\n yourPostWasRemoved: true,\n newCommentOnPost: true,\n replyToYourComment: true,\n yourCommentWasRemoved: true,\n requestApproved: true,\n requestDeclined: true,\n invitedToSpace: true,\n messageRequest: true,\n newMessage: true,\n addedToGroupChat: true,\n roleAssigned: true,\n digest: true,\n postAwaitingReview: true,\n joinRequest: true,\n somethingWasFlagged: true,\n messageWasFlagged: true,\n postReacted: true,\n commentReacted: true,\n mention: true,\n messageReacted: true,\n courseActivityAdded: true,\n activityCommentCreated: true,\n activityCommentRemoved: true,\n activityCommentReacted: true,\n activityCommentFlagged: true,\n activityMention: true,\n eventGranted: true,\n eventStartingSoon: true,\n eventChanged: true,\n eventCanceled: true,\n eventRevoked: true,\n};\n\nexport const DELIVER_WEBHOOK_ACTION = 'deliverWebhook';\n\nexport function catalogActions(): AvailableActions {\n return [\n {\n type: 'sendEmail',\n description: 'Send a transactional email using a built-in template.',\n inputSchema: {\n type: 'object',\n required: ['template'],\n properties: {\n template: { enum: Object.keys(ALL_EMAIL_TEMPLATE_IDS) },\n },\n },\n source: 'system',\n },\n {\n type: 'grantAccess',\n description: 'Grant a content item or bundle to the person on the event.',\n inputSchema: {\n type: 'object',\n properties: {\n contentId: { type: 'string' },\n bundleId: { type: 'string' },\n expiresInDays: { type: 'number' },\n },\n },\n source: 'system',\n },\n ];\n}\n\ninterface CatalogTrigger {\n readonly event: { readonly type: string; readonly publicData: z.ZodType };\n readonly label: string;\n readonly category: string;\n readonly description: string;\n}\n\nconst TRIGGERS: CatalogTrigger[] = [\n {\n event: identityEvents.userCreated,\n label: 'User created',\n category: 'Users',\n description: 'a user was created',\n },\n {\n event: identityEvents.userUpdated,\n label: 'User updated',\n category: 'Users',\n description: 'a user was updated',\n },\n {\n event: organizationEvents.organizationCreated,\n label: 'Organization created',\n category: 'Organization',\n description: 'an organization was created',\n },\n {\n event: organizationEvents.organizationUpdated,\n label: 'Organization updated',\n category: 'Organization',\n description: 'an organization was updated',\n },\n {\n event: organizationEvents.organizationDeleted,\n label: 'Organization deleted',\n category: 'Organization',\n description: 'an organization was deleted',\n },\n {\n event: organizationEvents.orgUserLinked,\n label: 'Member joined',\n category: 'Organization',\n description: 'a user was linked to an organization',\n },\n {\n event: organizationEvents.orgUserDeleted,\n label: 'Member removed',\n category: 'Organization',\n description: 'a user was removed from an organization',\n },\n {\n event: organizationEvents.orgUserUpdated,\n label: 'Member updated',\n category: 'Organization',\n description: 'an organization member was updated',\n },\n {\n event: organizationEvents.roleAssigned,\n label: 'Role assigned',\n category: 'Organization',\n description: 'a person was given a role on the organization or on a content item',\n },\n {\n event: organizationEvents.roleUnassigned,\n label: 'Role removed',\n category: 'Organization',\n description: 'a role was removed from a person on a content item',\n },\n {\n event: organizationEvents.roleCreated,\n label: 'Role created',\n category: 'Organization',\n description: 'the organization added a role',\n },\n {\n event: organizationEvents.roleUpdated,\n label: 'Role updated',\n category: 'Organization',\n description: 'a role was renamed, changed or reset',\n },\n {\n event: organizationEvents.roleDeleted,\n label: 'Role deleted',\n category: 'Organization',\n description: 'the organization deleted a role it added',\n },\n {\n event: organizationEvents.ownershipTransferred,\n label: 'Ownership transferred',\n category: 'Organization',\n description: 'ownership of the organization moved to another person',\n },\n {\n event: organizationEvents.inviteCreated,\n label: 'Invite sent',\n category: 'Organization',\n description: 'an invite was created or re-issued',\n },\n {\n event: organizationEvents.inviteAccepted,\n label: 'Invite accepted',\n category: 'Organization',\n description: 'an invite was accepted',\n },\n {\n event: organizationEvents.studentCreated,\n label: 'Student created',\n category: 'Students',\n description: 'a student was created',\n },\n {\n event: organizationEvents.studentDeleted,\n label: 'Student deleted',\n category: 'Students',\n description: 'a student was deleted',\n },\n {\n event: organizationEvents.studentLinked,\n label: 'Student linked to account',\n category: 'Students',\n description: 'a pending student was linked to an auth account',\n },\n {\n event: contentEvents.courseCreated,\n label: 'Course created',\n category: 'Courses',\n description: 'a course was created',\n },\n {\n event: contentEvents.courseUpdated,\n label: 'Course updated',\n category: 'Courses',\n description: 'a course was updated',\n },\n {\n event: contentEvents.courseDeleted,\n label: 'Course deleted',\n category: 'Courses',\n description: 'a course was deleted',\n },\n {\n event: contentEvents.moduleCreated,\n label: 'Module created',\n category: 'Courses',\n description: 'a course module was created',\n },\n {\n event: contentEvents.moduleUpdated,\n label: 'Module updated',\n category: 'Courses',\n description: 'a course module was updated',\n },\n {\n event: contentEvents.moduleDeleted,\n label: 'Module deleted',\n category: 'Courses',\n description: 'a course module was deleted',\n },\n {\n event: contentEvents.modulesReordered,\n label: 'Modules reordered',\n category: 'Courses',\n description: 'course modules were reordered',\n },\n {\n event: contentEvents.activityCreated,\n label: 'Activity created',\n category: 'Courses',\n description: 'a course activity was created',\n },\n {\n event: contentEvents.activityUpdated,\n label: 'Activity updated',\n category: 'Courses',\n description: 'a course activity was updated',\n },\n {\n event: contentEvents.activityDeleted,\n label: 'Activity deleted',\n category: 'Courses',\n description: 'a course activity was deleted',\n },\n {\n event: contentEvents.activityPublished,\n label: 'Activity published',\n category: 'Courses',\n description: 'an activity of a published course was published',\n },\n {\n event: contentEvents.activitiesReordered,\n label: 'Activities reordered',\n category: 'Courses',\n description: 'course activities were reordered',\n },\n {\n event: contentEvents.downloadCreated,\n label: 'Download created',\n category: 'Downloads',\n description: 'a download was created',\n },\n {\n event: contentEvents.downloadUpdated,\n label: 'Download updated',\n category: 'Downloads',\n description: 'a download was updated',\n },\n {\n event: contentEvents.downloadDeleted,\n label: 'Download deleted',\n category: 'Downloads',\n description: 'a download was deleted',\n },\n {\n event: contentEvents.downloadAssetAdded,\n label: 'Download file added',\n category: 'Downloads',\n description: 'a download asset was added',\n },\n {\n event: contentEvents.downloadAssetRemoved,\n label: 'Download file removed',\n category: 'Downloads',\n description: 'a download asset was removed',\n },\n {\n event: contentEvents.downloadAssetRenamed,\n label: 'Download file renamed',\n category: 'Downloads',\n description: 'a download asset was renamed',\n },\n {\n event: contentEvents.downloadAssetsReordered,\n label: 'Download files reordered',\n category: 'Downloads',\n description: 'download assets were reordered',\n },\n {\n event: contentEvents.eventCreated,\n label: 'Event created',\n category: 'Events',\n description: 'an event was created',\n },\n {\n event: contentEvents.eventUpdated,\n label: 'Event updated',\n category: 'Events',\n description: 'an event was updated, published, unpublished or canceled',\n },\n {\n event: contentEvents.eventDeleted,\n label: 'Event deleted',\n category: 'Events',\n description: 'an event was deleted',\n },\n {\n event: contentEvents.eventStartingSoon,\n label: 'Event starting soon',\n category: 'Events',\n description: 'an event starts in 1 day or in 1 hour',\n },\n {\n event: contentEvents.communityCreated,\n label: 'Community created',\n category: 'Communities',\n description: 'a community was created',\n },\n {\n event: contentEvents.communityUpdated,\n label: 'Community updated',\n category: 'Communities',\n description: 'a community was updated',\n },\n {\n event: contentEvents.communityDeleted,\n label: 'Community deleted',\n category: 'Communities',\n description: 'a community was deleted',\n },\n {\n event: contentEvents.spaceCreated,\n label: 'Space created',\n category: 'Communities',\n description: 'a space was created',\n },\n {\n event: contentEvents.spaceUpdated,\n label: 'Space updated',\n category: 'Communities',\n description: 'a space was updated',\n },\n {\n event: contentEvents.spaceDeleted,\n label: 'Space deleted',\n category: 'Communities',\n description: 'a space was deleted',\n },\n {\n event: contentEvents.spacesReordered,\n label: 'Spaces reordered',\n category: 'Communities',\n description: 'spaces were reordered',\n },\n {\n event: entitlementEvents.entitlementCreated,\n label: 'Access granted',\n category: 'Access',\n description: 'a student was granted access to content',\n },\n {\n event: entitlementEvents.entitlementUpdated,\n label: 'Access updated',\n category: 'Access',\n description: \"an entitlement's status or expiry changed\",\n },\n {\n event: entitlementEvents.entitlementDeleted,\n label: 'Access revoked',\n category: 'Access',\n description: \"a student's access to content was revoked\",\n },\n {\n event: entitlementEvents.entitlementExpired,\n label: 'Access expired',\n category: 'Access',\n description: 'an entitlement passed its expiry',\n },\n {\n event: entitlementEvents.entitlementEndingSoon,\n label: 'Access ending soon',\n category: 'Access',\n description: 'an entitlement expires within a week',\n },\n {\n event: entitlementEvents.spaceMemberCreated,\n label: 'Space member created',\n category: 'Access',\n description: 'a person was added to a space',\n },\n {\n event: entitlementEvents.spaceMemberUpdated,\n label: 'Space member updated',\n category: 'Access',\n description: 'a space membership changed',\n },\n {\n event: entitlementEvents.spaceMemberDeleted,\n label: 'Space member removed',\n category: 'Access',\n description: 'a space membership was removed',\n },\n {\n event: progressEvents.progressStarted,\n label: 'Progress started',\n category: 'Progress',\n description: 'a student started a progress record',\n },\n {\n event: progressEvents.progressCompleted,\n label: 'Progress completed',\n category: 'Progress',\n description: 'a student completed a progress record',\n },\n {\n event: integrationEvents.connectionCreated,\n label: 'Integration connected',\n category: 'Integrations',\n description: 'an integration connection was established',\n },\n {\n event: integrationEvents.connectionUpdated,\n label: 'Integration updated',\n category: 'Integrations',\n description: 'an integration connection changed',\n },\n {\n event: integrationEvents.connectionRemoved,\n label: 'Integration disconnected',\n category: 'Integrations',\n description: 'an integration connection was removed',\n },\n {\n event: discussionEvents.commentCreated,\n label: 'Comment created',\n category: 'Discussion',\n description: 'a comment was created',\n },\n {\n event: discussionEvents.commentPublished,\n label: 'Comment published',\n category: 'Discussion',\n description: 'a comment was published',\n },\n {\n event: discussionEvents.commentUpdated,\n label: 'Comment updated',\n category: 'Discussion',\n description: 'a comment was edited',\n },\n {\n event: discussionEvents.commentReactionAdded,\n label: 'Comment reaction added',\n category: 'Discussion',\n description: 'a person reacted to a comment for the first time',\n },\n {\n event: discussionEvents.commentRemoved,\n label: 'Comment removed',\n category: 'Discussion',\n description: 'a comment was removed',\n },\n {\n event: discussionEvents.commentReported,\n label: 'Comment reported',\n category: 'Discussion',\n description: 'a comment was reported',\n },\n {\n event: discussionEvents.postCreated,\n label: 'Post created',\n category: 'Discussion',\n description: 'a post was created',\n },\n {\n event: discussionEvents.postUpdated,\n label: 'Post updated',\n category: 'Discussion',\n description: 'a post was updated',\n },\n {\n event: discussionEvents.postPublished,\n label: 'Post published',\n category: 'Discussion',\n description: 'a post was published',\n },\n {\n event: discussionEvents.postReactionAdded,\n label: 'Post reaction added',\n category: 'Discussion',\n description: 'a person reacted to a post for the first time',\n },\n {\n event: discussionEvents.postRemoved,\n label: 'Post removed',\n category: 'Discussion',\n description: 'a post was removed',\n },\n {\n event: discussionEvents.postReported,\n label: 'Post reported',\n category: 'Discussion',\n description: 'a post was reported',\n },\n {\n event: discussionEvents.chatCreated,\n label: 'Chat created',\n category: 'Discussion',\n description: 'a chat was created',\n },\n {\n event: discussionEvents.chatUpdated,\n label: 'Chat updated',\n category: 'Discussion',\n description: 'a chat was updated',\n },\n {\n event: discussionEvents.chatParticipantAdded,\n label: 'Chat participant added',\n category: 'Discussion',\n description: 'a person was added to a chat',\n },\n {\n event: discussionEvents.messageSent,\n label: 'Message sent',\n category: 'Discussion',\n description: 'a message was sent',\n },\n {\n event: discussionEvents.messageUpdated,\n label: 'Message updated',\n category: 'Discussion',\n description: 'a message was edited',\n },\n {\n event: discussionEvents.messageReactionAdded,\n label: 'Message reaction added',\n category: 'Discussion',\n description: 'a person reacted to a message for the first time',\n },\n {\n event: discussionEvents.messageDeleted,\n label: 'Message deleted',\n category: 'Discussion',\n description: 'a message was deleted',\n },\n {\n event: discussionEvents.messageReported,\n label: 'Message reported',\n category: 'Discussion',\n description: 'a message was reported',\n },\n {\n event: discussionEvents.digestDue,\n label: 'Digest due',\n category: 'Discussion',\n description: \"a community's digest came due on its schedule\",\n },\n {\n event: assetEvents.assetCreated,\n label: 'Upload requested',\n category: 'Assets',\n description: 'an asset upload was requested',\n },\n {\n event: assetEvents.assetReady,\n label: 'Upload ready',\n category: 'Assets',\n description: 'an asset was confirmed ready',\n },\n {\n event: assetEvents.assetDeleted,\n label: 'Asset deleted',\n category: 'Assets',\n description: 'an asset was deleted',\n },\n];\n\nexport function catalogTriggers(): AvailableTriggers['triggers'] {\n return TRIGGERS.map(({ event, ...trigger }) => ({ type: event.type, ...trigger }));\n}\n\nexport function publicDataSchema(type: string): z.ZodType | undefined {\n return TRIGGERS.find((trigger) => trigger.event.type === type)?.event.publicData;\n}\n"],"mappings":";;;;;;;;;;AAgBA,MAAa,yBAAwD;CACnE,WAAW;CACX,eAAe;CACf,cAAc;CACd,eAAe;CACf,iBAAiB;CACjB,mBAAmB;CACnB,eAAe;CACf,eAAe;CACf,iBAAiB;CACjB,oBAAoB;CACpB,sBAAsB;CACtB,iBAAiB;CACjB,gBAAgB;CAChB,sBAAsB;CACtB,oBAAoB;CACpB,kBAAkB;CAClB,oBAAoB;CACpB,uBAAuB;CACvB,iBAAiB;CACjB,iBAAiB;CACjB,gBAAgB;CAChB,gBAAgB;CAChB,YAAY;CACZ,kBAAkB;CAClB,cAAc;CACd,QAAQ;CACR,oBAAoB;CACpB,aAAa;CACb,qBAAqB;CACrB,mBAAmB;CACnB,aAAa;CACb,gBAAgB;CAChB,SAAS;CACT,gBAAgB;CAChB,qBAAqB;CACrB,wBAAwB;CACxB,wBAAwB;CACxB,wBAAwB;CACxB,wBAAwB;CACxB,iBAAiB;CACjB,cAAc;CACd,mBAAmB;CACnB,cAAc;CACd,eAAe;CACf,cAAc;AAChB;AAEA,MAAa,yBAAyB;AAEtC,SAAgB,iBAAmC;CACjD,OAAO,CACL;EACE,MAAM;EACN,aAAa;EACb,aAAa;GACX,MAAM;GACN,UAAU,CAAC,UAAU;GACrB,YAAY,EACV,UAAU,EAAE,MAAM,OAAO,KAAK,sBAAsB,EAAE,EACxD;EACF;EACA,QAAQ;CACV,GACA;EACE,MAAM;EACN,aAAa;EACb,aAAa;GACX,MAAM;GACN,YAAY;IACV,WAAW,EAAE,MAAM,SAAS;IAC5B,UAAU,EAAE,MAAM,SAAS;IAC3B,eAAe,EAAE,MAAM,SAAS;GAClC;EACF;EACA,QAAQ;CACV,CACF;AACF;AASA,MAAM,WAA6B;CACjC;EACE,OAAO,eAAe;EACtB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,eAAe;EACtB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,mBAAmB;EAC1B,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,cAAc;EACrB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,eAAe;EACtB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,eAAe;EACtB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,kBAAkB;EACzB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,iBAAiB;EACxB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,YAAY;EACnB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,YAAY;EACnB,OAAO;EACP,UAAU;EACV,aAAa;CACf;CACA;EACE,OAAO,YAAY;EACnB,OAAO;EACP,UAAU;EACV,aAAa;CACf;AACF;AAEA,SAAgB,kBAAiD;CAC/D,OAAO,SAAS,KAAK,EAAE,OAAO,GAAG,eAAe;EAAE,MAAM,MAAM;EAAM,GAAG;CAAQ,EAAE;AACnF;AAEA,SAAgB,iBAAiB,MAAqC;CACpE,OAAO,SAAS,MAAM,YAAY,QAAQ,MAAM,SAAS,IAAI,CAAC,EAAE,MAAM;AACxE"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { organizationEvents } from "../organizations/events.js";
|
|
2
1
|
import { entitlementEvents } from "../entitlements/events.js";
|
|
3
2
|
import { discussionEvents } from "../discussion/events.js";
|
|
4
|
-
import {
|
|
3
|
+
import { organizationEvents } from "../organizations/events.js";
|
|
4
|
+
import { MESSAGE_BATCH_MS, actorOf, address, communityModerators, excerpt, previousStatusOf, roleGain } from "./community-mail.js";
|
|
5
5
|
//#region src/automations/community-derivations.ts
|
|
6
6
|
function silence(triggers) {
|
|
7
7
|
return {
|
|
@@ -103,7 +103,7 @@ const COMMUNITY_DERIVATIONS = {
|
|
|
103
103
|
spaceName: space.name,
|
|
104
104
|
postTitle: post.title,
|
|
105
105
|
authorName: await deps.mail.displayName(event.orgId, post.orgUserId),
|
|
106
|
-
excerpt: excerpt(post.body || post.title, 280),
|
|
106
|
+
excerpt: excerpt(deps.mail.postText(post.body) || post.title, 280),
|
|
107
107
|
postId: post.id,
|
|
108
108
|
communityId: post.communityId
|
|
109
109
|
});
|
|
@@ -213,7 +213,7 @@ const COMMUNITY_DERIVATIONS = {
|
|
|
213
213
|
if (post.status !== "pending" || previousStatusOf(event) === "pending") return [];
|
|
214
214
|
const space = await deps.mail.space(event.orgId, post.spaceId);
|
|
215
215
|
if (!space) return;
|
|
216
|
-
const audience = await deps.mail
|
|
216
|
+
const audience = await communityModerators(deps.mail, event.orgId, post.communityId);
|
|
217
217
|
return address(deps, event, event.orgId, audience, {
|
|
218
218
|
spaceName: space.name,
|
|
219
219
|
postTitle: post.title,
|
|
@@ -233,7 +233,7 @@ const COMMUNITY_DERIVATIONS = {
|
|
|
233
233
|
const space = await deps.mail.space(event.orgId, member.spaceId);
|
|
234
234
|
if (!space) return;
|
|
235
235
|
const active = await deps.mail.activeMemberCount(event.orgId, member.spaceId);
|
|
236
|
-
const audience = await deps.mail
|
|
236
|
+
const audience = await communityModerators(deps.mail, event.orgId, member.communityId);
|
|
237
237
|
return address(deps, event, event.orgId, audience, {
|
|
238
238
|
spaceName: space.name,
|
|
239
239
|
memberName: await deps.mail.displayName(event.orgId, member.orgUserId),
|
|
@@ -357,15 +357,15 @@ const COMMUNITY_DERIVATIONS = {
|
|
|
357
357
|
});
|
|
358
358
|
}
|
|
359
359
|
},
|
|
360
|
-
|
|
361
|
-
triggers: ["organization.
|
|
360
|
+
roleAssigned: {
|
|
361
|
+
triggers: ["organization.role.assigned"],
|
|
362
362
|
derive: async (event, deps) => {
|
|
363
|
-
const parsed = organizationEvents.
|
|
363
|
+
const parsed = organizationEvents.roleAssigned.safeParse(event);
|
|
364
364
|
if (!parsed.success) return;
|
|
365
|
-
const
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
return address(deps, event, event.orgId, [
|
|
365
|
+
const assigned = parsed.data.data;
|
|
366
|
+
const gain = await roleGain(deps.mail, event.orgId, assigned);
|
|
367
|
+
if (!gain) return [];
|
|
368
|
+
return address(deps, event, event.orgId, [assigned.orgUserId], gain);
|
|
369
369
|
}
|
|
370
370
|
},
|
|
371
371
|
digest: silence(["discussion.digest.due"]),
|
|
@@ -379,10 +379,10 @@ const COMMUNITY_DERIVATIONS = {
|
|
|
379
379
|
const post = await deps.mail.post(event.orgId, flag.postId);
|
|
380
380
|
const space = post ? await deps.mail.space(event.orgId, post.spaceId) : null;
|
|
381
381
|
if (!post || !space) return;
|
|
382
|
-
const audience = await deps.mail
|
|
382
|
+
const audience = await communityModerators(deps.mail, event.orgId, flag.communityId);
|
|
383
383
|
return address(deps, event, event.orgId, audience, {
|
|
384
384
|
spaceName: space.name,
|
|
385
|
-
excerpt: excerpt(post.body || post.title, 280),
|
|
385
|
+
excerpt: excerpt(deps.mail.postText(post.body) || post.title, 280),
|
|
386
386
|
reason: flag.reason,
|
|
387
387
|
spaceId: post.spaceId,
|
|
388
388
|
communityId: flag.communityId
|
|
@@ -397,7 +397,7 @@ const COMMUNITY_DERIVATIONS = {
|
|
|
397
397
|
const post = await deps.mail.post(event.orgId, comment.postId);
|
|
398
398
|
const space = post ? await deps.mail.space(event.orgId, post.spaceId) : null;
|
|
399
399
|
if (!post || !space) return;
|
|
400
|
-
const audience = await deps.mail
|
|
400
|
+
const audience = await communityModerators(deps.mail, event.orgId, flag.communityId);
|
|
401
401
|
return address(deps, event, event.orgId, audience, {
|
|
402
402
|
spaceName: space.name,
|
|
403
403
|
excerpt: excerpt(comment.body, 280),
|
|
@@ -413,7 +413,7 @@ const COMMUNITY_DERIVATIONS = {
|
|
|
413
413
|
const parsed = discussionEvents.messageReported.safeParse(event);
|
|
414
414
|
if (!parsed.success) return;
|
|
415
415
|
const flag = parsed.data.data;
|
|
416
|
-
const audience = await deps.mail
|
|
416
|
+
const audience = await communityModerators(deps.mail, event.orgId, flag.communityId);
|
|
417
417
|
return address(deps, event, event.orgId, audience, {
|
|
418
418
|
reason: flag.reason,
|
|
419
419
|
communityId: flag.communityId
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"community-derivations.js","names":[],"sources":["../../src/automations/community-derivations.ts"],"sourcesContent":["import { discussionEvents } from '../discussion/index.js';\nimport { entitlementEvents } from '../entitlements/index.js';\nimport { organizationEvents } from '../organizations/index.js';\nimport type { EmailTemplateId } from '../types/index.js';\nimport type { DomainEvent } from '../shared/ports.js';\nimport {\n actorOf,\n address,\n excerpt,\n MESSAGE_BATCH_MS,\n MESSAGE_EXCERPT,\n POST_EXCERPT,\n previousStatusOf,\n type ActionDeps,\n type EmailRecipient,\n} from './community-mail.js';\n\nexport interface EmailDerivation<K extends EmailTemplateId> {\n triggers: readonly string[];\n derive(event: DomainEvent, deps: ActionDeps): Promise<EmailRecipient<K>[] | undefined>;\n}\n\nfunction silence<K extends EmailTemplateId>(triggers: readonly string[]): EmailDerivation<K> {\n return { triggers, derive: async () => [] };\n}\n\nfunction seatsLeft(capacity: number | null, active: number): string {\n if (capacity == null) {\n return 'No cap';\n }\n return String(Math.max(capacity - active, 0));\n}\n\nfunction muted(participant: { muted: boolean; mutedUntil: Date | null }, at: Date): boolean {\n return (\n participant.muted &&\n (participant.mutedUntil === null || participant.mutedUntil.getTime() > at.getTime())\n );\n}\n\nfunction chatStatusOf(event: DomainEvent): string | undefined {\n const value = event.metadata?.chatStatus;\n return typeof value === 'string' ? value : undefined;\n}\n\nasync function communityTitle(deps: ActionDeps, orgId: string, communityId: string) {\n return deps.mail.communityTitle(orgId, communityId);\n}\n\nexport const COMMUNITY_DERIVATIONS: {\n [K in EmailTemplateId]?: EmailDerivation<K>;\n} = {\n welcomeToCommunity: {\n triggers: ['entitlement.created'],\n derive: async (event, deps) => {\n const parsed = entitlementEvents.entitlementCreated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const grant = parsed.data.data;\n const communityIds = await deps.mail.communitiesOpenedBy(event.orgId, {\n contentId: grant.contentId,\n bundleId: grant.bundleId,\n });\n const recipients: EmailRecipient<'welcomeToCommunity'>[] = [];\n for (const communityId of communityIds) {\n const title = await communityTitle(deps, event.orgId, communityId);\n if (!title) {\n return undefined;\n }\n const spaceNames = await deps.mail.openSpaceNames(\n event.orgId,\n grant.orgUserId,\n communityId,\n );\n const addressed = await address<'welcomeToCommunity'>(\n deps,\n event,\n event.orgId,\n [grant.orgUserId],\n { communityTitle: title, communityId, spaceNames },\n );\n if (!addressed) {\n return undefined;\n }\n recipients.push(...addressed);\n }\n return recipients;\n },\n },\n membershipEndingSoon: {\n triggers: ['entitlement.ending_soon'],\n derive: async (event, deps) => {\n const parsed = entitlementEvents.entitlementEndingSoon.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const grant = parsed.data.data;\n const communityId = grant.communityId;\n const title = await communityTitle(deps, event.orgId, communityId);\n if (!title) {\n return undefined;\n }\n return address(deps, event, event.orgId, [grant.orgUserId], {\n communityTitle: title,\n communityId,\n expiresAt: grant.expiresAt ? grant.expiresAt.toISOString() : '',\n });\n },\n },\n membershipEnded: {\n triggers: ['entitlement.expired', 'entitlement.deleted'],\n derive: async (event, deps) => {\n const parsed =\n event.type === 'entitlement.expired'\n ? entitlementEvents.entitlementExpired.safeParse(event)\n : entitlementEvents.entitlementDeleted.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const grant = parsed.data.data;\n const communityIds = await deps.mail.communitiesOpenedBy(event.orgId, {\n contentId: grant.contentId,\n bundleId: grant.bundleId,\n });\n const recipients: EmailRecipient<'membershipEnded'>[] = [];\n for (const communityId of communityIds) {\n const title = await communityTitle(deps, event.orgId, communityId);\n if (!title) {\n return undefined;\n }\n const addressed = await address<'membershipEnded'>(\n deps,\n event,\n event.orgId,\n [grant.orgUserId],\n { communities: [{ communityId, communityTitle: title }] },\n );\n if (!addressed) {\n return undefined;\n }\n recipients.push(...addressed);\n }\n return recipients;\n },\n },\n newPostInSpace: {\n triggers: ['discussion.post.published'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.postPublished.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const post = parsed.data.data;\n const space = await deps.mail.space(event.orgId, post.spaceId);\n if (!space) {\n return undefined;\n }\n const openers = await deps.mail.spaceOpeners(event.orgId, post.spaceId);\n return address(\n deps,\n event,\n event.orgId,\n openers.filter((id) => id !== post.orgUserId),\n {\n spaceName: space.name,\n postTitle: post.title,\n authorName: await deps.mail.displayName(event.orgId, post.orgUserId),\n excerpt: excerpt(post.body || post.title, POST_EXCERPT),\n postId: post.id,\n communityId: post.communityId,\n },\n );\n },\n },\n yourPostWasPublished: {\n triggers: ['discussion.post.published'],\n derive: async (event, deps) => {\n if (previousStatusOf(event) !== 'pending') {\n return [];\n }\n const parsed = discussionEvents.postPublished.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const post = parsed.data.data;\n const space = await deps.mail.space(event.orgId, post.spaceId);\n if (!space) {\n return undefined;\n }\n return address(deps, event, event.orgId, [post.orgUserId], {\n postTitle: post.title,\n spaceName: space.name,\n postId: post.id,\n communityId: post.communityId,\n });\n },\n },\n yourPostWasRemoved: {\n triggers: ['discussion.post.removed'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.postRemoved.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const post = parsed.data.data;\n if (post.removedBy === post.orgUserId) {\n return [];\n }\n const space = await deps.mail.space(event.orgId, post.spaceId);\n if (!space) {\n return undefined;\n }\n return address(deps, event, event.orgId, [post.orgUserId], {\n postTitle: post.title,\n spaceName: space.name,\n spaceId: post.spaceId,\n communityId: post.communityId,\n });\n },\n },\n newCommentOnPost: {\n triggers: ['discussion.comment.created'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.commentCreated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const comment = parsed.data.data;\n if (!comment.postId || comment.status !== 'published' || !comment.communityId) {\n return [];\n }\n const post = await deps.mail.post(event.orgId, comment.postId);\n if (!post) {\n return undefined;\n }\n const followers = await deps.mail.followerIds(event.orgId, comment.postId);\n const ids = [...new Set([...followers, post.orgUserId])].filter(\n (id) => id !== comment.orgUserId,\n );\n const openers = await deps.mail.spaceOpenersAmong(event.orgId, post.spaceId, ids);\n return address(deps, event, event.orgId, openers, {\n postTitle: post.title,\n commenterName: await deps.mail.displayName(event.orgId, comment.orgUserId),\n excerpt: excerpt(comment.body, POST_EXCERPT),\n commentId: comment.id,\n postId: comment.postId,\n communityId: comment.communityId,\n });\n },\n },\n replyToYourComment: {\n triggers: ['discussion.comment.created'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.commentCreated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const comment = parsed.data.data;\n if (!comment.parentId || !comment.postId || comment.status !== 'published') {\n return [];\n }\n const parent = await deps.mail.comment(event.orgId, comment.parentId);\n const post = await deps.mail.post(event.orgId, comment.postId);\n if (!parent || !post || !comment.communityId) {\n return parent && post ? [] : undefined;\n }\n if (parent.orgUserId === comment.orgUserId) {\n return [];\n }\n const openers = await deps.mail.spaceOpenersAmong(event.orgId, post.spaceId, [\n parent.orgUserId,\n ]);\n return address(deps, event, event.orgId, openers, {\n postTitle: post.title,\n commenterName: await deps.mail.displayName(event.orgId, comment.orgUserId),\n excerpt: excerpt(comment.body, POST_EXCERPT),\n commentId: comment.id,\n postId: comment.postId,\n communityId: comment.communityId,\n });\n },\n },\n yourCommentWasRemoved: {\n triggers: ['discussion.comment.removed'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.commentRemoved.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const comment = parsed.data.data;\n if (!comment.postId || !comment.communityId) {\n return [];\n }\n if (comment.removedBy === comment.orgUserId) {\n return [];\n }\n const post = await deps.mail.post(event.orgId, comment.postId);\n if (!post) {\n return undefined;\n }\n return address(deps, event, event.orgId, [comment.orgUserId], {\n postTitle: post.title,\n postId: comment.postId,\n communityId: comment.communityId,\n });\n },\n },\n postAwaitingReview: {\n triggers: ['discussion.post.created', 'discussion.post.updated'],\n derive: async (event, deps) => {\n const parsed =\n event.type === 'discussion.post.created'\n ? discussionEvents.postCreated.safeParse(event)\n : discussionEvents.postUpdated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const post = parsed.data.data;\n if (post.status !== 'pending' || previousStatusOf(event) === 'pending') {\n return [];\n }\n const space = await deps.mail.space(event.orgId, post.spaceId);\n if (!space) {\n return undefined;\n }\n const audience = await deps.mail.moderationAudience(event.orgId, post.communityId);\n return address(deps, event, event.orgId, audience, {\n spaceName: space.name,\n postTitle: post.title,\n authorName: await deps.mail.displayName(event.orgId, post.orgUserId),\n spaceId: post.spaceId,\n communityId: post.communityId,\n });\n },\n },\n joinRequest: {\n triggers: ['entitlement.space_member.created'],\n derive: async (event, deps) => {\n const parsed = entitlementEvents.spaceMemberCreated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const member = parsed.data.data;\n if (member.status !== 'requested') {\n return [];\n }\n const space = await deps.mail.space(event.orgId, member.spaceId);\n if (!space) {\n return undefined;\n }\n const active = await deps.mail.activeMemberCount(event.orgId, member.spaceId);\n const audience = await deps.mail.moderationAudience(event.orgId, member.communityId);\n return address(deps, event, event.orgId, audience, {\n spaceName: space.name,\n memberName: await deps.mail.displayName(event.orgId, member.orgUserId),\n seatsLeft: seatsLeft(space.capacity, active),\n spaceId: member.spaceId,\n communityId: member.communityId,\n });\n },\n },\n requestApproved: {\n triggers: ['entitlement.space_member.updated'],\n derive: async (event, deps) => {\n const parsed = entitlementEvents.spaceMemberUpdated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const member = parsed.data.data;\n if (member.status !== 'active' || previousStatusOf(event) !== 'requested') {\n return [];\n }\n const space = await deps.mail.space(event.orgId, member.spaceId);\n if (!space) {\n return undefined;\n }\n return address(deps, event, event.orgId, [member.orgUserId], {\n spaceName: space.name,\n spaceId: member.spaceId,\n communityId: member.communityId,\n });\n },\n },\n requestDeclined: {\n triggers: ['entitlement.space_member.deleted'],\n derive: async (event, deps) => {\n if (previousStatusOf(event) !== 'requested') {\n return [];\n }\n const parsed = entitlementEvents.spaceMemberDeleted.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const member = parsed.data.data;\n const space = await deps.mail.space(event.orgId, member.spaceId);\n if (!space) {\n return undefined;\n }\n return address(deps, event, event.orgId, [member.orgUserId], {\n spaceName: space.name,\n communityId: member.communityId,\n });\n },\n },\n invitedToSpace: {\n triggers: ['entitlement.space_member.created'],\n derive: async (event, deps) => {\n const parsed = entitlementEvents.spaceMemberCreated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const member = parsed.data.data;\n if (member.status !== 'invited') {\n return [];\n }\n const space = await deps.mail.space(event.orgId, member.spaceId);\n if (!space) {\n return undefined;\n }\n const actor = actorOf(event);\n return address(deps, event, event.orgId, [member.orgUserId], {\n spaceName: space.name,\n inviterName: actor ? await deps.mail.displayName(event.orgId, actor) : 'A member',\n spaceId: member.spaceId,\n communityId: member.communityId,\n });\n },\n },\n messageRequest: {\n triggers: ['discussion.chat.created'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.chatCreated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const chat = parsed.data.data;\n if (chat.kind !== 'direct' || chat.status !== 'pending') {\n return [];\n }\n const recipient = chat.participantIds.find((id) => id !== chat.createdBy);\n if (!recipient) {\n return [];\n }\n const members = await deps.mail.communityMembersAmong(event.orgId, chat.communityId, [\n recipient,\n ]);\n const body = await deps.mail.latestMessageBody(event.orgId, chat.id);\n return address(deps, event, event.orgId, members, {\n senderName: chat.createdBy\n ? await deps.mail.displayName(event.orgId, chat.createdBy)\n : 'A member',\n excerpt: excerpt(body ?? '', MESSAGE_EXCERPT),\n chatId: chat.id,\n communityId: chat.communityId,\n });\n },\n },\n newMessage: {\n triggers: ['discussion.message.sent'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.messageSent.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const message = parsed.data.data;\n const chat = await deps.mail.chat(event.orgId, message.chatId);\n if (!chat) {\n return undefined;\n }\n if (chatStatusOf(event) === 'pending') {\n return [];\n }\n const at = new Date(event.occurredAt);\n const since = new Date(at.getTime() - MESSAGE_BATCH_MS);\n const recent = new Set(\n await deps.mail.recentMessageRecipients(\n event.orgId,\n deps.automationId,\n message.chatId,\n since,\n ),\n );\n const ids: string[] = [];\n for (const participant of chat.participants) {\n if (participant.leftAt || participant.orgUserId === message.orgUserId) {\n continue;\n }\n if (muted(participant, at) || recent.has(participant.orgUserId)) {\n continue;\n }\n if (\n await deps.mail.pairBlocked(\n event.orgId,\n chat.communityId,\n message.orgUserId,\n participant.orgUserId,\n )\n ) {\n continue;\n }\n ids.push(participant.orgUserId);\n }\n const members = await deps.mail.communityMembersAmong(event.orgId, chat.communityId, ids);\n const title =\n chat.kind === 'direct'\n ? await deps.mail.displayName(event.orgId, message.orgUserId)\n : await groupTitle(deps, event.orgId, chat.participants);\n return address(deps, event, event.orgId, members, {\n senderName: await deps.mail.displayName(event.orgId, message.orgUserId),\n chatTitle: title,\n excerpt: excerpt(message.body, MESSAGE_EXCERPT),\n chatId: message.chatId,\n communityId: message.communityId,\n });\n },\n },\n addedToGroupChat: {\n triggers: ['discussion.chat.participant.added'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.chatParticipantAdded.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const added = parsed.data.data;\n const chat = await deps.mail.chat(event.orgId, added.chatId);\n if (!chat) {\n return undefined;\n }\n return address(deps, event, event.orgId, [added.orgUserId], {\n adderName: await deps.mail.displayName(event.orgId, added.addedBy),\n participantNames: await participantNames(deps, event.orgId, chat.participants),\n chatId: added.chatId,\n communityId: added.communityId,\n });\n },\n },\n youAreNowAModerator: {\n triggers: ['organization.user.updated'],\n derive: async (event, deps) => {\n const parsed = organizationEvents.orgUserUpdated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const orgUser = parsed.data.data;\n if (orgUser.role !== 'moderator') {\n return [];\n }\n const communities = await deps.mail.memberCommunityTitles(event.orgId, orgUser.id);\n return address(deps, event, event.orgId, [orgUser.id], {\n communityTitles: communities.map((community) => community.title),\n });\n },\n },\n digest: silence(['discussion.digest.due']),\n somethingWasFlagged: {\n triggers: ['discussion.post.reported', 'discussion.comment.reported'],\n derive: async (event, deps) => {\n if (event.type === 'discussion.post.reported') {\n const parsed = discussionEvents.postReported.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const flag = parsed.data.data;\n const post = await deps.mail.post(event.orgId, flag.postId);\n const space = post ? await deps.mail.space(event.orgId, post.spaceId) : null;\n if (!post || !space) {\n return undefined;\n }\n const audience = await deps.mail.moderationAudience(event.orgId, flag.communityId);\n return address(deps, event, event.orgId, audience, {\n spaceName: space.name,\n excerpt: excerpt(post.body || post.title, POST_EXCERPT),\n reason: flag.reason,\n spaceId: post.spaceId,\n communityId: flag.communityId,\n });\n }\n const parsed = discussionEvents.commentReported.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const flag = parsed.data.data;\n if (!flag.communityId) {\n return [];\n }\n const comment = await deps.mail.comment(event.orgId, flag.commentId);\n if (!comment?.postId) {\n return comment ? [] : undefined;\n }\n const post = await deps.mail.post(event.orgId, comment.postId);\n const space = post ? await deps.mail.space(event.orgId, post.spaceId) : null;\n if (!post || !space) {\n return undefined;\n }\n const audience = await deps.mail.moderationAudience(event.orgId, flag.communityId);\n return address(deps, event, event.orgId, audience, {\n spaceName: space.name,\n excerpt: excerpt(comment.body, POST_EXCERPT),\n reason: flag.reason,\n spaceId: post.spaceId,\n communityId: flag.communityId,\n });\n },\n },\n messageWasFlagged: {\n triggers: ['discussion.message.reported'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.messageReported.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const flag = parsed.data.data;\n const audience = await deps.mail.staffIds(event.orgId);\n return address(deps, event, event.orgId, audience, {\n reason: flag.reason,\n communityId: flag.communityId,\n });\n },\n },\n};\n\nasync function participantNames(\n deps: ActionDeps,\n orgId: string,\n participants: { orgUserId: string; leftAt: Date | null }[],\n): Promise<string[]> {\n const names: string[] = [];\n for (const participant of participants) {\n if (participant.leftAt) {\n continue;\n }\n names.push(await deps.mail.displayName(orgId, participant.orgUserId));\n }\n return names;\n}\n\nasync function groupTitle(\n deps: ActionDeps,\n orgId: string,\n participants: { orgUserId: string; leftAt: Date | null }[],\n): Promise<string> {\n const names = await participantNames(deps, orgId, participants);\n return names.length > 0 ? names.join(', ') : 'Group chat';\n}\n"],"mappings":";;;;;AAsBA,SAAS,QAAmC,UAAiD;CAC3F,OAAO;EAAE;EAAU,QAAQ,YAAY,CAAC;CAAE;AAC5C;AAEA,SAAS,UAAU,UAAyB,QAAwB;CAClE,IAAI,YAAY,MACd,OAAO;CAET,OAAO,OAAO,KAAK,IAAI,WAAW,QAAQ,CAAC,CAAC;AAC9C;AAEA,SAAS,MAAM,aAA0D,IAAmB;CAC1F,OACE,YAAY,UACX,YAAY,eAAe,QAAQ,YAAY,WAAW,QAAQ,IAAI,GAAG,QAAQ;AAEtF;AAEA,SAAS,aAAa,OAAwC;CAC5D,MAAM,QAAQ,MAAM,UAAU;CAC9B,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAA;AAC7C;AAEA,eAAe,eAAe,MAAkB,OAAe,aAAqB;CAClF,OAAO,KAAK,KAAK,eAAe,OAAO,WAAW;AACpD;AAEA,MAAa,wBAET;CACF,oBAAoB;EAClB,UAAU,CAAC,qBAAqB;EAChC,QAAQ,OAAO,OAAO,SAAS;GAC7B,MAAM,SAAS,kBAAkB,mBAAmB,UAAU,KAAK;GACnE,IAAI,CAAC,OAAO,SACV;GAEF,MAAM,QAAQ,OAAO,KAAK;GAC1B,MAAM,eAAe,MAAM,KAAK,KAAK,oBAAoB,MAAM,OAAO;IACpE,WAAW,MAAM;IACjB,UAAU,MAAM;GAClB,CAAC;GACD,MAAM,aAAqD,CAAC;GAC5D,KAAK,MAAM,eAAe,cAAc;IACtC,MAAM,QAAQ,MAAM,eAAe,MAAM,MAAM,OAAO,WAAW;IACjE,IAAI,CAAC,OACH;IAEF,MAAM,aAAa,MAAM,KAAK,KAAK,eACjC,MAAM,OACN,MAAM,WACN,WACF;IACA,MAAM,YAAY,MAAM,QACtB,MACA,OACA,MAAM,OACN,CAAC,MAAM,SAAS,GAChB;KAAE,gBAAgB;KAAO;KAAa;IAAW,CACnD;IACA,IAAI,CAAC,WACH;IAEF,WAAW,KAAK,GAAG,SAAS;GAC9B;GACA,OAAO;EACT;CACF;CACA,sBAAsB;EACpB,UAAU,CAAC,yBAAyB;EACpC,QAAQ,OAAO,OAAO,SAAS;GAC7B,MAAM,SAAS,kBAAkB,sBAAsB,UAAU,KAAK;GACtE,IAAI,CAAC,OAAO,SACV;GAEF,MAAM,QAAQ,OAAO,KAAK;GAC1B,MAAM,cAAc,MAAM;GAC1B,MAAM,QAAQ,MAAM,eAAe,MAAM,MAAM,OAAO,WAAW;GACjE,IAAI,CAAC,OACH;GAEF,OAAO,QAAQ,MAAM,OAAO,MAAM,OAAO,CAAC,MAAM,SAAS,GAAG;IAC1D,gBAAgB;IAChB;IACA,WAAW,MAAM,YAAY,MAAM,UAAU,YAAY,IAAI;GAC/D,CAAC;EACH;CACF;CACA,iBAAiB;EACf,UAAU,CAAC,uBAAuB,qBAAqB;EACvD,QAAQ,OAAO,OAAO,SAAS;GAC7B,MAAM,SACJ,MAAM,SAAS,wBACX,kBAAkB,mBAAmB,UAAU,KAAK,IACpD,kBAAkB,mBAAmB,UAAU,KAAK;GAC1D,IAAI,CAAC,OAAO,SACV;GAEF,MAAM,QAAQ,OAAO,KAAK;GAC1B,MAAM,eAAe,MAAM,KAAK,KAAK,oBAAoB,MAAM,OAAO;IACpE,WAAW,MAAM;IACjB,UAAU,MAAM;GAClB,CAAC;GACD,MAAM,aAAkD,CAAC;GACzD,KAAK,MAAM,eAAe,cAAc;IACtC,MAAM,QAAQ,MAAM,eAAe,MAAM,MAAM,OAAO,WAAW;IACjE,IAAI,CAAC,OACH;IAEF,MAAM,YAAY,MAAM,QACtB,MACA,OACA,MAAM,OACN,CAAC,MAAM,SAAS,GAChB,EAAE,aAAa,CAAC;KAAE;KAAa,gBAAgB;IAAM,CAAC,EAAE,CAC1D;IACA,IAAI,CAAC,WACH;IAEF,WAAW,KAAK,GAAG,SAAS;GAC9B;GACA,OAAO;EACT;CACF;CACA,gBAAgB;EACd,UAAU,CAAC,2BAA2B;EACtC,QAAQ,OAAO,OAAO,SAAS;GAC7B,MAAM,SAAS,iBAAiB,cAAc,UAAU,KAAK;GAC7D,IAAI,CAAC,OAAO,SACV;GAEF,MAAM,OAAO,OAAO,KAAK;GACzB,MAAM,QAAQ,MAAM,KAAK,KAAK,MAAM,MAAM,OAAO,KAAK,OAAO;GAC7D,IAAI,CAAC,OACH;GAEF,MAAM,UAAU,MAAM,KAAK,KAAK,aAAa,MAAM,OAAO,KAAK,OAAO;GACtE,OAAO,QACL,MACA,OACA,MAAM,OACN,QAAQ,QAAQ,OAAO,OAAO,KAAK,SAAS,GAC5C;IACE,WAAW,MAAM;IACjB,WAAW,KAAK;IAChB,YAAY,MAAM,KAAK,KAAK,YAAY,MAAM,OAAO,KAAK,SAAS;IACnE,SAAS,QAAQ,KAAK,QAAQ,KAAK,OAAA,GAAmB;IACtD,QAAQ,KAAK;IACb,aAAa,KAAK;GACpB,CACF;EACF;CACF;CACA,sBAAsB;EACpB,UAAU,CAAC,2BAA2B;EACtC,QAAQ,OAAO,OAAO,SAAS;GAC7B,IAAI,iBAAiB,KAAK,MAAM,WAC9B,OAAO,CAAC;GAEV,MAAM,SAAS,iBAAiB,cAAc,UAAU,KAAK;GAC7D,IAAI,CAAC,OAAO,SACV;GAEF,MAAM,OAAO,OAAO,KAAK;GACzB,MAAM,QAAQ,MAAM,KAAK,KAAK,MAAM,MAAM,OAAO,KAAK,OAAO;GAC7D,IAAI,CAAC,OACH;GAEF,OAAO,QAAQ,MAAM,OAAO,MAAM,OAAO,CAAC,KAAK,SAAS,GAAG;IACzD,WAAW,KAAK;IAChB,WAAW,MAAM;IACjB,QAAQ,KAAK;IACb,aAAa,KAAK;GACpB,CAAC;EACH;CACF;CACA,oBAAoB;EAClB,UAAU,CAAC,yBAAyB;EACpC,QAAQ,OAAO,OAAO,SAAS;GAC7B,MAAM,SAAS,iBAAiB,YAAY,UAAU,KAAK;GAC3D,IAAI,CAAC,OAAO,SACV;GAEF,MAAM,OAAO,OAAO,KAAK;GACzB,IAAI,KAAK,cAAc,KAAK,WAC1B,OAAO,CAAC;GAEV,MAAM,QAAQ,MAAM,KAAK,KAAK,MAAM,MAAM,OAAO,KAAK,OAAO;GAC7D,IAAI,CAAC,OACH;GAEF,OAAO,QAAQ,MAAM,OAAO,MAAM,OAAO,CAAC,KAAK,SAAS,GAAG;IACzD,WAAW,KAAK;IAChB,WAAW,MAAM;IACjB,SAAS,KAAK;IACd,aAAa,KAAK;GACpB,CAAC;EACH;CACF;CACA,kBAAkB;EAChB,UAAU,CAAC,4BAA4B;EACvC,QAAQ,OAAO,OAAO,SAAS;GAC7B,MAAM,SAAS,iBAAiB,eAAe,UAAU,KAAK;GAC9D,IAAI,CAAC,OAAO,SACV;GAEF,MAAM,UAAU,OAAO,KAAK;GAC5B,IAAI,CAAC,QAAQ,UAAU,QAAQ,WAAW,eAAe,CAAC,QAAQ,aAChE,OAAO,CAAC;GAEV,MAAM,OAAO,MAAM,KAAK,KAAK,KAAK,MAAM,OAAO,QAAQ,MAAM;GAC7D,IAAI,CAAC,MACH;GAEF,MAAM,YAAY,MAAM,KAAK,KAAK,YAAY,MAAM,OAAO,QAAQ,MAAM;GACzE,MAAM,MAAM,CAAC,mBAAG,IAAI,IAAI,CAAC,GAAG,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,QACtD,OAAO,OAAO,QAAQ,SACzB;GACA,MAAM,UAAU,MAAM,KAAK,KAAK,kBAAkB,MAAM,OAAO,KAAK,SAAS,GAAG;GAChF,OAAO,QAAQ,MAAM,OAAO,MAAM,OAAO,SAAS;IAChD,WAAW,KAAK;IAChB,eAAe,MAAM,KAAK,KAAK,YAAY,MAAM,OAAO,QAAQ,SAAS;IACzE,SAAS,QAAQ,QAAQ,MAAA,GAAkB;IAC3C,WAAW,QAAQ;IACnB,QAAQ,QAAQ;IAChB,aAAa,QAAQ;GACvB,CAAC;EACH;CACF;CACA,oBAAoB;EAClB,UAAU,CAAC,4BAA4B;EACvC,QAAQ,OAAO,OAAO,SAAS;GAC7B,MAAM,SAAS,iBAAiB,eAAe,UAAU,KAAK;GAC9D,IAAI,CAAC,OAAO,SACV;GAEF,MAAM,UAAU,OAAO,KAAK;GAC5B,IAAI,CAAC,QAAQ,YAAY,CAAC,QAAQ,UAAU,QAAQ,WAAW,aAC7D,OAAO,CAAC;GAEV,MAAM,SAAS,MAAM,KAAK,KAAK,QAAQ,MAAM,OAAO,QAAQ,QAAQ;GACpE,MAAM,OAAO,MAAM,KAAK,KAAK,KAAK,MAAM,OAAO,QAAQ,MAAM;GAC7D,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,QAAQ,aAC/B,OAAO,UAAU,OAAO,CAAC,IAAI,KAAA;GAE/B,IAAI,OAAO,cAAc,QAAQ,WAC/B,OAAO,CAAC;GAEV,MAAM,UAAU,MAAM,KAAK,KAAK,kBAAkB,MAAM,OAAO,KAAK,SAAS,CAC3E,OAAO,SACT,CAAC;GACD,OAAO,QAAQ,MAAM,OAAO,MAAM,OAAO,SAAS;IAChD,WAAW,KAAK;IAChB,eAAe,MAAM,KAAK,KAAK,YAAY,MAAM,OAAO,QAAQ,SAAS;IACzE,SAAS,QAAQ,QAAQ,MAAA,GAAkB;IAC3C,WAAW,QAAQ;IACnB,QAAQ,QAAQ;IAChB,aAAa,QAAQ;GACvB,CAAC;EACH;CACF;CACA,uBAAuB;EACrB,UAAU,CAAC,4BAA4B;EACvC,QAAQ,OAAO,OAAO,SAAS;GAC7B,MAAM,SAAS,iBAAiB,eAAe,UAAU,KAAK;GAC9D,IAAI,CAAC,OAAO,SACV;GAEF,MAAM,UAAU,OAAO,KAAK;GAC5B,IAAI,CAAC,QAAQ,UAAU,CAAC,QAAQ,aAC9B,OAAO,CAAC;GAEV,IAAI,QAAQ,cAAc,QAAQ,WAChC,OAAO,CAAC;GAEV,MAAM,OAAO,MAAM,KAAK,KAAK,KAAK,MAAM,OAAO,QAAQ,MAAM;GAC7D,IAAI,CAAC,MACH;GAEF,OAAO,QAAQ,MAAM,OAAO,MAAM,OAAO,CAAC,QAAQ,SAAS,GAAG;IAC5D,WAAW,KAAK;IAChB,QAAQ,QAAQ;IAChB,aAAa,QAAQ;GACvB,CAAC;EACH;CACF;CACA,oBAAoB;EAClB,UAAU,CAAC,2BAA2B,yBAAyB;EAC/D,QAAQ,OAAO,OAAO,SAAS;GAC7B,MAAM,SACJ,MAAM,SAAS,4BACX,iBAAiB,YAAY,UAAU,KAAK,IAC5C,iBAAiB,YAAY,UAAU,KAAK;GAClD,IAAI,CAAC,OAAO,SACV;GAEF,MAAM,OAAO,OAAO,KAAK;GACzB,IAAI,KAAK,WAAW,aAAa,iBAAiB,KAAK,MAAM,WAC3D,OAAO,CAAC;GAEV,MAAM,QAAQ,MAAM,KAAK,KAAK,MAAM,MAAM,OAAO,KAAK,OAAO;GAC7D,IAAI,CAAC,OACH;GAEF,MAAM,WAAW,MAAM,KAAK,KAAK,mBAAmB,MAAM,OAAO,KAAK,WAAW;GACjF,OAAO,QAAQ,MAAM,OAAO,MAAM,OAAO,UAAU;IACjD,WAAW,MAAM;IACjB,WAAW,KAAK;IAChB,YAAY,MAAM,KAAK,KAAK,YAAY,MAAM,OAAO,KAAK,SAAS;IACnE,SAAS,KAAK;IACd,aAAa,KAAK;GACpB,CAAC;EACH;CACF;CACA,aAAa;EACX,UAAU,CAAC,kCAAkC;EAC7C,QAAQ,OAAO,OAAO,SAAS;GAC7B,MAAM,SAAS,kBAAkB,mBAAmB,UAAU,KAAK;GACnE,IAAI,CAAC,OAAO,SACV;GAEF,MAAM,SAAS,OAAO,KAAK;GAC3B,IAAI,OAAO,WAAW,aACpB,OAAO,CAAC;GAEV,MAAM,QAAQ,MAAM,KAAK,KAAK,MAAM,MAAM,OAAO,OAAO,OAAO;GAC/D,IAAI,CAAC,OACH;GAEF,MAAM,SAAS,MAAM,KAAK,KAAK,kBAAkB,MAAM,OAAO,OAAO,OAAO;GAC5E,MAAM,WAAW,MAAM,KAAK,KAAK,mBAAmB,MAAM,OAAO,OAAO,WAAW;GACnF,OAAO,QAAQ,MAAM,OAAO,MAAM,OAAO,UAAU;IACjD,WAAW,MAAM;IACjB,YAAY,MAAM,KAAK,KAAK,YAAY,MAAM,OAAO,OAAO,SAAS;IACrE,WAAW,UAAU,MAAM,UAAU,MAAM;IAC3C,SAAS,OAAO;IAChB,aAAa,OAAO;GACtB,CAAC;EACH;CACF;CACA,iBAAiB;EACf,UAAU,CAAC,kCAAkC;EAC7C,QAAQ,OAAO,OAAO,SAAS;GAC7B,MAAM,SAAS,kBAAkB,mBAAmB,UAAU,KAAK;GACnE,IAAI,CAAC,OAAO,SACV;GAEF,MAAM,SAAS,OAAO,KAAK;GAC3B,IAAI,OAAO,WAAW,YAAY,iBAAiB,KAAK,MAAM,aAC5D,OAAO,CAAC;GAEV,MAAM,QAAQ,MAAM,KAAK,KAAK,MAAM,MAAM,OAAO,OAAO,OAAO;GAC/D,IAAI,CAAC,OACH;GAEF,OAAO,QAAQ,MAAM,OAAO,MAAM,OAAO,CAAC,OAAO,SAAS,GAAG;IAC3D,WAAW,MAAM;IACjB,SAAS,OAAO;IAChB,aAAa,OAAO;GACtB,CAAC;EACH;CACF;CACA,iBAAiB;EACf,UAAU,CAAC,kCAAkC;EAC7C,QAAQ,OAAO,OAAO,SAAS;GAC7B,IAAI,iBAAiB,KAAK,MAAM,aAC9B,OAAO,CAAC;GAEV,MAAM,SAAS,kBAAkB,mBAAmB,UAAU,KAAK;GACnE,IAAI,CAAC,OAAO,SACV;GAEF,MAAM,SAAS,OAAO,KAAK;GAC3B,MAAM,QAAQ,MAAM,KAAK,KAAK,MAAM,MAAM,OAAO,OAAO,OAAO;GAC/D,IAAI,CAAC,OACH;GAEF,OAAO,QAAQ,MAAM,OAAO,MAAM,OAAO,CAAC,OAAO,SAAS,GAAG;IAC3D,WAAW,MAAM;IACjB,aAAa,OAAO;GACtB,CAAC;EACH;CACF;CACA,gBAAgB;EACd,UAAU,CAAC,kCAAkC;EAC7C,QAAQ,OAAO,OAAO,SAAS;GAC7B,MAAM,SAAS,kBAAkB,mBAAmB,UAAU,KAAK;GACnE,IAAI,CAAC,OAAO,SACV;GAEF,MAAM,SAAS,OAAO,KAAK;GAC3B,IAAI,OAAO,WAAW,WACpB,OAAO,CAAC;GAEV,MAAM,QAAQ,MAAM,KAAK,KAAK,MAAM,MAAM,OAAO,OAAO,OAAO;GAC/D,IAAI,CAAC,OACH;GAEF,MAAM,QAAQ,QAAQ,KAAK;GAC3B,OAAO,QAAQ,MAAM,OAAO,MAAM,OAAO,CAAC,OAAO,SAAS,GAAG;IAC3D,WAAW,MAAM;IACjB,aAAa,QAAQ,MAAM,KAAK,KAAK,YAAY,MAAM,OAAO,KAAK,IAAI;IACvE,SAAS,OAAO;IAChB,aAAa,OAAO;GACtB,CAAC;EACH;CACF;CACA,gBAAgB;EACd,UAAU,CAAC,yBAAyB;EACpC,QAAQ,OAAO,OAAO,SAAS;GAC7B,MAAM,SAAS,iBAAiB,YAAY,UAAU,KAAK;GAC3D,IAAI,CAAC,OAAO,SACV;GAEF,MAAM,OAAO,OAAO,KAAK;GACzB,IAAI,KAAK,SAAS,YAAY,KAAK,WAAW,WAC5C,OAAO,CAAC;GAEV,MAAM,YAAY,KAAK,eAAe,MAAM,OAAO,OAAO,KAAK,SAAS;GACxE,IAAI,CAAC,WACH,OAAO,CAAC;GAEV,MAAM,UAAU,MAAM,KAAK,KAAK,sBAAsB,MAAM,OAAO,KAAK,aAAa,CACnF,SACF,CAAC;GACD,MAAM,OAAO,MAAM,KAAK,KAAK,kBAAkB,MAAM,OAAO,KAAK,EAAE;GACnE,OAAO,QAAQ,MAAM,OAAO,MAAM,OAAO,SAAS;IAChD,YAAY,KAAK,YACb,MAAM,KAAK,KAAK,YAAY,MAAM,OAAO,KAAK,SAAS,IACvD;IACJ,SAAS,QAAQ,QAAQ,IAAA,GAAmB;IAC5C,QAAQ,KAAK;IACb,aAAa,KAAK;GACpB,CAAC;EACH;CACF;CACA,YAAY;EACV,UAAU,CAAC,yBAAyB;EACpC,QAAQ,OAAO,OAAO,SAAS;GAC7B,MAAM,SAAS,iBAAiB,YAAY,UAAU,KAAK;GAC3D,IAAI,CAAC,OAAO,SACV;GAEF,MAAM,UAAU,OAAO,KAAK;GAC5B,MAAM,OAAO,MAAM,KAAK,KAAK,KAAK,MAAM,OAAO,QAAQ,MAAM;GAC7D,IAAI,CAAC,MACH;GAEF,IAAI,aAAa,KAAK,MAAM,WAC1B,OAAO,CAAC;GAEV,MAAM,KAAK,IAAI,KAAK,MAAM,UAAU;GACpC,MAAM,QAAQ,IAAI,KAAK,GAAG,QAAQ,IAAI,gBAAgB;GACtD,MAAM,SAAS,IAAI,IACjB,MAAM,KAAK,KAAK,wBACd,MAAM,OACN,KAAK,cACL,QAAQ,QACR,KACF,CACF;GACA,MAAM,MAAgB,CAAC;GACvB,KAAK,MAAM,eAAe,KAAK,cAAc;IAC3C,IAAI,YAAY,UAAU,YAAY,cAAc,QAAQ,WAC1D;IAEF,IAAI,MAAM,aAAa,EAAE,KAAK,OAAO,IAAI,YAAY,SAAS,GAC5D;IAEF,IACE,MAAM,KAAK,KAAK,YACd,MAAM,OACN,KAAK,aACL,QAAQ,WACR,YAAY,SACd,GAEA;IAEF,IAAI,KAAK,YAAY,SAAS;GAChC;GACA,MAAM,UAAU,MAAM,KAAK,KAAK,sBAAsB,MAAM,OAAO,KAAK,aAAa,GAAG;GACxF,MAAM,QACJ,KAAK,SAAS,WACV,MAAM,KAAK,KAAK,YAAY,MAAM,OAAO,QAAQ,SAAS,IAC1D,MAAM,WAAW,MAAM,MAAM,OAAO,KAAK,YAAY;GAC3D,OAAO,QAAQ,MAAM,OAAO,MAAM,OAAO,SAAS;IAChD,YAAY,MAAM,KAAK,KAAK,YAAY,MAAM,OAAO,QAAQ,SAAS;IACtE,WAAW;IACX,SAAS,QAAQ,QAAQ,MAAA,GAAqB;IAC9C,QAAQ,QAAQ;IAChB,aAAa,QAAQ;GACvB,CAAC;EACH;CACF;CACA,kBAAkB;EAChB,UAAU,CAAC,mCAAmC;EAC9C,QAAQ,OAAO,OAAO,SAAS;GAC7B,MAAM,SAAS,iBAAiB,qBAAqB,UAAU,KAAK;GACpE,IAAI,CAAC,OAAO,SACV;GAEF,MAAM,QAAQ,OAAO,KAAK;GAC1B,MAAM,OAAO,MAAM,KAAK,KAAK,KAAK,MAAM,OAAO,MAAM,MAAM;GAC3D,IAAI,CAAC,MACH;GAEF,OAAO,QAAQ,MAAM,OAAO,MAAM,OAAO,CAAC,MAAM,SAAS,GAAG;IAC1D,WAAW,MAAM,KAAK,KAAK,YAAY,MAAM,OAAO,MAAM,OAAO;IACjE,kBAAkB,MAAM,iBAAiB,MAAM,MAAM,OAAO,KAAK,YAAY;IAC7E,QAAQ,MAAM;IACd,aAAa,MAAM;GACrB,CAAC;EACH;CACF;CACA,qBAAqB;EACnB,UAAU,CAAC,2BAA2B;EACtC,QAAQ,OAAO,OAAO,SAAS;GAC7B,MAAM,SAAS,mBAAmB,eAAe,UAAU,KAAK;GAChE,IAAI,CAAC,OAAO,SACV;GAEF,MAAM,UAAU,OAAO,KAAK;GAC5B,IAAI,QAAQ,SAAS,aACnB,OAAO,CAAC;GAEV,MAAM,cAAc,MAAM,KAAK,KAAK,sBAAsB,MAAM,OAAO,QAAQ,EAAE;GACjF,OAAO,QAAQ,MAAM,OAAO,MAAM,OAAO,CAAC,QAAQ,EAAE,GAAG,EACrD,iBAAiB,YAAY,KAAK,cAAc,UAAU,KAAK,EACjE,CAAC;EACH;CACF;CACA,QAAQ,QAAQ,CAAC,uBAAuB,CAAC;CACzC,qBAAqB;EACnB,UAAU,CAAC,4BAA4B,6BAA6B;EACpE,QAAQ,OAAO,OAAO,SAAS;GAC7B,IAAI,MAAM,SAAS,4BAA4B;IAC7C,MAAM,SAAS,iBAAiB,aAAa,UAAU,KAAK;IAC5D,IAAI,CAAC,OAAO,SACV;IAEF,MAAM,OAAO,OAAO,KAAK;IACzB,MAAM,OAAO,MAAM,KAAK,KAAK,KAAK,MAAM,OAAO,KAAK,MAAM;IAC1D,MAAM,QAAQ,OAAO,MAAM,KAAK,KAAK,MAAM,MAAM,OAAO,KAAK,OAAO,IAAI;IACxE,IAAI,CAAC,QAAQ,CAAC,OACZ;IAEF,MAAM,WAAW,MAAM,KAAK,KAAK,mBAAmB,MAAM,OAAO,KAAK,WAAW;IACjF,OAAO,QAAQ,MAAM,OAAO,MAAM,OAAO,UAAU;KACjD,WAAW,MAAM;KACjB,SAAS,QAAQ,KAAK,QAAQ,KAAK,OAAA,GAAmB;KACtD,QAAQ,KAAK;KACb,SAAS,KAAK;KACd,aAAa,KAAK;IACpB,CAAC;GACH;GACA,MAAM,SAAS,iBAAiB,gBAAgB,UAAU,KAAK;GAC/D,IAAI,CAAC,OAAO,SACV;GAEF,MAAM,OAAO,OAAO,KAAK;GACzB,IAAI,CAAC,KAAK,aACR,OAAO,CAAC;GAEV,MAAM,UAAU,MAAM,KAAK,KAAK,QAAQ,MAAM,OAAO,KAAK,SAAS;GACnE,IAAI,CAAC,SAAS,QACZ,OAAO,UAAU,CAAC,IAAI,KAAA;GAExB,MAAM,OAAO,MAAM,KAAK,KAAK,KAAK,MAAM,OAAO,QAAQ,MAAM;GAC7D,MAAM,QAAQ,OAAO,MAAM,KAAK,KAAK,MAAM,MAAM,OAAO,KAAK,OAAO,IAAI;GACxE,IAAI,CAAC,QAAQ,CAAC,OACZ;GAEF,MAAM,WAAW,MAAM,KAAK,KAAK,mBAAmB,MAAM,OAAO,KAAK,WAAW;GACjF,OAAO,QAAQ,MAAM,OAAO,MAAM,OAAO,UAAU;IACjD,WAAW,MAAM;IACjB,SAAS,QAAQ,QAAQ,MAAA,GAAkB;IAC3C,QAAQ,KAAK;IACb,SAAS,KAAK;IACd,aAAa,KAAK;GACpB,CAAC;EACH;CACF;CACA,mBAAmB;EACjB,UAAU,CAAC,6BAA6B;EACxC,QAAQ,OAAO,OAAO,SAAS;GAC7B,MAAM,SAAS,iBAAiB,gBAAgB,UAAU,KAAK;GAC/D,IAAI,CAAC,OAAO,SACV;GAEF,MAAM,OAAO,OAAO,KAAK;GACzB,MAAM,WAAW,MAAM,KAAK,KAAK,SAAS,MAAM,KAAK;GACrD,OAAO,QAAQ,MAAM,OAAO,MAAM,OAAO,UAAU;IACjD,QAAQ,KAAK;IACb,aAAa,KAAK;GACpB,CAAC;EACH;CACF;AACF;AAEA,eAAe,iBACb,MACA,OACA,cACmB;CACnB,MAAM,QAAkB,CAAC;CACzB,KAAK,MAAM,eAAe,cAAc;EACtC,IAAI,YAAY,QACd;EAEF,MAAM,KAAK,MAAM,KAAK,KAAK,YAAY,OAAO,YAAY,SAAS,CAAC;CACtE;CACA,OAAO;AACT;AAEA,eAAe,WACb,MACA,OACA,cACiB;CACjB,MAAM,QAAQ,MAAM,iBAAiB,MAAM,OAAO,YAAY;CAC9D,OAAO,MAAM,SAAS,IAAI,MAAM,KAAK,IAAI,IAAI;AAC/C"}
|
|
1
|
+
{"version":3,"file":"community-derivations.js","names":[],"sources":["../../src/automations/community-derivations.ts"],"sourcesContent":["import { discussionEvents } from '../discussion/index.js';\nimport { entitlementEvents } from '../entitlements/index.js';\nimport { organizationEvents } from '../organizations/index.js';\nimport type { EmailTemplateId } from '../types/index.js';\nimport type { DomainEvent } from '../shared/ports.js';\nimport {\n actorOf,\n address,\n communityModerators,\n excerpt,\n MESSAGE_BATCH_MS,\n MESSAGE_EXCERPT,\n POST_EXCERPT,\n previousStatusOf,\n roleGain,\n type ActionDeps,\n type EmailRecipient,\n} from './community-mail.js';\n\nexport interface EmailDerivation<K extends EmailTemplateId> {\n triggers: readonly string[];\n derive(event: DomainEvent, deps: ActionDeps): Promise<EmailRecipient<K>[] | undefined>;\n}\n\nfunction silence<K extends EmailTemplateId>(triggers: readonly string[]): EmailDerivation<K> {\n return { triggers, derive: async () => [] };\n}\n\nfunction seatsLeft(capacity: number | null, active: number): string {\n if (capacity == null) {\n return 'No cap';\n }\n return String(Math.max(capacity - active, 0));\n}\n\nfunction muted(participant: { muted: boolean; mutedUntil: Date | null }, at: Date): boolean {\n return (\n participant.muted &&\n (participant.mutedUntil === null || participant.mutedUntil.getTime() > at.getTime())\n );\n}\n\nfunction chatStatusOf(event: DomainEvent): string | undefined {\n const value = event.metadata?.chatStatus;\n return typeof value === 'string' ? value : undefined;\n}\n\nasync function communityTitle(deps: ActionDeps, orgId: string, communityId: string) {\n return deps.mail.communityTitle(orgId, communityId);\n}\n\nexport const COMMUNITY_DERIVATIONS: {\n [K in EmailTemplateId]?: EmailDerivation<K>;\n} = {\n welcomeToCommunity: {\n triggers: ['entitlement.created'],\n derive: async (event, deps) => {\n const parsed = entitlementEvents.entitlementCreated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const grant = parsed.data.data;\n const communityIds = await deps.mail.communitiesOpenedBy(event.orgId, {\n contentId: grant.contentId,\n bundleId: grant.bundleId,\n });\n const recipients: EmailRecipient<'welcomeToCommunity'>[] = [];\n for (const communityId of communityIds) {\n const title = await communityTitle(deps, event.orgId, communityId);\n if (!title) {\n return undefined;\n }\n const spaceNames = await deps.mail.openSpaceNames(\n event.orgId,\n grant.orgUserId,\n communityId,\n );\n const addressed = await address<'welcomeToCommunity'>(\n deps,\n event,\n event.orgId,\n [grant.orgUserId],\n { communityTitle: title, communityId, spaceNames },\n );\n if (!addressed) {\n return undefined;\n }\n recipients.push(...addressed);\n }\n return recipients;\n },\n },\n membershipEndingSoon: {\n triggers: ['entitlement.ending_soon'],\n derive: async (event, deps) => {\n const parsed = entitlementEvents.entitlementEndingSoon.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const grant = parsed.data.data;\n const communityId = grant.communityId;\n const title = await communityTitle(deps, event.orgId, communityId);\n if (!title) {\n return undefined;\n }\n return address(deps, event, event.orgId, [grant.orgUserId], {\n communityTitle: title,\n communityId,\n expiresAt: grant.expiresAt ? grant.expiresAt.toISOString() : '',\n });\n },\n },\n membershipEnded: {\n triggers: ['entitlement.expired', 'entitlement.deleted'],\n derive: async (event, deps) => {\n const parsed =\n event.type === 'entitlement.expired'\n ? entitlementEvents.entitlementExpired.safeParse(event)\n : entitlementEvents.entitlementDeleted.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const grant = parsed.data.data;\n const communityIds = await deps.mail.communitiesOpenedBy(event.orgId, {\n contentId: grant.contentId,\n bundleId: grant.bundleId,\n });\n const recipients: EmailRecipient<'membershipEnded'>[] = [];\n for (const communityId of communityIds) {\n const title = await communityTitle(deps, event.orgId, communityId);\n if (!title) {\n return undefined;\n }\n const addressed = await address<'membershipEnded'>(\n deps,\n event,\n event.orgId,\n [grant.orgUserId],\n { communities: [{ communityId, communityTitle: title }] },\n );\n if (!addressed) {\n return undefined;\n }\n recipients.push(...addressed);\n }\n return recipients;\n },\n },\n newPostInSpace: {\n triggers: ['discussion.post.published'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.postPublished.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const post = parsed.data.data;\n const space = await deps.mail.space(event.orgId, post.spaceId);\n if (!space) {\n return undefined;\n }\n const openers = await deps.mail.spaceOpeners(event.orgId, post.spaceId);\n return address(\n deps,\n event,\n event.orgId,\n openers.filter((id) => id !== post.orgUserId),\n {\n spaceName: space.name,\n postTitle: post.title,\n authorName: await deps.mail.displayName(event.orgId, post.orgUserId),\n excerpt: excerpt(deps.mail.postText(post.body) || post.title, POST_EXCERPT),\n postId: post.id,\n communityId: post.communityId,\n },\n );\n },\n },\n yourPostWasPublished: {\n triggers: ['discussion.post.published'],\n derive: async (event, deps) => {\n if (previousStatusOf(event) !== 'pending') {\n return [];\n }\n const parsed = discussionEvents.postPublished.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const post = parsed.data.data;\n const space = await deps.mail.space(event.orgId, post.spaceId);\n if (!space) {\n return undefined;\n }\n return address(deps, event, event.orgId, [post.orgUserId], {\n postTitle: post.title,\n spaceName: space.name,\n postId: post.id,\n communityId: post.communityId,\n });\n },\n },\n yourPostWasRemoved: {\n triggers: ['discussion.post.removed'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.postRemoved.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const post = parsed.data.data;\n if (post.removedBy === post.orgUserId) {\n return [];\n }\n const space = await deps.mail.space(event.orgId, post.spaceId);\n if (!space) {\n return undefined;\n }\n return address(deps, event, event.orgId, [post.orgUserId], {\n postTitle: post.title,\n spaceName: space.name,\n spaceId: post.spaceId,\n communityId: post.communityId,\n });\n },\n },\n newCommentOnPost: {\n triggers: ['discussion.comment.created'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.commentCreated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const comment = parsed.data.data;\n if (!comment.postId || comment.status !== 'published' || !comment.communityId) {\n return [];\n }\n const post = await deps.mail.post(event.orgId, comment.postId);\n if (!post) {\n return undefined;\n }\n const followers = await deps.mail.followerIds(event.orgId, comment.postId);\n const ids = [...new Set([...followers, post.orgUserId])].filter(\n (id) => id !== comment.orgUserId,\n );\n const openers = await deps.mail.spaceOpenersAmong(event.orgId, post.spaceId, ids);\n return address(deps, event, event.orgId, openers, {\n postTitle: post.title,\n commenterName: await deps.mail.displayName(event.orgId, comment.orgUserId),\n excerpt: excerpt(comment.body, POST_EXCERPT),\n commentId: comment.id,\n postId: comment.postId,\n communityId: comment.communityId,\n });\n },\n },\n replyToYourComment: {\n triggers: ['discussion.comment.created'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.commentCreated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const comment = parsed.data.data;\n if (!comment.parentId || !comment.postId || comment.status !== 'published') {\n return [];\n }\n const parent = await deps.mail.comment(event.orgId, comment.parentId);\n const post = await deps.mail.post(event.orgId, comment.postId);\n if (!parent || !post || !comment.communityId) {\n return parent && post ? [] : undefined;\n }\n if (parent.orgUserId === comment.orgUserId) {\n return [];\n }\n const openers = await deps.mail.spaceOpenersAmong(event.orgId, post.spaceId, [\n parent.orgUserId,\n ]);\n return address(deps, event, event.orgId, openers, {\n postTitle: post.title,\n commenterName: await deps.mail.displayName(event.orgId, comment.orgUserId),\n excerpt: excerpt(comment.body, POST_EXCERPT),\n commentId: comment.id,\n postId: comment.postId,\n communityId: comment.communityId,\n });\n },\n },\n yourCommentWasRemoved: {\n triggers: ['discussion.comment.removed'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.commentRemoved.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const comment = parsed.data.data;\n if (!comment.postId || !comment.communityId) {\n return [];\n }\n if (comment.removedBy === comment.orgUserId) {\n return [];\n }\n const post = await deps.mail.post(event.orgId, comment.postId);\n if (!post) {\n return undefined;\n }\n return address(deps, event, event.orgId, [comment.orgUserId], {\n postTitle: post.title,\n postId: comment.postId,\n communityId: comment.communityId,\n });\n },\n },\n postAwaitingReview: {\n triggers: ['discussion.post.created', 'discussion.post.updated'],\n derive: async (event, deps) => {\n const parsed =\n event.type === 'discussion.post.created'\n ? discussionEvents.postCreated.safeParse(event)\n : discussionEvents.postUpdated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const post = parsed.data.data;\n if (post.status !== 'pending' || previousStatusOf(event) === 'pending') {\n return [];\n }\n const space = await deps.mail.space(event.orgId, post.spaceId);\n if (!space) {\n return undefined;\n }\n const audience = await communityModerators(deps.mail, event.orgId, post.communityId);\n return address(deps, event, event.orgId, audience, {\n spaceName: space.name,\n postTitle: post.title,\n authorName: await deps.mail.displayName(event.orgId, post.orgUserId),\n spaceId: post.spaceId,\n communityId: post.communityId,\n });\n },\n },\n joinRequest: {\n triggers: ['entitlement.space_member.created'],\n derive: async (event, deps) => {\n const parsed = entitlementEvents.spaceMemberCreated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const member = parsed.data.data;\n if (member.status !== 'requested') {\n return [];\n }\n const space = await deps.mail.space(event.orgId, member.spaceId);\n if (!space) {\n return undefined;\n }\n const active = await deps.mail.activeMemberCount(event.orgId, member.spaceId);\n const audience = await communityModerators(deps.mail, event.orgId, member.communityId);\n return address(deps, event, event.orgId, audience, {\n spaceName: space.name,\n memberName: await deps.mail.displayName(event.orgId, member.orgUserId),\n seatsLeft: seatsLeft(space.capacity, active),\n spaceId: member.spaceId,\n communityId: member.communityId,\n });\n },\n },\n requestApproved: {\n triggers: ['entitlement.space_member.updated'],\n derive: async (event, deps) => {\n const parsed = entitlementEvents.spaceMemberUpdated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const member = parsed.data.data;\n if (member.status !== 'active' || previousStatusOf(event) !== 'requested') {\n return [];\n }\n const space = await deps.mail.space(event.orgId, member.spaceId);\n if (!space) {\n return undefined;\n }\n return address(deps, event, event.orgId, [member.orgUserId], {\n spaceName: space.name,\n spaceId: member.spaceId,\n communityId: member.communityId,\n });\n },\n },\n requestDeclined: {\n triggers: ['entitlement.space_member.deleted'],\n derive: async (event, deps) => {\n if (previousStatusOf(event) !== 'requested') {\n return [];\n }\n const parsed = entitlementEvents.spaceMemberDeleted.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const member = parsed.data.data;\n const space = await deps.mail.space(event.orgId, member.spaceId);\n if (!space) {\n return undefined;\n }\n return address(deps, event, event.orgId, [member.orgUserId], {\n spaceName: space.name,\n communityId: member.communityId,\n });\n },\n },\n invitedToSpace: {\n triggers: ['entitlement.space_member.created'],\n derive: async (event, deps) => {\n const parsed = entitlementEvents.spaceMemberCreated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const member = parsed.data.data;\n if (member.status !== 'invited') {\n return [];\n }\n const space = await deps.mail.space(event.orgId, member.spaceId);\n if (!space) {\n return undefined;\n }\n const actor = actorOf(event);\n return address(deps, event, event.orgId, [member.orgUserId], {\n spaceName: space.name,\n inviterName: actor ? await deps.mail.displayName(event.orgId, actor) : 'A member',\n spaceId: member.spaceId,\n communityId: member.communityId,\n });\n },\n },\n messageRequest: {\n triggers: ['discussion.chat.created'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.chatCreated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const chat = parsed.data.data;\n if (chat.kind !== 'direct' || chat.status !== 'pending') {\n return [];\n }\n const recipient = chat.participantIds.find((id) => id !== chat.createdBy);\n if (!recipient) {\n return [];\n }\n const members = await deps.mail.communityMembersAmong(event.orgId, chat.communityId, [\n recipient,\n ]);\n const body = await deps.mail.latestMessageBody(event.orgId, chat.id);\n return address(deps, event, event.orgId, members, {\n senderName: chat.createdBy\n ? await deps.mail.displayName(event.orgId, chat.createdBy)\n : 'A member',\n excerpt: excerpt(body ?? '', MESSAGE_EXCERPT),\n chatId: chat.id,\n communityId: chat.communityId,\n });\n },\n },\n newMessage: {\n triggers: ['discussion.message.sent'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.messageSent.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const message = parsed.data.data;\n const chat = await deps.mail.chat(event.orgId, message.chatId);\n if (!chat) {\n return undefined;\n }\n if (chatStatusOf(event) === 'pending') {\n return [];\n }\n const at = new Date(event.occurredAt);\n const since = new Date(at.getTime() - MESSAGE_BATCH_MS);\n const recent = new Set(\n await deps.mail.recentMessageRecipients(\n event.orgId,\n deps.automationId,\n message.chatId,\n since,\n ),\n );\n const ids: string[] = [];\n for (const participant of chat.participants) {\n if (participant.leftAt || participant.orgUserId === message.orgUserId) {\n continue;\n }\n if (muted(participant, at) || recent.has(participant.orgUserId)) {\n continue;\n }\n if (\n await deps.mail.pairBlocked(\n event.orgId,\n chat.communityId,\n message.orgUserId,\n participant.orgUserId,\n )\n ) {\n continue;\n }\n ids.push(participant.orgUserId);\n }\n const members = await deps.mail.communityMembersAmong(event.orgId, chat.communityId, ids);\n const title =\n chat.kind === 'direct'\n ? await deps.mail.displayName(event.orgId, message.orgUserId)\n : await groupTitle(deps, event.orgId, chat.participants);\n return address(deps, event, event.orgId, members, {\n senderName: await deps.mail.displayName(event.orgId, message.orgUserId),\n chatTitle: title,\n excerpt: excerpt(message.body, MESSAGE_EXCERPT),\n chatId: message.chatId,\n communityId: message.communityId,\n });\n },\n },\n addedToGroupChat: {\n triggers: ['discussion.chat.participant.added'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.chatParticipantAdded.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const added = parsed.data.data;\n const chat = await deps.mail.chat(event.orgId, added.chatId);\n if (!chat) {\n return undefined;\n }\n return address(deps, event, event.orgId, [added.orgUserId], {\n adderName: await deps.mail.displayName(event.orgId, added.addedBy),\n participantNames: await participantNames(deps, event.orgId, chat.participants),\n chatId: added.chatId,\n communityId: added.communityId,\n });\n },\n },\n roleAssigned: {\n triggers: ['organization.role.assigned'],\n derive: async (event, deps) => {\n const parsed = organizationEvents.roleAssigned.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const assigned = parsed.data.data;\n const gain = await roleGain(deps.mail, event.orgId, assigned);\n if (!gain) {\n return [];\n }\n return address(deps, event, event.orgId, [assigned.orgUserId], gain);\n },\n },\n digest: silence(['discussion.digest.due']),\n somethingWasFlagged: {\n triggers: ['discussion.post.reported', 'discussion.comment.reported'],\n derive: async (event, deps) => {\n if (event.type === 'discussion.post.reported') {\n const parsed = discussionEvents.postReported.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const flag = parsed.data.data;\n const post = await deps.mail.post(event.orgId, flag.postId);\n const space = post ? await deps.mail.space(event.orgId, post.spaceId) : null;\n if (!post || !space) {\n return undefined;\n }\n const audience = await communityModerators(deps.mail, event.orgId, flag.communityId);\n return address(deps, event, event.orgId, audience, {\n spaceName: space.name,\n excerpt: excerpt(deps.mail.postText(post.body) || post.title, POST_EXCERPT),\n reason: flag.reason,\n spaceId: post.spaceId,\n communityId: flag.communityId,\n });\n }\n const parsed = discussionEvents.commentReported.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const flag = parsed.data.data;\n if (!flag.communityId) {\n return [];\n }\n const comment = await deps.mail.comment(event.orgId, flag.commentId);\n if (!comment?.postId) {\n return comment ? [] : undefined;\n }\n const post = await deps.mail.post(event.orgId, comment.postId);\n const space = post ? await deps.mail.space(event.orgId, post.spaceId) : null;\n if (!post || !space) {\n return undefined;\n }\n const audience = await communityModerators(deps.mail, event.orgId, flag.communityId);\n return address(deps, event, event.orgId, audience, {\n spaceName: space.name,\n excerpt: excerpt(comment.body, POST_EXCERPT),\n reason: flag.reason,\n spaceId: post.spaceId,\n communityId: flag.communityId,\n });\n },\n },\n messageWasFlagged: {\n triggers: ['discussion.message.reported'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.messageReported.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const flag = parsed.data.data;\n const audience = await communityModerators(deps.mail, event.orgId, flag.communityId);\n return address(deps, event, event.orgId, audience, {\n reason: flag.reason,\n communityId: flag.communityId,\n });\n },\n },\n};\n\nasync function participantNames(\n deps: ActionDeps,\n orgId: string,\n participants: { orgUserId: string; leftAt: Date | null }[],\n): Promise<string[]> {\n const names: string[] = [];\n for (const participant of participants) {\n if (participant.leftAt) {\n continue;\n }\n names.push(await deps.mail.displayName(orgId, participant.orgUserId));\n }\n return names;\n}\n\nasync function groupTitle(\n deps: ActionDeps,\n orgId: string,\n participants: { orgUserId: string; leftAt: Date | null }[],\n): Promise<string> {\n const names = await participantNames(deps, orgId, participants);\n return names.length > 0 ? names.join(', ') : 'Group chat';\n}\n"],"mappings":"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"}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { DomainEvent, JsonValue } from "../types/shared.js";
|
|
2
2
|
import { Logger } from "../types/ports.js";
|
|
3
|
+
import { ContentType } from "../types/schemas/content.js";
|
|
3
4
|
import { EmailTemplateId, EmailTemplateParams } from "../types/email-templates.js";
|
|
5
|
+
import { Permission } from "../types/schemas/organizations.js";
|
|
4
6
|
import { GrantEntitlementInput } from "../types/schemas/entitlements.js";
|
|
5
7
|
import { WebhookSender } from "../types/automations.js";
|
|
6
8
|
import { ChatStatus } from "../types/schemas/discussion.js";
|
|
7
9
|
import "../types/index.js";
|
|
8
10
|
import { CredentialStore } from "../shared/ports.js";
|
|
9
|
-
import { Mailer, MailerLookups } from "../shared/mailer.js";
|
|
10
11
|
import "../entitlements/index.js";
|
|
12
|
+
import { Mailer, MailerLookups } from "../shared/mailer.js";
|
|
11
13
|
import "./ports.js";
|
|
12
14
|
//#region src/automations/community-mail.d.ts
|
|
13
15
|
declare const MESSAGE_EXCERPT = 140;
|
|
@@ -34,9 +36,23 @@ interface CommunityMailReads {
|
|
|
34
36
|
communityMembersAmong(orgId: string, communityId: string, ids: string[]): Promise<string[]>;
|
|
35
37
|
spaceOpenersAmong(orgId: string, spaceId: string, ids: string[]): Promise<string[]>;
|
|
36
38
|
spaceOpeners(orgId: string, spaceId: string): Promise<string[]>;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
holders(orgId: string, permission: Permission, on?: string): Promise<string[]>;
|
|
40
|
+
role(orgId: string, roleKey: string): Promise<{
|
|
41
|
+
name: string;
|
|
42
|
+
permissions: Permission[];
|
|
43
|
+
} | null>;
|
|
44
|
+
heldOnOrg(orgId: string, orgUserId: string, permissions: Permission[]): Promise<Permission[]>;
|
|
45
|
+
orgName(orgId: string): Promise<string | null>;
|
|
46
|
+
contentInfo(orgId: string, item: {
|
|
47
|
+
contentId: string;
|
|
48
|
+
} | {
|
|
49
|
+
bundleId: string;
|
|
50
|
+
}): Promise<{
|
|
51
|
+
title: string;
|
|
52
|
+
type: string;
|
|
53
|
+
} | null>;
|
|
39
54
|
displayName(orgId: string, orgUserId: string): Promise<string>;
|
|
55
|
+
postText(body: string): string;
|
|
40
56
|
followerIds(orgId: string, postId: string): Promise<string[]>;
|
|
41
57
|
comment(orgId: string, commentId: string): Promise<{
|
|
42
58
|
orgUserId: string;
|
|
@@ -88,11 +104,25 @@ interface ActionDeps {
|
|
|
88
104
|
logger?: Logger;
|
|
89
105
|
}
|
|
90
106
|
declare const emptyCommunityMail: CommunityMailReads;
|
|
107
|
+
declare function communityModerators(reads: Pick<CommunityMailReads, 'holders' | 'communityMembersAmong'>, orgId: string, communityId: string): Promise<string[]>;
|
|
108
|
+
interface RoleGain {
|
|
109
|
+
roleName: string;
|
|
110
|
+
place: string;
|
|
111
|
+
contentId: string | null;
|
|
112
|
+
contentType: ContentType | null;
|
|
113
|
+
}
|
|
114
|
+
declare function roleGain(reads: Pick<CommunityMailReads, 'role' | 'orgName' | 'contentInfo' | 'heldOnOrg'>, orgId: string, assigned: {
|
|
115
|
+
orgUserId: string;
|
|
116
|
+
roleKey: string;
|
|
117
|
+
previousRoleKey: string | null;
|
|
118
|
+
contentId: string | null;
|
|
119
|
+
contentType: ContentType | null;
|
|
120
|
+
}): Promise<RoleGain | null>;
|
|
91
121
|
declare function actorOf(event: DomainEvent): string | undefined;
|
|
92
122
|
declare function previousStatusOf(event: DomainEvent): string | undefined;
|
|
93
123
|
declare function excerpt(value: string, max: number): string;
|
|
94
124
|
declare function seededCommunityId(deps: ActionDeps): string | null;
|
|
95
125
|
declare function address<K extends EmailTemplateId>(deps: ActionDeps, event: DomainEvent, orgId: string, orgUserIds: string[], params: EmailTemplateParams[K]): Promise<EmailRecipient<K>[] | undefined>;
|
|
96
126
|
//#endregion
|
|
97
|
-
export { ActionDeps, CommunityMailReads, EmailRecipient, GrantWriter, MESSAGE_BATCH_MS, MESSAGE_EXCERPT, POST_EXCERPT, actorOf, address, emptyCommunityMail, excerpt, previousStatusOf, seededCommunityId };
|
|
127
|
+
export { ActionDeps, CommunityMailReads, EmailRecipient, GrantWriter, MESSAGE_BATCH_MS, MESSAGE_EXCERPT, POST_EXCERPT, RoleGain, actorOf, address, communityModerators, emptyCommunityMail, excerpt, previousStatusOf, roleGain, seededCommunityId };
|
|
98
128
|
//# sourceMappingURL=community-mail.d.ts.map
|