getta 0.2.3 → 0.3.0

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/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ### 0.3.0 (2022-06-28)
2
+
3
+ ##### New Features
4
+
5
+ * add rate limiting ([f8275dd8](https://github.com/badbatch/getta/commit/f8275dd83608743bb587df9305cc85936678bced))
6
+
1
7
  #### 0.2.3 (2022-06-27)
2
8
 
3
9
  ##### Refactors
@@ -1,2 +1,2 @@
1
- import e from"@babel/runtime/helpers/defineProperty";import t from"lodash/merge";import r from"lodash/castArray";import"core-js/modules/es.promise.js";import a from"md5";import s from"query-string";const i="arrayBuffer",h="blob",c="formData",o="json",n="text",d={ARRAY_BUFFER_FORMAT:"arrayBuffer",BLOB_FORMAT:"blob",FORM_DATA_FORMAT:"formData",JSON_FORMAT:o,TEXT_FORMAT:"text"},u=e=>e,l=5e3,p={"content-type":"application/json"},m=5,_=3,y=/({type})|({id})|({id,\+})|({brief\|standard})/g,T=/({[a-zA-Z0-9_]+\?})/g,f=100,g="Getta expected to receive 'basePath' in the constructor options,\n but recevied undefined.",R="The request exceeded the maximum number of redirects, which is",E="The request exceeded the maximum number of retries, which is",b="Getta expected to receive 'get', 'post', 'put' or 'delete', but received",v="The requested resource could not been found.",q="The request timed out. Getta did not get a response within",x="get",P="post",w="put",k="delete",C=["get","post","put","delete"],$="information",D="successful",O="redirection",j="clientError",A="serverError",H=304,M=404,F="Cookie",G="ETag",S="Location",W="If-None-Match",B="Cache-Control";function L(e,t,r){const a=Object.keys(t);return e.replace(r,e=>a.reduce((r,a)=>e.includes(a)?t[a]:r,""))}function N(e,t,{optionalPathTemplateRegExp:r,pathTemplateCallback:a,pathTemplateData:i,pathTemplateRegExp:h,queryParams:c}){const o=e.endsWith("/")||t.startsWith("/")?"":"/";let n=`${e}${o}${t}`;if(i&&(n=a(n,i,h)),n=n.replace(r,""),n.endsWith("/")&&(n=n.substring(0,n.length-1)),c&&Object.keys(c).length){n=`${n}${s.extract(n)?"&":"?"}${s.stringify(c)}`}return n}class U{constructor(t){e(this,"_basePath",void 0),e(this,"_bodyParser",void 0),e(this,"_cache",void 0),e(this,"_conditionalRequestsEnabled",void 0),e(this,"_fetchTimeout",void 0),e(this,"_headers",void 0),e(this,"_maxRedirects",void 0),e(this,"_maxRetries",void 0),e(this,"_optionalPathTemplateRegExp",void 0),e(this,"_pathTemplateCallback",void 0),e(this,"_pathTemplateRegExp",void 0),e(this,"_queryParams",void 0),e(this,"_requestRetryWait",void 0),e(this,"_requestTracker",{active:[],pending:new Map}),e(this,"_streamReader",void 0);const{basePath:r,bodyParser:a=u,cache:s,enableConditionalRequests:i=!0,fetchTimeout:h=l,headers:c,maxRedirects:n=m,maxRetries:d=_,optionalPathTemplateRegExp:R=T,pathTemplateCallback:E=L,pathTemplateRegExp:b=y,queryParams:v={},requestRetryWait:q=f,streamReader:x=o}=t;if(!r)throw new Error(g);this._basePath=r,this._bodyParser=a,this._cache=s,this._conditionalRequestsEnabled=i,this._fetchTimeout=h,this._headers={...p,...c||{}},this._maxRedirects=n,this._maxRetries=d,this._optionalPathTemplateRegExp=R,this._pathTemplateCallback=E,this._pathTemplateRegExp=b,this._queryParams=v,this._requestRetryWait=q,this._streamReader=x}get cache(){return this._cache}createShortcut(e,r,{method:a,...s}){if(!C.includes(a))throw new Error("Getta expected to receive 'get', 'post', 'put' or 'delete', but received "+a);this[e]=async({method:e,...i}={})=>this[null!=e?e:a](r,t({},s,i))}async delete(e,t={}){return this._delete(e,t)}async get(e,t={}){return this._get(e,t)}async post(e,t){return this._request(e,{...t,method:"post"})}async put(e,t){return this._request(e,{...t,method:"put"})}async _cacheEntryDelete(e){if(!this._cache)return!1;try{return await this._cache.delete(e)}catch(e){return Promise.reject(e)}}async _cacheEntryGet(e){if(this._cache)try{return await this._cache.get(e)}catch(e){return Promise.reject(e)}}async _cacheEntryHas(e){if(!this._cache)return!1;try{return await this._cache.has(e)}catch(e){return!1}}async _cacheEntrySet(e,t,r){if(this._cache)try{return await this._cache.set(e,t,{cacheHeaders:r})}catch(e){return Promise.reject(e)}}async _delete(e,{headers:t={},pathTemplateData:r,queryParams:s={},...i}){const h=N(this._basePath,e,{optionalPathTemplateRegExp:this._optionalPathTemplateRegExp,pathTemplateCallback:this._pathTemplateCallback,pathTemplateData:r,pathTemplateRegExp:this._pathTemplateRegExp,queryParams:{...this._queryParams,...s}}),c=a(h);return await this._cacheEntryHas(c)&&this._cacheEntryDelete(c),this._fetch(h,{headers:{...this._headers,...t},method:"delete",...i})}async _fetch(e,{redirects:t,retries:a,...s}){try{return new Promise(async(r,i)=>{const h=setTimeout(()=>{i(new Error(`${q} ${this._fetchTimeout}ms.`))},this._fetchTimeout),c=await fetch(e,s);clearTimeout(h);const{headers:o,status:n}=c,d=function(e){switch(!0){case e<200:return"information";case e<300:return"successful";case e<400:return"redirection";case e<500:return"clientError";default:return"serverError"}}(n);"redirection"===d&&o.get("Location")&&r(await this._fetchRedirectHandler(o.get("Location"),{redirects:t,status:n,...s})),"serverError"===d&&r(await this._fetchRetryHandler(e,{retries:a,...s}));const u=c,l=c.clone();try{u.data=c.body?this._bodyParser(await c[this._streamReader]()):void 0,r(u)}catch(t){try{if("json"===this._streamReader&&c.body){const e=l,t=await l.text();e.data=JSON.parse(t),r(e)}else i([t,new Error(`Unable to ${s.method} ${e} due to previous error`)])}catch{i([t,new Error(`Unable to ${s.method} ${e} due to previous error`)])}}})}catch(e){return{errors:r(e)}}}async _fetchRedirectHandler(e,{method:t,redirects:r=1,status:a,...s}){if(r===this._maxRedirects){return{errors:[new Error(`The request exceeded the maximum number of redirects, which is ${this._maxRedirects}.`)]}}r+=1;const i=303===a?"get":t;return this._fetch(e,{method:i,redirects:r,...s})}async _fetchRetryHandler(e,{retries:t=1,...r}){return t===this._maxRetries?{errors:[new Error(`The request exceeded the maximum number of retries, which is ${this._maxRetries}.`)]}:(t+=1,await(a=this._requestRetryWait,new Promise(e=>setTimeout(e,a))),this._fetch(e,{retries:t,...r}));var a}async _get(e,{headers:t={},pathTemplateData:r,queryParams:s={}}){const i=N(this._basePath,e,{optionalPathTemplateRegExp:this._optionalPathTemplateRegExp,pathTemplateCallback:this._pathTemplateCallback,pathTemplateData:r,pathTemplateRegExp:this._pathTemplateRegExp,queryParams:{...this._queryParams,...s}}),h=a(i),c=await this._cacheEntryHas(h);if(c){if(function(e){var t,r,a;return!(null!==(t=null==e||null===(r=e.metadata)||void 0===r||null===(a=r.cacheControl)||void 0===a?void 0:a.noCache)&&void 0!==t&&t)&&e.checkTTL()}(c))return{data:await this._cacheEntryGet(h),headers:new Headers({"cache-control":c.printCacheControl()})};if(this._conditionalRequestsEnabled){var o,n;const e=null!==(o=null==c||null===(n=c.metadata)||void 0===n?void 0:n.etag)&&void 0!==o?o:null;e&&(t["If-None-Match"]=e)}}const d=this._trackRequest(h);return d||this._getResolve(h,await this._fetch(i,{headers:{...this._headers,...t},method:"get"}))}async _getResolve(e,t){const{data:r,headers:a,status:s}=t;if(404===s)this._cacheEntryDelete(e),t.errors||(t.errors=[]),t.errors.push(new Error("The requested resource could not been found."));else if(304===s&&a){const r=await this._cacheEntryGet(e);r&&(this._cacheEntrySet(e,r,{cacheControl:a.get("Cache-Control")||void 0,etag:a.get("ETag")||void 0}),t.data=r)}else r&&a&&this._cacheEntrySet(e,r,{cacheControl:a.get("Cache-Control")||void 0,etag:a.get("ETag")||void 0});return this._resolvePendingRequests(e,t),this._requestTracker.active=this._requestTracker.active.filter(t=>t!==e),t}async _request(e,{body:t,headers:r,method:a,pathTemplateData:s,queryParams:i,...h}){const c=N(this._basePath,e,{optionalPathTemplateRegExp:this._optionalPathTemplateRegExp,pathTemplateCallback:this._pathTemplateCallback,pathTemplateData:s,pathTemplateRegExp:this._pathTemplateRegExp,queryParams:{...this._queryParams,...i}});return this._fetch(c,{body:t,headers:{...this._headers,...r},method:a,...h})}_resolvePendingRequests(e,t){const r=this._requestTracker.pending.get(e);r&&(r.forEach(({resolve:e})=>{e(t)}),this._requestTracker.pending.delete(e))}_setPendingRequest(e,t){let r=this._requestTracker.pending.get(e);r||(r=[]),r.push(t),this._requestTracker.pending.set(e,r)}_trackRequest(e){if(this._requestTracker.active.includes(e))return new Promise(t=>{this._setPendingRequest(e,{resolve:t})});this._requestTracker.active.push(e)}}export default function(e,t){const r=new U(e);return t?(Object.keys(t).forEach(e=>{r.createShortcut(e,...t[e])}),r):r}export{i as ARRAY_BUFFER_FORMAT,h as BLOB_FORMAT,B as CACHE_CONTROL_HEADER,j as CLIENT_ERROR_REPSONSE,F as COOKIE_HEADER,u as DEFAULT_BODY_PARSER,l as DEFAULT_FETCH_TIMEOUT,p as DEFAULT_HEADERS,m as DEFAULT_MAX_REDIRECTS,_ as DEFAULT_MAX_RETRIES,y as DEFAULT_PATH_TEMPLATE_REGEX,f as DEFAULT_REQUEST_RETRY_WAIT,k as DELETE_METHOD,G as ETAG_HEADER,C as FETCH_METHODS,q as FETCH_TIMEOUT_ERROR,c as FORM_DATA_FORMAT,x as GET_METHOD,U as Getta,W as IF_NONE_MATCH_HEADER,$ as INFORMATION_REPSONSE,b as INVALID_FETCH_METHOD_ERROR,o as JSON_FORMAT,S as LOCATION_HEADER,R as MAX_REDIRECTS_EXCEEDED_ERROR,E as MAX_RETRIES_EXCEEDED_ERROR,g as MISSING_BASE_PATH_ERROR,M as NOT_FOUND_STATUS_CODE,H as NOT_MODIFIED_STATUS_CODE,T as OPTIONAL_PATH_TEMPLATE_REGEX,P as POST_METHOD,w as PUT_METHOD,O as REDIRECTION_REPSONSE,v as RESOURCE_NOT_FOUND_ERROR,A as SERVER_ERROR_REPSONSE,d as STREAM_READERS,D as SUCCESSFUL_REPSONSE,n as TEXT_FORMAT,L as defaultPathTemplateCallback};
1
+ import e from"@babel/runtime/helpers/defineProperty";import t from"lodash/merge";import r from"lodash/castArray";import"core-js/modules/es.promise.js";import a from"md5";import i from"query-string";const s="arrayBuffer",h="blob",c="formData",o="json",n="text",u={ARRAY_BUFFER_FORMAT:"arrayBuffer",BLOB_FORMAT:"blob",FORM_DATA_FORMAT:"formData",JSON_FORMAT:o,TEXT_FORMAT:"text"},d=e=>e,l=5e3,m={"content-type":"application/json"},_=5,p=3,T=/({type})|({id})|({id,\+})|({brief\|standard})/g,R=/({[a-zA-Z0-9_]+\?})/g,y=50,f=100,g="Getta expected to receive 'basePath' in the constructor options,\n but recevied undefined.",q="The request exceeded the maximum number of redirects, which is",E="The request exceeded the maximum number of retries, which is",v="Getta expected to receive 'get', 'post', 'put' or 'delete', but received",P="The requested resource could not been found.",b="The request timed out. Getta did not get a response within",x="get",w="post",C="put",L="delete",k=["get","post","put","delete"],D="information",$="successful",A="redirection",O="clientError",j="serverError",S=304,H=404,M="Cookie",Q="ETag",F="Location",G="If-None-Match",W="Cache-Control";function B(e,t,r){const a=Object.keys(t);return e.replace(r,e=>a.reduce((r,a)=>e.includes(a)?t[a]:r,""))}function N(e,t,{optionalPathTemplateRegExp:r,pathTemplateCallback:a,pathTemplateData:s,pathTemplateRegExp:h,queryParams:c}){const o=e.endsWith("/")||t.startsWith("/")?"":"/";let n=`${e}${o}${t}`;if(s&&(n=a(n,s,h)),n=n.replace(r,""),n.endsWith("/")&&(n=n.substring(0,n.length-1)),c&&Object.keys(c).length){n=`${n}${i.extract(n)?"&":"?"}${i.stringify(c)}`}return n}class I{constructor(t){e(this,"_basePath",void 0),e(this,"_bodyParser",void 0),e(this,"_cache",void 0),e(this,"_conditionalRequestsEnabled",void 0),e(this,"_fetchTimeout",void 0),e(this,"_headers",void 0),e(this,"_maxRedirects",void 0),e(this,"_maxRetries",void 0),e(this,"_optionalPathTemplateRegExp",void 0),e(this,"_pathTemplateCallback",void 0),e(this,"_pathTemplateRegExp",void 0),e(this,"_queryParams",void 0),e(this,"_rateLimitCount",0),e(this,"_rateLimitedRequestQueue",[]),e(this,"_rateLimitPerSecond",void 0),e(this,"_rateLimitTimer",null),e(this,"_requestRetryWait",void 0),e(this,"_requestTracker",{active:[],pending:new Map}),e(this,"_streamReader",void 0);const{basePath:r,bodyParser:a=d,cache:i,enableConditionalRequests:s=!0,fetchTimeout:h=l,headers:c,maxRedirects:n=_,maxRetries:u=p,optionalPathTemplateRegExp:q=R,pathTemplateCallback:E=B,pathTemplateRegExp:v=T,queryParams:P={},rateLimitPerSecond:b=y,requestRetryWait:x=f,streamReader:w=o}=t;if(!r)throw new Error(g);this._basePath=r,this._bodyParser=a,this._cache=i,this._conditionalRequestsEnabled=s,this._fetchTimeout=h,this._headers={...m,...c||{}},this._maxRedirects=n,this._maxRetries=u,this._optionalPathTemplateRegExp=q,this._pathTemplateCallback=E,this._pathTemplateRegExp=v,this._queryParams=P,this._rateLimitPerSecond=b,this._requestRetryWait=x,this._streamReader=w}get cache(){return this._cache}createShortcut(e,r,{method:a,...i}){if(!k.includes(a))throw new Error("Getta expected to receive 'get', 'post', 'put' or 'delete', but received "+a);this[e]=async({method:e,...s}={})=>this[null!=e?e:a](r,t({},i,s))}async delete(e,t={}){return this._delete(e,t)}async get(e,t={}){return this._get(e,t)}async post(e,t){return this._request(e,{...t,method:"post"})}async put(e,t){return this._request(e,{...t,method:"put"})}_addRequestToRateLimitedQueue(e,t){return new Promise(r=>{this._rateLimitedRequestQueue.push([r,e,t])})}async _cacheEntryDelete(e){if(!this._cache)return!1;try{return await this._cache.delete(e)}catch(e){return Promise.reject(e)}}async _cacheEntryGet(e){if(this._cache)try{return await this._cache.get(e)}catch(e){return Promise.reject(e)}}async _cacheEntryHas(e){if(!this._cache)return!1;try{return await this._cache.has(e)}catch(e){return!1}}async _cacheEntrySet(e,t,r){if(this._cache)try{return await this._cache.set(e,t,{cacheHeaders:r})}catch(e){return Promise.reject(e)}}async _delete(e,{headers:t={},pathTemplateData:r,queryParams:i={},...s}){const h=N(this._basePath,e,{optionalPathTemplateRegExp:this._optionalPathTemplateRegExp,pathTemplateCallback:this._pathTemplateCallback,pathTemplateData:r,pathTemplateRegExp:this._pathTemplateRegExp,queryParams:{...this._queryParams,...i}}),c=a(h);return await this._cacheEntryHas(c)&&this._cacheEntryDelete(c),this._fetch(h,{headers:{...this._headers,...t},method:"delete",...s})}async _fetch(e,{redirects:t,retries:a,...i}){try{return new Promise(async(r,s)=>{const h=setTimeout(()=>{s(new Error(`${b} ${this._fetchTimeout}ms.`))},this._fetchTimeout);if(this._rateLimit(),!(this._rateLimitCount<this._rateLimitPerSecond))return void r(await this._addRequestToRateLimitedQueue(e,{redirects:t,retries:a,...i}));const c=await fetch(e,i);clearTimeout(h);const{headers:o,status:n}=c,u=function(e){switch(!0){case e<200:return"information";case e<300:return"successful";case e<400:return"redirection";case e<500:return"clientError";default:return"serverError"}}(n);if("redirection"===u&&o.get("Location"))return void r(await this._fetchRedirectHandler(o.get("Location"),{redirects:t,status:n,...i}));if("serverError"===u)return void r(await this._fetchRetryHandler(e,{retries:a,...i}));const d=c;try{d.data=c.body?this._bodyParser(await c[this._streamReader]()):void 0,r(d)}catch(t){s([t,new Error(`Unable to ${i.method} ${e} due to previous error`)])}})}catch(e){return{errors:r(e)}}}async _fetchRedirectHandler(e,{method:t,redirects:r=1,status:a,...i}){if(r===this._maxRedirects){return{errors:[new Error(`The request exceeded the maximum number of redirects, which is ${this._maxRedirects}.`)]}}r+=1;const s=303===a?"get":t;return this._fetch(e,{method:s,redirects:r,...i})}async _fetchRetryHandler(e,{retries:t=1,...r}){return t===this._maxRetries?{errors:[new Error(`The request exceeded the maximum number of retries, which is ${this._maxRetries}.`)]}:(t+=1,await(a=this._requestRetryWait,new Promise(e=>setTimeout(e,a))),this._fetch(e,{retries:t,...r}));var a}async _get(e,{headers:t={},pathTemplateData:r,queryParams:i={}}){const s=N(this._basePath,e,{optionalPathTemplateRegExp:this._optionalPathTemplateRegExp,pathTemplateCallback:this._pathTemplateCallback,pathTemplateData:r,pathTemplateRegExp:this._pathTemplateRegExp,queryParams:{...this._queryParams,...i}}),h=a(s),c=await this._cacheEntryHas(h);if(c){if(function(e){var t,r,a;return!(null!==(t=null==e||null===(r=e.metadata)||void 0===r||null===(a=r.cacheControl)||void 0===a?void 0:a.noCache)&&void 0!==t&&t)&&e.checkTTL()}(c))return{data:await this._cacheEntryGet(h),headers:new Headers({"cache-control":c.printCacheControl()})};if(this._conditionalRequestsEnabled){var o,n;const e=null!==(o=null==c||null===(n=c.metadata)||void 0===n?void 0:n.etag)&&void 0!==o?o:null;e&&(t["If-None-Match"]=e)}}const u=this._trackRequest(h);return u||this._getResolve(h,await this._fetch(s,{headers:{...this._headers,...t},method:"get"}))}async _getResolve(e,t){const{data:r,headers:a,status:i}=t;if(404===i)this._cacheEntryDelete(e),t.errors||(t.errors=[]),t.errors.push(new Error("The requested resource could not been found."));else if(304===i&&a){const r=await this._cacheEntryGet(e);r&&(this._cacheEntrySet(e,r,{cacheControl:a.get("Cache-Control")||void 0,etag:a.get("ETag")||void 0}),t.data=r)}else r&&a&&this._cacheEntrySet(e,r,{cacheControl:a.get("Cache-Control")||void 0,etag:a.get("ETag")||void 0});return this._resolvePendingRequests(e,t),this._requestTracker.active=this._requestTracker.active.filter(t=>t!==e),t}_rateLimit(){this._rateLimitTimer||(this._rateLimitTimer=setTimeout(()=>{this._rateLimitTimer=null,this._rateLimitCount=0,this._rateLimitedRequestQueue.length&&this._releaseRateLimitedRequestQueue()},1e3)),this._rateLimitCount+=1}_releaseRateLimitedRequestQueue(){this._rateLimitedRequestQueue.forEach(async([e,t,r])=>{e(await this._fetch(t,r))}),this._rateLimitedRequestQueue=[]}async _request(e,{body:t,headers:r,method:a,pathTemplateData:i,queryParams:s,...h}){const c=N(this._basePath,e,{optionalPathTemplateRegExp:this._optionalPathTemplateRegExp,pathTemplateCallback:this._pathTemplateCallback,pathTemplateData:i,pathTemplateRegExp:this._pathTemplateRegExp,queryParams:{...this._queryParams,...s}});return this._fetch(c,{body:t,headers:{...this._headers,...r},method:a,...h})}_resolvePendingRequests(e,t){const r=this._requestTracker.pending.get(e);r&&(r.forEach(({resolve:e})=>{e(t)}),this._requestTracker.pending.delete(e))}_setPendingRequest(e,t){let r=this._requestTracker.pending.get(e);r||(r=[]),r.push(t),this._requestTracker.pending.set(e,r)}_trackRequest(e){if(this._requestTracker.active.includes(e))return new Promise(t=>{this._setPendingRequest(e,{resolve:t})});this._requestTracker.active.push(e)}}export default function(e,t){const r=new I(e);return t?(Object.keys(t).forEach(e=>{r.createShortcut(e,...t[e])}),r):r}export{s as ARRAY_BUFFER_FORMAT,h as BLOB_FORMAT,W as CACHE_CONTROL_HEADER,O as CLIENT_ERROR_REPSONSE,M as COOKIE_HEADER,d as DEFAULT_BODY_PARSER,l as DEFAULT_FETCH_TIMEOUT,m as DEFAULT_HEADERS,_ as DEFAULT_MAX_REDIRECTS,p as DEFAULT_MAX_RETRIES,T as DEFAULT_PATH_TEMPLATE_REGEX,y as DEFAULT_RATE_LIMIT,f as DEFAULT_REQUEST_RETRY_WAIT,L as DELETE_METHOD,Q as ETAG_HEADER,k as FETCH_METHODS,b as FETCH_TIMEOUT_ERROR,c as FORM_DATA_FORMAT,x as GET_METHOD,I as Getta,G as IF_NONE_MATCH_HEADER,D as INFORMATION_REPSONSE,v as INVALID_FETCH_METHOD_ERROR,o as JSON_FORMAT,F as LOCATION_HEADER,q as MAX_REDIRECTS_EXCEEDED_ERROR,E as MAX_RETRIES_EXCEEDED_ERROR,g as MISSING_BASE_PATH_ERROR,H as NOT_FOUND_STATUS_CODE,S as NOT_MODIFIED_STATUS_CODE,R as OPTIONAL_PATH_TEMPLATE_REGEX,w as POST_METHOD,C as PUT_METHOD,A as REDIRECTION_REPSONSE,P as RESOURCE_NOT_FOUND_ERROR,j as SERVER_ERROR_REPSONSE,u as STREAM_READERS,$ as SUCCESSFUL_REPSONSE,n as TEXT_FORMAT,B as defaultPathTemplateCallback};
2
2
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../getta/src/constants.ts","../getta/src/helpers/default-path-template-callback/index.ts","../getta/src/helpers/build-endpoint/index.ts","../getta/src/main.ts","../getta/src/helpers/get-response-group/index.ts","../getta/src/helpers/delay/index.ts","../getta/src/helpers/is-cacheability-valid/index.ts"],"sourcesContent":["import { PlainObject } from \"@repodog/types\";\n\nexport const ARRAY_BUFFER_FORMAT = \"arrayBuffer\" as const;\nexport const BLOB_FORMAT = \"blob\" as const;\nexport const FORM_DATA_FORMAT = \"formData\" as const;\nexport const JSON_FORMAT = \"json\" as const;\nexport const TEXT_FORMAT = \"text\" as const;\n\nexport const STREAM_READERS = {\n ARRAY_BUFFER_FORMAT,\n BLOB_FORMAT,\n FORM_DATA_FORMAT,\n JSON_FORMAT,\n TEXT_FORMAT,\n};\n\nexport const DEFAULT_BODY_PARSER = (body: PlainObject) => body;\nexport const DEFAULT_FETCH_TIMEOUT = 5000 as const;\nexport const DEFAULT_HEADERS = { \"content-type\": \"application/json\" };\nexport const DEFAULT_MAX_REDIRECTS = 5 as const;\nexport const DEFAULT_MAX_RETRIES = 3 as const;\nexport const DEFAULT_PATH_TEMPLATE_REGEX = /({type})|({id})|({id,\\+})|({brief\\|standard})/g;\nexport const OPTIONAL_PATH_TEMPLATE_REGEX = /({[a-zA-Z0-9_]+\\?})/g;\nexport const DEFAULT_REQUEST_RETRY_WAIT = 100;\n\nexport const MISSING_BASE_PATH_ERROR = `Getta expected to receive 'basePath' in the constructor options,\n but recevied undefined.`;\n\nexport const MAX_REDIRECTS_EXCEEDED_ERROR = \"The request exceeded the maximum number of redirects, which is\";\n\nexport const MAX_RETRIES_EXCEEDED_ERROR = \"The request exceeded the maximum number of retries, which is\";\n\nexport const INVALID_FETCH_METHOD_ERROR = \"Getta expected to receive 'get', 'post', 'put' or 'delete', but received\";\n\nexport const RESOURCE_NOT_FOUND_ERROR = \"The requested resource could not been found.\";\n\nexport const FETCH_TIMEOUT_ERROR = \"The request timed out. Getta did not get a response within\";\n\nexport const GET_METHOD = \"get\" as const;\nexport const POST_METHOD = \"post\" as const;\nexport const PUT_METHOD = \"put\" as const;\nexport const DELETE_METHOD = \"delete\" as const;\n\nexport const FETCH_METHODS = [GET_METHOD, POST_METHOD, PUT_METHOD, DELETE_METHOD];\n\nexport const INFORMATION_REPSONSE = \"information\" as const;\nexport const SUCCESSFUL_REPSONSE = \"successful\" as const;\nexport const REDIRECTION_REPSONSE = \"redirection\" as const;\nexport const CLIENT_ERROR_REPSONSE = \"clientError\" as const;\nexport const SERVER_ERROR_REPSONSE = \"serverError\" as const;\n\nexport const NOT_MODIFIED_STATUS_CODE = 304 as const;\nexport const NOT_FOUND_STATUS_CODE = 404 as const;\n\nexport const COOKIE_HEADER = \"Cookie\" as const;\nexport const ETAG_HEADER = \"ETag\" as const;\nexport const LOCATION_HEADER = \"Location\" as const;\nexport const IF_NONE_MATCH_HEADER = \"If-None-Match\" as const;\nexport const CACHE_CONTROL_HEADER = \"Cache-Control\" as const;\n","import { StringObject } from \"@repodog/types\";\n\nexport default function defaultPathTemplateCallback(\n pathTemplate: string,\n data: StringObject,\n pathTemplateRegExp: RegExp,\n) {\n const dataKeys = Object.keys(data);\n\n return pathTemplate.replace(pathTemplateRegExp, match => {\n return dataKeys.reduce((value, key) => {\n if (match.includes(key)) return data[key];\n return value;\n }, \"\");\n });\n}\n","import queryString from \"query-string\";\nimport { BuildEndpointOptions } from \"./types\";\n\nexport default function buildEndpoint(\n basePath: string,\n path: string,\n {\n optionalPathTemplateRegExp,\n pathTemplateCallback,\n pathTemplateData,\n pathTemplateRegExp,\n queryParams,\n }: BuildEndpointOptions,\n) {\n const pathJoiner = basePath.endsWith(\"/\") || path.startsWith(\"/\") ? \"\" : \"/\";\n let endpoint = `${basePath}${pathJoiner}${path}`;\n\n if (pathTemplateData) {\n endpoint = pathTemplateCallback(endpoint, pathTemplateData, pathTemplateRegExp);\n }\n\n endpoint = endpoint.replace(optionalPathTemplateRegExp, \"\");\n\n if (endpoint.endsWith(\"/\")) {\n endpoint = endpoint.substring(0, endpoint.length - 1);\n }\n\n if (queryParams && Object.keys(queryParams).length) {\n const queryJoin = queryString.extract(endpoint) ? \"&\" : \"?\";\n endpoint = `${endpoint}${queryJoin}${queryString.stringify(queryParams)}`;\n }\n\n return endpoint;\n}\n","import Cachemap, { CacheHeaders } from \"@cachemap/core\";\nimport { Func, PlainObject, StringObject } from \"@repodog/types\";\nimport Cacheability from \"cacheability\";\nimport { castArray, merge } from \"lodash\";\nimport md5 from \"md5\";\nimport { Required } from \"utility-types\";\nimport {\n CACHE_CONTROL_HEADER,\n DEFAULT_BODY_PARSER,\n DEFAULT_FETCH_TIMEOUT,\n DEFAULT_HEADERS,\n DEFAULT_MAX_REDIRECTS,\n DEFAULT_MAX_RETRIES,\n DEFAULT_PATH_TEMPLATE_REGEX,\n DEFAULT_REQUEST_RETRY_WAIT,\n DELETE_METHOD,\n ETAG_HEADER,\n FETCH_METHODS,\n FETCH_TIMEOUT_ERROR,\n GET_METHOD,\n IF_NONE_MATCH_HEADER,\n INVALID_FETCH_METHOD_ERROR,\n JSON_FORMAT,\n LOCATION_HEADER,\n MAX_REDIRECTS_EXCEEDED_ERROR,\n MAX_RETRIES_EXCEEDED_ERROR,\n MISSING_BASE_PATH_ERROR,\n NOT_FOUND_STATUS_CODE,\n NOT_MODIFIED_STATUS_CODE,\n OPTIONAL_PATH_TEMPLATE_REGEX,\n POST_METHOD,\n PUT_METHOD,\n REDIRECTION_REPSONSE,\n RESOURCE_NOT_FOUND_ERROR,\n SERVER_ERROR_REPSONSE,\n} from \"./constants\";\nimport buildEndpoint from \"./helpers/build-endpoint\";\nimport defaultPathTemplateCallback from \"./helpers/default-path-template-callback\";\nimport delay from \"./helpers/delay\";\nimport getResponseGroup from \"./helpers/get-response-group\";\nimport isCacheabilityValid from \"./helpers/is-cacheability-valid\";\nimport {\n ConstructorOptions,\n FetchOptions,\n FetchRedirectHandlerOptions,\n FetchResponse,\n PathTemplateCallback,\n PendingRequestResolver,\n PendingRequestResolvers,\n RequestOptions,\n RequestTracker,\n ShortcutProperties,\n Shortcuts,\n StreamReader,\n} from \"./types\";\n\nexport class Getta {\n private _basePath: string;\n private _bodyParser: Func;\n private _cache?: Cachemap;\n private _conditionalRequestsEnabled: boolean;\n private _fetchTimeout: number;\n private _headers: StringObject;\n private _maxRedirects: number;\n private _maxRetries: number;\n private _optionalPathTemplateRegExp: RegExp;\n private _pathTemplateCallback: PathTemplateCallback;\n private _pathTemplateRegExp: RegExp;\n private _queryParams: PlainObject;\n private _requestRetryWait: number;\n private _requestTracker: RequestTracker = { active: [], pending: new Map() };\n private _streamReader: StreamReader;\n\n constructor(options: ConstructorOptions) {\n const {\n basePath,\n bodyParser = DEFAULT_BODY_PARSER,\n cache,\n enableConditionalRequests = true,\n fetchTimeout = DEFAULT_FETCH_TIMEOUT,\n headers,\n maxRedirects = DEFAULT_MAX_REDIRECTS,\n maxRetries = DEFAULT_MAX_RETRIES,\n optionalPathTemplateRegExp = OPTIONAL_PATH_TEMPLATE_REGEX,\n pathTemplateCallback = defaultPathTemplateCallback,\n pathTemplateRegExp = DEFAULT_PATH_TEMPLATE_REGEX,\n queryParams = {},\n requestRetryWait = DEFAULT_REQUEST_RETRY_WAIT,\n streamReader = JSON_FORMAT,\n } = options;\n\n if (!basePath) {\n throw new Error(MISSING_BASE_PATH_ERROR);\n }\n\n this._basePath = basePath;\n this._bodyParser = bodyParser;\n this._cache = cache;\n this._conditionalRequestsEnabled = enableConditionalRequests;\n this._fetchTimeout = fetchTimeout;\n this._headers = { ...DEFAULT_HEADERS, ...(headers || {}) };\n this._maxRedirects = maxRedirects;\n this._maxRetries = maxRetries;\n this._optionalPathTemplateRegExp = optionalPathTemplateRegExp;\n this._pathTemplateCallback = pathTemplateCallback;\n this._pathTemplateRegExp = pathTemplateRegExp;\n this._queryParams = queryParams;\n this._requestRetryWait = requestRetryWait;\n this._streamReader = streamReader;\n }\n\n get cache(): Cachemap | undefined {\n return this._cache;\n }\n\n public createShortcut(name: string, path: string, { method, ...rest }: Required<RequestOptions, \"method\">) {\n if (!FETCH_METHODS.includes(method)) {\n throw new Error(`${INVALID_FETCH_METHOD_ERROR} ${method}`);\n }\n\n // @ts-ignore\n this[name] = async <Resource extends PlainObject>({ method: requestMethod, ...requestRest }: RequestOptions = {}) =>\n // @ts-ignore\n this[requestMethod ?? method](path, merge({}, rest, requestRest)) as Promise<FetchResponse<Resource>>;\n }\n\n public async delete(path: string, options: Omit<RequestOptions, \"method\"> = {}) {\n return this._delete(path, options);\n }\n\n public async get(path: string, options: Omit<RequestOptions, \"method\"> = {}) {\n return this._get(path, options);\n }\n\n public async post(path: string, options: Omit<Required<RequestOptions, \"body\">, \"method\">) {\n return this._request(path, { ...options, method: POST_METHOD });\n }\n\n public async put(path: string, options: Omit<Required<RequestOptions, \"body\">, \"methood\">) {\n return this._request(path, { ...options, method: PUT_METHOD });\n }\n\n private async _cacheEntryDelete(requestHash: string): Promise<boolean> {\n if (!this._cache) return false;\n\n try {\n return await this._cache.delete(requestHash);\n } catch (errors) {\n return Promise.reject(errors);\n }\n }\n\n private async _cacheEntryGet(requestHash: string): Promise<PlainObject | undefined> {\n if (!this._cache) return undefined;\n\n try {\n return await this._cache.get(requestHash);\n } catch (errors) {\n return Promise.reject(errors);\n }\n }\n\n private async _cacheEntryHas(requestHash: string): Promise<Cacheability | false> {\n if (!this._cache) return false;\n\n try {\n return await this._cache.has(requestHash);\n } catch (error) {\n return false;\n }\n }\n\n private async _cacheEntrySet(requestHash: string, data: PlainObject, cacheHeaders: CacheHeaders): Promise<void> {\n if (!this._cache) return undefined;\n\n try {\n return await this._cache.set(requestHash, data, { cacheHeaders });\n } catch (error) {\n return Promise.reject(error);\n }\n }\n\n private async _delete(\n path: string,\n { headers = {}, pathTemplateData, queryParams = {}, ...rest }: Omit<RequestOptions, \"method\">,\n ) {\n const endpoint = buildEndpoint(this._basePath, path, {\n optionalPathTemplateRegExp: this._optionalPathTemplateRegExp,\n pathTemplateCallback: this._pathTemplateCallback,\n pathTemplateData,\n pathTemplateRegExp: this._pathTemplateRegExp,\n queryParams: { ...this._queryParams, ...queryParams },\n });\n\n const requestHash = md5(endpoint);\n const cacheability = await this._cacheEntryHas(requestHash);\n\n if (cacheability) {\n this._cacheEntryDelete(requestHash);\n }\n\n return this._fetch(endpoint, {\n headers: { ...this._headers, ...headers },\n method: DELETE_METHOD,\n ...rest,\n });\n }\n\n private async _fetch(endpoint: string, { redirects, retries, ...rest }: FetchOptions): Promise<FetchResponse> {\n try {\n return new Promise(async (resolve: (value: FetchResponse) => void, reject) => {\n const fetchTimer = setTimeout(() => {\n reject(new Error(`${FETCH_TIMEOUT_ERROR} ${this._fetchTimeout}ms.`));\n }, this._fetchTimeout);\n\n const res = await fetch(endpoint, rest);\n\n clearTimeout(fetchTimer);\n\n const { headers, status } = res;\n const responseGroup = getResponseGroup(status);\n\n if (responseGroup === REDIRECTION_REPSONSE && headers.get(LOCATION_HEADER)) {\n resolve(\n await this._fetchRedirectHandler(headers.get(LOCATION_HEADER) as string, {\n redirects,\n status,\n ...rest,\n }),\n );\n }\n\n if (responseGroup === SERVER_ERROR_REPSONSE) {\n resolve(\n (await this._fetchRetryHandler(endpoint, {\n retries,\n ...rest,\n })) as FetchResponse,\n );\n }\n\n const fetchRes = res as FetchResponse;\n const resClone = res.clone();\n\n try {\n fetchRes.data = res.body ? this._bodyParser(await res[this._streamReader]()) : undefined;\n resolve(fetchRes);\n } catch (e) {\n try {\n if (this._streamReader === \"json\" && res.body) {\n const fetchResClone = resClone as FetchResponse;\n const text = await resClone.text();\n fetchResClone.data = JSON.parse(text);\n resolve(fetchResClone);\n } else {\n reject([e, new Error(`Unable to ${rest.method} ${endpoint} due to previous error`)]);\n }\n } catch {\n reject([e, new Error(`Unable to ${rest.method} ${endpoint} due to previous error`)]);\n }\n }\n });\n } catch (error) {\n const errorRes = { errors: castArray(error) };\n return errorRes as FetchResponse;\n }\n }\n\n private async _fetchRedirectHandler(\n endpoint: string,\n { method, redirects = 1, status, ...rest }: FetchRedirectHandlerOptions,\n ): Promise<FetchResponse> {\n if (redirects === this._maxRedirects) {\n const errorRes = {\n errors: [new Error(`${MAX_REDIRECTS_EXCEEDED_ERROR} ${this._maxRedirects}.`)],\n };\n\n return errorRes as FetchResponse;\n }\n\n redirects += 1;\n const redirectMethod = status === 303 ? GET_METHOD : method;\n return this._fetch(endpoint, { method: redirectMethod, redirects, ...rest });\n }\n\n private async _fetchRetryHandler(endpoint: string, { retries = 1, ...rest }: FetchOptions) {\n if (retries === this._maxRetries) {\n return {\n errors: [new Error(`${MAX_RETRIES_EXCEEDED_ERROR} ${this._maxRetries}.`)],\n };\n }\n\n retries += 1;\n await delay(this._requestRetryWait);\n return this._fetch(endpoint, { retries, ...rest });\n }\n\n private async _get(\n path: string,\n { headers = {}, pathTemplateData, queryParams = {} }: Omit<RequestOptions, \"method\">,\n ) {\n const endpoint = buildEndpoint(this._basePath, path, {\n optionalPathTemplateRegExp: this._optionalPathTemplateRegExp,\n pathTemplateCallback: this._pathTemplateCallback,\n pathTemplateData,\n pathTemplateRegExp: this._pathTemplateRegExp,\n queryParams: { ...this._queryParams, ...queryParams },\n });\n\n const requestHash = md5(endpoint);\n const cacheability = await this._cacheEntryHas(requestHash);\n\n if (cacheability) {\n if (isCacheabilityValid(cacheability)) {\n return {\n data: await this._cacheEntryGet(requestHash),\n headers: new Headers({ \"cache-control\": cacheability.printCacheControl() }),\n };\n }\n\n if (this._conditionalRequestsEnabled) {\n const etag = cacheability?.metadata?.etag ?? null;\n if (etag) headers[IF_NONE_MATCH_HEADER] = etag;\n }\n }\n\n const pendingRequest = this._trackRequest(requestHash);\n if (pendingRequest) return pendingRequest;\n\n return this._getResolve(\n requestHash,\n await this._fetch(endpoint, { headers: { ...this._headers, ...headers }, method: GET_METHOD }),\n );\n }\n\n private async _getResolve(requestHash: string, res: FetchResponse) {\n const { data, headers, status } = res;\n\n if (status === NOT_FOUND_STATUS_CODE) {\n this._cacheEntryDelete(requestHash);\n\n if (!res.errors) {\n res.errors = [];\n }\n\n res.errors.push(new Error(RESOURCE_NOT_FOUND_ERROR));\n } else if (status === NOT_MODIFIED_STATUS_CODE && headers) {\n const cachedData = await this._cacheEntryGet(requestHash);\n\n if (cachedData) {\n this._cacheEntrySet(requestHash, cachedData, {\n cacheControl: headers.get(CACHE_CONTROL_HEADER) || undefined,\n etag: headers.get(ETAG_HEADER) || undefined,\n });\n\n res.data = cachedData;\n }\n } else if (data && headers) {\n this._cacheEntrySet(requestHash, data, {\n cacheControl: headers.get(CACHE_CONTROL_HEADER) || undefined,\n etag: headers.get(ETAG_HEADER) || undefined,\n });\n }\n\n this._resolvePendingRequests(requestHash, res);\n this._requestTracker.active = this._requestTracker.active.filter(value => value !== requestHash);\n return res;\n }\n\n private async _request(\n path: string,\n { body, headers, method, pathTemplateData, queryParams, ...rest }: Required<RequestOptions, \"method\">,\n ) {\n const endpoint = buildEndpoint(this._basePath, path, {\n optionalPathTemplateRegExp: this._optionalPathTemplateRegExp,\n pathTemplateCallback: this._pathTemplateCallback,\n pathTemplateData,\n pathTemplateRegExp: this._pathTemplateRegExp,\n queryParams: { ...this._queryParams, ...queryParams },\n });\n\n return this._fetch(endpoint, {\n body,\n headers: { ...this._headers, ...headers },\n method,\n ...rest,\n });\n }\n\n private _resolvePendingRequests(requestHash: string, responseData: FetchResponse) {\n const pendingRequests = this._requestTracker.pending.get(requestHash);\n if (!pendingRequests) return;\n\n pendingRequests.forEach(({ resolve }) => {\n resolve(responseData);\n });\n\n this._requestTracker.pending.delete(requestHash);\n }\n\n private _setPendingRequest(requestHash: string, resolver: PendingRequestResolvers) {\n let pending = this._requestTracker.pending.get(requestHash);\n if (!pending) pending = [];\n pending.push(resolver);\n this._requestTracker.pending.set(requestHash, pending);\n }\n\n private _trackRequest(requestHash: string): Promise<FetchResponse> | void {\n if (this._requestTracker.active.includes(requestHash)) {\n return new Promise((resolve: PendingRequestResolver) => {\n this._setPendingRequest(requestHash, { resolve });\n });\n }\n\n this._requestTracker.active.push(requestHash);\n }\n}\n\nexport default function createRestClient<N extends string>(options: ConstructorOptions, shortcuts?: Shortcuts) {\n const getta = new Getta(options) as Getta & ShortcutProperties<N>;\n if (!shortcuts) return getta;\n\n Object.keys(shortcuts).forEach(key => {\n getta.createShortcut(key, ...shortcuts[key]);\n });\n\n return getta;\n}\n","import {\n CLIENT_ERROR_REPSONSE,\n INFORMATION_REPSONSE,\n REDIRECTION_REPSONSE,\n SERVER_ERROR_REPSONSE,\n SUCCESSFUL_REPSONSE,\n} from \"../../constants\";\n\nexport default function getResponseGroup(status: number) {\n switch (true) {\n case status < 200:\n return INFORMATION_REPSONSE;\n case status < 300:\n return SUCCESSFUL_REPSONSE;\n case status < 400:\n return REDIRECTION_REPSONSE;\n case status < 500:\n return CLIENT_ERROR_REPSONSE;\n default:\n return SERVER_ERROR_REPSONSE;\n }\n}\n","export default function delay(ms: number) {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n","import Cacheability from \"cacheability\";\n\nexport default function isCacheabilityValid(cacheability: Cacheability) {\n const noCache = cacheability?.metadata?.cacheControl?.noCache ?? false;\n return !noCache && cacheability.checkTTL();\n}\n"],"names":["ARRAY_BUFFER_FORMAT","BLOB_FORMAT","FORM_DATA_FORMAT","JSON_FORMAT","TEXT_FORMAT","STREAM_READERS","DEFAULT_BODY_PARSER","body","DEFAULT_FETCH_TIMEOUT","DEFAULT_HEADERS","DEFAULT_MAX_REDIRECTS","DEFAULT_MAX_RETRIES","DEFAULT_PATH_TEMPLATE_REGEX","OPTIONAL_PATH_TEMPLATE_REGEX","DEFAULT_REQUEST_RETRY_WAIT","MISSING_BASE_PATH_ERROR","MAX_REDIRECTS_EXCEEDED_ERROR","MAX_RETRIES_EXCEEDED_ERROR","INVALID_FETCH_METHOD_ERROR","RESOURCE_NOT_FOUND_ERROR","FETCH_TIMEOUT_ERROR","GET_METHOD","POST_METHOD","PUT_METHOD","DELETE_METHOD","FETCH_METHODS","INFORMATION_REPSONSE","SUCCESSFUL_REPSONSE","REDIRECTION_REPSONSE","CLIENT_ERROR_REPSONSE","SERVER_ERROR_REPSONSE","NOT_MODIFIED_STATUS_CODE","NOT_FOUND_STATUS_CODE","COOKIE_HEADER","ETAG_HEADER","LOCATION_HEADER","IF_NONE_MATCH_HEADER","CACHE_CONTROL_HEADER","defaultPathTemplateCallback","pathTemplate","data","pathTemplateRegExp","dataKeys","Object","keys","replace","match","reduce","value","key","includes","buildEndpoint","basePath","path","optionalPathTemplateRegExp","pathTemplateCallback","pathTemplateData","queryParams","pathJoiner","endsWith","startsWith","endpoint","substring","length","queryString","extract","stringify","Getta","constructor","options","active","pending","Map","bodyParser","cache","enableConditionalRequests","fetchTimeout","headers","maxRedirects","maxRetries","requestRetryWait","streamReader","Error","_basePath","_bodyParser","_cache","_conditionalRequestsEnabled","_fetchTimeout","_headers","_maxRedirects","_maxRetries","_optionalPathTemplateRegExp","_pathTemplateCallback","_pathTemplateRegExp","_queryParams","_requestRetryWait","_streamReader","this","createShortcut","name","method","rest","async","requestMethod","requestRest","_merge","_delete","_get","_request","requestHash","delete","errors","Promise","reject","get","has","error","cacheHeaders","set","md5","_cacheEntryHas","_cacheEntryDelete","_fetch","redirects","retries","resolve","fetchTimer","setTimeout","res","fetch","clearTimeout","status","responseGroup","getResponseGroup","_fetchRedirectHandler","_fetchRetryHandler","fetchRes","resClone","clone","undefined","e","fetchResClone","text","JSON","parse","_castArray","redirectMethod","ms","cacheability","metadata","_cacheability$metadat2","cacheControl","_cacheability$metadat3","noCache","checkTTL","isCacheabilityValid","_cacheEntryGet","Headers","printCacheControl","etag","pendingRequest","_trackRequest","_getResolve","push","cachedData","_cacheEntrySet","_resolvePendingRequests","_requestTracker","filter","responseData","pendingRequests","forEach","_setPendingRequest","resolver","shortcuts","getta"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../getta/src/constants.ts","../getta/src/helpers/default-path-template-callback/index.ts","../getta/src/helpers/build-endpoint/index.ts","../getta/src/main.ts","../getta/src/helpers/get-response-group/index.ts","../getta/src/helpers/delay/index.ts","../getta/src/helpers/is-cacheability-valid/index.ts"],"sourcesContent":["import { PlainObject } from \"@repodog/types\";\n\nexport const ARRAY_BUFFER_FORMAT = \"arrayBuffer\" as const;\nexport const BLOB_FORMAT = \"blob\" as const;\nexport const FORM_DATA_FORMAT = \"formData\" as const;\nexport const JSON_FORMAT = \"json\" as const;\nexport const TEXT_FORMAT = \"text\" as const;\n\nexport const STREAM_READERS = {\n ARRAY_BUFFER_FORMAT,\n BLOB_FORMAT,\n FORM_DATA_FORMAT,\n JSON_FORMAT,\n TEXT_FORMAT,\n};\n\nexport const DEFAULT_BODY_PARSER = (body: PlainObject) => body;\nexport const DEFAULT_FETCH_TIMEOUT = 5000 as const;\nexport const DEFAULT_HEADERS = { \"content-type\": \"application/json\" };\nexport const DEFAULT_MAX_REDIRECTS = 5 as const;\nexport const DEFAULT_MAX_RETRIES = 3 as const;\nexport const DEFAULT_PATH_TEMPLATE_REGEX = /({type})|({id})|({id,\\+})|({brief\\|standard})/g;\nexport const OPTIONAL_PATH_TEMPLATE_REGEX = /({[a-zA-Z0-9_]+\\?})/g;\nexport const DEFAULT_RATE_LIMIT = 50;\nexport const DEFAULT_REQUEST_RETRY_WAIT = 100;\n\nexport const MISSING_BASE_PATH_ERROR = `Getta expected to receive 'basePath' in the constructor options,\n but recevied undefined.`;\n\nexport const MAX_REDIRECTS_EXCEEDED_ERROR = \"The request exceeded the maximum number of redirects, which is\";\n\nexport const MAX_RETRIES_EXCEEDED_ERROR = \"The request exceeded the maximum number of retries, which is\";\n\nexport const INVALID_FETCH_METHOD_ERROR = \"Getta expected to receive 'get', 'post', 'put' or 'delete', but received\";\n\nexport const RESOURCE_NOT_FOUND_ERROR = \"The requested resource could not been found.\";\n\nexport const FETCH_TIMEOUT_ERROR = \"The request timed out. Getta did not get a response within\";\n\nexport const GET_METHOD = \"get\" as const;\nexport const POST_METHOD = \"post\" as const;\nexport const PUT_METHOD = \"put\" as const;\nexport const DELETE_METHOD = \"delete\" as const;\n\nexport const FETCH_METHODS = [GET_METHOD, POST_METHOD, PUT_METHOD, DELETE_METHOD];\n\nexport const INFORMATION_REPSONSE = \"information\" as const;\nexport const SUCCESSFUL_REPSONSE = \"successful\" as const;\nexport const REDIRECTION_REPSONSE = \"redirection\" as const;\nexport const CLIENT_ERROR_REPSONSE = \"clientError\" as const;\nexport const SERVER_ERROR_REPSONSE = \"serverError\" as const;\n\nexport const NOT_MODIFIED_STATUS_CODE = 304 as const;\nexport const NOT_FOUND_STATUS_CODE = 404 as const;\n\nexport const COOKIE_HEADER = \"Cookie\" as const;\nexport const ETAG_HEADER = \"ETag\" as const;\nexport const LOCATION_HEADER = \"Location\" as const;\nexport const IF_NONE_MATCH_HEADER = \"If-None-Match\" as const;\nexport const CACHE_CONTROL_HEADER = \"Cache-Control\" as const;\n","import { StringObject } from \"@repodog/types\";\n\nexport default function defaultPathTemplateCallback(\n pathTemplate: string,\n data: StringObject,\n pathTemplateRegExp: RegExp,\n) {\n const dataKeys = Object.keys(data);\n\n return pathTemplate.replace(pathTemplateRegExp, match => {\n return dataKeys.reduce((value, key) => {\n if (match.includes(key)) return data[key];\n return value;\n }, \"\");\n });\n}\n","import queryString from \"query-string\";\nimport { BuildEndpointOptions } from \"./types\";\n\nexport default function buildEndpoint(\n basePath: string,\n path: string,\n {\n optionalPathTemplateRegExp,\n pathTemplateCallback,\n pathTemplateData,\n pathTemplateRegExp,\n queryParams,\n }: BuildEndpointOptions,\n) {\n const pathJoiner = basePath.endsWith(\"/\") || path.startsWith(\"/\") ? \"\" : \"/\";\n let endpoint = `${basePath}${pathJoiner}${path}`;\n\n if (pathTemplateData) {\n endpoint = pathTemplateCallback(endpoint, pathTemplateData, pathTemplateRegExp);\n }\n\n endpoint = endpoint.replace(optionalPathTemplateRegExp, \"\");\n\n if (endpoint.endsWith(\"/\")) {\n endpoint = endpoint.substring(0, endpoint.length - 1);\n }\n\n if (queryParams && Object.keys(queryParams).length) {\n const queryJoin = queryString.extract(endpoint) ? \"&\" : \"?\";\n endpoint = `${endpoint}${queryJoin}${queryString.stringify(queryParams)}`;\n }\n\n return endpoint;\n}\n","import Cachemap, { CacheHeaders } from \"@cachemap/core\";\nimport { Func, PlainObject, StringObject } from \"@repodog/types\";\nimport Cacheability from \"cacheability\";\nimport { castArray, merge } from \"lodash\";\nimport md5 from \"md5\";\nimport { Required } from \"utility-types\";\nimport {\n CACHE_CONTROL_HEADER,\n DEFAULT_BODY_PARSER,\n DEFAULT_FETCH_TIMEOUT,\n DEFAULT_HEADERS,\n DEFAULT_MAX_REDIRECTS,\n DEFAULT_MAX_RETRIES,\n DEFAULT_PATH_TEMPLATE_REGEX,\n DEFAULT_RATE_LIMIT,\n DEFAULT_REQUEST_RETRY_WAIT,\n DELETE_METHOD,\n ETAG_HEADER,\n FETCH_METHODS,\n FETCH_TIMEOUT_ERROR,\n GET_METHOD,\n IF_NONE_MATCH_HEADER,\n INVALID_FETCH_METHOD_ERROR,\n JSON_FORMAT,\n LOCATION_HEADER,\n MAX_REDIRECTS_EXCEEDED_ERROR,\n MAX_RETRIES_EXCEEDED_ERROR,\n MISSING_BASE_PATH_ERROR,\n NOT_FOUND_STATUS_CODE,\n NOT_MODIFIED_STATUS_CODE,\n OPTIONAL_PATH_TEMPLATE_REGEX,\n POST_METHOD,\n PUT_METHOD,\n REDIRECTION_REPSONSE,\n RESOURCE_NOT_FOUND_ERROR,\n SERVER_ERROR_REPSONSE,\n} from \"./constants\";\nimport buildEndpoint from \"./helpers/build-endpoint\";\nimport defaultPathTemplateCallback from \"./helpers/default-path-template-callback\";\nimport delay from \"./helpers/delay\";\nimport getResponseGroup from \"./helpers/get-response-group\";\nimport isCacheabilityValid from \"./helpers/is-cacheability-valid\";\nimport {\n ConstructorOptions,\n FetchOptions,\n FetchRedirectHandlerOptions,\n FetchResponse,\n PathTemplateCallback,\n PendingRequestResolver,\n PendingRequestResolvers,\n RequestOptions,\n RequestQueue,\n RequestTracker,\n ShortcutProperties,\n Shortcuts,\n StreamReader,\n} from \"./types\";\n\nexport class Getta {\n private _basePath: string;\n private _bodyParser: Func;\n private _cache?: Cachemap;\n private _conditionalRequestsEnabled: boolean;\n private _fetchTimeout: number;\n private _headers: StringObject;\n private _maxRedirects: number;\n private _maxRetries: number;\n private _optionalPathTemplateRegExp: RegExp;\n private _pathTemplateCallback: PathTemplateCallback;\n private _pathTemplateRegExp: RegExp;\n private _queryParams: PlainObject;\n private _rateLimitCount: number = 0;\n private _rateLimitedRequestQueue: RequestQueue = [];\n private _rateLimitPerSecond: number;\n private _rateLimitTimer: NodeJS.Timer | null = null;\n private _requestRetryWait: number;\n private _requestTracker: RequestTracker = { active: [], pending: new Map() };\n private _streamReader: StreamReader;\n\n constructor(options: ConstructorOptions) {\n const {\n basePath,\n bodyParser = DEFAULT_BODY_PARSER,\n cache,\n enableConditionalRequests = true,\n fetchTimeout = DEFAULT_FETCH_TIMEOUT,\n headers,\n maxRedirects = DEFAULT_MAX_REDIRECTS,\n maxRetries = DEFAULT_MAX_RETRIES,\n optionalPathTemplateRegExp = OPTIONAL_PATH_TEMPLATE_REGEX,\n pathTemplateCallback = defaultPathTemplateCallback,\n pathTemplateRegExp = DEFAULT_PATH_TEMPLATE_REGEX,\n queryParams = {},\n rateLimitPerSecond = DEFAULT_RATE_LIMIT,\n requestRetryWait = DEFAULT_REQUEST_RETRY_WAIT,\n streamReader = JSON_FORMAT,\n } = options;\n\n if (!basePath) {\n throw new Error(MISSING_BASE_PATH_ERROR);\n }\n\n this._basePath = basePath;\n this._bodyParser = bodyParser;\n this._cache = cache;\n this._conditionalRequestsEnabled = enableConditionalRequests;\n this._fetchTimeout = fetchTimeout;\n this._headers = { ...DEFAULT_HEADERS, ...(headers || {}) };\n this._maxRedirects = maxRedirects;\n this._maxRetries = maxRetries;\n this._optionalPathTemplateRegExp = optionalPathTemplateRegExp;\n this._pathTemplateCallback = pathTemplateCallback;\n this._pathTemplateRegExp = pathTemplateRegExp;\n this._queryParams = queryParams;\n this._rateLimitPerSecond = rateLimitPerSecond;\n this._requestRetryWait = requestRetryWait;\n this._streamReader = streamReader;\n }\n\n get cache(): Cachemap | undefined {\n return this._cache;\n }\n\n public createShortcut(name: string, path: string, { method, ...rest }: Required<RequestOptions, \"method\">) {\n if (!FETCH_METHODS.includes(method)) {\n throw new Error(`${INVALID_FETCH_METHOD_ERROR} ${method}`);\n }\n\n // @ts-ignore\n this[name] = async <Resource extends PlainObject>({ method: requestMethod, ...requestRest }: RequestOptions = {}) =>\n // @ts-ignore\n this[requestMethod ?? method](path, merge({}, rest, requestRest)) as Promise<FetchResponse<Resource>>;\n }\n\n public async delete(path: string, options: Omit<RequestOptions, \"method\"> = {}) {\n return this._delete(path, options);\n }\n\n public async get(path: string, options: Omit<RequestOptions, \"method\"> = {}) {\n return this._get(path, options);\n }\n\n public async post(path: string, options: Omit<Required<RequestOptions, \"body\">, \"method\">) {\n return this._request(path, { ...options, method: POST_METHOD });\n }\n\n public async put(path: string, options: Omit<Required<RequestOptions, \"body\">, \"methood\">) {\n return this._request(path, { ...options, method: PUT_METHOD });\n }\n\n private _addRequestToRateLimitedQueue(endpoint: string, options: FetchOptions) {\n return new Promise((resolve: (value: FetchResponse) => void) => {\n this._rateLimitedRequestQueue.push([resolve, endpoint, options]);\n });\n }\n\n private async _cacheEntryDelete(requestHash: string): Promise<boolean> {\n if (!this._cache) return false;\n\n try {\n return await this._cache.delete(requestHash);\n } catch (errors) {\n return Promise.reject(errors);\n }\n }\n\n private async _cacheEntryGet(requestHash: string): Promise<PlainObject | undefined> {\n if (!this._cache) return undefined;\n\n try {\n return await this._cache.get(requestHash);\n } catch (errors) {\n return Promise.reject(errors);\n }\n }\n\n private async _cacheEntryHas(requestHash: string): Promise<Cacheability | false> {\n if (!this._cache) return false;\n\n try {\n return await this._cache.has(requestHash);\n } catch (error) {\n return false;\n }\n }\n\n private async _cacheEntrySet(requestHash: string, data: PlainObject, cacheHeaders: CacheHeaders): Promise<void> {\n if (!this._cache) return undefined;\n\n try {\n return await this._cache.set(requestHash, data, { cacheHeaders });\n } catch (error) {\n return Promise.reject(error);\n }\n }\n\n private async _delete(\n path: string,\n { headers = {}, pathTemplateData, queryParams = {}, ...rest }: Omit<RequestOptions, \"method\">,\n ) {\n const endpoint = buildEndpoint(this._basePath, path, {\n optionalPathTemplateRegExp: this._optionalPathTemplateRegExp,\n pathTemplateCallback: this._pathTemplateCallback,\n pathTemplateData,\n pathTemplateRegExp: this._pathTemplateRegExp,\n queryParams: { ...this._queryParams, ...queryParams },\n });\n\n const requestHash = md5(endpoint);\n const cacheability = await this._cacheEntryHas(requestHash);\n\n if (cacheability) {\n this._cacheEntryDelete(requestHash);\n }\n\n return this._fetch(endpoint, {\n headers: { ...this._headers, ...headers },\n method: DELETE_METHOD,\n ...rest,\n });\n }\n\n private async _fetch(endpoint: string, { redirects, retries, ...rest }: FetchOptions): Promise<FetchResponse> {\n try {\n return new Promise(async (resolve: (value: FetchResponse) => void, reject) => {\n const fetchTimer = setTimeout(() => {\n reject(new Error(`${FETCH_TIMEOUT_ERROR} ${this._fetchTimeout}ms.`));\n }, this._fetchTimeout);\n\n this._rateLimit();\n\n if (!(this._rateLimitCount < this._rateLimitPerSecond)) {\n resolve(await this._addRequestToRateLimitedQueue(endpoint, { redirects, retries, ...rest }));\n return;\n }\n\n const res = await fetch(endpoint, rest);\n\n clearTimeout(fetchTimer);\n\n const { headers, status } = res;\n const responseGroup = getResponseGroup(status);\n\n if (responseGroup === REDIRECTION_REPSONSE && headers.get(LOCATION_HEADER)) {\n resolve(\n await this._fetchRedirectHandler(headers.get(LOCATION_HEADER) as string, {\n redirects,\n status,\n ...rest,\n }),\n );\n\n return;\n }\n\n if (responseGroup === SERVER_ERROR_REPSONSE) {\n resolve(\n (await this._fetchRetryHandler(endpoint, {\n retries,\n ...rest,\n })) as FetchResponse,\n );\n\n return;\n }\n\n const fetchRes = res as FetchResponse;\n\n try {\n fetchRes.data = res.body ? this._bodyParser(await res[this._streamReader]()) : undefined;\n resolve(fetchRes);\n } catch (e) {\n reject([e, new Error(`Unable to ${rest.method} ${endpoint} due to previous error`)]);\n }\n });\n } catch (error) {\n const errorRes = { errors: castArray(error) };\n return errorRes as FetchResponse;\n }\n }\n\n private async _fetchRedirectHandler(\n endpoint: string,\n { method, redirects = 1, status, ...rest }: FetchRedirectHandlerOptions,\n ): Promise<FetchResponse> {\n if (redirects === this._maxRedirects) {\n const errorRes = {\n errors: [new Error(`${MAX_REDIRECTS_EXCEEDED_ERROR} ${this._maxRedirects}.`)],\n };\n\n return errorRes as FetchResponse;\n }\n\n redirects += 1;\n const redirectMethod = status === 303 ? GET_METHOD : method;\n return this._fetch(endpoint, { method: redirectMethod, redirects, ...rest });\n }\n\n private async _fetchRetryHandler(endpoint: string, { retries = 1, ...rest }: FetchOptions) {\n if (retries === this._maxRetries) {\n return {\n errors: [new Error(`${MAX_RETRIES_EXCEEDED_ERROR} ${this._maxRetries}.`)],\n };\n }\n\n retries += 1;\n await delay(this._requestRetryWait);\n return this._fetch(endpoint, { retries, ...rest });\n }\n\n private async _get(\n path: string,\n { headers = {}, pathTemplateData, queryParams = {} }: Omit<RequestOptions, \"method\">,\n ) {\n const endpoint = buildEndpoint(this._basePath, path, {\n optionalPathTemplateRegExp: this._optionalPathTemplateRegExp,\n pathTemplateCallback: this._pathTemplateCallback,\n pathTemplateData,\n pathTemplateRegExp: this._pathTemplateRegExp,\n queryParams: { ...this._queryParams, ...queryParams },\n });\n\n const requestHash = md5(endpoint);\n const cacheability = await this._cacheEntryHas(requestHash);\n\n if (cacheability) {\n if (isCacheabilityValid(cacheability)) {\n return {\n data: await this._cacheEntryGet(requestHash),\n headers: new Headers({ \"cache-control\": cacheability.printCacheControl() }),\n };\n }\n\n if (this._conditionalRequestsEnabled) {\n const etag = cacheability?.metadata?.etag ?? null;\n if (etag) headers[IF_NONE_MATCH_HEADER] = etag;\n }\n }\n\n const pendingRequest = this._trackRequest(requestHash);\n if (pendingRequest) return pendingRequest;\n\n return this._getResolve(\n requestHash,\n await this._fetch(endpoint, { headers: { ...this._headers, ...headers }, method: GET_METHOD }),\n );\n }\n\n private async _getResolve(requestHash: string, res: FetchResponse) {\n const { data, headers, status } = res;\n\n if (status === NOT_FOUND_STATUS_CODE) {\n this._cacheEntryDelete(requestHash);\n\n if (!res.errors) {\n res.errors = [];\n }\n\n res.errors.push(new Error(RESOURCE_NOT_FOUND_ERROR));\n } else if (status === NOT_MODIFIED_STATUS_CODE && headers) {\n const cachedData = await this._cacheEntryGet(requestHash);\n\n if (cachedData) {\n this._cacheEntrySet(requestHash, cachedData, {\n cacheControl: headers.get(CACHE_CONTROL_HEADER) || undefined,\n etag: headers.get(ETAG_HEADER) || undefined,\n });\n\n res.data = cachedData;\n }\n } else if (data && headers) {\n this._cacheEntrySet(requestHash, data, {\n cacheControl: headers.get(CACHE_CONTROL_HEADER) || undefined,\n etag: headers.get(ETAG_HEADER) || undefined,\n });\n }\n\n this._resolvePendingRequests(requestHash, res);\n this._requestTracker.active = this._requestTracker.active.filter(value => value !== requestHash);\n return res;\n }\n\n private _rateLimit() {\n if (!this._rateLimitTimer) {\n this._rateLimitTimer = setTimeout(() => {\n this._rateLimitTimer = null;\n this._rateLimitCount = 0;\n\n if (this._rateLimitedRequestQueue.length) {\n this._releaseRateLimitedRequestQueue();\n }\n }, 1000);\n }\n\n this._rateLimitCount += 1;\n }\n\n private _releaseRateLimitedRequestQueue() {\n this._rateLimitedRequestQueue.forEach(async ([resolve, endpoint, options]) => {\n // @ts-ignore\n resolve(await this._fetch(endpoint, options));\n });\n\n this._rateLimitedRequestQueue = [];\n }\n\n private async _request(\n path: string,\n { body, headers, method, pathTemplateData, queryParams, ...rest }: Required<RequestOptions, \"method\">,\n ) {\n const endpoint = buildEndpoint(this._basePath, path, {\n optionalPathTemplateRegExp: this._optionalPathTemplateRegExp,\n pathTemplateCallback: this._pathTemplateCallback,\n pathTemplateData,\n pathTemplateRegExp: this._pathTemplateRegExp,\n queryParams: { ...this._queryParams, ...queryParams },\n });\n\n return this._fetch(endpoint, {\n body,\n headers: { ...this._headers, ...headers },\n method,\n ...rest,\n });\n }\n\n private _resolvePendingRequests(requestHash: string, responseData: FetchResponse) {\n const pendingRequests = this._requestTracker.pending.get(requestHash);\n if (!pendingRequests) return;\n\n pendingRequests.forEach(({ resolve }) => {\n resolve(responseData);\n });\n\n this._requestTracker.pending.delete(requestHash);\n }\n\n private _setPendingRequest(requestHash: string, resolver: PendingRequestResolvers) {\n let pending = this._requestTracker.pending.get(requestHash);\n if (!pending) pending = [];\n pending.push(resolver);\n this._requestTracker.pending.set(requestHash, pending);\n }\n\n private _trackRequest(requestHash: string): Promise<FetchResponse> | void {\n if (this._requestTracker.active.includes(requestHash)) {\n return new Promise((resolve: PendingRequestResolver) => {\n this._setPendingRequest(requestHash, { resolve });\n });\n }\n\n this._requestTracker.active.push(requestHash);\n }\n}\n\nexport default function createRestClient<N extends string>(options: ConstructorOptions, shortcuts?: Shortcuts) {\n const getta = new Getta(options) as Getta & ShortcutProperties<N>;\n if (!shortcuts) return getta;\n\n Object.keys(shortcuts).forEach(key => {\n getta.createShortcut(key, ...shortcuts[key]);\n });\n\n return getta;\n}\n","import {\n CLIENT_ERROR_REPSONSE,\n INFORMATION_REPSONSE,\n REDIRECTION_REPSONSE,\n SERVER_ERROR_REPSONSE,\n SUCCESSFUL_REPSONSE,\n} from \"../../constants\";\n\nexport default function getResponseGroup(status: number) {\n switch (true) {\n case status < 200:\n return INFORMATION_REPSONSE;\n case status < 300:\n return SUCCESSFUL_REPSONSE;\n case status < 400:\n return REDIRECTION_REPSONSE;\n case status < 500:\n return CLIENT_ERROR_REPSONSE;\n default:\n return SERVER_ERROR_REPSONSE;\n }\n}\n","export default function delay(ms: number) {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n","import Cacheability from \"cacheability\";\n\nexport default function isCacheabilityValid(cacheability: Cacheability) {\n const noCache = cacheability?.metadata?.cacheControl?.noCache ?? false;\n return !noCache && cacheability.checkTTL();\n}\n"],"names":["ARRAY_BUFFER_FORMAT","BLOB_FORMAT","FORM_DATA_FORMAT","JSON_FORMAT","TEXT_FORMAT","STREAM_READERS","DEFAULT_BODY_PARSER","body","DEFAULT_FETCH_TIMEOUT","DEFAULT_HEADERS","DEFAULT_MAX_REDIRECTS","DEFAULT_MAX_RETRIES","DEFAULT_PATH_TEMPLATE_REGEX","OPTIONAL_PATH_TEMPLATE_REGEX","DEFAULT_RATE_LIMIT","DEFAULT_REQUEST_RETRY_WAIT","MISSING_BASE_PATH_ERROR","MAX_REDIRECTS_EXCEEDED_ERROR","MAX_RETRIES_EXCEEDED_ERROR","INVALID_FETCH_METHOD_ERROR","RESOURCE_NOT_FOUND_ERROR","FETCH_TIMEOUT_ERROR","GET_METHOD","POST_METHOD","PUT_METHOD","DELETE_METHOD","FETCH_METHODS","INFORMATION_REPSONSE","SUCCESSFUL_REPSONSE","REDIRECTION_REPSONSE","CLIENT_ERROR_REPSONSE","SERVER_ERROR_REPSONSE","NOT_MODIFIED_STATUS_CODE","NOT_FOUND_STATUS_CODE","COOKIE_HEADER","ETAG_HEADER","LOCATION_HEADER","IF_NONE_MATCH_HEADER","CACHE_CONTROL_HEADER","defaultPathTemplateCallback","pathTemplate","data","pathTemplateRegExp","dataKeys","Object","keys","replace","match","reduce","value","key","includes","buildEndpoint","basePath","path","optionalPathTemplateRegExp","pathTemplateCallback","pathTemplateData","queryParams","pathJoiner","endsWith","startsWith","endpoint","substring","length","queryString","extract","stringify","Getta","constructor","options","active","pending","Map","bodyParser","cache","enableConditionalRequests","fetchTimeout","headers","maxRedirects","maxRetries","rateLimitPerSecond","requestRetryWait","streamReader","Error","_basePath","_bodyParser","_cache","_conditionalRequestsEnabled","_fetchTimeout","_headers","_maxRedirects","_maxRetries","_optionalPathTemplateRegExp","_pathTemplateCallback","_pathTemplateRegExp","_queryParams","_rateLimitPerSecond","_requestRetryWait","_streamReader","this","createShortcut","name","method","rest","async","requestMethod","requestRest","_merge","_delete","_get","_request","_addRequestToRateLimitedQueue","Promise","resolve","_rateLimitedRequestQueue","push","requestHash","delete","errors","reject","get","has","error","cacheHeaders","set","md5","_cacheEntryHas","_cacheEntryDelete","_fetch","redirects","retries","fetchTimer","setTimeout","_rateLimit","_rateLimitCount","res","fetch","clearTimeout","status","responseGroup","getResponseGroup","_fetchRedirectHandler","_fetchRetryHandler","fetchRes","undefined","e","_castArray","redirectMethod","ms","cacheability","metadata","_cacheability$metadat2","cacheControl","_cacheability$metadat3","noCache","checkTTL","isCacheabilityValid","_cacheEntryGet","Headers","printCacheControl","etag","pendingRequest","_trackRequest","_getResolve","cachedData","_cacheEntrySet","_resolvePendingRequests","_requestTracker","filter","_rateLimitTimer","_releaseRateLimitedRequestQueue","forEach","responseData","pendingRequests","_setPendingRequest","resolver","shortcuts","getta"],"mappings":"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"}
@@ -1,26 +1,26 @@
1
1
  -----------------------------
2
2
  Rollup File Analysis
3
3
  -----------------------------
4
- bundle size: 15.825 KB
5
- original size: 20.645 KB
6
- code reduction: 23.35 %
4
+ bundle size: 16.759 KB
5
+ original size: 21.745 KB
6
+ code reduction: 22.93 %
7
7
  module count: 9
8
8
 
9
9
  █████████████████████████████████████░░░░░░░░░░░░░
10
10
  file: /src/main.ts
11
- bundle space: 74.18 %
12
- rendered size: 11.739 KB
13
- original size: 13.724 KB
14
- code reduction: 14.46 %
11
+ bundle space: 75.43 %
12
+ rendered size: 12.642 KB
13
+ original size: 14.669 KB
14
+ code reduction: 13.82 %
15
15
  dependents: 1
16
16
  - /src/index.ts
17
17
 
18
- ██████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
18
+ █████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
19
19
  file: /src/constants.ts
20
- bundle space: 12.33 %
21
- rendered size: 1.952 KB
22
- original size: 2.502 KB
23
- code reduction: 21.98 %
20
+ bundle space: 11.83 %
21
+ rendered size: 1.983 KB
22
+ original size: 2.54 KB
23
+ code reduction: 21.93 %
24
24
  dependents: 3
25
25
  - /src/helpers/get-response-group/index.ts
26
26
  - /src/main.ts
@@ -28,7 +28,7 @@ dependents: 3
28
28
 
29
29
  ██░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
30
30
  file: /src/helpers/build-endpoint/index.ts
31
- bundle space: 4.99 %
31
+ bundle space: 4.71 %
32
32
  rendered size: 790 Bytes
33
33
  original size: 955 Bytes
34
34
  code reduction: 17.28 %
@@ -37,7 +37,7 @@ dependents: 1
37
37
 
38
38
  █░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
39
39
  file: /src/helpers/is-cacheability-valid/index.ts
40
- bundle space: 3.85 %
40
+ bundle space: 3.63 %
41
41
  rendered size: 609 Bytes
42
42
  original size: 238 Bytes
43
43
  code reduction: 0 %
@@ -46,7 +46,7 @@ dependents: 1
46
46
 
47
47
  █░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
48
48
  file: /src/helpers/get-response-group/index.ts
49
- bundle space: 2.17 %
49
+ bundle space: 2.05 %
50
50
  rendered size: 344 Bytes
51
51
  original size: 521 Bytes
52
52
  code reduction: 33.97 %
@@ -55,7 +55,7 @@ dependents: 1
55
55
 
56
56
  ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
57
57
  file: /src/helpers/default-path-template-callback/index.ts
58
- bundle space: 1.97 %
58
+ bundle space: 1.86 %
59
59
  rendered size: 311 Bytes
60
60
  original size: 415 Bytes
61
61
  code reduction: 25.06 %
@@ -65,7 +65,7 @@ dependents: 2
65
65
 
66
66
  ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
67
67
  file: /src/helpers/delay/index.ts
68
- bundle space: 0.51 %
68
+ bundle space: 0.48 %
69
69
  rendered size: 80 Bytes
70
70
  original size: 104 Bytes
71
71
  code reduction: 23.08 %
@@ -76,7 +76,7 @@ dependents: 1
76
76
  file: /src/types.ts
77
77
  bundle space: 0 %
78
78
  rendered size: 0 Byte
79
- original size: 1.992 KB
79
+ original size: 2.109 KB
80
80
  code reduction: 100 %
81
81
  dependents: 1
82
82
  - /src/index.ts
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.CACHE_CONTROL_HEADER = exports.IF_NONE_MATCH_HEADER = exports.LOCATION_HEADER = exports.ETAG_HEADER = exports.COOKIE_HEADER = exports.NOT_FOUND_STATUS_CODE = exports.NOT_MODIFIED_STATUS_CODE = exports.SERVER_ERROR_REPSONSE = exports.CLIENT_ERROR_REPSONSE = exports.REDIRECTION_REPSONSE = exports.SUCCESSFUL_REPSONSE = exports.INFORMATION_REPSONSE = exports.FETCH_METHODS = exports.DELETE_METHOD = exports.PUT_METHOD = exports.POST_METHOD = exports.GET_METHOD = exports.FETCH_TIMEOUT_ERROR = exports.RESOURCE_NOT_FOUND_ERROR = exports.INVALID_FETCH_METHOD_ERROR = exports.MAX_RETRIES_EXCEEDED_ERROR = exports.MAX_REDIRECTS_EXCEEDED_ERROR = exports.MISSING_BASE_PATH_ERROR = exports.DEFAULT_REQUEST_RETRY_WAIT = exports.OPTIONAL_PATH_TEMPLATE_REGEX = exports.DEFAULT_PATH_TEMPLATE_REGEX = exports.DEFAULT_MAX_RETRIES = exports.DEFAULT_MAX_REDIRECTS = exports.DEFAULT_HEADERS = exports.DEFAULT_FETCH_TIMEOUT = exports.DEFAULT_BODY_PARSER = exports.STREAM_READERS = exports.TEXT_FORMAT = exports.JSON_FORMAT = exports.FORM_DATA_FORMAT = exports.BLOB_FORMAT = exports.ARRAY_BUFFER_FORMAT = void 0;
6
+ exports.CACHE_CONTROL_HEADER = exports.IF_NONE_MATCH_HEADER = exports.LOCATION_HEADER = exports.ETAG_HEADER = exports.COOKIE_HEADER = exports.NOT_FOUND_STATUS_CODE = exports.NOT_MODIFIED_STATUS_CODE = exports.SERVER_ERROR_REPSONSE = exports.CLIENT_ERROR_REPSONSE = exports.REDIRECTION_REPSONSE = exports.SUCCESSFUL_REPSONSE = exports.INFORMATION_REPSONSE = exports.FETCH_METHODS = exports.DELETE_METHOD = exports.PUT_METHOD = exports.POST_METHOD = exports.GET_METHOD = exports.FETCH_TIMEOUT_ERROR = exports.RESOURCE_NOT_FOUND_ERROR = exports.INVALID_FETCH_METHOD_ERROR = exports.MAX_RETRIES_EXCEEDED_ERROR = exports.MAX_REDIRECTS_EXCEEDED_ERROR = exports.MISSING_BASE_PATH_ERROR = exports.DEFAULT_REQUEST_RETRY_WAIT = exports.DEFAULT_RATE_LIMIT = exports.OPTIONAL_PATH_TEMPLATE_REGEX = exports.DEFAULT_PATH_TEMPLATE_REGEX = exports.DEFAULT_MAX_RETRIES = exports.DEFAULT_MAX_REDIRECTS = exports.DEFAULT_HEADERS = exports.DEFAULT_FETCH_TIMEOUT = exports.DEFAULT_BODY_PARSER = exports.STREAM_READERS = exports.TEXT_FORMAT = exports.JSON_FORMAT = exports.FORM_DATA_FORMAT = exports.BLOB_FORMAT = exports.ARRAY_BUFFER_FORMAT = void 0;
7
7
  const ARRAY_BUFFER_FORMAT = "arrayBuffer";
8
8
  exports.ARRAY_BUFFER_FORMAT = ARRAY_BUFFER_FORMAT;
9
9
  const BLOB_FORMAT = "blob";
@@ -40,6 +40,8 @@ const DEFAULT_PATH_TEMPLATE_REGEX = /({type})|({id})|({id,\+})|({brief\|standard
40
40
  exports.DEFAULT_PATH_TEMPLATE_REGEX = DEFAULT_PATH_TEMPLATE_REGEX;
41
41
  const OPTIONAL_PATH_TEMPLATE_REGEX = /({[a-zA-Z0-9_]+\?})/g;
42
42
  exports.OPTIONAL_PATH_TEMPLATE_REGEX = OPTIONAL_PATH_TEMPLATE_REGEX;
43
+ const DEFAULT_RATE_LIMIT = 50;
44
+ exports.DEFAULT_RATE_LIMIT = DEFAULT_RATE_LIMIT;
43
45
  const DEFAULT_REQUEST_RETRY_WAIT = 100;
44
46
  exports.DEFAULT_REQUEST_RETRY_WAIT = DEFAULT_REQUEST_RETRY_WAIT;
45
47
  const MISSING_BASE_PATH_ERROR = `Getta expected to receive 'basePath' in the constructor options,
package/lib/main/main.js CHANGED
@@ -44,6 +44,10 @@ class Getta {
44
44
  (0, _defineProperty2.default)(this, "_pathTemplateCallback", void 0);
45
45
  (0, _defineProperty2.default)(this, "_pathTemplateRegExp", void 0);
46
46
  (0, _defineProperty2.default)(this, "_queryParams", void 0);
47
+ (0, _defineProperty2.default)(this, "_rateLimitCount", 0);
48
+ (0, _defineProperty2.default)(this, "_rateLimitedRequestQueue", []);
49
+ (0, _defineProperty2.default)(this, "_rateLimitPerSecond", void 0);
50
+ (0, _defineProperty2.default)(this, "_rateLimitTimer", null);
47
51
  (0, _defineProperty2.default)(this, "_requestRetryWait", void 0);
48
52
  (0, _defineProperty2.default)(this, "_requestTracker", {
49
53
  active: [],
@@ -63,6 +67,7 @@ class Getta {
63
67
  pathTemplateCallback = _defaultPathTemplateCallback.default,
64
68
  pathTemplateRegExp = _constants.DEFAULT_PATH_TEMPLATE_REGEX,
65
69
  queryParams = {},
70
+ rateLimitPerSecond = _constants.DEFAULT_RATE_LIMIT,
66
71
  requestRetryWait = _constants.DEFAULT_REQUEST_RETRY_WAIT,
67
72
  streamReader = _constants.JSON_FORMAT
68
73
  } = options;
@@ -85,6 +90,7 @@ class Getta {
85
90
  this._pathTemplateCallback = pathTemplateCallback;
86
91
  this._pathTemplateRegExp = pathTemplateRegExp;
87
92
  this._queryParams = queryParams;
93
+ this._rateLimitPerSecond = rateLimitPerSecond;
88
94
  this._requestRetryWait = requestRetryWait;
89
95
  this._streamReader = streamReader;
90
96
  }
@@ -127,6 +133,12 @@ class Getta {
127
133
  });
128
134
  }
129
135
 
136
+ _addRequestToRateLimitedQueue(endpoint, options) {
137
+ return new Promise(resolve => {
138
+ this._rateLimitedRequestQueue.push([resolve, endpoint, options]);
139
+ });
140
+ }
141
+
130
142
  async _cacheEntryDelete(requestHash) {
131
143
  if (!this._cache) return false;
132
144
 
@@ -210,6 +222,18 @@ class Getta {
210
222
  const fetchTimer = setTimeout(() => {
211
223
  reject(new Error(`${_constants.FETCH_TIMEOUT_ERROR} ${this._fetchTimeout}ms.`));
212
224
  }, this._fetchTimeout);
225
+
226
+ this._rateLimit();
227
+
228
+ if (!(this._rateLimitCount < this._rateLimitPerSecond)) {
229
+ resolve(await this._addRequestToRateLimitedQueue(endpoint, {
230
+ redirects,
231
+ retries,
232
+ ...rest
233
+ }));
234
+ return;
235
+ }
236
+
213
237
  const res = await fetch(endpoint, rest);
214
238
  clearTimeout(fetchTimer);
215
239
  const {
@@ -224,6 +248,7 @@ class Getta {
224
248
  status,
225
249
  ...rest
226
250
  }));
251
+ return;
227
252
  }
228
253
 
229
254
  if (responseGroup === _constants.SERVER_ERROR_REPSONSE) {
@@ -231,27 +256,16 @@ class Getta {
231
256
  retries,
232
257
  ...rest
233
258
  }));
259
+ return;
234
260
  }
235
261
 
236
262
  const fetchRes = res;
237
- const resClone = res.clone();
238
263
 
239
264
  try {
240
265
  fetchRes.data = res.body ? this._bodyParser(await res[this._streamReader]()) : undefined;
241
266
  resolve(fetchRes);
242
267
  } catch (e) {
243
- try {
244
- if (this._streamReader === "json" && res.body) {
245
- const fetchResClone = resClone;
246
- const text = await resClone.text();
247
- fetchResClone.data = JSON.parse(text);
248
- resolve(fetchResClone);
249
- } else {
250
- reject([e, new Error(`Unable to ${rest.method} ${endpoint} due to previous error`)]);
251
- }
252
- } catch {
253
- reject([e, new Error(`Unable to ${rest.method} ${endpoint} due to previous error`)]);
254
- }
268
+ reject([e, new Error(`Unable to ${rest.method} ${endpoint} due to previous error`)]);
255
269
  }
256
270
  });
257
271
  } catch (error) {
@@ -387,6 +401,29 @@ class Getta {
387
401
  return res;
388
402
  }
389
403
 
404
+ _rateLimit() {
405
+ if (!this._rateLimitTimer) {
406
+ this._rateLimitTimer = setTimeout(() => {
407
+ this._rateLimitTimer = null;
408
+ this._rateLimitCount = 0;
409
+
410
+ if (this._rateLimitedRequestQueue.length) {
411
+ this._releaseRateLimitedRequestQueue();
412
+ }
413
+ }, 1000);
414
+ }
415
+
416
+ this._rateLimitCount += 1;
417
+ }
418
+
419
+ _releaseRateLimitedRequestQueue() {
420
+ this._rateLimitedRequestQueue.forEach(async ([resolve, endpoint, options]) => {
421
+ resolve(await this._fetch(endpoint, options));
422
+ });
423
+
424
+ this._rateLimitedRequestQueue = [];
425
+ }
426
+
390
427
  async _request(path, {
391
428
  body,
392
429
  headers,
@@ -19,6 +19,7 @@ export const DEFAULT_MAX_REDIRECTS = 5;
19
19
  export const DEFAULT_MAX_RETRIES = 3;
20
20
  export const DEFAULT_PATH_TEMPLATE_REGEX = /({type})|({id})|({id,\+})|({brief\|standard})/g;
21
21
  export const OPTIONAL_PATH_TEMPLATE_REGEX = /({[a-zA-Z0-9_]+\?})/g;
22
+ export const DEFAULT_RATE_LIMIT = 50;
22
23
  export const DEFAULT_REQUEST_RETRY_WAIT = 100;
23
24
  export const MISSING_BASE_PATH_ERROR = `Getta expected to receive 'basePath' in the constructor options,
24
25
  but recevied undefined.`;
@@ -3,7 +3,7 @@ import _merge from "lodash/merge";
3
3
  import _castArray from "lodash/castArray";
4
4
  import "core-js/modules/es.promise.js";
5
5
  import md5 from "md5";
6
- import { CACHE_CONTROL_HEADER, DEFAULT_BODY_PARSER, DEFAULT_FETCH_TIMEOUT, DEFAULT_HEADERS, DEFAULT_MAX_REDIRECTS, DEFAULT_MAX_RETRIES, DEFAULT_PATH_TEMPLATE_REGEX, DEFAULT_REQUEST_RETRY_WAIT, DELETE_METHOD, ETAG_HEADER, FETCH_METHODS, FETCH_TIMEOUT_ERROR, GET_METHOD, IF_NONE_MATCH_HEADER, INVALID_FETCH_METHOD_ERROR, JSON_FORMAT, LOCATION_HEADER, MAX_REDIRECTS_EXCEEDED_ERROR, MAX_RETRIES_EXCEEDED_ERROR, MISSING_BASE_PATH_ERROR, NOT_FOUND_STATUS_CODE, NOT_MODIFIED_STATUS_CODE, OPTIONAL_PATH_TEMPLATE_REGEX, POST_METHOD, PUT_METHOD, REDIRECTION_REPSONSE, RESOURCE_NOT_FOUND_ERROR, SERVER_ERROR_REPSONSE } from "./constants";
6
+ import { CACHE_CONTROL_HEADER, DEFAULT_BODY_PARSER, DEFAULT_FETCH_TIMEOUT, DEFAULT_HEADERS, DEFAULT_MAX_REDIRECTS, DEFAULT_MAX_RETRIES, DEFAULT_PATH_TEMPLATE_REGEX, DEFAULT_RATE_LIMIT, DEFAULT_REQUEST_RETRY_WAIT, DELETE_METHOD, ETAG_HEADER, FETCH_METHODS, FETCH_TIMEOUT_ERROR, GET_METHOD, IF_NONE_MATCH_HEADER, INVALID_FETCH_METHOD_ERROR, JSON_FORMAT, LOCATION_HEADER, MAX_REDIRECTS_EXCEEDED_ERROR, MAX_RETRIES_EXCEEDED_ERROR, MISSING_BASE_PATH_ERROR, NOT_FOUND_STATUS_CODE, NOT_MODIFIED_STATUS_CODE, OPTIONAL_PATH_TEMPLATE_REGEX, POST_METHOD, PUT_METHOD, REDIRECTION_REPSONSE, RESOURCE_NOT_FOUND_ERROR, SERVER_ERROR_REPSONSE } from "./constants";
7
7
  import buildEndpoint from "./helpers/build-endpoint";
8
8
  import defaultPathTemplateCallback from "./helpers/default-path-template-callback";
9
9
  import delay from "./helpers/delay";
@@ -35,6 +35,14 @@ export class Getta {
35
35
 
36
36
  _defineProperty(this, "_queryParams", void 0);
37
37
 
38
+ _defineProperty(this, "_rateLimitCount", 0);
39
+
40
+ _defineProperty(this, "_rateLimitedRequestQueue", []);
41
+
42
+ _defineProperty(this, "_rateLimitPerSecond", void 0);
43
+
44
+ _defineProperty(this, "_rateLimitTimer", null);
45
+
38
46
  _defineProperty(this, "_requestRetryWait", void 0);
39
47
 
40
48
  _defineProperty(this, "_requestTracker", {
@@ -57,6 +65,7 @@ export class Getta {
57
65
  pathTemplateCallback = defaultPathTemplateCallback,
58
66
  pathTemplateRegExp = DEFAULT_PATH_TEMPLATE_REGEX,
59
67
  queryParams = {},
68
+ rateLimitPerSecond = DEFAULT_RATE_LIMIT,
60
69
  requestRetryWait = DEFAULT_REQUEST_RETRY_WAIT,
61
70
  streamReader = JSON_FORMAT
62
71
  } = options;
@@ -79,6 +88,7 @@ export class Getta {
79
88
  this._pathTemplateCallback = pathTemplateCallback;
80
89
  this._pathTemplateRegExp = pathTemplateRegExp;
81
90
  this._queryParams = queryParams;
91
+ this._rateLimitPerSecond = rateLimitPerSecond;
82
92
  this._requestRetryWait = requestRetryWait;
83
93
  this._streamReader = streamReader;
84
94
  }
@@ -121,6 +131,12 @@ export class Getta {
121
131
  });
122
132
  }
123
133
 
134
+ _addRequestToRateLimitedQueue(endpoint, options) {
135
+ return new Promise(resolve => {
136
+ this._rateLimitedRequestQueue.push([resolve, endpoint, options]);
137
+ });
138
+ }
139
+
124
140
  async _cacheEntryDelete(requestHash) {
125
141
  if (!this._cache) return false;
126
142
 
@@ -204,6 +220,18 @@ export class Getta {
204
220
  const fetchTimer = setTimeout(() => {
205
221
  reject(new Error(`${FETCH_TIMEOUT_ERROR} ${this._fetchTimeout}ms.`));
206
222
  }, this._fetchTimeout);
223
+
224
+ this._rateLimit();
225
+
226
+ if (!(this._rateLimitCount < this._rateLimitPerSecond)) {
227
+ resolve(await this._addRequestToRateLimitedQueue(endpoint, {
228
+ redirects,
229
+ retries,
230
+ ...rest
231
+ }));
232
+ return;
233
+ }
234
+
207
235
  const res = await fetch(endpoint, rest);
208
236
  clearTimeout(fetchTimer);
209
237
  const {
@@ -218,6 +246,7 @@ export class Getta {
218
246
  status,
219
247
  ...rest
220
248
  }));
249
+ return;
221
250
  }
222
251
 
223
252
  if (responseGroup === SERVER_ERROR_REPSONSE) {
@@ -225,27 +254,16 @@ export class Getta {
225
254
  retries,
226
255
  ...rest
227
256
  }));
257
+ return;
228
258
  }
229
259
 
230
260
  const fetchRes = res;
231
- const resClone = res.clone();
232
261
 
233
262
  try {
234
263
  fetchRes.data = res.body ? this._bodyParser(await res[this._streamReader]()) : undefined;
235
264
  resolve(fetchRes);
236
265
  } catch (e) {
237
- try {
238
- if (this._streamReader === "json" && res.body) {
239
- const fetchResClone = resClone;
240
- const text = await resClone.text();
241
- fetchResClone.data = JSON.parse(text);
242
- resolve(fetchResClone);
243
- } else {
244
- reject([e, new Error(`Unable to ${rest.method} ${endpoint} due to previous error`)]);
245
- }
246
- } catch {
247
- reject([e, new Error(`Unable to ${rest.method} ${endpoint} due to previous error`)]);
248
- }
266
+ reject([e, new Error(`Unable to ${rest.method} ${endpoint} due to previous error`)]);
249
267
  }
250
268
  });
251
269
  } catch (error) {
@@ -381,6 +399,29 @@ export class Getta {
381
399
  return res;
382
400
  }
383
401
 
402
+ _rateLimit() {
403
+ if (!this._rateLimitTimer) {
404
+ this._rateLimitTimer = setTimeout(() => {
405
+ this._rateLimitTimer = null;
406
+ this._rateLimitCount = 0;
407
+
408
+ if (this._rateLimitedRequestQueue.length) {
409
+ this._releaseRateLimitedRequestQueue();
410
+ }
411
+ }, 1000);
412
+ }
413
+
414
+ this._rateLimitCount += 1;
415
+ }
416
+
417
+ _releaseRateLimitedRequestQueue() {
418
+ this._rateLimitedRequestQueue.forEach(async ([resolve, endpoint, options]) => {
419
+ resolve(await this._fetch(endpoint, options));
420
+ });
421
+
422
+ this._rateLimitedRequestQueue = [];
423
+ }
424
+
384
425
  async _request(path, {
385
426
  body,
386
427
  headers,
@@ -20,6 +20,7 @@ export declare const DEFAULT_MAX_REDIRECTS: 5;
20
20
  export declare const DEFAULT_MAX_RETRIES: 3;
21
21
  export declare const DEFAULT_PATH_TEMPLATE_REGEX: RegExp;
22
22
  export declare const OPTIONAL_PATH_TEMPLATE_REGEX: RegExp;
23
+ export declare const DEFAULT_RATE_LIMIT = 50;
23
24
  export declare const DEFAULT_REQUEST_RETRY_WAIT = 100;
24
25
  export declare const MISSING_BASE_PATH_ERROR = "Getta expected to receive 'basePath' in the constructor options,\n but recevied undefined.";
25
26
  export declare const MAX_REDIRECTS_EXCEEDED_ERROR = "The request exceeded the maximum number of redirects, which is";
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,eAAO,MAAM,mBAAmB,eAAyB,CAAC;AAC1D,eAAO,MAAM,WAAW,QAAkB,CAAC;AAC3C,eAAO,MAAM,gBAAgB,YAAsB,CAAC;AACpD,eAAO,MAAM,WAAW,QAAkB,CAAC;AAC3C,eAAO,MAAM,WAAW,QAAkB,CAAC;AAE3C,eAAO,MAAM,cAAc;;;;;;CAM1B,CAAC;AAEF,eAAO,MAAM,mBAAmB,SAAU,WAAW,gBAAS,CAAC;AAC/D,eAAO,MAAM,qBAAqB,MAAgB,CAAC;AACnD,eAAO,MAAM,eAAe;;CAAyC,CAAC;AACtE,eAAO,MAAM,qBAAqB,GAAa,CAAC;AAChD,eAAO,MAAM,mBAAmB,GAAa,CAAC;AAC9C,eAAO,MAAM,2BAA2B,QAAmD,CAAC;AAC5F,eAAO,MAAM,4BAA4B,QAAyB,CAAC;AACnE,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAE9C,eAAO,MAAM,uBAAuB,gGACV,CAAC;AAE3B,eAAO,MAAM,4BAA4B,mEAAmE,CAAC;AAE7G,eAAO,MAAM,0BAA0B,iEAAiE,CAAC;AAEzG,eAAO,MAAM,0BAA0B,6EAA6E,CAAC;AAErH,eAAO,MAAM,wBAAwB,iDAAiD,CAAC;AAEvF,eAAO,MAAM,mBAAmB,+DAA+D,CAAC;AAEhG,eAAO,MAAM,UAAU,OAAiB,CAAC;AACzC,eAAO,MAAM,WAAW,QAAkB,CAAC;AAC3C,eAAO,MAAM,UAAU,OAAiB,CAAC;AACzC,eAAO,MAAM,aAAa,UAAoB,CAAC;AAE/C,eAAO,MAAM,aAAa,uCAAuD,CAAC;AAElF,eAAO,MAAM,oBAAoB,eAAyB,CAAC;AAC3D,eAAO,MAAM,mBAAmB,cAAwB,CAAC;AACzD,eAAO,MAAM,oBAAoB,eAAyB,CAAC;AAC3D,eAAO,MAAM,qBAAqB,eAAyB,CAAC;AAC5D,eAAO,MAAM,qBAAqB,eAAyB,CAAC;AAE5D,eAAO,MAAM,wBAAwB,KAAe,CAAC;AACrD,eAAO,MAAM,qBAAqB,KAAe,CAAC;AAElD,eAAO,MAAM,aAAa,UAAoB,CAAC;AAC/C,eAAO,MAAM,WAAW,QAAkB,CAAC;AAC3C,eAAO,MAAM,eAAe,YAAsB,CAAC;AACnD,eAAO,MAAM,oBAAoB,iBAA2B,CAAC;AAC7D,eAAO,MAAM,oBAAoB,iBAA2B,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,eAAO,MAAM,mBAAmB,eAAyB,CAAC;AAC1D,eAAO,MAAM,WAAW,QAAkB,CAAC;AAC3C,eAAO,MAAM,gBAAgB,YAAsB,CAAC;AACpD,eAAO,MAAM,WAAW,QAAkB,CAAC;AAC3C,eAAO,MAAM,WAAW,QAAkB,CAAC;AAE3C,eAAO,MAAM,cAAc;;;;;;CAM1B,CAAC;AAEF,eAAO,MAAM,mBAAmB,SAAU,WAAW,gBAAS,CAAC;AAC/D,eAAO,MAAM,qBAAqB,MAAgB,CAAC;AACnD,eAAO,MAAM,eAAe;;CAAyC,CAAC;AACtE,eAAO,MAAM,qBAAqB,GAAa,CAAC;AAChD,eAAO,MAAM,mBAAmB,GAAa,CAAC;AAC9C,eAAO,MAAM,2BAA2B,QAAmD,CAAC;AAC5F,eAAO,MAAM,4BAA4B,QAAyB,CAAC;AACnE,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAE9C,eAAO,MAAM,uBAAuB,gGACV,CAAC;AAE3B,eAAO,MAAM,4BAA4B,mEAAmE,CAAC;AAE7G,eAAO,MAAM,0BAA0B,iEAAiE,CAAC;AAEzG,eAAO,MAAM,0BAA0B,6EAA6E,CAAC;AAErH,eAAO,MAAM,wBAAwB,iDAAiD,CAAC;AAEvF,eAAO,MAAM,mBAAmB,+DAA+D,CAAC;AAEhG,eAAO,MAAM,UAAU,OAAiB,CAAC;AACzC,eAAO,MAAM,WAAW,QAAkB,CAAC;AAC3C,eAAO,MAAM,UAAU,OAAiB,CAAC;AACzC,eAAO,MAAM,aAAa,UAAoB,CAAC;AAE/C,eAAO,MAAM,aAAa,uCAAuD,CAAC;AAElF,eAAO,MAAM,oBAAoB,eAAyB,CAAC;AAC3D,eAAO,MAAM,mBAAmB,cAAwB,CAAC;AACzD,eAAO,MAAM,oBAAoB,eAAyB,CAAC;AAC3D,eAAO,MAAM,qBAAqB,eAAyB,CAAC;AAC5D,eAAO,MAAM,qBAAqB,eAAyB,CAAC;AAE5D,eAAO,MAAM,wBAAwB,KAAe,CAAC;AACrD,eAAO,MAAM,qBAAqB,KAAe,CAAC;AAElD,eAAO,MAAM,aAAa,UAAoB,CAAC;AAC/C,eAAO,MAAM,WAAW,QAAkB,CAAC;AAC3C,eAAO,MAAM,eAAe,YAAsB,CAAC;AACnD,eAAO,MAAM,oBAAoB,iBAA2B,CAAC;AAC7D,eAAO,MAAM,oBAAoB,iBAA2B,CAAC"}
@@ -15,6 +15,10 @@ export declare class Getta {
15
15
  private _pathTemplateCallback;
16
16
  private _pathTemplateRegExp;
17
17
  private _queryParams;
18
+ private _rateLimitCount;
19
+ private _rateLimitedRequestQueue;
20
+ private _rateLimitPerSecond;
21
+ private _rateLimitTimer;
18
22
  private _requestRetryWait;
19
23
  private _requestTracker;
20
24
  private _streamReader;
@@ -28,6 +32,7 @@ export declare class Getta {
28
32
  }>;
29
33
  post(path: string, options: Omit<Required<RequestOptions, "body">, "method">): Promise<FetchResponse<PlainObject>>;
30
34
  put(path: string, options: Omit<Required<RequestOptions, "body">, "methood">): Promise<FetchResponse<PlainObject>>;
35
+ private _addRequestToRateLimitedQueue;
31
36
  private _cacheEntryDelete;
32
37
  private _cacheEntryGet;
33
38
  private _cacheEntryHas;
@@ -38,6 +43,8 @@ export declare class Getta {
38
43
  private _fetchRetryHandler;
39
44
  private _get;
40
45
  private _getResolve;
46
+ private _rateLimit;
47
+ private _releaseRateLimitedRequestQueue;
41
48
  private _request;
42
49
  private _resolvePendingRequests;
43
50
  private _setPendingRequest;
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/main.ts"],"names":[],"mappings":"AAAA,OAAO,QAA0B,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAQ,WAAW,EAAgB,MAAM,gBAAgB,CAAC;AAIjE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAoCzC,OAAO,EACL,kBAAkB,EAGlB,aAAa,EAIb,cAAc,EAEd,kBAAkB,EAClB,SAAS,EAEV,MAAM,SAAS,CAAC;AAEjB,qBAAa,KAAK;IAChB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAO;IAC1B,OAAO,CAAC,MAAM,CAAC,CAAW;IAC1B,OAAO,CAAC,2BAA2B,CAAU;IAC7C,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAe;IAC/B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,2BAA2B,CAAS;IAC5C,OAAO,CAAC,qBAAqB,CAAuB;IACpD,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,eAAe,CAAsD;IAC7E,OAAO,CAAC,aAAa,CAAe;gBAExB,OAAO,EAAE,kBAAkB;IAsCvC,IAAI,KAAK,IAAI,QAAQ,GAAG,SAAS,CAEhC;IAEM,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,EAAE,QAAQ,CAAC,cAAc,EAAE,QAAQ,CAAC;IAW5F,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,IAAI,CAAC,cAAc,EAAE,QAAQ,CAAM;IAIjE,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,IAAI,CAAC,cAAc,EAAE,QAAQ,CAAM;;;;IAI9D,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC,EAAE,QAAQ,CAAC;IAI5E,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC,EAAE,SAAS,CAAC;YAI3E,iBAAiB;YAUjB,cAAc;YAUd,cAAc;YAUd,cAAc;YAUd,OAAO;YA0BP,MAAM;YA4DN,qBAAqB;YAiBrB,kBAAkB;YAYlB,IAAI;YAsCJ,WAAW;YAkCX,QAAQ;IAoBtB,OAAO,CAAC,uBAAuB;IAW/B,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,aAAa;CAStB;AAED,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,CAAC,SAAS,MAAM,EAAE,OAAO,EAAE,kBAAkB,EAAE,SAAS,CAAC,EAAE,SAAS,iCAS5G"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/main.ts"],"names":[],"mappings":"AAAA,OAAO,QAA0B,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAQ,WAAW,EAAgB,MAAM,gBAAgB,CAAC;AAIjE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAqCzC,OAAO,EACL,kBAAkB,EAGlB,aAAa,EAIb,cAAc,EAGd,kBAAkB,EAClB,SAAS,EAEV,MAAM,SAAS,CAAC;AAEjB,qBAAa,KAAK;IAChB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAO;IAC1B,OAAO,CAAC,MAAM,CAAC,CAAW;IAC1B,OAAO,CAAC,2BAA2B,CAAU;IAC7C,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAe;IAC/B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,2BAA2B,CAAS;IAC5C,OAAO,CAAC,qBAAqB,CAAuB;IACpD,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,eAAe,CAAa;IACpC,OAAO,CAAC,wBAAwB,CAAoB;IACpD,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,eAAe,CAA6B;IACpD,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,eAAe,CAAsD;IAC7E,OAAO,CAAC,aAAa,CAAe;gBAExB,OAAO,EAAE,kBAAkB;IAwCvC,IAAI,KAAK,IAAI,QAAQ,GAAG,SAAS,CAEhC;IAEM,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,EAAE,QAAQ,CAAC,cAAc,EAAE,QAAQ,CAAC;IAW5F,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,IAAI,CAAC,cAAc,EAAE,QAAQ,CAAM;IAIjE,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,IAAI,CAAC,cAAc,EAAE,QAAQ,CAAM;;;;IAI9D,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC,EAAE,QAAQ,CAAC;IAI5E,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC,EAAE,SAAS,CAAC;IAIzF,OAAO,CAAC,6BAA6B;YAMvB,iBAAiB;YAUjB,cAAc;YAUd,cAAc;YAUd,cAAc;YAUd,OAAO;YA0BP,MAAM;YA2DN,qBAAqB;YAiBrB,kBAAkB;YAYlB,IAAI;YAsCJ,WAAW;IAkCzB,OAAO,CAAC,UAAU;IAelB,OAAO,CAAC,+BAA+B;YASzB,QAAQ;IAoBtB,OAAO,CAAC,uBAAuB;IAW/B,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,aAAa;CAStB;AAED,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,CAAC,SAAS,MAAM,EAAE,OAAO,EAAE,kBAAkB,EAAE,SAAS,CAAC,EAAE,SAAS,iCAS5G"}
@@ -19,6 +19,7 @@ export interface ConstructorOptions {
19
19
  pathTemplateCallback?: PathTemplateCallback;
20
20
  pathTemplateRegExp?: RegExp;
21
21
  queryParams?: PlainObject;
22
+ rateLimitPerSecond?: number;
22
23
  requestRetryWait?: number;
23
24
  streamReader?: StreamReader;
24
25
  }
@@ -41,6 +42,7 @@ export interface RequestOptions {
41
42
  pathTemplateData?: StringObject;
42
43
  queryParams?: PlainObject;
43
44
  }
45
+ export declare type RequestQueue = [(value: FetchResponse) => void, string, FetchOptions][];
44
46
  export interface ResponseDataWithErrors<Resource = PlainObject> {
45
47
  data?: Resource;
46
48
  errors?: Error[];
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,oBAAY,WAAW,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;AAE5D,oBAAY,YAAY,GAAG,aAAa,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;AAEjF,oBAAY,kBAAkB,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI;KACzD,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,GAAG,WAAW,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;CACvF,CAAC;AAEF,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,IAAI,CAAC;IAClB,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,OAAO,EAAE,YAAY,CAAC;IACtB,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa,CAAC,QAAQ,GAAG,WAAW,CAAE,SAAQ,sBAAsB,CAAC,QAAQ,CAAC,EAAE,QAAQ;CAAG;AAE5G,MAAM,WAAW,2BAA4B,SAAQ,YAAY;IAC/D,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,gBAAgB,CAAC,EAAE,YAAY,CAAC;IAChC,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,MAAM,WAAW,sBAAsB,CAAC,QAAQ,GAAG,WAAW;IAC5D,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;CAClB;AAED,oBAAY,oBAAoB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,KAAK,MAAM,CAAC;AAE5G,oBAAY,sBAAsB,GAAG,CAAC,KAAK,EAAE,aAAa,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;AAEjF,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,sBAAsB,CAAC;CACjC;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,uBAAuB,EAAE,CAAC,CAAC;CACjD;AAED,MAAM,WAAW,SAAS;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC,CAAC;CAC7D"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "getta",
3
- "version": "0.2.3",
3
+ "version": "0.3.0",
4
4
  "description": "An isomorphic rest client based on the Fetch API.",
5
5
  "keywords": [
6
6
  "api-client",
package/src/constants.ts CHANGED
@@ -21,6 +21,7 @@ export const DEFAULT_MAX_REDIRECTS = 5 as const;
21
21
  export const DEFAULT_MAX_RETRIES = 3 as const;
22
22
  export const DEFAULT_PATH_TEMPLATE_REGEX = /({type})|({id})|({id,\+})|({brief\|standard})/g;
23
23
  export const OPTIONAL_PATH_TEMPLATE_REGEX = /({[a-zA-Z0-9_]+\?})/g;
24
+ export const DEFAULT_RATE_LIMIT = 50;
24
25
  export const DEFAULT_REQUEST_RETRY_WAIT = 100;
25
26
 
26
27
  export const MISSING_BASE_PATH_ERROR = `Getta expected to receive 'basePath' in the constructor options,
package/src/main.test.ts CHANGED
@@ -657,4 +657,44 @@ describe("Getta", () => {
657
657
  });
658
658
  });
659
659
  });
660
+
661
+ describe("rate limiting", () => {
662
+ let restClient: Getta;
663
+
664
+ beforeAll(() => {
665
+ restClient = createRestClient({ basePath });
666
+ });
667
+
668
+ describe("WHEN the number of requests per second exceeds rateLimitPerSecond", () => {
669
+ beforeAll(async () => {
670
+ const requestKeys = [...Array(55).keys()];
671
+
672
+ requestKeys.forEach(key => {
673
+ mockRequest(`product/${key}`, {}, {}, ({ endpoint, ...rest }) => {
674
+ fetchMock.get(endpoint, rest);
675
+ });
676
+ });
677
+
678
+ // @ts-ignore
679
+ restClient._addRequestToRateLimitedQueue = jest
680
+ .fn()
681
+ .mockResolvedValue({ data: {}, headers: new Headers(), status: 200 });
682
+
683
+ await Promise.all(requestKeys.map(key => restClient.get(`product/${key}`)));
684
+ });
685
+
686
+ afterAll(async () => {
687
+ await tearDownTest({ fetchMock, restClient });
688
+ });
689
+
690
+ it("SHOULD call fetch one less than the rate limit", () => {
691
+ expect(fetchMock.calls().length).toBe(49);
692
+ });
693
+
694
+ it("SHOULD add the excess requests to rateLimitedRequestQueue", async () => {
695
+ // @ts-ignore
696
+ expect(restClient._addRequestToRateLimitedQueue).toHaveBeenCalledTimes(6);
697
+ });
698
+ });
699
+ });
660
700
  });
package/src/main.ts CHANGED
@@ -12,6 +12,7 @@ import {
12
12
  DEFAULT_MAX_REDIRECTS,
13
13
  DEFAULT_MAX_RETRIES,
14
14
  DEFAULT_PATH_TEMPLATE_REGEX,
15
+ DEFAULT_RATE_LIMIT,
15
16
  DEFAULT_REQUEST_RETRY_WAIT,
16
17
  DELETE_METHOD,
17
18
  ETAG_HEADER,
@@ -48,6 +49,7 @@ import {
48
49
  PendingRequestResolver,
49
50
  PendingRequestResolvers,
50
51
  RequestOptions,
52
+ RequestQueue,
51
53
  RequestTracker,
52
54
  ShortcutProperties,
53
55
  Shortcuts,
@@ -67,6 +69,10 @@ export class Getta {
67
69
  private _pathTemplateCallback: PathTemplateCallback;
68
70
  private _pathTemplateRegExp: RegExp;
69
71
  private _queryParams: PlainObject;
72
+ private _rateLimitCount: number = 0;
73
+ private _rateLimitedRequestQueue: RequestQueue = [];
74
+ private _rateLimitPerSecond: number;
75
+ private _rateLimitTimer: NodeJS.Timer | null = null;
70
76
  private _requestRetryWait: number;
71
77
  private _requestTracker: RequestTracker = { active: [], pending: new Map() };
72
78
  private _streamReader: StreamReader;
@@ -85,6 +91,7 @@ export class Getta {
85
91
  pathTemplateCallback = defaultPathTemplateCallback,
86
92
  pathTemplateRegExp = DEFAULT_PATH_TEMPLATE_REGEX,
87
93
  queryParams = {},
94
+ rateLimitPerSecond = DEFAULT_RATE_LIMIT,
88
95
  requestRetryWait = DEFAULT_REQUEST_RETRY_WAIT,
89
96
  streamReader = JSON_FORMAT,
90
97
  } = options;
@@ -105,6 +112,7 @@ export class Getta {
105
112
  this._pathTemplateCallback = pathTemplateCallback;
106
113
  this._pathTemplateRegExp = pathTemplateRegExp;
107
114
  this._queryParams = queryParams;
115
+ this._rateLimitPerSecond = rateLimitPerSecond;
108
116
  this._requestRetryWait = requestRetryWait;
109
117
  this._streamReader = streamReader;
110
118
  }
@@ -140,6 +148,12 @@ export class Getta {
140
148
  return this._request(path, { ...options, method: PUT_METHOD });
141
149
  }
142
150
 
151
+ private _addRequestToRateLimitedQueue(endpoint: string, options: FetchOptions) {
152
+ return new Promise((resolve: (value: FetchResponse) => void) => {
153
+ this._rateLimitedRequestQueue.push([resolve, endpoint, options]);
154
+ });
155
+ }
156
+
143
157
  private async _cacheEntryDelete(requestHash: string): Promise<boolean> {
144
158
  if (!this._cache) return false;
145
159
 
@@ -213,6 +227,13 @@ export class Getta {
213
227
  reject(new Error(`${FETCH_TIMEOUT_ERROR} ${this._fetchTimeout}ms.`));
214
228
  }, this._fetchTimeout);
215
229
 
230
+ this._rateLimit();
231
+
232
+ if (!(this._rateLimitCount < this._rateLimitPerSecond)) {
233
+ resolve(await this._addRequestToRateLimitedQueue(endpoint, { redirects, retries, ...rest }));
234
+ return;
235
+ }
236
+
216
237
  const res = await fetch(endpoint, rest);
217
238
 
218
239
  clearTimeout(fetchTimer);
@@ -228,6 +249,8 @@ export class Getta {
228
249
  ...rest,
229
250
  }),
230
251
  );
252
+
253
+ return;
231
254
  }
232
255
 
233
256
  if (responseGroup === SERVER_ERROR_REPSONSE) {
@@ -237,27 +260,17 @@ export class Getta {
237
260
  ...rest,
238
261
  })) as FetchResponse,
239
262
  );
263
+
264
+ return;
240
265
  }
241
266
 
242
267
  const fetchRes = res as FetchResponse;
243
- const resClone = res.clone();
244
268
 
245
269
  try {
246
270
  fetchRes.data = res.body ? this._bodyParser(await res[this._streamReader]()) : undefined;
247
271
  resolve(fetchRes);
248
272
  } catch (e) {
249
- try {
250
- if (this._streamReader === "json" && res.body) {
251
- const fetchResClone = resClone as FetchResponse;
252
- const text = await resClone.text();
253
- fetchResClone.data = JSON.parse(text);
254
- resolve(fetchResClone);
255
- } else {
256
- reject([e, new Error(`Unable to ${rest.method} ${endpoint} due to previous error`)]);
257
- }
258
- } catch {
259
- reject([e, new Error(`Unable to ${rest.method} ${endpoint} due to previous error`)]);
260
- }
273
+ reject([e, new Error(`Unable to ${rest.method} ${endpoint} due to previous error`)]);
261
274
  }
262
275
  });
263
276
  } catch (error) {
@@ -367,6 +380,30 @@ export class Getta {
367
380
  return res;
368
381
  }
369
382
 
383
+ private _rateLimit() {
384
+ if (!this._rateLimitTimer) {
385
+ this._rateLimitTimer = setTimeout(() => {
386
+ this._rateLimitTimer = null;
387
+ this._rateLimitCount = 0;
388
+
389
+ if (this._rateLimitedRequestQueue.length) {
390
+ this._releaseRateLimitedRequestQueue();
391
+ }
392
+ }, 1000);
393
+ }
394
+
395
+ this._rateLimitCount += 1;
396
+ }
397
+
398
+ private _releaseRateLimitedRequestQueue() {
399
+ this._rateLimitedRequestQueue.forEach(async ([resolve, endpoint, options]) => {
400
+ // @ts-ignore
401
+ resolve(await this._fetch(endpoint, options));
402
+ });
403
+
404
+ this._rateLimitedRequestQueue = [];
405
+ }
406
+
370
407
  private async _request(
371
408
  path: string,
372
409
  { body, headers, method, pathTemplateData, queryParams, ...rest }: Required<RequestOptions, "method">,
package/src/types.ts CHANGED
@@ -23,6 +23,7 @@ export interface ConstructorOptions {
23
23
  pathTemplateCallback?: PathTemplateCallback;
24
24
  pathTemplateRegExp?: RegExp;
25
25
  queryParams?: PlainObject;
26
+ rateLimitPerSecond?: number;
26
27
  requestRetryWait?: number;
27
28
  streamReader?: StreamReader;
28
29
  }
@@ -49,6 +50,8 @@ export interface RequestOptions {
49
50
  queryParams?: PlainObject;
50
51
  }
51
52
 
53
+ export type RequestQueue = [(value: FetchResponse) => void, string, FetchOptions][];
54
+
52
55
  export interface ResponseDataWithErrors<Resource = PlainObject> {
53
56
  data?: Resource;
54
57
  errors?: Error[];