UNPKG

@bhavjit/khan-api

Version:

A Khan Academy API client and wrapper

1,641 lines (1,617 loc) 117 kB
/** * Khan API v0.7.4 * https://khan-api.bhavjit.com * Licensed under MIT license * Copyright 2025 Bhavjit Chauhan */ var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __typeError = (msg) => { throw TypeError(msg); }; var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg); var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value); var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method); // node_modules/cross-fetch/dist/browser-ponyfill.js var require_browser_ponyfill = __commonJS({ "node_modules/cross-fetch/dist/browser-ponyfill.js"(exports, module) { var __global__ = typeof globalThis !== "undefined" && globalThis || typeof self !== "undefined" && self || typeof global !== "undefined" && global; var __globalThis__ = function() { function F() { this.fetch = false; this.DOMException = __global__.DOMException; } F.prototype = __global__; return new F(); }(); (function(globalThis2) { var irrelevant = function(exports2) { var g = typeof globalThis2 !== "undefined" && globalThis2 || typeof self !== "undefined" && self || // eslint-disable-next-line no-undef typeof global !== "undefined" && global || {}; var support = { searchParams: "URLSearchParams" in g, iterable: "Symbol" in g && "iterator" in Symbol, blob: "FileReader" in g && "Blob" in g && function() { try { new Blob(); return true; } catch (e) { return false; } }(), formData: "FormData" in g, arrayBuffer: "ArrayBuffer" in g }; function isDataView(obj) { return obj && DataView.prototype.isPrototypeOf(obj); } if (support.arrayBuffer) { var viewClasses = [ "[object Int8Array]", "[object Uint8Array]", "[object Uint8ClampedArray]", "[object Int16Array]", "[object Uint16Array]", "[object Int32Array]", "[object Uint32Array]", "[object Float32Array]", "[object Float64Array]" ]; var isArrayBufferView = ArrayBuffer.isView || function(obj) { return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1; }; } function normalizeName(name) { if (typeof name !== "string") { name = String(name); } if (/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(name) || name === "") { throw new TypeError('Invalid character in header field name: "' + name + '"'); } return name.toLowerCase(); } function normalizeValue(value) { if (typeof value !== "string") { value = String(value); } return value; } function iteratorFor(items) { var iterator = { next: function() { var value = items.shift(); return { done: value === void 0, value }; } }; if (support.iterable) { iterator[Symbol.iterator] = function() { return iterator; }; } return iterator; } function Headers(headers) { this.map = {}; if (headers instanceof Headers) { headers.forEach(function(value, name) { this.append(name, value); }, this); } else if (Array.isArray(headers)) { headers.forEach(function(header) { if (header.length != 2) { throw new TypeError("Headers constructor: expected name/value pair to be length 2, found" + header.length); } this.append(header[0], header[1]); }, this); } else if (headers) { Object.getOwnPropertyNames(headers).forEach(function(name) { this.append(name, headers[name]); }, this); } } Headers.prototype.append = function(name, value) { name = normalizeName(name); value = normalizeValue(value); var oldValue = this.map[name]; this.map[name] = oldValue ? oldValue + ", " + value : value; }; Headers.prototype["delete"] = function(name) { delete this.map[normalizeName(name)]; }; Headers.prototype.get = function(name) { name = normalizeName(name); return this.has(name) ? this.map[name] : null; }; Headers.prototype.has = function(name) { return this.map.hasOwnProperty(normalizeName(name)); }; Headers.prototype.set = function(name, value) { this.map[normalizeName(name)] = normalizeValue(value); }; Headers.prototype.forEach = function(callback, thisArg) { for (var name in this.map) { if (this.map.hasOwnProperty(name)) { callback.call(thisArg, this.map[name], name, this); } } }; Headers.prototype.keys = function() { var items = []; this.forEach(function(value, name) { items.push(name); }); return iteratorFor(items); }; Headers.prototype.values = function() { var items = []; this.forEach(function(value) { items.push(value); }); return iteratorFor(items); }; Headers.prototype.entries = function() { var items = []; this.forEach(function(value, name) { items.push([name, value]); }); return iteratorFor(items); }; if (support.iterable) { Headers.prototype[Symbol.iterator] = Headers.prototype.entries; } function consumed(body) { if (body._noBody) return; if (body.bodyUsed) { return Promise.reject(new TypeError("Already read")); } body.bodyUsed = true; } function fileReaderReady(reader) { return new Promise(function(resolve, reject) { reader.onload = function() { resolve(reader.result); }; reader.onerror = function() { reject(reader.error); }; }); } function readBlobAsArrayBuffer(blob) { var reader = new FileReader(); var promise = fileReaderReady(reader); reader.readAsArrayBuffer(blob); return promise; } function readBlobAsText(blob) { var reader = new FileReader(); var promise = fileReaderReady(reader); var match = /charset=([A-Za-z0-9_-]+)/.exec(blob.type); var encoding = match ? match[1] : "utf-8"; reader.readAsText(blob, encoding); return promise; } function readArrayBufferAsText(buf) { var view = new Uint8Array(buf); var chars = new Array(view.length); for (var i = 0; i < view.length; i++) { chars[i] = String.fromCharCode(view[i]); } return chars.join(""); } function bufferClone(buf) { if (buf.slice) { return buf.slice(0); } else { var view = new Uint8Array(buf.byteLength); view.set(new Uint8Array(buf)); return view.buffer; } } function Body() { this.bodyUsed = false; this._initBody = function(body) { this.bodyUsed = this.bodyUsed; this._bodyInit = body; if (!body) { this._noBody = true; this._bodyText = ""; } else if (typeof body === "string") { this._bodyText = body; } else if (support.blob && Blob.prototype.isPrototypeOf(body)) { this._bodyBlob = body; } else if (support.formData && FormData.prototype.isPrototypeOf(body)) { this._bodyFormData = body; } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { this._bodyText = body.toString(); } else if (support.arrayBuffer && support.blob && isDataView(body)) { this._bodyArrayBuffer = bufferClone(body.buffer); this._bodyInit = new Blob([this._bodyArrayBuffer]); } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) { this._bodyArrayBuffer = bufferClone(body); } else { this._bodyText = body = Object.prototype.toString.call(body); } if (!this.headers.get("content-type")) { if (typeof body === "string") { this.headers.set("content-type", "text/plain;charset=UTF-8"); } else if (this._bodyBlob && this._bodyBlob.type) { this.headers.set("content-type", this._bodyBlob.type); } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { this.headers.set("content-type", "application/x-www-form-urlencoded;charset=UTF-8"); } } }; if (support.blob) { this.blob = function() { var rejected = consumed(this); if (rejected) { return rejected; } if (this._bodyBlob) { return Promise.resolve(this._bodyBlob); } else if (this._bodyArrayBuffer) { return Promise.resolve(new Blob([this._bodyArrayBuffer])); } else if (this._bodyFormData) { throw new Error("could not read FormData body as blob"); } else { return Promise.resolve(new Blob([this._bodyText])); } }; } this.arrayBuffer = function() { if (this._bodyArrayBuffer) { var isConsumed = consumed(this); if (isConsumed) { return isConsumed; } else if (ArrayBuffer.isView(this._bodyArrayBuffer)) { return Promise.resolve( this._bodyArrayBuffer.buffer.slice( this._bodyArrayBuffer.byteOffset, this._bodyArrayBuffer.byteOffset + this._bodyArrayBuffer.byteLength ) ); } else { return Promise.resolve(this._bodyArrayBuffer); } } else if (support.blob) { return this.blob().then(readBlobAsArrayBuffer); } else { throw new Error("could not read as ArrayBuffer"); } }; this.text = function() { var rejected = consumed(this); if (rejected) { return rejected; } if (this._bodyBlob) { return readBlobAsText(this._bodyBlob); } else if (this._bodyArrayBuffer) { return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer)); } else if (this._bodyFormData) { throw new Error("could not read FormData body as text"); } else { return Promise.resolve(this._bodyText); } }; if (support.formData) { this.formData = function() { return this.text().then(decode); }; } this.json = function() { return this.text().then(JSON.parse); }; return this; } var methods = ["CONNECT", "DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT", "TRACE"]; function normalizeMethod(method) { var upcased = method.toUpperCase(); return methods.indexOf(upcased) > -1 ? upcased : method; } function Request(input, options) { if (!(this instanceof Request)) { throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.'); } options = options || {}; var body = options.body; if (input instanceof Request) { if (input.bodyUsed) { throw new TypeError("Already read"); } this.url = input.url; this.credentials = input.credentials; if (!options.headers) { this.headers = new Headers(input.headers); } this.method = input.method; this.mode = input.mode; this.signal = input.signal; if (!body && input._bodyInit != null) { body = input._bodyInit; input.bodyUsed = true; } } else { this.url = String(input); } this.credentials = options.credentials || this.credentials || "same-origin"; if (options.headers || !this.headers) { this.headers = new Headers(options.headers); } this.method = normalizeMethod(options.method || this.method || "GET"); this.mode = options.mode || this.mode || null; this.signal = options.signal || this.signal || function() { if ("AbortController" in g) { var ctrl = new AbortController(); return ctrl.signal; } }(); this.referrer = null; if ((this.method === "GET" || this.method === "HEAD") && body) { throw new TypeError("Body not allowed for GET or HEAD requests"); } this._initBody(body); if (this.method === "GET" || this.method === "HEAD") { if (options.cache === "no-store" || options.cache === "no-cache") { var reParamSearch = /([?&])_=[^&]*/; if (reParamSearch.test(this.url)) { this.url = this.url.replace(reParamSearch, "$1_=" + (/* @__PURE__ */ new Date()).getTime()); } else { var reQueryString = /\?/; this.url += (reQueryString.test(this.url) ? "&" : "?") + "_=" + (/* @__PURE__ */ new Date()).getTime(); } } } } Request.prototype.clone = function() { return new Request(this, { body: this._bodyInit }); }; function decode(body) { var form = new FormData(); body.trim().split("&").forEach(function(bytes) { if (bytes) { var split = bytes.split("="); var name = split.shift().replace(/\+/g, " "); var value = split.join("=").replace(/\+/g, " "); form.append(decodeURIComponent(name), decodeURIComponent(value)); } }); return form; } function parseHeaders(rawHeaders) { var headers = new Headers(); var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, " "); preProcessedHeaders.split("\r").map(function(header) { return header.indexOf("\n") === 0 ? header.substr(1, header.length) : header; }).forEach(function(line) { var parts = line.split(":"); var key = parts.shift().trim(); if (key) { var value = parts.join(":").trim(); try { headers.append(key, value); } catch (error) { console.warn("Response " + error.message); } } }); return headers; } Body.call(Request.prototype); function Response(bodyInit, options) { if (!(this instanceof Response)) { throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.'); } if (!options) { options = {}; } this.type = "default"; this.status = options.status === void 0 ? 200 : options.status; if (this.status < 200 || this.status > 599) { throw new RangeError("Failed to construct 'Response': The status provided (0) is outside the range [200, 599]."); } this.ok = this.status >= 200 && this.status < 300; this.statusText = options.statusText === void 0 ? "" : "" + options.statusText; this.headers = new Headers(options.headers); this.url = options.url || ""; this._initBody(bodyInit); } Body.call(Response.prototype); Response.prototype.clone = function() { return new Response(this._bodyInit, { status: this.status, statusText: this.statusText, headers: new Headers(this.headers), url: this.url }); }; Response.error = function() { var response = new Response(null, { status: 200, statusText: "" }); response.ok = false; response.status = 0; response.type = "error"; return response; }; var redirectStatuses = [301, 302, 303, 307, 308]; Response.redirect = function(url, status) { if (redirectStatuses.indexOf(status) === -1) { throw new RangeError("Invalid status code"); } return new Response(null, { status, headers: { location: url } }); }; exports2.DOMException = g.DOMException; try { new exports2.DOMException(); } catch (err) { exports2.DOMException = function(message, name) { this.message = message; this.name = name; var error = Error(message); this.stack = error.stack; }; exports2.DOMException.prototype = Object.create(Error.prototype); exports2.DOMException.prototype.constructor = exports2.DOMException; } function fetch2(input, init) { return new Promise(function(resolve, reject) { var request = new Request(input, init); if (request.signal && request.signal.aborted) { return reject(new exports2.DOMException("Aborted", "AbortError")); } var xhr = new XMLHttpRequest(); function abortXhr() { xhr.abort(); } xhr.onload = function() { var options = { statusText: xhr.statusText, headers: parseHeaders(xhr.getAllResponseHeaders() || "") }; if (request.url.indexOf("file://") === 0 && (xhr.status < 200 || xhr.status > 599)) { options.status = 200; } else { options.status = xhr.status; } options.url = "responseURL" in xhr ? xhr.responseURL : options.headers.get("X-Request-URL"); var body = "response" in xhr ? xhr.response : xhr.responseText; setTimeout(function() { resolve(new Response(body, options)); }, 0); }; xhr.onerror = function() { setTimeout(function() { reject(new TypeError("Network request failed")); }, 0); }; xhr.ontimeout = function() { setTimeout(function() { reject(new TypeError("Network request timed out")); }, 0); }; xhr.onabort = function() { setTimeout(function() { reject(new exports2.DOMException("Aborted", "AbortError")); }, 0); }; function fixUrl(url) { try { return url === "" && g.location.href ? g.location.href : url; } catch (e) { return url; } } xhr.open(request.method, fixUrl(request.url), true); if (request.credentials === "include") { xhr.withCredentials = true; } else if (request.credentials === "omit") { xhr.withCredentials = false; } if ("responseType" in xhr) { if (support.blob) { xhr.responseType = "blob"; } else if (support.arrayBuffer) { xhr.responseType = "arraybuffer"; } } if (init && typeof init.headers === "object" && !(init.headers instanceof Headers || g.Headers && init.headers instanceof g.Headers)) { var names = []; Object.getOwnPropertyNames(init.headers).forEach(function(name) { names.push(normalizeName(name)); xhr.setRequestHeader(name, normalizeValue(init.headers[name])); }); request.headers.forEach(function(value, name) { if (names.indexOf(name) === -1) { xhr.setRequestHeader(name, value); } }); } else { request.headers.forEach(function(value, name) { xhr.setRequestHeader(name, value); }); } if (request.signal) { request.signal.addEventListener("abort", abortXhr); xhr.onreadystatechange = function() { if (xhr.readyState === 4) { request.signal.removeEventListener("abort", abortXhr); } }; } xhr.send(typeof request._bodyInit === "undefined" ? null : request._bodyInit); }); } fetch2.polyfill = true; if (!g.fetch) { g.fetch = fetch2; g.Headers = Headers; g.Request = Request; g.Response = Response; } exports2.Headers = Headers; exports2.Request = Request; exports2.Response = Response; exports2.fetch = fetch2; return exports2; }({}); })(__globalThis__); __globalThis__.fetch.ponyfill = true; delete __globalThis__.fetch.polyfill; var ctx = __global__.fetch ? __global__ : __globalThis__; exports = ctx.fetch; exports.default = ctx.fetch; exports.fetch = ctx.fetch; exports.Headers = ctx.Headers; exports.Request = ctx.Request; exports.Response = ctx.Response; module.exports = exports; } }); // src/queries/index.ts var queries_exports = {}; __export(queries_exports, { AvatarDataForProfile: () => AvatarDataForProfile, FeedbackQuery: () => FeedbackQuery, GetFeedbackReplies: () => GetFeedbackReplies, GetFeedbackRepliesPage: () => GetFeedbackRepliesPage, GetFullUserProfile: () => GetFullUserProfile, GetProfileWidgets: () => GetProfileWidgets, GetUserByUsernameOrEmail: () => GetUserByUsernameOrEmail, GetUserHoverCardProfile: () => GetUserHoverCardProfile, Hotlist: () => Hotlist, ProgramQuery: () => ProgramQuery, ProjectsAuthoredByUser: () => ProjectsAuthoredByUser, QAExpandKeyInfo: () => QAExpandKeyInfo, QaExpandKeyInfo: () => QaExpandKeyInfo, avatarDataForProfile: () => avatarDataForProfile, feedbackQuery: () => feedbackQuery, getFeedbackReplies: () => getFeedbackReplies, getFeedbackRepliesPage: () => getFeedbackRepliesPage, getFullUserProfile: () => getFullUserProfile, getProfileWidgets: () => getProfileWidgets, getUserByUsernameOrEmail: () => getUserByUsernameOrEmail, getUserHoverCardProfile: () => getUserHoverCardProfile, hotlist: () => hotlist, programQuery: () => programQuery, projectsAuthoredByUser: () => projectsAuthoredByUser }); // src/lib/constants.ts var KHAN_GRAPHQL_URL = "https://www.khanacademy.org/api/internal/graphql"; var SAFELIST_URL = "https://cdn.jsdelivr.net/gh/bhavjitChauhan/khan-api@safelist"; var PROXY_DOMAIN = "khan-proxy.bhavjit.com"; var FKEY = `bhavjitchauhan/khan-api_${Date.now()}`; var PLACEHOLDER_PROGRAM_ID = 4669512406581248; // src/utils/fetch.ts var import_cross_fetch = __toESM(require_browser_ponyfill(), 1); // src/utils/safelist.ts async function getLatestQuery(query) { const response = await http(`${SAFELIST_URL}/query/${query}`); if (response.status === 404) return null; const text = await response.text(); return text; } async function getLatestMutation(mutation) { const response = await http(`${SAFELIST_URL}/mutation/${mutation}`); if (response.status === 404) return null; const text = await response.text(); return text; } async function getLatestFragment(fragment) { const response = await http(`${SAFELIST_URL}/fragment/${fragment}`); if (response.status === 404) return null; const text = await response.text(); return text; } function hashQuery(document) { let hash = 5381, i = document.length; while (i) hash = hash * 33 ^ document.charCodeAt(--i); return hash >>> 0; } async function getLatestQueryHash(query) { const text = await getLatestQuery(query); if (!text) return null; return hashQuery(text); } // src/utils/fetch.ts async function http(url, init) { return await (0, import_cross_fetch.default)(url, init); } async function get(url, init) { init = { method: "get", ...init }; return await http(url, init); } async function post(url, body, init) { init = { method: "post", body: JSON.stringify(body), ...init }; return await http(url, init); } async function graphql(url, query, variables = {}, init) { const body = { query, variables }; if (typeof window !== "undefined" && window.location.hostname !== "khanacademy.org") url = url.replace("www.khanacademy.org", PROXY_DOMAIN); const response = await post(url, body, init); if (response.status === 403) { const isQuery = query.startsWith("query"), operationName = query.match(/^(?:query|mutation) (\w+)/)?.[1]; if (!operationName) throw new Error(`An unknown query is no longer in the safelist`); console.warn( `The query for operation "${operationName}" is no longer in the safelist. Attempting to fetch the latest version from the safelist...` ); const latestQuery = isQuery ? await getLatestQuery(operationName) : await getLatestMutation(operationName); if (!latestQuery) throw new Error( `The query for operation "${operationName}" was not found in the safelist` ); return await post(url, { ...body, query: latestQuery }, init); } return response; } // src/queries/avatarDataForProfile.ts var AvatarDataForProfile; ((AvatarDataForProfile2) => { AvatarDataForProfile2.query = `query avatarDataForProfile($kaid: String!) { user(kaid: $kaid) { id avatar { name imageSrc __typename } __typename } }`; })(AvatarDataForProfile || (AvatarDataForProfile = {})); function avatarDataForProfile(variablesOrKaid, init) { return graphql( `${KHAN_GRAPHQL_URL}/avatarDataForProfile`, AvatarDataForProfile.query, typeof variablesOrKaid === "string" ? { kaid: variablesOrKaid } : variablesOrKaid, init ); } // src/queries/feedbackQuery.ts var FeedbackQuery; ((FeedbackQuery2) => { FeedbackQuery2.query = `query feedbackQuery($topicId: String!, $focusKind: String!, $cursor: String, $limit: Int, $feedbackType: FeedbackType!, $currentSort: Int, $qaExpandKey: String) { feedback( focusId: $topicId cursor: $cursor limit: $limit feedbackType: $feedbackType focusKind: $focusKind sort: $currentSort qaExpandKey: $qaExpandKey answersLimit: 1 ) { feedback { isLocked isPinned replyCount appearsAsDeleted author { id kaid nickname avatar { name imageSrc __typename } __typename } badges { name icons { smallUrl __typename } description __typename } content date definitelyNotSpam deleted downVoted expandKey feedbackType flaggedBy flaggedByUser flags focusUrl focus { kind id translatedTitle relativeUrl __typename } fromVideoAuthor key lowQualityScore notifyOnAnswer permalink qualityKind replyCount replyExpandKeys showLowQualityNotice sumVotesIncremented upVoted ... on QuestionFeedback { hasAnswered answers { isLocked isPinned replyCount appearsAsDeleted author { id kaid nickname avatar { name imageSrc __typename } __typename } badges { name icons { smallUrl __typename } description __typename } content date definitelyNotSpam deleted downVoted expandKey feedbackType flaggedBy flaggedByUser flags focusUrl focus { kind id translatedTitle relativeUrl __typename } fromVideoAuthor key lowQualityScore notifyOnAnswer permalink qualityKind replyCount replyExpandKeys showLowQualityNotice sumVotesIncremented upVoted __typename } isOld answerCount __typename } ... on AnswerFeedback { question { isLocked isPinned replyCount appearsAsDeleted author { id kaid nickname avatar { name imageSrc __typename } __typename } badges { name icons { smallUrl __typename } description __typename } content date definitelyNotSpam deleted downVoted expandKey feedbackType flaggedBy flaggedByUser flags focusUrl focus { kind id translatedTitle relativeUrl __typename } fromVideoAuthor key lowQualityScore notifyOnAnswer permalink qualityKind replyCount replyExpandKeys showLowQualityNotice sumVotesIncremented upVoted __typename } __typename } __typename } cursor isComplete sortedByDate __typename } }`; })(FeedbackQuery || (FeedbackQuery = {})); function feedbackQuery(variables, init) { return graphql( `${KHAN_GRAPHQL_URL}/feedbackQuery`, FeedbackQuery.query, variables, init ); } // src/queries/getFeedbackReplies.ts var GetFeedbackReplies; ((GetFeedbackReplies2) => { GetFeedbackReplies2.query = `query getFeedbackReplies($postKey: String!) { feedbackReplies(feedbackKey: $postKey) { isLocked isPinned expandKey appearsAsDeleted author { id kaid nickname avatar { name imageSrc __typename } __typename } content date definitelyNotSpam deleted downVoted expandKey feedbackType flaggedBy flaggedByUser flags focusUrl fromVideoAuthor key lowQualityScore notifyOnAnswer permalink qualityKind replyCount replyExpandKeys showLowQualityNotice sumVotesIncremented upVoted __typename } }`; })(GetFeedbackReplies || (GetFeedbackReplies = {})); function getFeedbackReplies(variablesOrPostKey, init) { return graphql( `${KHAN_GRAPHQL_URL}/getFeedbackReplies`, GetFeedbackReplies.query, typeof variablesOrPostKey === "string" ? { postKey: variablesOrPostKey } : variablesOrPostKey, init ); } // src/queries/getFeedbackRepliesPage.ts var GetFeedbackRepliesPage; ((GetFeedbackRepliesPage2) => { GetFeedbackRepliesPage2.query = `query getFeedbackRepliesPage($postKey: String!, $cursor: String, $limit: Int!) { feedbackRepliesPaginated(feedbackKey: $postKey, cursor: $cursor, limit: $limit) { cursor isComplete feedback { isLocked isPinned expandKey appearsAsDeleted author { id kaid nickname avatar { name imageSrc __typename } __typename } content date definitelyNotSpam deleted downVoted expandKey feedbackType flaggedBy flaggedByUser flags focusUrl fromVideoAuthor key lowQualityScore notifyOnAnswer permalink qualityKind replyCount replyExpandKeys showLowQualityNotice sumVotesIncremented upVoted __typename } __typename } }`; })(GetFeedbackRepliesPage || (GetFeedbackRepliesPage = {})); function getFeedbackRepliesPage(variables, init) { return graphql( `${KHAN_GRAPHQL_URL}/getFeedbackRepliesPage`, GetFeedbackRepliesPage.query, variables, init ); } // src/utils/format.ts function truncate(str, length, postfix = "...") { length = Math.max(0, length); return str.length > length ? str.slice(0, length - postfix.length) + postfix : str; } function toStandardBase64(base64) { return base64.replace(/-/g, "+").replace(/_/g, "/"); } function toURLSafeBase64(base64) { return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); } // src/utils/regexes.ts function matchify(regex) { return new RegExp(`(${regex.source.slice(1, -1)})`); } var URL_TLDS = ["com", "org"]; var URL_LOCALES = [ "as", "az", "cs", "da", "el", "gu", "hu", "id", "it", "lt", "ja", "kk", "kn", "ky", "lv", "mn", "mr", "my", "nl", "pt-pt", "ru", "sv", "ta", "uz", "bg", "bn", "de", "en", "es", "fr", "hi", "hy", "ka", "km", "ko", "nb", "pa", "pl", "pt", "ro", "sr", "tr", "vi", "zh-hans", "sgn-us" ]; var KaidRegex = /^kaid_\d{20,25}$/; var KaidRegexMatch = matchify(KaidRegex); var isKaid = (str) => KaidRegex.test(str); var UserURLRegex = new RegExp( `^https?:\\/\\/(?:(?:www|${URL_LOCALES.join( "|" )})\\.)?khanacademy\\.(?:${URL_TLDS.join( "|" )})\\/profile\\/((?!kaid_|kaid_\\D)\\w+|(?:${KaidRegex.toString().slice( 2, -2 )}))(?:\\/.*)?$`, "i" ); var UserURLRegexMatch = matchify(UserURLRegex); var isUserURL = (str) => UserURLRegex.test(str); var PROGRAM_ID_LENGTHS = [9, 10, 16]; var ProgramIDRegex = new RegExp( `^[1-9](?:(?:${PROGRAM_ID_LENGTHS.map((v) => `\\d{${v - 1}}`).join("|")}))$` ); var ProgramIDRegexMatch = new RegExp( `(?:^|\\D)(${ProgramIDRegex.source.slice(1, -1)})(?:$|\\D)` ); function isProgramID(strOrNum) { return ProgramIDRegex.test(strOrNum.toString()); } var PROGRAM_URL_PATHS = [ "computer-programming", "cs", "pixar", "nasa", "piab-sandbox", "computer-science", "hour-of-code", "math", "differential-equations", "electrical-engineering", "mcat", "apchem-topic", "chemistry", "art-history-basics", "biology" ]; var ProgramURLRegex = new RegExp( `^https?:\\/\\/(?:(?:www|${URL_LOCALES.join( "|" )})\\.)?khanacademy\\.(?:${URL_TLDS.join("|")})\\/(?:${PROGRAM_URL_PATHS.join( "|" )})\\/[\\w\\d-.~()'!*:@,;]+\\/(${ProgramIDRegex.toString().slice(2, -2)})$`, "i" ); var ProgramURLRegexMatch = matchify(ProgramURLRegex); function isProgramURL(str) { return ProgramURLRegex.test(str); } var ProgramImagePathRegex = new RegExp( `^\\/(?:${PROGRAM_URL_PATHS.join( "|" )})\\/[\\w\\d-.~()'!*:@,;]+\\/\\d+\\/(\\d+)\\.png$` ); var ProgramImagePathRegexMatch = matchify(ProgramImagePathRegex); var PROGRAM_KEY_LENGTHS = [51, 54]; var ProgramKeyRegex = /^ag5zfmtoYW4tYWNhZGVteXI(?:U|X)CxIKU2NyYXRjaHBhZB(?:i|j)(?=[\w-]*$)(?:.{7}w|.{9}C(?:g|w|A|Q)w)$/; var ProgramKeyRegexMatch = matchify(ProgramKeyRegex); function isProgramKey(str) { if (PROGRAM_KEY_LENGTHS.includes(str.length)) return false; if (!ProgramKeyRegex.test(str)) return false; try { atob(toStandardBase64(str)); return true; } catch { return false; } } var MESSAGE_KEY_LENGTHS = [106, 107, 108, 110, 111]; var FeedbackKeyRegex = /^ag5zfmtoYW4tYWNhZGVteX(?:I|J)(?:A|B|7|9|-|_)CxIIVXNlckRhdGEi(?:Gm|GG|G2|Hm|HG|HW)thaWRf(?:M|N|O)(?:\w{34}|\w{29,31}|\w{27})(?:LEghGZWVkYmFjaxiAg[I-P](?:O|P|W|X|2|3)|DAsSCEZlZWRiYWNrGICA|MCxIIRmVlZGJhY2sYgI(?:C|D)|wLEghGZWVkYmFjaxiAg|DAsSCEZlZWRiYWNrGICA)(?:[\w-]{5}|[\w-]{7,8})(?:M|DA|C(?:g|w|A|Q)w)$/; var FeedbackKeyRegexMatch = matchify(FeedbackKeyRegex); var isFeedbackKey = (str) => FeedbackKeyRegex.test(str); var MESSAGE_ENCRYPTED_KEY_LENGTHS = [301]; var MessageEncryptedKeyRegex = /^kaencrypted_[a-z0-9]{32}_[a-z0-9]{256}$/; var MessageEncryptedKeyRegexMatch = matchify(MessageEncryptedKeyRegex); var isEncryptedFeedbackKey = (str) => MessageEncryptedKeyRegex.test(str); var GoogleIDRegex = /^http:\/\/googleid\.khanacademy\.org\/(\d+)$/; var GoogleIDRegexMatch = matchify(GoogleIDRegex); var isGoogleID = (str) => GoogleIDRegex.test(str); var QualarooIDRegex = /^_gae_bingo_random:(\w+-\w+-\w+)$/; var QualarooIDRegexMatch = matchify(QualarooIDRegex); var isQualarooID = (str) => QualarooIDRegex.test(str); var EmailRegex = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/; var EmailRegexMatch = matchify(EmailRegex); var isEmail = (str) => EmailRegex.test(str); // src/queries/getFullUserProfile.ts var GetFullUserProfile; ((GetFullUserProfile2) => { GetFullUserProfile2.query = `query getFullUserProfile($kaid: String, $username: String) { user(kaid: $kaid, username: $username) { id kaid key userId email username profileRoot gaUserId isPhantom isDeveloper: hasPermission(name: "can_do_what_only_admins_can_do") isPublisher: hasPermission(name: "can_publish", scope: ANY_ON_CURRENT_LOCALE) isModerator: hasPermission(name: "can_moderate_users", scope: GLOBAL) isParent isTeacher isFormalTeacher isK4dStudent isKmapStudent isDataCollectible isChild isOrphan isCoachingLoggedInUser canModifyCoaches nickname hideVisual joined points countVideosCompleted bio profile { accessLevel __typename } soundOn muteVideos showCaptions prefersReducedMotion noColorInVideos newNotificationCount canHellban: hasPermission(name: "can_ban_users", scope: GLOBAL) canMessageUsers: hasPermission( name: "can_send_moderator_messages" scope: GLOBAL ) isSelf: isActor hasStudents: hasCoachees hasClasses hasChildren hasCoach badgeCounts homepageUrl isMidsignupPhantom includesDistrictOwnedData includesKmapDistrictOwnedData includesK4dDistrictOwnedData canAccessDistrictsHomepage underAgeGate { parentEmail daysUntilCutoff approvalGivenAt __typename } authEmails signupDataIfUnverified { email emailBounced __typename } pendingEmailVerifications { email __typename } hasAccessToAIGuideCompanionMode hasAccessToAIGuideLearner hasAccessToAIGuideDistrictAdmin hasAccessToAIGuideParent hasAccessToAIGuideTeacher tosAccepted shouldShowAgeCheck birthMonthYear lastLoginCountry region userDistrictInfos { id isKAD district { id region __typename } __typename } schoolAffiliation { id location __typename } __typename } actorIsImpersonatingUser isAIGuideEnabled hasAccessToAIGuideDev }`; })(GetFullUserProfile || (GetFullUserProfile = {})); function getFullUserProfile(variablesOrIdentifier, init) { return graphql( `${KHAN_GRAPHQL_URL}/getFullUserProfile`, GetFullUserProfile.query, typeof variablesOrIdentifier === "string" ? isKaid(variablesOrIdentifier) ? { kaid: variablesOrIdentifier } : { username: variablesOrIdentifier } : variablesOrIdentifier, init ); } // src/queries/getProfileWidgets.ts var GetProfileWidgets; ((GetProfileWidgets2) => { GetProfileWidgets2.query = `query getProfileWidgets($kaid: String!) { user(kaid: $kaid) { id kaid badgeCounts isChild profile { programs { id authorKaid authorNickname deleted displayableSpinoffCount imagePath key sumVotesIncremented translatedTitle: title url __typename } __typename } programs(sort: TOP, pageInfo: {itemsPerPage: 2}) { programs { id authorKaid authorNickname deleted displayableSpinoffCount imagePath key sumVotesIncremented translatedTitle: title url __typename } __typename } __typename } userSummary(kaid: $kaid) { statistics { answers comments flags projectanswers projectquestions questions replies votes __typename } __typename } }`; })(GetProfileWidgets || (GetProfileWidgets = {})); function getProfileWidgets(variablesOrKaid, init) { return graphql( `${KHAN_GRAPHQL_URL}/getProfileWidgets`, GetProfileWidgets.query, typeof variablesOrKaid === "string" && isKaid(variablesOrKaid) ? { kaid: variablesOrKaid } : variablesOrKaid, init ); } // src/queries/getUserByUsernameOrEmail.ts var GetUserByUsernameOrEmail; ((GetUserByUsernameOrEmail2) => { GetUserByUsernameOrEmail2.query = `query getUserByUsernameOrEmail($username: String, $email: String) { user(username: $username, email: $email) { id kaid __typename } }`; })(GetUserByUsernameOrEmail || (GetUserByUsernameOrEmail = {})); function getUserByUsernameOrEmail(variablesOrIdentifier, init) { return graphql( `${KHAN_GRAPHQL_URL}/getUserByUsernameOrEmail`, GetUserByUsernameOrEmail.query, typeof variablesOrIdentifier === "string" ? isEmail(variablesOrIdentifier) ? { email: variablesOrIdentifier } : { username: variablesOrIdentifier } : variablesOrIdentifier, init ); } // src/queries/getUserHoverCardProfile.ts var GetUserHoverCardProfile; ((GetUserHoverCardProfile2) => { GetUserHoverCardProfile2.query = `query getUserHoverCardProfile($kaid: String!) { user(kaid: $kaid) { id nickname username bio avatar { name imageSrc __typename } points isPhantom isActor isCoachedByActor userSummaryIsVisibleToActor actorHasUserScopedPermission(capability: CAN_VIEW_USER_IDENTITY) __typename } }`; })(GetUserHoverCardProfile || (GetUserHoverCardProfile = {})); function getUserHoverCardProfile(variablesOrKaid, init) { return graphql( `${KHAN_GRAPHQL_URL}/getUserHoverCardProfile`, GetUserHoverCardProfile.query, typeof variablesOrKaid === "string" ? { kaid: variablesOrKaid } : variablesOrKaid, init ); } // src/queries/hotlist.ts var Hotlist; ((Hotlist2) => { Hotlist2.query = `query hotlist($curationNodeId: String, $onlyOfficialProjectSpinoffs: Boolean!, $sort: ListProgramSortOrder, $pageInfo: ListProgramsPageInfo, $userAuthoredContentTypes: [UserAuthoredContentType!]) { listTopPrograms( curationNodeId: $curationNodeId onlyOfficialProjectSpinoffs: $onlyOfficialProjectSpinoffs sort: $sort pageInfo: $pageInfo userAuthoredContentTypes: $userAuthoredContentTypes ) { complete cursor programs { id key authorKaid authorNickname displayableSpinoffCount imagePath sumVotesIncremented translatedTitle: title url userAuthoredContentType __typename } __typename } }`; })(Hotlist || (Hotlist = {})); function hotlist(variables, init) { return graphql( `${KHAN_GRAPHQL_URL}/hotlist`, Hotlist.query, variables, init ); } // src/queries/programQuery.ts var ProgramQuery; ((ProgramQuery2) => { ProgramQuery2.query = `query programQuery($programId: String!) { programById(id: $programId) { byChild category created creatorProfile: author { id nickname profileRoot profile { accessLevel __typename } __typename } deleted description spinoffCount: displayableSpinoffCount docsUrlPath flags flaggedBy: flaggedByKaids flaggedByUser: isFlaggedByCurrentUser height hideFromHotlist id imagePath isProjectOrFork: originIsProject isOwner kaid: authorKaid key newUrlPath originScratchpad: originProgram { deleted translatedTitle url __typename } restrictPosting revision: latestRevision { id code configVersion created editorType folds __typename } slug sumVotesIncremented title topic: parentCurationNode { id nodeSlug: slug relativeUrl slug translatedTitle __typename } translatedTitle url userAuthoredContentType upVoted width __typename } }`; })(ProgramQuery || (ProgramQuery = {})); function programQuery(variablesOrProgramId, init) { return graphql( `${KHAN_GRAPHQL_URL}/programQuery`, ProgramQuery.query, typeof variablesOrProgramId === "number" ? { programId: variablesOrProgramId.toString() } : typeof variablesOrProgramId === "string" ? { programId: variablesOrProgramId } : variablesOrProgramId, init ); } // src/queries/projectsAuthoredByUser.ts var ProjectsAuthoredByUser; ((ProjectsAuthoredByUser2) => { ProjectsAuthoredByUser2.query = `query projectsAuthoredByUser($kaid: String, $pageInfo: ListProgramsPageInfo, $sort: ListProgramSortOrder) { user(kaid: $kaid) { id programs(pageInfo: $pageInfo, sort: $sort) { complete cursor programs { id key authorKaid authorNickname displayableSpinoffCount imagePath sumVotesIncremented translatedTitle: title url __typename } __typename } __typename } }`; })(ProjectsAuthoredByUser || (ProjectsAuthoredByUser = {})); function projectsAuthoredByUser(variables, init) { return graphql( `${KHAN_GRAPHQL_URL}/projectsAuthoredByUser`, ProjectsAuthoredByUser.query, variables, init ); } // src/queries/QAExpandKeyInfo.ts var QaExpandKeyInfo; ((QaExpandKeyInfo2) => { QaExpandKeyInfo2.query = `query QAExpandKeyInfo($encryptedKey: String) { qaExpandKeyInfo(encryptedQaExpandKey: $encryptedKey) { feedbackType unencryptedKey __typename } }`; })(QaExpandKeyInfo || (QaExpandKeyInfo = {})); function QAExpandKeyInfo(variablesOrEncryptedKey, init) { return graphql( `${KHAN_GRAPHQL_URL}/QAExpandKeyInfo`, QaExpandKeyInfo.query, typeof variablesOrEncryptedKey === "string" ? { encryptedKey: variablesOrEncryptedKey } : variablesOrEncryptedKey, init ); } // src/mutations/index.ts var mutations_exports = {}; __export(mutations_exports, { LoginWithPasswordMutation: () => LoginWithPasswordMutation, loginWithPasswordMutation: () => loginWithPasswordMutation }); // src/mutations/loginWithPasswordMutation.ts var LoginWithPasswordMutation; ((LoginWithPasswordMutation2) => { LoginWithPasswordMutation2.query = `mutation loginWithPasswordMutation($identifier: String!, $password: String!) { loginWithPassword(identifier: $identifier, password: $password) { user { id kaid canAccessDistrictsHomepage isTeacher hasUnresolvedInvitations preferredKaLocale { id kaLocale status __typename