@koine/api 2.0.0-beta.82 → 2.0.0-beta.83

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/ApiError.cjs.js CHANGED
@@ -2,7 +2,13 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- class ApiError extends Error{constructor(r){super(`Request failed with ${r.status} ${r.msg}`),this.name="ApiError",Object.assign(this,r);}}
5
+ /**
6
+ * Custom `ApiError` class extending `Error` to throw in failed response.
7
+ *
8
+ * @see https://eslint.org/docs/rules/no-throw-literal
9
+ * @see https://github.com/sindresorhus/ky/blob/main/source/errors/HTTPError.ts
10
+ *
11
+ */class ApiError extends Error{constructor(r){super(`Request failed with ${r.status} ${r.msg}`),this.name="ApiError",Object.assign(this,r);}}
6
12
 
7
13
  exports.ApiError = ApiError;
8
14
  exports["default"] = ApiError;
package/ApiError.esm.js CHANGED
@@ -1,3 +1,9 @@
1
- class ApiError extends Error{constructor(r){super(`Request failed with ${r.status} ${r.msg}`),this.name="ApiError",Object.assign(this,r);}}
1
+ /**
2
+ * Custom `ApiError` class extending `Error` to throw in failed response.
3
+ *
4
+ * @see https://eslint.org/docs/rules/no-throw-literal
5
+ * @see https://github.com/sindresorhus/ky/blob/main/source/errors/HTTPError.ts
6
+ *
7
+ */class ApiError extends Error{constructor(r){super(`Request failed with ${r.status} ${r.msg}`),this.name="ApiError",Object.assign(this,r);}}
2
8
 
3
9
  export { ApiError, ApiError as default };
package/createApi.cjs.js CHANGED
@@ -4,7 +4,24 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var utils = require('@koine/utils');
6
6
 
7
- let o=["get","post","put","patch","delete"];let createApi=(l,a,i)=>{let{headers:s={},request:n={},throwErr:p,timeout:c=1e4,processReq:u,processRes:f,processErr:d}=i||{};return o.reduce((o,i)=>(o[i]=async(o,h)=>{let $,m;let{request:w=n,headers:y=s,timeout:g=c,processReq:b,processRes:E=f,processErr:N=d,throwErr:k=p}=h||{},{params:A,json:C,query:j}=h||{},q=`${a}/${o+"".replace(/^\/*/,"")}`,v={method:i.toUpperCase(),...w,headers:{"content-type":"application/json",...y}};if(u){let e=u(i,q,j,C,A,v);q=e[0],j=e[1],C=e[2],A=e[3],v=e[4];}if(b){let e=b(i,q,j,C,A,v);q=e[0],j=e[1],C=e[2],A=e[3],v=e[4];}if(utils.isFullObject(A))for(let e in A)q=q.replace(`{${e}}`,A[e].toString());let x=Number(g);C&&(v.body=JSON.stringify(C)),x>0&&($=new AbortController,m=setTimeout(()=>$.abort(),x),v.signal=$.signal),j&&(q+=utils.buildUrlQueryString(j));let O=null,R=null,S="";try{O=await fetch(q,v);}catch(e){S=utils.errorToString(e);}if(m&&clearTimeout(m),O)try{R=E?await E(O,h||{}):await O.json();}catch(e){S=utils.errorToString(e);}if(null===R&&(R=N?await N(S,h||{}):{data:null,msg:S,status:100,fail:!0,ok:!1}),k&&R?.fail)throw R;if("development"===process.env.NODE_ENV){let e=`${R?.status}: api[${l}] ${i.toUpperCase()} ${q}`;R?.ok?console.info(`🟢 ${e}`):console.info(`🔴 ${e}`);}return R},o),{})};
7
+ let o=["get","post","put","patch","delete"];/**
8
+ * Create api client
9
+ *
10
+ * @param apiName Short name to use in debug logs
11
+ * @param baseUrl Either relativ eor absolute, it must end without trailing slash
12
+ */let createApi=(l,a,i)=>{let{headers:s={},request:n={},throwErr:p,timeout:c=1e4,processReq:u,processRes:f,processErr:d}=i||{};return o.reduce((o,i)=>(// @ts-expect-error FIXME: type
13
+ o[i]=async(o,h)=>{let $,m;let{request:w=n,headers:y=s,timeout:g=c,processReq:b,processRes:E=f,processErr:N=d,throwErr:k=p}=h||{},{params:A,json:C,query:j}=h||{},q=`${a}/${o+"".replace(/^\/*/,"")}`,v={method:i.toUpperCase(),...w,headers:{"content-type":"application/json",...y}};if(u){let e=u(i,q,j,C,A,v);q=e[0],j=e[1],C=e[2],A=e[3],v=e[4];}if(b){let e=b(i,q,j,C,A,v);q=e[0],j=e[1],C=e[2],A=e[3],v=e[4];}if(utils.isFullObject(A))for(let e in A)q=q.replace(`{${e}}`,A[e].toString());let x=Number(g);C&&(v.body=JSON.stringify(C)),x>0&&(// TODO: combine multiple abort signals
14
+ // @see https://dev.to/rashidshamloo/adding-timeout-and-multiple-abort-signals-to-fetch-typescriptreact-33bb
15
+ $=new AbortController,m=setTimeout(()=>$.abort(),x),v.signal=$.signal),j&&// FIXME: ts-expect-error this assertion is not the best, but nevermind for now
16
+ (q+=utils.buildUrlQueryString(j));let O=null,R=null,S="";try{O=await fetch(q,v);}catch(e){S=utils.errorToString(e);}if(m&&clearTimeout(m),O)try{R=E?await E(O,h||{}):await O.json();}catch(e){S=utils.errorToString(e);}if(null===R&&(R=N?await N(S,h||{}):// this error should only happen on network errors or wrong API urls
17
+ // there is no specific HTTP error for this, we can consider these
18
+ // two statuses though:
19
+ // - [100 Continue](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/100)
20
+ // - [501 Not Implemented](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/501)
21
+ {data:null,msg:S,status:100,fail:!0,ok:!1}),k&&R?.fail)// throw new ApiError<Failed>(result);
22
+ // I prefer to throw an object literal despite what eslint says
23
+ // eslint-disable-next-line no-throw-literal
24
+ throw R;if("development"===process.env.NODE_ENV){let e=`${R?.status}: api[${l}] ${i.toUpperCase()} ${q}`;R?.ok?console.info(`🟢 ${e}`):console.info(`🔴 ${e}`);}return R},o),{})};
8
25
 
9
26
  exports.createApi = createApi;
10
27
  exports["default"] = createApi;
package/createApi.esm.js CHANGED
@@ -1,5 +1,22 @@
1
1
  import { isFullObject, buildUrlQueryString, errorToString } from '@koine/utils';
2
2
 
3
- let o=["get","post","put","patch","delete"];let createApi=(l,a,i)=>{let{headers:s={},request:n={},throwErr:p,timeout:c=1e4,processReq:u,processRes:f,processErr:d}=i||{};return o.reduce((o,i)=>(o[i]=async(o,h)=>{let $,m;let{request:w=n,headers:y=s,timeout:g=c,processReq:b,processRes:E=f,processErr:N=d,throwErr:k=p}=h||{},{params:A,json:C,query:j}=h||{},q=`${a}/${o+"".replace(/^\/*/,"")}`,v={method:i.toUpperCase(),...w,headers:{"content-type":"application/json",...y}};if(u){let e=u(i,q,j,C,A,v);q=e[0],j=e[1],C=e[2],A=e[3],v=e[4];}if(b){let e=b(i,q,j,C,A,v);q=e[0],j=e[1],C=e[2],A=e[3],v=e[4];}if(isFullObject(A))for(let e in A)q=q.replace(`{${e}}`,A[e].toString());let x=Number(g);C&&(v.body=JSON.stringify(C)),x>0&&($=new AbortController,m=setTimeout(()=>$.abort(),x),v.signal=$.signal),j&&(q+=buildUrlQueryString(j));let O=null,R=null,S="";try{O=await fetch(q,v);}catch(e){S=errorToString(e);}if(m&&clearTimeout(m),O)try{R=E?await E(O,h||{}):await O.json();}catch(e){S=errorToString(e);}if(null===R&&(R=N?await N(S,h||{}):{data:null,msg:S,status:100,fail:!0,ok:!1}),k&&R?.fail)throw R;if("development"===process.env.NODE_ENV){let e=`${R?.status}: api[${l}] ${i.toUpperCase()} ${q}`;R?.ok?console.info(`🟢 ${e}`):console.info(`🔴 ${e}`);}return R},o),{})};
3
+ let o=["get","post","put","patch","delete"];/**
4
+ * Create api client
5
+ *
6
+ * @param apiName Short name to use in debug logs
7
+ * @param baseUrl Either relativ eor absolute, it must end without trailing slash
8
+ */let createApi=(l,a,i)=>{let{headers:s={},request:n={},throwErr:p,timeout:c=1e4,processReq:u,processRes:f,processErr:d}=i||{};return o.reduce((o,i)=>(// @ts-expect-error FIXME: type
9
+ o[i]=async(o,h)=>{let $,m;let{request:w=n,headers:y=s,timeout:g=c,processReq:b,processRes:E=f,processErr:N=d,throwErr:k=p}=h||{},{params:A,json:C,query:j}=h||{},q=`${a}/${o+"".replace(/^\/*/,"")}`,v={method:i.toUpperCase(),...w,headers:{"content-type":"application/json",...y}};if(u){let e=u(i,q,j,C,A,v);q=e[0],j=e[1],C=e[2],A=e[3],v=e[4];}if(b){let e=b(i,q,j,C,A,v);q=e[0],j=e[1],C=e[2],A=e[3],v=e[4];}if(isFullObject(A))for(let e in A)q=q.replace(`{${e}}`,A[e].toString());let x=Number(g);C&&(v.body=JSON.stringify(C)),x>0&&(// TODO: combine multiple abort signals
10
+ // @see https://dev.to/rashidshamloo/adding-timeout-and-multiple-abort-signals-to-fetch-typescriptreact-33bb
11
+ $=new AbortController,m=setTimeout(()=>$.abort(),x),v.signal=$.signal),j&&// FIXME: ts-expect-error this assertion is not the best, but nevermind for now
12
+ (q+=buildUrlQueryString(j));let O=null,R=null,S="";try{O=await fetch(q,v);}catch(e){S=errorToString(e);}if(m&&clearTimeout(m),O)try{R=E?await E(O,h||{}):await O.json();}catch(e){S=errorToString(e);}if(null===R&&(R=N?await N(S,h||{}):// this error should only happen on network errors or wrong API urls
13
+ // there is no specific HTTP error for this, we can consider these
14
+ // two statuses though:
15
+ // - [100 Continue](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/100)
16
+ // - [501 Not Implemented](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/501)
17
+ {data:null,msg:S,status:100,fail:!0,ok:!1}),k&&R?.fail)// throw new ApiError<Failed>(result);
18
+ // I prefer to throw an object literal despite what eslint says
19
+ // eslint-disable-next-line no-throw-literal
20
+ throw R;if("development"===process.env.NODE_ENV){let e=`${R?.status}: api[${l}] ${i.toUpperCase()} ${q}`;R?.ok?console.info(`🟢 ${e}`):console.info(`🔴 ${e}`);}return R},o),{})};
4
21
 
5
22
  export { createApi, createApi as default };
@@ -8,7 +8,45 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'defau
8
8
 
9
9
  var e__default = /*#__PURE__*/_interopDefaultLegacy(e);
10
10
 
11
- let createUseApi=t=>(i,o,p)=>{let a=async()=>{let{data:e}=await t.get(i,{...o||{},throwErr:!0});return e};return e__default["default"](p?.when===!1||utils.isFunction(p?.when)&&p?.when()===!1?null:o?[i,o]:[i],a,p)};let createSwrApi=(...e)=>{let r=createApi.createApi(...e);return r.use=createUseApi(r),r};
11
+ /**
12
+ * Conditional fetching as option
13
+ *
14
+ * Moving this to an option allows us to keep the endpoints typed dictionary,
15
+ * e.g. we can write:
16
+ *
17
+ * ```js
18
+ * const { data, mutate } = myApi.use("User/{id}",
19
+ * { params: { id: aVariableMaybeContainingAnId || "" }, },
20
+ * { when: !!aVariableMaybeContainingAnId }
21
+ * );
22
+ *
23
+ * // we still have typed `data`, `mutate`
24
+ * ```
25
+ * @see https://swr.vercel.app/docs/conditional-fetching
26
+ *//**
27
+ * @private
28
+ *//**
29
+ * @private
30
+ */let createUseApi=t=>(i,o,p)=>{// const fetcher = async (_endpoint: TEndpoint) => {
31
+ // try {
32
+ // const { ok, data } = await api.get(_endpoint, {
33
+ // ...(options || {}),
34
+ // throwErr: true,
35
+ // });
36
+ // if (ok) {
37
+ // return data;
38
+ // }
39
+ // throw new Error() as unknown as Api.EndpointResponseFail<TEndpoints, TEndpoint, TMethod>;
40
+ // } catch(e) {
41
+ // throw new Error() as unknown as Api.EndpointResponseFail<TEndpoints, TEndpoint, TMethod>;;
42
+ // }
43
+ // };
44
+ // }
45
+ let a=async()=>{let{data:e}=await t.get(i,{...o||{},throwErr:!0});return e};// <Data = any, Error = any>(key: Key, config: SWRConfigurationExtended<Data, Error, Fetcher<Data>> | undefined): SWRResponse<Data, Error>;
46
+ // eslint-disable-next-line react-hooks/rules-of-hooks
47
+ return e__default["default"](p?.when===!1||utils.isFunction(p?.when)&&p?.when()===!1?null:o?[i,o]:[i],a,p)};/**
48
+ * It creates an api client extended with auto-generated SWR wrapper hooks
49
+ */let createSwrApi=(...e)=>{let r=createApi.createApi(...e);return r.use=createUseApi(r),r};
12
50
 
13
51
  exports.createSwrApi = createSwrApi;
14
52
  exports.createUseApi = createUseApi;
@@ -2,6 +2,44 @@ import e from 'swr';
2
2
  import { isFunction } from '@koine/utils';
3
3
  import { createApi } from './createApi.esm.js';
4
4
 
5
- let createUseApi=t=>(i,o,p)=>{let a=async()=>{let{data:e}=await t.get(i,{...o||{},throwErr:!0});return e};return e(p?.when===!1||isFunction(p?.when)&&p?.when()===!1?null:o?[i,o]:[i],a,p)};let createSwrApi=(...e)=>{let r=createApi(...e);return r.use=createUseApi(r),r};
5
+ /**
6
+ * Conditional fetching as option
7
+ *
8
+ * Moving this to an option allows us to keep the endpoints typed dictionary,
9
+ * e.g. we can write:
10
+ *
11
+ * ```js
12
+ * const { data, mutate } = myApi.use("User/{id}",
13
+ * { params: { id: aVariableMaybeContainingAnId || "" }, },
14
+ * { when: !!aVariableMaybeContainingAnId }
15
+ * );
16
+ *
17
+ * // we still have typed `data`, `mutate`
18
+ * ```
19
+ * @see https://swr.vercel.app/docs/conditional-fetching
20
+ *//**
21
+ * @private
22
+ *//**
23
+ * @private
24
+ */let createUseApi=t=>(i,o,p)=>{// const fetcher = async (_endpoint: TEndpoint) => {
25
+ // try {
26
+ // const { ok, data } = await api.get(_endpoint, {
27
+ // ...(options || {}),
28
+ // throwErr: true,
29
+ // });
30
+ // if (ok) {
31
+ // return data;
32
+ // }
33
+ // throw new Error() as unknown as Api.EndpointResponseFail<TEndpoints, TEndpoint, TMethod>;
34
+ // } catch(e) {
35
+ // throw new Error() as unknown as Api.EndpointResponseFail<TEndpoints, TEndpoint, TMethod>;;
36
+ // }
37
+ // };
38
+ // }
39
+ let a=async()=>{let{data:e}=await t.get(i,{...o||{},throwErr:!0});return e};// <Data = any, Error = any>(key: Key, config: SWRConfigurationExtended<Data, Error, Fetcher<Data>> | undefined): SWRResponse<Data, Error>;
40
+ // eslint-disable-next-line react-hooks/rules-of-hooks
41
+ return e(p?.when===!1||isFunction(p?.when)&&p?.when()===!1?null:o?[i,o]:[i],a,p)};/**
42
+ * It creates an api client extended with auto-generated SWR wrapper hooks
43
+ */let createSwrApi=(...e)=>{let r=createApi(...e);return r.use=createUseApi(r),r};
6
44
 
7
45
  export { createUseApi as a, createSwrApi as c };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@koine/api",
3
3
  "sideEffects": false,
4
4
  "dependencies": {
5
- "@koine/utils": "2.0.0-beta.82"
5
+ "@koine/utils": "2.0.0-beta.83"
6
6
  },
7
7
  "peerDependencies": {
8
8
  "next": "^14.0.4",
@@ -61,5 +61,5 @@
61
61
  },
62
62
  "module": "./index.esm.js",
63
63
  "main": "./index.cjs.js",
64
- "version": "2.0.0-beta.82"
64
+ "version": "2.0.0-beta.83"
65
65
  }
@@ -1,5 +1,11 @@
1
+ import { type BareFetcher, type SWRConfiguration, type SWRResponse } from "swr";
1
2
  import type { Api } from "../types";
3
+ type SWRConfigurationExtended<Data = any, Error = any, Fn extends BareFetcher<any> = BareFetcher<any>> = SWRConfiguration<Data, Error, Fn> & {
4
+ when?: boolean | (() => boolean);
5
+ };
6
+ export type CreatedUseApi<TEndpoints extends Api.Endpoints> = ReturnType<typeof createUseApi<TEndpoints>>;
7
+ export declare let createUseApi: <TEndpoints extends Api.Endpoints>(api: Api.Client<TEndpoints>) => <TEndpoint extends Extract<keyof TEndpoints, string>>(endpoint: TEndpoint, options?: Api.EndpointOptions<TEndpoints, TEndpoint, "get">, config?: SWRConfigurationExtended<Api.EndpointResponseOk<TEndpoints, TEndpoint, "get">, Api.EndpointResponseFail<TEndpoints, TEndpoint, "get">>) => SWRResponse<Api.GetDataType<TEndpoints, TEndpoint, "get", "ok">, Api.GetDataType<TEndpoints, TEndpoint, "get", "fail">, any>;
2
8
  export declare let createSwrApi: <TEndpoints extends Api.Endpoints>(apiName: string, baseUrl: string, options?: Api.ClientOptions | undefined) => Api.Client<TEndpoints> & {
3
- use: ReturnType<typeof createUseApi<TEndpoints>>;
9
+ use: CreatedUseApi<TEndpoints>;
4
10
  };
5
11
  export default createSwrApi;
@@ -1,5 +1,5 @@
1
1
  import { type SWRMutationConfiguration, type SWRMutationResponse } from "swr/mutation";
2
- import { createUseApi } from "../swr/createSwrApi";
2
+ import { type CreatedUseApi } from "../swr/createSwrApi";
3
3
  import type { Api } from "../types";
4
4
  type MutationRequestMethod = Exclude<Api.RequestMethod, "get">;
5
5
  type MutationHookName = Exclude<keyof Api.HooksMapsByName, "use">;
@@ -10,6 +10,6 @@ export declare let createSwrMutationApi: <TEndpoints extends Api.Endpoints>(apiN
10
10
  usePatch: KoineApiMethodHookSWR<"usePatch", TEndpoints>;
11
11
  useDelete: KoineApiMethodHookSWR<"useDelete", TEndpoints>;
12
12
  } & {
13
- use: ReturnType<typeof createUseApi<TEndpoints>>;
13
+ use: CreatedUseApi<TEndpoints>;
14
14
  };
15
15
  export default createSwrMutationApi;
@@ -12,6 +12,15 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'defau
12
12
 
13
13
  var r__default = /*#__PURE__*/_interopDefaultLegacy(r);
14
14
 
15
- let a=(t,e)=>(a,o,i)=>r__default["default"](o?[a,o]:a,async(r,a)=>{let o=Array.isArray(r)?r[0]:r,i=Array.isArray(r)?r[1]:{},{ok:p,data:s}=await t[e](o,{...i,...a.arg||{},throwErr:!0});return s},i);let createSwrMutationApi=(...r)=>{let o=createApi.createApi(...r);return o.use=createSwrApi.createUseApi(o),["post","put","patch","delete"].forEach(r=>{o[`use${r.charAt(0).toUpperCase()+r.slice(1)}`]=a(o,r);}),o};
15
+ let a=(t,e)=>(a,o,i)=>// config.fetcher = sender;
16
+ // eslint-disable-next-line react-hooks/rules-of-hooks
17
+ r__default["default"](// @ts-expect-error FIXME: I can't get it...
18
+ o?[a,o]:a,async(// if the first argument is an array the second one are the base options
19
+ // defined when calling the usePost/Put/etc. hook, these will be overriden
20
+ // by the _options just here below
21
+ r,// these are the options arriving when calling `trigger({ json, query, etc... })
22
+ a)=>{let o=Array.isArray(r)?r[0]:r,i=Array.isArray(r)?r[1]:{},{ok:p,data:s}=await t[e](o,{...i,...a.arg||{},throwErr:!0});return s},i);/**
23
+ * It creates an api client extended with auto-generated SWR wrapper hooks
24
+ */let createSwrMutationApi=(...r)=>{let o=createApi.createApi(...r);return o.use=createSwrApi.createUseApi(o),["post","put","patch","delete"].forEach(r=>{o[`use${r.charAt(0).toUpperCase()+r.slice(1)}`]=a(o,r);}),o};
16
25
 
17
26
  exports.createSwrMutationApi = createSwrMutationApi;
@@ -4,6 +4,15 @@ import { a as createUseApi } from './createSwrApi.esm.js';
4
4
  import '@koine/utils';
5
5
  import 'swr';
6
6
 
7
- let a=(t,e)=>(a,o,i)=>r(o?[a,o]:a,async(r,a)=>{let o=Array.isArray(r)?r[0]:r,i=Array.isArray(r)?r[1]:{},{ok:p,data:s}=await t[e](o,{...i,...a.arg||{},throwErr:!0});return s},i);let createSwrMutationApi=(...r)=>{let o=createApi(...r);return o.use=createUseApi(o),["post","put","patch","delete"].forEach(r=>{o[`use${r.charAt(0).toUpperCase()+r.slice(1)}`]=a(o,r);}),o};
7
+ let a=(t,e)=>(a,o,i)=>// config.fetcher = sender;
8
+ // eslint-disable-next-line react-hooks/rules-of-hooks
9
+ r(// @ts-expect-error FIXME: I can't get it...
10
+ o?[a,o]:a,async(// if the first argument is an array the second one are the base options
11
+ // defined when calling the usePost/Put/etc. hook, these will be overriden
12
+ // by the _options just here below
13
+ r,// these are the options arriving when calling `trigger({ json, query, etc... })
14
+ a)=>{let o=Array.isArray(r)?r[0]:r,i=Array.isArray(r)?r[1]:{},{ok:p,data:s}=await t[e](o,{...i,...a.arg||{},throwErr:!0});return s},i);/**
15
+ * It creates an api client extended with auto-generated SWR wrapper hooks
16
+ */let createSwrMutationApi=(...r)=>{let o=createApi(...r);return o.use=createUseApi(o),["post","put","patch","delete"].forEach(r=>{o[`use${r.charAt(0).toUpperCase()+r.slice(1)}`]=a(o,r);}),o};
8
17
 
9
18
  export { createSwrMutationApi };