@headless-lms/core 0.3.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 +24 -18
- 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 +5 -4
- package/dist/assets/index.js +4 -3
- package/dist/assets/jobs.d.ts +15 -0
- package/dist/assets/jobs.js +30 -0
- package/dist/assets/jobs.js.map +1 -0
- package/dist/assets/ports.d.ts +37 -17
- package/dist/assets/service.d.ts +30 -15
- package/dist/assets/service.js +229 -59
- package/dist/assets/service.js.map +1 -1
- package/dist/automations/actions.js +2 -63
- package/dist/automations/actions.js.map +1 -1
- package/dist/automations/catalog.d.ts +1 -2
- package/dist/automations/catalog.js +137 -65
- package/dist/automations/catalog.js.map +1 -1
- package/dist/automations/community-derivations.js +20 -20
- package/dist/automations/community-derivations.js.map +1 -1
- package/dist/automations/community-mail.d.ts +36 -20
- package/dist/automations/community-mail.js +32 -24
- 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/events.d.ts +0 -10
- package/dist/automations/index.d.ts +12 -4
- package/dist/automations/index.js +9 -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.d.ts +25 -0
- package/dist/automations/notifications/debounce.js +93 -0
- package/dist/automations/notifications/debounce.js.map +1 -0
- package/dist/automations/notifications/defaults.d.ts +8 -0
- package/dist/automations/notifications/defaults.js +346 -0
- package/dist/automations/notifications/defaults.js.map +1 -0
- package/dist/automations/notifications/delivery.d.ts +28 -0
- package/dist/automations/notifications/delivery.js +88 -0
- package/dist/automations/notifications/delivery.js.map +1 -0
- package/dist/automations/notifications/processor.d.ts +64 -0
- package/dist/automations/notifications/processor.js +270 -0
- package/dist/automations/notifications/processor.js.map +1 -0
- package/dist/automations/notifications/rules/content.d.ts +6 -0
- package/dist/automations/notifications/rules/content.js +96 -0
- package/dist/automations/notifications/rules/content.js.map +1 -0
- package/dist/automations/notifications/rules/discussion.d.ts +7 -0
- package/dist/automations/notifications/rules/discussion.js +678 -0
- package/dist/automations/notifications/rules/discussion.js.map +1 -0
- package/dist/automations/notifications/rules/entitlements.d.ts +6 -0
- package/dist/automations/notifications/rules/entitlements.js +276 -0
- package/dist/automations/notifications/rules/entitlements.js.map +1 -0
- 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/index.d.ts +6 -0
- package/dist/automations/notifications/rules/index.js +15 -0
- package/dist/automations/notifications/rules/index.js.map +1 -0
- package/dist/automations/notifications/rules/organizations.d.ts +6 -0
- package/dist/automations/notifications/rules/organizations.js +35 -0
- package/dist/automations/notifications/rules/organizations.js.map +1 -0
- package/dist/automations/notifications/rules/recipients.d.ts +32 -0
- package/dist/automations/notifications/rules/recipients.js +98 -0
- package/dist/automations/notifications/rules/recipients.js.map +1 -0
- package/dist/automations/notifications/service.d.ts +29 -0
- package/dist/automations/notifications/service.js +45 -0
- package/dist/automations/notifications/service.js.map +1 -0
- package/dist/automations/notifications/types.d.ts +7 -0
- package/dist/automations/notifications/types.js +560 -0
- package/dist/automations/notifications/types.js.map +1 -0
- package/dist/automations/ports.d.ts +134 -21
- package/dist/automations/service.d.ts +30 -30
- package/dist/automations/service.js +111 -144
- 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 +437 -46
- package/dist/content/events.js +45 -1
- package/dist/content/events.js.map +1 -1
- package/dist/content/index.d.ts +8 -4
- package/dist/content/index.js +5 -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 +37 -0
- package/dist/content/notification-categories.js +73 -0
- package/dist/content/notification-categories.js.map +1 -0
- package/dist/content/ports.d.ts +132 -68
- package/dist/content/service.d.ts +100 -68
- package/dist/content/service.js +703 -146
- package/dist/content/service.js.map +1 -1
- package/dist/content/titles.d.ts +25 -0
- package/dist/content/titles.js +82 -0
- package/dist/content/titles.js.map +1 -0
- 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 +63 -39
- package/dist/discussion/chats.js +455 -190
- package/dist/discussion/chats.js.map +1 -1
- package/dist/discussion/digests.d.ts +24 -0
- package/dist/discussion/digests.js +104 -0
- package/dist/discussion/digests.js.map +1 -0
- package/dist/discussion/events.d.ts +227 -23
- package/dist/discussion/events.js +114 -19
- package/dist/discussion/events.js.map +1 -1
- package/dist/discussion/index.d.ts +12 -4
- package/dist/discussion/index.js +9 -2
- package/dist/discussion/jobs.d.ts +16 -0
- package/dist/discussion/jobs.js +40 -0
- package/dist/discussion/jobs.js.map +1 -0
- package/dist/discussion/live.d.ts +106 -0
- package/dist/discussion/live.js +642 -0
- package/dist/discussion/live.js.map +1 -0
- package/dist/discussion/mentions.d.ts +11 -0
- package/dist/discussion/mentions.js +35 -0
- package/dist/discussion/mentions.js.map +1 -0
- package/dist/discussion/ports.d.ts +279 -114
- package/dist/discussion/ports.js.map +1 -1
- package/dist/discussion/posts.d.ts +60 -44
- package/dist/discussion/posts.js +453 -287
- 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 +50 -35
- package/dist/discussion/service.js +333 -205
- package/dist/discussion/service.js.map +1 -1
- package/dist/discussion/subscriptions.d.ts +37 -0
- package/dist/discussion/subscriptions.js +115 -0
- package/dist/discussion/subscriptions.js.map +1 -0
- package/dist/discussion/titles.d.ts +28 -0
- package/dist/discussion/titles.js +98 -0
- package/dist/discussion/titles.js.map +1 -0
- package/dist/entitlements/events.d.ts +20 -20
- package/dist/entitlements/index.d.ts +5 -4
- package/dist/entitlements/index.js +3 -2
- package/dist/entitlements/jobs.d.ts +12 -0
- package/dist/entitlements/jobs.js +18 -0
- package/dist/entitlements/jobs.js.map +1 -0
- package/dist/entitlements/model.d.ts +2 -2
- package/dist/entitlements/ports.d.ts +68 -38
- 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 +43 -28
- package/dist/entitlements/service.js +387 -160
- package/dist/entitlements/service.js.map +1 -1
- package/dist/identity/devices.d.ts +30 -0
- package/dist/identity/devices.js +109 -0
- package/dist/identity/devices.js.map +1 -0
- package/dist/identity/index.d.ts +6 -5
- package/dist/identity/index.js +4 -3
- package/dist/identity/model.d.ts +2 -2
- package/dist/identity/notification-settings.d.ts +28 -0
- package/dist/identity/notification-settings.js +114 -0
- package/dist/identity/notification-settings.js.map +1 -0
- package/dist/identity/ports.d.ts +80 -56
- 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/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 +15 -10
- package/dist/organizations/index.js +8 -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 +34 -0
- package/dist/organizations/notification-categories.js +66 -0
- package/dist/organizations/notification-categories.js.map +1 -0
- 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 +187 -65
- package/dist/organizations/service.d.ts +104 -35
- package/dist/organizations/service.js +1247 -323
- 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/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 +80 -26
- package/dist/reporting/member/ports.d.ts +50 -28
- package/dist/reporting/member/service.d.ts +81 -35
- package/dist/reporting/member/service.js +438 -145
- package/dist/reporting/member/service.js.map +1 -1
- package/dist/shared/actor.d.ts +22 -3
- package/dist/shared/errors.d.ts +15 -2
- package/dist/shared/errors.js +21 -2
- package/dist/shared/errors.js.map +1 -1
- package/dist/shared/id.d.ts +7 -0
- package/dist/shared/id.js +8 -1
- 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/mailer.d.ts +11 -1
- package/dist/shared/mailer.js +32 -5
- package/dist/shared/mailer.js.map +1 -1
- package/dist/shared/notification-categories.d.ts +16 -0
- package/dist/shared/notification-categories.js +94 -0
- package/dist/shared/notification-categories.js.map +1 -0
- package/dist/shared/notification-settings.d.ts +14 -0
- package/dist/shared/notification-settings.js +61 -0
- package/dist/shared/notification-settings.js.map +1 -0
- package/dist/shared/ports.d.ts +68 -2
- package/dist/shared/settings.d.ts +2 -1
- package/dist/shared/settings.js +1 -0
- package/dist/shared/settings.js.map +1 -1
- package/dist/types/automations.d.ts +8 -2
- package/dist/types/content.d.ts +2 -2
- package/dist/types/discussion.d.ts +4 -5
- package/dist/types/email-templates.d.ts +119 -6
- package/dist/types/entitlements.d.ts +2 -2
- package/dist/types/identity.d.ts +14 -17
- package/dist/types/index.d.ts +13 -12
- package/dist/types/index.js +2 -1
- package/dist/types/notifications.d.ts +157 -0
- package/dist/types/notifications.js +1 -0
- 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/automations.d.ts +1 -10
- package/dist/types/schemas/automations.js +1 -3
- package/dist/types/schemas/automations.js.map +1 -1
- package/dist/types/schemas/content.d.ts +596 -11
- package/dist/types/schemas/content.js +225 -9
- package/dist/types/schemas/content.js.map +1 -1
- package/dist/types/schemas/discussion.d.ts +362 -66
- package/dist/types/schemas/discussion.js +123 -15
- 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/identity.d.ts +1 -85
- package/dist/types/schemas/identity.js +1 -42
- package/dist/types/schemas/identity.js.map +1 -1
- package/dist/types/schemas/index.d.ts +8 -7
- package/dist/types/schemas/index.js +8 -7
- package/dist/types/schemas/notifications.d.ts +96 -0
- package/dist/types/schemas/notifications.js +57 -0
- package/dist/types/schemas/notifications.js.map +1 -0
- 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 +0 -1
- package/dist/types/schemas/progress.js +0 -1
- package/dist/types/schemas/progress.js.map +1 -1
- package/package.json +1 -1
- package/dist/automations/community-notifications.d.ts +0 -16
- package/dist/automations/community-notifications.js +0 -84
- package/dist/automations/community-notifications.js.map +0 -1
- package/dist/automations/seeds.d.ts +0 -17
- package/dist/automations/seeds.js +0 -235
- package/dist/automations/seeds.js.map +0 -1
- package/dist/identity/notifications.d.ts +0 -27
- package/dist/identity/notifications.js +0 -101
- package/dist/identity/notifications.js.map +0 -1
- 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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discussion.js","names":[],"sources":["../../../../src/automations/notifications/rules/discussion.ts"],"sourcesContent":["import { discussionEvents } from '../../../discussion/index.js';\nimport type { DomainEvent } from '../../../shared/ports.js';\nimport type { JsonValue, RecipientList, SubjectRef } from '../../../types/index.js';\nimport {\n communityModerators,\n MESSAGE_EXCERPT,\n POST_EXCERPT,\n previousStatusOf,\n} from '../../community-mail.js';\nimport type { NotificationReads, RecipientRule } from '../../ports.js';\nimport {\n actorOr,\n chatReach,\n chatTitle,\n eventAt,\n listOf,\n participantNames,\n Reasons,\n recipientsOf,\n textExcerpt,\n type ListBase,\n type Reach,\n} from './recipients.js';\n\ntype Post = NonNullable<Awaited<ReturnType<NotificationReads['post']>>>;\ntype Activity = NonNullable<Awaited<ReturnType<NotificationReads['activity']>>>;\n\ntype Place =\n | { kind: 'post'; postId: string; post: Post; spaceName: string }\n | { kind: 'activity'; activityId: string; activity: Activity };\n\nconst CHAT_STATUS = 'chatStatus';\n\nfunction postReach(\n reads: NotificationReads,\n orgId: string,\n post: { id: string; spaceId: string },\n exclude: (string | null)[],\n at: Date,\n): Reach {\n return {\n exclude,\n at,\n canOpen: (orgUserIds) => reads.spaceOpenersAmong(orgId, post.spaceId, orgUserIds),\n mutedOn: [\n { type: 'post', id: post.id },\n { type: 'space', id: post.spaceId },\n ],\n };\n}\n\nfunction courseReach(\n reads: NotificationReads,\n orgId: string,\n courseId: string,\n exclude: (string | null)[],\n at: Date,\n): Reach {\n return {\n exclude,\n at,\n canOpen: async (orgUserIds) => {\n const [holders, readers] = await Promise.all([\n reads.courseMembers(orgId, courseId),\n reads.holders(orgId, 'courses:read', courseId),\n ]);\n const open = new Set([...holders, ...readers]);\n return orgUserIds.filter((orgUserId) => open.has(orgUserId));\n },\n };\n}\n\nasync function placeOf(\n reads: NotificationReads,\n orgId: string,\n comment: { postId: string | null; activityId: string | null },\n): Promise<Place | null> {\n if (comment.postId) {\n const post = await reads.post(orgId, comment.postId);\n const space = post ? await reads.space(orgId, post.spaceId) : null;\n return post && space\n ? { kind: 'post', postId: comment.postId, post, spaceName: space.name }\n : null;\n }\n if (comment.activityId) {\n const activity = await reads.activity(orgId, comment.activityId);\n return activity ? { kind: 'activity', activityId: comment.activityId, activity } : null;\n }\n return null;\n}\n\nfunction placeReach(\n reads: NotificationReads,\n orgId: string,\n place: Place,\n exclude: (string | null)[],\n at: Date,\n): Reach {\n return place.kind === 'post'\n ? postReach(reads, orgId, { id: place.postId, spaceId: place.post.spaceId }, exclude, at)\n : courseReach(reads, orgId, place.activity.courseId, exclude, at);\n}\n\nfunction commentBase(\n type: string,\n place: Place,\n commentId: string,\n actorId: string | null,\n display: Record<string, JsonValue>,\n): ListBase {\n const subject: SubjectRef = { type: 'comment', id: commentId };\n if (place.kind === 'post') {\n return {\n type,\n scope: 'space',\n actorId,\n communityId: place.post.communityId,\n spaceId: place.post.spaceId,\n subject,\n parent: { type: 'post', id: place.postId },\n display: { postTitle: place.post.title, ...display },\n };\n }\n return {\n type,\n scope: 'org',\n actorId,\n communityId: null,\n spaceId: null,\n subject,\n parent: { type: 'activity', id: place.activityId },\n display: {\n activityTitle: place.activity.title,\n courseTitle: place.activity.courseTitle,\n courseId: place.activity.courseId,\n ...display,\n },\n };\n}\n\nasync function mentionsOf(\n reads: NotificationReads,\n orgId: string,\n base: Omit<ListBase, 'type'>,\n mentioned: string[],\n reach: Reach,\n): Promise<RecipientList[]> {\n if (mentioned.length === 0) {\n return [];\n }\n const recipients = await recipientsOf(\n reads,\n orgId,\n new Reasons().add(mentioned, 'mentioned'),\n reach,\n );\n return listOf({ ...base, type: 'mention' }, recipients);\n}\n\nasync function commentLists(\n event: DomainEvent,\n reads: NotificationReads,\n comment: {\n id: string;\n postId: string | null;\n activityId: string | null;\n parentId: string | null;\n orgUserId: string;\n body: string;\n mentionedOrgUserIds: string[];\n },\n): Promise<RecipientList[]> {\n const orgId = event.orgId;\n const place = await placeOf(reads, orgId, comment);\n if (!place) {\n return [];\n }\n const at = eventAt(event);\n const exclude = [comment.orgUserId, actorOr(event, comment.orgUserId)];\n const reasons = new Reasons();\n if (place.kind === 'post') {\n reasons.add([place.post.orgUserId], 'post_author');\n }\n const parent = comment.parentId ? await reads.comment(orgId, comment.parentId) : null;\n reasons.add([parent?.orgUserId], 'comment_author');\n if (place.kind === 'post') {\n reasons.add(await reads.followerIds(orgId, place.postId), 'follower');\n }\n reasons.add(\n await reads.threadParticipants(\n orgId,\n place.kind === 'post'\n ? { type: 'post', id: place.postId }\n : { type: 'activity', id: place.activityId },\n ),\n 'thread_participant',\n );\n const name = await reads.displayName(orgId, comment.orgUserId);\n const text = await textExcerpt(reads, orgId, comment.body, POST_EXCERPT);\n const created = listOf(\n commentBase('comment.created', place, comment.id, comment.orgUserId, {\n commenterName: name,\n excerpt: text,\n }),\n await recipientsOf(reads, orgId, reasons, placeReach(reads, orgId, place, exclude, at)),\n );\n const mentions = await mentionsOf(\n reads,\n orgId,\n commentBase('mention', place, comment.id, comment.orgUserId, {\n authorName: name,\n excerpt: text,\n }),\n comment.mentionedOrgUserIds,\n placeReach(reads, orgId, place, exclude, at),\n );\n return [...created, ...mentions];\n}\n\nconst postPublished: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.postPublished.safeParse(event);\n if (!parsed.success || previousStatusOf(event) === 'removed') {\n return [];\n }\n const post = parsed.data.data;\n const orgId = event.orgId;\n const space = await reads.space(orgId, post.spaceId);\n if (!space) {\n return [];\n }\n const at = eventAt(event);\n const actor = actorOr(event, null);\n const exclude = [...new Set([post.orgUserId, ...(actor ? [actor] : [])])];\n const reach = postReach(reads, orgId, post, exclude, at);\n const base = {\n scope: 'space' as const,\n communityId: post.communityId,\n spaceId: post.spaceId,\n subject: { type: 'post' as const, id: post.id },\n parent: null,\n };\n const lists: RecipientList[] = [];\n if (previousStatusOf(event) === 'pending') {\n lists.push(\n ...listOf(\n {\n ...base,\n type: 'post.approved',\n actorId: actor,\n display: { postTitle: post.title, spaceName: space.name },\n },\n await recipientsOf(reads, orgId, new Reasons().add([post.orgUserId], 'subject'), {\n ...reach,\n exclude: [actor],\n }),\n ),\n );\n }\n const authorName = await reads.displayName(orgId, post.orgUserId);\n const text = await textExcerpt(\n reads,\n orgId,\n reads.postText(post.body) || post.title,\n POST_EXCERPT,\n );\n lists.push(\n ...(await mentionsOf(\n reads,\n orgId,\n {\n ...base,\n actorId: post.orgUserId,\n display: { authorName, excerpt: text, postTitle: post.title },\n },\n post.mentionedOrgUserIds,\n reach,\n )),\n );\n lists.push({\n ...base,\n type: 'post.published',\n actorId: post.orgUserId,\n display: {\n spaceName: space.name,\n postTitle: post.title,\n authorName,\n excerpt: text,\n },\n audience: { reason: 'space_member', spaceId: post.spaceId, exclude },\n });\n return lists;\n};\n\nasync function awaitingReview(\n event: DomainEvent,\n reads: NotificationReads,\n post: {\n id: string;\n communityId: string;\n spaceId: string;\n orgUserId: string;\n title: string;\n status: string;\n },\n): Promise<RecipientList[]> {\n if (post.status !== 'pending' || previousStatusOf(event) === 'pending') {\n return [];\n }\n const orgId = event.orgId;\n const space = await reads.space(orgId, post.spaceId);\n if (!space) {\n return [];\n }\n const actor = actorOr(event, post.orgUserId);\n return listOf(\n {\n type: 'post.awaiting_review',\n scope: 'space',\n actorId: post.orgUserId,\n communityId: post.communityId,\n spaceId: post.spaceId,\n subject: { type: 'post', id: post.id },\n parent: null,\n display: {\n spaceName: space.name,\n postTitle: post.title,\n authorName: await reads.displayName(orgId, post.orgUserId),\n },\n },\n await recipientsOf(\n reads,\n orgId,\n new Reasons().add(await communityModerators(reads, orgId, post.communityId), 'moderator'),\n {\n exclude: [actor, post.orgUserId],\n at: eventAt(event),\n canOpen: (orgUserIds) => reads.spaceOpenersAmong(orgId, post.spaceId, orgUserIds),\n },\n ),\n );\n}\n\nconst postCreated: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.postCreated.safeParse(event);\n return parsed.success ? awaitingReview(event, reads, parsed.data.data) : [];\n};\n\nconst postUpdated: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.postUpdated.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const post = parsed.data.data;\n const orgId = event.orgId;\n const lists = await awaitingReview(event, reads, post);\n if (post.mentionedOrgUserIds.length === 0) {\n return lists;\n }\n const actor = actorOr(event, post.orgUserId);\n lists.push(\n ...(await mentionsOf(\n reads,\n orgId,\n {\n scope: 'space',\n actorId: post.orgUserId,\n communityId: post.communityId,\n spaceId: post.spaceId,\n subject: { type: 'post', id: post.id },\n parent: null,\n display: {\n authorName: await reads.displayName(orgId, post.orgUserId),\n excerpt: await textExcerpt(\n reads,\n orgId,\n reads.postText(post.body) || post.title,\n POST_EXCERPT,\n ),\n postTitle: post.title,\n },\n },\n post.mentionedOrgUserIds,\n postReach(reads, orgId, post, [actor, post.orgUserId], eventAt(event)),\n )),\n );\n return lists;\n};\n\nconst postRemoved: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.postRemoved.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const post = parsed.data.data;\n const remover = post.removedBy ?? actorOr(event, null);\n if (!remover || remover === post.orgUserId) {\n return [];\n }\n const orgId = event.orgId;\n const space = await reads.space(orgId, post.spaceId);\n if (!space) {\n return [];\n }\n return listOf(\n {\n type: 'post.removed',\n scope: 'space',\n actorId: remover,\n communityId: post.communityId,\n spaceId: post.spaceId,\n subject: { type: 'post', id: post.id },\n parent: null,\n display: { postTitle: post.title, spaceName: space.name },\n },\n await recipientsOf(\n reads,\n orgId,\n new Reasons().add([post.orgUserId], 'subject'),\n postReach(reads, orgId, post, [remover], eventAt(event)),\n ),\n );\n};\n\nconst postReacted: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.postReactionAdded.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const reaction = parsed.data.data;\n const orgId = event.orgId;\n const post = await reads.post(orgId, reaction.postId);\n if (!post) {\n return [];\n }\n return listOf(\n {\n type: 'post.reacted',\n scope: 'space',\n actorId: reaction.orgUserId,\n communityId: post.communityId,\n spaceId: post.spaceId,\n subject: { type: 'post', id: reaction.postId },\n parent: null,\n display: {\n postTitle: post.title,\n reactorName: await reads.displayName(orgId, reaction.orgUserId),\n reaction: reaction.reaction,\n },\n },\n await recipientsOf(\n reads,\n orgId,\n new Reasons().add([post.orgUserId], 'post_author'),\n postReach(\n reads,\n orgId,\n { id: reaction.postId, spaceId: post.spaceId },\n [reaction.orgUserId, actorOr(event, null)],\n eventAt(event),\n ),\n ),\n );\n};\n\nconst commentCreated: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.commentCreated.safeParse(event);\n if (!parsed.success || parsed.data.data.status !== 'published') {\n return [];\n }\n return commentLists(event, reads, parsed.data.data);\n};\n\nconst commentPublished: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.commentPublished.safeParse(event);\n return parsed.success ? commentLists(event, reads, parsed.data.data) : [];\n};\n\nconst commentUpdated: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.commentUpdated.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const comment = parsed.data.data;\n if (comment.status !== 'published' || comment.mentionedOrgUserIds.length === 0) {\n return [];\n }\n const orgId = event.orgId;\n const place = await placeOf(reads, orgId, comment);\n if (!place) {\n return [];\n }\n return mentionsOf(\n reads,\n orgId,\n commentBase('mention', place, comment.id, comment.orgUserId, {\n authorName: await reads.displayName(orgId, comment.orgUserId),\n excerpt: await textExcerpt(reads, orgId, comment.body, POST_EXCERPT),\n }),\n comment.mentionedOrgUserIds,\n placeReach(\n reads,\n orgId,\n place,\n [comment.orgUserId, actorOr(event, comment.orgUserId)],\n eventAt(event),\n ),\n );\n};\n\nconst commentRemoved: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.commentRemoved.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const comment = parsed.data.data;\n const remover = comment.removedBy ?? actorOr(event, null);\n if (!remover || remover === comment.orgUserId) {\n return [];\n }\n const orgId = event.orgId;\n const place = await placeOf(reads, orgId, comment);\n if (!place) {\n return [];\n }\n return listOf(\n commentBase('comment.removed', place, comment.id, remover, {}),\n await recipientsOf(\n reads,\n orgId,\n new Reasons().add([comment.orgUserId], 'subject'),\n placeReach(reads, orgId, place, [remover], eventAt(event)),\n ),\n );\n};\n\nconst commentReacted: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.commentReactionAdded.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const reaction = parsed.data.data;\n const orgId = event.orgId;\n const comment = await reads.comment(orgId, reaction.commentId);\n const place = comment ? await placeOf(reads, orgId, comment) : null;\n if (!comment || !place) {\n return [];\n }\n return listOf(\n commentBase('comment.reacted', place, reaction.commentId, reaction.orgUserId, {\n excerpt: await textExcerpt(reads, orgId, comment.body, POST_EXCERPT),\n reactorName: await reads.displayName(orgId, reaction.orgUserId),\n reaction: reaction.reaction,\n }),\n await recipientsOf(\n reads,\n orgId,\n new Reasons().add([comment.orgUserId], 'comment_author'),\n placeReach(reads, orgId, place, [reaction.orgUserId, actorOr(event, null)], eventAt(event)),\n ),\n );\n};\n\nconst postReported: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.postReported.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const flag = parsed.data.data;\n const orgId = event.orgId;\n const post = await reads.post(orgId, flag.postId);\n const space = post ? await reads.space(orgId, post.spaceId) : null;\n if (!post || !space) {\n return [];\n }\n return listOf(\n {\n type: 'content.flagged',\n scope: 'space',\n actorId: flag.orgUserId,\n communityId: flag.communityId,\n spaceId: post.spaceId,\n subject: { type: 'post', id: flag.postId },\n parent: null,\n display: {\n spaceName: space.name,\n excerpt: await textExcerpt(\n reads,\n orgId,\n reads.postText(post.body) || post.title,\n POST_EXCERPT,\n ),\n reason: flag.reason,\n },\n },\n await recipientsOf(\n reads,\n orgId,\n new Reasons().add(await communityModerators(reads, orgId, flag.communityId), 'moderator'),\n {\n exclude: [actorOr(event, flag.orgUserId)],\n at: eventAt(event),\n canOpen: (orgUserIds) => reads.spaceOpenersAmong(orgId, post.spaceId, orgUserIds),\n },\n ),\n );\n};\n\nconst commentReported: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.commentReported.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const flag = parsed.data.data;\n const orgId = event.orgId;\n const comment = await reads.comment(orgId, flag.commentId);\n const place = comment ? await placeOf(reads, orgId, comment) : null;\n if (!comment || !place) {\n return [];\n }\n const actor = actorOr(event, flag.orgUserId);\n const text = await textExcerpt(reads, orgId, comment.body, POST_EXCERPT);\n if (place.kind === 'post') {\n return listOf(\n {\n ...commentBase('content.flagged', place, flag.commentId, actor, {}),\n display: { spaceName: place.spaceName, excerpt: text, reason: flag.reason },\n },\n await recipientsOf(\n reads,\n orgId,\n new Reasons().add(\n await communityModerators(reads, orgId, place.post.communityId),\n 'moderator',\n ),\n {\n exclude: [actor],\n at: eventAt(event),\n canOpen: (orgUserIds) => reads.spaceOpenersAmong(orgId, place.post.spaceId, orgUserIds),\n },\n ),\n );\n }\n return listOf(\n commentBase('content.flagged', place, flag.commentId, actor, {\n excerpt: text,\n reason: flag.reason,\n }),\n await recipientsOf(\n reads,\n orgId,\n new Reasons().add(\n await reads.holders(orgId, 'courses:moderate', place.activity.courseId),\n 'moderator',\n ),\n { exclude: [actor], at: eventAt(event) },\n ),\n );\n};\n\nfunction chatPending(event: DomainEvent, status: string): boolean {\n const value = event.metadata?.[CHAT_STATUS];\n return (typeof value === 'string' ? value : status) === 'pending';\n}\n\nasync function chatMentions(\n event: DomainEvent,\n reads: NotificationReads,\n message: { id: string; chatId: string; orgUserId: string; body: string },\n mentioned: string[],\n): Promise<RecipientList[]> {\n const orgId = event.orgId;\n const chat = await reads.chat(orgId, message.chatId);\n if (!chat || chatPending(event, chat.status)) {\n return [];\n }\n return mentionsOf(\n reads,\n orgId,\n {\n scope: 'chat',\n actorId: message.orgUserId,\n communityId: chat.communityId,\n spaceId: null,\n subject: { type: 'message', id: message.id },\n parent: { type: 'chat', id: message.chatId },\n display: {\n authorName: await reads.displayName(orgId, message.orgUserId),\n excerpt: await textExcerpt(reads, orgId, message.body, MESSAGE_EXCERPT),\n chatTitle: await chatTitle(reads, orgId, chat, message.orgUserId),\n },\n },\n mentioned,\n chatReach(reads, orgId, chat, message.chatId, message.orgUserId, eventAt(event)),\n );\n}\n\nexport function messageSummary(count: number): string {\n return count === 1 ? '1 new message' : `${count} new messages`;\n}\n\nconst messageSent: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.messageSent.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const message = parsed.data.data;\n const orgId = event.orgId;\n const chat = await reads.chat(orgId, message.chatId);\n if (!chat || chatPending(event, chat.status)) {\n return [];\n }\n const senderName = await reads.displayName(orgId, message.orgUserId);\n const sent = listOf(\n {\n type: 'message.sent',\n scope: 'chat',\n actorId: message.orgUserId,\n communityId: chat.communityId,\n spaceId: null,\n subject: { type: 'message', id: message.id },\n parent: { type: 'chat', id: message.chatId },\n display: {\n senderName,\n chatTitle: await chatTitle(reads, orgId, chat, message.orgUserId),\n excerpt: await textExcerpt(reads, orgId, message.body, MESSAGE_EXCERPT),\n count: '1',\n summary: messageSummary(1),\n },\n },\n await recipientsOf(\n reads,\n orgId,\n new Reasons().add(\n chat.participants.map((participant) => participant.orgUserId),\n 'participant',\n ),\n chatReach(reads, orgId, chat, message.chatId, message.orgUserId, eventAt(event)),\n ),\n );\n return [...sent, ...(await chatMentions(event, reads, message, message.mentionedOrgUserIds))];\n};\n\nconst messageUpdated: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.messageUpdated.safeParse(event);\n if (!parsed.success || parsed.data.data.mentionedOrgUserIds.length === 0) {\n return [];\n }\n const message = parsed.data.data;\n return chatMentions(event, reads, message, message.mentionedOrgUserIds);\n};\n\nconst messageReacted: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.messageReactionAdded.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const reaction = parsed.data.data;\n const orgId = event.orgId;\n const [message, chat] = await Promise.all([\n reads.message(orgId, reaction.messageId),\n reads.chat(orgId, reaction.chatId),\n ]);\n if (!message || !chat) {\n return [];\n }\n return listOf(\n {\n type: 'message.reacted',\n scope: 'chat',\n actorId: reaction.orgUserId,\n communityId: chat.communityId,\n spaceId: null,\n subject: { type: 'message', id: reaction.messageId },\n parent: { type: 'chat', id: reaction.chatId },\n display: {\n excerpt: await textExcerpt(reads, orgId, message.body, MESSAGE_EXCERPT),\n reactorName: await reads.displayName(orgId, reaction.orgUserId),\n reaction: reaction.reaction,\n },\n },\n await recipientsOf(\n reads,\n orgId,\n new Reasons().add([message.orgUserId], 'message_author'),\n chatReach(reads, orgId, chat, reaction.chatId, reaction.orgUserId, eventAt(event)),\n ),\n );\n};\n\nconst messageReported: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.messageReported.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const flag = parsed.data.data;\n const orgId = event.orgId;\n const message = await reads.message(orgId, flag.messageId);\n return listOf(\n {\n type: 'message.flagged',\n scope: 'chat',\n actorId: flag.orgUserId,\n communityId: flag.communityId,\n spaceId: null,\n subject: { type: 'message', id: flag.messageId },\n parent: message ? { type: 'chat', id: message.chatId } : null,\n display: { reason: flag.reason },\n },\n await recipientsOf(\n reads,\n orgId,\n new Reasons().add(await communityModerators(reads, orgId, flag.communityId), 'moderator'),\n { exclude: [actorOr(event, flag.orgUserId)], at: eventAt(event) },\n ),\n );\n};\n\nconst chatCreated: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.chatCreated.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const created = parsed.data.data;\n if (created.kind !== 'direct' || created.status !== 'pending' || !created.createdBy) {\n return [];\n }\n const orgId = event.orgId;\n const chat = await reads.chat(orgId, created.id);\n if (!chat) {\n return [];\n }\n const body = await reads.latestMessageBody(orgId, created.id);\n return listOf(\n {\n type: 'chat.request',\n scope: 'chat',\n actorId: created.createdBy,\n communityId: created.communityId,\n spaceId: null,\n subject: { type: 'chat', id: created.id },\n parent: null,\n display: {\n senderName: await reads.displayName(orgId, created.createdBy),\n excerpt: await textExcerpt(reads, orgId, body ?? '', MESSAGE_EXCERPT),\n },\n },\n await recipientsOf(\n reads,\n orgId,\n new Reasons().add(created.participantIds, 'participant'),\n chatReach(reads, orgId, chat, created.id, created.createdBy, eventAt(event)),\n ),\n );\n};\n\nconst chatParticipantAdded: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.chatParticipantAdded.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const added = parsed.data.data;\n const orgId = event.orgId;\n const chat = await reads.chat(orgId, added.chatId);\n if (!chat) {\n return [];\n }\n return listOf(\n {\n type: 'chat.added',\n scope: 'chat',\n actorId: added.addedBy,\n communityId: added.communityId,\n spaceId: null,\n subject: { type: 'chat', id: added.chatId },\n parent: null,\n display: {\n adderName: await reads.displayName(orgId, added.addedBy),\n participantNames: await participantNames(reads, orgId, chat),\n },\n },\n await recipientsOf(\n reads,\n orgId,\n new Reasons().add([added.orgUserId], 'subject'),\n chatReach(reads, orgId, chat, added.chatId, added.addedBy, eventAt(event)),\n ),\n );\n};\n\nconst digestDue: RecipientRule = async (event, reads) => {\n const parsed = discussionEvents.digestDue.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const { communityId, from, to } = parsed.data.data;\n const orgId = event.orgId;\n const posts = await reads.postsPublished(orgId, communityId, from, to);\n if (posts.length === 0) {\n return [];\n }\n const title = await reads.communityTitle(orgId, communityId);\n if (title === null) {\n return [];\n }\n const spaceIds = [...new Set(posts.map((post) => post.spaceId))];\n const openers = new Map<string, Set<string>>();\n for (const spaceId of spaceIds) {\n const open = await reads.spaceOpeners(orgId, spaceId);\n const muted = new Set(\n open.length > 0\n ? await reads.mutedAmong(orgId, { type: 'space', id: spaceId }, open, eventAt(event))\n : [],\n );\n openers.set(spaceId, new Set(open.filter((orgUserId) => !muted.has(orgUserId))));\n }\n const groups = new Map<string, { spaceIds: string[]; members: Reasons }>();\n for (const orgUserId of await reads.communityMembers(orgId, communityId)) {\n const open = spaceIds.filter((spaceId) => openers.get(spaceId)?.has(orgUserId));\n if (open.length === 0) {\n continue;\n }\n const key = open.join(' ');\n const group = groups.get(key) ?? { spaceIds: open, members: new Reasons() };\n group.members.add([orgUserId], 'community_member');\n groups.set(key, group);\n }\n const lists: RecipientList[] = [];\n for (const group of groups.values()) {\n const shown = posts.filter((post) => group.spaceIds.includes(post.spaceId));\n lists.push(\n ...listOf(\n {\n type: 'digest',\n scope: 'community',\n actorId: null,\n communityId,\n spaceId: null,\n subject: { type: 'community', id: communityId },\n parent: null,\n display: {\n communityTitle: title,\n from: from.toISOString(),\n to: to.toISOString(),\n posts: shown.map((post) => ({\n postId: post.postId,\n title: post.title,\n authorName: post.authorName,\n spaceName: post.spaceName,\n commentCount: post.commentCount,\n })),\n },\n },\n await recipientsOf(reads, orgId, group.members, { exclude: [], at: eventAt(event) }),\n ),\n );\n }\n return lists;\n};\n\nexport const DISCUSSION_RULES: Record<string, RecipientRule> = {\n 'discussion.post.published': postPublished,\n 'discussion.post.created': postCreated,\n 'discussion.post.updated': postUpdated,\n 'discussion.post.removed': postRemoved,\n 'discussion.post.reaction.added': postReacted,\n 'discussion.comment.created': commentCreated,\n 'discussion.comment.published': commentPublished,\n 'discussion.comment.updated': commentUpdated,\n 'discussion.comment.removed': commentRemoved,\n 'discussion.comment.reaction.added': commentReacted,\n 'discussion.post.reported': postReported,\n 'discussion.comment.reported': commentReported,\n 'discussion.message.sent': messageSent,\n 'discussion.message.updated': messageUpdated,\n 'discussion.message.reaction.added': messageReacted,\n 'discussion.message.reported': messageReported,\n 'discussion.chat.created': chatCreated,\n 'discussion.chat.participant.added': chatParticipantAdded,\n 'discussion.digest.due': digestDue,\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { RecipientRule } from "../../ports.js";
|
|
2
|
+
//#region src/automations/notifications/rules/entitlements.d.ts
|
|
3
|
+
declare const ENTITLEMENT_RULES: Record<string, RecipientRule>;
|
|
4
|
+
//#endregion
|
|
5
|
+
export { ENTITLEMENT_RULES };
|
|
6
|
+
//# sourceMappingURL=entitlements.d.ts.map
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { entitlementEvents } from "../../../entitlements/events.js";
|
|
2
|
+
import { communityModerators, previousStatusOf } from "../../community-mail.js";
|
|
3
|
+
import { Reasons, actorOr, eventAt, listOf, recipientsOf } from "./recipients.js";
|
|
4
|
+
import { eventBase, eventDisplay } from "./events.js";
|
|
5
|
+
//#region src/automations/notifications/rules/entitlements.ts
|
|
6
|
+
function seatsLeft(capacity, active) {
|
|
7
|
+
return capacity == null ? "No cap" : String(Math.max(capacity - active, 0));
|
|
8
|
+
}
|
|
9
|
+
function subjectOnly(event, reads, orgUserId) {
|
|
10
|
+
return recipientsOf(reads, event.orgId, new Reasons().add([orgUserId], "subject"), {
|
|
11
|
+
exclude: [actorOr(event, null)],
|
|
12
|
+
at: eventAt(event)
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
async function grantSubject(reads, orgId, grant) {
|
|
16
|
+
if (grant.bundleId) {
|
|
17
|
+
const info = await reads.contentInfo(orgId, { bundleId: grant.bundleId });
|
|
18
|
+
return info ? {
|
|
19
|
+
ref: {
|
|
20
|
+
type: "bundle",
|
|
21
|
+
id: grant.bundleId
|
|
22
|
+
},
|
|
23
|
+
...info
|
|
24
|
+
} : null;
|
|
25
|
+
}
|
|
26
|
+
if (!grant.contentId) return null;
|
|
27
|
+
const info = await reads.contentInfo(orgId, { contentId: grant.contentId });
|
|
28
|
+
if (!info) return null;
|
|
29
|
+
return {
|
|
30
|
+
ref: {
|
|
31
|
+
type: info.type === "download" ? "download" : info.type === "community" ? "community" : "course",
|
|
32
|
+
id: grant.contentId
|
|
33
|
+
},
|
|
34
|
+
...info
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
async function membershipEnded(event, reads, grant, ref) {
|
|
38
|
+
const orgId = event.orgId;
|
|
39
|
+
const communityIds = await reads.communitiesOpenedBy(orgId, grant);
|
|
40
|
+
if (communityIds.length === 0) return null;
|
|
41
|
+
const held = new Set((await reads.memberCommunityTitles(orgId, grant.orgUserId)).map((community) => community.id));
|
|
42
|
+
const communities = [];
|
|
43
|
+
for (const communityId of communityIds.filter((id) => !held.has(id))) {
|
|
44
|
+
const title = await reads.communityTitle(orgId, communityId);
|
|
45
|
+
if (title !== null) communities.push({
|
|
46
|
+
communityId,
|
|
47
|
+
communityTitle: title
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
if (communities.length === 0) return null;
|
|
51
|
+
return listOf({
|
|
52
|
+
type: "community.membership_ended",
|
|
53
|
+
scope: "org",
|
|
54
|
+
actorId: actorOr(event, null),
|
|
55
|
+
communityId: null,
|
|
56
|
+
spaceId: null,
|
|
57
|
+
subject: ref,
|
|
58
|
+
parent: null,
|
|
59
|
+
display: { communities }
|
|
60
|
+
}, await subjectOnly(event, reads, grant.orgUserId));
|
|
61
|
+
}
|
|
62
|
+
async function spaceBase(reads, orgId, member, type, actorId) {
|
|
63
|
+
const space = await reads.space(orgId, member.spaceId);
|
|
64
|
+
return space ? {
|
|
65
|
+
space,
|
|
66
|
+
base: {
|
|
67
|
+
type,
|
|
68
|
+
scope: "space",
|
|
69
|
+
actorId,
|
|
70
|
+
communityId: member.communityId,
|
|
71
|
+
spaceId: member.spaceId,
|
|
72
|
+
subject: {
|
|
73
|
+
type: "space",
|
|
74
|
+
id: member.spaceId
|
|
75
|
+
},
|
|
76
|
+
parent: null
|
|
77
|
+
}
|
|
78
|
+
} : null;
|
|
79
|
+
}
|
|
80
|
+
const spaceMemberCreated = async (event, reads) => {
|
|
81
|
+
const parsed = entitlementEvents.spaceMemberCreated.safeParse(event);
|
|
82
|
+
if (!parsed.success) return [];
|
|
83
|
+
const member = parsed.data.data;
|
|
84
|
+
const orgId = event.orgId;
|
|
85
|
+
if (member.status === "requested") {
|
|
86
|
+
const found = await spaceBase(reads, orgId, member, "space.join_requested", member.orgUserId);
|
|
87
|
+
if (!found) return [];
|
|
88
|
+
const active = await reads.activeMemberCount(orgId, member.spaceId);
|
|
89
|
+
return listOf({
|
|
90
|
+
...found.base,
|
|
91
|
+
display: {
|
|
92
|
+
spaceName: found.space.name,
|
|
93
|
+
spaceId: member.spaceId,
|
|
94
|
+
memberName: await reads.displayName(orgId, member.orgUserId),
|
|
95
|
+
seatsLeft: seatsLeft(found.space.capacity, active)
|
|
96
|
+
}
|
|
97
|
+
}, await recipientsOf(reads, orgId, new Reasons().add(await communityModerators(reads, orgId, member.communityId), "moderator"), {
|
|
98
|
+
exclude: [member.orgUserId, actorOr(event, null)],
|
|
99
|
+
at: eventAt(event),
|
|
100
|
+
canOpen: (orgUserIds) => reads.spaceOpenersAmong(orgId, member.spaceId, orgUserIds)
|
|
101
|
+
}));
|
|
102
|
+
}
|
|
103
|
+
if (member.status === "invited") {
|
|
104
|
+
const inviter = actorOr(event, null);
|
|
105
|
+
const found = await spaceBase(reads, orgId, member, "space.invited", inviter);
|
|
106
|
+
if (!found) return [];
|
|
107
|
+
return listOf({
|
|
108
|
+
...found.base,
|
|
109
|
+
display: {
|
|
110
|
+
spaceName: found.space.name,
|
|
111
|
+
spaceId: member.spaceId,
|
|
112
|
+
inviterName: inviter ? await reads.displayName(orgId, inviter) : "A member"
|
|
113
|
+
}
|
|
114
|
+
}, await subjectOnly(event, reads, member.orgUserId));
|
|
115
|
+
}
|
|
116
|
+
return [];
|
|
117
|
+
};
|
|
118
|
+
const spaceMemberUpdated = async (event, reads) => {
|
|
119
|
+
const parsed = entitlementEvents.spaceMemberUpdated.safeParse(event);
|
|
120
|
+
if (!parsed.success) return [];
|
|
121
|
+
const member = parsed.data.data;
|
|
122
|
+
if (member.status !== "active" || previousStatusOf(event) !== "requested") return [];
|
|
123
|
+
const found = await spaceBase(reads, event.orgId, member, "space.request_approved", actorOr(event, null));
|
|
124
|
+
return found ? listOf({
|
|
125
|
+
...found.base,
|
|
126
|
+
display: {
|
|
127
|
+
spaceName: found.space.name,
|
|
128
|
+
spaceId: member.spaceId
|
|
129
|
+
}
|
|
130
|
+
}, await subjectOnly(event, reads, member.orgUserId)) : [];
|
|
131
|
+
};
|
|
132
|
+
const spaceMemberDeleted = async (event, reads) => {
|
|
133
|
+
const parsed = entitlementEvents.spaceMemberDeleted.safeParse(event);
|
|
134
|
+
if (!parsed.success || previousStatusOf(event) !== "requested") return [];
|
|
135
|
+
const member = parsed.data.data;
|
|
136
|
+
const found = await spaceBase(reads, event.orgId, member, "space.request_declined", actorOr(event, null));
|
|
137
|
+
return found ? listOf({
|
|
138
|
+
...found.base,
|
|
139
|
+
display: {
|
|
140
|
+
spaceName: found.space.name,
|
|
141
|
+
spaceId: member.spaceId
|
|
142
|
+
}
|
|
143
|
+
}, await subjectOnly(event, reads, member.orgUserId)) : [];
|
|
144
|
+
};
|
|
145
|
+
async function grantedEvent(reads, orgId, grant) {
|
|
146
|
+
return grant.contentId ? reads.event(orgId, grant.contentId) : null;
|
|
147
|
+
}
|
|
148
|
+
const entitlementCreated = async (event, reads) => {
|
|
149
|
+
const parsed = entitlementEvents.entitlementCreated.safeParse(event);
|
|
150
|
+
if (!parsed.success) return [];
|
|
151
|
+
const grant = parsed.data.data;
|
|
152
|
+
const orgId = event.orgId;
|
|
153
|
+
const actor = actorOr(event, null);
|
|
154
|
+
const granted = await grantedEvent(reads, orgId, grant);
|
|
155
|
+
if (granted) {
|
|
156
|
+
if (!granted.settings.notifications) return [];
|
|
157
|
+
return listOf({
|
|
158
|
+
...eventBase("event.granted", granted, actor),
|
|
159
|
+
display: eventDisplay(granted)
|
|
160
|
+
}, await recipientsOf(reads, orgId, new Reasons().add([grant.orgUserId], "event_member"), {
|
|
161
|
+
exclude: [],
|
|
162
|
+
at: eventAt(event)
|
|
163
|
+
}));
|
|
164
|
+
}
|
|
165
|
+
const subject = await subjectOnly(event, reads, grant.orgUserId);
|
|
166
|
+
if (subject.length === 0) return [];
|
|
167
|
+
const lists = [];
|
|
168
|
+
const target = await grantSubject(reads, orgId, grant);
|
|
169
|
+
if (target) lists.push(...listOf({
|
|
170
|
+
type: "access.granted",
|
|
171
|
+
scope: "org",
|
|
172
|
+
actorId: actor,
|
|
173
|
+
communityId: null,
|
|
174
|
+
spaceId: null,
|
|
175
|
+
subject: target.ref,
|
|
176
|
+
parent: null,
|
|
177
|
+
display: {
|
|
178
|
+
contentTitle: target.title,
|
|
179
|
+
contentType: target.type
|
|
180
|
+
}
|
|
181
|
+
}, subject));
|
|
182
|
+
for (const communityId of await reads.communitiesOpenedBy(orgId, grant)) {
|
|
183
|
+
const title = await reads.communityTitle(orgId, communityId);
|
|
184
|
+
if (title === null) continue;
|
|
185
|
+
lists.push(...listOf({
|
|
186
|
+
type: "community.welcome",
|
|
187
|
+
scope: "community",
|
|
188
|
+
actorId: actor,
|
|
189
|
+
communityId,
|
|
190
|
+
spaceId: null,
|
|
191
|
+
subject: {
|
|
192
|
+
type: "community",
|
|
193
|
+
id: communityId
|
|
194
|
+
},
|
|
195
|
+
parent: null,
|
|
196
|
+
display: {
|
|
197
|
+
communityTitle: title,
|
|
198
|
+
spaceNames: await reads.openSpaceNames(orgId, grant.orgUserId, communityId)
|
|
199
|
+
}
|
|
200
|
+
}, subject));
|
|
201
|
+
}
|
|
202
|
+
return lists;
|
|
203
|
+
};
|
|
204
|
+
const entitlementEndingSoon = async (event, reads) => {
|
|
205
|
+
const parsed = entitlementEvents.entitlementEndingSoon.safeParse(event);
|
|
206
|
+
if (!parsed.success) return [];
|
|
207
|
+
const grant = parsed.data.data;
|
|
208
|
+
const title = await reads.communityTitle(event.orgId, grant.communityId);
|
|
209
|
+
if (title === null) return [];
|
|
210
|
+
return listOf({
|
|
211
|
+
type: "community.ending_soon",
|
|
212
|
+
scope: "community",
|
|
213
|
+
actorId: null,
|
|
214
|
+
communityId: grant.communityId,
|
|
215
|
+
spaceId: null,
|
|
216
|
+
subject: {
|
|
217
|
+
type: "community",
|
|
218
|
+
id: grant.communityId
|
|
219
|
+
},
|
|
220
|
+
parent: null,
|
|
221
|
+
display: {
|
|
222
|
+
communityTitle: title,
|
|
223
|
+
expiresAt: grant.expiresAt ? grant.expiresAt.toISOString() : ""
|
|
224
|
+
}
|
|
225
|
+
}, await subjectOnly(event, reads, grant.orgUserId));
|
|
226
|
+
};
|
|
227
|
+
const entitlementDeleted = async (event, reads) => {
|
|
228
|
+
const parsed = entitlementEvents.entitlementDeleted.safeParse(event);
|
|
229
|
+
if (!parsed.success) return [];
|
|
230
|
+
const grant = parsed.data.data;
|
|
231
|
+
const revokedFrom = await grantedEvent(reads, event.orgId, grant);
|
|
232
|
+
if (revokedFrom) {
|
|
233
|
+
if (!revokedFrom.settings.notifications || !(await reads.contentLostBy(event.orgId, grant)).includes(revokedFrom.id)) return [];
|
|
234
|
+
const actor = actorOr(event, null);
|
|
235
|
+
return listOf({
|
|
236
|
+
...eventBase("event.revoked", revokedFrom, actor),
|
|
237
|
+
display: eventDisplay(revokedFrom)
|
|
238
|
+
}, await recipientsOf(reads, event.orgId, new Reasons().add([grant.orgUserId], "event_member"), {
|
|
239
|
+
exclude: [actor],
|
|
240
|
+
at: eventAt(event)
|
|
241
|
+
}));
|
|
242
|
+
}
|
|
243
|
+
const target = await grantSubject(reads, event.orgId, grant);
|
|
244
|
+
const ended = await membershipEnded(event, reads, grant, target?.ref ?? null);
|
|
245
|
+
if (ended) return ended;
|
|
246
|
+
if (!target || (await reads.contentLostBy(event.orgId, grant)).length === 0) return [];
|
|
247
|
+
return listOf({
|
|
248
|
+
type: "access.revoked",
|
|
249
|
+
scope: "org",
|
|
250
|
+
actorId: actorOr(event, null),
|
|
251
|
+
communityId: null,
|
|
252
|
+
spaceId: null,
|
|
253
|
+
subject: target.ref,
|
|
254
|
+
parent: null,
|
|
255
|
+
display: { contentTitle: target.title }
|
|
256
|
+
}, await subjectOnly(event, reads, grant.orgUserId));
|
|
257
|
+
};
|
|
258
|
+
const entitlementExpired = async (event, reads) => {
|
|
259
|
+
const parsed = entitlementEvents.entitlementExpired.safeParse(event);
|
|
260
|
+
if (!parsed.success) return [];
|
|
261
|
+
const grant = parsed.data.data;
|
|
262
|
+
return await membershipEnded(event, reads, grant, (await grantSubject(reads, event.orgId, grant))?.ref ?? null) ?? [];
|
|
263
|
+
};
|
|
264
|
+
const ENTITLEMENT_RULES = {
|
|
265
|
+
"entitlement.space_member.created": spaceMemberCreated,
|
|
266
|
+
"entitlement.space_member.updated": spaceMemberUpdated,
|
|
267
|
+
"entitlement.space_member.deleted": spaceMemberDeleted,
|
|
268
|
+
"entitlement.created": entitlementCreated,
|
|
269
|
+
"entitlement.ending_soon": entitlementEndingSoon,
|
|
270
|
+
"entitlement.deleted": entitlementDeleted,
|
|
271
|
+
"entitlement.expired": entitlementExpired
|
|
272
|
+
};
|
|
273
|
+
//#endregion
|
|
274
|
+
export { ENTITLEMENT_RULES };
|
|
275
|
+
|
|
276
|
+
//# sourceMappingURL=entitlements.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entitlements.js","names":[],"sources":["../../../../src/automations/notifications/rules/entitlements.ts"],"sourcesContent":["import { entitlementEvents } from '../../../entitlements/index.js';\nimport type { DomainEvent } from '../../../shared/ports.js';\nimport type { RecipientList, SubjectRef } from '../../../types/index.js';\nimport { communityModerators, previousStatusOf } from '../../community-mail.js';\nimport type { NotificationReads, RecipientRule } from '../../ports.js';\nimport { actorOr, eventAt, listOf, Reasons, recipientsOf } from './recipients.js';\nimport { eventBase, eventDisplay } from './events.js';\n\ntype Grant = { orgUserId: string; contentId: string | null; bundleId: string | null };\ntype SpaceMember = { spaceId: string; communityId: string; orgUserId: string; status: string };\n\nfunction seatsLeft(capacity: number | null, active: number): string {\n return capacity == null ? 'No cap' : String(Math.max(capacity - active, 0));\n}\n\nfunction subjectOnly(event: DomainEvent, reads: NotificationReads, orgUserId: string) {\n return recipientsOf(reads, event.orgId, new Reasons().add([orgUserId], 'subject'), {\n exclude: [actorOr(event, null)],\n at: eventAt(event),\n });\n}\n\nasync function grantSubject(\n reads: NotificationReads,\n orgId: string,\n grant: Grant,\n): Promise<{ ref: SubjectRef; title: string; type: string } | null> {\n if (grant.bundleId) {\n const info = await reads.contentInfo(orgId, { bundleId: grant.bundleId });\n return info ? { ref: { type: 'bundle', id: grant.bundleId }, ...info } : null;\n }\n if (!grant.contentId) {\n return null;\n }\n const info = await reads.contentInfo(orgId, { contentId: grant.contentId });\n if (!info) {\n return null;\n }\n const type: SubjectRef['type'] =\n info.type === 'download' ? 'download' : info.type === 'community' ? 'community' : 'course';\n return { ref: { type, id: grant.contentId }, ...info };\n}\n\nasync function membershipEnded(\n event: DomainEvent,\n reads: NotificationReads,\n grant: Grant,\n ref: SubjectRef | null,\n): Promise<RecipientList[] | null> {\n const orgId = event.orgId;\n const communityIds = await reads.communitiesOpenedBy(orgId, grant);\n if (communityIds.length === 0) {\n return null;\n }\n const held = new Set(\n (await reads.memberCommunityTitles(orgId, grant.orgUserId)).map((community) => community.id),\n );\n const communities: { communityId: string; communityTitle: string }[] = [];\n for (const communityId of communityIds.filter((id) => !held.has(id))) {\n const title = await reads.communityTitle(orgId, communityId);\n if (title !== null) {\n communities.push({ communityId, communityTitle: title });\n }\n }\n if (communities.length === 0) {\n return null;\n }\n return listOf(\n {\n type: 'community.membership_ended',\n scope: 'org',\n actorId: actorOr(event, null),\n communityId: null,\n spaceId: null,\n subject: ref,\n parent: null,\n display: { communities },\n },\n await subjectOnly(event, reads, grant.orgUserId),\n );\n}\n\nasync function spaceBase(\n reads: NotificationReads,\n orgId: string,\n member: SpaceMember,\n type: string,\n actorId: string | null,\n) {\n const space = await reads.space(orgId, member.spaceId);\n return space\n ? {\n space,\n base: {\n type,\n scope: 'space' as const,\n actorId,\n communityId: member.communityId,\n spaceId: member.spaceId,\n subject: { type: 'space' as const, id: member.spaceId },\n parent: null,\n },\n }\n : null;\n}\n\nconst spaceMemberCreated: RecipientRule = async (event, reads) => {\n const parsed = entitlementEvents.spaceMemberCreated.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const member = parsed.data.data;\n const orgId = event.orgId;\n if (member.status === 'requested') {\n const found = await spaceBase(reads, orgId, member, 'space.join_requested', member.orgUserId);\n if (!found) {\n return [];\n }\n const active = await reads.activeMemberCount(orgId, member.spaceId);\n return listOf(\n {\n ...found.base,\n display: {\n spaceName: found.space.name,\n spaceId: member.spaceId,\n memberName: await reads.displayName(orgId, member.orgUserId),\n seatsLeft: seatsLeft(found.space.capacity, active),\n },\n },\n await recipientsOf(\n reads,\n orgId,\n new Reasons().add(await communityModerators(reads, orgId, member.communityId), 'moderator'),\n {\n exclude: [member.orgUserId, actorOr(event, null)],\n at: eventAt(event),\n canOpen: (orgUserIds) => reads.spaceOpenersAmong(orgId, member.spaceId, orgUserIds),\n },\n ),\n );\n }\n if (member.status === 'invited') {\n const inviter = actorOr(event, null);\n const found = await spaceBase(reads, orgId, member, 'space.invited', inviter);\n if (!found) {\n return [];\n }\n return listOf(\n {\n ...found.base,\n display: {\n spaceName: found.space.name,\n spaceId: member.spaceId,\n inviterName: inviter ? await reads.displayName(orgId, inviter) : 'A member',\n },\n },\n await subjectOnly(event, reads, member.orgUserId),\n );\n }\n return [];\n};\n\nconst spaceMemberUpdated: RecipientRule = async (event, reads) => {\n const parsed = entitlementEvents.spaceMemberUpdated.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const member = parsed.data.data;\n if (member.status !== 'active' || previousStatusOf(event) !== 'requested') {\n return [];\n }\n const found = await spaceBase(\n reads,\n event.orgId,\n member,\n 'space.request_approved',\n actorOr(event, null),\n );\n return found\n ? listOf(\n { ...found.base, display: { spaceName: found.space.name, spaceId: member.spaceId } },\n await subjectOnly(event, reads, member.orgUserId),\n )\n : [];\n};\n\nconst spaceMemberDeleted: RecipientRule = async (event, reads) => {\n const parsed = entitlementEvents.spaceMemberDeleted.safeParse(event);\n if (!parsed.success || previousStatusOf(event) !== 'requested') {\n return [];\n }\n const member = parsed.data.data;\n const found = await spaceBase(\n reads,\n event.orgId,\n member,\n 'space.request_declined',\n actorOr(event, null),\n );\n return found\n ? listOf(\n { ...found.base, display: { spaceName: found.space.name, spaceId: member.spaceId } },\n await subjectOnly(event, reads, member.orgUserId),\n )\n : [];\n};\n\nasync function grantedEvent(reads: NotificationReads, orgId: string, grant: Grant) {\n return grant.contentId ? reads.event(orgId, grant.contentId) : null;\n}\n\nconst entitlementCreated: RecipientRule = async (event, reads) => {\n const parsed = entitlementEvents.entitlementCreated.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const grant = parsed.data.data;\n const orgId = event.orgId;\n const actor = actorOr(event, null);\n const granted = await grantedEvent(reads, orgId, grant);\n if (granted) {\n if (!granted.settings.notifications) {\n return [];\n }\n return listOf(\n { ...eventBase('event.granted', granted, actor), display: eventDisplay(granted) },\n await recipientsOf(reads, orgId, new Reasons().add([grant.orgUserId], 'event_member'), {\n exclude: [],\n at: eventAt(event),\n }),\n );\n }\n const subject = await subjectOnly(event, reads, grant.orgUserId);\n if (subject.length === 0) {\n return [];\n }\n const lists: RecipientList[] = [];\n const target = await grantSubject(reads, orgId, grant);\n if (target) {\n lists.push(\n ...listOf(\n {\n type: 'access.granted',\n scope: 'org',\n actorId: actor,\n communityId: null,\n spaceId: null,\n subject: target.ref,\n parent: null,\n display: { contentTitle: target.title, contentType: target.type },\n },\n subject,\n ),\n );\n }\n for (const communityId of await reads.communitiesOpenedBy(orgId, grant)) {\n const title = await reads.communityTitle(orgId, communityId);\n if (title === null) {\n continue;\n }\n lists.push(\n ...listOf(\n {\n type: 'community.welcome',\n scope: 'community',\n actorId: actor,\n communityId,\n spaceId: null,\n subject: { type: 'community', id: communityId },\n parent: null,\n display: {\n communityTitle: title,\n spaceNames: await reads.openSpaceNames(orgId, grant.orgUserId, communityId),\n },\n },\n subject,\n ),\n );\n }\n return lists;\n};\n\nconst entitlementEndingSoon: RecipientRule = async (event, reads) => {\n const parsed = entitlementEvents.entitlementEndingSoon.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const grant = parsed.data.data;\n const title = await reads.communityTitle(event.orgId, grant.communityId);\n if (title === null) {\n return [];\n }\n return listOf(\n {\n type: 'community.ending_soon',\n scope: 'community',\n actorId: null,\n communityId: grant.communityId,\n spaceId: null,\n subject: { type: 'community', id: grant.communityId },\n parent: null,\n display: {\n communityTitle: title,\n expiresAt: grant.expiresAt ? grant.expiresAt.toISOString() : '',\n },\n },\n await subjectOnly(event, reads, grant.orgUserId),\n );\n};\n\nconst entitlementDeleted: RecipientRule = async (event, reads) => {\n const parsed = entitlementEvents.entitlementDeleted.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const grant = parsed.data.data;\n const revokedFrom = await grantedEvent(reads, event.orgId, grant);\n if (revokedFrom) {\n if (\n !revokedFrom.settings.notifications ||\n !(await reads.contentLostBy(event.orgId, grant)).includes(revokedFrom.id)\n ) {\n return [];\n }\n const actor = actorOr(event, null);\n return listOf(\n { ...eventBase('event.revoked', revokedFrom, actor), display: eventDisplay(revokedFrom) },\n await recipientsOf(reads, event.orgId, new Reasons().add([grant.orgUserId], 'event_member'), {\n exclude: [actor],\n at: eventAt(event),\n }),\n );\n }\n const target = await grantSubject(reads, event.orgId, grant);\n const ended = await membershipEnded(event, reads, grant, target?.ref ?? null);\n if (ended) {\n return ended;\n }\n if (!target || (await reads.contentLostBy(event.orgId, grant)).length === 0) {\n return [];\n }\n return listOf(\n {\n type: 'access.revoked',\n scope: 'org',\n actorId: actorOr(event, null),\n communityId: null,\n spaceId: null,\n subject: target.ref,\n parent: null,\n display: { contentTitle: target.title },\n },\n await subjectOnly(event, reads, grant.orgUserId),\n );\n};\n\nconst entitlementExpired: RecipientRule = async (event, reads) => {\n const parsed = entitlementEvents.entitlementExpired.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const grant = parsed.data.data;\n const target = await grantSubject(reads, event.orgId, grant);\n return (await membershipEnded(event, reads, grant, target?.ref ?? null)) ?? [];\n};\n\nexport const ENTITLEMENT_RULES: Record<string, RecipientRule> = {\n 'entitlement.space_member.created': spaceMemberCreated,\n 'entitlement.space_member.updated': spaceMemberUpdated,\n 'entitlement.space_member.deleted': spaceMemberDeleted,\n 'entitlement.created': entitlementCreated,\n 'entitlement.ending_soon': entitlementEndingSoon,\n 'entitlement.deleted': entitlementDeleted,\n 'entitlement.expired': entitlementExpired,\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Event } from "../../../types/schemas/content.js";
|
|
2
|
+
import "../../../content/index.js";
|
|
3
|
+
import { ListBase } from "./recipients.js";
|
|
4
|
+
//#region src/automations/notifications/rules/events.d.ts
|
|
5
|
+
declare function eventBase(type: string, event: Event, actorId: string | null): Omit<ListBase, 'display'>;
|
|
6
|
+
declare function eventDisplay(event: Event): Record<string, string>;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { eventBase, eventDisplay };
|
|
9
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//#region src/automations/notifications/rules/events.ts
|
|
2
|
+
function eventBase(type, event, actorId) {
|
|
3
|
+
return {
|
|
4
|
+
type,
|
|
5
|
+
scope: "org",
|
|
6
|
+
actorId,
|
|
7
|
+
communityId: null,
|
|
8
|
+
spaceId: null,
|
|
9
|
+
subject: {
|
|
10
|
+
type: "event",
|
|
11
|
+
id: event.id
|
|
12
|
+
},
|
|
13
|
+
parent: null
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function eventDisplay(event) {
|
|
17
|
+
return {
|
|
18
|
+
eventTitle: event.title,
|
|
19
|
+
startsAt: event.startsAt.toISOString(),
|
|
20
|
+
endsAt: event.endsAt.toISOString(),
|
|
21
|
+
timeZone: event.timeZone
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { eventBase, eventDisplay };
|
|
26
|
+
|
|
27
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.js","names":[],"sources":["../../../../src/automations/notifications/rules/events.ts"],"sourcesContent":["import type { Event } from '../../../content/index.js';\nimport type { ListBase } from './recipients.js';\n\nexport function eventBase(\n type: string,\n event: Event,\n actorId: string | null,\n): Omit<ListBase, 'display'> {\n return {\n type,\n scope: 'org',\n actorId,\n communityId: null,\n spaceId: null,\n subject: { type: 'event', id: event.id },\n parent: null,\n };\n}\n\nexport function eventDisplay(event: Event): Record<string, string> {\n return {\n eventTitle: event.title,\n startsAt: event.startsAt.toISOString(),\n endsAt: event.endsAt.toISOString(),\n timeZone: event.timeZone,\n };\n}\n"],"mappings":";AAGA,SAAgB,UACd,MACA,OACA,SAC2B;CAC3B,OAAO;EACL;EACA,OAAO;EACP;EACA,aAAa;EACb,SAAS;EACT,SAAS;GAAE,MAAM;GAAS,IAAI,MAAM;EAAG;EACvC,QAAQ;CACV;AACF;AAEA,SAAgB,aAAa,OAAsC;CACjE,OAAO;EACL,YAAY,MAAM;EAClB,UAAU,MAAM,SAAS,YAAY;EACrC,QAAQ,MAAM,OAAO,YAAY;EACjC,UAAU,MAAM;CAClB;AACF"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { DISCUSSION_RULES } from "./discussion.js";
|
|
2
|
+
import { CONTENT_RULES } from "./content.js";
|
|
3
|
+
import { ENTITLEMENT_RULES } from "./entitlements.js";
|
|
4
|
+
import { ORGANIZATION_RULES } from "./organizations.js";
|
|
5
|
+
//#region src/automations/notifications/rules/index.ts
|
|
6
|
+
const RECIPIENT_RULES = {
|
|
7
|
+
...DISCUSSION_RULES,
|
|
8
|
+
...ENTITLEMENT_RULES,
|
|
9
|
+
...CONTENT_RULES,
|
|
10
|
+
...ORGANIZATION_RULES
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
13
|
+
export { RECIPIENT_RULES };
|
|
14
|
+
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../../src/automations/notifications/rules/index.ts"],"sourcesContent":["import type { RecipientRule } from '../../ports.js';\nimport { CONTENT_RULES } from './content.js';\nimport { DISCUSSION_RULES } from './discussion.js';\nimport { ENTITLEMENT_RULES } from './entitlements.js';\nimport { ORGANIZATION_RULES } from './organizations.js';\n\nexport const RECIPIENT_RULES: Record<string, RecipientRule> = {\n ...DISCUSSION_RULES,\n ...ENTITLEMENT_RULES,\n ...CONTENT_RULES,\n ...ORGANIZATION_RULES,\n};\n"],"mappings":";;;;;AAMA,MAAa,kBAAiD;CAC5D,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;AACL"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { RecipientRule } from "../../ports.js";
|
|
2
|
+
//#region src/automations/notifications/rules/organizations.d.ts
|
|
3
|
+
declare const ORGANIZATION_RULES: Record<string, RecipientRule>;
|
|
4
|
+
//#endregion
|
|
5
|
+
export { ORGANIZATION_RULES };
|
|
6
|
+
//# sourceMappingURL=organizations.d.ts.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { organizationEvents } from "../../../organizations/events.js";
|
|
2
|
+
import { roleGain } from "../../community-mail.js";
|
|
3
|
+
import { Reasons, actorOr, eventAt, listOf, recipientsOf } from "./recipients.js";
|
|
4
|
+
//#region src/automations/notifications/rules/organizations.ts
|
|
5
|
+
const roleAssigned = async (event, reads) => {
|
|
6
|
+
const parsed = organizationEvents.roleAssigned.safeParse(event);
|
|
7
|
+
if (!parsed.success) return [];
|
|
8
|
+
const assigned = parsed.data.data;
|
|
9
|
+
const orgId = event.orgId;
|
|
10
|
+
const gain = await roleGain(reads, orgId, assigned);
|
|
11
|
+
if (!gain) return [];
|
|
12
|
+
return listOf({
|
|
13
|
+
type: "role.assigned",
|
|
14
|
+
scope: "org",
|
|
15
|
+
actorId: actorOr(event, null),
|
|
16
|
+
communityId: null,
|
|
17
|
+
spaceId: null,
|
|
18
|
+
subject: null,
|
|
19
|
+
parent: null,
|
|
20
|
+
display: {
|
|
21
|
+
roleName: gain.roleName,
|
|
22
|
+
place: gain.place,
|
|
23
|
+
contentId: gain.contentId,
|
|
24
|
+
contentType: gain.contentType
|
|
25
|
+
}
|
|
26
|
+
}, await recipientsOf(reads, orgId, new Reasons().add([assigned.orgUserId], "subject"), {
|
|
27
|
+
exclude: [actorOr(event, null)],
|
|
28
|
+
at: eventAt(event)
|
|
29
|
+
}));
|
|
30
|
+
};
|
|
31
|
+
const ORGANIZATION_RULES = { "organization.role.assigned": roleAssigned };
|
|
32
|
+
//#endregion
|
|
33
|
+
export { ORGANIZATION_RULES };
|
|
34
|
+
|
|
35
|
+
//# sourceMappingURL=organizations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"organizations.js","names":[],"sources":["../../../../src/automations/notifications/rules/organizations.ts"],"sourcesContent":["import { organizationEvents } from '../../../organizations/index.js';\nimport { roleGain } from '../../community-mail.js';\nimport type { RecipientRule } from '../../ports.js';\nimport { actorOr, eventAt, listOf, Reasons, recipientsOf } from './recipients.js';\n\nconst roleAssigned: RecipientRule = async (event, reads) => {\n const parsed = organizationEvents.roleAssigned.safeParse(event);\n if (!parsed.success) {\n return [];\n }\n const assigned = parsed.data.data;\n const orgId = event.orgId;\n const gain = await roleGain(reads, orgId, assigned);\n if (!gain) {\n return [];\n }\n return listOf(\n {\n type: 'role.assigned',\n scope: 'org',\n actorId: actorOr(event, null),\n communityId: null,\n spaceId: null,\n subject: null,\n parent: null,\n display: {\n roleName: gain.roleName,\n place: gain.place,\n contentId: gain.contentId,\n contentType: gain.contentType,\n },\n },\n await recipientsOf(reads, orgId, new Reasons().add([assigned.orgUserId], 'subject'), {\n exclude: [actorOr(event, null)],\n at: eventAt(event),\n }),\n );\n};\n\nexport const ORGANIZATION_RULES: Record<string, RecipientRule> = {\n 'organization.role.assigned': roleAssigned,\n};\n"],"mappings":";;;;AAKA,MAAM,eAA8B,OAAO,OAAO,UAAU;CAC1D,MAAM,SAAS,mBAAmB,aAAa,UAAU,KAAK;CAC9D,IAAI,CAAC,OAAO,SACV,OAAO,CAAC;CAEV,MAAM,WAAW,OAAO,KAAK;CAC7B,MAAM,QAAQ,MAAM;CACpB,MAAM,OAAO,MAAM,SAAS,OAAO,OAAO,QAAQ;CAClD,IAAI,CAAC,MACH,OAAO,CAAC;CAEV,OAAO,OACL;EACE,MAAM;EACN,OAAO;EACP,SAAS,QAAQ,OAAO,IAAI;EAC5B,aAAa;EACb,SAAS;EACT,SAAS;EACT,QAAQ;EACR,SAAS;GACP,UAAU,KAAK;GACf,OAAO,KAAK;GACZ,WAAW,KAAK;GAChB,aAAa,KAAK;EACpB;CACF,GACA,MAAM,aAAa,OAAO,OAAO,IAAI,QAAQ,CAAC,CAAC,IAAI,CAAC,SAAS,SAAS,GAAG,SAAS,GAAG;EACnF,SAAS,CAAC,QAAQ,OAAO,IAAI,CAAC;EAC9B,IAAI,QAAQ,KAAK;CACnB,CAAC,CACH;AACF;AAEA,MAAa,qBAAoD,EAC/D,8BAA8B,aAChC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { DomainEvent } from "../../../types/shared.js";
|
|
2
|
+
import { Reason, RecipientList, SubjectRef } from "../../../types/notifications.js";
|
|
3
|
+
import "../../../types/index.js";
|
|
4
|
+
import "../../../shared/ports.js";
|
|
5
|
+
import { NotificationReads } from "../../ports.js";
|
|
6
|
+
//#region src/automations/notifications/rules/recipients.d.ts
|
|
7
|
+
type Recipient = NonNullable<RecipientList['recipients']>[number];
|
|
8
|
+
type ListBase = Omit<RecipientList, 'recipients' | 'audience'>;
|
|
9
|
+
type Chat = NonNullable<Awaited<ReturnType<NotificationReads['chat']>>>;
|
|
10
|
+
declare class Reasons {
|
|
11
|
+
private readonly byPerson;
|
|
12
|
+
add(orgUserIds: (string | null | undefined)[], reason: Reason): this;
|
|
13
|
+
ids(): string[];
|
|
14
|
+
entries(): [string, Reason[]][];
|
|
15
|
+
}
|
|
16
|
+
interface Reach {
|
|
17
|
+
exclude: (string | null | undefined)[];
|
|
18
|
+
canOpen?: (orgUserIds: string[]) => Promise<string[]>;
|
|
19
|
+
mutedOn?: SubjectRef[];
|
|
20
|
+
at: Date;
|
|
21
|
+
}
|
|
22
|
+
declare function recipientsOf(reads: NotificationReads, orgId: string, reasons: Reasons, reach: Reach): Promise<Recipient[]>;
|
|
23
|
+
declare function listOf(base: ListBase, recipients: Recipient[]): RecipientList[];
|
|
24
|
+
declare function eventAt(event: DomainEvent): Date;
|
|
25
|
+
declare function actorOr(event: DomainEvent, fallback: string | null): string | null;
|
|
26
|
+
declare function textExcerpt(reads: NotificationReads, orgId: string, text: string, max: number): Promise<string>;
|
|
27
|
+
declare function participantNames(reads: NotificationReads, orgId: string, chat: Chat): Promise<string[]>;
|
|
28
|
+
declare function chatTitle(reads: NotificationReads, orgId: string, chat: Chat, senderOrgUserId: string): Promise<string>;
|
|
29
|
+
declare function chatReach(reads: NotificationReads, orgId: string, chat: Chat, chatId: string, actor: string, at: Date): Reach;
|
|
30
|
+
//#endregion
|
|
31
|
+
export { Chat, ListBase, Reach, Reasons, Recipient, actorOr, chatReach, chatTitle, eventAt, listOf, participantNames, recipientsOf, textExcerpt };
|
|
32
|
+
//# sourceMappingURL=recipients.d.ts.map
|