@headless-lms/core 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/assets/file-urls.d.ts +5 -19
  2. package/dist/assets/file-urls.js +2 -23
  3. package/dist/assets/file-urls.js.map +1 -1
  4. package/dist/assets/index.d.ts +3 -3
  5. package/dist/assets/ports.d.ts +9 -10
  6. package/dist/assets/service.d.ts +10 -13
  7. package/dist/assets/service.js +41 -62
  8. package/dist/assets/service.js.map +1 -1
  9. package/dist/automations/index.d.ts +4 -4
  10. package/dist/automations/notifications/delivery.d.ts +3 -3
  11. package/dist/automations/notifications/delivery.js +1 -1
  12. package/dist/automations/notifications/delivery.js.map +1 -1
  13. package/dist/automations/ports.d.ts +2 -2
  14. package/dist/automations/service.d.ts +1 -8
  15. package/dist/automations/service.js +0 -8
  16. package/dist/automations/service.js.map +1 -1
  17. package/dist/content/service.d.ts +4 -1
  18. package/dist/content/service.js +52 -29
  19. package/dist/content/service.js.map +1 -1
  20. package/dist/discussion/chats.d.ts +1 -2
  21. package/dist/discussion/chats.js +8 -17
  22. package/dist/discussion/chats.js.map +1 -1
  23. package/dist/discussion/ports.d.ts +0 -6
  24. package/dist/discussion/ports.js.map +1 -1
  25. package/dist/discussion/posts.d.ts +1 -4
  26. package/dist/discussion/posts.js +8 -21
  27. package/dist/discussion/posts.js.map +1 -1
  28. package/dist/entitlements/events.d.ts +33 -33
  29. package/dist/identity/events.d.ts +2 -2
  30. package/dist/integrations/events.d.ts +3 -3
  31. package/dist/organizations/events.d.ts +2 -2
  32. package/dist/reporting/member/ports.d.ts +1 -10
  33. package/dist/reporting/member/service.d.ts +2 -11
  34. package/dist/reporting/member/service.js +12 -62
  35. package/dist/reporting/member/service.js.map +1 -1
  36. package/dist/types/automations.d.ts +5 -2
  37. package/dist/types/index.d.ts +2 -2
  38. package/dist/types/ports.d.ts +4 -2
  39. package/dist/types/ports.js.map +1 -1
  40. package/dist/types/schemas/content.d.ts +2 -2
  41. package/dist/types/schemas/discussion.d.ts +14 -14
  42. package/dist/types/schemas/entitlements.d.ts +8 -8
  43. package/dist/types/schemas/notifications.d.ts +3 -3
  44. package/package.json +1 -1
@@ -22,9 +22,9 @@ declare const identityEvents: {
22
22
  id: import("zod").ZodString;
23
23
  createdAt: import("zod").ZodCoercedDate<unknown>;
24
24
  updatedAt: import("zod").ZodCoercedDate<unknown>;
25
+ email: import("zod").ZodEmail;
25
26
  firstName: import("zod").ZodNullable<import("zod").ZodString>;
26
27
  lastName: import("zod").ZodNullable<import("zod").ZodString>;
27
- email: import("zod").ZodEmail;
28
28
  }, import("zod/v4/core").$strip>>;
29
29
  userUpdated: RuntimeEventDefinition<VersionedDomainEvent<{
30
30
  id: string;
@@ -46,9 +46,9 @@ declare const identityEvents: {
46
46
  id: import("zod").ZodString;
47
47
  createdAt: import("zod").ZodCoercedDate<unknown>;
48
48
  updatedAt: import("zod").ZodCoercedDate<unknown>;
49
+ email: import("zod").ZodEmail;
49
50
  firstName: import("zod").ZodNullable<import("zod").ZodString>;
50
51
  lastName: import("zod").ZodNullable<import("zod").ZodString>;
51
- email: import("zod").ZodEmail;
52
52
  }, import("zod/v4/core").$strip>>;
53
53
  userDeleted: RuntimeEventDefinition<VersionedDomainEvent<{
54
54
  id: string;
@@ -26,9 +26,9 @@ declare const integrationEvents: {
26
26
  id: import("zod").ZodString;
27
27
  createdAt: import("zod").ZodCoercedDate<unknown>;
28
28
  updatedAt: import("zod").ZodCoercedDate<unknown>;
29
- active: import("zod").ZodBoolean;
30
29
  integrationId: import("zod").ZodString;
31
30
  config: import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodType<JsonValue, unknown, import("zod/v4/core").$ZodTypeInternals<JsonValue, unknown>>>;
31
+ active: import("zod").ZodBoolean;
32
32
  }, import("zod/v4/core").$strip>>;
33
33
  connectionUpdated: RuntimeEventDefinition<VersionedDomainEvent<{
34
34
  orgId: string;
@@ -53,9 +53,9 @@ declare const integrationEvents: {
53
53
  id: import("zod").ZodString;
54
54
  createdAt: import("zod").ZodCoercedDate<unknown>;
55
55
  updatedAt: import("zod").ZodCoercedDate<unknown>;
56
- active: import("zod").ZodBoolean;
57
56
  integrationId: import("zod").ZodString;
58
57
  config: import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodType<JsonValue, unknown, import("zod/v4/core").$ZodTypeInternals<JsonValue, unknown>>>;
58
+ active: import("zod").ZodBoolean;
59
59
  }, import("zod/v4/core").$strip>>;
60
60
  connectionRemoved: RuntimeEventDefinition<VersionedDomainEvent<{
61
61
  orgId: string;
@@ -80,9 +80,9 @@ declare const integrationEvents: {
80
80
  id: import("zod").ZodString;
81
81
  createdAt: import("zod").ZodCoercedDate<unknown>;
82
82
  updatedAt: import("zod").ZodCoercedDate<unknown>;
83
- active: import("zod").ZodBoolean;
84
83
  integrationId: import("zod").ZodString;
85
84
  config: import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodType<JsonValue, unknown, import("zod/v4/core").$ZodTypeInternals<JsonValue, unknown>>>;
85
+ active: import("zod").ZodBoolean;
86
86
  }, import("zod/v4/core").$strip>>;
87
87
  };
88
88
  type ConnectionCreated = EventOf<typeof integrationEvents.connectionCreated>;
@@ -218,8 +218,8 @@ declare const organizationEvents: {
218
218
  updatedAt: z.ZodCoercedDate<unknown>;
219
219
  email: z.ZodEmail;
220
220
  expiresAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
221
- roleKey: z.ZodString;
222
221
  invitedBy: z.ZodString;
222
+ roleKey: z.ZodString;
223
223
  }, z.core.$strip>>;
224
224
  inviteAccepted: RuntimeEventDefinition<VersionedDomainEvent<{
225
225
  id: string;
@@ -256,8 +256,8 @@ declare const organizationEvents: {
256
256
  updatedAt: z.ZodCoercedDate<unknown>;
257
257
  email: z.ZodEmail;
258
258
  expiresAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
259
- roleKey: z.ZodString;
260
259
  invitedBy: z.ZodString;
260
+ roleKey: z.ZodString;
261
261
  }, z.core.$strip>>;
262
262
  inviteCanceled: RuntimeEventDefinition<VersionedDomainEvent<{
263
263
  id: string;
@@ -1,5 +1,5 @@
1
1
  import { Page } from "../../types/shared.js";
2
- import { ActivityView, CourseView, DownloadFile, DownloadView, EventAttendee, MemberEvent, MemberEventSummary, MemberEventsQuery, Module, SpaceImage } from "../../types/schemas/content.js";
2
+ import { ActivityView, CourseView, DownloadFile, DownloadView, EventAttendee, MemberEvent, MemberEventSummary, MemberEventsQuery, Module } from "../../types/schemas/content.js";
3
3
  import { NotificationFilter } from "../../types/notifications.js";
4
4
  import "../../types/index.js";
5
5
  import { Actor } from "../../shared/actor.js";
@@ -21,10 +21,6 @@ interface MemberReportService {
21
21
  download: DownloadView;
22
22
  assets: DownloadFile[];
23
23
  }>;
24
- /** Signed URL of a course, download or community thumbnail: published, or the item's read
25
- * permission. An event's when the caller can read the event. */
26
- thumbnailFile(owner: 'course' | 'download' | 'community' | 'event', id: string, actor: Actor): Promise<string>;
27
- spaceImageFile(spaceId: string, image: SpaceImage, actor: Actor): Promise<string>;
28
24
  getEvent(eventId: string, actor: Actor): Promise<MemberEvent>;
29
25
  listEvents(query: MemberEventsQuery, actor: Actor): Promise<Page<MemberEventSummary>>;
30
26
  eventCalendar(eventId: string, actor: Actor): Promise<string>;
@@ -32,11 +28,6 @@ interface MemberReportService {
32
28
  page: number;
33
29
  pageSize: number;
34
30
  }, actor: Actor): Promise<Page<EventAttendee>>;
35
- eventFile(eventId: string, assetId: string, actor: Actor): Promise<string>;
36
- /** Signed URL of an asset the activity links, for a person who can open its course. */
37
- activityFile(activityId: string, assetId: string, actor: Actor): Promise<string>;
38
- /** Signed download URL of a file in a download the person can open. */
39
- downloadFile(downloadId: string, assetId: string, actor: Actor): Promise<string>;
40
31
  directory(communityId: string, query: DirectoryQuery, actor: Actor): Promise<Page<DirectoryEntry>>;
41
32
  communityHome(communityId: string, actor: Actor): Promise<CommunityHome>;
42
33
  communitySpace(communityId: string, spaceId: string, actor: Actor): Promise<CommunityHomeSpace>;
@@ -1,11 +1,10 @@
1
1
  import { Page } from "../../types/shared.js";
2
- import { ActivityView, CourseView, DownloadFile, DownloadView, EventAttendee, MemberEvent, MemberEventSummary, MemberEventsQuery, Module, SpaceImage } from "../../types/schemas/content.js";
2
+ import { ActivityView, CourseView, DownloadFile, DownloadView, EventAttendee, MemberEvent, MemberEventSummary, MemberEventsQuery, Module } from "../../types/schemas/content.js";
3
3
  import { NotificationFilter } from "../../types/notifications.js";
4
4
  import "../../types/index.js";
5
5
  import { Actor } from "../../shared/actor.js";
6
6
  import { ChannelRegistry, NotificationCategoryService } from "../../shared/ports.js";
7
7
  import { FileUrls } from "../../assets/file-urls.js";
8
- import { AssetFiles } from "../../assets/ports.js";
9
8
  import "../../assets/index.js";
10
9
  import { ContentService, ItemTitles } from "../../content/ports.js";
11
10
  import "../../content/index.js";
@@ -25,14 +24,12 @@ import { CommunityHome, CommunityHomeSpace, CommunityNotificationSettingsView, C
25
24
  import { MemberReportRepository, MemberReportService } from "./ports.js";
26
25
  //#region src/reporting/member/service.d.ts
27
26
  type MemberAccess = Pick<EntitlementsService, 'isCommunityMember' | 'openSpaceIds' | 'spaceStandings' | 'hasAccess' | 'listAccessibleContent' | 'openCommunityIds' | 'opensEverySpace' | 'everySpaceOpeners'>;
28
- type MemberContent = Pick<ContentService, 'findCourse' | 'listCourseModules' | 'listCourseActivityViews' | 'listCourseActivities' | 'listCourseActivityAssets' | 'getActivity' | 'findDownload' | 'listDownloadAssets' | 'findCommunity' | 'listSpaces' | 'getSpace' | 'findEvent' | 'findEvents' | 'eventFilesFor'>;
27
+ type MemberContent = Pick<ContentService, 'findCourse' | 'listCourseModules' | 'listCourseActivityViews' | 'listCourseActivities' | 'findDownload' | 'listDownloadAssets' | 'findCommunity' | 'listSpaces' | 'getSpace' | 'findEvent' | 'findEvents' | 'eventFilesFor'>;
29
28
  type MemberPeople = Pick<OrganizationService, 'systemActor' | 'permissionsOfMany' | 'holders'>;
30
- type Owner = 'course' | 'download' | 'community' | 'event';
31
29
  type MemberReportServiceParams = {
32
30
  repo: MemberReportRepository;
33
31
  content: MemberContent;
34
32
  progress: Pick<ProgressService, 'get' | 'listByTargets'>;
35
- files: Pick<AssetFiles, 'signFile'>;
36
33
  fileUrls: FileUrls;
37
34
  access: MemberAccess;
38
35
  people: MemberPeople;
@@ -55,7 +52,6 @@ declare class MemberReportServiceImpl implements MemberReportService {
55
52
  private readonly repo;
56
53
  private readonly content;
57
54
  private readonly progress;
58
- private readonly files;
59
55
  private readonly fileUrls;
60
56
  private readonly access;
61
57
  private readonly people;
@@ -93,10 +89,6 @@ declare class MemberReportServiceImpl implements MemberReportService {
93
89
  download: DownloadView;
94
90
  assets: DownloadFile[];
95
91
  }>;
96
- thumbnailFile(owner: Owner, id: string, actor: Actor): Promise<string>;
97
- spaceImageFile(spaceId: string, image: SpaceImage, actor: Actor): Promise<string>;
98
- activityFile(activityId: string, assetId: string, actor: Actor): Promise<string>;
99
- downloadFile(downloadId: string, assetId: string, actor: Actor): Promise<string>;
100
92
  private seeEvents;
101
93
  private requireEvent;
102
94
  private eventSummaries;
@@ -108,7 +100,6 @@ declare class MemberReportServiceImpl implements MemberReportService {
108
100
  page: number;
109
101
  pageSize: number;
110
102
  }, actor: Actor): Promise<Page<EventAttendee>>;
111
- eventFile(eventId: string, assetId: string, actor: Actor): Promise<string>;
112
103
  directory(communityId: string, query: DirectoryQuery, actor: Actor): Promise<Page<DirectoryEntry>>;
113
104
  communityHome(communityId: string, actor: Actor): Promise<CommunityHome>;
114
105
  communitySpace(communityId: string, spaceId: string, actor: Actor): Promise<CommunityHomeSpace>;
@@ -33,23 +33,11 @@ const READ_OF = {
33
33
  community: "communities:read",
34
34
  event: "events:read"
35
35
  };
36
- const THUMBNAIL_OF = {
37
- course: "Course thumbnail",
38
- download: "Download thumbnail",
39
- community: "Community thumbnail",
40
- event: "Event thumbnail"
41
- };
42
- const SPACE_IMAGE_ASSET = {
43
- cover: (space) => space.coverAssetId,
44
- background: (space) => space.backgroundAssetId,
45
- icon: (space) => space.iconAssetId
46
- };
47
36
  const EVENT_FACES = 10;
48
37
  var MemberReportServiceImpl = class {
49
38
  repo;
50
39
  content;
51
40
  progress;
52
- files;
53
41
  fileUrls;
54
42
  access;
55
43
  people;
@@ -65,7 +53,6 @@ var MemberReportServiceImpl = class {
65
53
  this.repo = params.repo;
66
54
  this.content = params.content;
67
55
  this.progress = params.progress;
68
- this.files = params.files;
69
56
  this.fileUrls = params.fileUrls;
70
57
  this.access = params.access;
71
58
  this.people = params.people;
@@ -293,36 +280,6 @@ var MemberReportServiceImpl = class {
293
280
  assets: await this.content.listDownloadAssets(downloadId, this.system(actor))
294
281
  };
295
282
  }
296
- async thumbnailFile(owner, id, actor) {
297
- const system = this.system(actor);
298
- const item = owner === "event" ? (await this.seeEvents([id], actor))[0]?.event : owner === "course" ? await this.content.findCourse(id, system) : owner === "download" ? await this.content.findDownload(id, system) : await this.content.findCommunity(id, system);
299
- if (!item?.thumbnailAssetId || owner !== "event" && item.status !== "published" && !actor.permissions.has(READ_OF[owner], id)) throw new NotFoundError(THUMBNAIL_OF[owner], id);
300
- return signed(await this.files.signFile(actor.orgId, item.thumbnailAssetId), THUMBNAIL_OF[owner], id);
301
- }
302
- async spaceImageFile(spaceId, image, actor) {
303
- const system = this.system(actor);
304
- const space = await this.content.getSpace(spaceId, system);
305
- const assetId = space ? SPACE_IMAGE_ASSET[image](space) : null;
306
- const community = space ? await this.content.findCommunity(space.communityId, system) : null;
307
- if (!space || !assetId || !community || community.status !== "published" && !actor.permissions.has("communities:read", space.communityId)) throw new NotFoundError("Space image", spaceId);
308
- return signed(await this.files.signFile(actor.orgId, assetId), "Space image", spaceId);
309
- }
310
- async activityFile(activityId, assetId, actor) {
311
- const system = this.system(actor);
312
- const activity = await this.content.getActivity(activityId, system);
313
- if (!activity) throw new NotFoundError("Lesson file", assetId);
314
- if (!actor.permissions.has("courses:read", activity.courseId)) {
315
- if (!isActivityPublished(activity.settings) || !await this.opensCourse(activity.courseId, actor)) throw new NotFoundError("Lesson file", assetId);
316
- }
317
- if (!(await this.content.listCourseActivityAssets(activity.courseId, system)).some((link) => link.activityId === activityId && link.assetId === assetId)) throw new NotFoundError("Lesson file", assetId);
318
- return signed(await this.files.signFile(actor.orgId, assetId), "Lesson file", assetId);
319
- }
320
- async downloadFile(downloadId, assetId, actor) {
321
- if (!await this.opensDownload(downloadId, actor)) throw new NotFoundError("Download file", assetId);
322
- const file = (await this.content.listDownloadAssets(downloadId, this.system(actor))).find((f) => f.assetId === assetId);
323
- if (!file) throw new NotFoundError("Download file", assetId);
324
- return signed(await this.files.signFile(actor.orgId, assetId, { saveAs: file.displayName ?? file.filename }), "Download file", assetId);
325
- }
326
283
  async seeEvents(ids, actor) {
327
284
  if (ids.length === 0) return [];
328
285
  const system = this.system(actor);
@@ -443,13 +400,6 @@ var MemberReportServiceImpl = class {
443
400
  rows: page.rows.map((row) => attendeeView(actor.orgId, row, this.fileUrls))
444
401
  };
445
402
  }
446
- async eventFile(eventId, assetId, actor) {
447
- const [seen] = await this.seeEvents([eventId], actor);
448
- if (!seen || seen.sight === "public") throw new NotFoundError("Event file", assetId);
449
- const file = (await this.content.eventFilesFor([eventId], this.system(actor)))[eventId]?.find((f) => f.assetId === assetId);
450
- if (!file) throw new NotFoundError("Event file", assetId);
451
- return signed(await this.files.signFile(actor.orgId, assetId, { saveAs: file.displayName ?? file.filename }), "Event file", assetId);
452
- }
453
403
  async directory(communityId, query, actor) {
454
404
  if (!await this.access.isCommunityMember(communityId, actor)) throw new NotFoundError("Community", communityId);
455
405
  const orgId = actor.orgId;
@@ -486,9 +436,9 @@ var MemberReportServiceImpl = class {
486
436
  communityId,
487
437
  title: community.title,
488
438
  description: community.description,
489
- thumbnailUrl: community.thumbnailUrl,
439
+ thumbnailUrl: showsImages(community.status, communityId, actor) ? community.thumbnailUrl : null,
490
440
  dm: community.settings.dm,
491
- spaces: await this.homeSpaces(actor, communityId, spaces, everySpace),
441
+ spaces: await this.homeSpaces(actor, communityId, spaces, everySpace, showsImages(community.status, communityId, actor)),
492
442
  viewer: {
493
443
  permissions: PERMISSION_KEYS.filter((p) => actor.permissions.has(p, communityId)),
494
444
  opensEverySpace: everySpace,
@@ -500,14 +450,15 @@ var MemberReportServiceImpl = class {
500
450
  }
501
451
  async communitySpace(communityId, spaceId, actor) {
502
452
  if (!await this.access.isCommunityMember(communityId, actor)) throw new NotFoundError("Space", spaceId);
503
- const space = await this.content.getSpace(spaceId, this.system(actor));
504
- if (!space || space.communityId !== communityId) throw new NotFoundError("Space", spaceId);
453
+ const system = this.system(actor);
454
+ const [space, community] = await Promise.all([this.content.getSpace(spaceId, system), this.content.findCommunity(communityId, system)]);
455
+ if (!space || space.communityId !== communityId || !community) throw new NotFoundError("Space", spaceId);
505
456
  const everySpace = await this.access.opensEverySpace(actor, communityId);
506
- const [view] = await this.homeSpaces(actor, communityId, [space], everySpace);
457
+ const [view] = await this.homeSpaces(actor, communityId, [space], everySpace, showsImages(community.status, communityId, actor));
507
458
  if (!view) throw new NotFoundError("Space", spaceId);
508
459
  return view;
509
460
  }
510
- async homeSpaces(actor, communityId, spaces, everySpace) {
461
+ async homeSpaces(actor, communityId, spaces, everySpace, images) {
511
462
  const orgId = actor.orgId;
512
463
  const excluding = await this.access.everySpaceOpeners(orgId, communityId);
513
464
  const [openIds, counts, standings, subscribed] = await Promise.all([
@@ -526,9 +477,9 @@ var MemberReportServiceImpl = class {
526
477
  memberCount,
527
478
  accessRule: space.accessRule,
528
479
  standing: standingOf(space, open.has(space.id), memberCount, standings[space.id]),
529
- coverUrl: space.coverUrl,
530
- backgroundUrl: space.backgroundUrl,
531
- iconUrl: space.iconUrl
480
+ coverUrl: images ? space.coverUrl : null,
481
+ backgroundUrl: images ? space.backgroundUrl : null,
482
+ iconUrl: images ? space.iconUrl : null
532
483
  };
533
484
  if (!open.has(space.id)) return {
534
485
  ...base,
@@ -578,9 +529,8 @@ var MemberReportServiceImpl = class {
578
529
  }))).filter((view) => view !== null);
579
530
  }
580
531
  };
581
- function signed(url, what, id) {
582
- if (!url) throw new NotFoundError(what, id);
583
- return url;
532
+ function showsImages(status, communityId, actor) {
533
+ return status === "published" || actor.permissions.has("communities:read", communityId);
584
534
  }
585
535
  function standingOf(space, open, memberCount, status) {
586
536
  if (space.accessRule === "all_members" || open && status !== "active") return "open";
@@ -1 +1 @@
1
- {"version":3,"file":"service.js","names":[],"sources":["../../../src/reporting/member/service.ts"],"sourcesContent":["import {\n eventSight,\n type ContentServicePort,\n type EventSight,\n type EventView,\n type SpaceDetail,\n type SpaceImage,\n type SpaceView,\n} from '../../content/index.js';\nimport { NotFoundError } from '../../shared/errors.js';\nimport { seesSpace, type EntitlementsService, type SpaceMember } from '../../entitlements/index.js';\nimport type { ProgressService } from '../../progress/index.js';\nimport type { AssetFiles, FileUrls } from '../../assets/index.js';\nimport type { OrganizationService } from '../../organizations/index.js';\nimport { PERMISSION_KEYS } from '../../types/schemas/organizations.js';\nimport type {\n ItemTitles as DiscussionItemTitles,\n SubscriptionService,\n} from '../../discussion/index.js';\nimport type { ItemTitles as ContentItemTitles } from '../../content/index.js';\nimport type { MemberActor, NotificationSettingsService } from '../../identity/index.js';\nimport {\n applicableCategories,\n type ListedNotification,\n type NotificationsService,\n} from '../../automations/index.js';\nimport type { ChannelRegistry, NotificationCategoryService } from '../../shared/ports.js';\nimport { resolveChannels } from '../../shared/notification-settings.js';\nimport { channelOrder } from '../../shared/notification-categories.js';\nimport type {\n NotificationCategory,\n NotificationFilter,\n StoredSetting,\n SubjectRef,\n} from '../../types/index.js';\nimport type { Actor } from '../../shared/actor.js';\nimport type { EventFile, Page, Permission } from '../../types/index.js';\nimport type {\n ActivityView,\n CommunityHome,\n CommunityHomeLink,\n CommunityHomeSpace,\n CourseProgressView,\n CourseView,\n DirectoryEntry,\n DirectoryQuery,\n DownloadFile,\n DownloadView,\n EventAttendee,\n MemberEvent,\n MemberEventSummary,\n MemberEventsQuery,\n MemberViewer,\n Module,\n NotificationItemView,\n NotificationView,\n NotificationsView,\n NotificationUnreadCounts,\n CommunityNotificationSettingsView,\n NotificationSettingsCategoryView,\n OrgNotificationSettingsView,\n SettingsChannelView,\n} from './model.js';\nimport type { MemberReportRepository, MemberReportService } from './ports.js';\nimport { eventCalendar } from './calendar.js';\nimport { attendeeFace, attendeeView, type EventFaces } from '../events/index.js';\n\nfunction refKey(ref: SubjectRef): string {\n return `${ref.type}:${ref.id}`;\n}\n\nfunction itemView(\n ref: SubjectRef | null,\n titles: Record<string, { title: string; excerpt?: string }>,\n): NotificationItemView | null {\n const found = ref ? titles[refKey(ref)] : undefined;\n if (!ref || !found) {\n return null;\n }\n return found.excerpt !== undefined\n ? { type: ref.type, id: ref.id, excerpt: found.excerpt }\n : { type: ref.type, id: ref.id, title: found.title };\n}\n\nfunction isActivityPublished(settings: unknown): boolean {\n return (settings as { published?: boolean } | null)?.published !== false;\n}\n\ntype MemberAccess = Pick<\n EntitlementsService,\n | 'isCommunityMember'\n | 'openSpaceIds'\n | 'spaceStandings'\n | 'hasAccess'\n | 'listAccessibleContent'\n | 'openCommunityIds'\n | 'opensEverySpace'\n | 'everySpaceOpeners'\n>;\n\ntype MemberContent = Pick<\n ContentServicePort,\n | 'findCourse'\n | 'listCourseModules'\n | 'listCourseActivityViews'\n | 'listCourseActivities'\n | 'listCourseActivityAssets'\n | 'getActivity'\n | 'findDownload'\n | 'listDownloadAssets'\n | 'findCommunity'\n | 'listSpaces'\n | 'getSpace'\n | 'findEvent'\n | 'findEvents'\n | 'eventFilesFor'\n>;\n\ntype MemberPeople = Pick<OrganizationService, 'systemActor' | 'permissionsOfMany' | 'holders'>;\n\ntype Owner = 'course' | 'download' | 'community' | 'event';\n\nconst READ_OF = {\n course: 'courses:read',\n download: 'downloads:read',\n community: 'communities:read',\n event: 'events:read',\n} as const satisfies Record<Owner, Permission>;\n\nconst THUMBNAIL_OF = {\n course: 'Course thumbnail',\n download: 'Download thumbnail',\n community: 'Community thumbnail',\n event: 'Event thumbnail',\n} as const satisfies Record<Owner, string>;\n\nconst SPACE_IMAGE_ASSET = {\n cover: (space) => space.coverAssetId,\n background: (space) => space.backgroundAssetId,\n icon: (space) => space.iconAssetId,\n} as const satisfies Record<SpaceImage, (space: SpaceView) => string | null>;\n\nconst EVENT_FACES = 10;\n\ninterface SeenEvent {\n event: EventView;\n sight: EventSight;\n attending: boolean;\n}\n\nexport type MemberReportServiceParams = {\n repo: MemberReportRepository;\n content: MemberContent;\n progress: Pick<ProgressService, 'get' | 'listByTargets'>;\n files: Pick<AssetFiles, 'signFile'>;\n fileUrls: FileUrls;\n access: MemberAccess;\n people: MemberPeople;\n notifications: Pick<NotificationsService, 'list' | 'get' | 'unreadCounts'>;\n titles: { discussion: DiscussionItemTitles; content: ContentItemTitles };\n subscriptions: Pick<SubscriptionService, 'viewerSubscriptions'>;\n notificationCategories: {\n community: Pick<NotificationCategoryService, 'get'>;\n org: Pick<NotificationCategoryService, 'get'>;\n };\n notificationSettings: Pick<NotificationSettingsService, 'get'>;\n channels: Pick<ChannelRegistry, 'list'>;\n studentPortalUrl: string;\n now?: () => Date;\n};\n\nexport class MemberReportServiceImpl implements MemberReportService {\n private readonly repo: MemberReportRepository;\n private readonly content: MemberContent;\n private readonly progress: Pick<ProgressService, 'get' | 'listByTargets'>;\n private readonly files: Pick<AssetFiles, 'signFile'>;\n private readonly fileUrls: FileUrls;\n private readonly access: MemberAccess;\n private readonly people: MemberPeople;\n private readonly notificationList: Pick<NotificationsService, 'list' | 'get' | 'unreadCounts'>;\n private readonly titles: MemberReportServiceParams['titles'];\n private readonly subscriptions: Pick<SubscriptionService, 'viewerSubscriptions'>;\n private readonly notificationCategories: MemberReportServiceParams['notificationCategories'];\n private readonly notificationSettings: Pick<NotificationSettingsService, 'get'>;\n private readonly channels: Pick<ChannelRegistry, 'list'>;\n private readonly studentPortalUrl: string;\n private readonly now: () => Date;\n\n constructor(params: MemberReportServiceParams) {\n this.repo = params.repo;\n this.content = params.content;\n this.progress = params.progress;\n this.files = params.files;\n this.fileUrls = params.fileUrls;\n this.access = params.access;\n this.people = params.people;\n this.notificationList = params.notifications;\n this.titles = params.titles;\n this.subscriptions = params.subscriptions;\n this.notificationCategories = params.notificationCategories;\n this.notificationSettings = params.notificationSettings;\n this.channels = params.channels;\n this.studentPortalUrl = params.studentPortalUrl.replace(/\\/+$/, '');\n this.now = params.now ?? (() => new Date());\n }\n\n private system(actor: Actor): Actor {\n return this.people.systemActor(actor.orgId);\n }\n\n viewer(actor: Actor): MemberViewer {\n const role = actor.permissions.roleOn();\n return {\n orgUserId: actor.id,\n orgRole: { key: role.key, name: role.name },\n permissions: PERMISSION_KEYS.filter((p) => actor.permissions.anywhere(p)),\n orgPermissions: PERMISSION_KEYS.filter((p) => actor.permissions.has(p)),\n };\n }\n\n async communityNotificationSettings(\n communityId: string,\n actor: MemberActor,\n ): Promise<CommunityNotificationSettingsView> {\n if (!(await this.access.isCommunityMember(communityId, actor))) {\n throw new NotFoundError('Community', communityId);\n }\n const orgId = actor.orgId;\n const system = this.system(actor);\n const [list, settings, spaceIds, spaces, community] = await Promise.all([\n this.notificationCategories.community.get(communityId, system),\n this.notificationSettings.get(orgId, actor.userId, { communityId }),\n this.access.openSpaceIds(communityId, actor),\n this.content.listSpaces(communityId, system),\n this.content.findCommunity(communityId, system),\n ]);\n const open = new Set(spaceIds);\n return {\n communityId,\n digest: community?.settings.digest ?? null,\n categories: this.settingsView(\n applicableCategories(list.categories, actor.permissions, { communityId }),\n settings,\n spaces\n .filter((space) => open.has(space.id))\n .map((space) => ({ spaceId: space.id, name: space.name })),\n ),\n };\n }\n\n async orgNotificationSettings(actor: MemberActor): Promise<OrgNotificationSettingsView> {\n const orgId = actor.orgId;\n const [list, settings] = await Promise.all([\n this.notificationCategories.org.get(orgId, this.system(actor)),\n this.notificationSettings.get(orgId, actor.userId, 'org'),\n ]);\n return {\n categories: this.settingsView(\n applicableCategories(list.categories, actor.permissions, 'org'),\n settings,\n [],\n ),\n };\n }\n\n private settingsView(\n categories: NotificationCategory[],\n settings: StoredSetting[],\n spaces: { spaceId: string; name: string }[],\n ): NotificationSettingsCategoryView[] {\n const configured = channelOrder(this.channels.list());\n const channelsAt = (\n category: NotificationCategory,\n spaceId: string | null,\n ): SettingsChannelView[] => {\n const withoutLevel = settings.filter(\n (setting) => setting.category !== category.key || setting.spaceId !== spaceId,\n );\n const fallback = new Map(\n resolveChannels(category, withoutLevel, spaceId, configured).map((resolved) => [\n resolved.channel,\n resolved.enabled,\n ]),\n );\n return resolveChannels(category, settings, spaceId, configured).map((resolved) => ({\n ...resolved,\n default: fallback.get(resolved.channel) ?? resolved.enabled,\n }));\n };\n return categories.map((category) => {\n const perSpace = category.perSpace === true;\n const view: NotificationSettingsCategoryView = {\n key: category.key,\n perSpace,\n channels: channelsAt(category, null),\n };\n if (perSpace) {\n view.spaces = spaces.map(({ spaceId, name }) => ({\n spaceId,\n name,\n channels: channelsAt(category, spaceId),\n }));\n }\n return view;\n });\n }\n\n async notifications(\n filter: NotificationFilter,\n page: { page: number; pageSize: number },\n actor: MemberActor,\n ): Promise<NotificationsView> {\n const listed = await this.notificationList.list(filter, page, actor);\n return {\n rows: await this.describeNotifications(actor, listed.items),\n total: listed.total,\n page: page.page,\n pageSize: page.pageSize,\n unreadCount: listed.unreadCount,\n };\n }\n\n async notification(notificationId: string, actor: MemberActor): Promise<NotificationView> {\n const found = await this.notificationList.get(notificationId, actor);\n const [view] = found ? await this.describeNotifications(actor, [found]) : [];\n if (!view) {\n throw new NotFoundError('Notification', notificationId);\n }\n return view;\n }\n\n async notificationUnreadCounts(actor: MemberActor): Promise<NotificationUnreadCounts> {\n const communityIds = await this.access.openCommunityIds(actor);\n return this.notificationList.unreadCounts(communityIds, actor);\n }\n\n private async describeNotifications(\n actor: Actor,\n rows: ListedNotification[],\n ): Promise<NotificationView[]> {\n const orgId = actor.orgId;\n const refs = rows.flatMap((row) => [\n ...(row.subject ? [row.subject] : []),\n ...(row.parent ? [row.parent] : []),\n ...(row.communityId ? [{ type: 'community' as const, id: row.communityId }] : []),\n ...(row.spaceId ? [{ type: 'space' as const, id: row.spaceId }] : []),\n ]);\n const actorIds = [...new Set(rows.flatMap((row) => row.actorId ?? []))];\n const [people, discussionTitles, contentTitles] = await Promise.all([\n this.repo.people(orgId, actorIds),\n this.titles.discussion.titlesFor(refs, actor),\n this.titles.content.titlesFor(refs, actor),\n ]);\n const titles = { ...contentTitles, ...discussionTitles };\n const personById = new Map(people.map((person) => [person.orgUserId, person]));\n return rows.map((row) => {\n const person = row.actorId ? personById.get(row.actorId) : undefined;\n const community = row.communityId ? titles[`community:${row.communityId}`] : undefined;\n const space = row.spaceId ? titles[`space:${row.spaceId}`] : undefined;\n return {\n id: row.id,\n type: row.type,\n scope: row.scope,\n reasons: row.reasons,\n matchedReason: row.matchedReason,\n category: row.category,\n display: row.display,\n community:\n row.communityId && community ? { id: row.communityId, title: community.title } : null,\n space: row.spaceId && space ? { id: row.spaceId, name: space.title } : null,\n actor: person\n ? {\n orgUserId: person.orgUserId,\n name: person.displayName,\n imageUrl: person.imageAssetId\n ? this.fileUrls.publicImage(orgId, person.orgUserId, person.imageAssetId)\n : null,\n }\n : null,\n subject: itemView(row.subject, titles),\n parent: itemView(row.parent, titles),\n createdAt: row.createdAt,\n readAt: row.readAt,\n };\n });\n }\n\n private async opensCourse(courseId: string, actor: Actor): Promise<CourseView | null> {\n const reads = actor.permissions.has('courses:read', courseId);\n if (!reads && !(await this.access.hasAccess(actor.orgId, actor.id, courseId))) {\n return null;\n }\n const course = await this.content.findCourse(courseId, this.system(actor));\n return course && (reads || course.status === 'published') ? course : null;\n }\n\n private async opensDownload(downloadId: string, actor: Actor): Promise<DownloadView | null> {\n const reads = actor.permissions.has('downloads:read', downloadId);\n if (!reads && !(await this.access.hasAccess(actor.orgId, actor.id, downloadId))) {\n return null;\n }\n const download = await this.content.findDownload(downloadId, this.system(actor));\n return download && (reads || download.status === 'published') ? download : null;\n }\n\n async listCourses(actor: Actor): Promise<CourseView[]> {\n const ids = await this.access.listAccessibleContent(actor.orgId, actor.id, 'course');\n const system = this.system(actor);\n const courses = await Promise.all(ids.map((id) => this.content.findCourse(id, system)));\n return courses.filter((c): c is CourseView => c !== null && c.status === 'published');\n }\n\n private async requireCourse(courseId: string, actor: Actor): Promise<CourseView> {\n const course = await this.opensCourse(courseId, actor);\n if (!course) {\n throw new NotFoundError('Course', courseId);\n }\n return course;\n }\n\n getCourse(courseId: string, actor: Actor): Promise<CourseView> {\n return this.requireCourse(courseId, actor);\n }\n\n async listModules(courseId: string, actor: Actor): Promise<Module[]> {\n await this.requireCourse(courseId, actor);\n return this.content.listCourseModules(courseId, this.system(actor));\n }\n\n async listActivities(courseId: string, actor: Actor): Promise<ActivityView[]> {\n await this.requireCourse(courseId, actor);\n const activities = await this.content.listCourseActivityViews(courseId, this.system(actor));\n return activities.filter((a) => isActivityPublished(a.settings));\n }\n\n async courseProgress(courseId: string, actor: Actor): Promise<CourseProgressView> {\n await this.requireCourse(courseId, actor);\n const orgId = actor.orgId;\n const courseActivities = await this.content.listCourseActivities(courseId, this.system(actor));\n const ids = courseActivities.filter((a) => isActivityPublished(a.settings)).map((a) => a.id);\n const records = await this.progress.listByTargets(orgId, actor.id, ids);\n const activities: CourseProgressView['activities'] = {};\n const positions: CourseProgressView['positions'] = {};\n let done = 0;\n for (const r of records) {\n if (r.targetType !== 'activity') {\n continue;\n }\n activities[r.targetId] = r.completedAt ? 'completed' : 'in-progress';\n if (r.completedAt) {\n done += 1;\n }\n if (r.position != null) {\n positions[r.targetId] = r.position;\n }\n }\n const courseRecord = await this.progress.get(orgId, {\n orgUserId: actor.id,\n targetType: 'course',\n targetId: courseId,\n });\n return {\n activities,\n percent: ids.length > 0 ? Math.round((done / ids.length) * 100) : 0,\n completed: courseRecord?.completedAt != null,\n positions,\n };\n }\n\n async listDownloads(actor: Actor): Promise<DownloadView[]> {\n const ids = await this.access.listAccessibleContent(actor.orgId, actor.id, 'download');\n const system = this.system(actor);\n const rows = await Promise.all(ids.map((id) => this.content.findDownload(id, system)));\n return rows.filter((d): d is DownloadView => d !== null && d.status === 'published');\n }\n\n async getDownload(\n downloadId: string,\n actor: Actor,\n ): Promise<{ download: DownloadView; assets: DownloadFile[] }> {\n const download = await this.opensDownload(downloadId, actor);\n if (!download) {\n throw new NotFoundError('Download', downloadId);\n }\n const assets = await this.content.listDownloadAssets(downloadId, this.system(actor));\n return { download, assets };\n }\n\n async thumbnailFile(owner: Owner, id: string, actor: Actor): Promise<string> {\n const system = this.system(actor);\n const item =\n owner === 'event'\n ? (await this.seeEvents([id], actor))[0]?.event\n : owner === 'course'\n ? await this.content.findCourse(id, system)\n : owner === 'download'\n ? await this.content.findDownload(id, system)\n : await this.content.findCommunity(id, system);\n if (\n !item?.thumbnailAssetId ||\n (owner !== 'event' &&\n item.status !== 'published' &&\n !actor.permissions.has(READ_OF[owner], id))\n ) {\n throw new NotFoundError(THUMBNAIL_OF[owner], id);\n }\n return signed(\n await this.files.signFile(actor.orgId, item.thumbnailAssetId),\n THUMBNAIL_OF[owner],\n id,\n );\n }\n\n async spaceImageFile(spaceId: string, image: SpaceImage, actor: Actor): Promise<string> {\n const system = this.system(actor);\n const space = await this.content.getSpace(spaceId, system);\n const assetId = space ? SPACE_IMAGE_ASSET[image](space) : null;\n const community = space ? await this.content.findCommunity(space.communityId, system) : null;\n if (\n !space ||\n !assetId ||\n !community ||\n (community.status !== 'published' &&\n !actor.permissions.has('communities:read', space.communityId))\n ) {\n throw new NotFoundError('Space image', spaceId);\n }\n return signed(await this.files.signFile(actor.orgId, assetId), 'Space image', spaceId);\n }\n\n async activityFile(activityId: string, assetId: string, actor: Actor): Promise<string> {\n const system = this.system(actor);\n const activity = await this.content.getActivity(activityId, system);\n if (!activity) {\n throw new NotFoundError('Lesson file', assetId);\n }\n if (!actor.permissions.has('courses:read', activity.courseId)) {\n if (\n !isActivityPublished(activity.settings) ||\n !(await this.opensCourse(activity.courseId, actor))\n ) {\n throw new NotFoundError('Lesson file', assetId);\n }\n }\n const links = await this.content.listCourseActivityAssets(activity.courseId, system);\n if (!links.some((link) => link.activityId === activityId && link.assetId === assetId)) {\n throw new NotFoundError('Lesson file', assetId);\n }\n return signed(await this.files.signFile(actor.orgId, assetId), 'Lesson file', assetId);\n }\n\n async downloadFile(downloadId: string, assetId: string, actor: Actor): Promise<string> {\n if (!(await this.opensDownload(downloadId, actor))) {\n throw new NotFoundError('Download file', assetId);\n }\n const files = await this.content.listDownloadAssets(downloadId, this.system(actor));\n const file = files.find((f) => f.assetId === assetId);\n if (!file) {\n throw new NotFoundError('Download file', assetId);\n }\n return signed(\n await this.files.signFile(actor.orgId, assetId, {\n saveAs: file.displayName ?? file.filename,\n }),\n 'Download file',\n assetId,\n );\n }\n\n private async seeEvents(ids: string[], actor: Actor): Promise<SeenEvent[]> {\n if (ids.length === 0) {\n return [];\n }\n const system = this.system(actor);\n const [events, attended] = await Promise.all([\n this.content.findEvents(ids, system),\n ids.length === 1\n ? this.access\n .hasAccess(actor.orgId, actor.id, ids[0]!)\n .then((attending) => (attending ? [ids[0]!] : []))\n : this.access.listAccessibleContent(actor.orgId, actor.id, 'event'),\n ]);\n const attending = new Set(attended);\n const byId = new Map(events.map((event) => [event.id, event]));\n return ids.flatMap((id) => {\n const event = byId.get(id);\n if (!event) {\n return [];\n }\n const sight = eventSight(event, {\n canRead: actor.permissions.has('events:read', id),\n attending: attending.has(id),\n });\n return sight ? [{ event, sight, attending: attending.has(id) }] : [];\n });\n }\n\n private async requireEvent(eventId: string, actor: Actor): Promise<SeenEvent> {\n const [seen] = await this.seeEvents([eventId], actor);\n if (!seen) {\n throw new NotFoundError('Event', eventId);\n }\n return seen;\n }\n\n private async eventSummaries(\n seen: SeenEvent[],\n actor: Actor,\n now: Date,\n ): Promise<MemberEventSummary[]> {\n const ids = seen.map((item) => item.event.id);\n const attended = seen.filter((item) => item.attending).map((item) => item.event.id);\n const [faces, direct] = await Promise.all([\n this.repo.eventFaces(actor.orgId, ids, EVENT_FACES),\n attended.length > 0\n ? this.repo.directEventGrants(actor.orgId, actor.id, attended)\n : Promise.resolve([]),\n ]);\n const leaves = new Set(direct);\n return seen.map((item) =>\n this.eventSummary(\n item,\n faces[item.event.id] ?? { total: 0, rows: [] },\n leaves.has(item.event.id),\n actor,\n now,\n ),\n );\n }\n\n private eventSummary(\n seen: SeenEvent,\n faces: EventFaces,\n canLeave: boolean,\n actor: Actor,\n now: Date,\n ): MemberEventSummary {\n const { event, sight, attending } = seen;\n const id = event.id;\n const canOpen = sight !== 'public';\n const capacity = event.settings.capacity;\n const attendeeCount = faces.total;\n const ended = event.endsAt.getTime() <= now.getTime();\n const recording =\n sight === 'read' || (sight === 'attendee' && ended && event.settings.replay)\n ? event.recording\n : null;\n const writes = actor.permissions.has('events:write', id);\n const canManageAttendees = actor.permissions.has('access:write') || writes;\n return {\n orgId: event.orgId,\n id,\n type: event.type,\n title: event.title,\n thumbnailAssetId: event.thumbnailAssetId,\n thumbnailUrl: event.thumbnailUrl,\n status: event.status,\n startsAt: event.startsAt,\n endsAt: event.endsAt,\n timeZone: event.timeZone,\n location: canOpen ? event.location : null,\n recording,\n settings: {\n type: event.settings.type,\n capacity,\n replay: event.settings.replay,\n },\n attendeeCount,\n attendees: faces.rows.map((row) => attendeeFace(actor.orgId, row, this.fileUrls)),\n placesLeft: capacity === null ? null : Math.max(capacity - attendeeCount, 0),\n viewer: {\n attending,\n canRegister:\n event.status === 'published' &&\n !attending &&\n event.settings.type === 'public' &&\n event.startsAt.getTime() > now.getTime() &&\n (capacity === null || attendeeCount < capacity),\n canOpen,\n canLeave,\n canManageAttendees,\n canAddAttendees: canManageAttendees && event.status === 'published' && !ended,\n canEdit: writes,\n canCancel: writes,\n canDelete: actor.permissions.has('events:delete', id),\n },\n };\n }\n\n async getEvent(eventId: string, actor: Actor): Promise<MemberEvent> {\n const seen = await this.requireEvent(eventId, actor);\n const now = this.now();\n const [[summary], files] = await Promise.all([\n this.eventSummaries([seen], actor, now),\n seen.sight === 'public'\n ? Promise.resolve({} as Record<string, EventFile[]>)\n : this.content.eventFilesFor([eventId], this.system(actor)),\n ]);\n return { ...summary!, description: seen.event.description, files: files[eventId] ?? [] };\n }\n\n async listEvents(query: MemberEventsQuery, actor: Actor): Promise<Page<MemberEventSummary>> {\n const now = this.now();\n const reads = actor.permissions.where('events:read');\n const page = await this.repo.events(\n actor.orgId,\n query,\n { orgUserId: actor.id, readAll: reads.org, readIds: reads.content },\n now,\n );\n const rows = await this.eventSummaries(await this.seeEvents(page.rows, actor), actor, now);\n return { ...page, rows };\n }\n\n async eventCalendar(eventId: string, actor: Actor): Promise<string> {\n const { event, sight } = await this.requireEvent(eventId, actor);\n return eventCalendar(event, {\n pageUrl: `${this.studentPortalUrl}/events/${encodeURIComponent(event.id)}`,\n withLocation: sight !== 'public',\n now: this.now(),\n });\n }\n\n async eventAttendees(\n eventId: string,\n query: { page: number; pageSize: number },\n actor: Actor,\n ): Promise<Page<EventAttendee>> {\n actor.permissions.require('events:read', eventId);\n if (!(await this.content.findEvent(eventId, this.system(actor)))) {\n throw new NotFoundError('Event', eventId);\n }\n const page = await this.repo.eventAttendees(actor.orgId, eventId, query);\n return {\n ...page,\n rows: page.rows.map((row) => attendeeView(actor.orgId, row, this.fileUrls)),\n };\n }\n\n async eventFile(eventId: string, assetId: string, actor: Actor): Promise<string> {\n const [seen] = await this.seeEvents([eventId], actor);\n if (!seen || seen.sight === 'public') {\n throw new NotFoundError('Event file', assetId);\n }\n const files = await this.content.eventFilesFor([eventId], this.system(actor));\n const file = files[eventId]?.find((f) => f.assetId === assetId);\n if (!file) {\n throw new NotFoundError('Event file', assetId);\n }\n return signed(\n await this.files.signFile(actor.orgId, assetId, {\n saveAs: file.displayName ?? file.filename,\n }),\n 'Event file',\n assetId,\n );\n }\n\n async directory(\n communityId: string,\n query: DirectoryQuery,\n actor: Actor,\n ): Promise<Page<DirectoryEntry>> {\n if (!(await this.access.isCommunityMember(communityId, actor))) {\n throw new NotFoundError('Community', communityId);\n }\n const orgId = actor.orgId;\n const community = await this.content.findCommunity(communityId, this.system(actor));\n if (!community) {\n throw new NotFoundError('Community', communityId);\n }\n const readers = await this.people.holders(orgId, 'communities:read', communityId);\n const page = await this.repo.directory(orgId, communityId, query, readers);\n const permissions = await this.people.permissionsOfMany(\n orgId,\n page.rows.map((row) => row.orgUserId),\n );\n return {\n ...page,\n rows: page.rows.map((row) => {\n const held = permissions.get(row.orgUserId);\n const item = held?.roleOn(communityId);\n const org = held?.roleOn();\n return {\n ...row,\n imageUrl: row.imageAssetId\n ? this.fileUrls.publicImage(orgId, row.orgUserId, row.imageAssetId)\n : null,\n team: (item?.team ?? false) || (org?.team ?? false),\n roleName: item?.team ? item.name : org?.team ? org.name : null,\n opensEverySpace:\n (held?.has('communities:read', communityId) ||\n held?.has('communities:moderate', communityId)) ??\n false,\n };\n }),\n };\n }\n\n async communityHome(communityId: string, actor: Actor): Promise<CommunityHome> {\n if (!(await this.access.isCommunityMember(communityId, actor))) {\n throw new NotFoundError('Community', communityId);\n }\n const system = this.system(actor);\n const community = await this.content.findCommunity(communityId, system);\n if (!community) {\n throw new NotFoundError('Community', communityId);\n }\n const [spaces, everySpace] = await Promise.all([\n this.content.listSpaces(communityId, system),\n this.access.opensEverySpace(actor, communityId),\n ]);\n const itemRole = actor.permissions.roleOn(communityId);\n const orgRole = actor.permissions.roleOn();\n const role = itemRole.team || !orgRole.team ? itemRole : orgRole;\n return {\n communityId,\n title: community.title,\n description: community.description,\n thumbnailUrl: community.thumbnailUrl,\n dm: community.settings.dm,\n spaces: await this.homeSpaces(actor, communityId, spaces, everySpace),\n viewer: {\n permissions: PERMISSION_KEYS.filter((p) => actor.permissions.has(p, communityId)),\n opensEverySpace: everySpace,\n team: itemRole.team || orgRole.team,\n roleName: role.name,\n canCreateEvents: actor.permissions.has('events:write'),\n },\n };\n }\n\n async communitySpace(\n communityId: string,\n spaceId: string,\n actor: Actor,\n ): Promise<CommunityHomeSpace> {\n if (!(await this.access.isCommunityMember(communityId, actor))) {\n throw new NotFoundError('Space', spaceId);\n }\n const space = await this.content.getSpace(spaceId, this.system(actor));\n if (!space || space.communityId !== communityId) {\n throw new NotFoundError('Space', spaceId);\n }\n const everySpace = await this.access.opensEverySpace(actor, communityId);\n const [view] = await this.homeSpaces(actor, communityId, [space], everySpace);\n if (!view) {\n throw new NotFoundError('Space', spaceId);\n }\n return view;\n }\n\n private async homeSpaces(\n actor: Actor,\n communityId: string,\n spaces: SpaceView[],\n everySpace: boolean,\n ): Promise<CommunityHomeSpace[]> {\n const orgId = actor.orgId;\n const excluding = await this.access.everySpaceOpeners(orgId, communityId);\n const [openIds, counts, standings, subscribed] = await Promise.all([\n this.access.openSpaceIds(communityId, actor),\n this.repo.spaceMemberCounts(orgId, communityId, excluding),\n this.access.spaceStandings(communityId, actor),\n this.subscriptions.viewerSubscriptions(\n orgId,\n actor.id,\n 'space',\n spaces.map((space) => space.id),\n new Date(),\n ),\n ]);\n const open = new Set(openIds);\n const seen = spaces.filter((space) =>\n seesSpace(everySpace, space.accessRule, standings[space.id]),\n );\n return Promise.all(\n seen.map(async (space): Promise<CommunityHomeSpace> => {\n const memberCount = counts[space.id] ?? 0;\n const base = {\n id: space.id,\n name: space.name,\n memberCount,\n accessRule: space.accessRule,\n standing: standingOf(space, open.has(space.id), memberCount, standings[space.id]),\n coverUrl: space.coverUrl,\n backgroundUrl: space.backgroundUrl,\n iconUrl: space.iconUrl,\n };\n if (!open.has(space.id)) {\n return { ...base, locked: true };\n }\n return {\n ...base,\n locked: false,\n description: space.description,\n capacity: space.capacity,\n settings: space.settings,\n topics: space.topics.map((topic) => ({ id: topic.id, name: topic.name })),\n links: await this.linkViews(actor, space.links),\n subscription: subscribed[space.id] ?? null,\n };\n }),\n );\n }\n\n private async linkViews(actor: Actor, links: SpaceDetail['links']): Promise<CommunityHomeLink[]> {\n const system = this.system(actor);\n const shown = (type: keyof typeof READ_OF, id: string, status: string) =>\n status === 'published' || actor.permissions.has(READ_OF[type], id);\n const views = await Promise.all(\n links.map(async (link): Promise<CommunityHomeLink | null> => {\n const course = await this.content.findCourse(link.contentId, system);\n if (course) {\n return shown('course', link.contentId, course.status)\n ? { id: link.id, contentId: link.contentId, type: 'course', title: course.title }\n : null;\n }\n const download = await this.content.findDownload(link.contentId, system);\n if (download) {\n return shown('download', link.contentId, download.status)\n ? { id: link.id, contentId: link.contentId, type: 'download', title: download.title }\n : null;\n }\n const community = await this.content.findCommunity(link.contentId, system);\n if (community) {\n return shown('community', link.contentId, community.status)\n ? { id: link.id, contentId: link.contentId, type: 'community', title: community.title }\n : null;\n }\n return null;\n }),\n );\n return views.filter((view): view is CommunityHomeLink => view !== null);\n }\n}\n\nfunction signed(url: string | null, what: string, id: string): string {\n if (!url) {\n throw new NotFoundError(what, id);\n }\n return url;\n}\n\nfunction standingOf(\n space: SpaceDetail,\n open: boolean,\n memberCount: number,\n status: SpaceMember['status'] | undefined,\n): CommunityHomeSpace['standing'] {\n if (space.accessRule === 'all_members' || (open && status !== 'active')) {\n return 'open';\n }\n if (status === 'active') {\n return 'member';\n }\n if (status === 'requested' || status === 'invited') {\n return status;\n }\n if (space.capacity !== null && memberCount >= space.capacity) {\n return 'full';\n }\n return 'none';\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"service.js","names":[],"sources":["../../../src/reporting/member/service.ts"],"sourcesContent":["import {\n eventSight,\n type ContentServicePort,\n type EventSight,\n type EventView,\n type SpaceDetail,\n type SpaceView,\n} from '../../content/index.js';\nimport { NotFoundError } from '../../shared/errors.js';\nimport { seesSpace, type EntitlementsService, type SpaceMember } from '../../entitlements/index.js';\nimport type { ProgressService } from '../../progress/index.js';\nimport type { FileUrls } from '../../assets/index.js';\nimport type { OrganizationService } from '../../organizations/index.js';\nimport { PERMISSION_KEYS } from '../../types/schemas/organizations.js';\nimport type {\n ItemTitles as DiscussionItemTitles,\n SubscriptionService,\n} from '../../discussion/index.js';\nimport type { ItemTitles as ContentItemTitles } from '../../content/index.js';\nimport type { MemberActor, NotificationSettingsService } from '../../identity/index.js';\nimport {\n applicableCategories,\n type ListedNotification,\n type NotificationsService,\n} from '../../automations/index.js';\nimport type { ChannelRegistry, NotificationCategoryService } from '../../shared/ports.js';\nimport { resolveChannels } from '../../shared/notification-settings.js';\nimport { channelOrder } from '../../shared/notification-categories.js';\nimport type {\n NotificationCategory,\n NotificationFilter,\n StoredSetting,\n SubjectRef,\n} from '../../types/index.js';\nimport type { Actor } from '../../shared/actor.js';\nimport type { EventFile, Page, Permission } from '../../types/index.js';\nimport type {\n ActivityView,\n CommunityHome,\n CommunityHomeLink,\n CommunityHomeSpace,\n CourseProgressView,\n CourseView,\n DirectoryEntry,\n DirectoryQuery,\n DownloadFile,\n DownloadView,\n EventAttendee,\n MemberEvent,\n MemberEventSummary,\n MemberEventsQuery,\n MemberViewer,\n Module,\n NotificationItemView,\n NotificationView,\n NotificationsView,\n NotificationUnreadCounts,\n CommunityNotificationSettingsView,\n NotificationSettingsCategoryView,\n OrgNotificationSettingsView,\n SettingsChannelView,\n} from './model.js';\nimport type { MemberReportRepository, MemberReportService } from './ports.js';\nimport { eventCalendar } from './calendar.js';\nimport { attendeeFace, attendeeView, type EventFaces } from '../events/index.js';\n\nfunction refKey(ref: SubjectRef): string {\n return `${ref.type}:${ref.id}`;\n}\n\nfunction itemView(\n ref: SubjectRef | null,\n titles: Record<string, { title: string; excerpt?: string }>,\n): NotificationItemView | null {\n const found = ref ? titles[refKey(ref)] : undefined;\n if (!ref || !found) {\n return null;\n }\n return found.excerpt !== undefined\n ? { type: ref.type, id: ref.id, excerpt: found.excerpt }\n : { type: ref.type, id: ref.id, title: found.title };\n}\n\nfunction isActivityPublished(settings: unknown): boolean {\n return (settings as { published?: boolean } | null)?.published !== false;\n}\n\ntype MemberAccess = Pick<\n EntitlementsService,\n | 'isCommunityMember'\n | 'openSpaceIds'\n | 'spaceStandings'\n | 'hasAccess'\n | 'listAccessibleContent'\n | 'openCommunityIds'\n | 'opensEverySpace'\n | 'everySpaceOpeners'\n>;\n\ntype MemberContent = Pick<\n ContentServicePort,\n | 'findCourse'\n | 'listCourseModules'\n | 'listCourseActivityViews'\n | 'listCourseActivities'\n | 'findDownload'\n | 'listDownloadAssets'\n | 'findCommunity'\n | 'listSpaces'\n | 'getSpace'\n | 'findEvent'\n | 'findEvents'\n | 'eventFilesFor'\n>;\n\ntype MemberPeople = Pick<OrganizationService, 'systemActor' | 'permissionsOfMany' | 'holders'>;\n\ntype Owner = 'course' | 'download' | 'community' | 'event';\n\nconst READ_OF = {\n course: 'courses:read',\n download: 'downloads:read',\n community: 'communities:read',\n event: 'events:read',\n} as const satisfies Record<Owner, Permission>;\n\nconst EVENT_FACES = 10;\n\ninterface SeenEvent {\n event: EventView;\n sight: EventSight;\n attending: boolean;\n}\n\nexport type MemberReportServiceParams = {\n repo: MemberReportRepository;\n content: MemberContent;\n progress: Pick<ProgressService, 'get' | 'listByTargets'>;\n fileUrls: FileUrls;\n access: MemberAccess;\n people: MemberPeople;\n notifications: Pick<NotificationsService, 'list' | 'get' | 'unreadCounts'>;\n titles: { discussion: DiscussionItemTitles; content: ContentItemTitles };\n subscriptions: Pick<SubscriptionService, 'viewerSubscriptions'>;\n notificationCategories: {\n community: Pick<NotificationCategoryService, 'get'>;\n org: Pick<NotificationCategoryService, 'get'>;\n };\n notificationSettings: Pick<NotificationSettingsService, 'get'>;\n channels: Pick<ChannelRegistry, 'list'>;\n studentPortalUrl: string;\n now?: () => Date;\n};\n\nexport class MemberReportServiceImpl implements MemberReportService {\n private readonly repo: MemberReportRepository;\n private readonly content: MemberContent;\n private readonly progress: Pick<ProgressService, 'get' | 'listByTargets'>;\n private readonly fileUrls: FileUrls;\n private readonly access: MemberAccess;\n private readonly people: MemberPeople;\n private readonly notificationList: Pick<NotificationsService, 'list' | 'get' | 'unreadCounts'>;\n private readonly titles: MemberReportServiceParams['titles'];\n private readonly subscriptions: Pick<SubscriptionService, 'viewerSubscriptions'>;\n private readonly notificationCategories: MemberReportServiceParams['notificationCategories'];\n private readonly notificationSettings: Pick<NotificationSettingsService, 'get'>;\n private readonly channels: Pick<ChannelRegistry, 'list'>;\n private readonly studentPortalUrl: string;\n private readonly now: () => Date;\n\n constructor(params: MemberReportServiceParams) {\n this.repo = params.repo;\n this.content = params.content;\n this.progress = params.progress;\n this.fileUrls = params.fileUrls;\n this.access = params.access;\n this.people = params.people;\n this.notificationList = params.notifications;\n this.titles = params.titles;\n this.subscriptions = params.subscriptions;\n this.notificationCategories = params.notificationCategories;\n this.notificationSettings = params.notificationSettings;\n this.channels = params.channels;\n this.studentPortalUrl = params.studentPortalUrl.replace(/\\/+$/, '');\n this.now = params.now ?? (() => new Date());\n }\n\n private system(actor: Actor): Actor {\n return this.people.systemActor(actor.orgId);\n }\n\n viewer(actor: Actor): MemberViewer {\n const role = actor.permissions.roleOn();\n return {\n orgUserId: actor.id,\n orgRole: { key: role.key, name: role.name },\n permissions: PERMISSION_KEYS.filter((p) => actor.permissions.anywhere(p)),\n orgPermissions: PERMISSION_KEYS.filter((p) => actor.permissions.has(p)),\n };\n }\n\n async communityNotificationSettings(\n communityId: string,\n actor: MemberActor,\n ): Promise<CommunityNotificationSettingsView> {\n if (!(await this.access.isCommunityMember(communityId, actor))) {\n throw new NotFoundError('Community', communityId);\n }\n const orgId = actor.orgId;\n const system = this.system(actor);\n const [list, settings, spaceIds, spaces, community] = await Promise.all([\n this.notificationCategories.community.get(communityId, system),\n this.notificationSettings.get(orgId, actor.userId, { communityId }),\n this.access.openSpaceIds(communityId, actor),\n this.content.listSpaces(communityId, system),\n this.content.findCommunity(communityId, system),\n ]);\n const open = new Set(spaceIds);\n return {\n communityId,\n digest: community?.settings.digest ?? null,\n categories: this.settingsView(\n applicableCategories(list.categories, actor.permissions, { communityId }),\n settings,\n spaces\n .filter((space) => open.has(space.id))\n .map((space) => ({ spaceId: space.id, name: space.name })),\n ),\n };\n }\n\n async orgNotificationSettings(actor: MemberActor): Promise<OrgNotificationSettingsView> {\n const orgId = actor.orgId;\n const [list, settings] = await Promise.all([\n this.notificationCategories.org.get(orgId, this.system(actor)),\n this.notificationSettings.get(orgId, actor.userId, 'org'),\n ]);\n return {\n categories: this.settingsView(\n applicableCategories(list.categories, actor.permissions, 'org'),\n settings,\n [],\n ),\n };\n }\n\n private settingsView(\n categories: NotificationCategory[],\n settings: StoredSetting[],\n spaces: { spaceId: string; name: string }[],\n ): NotificationSettingsCategoryView[] {\n const configured = channelOrder(this.channels.list());\n const channelsAt = (\n category: NotificationCategory,\n spaceId: string | null,\n ): SettingsChannelView[] => {\n const withoutLevel = settings.filter(\n (setting) => setting.category !== category.key || setting.spaceId !== spaceId,\n );\n const fallback = new Map(\n resolveChannels(category, withoutLevel, spaceId, configured).map((resolved) => [\n resolved.channel,\n resolved.enabled,\n ]),\n );\n return resolveChannels(category, settings, spaceId, configured).map((resolved) => ({\n ...resolved,\n default: fallback.get(resolved.channel) ?? resolved.enabled,\n }));\n };\n return categories.map((category) => {\n const perSpace = category.perSpace === true;\n const view: NotificationSettingsCategoryView = {\n key: category.key,\n perSpace,\n channels: channelsAt(category, null),\n };\n if (perSpace) {\n view.spaces = spaces.map(({ spaceId, name }) => ({\n spaceId,\n name,\n channels: channelsAt(category, spaceId),\n }));\n }\n return view;\n });\n }\n\n async notifications(\n filter: NotificationFilter,\n page: { page: number; pageSize: number },\n actor: MemberActor,\n ): Promise<NotificationsView> {\n const listed = await this.notificationList.list(filter, page, actor);\n return {\n rows: await this.describeNotifications(actor, listed.items),\n total: listed.total,\n page: page.page,\n pageSize: page.pageSize,\n unreadCount: listed.unreadCount,\n };\n }\n\n async notification(notificationId: string, actor: MemberActor): Promise<NotificationView> {\n const found = await this.notificationList.get(notificationId, actor);\n const [view] = found ? await this.describeNotifications(actor, [found]) : [];\n if (!view) {\n throw new NotFoundError('Notification', notificationId);\n }\n return view;\n }\n\n async notificationUnreadCounts(actor: MemberActor): Promise<NotificationUnreadCounts> {\n const communityIds = await this.access.openCommunityIds(actor);\n return this.notificationList.unreadCounts(communityIds, actor);\n }\n\n private async describeNotifications(\n actor: Actor,\n rows: ListedNotification[],\n ): Promise<NotificationView[]> {\n const orgId = actor.orgId;\n const refs = rows.flatMap((row) => [\n ...(row.subject ? [row.subject] : []),\n ...(row.parent ? [row.parent] : []),\n ...(row.communityId ? [{ type: 'community' as const, id: row.communityId }] : []),\n ...(row.spaceId ? [{ type: 'space' as const, id: row.spaceId }] : []),\n ]);\n const actorIds = [...new Set(rows.flatMap((row) => row.actorId ?? []))];\n const [people, discussionTitles, contentTitles] = await Promise.all([\n this.repo.people(orgId, actorIds),\n this.titles.discussion.titlesFor(refs, actor),\n this.titles.content.titlesFor(refs, actor),\n ]);\n const titles = { ...contentTitles, ...discussionTitles };\n const personById = new Map(people.map((person) => [person.orgUserId, person]));\n return rows.map((row) => {\n const person = row.actorId ? personById.get(row.actorId) : undefined;\n const community = row.communityId ? titles[`community:${row.communityId}`] : undefined;\n const space = row.spaceId ? titles[`space:${row.spaceId}`] : undefined;\n return {\n id: row.id,\n type: row.type,\n scope: row.scope,\n reasons: row.reasons,\n matchedReason: row.matchedReason,\n category: row.category,\n display: row.display,\n community:\n row.communityId && community ? { id: row.communityId, title: community.title } : null,\n space: row.spaceId && space ? { id: row.spaceId, name: space.title } : null,\n actor: person\n ? {\n orgUserId: person.orgUserId,\n name: person.displayName,\n imageUrl: person.imageAssetId\n ? this.fileUrls.publicImage(orgId, person.orgUserId, person.imageAssetId)\n : null,\n }\n : null,\n subject: itemView(row.subject, titles),\n parent: itemView(row.parent, titles),\n createdAt: row.createdAt,\n readAt: row.readAt,\n };\n });\n }\n\n private async opensCourse(courseId: string, actor: Actor): Promise<CourseView | null> {\n const reads = actor.permissions.has('courses:read', courseId);\n if (!reads && !(await this.access.hasAccess(actor.orgId, actor.id, courseId))) {\n return null;\n }\n const course = await this.content.findCourse(courseId, this.system(actor));\n return course && (reads || course.status === 'published') ? course : null;\n }\n\n private async opensDownload(downloadId: string, actor: Actor): Promise<DownloadView | null> {\n const reads = actor.permissions.has('downloads:read', downloadId);\n if (!reads && !(await this.access.hasAccess(actor.orgId, actor.id, downloadId))) {\n return null;\n }\n const download = await this.content.findDownload(downloadId, this.system(actor));\n return download && (reads || download.status === 'published') ? download : null;\n }\n\n async listCourses(actor: Actor): Promise<CourseView[]> {\n const ids = await this.access.listAccessibleContent(actor.orgId, actor.id, 'course');\n const system = this.system(actor);\n const courses = await Promise.all(ids.map((id) => this.content.findCourse(id, system)));\n return courses.filter((c): c is CourseView => c !== null && c.status === 'published');\n }\n\n private async requireCourse(courseId: string, actor: Actor): Promise<CourseView> {\n const course = await this.opensCourse(courseId, actor);\n if (!course) {\n throw new NotFoundError('Course', courseId);\n }\n return course;\n }\n\n getCourse(courseId: string, actor: Actor): Promise<CourseView> {\n return this.requireCourse(courseId, actor);\n }\n\n async listModules(courseId: string, actor: Actor): Promise<Module[]> {\n await this.requireCourse(courseId, actor);\n return this.content.listCourseModules(courseId, this.system(actor));\n }\n\n async listActivities(courseId: string, actor: Actor): Promise<ActivityView[]> {\n await this.requireCourse(courseId, actor);\n const activities = await this.content.listCourseActivityViews(courseId, this.system(actor));\n return activities.filter((a) => isActivityPublished(a.settings));\n }\n\n async courseProgress(courseId: string, actor: Actor): Promise<CourseProgressView> {\n await this.requireCourse(courseId, actor);\n const orgId = actor.orgId;\n const courseActivities = await this.content.listCourseActivities(courseId, this.system(actor));\n const ids = courseActivities.filter((a) => isActivityPublished(a.settings)).map((a) => a.id);\n const records = await this.progress.listByTargets(orgId, actor.id, ids);\n const activities: CourseProgressView['activities'] = {};\n const positions: CourseProgressView['positions'] = {};\n let done = 0;\n for (const r of records) {\n if (r.targetType !== 'activity') {\n continue;\n }\n activities[r.targetId] = r.completedAt ? 'completed' : 'in-progress';\n if (r.completedAt) {\n done += 1;\n }\n if (r.position != null) {\n positions[r.targetId] = r.position;\n }\n }\n const courseRecord = await this.progress.get(orgId, {\n orgUserId: actor.id,\n targetType: 'course',\n targetId: courseId,\n });\n return {\n activities,\n percent: ids.length > 0 ? Math.round((done / ids.length) * 100) : 0,\n completed: courseRecord?.completedAt != null,\n positions,\n };\n }\n\n async listDownloads(actor: Actor): Promise<DownloadView[]> {\n const ids = await this.access.listAccessibleContent(actor.orgId, actor.id, 'download');\n const system = this.system(actor);\n const rows = await Promise.all(ids.map((id) => this.content.findDownload(id, system)));\n return rows.filter((d): d is DownloadView => d !== null && d.status === 'published');\n }\n\n async getDownload(\n downloadId: string,\n actor: Actor,\n ): Promise<{ download: DownloadView; assets: DownloadFile[] }> {\n const download = await this.opensDownload(downloadId, actor);\n if (!download) {\n throw new NotFoundError('Download', downloadId);\n }\n const assets = await this.content.listDownloadAssets(downloadId, this.system(actor));\n return { download, assets };\n }\n\n private async seeEvents(ids: string[], actor: Actor): Promise<SeenEvent[]> {\n if (ids.length === 0) {\n return [];\n }\n const system = this.system(actor);\n const [events, attended] = await Promise.all([\n this.content.findEvents(ids, system),\n ids.length === 1\n ? this.access\n .hasAccess(actor.orgId, actor.id, ids[0]!)\n .then((attending) => (attending ? [ids[0]!] : []))\n : this.access.listAccessibleContent(actor.orgId, actor.id, 'event'),\n ]);\n const attending = new Set(attended);\n const byId = new Map(events.map((event) => [event.id, event]));\n return ids.flatMap((id) => {\n const event = byId.get(id);\n if (!event) {\n return [];\n }\n const sight = eventSight(event, {\n canRead: actor.permissions.has('events:read', id),\n attending: attending.has(id),\n });\n return sight ? [{ event, sight, attending: attending.has(id) }] : [];\n });\n }\n\n private async requireEvent(eventId: string, actor: Actor): Promise<SeenEvent> {\n const [seen] = await this.seeEvents([eventId], actor);\n if (!seen) {\n throw new NotFoundError('Event', eventId);\n }\n return seen;\n }\n\n private async eventSummaries(\n seen: SeenEvent[],\n actor: Actor,\n now: Date,\n ): Promise<MemberEventSummary[]> {\n const ids = seen.map((item) => item.event.id);\n const attended = seen.filter((item) => item.attending).map((item) => item.event.id);\n const [faces, direct] = await Promise.all([\n this.repo.eventFaces(actor.orgId, ids, EVENT_FACES),\n attended.length > 0\n ? this.repo.directEventGrants(actor.orgId, actor.id, attended)\n : Promise.resolve([]),\n ]);\n const leaves = new Set(direct);\n return seen.map((item) =>\n this.eventSummary(\n item,\n faces[item.event.id] ?? { total: 0, rows: [] },\n leaves.has(item.event.id),\n actor,\n now,\n ),\n );\n }\n\n private eventSummary(\n seen: SeenEvent,\n faces: EventFaces,\n canLeave: boolean,\n actor: Actor,\n now: Date,\n ): MemberEventSummary {\n const { event, sight, attending } = seen;\n const id = event.id;\n const canOpen = sight !== 'public';\n const capacity = event.settings.capacity;\n const attendeeCount = faces.total;\n const ended = event.endsAt.getTime() <= now.getTime();\n const recording =\n sight === 'read' || (sight === 'attendee' && ended && event.settings.replay)\n ? event.recording\n : null;\n const writes = actor.permissions.has('events:write', id);\n const canManageAttendees = actor.permissions.has('access:write') || writes;\n return {\n orgId: event.orgId,\n id,\n type: event.type,\n title: event.title,\n thumbnailAssetId: event.thumbnailAssetId,\n thumbnailUrl: event.thumbnailUrl,\n status: event.status,\n startsAt: event.startsAt,\n endsAt: event.endsAt,\n timeZone: event.timeZone,\n location: canOpen ? event.location : null,\n recording,\n settings: {\n type: event.settings.type,\n capacity,\n replay: event.settings.replay,\n },\n attendeeCount,\n attendees: faces.rows.map((row) => attendeeFace(actor.orgId, row, this.fileUrls)),\n placesLeft: capacity === null ? null : Math.max(capacity - attendeeCount, 0),\n viewer: {\n attending,\n canRegister:\n event.status === 'published' &&\n !attending &&\n event.settings.type === 'public' &&\n event.startsAt.getTime() > now.getTime() &&\n (capacity === null || attendeeCount < capacity),\n canOpen,\n canLeave,\n canManageAttendees,\n canAddAttendees: canManageAttendees && event.status === 'published' && !ended,\n canEdit: writes,\n canCancel: writes,\n canDelete: actor.permissions.has('events:delete', id),\n },\n };\n }\n\n async getEvent(eventId: string, actor: Actor): Promise<MemberEvent> {\n const seen = await this.requireEvent(eventId, actor);\n const now = this.now();\n const [[summary], files] = await Promise.all([\n this.eventSummaries([seen], actor, now),\n seen.sight === 'public'\n ? Promise.resolve({} as Record<string, EventFile[]>)\n : this.content.eventFilesFor([eventId], this.system(actor)),\n ]);\n return { ...summary!, description: seen.event.description, files: files[eventId] ?? [] };\n }\n\n async listEvents(query: MemberEventsQuery, actor: Actor): Promise<Page<MemberEventSummary>> {\n const now = this.now();\n const reads = actor.permissions.where('events:read');\n const page = await this.repo.events(\n actor.orgId,\n query,\n { orgUserId: actor.id, readAll: reads.org, readIds: reads.content },\n now,\n );\n const rows = await this.eventSummaries(await this.seeEvents(page.rows, actor), actor, now);\n return { ...page, rows };\n }\n\n async eventCalendar(eventId: string, actor: Actor): Promise<string> {\n const { event, sight } = await this.requireEvent(eventId, actor);\n return eventCalendar(event, {\n pageUrl: `${this.studentPortalUrl}/events/${encodeURIComponent(event.id)}`,\n withLocation: sight !== 'public',\n now: this.now(),\n });\n }\n\n async eventAttendees(\n eventId: string,\n query: { page: number; pageSize: number },\n actor: Actor,\n ): Promise<Page<EventAttendee>> {\n actor.permissions.require('events:read', eventId);\n if (!(await this.content.findEvent(eventId, this.system(actor)))) {\n throw new NotFoundError('Event', eventId);\n }\n const page = await this.repo.eventAttendees(actor.orgId, eventId, query);\n return {\n ...page,\n rows: page.rows.map((row) => attendeeView(actor.orgId, row, this.fileUrls)),\n };\n }\n\n async directory(\n communityId: string,\n query: DirectoryQuery,\n actor: Actor,\n ): Promise<Page<DirectoryEntry>> {\n if (!(await this.access.isCommunityMember(communityId, actor))) {\n throw new NotFoundError('Community', communityId);\n }\n const orgId = actor.orgId;\n const community = await this.content.findCommunity(communityId, this.system(actor));\n if (!community) {\n throw new NotFoundError('Community', communityId);\n }\n const readers = await this.people.holders(orgId, 'communities:read', communityId);\n const page = await this.repo.directory(orgId, communityId, query, readers);\n const permissions = await this.people.permissionsOfMany(\n orgId,\n page.rows.map((row) => row.orgUserId),\n );\n return {\n ...page,\n rows: page.rows.map((row) => {\n const held = permissions.get(row.orgUserId);\n const item = held?.roleOn(communityId);\n const org = held?.roleOn();\n return {\n ...row,\n imageUrl: row.imageAssetId\n ? this.fileUrls.publicImage(orgId, row.orgUserId, row.imageAssetId)\n : null,\n team: (item?.team ?? false) || (org?.team ?? false),\n roleName: item?.team ? item.name : org?.team ? org.name : null,\n opensEverySpace:\n (held?.has('communities:read', communityId) ||\n held?.has('communities:moderate', communityId)) ??\n false,\n };\n }),\n };\n }\n\n async communityHome(communityId: string, actor: Actor): Promise<CommunityHome> {\n if (!(await this.access.isCommunityMember(communityId, actor))) {\n throw new NotFoundError('Community', communityId);\n }\n const system = this.system(actor);\n const community = await this.content.findCommunity(communityId, system);\n if (!community) {\n throw new NotFoundError('Community', communityId);\n }\n const [spaces, everySpace] = await Promise.all([\n this.content.listSpaces(communityId, system),\n this.access.opensEverySpace(actor, communityId),\n ]);\n const itemRole = actor.permissions.roleOn(communityId);\n const orgRole = actor.permissions.roleOn();\n const role = itemRole.team || !orgRole.team ? itemRole : orgRole;\n return {\n communityId,\n title: community.title,\n description: community.description,\n thumbnailUrl: showsImages(community.status, communityId, actor)\n ? community.thumbnailUrl\n : null,\n dm: community.settings.dm,\n spaces: await this.homeSpaces(\n actor,\n communityId,\n spaces,\n everySpace,\n showsImages(community.status, communityId, actor),\n ),\n viewer: {\n permissions: PERMISSION_KEYS.filter((p) => actor.permissions.has(p, communityId)),\n opensEverySpace: everySpace,\n team: itemRole.team || orgRole.team,\n roleName: role.name,\n canCreateEvents: actor.permissions.has('events:write'),\n },\n };\n }\n\n async communitySpace(\n communityId: string,\n spaceId: string,\n actor: Actor,\n ): Promise<CommunityHomeSpace> {\n if (!(await this.access.isCommunityMember(communityId, actor))) {\n throw new NotFoundError('Space', spaceId);\n }\n const system = this.system(actor);\n const [space, community] = await Promise.all([\n this.content.getSpace(spaceId, system),\n this.content.findCommunity(communityId, system),\n ]);\n if (!space || space.communityId !== communityId || !community) {\n throw new NotFoundError('Space', spaceId);\n }\n const everySpace = await this.access.opensEverySpace(actor, communityId);\n const [view] = await this.homeSpaces(\n actor,\n communityId,\n [space],\n everySpace,\n showsImages(community.status, communityId, actor),\n );\n if (!view) {\n throw new NotFoundError('Space', spaceId);\n }\n return view;\n }\n\n private async homeSpaces(\n actor: Actor,\n communityId: string,\n spaces: SpaceView[],\n everySpace: boolean,\n images: boolean,\n ): Promise<CommunityHomeSpace[]> {\n const orgId = actor.orgId;\n const excluding = await this.access.everySpaceOpeners(orgId, communityId);\n const [openIds, counts, standings, subscribed] = await Promise.all([\n this.access.openSpaceIds(communityId, actor),\n this.repo.spaceMemberCounts(orgId, communityId, excluding),\n this.access.spaceStandings(communityId, actor),\n this.subscriptions.viewerSubscriptions(\n orgId,\n actor.id,\n 'space',\n spaces.map((space) => space.id),\n new Date(),\n ),\n ]);\n const open = new Set(openIds);\n const seen = spaces.filter((space) =>\n seesSpace(everySpace, space.accessRule, standings[space.id]),\n );\n return Promise.all(\n seen.map(async (space): Promise<CommunityHomeSpace> => {\n const memberCount = counts[space.id] ?? 0;\n const base = {\n id: space.id,\n name: space.name,\n memberCount,\n accessRule: space.accessRule,\n standing: standingOf(space, open.has(space.id), memberCount, standings[space.id]),\n coverUrl: images ? space.coverUrl : null,\n backgroundUrl: images ? space.backgroundUrl : null,\n iconUrl: images ? space.iconUrl : null,\n };\n if (!open.has(space.id)) {\n return { ...base, locked: true };\n }\n return {\n ...base,\n locked: false,\n description: space.description,\n capacity: space.capacity,\n settings: space.settings,\n topics: space.topics.map((topic) => ({ id: topic.id, name: topic.name })),\n links: await this.linkViews(actor, space.links),\n subscription: subscribed[space.id] ?? null,\n };\n }),\n );\n }\n\n private async linkViews(actor: Actor, links: SpaceDetail['links']): Promise<CommunityHomeLink[]> {\n const system = this.system(actor);\n const shown = (type: keyof typeof READ_OF, id: string, status: string) =>\n status === 'published' || actor.permissions.has(READ_OF[type], id);\n const views = await Promise.all(\n links.map(async (link): Promise<CommunityHomeLink | null> => {\n const course = await this.content.findCourse(link.contentId, system);\n if (course) {\n return shown('course', link.contentId, course.status)\n ? { id: link.id, contentId: link.contentId, type: 'course', title: course.title }\n : null;\n }\n const download = await this.content.findDownload(link.contentId, system);\n if (download) {\n return shown('download', link.contentId, download.status)\n ? { id: link.id, contentId: link.contentId, type: 'download', title: download.title }\n : null;\n }\n const community = await this.content.findCommunity(link.contentId, system);\n if (community) {\n return shown('community', link.contentId, community.status)\n ? { id: link.id, contentId: link.contentId, type: 'community', title: community.title }\n : null;\n }\n return null;\n }),\n );\n return views.filter((view): view is CommunityHomeLink => view !== null);\n }\n}\n\nfunction showsImages(status: string, communityId: string, actor: Actor): boolean {\n return status === 'published' || actor.permissions.has('communities:read', communityId);\n}\n\nfunction standingOf(\n space: SpaceDetail,\n open: boolean,\n memberCount: number,\n status: SpaceMember['status'] | undefined,\n): CommunityHomeSpace['standing'] {\n if (space.accessRule === 'all_members' || (open && status !== 'active')) {\n return 'open';\n }\n if (status === 'active') {\n return 'member';\n }\n if (status === 'requested' || status === 'invited') {\n return status;\n }\n if (space.capacity !== null && memberCount >= space.capacity) {\n return 'full';\n }\n return 'none';\n}\n"],"mappings":"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"}
@@ -20,12 +20,15 @@ interface ScheduledAutomation {
20
20
  interface AutomationExecutor {
21
21
  runAction(d: AutomationDispatch, index: number): Promise<AutomationActionResult>;
22
22
  finalize(d: AutomationDispatch, results: AutomationActionResult[]): Promise<void>;
23
- runDelivery(d: DeliveryDispatch, lastAttempt: boolean): Promise<void>;
24
23
  listSchedules?(): Promise<ScheduledAutomation[]>;
25
24
  fireSchedule?(orgId: string, automationId: string, firedAt: Date): Promise<void>;
26
25
  }
26
+ interface DeliveryExecutor {
27
+ runDelivery(d: DeliveryDispatch, lastAttempt: boolean): Promise<void>;
28
+ }
27
29
  interface AutomationEngine {
28
30
  register(executor: AutomationExecutor): void;
31
+ registerDeliveries(executor: DeliveryExecutor): void;
29
32
  dispatch(d: AutomationDispatch): Promise<void>;
30
33
  deliver(d: DeliveryDispatch): Promise<void>;
31
34
  start(): Promise<void>;
@@ -44,5 +47,5 @@ interface WebhookSender {
44
47
  send(request: WebhookRequest): Promise<WebhookResponse>;
45
48
  }
46
49
  //#endregion
47
- export { type Automation, type AutomationAction, type AutomationActionResult, AutomationDispatch, AutomationEngine, AutomationExecutor, type AutomationKind, type AutomationRun, type AutomationRunStatus, type AutomationRunsQuery, type AutomationTrigger, type AutomationsQuery, type AvailableAction, type AvailableActions, type AvailableTriggers, type CreateAutomationInput, type CreateWebhookInput, type CreatedWebhook, type DeliverWebhookInput, DeliveryDispatch, ScheduledAutomation, type UpdateAutomationInput, type UpdateWebhookInput, type Webhook, WebhookRequest, WebhookResponse, type WebhookSecret, WebhookSender };
50
+ export { type Automation, type AutomationAction, type AutomationActionResult, AutomationDispatch, AutomationEngine, AutomationExecutor, type AutomationKind, type AutomationRun, type AutomationRunStatus, type AutomationRunsQuery, type AutomationTrigger, type AutomationsQuery, type AvailableAction, type AvailableActions, type AvailableTriggers, type CreateAutomationInput, type CreateWebhookInput, type CreatedWebhook, type DeliverWebhookInput, DeliveryDispatch, DeliveryExecutor, ScheduledAutomation, type UpdateAutomationInput, type UpdateWebhookInput, type Webhook, WebhookRequest, WebhookResponse, type WebhookSecret, WebhookSender };
48
51
  //# sourceMappingURL=automations.d.ts.map
@@ -17,8 +17,8 @@ import "./assets.js";
17
17
  import { ConfigureInput, ConnectInput, Connection, Validation } from "./schemas/integrations.js";
18
18
  import { Action, ActionContext, Integration } from "./integrations.js";
19
19
  import { Automation, AutomationAction, AutomationActionResult, AutomationKind, AutomationRun, AutomationRunStatus, AutomationRunsQuery, AutomationTrigger, AutomationsQuery, AvailableAction, AvailableActions, AvailableTriggers, CreateAutomationInput, CreateWebhookInput, CreatedWebhook, DeliverWebhookInput, UpdateAutomationInput, UpdateWebhookInput, Webhook, WebhookSecret } from "./schemas/automations.js";
20
- import { AutomationDispatch, AutomationEngine, AutomationExecutor, DeliveryDispatch, ScheduledAutomation, WebhookRequest, WebhookResponse, WebhookSender } from "./automations.js";
20
+ import { AutomationDispatch, AutomationEngine, AutomationExecutor, DeliveryDispatch, DeliveryExecutor, ScheduledAutomation, WebhookRequest, WebhookResponse, WebhookSender } from "./automations.js";
21
21
  import { ActivityCommentsRule, BlockedUser, Chat, ChatKind, ChatLastMessage, ChatLiveEvent, ChatStatus, ChatUnreadSummary, ChatUser, CommentFlagResolution, CommentListItem, CommentReaction, CommentReportSummary, CommentSettings, CommentStatus, CommentView, CommentsConfig, DigestRun, FlaggedComment, FlaggedCommentView, ListCommentsQuery, LiveMessage, Mention, MentionItemType, MentionView, Message, MessageAttachment, MessageFile, MessageFlag, MessageFlagResolution, MessageReaction, MessageView, Post, PostBookmark, PostFlag, PostFlagResolution, PostReaction, PostStatus, PostView, ReactionCounts, ReactionEmoji, Subscription, SubscriptionItemType, SubscriptionState, SubscriptionView } from "./schemas/discussion.js";
22
22
  import { Comment, CommentAuthor, CommentReport } from "./discussion.js";
23
23
  import { ChannelRecipient, Delivery, DeliveryResult, DeliveryStatus, DeviceView, NotificationCategory, NotificationFilter, NotificationTypeDeclaration, PushConfig, PushDevice, PushMessage, PushProvider, Reason, RecipientList, Scope, SettingsScope, StoredNotification, StoredSetting, SubjectRef } from "./notifications.js";
24
- export { type AcceptInviteInput, AccessGrant, AccessRequest, AccessTokenVerifier, AccountProvisioner, Action, ActionContext, ActiveSession, type Activity, type ActivityAsset, type ActivityCommentsRule, type ActivitySettings, type ActivityView, type AddDownloadAssetInput, type AddEventAssetInput, type AddOrgUserInput, type AddSpaceLinkInput, ApiKeySummary, type Asset, type AssetKind, type AssetSource, type AssetStatus, type AssetUrls, type AssetView, type AssetsQuery, type Automation, type AutomationAction, type AutomationActionResult, AutomationDispatch, AutomationEngine, AutomationExecutor, type AutomationKind, type AutomationRun, type AutomationRunStatus, type AutomationRunsQuery, type AutomationTrigger, type AutomationsQuery, type AvailableAction, type AvailableActions, type AvailableTriggers, type BlockedUser, type Bundle, type BundleItem, ChannelRecipient, type Chat, type ChatKind, type ChatLastMessage, type ChatLiveEvent, type ChatStatus, type ChatUnreadSummary, type ChatUser, Comment, CommentAuthor, type CommentFlagResolution, type CommentListItem, type CommentReaction, CommentReport, type CommentReportSummary, type CommentSettings, type CommentStatus, type CommentView, type CommentsConfig, type Community, type CommunitySettings, type CommunityStatus, type CommunityView, type ConfigureInput, type ConnectInput, ConnectedApp, type Connection, ContentId, type ContentItem, type ContentType, ContractNewDomainEvent, type Course, type CourseSettings, type CourseStatus, type CourseView, type CreateAutomationInput, type CreateBundleInput, type CreateCommunityInput, type CreateCourseInput, type CreateDownloadInput, type CreateEventInput, type CreateInviteInput, type CreateOrgUserInput, CreateOrganizationInput, type CreateSpaceInput, type CreateUserInput, type CreateWebhookInput, CreatedApiKey, type CreatedWebhook, CredentialLimit, type DeliverWebhookInput, Delivery, DeliveryDispatch, DeliveryResult, DeliveryStatus, DeviceView, type DigestCadence, type DigestRun, type DigestSchedule, type DmSettings, DomainEvent, type Download, type DownloadAsset, type DownloadFile, type DownloadStatus, type DownloadView, EmailContent, EmailMessage, EmailSender, EmailTemplateId, EmailTemplateParams, type Entitlement, type EntitlementStatus, type EntitlementsQuery, type Event, type EventAddress, type EventAnalytics, type EventAsset, type EventAttendee, type EventAttendeeFace, type EventAttendeeSource, EventDefinition, EventEmailParams, type EventFile, type EventLead, type EventLocation, EventMetadata, type EventOnline, EventParseResult, type EventRecording, type EventSettings, type EventSort, type EventStatus, type EventType, type EventVenue, type EventView, type FlaggedComment, type FlaggedCommentView, type GrantEntitlementInput, type GrantSource, HeaderBag, Integration, type Invite, InviteId, type InviteInput, JsonValue, type Level, type ListBundlesQuery, type ListCommentsQuery, type ListCommunitiesQuery, type ListCoursesQuery, type ListDownloadsQuery, type ListEventsQuery, type LiveMessage, Logger, MakeEventInput, type MemberEvent, type MemberEventSummary, type MemberEventViewer, type MemberEventsQuery, type Mention, type MentionItemType, type MentionView, type Message, type MessageAttachment, type MessageFile, type MessageFlag, type MessageFlagResolution, type MessageReaction, type MessageView, type Module, NewDomainEvent, type NewOrganizationInput, NotificationCategory, NotificationFilter, NotificationTypeDeclaration, ObjectStorage, type OrgRoleRow, type OrgUser, OrgUserId, type OrgUserInput, type OrgUserProfile, type OrgUserStatus, type Organization, OrganizationId, type OrganizationInput, type OrganizationMetadata, PUBLIC_CACHE_CONTROL, PUBLIC_PREFIX, Page, type PatchCommunitySettingsInput, type PatchDmSettingsInput, type Permission, type Post, type PostBookmark, type PostFlag, type PostFlagResolution, type PostReaction, type PostStatus, type PostView, PresignDownloadInput, PresignedUpload, ProgressId, type ProgressRecord, type ProgressRecordInput, type ProgressReportItem, type ProgressTarget, type ProgressTargetType, type ProvisionUserInput, PushConfig, PushDevice, PushMessage, PushProvider, type ReactionCounts, type ReactionEmoji, Reason, RecipientList, type ReorderDownloadAssetsInput, type ReorderEventAssetsInput, type ReportProgressInput, type RequestUploadInput, type RoleAssignment, type RoleKey, type SaveActivityInput, ScheduledAutomation, Scope, SessionVerifier, type SetSpaceTopicsInput, SettingsScope, type Space, type SpaceAccessRule, type SpaceDetail, type SpaceImage, type SpaceLink, type SpaceMember, type SpaceMemberSource, type SpaceMemberStatus, type SpaceSettings, type SpaceTopic, type SpaceView, StoredNotification, StoredObjectInfo, StoredSetting, SubjectRef, type Subscription, type SubscriptionItemType, type SubscriptionState, type SubscriptionView, TemplateContext, TemplateRenderer, type UpdateAutomationInput, type UpdateBundleInput, type UpdateCommunityInput, type UpdateCourseInput, type UpdateDownloadInput, type UpdateEventInput, type UpdateOrgUserInput, type UpdateSpaceInput, type UpdateUserInput, type UpdateWebhookInput, type UploadTicket, User, UserId, type UserProfile, type UserProfileView, type Validation, type Webhook, WebhookRequest, WebhookResponse, type WebhookSecret, WebhookSender };
24
+ export { type AcceptInviteInput, AccessGrant, AccessRequest, AccessTokenVerifier, AccountProvisioner, Action, ActionContext, ActiveSession, type Activity, type ActivityAsset, type ActivityCommentsRule, type ActivitySettings, type ActivityView, type AddDownloadAssetInput, type AddEventAssetInput, type AddOrgUserInput, type AddSpaceLinkInput, ApiKeySummary, type Asset, type AssetKind, type AssetSource, type AssetStatus, type AssetUrls, type AssetView, type AssetsQuery, type Automation, type AutomationAction, type AutomationActionResult, AutomationDispatch, AutomationEngine, AutomationExecutor, type AutomationKind, type AutomationRun, type AutomationRunStatus, type AutomationRunsQuery, type AutomationTrigger, type AutomationsQuery, type AvailableAction, type AvailableActions, type AvailableTriggers, type BlockedUser, type Bundle, type BundleItem, ChannelRecipient, type Chat, type ChatKind, type ChatLastMessage, type ChatLiveEvent, type ChatStatus, type ChatUnreadSummary, type ChatUser, Comment, CommentAuthor, type CommentFlagResolution, type CommentListItem, type CommentReaction, CommentReport, type CommentReportSummary, type CommentSettings, type CommentStatus, type CommentView, type CommentsConfig, type Community, type CommunitySettings, type CommunityStatus, type CommunityView, type ConfigureInput, type ConnectInput, ConnectedApp, type Connection, ContentId, type ContentItem, type ContentType, ContractNewDomainEvent, type Course, type CourseSettings, type CourseStatus, type CourseView, type CreateAutomationInput, type CreateBundleInput, type CreateCommunityInput, type CreateCourseInput, type CreateDownloadInput, type CreateEventInput, type CreateInviteInput, type CreateOrgUserInput, CreateOrganizationInput, type CreateSpaceInput, type CreateUserInput, type CreateWebhookInput, CreatedApiKey, type CreatedWebhook, CredentialLimit, type DeliverWebhookInput, Delivery, DeliveryDispatch, DeliveryExecutor, DeliveryResult, DeliveryStatus, DeviceView, type DigestCadence, type DigestRun, type DigestSchedule, type DmSettings, DomainEvent, type Download, type DownloadAsset, type DownloadFile, type DownloadStatus, type DownloadView, EmailContent, EmailMessage, EmailSender, EmailTemplateId, EmailTemplateParams, type Entitlement, type EntitlementStatus, type EntitlementsQuery, type Event, type EventAddress, type EventAnalytics, type EventAsset, type EventAttendee, type EventAttendeeFace, type EventAttendeeSource, EventDefinition, EventEmailParams, type EventFile, type EventLead, type EventLocation, EventMetadata, type EventOnline, EventParseResult, type EventRecording, type EventSettings, type EventSort, type EventStatus, type EventType, type EventVenue, type EventView, type FlaggedComment, type FlaggedCommentView, type GrantEntitlementInput, type GrantSource, HeaderBag, Integration, type Invite, InviteId, type InviteInput, JsonValue, type Level, type ListBundlesQuery, type ListCommentsQuery, type ListCommunitiesQuery, type ListCoursesQuery, type ListDownloadsQuery, type ListEventsQuery, type LiveMessage, Logger, MakeEventInput, type MemberEvent, type MemberEventSummary, type MemberEventViewer, type MemberEventsQuery, type Mention, type MentionItemType, type MentionView, type Message, type MessageAttachment, type MessageFile, type MessageFlag, type MessageFlagResolution, type MessageReaction, type MessageView, type Module, NewDomainEvent, type NewOrganizationInput, NotificationCategory, NotificationFilter, NotificationTypeDeclaration, ObjectStorage, type OrgRoleRow, type OrgUser, OrgUserId, type OrgUserInput, type OrgUserProfile, type OrgUserStatus, type Organization, OrganizationId, type OrganizationInput, type OrganizationMetadata, PUBLIC_CACHE_CONTROL, PUBLIC_PREFIX, Page, type PatchCommunitySettingsInput, type PatchDmSettingsInput, type Permission, type Post, type PostBookmark, type PostFlag, type PostFlagResolution, type PostReaction, type PostStatus, type PostView, PresignDownloadInput, PresignedUpload, ProgressId, type ProgressRecord, type ProgressRecordInput, type ProgressReportItem, type ProgressTarget, type ProgressTargetType, type ProvisionUserInput, PushConfig, PushDevice, PushMessage, PushProvider, type ReactionCounts, type ReactionEmoji, Reason, RecipientList, type ReorderDownloadAssetsInput, type ReorderEventAssetsInput, type ReportProgressInput, type RequestUploadInput, type RoleAssignment, type RoleKey, type SaveActivityInput, ScheduledAutomation, Scope, SessionVerifier, type SetSpaceTopicsInput, SettingsScope, type Space, type SpaceAccessRule, type SpaceDetail, type SpaceImage, type SpaceLink, type SpaceMember, type SpaceMemberSource, type SpaceMemberStatus, type SpaceSettings, type SpaceTopic, type SpaceView, StoredNotification, StoredObjectInfo, StoredSetting, SubjectRef, type Subscription, type SubscriptionItemType, type SubscriptionState, type SubscriptionView, TemplateContext, TemplateRenderer, type UpdateAutomationInput, type UpdateBundleInput, type UpdateCommunityInput, type UpdateCourseInput, type UpdateDownloadInput, type UpdateEventInput, type UpdateOrgUserInput, type UpdateSpaceInput, type UpdateUserInput, type UpdateWebhookInput, type UploadTicket, User, UserId, type UserProfile, type UserProfileView, type Validation, type Webhook, WebhookRequest, WebhookResponse, type WebhookSecret, WebhookSender };