@tasoskakour/react-use-oauth2 1.3.3 → 2.0.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/README.md CHANGED
@@ -41,8 +41,10 @@ const Home = () => {
41
41
  redirectUri: `${document.location.origin}/callback`,
42
42
  scope: "YOUR_SCOPES",
43
43
  responseType: "code",
44
- exchangeCodeForTokenServerURL: "https://your-backend/token",
45
- exchangeCodeForTokenMethod: "POST",
44
+ exchangeCodeForTokenQuery: {
45
+ url: "https://your-backend/token",
46
+ method: "POST",
47
+ },
46
48
  onSuccess: (payload) => console.log("Success", payload),
47
49
  onError: (error_) => console.log("Error", error_)
48
50
  });
@@ -85,17 +87,52 @@ const App = () => {
85
87
  };
86
88
  ```
87
89
 
88
- ### What is the purpose of `exchangeCodeForTokenServerURL` for Authorization Code flows?
90
+ ##### Example with `exchangeCodeForTokenQueryFn`
91
+
92
+ You can also use `exchangeCodeForTokenQueryFn` if you want full control over your query to your backend, e.g if you must send your data as form-urlencoded:
93
+ ```js
94
+
95
+ const { ... } = useOAuth2({
96
+ // ...
97
+ // Instead of exchangeCodeForTokenQuery (e.g sending form-urlencoded or similar)...
98
+ exchangeCodeForTokenQueryFn: async (callbackParameters) => {
99
+ const formBody = [];
100
+ for (const key in callbackParameters) {
101
+ formBody.push(
102
+ `${encodeURIComponent(key)}=${encodeURIComponent(callbackParameters[key])}`
103
+ );
104
+ }
105
+ const response = await fetch(`YOUR_BACKEND_URL`, {
106
+ method: 'POST',
107
+ body: formBody.join('&'),
108
+ headers: {
109
+ 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
110
+ },
111
+ });
112
+ if (!response.ok) throw new Error('Failed');
113
+ const tokenData = await response.json();
114
+ return tokenData;
115
+ },.
116
+ // ...
117
+ })
118
+
119
+ ```
120
+
121
+ ### What is the purpose of `exchangeCodeForTokenQuery` for Authorization Code flows?
89
122
 
90
123
  Generally when we're working with authorization code flows, we need to *immediately* **exchange** the retrieved *code* with an actual *access token*, after a successful authorization. Most of the times this is needed for back-end apps, but there are many use cases this is useful for front-end apps as well.
91
124
 
92
125
  In order for the flow to be accomplished, the 3rd party provider we're authorizing against (e.g Google, Facebook etc), will provide an API call (e.g for Google is `https://oauth2.googleapis.com/token`) that we need to hit in order to exchange the code for an access token. However, this call requires the `client_secret` of your 3rd party app as a parameter to work - a secret that you cannot expose to your front-end app.
93
126
 
94
- That's why you need to proxy this call to your back-end and `exchangeCodeForTokenServerURL` is the API URL of your back-end route that will take care of this. The request parameters that will get passed along as **query parameters** are `{ code, client_id, grant_type, redirect_uri, state }`. By default this will be a **POST** request but you can change it with the `exchangeCodeForTokenMethod` property.
127
+ That's why you need to proxy this call to your back-end and with `exchangeCodeForTokenQuery` object you can provide the schematics of your call e.g `url`, `method` etc. The request parameters that will get passed along as **query parameters** are `{ code, client_id, grant_type, redirect_uri, state }`. By default this will be a **POST** request but you can change it with the `method` property.
95
128
 
96
129
 
97
130
  You can read more about "Exchanging authorization code for refresh and access tokens" in [Google OAuth2 documentation](https://developers.google.com/identity/protocols/oauth2/web-server#exchange-authorization-code).
98
131
 
132
+ ### What's the alternative option `exchangeCodeForTokenQueryFn`?
133
+
134
+ There could be certain cases where `exchangeCodeForTokenQuery` is not enough and you want full control over how you send the request to your backend. For example you may want to send it as a urlencoded form. With this property you can define your callback function which takes `callbackParameters: object` as a parameter (which includes whatever returned from OAuth2 callback e.g `code, scope, state` etc) and must return a promise with a valid object which will contain all the token data state e.g `access_token, expires_in` etc.
135
+
99
136
  ### What's the case with Implicit Grant flows?
100
137
 
101
138
  With an implicit grant flow things are much simpler as the 3rd-party provider immediately returns the `access_token` to the callback request so there's no need to make any action after that. Just set `responseType=token` to use this flow.
@@ -106,7 +143,7 @@ After a successful authorization, data will get persisted to **localStorage** an
106
143
 
107
144
  If you want to re-trigger the authorization flow just call `getAuth()` function again.
108
145
 
109
- **Note**: In case localStorage is throwing an error (e.g user has disabled it) then you can use the `isPersistent` property which - for this case -will be false. Useful if you want to notify the user that the data is only stored in-memory.
146
+ **Note**: In case localStorage is throwing an error (e.g user has disabled it) then you can use the `isPersistent` property which - for this case - will be false. Useful if you want to notify the user that the data is only stored in-memory.
110
147
 
111
148
  ## API
112
149
 
@@ -114,26 +151,28 @@ If you want to re-trigger the authorization flow just call `getAuth()` function
114
151
 
115
152
  This is the hook that makes this package to work. `Options` is an object that contains the properties below
116
153
 
117
- - **authorizeUrl** (string): The 3rd party authorization URL (e.g https://accounts.google.com/o/oauth2/v2/auth).
118
- - **clientId** (string): The OAuth2 client id of your application.
119
- - **redirectUri** (string): Determines where the 3rd party API server redirects the user after the user completes the authorization flow. In our [example](#usage-example) the Popup is rendered on that redirectUri.
120
- - **scope** (string - _optional_): A list of scopes depending on your application needs.
121
- - **responseType** (string): Can be either **code** for _code authorization grant_ or **token** for _implicit grant_.
122
- - **extraQueryParameters** (object - _optional_): An object of extra parameters that you'd like to pass to the query part of the authorizeUrl, e.g {audience: "xyz"}.
123
- - **exchangeCodeForTokenServerURL** (string): This property is only used when using _code authorization grant_ flow (responseType = code). It specifies the API URL of your server that will get called immediately after the user completes the authorization flow. Read more [here](#what-is-the-purpose-of-exchangecodefortokenserverurl-for-authorization-code-flows).
124
- - **exchangeCodeForTokenMethod** (string - _optional_): Specifies the HTTP method that will be used for the code-for-token exchange to your server. Defaults to **POST**.
125
- - **exchangeCodeForTokenHeaders** (object - _optional_): An object of extra parameters that will be used for the code-for-token exchange to your server.
154
+ - `authorizeUrl` (string): The 3rd party authorization URL (e.g https://accounts.google.com/o/oauth2/v2/auth).
155
+ - `clientId` (string): The OAuth2 client id of your application.
156
+ - `redirectUri` (string): Determines where the 3rd party API server redirects the user after the user completes the authorization flow. In our [example](#usage-example) the Popup is rendered on that redirectUri.
157
+ - `scope` (string - _optional_): A list of scopes depending on your application needs.
158
+ - `responseType` (string): Can be either **code** for _code authorization grant_ or **token** for _implicit grant_.
159
+ - `extraQueryParameters` (object - _optional_): An object of extra parameters that you'd like to pass to the query part of the authorizeUrl, e.g {audience: "xyz"}.
160
+ - `exchangeCodeForTokenQuery` (object): This property is only required when using _code authorization grant_ flow (responseType = code). It's properties are:
161
+ - `url` (string - _required_) It specifies the API URL of your server that will get called immediately after the user completes the authorization flow. Read more [here](#what-is-the-purpose-of-exchangecodefortokenserverurl-for-authorization-code-flows).
162
+ - `method` (string - _required_): Specifies the HTTP method that will be used for the code-for-token exchange to your server. Defaults to **POST**
163
+ - `headers` (object - _optional_): An object of extra parameters that will be used for the code-for-token exchange to your server.
164
+ - `exchangeCodeForTokenQueryFn` function(callbackParameters) => Promise\<Object\>: **Instead of using** `exchangeCodeForTokenQuery` to describe the query, you can take full control and provide query function yourself. `callbackParameters` will contain everything returned from the OAUth2 callback e.g `code, state` etc. You must return a promise with a valid object that will represent your final state - data of the auth procedure.
126
165
  - **onSuccess** (function): Called after a complete successful authorization flow.
127
166
  - **onError** (function): Called when an error occurs.
128
167
 
129
168
  **Returns**:
130
169
 
131
- - **data** (object): Consists of the retrieved auth data and generally will have the shape of `{access_token, token_type, expires_in}` (check [Typescript](#typescript) usage for providing custom shape).
132
- - **loading** (boolean): Is set to true while the authorization is taking place.
133
- - **error** (string): Is set when an error occurs.
134
- - **getAuth** (function): Call this function to trigger the authorization flow.
135
- - **logout** (function): Call this function to logout and clear all authorization data.
136
- - **isPersistent** (boolean): Property that returns false if localStorage is throwing an error and the data is stored only in-memory. Useful if you want to notify the user.
170
+ - `data` (object): Consists of the retrieved auth data and generally will have the shape of `{access_token, token_type, expires_in}` (check [Typescript](#typescript) usage for providing custom shape). If you're using `responseType: code` and `exchangeCodeForTokenQueryFn` this object will contain whatever you returnn from your query function.
171
+ - `loading` (boolean): Is set to true while the authorization is taking place.
172
+ - `error` (string): Is set when an error occurs.
173
+ - `getAuth` (function): Call this function to trigger the authorization flow.
174
+ - `logout` (function): Call this function to logout and clear all authorization data.
175
+ - `isPersistent` (boolean): Property that returns false if localStorage is throwing an error and the data is stored only in-memory. Useful if you want to notify the user.
137
176
 
138
177
  ---
139
178
 
@@ -143,7 +182,7 @@ This is the component that will be rendered as a window Popup for as long as the
143
182
 
144
183
  Props consists of:
145
184
 
146
- - **Component** (ReactElement - _optional_): You can optionally set a custom component to be rendered inside the Popup. By default it just displays a "Loading..." message.
185
+ - `Component` (ReactElement - _optional_): You can optionally set a custom component to be rendered inside the Popup. By default it just displays a "Loading..." message.
147
186
 
148
187
  ### Typescript
149
188
 
@@ -178,6 +217,14 @@ type MyCustomShapeData = {
178
217
  const {data, ...} = useOAuth2<MyCustomShapeData>({...});
179
218
  ```
180
219
 
220
+ ### Migrating to v2.0.0 (2024-03-05)
221
+
222
+ Please follow the steps below to migrate to `v2.0.0`:
223
+
224
+ - **DEPRECATED properties**: `exchangeCodeForTokenServerURL`, `exchangeCodeForTokenMethod`, `exchangeCodeForTokenHeaders`
225
+ - **INTRODUCED NEW PROPERTY**: `exchangeCodeForTokenQuery`
226
+ - `exchangeCodeForTokenQuery` just combines all the above deprecated properties, e.g you can use it like: `exchangeCodeForTokenQuery: { url:"...", method:"POST", headers:{} }`
227
+
181
228
  ### Tests
182
229
 
183
230
  You can run tests by calling
package/dist/cjs/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var e=require("react/jsx-runtime"),r=require("react"),t=function(){return t=Object.assign||function(e){for(var r,t=1,n=arguments.length;t<n;t++)for(var o in r=arguments[t])Object.prototype.hasOwnProperty.call(r,o)&&(e[o]=r[o]);return e},t.apply(this,arguments)};function n(e,r,t,n){return new(t||(t=Promise))((function(o,a){function i(e){try{u(n.next(e))}catch(e){a(e)}}function c(e){try{u(n.throw(e))}catch(e){a(e)}}function u(e){var r;e.done?o(e.value):(r=e.value,r instanceof t?r:new t((function(e){e(r)}))).then(i,c)}u((n=n.apply(e,r||[])).next())}))}function o(e,r){var t,n,o,a,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return a={next:c(0),throw:c(1),return:c(2)},"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function c(c){return function(u){return function(c){if(t)throw new TypeError("Generator is already executing.");for(;a&&(a=0,c[0]&&(i=0)),i;)try{if(t=1,n&&(o=2&c[0]?n.return:c[0]?n.throw||((o=n.return)&&o.call(n),0):n.next)&&!(o=o.call(n,c[1])).done)return o;switch(n=0,o&&(c=[2&c[0],o.value]),c[0]){case 0:case 1:o=c;break;case 4:return i.label++,{value:c[1],done:!1};case 5:i.label++,n=c[1],c=[0];continue;case 7:c=i.ops.pop(),i.trys.pop();continue;default:if(!(o=i.trys,(o=o.length>0&&o[o.length-1])||6!==c[0]&&2!==c[0])){i=0;continue}if(3===c[0]&&(!o||c[1]>o[0]&&c[1]<o[3])){i.label=c[1];break}if(6===c[0]&&i.label<o[1]){i.label=o[1],o=c;break}if(o&&i.label<o[2]){i.label=o[2],i.ops.push(c);break}o[2]&&i.ops.pop(),i.trys.pop();continue}c=r.call(e,i)}catch(e){c=[6,e],n=0}finally{t=o=0}if(5&c[0])throw c[1];return{value:c[0]?c[1]:void 0,done:!0}}([c,u])}}}var a="react-use-oauth2-state-key",i="react-use-oauth2-response",c=function(e){return new URLSearchParams(e).toString()},u=function(e){var r=new URLSearchParams(e);return Object.fromEntries(r.entries())},s=function(e,r){return e.postMessage(r)},l=function(e,r,t){clearInterval(e.current),r.current&&"function"==typeof r.current.close&&function(e){var r;null===(r=e.current)||void 0===r||r.close()}(r),sessionStorage.removeItem(a),window.removeEventListener("message",t)},d=function(e,r,n,o,a){var i=e.split("?")[0],s=u(e.split("?")[1]);return"".concat(i,"?").concat(c(t(t({},s),{client_id:r,grant_type:"authorization_code",code:n,redirect_uri:o,state:a})))};const f=new Map;function p(e,t){if(void 0===r.useSyncExternalStore)throw new TypeError('You are using React 17 or below. Install with "npm install use-local-storage-state@17".');const[n]=r.useState(null==t?void 0:t.defaultValue);if("undefined"==typeof window)return[n,()=>{},{isPersistent:!0,removeItem:()=>{}}];const o=null==t?void 0:t.serializer;return function(e,t,n=!0,o=w,a=JSON.stringify){f.has(e)||void 0===t||null!==g((()=>localStorage.getItem(e)))||g((()=>localStorage.setItem(e,a(t))));const i=r.useRef({item:null,parsed:t}),c=r.useSyncExternalStore(r.useCallback((r=>{const t=t=>{e===t&&r()};return v.add(t),()=>{v.delete(t)}}),[e]),(()=>{var r;const n=null!==(r=g((()=>localStorage.getItem(e))))&&void 0!==r?r:null;if(f.has(e))i.current={item:n,parsed:f.get(e)};else if(n!==i.current.item){let e;try{e=null===n?t:o(n)}catch(r){e=t}i.current={item:n,parsed:e}}return i.current.parsed}),(()=>t)),u=r.useCallback((r=>{const t=r instanceof Function?r(i.current.parsed):r;try{localStorage.setItem(e,a(t)),f.delete(e)}catch(r){f.set(e,t)}h(e)}),[e,a]);return r.useEffect((()=>{if(!n)return;const r=r=>{r.storageArea===g((()=>localStorage))&&r.key===e&&h(e)};return window.addEventListener("storage",r),()=>window.removeEventListener("storage",r)}),[e,n]),r.useMemo((()=>[c,u,{isPersistent:c===t||!f.has(e),removeItem(){g((()=>localStorage.removeItem(e))),f.delete(e),h(e)}}]),[e,u,c,t])}(e,n,null==t?void 0:t.storageSync,null==o?void 0:o.parse,null==o?void 0:o.stringify)}const v=new Set;function h(e){for(const r of[...v])r(e)}function w(e){return"undefined"===e?void 0:JSON.parse(e)}function g(e){try{return e()}catch(e){return}}exports.OAuthPopup=function(n){var o=n.Component,c=void 0===o?e.jsx("div",t({style:{margin:"12px"},"data-testid":"popup-loading"},{children:"Loading..."})):o;return r.useEffect((function(){var e=t(t({},u(window.location.search.split("?")[1])),u(window.location.hash.split("#")[1])),r=null==e?void 0:e.state,n=null==e?void 0:e.error,o=null===window||void 0===window?void 0:window.opener;if(!function(e){return null!=e}(o))throw new Error("No window opener");var c,l,d=r&&(c=o.sessionStorage,l=r,c.getItem(a)===l);if(!n&&d)s(o,{type:i,payload:e});else{var f=n?decodeURI(n):d?"OAuth error: An error has occured.":"OAuth error: State mismatch.";s(o,{type:i,error:f})}}),[]),c},exports.useOAuth2=function(e){var u=e.authorizeUrl,s=e.clientId,f=e.redirectUri,v=e.scope,h=void 0===v?"":v,w=e.responseType,g=e.extraQueryParameters,y=void 0===g?{}:g,m=e.onSuccess,S=e.onError;!function(e){var r=e.authorizeUrl,t=e.clientId,n=e.redirectUri,o=e.responseType,a=e.extraQueryParameters,i=void 0===a?{}:a,c=e.onSuccess,u=e.onError;if(!(r&&t&&n&&o))throw new Error("Missing required props for useOAuth2. Required props are: {authorizeUrl, clientId, redirectUri, responseType}");if("code"===o&&!e.exchangeCodeForTokenServerURL)throw new Error('exchangeCodeForTokenServerURL is required for responseType of "code" for useOAuth2.');if("code"===o&&e.exchangeCodeForTokenMethod&&!["POST","GET"].includes(e.exchangeCodeForTokenMethod))throw new Error('Invalid exchangeCodeForTokenServerURL value. It can be one of "POST" or "GET".');if("object"!=typeof i)throw new TypeError("extraQueryParameters must be an object for useOAuth2.");if(c&&"function"!=typeof c)throw new TypeError("onSuccess callback must be a function for useOAuth2.");if(u&&"function"!=typeof u)throw new TypeError("onError callback must be a function for useOAuth2.")}(e);var b=r.useRef(y),E=r.useRef(),x=r.useRef(),T=r.useState({loading:!1,error:null}),k=T[0],I=k.loading,O=k.error,P=T[1],A=p("".concat(w,"-").concat(u,"-").concat(s,"-").concat(h),{defaultValue:null}),C=A[0],U=A[1],R=A[2],L=R.removeItem,F=R.isPersistent,j="code"===w&&e.exchangeCodeForTokenServerURL,M="code"===w&&e.exchangeCodeForTokenMethod,_="code"===w&&e.exchangeCodeForTokenHeaders,q=r.useCallback((function(){P({loading:!0,error:null});var e,r,p,v,g,y=(e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",r=new Uint8Array(40),window.crypto.getRandomValues(r),r=r.map((function(r){return e.codePointAt(r%e.length)})),String.fromCharCode.apply(null,r));function T(e){var r,t,a;return n(this,void 0,void 0,(function(){var n,c,u;return o(this,(function(o){switch(o.label){case 0:if((null===(r=null==e?void 0:e.data)||void 0===r?void 0:r.type)!==i)return[2];o.label=1;case 1:return o.trys.push([1,10,11,12]),"error"in e.data?(n=(null===(t=e.data)||void 0===t?void 0:t.error)||"Unknown Error occured.",P({loading:!1,error:n}),S?[4,S(n)]:[3,3]):[3,4];case 2:o.sent(),o.label=3;case 3:return[3,9];case 4:return c=null===(a=null==e?void 0:e.data)||void 0===a?void 0:a.payload,"code"===w&&j?[4,fetch(d(j,s,null==c?void 0:c.code,f,y),{method:M||"POST",headers:_||{}})]:[3,7];case 5:return[4,o.sent().json()];case 6:c=o.sent(),o.label=7;case 7:return P({loading:!1,error:null}),U(c),m?[4,m(c)]:[3,9];case 8:o.sent(),o.label=9;case 9:return[3,12];case 10:return u=o.sent(),console.error(u),P({loading:!1,error:u.toString()}),[3,12];case 11:return l(x,E,T),[7];case 12:return[2]}}))}))}return function(e,r){e.setItem(a,r)}(sessionStorage,y),E.current=(p=function(e,r,n,o,a,i,u){void 0===u&&(u={});var s=c(t({response_type:i,client_id:r,redirect_uri:n,scope:o,state:a},u));return"".concat(e,"?").concat(s)}(u,s,f,h,y,w,b.current),v=window.outerHeight/2+window.screenY-350,g=window.outerWidth/2+window.screenX-300,window.open(p,"OAuth2 Popup","height=".concat(700,",width=").concat(600,",top=").concat(v,",left=").concat(g))),window.addEventListener("message",T),x.current=setInterval((function(){var e,r,n;(!(null===(e=E.current)||void 0===e?void 0:e.window)||(null===(n=null===(r=E.current)||void 0===r?void 0:r.window)||void 0===n?void 0:n.closed))&&(P((function(e){return t(t({},e),{loading:!1})})),console.warn("Warning: Popup was closed before completing authentication."),l(x,E,T))}),250),function(){window.removeEventListener("message",T),x.current&&clearInterval(x.current)}}),[u,s,f,h,w,j,M,_,m,S,P,U]);return{data:C,loading:I,error:O,getAuth:q,logout:r.useCallback((function(){L(),P({loading:!1,error:null})}),[L]),isPersistent:F}};
1
+ "use strict";var e=require("react/jsx-runtime"),r=require("react"),n=function(){return n=Object.assign||function(e){for(var r,n=1,t=arguments.length;n<t;n++)for(var o in r=arguments[n])Object.prototype.hasOwnProperty.call(r,o)&&(e[o]=r[o]);return e},n.apply(this,arguments)};function t(e,r,n,t){return new(n||(n=Promise))((function(o,a){function u(e){try{c(t.next(e))}catch(e){a(e)}}function i(e){try{c(t.throw(e))}catch(e){a(e)}}function c(e){var r;e.done?o(e.value):(r=e.value,r instanceof n?r:new n((function(e){e(r)}))).then(u,i)}c((t=t.apply(e,r||[])).next())}))}function o(e,r){var n,t,o,a,u={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return a={next:i(0),throw:i(1),return:i(2)},"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function i(i){return function(c){return function(i){if(n)throw new TypeError("Generator is already executing.");for(;a&&(a=0,i[0]&&(u=0)),u;)try{if(n=1,t&&(o=2&i[0]?t.return:i[0]?t.throw||((o=t.return)&&o.call(t),0):t.next)&&!(o=o.call(t,i[1])).done)return o;switch(t=0,o&&(i=[2&i[0],o.value]),i[0]){case 0:case 1:o=i;break;case 4:return u.label++,{value:i[1],done:!1};case 5:u.label++,t=i[1],i=[0];continue;case 7:i=u.ops.pop(),u.trys.pop();continue;default:if(!(o=u.trys,(o=o.length>0&&o[o.length-1])||6!==i[0]&&2!==i[0])){u=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]<o[3])){u.label=i[1];break}if(6===i[0]&&u.label<o[1]){u.label=o[1],o=i;break}if(o&&u.label<o[2]){u.label=o[2],u.ops.push(i);break}o[2]&&u.ops.pop(),u.trys.pop();continue}i=r.call(e,u)}catch(e){i=[6,e],t=0}finally{n=o=0}if(5&i[0])throw i[1];return{value:i[0]?i[1]:void 0,done:!0}}([i,c])}}}"function"==typeof SuppressedError&&SuppressedError;var a="react-use-oauth2-state-key",u="react-use-oauth2-response",i=["GET","POST","PUT","PATCH"],c=function(e){return new URLSearchParams(e).toString()},s=function(e){var r=new URLSearchParams(e);return Object.fromEntries(r.entries())},l=function(e,r){return e.postMessage(r)},d=function(e,r,n){clearInterval(e.current),r.current&&"function"==typeof r.current.close&&function(e){var r;null===(r=e.current)||void 0===r||r.close()}(r),sessionStorage.removeItem(a),window.removeEventListener("message",n)},f=function(e,r,t,o,a){var u=e.split("?")[0],i=s(e.split("?")[1]);return"".concat(u,"?").concat(c(n(n({},i),{client_id:r,grant_type:"authorization_code",code:t,redirect_uri:o,state:a})))},p=!1;const h=new Map;function v(e,n){if(void 0===r.useSyncExternalStore)throw new TypeError('You are using React 17 or below. Install with "npm install use-local-storage-state@17".');const[t]=r.useState(null==n?void 0:n.defaultValue);if("undefined"==typeof window)return[t,()=>{},{isPersistent:!0,removeItem:()=>{}}];const o=null==n?void 0:n.serializer;return function(e,n,t=!0,o=g,a=JSON.stringify){h.has(e)||void 0===n||null!==m((()=>localStorage.getItem(e)))||m((()=>localStorage.setItem(e,a(n))));const u=r.useRef({item:null,parsed:n}),i=r.useSyncExternalStore(r.useCallback((r=>{const n=n=>{e===n&&r()};return w.add(n),()=>{w.delete(n)}}),[e]),(()=>{var r;const t=null!==(r=m((()=>localStorage.getItem(e))))&&void 0!==r?r:null;if(h.has(e))u.current={item:t,parsed:h.get(e)};else if(t!==u.current.item){let e;try{e=null===t?n:o(t)}catch(r){e=n}u.current={item:t,parsed:e}}return u.current.parsed}),(()=>n)),c=r.useCallback((r=>{const n=r instanceof Function?r(u.current.parsed):r;try{localStorage.setItem(e,a(n)),h.delete(e)}catch(r){h.set(e,n)}y(e)}),[e,a]);return r.useEffect((()=>{if(!t)return;const r=r=>{r.storageArea===m((()=>localStorage))&&r.key===e&&y(e)};return window.addEventListener("storage",r),()=>window.removeEventListener("storage",r)}),[e,t]),r.useMemo((()=>[i,c,{isPersistent:i===n||!h.has(e),removeItem(){m((()=>localStorage.removeItem(e))),h.delete(e),y(e)}}]),[e,c,i,n])}(e,t,null==n?void 0:n.storageSync,null==o?void 0:o.parse,null==o?void 0:o.stringify)}const w=new Set;function y(e){for(const r of[...w])r(e)}function g(e){return"undefined"===e?void 0:JSON.parse(e)}function m(e){try{return e()}catch(e){return}}exports.OAuthPopup=function(t){var o=t.Component,i=void 0===o?e.jsx("div",{style:{margin:"12px"},"data-testid":"popup-loading",children:"Loading..."}):o;return r.useEffect((function(){if(!p){p=!0;var e=n(n({},s(window.location.search.split("?")[1])),s(window.location.hash.split("#")[1])),r=null==e?void 0:e.state,t=null==e?void 0:e.error,o=null===window||void 0===window?void 0:window.opener;if(!function(e){return null!=e}(o))throw new Error("No window opener");var i,c,d=r&&(i=o.sessionStorage,c=r,i.getItem(a)===c);if(!t&&d)l(o,{type:u,payload:e});else{var f=t?decodeURI(t):"".concat(d?"OAuth error: An error has occured.":"OAuth error: State mismatch.");l(o,{type:u,error:f})}}}),[]),i},exports.useOAuth2=function(e){var s=e.authorizeUrl,l=e.clientId,p=e.redirectUri,h=e.scope,w=void 0===h?"":h,y=e.responseType,g=e.extraQueryParameters,m=void 0===g?{}:g,b=e.onSuccess,S=e.onError;!function(e){var r=e.authorizeUrl,n=e.clientId,t=e.redirectUri,o=e.responseType,a=e.extraQueryParameters,u=void 0===a?{}:a,c=e.onSuccess,s=e.onError;if(!(r&&n&&t&&o))throw new Error("Missing required props for useOAuth2. Required props are: {authorizeUrl, clientId, redirectUri, responseType}");if("code"===o&&!e.exchangeCodeForTokenQuery&&!e.exchangeCodeForTokenQueryFn)throw new Error('Either `exchangeCodeForTokenQuery` or `exchangeCodeForTokenQueryFn` is required for responseType of "code" for useOAuth2.');if("code"===o&&e.exchangeCodeForTokenQuery&&!e.exchangeCodeForTokenQuery.url)throw new Error("Value `exchangeCodeForTokenQuery.url` is missing.");if("code"===o&&e.exchangeCodeForTokenQuery&&!["GET","POST","PUT","PATCH"].includes(e.exchangeCodeForTokenQuery.method))throw new Error("Invalid `exchangeCodeForTokenQuery.method` value. It can be one of ".concat(i.join(", "),"."));if("object"!=typeof u)throw new TypeError("extraQueryParameters must be an object for useOAuth2.");if(c&&"function"!=typeof c)throw new TypeError("onSuccess callback must be a function for useOAuth2.");if(s&&"function"!=typeof s)throw new TypeError("onError callback must be a function for useOAuth2.")}(e);var T=r.useRef(m),E=r.useRef(),x=r.useRef(),k=r.useRef("code"===y&&e.exchangeCodeForTokenQuery),C=r.useRef("code"===y&&e.exchangeCodeForTokenQueryFn),P=r.useState({loading:!1,error:null}),I=P[0],O=I.loading,F=I.error,A=P[1],Q=v("".concat(y,"-").concat(s,"-").concat(l,"-").concat(w),{defaultValue:null}),U=Q[0],R=Q[1],j=Q[2],L=j.removeItem,_=j.isPersistent,q=r.useCallback((function(){A({loading:!0,error:null});var e,r,i,h,v,g=(e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",r=new Uint8Array(40),window.crypto.getRandomValues(r),r=r.map((function(r){return e.codePointAt(r%62)})),String.fromCharCode.apply(null,r));function m(e){var r,n,a,i,c;return t(this,void 0,void 0,(function(){var t,s,h,v,w;return o(this,(function(o){switch(o.label){case 0:if((null===(r=null==e?void 0:e.data)||void 0===r?void 0:r.type)!==u)return[2];o.label=1;case 1:return o.trys.push([1,13,14,15]),"error"in e.data?(t=(null===(n=e.data)||void 0===n?void 0:n.error)||"Unknown Error occured.",A({loading:!1,error:t}),S?[4,S(t)]:[3,3]):[3,4];case 2:o.sent(),o.label=3;case 3:return[3,12];case 4:return s=null===(a=null==e?void 0:e.data)||void 0===a?void 0:a.payload,"code"!==y?[3,10]:(h=C.current,v=k.current,h&&"function"==typeof h?[4,h(null===(i=e.data)||void 0===i?void 0:i.payload)]:[3,6]);case 5:return s=o.sent(),[3,10];case 6:return v?[4,fetch(f(v.url,l,null==s?void 0:s.code,p,g),{method:null!==(c=v.method)&&void 0!==c?c:"POST",headers:v.headers||{}})]:[3,9];case 7:return[4,o.sent().json()];case 8:return s=o.sent(),[3,10];case 9:throw new Error("useOAuth2: You must provide `exchangeCodeForTokenQuery` or `exchangeCodeForTokenQueryFn`");case 10:return A({loading:!1,error:null}),R(s),b?[4,b(s)]:[3,12];case 11:o.sent(),o.label=12;case 12:return[3,15];case 13:return w=o.sent(),console.error(w),A({loading:!1,error:w.toString()}),[3,15];case 14:return d(x,E,m),[7];case 15:return[2]}}))}))}return function(e,r){e.setItem(a,r)}(sessionStorage,g),E.current=(i=function(e,r,t,o,a,u,i){void 0===i&&(i={});var s=c(n({response_type:u,client_id:r,redirect_uri:t,scope:o,state:a},i));return"".concat(e,"?").concat(s)}(s,l,p,w,g,y,T.current),h=window.outerHeight/2+window.screenY-350,v=window.outerWidth/2+window.screenX-300,window.open(i,"OAuth2 Popup","height=".concat(700,",width=").concat(600,",top=").concat(h,",left=").concat(v))),window.addEventListener("message",m),x.current=setInterval((function(){var e,r,t;(!(null===(e=E.current)||void 0===e?void 0:e.window)||(null===(t=null===(r=E.current)||void 0===r?void 0:r.window)||void 0===t?void 0:t.closed))&&(A((function(e){return n(n({},e),{loading:!1})})),console.warn("Warning: Popup was closed before completing authentication."),d(x,E,m))}),250),function(){window.removeEventListener("message",m),x.current&&clearInterval(x.current)}}),[s,l,p,w,y,b,S,A,R]);return{data:U,loading:O,error:F,getAuth:q,logout:r.useCallback((function(){L(),A({loading:!1,error:null})}),[L]),isPersistent:_}};
@@ -2,4 +2,5 @@ export declare const POPUP_HEIGHT = 700;
2
2
  export declare const POPUP_WIDTH = 600;
3
3
  export declare const OAUTH_STATE_KEY = "react-use-oauth2-state-key";
4
4
  export declare const OAUTH_RESPONSE = "react-use-oauth2-response";
5
+ export declare const EXCHANGE_CODE_FOR_TOKEN_METHODS: readonly ["GET", "POST", "PUT", "PATCH"];
5
6
  export declare const DEFAULT_EXCHANGE_CODE_FOR_TOKEN_METHOD = "POST";
@@ -13,4 +13,4 @@ export declare const closePopup: (popupRef: React.MutableRefObject<Window | null
13
13
  export declare const isWindowOpener: (opener: Window | null) => opener is Window;
14
14
  export declare const openerPostMessage: (opener: Window, message: TMessageData) => void;
15
15
  export declare const cleanup: (intervalRef: React.MutableRefObject<string | number | NodeJS.Timeout | undefined>, popupRef: React.MutableRefObject<Window | null | undefined>, handleMessageListener: any) => void;
16
- export declare const formatExchangeCodeForTokenServerURL: (exchangeCodeForTokenServerURL: string, clientId: string, code: string, redirectUri: string, state: string) => string;
16
+ export declare const formatExchangeCodeForTokenServerURL: (serverUrl: string, clientId: string, code: string, redirectUri: string, state: string) => string;
@@ -1,4 +1,4 @@
1
- import { OAUTH_RESPONSE } from './constants';
1
+ import { OAUTH_RESPONSE, EXCHANGE_CODE_FOR_TOKEN_METHODS } from './constants';
2
2
  export type TAuthTokenPayload = {
3
3
  token_type: string;
4
4
  expires_in: number;
@@ -6,13 +6,18 @@ export type TAuthTokenPayload = {
6
6
  scope: string;
7
7
  refresh_token: string;
8
8
  };
9
- export type TResponseTypeBasedProps<TData> = {
9
+ type TExchangeCodeForTokenQuery = {
10
+ url: string;
11
+ method: (typeof EXCHANGE_CODE_FOR_TOKEN_METHODS)[number];
12
+ headers?: Record<string, any>;
13
+ };
14
+ type TExchangeCodeForTokenQueryFn<TData = TAuthTokenPayload> = (callbackParameters: any) => Promise<TData>;
15
+ export type TResponseTypeBasedProps<TData = TAuthTokenPayload> = RequireOnlyOne<{
10
16
  responseType: 'code';
11
- exchangeCodeForTokenServerURL: string;
12
- exchangeCodeForTokenMethod?: 'POST' | 'GET';
13
- exchangeCodeForTokenHeaders?: Record<string, any>;
17
+ exchangeCodeForTokenQuery: TExchangeCodeForTokenQuery;
18
+ exchangeCodeForTokenQueryFn: TExchangeCodeForTokenQueryFn<TData>;
14
19
  onSuccess?: (payload: TData) => void;
15
- } | {
20
+ }, 'exchangeCodeForTokenQuery' | 'exchangeCodeForTokenQueryFn'> | {
16
21
  responseType: 'token';
17
22
  onSuccess?: (payload: TData) => void;
18
23
  };
@@ -32,3 +37,7 @@ export type TMessageData = {
32
37
  type: typeof OAUTH_RESPONSE;
33
38
  payload: any;
34
39
  };
40
+ type RequireOnlyOne<T, Keys extends keyof T = keyof T> = Pick<T, Exclude<keyof T, Keys>> & {
41
+ [K in Keys]-?: Required<Pick<T, K>> & Partial<Record<Exclude<Keys, K>, undefined>>;
42
+ }[Keys];
43
+ export {};
@@ -1,6 +1,6 @@
1
1
  import { TAuthTokenPayload, TOauth2Props, TState } from './types';
2
2
  export declare const useOAuth2: <TData = TAuthTokenPayload>(props: TOauth2Props<TData>) => {
3
- data: TState;
3
+ data: TState<TData>;
4
4
  loading: boolean;
5
5
  error: string | null;
6
6
  getAuth: () => () => void;
package/dist/esm/index.js CHANGED
@@ -1 +1 @@
1
- import{jsx as e}from"react/jsx-runtime";import{useEffect as r,useSyncExternalStore as n,useState as t,useRef as o,useCallback as a,useMemo as i}from"react";var c=function(){return c=Object.assign||function(e){for(var r,n=1,t=arguments.length;n<t;n++)for(var o in r=arguments[n])Object.prototype.hasOwnProperty.call(r,o)&&(e[o]=r[o]);return e},c.apply(this,arguments)};function u(e,r,n,t){return new(n||(n=Promise))((function(o,a){function i(e){try{u(t.next(e))}catch(e){a(e)}}function c(e){try{u(t.throw(e))}catch(e){a(e)}}function u(e){var r;e.done?o(e.value):(r=e.value,r instanceof n?r:new n((function(e){e(r)}))).then(i,c)}u((t=t.apply(e,r||[])).next())}))}function s(e,r){var n,t,o,a,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return a={next:c(0),throw:c(1),return:c(2)},"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function c(c){return function(u){return function(c){if(n)throw new TypeError("Generator is already executing.");for(;a&&(a=0,c[0]&&(i=0)),i;)try{if(n=1,t&&(o=2&c[0]?t.return:c[0]?t.throw||((o=t.return)&&o.call(t),0):t.next)&&!(o=o.call(t,c[1])).done)return o;switch(t=0,o&&(c=[2&c[0],o.value]),c[0]){case 0:case 1:o=c;break;case 4:return i.label++,{value:c[1],done:!1};case 5:i.label++,t=c[1],c=[0];continue;case 7:c=i.ops.pop(),i.trys.pop();continue;default:if(!(o=i.trys,(o=o.length>0&&o[o.length-1])||6!==c[0]&&2!==c[0])){i=0;continue}if(3===c[0]&&(!o||c[1]>o[0]&&c[1]<o[3])){i.label=c[1];break}if(6===c[0]&&i.label<o[1]){i.label=o[1],o=c;break}if(o&&i.label<o[2]){i.label=o[2],i.ops.push(c);break}o[2]&&i.ops.pop(),i.trys.pop();continue}c=r.call(e,i)}catch(e){c=[6,e],t=0}finally{n=o=0}if(5&c[0])throw c[1];return{value:c[0]?c[1]:void 0,done:!0}}([c,u])}}}var l="react-use-oauth2-state-key",d="react-use-oauth2-response",f=function(e){return new URLSearchParams(e).toString()},p=function(e){var r=new URLSearchParams(e);return Object.fromEntries(r.entries())},v=function(e,r){return e.postMessage(r)},h=function(e,r,n){clearInterval(e.current),r.current&&"function"==typeof r.current.close&&function(e){var r;null===(r=e.current)||void 0===r||r.close()}(r),sessionStorage.removeItem(l),window.removeEventListener("message",n)},w=function(e,r,n,t,o){var a=e.split("?")[0],i=p(e.split("?")[1]);return"".concat(a,"?").concat(f(c(c({},i),{client_id:r,grant_type:"authorization_code",code:n,redirect_uri:t,state:o})))},g=function(n){var t=n.Component,o=void 0===t?e("div",c({style:{margin:"12px"},"data-testid":"popup-loading"},{children:"Loading..."})):t;return r((function(){var e=c(c({},p(window.location.search.split("?")[1])),p(window.location.hash.split("#")[1])),r=null==e?void 0:e.state,n=null==e?void 0:e.error,t=null===window||void 0===window?void 0:window.opener;if(!function(e){return null!=e}(t))throw new Error("No window opener");var o,a,i=r&&(o=t.sessionStorage,a=r,o.getItem(l)===a);if(!n&&i)v(t,{type:d,payload:e});else{var u=n?decodeURI(n):i?"OAuth error: An error has occured.":"OAuth error: State mismatch.";v(t,{type:d,error:u})}}),[]),o};const m=new Map;function y(e,c){if(void 0===n)throw new TypeError('You are using React 17 or below. Install with "npm install use-local-storage-state@17".');const[u]=t(null==c?void 0:c.defaultValue);if("undefined"==typeof window)return[u,()=>{},{isPersistent:!0,removeItem:()=>{}}];const s=null==c?void 0:c.serializer;return function(e,t,c=!0,u=T,s=JSON.stringify){m.has(e)||void 0===t||null!==E((()=>localStorage.getItem(e)))||E((()=>localStorage.setItem(e,s(t))));const l=o({item:null,parsed:t}),d=n(a((r=>{const n=n=>{e===n&&r()};return b.add(n),()=>{b.delete(n)}}),[e]),(()=>{var r;const n=null!==(r=E((()=>localStorage.getItem(e))))&&void 0!==r?r:null;if(m.has(e))l.current={item:n,parsed:m.get(e)};else if(n!==l.current.item){let e;try{e=null===n?t:u(n)}catch(r){e=t}l.current={item:n,parsed:e}}return l.current.parsed}),(()=>t)),f=a((r=>{const n=r instanceof Function?r(l.current.parsed):r;try{localStorage.setItem(e,s(n)),m.delete(e)}catch(r){m.set(e,n)}S(e)}),[e,s]);return r((()=>{if(!c)return;const r=r=>{r.storageArea===E((()=>localStorage))&&r.key===e&&S(e)};return window.addEventListener("storage",r),()=>window.removeEventListener("storage",r)}),[e,c]),i((()=>[d,f,{isPersistent:d===t||!m.has(e),removeItem(){E((()=>localStorage.removeItem(e))),m.delete(e),S(e)}}]),[e,f,d,t])}(e,u,null==c?void 0:c.storageSync,null==s?void 0:s.parse,null==s?void 0:s.stringify)}const b=new Set;function S(e){for(const r of[...b])r(e)}function T(e){return"undefined"===e?void 0:JSON.parse(e)}function E(e){try{return e()}catch(e){return}}var I=function(e){var r=e.authorizeUrl,n=e.clientId,i=e.redirectUri,p=e.scope,v=void 0===p?"":p,g=e.responseType,m=e.extraQueryParameters,b=void 0===m?{}:m,S=e.onSuccess,T=e.onError;!function(e){var r=e.authorizeUrl,n=e.clientId,t=e.redirectUri,o=e.responseType,a=e.extraQueryParameters,i=void 0===a?{}:a,c=e.onSuccess,u=e.onError;if(!(r&&n&&t&&o))throw new Error("Missing required props for useOAuth2. Required props are: {authorizeUrl, clientId, redirectUri, responseType}");if("code"===o&&!e.exchangeCodeForTokenServerURL)throw new Error('exchangeCodeForTokenServerURL is required for responseType of "code" for useOAuth2.');if("code"===o&&e.exchangeCodeForTokenMethod&&!["POST","GET"].includes(e.exchangeCodeForTokenMethod))throw new Error('Invalid exchangeCodeForTokenServerURL value. It can be one of "POST" or "GET".');if("object"!=typeof i)throw new TypeError("extraQueryParameters must be an object for useOAuth2.");if(c&&"function"!=typeof c)throw new TypeError("onSuccess callback must be a function for useOAuth2.");if(u&&"function"!=typeof u)throw new TypeError("onError callback must be a function for useOAuth2.")}(e);var E=o(b),I=o(),x=o(),k=t({loading:!1,error:null}),O=k[0],P=O.loading,U=O.error,A=k[1],L=y("".concat(g,"-").concat(r,"-").concat(n,"-").concat(v),{defaultValue:null}),C=L[0],R=L[1],F=L[2],j=F.removeItem,M=F.isPersistent,_="code"===g&&e.exchangeCodeForTokenServerURL,z="code"===g&&e.exchangeCodeForTokenMethod,q="code"===g&&e.exchangeCodeForTokenHeaders,G=a((function(){A({loading:!0,error:null});var e,t,o,a,p,m=(e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",t=new Uint8Array(40),window.crypto.getRandomValues(t),t=t.map((function(r){return e.codePointAt(r%e.length)})),String.fromCharCode.apply(null,t));function y(e){var r,t,o;return u(this,void 0,void 0,(function(){var a,c,u;return s(this,(function(s){switch(s.label){case 0:if((null===(r=null==e?void 0:e.data)||void 0===r?void 0:r.type)!==d)return[2];s.label=1;case 1:return s.trys.push([1,10,11,12]),"error"in e.data?(a=(null===(t=e.data)||void 0===t?void 0:t.error)||"Unknown Error occured.",A({loading:!1,error:a}),T?[4,T(a)]:[3,3]):[3,4];case 2:s.sent(),s.label=3;case 3:return[3,9];case 4:return c=null===(o=null==e?void 0:e.data)||void 0===o?void 0:o.payload,"code"===g&&_?[4,fetch(w(_,n,null==c?void 0:c.code,i,m),{method:z||"POST",headers:q||{}})]:[3,7];case 5:return[4,s.sent().json()];case 6:c=s.sent(),s.label=7;case 7:return A({loading:!1,error:null}),R(c),S?[4,S(c)]:[3,9];case 8:s.sent(),s.label=9;case 9:return[3,12];case 10:return u=s.sent(),console.error(u),A({loading:!1,error:u.toString()}),[3,12];case 11:return h(x,I,y),[7];case 12:return[2]}}))}))}return function(e,r){e.setItem(l,r)}(sessionStorage,m),I.current=(o=function(e,r,n,t,o,a,i){void 0===i&&(i={});var u=f(c({response_type:a,client_id:r,redirect_uri:n,scope:t,state:o},i));return"".concat(e,"?").concat(u)}(r,n,i,v,m,g,E.current),a=window.outerHeight/2+window.screenY-350,p=window.outerWidth/2+window.screenX-300,window.open(o,"OAuth2 Popup","height=".concat(700,",width=").concat(600,",top=").concat(a,",left=").concat(p))),window.addEventListener("message",y),x.current=setInterval((function(){var e,r,n;(!(null===(e=I.current)||void 0===e?void 0:e.window)||(null===(n=null===(r=I.current)||void 0===r?void 0:r.window)||void 0===n?void 0:n.closed))&&(A((function(e){return c(c({},e),{loading:!1})})),console.warn("Warning: Popup was closed before completing authentication."),h(x,I,y))}),250),function(){window.removeEventListener("message",y),x.current&&clearInterval(x.current)}}),[r,n,i,v,g,_,z,q,S,T,A,R]);return{data:C,loading:P,error:U,getAuth:G,logout:a((function(){j(),A({loading:!1,error:null})}),[j]),isPersistent:M}};export{g as OAuthPopup,I as useOAuth2};
1
+ import{jsx as e}from"react/jsx-runtime";import{useEffect as r,useSyncExternalStore as n,useState as o,useRef as t,useCallback as a,useMemo as i}from"react";var c=function(){return c=Object.assign||function(e){for(var r,n=1,o=arguments.length;n<o;n++)for(var t in r=arguments[n])Object.prototype.hasOwnProperty.call(r,t)&&(e[t]=r[t]);return e},c.apply(this,arguments)};function u(e,r,n,o){return new(n||(n=Promise))((function(t,a){function i(e){try{u(o.next(e))}catch(e){a(e)}}function c(e){try{u(o.throw(e))}catch(e){a(e)}}function u(e){var r;e.done?t(e.value):(r=e.value,r instanceof n?r:new n((function(e){e(r)}))).then(i,c)}u((o=o.apply(e,r||[])).next())}))}function s(e,r){var n,o,t,a,i={label:0,sent:function(){if(1&t[0])throw t[1];return t[1]},trys:[],ops:[]};return a={next:c(0),throw:c(1),return:c(2)},"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function c(c){return function(u){return function(c){if(n)throw new TypeError("Generator is already executing.");for(;a&&(a=0,c[0]&&(i=0)),i;)try{if(n=1,o&&(t=2&c[0]?o.return:c[0]?o.throw||((t=o.return)&&t.call(o),0):o.next)&&!(t=t.call(o,c[1])).done)return t;switch(o=0,t&&(c=[2&c[0],t.value]),c[0]){case 0:case 1:t=c;break;case 4:return i.label++,{value:c[1],done:!1};case 5:i.label++,o=c[1],c=[0];continue;case 7:c=i.ops.pop(),i.trys.pop();continue;default:if(!(t=i.trys,(t=t.length>0&&t[t.length-1])||6!==c[0]&&2!==c[0])){i=0;continue}if(3===c[0]&&(!t||c[1]>t[0]&&c[1]<t[3])){i.label=c[1];break}if(6===c[0]&&i.label<t[1]){i.label=t[1],t=c;break}if(t&&i.label<t[2]){i.label=t[2],i.ops.push(c);break}t[2]&&i.ops.pop(),i.trys.pop();continue}c=r.call(e,i)}catch(e){c=[6,e],o=0}finally{n=t=0}if(5&c[0])throw c[1];return{value:c[0]?c[1]:void 0,done:!0}}([c,u])}}}"function"==typeof SuppressedError&&SuppressedError;var l="react-use-oauth2-state-key",d="react-use-oauth2-response",f=["GET","POST","PUT","PATCH"],p=function(e){return new URLSearchParams(e).toString()},h=function(e){var r=new URLSearchParams(e);return Object.fromEntries(r.entries())},v=function(e,r){return e.postMessage(r)},w=function(e,r,n){clearInterval(e.current),r.current&&"function"==typeof r.current.close&&function(e){var r;null===(r=e.current)||void 0===r||r.close()}(r),sessionStorage.removeItem(l),window.removeEventListener("message",n)},y=function(e,r,n,o,t){var a=e.split("?")[0],i=h(e.split("?")[1]);return"".concat(a,"?").concat(p(c(c({},i),{client_id:r,grant_type:"authorization_code",code:n,redirect_uri:o,state:t})))},g=!1,m=function(n){var o=n.Component,t=void 0===o?e("div",{style:{margin:"12px"},"data-testid":"popup-loading",children:"Loading..."}):o;return r((function(){if(!g){g=!0;var e=c(c({},h(window.location.search.split("?")[1])),h(window.location.hash.split("#")[1])),r=null==e?void 0:e.state,n=null==e?void 0:e.error,o=null===window||void 0===window?void 0:window.opener;if(!function(e){return null!=e}(o))throw new Error("No window opener");var t,a,i=r&&(t=o.sessionStorage,a=r,t.getItem(l)===a);if(!n&&i)v(o,{type:d,payload:e});else{var u=n?decodeURI(n):"".concat(i?"OAuth error: An error has occured.":"OAuth error: State mismatch.");v(o,{type:d,error:u})}}}),[]),t};const T=new Map;function b(e,c){if(void 0===n)throw new TypeError('You are using React 17 or below. Install with "npm install use-local-storage-state@17".');const[u]=o(null==c?void 0:c.defaultValue);if("undefined"==typeof window)return[u,()=>{},{isPersistent:!0,removeItem:()=>{}}];const s=null==c?void 0:c.serializer;return function(e,o,c=!0,u=x,s=JSON.stringify){T.has(e)||void 0===o||null!==k((()=>localStorage.getItem(e)))||k((()=>localStorage.setItem(e,s(o))));const l=t({item:null,parsed:o}),d=n(a((r=>{const n=n=>{e===n&&r()};return S.add(n),()=>{S.delete(n)}}),[e]),(()=>{var r;const n=null!==(r=k((()=>localStorage.getItem(e))))&&void 0!==r?r:null;if(T.has(e))l.current={item:n,parsed:T.get(e)};else if(n!==l.current.item){let e;try{e=null===n?o:u(n)}catch(r){e=o}l.current={item:n,parsed:e}}return l.current.parsed}),(()=>o)),f=a((r=>{const n=r instanceof Function?r(l.current.parsed):r;try{localStorage.setItem(e,s(n)),T.delete(e)}catch(r){T.set(e,n)}E(e)}),[e,s]);return r((()=>{if(!c)return;const r=r=>{r.storageArea===k((()=>localStorage))&&r.key===e&&E(e)};return window.addEventListener("storage",r),()=>window.removeEventListener("storage",r)}),[e,c]),i((()=>[d,f,{isPersistent:d===o||!T.has(e),removeItem(){k((()=>localStorage.removeItem(e))),T.delete(e),E(e)}}]),[e,f,d,o])}(e,u,null==c?void 0:c.storageSync,null==s?void 0:s.parse,null==s?void 0:s.stringify)}const S=new Set;function E(e){for(const r of[...S])r(e)}function x(e){return"undefined"===e?void 0:JSON.parse(e)}function k(e){try{return e()}catch(e){return}}var I=function(e){var r=e.authorizeUrl,n=e.clientId,i=e.redirectUri,h=e.scope,v=void 0===h?"":h,g=e.responseType,m=e.extraQueryParameters,T=void 0===m?{}:m,S=e.onSuccess,E=e.onError;!function(e){var r=e.authorizeUrl,n=e.clientId,o=e.redirectUri,t=e.responseType,a=e.extraQueryParameters,i=void 0===a?{}:a,c=e.onSuccess,u=e.onError;if(!(r&&n&&o&&t))throw new Error("Missing required props for useOAuth2. Required props are: {authorizeUrl, clientId, redirectUri, responseType}");if("code"===t&&!e.exchangeCodeForTokenQuery&&!e.exchangeCodeForTokenQueryFn)throw new Error('Either `exchangeCodeForTokenQuery` or `exchangeCodeForTokenQueryFn` is required for responseType of "code" for useOAuth2.');if("code"===t&&e.exchangeCodeForTokenQuery&&!e.exchangeCodeForTokenQuery.url)throw new Error("Value `exchangeCodeForTokenQuery.url` is missing.");if("code"===t&&e.exchangeCodeForTokenQuery&&!["GET","POST","PUT","PATCH"].includes(e.exchangeCodeForTokenQuery.method))throw new Error("Invalid `exchangeCodeForTokenQuery.method` value. It can be one of ".concat(f.join(", "),"."));if("object"!=typeof i)throw new TypeError("extraQueryParameters must be an object for useOAuth2.");if(c&&"function"!=typeof c)throw new TypeError("onSuccess callback must be a function for useOAuth2.");if(u&&"function"!=typeof u)throw new TypeError("onError callback must be a function for useOAuth2.")}(e);var x=t(T),k=t(),I=t(),P=t("code"===g&&e.exchangeCodeForTokenQuery),C=t("code"===g&&e.exchangeCodeForTokenQueryFn),F=o({loading:!1,error:null}),O=F[0],Q=O.loading,A=O.error,U=F[1],j=b("".concat(g,"-").concat(r,"-").concat(n,"-").concat(v),{defaultValue:null}),L=j[0],R=j[1],_=j[2],z=_.removeItem,V=_.isPersistent,q=a((function(){U({loading:!0,error:null});var e,o,t,a,f,h=(e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",o=new Uint8Array(40),window.crypto.getRandomValues(o),o=o.map((function(r){return e.codePointAt(r%62)})),String.fromCharCode.apply(null,o));function m(e){var r,o,t,a,c;return u(this,void 0,void 0,(function(){var u,l,f,p,v;return s(this,(function(s){switch(s.label){case 0:if((null===(r=null==e?void 0:e.data)||void 0===r?void 0:r.type)!==d)return[2];s.label=1;case 1:return s.trys.push([1,13,14,15]),"error"in e.data?(u=(null===(o=e.data)||void 0===o?void 0:o.error)||"Unknown Error occured.",U({loading:!1,error:u}),E?[4,E(u)]:[3,3]):[3,4];case 2:s.sent(),s.label=3;case 3:return[3,12];case 4:return l=null===(t=null==e?void 0:e.data)||void 0===t?void 0:t.payload,"code"!==g?[3,10]:(f=C.current,p=P.current,f&&"function"==typeof f?[4,f(null===(a=e.data)||void 0===a?void 0:a.payload)]:[3,6]);case 5:return l=s.sent(),[3,10];case 6:return p?[4,fetch(y(p.url,n,null==l?void 0:l.code,i,h),{method:null!==(c=p.method)&&void 0!==c?c:"POST",headers:p.headers||{}})]:[3,9];case 7:return[4,s.sent().json()];case 8:return l=s.sent(),[3,10];case 9:throw new Error("useOAuth2: You must provide `exchangeCodeForTokenQuery` or `exchangeCodeForTokenQueryFn`");case 10:return U({loading:!1,error:null}),R(l),S?[4,S(l)]:[3,12];case 11:s.sent(),s.label=12;case 12:return[3,15];case 13:return v=s.sent(),console.error(v),U({loading:!1,error:v.toString()}),[3,15];case 14:return w(I,k,m),[7];case 15:return[2]}}))}))}return function(e,r){e.setItem(l,r)}(sessionStorage,h),k.current=(t=function(e,r,n,o,t,a,i){void 0===i&&(i={});var u=p(c({response_type:a,client_id:r,redirect_uri:n,scope:o,state:t},i));return"".concat(e,"?").concat(u)}(r,n,i,v,h,g,x.current),a=window.outerHeight/2+window.screenY-350,f=window.outerWidth/2+window.screenX-300,window.open(t,"OAuth2 Popup","height=".concat(700,",width=").concat(600,",top=").concat(a,",left=").concat(f))),window.addEventListener("message",m),I.current=setInterval((function(){var e,r,n;(!(null===(e=k.current)||void 0===e?void 0:e.window)||(null===(n=null===(r=k.current)||void 0===r?void 0:r.window)||void 0===n?void 0:n.closed))&&(U((function(e){return c(c({},e),{loading:!1})})),console.warn("Warning: Popup was closed before completing authentication."),w(I,k,m))}),250),function(){window.removeEventListener("message",m),I.current&&clearInterval(I.current)}}),[r,n,i,v,g,S,E,U,R]);return{data:L,loading:Q,error:A,getAuth:q,logout:a((function(){z(),U({loading:!1,error:null})}),[z]),isPersistent:V}};export{m as OAuthPopup,I as useOAuth2};
@@ -2,4 +2,5 @@ export declare const POPUP_HEIGHT = 700;
2
2
  export declare const POPUP_WIDTH = 600;
3
3
  export declare const OAUTH_STATE_KEY = "react-use-oauth2-state-key";
4
4
  export declare const OAUTH_RESPONSE = "react-use-oauth2-response";
5
+ export declare const EXCHANGE_CODE_FOR_TOKEN_METHODS: readonly ["GET", "POST", "PUT", "PATCH"];
5
6
  export declare const DEFAULT_EXCHANGE_CODE_FOR_TOKEN_METHOD = "POST";
@@ -13,4 +13,4 @@ export declare const closePopup: (popupRef: React.MutableRefObject<Window | null
13
13
  export declare const isWindowOpener: (opener: Window | null) => opener is Window;
14
14
  export declare const openerPostMessage: (opener: Window, message: TMessageData) => void;
15
15
  export declare const cleanup: (intervalRef: React.MutableRefObject<string | number | NodeJS.Timeout | undefined>, popupRef: React.MutableRefObject<Window | null | undefined>, handleMessageListener: any) => void;
16
- export declare const formatExchangeCodeForTokenServerURL: (exchangeCodeForTokenServerURL: string, clientId: string, code: string, redirectUri: string, state: string) => string;
16
+ export declare const formatExchangeCodeForTokenServerURL: (serverUrl: string, clientId: string, code: string, redirectUri: string, state: string) => string;
@@ -1,4 +1,4 @@
1
- import { OAUTH_RESPONSE } from './constants';
1
+ import { OAUTH_RESPONSE, EXCHANGE_CODE_FOR_TOKEN_METHODS } from './constants';
2
2
  export type TAuthTokenPayload = {
3
3
  token_type: string;
4
4
  expires_in: number;
@@ -6,13 +6,18 @@ export type TAuthTokenPayload = {
6
6
  scope: string;
7
7
  refresh_token: string;
8
8
  };
9
- export type TResponseTypeBasedProps<TData> = {
9
+ type TExchangeCodeForTokenQuery = {
10
+ url: string;
11
+ method: (typeof EXCHANGE_CODE_FOR_TOKEN_METHODS)[number];
12
+ headers?: Record<string, any>;
13
+ };
14
+ type TExchangeCodeForTokenQueryFn<TData = TAuthTokenPayload> = (callbackParameters: any) => Promise<TData>;
15
+ export type TResponseTypeBasedProps<TData = TAuthTokenPayload> = RequireOnlyOne<{
10
16
  responseType: 'code';
11
- exchangeCodeForTokenServerURL: string;
12
- exchangeCodeForTokenMethod?: 'POST' | 'GET';
13
- exchangeCodeForTokenHeaders?: Record<string, any>;
17
+ exchangeCodeForTokenQuery: TExchangeCodeForTokenQuery;
18
+ exchangeCodeForTokenQueryFn: TExchangeCodeForTokenQueryFn<TData>;
14
19
  onSuccess?: (payload: TData) => void;
15
- } | {
20
+ }, 'exchangeCodeForTokenQuery' | 'exchangeCodeForTokenQueryFn'> | {
16
21
  responseType: 'token';
17
22
  onSuccess?: (payload: TData) => void;
18
23
  };
@@ -32,3 +37,7 @@ export type TMessageData = {
32
37
  type: typeof OAUTH_RESPONSE;
33
38
  payload: any;
34
39
  };
40
+ type RequireOnlyOne<T, Keys extends keyof T = keyof T> = Pick<T, Exclude<keyof T, Keys>> & {
41
+ [K in Keys]-?: Required<Pick<T, K>> & Partial<Record<Exclude<Keys, K>, undefined>>;
42
+ }[Keys];
43
+ export {};
@@ -1,6 +1,6 @@
1
1
  import { TAuthTokenPayload, TOauth2Props, TState } from './types';
2
2
  export declare const useOAuth2: <TData = TAuthTokenPayload>(props: TOauth2Props<TData>) => {
3
- data: TState;
3
+ data: TState<TData>;
4
4
  loading: boolean;
5
5
  error: string | null;
6
6
  getAuth: () => () => void;
package/dist/index.d.ts CHANGED
@@ -7,6 +7,7 @@ type Props = {
7
7
  declare const OAuthPopup: ({ Component, }: Props) => react.ReactElement<any, string | react.JSXElementConstructor<any>>;
8
8
 
9
9
  declare const OAUTH_RESPONSE = "react-use-oauth2-response";
10
+ declare const EXCHANGE_CODE_FOR_TOKEN_METHODS: readonly ["GET", "POST", "PUT", "PATCH"];
10
11
 
11
12
  type TAuthTokenPayload = {
12
13
  token_type: string;
@@ -15,13 +16,18 @@ type TAuthTokenPayload = {
15
16
  scope: string;
16
17
  refresh_token: string;
17
18
  };
18
- type TResponseTypeBasedProps<TData> = {
19
+ type TExchangeCodeForTokenQuery = {
20
+ url: string;
21
+ method: (typeof EXCHANGE_CODE_FOR_TOKEN_METHODS)[number];
22
+ headers?: Record<string, any>;
23
+ };
24
+ type TExchangeCodeForTokenQueryFn<TData = TAuthTokenPayload> = (callbackParameters: any) => Promise<TData>;
25
+ type TResponseTypeBasedProps<TData = TAuthTokenPayload> = RequireOnlyOne<{
19
26
  responseType: 'code';
20
- exchangeCodeForTokenServerURL: string;
21
- exchangeCodeForTokenMethod?: 'POST' | 'GET';
22
- exchangeCodeForTokenHeaders?: Record<string, any>;
27
+ exchangeCodeForTokenQuery: TExchangeCodeForTokenQuery;
28
+ exchangeCodeForTokenQueryFn: TExchangeCodeForTokenQueryFn<TData>;
23
29
  onSuccess?: (payload: TData) => void;
24
- } | {
30
+ }, 'exchangeCodeForTokenQuery' | 'exchangeCodeForTokenQueryFn'> | {
25
31
  responseType: 'token';
26
32
  onSuccess?: (payload: TData) => void;
27
33
  };
@@ -41,9 +47,12 @@ type TMessageData = {
41
47
  type: typeof OAUTH_RESPONSE;
42
48
  payload: any;
43
49
  };
50
+ type RequireOnlyOne<T, Keys extends keyof T = keyof T> = Pick<T, Exclude<keyof T, Keys>> & {
51
+ [K in Keys]-?: Required<Pick<T, K>> & Partial<Record<Exclude<Keys, K>, undefined>>;
52
+ }[Keys];
44
53
 
45
54
  declare const useOAuth2: <TData = TAuthTokenPayload>(props: TOauth2Props<TData>) => {
46
- data: TState;
55
+ data: TState<TData>;
47
56
  loading: boolean;
48
57
  error: string | null;
49
58
  getAuth: () => () => void;
package/package.json CHANGED
@@ -1,113 +1,116 @@
1
1
  {
2
- "name": "@tasoskakour/react-use-oauth2",
3
- "author": {
4
- "name": "Tasos Kakouris",
5
- "email": "tasoskakour@gmail.com",
6
- "website": "https://tasoskakour.com"
7
- },
8
- "keywords": [
9
- "react",
10
- "hooks",
11
- "typescript",
12
- "nodejs",
13
- "oauth2"
14
- ],
15
- "version": "1.3.3",
16
- "description": "A React hook that handles OAuth2 authorization flow.",
17
- "license": "MIT",
18
- "homepage": "https://github.com/tasoskakour/react-use-oauth2#readme",
19
- "bugs": {
20
- "url": "https://github.com/tasoskakour/react-use-oauth2/issues"
21
- },
22
- "repository": "github:tasoskakour/react-use-oauth2",
23
- "main": "dist/cjs/index.js",
24
- "module": "dist/esm/index.js",
25
- "dependencies": {
26
- "use-local-storage-state": "^18.3.3"
27
- },
28
- "scripts": {
29
- "build": "rollup -c",
30
- "example": "rollup -c rollup.config.example.mjs -w --silent",
31
- "lint": "eslint . --cache",
32
- "test-unit": "jest --detectOpenHandles --runInBand --config jest.config.unit.ts",
33
- "test-e2e": "start-server-and-test example \"http://localhost:3000|http://localhost:3001\" \"jest --detectOpenHandles --runInBand --config jest.config.e2e.ts\"",
34
- "test": "npm run lint && npm run test-unit && npm run test-e2e",
35
- "prepare": "husky install"
36
- },
37
- "browserslist": {
38
- "production": [
39
- ">1%",
40
- "not dead",
41
- "not op_mini all",
42
- "not ie > 0",
43
- "not ie_mob > 0"
44
- ],
45
- "development": [
46
- "last 1 chrome version",
47
- "last 1 firefox version",
48
- "last 1 safari version"
49
- ]
50
- },
51
- "peerDependencies": {
52
- "react": "^18.0.0"
53
- },
54
- "devDependencies": {
55
- "@babel/core": "^7.21.4",
56
- "@babel/plugin-transform-runtime": "^7.21.4",
57
- "@babel/preset-env": "^7.21.5",
58
- "@babel/preset-react": "^7.18.6",
59
- "@babel/preset-typescript": "^7.21.4",
60
- "@rollup/plugin-commonjs": "^25.0.0",
61
- "@rollup/plugin-json": "^6.0.0",
62
- "@rollup/plugin-node-resolve": "^15.0.2",
63
- "@rollup/plugin-replace": "^5.0.2",
64
- "@rollup/plugin-run": "^3.0.1",
65
- "@rollup/plugin-terser": "^0.4.0",
66
- "@rollup/plugin-typescript": "^11.1.1",
67
- "@testing-library/react": "^14.0.0",
68
- "@types/jest": "^29.5.1",
69
- "@types/node": "^20.2.3",
70
- "@types/react": "^18.2.8",
71
- "@types/react-dom": "^18.2.4",
72
- "babel-jest": "^29.5.0",
73
- "babel-loader": "^9.1.2",
74
- "builtin-modules": "^3.3.0",
75
- "delay": "^5.0.0",
76
- "eslint": "^8.39.0",
77
- "eslint-config-tasoskakour-typescript-prettier": "^2.1.0",
78
- "fastify": "^4.17.0",
79
- "husky": "^8.0.3",
80
- "jest": "^29.7.0",
81
- "jest-environment-jsdom": "^29.7.0",
82
- "jest-fetch-mock": "^3.0.3",
83
- "jest-puppeteer": "^9.0.0",
84
- "lint-staged": "^13.2.2",
85
- "puppeteer": "21.0.0",
86
- "react-dom": "^18.2.0",
87
- "react-router-dom": "^6.11.2",
88
- "rollup": "^3.20.7",
89
- "rollup-plugin-delete": "^2.0.0",
90
- "rollup-plugin-dts": "^5.3.0",
91
- "rollup-plugin-livereload": "^2.0.5",
92
- "rollup-plugin-peer-deps-external": "^2.2.4",
93
- "rollup-plugin-serve": "^2.0.2",
94
- "start-server-and-test": "^2.0.0",
95
- "ts-jest": "^29.1.1",
96
- "ts-jest-resolver": "^2.0.1",
97
- "ts-node": "^10.9.1",
98
- "typescript": "^5.0.3"
99
- },
100
- "engines": {
101
- "node": ">=16"
102
- },
103
- "files": [
104
- "dist"
105
- ],
106
- "types": "dist/index.d.ts",
107
- "lint-staged": {
108
- "*.{js,ts,tsx}": "eslint --cache --fix"
109
- },
110
- "publishConfig": {
111
- "access": "public"
112
- }
2
+ "name": "@tasoskakour/react-use-oauth2",
3
+ "author": {
4
+ "name": "Tasos Kakouris",
5
+ "email": "tasoskakour@gmail.com",
6
+ "website": "https://tasoskakour.com"
7
+ },
8
+ "keywords": [
9
+ "react",
10
+ "hooks",
11
+ "typescript",
12
+ "nodejs",
13
+ "oauth2"
14
+ ],
15
+ "version": "2.0.0",
16
+ "description": "A React hook that handles OAuth2 authorization flow.",
17
+ "license": "MIT",
18
+ "homepage": "https://github.com/tasoskakour/react-use-oauth2#readme",
19
+ "bugs": {
20
+ "url": "https://github.com/tasoskakour/react-use-oauth2/issues"
21
+ },
22
+ "repository": "github:tasoskakour/react-use-oauth2",
23
+ "main": "dist/cjs/index.js",
24
+ "module": "dist/esm/index.js",
25
+ "dependencies": {
26
+ "use-local-storage-state": "^18.3.3"
27
+ },
28
+ "scripts": {
29
+ "build": "rollup -c",
30
+ "example": "rollup -c rollup.config.example.mjs -w --silent",
31
+ "lint": "eslint . --cache",
32
+ "test-typeCheck": "tsc --emitDeclarationOnly false --noEmit",
33
+ "test-unit": "jest --detectOpenHandles --runInBand --config jest.config.unit.ts",
34
+ "test-e2e": "start-server-and-test example \"http://localhost:3000|http://localhost:3001\" \"jest --detectOpenHandles --runInBand --config jest.config.e2e.ts\"",
35
+ "test": "npm run lint && npm run test-typeCheck && npm run test-unit && npm run test-e2e",
36
+ "prepare": "husky install"
37
+ },
38
+ "browserslist": {
39
+ "production": [
40
+ ">1%",
41
+ "not dead",
42
+ "not op_mini all",
43
+ "not ie > 0",
44
+ "not ie_mob > 0"
45
+ ],
46
+ "development": [
47
+ "last 1 chrome version",
48
+ "last 1 firefox version",
49
+ "last 1 safari version"
50
+ ]
51
+ },
52
+ "peerDependencies": {
53
+ "react": "^18.0.0"
54
+ },
55
+ "devDependencies": {
56
+ "@babel/core": "^7.21.4",
57
+ "@babel/plugin-transform-runtime": "^7.21.4",
58
+ "@babel/preset-env": "^7.22.14",
59
+ "@babel/preset-react": "^7.23.3",
60
+ "@babel/preset-typescript": "^7.21.4",
61
+ "@fastify/cors": "^9.0.1",
62
+ "@fastify/formbody": "^7.4.0",
63
+ "@rollup/plugin-commonjs": "^25.0.0",
64
+ "@rollup/plugin-json": "^6.0.0",
65
+ "@rollup/plugin-node-resolve": "^15.2.3",
66
+ "@rollup/plugin-replace": "^5.0.2",
67
+ "@rollup/plugin-run": "^3.0.1",
68
+ "@rollup/plugin-terser": "^0.4.0",
69
+ "@rollup/plugin-typescript": "^11.1.1",
70
+ "@testing-library/react": "^14.0.0",
71
+ "@types/jest": "^29.5.1",
72
+ "@types/node": "^20.2.3",
73
+ "@types/react": "^18.2.8",
74
+ "@types/react-dom": "^18.2.4",
75
+ "babel-jest": "^29.5.0",
76
+ "babel-loader": "^9.1.2",
77
+ "builtin-modules": "^3.3.0",
78
+ "delay": "^5.0.0",
79
+ "eslint": "^8.57.0",
80
+ "eslint-config-tasoskakour-typescript-prettier": "^3.0.0",
81
+ "fastify": "^4.17.0",
82
+ "husky": "^8.0.3",
83
+ "jest": "^29.7.0",
84
+ "jest-environment-jsdom": "^29.7.0",
85
+ "jest-fetch-mock": "^3.0.3",
86
+ "jest-puppeteer": "^9.0.0",
87
+ "lint-staged": "^13.2.2",
88
+ "puppeteer": "21.0.0",
89
+ "react-dom": "^18.2.0",
90
+ "react-router-dom": "^6.11.2",
91
+ "rollup": "^3.29.4",
92
+ "rollup-plugin-delete": "^2.0.0",
93
+ "rollup-plugin-dts": "^5.3.0",
94
+ "rollup-plugin-livereload": "^2.0.5",
95
+ "rollup-plugin-peer-deps-external": "^2.2.4",
96
+ "rollup-plugin-serve": "^2.0.2",
97
+ "start-server-and-test": "^2.0.0",
98
+ "ts-jest": "^29.1.1",
99
+ "ts-jest-resolver": "^2.0.1",
100
+ "ts-node": "^10.9.1",
101
+ "typescript": "^5.0.3"
102
+ },
103
+ "engines": {
104
+ "node": ">=16"
105
+ },
106
+ "files": [
107
+ "dist"
108
+ ],
109
+ "types": "dist/index.d.ts",
110
+ "lint-staged": {
111
+ "*.{js,ts,tsx}": "eslint --cache --fix"
112
+ },
113
+ "publishConfig": {
114
+ "access": "public"
115
+ }
113
116
  }