@headless-lms/core 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. package/dist/assets/events.d.ts +27 -21
  2. package/dist/assets/file-urls.d.ts +11 -7
  3. package/dist/assets/file-urls.js +14 -4
  4. package/dist/assets/file-urls.js.map +1 -1
  5. package/dist/assets/index.d.ts +4 -4
  6. package/dist/assets/index.js +3 -3
  7. package/dist/assets/ports.d.ts +34 -15
  8. package/dist/assets/service.d.ts +30 -14
  9. package/dist/assets/service.js +230 -56
  10. package/dist/assets/service.js.map +1 -1
  11. package/dist/automations/actions.js.map +1 -1
  12. package/dist/automations/catalog.js +69 -4
  13. package/dist/automations/catalog.js.map +1 -1
  14. package/dist/automations/community-derivations.js +16 -16
  15. package/dist/automations/community-derivations.js.map +1 -1
  16. package/dist/automations/community-mail.d.ts +34 -4
  17. package/dist/automations/community-mail.js +32 -4
  18. package/dist/automations/community-mail.js.map +1 -1
  19. package/dist/automations/event-permissions.d.ts +12 -0
  20. package/dist/automations/event-permissions.js +38 -0
  21. package/dist/automations/event-permissions.js.map +1 -0
  22. package/dist/automations/index.d.ts +2 -1
  23. package/dist/automations/index.js +2 -1
  24. package/dist/automations/notifications/applicable.d.ts +8 -0
  25. package/dist/automations/notifications/applicable.js +12 -0
  26. package/dist/automations/notifications/applicable.js.map +1 -0
  27. package/dist/automations/notifications/debounce.js +1 -1
  28. package/dist/automations/notifications/debounce.js.map +1 -1
  29. package/dist/automations/notifications/defaults.js +33 -3
  30. package/dist/automations/notifications/defaults.js.map +1 -1
  31. package/dist/automations/notifications/processor.js +3 -3
  32. package/dist/automations/notifications/processor.js.map +1 -1
  33. package/dist/automations/notifications/rules/content.js +60 -3
  34. package/dist/automations/notifications/rules/content.js.map +1 -1
  35. package/dist/automations/notifications/rules/discussion.js +29 -29
  36. package/dist/automations/notifications/rules/discussion.js.map +1 -1
  37. package/dist/automations/notifications/rules/entitlements.js +36 -9
  38. package/dist/automations/notifications/rules/entitlements.js.map +1 -1
  39. package/dist/automations/notifications/rules/events.d.ts +9 -0
  40. package/dist/automations/notifications/rules/events.js +27 -0
  41. package/dist/automations/notifications/rules/events.js.map +1 -0
  42. package/dist/automations/notifications/rules/organizations.js +17 -10
  43. package/dist/automations/notifications/rules/organizations.js.map +1 -1
  44. package/dist/automations/notifications/rules/recipients.js +0 -1
  45. package/dist/automations/notifications/rules/recipients.js.map +1 -1
  46. package/dist/automations/notifications/service.d.ts +8 -6
  47. package/dist/automations/notifications/service.js +13 -13
  48. package/dist/automations/notifications/service.js.map +1 -1
  49. package/dist/automations/notifications/types.js +94 -7
  50. package/dist/automations/notifications/types.js.map +1 -1
  51. package/dist/automations/ports.d.ts +30 -31
  52. package/dist/automations/service.d.ts +20 -17
  53. package/dist/automations/service.js +91 -62
  54. package/dist/automations/service.js.map +1 -1
  55. package/dist/content/event-sight.d.ts +11 -0
  56. package/dist/content/event-sight.js +11 -0
  57. package/dist/content/event-sight.js.map +1 -0
  58. package/dist/content/events.d.ts +413 -72
  59. package/dist/content/events.js +34 -1
  60. package/dist/content/events.js.map +1 -1
  61. package/dist/content/index.d.ts +6 -4
  62. package/dist/content/index.js +3 -1
  63. package/dist/content/jobs.d.ts +16 -0
  64. package/dist/content/jobs.js +37 -0
  65. package/dist/content/jobs.js.map +1 -0
  66. package/dist/content/model.d.ts +2 -2
  67. package/dist/content/notification-categories.d.ts +5 -4
  68. package/dist/content/notification-categories.js +15 -6
  69. package/dist/content/notification-categories.js.map +1 -1
  70. package/dist/content/ports.d.ts +113 -68
  71. package/dist/content/service.d.ts +99 -67
  72. package/dist/content/service.js +680 -139
  73. package/dist/content/service.js.map +1 -1
  74. package/dist/content/titles.d.ts +6 -3
  75. package/dist/content/titles.js +37 -24
  76. package/dist/content/titles.js.map +1 -1
  77. package/dist/content/types.d.ts +2 -2
  78. package/dist/discussion/authors.d.ts +4 -2
  79. package/dist/discussion/authors.js +9 -5
  80. package/dist/discussion/authors.js.map +1 -1
  81. package/dist/discussion/chats.d.ts +56 -37
  82. package/dist/discussion/chats.js +393 -161
  83. package/dist/discussion/chats.js.map +1 -1
  84. package/dist/discussion/events.d.ts +77 -39
  85. package/dist/discussion/events.js +41 -3
  86. package/dist/discussion/events.js.map +1 -1
  87. package/dist/discussion/index.d.ts +8 -5
  88. package/dist/discussion/index.js +5 -3
  89. package/dist/discussion/live.d.ts +106 -0
  90. package/dist/discussion/live.js +642 -0
  91. package/dist/discussion/live.js.map +1 -0
  92. package/dist/discussion/mentions.js +2 -2
  93. package/dist/discussion/mentions.js.map +1 -1
  94. package/dist/discussion/ports.d.ts +182 -100
  95. package/dist/discussion/ports.js.map +1 -1
  96. package/dist/discussion/posts.d.ts +45 -39
  97. package/dist/discussion/posts.js +268 -154
  98. package/dist/discussion/posts.js.map +1 -1
  99. package/dist/discussion/rules.d.ts +18 -0
  100. package/dist/discussion/rules.js +22 -0
  101. package/dist/discussion/rules.js.map +1 -0
  102. package/dist/discussion/service.d.ts +37 -31
  103. package/dist/discussion/service.js +252 -188
  104. package/dist/discussion/service.js.map +1 -1
  105. package/dist/discussion/subscriptions.d.ts +8 -8
  106. package/dist/discussion/subscriptions.js +29 -13
  107. package/dist/discussion/subscriptions.js.map +1 -1
  108. package/dist/discussion/titles.d.ts +4 -2
  109. package/dist/discussion/titles.js +27 -13
  110. package/dist/discussion/titles.js.map +1 -1
  111. package/dist/entitlements/events.d.ts +20 -20
  112. package/dist/entitlements/index.d.ts +4 -4
  113. package/dist/entitlements/index.js +2 -2
  114. package/dist/entitlements/model.d.ts +2 -2
  115. package/dist/entitlements/ports.d.ts +65 -39
  116. package/dist/entitlements/rules.d.ts +3 -6
  117. package/dist/entitlements/rules.js +5 -10
  118. package/dist/entitlements/rules.js.map +1 -1
  119. package/dist/entitlements/service.d.ts +40 -28
  120. package/dist/entitlements/service.js +370 -163
  121. package/dist/entitlements/service.js.map +1 -1
  122. package/dist/identity/devices.d.ts +3 -3
  123. package/dist/identity/devices.js +7 -5
  124. package/dist/identity/devices.js.map +1 -1
  125. package/dist/identity/events.d.ts +2 -2
  126. package/dist/identity/index.d.ts +2 -2
  127. package/dist/identity/index.js +2 -2
  128. package/dist/identity/notification-settings.d.ts +1 -1
  129. package/dist/identity/notification-settings.js +3 -2
  130. package/dist/identity/notification-settings.js.map +1 -1
  131. package/dist/identity/ports.d.ts +7 -7
  132. package/dist/identity/service.d.ts +8 -7
  133. package/dist/identity/service.js +17 -13
  134. package/dist/identity/service.js.map +1 -1
  135. package/dist/integrations/events.d.ts +3 -3
  136. package/dist/integrations/ports.d.ts +9 -8
  137. package/dist/integrations/service.d.ts +9 -8
  138. package/dist/integrations/service.js +33 -16
  139. package/dist/integrations/service.js.map +1 -1
  140. package/dist/organizations/default-roles.d.ts +22 -0
  141. package/dist/organizations/default-roles.js +89 -0
  142. package/dist/organizations/default-roles.js.map +1 -0
  143. package/dist/organizations/effective-permissions.d.ts +45 -0
  144. package/dist/organizations/effective-permissions.js +113 -0
  145. package/dist/organizations/effective-permissions.js.map +1 -0
  146. package/dist/organizations/events.d.ts +320 -181
  147. package/dist/organizations/events.js +90 -18
  148. package/dist/organizations/events.js.map +1 -1
  149. package/dist/organizations/index.d.ts +14 -10
  150. package/dist/organizations/index.js +7 -3
  151. package/dist/organizations/members.d.ts +66 -8
  152. package/dist/organizations/members.js.map +1 -1
  153. package/dist/organizations/notification-catalog.d.ts +31 -0
  154. package/dist/organizations/notification-catalog.js +32 -0
  155. package/dist/organizations/notification-catalog.js.map +1 -0
  156. package/dist/organizations/notification-categories.d.ts +6 -4
  157. package/dist/organizations/notification-categories.js +25 -10
  158. package/dist/organizations/notification-categories.js.map +1 -1
  159. package/dist/organizations/org-roles.d.ts +33 -0
  160. package/dist/organizations/org-roles.js +49 -0
  161. package/dist/organizations/org-roles.js.map +1 -0
  162. package/dist/organizations/permissions.d.ts +311 -0
  163. package/dist/organizations/permissions.js +327 -0
  164. package/dist/organizations/permissions.js.map +1 -0
  165. package/dist/organizations/ports.d.ts +184 -65
  166. package/dist/organizations/service.d.ts +102 -35
  167. package/dist/organizations/service.js +1245 -328
  168. package/dist/organizations/service.js.map +1 -1
  169. package/dist/organizations/testing.d.ts +18 -0
  170. package/dist/organizations/testing.js +37 -0
  171. package/dist/organizations/testing.js.map +1 -0
  172. package/dist/organizations/types.d.ts +5 -16
  173. package/dist/progress/events.d.ts +14 -14
  174. package/dist/progress/index.d.ts +2 -2
  175. package/dist/progress/ports.d.ts +11 -3
  176. package/dist/progress/service.d.ts +6 -5
  177. package/dist/progress/service.js +23 -8
  178. package/dist/progress/service.js.map +1 -1
  179. package/dist/reporting/admin/index.d.ts +6 -4
  180. package/dist/reporting/admin/model.d.ts +36 -3
  181. package/dist/reporting/admin/ports.d.ts +39 -14
  182. package/dist/reporting/admin/service.d.ts +32 -11
  183. package/dist/reporting/admin/service.js +102 -18
  184. package/dist/reporting/admin/service.js.map +1 -1
  185. package/dist/reporting/events/index.d.ts +27 -0
  186. package/dist/reporting/events/index.js +20 -0
  187. package/dist/reporting/events/index.js.map +1 -0
  188. package/dist/reporting/member/calendar.d.ts +11 -0
  189. package/dist/reporting/member/calendar.js +75 -0
  190. package/dist/reporting/member/calendar.js.map +1 -0
  191. package/dist/reporting/member/index.d.ts +3 -2
  192. package/dist/reporting/member/model.d.ts +26 -5
  193. package/dist/reporting/member/ports.d.ts +48 -29
  194. package/dist/reporting/member/service.d.ts +56 -29
  195. package/dist/reporting/member/service.js +328 -113
  196. package/dist/reporting/member/service.js.map +1 -1
  197. package/dist/shared/actor.d.ts +22 -3
  198. package/dist/shared/errors.d.ts +2 -1
  199. package/dist/shared/errors.js +3 -1
  200. package/dist/shared/errors.js.map +1 -1
  201. package/dist/shared/id.d.ts +4 -0
  202. package/dist/shared/id.js +4 -0
  203. package/dist/shared/id.js.map +1 -1
  204. package/dist/shared/logger.d.ts +2 -1
  205. package/dist/shared/logger.js +10 -1
  206. package/dist/shared/logger.js.map +1 -1
  207. package/dist/shared/ports.d.ts +25 -5
  208. package/dist/types/content.d.ts +2 -2
  209. package/dist/types/discussion.d.ts +4 -5
  210. package/dist/types/email-templates.d.ts +22 -3
  211. package/dist/types/entitlements.d.ts +2 -2
  212. package/dist/types/identity.d.ts +13 -16
  213. package/dist/types/index.d.ts +9 -9
  214. package/dist/types/index.js +2 -1
  215. package/dist/types/notifications.d.ts +4 -4
  216. package/dist/types/organizations.d.ts +3 -2
  217. package/dist/types/ports.d.ts +8 -1
  218. package/dist/types/ports.js +7 -1
  219. package/dist/types/ports.js.map +1 -0
  220. package/dist/types/schemas/assets.d.ts +5 -5
  221. package/dist/types/schemas/assets.js +4 -3
  222. package/dist/types/schemas/assets.js.map +1 -1
  223. package/dist/types/schemas/content.d.ts +537 -13
  224. package/dist/types/schemas/content.js +201 -7
  225. package/dist/types/schemas/content.js.map +1 -1
  226. package/dist/types/schemas/discussion.d.ts +282 -59
  227. package/dist/types/schemas/discussion.js +77 -3
  228. package/dist/types/schemas/discussion.js.map +1 -1
  229. package/dist/types/schemas/entitlements.d.ts +14 -1
  230. package/dist/types/schemas/entitlements.js +11 -4
  231. package/dist/types/schemas/entitlements.js.map +1 -1
  232. package/dist/types/schemas/index.d.ts +5 -5
  233. package/dist/types/schemas/index.js +5 -5
  234. package/dist/types/schemas/notifications.d.ts +13 -12
  235. package/dist/types/schemas/notifications.js +3 -2
  236. package/dist/types/schemas/notifications.js.map +1 -1
  237. package/dist/types/schemas/organizations.d.ts +96 -60
  238. package/dist/types/schemas/organizations.js +78 -27
  239. package/dist/types/schemas/organizations.js.map +1 -1
  240. package/dist/types/schemas/progress.d.ts +3 -4
  241. package/dist/types/schemas/progress.js +0 -1
  242. package/dist/types/schemas/progress.js.map +1 -1
  243. package/package.json +1 -1
  244. package/dist/organizations/access-scopes.d.ts +0 -19
  245. package/dist/organizations/access-scopes.js +0 -89
  246. package/dist/organizations/access-scopes.js.map +0 -1
  247. package/dist/organizations/roles.d.ts +0 -19
  248. package/dist/organizations/roles.js +0 -82
  249. package/dist/organizations/roles.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"service.js","names":[],"sources":["../../src/organizations/service.ts"],"sourcesContent":["// organizations context — service implementation (inbound port).\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport type {\n MemberRecord,\n MembersRepository,\n MemberWriteContext,\n OrgAdmin,\n OrganizationService,\n OrganizationsRepository,\n OrganizationsUnitOfWork,\n} from './ports.js';\nimport type { Invite, Organization, OrgUser } from './model.js';\nimport { capability, type Role, STAFF_ROLES, STUDENT_ROLE } from './roles.js';\nimport {\n InviteError,\n type Member,\n type MemberRoleChange,\n type MembersQuery,\n OrganizationRuleError,\n type Page,\n} from './members.js';\nimport type {\n AcceptInviteInput,\n AddOrgUserInput,\n CreateInviteInput,\n CreateOrganizationInput,\n ResendStudentInviteInput,\n UpdateOrganizationInput,\n UpdateOrgUserInput,\n} from './types.js';\nimport { organizationEvents, type OrganizationEvent } from './events.js';\nimport type { Logger, NewDomainEvent } from '../shared/ports.js';\nimport { noopLogger } from '../shared/logger.js';\nimport type { Mailer } from '../shared/mailer.js';\nimport { generateInviteToken, hashInviteToken } from '../shared/invite-token.js';\nimport { ForbiddenError, NotFoundError } from '../shared/errors.js';\nimport type { Actor } from '../shared/actor.js';\nimport type { IdentityService } from '../identity/index.js';\nimport { genId } from '../shared/id.js';\n\nconst INVITE_TTL_MS = 7 * 24 * 60 * 60 * 1000;\n\ntype NewOrganizationEvent = NewDomainEvent<OrganizationEvent>;\n\ninterface MemberWrite {\n orgUserId: string;\n actorOrgUserId: string;\n demote: boolean;\n}\n\nexport interface InviteUrls {\n studentPortalUrl: string;\n adminAppUrl: string;\n}\n\nfunction toMember(r: MemberRecord): Member {\n return {\n id: r.id,\n firstName: r.firstName,\n lastName: r.lastName,\n email: r.email,\n image: r.image,\n role: r.role,\n status: r.status,\n joinedAt: r.joinedAt,\n invitedAt: r.invitedAt,\n };\n}\n\nexport type OrganizationServiceParams = {\n repo: OrganizationsRepository;\n membersRepo: MembersRepository;\n people: IdentityService;\n uow: OrganizationsUnitOfWork;\n orgAdmin: () => OrgAdmin;\n logger?: Logger;\n mailer?: Pick<Mailer, 'send'>;\n inviteUrls?: InviteUrls;\n};\n\nexport class OrganizationServiceImpl implements OrganizationService {\n private readonly repo: OrganizationsRepository;\n private readonly membersRepo: MembersRepository;\n private readonly people: IdentityService;\n private readonly uow: OrganizationsUnitOfWork;\n private readonly orgAdmin: () => OrgAdmin;\n private readonly logger: Logger;\n private readonly mailer?: Pick<Mailer, 'send'>;\n private readonly inviteUrls?: InviteUrls;\n private readonly memberWrites = new AsyncLocalStorage<MemberWrite>();\n\n constructor(params: OrganizationServiceParams) {\n this.repo = params.repo;\n this.membersRepo = params.membersRepo;\n this.people = params.people;\n this.uow = params.uow;\n this.orgAdmin = params.orgAdmin;\n this.logger = params.logger ?? noopLogger;\n this.mailer = params.mailer;\n this.inviteUrls = params.inviteUrls;\n }\n\n async createOrganization(input: CreateOrganizationInput): Promise<Organization> {\n const created = await this.uow.run(async ({ organizations, outbox }) => {\n const id = genId('organization');\n\n const created = await organizations.create({\n id,\n // The auth provider adopts this id for its own org row, so the org is\n // its own external id — a lookup by either id lands on the same row.\n externalId: input.externalId ?? id,\n name: input.name ?? `My Org`,\n slug: input.slug ?? '',\n ownerId: input.ownerId,\n });\n await outbox.append([\n organizationEvents.organizationCreated.make({\n orgId: created.id,\n data: created,\n }),\n ]);\n return created;\n });\n this.logger.info('organization created', { orgId: created.id, slug: created.slug });\n return created;\n }\n\n async updateOrganization(id: string, input: UpdateOrganizationInput): Promise<Organization> {\n const updatedOrg = await this.uow.run(async ({ organizations, outbox }) => {\n const updated = await organizations.update(id, {\n name: input.name,\n slug: input.slug,\n });\n if (!updated) {\n throw new NotFoundError('Organization', id);\n }\n await outbox.append([\n organizationEvents.organizationUpdated.make({ orgId: id, data: updated }),\n ]);\n return updated;\n });\n\n this.logger.info('organization updated', { updatedOrg });\n return updatedOrg!;\n }\n\n async deleteOrganization(id: string): Promise<Organization> {\n const deleted = await this.uow.run(async ({ organizations, outbox }) => {\n const deleted = await organizations.delete(id);\n\n await outbox.append([\n organizationEvents.organizationDeleted.make({\n orgId: deleted.id,\n data: deleted,\n }),\n ]);\n return deleted;\n });\n this.logger.info('organization deleted', { orgId: deleted.id });\n return deleted;\n }\n\n async addOrgUser({ orgId, userId, role }: AddOrgUserInput): Promise<OrgUser> {\n const existing = await this.repo.findOrgUser(orgId, userId);\n if (existing) {\n return this.updateOrgUser(orgId, userId, { role, status: 'active' });\n }\n const orgUser = await this.uow.run(async ({ organizations, outbox }) => {\n const created = await organizations.createOrgUser({ orgId, role, userId, status: 'active' });\n await outbox.append([organizationEvents.orgUserLinked.make({ orgId, data: created })]);\n return created;\n });\n this.logger.info('user added to organization', { orgId, userId, role });\n return orgUser;\n }\n\n async removeOrgUser(orgId: string, userId: string): Promise<OrgUser> {\n const deleted = await this.uow.run(async ({ organizations, outbox }) => {\n const orgUser = await organizations.findOrgUser(orgId, userId);\n if (!orgUser) {\n throw new NotFoundError('Member', userId);\n }\n const write = this.memberWrite(orgUser.id);\n if (write?.demote) {\n const demoted = await organizations.updateOrgUser(orgId, orgUser.id, {\n role: STUDENT_ROLE,\n });\n if (!demoted) {\n throw new NotFoundError('Member', orgUser.id);\n }\n if (orgUser.role !== STUDENT_ROLE) {\n await outbox.append([\n organizationEvents.orgUserUpdated.make({\n orgId,\n data: demoted,\n metadata: { actorOrgUserId: write.actorOrgUserId },\n }),\n ]);\n }\n return demoted;\n }\n if (orgUser.role === STUDENT_ROLE) {\n return orgUser;\n }\n const deletedUser = await organizations.deleteOrgUser(orgId, orgUser.id);\n if (!deletedUser) {\n throw new NotFoundError('Member', orgUser.id);\n }\n await outbox.append([\n organizationEvents.orgUserDeleted.make({\n orgId,\n data: deletedUser,\n }),\n ]);\n return deletedUser;\n });\n this.logger.info('org user removed', { orgId, userId, orgUserId: deleted.id });\n return deleted;\n }\n\n async updateOrgUser(orgId: string, userId: string, patch: UpdateOrgUserInput): Promise<OrgUser> {\n const updated = await this.uow.run(async ({ organizations, outbox }) => {\n const orgUser = await organizations.findOrgUser(orgId, userId);\n if (!orgUser) {\n throw new NotFoundError('Member', userId);\n }\n const next = await organizations.updateOrgUser(orgId, orgUser.id, patch);\n if (!next) {\n throw new NotFoundError('Member', orgUser.id);\n }\n if (patch.role !== undefined && patch.role !== orgUser.role) {\n const actorOrgUserId = this.memberWrite(orgUser.id)?.actorOrgUserId;\n await outbox.append([\n organizationEvents.orgUserUpdated.make({\n orgId,\n data: next,\n ...(actorOrgUserId !== undefined && { metadata: { actorOrgUserId } }),\n }),\n ]);\n }\n return next;\n });\n this.logger.info('org user updated', { orgId, userId, orgUserId: updated.id, patch });\n return updated;\n }\n\n async createInvite(actor: Actor, input: CreateInviteInput): Promise<Invite> {\n const { orgId, email, role, inviterUserId, firstName, lastName } = input;\n this.assertManagesUsers(actor, orgId);\n this.assertNotOwner(role, orgId);\n const sendEmail = input.sendEmail ?? true;\n const { token, tokenHash } = generateInviteToken();\n const expiresAt = new Date(Date.now() + INVITE_TTL_MS);\n\n // A student belongs to the org from the moment an admin adds them, not from\n // the moment they click a link — so the person exists before the invite\n // does. Staff are excluded: their org_users row is mirrored from the auth\n // provider's member record, which does not exist until they join.\n const person =\n role === STUDENT_ROLE\n ? ((await this.people.getUserByEmail(email)) ??\n (await this.people.createUser({\n email,\n ...(firstName !== undefined && { firstName }),\n ...(lastName !== undefined && { lastName }),\n })))\n : null;\n\n const invite = await this.uow.run(async ({ organizations, outbox }) => {\n const events: NewOrganizationEvent[] = [];\n if (person) {\n const orgUser = await organizations.createOrgUser({\n orgId,\n userId: person.id,\n role: STUDENT_ROLE,\n status: 'invited',\n });\n events.push(organizationEvents.studentCreated.make({ orgId, data: orgUser }));\n }\n const row = await organizations.upsertPendingInvite(orgId, {\n email,\n role,\n invitedBy: inviterUserId,\n tokenHash,\n expiresAt,\n });\n events.push(organizationEvents.inviteCreated.make({ orgId, data: row }));\n await outbox.append(events);\n return row;\n });\n\n if (sendEmail) {\n // TODO make this workflow durable\n try {\n await this.sendInviteEmail(invite, token);\n } catch (err) {\n // A failed email must not abort invite creation: the token is already\n // minted and recorded, so the admin can fix transport and resend.\n this.logger.error('failed to send invite email', {\n orgId,\n inviteId: invite.id,\n role: invite.role,\n err: err instanceof Error ? err : new Error(String(err)),\n });\n }\n }\n\n this.logger.info('invite created', { orgId, inviteId: invite.id, role, sendEmail });\n return invite;\n }\n\n async resendStudentInvite(actor: Actor, input: ResendStudentInviteInput): Promise<void> {\n const { orgId, orgUserId, inviterUserId } = input;\n this.assertManagesUsers(actor, orgId);\n const orgUser = await this.repo.findOrgUserById(orgId, orgUserId);\n if (!orgUser || orgUser.role !== STUDENT_ROLE) {\n throw new NotFoundError('Student', orgUserId);\n }\n if (orgUser.status === 'active') {\n throw new OrganizationRuleError('That student has already accepted their invitation');\n }\n const person = await this.people.getUserById(orgUser.userId);\n if (!person) {\n throw new NotFoundError('User', orgUser.userId);\n }\n const pending = await this.repo.findPendingInvite(orgId, person.email);\n if (!pending) {\n throw new NotFoundError('Invite', person.email);\n }\n const { token, tokenHash } = generateInviteToken();\n const expiresAt = new Date(Date.now() + INVITE_TTL_MS);\n const invite = await this.repo.setInviteToken(orgId, pending.id, tokenHash, expiresAt);\n if (!invite) {\n throw new NotFoundError('Invite', pending.id);\n }\n await this.sendInviteEmail(invite, token);\n this.logger.info('student invite resent', {\n orgId,\n orgUserId,\n inviteId: invite.id,\n inviterUserId,\n });\n }\n\n async peekInvite(token: string): Promise<Invite | null> {\n const invite = await this.repo.findInviteByTokenHash(hashInviteToken(token));\n if (!invite || invite.status !== 'pending') {\n return null;\n }\n if (invite.expiresAt && invite.expiresAt < new Date()) {\n return null;\n }\n return invite;\n }\n\n async acceptInvite(input: AcceptInviteInput): Promise<OrgUser> {\n const invite = await this.peekInvite(input.token);\n if (!invite) {\n this.logger.warn('invite accept refused: token invalid or expired');\n throw new InviteError('invalid invite');\n }\n if (invite.email.toLowerCase() !== input.email.toLowerCase()) {\n this.logger.warn('invite accept refused: email mismatch', {\n orgId: invite.orgId,\n inviteId: invite.id,\n });\n throw new InviteError('emails must match');\n }\n const org = await this.repo.findById(invite.orgId);\n if (!org) {\n throw new InviteError('Org not found');\n }\n\n const orgUser =\n invite.role === STUDENT_ROLE\n ? await this.acceptStudentInvite(invite, input.userId)\n : await this.acceptStaffInvite(invite, org, input.userId);\n this.logger.info('invite accepted', { orgId: invite.orgId, userId: input.userId });\n return orgUser;\n }\n\n private async acceptStudentInvite(invite: Invite, userId: string): Promise<OrgUser> {\n return this.uow.run(async ({ organizations, outbox }) => {\n const existing = await organizations.findOrgUser(invite.orgId, userId);\n if (!existing) {\n throw new NotFoundError('Member', userId);\n }\n const activated = await organizations.updateOrgUser(invite.orgId, existing.id, {\n status: 'active',\n });\n if (!activated) {\n throw new NotFoundError('Member', existing.id);\n }\n const acceptedInvite = await organizations.setInviteStatus(\n invite.orgId,\n invite.id,\n 'accepted',\n );\n if (!acceptedInvite) {\n throw new NotFoundError('Invite', invite.id);\n }\n await outbox.append([\n organizationEvents.inviteAccepted.make({ orgId: invite.orgId, data: acceptedInvite }),\n organizationEvents.studentLinked.make({ orgId: invite.orgId, data: activated }),\n ]);\n return activated;\n });\n }\n\n private async acceptStaffInvite(\n invite: Invite,\n org: Organization,\n userId: string,\n ): Promise<OrgUser> {\n const person = await this.people.getUserById(userId);\n if (!person?.externalId) {\n throw new NotFoundError('User', userId);\n }\n await this.orgAdmin().grantMembership(org.externalId ?? org.id, person.externalId, invite.role);\n return this.uow.run(async ({ organizations, outbox }) => {\n const orgUser = await organizations.findOrgUser(invite.orgId, userId);\n if (!orgUser) {\n throw new NotFoundError('Member', userId);\n }\n const acceptedInvite = await organizations.setInviteStatus(\n invite.orgId,\n invite.id,\n 'accepted',\n );\n if (!acceptedInvite) {\n throw new NotFoundError('Invite', invite.id);\n }\n await outbox.append([\n organizationEvents.inviteAccepted.make({ orgId: invite.orgId, data: acceptedInvite }),\n ]);\n return orgUser;\n });\n }\n\n // By identity USER id — `(org_id, user_id)` is unique. Every caller (scope\n // resolution, the MCP principal, the member routes) asks \"what is this person\n // in this org\", never \"what is this org_users row\".\n getOrgUserById(orgId: string, id: string): Promise<OrgUser | null> {\n return this.repo.findOrgUserById(orgId, id);\n }\n\n async getOrgUsersByIds(orgId: string, ids: string[]): Promise<OrgUser[]> {\n const unique = [...new Set(ids)];\n return unique.length > 0 ? this.repo.findOrgUsersByIds(orgId, unique) : [];\n }\n\n async getOrgUser(orgId: string, userId: string): Promise<OrgUser | null> {\n return this.repo.findOrgUser(orgId, userId);\n }\n\n async deleteOrgUser(orgId: string, actor: Actor, id: string): Promise<OrgUser> {\n this.assertManagesUsers(actor, orgId);\n const deleted = await this.uow.run(async ({ organizations, outbox }) => {\n const orgUser = await organizations.findOrgUserById(orgId, id);\n if (!orgUser || orgUser.role !== STUDENT_ROLE) {\n throw new NotFoundError('Student', id);\n }\n const deletedUser = await organizations.deleteOrgUser(orgId, id);\n if (!deletedUser) {\n throw new NotFoundError('Student', id);\n }\n await outbox.append([organizationEvents.studentDeleted.make({ orgId, data: orgUser })]);\n return deletedUser;\n });\n this.logger.info('org user deleted', { orgId, orgUserId: id });\n return deleted;\n }\n\n async getOrgUsersForUser(userId: string): Promise<OrgUser[]> {\n return this.repo.findOrgUsersByUser(userId);\n }\n\n async getById(id: string): Promise<Organization | null> {\n return this.repo.findById(id);\n }\n\n orgIds(): Promise<string[]> {\n return this.repo.listIds();\n }\n\n private async sendInviteEmail(invite: Invite, token: string): Promise<void> {\n if (!this.mailer || !this.inviteUrls) {\n throw new Error('invite delivery is not configured (mailer / invite urls missing)');\n }\n const { email, role } = invite;\n const isStudent = role === STUDENT_ROLE;\n const base = isStudent\n ? `${this.inviteUrls.studentPortalUrl}/welcome`\n : `${this.inviteUrls.adminAppUrl}/invite`;\n // The portal resolves the token to the invited email, so the link carries\n // only the token. The admin app still reads it from the query.\n const query = isStudent\n ? new URLSearchParams({ token })\n : new URLSearchParams({ token, email });\n const inviteUrl = `${base}?${query.toString()}`;\n if (role === STUDENT_ROLE) {\n await this.mailer.send(email, 'studentInvite', { inviteUrl, studentName: email });\n } else {\n await this.mailer.send(email, 'memberInvite', {\n inviteUrl,\n inviterName: 'Your team',\n role,\n });\n }\n }\n\n async getByExternalId(externalId: string): Promise<Organization | null> {\n return this.repo.findByExternalId(externalId);\n }\n\n async getBySlug(slug: string): Promise<Organization | null> {\n return this.repo.findBySlug(slug);\n }\n\n staffOrgUserIds(orgId: string): Promise<string[]> {\n return this.repo.findOrgUserIdsByRole(orgId, [...STAFF_ROLES]);\n }\n\n listMembers(orgId: string, query: MembersQuery): Promise<Page<Member>> {\n return this.membersRepo.list(orgId, query);\n }\n\n async updateMemberRole(\n ctx: MemberWriteContext,\n actor: Actor,\n id: string,\n role: Role,\n ): Promise<MemberRoleChange | null> {\n this.assertManagesUsers(actor, ctx.orgId);\n this.assertNotOwner(role, ctx.orgId);\n const member = await this.membersRepo.findById(ctx.orgId, id);\n if (!member) {\n return this.promoteStudent(ctx, actor, id, role);\n }\n if (member.role === 'owner') {\n this.logger.warn('role change rejected: owner role immutable', {\n orgId: ctx.orgId,\n memberId: id,\n });\n throw new OrganizationRuleError('The owner role cannot be reassigned');\n }\n if (member.kind !== 'member' || !member.userExternalId) {\n this.logger.warn('role change rejected: not an active member', {\n orgId: ctx.orgId,\n memberId: id,\n });\n throw new OrganizationRuleError('Only active members can have their role changed');\n }\n if (role === STUDENT_ROLE) {\n return this.demoteModerator(ctx, actor, member, member.userExternalId);\n }\n const userExternalId = member.userExternalId;\n await this.asActor(actor, id, false, () =>\n this.orgAdmin().updateRole(ctx, userExternalId, role),\n );\n const updated = await this.membersRepo.findById(ctx.orgId, id);\n this.logger.info('member role updated', { orgId: ctx.orgId, memberId: id, role });\n return updated ? toMember(updated) : null;\n }\n\n private async demoteModerator(\n ctx: MemberWriteContext,\n actor: Actor,\n member: MemberRecord,\n userExternalId: string,\n ): Promise<MemberRoleChange | null> {\n if (member.role !== 'moderator') {\n this.logger.warn('role change rejected: only a moderator becomes a student', {\n orgId: ctx.orgId,\n memberId: member.id,\n });\n throw new OrganizationRuleError('Only a moderator can be made a student');\n }\n await this.asActor(actor, member.id, true, () =>\n this.orgAdmin().removeMember(ctx, userExternalId),\n );\n const orgUser = await this.repo.findOrgUserById(ctx.orgId, member.id);\n if (!orgUser) {\n return null;\n }\n this.logger.info('moderator made a student', { orgId: ctx.orgId, memberId: member.id });\n return { ...toMember(member), role: orgUser.role, status: orgUser.status };\n }\n\n private async promoteStudent(\n ctx: MemberWriteContext,\n actor: Actor,\n id: string,\n role: Role,\n ): Promise<MemberRoleChange | null> {\n const orgUser = await this.repo.findOrgUserById(ctx.orgId, id);\n if (!orgUser) {\n return null;\n }\n if (orgUser.role !== STUDENT_ROLE || role === STUDENT_ROLE) {\n this.logger.warn('role change rejected: not an active member', {\n orgId: ctx.orgId,\n memberId: id,\n });\n throw new OrganizationRuleError('Only active members can have their role changed');\n }\n const person = await this.people.getUserById(orgUser.userId);\n if (!person?.externalId) {\n this.logger.warn('role change rejected: student has no account', {\n orgId: ctx.orgId,\n memberId: id,\n });\n throw new OrganizationRuleError('That student has not joined yet');\n }\n const org = await this.repo.findById(ctx.orgId);\n if (!org) {\n return null;\n }\n const orgExternalId = org.externalId ?? org.id;\n const userExternalId = person.externalId;\n await this.asActor(actor, orgUser.id, false, async () => {\n await this.orgAdmin().grantMembership(orgExternalId, userExternalId, role);\n if (!(await this.membersRepo.findById(ctx.orgId, id))) {\n await this.orgAdmin().updateRole(ctx, userExternalId, role);\n }\n });\n const updated = await this.membersRepo.findById(ctx.orgId, id);\n if (!updated) {\n this.logger.warn('role change rejected: membership not mirrored', {\n orgId: ctx.orgId,\n memberId: id,\n });\n throw new OrganizationRuleError('The role change did not take effect');\n }\n this.logger.info('student promoted', { orgId: ctx.orgId, memberId: id, role });\n return toMember(updated);\n }\n\n private asActor<T>(\n actor: Actor,\n orgUserId: string,\n demote: boolean,\n write: () => Promise<T>,\n ): Promise<T> {\n return this.memberWrites.run({ orgUserId, actorOrgUserId: actor.id, demote }, write);\n }\n\n private memberWrite(orgUserId: string): MemberWrite | undefined {\n const write = this.memberWrites.getStore();\n return write?.orgUserId === orgUserId ? write : undefined;\n }\n\n private assertManagesUsers(actor: Actor, orgId: string): void {\n if (capability(actor.role, 'manage_users') !== true) {\n this.logger.warn('member write rejected: actor cannot manage users', {\n orgId,\n actorOrgUserId: actor.id,\n });\n throw new ForbiddenError('Only an owner or admin can manage members');\n }\n }\n\n private assertNotOwner(role: Role, orgId: string): void {\n if (role === 'owner') {\n this.logger.warn('member write rejected: owner cannot be granted', { orgId });\n throw new OrganizationRuleError('Ownership cannot be granted');\n }\n }\n\n async removeMember(ctx: MemberWriteContext, actor: Actor, id: string): Promise<boolean> {\n this.assertManagesUsers(actor, ctx.orgId);\n const member = await this.membersRepo.findById(ctx.orgId, id);\n if (!member) {\n return false;\n }\n if (member.role === 'owner') {\n this.logger.warn('member removal rejected: owner cannot be removed', {\n orgId: ctx.orgId,\n memberId: id,\n });\n throw new OrganizationRuleError('The owner cannot be removed');\n }\n if (member.kind === 'member' && member.userExternalId) {\n await this.orgAdmin().removeMember(ctx, member.userExternalId);\n } else if (member.kind === 'invite' && member.inviteId) {\n const inviteId = member.inviteId;\n await this.uow.run(async ({ organizations, outbox }) => {\n const canceled = await organizations.setInviteStatus(ctx.orgId, inviteId, 'canceled');\n if (!canceled) {\n throw new NotFoundError('Invite', inviteId);\n }\n await outbox.append([\n organizationEvents.inviteCanceled.make({ orgId: ctx.orgId, data: canceled }),\n ]);\n });\n }\n this.logger.info('member removed', { orgId: ctx.orgId, memberId: id });\n return true;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"service.js","names":[],"sources":["../../src/organizations/service.ts"],"sourcesContent":["import type {\n Accounts,\n ApiKeyRole,\n ApiKeys,\n AuthOrganization,\n CallerLimit,\n CreateApiKeyInput,\n GrantReader,\n MembersRepository,\n OrgAdmin,\n OrganizationService,\n OrganizationsRepository,\n OrganizationsUnitOfWork,\n OrganizationsWriteScope,\n PlatformAccount,\n PlatformOrganization,\n PlatformOrganizationInput,\n ResolvedCaller,\n RolesRepository,\n StoredApiKey,\n TeamRow,\n TransferOwnershipInput,\n} from './ports.js';\nimport type { Invite, Organization, OrgUser } from './model.js';\nimport {\n InviteError,\n OrganizationRuleError,\n type AssignmentView,\n type CreateRoleInput,\n type InviteDetails,\n type Member,\n type MembersQuery,\n type Page,\n type RoleView,\n type UpdateRoleInput,\n type ViewerPermissions,\n} from './members.js';\nimport type {\n AcceptInviteInput,\n AddOrgUserInput,\n CreateInviteInput,\n CreateOrganizationInput,\n NewOrganizationInput,\n UpdateOrganizationInput,\n} from './types.js';\nimport { organizationEvents, type OrganizationEvent } from './events.js';\nimport type { Logger, NewDomainEvent } from '../shared/ports.js';\nimport { noopLogger } from '../shared/logger.js';\nimport type { Mailer } from '../shared/mailer.js';\nimport { generateInviteToken, hashInviteToken } from '../shared/invite-token.js';\nimport { ForbiddenError, NotFoundError } from '../shared/errors.js';\nimport type { Actor, EffectivePermissions } from '../shared/actor.js';\nimport type { AuthHeaders, IdentityService, SessionAdmin } from '../identity/index.js';\nimport type { AssetFiles } from '../assets/index.js';\nimport { genId, prefixId } from '../shared/id.js';\nimport { levelSchema } from '../types/schemas/organizations.js';\nimport type {\n ApiKeySummary,\n ConnectedApp,\n ContentType,\n CreatedApiKey,\n Level,\n Permission,\n} from '../types/index.js';\nimport {\n DEFAULT_ROLES,\n MEMBER_ROLE_KEY,\n OWNER_ROLE_KEY,\n validateRoleSet,\n type RoleDefinition,\n} from './default-roles.js';\nimport {\n catalogOf,\n isSystemRole,\n resolveOrgRoles,\n resolveRole,\n type OrgRole,\n type RoleCatalog,\n type StoredOrgRole,\n} from './org-roles.js';\nimport {\n ResolvedPermissions,\n resolvePermissions,\n systemPermissions,\n} from './effective-permissions.js';\nimport {\n ALL_PERMISSIONS,\n fitsLevels,\n permissionAbout,\n permissionList,\n withImplied,\n type PermissionListEntry,\n} from './permissions.js';\n\nconst INVITE_TTL_MS = 7 * 24 * 60 * 60 * 1000;\nconst SYSTEM_ID = 'system';\n\ntype NewOrganizationEvent = NewDomainEvent<OrganizationEvent>;\n\nexport interface InviteUrls {\n studentPortalUrl: string;\n adminAppUrl: string;\n}\n\nexport type OrganizationServiceParams = {\n repo: OrganizationsRepository;\n roles: RolesRepository;\n membersRepo: MembersRepository;\n people: IdentityService;\n images: Pick<AssetFiles, 'syncPublicImage'>;\n uow: OrganizationsUnitOfWork;\n orgAdmin: () => OrgAdmin;\n sessions: () => SessionAdmin;\n apiKeys?: () => ApiKeys;\n accounts?: () => Accounts;\n platform?: PlatformAccount;\n grants?: () => GrantReader;\n defaultRoles?: readonly RoleDefinition[];\n logger?: Logger;\n mailer?: Pick<Mailer, 'send'>;\n inviteUrls?: InviteUrls;\n};\n\nfunction snapshot(role: OrgRole) {\n return {\n key: role.key,\n name: role.name,\n description: role.description,\n base: role.base,\n levels: role.levels,\n team: role.team,\n permissions: role.permissions,\n };\n}\n\nfunction lockKeys(orgId: string, roleKeys: string[], orgUserIds: string[]): string[] {\n return [\n ...roleKeys.map((key) => `role:${orgId}:${key}`),\n ...orgUserIds.map((id) => `org-user:${orgId}:${id}`),\n ];\n}\n\nfunction roleView(role: OrgRole, holders: number): RoleView {\n return { ...role, holders, givable: role.key !== OWNER_ROLE_KEY };\n}\n\nfunction keySummary(key: StoredApiKey, roles: ReadonlyMap<string, OrgRole>): ApiKeySummary {\n return {\n id: key.id,\n name: key.name,\n start: key.start,\n role: key.roleKey ? { key: key.roleKey, name: roles.get(key.roleKey)?.name ?? null } : null,\n createdAt: key.createdAt,\n lastUsedAt: key.lastUsedAt,\n expiresAt: key.expiresAt,\n };\n}\n\nfunction limitScopes(\n limit: CallerLimit | undefined,\n roles: ReadonlyMap<string, OrgRole>,\n): readonly Permission[] | undefined {\n if (!limit) {\n return undefined;\n }\n if ('scopes' in limit) {\n return limit.scopes;\n }\n return roles.get(limit.roleKey)?.permissions ?? [];\n}\n\nexport class OrganizationServiceImpl implements OrganizationService {\n private readonly repo: OrganizationsRepository;\n private readonly roles: RolesRepository;\n private readonly membersRepo: MembersRepository;\n private readonly people: IdentityService;\n private readonly images: Pick<AssetFiles, 'syncPublicImage'>;\n private readonly uow: OrganizationsUnitOfWork;\n private readonly orgAdmin: () => OrgAdmin;\n private readonly sessions: () => SessionAdmin;\n private readonly apiKeysPort?: () => ApiKeys;\n private readonly accountsPort?: () => Accounts;\n private readonly platform?: PlatformAccount;\n private readonly grants?: () => GrantReader;\n private readonly catalog: RoleCatalog;\n private readonly logger: Logger;\n private readonly mailer?: Pick<Mailer, 'send'>;\n private readonly inviteUrls?: InviteUrls;\n\n constructor(params: OrganizationServiceParams) {\n this.repo = params.repo;\n this.roles = params.roles;\n this.membersRepo = params.membersRepo;\n this.people = params.people;\n this.images = params.images;\n this.uow = params.uow;\n this.orgAdmin = params.orgAdmin;\n this.sessions = params.sessions;\n this.apiKeysPort = params.apiKeys;\n this.accountsPort = params.accounts;\n this.platform = params.platform && {\n ...params.platform,\n email: params.platform.email.toLowerCase(),\n };\n this.grants = params.grants;\n const defaults = params.defaultRoles ?? DEFAULT_ROLES;\n validateRoleSet(defaults);\n this.catalog = catalogOf(defaults);\n this.logger = params.logger ?? noopLogger;\n this.mailer = params.mailer;\n this.inviteUrls = params.inviteUrls;\n }\n\n createOrganization(input: NewOrganizationInput, headers: AuthHeaders): Promise<AuthOrganization> {\n return this.orgAdmin().createOrganization(headers, input);\n }\n\n async provisionOrganization(input: CreateOrganizationInput): Promise<Organization> {\n const created = await this.uow.run(async ({ organizations, roles, outbox }) => {\n const id = genId('organization');\n const org = await organizations.create({\n id,\n externalId: input.externalId ?? id,\n name: input.name ?? 'My Org',\n slug: input.slug ?? '',\n ownerId: input.ownerId,\n });\n const owner = await organizations.createOrgUser({\n orgId: org.id,\n userId: input.ownerId,\n status: 'active',\n });\n await roles.setOrgRole(org.id, owner.id, OWNER_ROLE_KEY, SYSTEM_ID);\n await outbox.append([\n organizationEvents.organizationCreated.make({ orgId: org.id, data: org }),\n organizationEvents.orgUserLinked.make({\n orgId: org.id,\n data: { ...owner, orgRole: { key: OWNER_ROLE_KEY } },\n }),\n ]);\n return org;\n });\n this.logger.info('organization created', { orgId: created.id, slug: created.slug });\n return created;\n }\n\n async updateOrganization(input: UpdateOrganizationInput, actor: Actor): Promise<Organization> {\n actor.permissions.require('organizations:write');\n const orgId = actor.orgId;\n const org = await this.repo.findById(orgId);\n if (!org) {\n throw new NotFoundError('Organization', orgId);\n }\n if (input.slug !== undefined && input.slug !== org.slug) {\n const taken = await this.repo.findBySlug(input.slug);\n if (taken && taken.id !== orgId) {\n this.logger.warn('organization update refused: slug taken', { orgId, slug: input.slug });\n throw new OrganizationRuleError('That slug is already taken');\n }\n }\n await this.orgAdmin().updateOrganization(org.externalId ?? org.id, input);\n const updated = await this.uow.run(async ({ organizations, outbox }) => {\n const next = await organizations.update(orgId, { name: input.name, slug: input.slug });\n if (!next) {\n throw new NotFoundError('Organization', orgId);\n }\n await outbox.append([\n organizationEvents.organizationUpdated.make({\n orgId,\n data: next,\n metadata: { actorId: actor.id },\n }),\n ]);\n return next;\n });\n this.logger.info('organization updated', { orgId, actorId: actor.id });\n return updated;\n }\n\n async deleteOrganization(id: string): Promise<Organization> {\n const deleted = await this.uow.run(async ({ organizations, outbox }) => {\n const deleted = await organizations.delete(id);\n await outbox.append([\n organizationEvents.organizationDeleted.make({ orgId: deleted.id, data: deleted }),\n ]);\n return deleted;\n });\n this.logger.info('organization deleted', { orgId: deleted.id });\n return deleted;\n }\n\n async addOrgUser({ orgId, userId }: AddOrgUserInput): Promise<OrgUser> {\n const existing = await this.repo.findOrgUser(orgId, userId);\n if (existing) {\n return existing;\n }\n const orgUser = await this.uow.run(async ({ organizations, outbox }) => {\n const created = await organizations.createOrgUser({ orgId, userId, status: 'active' });\n await outbox.append([\n organizationEvents.orgUserLinked.make({\n orgId,\n data: { ...created, orgRole: { key: MEMBER_ROLE_KEY } },\n }),\n ]);\n return created;\n });\n this.logger.info('user added to organization', { orgId, userId, orgUserId: orgUser.id });\n return orgUser;\n }\n\n async removeOrgUser(orgId: string, userId: string): Promise<OrgUser | null> {\n const orgUser = await this.repo.findOrgUser(orgId, userId);\n if (!orgUser) {\n return null;\n }\n return this.deleteOrgUserRow(orgId, orgUser);\n }\n\n getByExternalId(externalId: string): Promise<Organization | null> {\n return this.repo.findByExternalId(externalId);\n }\n\n getBySlug(slug: string): Promise<Organization | null> {\n return this.repo.findBySlug(slug);\n }\n\n getById(id: string): Promise<Organization | null> {\n return this.repo.findById(id);\n }\n\n async portalIdentity(id: string): Promise<Pick<Organization, 'id' | 'name' | 'slug'>> {\n const org = await this.repo.findById(id);\n if (!org) {\n throw new NotFoundError('Organization', id);\n }\n return { id: org.id, name: org.name, slug: org.slug };\n }\n\n getOrgUser(orgId: string, userId: string): Promise<OrgUser | null> {\n return this.repo.findOrgUser(orgId, userId);\n }\n\n getOrgUserById(orgId: string, id: string): Promise<OrgUser | null> {\n return this.repo.findOrgUserById(orgId, id);\n }\n\n async getOrgUsersByIds(orgId: string, ids: string[]): Promise<OrgUser[]> {\n const unique = [...new Set(ids)];\n return unique.length > 0 ? this.repo.findOrgUsersByIds(orgId, unique) : [];\n }\n\n getOrgUsersForUser(userId: string): Promise<OrgUser[]> {\n return this.repo.findOrgUsersByUser(userId);\n }\n\n orgIds(): Promise<string[]> {\n return this.repo.listIds();\n }\n\n async actorFor(orgId: string, orgUserId: string, limit?: CallerLimit): Promise<Actor | null> {\n const facts = await this.roles.actorFacts(orgId, orgUserId);\n if (!facts || facts.status !== 'active') {\n return null;\n }\n const roles = this.roleMap(facts.storedRoles);\n const tokenScopes = limitScopes(limit, roles);\n return {\n id: orgUserId,\n orgId,\n permissions: resolvePermissions({\n assignments: facts.assignments,\n roles,\n ...(tokenScopes !== undefined && { tokenScopes }),\n }),\n };\n }\n\n async resolveCaller(\n authOrgId: string,\n authUserId: string,\n limit?: CallerLimit,\n ): Promise<ResolvedCaller | null> {\n const facts = await this.roles.callerFacts(authOrgId, authUserId);\n if (!facts) {\n return null;\n }\n const orgUser = facts.orgUser;\n if (orgUser?.status !== 'active') {\n return {\n orgId: facts.orgId,\n userId: facts.userId,\n orgUserId: orgUser?.orgUserId ?? null,\n actor: null,\n };\n }\n const roles = this.roleMap(orgUser.storedRoles);\n const tokenScopes = limitScopes(limit, roles);\n return {\n orgId: facts.orgId,\n userId: facts.userId,\n orgUserId: orgUser.orgUserId,\n actor: {\n id: orgUser.orgUserId,\n orgId: facts.orgId,\n permissions: resolvePermissions({\n assignments: orgUser.assignments,\n roles,\n ...(tokenScopes !== undefined && { tokenScopes }),\n }),\n },\n };\n }\n\n systemActor(orgId: string): Actor {\n return { id: SYSTEM_ID, orgId, permissions: systemPermissions() };\n }\n\n async permissionsOfMany(\n orgId: string,\n orgUserIds: string[],\n ): Promise<Map<string, EffectivePermissions>> {\n const unique = [...new Set(orgUserIds)];\n const out = new Map<string, EffectivePermissions>();\n if (unique.length === 0) {\n return out;\n }\n const [stored, assignments] = await Promise.all([\n this.roles.storedRoles(orgId),\n this.roles.assignmentsOfMany(orgId, unique),\n ]);\n const roles = this.roleMap(stored);\n for (const id of unique) {\n out.set(id, resolvePermissions({ assignments: assignments.get(id) ?? [], roles }));\n }\n return out;\n }\n\n async holders(orgId: string, permission: Permission, on?: string): Promise<string[]> {\n const stored = await this.roles.storedRoles(orgId);\n const keys = resolveOrgRoles(this.catalog, stored)\n .filter((role) => withImplied(role.permissions).has(permission))\n .map((role) => role.key);\n const candidates = await this.roles.holdersOf(orgId, keys, on);\n if (candidates.length === 0) {\n return [];\n }\n const permissions = await this.permissionsOfMany(orgId, candidates);\n return candidates.filter((id) => permissions.get(id)?.has(permission, on) ?? false);\n }\n\n permissionList(): PermissionListEntry[] {\n return permissionList();\n }\n\n viewerPermissions(actor: Actor): ViewerPermissions {\n const role = actor.permissions.roleOn();\n if (actor.permissions instanceof ResolvedPermissions) {\n return {\n orgRole: { key: role.key, name: role.name },\n isOwner: actor.permissions.isOwner(),\n org: actor.permissions.orgPermissions(),\n content: actor.permissions.contentPermissions(),\n };\n }\n return {\n orgRole: { key: role.key, name: role.name },\n isOwner: actor.permissions.isOwner(),\n org: ALL_PERMISSIONS.filter((p) => actor.permissions.has(p)),\n content: {},\n };\n }\n\n async auditRoleKeys(): Promise<number> {\n const usage = await this.roles.roleKeyUsage();\n const storedByOrg = new Map<string, StoredOrgRole[]>();\n let dangling = 0;\n for (const row of usage) {\n if (!storedByOrg.has(row.orgId)) {\n storedByOrg.set(row.orgId, await this.roles.storedRoles(row.orgId));\n }\n const role = this.resolveIn(storedByOrg.get(row.orgId)!, row.roleKey);\n if (!role) {\n dangling += row.count;\n this.logger.warn('assignments name a role the set no longer has', {\n orgId: row.orgId,\n roleKey: row.roleKey,\n level: row.level,\n count: row.count,\n });\n } else if (!role.levels.includes(row.level as Level)) {\n dangling += row.count;\n this.logger.warn('assignments sit at a level their role no longer lists', {\n orgId: row.orgId,\n roleKey: row.roleKey,\n level: row.level,\n count: row.count,\n });\n }\n }\n return dangling;\n }\n\n async listRoles(actor: Actor): Promise<RoleView[]> {\n actor.permissions.require('roles:read');\n const [stored, counts] = await Promise.all([\n this.roles.storedRoles(actor.orgId),\n this.roles.holderCounts(actor.orgId),\n ]);\n return resolveOrgRoles(this.catalog, stored).map((role) =>\n roleView(role, counts[role.key] ?? 0),\n );\n }\n\n async getRole(key: string, actor: Actor): Promise<RoleView> {\n actor.permissions.require('roles:read');\n const role = await this.findRole(actor.orgId, key);\n const holders = await this.roles.countHolders(actor.orgId, key);\n return roleView(role, holders);\n }\n\n async updateRole(key: string, input: UpdateRoleInput, actor: Actor): Promise<RoleView> {\n actor.permissions.require('roles:write');\n const orgId = actor.orgId;\n const changesAccess =\n input.levels !== undefined || input.team !== undefined || input.permissions !== undefined;\n const result = await this.uow.run(async (scope) => {\n const { roles, outbox } = scope;\n await roles.lock(lockKeys(orgId, [key], [actor.id]));\n const editor = await this.relockedActor(scope, actor);\n editor.permissions.require('roles:write');\n const stored = await roles.storedRoles(orgId);\n const current = stored.find((row) => row.key === key);\n const before = this.resolveIn(stored, key);\n if (!before) {\n throw new NotFoundError('Role', key);\n }\n if (changesAccess && isSystemRole(key)) {\n throw new OrganizationRuleError(`${before.name} can only be renamed`);\n }\n if (input.name !== undefined) {\n this.assertUniqueName(stored, key, input.name);\n }\n const next: StoredOrgRole = {\n key,\n name: input.name ?? current?.name ?? null,\n description: input.description ?? current?.description ?? null,\n base: current?.base ?? before.base,\n levels: input.levels ? this.parseLevels(input.levels) : (current?.levels ?? null),\n permissions: { ...(current?.permissions ?? {}), ...(input.permissions ?? {}) },\n team: input.team ?? current?.team ?? null,\n };\n const after = resolveRole(key, this.catalog, next);\n if (!after) {\n throw new NotFoundError('Role', key);\n }\n if (changesAccess) {\n this.assertFits(next);\n this.assertHolds(editor, [...before.permissions, ...after.permissions]);\n await this.assertLevelsNotInUse(roles, orgId, before, after);\n }\n await roles.saveStoredRole(orgId, next);\n await outbox.append([\n organizationEvents.roleUpdated.make({\n orgId,\n data: { key, before: snapshot(before), after: snapshot(after) },\n metadata: { actorId: actor.id },\n }),\n ]);\n return after;\n });\n this.logger.info('role updated', { orgId, roleKey: key, actorId: actor.id });\n return roleView(result, await this.roles.countHolders(orgId, key));\n }\n\n async createRole(input: CreateRoleInput, actor: Actor): Promise<RoleView> {\n actor.permissions.require('roles:write');\n const orgId = actor.orgId;\n const base = this.catalog.get(input.base);\n if (!base || isSystemRole(input.base)) {\n throw new OrganizationRuleError('A role is added from one of the deployment roles');\n }\n const key = prefixId('role');\n const created = await this.uow.run(async (scope) => {\n const { roles, outbox } = scope;\n await roles.lock(\n lockKeys(orgId, [key, `name:${input.name.trim().toLowerCase()}`], [actor.id]),\n );\n const editor = await this.relockedActor(scope, actor);\n editor.permissions.require('roles:write');\n const stored = await roles.storedRoles(orgId);\n this.assertUniqueName(stored, key, input.name);\n const row: StoredOrgRole = {\n key,\n name: input.name,\n description: input.description ?? null,\n base: input.base,\n levels: input.levels ? this.parseLevels(input.levels) : null,\n permissions: input.permissions ?? {},\n team: input.team ?? null,\n };\n this.assertFits(row);\n const role = resolveRole(key, this.catalog, row)!;\n this.assertHolds(editor, role.permissions);\n await roles.saveStoredRole(orgId, row);\n await outbox.append([\n organizationEvents.roleCreated.make({\n orgId,\n data: { role: snapshot(role) },\n metadata: { actorId: actor.id },\n }),\n ]);\n return role;\n });\n this.logger.info('role created', { orgId, roleKey: key, actorId: actor.id });\n return roleView(created, 0);\n }\n\n async resetRole(key: string, actor: Actor): Promise<RoleView> {\n actor.permissions.require('roles:write');\n const orgId = actor.orgId;\n if (!this.catalog.has(key)) {\n throw new OrganizationRuleError('Only a deployment role can be reset');\n }\n const result = await this.uow.run(async (scope) => {\n const { roles, outbox } = scope;\n await roles.lock(lockKeys(orgId, [key], [actor.id]));\n const editor = await this.relockedActor(scope, actor);\n editor.permissions.require('roles:write');\n const stored = await roles.storedRoles(orgId);\n const before = this.resolveIn(stored, key);\n const after = resolveRole(key, this.catalog, undefined);\n if (!before || !after) {\n throw new NotFoundError('Role', key);\n }\n this.assertUniqueName(\n stored.filter((row) => row.key !== key),\n key,\n after.name,\n );\n this.assertHolds(editor, [...before.permissions, ...after.permissions]);\n await this.assertLevelsNotInUse(roles, orgId, before, after);\n await roles.deleteStoredRole(orgId, key);\n await outbox.append([\n organizationEvents.roleUpdated.make({\n orgId,\n data: { key, before: snapshot(before), after: snapshot(after) },\n metadata: { actorId: actor.id },\n }),\n ]);\n return after;\n });\n this.logger.info('role reset', { orgId, roleKey: key, actorId: actor.id });\n return roleView(result, await this.roles.countHolders(orgId, key));\n }\n\n async deleteRole(key: string, actor: Actor): Promise<void> {\n actor.permissions.require('roles:delete');\n const orgId = actor.orgId;\n if (this.catalog.has(key)) {\n throw new OrganizationRuleError('A deployment role can be renamed and reset, not deleted');\n }\n const keys = await this.keysWithRole(orgId, key);\n if (keys > 0) {\n this.logger.warn('role deletion refused: role is on api keys', { orgId, roleKey: key, keys });\n throw new OrganizationRuleError(\n `This role is on ${keys} API ${keys === 1 ? 'key' : 'keys'}; revoke ${keys === 1 ? 'it' : 'them'} first`,\n );\n }\n await this.uow.run(async (scope) => {\n const { roles, outbox } = scope;\n await roles.lock(lockKeys(orgId, [key], [actor.id]));\n (await this.relockedActor(scope, actor)).permissions.require('roles:delete');\n const holders = await roles.countHolders(orgId, key);\n if (holders > 0) {\n this.logger.warn('role deletion refused: role is held', { orgId, roleKey: key, holders });\n throw new OrganizationRuleError(\n `This role is held by ${holders} ${holders === 1 ? 'person' : 'people'}`,\n );\n }\n const deleted = await roles.deleteStoredRole(orgId, key);\n if (!deleted) {\n throw new NotFoundError('Role', key);\n }\n await outbox.append([\n organizationEvents.roleDeleted.make({\n orgId,\n data: { key },\n metadata: { actorId: actor.id },\n }),\n ]);\n });\n this.logger.info('role deleted', { orgId, roleKey: key, actorId: actor.id });\n }\n\n async listTeam(query: MembersQuery, actor: Actor): Promise<Page<Member>> {\n actor.permissions.require('people:read');\n const orgId = actor.orgId;\n const [rows, stored] = await Promise.all([\n this.membersRepo.teamRows(orgId, MEMBER_ROLE_KEY),\n this.roles.storedRoles(orgId),\n ]);\n const roles = this.roleMap(stored);\n const nameOf = (key: string): string => roles.get(key)?.name ?? key;\n let members = rows.map((row) => this.toMember(row, nameOf));\n if (query.roleKey) {\n members = members.filter(\n (m) => m.orgRole.key === query.roleKey || m.items.some((i) => i.roleKey === query.roleKey),\n );\n }\n if (query.status) {\n members = members.filter((m) => m.status === query.status);\n }\n const q = query.search?.trim().toLowerCase();\n if (q) {\n members = members.filter((m) =>\n [m.firstName, m.lastName, m.email].some((v) => v?.toLowerCase().includes(q)),\n );\n }\n const sort = query.sort;\n const desc = sort?.startsWith('-') ?? false;\n const key = desc ? sort!.slice(1) : sort;\n const byName = (a: string | null, b: string | null): number =>\n a === b ? 0 : a === null ? 1 : b === null ? -1 : a.localeCompare(b);\n members.sort((a, b) => {\n const cmp =\n key === 'email'\n ? a.email.localeCompare(b.email)\n : key === 'role'\n ? a.orgRole.name.localeCompare(b.orgRole.name)\n : key === 'lastName'\n ? byName(a.lastName, b.lastName) || byName(a.firstName, b.firstName)\n : byName(a.firstName, b.firstName) || byName(a.lastName, b.lastName);\n return desc ? -cmp : cmp;\n });\n const start = (query.page - 1) * query.pageSize;\n return {\n rows: members.slice(start, start + query.pageSize),\n total: members.length,\n page: query.page,\n pageSize: query.pageSize,\n };\n }\n\n async listAssignments(orgUserId: string, actor: Actor): Promise<AssignmentView[]> {\n actor.permissions.require('people:read');\n return this.assignmentViews(actor.orgId, orgUserId);\n }\n\n async listAssignmentsOn(contentId: string, actor: Actor): Promise<AssignmentView[]> {\n actor.permissions.require('people:read');\n const [rows, stored] = await Promise.all([\n this.roles.assignmentsOn(actor.orgId, contentId),\n this.roles.storedRoles(actor.orgId),\n ]);\n const roles = this.roleMap(stored);\n return rows.map((row) => ({\n orgUserId: row.orgUserId,\n roleKey: row.roleKey,\n roleName: roles.get(row.roleKey)?.name ?? row.roleKey,\n contentId: row.contentId,\n contentType: row.contentType,\n }));\n }\n\n async setOrgRole(orgUserId: string, roleKey: string, actor: Actor): Promise<AssignmentView[]> {\n actor.permissions.require('people:write');\n const orgId = actor.orgId;\n this.assertNotSelf(actor, orgUserId);\n const seen = (await this.roles.assignmentsOf(orgId, orgUserId)).find(\n (row) => row.contentId === null,\n );\n const target = await this.repo.findOrgUserById(orgId, orgUserId);\n const invitee =\n target?.status === 'invited' ? await this.people.getUserById(target.userId) : null;\n await this.uow.run(async (scope) => {\n const { roles, organizations, outbox } = scope;\n await roles.lock(\n lockKeys(orgId, [roleKey, seen?.roleKey ?? MEMBER_ROLE_KEY], [orgUserId, actor.id]),\n );\n const editor = await this.relockedActor(scope, actor);\n editor.permissions.require('people:write');\n const orgUser = await organizations.findOrgUserById(orgId, orgUserId);\n if (!orgUser) {\n throw new NotFoundError('Person', orgUserId);\n }\n const stored = await roles.storedRoles(orgId);\n const next = this.resolveIn(stored, roleKey);\n if (!next) {\n throw new NotFoundError('Role', roleKey);\n }\n if (roleKey === OWNER_ROLE_KEY) {\n throw new OrganizationRuleError('Ownership moves only by transfer');\n }\n if (!next.levels.includes('org')) {\n throw new OrganizationRuleError(`${next.name} can't be given on the organization`);\n }\n const current = (await roles.assignmentsOf(orgId, orgUserId)).find(\n (row) => row.contentId === null,\n );\n if (current?.roleKey === OWNER_ROLE_KEY) {\n this.logger.warn('org role change refused: target is the owner', { orgId, orgUserId });\n throw new OrganizationRuleError(\"The owner's role changes only by transfer\");\n }\n if (orgUser.status === 'invited') {\n const person = invitee?.id === orgUser.userId ? invitee : null;\n const pending = person ? await organizations.findPendingInvite(orgId, person.email) : null;\n if (!pending) {\n throw new NotFoundError('Invite', orgUserId);\n }\n const invited = this.resolveIn(stored, pending.roleKey);\n this.assertHolds(editor, [...(invited?.permissions ?? []), ...next.permissions]);\n const editorUser = await organizations.findOrgUserById(orgId, actor.id);\n if (!editorUser) {\n throw new ForbiddenError('You are no longer an active member of this organization');\n }\n await organizations.setInviteRole(orgId, pending.id, roleKey, editorUser.userId);\n this.logger.info('pending invite role changed', {\n orgId,\n orgUserId,\n inviteId: pending.id,\n roleKey,\n actorId: actor.id,\n });\n return;\n }\n const previousKey = current?.roleKey ?? MEMBER_ROLE_KEY;\n const previous = this.resolveIn(stored, previousKey);\n this.assertHolds(editor, [...(previous?.permissions ?? []), ...next.permissions]);\n await roles.setOrgRole(\n orgId,\n orgUserId,\n roleKey === MEMBER_ROLE_KEY ? null : roleKey,\n actor.id,\n );\n if (previousKey !== roleKey) {\n await outbox.append([\n organizationEvents.roleAssigned.make({\n orgId,\n data: {\n orgUserId,\n roleKey,\n contentId: null,\n contentType: null,\n previousRoleKey: previousKey,\n },\n metadata: { actorId: actor.id },\n }),\n ]);\n }\n this.logger.info('org role set', { orgId, orgUserId, roleKey, actorId: actor.id });\n });\n return this.assignmentViews(orgId, orgUserId);\n }\n\n async assignRole(\n orgUserId: string,\n contentId: string,\n roleKey: string,\n actor: Actor,\n ): Promise<AssignmentView[]> {\n actor.permissions.require('people:write');\n const orgId = actor.orgId;\n this.assertNotSelf(actor, orgUserId);\n const seen = (await this.roles.assignmentsOf(orgId, orgUserId)).find(\n (row) => row.contentId === contentId,\n );\n await this.uow.run(async (scope) => {\n const { roles, organizations, outbox } = scope;\n await roles.lock(\n lockKeys(orgId, [roleKey, ...(seen ? [seen.roleKey] : [])], [orgUserId, actor.id]),\n );\n const editor = await this.relockedActor(scope, actor);\n editor.permissions.require('people:write');\n const orgUser = await organizations.findOrgUserById(orgId, orgUserId);\n if (!orgUser) {\n throw new NotFoundError('Person', orgUserId);\n }\n const contentType = await roles.contentTypeOf(orgId, contentId);\n if (!contentType) {\n throw new NotFoundError('Content', contentId);\n }\n const stored = await roles.storedRoles(orgId);\n const next = this.resolveIn(stored, roleKey);\n if (!next) {\n throw new NotFoundError('Role', roleKey);\n }\n if (isSystemRole(roleKey) || !next.levels.includes(contentType)) {\n this.logger.warn('role assignment refused: level not listed', {\n orgId,\n roleKey,\n contentType,\n });\n throw new OrganizationRuleError(`${next.name} can't be given on a ${contentType}`);\n }\n const current = (await roles.assignmentsOf(orgId, orgUserId)).find(\n (row) => row.contentId === contentId,\n );\n const previous = current ? this.resolveIn(stored, current.roleKey) : null;\n this.assertHoldsOn(\n editor,\n [\n ...this.itemPermissions(previous, contentType),\n ...this.itemPermissions(next, contentType),\n ],\n contentId,\n );\n await roles.setItemRole(orgId, orgUserId, contentId, contentType, roleKey, actor.id);\n if (current?.roleKey !== roleKey) {\n await outbox.append([\n organizationEvents.roleAssigned.make({\n orgId,\n data: {\n orgUserId,\n roleKey,\n contentId,\n contentType,\n previousRoleKey: current?.roleKey ?? null,\n },\n metadata: { actorId: actor.id },\n }),\n ]);\n }\n });\n this.logger.info('item role given', {\n orgId,\n orgUserId,\n contentId,\n roleKey,\n actorId: actor.id,\n });\n return this.assignmentViews(orgId, orgUserId);\n }\n\n async unassignRole(\n orgUserId: string,\n contentId: string,\n actor: Actor,\n ): Promise<AssignmentView[]> {\n actor.permissions.require('people:write');\n const orgId = actor.orgId;\n this.assertNotSelf(actor, orgUserId);\n const seen = (await this.roles.assignmentsOf(orgId, orgUserId)).find(\n (row) => row.contentId === contentId,\n );\n await this.uow.run(async (scope) => {\n const { roles, outbox } = scope;\n await roles.lock(lockKeys(orgId, seen ? [seen.roleKey] : [], [orgUserId, actor.id]));\n const current = (await roles.assignmentsOf(orgId, orgUserId)).find(\n (row) => row.contentId === contentId,\n );\n if (!current || !current.contentType) {\n throw new NotFoundError('Role assignment', contentId);\n }\n const editor = await this.relockedActor(scope, actor);\n editor.permissions.require('people:write');\n const stored = await roles.storedRoles(orgId);\n const role = this.resolveIn(stored, current.roleKey);\n this.assertHoldsOn(editor, this.itemPermissions(role, current.contentType), contentId);\n const removed = await roles.removeItemRole(orgId, orgUserId, contentId);\n if (removed) {\n await outbox.append([\n organizationEvents.roleUnassigned.make({\n orgId,\n data: {\n orgUserId,\n roleKey: removed.roleKey,\n contentId,\n contentType: current.contentType,\n },\n metadata: { actorId: actor.id },\n }),\n ]);\n }\n });\n this.logger.info('item role removed', {\n orgId,\n orgUserId,\n contentId,\n actorId: actor.id,\n });\n return this.assignmentViews(orgId, orgUserId);\n }\n\n async removePerson(orgUserId: string, actor: Actor): Promise<void> {\n actor.permissions.require('people:delete');\n const orgId = actor.orgId;\n this.assertNotSelf(actor, orgUserId);\n const orgUser = await this.repo.findOrgUserById(orgId, orgUserId);\n if (!orgUser) {\n throw new NotFoundError('Person', orgUserId);\n }\n const person = await this.people.getUserById(orgUser.userId);\n await this.uow.run(async (scope) => {\n const { roles } = scope;\n await roles.lock(lockKeys(orgId, [], [orgUserId, actor.id]));\n const editor = await this.relockedActor(scope, actor);\n editor.permissions.require('people:delete');\n const held = await roles.assignmentsOf(orgId, orgUserId);\n if (held.some((row) => row.roleKey === OWNER_ROLE_KEY)) {\n this.logger.warn('removal refused: target is the owner', { orgId, orgUserId });\n throw new OrganizationRuleError('The owner cannot be removed');\n }\n const stored = await roles.storedRoles(orgId);\n for (const row of held) {\n const role = this.resolveIn(stored, row.roleKey);\n if (!role) {\n continue;\n }\n if (row.contentId === null) {\n this.assertHolds(editor, role.permissions);\n } else if (row.contentType) {\n this.assertHoldsOn(editor, this.itemPermissions(role, row.contentType), row.contentId);\n }\n }\n await this.deleteOrgUserIn(scope, orgId, orgUser, held, actor.id);\n });\n this.logger.info('person removed', { orgId, orgUserId, actorId: actor.id });\n await this.cleanUpAfterRemoval(orgId, orgUserId, person);\n }\n\n async transferOwnership(input: TransferOwnershipInput, actor: Actor): Promise<void> {\n if (!actor.permissions.isOwner()) {\n this.logger.warn('ownership transfer refused: caller is not the owner', {\n orgId: actor.orgId,\n actorId: actor.id,\n });\n throw new ForbiddenError('Only the owner can transfer ownership');\n }\n const orgId = actor.orgId;\n const previousRoleKey = input.previousOwnerRoleKey ?? MEMBER_ROLE_KEY;\n if (input.newOwnerOrgUserId === actor.id) {\n throw new OrganizationRuleError('You already own this organization');\n }\n const stored = await this.roles.storedRoles(orgId);\n const previousRole = this.resolveIn(stored, previousRoleKey);\n if (\n !previousRole ||\n previousRoleKey === OWNER_ROLE_KEY ||\n !previousRole.levels.includes('org')\n ) {\n throw new OrganizationRuleError('The previous owner needs a role given on the organization');\n }\n const [org, newOwner, previousOwner] = await Promise.all([\n this.repo.findById(orgId),\n this.repo.findOrgUserById(orgId, input.newOwnerOrgUserId),\n this.repo.findOrgUserById(orgId, actor.id),\n ]);\n if (!org || !previousOwner) {\n throw new NotFoundError('Organization', orgId);\n }\n if (!newOwner || newOwner.status !== 'active') {\n throw new OrganizationRuleError('The new owner must be an active member of the organization');\n }\n const [newPerson, previousPerson] = await Promise.all([\n this.people.getUserById(newOwner.userId),\n this.people.getUserById(previousOwner.userId),\n ]);\n if (!newPerson?.externalId || !previousPerson?.externalId) {\n throw new OrganizationRuleError('Both people need an account');\n }\n const orgExternalId = org.externalId ?? org.id;\n await this.orgAdmin().setMembershipOwner(orgExternalId, newPerson.externalId, true);\n await this.orgAdmin().setMembershipOwner(orgExternalId, previousPerson.externalId, false);\n await this.uow.run(async ({ roles, outbox }) => {\n await roles.lock(\n lockKeys(orgId, [OWNER_ROLE_KEY, previousRoleKey], [newOwner.id, previousOwner.id]),\n );\n const owner = await roles.ownerOrgUserId(orgId);\n if (owner !== previousOwner.id) {\n throw new ForbiddenError('Only the owner can transfer ownership');\n }\n await roles.setOrgRole(orgId, newOwner.id, null, actor.id);\n await roles.setOrgRole(\n orgId,\n previousOwner.id,\n previousRoleKey === MEMBER_ROLE_KEY ? null : previousRoleKey,\n actor.id,\n );\n await roles.setOrgRole(orgId, newOwner.id, OWNER_ROLE_KEY, actor.id);\n await outbox.append([\n organizationEvents.ownershipTransferred.make({\n orgId,\n data: { previousOwnerOrgUserId: previousOwner.id, newOwnerOrgUserId: newOwner.id },\n metadata: { actorId: actor.id },\n }),\n ]);\n });\n this.logger.info('ownership transferred', {\n orgId,\n previousOwnerOrgUserId: previousOwner.id,\n newOwnerOrgUserId: newOwner.id,\n });\n }\n\n async createInvite(\n input: CreateInviteInput,\n actor: Actor,\n inviterUserId: string,\n ): Promise<Invite> {\n actor.permissions.require('people:write');\n const orgId = actor.orgId;\n const roleKey = input.roleKey ?? MEMBER_ROLE_KEY;\n const email = input.email;\n if (roleKey === OWNER_ROLE_KEY) {\n this.logger.warn('invite refused: ownership cannot be granted', { orgId });\n throw new OrganizationRuleError('Ownership moves only by transfer');\n }\n const stored = await this.roles.storedRoles(orgId);\n const role = this.resolveIn(stored, roleKey);\n if (!role) {\n throw new NotFoundError('Role', roleKey);\n }\n if (!role.levels.includes('org')) {\n throw new OrganizationRuleError(`${role.name} can't be given on the organization`);\n }\n this.assertHolds(actor, role.permissions);\n const known = await this.people.getUserByEmail(email);\n const existing = known ? await this.repo.findOrgUser(orgId, known.id) : null;\n if (existing?.id === actor.id) {\n this.logger.warn('invite refused: inviting yourself', { orgId, actorId: actor.id });\n throw new ForbiddenError(\"You can't change your own roles\");\n }\n let contributed = role.permissions;\n if (existing) {\n const held = await this.roles.assignmentsOf(orgId, existing.id);\n const current = held.find((row) => row.contentId === null);\n if (current?.roleKey === OWNER_ROLE_KEY) {\n this.logger.warn('invite refused: target is the owner', { orgId });\n throw new OrganizationRuleError('The owner cannot be invited');\n }\n const currentRole = this.resolveIn(stored, current?.roleKey ?? MEMBER_ROLE_KEY);\n contributed = [...contributed, ...(currentRole?.permissions ?? [])];\n }\n this.assertHolds(actor, contributed);\n const person =\n known ??\n (await this.people.createUser({\n email,\n ...(input.firstName !== undefined && { firstName: input.firstName }),\n ...(input.lastName !== undefined && { lastName: input.lastName }),\n }));\n const sendEmail = input.sendEmail ?? true;\n const { token, tokenHash } = generateInviteToken();\n const expiresAt = new Date(Date.now() + INVITE_TTL_MS);\n const invite = await this.uow.run(async ({ organizations, outbox }) => {\n const events: NewOrganizationEvent[] = [];\n if (!existing) {\n const orgUser = await organizations.createOrgUser({\n orgId,\n userId: person.id,\n status: 'invited',\n });\n events.push(\n roleKey === MEMBER_ROLE_KEY\n ? organizationEvents.studentCreated.make({ orgId, data: orgUser })\n : organizationEvents.orgUserLinked.make({\n orgId,\n data: { ...orgUser, orgRole: { key: MEMBER_ROLE_KEY } },\n }),\n );\n }\n const row = await organizations.upsertPendingInvite(orgId, {\n email,\n roleKey,\n invitedBy: inviterUserId,\n tokenHash,\n expiresAt,\n });\n events.push(\n organizationEvents.inviteCreated.make({\n orgId,\n data: row,\n metadata: { actorId: actor.id },\n }),\n );\n await outbox.append(events);\n return row;\n });\n if (sendEmail) {\n try {\n await this.sendInviteEmail(invite, token, role);\n } catch (err) {\n this.logger.error('failed to send invite email', {\n orgId,\n inviteId: invite.id,\n roleKey,\n err: err instanceof Error ? err : new Error(String(err)),\n });\n }\n }\n this.logger.info('invite created', {\n orgId,\n inviteId: invite.id,\n roleKey,\n sendEmail,\n actorId: actor.id,\n });\n return invite;\n }\n\n async resendInvite(orgUserId: string, actor: Actor, inviterUserId: string): Promise<void> {\n actor.permissions.require('people:write');\n const orgId = actor.orgId;\n const { invite, role } = await this.pendingInviteOf(orgId, orgUserId);\n this.assertHolds(actor, role.permissions);\n const { token, tokenHash } = generateInviteToken();\n const expiresAt = new Date(Date.now() + INVITE_TTL_MS);\n const refreshed = await this.repo.setInviteToken(\n orgId,\n invite.id,\n tokenHash,\n expiresAt,\n inviterUserId,\n );\n if (!refreshed) {\n throw new NotFoundError('Invite', invite.id);\n }\n await this.sendInviteEmail(refreshed, token, role);\n this.logger.info('invite resent', {\n orgId,\n orgUserId,\n inviteId: invite.id,\n actorId: actor.id,\n });\n }\n\n async cancelInvite(orgUserId: string, actor: Actor): Promise<void> {\n actor.permissions.require('people:write');\n const orgId = actor.orgId;\n const { invite, role, orgUser } = await this.pendingInviteOf(orgId, orgUserId);\n this.assertHolds(actor, role.permissions);\n const held = await this.roles.assignmentsOf(orgId, orgUserId);\n const holdsGrant = this.grants ? await this.grants().holdsAnyGrant(orgId, orgUserId) : true;\n await this.uow.run(async (scope) => {\n const canceled = await scope.organizations.setInviteStatus(orgId, invite.id, 'canceled');\n if (!canceled) {\n throw new NotFoundError('Invite', invite.id);\n }\n await scope.outbox.append([\n organizationEvents.inviteCanceled.make({\n orgId,\n data: canceled,\n metadata: { actorId: actor.id },\n }),\n ]);\n if (orgUser.status === 'invited' && held.length === 0 && !holdsGrant) {\n await this.deleteOrgUserIn(scope, orgId, orgUser, held, actor.id);\n }\n });\n this.logger.info('invite canceled', {\n orgId,\n orgUserId,\n inviteId: invite.id,\n actorId: actor.id,\n });\n }\n\n async peekInvite(token: string): Promise<Invite | null> {\n const invite = await this.repo.findInviteByTokenHash(hashInviteToken(token));\n if (!invite || invite.status !== 'pending') {\n return null;\n }\n if (invite.expiresAt && invite.expiresAt < new Date()) {\n return null;\n }\n return invite;\n }\n\n async describeInvite(token: string): Promise<InviteDetails> {\n const invite = await this.peekInvite(token);\n if (!invite) {\n throw new NotFoundError('Invite', 'token');\n }\n const person = await this.people.getUserByEmail(invite.email);\n return {\n email: invite.email,\n firstName: person?.firstName ?? null,\n lastName: person?.lastName ?? null,\n };\n }\n\n async acceptInvite(input: AcceptInviteInput, headers: AuthHeaders): Promise<OrgUser> {\n const invite = await this.peekInvite(input.token);\n if (!invite) {\n this.logger.warn('invite accept refused: token invalid or expired');\n throw new InviteError('invalid invite');\n }\n if (invite.email.toLowerCase() !== input.email.toLowerCase()) {\n this.logger.warn('invite accept refused: email mismatch', {\n orgId: invite.orgId,\n inviteId: invite.id,\n });\n throw new InviteError('emails must match');\n }\n const orgId = invite.orgId;\n const org = await this.repo.findById(orgId);\n if (!org) {\n throw new InviteError('Org not found');\n }\n const person = await this.people.getUserByExternalId(input.authUserId);\n if (!person?.externalId) {\n throw new NotFoundError('User', input.authUserId);\n }\n const orgUser = await this.repo.findOrgUser(orgId, person.id);\n if (!orgUser) {\n throw new NotFoundError('Person', person.id);\n }\n const inviter = await this.repo.findOrgUser(orgId, invite.invitedBy);\n if (!inviter) {\n this.logger.warn('invite accept refused: inviter left', { orgId, inviteId: invite.id });\n throw new InviteError('This invitation is no longer valid; ask for a new one');\n }\n await this.uow.run((scope) => this.checkAcceptance(scope, invite, orgUser, inviter.id));\n await this.orgAdmin().grantMembership(org.externalId ?? org.id, person.externalId);\n const accepted = await this.uow.run(async (scope) => {\n const { organizations, roles, outbox } = scope;\n const seen = (await roles.assignmentsOf(orgId, orgUser.id)).find(\n (row) => row.contentId === null,\n );\n await roles.lock(\n lockKeys(\n orgId,\n [invite.roleKey, seen?.roleKey ?? MEMBER_ROLE_KEY],\n [orgUser.id, inviter.id],\n ),\n );\n const previousKey = await this.checkAcceptance(scope, invite, orgUser, inviter.id);\n const activated =\n orgUser.status === 'active'\n ? orgUser\n : await organizations.updateOrgUser(orgId, orgUser.id, { status: 'active' });\n if (!activated) {\n throw new NotFoundError('Person', orgUser.id);\n }\n const acceptedInvite = await organizations.setInviteStatus(orgId, invite.id, 'accepted');\n if (!acceptedInvite) {\n throw new NotFoundError('Invite', invite.id);\n }\n await roles.setOrgRole(\n orgId,\n orgUser.id,\n invite.roleKey === MEMBER_ROLE_KEY ? null : invite.roleKey,\n inviter.id,\n );\n const events: NewOrganizationEvent[] = [\n organizationEvents.inviteAccepted.make({ orgId, data: acceptedInvite }),\n ];\n if (orgUser.status !== 'active') {\n events.push(\n organizationEvents.orgUserUpdated.make({\n orgId,\n data: { ...activated, orgRole: { key: invite.roleKey } },\n }),\n );\n if (invite.roleKey === MEMBER_ROLE_KEY) {\n events.push(organizationEvents.studentLinked.make({ orgId, data: activated }));\n }\n }\n if (previousKey !== invite.roleKey) {\n events.push(\n organizationEvents.roleAssigned.make({\n orgId,\n data: {\n orgUserId: orgUser.id,\n roleKey: invite.roleKey,\n contentId: null,\n contentType: null,\n previousRoleKey: previousKey,\n },\n metadata: { actorId: inviter.id },\n }),\n );\n }\n await outbox.append(events);\n return activated;\n });\n await this.sessions().stampActiveOrganization(headers, orgId);\n this.logger.info('invite accepted', { orgId, inviteId: invite.id, orgUserId: orgUser.id });\n return accepted;\n }\n\n private async checkAcceptance(\n scope: OrganizationsWriteScope,\n invite: Invite,\n orgUser: OrgUser,\n inviterOrgUserId: string,\n ): Promise<string> {\n const orgId = invite.orgId;\n const stored = await scope.roles.storedRoles(orgId);\n const role = this.resolveIn(stored, invite.roleKey);\n if (!role || invite.roleKey === OWNER_ROLE_KEY) {\n throw new InviteError('The invited role no longer exists');\n }\n const current = (await scope.roles.assignmentsOf(orgId, orgUser.id)).find(\n (row) => row.contentId === null,\n );\n if (current?.roleKey === OWNER_ROLE_KEY) {\n throw new InviteError(\"The owner's role changes only by transfer\");\n }\n const currentRole = this.resolveIn(stored, current?.roleKey ?? MEMBER_ROLE_KEY);\n const facts = await scope.roles.actorFacts(orgId, inviterOrgUserId);\n const inviter =\n facts && facts.status === 'active'\n ? resolvePermissions({\n assignments: facts.assignments,\n roles: this.roleMap(facts.storedRoles),\n })\n : null;\n const needed: Permission[] = [\n 'people:write',\n ...role.permissions,\n ...(currentRole?.permissions ?? []),\n ];\n const missing = inviter ? needed.filter((p) => !inviter.has(p)) : needed;\n if (missing.length > 0) {\n this.logger.warn('invite accept refused: inviter no longer holds the role', {\n orgId,\n inviteId: invite.id,\n inviterOrgUserId,\n missing,\n });\n throw new InviteError('This invitation is no longer valid; ask for a new one');\n }\n return current?.roleKey ?? MEMBER_ROLE_KEY;\n }\n\n async listApiKeys(actor: Actor): Promise<ApiKeySummary[]> {\n actor.permissions.require('api_keys:read');\n const { orgExternalId, userExternalId } = await this.externalIdsOf(actor);\n const [keys, stored] = await Promise.all([\n this.apiKeys().list(orgExternalId, userExternalId),\n this.roles.storedRoles(actor.orgId),\n ]);\n const roles = this.roleMap(stored);\n return keys.map((key) => keySummary(key, roles));\n }\n\n async listApiKeyRoles(actor: Actor): Promise<ApiKeyRole[]> {\n actor.permissions.require('api_keys:write');\n const stored = await this.roles.storedRoles(actor.orgId);\n return resolveOrgRoles(this.catalog, stored)\n .filter((role) =>\n role.permissions.every((permission) => actor.permissions.anywhere(permission)),\n )\n .map((role) => ({ key: role.key, name: role.name, description: role.description }));\n }\n\n async createApiKey(input: CreateApiKeyInput, actor: Actor): Promise<CreatedApiKey> {\n actor.permissions.require('api_keys:write');\n const roles = this.roleMap(await this.roles.storedRoles(actor.orgId));\n const role = roles.get(input.role);\n if (!role) {\n throw new NotFoundError('Role', input.role);\n }\n const missing = role.permissions.filter(\n (permission) => !actor.permissions.anywhere(permission),\n );\n if (missing.length > 0) {\n this.logger.warn('api key refused: role permissions not held', {\n orgId: actor.orgId,\n actorId: actor.id,\n roleKey: role.key,\n missing,\n });\n throw new ForbiddenError(`You don't hold ${missing.join(', ')}`);\n }\n const { orgExternalId, userExternalId } = await this.externalIdsOf(actor);\n const created = await this.apiKeys().create(orgExternalId, userExternalId, {\n name: input.name,\n roleKey: role.key,\n expiresInDays: input.expiresInDays,\n });\n this.logger.info('api key created', {\n orgId: actor.orgId,\n actorId: actor.id,\n keyId: created.id,\n roleKey: role.key,\n });\n return { ...keySummary(created, roles), key: created.key };\n }\n\n async deleteApiKey(keyId: string, actor: Actor): Promise<void> {\n actor.permissions.require('api_keys:delete');\n const { orgExternalId, userExternalId } = await this.externalIdsOf(actor);\n await this.apiKeys().revoke(orgExternalId, userExternalId, keyId);\n this.logger.info('api key revoked', { orgId: actor.orgId, actorId: actor.id, keyId });\n }\n\n async listConnectedApps(actor: Actor): Promise<ConnectedApp[]> {\n const { orgExternalId, userExternalId } = await this.externalIdsOf(actor);\n return this.apiKeys().listApps(orgExternalId, userExternalId);\n }\n\n async revokeConnectedApp(clientId: string, actor: Actor): Promise<void> {\n const { orgExternalId, userExternalId } = await this.externalIdsOf(actor);\n await this.apiKeys().revokeApp(orgExternalId, userExternalId, clientId);\n this.logger.info('connected app revoked', {\n orgId: actor.orgId,\n actorId: actor.id,\n clientId,\n });\n }\n\n async createPlatformOrganization(\n input: PlatformOrganizationInput,\n ): Promise<PlatformOrganization> {\n const ownerExternalId = await this.platformAccountId();\n const created = await this.orgAdmin().createOrganizationFor(ownerExternalId, {\n name: input.name,\n slug: input.slug,\n });\n const org = await this.repo.findByExternalId(created.id);\n if (!org) {\n throw new NotFoundError('Organization', created.id);\n }\n const apiKey = await this.platformKey(org, ownerExternalId, input.keyName ?? 'Platform');\n this.logger.info('platform organization created', { orgId: org.id, slug: org.slug });\n return {\n organization: { id: org.id, name: org.name, slug: org.slug, createdAt: org.createdAt },\n apiKey,\n };\n }\n\n async createPlatformApiKey(orgId: string, name: string): Promise<CreatedApiKey> {\n const account = this.platformAccount();\n const [org, person] = await Promise.all([\n this.repo.findById(orgId),\n this.people.getUserByEmail(account.email),\n ]);\n const orgUser = org && person ? await this.repo.findOrgUser(org.id, person.id) : null;\n if (!org || !person?.externalId || orgUser?.status !== 'active') {\n throw new NotFoundError('Organization', orgId);\n }\n const apiKey = await this.platformKey(org, person.externalId, name);\n this.logger.info('platform api key created', { orgId: org.id, keyId: apiKey.id });\n return apiKey;\n }\n\n isPlatformAccount(email: string): boolean {\n return this.platform !== undefined && email.toLowerCase() === this.platform.email;\n }\n\n private async platformKey(\n org: Organization,\n ownerExternalId: string,\n name: string,\n ): Promise<CreatedApiKey> {\n const [created, stored] = await Promise.all([\n this.apiKeys().create(org.externalId ?? org.id, ownerExternalId, {\n name,\n roleKey: OWNER_ROLE_KEY,\n }),\n this.roles.storedRoles(org.id),\n ]);\n return { ...keySummary(created, this.roleMap(stored)), key: created.key };\n }\n\n private async platformAccountId(): Promise<string> {\n const account = this.platformAccount();\n const person = await this.people.getUserByEmail(account.email);\n if (person?.externalId) {\n return person.externalId;\n }\n const { externalId } = await this.accounts().createAccount(account);\n this.logger.info('platform account created', { userId: externalId });\n return externalId;\n }\n\n private platformAccount(): PlatformAccount {\n if (!this.platform) {\n throw new Error('the platform account is not configured');\n }\n return this.platform;\n }\n\n private accounts(): Accounts {\n if (!this.accountsPort) {\n throw new Error('accounts are not configured');\n }\n return this.accountsPort();\n }\n\n private async keysWithRole(orgId: string, roleKey: string): Promise<number> {\n if (!this.apiKeysPort) {\n return 0;\n }\n const org = await this.repo.findById(orgId);\n return org ? this.apiKeysPort().countWithRole(org.externalId ?? org.id, roleKey) : 0;\n }\n\n private async cleanUpAfterRemoval(\n orgId: string,\n orgUserId: string,\n person: { email: string; externalId: string | null } | null,\n ): Promise<void> {\n const steps: Array<[string, () => Promise<unknown>]> = [];\n const org = await this.repo.findById(orgId);\n if (person?.externalId && org) {\n const orgExternalId = org.externalId ?? org.id;\n const userExternalId = person.externalId;\n steps.push([\n 'membership',\n () => this.orgAdmin().removeMembership(orgExternalId, userExternalId),\n ]);\n steps.push([\n 'api keys and tokens',\n () => this.apiKeys().revokeAllFor(orgExternalId, userExternalId),\n ]);\n }\n steps.push(['profile photo', () => this.images.syncPublicImage(orgId, orgUserId, null)]);\n if (person) {\n steps.push([\n 'pending invite',\n async () => {\n const pending = await this.repo.findPendingInvite(orgId, person.email);\n if (pending) {\n await this.repo.setInviteStatus(orgId, pending.id, 'canceled');\n }\n },\n ]);\n }\n for (const [step, run] of steps) {\n for (let attempt = 1; attempt <= 3; attempt++) {\n try {\n await run();\n break;\n } catch (err) {\n this.logger.error('cleanup after removal failed', {\n orgId,\n orgUserId,\n step,\n attempt,\n err: err instanceof Error ? err : new Error(String(err)),\n });\n }\n }\n }\n }\n\n private apiKeys(): ApiKeys {\n if (!this.apiKeysPort) {\n throw new Error('api keys are not configured');\n }\n return this.apiKeysPort();\n }\n\n private async externalIdsOf(\n actor: Actor,\n ): Promise<{ orgExternalId: string; userExternalId: string }> {\n const [org, orgUser] = await Promise.all([\n this.repo.findById(actor.orgId),\n this.repo.findOrgUserById(actor.orgId, actor.id),\n ]);\n if (!org || !orgUser) {\n throw new NotFoundError('Person', actor.id);\n }\n const person = await this.people.getUserById(orgUser.userId);\n if (!person?.externalId) {\n throw new NotFoundError('User', orgUser.userId);\n }\n return { orgExternalId: org.externalId ?? org.id, userExternalId: person.externalId };\n }\n\n private async pendingInviteOf(\n orgId: string,\n orgUserId: string,\n ): Promise<{ invite: Invite; role: OrgRole; orgUser: OrgUser }> {\n const orgUser = await this.repo.findOrgUserById(orgId, orgUserId);\n if (!orgUser) {\n throw new NotFoundError('Person', orgUserId);\n }\n const person = await this.people.getUserById(orgUser.userId);\n const invite = person ? await this.repo.findPendingInvite(orgId, person.email) : null;\n if (!invite) {\n throw new NotFoundError('Invite', orgUserId);\n }\n const stored = await this.roles.storedRoles(orgId);\n const role = this.resolveIn(stored, invite.roleKey);\n if (!role) {\n throw new NotFoundError('Role', invite.roleKey);\n }\n return { invite, role, orgUser };\n }\n\n private async deleteOrgUserRow(orgId: string, orgUser: OrgUser): Promise<OrgUser | null> {\n const held = await this.roles.assignmentsOf(orgId, orgUser.id);\n return this.uow.run((scope) => this.deleteOrgUserIn(scope, orgId, orgUser, held, SYSTEM_ID));\n }\n\n private async deleteOrgUserIn(\n scope: OrganizationsWriteScope,\n orgId: string,\n orgUser: OrgUser,\n held: { roleKey: string; contentId: string | null }[],\n actorId: string,\n ): Promise<OrgUser | null> {\n const orgRoleKey = held.find((row) => row.contentId === null)?.roleKey ?? MEMBER_ROLE_KEY;\n const deleted = await scope.organizations.deleteOrgUser(orgId, orgUser.id);\n if (!deleted) {\n return null;\n }\n const events: NewOrganizationEvent[] = [\n organizationEvents.orgUserDeleted.make({\n orgId,\n data: { ...deleted, orgRole: { key: orgRoleKey } },\n metadata: { actorId },\n }),\n ];\n if (orgRoleKey === MEMBER_ROLE_KEY) {\n events.push(organizationEvents.studentDeleted.make({ orgId, data: deleted }));\n }\n await scope.outbox.append(events);\n return deleted;\n }\n\n private async sendInviteEmail(invite: Invite, token: string, role: OrgRole): Promise<void> {\n if (!this.mailer || !this.inviteUrls) {\n throw new Error('invite delivery is not configured (mailer / invite urls missing)');\n }\n const learner = role.permissions.length === 0;\n const base = learner\n ? `${this.inviteUrls.studentPortalUrl}/welcome`\n : `${this.inviteUrls.adminAppUrl}/invite`;\n const query = learner\n ? new URLSearchParams({ token })\n : new URLSearchParams({ token, email: invite.email });\n const inviteUrl = `${base}?${query.toString()}`;\n if (learner) {\n await this.mailer.send(invite.email, 'studentInvite', {\n inviteUrl,\n studentName: invite.email,\n });\n } else {\n await this.mailer.send(invite.email, 'memberInvite', {\n inviteUrl,\n inviterName: 'Your team',\n role: role.name,\n });\n }\n }\n\n private toMember(row: TeamRow, nameOf: (key: string) => string): Member {\n const orgRoleKey =\n row.status === 'invited' && row.inviteRoleKey\n ? row.inviteRoleKey\n : (row.orgRoleKey ?? MEMBER_ROLE_KEY);\n return {\n id: row.id,\n firstName: row.firstName,\n lastName: row.lastName,\n email: row.email,\n image: row.image,\n orgRole: { key: orgRoleKey, name: nameOf(orgRoleKey) },\n isOwner: row.orgRoleKey === OWNER_ROLE_KEY,\n items: row.items.map((item) => ({ ...item, roleName: nameOf(item.roleKey) })),\n status: row.status,\n joinedAt: row.status === 'active' ? row.joinedAt.toISOString() : null,\n invitedAt: row.invitedAt ? row.invitedAt.toISOString() : null,\n };\n }\n\n private async assignmentViews(orgId: string, orgUserId: string): Promise<AssignmentView[]> {\n const [rows, stored] = await Promise.all([\n this.roles.assignmentsOf(orgId, orgUserId),\n this.roles.storedRoles(orgId),\n ]);\n const roles = this.roleMap(stored);\n const views: AssignmentView[] = rows.map((row) => ({\n orgUserId,\n roleKey: row.roleKey,\n roleName: roles.get(row.roleKey)?.name ?? row.roleKey,\n contentId: row.contentId,\n contentType: row.contentType,\n }));\n if (!rows.some((row) => row.contentId === null)) {\n views.unshift({\n orgUserId,\n roleKey: MEMBER_ROLE_KEY,\n roleName: roles.get(MEMBER_ROLE_KEY)?.name ?? 'Member',\n contentId: null,\n contentType: null,\n });\n }\n return views;\n }\n\n private roleMap(stored: readonly StoredOrgRole[]): Map<string, OrgRole> {\n return new Map(resolveOrgRoles(this.catalog, stored).map((role) => [role.key, role]));\n }\n\n private resolveIn(stored: readonly StoredOrgRole[], key: string): OrgRole | null {\n return resolveRole(\n key,\n this.catalog,\n stored.find((row) => row.key === key),\n );\n }\n\n private async findRole(orgId: string, key: string): Promise<OrgRole> {\n const role = this.resolveIn(await this.roles.storedRoles(orgId), key);\n if (!role) {\n throw new NotFoundError('Role', key);\n }\n return role;\n }\n\n private async relockedActor(scope: OrganizationsWriteScope, actor: Actor): Promise<Actor> {\n if (actor.id === SYSTEM_ID) {\n return actor;\n }\n const facts = await scope.roles.actorFacts(actor.orgId, actor.id);\n if (!facts || facts.status !== 'active') {\n throw new ForbiddenError('You are no longer an active member of this organization');\n }\n const fresh = resolvePermissions({\n assignments: facts.assignments,\n roles: this.roleMap(facts.storedRoles),\n });\n return {\n ...actor,\n permissions: {\n has: (p, on) => fresh.has(p, on) && actor.permissions.has(p, on),\n require: (p, on) => {\n if (!(fresh.has(p, on) && actor.permissions.has(p, on))) {\n throw new ForbiddenError(`You need ${p}`);\n }\n },\n anywhere: (p) => fresh.anywhere(p) && actor.permissions.anywhere(p),\n where: (p) => actor.permissions.where(p),\n roleOn: (on) => fresh.roleOn(on),\n isOwner: () => fresh.isOwner() && actor.permissions.isOwner(),\n },\n };\n }\n\n private itemPermissions(role: OrgRole | null, type: ContentType): Permission[] {\n if (!role || !role.levels.includes(type)) {\n return [];\n }\n return role.permissions.filter((p) => permissionAbout(p) === type);\n }\n\n private assertHolds(actor: Actor, permissions: readonly Permission[]): void {\n const missing = [...new Set(permissions)].filter((p) => !actor.permissions.has(p));\n if (missing.length > 0) {\n this.logger.warn('refused: editor lacks permissions', {\n orgId: actor.orgId,\n actorId: actor.id,\n missing,\n });\n throw new ForbiddenError(`You need ${missing.join(', ')}`);\n }\n }\n\n private assertHoldsOn(actor: Actor, permissions: readonly Permission[], contentId: string): void {\n const missing = [...new Set(permissions)].filter((p) => !actor.permissions.has(p, contentId));\n if (missing.length > 0) {\n this.logger.warn('refused: editor lacks permissions on the item', {\n orgId: actor.orgId,\n actorId: actor.id,\n contentId,\n missing,\n });\n throw new ForbiddenError(`You need ${missing.join(', ')} there`);\n }\n }\n\n private assertNotSelf(actor: Actor, orgUserId: string): void {\n if (actor.id === orgUserId) {\n this.logger.warn('refused: changing own roles', { orgId: actor.orgId, orgUserId });\n throw new ForbiddenError(\"You can't change your own roles\");\n }\n }\n\n private assertUniqueName(stored: readonly StoredOrgRole[], key: string, name: string): void {\n const wanted = name.trim().toLowerCase();\n if (wanted.length === 0) {\n throw new OrganizationRuleError('A role needs a name');\n }\n const taken = resolveOrgRoles(this.catalog, stored).some(\n (role) => role.key !== key && role.name.trim().toLowerCase() === wanted,\n );\n if (taken) {\n throw new OrganizationRuleError(`A role named ${name} already exists`);\n }\n }\n\n private parseLevels(levels: Level[]): Level[] {\n if (levels.length === 0) {\n throw new OrganizationRuleError('A role needs at least one level');\n }\n for (const level of levels) {\n if (!levelSchema.safeParse(level).success) {\n throw new OrganizationRuleError(`Unknown level ${String(level)}`);\n }\n }\n return [...new Set(levels)];\n }\n\n private assertFits(row: StoredOrgRole): void {\n const base = this.catalog.get(row.base);\n const levels = row.levels ?? base?.levels ?? [];\n const misfits = Object.entries(row.permissions)\n .filter(([, held]) => held)\n .map(([p]) => p as Permission)\n .filter((p) => !fitsLevels(p, levels));\n if (misfits.length > 0) {\n throw new OrganizationRuleError(\n `${misfits.join(', ')} ${misfits.length === 1 ? \"doesn't\" : \"don't\"} fit the role's levels`,\n );\n }\n }\n\n private async assertLevelsNotInUse(\n roles: RolesRepository,\n orgId: string,\n before: OrgRole,\n after: OrgRole,\n ): Promise<void> {\n const removed = before.levels.filter((level) => !after.levels.includes(level));\n for (const level of removed) {\n const count = await roles.countHolders(orgId, before.key, level);\n if (count > 0) {\n throw new OrganizationRuleError(\n `${before.name} is given on ${level === 'org' ? 'the organization' : `a ${level}`}; remove those first`,\n );\n }\n }\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { Permission } from "../types/schemas/organizations.js";
2
+ import { ContentId } from "../types/organizations.js";
3
+ import "../types/index.js";
4
+ import { Actor, RoleRef } from "../shared/actor.js";
5
+ //#region src/organizations/testing.d.ts
6
+ interface ActorShape {
7
+ id?: string;
8
+ orgId?: string;
9
+ org?: readonly Permission[];
10
+ content?: Readonly<Record<ContentId, readonly Permission[]>>;
11
+ owner?: boolean;
12
+ orgRole?: RoleRef;
13
+ contentRoles?: Readonly<Record<ContentId, RoleRef>>;
14
+ }
15
+ declare function actorWith(shape?: ActorShape): Actor;
16
+ //#endregion
17
+ export { ActorShape, actorWith };
18
+ //# sourceMappingURL=testing.d.ts.map
@@ -0,0 +1,37 @@
1
+ import { ALL_PERMISSIONS, withImplied } from "./permissions.js";
2
+ import { ResolvedPermissions } from "./effective-permissions.js";
3
+ //#region src/organizations/testing.ts
4
+ const MEMBER = {
5
+ key: "member",
6
+ name: "Member",
7
+ team: false
8
+ };
9
+ function actorWith(shape = {}) {
10
+ const org = shape.owner ? new Set(ALL_PERMISSIONS) : withImplied(shape.org ?? []);
11
+ const content = new Map(Object.entries(shape.content ?? {}).map(([id, permissions]) => [id, {
12
+ permissions: withImplied(permissions),
13
+ role: shape.contentRoles?.[id] ?? {
14
+ key: "item_role",
15
+ name: "Item role",
16
+ team: true
17
+ }
18
+ }]));
19
+ const orgRole = shape.orgRole ?? (shape.owner ? {
20
+ key: "owner",
21
+ name: "Owner",
22
+ team: true
23
+ } : (shape.org?.length ?? 0) > 0 ? {
24
+ key: "org_role",
25
+ name: "Org role",
26
+ team: true
27
+ } : MEMBER);
28
+ return {
29
+ id: shape.id ?? "orm_actor",
30
+ orgId: shape.orgId ?? "org_1",
31
+ permissions: new ResolvedPermissions(org, orgRole, content, shape.owner ?? false)
32
+ };
33
+ }
34
+ //#endregion
35
+ export { actorWith };
36
+
37
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.js","names":[],"sources":["../../src/organizations/testing.ts"],"sourcesContent":["import type { ContentId, Permission } from '../types/index.js';\nimport type { Actor, RoleRef } from '../shared/actor.js';\nimport { ALL_PERMISSIONS, withImplied } from './permissions.js';\nimport { ResolvedPermissions } from './effective-permissions.js';\n\nexport interface ActorShape {\n id?: string;\n orgId?: string;\n org?: readonly Permission[];\n content?: Readonly<Record<ContentId, readonly Permission[]>>;\n owner?: boolean;\n orgRole?: RoleRef;\n contentRoles?: Readonly<Record<ContentId, RoleRef>>;\n}\n\nconst MEMBER: RoleRef = { key: 'member', name: 'Member', team: false };\n\nexport function actorWith(shape: ActorShape = {}): Actor {\n const org = shape.owner ? new Set(ALL_PERMISSIONS) : withImplied(shape.org ?? []);\n const content = new Map(\n Object.entries(shape.content ?? {}).map(([id, permissions]) => [\n id,\n {\n permissions: withImplied(permissions),\n role: shape.contentRoles?.[id] ?? { key: 'item_role', name: 'Item role', team: true },\n },\n ]),\n );\n const orgRole =\n shape.orgRole ??\n (shape.owner\n ? { key: 'owner', name: 'Owner', team: true }\n : (shape.org?.length ?? 0) > 0\n ? { key: 'org_role', name: 'Org role', team: true }\n : MEMBER);\n return {\n id: shape.id ?? 'orm_actor',\n orgId: shape.orgId ?? 'org_1',\n permissions: new ResolvedPermissions(org, orgRole, content, shape.owner ?? false),\n };\n}\n"],"mappings":";;;AAeA,MAAM,SAAkB;CAAE,KAAK;CAAU,MAAM;CAAU,MAAM;AAAM;AAErE,SAAgB,UAAU,QAAoB,CAAC,GAAU;CACvD,MAAM,MAAM,MAAM,QAAQ,IAAI,IAAI,eAAe,IAAI,YAAY,MAAM,OAAO,CAAC,CAAC;CAChF,MAAM,UAAU,IAAI,IAClB,OAAO,QAAQ,MAAM,WAAW,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,iBAAiB,CAC7D,IACA;EACE,aAAa,YAAY,WAAW;EACpC,MAAM,MAAM,eAAe,OAAO;GAAE,KAAK;GAAa,MAAM;GAAa,MAAM;EAAK;CACtF,CACF,CAAC,CACH;CACA,MAAM,UACJ,MAAM,YACL,MAAM,QACH;EAAE,KAAK;EAAS,MAAM;EAAS,MAAM;CAAK,KACzC,MAAM,KAAK,UAAU,KAAK,IACzB;EAAE,KAAK;EAAY,MAAM;EAAY,MAAM;CAAK,IAChD;CACR,OAAO;EACL,IAAI,MAAM,MAAM;EAChB,OAAO,MAAM,SAAS;EACtB,aAAa,IAAI,oBAAoB,KAAK,SAAS,SAAS,MAAM,SAAS,KAAK;CAClF;AACF"}
@@ -1,5 +1,5 @@
1
1
  import { ProvisionUserInput } from "../types/schemas/identity.js";
2
- import { AcceptInviteInput, AddOrgUserInput, CreateInviteInput, CreateOrgUserInput, InviteRole, NewOrganizationInput, OrgUserStatus, Organization, UpdateOrgUserInput } from "../types/schemas/organizations.js";
2
+ import { AcceptInviteInput, AddOrgUserInput, CreateInviteInput, CreateOrgUserInput, NewOrganizationInput, OrgUserStatus, UpdateOrgUserInput } from "../types/schemas/organizations.js";
3
3
  import { InviteId, OrgUserId, OrganizationId } from "../types/organizations.js";
4
4
  import "../types/index.js";
5
5
  //#region src/organizations/types.d.ts
@@ -10,21 +10,10 @@ interface CreateOrganizationInput {
10
10
  logo?: string;
11
11
  ownerId: string;
12
12
  }
13
- type UpdateOrganizationInput = Partial<Omit<Organization, 'createdAt,updatedAt'>>;
14
- interface LinkOrgUserInput {
15
- orgId: string;
16
- userId: string;
17
- role: string;
18
- }
19
- interface UnlinkOrgUserInput {
20
- orgExternalId: string;
21
- userExternalId: string;
22
- }
23
- interface ResendStudentInviteInput {
24
- orgId: string;
25
- orgUserId: string;
26
- inviterUserId: string;
13
+ interface UpdateOrganizationInput {
14
+ name?: string | undefined;
15
+ slug?: string | undefined;
27
16
  }
28
17
  //#endregion
29
- export { type AcceptInviteInput, type AddOrgUserInput, type CreateInviteInput, type CreateOrgUserInput, CreateOrganizationInput, type InviteId, type InviteRole, LinkOrgUserInput, type NewOrganizationInput, type OrgUserId, type OrgUserStatus, type OrganizationId, type ProvisionUserInput, ResendStudentInviteInput, UnlinkOrgUserInput, type UpdateOrgUserInput, UpdateOrganizationInput };
18
+ export { type AcceptInviteInput, type AddOrgUserInput, type CreateInviteInput, type CreateOrgUserInput, CreateOrganizationInput, type InviteId, type NewOrganizationInput, type OrgUserId, type OrgUserStatus, type OrganizationId, type ProvisionUserInput, type UpdateOrgUserInput, UpdateOrganizationInput };
30
19
  //# sourceMappingURL=types.d.ts.map
@@ -7,7 +7,7 @@ declare const progressEvents: {
7
7
  id: string;
8
8
  orgId: string;
9
9
  orgUserId: string;
10
- targetType: "activity" | "module" | "course";
10
+ targetType: "course" | "activity" | "module";
11
11
  targetId: string;
12
12
  startedAt: Date;
13
13
  position: JsonValue;
@@ -18,7 +18,7 @@ declare const progressEvents: {
18
18
  id: string;
19
19
  orgId: string;
20
20
  orgUserId: string;
21
- targetType: "activity" | "module" | "course";
21
+ targetType: "course" | "activity" | "module";
22
22
  targetId: string;
23
23
  startedAt: unknown;
24
24
  position: unknown;
@@ -26,25 +26,25 @@ declare const progressEvents: {
26
26
  createdAt: unknown;
27
27
  updatedAt: unknown;
28
28
  }, import("zod").ZodObject<{
29
- id: import("zod").ZodString;
30
29
  orgId: import("zod").ZodString;
30
+ id: import("zod").ZodString;
31
+ createdAt: import("zod").ZodCoercedDate<unknown>;
32
+ updatedAt: import("zod").ZodCoercedDate<unknown>;
33
+ startedAt: import("zod").ZodCoercedDate<unknown>;
31
34
  orgUserId: import("zod").ZodString;
32
35
  targetType: import("zod").ZodEnum<{
36
+ course: "course";
33
37
  activity: "activity";
34
38
  module: "module";
35
- course: "course";
36
39
  }>;
37
40
  targetId: import("zod").ZodString;
38
- startedAt: import("zod").ZodCoercedDate<unknown>;
39
41
  completedAt: import("zod").ZodNullable<import("zod").ZodCoercedDate<unknown>>;
40
- createdAt: import("zod").ZodCoercedDate<unknown>;
41
- updatedAt: import("zod").ZodCoercedDate<unknown>;
42
42
  }, import("zod/v4/core").$strip>>;
43
43
  progressCompleted: RuntimeEventDefinition<VersionedDomainEvent<{
44
44
  id: string;
45
45
  orgId: string;
46
46
  orgUserId: string;
47
- targetType: "activity" | "module" | "course";
47
+ targetType: "course" | "activity" | "module";
48
48
  targetId: string;
49
49
  startedAt: Date;
50
50
  position: JsonValue;
@@ -55,7 +55,7 @@ declare const progressEvents: {
55
55
  id: string;
56
56
  orgId: string;
57
57
  orgUserId: string;
58
- targetType: "activity" | "module" | "course";
58
+ targetType: "course" | "activity" | "module";
59
59
  targetId: string;
60
60
  startedAt: unknown;
61
61
  position: unknown;
@@ -63,19 +63,19 @@ declare const progressEvents: {
63
63
  createdAt: unknown;
64
64
  updatedAt: unknown;
65
65
  }, import("zod").ZodObject<{
66
- id: import("zod").ZodString;
67
66
  orgId: import("zod").ZodString;
67
+ id: import("zod").ZodString;
68
+ createdAt: import("zod").ZodCoercedDate<unknown>;
69
+ updatedAt: import("zod").ZodCoercedDate<unknown>;
70
+ startedAt: import("zod").ZodCoercedDate<unknown>;
68
71
  orgUserId: import("zod").ZodString;
69
72
  targetType: import("zod").ZodEnum<{
73
+ course: "course";
70
74
  activity: "activity";
71
75
  module: "module";
72
- course: "course";
73
76
  }>;
74
77
  targetId: import("zod").ZodString;
75
- startedAt: import("zod").ZodCoercedDate<unknown>;
76
78
  completedAt: import("zod").ZodNullable<import("zod").ZodCoercedDate<unknown>>;
77
- createdAt: import("zod").ZodCoercedDate<unknown>;
78
- updatedAt: import("zod").ZodCoercedDate<unknown>;
79
79
  }, import("zod/v4/core").$strip>>;
80
80
  };
81
81
  type ProgressStarted = EventOf<typeof progressEvents.progressStarted>;
@@ -3,6 +3,6 @@ import { ProgressId } from "../types/progress.js";
3
3
  import { ProgressCompleted, ProgressEvent, ProgressStarted, progressEvents } from "./events.js";
4
4
  import "./model.js";
5
5
  import "./types.js";
6
- import { ContentAccessReader, ProgressRepository, ProgressService, ProgressUnitOfWork } from "./ports.js";
6
+ import { ContentAccessReader, CourseReader, ProgressRepository, ProgressService, ProgressUnitOfWork, SystemActors } from "./ports.js";
7
7
  import { ProgressServiceImpl } from "./service.js";
8
- export { type ContentAccessReader, type ProgressCompleted, type ProgressEvent, type ProgressId, type ProgressRecord, type ProgressReportItem, type ProgressRepository, type ProgressService, ProgressServiceImpl, type ProgressStarted, type ProgressTarget, type ProgressTargetType, type ProgressUnitOfWork, type ReportProgressInput, progressEvents };
8
+ export { type ContentAccessReader, type CourseReader, type ProgressCompleted, type ProgressEvent, type ProgressId, type ProgressRecord, type ProgressReportItem, type ProgressRepository, type ProgressService, ProgressServiceImpl, type ProgressStarted, type ProgressTarget, type ProgressTargetType, type ProgressUnitOfWork, type ReportProgressInput, type SystemActors, progressEvents };
@@ -1,13 +1,17 @@
1
1
  import { ProgressRecord, ProgressTarget, ReportProgressInput } from "../types/schemas/progress.js";
2
+ import { Actor } from "../shared/actor.js";
2
3
  import { OutboxAppender, UnitOfWork } from "../shared/ports.js";
4
+ import { CourseManagementService } from "../content/ports.js";
5
+ import "../content/index.js";
3
6
  import "./model.js";
4
7
  import "./types.js";
5
8
  //#region src/progress/ports.d.ts
6
9
  interface ProgressService {
7
10
  /** Process a usage report: ensure the record, apply position, evaluate
8
11
  * completion (activity rule, then module/course against current structure),
9
- * emit events. Returns the activity's record after the decision. */
10
- report(orgId: string, input: ReportProgressInput): Promise<ProgressRecord>;
12
+ * emit events. Returns the activity's record after the decision. The actor
13
+ * reports on themselves. */
14
+ report(input: ReportProgressInput, actor: Actor): Promise<ProgressRecord>;
11
15
  /** Fetch the record for a single (student, target), or null. */
12
16
  get(orgId: string, target: ProgressTarget): Promise<ProgressRecord | null>;
13
17
  /** Records for a set of target ids — the read the reporting layer composes. */
@@ -30,6 +34,10 @@ interface ProgressRepository {
30
34
  interface ContentAccessReader {
31
35
  hasAccess(orgId: string, orgUserId: string, contentId: string): Promise<boolean>;
32
36
  }
37
+ type CourseReader = Pick<CourseManagementService, 'getActivity' | 'findCourse' | 'listCourseModules' | 'listCourseActivities'>;
38
+ interface SystemActors {
39
+ systemActor(orgId: string): Actor;
40
+ }
33
41
  /** Writes that emit events run through this scope so row + outbox entry commit
34
42
  * in one transaction. */
35
43
  interface ProgressWriteScope {
@@ -38,5 +46,5 @@ interface ProgressWriteScope {
38
46
  }
39
47
  type ProgressUnitOfWork = UnitOfWork<ProgressWriteScope>;
40
48
  //#endregion
41
- export { ContentAccessReader, ProgressRepository, ProgressService, ProgressUnitOfWork, ProgressWriteScope };
49
+ export { ContentAccessReader, CourseReader, ProgressRepository, ProgressService, ProgressUnitOfWork, ProgressWriteScope, SystemActors };
42
50
  //# sourceMappingURL=ports.d.ts.map
@@ -1,16 +1,16 @@
1
1
  import { Logger } from "../types/ports.js";
2
2
  import { ProgressRecord, ProgressTarget, ReportProgressInput } from "../types/schemas/progress.js";
3
+ import { Actor } from "../shared/actor.js";
3
4
  import "../shared/ports.js";
4
- import { CourseManagementService } from "../content/ports.js";
5
- import "../content/index.js";
6
5
  import "./model.js";
7
6
  import "./types.js";
8
- import { ContentAccessReader, ProgressRepository, ProgressService, ProgressUnitOfWork } from "./ports.js";
7
+ import { ContentAccessReader, CourseReader, ProgressRepository, ProgressService, ProgressUnitOfWork, SystemActors } from "./ports.js";
9
8
  //#region src/progress/service.d.ts
10
9
  type ProgressServiceParams = {
11
10
  repo: ProgressRepository;
12
- content: CourseManagementService;
11
+ content: CourseReader;
13
12
  access: ContentAccessReader;
13
+ system: SystemActors;
14
14
  uow: ProgressUnitOfWork;
15
15
  logger?: Logger;
16
16
  };
@@ -18,10 +18,11 @@ declare class ProgressServiceImpl implements ProgressService {
18
18
  private readonly repo;
19
19
  private readonly content;
20
20
  private readonly access;
21
+ private readonly system;
21
22
  private readonly uow;
22
23
  private readonly logger;
23
24
  constructor(params: ProgressServiceParams);
24
- report(orgId: string, input: ReportProgressInput): Promise<ProgressRecord>;
25
+ report(report: ReportProgressInput, actor: Actor): Promise<ProgressRecord>;
25
26
  private ensureActivityRecord;
26
27
  /** After an activity completes: newly-complete containers get their records
27
28
  * (created complete) and a progress.completed event — same transaction. */