@webority/web-core 0.40.0 → 0.41.1
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
|
@@ -222,7 +222,7 @@ seconds. An app's own error boundary shares that window: reload only when
|
|
|
222
222
|
`Logger` is mobile-core's Logger: `debug`, `info`, `warn`, `error`, `breadcrumb`, `recordError`,
|
|
223
223
|
`requestFailed`, `setUser`, `setAttribute`, `trackScreen`, with secrets redacted before anything is
|
|
224
224
|
printed or forwarded. `Logger.requestFailed(error, { label, expectedStatuses })` logs a failed service
|
|
225
|
-
call: a cancel as info, an expected status as a warning, anything else as a recorded error with its
|
|
225
|
+
call: a cancel as info, an expected status as info (never a console warning), anything else as a recorded error with its
|
|
226
226
|
status, method, path (no query string), the server's `code` and `traceId`, and the correlation id.
|
|
227
227
|
`setConfig({ logLevel, loggingEnabled })` decides what reaches the console; `setRemoteLogger` forwards
|
|
228
228
|
info and above (never debug) to an error-reporting service, and errors are forwarded even with logging
|
package/dist/api/publicClient.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Logger as e}from"../logger/logger.js";import{applyConfiguredBaseUrl as t}from"./baseUrl.js";import{applyClientHeaders as n}from"./clientHeaders.js";import{readMinAppVersion as r}from"./minAppVersion.js";import{sendFormDataAsMultipart as i}from"./multipart.js";import{applyRequestDefaults as a}from"./requestDefaults.js";import{responseBodyText as o}from"./responseBodyText.js";import{attachRetry as s}from"./retry.js";import c from"axios";var l=c.create({headers:{"Content-Type":`application/json`,Accept:`application/json`}});t(l),a(l),i(l),r(l),s(l),l.interceptors.request.use(t=>(e.info(`Request: ${t.method?.toUpperCase()} ${t.url}`),t),t=>{let n=t instanceof Error?t.message:String(t);return e.error(`Request error:`,n),Promise.reject(t)}),n(l),l.interceptors.response.use(t=>(e.info(`Response: ${t.status} from ${t.config.url}`),t),t=>{if(c.isAxiosError(t)){let n=t.config;if(n?.skipErrorLog)return Promise.reject(t);t.response?e.error(`Response error: ${o(t.response.data,t.message)}, URL: ${n?.url}, Status: ${t.response.status}`):e.error(`Network error or no response from server: ${t.message}, URL: ${n?.url}`)}else{let n=t instanceof Error?t.message:String(t);e.error(`Unknown response error: ${n}`)}return Promise.reject(t)});export{l as default};
|
|
1
|
+
import{Logger as e}from"../logger/logger.js";import{applyConfiguredBaseUrl as t}from"./baseUrl.js";import{applyClientHeaders as n}from"./clientHeaders.js";import{readMinAppVersion as r}from"./minAppVersion.js";import{sendFormDataAsMultipart as i}from"./multipart.js";import{applyRequestDefaults as a}from"./requestDefaults.js";import{responseBodyText as o}from"./responseBodyText.js";import{attachRetry as s}from"./retry.js";import c from"axios";var l=c.create({headers:{"Content-Type":`application/json`,Accept:`application/json`}});t(l),a(l),i(l),r(l),s(l),l.interceptors.request.use(t=>(e.info(`Request: ${t.method?.toUpperCase()} ${t.url}`),t),t=>{let n=t instanceof Error?t.message:String(t);return e.error(`Request error:`,n),Promise.reject(t)}),n(l),l.interceptors.response.use(t=>(e.info(`Response: ${t.status} from ${t.config.url}`),t),t=>{if(c.isCancel(t))return Promise.reject(t);if(c.isAxiosError(t)){let n=t.config;if(n?.skipErrorLog)return Promise.reject(t);t.response?e.error(`Response error: ${o(t.response.data,t.message)}, URL: ${n?.url}, Status: ${t.response.status}`):e.error(`Network error or no response from server: ${t.message}, URL: ${n?.url}`)}else{let n=t instanceof Error?t.message:String(t);e.error(`Unknown response error: ${n}`)}return Promise.reject(t)});export{l as default};
|
package/dist/api/userClient.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Logger as e}from"../logger/logger.js";import{ACCESS_TOKEN_KEY as t,getToken as n}from"../auth/authStore.js";import{getLastKnownNetworkStatus as r}from"../network/networkStatus.js";import{applyConfiguredBaseUrl as i}from"./baseUrl.js";import{applyAccountIdHeader as a,applyClientHeaders as o}from"./clientHeaders.js";import{readMinAppVersion as s}from"./minAppVersion.js";import{sendFormDataAsMultipart as c}from"./multipart.js";import{applyRequestDefaults as l}from"./requestDefaults.js";import{responseBodyText as u}from"./responseBodyText.js";import{attachRetry as d}from"./retry.js";import{SIGN_OUT_REASON_HEADER as f,endSessionAndLogout as p,getAccountIdProvider as m,getErrorMessage as h,getRefreshPath as g,injectStore as _,injectUserAuth as v,isRefreshInFlight as y,queueBehindRefresh as b,readSignOutReason as x,refreshAccessToken as S,resetRefreshState as C}from"./tokenRefresh.js";import w,{AxiosHeaders as T}from"axios";var E=class e extends Error{constructor(t=`Request blocked: device is offline`){super(t),this.name=`RequestOfflineError`,Object.setPrototypeOf(this,e.prototype)}},D={TERMINATED:`SESSION_TERMINATED`,EXPIRED:`SESSION_EXPIRED`},
|
|
1
|
+
import{Logger as e}from"../logger/logger.js";import{ACCESS_TOKEN_KEY as t,getToken as n}from"../auth/authStore.js";import{getLastKnownNetworkStatus as r}from"../network/networkStatus.js";import{applyConfiguredBaseUrl as i}from"./baseUrl.js";import{applyAccountIdHeader as a,applyClientHeaders as o}from"./clientHeaders.js";import{readMinAppVersion as s}from"./minAppVersion.js";import{sendFormDataAsMultipart as c}from"./multipart.js";import{applyRequestDefaults as l}from"./requestDefaults.js";import{responseBodyText as u}from"./responseBodyText.js";import{attachRetry as d}from"./retry.js";import{SIGN_OUT_REASON_HEADER as f,endSessionAndLogout as p,getAccountIdProvider as m,getErrorMessage as h,getRefreshPath as g,injectStore as _,injectUserAuth as v,isRefreshInFlight as y,queueBehindRefresh as b,readSignOutReason as x,refreshAccessToken as S,resetRefreshState as C}from"./tokenRefresh.js";import w,{AxiosHeaders as T}from"axios";var E=class e extends Error{constructor(t=`Request blocked: device is offline`){super(t),this.name=`RequestOfflineError`,Object.setPrototypeOf(this,e.prototype)}},D=class e extends Error{constructor(){super(`No access token available`),this.name=`NoAccessTokenError`,Object.setPrototypeOf(this,e.prototype)}},O={TERMINATED:`SESSION_TERMINATED`,EXPIRED:`SESSION_EXPIRED`},k=new Set(Object.values(O)),A=`x-security-stamp-invalid`,j=e=>{if(!e||typeof e!=`object`)return!1;let t=e.get,n=typeof t==`function`?t.call(e,A):e[A];return String(n).toLowerCase()===`true`},M=e=>{if(!e||typeof e!=`object`)return;let t=e,n=t.error,r=[t.code,t.errorCode,n&&typeof n==`object`?n.code:void 0];for(let e of r)if(typeof e==`string`&&k.has(e))return e},N=w.create({headers:{"Content-Type":`application/json`}});s(N),d(N);var P=(e,t)=>{e.headers instanceof T?e.headers.set(`Authorization`,`Bearer ${t}`):e.headers=T.from({...e.headers,Authorization:`Bearer ${t}`})},F=e=>{let t=g();return!!t&&!!e?.includes(t)};i(N),l(N),c(N),o(N),N.interceptors.request.use(async i=>{if((await r()).isConnected===!1)return e.warn(`Request blocked: browser is offline. URL: ${i.url}`),Promise.reject(new E);try{if(F(i.url))return i;let r=await n(t);if(!r?.token)throw new D;let o=Date.now();return Date.parse(r.expiry)<=o+30*1e3?(e.info(`Access token expired or about to expire, refreshing before request`),P(i,await S()),await a(i.headers,m()),i):(P(i,r.token),await a(i.headers,m()),i)}catch(t){return t instanceof D?e.info(`Request not sent: signed out. URL: ${i.url}`):e.error(`Failed to attach token:`,h(t)),Promise.reject(t)}},t=>(e.error(`Request error:`,h(t)),Promise.reject(t))),N.interceptors.response.use(e=>e,async t=>{if(w.isCancel(t)||t instanceof E||t instanceof D)return Promise.reject(t);if(!w.isAxiosError(t))return e.error(`Non-axios error in response interceptor: ${h(t)}`),Promise.reject(t);let n=t,r=n.config;if(!n.response)return r?.skipErrorLog||e.error(`Network error or no response from server: ${n.message}, URL: ${r?.url}`),Promise.reject(t);if(n.response.status===401&&r&&!r._retry){if(F(r.url))return Promise.reject(t);let i=x(n.response.headers),a=M(n.response.data)??(j(n.response.headers)||i!==null?O.TERMINATED:void 0);if(a)return e.warn(`Session ${a}, logging out without refresh. URL: ${r.url}`),C(),await p(`terminated`,i),Promise.reject(t);if(e.warn(`Unauthorized URL: ${r.url}`),r._retry=!0,y())return b().then(t=>(e.info(`Retrying request after token refresh:`,r.url),P(r,t),N(r))).catch(t=>(e.error(`Failed to retry request after token refresh:`,h(t)),Promise.reject(t)));try{return P(r,await S()),N(r)}catch(e){return Promise.reject(e)}}return r?.skipErrorLog||e.error(`Response error: ${u(n.response?.data,n.message)}, URL: ${r?.url}, Status: ${n.response.status}`),Promise.reject(t)});export{E as RequestOfflineError,A as SESSION_INVALID_HEADER,O as SESSION_TERMINATION_CODES,f as SIGN_OUT_REASON_HEADER,N as default,_ as injectStore,v as injectUserAuth,C as resetRefreshState};
|
package/dist/logger/logger.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import{getConfigValue as e}from"../config/index.js";import{redact as t,redactString as n}from"./redact.js";var r=[`debug`,`info`,`warn`,`error`],i=t=>{let n=e(`logLevel`,`info`);return r.indexOf(t)>=r.indexOf(n)},a=()=>e(`loggingEnabled`,!0),o=null,s=e=>{o=e},c=e=>{if(e)try{e()}catch{}},l=e=>{if(e instanceof Error)return e;if(typeof e==`string`)return Error(e);try{return Error(JSON.stringify(e))}catch{return Error(String(e))}},u=null,d=e=>e==null?null:typeof e==`string`||typeof e==`number`||typeof e==`boolean`?e:String(e),f=e=>{if(u=e,e)for(let[t,n]of Object.entries(e))c(()=>o?.setAttribute?.(t,d(n)))},p=()=>u,m={dedupeWindowMs:5e3,dedupeMaxPerWindow:1,screenHistoryLimit:20},h=null,g=[],_=e=>{m={...m,...e},g.length>m.screenHistoryLimit&&(g=g.slice(g.length-m.screenHistoryLimit))},v=new Map,y=200,b=e=>{let t=e.stack?.split(`
|
|
2
|
-
`)[1]?.trim();return t?`${e.message}::${t}`:e.message},x=()=>{for(;v.size>y;){let e=v.keys().next().value;if(e===void 0)return;v.delete(e)}},S=e=>{if(m.dedupeWindowMs<=0)return{forward:!0};let t=Date.now(),n=v.get(e);if(!n||t-n.windowStart>=m.dedupeWindowMs){let r=n?.suppressedCount??0;return v.set(e,{windowStart:t,count:1,suppressedCount:0}),x(),r>0?{forward:!0,suppressedCount:r}:{forward:!0}}return n.count+=1,n.count>m.dedupeMaxPerWindow?(n.suppressedCount+=1,{forward:!1}):{forward:!0}},C=(e,t)=>{let n={...u};return h!==null&&(n.screen=h),t&&(n.suppressedCount=t),{...n,...e}},w=(e,t)=>{let{forward:n,suppressedCount:r}=S(b(e));n&&c(()=>o?.recordError?.(e,C(t,r)))},T=(e,...r)=>{!a()||!i(`debug`)||console.debug(`[DEBUG]: ${n(e)}`,...t(r))},E=(e,...r)=>{let s=n(e),l=t(r);!a()||!i(`info`)||(console.info(`[INFO]: ${s}`,...l),c(()=>o?.log?.(`info`,s,l)))},D=(e,...r)=>{let s=n(e),l=t(r);!a()||!i(`warn`)||(console.warn(`[WARNING]: ${s}`,...l),c(()=>o?.log?.(`warn`,s,l)))},O=(e,...r)=>{let s=n(e),u=t(r);a()&&i(`error`)&&console.error(`[ERROR]: ${s}`,...u),c(()=>o?.log?.(`error`,s,u));let d=u[0];d!==void 0&&w(l(d),{source:`Logger.error`,message:s})},k=(e,r)=>{let s=n(e),l=t(r);a()&&i(`info`)&&console.info(`[BREADCRUMB]: ${s}`,l??``),c(()=>o?.breadcrumb?.(s,l))},A=(e,n)=>{let r=t(l(e)),o=t(n);a()&&i(`error`)&&console.error(`[RECORDED]: ${r.message}`,o??``),w(r,o)},j=e=>typeof e==`object`&&e?e:null,M=(e,t)=>{let n=j(e);if(!n)return null;let r=typeof n.get==`function`?n.get.call(e,t):n[t]??n[t.toLowerCase()];return typeof r==`string`&&r!==``?r:null},N={debug:T,info:E,warn:D,error:O,breadcrumb:k,recordError:A,requestFailed:(r,s={})=>{let{label:l,expectedStatuses:u=[],...d}=s,f=j(r),p=j(f?.config),m=j(f?.response),h=j(m?.data),g=typeof m?.status==`number`?m.status:null,_=typeof p?.method==`string`?p.method.toUpperCase():null,v=typeof p?.url==`string`?p.url.split(`?`)[0]??null:null,y=l??([_,v].filter(Boolean).join(` `)||`Request`);if(f?.name===`CanceledError`||f?.code===`ERR_CANCELED`||f?.__CANCEL__===!0){E(`${y} canceled`);return}if(g!==null&&u.includes(g)){
|
|
2
|
+
`)[1]?.trim();return t?`${e.message}::${t}`:e.message},x=()=>{for(;v.size>y;){let e=v.keys().next().value;if(e===void 0)return;v.delete(e)}},S=e=>{if(m.dedupeWindowMs<=0)return{forward:!0};let t=Date.now(),n=v.get(e);if(!n||t-n.windowStart>=m.dedupeWindowMs){let r=n?.suppressedCount??0;return v.set(e,{windowStart:t,count:1,suppressedCount:0}),x(),r>0?{forward:!0,suppressedCount:r}:{forward:!0}}return n.count+=1,n.count>m.dedupeMaxPerWindow?(n.suppressedCount+=1,{forward:!1}):{forward:!0}},C=(e,t)=>{let n={...u};return h!==null&&(n.screen=h),t&&(n.suppressedCount=t),{...n,...e}},w=(e,t)=>{let{forward:n,suppressedCount:r}=S(b(e));n&&c(()=>o?.recordError?.(e,C(t,r)))},T=(e,...r)=>{!a()||!i(`debug`)||console.debug(`[DEBUG]: ${n(e)}`,...t(r))},E=(e,...r)=>{let s=n(e),l=t(r);!a()||!i(`info`)||(console.info(`[INFO]: ${s}`,...l),c(()=>o?.log?.(`info`,s,l)))},D=(e,...r)=>{let s=n(e),l=t(r);!a()||!i(`warn`)||(console.warn(`[WARNING]: ${s}`,...l),c(()=>o?.log?.(`warn`,s,l)))},O=(e,...r)=>{let s=n(e),u=t(r);a()&&i(`error`)&&console.error(`[ERROR]: ${s}`,...u),c(()=>o?.log?.(`error`,s,u));let d=u[0];d!==void 0&&w(l(d),{source:`Logger.error`,message:s})},k=(e,r)=>{let s=n(e),l=t(r);a()&&i(`info`)&&console.info(`[BREADCRUMB]: ${s}`,l??``),c(()=>o?.breadcrumb?.(s,l))},A=(e,n)=>{let r=t(l(e)),o=t(n);a()&&i(`error`)&&console.error(`[RECORDED]: ${r.message}`,o??``),w(r,o)},j=e=>typeof e==`object`&&e?e:null,M=(e,t)=>{let n=j(e);if(!n)return null;let r=typeof n.get==`function`?n.get.call(e,t):n[t]??n[t.toLowerCase()];return typeof r==`string`&&r!==``?r:null},N={debug:T,info:E,warn:D,error:O,breadcrumb:k,recordError:A,requestFailed:(r,s={})=>{let{label:l,expectedStatuses:u=[],...d}=s,f=j(r),p=j(f?.config),m=j(f?.response),h=j(m?.data),g=typeof m?.status==`number`?m.status:null,_=typeof p?.method==`string`?p.method.toUpperCase():null,v=typeof p?.url==`string`?p.url.split(`?`)[0]??null:null,y=l??([_,v].filter(Boolean).join(` `)||`Request`);if(f?.name===`CanceledError`||f?.code===`ERR_CANCELED`||f?.__CANCEL__===!0){E(`${y} canceled`);return}if(g!==null&&u.includes(g)){E(`${y} refused (${g})`);return}let b=typeof f?.message==`string`?f.message:r instanceof Error?r.message:null,x=n(g===null?`${y} failed`:`${y} failed (${g})`);a()&&i(`error`)&&console.error(`[ERROR]: ${x}`,n(b??``)),c(()=>o?.log?.(`error`,x,[]));let S=e(`correlationIdHeader`,null),C=t({source:`Logger.requestFailed`,label:y,status:g,method:_,path:v,code:typeof h?.code==`string`?h.code:null,traceId:typeof h?.traceId==`string`?h.traceId:null,correlationId:S?M(p?.headers,S):null,detail:b,...d});w(t(Error(x)),C)},setUser:(e,t)=>{c(()=>o?.setUser?.(e,t))},setAttribute:(e,t)=>{c(()=>o?.setAttribute?.(e,t))},trackScreen:(e,t)=>{e!==h&&(h=e,g.push(e),g.length>m.screenHistoryLimit&&(g=g.slice(g.length-m.screenHistoryLimit)),k(`Screen: ${e}`,t))},getCurrentScreen:()=>h,getScreenHistory:()=>[...g]};export{N as Logger,_ as configureLogger,p as getDeviceContext,f as setDeviceContext,s as setRemoteLogger};
|
|
@@ -60,7 +60,7 @@ export interface RequestFailedContext {
|
|
|
60
60
|
label?: string;
|
|
61
61
|
/**
|
|
62
62
|
* Statuses this call answers as part of its work (a 409 the screen explains, a 404 that
|
|
63
|
-
* means "none yet"): logged as a warning
|
|
63
|
+
* means "none yet"): logged as info, never a console warning and never recorded as an error.
|
|
64
64
|
*/
|
|
65
65
|
expectedStatuses?: readonly number[];
|
|
66
66
|
/** Anything else worth carrying to the remote sink. Never a token or a request body. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webority/web-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.41.1",
|
|
4
4
|
"description": "Platform layer for Webority React web portals: HTTP clients with single-flight token refresh, auth store, API error extraction, RTK Query base query, logger, formatters, storage, version check and route guards. No UI.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"type": "module",
|