UNPKG

wiki-saikou

Version:

The library provides the out of box accessing to MediaWiki API in both browsers & Node.js, and the syntax is very similar to vanilla `new mw.Api()`. TypeScript definition included~

15 lines (14 loc) 16.5 kB
var WikiSaikou=function(b){"use strict";var g=(i=>(i.BODY_USED="BODY_USED",i.NO_BODY_READER="NO_BODY_READER",i.TIMEOUT="TIMEOUT",i.NETWORK_ERROR="NETWORK_ERROR",i.BODY_NOT_ALLOWED="BODY_NOT_ALLOWED",i.HOOK_CONTEXT_CHANGED="HOOK_CONTEXT_CHANGED",i.ABORTED_BY_HOOK="ABORTED_BY_HOOK",i.INVALID_HOOK_CALLBACK="INVALID_HOOK_CALLBACK",i.UNEXPECTED_HOOK_RETURN="UNEXPECTED_HOOK_RETURN",i))(g||{});class f extends Error{constructor(e,o,r,n){super(o,n),this.code=e,this.context=r,this.name="FexiosError"}}class j extends f{constructor(e,o,r){super(o.statusText,e,void 0,r),this.response=o,this.name="FexiosResponseError"}}function W(i,e=1024){if(!(i instanceof Uint8Array))throw new TypeError("Input must be a Uint8Array");const o=i.slice(0,e),r=new TextDecoder("utf-8",{fatal:!0});try{const n=r.decode(o),s=/[\x00-\x08\x0E-\x1F\x7F]/g,a=n.match(s);return!(a&&a.length/n.length>.1)}catch{return!1}}function P(i){if(typeof i!="object"||i===null||Object.prototype.toString.call(i)!=="[object Object]")return!1;const e=Object.getPrototypeOf(i);return e===Object.prototype||e===null}function I(i,e={}){const o={};return Object.entries(i).forEach(([r,n])=>{n!=null&&(e.dropEmptyString&&n===""||(o[r]=n))}),o}class _{constructor(e,o,r){this.rawResponse=e,this.data=o,this.ok=e.ok,this.status=e.status,this.statusText=e.statusText,this.headers=e.headers,Object.entries(r||{}).forEach(([n,s])=>{this[n]=s})}}async function H(i,e,o){var r;if(i.bodyUsed)throw new f(g.BODY_USED,"Response body has already been used or locked");const n=i.headers.get("content-type")||"",s=Number(i.headers.get("content-length"))||0,a=(u,c)=>c==="json"||u.startsWith("application/json"),t=(u,c,h)=>h==="blob"||u.startsWith("image/")||u.startsWith("video/")||u.startsWith("audio/")||!W(c);if((i.status===101||i.status===426||i.headers.get("upgrade"))&&typeof globalThis.WebSocket<"u"){const u=new WebSocket(i.url);return await new Promise((c,h)=>{u.onopen=c,u.onerror=h}),new _(i,u,{ok:!0,status:101,statusText:"Switching Protocols"})}else if(n.startsWith("text/event-stream")&&!["text","json"].includes(e||"")&&typeof globalThis.EventSource<"u"){const u=new EventSource(i.url);return await new Promise((c,h)=>{u.onopen=c,u.onerror=h}),new _(i,u)}else{if(e==="stream")return new _(i,i.body);{const u=(r=i.clone().body)==null?void 0:r.getReader();if(!u)throw new f(g.NO_BODY_READER,"Failed to get ReadableStream from response body");let c=new Uint8Array;for(;;){const{done:l,value:w}=await u.read();if(l)break;if(w&&(c=new Uint8Array([...c,...w]),o&&s>0)){const d=Math.min(c.length/s,1);o(d,c)}}const h=new _(i,void 0);if(t(n,c,e)?h.data=new Blob([c],{type:i.headers.get("content-type")||void 0}):h.data=new TextDecoder().decode(c),a(n,e))try{h.data=JSON.parse(h.data)}catch{}if(typeof h.data=="string"&&e!=="text"){const l=h.data.trim(),w=l[0],d=l[l.length-1];if(w==="{"&&d==="}"||w==="["&&d==="]")try{h.data=JSON.parse(h.data)}catch{}}if(typeof h.data>"u"&&(h.data=c.length>0?c:void 0),h.ok)return h;throw new j(`Request failed with status code ${i.status}`,h)}}}class B{static makeSearchParams(e){const o=new URLSearchParams;return Object.entries(e).forEach(([r,n])=>{Array.isArray(n)?n.forEach(s=>o.append(r,String(s))):o.set(r,String(n))}),o}static makeQueryString(e){return this.makeSearchParams(e).toString()}}function M(i){return i&&i.__esModule&&Object.prototype.hasOwnProperty.call(i,"default")?i.default:i}var A,v;function F(){if(v)return A;v=1;function i(e){var o=this.constructor.prototype[e],r=function(){return o.apply(r,arguments)};return Object.setPrototypeOf(r,this.constructor.prototype),Object.getOwnPropertyNames(o).forEach(function(n){Object.defineProperty(r,n,Object.getOwnPropertyDescriptor(o,n))}),r}return i.prototype=Object.create(Function.prototype),A=i,A}var K=F();const $=M(K);class O extends ${constructor(e={}){super("request"),this.baseConfigs=e,this.hooks=[],this.DEFAULT_CONFIGS={baseURL:"",timeout:6e4,credentials:"same-origin",headers:{},query:{},responseType:void 0},this.ALL_METHODS=["get","post","put","patch","delete","head","options","trace"],this.METHODS_WITHOUT_BODY=["get","head","options","trace"],this.interceptors={request:this.createInterceptor("beforeRequest"),response:this.createInterceptor("afterResponse")},this.create=O.create,this.dropUndefinedAndNull=I,this.checkIsPlainObject=P,this.ALL_METHODS.forEach(this.createMethodShortcut.bind(this))}async request(e,o){var r,n,s,a;let t=o=o||{};typeof e=="string"||e instanceof URL?t.url=e.toString():typeof e=="object"&&(t={...e,...t}),t=await this.emit("beforeInit",t);const u=o.baseURL||this.baseConfigs.baseURL||((r=globalThis.location)==null?void 0:r.href),c=u?new URL(u,(n=globalThis.location)==null?void 0:n.href):void 0,h=new URL(t.url.toString(),c);t.url=h.href,t.baseURL=c?c.href:h.origin,t.headers=this.mergeHeaders(this.baseConfigs.headers,o.headers);const l=c==null?void 0:c.searchParams,w=new URLSearchParams(h.searchParams);if(h.search="",t.url=h.href,t.query=this.mergeQuery(l,this.baseConfigs.query,w,o.query),h.search=B.makeQueryString(t.query),t.url=h.toString(),this.METHODS_WITHOUT_BODY.includes((s=t.method)==null?void 0:s.toLocaleLowerCase())&&t.body)throw new f(g.BODY_NOT_ALLOWED,`Request method "${t.method}" does not allow body`);t=await this.emit("beforeRequest",t);let d;typeof t.body<"u"&&t.body!==null&&(t.body instanceof Blob||t.body instanceof FormData||t.body instanceof URLSearchParams?d=t.body:typeof t.body=="object"&&t.body!==null?(d=JSON.stringify(t.body),t.headers["content-type"]="application/json"):d=t.body),!((a=o.headers)!=null&&a["content-type"])&&d&&(d instanceof FormData||d instanceof URLSearchParams?delete t.headers["content-type"]:typeof d=="string"&&typeof t.body=="object"?t.headers["content-type"]="application/json":d instanceof Blob&&(t.headers["content-type"]=d.type)),t.body=d,t=await this.emit("afterBodyTransformed",t);const R=t.abortController||globalThis.AbortController?new AbortController:void 0,G=new Request(t.url,{method:t.method||"GET",credentials:t.credentials,cache:t.cache,mode:t.mode,headers:t.headers,body:t.body,signal:R==null?void 0:R.signal});t.rawRequest=G,t=await this.emit("beforeActualFetch",t);const S=t.timeout||this.baseConfigs.timeout||60*1e3;if(t.url.startsWith("ws"))try{const y=new WebSocket(t.url);return await new Promise((D,L)=>{const k=setTimeout(()=>{L(new f(g.TIMEOUT,`WebSocket connection timed out after ${S}ms`,t))},S);y.onopen=()=>{clearTimeout(k),D()},y.onerror=q=>{clearTimeout(k),L(new f(g.NETWORK_ERROR,"WebSocket connection failed",t))},y.onclose=q=>{q.code!==1e3&&(clearTimeout(k),L(new f(g.NETWORK_ERROR,`WebSocket closed with code ${q.code}`,t)))}}),t.rawResponse=new Response,t.response=new _(t.rawResponse,y,{ok:!0,status:101,statusText:"Switching Protocols"}),t.data=y,t.headers=new Headers,this.emit("afterResponse",t)}catch(y){throw y instanceof f?y:new f(g.NETWORK_ERROR,`WebSocket creation failed: ${y}`,t)}let E;try{R&&(E=setTimeout(()=>{R.abort()},S));const y=await fetch(t.rawRequest).catch(D=>{throw E&&clearTimeout(E),R!=null&&R.signal.aborted?new f(g.TIMEOUT,`Request timed out after ${S}ms`,t):new f(g.NETWORK_ERROR,D.message,t)});return E&&clearTimeout(E),t.rawResponse=y,t.response=await H(y,t.responseType,(D,L)=>{var k;(k=o==null?void 0:o.onProgress)==null||k.call(o,D,L)}),t.data=t.response.data,t.headers=t.response.headers,this.emit("afterResponse",t)}catch(y){throw E&&clearTimeout(E),y}}mergeQuery(e,...o){const r={},n=s=>{s&&(P(s)?Object.entries(s).forEach(([a,t])=>{t==null?delete r[a]:Array.isArray(t)?(a.endsWith("[]"),r[a]=t.map(String)):r[a]=String(t)}):new URLSearchParams(s).forEach((a,t)=>{r[t]=a}))};return n(e),o.forEach(n),r}mergeHeaders(e,...o){const r={},n=new Headers(e);for(const s of o)if(s!=null)if(P(s)){const a=I(s);if(Object.keys(a).length===0)continue;new Headers(a).forEach((t,u)=>{n.set(u,t)})}else new Headers(s).forEach((a,t)=>{n.set(t,a)});return n.forEach((s,a)=>{r[a]=s}),r}async emit(e,o){const r=this.hooks.filter(n=>n.event===e);try{let n=0;for(const s of r){const a=`${e}#${s.action.name||`anonymous#${n}`}`,t=Symbol("FexiosHookContext");o[t]=t;const u=await s.action.call(this,o);if(u===!1)throw new f(g.ABORTED_BY_HOOK,`Request aborted by hook "${a}"`,o);if(typeof u=="object"&&u[t]===t)o=u;else{const c=globalThis["".concat("console")];try{throw new f(g.HOOK_CONTEXT_CHANGED,`Hook "${a}" should return the original FexiosContext or return false to abort the request, but got "${u}".`)}catch(h){c.warn(h.stack||h)}}delete o[t],n++}}catch(n){return Promise.reject(n)}return o}on(e,o,r=!1){if(typeof o!="function")throw new f(g.INVALID_HOOK_CALLBACK,`Hook should be a function, but got "${typeof o}"`);return this.hooks[r?"unshift":"push"]({event:e,action:o}),this}off(e,o){return e==="*"||!e?this.hooks=this.hooks.filter(r=>r.action!==o):this.hooks=this.hooks.filter(r=>r.event!==e||r.action!==o),this}createInterceptor(e){return{handlers:()=>this.hooks.filter(o=>o.event===e).map(o=>o.action),use:(o,r=!1)=>this.on(e,o,r),clear:()=>{this.hooks=this.hooks.filter(o=>o.event!==e)}}}createMethodShortcut(e){return Object.defineProperty(this,e,{value:(o,r,n)=>(this.METHODS_WITHOUT_BODY.includes(e.toLocaleLowerCase())?n=r:(n=n||{},n.body=r),this.request(o,{...n,method:e}))}),this}extends(e){const o=new O({...this.baseConfigs,...e});return o.hooks=[...this.hooks],o}static create(e){return new O(e)}}/** * Fexios * @desc Fetch based HTTP client with similar API to axios for browser and Node.js * * @license MIT * @author dragon-fish <dragon-fish@qq.com> */const Y=O.create,N=Y();typeof globalThis<"u"?globalThis.fexios=N:typeof window<"u"&&(window.fexios=N);/** * MediaWiki Api for Axios * Provides the API call methods similar to `mw.Api` at non-mw environments * * @author Dragon-Fish <dragon-fish@qq.com> * @license MIT */const m=class m{constructor(e,o,r){var s,a;if(this.baseURL=e,this.version="4.2.1",this.cookies=new Map,!e&&typeof window=="object"&&window.mediaWiki){const{wgServer:t,wgScriptPath:u}=((a=(s=window.mediaWiki)==null?void 0:s.config)==null?void 0:a.get(["wgServer","wgScriptPath"]))||{};typeof t=="string"&&typeof u=="string"&&(e=`${t}${u}/api.php`)}if(typeof e!="string")throw new Error("baseURL is undefined");this.baseURL=e,this.tokens={},this.defaultParams={...m.INIT_DEFAULT_PARAMS,...r},this.defaultOptions=o||{};const n=m.createRequestHandler(this.baseURL);this.request=n,"document"in globalThis||(n.interceptors.request.use(t=>(t.headers=t.headers||{},t.headers.cookie=Array.from(this.cookies.entries()).map(([u,c])=>`${u}=${c}`).join("; "),t)),n.interceptors.response.use(t=>{const u=t.rawResponse.headers.get("set-cookie"),c=u==null?void 0:u.split(",").map(h=>h.trim());return c==null||c.forEach(h=>{const[l,...w]=h.split(";")[0].split("=");this.cookies.set(l,w.join("="))}),t}))}setBaseURL(e){return this.request.baseConfigs.baseURL=e,this}static normalizeParamValue(e){return Array.isArray(e)?e.join("|"):typeof e=="boolean"?e?"1":void 0:typeof e=="number"?""+e:e}static createRequestHandler(e){const o=new O({baseURL:e,responseType:"json"});return o.on("beforeInit",r=>{var n;if(((n=r.method)==null?void 0:n.toLowerCase())!=="post")return r;if(typeof r.body=="object"&&r.body!==null&&!(r.body instanceof URLSearchParams)&&!(r.body instanceof FormData)){const s=r.body;Object.keys(s).forEach(a=>{const t=m.normalizeParamValue(s[a]);typeof t>"u"||t===null?delete s[a]:t!==s[a]&&(s[a]=t)}),r.body=new URLSearchParams(r.body)}if(globalThis.FormData&&r.body instanceof FormData||r.body instanceof URLSearchParams){const s=r.body;s.forEach((t,u)=>{const c=m.normalizeParamValue(t);typeof c>"u"||c===null?s.delete(u):c!==t&&s.set(u,c)});const a=new URLSearchParams(r.query);!a.has("format")&&a.set("format",""+(s.get("format")||"json")),!a.has("formatversion")&&a.set("formatversion",""+(s.get("formatversion")||"2")),s.has("origin")&&a.set("origin",""+s.get("origin")),r.query=Object.fromEntries(a.entries()),s.has("action")&&(r.query.action=""+s.get("action"))}return r}),o.on("beforeInit",r=>{r.query=r.query;for(const n in r.query){const s=m.normalizeParamValue(r.query[n]);typeof s>"u"||s===null?delete r.query[n]:s!==r.query[n]&&(r.query[n]=""+s)}return r}),o.on("beforeRequest",r=>{const n=new URL(r.url),s=n.searchParams;if(globalThis.location&&(!s.has("origin")&&location.origin!==new URL(e).origin?(s.set("origin",location.origin),o.baseConfigs.credentials="include",o.baseConfigs.mode="cors"):location.origin===new URL(e).origin&&(s.delete("origin"),o.baseConfigs.credentials=void 0,o.baseConfigs.mode=void 0)),n.searchParams.has("origin")){const a=encodeURIComponent(n.searchParams.get("origin")||"").replace(/\./g,"%2E");r.query={},n.searchParams.delete("origin"),r.url=`${n}${n.search?"&":"?"}origin=${a}`}return r}),o}get(e,o){return this.request.get("",{...this.defaultOptions,query:{...this.defaultParams,...this.defaultOptions.query,...e},...o})}post(e,o){return this.request.post("",e,{...this.defaultOptions,query:{...this.defaultParams,...this.defaultOptions.query},...o})}async login(e,o,r,n){var a,t,u,c,h;if(this.defaultOptions.credentials="include",n=n||{},n.retry??(n.retry=3),n.retry<1)throw new p("LOGIN_RETRY_LIMIT_EXCEEDED","The limit of the number of times to automatically re-login has been exceeded");let s;try{const l=await this.postWithToken("login",{action:"login",lgname:e,lgpassword:o,...r},{tokenName:"lgtoken",...n});if((a=l==null?void 0:l.data)!=null&&a.login)s=l.data;else throw l}catch(l){if(l instanceof p)throw l;if((l==null?void 0:l.ok)===!1)return this.login(e,o,r,{...n,noCache:!0,retry:n.retry-1});throw new p("HTTP_ERROR","The server returns an error, but it doesn't seem to be caused by MediaWiki",l)}if(((t=s==null?void 0:s.login)==null?void 0:t.result)!=="Success")throw new p("LOGIN_FAILED",((c=(u=s==null?void 0:s.login)==null?void 0:u.reason)==null?void 0:c.text)||((h=s==null?void 0:s.login)==null?void 0:h.result)||"Login failed with unknown reason",s);return s.login}async getUserInfo(){var o;const{data:e}=await this.get({action:"query",meta:"userinfo",uiprop:["groups","rights","blockinfo"]});return(o=e==null?void 0:e.query)==null?void 0:o.userinfo}async getTokens(e=["csrf"]){this.defaultOptions.credentials="include";const{data:o}=await this.get({action:"query",meta:"tokens",type:e});return this.tokens={...this.tokens,...o.query.tokens},this.tokens}async token(e="csrf",o=!1){return(!this.tokens[`${e}token`]||o)&&(delete this.tokens[`${e}token`],await this.getTokens([e])),this.tokens[`${e}token`]}async postWithToken(e,o,r){const{tokenName:n="token",retry:s=3,noCache:a=!1}=r||{};if(s<1)throw new p("TOKEN_RETRY_LIMIT_EXCEEDED","The limit of the number of times to automatically re-acquire the token has been exceeded");const t=await this.token(e,a),u=()=>this.postWithToken(e,o,{tokenName:n,retry:s-1,noCache:!0});return this.post({[n]:t,...o}).then(c=>{const h=c.data;return m.isBadTokenError(h)?u():c}).catch(c=>{const h=c.data;if(m.isBadTokenError(h)||(c==null?void 0:c.ok)===!1)return u();if(typeof h=="object"&&h!==null)return Promise.reject(h);throw new p("HTTP_ERROR","The server returns an error, but it doesn’t seem to be caused by MediaWiki",c)})}postWithEditToken(e){return this.postWithToken("csrf",e)}static isBadTokenError(e){var o,r,n;return((o=e==null?void 0:e.error)==null?void 0:o.code)==="badtoken"||((r=e==null?void 0:e.errors)==null?void 0:r.some(s=>s.code==="badtoken"))||["NeedToken","WrongToken"].includes((n=e==null?void 0:e.login)==null?void 0:n.result)}async getMessages(e,o="zh",r){const{data:n}=await this.get({action:"query",meta:"allmessages",ammessages:e,amlang:o,...r}),s={};return n.query.allmessages.forEach(function(a){a.missing||(s[a.name]=a.content)}),s}async parseWikitext(e,o,r,n){const{data:s}=await this.post({action:"parse",title:o,text:e,...r},n);return s.parse.text}};m.INIT_DEFAULT_PARAMS={action:"query",errorformat:"plaintext",format:"json",formatversion:2};let T=m;class U extends T{constructor(e,o,r){super(e,{credentials:"include",mode:"cors",...o},{origin:location.origin,...r})}}var C=(i=>(i.HTTP_ERROR="HTTP_ERROR",i.LOGIN_FAILED="LOGIN_FAILED",i.LOGIN_RETRY_LIMIT_EXCEEDED="LOGIN_RETRY_LIMIT_EXCEEDED",i.TOKEN_RETRY_LIMIT_EXCEEDED="TOKEN_RETRY_LIMIT_EXCEEDED",i))(C||{});class p extends Error{constructor(e,o="",r){super(),this.code=e,this.message=o,this.cause=r,this.name="WikiSaikouError"}}return b.ForeignApi=U,b.MediaWikiApi=T,b.MediaWikiForeignApi=U,b.MwApi=T,b.WikiSaikouError=p,b.WikiSaikouErrorCode=C,b.default=T,Object.defineProperties(b,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}}),b}({}); //# sourceMappingURL=index.iife.js.map