edge-utils
Version:
Platform-agnostic utilities for edge computing and serverless environments
3 lines (2 loc) • 5.81 kB
JavaScript
"use strict";const e={QUERY:"query",MUTATION:"mutation",SUBSCRIPTION:"subscription"},t={INFO:"info",WARNING:"warning",ERROR:"error",CRITICAL:"critical"};class s{constructor(e={}){this.options={endpoint:"",headers:{},timeout:3e4,enableCache:!0,cacheTTL:3e5,...e},this.cache=new Map,this.activeRequests=new Map,this.timeouts=new Set}async query(t,s={},r={}){return this._execute(e.QUERY,t,s,r)}async mutate(t,s={},r={}){return this._execute(e.MUTATION,t,s,r)}async subscribe(t,s={},r,i={}){return this._execute(e.SUBSCRIPTION,t,s,{...i,subscriptionCallback:r})}async _execute(t,s,r={},i={}){const n=this._generateCacheKey(t,s,r),o=this.options.enableCache&&t===e.QUERY&&!i.skipCache;if(o){const e=this._getCached(n);if(e)return e}if(this.activeRequests.has(n))return this.activeRequests.get(n);const a=this._performRequest(t,s,r,i);o&&this.activeRequests.set(n,a);try{const e=await a;return o&&e.data&&!e.errors&&this._setCached(n,e),this.options.onSuccess&&this.options.onSuccess(t,e),e}catch(e){throw this.options.onError&&this.options.onError(t,e),e}finally{o&&this.activeRequests.delete(n)}}async _performRequest(e,s,i,n){const o={query:s.trim(),variables:i||{},operationName:n.operationName},a={method:"POST",headers:{"Content-Type":"application/json",...this.options.headers,...n.headers},body:JSON.stringify(o),signal:this._createAbortSignal(n.timeout||this.options.timeout)};try{const e=await fetch(this.options.endpoint,a);if(this._clearTimeouts(),!e.ok)throw new r(`HTTP ${e.status}: ${e.statusText}`,t.CRITICAL,{status:e.status,statusText:e.statusText});const s=await e.json();if(s.errors&&s.errors.length>0){const e=this._determineErrorSeverity(s.errors);throw new r("GraphQL operation failed",e,{graphqlErrors:s.errors,data:s.data})}return s}catch(e){if(this._clearTimeouts(),"AbortError"===e.name)throw new r("Request timeout",t.ERROR,{timeout:!0});throw e}}_createAbortSignal(e){if("undefined"==typeof AbortController)return;const t=new AbortController,s=setTimeout(()=>{t.abort(),this.timeouts.delete(s)},e);return this.timeouts.add(s),t.signal}_generateCacheKey(e,t,s){return`${e}:${t.replace(/\s+/g," ").trim()}:${JSON.stringify(s||{})}`}_getCached(e){const t=this.cache.get(e);return t?Date.now()-t.timestamp>this.options.cacheTTL?(this.cache.delete(e),null):t.data:null}_setCached(e,t){this.cache.set(e,{data:t,timestamp:Date.now()}),this.cache.size>100&&this._cleanupCache()}_cleanupCache(){const e=Date.now();for(const[t,s]of this.cache.entries())e-s.timestamp>this.options.cacheTTL&&this.cache.delete(t)}_determineErrorSeverity(e){if(e.some(e=>"INTERNAL_SERVER_ERROR"===e.extensions?.code||e.message.includes("timeout")||e.message.includes("network")))return t.CRITICAL;return e.some(e=>"VALIDATION_ERROR"===e.extensions?.code||"FORBIDDEN"===e.extensions?.code)?t.ERROR:t.WARNING}_clearTimeouts(){for(const e of this.timeouts)clearTimeout(e);this.timeouts.clear()}clearCache(){this.cache.clear(),this.activeRequests.clear(),this._clearTimeouts()}getCacheStats(){return{size:this.cache.size,activeRequests:this.activeRequests.size}}}class r extends Error{constructor(e,s=t.ERROR,r={}){super(e),this.name="GraphQLError",this.severity=s,this.details=r}}module.exports={GraphQLClient:s,GraphQLError:r,GraphQLSchema:class{static validateQuery(e,t){const s=[];e&&"string"==typeof e||s.push({message:"Query must be a non-empty string"});return(e.match(/\{/g)||[]).length!==(e.match(/\}/g)||[]).length&&s.push({message:"Unbalanced braces in query"}),{isValid:0===s.length,errors:s}}static extractOperationNames(e){const t=/(?:query|mutation|subscription)\s+(\w+)/g,s=[];let r;for(;null!==(r=t.exec(e));)s.push(r[1]);return s}static isQueryOperation(e){return/^\s*query\s/i.test(e)}static isMutationOperation(e){return/^\s*mutation\s/i.test(e)}},GraphQLQueryBuilder:class{static buildQuery(e,t,s={}){const r=this._buildArgsString(s),i=this._buildFieldsString(t);return`query ${e}${r} {\n ${e}${r?`(${Object.keys(s).join(", ")})`:""} {\n ${i}\n }\n}`}static buildMutation(e,t,s){return`mutation {\n ${e}(input: ${JSON.stringify(t).replace(/"/g,"")}) {\n ${this._buildFieldsString(s)}\n }\n}`}static _buildArgsString(e){if(!e||0===Object.keys(e).length)return"";return`(${Object.entries(e).map(([e,t])=>"string"==typeof t?`${e}: "${t}"`:`${e}: ${t}`).join(", ")})`}static _buildFieldsString(e){return Array.isArray(e)?e.map(e=>{if("string"==typeof e)return e;if("object"==typeof e){return Object.entries(e).map(([e,t])=>Array.isArray(t)?`${e} { ${this._buildFieldsString(t)} }`:e).join(" ")}return e}).join(" "):e}},createGraphQLMiddleware:function(r={}){const i=new s(r);return{client:i,async handleRequest(s,r,n){try{if("POST"!==s.method)return new Response("Method not allowed",{status:405});const t=s.headers.get("content-type");if(!t||!t.includes("application/json"))return new Response("Content-Type must be application/json",{status:400});const r=await s.json();if(!r.query)return new Response("Missing query in request body",{status:400});const n=await i._execute(r.operationName||e.QUERY,r.query,r.variables,{headers:Object.fromEntries(s.headers.entries())});return new Response(JSON.stringify(n),{status:200,headers:{"Content-Type":"application/json","Access-Control-Allow-Origin":"*","Access-Control-Allow-Methods":"POST, OPTIONS","Access-Control-Allow-Headers":"Content-Type"}})}catch(e){const s=e.severity===t.CRITICAL?500:400;return new Response(JSON.stringify({errors:[{message:e.message,severity:e.severity,details:e.details}]}),{status:s,headers:{"Content-Type":"application/json"}})}},handleOptions:e=>new Response(null,{status:200,headers:{"Access-Control-Allow-Origin":"*","Access-Control-Allow-Methods":"POST, OPTIONS","Access-Control-Allow-Headers":"Content-Type","Access-Control-Max-Age":"86400"}})}},OperationType:e,ErrorSeverity:t};
//# sourceMappingURL=graphql.js.map