@headless-lms/server 0.2.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 (339) hide show
  1. package/dist/_virtual/_rolldown/runtime.js +5 -0
  2. package/dist/app/community-mail.d.ts +26 -0
  3. package/dist/app/community-mail.js +96 -0
  4. package/dist/app/community-mail.js.map +1 -0
  5. package/dist/app/container.d.ts +18 -14
  6. package/dist/app/container.js +136 -43
  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/http/config.d.ts +2 -1
  12. package/dist/http/errors.d.ts +14 -0
  13. package/dist/http/errors.js +147 -0
  14. package/dist/http/errors.js.map +1 -0
  15. package/dist/http/mcp/catalogue.d.ts +37 -0
  16. package/dist/http/mcp/catalogue.js +69 -0
  17. package/dist/http/mcp/catalogue.js.map +1 -0
  18. package/dist/http/mcp/challenge.d.ts +7 -0
  19. package/dist/http/mcp/challenge.js +35 -0
  20. package/dist/http/mcp/challenge.js.map +1 -0
  21. package/dist/http/mcp/input-schema.d.ts +7 -0
  22. package/dist/http/mcp/input-schema.js +28 -0
  23. package/dist/http/mcp/input-schema.js.map +1 -0
  24. package/dist/http/mcp/route.d.ts +8 -0
  25. package/dist/http/mcp/route.js +75 -0
  26. package/dist/http/mcp/route.js.map +1 -0
  27. package/dist/http/mcp/server.d.ts +8 -0
  28. package/dist/http/mcp/server.js +31 -0
  29. package/dist/http/mcp/server.js.map +1 -0
  30. package/dist/http/mcp/test-support.d.ts +55 -0
  31. package/dist/http/mcp/test-support.js +151 -0
  32. package/dist/http/mcp/test-support.js.map +1 -0
  33. package/dist/http/mcp/tool.d.ts +48 -0
  34. package/dist/http/mcp/tool.js +58 -0
  35. package/dist/http/mcp/tool.js.map +1 -0
  36. package/dist/http/mcp/tools/automations.d.ts +6 -0
  37. package/dist/http/mcp/tools/automations.js +120 -0
  38. package/dist/http/mcp/tools/automations.js.map +1 -0
  39. package/dist/http/mcp/tools/catalog.d.ts +6 -0
  40. package/dist/http/mcp/tools/catalog.js +200 -0
  41. package/dist/http/mcp/tools/catalog.js.map +1 -0
  42. package/dist/http/mcp/tools/comments.d.ts +6 -0
  43. package/dist/http/mcp/tools/comments.js +70 -0
  44. package/dist/http/mcp/tools/comments.js.map +1 -0
  45. package/dist/http/mcp/tools/communities.d.ts +6 -0
  46. package/dist/http/mcp/tools/communities.js +175 -0
  47. package/dist/http/mcp/tools/communities.js.map +1 -0
  48. package/dist/http/mcp/tools/community-members.d.ts +6 -0
  49. package/dist/http/mcp/tools/community-members.js +89 -0
  50. package/dist/http/mcp/tools/community-members.js.map +1 -0
  51. package/dist/http/mcp/tools/community-moderation.d.ts +6 -0
  52. package/dist/http/mcp/tools/community-moderation.js +144 -0
  53. package/dist/http/mcp/tools/community-moderation.js.map +1 -0
  54. package/dist/http/mcp/tools/courses.d.ts +6 -0
  55. package/dist/http/mcp/tools/courses.js +121 -0
  56. package/dist/http/mcp/tools/courses.js.map +1 -0
  57. package/dist/http/mcp/tools/index.d.ts +6 -0
  58. package/dist/http/mcp/tools/index.js +23 -0
  59. package/dist/http/mcp/tools/index.js.map +1 -0
  60. package/dist/http/mcp/tools/people.d.ts +6 -0
  61. package/dist/http/mcp/tools/people.js +145 -0
  62. package/dist/http/mcp/tools/people.js.map +1 -0
  63. package/dist/http/plugins/auth.js +13 -5
  64. package/dist/http/plugins/auth.js.map +1 -1
  65. package/dist/http/plugins/error-handler.d.ts +2 -1
  66. package/dist/http/plugins/error-handler.js +49 -65
  67. package/dist/http/plugins/error-handler.js.map +1 -1
  68. package/dist/http/plugins/openapi.js +49 -2
  69. package/dist/http/plugins/openapi.js.map +1 -1
  70. package/dist/http/routes/access.d.ts +7 -0
  71. package/dist/http/routes/access.js +140 -0
  72. package/dist/http/routes/access.js.map +1 -0
  73. package/dist/http/routes/activities.js +68 -23
  74. package/dist/http/routes/activities.js.map +1 -1
  75. package/dist/http/routes/assets.js +60 -55
  76. package/dist/http/routes/assets.js.map +1 -1
  77. package/dist/http/routes/automations.js +44 -24
  78. package/dist/http/routes/automations.js.map +1 -1
  79. package/dist/http/routes/bundles.js +29 -18
  80. package/dist/http/routes/bundles.js.map +1 -1
  81. package/dist/http/routes/communities.d.ts +7 -0
  82. package/dist/http/routes/communities.js +462 -0
  83. package/dist/http/routes/communities.js.map +1 -0
  84. package/dist/http/routes/community-moderation.d.ts +7 -0
  85. package/dist/http/routes/community-moderation.js +425 -0
  86. package/dist/http/routes/community-moderation.js.map +1 -0
  87. package/dist/http/routes/course-analytics.js +9 -7
  88. package/dist/http/routes/course-analytics.js.map +1 -1
  89. package/dist/http/routes/courses.js +30 -15
  90. package/dist/http/routes/courses.js.map +1 -1
  91. package/dist/http/routes/dashboard.js +15 -6
  92. package/dist/http/routes/dashboard.js.map +1 -1
  93. package/dist/http/routes/discussion.js +34 -26
  94. package/dist/http/routes/discussion.js.map +1 -1
  95. package/dist/http/routes/downloads.js +48 -30
  96. package/dist/http/routes/downloads.js.map +1 -1
  97. package/dist/http/routes/entitlements.js +16 -7
  98. package/dist/http/routes/entitlements.js.map +1 -1
  99. package/dist/http/routes/integrations.js +38 -41
  100. package/dist/http/routes/integrations.js.map +1 -1
  101. package/dist/http/routes/member/actor.d.ts +14 -0
  102. package/dist/http/routes/member/actor.js +11 -0
  103. package/dist/http/routes/member/actor.js.map +1 -0
  104. package/dist/http/routes/member/assets.d.ts +7 -0
  105. package/dist/http/routes/member/assets.js +79 -0
  106. package/dist/http/routes/member/assets.js.map +1 -0
  107. package/dist/http/routes/member/chats.d.ts +7 -0
  108. package/dist/http/routes/member/chats.js +456 -0
  109. package/dist/http/routes/member/chats.js.map +1 -0
  110. package/dist/http/routes/member/comments.d.ts +7 -0
  111. package/dist/http/routes/member/comments.js +187 -0
  112. package/dist/http/routes/member/comments.js.map +1 -0
  113. package/dist/http/routes/member/community.d.ts +7 -0
  114. package/dist/http/routes/member/community.js +288 -0
  115. package/dist/http/routes/member/community.js.map +1 -0
  116. package/dist/http/routes/member/courses.d.ts +7 -0
  117. package/dist/http/routes/member/courses.js +101 -0
  118. package/dist/http/routes/member/courses.js.map +1 -0
  119. package/dist/http/routes/member/downloads.d.ts +7 -0
  120. package/dist/http/routes/member/downloads.js +56 -0
  121. package/dist/http/routes/member/downloads.js.map +1 -0
  122. package/dist/http/routes/member/files.d.ts +7 -0
  123. package/dist/http/routes/member/files.js +184 -0
  124. package/dist/http/routes/member/files.js.map +1 -0
  125. package/dist/http/routes/member/index.d.ts +7 -0
  126. package/dist/http/routes/member/index.js +31 -0
  127. package/dist/http/routes/member/index.js.map +1 -0
  128. package/dist/http/routes/member/notifications.d.ts +7 -0
  129. package/dist/http/routes/member/notifications.js +101 -0
  130. package/dist/http/routes/member/notifications.js.map +1 -0
  131. package/dist/http/routes/member/org.d.ts +7 -0
  132. package/dist/http/routes/member/org.js +56 -0
  133. package/dist/http/routes/member/org.js.map +1 -0
  134. package/dist/http/routes/member/posts.d.ts +7 -0
  135. package/dist/http/routes/member/posts.js +612 -0
  136. package/dist/http/routes/member/posts.js.map +1 -0
  137. package/dist/http/routes/member/profile.d.ts +7 -0
  138. package/dist/http/routes/member/profile.js +48 -0
  139. package/dist/http/routes/member/profile.js.map +1 -0
  140. package/dist/http/routes/member/progress.d.ts +7 -0
  141. package/dist/http/routes/member/progress.js +60 -0
  142. package/dist/http/routes/member/progress.js.map +1 -0
  143. package/dist/http/routes/organizations.js +49 -24
  144. package/dist/http/routes/organizations.js.map +1 -1
  145. package/dist/http/routes/students.js +32 -47
  146. package/dist/http/routes/students.js.map +1 -1
  147. package/dist/http/routes/webhooks.js +26 -18
  148. package/dist/http/routes/webhooks.js.map +1 -1
  149. package/dist/http/routes.js +8 -2
  150. package/dist/http/routes.js.map +1 -1
  151. package/dist/http/schemas/access.d.ts +146 -0
  152. package/dist/http/schemas/access.js +36 -0
  153. package/dist/http/schemas/access.js.map +1 -0
  154. package/dist/http/schemas/activities.d.ts +3 -1
  155. package/dist/http/schemas/activities.js +4 -4
  156. package/dist/http/schemas/activities.js.map +1 -1
  157. package/dist/http/schemas/assets.d.ts +73 -74
  158. package/dist/http/schemas/assets.js +23 -13
  159. package/dist/http/schemas/assets.js.map +1 -1
  160. package/dist/http/schemas/automations.d.ts +10 -0
  161. package/dist/http/schemas/bundles.js +6 -6
  162. package/dist/http/schemas/bundles.js.map +1 -1
  163. package/dist/http/schemas/chats.d.ts +386 -0
  164. package/dist/http/schemas/chats.js +58 -0
  165. package/dist/http/schemas/chats.js.map +1 -0
  166. package/dist/http/schemas/community.d.ts +537 -0
  167. package/dist/http/schemas/community.js +129 -0
  168. package/dist/http/schemas/community.js.map +1 -0
  169. package/dist/http/schemas/content.d.ts +4 -2
  170. package/dist/http/schemas/content.js +11 -8
  171. package/dist/http/schemas/content.js.map +1 -1
  172. package/dist/http/schemas/discussion.d.ts +76 -14
  173. package/dist/http/schemas/discussion.js +12 -11
  174. package/dist/http/schemas/discussion.js.map +1 -1
  175. package/dist/http/schemas/downloads.d.ts +11 -5
  176. package/dist/http/schemas/downloads.js +14 -11
  177. package/dist/http/schemas/downloads.js.map +1 -1
  178. package/dist/http/schemas/entitlements.d.ts +6 -6
  179. package/dist/http/schemas/entitlements.js +6 -6
  180. package/dist/http/schemas/entitlements.js.map +1 -1
  181. package/dist/http/schemas/files.d.ts +45 -0
  182. package/dist/http/schemas/files.js +25 -0
  183. package/dist/http/schemas/files.js.map +1 -0
  184. package/dist/http/schemas/index.d.ts +15 -8
  185. package/dist/http/schemas/index.js +15 -8
  186. package/dist/http/schemas/integrations.d.ts +2 -2
  187. package/dist/http/schemas/invites.d.ts +4 -0
  188. package/dist/http/schemas/invites.js +1 -0
  189. package/dist/http/schemas/invites.js.map +1 -1
  190. package/dist/http/schemas/{learn.d.ts → member.d.ts} +31 -18
  191. package/dist/http/schemas/{learn.js → member.js} +17 -13
  192. package/dist/http/schemas/member.js.map +1 -0
  193. package/dist/http/schemas/members.d.ts +39 -6
  194. package/dist/http/schemas/members.js +17 -3
  195. package/dist/http/schemas/members.js.map +1 -1
  196. package/dist/http/schemas/notifications.d.ts +227 -0
  197. package/dist/http/schemas/notifications.js +44 -0
  198. package/dist/http/schemas/notifications.js.map +1 -0
  199. package/dist/http/schemas/posts.d.ts +316 -0
  200. package/dist/http/schemas/posts.js +47 -0
  201. package/dist/http/schemas/posts.js.map +1 -0
  202. package/dist/http/schemas/profile.d.ts +23 -0
  203. package/dist/http/schemas/profile.js +15 -0
  204. package/dist/http/schemas/profile.js.map +1 -0
  205. package/dist/http/schemas/shared.d.ts +25 -1
  206. package/dist/http/schemas/shared.js +19 -2
  207. package/dist/http/schemas/shared.js.map +1 -1
  208. package/dist/http/schemas/students.d.ts +4 -10
  209. package/dist/http/schemas/students.js +1 -7
  210. package/dist/http/schemas/students.js.map +1 -1
  211. package/dist/http/scope.d.ts +2 -1
  212. package/dist/http/scope.js +20 -4
  213. package/dist/http/scope.js.map +1 -1
  214. package/dist/http/server.js +6 -1
  215. package/dist/http/server.js.map +1 -1
  216. package/dist/http/web-bridge.d.ts +1 -1
  217. package/dist/http/web-bridge.js +2 -2
  218. package/dist/http/web-bridge.js.map +1 -1
  219. package/dist/http/well-known.d.ts +7 -0
  220. package/dist/http/well-known.js +22 -0
  221. package/dist/http/well-known.js.map +1 -0
  222. package/dist/index.d.ts +2 -1
  223. package/dist/index.js +4 -2
  224. package/dist/index.js.map +1 -1
  225. package/dist/node_modules/.pnpm/@jridgewell_sourcemap-codec@1.5.5/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.js +78 -0
  226. package/dist/node_modules/.pnpm/@jridgewell_sourcemap-codec@1.5.5/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.js.map +1 -0
  227. package/dist/node_modules/.pnpm/@vitest_expect@2.1.9/node_modules/@vitest/expect/dist/index.js +1267 -0
  228. package/dist/node_modules/.pnpm/@vitest_expect@2.1.9/node_modules/@vitest/expect/dist/index.js.map +1 -0
  229. package/dist/node_modules/.pnpm/@vitest_pretty-format@2.1.9/node_modules/@vitest/pretty-format/dist/index.js +814 -0
  230. package/dist/node_modules/.pnpm/@vitest_pretty-format@2.1.9/node_modules/@vitest/pretty-format/dist/index.js.map +1 -0
  231. package/dist/node_modules/.pnpm/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/chunk-tasks.js +43 -0
  232. package/dist/node_modules/.pnpm/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/chunk-tasks.js.map +1 -0
  233. package/dist/node_modules/.pnpm/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/index.js +527 -0
  234. package/dist/node_modules/.pnpm/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/index.js.map +1 -0
  235. package/dist/node_modules/.pnpm/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/utils.js +2 -0
  236. package/dist/node_modules/.pnpm/@vitest_snapshot@2.1.9/node_modules/@vitest/snapshot/dist/index.js +1737 -0
  237. package/dist/node_modules/.pnpm/@vitest_snapshot@2.1.9/node_modules/@vitest/snapshot/dist/index.js.map +1 -0
  238. package/dist/node_modules/.pnpm/@vitest_spy@2.1.9/node_modules/@vitest/spy/dist/index.js +137 -0
  239. package/dist/node_modules/.pnpm/@vitest_spy@2.1.9/node_modules/@vitest/spy/dist/index.js.map +1 -0
  240. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/chunk-_commonjsHelpers.js +118 -0
  241. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/chunk-_commonjsHelpers.js.map +1 -0
  242. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/diff.js +1222 -0
  243. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/diff.js.map +1 -0
  244. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/error.js +91 -0
  245. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/error.js.map +1 -0
  246. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/helpers.js +113 -0
  247. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/helpers.js.map +1 -0
  248. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/index.js +449 -0
  249. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/index.js.map +1 -0
  250. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/source-map.js +163 -0
  251. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/source-map.js.map +1 -0
  252. package/dist/node_modules/.pnpm/chai@5.3.3/node_modules/chai/index.js +3187 -0
  253. package/dist/node_modules/.pnpm/chai@5.3.3/node_modules/chai/index.js.map +1 -0
  254. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/arguments.js +11 -0
  255. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/arguments.js.map +1 -0
  256. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/array.js +16 -0
  257. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/array.js.map +1 -0
  258. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/bigint.js +11 -0
  259. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/bigint.js.map +1 -0
  260. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/class.js +15 -0
  261. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/class.js.map +1 -0
  262. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/date.js +13 -0
  263. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/date.js.map +1 -0
  264. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/error.js +34 -0
  265. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/error.js.map +1 -0
  266. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/function.js +12 -0
  267. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/function.js.map +1 -0
  268. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/helpers.js +122 -0
  269. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/helpers.js.map +1 -0
  270. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/html.js +39 -0
  271. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/html.js.map +1 -0
  272. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/index.js +99 -0
  273. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/index.js.map +1 -0
  274. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/map.js +25 -0
  275. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/map.js.map +1 -0
  276. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/number.js +14 -0
  277. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/number.js.map +1 -0
  278. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/object.js +21 -0
  279. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/object.js.map +1 -0
  280. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/promise.js +6 -0
  281. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/promise.js.map +1 -0
  282. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/regexp.js +12 -0
  283. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/regexp.js.map +1 -0
  284. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/set.js +18 -0
  285. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/set.js.map +1 -0
  286. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/string.js +24 -0
  287. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/string.js.map +1 -0
  288. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/symbol.js +9 -0
  289. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/symbol.js.map +1 -0
  290. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/typedarray.js +30 -0
  291. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/typedarray.js.map +1 -0
  292. package/dist/node_modules/.pnpm/magic-string@0.30.21/node_modules/magic-string/dist/magic-string.es.js +939 -0
  293. package/dist/node_modules/.pnpm/magic-string@0.30.21/node_modules/magic-string/dist/magic-string.es.js.map +1 -0
  294. package/dist/node_modules/.pnpm/pathe@1.1.2/node_modules/pathe/dist/shared/pathe.ff20891b.js +82 -0
  295. package/dist/node_modules/.pnpm/pathe@1.1.2/node_modules/pathe/dist/shared/pathe.ff20891b.js.map +1 -0
  296. package/dist/node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/chunk-BVHSVHOK.js +82 -0
  297. package/dist/node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/chunk-BVHSVHOK.js.map +1 -0
  298. package/dist/node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/node.js +8 -0
  299. package/dist/node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/node.js.map +1 -0
  300. package/dist/node_modules/.pnpm/tinyspy@3.0.2/node_modules/tinyspy/dist/index.js +83 -0
  301. package/dist/node_modules/.pnpm/tinyspy@3.0.2/node_modules/tinyspy/dist/index.js.map +1 -0
  302. 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
  303. 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
  304. 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
  305. 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
  306. 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
  307. 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
  308. 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
  309. 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
  310. package/dist/packages/server/package.js +6 -0
  311. package/dist/packages/server/package.js.map +1 -0
  312. package/package.json +9 -7
  313. package/dist/app/notifications.d.ts +0 -7
  314. package/dist/app/notifications.js +0 -26
  315. package/dist/app/notifications.js.map +0 -1
  316. package/dist/http/routes/learn/assets.d.ts +0 -8
  317. package/dist/http/routes/learn/assets.js +0 -32
  318. package/dist/http/routes/learn/assets.js.map +0 -1
  319. package/dist/http/routes/learn/comments.d.ts +0 -7
  320. package/dist/http/routes/learn/comments.js +0 -175
  321. package/dist/http/routes/learn/comments.js.map +0 -1
  322. package/dist/http/routes/learn/courses.d.ts +0 -7
  323. package/dist/http/routes/learn/courses.js +0 -98
  324. package/dist/http/routes/learn/courses.js.map +0 -1
  325. package/dist/http/routes/learn/downloads.d.ts +0 -7
  326. package/dist/http/routes/learn/downloads.js +0 -76
  327. package/dist/http/routes/learn/downloads.js.map +0 -1
  328. package/dist/http/routes/learn/index.d.ts +0 -7
  329. package/dist/http/routes/learn/index.js +0 -19
  330. package/dist/http/routes/learn/index.js.map +0 -1
  331. package/dist/http/routes/learn/org.d.ts +0 -7
  332. package/dist/http/routes/learn/org.js +0 -50
  333. package/dist/http/routes/learn/org.js.map +0 -1
  334. package/dist/http/routes/learn/progress.d.ts +0 -7
  335. package/dist/http/routes/learn/progress.js +0 -63
  336. package/dist/http/routes/learn/progress.js.map +0 -1
  337. package/dist/http/schemas/learn.js.map +0 -1
  338. package/dist/package.js +0 -6
  339. package/dist/package.js.map +0 -1
@@ -1,75 +1,59 @@
1
- import { UnauthorizedError } from "./auth.js";
2
- import { NoActiveOrgError } from "../scope.js";
3
- import { OrganizationRuleError } from "@headless-lms/core/organizations";
4
- import { ActionInvocationError, AlreadyConnectedError, InvalidConfigError, UnknownIntegrationError } from "@headless-lms/core/integrations";
5
- import { AutomationKindMismatchError, InvalidTriggerError, ReservedActionError } from "@headless-lms/core/automations";
6
- import { ConflictError, ForbiddenError, NotFoundError } from "@headless-lms/core/shared/errors";
1
+ import { classifyError } from "../errors.js";
2
+ import { ActionInvocationError } from "@headless-lms/core/integrations";
3
+ import { isResponseSerializationError } from "fastify-type-provider-zod";
7
4
  //#region src/http/plugins/error-handler.ts
5
+ function routeOf(request) {
6
+ return `${request.method} ${request.routeOptions.url ?? "unmatched route"}`;
7
+ }
8
8
  function errorHandler(error, request, reply) {
9
- if (error instanceof NotFoundError) return reply.status(404).send({
10
- error: "not_found",
11
- message: error.message
12
- });
13
- if (error instanceof ConflictError) return reply.status(409).send({
14
- error: "conflict",
15
- message: error.message
16
- });
17
- if (error instanceof ForbiddenError) return reply.status(403).send({
18
- error: "forbidden",
19
- message: error.message
20
- });
21
- if (error instanceof OrganizationRuleError) return reply.status(409).send({
22
- error: "conflict",
23
- message: error.message
24
- });
25
- if (error instanceof AlreadyConnectedError) return reply.status(409).send({
26
- error: "already_connected",
27
- message: error.message
28
- });
29
- if (error instanceof UnknownIntegrationError) return reply.status(400).send({
30
- error: "unknown_integration",
31
- message: error.message
32
- });
33
- if (error instanceof InvalidConfigError) return reply.status(400).send({
34
- error: "invalid_config",
35
- message: error.message
36
- });
37
- if (error instanceof ActionInvocationError) return reply.status(502).send({
38
- error: "action_failed",
39
- message: error.message
40
- });
41
- if (error instanceof InvalidTriggerError) return reply.status(400).send({
42
- error: "invalid_trigger",
43
- message: error.message
44
- });
45
- if (error instanceof ReservedActionError) return reply.status(400).send({
46
- error: "reserved_action",
47
- message: error.message
48
- });
49
- if (error instanceof AutomationKindMismatchError) return reply.status(409).send({
50
- error: "kind_mismatch",
51
- message: error.message
52
- });
53
- if (error instanceof NoActiveOrgError) return reply.status(403).send({
54
- error: "forbidden",
55
- message: error.message
56
- });
57
- if (error instanceof UnauthorizedError) return reply.status(401).send({
58
- error: "unauthorized",
59
- message: error.message
60
- });
61
- const err = error;
62
- if (typeof err.statusCode === "number" && err.statusCode >= 400 && err.statusCode < 500) return reply.status(err.statusCode).send({
63
- error: err.code ?? "bad_request",
64
- message: err.message ?? "Bad request"
65
- });
66
- request.log.error({ err: error }, `unhandled error in ${request.method} ${request.url}`);
9
+ if (error instanceof ActionInvocationError) {
10
+ request.log.error({
11
+ err: error,
12
+ integrationId: error.integrationId,
13
+ actionId: error.actionId
14
+ }, `integration action failed in ${routeOf(request)}`);
15
+ return reply.status(502).send({
16
+ error: "action_failed",
17
+ message: "The integration did not respond as expected"
18
+ });
19
+ }
20
+ const refusal = classifyError(error);
21
+ if (refusal) {
22
+ request.log.info({
23
+ status: refusal.status,
24
+ error: refusal.error,
25
+ message: refusal.message,
26
+ ...refusal.issues && { issues: refusal.issues },
27
+ ...refusal.error === "bad_request" && { cause: error.message }
28
+ }, `${routeOf(request)} refused`);
29
+ return reply.status(refusal.status).send({
30
+ error: refusal.error,
31
+ message: refusal.message,
32
+ ...refusal.issues && { issues: refusal.issues }
33
+ });
34
+ }
35
+ if (isResponseSerializationError(error)) request.log.error({
36
+ err: error,
37
+ issues: error.cause.issues
38
+ }, `response of ${routeOf(request)} does not match its schema`);
39
+ else request.log.error({ err: error }, `unhandled error in ${routeOf(request)}`);
67
40
  return reply.status(500).send({
68
41
  error: "internal_error",
42
+ message: "Something went wrong on our side. Quote the request id when reporting it.",
69
43
  requestId: request.id
70
44
  });
71
45
  }
46
+ function notFoundHandler(request, reply) {
47
+ request.log.info({
48
+ status: 404,
49
+ error: "not_found"
50
+ }, `${request.method} unmatched route refused`);
51
+ return reply.status(404).send({
52
+ error: "not_found",
53
+ message: `No route for ${request.method} ${request.url}`
54
+ });
55
+ }
72
56
  //#endregion
73
- export { errorHandler };
57
+ export { errorHandler, notFoundHandler };
74
58
 
75
59
  //# sourceMappingURL=error-handler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"error-handler.js","names":[],"sources":["../../../src/http/plugins/error-handler.ts"],"sourcesContent":["import { ConflictError, ForbiddenError, NotFoundError } from '@headless-lms/core/shared/errors';\nimport { OrganizationRuleError } from '@headless-lms/core/organizations';\nimport {\n ActionInvocationError,\n AlreadyConnectedError,\n InvalidConfigError,\n UnknownIntegrationError,\n} from '@headless-lms/core/integrations';\nimport {\n AutomationKindMismatchError,\n InvalidTriggerError,\n ReservedActionError,\n} from '@headless-lms/core/automations';\nimport { NoActiveOrgError } from '../scope.js';\nimport { UnauthorizedError } from './auth.js';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\n\nexport function errorHandler(error: unknown, request: FastifyRequest, reply: FastifyReply) {\n if (error instanceof NotFoundError) {\n return reply.status(404).send({ error: 'not_found', message: error.message });\n }\n if (error instanceof ConflictError) {\n return reply.status(409).send({ error: 'conflict', message: error.message });\n }\n if (error instanceof ForbiddenError) {\n return reply.status(403).send({ error: 'forbidden', message: error.message });\n }\n if (error instanceof OrganizationRuleError) {\n return reply.status(409).send({ error: 'conflict', message: error.message });\n }\n if (error instanceof AlreadyConnectedError) {\n return reply.status(409).send({ error: 'already_connected', message: error.message });\n }\n if (error instanceof UnknownIntegrationError) {\n return reply.status(400).send({ error: 'unknown_integration', message: error.message });\n }\n if (error instanceof InvalidConfigError) {\n return reply.status(400).send({ error: 'invalid_config', message: error.message });\n }\n if (error instanceof ActionInvocationError) {\n return reply.status(502).send({ error: 'action_failed', message: error.message });\n }\n if (error instanceof InvalidTriggerError) {\n return reply.status(400).send({ error: 'invalid_trigger', message: error.message });\n }\n if (error instanceof ReservedActionError) {\n return reply.status(400).send({ error: 'reserved_action', message: error.message });\n }\n if (error instanceof AutomationKindMismatchError) {\n return reply.status(409).send({ error: 'kind_mismatch', message: error.message });\n }\n if (error instanceof NoActiveOrgError) {\n return reply.status(403).send({ error: 'forbidden', message: error.message });\n }\n // No session at all — 401 so the client bounces to login, not a generic 403.\n if (error instanceof UnauthorizedError) {\n return reply.status(401).send({ error: 'unauthorized', message: error.message });\n }\n const err = error as { statusCode?: number; code?: string; message?: string };\n if (typeof err.statusCode === 'number' && err.statusCode >= 400 && err.statusCode < 500) {\n return reply\n .status(err.statusCode)\n .send({ error: err.code ?? 'bad_request', message: err.message ?? 'Bad request' });\n }\n request.log.error({ err: error }, `unhandled error in ${request.method} ${request.url}`);\n return reply.status(500).send({ error: 'internal_error', requestId: request.id });\n}\n"],"mappings":";;;;;;;AAiBA,SAAgB,aAAa,OAAgB,SAAyB,OAAqB;CACzF,IAAI,iBAAiB,eACnB,OAAO,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK;EAAE,OAAO;EAAa,SAAS,MAAM;CAAQ,CAAC;CAE9E,IAAI,iBAAiB,eACnB,OAAO,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK;EAAE,OAAO;EAAY,SAAS,MAAM;CAAQ,CAAC;CAE7E,IAAI,iBAAiB,gBACnB,OAAO,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK;EAAE,OAAO;EAAa,SAAS,MAAM;CAAQ,CAAC;CAE9E,IAAI,iBAAiB,uBACnB,OAAO,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK;EAAE,OAAO;EAAY,SAAS,MAAM;CAAQ,CAAC;CAE7E,IAAI,iBAAiB,uBACnB,OAAO,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK;EAAE,OAAO;EAAqB,SAAS,MAAM;CAAQ,CAAC;CAEtF,IAAI,iBAAiB,yBACnB,OAAO,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK;EAAE,OAAO;EAAuB,SAAS,MAAM;CAAQ,CAAC;CAExF,IAAI,iBAAiB,oBACnB,OAAO,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK;EAAE,OAAO;EAAkB,SAAS,MAAM;CAAQ,CAAC;CAEnF,IAAI,iBAAiB,uBACnB,OAAO,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK;EAAE,OAAO;EAAiB,SAAS,MAAM;CAAQ,CAAC;CAElF,IAAI,iBAAiB,qBACnB,OAAO,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK;EAAE,OAAO;EAAmB,SAAS,MAAM;CAAQ,CAAC;CAEpF,IAAI,iBAAiB,qBACnB,OAAO,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK;EAAE,OAAO;EAAmB,SAAS,MAAM;CAAQ,CAAC;CAEpF,IAAI,iBAAiB,6BACnB,OAAO,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK;EAAE,OAAO;EAAiB,SAAS,MAAM;CAAQ,CAAC;CAElF,IAAI,iBAAiB,kBACnB,OAAO,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK;EAAE,OAAO;EAAa,SAAS,MAAM;CAAQ,CAAC;CAG9E,IAAI,iBAAiB,mBACnB,OAAO,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK;EAAE,OAAO;EAAgB,SAAS,MAAM;CAAQ,CAAC;CAEjF,MAAM,MAAM;CACZ,IAAI,OAAO,IAAI,eAAe,YAAY,IAAI,cAAc,OAAO,IAAI,aAAa,KAClF,OAAO,MACJ,OAAO,IAAI,UAAU,CAAC,CACtB,KAAK;EAAE,OAAO,IAAI,QAAQ;EAAe,SAAS,IAAI,WAAW;CAAc,CAAC;CAErF,QAAQ,IAAI,MAAM,EAAE,KAAK,MAAM,GAAG,sBAAsB,QAAQ,OAAO,GAAG,QAAQ,KAAK;CACvF,OAAO,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK;EAAE,OAAO;EAAkB,WAAW,QAAQ;CAAG,CAAC;AAClF"}
1
+ {"version":3,"file":"error-handler.js","names":[],"sources":["../../../src/http/plugins/error-handler.ts"],"sourcesContent":["import type { FastifyReply, FastifyRequest } from 'fastify';\nimport { ActionInvocationError } from '@headless-lms/core/integrations';\nimport { isResponseSerializationError } from 'fastify-type-provider-zod';\nimport { classifyError } from '../errors.js';\n\nfunction routeOf(request: FastifyRequest): string {\n return `${request.method} ${request.routeOptions.url ?? 'unmatched route'}`;\n}\n\nexport function errorHandler(error: unknown, request: FastifyRequest, reply: FastifyReply) {\n if (error instanceof ActionInvocationError) {\n request.log.error(\n { err: error, integrationId: error.integrationId, actionId: error.actionId },\n `integration action failed in ${routeOf(request)}`,\n );\n return reply.status(502).send({\n error: 'action_failed',\n message: 'The integration did not respond as expected',\n });\n }\n const refusal = classifyError(error);\n if (refusal) {\n request.log.info(\n {\n status: refusal.status,\n error: refusal.error,\n message: refusal.message,\n ...(refusal.issues && { issues: refusal.issues }),\n ...(refusal.error === 'bad_request' && { cause: (error as Error).message }),\n },\n `${routeOf(request)} refused`,\n );\n return reply.status(refusal.status).send({\n error: refusal.error,\n message: refusal.message,\n ...(refusal.issues && { issues: refusal.issues }),\n });\n }\n if (isResponseSerializationError(error)) {\n request.log.error(\n { err: error, issues: error.cause.issues },\n `response of ${routeOf(request)} does not match its schema`,\n );\n } else {\n request.log.error({ err: error }, `unhandled error in ${routeOf(request)}`);\n }\n return reply.status(500).send({\n error: 'internal_error',\n message: 'Something went wrong on our side. Quote the request id when reporting it.',\n requestId: request.id,\n });\n}\n\nexport function notFoundHandler(request: FastifyRequest, reply: FastifyReply) {\n request.log.info(\n { status: 404, error: 'not_found' },\n `${request.method} unmatched route refused`,\n );\n return reply\n .status(404)\n .send({ error: 'not_found', message: `No route for ${request.method} ${request.url}` });\n}\n"],"mappings":";;;;AAKA,SAAS,QAAQ,SAAiC;CAChD,OAAO,GAAG,QAAQ,OAAO,GAAG,QAAQ,aAAa,OAAO;AAC1D;AAEA,SAAgB,aAAa,OAAgB,SAAyB,OAAqB;CACzF,IAAI,iBAAiB,uBAAuB;EAC1C,QAAQ,IAAI,MACV;GAAE,KAAK;GAAO,eAAe,MAAM;GAAe,UAAU,MAAM;EAAS,GAC3E,gCAAgC,QAAQ,OAAO,GACjD;EACA,OAAO,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK;GAC5B,OAAO;GACP,SAAS;EACX,CAAC;CACH;CACA,MAAM,UAAU,cAAc,KAAK;CACnC,IAAI,SAAS;EACX,QAAQ,IAAI,KACV;GACE,QAAQ,QAAQ;GAChB,OAAO,QAAQ;GACf,SAAS,QAAQ;GACjB,GAAI,QAAQ,UAAU,EAAE,QAAQ,QAAQ,OAAO;GAC/C,GAAI,QAAQ,UAAU,iBAAiB,EAAE,OAAQ,MAAgB,QAAQ;EAC3E,GACA,GAAG,QAAQ,OAAO,EAAE,SACtB;EACA,OAAO,MAAM,OAAO,QAAQ,MAAM,CAAC,CAAC,KAAK;GACvC,OAAO,QAAQ;GACf,SAAS,QAAQ;GACjB,GAAI,QAAQ,UAAU,EAAE,QAAQ,QAAQ,OAAO;EACjD,CAAC;CACH;CACA,IAAI,6BAA6B,KAAK,GACpC,QAAQ,IAAI,MACV;EAAE,KAAK;EAAO,QAAQ,MAAM,MAAM;CAAO,GACzC,eAAe,QAAQ,OAAO,EAAE,2BAClC;MAEA,QAAQ,IAAI,MAAM,EAAE,KAAK,MAAM,GAAG,sBAAsB,QAAQ,OAAO,GAAG;CAE5E,OAAO,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK;EAC5B,OAAO;EACP,SAAS;EACT,WAAW,QAAQ;CACrB,CAAC;AACH;AAEA,SAAgB,gBAAgB,SAAyB,OAAqB;CAC5E,QAAQ,IAAI,KACV;EAAE,QAAQ;EAAK,OAAO;CAAY,GAClC,GAAG,QAAQ,OAAO,yBACpB;CACA,OAAO,MACJ,OAAO,GAAG,CAAC,CACX,KAAK;EAAE,OAAO;EAAa,SAAS,gBAAgB,QAAQ,OAAO,GAAG,QAAQ;CAAM,CAAC;AAC1F"}
@@ -1,8 +1,21 @@
1
- import { version } from "../../package.js";
1
+ import { ErrorBody, ValidationErrorBody } from "../schemas/shared.js";
2
+ import { version } from "../../packages/server/package.js";
2
3
  import { jsonSchemaTransform, jsonSchemaTransformObject } from "fastify-type-provider-zod";
3
4
  import swagger from "@fastify/swagger";
4
5
  import swaggerUi from "@fastify/swagger-ui";
5
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
+ }
6
19
  function registerOpenApi(app, config) {
7
20
  app.register(swagger, {
8
21
  openapi: {
@@ -37,10 +50,34 @@ function registerOpenApi(app, config) {
37
50
  name: "Discussion",
38
51
  description: "Learner comments, replies, reactions and moderation"
39
52
  },
53
+ {
54
+ name: "Communities",
55
+ description: "Communities, their spaces, topics, and direct-message settings"
56
+ },
57
+ {
58
+ name: "Posts",
59
+ description: "Posts in a space, reactions, and the moderation queue"
60
+ },
61
+ {
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"
72
+ },
40
73
  {
41
74
  name: "Assets",
42
75
  description: "The org media library: uploads, serving and lifecycle"
43
76
  },
77
+ {
78
+ name: "Files",
79
+ description: "Stable file URLs that check access and redirect to a signed storage URL"
80
+ },
44
81
  {
45
82
  name: "Automations",
46
83
  description: "Workflows and webhooks that react to system events"
@@ -52,10 +89,20 @@ function registerOpenApi(app, config) {
52
89
  {
53
90
  name: "Reporting",
54
91
  description: "Reporting and analytics"
92
+ },
93
+ {
94
+ name: "Access",
95
+ description: "Access scopes, connected apps and API keys"
55
96
  }
56
97
  ]
57
98
  },
58
- 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
+ },
59
106
  transformObject: jsonSchemaTransformObject
60
107
  });
61
108
  app.register(swaggerUi, { routePrefix: "/docs" });
@@ -1 +1 @@
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 pkg from '../../../package.json' with { type: 'json' };\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 { name: 'Assets', description: 'The org media library: uploads, serving and lifecycle' },\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 ],\n },\n transform: jsonSchemaTransform,\n transformObject: jsonSchemaTransformObject,\n });\n app.register(swaggerUi, { routePrefix: '/docs' });\n}\n"],"mappings":";;;;;AAWA,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;KAAE,MAAM;KAAU,aAAa;IAAwD;IACvF;KAAE,MAAM;KAAe,aAAa;IAAqD;IACzF;KAAE,MAAM;KAAgB,aAAa;IAAmC;IACxE;KAAE,MAAM;KAAa,aAAa;IAA0B;GAC9D;EACF;EACA,WAAW;EACX,iBAAiB;CACnB,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":";;;;;;;AAiBA,eAAsB,aAAa,KAAsB,WAAqC;CAC5F,MAAM,IAAI,IAAI,iBAAkC;CAChD,MAAM,OAAO,CAAC,QAAQ;CAEtB,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aACE;GACF,UAAU;IAAE,KAAK;IAAiB,GAAG,SAAS,GAAG;GAAE;EACrD;EACA,SAAS,OAAO,QAAQ;GAEtB,OAAO,cAAa,MADA,aAAa,WAAW,GAAG,EAAA,CACrB,IAAI;EAChC;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aACE;GACF,UAAU;IAAE,KAAK;IAAkB,GAAG,SAAS,GAAG;GAAE;EACtD;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,UAAU,KAAK,kBAAkB,IAAI,SAAS,MAAM,SAAS;EACtE;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aACE;GACF,QAAQ;GACR,UAAU;IAAE,KAAK,EAAE,KAAK;IAAG,GAAG,SAAS,GAAG;GAAE;EAC9C;EACA,SAAS,OAAO,KAAK,UAAU;GAC7B,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,MAAM,UAAU,KAAK,mBAAmB,IAAI,SAAS,MAAM,WAAW,IAAI,OAAO,QAAQ;GACzF,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;GACA,SAAS;GACT,aACE;GACF,UAAU;IAAE,KAAK;IAAY,GAAG,SAAS,GAAG;GAAE;EAChD;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,UAAU,KAAK,YAAY,IAAI,SAAS,MAAM,SAAS;EAChE;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aACE;GACF,MAAM;GACN,UAAU;IAAE,KAAK;IAAe,GAAG,SAAS,KAAK,GAAG;GAAE;EACxD;EACA,SAAS,OAAO,KAAK,UAAU;GAC7B,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,sBAAsB,MAAM,MAAM,IAAI,KAAK,MAAM;GACjD,MAAM,UAAU,MAAM,UAAU,KAAK,aAAa;IAChD,YAAY,IAAI,SAAS;IACzB,OAAO,MAAM;IACb,MAAM,IAAI,KAAK;IACf,QAAQ,IAAI,KAAK;GACnB,CAAC;GACD,OAAO,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,OAAO;EACrC;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aACE;GACF,QAAQ;GACR,UAAU;IAAE,KAAK,EAAE,KAAK;IAAG,GAAG,SAAS,KAAK,GAAG;GAAE;EACnD;EACA,SAAS,OAAO,KAAK,UAAU;GAC7B,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,MAAM,UAAU,KAAK,aAAa,IAAI,SAAS,MAAM,WAAW,IAAI,OAAO,EAAE;GAC7E,OAAO,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK;EAC9B;CACF,CAAC;AACH"}
@@ -1,3 +1,4 @@
1
+ import { refusals } from "../schemas/shared.js";
1
2
  import { resolveScope } from "../scope.js";
2
3
  import { ActivityAssetList, ActivityList, ActivityPathParam, CourseIdPathParam, CreateModule, ModuleList, ModulePathParam, ReorderInput, SaveActivity, UpdateModule } from "../schemas/activities.js";
3
4
  import "../schemas/index.js";
@@ -13,9 +14,13 @@ 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);
@@ -29,13 +34,17 @@ async function activitiesRoutes(app, container) {
29
34
  schema: {
30
35
  operationId: "listActivities",
31
36
  tags,
32
- summary: "List a course's activities",
37
+ summary: "List a course's activities (staff)",
38
+ description: "Staff only (owner, admin, instructor). Every activity, published or not.",
33
39
  params: CourseIdPathParam,
34
- response: { 200: ActivityList }
40
+ response: {
41
+ 200: ActivityList,
42
+ ...refusals(403)
43
+ }
35
44
  },
36
45
  handler: async (req) => {
37
46
  const scope = await resolveScope(container, req);
38
- return content.listCourseActivities(scope.orgId, req.params.courseId);
47
+ return content.listCourseActivityViews(scope.orgId, req.params.courseId);
39
48
  }
40
49
  });
41
50
  r.route({
@@ -45,9 +54,13 @@ async function activitiesRoutes(app, container) {
45
54
  schema: {
46
55
  operationId: "listActivityAssets",
47
56
  tags,
48
- summary: "List a course's activity→asset links",
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.",
49
59
  params: CourseIdPathParam,
50
- response: { 200: ActivityAssetList }
60
+ response: {
61
+ 200: ActivityAssetList,
62
+ ...refusals(403)
63
+ }
51
64
  },
52
65
  handler: async (req) => {
53
66
  const scope = await resolveScope(container, req);
@@ -61,10 +74,14 @@ async function activitiesRoutes(app, container) {
61
74
  schema: {
62
75
  operationId: "createModule",
63
76
  tags,
64
- summary: "Add a module",
77
+ summary: "Add a module (staff)",
78
+ description: "Staff only (owner, admin, instructor). Added last. Returns the course's modules.",
65
79
  params: CourseIdPathParam,
66
80
  body: CreateModule,
67
- response: { 200: ModuleList }
81
+ response: {
82
+ 200: ModuleList,
83
+ ...refusals(403)
84
+ }
68
85
  },
69
86
  handler: async (req) => {
70
87
  const scope = await resolveScope(container, req);
@@ -78,10 +95,14 @@ async function activitiesRoutes(app, container) {
78
95
  schema: {
79
96
  operationId: "reorderModules",
80
97
  tags,
81
- 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.",
82
100
  params: CourseIdPathParam,
83
101
  body: ReorderInput,
84
- response: { 200: ModuleList }
102
+ response: {
103
+ 200: ModuleList,
104
+ ...refusals(403)
105
+ }
85
106
  },
86
107
  handler: async (req) => {
87
108
  const scope = await resolveScope(container, req);
@@ -95,10 +116,14 @@ async function activitiesRoutes(app, container) {
95
116
  schema: {
96
117
  operationId: "updateModule",
97
118
  tags,
98
- summary: "Rename a module",
119
+ summary: "Rename a module (staff)",
120
+ description: "Staff only (owner, admin, instructor). Returns the course's modules.",
99
121
  params: ModulePathParam,
100
122
  body: UpdateModule,
101
- response: { 200: ModuleList }
123
+ response: {
124
+ 200: ModuleList,
125
+ ...refusals(403, 404)
126
+ }
102
127
  },
103
128
  handler: async (req) => {
104
129
  const scope = await resolveScope(container, req);
@@ -112,9 +137,13 @@ async function activitiesRoutes(app, container) {
112
137
  schema: {
113
138
  operationId: "deleteModule",
114
139
  tags,
115
- summary: "Delete a module",
140
+ summary: "Delete a module (staff)",
141
+ description: "Staff only (owner, admin, instructor). Returns the course's modules.",
116
142
  params: ModulePathParam,
117
- response: { 200: ModuleList }
143
+ response: {
144
+ 200: ModuleList,
145
+ ...refusals(403, 404)
146
+ }
118
147
  },
119
148
  handler: async (req) => {
120
149
  const scope = await resolveScope(container, req);
@@ -128,10 +157,14 @@ async function activitiesRoutes(app, container) {
128
157
  schema: {
129
158
  operationId: "reorderActivities",
130
159
  tags,
131
- 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.",
132
162
  params: ModulePathParam,
133
163
  body: ReorderInput,
134
- response: { 200: ModuleList }
164
+ response: {
165
+ 200: ModuleList,
166
+ ...refusals(403, 404, 409)
167
+ }
135
168
  },
136
169
  handler: async (req) => {
137
170
  const scope = await resolveScope(container, req);
@@ -145,10 +178,14 @@ async function activitiesRoutes(app, container) {
145
178
  schema: {
146
179
  operationId: "createActivity",
147
180
  tags,
148
- 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.",
149
183
  params: ModulePathParam,
150
184
  body: SaveActivity,
151
- response: { 200: ModuleList }
185
+ response: {
186
+ 200: ModuleList,
187
+ ...refusals(403, 404, 409)
188
+ }
152
189
  },
153
190
  handler: async (req) => {
154
191
  const scope = await resolveScope(container, req);
@@ -162,10 +199,14 @@ async function activitiesRoutes(app, container) {
162
199
  schema: {
163
200
  operationId: "updateActivity",
164
201
  tags,
165
- 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.",
166
204
  params: ActivityPathParam,
167
205
  body: SaveActivity,
168
- response: { 200: ModuleList }
206
+ response: {
207
+ 200: ModuleList,
208
+ ...refusals(403, 404, 409)
209
+ }
169
210
  },
170
211
  handler: async (req) => {
171
212
  const scope = await resolveScope(container, req);
@@ -179,9 +220,13 @@ async function activitiesRoutes(app, container) {
179
220
  schema: {
180
221
  operationId: "deleteActivity",
181
222
  tags,
182
- summary: "Delete an activity",
223
+ summary: "Delete an activity (staff)",
224
+ description: "Staff only (owner, admin, instructor). Returns the course's modules.",
183
225
  params: ActivityPathParam,
184
- response: { 200: ModuleList }
226
+ response: {
227
+ 200: ModuleList,
228
+ ...refusals(403, 404)
229
+ }
185
230
  },
186
231
  handler: async (req) => {
187
232
  const scope = await resolveScope(container, req);