mediawiki-sdk
Version:
TypeScript SDK for generating and working with WikiText and MediaWiki API. Allows to get pages from wiki projects built on mediawiki.
2 lines (1 loc) • 18.4 kB
JavaScript
var f=class{constructor(){this.store=[]}parseSetCookie(i,e){let t=i.split(";").map(a=>a.trim()),[r,n]=t[0].split("="),s={name:r,value:n,domain:"",path:"/",creationTime:new Date};for(let a of t.slice(1)){let[l,o]=a.includes("=")?a.split("="):[a,""];switch(l.toLowerCase()){case"expires":let p=new Date(o);isNaN(p.getTime())||(s.expires=p);break;case"path":s.path=o;break;case"domain":s.domain=o.startsWith(".")?o.slice(1):o;break;case"secure":s.secure=!0;break;case"httponly":s.httpOnly=!0;break;case"samesite":s.sameSite=o;break}}s.domain||(s.domain=e,s.hostOnly=!0),this.storeCookie(s)}storeCookie(i){this.store=this.store.filter(e=>!(e.name===i.name&&e.domain===i.domain&&e.path===i.path)),this.store.push(i)}getCookieHeader(i){let e=new URL(i),t=new Date;return this.store.filter(n=>{if(n.expires&&n.expires<t||n.secure&&e.protocol!=="https:")return!1;let s=n.hostOnly?e.hostname===n.domain:e.hostname===n.domain||e.hostname.endsWith("."+n.domain),a=e.pathname.startsWith(n.path);return s&&a})}},m=class{constructor(i,e){if(this.batchcomplete=i.batchcomplete,this.query=i.query,this.wiki=e,i.query?.pages){let t=Object.keys(i.query.pages);t.length>0&&(this.pageDetails=i.query.pages[t[0]])}}html(){return this.pageDetails?.extract??""}title(){return this.pageDetails?.title??""}categories(){return this.pageDetails?.categories?.map(i=>i.title)??[]}async edit(i){if(!this.pageDetails)throw new Error("Page details not available for editing.");if(!this.pageDetails.title&&!this.pageDetails.pageid)throw new Error("Page title or ID is required for editing.");let e={...i};if(this.pageDetails.pageid?e.pageid=this.pageDetails.pageid:e.title=this.pageDetails.title,typeof i.text!="string")throw new Error("Parameter 'text' is required for editing.");return this.wiki.client.editPage(e)}},h=class{constructor(i){Object.assign(this,i),this.parse=i.parse,this.warnings=i.warnings}text(){return this.parse?.text??""}html(){return this.parse?.text??""}title(){return this.parse?.title??""}categories(){return this.parse&&Array.isArray(this.parse.categories)?this.parse.categories.map(i=>i&&typeof i=="object"&&i["*"]).filter(Boolean):[]}},c=class{constructor(i){Object.assign(this,i),this.query=i.query,this.batchcomplete=i.batchcomplete}text(){return this.query?.pages?.[0]?.extract??""}},g=class{constructor(i){this.batchcomplete=i.batchcomplete,this.query=i.query,i.warnings&&(this.warnings=i.warnings),i.errors&&(this.errors=i.errors)}isAnonymous(){return this.query.userinfo.anon===!0||this.query.userinfo.id===0}getUserId(){return this.query.userinfo.id}getUserName(){return this.query.userinfo.name}getUserInfo(){return this.query.userinfo}getUserOptions(){return this.query.userinfo.options}},b=class{constructor(i){this.batchcomplete=i.batchcomplete,this.query=i.query,i.warnings&&(this.warnings=i.warnings),i.errors&&(this.errors=i.errors)}getCsrfToken(){return this.query.tokens.csrftoken}getWatchToken(){return this.query.tokens.watchtoken}getPatrolToken(){return this.query.tokens.patroltoken}getRollbackToken(){return this.query.tokens.rollbacktoken}getUserRightsToken(){return this.query.tokens.userrightstoken}getLoginToken(){return this.query.tokens.logintoken}getCreateAccountToken(){return this.query.tokens.createaccounttoken}getEditToken(){return this.query.tokens.csrftoken}},y=class{constructor(i){let e={result:"Unknown",pageid:0,title:"",contentmodel:"wikitext",oldrevid:0,newrevid:0,newtimestamp:"",watched:!1};i&&typeof i=="object"?(this.batchcomplete=i.batchcomplete??!1,this.query=i.query??{edit:e},this.query.edit||(this.query.edit=e),i.warnings&&(this.warnings=i.warnings),i.errors&&(this.errors=i.errors)):(this.batchcomplete=!1,this.query={edit:e})}getResult(){return this.query?.edit?.result??"Unknown"}getPageId(){return this.query.edit.pageid??0}getTitle(){return this.query.edit.title??""}getContentModel(){return this.query.edit.contentmodel??""}getOldRevisionId(){return this.query.edit.oldrevid??0}getNewRevisionId(){return this.query.edit.newrevid??0}getNewTimestamp(){return this.query.edit.newtimestamp??""}isWatched(){return this.query.edit.watched??!1}hasWarnings(){return this.warnings!==void 0}getWarnings(){return this.warnings}hasErrors(){return this.errors!==void 0}getErrors(){return this.errors}},k=class{constructor(i){this.batchcomplete=i?.batchcomplete??!1,this.upload=i?.upload,this.error=i?.error}getResult(){return this.upload?.result??"Error"}getFilename(){return this.upload?.filename??""}getCanonicalTitle(){return this.upload?.imageinfo.canonicaltitle??""}getFileUrl(){return this.upload?.imageinfo.url??""}getDescriptionUrl(){return this.upload?.imageinfo.descriptionurl??""}getSha1(){return this.upload?.imageinfo.sha1??""}getMime(){return this.upload?.imageinfo.mime??""}getMediaType(){return this.upload?.imageinfo.mediatype??""}getError(){return this.error}},M=class extends Error{constructor(i,e,t,r){super(i),this.name="MediaWikiApiError",this.status=e,this.responseText=t,this.responseData=r,r&&r.error&&(this.code=r.error.code,this.info=r.error.info,this.info&&i.startsWith("Request failed")?this.message=`Request failed with status ${e}: ${this.info} (Code: ${this.code||"N/A"})`:this.code&&i.startsWith("Request failed")&&(this.message=`Request failed with status ${e} (Code: ${this.code})`))}},w=class{constructor(i){if(!i.baseURL)throw new Error("baseURL is required");if(this.cookieStore=new f,this.baseURL=i.baseURL.endsWith("/api.php")?i.baseURL:`${i.baseURL}/api.php`,this.params={servedby:i.servedby,curtimestamp:i.curtimestamp,responselanginfo:i.responselanginfo,requestid:i.requestid,format:i.format??"json",formatversion:i.formatversion??2,ascii:i.ascii,utf8:i.utf8},this.authorized=!1,this.siteInfo=null,i.format&&i.format!=="json")throw new Error(`Expected "json" format but got "${i.format}". The library only speaks JSON...`);this.parent=this,this.client={parent:this.parent,async query(e){if(!e)throw new Error("Options are required for the query method.");if(e.title&&e.titles)throw new Error('Use either "title" or "titles", not both.');if(e.titles&&e.pageids)throw new Error('Cannot use both "titles" and "pageids". Use only one identifier method.');if(e.titles&&!e.prop&&!e.meta&&e.list)throw new Error('"titles" provided but no "prop", "meta", or "list" specified. Nothing to retrieve.');if(!e.titles&&e.export)throw new Error('"export" requires "titles" to be set.');if(e.indexpageids&&!e.titles&&!e.pageids)throw new Error('"indexpageids" only works with "titles" or "pageids".');if(e.redirects&&!e.titles&&!e.title&&!e.pageids)throw new Error('"redirects" has no effect without "titles", "pageids", or "title".');if(e.prop&&!e.titles&&!e.pageids)throw new Error('"prop" requires either "titles", "pageids".');let t={action:"query",prop:e.prop?.join("|"),list:e.list?.join("|"),meta:e.meta?.join("|"),indexpageids:e.indexpageids,export:e.export,titles:e.titles?.join("|"),pageids:e.pageids?.join("|"),srsearch:e.srsearch,srnamespace:e.srnamespace?.join("|"),srlimit:e.srlimit,srprop:e.srprop?.join("|"),srwhat:e.srwhat,srinfo:e.srinfo,rvlimit:e.rvlimit,exintro:e.exintro,explaintext:e.explaintext,uiprop:e.uiprop,type:e.type},r=this.parent.filterParams(t);return this.parent.fetchData({method:"GET",params:r,url:this.parent.baseURL})},async page(e){if(!e||e.length===0)throw new Error("Missing or empty 'titles' - must be a non-empty.");let t={prop:["info","extracts","categories","revisions"],titles:e,indexpageids:!0},r=await this.query(t);return!r||!r.query?new m(r,this.parent):new m(r,this.parent)},async search(e,t,r){if(!e||e.trim()==="")throw new Error('Missing "srsearch" - must be a non-empty string.');let n={list:["search"],srsearch:e,srnamespace:t??[],srlimit:r??10},s=await this.parent.client.query(n);return!s||!s.query,s},async siteInfo(){let e={meta:["siteinfo"]},t=await this.query(e);return this.parent.siteInfo=t?.query??null,!this.parent.siteInfo||!this.parent.siteInfo.general,t},async opensearch(e){if(!e)throw new Error("Options are required for the opensearch method.");if(!e.search)throw new Error("A search is required for the opensearch method.");let t={action:"opensearch",...e},r=this.parent.filterParams(t),n=await this.parent.fetchData({method:"GET",url:this.parent.baseURL,params:r});return!n||!n.query,n},async parse(e){if(!e)throw new Error("Options are required for the parse method.");if(!e.page&&!e.pageid&&!e.text)throw new Error('You must provide either "page", "pageid" or "text" for the parse method.');let t={action:"parse",...e},r=this.parent.filterParams(t),n=await this.parent.fetchData({method:"GET",url:this.parent.baseURL,params:r});return!n||!n.parse?new h(n):new h(n)},async categories(e){if(!e||!e.title)throw new Error('Missing or invalid "title" - must be a non-empty string.');let t={titles:[e.title],prop:["categories"]},r=await this.query(t);if(!r||!r.query||typeof r.query.pages!="object")return{continue:r.continue??{},query:{normalized:[],pages:[]}};let n=Object.values(r.query.pages??{}),s=Array.isArray(r.query.normalized)?r.query.normalized:[];return{continue:r.continue,query:{normalized:s,pages:n}}},async revisions(e){if(!e||!e.title)throw new Error('Missing or invalid "title" - must be a non-empty string.');let t={titles:[e.title],prop:["revisions"],rvlimit:e.rvlimit},r=await this.parent.client.query(t);if(!r||!r.query||typeof r.query.pages!="object")return{batchcomplete:!1,query:{normalized:[],pages:[]}};let n=Object.values(r.query.pages??{}),s=Array.isArray(r.query.normalized)?r.query.normalized:[];return{batchcomplete:r.batchcomplete??!1,query:{normalized:s,pages:n}}},async summary(e){if(!e||!e.title)throw new Error('Missing or invalid "title" - must be a non-empty string.');let t={titles:[e.title],prop:["extracts"],exintro:!0,explaintext:!0},r=await this.query(t);if(!r)return new c({batchcomplete:!1,query:{pages:[],normalized:[]}});if(!r||!r.query||!r.query.normalized||!r.query.pages||typeof r.query.pages!="object")return new c({batchcomplete:r.batchcomplete??!1,query:{pages:r.query?.pages&&typeof r.query.pages=="object"?Object.values(r.query.pages):[],normalized:r.query?.normalized??[]},warnings:r.warnings,errors:r.errors});let n=Object.values(r.query.pages??{});return new c({batchcomplete:r.batchcomplete,query:{normalized:r.query.normalized??[],pages:n}})},async userInfo(){let e={meta:["userinfo"],uiprop:"*"},t=await this.query(e);return t?!t||!t.query||!t.query.normalized||!t.query.pages||typeof t.query.pages!="object"?new g({batchcomplete:t.batchcomplete??!1,query:{userinfo:t.query?.userinfo},warnings:t.warnings,errors:t.errors}):new g(t):new g({batchcomplete:!1,query:{userinfo:{id:-1,name:"",groups:[],groupmemberships:[],implicitgroups:[],rights:[],changeablegroups:{add:[],remove:[],"add-self":[],"remove-self":[]},options:{},editcount:-1,ratelimits:{}}}})},async getToken(e){let t={meta:["tokens"],type:e.type.join("|")},r=await this.query(t);if(!r||!r.query||!r.query.tokens)throw new Error(`Failed to retrieve tokens or unexpected response structure: ${JSON.stringify(r)}`);return new b(r)},async editPage(e){if(!e)throw new Error("Options are required for the editpage method.");if(!e.title&&!e.pageid&&!e.text)throw new Error('You must provide either "page", "pageid" or "text" for the editpage method.');let r={token:(await this.getToken({type:["csrf"]})).query.tokens.csrftoken,action:"edit",...e},n=this.parent.filterParams(r),s=await this.parent.fetchData({method:"POST",url:this.parent.baseURL,headers:{"Content-Type":"application/x-www-form-urlencoded"},data:this.parent.formURLEncoder(n)});return!s||!s.query?new y(s):new y(s)},async random(){let e={action:"query",list:["random"]},t=await this.query(e);return!t||!t.query,t},async uploadFile(e){if(!e)throw new Error("The 'options' parameter is required for uploadFile.");if(!e.filekey&&!e.filename)throw new Error("The 'filename' parameter is required if 'filekey' is not provided.");if(!e.file&&!e.url&&!e.filekey)throw new Error("A file source must be provided: 'file', 'url', or 'filekey'.");e.file&&!e.mimeType&&e.file instanceof Buffer&&console.warn("It's recommended to specify 'mimeType' when 'file' is a Buffer. Defaulting to 'application/octet-stream'.");let r=(await this.getToken({type:["csrf"]})).getCsrfToken();if(!r)throw new Error("Failed to obtain CSRF token for file upload.");let n=new FormData;if(n.append("action","upload"),n.append("token",r),n.append("format",this.parent.params.format??"json"),this.parent.params.formatversion&&n.append("formatversion",String(this.parent.params.formatversion)),e.filename&&n.append("filename",e.filename),e.comment&&n.append("comment",e.comment),e.text&&n.append("text",e.text),e.tags&&n.append("tags",e.tags),e.watchlist&&n.append("watchlist",e.watchlist),e.ignorewarnings!==void 0&&n.append("ignorewarnings",e.ignorewarnings?"1":"0"),e.async!==void 0&&n.append("async",e.async?"1":"0"),e.bot!==void 0&&n.append("bot",e.bot?"1":"0"),e.file){let a,l=e.filename||"upload.dat";if(e.file instanceof Buffer)a=new Blob([e.file],{type:e.mimeType||"application/octet-stream"});else{if(typeof e.file.pipe=="function"&&typeof e.file.on=="function")throw new Error("Direct upload of ReadableStream is not supported. Convert to Buffer or Blob, or use 'url' or 'filekey'.");a=e.file,e.file instanceof File&&e.file.name&&(l=e.file.name)}n.append("file",a,l)}else e.url?n.append("url",e.url):e.filekey&&n.append("filekey",e.filekey);let s=await this.parent.fetchData({method:"POST",url:this.parent.baseURL,data:n});return new k(s)},async searchTitles(e){let{query:t,limit:r,namespace:n}=e;if(!t||t.trim()==="")throw new Error("Search query cannot be empty.");let s={action:"query",list:["search"],srsearch:t,srlimit:r,srprop:["titlesnippet"]};n!==void 0&&(Array.isArray(n)?s.srnamespace=n.map(a=>String(a)):s.srnamespace=[String(n)]);try{let a=await this.query(s);return a.query&&a.query.search?a.query.search.map(l=>l.title):[]}catch(a){throw console.error(`Error in searchTitles for query "${t}":`,a),a}}},Object.defineProperty(this.client,"parent",{value:this.parent,writable:!1,enumerable:!1,configurable:!1})}async fetchData(i){try{let e={};for(let o in i.params)i.params[o]!==void 0&&i.params[o]!==null&&(e[o]=i.params[o]);for(let o in this.params)e[o]===void 0&&this.params[o]!==void 0&&this.params[o]!==null&&(e[o]=this.params[o]);let t=new URL(i.url);Object.entries(e).forEach(([o,u])=>{t.searchParams.append(o,u)});let r=this.cookieStore.getCookieHeader(this.baseURL),n={...i.headers};if(r&&r.length>0){let o=r.map(u=>`${u.name}=${u.value}`).join("; ");n.Cookie=o}let s;i.method!=="GET"&&i.data&&(i.data instanceof FormData||typeof i.data=="string"||i.data instanceof URLSearchParams||i.data instanceof ReadableStream?s=i.data:(s=JSON.stringify(i.data),i.headers&&!i.headers["Content-Type"]&&!i.headers["content-type"]?i.headers["Content-Type"]="application/json":i.headers||(i.headers={"Content-Type":"application/json"})));let a=await fetch(t.toString(),{method:i.method,headers:n,body:s}),l=a.headers.getSetCookie?.()||a.headers.get?.("set-cookie");if(l){let o=Array.isArray(l)?l:[l];for(let u of o)this.cookieStore.parseSetCookie(u,new URL(this.baseURL).hostname)}if(!a.ok){let o=await a.text(),u;try{u=JSON.parse(o)}catch{}let p=`Request failed with status ${a.status}`;throw new M(p,a.status,o,u)}return await a.json()}catch(e){throw e}}filterParams(i){let e={};for(let t in i)i[t]!==void 0&&i[t]!==null&&(e[t]=i[t]);return e}assertSiteInfo(){if(!this.siteInfo)throw new Error("siteInfo not loaded. Call siteInfo() first.")}formURLEncoder(i){let e=new URLSearchParams;for(let t in i)i[t]!==void 0&&i[t]!==null&&e.append(t,i[t]);return e.toString()}async login(i,e){let r=(await this.fetchData({method:"GET",url:this.baseURL,params:{action:"query",meta:"tokens",type:"login"}}))?.query?.tokens?.logintoken;if(!r)throw new Error("Failed to retrieve initial login token");let n=await this.fetchData({method:"POST",url:this.baseURL,data:this.formURLEncoder({action:"login",lgname:i,lgpassword:e,lgtoken:r,format:this.params.format}),headers:{"Content-Type":"application/x-www-form-urlencoded"}});if(n.login.result==="NeedToken"){let o=n.login.token;if(!o)throw new Error('Login responded with "NeedToken" but did not provide the required challenge token in response.login.token.');n=await this.fetchData({method:"POST",url:this.baseURL,data:this.formURLEncoder({action:"login",lgname:i,lgpassword:e,lgtoken:o,format:this.params.format}),headers:{"Content-Type":"application/x-www-form-urlencoded"}})}let s=n?.login?.result,a=n?.login;if(s!=="Success"||!a){let o=n?.login?.reason||"Unknown reason";throw new Error(`Login failed. Result: ${s}, Reason: ${o}. Full response: ${JSON.stringify(n)}`)}let l={userId:a.lguserid,userName:a.lgusername};return this.authorized=!0,l}async logout(){if(!this.authorized)throw new Error("You are not authorized.");let e=(await this.client.getToken({type:["csrf"]}))?.query.tokens?.csrftoken;if(!e)throw new Error("Failed to retrieve login token");let t=await this.fetchData({method:"POST",url:this.baseURL,data:this.formURLEncoder({action:"logout",token:e}),headers:{"Content-Type":"application/x-www-form-urlencoded"}});if(t.error)throw new Error(`Response: ${JSON.stringify(t)}`);return this.authorized=!1,!0}isAuthorized(){return this.authorized}async isLoggedIn(){let i=await this.client.userInfo();return i.query.userinfo.anon?i.query.userinfo.anon:!1}getBaseURL(){return this.baseURL}getParams(){return{...this.params}}getCookies(){return this.cookieStore.getCookieHeader(this.baseURL)}getDebugInfo(){return{baseURL:this.baseURL,params:this.params,authorized:this.authorized,cookies:this.getCookies()}}getSiteName(){return this.assertSiteInfo(),this.siteInfo&&this.siteInfo.general&&typeof this.siteInfo.general.sitename=="string"?this.siteInfo.general.sitename:null}getNamespaceList(){return this.assertSiteInfo(),this.siteInfo&&this.siteInfo.namespaces?this.siteInfo.namespaces:null}getNamespaceArray(){this.assertSiteInfo();let i=this.siteInfo.namespaces;return i==null?[]:typeof i!="object"?[]:Object.entries(i).map(([e,t])=>({id:parseInt(e,10),name:t["*"]}))}};export{f as CookieStore,w as MediaWiki,M as MediaWikiApiError,y as MediaWikiQueryEditPageResponseClass,m as MediaWikiQueryPageResponseClass,h as MediaWikiQueryParseResponseClass,c as MediaWikiQuerySummaryResponseClass,b as MediaWikiQueryTokensResponseClass,k as MediaWikiQueryUploadFileResponseClass,g as MediaWikiQueryUserInfoResponseClass};