@headless-lms/core 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/events.d.ts +42 -24
- package/dist/assets/events.js +1 -1
- package/dist/assets/events.js.map +1 -1
- package/dist/assets/file-urls.d.ts +20 -0
- package/dist/assets/file-urls.js +25 -0
- package/dist/assets/file-urls.js.map +1 -0
- package/dist/assets/index.d.ts +6 -4
- package/dist/assets/index.js +4 -2
- 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/model.d.ts +2 -2
- package/dist/assets/ports.d.ts +46 -15
- package/dist/assets/service.d.ts +38 -7
- package/dist/assets/service.js +287 -38
- package/dist/assets/service.js.map +1 -1
- package/dist/automations/actions.d.ts +13 -24
- package/dist/automations/actions.js +106 -42
- package/dist/automations/actions.js.map +1 -1
- package/dist/automations/catalog.js +226 -3
- package/dist/automations/catalog.js.map +1 -1
- package/dist/automations/community-derivations.d.ts +14 -0
- package/dist/automations/community-derivations.js +439 -0
- package/dist/automations/community-derivations.js.map +1 -0
- package/dist/automations/community-mail.d.ts +98 -0
- package/dist/automations/community-mail.js +70 -0
- package/dist/automations/community-mail.js.map +1 -0
- package/dist/automations/events.d.ts +18 -0
- package/dist/automations/index.d.ts +13 -4
- package/dist/automations/index.js +10 -2
- package/dist/automations/model.d.ts +10 -3
- package/dist/automations/model.js +15 -2
- package/dist/automations/model.js.map +1 -1
- package/dist/automations/notifications/debounce.d.ts +25 -0
- package/dist/automations/notifications/debounce.js +93 -0
- package/dist/automations/notifications/debounce.js.map +1 -0
- package/dist/automations/notifications/defaults.d.ts +8 -0
- package/dist/automations/notifications/defaults.js +316 -0
- package/dist/automations/notifications/defaults.js.map +1 -0
- package/dist/automations/notifications/delivery.d.ts +28 -0
- package/dist/automations/notifications/delivery.js +88 -0
- package/dist/automations/notifications/delivery.js.map +1 -0
- package/dist/automations/notifications/processor.d.ts +64 -0
- package/dist/automations/notifications/processor.js +270 -0
- package/dist/automations/notifications/processor.js.map +1 -0
- package/dist/automations/notifications/rules/content.d.ts +6 -0
- package/dist/automations/notifications/rules/content.js +39 -0
- package/dist/automations/notifications/rules/content.js.map +1 -0
- package/dist/automations/notifications/rules/discussion.d.ts +7 -0
- package/dist/automations/notifications/rules/discussion.js +678 -0
- package/dist/automations/notifications/rules/discussion.js.map +1 -0
- package/dist/automations/notifications/rules/entitlements.d.ts +6 -0
- package/dist/automations/notifications/rules/entitlements.js +249 -0
- package/dist/automations/notifications/rules/entitlements.js.map +1 -0
- package/dist/automations/notifications/rules/index.d.ts +6 -0
- package/dist/automations/notifications/rules/index.js +15 -0
- package/dist/automations/notifications/rules/index.js.map +1 -0
- package/dist/automations/notifications/rules/organizations.d.ts +6 -0
- package/dist/automations/notifications/rules/organizations.js +28 -0
- package/dist/automations/notifications/rules/organizations.js.map +1 -0
- package/dist/automations/notifications/rules/recipients.d.ts +32 -0
- package/dist/automations/notifications/rules/recipients.js +99 -0
- package/dist/automations/notifications/rules/recipients.js.map +1 -0
- package/dist/automations/notifications/service.d.ts +27 -0
- package/dist/automations/notifications/service.js +45 -0
- package/dist/automations/notifications/service.js.map +1 -0
- package/dist/automations/notifications/types.d.ts +7 -0
- package/dist/automations/notifications/types.js +473 -0
- package/dist/automations/notifications/types.js.map +1 -0
- package/dist/automations/ports.d.ts +121 -3
- package/dist/automations/schedule.d.ts +6 -0
- package/dist/automations/schedule.js +41 -0
- package/dist/automations/schedule.js.map +1 -0
- package/dist/automations/service.d.ts +20 -4
- package/dist/automations/service.js +246 -45
- package/dist/automations/service.js.map +1 -1
- package/dist/content/events.d.ts +712 -50
- package/dist/content/events.js +55 -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/model.d.ts +2 -2
- package/dist/content/notification-categories.d.ts +36 -0
- package/dist/content/notification-categories.js +64 -0
- package/dist/content/notification-categories.js.map +1 -0
- package/dist/content/ports.d.ts +90 -18
- package/dist/content/service.d.ts +57 -18
- package/dist/content/service.js +520 -25
- package/dist/content/service.js.map +1 -1
- package/dist/content/titles.d.ts +22 -0
- package/dist/content/titles.js +69 -0
- package/dist/content/titles.js.map +1 -0
- package/dist/content/types.d.ts +2 -2
- package/dist/discussion/authors.d.ts +10 -0
- package/dist/discussion/authors.js +23 -0
- package/dist/discussion/authors.js.map +1 -0
- package/dist/discussion/chats.d.ts +109 -0
- package/dist/discussion/chats.js +831 -0
- package/dist/discussion/chats.js.map +1 -0
- 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 +785 -56
- package/dist/discussion/events.js +165 -5
- package/dist/discussion/events.js.map +1 -1
- package/dist/discussion/index.d.ts +12 -3
- package/dist/discussion/index.js +9 -1
- package/dist/discussion/jobs.d.ts +16 -0
- package/dist/discussion/jobs.js +40 -0
- package/dist/discussion/jobs.js.map +1 -0
- package/dist/discussion/mentions.d.ts +11 -0
- package/dist/discussion/mentions.js +35 -0
- package/dist/discussion/mentions.js.map +1 -0
- package/dist/discussion/ports.d.ts +372 -28
- package/dist/discussion/ports.js +6 -1
- package/dist/discussion/ports.js.map +1 -0
- package/dist/discussion/posts.d.ts +113 -0
- package/dist/discussion/posts.js +775 -0
- package/dist/discussion/posts.js.map +1 -0
- package/dist/discussion/service.d.ts +42 -11
- package/dist/discussion/service.js +364 -85
- package/dist/discussion/service.js.map +1 -1
- package/dist/discussion/subscriptions.d.ts +37 -0
- package/dist/discussion/subscriptions.js +99 -0
- package/dist/discussion/subscriptions.js.map +1 -0
- package/dist/discussion/titles.d.ts +26 -0
- package/dist/discussion/titles.js +84 -0
- package/dist/discussion/titles.js.map +1 -0
- package/dist/entitlements/events.d.ts +178 -11
- package/dist/entitlements/events.js +28 -1
- package/dist/entitlements/events.js.map +1 -1
- package/dist/entitlements/index.d.ts +6 -4
- package/dist/entitlements/index.js +3 -1
- package/dist/entitlements/jobs.d.ts +12 -0
- package/dist/entitlements/jobs.js +18 -0
- package/dist/entitlements/jobs.js.map +1 -0
- package/dist/entitlements/model.d.ts +3 -2
- package/dist/entitlements/ports.d.ts +128 -11
- package/dist/entitlements/rules.d.ts +12 -0
- package/dist/entitlements/rules.js +17 -0
- package/dist/entitlements/rules.js.map +1 -0
- package/dist/entitlements/service.d.ts +57 -3
- package/dist/entitlements/service.js +492 -2
- package/dist/entitlements/service.js.map +1 -1
- package/dist/identity/access.d.ts +6 -0
- package/dist/identity/access.js +7 -0
- package/dist/identity/access.js.map +1 -0
- package/dist/identity/devices.d.ts +30 -0
- package/dist/identity/devices.js +107 -0
- package/dist/identity/devices.js.map +1 -0
- package/dist/identity/events.d.ts +2 -2
- package/dist/identity/index.d.ts +7 -4
- package/dist/identity/index.js +4 -1
- package/dist/identity/model.d.ts +2 -1
- package/dist/identity/notification-settings.d.ts +28 -0
- package/dist/identity/notification-settings.js +113 -0
- package/dist/identity/notification-settings.js.map +1 -0
- package/dist/identity/ports.d.ts +97 -3
- package/dist/identity/service.d.ts +14 -3
- package/dist/identity/service.js +62 -5
- package/dist/identity/service.js.map +1 -1
- package/dist/integrations/events.d.ts +3 -3
- package/dist/integrations/model.js +1 -1
- package/dist/integrations/model.js.map +1 -1
- package/dist/integrations/service.js +2 -2
- package/dist/integrations/service.js.map +1 -1
- package/dist/organizations/access-scopes.d.ts +19 -0
- package/dist/organizations/access-scopes.js +89 -0
- package/dist/organizations/access-scopes.js.map +1 -0
- package/dist/organizations/events.d.ts +75 -33
- package/dist/organizations/events.js +6 -0
- package/dist/organizations/events.js.map +1 -1
- package/dist/organizations/index.d.ts +8 -6
- package/dist/organizations/index.js +5 -3
- package/dist/organizations/members.d.ts +8 -5
- package/dist/organizations/members.js.map +1 -1
- package/dist/organizations/notification-categories.d.ts +32 -0
- package/dist/organizations/notification-categories.js +51 -0
- package/dist/organizations/notification-categories.js.map +1 -0
- package/dist/organizations/ports.d.ts +17 -11
- package/dist/organizations/roles.d.ts +6 -10
- package/dist/organizations/roles.js +19 -7
- package/dist/organizations/roles.js.map +1 -1
- package/dist/organizations/service.d.ts +22 -11
- package/dist/organizations/service.js +164 -47
- package/dist/organizations/service.js.map +1 -1
- package/dist/organizations/types.d.ts +1 -10
- package/dist/progress/events.d.ts +14 -14
- package/dist/progress/index.d.ts +2 -2
- package/dist/progress/ports.d.ts +6 -1
- package/dist/progress/service.d.ts +3 -1
- package/dist/progress/service.js +6 -2
- package/dist/progress/service.js.map +1 -1
- package/dist/reporting/admin/index.d.ts +4 -0
- package/dist/reporting/admin/index.js +2 -0
- package/dist/reporting/admin/model.d.ts +108 -0
- package/dist/reporting/admin/ports.d.ts +27 -0
- package/dist/reporting/admin/service.d.ts +22 -0
- package/dist/reporting/admin/service.js +38 -0
- package/dist/reporting/admin/service.js.map +1 -0
- package/dist/reporting/member/index.d.ts +5 -0
- package/dist/reporting/member/index.js +2 -0
- package/dist/reporting/member/model.d.ts +157 -0
- package/dist/reporting/member/ports.d.ts +48 -0
- package/dist/reporting/member/service.d.ts +92 -0
- package/dist/reporting/member/service.js +364 -0
- package/dist/reporting/member/service.js.map +1 -0
- package/dist/shared/actor.d.ts +9 -0
- package/dist/shared/errors.d.ts +19 -1
- package/dist/shared/errors.js +27 -1
- package/dist/shared/errors.js.map +1 -1
- package/dist/shared/id.d.ts +14 -0
- package/dist/shared/id.js +15 -1
- package/dist/shared/id.js.map +1 -1
- package/dist/shared/mailer.d.ts +15 -2
- package/dist/shared/mailer.js +32 -5
- package/dist/shared/mailer.js.map +1 -1
- package/dist/shared/network.d.ts +10 -0
- package/dist/shared/network.js +211 -0
- package/dist/shared/network.js.map +1 -0
- package/dist/shared/notification-categories.d.ts +16 -0
- package/dist/shared/notification-categories.js +94 -0
- package/dist/shared/notification-categories.js.map +1 -0
- package/dist/shared/notification-settings.d.ts +14 -0
- package/dist/shared/notification-settings.js +61 -0
- package/dist/shared/notification-settings.js.map +1 -0
- package/dist/shared/ports.d.ts +48 -2
- package/dist/shared/settings.d.ts +2 -1
- package/dist/shared/settings.js +1 -0
- package/dist/shared/settings.js.map +1 -1
- package/dist/types/assets.d.ts +2 -2
- package/dist/types/automations.d.ts +15 -1
- package/dist/types/content.d.ts +2 -2
- package/dist/types/discussion.d.ts +11 -3
- package/dist/types/email-templates.d.ts +216 -1
- package/dist/types/entitlements.d.ts +2 -2
- package/dist/types/identity.d.ts +48 -2
- package/dist/types/index.d.ts +9 -8
- package/dist/types/notifications.d.ts +157 -0
- package/dist/types/ports.d.ts +6 -0
- package/dist/types/schemas/assets.d.ts +65 -37
- package/dist/types/schemas/assets.js +17 -12
- package/dist/types/schemas/assets.js.map +1 -1
- package/dist/types/schemas/automations.d.ts +9 -1
- package/dist/types/schemas/automations.js +13 -6
- package/dist/types/schemas/automations.js.map +1 -1
- package/dist/types/schemas/content.d.ts +396 -3
- package/dist/types/schemas/content.js +161 -3
- package/dist/types/schemas/content.js.map +1 -1
- package/dist/types/schemas/discussion.d.ts +443 -3
- package/dist/types/schemas/discussion.js +233 -4
- package/dist/types/schemas/discussion.js.map +1 -1
- package/dist/types/schemas/entitlements.d.ts +36 -2
- package/dist/types/schemas/entitlements.js +23 -1
- package/dist/types/schemas/entitlements.js.map +1 -1
- package/dist/types/schemas/identity.d.ts +27 -1
- package/dist/types/schemas/identity.js +14 -2
- package/dist/types/schemas/identity.js.map +1 -1
- package/dist/types/schemas/index.d.ts +9 -8
- package/dist/types/schemas/index.js +9 -8
- package/dist/types/schemas/notifications.d.ts +95 -0
- package/dist/types/schemas/notifications.js +56 -0
- package/dist/types/schemas/notifications.js.map +1 -0
- package/dist/types/schemas/organizations.d.ts +8 -0
- package/dist/types/schemas/organizations.js +2 -0
- package/dist/types/schemas/organizations.js.map +1 -1
- package/dist/types/schemas/progress.d.ts +3 -3
- package/dist/types/schemas/shared.d.ts +3 -1
- package/dist/types/schemas/shared.js +5 -1
- package/dist/types/schemas/shared.js.map +1 -1
- package/package.json +2 -2
- package/dist/reporting/courses/index.d.ts +0 -4
- package/dist/reporting/courses/index.js +0 -2
- package/dist/reporting/courses/model.d.ts +0 -24
- package/dist/reporting/courses/ports.d.ts +0 -13
- package/dist/reporting/courses/service.d.ts +0 -19
- package/dist/reporting/courses/service.js +0 -20
- package/dist/reporting/courses/service.js.map +0 -1
- package/dist/reporting/dashboard/index.d.ts +0 -4
- package/dist/reporting/dashboard/index.js +0 -2
- package/dist/reporting/dashboard/model.d.ts +0 -14
- package/dist/reporting/dashboard/ports.d.ts +0 -13
- package/dist/reporting/dashboard/service.d.ts +0 -19
- package/dist/reporting/dashboard/service.js +0 -20
- package/dist/reporting/dashboard/service.js.map +0 -1
- package/dist/reporting/learn/index.d.ts +0 -5
- package/dist/reporting/learn/index.js +0 -2
- package/dist/reporting/learn/model.d.ts +0 -23
- package/dist/reporting/learn/ports.d.ts +0 -43
- package/dist/reporting/learn/service.d.ts +0 -49
- package/dist/reporting/learn/service.js +0 -116
- package/dist/reporting/learn/service.js.map +0 -1
- package/dist/reporting/students/index.d.ts +0 -4
- package/dist/reporting/students/index.js +0 -2
- package/dist/reporting/students/model.d.ts +0 -51
- package/dist/reporting/students/model.js +0 -1
- package/dist/reporting/students/ports.d.ts +0 -15
- package/dist/reporting/students/ports.js +0 -1
- package/dist/reporting/students/service.d.ts +0 -20
- package/dist/reporting/students/service.js +0 -23
- package/dist/reporting/students/service.js.map +0 -1
- /package/dist/reporting/{courses → admin}/model.js +0 -0
- /package/dist/reporting/{courses → admin}/ports.js +0 -0
- /package/dist/reporting/{dashboard → member}/model.js +0 -0
- /package/dist/reporting/{dashboard → member}/ports.js +0 -0
- /package/dist/{reporting/learn/model.js → shared/actor.js} +0 -0
- /package/dist/{reporting/learn/ports.js → types/notifications.js} +0 -0
|
@@ -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 { MESSAGE_EXCERPT, POST_EXCERPT, previousStatusOf } 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, staff] = await Promise.all([\n reads.courseHolders(orgId, courseId),\n reads.staffIds(orgId),\n ]);\n const open = new Set([...holders, ...staff]);\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 actorOrgUserId: 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 actorOrgUserId,\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 actorOrgUserId,\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 actorOrgUserId: 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(reads, orgId, post.body || post.title, POST_EXCERPT);\n lists.push(\n ...(await mentionsOf(\n reads,\n orgId,\n {\n ...base,\n actorOrgUserId: 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 actorOrgUserId: 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 actorOrgUserId: 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 reads.moderationAudience(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 actorOrgUserId: 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(reads, orgId, post.body || post.title, POST_EXCERPT),\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 actorOrgUserId: 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 actorOrgUserId: 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 actorOrgUserId: 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(reads, orgId, post.body || post.title, POST_EXCERPT),\n reason: flag.reason,\n },\n },\n await recipientsOf(\n reads,\n orgId,\n new Reasons().add(await reads.moderationAudience(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 reads.moderationAudience(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(reads, orgId, new Reasons().add(await reads.staffIds(orgId), 'staff'), {\n exclude: [actor],\n 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 actorOrgUserId: 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 actorOrgUserId: 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 actorOrgUserId: 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 actorOrgUserId: 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(reads, orgId, new Reasons().add(await reads.staffIds(orgId), 'staff'), {\n exclude: [actorOr(event, flag.orgUserId)],\n 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 actorOrgUserId: 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 actorOrgUserId: 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 actorOrgUserId: 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,249 @@
|
|
|
1
|
+
import { entitlementEvents } from "../../../entitlements/events.js";
|
|
2
|
+
import { previousStatusOf } from "../../community-mail.js";
|
|
3
|
+
import { Reasons, actorOr, eventAt, listOf, recipientsOf } from "./recipients.js";
|
|
4
|
+
//#region src/automations/notifications/rules/entitlements.ts
|
|
5
|
+
function seatsLeft(capacity, active) {
|
|
6
|
+
return capacity == null ? "No cap" : String(Math.max(capacity - active, 0));
|
|
7
|
+
}
|
|
8
|
+
function subjectOnly(event, reads, orgUserId) {
|
|
9
|
+
return recipientsOf(reads, event.orgId, new Reasons().add([orgUserId], "subject"), {
|
|
10
|
+
exclude: [actorOr(event, null)],
|
|
11
|
+
at: eventAt(event)
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
async function grantSubject(reads, orgId, grant) {
|
|
15
|
+
if (grant.bundleId) {
|
|
16
|
+
const info = await reads.contentInfo(orgId, { bundleId: grant.bundleId });
|
|
17
|
+
return info ? {
|
|
18
|
+
ref: {
|
|
19
|
+
type: "bundle",
|
|
20
|
+
id: grant.bundleId
|
|
21
|
+
},
|
|
22
|
+
...info
|
|
23
|
+
} : null;
|
|
24
|
+
}
|
|
25
|
+
if (!grant.contentId) return null;
|
|
26
|
+
const info = await reads.contentInfo(orgId, { contentId: grant.contentId });
|
|
27
|
+
if (!info) return null;
|
|
28
|
+
return {
|
|
29
|
+
ref: {
|
|
30
|
+
type: info.type === "download" ? "download" : info.type === "community" ? "community" : "course",
|
|
31
|
+
id: grant.contentId
|
|
32
|
+
},
|
|
33
|
+
...info
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
async function membershipEnded(event, reads, grant, ref) {
|
|
37
|
+
const orgId = event.orgId;
|
|
38
|
+
const communityIds = await reads.communitiesOpenedBy(orgId, grant);
|
|
39
|
+
if (communityIds.length === 0) return null;
|
|
40
|
+
const held = new Set((await reads.memberCommunityTitles(orgId, grant.orgUserId)).map((community) => community.id));
|
|
41
|
+
const communities = [];
|
|
42
|
+
for (const communityId of communityIds.filter((id) => !held.has(id))) {
|
|
43
|
+
const title = await reads.communityTitle(orgId, communityId);
|
|
44
|
+
if (title !== null) communities.push({
|
|
45
|
+
communityId,
|
|
46
|
+
communityTitle: title
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
if (communities.length === 0) return null;
|
|
50
|
+
return listOf({
|
|
51
|
+
type: "community.membership_ended",
|
|
52
|
+
scope: "org",
|
|
53
|
+
actorOrgUserId: actorOr(event, null),
|
|
54
|
+
communityId: null,
|
|
55
|
+
spaceId: null,
|
|
56
|
+
subject: ref,
|
|
57
|
+
parent: null,
|
|
58
|
+
display: { communities }
|
|
59
|
+
}, await subjectOnly(event, reads, grant.orgUserId));
|
|
60
|
+
}
|
|
61
|
+
async function spaceBase(reads, orgId, member, type, actorOrgUserId) {
|
|
62
|
+
const space = await reads.space(orgId, member.spaceId);
|
|
63
|
+
return space ? {
|
|
64
|
+
space,
|
|
65
|
+
base: {
|
|
66
|
+
type,
|
|
67
|
+
scope: "space",
|
|
68
|
+
actorOrgUserId,
|
|
69
|
+
communityId: member.communityId,
|
|
70
|
+
spaceId: member.spaceId,
|
|
71
|
+
subject: {
|
|
72
|
+
type: "space",
|
|
73
|
+
id: member.spaceId
|
|
74
|
+
},
|
|
75
|
+
parent: null
|
|
76
|
+
}
|
|
77
|
+
} : null;
|
|
78
|
+
}
|
|
79
|
+
const spaceMemberCreated = async (event, reads) => {
|
|
80
|
+
const parsed = entitlementEvents.spaceMemberCreated.safeParse(event);
|
|
81
|
+
if (!parsed.success) return [];
|
|
82
|
+
const member = parsed.data.data;
|
|
83
|
+
const orgId = event.orgId;
|
|
84
|
+
if (member.status === "requested") {
|
|
85
|
+
const found = await spaceBase(reads, orgId, member, "space.join_requested", member.orgUserId);
|
|
86
|
+
if (!found) return [];
|
|
87
|
+
const active = await reads.activeMemberCount(orgId, member.spaceId);
|
|
88
|
+
return listOf({
|
|
89
|
+
...found.base,
|
|
90
|
+
display: {
|
|
91
|
+
spaceName: found.space.name,
|
|
92
|
+
spaceId: member.spaceId,
|
|
93
|
+
memberName: await reads.displayName(orgId, member.orgUserId),
|
|
94
|
+
seatsLeft: seatsLeft(found.space.capacity, active)
|
|
95
|
+
}
|
|
96
|
+
}, await recipientsOf(reads, orgId, new Reasons().add(await reads.moderationAudience(orgId, member.communityId), "moderator"), {
|
|
97
|
+
exclude: [member.orgUserId, actorOr(event, null)],
|
|
98
|
+
at: eventAt(event),
|
|
99
|
+
canOpen: (orgUserIds) => reads.spaceOpenersAmong(orgId, member.spaceId, orgUserIds)
|
|
100
|
+
}));
|
|
101
|
+
}
|
|
102
|
+
if (member.status === "invited") {
|
|
103
|
+
const inviter = actorOr(event, null);
|
|
104
|
+
const found = await spaceBase(reads, orgId, member, "space.invited", inviter);
|
|
105
|
+
if (!found) return [];
|
|
106
|
+
return listOf({
|
|
107
|
+
...found.base,
|
|
108
|
+
display: {
|
|
109
|
+
spaceName: found.space.name,
|
|
110
|
+
spaceId: member.spaceId,
|
|
111
|
+
inviterName: inviter ? await reads.displayName(orgId, inviter) : "A member"
|
|
112
|
+
}
|
|
113
|
+
}, await subjectOnly(event, reads, member.orgUserId));
|
|
114
|
+
}
|
|
115
|
+
return [];
|
|
116
|
+
};
|
|
117
|
+
const spaceMemberUpdated = async (event, reads) => {
|
|
118
|
+
const parsed = entitlementEvents.spaceMemberUpdated.safeParse(event);
|
|
119
|
+
if (!parsed.success) return [];
|
|
120
|
+
const member = parsed.data.data;
|
|
121
|
+
if (member.status !== "active" || previousStatusOf(event) !== "requested") return [];
|
|
122
|
+
const found = await spaceBase(reads, event.orgId, member, "space.request_approved", actorOr(event, null));
|
|
123
|
+
return found ? listOf({
|
|
124
|
+
...found.base,
|
|
125
|
+
display: {
|
|
126
|
+
spaceName: found.space.name,
|
|
127
|
+
spaceId: member.spaceId
|
|
128
|
+
}
|
|
129
|
+
}, await subjectOnly(event, reads, member.orgUserId)) : [];
|
|
130
|
+
};
|
|
131
|
+
const spaceMemberDeleted = async (event, reads) => {
|
|
132
|
+
const parsed = entitlementEvents.spaceMemberDeleted.safeParse(event);
|
|
133
|
+
if (!parsed.success || previousStatusOf(event) !== "requested") return [];
|
|
134
|
+
const member = parsed.data.data;
|
|
135
|
+
const found = await spaceBase(reads, event.orgId, member, "space.request_declined", actorOr(event, null));
|
|
136
|
+
return found ? listOf({
|
|
137
|
+
...found.base,
|
|
138
|
+
display: {
|
|
139
|
+
spaceName: found.space.name,
|
|
140
|
+
spaceId: member.spaceId
|
|
141
|
+
}
|
|
142
|
+
}, await subjectOnly(event, reads, member.orgUserId)) : [];
|
|
143
|
+
};
|
|
144
|
+
const entitlementCreated = async (event, reads) => {
|
|
145
|
+
const parsed = entitlementEvents.entitlementCreated.safeParse(event);
|
|
146
|
+
if (!parsed.success) return [];
|
|
147
|
+
const grant = parsed.data.data;
|
|
148
|
+
const orgId = event.orgId;
|
|
149
|
+
const actor = actorOr(event, null);
|
|
150
|
+
const subject = await subjectOnly(event, reads, grant.orgUserId);
|
|
151
|
+
if (subject.length === 0) return [];
|
|
152
|
+
const lists = [];
|
|
153
|
+
const target = await grantSubject(reads, orgId, grant);
|
|
154
|
+
if (target) lists.push(...listOf({
|
|
155
|
+
type: "access.granted",
|
|
156
|
+
scope: "org",
|
|
157
|
+
actorOrgUserId: actor,
|
|
158
|
+
communityId: null,
|
|
159
|
+
spaceId: null,
|
|
160
|
+
subject: target.ref,
|
|
161
|
+
parent: null,
|
|
162
|
+
display: {
|
|
163
|
+
contentTitle: target.title,
|
|
164
|
+
contentType: target.type
|
|
165
|
+
}
|
|
166
|
+
}, subject));
|
|
167
|
+
for (const communityId of await reads.communitiesOpenedBy(orgId, grant)) {
|
|
168
|
+
const title = await reads.communityTitle(orgId, communityId);
|
|
169
|
+
if (title === null) continue;
|
|
170
|
+
lists.push(...listOf({
|
|
171
|
+
type: "community.welcome",
|
|
172
|
+
scope: "community",
|
|
173
|
+
actorOrgUserId: actor,
|
|
174
|
+
communityId,
|
|
175
|
+
spaceId: null,
|
|
176
|
+
subject: {
|
|
177
|
+
type: "community",
|
|
178
|
+
id: communityId
|
|
179
|
+
},
|
|
180
|
+
parent: null,
|
|
181
|
+
display: {
|
|
182
|
+
communityTitle: title,
|
|
183
|
+
spaceNames: await reads.openSpaceNames(orgId, grant.orgUserId, communityId)
|
|
184
|
+
}
|
|
185
|
+
}, subject));
|
|
186
|
+
}
|
|
187
|
+
return lists;
|
|
188
|
+
};
|
|
189
|
+
const entitlementEndingSoon = async (event, reads) => {
|
|
190
|
+
const parsed = entitlementEvents.entitlementEndingSoon.safeParse(event);
|
|
191
|
+
if (!parsed.success) return [];
|
|
192
|
+
const grant = parsed.data.data;
|
|
193
|
+
const title = await reads.communityTitle(event.orgId, grant.communityId);
|
|
194
|
+
if (title === null) return [];
|
|
195
|
+
return listOf({
|
|
196
|
+
type: "community.ending_soon",
|
|
197
|
+
scope: "community",
|
|
198
|
+
actorOrgUserId: null,
|
|
199
|
+
communityId: grant.communityId,
|
|
200
|
+
spaceId: null,
|
|
201
|
+
subject: {
|
|
202
|
+
type: "community",
|
|
203
|
+
id: grant.communityId
|
|
204
|
+
},
|
|
205
|
+
parent: null,
|
|
206
|
+
display: {
|
|
207
|
+
communityTitle: title,
|
|
208
|
+
expiresAt: grant.expiresAt ? grant.expiresAt.toISOString() : ""
|
|
209
|
+
}
|
|
210
|
+
}, await subjectOnly(event, reads, grant.orgUserId));
|
|
211
|
+
};
|
|
212
|
+
const entitlementDeleted = async (event, reads) => {
|
|
213
|
+
const parsed = entitlementEvents.entitlementDeleted.safeParse(event);
|
|
214
|
+
if (!parsed.success) return [];
|
|
215
|
+
const grant = parsed.data.data;
|
|
216
|
+
const target = await grantSubject(reads, event.orgId, grant);
|
|
217
|
+
const ended = await membershipEnded(event, reads, grant, target?.ref ?? null);
|
|
218
|
+
if (ended) return ended;
|
|
219
|
+
if (!target || (await reads.contentLostBy(event.orgId, grant)).length === 0) return [];
|
|
220
|
+
return listOf({
|
|
221
|
+
type: "access.revoked",
|
|
222
|
+
scope: "org",
|
|
223
|
+
actorOrgUserId: actorOr(event, null),
|
|
224
|
+
communityId: null,
|
|
225
|
+
spaceId: null,
|
|
226
|
+
subject: target.ref,
|
|
227
|
+
parent: null,
|
|
228
|
+
display: { contentTitle: target.title }
|
|
229
|
+
}, await subjectOnly(event, reads, grant.orgUserId));
|
|
230
|
+
};
|
|
231
|
+
const entitlementExpired = async (event, reads) => {
|
|
232
|
+
const parsed = entitlementEvents.entitlementExpired.safeParse(event);
|
|
233
|
+
if (!parsed.success) return [];
|
|
234
|
+
const grant = parsed.data.data;
|
|
235
|
+
return await membershipEnded(event, reads, grant, (await grantSubject(reads, event.orgId, grant))?.ref ?? null) ?? [];
|
|
236
|
+
};
|
|
237
|
+
const ENTITLEMENT_RULES = {
|
|
238
|
+
"entitlement.space_member.created": spaceMemberCreated,
|
|
239
|
+
"entitlement.space_member.updated": spaceMemberUpdated,
|
|
240
|
+
"entitlement.space_member.deleted": spaceMemberDeleted,
|
|
241
|
+
"entitlement.created": entitlementCreated,
|
|
242
|
+
"entitlement.ending_soon": entitlementEndingSoon,
|
|
243
|
+
"entitlement.deleted": entitlementDeleted,
|
|
244
|
+
"entitlement.expired": entitlementExpired
|
|
245
|
+
};
|
|
246
|
+
//#endregion
|
|
247
|
+
export { ENTITLEMENT_RULES };
|
|
248
|
+
|
|
249
|
+
//# 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 { previousStatusOf } from '../../community-mail.js';\nimport type { NotificationReads, RecipientRule } from '../../ports.js';\nimport { actorOr, eventAt, listOf, Reasons, recipientsOf } from './recipients.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 actorOrgUserId: 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 actorOrgUserId: 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 actorOrgUserId,\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 reads.moderationAudience(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\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 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 actorOrgUserId: 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 actorOrgUserId: 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 actorOrgUserId: 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 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 actorOrgUserId: 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,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,28 @@
|
|
|
1
|
+
import { organizationEvents } from "../../../organizations/events.js";
|
|
2
|
+
import { Reasons, actorOr, eventAt, listOf, recipientsOf } from "./recipients.js";
|
|
3
|
+
//#region src/automations/notifications/rules/organizations.ts
|
|
4
|
+
const orgUserUpdated = async (event, reads) => {
|
|
5
|
+
const parsed = organizationEvents.orgUserUpdated.safeParse(event);
|
|
6
|
+
if (!parsed.success || parsed.data.data.role !== "moderator") return [];
|
|
7
|
+
const orgUser = parsed.data.data;
|
|
8
|
+
const orgId = event.orgId;
|
|
9
|
+
const communities = await reads.memberCommunityTitles(orgId, orgUser.id);
|
|
10
|
+
return listOf({
|
|
11
|
+
type: "moderator.assigned",
|
|
12
|
+
scope: "org",
|
|
13
|
+
actorOrgUserId: actorOr(event, null),
|
|
14
|
+
communityId: null,
|
|
15
|
+
spaceId: null,
|
|
16
|
+
subject: null,
|
|
17
|
+
parent: null,
|
|
18
|
+
display: { communityTitles: communities.map((community) => community.title) }
|
|
19
|
+
}, await recipientsOf(reads, orgId, new Reasons().add([orgUser.id], "subject"), {
|
|
20
|
+
exclude: [actorOr(event, null)],
|
|
21
|
+
at: eventAt(event)
|
|
22
|
+
}));
|
|
23
|
+
};
|
|
24
|
+
const ORGANIZATION_RULES = { "organization.user.updated": orgUserUpdated };
|
|
25
|
+
//#endregion
|
|
26
|
+
export { ORGANIZATION_RULES };
|
|
27
|
+
|
|
28
|
+
//# 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 type { RecipientRule } from '../../ports.js';\nimport { actorOr, eventAt, listOf, Reasons, recipientsOf } from './recipients.js';\n\nconst orgUserUpdated: RecipientRule = async (event, reads) => {\n const parsed = organizationEvents.orgUserUpdated.safeParse(event);\n if (!parsed.success || parsed.data.data.role !== 'moderator') {\n return [];\n }\n const orgUser = parsed.data.data;\n const orgId = event.orgId;\n const communities = await reads.memberCommunityTitles(orgId, orgUser.id);\n return listOf(\n {\n type: 'moderator.assigned',\n scope: 'org',\n actorOrgUserId: actorOr(event, null),\n communityId: null,\n spaceId: null,\n subject: null,\n parent: null,\n display: { communityTitles: communities.map((community) => community.title) },\n },\n await recipientsOf(reads, orgId, new Reasons().add([orgUser.id], 'subject'), {\n exclude: [actorOr(event, null)],\n at: eventAt(event),\n }),\n );\n};\n\nexport const ORGANIZATION_RULES: Record<string, RecipientRule> = {\n 'organization.user.updated': orgUserUpdated,\n};\n"],"mappings":";;;AAIA,MAAM,iBAAgC,OAAO,OAAO,UAAU;CAC5D,MAAM,SAAS,mBAAmB,eAAe,UAAU,KAAK;CAChE,IAAI,CAAC,OAAO,WAAW,OAAO,KAAK,KAAK,SAAS,aAC/C,OAAO,CAAC;CAEV,MAAM,UAAU,OAAO,KAAK;CAC5B,MAAM,QAAQ,MAAM;CACpB,MAAM,cAAc,MAAM,MAAM,sBAAsB,OAAO,QAAQ,EAAE;CACvE,OAAO,OACL;EACE,MAAM;EACN,OAAO;EACP,gBAAgB,QAAQ,OAAO,IAAI;EACnC,aAAa;EACb,SAAS;EACT,SAAS;EACT,QAAQ;EACR,SAAS,EAAE,iBAAiB,YAAY,KAAK,cAAc,UAAU,KAAK,EAAE;CAC9E,GACA,MAAM,aAAa,OAAO,OAAO,IAAI,QAAQ,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,SAAS,GAAG;EAC3E,SAAS,CAAC,QAAQ,OAAO,IAAI,CAAC;EAC9B,IAAI,QAAQ,KAAK;CACnB,CAAC,CACH;AACF;AAEA,MAAa,qBAAoD,EAC/D,6BAA6B,eAC/B"}
|
|
@@ -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
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { nameMentions } from "../../../discussion/mentions.js";
|
|
2
|
+
import { actorOf, excerpt } from "../../community-mail.js";
|
|
3
|
+
//#region src/automations/notifications/rules/recipients.ts
|
|
4
|
+
const MUTE_PROOF = [
|
|
5
|
+
"subject",
|
|
6
|
+
"moderator",
|
|
7
|
+
"staff",
|
|
8
|
+
"mentioned"
|
|
9
|
+
];
|
|
10
|
+
var Reasons = class {
|
|
11
|
+
byPerson = /* @__PURE__ */ new Map();
|
|
12
|
+
add(orgUserIds, reason) {
|
|
13
|
+
for (const orgUserId of orgUserIds) {
|
|
14
|
+
if (!orgUserId) continue;
|
|
15
|
+
const reasons = this.byPerson.get(orgUserId) ?? [];
|
|
16
|
+
if (!reasons.includes(reason)) reasons.push(reason);
|
|
17
|
+
this.byPerson.set(orgUserId, reasons);
|
|
18
|
+
}
|
|
19
|
+
return this;
|
|
20
|
+
}
|
|
21
|
+
ids() {
|
|
22
|
+
return [...this.byPerson.keys()];
|
|
23
|
+
}
|
|
24
|
+
entries() {
|
|
25
|
+
return [...this.byPerson.entries()];
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
async function recipientsOf(reads, orgId, reasons, reach) {
|
|
29
|
+
const excluded = new Set(reach.exclude.filter((id) => Boolean(id)));
|
|
30
|
+
let people = new Map(reasons.entries().filter(([orgUserId]) => !excluded.has(orgUserId)));
|
|
31
|
+
if (people.size > 0 && reach.canOpen) {
|
|
32
|
+
const open = new Set(await reach.canOpen([...people.keys()]));
|
|
33
|
+
people = new Map([...people].filter(([orgUserId]) => open.has(orgUserId)));
|
|
34
|
+
}
|
|
35
|
+
for (const item of reach.mutedOn ?? []) {
|
|
36
|
+
const exposed = [...people].filter(([, held]) => held.some((reason) => !MUTE_PROOF.includes(reason))).map(([orgUserId]) => orgUserId);
|
|
37
|
+
if (exposed.length === 0) break;
|
|
38
|
+
for (const orgUserId of await reads.mutedAmong(orgId, item, exposed, reach.at)) {
|
|
39
|
+
const kept = (people.get(orgUserId) ?? []).filter((reason) => MUTE_PROOF.includes(reason));
|
|
40
|
+
if (kept.length > 0) people.set(orgUserId, kept);
|
|
41
|
+
else people.delete(orgUserId);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (people.size === 0) return [];
|
|
45
|
+
const userIds = await reads.userIdsOf(orgId, [...people.keys()]);
|
|
46
|
+
return [...people].flatMap(([orgUserId, held]) => {
|
|
47
|
+
const userId = userIds[orgUserId];
|
|
48
|
+
return userId ? [{
|
|
49
|
+
orgUserId,
|
|
50
|
+
userId,
|
|
51
|
+
reasons: held
|
|
52
|
+
}] : [];
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function listOf(base, recipients) {
|
|
56
|
+
return recipients.length > 0 ? [{
|
|
57
|
+
...base,
|
|
58
|
+
recipients
|
|
59
|
+
}] : [];
|
|
60
|
+
}
|
|
61
|
+
function eventAt(event) {
|
|
62
|
+
return new Date(event.occurredAt);
|
|
63
|
+
}
|
|
64
|
+
function actorOr(event, fallback) {
|
|
65
|
+
return actorOf(event) ?? fallback;
|
|
66
|
+
}
|
|
67
|
+
async function textExcerpt(reads, orgId, text, max) {
|
|
68
|
+
return excerpt(await nameMentions(text, (orgUserId) => reads.displayName(orgId, orgUserId)), max);
|
|
69
|
+
}
|
|
70
|
+
async function participantNames(reads, orgId, chat) {
|
|
71
|
+
const names = [];
|
|
72
|
+
for (const participant of chat.participants) if (!participant.leftAt) names.push(await reads.displayName(orgId, participant.orgUserId));
|
|
73
|
+
return names;
|
|
74
|
+
}
|
|
75
|
+
async function chatTitle(reads, orgId, chat, senderOrgUserId) {
|
|
76
|
+
if (chat.kind === "direct") return reads.displayName(orgId, senderOrgUserId);
|
|
77
|
+
const names = await participantNames(reads, orgId, chat);
|
|
78
|
+
return names.length > 0 ? names.join(", ") : "Group chat";
|
|
79
|
+
}
|
|
80
|
+
function chatReach(reads, orgId, chat, chatId, actor, at) {
|
|
81
|
+
return {
|
|
82
|
+
exclude: [actor],
|
|
83
|
+
at,
|
|
84
|
+
mutedOn: [{
|
|
85
|
+
type: "chat",
|
|
86
|
+
id: chatId
|
|
87
|
+
}],
|
|
88
|
+
canOpen: async (orgUserIds) => {
|
|
89
|
+
const present = new Set(chat.participants.filter((participant) => participant.leftAt === null).map((participant) => participant.orgUserId));
|
|
90
|
+
const unblocked = [];
|
|
91
|
+
for (const orgUserId of orgUserIds) if (present.has(orgUserId) && !await reads.pairBlocked(orgId, chat.communityId, actor, orgUserId)) unblocked.push(orgUserId);
|
|
92
|
+
return unblocked.length > 0 ? reads.communityMembersAmong(orgId, chat.communityId, unblocked) : [];
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
//#endregion
|
|
97
|
+
export { Reasons, actorOr, chatReach, chatTitle, eventAt, listOf, participantNames, recipientsOf, textExcerpt };
|
|
98
|
+
|
|
99
|
+
//# sourceMappingURL=recipients.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recipients.js","names":[],"sources":["../../../../src/automations/notifications/rules/recipients.ts"],"sourcesContent":["import { nameMentions } from '../../../discussion/index.js';\nimport type { DomainEvent } from '../../../shared/ports.js';\nimport type { Reason, RecipientList, SubjectRef } from '../../../types/index.js';\nimport { actorOf, excerpt } from '../../community-mail.js';\nimport type { NotificationReads } from '../../ports.js';\n\nexport type Recipient = NonNullable<RecipientList['recipients']>[number];\nexport type ListBase = Omit<RecipientList, 'recipients' | 'audience'>;\nexport type Chat = NonNullable<Awaited<ReturnType<NotificationReads['chat']>>>;\n\nconst MUTE_PROOF: Reason[] = ['subject', 'moderator', 'staff', 'mentioned'];\n\nexport class Reasons {\n private readonly byPerson = new Map<string, Reason[]>();\n\n add(orgUserIds: (string | null | undefined)[], reason: Reason): this {\n for (const orgUserId of orgUserIds) {\n if (!orgUserId) {\n continue;\n }\n const reasons = this.byPerson.get(orgUserId) ?? [];\n if (!reasons.includes(reason)) {\n reasons.push(reason);\n }\n this.byPerson.set(orgUserId, reasons);\n }\n return this;\n }\n\n ids(): string[] {\n return [...this.byPerson.keys()];\n }\n\n entries(): [string, Reason[]][] {\n return [...this.byPerson.entries()];\n }\n}\n\nexport interface Reach {\n exclude: (string | null | undefined)[];\n canOpen?: (orgUserIds: string[]) => Promise<string[]>;\n mutedOn?: SubjectRef[];\n at: Date;\n}\n\nexport async function recipientsOf(\n reads: NotificationReads,\n orgId: string,\n reasons: Reasons,\n reach: Reach,\n): Promise<Recipient[]> {\n const excluded = new Set(reach.exclude.filter((id): id is string => Boolean(id)));\n let people = new Map(reasons.entries().filter(([orgUserId]) => !excluded.has(orgUserId)));\n if (people.size > 0 && reach.canOpen) {\n const open = new Set(await reach.canOpen([...people.keys()]));\n people = new Map([...people].filter(([orgUserId]) => open.has(orgUserId)));\n }\n for (const item of reach.mutedOn ?? []) {\n const exposed = [...people]\n .filter(([, held]) => held.some((reason) => !MUTE_PROOF.includes(reason)))\n .map(([orgUserId]) => orgUserId);\n if (exposed.length === 0) {\n break;\n }\n for (const orgUserId of await reads.mutedAmong(orgId, item, exposed, reach.at)) {\n const kept = (people.get(orgUserId) ?? []).filter((reason) => MUTE_PROOF.includes(reason));\n if (kept.length > 0) {\n people.set(orgUserId, kept);\n } else {\n people.delete(orgUserId);\n }\n }\n }\n if (people.size === 0) {\n return [];\n }\n const userIds = await reads.userIdsOf(orgId, [...people.keys()]);\n return [...people].flatMap(([orgUserId, held]) => {\n const userId = userIds[orgUserId];\n return userId ? [{ orgUserId, userId, reasons: held }] : [];\n });\n}\n\nexport function listOf(base: ListBase, recipients: Recipient[]): RecipientList[] {\n return recipients.length > 0 ? [{ ...base, recipients }] : [];\n}\n\nexport function eventAt(event: DomainEvent): Date {\n return new Date(event.occurredAt);\n}\n\nexport function actorOr(event: DomainEvent, fallback: string | null): string | null {\n return actorOf(event) ?? fallback;\n}\n\nexport async function textExcerpt(\n reads: NotificationReads,\n orgId: string,\n text: string,\n max: number,\n): Promise<string> {\n return excerpt(await nameMentions(text, (orgUserId) => reads.displayName(orgId, orgUserId)), max);\n}\n\nexport async function participantNames(\n reads: NotificationReads,\n orgId: string,\n chat: Chat,\n): Promise<string[]> {\n const names: string[] = [];\n for (const participant of chat.participants) {\n if (!participant.leftAt) {\n names.push(await reads.displayName(orgId, participant.orgUserId));\n }\n }\n return names;\n}\n\nexport async function chatTitle(\n reads: NotificationReads,\n orgId: string,\n chat: Chat,\n senderOrgUserId: string,\n): Promise<string> {\n if (chat.kind === 'direct') {\n return reads.displayName(orgId, senderOrgUserId);\n }\n const names = await participantNames(reads, orgId, chat);\n return names.length > 0 ? names.join(', ') : 'Group chat';\n}\n\nexport function chatReach(\n reads: NotificationReads,\n orgId: string,\n chat: Chat,\n chatId: string,\n actor: string,\n at: Date,\n): Reach {\n return {\n exclude: [actor],\n at,\n mutedOn: [{ type: 'chat', id: chatId }],\n canOpen: async (orgUserIds) => {\n const present = new Set(\n chat.participants\n .filter((participant) => participant.leftAt === null)\n .map((participant) => participant.orgUserId),\n );\n const unblocked: string[] = [];\n for (const orgUserId of orgUserIds) {\n if (\n present.has(orgUserId) &&\n !(await reads.pairBlocked(orgId, chat.communityId, actor, orgUserId))\n ) {\n unblocked.push(orgUserId);\n }\n }\n return unblocked.length > 0\n ? reads.communityMembersAmong(orgId, chat.communityId, unblocked)\n : [];\n },\n };\n}\n"],"mappings":"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"}
|