@falkenslab/moodle-driver 0.0.0-stage → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (300) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +131 -2
  3. package/dist/areas/activities/build.d.ts +98 -0
  4. package/dist/areas/activities/build.js +248 -0
  5. package/dist/areas/activities/build.js.map +1 -0
  6. package/dist/areas/activities/content.d.ts +53 -0
  7. package/dist/areas/activities/content.js +113 -0
  8. package/dist/areas/activities/content.js.map +1 -0
  9. package/dist/areas/assignments/grader.d.ts +68 -0
  10. package/dist/areas/assignments/grader.js +169 -0
  11. package/dist/areas/assignments/grader.js.map +1 -0
  12. package/dist/areas/assignments/grading.d.ts +86 -0
  13. package/dist/areas/assignments/grading.js +162 -0
  14. package/dist/areas/assignments/grading.js.map +1 -0
  15. package/dist/areas/assignments/student.d.ts +57 -0
  16. package/dist/areas/assignments/student.js +115 -0
  17. package/dist/areas/assignments/student.js.map +1 -0
  18. package/dist/areas/calendar/calendar.d.ts +43 -0
  19. package/dist/areas/calendar/calendar.js +84 -0
  20. package/dist/areas/calendar/calendar.js.map +1 -0
  21. package/dist/areas/completion/completion.d.ts +50 -0
  22. package/dist/areas/completion/completion.js +112 -0
  23. package/dist/areas/completion/completion.js.map +1 -0
  24. package/dist/areas/courses/list.d.ts +59 -0
  25. package/dist/areas/courses/list.js +74 -0
  26. package/dist/areas/courses/list.js.map +1 -0
  27. package/dist/areas/courses/overview.d.ts +83 -0
  28. package/dist/areas/courses/overview.js +137 -0
  29. package/dist/areas/courses/overview.js.map +1 -0
  30. package/dist/areas/courses/state.d.ts +51 -0
  31. package/dist/areas/courses/state.js +45 -0
  32. package/dist/areas/courses/state.js.map +1 -0
  33. package/dist/areas/courses/structure.d.ts +89 -0
  34. package/dist/areas/courses/structure.js +104 -0
  35. package/dist/areas/courses/structure.js.map +1 -0
  36. package/dist/areas/forums/discussions.d.ts +57 -0
  37. package/dist/areas/forums/discussions.js +119 -0
  38. package/dist/areas/forums/discussions.js.map +1 -0
  39. package/dist/areas/forums/posts.d.ts +83 -0
  40. package/dist/areas/forums/posts.js +114 -0
  41. package/dist/areas/forums/posts.js.map +1 -0
  42. package/dist/areas/grades/gradebook.d.ts +104 -0
  43. package/dist/areas/grades/gradebook.js +250 -0
  44. package/dist/areas/grades/gradebook.js.map +1 -0
  45. package/dist/areas/grades/user-report.d.ts +26 -0
  46. package/dist/areas/grades/user-report.js +71 -0
  47. package/dist/areas/grades/user-report.js.map +1 -0
  48. package/dist/areas/h5p/h5p.d.ts +33 -0
  49. package/dist/areas/h5p/h5p.js +104 -0
  50. package/dist/areas/h5p/h5p.js.map +1 -0
  51. package/dist/areas/lessons/lessons.d.ts +60 -0
  52. package/dist/areas/lessons/lessons.js +145 -0
  53. package/dist/areas/lessons/lessons.js.map +1 -0
  54. package/dist/areas/messages/messages.d.ts +68 -0
  55. package/dist/areas/messages/messages.js +84 -0
  56. package/dist/areas/messages/messages.js.map +1 -0
  57. package/dist/areas/participants/participants.d.ts +57 -0
  58. package/dist/areas/participants/participants.js +135 -0
  59. package/dist/areas/participants/participants.js.map +1 -0
  60. package/dist/areas/questions/banks.d.ts +83 -0
  61. package/dist/areas/questions/banks.js +239 -0
  62. package/dist/areas/questions/banks.js.map +1 -0
  63. package/dist/areas/quizzes/attempts.d.ts +123 -0
  64. package/dist/areas/quizzes/attempts.js +300 -0
  65. package/dist/areas/quizzes/attempts.js.map +1 -0
  66. package/dist/areas/quizzes/results.d.ts +36 -0
  67. package/dist/areas/quizzes/results.js +93 -0
  68. package/dist/areas/quizzes/results.js.map +1 -0
  69. package/dist/areas/quizzes/structure.d.ts +42 -0
  70. package/dist/areas/quizzes/structure.js +100 -0
  71. package/dist/areas/quizzes/structure.js.map +1 -0
  72. package/dist/areas/scorm/scorm.d.ts +60 -0
  73. package/dist/areas/scorm/scorm.js +171 -0
  74. package/dist/areas/scorm/scorm.js.map +1 -0
  75. package/dist/areas/site/info.d.ts +25 -0
  76. package/dist/areas/site/info.js +45 -0
  77. package/dist/areas/site/info.js.map +1 -0
  78. package/dist/areas/site/users.d.ts +56 -0
  79. package/dist/areas/site/users.js +89 -0
  80. package/dist/areas/site/users.js.map +1 -0
  81. package/dist/areas/surveys/choice.d.ts +38 -0
  82. package/dist/areas/surveys/choice.js +105 -0
  83. package/dist/areas/surveys/choice.js.map +1 -0
  84. package/dist/areas/surveys/surveys.d.ts +51 -0
  85. package/dist/areas/surveys/surveys.js +201 -0
  86. package/dist/areas/surveys/surveys.js.map +1 -0
  87. package/dist/browser/chromium.d.ts +15 -0
  88. package/dist/browser/chromium.js +53 -0
  89. package/dist/browser/chromium.js.map +1 -0
  90. package/dist/browser/cookies.d.ts +11 -0
  91. package/dist/browser/cookies.js +31 -0
  92. package/dist/browser/cookies.js.map +1 -0
  93. package/dist/browser/manager.d.ts +57 -0
  94. package/dist/browser/manager.js +177 -0
  95. package/dist/browser/manager.js.map +1 -0
  96. package/dist/browser.d.ts +53 -0
  97. package/dist/browser.js +167 -0
  98. package/dist/browser.js.map +1 -0
  99. package/dist/categories.d.ts +28 -0
  100. package/dist/categories.js +225 -0
  101. package/dist/categories.js.map +1 -0
  102. package/dist/chromiumInstaller.d.ts +15 -0
  103. package/dist/chromiumInstaller.js +52 -0
  104. package/dist/chromiumInstaller.js.map +1 -0
  105. package/dist/cli/args.d.ts +33 -0
  106. package/dist/cli/args.js +194 -0
  107. package/dist/cli/args.js.map +1 -0
  108. package/dist/cli/commands.generated.d.ts +4 -0
  109. package/dist/cli/commands.generated.js +2217 -0
  110. package/dist/cli/commands.generated.js.map +1 -0
  111. package/dist/cli/config.d.ts +46 -0
  112. package/dist/cli/config.js +107 -0
  113. package/dist/cli/config.js.map +1 -0
  114. package/dist/cli/main.d.ts +2 -0
  115. package/dist/cli/main.js +19 -0
  116. package/dist/cli/main.js.map +1 -0
  117. package/dist/cli/output.d.ts +4 -0
  118. package/dist/cli/output.js +71 -0
  119. package/dist/cli/output.js.map +1 -0
  120. package/dist/cli/run.d.ts +15 -0
  121. package/dist/cli/run.js +194 -0
  122. package/dist/cli/run.js.map +1 -0
  123. package/dist/cli/spec.d.ts +42 -0
  124. package/dist/cli/spec.js +4 -0
  125. package/dist/cli/spec.js.map +1 -0
  126. package/dist/client.d.ts +224 -0
  127. package/dist/client.js +182 -0
  128. package/dist/client.js.map +1 -0
  129. package/dist/core/ajax.d.ts +25 -0
  130. package/dist/core/ajax.js +85 -0
  131. package/dist/core/ajax.js.map +1 -0
  132. package/dist/core/editor.d.ts +18 -0
  133. package/dist/core/editor.js +51 -0
  134. package/dist/core/editor.js.map +1 -0
  135. package/dist/core/errors.d.ts +12 -0
  136. package/dist/core/errors.js +10 -0
  137. package/dist/core/errors.js.map +1 -0
  138. package/dist/core/files.d.ts +16 -0
  139. package/dist/core/files.js +58 -0
  140. package/dist/core/files.js.map +1 -0
  141. package/dist/core/forms.d.ts +45 -0
  142. package/dist/core/forms.js +200 -0
  143. package/dist/core/forms.js.map +1 -0
  144. package/dist/core/http.d.ts +18 -0
  145. package/dist/core/http.js +22 -0
  146. package/dist/core/http.js.map +1 -0
  147. package/dist/core/inPage.d.ts +8 -0
  148. package/dist/core/inPage.js +10 -0
  149. package/dist/core/inPage.js.map +1 -0
  150. package/dist/core/login.d.ts +40 -0
  151. package/dist/core/login.js +83 -0
  152. package/dist/core/login.js.map +1 -0
  153. package/dist/core/pace.d.ts +4 -0
  154. package/dist/core/pace.js +21 -0
  155. package/dist/core/pace.js.map +1 -0
  156. package/dist/core/session.d.ts +55 -0
  157. package/dist/core/session.js +157 -0
  158. package/dist/core/session.js.map +1 -0
  159. package/dist/core/site.d.ts +8 -0
  160. package/dist/core/site.js +36 -0
  161. package/dist/core/site.js.map +1 -0
  162. package/dist/core/text.d.ts +21 -0
  163. package/dist/core/text.js +113 -0
  164. package/dist/core/text.js.map +1 -0
  165. package/dist/home.d.ts +19 -0
  166. package/dist/home.js +54 -0
  167. package/dist/home.js.map +1 -0
  168. package/dist/index.d.ts +41 -0
  169. package/dist/index.js +52 -0
  170. package/dist/index.js.map +1 -0
  171. package/dist/internals.d.ts +11 -0
  172. package/dist/internals.js +15 -0
  173. package/dist/internals.js.map +1 -0
  174. package/dist/moodle/activities.d.ts +94 -0
  175. package/dist/moodle/activities.js +240 -0
  176. package/dist/moodle/activities.js.map +1 -0
  177. package/dist/moodle/ajax.d.ts +22 -0
  178. package/dist/moodle/ajax.js +91 -0
  179. package/dist/moodle/ajax.js.map +1 -0
  180. package/dist/moodle/assign.d.ts +65 -0
  181. package/dist/moodle/assign.js +162 -0
  182. package/dist/moodle/assign.js.map +1 -0
  183. package/dist/moodle/calendar.d.ts +41 -0
  184. package/dist/moodle/calendar.js +80 -0
  185. package/dist/moodle/calendar.js.map +1 -0
  186. package/dist/moodle/choice.d.ts +33 -0
  187. package/dist/moodle/choice.js +100 -0
  188. package/dist/moodle/choice.js.map +1 -0
  189. package/dist/moodle/completion.d.ts +48 -0
  190. package/dist/moodle/completion.js +108 -0
  191. package/dist/moodle/completion.js.map +1 -0
  192. package/dist/moodle/content.d.ts +43 -0
  193. package/dist/moodle/content.js +105 -0
  194. package/dist/moodle/content.js.map +1 -0
  195. package/dist/moodle/course.d.ts +51 -0
  196. package/dist/moodle/course.js +45 -0
  197. package/dist/moodle/course.js.map +1 -0
  198. package/dist/moodle/courses.d.ts +57 -0
  199. package/dist/moodle/courses.js +70 -0
  200. package/dist/moodle/courses.js.map +1 -0
  201. package/dist/moodle/editor.d.ts +18 -0
  202. package/dist/moodle/editor.js +50 -0
  203. package/dist/moodle/editor.js.map +1 -0
  204. package/dist/moodle/files.d.ts +16 -0
  205. package/dist/moodle/files.js +57 -0
  206. package/dist/moodle/files.js.map +1 -0
  207. package/dist/moodle/forms.d.ts +44 -0
  208. package/dist/moodle/forms.js +199 -0
  209. package/dist/moodle/forms.js.map +1 -0
  210. package/dist/moodle/forum.d.ts +55 -0
  211. package/dist/moodle/forum.js +115 -0
  212. package/dist/moodle/forum.js.map +1 -0
  213. package/dist/moodle/forumPosts.d.ts +83 -0
  214. package/dist/moodle/forumPosts.js +113 -0
  215. package/dist/moodle/forumPosts.js.map +1 -0
  216. package/dist/moodle/gradebook.d.ts +102 -0
  217. package/dist/moodle/gradebook.js +246 -0
  218. package/dist/moodle/gradebook.js.map +1 -0
  219. package/dist/moodle/grades.d.ts +24 -0
  220. package/dist/moodle/grades.js +67 -0
  221. package/dist/moodle/grades.js.map +1 -0
  222. package/dist/moodle/grading.d.ts +92 -0
  223. package/dist/moodle/grading.js +161 -0
  224. package/dist/moodle/grading.js.map +1 -0
  225. package/dist/moodle/h5p.d.ts +31 -0
  226. package/dist/moodle/h5p.js +100 -0
  227. package/dist/moodle/h5p.js.map +1 -0
  228. package/dist/moodle/inPage.d.ts +8 -0
  229. package/dist/moodle/inPage.js +10 -0
  230. package/dist/moodle/inPage.js.map +1 -0
  231. package/dist/moodle/lesson.d.ts +57 -0
  232. package/dist/moodle/lesson.js +140 -0
  233. package/dist/moodle/lesson.js.map +1 -0
  234. package/dist/moodle/login.d.ts +37 -0
  235. package/dist/moodle/login.js +77 -0
  236. package/dist/moodle/login.js.map +1 -0
  237. package/dist/moodle/messages.d.ts +66 -0
  238. package/dist/moodle/messages.js +80 -0
  239. package/dist/moodle/messages.js.map +1 -0
  240. package/dist/moodle/overview.d.ts +83 -0
  241. package/dist/moodle/overview.js +137 -0
  242. package/dist/moodle/overview.js.map +1 -0
  243. package/dist/moodle/pace.d.ts +4 -0
  244. package/dist/moodle/pace.js +19 -0
  245. package/dist/moodle/pace.js.map +1 -0
  246. package/dist/moodle/participants.d.ts +55 -0
  247. package/dist/moodle/participants.js +131 -0
  248. package/dist/moodle/participants.js.map +1 -0
  249. package/dist/moodle/questionBank.d.ts +81 -0
  250. package/dist/moodle/questionBank.js +235 -0
  251. package/dist/moodle/questionBank.js.map +1 -0
  252. package/dist/moodle/quiz.d.ts +117 -0
  253. package/dist/moodle/quiz.js +291 -0
  254. package/dist/moodle/quiz.js.map +1 -0
  255. package/dist/moodle/quizResults.d.ts +36 -0
  256. package/dist/moodle/quizResults.js +93 -0
  257. package/dist/moodle/quizResults.js.map +1 -0
  258. package/dist/moodle/quizStructure.d.ts +42 -0
  259. package/dist/moodle/quizStructure.js +100 -0
  260. package/dist/moodle/quizStructure.js.map +1 -0
  261. package/dist/moodle/session.d.ts +39 -0
  262. package/dist/moodle/session.js +99 -0
  263. package/dist/moodle/session.js.map +1 -0
  264. package/dist/moodle/site.d.ts +8 -0
  265. package/dist/moodle/site.js +36 -0
  266. package/dist/moodle/site.js.map +1 -0
  267. package/dist/moodle/siteInfo.d.ts +25 -0
  268. package/dist/moodle/siteInfo.js +45 -0
  269. package/dist/moodle/siteInfo.js.map +1 -0
  270. package/dist/moodle/structure.d.ts +94 -0
  271. package/dist/moodle/structure.js +103 -0
  272. package/dist/moodle/structure.js.map +1 -0
  273. package/dist/moodle/submission.d.ts +48 -0
  274. package/dist/moodle/submission.js +109 -0
  275. package/dist/moodle/submission.js.map +1 -0
  276. package/dist/moodle/survey.d.ts +47 -0
  277. package/dist/moodle/survey.js +197 -0
  278. package/dist/moodle/survey.js.map +1 -0
  279. package/dist/moodle/text.d.ts +21 -0
  280. package/dist/moodle/text.js +113 -0
  281. package/dist/moodle/text.js.map +1 -0
  282. package/dist/moodle/user.d.ts +54 -0
  283. package/dist/moodle/user.js +78 -0
  284. package/dist/moodle/user.js.map +1 -0
  285. package/dist/options.d.ts +30 -0
  286. package/dist/options.js +35 -0
  287. package/dist/options.js.map +1 -0
  288. package/dist/sessionCookies.d.ts +11 -0
  289. package/dist/sessionCookies.js +31 -0
  290. package/dist/sessionCookies.js.map +1 -0
  291. package/dist/testing/fakeMoodle.d.ts +55 -0
  292. package/dist/testing/fakeMoodle.js +103 -0
  293. package/dist/testing/fakeMoodle.js.map +1 -0
  294. package/dist/testing/gradingPanel.d.ts +6 -0
  295. package/dist/testing/gradingPanel.js +22 -0
  296. package/dist/testing/gradingPanel.js.map +1 -0
  297. package/dist/testing/index.d.ts +5 -0
  298. package/dist/testing/index.js +18 -0
  299. package/dist/testing/index.js.map +1 -0
  300. package/package.json +78 -4
@@ -0,0 +1,55 @@
1
+ import type { BrowserContext, Page } from "playwright-core";
2
+ import { MoodleError } from "./errors.js";
3
+ /** No live Moodle session: the person has to log in again (ADR-002). */
4
+ export declare class NotLoggedInError extends MoodleError {
5
+ name: string;
6
+ constructor();
7
+ }
8
+ /** What a Moodle page says about its session, read without depending on the interface's language. */
9
+ export interface SessionFacts {
10
+ /** Moodle adds the `notloggedin` body class when nobody is logged in. */
11
+ notLoggedIn: boolean;
12
+ /** `M.cfg.userId`: 0 when logged out; `null` on a page that isn't Moodle's (an SSO provider's). */
13
+ userId: number | null;
14
+ /** A logout link, in the user menu of a real user's pages. */
15
+ hasLogoutLink: boolean;
16
+ /** A login link: a guest (who also has a user id) gets one and no logout link. */
17
+ hasLoginLink: boolean;
18
+ }
19
+ export declare function readSessionFacts(page: Page): Promise<SessionFacts>;
20
+ /** Whether a page at `url`, with these facts, belongs to the site and has a real user logged in. */
21
+ export declare function isLoggedIn(url: string, siteUrl: string, facts: SessionFacts): boolean;
22
+ /** Whether `page`, as it is now, shows the site with a real user logged in. */
23
+ export declare function pageIsLoggedIn(page: Page, siteUrl: string): Promise<boolean>;
24
+ /** Has `show` called with the page each function opened, once it's done with it. */
25
+ export declare function followPages(context: BrowserContext, show: (url: string) => Promise<void>): void;
26
+ /**
27
+ * Opens a page of the site (`path` relative to it) in a new tab and checks the session; throws
28
+ * `NotLoggedInError`, closing the tab, when Moodle answers with its login page.
29
+ */
30
+ export declare function openSitePage(context: BrowserContext, siteUrl: string, path: string, { follow }?: {
31
+ follow?: boolean | undefined;
32
+ }): Promise<Page>;
33
+ /** `M.cfg` as a Moodle page's HTML sets it (`M.cfg = {…};`), parsed; `undefined` when it has none. */
34
+ export declare function readMoodleConfig(html: string): Record<string, unknown> | undefined;
35
+ /** What a page's HTML says about its session, read as `readSessionFacts` reads a page. */
36
+ export declare function htmlSessionFacts(html: string): SessionFacts;
37
+ /**
38
+ * A page of the site over HTTP with the profile's session, without a tab (ADR-011): its final URL and
39
+ * HTML. Throws `NotLoggedInError` when Moodle answers with its login page. While the browser is shown,
40
+ * the working tab shows the page afterwards (ADR-007), unless `follow` is false.
41
+ */
42
+ export declare function fetchSitePage(context: BrowserContext, siteUrl: string, path: string, { follow }?: {
43
+ follow?: boolean | undefined;
44
+ }): Promise<{
45
+ url: string;
46
+ html: string;
47
+ }>;
48
+ /**
49
+ * Logs the person out of Moodle through the user menu's logout link (it carries the sesskey; without
50
+ * one Moodle asks to confirm, and the confirmation is accepted). Returns false when nobody was logged
51
+ * in. Only Moodle's session ends: a single sign-on provider may keep its own.
52
+ */
53
+ export declare function logOut(context: BrowserContext, siteUrl: string): Promise<boolean>;
54
+ /** Moodle's message on an error page (its fatal error box: no permission, no such record), which keeps the page's body id. */
55
+ export declare function moodleErrorMessage(page: Page): Promise<string | undefined>;
@@ -0,0 +1,157 @@
1
+ import { MoodleError } from "./errors.js";
2
+ import { siteFetch } from "./http.js";
3
+ /** No live Moodle session: the person has to log in again (ADR-002). */
4
+ export class NotLoggedInError extends MoodleError {
5
+ name = "NotLoggedInError";
6
+ constructor() {
7
+ super("There is no Moodle session, or it expired: the person has to log in again.", "not-logged-in");
8
+ }
9
+ }
10
+ export function readSessionFacts(page) {
11
+ return page.evaluate(() => {
12
+ // Runs in the page: Moodle's globals and the DOM, typed loosely on purpose (no DOM lib in Node).
13
+ const g = globalThis;
14
+ const userId = g.M?.cfg?.userId;
15
+ return {
16
+ notLoggedIn: g.document.body?.classList.contains("notloggedin") ?? true,
17
+ userId: typeof userId === "number" ? userId : null,
18
+ hasLogoutLink: g.document.querySelector('a[href*="/login/logout.php"]') !== null,
19
+ hasLoginLink: g.document.querySelector('a[href*="/login/index.php"]') !== null,
20
+ };
21
+ });
22
+ }
23
+ /** Whether a page at `url`, with these facts, belongs to the site and has a real user logged in. */
24
+ export function isLoggedIn(url, siteUrl, facts) {
25
+ if (!url.startsWith(siteUrl))
26
+ return false;
27
+ if (url.startsWith(new URL("login/", siteUrl).href))
28
+ return false;
29
+ // Pages without the navigation bar (the assignment grader) have neither link (confirmed empirically,
30
+ // Moodle 5.2): only a login link without a logout one means a guest.
31
+ const guest = facts.hasLoginLink && !facts.hasLogoutLink;
32
+ return !facts.notLoggedIn && (facts.userId ?? 0) > 0 && !guest;
33
+ }
34
+ /** Whether `page`, as it is now, shows the site with a real user logged in. */
35
+ export async function pageIsLoggedIn(page, siteUrl) {
36
+ return isLoggedIn(page.url(), siteUrl, await readSessionFacts(page));
37
+ }
38
+ // While the person watches (ADR-007), a context's browser manager shows the pages functions worked on.
39
+ const followers = new WeakMap();
40
+ /** Has `show` called with the page each function opened, once it's done with it. */
41
+ export function followPages(context, show) {
42
+ followers.set(context, show);
43
+ }
44
+ /**
45
+ * Opens a page of the site (`path` relative to it) in a new tab and checks the session; throws
46
+ * `NotLoggedInError`, closing the tab, when Moodle answers with its login page.
47
+ */
48
+ export async function openSitePage(context, siteUrl, path, { follow = true } = {}) {
49
+ const page = await context.newPage();
50
+ try {
51
+ await page.goto(new URL(path, siteUrl).href, { waitUntil: "domcontentloaded" });
52
+ if (!(await pageIsLoggedIn(page, siteUrl)))
53
+ throw new NotLoggedInError();
54
+ // The page as it was opened (a form's URL, not where posting it led), once it's closed.
55
+ const opened = page.url();
56
+ if (follow)
57
+ page.once("close", () => void followers.get(context)?.(opened));
58
+ return page;
59
+ }
60
+ catch (error) {
61
+ await page.close().catch(() => undefined);
62
+ throw error;
63
+ }
64
+ }
65
+ /** `M.cfg` as a Moodle page's HTML sets it (`M.cfg = {…};`), parsed; `undefined` when it has none. */
66
+ export function readMoodleConfig(html) {
67
+ const start = html.indexOf("M.cfg = {");
68
+ if (start < 0)
69
+ return undefined;
70
+ // The object literal is JSON (Moodle writes it with json_encode): its end is where the braces balance.
71
+ let depth = 0;
72
+ let inString = false;
73
+ const from = html.indexOf("{", start);
74
+ for (let i = from; i < html.length; i++) {
75
+ const c = html[i];
76
+ if (inString) {
77
+ if (c === "\\")
78
+ i++;
79
+ else if (c === '"')
80
+ inString = false;
81
+ }
82
+ else if (c === '"')
83
+ inString = true;
84
+ else if (c === "{")
85
+ depth++;
86
+ else if (c === "}" && --depth === 0) {
87
+ try {
88
+ return JSON.parse(html.slice(from, i + 1));
89
+ }
90
+ catch {
91
+ return undefined;
92
+ }
93
+ }
94
+ }
95
+ return undefined;
96
+ }
97
+ /** What a page's HTML says about its session, read as `readSessionFacts` reads a page. */
98
+ export function htmlSessionFacts(html) {
99
+ const userId = readMoodleConfig(html)?.userId;
100
+ const bodyClass = /<body\b[^>]*\bclass="([^"]*)"/i.exec(html)?.[1] ?? "notloggedin";
101
+ return {
102
+ notLoggedIn: /(^|\s)notloggedin(\s|$)/.test(bodyClass),
103
+ userId: typeof userId === "number" ? userId : null,
104
+ hasLogoutLink: /href="[^"]*\/login\/logout\.php/.test(html),
105
+ hasLoginLink: /href="[^"]*\/login\/index\.php/.test(html),
106
+ };
107
+ }
108
+ /**
109
+ * A page of the site over HTTP with the profile's session, without a tab (ADR-011): its final URL and
110
+ * HTML. Throws `NotLoggedInError` when Moodle answers with its login page. While the browser is shown,
111
+ * the working tab shows the page afterwards (ADR-007), unless `follow` is false.
112
+ */
113
+ export async function fetchSitePage(context, siteUrl, path, { follow = true } = {}) {
114
+ const response = await siteFetch(context, new URL(path, siteUrl).href);
115
+ if (!isLoggedIn(response.url, siteUrl, htmlSessionFacts(response.body)))
116
+ throw new NotLoggedInError();
117
+ if (follow)
118
+ void followers.get(context)?.(response.url);
119
+ return { url: response.url, html: response.body };
120
+ }
121
+ /**
122
+ * Logs the person out of Moodle through the user menu's logout link (it carries the sesskey; without
123
+ * one Moodle asks to confirm, and the confirmation is accepted). Returns false when nobody was logged
124
+ * in. Only Moodle's session ends: a single sign-on provider may keep its own.
125
+ */
126
+ export async function logOut(context, siteUrl) {
127
+ const page = await context.newPage();
128
+ const home = new URL("my/", siteUrl).href;
129
+ try {
130
+ await page.goto(home, { waitUntil: "domcontentloaded" });
131
+ if (!(await pageIsLoggedIn(page, siteUrl)))
132
+ return false;
133
+ const link = await page.locator('a[href*="/login/logout.php"]').first().getAttribute("href").catch(() => null);
134
+ // Moodle's redirect after logging out can abort this navigation (ERR_ABORTED, confirmed empirically,
135
+ // Moodle 5.2); whether the session ended is checked below either way.
136
+ await page.goto(new URL(link ?? "login/logout.php", siteUrl).href, { waitUntil: "domcontentloaded" }).catch((error) => {
137
+ if (!/ERR_ABORTED/.test(error.message))
138
+ throw error;
139
+ });
140
+ const confirm = page.locator('form[action*="/login/logout.php"] [type="submit"]');
141
+ if ((await confirm.count()) > 0)
142
+ await Promise.all([page.waitForNavigation({ waitUntil: "domcontentloaded" }), confirm.first().click()]);
143
+ await page.goto(home, { waitUntil: "domcontentloaded" });
144
+ if (await pageIsLoggedIn(page, siteUrl))
145
+ throw new MoodleError("Moodle didn't end the session: the person is still logged in. Try again, or log out in the browser.", "logout-failed");
146
+ return true;
147
+ }
148
+ finally {
149
+ await page.close().catch(() => undefined);
150
+ }
151
+ }
152
+ /** Moodle's message on an error page (its fatal error box: no permission, no such record), which keeps the page's body id. */
153
+ export async function moodleErrorMessage(page) {
154
+ const box = page.locator('[data-rel="fatalerror"] .errormessage');
155
+ return (await box.count()) > 0 ? ((await box.first().textContent()) ?? "").replace(/\s+/g, " ").trim() : undefined;
156
+ }
157
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/core/session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /** `M.cfg.wwwroot` in a Moodle page, JSON-escaped as Moodle writes it (`"http:\/\/host"`). */
2
+ export declare function readWwwroot(html: string): string | undefined;
3
+ /**
4
+ * The site's base URL for any URL of it (the site, a course, the login page), read from the
5
+ * `M.cfg.wwwroot` Moodle puts in every page; fetched without the browser or any session. Throws
6
+ * `MoodleUrlError` when it can't be reached or isn't a Moodle.
7
+ */
8
+ export declare function findMoodleSite(url: string, fetchImpl?: typeof fetch): Promise<string>;
@@ -0,0 +1,36 @@
1
+ import { MoodleUrlError, parseSiteUrl } from "../options.js";
2
+ // The first answer of a site that just started can take long (confirmed empirically, the sandbox).
3
+ const TIMEOUT_MS = 60_000;
4
+ /** `M.cfg.wwwroot` in a Moodle page, JSON-escaped as Moodle writes it (`"http:\/\/host"`). */
5
+ export function readWwwroot(html) {
6
+ const match = /"wwwroot":"((?:[^"\\]|\\.)*)"/.exec(html);
7
+ if (!match)
8
+ return undefined;
9
+ try {
10
+ return JSON.parse(`"${match[1]}"`);
11
+ }
12
+ catch {
13
+ return undefined;
14
+ }
15
+ }
16
+ /**
17
+ * The site's base URL for any URL of it (the site, a course, the login page), read from the
18
+ * `M.cfg.wwwroot` Moodle puts in every page; fetched without the browser or any session. Throws
19
+ * `MoodleUrlError` when it can't be reached or isn't a Moodle.
20
+ */
21
+ export async function findMoodleSite(url, fetchImpl = fetch) {
22
+ parseSiteUrl(url); // http(s) and well formed, or MoodleUrlError.
23
+ let html;
24
+ try {
25
+ const response = await fetchImpl(url.trim(), { redirect: "follow", signal: AbortSignal.timeout(TIMEOUT_MS) });
26
+ html = await response.text();
27
+ }
28
+ catch (error) {
29
+ throw new MoodleUrlError(`Couldn't reach ${url}: ${error.message}. Check the URL and that the site is up.`);
30
+ }
31
+ const wwwroot = readWwwroot(html);
32
+ if (!wwwroot)
33
+ throw new MoodleUrlError(`${url} doesn't look like a Moodle site (its page has no M.cfg.wwwroot). Check the URL.`);
34
+ return parseSiteUrl(wwwroot);
35
+ }
36
+ //# sourceMappingURL=site.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"site.js","sourceRoot":"","sources":["../../src/core/site.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7D,mGAAmG;AACnG,MAAM,UAAU,GAAG,MAAM,CAAC;AAE1B,8FAA8F;AAC9F,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,MAAM,KAAK,GAAG,+BAA+B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzD,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,GAAG,CAAW,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAW,EAAE,YAA0B,KAAK;IAC/E,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,8CAA8C;IACjE,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QAC9G,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC/B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,cAAc,CAAC,kBAAkB,GAAG,KAAM,KAAe,CAAC,OAAO,0CAA0C,CAAC,CAAC;IACzH,CAAC;IACD,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,cAAc,CAAC,GAAG,GAAG,kFAAkF,CAAC,CAAC;IACjI,OAAO,YAAY,CAAC,OAAO,CAAC,CAAC;AAC/B,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { Page } from "playwright-core";
2
+ /** Moodle formats names for HTML (`format_string`): plain text for the client. */
3
+ export declare function decodeEntities(text: string): string;
4
+ /**
5
+ * The text of the elements matching `selector` in `page`, one string each: paragraphs, line breaks
6
+ * and list items kept, scripts and styles left out. Runs in the page with no named functions inside
7
+ * (see the conventions on page.evaluate).
8
+ */
9
+ export declare function readTexts(page: Page, selector: string): Promise<string[]>;
10
+ /** A video, audio or player found in a page's content. */
11
+ export interface MediaItem {
12
+ kind: "video" | "audio" | "youtube" | "vimeo" | "embed";
13
+ url: string;
14
+ title?: string;
15
+ }
16
+ /**
17
+ * The media in the elements matching `selector`: `<video>`/`<audio>` and their sources, players in
18
+ * iframes, video.js's `data-setup` sources (Moodle's media filter turns a YouTube link into one), and
19
+ * plain links to media. YouTube URLs come back as `watch?v=` links. Read in the page.
20
+ */
21
+ export declare function readMedia(page: Page, selector: string): Promise<MediaItem[]>;
@@ -0,0 +1,113 @@
1
+ import { inPage } from "./inPage.js";
2
+ /** Moodle formats names for HTML (`format_string`): plain text for the client. */
3
+ export function decodeEntities(text) {
4
+ return text.replace(/&(#x[0-9a-f]+|#\d+|amp|lt|gt|quot|apos|nbsp);/gi, (entity, code) => {
5
+ const lower = code.toLowerCase();
6
+ if (lower.startsWith("#x"))
7
+ return String.fromCodePoint(parseInt(lower.slice(2), 16));
8
+ if (lower.startsWith("#"))
9
+ return String.fromCodePoint(parseInt(lower.slice(1), 10));
10
+ return { amp: "&", lt: "<", gt: ">", quot: '"', apos: "'", nbsp: " " }[lower] ?? entity;
11
+ });
12
+ }
13
+ /**
14
+ * The text of the elements matching `selector` in `page`, one string each: paragraphs, line breaks
15
+ * and list items kept, scripts and styles left out. Runs in the page with no named functions inside
16
+ * (see the conventions on page.evaluate).
17
+ */
18
+ export function readTexts(page, selector) {
19
+ return page.evaluate((selector) => {
20
+ const doc = globalThis;
21
+ const BLOCKS = new Set(["P", "DIV", "LI", "UL", "OL", "TR", "TABLE", "PRE", "BLOCKQUOTE", "SECTION", "ARTICLE", "H1", "H2", "H3", "H4", "H5", "H6"]);
22
+ return Array.from(doc.document.querySelectorAll(selector)).map((root) => {
23
+ let raw = "";
24
+ const stack = [root];
25
+ while (stack.length > 0) {
26
+ const item = stack.pop();
27
+ if (typeof item === "string")
28
+ raw += item;
29
+ else if (item.nodeType === 3)
30
+ raw += (item.textContent ?? "").replace(/\s+/g, " ");
31
+ else if (item.nodeType !== 1 || item.nodeName === "SCRIPT" || item.nodeName === "STYLE")
32
+ continue;
33
+ else if (item.nodeName === "BR")
34
+ raw += "\n";
35
+ else if (item.nodeName === "PRE")
36
+ raw += `\n${item.textContent ?? ""}\n`;
37
+ else {
38
+ const isBlock = BLOCKS.has(item.nodeName);
39
+ raw += item.nodeName === "LI" ? "\n- " : isBlock ? "\n" : "";
40
+ // A list item ends where the next one starts: no blank line between items.
41
+ stack.push(isBlock && item.nodeName !== "LI" ? "\n" : "", ...Array.from(item.childNodes).reverse());
42
+ }
43
+ }
44
+ return raw
45
+ .replace(/[ \t]+\n/g, "\n")
46
+ .replace(/\n[ \t]+/g, "\n")
47
+ .replace(/\n- +/g, "\n- ")
48
+ .replace(/\n{3,}/g, "\n\n")
49
+ .trim();
50
+ });
51
+ }, selector);
52
+ }
53
+ /**
54
+ * The media in the elements matching `selector`: `<video>`/`<audio>` and their sources, players in
55
+ * iframes, video.js's `data-setup` sources (Moodle's media filter turns a YouTube link into one), and
56
+ * plain links to media. YouTube URLs come back as `watch?v=` links. Read in the page.
57
+ */
58
+ export function readMedia(page, selector) {
59
+ return inPage(page, (selector) => {
60
+ const roots = Array.from(globalThis.document.querySelectorAll(selector));
61
+ const found = new Map();
62
+ const add = (raw, tag, title) => {
63
+ if (!raw || raw.startsWith("blob:") || raw.startsWith("data:"))
64
+ return;
65
+ let url;
66
+ try {
67
+ url = new URL(raw, globalThis.location.href);
68
+ }
69
+ catch {
70
+ return;
71
+ }
72
+ const youtube = /(?:youtube(?:-nocookie)?\.com\/(?:embed\/|watch\?v=|shorts\/)|youtu\.be\/)([\w-]{6,})/.exec(url.href)?.[1];
73
+ let kind;
74
+ let href = url.href;
75
+ if (youtube) {
76
+ kind = "youtube";
77
+ href = `https://www.youtube.com/watch?v=${youtube}`;
78
+ }
79
+ else if (/vimeo\.com\//.test(url.href))
80
+ kind = "vimeo";
81
+ else if (tag === "AUDIO" || /\.(mp3|ogg|oga|wav|m4a|aac)(\?|$)/i.test(url.pathname))
82
+ kind = "audio";
83
+ else if (tag === "VIDEO" || /\.(mp4|webm|m4v|mov|ogv|m3u8)(\?|$)/i.test(url.pathname))
84
+ kind = "video";
85
+ else if (tag === "IFRAME")
86
+ kind = "embed";
87
+ else
88
+ return;
89
+ if (!found.has(href))
90
+ found.set(href, { kind, url: href, ...(title ? { title } : {}) });
91
+ };
92
+ for (const root of roots) {
93
+ for (const el of Array.from(root.querySelectorAll("video, audio, source, iframe, a[href]"))) {
94
+ const media = el.tagName === "SOURCE" ? "VIDEO" : el.tagName;
95
+ add(el.getAttribute("src") ?? el.getAttribute("href"), media, el.getAttribute("title"));
96
+ for (const attribute of ["data-setup-lazy", "data-setup"]) {
97
+ const setup = el.getAttribute(attribute);
98
+ if (!setup)
99
+ continue;
100
+ try {
101
+ for (const source of JSON.parse(setup).sources ?? [])
102
+ add(source.src, "VIDEO", el.getAttribute("title"));
103
+ }
104
+ catch {
105
+ // Not JSON: nothing to read.
106
+ }
107
+ }
108
+ }
109
+ }
110
+ return Array.from(found.values());
111
+ }, selector);
112
+ }
113
+ //# sourceMappingURL=text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/core/text.ts"],"names":[],"mappings":"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"}
package/dist/home.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ /** The command's home when nothing else is given: `~/.moodle-driver`, the same on every system. */
2
+ export declare function defaultHome(home?: string): string;
3
+ /** A site's folder name: its host and port, with anything but letters, digits, dots and hyphens as `_` (`localhost:8080` is `localhost_8080`). */
4
+ export declare function siteFolderName(site: string): string;
5
+ /** A site's folder in the command's home: its `site.json` and its profiles. */
6
+ export declare function siteDir(site: string, home?: string): string;
7
+ /** A profile's name from a Moodle username: anything but letters, digits, dots, hyphens and underscores as `_`. */
8
+ export declare function profileName(username: string): string;
9
+ /**
10
+ * The browser profile folder the `moodle` command uses for an account on a site:
11
+ * `sites/<host>/profiles/<as>`. Without `as`, the site's default (the account last logged in with,
12
+ * named in its `site.json`); `MoodleError` with code `not-found` when the command hasn't logged in to
13
+ * the site. Pass it as `profileDir` to share the command's session; `home` is `~/.moodle-driver`
14
+ * unless given.
15
+ */
16
+ export declare function siteProfileDir(site: string, { as, home }?: {
17
+ as?: string;
18
+ home?: string;
19
+ }): string;
package/dist/home.js ADDED
@@ -0,0 +1,54 @@
1
+ // Where the `moodle` command keeps each site's browser profiles (ADR-013): `~/.moodle-driver` on every
2
+ // system, a folder per site, a named profile per account. The library still chooses no folder by
3
+ // itself; a program that wants to share the command's sessions asks for one here.
4
+ import { readFileSync } from "node:fs";
5
+ import os from "node:os";
6
+ import path from "node:path";
7
+ import { MoodleError } from "./core/errors.js";
8
+ import { parseSiteUrl } from "./options.js";
9
+ /** The command's home when nothing else is given: `~/.moodle-driver`, the same on every system. */
10
+ export function defaultHome(home = os.homedir()) {
11
+ return path.join(home, ".moodle-driver");
12
+ }
13
+ /** A site's folder name: its host and port, with anything but letters, digits, dots and hyphens as `_` (`localhost:8080` is `localhost_8080`). */
14
+ export function siteFolderName(site) {
15
+ return new URL(parseSiteUrl(site)).host.replace(/[^a-z0-9.-]/gi, "_");
16
+ }
17
+ /** A site's folder in the command's home: its `site.json` and its profiles. */
18
+ export function siteDir(site, home = defaultHome()) {
19
+ return path.join(home, "sites", siteFolderName(site));
20
+ }
21
+ /** A profile's name from a Moodle username: anything but letters, digits, dots, hyphens and underscores as `_`. */
22
+ export function profileName(username) {
23
+ const name = username.trim().replace(/[^\w.-]/g, "_").replace(/^\.+/, "");
24
+ return name === "" ? "user" : name;
25
+ }
26
+ /**
27
+ * The browser profile folder the `moodle` command uses for an account on a site:
28
+ * `sites/<host>/profiles/<as>`. Without `as`, the site's default (the account last logged in with,
29
+ * named in its `site.json`); `MoodleError` with code `not-found` when the command hasn't logged in to
30
+ * the site. Pass it as `profileDir` to share the command's session; `home` is `~/.moodle-driver`
31
+ * unless given.
32
+ */
33
+ export function siteProfileDir(site, { as, home = defaultHome() } = {}) {
34
+ if (as === undefined) {
35
+ let record;
36
+ try {
37
+ record = JSON.parse(readFileSync(path.join(siteDir(site, home), "site.json"), "utf8"));
38
+ }
39
+ catch {
40
+ // No site.json: the command hasn't logged in to this site.
41
+ }
42
+ if (record?.default)
43
+ return siteProfileDir(site, { as: record.default, home });
44
+ if (record?.profileDir)
45
+ return record.profileDir;
46
+ const url = parseSiteUrl(site);
47
+ throw new MoodleError(`There's no profile for ${url} yet: log in with \`moodle login --site ${url}\`, or name one with \`as\`.`, "not-found");
48
+ }
49
+ if (!/^[\w.-]+$/.test(as) || as.startsWith(".")) {
50
+ throw new MoodleError(`A profile's name is letters, digits, dots, hyphens and underscores, not starting with a dot: "${as}".`, "invalid-input");
51
+ }
52
+ return path.join(siteDir(site, home), "profiles", as);
53
+ }
54
+ //# sourceMappingURL=home.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"home.js","sourceRoot":"","sources":["../src/home.ts"],"names":[],"mappings":"AAAA,uGAAuG;AACvG,iGAAiG;AACjG,kFAAkF;AAClF,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,mGAAmG;AACnG,MAAM,UAAU,WAAW,CAAC,IAAI,GAAG,EAAE,CAAC,OAAO,EAAE;IAC7C,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;AAC3C,CAAC;AAED,kJAAkJ;AAClJ,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,IAAI,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC;AACxE,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,OAAO,CAAC,IAAY,EAAE,IAAI,GAAG,WAAW,EAAE;IACxD,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;AACxD,CAAC;AAED,mHAAmH;AACnH,MAAM,UAAU,WAAW,CAAC,QAAgB;IAC1C,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC1E,OAAO,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;AACrC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,EAAE,EAAE,EAAE,IAAI,GAAG,WAAW,EAAE,KAAqC,EAAE;IAC5G,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;QACrB,IAAI,MAA6D,CAAC;QAClE,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,WAAW,CAAC,EAAE,MAAM,CAAC,CAAkB,CAAC;QAC1G,CAAC;QAAC,MAAM,CAAC;YACP,2DAA2D;QAC7D,CAAC;QACD,IAAI,MAAM,EAAE,OAAO;YAAE,OAAO,cAAc,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/E,IAAI,MAAM,EAAE,UAAU;YAAE,OAAO,MAAM,CAAC,UAAU,CAAC;QACjD,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;QAC/B,MAAM,IAAI,WAAW,CAAC,0BAA0B,GAAG,2CAA2C,GAAG,8BAA8B,EAAE,WAAW,CAAC,CAAC;IAChJ,CAAC;IACD,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,WAAW,CAAC,iGAAiG,EAAE,IAAI,EAAE,eAAe,CAAC,CAAC;IAClJ,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC;AACxD,CAAC"}
@@ -0,0 +1,41 @@
1
+ export * from "./client.js";
2
+ export * from "./options.js";
3
+ export { createBrowserManager, canShowWindow, BrowserNotFoundError, BrowserDownloadingError, NoDisplayError, type BrowserManager, type ContextOptions } from "./browser/manager.js";
4
+ export { SESSION_COOKIES_FILE } from "./browser/cookies.js";
5
+ export { siteProfileDir } from "./home.js";
6
+ export * from "./categories.js";
7
+ export * from "./core/errors.js";
8
+ export { logIn, LoginTimeoutError, LoginWindowClosedError, type LoginResult } from "./core/login.js";
9
+ export { logOut, NotLoggedInError } from "./core/session.js";
10
+ export { MoodleServiceError } from "./core/ajax.js";
11
+ export { FormError, type FieldValue } from "./core/forms.js";
12
+ export { findMoodleSite } from "./core/site.js";
13
+ export { checkLocalFiles, downloadFile, type DownloadedFile } from "./core/files.js";
14
+ export type { MediaItem } from "./core/text.js";
15
+ export { publicSiteInfo, courseActivityTypes } from "./areas/site/info.js";
16
+ export { userById, siteName, courseRoles, courseAbilities, whoAmI, type User, type Me } from "./areas/site/users.js";
17
+ export { listCourses, updateCourse, COURSE_CLASSIFICATIONS, CourseError, type CourseClassification, type Course } from "./areas/courses/list.js";
18
+ export { courseStructure, listAssignments, pendingActivities, COMPLETION_STATES, type CompletionState, type CourseActivity, type CourseSection, type CourseAssignment, type PendingActivity, } from "./areas/courses/structure.js";
19
+ export { activitiesOverview, deadlines, courseUpdates, type OverviewField } from "./areas/courses/overview.js";
20
+ export { createActivity, updateActivity, editActivity, duplicateActivity, deleteActivity, addSection, editSection, deleteSection, activitySettings, ACTIVITY_VISIBILITIES, ActivitySettingsError, type ActivityVisibility, type ActivityFields, type CourseChange, type FormField, } from "./areas/activities/build.js";
21
+ export { activityContent, CONTENT_TYPES, NotViewableError, type ContentType, type ContentOptions, type ActivityContent } from "./areas/activities/content.js";
22
+ export { NotAnAssignmentError, GradingError, type PluginContent } from "./areas/assignments/grader.js";
23
+ export { assignmentSubmissions, gradeSubmission, rubric, grantExtension, type Submission, type GradingCriterion, type GradingDefinition } from "./areas/assignments/grading.js";
24
+ export { studentAssignment, saveSubmission, submitForGrading, SubmissionError, type StudentAssignment } from "./areas/assignments/student.js";
25
+ export { quizInfo, startQuizAttempt, readAttempt, answerQuizQuestions, finishQuizAttempt, readReview, setQuizOverride, QuizError, type Detail, type QuizInfo, type QuestionField, type Question, } from "./areas/quizzes/attempts.js";
26
+ export { quizResults, type QuizAttemptResult } from "./areas/quizzes/results.js";
27
+ export { quizStructure, addQuestionsToQuiz, type QuizSlot } from "./areas/quizzes/structure.js";
28
+ export { questionBanks, questions, importQuestions, createQuestion, QUESTION_TYPES, QuestionBankError, type QuestionBank, type BankQuestion, type BankCategory, type QuestionType, type QuestionInput, } from "./areas/questions/banks.js";
29
+ export { NotAForumError } from "./areas/forums/discussions.js";
30
+ export { forumDiscussions, discussion, replyToPost, startDiscussion, setDiscussionState, type ForumDiscussion, type ForumPost, type PostInput } from "./areas/forums/posts.js";
31
+ export { getSurvey, submitSurvey, SurveyError, type SurveyType, type SurveyField, type SurveyQuestion } from "./areas/surveys/surveys.js";
32
+ export { getChoice, answerChoice, ChoiceError, type Choice } from "./areas/surveys/choice.js";
33
+ export { lessonPage, answerLessonPage, LessonError, type LessonField, type LessonPage } from "./areas/lessons/lessons.js";
34
+ export { h5pActivity, NotAnH5PActivityError, type H5PAttempt, type H5PActivity } from "./areas/h5p/h5p.js";
35
+ export { scorm, openScorm, ScormError, type ScoStatus, type ScormSco, type ScormActivity, type OpenedScorm } from "./areas/scorm/scorm.js";
36
+ export { myGrades, NoGradeReportError, type GradeRow } from "./areas/grades/user-report.js";
37
+ export { graderReport, gradebookSetup, updateGradeItem, addGradeCategory, GRADE_AGGREGATIONS, GradebookError, type GradeAggregation, type GradeColumn, type GraderReport, type GradebookCategory, type GradebookItem, } from "./areas/grades/gradebook.js";
38
+ export { setCompletion, overrideCompletion, completionReport, CompletionError, type CompletionCell, type CompletionReport } from "./areas/completion/completion.js";
39
+ export { participants, participantFilters, groups, NoParticipantsError, type Participant, type CourseGroup } from "./areas/participants/participants.js";
40
+ export { upcomingEvents, createEvent, deleteEvent, CalendarError, type CalendarEvent } from "./areas/calendar/calendar.js";
41
+ export { notifications, markNotificationsRead, conversations, conversation, sendMessage, MessageError, type Notification, type ConversationSummary } from "./areas/messages/messages.js";
package/dist/index.js ADDED
@@ -0,0 +1,52 @@
1
+ // The public API of @falkenslab/moodle-driver: everything a program needs to work a Moodle site with
2
+ // the person's own session. The primitives the operations are built from are in ./internals.ts
3
+ // (`@falkenslab/moodle-driver/internals`); what neither exports is internal.
4
+ // The Moodle object, the browser and what a program chooses.
5
+ export * from "./client.js";
6
+ export * from "./options.js";
7
+ export { createBrowserManager, canShowWindow, BrowserNotFoundError, BrowserDownloadingError, NoDisplayError } from "./browser/manager.js";
8
+ export { SESSION_COOKIES_FILE } from "./browser/cookies.js";
9
+ export { siteProfileDir } from "./home.js";
10
+ export * from "./categories.js";
11
+ export * from "./core/errors.js";
12
+ // The session and the site.
13
+ export { logIn, LoginTimeoutError, LoginWindowClosedError } from "./core/login.js";
14
+ export { logOut, NotLoggedInError } from "./core/session.js";
15
+ export { MoodleServiceError } from "./core/ajax.js";
16
+ export { FormError } from "./core/forms.js";
17
+ export { findMoodleSite } from "./core/site.js";
18
+ export { checkLocalFiles, downloadFile } from "./core/files.js";
19
+ export { publicSiteInfo, courseActivityTypes } from "./areas/site/info.js";
20
+ export { userById, siteName, courseRoles, courseAbilities, whoAmI } from "./areas/site/users.js";
21
+ // Courses.
22
+ export { listCourses, updateCourse, COURSE_CLASSIFICATIONS, CourseError } from "./areas/courses/list.js";
23
+ export { courseStructure, listAssignments, pendingActivities, COMPLETION_STATES, } from "./areas/courses/structure.js";
24
+ export { activitiesOverview, deadlines, courseUpdates } from "./areas/courses/overview.js";
25
+ // Activities: building them and viewing their content.
26
+ export { createActivity, updateActivity, editActivity, duplicateActivity, deleteActivity, addSection, editSection, deleteSection, activitySettings, ACTIVITY_VISIBILITIES, ActivitySettingsError, } from "./areas/activities/build.js";
27
+ export { activityContent, CONTENT_TYPES, NotViewableError } from "./areas/activities/content.js";
28
+ // Assignments.
29
+ export { NotAnAssignmentError, GradingError } from "./areas/assignments/grader.js";
30
+ export { assignmentSubmissions, gradeSubmission, rubric, grantExtension } from "./areas/assignments/grading.js";
31
+ export { studentAssignment, saveSubmission, submitForGrading, SubmissionError } from "./areas/assignments/student.js";
32
+ // Quizzes and question banks.
33
+ export { quizInfo, startQuizAttempt, readAttempt, answerQuizQuestions, finishQuizAttempt, readReview, setQuizOverride, QuizError, } from "./areas/quizzes/attempts.js";
34
+ export { quizResults } from "./areas/quizzes/results.js";
35
+ export { quizStructure, addQuestionsToQuiz } from "./areas/quizzes/structure.js";
36
+ export { questionBanks, questions, importQuestions, createQuestion, QUESTION_TYPES, QuestionBankError, } from "./areas/questions/banks.js";
37
+ // Forums, surveys, choices, lessons, H5P.
38
+ export { NotAForumError } from "./areas/forums/discussions.js";
39
+ export { forumDiscussions, discussion, replyToPost, startDiscussion, setDiscussionState } from "./areas/forums/posts.js";
40
+ export { getSurvey, submitSurvey, SurveyError } from "./areas/surveys/surveys.js";
41
+ export { getChoice, answerChoice, ChoiceError } from "./areas/surveys/choice.js";
42
+ export { lessonPage, answerLessonPage, LessonError } from "./areas/lessons/lessons.js";
43
+ export { h5pActivity, NotAnH5PActivityError } from "./areas/h5p/h5p.js";
44
+ export { scorm, openScorm, ScormError } from "./areas/scorm/scorm.js";
45
+ // Grades, completion, participants, calendar, messages.
46
+ export { myGrades, NoGradeReportError } from "./areas/grades/user-report.js";
47
+ export { graderReport, gradebookSetup, updateGradeItem, addGradeCategory, GRADE_AGGREGATIONS, GradebookError, } from "./areas/grades/gradebook.js";
48
+ export { setCompletion, overrideCompletion, completionReport, CompletionError } from "./areas/completion/completion.js";
49
+ export { participants, participantFilters, groups, NoParticipantsError } from "./areas/participants/participants.js";
50
+ export { upcomingEvents, createEvent, deleteEvent, CalendarError } from "./areas/calendar/calendar.js";
51
+ export { notifications, markNotificationsRead, conversations, conversation, sendMessage, MessageError } from "./areas/messages/messages.js";
52
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export { callService, readPageConfig, type PageConfig } from "./core/ajax.js";
2
+ export { openSitePage, fetchSitePage, readSessionFacts, htmlSessionFacts, readMoodleConfig, isLoggedIn, pageIsLoggedIn, moodleErrorMessage, type SessionFacts } from "./core/session.js";
3
+ export { readOwnProfile } from "./core/login.js";
4
+ export { openForm, formValue, setFields, submitForm, uploadToDraft, dateSelectorValues, dateSelectorDate } from "./core/forms.js";
5
+ export { inPage } from "./core/inPage.js";
6
+ export { decodeEntities, readTexts, readMedia } from "./core/text.js";
7
+ export { readingSeconds, pause } from "./core/pace.js";
8
+ export { editCourse, renameActivity, renameSection, type EditorAction } from "./core/editor.js";
9
+ export { readWwwroot } from "./core/site.js";
10
+ export { fileNameFor } from "./core/files.js";
11
+ export { courseState, activityDetails, type CourseState, type ActivityDetails } from "./areas/courses/state.js";