@headless-lms/core 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/events.d.ts +9 -9
- package/dist/assets/index.d.ts +3 -2
- package/dist/assets/index.js +2 -1
- package/dist/assets/jobs.d.ts +15 -0
- package/dist/assets/jobs.js +30 -0
- package/dist/assets/jobs.js.map +1 -0
- package/dist/assets/ports.d.ts +4 -3
- package/dist/assets/service.d.ts +0 -1
- package/dist/assets/service.js +0 -4
- package/dist/assets/service.js.map +1 -1
- package/dist/automations/actions.js +2 -63
- package/dist/automations/actions.js.map +1 -1
- package/dist/automations/catalog.d.ts +1 -2
- package/dist/automations/catalog.js +69 -62
- package/dist/automations/catalog.js.map +1 -1
- package/dist/automations/community-derivations.js +4 -4
- package/dist/automations/community-derivations.js.map +1 -1
- package/dist/automations/community-mail.d.ts +3 -17
- package/dist/automations/community-mail.js +1 -21
- package/dist/automations/community-mail.js.map +1 -1
- package/dist/automations/events.d.ts +0 -10
- package/dist/automations/index.d.ts +11 -4
- package/dist/automations/index.js +8 -1
- package/dist/automations/notifications/debounce.d.ts +25 -0
- package/dist/automations/notifications/debounce.js +93 -0
- package/dist/automations/notifications/debounce.js.map +1 -0
- package/dist/automations/notifications/defaults.d.ts +8 -0
- package/dist/automations/notifications/defaults.js +316 -0
- package/dist/automations/notifications/defaults.js.map +1 -0
- package/dist/automations/notifications/delivery.d.ts +28 -0
- package/dist/automations/notifications/delivery.js +88 -0
- package/dist/automations/notifications/delivery.js.map +1 -0
- package/dist/automations/notifications/processor.d.ts +64 -0
- package/dist/automations/notifications/processor.js +270 -0
- package/dist/automations/notifications/processor.js.map +1 -0
- package/dist/automations/notifications/rules/content.d.ts +6 -0
- package/dist/automations/notifications/rules/content.js +39 -0
- package/dist/automations/notifications/rules/content.js.map +1 -0
- package/dist/automations/notifications/rules/discussion.d.ts +7 -0
- package/dist/automations/notifications/rules/discussion.js +678 -0
- package/dist/automations/notifications/rules/discussion.js.map +1 -0
- package/dist/automations/notifications/rules/entitlements.d.ts +6 -0
- package/dist/automations/notifications/rules/entitlements.js +249 -0
- package/dist/automations/notifications/rules/entitlements.js.map +1 -0
- package/dist/automations/notifications/rules/index.d.ts +6 -0
- package/dist/automations/notifications/rules/index.js +15 -0
- package/dist/automations/notifications/rules/index.js.map +1 -0
- package/dist/automations/notifications/rules/organizations.d.ts +6 -0
- package/dist/automations/notifications/rules/organizations.js +28 -0
- package/dist/automations/notifications/rules/organizations.js.map +1 -0
- package/dist/automations/notifications/rules/recipients.d.ts +32 -0
- package/dist/automations/notifications/rules/recipients.js +99 -0
- package/dist/automations/notifications/rules/recipients.js.map +1 -0
- package/dist/automations/notifications/service.d.ts +27 -0
- package/dist/automations/notifications/service.js +45 -0
- package/dist/automations/notifications/service.js.map +1 -0
- package/dist/automations/notifications/types.d.ts +7 -0
- package/dist/automations/notifications/types.js +473 -0
- package/dist/automations/notifications/types.js.map +1 -0
- package/dist/automations/ports.d.ts +119 -5
- package/dist/automations/service.d.ts +10 -13
- package/dist/automations/service.js +21 -83
- package/dist/automations/service.js.map +1 -1
- package/dist/content/events.d.ts +130 -80
- package/dist/content/events.js +11 -0
- package/dist/content/events.js.map +1 -1
- package/dist/content/index.d.ts +6 -4
- package/dist/content/index.js +3 -1
- package/dist/content/model.d.ts +2 -2
- package/dist/content/notification-categories.d.ts +36 -0
- package/dist/content/notification-categories.js +64 -0
- package/dist/content/notification-categories.js.map +1 -0
- package/dist/content/ports.d.ts +25 -6
- package/dist/content/service.d.ts +4 -4
- package/dist/content/service.js +24 -8
- package/dist/content/service.js.map +1 -1
- package/dist/content/titles.d.ts +22 -0
- package/dist/content/titles.js +69 -0
- package/dist/content/titles.js.map +1 -0
- package/dist/content/types.d.ts +2 -2
- package/dist/discussion/chats.d.ts +9 -4
- package/dist/discussion/chats.js +80 -47
- package/dist/discussion/chats.js.map +1 -1
- package/dist/discussion/digests.d.ts +24 -0
- package/dist/discussion/digests.js +104 -0
- package/dist/discussion/digests.js.map +1 -0
- package/dist/discussion/events.d.ts +194 -28
- package/dist/discussion/events.js +74 -17
- package/dist/discussion/events.js.map +1 -1
- package/dist/discussion/index.d.ts +7 -2
- package/dist/discussion/index.js +6 -1
- package/dist/discussion/jobs.d.ts +16 -0
- package/dist/discussion/jobs.js +40 -0
- package/dist/discussion/jobs.js.map +1 -0
- package/dist/discussion/mentions.d.ts +11 -0
- package/dist/discussion/mentions.js +35 -0
- package/dist/discussion/mentions.js.map +1 -0
- package/dist/discussion/ports.d.ts +107 -24
- package/dist/discussion/ports.js.map +1 -1
- package/dist/discussion/posts.d.ts +18 -8
- package/dist/discussion/posts.js +207 -155
- package/dist/discussion/posts.js.map +1 -1
- package/dist/discussion/service.d.ts +17 -8
- package/dist/discussion/service.js +105 -41
- package/dist/discussion/service.js.map +1 -1
- package/dist/discussion/subscriptions.d.ts +37 -0
- package/dist/discussion/subscriptions.js +99 -0
- package/dist/discussion/subscriptions.js.map +1 -0
- package/dist/discussion/titles.d.ts +26 -0
- package/dist/discussion/titles.js +84 -0
- package/dist/discussion/titles.js.map +1 -0
- package/dist/entitlements/events.d.ts +20 -20
- package/dist/entitlements/index.d.ts +2 -1
- package/dist/entitlements/index.js +2 -1
- package/dist/entitlements/jobs.d.ts +12 -0
- package/dist/entitlements/jobs.js +18 -0
- package/dist/entitlements/jobs.js.map +1 -0
- package/dist/entitlements/ports.d.ts +7 -3
- package/dist/entitlements/service.d.ts +5 -2
- package/dist/entitlements/service.js +23 -3
- package/dist/entitlements/service.js.map +1 -1
- package/dist/identity/devices.d.ts +30 -0
- package/dist/identity/devices.js +107 -0
- package/dist/identity/devices.js.map +1 -0
- package/dist/identity/events.d.ts +2 -2
- package/dist/identity/index.d.ts +5 -4
- package/dist/identity/index.js +3 -2
- package/dist/identity/model.d.ts +2 -2
- package/dist/identity/notification-settings.d.ts +28 -0
- package/dist/identity/notification-settings.js +113 -0
- package/dist/identity/notification-settings.js.map +1 -0
- package/dist/identity/ports.d.ts +78 -54
- package/dist/integrations/events.d.ts +3 -3
- package/dist/organizations/events.d.ts +32 -32
- package/dist/organizations/index.d.ts +2 -1
- package/dist/organizations/index.js +2 -1
- package/dist/organizations/notification-categories.d.ts +32 -0
- package/dist/organizations/notification-categories.js +51 -0
- package/dist/organizations/notification-categories.js.map +1 -0
- package/dist/organizations/ports.d.ts +3 -0
- package/dist/organizations/service.d.ts +2 -0
- package/dist/organizations/service.js +7 -0
- package/dist/organizations/service.js.map +1 -1
- package/dist/progress/events.d.ts +14 -14
- package/dist/reporting/member/index.d.ts +2 -2
- package/dist/reporting/member/model.d.ts +56 -23
- package/dist/reporting/member/ports.d.ts +9 -6
- package/dist/reporting/member/service.d.ts +33 -14
- package/dist/reporting/member/service.js +138 -60
- package/dist/reporting/member/service.js.map +1 -1
- package/dist/shared/errors.d.ts +13 -1
- package/dist/shared/errors.js +18 -1
- package/dist/shared/errors.js.map +1 -1
- package/dist/shared/id.d.ts +3 -0
- package/dist/shared/id.js +4 -1
- package/dist/shared/id.js.map +1 -1
- package/dist/shared/mailer.d.ts +11 -1
- package/dist/shared/mailer.js +32 -5
- package/dist/shared/mailer.js.map +1 -1
- package/dist/shared/notification-categories.d.ts +16 -0
- package/dist/shared/notification-categories.js +94 -0
- package/dist/shared/notification-categories.js.map +1 -0
- package/dist/shared/notification-settings.d.ts +14 -0
- package/dist/shared/notification-settings.js +61 -0
- package/dist/shared/notification-settings.js.map +1 -0
- package/dist/shared/ports.d.ts +48 -2
- package/dist/shared/settings.d.ts +2 -1
- package/dist/shared/settings.js +1 -0
- package/dist/shared/settings.js.map +1 -1
- package/dist/types/automations.d.ts +8 -2
- package/dist/types/content.d.ts +2 -2
- package/dist/types/discussion.d.ts +2 -2
- package/dist/types/email-templates.d.ts +97 -3
- package/dist/types/identity.d.ts +2 -2
- package/dist/types/index.d.ts +7 -6
- package/dist/types/notifications.d.ts +157 -0
- package/dist/types/notifications.js +1 -0
- package/dist/types/schemas/automations.d.ts +1 -10
- package/dist/types/schemas/automations.js +1 -3
- package/dist/types/schemas/automations.js.map +1 -1
- package/dist/types/schemas/content.d.ts +62 -1
- package/dist/types/schemas/content.js +25 -3
- package/dist/types/schemas/content.js.map +1 -1
- package/dist/types/schemas/discussion.d.ts +85 -12
- package/dist/types/schemas/discussion.js +47 -13
- package/dist/types/schemas/discussion.js.map +1 -1
- package/dist/types/schemas/identity.d.ts +1 -85
- package/dist/types/schemas/identity.js +1 -42
- package/dist/types/schemas/identity.js.map +1 -1
- package/dist/types/schemas/index.d.ts +6 -5
- package/dist/types/schemas/index.js +6 -5
- package/dist/types/schemas/notifications.d.ts +95 -0
- package/dist/types/schemas/notifications.js +56 -0
- package/dist/types/schemas/notifications.js.map +1 -0
- package/dist/types/schemas/progress.d.ts +3 -3
- package/package.json +1 -1
- package/dist/automations/community-notifications.d.ts +0 -16
- package/dist/automations/community-notifications.js +0 -84
- package/dist/automations/community-notifications.js.map +0 -1
- package/dist/automations/seeds.d.ts +0 -17
- package/dist/automations/seeds.js +0 -235
- package/dist/automations/seeds.js.map +0 -1
- package/dist/identity/notifications.d.ts +0 -27
- package/dist/identity/notifications.js +0 -101
- package/dist/identity/notifications.js.map +0 -1
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { NotFoundError, ValidationError } from "../shared/errors.js";
|
|
2
|
+
import { noopLogger } from "../shared/logger.js";
|
|
3
|
+
//#region src/identity/notification-settings.ts
|
|
4
|
+
function communityOf(scope) {
|
|
5
|
+
return scope === "org" ? null : scope.communityId;
|
|
6
|
+
}
|
|
7
|
+
function keyOf(key) {
|
|
8
|
+
return JSON.stringify([
|
|
9
|
+
key.communityId,
|
|
10
|
+
key.spaceId,
|
|
11
|
+
key.category,
|
|
12
|
+
key.channel
|
|
13
|
+
]);
|
|
14
|
+
}
|
|
15
|
+
var NotificationSettingsServiceImpl = class {
|
|
16
|
+
repo;
|
|
17
|
+
categories;
|
|
18
|
+
access;
|
|
19
|
+
channels;
|
|
20
|
+
logger;
|
|
21
|
+
constructor(params) {
|
|
22
|
+
this.repo = params.repo;
|
|
23
|
+
this.categories = params.categories;
|
|
24
|
+
this.access = params.access;
|
|
25
|
+
this.channels = params.channels;
|
|
26
|
+
this.logger = params.logger ?? noopLogger;
|
|
27
|
+
}
|
|
28
|
+
async get(orgId, userId, scope) {
|
|
29
|
+
return (await this.repo.forUsers(orgId, [userId], communityOf(scope), null)).map(({ userId: _userId, ...setting }) => setting);
|
|
30
|
+
}
|
|
31
|
+
async getMany(orgId, userIds, scope, categories) {
|
|
32
|
+
const unique = [...new Set(userIds)];
|
|
33
|
+
const result = Object.fromEntries(unique.map((userId) => [userId, []]));
|
|
34
|
+
if (unique.length === 0 || categories.length === 0) return result;
|
|
35
|
+
const rows = await this.repo.forUsers(orgId, unique, communityOf(scope), categories);
|
|
36
|
+
for (const { userId, ...setting } of rows) result[userId]?.push(setting);
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
async set(orgId, actor, scope, changes) {
|
|
40
|
+
const communityId = communityOf(scope);
|
|
41
|
+
let list;
|
|
42
|
+
if (communityId === null) list = await this.categories.orgList(orgId);
|
|
43
|
+
else {
|
|
44
|
+
if (!await this.access.canOpen(orgId, actor, communityId)) throw new NotFoundError("Community", communityId);
|
|
45
|
+
list = await this.categories.communityList(orgId, communityId);
|
|
46
|
+
}
|
|
47
|
+
const configured = this.channels.list();
|
|
48
|
+
const problems = [];
|
|
49
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
50
|
+
changes.forEach((change, index) => {
|
|
51
|
+
const at = `settings[${index}]`;
|
|
52
|
+
const category = list.find((candidate) => candidate.key === change.category);
|
|
53
|
+
if (!category) {
|
|
54
|
+
problems.push({
|
|
55
|
+
path: `${at}.category`,
|
|
56
|
+
message: `there is no category ${change.category}`
|
|
57
|
+
});
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const offered = category.channels[change.channel];
|
|
61
|
+
if (!offered || !configured.includes(change.channel)) {
|
|
62
|
+
problems.push({
|
|
63
|
+
path: `${at}.channel`,
|
|
64
|
+
message: `${change.category} does not offer the channel ${change.channel}`
|
|
65
|
+
});
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (offered.locked) {
|
|
69
|
+
problems.push({
|
|
70
|
+
path: `${at}.channel`,
|
|
71
|
+
message: `the channel ${change.channel} of ${change.category} is locked`
|
|
72
|
+
});
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (change.spaceId !== void 0 && category.perSpace !== true) {
|
|
76
|
+
problems.push({
|
|
77
|
+
path: `${at}.spaceId`,
|
|
78
|
+
message: `${change.category} cannot be set per space`
|
|
79
|
+
});
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const key = {
|
|
83
|
+
communityId,
|
|
84
|
+
spaceId: change.spaceId ?? null,
|
|
85
|
+
category: change.category,
|
|
86
|
+
channel: change.channel
|
|
87
|
+
};
|
|
88
|
+
byKey.set(keyOf(key), {
|
|
89
|
+
key,
|
|
90
|
+
enabled: change.enabled
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
if (problems.length > 0) throw new ValidationError(problems);
|
|
94
|
+
const entries = [...byKey.values()];
|
|
95
|
+
await this.repo.apply(orgId, actor.userId, entries.flatMap(({ key, enabled }) => enabled === null ? [] : [{
|
|
96
|
+
...key,
|
|
97
|
+
enabled
|
|
98
|
+
}]), entries.flatMap(({ key, enabled }) => enabled === null ? [key] : []), /* @__PURE__ */ new Date());
|
|
99
|
+
this.logger.info("notification settings changed", {
|
|
100
|
+
orgId,
|
|
101
|
+
userId: actor.userId,
|
|
102
|
+
communityId,
|
|
103
|
+
count: entries.length
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
switchedOn(orgId, communityId, category, spaceId) {
|
|
107
|
+
return this.repo.switchedOn(orgId, communityId, category, spaceId);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
//#endregion
|
|
111
|
+
export { NotificationSettingsServiceImpl };
|
|
112
|
+
|
|
113
|
+
//# sourceMappingURL=notification-settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-settings.js","names":[],"sources":["../../src/identity/notification-settings.ts"],"sourcesContent":["import type { NotificationCategory, SettingsScope, StoredSetting } from '../types/index.js';\nimport type { ChannelRegistry, Logger } from '../shared/ports.js';\nimport { NotFoundError, ValidationError, type ValidationProblem } from '../shared/errors.js';\nimport { noopLogger } from '../shared/logger.js';\nimport type {\n CommunityAccessReader,\n MemberActor,\n NotificationCategoryReader,\n NotificationSettingsRepository,\n NotificationSettingsService,\n SettingChange,\n SettingKey,\n} from './ports.js';\n\nfunction communityOf(scope: SettingsScope): string | null {\n return scope === 'org' ? null : scope.communityId;\n}\n\nfunction keyOf(key: SettingKey): string {\n return JSON.stringify([key.communityId, key.spaceId, key.category, key.channel]);\n}\n\nexport type NotificationSettingsServiceParams = {\n repo: NotificationSettingsRepository;\n categories: NotificationCategoryReader;\n access: CommunityAccessReader;\n channels: Pick<ChannelRegistry, 'list'>;\n logger?: Logger;\n};\n\nexport class NotificationSettingsServiceImpl implements NotificationSettingsService {\n private readonly repo: NotificationSettingsRepository;\n private readonly categories: NotificationCategoryReader;\n private readonly access: CommunityAccessReader;\n private readonly channels: Pick<ChannelRegistry, 'list'>;\n private readonly logger: Logger;\n\n constructor(params: NotificationSettingsServiceParams) {\n this.repo = params.repo;\n this.categories = params.categories;\n this.access = params.access;\n this.channels = params.channels;\n this.logger = params.logger ?? noopLogger;\n }\n\n async get(orgId: string, userId: string, scope: SettingsScope): Promise<StoredSetting[]> {\n const rows = await this.repo.forUsers(orgId, [userId], communityOf(scope), null);\n return rows.map(({ userId: _userId, ...setting }) => setting);\n }\n\n async getMany(\n orgId: string,\n userIds: string[],\n scope: SettingsScope,\n categories: string[],\n ): Promise<Record<string, StoredSetting[]>> {\n const unique = [...new Set(userIds)];\n const result: Record<string, StoredSetting[]> = Object.fromEntries(\n unique.map((userId) => [userId, []]),\n );\n if (unique.length === 0 || categories.length === 0) {\n return result;\n }\n const rows = await this.repo.forUsers(orgId, unique, communityOf(scope), categories);\n for (const { userId, ...setting } of rows) {\n result[userId]?.push(setting);\n }\n return result;\n }\n\n async set(\n orgId: string,\n actor: MemberActor,\n scope: SettingsScope,\n changes: SettingChange[],\n ): Promise<void> {\n const communityId = communityOf(scope);\n let list: NotificationCategory[];\n if (communityId === null) {\n list = await this.categories.orgList(orgId);\n } else {\n if (!(await this.access.canOpen(orgId, actor, communityId))) {\n throw new NotFoundError('Community', communityId);\n }\n list = await this.categories.communityList(orgId, communityId);\n }\n const configured = this.channels.list();\n const problems: ValidationProblem[] = [];\n const byKey = new Map<string, { key: SettingKey; enabled: boolean | null }>();\n changes.forEach((change, index) => {\n const at = `settings[${index}]`;\n const category = list.find((candidate) => candidate.key === change.category);\n if (!category) {\n problems.push({\n path: `${at}.category`,\n message: `there is no category ${change.category}`,\n });\n return;\n }\n const offered = category.channels[change.channel];\n if (!offered || !configured.includes(change.channel)) {\n problems.push({\n path: `${at}.channel`,\n message: `${change.category} does not offer the channel ${change.channel}`,\n });\n return;\n }\n if (offered.locked) {\n problems.push({\n path: `${at}.channel`,\n message: `the channel ${change.channel} of ${change.category} is locked`,\n });\n return;\n }\n if (change.spaceId !== undefined && category.perSpace !== true) {\n problems.push({\n path: `${at}.spaceId`,\n message: `${change.category} cannot be set per space`,\n });\n return;\n }\n const key: SettingKey = {\n communityId,\n spaceId: change.spaceId ?? null,\n category: change.category,\n channel: change.channel,\n };\n byKey.set(keyOf(key), { key, enabled: change.enabled });\n });\n if (problems.length > 0) {\n throw new ValidationError(problems);\n }\n const entries = [...byKey.values()];\n await this.repo.apply(\n orgId,\n actor.userId,\n entries.flatMap(({ key, enabled }) => (enabled === null ? [] : [{ ...key, enabled }])),\n entries.flatMap(({ key, enabled }) => (enabled === null ? [key] : [])),\n new Date(),\n );\n this.logger.info('notification settings changed', {\n orgId,\n userId: actor.userId,\n communityId,\n count: entries.length,\n });\n }\n\n switchedOn(\n orgId: string,\n communityId: string,\n category: string,\n spaceId: string | null,\n ): Promise<string[]> {\n return this.repo.switchedOn(orgId, communityId, category, spaceId);\n }\n}\n"],"mappings":"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"}
|
package/dist/identity/ports.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { JsonValue } from "../types/shared.js";
|
|
2
|
-
import { CreateUserInput,
|
|
2
|
+
import { CreateUserInput, UpdateUserInput, UserProfile, UserProfileView } from "../types/schemas/identity.js";
|
|
3
3
|
import { User } from "../types/identity.js";
|
|
4
|
+
import { DeviceView, NotificationCategory, PushDevice, PushProvider, SettingsScope, StoredSetting } from "../types/notifications.js";
|
|
4
5
|
import "../types/index.js";
|
|
5
6
|
import { OutboxAppender, UnitOfWork } from "../shared/ports.js";
|
|
7
|
+
import { Actor } from "../shared/actor.js";
|
|
6
8
|
import "./model.js";
|
|
7
9
|
import "./types.js";
|
|
8
10
|
//#region src/identity/ports.d.ts
|
|
@@ -55,44 +57,6 @@ interface SessionAdmin {
|
|
|
55
57
|
/** Points the caller's current session at `orgId`. */
|
|
56
58
|
stampActiveOrganization(headers: AuthHeaders, orgId: string): Promise<boolean>;
|
|
57
59
|
}
|
|
58
|
-
interface NewNotification {
|
|
59
|
-
userId: string;
|
|
60
|
-
kind: NotificationKind;
|
|
61
|
-
actorOrgUserId: string | null;
|
|
62
|
-
communityId: string | null;
|
|
63
|
-
spaceId: string | null;
|
|
64
|
-
postId: string | null;
|
|
65
|
-
commentId: string | null;
|
|
66
|
-
chatId: string | null;
|
|
67
|
-
messageId: string | null;
|
|
68
|
-
}
|
|
69
|
-
interface NotificationsQuery {
|
|
70
|
-
page: number;
|
|
71
|
-
pageSize: number;
|
|
72
|
-
}
|
|
73
|
-
interface NotificationPage {
|
|
74
|
-
rows: Notification[];
|
|
75
|
-
total: number;
|
|
76
|
-
page: number;
|
|
77
|
-
pageSize: number;
|
|
78
|
-
unreadCount: number;
|
|
79
|
-
}
|
|
80
|
-
/** What automations calls to tell people. */
|
|
81
|
-
interface NotificationDesk {
|
|
82
|
-
notify(orgId: string, notifications: NewNotification[]): Promise<void>;
|
|
83
|
-
preferencesFor(orgId: string, userIds: string[], kind: NotificationKind): Promise<Record<string, NotificationPreference>>;
|
|
84
|
-
}
|
|
85
|
-
/** A person's own notifications and preferences. */
|
|
86
|
-
interface NotificationInbox {
|
|
87
|
-
listNotifications(orgId: string, userId: string, query: NotificationsQuery): Promise<NotificationPage>;
|
|
88
|
-
markNotificationRead(orgId: string, userId: string, notificationId: string): Promise<Notification>;
|
|
89
|
-
markAllNotificationsRead(orgId: string, userId: string): Promise<{
|
|
90
|
-
updated: number;
|
|
91
|
-
}>;
|
|
92
|
-
notificationPreferences(orgId: string, userId: string): Promise<NotificationPreference[]>;
|
|
93
|
-
setNotificationPreferences(orgId: string, userId: string, preferences: NotificationPreference[]): Promise<NotificationPreference[]>;
|
|
94
|
-
}
|
|
95
|
-
interface NotificationService extends NotificationDesk, NotificationInbox {}
|
|
96
60
|
interface IdentityService extends UserProvisioner, UserResolver, UserEditor {
|
|
97
61
|
profileOf(orgId: string, userId: string): Promise<UserProfileView>;
|
|
98
62
|
displayName(userId: string | null): Promise<string>;
|
|
@@ -107,25 +71,85 @@ interface IdentityRepository {
|
|
|
107
71
|
findProfile(userId: string): Promise<UserProfile | null>;
|
|
108
72
|
upsertProfile(userId: string, patch: ProfilePatch): Promise<UserProfile>;
|
|
109
73
|
}
|
|
110
|
-
interface NotificationsRepository {
|
|
111
|
-
insert(orgId: string, rows: Notification[]): Promise<void>;
|
|
112
|
-
page(orgId: string, userId: string, query: NotificationsQuery): Promise<{
|
|
113
|
-
rows: Notification[];
|
|
114
|
-
total: number;
|
|
115
|
-
unreadCount: number;
|
|
116
|
-
}>;
|
|
117
|
-
markRead(orgId: string, userId: string, notificationId: string, at: Date): Promise<Notification | null>;
|
|
118
|
-
markAllRead(orgId: string, userId: string, at: Date): Promise<number>;
|
|
119
|
-
preferences(orgId: string, userIds: string[]): Promise<(NotificationPreference & {
|
|
120
|
-
userId: string;
|
|
121
|
-
})[]>;
|
|
122
|
-
upsertPreferences(orgId: string, userId: string, preferences: NotificationPreference[]): Promise<void>;
|
|
123
|
-
}
|
|
124
74
|
interface IdentityTxScope {
|
|
125
75
|
identity: IdentityRepository;
|
|
126
76
|
outbox: OutboxAppender;
|
|
127
77
|
}
|
|
128
78
|
type IdentityUnitOfWork = UnitOfWork<IdentityTxScope>;
|
|
79
|
+
type MemberActor = Actor & {
|
|
80
|
+
userId: string;
|
|
81
|
+
};
|
|
82
|
+
interface SettingChange {
|
|
83
|
+
category: string;
|
|
84
|
+
spaceId?: string;
|
|
85
|
+
channel: string;
|
|
86
|
+
enabled: boolean | null;
|
|
87
|
+
}
|
|
88
|
+
interface NotificationSettingsService {
|
|
89
|
+
get(orgId: string, userId: string, scope: SettingsScope): Promise<StoredSetting[]>;
|
|
90
|
+
getMany(orgId: string, userIds: string[], scope: SettingsScope, categories: string[]): Promise<Record<string, StoredSetting[]>>;
|
|
91
|
+
set(orgId: string, actor: MemberActor, scope: SettingsScope, changes: SettingChange[]): Promise<void>;
|
|
92
|
+
switchedOn(orgId: string, communityId: string, category: string, spaceId: string | null): Promise<string[]>;
|
|
93
|
+
}
|
|
94
|
+
interface NotificationCategoryReader {
|
|
95
|
+
communityList(orgId: string, communityId: string): Promise<NotificationCategory[]>;
|
|
96
|
+
orgList(orgId: string): Promise<NotificationCategory[]>;
|
|
97
|
+
}
|
|
98
|
+
interface CommunityAccessReader {
|
|
99
|
+
canOpen(orgId: string, actor: Actor, communityId: string): Promise<boolean>;
|
|
100
|
+
}
|
|
101
|
+
interface SettingKey {
|
|
102
|
+
communityId: string | null;
|
|
103
|
+
spaceId: string | null;
|
|
104
|
+
category: string;
|
|
105
|
+
channel: string;
|
|
106
|
+
}
|
|
107
|
+
interface NotificationSettingsRepository {
|
|
108
|
+
forUsers(orgId: string, userIds: string[], communityId: string | null, categories: string[] | null): Promise<(StoredSetting & {
|
|
109
|
+
userId: string;
|
|
110
|
+
})[]>;
|
|
111
|
+
apply(orgId: string, userId: string, upserts: (SettingKey & {
|
|
112
|
+
enabled: boolean;
|
|
113
|
+
})[], deletes: SettingKey[], at: Date): Promise<void>;
|
|
114
|
+
switchedOn(orgId: string, communityId: string, category: string, spaceId: string | null): Promise<string[]>;
|
|
115
|
+
}
|
|
116
|
+
interface DeviceRegistration {
|
|
117
|
+
provider: PushProvider;
|
|
118
|
+
token: string;
|
|
119
|
+
keys?: {
|
|
120
|
+
p256dh: string;
|
|
121
|
+
auth: string;
|
|
122
|
+
};
|
|
123
|
+
appId?: string;
|
|
124
|
+
environment?: 'production' | 'sandbox';
|
|
125
|
+
}
|
|
126
|
+
interface DevicesService {
|
|
127
|
+
register(orgId: string, actor: MemberActor, input: DeviceRegistration): Promise<PushDevice>;
|
|
128
|
+
list(orgId: string, actor: MemberActor): Promise<DeviceView[]>;
|
|
129
|
+
remove(orgId: string, actor: MemberActor, deviceId: string): Promise<void>;
|
|
130
|
+
activeDevices(orgId: string, userId: string, since: Date): Promise<PushDevice[]>;
|
|
131
|
+
withActiveDevices(orgId: string, userIds: string[], since: Date): Promise<string[]>;
|
|
132
|
+
removeDead(orgId: string, deviceIds: string[]): Promise<void>;
|
|
133
|
+
pushConfig(): {
|
|
134
|
+
vapidPublicKey: string | null;
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
interface NewDevice {
|
|
138
|
+
userId: string;
|
|
139
|
+
provider: PushProvider;
|
|
140
|
+
token: string;
|
|
141
|
+
p256dh: string | null;
|
|
142
|
+
auth: string | null;
|
|
143
|
+
appId: string | null;
|
|
144
|
+
environment: 'production' | 'sandbox' | null;
|
|
145
|
+
}
|
|
146
|
+
interface DevicesRepository {
|
|
147
|
+
upsert(orgId: string, device: NewDevice, at: Date): Promise<DeviceView>;
|
|
148
|
+
listForUser(orgId: string, userId: string, since: Date | null): Promise<DeviceView[]>;
|
|
149
|
+
usersWithDevices(orgId: string, userIds: string[], since: Date): Promise<string[]>;
|
|
150
|
+
deleteOwn(orgId: string, userId: string, deviceId: string): Promise<boolean>;
|
|
151
|
+
deleteMany(orgId: string, deviceIds: string[]): Promise<void>;
|
|
152
|
+
}
|
|
129
153
|
//#endregion
|
|
130
|
-
export { AuthHeaders, IdentityRepository, IdentityService, IdentityTxScope, IdentityUnitOfWork,
|
|
154
|
+
export { AuthHeaders, CommunityAccessReader, DeviceRegistration, DevicesRepository, DevicesService, IdentityRepository, IdentityService, IdentityTxScope, IdentityUnitOfWork, MemberActor, NewDevice, NotificationCategoryReader, NotificationSettingsRepository, NotificationSettingsService, ProfilePatch, SessionAdmin, SettingChange, SettingKey, UpdateProfileInput, UserEditor, UserProvisioner, UserResolver };
|
|
131
155
|
//# sourceMappingURL=ports.d.ts.map
|
|
@@ -22,8 +22,8 @@ declare const integrationEvents: {
|
|
|
22
22
|
createdAt: unknown;
|
|
23
23
|
updatedAt: unknown;
|
|
24
24
|
}, import("zod").ZodObject<{
|
|
25
|
-
orgId: import("zod").ZodString;
|
|
26
25
|
id: import("zod").ZodString;
|
|
26
|
+
orgId: import("zod").ZodString;
|
|
27
27
|
createdAt: import("zod").ZodCoercedDate<unknown>;
|
|
28
28
|
updatedAt: import("zod").ZodCoercedDate<unknown>;
|
|
29
29
|
active: import("zod").ZodBoolean;
|
|
@@ -49,8 +49,8 @@ declare const integrationEvents: {
|
|
|
49
49
|
createdAt: unknown;
|
|
50
50
|
updatedAt: unknown;
|
|
51
51
|
}, import("zod").ZodObject<{
|
|
52
|
-
orgId: import("zod").ZodString;
|
|
53
52
|
id: import("zod").ZodString;
|
|
53
|
+
orgId: import("zod").ZodString;
|
|
54
54
|
createdAt: import("zod").ZodCoercedDate<unknown>;
|
|
55
55
|
updatedAt: import("zod").ZodCoercedDate<unknown>;
|
|
56
56
|
active: import("zod").ZodBoolean;
|
|
@@ -76,8 +76,8 @@ declare const integrationEvents: {
|
|
|
76
76
|
createdAt: unknown;
|
|
77
77
|
updatedAt: unknown;
|
|
78
78
|
}, import("zod").ZodObject<{
|
|
79
|
-
orgId: import("zod").ZodString;
|
|
80
79
|
id: import("zod").ZodString;
|
|
80
|
+
orgId: import("zod").ZodString;
|
|
81
81
|
createdAt: import("zod").ZodCoercedDate<unknown>;
|
|
82
82
|
updatedAt: import("zod").ZodCoercedDate<unknown>;
|
|
83
83
|
active: import("zod").ZodBoolean;
|
|
@@ -91,15 +91,14 @@ declare const organizationEvents: {
|
|
|
91
91
|
createdAt: unknown;
|
|
92
92
|
updatedAt: unknown;
|
|
93
93
|
}, import("zod").ZodObject<{
|
|
94
|
-
orgId: import("zod").ZodString;
|
|
95
94
|
id: import("zod").ZodString;
|
|
95
|
+
orgId: import("zod").ZodString;
|
|
96
|
+
createdAt: import("zod").ZodCoercedDate<unknown>;
|
|
97
|
+
updatedAt: import("zod").ZodCoercedDate<unknown>;
|
|
96
98
|
status: import("zod").ZodEnum<{
|
|
97
99
|
active: "active";
|
|
98
100
|
invited: "invited";
|
|
99
101
|
}>;
|
|
100
|
-
createdAt: import("zod").ZodCoercedDate<unknown>;
|
|
101
|
-
updatedAt: import("zod").ZodCoercedDate<unknown>;
|
|
102
|
-
userId: import("zod").ZodString;
|
|
103
102
|
role: import("zod").ZodEnum<{
|
|
104
103
|
owner: "owner";
|
|
105
104
|
admin: "admin";
|
|
@@ -107,6 +106,7 @@ declare const organizationEvents: {
|
|
|
107
106
|
moderator: "moderator";
|
|
108
107
|
student: "student";
|
|
109
108
|
}>;
|
|
109
|
+
userId: import("zod").ZodString;
|
|
110
110
|
}, import("zod/v4/core").$strip>>;
|
|
111
111
|
orgUserDeleted: RuntimeEventDefinition<VersionedDomainEvent<{
|
|
112
112
|
id: string;
|
|
@@ -125,15 +125,14 @@ declare const organizationEvents: {
|
|
|
125
125
|
createdAt: unknown;
|
|
126
126
|
updatedAt: unknown;
|
|
127
127
|
}, import("zod").ZodObject<{
|
|
128
|
-
orgId: import("zod").ZodString;
|
|
129
128
|
id: import("zod").ZodString;
|
|
129
|
+
orgId: import("zod").ZodString;
|
|
130
|
+
createdAt: import("zod").ZodCoercedDate<unknown>;
|
|
131
|
+
updatedAt: import("zod").ZodCoercedDate<unknown>;
|
|
130
132
|
status: import("zod").ZodEnum<{
|
|
131
133
|
active: "active";
|
|
132
134
|
invited: "invited";
|
|
133
135
|
}>;
|
|
134
|
-
createdAt: import("zod").ZodCoercedDate<unknown>;
|
|
135
|
-
updatedAt: import("zod").ZodCoercedDate<unknown>;
|
|
136
|
-
userId: import("zod").ZodString;
|
|
137
136
|
role: import("zod").ZodEnum<{
|
|
138
137
|
owner: "owner";
|
|
139
138
|
admin: "admin";
|
|
@@ -141,6 +140,7 @@ declare const organizationEvents: {
|
|
|
141
140
|
moderator: "moderator";
|
|
142
141
|
student: "student";
|
|
143
142
|
}>;
|
|
143
|
+
userId: import("zod").ZodString;
|
|
144
144
|
}, import("zod/v4/core").$strip>>;
|
|
145
145
|
orgUserUpdated: RuntimeEventDefinition<VersionedDomainEvent<{
|
|
146
146
|
id: string;
|
|
@@ -159,15 +159,14 @@ declare const organizationEvents: {
|
|
|
159
159
|
createdAt: unknown;
|
|
160
160
|
updatedAt: unknown;
|
|
161
161
|
}, import("zod").ZodObject<{
|
|
162
|
-
orgId: import("zod").ZodString;
|
|
163
162
|
id: import("zod").ZodString;
|
|
163
|
+
orgId: import("zod").ZodString;
|
|
164
|
+
createdAt: import("zod").ZodCoercedDate<unknown>;
|
|
165
|
+
updatedAt: import("zod").ZodCoercedDate<unknown>;
|
|
164
166
|
status: import("zod").ZodEnum<{
|
|
165
167
|
active: "active";
|
|
166
168
|
invited: "invited";
|
|
167
169
|
}>;
|
|
168
|
-
createdAt: import("zod").ZodCoercedDate<unknown>;
|
|
169
|
-
updatedAt: import("zod").ZodCoercedDate<unknown>;
|
|
170
|
-
userId: import("zod").ZodString;
|
|
171
170
|
role: import("zod").ZodEnum<{
|
|
172
171
|
owner: "owner";
|
|
173
172
|
admin: "admin";
|
|
@@ -175,6 +174,7 @@ declare const organizationEvents: {
|
|
|
175
174
|
moderator: "moderator";
|
|
176
175
|
student: "student";
|
|
177
176
|
}>;
|
|
177
|
+
userId: import("zod").ZodString;
|
|
178
178
|
}, import("zod/v4/core").$strip>>;
|
|
179
179
|
inviteCreated: RuntimeEventDefinition<VersionedDomainEvent<{
|
|
180
180
|
id: string;
|
|
@@ -199,24 +199,24 @@ declare const organizationEvents: {
|
|
|
199
199
|
createdAt: unknown;
|
|
200
200
|
updatedAt: unknown;
|
|
201
201
|
}, import("zod").ZodObject<{
|
|
202
|
-
orgId: import("zod").ZodString;
|
|
203
202
|
id: import("zod").ZodString;
|
|
203
|
+
orgId: import("zod").ZodString;
|
|
204
|
+
createdAt: import("zod").ZodCoercedDate<unknown>;
|
|
205
|
+
updatedAt: import("zod").ZodCoercedDate<unknown>;
|
|
204
206
|
status: import("zod").ZodEnum<{
|
|
205
207
|
pending: "pending";
|
|
206
208
|
accepted: "accepted";
|
|
207
209
|
rejected: "rejected";
|
|
208
210
|
canceled: "canceled";
|
|
209
211
|
}>;
|
|
210
|
-
createdAt: import("zod").ZodCoercedDate<unknown>;
|
|
211
|
-
updatedAt: import("zod").ZodCoercedDate<unknown>;
|
|
212
212
|
expiresAt: import("zod").ZodNullable<import("zod").ZodCoercedDate<unknown>>;
|
|
213
|
-
email: import("zod").ZodEmail;
|
|
214
213
|
role: import("zod").ZodEnum<{
|
|
215
214
|
admin: "admin";
|
|
216
215
|
instructor: "instructor";
|
|
217
216
|
moderator: "moderator";
|
|
218
217
|
student: "student";
|
|
219
218
|
}>;
|
|
219
|
+
email: import("zod").ZodEmail;
|
|
220
220
|
invitedBy: import("zod").ZodString;
|
|
221
221
|
}, import("zod/v4/core").$strip>>;
|
|
222
222
|
inviteAccepted: RuntimeEventDefinition<VersionedDomainEvent<{
|
|
@@ -242,24 +242,24 @@ declare const organizationEvents: {
|
|
|
242
242
|
createdAt: unknown;
|
|
243
243
|
updatedAt: unknown;
|
|
244
244
|
}, import("zod").ZodObject<{
|
|
245
|
-
orgId: import("zod").ZodString;
|
|
246
245
|
id: import("zod").ZodString;
|
|
246
|
+
orgId: import("zod").ZodString;
|
|
247
|
+
createdAt: import("zod").ZodCoercedDate<unknown>;
|
|
248
|
+
updatedAt: import("zod").ZodCoercedDate<unknown>;
|
|
247
249
|
status: import("zod").ZodEnum<{
|
|
248
250
|
pending: "pending";
|
|
249
251
|
accepted: "accepted";
|
|
250
252
|
rejected: "rejected";
|
|
251
253
|
canceled: "canceled";
|
|
252
254
|
}>;
|
|
253
|
-
createdAt: import("zod").ZodCoercedDate<unknown>;
|
|
254
|
-
updatedAt: import("zod").ZodCoercedDate<unknown>;
|
|
255
255
|
expiresAt: import("zod").ZodNullable<import("zod").ZodCoercedDate<unknown>>;
|
|
256
|
-
email: import("zod").ZodEmail;
|
|
257
256
|
role: import("zod").ZodEnum<{
|
|
258
257
|
admin: "admin";
|
|
259
258
|
instructor: "instructor";
|
|
260
259
|
moderator: "moderator";
|
|
261
260
|
student: "student";
|
|
262
261
|
}>;
|
|
262
|
+
email: import("zod").ZodEmail;
|
|
263
263
|
invitedBy: import("zod").ZodString;
|
|
264
264
|
}, import("zod/v4/core").$strip>>;
|
|
265
265
|
inviteCanceled: RuntimeEventDefinition<VersionedDomainEvent<{
|
|
@@ -302,15 +302,14 @@ declare const organizationEvents: {
|
|
|
302
302
|
createdAt: unknown;
|
|
303
303
|
updatedAt: unknown;
|
|
304
304
|
}, import("zod").ZodObject<{
|
|
305
|
-
orgId: import("zod").ZodString;
|
|
306
305
|
id: import("zod").ZodString;
|
|
306
|
+
orgId: import("zod").ZodString;
|
|
307
|
+
createdAt: import("zod").ZodCoercedDate<unknown>;
|
|
308
|
+
updatedAt: import("zod").ZodCoercedDate<unknown>;
|
|
307
309
|
status: import("zod").ZodEnum<{
|
|
308
310
|
active: "active";
|
|
309
311
|
invited: "invited";
|
|
310
312
|
}>;
|
|
311
|
-
createdAt: import("zod").ZodCoercedDate<unknown>;
|
|
312
|
-
updatedAt: import("zod").ZodCoercedDate<unknown>;
|
|
313
|
-
userId: import("zod").ZodString;
|
|
314
313
|
role: import("zod").ZodEnum<{
|
|
315
314
|
owner: "owner";
|
|
316
315
|
admin: "admin";
|
|
@@ -318,6 +317,7 @@ declare const organizationEvents: {
|
|
|
318
317
|
moderator: "moderator";
|
|
319
318
|
student: "student";
|
|
320
319
|
}>;
|
|
320
|
+
userId: import("zod").ZodString;
|
|
321
321
|
}, import("zod/v4/core").$strip>>;
|
|
322
322
|
studentDeleted: RuntimeEventDefinition<VersionedDomainEvent<{
|
|
323
323
|
id: string;
|
|
@@ -336,15 +336,14 @@ declare const organizationEvents: {
|
|
|
336
336
|
createdAt: unknown;
|
|
337
337
|
updatedAt: unknown;
|
|
338
338
|
}, import("zod").ZodObject<{
|
|
339
|
-
orgId: import("zod").ZodString;
|
|
340
339
|
id: import("zod").ZodString;
|
|
340
|
+
orgId: import("zod").ZodString;
|
|
341
|
+
createdAt: import("zod").ZodCoercedDate<unknown>;
|
|
342
|
+
updatedAt: import("zod").ZodCoercedDate<unknown>;
|
|
341
343
|
status: import("zod").ZodEnum<{
|
|
342
344
|
active: "active";
|
|
343
345
|
invited: "invited";
|
|
344
346
|
}>;
|
|
345
|
-
createdAt: import("zod").ZodCoercedDate<unknown>;
|
|
346
|
-
updatedAt: import("zod").ZodCoercedDate<unknown>;
|
|
347
|
-
userId: import("zod").ZodString;
|
|
348
347
|
role: import("zod").ZodEnum<{
|
|
349
348
|
owner: "owner";
|
|
350
349
|
admin: "admin";
|
|
@@ -352,6 +351,7 @@ declare const organizationEvents: {
|
|
|
352
351
|
moderator: "moderator";
|
|
353
352
|
student: "student";
|
|
354
353
|
}>;
|
|
354
|
+
userId: import("zod").ZodString;
|
|
355
355
|
}, import("zod/v4/core").$strip>>;
|
|
356
356
|
studentLinked: RuntimeEventDefinition<VersionedDomainEvent<{
|
|
357
357
|
id: string;
|
|
@@ -370,15 +370,14 @@ declare const organizationEvents: {
|
|
|
370
370
|
createdAt: unknown;
|
|
371
371
|
updatedAt: unknown;
|
|
372
372
|
}, import("zod").ZodObject<{
|
|
373
|
-
orgId: import("zod").ZodString;
|
|
374
373
|
id: import("zod").ZodString;
|
|
374
|
+
orgId: import("zod").ZodString;
|
|
375
|
+
createdAt: import("zod").ZodCoercedDate<unknown>;
|
|
376
|
+
updatedAt: import("zod").ZodCoercedDate<unknown>;
|
|
375
377
|
status: import("zod").ZodEnum<{
|
|
376
378
|
active: "active";
|
|
377
379
|
invited: "invited";
|
|
378
380
|
}>;
|
|
379
|
-
createdAt: import("zod").ZodCoercedDate<unknown>;
|
|
380
|
-
updatedAt: import("zod").ZodCoercedDate<unknown>;
|
|
381
|
-
userId: import("zod").ZodString;
|
|
382
381
|
role: import("zod").ZodEnum<{
|
|
383
382
|
owner: "owner";
|
|
384
383
|
admin: "admin";
|
|
@@ -386,6 +385,7 @@ declare const organizationEvents: {
|
|
|
386
385
|
moderator: "moderator";
|
|
387
386
|
student: "student";
|
|
388
387
|
}>;
|
|
388
|
+
userId: import("zod").ZodString;
|
|
389
389
|
}, import("zod/v4/core").$strip>>;
|
|
390
390
|
};
|
|
391
391
|
type OrganizationCreated = EventOf<typeof organizationEvents.organizationCreated>;
|
|
@@ -7,6 +7,7 @@ import { CreateOrganizationInput, LinkOrgUserInput, ResendStudentInviteInput, Un
|
|
|
7
7
|
import { AuthHeaders } from "../identity/ports.js";
|
|
8
8
|
import { AuthOrganization, MemberRecord, MemberWriteContext, MembersRepository, NewInviteRow, NewOrganizationRow, OrgAdmin, OrganizationService, OrganizationsRepository } from "./ports.js";
|
|
9
9
|
import { OrganizationServiceImpl } from "./service.js";
|
|
10
|
+
import { OrgNotificationCategories } from "./notification-categories.js";
|
|
10
11
|
import { InviteAccepted, InviteCanceled, InviteCreated, OrgUserDeleted, OrgUserLinked, OrgUserUpdated, OrganizationCreated, OrganizationDeleted, OrganizationEvent, OrganizationUpdated, StudentCreated, StudentDeleted, StudentLinked, organizationEvents } from "./events.js";
|
|
11
12
|
import { ACCESS_SCOPES, AccessScope, SCOPE_PERMISSION, ScopeOption, assertRoleHoldsScopes, isAccessScope, isDeleteScope, roleHoldsScope, scopeOptions, scopesForRole } from "./access-scopes.js";
|
|
12
|
-
export { ACCESS_SCOPES, type AcceptInviteInput, AccessScope, type AddOrgUserInput, type AuthHeaders, type AuthOrganization, type Capability, type CreateInviteInput, type CreateOrgUserInput, type CreateOrganizationInput, type Invite, type InviteAccepted, type InviteCanceled, type InviteCreated, InviteError, type InviteId, type InviteRole, type LinkOrgUserInput, type Member, type MemberRecord, type MemberRoleChange, type MemberStatus, type MemberWriteContext, type MembersQuery, type MembersRepository, type NewInviteRow, type NewOrganizationInput, type NewOrganizationRow, type OrgAdmin, type OrgUser, type OrgUserDeleted, type OrgUserId, type OrgUserLinked, type OrgUserStatus, type OrgUserUpdated, type Organization, type OrganizationCreated, type OrganizationDeleted, type OrganizationEvent, type OrganizationId, OrganizationRuleError, type OrganizationService, OrganizationServiceImpl, type OrganizationUpdated, type OrganizationsRepository, type Page, type Permission, ROLES, type ResendStudentInviteInput, type Role, SCOPE_PERMISSION, STAFF_ROLES, STUDENT_ROLE, ScopeOption, type StaffRole, type StudentCreated, type StudentDeleted, type StudentLinked, type TeamRole, type UnlinkOrgUserInput, type UpdateOrgUserInput, type UpdateOrganizationInput, assertRoleHoldsScopes, canModerate, capability, isAccessScope, isDeleteScope, isRole, isStaffRole, normalizeRole, organizationEvents, parseRole, roleHoldsScope, scopeOptions, scopesForRole };
|
|
13
|
+
export { ACCESS_SCOPES, type AcceptInviteInput, AccessScope, type AddOrgUserInput, type AuthHeaders, type AuthOrganization, type Capability, type CreateInviteInput, type CreateOrgUserInput, type CreateOrganizationInput, type Invite, type InviteAccepted, type InviteCanceled, type InviteCreated, InviteError, type InviteId, type InviteRole, type LinkOrgUserInput, type Member, type MemberRecord, type MemberRoleChange, type MemberStatus, type MemberWriteContext, type MembersQuery, type MembersRepository, type NewInviteRow, type NewOrganizationInput, type NewOrganizationRow, type OrgAdmin, OrgNotificationCategories, type OrgUser, type OrgUserDeleted, type OrgUserId, type OrgUserLinked, type OrgUserStatus, type OrgUserUpdated, type Organization, type OrganizationCreated, type OrganizationDeleted, type OrganizationEvent, type OrganizationId, OrganizationRuleError, type OrganizationService, OrganizationServiceImpl, type OrganizationUpdated, type OrganizationsRepository, type Page, type Permission, ROLES, type ResendStudentInviteInput, type Role, SCOPE_PERMISSION, STAFF_ROLES, STUDENT_ROLE, ScopeOption, type StaffRole, type StudentCreated, type StudentDeleted, type StudentLinked, type TeamRole, type UnlinkOrgUserInput, type UpdateOrgUserInput, type UpdateOrganizationInput, assertRoleHoldsScopes, canModerate, capability, isAccessScope, isDeleteScope, isRole, isStaffRole, normalizeRole, organizationEvents, parseRole, roleHoldsScope, scopeOptions, scopesForRole };
|
|
@@ -2,5 +2,6 @@ import { ROLES, STAFF_ROLES, STUDENT_ROLE, canModerate, capability, isRole, isSt
|
|
|
2
2
|
import { InviteError, OrganizationRuleError } from "./members.js";
|
|
3
3
|
import { organizationEvents } from "./events.js";
|
|
4
4
|
import { OrganizationServiceImpl } from "./service.js";
|
|
5
|
+
import { OrgNotificationCategories } from "./notification-categories.js";
|
|
5
6
|
import { ACCESS_SCOPES, SCOPE_PERMISSION, assertRoleHoldsScopes, isAccessScope, isDeleteScope, roleHoldsScope, scopeOptions, scopesForRole } from "./access-scopes.js";
|
|
6
|
-
export { ACCESS_SCOPES, InviteError, OrganizationRuleError, OrganizationServiceImpl, ROLES, SCOPE_PERMISSION, STAFF_ROLES, STUDENT_ROLE, assertRoleHoldsScopes, canModerate, capability, isAccessScope, isDeleteScope, isRole, isStaffRole, normalizeRole, organizationEvents, parseRole, roleHoldsScope, scopeOptions, scopesForRole };
|
|
7
|
+
export { ACCESS_SCOPES, InviteError, OrgNotificationCategories, OrganizationRuleError, OrganizationServiceImpl, ROLES, SCOPE_PERMISSION, STAFF_ROLES, STUDENT_ROLE, assertRoleHoldsScopes, canModerate, capability, isAccessScope, isDeleteScope, isRole, isStaffRole, normalizeRole, organizationEvents, parseRole, roleHoldsScope, scopeOptions, scopesForRole };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Logger } from "../types/ports.js";
|
|
2
|
+
import { NotificationCategory } from "../types/notifications.js";
|
|
3
|
+
import "../types/index.js";
|
|
4
|
+
import { ChannelRegistry, NotificationCategoryService, NotificationTypeRegistry } from "../shared/ports.js";
|
|
5
|
+
import { SettingsService } from "../shared/settings.js";
|
|
6
|
+
//#region src/organizations/notification-categories.d.ts
|
|
7
|
+
type OrgNotificationCategoriesParams = {
|
|
8
|
+
settings: SettingsService;
|
|
9
|
+
types: NotificationTypeRegistry;
|
|
10
|
+
channels: ChannelRegistry;
|
|
11
|
+
logger?: Logger;
|
|
12
|
+
};
|
|
13
|
+
declare class OrgNotificationCategories implements NotificationCategoryService {
|
|
14
|
+
private readonly settings;
|
|
15
|
+
private readonly types;
|
|
16
|
+
private readonly channels;
|
|
17
|
+
private readonly logger;
|
|
18
|
+
constructor(params: OrgNotificationCategoriesParams);
|
|
19
|
+
get(orgId: string, scopeId: string): Promise<{
|
|
20
|
+
categories: NotificationCategory[];
|
|
21
|
+
source: 'default' | 'stored';
|
|
22
|
+
}>;
|
|
23
|
+
replace(orgId: string, scopeId: string, categories: NotificationCategory[]): Promise<{
|
|
24
|
+
categories: NotificationCategory[];
|
|
25
|
+
source: 'stored';
|
|
26
|
+
}>;
|
|
27
|
+
reset(orgId: string, scopeId: string): Promise<void>;
|
|
28
|
+
defaults(): NotificationCategory[];
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
export { OrgNotificationCategories, OrgNotificationCategoriesParams };
|
|
32
|
+
//# sourceMappingURL=notification-categories.d.ts.map
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { noopLogger } from "../shared/logger.js";
|
|
2
|
+
import "../shared/settings.js";
|
|
3
|
+
import { assertValidCategoryList, offeringConfigured, storedCategoryList } from "../shared/notification-categories.js";
|
|
4
|
+
//#region src/organizations/notification-categories.ts
|
|
5
|
+
var OrgNotificationCategories = class {
|
|
6
|
+
settings;
|
|
7
|
+
types;
|
|
8
|
+
channels;
|
|
9
|
+
logger;
|
|
10
|
+
constructor(params) {
|
|
11
|
+
this.settings = params.settings;
|
|
12
|
+
this.types = params.types;
|
|
13
|
+
this.channels = params.channels;
|
|
14
|
+
this.logger = params.logger ?? noopLogger;
|
|
15
|
+
}
|
|
16
|
+
async get(orgId, scopeId) {
|
|
17
|
+
const stored = storedCategoryList((await this.settings.get(orgId, "organizations", scopeId))?.notificationCategories);
|
|
18
|
+
return stored ? {
|
|
19
|
+
categories: offeringConfigured(stored, this.channels.list()),
|
|
20
|
+
source: "stored"
|
|
21
|
+
} : {
|
|
22
|
+
categories: this.defaults(),
|
|
23
|
+
source: "default"
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
async replace(orgId, scopeId, categories) {
|
|
27
|
+
const configured = this.channels.list();
|
|
28
|
+
assertValidCategoryList(categories, "org", this.types, configured);
|
|
29
|
+
const ordered = offeringConfigured(categories, configured);
|
|
30
|
+
await this.settings.patch(orgId, "organizations", scopeId, { notificationCategories: ordered });
|
|
31
|
+
this.logger.info("org notification categories replaced", {
|
|
32
|
+
orgId,
|
|
33
|
+
keys: categories.map((category) => category.key)
|
|
34
|
+
});
|
|
35
|
+
return {
|
|
36
|
+
categories: ordered,
|
|
37
|
+
source: "stored"
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
async reset(orgId, scopeId) {
|
|
41
|
+
await this.settings.patch(orgId, "organizations", scopeId, { notificationCategories: null });
|
|
42
|
+
this.logger.info("org notification categories reset", { orgId });
|
|
43
|
+
}
|
|
44
|
+
defaults() {
|
|
45
|
+
return this.types.defaults("org");
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
//#endregion
|
|
49
|
+
export { OrgNotificationCategories };
|
|
50
|
+
|
|
51
|
+
//# sourceMappingURL=notification-categories.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-categories.js","names":[],"sources":["../../src/organizations/notification-categories.ts"],"sourcesContent":["import type { NotificationCategory } from '../types/index.js';\nimport type {\n ChannelRegistry,\n Logger,\n NotificationCategoryService,\n NotificationTypeRegistry,\n} from '../shared/ports.js';\nimport { noopLogger } from '../shared/logger.js';\nimport { SettingsNamespace, type SettingsService } from '../shared/settings.js';\nimport {\n assertValidCategoryList,\n offeringConfigured,\n storedCategoryList,\n} from '../shared/notification-categories.js';\n\nexport type OrgNotificationCategoriesParams = {\n settings: SettingsService;\n types: NotificationTypeRegistry;\n channels: ChannelRegistry;\n logger?: Logger;\n};\n\nexport class OrgNotificationCategories implements NotificationCategoryService {\n private readonly settings: SettingsService;\n private readonly types: NotificationTypeRegistry;\n private readonly channels: ChannelRegistry;\n private readonly logger: Logger;\n\n constructor(params: OrgNotificationCategoriesParams) {\n this.settings = params.settings;\n this.types = params.types;\n this.channels = params.channels;\n this.logger = params.logger ?? noopLogger;\n }\n\n async get(\n orgId: string,\n scopeId: string,\n ): Promise<{ categories: NotificationCategory[]; source: 'default' | 'stored' }> {\n const value = await this.settings.get<Record<string, unknown>>(\n orgId,\n SettingsNamespace.organizations,\n scopeId,\n );\n const stored = storedCategoryList(value?.notificationCategories);\n return stored\n ? { categories: offeringConfigured(stored, this.channels.list()), source: 'stored' }\n : { categories: this.defaults(), source: 'default' };\n }\n\n async replace(\n orgId: string,\n scopeId: string,\n categories: NotificationCategory[],\n ): Promise<{ categories: NotificationCategory[]; source: 'stored' }> {\n const configured = this.channels.list();\n assertValidCategoryList(categories, 'org', this.types, configured);\n const ordered = offeringConfigured(categories, configured);\n await this.settings.patch(orgId, SettingsNamespace.organizations, scopeId, {\n notificationCategories: ordered,\n });\n this.logger.info('org notification categories replaced', {\n orgId,\n keys: categories.map((category) => category.key),\n });\n return { categories: ordered, source: 'stored' };\n }\n\n async reset(orgId: string, scopeId: string): Promise<void> {\n await this.settings.patch(orgId, SettingsNamespace.organizations, scopeId, {\n notificationCategories: null,\n });\n this.logger.info('org notification categories reset', { orgId });\n }\n\n defaults(): NotificationCategory[] {\n return this.types.defaults('org');\n }\n}\n"],"mappings":";;;;AAsBA,IAAa,4BAAb,MAA8E;CAC5E;CACA;CACA;CACA;CAEA,YAAY,QAAyC;EACnD,KAAK,WAAW,OAAO;EACvB,KAAK,QAAQ,OAAO;EACpB,KAAK,WAAW,OAAO;EACvB,KAAK,SAAS,OAAO,UAAU;CACjC;CAEA,MAAM,IACJ,OACA,SAC+E;EAM/E,MAAM,SAAS,oBAAmB,MALd,KAAK,SAAS,IAChC,OAAA,iBAEA,OACF,EAAA,EACyC,sBAAsB;EAC/D,OAAO,SACH;GAAE,YAAY,mBAAmB,QAAQ,KAAK,SAAS,KAAK,CAAC;GAAG,QAAQ;EAAS,IACjF;GAAE,YAAY,KAAK,SAAS;GAAG,QAAQ;EAAU;CACvD;CAEA,MAAM,QACJ,OACA,SACA,YACmE;EACnE,MAAM,aAAa,KAAK,SAAS,KAAK;EACtC,wBAAwB,YAAY,OAAO,KAAK,OAAO,UAAU;EACjE,MAAM,UAAU,mBAAmB,YAAY,UAAU;EACzD,MAAM,KAAK,SAAS,MAAM,OAAA,iBAAwC,SAAS,EACzE,wBAAwB,QAC1B,CAAC;EACD,KAAK,OAAO,KAAK,wCAAwC;GACvD;GACA,MAAM,WAAW,KAAK,aAAa,SAAS,GAAG;EACjD,CAAC;EACD,OAAO;GAAE,YAAY;GAAS,QAAQ;EAAS;CACjD;CAEA,MAAM,MAAM,OAAe,SAAgC;EACzD,MAAM,KAAK,SAAS,MAAM,OAAA,iBAAwC,SAAS,EACzE,wBAAwB,KAC1B,CAAC;EACD,KAAK,OAAO,KAAK,qCAAqC,EAAE,MAAM,CAAC;CACjE;CAEA,WAAmC;EACjC,OAAO,KAAK,MAAM,SAAS,KAAK;CAClC;AACF"}
|