@headless-lms/server 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (351) hide show
  1. package/README.md +1 -1
  2. package/dist/_virtual/_rolldown/runtime.js +5 -0
  3. package/dist/app/community-mail.d.ts +26 -0
  4. package/dist/app/community-mail.js +96 -0
  5. package/dist/app/community-mail.js.map +1 -0
  6. package/dist/app/container.d.ts +22 -14
  7. package/dist/app/container.js +186 -62
  8. package/dist/app/container.js.map +1 -1
  9. package/dist/app/mcp-endpoints.d.ts +13 -0
  10. package/dist/app/mcp-endpoints.js +25 -0
  11. package/dist/app/mcp-endpoints.js.map +1 -0
  12. package/dist/http/config.d.ts +2 -1
  13. package/dist/http/errors.d.ts +14 -0
  14. package/dist/http/errors.js +147 -0
  15. package/dist/http/errors.js.map +1 -0
  16. package/dist/http/mcp/catalogue.d.ts +37 -0
  17. package/dist/http/mcp/catalogue.js +69 -0
  18. package/dist/http/mcp/catalogue.js.map +1 -0
  19. package/dist/http/mcp/challenge.d.ts +7 -0
  20. package/dist/http/mcp/challenge.js +35 -0
  21. package/dist/http/mcp/challenge.js.map +1 -0
  22. package/dist/http/mcp/input-schema.d.ts +7 -0
  23. package/dist/http/mcp/input-schema.js +28 -0
  24. package/dist/http/mcp/input-schema.js.map +1 -0
  25. package/dist/http/mcp/route.d.ts +8 -0
  26. package/dist/http/mcp/route.js +75 -0
  27. package/dist/http/mcp/route.js.map +1 -0
  28. package/dist/http/mcp/server.d.ts +8 -0
  29. package/dist/http/mcp/server.js +31 -0
  30. package/dist/http/mcp/server.js.map +1 -0
  31. package/dist/http/mcp/test-support.d.ts +55 -0
  32. package/dist/http/mcp/test-support.js +151 -0
  33. package/dist/http/mcp/test-support.js.map +1 -0
  34. package/dist/http/mcp/tool.d.ts +48 -0
  35. package/dist/http/mcp/tool.js +58 -0
  36. package/dist/http/mcp/tool.js.map +1 -0
  37. package/dist/http/mcp/tools/automations.d.ts +6 -0
  38. package/dist/http/mcp/tools/automations.js +120 -0
  39. package/dist/http/mcp/tools/automations.js.map +1 -0
  40. package/dist/http/mcp/tools/catalog.d.ts +6 -0
  41. package/dist/http/mcp/tools/catalog.js +200 -0
  42. package/dist/http/mcp/tools/catalog.js.map +1 -0
  43. package/dist/http/mcp/tools/comments.d.ts +6 -0
  44. package/dist/http/mcp/tools/comments.js +70 -0
  45. package/dist/http/mcp/tools/comments.js.map +1 -0
  46. package/dist/http/mcp/tools/communities.d.ts +6 -0
  47. package/dist/http/mcp/tools/communities.js +175 -0
  48. package/dist/http/mcp/tools/communities.js.map +1 -0
  49. package/dist/http/mcp/tools/community-members.d.ts +6 -0
  50. package/dist/http/mcp/tools/community-members.js +89 -0
  51. package/dist/http/mcp/tools/community-members.js.map +1 -0
  52. package/dist/http/mcp/tools/community-moderation.d.ts +6 -0
  53. package/dist/http/mcp/tools/community-moderation.js +144 -0
  54. package/dist/http/mcp/tools/community-moderation.js.map +1 -0
  55. package/dist/http/mcp/tools/courses.d.ts +6 -0
  56. package/dist/http/mcp/tools/courses.js +121 -0
  57. package/dist/http/mcp/tools/courses.js.map +1 -0
  58. package/dist/http/mcp/tools/index.d.ts +6 -0
  59. package/dist/http/mcp/tools/index.js +23 -0
  60. package/dist/http/mcp/tools/index.js.map +1 -0
  61. package/dist/http/mcp/tools/people.d.ts +6 -0
  62. package/dist/http/mcp/tools/people.js +145 -0
  63. package/dist/http/mcp/tools/people.js.map +1 -0
  64. package/dist/http/plugins/auth.js +13 -5
  65. package/dist/http/plugins/auth.js.map +1 -1
  66. package/dist/http/plugins/error-handler.d.ts +3 -2
  67. package/dist/http/plugins/error-handler.js +52 -57
  68. package/dist/http/plugins/error-handler.js.map +1 -1
  69. package/dist/http/plugins/openapi.js +70 -20
  70. package/dist/http/plugins/openapi.js.map +1 -1
  71. package/dist/http/routes/access.d.ts +7 -0
  72. package/dist/http/routes/access.js +140 -0
  73. package/dist/http/routes/access.js.map +1 -0
  74. package/dist/http/routes/activities.js +97 -20
  75. package/dist/http/routes/activities.js.map +1 -1
  76. package/dist/http/routes/assets.js +60 -51
  77. package/dist/http/routes/assets.js.map +1 -1
  78. package/dist/http/routes/automations.js +64 -20
  79. package/dist/http/routes/automations.js.map +1 -1
  80. package/dist/http/routes/bundles.d.ts +7 -0
  81. package/dist/http/routes/bundles.js +181 -0
  82. package/dist/http/routes/bundles.js.map +1 -0
  83. package/dist/http/routes/communities.d.ts +7 -0
  84. package/dist/http/routes/communities.js +462 -0
  85. package/dist/http/routes/communities.js.map +1 -0
  86. package/dist/http/routes/community-moderation.d.ts +7 -0
  87. package/dist/http/routes/community-moderation.js +425 -0
  88. package/dist/http/routes/community-moderation.js.map +1 -0
  89. package/dist/http/routes/course-analytics.d.ts +7 -0
  90. package/dist/http/routes/course-analytics.js +60 -0
  91. package/dist/http/routes/course-analytics.js.map +1 -0
  92. package/dist/http/routes/courses.js +38 -23
  93. package/dist/http/routes/courses.js.map +1 -1
  94. package/dist/http/routes/dashboard.js +17 -8
  95. package/dist/http/routes/dashboard.js.map +1 -1
  96. package/dist/http/routes/discussion.js +35 -27
  97. package/dist/http/routes/discussion.js.map +1 -1
  98. package/dist/http/routes/downloads.js +58 -40
  99. package/dist/http/routes/downloads.js.map +1 -1
  100. package/dist/http/routes/entitlements.js +16 -7
  101. package/dist/http/routes/entitlements.js.map +1 -1
  102. package/dist/http/routes/integrations.js +38 -41
  103. package/dist/http/routes/integrations.js.map +1 -1
  104. package/dist/http/routes/member/actor.d.ts +14 -0
  105. package/dist/http/routes/member/actor.js +11 -0
  106. package/dist/http/routes/member/actor.js.map +1 -0
  107. package/dist/http/routes/member/assets.d.ts +7 -0
  108. package/dist/http/routes/member/assets.js +79 -0
  109. package/dist/http/routes/member/assets.js.map +1 -0
  110. package/dist/http/routes/member/chats.d.ts +7 -0
  111. package/dist/http/routes/member/chats.js +456 -0
  112. package/dist/http/routes/member/chats.js.map +1 -0
  113. package/dist/http/routes/member/comments.d.ts +7 -0
  114. package/dist/http/routes/member/comments.js +187 -0
  115. package/dist/http/routes/member/comments.js.map +1 -0
  116. package/dist/http/routes/member/community.d.ts +7 -0
  117. package/dist/http/routes/member/community.js +288 -0
  118. package/dist/http/routes/member/community.js.map +1 -0
  119. package/dist/http/routes/member/courses.d.ts +7 -0
  120. package/dist/http/routes/member/courses.js +101 -0
  121. package/dist/http/routes/member/courses.js.map +1 -0
  122. package/dist/http/routes/member/downloads.d.ts +7 -0
  123. package/dist/http/routes/member/downloads.js +56 -0
  124. package/dist/http/routes/member/downloads.js.map +1 -0
  125. package/dist/http/routes/member/files.d.ts +7 -0
  126. package/dist/http/routes/member/files.js +184 -0
  127. package/dist/http/routes/member/files.js.map +1 -0
  128. package/dist/http/routes/member/index.d.ts +7 -0
  129. package/dist/http/routes/member/index.js +31 -0
  130. package/dist/http/routes/member/index.js.map +1 -0
  131. package/dist/http/routes/member/notifications.d.ts +7 -0
  132. package/dist/http/routes/member/notifications.js +101 -0
  133. package/dist/http/routes/member/notifications.js.map +1 -0
  134. package/dist/http/routes/member/org.d.ts +7 -0
  135. package/dist/http/routes/member/org.js +56 -0
  136. package/dist/http/routes/member/org.js.map +1 -0
  137. package/dist/http/routes/member/posts.d.ts +7 -0
  138. package/dist/http/routes/member/posts.js +612 -0
  139. package/dist/http/routes/member/posts.js.map +1 -0
  140. package/dist/http/routes/member/profile.d.ts +7 -0
  141. package/dist/http/routes/member/profile.js +48 -0
  142. package/dist/http/routes/member/profile.js.map +1 -0
  143. package/dist/http/routes/member/progress.d.ts +7 -0
  144. package/dist/http/routes/member/progress.js +60 -0
  145. package/dist/http/routes/member/progress.js.map +1 -0
  146. package/dist/http/routes/organizations.js +49 -24
  147. package/dist/http/routes/organizations.js.map +1 -1
  148. package/dist/http/routes/students.js +43 -37
  149. package/dist/http/routes/students.js.map +1 -1
  150. package/dist/http/routes/webhooks.d.ts +7 -0
  151. package/dist/http/routes/webhooks.js +167 -0
  152. package/dist/http/routes/webhooks.js.map +1 -0
  153. package/dist/http/routes.js +14 -2
  154. package/dist/http/routes.js.map +1 -1
  155. package/dist/http/schemas/access.d.ts +146 -0
  156. package/dist/http/schemas/access.js +36 -0
  157. package/dist/http/schemas/access.js.map +1 -0
  158. package/dist/http/schemas/activities.d.ts +36 -1
  159. package/dist/http/schemas/activities.js +9 -4
  160. package/dist/http/schemas/activities.js.map +1 -1
  161. package/dist/http/schemas/assets.d.ts +76 -73
  162. package/dist/http/schemas/assets.js +23 -13
  163. package/dist/http/schemas/assets.js.map +1 -1
  164. package/dist/http/schemas/automations.d.ts +85 -8
  165. package/dist/http/schemas/automations.js +13 -2
  166. package/dist/http/schemas/automations.js.map +1 -1
  167. package/dist/http/schemas/bundles.d.ts +63 -0
  168. package/dist/http/schemas/bundles.js +23 -0
  169. package/dist/http/schemas/bundles.js.map +1 -0
  170. package/dist/http/schemas/chats.d.ts +386 -0
  171. package/dist/http/schemas/chats.js +58 -0
  172. package/dist/http/schemas/chats.js.map +1 -0
  173. package/dist/http/schemas/community.d.ts +537 -0
  174. package/dist/http/schemas/community.js +129 -0
  175. package/dist/http/schemas/community.js.map +1 -0
  176. package/dist/http/schemas/content.d.ts +22 -13
  177. package/dist/http/schemas/content.js +11 -9
  178. package/dist/http/schemas/content.js.map +1 -1
  179. package/dist/http/schemas/course-analytics.d.ts +29 -0
  180. package/dist/http/schemas/course-analytics.js +21 -0
  181. package/dist/http/schemas/course-analytics.js.map +1 -0
  182. package/dist/http/schemas/dashboard.js.map +1 -1
  183. package/dist/http/schemas/discussion.d.ts +85 -23
  184. package/dist/http/schemas/discussion.js +12 -11
  185. package/dist/http/schemas/discussion.js.map +1 -1
  186. package/dist/http/schemas/downloads.d.ts +13 -5
  187. package/dist/http/schemas/downloads.js +14 -11
  188. package/dist/http/schemas/downloads.js.map +1 -1
  189. package/dist/http/schemas/entitlements.d.ts +14 -6
  190. package/dist/http/schemas/entitlements.js +8 -6
  191. package/dist/http/schemas/entitlements.js.map +1 -1
  192. package/dist/http/schemas/files.d.ts +45 -0
  193. package/dist/http/schemas/files.js +25 -0
  194. package/dist/http/schemas/files.js.map +1 -0
  195. package/dist/http/schemas/index.d.ts +19 -10
  196. package/dist/http/schemas/index.js +19 -10
  197. package/dist/http/schemas/integrations.d.ts +4 -4
  198. package/dist/http/schemas/integrations.js.map +1 -1
  199. package/dist/http/schemas/invites.d.ts +4 -0
  200. package/dist/http/schemas/invites.js +3 -1
  201. package/dist/http/schemas/invites.js.map +1 -1
  202. package/dist/http/schemas/{learn.d.ts → member.d.ts} +42 -19
  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 +39 -6
  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 +227 -0
  209. package/dist/http/schemas/notifications.js +44 -0
  210. package/dist/http/schemas/notifications.js.map +1 -0
  211. package/dist/http/schemas/organizations.js.map +1 -1
  212. package/dist/http/schemas/posts.d.ts +316 -0
  213. package/dist/http/schemas/posts.js +47 -0
  214. package/dist/http/schemas/posts.js.map +1 -0
  215. package/dist/http/schemas/profile.d.ts +23 -0
  216. package/dist/http/schemas/profile.js +15 -0
  217. package/dist/http/schemas/profile.js.map +1 -0
  218. package/dist/http/schemas/settings.js.map +1 -1
  219. package/dist/http/schemas/shared.d.ts +26 -1
  220. package/dist/http/schemas/shared.js +22 -3
  221. package/dist/http/schemas/shared.js.map +1 -1
  222. package/dist/http/schemas/students.d.ts +32 -10
  223. package/dist/http/schemas/students.js +19 -6
  224. package/dist/http/schemas/students.js.map +1 -1
  225. package/dist/http/scope.d.ts +2 -1
  226. package/dist/http/scope.js +20 -4
  227. package/dist/http/scope.js.map +1 -1
  228. package/dist/http/server.js +10 -3
  229. package/dist/http/server.js.map +1 -1
  230. package/dist/http/web-bridge.d.ts +1 -1
  231. package/dist/http/web-bridge.js +2 -2
  232. package/dist/http/web-bridge.js.map +1 -1
  233. package/dist/http/well-known.d.ts +7 -0
  234. package/dist/http/well-known.js +22 -0
  235. package/dist/http/well-known.js.map +1 -0
  236. package/dist/index.d.ts +3 -2
  237. package/dist/index.js +4 -2
  238. package/dist/index.js.map +1 -1
  239. package/dist/node_modules/.pnpm/@jridgewell_sourcemap-codec@1.5.5/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.js +78 -0
  240. package/dist/node_modules/.pnpm/@jridgewell_sourcemap-codec@1.5.5/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.js.map +1 -0
  241. package/dist/node_modules/.pnpm/@vitest_expect@2.1.9/node_modules/@vitest/expect/dist/index.js +1267 -0
  242. package/dist/node_modules/.pnpm/@vitest_expect@2.1.9/node_modules/@vitest/expect/dist/index.js.map +1 -0
  243. package/dist/node_modules/.pnpm/@vitest_pretty-format@2.1.9/node_modules/@vitest/pretty-format/dist/index.js +814 -0
  244. package/dist/node_modules/.pnpm/@vitest_pretty-format@2.1.9/node_modules/@vitest/pretty-format/dist/index.js.map +1 -0
  245. package/dist/node_modules/.pnpm/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/chunk-tasks.js +43 -0
  246. package/dist/node_modules/.pnpm/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/chunk-tasks.js.map +1 -0
  247. package/dist/node_modules/.pnpm/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/index.js +527 -0
  248. package/dist/node_modules/.pnpm/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/index.js.map +1 -0
  249. package/dist/node_modules/.pnpm/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/utils.js +2 -0
  250. package/dist/node_modules/.pnpm/@vitest_snapshot@2.1.9/node_modules/@vitest/snapshot/dist/index.js +1737 -0
  251. package/dist/node_modules/.pnpm/@vitest_snapshot@2.1.9/node_modules/@vitest/snapshot/dist/index.js.map +1 -0
  252. package/dist/node_modules/.pnpm/@vitest_spy@2.1.9/node_modules/@vitest/spy/dist/index.js +137 -0
  253. package/dist/node_modules/.pnpm/@vitest_spy@2.1.9/node_modules/@vitest/spy/dist/index.js.map +1 -0
  254. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/chunk-_commonjsHelpers.js +118 -0
  255. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/chunk-_commonjsHelpers.js.map +1 -0
  256. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/diff.js +1222 -0
  257. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/diff.js.map +1 -0
  258. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/error.js +91 -0
  259. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/error.js.map +1 -0
  260. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/helpers.js +113 -0
  261. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/helpers.js.map +1 -0
  262. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/index.js +449 -0
  263. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/index.js.map +1 -0
  264. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/source-map.js +163 -0
  265. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/source-map.js.map +1 -0
  266. package/dist/node_modules/.pnpm/chai@5.3.3/node_modules/chai/index.js +3187 -0
  267. package/dist/node_modules/.pnpm/chai@5.3.3/node_modules/chai/index.js.map +1 -0
  268. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/arguments.js +11 -0
  269. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/arguments.js.map +1 -0
  270. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/array.js +16 -0
  271. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/array.js.map +1 -0
  272. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/bigint.js +11 -0
  273. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/bigint.js.map +1 -0
  274. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/class.js +15 -0
  275. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/class.js.map +1 -0
  276. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/date.js +13 -0
  277. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/date.js.map +1 -0
  278. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/error.js +34 -0
  279. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/error.js.map +1 -0
  280. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/function.js +12 -0
  281. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/function.js.map +1 -0
  282. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/helpers.js +122 -0
  283. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/helpers.js.map +1 -0
  284. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/html.js +39 -0
  285. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/html.js.map +1 -0
  286. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/index.js +99 -0
  287. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/index.js.map +1 -0
  288. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/map.js +25 -0
  289. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/map.js.map +1 -0
  290. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/number.js +14 -0
  291. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/number.js.map +1 -0
  292. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/object.js +21 -0
  293. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/object.js.map +1 -0
  294. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/promise.js +6 -0
  295. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/promise.js.map +1 -0
  296. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/regexp.js +12 -0
  297. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/regexp.js.map +1 -0
  298. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/set.js +18 -0
  299. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/set.js.map +1 -0
  300. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/string.js +24 -0
  301. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/string.js.map +1 -0
  302. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/symbol.js +9 -0
  303. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/symbol.js.map +1 -0
  304. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/typedarray.js +30 -0
  305. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/typedarray.js.map +1 -0
  306. package/dist/node_modules/.pnpm/magic-string@0.30.21/node_modules/magic-string/dist/magic-string.es.js +939 -0
  307. package/dist/node_modules/.pnpm/magic-string@0.30.21/node_modules/magic-string/dist/magic-string.es.js.map +1 -0
  308. package/dist/node_modules/.pnpm/pathe@1.1.2/node_modules/pathe/dist/shared/pathe.ff20891b.js +82 -0
  309. package/dist/node_modules/.pnpm/pathe@1.1.2/node_modules/pathe/dist/shared/pathe.ff20891b.js.map +1 -0
  310. package/dist/node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/chunk-BVHSVHOK.js +82 -0
  311. package/dist/node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/chunk-BVHSVHOK.js.map +1 -0
  312. package/dist/node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/node.js +8 -0
  313. package/dist/node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/node.js.map +1 -0
  314. package/dist/node_modules/.pnpm/tinyspy@3.0.2/node_modules/tinyspy/dist/index.js +83 -0
  315. package/dist/node_modules/.pnpm/tinyspy@3.0.2/node_modules/tinyspy/dist/index.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/_commonjsHelpers.BFTU3MAI.js +9 -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/_commonjsHelpers.BFTU3MAI.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/date.W2xKR2qe.js +51 -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/date.W2xKR2qe.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/utils.C8RiOc4B.js +48 -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/utils.C8RiOc4B.js.map +1 -0
  322. 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
  323. 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
  324. package/dist/packages/server/package.js +6 -0
  325. package/dist/packages/server/package.js.map +1 -0
  326. package/package.json +9 -7
  327. package/dist/app/notifications.d.ts +0 -7
  328. package/dist/app/notifications.js +0 -23
  329. package/dist/app/notifications.js.map +0 -1
  330. package/dist/http/routes/learn/assets.d.ts +0 -8
  331. package/dist/http/routes/learn/assets.js +0 -32
  332. package/dist/http/routes/learn/assets.js.map +0 -1
  333. package/dist/http/routes/learn/comments.d.ts +0 -7
  334. package/dist/http/routes/learn/comments.js +0 -175
  335. package/dist/http/routes/learn/comments.js.map +0 -1
  336. package/dist/http/routes/learn/courses.d.ts +0 -7
  337. package/dist/http/routes/learn/courses.js +0 -75
  338. package/dist/http/routes/learn/courses.js.map +0 -1
  339. package/dist/http/routes/learn/downloads.d.ts +0 -7
  340. package/dist/http/routes/learn/downloads.js +0 -76
  341. package/dist/http/routes/learn/downloads.js.map +0 -1
  342. package/dist/http/routes/learn/index.d.ts +0 -7
  343. package/dist/http/routes/learn/index.js +0 -19
  344. package/dist/http/routes/learn/index.js.map +0 -1
  345. package/dist/http/routes/learn/org.d.ts +0 -7
  346. package/dist/http/routes/learn/org.js +0 -50
  347. package/dist/http/routes/learn/org.js.map +0 -1
  348. package/dist/http/routes/learn/progress.d.ts +0 -7
  349. package/dist/http/routes/learn/progress.js +0 -63
  350. package/dist/http/routes/learn/progress.js.map +0 -1
  351. package/dist/http/schemas/learn.js.map +0 -1
@@ -1,59 +1,109 @@
1
- import { jsonSchemaTransform } from "fastify-type-provider-zod";
1
+ import { ErrorBody, ValidationErrorBody } from "../schemas/shared.js";
2
+ import { version } from "../../packages/server/package.js";
3
+ import { jsonSchemaTransform, jsonSchemaTransformObject } from "fastify-type-provider-zod";
2
4
  import swagger from "@fastify/swagger";
3
5
  import swaggerUi from "@fastify/swagger-ui";
4
6
  //#region src/http/plugins/openapi.ts
7
+ const PUBLIC_ROUTES = /* @__PURE__ */ new Set(["GET /api/organizations/invites/:token"]);
8
+ function withDefaultResponses(schema, method, url) {
9
+ if (!schema || !url.startsWith("/api/") || url.startsWith("/api/auth/")) return schema;
10
+ const response = { ...schema.response ?? {} };
11
+ if ((schema.body || schema.querystring || schema.params) && !response["400"]) response["400"] = ValidationErrorBody;
12
+ if (!PUBLIC_ROUTES.has(`${method} ${url}`) && !response["401"]) response["401"] = ErrorBody;
13
+ if (!response["500"]) response["500"] = ErrorBody;
14
+ return {
15
+ ...schema,
16
+ response
17
+ };
18
+ }
5
19
  function registerOpenApi(app, config) {
6
20
  app.register(swagger, {
7
21
  openapi: {
8
22
  info: {
9
23
  title: "Headless LMS API",
10
- version: "0.0.0"
24
+ version,
25
+ description: "API documentation for the Headless LMS platform",
26
+ license: {
27
+ name: "MIT",
28
+ url: "https://opensource.org/licenses/MIT"
29
+ }
11
30
  },
12
31
  servers: [{ url: config.publicUrl }],
13
32
  tags: [
14
33
  {
15
34
  name: "Organizations",
16
- description: "Organizations and member management"
35
+ description: "The tenant: its members, roles, invites and students"
17
36
  },
18
37
  {
19
- name: "Courses",
20
- description: "Course authoring: courses, modules, activities"
38
+ name: "Content",
39
+ description: "Authored content: courses and downloads, their structure and delivery"
21
40
  },
22
41
  {
23
- name: "Learn",
24
- description: "Student-facing course delivery"
42
+ name: "Entitlements",
43
+ description: "Access grants to content"
25
44
  },
26
45
  {
27
- name: "Students",
28
- description: "Students and their enrollments"
46
+ name: "Progress",
47
+ description: "Per-student progress and completion through course content"
29
48
  },
30
49
  {
31
- name: "Entitlements",
32
- description: "Access grants to content"
50
+ name: "Discussion",
51
+ description: "Learner comments, replies, reactions and moderation"
33
52
  },
34
53
  {
35
- name: "Progress",
36
- description: "Completion tracking"
54
+ name: "Communities",
55
+ description: "Communities, their spaces, topics, and direct-message settings"
37
56
  },
38
57
  {
39
- name: "Automations",
40
- description: "Trigger/action workflows"
58
+ name: "Posts",
59
+ description: "Posts in a space, reactions, and the moderation queue"
41
60
  },
42
61
  {
43
- name: "Dashboard",
44
- description: "Back-office overview"
62
+ name: "Chats",
63
+ description: "Direct and group chats inside one community"
64
+ },
65
+ {
66
+ name: "Profile",
67
+ description: "The signed-in member profile"
68
+ },
69
+ {
70
+ name: "Notifications",
71
+ description: "What happened to the signed-in member, and how they want to be told"
45
72
  },
46
73
  {
47
74
  name: "Assets",
48
- description: "Media library and uploads"
75
+ description: "The org media library: uploads, serving and lifecycle"
76
+ },
77
+ {
78
+ name: "Files",
79
+ description: "Stable file URLs that check access and redirect to a signed storage URL"
80
+ },
81
+ {
82
+ name: "Automations",
83
+ description: "Workflows and webhooks that react to system events"
49
84
  },
50
85
  {
51
86
  name: "Integrations",
52
- description: "Third-party integration connections"
87
+ description: "Connections to external services"
88
+ },
89
+ {
90
+ name: "Reporting",
91
+ description: "Reporting and analytics"
92
+ },
93
+ {
94
+ name: "Access",
95
+ description: "Access scopes, connected apps and API keys"
53
96
  }
54
97
  ]
55
98
  },
56
- transform: jsonSchemaTransform
99
+ transform: (input) => {
100
+ const method = String(Array.isArray(input.route.method) ? input.route.method[0] : input.route.method);
101
+ return jsonSchemaTransform({
102
+ ...input,
103
+ schema: withDefaultResponses(input.schema, method, input.url)
104
+ });
105
+ },
106
+ transformObject: jsonSchemaTransformObject
57
107
  });
58
108
  app.register(swaggerUi, { routePrefix: "/docs" });
59
109
  }
@@ -1 +1 @@
1
- {"version":3,"file":"openapi.js","names":[],"sources":["../../../src/http/plugins/openapi.ts"],"sourcesContent":["// OpenAPI documentation. @fastify/swagger reads the Zod route schemas (via the\n// transform) to build the spec the frontend SDK is generated from; it must be\n// registered before the routes so its onRoute hook captures them. UI + JSON are\n// served under /docs.\nimport type { FastifyInstance } from 'fastify';\nimport swagger from '@fastify/swagger';\nimport swaggerUi from '@fastify/swagger-ui';\nimport { jsonSchemaTransform } from 'fastify-type-provider-zod';\nimport type { ServerConfig } from '../config.js';\n\nexport function registerOpenApi(app: FastifyInstance, config: ServerConfig): void {\n app.register(swagger, {\n openapi: {\n info: { title: 'Headless LMS API', version: '0.0.0' },\n servers: [{ url: config.publicUrl }],\n tags: [\n { name: 'Organizations', description: 'Organizations and member management' },\n { name: 'Courses', description: 'Course authoring: courses, modules, activities' },\n { name: 'Learn', description: 'Student-facing course delivery' },\n { name: 'Students', description: 'Students and their enrollments' },\n { name: 'Entitlements', description: 'Access grants to content' },\n { name: 'Progress', description: 'Completion tracking' },\n { name: 'Automations', description: 'Trigger/action workflows' },\n { name: 'Dashboard', description: 'Back-office overview' },\n { name: 'Assets', description: 'Media library and uploads' },\n { name: 'Integrations', description: 'Third-party integration connections' },\n ],\n },\n transform: jsonSchemaTransform,\n });\n app.register(swaggerUi, { routePrefix: '/docs' });\n}\n"],"mappings":";;;;AAUA,SAAgB,gBAAgB,KAAsB,QAA4B;CAChF,IAAI,SAAS,SAAS;EACpB,SAAS;GACP,MAAM;IAAE,OAAO;IAAoB,SAAS;GAAQ;GACpD,SAAS,CAAC,EAAE,KAAK,OAAO,UAAU,CAAC;GACnC,MAAM;IACJ;KAAE,MAAM;KAAiB,aAAa;IAAsC;IAC5E;KAAE,MAAM;KAAW,aAAa;IAAiD;IACjF;KAAE,MAAM;KAAS,aAAa;IAAiC;IAC/D;KAAE,MAAM;KAAY,aAAa;IAAiC;IAClE;KAAE,MAAM;KAAgB,aAAa;IAA2B;IAChE;KAAE,MAAM;KAAY,aAAa;IAAsB;IACvD;KAAE,MAAM;KAAe,aAAa;IAA2B;IAC/D;KAAE,MAAM;KAAa,aAAa;IAAuB;IACzD;KAAE,MAAM;KAAU,aAAa;IAA4B;IAC3D;KAAE,MAAM;KAAgB,aAAa;IAAsC;GAC7E;EACF;EACA,WAAW;CACb,CAAC;CACD,IAAI,SAAS,WAAW,EAAE,aAAa,QAAQ,CAAC;AAClD"}
1
+ {"version":3,"file":"openapi.js","names":["pkg.version"],"sources":["../../../src/http/plugins/openapi.ts"],"sourcesContent":["// OpenAPI documentation. @fastify/swagger reads the Zod route schemas (via the\n// transform) to build the spec the frontend SDK is generated from; it must be\n// registered before the routes so its onRoute hook captures them. UI + JSON are\n// served under /docs.\nimport type { FastifyInstance } from 'fastify';\nimport swagger from '@fastify/swagger';\nimport swaggerUi from '@fastify/swagger-ui';\nimport { jsonSchemaTransform, jsonSchemaTransformObject } from 'fastify-type-provider-zod';\nimport type { ServerConfig } from '../config.js';\nimport { ErrorBody, ValidationErrorBody } from '../schemas/shared.js';\nimport pkg from '../../../package.json' with { type: 'json' };\n\nconst PUBLIC_ROUTES = new Set(['GET /api/organizations/invites/:token']);\n\ntype RouteSchema = {\n body?: unknown;\n querystring?: unknown;\n params?: unknown;\n response?: Record<string, unknown>;\n};\n\nfunction withDefaultResponses(\n schema: RouteSchema | undefined,\n method: string,\n url: string,\n): RouteSchema | undefined {\n if (!schema || !url.startsWith('/api/') || url.startsWith('/api/auth/')) {\n return schema;\n }\n const response: Record<string, unknown> = { ...(schema.response ?? {}) };\n if ((schema.body || schema.querystring || schema.params) && !response['400']) {\n response['400'] = ValidationErrorBody;\n }\n if (!PUBLIC_ROUTES.has(`${method} ${url}`) && !response['401']) {\n response['401'] = ErrorBody;\n }\n if (!response['500']) {\n response['500'] = ErrorBody;\n }\n return { ...schema, response };\n}\n\nexport function registerOpenApi(app: FastifyInstance, config: ServerConfig): void {\n app.register(swagger, {\n openapi: {\n info: {\n title: 'Headless LMS API',\n version: pkg.version,\n description: 'API documentation for the Headless LMS platform',\n license: {\n name: 'MIT',\n url: 'https://opensource.org/licenses/MIT',\n },\n },\n servers: [{ url: config.publicUrl }],\n tags: [\n {\n name: 'Organizations',\n description: 'The tenant: its members, roles, invites and students',\n },\n {\n name: 'Content',\n description: 'Authored content: courses and downloads, their structure and delivery',\n },\n { name: 'Entitlements', description: 'Access grants to content' },\n {\n name: 'Progress',\n description: 'Per-student progress and completion through course content',\n },\n { name: 'Discussion', description: 'Learner comments, replies, reactions and moderation' },\n {\n name: 'Communities',\n description: 'Communities, their spaces, topics, and direct-message settings',\n },\n { name: 'Posts', description: 'Posts in a space, reactions, and the moderation queue' },\n { name: 'Chats', description: 'Direct and group chats inside one community' },\n { name: 'Profile', description: 'The signed-in member profile' },\n {\n name: 'Notifications',\n description: 'What happened to the signed-in member, and how they want to be told',\n },\n { name: 'Assets', description: 'The org media library: uploads, serving and lifecycle' },\n {\n name: 'Files',\n description: 'Stable file URLs that check access and redirect to a signed storage URL',\n },\n { name: 'Automations', description: 'Workflows and webhooks that react to system events' },\n { name: 'Integrations', description: 'Connections to external services' },\n { name: 'Reporting', description: 'Reporting and analytics' },\n { name: 'Access', description: 'Access scopes, connected apps and API keys' },\n ],\n },\n transform: (input) => {\n const method = String(\n Array.isArray(input.route.method) ? input.route.method[0] : input.route.method,\n );\n return jsonSchemaTransform({\n ...input,\n schema: withDefaultResponses(input.schema as RouteSchema | undefined, method, input.url),\n } as typeof input);\n },\n transformObject: jsonSchemaTransformObject,\n });\n app.register(swaggerUi, { routePrefix: '/docs' });\n}\n"],"mappings":";;;;;;AAYA,MAAM,gCAAgB,IAAI,IAAI,CAAC,uCAAuC,CAAC;AASvE,SAAS,qBACP,QACA,QACA,KACyB;CACzB,IAAI,CAAC,UAAU,CAAC,IAAI,WAAW,OAAO,KAAK,IAAI,WAAW,YAAY,GACpE,OAAO;CAET,MAAM,WAAoC,EAAE,GAAI,OAAO,YAAY,CAAC,EAAG;CACvE,KAAK,OAAO,QAAQ,OAAO,eAAe,OAAO,WAAW,CAAC,SAAS,QACpE,SAAS,SAAS;CAEpB,IAAI,CAAC,cAAc,IAAI,GAAG,OAAO,GAAG,KAAK,KAAK,CAAC,SAAS,QACtD,SAAS,SAAS;CAEpB,IAAI,CAAC,SAAS,QACZ,SAAS,SAAS;CAEpB,OAAO;EAAE,GAAG;EAAQ;CAAS;AAC/B;AAEA,SAAgB,gBAAgB,KAAsB,QAA4B;CAChF,IAAI,SAAS,SAAS;EACpB,SAAS;GACP,MAAM;IACJ,OAAO;IACEA;IACT,aAAa;IACb,SAAS;KACP,MAAM;KACN,KAAK;IACP;GACF;GACA,SAAS,CAAC,EAAE,KAAK,OAAO,UAAU,CAAC;GACnC,MAAM;IACJ;KACE,MAAM;KACN,aAAa;IACf;IACA;KACE,MAAM;KACN,aAAa;IACf;IACA;KAAE,MAAM;KAAgB,aAAa;IAA2B;IAChE;KACE,MAAM;KACN,aAAa;IACf;IACA;KAAE,MAAM;KAAc,aAAa;IAAsD;IACzF;KACE,MAAM;KACN,aAAa;IACf;IACA;KAAE,MAAM;KAAS,aAAa;IAAwD;IACtF;KAAE,MAAM;KAAS,aAAa;IAA8C;IAC5E;KAAE,MAAM;KAAW,aAAa;IAA+B;IAC/D;KACE,MAAM;KACN,aAAa;IACf;IACA;KAAE,MAAM;KAAU,aAAa;IAAwD;IACvF;KACE,MAAM;KACN,aAAa;IACf;IACA;KAAE,MAAM;KAAe,aAAa;IAAqD;IACzF;KAAE,MAAM;KAAgB,aAAa;IAAmC;IACxE;KAAE,MAAM;KAAa,aAAa;IAA0B;IAC5D;KAAE,MAAM;KAAU,aAAa;IAA6C;GAC9E;EACF;EACA,YAAY,UAAU;GACpB,MAAM,SAAS,OACb,MAAM,QAAQ,MAAM,MAAM,MAAM,IAAI,MAAM,MAAM,OAAO,KAAK,MAAM,MAAM,MAC1E;GACA,OAAO,oBAAoB;IACzB,GAAG;IACH,QAAQ,qBAAqB,MAAM,QAAmC,QAAQ,MAAM,GAAG;GACzF,CAAiB;EACnB;EACA,iBAAiB;CACnB,CAAC;CACD,IAAI,SAAS,WAAW,EAAE,aAAa,QAAQ,CAAC;AAClD"}
@@ -0,0 +1,7 @@
1
+ import { Container } from "../../app/container.js";
2
+ import { FastifyInstance } from "fastify";
3
+ //#region src/http/routes/access.d.ts
4
+ declare function accessRoutes(app: FastifyInstance, container: Container): Promise<void>;
5
+ //#endregion
6
+ export { accessRoutes };
7
+ //# sourceMappingURL=access.d.ts.map
@@ -0,0 +1,140 @@
1
+ import { refusals } from "../schemas/shared.js";
2
+ import { resolveScope } from "../scope.js";
3
+ import { ApiKeyIdParam, ApiKeyList, ClientIdParam, ConnectedAppList, CreateApiKey, CreatedApiKey, ScopeOptionList } from "../schemas/access.js";
4
+ import "../schemas/index.js";
5
+ import { assertRoleHoldsScopes, scopeOptions } from "@headless-lms/core/organizations";
6
+ import { z } from "zod";
7
+ //#region src/http/routes/access.ts
8
+ async function accessRoutes(app, container) {
9
+ const r = app.withTypeProvider();
10
+ const tags = ["Access"];
11
+ r.route({
12
+ method: "GET",
13
+ url: "/api/access/scopes",
14
+ preHandler: app.requireOrgSession,
15
+ schema: {
16
+ operationId: "listAccessScopes",
17
+ tags,
18
+ summary: "Every access scope and whether your role can grant it",
19
+ description: "Staff only (owner, admin, instructor). Each scope an app connection or API key can carry, marked with whether your role holds it. Only allowed scopes can be granted.",
20
+ response: {
21
+ 200: ScopeOptionList,
22
+ ...refusals(403)
23
+ }
24
+ },
25
+ handler: async (req) => {
26
+ return scopeOptions((await resolveScope(container, req)).role);
27
+ }
28
+ });
29
+ r.route({
30
+ method: "GET",
31
+ url: "/api/access/apps",
32
+ preHandler: app.requireOrgSession,
33
+ schema: {
34
+ operationId: "listConnectedApps",
35
+ tags,
36
+ summary: "Apps connected to this organization as you",
37
+ description: "Staff only (owner, admin, instructor). The apps you connected to this organization, each with the scopes you granted and when.",
38
+ response: {
39
+ 200: ConnectedAppList,
40
+ ...refusals(403)
41
+ }
42
+ },
43
+ handler: async (req) => {
44
+ const scope = await resolveScope(container, req);
45
+ return container.auth.listConnectedApps(req.headers, scope.authOrgId);
46
+ }
47
+ });
48
+ r.route({
49
+ method: "DELETE",
50
+ url: "/api/access/apps/:clientId",
51
+ preHandler: app.requireOrgSession,
52
+ schema: {
53
+ operationId: "revokeConnectedApp",
54
+ tags,
55
+ summary: "Disconnect an app",
56
+ description: "Staff only (owner, admin, instructor). Revokes your consent and refresh tokens for the app in this organization; its next call is refused.",
57
+ params: ClientIdParam,
58
+ response: {
59
+ 204: z.void(),
60
+ ...refusals(403)
61
+ }
62
+ },
63
+ handler: async (req, reply) => {
64
+ const scope = await resolveScope(container, req);
65
+ await container.auth.revokeConnectedApp(req.headers, scope.authOrgId, req.params.clientId);
66
+ return reply.code(204).send();
67
+ }
68
+ });
69
+ r.route({
70
+ method: "GET",
71
+ url: "/api/access/api-keys",
72
+ preHandler: app.requireOrgSession,
73
+ schema: {
74
+ operationId: "listApiKeys",
75
+ tags,
76
+ summary: "Your API keys for this organization",
77
+ description: "Staff only (owner, admin, instructor). Your keys for this organization with their scopes. The key itself is never returned again after it is created.",
78
+ response: {
79
+ 200: ApiKeyList,
80
+ ...refusals(403)
81
+ }
82
+ },
83
+ handler: async (req) => {
84
+ const scope = await resolveScope(container, req);
85
+ return container.auth.listApiKeys(req.headers, scope.authOrgId);
86
+ }
87
+ });
88
+ r.route({
89
+ method: "POST",
90
+ url: "/api/access/api-keys",
91
+ preHandler: app.requireOrgSession,
92
+ schema: {
93
+ operationId: "createApiKey",
94
+ tags,
95
+ summary: "Create an API key",
96
+ description: "Staff only (owner, admin, instructor). The key acts as you in this organization with the scopes given. 403 when your role cannot hold one of them. The key is returned once.",
97
+ body: CreateApiKey,
98
+ response: {
99
+ 201: CreatedApiKey,
100
+ ...refusals(400, 403)
101
+ }
102
+ },
103
+ handler: async (req, reply) => {
104
+ const scope = await resolveScope(container, req);
105
+ assertRoleHoldsScopes(scope.role, req.body.scopes);
106
+ const created = await container.auth.createApiKey({
107
+ authUserId: req.authUser.id,
108
+ orgId: scope.authOrgId,
109
+ name: req.body.name,
110
+ scopes: req.body.scopes
111
+ });
112
+ return reply.code(201).send(created);
113
+ }
114
+ });
115
+ r.route({
116
+ method: "DELETE",
117
+ url: "/api/access/api-keys/:id",
118
+ preHandler: app.requireOrgSession,
119
+ schema: {
120
+ operationId: "deleteApiKey",
121
+ tags,
122
+ summary: "Delete an API key",
123
+ description: "Staff only (owner, admin, instructor). 404 when the key is not one of yours in this organization.",
124
+ params: ApiKeyIdParam,
125
+ response: {
126
+ 204: z.void(),
127
+ ...refusals(403, 404)
128
+ }
129
+ },
130
+ handler: async (req, reply) => {
131
+ const scope = await resolveScope(container, req);
132
+ await container.auth.deleteApiKey(req.headers, scope.authOrgId, req.params.id);
133
+ return reply.code(204).send();
134
+ }
135
+ });
136
+ }
137
+ //#endregion
138
+ export { accessRoutes };
139
+
140
+ //# sourceMappingURL=access.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"access.js","names":[],"sources":["../../../src/http/routes/access.ts"],"sourcesContent":["import type { FastifyInstance } from 'fastify';\nimport type { ZodTypeProvider } from 'fastify-type-provider-zod';\nimport { z } from 'zod';\nimport { assertRoleHoldsScopes, scopeOptions } from '@headless-lms/core/organizations';\nimport type { Container } from '../../app/container.js';\nimport {\n ApiKeyIdParam,\n ApiKeyList,\n ClientIdParam,\n ConnectedAppList,\n CreateApiKey,\n CreatedApiKey,\n refusals,\n ScopeOptionList,\n} from '../schemas/index.js';\nimport { resolveScope } from '../scope.js';\n\nexport async function accessRoutes(app: FastifyInstance, container: Container): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n const tags = ['Access'];\n\n r.route({\n method: 'GET',\n url: '/api/access/scopes',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listAccessScopes',\n tags,\n summary: 'Every access scope and whether your role can grant it',\n description:\n 'Staff only (owner, admin, instructor). Each scope an app connection or API key can carry, marked with whether your role holds it. Only allowed scopes can be granted.',\n response: { 200: ScopeOptionList, ...refusals(403) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return scopeOptions(scope.role);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/access/apps',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listConnectedApps',\n tags,\n summary: 'Apps connected to this organization as you',\n description:\n 'Staff only (owner, admin, instructor). The apps you connected to this organization, each with the scopes you granted and when.',\n response: { 200: ConnectedAppList, ...refusals(403) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return container.auth.listConnectedApps(req.headers, scope.authOrgId);\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/access/apps/:clientId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'revokeConnectedApp',\n tags,\n summary: 'Disconnect an app',\n description:\n 'Staff only (owner, admin, instructor). Revokes your consent and refresh tokens for the app in this organization; its next call is refused.',\n params: ClientIdParam,\n response: { 204: z.void(), ...refusals(403) },\n },\n handler: async (req, reply) => {\n const scope = await resolveScope(container, req);\n await container.auth.revokeConnectedApp(req.headers, scope.authOrgId, req.params.clientId);\n return reply.code(204).send();\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/access/api-keys',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listApiKeys',\n tags,\n summary: 'Your API keys for this organization',\n description:\n 'Staff only (owner, admin, instructor). Your keys for this organization with their scopes. The key itself is never returned again after it is created.',\n response: { 200: ApiKeyList, ...refusals(403) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return container.auth.listApiKeys(req.headers, scope.authOrgId);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/access/api-keys',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'createApiKey',\n tags,\n summary: 'Create an API key',\n description:\n 'Staff only (owner, admin, instructor). The key acts as you in this organization with the scopes given. 403 when your role cannot hold one of them. The key is returned once.',\n body: CreateApiKey,\n response: { 201: CreatedApiKey, ...refusals(400, 403) },\n },\n handler: async (req, reply) => {\n const scope = await resolveScope(container, req);\n assertRoleHoldsScopes(scope.role, req.body.scopes);\n const created = await container.auth.createApiKey({\n authUserId: req.authUser.id,\n orgId: scope.authOrgId,\n name: req.body.name,\n scopes: req.body.scopes,\n });\n return reply.code(201).send(created);\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/access/api-keys/:id',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'deleteApiKey',\n tags,\n summary: 'Delete an API key',\n description:\n 'Staff only (owner, admin, instructor). 404 when the key is not one of yours in this organization.',\n params: ApiKeyIdParam,\n response: { 204: z.void(), ...refusals(403, 404) },\n },\n handler: async (req, reply) => {\n const scope = await resolveScope(container, req);\n await container.auth.deleteApiKey(req.headers, scope.authOrgId, req.params.id);\n return reply.code(204).send();\n },\n });\n}\n"],"mappings":"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"}
@@ -1,11 +1,12 @@
1
+ import { refusals } from "../schemas/shared.js";
1
2
  import { resolveScope } from "../scope.js";
2
- import { ActivityPathParam, CourseIdPathParam, CreateModule, ModuleList, ModulePathParam, ReorderInput, SaveActivity, UpdateModule } from "../schemas/activities.js";
3
+ import { ActivityAssetList, ActivityList, ActivityPathParam, CourseIdPathParam, CreateModule, ModuleList, ModulePathParam, ReorderInput, SaveActivity, UpdateModule } from "../schemas/activities.js";
3
4
  import "../schemas/index.js";
4
5
  //#region src/http/routes/activities.ts
5
6
  async function activitiesRoutes(app, container) {
6
7
  const r = app.withTypeProvider();
7
8
  const content = container.content;
8
- const tags = ["Courses"];
9
+ const tags = ["Content"];
9
10
  r.route({
10
11
  method: "GET",
11
12
  url: "/api/courses/:courseId/modules",
@@ -13,15 +14,59 @@ async function activitiesRoutes(app, container) {
13
14
  schema: {
14
15
  operationId: "listModules",
15
16
  tags,
16
- summary: "List a course's modules",
17
+ summary: "List a course's modules (staff)",
18
+ description: "Staff only (owner, admin, instructor). Modules in order, each with its activities.",
17
19
  params: CourseIdPathParam,
18
- response: { 200: ModuleList }
20
+ response: {
21
+ 200: ModuleList,
22
+ ...refusals(403)
23
+ }
19
24
  },
20
25
  handler: async (req) => {
21
26
  const scope = await resolveScope(container, req);
22
27
  return content.listCourseModules(scope.orgId, req.params.courseId);
23
28
  }
24
29
  });
30
+ r.route({
31
+ method: "GET",
32
+ url: "/api/courses/:courseId/activities",
33
+ preHandler: app.requireOrgSession,
34
+ schema: {
35
+ operationId: "listActivities",
36
+ tags,
37
+ summary: "List a course's activities (staff)",
38
+ description: "Staff only (owner, admin, instructor). Every activity, published or not.",
39
+ params: CourseIdPathParam,
40
+ response: {
41
+ 200: ActivityList,
42
+ ...refusals(403)
43
+ }
44
+ },
45
+ handler: async (req) => {
46
+ const scope = await resolveScope(container, req);
47
+ return content.listCourseActivityViews(scope.orgId, req.params.courseId);
48
+ }
49
+ });
50
+ r.route({
51
+ method: "GET",
52
+ url: "/api/courses/:courseId/activity-assets",
53
+ preHandler: app.requireOrgSession,
54
+ schema: {
55
+ operationId: "listActivityAssets",
56
+ tags,
57
+ summary: "List the library files each activity of a course uses (staff)",
58
+ description: "Staff only (owner, admin, instructor). One row per activity and file, as recorded when the activity was saved.",
59
+ params: CourseIdPathParam,
60
+ response: {
61
+ 200: ActivityAssetList,
62
+ ...refusals(403)
63
+ }
64
+ },
65
+ handler: async (req) => {
66
+ const scope = await resolveScope(container, req);
67
+ return content.listCourseActivityAssets(scope.orgId, req.params.courseId);
68
+ }
69
+ });
25
70
  r.route({
26
71
  method: "POST",
27
72
  url: "/api/courses/:courseId/modules",
@@ -29,10 +74,14 @@ async function activitiesRoutes(app, container) {
29
74
  schema: {
30
75
  operationId: "createModule",
31
76
  tags,
32
- summary: "Add a module",
77
+ summary: "Add a module (staff)",
78
+ description: "Staff only (owner, admin, instructor). Added last. Returns the course's modules.",
33
79
  params: CourseIdPathParam,
34
80
  body: CreateModule,
35
- response: { 200: ModuleList }
81
+ response: {
82
+ 200: ModuleList,
83
+ ...refusals(403)
84
+ }
36
85
  },
37
86
  handler: async (req) => {
38
87
  const scope = await resolveScope(container, req);
@@ -46,10 +95,14 @@ async function activitiesRoutes(app, container) {
46
95
  schema: {
47
96
  operationId: "reorderModules",
48
97
  tags,
49
- summary: "Reorder modules",
98
+ summary: "Reorder a course's modules (staff)",
99
+ description: "Staff only (owner, admin, instructor). Ids that are not modules of the course are ignored. Returns the course's modules.",
50
100
  params: CourseIdPathParam,
51
101
  body: ReorderInput,
52
- response: { 200: ModuleList }
102
+ response: {
103
+ 200: ModuleList,
104
+ ...refusals(403)
105
+ }
53
106
  },
54
107
  handler: async (req) => {
55
108
  const scope = await resolveScope(container, req);
@@ -63,10 +116,14 @@ async function activitiesRoutes(app, container) {
63
116
  schema: {
64
117
  operationId: "updateModule",
65
118
  tags,
66
- summary: "Rename a module",
119
+ summary: "Rename a module (staff)",
120
+ description: "Staff only (owner, admin, instructor). Returns the course's modules.",
67
121
  params: ModulePathParam,
68
122
  body: UpdateModule,
69
- response: { 200: ModuleList }
123
+ response: {
124
+ 200: ModuleList,
125
+ ...refusals(403, 404)
126
+ }
70
127
  },
71
128
  handler: async (req) => {
72
129
  const scope = await resolveScope(container, req);
@@ -80,9 +137,13 @@ async function activitiesRoutes(app, container) {
80
137
  schema: {
81
138
  operationId: "deleteModule",
82
139
  tags,
83
- summary: "Delete a module",
140
+ summary: "Delete a module (staff)",
141
+ description: "Staff only (owner, admin, instructor). Returns the course's modules.",
84
142
  params: ModulePathParam,
85
- response: { 200: ModuleList }
143
+ response: {
144
+ 200: ModuleList,
145
+ ...refusals(403, 404)
146
+ }
86
147
  },
87
148
  handler: async (req) => {
88
149
  const scope = await resolveScope(container, req);
@@ -96,10 +157,14 @@ async function activitiesRoutes(app, container) {
96
157
  schema: {
97
158
  operationId: "reorderActivities",
98
159
  tags,
99
- summary: "Reorder activities in a module",
160
+ summary: "Reorder the activities in a module (staff)",
161
+ description: "Staff only (owner, admin, instructor). Send every activity id of the module once (409 otherwise). Returns the course's modules.",
100
162
  params: ModulePathParam,
101
163
  body: ReorderInput,
102
- response: { 200: ModuleList }
164
+ response: {
165
+ 200: ModuleList,
166
+ ...refusals(403, 404, 409)
167
+ }
103
168
  },
104
169
  handler: async (req) => {
105
170
  const scope = await resolveScope(container, req);
@@ -113,10 +178,14 @@ async function activitiesRoutes(app, container) {
113
178
  schema: {
114
179
  operationId: "createActivity",
115
180
  tags,
116
- summary: "Add an activity",
181
+ summary: "Add an activity (staff)",
182
+ description: "Staff only (owner, admin, instructor). Records the library files the activity uses; each must be a ready library file (409 otherwise). Returns the course's modules.",
117
183
  params: ModulePathParam,
118
184
  body: SaveActivity,
119
- response: { 200: ModuleList }
185
+ response: {
186
+ 200: ModuleList,
187
+ ...refusals(403, 404, 409)
188
+ }
120
189
  },
121
190
  handler: async (req) => {
122
191
  const scope = await resolveScope(container, req);
@@ -130,10 +199,14 @@ async function activitiesRoutes(app, container) {
130
199
  schema: {
131
200
  operationId: "updateActivity",
132
201
  tags,
133
- summary: "Update an activity",
202
+ summary: "Update an activity (staff)",
203
+ description: "Staff only (owner, admin, instructor). Replaces its content and records the library files it uses; each must be a ready library file (409 otherwise). Returns the course's modules.",
134
204
  params: ActivityPathParam,
135
205
  body: SaveActivity,
136
- response: { 200: ModuleList }
206
+ response: {
207
+ 200: ModuleList,
208
+ ...refusals(403, 404, 409)
209
+ }
137
210
  },
138
211
  handler: async (req) => {
139
212
  const scope = await resolveScope(container, req);
@@ -147,9 +220,13 @@ async function activitiesRoutes(app, container) {
147
220
  schema: {
148
221
  operationId: "deleteActivity",
149
222
  tags,
150
- summary: "Delete an activity",
223
+ summary: "Delete an activity (staff)",
224
+ description: "Staff only (owner, admin, instructor). Returns the course's modules.",
151
225
  params: ActivityPathParam,
152
- response: { 200: ModuleList }
226
+ response: {
227
+ 200: ModuleList,
228
+ ...refusals(403, 404)
229
+ }
153
230
  },
154
231
  handler: async (req) => {
155
232
  const scope = await resolveScope(container, req);
@@ -1 +1 @@
1
- {"version":3,"file":"activities.js","names":[],"sources":["../../../src/http/routes/activities.ts"],"sourcesContent":["// HTTP routes for course modules + activities. Write operations return the\n// course's full, reordered module list (how the editor re-renders). Nested under\n// courses. All require a session with an active organization; handlers resolve\n// the session's active org to the domain org id and scope every call by it.\nimport type { FastifyInstance } from 'fastify';\nimport type { ZodTypeProvider } from 'fastify-type-provider-zod';\nimport {\n ActivityPathParam,\n CourseIdPathParam,\n CreateModule,\n ModuleList,\n ModulePathParam,\n ReorderInput,\n SaveActivity,\n UpdateModule,\n} from '../schemas/index.js';\nimport type { Container } from '../../app/container.js';\nimport { resolveScope } from '../scope.js';\n\nexport async function activitiesRoutes(app: FastifyInstance, container: Container): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n // Modules/activities are part of the content aggregate; grouped under the Courses tag.\n const content = container.content;\n const tags = ['Courses'];\n\n r.route({\n method: 'GET',\n url: '/api/courses/:courseId/modules',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listModules',\n tags,\n summary: \"List a course's modules\",\n params: CourseIdPathParam,\n response: { 200: ModuleList },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.listCourseModules(scope.orgId, req.params.courseId);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/courses/:courseId/modules',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'createModule',\n tags,\n summary: 'Add a module',\n params: CourseIdPathParam,\n body: CreateModule,\n response: { 200: ModuleList },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.createModule(scope.orgId, req.params.courseId, req.body.title);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/courses/:courseId/modules/reorder',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'reorderModules',\n tags,\n summary: 'Reorder modules',\n params: CourseIdPathParam,\n body: ReorderInput,\n response: { 200: ModuleList },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.reorderModules(scope.orgId, req.params.courseId, req.body.orderedIds);\n },\n });\n\n r.route({\n method: 'PATCH',\n url: '/api/courses/:courseId/modules/:moduleId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'updateModule',\n tags,\n summary: 'Rename a module',\n params: ModulePathParam,\n body: UpdateModule,\n response: { 200: ModuleList },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.updateModule(\n scope.orgId,\n req.params.courseId,\n req.params.moduleId,\n req.body.title,\n );\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/courses/:courseId/modules/:moduleId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'deleteModule',\n tags,\n summary: 'Delete a module',\n params: ModulePathParam,\n response: { 200: ModuleList },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.deleteModule(scope.orgId, req.params.courseId, req.params.moduleId);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/courses/:courseId/modules/:moduleId/activities/reorder',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'reorderActivities',\n tags,\n summary: 'Reorder activities in a module',\n params: ModulePathParam,\n body: ReorderInput,\n response: { 200: ModuleList },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.reorderActivities(\n scope.orgId,\n req.params.courseId,\n req.params.moduleId,\n req.body.orderedIds,\n );\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/courses/:courseId/modules/:moduleId/activities',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'createActivity',\n tags,\n summary: 'Add an activity',\n params: ModulePathParam,\n body: SaveActivity,\n response: { 200: ModuleList },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.saveActivity(scope.orgId, req.params.courseId, req.params.moduleId, req.body);\n },\n });\n\n r.route({\n method: 'PATCH',\n url: '/api/courses/:courseId/modules/:moduleId/activities/:activityId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'updateActivity',\n tags,\n summary: 'Update an activity',\n params: ActivityPathParam,\n body: SaveActivity,\n response: { 200: ModuleList },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.saveActivity(\n scope.orgId,\n req.params.courseId,\n req.params.moduleId,\n req.body,\n req.params.activityId,\n );\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/courses/:courseId/modules/:moduleId/activities/:activityId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'deleteActivity',\n tags,\n summary: 'Delete an activity',\n params: ActivityPathParam,\n response: { 200: ModuleList },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.deleteActivity(\n scope.orgId,\n req.params.courseId,\n req.params.moduleId,\n req.params.activityId,\n );\n },\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"activities.js","names":[],"sources":["../../../src/http/routes/activities.ts"],"sourcesContent":["// HTTP routes for course modules + activities. Write operations return the\n// course's full, reordered module list (how the editor re-renders). Nested under\n// courses. All require a session with an active organization; handlers resolve\n// the session's active org to the domain org id and scope every call by it.\nimport type { FastifyInstance } from 'fastify';\nimport type { ZodTypeProvider } from 'fastify-type-provider-zod';\nimport {\n ActivityAssetList,\n ActivityList,\n ActivityPathParam,\n CourseIdPathParam,\n CreateModule,\n ModuleList,\n ModulePathParam,\n ReorderInput,\n SaveActivity,\n UpdateModule,\n refusals,\n} from '../schemas/index.js';\nimport type { Container } from '../../app/container.js';\nimport { resolveScope } from '../scope.js';\n\nexport async function activitiesRoutes(app: FastifyInstance, container: Container): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n // Modules/activities are part of the content aggregate; grouped under the Courses tag.\n const content = container.content;\n const tags = ['Content'];\n\n r.route({\n method: 'GET',\n url: '/api/courses/:courseId/modules',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listModules',\n tags,\n summary: \"List a course's modules (staff)\",\n description:\n 'Staff only (owner, admin, instructor). Modules in order, each with its activities.',\n params: CourseIdPathParam,\n response: { 200: ModuleList, ...refusals(403) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.listCourseModules(scope.orgId, req.params.courseId);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/courses/:courseId/activities',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listActivities',\n tags,\n summary: \"List a course's activities (staff)\",\n description: 'Staff only (owner, admin, instructor). Every activity, published or not.',\n params: CourseIdPathParam,\n response: { 200: ActivityList, ...refusals(403) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.listCourseActivityViews(scope.orgId, req.params.courseId);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/courses/:courseId/activity-assets',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listActivityAssets',\n tags,\n summary: 'List the library files each activity of a course uses (staff)',\n description:\n 'Staff only (owner, admin, instructor). One row per activity and file, as recorded when the activity was saved.',\n params: CourseIdPathParam,\n response: { 200: ActivityAssetList, ...refusals(403) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.listCourseActivityAssets(scope.orgId, req.params.courseId);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/courses/:courseId/modules',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'createModule',\n tags,\n summary: 'Add a module (staff)',\n description:\n \"Staff only (owner, admin, instructor). Added last. Returns the course's modules.\",\n params: CourseIdPathParam,\n body: CreateModule,\n response: { 200: ModuleList, ...refusals(403) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.createModule(scope.orgId, req.params.courseId, req.body.title);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/courses/:courseId/modules/reorder',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'reorderModules',\n tags,\n summary: \"Reorder a course's modules (staff)\",\n description:\n \"Staff only (owner, admin, instructor). Ids that are not modules of the course are ignored. Returns the course's modules.\",\n params: CourseIdPathParam,\n body: ReorderInput,\n response: { 200: ModuleList, ...refusals(403) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.reorderModules(scope.orgId, req.params.courseId, req.body.orderedIds);\n },\n });\n\n r.route({\n method: 'PATCH',\n url: '/api/courses/:courseId/modules/:moduleId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'updateModule',\n tags,\n summary: 'Rename a module (staff)',\n description: \"Staff only (owner, admin, instructor). Returns the course's modules.\",\n params: ModulePathParam,\n body: UpdateModule,\n response: { 200: ModuleList, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.updateModule(\n scope.orgId,\n req.params.courseId,\n req.params.moduleId,\n req.body.title,\n );\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/courses/:courseId/modules/:moduleId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'deleteModule',\n tags,\n summary: 'Delete a module (staff)',\n description: \"Staff only (owner, admin, instructor). Returns the course's modules.\",\n params: ModulePathParam,\n response: { 200: ModuleList, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.deleteModule(scope.orgId, req.params.courseId, req.params.moduleId);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/courses/:courseId/modules/:moduleId/activities/reorder',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'reorderActivities',\n tags,\n summary: 'Reorder the activities in a module (staff)',\n description:\n \"Staff only (owner, admin, instructor). Send every activity id of the module once (409 otherwise). Returns the course's modules.\",\n params: ModulePathParam,\n body: ReorderInput,\n response: { 200: ModuleList, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.reorderActivities(\n scope.orgId,\n req.params.courseId,\n req.params.moduleId,\n req.body.orderedIds,\n );\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/courses/:courseId/modules/:moduleId/activities',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'createActivity',\n tags,\n summary: 'Add an activity (staff)',\n description:\n \"Staff only (owner, admin, instructor). Records the library files the activity uses; each must be a ready library file (409 otherwise). Returns the course's modules.\",\n params: ModulePathParam,\n body: SaveActivity,\n response: { 200: ModuleList, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.saveActivity(scope.orgId, req.params.courseId, req.params.moduleId, req.body);\n },\n });\n\n r.route({\n method: 'PATCH',\n url: '/api/courses/:courseId/modules/:moduleId/activities/:activityId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'updateActivity',\n tags,\n summary: 'Update an activity (staff)',\n description:\n \"Staff only (owner, admin, instructor). Replaces its content and records the library files it uses; each must be a ready library file (409 otherwise). Returns the course's modules.\",\n params: ActivityPathParam,\n body: SaveActivity,\n response: { 200: ModuleList, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.saveActivity(\n scope.orgId,\n req.params.courseId,\n req.params.moduleId,\n req.body,\n req.params.activityId,\n );\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/courses/:courseId/modules/:moduleId/activities/:activityId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'deleteActivity',\n tags,\n summary: 'Delete an activity (staff)',\n description: \"Staff only (owner, admin, instructor). Returns the course's modules.\",\n params: ActivityPathParam,\n response: { 200: ModuleList, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.deleteActivity(\n scope.orgId,\n req.params.courseId,\n req.params.moduleId,\n req.params.activityId,\n );\n },\n });\n}\n"],"mappings":"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"}