@headless-lms/server 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (351) hide show
  1. package/dist/_virtual/_rolldown/runtime.js +5 -0
  2. package/dist/app/community-mail.d.ts +24 -0
  3. package/dist/app/community-mail.js +92 -0
  4. package/dist/app/community-mail.js.map +1 -0
  5. package/dist/app/container.d.ts +40 -19
  6. package/dist/app/container.js +294 -45
  7. package/dist/app/container.js.map +1 -1
  8. package/dist/app/mcp-endpoints.d.ts +13 -0
  9. package/dist/app/mcp-endpoints.js +25 -0
  10. package/dist/app/mcp-endpoints.js.map +1 -0
  11. package/dist/app/notification-channels.d.ts +47 -0
  12. package/dist/app/notification-channels.js +174 -0
  13. package/dist/app/notification-channels.js.map +1 -0
  14. package/dist/app/notification-reads.d.ts +23 -0
  15. package/dist/app/notification-reads.js +95 -0
  16. package/dist/app/notification-reads.js.map +1 -0
  17. package/dist/http/config.d.ts +2 -1
  18. package/dist/http/errors.d.ts +15 -0
  19. package/dist/http/errors.js +162 -0
  20. package/dist/http/errors.js.map +1 -0
  21. package/dist/http/mcp/catalogue.d.ts +37 -0
  22. package/dist/http/mcp/catalogue.js +69 -0
  23. package/dist/http/mcp/catalogue.js.map +1 -0
  24. package/dist/http/mcp/challenge.d.ts +7 -0
  25. package/dist/http/mcp/challenge.js +35 -0
  26. package/dist/http/mcp/challenge.js.map +1 -0
  27. package/dist/http/mcp/input-schema.d.ts +7 -0
  28. package/dist/http/mcp/input-schema.js +28 -0
  29. package/dist/http/mcp/input-schema.js.map +1 -0
  30. package/dist/http/mcp/route.d.ts +8 -0
  31. package/dist/http/mcp/route.js +75 -0
  32. package/dist/http/mcp/route.js.map +1 -0
  33. package/dist/http/mcp/server.d.ts +8 -0
  34. package/dist/http/mcp/server.js +31 -0
  35. package/dist/http/mcp/server.js.map +1 -0
  36. package/dist/http/mcp/test-support.d.ts +55 -0
  37. package/dist/http/mcp/test-support.js +151 -0
  38. package/dist/http/mcp/test-support.js.map +1 -0
  39. package/dist/http/mcp/tool.d.ts +48 -0
  40. package/dist/http/mcp/tool.js +58 -0
  41. package/dist/http/mcp/tool.js.map +1 -0
  42. package/dist/http/mcp/tools/automations.d.ts +6 -0
  43. package/dist/http/mcp/tools/automations.js +120 -0
  44. package/dist/http/mcp/tools/automations.js.map +1 -0
  45. package/dist/http/mcp/tools/catalog.d.ts +6 -0
  46. package/dist/http/mcp/tools/catalog.js +200 -0
  47. package/dist/http/mcp/tools/catalog.js.map +1 -0
  48. package/dist/http/mcp/tools/comments.d.ts +6 -0
  49. package/dist/http/mcp/tools/comments.js +70 -0
  50. package/dist/http/mcp/tools/comments.js.map +1 -0
  51. package/dist/http/mcp/tools/communities.d.ts +6 -0
  52. package/dist/http/mcp/tools/communities.js +175 -0
  53. package/dist/http/mcp/tools/communities.js.map +1 -0
  54. package/dist/http/mcp/tools/community-members.d.ts +6 -0
  55. package/dist/http/mcp/tools/community-members.js +89 -0
  56. package/dist/http/mcp/tools/community-members.js.map +1 -0
  57. package/dist/http/mcp/tools/community-moderation.d.ts +6 -0
  58. package/dist/http/mcp/tools/community-moderation.js +144 -0
  59. package/dist/http/mcp/tools/community-moderation.js.map +1 -0
  60. package/dist/http/mcp/tools/courses.d.ts +6 -0
  61. package/dist/http/mcp/tools/courses.js +121 -0
  62. package/dist/http/mcp/tools/courses.js.map +1 -0
  63. package/dist/http/mcp/tools/index.d.ts +6 -0
  64. package/dist/http/mcp/tools/index.js +23 -0
  65. package/dist/http/mcp/tools/index.js.map +1 -0
  66. package/dist/http/mcp/tools/people.d.ts +6 -0
  67. package/dist/http/mcp/tools/people.js +145 -0
  68. package/dist/http/mcp/tools/people.js.map +1 -0
  69. package/dist/http/plugins/auth.js +13 -5
  70. package/dist/http/plugins/auth.js.map +1 -1
  71. package/dist/http/plugins/error-handler.d.ts +2 -1
  72. package/dist/http/plugins/error-handler.js +51 -65
  73. package/dist/http/plugins/error-handler.js.map +1 -1
  74. package/dist/http/plugins/openapi.js +49 -2
  75. package/dist/http/plugins/openapi.js.map +1 -1
  76. package/dist/http/routes/access.d.ts +7 -0
  77. package/dist/http/routes/access.js +140 -0
  78. package/dist/http/routes/access.js.map +1 -0
  79. package/dist/http/routes/activities.js +68 -23
  80. package/dist/http/routes/activities.js.map +1 -1
  81. package/dist/http/routes/assets.js +60 -55
  82. package/dist/http/routes/assets.js.map +1 -1
  83. package/dist/http/routes/automations.js +44 -24
  84. package/dist/http/routes/automations.js.map +1 -1
  85. package/dist/http/routes/bundles.js +29 -18
  86. package/dist/http/routes/bundles.js.map +1 -1
  87. package/dist/http/routes/communities.d.ts +7 -0
  88. package/dist/http/routes/communities.js +522 -0
  89. package/dist/http/routes/communities.js.map +1 -0
  90. package/dist/http/routes/community-moderation.d.ts +7 -0
  91. package/dist/http/routes/community-moderation.js +425 -0
  92. package/dist/http/routes/community-moderation.js.map +1 -0
  93. package/dist/http/routes/course-analytics.js +9 -7
  94. package/dist/http/routes/course-analytics.js.map +1 -1
  95. package/dist/http/routes/courses.js +30 -15
  96. package/dist/http/routes/courses.js.map +1 -1
  97. package/dist/http/routes/dashboard.js +15 -6
  98. package/dist/http/routes/dashboard.js.map +1 -1
  99. package/dist/http/routes/discussion.js +34 -26
  100. package/dist/http/routes/discussion.js.map +1 -1
  101. package/dist/http/routes/downloads.js +48 -30
  102. package/dist/http/routes/downloads.js.map +1 -1
  103. package/dist/http/routes/entitlements.js +16 -7
  104. package/dist/http/routes/entitlements.js.map +1 -1
  105. package/dist/http/routes/integrations.js +38 -41
  106. package/dist/http/routes/integrations.js.map +1 -1
  107. package/dist/http/routes/member/actor.d.ts +14 -0
  108. package/dist/http/routes/member/actor.js +11 -0
  109. package/dist/http/routes/member/actor.js.map +1 -0
  110. package/dist/http/routes/member/assets.d.ts +7 -0
  111. package/dist/http/routes/member/assets.js +79 -0
  112. package/dist/http/routes/member/assets.js.map +1 -0
  113. package/dist/http/routes/member/chats.d.ts +7 -0
  114. package/dist/http/routes/member/chats.js +435 -0
  115. package/dist/http/routes/member/chats.js.map +1 -0
  116. package/dist/http/routes/member/comments.d.ts +7 -0
  117. package/dist/http/routes/member/comments.js +187 -0
  118. package/dist/http/routes/member/comments.js.map +1 -0
  119. package/dist/http/routes/member/community.d.ts +7 -0
  120. package/dist/http/routes/member/community.js +312 -0
  121. package/dist/http/routes/member/community.js.map +1 -0
  122. package/dist/http/routes/member/courses.d.ts +7 -0
  123. package/dist/http/routes/member/courses.js +101 -0
  124. package/dist/http/routes/member/courses.js.map +1 -0
  125. package/dist/http/routes/member/devices.d.ts +7 -0
  126. package/dist/http/routes/member/devices.js +98 -0
  127. package/dist/http/routes/member/devices.js.map +1 -0
  128. package/dist/http/routes/member/downloads.d.ts +7 -0
  129. package/dist/http/routes/member/downloads.js +56 -0
  130. package/dist/http/routes/member/downloads.js.map +1 -0
  131. package/dist/http/routes/member/files.d.ts +7 -0
  132. package/dist/http/routes/member/files.js +184 -0
  133. package/dist/http/routes/member/files.js.map +1 -0
  134. package/dist/http/routes/member/index.d.ts +7 -0
  135. package/dist/http/routes/member/index.js +35 -0
  136. package/dist/http/routes/member/index.js.map +1 -0
  137. package/dist/http/routes/member/notification-settings.d.ts +7 -0
  138. package/dist/http/routes/member/notification-settings.js +108 -0
  139. package/dist/http/routes/member/notification-settings.js.map +1 -0
  140. package/dist/http/routes/member/notifications.d.ts +7 -0
  141. package/dist/http/routes/member/notifications.js +125 -0
  142. package/dist/http/routes/member/notifications.js.map +1 -0
  143. package/dist/http/routes/member/org.d.ts +7 -0
  144. package/dist/http/routes/member/org.js +56 -0
  145. package/dist/http/routes/member/org.js.map +1 -0
  146. package/dist/http/routes/member/posts.d.ts +7 -0
  147. package/dist/http/routes/member/posts.js +612 -0
  148. package/dist/http/routes/member/posts.js.map +1 -0
  149. package/dist/http/routes/member/profile.d.ts +7 -0
  150. package/dist/http/routes/member/profile.js +48 -0
  151. package/dist/http/routes/member/profile.js.map +1 -0
  152. package/dist/http/routes/member/progress.d.ts +7 -0
  153. package/dist/http/routes/member/progress.js +60 -0
  154. package/dist/http/routes/member/progress.js.map +1 -0
  155. package/dist/http/routes/organizations.js +171 -25
  156. package/dist/http/routes/organizations.js.map +1 -1
  157. package/dist/http/routes/students.js +32 -47
  158. package/dist/http/routes/students.js.map +1 -1
  159. package/dist/http/routes/webhooks.js +26 -18
  160. package/dist/http/routes/webhooks.js.map +1 -1
  161. package/dist/http/routes.js +8 -2
  162. package/dist/http/routes.js.map +1 -1
  163. package/dist/http/schemas/access.d.ts +146 -0
  164. package/dist/http/schemas/access.js +36 -0
  165. package/dist/http/schemas/access.js.map +1 -0
  166. package/dist/http/schemas/activities.d.ts +3 -1
  167. package/dist/http/schemas/activities.js +4 -4
  168. package/dist/http/schemas/activities.js.map +1 -1
  169. package/dist/http/schemas/assets.d.ts +79 -80
  170. package/dist/http/schemas/assets.js +23 -13
  171. package/dist/http/schemas/assets.js.map +1 -1
  172. package/dist/http/schemas/automations.d.ts +6 -0
  173. package/dist/http/schemas/bundles.js +6 -6
  174. package/dist/http/schemas/bundles.js.map +1 -1
  175. package/dist/http/schemas/chats.d.ts +409 -0
  176. package/dist/http/schemas/chats.js +56 -0
  177. package/dist/http/schemas/chats.js.map +1 -0
  178. package/dist/http/schemas/community.d.ts +597 -0
  179. package/dist/http/schemas/community.js +132 -0
  180. package/dist/http/schemas/community.js.map +1 -0
  181. package/dist/http/schemas/content.d.ts +4 -2
  182. package/dist/http/schemas/content.js +11 -8
  183. package/dist/http/schemas/content.js.map +1 -1
  184. package/dist/http/schemas/discussion.d.ts +98 -14
  185. package/dist/http/schemas/discussion.js +25 -12
  186. package/dist/http/schemas/discussion.js.map +1 -1
  187. package/dist/http/schemas/downloads.d.ts +11 -5
  188. package/dist/http/schemas/downloads.js +14 -11
  189. package/dist/http/schemas/downloads.js.map +1 -1
  190. package/dist/http/schemas/entitlements.d.ts +6 -6
  191. package/dist/http/schemas/entitlements.js +6 -6
  192. package/dist/http/schemas/entitlements.js.map +1 -1
  193. package/dist/http/schemas/files.d.ts +45 -0
  194. package/dist/http/schemas/files.js +25 -0
  195. package/dist/http/schemas/files.js.map +1 -0
  196. package/dist/http/schemas/index.d.ts +15 -8
  197. package/dist/http/schemas/index.js +15 -8
  198. package/dist/http/schemas/integrations.d.ts +4 -4
  199. package/dist/http/schemas/invites.d.ts +4 -0
  200. package/dist/http/schemas/invites.js +1 -0
  201. package/dist/http/schemas/invites.js.map +1 -1
  202. package/dist/http/schemas/{learn.d.ts → member.d.ts} +31 -18
  203. package/dist/http/schemas/{learn.js → member.js} +17 -13
  204. package/dist/http/schemas/member.js.map +1 -0
  205. package/dist/http/schemas/members.d.ts +35 -2
  206. package/dist/http/schemas/members.js +17 -3
  207. package/dist/http/schemas/members.js.map +1 -1
  208. package/dist/http/schemas/notifications.d.ts +550 -0
  209. package/dist/http/schemas/notifications.js +164 -0
  210. package/dist/http/schemas/notifications.js.map +1 -0
  211. package/dist/http/schemas/posts.d.ts +350 -0
  212. package/dist/http/schemas/posts.js +47 -0
  213. package/dist/http/schemas/posts.js.map +1 -0
  214. package/dist/http/schemas/profile.d.ts +23 -0
  215. package/dist/http/schemas/profile.js +15 -0
  216. package/dist/http/schemas/profile.js.map +1 -0
  217. package/dist/http/schemas/shared.d.ts +32 -1
  218. package/dist/http/schemas/shared.js +20 -2
  219. package/dist/http/schemas/shared.js.map +1 -1
  220. package/dist/http/schemas/students.d.ts +1 -7
  221. package/dist/http/schemas/students.js +1 -7
  222. package/dist/http/schemas/students.js.map +1 -1
  223. package/dist/http/scope.d.ts +3 -1
  224. package/dist/http/scope.js +26 -4
  225. package/dist/http/scope.js.map +1 -1
  226. package/dist/http/server.js +6 -1
  227. package/dist/http/server.js.map +1 -1
  228. package/dist/http/web-bridge.d.ts +1 -1
  229. package/dist/http/web-bridge.js +2 -2
  230. package/dist/http/web-bridge.js.map +1 -1
  231. package/dist/http/well-known.d.ts +7 -0
  232. package/dist/http/well-known.js +22 -0
  233. package/dist/http/well-known.js.map +1 -0
  234. package/dist/index.d.ts +3 -1
  235. package/dist/index.js +4 -2
  236. package/dist/index.js.map +1 -1
  237. package/dist/node_modules/.pnpm/@jridgewell_sourcemap-codec@1.5.5/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.js +78 -0
  238. package/dist/node_modules/.pnpm/@jridgewell_sourcemap-codec@1.5.5/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.js.map +1 -0
  239. package/dist/node_modules/.pnpm/@vitest_expect@2.1.9/node_modules/@vitest/expect/dist/index.js +1267 -0
  240. package/dist/node_modules/.pnpm/@vitest_expect@2.1.9/node_modules/@vitest/expect/dist/index.js.map +1 -0
  241. package/dist/node_modules/.pnpm/@vitest_pretty-format@2.1.9/node_modules/@vitest/pretty-format/dist/index.js +814 -0
  242. package/dist/node_modules/.pnpm/@vitest_pretty-format@2.1.9/node_modules/@vitest/pretty-format/dist/index.js.map +1 -0
  243. package/dist/node_modules/.pnpm/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/chunk-tasks.js +43 -0
  244. package/dist/node_modules/.pnpm/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/chunk-tasks.js.map +1 -0
  245. package/dist/node_modules/.pnpm/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/index.js +527 -0
  246. package/dist/node_modules/.pnpm/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/index.js.map +1 -0
  247. package/dist/node_modules/.pnpm/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/utils.js +2 -0
  248. package/dist/node_modules/.pnpm/@vitest_snapshot@2.1.9/node_modules/@vitest/snapshot/dist/index.js +1737 -0
  249. package/dist/node_modules/.pnpm/@vitest_snapshot@2.1.9/node_modules/@vitest/snapshot/dist/index.js.map +1 -0
  250. package/dist/node_modules/.pnpm/@vitest_spy@2.1.9/node_modules/@vitest/spy/dist/index.js +137 -0
  251. package/dist/node_modules/.pnpm/@vitest_spy@2.1.9/node_modules/@vitest/spy/dist/index.js.map +1 -0
  252. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/chunk-_commonjsHelpers.js +118 -0
  253. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/chunk-_commonjsHelpers.js.map +1 -0
  254. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/diff.js +1222 -0
  255. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/diff.js.map +1 -0
  256. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/error.js +91 -0
  257. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/error.js.map +1 -0
  258. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/helpers.js +113 -0
  259. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/helpers.js.map +1 -0
  260. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/index.js +449 -0
  261. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/index.js.map +1 -0
  262. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/source-map.js +163 -0
  263. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/source-map.js.map +1 -0
  264. package/dist/node_modules/.pnpm/chai@5.3.3/node_modules/chai/index.js +3187 -0
  265. package/dist/node_modules/.pnpm/chai@5.3.3/node_modules/chai/index.js.map +1 -0
  266. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/arguments.js +11 -0
  267. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/arguments.js.map +1 -0
  268. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/array.js +16 -0
  269. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/array.js.map +1 -0
  270. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/bigint.js +11 -0
  271. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/bigint.js.map +1 -0
  272. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/class.js +15 -0
  273. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/class.js.map +1 -0
  274. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/date.js +13 -0
  275. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/date.js.map +1 -0
  276. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/error.js +34 -0
  277. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/error.js.map +1 -0
  278. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/function.js +12 -0
  279. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/function.js.map +1 -0
  280. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/helpers.js +122 -0
  281. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/helpers.js.map +1 -0
  282. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/html.js +39 -0
  283. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/html.js.map +1 -0
  284. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/index.js +99 -0
  285. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/index.js.map +1 -0
  286. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/map.js +25 -0
  287. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/map.js.map +1 -0
  288. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/number.js +14 -0
  289. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/number.js.map +1 -0
  290. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/object.js +21 -0
  291. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/object.js.map +1 -0
  292. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/promise.js +6 -0
  293. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/promise.js.map +1 -0
  294. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/regexp.js +12 -0
  295. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/regexp.js.map +1 -0
  296. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/set.js +18 -0
  297. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/set.js.map +1 -0
  298. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/string.js +24 -0
  299. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/string.js.map +1 -0
  300. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/symbol.js +9 -0
  301. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/symbol.js.map +1 -0
  302. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/typedarray.js +30 -0
  303. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/typedarray.js.map +1 -0
  304. package/dist/node_modules/.pnpm/magic-string@0.30.21/node_modules/magic-string/dist/magic-string.es.js +939 -0
  305. package/dist/node_modules/.pnpm/magic-string@0.30.21/node_modules/magic-string/dist/magic-string.es.js.map +1 -0
  306. package/dist/node_modules/.pnpm/pathe@1.1.2/node_modules/pathe/dist/shared/pathe.ff20891b.js +82 -0
  307. package/dist/node_modules/.pnpm/pathe@1.1.2/node_modules/pathe/dist/shared/pathe.ff20891b.js.map +1 -0
  308. package/dist/node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/chunk-BVHSVHOK.js +82 -0
  309. package/dist/node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/chunk-BVHSVHOK.js.map +1 -0
  310. package/dist/node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/node.js +8 -0
  311. package/dist/node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/node.js.map +1 -0
  312. package/dist/node_modules/.pnpm/tinyspy@3.0.2/node_modules/tinyspy/dist/index.js +83 -0
  313. package/dist/node_modules/.pnpm/tinyspy@3.0.2/node_modules/tinyspy/dist/index.js.map +1 -0
  314. package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/_commonjsHelpers.BFTU3MAI.js +9 -0
  315. package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/_commonjsHelpers.BFTU3MAI.js.map +1 -0
  316. package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/date.W2xKR2qe.js +51 -0
  317. package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/date.W2xKR2qe.js.map +1 -0
  318. package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/utils.C8RiOc4B.js +48 -0
  319. package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/utils.C8RiOc4B.js.map +1 -0
  320. package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/vi.DgezovHB.js +2426 -0
  321. package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/vi.DgezovHB.js.map +1 -0
  322. package/dist/packages/server/package.js +6 -0
  323. package/dist/packages/server/package.js.map +1 -0
  324. package/package.json +9 -7
  325. package/dist/app/notifications.d.ts +0 -7
  326. package/dist/app/notifications.js +0 -26
  327. package/dist/app/notifications.js.map +0 -1
  328. package/dist/http/routes/learn/assets.d.ts +0 -8
  329. package/dist/http/routes/learn/assets.js +0 -32
  330. package/dist/http/routes/learn/assets.js.map +0 -1
  331. package/dist/http/routes/learn/comments.d.ts +0 -7
  332. package/dist/http/routes/learn/comments.js +0 -175
  333. package/dist/http/routes/learn/comments.js.map +0 -1
  334. package/dist/http/routes/learn/courses.d.ts +0 -7
  335. package/dist/http/routes/learn/courses.js +0 -98
  336. package/dist/http/routes/learn/courses.js.map +0 -1
  337. package/dist/http/routes/learn/downloads.d.ts +0 -7
  338. package/dist/http/routes/learn/downloads.js +0 -76
  339. package/dist/http/routes/learn/downloads.js.map +0 -1
  340. package/dist/http/routes/learn/index.d.ts +0 -7
  341. package/dist/http/routes/learn/index.js +0 -19
  342. package/dist/http/routes/learn/index.js.map +0 -1
  343. package/dist/http/routes/learn/org.d.ts +0 -7
  344. package/dist/http/routes/learn/org.js +0 -50
  345. package/dist/http/routes/learn/org.js.map +0 -1
  346. package/dist/http/routes/learn/progress.d.ts +0 -7
  347. package/dist/http/routes/learn/progress.js +0 -63
  348. package/dist/http/routes/learn/progress.js.map +0 -1
  349. package/dist/http/schemas/learn.js.map +0 -1
  350. package/dist/package.js +0 -6
  351. package/dist/package.js.map +0 -1
@@ -1,13 +1,13 @@
1
+ import { refusals } from "../schemas/shared.js";
1
2
  import { resolveScope } from "../scope.js";
2
- import { ErrorBody } from "../schemas/shared.js";
3
- import { Student, StudentAnalytics, StudentIdParam, StudentsPage, StudentsQuery, UpdateStudentBody } from "../schemas/students.js";
3
+ import { Student, StudentAnalytics, StudentIdParam, StudentsPage, StudentsQuery } from "../schemas/students.js";
4
4
  import "../schemas/index.js";
5
- import { NotFoundError } from "@headless-lms/core/shared/errors";
6
5
  import { z } from "zod";
6
+ import { NotFoundError } from "@headless-lms/core/shared/errors";
7
7
  //#region src/http/routes/students.ts
8
8
  async function studentsRoutes(app, container) {
9
9
  const r = app.withTypeProvider();
10
- const students = container.reporting.students;
10
+ const students = container.reporting.admin;
11
11
  r.route({
12
12
  method: "GET",
13
13
  url: "/api/students",
@@ -15,13 +15,17 @@ async function studentsRoutes(app, container) {
15
15
  schema: {
16
16
  operationId: "listStudents",
17
17
  tags: ["Organizations"],
18
- summary: "List students",
18
+ summary: "List students (staff)",
19
+ description: "Staff only (owner, admin, instructor).",
19
20
  querystring: StudentsQuery,
20
- response: { 200: StudentsPage }
21
+ response: {
22
+ 200: StudentsPage,
23
+ ...refusals(403)
24
+ }
21
25
  },
22
26
  handler: async (req) => {
23
27
  const scope = await resolveScope(container, req);
24
- return students.list(scope.orgId, req.query);
28
+ return students.listStudents(scope.orgId, req.query);
25
29
  }
26
30
  });
27
31
  r.route({
@@ -31,16 +35,17 @@ async function studentsRoutes(app, container) {
31
35
  schema: {
32
36
  operationId: "getStudent",
33
37
  tags: ["Organizations"],
34
- summary: "Get a student by id",
38
+ summary: "Get a student (staff)",
39
+ description: "Staff only (owner, admin, instructor).",
35
40
  params: StudentIdParam,
36
41
  response: {
37
42
  200: Student,
38
- 404: ErrorBody
43
+ ...refusals(403, 404)
39
44
  }
40
45
  },
41
46
  handler: async (req) => {
42
47
  const scope = await resolveScope(container, req);
43
- const student = await students.get(scope.orgId, req.params.id);
48
+ const student = await students.getStudent(scope.orgId, req.params.id);
44
49
  if (!student) throw new NotFoundError("Student", req.params.id);
45
50
  return student;
46
51
  }
@@ -52,48 +57,21 @@ async function studentsRoutes(app, container) {
52
57
  schema: {
53
58
  operationId: "getStudentAnalytics",
54
59
  tags: ["Reporting"],
55
- summary: "A student's learner record: per-course progress and completion",
60
+ summary: "A student's learner record (staff)",
61
+ description: "Staff only (owner, admin, instructor). Per-course progress and completion.",
56
62
  params: StudentIdParam,
57
63
  response: {
58
64
  200: StudentAnalytics,
59
- 404: ErrorBody
65
+ ...refusals(403, 404)
60
66
  }
61
67
  },
62
68
  handler: async (req) => {
63
69
  const scope = await resolveScope(container, req);
64
- const analytics = await students.analytics(scope.orgId, req.params.id);
70
+ const analytics = await students.studentAnalytics(scope.orgId, req.params.id);
65
71
  if (!analytics) throw new NotFoundError("Student", req.params.id);
66
72
  return analytics;
67
73
  }
68
74
  });
69
- r.route({
70
- method: "PATCH",
71
- url: "/api/students/:id",
72
- preHandler: app.requireOrgSession,
73
- schema: {
74
- operationId: "updateStudent",
75
- tags: ["Organizations"],
76
- summary: "Update a student's profile",
77
- params: StudentIdParam,
78
- body: UpdateStudentBody,
79
- response: {
80
- 200: Student,
81
- 404: ErrorBody,
82
- 409: ErrorBody
83
- }
84
- },
85
- handler: async (req) => {
86
- const scope = await resolveScope(container, req);
87
- await container.organizations.updateStudent({
88
- orgId: scope.orgId,
89
- orgUserId: req.params.id,
90
- ...req.body
91
- });
92
- const student = await students.get(scope.orgId, req.params.id);
93
- if (!student) throw new NotFoundError("Student", req.params.id);
94
- return student;
95
- }
96
- });
97
75
  r.route({
98
76
  method: "POST",
99
77
  url: "/api/students/:id/invite/resend",
@@ -101,17 +79,20 @@ async function studentsRoutes(app, container) {
101
79
  schema: {
102
80
  operationId: "resendStudentInvite",
103
81
  tags: ["Organizations"],
104
- summary: "Re-send a pending student's invite email",
82
+ summary: "Re-send a pending student's invite email (staff)",
83
+ description: "Owner or admin only. Issues a new token, so the previous link stops working. 404 when the student has no pending invite; 409 when it was already accepted.",
105
84
  params: StudentIdParam,
106
85
  response: {
107
86
  204: z.void(),
108
- 404: ErrorBody,
109
- 409: ErrorBody
87
+ ...refusals(403, 404, 409)
110
88
  }
111
89
  },
112
90
  handler: async (req, reply) => {
113
91
  const scope = await resolveScope(container, req);
114
92
  await container.organizations.resendStudentInvite({
93
+ id: scope.orgUserId,
94
+ role: scope.role
95
+ }, {
115
96
  orgId: scope.orgId,
116
97
  orgUserId: req.params.id,
117
98
  inviterUserId: scope.userId
@@ -126,16 +107,20 @@ async function studentsRoutes(app, container) {
126
107
  schema: {
127
108
  operationId: "deleteStudent",
128
109
  tags: ["Organizations"],
129
- summary: "Delete a student",
110
+ summary: "Delete a student (staff)",
111
+ description: "Owner or admin only. 409 when the person has written posts, comments or messages.",
130
112
  params: StudentIdParam,
131
113
  response: {
132
114
  204: z.void(),
133
- 404: ErrorBody
115
+ ...refusals(403, 404, 409)
134
116
  }
135
117
  },
136
118
  handler: async (req, reply) => {
137
119
  const scope = await resolveScope(container, req);
138
- await container.organizations.deleteOrgUser(scope.orgId, req.params.id);
120
+ await container.organizations.deleteOrgUser(scope.orgId, {
121
+ id: scope.orgUserId,
122
+ role: scope.role
123
+ }, req.params.id);
139
124
  return reply.code(204).send();
140
125
  }
141
126
  });
@@ -1 +1 @@
1
- {"version":3,"file":"students.js","names":[],"sources":["../../../src/http/routes/students.ts"],"sourcesContent":["// HTTP routes for students: the reporting-backed list/read endpoints, the\n// delete, and re-issuing a pending invite. There is no create here — adding a\n// student IS inviting one, at /api/organizations/invites, which provisions the\n// student record up front.\nimport type { FastifyInstance } from 'fastify';\nimport { z } from 'zod';\nimport type { ZodTypeProvider } from 'fastify-type-provider-zod';\nimport {\n ErrorBody,\n Student,\n StudentAnalytics,\n StudentIdParam,\n StudentsPage,\n StudentsQuery,\n UpdateStudentBody,\n} from '../schemas/index.js';\nimport { NotFoundError } from '@headless-lms/core/shared/errors';\nimport type { Container } from '../../app/container.js';\nimport { resolveScope } from '../scope.js';\n\nexport async function studentsRoutes(app: FastifyInstance, container: Container): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n const students = container.reporting.students;\n\n r.route({\n method: 'GET',\n url: '/api/students',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listStudents',\n tags: ['Organizations'],\n summary: 'List students',\n querystring: StudentsQuery,\n response: { 200: StudentsPage },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return students.list(scope.orgId, req.query);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/students/:id',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getStudent',\n tags: ['Organizations'],\n summary: 'Get a student by id',\n params: StudentIdParam,\n response: { 200: Student, 404: ErrorBody },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n const student = await students.get(scope.orgId, req.params.id);\n if (!student) {\n throw new NotFoundError('Student', req.params.id);\n }\n return student;\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/students/:id/analytics',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getStudentAnalytics',\n tags: ['Reporting'],\n summary: \"A student's learner record: per-course progress and completion\",\n params: StudentIdParam,\n response: { 200: StudentAnalytics, 404: ErrorBody },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n const analytics = await students.analytics(scope.orgId, req.params.id);\n if (!analytics) {\n throw new NotFoundError('Student', req.params.id);\n }\n return analytics;\n },\n });\n\n // Corrects the person behind the student row. The write lands in identity\n // (the person is global); organizations owns the rule that this org user is\n // a student here, and reconciles their pending invite when the address moves.\n r.route({\n method: 'PATCH',\n url: '/api/students/:id',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'updateStudent',\n tags: ['Organizations'],\n summary: \"Update a student's profile\",\n params: StudentIdParam,\n body: UpdateStudentBody,\n response: { 200: Student, 404: ErrorBody, 409: ErrorBody },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n await container.organizations.updateStudent({\n orgId: scope.orgId,\n orgUserId: req.params.id,\n ...req.body,\n });\n // Re-read through the report so the response is the same shape the list\n // and detail GETs return, aggregates included.\n const student = await students.get(scope.orgId, req.params.id);\n if (!student) {\n throw new NotFoundError('Student', req.params.id);\n }\n return student;\n },\n });\n\n // Re-mints the token on the student's pending invite and emails it. Lives\n // here rather than under invites because the admin app knows the student,\n // not the invitation.\n r.route({\n method: 'POST',\n url: '/api/students/:id/invite/resend',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'resendStudentInvite',\n tags: ['Organizations'],\n summary: \"Re-send a pending student's invite email\",\n params: StudentIdParam,\n response: { 204: z.void(), 404: ErrorBody, 409: ErrorBody },\n },\n handler: async (req, reply) => {\n const scope = await resolveScope(container, req);\n await container.organizations.resendStudentInvite({\n orgId: scope.orgId,\n orgUserId: req.params.id,\n inviterUserId: scope.userId,\n });\n return reply.code(204).send();\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/students/:id',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'deleteStudent',\n tags: ['Organizations'],\n summary: 'Delete a student',\n params: StudentIdParam,\n response: { 204: z.void(), 404: ErrorBody },\n },\n handler: async (req, reply) => {\n const scope = await resolveScope(container, req);\n await container.organizations.deleteOrgUser(scope.orgId, req.params.id);\n return reply.code(204).send();\n },\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"students.js","names":[],"sources":["../../../src/http/routes/students.ts"],"sourcesContent":["// HTTP routes for students: the reporting-backed list/read endpoints, the\n// delete, and re-issuing a pending invite. There is no create here — adding a\n// student IS inviting one, at /api/organizations/invites, which provisions the\n// student record up front.\nimport type { FastifyInstance } from 'fastify';\nimport { z } from 'zod';\nimport type { ZodTypeProvider } from 'fastify-type-provider-zod';\nimport {\n Student,\n StudentAnalytics,\n StudentIdParam,\n StudentsPage,\n StudentsQuery,\n refusals,\n} from '../schemas/index.js';\nimport { NotFoundError } from '@headless-lms/core/shared/errors';\nimport type { Container } from '../../app/container.js';\nimport { resolveScope } from '../scope.js';\n\nexport async function studentsRoutes(app: FastifyInstance, container: Container): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n const students = container.reporting.admin;\n\n r.route({\n method: 'GET',\n url: '/api/students',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listStudents',\n tags: ['Organizations'],\n summary: 'List students (staff)',\n description: 'Staff only (owner, admin, instructor).',\n querystring: StudentsQuery,\n response: { 200: StudentsPage, ...refusals(403) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return students.listStudents(scope.orgId, req.query);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/students/:id',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getStudent',\n tags: ['Organizations'],\n summary: 'Get a student (staff)',\n description: 'Staff only (owner, admin, instructor).',\n params: StudentIdParam,\n response: { 200: Student, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n const student = await students.getStudent(scope.orgId, req.params.id);\n if (!student) {\n throw new NotFoundError('Student', req.params.id);\n }\n return student;\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/students/:id/analytics',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getStudentAnalytics',\n tags: ['Reporting'],\n summary: \"A student's learner record (staff)\",\n description: 'Staff only (owner, admin, instructor). Per-course progress and completion.',\n params: StudentIdParam,\n response: { 200: StudentAnalytics, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n const analytics = await students.studentAnalytics(scope.orgId, req.params.id);\n if (!analytics) {\n throw new NotFoundError('Student', req.params.id);\n }\n return analytics;\n },\n });\n\n // Re-mints the token on the student's pending invite and emails it. Lives\n // here rather than under invites because the admin app knows the student,\n // not the invitation.\n r.route({\n method: 'POST',\n url: '/api/students/:id/invite/resend',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'resendStudentInvite',\n tags: ['Organizations'],\n summary: \"Re-send a pending student's invite email (staff)\",\n description:\n 'Owner or admin only. Issues a new token, so the previous link stops working. 404 when the student has no pending invite; 409 when it was already accepted.',\n params: StudentIdParam,\n response: { 204: z.void(), ...refusals(403, 404, 409) },\n },\n handler: async (req, reply) => {\n const scope = await resolveScope(container, req);\n await container.organizations.resendStudentInvite(\n { id: scope.orgUserId, role: scope.role },\n {\n orgId: scope.orgId,\n orgUserId: req.params.id,\n inviterUserId: scope.userId,\n },\n );\n return reply.code(204).send();\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/students/:id',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'deleteStudent',\n tags: ['Organizations'],\n summary: 'Delete a student (staff)',\n description:\n 'Owner or admin only. 409 when the person has written posts, comments or messages.',\n params: StudentIdParam,\n response: { 204: z.void(), ...refusals(403, 404, 409) },\n },\n handler: async (req, reply) => {\n const scope = await resolveScope(container, req);\n await container.organizations.deleteOrgUser(\n scope.orgId,\n { id: scope.orgUserId, role: scope.role },\n req.params.id,\n );\n return reply.code(204).send();\n },\n });\n}\n"],"mappings":";;;;;;;AAmBA,eAAsB,eAAe,KAAsB,WAAqC;CAC9F,MAAM,IAAI,IAAI,iBAAkC;CAChD,MAAM,WAAW,UAAU,UAAU;CAErC,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb,MAAM,CAAC,eAAe;GACtB,SAAS;GACT,aAAa;GACb,aAAa;GACb,UAAU;IAAE,KAAK;IAAc,GAAG,SAAS,GAAG;GAAE;EAClD;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,SAAS,aAAa,MAAM,OAAO,IAAI,KAAK;EACrD;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb,MAAM,CAAC,eAAe;GACtB,SAAS;GACT,aAAa;GACb,QAAQ;GACR,UAAU;IAAE,KAAK;IAAS,GAAG,SAAS,KAAK,GAAG;GAAE;EAClD;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,MAAM,UAAU,MAAM,SAAS,WAAW,MAAM,OAAO,IAAI,OAAO,EAAE;GACpE,IAAI,CAAC,SACH,MAAM,IAAI,cAAc,WAAW,IAAI,OAAO,EAAE;GAElD,OAAO;EACT;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb,MAAM,CAAC,WAAW;GAClB,SAAS;GACT,aAAa;GACb,QAAQ;GACR,UAAU;IAAE,KAAK;IAAkB,GAAG,SAAS,KAAK,GAAG;GAAE;EAC3D;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,MAAM,YAAY,MAAM,SAAS,iBAAiB,MAAM,OAAO,IAAI,OAAO,EAAE;GAC5E,IAAI,CAAC,WACH,MAAM,IAAI,cAAc,WAAW,IAAI,OAAO,EAAE;GAElD,OAAO;EACT;CACF,CAAC;CAKD,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb,MAAM,CAAC,eAAe;GACtB,SAAS;GACT,aACE;GACF,QAAQ;GACR,UAAU;IAAE,KAAK,EAAE,KAAK;IAAG,GAAG,SAAS,KAAK,KAAK,GAAG;GAAE;EACxD;EACA,SAAS,OAAO,KAAK,UAAU;GAC7B,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,MAAM,UAAU,cAAc,oBAC5B;IAAE,IAAI,MAAM;IAAW,MAAM,MAAM;GAAK,GACxC;IACE,OAAO,MAAM;IACb,WAAW,IAAI,OAAO;IACtB,eAAe,MAAM;GACvB,CACF;GACA,OAAO,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK;EAC9B;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb,MAAM,CAAC,eAAe;GACtB,SAAS;GACT,aACE;GACF,QAAQ;GACR,UAAU;IAAE,KAAK,EAAE,KAAK;IAAG,GAAG,SAAS,KAAK,KAAK,GAAG;GAAE;EACxD;EACA,SAAS,OAAO,KAAK,UAAU;GAC7B,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,MAAM,UAAU,cAAc,cAC5B,MAAM,OACN;IAAE,IAAI,MAAM;IAAW,MAAM,MAAM;GAAK,GACxC,IAAI,OAAO,EACb;GACA,OAAO,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK;EAC9B;CACF,CAAC;AACH"}
@@ -1,11 +1,11 @@
1
+ import { refusals } from "../schemas/shared.js";
1
2
  import { resolveScope } from "../scope.js";
2
- import { ErrorBody } from "../schemas/shared.js";
3
3
  import { AutomationIdParam, CreateWebhookBody, CreatedWebhook, UpdateWebhookBody, Webhook, WebhookSecret } from "../schemas/automations.js";
4
4
  import "../schemas/index.js";
5
- import { NotFoundError } from "@headless-lms/core/shared/errors";
6
5
  import { z } from "zod";
6
+ import { NotFoundError } from "@headless-lms/core/shared/errors";
7
7
  //#region src/http/routes/webhooks.ts
8
- const DELIVERY_DESCRIPTION = "Each subscribed event is POSTed to `url` as the JSON event envelope (`id`, `type`, `version`, `orgId`, `occurredAt`, `data`), signed per the Standard Webhooks spec: `webhook-id` (the event id, stable across retries), `webhook-timestamp` (unix seconds) and `webhook-signature` (`v1,` + base64 HMAC-SHA256 of `{webhook-id}.{webhook-timestamp}.{body}`, keyed with the base64-decoded part of the `whsec_` secret). Any non-2xx response or timeout is a failed attempt and is retried. Deliveries are the webhook's automation runs: `GET /api/automations/{id}/runs`.";
8
+ const DELIVERY_DESCRIPTION = "Staff only (owner, admin, instructor). `url` must be a public address: localhost and private network addresses are refused when saved and when delivering. Each subscribed event is POSTed to `url` as the JSON event envelope (`id`, `type`, `version`, `orgId`, `occurredAt`, `data`), signed per the Standard Webhooks spec: `webhook-id` (the event id, stable across retries), `webhook-timestamp` (unix seconds) and `webhook-signature` (`v1,` + base64 HMAC-SHA256 of `{webhook-id}.{webhook-timestamp}.{body}`, keyed with the base64-decoded part of the `whsec_` secret). Any non-2xx response or timeout is a failed attempt and is retried. Deliveries are the webhook's automation runs: `GET /api/automations/{id}/runs`.";
9
9
  async function webhooksRoutes(app, container) {
10
10
  const r = app.withTypeProvider();
11
11
  const automations = container.automations;
@@ -17,8 +17,12 @@ async function webhooksRoutes(app, container) {
17
17
  schema: {
18
18
  operationId: "listWebhooks",
19
19
  tags,
20
- summary: "List the organization's webhooks",
21
- response: { 200: z.array(Webhook) }
20
+ summary: "List the organization's webhooks (staff)",
21
+ description: "Staff only (owner, admin, instructor). Signing secrets are not included.",
22
+ response: {
23
+ 200: z.array(Webhook),
24
+ ...refusals(403)
25
+ }
22
26
  },
23
27
  handler: async (req) => {
24
28
  const scope = await resolveScope(container, req);
@@ -32,12 +36,12 @@ async function webhooksRoutes(app, container) {
32
36
  schema: {
33
37
  operationId: "createWebhook",
34
38
  tags,
35
- summary: "Create a webhook — returns its signing secret",
39
+ summary: "Create a webhook (staff)",
36
40
  description: DELIVERY_DESCRIPTION,
37
41
  body: CreateWebhookBody,
38
42
  response: {
39
43
  201: CreatedWebhook,
40
- 400: ErrorBody
44
+ ...refusals(400, 403)
41
45
  }
42
46
  },
43
47
  handler: async (req, reply) => {
@@ -53,11 +57,12 @@ async function webhooksRoutes(app, container) {
53
57
  schema: {
54
58
  operationId: "getWebhook",
55
59
  tags,
56
- summary: "Get a webhook by id",
60
+ summary: "Get a webhook (staff)",
61
+ description: "Staff only (owner, admin, instructor). The signing secret is not included.",
57
62
  params: AutomationIdParam,
58
63
  response: {
59
64
  200: Webhook,
60
- 404: ErrorBody
65
+ ...refusals(403, 404)
61
66
  }
62
67
  },
63
68
  handler: async (req) => {
@@ -74,13 +79,13 @@ async function webhooksRoutes(app, container) {
74
79
  schema: {
75
80
  operationId: "updateWebhook",
76
81
  tags,
77
- summary: "Change a webhook's URL, events, description or enabled flag",
82
+ summary: "Change a webhook's URL, events, description or enabled flag (staff)",
83
+ description: "Staff only (owner, admin, instructor). Changes only the fields sent. 400 when an event is reserved or unknown.",
78
84
  params: AutomationIdParam,
79
85
  body: UpdateWebhookBody,
80
86
  response: {
81
87
  200: Webhook,
82
- 400: ErrorBody,
83
- 404: ErrorBody
88
+ ...refusals(400, 403, 404)
84
89
  }
85
90
  },
86
91
  handler: async (req) => {
@@ -97,11 +102,12 @@ async function webhooksRoutes(app, container) {
97
102
  schema: {
98
103
  operationId: "deleteWebhook",
99
104
  tags,
100
- summary: "Delete a webhook and its signing secret — its delivery history is kept",
105
+ summary: "Delete a webhook and its signing secret (staff)",
106
+ description: "Staff only (owner, admin, instructor). Its delivery history is kept.",
101
107
  params: AutomationIdParam,
102
108
  response: {
103
109
  204: z.void(),
104
- 404: ErrorBody
110
+ ...refusals(403, 404)
105
111
  }
106
112
  },
107
113
  handler: async (req, reply) => {
@@ -117,11 +123,12 @@ async function webhooksRoutes(app, container) {
117
123
  schema: {
118
124
  operationId: "getWebhookSecret",
119
125
  tags,
120
- summary: "Reveal a webhook's signing secret",
126
+ summary: "Reveal a webhook's signing secret (staff)",
127
+ description: "Staff only (owner, admin, instructor).",
121
128
  params: AutomationIdParam,
122
129
  response: {
123
130
  200: WebhookSecret,
124
- 404: ErrorBody
131
+ ...refusals(403, 404)
125
132
  }
126
133
  },
127
134
  handler: async (req) => {
@@ -138,11 +145,12 @@ async function webhooksRoutes(app, container) {
138
145
  schema: {
139
146
  operationId: "rotateWebhookSecret",
140
147
  tags,
141
- summary: "Replace a webhook's signing secret — the old one stops working immediately",
148
+ summary: "Replace a webhook's signing secret (staff)",
149
+ description: "Staff only (owner, admin, instructor). The old secret stops working immediately.",
142
150
  params: AutomationIdParam,
143
151
  response: {
144
152
  200: WebhookSecret,
145
- 404: ErrorBody
153
+ ...refusals(403, 404)
146
154
  }
147
155
  },
148
156
  handler: async (req) => {
@@ -1 +1 @@
1
- {"version":3,"file":"webhooks.js","names":[],"sources":["../../../src/http/routes/webhooks.ts"],"sourcesContent":["import type { FastifyInstance } from 'fastify';\nimport { z } from 'zod';\nimport type { ZodTypeProvider } from 'fastify-type-provider-zod';\nimport {\n AutomationIdParam,\n CreatedWebhook,\n CreateWebhookBody,\n ErrorBody,\n UpdateWebhookBody,\n Webhook,\n WebhookSecret,\n} from '../schemas/index.js';\nimport { NotFoundError } from '@headless-lms/core/shared/errors';\nimport type { Container } from '../../app/container.js';\nimport { resolveScope } from '../scope.js';\n\nconst DELIVERY_DESCRIPTION =\n \"Each subscribed event is POSTed to `url` as the JSON event envelope (`id`, `type`, `version`, `orgId`, `occurredAt`, `data`), signed per the Standard Webhooks spec: `webhook-id` (the event id, stable across retries), `webhook-timestamp` (unix seconds) and `webhook-signature` (`v1,` + base64 HMAC-SHA256 of `{webhook-id}.{webhook-timestamp}.{body}`, keyed with the base64-decoded part of the `whsec_` secret). Any non-2xx response or timeout is a failed attempt and is retried. Deliveries are the webhook's automation runs: `GET /api/automations/{id}/runs`.\";\n\nexport async function webhooksRoutes(app: FastifyInstance, container: Container): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n const automations = container.automations;\n const tags = ['Automations'];\n\n r.route({\n method: 'GET',\n url: '/api/automations/webhooks',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listWebhooks',\n tags,\n summary: \"List the organization's webhooks\",\n response: { 200: z.array(Webhook) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return automations.listWebhooks(scope.orgId);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/automations/webhooks',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'createWebhook',\n tags,\n summary: 'Create a webhook — returns its signing secret',\n description: DELIVERY_DESCRIPTION,\n body: CreateWebhookBody,\n response: { 201: CreatedWebhook, 400: ErrorBody },\n },\n handler: async (req, reply) => {\n const scope = await resolveScope(container, req);\n const webhook = await automations.createWebhook(scope.orgId, req.body);\n return reply.code(201).send(webhook);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/automations/webhooks/:id',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getWebhook',\n tags,\n summary: 'Get a webhook by id',\n params: AutomationIdParam,\n response: { 200: Webhook, 404: ErrorBody },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n const webhook = await automations.getWebhook(scope.orgId, req.params.id);\n if (!webhook) {\n throw new NotFoundError('Webhook', req.params.id);\n }\n return webhook;\n },\n });\n\n r.route({\n method: 'PATCH',\n url: '/api/automations/webhooks/:id',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'updateWebhook',\n tags,\n summary: \"Change a webhook's URL, events, description or enabled flag\",\n params: AutomationIdParam,\n body: UpdateWebhookBody,\n response: { 200: Webhook, 400: ErrorBody, 404: ErrorBody },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n const webhook = await automations.updateWebhook(scope.orgId, req.params.id, req.body);\n if (!webhook) {\n throw new NotFoundError('Webhook', req.params.id);\n }\n return webhook;\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/automations/webhooks/:id',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'deleteWebhook',\n tags,\n summary: 'Delete a webhook and its signing secret — its delivery history is kept',\n params: AutomationIdParam,\n response: { 204: z.void(), 404: ErrorBody },\n },\n handler: async (req, reply) => {\n const scope = await resolveScope(container, req);\n const deleted = await automations.deleteWebhook(scope.orgId, req.params.id);\n if (!deleted) {\n throw new NotFoundError('Webhook', req.params.id);\n }\n return reply.code(204).send();\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/automations/webhooks/:id/secret',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getWebhookSecret',\n tags,\n summary: \"Reveal a webhook's signing secret\",\n params: AutomationIdParam,\n response: { 200: WebhookSecret, 404: ErrorBody },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n const secret = await automations.revealWebhookSecret(scope.orgId, req.params.id);\n if (!secret) {\n throw new NotFoundError('Webhook', req.params.id);\n }\n return secret;\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/automations/webhooks/:id/secret/rotate',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'rotateWebhookSecret',\n tags,\n summary: \"Replace a webhook's signing secret — the old one stops working immediately\",\n params: AutomationIdParam,\n response: { 200: WebhookSecret, 404: ErrorBody },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n const secret = await automations.rotateWebhookSecret(scope.orgId, req.params.id);\n if (!secret) {\n throw new NotFoundError('Webhook', req.params.id);\n }\n return secret;\n },\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"webhooks.js","names":[],"sources":["../../../src/http/routes/webhooks.ts"],"sourcesContent":["import type { FastifyInstance } from 'fastify';\nimport { z } from 'zod';\nimport type { ZodTypeProvider } from 'fastify-type-provider-zod';\nimport {\n AutomationIdParam,\n CreatedWebhook,\n CreateWebhookBody,\n UpdateWebhookBody,\n Webhook,\n WebhookSecret,\n refusals,\n} from '../schemas/index.js';\nimport { NotFoundError } from '@headless-lms/core/shared/errors';\nimport type { Container } from '../../app/container.js';\nimport { resolveScope } from '../scope.js';\n\nconst DELIVERY_DESCRIPTION =\n \"Staff only (owner, admin, instructor). `url` must be a public address: localhost and private network addresses are refused when saved and when delivering. Each subscribed event is POSTed to `url` as the JSON event envelope (`id`, `type`, `version`, `orgId`, `occurredAt`, `data`), signed per the Standard Webhooks spec: `webhook-id` (the event id, stable across retries), `webhook-timestamp` (unix seconds) and `webhook-signature` (`v1,` + base64 HMAC-SHA256 of `{webhook-id}.{webhook-timestamp}.{body}`, keyed with the base64-decoded part of the `whsec_` secret). Any non-2xx response or timeout is a failed attempt and is retried. Deliveries are the webhook's automation runs: `GET /api/automations/{id}/runs`.\";\n\nexport async function webhooksRoutes(app: FastifyInstance, container: Container): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n const automations = container.automations;\n const tags = ['Automations'];\n\n r.route({\n method: 'GET',\n url: '/api/automations/webhooks',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listWebhooks',\n tags,\n summary: \"List the organization's webhooks (staff)\",\n description: 'Staff only (owner, admin, instructor). Signing secrets are not included.',\n response: { 200: z.array(Webhook), ...refusals(403) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return automations.listWebhooks(scope.orgId);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/automations/webhooks',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'createWebhook',\n tags,\n summary: 'Create a webhook (staff)',\n description: DELIVERY_DESCRIPTION,\n body: CreateWebhookBody,\n response: { 201: CreatedWebhook, ...refusals(400, 403) },\n },\n handler: async (req, reply) => {\n const scope = await resolveScope(container, req);\n const webhook = await automations.createWebhook(scope.orgId, req.body);\n return reply.code(201).send(webhook);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/automations/webhooks/:id',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getWebhook',\n tags,\n summary: 'Get a webhook (staff)',\n description: 'Staff only (owner, admin, instructor). The signing secret is not included.',\n params: AutomationIdParam,\n response: { 200: Webhook, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n const webhook = await automations.getWebhook(scope.orgId, req.params.id);\n if (!webhook) {\n throw new NotFoundError('Webhook', req.params.id);\n }\n return webhook;\n },\n });\n\n r.route({\n method: 'PATCH',\n url: '/api/automations/webhooks/:id',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'updateWebhook',\n tags,\n summary: \"Change a webhook's URL, events, description or enabled flag (staff)\",\n description:\n 'Staff only (owner, admin, instructor). Changes only the fields sent. 400 when an event is reserved or unknown.',\n params: AutomationIdParam,\n body: UpdateWebhookBody,\n response: { 200: Webhook, ...refusals(400, 403, 404) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n const webhook = await automations.updateWebhook(scope.orgId, req.params.id, req.body);\n if (!webhook) {\n throw new NotFoundError('Webhook', req.params.id);\n }\n return webhook;\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/automations/webhooks/:id',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'deleteWebhook',\n tags,\n summary: 'Delete a webhook and its signing secret (staff)',\n description: 'Staff only (owner, admin, instructor). Its delivery history is kept.',\n params: AutomationIdParam,\n response: { 204: z.void(), ...refusals(403, 404) },\n },\n handler: async (req, reply) => {\n const scope = await resolveScope(container, req);\n const deleted = await automations.deleteWebhook(scope.orgId, req.params.id);\n if (!deleted) {\n throw new NotFoundError('Webhook', req.params.id);\n }\n return reply.code(204).send();\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/automations/webhooks/:id/secret',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getWebhookSecret',\n tags,\n summary: \"Reveal a webhook's signing secret (staff)\",\n description: 'Staff only (owner, admin, instructor).',\n params: AutomationIdParam,\n response: { 200: WebhookSecret, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n const secret = await automations.revealWebhookSecret(scope.orgId, req.params.id);\n if (!secret) {\n throw new NotFoundError('Webhook', req.params.id);\n }\n return secret;\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/automations/webhooks/:id/secret/rotate',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'rotateWebhookSecret',\n tags,\n summary: \"Replace a webhook's signing secret (staff)\",\n description:\n 'Staff only (owner, admin, instructor). The old secret stops working immediately.',\n params: AutomationIdParam,\n response: { 200: WebhookSecret, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n const secret = await automations.rotateWebhookSecret(scope.orgId, req.params.id);\n if (!secret) {\n throw new NotFoundError('Webhook', req.params.id);\n }\n return secret;\n },\n });\n}\n"],"mappings":"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"}
@@ -1,7 +1,9 @@
1
+ import { communitiesRoutes } from "./routes/communities.js";
2
+ import { communityModerationRoutes } from "./routes/community-moderation.js";
1
3
  import { coursesRoutes } from "./routes/courses.js";
2
4
  import { downloadsRoutes } from "./routes/downloads.js";
3
5
  import { bundlesRoutes } from "./routes/bundles.js";
4
- import { learnRoutes } from "./routes/learn/index.js";
6
+ import { memberRoutes } from "./routes/member/index.js";
5
7
  import { discussionRoutes } from "./routes/discussion.js";
6
8
  import { activitiesRoutes } from "./routes/activities.js";
7
9
  import { studentsRoutes } from "./routes/students.js";
@@ -13,6 +15,7 @@ import { dashboardRoutes } from "./routes/dashboard.js";
13
15
  import { courseAnalyticsRoutes } from "./routes/course-analytics.js";
14
16
  import { assetsRoutes } from "./routes/assets.js";
15
17
  import { integrationsRoutes } from "./routes/integrations.js";
18
+ import { accessRoutes } from "./routes/access.js";
16
19
  //#region src/http/routes.ts
17
20
  function registerRoutes(app, container) {
18
21
  app.get("/health", async () => ({ status: "ok" }));
@@ -22,9 +25,11 @@ function registerRoutes(app, container) {
22
25
  app.register(async (instance) => {
23
26
  instance.addHook("onRequest", instance.requireOrgSession);
24
27
  await coursesRoutes(instance, container);
28
+ await communitiesRoutes(instance, container);
29
+ await communityModerationRoutes(instance, container);
25
30
  await downloadsRoutes(instance, container);
26
31
  await bundlesRoutes(instance, container);
27
- await learnRoutes(instance, container);
32
+ await memberRoutes(instance, container);
28
33
  await discussionRoutes(instance, container);
29
34
  await activitiesRoutes(instance, container);
30
35
  await studentsRoutes(instance, container);
@@ -35,6 +40,7 @@ function registerRoutes(app, container) {
35
40
  await courseAnalyticsRoutes(instance, container);
36
41
  await assetsRoutes(instance, container);
37
42
  await integrationsRoutes(instance, container);
43
+ await accessRoutes(instance, container);
38
44
  });
39
45
  }
40
46
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"routes.js","names":[],"sources":["../../src/http/routes.ts"],"sourcesContent":["import type { FastifyInstance } from 'fastify';\nimport type { Container } from '../app/container.js';\nimport { coursesRoutes } from './routes/courses.js';\nimport { downloadsRoutes } from './routes/downloads.js';\nimport { bundlesRoutes } from './routes/bundles.js';\nimport { learnRoutes } from './routes/learn/index.js';\nimport { discussionRoutes } from './routes/discussion.js';\nimport { activitiesRoutes } from './routes/activities.js';\nimport { studentsRoutes } from './routes/students.js';\nimport { entitlementsRoutes } from './routes/entitlements.js';\nimport { automationsRoutes } from './routes/automations.js';\nimport { webhooksRoutes } from './routes/webhooks.js';\nimport { organizationsRoutes } from './routes/organizations.js';\nimport { dashboardRoutes } from './routes/dashboard.js';\nimport { courseAnalyticsRoutes } from './routes/course-analytics.js';\nimport { assetsRoutes } from './routes/assets.js';\nimport { integrationsRoutes } from './routes/integrations.js';\n\nexport function registerRoutes(app: FastifyInstance, container: Container): void {\n app.get('/health', async () => ({ status: 'ok' }));\n\n app.register(async (instance) => {\n // instance.addHook('onRequest', instance.requireSession);\n await organizationsRoutes(instance, container);\n });\n\n app.register(async (instance) => {\n instance.addHook('onRequest', instance.requireOrgSession);\n await coursesRoutes(instance, container);\n await downloadsRoutes(instance, container);\n await bundlesRoutes(instance, container);\n await learnRoutes(instance, container);\n await discussionRoutes(instance, container);\n await activitiesRoutes(instance, container);\n await studentsRoutes(instance, container);\n await entitlementsRoutes(instance, container);\n await automationsRoutes(instance, container);\n await webhooksRoutes(instance, container);\n await dashboardRoutes(instance, container);\n await courseAnalyticsRoutes(instance, container);\n await assetsRoutes(instance, container);\n await integrationsRoutes(instance, container);\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAkBA,SAAgB,eAAe,KAAsB,WAA4B;CAC/E,IAAI,IAAI,WAAW,aAAa,EAAE,QAAQ,KAAK,EAAE;CAEjD,IAAI,SAAS,OAAO,aAAa;EAE/B,MAAM,oBAAoB,UAAU,SAAS;CAC/C,CAAC;CAED,IAAI,SAAS,OAAO,aAAa;EAC/B,SAAS,QAAQ,aAAa,SAAS,iBAAiB;EACxD,MAAM,cAAc,UAAU,SAAS;EACvC,MAAM,gBAAgB,UAAU,SAAS;EACzC,MAAM,cAAc,UAAU,SAAS;EACvC,MAAM,YAAY,UAAU,SAAS;EACrC,MAAM,iBAAiB,UAAU,SAAS;EAC1C,MAAM,iBAAiB,UAAU,SAAS;EAC1C,MAAM,eAAe,UAAU,SAAS;EACxC,MAAM,mBAAmB,UAAU,SAAS;EAC5C,MAAM,kBAAkB,UAAU,SAAS;EAC3C,MAAM,eAAe,UAAU,SAAS;EACxC,MAAM,gBAAgB,UAAU,SAAS;EACzC,MAAM,sBAAsB,UAAU,SAAS;EAC/C,MAAM,aAAa,UAAU,SAAS;EACtC,MAAM,mBAAmB,UAAU,SAAS;CAC9C,CAAC;AACH"}
1
+ {"version":3,"file":"routes.js","names":[],"sources":["../../src/http/routes.ts"],"sourcesContent":["import type { FastifyInstance } from 'fastify';\nimport type { Container } from '../app/container.js';\nimport { communitiesRoutes } from './routes/communities.js';\nimport { communityModerationRoutes } from './routes/community-moderation.js';\nimport { coursesRoutes } from './routes/courses.js';\nimport { downloadsRoutes } from './routes/downloads.js';\nimport { bundlesRoutes } from './routes/bundles.js';\nimport { memberRoutes } from './routes/member/index.js';\nimport { discussionRoutes } from './routes/discussion.js';\nimport { activitiesRoutes } from './routes/activities.js';\nimport { studentsRoutes } from './routes/students.js';\nimport { entitlementsRoutes } from './routes/entitlements.js';\nimport { automationsRoutes } from './routes/automations.js';\nimport { webhooksRoutes } from './routes/webhooks.js';\nimport { organizationsRoutes } from './routes/organizations.js';\nimport { dashboardRoutes } from './routes/dashboard.js';\nimport { courseAnalyticsRoutes } from './routes/course-analytics.js';\nimport { assetsRoutes } from './routes/assets.js';\nimport { integrationsRoutes } from './routes/integrations.js';\nimport { accessRoutes } from './routes/access.js';\n\nexport function registerRoutes(app: FastifyInstance, container: Container): void {\n app.get('/health', async () => ({ status: 'ok' }));\n\n app.register(async (instance) => {\n // instance.addHook('onRequest', instance.requireSession);\n await organizationsRoutes(instance, container);\n });\n\n app.register(async (instance) => {\n instance.addHook('onRequest', instance.requireOrgSession);\n await coursesRoutes(instance, container);\n await communitiesRoutes(instance, container);\n await communityModerationRoutes(instance, container);\n await downloadsRoutes(instance, container);\n await bundlesRoutes(instance, container);\n await memberRoutes(instance, container);\n await discussionRoutes(instance, container);\n await activitiesRoutes(instance, container);\n await studentsRoutes(instance, container);\n await entitlementsRoutes(instance, container);\n await automationsRoutes(instance, container);\n await webhooksRoutes(instance, container);\n await dashboardRoutes(instance, container);\n await courseAnalyticsRoutes(instance, container);\n await assetsRoutes(instance, container);\n await integrationsRoutes(instance, container);\n await accessRoutes(instance, container);\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,eAAe,KAAsB,WAA4B;CAC/E,IAAI,IAAI,WAAW,aAAa,EAAE,QAAQ,KAAK,EAAE;CAEjD,IAAI,SAAS,OAAO,aAAa;EAE/B,MAAM,oBAAoB,UAAU,SAAS;CAC/C,CAAC;CAED,IAAI,SAAS,OAAO,aAAa;EAC/B,SAAS,QAAQ,aAAa,SAAS,iBAAiB;EACxD,MAAM,cAAc,UAAU,SAAS;EACvC,MAAM,kBAAkB,UAAU,SAAS;EAC3C,MAAM,0BAA0B,UAAU,SAAS;EACnD,MAAM,gBAAgB,UAAU,SAAS;EACzC,MAAM,cAAc,UAAU,SAAS;EACvC,MAAM,aAAa,UAAU,SAAS;EACtC,MAAM,iBAAiB,UAAU,SAAS;EAC1C,MAAM,iBAAiB,UAAU,SAAS;EAC1C,MAAM,eAAe,UAAU,SAAS;EACxC,MAAM,mBAAmB,UAAU,SAAS;EAC5C,MAAM,kBAAkB,UAAU,SAAS;EAC3C,MAAM,eAAe,UAAU,SAAS;EACxC,MAAM,gBAAgB,UAAU,SAAS;EACzC,MAAM,sBAAsB,UAAU,SAAS;EAC/C,MAAM,aAAa,UAAU,SAAS;EACtC,MAAM,mBAAmB,UAAU,SAAS;EAC5C,MAAM,aAAa,UAAU,SAAS;CACxC,CAAC;AACH"}
@@ -0,0 +1,146 @@
1
+ import { z } from "zod";
2
+ //#region src/http/schemas/access.d.ts
3
+ declare const ScopeOption: z.ZodObject<{
4
+ scope: z.ZodEnum<{
5
+ "courses:read": "courses:read";
6
+ "courses:write": "courses:write";
7
+ "courses:delete": "courses:delete";
8
+ "downloads:read": "downloads:read";
9
+ "downloads:write": "downloads:write";
10
+ "downloads:delete": "downloads:delete";
11
+ "bundles:read": "bundles:read";
12
+ "bundles:write": "bundles:write";
13
+ "bundles:delete": "bundles:delete";
14
+ "media:read": "media:read";
15
+ "students:read": "students:read";
16
+ "students:invite": "students:invite";
17
+ "access:read": "access:read";
18
+ "access:write": "access:write";
19
+ "analytics:read": "analytics:read";
20
+ "automations:read": "automations:read";
21
+ "automations:write": "automations:write";
22
+ "automations:delete": "automations:delete";
23
+ "comments:read": "comments:read";
24
+ "comments:moderate": "comments:moderate";
25
+ "communities:read": "communities:read";
26
+ "communities:write": "communities:write";
27
+ "communities:delete": "communities:delete";
28
+ "community_members:read": "community_members:read";
29
+ "community_members:write": "community_members:write";
30
+ "community_moderation:read": "community_moderation:read";
31
+ "community_moderation:write": "community_moderation:write";
32
+ }>;
33
+ allowed: z.ZodBoolean;
34
+ }, z.core.$strip>;
35
+ declare const ScopeOptionList: z.ZodArray<z.ZodObject<{
36
+ scope: z.ZodEnum<{
37
+ "courses:read": "courses:read";
38
+ "courses:write": "courses:write";
39
+ "courses:delete": "courses:delete";
40
+ "downloads:read": "downloads:read";
41
+ "downloads:write": "downloads:write";
42
+ "downloads:delete": "downloads:delete";
43
+ "bundles:read": "bundles:read";
44
+ "bundles:write": "bundles:write";
45
+ "bundles:delete": "bundles:delete";
46
+ "media:read": "media:read";
47
+ "students:read": "students:read";
48
+ "students:invite": "students:invite";
49
+ "access:read": "access:read";
50
+ "access:write": "access:write";
51
+ "analytics:read": "analytics:read";
52
+ "automations:read": "automations:read";
53
+ "automations:write": "automations:write";
54
+ "automations:delete": "automations:delete";
55
+ "comments:read": "comments:read";
56
+ "comments:moderate": "comments:moderate";
57
+ "communities:read": "communities:read";
58
+ "communities:write": "communities:write";
59
+ "communities:delete": "communities:delete";
60
+ "community_members:read": "community_members:read";
61
+ "community_members:write": "community_members:write";
62
+ "community_moderation:read": "community_moderation:read";
63
+ "community_moderation:write": "community_moderation:write";
64
+ }>;
65
+ allowed: z.ZodBoolean;
66
+ }, z.core.$strip>>;
67
+ declare const ConnectedApp: z.ZodObject<{
68
+ clientId: z.ZodString;
69
+ name: z.ZodString;
70
+ iconUrl: z.ZodNullable<z.ZodString>;
71
+ scopes: z.ZodArray<z.ZodString>;
72
+ connectedAt: z.ZodCoercedDate<unknown>;
73
+ }, z.core.$strip>;
74
+ declare const ConnectedAppList: z.ZodArray<z.ZodObject<{
75
+ clientId: z.ZodString;
76
+ name: z.ZodString;
77
+ iconUrl: z.ZodNullable<z.ZodString>;
78
+ scopes: z.ZodArray<z.ZodString>;
79
+ connectedAt: z.ZodCoercedDate<unknown>;
80
+ }, z.core.$strip>>;
81
+ declare const ClientIdParam: z.ZodObject<{
82
+ clientId: z.ZodString;
83
+ }, z.core.$strip>;
84
+ declare const ApiKey: z.ZodObject<{
85
+ id: z.ZodString;
86
+ name: z.ZodNullable<z.ZodString>;
87
+ start: z.ZodNullable<z.ZodString>;
88
+ scopes: z.ZodArray<z.ZodString>;
89
+ createdAt: z.ZodCoercedDate<unknown>;
90
+ lastUsedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
91
+ }, z.core.$strip>;
92
+ declare const ApiKeyList: z.ZodArray<z.ZodObject<{
93
+ id: z.ZodString;
94
+ name: z.ZodNullable<z.ZodString>;
95
+ start: z.ZodNullable<z.ZodString>;
96
+ scopes: z.ZodArray<z.ZodString>;
97
+ createdAt: z.ZodCoercedDate<unknown>;
98
+ lastUsedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
99
+ }, z.core.$strip>>;
100
+ declare const CreatedApiKey: z.ZodObject<{
101
+ id: z.ZodString;
102
+ name: z.ZodNullable<z.ZodString>;
103
+ start: z.ZodNullable<z.ZodString>;
104
+ scopes: z.ZodArray<z.ZodString>;
105
+ createdAt: z.ZodCoercedDate<unknown>;
106
+ lastUsedAt: z.ZodNullable<z.ZodCoercedDate<unknown>>;
107
+ key: z.ZodString;
108
+ }, z.core.$strip>;
109
+ declare const CreateApiKey: z.ZodObject<{
110
+ name: z.ZodString;
111
+ scopes: z.ZodArray<z.ZodEnum<{
112
+ "courses:read": "courses:read";
113
+ "courses:write": "courses:write";
114
+ "courses:delete": "courses:delete";
115
+ "downloads:read": "downloads:read";
116
+ "downloads:write": "downloads:write";
117
+ "downloads:delete": "downloads:delete";
118
+ "bundles:read": "bundles:read";
119
+ "bundles:write": "bundles:write";
120
+ "bundles:delete": "bundles:delete";
121
+ "media:read": "media:read";
122
+ "students:read": "students:read";
123
+ "students:invite": "students:invite";
124
+ "access:read": "access:read";
125
+ "access:write": "access:write";
126
+ "analytics:read": "analytics:read";
127
+ "automations:read": "automations:read";
128
+ "automations:write": "automations:write";
129
+ "automations:delete": "automations:delete";
130
+ "comments:read": "comments:read";
131
+ "comments:moderate": "comments:moderate";
132
+ "communities:read": "communities:read";
133
+ "communities:write": "communities:write";
134
+ "communities:delete": "communities:delete";
135
+ "community_members:read": "community_members:read";
136
+ "community_members:write": "community_members:write";
137
+ "community_moderation:read": "community_moderation:read";
138
+ "community_moderation:write": "community_moderation:write";
139
+ }>>;
140
+ }, z.core.$strip>;
141
+ declare const ApiKeyIdParam: z.ZodObject<{
142
+ id: z.ZodString;
143
+ }, z.core.$strip>;
144
+ //#endregion
145
+ export { ApiKey, ApiKeyIdParam, ApiKeyList, ClientIdParam, ConnectedApp, ConnectedAppList, CreateApiKey, CreatedApiKey, ScopeOption, ScopeOptionList };
146
+ //# sourceMappingURL=access.d.ts.map
@@ -0,0 +1,36 @@
1
+ import { ACCESS_SCOPES } from "@headless-lms/core/organizations";
2
+ import { z } from "zod";
3
+ //#region src/http/schemas/access.ts
4
+ const ScopeOption = z.object({
5
+ scope: z.enum(ACCESS_SCOPES),
6
+ allowed: z.boolean()
7
+ });
8
+ const ScopeOptionList = z.array(ScopeOption);
9
+ const ConnectedApp = z.object({
10
+ clientId: z.string(),
11
+ name: z.string(),
12
+ iconUrl: z.string().nullable(),
13
+ scopes: z.array(z.string()),
14
+ connectedAt: z.coerce.date()
15
+ });
16
+ const ConnectedAppList = z.array(ConnectedApp);
17
+ const ClientIdParam = z.object({ clientId: z.string() });
18
+ const ApiKey = z.object({
19
+ id: z.string(),
20
+ name: z.string().nullable(),
21
+ start: z.string().nullable(),
22
+ scopes: z.array(z.string()),
23
+ createdAt: z.coerce.date(),
24
+ lastUsedAt: z.coerce.date().nullable()
25
+ });
26
+ const ApiKeyList = z.array(ApiKey);
27
+ const CreatedApiKey = ApiKey.extend({ key: z.string() });
28
+ const CreateApiKey = z.object({
29
+ name: z.string().trim().min(1).max(80),
30
+ scopes: z.array(z.enum(ACCESS_SCOPES)).min(1)
31
+ });
32
+ const ApiKeyIdParam = z.object({ id: z.string() });
33
+ //#endregion
34
+ export { ApiKey, ApiKeyIdParam, ApiKeyList, ClientIdParam, ConnectedApp, ConnectedAppList, CreateApiKey, CreatedApiKey, ScopeOption, ScopeOptionList };
35
+
36
+ //# sourceMappingURL=access.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"access.js","names":[],"sources":["../../../src/http/schemas/access.ts"],"sourcesContent":["import { z } from 'zod';\nimport { ACCESS_SCOPES } from '@headless-lms/core/organizations';\n\nexport const ScopeOption = z.object({ scope: z.enum(ACCESS_SCOPES), allowed: z.boolean() });\nexport const ScopeOptionList = z.array(ScopeOption);\n\nexport const ConnectedApp = z.object({\n clientId: z.string(),\n name: z.string(),\n iconUrl: z.string().nullable(),\n scopes: z.array(z.string()),\n connectedAt: z.coerce.date(),\n});\nexport const ConnectedAppList = z.array(ConnectedApp);\nexport const ClientIdParam = z.object({ clientId: z.string() });\n\nexport const ApiKey = z.object({\n id: z.string(),\n name: z.string().nullable(),\n start: z.string().nullable(),\n scopes: z.array(z.string()),\n createdAt: z.coerce.date(),\n lastUsedAt: z.coerce.date().nullable(),\n});\nexport const ApiKeyList = z.array(ApiKey);\nexport const CreatedApiKey = ApiKey.extend({ key: z.string() });\nexport const CreateApiKey = z.object({\n name: z.string().trim().min(1).max(80),\n scopes: z.array(z.enum(ACCESS_SCOPES)).min(1),\n});\nexport const ApiKeyIdParam = z.object({ id: z.string() });\n"],"mappings":";;;AAGA,MAAa,cAAc,EAAE,OAAO;CAAE,OAAO,EAAE,KAAK,aAAa;CAAG,SAAS,EAAE,QAAQ;AAAE,CAAC;AAC1F,MAAa,kBAAkB,EAAE,MAAM,WAAW;AAElD,MAAa,eAAe,EAAE,OAAO;CACnC,UAAU,EAAE,OAAO;CACnB,MAAM,EAAE,OAAO;CACf,SAAS,EAAE,OAAO,CAAC,CAAC,SAAS;CAC7B,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC;CAC1B,aAAa,EAAE,OAAO,KAAK;AAC7B,CAAC;AACD,MAAa,mBAAmB,EAAE,MAAM,YAAY;AACpD,MAAa,gBAAgB,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC;AAE9D,MAAa,SAAS,EAAE,OAAO;CAC7B,IAAI,EAAE,OAAO;CACb,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS;CAC1B,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS;CAC3B,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC;CAC1B,WAAW,EAAE,OAAO,KAAK;CACzB,YAAY,EAAE,OAAO,KAAK,CAAC,CAAC,SAAS;AACvC,CAAC;AACD,MAAa,aAAa,EAAE,MAAM,MAAM;AACxC,MAAa,gBAAgB,OAAO,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AAC9D,MAAa,eAAe,EAAE,OAAO;CACnC,MAAM,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE;CACrC,QAAQ,EAAE,MAAM,EAAE,KAAK,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC;AAC9C,CAAC;AACD,MAAa,gBAAgB,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC"}
@@ -9,6 +9,7 @@ declare const Activity: z.ZodObject<{
9
9
  settings: z.ZodNullable<z.ZodType<import("@headless-lms/core/types").JsonValue, unknown, z.core.$ZodTypeInternals<import("@headless-lms/core/types").JsonValue, unknown>>>;
10
10
  createdAt: z.ZodCoercedDate<unknown>;
11
11
  updatedAt: z.ZodCoercedDate<unknown>;
12
+ assetUrls: z.ZodRecord<z.ZodString, z.ZodString>;
12
13
  }, z.core.$strict>;
13
14
  type Activity = z.infer<typeof Activity>;
14
15
  declare const ActivityAsset: z.ZodObject<{
@@ -42,7 +43,7 @@ declare const ModuleList: z.ZodArray<z.ZodObject<{
42
43
  updatedAt: z.ZodCoercedDate<unknown>;
43
44
  }, z.core.$strict>>;
44
45
  type ModuleList = z.infer<typeof ModuleList>;
45
- /** A course's activities as bare rows, ordered by (moduleId, seq). */
46
+ /** A course's activities, each with the stable file URLs of the files it links, ordered by (moduleId, seq). */
46
47
  declare const ActivityList: z.ZodArray<z.ZodObject<{
47
48
  orgId: z.ZodString;
48
49
  id: z.ZodString;
@@ -52,6 +53,7 @@ declare const ActivityList: z.ZodArray<z.ZodObject<{
52
53
  settings: z.ZodNullable<z.ZodType<import("@headless-lms/core/types").JsonValue, unknown, z.core.$ZodTypeInternals<import("@headless-lms/core/types").JsonValue, unknown>>>;
53
54
  createdAt: z.ZodCoercedDate<unknown>;
54
55
  updatedAt: z.ZodCoercedDate<unknown>;
56
+ assetUrls: z.ZodRecord<z.ZodString, z.ZodString>;
55
57
  }, z.core.$strict>>;
56
58
  type ActivityList = z.infer<typeof ActivityList>;
57
59
  /** A course's activity→asset link rows, ordered by (activityId, seq). */