canvas-lms-mcp 1.20.0 → 1.22.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.
- package/README.md +16 -6
- package/dist/canvas/index.cjs +3 -3
- package/dist/canvas/index.js +2 -2
- package/dist/{chunk-AAVMFVMI.cjs → chunk-22DWAWDC.cjs} +3 -3
- package/dist/{chunk-AAVMFVMI.cjs.map → chunk-22DWAWDC.cjs.map} +1 -1
- package/dist/{chunk-GXFYXAO5.js → chunk-BDB7U4WG.js} +2 -2
- package/dist/chunk-BDB7U4WG.js.map +1 -0
- package/dist/{chunk-Q4NKB7OZ.js → chunk-ICLQ42RW.js} +2 -2
- package/dist/{chunk-EBW2RMVA.cjs → chunk-NJOPOZP7.cjs} +2 -2
- package/dist/chunk-NJOPOZP7.cjs.map +1 -0
- package/dist/{chunk-PAQMXEJC.cjs → chunk-U4JFO6GQ.cjs} +504 -46
- package/dist/chunk-U4JFO6GQ.cjs.map +1 -0
- package/dist/{chunk-6GUSCS62.js → chunk-ZUQWZ2YH.js} +483 -25
- package/dist/chunk-ZUQWZ2YH.js.map +1 -0
- package/dist/http.cjs +5 -5
- package/dist/http.js +3 -3
- package/dist/init.cjs +3 -3
- package/dist/init.js +1 -1
- package/dist/resources/index.d.ts.map +1 -1
- package/dist/resources/ui-course-structure.d.ts +3 -0
- package/dist/resources/ui-course-structure.d.ts.map +1 -0
- package/dist/server.cjs +4 -4
- package/dist/server.js +3 -3
- package/dist/stdio.cjs +4 -4
- package/dist/stdio.js +3 -3
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/modules.d.ts.map +1 -1
- package/dist/tools/types.d.ts +10 -0
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/ui/course-structure.html.d.ts +2 -0
- package/dist/ui/course-structure.html.d.ts.map +1 -0
- package/package.json +3 -2
- package/dist/chunk-6GUSCS62.js.map +0 -1
- package/dist/chunk-EBW2RMVA.cjs.map +0 -1
- package/dist/chunk-GXFYXAO5.js.map +0 -1
- package/dist/chunk-PAQMXEJC.cjs.map +0 -1
- /package/dist/{chunk-Q4NKB7OZ.js.map → chunk-ICLQ42RW.js.map} +0 -0
package/README.md
CHANGED
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
|
|
12
12
|
MCP server for [Canvas LMS](https://www.instructure.com/canvas). Read courses, assignments, submissions, rubrics, quizzes; grade, comment, manage course content, and handle Canvas admin workflows from any AI agent.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
117 tools across Canvas courses, assignments, submissions, gradebook history, rubrics, quizzes, New Quizzes (LTI), files, users, groups, enrollments, discussions, modules, pages, calendar, conversations, peer reviews, accounts, analytics, outcomes, student workflows, dashboard, and health checks. Three deployment modes: stdio, HTTP, and library import.
|
|
15
15
|
|
|
16
16
|
## One-click install (Claude Desktop)
|
|
17
17
|
|
|
18
18
|
1. **[Download `canvas-lms-mcp.mcpb`](https://github.com/bruchris/canvas-lms-mcp/releases/latest/download/canvas-lms-mcp.mcpb)** from the latest release.
|
|
19
19
|
2. Double-click the file (or drag it into Claude Desktop's Extensions settings).
|
|
20
|
-
3. When prompted, paste your Canvas API token and Canvas API base URL (e.g. `https://school.instructure.com/api/v1`).
|
|
20
|
+
3. When prompted, paste your Canvas API token and Canvas API base URL (e.g. `https://school.instructure.com/api/v1`). Teachers and staff handling student data can also flip **FERPA mode — pseudonymize students** on in the same dialog ([what it does](#ferpa-mode-student-pseudonymization)).
|
|
21
21
|
|
|
22
22
|
No terminal, no Node.js install, no config-file editing — Claude Desktop bundles the runtime and handles config for you. The same `.mcpb` works in Claude Code and MCP for Windows.
|
|
23
23
|
|
|
@@ -28,7 +28,7 @@ Prefer the terminal? Use the [Quick Start](#quick-start) below.
|
|
|
28
28
|
| | canvas-lms-mcp | [vishalsachdev/canvas-mcp](https://github.com/vishalsachdev/canvas-mcp) | [DMontgomery40/mcp-canvas-lms](https://github.com/DMontgomery40/mcp-canvas-lms) |
|
|
29
29
|
|---|---|---|---|
|
|
30
30
|
| Language | TypeScript | Python | TypeScript |
|
|
31
|
-
| Tools |
|
|
31
|
+
| Tools | 117 | 80+ | 54 |
|
|
32
32
|
| License | [](https://github.com/bruchris/canvas-lms-mcp/blob/main/LICENSE) | [](https://github.com/vishalsachdev/canvas-mcp/blob/main/LICENSE) | [](https://github.com/DMontgomery40/mcp-canvas-lms/blob/main/LICENSE) |
|
|
33
33
|
| Last commit | [](https://github.com/bruchris/canvas-lms-mcp) | [](https://github.com/vishalsachdev/canvas-mcp) | [](https://github.com/DMontgomery40/mcp-canvas-lms) |
|
|
34
34
|
|
|
@@ -85,7 +85,7 @@ Once configured, try these prompts with your AI client:
|
|
|
85
85
|
|
|
86
86
|
## Tool Inventory
|
|
87
87
|
|
|
88
|
-
### All Registered Tools (
|
|
88
|
+
### All Registered Tools (117)
|
|
89
89
|
|
|
90
90
|
| Category | Tools |
|
|
91
91
|
|----------|-------|
|
|
@@ -102,7 +102,7 @@ Once configured, try these prompts with your AI client:
|
|
|
102
102
|
| Groups | `list_groups`, `list_group_members` |
|
|
103
103
|
| Enrollments | `list_enrollments`, `enroll_user`, `remove_enrollment` |
|
|
104
104
|
| Discussions | `list_discussions`, `get_discussion`, `list_announcements`, `post_discussion_entry`, `create_discussion`, `update_discussion`, `delete_discussion` |
|
|
105
|
-
| Modules | `list_modules`, `get_module`, `list_module_items`, `get_course_structure`, `create_module`, `update_module`, `create_module_item` |
|
|
105
|
+
| Modules | `list_modules`, `get_module`, `list_module_items`, `get_course_structure`, `view_course_structure`, `create_module`, `update_module`, `create_module_item` |
|
|
106
106
|
| Pages | `list_pages`, `get_page`, `create_page`, `update_page`, `delete_page` |
|
|
107
107
|
| Calendar | `list_calendar_events`, `create_calendar_event`, `update_calendar_event` |
|
|
108
108
|
| Conversations | `list_conversations`, `get_conversation`, `get_conversation_unread_count`, `send_conversation` |
|
|
@@ -114,7 +114,7 @@ Once configured, try these prompts with your AI client:
|
|
|
114
114
|
| Dashboard | `get_dashboard_cards`, `get_todo_items`, `get_upcoming_events`, `get_missing_submissions` |
|
|
115
115
|
| FERPA (conditional) | `resolve_pseudonym` — registered only when `CANVAS_PSEUDONYMIZE_STUDENTS=true` |
|
|
116
116
|
|
|
117
|
-
|
|
117
|
+
82 tools are read-only and 35 tools perform Canvas write operations. When FERPA mode is enabled, `resolve_pseudonym` adds a 118th read tool.
|
|
118
118
|
|
|
119
119
|
All write tools require appropriate Canvas permissions. Canvas enforces its own permission model -- the MCP server does not bypass it.
|
|
120
120
|
|
|
@@ -129,6 +129,16 @@ Canvas applies rate limits per-user. When creating many New Quizzes items (e.g.,
|
|
|
129
129
|
| Course Syllabus | `canvas://course/{courseId}/syllabus` | text/html |
|
|
130
130
|
| Assignment Description | `canvas://course/{courseId}/assignment/{assignmentId}/description` | text/html |
|
|
131
131
|
|
|
132
|
+
### Interactive widgets
|
|
133
|
+
|
|
134
|
+
`view_course_structure` is an [MCP Apps](https://github.com/modelcontextprotocol/ext-apps) tool: hosts that support the spec render an interactive tree explorer (collapsible modules, type-filter chips, title search, published/unpublished badges, links open in a new tab); hosts that don't fall back transparently to the same JSON payload that `get_course_structure` returns. The widget is self-contained — no external scripts, fonts, or network calls — and is shipped inline with the tool definition.
|
|
135
|
+
|
|
136
|
+
| Tool | UI resource URI | Fallback |
|
|
137
|
+
|------|----------------|----------|
|
|
138
|
+
| `view_course_structure` | `ui://canvas-lms-mcp/course-structure.html` | Same JSON payload as `get_course_structure` |
|
|
139
|
+
|
|
140
|
+
Host verification (Claude Desktop, ChatGPT, Codex fallback) is performed manually after each release, since it requires real Canvas credentials. A screenshot will be added once the first verified host pass lands.
|
|
141
|
+
|
|
132
142
|
## Deployment Modes
|
|
133
143
|
|
|
134
144
|
### stdio (Default)
|
package/dist/canvas/index.cjs
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var _chunk22DWAWDCcjs = require('../chunk-22DWAWDC.cjs');
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
var
|
|
9
|
+
var _chunkNJOPOZP7cjs = require('../chunk-NJOPOZP7.cjs');
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
exports.CanvasApiError =
|
|
16
|
+
exports.CanvasApiError = _chunkNJOPOZP7cjs.CanvasApiError; exports.CanvasClient = _chunk22DWAWDCcjs.CanvasClient; exports.CanvasHttpClient = _chunkNJOPOZP7cjs.CanvasHttpClient; exports.appendCanvasQuery = _chunkNJOPOZP7cjs.appendCanvasQuery; exports.toCanvasQuery = _chunkNJOPOZP7cjs.toCanvasQuery;
|
|
17
17
|
//# sourceMappingURL=index.cjs.map
|
package/dist/canvas/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CanvasClient
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-ICLQ42RW.js";
|
|
4
4
|
import {
|
|
5
5
|
CanvasApiError,
|
|
6
6
|
CanvasHttpClient,
|
|
7
7
|
appendCanvasQuery,
|
|
8
8
|
toCanvasQuery
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-BDB7U4WG.js";
|
|
10
10
|
export {
|
|
11
11
|
CanvasApiError,
|
|
12
12
|
CanvasClient,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var _chunkNJOPOZP7cjs = require('./chunk-NJOPOZP7.cjs');
|
|
4
4
|
|
|
5
5
|
// src/canvas/courses.ts
|
|
6
6
|
var DEFAULT_LIST_INCLUDE = ["term"];
|
|
@@ -1360,7 +1360,7 @@ var CanvasClient = class {
|
|
|
1360
1360
|
|
|
1361
1361
|
|
|
1362
1362
|
constructor(config) {
|
|
1363
|
-
this.client = new (0,
|
|
1363
|
+
this.client = new (0, _chunkNJOPOZP7cjs.CanvasHttpClient)(config);
|
|
1364
1364
|
this.courses = new CoursesModule(this.client);
|
|
1365
1365
|
this.assignments = new AssignmentsModule(this.client);
|
|
1366
1366
|
this.submissions = new SubmissionsModule(this.client);
|
|
@@ -1395,4 +1395,4 @@ var CanvasClient = class {
|
|
|
1395
1395
|
|
|
1396
1396
|
|
|
1397
1397
|
exports.SEARCH_CONTENT_TYPES = SEARCH_CONTENT_TYPES; exports.OUTCOME_CONTEXT_TYPES = OUTCOME_CONTEXT_TYPES; exports.OUTCOME_DETAIL_LEVELS = OUTCOME_DETAIL_LEVELS; exports.OUTCOME_ROLLUP_AGGREGATE_STATS = OUTCOME_ROLLUP_AGGREGATE_STATS; exports.OUTCOME_ROLLUP_SORT_BY = OUTCOME_ROLLUP_SORT_BY; exports.OUTCOME_SORT_ORDER = OUTCOME_SORT_ORDER; exports.OUTCOME_EXCLUDE_OPTIONS = OUTCOME_EXCLUDE_OPTIONS; exports.CanvasClient = CanvasClient;
|
|
1398
|
-
//# sourceMappingURL=chunk-
|
|
1398
|
+
//# sourceMappingURL=chunk-22DWAWDC.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/canvas-lms-mcp/canvas-lms-mcp/dist/chunk-AAVMFVMI.cjs","../src/canvas/courses.ts","../src/canvas/assignments.ts","../src/canvas/submissions.ts","../src/canvas/rubrics.ts","../src/canvas/quizzes.ts","../src/canvas/files.ts","../src/canvas/users.ts","../src/canvas/groups.ts","../src/canvas/enrollments.ts","../src/canvas/discussions.ts","../src/canvas/modules.ts","../src/canvas/pages.ts","../src/canvas/calendar.ts","../src/canvas/conversations.ts","../src/canvas/peer-reviews.ts","../src/canvas/accounts.ts","../src/canvas/analytics.ts","../src/canvas/dashboard.ts","../src/canvas/outcomes.ts","../src/canvas/gradebook-history.ts","../src/canvas/new-quizzes.ts","../src/canvas/index.ts"],"names":[],"mappings":"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A;AACL,QAAA;AACT,MAAA;AACmB,MAAA;AACrB,IAAA;AACF,EAAA;AAEkE,EAAA;AAC3C,IAAA;AACE,IAAA;AACE,IAAA;AACP,MAAA;AAClB,IAAA;AACI,IAAA;AACA,IAAA;AACwB,IAAA;AACyB,MAAA;AAChC,MAAA;AACc,IAAA;AACoB,MAAA;AAClC,MAAA;AACd,IAAA;AACsC,MAAA;AACxB,MAAA;AACrB,IAAA;AACO,IAAA;AACkB,MAAA;AACP,MAAA;AACG,MAAA;AACN,MAAA;AACC,MAAA;AACK,MAAA;AACrB,IAAA;AACF,EAAA;AACF;ArB4hC2D;AACA;AsBrxCjC;AAChB,EAAA;AACR,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AAEwC,EAAA;AACG,IAAA;AACG,IAAA;AACQ,IAAA;AACA,IAAA;AACR,IAAA;AACA,IAAA;AACJ,IAAA;AACA,IAAA;AACE,IAAA;AACU,IAAA;AACA,IAAA;AACR,IAAA;AACJ,IAAA;AACM,IAAA;AACI,IAAA;AACE,IAAA;AACN,IAAA;AACE,IAAA;AACA,IAAA;AACF,IAAA;AACK,IAAA;AACD,IAAA;AACpD,EAAA;AACF;AtBsxC2D;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/canvas-lms-mcp/canvas-lms-mcp/dist/chunk-AAVMFVMI.cjs","sourcesContent":[null,"import type { CanvasHttpClient } from './client'\nimport type { CanvasQueryParams } from './query'\nimport type { CanvasCourse, CreateCourseParams, UpdateCourseParams } from './types'\n\nexport type CourseEnrollmentState = 'active' | 'invited_or_pending' | 'completed'\n\nexport type CourseWorkflowState = 'unpublished' | 'available' | 'completed' | 'deleted'\n\nexport type CourseListInclude =\n | 'needs_grading_count'\n | 'syllabus_body'\n | 'public_description'\n | 'total_scores'\n | 'current_grading_period_scores'\n | 'grading_periods'\n | 'term'\n | 'account'\n | 'course_progress'\n | 'sections'\n | 'storage_quota_used_mb'\n | 'total_students'\n | 'passback_status'\n | 'favorites'\n | 'teachers'\n | 'observed_users'\n | 'tabs'\n | 'course_image'\n | 'banner_image'\n | 'concluded'\n | 'lti_context_id'\n | 'post_manually'\n\nexport type CourseGetInclude =\n | CourseListInclude\n | 'all_courses'\n | 'permissions'\n | 'public_description'\n\nexport interface ListCoursesOptions {\n enrollment_state?: CourseEnrollmentState\n state?: ReadonlyArray<CourseWorkflowState>\n enrollment_role_id?: number\n include?: ReadonlyArray<CourseListInclude>\n exclude_blueprint_courses?: boolean\n}\n\nexport interface GetCourseOptions {\n include?: ReadonlyArray<CourseGetInclude>\n teacher_limit?: number\n}\n\nconst DEFAULT_LIST_INCLUDE: ReadonlyArray<CourseListInclude> = ['term']\nconst DEFAULT_GET_INCLUDE: ReadonlyArray<CourseGetInclude> = ['term', 'total_students']\n\nexport class CoursesModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(opts: ListCoursesOptions = {}): Promise<CanvasCourse[]> {\n const params: CanvasQueryParams = {}\n params.include = opts.include && opts.include.length > 0 ? opts.include : DEFAULT_LIST_INCLUDE\n if (opts.enrollment_state) params.enrollment_state = opts.enrollment_state\n if (opts.state && opts.state.length > 0) params.state = opts.state\n if (opts.enrollment_role_id !== undefined) params.enrollment_role_id = opts.enrollment_role_id\n if (opts.exclude_blueprint_courses !== undefined) {\n params.exclude_blueprint_courses = opts.exclude_blueprint_courses\n }\n return this.client.paginate<CanvasCourse>('/api/v1/courses', params)\n }\n\n async get(courseId: number, opts: GetCourseOptions = {}): Promise<CanvasCourse> {\n const include = opts.include && opts.include.length > 0 ? opts.include : DEFAULT_GET_INCLUDE\n const query: CanvasQueryParams = { include }\n if (opts.teacher_limit !== undefined) query.teacher_limit = opts.teacher_limit\n return this.client.request<CanvasCourse>(`/api/v1/courses/${courseId}`, { query })\n }\n\n async getSyllabus(courseId: number): Promise<string | null> {\n const course = await this.client.request<CanvasCourse>(`/api/v1/courses/${courseId}`, {\n query: { include: ['syllabus_body'] },\n })\n return course.syllabus_body ?? null\n }\n\n async create(params: CreateCourseParams): Promise<CanvasCourse> {\n const { account_id, ...courseFields } = params\n return this.client.request<CanvasCourse>(`/api/v1/accounts/${account_id}/courses`, {\n method: 'POST',\n body: JSON.stringify({ course: courseFields }),\n })\n }\n\n async update(courseId: number, params: UpdateCourseParams): Promise<CanvasCourse> {\n return this.client.request<CanvasCourse>(`/api/v1/courses/${courseId}`, {\n method: 'PUT',\n body: JSON.stringify({ course: params }),\n })\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasQueryParams } from './query'\nimport type {\n CanvasAssignment,\n CanvasAssignmentGroup,\n CreateAssignmentParams,\n UpdateAssignmentParams,\n} from './types'\n\nexport type AssignmentListInclude =\n | 'submission'\n | 'assignment_visibility'\n | 'all_dates'\n | 'overrides'\n | 'observed_users'\n | 'can_edit'\n | 'score_statistics'\n\nexport type AssignmentGetInclude =\n | 'submission'\n | 'assignment_visibility'\n | 'overrides'\n | 'observed_users'\n | 'can_edit'\n | 'score_statistics'\n | 'all_dates'\n\nexport type AssignmentBucket =\n | 'past'\n | 'overdue'\n | 'undated'\n | 'ungraded'\n | 'unsubmitted'\n | 'upcoming'\n | 'future'\n\nexport type AssignmentOrderBy = 'position' | 'name' | 'due_at'\n\nexport type AssignmentGroupInclude =\n | 'assignments'\n | 'discussion_topic'\n | 'all_dates'\n | 'assignment_visibility'\n | 'overrides'\n | 'submission'\n | 'observed_users'\n | 'can_edit'\n | 'score_statistics'\n\nexport interface ListAssignmentsOptions {\n include?: ReadonlyArray<AssignmentListInclude>\n search_term?: string\n override_assignment_dates?: boolean\n needs_grading_count_by_section?: boolean\n bucket?: AssignmentBucket\n assignment_ids?: ReadonlyArray<number>\n order_by?: AssignmentOrderBy\n post_to_sis?: boolean\n}\n\nexport interface GetAssignmentOptions {\n include?: ReadonlyArray<AssignmentGetInclude>\n override_assignment_dates?: boolean\n needs_grading_count_by_section?: boolean\n all_dates?: boolean\n}\n\nexport interface ListAssignmentGroupsOptions {\n include?: ReadonlyArray<AssignmentGroupInclude>\n assignment_ids?: ReadonlyArray<number>\n exclude_assignment_submission_types?: ReadonlyArray<string>\n override_assignment_dates?: boolean\n grading_period_id?: number\n scope_assignments_to_student?: boolean\n}\n\nfunction toQuery(opts: object): CanvasQueryParams {\n const params: CanvasQueryParams = {}\n for (const [key, value] of Object.entries(opts)) {\n if (value === undefined || value === null) continue\n if (Array.isArray(value)) {\n if (value.length === 0) continue\n params[key] = value as ReadonlyArray<string | number | boolean>\n } else if (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ) {\n params[key] = value\n }\n }\n return params\n}\n\nexport class AssignmentsModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(courseId: number, opts: ListAssignmentsOptions = {}): Promise<CanvasAssignment[]> {\n return this.client.paginate<CanvasAssignment>(\n `/api/v1/courses/${courseId}/assignments`,\n toQuery(opts),\n )\n }\n\n async get(\n courseId: number,\n assignmentId: number,\n opts: GetAssignmentOptions = {},\n ): Promise<CanvasAssignment> {\n return this.client.request<CanvasAssignment>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}`,\n { query: toQuery(opts) },\n )\n }\n\n async listGroups(\n courseId: number,\n opts: ListAssignmentGroupsOptions = {},\n ): Promise<CanvasAssignmentGroup[]> {\n return this.client.paginate<CanvasAssignmentGroup>(\n `/api/v1/courses/${courseId}/assignment_groups`,\n toQuery(opts),\n )\n }\n\n async create(courseId: number, params: CreateAssignmentParams): Promise<CanvasAssignment> {\n return this.client.request<CanvasAssignment>(`/api/v1/courses/${courseId}/assignments`, {\n method: 'POST',\n body: JSON.stringify({ assignment: params }),\n })\n }\n\n async update(\n courseId: number,\n assignmentId: number,\n params: UpdateAssignmentParams,\n ): Promise<CanvasAssignment> {\n return this.client.request<CanvasAssignment>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}`,\n {\n method: 'PUT',\n body: JSON.stringify({ assignment: params }),\n },\n )\n }\n\n async delete(courseId: number, assignmentId: number): Promise<void> {\n await this.client.request<void>(`/api/v1/courses/${courseId}/assignments/${assignmentId}`, {\n method: 'DELETE',\n })\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasQueryParams } from './query'\nimport type { CanvasSubmission } from './types'\n\nexport type SubmissionListInclude =\n | 'submission_history'\n | 'submission_comments'\n | 'rubric_assessment'\n | 'assignment'\n | 'visibility'\n | 'course'\n | 'user'\n | 'group'\n | 'read_status'\n | 'sub_assignment_submissions'\n\nexport type SubmissionGetInclude =\n | 'submission_history'\n | 'submission_comments'\n | 'rubric_assessment'\n | 'visibility'\n | 'course'\n | 'user'\n | 'read_status'\n\nexport type SubmissionWorkflowState = 'submitted' | 'unsubmitted' | 'graded' | 'pending_review'\n\nexport interface ListSubmissionsOptions {\n include?: ReadonlyArray<SubmissionListInclude>\n student_ids?: ReadonlyArray<number | string>\n assignment_ids?: ReadonlyArray<number>\n section_ids?: ReadonlyArray<number>\n grouped?: boolean\n workflow_state?: SubmissionWorkflowState\n grading_period_id?: number\n post_to_sis?: boolean\n submitted_since?: string\n graded_since?: string\n}\n\nexport interface GetSubmissionOptions {\n include?: ReadonlyArray<SubmissionGetInclude>\n}\n\nconst DEFAULT_LIST_INCLUDE: ReadonlyArray<SubmissionListInclude> = ['submission_comments']\nconst DEFAULT_GET_INCLUDE: ReadonlyArray<SubmissionGetInclude> = ['submission_comments']\n\nfunction buildListParams(opts: ListSubmissionsOptions): CanvasQueryParams {\n const params: CanvasQueryParams = {}\n params.include = opts.include && opts.include.length > 0 ? opts.include : DEFAULT_LIST_INCLUDE\n if (opts.student_ids && opts.student_ids.length > 0) params.student_ids = opts.student_ids\n if (opts.assignment_ids && opts.assignment_ids.length > 0)\n params.assignment_ids = opts.assignment_ids\n if (opts.section_ids && opts.section_ids.length > 0) params.section_ids = opts.section_ids\n if (opts.grouped !== undefined) params.grouped = opts.grouped\n if (opts.workflow_state) params.workflow_state = opts.workflow_state\n if (opts.grading_period_id !== undefined) params.grading_period_id = opts.grading_period_id\n if (opts.post_to_sis !== undefined) params.post_to_sis = opts.post_to_sis\n if (opts.submitted_since) params.submitted_since = opts.submitted_since\n if (opts.graded_since) params.graded_since = opts.graded_since\n return params\n}\n\nexport class SubmissionsModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(\n courseId: number,\n assignmentId: number,\n opts: ListSubmissionsOptions = {},\n ): Promise<CanvasSubmission[]> {\n return this.client.paginate<CanvasSubmission>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions`,\n buildListParams(opts),\n )\n }\n\n async get(\n courseId: number,\n assignmentId: number,\n userId: number,\n opts: GetSubmissionOptions = {},\n ): Promise<CanvasSubmission> {\n const include = opts.include && opts.include.length > 0 ? opts.include : DEFAULT_GET_INCLUDE\n return this.client.request<CanvasSubmission>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions/${userId}`,\n { query: { include } },\n )\n }\n\n async grade(\n courseId: number,\n assignmentId: number,\n userId: number,\n grade: string,\n ): Promise<CanvasSubmission> {\n return this.client.request<CanvasSubmission>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions/${userId}`,\n {\n method: 'PUT',\n body: JSON.stringify({ submission: { posted_grade: grade } }),\n },\n )\n }\n\n async comment(\n courseId: number,\n assignmentId: number,\n userId: number,\n comment: string,\n ): Promise<CanvasSubmission> {\n return this.client.request<CanvasSubmission>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions/${userId}`,\n {\n method: 'PUT',\n body: JSON.stringify({\n comment: { text_comment: comment },\n }),\n },\n )\n }\n\n async listMy(courseId: number): Promise<CanvasSubmission[]> {\n return this.client.paginate<CanvasSubmission>(\n `/api/v1/courses/${courseId}/students/submissions`,\n { student_ids: ['self'] },\n )\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasRubric, CanvasRubricAssessment, CanvasSubmission } from './types'\n\nexport interface RubricRatingInput {\n description: string\n points: number\n}\n\nexport interface RubricCriterionInput {\n description: string\n points: number\n ratings: RubricRatingInput[]\n}\n\nexport interface RubricCreateInput {\n title: string\n criteria: RubricCriterionInput[]\n}\n\nexport interface RubricAssociationInput {\n assignment_id: number\n use_for_grading?: boolean\n purpose?: string\n}\n\nexport class RubricsModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(courseId: number): Promise<CanvasRubric[]> {\n return this.client.paginate<CanvasRubric>(`/api/v1/courses/${courseId}/rubrics`)\n }\n\n async get(courseId: number, rubricId: number): Promise<CanvasRubric> {\n return this.client.request<CanvasRubric>(`/api/v1/courses/${courseId}/rubrics/${rubricId}`)\n }\n\n async getAssessment(\n courseId: number,\n assignmentId: number,\n userId: number,\n ): Promise<CanvasSubmission> {\n return this.client.request<CanvasSubmission>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions/${userId}?include[]=rubric_assessment`,\n )\n }\n\n async submitAssessment(\n courseId: number,\n associationId: number,\n data: CanvasRubricAssessment['data'],\n ): Promise<CanvasRubricAssessment> {\n return this.client.request<CanvasRubricAssessment>(\n `/api/v1/courses/${courseId}/rubric_associations/${associationId}/rubric_assessments`,\n {\n method: 'POST',\n body: JSON.stringify({ rubric_assessment: { data } }),\n },\n )\n }\n\n async create(\n courseId: number,\n rubric: RubricCreateInput,\n association?: RubricAssociationInput,\n ): Promise<CanvasRubric> {\n const params = new URLSearchParams()\n params.append('rubric[title]', rubric.title)\n\n rubric.criteria.forEach((criterion, ci) => {\n params.append(`rubric[criteria][${ci}][description]`, criterion.description)\n params.append(`rubric[criteria][${ci}][points]`, String(criterion.points))\n\n // Sort ratings highest → lowest before encoding\n const sortedRatings = [...criterion.ratings].sort((a, b) => b.points - a.points)\n sortedRatings.forEach((rating, ri) => {\n params.append(`rubric[criteria][${ci}][ratings][${ri}][description]`, rating.description)\n params.append(`rubric[criteria][${ci}][ratings][${ri}][points]`, String(rating.points))\n })\n })\n\n if (association) {\n params.append('rubric_association[association_id]', String(association.assignment_id))\n params.append('rubric_association[association_type]', 'Assignment')\n if (association.use_for_grading !== undefined) {\n params.append('rubric_association[use_for_grading]', String(association.use_for_grading))\n }\n if (association.purpose) {\n params.append('rubric_association[purpose]', association.purpose)\n }\n }\n\n return this.client.request<CanvasRubric>(`/api/v1/courses/${courseId}/rubrics`, {\n method: 'POST',\n body: params.toString(),\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n })\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type {\n CanvasQuiz,\n CanvasQuizSubmission,\n CanvasQuizQuestion,\n CanvasQuizSubmissionQuestion,\n} from './types'\n\nexport class QuizzesModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(courseId: number): Promise<CanvasQuiz[]> {\n return this.client.paginate<CanvasQuiz>(`/api/v1/courses/${courseId}/quizzes`)\n }\n\n async get(courseId: number, quizId: number): Promise<CanvasQuiz> {\n return this.client.request<CanvasQuiz>(`/api/v1/courses/${courseId}/quizzes/${quizId}`)\n }\n\n async listSubmissions(courseId: number, quizId: number): Promise<CanvasQuizSubmission[]> {\n return this.client.paginateEnvelope<CanvasQuizSubmission>(\n `/api/v1/courses/${courseId}/quizzes/${quizId}/submissions`,\n 'quiz_submissions',\n )\n }\n\n async listQuestions(courseId: number, quizId: number): Promise<CanvasQuizQuestion[]> {\n return this.client.paginate<CanvasQuizQuestion>(\n `/api/v1/courses/${courseId}/quizzes/${quizId}/questions`,\n )\n }\n\n async getSubmissionAnswers(quizSubmissionId: number): Promise<CanvasQuizSubmissionQuestion[]> {\n return this.client.paginateEnvelope<CanvasQuizSubmissionQuestion>(\n `/api/v1/quiz_submissions/${quizSubmissionId}/questions`,\n 'quiz_submission_questions',\n { 'include[]': 'quiz_question' },\n )\n }\n\n async scoreQuestion(\n courseId: number,\n quizId: number,\n submissionId: number,\n questionId: number,\n score: number,\n comment?: string,\n attempt?: number,\n ): Promise<void> {\n const submission: Record<string, unknown> = {\n questions: {\n [questionId]: { score, comment },\n },\n }\n if (attempt !== undefined) {\n submission.attempt = attempt\n }\n const body: Record<string, unknown> = {\n quiz_submissions: [submission],\n }\n await this.client.request(\n `/api/v1/courses/${courseId}/quizzes/${quizId}/submissions/${submissionId}`,\n {\n method: 'PUT',\n body: JSON.stringify(body),\n },\n )\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasFile, CanvasFileUploadInfo, CanvasFolder, DownloadedFile } from './types'\n\nconst MAX_DOWNLOAD_BYTES = 10 * 1024 * 1024 // 10 MB\n\nconst TEXT_CONTENT_TYPES = new Set([\n 'application/json',\n 'application/xml',\n 'application/javascript',\n 'application/x-javascript',\n])\n\nfunction isTextContentType(contentType: string): boolean {\n const base = (contentType.split(';')[0] ?? '').trim().toLowerCase()\n return base.startsWith('text/') || TEXT_CONTENT_TYPES.has(base)\n}\n\nexport class FilesModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(courseId: number): Promise<CanvasFile[]> {\n return this.client.paginate<CanvasFile>(`/api/v1/courses/${courseId}/files`)\n }\n\n async listFolders(courseId: number): Promise<CanvasFolder[]> {\n return this.client.paginate<CanvasFolder>(`/api/v1/courses/${courseId}/folders`)\n }\n\n async get(courseId: number, fileId: number): Promise<CanvasFile> {\n return this.client.request<CanvasFile>(`/api/v1/courses/${courseId}/files/${fileId}`)\n }\n\n async upload(\n courseId: number,\n name: string,\n contentBase64: string,\n contentType: string,\n parentFolderPath?: string,\n ): Promise<CanvasFile> {\n const content = Buffer.from(contentBase64, 'base64')\n if (content.toString('base64') !== contentBase64) {\n throw new Error('Invalid base64 content: string is not valid base64 encoding')\n }\n\n // Step 1: Notify Canvas of the pending upload\n const uploadBody: Record<string, string> = {\n name,\n content_type: contentType,\n size: String(content.length),\n }\n if (parentFolderPath) {\n uploadBody.parent_folder_path = parentFolderPath\n }\n\n const uploadInfo = await this.client.request<CanvasFileUploadInfo>(\n `/api/v1/courses/${courseId}/files`,\n { method: 'POST', body: JSON.stringify(uploadBody) },\n )\n\n // Step 2: POST file to upload_url (may be S3 — must NOT add Authorization header)\n const form = new FormData()\n for (const [key, value] of Object.entries(uploadInfo.upload_params)) {\n form.append(key, value)\n }\n form.append('file', new Blob([content], { type: contentType }), name)\n\n // Use redirect: 'manual' so we can handle the 303 → Canvas confirm step ourselves\n let s3Response: Response\n try {\n s3Response = await fetch(uploadInfo.upload_url, {\n method: 'POST',\n body: form,\n redirect: 'manual',\n })\n } catch (networkErr) {\n let hostname: string\n try {\n hostname = new URL(uploadInfo.upload_url).hostname\n } catch {\n hostname = uploadInfo.upload_url\n }\n throw new Error(`Unable to reach file storage (${hostname})`, { cause: networkErr })\n }\n\n // Step 3: If S3 returned a redirect, POST to Canvas confirm URL\n if (s3Response.status >= 300 && s3Response.status < 400) {\n const confirmUrl = s3Response.headers.get('location')\n if (!confirmUrl) {\n throw new Error(`File upload redirect missing Location header (HTTP ${s3Response.status})`)\n }\n let confirmHostname: string\n try {\n confirmHostname = new URL(confirmUrl).hostname\n } catch {\n confirmHostname = ''\n }\n const canvasHostname = new URL(this.client.baseUrl).hostname\n if (confirmHostname !== canvasHostname) {\n throw new Error(\n `File upload redirect points to unexpected host (${confirmHostname}); expected Canvas host (${canvasHostname})`,\n )\n }\n return this.client.request<CanvasFile>(confirmUrl, { method: 'POST' })\n }\n\n if (!s3Response.ok) {\n const body = await s3Response.text()\n throw new Error(\n `File upload to storage failed (HTTP ${s3Response.status}): ${body.slice(0, 500)}`,\n )\n }\n\n const responseText = await s3Response.text()\n let parsed: unknown\n try {\n parsed = JSON.parse(responseText)\n } catch (parseErr) {\n throw new Error(\n `File upload response is not valid JSON (${parseErr instanceof Error ? parseErr.message : String(parseErr)}): ${responseText.slice(0, 500)}`,\n { cause: parseErr },\n )\n }\n if (typeof parsed !== 'object' || parsed === null || !('id' in parsed)) {\n throw new Error(\n `File upload returned unexpected response (no file ID): ${responseText.slice(0, 500)}`,\n )\n }\n return parsed as CanvasFile\n }\n\n async download(fileId: number, courseId?: number): Promise<DownloadedFile> {\n const endpoint =\n courseId != null ? `/api/v1/courses/${courseId}/files/${fileId}` : `/api/v1/files/${fileId}`\n const meta = await this.client.request<CanvasFile>(endpoint)\n\n if (meta.size > MAX_DOWNLOAD_BYTES) {\n throw new Error(\n `File too large to download (${meta.size.toLocaleString()} bytes; limit is 10 MB)`,\n )\n }\n\n const response = await fetch(meta.url)\n if (!response.ok) {\n throw new Error(`Failed to download file content (HTTP ${response.status})`)\n }\n\n const contentLengthHeader = response.headers.get('content-length')\n if (contentLengthHeader != null) {\n const declared = parseInt(contentLengthHeader, 10)\n if (!isNaN(declared) && declared > MAX_DOWNLOAD_BYTES) {\n throw new Error(\n `File too large to download (${declared.toLocaleString()} bytes declared; limit is 10 MB)`,\n )\n }\n }\n\n const contentType = (\n (response.headers.get('content-type') ?? meta.content_type).split(';')[0] ?? ''\n ).trim()\n const filename = meta.display_name ?? meta.filename ?? `file-${fileId}`\n const buffer = await response.arrayBuffer()\n\n if (buffer.byteLength > MAX_DOWNLOAD_BYTES) {\n throw new Error(\n `File too large to download (${buffer.byteLength.toLocaleString()} bytes; limit is 10 MB)`,\n )\n }\n\n if (isTextContentType(contentType)) {\n return {\n type: 'text',\n filename,\n contentType,\n size: buffer.byteLength,\n text: Buffer.from(buffer).toString('utf-8'),\n }\n }\n\n return {\n type: 'resource',\n filename,\n contentType,\n size: buffer.byteLength,\n base64: Buffer.from(buffer).toString('base64'),\n }\n }\n\n async delete(fileId: number): Promise<CanvasFile> {\n return this.client.request<CanvasFile>(`/api/v1/files/${fileId}`, { method: 'DELETE' })\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasQueryParams } from './query'\nimport type { CanvasUpcomingEvent, CanvasUser, CanvasUserProfile } from './types'\n\nexport type CourseUserEnrollmentType = 'student' | 'teacher' | 'ta' | 'observer' | 'designer'\n\nexport type CourseUserEnrollmentState = 'active' | 'invited' | 'rejected' | 'completed' | 'inactive'\n\nexport type CourseUserInclude =\n | 'email'\n | 'enrollments'\n | 'locked'\n | 'avatar_url'\n | 'test_student'\n | 'bio'\n | 'custom_links'\n | 'current_grading_period_scores'\n | 'uuid'\n\nexport type UserSort = 'username' | 'email' | 'sis_id' | 'integration_id' | 'last_login'\n\nexport type SearchUserInclude = 'email' | 'last_login' | 'avatar_url' | 'time_zone' | 'uuid'\n\nexport interface ListCourseUsersOptions {\n enrollment_type?: CourseUserEnrollmentType | ReadonlyArray<CourseUserEnrollmentType>\n enrollment_state?: ReadonlyArray<CourseUserEnrollmentState>\n enrollment_role_id?: number\n include?: ReadonlyArray<CourseUserInclude>\n user_ids?: ReadonlyArray<number | string>\n user_id?: number | string\n search_term?: string\n sort?: UserSort\n order?: 'asc' | 'desc'\n}\n\nexport interface SearchUsersOptions {\n sort?: UserSort\n order?: 'asc' | 'desc'\n include?: ReadonlyArray<SearchUserInclude>\n}\n\nexport class UsersModule {\n constructor(private client: CanvasHttpClient) {}\n\n async listStudents(courseId: number): Promise<CanvasUser[]> {\n return this.client.paginate<CanvasUser>(`/api/v1/courses/${courseId}/users`, {\n enrollment_type: ['student'],\n })\n }\n\n async get(userId: number): Promise<CanvasUser> {\n return this.client.request<CanvasUser>(`/api/v1/users/${userId}`)\n }\n\n async getProfile(): Promise<CanvasUserProfile> {\n return this.client.request<CanvasUserProfile>('/api/v1/users/self/profile')\n }\n\n async searchUsers(\n accountId: number,\n searchTerm: string,\n opts: SearchUsersOptions = {},\n ): Promise<CanvasUser[]> {\n const params: CanvasQueryParams = { search_term: searchTerm }\n if (opts.sort) params.sort = opts.sort\n if (opts.order) params.order = opts.order\n if (opts.include && opts.include.length > 0) params.include = opts.include\n return this.client.paginate<CanvasUser>(`/api/v1/accounts/${accountId}/users`, params)\n }\n\n async listCourseUsers(\n courseId: number,\n opts: ListCourseUsersOptions = {},\n ): Promise<CanvasUser[]> {\n const params: CanvasQueryParams = {}\n if (opts.enrollment_type !== undefined) {\n params.enrollment_type = Array.isArray(opts.enrollment_type)\n ? opts.enrollment_type\n : [opts.enrollment_type]\n }\n if (opts.enrollment_state && opts.enrollment_state.length > 0) {\n params.enrollment_state = opts.enrollment_state\n }\n if (opts.enrollment_role_id !== undefined) params.enrollment_role_id = opts.enrollment_role_id\n if (opts.include && opts.include.length > 0) params.include = opts.include\n if (opts.user_ids && opts.user_ids.length > 0) params.user_ids = opts.user_ids\n if (opts.user_id !== undefined) params.user_id = opts.user_id\n if (opts.search_term) params.search_term = opts.search_term\n if (opts.sort) params.sort = opts.sort\n if (opts.order) params.order = opts.order\n return this.client.paginate<CanvasUser>(`/api/v1/courses/${courseId}/users`, params)\n }\n\n async getUpcomingAssignments(): Promise<CanvasUpcomingEvent[]> {\n return this.client.request<CanvasUpcomingEvent[]>(\n '/api/v1/users/self/upcoming_events?type=Assignment',\n )\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasGroup, CanvasUser } from './types'\n\nexport class GroupsModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(courseId: number): Promise<CanvasGroup[]> {\n return this.client.paginate<CanvasGroup>(`/api/v1/courses/${courseId}/groups`)\n }\n\n async listMembers(groupId: number): Promise<CanvasUser[]> {\n return this.client.paginate<CanvasUser>(`/api/v1/groups/${groupId}/users`)\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasQueryParams } from './query'\nimport type { CanvasEnrollment } from './types'\n\nexport type EnrollmentType =\n | 'StudentEnrollment'\n | 'TeacherEnrollment'\n | 'TaEnrollment'\n | 'DesignerEnrollment'\n | 'ObserverEnrollment'\n\nexport type EnrollmentState =\n | 'active'\n | 'invited'\n | 'creation_pending'\n | 'deleted'\n | 'rejected'\n | 'completed'\n | 'inactive'\n | 'current_and_invited'\n | 'current_and_future'\n | 'current_and_concluded'\n\nexport type EnrollmentInclude =\n | 'avatar_url'\n | 'group_ids'\n | 'locked'\n | 'observed_users'\n | 'can_be_removed'\n | 'uuid'\n | 'current_points'\n | 'grades'\n\nexport interface ListCourseEnrollmentsOptions {\n type?: ReadonlyArray<EnrollmentType>\n role?: ReadonlyArray<string>\n state?: ReadonlyArray<EnrollmentState>\n include?: ReadonlyArray<EnrollmentInclude>\n user_id?: number | string\n grading_period_id?: number\n enrollment_term_id?: number\n sis_account_id?: ReadonlyArray<string>\n sis_course_id?: ReadonlyArray<string>\n sis_section_id?: ReadonlyArray<string>\n sis_user_id?: ReadonlyArray<string>\n created_for_sis_id?: ReadonlyArray<boolean>\n}\n\nexport interface ListUserEnrollmentsOptions {\n type?: ReadonlyArray<EnrollmentType>\n role?: ReadonlyArray<string>\n state?: ReadonlyArray<EnrollmentState>\n include?: ReadonlyArray<EnrollmentInclude>\n grading_period_id?: number\n enrollment_term_id?: number\n}\n\nfunction buildParams(opts: object): CanvasQueryParams {\n const params: CanvasQueryParams = {}\n for (const [key, value] of Object.entries(opts)) {\n if (value === undefined || value === null) continue\n if (Array.isArray(value)) {\n if (value.length === 0) continue\n params[key] = value as ReadonlyArray<string | number | boolean>\n } else if (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ) {\n params[key] = value\n }\n }\n return params\n}\n\nexport class EnrollmentsModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(opts: ListUserEnrollmentsOptions = {}): Promise<CanvasEnrollment[]> {\n return this.client.paginate<CanvasEnrollment>(\n '/api/v1/users/self/enrollments',\n buildParams(opts),\n )\n }\n\n async listForCourse(\n courseId: number,\n opts: ListCourseEnrollmentsOptions = {},\n ): Promise<CanvasEnrollment[]> {\n return this.client.paginate<CanvasEnrollment>(\n `/api/v1/courses/${courseId}/enrollments`,\n buildParams(opts),\n )\n }\n\n async enroll(\n courseId: number,\n userId: number,\n type: string,\n enrollmentState?: string,\n ): Promise<CanvasEnrollment> {\n const body: Record<string, unknown> = {\n enrollment: {\n user_id: userId,\n type,\n ...(enrollmentState ? { enrollment_state: enrollmentState } : {}),\n },\n }\n return this.client.request<CanvasEnrollment>(`/api/v1/courses/${courseId}/enrollments`, {\n method: 'POST',\n body: JSON.stringify(body),\n })\n }\n\n async remove(courseId: number, enrollmentId: number, task: string): Promise<CanvasEnrollment> {\n return this.client.request<CanvasEnrollment>(\n `/api/v1/courses/${courseId}/enrollments/${enrollmentId}?task=${encodeURIComponent(task)}`,\n { method: 'DELETE' },\n )\n }\n\n async listMyGrades(courseId?: number): Promise<CanvasEnrollment[]> {\n if (courseId !== undefined) {\n return this.client.paginate<CanvasEnrollment>(`/api/v1/courses/${courseId}/enrollments`, {\n user_id: 'self',\n include: ['grades'],\n })\n }\n return this.client.paginate<CanvasEnrollment>('/api/v1/users/self/enrollments', {\n include: ['grades'],\n })\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type {\n CanvasDiscussionTopic,\n CanvasDiscussionEntry,\n CanvasAnnouncement,\n CreateDiscussionParams,\n UpdateDiscussionParams,\n} from './types'\n\nexport class DiscussionsModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(courseId: number): Promise<CanvasDiscussionTopic[]> {\n return this.client.paginate<CanvasDiscussionTopic>(\n `/api/v1/courses/${courseId}/discussion_topics`,\n )\n }\n\n async get(courseId: number, topicId: number): Promise<CanvasDiscussionTopic> {\n return this.client.request<CanvasDiscussionTopic>(\n `/api/v1/courses/${courseId}/discussion_topics/${topicId}?include[]=all_dates`,\n )\n }\n\n async listAnnouncements(courseId: number): Promise<CanvasAnnouncement[]> {\n return this.client.paginate<CanvasAnnouncement>(\n `/api/v1/courses/${courseId}/discussion_topics`,\n { only_announcements: 'true' },\n )\n }\n\n async postEntry(\n courseId: number,\n topicId: number,\n message: string,\n ): Promise<CanvasDiscussionEntry> {\n return this.client.request<CanvasDiscussionEntry>(\n `/api/v1/courses/${courseId}/discussion_topics/${topicId}/entries`,\n {\n method: 'POST',\n body: JSON.stringify({ message }),\n },\n )\n }\n\n async create(courseId: number, params: CreateDiscussionParams): Promise<CanvasDiscussionTopic> {\n return this.client.request<CanvasDiscussionTopic>(\n `/api/v1/courses/${courseId}/discussion_topics`,\n {\n method: 'POST',\n body: JSON.stringify(params),\n },\n )\n }\n\n async update(\n courseId: number,\n topicId: number,\n params: UpdateDiscussionParams,\n ): Promise<CanvasDiscussionTopic> {\n return this.client.request<CanvasDiscussionTopic>(\n `/api/v1/courses/${courseId}/discussion_topics/${topicId}`,\n {\n method: 'PUT',\n body: JSON.stringify(params),\n },\n )\n }\n\n async delete(courseId: number, topicId: number): Promise<void> {\n await this.client.request<void>(`/api/v1/courses/${courseId}/discussion_topics/${topicId}`, {\n method: 'DELETE',\n })\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasModule, CanvasModuleItem, CanvasCourseStructure } from './types'\n\nexport class ModulesModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(courseId: number): Promise<CanvasModule[]> {\n return this.client.paginate<CanvasModule>(`/api/v1/courses/${courseId}/modules`)\n }\n\n async get(courseId: number, moduleId: number): Promise<CanvasModule> {\n return this.client.request<CanvasModule>(`/api/v1/courses/${courseId}/modules/${moduleId}`)\n }\n\n async listItems(courseId: number, moduleId: number): Promise<CanvasModuleItem[]> {\n return this.client.paginate<CanvasModuleItem>(\n `/api/v1/courses/${courseId}/modules/${moduleId}/items`,\n )\n }\n\n async create(\n courseId: number,\n params: {\n name: string\n position?: number\n unlock_at?: string\n prerequisite_module_ids?: number[]\n },\n ): Promise<CanvasModule> {\n return this.client.request<CanvasModule>(`/api/v1/courses/${courseId}/modules`, {\n method: 'POST',\n body: JSON.stringify({ module: params }),\n })\n }\n\n async update(\n courseId: number,\n moduleId: number,\n params: { name?: string; position?: number; published?: boolean },\n ): Promise<CanvasModule> {\n return this.client.request<CanvasModule>(`/api/v1/courses/${courseId}/modules/${moduleId}`, {\n method: 'PUT',\n body: JSON.stringify({ module: params }),\n })\n }\n\n async createItem(\n courseId: number,\n moduleId: number,\n params: {\n title: string\n type: string\n content_id?: number\n external_url?: string\n position?: number\n },\n ): Promise<CanvasModuleItem> {\n return this.client.request<CanvasModuleItem>(\n `/api/v1/courses/${courseId}/modules/${moduleId}/items`,\n {\n method: 'POST',\n body: JSON.stringify({ module_item: params }),\n },\n )\n }\n\n async getCourseStructure(\n courseId: number,\n opts: { includePublishedOnly?: boolean; includeContentDetails?: boolean } = {},\n ): Promise<CanvasCourseStructure> {\n const include: string[] = ['items']\n if (opts.includeContentDetails) include.push('content_details')\n\n const modules = await this.client.paginate<CanvasModule & { items?: CanvasModuleItem[] }>(\n `/api/v1/courses/${courseId}/modules`,\n { include },\n )\n\n const itemsByType: Record<string, number> = {}\n let totalItems = 0\n\n const filteredModules = modules.map((mod) => {\n let items = mod.items ?? []\n if (opts.includePublishedOnly) {\n items = items.filter((item) => item.published)\n }\n for (const item of items) {\n itemsByType[item.type] = (itemsByType[item.type] ?? 0) + 1\n totalItems++\n }\n return {\n id: mod.id,\n name: mod.name,\n position: mod.position,\n state: mod.state ?? (mod.published ? 'active' : 'unpublished'),\n unlock_at: mod.unlock_at,\n items: items.map((item) => ({\n id: item.id,\n title: item.title,\n type: item.type,\n position: item.position,\n published: item.published,\n html_url: item.html_url,\n page_url: item.page_url,\n content_id: item.content_id,\n content_details: item.content_details,\n })),\n }\n })\n\n return {\n modules: filteredModules,\n summary: {\n total_modules: filteredModules.length,\n total_items: totalItems,\n items_by_type: itemsByType,\n },\n }\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasPage } from './types'\n\nexport class PagesModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(courseId: number): Promise<CanvasPage[]> {\n return this.client.paginate<CanvasPage>(`/api/v1/courses/${courseId}/pages`)\n }\n\n async get(courseId: number, pageUrl: string): Promise<CanvasPage> {\n return this.client.request<CanvasPage>(\n `/api/v1/courses/${courseId}/pages/${encodeURIComponent(pageUrl)}`,\n )\n }\n\n async create(\n courseId: number,\n params: { title: string; body?: string; published?: boolean; editing_roles?: string },\n ): Promise<CanvasPage> {\n return this.client.request<CanvasPage>(`/api/v1/courses/${courseId}/pages`, {\n method: 'POST',\n body: JSON.stringify({ wiki_page: params }),\n })\n }\n\n async update(\n courseId: number,\n pageUrl: string,\n params: { title?: string; body?: string; published?: boolean; editing_roles?: string },\n ): Promise<CanvasPage> {\n return this.client.request<CanvasPage>(\n `/api/v1/courses/${courseId}/pages/${encodeURIComponent(pageUrl)}`,\n {\n method: 'PUT',\n body: JSON.stringify({ wiki_page: params }),\n },\n )\n }\n\n async delete(courseId: number, pageUrl: string): Promise<void> {\n await this.client.request<void>(\n `/api/v1/courses/${courseId}/pages/${encodeURIComponent(pageUrl)}`,\n { method: 'DELETE' },\n )\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasCalendarEvent } from './types'\n\nexport class CalendarModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(courseId: number): Promise<CanvasCalendarEvent[]> {\n return this.client.paginate<CanvasCalendarEvent>('/api/v1/calendar_events', {\n 'context_codes[]': `course_${courseId}`,\n })\n }\n\n async createEvent(params: {\n context_code: string\n title: string\n start_at: string\n end_at?: string\n description?: string\n location_name?: string\n }): Promise<CanvasCalendarEvent> {\n return this.client.request<CanvasCalendarEvent>('/api/v1/calendar_events', {\n method: 'POST',\n body: JSON.stringify({ calendar_event: params }),\n })\n }\n\n async updateEvent(\n eventId: number,\n params: {\n title?: string\n start_at?: string\n end_at?: string\n description?: string\n location_name?: string\n },\n ): Promise<CanvasCalendarEvent> {\n return this.client.request<CanvasCalendarEvent>(`/api/v1/calendar_events/${eventId}`, {\n method: 'PUT',\n body: JSON.stringify({ calendar_event: params }),\n })\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type {\n CanvasConversation,\n CanvasConversationDetail,\n CanvasConversationUnreadCount,\n} from './types'\n\nexport class ConversationsModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(): Promise<CanvasConversation[]> {\n return this.client.paginate<CanvasConversation>('/api/v1/conversations')\n }\n\n async get(conversationId: number): Promise<CanvasConversationDetail> {\n return this.client.request<CanvasConversationDetail>(`/api/v1/conversations/${conversationId}`)\n }\n\n async getUnreadCount(): Promise<CanvasConversationUnreadCount> {\n const raw = await this.client.request<{ unread_count: string }>(\n '/api/v1/conversations/unread_count',\n )\n const count = parseInt(raw.unread_count, 10)\n if (Number.isNaN(count)) {\n throw new Error(\n `Canvas returned unexpected unread_count value: ${JSON.stringify(raw.unread_count)}`,\n )\n }\n return { unread_count: count }\n }\n\n async send(recipients: string[], subject: string, body: string): Promise<CanvasConversation[]> {\n return this.client.request<CanvasConversation[]>('/api/v1/conversations', {\n method: 'POST',\n body: JSON.stringify({\n recipients,\n subject,\n body,\n }),\n })\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasPeerReview } from './types'\n\nexport class PeerReviewsModule {\n constructor(private client: CanvasHttpClient) {}\n\n async listForAssignment(courseId: number, assignmentId: number): Promise<CanvasPeerReview[]> {\n return this.client.paginate<CanvasPeerReview>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}/peer_reviews`,\n )\n }\n\n async listForSubmission(\n courseId: number,\n assignmentId: number,\n submissionId: number,\n ): Promise<CanvasPeerReview[]> {\n return this.client.paginate<CanvasPeerReview>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions/${submissionId}/peer_reviews`,\n )\n }\n\n async create(\n courseId: number,\n assignmentId: number,\n submissionId: number,\n userId: number,\n ): Promise<CanvasPeerReview> {\n return this.client.request<CanvasPeerReview>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions/${submissionId}/peer_reviews`,\n {\n method: 'POST',\n body: JSON.stringify({ user_id: userId }),\n },\n )\n }\n\n async delete(\n courseId: number,\n assignmentId: number,\n submissionId: number,\n userId: number,\n ): Promise<void> {\n await this.client.request<void>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions/${submissionId}/peer_reviews?user_id=${userId}`,\n { method: 'DELETE' },\n )\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasAccount, CanvasAccountReport, CanvasCourse, CanvasUser } from './types'\n\nexport class AccountsModule {\n constructor(private client: CanvasHttpClient) {}\n\n async get(accountId: number): Promise<CanvasAccount> {\n return this.client.request<CanvasAccount>(`/api/v1/accounts/${accountId}`)\n }\n\n async list(): Promise<CanvasAccount[]> {\n return this.client.paginate<CanvasAccount>('/api/v1/accounts')\n }\n\n async listSubAccounts(accountId: number): Promise<CanvasAccount[]> {\n return this.client.paginate<CanvasAccount>(`/api/v1/accounts/${accountId}/sub_accounts`)\n }\n\n async listCourses(accountId: number, params?: { search_term?: string }): Promise<CanvasCourse[]> {\n const query: Record<string, string> = {}\n if (params?.search_term) query.search_term = params.search_term\n return this.client.paginate<CanvasCourse>(\n `/api/v1/accounts/${accountId}/courses`,\n Object.keys(query).length ? query : undefined,\n )\n }\n\n async listUsers(accountId: number, params?: { search_term?: string }): Promise<CanvasUser[]> {\n const query: Record<string, string> = {}\n if (params?.search_term) query.search_term = params.search_term\n return this.client.paginate<CanvasUser>(\n `/api/v1/accounts/${accountId}/users`,\n Object.keys(query).length ? query : undefined,\n )\n }\n\n async getReports(accountId: number): Promise<CanvasAccountReport[]> {\n return this.client.request<CanvasAccountReport[]>(`/api/v1/accounts/${accountId}/reports`)\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type {\n CanvasCourseActivitySummary,\n CanvasStudentActivitySummary,\n CanvasActivityStreamItem,\n CourseSearchResult,\n} from './types'\n\nexport const SEARCH_CONTENT_TYPES = [\n 'pages',\n 'discussions',\n 'assignments',\n 'announcements',\n] as const\nexport type SearchContentType = (typeof SEARCH_CONTENT_TYPES)[number]\n\nexport class AnalyticsModule {\n constructor(private client: CanvasHttpClient) {}\n\n async searchContentType(\n courseId: number,\n searchTerm: string,\n type: SearchContentType,\n ): Promise<CourseSearchResult[]> {\n switch (type) {\n case 'pages': {\n const pages = await this.client.paginate<{ page_id: number; title: string; url: string }>(\n `/api/v1/courses/${courseId}/pages`,\n { search_term: searchTerm },\n )\n return pages.map((p) => ({\n id: p.page_id,\n title: p.title,\n type: 'page' as const,\n url: p.url,\n course_id: courseId,\n }))\n }\n case 'assignments': {\n const assignments = await this.client.paginate<{ id: number; name: string }>(\n `/api/v1/courses/${courseId}/assignments`,\n { search_term: searchTerm },\n )\n return assignments.map((a) => ({\n id: a.id,\n title: a.name,\n type: 'assignment' as const,\n course_id: courseId,\n }))\n }\n case 'discussions': {\n const discussions = await this.client.paginate<{ id: number; title: string }>(\n `/api/v1/courses/${courseId}/discussion_topics`,\n { search_term: searchTerm },\n )\n return discussions.map((d) => ({\n id: d.id,\n title: d.title,\n type: 'discussion' as const,\n course_id: courseId,\n }))\n }\n case 'announcements': {\n const announcements = await this.client.paginate<{ id: number; title: string }>(\n `/api/v1/courses/${courseId}/discussion_topics`,\n { search_term: searchTerm, only_announcements: 'true' },\n )\n return announcements.map((a) => ({\n id: a.id,\n title: a.title,\n type: 'announcement' as const,\n course_id: courseId,\n }))\n }\n default: {\n const _exhaustive: never = type\n throw new Error(`Unknown content type: ${_exhaustive}`)\n }\n }\n }\n\n async getCourseActivity(courseId: number): Promise<CanvasCourseActivitySummary[]> {\n return this.client.request<CanvasCourseActivitySummary[]>(\n `/api/v1/courses/${courseId}/analytics/activity`,\n )\n }\n\n async getStudentActivity(\n courseId: number,\n studentId: number,\n ): Promise<CanvasStudentActivitySummary> {\n return this.client.request<CanvasStudentActivitySummary>(\n `/api/v1/courses/${courseId}/analytics/users/${studentId}/activity`,\n )\n }\n\n async getCourseActivityStream(courseId: number): Promise<CanvasActivityStreamItem[]> {\n return this.client.request<CanvasActivityStreamItem[]>(\n `/api/v1/courses/${courseId}/activity_stream/summary`,\n )\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type {\n CanvasDashboardCard,\n CanvasTodoItem,\n CanvasUpcomingEvent,\n CanvasMissingSubmission,\n} from './types'\n\nexport class DashboardModule {\n constructor(private client: CanvasHttpClient) {}\n\n async getDashboardCards(): Promise<CanvasDashboardCard[]> {\n return this.client.request<CanvasDashboardCard[]>('/api/v1/dashboard/dashboard_cards')\n }\n\n async getTodoItems(): Promise<CanvasTodoItem[]> {\n return this.client.paginate<CanvasTodoItem>('/api/v1/users/self/todo')\n }\n\n async getUpcomingEvents(): Promise<CanvasUpcomingEvent[]> {\n return this.client.request<CanvasUpcomingEvent[]>('/api/v1/users/self/upcoming_events')\n }\n\n async getMissingSubmissions(): Promise<CanvasMissingSubmission[]> {\n return this.client.paginate<CanvasMissingSubmission>('/api/v1/users/self/missing_submissions')\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type {\n CanvasOutcomeContextType,\n CanvasOutcome,\n CanvasOutcomeAlignment,\n CanvasOutcomeContributingScoresResponse,\n CanvasOutcomeGroup,\n CanvasOutcomeLink,\n CanvasOutcomeMasteryDistributionResponse,\n CanvasOutcomeResultsResponse,\n CanvasOutcomeRollupsResponse,\n} from './types'\n\nexport const OUTCOME_CONTEXT_TYPES = ['account', 'course'] as const\nexport const OUTCOME_DETAIL_LEVELS = ['abbrev', 'full'] as const\nexport const OUTCOME_ROLLUP_AGGREGATE_STATS = ['mean', 'median'] as const\nexport const OUTCOME_ROLLUP_SORT_BY = ['student', 'outcome'] as const\nexport const OUTCOME_SORT_ORDER = ['asc', 'desc'] as const\nexport const OUTCOME_EXCLUDE_OPTIONS = ['missing_user_rollups', 'missing_outcome_results'] as const\n\ntype ContextPathSuffix = 'outcome_groups' | 'outcome_group_links' | 'root_outcome_group'\n\nexport class OutcomesModule {\n constructor(private client: CanvasHttpClient) {}\n\n async getRootOutcomeGroup(\n contextType: CanvasOutcomeContextType,\n contextId: number,\n ): Promise<CanvasOutcomeGroup> {\n return this.client.request<CanvasOutcomeGroup>(this.buildContextPath(contextType, contextId))\n }\n\n async listOutcomeGroups(\n contextType: CanvasOutcomeContextType,\n contextId: number,\n ): Promise<CanvasOutcomeGroup[]> {\n return this.client.paginate<CanvasOutcomeGroup>(\n this.buildContextPath(contextType, contextId, 'outcome_groups'),\n )\n }\n\n async listOutcomeGroupLinks(\n contextType: CanvasOutcomeContextType,\n contextId: number,\n options?: { outcome_style?: 'abbrev' | 'full'; outcome_group_style?: 'abbrev' | 'full' },\n ): Promise<CanvasOutcomeLink[]> {\n return this.client.paginate<CanvasOutcomeLink>(\n this.withQuery(this.buildContextPath(contextType, contextId, 'outcome_group_links'), {\n outcome_style: options?.outcome_style,\n outcome_group_style: options?.outcome_group_style,\n }),\n )\n }\n\n async getOutcomeGroup(\n contextType: CanvasOutcomeContextType,\n contextId: number,\n groupId: number,\n ): Promise<CanvasOutcomeGroup> {\n return this.client.request<CanvasOutcomeGroup>(\n `${this.buildContextPath(contextType, contextId, 'outcome_groups')}/${groupId}`,\n )\n }\n\n async listGroupOutcomes(\n contextType: CanvasOutcomeContextType,\n contextId: number,\n groupId: number,\n options?: { outcome_style?: 'abbrev' | 'full' },\n ): Promise<CanvasOutcomeLink[]> {\n return this.client.paginate<CanvasOutcomeLink>(\n this.withQuery(\n `${this.buildContextPath(contextType, contextId, 'outcome_groups')}/${groupId}/outcomes`,\n { outcome_style: options?.outcome_style },\n ),\n )\n }\n\n async listGroupSubgroups(\n contextType: CanvasOutcomeContextType,\n contextId: number,\n groupId: number,\n ): Promise<CanvasOutcomeGroup[]> {\n return this.client.paginate<CanvasOutcomeGroup>(\n `${this.buildContextPath(contextType, contextId, 'outcome_groups')}/${groupId}/subgroups`,\n )\n }\n\n async getOutcome(\n outcomeId: number,\n options?: { add_defaults?: boolean },\n ): Promise<CanvasOutcome> {\n return this.client.request<CanvasOutcome>(\n this.withQuery(`/api/v1/outcomes/${outcomeId}`, {\n add_defaults: options?.add_defaults,\n }),\n )\n }\n\n async getOutcomeAlignments(\n courseId: number,\n options?: { student_id?: number; assignment_id?: number },\n ): Promise<CanvasOutcomeAlignment[]> {\n return this.client.request<CanvasOutcomeAlignment[]>(\n this.withQuery(`/api/v1/courses/${courseId}/outcome_alignments`, {\n student_id: options?.student_id,\n assignment_id: options?.assignment_id,\n }),\n )\n }\n\n async getOutcomeResults(\n courseId: number,\n options?: {\n user_ids?: Array<number | string>\n outcome_ids?: number[]\n include_alignments?: boolean\n include_hidden?: boolean\n },\n ): Promise<CanvasOutcomeResultsResponse> {\n return this.client.request<CanvasOutcomeResultsResponse>(\n this.withQuery(`/api/v1/courses/${courseId}/outcome_results`, {\n 'user_ids[]': options?.user_ids,\n 'outcome_ids[]': options?.outcome_ids,\n 'include[]': options?.include_alignments ? ['alignments'] : undefined,\n include_hidden: options?.include_hidden,\n }),\n )\n }\n\n async getOutcomeRollups(\n courseId: number,\n options?: {\n aggregate?: 'course'\n aggregate_stat?: 'mean' | 'median'\n user_ids?: Array<number | string>\n outcome_ids?: number[]\n include_courses?: boolean\n exclude?: Array<'missing_user_rollups' | 'missing_outcome_results'>\n sort_by?: 'student' | 'outcome'\n sort_outcome_id?: number\n sort_order?: 'asc' | 'desc'\n add_defaults?: boolean\n },\n ): Promise<CanvasOutcomeRollupsResponse> {\n return this.client.request<CanvasOutcomeRollupsResponse>(\n this.withQuery(`/api/v1/courses/${courseId}/outcome_rollups`, {\n aggregate: options?.aggregate,\n aggregate_stat: options?.aggregate_stat,\n 'user_ids[]': options?.user_ids,\n 'outcome_ids[]': options?.outcome_ids,\n 'include[]': options?.include_courses ? ['courses'] : undefined,\n 'exclude[]': options?.exclude,\n sort_by: options?.sort_by,\n sort_outcome_id: options?.sort_outcome_id,\n sort_order: options?.sort_order,\n add_defaults: options?.add_defaults,\n }),\n )\n }\n\n async getOutcomeContributingScores(\n courseId: number,\n outcomeId: number,\n options?: {\n user_ids?: Array<number | string>\n only_assignment_alignments?: boolean\n show_unpublished_assignments?: boolean\n },\n ): Promise<CanvasOutcomeContributingScoresResponse> {\n return this.client.request<CanvasOutcomeContributingScoresResponse>(\n this.withQuery(`/api/v1/courses/${courseId}/outcomes/${outcomeId}/contributing_scores`, {\n 'user_ids[]': options?.user_ids,\n only_assignment_alignments: options?.only_assignment_alignments,\n show_unpublished_assignments: options?.show_unpublished_assignments,\n }),\n )\n }\n\n async getOutcomeMasteryDistribution(\n courseId: number,\n options?: {\n exclude?: Array<'missing_user_rollups' | 'missing_outcome_results'>\n outcome_ids?: number[]\n student_ids?: Array<number | string>\n include_alignment_distributions?: boolean\n only_assignment_alignments?: boolean\n show_unpublished_assignments?: boolean\n add_defaults?: boolean\n },\n ): Promise<CanvasOutcomeMasteryDistributionResponse> {\n return this.client.request<CanvasOutcomeMasteryDistributionResponse>(\n this.withQuery(`/api/v1/courses/${courseId}/outcome_mastery_distribution`, {\n 'exclude[]': options?.exclude,\n 'outcome_ids[]': options?.outcome_ids,\n 'student_ids[]': options?.student_ids,\n 'include[]': options?.include_alignment_distributions\n ? ['alignment_distributions']\n : undefined,\n only_assignment_alignments: options?.only_assignment_alignments,\n show_unpublished_assignments: options?.show_unpublished_assignments,\n add_defaults: options?.add_defaults,\n }),\n )\n }\n\n private buildContextPath(\n contextType: CanvasOutcomeContextType,\n contextId: number,\n suffix: ContextPathSuffix = 'root_outcome_group',\n ): string {\n const segment = contextType === 'account' ? 'accounts' : 'courses'\n return `/api/v1/${segment}/${contextId}/${suffix}`\n }\n\n private withQuery(\n endpoint: string,\n params: Record<string, string | number | boolean | Array<string | number> | undefined>,\n ): string {\n const searchParams = new URLSearchParams()\n\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined) continue\n if (Array.isArray(value)) {\n for (const item of value) searchParams.append(key, String(item))\n continue\n }\n searchParams.set(key, String(value))\n }\n\n const query = searchParams.toString()\n return query ? `${endpoint}?${query}` : endpoint\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type {\n CanvasGradebookHistoryDay,\n CanvasGradebookHistoryGrader,\n CanvasGradebookHistorySubmission,\n CanvasGradebookHistorySubmissionVersion,\n} from './types'\n\nexport interface GradebookHistoryFeedParams {\n assignment_id?: number\n user_id?: number\n ascending?: boolean\n}\n\nexport class GradebookHistoryModule {\n constructor(private client: CanvasHttpClient) {}\n\n async listDays(courseId: number): Promise<CanvasGradebookHistoryDay[]> {\n return this.client.request<CanvasGradebookHistoryDay[]>(\n `/api/v1/courses/${courseId}/gradebook_history/days`,\n )\n }\n\n async getDay(courseId: number, date: string): Promise<CanvasGradebookHistoryGrader[]> {\n return this.client.request<CanvasGradebookHistoryGrader[]>(\n `/api/v1/courses/${courseId}/gradebook_history/${encodeURIComponent(date)}`,\n )\n }\n\n async listSubmissions(\n courseId: number,\n date: string,\n graderId: number,\n assignmentId: number,\n ): Promise<CanvasGradebookHistorySubmission[]> {\n return this.client.request<CanvasGradebookHistorySubmission[]>(\n `/api/v1/courses/${courseId}/gradebook_history/${encodeURIComponent(date)}/graders/${graderId}/assignments/${assignmentId}/submissions`,\n )\n }\n\n async getFeed(\n courseId: number,\n params: GradebookHistoryFeedParams = {},\n ): Promise<CanvasGradebookHistorySubmissionVersion[]> {\n const searchParams = new URLSearchParams()\n\n if (params.assignment_id !== undefined) {\n searchParams.set('assignment_id', String(params.assignment_id))\n }\n if (params.user_id !== undefined) {\n searchParams.set('user_id', String(params.user_id))\n }\n if (params.ascending !== undefined) {\n searchParams.set('ascending', String(params.ascending))\n }\n\n const query = searchParams.toString()\n\n return this.client.paginate<CanvasGradebookHistorySubmissionVersion>(\n `/api/v1/courses/${courseId}/gradebook_history/feed${query ? `?${query}` : ''}`,\n )\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasNewQuiz, CanvasNewQuizItem } from './types'\n\nexport interface NewQuizPayload {\n title?: string\n instructions?: string | null\n points_possible?: number\n due_at?: string | null\n unlock_at?: string | null\n lock_at?: string | null\n published?: boolean\n}\n\ntype ChoiceItem = {\n interaction_type_slug: 'choice'\n title?: string\n item_body: string\n choices: Array<{ id: string; item_body: string }>\n correct_choice_id: string\n}\n\ntype TrueFalseItem = {\n interaction_type_slug: 'true-false'\n item_body: string\n correct_answer: boolean\n}\n\ntype EssayItem = {\n interaction_type_slug: 'essay'\n item_body: string\n rich_text?: boolean\n word_count_min?: number\n word_count_max?: number\n}\n\ntype MatchingItem = {\n interaction_type_slug: 'matching'\n item_body: string\n matches: Array<{ question: string; answer: string }>\n distractors?: string[]\n}\n\ntype NumericAnswer =\n | { kind: 'exact'; value: number; margin?: number }\n | { kind: 'range'; min: number; max: number }\n | { kind: 'precision'; value: number; precision: number }\n\ntype NumericItem = {\n interaction_type_slug: 'numeric'\n item_body: string\n answers: NumericAnswer[]\n}\n\nexport type NewQuizItemInput = ChoiceItem | TrueFalseItem | EssayItem | MatchingItem | NumericItem\n\nexport interface NewQuizItemCreatePayload {\n position?: number\n points_possible: number\n item: NewQuizItemInput\n}\n\nexport interface NewQuizItemUpdatePayload {\n position?: number\n points_possible?: number\n item?: NewQuizItemInput\n}\n\nexport class NewQuizzesModule {\n constructor(private client: CanvasHttpClient) {}\n\n async create(courseId: number, payload: NewQuizPayload): Promise<CanvasNewQuiz> {\n return this.client.request<CanvasNewQuiz>(`/api/quiz/v1/courses/${courseId}/quizzes`, {\n method: 'POST',\n body: JSON.stringify(payload),\n })\n }\n\n async update(\n courseId: number,\n assignmentId: number,\n patch: NewQuizPayload,\n ): Promise<CanvasNewQuiz> {\n return this.client.request<CanvasNewQuiz>(\n `/api/quiz/v1/courses/${courseId}/quizzes/${assignmentId}`,\n { method: 'PATCH', body: JSON.stringify(patch) },\n )\n }\n\n async delete(courseId: number, assignmentId: number): Promise<void> {\n await this.client.request<void>(`/api/quiz/v1/courses/${courseId}/quizzes/${assignmentId}`, {\n method: 'DELETE',\n })\n }\n\n async listItems(courseId: number, assignmentId: number): Promise<CanvasNewQuizItem[]> {\n return this.client.paginate<CanvasNewQuizItem>(\n `/api/quiz/v1/courses/${courseId}/quizzes/${assignmentId}/items`,\n )\n }\n\n async getItem(\n courseId: number,\n assignmentId: number,\n itemId: string,\n ): Promise<CanvasNewQuizItem> {\n return this.client.request<CanvasNewQuizItem>(\n `/api/quiz/v1/courses/${courseId}/quizzes/${assignmentId}/items/${itemId}`,\n )\n }\n\n async createItem(\n courseId: number,\n assignmentId: number,\n input: NewQuizItemCreatePayload,\n ): Promise<CanvasNewQuizItem> {\n const wireBody: Record<string, unknown> = {\n entry_type: 'Item',\n points_possible: input.points_possible,\n entry: this.toWireItem(input.item),\n }\n if (input.position !== undefined) wireBody.position = input.position\n return this.client.request<CanvasNewQuizItem>(\n `/api/quiz/v1/courses/${courseId}/quizzes/${assignmentId}/items`,\n { method: 'POST', body: JSON.stringify(wireBody) },\n )\n }\n\n async updateItem(\n courseId: number,\n assignmentId: number,\n itemId: string,\n patch: NewQuizItemUpdatePayload,\n ): Promise<CanvasNewQuizItem> {\n const wireBody: Record<string, unknown> = {}\n if (patch.position !== undefined) wireBody.position = patch.position\n if (patch.points_possible !== undefined) wireBody.points_possible = patch.points_possible\n if (patch.item !== undefined) wireBody.entry = this.toWireItem(patch.item)\n return this.client.request<CanvasNewQuizItem>(\n `/api/quiz/v1/courses/${courseId}/quizzes/${assignmentId}/items/${itemId}`,\n { method: 'PATCH', body: JSON.stringify(wireBody) },\n )\n }\n\n async deleteItem(courseId: number, assignmentId: number, itemId: string): Promise<void> {\n await this.client.request<void>(\n `/api/quiz/v1/courses/${courseId}/quizzes/${assignmentId}/items/${itemId}`,\n { method: 'DELETE' },\n )\n }\n\n private toWireItem(item: NewQuizItemInput): Record<string, unknown> {\n switch (item.interaction_type_slug) {\n case 'choice':\n return this.choiceToWire(item)\n case 'true-false':\n return this.trueFalseToWire(item)\n case 'essay':\n return this.essayToWire(item)\n case 'matching':\n return this.matchingToWire(item)\n case 'numeric':\n return this.numericToWire(item)\n }\n }\n\n private choiceToWire(item: ChoiceItem): Record<string, unknown> {\n return {\n interaction_type_slug: 'choice',\n item_body: item.item_body,\n interaction_data: {\n choices: item.choices.map((c, i) => ({\n id: c.id,\n position: i + 1,\n item_body: c.item_body,\n })),\n },\n properties: {\n shuffle_rules: { choices: { shuffled: true } },\n },\n scoring_data: {\n value: item.correct_choice_id,\n },\n // Equivalence hard-coded per CTO direction; not exposed to callers\n scoring_algorithm: 'Equivalence',\n }\n }\n\n private trueFalseToWire(item: TrueFalseItem): Record<string, unknown> {\n return {\n interaction_type_slug: 'true-false',\n item_body: item.item_body,\n interaction_data: {\n choices: [\n { id: 'true', position: 1, item_body: 'True' },\n { id: 'false', position: 2, item_body: 'False' },\n ],\n },\n properties: {},\n scoring_data: {\n value: item.correct_answer ? 'true' : 'false',\n },\n scoring_algorithm: 'Equivalence',\n }\n }\n\n private essayToWire(item: EssayItem): Record<string, unknown> {\n const properties: Record<string, unknown> = {}\n if (item.word_count_min !== undefined || item.word_count_max !== undefined) {\n const wc: Record<string, unknown> = {}\n if (item.word_count_min !== undefined) wc.min = item.word_count_min\n if (item.word_count_max !== undefined) wc.max = item.word_count_max\n properties.word_count = wc\n }\n return {\n interaction_type_slug: 'essay',\n item_body: item.item_body,\n interaction_data: {\n rce_enabled: item.rich_text !== false,\n },\n properties,\n scoring_data: {},\n scoring_algorithm: 'None',\n }\n }\n\n private matchingToWire(item: MatchingItem): Record<string, unknown> {\n const matches = item.matches.map((m, i) => ({\n id: `match_${i}`,\n item_body: m.question,\n }))\n const correctResponses = item.matches.map((m, i) => ({\n id: `resp_${i}`,\n item_body: m.answer,\n }))\n const distractorResponses = (item.distractors ?? []).map((d, i) => ({\n id: `dist_${i}`,\n item_body: d,\n }))\n const scoringValue = item.matches.map((_, i) => ({\n id: `match_${i}`,\n match_id: `resp_${i}`,\n }))\n return {\n interaction_type_slug: 'matching',\n item_body: item.item_body,\n interaction_data: {\n matches,\n responses: [...correctResponses, ...distractorResponses],\n },\n properties: {},\n scoring_data: {\n value: scoringValue,\n },\n scoring_algorithm: 'Equivalence',\n }\n }\n\n private numericToWire(item: NumericItem): Record<string, unknown> {\n const answers = item.answers\n const first = answers[0]\n if (first === undefined) {\n throw new Error('numeric item must have at least one answer')\n }\n let scoringData: Record<string, unknown>\n let scoringAlgorithm: string\n if (first.kind === 'exact') {\n scoringData = { exact_answer: first.value, margin: first.margin ?? 0 }\n scoringAlgorithm = 'Exact'\n } else if (first.kind === 'range') {\n scoringData = { lower_bound: first.min, upper_bound: first.max }\n scoringAlgorithm = 'Range'\n } else {\n scoringData = { exact_answer: first.value, precision: first.precision }\n scoringAlgorithm = 'Precision'\n }\n return {\n interaction_type_slug: 'numeric',\n item_body: item.item_body,\n interaction_data: {},\n properties: {},\n scoring_data: scoringData,\n scoring_algorithm: scoringAlgorithm,\n }\n }\n}\n","import { CanvasHttpClient } from './client'\nimport type { CanvasClientConfig } from './types'\nimport { CoursesModule } from './courses'\nimport { AssignmentsModule } from './assignments'\nimport { SubmissionsModule } from './submissions'\nimport { RubricsModule } from './rubrics'\nimport { QuizzesModule } from './quizzes'\nimport { FilesModule } from './files'\nimport { UsersModule } from './users'\nimport { GroupsModule } from './groups'\nimport { EnrollmentsModule } from './enrollments'\nimport { DiscussionsModule } from './discussions'\nimport { ModulesModule } from './modules'\nimport { PagesModule } from './pages'\nimport { CalendarModule } from './calendar'\nimport { ConversationsModule } from './conversations'\nimport { PeerReviewsModule } from './peer-reviews'\nimport { AccountsModule } from './accounts'\nimport { AnalyticsModule } from './analytics'\nimport { DashboardModule } from './dashboard'\nimport { OutcomesModule } from './outcomes'\nimport { GradebookHistoryModule } from './gradebook-history'\nimport { NewQuizzesModule } from './new-quizzes'\n\n/**\n * Standalone Canvas REST API client. Composed of modular per-domain classes\n * sharing a `CanvasHttpClient`. Throws `CanvasApiError` on Canvas-side failures.\n *\n * NOTE on FERPA mode: pseudonymization is a tool-layer concern provided by the\n * `Pseudonymizer` in `src/pseudonym/`. Library consumers that import this\n * client directly (`canvas-lms-mcp/canvas`) bypass the MCP tool wrappers and\n * therefore receive raw Canvas responses with real student names and contact\n * info. If you need pseudonymized output, either use the MCP server entry\n * points (`canvas-lms-mcp` stdio / `canvas-lms-mcp serve` HTTP) or wrap your\n * own calls with a `Pseudonymizer` instance.\n */\nexport class CanvasClient {\n private client: CanvasHttpClient\n courses: CoursesModule\n assignments: AssignmentsModule\n submissions: SubmissionsModule\n rubrics: RubricsModule\n quizzes: QuizzesModule\n files: FilesModule\n users: UsersModule\n groups: GroupsModule\n enrollments: EnrollmentsModule\n discussions: DiscussionsModule\n modules: ModulesModule\n pages: PagesModule\n calendar: CalendarModule\n conversations: ConversationsModule\n peerReviews: PeerReviewsModule\n accounts: AccountsModule\n analytics: AnalyticsModule\n dashboard: DashboardModule\n outcomes: OutcomesModule\n gradebookHistory: GradebookHistoryModule\n newQuizzes: NewQuizzesModule\n\n constructor(config: CanvasClientConfig) {\n this.client = new CanvasHttpClient(config)\n this.courses = new CoursesModule(this.client)\n this.assignments = new AssignmentsModule(this.client)\n this.submissions = new SubmissionsModule(this.client)\n this.rubrics = new RubricsModule(this.client)\n this.quizzes = new QuizzesModule(this.client)\n this.files = new FilesModule(this.client)\n this.users = new UsersModule(this.client)\n this.groups = new GroupsModule(this.client)\n this.enrollments = new EnrollmentsModule(this.client)\n this.discussions = new DiscussionsModule(this.client)\n this.modules = new ModulesModule(this.client)\n this.pages = new PagesModule(this.client)\n this.calendar = new CalendarModule(this.client)\n this.conversations = new ConversationsModule(this.client)\n this.peerReviews = new PeerReviewsModule(this.client)\n this.accounts = new AccountsModule(this.client)\n this.analytics = new AnalyticsModule(this.client)\n this.dashboard = new DashboardModule(this.client)\n this.outcomes = new OutcomesModule(this.client)\n this.gradebookHistory = new GradebookHistoryModule(this.client)\n this.newQuizzes = new NewQuizzesModule(this.client)\n }\n}\n\nexport { CanvasHttpClient, CanvasApiError } from './client'\nexport type { CanvasRequestOptions } from './client'\nexport { appendCanvasQuery, toCanvasQuery } from './query'\nexport type { CanvasQueryParams, CanvasQueryPrimitive, CanvasQueryValue } from './query'\nexport type * from './types'\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/canvas-lms-mcp/canvas-lms-mcp/dist/chunk-22DWAWDC.cjs","../src/canvas/courses.ts","../src/canvas/assignments.ts","../src/canvas/submissions.ts","../src/canvas/rubrics.ts","../src/canvas/quizzes.ts","../src/canvas/files.ts","../src/canvas/users.ts","../src/canvas/groups.ts","../src/canvas/enrollments.ts","../src/canvas/discussions.ts","../src/canvas/modules.ts","../src/canvas/pages.ts","../src/canvas/calendar.ts","../src/canvas/conversations.ts","../src/canvas/peer-reviews.ts","../src/canvas/accounts.ts","../src/canvas/analytics.ts","../src/canvas/dashboard.ts","../src/canvas/outcomes.ts","../src/canvas/gradebook-history.ts","../src/canvas/new-quizzes.ts","../src/canvas/index.ts"],"names":[],"mappings":"AAAA;AACE;AACF,wDAA6B;AAC7B;AACA;AC+CA,IAAM,qBAAA,EAAyD,CAAC,MAAM,CAAA;AACtE,IAAM,oBAAA,EAAuD,CAAC,MAAA,EAAQ,gBAAgB,CAAA;AAE/E,IAAM,cAAA,EAAN,MAAoB;AAAA,EACzB,WAAA,CAAoB,MAAA,EAA0B;AAA1B,IAAA,IAAA,CAAA,OAAA,EAAA,MAAA;AAAA,EAA2B;AAAA,EAA3B;AAAA,EAEpB,MAAM,IAAA,CAAK,KAAA,EAA2B,CAAC,CAAA,EAA4B;AACjE,IAAA,MAAM,OAAA,EAA4B,CAAC,CAAA;AACnC,IAAA,MAAA,CAAO,QAAA,EAAU,IAAA,CAAK,QAAA,GAAW,IAAA,CAAK,OAAA,CAAQ,OAAA,EAAS,EAAA,EAAI,IAAA,CAAK,QAAA,EAAU,oBAAA;AAC1E,IAAA,GAAA,CAAI,IAAA,CAAK,gBAAA,EAAkB,MAAA,CAAO,iBAAA,EAAmB,IAAA,CAAK,gBAAA;AAC1D,IAAA,GAAA,CAAI,IAAA,CAAK,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,OAAA,EAAS,CAAA,EAAG,MAAA,CAAO,MAAA,EAAQ,IAAA,CAAK,KAAA;AAC7D,IAAA,GAAA,CAAI,IAAA,CAAK,mBAAA,IAAuB,KAAA,CAAA,EAAW,MAAA,CAAO,mBAAA,EAAqB,IAAA,CAAK,kBAAA;AAC5E,IAAA,GAAA,CAAI,IAAA,CAAK,0BAAA,IAA8B,KAAA,CAAA,EAAW;AAChD,MAAA,MAAA,CAAO,0BAAA,EAA4B,IAAA,CAAK,yBAAA;AAAA,IAC1C;AACA,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,QAAA,CAAuB,iBAAA,EAAmB,MAAM,CAAA;AAAA,EACrE;AAAA,EAEA,MAAM,GAAA,CAAI,QAAA,EAAkB,KAAA,EAAyB,CAAC,CAAA,EAA0B;AAC9E,IAAA,MAAM,QAAA,EAAU,IAAA,CAAK,QAAA,GAAW,IAAA,CAAK,OAAA,CAAQ,OAAA,EAAS,EAAA,EAAI,IAAA,CAAK,QAAA,EAAU,mBAAA;AACzE,IAAA,MAAM,MAAA,EAA2B,EAAE,QAAQ,CAAA;AAC3C,IAAA,GAAA,CAAI,IAAA,CAAK,cAAA,IAAkB,KAAA,CAAA,EAAW,KAAA,CAAM,cAAA,EAAgB,IAAA,CAAK,aAAA;AACjE,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,OAAA,CAAsB,CAAA,gBAAA,EAAmB,QAAQ,CAAA,CAAA;AACtE,EAAA;AAE4D,EAAA;AACH,IAAA;AACjB,MAAA;AACrC,IAAA;AAC8B,IAAA;AACjC,EAAA;AAEgE,EAAA;AACtB,IAAA;AACqB,IAAA;AACnD,MAAA;AACqC,MAAA;AAC9C,IAAA;AACH,EAAA;AAEkF,EAAA;AACZ,IAAA;AAC1D,MAAA;AAC+B,MAAA;AACxC,IAAA;AACH,EAAA;AACF;ADhD2D;AACA;AE0BT;AACb,EAAA;AACc,EAAA;AACJ,IAAA;AACjB,IAAA;AACA,MAAA;AACV,MAAA;AAGP,IAAA;AAGO,MAAA;AAChB,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAE+B;AACiB,EAAA;AAA1B,IAAA;AAA2B,EAAA;AAA3B,EAAA;AAEyE,EAAA;AACxE,IAAA;AACU,MAAA;AACf,MAAA;AACd,IAAA;AACF,EAAA;AAM6B,EAAA;AACR,IAAA;AAC0B,MAAA;AACpB,MAAA;AACzB,IAAA;AACF,EAAA;AAKoC,EAAA;AACf,IAAA;AACU,MAAA;AACf,MAAA;AACd,IAAA;AACF,EAAA;AAE0F,EAAA;AAChB,IAAA;AAC9D,MAAA;AACmC,MAAA;AAC5C,IAAA;AACH,EAAA;AAM6B,EAAA;AACR,IAAA;AAC0B,MAAA;AAC3C,MAAA;AACU,QAAA;AACmC,QAAA;AAC7C,MAAA;AACF,IAAA;AACF,EAAA;AAEoE,EAAA;AACP,IAAA;AACjD,MAAA;AACT,IAAA;AACH,EAAA;AACF;AF3C2D;AACA;AGjE8B;AACF;AAEb;AACrC,EAAA;AACoB,EAAA;AACF,EAAA;AACG,EAAA;AACzB,IAAA;AACsB,EAAA;AACC,EAAA;AACA,EAAA;AACL,EAAA;AACQ,EAAA;AACD,EAAA;AACN,EAAA;AAC3C,EAAA;AACT;AAE+B;AACiB,EAAA;AAA1B,IAAA;AAA2B,EAAA;AAA3B,EAAA;AAMW,EAAA;AACV,IAAA;AAC0B,MAAA;AACvB,MAAA;AACtB,IAAA;AACF,EAAA;AAO6B,EAAA;AAC2B,IAAA;AACnC,IAAA;AAC0B,MAAA;AACtB,MAAA;AACvB,IAAA;AACF,EAAA;AAO6B,EAAA;AACR,IAAA;AAC0B,MAAA;AAC3C,MAAA;AACU,QAAA;AAC2C,QAAA;AACrD,MAAA;AACF,IAAA;AACF,EAAA;AAO6B,EAAA;AACR,IAAA;AAC0B,MAAA;AAC3C,MAAA;AACU,QAAA;AACa,QAAA;AACc,UAAA;AAClC,QAAA;AACH,MAAA;AACF,IAAA;AACF,EAAA;AAE4D,EAAA;AACvC,IAAA;AACU,MAAA;AACH,MAAA;AAC1B,IAAA;AACF,EAAA;AACF;AH4C2D;AACA;AIpJhC;AACqB,EAAA;AAA1B,IAAA;AAA2B,EAAA;AAA3B,EAAA;AAEkC,EAAA;AACiB,IAAA;AACvE,EAAA;AAEqE,EAAA;AACC,IAAA;AACtE,EAAA;AAM6B,EAAA;AACR,IAAA;AAC0B,MAAA;AAC7C,IAAA;AACF,EAAA;AAMmC,EAAA;AACd,IAAA;AACkC,MAAA;AACnD,MAAA;AACU,QAAA;AAC2C,QAAA;AACrD,MAAA;AACF,IAAA;AACF,EAAA;AAMyB,EAAA;AACY,IAAA;AACQ,IAAA;AAEA,IAAA;AACL,MAAA;AACa,MAAA;AAGE,MAAA;AACb,MAAA;AACc,QAAA;AACA,QAAA;AACnD,MAAA;AACF,IAAA;AAEgB,IAAA;AACqC,MAAA;AACtC,MAAA;AACiC,MAAA;AAC/B,QAAA;AAChB,MAAA;AACyB,MAAA;AACsB,QAAA;AAC/C,MAAA;AACF,IAAA;AAEoE,IAAA;AAC1D,MAAA;AACc,MAAA;AACK,MAAA;AAC5B,IAAA;AACH,EAAA;AACF;AJmI2D;AACA;AK7NhC;AACqB,EAAA;AAA1B,IAAA;AAA2B,EAAA;AAA3B,EAAA;AAEgC,EAAA;AACiB,IAAA;AACrE,EAAA;AAEiE,EAAA;AACG,IAAA;AACpE,EAAA;AAEyF,EAAA;AACpE,IAAA;AAC4B,MAAA;AAC7C,MAAA;AACF,IAAA;AACF,EAAA;AAEqF,EAAA;AAChE,IAAA;AAC4B,MAAA;AAC/C,IAAA;AACF,EAAA;AAE8F,EAAA;AACzE,IAAA;AAC2B,MAAA;AAC5C,MAAA;AAC+B,MAAA;AACjC,IAAA;AACF,EAAA;AAME,EAAA;AAK4C,IAAA;AAC/B,MAAA;AACsB,QAAA;AACjC,MAAA;AACF,IAAA;AAC2B,IAAA;AACJ,MAAA;AACvB,IAAA;AACsC,IAAA;AACP,MAAA;AAC/B,IAAA;AACkB,IAAA;AAC6B,MAAA;AAC7C,MAAA;AACU,QAAA;AACiB,QAAA;AAC3B,MAAA;AACF,IAAA;AACF,EAAA;AACF;ALoN2D;AACA;AMtRpB;AAEJ;AACjC,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACD;AAEwD;AACD,EAAA;AACA,EAAA;AACxD;AAEyB;AACuB,EAAA;AAA1B,IAAA;AAA2B,EAAA;AAA3B,EAAA;AAEgC,EAAA;AACiB,IAAA;AACrE,EAAA;AAE6D,EAAA;AACU,IAAA;AACvE,EAAA;AAEiE,EAAA;AACG,IAAA;AACpE,EAAA;AAOE,EAAA;AAEmD,IAAA;AACD,IAAA;AAChC,MAAA;AAClB,IAAA;AAG2C,IAAA;AACzC,MAAA;AACc,MAAA;AACa,MAAA;AAC7B,IAAA;AACsB,IAAA;AACY,MAAA;AAClC,IAAA;AAEqC,IAAA;AACR,MAAA;AACwB,MAAA;AACrD,IAAA;AAG0B,IAAA;AAC2B,IAAA;AAC7B,MAAA;AACxB,IAAA;AACgD,IAAA;AAG5C,IAAA;AACA,IAAA;AAC8C,MAAA;AACtC,QAAA;AACF,QAAA;AACI,QAAA;AACX,MAAA;AACkB,IAAA;AACf,MAAA;AACA,MAAA;AACwC,QAAA;AACpC,MAAA;AACgB,QAAA;AACxB,MAAA;AACiD,MAAA;AACnD,IAAA;AAGoD,IAAA;AACE,MAAA;AACnC,MAAA;AACC,QAAA;AAClB,MAAA;AACI,MAAA;AACA,MAAA;AACoC,QAAA;AAChC,MAAA;AACY,QAAA;AACpB,MAAA;AACoD,MAAA;AACZ,MAAA;AAC5B,QAAA;AACR,UAAA;AACF,QAAA;AACF,MAAA;AAC6D,MAAA;AAC/D,IAAA;AAEoB,IAAA;AACiB,MAAA;AACzB,MAAA;AAC0C,QAAA;AACpD,MAAA;AACF,IAAA;AAE2C,IAAA;AACvC,IAAA;AACA,IAAA;AAC8B,MAAA;AACf,IAAA;AACP,MAAA;AACmC,QAAA;AACzB,QAAA;AACpB,MAAA;AACF,IAAA;AACuD,IAAA;AAC3C,MAAA;AACR,QAAA;AACF,MAAA;AACF,IAAA;AACO,IAAA;AACT,EAAA;AAE2E,EAAA;AAEjC,IAAA;AACmB,IAAA;AAEvB,IAAA;AACxB,MAAA;AACiC,QAAA;AAC3C,MAAA;AACF,IAAA;AAEqC,IAAA;AACnB,IAAA;AACA,MAAA;AAClB,IAAA;AAEiD,IAAA;AAChB,IAAA;AACkB,MAAA;AACd,MAAA;AACvB,QAAA;AACgC,UAAA;AAC1C,QAAA;AACF,MAAA;AACF,IAAA;AAGwB,IAAA;AAE+B,IAAA;AACb,IAAA;AAEE,IAAA;AAChC,MAAA;AACyC,QAAA;AACnD,MAAA;AACF,IAAA;AAEoC,IAAA;AAC3B,MAAA;AACC,QAAA;AACN,QAAA;AACA,QAAA;AACa,QAAA;AAC6B,QAAA;AAC5C,MAAA;AACF,IAAA;AAEO,IAAA;AACC,MAAA;AACN,MAAA;AACA,MAAA;AACa,MAAA;AACgC,MAAA;AAC/C,IAAA;AACF,EAAA;AAEkD,EAAA;AACkB,IAAA;AACpE,EAAA;AACF;ANuP2D;AACA;AO7YlC;AACuB,EAAA;AAA1B,IAAA;AAA2B,EAAA;AAA3B,EAAA;AAEwC,EAAA;AACS,IAAA;AACtC,MAAA;AAC5B,IAAA;AACH,EAAA;AAE+C,EAAA;AACmB,IAAA;AAClE,EAAA;AAE+C,EAAA;AAC6B,IAAA;AAC5E,EAAA;AAMyB,EAAA;AACqC,IAAA;AAC1B,IAAA;AACE,IAAA;AACgB,IAAA;AACQ,IAAA;AAC9D,EAAA;AAKyB,EAAA;AACY,IAAA;AACK,IAAA;AACM,MAAA;AAG9C,IAAA;AACmD,IAAA;AAClB,MAAA;AACjC,IAAA;AACkD,IAAA;AACE,IAAA;AACE,IAAA;AACA,IAAA;AACN,IAAA;AACd,IAAA;AACE,IAAA;AAC+B,IAAA;AACrE,EAAA;AAE+D,EAAA;AAC1C,IAAA;AACjB,MAAA;AACF,IAAA;AACF,EAAA;AACF;APmY2D;AACA;AQnejC;AACsB,EAAA;AAA1B,IAAA;AAA2B,EAAA;AAA3B,EAAA;AAEiC,EAAA;AACiB,IAAA;AACtE,EAAA;AAE0D,EAAA;AACS,IAAA;AACnE,EAAA;AACF;ARse2D;AACA;AS3bL;AACjB,EAAA;AACc,EAAA;AACJ,IAAA;AACjB,IAAA;AACA,MAAA;AACV,MAAA;AAGP,IAAA;AAGO,MAAA;AAChB,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAE+B;AACiB,EAAA;AAA1B,IAAA;AAA2B,EAAA;AAA3B,EAAA;AAE2D,EAAA;AAC1D,IAAA;AACjB,MAAA;AACgB,MAAA;AAClB,IAAA;AACF,EAAA;AAK+B,EAAA;AACV,IAAA;AACU,MAAA;AACX,MAAA;AAClB,IAAA;AACF,EAAA;AAO6B,EAAA;AACW,IAAA;AACxB,MAAA;AACD,QAAA;AACT,QAAA;AAC0C,QAAA;AAC5C,MAAA;AACF,IAAA;AACwE,IAAA;AAC9D,MAAA;AACiB,MAAA;AAC1B,IAAA;AACH,EAAA;AAE8F,EAAA;AACzE,IAAA;AAC0B,MAAA;AACxB,MAAA;AACrB,IAAA;AACF,EAAA;AAEmE,EAAA;AACrC,IAAA;AACuC,MAAA;AACtD,QAAA;AACS,QAAA;AACnB,MAAA;AACH,IAAA;AAC8C,IAAA;AAC1B,MAAA;AACnB,IAAA;AACH,EAAA;AACF;AT8a2D;AACA;AU1iB5B;AACiB,EAAA;AAA1B,IAAA;AAA2B,EAAA;AAA3B,EAAA;AAE2C,EAAA;AAC1C,IAAA;AACU,MAAA;AAC7B,IAAA;AACF,EAAA;AAE6E,EAAA;AACxD,IAAA;AACgC,MAAA;AACnD,IAAA;AACF,EAAA;AAEyE,EAAA;AACpD,IAAA;AACU,MAAA;AACE,MAAA;AAC/B,IAAA;AACF,EAAA;AAMkC,EAAA;AACb,IAAA;AACgC,MAAA;AACjD,MAAA;AACU,QAAA;AACwB,QAAA;AAClC,MAAA;AACF,IAAA;AACF,EAAA;AAE+F,EAAA;AAC1E,IAAA;AACU,MAAA;AAC3B,MAAA;AACU,QAAA;AACmB,QAAA;AAC7B,MAAA;AACF,IAAA;AACF,EAAA;AAMkC,EAAA;AACb,IAAA;AACgC,MAAA;AACjD,MAAA;AACU,QAAA;AACmB,QAAA;AAC7B,MAAA;AACF,IAAA;AACF,EAAA;AAE+D,EAAA;AACF,IAAA;AACjD,MAAA;AACT,IAAA;AACH,EAAA;AACF;AVgiB2D;AACA;AWxmBhC;AACqB,EAAA;AAA1B,IAAA;AAA2B,EAAA;AAA3B,EAAA;AAEkC,EAAA;AACiB,IAAA;AACvE,EAAA;AAEqE,EAAA;AACC,IAAA;AACtE,EAAA;AAEiF,EAAA;AAC5D,IAAA;AAC8B,MAAA;AACjD,IAAA;AACF,EAAA;AAUyB,EAAA;AAC6C,IAAA;AAC1D,MAAA;AAC+B,MAAA;AACxC,IAAA;AACH,EAAA;AAMyB,EAAA;AAC6C,IAAA;AAC1D,MAAA;AAC+B,MAAA;AACxC,IAAA;AACH,EAAA;AAY6B,EAAA;AACR,IAAA;AAC8B,MAAA;AAC/C,MAAA;AACU,QAAA;AACoC,QAAA;AAC9C,MAAA;AACF,IAAA;AACF,EAAA;AAKkC,EAAA;AACE,IAAA;AACW,IAAA;AAEX,IAAA;AACL,MAAA;AACjB,MAAA;AACZ,IAAA;AAE6C,IAAA;AAC5B,IAAA;AAE4B,IAAA;AACjB,MAAA;AACK,MAAA;AACgB,QAAA;AAC/C,MAAA;AAC0B,MAAA;AACuB,QAAA;AAC/C,QAAA;AACF,MAAA;AACO,MAAA;AACG,QAAA;AACE,QAAA;AACI,QAAA;AACkC,QAAA;AACjC,QAAA;AACa,QAAA;AACjB,UAAA;AACG,UAAA;AACD,UAAA;AACI,UAAA;AACC,UAAA;AACD,UAAA;AACA,UAAA;AACE,UAAA;AACK,UAAA;AACtB,QAAA;AACJ,MAAA;AACD,IAAA;AAEM,IAAA;AACI,MAAA;AACA,MAAA;AACwB,QAAA;AAClB,QAAA;AACE,QAAA;AACjB,MAAA;AACF,IAAA;AACF,EAAA;AACF;AXykB2D;AACA;AY9rBlC;AACuB,EAAA;AAA1B,IAAA;AAA2B,EAAA;AAA3B,EAAA;AAEgC,EAAA;AACiB,IAAA;AACrE,EAAA;AAEkE,EAAA;AAC7C,IAAA;AACoB,MAAA;AACvC,IAAA;AACF,EAAA;AAKuB,EAAA;AAC6C,IAAA;AACxD,MAAA;AACkC,MAAA;AAC3C,IAAA;AACH,EAAA;AAMuB,EAAA;AACF,IAAA;AACoB,MAAA;AACrC,MAAA;AACU,QAAA;AACkC,QAAA;AAC5C,MAAA;AACF,IAAA;AACF,EAAA;AAE+D,EAAA;AAC3C,IAAA;AACqB,MAAA;AAClB,MAAA;AACrB,IAAA;AACF,EAAA;AACF;AZurB2D;AACA;AanuB/B;AACoB,EAAA;AAA1B,IAAA;AAA2B,EAAA;AAA3B,EAAA;AAEyC,EAAA;AACiB,IAAA;AACrC,MAAA;AACtC,IAAA;AACH,EAAA;AASiC,EAAA;AAC4C,IAAA;AACjE,MAAA;AACuC,MAAA;AAChD,IAAA;AACH,EAAA;AAWgC,EAAA;AAC6C,IAAA;AACjE,MAAA;AACuC,MAAA;AAChD,IAAA;AACH,EAAA;AACF;AbqtB2D;AACA;AcxvB1B;AACe,EAAA;AAA1B,IAAA;AAA2B,EAAA;AAA3B,EAAA;AAEwB,EAAA;AAC6B,IAAA;AACzE,EAAA;AAEqE,EAAA;AACW,IAAA;AAChF,EAAA;AAE+D,EAAA;AAC/B,IAAA;AAC5B,MAAA;AACF,IAAA;AAC2C,IAAA;AAClB,IAAA;AACb,MAAA;AAC0C,QAAA;AACpD,MAAA;AACF,IAAA;AAC6B,IAAA;AAC/B,EAAA;AAE+F,EAAA;AACnB,IAAA;AAChE,MAAA;AACa,MAAA;AACnB,QAAA;AACA,QAAA;AACA,QAAA;AACD,MAAA;AACF,IAAA;AACH,EAAA;AACF;AdyvB2D;AACA;AehyB5B;AACiB,EAAA;AAA1B,IAAA;AAA2B,EAAA;AAA3B,EAAA;AAEyE,EAAA;AACxE,IAAA;AAC0B,MAAA;AAC7C,IAAA;AACF,EAAA;AAKE,EAAA;AAEmB,IAAA;AAC0B,MAAA;AAC7C,IAAA;AACF,EAAA;AAME,EAAA;AAEmB,IAAA;AAC0B,MAAA;AAC3C,MAAA;AACU,QAAA;AACgC,QAAA;AAC1C,MAAA;AACF,IAAA;AACF,EAAA;AAME,EAAA;AAEkB,IAAA;AAC2B,MAAA;AACxB,MAAA;AACrB,IAAA;AACF,EAAA;AACF;AfmxB2D;AACA;AgBj0B/B;AACoB,EAAA;AAA1B,IAAA;AAA2B,EAAA;AAA3B,EAAA;AAEiC,EAAA;AACW,IAAA;AAChE,EAAA;AAEuC,EAAA;AACwB,IAAA;AAC/D,EAAA;AAEmE,EAAA;AACF,IAAA;AACjE,EAAA;AAEiG,EAAA;AACxD,IAAA;AACa,IAAA;AACjC,IAAA;AACY,MAAA;AACO,MAAA;AACtC,IAAA;AACF,EAAA;AAE6F,EAAA;AACpD,IAAA;AACa,IAAA;AACjC,IAAA;AACY,MAAA;AACO,MAAA;AACtC,IAAA;AACF,EAAA;AAEoE,EAAA;AACI,IAAA;AACxE,EAAA;AACF;AhBg0B2D;AACA;AiBh2BvB;AAClC,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACF;AAG6B;AACmB,EAAA;AAA1B,IAAA;AAA2B,EAAA;AAA3B,EAAA;AAMa,EAAA;AACjB,IAAA;AACE,MAAA;AACoB,QAAA;AACH,UAAA;AACD,UAAA;AAC5B,QAAA;AACyB,QAAA;AACjB,UAAA;AACG,UAAA;AACH,UAAA;AACC,UAAA;AACI,UAAA;AACX,QAAA;AACJ,MAAA;AACoB,MAAA;AACoB,QAAA;AACT,UAAA;AACD,UAAA;AAC5B,QAAA;AAC+B,QAAA;AACvB,UAAA;AACG,UAAA;AACH,UAAA;AACK,UAAA;AACX,QAAA;AACJ,MAAA;AACoB,MAAA;AACoB,QAAA;AACT,UAAA;AACD,UAAA;AAC5B,QAAA;AAC+B,QAAA;AACvB,UAAA;AACG,UAAA;AACH,UAAA;AACK,UAAA;AACX,QAAA;AACJ,MAAA;AACsB,MAAA;AACoB,QAAA;AACX,UAAA;AACoB,UAAA;AACjD,QAAA;AACiC,QAAA;AACzB,UAAA;AACG,UAAA;AACH,UAAA;AACK,UAAA;AACX,QAAA;AACJ,MAAA;AACS,MAAA;AACoB,QAAA;AACc,QAAA;AAC3C,MAAA;AACF,IAAA;AACF,EAAA;AAEkF,EAAA;AAC7D,IAAA;AACU,MAAA;AAC7B,IAAA;AACF,EAAA;AAKyC,EAAA;AACpB,IAAA;AAC8B,MAAA;AACjD,IAAA;AACF,EAAA;AAEqF,EAAA;AAChE,IAAA;AACU,MAAA;AAC7B,IAAA;AACF,EAAA;AACF;AjBw1B2D;AACA;AkBt7B9B;AACmB,EAAA;AAA1B,IAAA;AAA2B,EAAA;AAA3B,EAAA;AAEsC,EAAA;AACN,IAAA;AACpD,EAAA;AAEgD,EAAA;AACuB,IAAA;AACvE,EAAA;AAE0D,EAAA;AACN,IAAA;AACpD,EAAA;AAEkE,EAAA;AACX,IAAA;AACvD,EAAA;AACF;AlBu7B2D;AACA;AmBr8BF;AACH;AACS;AACJ;AACX;AACgB;AAIpC;AACoB,EAAA;AAA1B,IAAA;AAA2B,EAAA;AAA3B,EAAA;AAKW,EAAA;AACwC,IAAA;AACvE,EAAA;AAKiC,EAAA;AACZ,IAAA;AAC6B,MAAA;AAChD,IAAA;AACF,EAAA;AAKE,EAAA;AAEmB,IAAA;AACiC,MAAA;AACxB,QAAA;AACM,QAAA;AAC/B,MAAA;AACH,IAAA;AACF,EAAA;AAM+B,EAAA;AACV,IAAA;AACgC,MAAA;AACnD,IAAA;AACF,EAAA;AAME,EAAA;AAEmB,IAAA;AACZ,MAAA;AAC8C,QAAA;AACT,QAAA;AAC1C,MAAA;AACF,IAAA;AACF,EAAA;AAKE,EAAA;AAEmB,IAAA;AACgC,MAAA;AACnD,IAAA;AACF,EAAA;AAK0B,EAAA;AACL,IAAA;AAC+B,MAAA;AACvB,QAAA;AACxB,MAAA;AACH,IAAA;AACF,EAAA;AAKqC,EAAA;AAChB,IAAA;AACyB,MAAA;AACnB,QAAA;AACG,QAAA;AACzB,MAAA;AACH,IAAA;AACF,EAAA;AAUyC,EAAA;AACpB,IAAA;AACyB,MAAA;AACjB,QAAA;AACG,QAAA;AACkB,QAAA;AACnB,QAAA;AAC1B,MAAA;AACH,IAAA;AACF,EAAA;AAgByC,EAAA;AACpB,IAAA;AACyB,MAAA;AACpB,QAAA;AACK,QAAA;AACF,QAAA;AACG,QAAA;AACwB,QAAA;AAC5B,QAAA;AACJ,QAAA;AACQ,QAAA;AACL,QAAA;AACE,QAAA;AACxB,MAAA;AACH,IAAA;AACF,EAAA;AAKE,EAAA;AAMmB,IAAA;AACyB,MAAA;AACjB,QAAA;AACc,QAAA;AACE,QAAA;AACxC,MAAA;AACH,IAAA;AACF,EAAA;AAaqD,EAAA;AAChC,IAAA;AACyB,MAAA;AAClB,QAAA;AACI,QAAA;AACA,QAAA;AACJ,QAAA;AAGe,QAAA;AACE,QAAA;AAChB,QAAA;AACxB,MAAA;AACH,IAAA;AACF,EAAA;AAK8B,EAAA;AAEgB,IAAA;AACI,IAAA;AAClD,EAAA;AAKU,EAAA;AACiC,IAAA;AAEU,IAAA;AACxB,MAAA;AACC,MAAA;AAC2B,QAAA;AACnD,QAAA;AACF,MAAA;AACmC,MAAA;AACrC,IAAA;AAEoC,IAAA;AACI,IAAA;AAC1C,EAAA;AACF;AnBw2B2D;AACA;AoBpkCvB;AACY,EAAA;AAA1B,IAAA;AAA2B,EAAA;AAA3B,EAAA;AAEmD,EAAA;AAClD,IAAA;AACU,MAAA;AAC7B,IAAA;AACF,EAAA;AAEsF,EAAA;AACjE,IAAA;AACgC,MAAA;AACnD,IAAA;AACF,EAAA;AAME,EAAA;AAEmB,IAAA;AACgC,MAAA;AACnD,IAAA;AACF,EAAA;AAKsD,EAAA;AACX,IAAA;AAED,IAAA;AACU,MAAA;AAClD,IAAA;AACkC,IAAA;AACkB,MAAA;AACpD,IAAA;AACoC,IAAA;AACmB,MAAA;AACvD,IAAA;AAEoC,IAAA;AAEjB,IAAA;AACoC,MAAA;AACvD,IAAA;AACF,EAAA;AACF;ApB0jC2D;AACA;AqBtjC7B;AACkB,EAAA;AAA1B,IAAA;AAA2B,EAAA;AAA3B,EAAA;AAE4D,EAAA;AACZ,IAAA;AACxD,MAAA;AACoB,MAAA;AAC7B,IAAA;AACH,EAAA;AAM0B,EAAA;AACL,IAAA;AAC2B,MAAA;AACG,MAAA;AACjD,IAAA;AACF,EAAA;AAEoE,EAAA;AACV,IAAA;AAC9C,MAAA;AACT,IAAA;AACH,EAAA;AAEsF,EAAA;AACjE,IAAA;AAC2B,MAAA;AAC9C,IAAA;AACF,EAAA;AAM8B,EAAA;AACT,IAAA;AAC2B,MAAA;AAC9C,IAAA;AACF,EAAA;AAM8B,EAAA;AACc,IAAA;AAC5B,MAAA;AACW,MAAA;AACU,MAAA;AACnC,IAAA;AACsD,IAAA;AACnC,IAAA;AAC2B,MAAA;AACK,MAAA;AACnD,IAAA;AACF,EAAA;AAO8B,EAAA;AACe,IAAA;AACW,IAAA;AACJ,IAAA;AACE,IAAA;AACjC,IAAA;AAC2B,MAAA;AACM,MAAA;AACpD,IAAA;AACF,EAAA;AAEwF,EAAA;AACpE,IAAA;AAC4B,MAAA;AACzB,MAAA;AACrB,IAAA;AACF,EAAA;AAEoE,EAAA;AAC9B,IAAA;AAC7B,MAAA;AAC0B,QAAA;AAC1B,MAAA;AAC6B,QAAA;AAC7B,MAAA;AACyB,QAAA;AACzB,MAAA;AAC4B,QAAA;AAC5B,MAAA;AAC2B,QAAA;AAClC,IAAA;AACF,EAAA;AAEgE,EAAA;AACvD,IAAA;AACkB,MAAA;AACP,MAAA;AACE,MAAA;AACqB,QAAA;AAC7B,UAAA;AACQ,UAAA;AACD,UAAA;AACb,QAAA;AACJ,MAAA;AACY,MAAA;AACmC,QAAA;AAC/C,MAAA;AACc,MAAA;AACA,QAAA;AACd,MAAA;AAAA;AAEmB,MAAA;AACrB,IAAA;AACF,EAAA;AAEsE,EAAA;AAC7D,IAAA;AACkB,MAAA;AACP,MAAA;AACE,MAAA;AACP,QAAA;AACsC,UAAA;AACE,UAAA;AACjD,QAAA;AACF,MAAA;AACa,MAAA;AACC,MAAA;AAC0B,QAAA;AACxC,MAAA;AACmB,MAAA;AACrB,IAAA;AACF,EAAA;AAE8D,EAAA;AACf,IAAA;AACC,IAAA;AACP,MAAA;AACgB,MAAA;AACA,MAAA;AAC7B,MAAA;AAC1B,IAAA;AACO,IAAA;AACkB,MAAA;AACP,MAAA;AACE,MAAA;AACgB,QAAA;AAClC,MAAA;AACA,MAAA;AACe,MAAA;AACI,MAAA;AACrB,IAAA;AACF,EAAA;AAEoE,EAAA;AACtB,IAAA;AAC5B,MAAA;AACD,MAAA;AACb,IAAA;AACmD,IAAA;AACtC,MAAA;AACA,MAAA;AACb,IAAA;AACmD,IAAA;AACtC,MAAA;AACF,MAAA;AACX,IAAA;AAC+C,IAAA;AACjC,MAAA;AACK,MAAA;AACnB,IAAA;AACK,IAAA;AACkB,MAAA;AACP,MAAA;AACE,MAAA;AAChB,QAAA;AACoC,QAAA;AACtC,MAAA;AACa,MAAA;AACC,MAAA;AACL,QAAA;AACT,MAAA;AACmB,MAAA;AACrB,IAAA;AACF,EAAA;AAEkE,EAAA;AAC3C,IAAA;AACE,IAAA;AACE,IAAA;AACP,MAAA;AAClB,IAAA;AACI,IAAA;AACA,IAAA;AACwB,IAAA;AACyB,MAAA;AAChC,MAAA;AACc,IAAA;AACoB,MAAA;AAClC,MAAA;AACd,IAAA;AACsC,MAAA;AACxB,MAAA;AACrB,IAAA;AACO,IAAA;AACkB,MAAA;AACP,MAAA;AACG,MAAA;AACN,MAAA;AACC,MAAA;AACK,MAAA;AACrB,IAAA;AACF,EAAA;AACF;ArB4hC2D;AACA;AsBrxCjC;AAChB,EAAA;AACR,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AAEwC,EAAA;AACG,IAAA;AACG,IAAA;AACQ,IAAA;AACA,IAAA;AACR,IAAA;AACA,IAAA;AACJ,IAAA;AACA,IAAA;AACE,IAAA;AACU,IAAA;AACA,IAAA;AACR,IAAA;AACJ,IAAA;AACM,IAAA;AACI,IAAA;AACE,IAAA;AACN,IAAA;AACE,IAAA;AACA,IAAA;AACF,IAAA;AACK,IAAA;AACD,IAAA;AACpD,EAAA;AACF;AtBsxC2D;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/canvas-lms-mcp/canvas-lms-mcp/dist/chunk-22DWAWDC.cjs","sourcesContent":[null,"import type { CanvasHttpClient } from './client'\nimport type { CanvasQueryParams } from './query'\nimport type { CanvasCourse, CreateCourseParams, UpdateCourseParams } from './types'\n\nexport type CourseEnrollmentState = 'active' | 'invited_or_pending' | 'completed'\n\nexport type CourseWorkflowState = 'unpublished' | 'available' | 'completed' | 'deleted'\n\nexport type CourseListInclude =\n | 'needs_grading_count'\n | 'syllabus_body'\n | 'public_description'\n | 'total_scores'\n | 'current_grading_period_scores'\n | 'grading_periods'\n | 'term'\n | 'account'\n | 'course_progress'\n | 'sections'\n | 'storage_quota_used_mb'\n | 'total_students'\n | 'passback_status'\n | 'favorites'\n | 'teachers'\n | 'observed_users'\n | 'tabs'\n | 'course_image'\n | 'banner_image'\n | 'concluded'\n | 'lti_context_id'\n | 'post_manually'\n\nexport type CourseGetInclude =\n | CourseListInclude\n | 'all_courses'\n | 'permissions'\n | 'public_description'\n\nexport interface ListCoursesOptions {\n enrollment_state?: CourseEnrollmentState\n state?: ReadonlyArray<CourseWorkflowState>\n enrollment_role_id?: number\n include?: ReadonlyArray<CourseListInclude>\n exclude_blueprint_courses?: boolean\n}\n\nexport interface GetCourseOptions {\n include?: ReadonlyArray<CourseGetInclude>\n teacher_limit?: number\n}\n\nconst DEFAULT_LIST_INCLUDE: ReadonlyArray<CourseListInclude> = ['term']\nconst DEFAULT_GET_INCLUDE: ReadonlyArray<CourseGetInclude> = ['term', 'total_students']\n\nexport class CoursesModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(opts: ListCoursesOptions = {}): Promise<CanvasCourse[]> {\n const params: CanvasQueryParams = {}\n params.include = opts.include && opts.include.length > 0 ? opts.include : DEFAULT_LIST_INCLUDE\n if (opts.enrollment_state) params.enrollment_state = opts.enrollment_state\n if (opts.state && opts.state.length > 0) params.state = opts.state\n if (opts.enrollment_role_id !== undefined) params.enrollment_role_id = opts.enrollment_role_id\n if (opts.exclude_blueprint_courses !== undefined) {\n params.exclude_blueprint_courses = opts.exclude_blueprint_courses\n }\n return this.client.paginate<CanvasCourse>('/api/v1/courses', params)\n }\n\n async get(courseId: number, opts: GetCourseOptions = {}): Promise<CanvasCourse> {\n const include = opts.include && opts.include.length > 0 ? opts.include : DEFAULT_GET_INCLUDE\n const query: CanvasQueryParams = { include }\n if (opts.teacher_limit !== undefined) query.teacher_limit = opts.teacher_limit\n return this.client.request<CanvasCourse>(`/api/v1/courses/${courseId}`, { query })\n }\n\n async getSyllabus(courseId: number): Promise<string | null> {\n const course = await this.client.request<CanvasCourse>(`/api/v1/courses/${courseId}`, {\n query: { include: ['syllabus_body'] },\n })\n return course.syllabus_body ?? null\n }\n\n async create(params: CreateCourseParams): Promise<CanvasCourse> {\n const { account_id, ...courseFields } = params\n return this.client.request<CanvasCourse>(`/api/v1/accounts/${account_id}/courses`, {\n method: 'POST',\n body: JSON.stringify({ course: courseFields }),\n })\n }\n\n async update(courseId: number, params: UpdateCourseParams): Promise<CanvasCourse> {\n return this.client.request<CanvasCourse>(`/api/v1/courses/${courseId}`, {\n method: 'PUT',\n body: JSON.stringify({ course: params }),\n })\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasQueryParams } from './query'\nimport type {\n CanvasAssignment,\n CanvasAssignmentGroup,\n CreateAssignmentParams,\n UpdateAssignmentParams,\n} from './types'\n\nexport type AssignmentListInclude =\n | 'submission'\n | 'assignment_visibility'\n | 'all_dates'\n | 'overrides'\n | 'observed_users'\n | 'can_edit'\n | 'score_statistics'\n\nexport type AssignmentGetInclude =\n | 'submission'\n | 'assignment_visibility'\n | 'overrides'\n | 'observed_users'\n | 'can_edit'\n | 'score_statistics'\n | 'all_dates'\n\nexport type AssignmentBucket =\n | 'past'\n | 'overdue'\n | 'undated'\n | 'ungraded'\n | 'unsubmitted'\n | 'upcoming'\n | 'future'\n\nexport type AssignmentOrderBy = 'position' | 'name' | 'due_at'\n\nexport type AssignmentGroupInclude =\n | 'assignments'\n | 'discussion_topic'\n | 'all_dates'\n | 'assignment_visibility'\n | 'overrides'\n | 'submission'\n | 'observed_users'\n | 'can_edit'\n | 'score_statistics'\n\nexport interface ListAssignmentsOptions {\n include?: ReadonlyArray<AssignmentListInclude>\n search_term?: string\n override_assignment_dates?: boolean\n needs_grading_count_by_section?: boolean\n bucket?: AssignmentBucket\n assignment_ids?: ReadonlyArray<number>\n order_by?: AssignmentOrderBy\n post_to_sis?: boolean\n}\n\nexport interface GetAssignmentOptions {\n include?: ReadonlyArray<AssignmentGetInclude>\n override_assignment_dates?: boolean\n needs_grading_count_by_section?: boolean\n all_dates?: boolean\n}\n\nexport interface ListAssignmentGroupsOptions {\n include?: ReadonlyArray<AssignmentGroupInclude>\n assignment_ids?: ReadonlyArray<number>\n exclude_assignment_submission_types?: ReadonlyArray<string>\n override_assignment_dates?: boolean\n grading_period_id?: number\n scope_assignments_to_student?: boolean\n}\n\nfunction toQuery(opts: object): CanvasQueryParams {\n const params: CanvasQueryParams = {}\n for (const [key, value] of Object.entries(opts)) {\n if (value === undefined || value === null) continue\n if (Array.isArray(value)) {\n if (value.length === 0) continue\n params[key] = value as ReadonlyArray<string | number | boolean>\n } else if (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ) {\n params[key] = value\n }\n }\n return params\n}\n\nexport class AssignmentsModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(courseId: number, opts: ListAssignmentsOptions = {}): Promise<CanvasAssignment[]> {\n return this.client.paginate<CanvasAssignment>(\n `/api/v1/courses/${courseId}/assignments`,\n toQuery(opts),\n )\n }\n\n async get(\n courseId: number,\n assignmentId: number,\n opts: GetAssignmentOptions = {},\n ): Promise<CanvasAssignment> {\n return this.client.request<CanvasAssignment>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}`,\n { query: toQuery(opts) },\n )\n }\n\n async listGroups(\n courseId: number,\n opts: ListAssignmentGroupsOptions = {},\n ): Promise<CanvasAssignmentGroup[]> {\n return this.client.paginate<CanvasAssignmentGroup>(\n `/api/v1/courses/${courseId}/assignment_groups`,\n toQuery(opts),\n )\n }\n\n async create(courseId: number, params: CreateAssignmentParams): Promise<CanvasAssignment> {\n return this.client.request<CanvasAssignment>(`/api/v1/courses/${courseId}/assignments`, {\n method: 'POST',\n body: JSON.stringify({ assignment: params }),\n })\n }\n\n async update(\n courseId: number,\n assignmentId: number,\n params: UpdateAssignmentParams,\n ): Promise<CanvasAssignment> {\n return this.client.request<CanvasAssignment>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}`,\n {\n method: 'PUT',\n body: JSON.stringify({ assignment: params }),\n },\n )\n }\n\n async delete(courseId: number, assignmentId: number): Promise<void> {\n await this.client.request<void>(`/api/v1/courses/${courseId}/assignments/${assignmentId}`, {\n method: 'DELETE',\n })\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasQueryParams } from './query'\nimport type { CanvasSubmission } from './types'\n\nexport type SubmissionListInclude =\n | 'submission_history'\n | 'submission_comments'\n | 'rubric_assessment'\n | 'assignment'\n | 'visibility'\n | 'course'\n | 'user'\n | 'group'\n | 'read_status'\n | 'sub_assignment_submissions'\n\nexport type SubmissionGetInclude =\n | 'submission_history'\n | 'submission_comments'\n | 'rubric_assessment'\n | 'visibility'\n | 'course'\n | 'user'\n | 'read_status'\n\nexport type SubmissionWorkflowState = 'submitted' | 'unsubmitted' | 'graded' | 'pending_review'\n\nexport interface ListSubmissionsOptions {\n include?: ReadonlyArray<SubmissionListInclude>\n student_ids?: ReadonlyArray<number | string>\n assignment_ids?: ReadonlyArray<number>\n section_ids?: ReadonlyArray<number>\n grouped?: boolean\n workflow_state?: SubmissionWorkflowState\n grading_period_id?: number\n post_to_sis?: boolean\n submitted_since?: string\n graded_since?: string\n}\n\nexport interface GetSubmissionOptions {\n include?: ReadonlyArray<SubmissionGetInclude>\n}\n\nconst DEFAULT_LIST_INCLUDE: ReadonlyArray<SubmissionListInclude> = ['submission_comments']\nconst DEFAULT_GET_INCLUDE: ReadonlyArray<SubmissionGetInclude> = ['submission_comments']\n\nfunction buildListParams(opts: ListSubmissionsOptions): CanvasQueryParams {\n const params: CanvasQueryParams = {}\n params.include = opts.include && opts.include.length > 0 ? opts.include : DEFAULT_LIST_INCLUDE\n if (opts.student_ids && opts.student_ids.length > 0) params.student_ids = opts.student_ids\n if (opts.assignment_ids && opts.assignment_ids.length > 0)\n params.assignment_ids = opts.assignment_ids\n if (opts.section_ids && opts.section_ids.length > 0) params.section_ids = opts.section_ids\n if (opts.grouped !== undefined) params.grouped = opts.grouped\n if (opts.workflow_state) params.workflow_state = opts.workflow_state\n if (opts.grading_period_id !== undefined) params.grading_period_id = opts.grading_period_id\n if (opts.post_to_sis !== undefined) params.post_to_sis = opts.post_to_sis\n if (opts.submitted_since) params.submitted_since = opts.submitted_since\n if (opts.graded_since) params.graded_since = opts.graded_since\n return params\n}\n\nexport class SubmissionsModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(\n courseId: number,\n assignmentId: number,\n opts: ListSubmissionsOptions = {},\n ): Promise<CanvasSubmission[]> {\n return this.client.paginate<CanvasSubmission>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions`,\n buildListParams(opts),\n )\n }\n\n async get(\n courseId: number,\n assignmentId: number,\n userId: number,\n opts: GetSubmissionOptions = {},\n ): Promise<CanvasSubmission> {\n const include = opts.include && opts.include.length > 0 ? opts.include : DEFAULT_GET_INCLUDE\n return this.client.request<CanvasSubmission>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions/${userId}`,\n { query: { include } },\n )\n }\n\n async grade(\n courseId: number,\n assignmentId: number,\n userId: number,\n grade: string,\n ): Promise<CanvasSubmission> {\n return this.client.request<CanvasSubmission>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions/${userId}`,\n {\n method: 'PUT',\n body: JSON.stringify({ submission: { posted_grade: grade } }),\n },\n )\n }\n\n async comment(\n courseId: number,\n assignmentId: number,\n userId: number,\n comment: string,\n ): Promise<CanvasSubmission> {\n return this.client.request<CanvasSubmission>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions/${userId}`,\n {\n method: 'PUT',\n body: JSON.stringify({\n comment: { text_comment: comment },\n }),\n },\n )\n }\n\n async listMy(courseId: number): Promise<CanvasSubmission[]> {\n return this.client.paginate<CanvasSubmission>(\n `/api/v1/courses/${courseId}/students/submissions`,\n { student_ids: ['self'] },\n )\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasRubric, CanvasRubricAssessment, CanvasSubmission } from './types'\n\nexport interface RubricRatingInput {\n description: string\n points: number\n}\n\nexport interface RubricCriterionInput {\n description: string\n points: number\n ratings: RubricRatingInput[]\n}\n\nexport interface RubricCreateInput {\n title: string\n criteria: RubricCriterionInput[]\n}\n\nexport interface RubricAssociationInput {\n assignment_id: number\n use_for_grading?: boolean\n purpose?: string\n}\n\nexport class RubricsModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(courseId: number): Promise<CanvasRubric[]> {\n return this.client.paginate<CanvasRubric>(`/api/v1/courses/${courseId}/rubrics`)\n }\n\n async get(courseId: number, rubricId: number): Promise<CanvasRubric> {\n return this.client.request<CanvasRubric>(`/api/v1/courses/${courseId}/rubrics/${rubricId}`)\n }\n\n async getAssessment(\n courseId: number,\n assignmentId: number,\n userId: number,\n ): Promise<CanvasSubmission> {\n return this.client.request<CanvasSubmission>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions/${userId}?include[]=rubric_assessment`,\n )\n }\n\n async submitAssessment(\n courseId: number,\n associationId: number,\n data: CanvasRubricAssessment['data'],\n ): Promise<CanvasRubricAssessment> {\n return this.client.request<CanvasRubricAssessment>(\n `/api/v1/courses/${courseId}/rubric_associations/${associationId}/rubric_assessments`,\n {\n method: 'POST',\n body: JSON.stringify({ rubric_assessment: { data } }),\n },\n )\n }\n\n async create(\n courseId: number,\n rubric: RubricCreateInput,\n association?: RubricAssociationInput,\n ): Promise<CanvasRubric> {\n const params = new URLSearchParams()\n params.append('rubric[title]', rubric.title)\n\n rubric.criteria.forEach((criterion, ci) => {\n params.append(`rubric[criteria][${ci}][description]`, criterion.description)\n params.append(`rubric[criteria][${ci}][points]`, String(criterion.points))\n\n // Sort ratings highest → lowest before encoding\n const sortedRatings = [...criterion.ratings].sort((a, b) => b.points - a.points)\n sortedRatings.forEach((rating, ri) => {\n params.append(`rubric[criteria][${ci}][ratings][${ri}][description]`, rating.description)\n params.append(`rubric[criteria][${ci}][ratings][${ri}][points]`, String(rating.points))\n })\n })\n\n if (association) {\n params.append('rubric_association[association_id]', String(association.assignment_id))\n params.append('rubric_association[association_type]', 'Assignment')\n if (association.use_for_grading !== undefined) {\n params.append('rubric_association[use_for_grading]', String(association.use_for_grading))\n }\n if (association.purpose) {\n params.append('rubric_association[purpose]', association.purpose)\n }\n }\n\n return this.client.request<CanvasRubric>(`/api/v1/courses/${courseId}/rubrics`, {\n method: 'POST',\n body: params.toString(),\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n })\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type {\n CanvasQuiz,\n CanvasQuizSubmission,\n CanvasQuizQuestion,\n CanvasQuizSubmissionQuestion,\n} from './types'\n\nexport class QuizzesModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(courseId: number): Promise<CanvasQuiz[]> {\n return this.client.paginate<CanvasQuiz>(`/api/v1/courses/${courseId}/quizzes`)\n }\n\n async get(courseId: number, quizId: number): Promise<CanvasQuiz> {\n return this.client.request<CanvasQuiz>(`/api/v1/courses/${courseId}/quizzes/${quizId}`)\n }\n\n async listSubmissions(courseId: number, quizId: number): Promise<CanvasQuizSubmission[]> {\n return this.client.paginateEnvelope<CanvasQuizSubmission>(\n `/api/v1/courses/${courseId}/quizzes/${quizId}/submissions`,\n 'quiz_submissions',\n )\n }\n\n async listQuestions(courseId: number, quizId: number): Promise<CanvasQuizQuestion[]> {\n return this.client.paginate<CanvasQuizQuestion>(\n `/api/v1/courses/${courseId}/quizzes/${quizId}/questions`,\n )\n }\n\n async getSubmissionAnswers(quizSubmissionId: number): Promise<CanvasQuizSubmissionQuestion[]> {\n return this.client.paginateEnvelope<CanvasQuizSubmissionQuestion>(\n `/api/v1/quiz_submissions/${quizSubmissionId}/questions`,\n 'quiz_submission_questions',\n { 'include[]': 'quiz_question' },\n )\n }\n\n async scoreQuestion(\n courseId: number,\n quizId: number,\n submissionId: number,\n questionId: number,\n score: number,\n comment?: string,\n attempt?: number,\n ): Promise<void> {\n const submission: Record<string, unknown> = {\n questions: {\n [questionId]: { score, comment },\n },\n }\n if (attempt !== undefined) {\n submission.attempt = attempt\n }\n const body: Record<string, unknown> = {\n quiz_submissions: [submission],\n }\n await this.client.request(\n `/api/v1/courses/${courseId}/quizzes/${quizId}/submissions/${submissionId}`,\n {\n method: 'PUT',\n body: JSON.stringify(body),\n },\n )\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasFile, CanvasFileUploadInfo, CanvasFolder, DownloadedFile } from './types'\n\nconst MAX_DOWNLOAD_BYTES = 10 * 1024 * 1024 // 10 MB\n\nconst TEXT_CONTENT_TYPES = new Set([\n 'application/json',\n 'application/xml',\n 'application/javascript',\n 'application/x-javascript',\n])\n\nfunction isTextContentType(contentType: string): boolean {\n const base = (contentType.split(';')[0] ?? '').trim().toLowerCase()\n return base.startsWith('text/') || TEXT_CONTENT_TYPES.has(base)\n}\n\nexport class FilesModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(courseId: number): Promise<CanvasFile[]> {\n return this.client.paginate<CanvasFile>(`/api/v1/courses/${courseId}/files`)\n }\n\n async listFolders(courseId: number): Promise<CanvasFolder[]> {\n return this.client.paginate<CanvasFolder>(`/api/v1/courses/${courseId}/folders`)\n }\n\n async get(courseId: number, fileId: number): Promise<CanvasFile> {\n return this.client.request<CanvasFile>(`/api/v1/courses/${courseId}/files/${fileId}`)\n }\n\n async upload(\n courseId: number,\n name: string,\n contentBase64: string,\n contentType: string,\n parentFolderPath?: string,\n ): Promise<CanvasFile> {\n const content = Buffer.from(contentBase64, 'base64')\n if (content.toString('base64') !== contentBase64) {\n throw new Error('Invalid base64 content: string is not valid base64 encoding')\n }\n\n // Step 1: Notify Canvas of the pending upload\n const uploadBody: Record<string, string> = {\n name,\n content_type: contentType,\n size: String(content.length),\n }\n if (parentFolderPath) {\n uploadBody.parent_folder_path = parentFolderPath\n }\n\n const uploadInfo = await this.client.request<CanvasFileUploadInfo>(\n `/api/v1/courses/${courseId}/files`,\n { method: 'POST', body: JSON.stringify(uploadBody) },\n )\n\n // Step 2: POST file to upload_url (may be S3 — must NOT add Authorization header)\n const form = new FormData()\n for (const [key, value] of Object.entries(uploadInfo.upload_params)) {\n form.append(key, value)\n }\n form.append('file', new Blob([content], { type: contentType }), name)\n\n // Use redirect: 'manual' so we can handle the 303 → Canvas confirm step ourselves\n let s3Response: Response\n try {\n s3Response = await fetch(uploadInfo.upload_url, {\n method: 'POST',\n body: form,\n redirect: 'manual',\n })\n } catch (networkErr) {\n let hostname: string\n try {\n hostname = new URL(uploadInfo.upload_url).hostname\n } catch {\n hostname = uploadInfo.upload_url\n }\n throw new Error(`Unable to reach file storage (${hostname})`, { cause: networkErr })\n }\n\n // Step 3: If S3 returned a redirect, POST to Canvas confirm URL\n if (s3Response.status >= 300 && s3Response.status < 400) {\n const confirmUrl = s3Response.headers.get('location')\n if (!confirmUrl) {\n throw new Error(`File upload redirect missing Location header (HTTP ${s3Response.status})`)\n }\n let confirmHostname: string\n try {\n confirmHostname = new URL(confirmUrl).hostname\n } catch {\n confirmHostname = ''\n }\n const canvasHostname = new URL(this.client.baseUrl).hostname\n if (confirmHostname !== canvasHostname) {\n throw new Error(\n `File upload redirect points to unexpected host (${confirmHostname}); expected Canvas host (${canvasHostname})`,\n )\n }\n return this.client.request<CanvasFile>(confirmUrl, { method: 'POST' })\n }\n\n if (!s3Response.ok) {\n const body = await s3Response.text()\n throw new Error(\n `File upload to storage failed (HTTP ${s3Response.status}): ${body.slice(0, 500)}`,\n )\n }\n\n const responseText = await s3Response.text()\n let parsed: unknown\n try {\n parsed = JSON.parse(responseText)\n } catch (parseErr) {\n throw new Error(\n `File upload response is not valid JSON (${parseErr instanceof Error ? parseErr.message : String(parseErr)}): ${responseText.slice(0, 500)}`,\n { cause: parseErr },\n )\n }\n if (typeof parsed !== 'object' || parsed === null || !('id' in parsed)) {\n throw new Error(\n `File upload returned unexpected response (no file ID): ${responseText.slice(0, 500)}`,\n )\n }\n return parsed as CanvasFile\n }\n\n async download(fileId: number, courseId?: number): Promise<DownloadedFile> {\n const endpoint =\n courseId != null ? `/api/v1/courses/${courseId}/files/${fileId}` : `/api/v1/files/${fileId}`\n const meta = await this.client.request<CanvasFile>(endpoint)\n\n if (meta.size > MAX_DOWNLOAD_BYTES) {\n throw new Error(\n `File too large to download (${meta.size.toLocaleString()} bytes; limit is 10 MB)`,\n )\n }\n\n const response = await fetch(meta.url)\n if (!response.ok) {\n throw new Error(`Failed to download file content (HTTP ${response.status})`)\n }\n\n const contentLengthHeader = response.headers.get('content-length')\n if (contentLengthHeader != null) {\n const declared = parseInt(contentLengthHeader, 10)\n if (!isNaN(declared) && declared > MAX_DOWNLOAD_BYTES) {\n throw new Error(\n `File too large to download (${declared.toLocaleString()} bytes declared; limit is 10 MB)`,\n )\n }\n }\n\n const contentType = (\n (response.headers.get('content-type') ?? meta.content_type).split(';')[0] ?? ''\n ).trim()\n const filename = meta.display_name ?? meta.filename ?? `file-${fileId}`\n const buffer = await response.arrayBuffer()\n\n if (buffer.byteLength > MAX_DOWNLOAD_BYTES) {\n throw new Error(\n `File too large to download (${buffer.byteLength.toLocaleString()} bytes; limit is 10 MB)`,\n )\n }\n\n if (isTextContentType(contentType)) {\n return {\n type: 'text',\n filename,\n contentType,\n size: buffer.byteLength,\n text: Buffer.from(buffer).toString('utf-8'),\n }\n }\n\n return {\n type: 'resource',\n filename,\n contentType,\n size: buffer.byteLength,\n base64: Buffer.from(buffer).toString('base64'),\n }\n }\n\n async delete(fileId: number): Promise<CanvasFile> {\n return this.client.request<CanvasFile>(`/api/v1/files/${fileId}`, { method: 'DELETE' })\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasQueryParams } from './query'\nimport type { CanvasUpcomingEvent, CanvasUser, CanvasUserProfile } from './types'\n\nexport type CourseUserEnrollmentType = 'student' | 'teacher' | 'ta' | 'observer' | 'designer'\n\nexport type CourseUserEnrollmentState = 'active' | 'invited' | 'rejected' | 'completed' | 'inactive'\n\nexport type CourseUserInclude =\n | 'email'\n | 'enrollments'\n | 'locked'\n | 'avatar_url'\n | 'test_student'\n | 'bio'\n | 'custom_links'\n | 'current_grading_period_scores'\n | 'uuid'\n\nexport type UserSort = 'username' | 'email' | 'sis_id' | 'integration_id' | 'last_login'\n\nexport type SearchUserInclude = 'email' | 'last_login' | 'avatar_url' | 'time_zone' | 'uuid'\n\nexport interface ListCourseUsersOptions {\n enrollment_type?: CourseUserEnrollmentType | ReadonlyArray<CourseUserEnrollmentType>\n enrollment_state?: ReadonlyArray<CourseUserEnrollmentState>\n enrollment_role_id?: number\n include?: ReadonlyArray<CourseUserInclude>\n user_ids?: ReadonlyArray<number | string>\n user_id?: number | string\n search_term?: string\n sort?: UserSort\n order?: 'asc' | 'desc'\n}\n\nexport interface SearchUsersOptions {\n sort?: UserSort\n order?: 'asc' | 'desc'\n include?: ReadonlyArray<SearchUserInclude>\n}\n\nexport class UsersModule {\n constructor(private client: CanvasHttpClient) {}\n\n async listStudents(courseId: number): Promise<CanvasUser[]> {\n return this.client.paginate<CanvasUser>(`/api/v1/courses/${courseId}/users`, {\n enrollment_type: ['student'],\n })\n }\n\n async get(userId: number): Promise<CanvasUser> {\n return this.client.request<CanvasUser>(`/api/v1/users/${userId}`)\n }\n\n async getProfile(): Promise<CanvasUserProfile> {\n return this.client.request<CanvasUserProfile>('/api/v1/users/self/profile')\n }\n\n async searchUsers(\n accountId: number,\n searchTerm: string,\n opts: SearchUsersOptions = {},\n ): Promise<CanvasUser[]> {\n const params: CanvasQueryParams = { search_term: searchTerm }\n if (opts.sort) params.sort = opts.sort\n if (opts.order) params.order = opts.order\n if (opts.include && opts.include.length > 0) params.include = opts.include\n return this.client.paginate<CanvasUser>(`/api/v1/accounts/${accountId}/users`, params)\n }\n\n async listCourseUsers(\n courseId: number,\n opts: ListCourseUsersOptions = {},\n ): Promise<CanvasUser[]> {\n const params: CanvasQueryParams = {}\n if (opts.enrollment_type !== undefined) {\n params.enrollment_type = Array.isArray(opts.enrollment_type)\n ? opts.enrollment_type\n : [opts.enrollment_type]\n }\n if (opts.enrollment_state && opts.enrollment_state.length > 0) {\n params.enrollment_state = opts.enrollment_state\n }\n if (opts.enrollment_role_id !== undefined) params.enrollment_role_id = opts.enrollment_role_id\n if (opts.include && opts.include.length > 0) params.include = opts.include\n if (opts.user_ids && opts.user_ids.length > 0) params.user_ids = opts.user_ids\n if (opts.user_id !== undefined) params.user_id = opts.user_id\n if (opts.search_term) params.search_term = opts.search_term\n if (opts.sort) params.sort = opts.sort\n if (opts.order) params.order = opts.order\n return this.client.paginate<CanvasUser>(`/api/v1/courses/${courseId}/users`, params)\n }\n\n async getUpcomingAssignments(): Promise<CanvasUpcomingEvent[]> {\n return this.client.request<CanvasUpcomingEvent[]>(\n '/api/v1/users/self/upcoming_events?type=Assignment',\n )\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasGroup, CanvasUser } from './types'\n\nexport class GroupsModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(courseId: number): Promise<CanvasGroup[]> {\n return this.client.paginate<CanvasGroup>(`/api/v1/courses/${courseId}/groups`)\n }\n\n async listMembers(groupId: number): Promise<CanvasUser[]> {\n return this.client.paginate<CanvasUser>(`/api/v1/groups/${groupId}/users`)\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasQueryParams } from './query'\nimport type { CanvasEnrollment } from './types'\n\nexport type EnrollmentType =\n | 'StudentEnrollment'\n | 'TeacherEnrollment'\n | 'TaEnrollment'\n | 'DesignerEnrollment'\n | 'ObserverEnrollment'\n\nexport type EnrollmentState =\n | 'active'\n | 'invited'\n | 'creation_pending'\n | 'deleted'\n | 'rejected'\n | 'completed'\n | 'inactive'\n | 'current_and_invited'\n | 'current_and_future'\n | 'current_and_concluded'\n\nexport type EnrollmentInclude =\n | 'avatar_url'\n | 'group_ids'\n | 'locked'\n | 'observed_users'\n | 'can_be_removed'\n | 'uuid'\n | 'current_points'\n | 'grades'\n\nexport interface ListCourseEnrollmentsOptions {\n type?: ReadonlyArray<EnrollmentType>\n role?: ReadonlyArray<string>\n state?: ReadonlyArray<EnrollmentState>\n include?: ReadonlyArray<EnrollmentInclude>\n user_id?: number | string\n grading_period_id?: number\n enrollment_term_id?: number\n sis_account_id?: ReadonlyArray<string>\n sis_course_id?: ReadonlyArray<string>\n sis_section_id?: ReadonlyArray<string>\n sis_user_id?: ReadonlyArray<string>\n created_for_sis_id?: ReadonlyArray<boolean>\n}\n\nexport interface ListUserEnrollmentsOptions {\n type?: ReadonlyArray<EnrollmentType>\n role?: ReadonlyArray<string>\n state?: ReadonlyArray<EnrollmentState>\n include?: ReadonlyArray<EnrollmentInclude>\n grading_period_id?: number\n enrollment_term_id?: number\n}\n\nfunction buildParams(opts: object): CanvasQueryParams {\n const params: CanvasQueryParams = {}\n for (const [key, value] of Object.entries(opts)) {\n if (value === undefined || value === null) continue\n if (Array.isArray(value)) {\n if (value.length === 0) continue\n params[key] = value as ReadonlyArray<string | number | boolean>\n } else if (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ) {\n params[key] = value\n }\n }\n return params\n}\n\nexport class EnrollmentsModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(opts: ListUserEnrollmentsOptions = {}): Promise<CanvasEnrollment[]> {\n return this.client.paginate<CanvasEnrollment>(\n '/api/v1/users/self/enrollments',\n buildParams(opts),\n )\n }\n\n async listForCourse(\n courseId: number,\n opts: ListCourseEnrollmentsOptions = {},\n ): Promise<CanvasEnrollment[]> {\n return this.client.paginate<CanvasEnrollment>(\n `/api/v1/courses/${courseId}/enrollments`,\n buildParams(opts),\n )\n }\n\n async enroll(\n courseId: number,\n userId: number,\n type: string,\n enrollmentState?: string,\n ): Promise<CanvasEnrollment> {\n const body: Record<string, unknown> = {\n enrollment: {\n user_id: userId,\n type,\n ...(enrollmentState ? { enrollment_state: enrollmentState } : {}),\n },\n }\n return this.client.request<CanvasEnrollment>(`/api/v1/courses/${courseId}/enrollments`, {\n method: 'POST',\n body: JSON.stringify(body),\n })\n }\n\n async remove(courseId: number, enrollmentId: number, task: string): Promise<CanvasEnrollment> {\n return this.client.request<CanvasEnrollment>(\n `/api/v1/courses/${courseId}/enrollments/${enrollmentId}?task=${encodeURIComponent(task)}`,\n { method: 'DELETE' },\n )\n }\n\n async listMyGrades(courseId?: number): Promise<CanvasEnrollment[]> {\n if (courseId !== undefined) {\n return this.client.paginate<CanvasEnrollment>(`/api/v1/courses/${courseId}/enrollments`, {\n user_id: 'self',\n include: ['grades'],\n })\n }\n return this.client.paginate<CanvasEnrollment>('/api/v1/users/self/enrollments', {\n include: ['grades'],\n })\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type {\n CanvasDiscussionTopic,\n CanvasDiscussionEntry,\n CanvasAnnouncement,\n CreateDiscussionParams,\n UpdateDiscussionParams,\n} from './types'\n\nexport class DiscussionsModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(courseId: number): Promise<CanvasDiscussionTopic[]> {\n return this.client.paginate<CanvasDiscussionTopic>(\n `/api/v1/courses/${courseId}/discussion_topics`,\n )\n }\n\n async get(courseId: number, topicId: number): Promise<CanvasDiscussionTopic> {\n return this.client.request<CanvasDiscussionTopic>(\n `/api/v1/courses/${courseId}/discussion_topics/${topicId}?include[]=all_dates`,\n )\n }\n\n async listAnnouncements(courseId: number): Promise<CanvasAnnouncement[]> {\n return this.client.paginate<CanvasAnnouncement>(\n `/api/v1/courses/${courseId}/discussion_topics`,\n { only_announcements: 'true' },\n )\n }\n\n async postEntry(\n courseId: number,\n topicId: number,\n message: string,\n ): Promise<CanvasDiscussionEntry> {\n return this.client.request<CanvasDiscussionEntry>(\n `/api/v1/courses/${courseId}/discussion_topics/${topicId}/entries`,\n {\n method: 'POST',\n body: JSON.stringify({ message }),\n },\n )\n }\n\n async create(courseId: number, params: CreateDiscussionParams): Promise<CanvasDiscussionTopic> {\n return this.client.request<CanvasDiscussionTopic>(\n `/api/v1/courses/${courseId}/discussion_topics`,\n {\n method: 'POST',\n body: JSON.stringify(params),\n },\n )\n }\n\n async update(\n courseId: number,\n topicId: number,\n params: UpdateDiscussionParams,\n ): Promise<CanvasDiscussionTopic> {\n return this.client.request<CanvasDiscussionTopic>(\n `/api/v1/courses/${courseId}/discussion_topics/${topicId}`,\n {\n method: 'PUT',\n body: JSON.stringify(params),\n },\n )\n }\n\n async delete(courseId: number, topicId: number): Promise<void> {\n await this.client.request<void>(`/api/v1/courses/${courseId}/discussion_topics/${topicId}`, {\n method: 'DELETE',\n })\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasModule, CanvasModuleItem, CanvasCourseStructure } from './types'\n\nexport class ModulesModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(courseId: number): Promise<CanvasModule[]> {\n return this.client.paginate<CanvasModule>(`/api/v1/courses/${courseId}/modules`)\n }\n\n async get(courseId: number, moduleId: number): Promise<CanvasModule> {\n return this.client.request<CanvasModule>(`/api/v1/courses/${courseId}/modules/${moduleId}`)\n }\n\n async listItems(courseId: number, moduleId: number): Promise<CanvasModuleItem[]> {\n return this.client.paginate<CanvasModuleItem>(\n `/api/v1/courses/${courseId}/modules/${moduleId}/items`,\n )\n }\n\n async create(\n courseId: number,\n params: {\n name: string\n position?: number\n unlock_at?: string\n prerequisite_module_ids?: number[]\n },\n ): Promise<CanvasModule> {\n return this.client.request<CanvasModule>(`/api/v1/courses/${courseId}/modules`, {\n method: 'POST',\n body: JSON.stringify({ module: params }),\n })\n }\n\n async update(\n courseId: number,\n moduleId: number,\n params: { name?: string; position?: number; published?: boolean },\n ): Promise<CanvasModule> {\n return this.client.request<CanvasModule>(`/api/v1/courses/${courseId}/modules/${moduleId}`, {\n method: 'PUT',\n body: JSON.stringify({ module: params }),\n })\n }\n\n async createItem(\n courseId: number,\n moduleId: number,\n params: {\n title: string\n type: string\n content_id?: number\n external_url?: string\n position?: number\n },\n ): Promise<CanvasModuleItem> {\n return this.client.request<CanvasModuleItem>(\n `/api/v1/courses/${courseId}/modules/${moduleId}/items`,\n {\n method: 'POST',\n body: JSON.stringify({ module_item: params }),\n },\n )\n }\n\n async getCourseStructure(\n courseId: number,\n opts: { includePublishedOnly?: boolean; includeContentDetails?: boolean } = {},\n ): Promise<CanvasCourseStructure> {\n const include: string[] = ['items']\n if (opts.includeContentDetails) include.push('content_details')\n\n const modules = await this.client.paginate<CanvasModule & { items?: CanvasModuleItem[] }>(\n `/api/v1/courses/${courseId}/modules`,\n { include },\n )\n\n const itemsByType: Record<string, number> = {}\n let totalItems = 0\n\n const filteredModules = modules.map((mod) => {\n let items = mod.items ?? []\n if (opts.includePublishedOnly) {\n items = items.filter((item) => item.published)\n }\n for (const item of items) {\n itemsByType[item.type] = (itemsByType[item.type] ?? 0) + 1\n totalItems++\n }\n return {\n id: mod.id,\n name: mod.name,\n position: mod.position,\n state: mod.state ?? (mod.published ? 'active' : 'unpublished'),\n unlock_at: mod.unlock_at,\n items: items.map((item) => ({\n id: item.id,\n title: item.title,\n type: item.type,\n position: item.position,\n published: item.published,\n html_url: item.html_url,\n page_url: item.page_url,\n content_id: item.content_id,\n content_details: item.content_details,\n })),\n }\n })\n\n return {\n modules: filteredModules,\n summary: {\n total_modules: filteredModules.length,\n total_items: totalItems,\n items_by_type: itemsByType,\n },\n }\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasPage } from './types'\n\nexport class PagesModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(courseId: number): Promise<CanvasPage[]> {\n return this.client.paginate<CanvasPage>(`/api/v1/courses/${courseId}/pages`)\n }\n\n async get(courseId: number, pageUrl: string): Promise<CanvasPage> {\n return this.client.request<CanvasPage>(\n `/api/v1/courses/${courseId}/pages/${encodeURIComponent(pageUrl)}`,\n )\n }\n\n async create(\n courseId: number,\n params: { title: string; body?: string; published?: boolean; editing_roles?: string },\n ): Promise<CanvasPage> {\n return this.client.request<CanvasPage>(`/api/v1/courses/${courseId}/pages`, {\n method: 'POST',\n body: JSON.stringify({ wiki_page: params }),\n })\n }\n\n async update(\n courseId: number,\n pageUrl: string,\n params: { title?: string; body?: string; published?: boolean; editing_roles?: string },\n ): Promise<CanvasPage> {\n return this.client.request<CanvasPage>(\n `/api/v1/courses/${courseId}/pages/${encodeURIComponent(pageUrl)}`,\n {\n method: 'PUT',\n body: JSON.stringify({ wiki_page: params }),\n },\n )\n }\n\n async delete(courseId: number, pageUrl: string): Promise<void> {\n await this.client.request<void>(\n `/api/v1/courses/${courseId}/pages/${encodeURIComponent(pageUrl)}`,\n { method: 'DELETE' },\n )\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasCalendarEvent } from './types'\n\nexport class CalendarModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(courseId: number): Promise<CanvasCalendarEvent[]> {\n return this.client.paginate<CanvasCalendarEvent>('/api/v1/calendar_events', {\n 'context_codes[]': `course_${courseId}`,\n })\n }\n\n async createEvent(params: {\n context_code: string\n title: string\n start_at: string\n end_at?: string\n description?: string\n location_name?: string\n }): Promise<CanvasCalendarEvent> {\n return this.client.request<CanvasCalendarEvent>('/api/v1/calendar_events', {\n method: 'POST',\n body: JSON.stringify({ calendar_event: params }),\n })\n }\n\n async updateEvent(\n eventId: number,\n params: {\n title?: string\n start_at?: string\n end_at?: string\n description?: string\n location_name?: string\n },\n ): Promise<CanvasCalendarEvent> {\n return this.client.request<CanvasCalendarEvent>(`/api/v1/calendar_events/${eventId}`, {\n method: 'PUT',\n body: JSON.stringify({ calendar_event: params }),\n })\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type {\n CanvasConversation,\n CanvasConversationDetail,\n CanvasConversationUnreadCount,\n} from './types'\n\nexport class ConversationsModule {\n constructor(private client: CanvasHttpClient) {}\n\n async list(): Promise<CanvasConversation[]> {\n return this.client.paginate<CanvasConversation>('/api/v1/conversations')\n }\n\n async get(conversationId: number): Promise<CanvasConversationDetail> {\n return this.client.request<CanvasConversationDetail>(`/api/v1/conversations/${conversationId}`)\n }\n\n async getUnreadCount(): Promise<CanvasConversationUnreadCount> {\n const raw = await this.client.request<{ unread_count: string }>(\n '/api/v1/conversations/unread_count',\n )\n const count = parseInt(raw.unread_count, 10)\n if (Number.isNaN(count)) {\n throw new Error(\n `Canvas returned unexpected unread_count value: ${JSON.stringify(raw.unread_count)}`,\n )\n }\n return { unread_count: count }\n }\n\n async send(recipients: string[], subject: string, body: string): Promise<CanvasConversation[]> {\n return this.client.request<CanvasConversation[]>('/api/v1/conversations', {\n method: 'POST',\n body: JSON.stringify({\n recipients,\n subject,\n body,\n }),\n })\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasPeerReview } from './types'\n\nexport class PeerReviewsModule {\n constructor(private client: CanvasHttpClient) {}\n\n async listForAssignment(courseId: number, assignmentId: number): Promise<CanvasPeerReview[]> {\n return this.client.paginate<CanvasPeerReview>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}/peer_reviews`,\n )\n }\n\n async listForSubmission(\n courseId: number,\n assignmentId: number,\n submissionId: number,\n ): Promise<CanvasPeerReview[]> {\n return this.client.paginate<CanvasPeerReview>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions/${submissionId}/peer_reviews`,\n )\n }\n\n async create(\n courseId: number,\n assignmentId: number,\n submissionId: number,\n userId: number,\n ): Promise<CanvasPeerReview> {\n return this.client.request<CanvasPeerReview>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions/${submissionId}/peer_reviews`,\n {\n method: 'POST',\n body: JSON.stringify({ user_id: userId }),\n },\n )\n }\n\n async delete(\n courseId: number,\n assignmentId: number,\n submissionId: number,\n userId: number,\n ): Promise<void> {\n await this.client.request<void>(\n `/api/v1/courses/${courseId}/assignments/${assignmentId}/submissions/${submissionId}/peer_reviews?user_id=${userId}`,\n { method: 'DELETE' },\n )\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasAccount, CanvasAccountReport, CanvasCourse, CanvasUser } from './types'\n\nexport class AccountsModule {\n constructor(private client: CanvasHttpClient) {}\n\n async get(accountId: number): Promise<CanvasAccount> {\n return this.client.request<CanvasAccount>(`/api/v1/accounts/${accountId}`)\n }\n\n async list(): Promise<CanvasAccount[]> {\n return this.client.paginate<CanvasAccount>('/api/v1/accounts')\n }\n\n async listSubAccounts(accountId: number): Promise<CanvasAccount[]> {\n return this.client.paginate<CanvasAccount>(`/api/v1/accounts/${accountId}/sub_accounts`)\n }\n\n async listCourses(accountId: number, params?: { search_term?: string }): Promise<CanvasCourse[]> {\n const query: Record<string, string> = {}\n if (params?.search_term) query.search_term = params.search_term\n return this.client.paginate<CanvasCourse>(\n `/api/v1/accounts/${accountId}/courses`,\n Object.keys(query).length ? query : undefined,\n )\n }\n\n async listUsers(accountId: number, params?: { search_term?: string }): Promise<CanvasUser[]> {\n const query: Record<string, string> = {}\n if (params?.search_term) query.search_term = params.search_term\n return this.client.paginate<CanvasUser>(\n `/api/v1/accounts/${accountId}/users`,\n Object.keys(query).length ? query : undefined,\n )\n }\n\n async getReports(accountId: number): Promise<CanvasAccountReport[]> {\n return this.client.request<CanvasAccountReport[]>(`/api/v1/accounts/${accountId}/reports`)\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type {\n CanvasCourseActivitySummary,\n CanvasStudentActivitySummary,\n CanvasActivityStreamItem,\n CourseSearchResult,\n} from './types'\n\nexport const SEARCH_CONTENT_TYPES = [\n 'pages',\n 'discussions',\n 'assignments',\n 'announcements',\n] as const\nexport type SearchContentType = (typeof SEARCH_CONTENT_TYPES)[number]\n\nexport class AnalyticsModule {\n constructor(private client: CanvasHttpClient) {}\n\n async searchContentType(\n courseId: number,\n searchTerm: string,\n type: SearchContentType,\n ): Promise<CourseSearchResult[]> {\n switch (type) {\n case 'pages': {\n const pages = await this.client.paginate<{ page_id: number; title: string; url: string }>(\n `/api/v1/courses/${courseId}/pages`,\n { search_term: searchTerm },\n )\n return pages.map((p) => ({\n id: p.page_id,\n title: p.title,\n type: 'page' as const,\n url: p.url,\n course_id: courseId,\n }))\n }\n case 'assignments': {\n const assignments = await this.client.paginate<{ id: number; name: string }>(\n `/api/v1/courses/${courseId}/assignments`,\n { search_term: searchTerm },\n )\n return assignments.map((a) => ({\n id: a.id,\n title: a.name,\n type: 'assignment' as const,\n course_id: courseId,\n }))\n }\n case 'discussions': {\n const discussions = await this.client.paginate<{ id: number; title: string }>(\n `/api/v1/courses/${courseId}/discussion_topics`,\n { search_term: searchTerm },\n )\n return discussions.map((d) => ({\n id: d.id,\n title: d.title,\n type: 'discussion' as const,\n course_id: courseId,\n }))\n }\n case 'announcements': {\n const announcements = await this.client.paginate<{ id: number; title: string }>(\n `/api/v1/courses/${courseId}/discussion_topics`,\n { search_term: searchTerm, only_announcements: 'true' },\n )\n return announcements.map((a) => ({\n id: a.id,\n title: a.title,\n type: 'announcement' as const,\n course_id: courseId,\n }))\n }\n default: {\n const _exhaustive: never = type\n throw new Error(`Unknown content type: ${_exhaustive}`)\n }\n }\n }\n\n async getCourseActivity(courseId: number): Promise<CanvasCourseActivitySummary[]> {\n return this.client.request<CanvasCourseActivitySummary[]>(\n `/api/v1/courses/${courseId}/analytics/activity`,\n )\n }\n\n async getStudentActivity(\n courseId: number,\n studentId: number,\n ): Promise<CanvasStudentActivitySummary> {\n return this.client.request<CanvasStudentActivitySummary>(\n `/api/v1/courses/${courseId}/analytics/users/${studentId}/activity`,\n )\n }\n\n async getCourseActivityStream(courseId: number): Promise<CanvasActivityStreamItem[]> {\n return this.client.request<CanvasActivityStreamItem[]>(\n `/api/v1/courses/${courseId}/activity_stream/summary`,\n )\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type {\n CanvasDashboardCard,\n CanvasTodoItem,\n CanvasUpcomingEvent,\n CanvasMissingSubmission,\n} from './types'\n\nexport class DashboardModule {\n constructor(private client: CanvasHttpClient) {}\n\n async getDashboardCards(): Promise<CanvasDashboardCard[]> {\n return this.client.request<CanvasDashboardCard[]>('/api/v1/dashboard/dashboard_cards')\n }\n\n async getTodoItems(): Promise<CanvasTodoItem[]> {\n return this.client.paginate<CanvasTodoItem>('/api/v1/users/self/todo')\n }\n\n async getUpcomingEvents(): Promise<CanvasUpcomingEvent[]> {\n return this.client.request<CanvasUpcomingEvent[]>('/api/v1/users/self/upcoming_events')\n }\n\n async getMissingSubmissions(): Promise<CanvasMissingSubmission[]> {\n return this.client.paginate<CanvasMissingSubmission>('/api/v1/users/self/missing_submissions')\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type {\n CanvasOutcomeContextType,\n CanvasOutcome,\n CanvasOutcomeAlignment,\n CanvasOutcomeContributingScoresResponse,\n CanvasOutcomeGroup,\n CanvasOutcomeLink,\n CanvasOutcomeMasteryDistributionResponse,\n CanvasOutcomeResultsResponse,\n CanvasOutcomeRollupsResponse,\n} from './types'\n\nexport const OUTCOME_CONTEXT_TYPES = ['account', 'course'] as const\nexport const OUTCOME_DETAIL_LEVELS = ['abbrev', 'full'] as const\nexport const OUTCOME_ROLLUP_AGGREGATE_STATS = ['mean', 'median'] as const\nexport const OUTCOME_ROLLUP_SORT_BY = ['student', 'outcome'] as const\nexport const OUTCOME_SORT_ORDER = ['asc', 'desc'] as const\nexport const OUTCOME_EXCLUDE_OPTIONS = ['missing_user_rollups', 'missing_outcome_results'] as const\n\ntype ContextPathSuffix = 'outcome_groups' | 'outcome_group_links' | 'root_outcome_group'\n\nexport class OutcomesModule {\n constructor(private client: CanvasHttpClient) {}\n\n async getRootOutcomeGroup(\n contextType: CanvasOutcomeContextType,\n contextId: number,\n ): Promise<CanvasOutcomeGroup> {\n return this.client.request<CanvasOutcomeGroup>(this.buildContextPath(contextType, contextId))\n }\n\n async listOutcomeGroups(\n contextType: CanvasOutcomeContextType,\n contextId: number,\n ): Promise<CanvasOutcomeGroup[]> {\n return this.client.paginate<CanvasOutcomeGroup>(\n this.buildContextPath(contextType, contextId, 'outcome_groups'),\n )\n }\n\n async listOutcomeGroupLinks(\n contextType: CanvasOutcomeContextType,\n contextId: number,\n options?: { outcome_style?: 'abbrev' | 'full'; outcome_group_style?: 'abbrev' | 'full' },\n ): Promise<CanvasOutcomeLink[]> {\n return this.client.paginate<CanvasOutcomeLink>(\n this.withQuery(this.buildContextPath(contextType, contextId, 'outcome_group_links'), {\n outcome_style: options?.outcome_style,\n outcome_group_style: options?.outcome_group_style,\n }),\n )\n }\n\n async getOutcomeGroup(\n contextType: CanvasOutcomeContextType,\n contextId: number,\n groupId: number,\n ): Promise<CanvasOutcomeGroup> {\n return this.client.request<CanvasOutcomeGroup>(\n `${this.buildContextPath(contextType, contextId, 'outcome_groups')}/${groupId}`,\n )\n }\n\n async listGroupOutcomes(\n contextType: CanvasOutcomeContextType,\n contextId: number,\n groupId: number,\n options?: { outcome_style?: 'abbrev' | 'full' },\n ): Promise<CanvasOutcomeLink[]> {\n return this.client.paginate<CanvasOutcomeLink>(\n this.withQuery(\n `${this.buildContextPath(contextType, contextId, 'outcome_groups')}/${groupId}/outcomes`,\n { outcome_style: options?.outcome_style },\n ),\n )\n }\n\n async listGroupSubgroups(\n contextType: CanvasOutcomeContextType,\n contextId: number,\n groupId: number,\n ): Promise<CanvasOutcomeGroup[]> {\n return this.client.paginate<CanvasOutcomeGroup>(\n `${this.buildContextPath(contextType, contextId, 'outcome_groups')}/${groupId}/subgroups`,\n )\n }\n\n async getOutcome(\n outcomeId: number,\n options?: { add_defaults?: boolean },\n ): Promise<CanvasOutcome> {\n return this.client.request<CanvasOutcome>(\n this.withQuery(`/api/v1/outcomes/${outcomeId}`, {\n add_defaults: options?.add_defaults,\n }),\n )\n }\n\n async getOutcomeAlignments(\n courseId: number,\n options?: { student_id?: number; assignment_id?: number },\n ): Promise<CanvasOutcomeAlignment[]> {\n return this.client.request<CanvasOutcomeAlignment[]>(\n this.withQuery(`/api/v1/courses/${courseId}/outcome_alignments`, {\n student_id: options?.student_id,\n assignment_id: options?.assignment_id,\n }),\n )\n }\n\n async getOutcomeResults(\n courseId: number,\n options?: {\n user_ids?: Array<number | string>\n outcome_ids?: number[]\n include_alignments?: boolean\n include_hidden?: boolean\n },\n ): Promise<CanvasOutcomeResultsResponse> {\n return this.client.request<CanvasOutcomeResultsResponse>(\n this.withQuery(`/api/v1/courses/${courseId}/outcome_results`, {\n 'user_ids[]': options?.user_ids,\n 'outcome_ids[]': options?.outcome_ids,\n 'include[]': options?.include_alignments ? ['alignments'] : undefined,\n include_hidden: options?.include_hidden,\n }),\n )\n }\n\n async getOutcomeRollups(\n courseId: number,\n options?: {\n aggregate?: 'course'\n aggregate_stat?: 'mean' | 'median'\n user_ids?: Array<number | string>\n outcome_ids?: number[]\n include_courses?: boolean\n exclude?: Array<'missing_user_rollups' | 'missing_outcome_results'>\n sort_by?: 'student' | 'outcome'\n sort_outcome_id?: number\n sort_order?: 'asc' | 'desc'\n add_defaults?: boolean\n },\n ): Promise<CanvasOutcomeRollupsResponse> {\n return this.client.request<CanvasOutcomeRollupsResponse>(\n this.withQuery(`/api/v1/courses/${courseId}/outcome_rollups`, {\n aggregate: options?.aggregate,\n aggregate_stat: options?.aggregate_stat,\n 'user_ids[]': options?.user_ids,\n 'outcome_ids[]': options?.outcome_ids,\n 'include[]': options?.include_courses ? ['courses'] : undefined,\n 'exclude[]': options?.exclude,\n sort_by: options?.sort_by,\n sort_outcome_id: options?.sort_outcome_id,\n sort_order: options?.sort_order,\n add_defaults: options?.add_defaults,\n }),\n )\n }\n\n async getOutcomeContributingScores(\n courseId: number,\n outcomeId: number,\n options?: {\n user_ids?: Array<number | string>\n only_assignment_alignments?: boolean\n show_unpublished_assignments?: boolean\n },\n ): Promise<CanvasOutcomeContributingScoresResponse> {\n return this.client.request<CanvasOutcomeContributingScoresResponse>(\n this.withQuery(`/api/v1/courses/${courseId}/outcomes/${outcomeId}/contributing_scores`, {\n 'user_ids[]': options?.user_ids,\n only_assignment_alignments: options?.only_assignment_alignments,\n show_unpublished_assignments: options?.show_unpublished_assignments,\n }),\n )\n }\n\n async getOutcomeMasteryDistribution(\n courseId: number,\n options?: {\n exclude?: Array<'missing_user_rollups' | 'missing_outcome_results'>\n outcome_ids?: number[]\n student_ids?: Array<number | string>\n include_alignment_distributions?: boolean\n only_assignment_alignments?: boolean\n show_unpublished_assignments?: boolean\n add_defaults?: boolean\n },\n ): Promise<CanvasOutcomeMasteryDistributionResponse> {\n return this.client.request<CanvasOutcomeMasteryDistributionResponse>(\n this.withQuery(`/api/v1/courses/${courseId}/outcome_mastery_distribution`, {\n 'exclude[]': options?.exclude,\n 'outcome_ids[]': options?.outcome_ids,\n 'student_ids[]': options?.student_ids,\n 'include[]': options?.include_alignment_distributions\n ? ['alignment_distributions']\n : undefined,\n only_assignment_alignments: options?.only_assignment_alignments,\n show_unpublished_assignments: options?.show_unpublished_assignments,\n add_defaults: options?.add_defaults,\n }),\n )\n }\n\n private buildContextPath(\n contextType: CanvasOutcomeContextType,\n contextId: number,\n suffix: ContextPathSuffix = 'root_outcome_group',\n ): string {\n const segment = contextType === 'account' ? 'accounts' : 'courses'\n return `/api/v1/${segment}/${contextId}/${suffix}`\n }\n\n private withQuery(\n endpoint: string,\n params: Record<string, string | number | boolean | Array<string | number> | undefined>,\n ): string {\n const searchParams = new URLSearchParams()\n\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined) continue\n if (Array.isArray(value)) {\n for (const item of value) searchParams.append(key, String(item))\n continue\n }\n searchParams.set(key, String(value))\n }\n\n const query = searchParams.toString()\n return query ? `${endpoint}?${query}` : endpoint\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type {\n CanvasGradebookHistoryDay,\n CanvasGradebookHistoryGrader,\n CanvasGradebookHistorySubmission,\n CanvasGradebookHistorySubmissionVersion,\n} from './types'\n\nexport interface GradebookHistoryFeedParams {\n assignment_id?: number\n user_id?: number\n ascending?: boolean\n}\n\nexport class GradebookHistoryModule {\n constructor(private client: CanvasHttpClient) {}\n\n async listDays(courseId: number): Promise<CanvasGradebookHistoryDay[]> {\n return this.client.request<CanvasGradebookHistoryDay[]>(\n `/api/v1/courses/${courseId}/gradebook_history/days`,\n )\n }\n\n async getDay(courseId: number, date: string): Promise<CanvasGradebookHistoryGrader[]> {\n return this.client.request<CanvasGradebookHistoryGrader[]>(\n `/api/v1/courses/${courseId}/gradebook_history/${encodeURIComponent(date)}`,\n )\n }\n\n async listSubmissions(\n courseId: number,\n date: string,\n graderId: number,\n assignmentId: number,\n ): Promise<CanvasGradebookHistorySubmission[]> {\n return this.client.request<CanvasGradebookHistorySubmission[]>(\n `/api/v1/courses/${courseId}/gradebook_history/${encodeURIComponent(date)}/graders/${graderId}/assignments/${assignmentId}/submissions`,\n )\n }\n\n async getFeed(\n courseId: number,\n params: GradebookHistoryFeedParams = {},\n ): Promise<CanvasGradebookHistorySubmissionVersion[]> {\n const searchParams = new URLSearchParams()\n\n if (params.assignment_id !== undefined) {\n searchParams.set('assignment_id', String(params.assignment_id))\n }\n if (params.user_id !== undefined) {\n searchParams.set('user_id', String(params.user_id))\n }\n if (params.ascending !== undefined) {\n searchParams.set('ascending', String(params.ascending))\n }\n\n const query = searchParams.toString()\n\n return this.client.paginate<CanvasGradebookHistorySubmissionVersion>(\n `/api/v1/courses/${courseId}/gradebook_history/feed${query ? `?${query}` : ''}`,\n )\n }\n}\n","import type { CanvasHttpClient } from './client'\nimport type { CanvasNewQuiz, CanvasNewQuizItem } from './types'\n\nexport interface NewQuizPayload {\n title?: string\n instructions?: string | null\n points_possible?: number\n due_at?: string | null\n unlock_at?: string | null\n lock_at?: string | null\n published?: boolean\n}\n\ntype ChoiceItem = {\n interaction_type_slug: 'choice'\n title?: string\n item_body: string\n choices: Array<{ id: string; item_body: string }>\n correct_choice_id: string\n}\n\ntype TrueFalseItem = {\n interaction_type_slug: 'true-false'\n item_body: string\n correct_answer: boolean\n}\n\ntype EssayItem = {\n interaction_type_slug: 'essay'\n item_body: string\n rich_text?: boolean\n word_count_min?: number\n word_count_max?: number\n}\n\ntype MatchingItem = {\n interaction_type_slug: 'matching'\n item_body: string\n matches: Array<{ question: string; answer: string }>\n distractors?: string[]\n}\n\ntype NumericAnswer =\n | { kind: 'exact'; value: number; margin?: number }\n | { kind: 'range'; min: number; max: number }\n | { kind: 'precision'; value: number; precision: number }\n\ntype NumericItem = {\n interaction_type_slug: 'numeric'\n item_body: string\n answers: NumericAnswer[]\n}\n\nexport type NewQuizItemInput = ChoiceItem | TrueFalseItem | EssayItem | MatchingItem | NumericItem\n\nexport interface NewQuizItemCreatePayload {\n position?: number\n points_possible: number\n item: NewQuizItemInput\n}\n\nexport interface NewQuizItemUpdatePayload {\n position?: number\n points_possible?: number\n item?: NewQuizItemInput\n}\n\nexport class NewQuizzesModule {\n constructor(private client: CanvasHttpClient) {}\n\n async create(courseId: number, payload: NewQuizPayload): Promise<CanvasNewQuiz> {\n return this.client.request<CanvasNewQuiz>(`/api/quiz/v1/courses/${courseId}/quizzes`, {\n method: 'POST',\n body: JSON.stringify(payload),\n })\n }\n\n async update(\n courseId: number,\n assignmentId: number,\n patch: NewQuizPayload,\n ): Promise<CanvasNewQuiz> {\n return this.client.request<CanvasNewQuiz>(\n `/api/quiz/v1/courses/${courseId}/quizzes/${assignmentId}`,\n { method: 'PATCH', body: JSON.stringify(patch) },\n )\n }\n\n async delete(courseId: number, assignmentId: number): Promise<void> {\n await this.client.request<void>(`/api/quiz/v1/courses/${courseId}/quizzes/${assignmentId}`, {\n method: 'DELETE',\n })\n }\n\n async listItems(courseId: number, assignmentId: number): Promise<CanvasNewQuizItem[]> {\n return this.client.paginate<CanvasNewQuizItem>(\n `/api/quiz/v1/courses/${courseId}/quizzes/${assignmentId}/items`,\n )\n }\n\n async getItem(\n courseId: number,\n assignmentId: number,\n itemId: string,\n ): Promise<CanvasNewQuizItem> {\n return this.client.request<CanvasNewQuizItem>(\n `/api/quiz/v1/courses/${courseId}/quizzes/${assignmentId}/items/${itemId}`,\n )\n }\n\n async createItem(\n courseId: number,\n assignmentId: number,\n input: NewQuizItemCreatePayload,\n ): Promise<CanvasNewQuizItem> {\n const wireBody: Record<string, unknown> = {\n entry_type: 'Item',\n points_possible: input.points_possible,\n entry: this.toWireItem(input.item),\n }\n if (input.position !== undefined) wireBody.position = input.position\n return this.client.request<CanvasNewQuizItem>(\n `/api/quiz/v1/courses/${courseId}/quizzes/${assignmentId}/items`,\n { method: 'POST', body: JSON.stringify(wireBody) },\n )\n }\n\n async updateItem(\n courseId: number,\n assignmentId: number,\n itemId: string,\n patch: NewQuizItemUpdatePayload,\n ): Promise<CanvasNewQuizItem> {\n const wireBody: Record<string, unknown> = {}\n if (patch.position !== undefined) wireBody.position = patch.position\n if (patch.points_possible !== undefined) wireBody.points_possible = patch.points_possible\n if (patch.item !== undefined) wireBody.entry = this.toWireItem(patch.item)\n return this.client.request<CanvasNewQuizItem>(\n `/api/quiz/v1/courses/${courseId}/quizzes/${assignmentId}/items/${itemId}`,\n { method: 'PATCH', body: JSON.stringify(wireBody) },\n )\n }\n\n async deleteItem(courseId: number, assignmentId: number, itemId: string): Promise<void> {\n await this.client.request<void>(\n `/api/quiz/v1/courses/${courseId}/quizzes/${assignmentId}/items/${itemId}`,\n { method: 'DELETE' },\n )\n }\n\n private toWireItem(item: NewQuizItemInput): Record<string, unknown> {\n switch (item.interaction_type_slug) {\n case 'choice':\n return this.choiceToWire(item)\n case 'true-false':\n return this.trueFalseToWire(item)\n case 'essay':\n return this.essayToWire(item)\n case 'matching':\n return this.matchingToWire(item)\n case 'numeric':\n return this.numericToWire(item)\n }\n }\n\n private choiceToWire(item: ChoiceItem): Record<string, unknown> {\n return {\n interaction_type_slug: 'choice',\n item_body: item.item_body,\n interaction_data: {\n choices: item.choices.map((c, i) => ({\n id: c.id,\n position: i + 1,\n item_body: c.item_body,\n })),\n },\n properties: {\n shuffle_rules: { choices: { shuffled: true } },\n },\n scoring_data: {\n value: item.correct_choice_id,\n },\n // Equivalence hard-coded per CTO direction; not exposed to callers\n scoring_algorithm: 'Equivalence',\n }\n }\n\n private trueFalseToWire(item: TrueFalseItem): Record<string, unknown> {\n return {\n interaction_type_slug: 'true-false',\n item_body: item.item_body,\n interaction_data: {\n choices: [\n { id: 'true', position: 1, item_body: 'True' },\n { id: 'false', position: 2, item_body: 'False' },\n ],\n },\n properties: {},\n scoring_data: {\n value: item.correct_answer ? 'true' : 'false',\n },\n scoring_algorithm: 'Equivalence',\n }\n }\n\n private essayToWire(item: EssayItem): Record<string, unknown> {\n const properties: Record<string, unknown> = {}\n if (item.word_count_min !== undefined || item.word_count_max !== undefined) {\n const wc: Record<string, unknown> = {}\n if (item.word_count_min !== undefined) wc.min = item.word_count_min\n if (item.word_count_max !== undefined) wc.max = item.word_count_max\n properties.word_count = wc\n }\n return {\n interaction_type_slug: 'essay',\n item_body: item.item_body,\n interaction_data: {\n rce_enabled: item.rich_text !== false,\n },\n properties,\n scoring_data: {},\n scoring_algorithm: 'None',\n }\n }\n\n private matchingToWire(item: MatchingItem): Record<string, unknown> {\n const matches = item.matches.map((m, i) => ({\n id: `match_${i}`,\n item_body: m.question,\n }))\n const correctResponses = item.matches.map((m, i) => ({\n id: `resp_${i}`,\n item_body: m.answer,\n }))\n const distractorResponses = (item.distractors ?? []).map((d, i) => ({\n id: `dist_${i}`,\n item_body: d,\n }))\n const scoringValue = item.matches.map((_, i) => ({\n id: `match_${i}`,\n match_id: `resp_${i}`,\n }))\n return {\n interaction_type_slug: 'matching',\n item_body: item.item_body,\n interaction_data: {\n matches,\n responses: [...correctResponses, ...distractorResponses],\n },\n properties: {},\n scoring_data: {\n value: scoringValue,\n },\n scoring_algorithm: 'Equivalence',\n }\n }\n\n private numericToWire(item: NumericItem): Record<string, unknown> {\n const answers = item.answers\n const first = answers[0]\n if (first === undefined) {\n throw new Error('numeric item must have at least one answer')\n }\n let scoringData: Record<string, unknown>\n let scoringAlgorithm: string\n if (first.kind === 'exact') {\n scoringData = { exact_answer: first.value, margin: first.margin ?? 0 }\n scoringAlgorithm = 'Exact'\n } else if (first.kind === 'range') {\n scoringData = { lower_bound: first.min, upper_bound: first.max }\n scoringAlgorithm = 'Range'\n } else {\n scoringData = { exact_answer: first.value, precision: first.precision }\n scoringAlgorithm = 'Precision'\n }\n return {\n interaction_type_slug: 'numeric',\n item_body: item.item_body,\n interaction_data: {},\n properties: {},\n scoring_data: scoringData,\n scoring_algorithm: scoringAlgorithm,\n }\n }\n}\n","import { CanvasHttpClient } from './client'\nimport type { CanvasClientConfig } from './types'\nimport { CoursesModule } from './courses'\nimport { AssignmentsModule } from './assignments'\nimport { SubmissionsModule } from './submissions'\nimport { RubricsModule } from './rubrics'\nimport { QuizzesModule } from './quizzes'\nimport { FilesModule } from './files'\nimport { UsersModule } from './users'\nimport { GroupsModule } from './groups'\nimport { EnrollmentsModule } from './enrollments'\nimport { DiscussionsModule } from './discussions'\nimport { ModulesModule } from './modules'\nimport { PagesModule } from './pages'\nimport { CalendarModule } from './calendar'\nimport { ConversationsModule } from './conversations'\nimport { PeerReviewsModule } from './peer-reviews'\nimport { AccountsModule } from './accounts'\nimport { AnalyticsModule } from './analytics'\nimport { DashboardModule } from './dashboard'\nimport { OutcomesModule } from './outcomes'\nimport { GradebookHistoryModule } from './gradebook-history'\nimport { NewQuizzesModule } from './new-quizzes'\n\n/**\n * Standalone Canvas REST API client. Composed of modular per-domain classes\n * sharing a `CanvasHttpClient`. Throws `CanvasApiError` on Canvas-side failures.\n *\n * NOTE on FERPA mode: pseudonymization is a tool-layer concern provided by the\n * `Pseudonymizer` in `src/pseudonym/`. Library consumers that import this\n * client directly (`canvas-lms-mcp/canvas`) bypass the MCP tool wrappers and\n * therefore receive raw Canvas responses with real student names and contact\n * info. If you need pseudonymized output, either use the MCP server entry\n * points (`canvas-lms-mcp` stdio / `canvas-lms-mcp serve` HTTP) or wrap your\n * own calls with a `Pseudonymizer` instance.\n */\nexport class CanvasClient {\n private client: CanvasHttpClient\n courses: CoursesModule\n assignments: AssignmentsModule\n submissions: SubmissionsModule\n rubrics: RubricsModule\n quizzes: QuizzesModule\n files: FilesModule\n users: UsersModule\n groups: GroupsModule\n enrollments: EnrollmentsModule\n discussions: DiscussionsModule\n modules: ModulesModule\n pages: PagesModule\n calendar: CalendarModule\n conversations: ConversationsModule\n peerReviews: PeerReviewsModule\n accounts: AccountsModule\n analytics: AnalyticsModule\n dashboard: DashboardModule\n outcomes: OutcomesModule\n gradebookHistory: GradebookHistoryModule\n newQuizzes: NewQuizzesModule\n\n constructor(config: CanvasClientConfig) {\n this.client = new CanvasHttpClient(config)\n this.courses = new CoursesModule(this.client)\n this.assignments = new AssignmentsModule(this.client)\n this.submissions = new SubmissionsModule(this.client)\n this.rubrics = new RubricsModule(this.client)\n this.quizzes = new QuizzesModule(this.client)\n this.files = new FilesModule(this.client)\n this.users = new UsersModule(this.client)\n this.groups = new GroupsModule(this.client)\n this.enrollments = new EnrollmentsModule(this.client)\n this.discussions = new DiscussionsModule(this.client)\n this.modules = new ModulesModule(this.client)\n this.pages = new PagesModule(this.client)\n this.calendar = new CalendarModule(this.client)\n this.conversations = new ConversationsModule(this.client)\n this.peerReviews = new PeerReviewsModule(this.client)\n this.accounts = new AccountsModule(this.client)\n this.analytics = new AnalyticsModule(this.client)\n this.dashboard = new DashboardModule(this.client)\n this.outcomes = new OutcomesModule(this.client)\n this.gradebookHistory = new GradebookHistoryModule(this.client)\n this.newQuizzes = new NewQuizzesModule(this.client)\n }\n}\n\nexport { CanvasHttpClient, CanvasApiError } from './client'\nexport type { CanvasRequestOptions } from './client'\nexport { appendCanvasQuery, toCanvasQuery } from './query'\nexport type { CanvasQueryParams, CanvasQueryPrimitive, CanvasQueryValue } from './query'\nexport type * from './types'\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// package.json
|
|
2
|
-
var version = "1.
|
|
2
|
+
var version = "1.22.0";
|
|
3
3
|
|
|
4
4
|
// src/canvas/query.ts
|
|
5
5
|
function appendCanvasQuery(target, params) {
|
|
@@ -163,4 +163,4 @@ export {
|
|
|
163
163
|
CanvasApiError,
|
|
164
164
|
CanvasHttpClient
|
|
165
165
|
};
|
|
166
|
-
//# sourceMappingURL=chunk-
|
|
166
|
+
//# sourceMappingURL=chunk-BDB7U4WG.js.map
|