@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
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { organizationEvents } from "../organizations/events.js";
|
|
2
1
|
import { entitlementEvents } from "../entitlements/events.js";
|
|
3
2
|
import { discussionEvents } from "../discussion/events.js";
|
|
4
|
-
import {
|
|
3
|
+
import { organizationEvents } from "../organizations/events.js";
|
|
4
|
+
import { MESSAGE_BATCH_MS, actorOf, address, communityModerators, excerpt, previousStatusOf, roleGain } from "./community-mail.js";
|
|
5
5
|
//#region src/automations/community-derivations.ts
|
|
6
6
|
function silence(triggers) {
|
|
7
7
|
return {
|
|
@@ -80,10 +80,10 @@ const COMMUNITY_DERIVATIONS = {
|
|
|
80
80
|
for (const communityId of communityIds) {
|
|
81
81
|
const title = await communityTitle(deps, event.orgId, communityId);
|
|
82
82
|
if (!title) return;
|
|
83
|
-
const addressed = await address(deps, event, event.orgId, [grant.orgUserId], {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
});
|
|
83
|
+
const addressed = await address(deps, event, event.orgId, [grant.orgUserId], { communities: [{
|
|
84
|
+
communityId,
|
|
85
|
+
communityTitle: title
|
|
86
|
+
}] });
|
|
87
87
|
if (!addressed) return;
|
|
88
88
|
recipients.push(...addressed);
|
|
89
89
|
}
|
|
@@ -103,7 +103,7 @@ const COMMUNITY_DERIVATIONS = {
|
|
|
103
103
|
spaceName: space.name,
|
|
104
104
|
postTitle: post.title,
|
|
105
105
|
authorName: await deps.mail.displayName(event.orgId, post.orgUserId),
|
|
106
|
-
excerpt: excerpt(post.body || post.title, 280),
|
|
106
|
+
excerpt: excerpt(deps.mail.postText(post.body) || post.title, 280),
|
|
107
107
|
postId: post.id,
|
|
108
108
|
communityId: post.communityId
|
|
109
109
|
});
|
|
@@ -213,7 +213,7 @@ const COMMUNITY_DERIVATIONS = {
|
|
|
213
213
|
if (post.status !== "pending" || previousStatusOf(event) === "pending") return [];
|
|
214
214
|
const space = await deps.mail.space(event.orgId, post.spaceId);
|
|
215
215
|
if (!space) return;
|
|
216
|
-
const audience = await deps.mail
|
|
216
|
+
const audience = await communityModerators(deps.mail, event.orgId, post.communityId);
|
|
217
217
|
return address(deps, event, event.orgId, audience, {
|
|
218
218
|
spaceName: space.name,
|
|
219
219
|
postTitle: post.title,
|
|
@@ -233,7 +233,7 @@ const COMMUNITY_DERIVATIONS = {
|
|
|
233
233
|
const space = await deps.mail.space(event.orgId, member.spaceId);
|
|
234
234
|
if (!space) return;
|
|
235
235
|
const active = await deps.mail.activeMemberCount(event.orgId, member.spaceId);
|
|
236
|
-
const audience = await deps.mail
|
|
236
|
+
const audience = await communityModerators(deps.mail, event.orgId, member.communityId);
|
|
237
237
|
return address(deps, event, event.orgId, audience, {
|
|
238
238
|
spaceName: space.name,
|
|
239
239
|
memberName: await deps.mail.displayName(event.orgId, member.orgUserId),
|
|
@@ -357,15 +357,15 @@ const COMMUNITY_DERIVATIONS = {
|
|
|
357
357
|
});
|
|
358
358
|
}
|
|
359
359
|
},
|
|
360
|
-
|
|
361
|
-
triggers: ["organization.
|
|
360
|
+
roleAssigned: {
|
|
361
|
+
triggers: ["organization.role.assigned"],
|
|
362
362
|
derive: async (event, deps) => {
|
|
363
|
-
const parsed = organizationEvents.
|
|
363
|
+
const parsed = organizationEvents.roleAssigned.safeParse(event);
|
|
364
364
|
if (!parsed.success) return;
|
|
365
|
-
const
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
return address(deps, event, event.orgId, [
|
|
365
|
+
const assigned = parsed.data.data;
|
|
366
|
+
const gain = await roleGain(deps.mail, event.orgId, assigned);
|
|
367
|
+
if (!gain) return [];
|
|
368
|
+
return address(deps, event, event.orgId, [assigned.orgUserId], gain);
|
|
369
369
|
}
|
|
370
370
|
},
|
|
371
371
|
digest: silence(["discussion.digest.due"]),
|
|
@@ -379,10 +379,10 @@ const COMMUNITY_DERIVATIONS = {
|
|
|
379
379
|
const post = await deps.mail.post(event.orgId, flag.postId);
|
|
380
380
|
const space = post ? await deps.mail.space(event.orgId, post.spaceId) : null;
|
|
381
381
|
if (!post || !space) return;
|
|
382
|
-
const audience = await deps.mail
|
|
382
|
+
const audience = await communityModerators(deps.mail, event.orgId, flag.communityId);
|
|
383
383
|
return address(deps, event, event.orgId, audience, {
|
|
384
384
|
spaceName: space.name,
|
|
385
|
-
excerpt: excerpt(post.body || post.title, 280),
|
|
385
|
+
excerpt: excerpt(deps.mail.postText(post.body) || post.title, 280),
|
|
386
386
|
reason: flag.reason,
|
|
387
387
|
spaceId: post.spaceId,
|
|
388
388
|
communityId: flag.communityId
|
|
@@ -397,7 +397,7 @@ const COMMUNITY_DERIVATIONS = {
|
|
|
397
397
|
const post = await deps.mail.post(event.orgId, comment.postId);
|
|
398
398
|
const space = post ? await deps.mail.space(event.orgId, post.spaceId) : null;
|
|
399
399
|
if (!post || !space) return;
|
|
400
|
-
const audience = await deps.mail
|
|
400
|
+
const audience = await communityModerators(deps.mail, event.orgId, flag.communityId);
|
|
401
401
|
return address(deps, event, event.orgId, audience, {
|
|
402
402
|
spaceName: space.name,
|
|
403
403
|
excerpt: excerpt(comment.body, 280),
|
|
@@ -413,7 +413,7 @@ const COMMUNITY_DERIVATIONS = {
|
|
|
413
413
|
const parsed = discussionEvents.messageReported.safeParse(event);
|
|
414
414
|
if (!parsed.success) return;
|
|
415
415
|
const flag = parsed.data.data;
|
|
416
|
-
const audience = await deps.mail
|
|
416
|
+
const audience = await communityModerators(deps.mail, event.orgId, flag.communityId);
|
|
417
417
|
return address(deps, event, event.orgId, audience, {
|
|
418
418
|
reason: flag.reason,
|
|
419
419
|
communityId: flag.communityId
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"community-derivations.js","names":[],"sources":["../../src/automations/community-derivations.ts"],"sourcesContent":["import { discussionEvents } from '../discussion/index.js';\nimport { entitlementEvents } from '../entitlements/index.js';\nimport { organizationEvents } from '../organizations/index.js';\nimport type { EmailTemplateId } from '../types/index.js';\nimport type { DomainEvent } from '../shared/ports.js';\nimport {\n actorOf,\n address,\n excerpt,\n MESSAGE_BATCH_MS,\n MESSAGE_EXCERPT,\n POST_EXCERPT,\n previousStatusOf,\n type ActionDeps,\n type EmailRecipient,\n} from './community-mail.js';\n\nexport interface EmailDerivation<K extends EmailTemplateId> {\n triggers: readonly string[];\n derive(event: DomainEvent, deps: ActionDeps): Promise<EmailRecipient<K>[] | undefined>;\n}\n\nfunction silence<K extends EmailTemplateId>(triggers: readonly string[]): EmailDerivation<K> {\n return { triggers, derive: async () => [] };\n}\n\nfunction seatsLeft(capacity: number | null, active: number): string {\n if (capacity == null) {\n return 'No cap';\n }\n return String(Math.max(capacity - active, 0));\n}\n\nfunction muted(participant: { muted: boolean; mutedUntil: Date | null }, at: Date): boolean {\n return (\n participant.muted &&\n (participant.mutedUntil === null || participant.mutedUntil.getTime() > at.getTime())\n );\n}\n\nfunction chatStatusOf(event: DomainEvent): string | undefined {\n const value = event.metadata?.chatStatus;\n return typeof value === 'string' ? value : undefined;\n}\n\nasync function communityTitle(deps: ActionDeps, orgId: string, communityId: string) {\n return deps.mail.communityTitle(orgId, communityId);\n}\n\nexport const COMMUNITY_DERIVATIONS: {\n [K in EmailTemplateId]?: EmailDerivation<K>;\n} = {\n welcomeToCommunity: {\n triggers: ['entitlement.created'],\n derive: async (event, deps) => {\n const parsed = entitlementEvents.entitlementCreated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const grant = parsed.data.data;\n const communityIds = await deps.mail.communitiesOpenedBy(event.orgId, {\n contentId: grant.contentId,\n bundleId: grant.bundleId,\n });\n const recipients: EmailRecipient<'welcomeToCommunity'>[] = [];\n for (const communityId of communityIds) {\n const title = await communityTitle(deps, event.orgId, communityId);\n if (!title) {\n return undefined;\n }\n const spaceNames = await deps.mail.openSpaceNames(\n event.orgId,\n grant.orgUserId,\n communityId,\n );\n const addressed = await address<'welcomeToCommunity'>(\n deps,\n event,\n event.orgId,\n [grant.orgUserId],\n { communityTitle: title, communityId, spaceNames },\n );\n if (!addressed) {\n return undefined;\n }\n recipients.push(...addressed);\n }\n return recipients;\n },\n },\n membershipEndingSoon: {\n triggers: ['entitlement.ending_soon'],\n derive: async (event, deps) => {\n const parsed = entitlementEvents.entitlementEndingSoon.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const grant = parsed.data.data;\n const communityId = grant.communityId;\n const title = await communityTitle(deps, event.orgId, communityId);\n if (!title) {\n return undefined;\n }\n return address(deps, event, event.orgId, [grant.orgUserId], {\n communityTitle: title,\n communityId,\n expiresAt: grant.expiresAt ? grant.expiresAt.toISOString() : '',\n });\n },\n },\n membershipEnded: {\n triggers: ['entitlement.expired', 'entitlement.deleted'],\n derive: async (event, deps) => {\n const parsed =\n event.type === 'entitlement.expired'\n ? entitlementEvents.entitlementExpired.safeParse(event)\n : entitlementEvents.entitlementDeleted.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const grant = parsed.data.data;\n const communityIds = await deps.mail.communitiesOpenedBy(event.orgId, {\n contentId: grant.contentId,\n bundleId: grant.bundleId,\n });\n const recipients: EmailRecipient<'membershipEnded'>[] = [];\n for (const communityId of communityIds) {\n const title = await communityTitle(deps, event.orgId, communityId);\n if (!title) {\n return undefined;\n }\n const addressed = await address<'membershipEnded'>(\n deps,\n event,\n event.orgId,\n [grant.orgUserId],\n { communityTitle: title, communityId },\n );\n if (!addressed) {\n return undefined;\n }\n recipients.push(...addressed);\n }\n return recipients;\n },\n },\n newPostInSpace: {\n triggers: ['discussion.post.published'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.postPublished.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const post = parsed.data.data;\n const space = await deps.mail.space(event.orgId, post.spaceId);\n if (!space) {\n return undefined;\n }\n const openers = await deps.mail.spaceOpeners(event.orgId, post.spaceId);\n return address(\n deps,\n event,\n event.orgId,\n openers.filter((id) => id !== post.orgUserId),\n {\n spaceName: space.name,\n postTitle: post.title,\n authorName: await deps.mail.displayName(event.orgId, post.orgUserId),\n excerpt: excerpt(post.body || post.title, POST_EXCERPT),\n postId: post.id,\n communityId: post.communityId,\n },\n );\n },\n },\n yourPostWasPublished: {\n triggers: ['discussion.post.published'],\n derive: async (event, deps) => {\n if (previousStatusOf(event) !== 'pending') {\n return [];\n }\n const parsed = discussionEvents.postPublished.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const post = parsed.data.data;\n const space = await deps.mail.space(event.orgId, post.spaceId);\n if (!space) {\n return undefined;\n }\n return address(deps, event, event.orgId, [post.orgUserId], {\n postTitle: post.title,\n spaceName: space.name,\n postId: post.id,\n communityId: post.communityId,\n });\n },\n },\n yourPostWasRemoved: {\n triggers: ['discussion.post.removed'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.postRemoved.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const post = parsed.data.data;\n if (post.removedBy === post.orgUserId) {\n return [];\n }\n const space = await deps.mail.space(event.orgId, post.spaceId);\n if (!space) {\n return undefined;\n }\n return address(deps, event, event.orgId, [post.orgUserId], {\n postTitle: post.title,\n spaceName: space.name,\n spaceId: post.spaceId,\n communityId: post.communityId,\n });\n },\n },\n newCommentOnPost: {\n triggers: ['discussion.comment.created'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.commentCreated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const comment = parsed.data.data;\n if (!comment.postId || comment.status !== 'published' || !comment.communityId) {\n return [];\n }\n const post = await deps.mail.post(event.orgId, comment.postId);\n if (!post) {\n return undefined;\n }\n const followers = await deps.mail.followerIds(event.orgId, comment.postId);\n const ids = [...new Set([...followers, post.orgUserId])].filter(\n (id) => id !== comment.orgUserId,\n );\n const openers = await deps.mail.spaceOpenersAmong(event.orgId, post.spaceId, ids);\n return address(deps, event, event.orgId, openers, {\n postTitle: post.title,\n commenterName: await deps.mail.displayName(event.orgId, comment.orgUserId),\n excerpt: excerpt(comment.body, POST_EXCERPT),\n commentId: comment.id,\n postId: comment.postId,\n communityId: comment.communityId,\n });\n },\n },\n replyToYourComment: {\n triggers: ['discussion.comment.created'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.commentCreated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const comment = parsed.data.data;\n if (!comment.parentId || !comment.postId || comment.status !== 'published') {\n return [];\n }\n const parent = await deps.mail.comment(event.orgId, comment.parentId);\n const post = await deps.mail.post(event.orgId, comment.postId);\n if (!parent || !post || !comment.communityId) {\n return parent && post ? [] : undefined;\n }\n if (parent.orgUserId === comment.orgUserId) {\n return [];\n }\n const openers = await deps.mail.spaceOpenersAmong(event.orgId, post.spaceId, [\n parent.orgUserId,\n ]);\n return address(deps, event, event.orgId, openers, {\n postTitle: post.title,\n commenterName: await deps.mail.displayName(event.orgId, comment.orgUserId),\n excerpt: excerpt(comment.body, POST_EXCERPT),\n commentId: comment.id,\n postId: comment.postId,\n communityId: comment.communityId,\n });\n },\n },\n yourCommentWasRemoved: {\n triggers: ['discussion.comment.removed'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.commentRemoved.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const comment = parsed.data.data;\n if (!comment.postId || !comment.communityId) {\n return [];\n }\n if (comment.removedBy === comment.orgUserId) {\n return [];\n }\n const post = await deps.mail.post(event.orgId, comment.postId);\n if (!post) {\n return undefined;\n }\n return address(deps, event, event.orgId, [comment.orgUserId], {\n postTitle: post.title,\n postId: comment.postId,\n communityId: comment.communityId,\n });\n },\n },\n postAwaitingReview: {\n triggers: ['discussion.post.created', 'discussion.post.updated'],\n derive: async (event, deps) => {\n const parsed =\n event.type === 'discussion.post.created'\n ? discussionEvents.postCreated.safeParse(event)\n : discussionEvents.postUpdated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const post = parsed.data.data;\n if (post.status !== 'pending' || previousStatusOf(event) === 'pending') {\n return [];\n }\n const space = await deps.mail.space(event.orgId, post.spaceId);\n if (!space) {\n return undefined;\n }\n const audience = await deps.mail.moderationAudience(event.orgId, post.communityId);\n return address(deps, event, event.orgId, audience, {\n spaceName: space.name,\n postTitle: post.title,\n authorName: await deps.mail.displayName(event.orgId, post.orgUserId),\n spaceId: post.spaceId,\n communityId: post.communityId,\n });\n },\n },\n joinRequest: {\n triggers: ['entitlement.space_member.created'],\n derive: async (event, deps) => {\n const parsed = entitlementEvents.spaceMemberCreated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const member = parsed.data.data;\n if (member.status !== 'requested') {\n return [];\n }\n const space = await deps.mail.space(event.orgId, member.spaceId);\n if (!space) {\n return undefined;\n }\n const active = await deps.mail.activeMemberCount(event.orgId, member.spaceId);\n const audience = await deps.mail.moderationAudience(event.orgId, member.communityId);\n return address(deps, event, event.orgId, audience, {\n spaceName: space.name,\n memberName: await deps.mail.displayName(event.orgId, member.orgUserId),\n seatsLeft: seatsLeft(space.capacity, active),\n spaceId: member.spaceId,\n communityId: member.communityId,\n });\n },\n },\n requestApproved: {\n triggers: ['entitlement.space_member.updated'],\n derive: async (event, deps) => {\n const parsed = entitlementEvents.spaceMemberUpdated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const member = parsed.data.data;\n if (member.status !== 'active' || previousStatusOf(event) !== 'requested') {\n return [];\n }\n const space = await deps.mail.space(event.orgId, member.spaceId);\n if (!space) {\n return undefined;\n }\n return address(deps, event, event.orgId, [member.orgUserId], {\n spaceName: space.name,\n spaceId: member.spaceId,\n communityId: member.communityId,\n });\n },\n },\n requestDeclined: {\n triggers: ['entitlement.space_member.deleted'],\n derive: async (event, deps) => {\n if (previousStatusOf(event) !== 'requested') {\n return [];\n }\n const parsed = entitlementEvents.spaceMemberDeleted.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const member = parsed.data.data;\n const space = await deps.mail.space(event.orgId, member.spaceId);\n if (!space) {\n return undefined;\n }\n return address(deps, event, event.orgId, [member.orgUserId], {\n spaceName: space.name,\n communityId: member.communityId,\n });\n },\n },\n invitedToSpace: {\n triggers: ['entitlement.space_member.created'],\n derive: async (event, deps) => {\n const parsed = entitlementEvents.spaceMemberCreated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const member = parsed.data.data;\n if (member.status !== 'invited') {\n return [];\n }\n const space = await deps.mail.space(event.orgId, member.spaceId);\n if (!space) {\n return undefined;\n }\n const actor = actorOf(event);\n return address(deps, event, event.orgId, [member.orgUserId], {\n spaceName: space.name,\n inviterName: actor ? await deps.mail.displayName(event.orgId, actor) : 'A member',\n spaceId: member.spaceId,\n communityId: member.communityId,\n });\n },\n },\n messageRequest: {\n triggers: ['discussion.chat.created'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.chatCreated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const chat = parsed.data.data;\n if (chat.kind !== 'direct' || chat.status !== 'pending') {\n return [];\n }\n const recipient = chat.participantIds.find((id) => id !== chat.createdBy);\n if (!recipient) {\n return [];\n }\n const members = await deps.mail.communityMembersAmong(event.orgId, chat.communityId, [\n recipient,\n ]);\n const body = await deps.mail.latestMessageBody(event.orgId, chat.id);\n return address(deps, event, event.orgId, members, {\n senderName: chat.createdBy\n ? await deps.mail.displayName(event.orgId, chat.createdBy)\n : 'A member',\n excerpt: excerpt(body ?? '', MESSAGE_EXCERPT),\n chatId: chat.id,\n communityId: chat.communityId,\n });\n },\n },\n newMessage: {\n triggers: ['discussion.message.sent'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.messageSent.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const message = parsed.data.data;\n const chat = await deps.mail.chat(event.orgId, message.chatId);\n if (!chat) {\n return undefined;\n }\n if (chatStatusOf(event) === 'pending') {\n return [];\n }\n const at = new Date(event.occurredAt);\n const since = new Date(at.getTime() - MESSAGE_BATCH_MS);\n const recent = new Set(\n await deps.mail.recentMessageRecipients(\n event.orgId,\n deps.automationId,\n message.chatId,\n since,\n ),\n );\n const ids: string[] = [];\n for (const participant of chat.participants) {\n if (participant.leftAt || participant.orgUserId === message.orgUserId) {\n continue;\n }\n if (muted(participant, at) || recent.has(participant.orgUserId)) {\n continue;\n }\n if (\n await deps.mail.pairBlocked(\n event.orgId,\n chat.communityId,\n message.orgUserId,\n participant.orgUserId,\n )\n ) {\n continue;\n }\n ids.push(participant.orgUserId);\n }\n const members = await deps.mail.communityMembersAmong(event.orgId, chat.communityId, ids);\n const title =\n chat.kind === 'direct'\n ? await deps.mail.displayName(event.orgId, message.orgUserId)\n : await groupTitle(deps, event.orgId, chat.participants);\n return address(deps, event, event.orgId, members, {\n senderName: await deps.mail.displayName(event.orgId, message.orgUserId),\n chatTitle: title,\n excerpt: excerpt(message.body, MESSAGE_EXCERPT),\n chatId: message.chatId,\n communityId: message.communityId,\n });\n },\n },\n addedToGroupChat: {\n triggers: ['discussion.chat.participant.added'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.chatParticipantAdded.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const added = parsed.data.data;\n const chat = await deps.mail.chat(event.orgId, added.chatId);\n if (!chat) {\n return undefined;\n }\n return address(deps, event, event.orgId, [added.orgUserId], {\n adderName: await deps.mail.displayName(event.orgId, added.addedBy),\n participantNames: await participantNames(deps, event.orgId, chat.participants),\n chatId: added.chatId,\n communityId: added.communityId,\n });\n },\n },\n youAreNowAModerator: {\n triggers: ['organization.user.updated'],\n derive: async (event, deps) => {\n const parsed = organizationEvents.orgUserUpdated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const orgUser = parsed.data.data;\n if (orgUser.role !== 'moderator') {\n return [];\n }\n const communities = await deps.mail.memberCommunityTitles(event.orgId, orgUser.id);\n return address(deps, event, event.orgId, [orgUser.id], {\n communityTitles: communities.map((community) => community.title),\n });\n },\n },\n digest: silence(['discussion.digest.due']),\n somethingWasFlagged: {\n triggers: ['discussion.post.reported', 'discussion.comment.reported'],\n derive: async (event, deps) => {\n if (event.type === 'discussion.post.reported') {\n const parsed = discussionEvents.postReported.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const flag = parsed.data.data;\n const post = await deps.mail.post(event.orgId, flag.postId);\n const space = post ? await deps.mail.space(event.orgId, post.spaceId) : null;\n if (!post || !space) {\n return undefined;\n }\n const audience = await deps.mail.moderationAudience(event.orgId, flag.communityId);\n return address(deps, event, event.orgId, audience, {\n spaceName: space.name,\n excerpt: excerpt(post.body || post.title, POST_EXCERPT),\n reason: flag.reason,\n spaceId: post.spaceId,\n communityId: flag.communityId,\n });\n }\n const parsed = discussionEvents.commentReported.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const flag = parsed.data.data;\n if (!flag.communityId) {\n return [];\n }\n const comment = await deps.mail.comment(event.orgId, flag.commentId);\n if (!comment?.postId) {\n return comment ? [] : undefined;\n }\n const post = await deps.mail.post(event.orgId, comment.postId);\n const space = post ? await deps.mail.space(event.orgId, post.spaceId) : null;\n if (!post || !space) {\n return undefined;\n }\n const audience = await deps.mail.moderationAudience(event.orgId, flag.communityId);\n return address(deps, event, event.orgId, audience, {\n spaceName: space.name,\n excerpt: excerpt(comment.body, POST_EXCERPT),\n reason: flag.reason,\n spaceId: post.spaceId,\n communityId: flag.communityId,\n });\n },\n },\n messageWasFlagged: {\n triggers: ['discussion.message.reported'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.messageReported.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const flag = parsed.data.data;\n const audience = await deps.mail.staffIds(event.orgId);\n return address(deps, event, event.orgId, audience, {\n reason: flag.reason,\n communityId: flag.communityId,\n });\n },\n },\n};\n\nasync function participantNames(\n deps: ActionDeps,\n orgId: string,\n participants: { orgUserId: string; leftAt: Date | null }[],\n): Promise<string[]> {\n const names: string[] = [];\n for (const participant of participants) {\n if (participant.leftAt) {\n continue;\n }\n names.push(await deps.mail.displayName(orgId, participant.orgUserId));\n }\n return names;\n}\n\nasync function groupTitle(\n deps: ActionDeps,\n orgId: string,\n participants: { orgUserId: string; leftAt: Date | null }[],\n): Promise<string> {\n const names = await participantNames(deps, orgId, participants);\n return names.length > 0 ? names.join(', ') : 'Group chat';\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"community-derivations.js","names":[],"sources":["../../src/automations/community-derivations.ts"],"sourcesContent":["import { discussionEvents } from '../discussion/index.js';\nimport { entitlementEvents } from '../entitlements/index.js';\nimport { organizationEvents } from '../organizations/index.js';\nimport type { EmailTemplateId } from '../types/index.js';\nimport type { DomainEvent } from '../shared/ports.js';\nimport {\n actorOf,\n address,\n communityModerators,\n excerpt,\n MESSAGE_BATCH_MS,\n MESSAGE_EXCERPT,\n POST_EXCERPT,\n previousStatusOf,\n roleGain,\n type ActionDeps,\n type EmailRecipient,\n} from './community-mail.js';\n\nexport interface EmailDerivation<K extends EmailTemplateId> {\n triggers: readonly string[];\n derive(event: DomainEvent, deps: ActionDeps): Promise<EmailRecipient<K>[] | undefined>;\n}\n\nfunction silence<K extends EmailTemplateId>(triggers: readonly string[]): EmailDerivation<K> {\n return { triggers, derive: async () => [] };\n}\n\nfunction seatsLeft(capacity: number | null, active: number): string {\n if (capacity == null) {\n return 'No cap';\n }\n return String(Math.max(capacity - active, 0));\n}\n\nfunction muted(participant: { muted: boolean; mutedUntil: Date | null }, at: Date): boolean {\n return (\n participant.muted &&\n (participant.mutedUntil === null || participant.mutedUntil.getTime() > at.getTime())\n );\n}\n\nfunction chatStatusOf(event: DomainEvent): string | undefined {\n const value = event.metadata?.chatStatus;\n return typeof value === 'string' ? value : undefined;\n}\n\nasync function communityTitle(deps: ActionDeps, orgId: string, communityId: string) {\n return deps.mail.communityTitle(orgId, communityId);\n}\n\nexport const COMMUNITY_DERIVATIONS: {\n [K in EmailTemplateId]?: EmailDerivation<K>;\n} = {\n welcomeToCommunity: {\n triggers: ['entitlement.created'],\n derive: async (event, deps) => {\n const parsed = entitlementEvents.entitlementCreated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const grant = parsed.data.data;\n const communityIds = await deps.mail.communitiesOpenedBy(event.orgId, {\n contentId: grant.contentId,\n bundleId: grant.bundleId,\n });\n const recipients: EmailRecipient<'welcomeToCommunity'>[] = [];\n for (const communityId of communityIds) {\n const title = await communityTitle(deps, event.orgId, communityId);\n if (!title) {\n return undefined;\n }\n const spaceNames = await deps.mail.openSpaceNames(\n event.orgId,\n grant.orgUserId,\n communityId,\n );\n const addressed = await address<'welcomeToCommunity'>(\n deps,\n event,\n event.orgId,\n [grant.orgUserId],\n { communityTitle: title, communityId, spaceNames },\n );\n if (!addressed) {\n return undefined;\n }\n recipients.push(...addressed);\n }\n return recipients;\n },\n },\n membershipEndingSoon: {\n triggers: ['entitlement.ending_soon'],\n derive: async (event, deps) => {\n const parsed = entitlementEvents.entitlementEndingSoon.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const grant = parsed.data.data;\n const communityId = grant.communityId;\n const title = await communityTitle(deps, event.orgId, communityId);\n if (!title) {\n return undefined;\n }\n return address(deps, event, event.orgId, [grant.orgUserId], {\n communityTitle: title,\n communityId,\n expiresAt: grant.expiresAt ? grant.expiresAt.toISOString() : '',\n });\n },\n },\n membershipEnded: {\n triggers: ['entitlement.expired', 'entitlement.deleted'],\n derive: async (event, deps) => {\n const parsed =\n event.type === 'entitlement.expired'\n ? entitlementEvents.entitlementExpired.safeParse(event)\n : entitlementEvents.entitlementDeleted.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const grant = parsed.data.data;\n const communityIds = await deps.mail.communitiesOpenedBy(event.orgId, {\n contentId: grant.contentId,\n bundleId: grant.bundleId,\n });\n const recipients: EmailRecipient<'membershipEnded'>[] = [];\n for (const communityId of communityIds) {\n const title = await communityTitle(deps, event.orgId, communityId);\n if (!title) {\n return undefined;\n }\n const addressed = await address<'membershipEnded'>(\n deps,\n event,\n event.orgId,\n [grant.orgUserId],\n { communities: [{ communityId, communityTitle: title }] },\n );\n if (!addressed) {\n return undefined;\n }\n recipients.push(...addressed);\n }\n return recipients;\n },\n },\n newPostInSpace: {\n triggers: ['discussion.post.published'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.postPublished.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const post = parsed.data.data;\n const space = await deps.mail.space(event.orgId, post.spaceId);\n if (!space) {\n return undefined;\n }\n const openers = await deps.mail.spaceOpeners(event.orgId, post.spaceId);\n return address(\n deps,\n event,\n event.orgId,\n openers.filter((id) => id !== post.orgUserId),\n {\n spaceName: space.name,\n postTitle: post.title,\n authorName: await deps.mail.displayName(event.orgId, post.orgUserId),\n excerpt: excerpt(deps.mail.postText(post.body) || post.title, POST_EXCERPT),\n postId: post.id,\n communityId: post.communityId,\n },\n );\n },\n },\n yourPostWasPublished: {\n triggers: ['discussion.post.published'],\n derive: async (event, deps) => {\n if (previousStatusOf(event) !== 'pending') {\n return [];\n }\n const parsed = discussionEvents.postPublished.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const post = parsed.data.data;\n const space = await deps.mail.space(event.orgId, post.spaceId);\n if (!space) {\n return undefined;\n }\n return address(deps, event, event.orgId, [post.orgUserId], {\n postTitle: post.title,\n spaceName: space.name,\n postId: post.id,\n communityId: post.communityId,\n });\n },\n },\n yourPostWasRemoved: {\n triggers: ['discussion.post.removed'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.postRemoved.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const post = parsed.data.data;\n if (post.removedBy === post.orgUserId) {\n return [];\n }\n const space = await deps.mail.space(event.orgId, post.spaceId);\n if (!space) {\n return undefined;\n }\n return address(deps, event, event.orgId, [post.orgUserId], {\n postTitle: post.title,\n spaceName: space.name,\n spaceId: post.spaceId,\n communityId: post.communityId,\n });\n },\n },\n newCommentOnPost: {\n triggers: ['discussion.comment.created'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.commentCreated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const comment = parsed.data.data;\n if (!comment.postId || comment.status !== 'published' || !comment.communityId) {\n return [];\n }\n const post = await deps.mail.post(event.orgId, comment.postId);\n if (!post) {\n return undefined;\n }\n const followers = await deps.mail.followerIds(event.orgId, comment.postId);\n const ids = [...new Set([...followers, post.orgUserId])].filter(\n (id) => id !== comment.orgUserId,\n );\n const openers = await deps.mail.spaceOpenersAmong(event.orgId, post.spaceId, ids);\n return address(deps, event, event.orgId, openers, {\n postTitle: post.title,\n commenterName: await deps.mail.displayName(event.orgId, comment.orgUserId),\n excerpt: excerpt(comment.body, POST_EXCERPT),\n commentId: comment.id,\n postId: comment.postId,\n communityId: comment.communityId,\n });\n },\n },\n replyToYourComment: {\n triggers: ['discussion.comment.created'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.commentCreated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const comment = parsed.data.data;\n if (!comment.parentId || !comment.postId || comment.status !== 'published') {\n return [];\n }\n const parent = await deps.mail.comment(event.orgId, comment.parentId);\n const post = await deps.mail.post(event.orgId, comment.postId);\n if (!parent || !post || !comment.communityId) {\n return parent && post ? [] : undefined;\n }\n if (parent.orgUserId === comment.orgUserId) {\n return [];\n }\n const openers = await deps.mail.spaceOpenersAmong(event.orgId, post.spaceId, [\n parent.orgUserId,\n ]);\n return address(deps, event, event.orgId, openers, {\n postTitle: post.title,\n commenterName: await deps.mail.displayName(event.orgId, comment.orgUserId),\n excerpt: excerpt(comment.body, POST_EXCERPT),\n commentId: comment.id,\n postId: comment.postId,\n communityId: comment.communityId,\n });\n },\n },\n yourCommentWasRemoved: {\n triggers: ['discussion.comment.removed'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.commentRemoved.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const comment = parsed.data.data;\n if (!comment.postId || !comment.communityId) {\n return [];\n }\n if (comment.removedBy === comment.orgUserId) {\n return [];\n }\n const post = await deps.mail.post(event.orgId, comment.postId);\n if (!post) {\n return undefined;\n }\n return address(deps, event, event.orgId, [comment.orgUserId], {\n postTitle: post.title,\n postId: comment.postId,\n communityId: comment.communityId,\n });\n },\n },\n postAwaitingReview: {\n triggers: ['discussion.post.created', 'discussion.post.updated'],\n derive: async (event, deps) => {\n const parsed =\n event.type === 'discussion.post.created'\n ? discussionEvents.postCreated.safeParse(event)\n : discussionEvents.postUpdated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const post = parsed.data.data;\n if (post.status !== 'pending' || previousStatusOf(event) === 'pending') {\n return [];\n }\n const space = await deps.mail.space(event.orgId, post.spaceId);\n if (!space) {\n return undefined;\n }\n const audience = await communityModerators(deps.mail, event.orgId, post.communityId);\n return address(deps, event, event.orgId, audience, {\n spaceName: space.name,\n postTitle: post.title,\n authorName: await deps.mail.displayName(event.orgId, post.orgUserId),\n spaceId: post.spaceId,\n communityId: post.communityId,\n });\n },\n },\n joinRequest: {\n triggers: ['entitlement.space_member.created'],\n derive: async (event, deps) => {\n const parsed = entitlementEvents.spaceMemberCreated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const member = parsed.data.data;\n if (member.status !== 'requested') {\n return [];\n }\n const space = await deps.mail.space(event.orgId, member.spaceId);\n if (!space) {\n return undefined;\n }\n const active = await deps.mail.activeMemberCount(event.orgId, member.spaceId);\n const audience = await communityModerators(deps.mail, event.orgId, member.communityId);\n return address(deps, event, event.orgId, audience, {\n spaceName: space.name,\n memberName: await deps.mail.displayName(event.orgId, member.orgUserId),\n seatsLeft: seatsLeft(space.capacity, active),\n spaceId: member.spaceId,\n communityId: member.communityId,\n });\n },\n },\n requestApproved: {\n triggers: ['entitlement.space_member.updated'],\n derive: async (event, deps) => {\n const parsed = entitlementEvents.spaceMemberUpdated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const member = parsed.data.data;\n if (member.status !== 'active' || previousStatusOf(event) !== 'requested') {\n return [];\n }\n const space = await deps.mail.space(event.orgId, member.spaceId);\n if (!space) {\n return undefined;\n }\n return address(deps, event, event.orgId, [member.orgUserId], {\n spaceName: space.name,\n spaceId: member.spaceId,\n communityId: member.communityId,\n });\n },\n },\n requestDeclined: {\n triggers: ['entitlement.space_member.deleted'],\n derive: async (event, deps) => {\n if (previousStatusOf(event) !== 'requested') {\n return [];\n }\n const parsed = entitlementEvents.spaceMemberDeleted.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const member = parsed.data.data;\n const space = await deps.mail.space(event.orgId, member.spaceId);\n if (!space) {\n return undefined;\n }\n return address(deps, event, event.orgId, [member.orgUserId], {\n spaceName: space.name,\n communityId: member.communityId,\n });\n },\n },\n invitedToSpace: {\n triggers: ['entitlement.space_member.created'],\n derive: async (event, deps) => {\n const parsed = entitlementEvents.spaceMemberCreated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const member = parsed.data.data;\n if (member.status !== 'invited') {\n return [];\n }\n const space = await deps.mail.space(event.orgId, member.spaceId);\n if (!space) {\n return undefined;\n }\n const actor = actorOf(event);\n return address(deps, event, event.orgId, [member.orgUserId], {\n spaceName: space.name,\n inviterName: actor ? await deps.mail.displayName(event.orgId, actor) : 'A member',\n spaceId: member.spaceId,\n communityId: member.communityId,\n });\n },\n },\n messageRequest: {\n triggers: ['discussion.chat.created'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.chatCreated.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const chat = parsed.data.data;\n if (chat.kind !== 'direct' || chat.status !== 'pending') {\n return [];\n }\n const recipient = chat.participantIds.find((id) => id !== chat.createdBy);\n if (!recipient) {\n return [];\n }\n const members = await deps.mail.communityMembersAmong(event.orgId, chat.communityId, [\n recipient,\n ]);\n const body = await deps.mail.latestMessageBody(event.orgId, chat.id);\n return address(deps, event, event.orgId, members, {\n senderName: chat.createdBy\n ? await deps.mail.displayName(event.orgId, chat.createdBy)\n : 'A member',\n excerpt: excerpt(body ?? '', MESSAGE_EXCERPT),\n chatId: chat.id,\n communityId: chat.communityId,\n });\n },\n },\n newMessage: {\n triggers: ['discussion.message.sent'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.messageSent.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const message = parsed.data.data;\n const chat = await deps.mail.chat(event.orgId, message.chatId);\n if (!chat) {\n return undefined;\n }\n if (chatStatusOf(event) === 'pending') {\n return [];\n }\n const at = new Date(event.occurredAt);\n const since = new Date(at.getTime() - MESSAGE_BATCH_MS);\n const recent = new Set(\n await deps.mail.recentMessageRecipients(\n event.orgId,\n deps.automationId,\n message.chatId,\n since,\n ),\n );\n const ids: string[] = [];\n for (const participant of chat.participants) {\n if (participant.leftAt || participant.orgUserId === message.orgUserId) {\n continue;\n }\n if (muted(participant, at) || recent.has(participant.orgUserId)) {\n continue;\n }\n if (\n await deps.mail.pairBlocked(\n event.orgId,\n chat.communityId,\n message.orgUserId,\n participant.orgUserId,\n )\n ) {\n continue;\n }\n ids.push(participant.orgUserId);\n }\n const members = await deps.mail.communityMembersAmong(event.orgId, chat.communityId, ids);\n const title =\n chat.kind === 'direct'\n ? await deps.mail.displayName(event.orgId, message.orgUserId)\n : await groupTitle(deps, event.orgId, chat.participants);\n return address(deps, event, event.orgId, members, {\n senderName: await deps.mail.displayName(event.orgId, message.orgUserId),\n chatTitle: title,\n excerpt: excerpt(message.body, MESSAGE_EXCERPT),\n chatId: message.chatId,\n communityId: message.communityId,\n });\n },\n },\n addedToGroupChat: {\n triggers: ['discussion.chat.participant.added'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.chatParticipantAdded.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const added = parsed.data.data;\n const chat = await deps.mail.chat(event.orgId, added.chatId);\n if (!chat) {\n return undefined;\n }\n return address(deps, event, event.orgId, [added.orgUserId], {\n adderName: await deps.mail.displayName(event.orgId, added.addedBy),\n participantNames: await participantNames(deps, event.orgId, chat.participants),\n chatId: added.chatId,\n communityId: added.communityId,\n });\n },\n },\n roleAssigned: {\n triggers: ['organization.role.assigned'],\n derive: async (event, deps) => {\n const parsed = organizationEvents.roleAssigned.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const assigned = parsed.data.data;\n const gain = await roleGain(deps.mail, event.orgId, assigned);\n if (!gain) {\n return [];\n }\n return address(deps, event, event.orgId, [assigned.orgUserId], gain);\n },\n },\n digest: silence(['discussion.digest.due']),\n somethingWasFlagged: {\n triggers: ['discussion.post.reported', 'discussion.comment.reported'],\n derive: async (event, deps) => {\n if (event.type === 'discussion.post.reported') {\n const parsed = discussionEvents.postReported.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const flag = parsed.data.data;\n const post = await deps.mail.post(event.orgId, flag.postId);\n const space = post ? await deps.mail.space(event.orgId, post.spaceId) : null;\n if (!post || !space) {\n return undefined;\n }\n const audience = await communityModerators(deps.mail, event.orgId, flag.communityId);\n return address(deps, event, event.orgId, audience, {\n spaceName: space.name,\n excerpt: excerpt(deps.mail.postText(post.body) || post.title, POST_EXCERPT),\n reason: flag.reason,\n spaceId: post.spaceId,\n communityId: flag.communityId,\n });\n }\n const parsed = discussionEvents.commentReported.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const flag = parsed.data.data;\n if (!flag.communityId) {\n return [];\n }\n const comment = await deps.mail.comment(event.orgId, flag.commentId);\n if (!comment?.postId) {\n return comment ? [] : undefined;\n }\n const post = await deps.mail.post(event.orgId, comment.postId);\n const space = post ? await deps.mail.space(event.orgId, post.spaceId) : null;\n if (!post || !space) {\n return undefined;\n }\n const audience = await communityModerators(deps.mail, event.orgId, flag.communityId);\n return address(deps, event, event.orgId, audience, {\n spaceName: space.name,\n excerpt: excerpt(comment.body, POST_EXCERPT),\n reason: flag.reason,\n spaceId: post.spaceId,\n communityId: flag.communityId,\n });\n },\n },\n messageWasFlagged: {\n triggers: ['discussion.message.reported'],\n derive: async (event, deps) => {\n const parsed = discussionEvents.messageReported.safeParse(event);\n if (!parsed.success) {\n return undefined;\n }\n const flag = parsed.data.data;\n const audience = await communityModerators(deps.mail, event.orgId, flag.communityId);\n return address(deps, event, event.orgId, audience, {\n reason: flag.reason,\n communityId: flag.communityId,\n });\n },\n },\n};\n\nasync function participantNames(\n deps: ActionDeps,\n orgId: string,\n participants: { orgUserId: string; leftAt: Date | null }[],\n): Promise<string[]> {\n const names: string[] = [];\n for (const participant of participants) {\n if (participant.leftAt) {\n continue;\n }\n names.push(await deps.mail.displayName(orgId, participant.orgUserId));\n }\n return names;\n}\n\nasync function groupTitle(\n deps: ActionDeps,\n orgId: string,\n participants: { orgUserId: string; leftAt: Date | null }[],\n): Promise<string> {\n const names = await participantNames(deps, orgId, participants);\n return names.length > 0 ? names.join(', ') : 'Group chat';\n}\n"],"mappings":"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"}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { DomainEvent, JsonValue } from "../types/shared.js";
|
|
2
2
|
import { Logger } from "../types/ports.js";
|
|
3
|
+
import { ContentType } from "../types/schemas/content.js";
|
|
3
4
|
import { EmailTemplateId, EmailTemplateParams } from "../types/email-templates.js";
|
|
5
|
+
import { Permission } from "../types/schemas/organizations.js";
|
|
4
6
|
import { GrantEntitlementInput } from "../types/schemas/entitlements.js";
|
|
5
7
|
import { WebhookSender } from "../types/automations.js";
|
|
6
8
|
import { ChatStatus } from "../types/schemas/discussion.js";
|
|
7
9
|
import "../types/index.js";
|
|
8
10
|
import { CredentialStore } from "../shared/ports.js";
|
|
9
|
-
import { NotificationDesk } from "../identity/ports.js";
|
|
10
|
-
import { Mailer, MailerLookups } from "../shared/mailer.js";
|
|
11
|
-
import "../identity/index.js";
|
|
12
11
|
import "../entitlements/index.js";
|
|
12
|
+
import { Mailer, MailerLookups } from "../shared/mailer.js";
|
|
13
13
|
import "./ports.js";
|
|
14
14
|
//#region src/automations/community-mail.d.ts
|
|
15
15
|
declare const MESSAGE_EXCERPT = 140;
|
|
@@ -36,13 +36,29 @@ interface CommunityMailReads {
|
|
|
36
36
|
communityMembersAmong(orgId: string, communityId: string, ids: string[]): Promise<string[]>;
|
|
37
37
|
spaceOpenersAmong(orgId: string, spaceId: string, ids: string[]): Promise<string[]>;
|
|
38
38
|
spaceOpeners(orgId: string, spaceId: string): Promise<string[]>;
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
holders(orgId: string, permission: Permission, on?: string): Promise<string[]>;
|
|
40
|
+
role(orgId: string, roleKey: string): Promise<{
|
|
41
|
+
name: string;
|
|
42
|
+
permissions: Permission[];
|
|
43
|
+
} | null>;
|
|
44
|
+
heldOnOrg(orgId: string, orgUserId: string, permissions: Permission[]): Promise<Permission[]>;
|
|
45
|
+
orgName(orgId: string): Promise<string | null>;
|
|
46
|
+
contentInfo(orgId: string, item: {
|
|
47
|
+
contentId: string;
|
|
48
|
+
} | {
|
|
49
|
+
bundleId: string;
|
|
50
|
+
}): Promise<{
|
|
51
|
+
title: string;
|
|
52
|
+
type: string;
|
|
53
|
+
} | null>;
|
|
41
54
|
displayName(orgId: string, orgUserId: string): Promise<string>;
|
|
55
|
+
postText(body: string): string;
|
|
42
56
|
followerIds(orgId: string, postId: string): Promise<string[]>;
|
|
43
57
|
comment(orgId: string, commentId: string): Promise<{
|
|
44
58
|
orgUserId: string;
|
|
45
59
|
postId: string | null;
|
|
60
|
+
activityId: string | null;
|
|
61
|
+
parentId: string | null;
|
|
46
62
|
body: string;
|
|
47
63
|
} | null>;
|
|
48
64
|
post(orgId: string, postId: string): Promise<{
|
|
@@ -73,15 +89,6 @@ interface CommunityMailReads {
|
|
|
73
89
|
}[]>;
|
|
74
90
|
recentMessageRecipients(orgId: string, automationId: string, chatId: string, since: Date): Promise<string[]>;
|
|
75
91
|
}
|
|
76
|
-
interface ScheduledWork {
|
|
77
|
-
publishDuePosts(orgId: string, now: Date): Promise<void>;
|
|
78
|
-
expireDueGrants(orgId: string, now: Date): Promise<void>;
|
|
79
|
-
sendDigest(orgId: string, now: Date): Promise<void>;
|
|
80
|
-
hasDuePosts(orgId: string, now: Date): Promise<boolean>;
|
|
81
|
-
hasDueGrants(orgId: string, now: Date): Promise<boolean>;
|
|
82
|
-
removeUnattachedUploads(orgId: string, now: Date): Promise<void>;
|
|
83
|
-
hasUnattachedUploads(orgId: string, now: Date): Promise<boolean>;
|
|
84
|
-
}
|
|
85
92
|
interface GrantWriter {
|
|
86
93
|
grant(orgId: string, input: GrantEntitlementInput): Promise<unknown>;
|
|
87
94
|
}
|
|
@@ -92,21 +99,30 @@ interface ActionDeps {
|
|
|
92
99
|
webhooks: WebhookSender;
|
|
93
100
|
mail: CommunityMailReads;
|
|
94
101
|
grants: GrantWriter;
|
|
95
|
-
desk: NotificationDesk;
|
|
96
|
-
scheduledWork: ScheduledWork;
|
|
97
102
|
automationId: string;
|
|
98
103
|
actionInput: Record<string, JsonValue>;
|
|
99
104
|
logger?: Logger;
|
|
100
105
|
}
|
|
101
106
|
declare const emptyCommunityMail: CommunityMailReads;
|
|
102
|
-
declare
|
|
103
|
-
|
|
107
|
+
declare function communityModerators(reads: Pick<CommunityMailReads, 'holders' | 'communityMembersAmong'>, orgId: string, communityId: string): Promise<string[]>;
|
|
108
|
+
interface RoleGain {
|
|
109
|
+
roleName: string;
|
|
110
|
+
place: string;
|
|
111
|
+
contentId: string | null;
|
|
112
|
+
contentType: ContentType | null;
|
|
113
|
+
}
|
|
114
|
+
declare function roleGain(reads: Pick<CommunityMailReads, 'role' | 'orgName' | 'contentInfo' | 'heldOnOrg'>, orgId: string, assigned: {
|
|
115
|
+
orgUserId: string;
|
|
116
|
+
roleKey: string;
|
|
117
|
+
previousRoleKey: string | null;
|
|
118
|
+
contentId: string | null;
|
|
119
|
+
contentType: ContentType | null;
|
|
120
|
+
}): Promise<RoleGain | null>;
|
|
104
121
|
declare function actorOf(event: DomainEvent): string | undefined;
|
|
105
122
|
declare function previousStatusOf(event: DomainEvent): string | undefined;
|
|
106
123
|
declare function excerpt(value: string, max: number): string;
|
|
107
124
|
declare function seededCommunityId(deps: ActionDeps): string | null;
|
|
108
|
-
declare function firedAtOf(event: DomainEvent): Date;
|
|
109
125
|
declare function address<K extends EmailTemplateId>(deps: ActionDeps, event: DomainEvent, orgId: string, orgUserIds: string[], params: EmailTemplateParams[K]): Promise<EmailRecipient<K>[] | undefined>;
|
|
110
126
|
//#endregion
|
|
111
|
-
export { ActionDeps, CommunityMailReads, EmailRecipient, GrantWriter, MESSAGE_BATCH_MS, MESSAGE_EXCERPT, POST_EXCERPT,
|
|
127
|
+
export { ActionDeps, CommunityMailReads, EmailRecipient, GrantWriter, MESSAGE_BATCH_MS, MESSAGE_EXCERPT, POST_EXCERPT, RoleGain, actorOf, address, communityModerators, emptyCommunityMail, excerpt, previousStatusOf, roleGain, seededCommunityId };
|
|
112
128
|
//# sourceMappingURL=community-mail.d.ts.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { noopLogger } from "../shared/logger.js";
|
|
2
|
+
import { PERMISSIONS, withImplied } from "../organizations/permissions.js";
|
|
2
3
|
//#region src/automations/community-mail.ts
|
|
3
4
|
const MESSAGE_EXCERPT = 140;
|
|
4
5
|
const POST_EXCERPT = 280;
|
|
@@ -11,9 +12,13 @@ const emptyCommunityMail = {
|
|
|
11
12
|
communityMembersAmong: async () => [],
|
|
12
13
|
spaceOpenersAmong: async () => [],
|
|
13
14
|
spaceOpeners: async () => [],
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
holders: async () => [],
|
|
16
|
+
role: async () => null,
|
|
17
|
+
heldOnOrg: async () => [],
|
|
18
|
+
orgName: async () => null,
|
|
19
|
+
contentInfo: async () => null,
|
|
16
20
|
displayName: async () => "A member",
|
|
21
|
+
postText: (body) => body,
|
|
17
22
|
followerIds: async () => [],
|
|
18
23
|
comment: async () => null,
|
|
19
24
|
post: async () => null,
|
|
@@ -24,21 +29,31 @@ const emptyCommunityMail = {
|
|
|
24
29
|
memberCommunityTitles: async () => [],
|
|
25
30
|
recentMessageRecipients: async () => []
|
|
26
31
|
};
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
32
|
+
async function communityModerators(reads, orgId, communityId) {
|
|
33
|
+
const holders = await reads.holders(orgId, "communities:moderate", communityId);
|
|
34
|
+
return holders.length === 0 ? [] : reads.communityMembersAmong(orgId, communityId, holders);
|
|
35
|
+
}
|
|
36
|
+
function contributes(permissions, contentType) {
|
|
37
|
+
const expanded = [...withImplied(permissions)];
|
|
38
|
+
return new Set(contentType === null ? expanded : expanded.filter((permission) => PERMISSIONS[permission].about === contentType));
|
|
39
|
+
}
|
|
40
|
+
async function roleGain(reads, orgId, assigned) {
|
|
41
|
+
const next = await reads.role(orgId, assigned.roleKey);
|
|
42
|
+
if (!next) return null;
|
|
43
|
+
const before = contributes((assigned.previousRoleKey ? await reads.role(orgId, assigned.previousRoleKey) : null)?.permissions ?? [], assigned.contentType);
|
|
44
|
+
const offered = [...contributes(next.permissions, assigned.contentType)].filter((permission) => !before.has(permission));
|
|
45
|
+
const fromOrg = assigned.contentId === null || offered.length === 0 ? [] : await reads.heldOnOrg(orgId, assigned.orgUserId, offered);
|
|
46
|
+
if (!offered.some((permission) => !fromOrg.includes(permission))) return null;
|
|
47
|
+
const place = assigned.contentId === null ? `in ${await reads.orgName(orgId) ?? "the organization"}` : `of ${(await reads.contentInfo(orgId, { contentId: assigned.contentId }))?.title ?? "an item"}`;
|
|
48
|
+
return {
|
|
49
|
+
roleName: next.name,
|
|
50
|
+
place,
|
|
51
|
+
contentId: assigned.contentId,
|
|
52
|
+
contentType: assigned.contentType
|
|
53
|
+
};
|
|
54
|
+
}
|
|
40
55
|
function actorOf(event) {
|
|
41
|
-
const value = event.metadata?.
|
|
56
|
+
const value = event.metadata?.actorId;
|
|
42
57
|
return typeof value === "string" ? value : void 0;
|
|
43
58
|
}
|
|
44
59
|
function previousStatusOf(event) {
|
|
@@ -53,13 +68,6 @@ function seededCommunityId(deps) {
|
|
|
53
68
|
const id = deps.actionInput["communityId"];
|
|
54
69
|
return typeof id === "string" ? id : null;
|
|
55
70
|
}
|
|
56
|
-
function firedAtOf(event) {
|
|
57
|
-
if (typeof event.data === "object" && event.data !== null && "firedAt" in event.data) {
|
|
58
|
-
const firedAt = event.data.firedAt;
|
|
59
|
-
if (typeof firedAt === "string") return new Date(firedAt);
|
|
60
|
-
}
|
|
61
|
-
return new Date(event.occurredAt);
|
|
62
|
-
}
|
|
63
71
|
async function address(deps, event, orgId, orgUserIds, params) {
|
|
64
72
|
const actor = actorOf(event);
|
|
65
73
|
const wanted = [...new Set(orgUserIds)].filter((id) => id !== actor);
|
|
@@ -85,6 +93,6 @@ async function address(deps, event, orgId, orgUserIds, params) {
|
|
|
85
93
|
return recipients;
|
|
86
94
|
}
|
|
87
95
|
//#endregion
|
|
88
|
-
export { MESSAGE_BATCH_MS, MESSAGE_EXCERPT, POST_EXCERPT, actorOf, address,
|
|
96
|
+
export { MESSAGE_BATCH_MS, MESSAGE_EXCERPT, POST_EXCERPT, actorOf, address, communityModerators, emptyCommunityMail, excerpt, previousStatusOf, roleGain, seededCommunityId };
|
|
89
97
|
|
|
90
98
|
//# sourceMappingURL=community-mail.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"community-mail.js","names":[],"sources":["../../src/automations/community-mail.ts"],"sourcesContent":["import type { GrantEntitlementInput } from '../entitlements/index.js';\nimport type { NotificationDesk } from '../identity/index.js';\nimport type {\n ChatStatus,\n EmailTemplateId,\n EmailTemplateParams,\n JsonValue,\n} from '../types/index.js';\nimport type { Mailer, MailerLookups } from '../shared/mailer.js';\nimport type { CredentialStore, DomainEvent, Logger } from '../shared/ports.js';\nimport { noopLogger } from '../shared/logger.js';\nimport type { WebhookSender } from './ports.js';\n\nexport const MESSAGE_EXCERPT = 140;\nexport const POST_EXCERPT = 280;\nexport const MESSAGE_BATCH_MS = 15 * 60 * 1000;\n\nexport interface EmailRecipient<K extends EmailTemplateId = EmailTemplateId> {\n to: string;\n orgUserId: string;\n userId: string;\n params: EmailTemplateParams[K];\n}\n\nexport interface CommunityMailReads {\n communityTitle(orgId: string, communityId: string): Promise<string | null>;\n space(\n orgId: string,\n spaceId: string,\n ): Promise<{ name: string; communityId: string; capacity: number | null } | null>;\n openSpaceNames(orgId: string, orgUserId: string, communityId: string): Promise<string[]>;\n communitiesOpenedBy(\n orgId: string,\n grant: { contentId: string | null; bundleId: string | null },\n ): Promise<string[]>;\n communityMembersAmong(orgId: string, communityId: string, ids: string[]): Promise<string[]>;\n spaceOpenersAmong(orgId: string, spaceId: string, ids: string[]): Promise<string[]>;\n spaceOpeners(orgId: string, spaceId: string): Promise<string[]>;\n moderationAudience(orgId: string, communityId: string): Promise<string[]>;\n staffIds(orgId: string): Promise<string[]>;\n displayName(orgId: string, orgUserId: string): Promise<string>;\n followerIds(orgId: string, postId: string): Promise<string[]>;\n comment(\n orgId: string,\n commentId: string,\n ): Promise<{ orgUserId: string; postId: string | null; body: string } | null>;\n post(\n orgId: string,\n postId: string,\n ): Promise<{\n title: string;\n body: string;\n spaceId: string;\n orgUserId: string;\n communityId: string;\n } | null>;\n chat(\n orgId: string,\n chatId: string,\n ): Promise<{\n communityId: string;\n kind: 'direct' | 'group';\n status: ChatStatus;\n createdBy: string | null;\n participants: {\n orgUserId: string;\n muted: boolean;\n mutedUntil: Date | null;\n leftAt: Date | null;\n }[];\n } | null>;\n latestMessageBody(orgId: string, chatId: string): Promise<string | null>;\n pairBlocked(orgId: string, communityId: string, a: string, b: string): Promise<boolean>;\n activeMemberCount(orgId: string, spaceId: string): Promise<number>;\n memberCommunityTitles(orgId: string, orgUserId: string): Promise<{ id: string; title: string }[]>;\n recentMessageRecipients(\n orgId: string,\n automationId: string,\n chatId: string,\n since: Date,\n ): Promise<string[]>;\n}\n\nexport interface ScheduledWork {\n publishDuePosts(orgId: string, now: Date): Promise<void>;\n expireDueGrants(orgId: string, now: Date): Promise<void>;\n sendDigest(orgId: string, now: Date): Promise<void>;\n hasDuePosts(orgId: string, now: Date): Promise<boolean>;\n hasDueGrants(orgId: string, now: Date): Promise<boolean>;\n removeUnattachedUploads(orgId: string, now: Date): Promise<void>;\n hasUnattachedUploads(orgId: string, now: Date): Promise<boolean>;\n}\n\nexport interface GrantWriter {\n grant(orgId: string, input: GrantEntitlementInput): Promise<unknown>;\n}\n\nexport interface ActionDeps {\n mailer: Pick<Mailer, 'send'>;\n lookups: MailerLookups;\n credentials: Pick<CredentialStore, 'reveal'>;\n webhooks: WebhookSender;\n mail: CommunityMailReads;\n grants: GrantWriter;\n desk: NotificationDesk;\n scheduledWork: ScheduledWork;\n automationId: string;\n actionInput: Record<string, JsonValue>;\n logger?: Logger;\n}\n\nexport const emptyCommunityMail: CommunityMailReads = {\n communityTitle: async () => null,\n space: async () => null,\n openSpaceNames: async () => [],\n communitiesOpenedBy: async () => [],\n communityMembersAmong: async () => [],\n spaceOpenersAmong: async () => [],\n spaceOpeners: async () => [],\n moderationAudience: async () => [],\n staffIds: async () => [],\n displayName: async () => 'A member',\n followerIds: async () => [],\n comment: async () => null,\n post: async () => null,\n chat: async () => null,\n latestMessageBody: async () => null,\n pairBlocked: async () => false,\n activeMemberCount: async () => 0,\n memberCommunityTitles: async () => [],\n recentMessageRecipients: async () => [],\n};\n\nexport const emptyNotificationDesk: NotificationDesk = {\n notify: async () => {},\n preferencesFor: async () => ({}),\n};\n\nexport const emptyScheduledWork: ScheduledWork = {\n publishDuePosts: async () => {},\n expireDueGrants: async () => {},\n sendDigest: async () => {},\n hasDuePosts: async () => false,\n hasDueGrants: async () => false,\n removeUnattachedUploads: async () => {},\n hasUnattachedUploads: async () => false,\n};\n\nexport function actorOf(event: DomainEvent): string | undefined {\n const value = event.metadata?.actorOrgUserId;\n return typeof value === 'string' ? value : undefined;\n}\n\nexport function previousStatusOf(event: DomainEvent): string | undefined {\n const value = event.metadata?.previousStatus;\n return typeof value === 'string' ? value : undefined;\n}\n\nexport function excerpt(value: string, max: number): string {\n const flat = value.replace(/\\s+/g, ' ').trim();\n return flat.length <= max ? flat : flat.slice(0, max);\n}\n\nexport function seededCommunityId(deps: ActionDeps): string | null {\n const id = deps.actionInput['communityId'];\n return typeof id === 'string' ? id : null;\n}\n\nexport function firedAtOf(event: DomainEvent): Date {\n if (typeof event.data === 'object' && event.data !== null && 'firedAt' in event.data) {\n const firedAt = (event.data as { firedAt?: unknown }).firedAt;\n if (typeof firedAt === 'string') {\n return new Date(firedAt);\n }\n }\n return new Date(event.occurredAt);\n}\n\nexport async function address<K extends EmailTemplateId>(\n deps: ActionDeps,\n event: DomainEvent,\n orgId: string,\n orgUserIds: string[],\n params: EmailTemplateParams[K],\n): Promise<EmailRecipient<K>[] | undefined> {\n const actor = actorOf(event);\n const wanted = [...new Set(orgUserIds)].filter((id) => id !== actor);\n if (wanted.length === 0) {\n return [];\n }\n const recipients: EmailRecipient<K>[] = [];\n const logger = deps.logger ?? noopLogger;\n for (const orgUserId of wanted) {\n const person = await deps.lookups.orgUserPerson(orgId, orgUserId);\n if (!person) {\n logger.warn('email skipped: recipient has no address', { orgId, orgUserId });\n continue;\n }\n recipients.push({ to: person.email, orgUserId, userId: person.userId, params });\n }\n return recipients;\n}\n"],"mappings":";;AAaA,MAAa,kBAAkB;AAC/B,MAAa,eAAe;AAC5B,MAAa,mBAAmB,MAAU;AAgG1C,MAAa,qBAAyC;CACpD,gBAAgB,YAAY;CAC5B,OAAO,YAAY;CACnB,gBAAgB,YAAY,CAAC;CAC7B,qBAAqB,YAAY,CAAC;CAClC,uBAAuB,YAAY,CAAC;CACpC,mBAAmB,YAAY,CAAC;CAChC,cAAc,YAAY,CAAC;CAC3B,oBAAoB,YAAY,CAAC;CACjC,UAAU,YAAY,CAAC;CACvB,aAAa,YAAY;CACzB,aAAa,YAAY,CAAC;CAC1B,SAAS,YAAY;CACrB,MAAM,YAAY;CAClB,MAAM,YAAY;CAClB,mBAAmB,YAAY;CAC/B,aAAa,YAAY;CACzB,mBAAmB,YAAY;CAC/B,uBAAuB,YAAY,CAAC;CACpC,yBAAyB,YAAY,CAAC;AACxC;AAEA,MAAa,wBAA0C;CACrD,QAAQ,YAAY,CAAC;CACrB,gBAAgB,aAAa,CAAC;AAChC;AAEA,MAAa,qBAAoC;CAC/C,iBAAiB,YAAY,CAAC;CAC9B,iBAAiB,YAAY,CAAC;CAC9B,YAAY,YAAY,CAAC;CACzB,aAAa,YAAY;CACzB,cAAc,YAAY;CAC1B,yBAAyB,YAAY,CAAC;CACtC,sBAAsB,YAAY;AACpC;AAEA,SAAgB,QAAQ,OAAwC;CAC9D,MAAM,QAAQ,MAAM,UAAU;CAC9B,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAA;AAC7C;AAEA,SAAgB,iBAAiB,OAAwC;CACvE,MAAM,QAAQ,MAAM,UAAU;CAC9B,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAA;AAC7C;AAEA,SAAgB,QAAQ,OAAe,KAAqB;CAC1D,MAAM,OAAO,MAAM,QAAQ,QAAQ,GAAG,CAAC,CAAC,KAAK;CAC7C,OAAO,KAAK,UAAU,MAAM,OAAO,KAAK,MAAM,GAAG,GAAG;AACtD;AAEA,SAAgB,kBAAkB,MAAiC;CACjE,MAAM,KAAK,KAAK,YAAY;CAC5B,OAAO,OAAO,OAAO,WAAW,KAAK;AACvC;AAEA,SAAgB,UAAU,OAA0B;CAClD,IAAI,OAAO,MAAM,SAAS,YAAY,MAAM,SAAS,QAAQ,aAAa,MAAM,MAAM;EACpF,MAAM,UAAW,MAAM,KAA+B;EACtD,IAAI,OAAO,YAAY,UACrB,OAAO,IAAI,KAAK,OAAO;CAE3B;CACA,OAAO,IAAI,KAAK,MAAM,UAAU;AAClC;AAEA,eAAsB,QACpB,MACA,OACA,OACA,YACA,QAC0C;CAC1C,MAAM,QAAQ,QAAQ,KAAK;CAC3B,MAAM,SAAS,CAAC,GAAG,IAAI,IAAI,UAAU,CAAC,CAAC,CAAC,QAAQ,OAAO,OAAO,KAAK;CACnE,IAAI,OAAO,WAAW,GACpB,OAAO,CAAC;CAEV,MAAM,aAAkC,CAAC;CACzC,MAAM,SAAS,KAAK,UAAU;CAC9B,KAAK,MAAM,aAAa,QAAQ;EAC9B,MAAM,SAAS,MAAM,KAAK,QAAQ,cAAc,OAAO,SAAS;EAChE,IAAI,CAAC,QAAQ;GACX,OAAO,KAAK,2CAA2C;IAAE;IAAO;GAAU,CAAC;GAC3E;EACF;EACA,WAAW,KAAK;GAAE,IAAI,OAAO;GAAO;GAAW,QAAQ,OAAO;GAAQ;EAAO,CAAC;CAChF;CACA,OAAO;AACT"}
|
|
1
|
+
{"version":3,"file":"community-mail.js","names":[],"sources":["../../src/automations/community-mail.ts"],"sourcesContent":["import type { GrantEntitlementInput } from '../entitlements/index.js';\nimport type {\n ChatStatus,\n ContentType,\n EmailTemplateId,\n EmailTemplateParams,\n JsonValue,\n Permission,\n} from '../types/index.js';\nimport { PERMISSIONS, withImplied } from '../organizations/index.js';\nimport type { Mailer, MailerLookups } from '../shared/mailer.js';\nimport type { CredentialStore, DomainEvent, Logger } from '../shared/ports.js';\nimport { noopLogger } from '../shared/logger.js';\nimport type { WebhookSender } from './ports.js';\n\nexport const MESSAGE_EXCERPT = 140;\nexport const POST_EXCERPT = 280;\nexport const MESSAGE_BATCH_MS = 15 * 60 * 1000;\n\nexport interface EmailRecipient<K extends EmailTemplateId = EmailTemplateId> {\n to: string;\n orgUserId: string;\n userId: string;\n params: EmailTemplateParams[K];\n}\n\nexport interface CommunityMailReads {\n communityTitle(orgId: string, communityId: string): Promise<string | null>;\n space(\n orgId: string,\n spaceId: string,\n ): Promise<{ name: string; communityId: string; capacity: number | null } | null>;\n openSpaceNames(orgId: string, orgUserId: string, communityId: string): Promise<string[]>;\n communitiesOpenedBy(\n orgId: string,\n grant: { contentId: string | null; bundleId: string | null },\n ): Promise<string[]>;\n communityMembersAmong(orgId: string, communityId: string, ids: string[]): Promise<string[]>;\n spaceOpenersAmong(orgId: string, spaceId: string, ids: string[]): Promise<string[]>;\n spaceOpeners(orgId: string, spaceId: string): Promise<string[]>;\n holders(orgId: string, permission: Permission, on?: string): Promise<string[]>;\n role(orgId: string, roleKey: string): Promise<{ name: string; permissions: Permission[] } | null>;\n heldOnOrg(orgId: string, orgUserId: string, permissions: Permission[]): Promise<Permission[]>;\n orgName(orgId: string): Promise<string | null>;\n contentInfo(\n orgId: string,\n item: { contentId: string } | { bundleId: string },\n ): Promise<{ title: string; type: string } | null>;\n displayName(orgId: string, orgUserId: string): Promise<string>;\n postText(body: string): string;\n followerIds(orgId: string, postId: string): Promise<string[]>;\n comment(\n orgId: string,\n commentId: string,\n ): Promise<{\n orgUserId: string;\n postId: string | null;\n activityId: string | null;\n parentId: string | null;\n body: string;\n } | null>;\n post(\n orgId: string,\n postId: string,\n ): Promise<{\n title: string;\n body: string;\n spaceId: string;\n orgUserId: string;\n communityId: string;\n } | null>;\n chat(\n orgId: string,\n chatId: string,\n ): Promise<{\n communityId: string;\n kind: 'direct' | 'group';\n status: ChatStatus;\n createdBy: string | null;\n participants: {\n orgUserId: string;\n muted: boolean;\n mutedUntil: Date | null;\n leftAt: Date | null;\n }[];\n } | null>;\n latestMessageBody(orgId: string, chatId: string): Promise<string | null>;\n pairBlocked(orgId: string, communityId: string, a: string, b: string): Promise<boolean>;\n activeMemberCount(orgId: string, spaceId: string): Promise<number>;\n memberCommunityTitles(orgId: string, orgUserId: string): Promise<{ id: string; title: string }[]>;\n recentMessageRecipients(\n orgId: string,\n automationId: string,\n chatId: string,\n since: Date,\n ): Promise<string[]>;\n}\n\nexport interface GrantWriter {\n grant(orgId: string, input: GrantEntitlementInput): Promise<unknown>;\n}\n\nexport interface ActionDeps {\n mailer: Pick<Mailer, 'send'>;\n lookups: MailerLookups;\n credentials: Pick<CredentialStore, 'reveal'>;\n webhooks: WebhookSender;\n mail: CommunityMailReads;\n grants: GrantWriter;\n automationId: string;\n actionInput: Record<string, JsonValue>;\n logger?: Logger;\n}\n\nexport const emptyCommunityMail: CommunityMailReads = {\n communityTitle: async () => null,\n space: async () => null,\n openSpaceNames: async () => [],\n communitiesOpenedBy: async () => [],\n communityMembersAmong: async () => [],\n spaceOpenersAmong: async () => [],\n spaceOpeners: async () => [],\n holders: async () => [],\n role: async () => null,\n heldOnOrg: async () => [],\n orgName: async () => null,\n contentInfo: async () => null,\n displayName: async () => 'A member',\n postText: (body) => body,\n followerIds: async () => [],\n comment: async () => null,\n post: async () => null,\n chat: async () => null,\n latestMessageBody: async () => null,\n pairBlocked: async () => false,\n activeMemberCount: async () => 0,\n memberCommunityTitles: async () => [],\n recentMessageRecipients: async () => [],\n};\n\nexport async function communityModerators(\n reads: Pick<CommunityMailReads, 'holders' | 'communityMembersAmong'>,\n orgId: string,\n communityId: string,\n): Promise<string[]> {\n const holders = await reads.holders(orgId, 'communities:moderate', communityId);\n return holders.length === 0 ? [] : reads.communityMembersAmong(orgId, communityId, holders);\n}\n\nexport interface RoleGain {\n roleName: string;\n place: string;\n contentId: string | null;\n contentType: ContentType | null;\n}\n\nfunction contributes(\n permissions: readonly Permission[],\n contentType: ContentType | null,\n): Set<Permission> {\n const expanded = [...withImplied(permissions)];\n return new Set(\n contentType === null\n ? expanded\n : expanded.filter((permission) => PERMISSIONS[permission].about === contentType),\n );\n}\n\nexport async function roleGain(\n reads: Pick<CommunityMailReads, 'role' | 'orgName' | 'contentInfo' | 'heldOnOrg'>,\n orgId: string,\n assigned: {\n orgUserId: string;\n roleKey: string;\n previousRoleKey: string | null;\n contentId: string | null;\n contentType: ContentType | null;\n },\n): Promise<RoleGain | null> {\n const next = await reads.role(orgId, assigned.roleKey);\n if (!next) {\n return null;\n }\n const previous = assigned.previousRoleKey\n ? await reads.role(orgId, assigned.previousRoleKey)\n : null;\n const before = contributes(previous?.permissions ?? [], assigned.contentType);\n const offered = [...contributes(next.permissions, assigned.contentType)].filter(\n (permission) => !before.has(permission),\n );\n const fromOrg =\n assigned.contentId === null || offered.length === 0\n ? []\n : await reads.heldOnOrg(orgId, assigned.orgUserId, offered);\n const gained = offered.some((permission) => !fromOrg.includes(permission));\n if (!gained) {\n return null;\n }\n const place =\n assigned.contentId === null\n ? `in ${(await reads.orgName(orgId)) ?? 'the organization'}`\n : `of ${(await reads.contentInfo(orgId, { contentId: assigned.contentId }))?.title ?? 'an item'}`;\n return {\n roleName: next.name,\n place,\n contentId: assigned.contentId,\n contentType: assigned.contentType,\n };\n}\n\nexport function actorOf(event: DomainEvent): string | undefined {\n const value = event.metadata?.actorId;\n return typeof value === 'string' ? value : undefined;\n}\n\nexport function previousStatusOf(event: DomainEvent): string | undefined {\n const value = event.metadata?.previousStatus;\n return typeof value === 'string' ? value : undefined;\n}\n\nexport function excerpt(value: string, max: number): string {\n const flat = value.replace(/\\s+/g, ' ').trim();\n return flat.length <= max ? flat : flat.slice(0, max);\n}\n\nexport function seededCommunityId(deps: ActionDeps): string | null {\n const id = deps.actionInput['communityId'];\n return typeof id === 'string' ? id : null;\n}\n\nexport async function address<K extends EmailTemplateId>(\n deps: ActionDeps,\n event: DomainEvent,\n orgId: string,\n orgUserIds: string[],\n params: EmailTemplateParams[K],\n): Promise<EmailRecipient<K>[] | undefined> {\n const actor = actorOf(event);\n const wanted = [...new Set(orgUserIds)].filter((id) => id !== actor);\n if (wanted.length === 0) {\n return [];\n }\n const recipients: EmailRecipient<K>[] = [];\n const logger = deps.logger ?? noopLogger;\n for (const orgUserId of wanted) {\n const person = await deps.lookups.orgUserPerson(orgId, orgUserId);\n if (!person) {\n logger.warn('email skipped: recipient has no address', { orgId, orgUserId });\n continue;\n }\n recipients.push({ to: person.email, orgUserId, userId: person.userId, params });\n }\n return recipients;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Permission } from "../types/schemas/organizations.js";
|
|
2
|
+
import "../types/index.js";
|
|
3
|
+
import { Actor } from "../shared/actor.js";
|
|
4
|
+
//#region src/automations/event-permissions.d.ts
|
|
5
|
+
declare function eventReadPermissions(type: string): readonly Permission[];
|
|
6
|
+
declare function assertReadsEvents(actor: Actor, types: readonly string[]): void;
|
|
7
|
+
declare function assertGrantsAllowed(actor: Actor, actions: readonly {
|
|
8
|
+
type: string;
|
|
9
|
+
}[]): void;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { assertGrantsAllowed, assertReadsEvents, eventReadPermissions };
|
|
12
|
+
//# sourceMappingURL=event-permissions.d.ts.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ForbiddenError } from "../shared/errors.js";
|
|
2
|
+
//#region src/automations/event-permissions.ts
|
|
3
|
+
const BY_PREFIX = [
|
|
4
|
+
["organization.role.", ["roles:read", "people:read"]],
|
|
5
|
+
["organization.ownership.", ["people:read"]],
|
|
6
|
+
["organization.created", ["organizations:read"]],
|
|
7
|
+
["organization.updated", ["organizations:read"]],
|
|
8
|
+
["organization.deleted", ["organizations:read"]],
|
|
9
|
+
["organization.", ["people:read"]],
|
|
10
|
+
["identity.", ["people:read"]],
|
|
11
|
+
["entitlement.space_member.", ["communities:moderate"]],
|
|
12
|
+
["entitlement.", ["access:read"]],
|
|
13
|
+
["progress.", ["progress:read"]],
|
|
14
|
+
["content.course.", ["courses:read"]],
|
|
15
|
+
["content.download.", ["downloads:read"]],
|
|
16
|
+
["content.bundle.", ["bundles:read"]],
|
|
17
|
+
["content.community.", ["communities:read"]],
|
|
18
|
+
["content.event.", ["events:read"]],
|
|
19
|
+
["discussion.comment.", ["courses:moderate", "communities:moderate"]],
|
|
20
|
+
["discussion.", ["communities:moderate"]],
|
|
21
|
+
["asset.", ["media:read"]],
|
|
22
|
+
["integration.", ["integrations:read"]],
|
|
23
|
+
["automation.", ["automations:read"]]
|
|
24
|
+
];
|
|
25
|
+
function eventReadPermissions(type) {
|
|
26
|
+
return BY_PREFIX.find(([prefix]) => type.startsWith(prefix))?.[1] ?? ["people:read"];
|
|
27
|
+
}
|
|
28
|
+
function assertReadsEvents(actor, types) {
|
|
29
|
+
const missing = [...new Set(types.flatMap((type) => eventReadPermissions(type)))].filter((permission) => !actor.permissions.has(permission));
|
|
30
|
+
if (missing.length > 0) throw Object.assign(new ForbiddenError(`You need ${missing.join(", ")} to receive these events`), { permission: missing[0] });
|
|
31
|
+
}
|
|
32
|
+
function assertGrantsAllowed(actor, actions) {
|
|
33
|
+
if (actions.some((action) => action.type === "grantAccess")) actor.permissions.require("access:write");
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
export { assertGrantsAllowed, assertReadsEvents, eventReadPermissions };
|
|
37
|
+
|
|
38
|
+
//# sourceMappingURL=event-permissions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-permissions.js","names":[],"sources":["../../src/automations/event-permissions.ts"],"sourcesContent":["import type { Permission } from '../types/index.js';\nimport type { Actor } from '../shared/actor.js';\nimport { ForbiddenError } from '../shared/errors.js';\n\nconst BY_PREFIX: ReadonlyArray<[string, readonly Permission[]]> = [\n ['organization.role.', ['roles:read', 'people:read']],\n ['organization.ownership.', ['people:read']],\n ['organization.created', ['organizations:read']],\n ['organization.updated', ['organizations:read']],\n ['organization.deleted', ['organizations:read']],\n ['organization.', ['people:read']],\n ['identity.', ['people:read']],\n ['entitlement.space_member.', ['communities:moderate']],\n ['entitlement.', ['access:read']],\n ['progress.', ['progress:read']],\n ['content.course.', ['courses:read']],\n ['content.download.', ['downloads:read']],\n ['content.bundle.', ['bundles:read']],\n ['content.community.', ['communities:read']],\n ['content.event.', ['events:read']],\n ['discussion.comment.', ['courses:moderate', 'communities:moderate']],\n ['discussion.', ['communities:moderate']],\n ['asset.', ['media:read']],\n ['integration.', ['integrations:read']],\n ['automation.', ['automations:read']],\n];\n\nexport function eventReadPermissions(type: string): readonly Permission[] {\n return BY_PREFIX.find(([prefix]) => type.startsWith(prefix))?.[1] ?? ['people:read'];\n}\n\nexport function assertReadsEvents(actor: Actor, types: readonly string[]): void {\n const missing = [...new Set(types.flatMap((type) => eventReadPermissions(type)))].filter(\n (permission) => !actor.permissions.has(permission),\n );\n if (missing.length > 0) {\n throw Object.assign(\n new ForbiddenError(`You need ${missing.join(', ')} to receive these events`),\n {\n permission: missing[0],\n },\n );\n }\n}\n\nexport function assertGrantsAllowed(actor: Actor, actions: readonly { type: string }[]): void {\n if (actions.some((action) => action.type === 'grantAccess')) {\n actor.permissions.require('access:write');\n }\n}\n"],"mappings":";;AAIA,MAAM,YAA4D;CAChE,CAAC,sBAAsB,CAAC,cAAc,aAAa,CAAC;CACpD,CAAC,2BAA2B,CAAC,aAAa,CAAC;CAC3C,CAAC,wBAAwB,CAAC,oBAAoB,CAAC;CAC/C,CAAC,wBAAwB,CAAC,oBAAoB,CAAC;CAC/C,CAAC,wBAAwB,CAAC,oBAAoB,CAAC;CAC/C,CAAC,iBAAiB,CAAC,aAAa,CAAC;CACjC,CAAC,aAAa,CAAC,aAAa,CAAC;CAC7B,CAAC,6BAA6B,CAAC,sBAAsB,CAAC;CACtD,CAAC,gBAAgB,CAAC,aAAa,CAAC;CAChC,CAAC,aAAa,CAAC,eAAe,CAAC;CAC/B,CAAC,mBAAmB,CAAC,cAAc,CAAC;CACpC,CAAC,qBAAqB,CAAC,gBAAgB,CAAC;CACxC,CAAC,mBAAmB,CAAC,cAAc,CAAC;CACpC,CAAC,sBAAsB,CAAC,kBAAkB,CAAC;CAC3C,CAAC,kBAAkB,CAAC,aAAa,CAAC;CAClC,CAAC,uBAAuB,CAAC,oBAAoB,sBAAsB,CAAC;CACpE,CAAC,eAAe,CAAC,sBAAsB,CAAC;CACxC,CAAC,UAAU,CAAC,YAAY,CAAC;CACzB,CAAC,gBAAgB,CAAC,mBAAmB,CAAC;CACtC,CAAC,eAAe,CAAC,kBAAkB,CAAC;AACtC;AAEA,SAAgB,qBAAqB,MAAqC;CACxE,OAAO,UAAU,MAAM,CAAC,YAAY,KAAK,WAAW,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,aAAa;AACrF;AAEA,SAAgB,kBAAkB,OAAc,OAAgC;CAC9E,MAAM,UAAU,CAAC,GAAG,IAAI,IAAI,MAAM,SAAS,SAAS,qBAAqB,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,QAC/E,eAAe,CAAC,MAAM,YAAY,IAAI,UAAU,CACnD;CACA,IAAI,QAAQ,SAAS,GACnB,MAAM,OAAO,OACX,IAAI,eAAe,YAAY,QAAQ,KAAK,IAAI,EAAE,yBAAyB,GAC3E,EACE,YAAY,QAAQ,GACtB,CACF;AAEJ;AAEA,SAAgB,oBAAoB,OAAc,SAA4C;CAC5F,IAAI,QAAQ,MAAM,WAAW,OAAO,SAAS,aAAa,GACxD,MAAM,YAAY,QAAQ,cAAc;AAE5C"}
|