@webority/mobile-core 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/lib/commonjs/api/publicClient.js +1 -1
- package/lib/commonjs/api/userClient.js +1 -1
- package/lib/commonjs/logger/index.js +1 -1
- package/lib/commonjs/push/index.js +1 -1
- package/lib/commonjs/push/push.js +1 -1
- package/lib/module/api/publicClient.js +1 -1
- package/lib/module/api/userClient.js +1 -1
- package/lib/module/logger/index.js +1 -1
- package/lib/module/push/index.js +1 -1
- package/lib/module/push/push.js +1 -1
- package/lib/typescript/commonjs/push/index.d.ts +2 -2
- package/lib/typescript/commonjs/push/push.d.ts +62 -19
- package/lib/typescript/module/push/index.d.ts +2 -2
- package/lib/typescript/module/push/push.d.ts +62 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -78,6 +78,19 @@ tapped notification's data as strings, including the tap that launched the app.
|
|
|
78
78
|
settings switch its `status` and `enabled`. The library does not remember the switch: an app that
|
|
79
79
|
keeps push on calls `enablePush` at each start. `setBadgeCount(count)` sets the app icon badge.
|
|
80
80
|
|
|
81
|
+
- `enablePush({ prompt: false })` never shows the permission prompt: a phone already granted
|
|
82
|
+
registers silently, and any other resolves with its status (`unknown` when the person has not
|
|
83
|
+
been asked yet, `denied` when refused). Call it at sign-in, and call `enablePush()` when the
|
|
84
|
+
person reaches a moment where asking makes sense.
|
|
85
|
+
- Every registered device carries `apnsEnvironment`: `Development` or `Production` on iOS, read
|
|
86
|
+
from the build's `aps-environment` (expo-application), so the server sends each phone to the
|
|
87
|
+
APNs gateway its signing accepts. It is null on Android, and on iOS when it cannot be read (the
|
|
88
|
+
simulator); the server then uses its own setting.
|
|
89
|
+
- `onReceive(handler)` calls `handler({ notificationId, title, body, data })` for each
|
|
90
|
+
notification that arrives while the app is open, shown or not, so the app can refresh a list or
|
|
91
|
+
an unread count. It returns a function that removes the handler. A notification arriving in the
|
|
92
|
+
background does not reach it; its tap still goes to `onOpen`.
|
|
93
|
+
|
|
81
94
|
### Notification buttons
|
|
82
95
|
|
|
83
96
|
A push names a category; the category's buttons are registered on the phone. Register them at
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=void 0;var _axios=_interopRequireDefault(require("axios")),_index=require("../config/index.js"),_index2=require("../logger/index.js");require("../types/axios.js");var _baseUrl=require("./baseUrl.js"),_clientHeaders=require("./clientHeaders.js"),_curl=require("./curl.js"),_minAppVersion=require("./minAppVersion.js"),_multipart=require("./multipart.js"),_requestDefaults=require("./requestDefaults.js"),_responseBodyText=require("./responseBodyText.js"),_retry=require("./retry.js");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}const publicClient=_axios.default.create({headers:{"Content-Type":"application/json",Accept:"application/json"}});(0,_baseUrl.applyConfiguredBaseUrl)(publicClient),(0,_requestDefaults.applyRequestDefaults)(publicClient),(0,_multipart.sendFormDataAsMultipart)(publicClient),(0,_minAppVersion.readMinAppVersion)(publicClient),(0,_retry.attachRetry)(publicClient),publicClient.interceptors.request.use(e=>(_index2.Logger.info(`Request: ${e.method?.toUpperCase()} ${e.url}`),(0,_index.getConfigValue)("logCurl")&&(0,_curl.logCurl)(e),e),e=>{const r=e instanceof Error?e.message:String(e);return _index2.Logger.error("Request error:",r),Promise.reject(e)}),(0,_clientHeaders.applyClientHeaders)(publicClient),publicClient.interceptors.response.use(e=>(_index2.Logger.info(`Response: ${e.status} from ${e.config.url}`),e),e=>{if(_axios.default.isAxiosError(e)){const r=e.config;if(r?.skipErrorLog)return Promise.reject(e);e.response?_index2.Logger.error(`Response error: ${(0,_responseBodyText.responseBodyText)(e.response.data,e.message)}, URL: ${r?.url}, Status: ${e.response.status}`):_index2.Logger.error(`Network error or no response from server: ${e.message}, URL: ${r?.url}`)}else{const r=e instanceof Error?e.message:String(e);_index2.Logger.error(`Unknown response error: ${r}`)}return Promise.reject(e)});var _default=exports.default=publicClient;
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=void 0;var _axios=_interopRequireDefault(require("axios")),_index=require("../config/index.js"),_index2=require("../logger/index.js");require("../types/axios.js");var _baseUrl=require("./baseUrl.js"),_clientHeaders=require("./clientHeaders.js"),_curl=require("./curl.js"),_minAppVersion=require("./minAppVersion.js"),_multipart=require("./multipart.js"),_requestDefaults=require("./requestDefaults.js"),_responseBodyText=require("./responseBodyText.js"),_retry=require("./retry.js");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}const publicClient=_axios.default.create({headers:{"Content-Type":"application/json",Accept:"application/json"}});(0,_baseUrl.applyConfiguredBaseUrl)(publicClient),(0,_requestDefaults.applyRequestDefaults)(publicClient),(0,_multipart.sendFormDataAsMultipart)(publicClient),(0,_minAppVersion.readMinAppVersion)(publicClient),(0,_retry.attachRetry)(publicClient),publicClient.interceptors.request.use(e=>(_index2.Logger.info(`Request: ${e.method?.toUpperCase()} ${e.url}`),(0,_index.getConfigValue)("logCurl")&&(0,_curl.logCurl)(e),e),e=>{const r=e instanceof Error?e.message:String(e);return _index2.Logger.error("Request error:",r),Promise.reject(e)}),(0,_clientHeaders.applyClientHeaders)(publicClient),publicClient.interceptors.response.use(e=>(_index2.Logger.info(`Response: ${e.status} from ${e.config.url}`),e),e=>{if(_axios.default.isCancel(e))return Promise.reject(e);if(_axios.default.isAxiosError(e)){const r=e.config;if(r?.skipErrorLog)return Promise.reject(e);e.response?_index2.Logger.error(`Response error: ${(0,_responseBodyText.responseBodyText)(e.response.data,e.message)}, URL: ${r?.url}, Status: ${e.response.status}`):_index2.Logger.error(`Network error or no response from server: ${e.message}, URL: ${r?.url}`)}else{const r=e instanceof Error?e.message:String(e);_index2.Logger.error(`Unknown response error: ${r}`)}return Promise.reject(e)});var _default=exports.default=publicClient;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.SESSION_TERMINATION_CODES=exports.SESSION_INVALID_HEADER=exports.RequestOfflineError=void 0,Object.defineProperty(exports,"SIGN_OUT_REASON_HEADER",{enumerable:!0,get:function(){return _tokenRefresh.SIGN_OUT_REASON_HEADER}}),exports.default=void 0,Object.defineProperty(exports,"injectStore",{enumerable:!0,get:function(){return _tokenRefresh.injectStore}}),Object.defineProperty(exports,"injectUserAuth",{enumerable:!0,get:function(){return _tokenRefresh.injectUserAuth}}),Object.defineProperty(exports,"resetRefreshState",{enumerable:!0,get:function(){return _tokenRefresh.resetRefreshState}});var _axios=_interopRequireWildcard(require("axios")),_authStore=require("../auth/authStore.js"),_index=require("../config/index.js"),_index2=require("../logger/index.js"),_networkStatus=require("../network/networkStatus.js");require("../types/axios.js");var _baseUrl=require("./baseUrl.js"),_clientHeaders=require("./clientHeaders.js"),_curl=require("./curl.js"),_minAppVersion=require("./minAppVersion.js"),_multipart=require("./multipart.js"),_requestDefaults=require("./requestDefaults.js"),_responseBodyText=require("./responseBodyText.js"),_retry=require("./retry.js"),_tokenRefresh=require("./tokenRefresh.js");function _interopRequireWildcard(e,r){if("function"==typeof WeakMap)var t=new WeakMap,s=new WeakMap;return(_interopRequireWildcard=function(e,r){if(!r&&e&&e.__esModule)return e;var o,n,i={__proto__:null,default:e};if(null===e||"object"!=typeof e&&"function"!=typeof e)return i;if(o=r?s:t){if(o.has(e))return o.get(e);o.set(e,i)}for(const r in e)"default"!==r&&{}.hasOwnProperty.call(e,r)&&((n=(o=Object.defineProperty)&&Object.getOwnPropertyDescriptor(e,r))&&(n.get||n.set)?o(i,r,n):i[r]=e[r]);return i})(e,r)}class RequestOfflineError extends Error{constructor(e="Request blocked: device is offline"){super(e),this.name="RequestOfflineError",Object.setPrototypeOf(this,RequestOfflineError.prototype)}}exports.RequestOfflineError=RequestOfflineError;const SESSION_TERMINATION_CODES=exports.SESSION_TERMINATION_CODES={TERMINATED:"SESSION_TERMINATED",EXPIRED:"SESSION_EXPIRED"},TERMINATION_CODES=new Set(Object.values(SESSION_TERMINATION_CODES)),SESSION_INVALID_HEADER=exports.SESSION_INVALID_HEADER="x-security-stamp-invalid",hasSessionInvalidHeader=e=>{if(!e||"object"!=typeof e)return!1;const r=e.get,t="function"==typeof r?r.call(e,SESSION_INVALID_HEADER):e[SESSION_INVALID_HEADER];return"true"===String(t).toLowerCase()},getSessionTerminationCode=e=>{if(!e||"object"!=typeof e)return;const r=e,t=r.error,s=[r.code,r.errorCode,t&&"object"==typeof t?t.code:void 0];for(const e of s)if("string"==typeof e&&TERMINATION_CODES.has(e))return e},userClient=_axios.default.create({headers:{"Content-Type":"application/json"}});(0,_minAppVersion.readMinAppVersion)(userClient),(0,_retry.attachRetry)(userClient);const setAuthHeader=(e,r)=>{e.headers instanceof _axios.AxiosHeaders?e.headers.set("Authorization",`Bearer ${r}`):e.headers=_axios.AxiosHeaders.from({...e.headers,Authorization:`Bearer ${r}`})},isRefreshTokenRequest=e=>{const r=(0,_tokenRefresh.getRefreshPath)();return!!r&&!!e?.includes(r)};(0,_baseUrl.applyConfiguredBaseUrl)(userClient),(0,_requestDefaults.applyRequestDefaults)(userClient),(0,_multipart.sendFormDataAsMultipart)(userClient),userClient.interceptors.request.use(e=>((0,_index.getConfigValue)("logCurl")&&(0,_curl.logCurl)(e),e)),(0,_clientHeaders.applyClientHeaders)(userClient),userClient.interceptors.request.use(async e=>{if(!1===(await(0,_networkStatus.getLastKnownNetworkStatus)()).isConnected)return _index2.Logger.warn(`Request blocked: device is offline. URL: ${e.url}`),Promise.reject(new RequestOfflineError);try{if(isRefreshTokenRequest(e.url))return e;const r=await(0,_authStore.getToken)(_authStore.ACCESS_TOKEN_KEY);if(!r?.token)throw new
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.SESSION_TERMINATION_CODES=exports.SESSION_INVALID_HEADER=exports.RequestOfflineError=void 0,Object.defineProperty(exports,"SIGN_OUT_REASON_HEADER",{enumerable:!0,get:function(){return _tokenRefresh.SIGN_OUT_REASON_HEADER}}),exports.default=void 0,Object.defineProperty(exports,"injectStore",{enumerable:!0,get:function(){return _tokenRefresh.injectStore}}),Object.defineProperty(exports,"injectUserAuth",{enumerable:!0,get:function(){return _tokenRefresh.injectUserAuth}}),Object.defineProperty(exports,"resetRefreshState",{enumerable:!0,get:function(){return _tokenRefresh.resetRefreshState}});var _axios=_interopRequireWildcard(require("axios")),_authStore=require("../auth/authStore.js"),_index=require("../config/index.js"),_index2=require("../logger/index.js"),_networkStatus=require("../network/networkStatus.js");require("../types/axios.js");var _baseUrl=require("./baseUrl.js"),_clientHeaders=require("./clientHeaders.js"),_curl=require("./curl.js"),_minAppVersion=require("./minAppVersion.js"),_multipart=require("./multipart.js"),_requestDefaults=require("./requestDefaults.js"),_responseBodyText=require("./responseBodyText.js"),_retry=require("./retry.js"),_tokenRefresh=require("./tokenRefresh.js");function _interopRequireWildcard(e,r){if("function"==typeof WeakMap)var t=new WeakMap,s=new WeakMap;return(_interopRequireWildcard=function(e,r){if(!r&&e&&e.__esModule)return e;var o,n,i={__proto__:null,default:e};if(null===e||"object"!=typeof e&&"function"!=typeof e)return i;if(o=r?s:t){if(o.has(e))return o.get(e);o.set(e,i)}for(const r in e)"default"!==r&&{}.hasOwnProperty.call(e,r)&&((n=(o=Object.defineProperty)&&Object.getOwnPropertyDescriptor(e,r))&&(n.get||n.set)?o(i,r,n):i[r]=e[r]);return i})(e,r)}class RequestOfflineError extends Error{constructor(e="Request blocked: device is offline"){super(e),this.name="RequestOfflineError",Object.setPrototypeOf(this,RequestOfflineError.prototype)}}exports.RequestOfflineError=RequestOfflineError;class NoAccessTokenError extends Error{constructor(){super("No access token available"),this.name="NoAccessTokenError",Object.setPrototypeOf(this,NoAccessTokenError.prototype)}}const SESSION_TERMINATION_CODES=exports.SESSION_TERMINATION_CODES={TERMINATED:"SESSION_TERMINATED",EXPIRED:"SESSION_EXPIRED"},TERMINATION_CODES=new Set(Object.values(SESSION_TERMINATION_CODES)),SESSION_INVALID_HEADER=exports.SESSION_INVALID_HEADER="x-security-stamp-invalid",hasSessionInvalidHeader=e=>{if(!e||"object"!=typeof e)return!1;const r=e.get,t="function"==typeof r?r.call(e,SESSION_INVALID_HEADER):e[SESSION_INVALID_HEADER];return"true"===String(t).toLowerCase()},getSessionTerminationCode=e=>{if(!e||"object"!=typeof e)return;const r=e,t=r.error,s=[r.code,r.errorCode,t&&"object"==typeof t?t.code:void 0];for(const e of s)if("string"==typeof e&&TERMINATION_CODES.has(e))return e},userClient=_axios.default.create({headers:{"Content-Type":"application/json"}});(0,_minAppVersion.readMinAppVersion)(userClient),(0,_retry.attachRetry)(userClient);const setAuthHeader=(e,r)=>{e.headers instanceof _axios.AxiosHeaders?e.headers.set("Authorization",`Bearer ${r}`):e.headers=_axios.AxiosHeaders.from({...e.headers,Authorization:`Bearer ${r}`})},isRefreshTokenRequest=e=>{const r=(0,_tokenRefresh.getRefreshPath)();return!!r&&!!e?.includes(r)};(0,_baseUrl.applyConfiguredBaseUrl)(userClient),(0,_requestDefaults.applyRequestDefaults)(userClient),(0,_multipart.sendFormDataAsMultipart)(userClient),userClient.interceptors.request.use(e=>((0,_index.getConfigValue)("logCurl")&&(0,_curl.logCurl)(e),e)),(0,_clientHeaders.applyClientHeaders)(userClient),userClient.interceptors.request.use(async e=>{if(!1===(await(0,_networkStatus.getLastKnownNetworkStatus)()).isConnected)return _index2.Logger.warn(`Request blocked: device is offline. URL: ${e.url}`),Promise.reject(new RequestOfflineError);try{if(isRefreshTokenRequest(e.url))return e;const r=await(0,_authStore.getToken)(_authStore.ACCESS_TOKEN_KEY);if(!r?.token)throw new NoAccessTokenError;const t=Date.now(),s=Date.parse(r.expiry);if(s<=t+3e4){_index2.Logger.info("Access token expired or about to expire, refreshing before request");const r=await(0,_tokenRefresh.refreshAccessToken)();return setAuthHeader(e,r),await(0,_clientHeaders.applyAccountIdHeader)(e.headers,(0,_tokenRefresh.getAccountIdProvider)()),e}return setAuthHeader(e,r.token),await(0,_clientHeaders.applyAccountIdHeader)(e.headers,(0,_tokenRefresh.getAccountIdProvider)()),e}catch(r){return r instanceof NoAccessTokenError?_index2.Logger.info(`Request not sent: signed out. URL: ${e.url}`):_index2.Logger.error("Failed to attach token:",(0,_tokenRefresh.getErrorMessage)(r)),Promise.reject(r)}},e=>(_index2.Logger.error("Request error:",(0,_tokenRefresh.getErrorMessage)(e)),Promise.reject(e))),userClient.interceptors.response.use(e=>e,async e=>{if(_axios.default.isCancel(e))return Promise.reject(e);if(e instanceof RequestOfflineError||e instanceof NoAccessTokenError)return Promise.reject(e);if(!_axios.default.isAxiosError(e))return _index2.Logger.error(`Non-axios error in response interceptor: ${(0,_tokenRefresh.getErrorMessage)(e)}`),Promise.reject(e);const r=e,t=r.config;if(!r.response)return t?.skipErrorLog||_index2.Logger.error(`Network error or no response from server: ${r.message}, URL: ${t?.url}`),Promise.reject(e);if(401===r.response.status&&t&&!t._retry){if(isRefreshTokenRequest(t.url))return Promise.reject(e);const s=(0,_tokenRefresh.readSignOutReason)(r.response.headers),o=getSessionTerminationCode(r.response.data)??(hasSessionInvalidHeader(r.response.headers)||null!==s?SESSION_TERMINATION_CODES.TERMINATED:void 0);if(o)return _index2.Logger.warn(`Session ${o}, logging out without refresh. URL: ${t.url}`),(0,_tokenRefresh.resetRefreshState)(),await(0,_tokenRefresh.endSessionAndLogout)("terminated",s),Promise.reject(e);if(_index2.Logger.warn(`Unauthorized URL: ${t.url}`),t._retry=!0,(0,_tokenRefresh.isRefreshInFlight)())return(0,_tokenRefresh.queueBehindRefresh)().then(e=>(_index2.Logger.info("Retrying request after token refresh:",t.url),setAuthHeader(t,e),userClient(t))).catch(e=>(_index2.Logger.error("Failed to retry request after token refresh:",(0,_tokenRefresh.getErrorMessage)(e)),Promise.reject(e)));try{const e=await(0,_tokenRefresh.refreshAccessToken)();return setAuthHeader(t,e),userClient(t)}catch(e){return Promise.reject(e)}}return t?.skipErrorLog||_index2.Logger.error(`Response error: ${(0,_responseBodyText.responseBodyText)(r.response?.data,r.message)}, URL: ${t?.url}, Status: ${r.response.status}`),Promise.reject(e)});var _default=exports.default=userClient;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.setRemoteLogger=exports.setDeviceContext=exports.getDeviceContext=exports.configureLogger=exports.Logger=void 0;var _index=require("../config/index.js"),_redact=require("./redact.js");const Colors={RESET:"[0m",GRAY:"[90m",BLUE:"[34m",YELLOW:"[33m",RED_BOLD:"[31;1m"},LogLevels={DEBUG:{level:"debug",color:Colors.GRAY},INFO:{level:"info",color:Colors.BLUE},WARN:{level:"warn",color:Colors.YELLOW},ERROR:{level:"error",color:Colors.RED_BOLD}},isLogLevelAllowed=e=>{const r=["debug","info","warn","error"],o=(0,_index.getConfigValue)("logLevel","info");return r.indexOf(e)>=r.indexOf(o)},isEnabled=()=>(0,_index.getConfigValue)("loggingEnabled",!0);let remote=null;const setRemoteLogger=e=>{remote=e};exports.setRemoteLogger=setRemoteLogger;const safe=e=>{if(e)try{e()}catch{}},toErrorObject=e=>{if(e instanceof Error)return e;if("string"==typeof e)return new Error(e);try{return new Error(JSON.stringify(e))}catch{return new Error(String(e))}};let deviceContext=null;const normalizeAttributeValue=e=>null==e?null:"string"==typeof e||"number"==typeof e||"boolean"==typeof e?e:String(e),setDeviceContext=e=>{if(deviceContext=e,e)for(const[r,o]of Object.entries(e))safe(()=>remote?.setAttribute?.(r,normalizeAttributeValue(o)))};exports.setDeviceContext=setDeviceContext;const getDeviceContext=()=>deviceContext;exports.getDeviceContext=getDeviceContext;const DEFAULT_LOGGER_OPTIONS={dedupeWindowMs:5e3,dedupeMaxPerWindow:1,screenHistoryLimit:20};let loggerOptions={...DEFAULT_LOGGER_OPTIONS};const configureLogger=e=>{loggerOptions={...loggerOptions,...e},screenHistory.length>loggerOptions.screenHistoryLimit&&(screenHistory=screenHistory.slice(screenHistory.length-loggerOptions.screenHistoryLimit))};exports.configureLogger=configureLogger;let currentScreen=null,screenHistory=[];const dedupeMap=new Map,MAX_DEDUPE_ENTRIES=200,dedupeKeyFor=e=>{const r=e.stack?.split("\n")[1]?.trim();return r?`${e.message}::${r}`:e.message},capDedupeMap=()=>{for(;dedupeMap.size>200;){const e=dedupeMap.keys().next().value;if(void 0===e)return;dedupeMap.delete(e)}},shouldForward=e=>{if(loggerOptions.dedupeWindowMs<=0)return{forward:!0};const r=Date.now(),o=dedupeMap.get(e);if(!o||r-o.windowStart>=loggerOptions.dedupeWindowMs){const t=o?.suppressedCount??0;return dedupeMap.set(e,{windowStart:r,count:1,suppressedCount:0}),capDedupeMap(),t>0?{forward:!0,suppressedCount:t}:{forward:!0}}return o.count+=1,o.count>loggerOptions.dedupeMaxPerWindow?(o.suppressedCount+=1,{forward:!1}):{forward:!0}},buildErrorContext=(e,r)=>{const o={...deviceContext};return null!==currentScreen&&(o.screen=currentScreen),r&&(o.suppressedCount=r),{...o,...e}},forwardErrorToRemote=(e,r)=>{const o=dedupeKeyFor(e),{forward:t,suppressedCount:s}=shouldForward(o);t&&safe(()=>remote?.recordError?.(e,buildErrorContext(r,s)))},logDebug=(e,...r)=>{isEnabled()&&isLogLevelAllowed(LogLevels.DEBUG.level)&&console.debug(`${LogLevels.DEBUG.color}[DEBUG]: ${(0,_redact.redactString)(e)}${Colors.RESET}`,...(0,_redact.redact)(r))},logInfo=(e,...r)=>{const o=(0,_redact.redactString)(e),t=(0,_redact.redact)(r);isEnabled()&&isLogLevelAllowed(LogLevels.INFO.level)&&(console.info(`${LogLevels.INFO.color}[INFO]: ${o}${Colors.RESET}`,...t),safe(()=>remote?.log?.("info",o,t)))},logWarning=(e,...r)=>{const o=(0,_redact.redactString)(e),t=(0,_redact.redact)(r);isEnabled()&&isLogLevelAllowed(LogLevels.WARN.level)&&(console.warn(`${LogLevels.WARN.color}[WARNING]: ${o}${Colors.RESET}`,...t),safe(()=>remote?.log?.("warn",o,t)))},logError=(e,...r)=>{const o=(0,_redact.redactString)(e),t=(0,_redact.redact)(r);isEnabled()&&isLogLevelAllowed(LogLevels.ERROR.level)&&console.error(`${LogLevels.ERROR.color}[ERROR]: ${o}${Colors.RESET}`,...t),safe(()=>remote?.log?.("error",o,t));const s=t[0];void 0!==s&&forwardErrorToRemote(toErrorObject(s),{source:"Logger.error",message:o})},breadcrumb=(e,r)=>{const o=(0,_redact.redactString)(e),t=(0,_redact.redact)(r);isEnabled()&&isLogLevelAllowed("info")&&console.info(`${Colors.BLUE}[BREADCRUMB]: ${o}${Colors.RESET}`,t??""),safe(()=>remote?.breadcrumb?.(o,t))},recordError=(e,r)=>{const o=(0,_redact.redact)(toErrorObject(e)),t=(0,_redact.redact)(r);isEnabled()&&isLogLevelAllowed("error")&&console.error(`${Colors.RED_BOLD}[RECORDED]: ${o.message}${Colors.RESET}`,t??""),forwardErrorToRemote(o,t)},asDict=e=>"object"==typeof e&&null!==e?e:null,headerValue=(e,r)=>{const o=asDict(e);if(!o)return null;const t="function"==typeof o.get?o.get.call(e,r):o[r]??o[r.toLowerCase()];return"string"==typeof t&&""!==t?t:null},requestFailed=(e,r={})=>{const{label:o,expectedStatuses:t=[],...s}=r,n=asDict(e),l=asDict(n?.config),c=asDict(n?.response),i=asDict(c?.data),d="number"==typeof c?.status?c.status:null,a="string"==typeof l?.method?l.method.toUpperCase():null,g="string"==typeof l?.url?l.url.split("?")[0]??null:null,u=o??([a,g].filter(Boolean).join(" ")||"Request");if("CanceledError"===n?.name||"ERR_CANCELED"===n?.code||!0===n?.__CANCEL__)return void logInfo(`${u} canceled`);if(null!==d&&t.includes(d))return void
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.setRemoteLogger=exports.setDeviceContext=exports.getDeviceContext=exports.configureLogger=exports.Logger=void 0;var _index=require("../config/index.js"),_redact=require("./redact.js");const Colors={RESET:"[0m",GRAY:"[90m",BLUE:"[34m",YELLOW:"[33m",RED_BOLD:"[31;1m"},LogLevels={DEBUG:{level:"debug",color:Colors.GRAY},INFO:{level:"info",color:Colors.BLUE},WARN:{level:"warn",color:Colors.YELLOW},ERROR:{level:"error",color:Colors.RED_BOLD}},isLogLevelAllowed=e=>{const r=["debug","info","warn","error"],o=(0,_index.getConfigValue)("logLevel","info");return r.indexOf(e)>=r.indexOf(o)},isEnabled=()=>(0,_index.getConfigValue)("loggingEnabled",!0);let remote=null;const setRemoteLogger=e=>{remote=e};exports.setRemoteLogger=setRemoteLogger;const safe=e=>{if(e)try{e()}catch{}},toErrorObject=e=>{if(e instanceof Error)return e;if("string"==typeof e)return new Error(e);try{return new Error(JSON.stringify(e))}catch{return new Error(String(e))}};let deviceContext=null;const normalizeAttributeValue=e=>null==e?null:"string"==typeof e||"number"==typeof e||"boolean"==typeof e?e:String(e),setDeviceContext=e=>{if(deviceContext=e,e)for(const[r,o]of Object.entries(e))safe(()=>remote?.setAttribute?.(r,normalizeAttributeValue(o)))};exports.setDeviceContext=setDeviceContext;const getDeviceContext=()=>deviceContext;exports.getDeviceContext=getDeviceContext;const DEFAULT_LOGGER_OPTIONS={dedupeWindowMs:5e3,dedupeMaxPerWindow:1,screenHistoryLimit:20};let loggerOptions={...DEFAULT_LOGGER_OPTIONS};const configureLogger=e=>{loggerOptions={...loggerOptions,...e},screenHistory.length>loggerOptions.screenHistoryLimit&&(screenHistory=screenHistory.slice(screenHistory.length-loggerOptions.screenHistoryLimit))};exports.configureLogger=configureLogger;let currentScreen=null,screenHistory=[];const dedupeMap=new Map,MAX_DEDUPE_ENTRIES=200,dedupeKeyFor=e=>{const r=e.stack?.split("\n")[1]?.trim();return r?`${e.message}::${r}`:e.message},capDedupeMap=()=>{for(;dedupeMap.size>200;){const e=dedupeMap.keys().next().value;if(void 0===e)return;dedupeMap.delete(e)}},shouldForward=e=>{if(loggerOptions.dedupeWindowMs<=0)return{forward:!0};const r=Date.now(),o=dedupeMap.get(e);if(!o||r-o.windowStart>=loggerOptions.dedupeWindowMs){const t=o?.suppressedCount??0;return dedupeMap.set(e,{windowStart:r,count:1,suppressedCount:0}),capDedupeMap(),t>0?{forward:!0,suppressedCount:t}:{forward:!0}}return o.count+=1,o.count>loggerOptions.dedupeMaxPerWindow?(o.suppressedCount+=1,{forward:!1}):{forward:!0}},buildErrorContext=(e,r)=>{const o={...deviceContext};return null!==currentScreen&&(o.screen=currentScreen),r&&(o.suppressedCount=r),{...o,...e}},forwardErrorToRemote=(e,r)=>{const o=dedupeKeyFor(e),{forward:t,suppressedCount:s}=shouldForward(o);t&&safe(()=>remote?.recordError?.(e,buildErrorContext(r,s)))},logDebug=(e,...r)=>{isEnabled()&&isLogLevelAllowed(LogLevels.DEBUG.level)&&console.debug(`${LogLevels.DEBUG.color}[DEBUG]: ${(0,_redact.redactString)(e)}${Colors.RESET}`,...(0,_redact.redact)(r))},logInfo=(e,...r)=>{const o=(0,_redact.redactString)(e),t=(0,_redact.redact)(r);isEnabled()&&isLogLevelAllowed(LogLevels.INFO.level)&&(console.info(`${LogLevels.INFO.color}[INFO]: ${o}${Colors.RESET}`,...t),safe(()=>remote?.log?.("info",o,t)))},logWarning=(e,...r)=>{const o=(0,_redact.redactString)(e),t=(0,_redact.redact)(r);isEnabled()&&isLogLevelAllowed(LogLevels.WARN.level)&&(console.warn(`${LogLevels.WARN.color}[WARNING]: ${o}${Colors.RESET}`,...t),safe(()=>remote?.log?.("warn",o,t)))},logError=(e,...r)=>{const o=(0,_redact.redactString)(e),t=(0,_redact.redact)(r);isEnabled()&&isLogLevelAllowed(LogLevels.ERROR.level)&&console.error(`${LogLevels.ERROR.color}[ERROR]: ${o}${Colors.RESET}`,...t),safe(()=>remote?.log?.("error",o,t));const s=t[0];void 0!==s&&forwardErrorToRemote(toErrorObject(s),{source:"Logger.error",message:o})},breadcrumb=(e,r)=>{const o=(0,_redact.redactString)(e),t=(0,_redact.redact)(r);isEnabled()&&isLogLevelAllowed("info")&&console.info(`${Colors.BLUE}[BREADCRUMB]: ${o}${Colors.RESET}`,t??""),safe(()=>remote?.breadcrumb?.(o,t))},recordError=(e,r)=>{const o=(0,_redact.redact)(toErrorObject(e)),t=(0,_redact.redact)(r);isEnabled()&&isLogLevelAllowed("error")&&console.error(`${Colors.RED_BOLD}[RECORDED]: ${o.message}${Colors.RESET}`,t??""),forwardErrorToRemote(o,t)},asDict=e=>"object"==typeof e&&null!==e?e:null,headerValue=(e,r)=>{const o=asDict(e);if(!o)return null;const t="function"==typeof o.get?o.get.call(e,r):o[r]??o[r.toLowerCase()];return"string"==typeof t&&""!==t?t:null},requestFailed=(e,r={})=>{const{label:o,expectedStatuses:t=[],...s}=r,n=asDict(e),l=asDict(n?.config),c=asDict(n?.response),i=asDict(c?.data),d="number"==typeof c?.status?c.status:null,a="string"==typeof l?.method?l.method.toUpperCase():null,g="string"==typeof l?.url?l.url.split("?")[0]??null:null,u=o??([a,g].filter(Boolean).join(" ")||"Request");if("CanceledError"===n?.name||"ERR_CANCELED"===n?.code||!0===n?.__CANCEL__)return void logInfo(`${u} canceled`);if(null!==d&&t.includes(d))return void logInfo(`${u} refused (${d})`);const p="string"==typeof n?.message?n.message:e instanceof Error?e.message:null,E=(0,_redact.redactString)(null===d?`${u} failed`:`${u} failed (${d})`);isEnabled()&&isLogLevelAllowed(LogLevels.ERROR.level)&&console.error(`${LogLevels.ERROR.color}[ERROR]: ${E}${Colors.RESET}`,(0,_redact.redactString)(p??"")),safe(()=>remote?.log?.("error",E,[]));const f=(0,_index.getConfigValue)("correlationIdHeader",null),L=(0,_redact.redact)({source:"Logger.requestFailed",label:u,status:d,method:a,path:g,code:"string"==typeof i?.code?i.code:null,traceId:"string"==typeof i?.traceId?i.traceId:null,correlationId:f?headerValue(l?.headers,f):null,detail:p,...s});forwardErrorToRemote((0,_redact.redact)(new Error(E)),L)},setUser=(e,r)=>{safe(()=>remote?.setUser?.(e,r))},setAttribute=(e,r)=>{safe(()=>remote?.setAttribute?.(e,r))},trackScreen=(e,r)=>{e!==currentScreen&&(currentScreen=e,screenHistory.push(e),screenHistory.length>loggerOptions.screenHistoryLimit&&(screenHistory=screenHistory.slice(screenHistory.length-loggerOptions.screenHistoryLimit)),breadcrumb(`Screen: ${e}`,r))},getCurrentScreen=()=>currentScreen,getScreenHistory=()=>[...screenHistory],Logger=exports.Logger={debug:logDebug,info:logInfo,warn:logWarning,error:logError,breadcrumb:breadcrumb,recordError:recordError,requestFailed:requestFailed,setUser:setUser,setAttribute:setAttribute,trackScreen:trackScreen,getCurrentScreen:getCurrentScreen,getScreenHistory:getScreenHistory};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),Object.defineProperty(exports,"PUSH_STATUS",{enumerable:!0,get:function(){return _status.PUSH_STATUS}}),Object.defineProperty(exports,"disablePush",{enumerable:!0,get:function(){return _push.disablePush}}),Object.defineProperty(exports,"enablePush",{enumerable:!0,get:function(){return _push.enablePush}}),Object.defineProperty(exports,"initPush",{enumerable:!0,get:function(){return _push.initPush}}),Object.defineProperty(exports,"notifyLocal",{enumerable:!0,get:function(){return _push.notifyLocal}}),Object.defineProperty(exports,"onAction",{enumerable:!0,get:function(){return _push.onAction}}),Object.defineProperty(exports,"setBadgeCount",{enumerable:!0,get:function(){return _push.setBadgeCount}}),Object.defineProperty(exports,"setCategories",{enumerable:!0,get:function(){return _push.setCategories}}),Object.defineProperty(exports,"setPushImplementation",{enumerable:!0,get:function(){return _push.setPushImplementation}}),Object.defineProperty(exports,"usePush",{enumerable:!0,get:function(){return _push.usePush}});var _push=require("./push.js"),_status=require("./status.js");
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),Object.defineProperty(exports,"PUSH_STATUS",{enumerable:!0,get:function(){return _status.PUSH_STATUS}}),Object.defineProperty(exports,"disablePush",{enumerable:!0,get:function(){return _push.disablePush}}),Object.defineProperty(exports,"enablePush",{enumerable:!0,get:function(){return _push.enablePush}}),Object.defineProperty(exports,"initPush",{enumerable:!0,get:function(){return _push.initPush}}),Object.defineProperty(exports,"notifyLocal",{enumerable:!0,get:function(){return _push.notifyLocal}}),Object.defineProperty(exports,"onAction",{enumerable:!0,get:function(){return _push.onAction}}),Object.defineProperty(exports,"onReceive",{enumerable:!0,get:function(){return _push.onReceive}}),Object.defineProperty(exports,"setBadgeCount",{enumerable:!0,get:function(){return _push.setBadgeCount}}),Object.defineProperty(exports,"setCategories",{enumerable:!0,get:function(){return _push.setCategories}}),Object.defineProperty(exports,"setPushImplementation",{enumerable:!0,get:function(){return _push.setPushImplementation}}),Object.defineProperty(exports,"usePush",{enumerable:!0,get:function(){return _push.usePush}});var _push=require("./push.js"),_status=require("./status.js");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.usePush=exports.toPushAction=exports.toNotificationActions=exports.setPushImplementation=exports.setCategories=exports.setBadgeCount=exports.routeTaskPayload=exports.resolveModules=exports.onAction=exports.notifyLocal=exports.initPush=exports.enablePush=exports.disablePush=void 0;var _react=require("react"),_reactNative=require("react-native"),_index=require("../logger/index.js");let resolved,override=null;const setPushImplementation=e=>{override=e};exports.setPushImplementation=setPushImplementation;const resolveModules=()=>{if(override)return override;if(void 0!==resolved)return resolved;try{if(require("expo").isRunningInExpoGo())return resolved=null,resolved;const e=require("expo-notifications"),t=require("expo-application");resolved={notifications:e,applicationId:t.applicationId}}catch(e){_index.Logger.warn("[push] expo-notifications is not available; push is off",e),resolved=null}return resolved};exports.resolveModules=resolveModules;let state={status:"unknown",enabled:!1};const subscribers=new Set,setState=e=>{state={...state,...e};for(const e of subscribers)e()};let config=null,channelsReady=Promise.resolve(),tokenSubscription=null,actionHandler=null
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.usePush=exports.toPushReceived=exports.toPushAction=exports.toNotificationActions=exports.setPushImplementation=exports.setCategories=exports.setBadgeCount=exports.routeTaskPayload=exports.resolveModules=exports.onReceive=exports.onAction=exports.notifyLocal=exports.initPush=exports.enablePush=exports.disablePush=void 0;var _react=require("react"),_reactNative=require("react-native"),_index=require("../logger/index.js");let resolved,override=null;const setPushImplementation=e=>{override=e};exports.setPushImplementation=setPushImplementation;const resolveModules=()=>{if(override)return override;if(void 0!==resolved)return resolved;try{if(require("expo").isRunningInExpoGo())return resolved=null,resolved;const e=require("expo-notifications"),t=require("expo-application");resolved={notifications:e,applicationId:t.applicationId,getApnsEnvironment:()=>t.getIosPushNotificationServiceEnvironmentAsync()}}catch(e){_index.Logger.warn("[push] expo-notifications is not available; push is off",e),resolved=null}return resolved};exports.resolveModules=resolveModules;let state={status:"unknown",enabled:!1};const subscribers=new Set,setState=e=>{state={...state,...e};for(const e of subscribers)e()};let config=null,channelsReady=Promise.resolve(),tokenSubscription=null,actionHandler=null;const receiveHandlers=new Set;let listening=!1,listeningForReceived=!1;const pendingResponses=[],routedAt=new Map,REPEAT_WINDOW_MS=1e4,requireConfig=()=>{if(!config)throw new Error("[@webority/mobile-core] call initPush before enablePush or disablePush.");return config},toPermissionStatus=e=>e.granted?"granted":"denied"===e.status?"denied":"unknown",toStringData=e=>{const t={};for(const[n,o]of Object.entries(e??{}))null!=o&&(t[n]="object"==typeof o?JSON.stringify(o):String(o));return t},contentData=e=>{const t=e.request.content;if(t.data)return toStringData(t.data);if("string"==typeof t.dataString)try{return toStringData(JSON.parse(t.dataString))}catch(e){_index.Logger.error("[push] notification data is not JSON",e)}return{}},triggerData=e=>{const t=e.request.trigger,n={...t?.remoteMessage?.data??{}};for(const[e,o]of Object.entries(t?.payload??{}))"aps"===e||"body"===e&&"object"==typeof o||(n[e]=o);return toStringData(n)},notificationData=e=>({...triggerData(e),...contentData(e)}),responseData=e=>notificationData(e.notification),toPushReceived=e=>({notificationId:e.request.identifier,title:e.request.content.title??null,body:e.request.content.body??null,data:notificationData(e)});exports.toPushReceived=toPushReceived;const toPushAction=e=>({actionId:e.actionIdentifier,...e.userText?{userText:e.userText}:{},data:responseData(e),notificationId:e.notification.request.identifier});exports.toPushAction=toPushAction;const toNotificationActions=e=>e.map(e=>({identifier:e.id,buttonTitle:e.title,...e.textInput?{textInput:{submitButtonTitle:e.textInput.submitTitle,placeholder:e.textInput.placeholder}}:{},options:{opensAppToForeground:e.opensApp??!0,isDestructive:e.destructive??!1,isAuthenticationRequired:e.authenticationRequired??!1}}));exports.toNotificationActions=toNotificationActions;const routeResponse=async(e,t)=>{const n=`${t.notification.request.identifier}:${t.actionIdentifier}`,o=routedAt.get(n);if(void 0!==o&&Date.now()-o<1e4)return;if(t.actionIdentifier!==e.DEFAULT_ACTION_IDENTIFIER&&actionHandler){routedAt.set(n,Date.now());try{await actionHandler(toPushAction(t))}catch(e){_index.Logger.error("[push] onAction threw",e)}}else if(config){routedAt.set(n,Date.now());try{config.onOpen(responseData(t))}catch(e){_index.Logger.error("[push] onOpen threw",e)}}else pendingResponses.push(t)},drainPending=async e=>{const t=pendingResponses.splice(0);for(const n of t)await routeResponse(e,n)},listenForResponses=e=>{listening||(listening=!0,e.addNotificationResponseReceivedListener(t=>{routeResponse(e,t)}),e.getLastNotificationResponseAsync().then(async t=>(t&&await routeResponse(e,t),e.clearLastNotificationResponseAsync?.())).catch(e=>_index.Logger.error("[push] reading the launch notification failed",e)))},routeTaskPayload=async e=>{const t=resolveModules(),n=e;t&&"string"==typeof n?.actionIdentifier&&"string"==typeof n.notification?.request?.identifier&&await routeResponse(t.notifications,n)};exports.routeTaskPayload=routeTaskPayload;const createChannels=async(e,t)=>{for(const n of t)try{await e.setNotificationChannelAsync(n.id,{name:n.name,importance:"high"===n.importance?e.AndroidImportance.HIGH:e.AndroidImportance.DEFAULT})}catch(e){_index.Logger.error(`[push] creating channel ${n.id} failed`,e)}},APNS_ENVIRONMENTS={development:"Development",production:"Production"},readApnsEnvironment=async e=>{try{return APNS_ENVIRONMENTS[await e.getApnsEnvironment()??""]??null}catch(e){return _index.Logger.warn("[push] reading the APNs environment failed",e),null}},toDevice=async(e,t)=>{const n="android"===e.type?"Fcm":"ios"===e.type?"Apns":null;if(!n||"string"!=typeof e.data)return null;if(!t.applicationId)throw new Error("[@webority/mobile-core] push needs the app bundle id from expo-application.");return{installationId:await requireConfig().getInstallationId(),platform:n,token:e.data,appId:t.applicationId,apnsEnvironment:"Apns"===n?await readApnsEnvironment(t):null}},initPush=e=>{const t=null===config;if(config=e,!t)return;const n=resolveModules();if(!n)return void setState({status:"unavailable"});const o=n.notifications;o.setNotificationHandler({handleNotification:async()=>{const e=config?.showInForeground?.()??!0;return{shouldShowBanner:e,shouldShowList:e,shouldPlaySound:e&&(config?.playSoundInForeground?.()??!0),shouldSetBadge:!0}}}),"android"===_reactNative.Platform.OS&&e.androidChannels?.length&&(channelsReady=createChannels(o,e.androidChannels)),listenForResponses(o),drainPending(o),o.getPermissionsAsync().then(e=>setState({status:toPermissionStatus(e)})).catch(e=>_index.Logger.error("[push] permission check failed",e))};exports.initPush=initPush;let registering=null,registeredKey=null;const registerOnce=e=>{const t=JSON.stringify(e);if(registering?.key===t)return registering.promise;const n=requireConfig().register(e).then(()=>{registeredKey=t}).finally(()=>{registering?.promise===n&&(registering=null)});return registering={key:t,promise:n},n},enablePush=async({prompt:e=!0}={})=>{requireConfig();const t=resolveModules();if(!t)return setState({status:"unavailable"}),"unavailable";const n=t.notifications;await channelsReady;let o=await n.getPermissionsAsync();if(!o.granted&&e&&(o=await n.requestPermissionsAsync()),!o.granted){const e=toPermissionStatus(o);return setState({status:e}),e}const i=await toDevice(await n.getDevicePushTokenAsync(),t);return i?(await registerOnce(i),tokenSubscription?.remove(),tokenSubscription=n.addPushTokenListener(e=>{toDevice(e,t).then(e=>e&&JSON.stringify(e)!==registeredKey?registerOnce(e):void 0).catch(e=>_index.Logger.error("[push] re-registering a new token failed",e))}),setState({status:"granted",enabled:!0}),"granted"):(setState({status:"unavailable"}),"unavailable")};exports.enablePush=enablePush;const disablePush=async()=>{const e=requireConfig();tokenSubscription?.remove(),tokenSubscription=null,registeredKey=null,setState({enabled:!1}),await e.unregister(await e.getInstallationId())};exports.disablePush=disablePush;const setBadgeCount=async e=>{const t=resolveModules();t&&await t.notifications.setBadgeCountAsync(e)};exports.setBadgeCount=setBadgeCount;const setCategories=async e=>{const t=resolveModules();if(t)for(const n of e)await t.notifications.setNotificationCategoryAsync(n.id,toNotificationActions(n.actions))};exports.setCategories=setCategories;const onAction=e=>{actionHandler=e;const t=resolveModules();return t&&(listenForResponses(t.notifications),drainPending(t.notifications)),()=>{actionHandler===e&&(actionHandler=null)}};exports.onAction=onAction;const onReceive=e=>{receiveHandlers.add(e);const t=resolveModules();return t&&!listeningForReceived&&(listeningForReceived=!0,t.notifications.addNotificationReceivedListener(e=>{const t=toPushReceived(e);for(const e of[...receiveHandlers])try{e(t)}catch(e){_index.Logger.error("[push] onReceive threw",e)}})),()=>{receiveHandlers.delete(e)}};exports.onReceive=onReceive;const notifyLocal=async({title:e,body:t,data:n})=>{const o=resolveModules();o&&await o.notifications.scheduleNotificationAsync({content:{title:e,body:t,data:n??{}},trigger:null})};exports.notifyLocal=notifyLocal;const subscribe=e=>(subscribers.add(e),()=>{subscribers.delete(e)}),getSnapshot=()=>state,enable=async()=>{await enablePush()},usePush=()=>({...(0,_react.useSyncExternalStore)(subscribe,getSnapshot,getSnapshot),enable:enable,disable:disablePush});exports.usePush=usePush;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import axios from"axios";import{getConfigValue}from"../config/index.js";import{Logger}from"../logger/index.js";import"../types/axios.js";import{applyConfiguredBaseUrl}from"./baseUrl.js";import{applyClientHeaders}from"./clientHeaders.js";import{logCurl}from"./curl.js";import{readMinAppVersion}from"./minAppVersion.js";import{sendFormDataAsMultipart}from"./multipart.js";import{applyRequestDefaults}from"./requestDefaults.js";import{responseBodyText}from"./responseBodyText.js";import{attachRetry}from"./retry.js";const publicClient=axios.create({headers:{"Content-Type":"application/json",Accept:"application/json"}});applyConfiguredBaseUrl(publicClient),applyRequestDefaults(publicClient),sendFormDataAsMultipart(publicClient),readMinAppVersion(publicClient),attachRetry(publicClient),publicClient.interceptors.request.use(e=>(Logger.info(`Request: ${e.method?.toUpperCase()} ${e.url}`),getConfigValue("logCurl")&&logCurl(e),e),e=>{const r=e instanceof Error?e.message:String(e);return Logger.error("Request error:",r),Promise.reject(e)}),applyClientHeaders(publicClient),publicClient.interceptors.response.use(e=>(Logger.info(`Response: ${e.status} from ${e.config.url}`),e),e=>{if(axios.isAxiosError(e)){const r=e.config;if(r?.skipErrorLog)return Promise.reject(e);e.response?Logger.error(`Response error: ${responseBodyText(e.response.data,e.message)}, URL: ${r?.url}, Status: ${e.response.status}`):Logger.error(`Network error or no response from server: ${e.message}, URL: ${r?.url}`)}else{const r=e instanceof Error?e.message:String(e);Logger.error(`Unknown response error: ${r}`)}return Promise.reject(e)});export default publicClient;
|
|
1
|
+
"use strict";import axios from"axios";import{getConfigValue}from"../config/index.js";import{Logger}from"../logger/index.js";import"../types/axios.js";import{applyConfiguredBaseUrl}from"./baseUrl.js";import{applyClientHeaders}from"./clientHeaders.js";import{logCurl}from"./curl.js";import{readMinAppVersion}from"./minAppVersion.js";import{sendFormDataAsMultipart}from"./multipart.js";import{applyRequestDefaults}from"./requestDefaults.js";import{responseBodyText}from"./responseBodyText.js";import{attachRetry}from"./retry.js";const publicClient=axios.create({headers:{"Content-Type":"application/json",Accept:"application/json"}});applyConfiguredBaseUrl(publicClient),applyRequestDefaults(publicClient),sendFormDataAsMultipart(publicClient),readMinAppVersion(publicClient),attachRetry(publicClient),publicClient.interceptors.request.use(e=>(Logger.info(`Request: ${e.method?.toUpperCase()} ${e.url}`),getConfigValue("logCurl")&&logCurl(e),e),e=>{const r=e instanceof Error?e.message:String(e);return Logger.error("Request error:",r),Promise.reject(e)}),applyClientHeaders(publicClient),publicClient.interceptors.response.use(e=>(Logger.info(`Response: ${e.status} from ${e.config.url}`),e),e=>{if(axios.isCancel(e))return Promise.reject(e);if(axios.isAxiosError(e)){const r=e.config;if(r?.skipErrorLog)return Promise.reject(e);e.response?Logger.error(`Response error: ${responseBodyText(e.response.data,e.message)}, URL: ${r?.url}, Status: ${e.response.status}`):Logger.error(`Network error or no response from server: ${e.message}, URL: ${r?.url}`)}else{const r=e instanceof Error?e.message:String(e);Logger.error(`Unknown response error: ${r}`)}return Promise.reject(e)});export default publicClient;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import axios,{AxiosHeaders}from"axios";import{ACCESS_TOKEN_KEY,getToken}from"../auth/authStore.js";import{getConfigValue}from"../config/index.js";import{Logger}from"../logger/index.js";import{getLastKnownNetworkStatus}from"../network/networkStatus.js";import"../types/axios.js";import{applyConfiguredBaseUrl}from"./baseUrl.js";import{applyAccountIdHeader,applyClientHeaders}from"./clientHeaders.js";import{logCurl}from"./curl.js";import{readMinAppVersion}from"./minAppVersion.js";import{sendFormDataAsMultipart}from"./multipart.js";import{applyRequestDefaults}from"./requestDefaults.js";import{responseBodyText}from"./responseBodyText.js";import{attachRetry}from"./retry.js";import{endSessionAndLogout,getAccountIdProvider,getErrorMessage,getRefreshPath,isRefreshInFlight,queueBehindRefresh,readSignOutReason,refreshAccessToken,resetRefreshState}from"./tokenRefresh.js";export{injectStore,injectUserAuth,resetRefreshState,SIGN_OUT_REASON_HEADER}from"./tokenRefresh.js";export class RequestOfflineError extends Error{constructor(e="Request blocked: device is offline"){super(e),this.name="RequestOfflineError",Object.setPrototypeOf(this,RequestOfflineError.prototype)}}export const SESSION_TERMINATION_CODES={TERMINATED:"SESSION_TERMINATED",EXPIRED:"SESSION_EXPIRED"};const TERMINATION_CODES=new Set(Object.values(SESSION_TERMINATION_CODES));export const SESSION_INVALID_HEADER="x-security-stamp-invalid";const hasSessionInvalidHeader=e=>{if(!e||"object"!=typeof e)return!1;const r=e.get,t="function"==typeof r?r.call(e,SESSION_INVALID_HEADER):e[SESSION_INVALID_HEADER];return"true"===String(t).toLowerCase()},getSessionTerminationCode=e=>{if(!e||"object"!=typeof e)return;const r=e,t=r.error,s=[r.code,r.errorCode,t&&"object"==typeof t?t.code:void 0];for(const e of s)if("string"==typeof e&&TERMINATION_CODES.has(e))return e},userClient=axios.create({headers:{"Content-Type":"application/json"}});readMinAppVersion(userClient),attachRetry(userClient);const setAuthHeader=(e,r)=>{e.headers instanceof AxiosHeaders?e.headers.set("Authorization",`Bearer ${r}`):e.headers=AxiosHeaders.from({...e.headers,Authorization:`Bearer ${r}`})},isRefreshTokenRequest=e=>{const r=getRefreshPath();return!!r&&!!e?.includes(r)};applyConfiguredBaseUrl(userClient),applyRequestDefaults(userClient),sendFormDataAsMultipart(userClient),userClient.interceptors.request.use(e=>(getConfigValue("logCurl")&&logCurl(e),e)),applyClientHeaders(userClient),userClient.interceptors.request.use(async e=>{if(!1===(await getLastKnownNetworkStatus()).isConnected)return Logger.warn(`Request blocked: device is offline. URL: ${e.url}`),Promise.reject(new RequestOfflineError);try{if(isRefreshTokenRequest(e.url))return e;const r=await getToken(ACCESS_TOKEN_KEY);if(!r?.token)throw new
|
|
1
|
+
"use strict";import axios,{AxiosHeaders}from"axios";import{ACCESS_TOKEN_KEY,getToken}from"../auth/authStore.js";import{getConfigValue}from"../config/index.js";import{Logger}from"../logger/index.js";import{getLastKnownNetworkStatus}from"../network/networkStatus.js";import"../types/axios.js";import{applyConfiguredBaseUrl}from"./baseUrl.js";import{applyAccountIdHeader,applyClientHeaders}from"./clientHeaders.js";import{logCurl}from"./curl.js";import{readMinAppVersion}from"./minAppVersion.js";import{sendFormDataAsMultipart}from"./multipart.js";import{applyRequestDefaults}from"./requestDefaults.js";import{responseBodyText}from"./responseBodyText.js";import{attachRetry}from"./retry.js";import{endSessionAndLogout,getAccountIdProvider,getErrorMessage,getRefreshPath,isRefreshInFlight,queueBehindRefresh,readSignOutReason,refreshAccessToken,resetRefreshState}from"./tokenRefresh.js";export{injectStore,injectUserAuth,resetRefreshState,SIGN_OUT_REASON_HEADER}from"./tokenRefresh.js";export class RequestOfflineError extends Error{constructor(e="Request blocked: device is offline"){super(e),this.name="RequestOfflineError",Object.setPrototypeOf(this,RequestOfflineError.prototype)}}class NoAccessTokenError extends Error{constructor(){super("No access token available"),this.name="NoAccessTokenError",Object.setPrototypeOf(this,NoAccessTokenError.prototype)}}export const SESSION_TERMINATION_CODES={TERMINATED:"SESSION_TERMINATED",EXPIRED:"SESSION_EXPIRED"};const TERMINATION_CODES=new Set(Object.values(SESSION_TERMINATION_CODES));export const SESSION_INVALID_HEADER="x-security-stamp-invalid";const hasSessionInvalidHeader=e=>{if(!e||"object"!=typeof e)return!1;const r=e.get,t="function"==typeof r?r.call(e,SESSION_INVALID_HEADER):e[SESSION_INVALID_HEADER];return"true"===String(t).toLowerCase()},getSessionTerminationCode=e=>{if(!e||"object"!=typeof e)return;const r=e,t=r.error,s=[r.code,r.errorCode,t&&"object"==typeof t?t.code:void 0];for(const e of s)if("string"==typeof e&&TERMINATION_CODES.has(e))return e},userClient=axios.create({headers:{"Content-Type":"application/json"}});readMinAppVersion(userClient),attachRetry(userClient);const setAuthHeader=(e,r)=>{e.headers instanceof AxiosHeaders?e.headers.set("Authorization",`Bearer ${r}`):e.headers=AxiosHeaders.from({...e.headers,Authorization:`Bearer ${r}`})},isRefreshTokenRequest=e=>{const r=getRefreshPath();return!!r&&!!e?.includes(r)};applyConfiguredBaseUrl(userClient),applyRequestDefaults(userClient),sendFormDataAsMultipart(userClient),userClient.interceptors.request.use(e=>(getConfigValue("logCurl")&&logCurl(e),e)),applyClientHeaders(userClient),userClient.interceptors.request.use(async e=>{if(!1===(await getLastKnownNetworkStatus()).isConnected)return Logger.warn(`Request blocked: device is offline. URL: ${e.url}`),Promise.reject(new RequestOfflineError);try{if(isRefreshTokenRequest(e.url))return e;const r=await getToken(ACCESS_TOKEN_KEY);if(!r?.token)throw new NoAccessTokenError;const t=Date.now(),s=Date.parse(r.expiry);if(s<=t+3e4){Logger.info("Access token expired or about to expire, refreshing before request");const r=await refreshAccessToken();return setAuthHeader(e,r),await applyAccountIdHeader(e.headers,getAccountIdProvider()),e}return setAuthHeader(e,r.token),await applyAccountIdHeader(e.headers,getAccountIdProvider()),e}catch(r){return r instanceof NoAccessTokenError?Logger.info(`Request not sent: signed out. URL: ${e.url}`):Logger.error("Failed to attach token:",getErrorMessage(r)),Promise.reject(r)}},e=>(Logger.error("Request error:",getErrorMessage(e)),Promise.reject(e))),userClient.interceptors.response.use(e=>e,async e=>{if(axios.isCancel(e))return Promise.reject(e);if(e instanceof RequestOfflineError||e instanceof NoAccessTokenError)return Promise.reject(e);if(!axios.isAxiosError(e))return Logger.error(`Non-axios error in response interceptor: ${getErrorMessage(e)}`),Promise.reject(e);const r=e,t=r.config;if(!r.response)return t?.skipErrorLog||Logger.error(`Network error or no response from server: ${r.message}, URL: ${t?.url}`),Promise.reject(e);if(401===r.response.status&&t&&!t._retry){if(isRefreshTokenRequest(t.url))return Promise.reject(e);const s=readSignOutReason(r.response.headers),o=getSessionTerminationCode(r.response.data)??(hasSessionInvalidHeader(r.response.headers)||null!==s?SESSION_TERMINATION_CODES.TERMINATED:void 0);if(o)return Logger.warn(`Session ${o}, logging out without refresh. URL: ${t.url}`),resetRefreshState(),await endSessionAndLogout("terminated",s),Promise.reject(e);if(Logger.warn(`Unauthorized URL: ${t.url}`),t._retry=!0,isRefreshInFlight())return queueBehindRefresh().then(e=>(Logger.info("Retrying request after token refresh:",t.url),setAuthHeader(t,e),userClient(t))).catch(e=>(Logger.error("Failed to retry request after token refresh:",getErrorMessage(e)),Promise.reject(e)));try{const e=await refreshAccessToken();return setAuthHeader(t,e),userClient(t)}catch(e){return Promise.reject(e)}}return t?.skipErrorLog||Logger.error(`Response error: ${responseBodyText(r.response?.data,r.message)}, URL: ${t?.url}, Status: ${r.response.status}`),Promise.reject(e)});export default userClient;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{getConfigValue}from"../config/index.js";import{redact,redactString}from"./redact.js";const Colors={RESET:"[0m",GRAY:"[90m",BLUE:"[34m",YELLOW:"[33m",RED_BOLD:"[31;1m"},LogLevels={DEBUG:{level:"debug",color:Colors.GRAY},INFO:{level:"info",color:Colors.BLUE},WARN:{level:"warn",color:Colors.YELLOW},ERROR:{level:"error",color:Colors.RED_BOLD}},isLogLevelAllowed=e=>{const r=["debug","info","warn","error"],o=getConfigValue("logLevel","info");return r.indexOf(e)>=r.indexOf(o)},isEnabled=()=>getConfigValue("loggingEnabled",!0);let remote=null;export const setRemoteLogger=e=>{remote=e};const safe=e=>{if(e)try{e()}catch{}},toErrorObject=e=>{if(e instanceof Error)return e;if("string"==typeof e)return new Error(e);try{return new Error(JSON.stringify(e))}catch{return new Error(String(e))}};let deviceContext=null;const normalizeAttributeValue=e=>null==e?null:"string"==typeof e||"number"==typeof e||"boolean"==typeof e?e:String(e);export const setDeviceContext=e=>{if(deviceContext=e,e)for(const[r,o]of Object.entries(e))safe(()=>remote?.setAttribute?.(r,normalizeAttributeValue(o)))};export const getDeviceContext=()=>deviceContext;const DEFAULT_LOGGER_OPTIONS={dedupeWindowMs:5e3,dedupeMaxPerWindow:1,screenHistoryLimit:20};let loggerOptions={...DEFAULT_LOGGER_OPTIONS};export const configureLogger=e=>{loggerOptions={...loggerOptions,...e},screenHistory.length>loggerOptions.screenHistoryLimit&&(screenHistory=screenHistory.slice(screenHistory.length-loggerOptions.screenHistoryLimit))};let currentScreen=null,screenHistory=[];const dedupeMap=new Map,MAX_DEDUPE_ENTRIES=200,dedupeKeyFor=e=>{const r=e.stack?.split("\n")[1]?.trim();return r?`${e.message}::${r}`:e.message},capDedupeMap=()=>{for(;dedupeMap.size>200;){const e=dedupeMap.keys().next().value;if(void 0===e)return;dedupeMap.delete(e)}},shouldForward=e=>{if(loggerOptions.dedupeWindowMs<=0)return{forward:!0};const r=Date.now(),o=dedupeMap.get(e);if(!o||r-o.windowStart>=loggerOptions.dedupeWindowMs){const t=o?.suppressedCount??0;return dedupeMap.set(e,{windowStart:r,count:1,suppressedCount:0}),capDedupeMap(),t>0?{forward:!0,suppressedCount:t}:{forward:!0}}return o.count+=1,o.count>loggerOptions.dedupeMaxPerWindow?(o.suppressedCount+=1,{forward:!1}):{forward:!0}},buildErrorContext=(e,r)=>{const o={...deviceContext};return null!==currentScreen&&(o.screen=currentScreen),r&&(o.suppressedCount=r),{...o,...e}},forwardErrorToRemote=(e,r)=>{const o=dedupeKeyFor(e),{forward:t,suppressedCount:n}=shouldForward(o);t&&safe(()=>remote?.recordError?.(e,buildErrorContext(r,n)))},logDebug=(e,...r)=>{isEnabled()&&isLogLevelAllowed(LogLevels.DEBUG.level)&&console.debug(`${LogLevels.DEBUG.color}[DEBUG]: ${redactString(e)}${Colors.RESET}`,...redact(r))},logInfo=(e,...r)=>{const o=redactString(e),t=redact(r);isEnabled()&&isLogLevelAllowed(LogLevels.INFO.level)&&(console.info(`${LogLevels.INFO.color}[INFO]: ${o}${Colors.RESET}`,...t),safe(()=>remote?.log?.("info",o,t)))},logWarning=(e,...r)=>{const o=redactString(e),t=redact(r);isEnabled()&&isLogLevelAllowed(LogLevels.WARN.level)&&(console.warn(`${LogLevels.WARN.color}[WARNING]: ${o}${Colors.RESET}`,...t),safe(()=>remote?.log?.("warn",o,t)))},logError=(e,...r)=>{const o=redactString(e),t=redact(r);isEnabled()&&isLogLevelAllowed(LogLevels.ERROR.level)&&console.error(`${LogLevels.ERROR.color}[ERROR]: ${o}${Colors.RESET}`,...t),safe(()=>remote?.log?.("error",o,t));const n=t[0];void 0!==n&&forwardErrorToRemote(toErrorObject(n),{source:"Logger.error",message:o})},breadcrumb=(e,r)=>{const o=redactString(e),t=redact(r);isEnabled()&&isLogLevelAllowed("info")&&console.info(`${Colors.BLUE}[BREADCRUMB]: ${o}${Colors.RESET}`,t??""),safe(()=>remote?.breadcrumb?.(o,t))},recordError=(e,r)=>{const o=redact(toErrorObject(e)),t=redact(r);isEnabled()&&isLogLevelAllowed("error")&&console.error(`${Colors.RED_BOLD}[RECORDED]: ${o.message}${Colors.RESET}`,t??""),forwardErrorToRemote(o,t)},asDict=e=>"object"==typeof e&&null!==e?e:null,headerValue=(e,r)=>{const o=asDict(e);if(!o)return null;const t="function"==typeof o.get?o.get.call(e,r):o[r]??o[r.toLowerCase()];return"string"==typeof t&&""!==t?t:null},requestFailed=(e,r={})=>{const{label:o,expectedStatuses:t=[],...n}=r,s=asDict(e),l=asDict(s?.config),c=asDict(s?.response),i=asDict(c?.data),d="number"==typeof c?.status?c.status:null,a="string"==typeof l?.method?l.method.toUpperCase():null,
|
|
1
|
+
"use strict";import{getConfigValue}from"../config/index.js";import{redact,redactString}from"./redact.js";const Colors={RESET:"[0m",GRAY:"[90m",BLUE:"[34m",YELLOW:"[33m",RED_BOLD:"[31;1m"},LogLevels={DEBUG:{level:"debug",color:Colors.GRAY},INFO:{level:"info",color:Colors.BLUE},WARN:{level:"warn",color:Colors.YELLOW},ERROR:{level:"error",color:Colors.RED_BOLD}},isLogLevelAllowed=e=>{const r=["debug","info","warn","error"],o=getConfigValue("logLevel","info");return r.indexOf(e)>=r.indexOf(o)},isEnabled=()=>getConfigValue("loggingEnabled",!0);let remote=null;export const setRemoteLogger=e=>{remote=e};const safe=e=>{if(e)try{e()}catch{}},toErrorObject=e=>{if(e instanceof Error)return e;if("string"==typeof e)return new Error(e);try{return new Error(JSON.stringify(e))}catch{return new Error(String(e))}};let deviceContext=null;const normalizeAttributeValue=e=>null==e?null:"string"==typeof e||"number"==typeof e||"boolean"==typeof e?e:String(e);export const setDeviceContext=e=>{if(deviceContext=e,e)for(const[r,o]of Object.entries(e))safe(()=>remote?.setAttribute?.(r,normalizeAttributeValue(o)))};export const getDeviceContext=()=>deviceContext;const DEFAULT_LOGGER_OPTIONS={dedupeWindowMs:5e3,dedupeMaxPerWindow:1,screenHistoryLimit:20};let loggerOptions={...DEFAULT_LOGGER_OPTIONS};export const configureLogger=e=>{loggerOptions={...loggerOptions,...e},screenHistory.length>loggerOptions.screenHistoryLimit&&(screenHistory=screenHistory.slice(screenHistory.length-loggerOptions.screenHistoryLimit))};let currentScreen=null,screenHistory=[];const dedupeMap=new Map,MAX_DEDUPE_ENTRIES=200,dedupeKeyFor=e=>{const r=e.stack?.split("\n")[1]?.trim();return r?`${e.message}::${r}`:e.message},capDedupeMap=()=>{for(;dedupeMap.size>200;){const e=dedupeMap.keys().next().value;if(void 0===e)return;dedupeMap.delete(e)}},shouldForward=e=>{if(loggerOptions.dedupeWindowMs<=0)return{forward:!0};const r=Date.now(),o=dedupeMap.get(e);if(!o||r-o.windowStart>=loggerOptions.dedupeWindowMs){const t=o?.suppressedCount??0;return dedupeMap.set(e,{windowStart:r,count:1,suppressedCount:0}),capDedupeMap(),t>0?{forward:!0,suppressedCount:t}:{forward:!0}}return o.count+=1,o.count>loggerOptions.dedupeMaxPerWindow?(o.suppressedCount+=1,{forward:!1}):{forward:!0}},buildErrorContext=(e,r)=>{const o={...deviceContext};return null!==currentScreen&&(o.screen=currentScreen),r&&(o.suppressedCount=r),{...o,...e}},forwardErrorToRemote=(e,r)=>{const o=dedupeKeyFor(e),{forward:t,suppressedCount:n}=shouldForward(o);t&&safe(()=>remote?.recordError?.(e,buildErrorContext(r,n)))},logDebug=(e,...r)=>{isEnabled()&&isLogLevelAllowed(LogLevels.DEBUG.level)&&console.debug(`${LogLevels.DEBUG.color}[DEBUG]: ${redactString(e)}${Colors.RESET}`,...redact(r))},logInfo=(e,...r)=>{const o=redactString(e),t=redact(r);isEnabled()&&isLogLevelAllowed(LogLevels.INFO.level)&&(console.info(`${LogLevels.INFO.color}[INFO]: ${o}${Colors.RESET}`,...t),safe(()=>remote?.log?.("info",o,t)))},logWarning=(e,...r)=>{const o=redactString(e),t=redact(r);isEnabled()&&isLogLevelAllowed(LogLevels.WARN.level)&&(console.warn(`${LogLevels.WARN.color}[WARNING]: ${o}${Colors.RESET}`,...t),safe(()=>remote?.log?.("warn",o,t)))},logError=(e,...r)=>{const o=redactString(e),t=redact(r);isEnabled()&&isLogLevelAllowed(LogLevels.ERROR.level)&&console.error(`${LogLevels.ERROR.color}[ERROR]: ${o}${Colors.RESET}`,...t),safe(()=>remote?.log?.("error",o,t));const n=t[0];void 0!==n&&forwardErrorToRemote(toErrorObject(n),{source:"Logger.error",message:o})},breadcrumb=(e,r)=>{const o=redactString(e),t=redact(r);isEnabled()&&isLogLevelAllowed("info")&&console.info(`${Colors.BLUE}[BREADCRUMB]: ${o}${Colors.RESET}`,t??""),safe(()=>remote?.breadcrumb?.(o,t))},recordError=(e,r)=>{const o=redact(toErrorObject(e)),t=redact(r);isEnabled()&&isLogLevelAllowed("error")&&console.error(`${Colors.RED_BOLD}[RECORDED]: ${o.message}${Colors.RESET}`,t??""),forwardErrorToRemote(o,t)},asDict=e=>"object"==typeof e&&null!==e?e:null,headerValue=(e,r)=>{const o=asDict(e);if(!o)return null;const t="function"==typeof o.get?o.get.call(e,r):o[r]??o[r.toLowerCase()];return"string"==typeof t&&""!==t?t:null},requestFailed=(e,r={})=>{const{label:o,expectedStatuses:t=[],...n}=r,s=asDict(e),l=asDict(s?.config),c=asDict(s?.response),i=asDict(c?.data),d="number"==typeof c?.status?c.status:null,a="string"==typeof l?.method?l.method.toUpperCase():null,u="string"==typeof l?.url?l.url.split("?")[0]??null:null,g=o??([a,u].filter(Boolean).join(" ")||"Request");if("CanceledError"===s?.name||"ERR_CANCELED"===s?.code||!0===s?.__CANCEL__)return void logInfo(`${g} canceled`);if(null!==d&&t.includes(d))return void logInfo(`${g} refused (${d})`);const E="string"==typeof s?.message?s.message:e instanceof Error?e.message:null,f=redactString(null===d?`${g} failed`:`${g} failed (${d})`);isEnabled()&&isLogLevelAllowed(LogLevels.ERROR.level)&&console.error(`${LogLevels.ERROR.color}[ERROR]: ${f}${Colors.RESET}`,redactString(E??"")),safe(()=>remote?.log?.("error",f,[]));const p=getConfigValue("correlationIdHeader",null),L=redact({source:"Logger.requestFailed",label:g,status:d,method:a,path:u,code:"string"==typeof i?.code?i.code:null,traceId:"string"==typeof i?.traceId?i.traceId:null,correlationId:p?headerValue(l?.headers,p):null,detail:E,...n});forwardErrorToRemote(redact(new Error(f)),L)},setUser=(e,r)=>{safe(()=>remote?.setUser?.(e,r))},setAttribute=(e,r)=>{safe(()=>remote?.setAttribute?.(e,r))},trackScreen=(e,r)=>{e!==currentScreen&&(currentScreen=e,screenHistory.push(e),screenHistory.length>loggerOptions.screenHistoryLimit&&(screenHistory=screenHistory.slice(screenHistory.length-loggerOptions.screenHistoryLimit)),breadcrumb(`Screen: ${e}`,r))},getCurrentScreen=()=>currentScreen,getScreenHistory=()=>[...screenHistory];export const Logger={debug:logDebug,info:logInfo,warn:logWarning,error:logError,breadcrumb:breadcrumb,recordError:recordError,requestFailed:requestFailed,setUser:setUser,setAttribute:setAttribute,trackScreen:trackScreen,getCurrentScreen:getCurrentScreen,getScreenHistory:getScreenHistory};
|
package/lib/module/push/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";export{disablePush,enablePush,initPush,notifyLocal,onAction,setBadgeCount,setCategories,setPushImplementation,usePush}from"./push.js";export{PUSH_STATUS}from"./status.js";
|
|
1
|
+
"use strict";export{disablePush,enablePush,initPush,notifyLocal,onAction,onReceive,setBadgeCount,setCategories,setPushImplementation,usePush}from"./push.js";export{PUSH_STATUS}from"./status.js";
|
package/lib/module/push/push.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{useSyncExternalStore}from"react";import{Platform}from"react-native";import{Logger}from"../logger/index.js";let resolved,override=null;export const setPushImplementation=e=>{override=e};export const resolveModules=()=>{if(override)return override;if(void 0!==resolved)return resolved;try{if(require("expo").isRunningInExpoGo())return resolved=null,resolved;const e=require("expo-notifications"),t=require("expo-application");resolved={notifications:e,applicationId:t.applicationId}}catch(e){Logger.warn("[push] expo-notifications is not available; push is off",e),resolved=null}return resolved};let state={status:"unknown",enabled:!1};const subscribers=new Set,setState=e=>{state={...state,...e};for(const e of subscribers)e()};let config=null,channelsReady=Promise.resolve(),tokenSubscription=null,actionHandler=null
|
|
1
|
+
"use strict";import{useSyncExternalStore}from"react";import{Platform}from"react-native";import{Logger}from"../logger/index.js";let resolved,override=null;export const setPushImplementation=e=>{override=e};export const resolveModules=()=>{if(override)return override;if(void 0!==resolved)return resolved;try{if(require("expo").isRunningInExpoGo())return resolved=null,resolved;const e=require("expo-notifications"),t=require("expo-application");resolved={notifications:e,applicationId:t.applicationId,getApnsEnvironment:()=>t.getIosPushNotificationServiceEnvironmentAsync()}}catch(e){Logger.warn("[push] expo-notifications is not available; push is off",e),resolved=null}return resolved};let state={status:"unknown",enabled:!1};const subscribers=new Set,setState=e=>{state={...state,...e};for(const e of subscribers)e()};let config=null,channelsReady=Promise.resolve(),tokenSubscription=null,actionHandler=null;const receiveHandlers=new Set;let listening=!1,listeningForReceived=!1;const pendingResponses=[],routedAt=new Map,REPEAT_WINDOW_MS=1e4,requireConfig=()=>{if(!config)throw new Error("[@webority/mobile-core] call initPush before enablePush or disablePush.");return config},toPermissionStatus=e=>e.granted?"granted":"denied"===e.status?"denied":"unknown",toStringData=e=>{const t={};for(const[n,o]of Object.entries(e??{}))null!=o&&(t[n]="object"==typeof o?JSON.stringify(o):String(o));return t},contentData=e=>{const t=e.request.content;if(t.data)return toStringData(t.data);if("string"==typeof t.dataString)try{return toStringData(JSON.parse(t.dataString))}catch(e){Logger.error("[push] notification data is not JSON",e)}return{}},triggerData=e=>{const t=e.request.trigger,n={...t?.remoteMessage?.data??{}};for(const[e,o]of Object.entries(t?.payload??{}))"aps"===e||"body"===e&&"object"==typeof o||(n[e]=o);return toStringData(n)},notificationData=e=>({...triggerData(e),...contentData(e)}),responseData=e=>notificationData(e.notification);export const toPushReceived=e=>({notificationId:e.request.identifier,title:e.request.content.title??null,body:e.request.content.body??null,data:notificationData(e)});export const toPushAction=e=>({actionId:e.actionIdentifier,...e.userText?{userText:e.userText}:{},data:responseData(e),notificationId:e.notification.request.identifier});export const toNotificationActions=e=>e.map(e=>({identifier:e.id,buttonTitle:e.title,...e.textInput?{textInput:{submitButtonTitle:e.textInput.submitTitle,placeholder:e.textInput.placeholder}}:{},options:{opensAppToForeground:e.opensApp??!0,isDestructive:e.destructive??!1,isAuthenticationRequired:e.authenticationRequired??!1}}));const routeResponse=async(e,t)=>{const n=`${t.notification.request.identifier}:${t.actionIdentifier}`,o=routedAt.get(n);if(void 0!==o&&Date.now()-o<1e4)return;if(t.actionIdentifier!==e.DEFAULT_ACTION_IDENTIFIER&&actionHandler){routedAt.set(n,Date.now());try{await actionHandler(toPushAction(t))}catch(e){Logger.error("[push] onAction threw",e)}}else if(config){routedAt.set(n,Date.now());try{config.onOpen(responseData(t))}catch(e){Logger.error("[push] onOpen threw",e)}}else pendingResponses.push(t)},drainPending=async e=>{const t=pendingResponses.splice(0);for(const n of t)await routeResponse(e,n)},listenForResponses=e=>{listening||(listening=!0,e.addNotificationResponseReceivedListener(t=>{routeResponse(e,t)}),e.getLastNotificationResponseAsync().then(async t=>(t&&await routeResponse(e,t),e.clearLastNotificationResponseAsync?.())).catch(e=>Logger.error("[push] reading the launch notification failed",e)))};export const routeTaskPayload=async e=>{const t=resolveModules(),n=e;t&&"string"==typeof n?.actionIdentifier&&"string"==typeof n.notification?.request?.identifier&&await routeResponse(t.notifications,n)};const createChannels=async(e,t)=>{for(const n of t)try{await e.setNotificationChannelAsync(n.id,{name:n.name,importance:"high"===n.importance?e.AndroidImportance.HIGH:e.AndroidImportance.DEFAULT})}catch(e){Logger.error(`[push] creating channel ${n.id} failed`,e)}},APNS_ENVIRONMENTS={development:"Development",production:"Production"},readApnsEnvironment=async e=>{try{return APNS_ENVIRONMENTS[await e.getApnsEnvironment()??""]??null}catch(e){return Logger.warn("[push] reading the APNs environment failed",e),null}},toDevice=async(e,t)=>{const n="android"===e.type?"Fcm":"ios"===e.type?"Apns":null;if(!n||"string"!=typeof e.data)return null;if(!t.applicationId)throw new Error("[@webority/mobile-core] push needs the app bundle id from expo-application.");return{installationId:await requireConfig().getInstallationId(),platform:n,token:e.data,appId:t.applicationId,apnsEnvironment:"Apns"===n?await readApnsEnvironment(t):null}};export const initPush=e=>{const t=null===config;if(config=e,!t)return;const n=resolveModules();if(!n)return void setState({status:"unavailable"});const o=n.notifications;o.setNotificationHandler({handleNotification:async()=>{const e=config?.showInForeground?.()??!0;return{shouldShowBanner:e,shouldShowList:e,shouldPlaySound:e&&(config?.playSoundInForeground?.()??!0),shouldSetBadge:!0}}}),"android"===Platform.OS&&e.androidChannels?.length&&(channelsReady=createChannels(o,e.androidChannels)),listenForResponses(o),drainPending(o),o.getPermissionsAsync().then(e=>setState({status:toPermissionStatus(e)})).catch(e=>Logger.error("[push] permission check failed",e))};let registering=null,registeredKey=null;const registerOnce=e=>{const t=JSON.stringify(e);if(registering?.key===t)return registering.promise;const n=requireConfig().register(e).then(()=>{registeredKey=t}).finally(()=>{registering?.promise===n&&(registering=null)});return registering={key:t,promise:n},n};export const enablePush=async({prompt:e=!0}={})=>{requireConfig();const t=resolveModules();if(!t)return setState({status:"unavailable"}),"unavailable";const n=t.notifications;await channelsReady;let o=await n.getPermissionsAsync();if(!o.granted&&e&&(o=await n.requestPermissionsAsync()),!o.granted){const e=toPermissionStatus(o);return setState({status:e}),e}const i=await toDevice(await n.getDevicePushTokenAsync(),t);return i?(await registerOnce(i),tokenSubscription?.remove(),tokenSubscription=n.addPushTokenListener(e=>{toDevice(e,t).then(e=>e&&JSON.stringify(e)!==registeredKey?registerOnce(e):void 0).catch(e=>Logger.error("[push] re-registering a new token failed",e))}),setState({status:"granted",enabled:!0}),"granted"):(setState({status:"unavailable"}),"unavailable")};export const disablePush=async()=>{const e=requireConfig();tokenSubscription?.remove(),tokenSubscription=null,registeredKey=null,setState({enabled:!1}),await e.unregister(await e.getInstallationId())};export const setBadgeCount=async e=>{const t=resolveModules();t&&await t.notifications.setBadgeCountAsync(e)};export const setCategories=async e=>{const t=resolveModules();if(t)for(const n of e)await t.notifications.setNotificationCategoryAsync(n.id,toNotificationActions(n.actions))};export const onAction=e=>{actionHandler=e;const t=resolveModules();return t&&(listenForResponses(t.notifications),drainPending(t.notifications)),()=>{actionHandler===e&&(actionHandler=null)}};export const onReceive=e=>{receiveHandlers.add(e);const t=resolveModules();return t&&!listeningForReceived&&(listeningForReceived=!0,t.notifications.addNotificationReceivedListener(e=>{const t=toPushReceived(e);for(const e of[...receiveHandlers])try{e(t)}catch(e){Logger.error("[push] onReceive threw",e)}})),()=>{receiveHandlers.delete(e)}};export const notifyLocal=async({title:e,body:t,data:n})=>{const o=resolveModules();o&&await o.notifications.scheduleNotificationAsync({content:{title:e,body:t,data:n??{}},trigger:null})};const subscribe=e=>(subscribers.add(e),()=>{subscribers.delete(e)}),getSnapshot=()=>state,enable=async()=>{await enablePush()};export const usePush=()=>({...useSyncExternalStore(subscribe,getSnapshot,getSnapshot),enable:enable,disable:disablePush});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export type { PushAction, PushActionHandler, PushAndroidChannel, PushCategory, PushCategoryAction, PushConfig, PushDevice, PushLocalNotification, PushModules, PushNotificationsLike, PushPlatform, PushState } from './push';
|
|
2
|
-
export { disablePush, enablePush, initPush, notifyLocal, onAction, setBadgeCount, setCategories, setPushImplementation, usePush } from './push';
|
|
1
|
+
export type { EnablePushOptions, PushAction, PushActionHandler, PushAndroidChannel, PushApnsEnvironment, PushCategory, PushCategoryAction, PushConfig, PushDevice, PushLocalNotification, PushModules, PushNotificationsLike, PushPlatform, PushReceived, PushReceiveHandler, PushState } from './push';
|
|
2
|
+
export { disablePush, enablePush, initPush, notifyLocal, onAction, onReceive, setBadgeCount, setCategories, setPushImplementation, usePush } from './push';
|
|
3
3
|
export type { PushStatus } from './status';
|
|
4
4
|
export { PUSH_STATUS } from './status';
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,11 +1,37 @@
|
|
|
1
1
|
import type { PushStatus } from './status';
|
|
2
2
|
export type PushPlatform = 'Fcm' | 'Apns';
|
|
3
|
+
/** Which APNs gateway accepts this build's token: the sandbox for development signing, else production. */
|
|
4
|
+
export type PushApnsEnvironment = 'Development' | 'Production';
|
|
3
5
|
export interface PushDevice {
|
|
4
6
|
installationId: string;
|
|
5
7
|
platform: PushPlatform;
|
|
6
8
|
token: string;
|
|
7
9
|
/** The app's bundle id, the APNs topic on iOS. */
|
|
8
10
|
appId: string;
|
|
11
|
+
/**
|
|
12
|
+
* The APNs gateway this build's signing entitles, read from the app's
|
|
13
|
+
* `aps-environment`. Null on Android, and on iOS when it cannot be read
|
|
14
|
+
* (the simulator), so the server falls back to its own setting.
|
|
15
|
+
*/
|
|
16
|
+
apnsEnvironment: PushApnsEnvironment | null;
|
|
17
|
+
}
|
|
18
|
+
/** A notification that arrived while the app was open. */
|
|
19
|
+
export interface PushReceived {
|
|
20
|
+
/** The notification's identifier on the phone, not a server id in `data`. */
|
|
21
|
+
notificationId: string;
|
|
22
|
+
title: string | null;
|
|
23
|
+
body: string | null;
|
|
24
|
+
data: Record<string, string>;
|
|
25
|
+
}
|
|
26
|
+
export type PushReceiveHandler = (notification: PushReceived) => void;
|
|
27
|
+
export interface EnablePushOptions {
|
|
28
|
+
/**
|
|
29
|
+
* Whether to show the system permission prompt when permission has not been
|
|
30
|
+
* granted yet. Defaults to true. With false, a phone already granted is
|
|
31
|
+
* registered silently and any other phone is left alone, so the app can ask
|
|
32
|
+
* later at a moment it chooses.
|
|
33
|
+
*/
|
|
34
|
+
prompt?: boolean;
|
|
9
35
|
}
|
|
10
36
|
export interface PushAndroidChannel {
|
|
11
37
|
id: string;
|
|
@@ -75,28 +101,31 @@ interface PermissionResponseLike {
|
|
|
75
101
|
interface SubscriptionLike {
|
|
76
102
|
remove: () => void;
|
|
77
103
|
}
|
|
78
|
-
interface
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
104
|
+
interface NotificationLike {
|
|
105
|
+
request: {
|
|
106
|
+
identifier: string;
|
|
107
|
+
/** A background task receives the raw content, where data may still be a JSON `dataString`. */
|
|
108
|
+
content: {
|
|
109
|
+
title?: string | null;
|
|
110
|
+
body?: string | null;
|
|
111
|
+
data?: Record<string, unknown> | null;
|
|
112
|
+
dataString?: string;
|
|
113
|
+
};
|
|
114
|
+
/** A remote push carries its raw payload: APNs `userInfo` on iOS, the FCM message on Android. */
|
|
115
|
+
trigger?: {
|
|
116
|
+
type?: string;
|
|
117
|
+
payload?: Record<string, unknown> | null;
|
|
118
|
+
remoteMessage?: {
|
|
86
119
|
data?: Record<string, unknown> | null;
|
|
87
|
-
dataString?: string;
|
|
88
|
-
};
|
|
89
|
-
/** A remote push carries its raw payload: APNs `userInfo` on iOS, the FCM message on Android. */
|
|
90
|
-
trigger?: {
|
|
91
|
-
type?: string;
|
|
92
|
-
payload?: Record<string, unknown> | null;
|
|
93
|
-
remoteMessage?: {
|
|
94
|
-
data?: Record<string, unknown> | null;
|
|
95
|
-
} | null;
|
|
96
120
|
} | null;
|
|
97
|
-
};
|
|
121
|
+
} | null;
|
|
98
122
|
};
|
|
99
123
|
}
|
|
124
|
+
interface NotificationResponseLike {
|
|
125
|
+
actionIdentifier: string;
|
|
126
|
+
userText?: string;
|
|
127
|
+
notification: NotificationLike;
|
|
128
|
+
}
|
|
100
129
|
interface NotificationActionLike {
|
|
101
130
|
identifier: string;
|
|
102
131
|
buttonTitle: string;
|
|
@@ -121,6 +150,7 @@ export interface PushNotificationsLike {
|
|
|
121
150
|
getDevicePushTokenAsync: () => Promise<DevicePushTokenLike>;
|
|
122
151
|
addPushTokenListener: (listener: (token: DevicePushTokenLike) => void) => SubscriptionLike;
|
|
123
152
|
addNotificationResponseReceivedListener: (listener: (response: NotificationResponseLike) => void) => SubscriptionLike;
|
|
153
|
+
addNotificationReceivedListener: (listener: (notification: NotificationLike) => void) => SubscriptionLike;
|
|
124
154
|
getLastNotificationResponseAsync: () => Promise<NotificationResponseLike | null>;
|
|
125
155
|
clearLastNotificationResponseAsync?: () => Promise<void>;
|
|
126
156
|
setNotificationHandler: (handler: {
|
|
@@ -157,6 +187,8 @@ export interface PushModules {
|
|
|
157
187
|
notifications: PushNotificationsLike;
|
|
158
188
|
/** The bundle id, from expo-application. */
|
|
159
189
|
applicationId: string | null;
|
|
190
|
+
/** expo-application's `getIosPushNotificationServiceEnvironmentAsync`; iOS only. */
|
|
191
|
+
getApnsEnvironment: () => Promise<'development' | 'production' | null>;
|
|
160
192
|
}
|
|
161
193
|
/**
|
|
162
194
|
* Replace the native modules. For tests and the showcase, to reach paths a
|
|
@@ -170,6 +202,7 @@ export declare const setPushImplementation: (impl: PushModules | null) => void;
|
|
|
170
202
|
* import throws on Android. Exported for `./task`; not on the `/push` entry.
|
|
171
203
|
*/
|
|
172
204
|
export declare const resolveModules: () => PushModules | null;
|
|
205
|
+
export declare const toPushReceived: (notification: NotificationLike) => PushReceived;
|
|
173
206
|
export declare const toPushAction: (response: NotificationResponseLike) => PushAction;
|
|
174
207
|
export declare const toNotificationActions: (actions: PushCategoryAction[]) => NotificationActionLike[];
|
|
175
208
|
/** The payload of the background notification task: routes button taps, ignores received pushes. */
|
|
@@ -183,8 +216,11 @@ export declare const initPush: (next: PushConfig) => void;
|
|
|
183
216
|
/**
|
|
184
217
|
* Asks for permission, reads the native device token and registers it through
|
|
185
218
|
* `config.register`, then re-registers whenever the OS rotates the token.
|
|
219
|
+
* With `{ prompt: false }` it never shows the permission prompt: a phone
|
|
220
|
+
* already granted registers, and any other resolves with its status
|
|
221
|
+
* (`unknown` when the person has not been asked yet, `denied` when refused).
|
|
186
222
|
*/
|
|
187
|
-
export declare const enablePush: () => Promise<
|
|
223
|
+
export declare const enablePush: ({ prompt }?: EnablePushOptions) => Promise<PushStatus>;
|
|
188
224
|
/**
|
|
189
225
|
* Unregisters the device through `config.unregister` and stops following token
|
|
190
226
|
* changes. Taps on notifications already delivered still reach `onOpen`.
|
|
@@ -204,6 +240,13 @@ export declare const setCategories: (categories: PushCategory[]) => Promise<void
|
|
|
204
240
|
* `/push/task`, which sets this handler too. Returns a function that removes it.
|
|
205
241
|
*/
|
|
206
242
|
export declare const onAction: (handler: PushActionHandler) => (() => void);
|
|
243
|
+
/**
|
|
244
|
+
* Calls `handler` for each notification that arrives while the app is open,
|
|
245
|
+
* whether or not it is shown, so the app can refresh what it displays. Not
|
|
246
|
+
* called for a notification that arrives in the background; its tap still
|
|
247
|
+
* goes to `onOpen`. Returns a function that removes the handler.
|
|
248
|
+
*/
|
|
249
|
+
export declare const onReceive: (handler: PushReceiveHandler) => (() => void);
|
|
207
250
|
/** Shows a notification from the phone itself, for example when a button's request failed. */
|
|
208
251
|
export declare const notifyLocal: ({ title, body, data }: PushLocalNotification) => Promise<void>;
|
|
209
252
|
/** Push status for a settings switch, re-rendering when it changes. */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export type { PushAction, PushActionHandler, PushAndroidChannel, PushCategory, PushCategoryAction, PushConfig, PushDevice, PushLocalNotification, PushModules, PushNotificationsLike, PushPlatform, PushState } from './push.js';
|
|
2
|
-
export { disablePush, enablePush, initPush, notifyLocal, onAction, setBadgeCount, setCategories, setPushImplementation, usePush } from './push.js';
|
|
1
|
+
export type { EnablePushOptions, PushAction, PushActionHandler, PushAndroidChannel, PushApnsEnvironment, PushCategory, PushCategoryAction, PushConfig, PushDevice, PushLocalNotification, PushModules, PushNotificationsLike, PushPlatform, PushReceived, PushReceiveHandler, PushState } from './push.js';
|
|
2
|
+
export { disablePush, enablePush, initPush, notifyLocal, onAction, onReceive, setBadgeCount, setCategories, setPushImplementation, usePush } from './push.js';
|
|
3
3
|
export type { PushStatus } from './status.js';
|
|
4
4
|
export { PUSH_STATUS } from './status.js';
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,11 +1,37 @@
|
|
|
1
1
|
import type { PushStatus } from './status.js';
|
|
2
2
|
export type PushPlatform = 'Fcm' | 'Apns';
|
|
3
|
+
/** Which APNs gateway accepts this build's token: the sandbox for development signing, else production. */
|
|
4
|
+
export type PushApnsEnvironment = 'Development' | 'Production';
|
|
3
5
|
export interface PushDevice {
|
|
4
6
|
installationId: string;
|
|
5
7
|
platform: PushPlatform;
|
|
6
8
|
token: string;
|
|
7
9
|
/** The app's bundle id, the APNs topic on iOS. */
|
|
8
10
|
appId: string;
|
|
11
|
+
/**
|
|
12
|
+
* The APNs gateway this build's signing entitles, read from the app's
|
|
13
|
+
* `aps-environment`. Null on Android, and on iOS when it cannot be read
|
|
14
|
+
* (the simulator), so the server falls back to its own setting.
|
|
15
|
+
*/
|
|
16
|
+
apnsEnvironment: PushApnsEnvironment | null;
|
|
17
|
+
}
|
|
18
|
+
/** A notification that arrived while the app was open. */
|
|
19
|
+
export interface PushReceived {
|
|
20
|
+
/** The notification's identifier on the phone, not a server id in `data`. */
|
|
21
|
+
notificationId: string;
|
|
22
|
+
title: string | null;
|
|
23
|
+
body: string | null;
|
|
24
|
+
data: Record<string, string>;
|
|
25
|
+
}
|
|
26
|
+
export type PushReceiveHandler = (notification: PushReceived) => void;
|
|
27
|
+
export interface EnablePushOptions {
|
|
28
|
+
/**
|
|
29
|
+
* Whether to show the system permission prompt when permission has not been
|
|
30
|
+
* granted yet. Defaults to true. With false, a phone already granted is
|
|
31
|
+
* registered silently and any other phone is left alone, so the app can ask
|
|
32
|
+
* later at a moment it chooses.
|
|
33
|
+
*/
|
|
34
|
+
prompt?: boolean;
|
|
9
35
|
}
|
|
10
36
|
export interface PushAndroidChannel {
|
|
11
37
|
id: string;
|
|
@@ -75,28 +101,31 @@ interface PermissionResponseLike {
|
|
|
75
101
|
interface SubscriptionLike {
|
|
76
102
|
remove: () => void;
|
|
77
103
|
}
|
|
78
|
-
interface
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
104
|
+
interface NotificationLike {
|
|
105
|
+
request: {
|
|
106
|
+
identifier: string;
|
|
107
|
+
/** A background task receives the raw content, where data may still be a JSON `dataString`. */
|
|
108
|
+
content: {
|
|
109
|
+
title?: string | null;
|
|
110
|
+
body?: string | null;
|
|
111
|
+
data?: Record<string, unknown> | null;
|
|
112
|
+
dataString?: string;
|
|
113
|
+
};
|
|
114
|
+
/** A remote push carries its raw payload: APNs `userInfo` on iOS, the FCM message on Android. */
|
|
115
|
+
trigger?: {
|
|
116
|
+
type?: string;
|
|
117
|
+
payload?: Record<string, unknown> | null;
|
|
118
|
+
remoteMessage?: {
|
|
86
119
|
data?: Record<string, unknown> | null;
|
|
87
|
-
dataString?: string;
|
|
88
|
-
};
|
|
89
|
-
/** A remote push carries its raw payload: APNs `userInfo` on iOS, the FCM message on Android. */
|
|
90
|
-
trigger?: {
|
|
91
|
-
type?: string;
|
|
92
|
-
payload?: Record<string, unknown> | null;
|
|
93
|
-
remoteMessage?: {
|
|
94
|
-
data?: Record<string, unknown> | null;
|
|
95
|
-
} | null;
|
|
96
120
|
} | null;
|
|
97
|
-
};
|
|
121
|
+
} | null;
|
|
98
122
|
};
|
|
99
123
|
}
|
|
124
|
+
interface NotificationResponseLike {
|
|
125
|
+
actionIdentifier: string;
|
|
126
|
+
userText?: string;
|
|
127
|
+
notification: NotificationLike;
|
|
128
|
+
}
|
|
100
129
|
interface NotificationActionLike {
|
|
101
130
|
identifier: string;
|
|
102
131
|
buttonTitle: string;
|
|
@@ -121,6 +150,7 @@ export interface PushNotificationsLike {
|
|
|
121
150
|
getDevicePushTokenAsync: () => Promise<DevicePushTokenLike>;
|
|
122
151
|
addPushTokenListener: (listener: (token: DevicePushTokenLike) => void) => SubscriptionLike;
|
|
123
152
|
addNotificationResponseReceivedListener: (listener: (response: NotificationResponseLike) => void) => SubscriptionLike;
|
|
153
|
+
addNotificationReceivedListener: (listener: (notification: NotificationLike) => void) => SubscriptionLike;
|
|
124
154
|
getLastNotificationResponseAsync: () => Promise<NotificationResponseLike | null>;
|
|
125
155
|
clearLastNotificationResponseAsync?: () => Promise<void>;
|
|
126
156
|
setNotificationHandler: (handler: {
|
|
@@ -157,6 +187,8 @@ export interface PushModules {
|
|
|
157
187
|
notifications: PushNotificationsLike;
|
|
158
188
|
/** The bundle id, from expo-application. */
|
|
159
189
|
applicationId: string | null;
|
|
190
|
+
/** expo-application's `getIosPushNotificationServiceEnvironmentAsync`; iOS only. */
|
|
191
|
+
getApnsEnvironment: () => Promise<'development' | 'production' | null>;
|
|
160
192
|
}
|
|
161
193
|
/**
|
|
162
194
|
* Replace the native modules. For tests and the showcase, to reach paths a
|
|
@@ -170,6 +202,7 @@ export declare const setPushImplementation: (impl: PushModules | null) => void;
|
|
|
170
202
|
* import throws on Android. Exported for `./task`; not on the `/push` entry.
|
|
171
203
|
*/
|
|
172
204
|
export declare const resolveModules: () => PushModules | null;
|
|
205
|
+
export declare const toPushReceived: (notification: NotificationLike) => PushReceived;
|
|
173
206
|
export declare const toPushAction: (response: NotificationResponseLike) => PushAction;
|
|
174
207
|
export declare const toNotificationActions: (actions: PushCategoryAction[]) => NotificationActionLike[];
|
|
175
208
|
/** The payload of the background notification task: routes button taps, ignores received pushes. */
|
|
@@ -183,8 +216,11 @@ export declare const initPush: (next: PushConfig) => void;
|
|
|
183
216
|
/**
|
|
184
217
|
* Asks for permission, reads the native device token and registers it through
|
|
185
218
|
* `config.register`, then re-registers whenever the OS rotates the token.
|
|
219
|
+
* With `{ prompt: false }` it never shows the permission prompt: a phone
|
|
220
|
+
* already granted registers, and any other resolves with its status
|
|
221
|
+
* (`unknown` when the person has not been asked yet, `denied` when refused).
|
|
186
222
|
*/
|
|
187
|
-
export declare const enablePush: () => Promise<
|
|
223
|
+
export declare const enablePush: ({ prompt }?: EnablePushOptions) => Promise<PushStatus>;
|
|
188
224
|
/**
|
|
189
225
|
* Unregisters the device through `config.unregister` and stops following token
|
|
190
226
|
* changes. Taps on notifications already delivered still reach `onOpen`.
|
|
@@ -204,6 +240,13 @@ export declare const setCategories: (categories: PushCategory[]) => Promise<void
|
|
|
204
240
|
* `/push/task`, which sets this handler too. Returns a function that removes it.
|
|
205
241
|
*/
|
|
206
242
|
export declare const onAction: (handler: PushActionHandler) => (() => void);
|
|
243
|
+
/**
|
|
244
|
+
* Calls `handler` for each notification that arrives while the app is open,
|
|
245
|
+
* whether or not it is shown, so the app can refresh what it displays. Not
|
|
246
|
+
* called for a notification that arrives in the background; its tap still
|
|
247
|
+
* goes to `onOpen`. Returns a function that removes the handler.
|
|
248
|
+
*/
|
|
249
|
+
export declare const onReceive: (handler: PushReceiveHandler) => (() => void);
|
|
207
250
|
/** Shows a notification from the phone itself, for example when a button's request failed. */
|
|
208
251
|
export declare const notifyLocal: ({ title, body, data }: PushLocalNotification) => Promise<void>;
|
|
209
252
|
/** Push status for a settings switch, re-rendering when it changes. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webority/mobile-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Platform layer for Webority React Native apps: HTTP clients, auth/token storage, config, logging, formatters, validators, network status, permissions, storage and version checks. No UI.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react-native",
|