@headless-lms/server 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/app/community-mail.d.ts +6 -8
- package/dist/app/community-mail.js +13 -17
- package/dist/app/community-mail.js.map +1 -1
- package/dist/app/container.d.ts +27 -10
- package/dist/app/container.js +178 -22
- package/dist/app/container.js.map +1 -1
- package/dist/app/notification-channels.d.ts +47 -0
- package/dist/app/notification-channels.js +174 -0
- package/dist/app/notification-channels.js.map +1 -0
- package/dist/app/notification-reads.d.ts +23 -0
- package/dist/app/notification-reads.js +95 -0
- package/dist/app/notification-reads.js.map +1 -0
- package/dist/http/errors.d.ts +1 -0
- package/dist/http/errors.js +16 -1
- package/dist/http/errors.js.map +1 -1
- package/dist/http/mcp/tools/communities.js +1 -1
- package/dist/http/mcp/tools/communities.js.map +1 -1
- package/dist/http/plugins/error-handler.js +3 -1
- package/dist/http/plugins/error-handler.js.map +1 -1
- package/dist/http/routes/automations.js +3 -3
- package/dist/http/routes/automations.js.map +1 -1
- package/dist/http/routes/communities.js +67 -7
- package/dist/http/routes/communities.js.map +1 -1
- package/dist/http/routes/member/actor.d.ts +1 -1
- package/dist/http/routes/member/chats.js +9 -30
- package/dist/http/routes/member/chats.js.map +1 -1
- package/dist/http/routes/member/community.js +25 -1
- package/dist/http/routes/member/community.js.map +1 -1
- package/dist/http/routes/member/devices.d.ts +7 -0
- package/dist/http/routes/member/devices.js +98 -0
- package/dist/http/routes/member/devices.js.map +1 -0
- package/dist/http/routes/member/index.js +4 -0
- package/dist/http/routes/member/index.js.map +1 -1
- package/dist/http/routes/member/notification-settings.d.ts +7 -0
- package/dist/http/routes/member/notification-settings.js +108 -0
- package/dist/http/routes/member/notification-settings.js.map +1 -0
- package/dist/http/routes/member/notifications.js +66 -42
- package/dist/http/routes/member/notifications.js.map +1 -1
- package/dist/http/routes/member/posts.js +33 -33
- package/dist/http/routes/member/posts.js.map +1 -1
- package/dist/http/routes/organizations.js +122 -1
- package/dist/http/routes/organizations.js.map +1 -1
- package/dist/http/schemas/assets.d.ts +6 -6
- package/dist/http/schemas/automations.d.ts +0 -4
- package/dist/http/schemas/chats.d.ts +34 -11
- package/dist/http/schemas/chats.js +4 -6
- package/dist/http/schemas/chats.js.map +1 -1
- package/dist/http/schemas/community.d.ts +77 -17
- package/dist/http/schemas/community.js +6 -3
- package/dist/http/schemas/community.js.map +1 -1
- package/dist/http/schemas/discussion.d.ts +23 -1
- package/dist/http/schemas/discussion.js +14 -2
- package/dist/http/schemas/discussion.js.map +1 -1
- package/dist/http/schemas/index.d.ts +6 -6
- package/dist/http/schemas/index.js +6 -6
- package/dist/http/schemas/integrations.d.ts +4 -4
- package/dist/http/schemas/members.d.ts +5 -5
- package/dist/http/schemas/notifications.d.ts +498 -175
- package/dist/http/schemas/notifications.js +148 -28
- package/dist/http/schemas/notifications.js.map +1 -1
- package/dist/http/schemas/posts.d.ts +37 -3
- package/dist/http/schemas/profile.d.ts +2 -2
- package/dist/http/schemas/shared.d.ts +10 -3
- package/dist/http/schemas/shared.js +3 -2
- package/dist/http/schemas/shared.js.map +1 -1
- package/dist/http/schemas/students.d.ts +3 -3
- package/dist/http/scope.d.ts +2 -1
- package/dist/http/scope.js +7 -1
- package/dist/http/scope.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js.map +1 -1
- package/dist/packages/server/package.js +1 -1
- package/package.json +5 -5
|
@@ -1,19 +1,53 @@
|
|
|
1
|
-
import { paginated } from "./shared.js";
|
|
1
|
+
import { PageQuery, paginated } from "./shared.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import {
|
|
3
|
+
import { digestScheduleSchema, jsonValueSchema, notificationCategoryKeySchema, notificationCategoryListSchema, notificationCategorySchema, notificationChannelKeySchema, notificationScopeSchema, notificationTypeKeySchema, reasonSchema, subjectTypeSchema } from "@headless-lms/core/schemas";
|
|
4
4
|
//#region src/http/schemas/notifications.ts
|
|
5
|
-
const Notification = notificationSchema;
|
|
6
5
|
const NotificationIdParam = z.object({ notificationId: z.string() });
|
|
6
|
+
function commaSeparated(item, description) {
|
|
7
|
+
return z.string().max(2e3).transform((value) => value.split(",").map((part) => part.trim()).filter((part) => part.length > 0)).pipe(z.array(item).min(1).max(50)).describe(description);
|
|
8
|
+
}
|
|
9
|
+
function scopeMatchesCommunity(filter, ctx) {
|
|
10
|
+
if (filter.scope === "org" && filter.communityId !== void 0) ctx.addIssue({
|
|
11
|
+
code: "custom",
|
|
12
|
+
path: ["communityId"],
|
|
13
|
+
message: "`communityId` implies `scope=community`; it cannot go with `scope=org`"
|
|
14
|
+
});
|
|
15
|
+
if (filter.includeOrg === true && filter.communityId === void 0) ctx.addIssue({
|
|
16
|
+
code: "custom",
|
|
17
|
+
path: ["includeOrg"],
|
|
18
|
+
message: "`includeOrg` goes with `communityId`"
|
|
19
|
+
});
|
|
20
|
+
if (filter.includeOrg === true && filter.scope !== void 0) ctx.addIssue({
|
|
21
|
+
code: "custom",
|
|
22
|
+
path: ["includeOrg"],
|
|
23
|
+
message: "`includeOrg` cannot go with `scope`"
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
const notificationFilterShape = z.object({
|
|
27
|
+
scope: z.enum(["org", "community"]).optional().describe("`org`: the org list only; `community`: notifications in any community"),
|
|
28
|
+
communityId: z.string().min(1).max(64).optional().describe("Notifications in this community; implies `scope=community`"),
|
|
29
|
+
includeOrg: z.stringbool().optional().describe("With `communityId`, `true` adds the org list (notifications in no community); not with `scope`"),
|
|
30
|
+
category: commaSeparated(notificationCategoryKeySchema, "One or more category keys, comma separated").optional(),
|
|
31
|
+
reason: commaSeparated(reasonSchema, "One or more reasons, comma separated; matches any").optional(),
|
|
32
|
+
type: commaSeparated(notificationTypeKeySchema, "One or more types, comma separated").optional(),
|
|
33
|
+
unread: z.stringbool().optional().describe("`true` for unread notifications only")
|
|
34
|
+
}).strict();
|
|
35
|
+
const NotificationFilterQuery = notificationFilterShape.superRefine(scopeMatchesCommunity);
|
|
36
|
+
const NotificationListQuery = PageQuery.extend(notificationFilterShape.shape).strict().superRefine(scopeMatchesCommunity);
|
|
37
|
+
const NotificationItem = z.object({
|
|
38
|
+
type: subjectTypeSchema,
|
|
39
|
+
id: z.string(),
|
|
40
|
+
title: z.string().optional(),
|
|
41
|
+
excerpt: z.string().optional()
|
|
42
|
+
});
|
|
7
43
|
const NotificationView = z.object({
|
|
8
44
|
id: z.string(),
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
imageUrl: z.string().nullable()
|
|
16
|
-
}).nullable(),
|
|
45
|
+
type: z.string(),
|
|
46
|
+
scope: notificationScopeSchema,
|
|
47
|
+
reasons: z.array(reasonSchema),
|
|
48
|
+
matchedReason: reasonSchema.describe("The reason of the pair the category matched, which picks the wording, such as a reply to your comment"),
|
|
49
|
+
category: z.string(),
|
|
50
|
+
display: z.record(z.string(), jsonValueSchema).describe("The text stored when the notification was sent"),
|
|
17
51
|
community: z.object({
|
|
18
52
|
id: z.string(),
|
|
19
53
|
title: z.string()
|
|
@@ -22,23 +56,109 @@ const NotificationView = z.object({
|
|
|
22
56
|
id: z.string(),
|
|
23
57
|
name: z.string()
|
|
24
58
|
}).nullable(),
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
});
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
59
|
+
actor: z.object({
|
|
60
|
+
orgUserId: z.string(),
|
|
61
|
+
name: z.string().nullable(),
|
|
62
|
+
imageUrl: z.string().nullable()
|
|
63
|
+
}).nullable(),
|
|
64
|
+
subject: NotificationItem.nullable().describe("What the notification is about, with its current title or excerpt; null when you can no longer open it or it was deleted"),
|
|
65
|
+
parent: NotificationItem.nullable().describe("The subject's container, such as a comment's post; null when you can no longer open it or it was deleted"),
|
|
66
|
+
createdAt: z.coerce.date(),
|
|
67
|
+
readAt: z.coerce.date().nullable()
|
|
68
|
+
});
|
|
69
|
+
const NotificationsPage = paginated(NotificationView).extend({ unreadCount: z.number().int().describe("Unread notifications matching the same filters") });
|
|
70
|
+
const NotificationUnreadCounts = z.object({
|
|
71
|
+
org: z.number().int(),
|
|
72
|
+
communities: z.array(z.object({
|
|
73
|
+
communityId: z.string(),
|
|
74
|
+
unread: z.number().int()
|
|
75
|
+
}))
|
|
76
|
+
});
|
|
77
|
+
const NotificationCategory = notificationCategorySchema;
|
|
78
|
+
const NotificationCategoryList = z.object({
|
|
79
|
+
categories: z.array(NotificationCategory),
|
|
80
|
+
source: z.enum(["default", "stored"]).describe("`default` while the list in use is the server default, `stored` once one is saved")
|
|
81
|
+
});
|
|
82
|
+
const ReplaceNotificationCategories = z.object({ categories: notificationCategoryListSchema.describe("The whole ordered list; a notification is filed under the first category that matches") }).strict();
|
|
83
|
+
const NotificationCategoryDefaults = z.object({ categories: z.array(NotificationCategory) });
|
|
84
|
+
const NotificationListScopeQuery = z.object({ scope: z.enum(["org", "community"]) }).strict();
|
|
85
|
+
const NotificationTypeInfo = z.object({
|
|
86
|
+
type: z.string(),
|
|
87
|
+
scopes: z.array(notificationScopeSchema),
|
|
88
|
+
reasons: z.array(reasonSchema)
|
|
89
|
+
});
|
|
90
|
+
const NotificationTypeInfos = z.array(NotificationTypeInfo);
|
|
91
|
+
const NotificationChannelInfos = z.array(z.object({ channel: z.string() }));
|
|
92
|
+
const ResolvedChannel = z.object({
|
|
93
|
+
channel: z.string(),
|
|
94
|
+
enabled: z.boolean(),
|
|
95
|
+
default: z.boolean().describe("What the channel would be without your own setting at this level: what `enabled: null` gives"),
|
|
96
|
+
source: z.enum([
|
|
97
|
+
"locked",
|
|
98
|
+
"space",
|
|
99
|
+
"community",
|
|
100
|
+
"spaceDefault",
|
|
101
|
+
"default"
|
|
102
|
+
]).describe("Why the channel is on or off: locked by the category, your setting for the space, your setting for the whole community (or the org), the default for the space, or the category default"),
|
|
103
|
+
locked: z.boolean()
|
|
104
|
+
});
|
|
105
|
+
const NotificationSettingsCategory = z.object({
|
|
106
|
+
key: z.string(),
|
|
107
|
+
perSpace: z.boolean(),
|
|
108
|
+
channels: z.array(ResolvedChannel),
|
|
109
|
+
spaces: z.array(z.object({
|
|
110
|
+
spaceId: z.string(),
|
|
111
|
+
name: z.string(),
|
|
112
|
+
channels: z.array(ResolvedChannel)
|
|
113
|
+
})).optional().describe("Per-space categories only: every space you can open")
|
|
114
|
+
});
|
|
115
|
+
const CommunityNotificationSettings = z.object({
|
|
116
|
+
communityId: z.string(),
|
|
117
|
+
digest: digestScheduleSchema.nullable().describe("The community's digest schedule; null when it sends no digest"),
|
|
118
|
+
categories: z.array(NotificationSettingsCategory)
|
|
119
|
+
});
|
|
120
|
+
const OrgNotificationSettings = z.object({ categories: z.array(NotificationSettingsCategory) });
|
|
121
|
+
const PatchNotificationSettings = z.object({ settings: z.array(z.object({
|
|
122
|
+
category: notificationCategoryKeySchema,
|
|
123
|
+
spaceId: z.string().min(1).max(64).optional(),
|
|
124
|
+
channel: notificationChannelKeySchema,
|
|
125
|
+
enabled: z.boolean().nullable().describe("null removes your choice, so the default applies again")
|
|
126
|
+
}).strict()).min(1).max(200) }).strict();
|
|
127
|
+
const DeviceFields = {
|
|
128
|
+
id: z.string(),
|
|
129
|
+
provider: z.enum([
|
|
130
|
+
"apns",
|
|
131
|
+
"fcm",
|
|
132
|
+
"webpush"
|
|
133
|
+
]),
|
|
134
|
+
token: z.string(),
|
|
135
|
+
p256dh: z.string().nullable(),
|
|
136
|
+
auth: z.string().nullable(),
|
|
137
|
+
appId: z.string().nullable(),
|
|
138
|
+
environment: z.enum(["production", "sandbox"]).nullable()
|
|
139
|
+
};
|
|
140
|
+
const Device = z.object(DeviceFields);
|
|
141
|
+
const DeviceList = z.array(z.object({
|
|
142
|
+
...DeviceFields,
|
|
143
|
+
lastSeenAt: z.coerce.date()
|
|
144
|
+
}));
|
|
145
|
+
const DeviceIdParam = z.object({ deviceId: z.string() });
|
|
146
|
+
const RegisterDevice = z.object({
|
|
147
|
+
provider: z.enum([
|
|
148
|
+
"apns",
|
|
149
|
+
"fcm",
|
|
150
|
+
"webpush"
|
|
151
|
+
]),
|
|
152
|
+
token: z.string().min(1).max(4096).describe("The APNs device token, the FCM registration token, or the Web Push endpoint URL"),
|
|
153
|
+
keys: z.object({
|
|
154
|
+
p256dh: z.string().min(1).max(512),
|
|
155
|
+
auth: z.string().min(1).max(512)
|
|
156
|
+
}).strict().optional().describe("The Web Push subscription keys; webpush only"),
|
|
157
|
+
appId: z.string().min(1).max(255).optional().describe("The iOS bundle id or Android package name"),
|
|
158
|
+
environment: z.enum(["production", "sandbox"]).optional().describe("apns only")
|
|
159
|
+
}).strict();
|
|
160
|
+
const PushConfigView = z.object({ vapidPublicKey: z.string().nullable() });
|
|
41
161
|
//#endregion
|
|
42
|
-
export {
|
|
162
|
+
export { CommunityNotificationSettings, Device, DeviceIdParam, DeviceList, NotificationCategory, NotificationCategoryDefaults, NotificationCategoryList, NotificationChannelInfos, NotificationFilterQuery, NotificationIdParam, NotificationListQuery, NotificationListScopeQuery, NotificationTypeInfo, NotificationTypeInfos, NotificationUnreadCounts, NotificationView, NotificationsPage, OrgNotificationSettings, PatchNotificationSettings, PushConfigView, RegisterDevice, ReplaceNotificationCategories };
|
|
43
163
|
|
|
44
164
|
//# sourceMappingURL=notifications.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notifications.js","names":[],"sources":["../../../src/http/schemas/notifications.ts"],"sourcesContent":["import { z } from 'zod';\nimport {\n notificationKindSchema,\n notificationPreferenceSchema,\n notificationSchema,\n} from '@headless-lms/core/schemas';\nimport type { NotificationView as DomainNotificationView } from '@headless-lms/core/reporting/member';\nimport { paginated, type Matches } from './shared.js';\n\nexport const Notification = notificationSchema;\nexport const NotificationIdParam = z.object({ notificationId: z.string() });\n\nexport const NotificationView = z.object({\n id: z.string(),\n kind: notificationKindSchema,\n createdAt: z.coerce.date(),\n readAt: z.coerce.date().nullable(),\n actor: z\n .object({\n orgUserId: z.string(),\n displayName: z.string().nullable(),\n imageUrl: z.string().nullable(),\n })\n .nullable(),\n community: z.object({ id: z.string(), title: z.string() }).nullable(),\n space: z.object({ id: z.string(), name: z.string() }).nullable(),\n post: z\n .object({ id: z.string(), title: z.string() })\n .nullable()\n .describe('Null when the viewer can no longer see the post'),\n commentId: z.string().nullable(),\n chat: z\n .object({ id: z.string(), title: z.string() })\n .nullable()\n .describe('Null when the viewer is not in the chat'),\n messageId: z.string().nullable(),\n});\ntype _NotificationViewMatches = Matches<DomainNotificationView, z.infer<typeof NotificationView>> &\n Matches<z.infer<typeof NotificationView>, DomainNotificationView>;\n\nexport const NotificationsPage = paginated(NotificationView).extend({\n unreadCount: z.number().int(),\n});\n\nexport const MarkedRead = z.object({ updated: z.number().int() });\n\nexport const NotificationPreference = notificationPreferenceSchema;\nexport const NotificationPreferences = z.array(NotificationPreference);\nexport const SetNotificationPreferences = z\n .object({\n preferences: z\n .array(NotificationPreference)\n .min(1)\n .max(notificationKindSchema.options.length)\n .refine(\n (preferences) => new Set(preferences.map((p) => p.kind)).size === preferences.length,\n { message: 'must name each kind at most once' },\n )\n .describe('One entry per kind to change, each kind at most once; kinds left out keep theirs'),\n })\n .strict();\n"],"mappings":";;;;AASA,MAAa,eAAe;AAC5B,MAAa,sBAAsB,EAAE,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,CAAC;AAE1E,MAAa,mBAAmB,EAAE,OAAO;CACvC,IAAI,EAAE,OAAO;CACb,MAAM;CACN,WAAW,EAAE,OAAO,KAAK;CACzB,QAAQ,EAAE,OAAO,KAAK,CAAC,CAAC,SAAS;CACjC,OAAO,EACJ,OAAO;EACN,WAAW,EAAE,OAAO;EACpB,aAAa,EAAE,OAAO,CAAC,CAAC,SAAS;EACjC,UAAU,EAAE,OAAO,CAAC,CAAC,SAAS;CAChC,CAAC,CAAC,CACD,SAAS;CACZ,WAAW,EAAE,OAAO;EAAE,IAAI,EAAE,OAAO;EAAG,OAAO,EAAE,OAAO;CAAE,CAAC,CAAC,CAAC,SAAS;CACpE,OAAO,EAAE,OAAO;EAAE,IAAI,EAAE,OAAO;EAAG,MAAM,EAAE,OAAO;CAAE,CAAC,CAAC,CAAC,SAAS;CAC/D,MAAM,EACH,OAAO;EAAE,IAAI,EAAE,OAAO;EAAG,OAAO,EAAE,OAAO;CAAE,CAAC,CAAC,CAC7C,SAAS,CAAC,CACV,SAAS,iDAAiD;CAC7D,WAAW,EAAE,OAAO,CAAC,CAAC,SAAS;CAC/B,MAAM,EACH,OAAO;EAAE,IAAI,EAAE,OAAO;EAAG,OAAO,EAAE,OAAO;CAAE,CAAC,CAAC,CAC7C,SAAS,CAAC,CACV,SAAS,yCAAyC;CACrD,WAAW,EAAE,OAAO,CAAC,CAAC,SAAS;AACjC,CAAC;AAID,MAAa,oBAAoB,UAAU,gBAAgB,CAAC,CAAC,OAAO,EAClE,aAAa,EAAE,OAAO,CAAC,CAAC,IAAI,EAC9B,CAAC;AAED,MAAa,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;AAEhE,MAAa,yBAAyB;AACtC,MAAa,0BAA0B,EAAE,MAAM,sBAAsB;AACrE,MAAa,6BAA6B,EACvC,OAAO,EACN,aAAa,EACV,MAAM,sBAAsB,CAAC,CAC7B,IAAI,CAAC,CAAC,CACN,IAAI,uBAAuB,QAAQ,MAAM,CAAC,CAC1C,QACE,gBAAgB,IAAI,IAAI,YAAY,KAAK,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,SAAS,YAAY,QAC9E,EAAE,SAAS,mCAAmC,CAChD,CAAC,CACA,SAAS,kFAAkF,EAChG,CAAC,CAAC,CACD,OAAO"}
|
|
1
|
+
{"version":3,"file":"notifications.js","names":[],"sources":["../../../src/http/schemas/notifications.ts"],"sourcesContent":["import { z } from 'zod';\nimport {\n digestScheduleSchema,\n jsonValueSchema,\n notificationCategoryKeySchema,\n notificationChannelKeySchema,\n notificationCategoryListSchema,\n notificationCategorySchema,\n notificationScopeSchema,\n notificationTypeKeySchema,\n reasonSchema,\n subjectTypeSchema,\n} from '@headless-lms/core/schemas';\nimport type {\n CommunityNotificationSettingsView as DomainCommunityNotificationSettingsView,\n NotificationUnreadCounts as DomainNotificationUnreadCounts,\n NotificationView as DomainNotificationView,\n} from '@headless-lms/core/reporting/member';\nimport { PageQuery, paginated, type Matches } from './shared.js';\n\nexport const NotificationIdParam = z.object({ notificationId: z.string() });\n\nfunction commaSeparated<T extends string>(item: z.ZodType<T, string>, description: string) {\n return z\n .string()\n .max(2000)\n .transform((value) =>\n value\n .split(',')\n .map((part) => part.trim())\n .filter((part) => part.length > 0),\n )\n .pipe(z.array(item).min(1).max(50))\n .describe(description);\n}\n\nfunction scopeMatchesCommunity(\n filter: { scope?: 'org' | 'community'; communityId?: string; includeOrg?: boolean },\n ctx: z.RefinementCtx,\n): void {\n if (filter.scope === 'org' && filter.communityId !== undefined) {\n ctx.addIssue({\n code: 'custom',\n path: ['communityId'],\n message: '`communityId` implies `scope=community`; it cannot go with `scope=org`',\n });\n }\n if (filter.includeOrg === true && filter.communityId === undefined) {\n ctx.addIssue({\n code: 'custom',\n path: ['includeOrg'],\n message: '`includeOrg` goes with `communityId`',\n });\n }\n if (filter.includeOrg === true && filter.scope !== undefined) {\n ctx.addIssue({\n code: 'custom',\n path: ['includeOrg'],\n message: '`includeOrg` cannot go with `scope`',\n });\n }\n}\n\nconst notificationFilterShape = z\n .object({\n scope: z\n .enum(['org', 'community'])\n .optional()\n .describe('`org`: the org list only; `community`: notifications in any community'),\n communityId: z\n .string()\n .min(1)\n .max(64)\n .optional()\n .describe('Notifications in this community; implies `scope=community`'),\n includeOrg: z\n .stringbool()\n .optional()\n .describe(\n 'With `communityId`, `true` adds the org list (notifications in no community); not with `scope`',\n ),\n category: commaSeparated(\n notificationCategoryKeySchema,\n 'One or more category keys, comma separated',\n ).optional(),\n reason: commaSeparated(\n reasonSchema,\n 'One or more reasons, comma separated; matches any',\n ).optional(),\n type: commaSeparated(\n notificationTypeKeySchema,\n 'One or more types, comma separated',\n ).optional(),\n unread: z.stringbool().optional().describe('`true` for unread notifications only'),\n })\n .strict();\n\nexport const NotificationFilterQuery = notificationFilterShape.superRefine(scopeMatchesCommunity);\n\nexport const NotificationListQuery = PageQuery.extend(notificationFilterShape.shape)\n .strict()\n .superRefine(scopeMatchesCommunity);\n\nconst NotificationItem = z.object({\n type: subjectTypeSchema,\n id: z.string(),\n title: z.string().optional(),\n excerpt: z.string().optional(),\n});\n\nexport const NotificationView = z.object({\n id: z.string(),\n type: z.string(),\n scope: notificationScopeSchema,\n reasons: z.array(reasonSchema),\n matchedReason: reasonSchema.describe(\n 'The reason of the pair the category matched, which picks the wording, such as a reply to your comment',\n ),\n category: z.string(),\n display: z\n .record(z.string(), jsonValueSchema)\n .describe('The text stored when the notification was sent'),\n community: z.object({ id: z.string(), title: z.string() }).nullable(),\n space: z.object({ id: z.string(), name: z.string() }).nullable(),\n actor: z\n .object({\n orgUserId: z.string(),\n name: z.string().nullable(),\n imageUrl: z.string().nullable(),\n })\n .nullable(),\n subject: NotificationItem.nullable().describe(\n 'What the notification is about, with its current title or excerpt; null when you can no longer open it or it was deleted',\n ),\n parent: NotificationItem.nullable().describe(\n \"The subject's container, such as a comment's post; null when you can no longer open it or it was deleted\",\n ),\n createdAt: z.coerce.date(),\n readAt: z.coerce.date().nullable(),\n});\ntype _NotificationViewMatches = Matches<DomainNotificationView, z.infer<typeof NotificationView>> &\n Matches<z.infer<typeof NotificationView>, DomainNotificationView>;\n\nexport const NotificationsPage = paginated(NotificationView).extend({\n unreadCount: z.number().int().describe('Unread notifications matching the same filters'),\n});\n\nexport const NotificationUnreadCounts = z.object({\n org: z.number().int(),\n communities: z.array(z.object({ communityId: z.string(), unread: z.number().int() })),\n});\ntype _NotificationUnreadCountsMatches = Matches<\n DomainNotificationUnreadCounts,\n z.infer<typeof NotificationUnreadCounts>\n>;\n\nexport const NotificationCategory = notificationCategorySchema;\n\nexport const NotificationCategoryList = z.object({\n categories: z.array(NotificationCategory),\n source: z\n .enum(['default', 'stored'])\n .describe('`default` while the list in use is the server default, `stored` once one is saved'),\n});\n\nexport const ReplaceNotificationCategories = z\n .object({\n categories: notificationCategoryListSchema.describe(\n 'The whole ordered list; a notification is filed under the first category that matches',\n ),\n })\n .strict();\n\nexport const NotificationCategoryDefaults = z.object({ categories: z.array(NotificationCategory) });\n\nexport const NotificationListScopeQuery = z\n .object({ scope: z.enum(['org', 'community']) })\n .strict();\n\nexport const NotificationTypeInfo = z.object({\n type: z.string(),\n scopes: z.array(notificationScopeSchema),\n reasons: z.array(reasonSchema),\n});\nexport const NotificationTypeInfos = z.array(NotificationTypeInfo);\n\nexport const NotificationChannelInfos = z.array(z.object({ channel: z.string() }));\n\nconst ResolvedChannel = z.object({\n channel: z.string(),\n enabled: z.boolean(),\n default: z\n .boolean()\n .describe(\n 'What the channel would be without your own setting at this level: what `enabled: null` gives',\n ),\n source: z\n .enum(['locked', 'space', 'community', 'spaceDefault', 'default'])\n .describe(\n 'Why the channel is on or off: locked by the category, your setting for the space, your setting for the whole community (or the org), the default for the space, or the category default',\n ),\n locked: z.boolean(),\n});\n\nconst NotificationSettingsCategory = z.object({\n key: z.string(),\n perSpace: z.boolean(),\n channels: z.array(ResolvedChannel),\n spaces: z\n .array(z.object({ spaceId: z.string(), name: z.string(), channels: z.array(ResolvedChannel) }))\n .optional()\n .describe('Per-space categories only: every space you can open'),\n});\n\nexport const CommunityNotificationSettings = z.object({\n communityId: z.string(),\n digest: digestScheduleSchema\n .nullable()\n .describe(\"The community's digest schedule; null when it sends no digest\"),\n categories: z.array(NotificationSettingsCategory),\n});\ntype _CommunityNotificationSettingsMatches = Matches<\n DomainCommunityNotificationSettingsView,\n z.infer<typeof CommunityNotificationSettings>\n>;\n\nexport const OrgNotificationSettings = z.object({\n categories: z.array(NotificationSettingsCategory),\n});\n\nexport const PatchNotificationSettings = z\n .object({\n settings: z\n .array(\n z\n .object({\n category: notificationCategoryKeySchema,\n spaceId: z.string().min(1).max(64).optional(),\n channel: notificationChannelKeySchema,\n enabled: z\n .boolean()\n .nullable()\n .describe('null removes your choice, so the default applies again'),\n })\n .strict(),\n )\n .min(1)\n .max(200),\n })\n .strict();\n\nconst DeviceFields = {\n id: z.string(),\n provider: z.enum(['apns', 'fcm', 'webpush']),\n token: z.string(),\n p256dh: z.string().nullable(),\n auth: z.string().nullable(),\n appId: z.string().nullable(),\n environment: z.enum(['production', 'sandbox']).nullable(),\n};\n\nexport const Device = z.object(DeviceFields);\nexport const DeviceList = z.array(z.object({ ...DeviceFields, lastSeenAt: z.coerce.date() }));\nexport const DeviceIdParam = z.object({ deviceId: z.string() });\n\nexport const RegisterDevice = z\n .object({\n provider: z.enum(['apns', 'fcm', 'webpush']),\n token: z\n .string()\n .min(1)\n .max(4096)\n .describe('The APNs device token, the FCM registration token, or the Web Push endpoint URL'),\n keys: z\n .object({ p256dh: z.string().min(1).max(512), auth: z.string().min(1).max(512) })\n .strict()\n .optional()\n .describe('The Web Push subscription keys; webpush only'),\n appId: z\n .string()\n .min(1)\n .max(255)\n .optional()\n .describe('The iOS bundle id or Android package name'),\n environment: z.enum(['production', 'sandbox']).optional().describe('apns only'),\n })\n .strict();\n\nexport const PushConfigView = z.object({ vapidPublicKey: z.string().nullable() });\n"],"mappings":"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"}
|
|
@@ -47,8 +47,18 @@ declare const PostView: z.ZodObject<{
|
|
|
47
47
|
reactions: z.ZodRecord<z.ZodString, z.ZodNumber>;
|
|
48
48
|
viewerReaction: z.ZodNullable<z.ZodString>;
|
|
49
49
|
commentCount: z.ZodNumber;
|
|
50
|
-
|
|
50
|
+
subscription: z.ZodNullable<z.ZodObject<{
|
|
51
|
+
state: z.ZodEnum<{
|
|
52
|
+
following: "following";
|
|
53
|
+
muted: "muted";
|
|
54
|
+
}>;
|
|
55
|
+
mutedUntil: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
56
|
+
}, z.core.$strict>>;
|
|
51
57
|
bookmarked: z.ZodBoolean;
|
|
58
|
+
mentions: z.ZodArray<z.ZodObject<{
|
|
59
|
+
orgUserId: z.ZodString;
|
|
60
|
+
name: z.ZodString;
|
|
61
|
+
}, z.core.$strict>>;
|
|
52
62
|
}, z.core.$strict>;
|
|
53
63
|
declare const PostFlag: z.ZodObject<{
|
|
54
64
|
orgId: z.ZodString;
|
|
@@ -113,8 +123,18 @@ declare const PostFeedPage: z.ZodObject<{
|
|
|
113
123
|
reactions: z.ZodRecord<z.ZodString, z.ZodNumber>;
|
|
114
124
|
viewerReaction: z.ZodNullable<z.ZodString>;
|
|
115
125
|
commentCount: z.ZodNumber;
|
|
116
|
-
|
|
126
|
+
subscription: z.ZodNullable<z.ZodObject<{
|
|
127
|
+
state: z.ZodEnum<{
|
|
128
|
+
following: "following";
|
|
129
|
+
muted: "muted";
|
|
130
|
+
}>;
|
|
131
|
+
mutedUntil: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
132
|
+
}, z.core.$strict>>;
|
|
117
133
|
bookmarked: z.ZodBoolean;
|
|
134
|
+
mentions: z.ZodArray<z.ZodObject<{
|
|
135
|
+
orgUserId: z.ZodString;
|
|
136
|
+
name: z.ZodString;
|
|
137
|
+
}, z.core.$strict>>;
|
|
118
138
|
}, z.core.$strict>>;
|
|
119
139
|
total: z.ZodNumber;
|
|
120
140
|
page: z.ZodNumber;
|
|
@@ -210,8 +230,18 @@ declare const SpaceQueue: z.ZodObject<{
|
|
|
210
230
|
reactions: z.ZodRecord<z.ZodString, z.ZodNumber>;
|
|
211
231
|
viewerReaction: z.ZodNullable<z.ZodString>;
|
|
212
232
|
commentCount: z.ZodNumber;
|
|
213
|
-
|
|
233
|
+
subscription: z.ZodNullable<z.ZodObject<{
|
|
234
|
+
state: z.ZodEnum<{
|
|
235
|
+
following: "following";
|
|
236
|
+
muted: "muted";
|
|
237
|
+
}>;
|
|
238
|
+
mutedUntil: z.ZodNullable<z.ZodCoercedDate<unknown>>;
|
|
239
|
+
}, z.core.$strict>>;
|
|
214
240
|
bookmarked: z.ZodBoolean;
|
|
241
|
+
mentions: z.ZodArray<z.ZodObject<{
|
|
242
|
+
orgUserId: z.ZodString;
|
|
243
|
+
name: z.ZodString;
|
|
244
|
+
}, z.core.$strict>>;
|
|
215
245
|
}, z.core.$strict>>;
|
|
216
246
|
flags: z.ZodArray<z.ZodObject<{
|
|
217
247
|
orgId: z.ZodString;
|
|
@@ -304,6 +334,10 @@ declare const PostCommentsPage: z.ZodObject<{
|
|
|
304
334
|
}, z.core.$strict>>;
|
|
305
335
|
reactions: z.ZodRecord<z.ZodString, z.ZodNumber>;
|
|
306
336
|
viewerReaction: z.ZodOptional<z.ZodString>;
|
|
337
|
+
mentions: z.ZodArray<z.ZodObject<{
|
|
338
|
+
orgUserId: z.ZodString;
|
|
339
|
+
name: z.ZodString;
|
|
340
|
+
}, z.core.$strict>>;
|
|
307
341
|
createdAt: z.ZodCoercedDate<unknown>;
|
|
308
342
|
updatedAt: z.ZodCoercedDate<unknown>;
|
|
309
343
|
}, z.core.$strict>>;
|
|
@@ -3,13 +3,13 @@ import { z } from "zod";
|
|
|
3
3
|
declare const UserProfile: z.ZodObject<{
|
|
4
4
|
createdAt: z.ZodCoercedDate<unknown>;
|
|
5
5
|
updatedAt: z.ZodCoercedDate<unknown>;
|
|
6
|
+
imageUrl: z.ZodNullable<z.ZodString>;
|
|
6
7
|
displayName: z.ZodNullable<z.ZodString>;
|
|
7
8
|
headline: z.ZodNullable<z.ZodString>;
|
|
8
9
|
imageAssetId: z.ZodNullable<z.ZodString>;
|
|
9
|
-
imageUrl: z.ZodNullable<z.ZodString>;
|
|
10
10
|
bio: z.ZodNullable<z.ZodString>;
|
|
11
|
-
userId: z.ZodString;
|
|
12
11
|
extra: z.ZodType<import("@headless-lms/core/types").JsonValue, unknown, z.core.$ZodTypeInternals<import("@headless-lms/core/types").JsonValue, unknown>>;
|
|
12
|
+
userId: z.ZodString;
|
|
13
13
|
}, z.core.$strip>;
|
|
14
14
|
declare const UpdateProfile: z.ZodObject<{
|
|
15
15
|
displayName: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
@@ -20,8 +20,15 @@ declare const ValidationErrorBody: z.ZodObject<{
|
|
|
20
20
|
}, z.core.$strip>>>;
|
|
21
21
|
}, z.core.$strip>;
|
|
22
22
|
type ValidationErrorBody = z.infer<typeof ValidationErrorBody>;
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
declare const UnavailableErrorBody: z.ZodObject<{
|
|
24
|
+
error: z.ZodString;
|
|
25
|
+
message: z.ZodOptional<z.ZodString>;
|
|
26
|
+
requestId: z.ZodOptional<z.ZodString>;
|
|
27
|
+
details: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
28
|
+
}, z.core.$strip>;
|
|
29
|
+
type UnavailableErrorBody = z.infer<typeof UnavailableErrorBody>;
|
|
30
|
+
type RefusalStatus = 400 | 403 | 404 | 409 | 413 | 415 | 422 | 502 | 503;
|
|
31
|
+
type RefusalBodies<S extends RefusalStatus> = { [K in S]: K extends 400 | 422 ? typeof ValidationErrorBody : K extends 503 ? typeof UnavailableErrorBody : typeof ErrorBody; };
|
|
25
32
|
/** Response schemas for the refusals a route can answer. A 400 keeps the validation `issues`. */
|
|
26
33
|
declare function refusals<S extends RefusalStatus>(...statuses: S[]): RefusalBodies<S>;
|
|
27
34
|
/** An RFC 3339 date-time in a request, such as `2026-10-29T12:00:00Z`, read as a Date. */
|
|
@@ -78,5 +85,5 @@ declare const OrgUserProfileSchema: z.ZodObject<{
|
|
|
78
85
|
}, z.core.$strip>;
|
|
79
86
|
type OrgUserProfileSchema = z.infer<typeof OrgUserProfileSchema>;
|
|
80
87
|
//#endregion
|
|
81
|
-
export { DateTimeInput, ErrorBody, ListQuery, Matches, OrgRole, OrgUserProfileSchema, PageQuery, UserProfileSchema, ValidationErrorBody, paginated, refusals };
|
|
88
|
+
export { DateTimeInput, ErrorBody, ListQuery, Matches, OrgRole, OrgUserProfileSchema, PageQuery, UnavailableErrorBody, UserProfileSchema, ValidationErrorBody, paginated, refusals };
|
|
82
89
|
//# sourceMappingURL=shared.d.ts.map
|
|
@@ -12,9 +12,10 @@ const ValidationErrorBody = ErrorBody.extend({ issues: z.array(z.object({
|
|
|
12
12
|
path: z.string().describe("Where in the request, such as `body.topics[1].name`"),
|
|
13
13
|
message: z.string()
|
|
14
14
|
})).optional() });
|
|
15
|
+
const UnavailableErrorBody = ErrorBody.extend({ details: z.record(z.string(), z.string()).optional().describe("Ids of what the request left behind, such as the post kept as a draft") });
|
|
15
16
|
/** Response schemas for the refusals a route can answer. A 400 keeps the validation `issues`. */
|
|
16
17
|
function refusals(...statuses) {
|
|
17
|
-
return Object.fromEntries(statuses.map((status) => [status, status === 400 ? ValidationErrorBody : ErrorBody]));
|
|
18
|
+
return Object.fromEntries(statuses.map((status) => [status, status === 400 || status === 422 ? ValidationErrorBody : status === 503 ? UnavailableErrorBody : ErrorBody]));
|
|
18
19
|
}
|
|
19
20
|
/** An RFC 3339 date-time in a request, such as `2026-10-29T12:00:00Z`, read as a Date. */
|
|
20
21
|
const DateTimeInput = z.iso.datetime({ offset: true }).pipe(z.coerce.date());
|
|
@@ -68,6 +69,6 @@ const OrgUserProfileSchema = z.object({
|
|
|
68
69
|
image: z.string().nullable()
|
|
69
70
|
});
|
|
70
71
|
//#endregion
|
|
71
|
-
export { DateTimeInput, ErrorBody, ListQuery, OrgRole, OrgUserProfileSchema, PageQuery, UserProfileSchema, ValidationErrorBody, paginated, refusals };
|
|
72
|
+
export { DateTimeInput, ErrorBody, ListQuery, OrgRole, OrgUserProfileSchema, PageQuery, UnavailableErrorBody, UserProfileSchema, ValidationErrorBody, paginated, refusals };
|
|
72
73
|
|
|
73
74
|
//# sourceMappingURL=shared.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared.js","names":[],"sources":["../../../src/http/schemas/shared.ts"],"sourcesContent":["// Shared contract primitives reused across resource contracts.\nimport { z } from 'zod';\n\n/** Fails typecheck when a schema and its domain type drift apart. */\nexport type Matches<A, _B extends A> = true;\n\n/** Uniform error envelope returned by every non-2xx response. */\nexport const ErrorBody = z.object({\n error: z.string(),\n message: z.string().optional(),\n /** Server-side correlation id — quote it to find the matching API log line. */\n requestId: z.string().optional(),\n});\nexport type ErrorBody = z.infer<typeof ErrorBody>;\n\n/** A 400 from request validation: `message` names the first problem, `issues` lists them all. */\nexport const ValidationErrorBody = ErrorBody.extend({\n issues: z\n .array(\n z.object({\n path: z.string().describe('Where in the request, such as `body.topics[1].name`'),\n message: z.string(),\n }),\n )\n .optional(),\n});\nexport type ValidationErrorBody = z.infer<typeof ValidationErrorBody>;\n\ntype RefusalStatus = 400 | 403 | 404 | 409 | 413 | 415 | 502;\ntype RefusalBodies<S extends RefusalStatus> = {\n [K in S]: K extends 400 ? typeof ValidationErrorBody : typeof ErrorBody;\n};\n\n/** Response schemas for the refusals a route can answer. A 400 keeps the validation `issues`. */\nexport function refusals<S extends RefusalStatus>(...statuses: S[]): RefusalBodies<S> {\n return Object.fromEntries(\n statuses.map((status) => [status
|
|
1
|
+
{"version":3,"file":"shared.js","names":[],"sources":["../../../src/http/schemas/shared.ts"],"sourcesContent":["// Shared contract primitives reused across resource contracts.\nimport { z } from 'zod';\n\n/** Fails typecheck when a schema and its domain type drift apart. */\nexport type Matches<A, _B extends A> = true;\n\n/** Uniform error envelope returned by every non-2xx response. */\nexport const ErrorBody = z.object({\n error: z.string(),\n message: z.string().optional(),\n /** Server-side correlation id — quote it to find the matching API log line. */\n requestId: z.string().optional(),\n});\nexport type ErrorBody = z.infer<typeof ErrorBody>;\n\n/** A 400 from request validation: `message` names the first problem, `issues` lists them all. */\nexport const ValidationErrorBody = ErrorBody.extend({\n issues: z\n .array(\n z.object({\n path: z.string().describe('Where in the request, such as `body.topics[1].name`'),\n message: z.string(),\n }),\n )\n .optional(),\n});\nexport type ValidationErrorBody = z.infer<typeof ValidationErrorBody>;\n\nexport const UnavailableErrorBody = ErrorBody.extend({\n details: z\n .record(z.string(), z.string())\n .optional()\n .describe('Ids of what the request left behind, such as the post kept as a draft'),\n});\nexport type UnavailableErrorBody = z.infer<typeof UnavailableErrorBody>;\n\ntype RefusalStatus = 400 | 403 | 404 | 409 | 413 | 415 | 422 | 502 | 503;\ntype RefusalBodies<S extends RefusalStatus> = {\n [K in S]: K extends 400 | 422\n ? typeof ValidationErrorBody\n : K extends 503\n ? typeof UnavailableErrorBody\n : typeof ErrorBody;\n};\n\n/** Response schemas for the refusals a route can answer. A 400 keeps the validation `issues`. */\nexport function refusals<S extends RefusalStatus>(...statuses: S[]): RefusalBodies<S> {\n return Object.fromEntries(\n statuses.map((status) => [\n status,\n status === 400 || status === 422\n ? ValidationErrorBody\n : status === 503\n ? UnavailableErrorBody\n : ErrorBody,\n ]),\n ) as RefusalBodies<S>;\n}\n\n/** An RFC 3339 date-time in a request, such as `2026-10-29T12:00:00Z`, read as a Date. */\nexport const DateTimeInput = z.iso.datetime({ offset: true }).pipe(z.coerce.date());\n\n/** Page position only, for lists that take no search or sort. */\nexport const PageQuery = z\n .object({\n page: z.coerce.number().int().min(1).default(1),\n pageSize: z.coerce.number().int().min(1).max(100).default(20),\n })\n .strict();\nexport type PageQuery = z.infer<typeof PageQuery>;\n\n/**\n * Common list query for page-based collections. `z.coerce` because query-string\n * values arrive as strings; defaults make the params optional for callers.\n */\nexport const ListQuery = z.object({\n page: z.coerce.number().int().min(1).default(1),\n pageSize: z.coerce.number().int().min(1).max(100).default(20),\n search: z.string().trim().max(200).optional(),\n /** Sort field, optionally prefixed with `-` for descending (e.g. `-updatedAt`). */\n sort: z.string().optional(),\n});\nexport type ListQuery = z.infer<typeof ListQuery>;\n\n/** Wrap a row schema into the standard paginated envelope. */\nexport function paginated<T extends z.ZodTypeAny>(row: T) {\n return z.object({\n rows: z.array(row),\n total: z.number().int(),\n page: z.number().int(),\n pageSize: z.number().int(),\n });\n}\n\n/** All four org roles. `members.ts` exports a staff-only three-value `Role`;\n * this is the full set, used wherever a learner can also appear. */\nexport const OrgRole = z.enum(['owner', 'admin', 'instructor', 'moderator', 'student']);\nexport type OrgRole = z.infer<typeof OrgRole>;\n\n/** Mirrors UserProfile in @headless-lms/core/types — keyed on the auth user id. */\nexport const UserProfileSchema = z.object({\n id: z.string(),\n name: z.string(),\n email: z.string(),\n image: z.string().nullable(),\n});\nexport type UserProfileSchema = z.infer<typeof UserProfileSchema>;\n\n/** Mirrors OrgUserProfile in @headless-lms/core/types — keyed on org_users.id. */\nexport const OrgUserProfileSchema = z.object({\n id: z.string(),\n firstName: z.string().nullable(),\n lastName: z.string().nullable(),\n email: z.string(),\n image: z.string().nullable(),\n});\nexport type OrgUserProfileSchema = z.infer<typeof OrgUserProfileSchema>;\n"],"mappings":";;;AAOA,MAAa,YAAY,EAAE,OAAO;CAChC,OAAO,EAAE,OAAO;CAChB,SAAS,EAAE,OAAO,CAAC,CAAC,SAAS;;CAE7B,WAAW,EAAE,OAAO,CAAC,CAAC,SAAS;AACjC,CAAC;;AAID,MAAa,sBAAsB,UAAU,OAAO,EAClD,QAAQ,EACL,MACC,EAAE,OAAO;CACP,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS,qDAAqD;CAC/E,SAAS,EAAE,OAAO;AACpB,CAAC,CACH,CAAC,CACA,SAAS,EACd,CAAC;AAGD,MAAa,uBAAuB,UAAU,OAAO,EACnD,SAAS,EACN,OAAO,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC,CAC9B,SAAS,CAAC,CACV,SAAS,uEAAuE,EACrF,CAAC;;AAaD,SAAgB,SAAkC,GAAG,UAAiC;CACpF,OAAO,OAAO,YACZ,SAAS,KAAK,WAAW,CACvB,QACA,WAAW,OAAO,WAAW,MACzB,sBACA,WAAW,MACT,uBACA,SACR,CAAC,CACH;AACF;;AAGA,MAAa,gBAAgB,EAAE,IAAI,SAAS,EAAE,QAAQ,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,CAAC;;AAGlF,MAAa,YAAY,EACtB,OAAO;CACN,MAAM,EAAE,OAAO,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC;CAC9C,UAAU,EAAE,OAAO,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE;AAC9D,CAAC,CAAC,CACD,OAAO;;;;;AAOV,MAAa,YAAY,EAAE,OAAO;CAChC,MAAM,EAAE,OAAO,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC;CAC9C,UAAU,EAAE,OAAO,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE;CAC5D,QAAQ,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,SAAS;;CAE5C,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS;AAC5B,CAAC;;AAID,SAAgB,UAAkC,KAAQ;CACxD,OAAO,EAAE,OAAO;EACd,MAAM,EAAE,MAAM,GAAG;EACjB,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI;EACtB,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI;EACrB,UAAU,EAAE,OAAO,CAAC,CAAC,IAAI;CAC3B,CAAC;AACH;;;AAIA,MAAa,UAAU,EAAE,KAAK;CAAC;CAAS;CAAS;CAAc;CAAa;AAAS,CAAC;;AAItF,MAAa,oBAAoB,EAAE,OAAO;CACxC,IAAI,EAAE,OAAO;CACb,MAAM,EAAE,OAAO;CACf,OAAO,EAAE,OAAO;CAChB,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS;AAC7B,CAAC;;AAID,MAAa,uBAAuB,EAAE,OAAO;CAC3C,IAAI,EAAE,OAAO;CACb,WAAW,EAAE,OAAO,CAAC,CAAC,SAAS;CAC/B,UAAU,EAAE,OAAO,CAAC,CAAC,SAAS;CAC9B,OAAO,EAAE,OAAO;CAChB,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS;AAC7B,CAAC"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
//#region src/http/schemas/students.d.ts
|
|
3
3
|
declare const OrgUserStatus: z.ZodEnum<{
|
|
4
|
-
invited: "invited";
|
|
5
4
|
active: "active";
|
|
5
|
+
invited: "invited";
|
|
6
6
|
}>;
|
|
7
7
|
type OrgUserStatus = z.infer<typeof OrgUserStatus>;
|
|
8
8
|
declare const Student: z.ZodObject<{
|
|
@@ -14,8 +14,8 @@ declare const Student: z.ZodObject<{
|
|
|
14
14
|
entitlementCount: z.ZodNumber;
|
|
15
15
|
avgProgress: z.ZodNumber;
|
|
16
16
|
status: z.ZodEnum<{
|
|
17
|
-
invited: "invited";
|
|
18
17
|
active: "active";
|
|
18
|
+
invited: "invited";
|
|
19
19
|
}>;
|
|
20
20
|
joinedAt: z.ZodString;
|
|
21
21
|
lastActiveAt: z.ZodNullable<z.ZodString>;
|
|
@@ -66,8 +66,8 @@ declare const StudentsPage: z.ZodObject<{
|
|
|
66
66
|
entitlementCount: z.ZodNumber;
|
|
67
67
|
avgProgress: z.ZodNumber;
|
|
68
68
|
status: z.ZodEnum<{
|
|
69
|
-
invited: "invited";
|
|
70
69
|
active: "active";
|
|
70
|
+
invited: "invited";
|
|
71
71
|
}>;
|
|
72
72
|
joinedAt: z.ZodString;
|
|
73
73
|
lastActiveAt: z.ZodNullable<z.ZodString>;
|
package/dist/http/scope.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ interface OrgScope {
|
|
|
18
18
|
declare class NoActiveOrgError extends Error {}
|
|
19
19
|
declare function resolveScope(container: Container, req: FastifyRequest): Promise<OrgScope>;
|
|
20
20
|
declare function resolveScopeFor(container: Container, authUserId: string, authOrgId: string): Promise<OrgScope>;
|
|
21
|
+
declare function resolveConfiguratorScope(container: Container, req: FastifyRequest): Promise<OrgScope>;
|
|
21
22
|
//#endregion
|
|
22
|
-
export { NoActiveOrgError, OrgScope, resolveScope, resolveScopeFor };
|
|
23
|
+
export { NoActiveOrgError, OrgScope, resolveConfiguratorScope, resolveScope, resolveScopeFor };
|
|
23
24
|
//# sourceMappingURL=scope.d.ts.map
|
package/dist/http/scope.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isStaffRole } from "@headless-lms/core/organizations";
|
|
2
|
+
import { ForbiddenError } from "@headless-lms/core/shared/errors";
|
|
2
3
|
//#region src/http/scope.ts
|
|
3
4
|
/** Thrown when the session has no resolvable active org / domain user. */
|
|
4
5
|
var NoActiveOrgError = class extends Error {};
|
|
@@ -38,7 +39,12 @@ async function resolveScopeFor(container, authUserId, authOrgId) {
|
|
|
38
39
|
authOrgId
|
|
39
40
|
};
|
|
40
41
|
}
|
|
42
|
+
async function resolveConfiguratorScope(container, req) {
|
|
43
|
+
const scope = await resolveScope(container, req);
|
|
44
|
+
if (scope.role !== "owner" && scope.role !== "admin") throw new ForbiddenError("Only an owner or an admin can configure notifications");
|
|
45
|
+
return scope;
|
|
46
|
+
}
|
|
41
47
|
//#endregion
|
|
42
|
-
export { NoActiveOrgError, resolveScope, resolveScopeFor };
|
|
48
|
+
export { NoActiveOrgError, resolveConfiguratorScope, resolveScope, resolveScopeFor };
|
|
43
49
|
|
|
44
50
|
//# sourceMappingURL=scope.js.map
|
package/dist/http/scope.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scope.js","names":[],"sources":["../../src/http/scope.ts"],"sourcesContent":["import type { FastifyRequest } from 'fastify';\nimport type { Container } from '../app/container.js';\nimport { isStaffRole, type StaffRole } from '@headless-lms/core/organizations';\n\nexport interface OrgScope {\n /** Domain `organizations.id` for the session's active org. */\n orgId: string;\n /** Domain `users.id` of the acting person. */\n userId: string;\n /** Domain `org_users.id` — the acting org user in this org. */\n orgUserId: string;\n /** The role held in this org. */\n role: StaffRole;\n /** Better-auth organization id (for writes that go through the auth provider). */\n authOrgId: string;\n}\n\n/** Thrown when the session has no resolvable active org / domain user. */\nexport class NoActiveOrgError extends Error {}\n\nexport async function resolveScope(container: Container, req: FastifyRequest): Promise<OrgScope> {\n const authUser = req.authUser;\n const authOrgId = req.authOrgId ?? null;\n if (!authUser) {\n throw new NoActiveOrgError('Sign in to continue');\n }\n if (!authOrgId || !req.orgId) {\n throw new NoActiveOrgError('Choose an organization to continue');\n }\n // The session guard already translated the auth org id to the domain org.\n const org = await container.organizations.getById(req.orgId);\n if (!org) {\n throw new NoActiveOrgError('The active organization no longer exists');\n }\n const user = await container.identity.getUserByExternalId(authUser.id);\n if (!user) {\n throw new NoActiveOrgError('Your account is not set up yet');\n }\n const orgUser = await container.organizations.getOrgUser(org.id, user.id);\n if (!orgUser || !isStaffRole(orgUser.role)) {\n throw new NoActiveOrgError('Only staff of this organization can do this');\n }\n container.requestContext.set({ orgId: org.id });\n return {\n orgId: org.id,\n userId: user.id,\n orgUserId: orgUser.id,\n role: orgUser.role,\n authOrgId,\n };\n}\n\nexport async function resolveScopeFor(\n container: Container,\n authUserId: string,\n authOrgId: string,\n): Promise<OrgScope> {\n const org = await container.organizations.getByExternalId(authOrgId);\n if (!org) {\n throw new NoActiveOrgError('active organization not found');\n }\n const user = await container.identity.getUserByExternalId(authUserId);\n if (!user) {\n throw new NoActiveOrgError('no domain user for the current user');\n }\n const orgUser = await container.organizations.getOrgUser(org.id, user.id);\n if (!orgUser || !isStaffRole(orgUser.role)) {\n throw new NoActiveOrgError('not a staff member of the active organization');\n }\n container.requestContext.set({ orgId: org.id });\n return { orgId: org.id, userId: user.id, orgUserId: orgUser.id, role: orgUser.role, authOrgId };\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"scope.js","names":[],"sources":["../../src/http/scope.ts"],"sourcesContent":["import type { FastifyRequest } from 'fastify';\nimport type { Container } from '../app/container.js';\nimport { isStaffRole, type StaffRole } from '@headless-lms/core/organizations';\nimport { ForbiddenError } from '@headless-lms/core/shared/errors';\n\nexport interface OrgScope {\n /** Domain `organizations.id` for the session's active org. */\n orgId: string;\n /** Domain `users.id` of the acting person. */\n userId: string;\n /** Domain `org_users.id` — the acting org user in this org. */\n orgUserId: string;\n /** The role held in this org. */\n role: StaffRole;\n /** Better-auth organization id (for writes that go through the auth provider). */\n authOrgId: string;\n}\n\n/** Thrown when the session has no resolvable active org / domain user. */\nexport class NoActiveOrgError extends Error {}\n\nexport async function resolveScope(container: Container, req: FastifyRequest): Promise<OrgScope> {\n const authUser = req.authUser;\n const authOrgId = req.authOrgId ?? null;\n if (!authUser) {\n throw new NoActiveOrgError('Sign in to continue');\n }\n if (!authOrgId || !req.orgId) {\n throw new NoActiveOrgError('Choose an organization to continue');\n }\n // The session guard already translated the auth org id to the domain org.\n const org = await container.organizations.getById(req.orgId);\n if (!org) {\n throw new NoActiveOrgError('The active organization no longer exists');\n }\n const user = await container.identity.getUserByExternalId(authUser.id);\n if (!user) {\n throw new NoActiveOrgError('Your account is not set up yet');\n }\n const orgUser = await container.organizations.getOrgUser(org.id, user.id);\n if (!orgUser || !isStaffRole(orgUser.role)) {\n throw new NoActiveOrgError('Only staff of this organization can do this');\n }\n container.requestContext.set({ orgId: org.id });\n return {\n orgId: org.id,\n userId: user.id,\n orgUserId: orgUser.id,\n role: orgUser.role,\n authOrgId,\n };\n}\n\nexport async function resolveScopeFor(\n container: Container,\n authUserId: string,\n authOrgId: string,\n): Promise<OrgScope> {\n const org = await container.organizations.getByExternalId(authOrgId);\n if (!org) {\n throw new NoActiveOrgError('active organization not found');\n }\n const user = await container.identity.getUserByExternalId(authUserId);\n if (!user) {\n throw new NoActiveOrgError('no domain user for the current user');\n }\n const orgUser = await container.organizations.getOrgUser(org.id, user.id);\n if (!orgUser || !isStaffRole(orgUser.role)) {\n throw new NoActiveOrgError('not a staff member of the active organization');\n }\n container.requestContext.set({ orgId: org.id });\n return { orgId: org.id, userId: user.id, orgUserId: orgUser.id, role: orgUser.role, authOrgId };\n}\n\nexport async function resolveConfiguratorScope(\n container: Container,\n req: FastifyRequest,\n): Promise<OrgScope> {\n const scope = await resolveScope(container, req);\n if (scope.role !== 'owner' && scope.role !== 'admin') {\n throw new ForbiddenError('Only an owner or an admin can configure notifications');\n }\n return scope;\n}\n"],"mappings":";;;;AAmBA,IAAa,mBAAb,cAAsC,MAAM,CAAC;AAE7C,eAAsB,aAAa,WAAsB,KAAwC;CAC/F,MAAM,WAAW,IAAI;CACrB,MAAM,YAAY,IAAI,aAAa;CACnC,IAAI,CAAC,UACH,MAAM,IAAI,iBAAiB,qBAAqB;CAElD,IAAI,CAAC,aAAa,CAAC,IAAI,OACrB,MAAM,IAAI,iBAAiB,oCAAoC;CAGjE,MAAM,MAAM,MAAM,UAAU,cAAc,QAAQ,IAAI,KAAK;CAC3D,IAAI,CAAC,KACH,MAAM,IAAI,iBAAiB,0CAA0C;CAEvE,MAAM,OAAO,MAAM,UAAU,SAAS,oBAAoB,SAAS,EAAE;CACrE,IAAI,CAAC,MACH,MAAM,IAAI,iBAAiB,gCAAgC;CAE7D,MAAM,UAAU,MAAM,UAAU,cAAc,WAAW,IAAI,IAAI,KAAK,EAAE;CACxE,IAAI,CAAC,WAAW,CAAC,YAAY,QAAQ,IAAI,GACvC,MAAM,IAAI,iBAAiB,6CAA6C;CAE1E,UAAU,eAAe,IAAI,EAAE,OAAO,IAAI,GAAG,CAAC;CAC9C,OAAO;EACL,OAAO,IAAI;EACX,QAAQ,KAAK;EACb,WAAW,QAAQ;EACnB,MAAM,QAAQ;EACd;CACF;AACF;AAEA,eAAsB,gBACpB,WACA,YACA,WACmB;CACnB,MAAM,MAAM,MAAM,UAAU,cAAc,gBAAgB,SAAS;CACnE,IAAI,CAAC,KACH,MAAM,IAAI,iBAAiB,+BAA+B;CAE5D,MAAM,OAAO,MAAM,UAAU,SAAS,oBAAoB,UAAU;CACpE,IAAI,CAAC,MACH,MAAM,IAAI,iBAAiB,qCAAqC;CAElE,MAAM,UAAU,MAAM,UAAU,cAAc,WAAW,IAAI,IAAI,KAAK,EAAE;CACxE,IAAI,CAAC,WAAW,CAAC,YAAY,QAAQ,IAAI,GACvC,MAAM,IAAI,iBAAiB,+CAA+C;CAE5E,UAAU,eAAe,IAAI,EAAE,OAAO,IAAI,GAAG,CAAC;CAC9C,OAAO;EAAE,OAAO,IAAI;EAAI,QAAQ,KAAK;EAAI,WAAW,QAAQ;EAAI,MAAM,QAAQ;EAAM;CAAU;AAChG;AAEA,eAAsB,yBACpB,WACA,KACmB;CACnB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;CAC/C,IAAI,MAAM,SAAS,WAAW,MAAM,SAAS,SAC3C,MAAM,IAAI,eAAe,uDAAuD;CAElF,OAAO;AACT"}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,9 +8,10 @@ import { runMigrations } from "@headless-lms/adapter-db";
|
|
|
8
8
|
import { InlineAutomationEngine } from "@headless-lms/adapter-defaults/workflows";
|
|
9
9
|
import { AutomationEngine, WebhookSender } from "@headless-lms/core/automations";
|
|
10
10
|
import { Mailer } from "@headless-lms/core/shared/mailer";
|
|
11
|
+
import { PushConfig } from "@headless-lms/core/types";
|
|
11
12
|
import { EmailMessage, EmailSender, ObjectStorage } from "@headless-lms/core/shared/ports";
|
|
12
13
|
//#region src/index.d.ts
|
|
13
14
|
declare function createContainer(config: ServerConfig, options?: BuildContainerOptions): Promise<Container>;
|
|
14
15
|
//#endregion
|
|
15
|
-
export { type AdapterOverrides, type AuthUser, type AutomationEngine, type BuildContainerOptions, type CatalogueField, type CatalogueScope, type CatalogueTool, type Container, type Config as ContainerConfig, type EmailMessage, type EmailSender, InlineAutomationEngine, type LoggingConfig, type Mailer, type McpCatalogue, type ObjectStorage, type ServerConfig, type WebhookSender, buildServer, createContainer, loadIntegrations, mcpCatalogue, runMigrations };
|
|
16
|
+
export { type AdapterOverrides, type AuthUser, type AutomationEngine, type BuildContainerOptions, type CatalogueField, type CatalogueScope, type CatalogueTool, type Container, type Config as ContainerConfig, type EmailMessage, type EmailSender, InlineAutomationEngine, type LoggingConfig, type Mailer, type McpCatalogue, type ObjectStorage, type PushConfig, type ServerConfig, type WebhookSender, buildServer, createContainer, loadIntegrations, mcpCatalogue, runMigrations };
|
|
16
17
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["// Public surface of @headless-lms/server. Installations compose these:\n// const container = await createContainer(config, { pluginsDir, adapters })\n// const app = await buildServer(config, container)\nimport { buildContainer, type BuildContainerOptions, type Container } from './app/container.js';\nimport type { ServerConfig } from './http/config.js';\n\nexport { buildServer } from './http/server.js';\nexport { mcpCatalogue } from './http/mcp/catalogue.js';\nexport type {\n CatalogueField,\n CatalogueScope,\n CatalogueTool,\n McpCatalogue,\n} from './http/mcp/catalogue.js';\nexport { loadIntegrations } from './app/integrations.js';\nexport { InlineAutomationEngine } from '@headless-lms/adapter-defaults/workflows';\n// Operational functions consumed by the @headless-lms/cli bin.\nexport { runMigrations } from '@headless-lms/adapter-db';\nexport type { ServerConfig } from './http/config.js';\n// Re-exporting AuthUser also pulls its module into any program that imports\n// this file (directly or via the workspace path mapping), which is what\n// applies its ambient `declare module \"fastify\"` augmentation\n// (FastifyRequest.authUser/orgId, FastifyInstance.requireOrgSession) —\n// otherwise nothing imports fastify.d.ts and the augmentation never loads.\nexport type { AuthUser } from './http/fastify.js';\nexport type {\n Config as ContainerConfig,\n Container,\n AdapterOverrides,\n BuildContainerOptions,\n LoggingConfig,\n} from './app/container.js';\nexport type { EmailSender, EmailMessage, ObjectStorage } from '@headless-lms/core/shared/ports';\nexport type { Mailer } from '@headless-lms/core/shared/mailer';\nexport type { AutomationEngine, WebhookSender } from '@headless-lms/core/automations';\n\nexport async function createContainer(\n config: ServerConfig,\n options?: BuildContainerOptions,\n): Promise<Container> {\n return buildContainer(\n {\n ...config.container,\n deliveryExpirySeconds: config.deliveryExpirySeconds,\n memberImageMaxBytes: config.memberImageMaxBytes,\n },\n options,\n );\n}\n"],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["// Public surface of @headless-lms/server. Installations compose these:\n// const container = await createContainer(config, { pluginsDir, adapters })\n// const app = await buildServer(config, container)\nimport { buildContainer, type BuildContainerOptions, type Container } from './app/container.js';\nimport type { ServerConfig } from './http/config.js';\n\nexport { buildServer } from './http/server.js';\nexport { mcpCatalogue } from './http/mcp/catalogue.js';\nexport type {\n CatalogueField,\n CatalogueScope,\n CatalogueTool,\n McpCatalogue,\n} from './http/mcp/catalogue.js';\nexport { loadIntegrations } from './app/integrations.js';\nexport { InlineAutomationEngine } from '@headless-lms/adapter-defaults/workflows';\n// Operational functions consumed by the @headless-lms/cli bin.\nexport { runMigrations } from '@headless-lms/adapter-db';\nexport type { ServerConfig } from './http/config.js';\n// Re-exporting AuthUser also pulls its module into any program that imports\n// this file (directly or via the workspace path mapping), which is what\n// applies its ambient `declare module \"fastify\"` augmentation\n// (FastifyRequest.authUser/orgId, FastifyInstance.requireOrgSession) —\n// otherwise nothing imports fastify.d.ts and the augmentation never loads.\nexport type { AuthUser } from './http/fastify.js';\nexport type {\n Config as ContainerConfig,\n Container,\n AdapterOverrides,\n BuildContainerOptions,\n LoggingConfig,\n} from './app/container.js';\nexport type { EmailSender, EmailMessage, ObjectStorage } from '@headless-lms/core/shared/ports';\nexport type { Mailer } from '@headless-lms/core/shared/mailer';\nexport type { AutomationEngine, WebhookSender } from '@headless-lms/core/automations';\nexport type { PushConfig } from '@headless-lms/core/types';\n\nexport async function createContainer(\n config: ServerConfig,\n options?: BuildContainerOptions,\n): Promise<Container> {\n return buildContainer(\n {\n ...config.container,\n deliveryExpirySeconds: config.deliveryExpirySeconds,\n memberImageMaxBytes: config.memberImageMaxBytes,\n },\n options,\n );\n}\n"],"mappings":";;;;;;;AAqCA,eAAsB,gBACpB,QACA,SACoB;CACpB,OAAO,eACL;EACE,GAAG,OAAO;EACV,uBAAuB,OAAO;EAC9B,qBAAqB,OAAO;CAC9B,GACA,OACF;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@headless-lms/server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -23,10 +23,10 @@
|
|
|
23
23
|
"fastify": "^5.2.0",
|
|
24
24
|
"fastify-type-provider-zod": "^7.0.0",
|
|
25
25
|
"zod": "4.6.7",
|
|
26
|
-
"@headless-lms/adapter-auth": "0.3.
|
|
27
|
-
"@headless-lms/adapter-db": "0.
|
|
28
|
-
"@headless-lms/adapter-defaults": "0.
|
|
29
|
-
"@headless-lms/core": "0.
|
|
26
|
+
"@headless-lms/adapter-auth": "0.3.1",
|
|
27
|
+
"@headless-lms/adapter-db": "0.4.0",
|
|
28
|
+
"@headless-lms/adapter-defaults": "0.4.0",
|
|
29
|
+
"@headless-lms/core": "0.4.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@types/node": "^22.10.2",
|