@headless-lms/core 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. package/dist/assets/events.d.ts +90 -9
  2. package/dist/assets/events.js +19 -3
  3. package/dist/assets/events.js.map +1 -1
  4. package/dist/assets/file-urls.d.ts +20 -0
  5. package/dist/assets/file-urls.js +25 -0
  6. package/dist/assets/file-urls.js.map +1 -0
  7. package/dist/assets/index.d.ts +5 -4
  8. package/dist/assets/index.js +3 -2
  9. package/dist/assets/model.d.ts +2 -2
  10. package/dist/assets/ports.d.ts +45 -15
  11. package/dist/assets/service.d.ts +39 -7
  12. package/dist/assets/service.js +292 -37
  13. package/dist/assets/service.js.map +1 -1
  14. package/dist/automations/actions.d.ts +12 -15
  15. package/dist/automations/actions.js +188 -30
  16. package/dist/automations/actions.js.map +1 -1
  17. package/dist/automations/catalog.d.ts +5 -1
  18. package/dist/automations/catalog.js +514 -197
  19. package/dist/automations/catalog.js.map +1 -1
  20. package/dist/automations/community-derivations.d.ts +14 -0
  21. package/dist/automations/community-derivations.js +439 -0
  22. package/dist/automations/community-derivations.js.map +1 -0
  23. package/dist/automations/community-mail.d.ts +112 -0
  24. package/dist/automations/community-mail.js +90 -0
  25. package/dist/automations/community-mail.js.map +1 -0
  26. package/dist/automations/community-notifications.d.ts +16 -0
  27. package/dist/automations/community-notifications.js +84 -0
  28. package/dist/automations/community-notifications.js.map +1 -0
  29. package/dist/automations/events.d.ts +74 -24
  30. package/dist/automations/events.js +5 -5
  31. package/dist/automations/events.js.map +1 -1
  32. package/dist/automations/index.d.ts +7 -5
  33. package/dist/automations/index.js +3 -2
  34. package/dist/automations/model.d.ts +20 -3
  35. package/dist/automations/model.js +36 -3
  36. package/dist/automations/model.js.map +1 -1
  37. package/dist/automations/ports.d.ts +27 -8
  38. package/dist/automations/schedule.d.ts +6 -0
  39. package/dist/automations/schedule.js +41 -0
  40. package/dist/automations/schedule.js.map +1 -0
  41. package/dist/automations/seeds.d.ts +17 -0
  42. package/dist/automations/seeds.js +235 -0
  43. package/dist/automations/seeds.js.map +1 -0
  44. package/dist/automations/service.d.ts +41 -6
  45. package/dist/automations/service.js +542 -59
  46. package/dist/automations/service.js.map +1 -1
  47. package/dist/automations/types.d.ts +2 -2
  48. package/dist/automations/webhook-signing.d.ts +7 -0
  49. package/dist/automations/webhook-signing.js +22 -0
  50. package/dist/automations/webhook-signing.js.map +1 -0
  51. package/dist/content/events.d.ts +1013 -25
  52. package/dist/content/events.js +168 -19
  53. package/dist/content/events.js.map +1 -1
  54. package/dist/content/index.d.ts +5 -5
  55. package/dist/content/index.js +2 -2
  56. package/dist/content/model.d.ts +2 -2
  57. package/dist/content/ports.d.ts +109 -25
  58. package/dist/content/service.d.ts +68 -23
  59. package/dist/content/service.js +647 -60
  60. package/dist/content/service.js.map +1 -1
  61. package/dist/content/types.d.ts +2 -2
  62. package/dist/discussion/authors.d.ts +10 -0
  63. package/dist/discussion/authors.js +23 -0
  64. package/dist/discussion/authors.js.map +1 -0
  65. package/dist/discussion/chats.d.ts +104 -0
  66. package/dist/discussion/chats.js +798 -0
  67. package/dist/discussion/chats.js.map +1 -0
  68. package/dist/discussion/events.d.ts +633 -14
  69. package/dist/discussion/events.js +134 -5
  70. package/dist/discussion/events.js.map +1 -1
  71. package/dist/discussion/index.d.ts +7 -3
  72. package/dist/discussion/index.js +4 -1
  73. package/dist/discussion/model.d.ts +1 -2
  74. package/dist/discussion/ports.d.ts +288 -27
  75. package/dist/discussion/ports.js +6 -1
  76. package/dist/discussion/ports.js.map +1 -0
  77. package/dist/discussion/posts.d.ts +103 -0
  78. package/dist/discussion/posts.js +723 -0
  79. package/dist/discussion/posts.js.map +1 -0
  80. package/dist/discussion/service.d.ts +33 -11
  81. package/dist/discussion/service.js +297 -81
  82. package/dist/discussion/service.js.map +1 -1
  83. package/dist/entitlements/events.d.ts +264 -13
  84. package/dist/entitlements/events.js +49 -5
  85. package/dist/entitlements/events.js.map +1 -1
  86. package/dist/entitlements/index.d.ts +5 -4
  87. package/dist/entitlements/index.js +2 -1
  88. package/dist/entitlements/model.d.ts +3 -2
  89. package/dist/entitlements/ports.d.ts +124 -11
  90. package/dist/entitlements/rules.d.ts +12 -0
  91. package/dist/entitlements/rules.js +17 -0
  92. package/dist/entitlements/rules.js.map +1 -0
  93. package/dist/entitlements/service.d.ts +54 -3
  94. package/dist/entitlements/service.js +473 -2
  95. package/dist/entitlements/service.js.map +1 -1
  96. package/dist/identity/access.d.ts +6 -0
  97. package/dist/identity/access.js +7 -0
  98. package/dist/identity/access.js.map +1 -0
  99. package/dist/identity/events.d.ts +17 -3
  100. package/dist/identity/events.js +12 -2
  101. package/dist/identity/events.js.map +1 -1
  102. package/dist/identity/index.d.ts +6 -4
  103. package/dist/identity/index.js +3 -1
  104. package/dist/identity/model.d.ts +2 -1
  105. package/dist/identity/notifications.d.ts +27 -0
  106. package/dist/identity/notifications.js +101 -0
  107. package/dist/identity/notifications.js.map +1 -0
  108. package/dist/identity/ports.d.ts +76 -3
  109. package/dist/identity/service.d.ts +17 -3
  110. package/dist/identity/service.js +73 -5
  111. package/dist/identity/service.js.map +1 -1
  112. package/dist/integrations/events.d.ts +27 -3
  113. package/dist/integrations/events.js +15 -3
  114. package/dist/integrations/events.js.map +1 -1
  115. package/dist/integrations/model.js +1 -1
  116. package/dist/integrations/model.js.map +1 -1
  117. package/dist/integrations/service.js +2 -2
  118. package/dist/integrations/service.js.map +1 -1
  119. package/dist/organizations/access-scopes.d.ts +19 -0
  120. package/dist/organizations/access-scopes.js +89 -0
  121. package/dist/organizations/access-scopes.js.map +1 -0
  122. package/dist/organizations/events.d.ts +234 -25
  123. package/dist/organizations/events.js +58 -9
  124. package/dist/organizations/events.js.map +1 -1
  125. package/dist/organizations/index.d.ts +8 -7
  126. package/dist/organizations/index.js +4 -3
  127. package/dist/organizations/members.d.ts +8 -5
  128. package/dist/organizations/members.js.map +1 -1
  129. package/dist/organizations/ports.d.ts +20 -21
  130. package/dist/organizations/roles.d.ts +6 -10
  131. package/dist/organizations/roles.js +19 -7
  132. package/dist/organizations/roles.js.map +1 -1
  133. package/dist/organizations/service.d.ts +28 -14
  134. package/dist/organizations/service.js +274 -105
  135. package/dist/organizations/service.js.map +1 -1
  136. package/dist/organizations/types.d.ts +2 -11
  137. package/dist/progress/events.d.ts +34 -2
  138. package/dist/progress/events.js +15 -2
  139. package/dist/progress/events.js.map +1 -1
  140. package/dist/progress/index.d.ts +2 -2
  141. package/dist/progress/ports.d.ts +6 -1
  142. package/dist/progress/service.d.ts +5 -3
  143. package/dist/progress/service.js +9 -3
  144. package/dist/progress/service.js.map +1 -1
  145. package/dist/reporting/admin/index.d.ts +4 -0
  146. package/dist/reporting/admin/index.js +2 -0
  147. package/dist/reporting/admin/model.d.ts +108 -0
  148. package/dist/reporting/admin/ports.d.ts +27 -0
  149. package/dist/reporting/admin/service.d.ts +22 -0
  150. package/dist/reporting/admin/service.js +38 -0
  151. package/dist/reporting/admin/service.js.map +1 -0
  152. package/dist/reporting/member/index.d.ts +5 -0
  153. package/dist/reporting/member/index.js +2 -0
  154. package/dist/reporting/member/model.d.ts +124 -0
  155. package/dist/reporting/member/ports.d.ts +45 -0
  156. package/dist/reporting/member/service.d.ts +73 -0
  157. package/dist/reporting/member/service.js +286 -0
  158. package/dist/reporting/member/service.js.map +1 -0
  159. package/dist/shared/actor.d.ts +9 -0
  160. package/dist/shared/errors.d.ts +7 -1
  161. package/dist/shared/errors.js +10 -1
  162. package/dist/shared/errors.js.map +1 -1
  163. package/dist/shared/events.d.ts +5 -3
  164. package/dist/shared/events.js +1 -0
  165. package/dist/shared/events.js.map +1 -1
  166. package/dist/shared/id.d.ts +12 -0
  167. package/dist/shared/id.js +13 -1
  168. package/dist/shared/id.js.map +1 -1
  169. package/dist/shared/mailer.d.ts +6 -1
  170. package/dist/shared/mailer.js.map +1 -1
  171. package/dist/shared/network.d.ts +10 -0
  172. package/dist/shared/network.js +211 -0
  173. package/dist/shared/network.js.map +1 -0
  174. package/dist/shared/settings.js.map +1 -1
  175. package/dist/types/assets.d.ts +2 -2
  176. package/dist/types/automations.d.ts +21 -2
  177. package/dist/types/content.d.ts +2 -2
  178. package/dist/types/discussion.d.ts +12 -3
  179. package/dist/types/email-templates.d.ts +124 -1
  180. package/dist/types/entitlements.d.ts +2 -2
  181. package/dist/types/events.d.ts +3 -3
  182. package/dist/types/identity.d.ts +48 -2
  183. package/dist/types/index.d.ts +10 -10
  184. package/dist/types/organizations.d.ts +2 -2
  185. package/dist/types/ports.d.ts +7 -1
  186. package/dist/types/schemas/assets.d.ts +67 -36
  187. package/dist/types/schemas/assets.js +19 -13
  188. package/dist/types/schemas/assets.js.map +1 -1
  189. package/dist/types/schemas/automations.d.ts +88 -4
  190. package/dist/types/schemas/automations.js +46 -6
  191. package/dist/types/schemas/automations.js.map +1 -1
  192. package/dist/types/schemas/content.d.ts +385 -20
  193. package/dist/types/schemas/content.js +178 -15
  194. package/dist/types/schemas/content.js.map +1 -1
  195. package/dist/types/schemas/credentials.js.map +1 -1
  196. package/dist/types/schemas/discussion.d.ts +379 -4
  197. package/dist/types/schemas/discussion.js +209 -7
  198. package/dist/types/schemas/discussion.js.map +1 -1
  199. package/dist/types/schemas/entitlements.d.ts +44 -4
  200. package/dist/types/schemas/entitlements.js +32 -4
  201. package/dist/types/schemas/entitlements.js.map +1 -1
  202. package/dist/types/schemas/identity.d.ts +115 -5
  203. package/dist/types/schemas/identity.js +59 -6
  204. package/dist/types/schemas/identity.js.map +1 -1
  205. package/dist/types/schemas/index.d.ts +9 -9
  206. package/dist/types/schemas/index.js +9 -9
  207. package/dist/types/schemas/integrations.js.map +1 -1
  208. package/dist/types/schemas/organizations.d.ts +27 -5
  209. package/dist/types/schemas/organizations.js +14 -7
  210. package/dist/types/schemas/organizations.js.map +1 -1
  211. package/dist/types/schemas/outbox.d.ts +1 -0
  212. package/dist/types/schemas/outbox.js +1 -0
  213. package/dist/types/schemas/outbox.js.map +1 -1
  214. package/dist/types/schemas/progress.d.ts +1 -0
  215. package/dist/types/schemas/progress.js +1 -0
  216. package/dist/types/schemas/progress.js.map +1 -1
  217. package/dist/types/schemas/settings.js.map +1 -1
  218. package/dist/types/schemas/shared.d.ts +4 -1
  219. package/dist/types/schemas/shared.js +7 -1
  220. package/dist/types/schemas/shared.js.map +1 -1
  221. package/dist/types/shared.d.ts +1 -1
  222. package/package.json +2 -2
  223. package/dist/reporting/dashboard/index.d.ts +0 -4
  224. package/dist/reporting/dashboard/index.js +0 -2
  225. package/dist/reporting/dashboard/model.d.ts +0 -14
  226. package/dist/reporting/dashboard/ports.d.ts +0 -13
  227. package/dist/reporting/dashboard/service.d.ts +0 -19
  228. package/dist/reporting/dashboard/service.js +0 -20
  229. package/dist/reporting/dashboard/service.js.map +0 -1
  230. package/dist/reporting/learn/index.d.ts +0 -5
  231. package/dist/reporting/learn/index.js +0 -2
  232. package/dist/reporting/learn/model.d.ts +0 -23
  233. package/dist/reporting/learn/ports.d.ts +0 -42
  234. package/dist/reporting/learn/service.d.ts +0 -48
  235. package/dist/reporting/learn/service.js +0 -111
  236. package/dist/reporting/learn/service.js.map +0 -1
  237. package/dist/reporting/students/index.d.ts +0 -4
  238. package/dist/reporting/students/index.js +0 -2
  239. package/dist/reporting/students/model.d.ts +0 -31
  240. package/dist/reporting/students/ports.d.ts +0 -13
  241. package/dist/reporting/students/ports.js +0 -1
  242. package/dist/reporting/students/service.d.ts +0 -19
  243. package/dist/reporting/students/service.js +0 -20
  244. package/dist/reporting/students/service.js.map +0 -1
  245. /package/dist/reporting/{dashboard → admin}/model.js +0 -0
  246. /package/dist/reporting/{dashboard → admin}/ports.js +0 -0
  247. /package/dist/reporting/{learn → member}/model.js +0 -0
  248. /package/dist/reporting/{learn → member}/ports.js +0 -0
  249. /package/dist/{reporting/students/model.js → shared/actor.js} +0 -0
@@ -1,56 +1,105 @@
1
1
  import { inviteSchema, orgUserSchema, organizationSchema } from "../types/schemas/organizations.js";
2
2
  import { defineEvent } from "../shared/events.js";
3
3
  //#region src/organizations/events.ts
4
+ const publicOrganization = organizationSchema.pick({
5
+ id: true,
6
+ name: true,
7
+ slug: true,
8
+ ownerId: true,
9
+ createdAt: true,
10
+ updatedAt: true
11
+ }).strip();
12
+ const publicOrgUser = orgUserSchema.pick({
13
+ id: true,
14
+ orgId: true,
15
+ userId: true,
16
+ role: true,
17
+ status: true,
18
+ createdAt: true,
19
+ updatedAt: true
20
+ }).strip();
21
+ const publicInvite = inviteSchema.pick({
22
+ id: true,
23
+ orgId: true,
24
+ email: true,
25
+ role: true,
26
+ status: true,
27
+ invitedBy: true,
28
+ expiresAt: true,
29
+ createdAt: true,
30
+ updatedAt: true
31
+ }).strip();
4
32
  const organizationEvents = {
5
33
  organizationCreated: defineEvent({
6
34
  type: "organization.created",
7
35
  version: 1,
8
- data: organizationSchema
36
+ data: organizationSchema,
37
+ publicData: publicOrganization
9
38
  }),
10
39
  organizationUpdated: defineEvent({
11
40
  type: "organization.updated",
12
41
  version: 1,
13
- data: organizationSchema
42
+ data: organizationSchema,
43
+ publicData: publicOrganization
14
44
  }),
15
45
  organizationDeleted: defineEvent({
16
46
  type: "organization.deleted",
17
47
  version: 1,
18
- data: organizationSchema
48
+ data: organizationSchema,
49
+ publicData: publicOrganization
19
50
  }),
20
51
  orgUserLinked: defineEvent({
21
52
  type: "organization.user.linked",
22
53
  version: 1,
23
- data: orgUserSchema
54
+ data: orgUserSchema,
55
+ publicData: publicOrgUser
24
56
  }),
25
57
  orgUserDeleted: defineEvent({
26
58
  type: "organization.user.deleted",
27
59
  version: 1,
28
- data: orgUserSchema
60
+ data: orgUserSchema,
61
+ publicData: publicOrgUser
62
+ }),
63
+ orgUserUpdated: defineEvent({
64
+ type: "organization.user.updated",
65
+ version: 1,
66
+ data: orgUserSchema,
67
+ publicData: publicOrgUser
29
68
  }),
30
69
  inviteCreated: defineEvent({
31
70
  type: "organization.invite.created",
32
71
  version: 1,
33
- data: inviteSchema
72
+ data: inviteSchema,
73
+ publicData: publicInvite
34
74
  }),
35
75
  inviteAccepted: defineEvent({
36
76
  type: "organization.invite.accepted",
37
77
  version: 1,
78
+ data: inviteSchema,
79
+ publicData: publicInvite
80
+ }),
81
+ inviteCanceled: defineEvent({
82
+ type: "organization.invite.canceled",
83
+ version: 1,
38
84
  data: inviteSchema
39
85
  }),
40
86
  studentCreated: defineEvent({
41
87
  type: "organization.student.created",
42
88
  version: 1,
43
- data: orgUserSchema
89
+ data: orgUserSchema,
90
+ publicData: publicOrgUser
44
91
  }),
45
92
  studentDeleted: defineEvent({
46
93
  type: "organization.student.deleted",
47
94
  version: 1,
48
- data: orgUserSchema
95
+ data: orgUserSchema,
96
+ publicData: publicOrgUser
49
97
  }),
50
98
  studentLinked: defineEvent({
51
99
  type: "organization.student.linked",
52
100
  version: 1,
53
- data: orgUserSchema
101
+ data: orgUserSchema,
102
+ publicData: publicOrgUser
54
103
  })
55
104
  };
56
105
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"events.js","names":[],"sources":["../../src/organizations/events.ts"],"sourcesContent":["import {\n inviteSchema,\n organizationSchema,\n orgUserSchema,\n} from '../types/schemas/index.js';\nimport { defineEvent, type EventOf, type EventOfValues } from '../shared/ports.js';\n\nexport const organizationEvents = {\n organizationCreated: defineEvent({\n type: 'organization.created',\n version: 1,\n data: organizationSchema,\n }),\n organizationUpdated: defineEvent({\n type: 'organization.updated',\n version: 1,\n data: organizationSchema,\n }),\n organizationDeleted: defineEvent({\n type: 'organization.deleted',\n version: 1,\n data: organizationSchema,\n }),\n orgUserLinked: defineEvent({\n type: 'organization.user.linked',\n version: 1,\n data: orgUserSchema,\n }),\n orgUserDeleted: defineEvent({\n type: 'organization.user.deleted',\n version: 1,\n data: orgUserSchema,\n }),\n inviteCreated: defineEvent({\n type: 'organization.invite.created',\n version: 1,\n data: inviteSchema,\n }),\n inviteAccepted: defineEvent({\n type: 'organization.invite.accepted',\n version: 1,\n data: inviteSchema,\n }),\n studentCreated: defineEvent({\n type: 'organization.student.created',\n version: 1,\n data: orgUserSchema,\n }),\n studentDeleted: defineEvent({\n type: 'organization.student.deleted',\n version: 1,\n data: orgUserSchema,\n }),\n studentLinked: defineEvent({\n type: 'organization.student.linked',\n version: 1,\n data: orgUserSchema,\n }),\n};\n\nexport type OrganizationCreated = EventOf<typeof organizationEvents.organizationCreated>;\nexport type OrganizationUpdated = EventOf<typeof organizationEvents.organizationUpdated>;\nexport type OrganizationDeleted = EventOf<typeof organizationEvents.organizationDeleted>;\nexport type OrgUserLinked = EventOf<typeof organizationEvents.orgUserLinked>;\nexport type OrgUserDeleted = EventOf<typeof organizationEvents.orgUserDeleted>;\nexport type InviteCreated = EventOf<typeof organizationEvents.inviteCreated>;\nexport type InviteAccepted = EventOf<typeof organizationEvents.inviteAccepted>;\nexport type StudentCreated = EventOf<typeof organizationEvents.studentCreated>;\nexport type StudentDeleted = EventOf<typeof organizationEvents.studentDeleted>;\nexport type StudentLinked = EventOf<typeof organizationEvents.studentLinked>;\nexport type OrganizationEvent = EventOfValues<typeof organizationEvents>;\n"],"mappings":";;;AAOA,MAAa,qBAAqB;CAChC,qBAAqB,YAAY;EAC/B,MAAM;EACN,SAAS;EACT,MAAM;CACR,CAAC;CACD,qBAAqB,YAAY;EAC/B,MAAM;EACN,SAAS;EACT,MAAM;CACR,CAAC;CACD,qBAAqB,YAAY;EAC/B,MAAM;EACN,SAAS;EACT,MAAM;CACR,CAAC;CACD,eAAe,YAAY;EACzB,MAAM;EACN,SAAS;EACT,MAAM;CACR,CAAC;CACD,gBAAgB,YAAY;EAC1B,MAAM;EACN,SAAS;EACT,MAAM;CACR,CAAC;CACD,eAAe,YAAY;EACzB,MAAM;EACN,SAAS;EACT,MAAM;CACR,CAAC;CACD,gBAAgB,YAAY;EAC1B,MAAM;EACN,SAAS;EACT,MAAM;CACR,CAAC;CACD,gBAAgB,YAAY;EAC1B,MAAM;EACN,SAAS;EACT,MAAM;CACR,CAAC;CACD,gBAAgB,YAAY;EAC1B,MAAM;EACN,SAAS;EACT,MAAM;CACR,CAAC;CACD,eAAe,YAAY;EACzB,MAAM;EACN,SAAS;EACT,MAAM;CACR,CAAC;AACH"}
1
+ {"version":3,"file":"events.js","names":[],"sources":["../../src/organizations/events.ts"],"sourcesContent":["import { inviteSchema, organizationSchema, orgUserSchema } from '../types/schemas/index.js';\nimport { defineEvent, type EventOf, type EventOfValues } from '../shared/ports.js';\n\nconst publicOrganization = organizationSchema\n .pick({\n id: true,\n name: true,\n slug: true,\n ownerId: true,\n createdAt: true,\n updatedAt: true,\n })\n .strip();\n\nconst publicOrgUser = orgUserSchema\n .pick({\n id: true,\n orgId: true,\n userId: true,\n role: true,\n status: true,\n createdAt: true,\n updatedAt: true,\n })\n .strip();\n\nconst publicInvite = inviteSchema\n .pick({\n id: true,\n orgId: true,\n email: true,\n role: true,\n status: true,\n invitedBy: true,\n expiresAt: true,\n createdAt: true,\n updatedAt: true,\n })\n .strip();\n\nexport const organizationEvents = {\n organizationCreated: defineEvent({\n type: 'organization.created',\n version: 1,\n data: organizationSchema,\n publicData: publicOrganization,\n }),\n organizationUpdated: defineEvent({\n type: 'organization.updated',\n version: 1,\n data: organizationSchema,\n publicData: publicOrganization,\n }),\n organizationDeleted: defineEvent({\n type: 'organization.deleted',\n version: 1,\n data: organizationSchema,\n publicData: publicOrganization,\n }),\n orgUserLinked: defineEvent({\n type: 'organization.user.linked',\n version: 1,\n data: orgUserSchema,\n publicData: publicOrgUser,\n }),\n orgUserDeleted: defineEvent({\n type: 'organization.user.deleted',\n version: 1,\n data: orgUserSchema,\n publicData: publicOrgUser,\n }),\n orgUserUpdated: defineEvent({\n type: 'organization.user.updated',\n version: 1,\n data: orgUserSchema,\n publicData: publicOrgUser,\n }),\n inviteCreated: defineEvent({\n type: 'organization.invite.created',\n version: 1,\n data: inviteSchema,\n publicData: publicInvite,\n }),\n inviteAccepted: defineEvent({\n type: 'organization.invite.accepted',\n version: 1,\n data: inviteSchema,\n publicData: publicInvite,\n }),\n inviteCanceled: defineEvent({\n type: 'organization.invite.canceled',\n version: 1,\n data: inviteSchema,\n }),\n studentCreated: defineEvent({\n type: 'organization.student.created',\n version: 1,\n data: orgUserSchema,\n publicData: publicOrgUser,\n }),\n studentDeleted: defineEvent({\n type: 'organization.student.deleted',\n version: 1,\n data: orgUserSchema,\n publicData: publicOrgUser,\n }),\n studentLinked: defineEvent({\n type: 'organization.student.linked',\n version: 1,\n data: orgUserSchema,\n publicData: publicOrgUser,\n }),\n};\n\nexport type OrganizationCreated = EventOf<typeof organizationEvents.organizationCreated>;\nexport type OrganizationUpdated = EventOf<typeof organizationEvents.organizationUpdated>;\nexport type OrganizationDeleted = EventOf<typeof organizationEvents.organizationDeleted>;\nexport type OrgUserLinked = EventOf<typeof organizationEvents.orgUserLinked>;\nexport type OrgUserDeleted = EventOf<typeof organizationEvents.orgUserDeleted>;\nexport type OrgUserUpdated = EventOf<typeof organizationEvents.orgUserUpdated>;\nexport type InviteCreated = EventOf<typeof organizationEvents.inviteCreated>;\nexport type InviteAccepted = EventOf<typeof organizationEvents.inviteAccepted>;\nexport type InviteCanceled = EventOf<typeof organizationEvents.inviteCanceled>;\nexport type StudentCreated = EventOf<typeof organizationEvents.studentCreated>;\nexport type StudentDeleted = EventOf<typeof organizationEvents.studentDeleted>;\nexport type StudentLinked = EventOf<typeof organizationEvents.studentLinked>;\nexport type OrganizationEvent = EventOfValues<typeof organizationEvents>;\n"],"mappings":";;;AAGA,MAAM,qBAAqB,mBACxB,KAAK;CACJ,IAAI;CACJ,MAAM;CACN,MAAM;CACN,SAAS;CACT,WAAW;CACX,WAAW;AACb,CAAC,CAAC,CACD,MAAM;AAET,MAAM,gBAAgB,cACnB,KAAK;CACJ,IAAI;CACJ,OAAO;CACP,QAAQ;CACR,MAAM;CACN,QAAQ;CACR,WAAW;CACX,WAAW;AACb,CAAC,CAAC,CACD,MAAM;AAET,MAAM,eAAe,aAClB,KAAK;CACJ,IAAI;CACJ,OAAO;CACP,OAAO;CACP,MAAM;CACN,QAAQ;CACR,WAAW;CACX,WAAW;CACX,WAAW;CACX,WAAW;AACb,CAAC,CAAC,CACD,MAAM;AAET,MAAa,qBAAqB;CAChC,qBAAqB,YAAY;EAC/B,MAAM;EACN,SAAS;EACT,MAAM;EACN,YAAY;CACd,CAAC;CACD,qBAAqB,YAAY;EAC/B,MAAM;EACN,SAAS;EACT,MAAM;EACN,YAAY;CACd,CAAC;CACD,qBAAqB,YAAY;EAC/B,MAAM;EACN,SAAS;EACT,MAAM;EACN,YAAY;CACd,CAAC;CACD,eAAe,YAAY;EACzB,MAAM;EACN,SAAS;EACT,MAAM;EACN,YAAY;CACd,CAAC;CACD,gBAAgB,YAAY;EAC1B,MAAM;EACN,SAAS;EACT,MAAM;EACN,YAAY;CACd,CAAC;CACD,gBAAgB,YAAY;EAC1B,MAAM;EACN,SAAS;EACT,MAAM;EACN,YAAY;CACd,CAAC;CACD,eAAe,YAAY;EACzB,MAAM;EACN,SAAS;EACT,MAAM;EACN,YAAY;CACd,CAAC;CACD,gBAAgB,YAAY;EAC1B,MAAM;EACN,SAAS;EACT,MAAM;EACN,YAAY;CACd,CAAC;CACD,gBAAgB,YAAY;EAC1B,MAAM;EACN,SAAS;EACT,MAAM;CACR,CAAC;CACD,gBAAgB,YAAY;EAC1B,MAAM;EACN,SAAS;EACT,MAAM;EACN,YAAY;CACd,CAAC;CACD,gBAAgB,YAAY;EAC1B,MAAM;EACN,SAAS;EACT,MAAM;EACN,YAAY;CACd,CAAC;CACD,eAAe,YAAY;EACzB,MAAM;EACN,SAAS;EACT,MAAM;EACN,YAAY;CACd,CAAC;AACH"}
@@ -1,11 +1,12 @@
1
- import { AcceptInviteInput, AddOrgUserInput, CreateInviteInput, CreateOrgUserInput, Invite, InviteRole, NewOrganizationInput, OrgUser, OrgUserStatus, Organization, Role } from "../types/schemas/organizations.js";
1
+ import { AcceptInviteInput, AddOrgUserInput, CreateInviteInput, CreateOrgUserInput, Invite, InviteRole, NewOrganizationInput, OrgUser, OrgUserStatus, Organization, Role, UpdateOrgUserInput } from "../types/schemas/organizations.js";
2
2
  import { InviteId, OrgUserId, OrganizationId } from "../types/organizations.js";
3
- import { AuthHeaders } from "../identity/ports.js";
4
- import { InviteAccepted, InviteCreated, OrgUserDeleted, OrgUserLinked, OrganizationCreated, OrganizationDeleted, OrganizationEvent, OrganizationUpdated, StudentCreated, StudentDeleted, StudentLinked, organizationEvents } from "./events.js";
5
3
  import "./model.js";
6
- import { Capability, Permission, ROLES, STAFF_ROLES, STUDENT_ROLE, StaffRole, capability, isRole, isStaffRole, normalizeRole, parseRole } from "./roles.js";
7
- import { Member, MemberStatus, MembersQuery, OrganizationRuleError, Page } from "./members.js";
8
- import { CreateOrganizationInput, LinkOrgUserInput, ResendStudentInviteInput, UnlinkOrgUserInput, UpdateOrganizationInput, UpdatePersonInput, UpdateStudentInput } from "./types.js";
4
+ import { Capability, Permission, ROLES, STAFF_ROLES, STUDENT_ROLE, StaffRole, TeamRole, canModerate, capability, isRole, isStaffRole, normalizeRole, parseRole } from "./roles.js";
5
+ import { InviteError, Member, MemberRoleChange, MemberStatus, MembersQuery, OrganizationRuleError, Page } from "./members.js";
6
+ import { CreateOrganizationInput, LinkOrgUserInput, ResendStudentInviteInput, UnlinkOrgUserInput, UpdateOrganizationInput } from "./types.js";
7
+ import { AuthHeaders } from "../identity/ports.js";
9
8
  import { AuthOrganization, MemberRecord, MemberWriteContext, MembersRepository, NewInviteRow, NewOrganizationRow, OrgAdmin, OrganizationService, OrganizationsRepository } from "./ports.js";
10
9
  import { OrganizationServiceImpl } from "./service.js";
11
- export { type AcceptInviteInput, type AddOrgUserInput, type AuthHeaders, type AuthOrganization, type Capability, type CreateInviteInput, type CreateOrgUserInput, type CreateOrganizationInput, type Invite, type InviteAccepted, type InviteCreated, type InviteId, type InviteRole, type LinkOrgUserInput, type Member, type MemberRecord, 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 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, STAFF_ROLES, STUDENT_ROLE, type StaffRole, type StudentCreated, type StudentDeleted, type StudentLinked, type UnlinkOrgUserInput, type UpdateOrganizationInput, type UpdatePersonInput, type UpdateStudentInput, capability, isRole, isStaffRole, normalizeRole, organizationEvents, parseRole };
10
+ import { InviteAccepted, InviteCanceled, InviteCreated, OrgUserDeleted, OrgUserLinked, OrgUserUpdated, OrganizationCreated, OrganizationDeleted, OrganizationEvent, OrganizationUpdated, StudentCreated, StudentDeleted, StudentLinked, organizationEvents } from "./events.js";
11
+ 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 };
@@ -1,5 +1,6 @@
1
- import { ROLES, STAFF_ROLES, STUDENT_ROLE, capability, isRole, isStaffRole, normalizeRole, parseRole } from "./roles.js";
2
- import { OrganizationRuleError } from "./members.js";
1
+ import { ROLES, STAFF_ROLES, STUDENT_ROLE, canModerate, capability, isRole, isStaffRole, normalizeRole, parseRole } from "./roles.js";
2
+ import { InviteError, OrganizationRuleError } from "./members.js";
3
3
  import { organizationEvents } from "./events.js";
4
4
  import { OrganizationServiceImpl } from "./service.js";
5
- export { OrganizationRuleError, OrganizationServiceImpl, ROLES, STAFF_ROLES, STUDENT_ROLE, capability, isRole, isStaffRole, normalizeRole, organizationEvents, parseRole };
5
+ 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 };
@@ -1,20 +1,23 @@
1
- import { OrgUserProfile } from "../types/schemas/organizations.js";
1
+ import { OrgUserProfile, Role } from "../types/schemas/organizations.js";
2
2
  import "../types/index.js";
3
- import { StaffRole } from "./roles.js";
3
+ import { TeamRole } from "./roles.js";
4
4
  //#region src/organizations/members.d.ts
5
5
  type MemberStatus = 'active' | 'invited';
6
6
  interface Member extends OrgUserProfile {
7
- role: StaffRole;
7
+ role: TeamRole;
8
8
  status: MemberStatus;
9
9
  joinedAt: string | null;
10
10
  invitedAt: string | null;
11
11
  }
12
+ interface MemberRoleChange extends Omit<Member, 'role'> {
13
+ role: Role;
14
+ }
12
15
  interface MembersQuery {
13
16
  page: number;
14
17
  pageSize: number;
15
18
  search?: string | undefined;
16
19
  sort?: string | undefined;
17
- role?: StaffRole | undefined;
20
+ role?: TeamRole | undefined;
18
21
  status?: MemberStatus | undefined;
19
22
  }
20
23
  interface Page<T> {
@@ -28,5 +31,5 @@ declare class InviteError extends Error {
28
31
  constructor(message: string);
29
32
  }
30
33
  //#endregion
31
- export { InviteError, Member, MemberStatus, MembersQuery, OrganizationRuleError, Page };
34
+ export { InviteError, Member, MemberRoleChange, MemberStatus, MembersQuery, OrganizationRuleError, Page };
32
35
  //# sourceMappingURL=members.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"members.js","names":[],"sources":["../../src/organizations/members.ts"],"sourcesContent":["// organizations context — member-management types (invite / change-role / remove).\n// The operational surface over the org's members and pending invites. Roles\n// come from ./roles.ts (the single role model); writes go through Better Auth.\nimport type { StaffRole } from './roles.js';\nimport type { OrgUserProfile } from '../types/index.js';\n\nexport type MemberStatus = 'active' | 'invited';\n\nexport interface Member extends OrgUserProfile {\n role: StaffRole;\n status: MemberStatus;\n joinedAt: string | null;\n invitedAt: string | null;\n}\n\nexport interface MembersQuery {\n page: number;\n pageSize: number;\n search?: string | undefined;\n sort?: string | undefined;\n role?: StaffRole | undefined;\n status?: MemberStatus | undefined;\n}\n\nexport interface Page<T> {\n rows: T[];\n total: number;\n page: number;\n pageSize: number;\n}\n\n\nexport class OrganizationRuleError extends Error {}\nexport class InviteError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'InviteError';\n }\n}\n"],"mappings":";AAgCA,IAAa,wBAAb,cAA2C,MAAM,CAAC;AAClD,IAAa,cAAb,cAAiC,MAAM;CACrC,YAAY,SAAiB;EAC3B,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF"}
1
+ {"version":3,"file":"members.js","names":[],"sources":["../../src/organizations/members.ts"],"sourcesContent":["// organizations context — member-management types (invite / change-role / remove).\n// The operational surface over the org's members and pending invites. Roles\n// come from ./roles.ts (the single role model); writes go through Better Auth.\nimport type { Role, TeamRole } from './roles.js';\nimport type { OrgUserProfile } from '../types/index.js';\n\nexport type MemberStatus = 'active' | 'invited';\n\nexport interface Member extends OrgUserProfile {\n role: TeamRole;\n status: MemberStatus;\n joinedAt: string | null;\n invitedAt: string | null;\n}\n\nexport interface MemberRoleChange extends Omit<Member, 'role'> {\n role: Role;\n}\n\nexport interface MembersQuery {\n page: number;\n pageSize: number;\n search?: string | undefined;\n sort?: string | undefined;\n role?: TeamRole | undefined;\n status?: MemberStatus | undefined;\n}\n\nexport interface Page<T> {\n rows: T[];\n total: number;\n page: number;\n pageSize: number;\n}\n\nexport class OrganizationRuleError extends Error {}\nexport class InviteError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'InviteError';\n }\n}\n"],"mappings":";AAmCA,IAAa,wBAAb,cAA2C,MAAM,CAAC;AAClD,IAAa,cAAb,cAAiC,MAAM;CACrC,YAAY,SAAiB;EAC3B,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF"}
@@ -1,32 +1,35 @@
1
- import { AcceptInviteInput, AddOrgUserInput, CreateInviteInput, CreateOrgUserInput, Invite, InviteRole, NewOrganizationInput, OrgUser, Organization, Role } from "../types/schemas/organizations.js";
1
+ import { AcceptInviteInput, AddOrgUserInput, CreateInviteInput, CreateOrgUserInput, Invite, InviteRole, NewOrganizationInput, OrgUser, Organization, Role, UpdateOrgUserInput } from "../types/schemas/organizations.js";
2
2
  import { OutboxAppender, UnitOfWork } from "../shared/ports.js";
3
- import { AuthHeaders } from "../identity/ports.js";
4
- import "../identity/index.js";
3
+ import { Actor } from "../shared/actor.js";
5
4
  import "./model.js";
6
5
  import "./roles.js";
7
- import { Member, MembersQuery, Page } from "./members.js";
8
- import { CreateOrganizationInput, ResendStudentInviteInput, UpdateOrganizationInput, UpdateStudentInput } from "./types.js";
6
+ import { Member, MemberRoleChange, MembersQuery, Page } from "./members.js";
7
+ import { CreateOrganizationInput, ResendStudentInviteInput, UpdateOrganizationInput } from "./types.js";
8
+ import { AuthHeaders } from "../identity/ports.js";
9
+ import "../identity/index.js";
9
10
  //#region src/organizations/ports.d.ts
10
11
  interface OrganizationService {
11
12
  createOrganization(input: CreateOrganizationInput): Promise<Organization>;
12
13
  updateOrganization(id: string, input: UpdateOrganizationInput): Promise<Organization>;
13
14
  deleteOrganization(id: string): Promise<Organization>;
14
15
  addOrgUser(input: AddOrgUserInput): Promise<OrgUser>;
15
- removeOrgUser(orgId: string, id: string): Promise<OrgUser>;
16
+ removeOrgUser(orgId: string, userId: string): Promise<OrgUser>;
17
+ updateOrgUser(orgId: string, userId: string, patch: UpdateOrgUserInput): Promise<OrgUser>;
16
18
  getByExternalId(externalId: string): Promise<Organization | null>;
17
19
  getOrgUser(orgId: string, userId: string): Promise<OrgUser | null>;
20
+ getOrgUserById(orgId: string, id: string): Promise<OrgUser | null>;
18
21
  getOrgUsersForUser(userId: string): Promise<OrgUser[]>;
19
22
  getById(id: string): Promise<Organization | null>;
20
- createInvite(input: CreateInviteInput): Promise<Invite>;
23
+ createInvite(actor: Actor, input: CreateInviteInput): Promise<Invite>;
21
24
  peekInvite(token: string): Promise<Invite | null>;
22
25
  acceptInvite(input: AcceptInviteInput): Promise<OrgUser>;
23
- deleteOrgUser(orgId: string, id: string): Promise<OrgUser>;
24
- resendStudentInvite(input: ResendStudentInviteInput): Promise<void>;
25
- updateStudent(input: UpdateStudentInput): Promise<OrgUser>;
26
+ deleteOrgUser(orgId: string, actor: Actor, id: string): Promise<OrgUser>;
27
+ resendStudentInvite(actor: Actor, input: ResendStudentInviteInput): Promise<void>;
26
28
  getBySlug(slug: string): Promise<Organization | null>;
27
29
  listMembers(orgId: string, query: MembersQuery): Promise<Page<Member>>;
28
- updateMemberRole(ctx: MemberWriteContext, id: string, role: Role): Promise<Member | null>;
29
- removeMember(ctx: MemberWriteContext, id: string): Promise<boolean>;
30
+ updateMemberRole(ctx: MemberWriteContext, actor: Actor, id: string, role: Role): Promise<MemberRoleChange | null>;
31
+ removeMember(ctx: MemberWriteContext, actor: Actor, id: string): Promise<boolean>;
32
+ staffOrgUserIds(orgId: string): Promise<string[]>;
30
33
  }
31
34
  /** Atomic write scope: tx-bound repo + outbox appender, one transaction. */
32
35
  interface OrganizationsWriteScope {
@@ -59,31 +62,27 @@ interface OrganizationsRepository {
59
62
  findById(id: string): Promise<Organization | null>;
60
63
  findByExternalId(externalId: string): Promise<Organization | null>;
61
64
  findBySlug(slug: string): Promise<Organization | null>;
62
- upsertOrgUser(orgId: string, input: AddOrgUserInput): Promise<OrgUser>;
63
65
  /** Inserts a pending invite, or re-issues the org's existing pending one
64
66
  * for this email (fresh token/expiry/role) — atomic upsert. */
65
67
  upsertPendingInvite(orgId: string, input: NewInviteRow): Promise<Invite>;
66
68
  setInviteStatus(orgId: string, id: string, status: string): Promise<Invite | null>;
67
69
  findInviteByTokenHash(tokenHash: string): Promise<Invite | null>;
70
+ findPendingInvite(orgId: string, email: string): Promise<Invite | null>;
71
+ setInviteToken(orgId: string, id: string, tokenHash: string, expiresAt: Date): Promise<Invite | null>;
68
72
  /** The person's row in one org. `(org_id, user_id)` is unique. */
69
73
  findOrgUser(orgId: string, userId: string): Promise<OrgUser | null>;
70
74
  /** Every org this person belongs to, oldest first. */
71
75
  findOrgUsersByUser(userId: string): Promise<OrgUser[]>;
72
76
  findOrgUserById(orgId: string, id: string): Promise<OrgUser | null>;
77
+ findOrgUsersByIds(orgId: string, ids: string[]): Promise<OrgUser[]>;
78
+ findOrgUserIdsByRole(orgId: string, roles: Role[]): Promise<string[]>;
73
79
  /** Links a user to the org under a role. Throws ConflictError when they
74
80
  * already hold a row there. */
75
81
  createOrgUser(input: CreateOrgUserInput): Promise<OrgUser>;
76
- /** createOrgUser, but idempotent on `(org_id, user_id)`: an existing row is
77
- * promoted to the requested status rather than refused. `created` says
78
- * whether a row appeared, so `student.created` fires exactly once. */
79
- ensureOrgUser(input: CreateOrgUserInput): Promise<{
80
- orgUser: OrgUser;
81
- created: boolean;
82
- }>;
82
+ updateOrgUser(orgId: string, id: string, patch: UpdateOrgUserInput): Promise<OrgUser | null>;
83
83
  /** Every org this person is a student in — the scope of `student.linked`. */
84
84
  findStudentOrgUsers(userId: string): Promise<OrgUser[]>;
85
85
  /** Kills the org's pending invite for an address, if any. */
86
- cancelPendingInvite(orgId: string, email: string): Promise<Invite | null>;
87
86
  deleteOrgUser(orgId: string, id: string): Promise<OrgUser | null>;
88
87
  }
89
88
  /** A member row enriched with the ids needed to drive writes. */
@@ -1,23 +1,19 @@
1
1
  import { Role } from "../types/schemas/organizations.js";
2
2
  import "../types/index.js";
3
3
  //#region src/organizations/roles.d.ts
4
- declare const ROLES: readonly ["owner", "admin", "instructor", "student"];
5
- /**
6
- * The roles that operate the back office. `student` belongs to the org but
7
- * never appears on a staff surface, so the member-management types are typed
8
- * with this narrower union — the exclusion is a compiler guarantee, not a
9
- * convention a query has to remember.
10
- */
11
- type StaffRole = Exclude<Role, 'student'>;
4
+ declare const ROLES: readonly ["owner", "admin", "instructor", "moderator", "student"];
5
+ type StaffRole = 'owner' | 'admin' | 'instructor';
12
6
  declare const STAFF_ROLES: readonly ["owner", "admin", "instructor"];
7
+ type TeamRole = Exclude<Role, 'student'>;
13
8
  declare function isRole(value: string): value is Role;
14
9
  declare function isStaffRole(value: string): value is StaffRole;
15
10
  declare function parseRole(value: string): Role;
16
- type Permission = 'manage_billing' | 'manage_org_settings' | 'manage_users' | 'create_course' | 'edit_assigned_course' | 'view_student_progress' | 'consume_content';
11
+ type Permission = 'manage_billing' | 'manage_org_settings' | 'manage_users' | 'create_course' | 'edit_assigned_course' | 'view_student_progress' | 'moderate' | 'consume_content';
17
12
  type Capability = true | 'assigned' | 'enrolled';
18
13
  declare function capability(role: Role, permission: Permission): Capability | false;
14
+ declare function canModerate(role: Role): boolean;
19
15
  declare function normalizeRole(raw: string): Role;
20
16
  declare const STUDENT_ROLE = "student";
21
17
  //#endregion
22
- export { Capability, Permission, ROLES, type Role, STAFF_ROLES, STUDENT_ROLE, StaffRole, capability, isRole, isStaffRole, normalizeRole, parseRole };
18
+ export { Capability, Permission, ROLES, type Role, STAFF_ROLES, STUDENT_ROLE, StaffRole, TeamRole, canModerate, capability, isRole, isStaffRole, normalizeRole, parseRole };
23
19
  //# sourceMappingURL=roles.d.ts.map
@@ -3,6 +3,7 @@ const ROLES = [
3
3
  "owner",
4
4
  "admin",
5
5
  "instructor",
6
+ "moderator",
6
7
  "student"
7
8
  ];
8
9
  const STAFF_ROLES = [
@@ -27,28 +28,39 @@ const MATRIX = {
27
28
  manage_users: true,
28
29
  create_course: true,
29
30
  edit_assigned_course: true,
30
- view_student_progress: true
31
+ view_student_progress: true,
32
+ moderate: true
31
33
  },
32
34
  admin: {
33
35
  manage_org_settings: true,
34
36
  manage_users: true,
35
37
  create_course: true,
36
38
  edit_assigned_course: true,
37
- view_student_progress: true
39
+ view_student_progress: true,
40
+ moderate: true
38
41
  },
39
42
  instructor: {
40
43
  edit_assigned_course: "assigned",
41
- view_student_progress: "assigned"
44
+ view_student_progress: "assigned",
45
+ moderate: true
46
+ },
47
+ moderator: {
48
+ moderate: true,
49
+ consume_content: "enrolled"
42
50
  },
43
51
  student: { consume_content: "enrolled" }
44
52
  };
45
53
  function capability(role, permission) {
46
54
  return MATRIX[role][permission] ?? false;
47
55
  }
56
+ function canModerate(role) {
57
+ return capability(role, "moderate") === true;
58
+ }
48
59
  const RANK = {
49
- owner: 3,
50
- admin: 2,
51
- instructor: 1,
60
+ owner: 4,
61
+ admin: 3,
62
+ instructor: 2,
63
+ moderator: 1,
52
64
  student: 0
53
65
  };
54
66
  function normalizeRole(raw) {
@@ -65,6 +77,6 @@ function normalizeRole(raw) {
65
77
  }
66
78
  const STUDENT_ROLE = "student";
67
79
  //#endregion
68
- export { ROLES, STAFF_ROLES, STUDENT_ROLE, capability, isRole, isStaffRole, normalizeRole, parseRole };
80
+ export { ROLES, STAFF_ROLES, STUDENT_ROLE, canModerate, capability, isRole, isStaffRole, normalizeRole, parseRole };
69
81
 
70
82
  //# sourceMappingURL=roles.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"roles.js","names":[],"sources":["../../src/organizations/roles.ts"],"sourcesContent":["// organizations context — roles and the authorization matrix.\n// Defined in code (no DB enum). The DB stores role as text; the domain narrows\n// it to Role and answers authorization questions here. The Role type itself is\n// owned by @headless-lms/core/types; RANK/MATRIX below are Record<Role, …>, so the\n// compiler forces this file to cover every role the published type declares.\nimport type { Role } from '../types/index.js';\n\nexport type { Role };\nexport const ROLES = [\n 'owner',\n 'admin',\n 'instructor',\n 'student',\n] as const satisfies readonly Role[];\n\n/**\n * The roles that operate the back office. `student` belongs to the org but\n * never appears on a staff surface, so the member-management types are typed\n * with this narrower union — the exclusion is a compiler guarantee, not a\n * convention a query has to remember.\n */\nexport type StaffRole = Exclude<Role, 'student'>;\nexport const STAFF_ROLES = ['owner', 'admin', 'instructor'] as const satisfies readonly StaffRole[];\n\nexport function isRole(value: string): value is Role {\n return (ROLES as readonly string[]).includes(value);\n}\n\nexport function isStaffRole(value: string): value is StaffRole {\n return (STAFF_ROLES as readonly string[]).includes(value);\n}\n\nexport function parseRole(value: string): Role {\n if (!isRole(value)) {\n throw new Error(`unknown role: ${value}`);\n }\n return value;\n}\n\nexport type Permission =\n | 'manage_billing'\n | 'manage_org_settings'\n | 'manage_users'\n | 'create_course'\n | 'edit_assigned_course'\n | 'view_student_progress'\n | 'consume_content';\n\n// Unconditional (true), course-scoped (\"assigned\"), or enrollment-scoped\n// (\"enrolled\" — access owned by entitlements). Absent ⇒ denied.\n//\n// \"assigned\" is a granted-but-narrowed answer: the role holds the permission,\n// but only over some subset of courses. Nothing narrows it today — there is no\n// course-assignment store — so callers must decide what a narrowed grant means\n// for them. `authorize` treats it as denied; `tools.ts` treats it as granted.\nexport type Capability = true | 'assigned' | 'enrolled';\n\nconst MATRIX: Record<Role, Partial<Record<Permission, Capability>>> = {\n owner: {\n manage_billing: true,\n manage_org_settings: true,\n manage_users: true,\n create_course: true,\n edit_assigned_course: true,\n view_student_progress: true,\n },\n admin: {\n manage_org_settings: true,\n manage_users: true,\n create_course: true,\n edit_assigned_course: true,\n view_student_progress: true,\n },\n instructor: {\n edit_assigned_course: 'assigned',\n view_student_progress: 'assigned',\n },\n // A learner belongs to the org to consume content they hold an\n // entitlement for. No management capability of any kind.\n student: {\n consume_content: 'enrolled',\n },\n};\n\nexport function capability(role: Role, permission: Permission): Capability | false {\n return MATRIX[role][permission] ?? false;\n}\n\n// Better Auth's role model is broader than ours: it keeps a built-in `member`\n// role and allows comma-joined multi-role strings. Normalize any incoming role\n// string to a single domain Role before persisting: `member` -> `instructor`, a\n// multi-role string -> its highest-privilege known role, unknown -> `instructor`.\nconst RANK: Record<Role, number> = { owner: 3, admin: 2, instructor: 1, student: 0 };\n\nexport function normalizeRole(raw: string): Role {\n let best: Role = 'instructor';\n let bestRank = -1;\n for (const token of raw.split(',').map((t) => t.trim())) {\n const r: Role | null = isRole(token) ? token : token === 'member' ? 'instructor' : null;\n if (r && RANK[r] > bestRank) {\n best = r;\n bestRank = RANK[r];\n }\n }\n return best;\n}\n\n// Invites carry either a staff Role or this marker: the invited account is\n// a portal student, not an org member. Owned by the organizations domain — the\n// invite provider adapter reuses it to branch links, emails, and grants.\nexport const STUDENT_ROLE = 'student';\n"],"mappings":";AAQA,MAAa,QAAQ;CACnB;CACA;CACA;CACA;AACF;AASA,MAAa,cAAc;CAAC;CAAS;CAAS;AAAY;AAE1D,SAAgB,OAAO,OAA8B;CACnD,OAAQ,MAA4B,SAAS,KAAK;AACpD;AAEA,SAAgB,YAAY,OAAmC;CAC7D,OAAQ,YAAkC,SAAS,KAAK;AAC1D;AAEA,SAAgB,UAAU,OAAqB;CAC7C,IAAI,CAAC,OAAO,KAAK,GACf,MAAM,IAAI,MAAM,iBAAiB,OAAO;CAE1C,OAAO;AACT;AAoBA,MAAM,SAAgE;CACpE,OAAO;EACL,gBAAgB;EAChB,qBAAqB;EACrB,cAAc;EACd,eAAe;EACf,sBAAsB;EACtB,uBAAuB;CACzB;CACA,OAAO;EACL,qBAAqB;EACrB,cAAc;EACd,eAAe;EACf,sBAAsB;EACtB,uBAAuB;CACzB;CACA,YAAY;EACV,sBAAsB;EACtB,uBAAuB;CACzB;CAGA,SAAS,EACP,iBAAiB,WACnB;AACF;AAEA,SAAgB,WAAW,MAAY,YAA4C;CACjF,OAAO,OAAO,KAAK,CAAC,eAAe;AACrC;AAMA,MAAM,OAA6B;CAAE,OAAO;CAAG,OAAO;CAAG,YAAY;CAAG,SAAS;AAAE;AAEnF,SAAgB,cAAc,KAAmB;CAC/C,IAAI,OAAa;CACjB,IAAI,WAAW;CACf,KAAK,MAAM,SAAS,IAAI,MAAM,GAAG,CAAC,CAAC,KAAK,MAAM,EAAE,KAAK,CAAC,GAAG;EACvD,MAAM,IAAiB,OAAO,KAAK,IAAI,QAAQ,UAAU,WAAW,eAAe;EACnF,IAAI,KAAK,KAAK,KAAK,UAAU;GAC3B,OAAO;GACP,WAAW,KAAK;EAClB;CACF;CACA,OAAO;AACT;AAKA,MAAa,eAAe"}
1
+ {"version":3,"file":"roles.js","names":[],"sources":["../../src/organizations/roles.ts"],"sourcesContent":["// organizations context — roles and the authorization matrix.\n// Defined in code (no DB enum). The DB stores role as text; the domain narrows\n// it to Role and answers authorization questions here. The Role type itself is\n// owned by @headless-lms/core/types; RANK/MATRIX below are Record<Role, …>, so the\n// compiler forces this file to cover every role the published type declares.\nimport type { Role } from '../types/index.js';\n\nexport type { Role };\nexport const ROLES = [\n 'owner',\n 'admin',\n 'instructor',\n 'moderator',\n 'student',\n] as const satisfies readonly Role[];\n\nexport type StaffRole = 'owner' | 'admin' | 'instructor';\nexport const STAFF_ROLES = ['owner', 'admin', 'instructor'] as const satisfies readonly StaffRole[];\n\nexport type TeamRole = Exclude<Role, 'student'>;\n\nexport function isRole(value: string): value is Role {\n return (ROLES as readonly string[]).includes(value);\n}\n\nexport function isStaffRole(value: string): value is StaffRole {\n return (STAFF_ROLES as readonly string[]).includes(value);\n}\n\nexport function parseRole(value: string): Role {\n if (!isRole(value)) {\n throw new Error(`unknown role: ${value}`);\n }\n return value;\n}\n\nexport type Permission =\n | 'manage_billing'\n | 'manage_org_settings'\n | 'manage_users'\n | 'create_course'\n | 'edit_assigned_course'\n | 'view_student_progress'\n | 'moderate'\n | 'consume_content';\n\n// Unconditional (true), course-scoped (\"assigned\"), or enrollment-scoped\n// (\"enrolled\" — access owned by entitlements). Absent ⇒ denied.\n//\n// \"assigned\" is a granted-but-narrowed answer: the role holds the permission,\n// but only over some subset of courses. Nothing narrows it today — there is no\n// course-assignment store — so callers must decide what a narrowed grant means\n// for them. `authorize` treats it as denied; `tools.ts` treats it as granted.\nexport type Capability = true | 'assigned' | 'enrolled';\n\nconst MATRIX: Record<Role, Partial<Record<Permission, Capability>>> = {\n owner: {\n manage_billing: true,\n manage_org_settings: true,\n manage_users: true,\n create_course: true,\n edit_assigned_course: true,\n view_student_progress: true,\n moderate: true,\n },\n admin: {\n manage_org_settings: true,\n manage_users: true,\n create_course: true,\n edit_assigned_course: true,\n view_student_progress: true,\n moderate: true,\n },\n instructor: {\n edit_assigned_course: 'assigned',\n view_student_progress: 'assigned',\n moderate: true,\n },\n moderator: {\n moderate: true,\n consume_content: 'enrolled',\n },\n student: {\n consume_content: 'enrolled',\n },\n};\n\nexport function capability(role: Role, permission: Permission): Capability | false {\n return MATRIX[role][permission] ?? false;\n}\n\nexport function canModerate(role: Role): boolean {\n return capability(role, 'moderate') === true;\n}\n\n// Better Auth's role model is broader than ours: it keeps a built-in `member`\n// role and allows comma-joined multi-role strings. Normalize any incoming role\n// string to a single domain Role before persisting: `member` -> `instructor`, a\n// multi-role string -> its highest-privilege known role, unknown -> `instructor`.\nconst RANK: Record<Role, number> = { owner: 4, admin: 3, instructor: 2, moderator: 1, student: 0 };\n\nexport function normalizeRole(raw: string): Role {\n let best: Role = 'instructor';\n let bestRank = -1;\n for (const token of raw.split(',').map((t) => t.trim())) {\n const r: Role | null = isRole(token) ? token : token === 'member' ? 'instructor' : null;\n if (r && RANK[r] > bestRank) {\n best = r;\n bestRank = RANK[r];\n }\n }\n return best;\n}\n\n// Invites carry either a staff Role or this marker: the invited account is\n// a portal student, not an org member. Owned by the organizations domain — the\n// invite provider adapter reuses it to branch links, emails, and grants.\nexport const STUDENT_ROLE = 'student';\n"],"mappings":";AAQA,MAAa,QAAQ;CACnB;CACA;CACA;CACA;CACA;AACF;AAGA,MAAa,cAAc;CAAC;CAAS;CAAS;AAAY;AAI1D,SAAgB,OAAO,OAA8B;CACnD,OAAQ,MAA4B,SAAS,KAAK;AACpD;AAEA,SAAgB,YAAY,OAAmC;CAC7D,OAAQ,YAAkC,SAAS,KAAK;AAC1D;AAEA,SAAgB,UAAU,OAAqB;CAC7C,IAAI,CAAC,OAAO,KAAK,GACf,MAAM,IAAI,MAAM,iBAAiB,OAAO;CAE1C,OAAO;AACT;AAqBA,MAAM,SAAgE;CACpE,OAAO;EACL,gBAAgB;EAChB,qBAAqB;EACrB,cAAc;EACd,eAAe;EACf,sBAAsB;EACtB,uBAAuB;EACvB,UAAU;CACZ;CACA,OAAO;EACL,qBAAqB;EACrB,cAAc;EACd,eAAe;EACf,sBAAsB;EACtB,uBAAuB;EACvB,UAAU;CACZ;CACA,YAAY;EACV,sBAAsB;EACtB,uBAAuB;EACvB,UAAU;CACZ;CACA,WAAW;EACT,UAAU;EACV,iBAAiB;CACnB;CACA,SAAS,EACP,iBAAiB,WACnB;AACF;AAEA,SAAgB,WAAW,MAAY,YAA4C;CACjF,OAAO,OAAO,KAAK,CAAC,eAAe;AACrC;AAEA,SAAgB,YAAY,MAAqB;CAC/C,OAAO,WAAW,MAAM,UAAU,MAAM;AAC1C;AAMA,MAAM,OAA6B;CAAE,OAAO;CAAG,OAAO;CAAG,YAAY;CAAG,WAAW;CAAG,SAAS;AAAE;AAEjG,SAAgB,cAAc,KAAmB;CAC/C,IAAI,OAAa;CACjB,IAAI,WAAW;CACf,KAAK,MAAM,SAAS,IAAI,MAAM,GAAG,CAAC,CAAC,KAAK,MAAM,EAAE,KAAK,CAAC,GAAG;EACvD,MAAM,IAAiB,OAAO,KAAK,IAAI,QAAQ,UAAU,WAAW,eAAe;EACnF,IAAI,KAAK,KAAK,KAAK,UAAU;GAC3B,OAAO;GACP,WAAW,KAAK;EAClB;CACF;CACA,OAAO;AACT;AAKA,MAAa,eAAe"}
@@ -1,14 +1,15 @@
1
1
  import { Logger } from "../types/ports.js";
2
- import { AcceptInviteInput, AddOrgUserInput, CreateInviteInput, Invite, OrgUser, Organization, Role } from "../types/schemas/organizations.js";
2
+ import { AcceptInviteInput, AddOrgUserInput, CreateInviteInput, Invite, OrgUser, Organization, Role, UpdateOrgUserInput } from "../types/schemas/organizations.js";
3
3
  import "../shared/ports.js";
4
- import { Mailer } from "../shared/mailer.js";
5
- import { IdentityService } from "../identity/ports.js";
6
- import "../identity/index.js";
4
+ import { Actor } from "../shared/actor.js";
7
5
  import "./model.js";
8
6
  import "./roles.js";
9
- import { Member, MembersQuery, Page } from "./members.js";
10
- import { CreateOrganizationInput, ResendStudentInviteInput, UpdateOrganizationInput, UpdateStudentInput } from "./types.js";
11
- import { MemberWriteContext, MembersRepository, OrganizationService, OrganizationsRepository, OrganizationsUnitOfWork } from "./ports.js";
7
+ import { Member, MemberRoleChange, MembersQuery, Page } from "./members.js";
8
+ import { CreateOrganizationInput, ResendStudentInviteInput, UpdateOrganizationInput } from "./types.js";
9
+ import { IdentityService } from "../identity/ports.js";
10
+ import { Mailer } from "../shared/mailer.js";
11
+ import "../identity/index.js";
12
+ import { MemberWriteContext, MembersRepository, OrgAdmin, OrganizationService, OrganizationsRepository, OrganizationsUnitOfWork } from "./ports.js";
12
13
  //#region src/organizations/service.d.ts
13
14
  interface InviteUrls {
14
15
  studentPortalUrl: string;
@@ -19,6 +20,7 @@ type OrganizationServiceParams = {
19
20
  membersRepo: MembersRepository;
20
21
  people: IdentityService;
21
22
  uow: OrganizationsUnitOfWork;
23
+ orgAdmin: () => OrgAdmin;
22
24
  logger?: Logger;
23
25
  mailer?: Pick<Mailer, 'send'>;
24
26
  inviteUrls?: InviteUrls;
@@ -28,30 +30,42 @@ declare class OrganizationServiceImpl implements OrganizationService {
28
30
  private readonly membersRepo;
29
31
  private readonly people;
30
32
  private readonly uow;
33
+ private readonly orgAdmin;
31
34
  private readonly logger;
32
35
  private readonly mailer?;
33
36
  private readonly inviteUrls?;
37
+ private readonly memberWrites;
34
38
  constructor(params: OrganizationServiceParams);
35
39
  createOrganization(input: CreateOrganizationInput): Promise<Organization>;
36
40
  updateOrganization(id: string, input: UpdateOrganizationInput): Promise<Organization>;
37
41
  deleteOrganization(id: string): Promise<Organization>;
38
42
  addOrgUser({ orgId, userId, role }: AddOrgUserInput): Promise<OrgUser>;
39
- removeOrgUser(orgId: string, id: string): Promise<OrgUser>;
40
- createInvite(input: CreateInviteInput): Promise<Invite>;
41
- updateStudent(input: UpdateStudentInput): Promise<OrgUser>;
42
- resendStudentInvite(input: ResendStudentInviteInput): Promise<void>;
43
+ removeOrgUser(orgId: string, userId: string): Promise<OrgUser>;
44
+ updateOrgUser(orgId: string, userId: string, patch: UpdateOrgUserInput): Promise<OrgUser>;
45
+ createInvite(actor: Actor, input: CreateInviteInput): Promise<Invite>;
46
+ resendStudentInvite(actor: Actor, input: ResendStudentInviteInput): Promise<void>;
43
47
  peekInvite(token: string): Promise<Invite | null>;
44
48
  acceptInvite(input: AcceptInviteInput): Promise<OrgUser>;
49
+ private acceptStudentInvite;
50
+ private acceptStaffInvite;
51
+ getOrgUserById(orgId: string, id: string): Promise<OrgUser | null>;
45
52
  getOrgUser(orgId: string, userId: string): Promise<OrgUser | null>;
46
- deleteOrgUser(orgId: string, id: string): Promise<OrgUser>;
53
+ deleteOrgUser(orgId: string, actor: Actor, id: string): Promise<OrgUser>;
47
54
  getOrgUsersForUser(userId: string): Promise<OrgUser[]>;
48
55
  getById(id: string): Promise<Organization | null>;
49
56
  private sendInviteEmail;
50
57
  getByExternalId(externalId: string): Promise<Organization | null>;
51
58
  getBySlug(slug: string): Promise<Organization | null>;
59
+ staffOrgUserIds(orgId: string): Promise<string[]>;
52
60
  listMembers(orgId: string, query: MembersQuery): Promise<Page<Member>>;
53
- updateMemberRole(ctx: MemberWriteContext, id: string, role: Role): Promise<Member | null>;
54
- removeMember(_ctx: MemberWriteContext, _id: string): Promise<boolean>;
61
+ updateMemberRole(ctx: MemberWriteContext, actor: Actor, id: string, role: Role): Promise<MemberRoleChange | null>;
62
+ private demoteModerator;
63
+ private promoteStudent;
64
+ private asActor;
65
+ private memberWrite;
66
+ private assertManagesUsers;
67
+ private assertNotOwner;
68
+ removeMember(ctx: MemberWriteContext, actor: Actor, id: string): Promise<boolean>;
55
69
  }
56
70
  //#endregion
57
71
  export { InviteUrls, OrganizationServiceImpl, OrganizationServiceParams };