canvas-lms-mcp 1.15.2 → 1.15.3
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/dist/canvas/index.cjs +2 -2
- package/dist/canvas/index.js +1 -1
- package/dist/{chunk-C2VV5O7H.cjs → chunk-77TPFLVF.cjs} +2 -2
- package/dist/{chunk-C2VV5O7H.cjs.map → chunk-77TPFLVF.cjs.map} +1 -1
- package/dist/{chunk-YUF25C6C.js → chunk-JWBMLL75.js} +2 -2
- package/dist/{chunk-YUF25C6C.js.map → chunk-JWBMLL75.js.map} +1 -1
- package/dist/{chunk-2GUCKHLM.cjs → chunk-MGAEQT6X.cjs} +24 -24
- package/dist/{chunk-2GUCKHLM.cjs.map → chunk-MGAEQT6X.cjs.map} +1 -1
- package/dist/{chunk-X6GSTWAI.js → chunk-V74P7DJD.js} +2 -2
- package/dist/http.cjs +3 -3
- package/dist/http.js +2 -2
- package/dist/server.cjs +3 -3
- package/dist/server.js +2 -2
- package/dist/stdio.cjs +3 -3
- package/dist/stdio.js +2 -2
- package/package.json +1 -1
- /package/dist/{chunk-X6GSTWAI.js.map → chunk-V74P7DJD.js.map} +0 -0
package/dist/canvas/index.cjs
CHANGED
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
var
|
|
7
|
+
var _chunk77TPFLVFcjs = require('../chunk-77TPFLVF.cjs');
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
exports.CanvasApiError =
|
|
14
|
+
exports.CanvasApiError = _chunk77TPFLVFcjs.CanvasApiError; exports.CanvasClient = _chunk77TPFLVFcjs.CanvasClient; exports.CanvasHttpClient = _chunk77TPFLVFcjs.CanvasHttpClient; exports.appendCanvasQuery = _chunk77TPFLVFcjs.appendCanvasQuery; exports.toCanvasQuery = _chunk77TPFLVFcjs.toCanvasQuery;
|
|
15
15
|
//# sourceMappingURL=index.cjs.map
|
package/dist/canvas/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
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; }// package.json
|
|
2
|
-
var version = "1.15.
|
|
2
|
+
var version = "1.15.3";
|
|
3
3
|
|
|
4
4
|
// src/canvas/query.ts
|
|
5
5
|
function appendCanvasQuery(target, params) {
|
|
@@ -1508,4 +1508,4 @@ var CanvasClient = class {
|
|
|
1508
1508
|
|
|
1509
1509
|
|
|
1510
1510
|
exports.version = version; exports.appendCanvasQuery = appendCanvasQuery; exports.toCanvasQuery = toCanvasQuery; exports.CanvasApiError = CanvasApiError; exports.CanvasHttpClient = CanvasHttpClient; 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;
|
|
1511
|
-
//# sourceMappingURL=chunk-
|
|
1511
|
+
//# sourceMappingURL=chunk-77TPFLVF.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/canvas-lms-mcp/canvas-lms-mcp/dist/chunk-C2VV5O7H.cjs","../package.json","../src/canvas/query.ts","../src/canvas/client.ts","../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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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;AAC4C,IAAA;AAC/B,MAAA;AACA,MAAA;AACb,IAAA;AACgC,IAAA;AACnB,MAAA;AACF,MAAA;AACX,IAAA;AACwC,IAAA;AAC1B,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;AACiB,MAAA;AACxB,MAAA;AACc,IAAA;AACO,MAAA;AACrB,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;AxBwoCmD;AACA;AyB74CzB;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;AACE,IAAA;AACA,IAAA;AACF,IAAA;AACA,IAAA;AACJ,IAAA;AACA,IAAA;AACE,IAAA;AACI,IAAA;AACA,IAAA;AACF,IAAA;AACJ,IAAA;AACM,IAAA;AACD,IAAA;AACC,IAAA;AACA,IAAA;AACJ,IAAA;AACA,IAAA;AACI,IAAA;AAClB,IAAA;AACgB,IAAA;AAC9C,EAAA;AACF;AzB84CmD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/canvas-lms-mcp/canvas-lms-mcp/dist/chunk-C2VV5O7H.cjs","sourcesContent":[null,"{\n \"name\": \"canvas-lms-mcp\",\n \"version\": \"1.15.2\",\n \"mcpName\": \"io.github.bruchris/canvas-lms-mcp\",\n \"description\": \"TypeScript MCP 1.x server for Canvas LMS — 115 tools across Canvas courses, assignments, grades, gradebook history, quizzes, New Quizzes (LTI), outcomes, discussions, admin workflows, and more.\",\n \"type\": \"module\",\n \"types\": \"./dist/server.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/server.d.ts\",\n \"import\": \"./dist/server.js\",\n \"require\": \"./dist/server.cjs\"\n },\n \"./stdio\": {\n \"types\": \"./dist/stdio.d.ts\",\n \"import\": \"./dist/stdio.js\",\n \"require\": \"./dist/stdio.cjs\"\n },\n \"./http\": {\n \"types\": \"./dist/http.d.ts\",\n \"import\": \"./dist/http.js\",\n \"require\": \"./dist/http.cjs\"\n },\n \"./canvas\": {\n \"types\": \"./dist/canvas/index.d.ts\",\n \"import\": \"./dist/canvas/index.js\",\n \"require\": \"./dist/canvas/index.cjs\"\n }\n },\n \"bin\": {\n \"canvas-lms-mcp\": \"bin/canvas-lms-mcp.js\"\n },\n \"files\": [\n \"bin/\",\n \"dist/\"\n ],\n \"scripts\": {\n \"dev\": \"tsup --watch\",\n \"build\": \"tsup && tsc -p tsconfig.build.json\",\n \"generate:manifests\": \"tsx scripts/generate-manifests.ts\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"lint\": \"eslint src/ tests/ && prettier --check src/ tests/\",\n \"lint:fix\": \"eslint src/ tests/ --fix && prettier --write src/ tests/\",\n \"typecheck\": \"tsc --noEmit\"\n },\n \"keywords\": [\n \"canvas\",\n \"canvas-lms\",\n \"lms\",\n \"mcp\",\n \"model-context-protocol\",\n \"education\",\n \"grading\",\n \"ai-agent\",\n \"ai-tools\",\n \"claude-desktop\",\n \"cursor\",\n \"vscode\",\n \"instructure\",\n \"education-technology\",\n \"edtech\",\n \"teacher\",\n \"student\",\n \"grading-automation\",\n \"typescript\"\n ],\n \"author\": \"Christian Bru\",\n \"license\": \"MIT\",\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/bruchris/canvas-lms-mcp.git\"\n },\n \"engines\": {\n \"node\": \">=22\"\n },\n \"packageManager\": \"pnpm@10.11.0\",\n \"pnpm\": {\n \"overrides\": {\n \"hono\": \"^4.12.16\",\n \"ip-address\": \"^10.1.1\",\n \"fast-uri\": \"^3.1.2\"\n }\n },\n \"dependencies\": {\n \"@modelcontextprotocol/sdk\": \"^1.29.0\",\n \"zod\": \"^4.4.3\"\n },\n \"devDependencies\": {\n \"@eslint/js\": \"^10.0.1\",\n \"@types/node\": \"^25.8.0\",\n \"@typescript-eslint/eslint-plugin\": \"^8.59.3\",\n \"@typescript-eslint/parser\": \"^8.59.3\",\n \"@vitest/coverage-v8\": \"^4.1.6\",\n \"eslint\": \"^10.3.0\",\n \"eslint-config-prettier\": \"^10.1.8\",\n \"eslint-plugin-prettier\": \"^5.5.5\",\n \"prettier\": \"^3.8.3\",\n \"tsup\": \"^8.5.1\",\n \"tsx\": \"^4.22.0\",\n \"typescript\": \"^6.0.3\",\n \"typescript-eslint\": \"^8.59.3\",\n \"vitest\": \"^4.1.6\"\n }\n}\n","export type CanvasQueryPrimitive = string | number | boolean\n\nexport type CanvasQueryValue = CanvasQueryPrimitive | ReadonlyArray<CanvasQueryPrimitive>\n\nexport type CanvasQueryParams = Record<string, CanvasQueryValue | undefined | null>\n\nexport function appendCanvasQuery(target: URLSearchParams, params?: CanvasQueryParams): void {\n if (!params) return\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined || value === null) continue\n if (Array.isArray(value)) {\n if (value.length === 0) continue\n const arrayKey = key.endsWith('[]') ? key : `${key}[]`\n for (const item of value) {\n if (item === undefined || item === null) continue\n target.append(arrayKey, String(item))\n }\n } else {\n target.set(key, String(value))\n }\n }\n}\n\nexport function toCanvasQuery(params?: CanvasQueryParams): URLSearchParams {\n const search = new URLSearchParams()\n appendCanvasQuery(search, params)\n return search\n}\n","import { version } from '../../package.json'\nimport { appendCanvasQuery, type CanvasQueryParams } from './query'\nimport type { CanvasClientConfig, CanvasErrorResponse } from './types'\n\nexport class CanvasApiError extends Error {\n status: number\n endpoint: string\n\n constructor(message: string, status: number, endpoint: string) {\n super(message)\n this.name = 'CanvasApiError'\n this.status = status\n this.endpoint = endpoint\n }\n}\n\nconst DEFAULT_MAX_PAGINATION_PAGES = 1000\nconst USER_AGENT = `canvas-lms-mcp/${version}`\n\nexport interface CanvasRequestOptions extends RequestInit {\n /**\n * Query params to append to the endpoint URL. Arrays produce repeated\n * `key[]=v1&key[]=v2` entries (the convention Canvas uses for `include[]`).\n * Preserves any query string already present on the endpoint.\n */\n query?: CanvasQueryParams\n}\n\nexport class CanvasHttpClient {\n private token: string\n private _baseUrl: string\n private maxPaginationPages: number\n\n constructor(config: CanvasClientConfig) {\n this.token = config.token\n this._baseUrl = config.baseUrl.replace(/\\/+$/, '')\n this.maxPaginationPages = config.maxPaginationPages ?? DEFAULT_MAX_PAGINATION_PAGES\n }\n\n get baseUrl(): string {\n return this._baseUrl\n }\n\n /**\n * Makes a single authenticated request to the Canvas API.\n * Returns `undefined` (typed as `T`) when Canvas responds with 204 No Content,\n * which is the expected response for DELETE operations.\n */\n async request<T>(endpoint: string, options: CanvasRequestOptions = {}): Promise<T> {\n const { query, ...init } = options\n let url = endpoint.startsWith('http') ? endpoint : `${this._baseUrl}${endpoint}`\n if (query) {\n const parsed = new URL(url)\n appendCanvasQuery(parsed.searchParams, query)\n url = parsed.toString()\n }\n\n const method = (init.method ?? 'GET').toUpperCase()\n if (init.body != null && (method === 'GET' || method === 'HEAD')) {\n throw new Error(\n `GET requests must not include a body (Canvas CloudFront CDN rejects them with 403): ${endpoint}`,\n )\n }\n\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.token}`,\n 'User-Agent': USER_AGENT,\n }\n if (init.body) {\n headers['Content-Type'] = 'application/json'\n }\n\n const response = await fetch(url, {\n ...init,\n headers: {\n ...headers,\n ...init.headers,\n },\n })\n\n if (!response.ok) {\n const body = (await response.json().catch(() => ({}))) as CanvasErrorResponse\n const message =\n body.errors?.[0]?.message ?? body.message ?? `Canvas API error: ${response.status}`\n throw new CanvasApiError(message, response.status, endpoint)\n }\n\n if (response.status === 204) {\n return undefined as T\n }\n\n return response.json() as Promise<T>\n }\n\n async paginate<T>(endpoint: string, params?: CanvasQueryParams): Promise<T[]> {\n const url = new URL(`${this._baseUrl}${endpoint}`)\n if (!url.searchParams.has('per_page')) {\n url.searchParams.set('per_page', '100')\n }\n appendCanvasQuery(url.searchParams, params)\n\n const results: T[] = []\n let nextUrl: string | null = url.toString()\n let pages = 0\n\n while (nextUrl && pages < this.maxPaginationPages) {\n const response = await fetch(nextUrl, {\n headers: {\n Authorization: `Bearer ${this.token}`,\n 'User-Agent': USER_AGENT,\n },\n })\n\n if (!response.ok) {\n const body = (await response.json().catch(() => ({}))) as CanvasErrorResponse\n const message =\n body.errors?.[0]?.message ?? body.message ?? `Canvas API error: ${response.status}`\n throw new CanvasApiError(message, response.status, endpoint)\n }\n\n const data = (await response.json()) as T[]\n results.push(...data)\n pages++\n\n nextUrl = this.parseNextLink(response.headers.get('Link'))\n }\n\n return results\n }\n\n async paginateEnvelope<T>(\n endpoint: string,\n envelopeKey: string,\n params?: CanvasQueryParams,\n ): Promise<T[]> {\n const url = new URL(`${this._baseUrl}${endpoint}`)\n if (!url.searchParams.has('per_page')) {\n url.searchParams.set('per_page', '100')\n }\n appendCanvasQuery(url.searchParams, params)\n\n const results: T[] = []\n let nextUrl: string | null = url.toString()\n let pages = 0\n\n while (nextUrl && pages < this.maxPaginationPages) {\n const response = await fetch(nextUrl, {\n headers: {\n Authorization: `Bearer ${this.token}`,\n 'User-Agent': USER_AGENT,\n },\n })\n\n if (!response.ok) {\n const body = (await response.json().catch(() => ({}))) as CanvasErrorResponse\n const message =\n body.errors?.[0]?.message ?? body.message ?? `Canvas API error: ${response.status}`\n throw new CanvasApiError(message, response.status, endpoint)\n }\n\n const body = (await response.json()) as Record<string, T[]>\n const data = body[envelopeKey] ?? []\n results.push(...data)\n pages++\n\n nextUrl = this.parseNextLink(response.headers.get('Link'))\n }\n\n return results\n }\n\n private parseNextLink(linkHeader: string | null): string | null {\n if (!linkHeader) return null\n const match = linkHeader.match(/<([^>]+)>;\\s*rel=\"next\"/)\n return match?.[1] ?? null\n }\n}\n","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 } 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","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\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-77TPFLVF.cjs","../package.json","../src/canvas/query.ts","../src/canvas/client.ts","../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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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;AAC4C,IAAA;AAC/B,MAAA;AACA,MAAA;AACb,IAAA;AACgC,IAAA;AACnB,MAAA;AACF,MAAA;AACX,IAAA;AACwC,IAAA;AAC1B,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;AACiB,MAAA;AACxB,MAAA;AACc,IAAA;AACO,MAAA;AACrB,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;AxBwoCmD;AACA;AyB74CzB;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;AACE,IAAA;AACA,IAAA;AACF,IAAA;AACA,IAAA;AACJ,IAAA;AACA,IAAA;AACE,IAAA;AACI,IAAA;AACA,IAAA;AACF,IAAA;AACJ,IAAA;AACM,IAAA;AACD,IAAA;AACC,IAAA;AACA,IAAA;AACJ,IAAA;AACA,IAAA;AACI,IAAA;AAClB,IAAA;AACgB,IAAA;AAC9C,EAAA;AACF;AzB84CmD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/canvas-lms-mcp/canvas-lms-mcp/dist/chunk-77TPFLVF.cjs","sourcesContent":[null,"{\n \"name\": \"canvas-lms-mcp\",\n \"version\": \"1.15.3\",\n \"mcpName\": \"io.github.bruchris/canvas-lms-mcp\",\n \"description\": \"TypeScript MCP 1.x server for Canvas LMS — 115 tools across Canvas courses, assignments, grades, gradebook history, quizzes, New Quizzes (LTI), outcomes, discussions, admin workflows, and more.\",\n \"type\": \"module\",\n \"types\": \"./dist/server.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/server.d.ts\",\n \"import\": \"./dist/server.js\",\n \"require\": \"./dist/server.cjs\"\n },\n \"./stdio\": {\n \"types\": \"./dist/stdio.d.ts\",\n \"import\": \"./dist/stdio.js\",\n \"require\": \"./dist/stdio.cjs\"\n },\n \"./http\": {\n \"types\": \"./dist/http.d.ts\",\n \"import\": \"./dist/http.js\",\n \"require\": \"./dist/http.cjs\"\n },\n \"./canvas\": {\n \"types\": \"./dist/canvas/index.d.ts\",\n \"import\": \"./dist/canvas/index.js\",\n \"require\": \"./dist/canvas/index.cjs\"\n }\n },\n \"bin\": {\n \"canvas-lms-mcp\": \"bin/canvas-lms-mcp.js\"\n },\n \"files\": [\n \"bin/\",\n \"dist/\"\n ],\n \"scripts\": {\n \"dev\": \"tsup --watch\",\n \"build\": \"tsup && tsc -p tsconfig.build.json\",\n \"generate:manifests\": \"tsx scripts/generate-manifests.ts\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"lint\": \"eslint src/ tests/ && prettier --check src/ tests/\",\n \"lint:fix\": \"eslint src/ tests/ --fix && prettier --write src/ tests/\",\n \"typecheck\": \"tsc --noEmit\"\n },\n \"keywords\": [\n \"canvas\",\n \"canvas-lms\",\n \"lms\",\n \"mcp\",\n \"model-context-protocol\",\n \"education\",\n \"grading\",\n \"ai-agent\",\n \"ai-tools\",\n \"claude-desktop\",\n \"cursor\",\n \"vscode\",\n \"instructure\",\n \"education-technology\",\n \"edtech\",\n \"teacher\",\n \"student\",\n \"grading-automation\",\n \"typescript\"\n ],\n \"author\": \"Christian Bru\",\n \"license\": \"MIT\",\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/bruchris/canvas-lms-mcp.git\"\n },\n \"engines\": {\n \"node\": \">=22\"\n },\n \"packageManager\": \"pnpm@10.11.0\",\n \"pnpm\": {\n \"overrides\": {\n \"hono\": \"^4.12.16\",\n \"ip-address\": \"^10.1.1\",\n \"fast-uri\": \"^3.1.2\"\n }\n },\n \"dependencies\": {\n \"@modelcontextprotocol/sdk\": \"^1.29.0\",\n \"zod\": \"^4.4.3\"\n },\n \"devDependencies\": {\n \"@eslint/js\": \"^10.0.1\",\n \"@types/node\": \"^25.8.0\",\n \"@typescript-eslint/eslint-plugin\": \"^8.59.3\",\n \"@typescript-eslint/parser\": \"^8.59.3\",\n \"@vitest/coverage-v8\": \"^4.1.6\",\n \"eslint\": \"^10.3.0\",\n \"eslint-config-prettier\": \"^10.1.8\",\n \"eslint-plugin-prettier\": \"^5.5.5\",\n \"prettier\": \"^3.8.3\",\n \"tsup\": \"^8.5.1\",\n \"tsx\": \"^4.22.0\",\n \"typescript\": \"^6.0.3\",\n \"typescript-eslint\": \"^8.59.3\",\n \"vitest\": \"^4.1.6\"\n }\n}\n","export type CanvasQueryPrimitive = string | number | boolean\n\nexport type CanvasQueryValue = CanvasQueryPrimitive | ReadonlyArray<CanvasQueryPrimitive>\n\nexport type CanvasQueryParams = Record<string, CanvasQueryValue | undefined | null>\n\nexport function appendCanvasQuery(target: URLSearchParams, params?: CanvasQueryParams): void {\n if (!params) return\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined || value === null) continue\n if (Array.isArray(value)) {\n if (value.length === 0) continue\n const arrayKey = key.endsWith('[]') ? key : `${key}[]`\n for (const item of value) {\n if (item === undefined || item === null) continue\n target.append(arrayKey, String(item))\n }\n } else {\n target.set(key, String(value))\n }\n }\n}\n\nexport function toCanvasQuery(params?: CanvasQueryParams): URLSearchParams {\n const search = new URLSearchParams()\n appendCanvasQuery(search, params)\n return search\n}\n","import { version } from '../../package.json'\nimport { appendCanvasQuery, type CanvasQueryParams } from './query'\nimport type { CanvasClientConfig, CanvasErrorResponse } from './types'\n\nexport class CanvasApiError extends Error {\n status: number\n endpoint: string\n\n constructor(message: string, status: number, endpoint: string) {\n super(message)\n this.name = 'CanvasApiError'\n this.status = status\n this.endpoint = endpoint\n }\n}\n\nconst DEFAULT_MAX_PAGINATION_PAGES = 1000\nconst USER_AGENT = `canvas-lms-mcp/${version}`\n\nexport interface CanvasRequestOptions extends RequestInit {\n /**\n * Query params to append to the endpoint URL. Arrays produce repeated\n * `key[]=v1&key[]=v2` entries (the convention Canvas uses for `include[]`).\n * Preserves any query string already present on the endpoint.\n */\n query?: CanvasQueryParams\n}\n\nexport class CanvasHttpClient {\n private token: string\n private _baseUrl: string\n private maxPaginationPages: number\n\n constructor(config: CanvasClientConfig) {\n this.token = config.token\n this._baseUrl = config.baseUrl.replace(/\\/+$/, '')\n this.maxPaginationPages = config.maxPaginationPages ?? DEFAULT_MAX_PAGINATION_PAGES\n }\n\n get baseUrl(): string {\n return this._baseUrl\n }\n\n /**\n * Makes a single authenticated request to the Canvas API.\n * Returns `undefined` (typed as `T`) when Canvas responds with 204 No Content,\n * which is the expected response for DELETE operations.\n */\n async request<T>(endpoint: string, options: CanvasRequestOptions = {}): Promise<T> {\n const { query, ...init } = options\n let url = endpoint.startsWith('http') ? endpoint : `${this._baseUrl}${endpoint}`\n if (query) {\n const parsed = new URL(url)\n appendCanvasQuery(parsed.searchParams, query)\n url = parsed.toString()\n }\n\n const method = (init.method ?? 'GET').toUpperCase()\n if (init.body != null && (method === 'GET' || method === 'HEAD')) {\n throw new Error(\n `GET requests must not include a body (Canvas CloudFront CDN rejects them with 403): ${endpoint}`,\n )\n }\n\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.token}`,\n 'User-Agent': USER_AGENT,\n }\n if (init.body) {\n headers['Content-Type'] = 'application/json'\n }\n\n const response = await fetch(url, {\n ...init,\n headers: {\n ...headers,\n ...init.headers,\n },\n })\n\n if (!response.ok) {\n const body = (await response.json().catch(() => ({}))) as CanvasErrorResponse\n const message =\n body.errors?.[0]?.message ?? body.message ?? `Canvas API error: ${response.status}`\n throw new CanvasApiError(message, response.status, endpoint)\n }\n\n if (response.status === 204) {\n return undefined as T\n }\n\n return response.json() as Promise<T>\n }\n\n async paginate<T>(endpoint: string, params?: CanvasQueryParams): Promise<T[]> {\n const url = new URL(`${this._baseUrl}${endpoint}`)\n if (!url.searchParams.has('per_page')) {\n url.searchParams.set('per_page', '100')\n }\n appendCanvasQuery(url.searchParams, params)\n\n const results: T[] = []\n let nextUrl: string | null = url.toString()\n let pages = 0\n\n while (nextUrl && pages < this.maxPaginationPages) {\n const response = await fetch(nextUrl, {\n headers: {\n Authorization: `Bearer ${this.token}`,\n 'User-Agent': USER_AGENT,\n },\n })\n\n if (!response.ok) {\n const body = (await response.json().catch(() => ({}))) as CanvasErrorResponse\n const message =\n body.errors?.[0]?.message ?? body.message ?? `Canvas API error: ${response.status}`\n throw new CanvasApiError(message, response.status, endpoint)\n }\n\n const data = (await response.json()) as T[]\n results.push(...data)\n pages++\n\n nextUrl = this.parseNextLink(response.headers.get('Link'))\n }\n\n return results\n }\n\n async paginateEnvelope<T>(\n endpoint: string,\n envelopeKey: string,\n params?: CanvasQueryParams,\n ): Promise<T[]> {\n const url = new URL(`${this._baseUrl}${endpoint}`)\n if (!url.searchParams.has('per_page')) {\n url.searchParams.set('per_page', '100')\n }\n appendCanvasQuery(url.searchParams, params)\n\n const results: T[] = []\n let nextUrl: string | null = url.toString()\n let pages = 0\n\n while (nextUrl && pages < this.maxPaginationPages) {\n const response = await fetch(nextUrl, {\n headers: {\n Authorization: `Bearer ${this.token}`,\n 'User-Agent': USER_AGENT,\n },\n })\n\n if (!response.ok) {\n const body = (await response.json().catch(() => ({}))) as CanvasErrorResponse\n const message =\n body.errors?.[0]?.message ?? body.message ?? `Canvas API error: ${response.status}`\n throw new CanvasApiError(message, response.status, endpoint)\n }\n\n const body = (await response.json()) as Record<string, T[]>\n const data = body[envelopeKey] ?? []\n results.push(...data)\n pages++\n\n nextUrl = this.parseNextLink(response.headers.get('Link'))\n }\n\n return results\n }\n\n private parseNextLink(linkHeader: string | null): string | null {\n if (!linkHeader) return null\n const match = linkHeader.match(/<([^>]+)>;\\s*rel=\"next\"/)\n return match?.[1] ?? null\n }\n}\n","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 } 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","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\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.15.
|
|
2
|
+
var version = "1.15.3";
|
|
3
3
|
|
|
4
4
|
// src/canvas/query.ts
|
|
5
5
|
function appendCanvasQuery(target, params) {
|
|
@@ -1508,4 +1508,4 @@ export {
|
|
|
1508
1508
|
OUTCOME_EXCLUDE_OPTIONS,
|
|
1509
1509
|
CanvasClient
|
|
1510
1510
|
};
|
|
1511
|
-
//# sourceMappingURL=chunk-
|
|
1511
|
+
//# sourceMappingURL=chunk-JWBMLL75.js.map
|