fch 6.0.0 → 6.0.2

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/index.d.ts CHANGED
@@ -5,12 +5,6 @@ type Store = {
5
5
  has?: (key: string) => Promise<boolean>;
6
6
  clear?: () => Promise<any>;
7
7
  };
8
- type Cache = boolean | number | string | Store | {
9
- expire?: number | string;
10
- store?: Store;
11
- shouldCache?: (request: any) => boolean;
12
- createKey?: (request: any) => string;
13
- };
14
8
  type Headers = {
15
9
  [name: string]: string;
16
10
  };
@@ -28,7 +22,7 @@ type Options = {
28
22
  headers?: Headers;
29
23
  baseUrl?: string;
30
24
  baseURL?: string;
31
- cache?: Cache;
25
+ cache?: Store;
32
26
  output?: string;
33
27
  credentials?: string;
34
28
  before?: (req: any) => any;
@@ -63,7 +57,7 @@ interface FchInstance {
63
57
  headers: Headers;
64
58
  baseUrl: string | null;
65
59
  baseURL: string | null;
66
- cache: any;
60
+ cache: Store | null;
67
61
  output: string;
68
62
  credentials: string;
69
63
  before?: (req: any) => any;
package/index.min.js CHANGED
@@ -1 +1 @@
1
- var z=async(i)=>(i=await i,Array.isArray(i)?await Promise.all(i.map(z)):i),Y=(i,n)=>(...x)=>((w)=>w instanceof RegExp?w.test.bind(w):w)(i).call(n,...x),T=(i,n)=>async(x,w,m)=>({value:x,extra:await Y(i,n)(x,w,m)}),D=({extra:i})=>i,K=({value:i})=>i,Q={every:async(i,n,x)=>{for(let w=0;w<i.length;w++)if(!await Y(n,x)(i[w],w,i))return!1;return!0},filter:async(i,n,x)=>(await z(i.map(T(n,x)))).filter(D).map(K),find:async(i,n,x)=>{for(let w=0;w<i.length;w++)if(await Y(n,x)(i[w],w,i))return i[w]},findIndex:async(i,n,x)=>{for(let w=0;w<i.length;w++)if(await Y(n,x)(i[w],w,i))return w;return-1},forEach:async(i,n,x)=>(await z(i.map(T(n,x))),i),reduce:async(i,n,x)=>{let w=x!==void 0;w||(x=i[0]);for(let m=w?0:1;m<i.length;m++)x=await Y(n)(x,i[m],m,i);return x},reduceRight:async(i,n,x)=>{let w=x!==void 0;w||(x=i[i.length-1]);for(let m=i.length-(w?1:2);m>=0;m--)x=await Y(n)(x,i[m],m,i);return x},some:async(i,n,x)=>{for(let w=0;w<i.length;w++)if(await Y(n,x)(i[w],w,i))return!0;return!1}},V=(i,n)=>(x,w)=>w==="then"?(...m)=>z(i).then(...m):w==="catch"?(...m)=>G(z(i).catch(...m)):E(z(i).then((m)=>typeof w=="symbol"?m[w]:(w in n)?E((...P)=>n[w](m,...P),n):typeof m=="number"&&(w in n.number)?E((...P)=>n.number[w](m,...P),n):typeof m=="string"&&(w in n.string)?E((...P)=>n.string[w](m,...P),n):Array.isArray(m)&&(w in n.array)?E((...P)=>n.array[w](m,...P),n):m[w]&&m[w].bind?E(m[w].bind(m),n):E(m[w],n)),n),F=(i,n)=>(x,w,m)=>E(z(i).then((P)=>{return typeof P!="function"?(g=`You tried to call "${JSON.stringify(P)}" (${typeof P}) as a function, but it is not.`,Promise.reject(Error(g))):P(...m);var g}),n),E=(i,n)=>new Proxy(()=>{},{get:V(i,n),apply:F(i,n)});function G(i,{number:n,string:x,array:w,...m}={}){return typeof i=="function"?(...P)=>G(Promise.all(P).then((g)=>i(...g)),{number:n,string:x,array:w,...m}):new Proxy({},{get:V(i,{number:{...n},string:{...x},array:{...Q,...w},...m})})}var H=(i)=>{if(!i)return!1;if(i instanceof FormData)return!1;if(typeof(i.pipe||i.pipeTo)==="function")return!1;return typeof i==="object"||Array.isArray(i)},J=(i)=>{if(typeof i!=="object")return i;for(let n in i)if(i[n]===void 0)delete i[n];return i};class X extends Error{response;constructor(i){let n="Error "+i.status;super(n);this.response=i,this.message=n}}var R=(i,n,x)=>{let[w,m=""]=i.split("?"),P=new URLSearchParams(Object.fromEntries([...new URLSearchParams(J(n)),...new URLSearchParams(J(m))])).toString();if(P)w=w+"?"+P;if(!x)return w;return new URL(w.replace(/^\//,""),x).href},B=(i)=>{let n={};for(let[x,w]of Object.entries(i))n[x.toLowerCase()]=w;return n},Z=async(i)=>{let n=i.headers.get("content-type"),x=n&&n.includes("application/json"),w=await i.clone().text();return x?JSON.parse(w):w},_=async(i)=>{let n={status:i.status,statusText:i.statusText,headers:{},body:void 0};if(i.headers.forEach((x,w)=>{n.headers[w.toLowerCase()]=x}),!i.ok)throw new X(i);return n.body=await Z(i),n},W=(i,{ref:n,after:x,error:w,output:m})=>{return fetch(i.url,i).then(async(P)=>{if(n.res=P,P.ok&&m==="stream")return P.body;if(P.ok&&P[m]&&typeof P[m]==="function")return P[m]();let g=x(await _(P));if(m==="body")return g.body;else if(m==="response")return g;else if(m==="raw")return P.clone();else throw Error(`Invalid option output="${m}"`)}).catch(w)},C=(i)=>i.method==="get",U=(i)=>i.method+":"+i.url,y=(i)=>{let n={store:i??null,shouldCache:C,createKey:U,clear:()=>Promise.resolve(n.store).then((x)=>x?.clear?x.clear():Promise.resolve())};return n};function L(i={}){let n={},x={},m=G(async(P="/",g={})=>{let{output:v,before:M,after:O,error:S,cache:j,...A}={...m,...g},N={...m.cache};if(A.url=R(P,{...m.query,...g.query},A.baseUrl??A.baseURL),A.method=A.method.toLowerCase()||"GET",A.headers=B({...m.headers,...g.headers}),typeof SubmitEvent<"u"&&A.body instanceof SubmitEvent)A.body=new FormData(A.body.target);if(typeof HTMLFormElement<"u"&&A.body instanceof HTMLFormElement)A.body=new FormData(A.body);if(H(A.body))A.body=JSON.stringify(J(A.body)),A.headers["content-type"]="application/json";if(A=M?M(A):A,N.shouldCache(A)&&N.store){let $=N.createKey(A);if(await N.store.has($))return N.store.get($);if(n[$])return n[$];let I;try{n[$]=W(A,{ref:x,cache:N,output:v,error:S,after:O}),I=await n[$]}finally{delete n[$]}return await N.store.set($,I),I}return W(A,{ref:x,output:v,error:S,after:O})},{text:()=>x.res.clone().text(),json:()=>x.res.clone().json(),blob:()=>x.res.clone().blob(),stream:()=>x.res.clone().body,arrayBuffer:()=>x.res.clone().arrayBuffer(),formData:()=>x.res.clone().formData(),body:()=>Z(x.res.clone()),clone:()=>x.res.clone(),raw:()=>x.res.clone(),response:()=>_(x.res.clone())});return m.url=i.url??"/",m.method=i.method??"get",m.query=i.query??{},m.headers=i.headers??{},m.baseUrl=i.baseUrl??i.baseURL??null,m.baseURL=i.baseUrl??i.baseURL??null,m.cache=y(i.cache),m.output=i.output??"body",m.credentials=i.credentials??"include",m.before=i.before??((P)=>P),m.after=i.after??((P)=>P),m.error=i.error??((P)=>Promise.reject(P)),m.get=(P,g)=>m(P,{method:"get",...g}),m.head=(P,g)=>m(P,{method:"head",...g}),m.post=(P,g,v)=>m(P,{method:"post",body:g,...v}),m.patch=(P,g,v)=>m(P,{method:"patch",body:g,...v}),m.put=(P,g,v)=>m(P,{method:"put",body:g,...v}),m.delete=(P,g)=>m(P,{method:"delete",...g}),m.del=m.delete,m.create=L,m}if(typeof window<"u")window.fch=L();var b=L();export{b as default,L as create};
1
+ var S=async(i)=>(i=await i,Array.isArray(i)?await Promise.all(i.map(S)):i),N=(i,w)=>(...x)=>((n)=>n instanceof RegExp?n.test.bind(n):n)(i).call(w,...x),O=(i,w)=>async(x,n,m)=>({value:x,extra:await N(i,w)(x,n,m)}),_=({extra:i})=>i,D=({value:i})=>i,Q={every:async(i,w,x)=>{for(let n=0;n<i.length;n++)if(!await N(w,x)(i[n],n,i))return!1;return!0},filter:async(i,w,x)=>(await S(i.map(O(w,x)))).filter(_).map(D),find:async(i,w,x)=>{for(let n=0;n<i.length;n++)if(await N(w,x)(i[n],n,i))return i[n]},findIndex:async(i,w,x)=>{for(let n=0;n<i.length;n++)if(await N(w,x)(i[n],n,i))return n;return-1},forEach:async(i,w,x)=>(await S(i.map(O(w,x))),i),reduce:async(i,w,x)=>{let n=x!==void 0;n||(x=i[0]);for(let m=n?0:1;m<i.length;m++)x=await N(w)(x,i[m],m,i);return x},reduceRight:async(i,w,x)=>{let n=x!==void 0;n||(x=i[i.length-1]);for(let m=i.length-(n?1:2);m>=0;m--)x=await N(w)(x,i[m],m,i);return x},some:async(i,w,x)=>{for(let n=0;n<i.length;n++)if(await N(w,x)(i[n],n,i))return!0;return!1}},T=(i,w)=>(x,n)=>n==="then"?(...m)=>S(i).then(...m):n==="catch"?(...m)=>Y(S(i).catch(...m)):v(S(i).then((m)=>typeof n=="symbol"?m[n]:(n in w)?v((...P)=>w[n](m,...P),w):typeof m=="number"&&(n in w.number)?v((...P)=>w.number[n](m,...P),w):typeof m=="string"&&(n in w.string)?v((...P)=>w.string[n](m,...P),w):Array.isArray(m)&&(n in w.array)?v((...P)=>w.array[n](m,...P),w):m[n]&&m[n].bind?v(m[n].bind(m),w):v(m[n],w)),w),F=(i,w)=>(x,n,m)=>v(S(i).then((P)=>{return typeof P!="function"?(g=`You tried to call "${JSON.stringify(P)}" (${typeof P}) as a function, but it is not.`,Promise.reject(Error(g))):P(...m);var g}),w),v=(i,w)=>new Proxy(()=>{},{get:T(i,w),apply:F(i,w)});function Y(i,{number:w,string:x,array:n,...m}={}){return typeof i=="function"?(...P)=>Y(Promise.all(P).then((g)=>i(...g)),{number:w,string:x,array:n,...m}):new Proxy({},{get:T(i,{number:{...w},string:{...x},array:{...Q,...n},...m})})}var H=(i)=>{if(!i)return!1;if(i instanceof FormData)return!1;if(typeof(i.pipe||i.pipeTo)==="function")return!1;return typeof i==="object"||Array.isArray(i)},G=(i)=>{if(typeof i!=="object")return i;for(let w in i)if(i[w]===void 0)delete i[w];return i};class W extends Error{response;constructor(i){let w="Error "+i.status;super(w);this.response=i,this.message=w}}var R=(i,w,x)=>{let[n,m=""]=i.split("?"),P=new URLSearchParams(Object.fromEntries([...new URLSearchParams(G(w)),...new URLSearchParams(G(m))])).toString();if(P)n=n+"?"+P;if(!x)return n;return new URL(n.replace(/^\//,""),x).href},B=(i)=>{let w={};for(let[x,n]of Object.entries(i))w[x.toLowerCase()]=n;return w},X=async(i)=>{let w=i.headers.get("content-type"),x=w&&w.includes("application/json"),n=await i.clone().text();return x?JSON.parse(n):n},Z=async(i)=>{let w={status:i.status,statusText:i.statusText,headers:{},body:void 0};if(i.headers.forEach((x,n)=>{w.headers[n.toLowerCase()]=x}),!i.ok)throw new W(i);return w.body=await X(i),w},V=(i,{ref:w,after:x,error:n,output:m})=>{return fetch(i.url,i).then(async(P)=>{if(w.res=P,P.ok&&m==="stream")return P.body;if(P.ok&&P[m]&&typeof P[m]==="function")return P[m]();let g=x(await Z(P));if(m==="body")return g.body;else if(m==="response")return g;else if(m==="raw")return P.clone();else throw Error(`Invalid option output="${m}"`)}).catch(n)};function I(i={}){let w={},x={},m=Y(async(P="/",g={})=>{let{output:$,before:J,after:K,error:L,cache:z,...A}={...m,...g};if(A.url=R(P,{...m.query,...g.query},A.baseUrl??A.baseURL),A.method=(A.method||"get").toLowerCase(),A.headers=B({...m.headers,...g.headers}),typeof SubmitEvent<"u"&&A.body instanceof SubmitEvent||typeof HTMLFormElement<"u"&&A.body instanceof HTMLFormElement)A.body=new FormData(A.body);if(H(A.body))A.body=JSON.stringify(G(A.body)),A.headers["content-type"]="application/json";if(A=J?J(A):A,!z||A.method!=="get")return V(A,{ref:x,output:$,error:L,after:K});let E=A.method+":"+A.url,M=await z.get(E);if(M)return M;if(w[E])return w[E];let C;try{w[E]=V(A,{ref:x,output:$,error:L,after:K}),C=await w[E]}finally{delete w[E]}return await z.set(E,C),C},{text:()=>x.res.clone().text(),json:()=>x.res.clone().json(),blob:()=>x.res.clone().blob(),stream:()=>x.res.clone().body,arrayBuffer:()=>x.res.clone().arrayBuffer(),formData:()=>x.res.clone().formData(),body:()=>X(x.res.clone()),clone:()=>x.res.clone(),raw:()=>x.res.clone(),response:()=>Z(x.res.clone())});return m.url=i.url??"/",m.method=i.method??"get",m.query=i.query??{},m.headers=i.headers??{},m.baseUrl=i.baseUrl??i.baseURL??null,m.baseURL=i.baseUrl??i.baseURL??null,m.cache=i.cache??null,m.output=i.output??"body",m.credentials=i.credentials??"include",m.before=i.before??((P)=>P),m.after=i.after??((P)=>P),m.error=i.error??((P)=>Promise.reject(P)),m.get=(P,g)=>m(P,{method:"get",...g}),m.head=(P,g)=>m(P,{method:"head",...g}),m.post=(P,g,$)=>m(P,{method:"post",body:g,...$}),m.patch=(P,g,$)=>m(P,{method:"patch",body:g,...$}),m.put=(P,g,$)=>m(P,{method:"put",body:g,...$}),m.delete=(P,g)=>m(P,{method:"delete",...g}),m.del=m.delete,m.create=I,m}if(typeof window<"u")window.fch=I();var j=I();export{j as default,I as create};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fch",
3
- "version": "6.0.0",
3
+ "version": "6.0.2",
4
4
  "description": "Fetch interface with better promises, deduplication, defaults, etc.",
5
5
  "homepage": "https://github.com/franciscop/fetch",
6
6
  "repository": "https://github.com/franciscop/fetch.git",
package/readme.md CHANGED
@@ -1,4 +1,4 @@
1
- # Fch [![npm install fch](https://img.shields.io/badge/npm%20install-fch-blue.svg)](https://www.npmjs.com/package/fch) [![test badge](https://github.com/franciscop/fetch/workflows/tests/badge.svg "test badge")](https://github.com/franciscop/fetch/blob/master/.github/workflows/tests.yml) [![gzip size](https://img.badgesize.io/franciscop/fetch/master/index.min.js.svg?compression=gzip)](https://github.com/franciscop/fetch/blob/master/index.min.js)
1
+ # Fch [![npm install fch](https://img.shields.io/badge/npm%20install-fch-blue.svg)](https://www.npmjs.com/package/fch) [![test badge](https://github.com/franciscop/fetch/workflows/tests/badge.svg "test badge")](https://github.com/franciscop/fetch/actions) [![gzip size](https://img.badgesize.io/franciscop/fetch/master/index.min.js.svg?compression=gzip)](https://bundlephobia.com/package/fch)
2
2
 
3
3
  A tiny library to make API calls easier. Similar to Axios, but tiny size and simpler API:
4
4
 
@@ -17,7 +17,7 @@ await api.patch("/pokemon/150", { type: "psychic" });
17
17
  - Create instances with shared options across requests.
18
18
  - Configurable **cache** that works in-memory, with redis, or others.
19
19
  - Automatically encode and decode JSON bodies.
20
- - Await/Async Promises; `>= 400 and <= 100` will throw an error.
20
+ - Await/Async Promises; non-OK responses (status < 200 or >= 300) will throw an error.
21
21
  - Credentials: "include" by default
22
22
  - Interceptors: `before` the request, `after` the response and catch with `error`.
23
23
  - Designed for both Node.js and the browser through its extensible cache system.
@@ -156,9 +156,9 @@ It can be either absolute or relative, in which case it'll use the local one in
156
156
 
157
157
  ```js
158
158
  import api from "fch";
159
- api.baseUrl = "https//api.filemon.io/";
159
+ api.baseUrl = "https://api.filemon.io/";
160
160
  api.get("/hello");
161
- // Called https//api.filemon.io/hello
161
+ // Called https://api.filemon.io/hello
162
162
  ```
163
163
 
164
164
  > Note: with Node.js you need to either set an absolute baseUrl or make the URL absolute
@@ -170,10 +170,10 @@ api.get("/hello");
170
170
  The `body` can be a string, a plain object|array, a FormData instance, a ReadableStream, a SubmitEvent or a HTMLFormElement. If it's a plain array or object, it'll be stringified and the header `application/json` will be added:
171
171
 
172
172
  ```js
173
- import api from "api";
173
+ import fch from "fch";
174
174
 
175
175
  // Sending plain text
176
- await api.post("/houses", "plain text");
176
+ await fch.post("/houses", "plain text");
177
177
 
178
178
  // Will JSON.stringify it internally and add the JSON headers
179
179
  await api.post("/houses", { id: 1, name: "Cute Cottage" });
@@ -182,12 +182,12 @@ await api.post("/houses", { id: 1, name: "Cute Cottage" });
182
182
  form.onsubmit = (e) => api.post("/houses", new FormData(e.target));
183
183
 
184
184
  // We have some helpers so you can just pass the Event or <form> itself!
185
- form.onsubmit = (e) => api.post("/houses", e); // does not work with jquery BTW
185
+ form.onsubmit = (e) => api.post("/houses", e);
186
186
  form.onsubmit = (e) => api.post("/houses", e.target);
187
187
  form.onsubmit = (e) => api.post("/houses", new FormData(e.target));
188
188
  ```
189
189
 
190
- The methods `GET`, `HEAD` and `DELETE` do not accept a body and it'll be ignored if you try to force it into the options.
190
+ The methods `GET` and `HEAD` do not accept a body. For `DELETE`, while not common, you can pass a body via the options parameter if needed: `fch.delete('/resource', { body: {...} })`.
191
191
 
192
192
  ### Query
193
193
 
@@ -295,7 +295,7 @@ There are few options that can be specified:
295
295
  - `output: "response"`: return the full response with the properties `body`, `headers`, `status`. The body will be parsed as JSON or plain TEXT depending on the headers. If you want the raw `response`, use `raw` or `clone` instead (see below in "raw" or "clone").
296
296
  - `output: "stream"`: return a [web ReadableStream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) of the body as the result of the promise.
297
297
  - `output: "arrayBuffer"`\*: returns an arrayBuffer of the response body.
298
- - `output: "blob"`\*: returns an arrayBuffer of the response body.
298
+ - `output: "blob"`\*: returns a Blob of the response body.
299
299
  - `output: "clone"`\*: returns the raw Response, with the raw body. See also `raw` below.
300
300
  - `output: "formData"`\* (might be unavailable): returns an instance of FormData with all the parsed data.
301
301
  - `output: "json"`\*: attempts to parse the response as JSON.
@@ -326,36 +326,24 @@ stream.pipeTo(...);
326
326
 
327
327
  ### Cache
328
328
 
329
- The cache (disabled by default) is a great method to reduce the number of API requests we make. We use [polystore](https://polystore.dev/) for cache storage, which provides a unified interface for multiple storage backends.
330
-
331
- > Note: cache should only be used through `fch.create({ cache: ... })`, not through the global instance.
332
-
333
- To activate the cache, you can pass a time string or number (seconds), and an in-memory store will be created automatically:
329
+ > We use **[polystore](https://polystore.dev/)** for Cache management.
334
330
 
335
- ```js
336
- // This API has 1h cache by default (uses in-memory Map):
337
- const api = fch.create({
338
- baseUrl: 'https://api.myweb.com/',
339
- cache: '1h'
340
- });
331
+ The cache (disabled by default) is a great method to reduce the number of API requests we make. Provide a [polystore](https://polystore.dev/) Store instance directly to `fch.create()` and fch will cache GET responses using whatever persistence that store offers. Set `cache` to `null` on a call to skip caching entirely.
341
332
 
342
- // This specific call will be cached for 20s
343
- api.get('/somedata', { cache: '20s' });
344
- ```
333
+ While a GET request is in flight, fch keeps an internal map of ongoing requests to deduplicate parallel calls. As soon as the request resolves, the result is written to your store (if available) and the entry is removed from the in-flight map.
345
334
 
346
- For more control, you can use **polystore** to create a custom cache store with different backends (in-memory, Redis, localStorage, etc.):
335
+ To activate the cache, create a **polystore** store and pass it to fch:
347
336
 
348
337
  ```js
349
- import fch from "fch";
350
338
  import kv from "polystore";
351
339
 
352
- // In-memory cache with 1 hour expiration
353
- const cache = kv(new Map()).expires("1h");
340
+ // This API reuses responses using an in-memory Map:
341
+ const cache = kv(new Map(), { expires: '1h' });
342
+ const api = fch.create({ baseUrl: "...", cache });
354
343
 
355
- const api = fch.create({
356
- baseUrl: 'https://api.myweb.com/',
357
- cache: cache
358
- });
344
+ // Call `.expires()` to return an instance with a different cache time
345
+ const shortCache = cache.expires('10min');
346
+ api.get("/somedata", { cache: shortCache });
359
347
  ```
360
348
 
361
349
  Polystore supports multiple backends. For example, with Redis:
@@ -365,21 +353,18 @@ import fch from "fch";
365
353
  import kv from "polystore";
366
354
  import { createClient } from "redis";
367
355
 
368
- // Redis cache with 1 hour expiration
369
- const redis = await createClient().connect();
370
- const cache = kv(redis).expires("1h");
356
+ // Redis-backed cache store
357
+ const cache = kv(createClient().connect());
371
358
 
372
- const api = fch.create({
373
- cache: cache
374
- });
359
+ const api = fch.create({ cache });
375
360
  ```
376
361
 
377
- That's the basic usage, but "invalidating cache" is not one of the complex topics in CS for no reason. Let's dig deeper. To clear the cache, you can call `cache.clear()` at any time:
362
+ That's the basic usage, but "invalidating cache" is not one of the complex topics in CS for no reason. To clear the cache, call `cache.clear()` at any time:
378
363
 
379
364
  ```js
380
365
  import kv from "polystore";
381
366
 
382
- const cache = kv(new Map()).expires("1h");
367
+ const cache = kv(new Map());
383
368
  const api = fch.create({ cache });
384
369
 
385
370
  // Remove them all
@@ -394,7 +379,7 @@ import kv from "polystore";
394
379
  import { createClient } from "redis";
395
380
 
396
381
  const redis = await createClient().connect();
397
- const cache = kv(redis).expires("1h");
382
+ const cache = kv(redis);
398
383
 
399
384
  const api = fch.create({ cache });
400
385
 
@@ -402,67 +387,9 @@ const api = fch.create({ cache });
402
387
  await redis.flushDB();
403
388
  ```
404
389
 
405
- For advanced cache configuration, you can pass an object with additional options like `shouldCache` and `createKey`:
406
-
407
- ```js
408
- import kv from "polystore";
409
-
410
- const cache = kv(new Map()).expires("1h");
411
-
412
- const api = fch.create({
413
- cache: {
414
- store: cache,
415
- // Default shouldCache; Note the lowercase
416
- shouldCache: (request) => request.method === "get",
417
-
418
- // Default createKey;
419
- createKey: (request) => request.method + ":" + request.url,
420
- },
421
- });
422
- ```
423
-
424
- For example, if you want to differentiate the auth requests from the non-auth requests, you can do it so:
425
-
426
- ```js
427
- import kv from "polystore";
428
-
429
- const cache = kv(new Map()).expires("1h");
430
-
431
- const api = fch.create({
432
- cache: {
433
- store: cache,
434
- // Create a key unique for each user
435
- createKey: (req) => user.id + ":" + req.method + ":" + req.url,
436
- }
437
- });
438
-
439
- const onLogin = (user) => {
440
- // Remove the old requests since we were not auth'ed yet
441
- cache.clear();
442
- };
443
- ```
444
-
445
- Or maybe you just want to NOT cache any of the requests that have an `Authorization` header, you can do so:
446
-
447
- ```js
448
- import kv from "polystore";
449
-
450
- const cache = kv(new Map()).expires("1week");
451
-
452
- const api = fch.create({
453
- cache: {
454
- store: cache,
455
- // Note the lowercase in both! we normalize them to be lowercase
456
- shouldCache: (req) => req.method === "get" && !req.headers.authorization,
457
- },
458
- });
459
- ```
460
-
461
- It is this flexible since you can use fch both in the front-end and back-end, so usually in each of them you might want to follow a slightly different strategy.
462
-
463
390
  #### Creating a custom store
464
391
 
465
- You can create a custom store that works with fch by implementing the polystore-compatible interface. See the [polystore documentation](https://polystore.dev/) for details on creating custom stores and adapters.
392
+ You can create a custom store that works with fch by implementing the polystore-compatible interface. See the [polystore documentation](https://polystore.dev/documentation#custom-store) for details on creating custom stores and adapters.
466
393
 
467
394
  ### Interceptors
468
395
 
@@ -581,8 +508,9 @@ import fch from "fch";
581
508
  // All the requests will add the Authorization header when the token is
582
509
  // in localStorage
583
510
  fch.before = (req) => {
584
- if (localStorage.get("token")) {
585
- req.headers.Authorization = "bearer " + localStorage.get("token");
511
+ const token = localStorage.getItem("token");
512
+ if (token) {
513
+ req.headers.Authorization = "bearer " + token;
586
514
  }
587
515
  return req;
588
516
  };
@@ -696,7 +624,7 @@ axios.interceptors.request.use(fn);
696
624
  fch.before = fn;
697
625
  ```
698
626
 
699
- API size is also strikingly different, with **7.8kb** for Axios and **1.1kb** for fch.
627
+ API size is also strikingly different, with **7.8kb** for Axios and **2.1kb** for fch (gzipped).
700
628
 
701
629
  As disadvantages, I can think of two major ones for `fch`:
702
630