@webority/mobile-core 0.1.0 → 0.2.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.
Files changed (185) hide show
  1. package/README.md +32 -0
  2. package/lib/commonjs/api/clientHeaders.js +1 -0
  3. package/lib/commonjs/api/minAppVersion.js +1 -0
  4. package/lib/commonjs/api/publicClient.js +1 -1
  5. package/lib/commonjs/api/requestDefaults.js +1 -0
  6. package/lib/commonjs/api/retry.js +1 -1
  7. package/lib/commonjs/api/tokenRefresh.js +1 -1
  8. package/lib/commonjs/api/userClient.js +1 -1
  9. package/lib/commonjs/auth/authStore.js +1 -1
  10. package/lib/commonjs/auth/index.js +1 -1
  11. package/lib/commonjs/config/index.js +1 -1
  12. package/lib/commonjs/errors/extractApiError.js +1 -0
  13. package/lib/commonjs/errors/index.js +1 -0
  14. package/lib/commonjs/formatters/currency.js +1 -1
  15. package/lib/commonjs/formatters/date.js +1 -1
  16. package/lib/commonjs/formatters/dateMath.js +1 -1
  17. package/lib/commonjs/formatters/formatConfig.js +1 -0
  18. package/lib/commonjs/formatters/house.js +1 -0
  19. package/lib/commonjs/formatters/index.js +1 -1
  20. package/lib/commonjs/formatters/number.js +1 -1
  21. package/lib/commonjs/index.js +1 -1
  22. package/lib/commonjs/initSDK.js +1 -1
  23. package/lib/commonjs/initUserAuth.js +1 -1
  24. package/lib/commonjs/logger/index.js +1 -1
  25. package/lib/commonjs/query/apiBaseQuery.js +1 -0
  26. package/lib/commonjs/query/createAppApi.js +1 -0
  27. package/lib/commonjs/query/index.js +1 -0
  28. package/lib/commonjs/query/listeners.js +1 -0
  29. package/lib/commonjs/query/paging.js +1 -0
  30. package/lib/commonjs/query/tags.js +1 -0
  31. package/lib/commonjs/redux/errorToastMiddleware.js +1 -0
  32. package/lib/commonjs/redux/index.js +1 -0
  33. package/lib/commonjs/redux/rootReducer.js +1 -0
  34. package/lib/commonjs/redux/serializableCheck.js +1 -0
  35. package/lib/commonjs/versionCheck/buildNumber.js +1 -0
  36. package/lib/commonjs/versionCheck/index.js +1 -1
  37. package/lib/module/api/baseUrl.js +1 -1
  38. package/lib/module/api/clientHeaders.js +1 -0
  39. package/lib/module/api/curl.js +1 -1
  40. package/lib/module/api/minAppVersion.js +1 -0
  41. package/lib/module/api/multipart.js +1 -1
  42. package/lib/module/api/publicClient.js +1 -1
  43. package/lib/module/api/requestDefaults.js +1 -0
  44. package/lib/module/api/responseBodyText.js +1 -1
  45. package/lib/module/api/retry.js +1 -1
  46. package/lib/module/api/tokenRefresh.js +1 -1
  47. package/lib/module/api/userClient.js +1 -1
  48. package/lib/module/auth/authStore.js +1 -1
  49. package/lib/module/auth/index.js +1 -1
  50. package/lib/module/auth/jwt.js +1 -1
  51. package/lib/module/config/index.js +1 -1
  52. package/lib/module/deepLink/index.js +1 -1
  53. package/lib/module/download/adapters/expoFs.js +1 -1
  54. package/lib/module/download/adapters/shared.js +1 -1
  55. package/lib/module/download/index.js +1 -1
  56. package/lib/module/errors/extractApiError.js +1 -0
  57. package/lib/module/errors/index.js +1 -0
  58. package/lib/module/formatters/currency.js +1 -1
  59. package/lib/module/formatters/date.js +1 -1
  60. package/lib/module/formatters/dateMath.js +1 -1
  61. package/lib/module/formatters/formatConfig.js +1 -0
  62. package/lib/module/formatters/house.js +1 -0
  63. package/lib/module/formatters/index.js +1 -1
  64. package/lib/module/formatters/initials.js +1 -1
  65. package/lib/module/formatters/intlCache.js +1 -1
  66. package/lib/module/formatters/number.js +1 -1
  67. package/lib/module/formatters/phone.js +1 -1
  68. package/lib/module/formatters/windowsToIanaTable.js +1 -1
  69. package/lib/module/geolocation/adapters/expoLocation.js +1 -1
  70. package/lib/module/geolocation/index.js +1 -1
  71. package/lib/module/index.js +1 -1
  72. package/lib/module/initSDK.js +1 -1
  73. package/lib/module/initUserAuth.js +1 -1
  74. package/lib/module/logger/crashlytics.js +1 -1
  75. package/lib/module/logger/index.js +1 -1
  76. package/lib/module/logger/redact.js +1 -1
  77. package/lib/module/network/index.js +1 -1
  78. package/lib/module/network/networkStatus.js +1 -1
  79. package/lib/module/permissions/calendar.js +1 -1
  80. package/lib/module/permissions/camera.js +1 -1
  81. package/lib/module/permissions/contacts.js +1 -1
  82. package/lib/module/permissions/index.js +1 -1
  83. package/lib/module/permissions/location.js +1 -1
  84. package/lib/module/permissions/mediaLibrary.js +1 -1
  85. package/lib/module/permissions/notifications.js +1 -1
  86. package/lib/module/permissions/photos.js +1 -1
  87. package/lib/module/phone/countries.js +1 -1
  88. package/lib/module/push/index.js +1 -1
  89. package/lib/module/push/push.js +1 -1
  90. package/lib/module/push/status.js +1 -1
  91. package/lib/module/push/task.js +1 -1
  92. package/lib/module/query/apiBaseQuery.js +1 -0
  93. package/lib/module/query/createAppApi.js +1 -0
  94. package/lib/module/query/index.js +1 -0
  95. package/lib/module/query/listeners.js +1 -0
  96. package/lib/module/query/paging.js +1 -0
  97. package/lib/module/query/tags.js +1 -0
  98. package/lib/module/redux/errorToastMiddleware.js +1 -0
  99. package/lib/module/redux/index.js +1 -0
  100. package/lib/module/redux/rootReducer.js +1 -0
  101. package/lib/module/redux/serializableCheck.js +1 -0
  102. package/lib/module/secureScreen/index.js +1 -1
  103. package/lib/module/share/index.js +1 -1
  104. package/lib/module/smsRetriever/index.js +1 -1
  105. package/lib/module/specs/NativeSmsRetriever.js +1 -1
  106. package/lib/module/sqlite/adapters/expo.js +1 -1
  107. package/lib/module/sqlite/index.js +1 -1
  108. package/lib/module/storage/index.js +1 -1
  109. package/lib/module/types/axios.js +1 -1
  110. package/lib/module/types/globals.d.js +1 -1
  111. package/lib/module/utils/base64.js +1 -1
  112. package/lib/module/utils/imageCompression.js +1 -1
  113. package/lib/module/utils/index.js +1 -1
  114. package/lib/module/validators/index.js +1 -1
  115. package/lib/module/versionCheck/buildNumber.js +1 -0
  116. package/lib/module/versionCheck/index.js +1 -1
  117. package/lib/typescript/commonjs/api/clientHeaders.d.ts +25 -0
  118. package/lib/typescript/commonjs/api/minAppVersion.d.ts +24 -0
  119. package/lib/typescript/commonjs/api/requestDefaults.d.ts +21 -0
  120. package/lib/typescript/commonjs/api/retry.d.ts +23 -9
  121. package/lib/typescript/commonjs/api/tokenRefresh.d.ts +14 -2
  122. package/lib/typescript/commonjs/api/userClient.d.ts +1 -1
  123. package/lib/typescript/commonjs/auth/authStore.d.ts +76 -0
  124. package/lib/typescript/commonjs/auth/index.d.ts +2 -2
  125. package/lib/typescript/commonjs/config/index.d.ts +31 -2
  126. package/lib/typescript/commonjs/errors/extractApiError.d.ts +69 -0
  127. package/lib/typescript/commonjs/errors/index.d.ts +3 -0
  128. package/lib/typescript/commonjs/formatters/currency.d.ts +22 -7
  129. package/lib/typescript/commonjs/formatters/date.d.ts +13 -13
  130. package/lib/typescript/commonjs/formatters/dateMath.d.ts +4 -2
  131. package/lib/typescript/commonjs/formatters/formatConfig.d.ts +48 -0
  132. package/lib/typescript/commonjs/formatters/house.d.ts +143 -0
  133. package/lib/typescript/commonjs/formatters/index.d.ts +4 -0
  134. package/lib/typescript/commonjs/formatters/number.d.ts +15 -5
  135. package/lib/typescript/commonjs/index.d.ts +21 -8
  136. package/lib/typescript/commonjs/initSDK.d.ts +18 -6
  137. package/lib/typescript/commonjs/initUserAuth.d.ts +29 -0
  138. package/lib/typescript/commonjs/logger/index.d.ts +17 -0
  139. package/lib/typescript/commonjs/query/apiBaseQuery.d.ts +55 -0
  140. package/lib/typescript/commonjs/query/createAppApi.d.ts +21 -0
  141. package/lib/typescript/commonjs/query/index.d.ts +9 -0
  142. package/lib/typescript/commonjs/query/listeners.d.ts +11 -0
  143. package/lib/typescript/commonjs/query/paging.d.ts +42 -0
  144. package/lib/typescript/commonjs/query/tags.d.ts +31 -0
  145. package/lib/typescript/commonjs/redux/errorToastMiddleware.d.ts +51 -0
  146. package/lib/typescript/commonjs/redux/index.d.ts +6 -0
  147. package/lib/typescript/commonjs/redux/rootReducer.d.ts +19 -0
  148. package/lib/typescript/commonjs/redux/serializableCheck.d.ts +14 -0
  149. package/lib/typescript/commonjs/versionCheck/buildNumber.d.ts +14 -0
  150. package/lib/typescript/commonjs/versionCheck/index.d.ts +28 -17
  151. package/lib/typescript/module/api/clientHeaders.d.ts +25 -0
  152. package/lib/typescript/module/api/minAppVersion.d.ts +24 -0
  153. package/lib/typescript/module/api/requestDefaults.d.ts +21 -0
  154. package/lib/typescript/module/api/retry.d.ts +23 -9
  155. package/lib/typescript/module/api/tokenRefresh.d.ts +14 -2
  156. package/lib/typescript/module/api/userClient.d.ts +1 -1
  157. package/lib/typescript/module/auth/authStore.d.ts +76 -0
  158. package/lib/typescript/module/auth/index.d.ts +2 -2
  159. package/lib/typescript/module/config/index.d.ts +31 -2
  160. package/lib/typescript/module/errors/extractApiError.d.ts +69 -0
  161. package/lib/typescript/module/errors/index.d.ts +3 -0
  162. package/lib/typescript/module/formatters/currency.d.ts +22 -7
  163. package/lib/typescript/module/formatters/date.d.ts +13 -13
  164. package/lib/typescript/module/formatters/dateMath.d.ts +4 -2
  165. package/lib/typescript/module/formatters/formatConfig.d.ts +48 -0
  166. package/lib/typescript/module/formatters/house.d.ts +143 -0
  167. package/lib/typescript/module/formatters/index.d.ts +4 -0
  168. package/lib/typescript/module/formatters/number.d.ts +15 -5
  169. package/lib/typescript/module/index.d.ts +21 -8
  170. package/lib/typescript/module/initSDK.d.ts +18 -6
  171. package/lib/typescript/module/initUserAuth.d.ts +29 -0
  172. package/lib/typescript/module/logger/index.d.ts +17 -0
  173. package/lib/typescript/module/query/apiBaseQuery.d.ts +55 -0
  174. package/lib/typescript/module/query/createAppApi.d.ts +21 -0
  175. package/lib/typescript/module/query/index.d.ts +9 -0
  176. package/lib/typescript/module/query/listeners.d.ts +11 -0
  177. package/lib/typescript/module/query/paging.d.ts +42 -0
  178. package/lib/typescript/module/query/tags.d.ts +31 -0
  179. package/lib/typescript/module/redux/errorToastMiddleware.d.ts +51 -0
  180. package/lib/typescript/module/redux/index.d.ts +6 -0
  181. package/lib/typescript/module/redux/rootReducer.d.ts +19 -0
  182. package/lib/typescript/module/redux/serializableCheck.d.ts +14 -0
  183. package/lib/typescript/module/versionCheck/buildNumber.d.ts +14 -0
  184. package/lib/typescript/module/versionCheck/index.d.ts +28 -17
  185. package/package.json +30 -2
@@ -1 +1 @@
1
- import*as e from"expo-application";import{useEffect as r,useState as t}from"react";import{getConfigValue as n}from"../config/index.js";import{Logger as o}from"../logger/index.js";const i={getBundleId:()=>e.applicationId??"",getVersion:()=>e.nativeApplicationVersion??""};let a=null;export const setDeviceInfoImplementation=e=>{a=e};const missing=(e,r)=>{const t=new Error(`[VersionCheck] the app's ${e} is not available. Call setConfig({ ${r}: '...' }) — every Webority app already has this value in appsettings.Compiled.json — or setDeviceInfoImplementation() with a native provider.`);return o.error(t.message),t},getSystemName=()=>{const{Platform:e}=require("react-native");return e.OS},getBundleId=()=>{const e=n("appId");if(e)return e;const r=(a??i).getBundleId();if(r)return r;throw missing("store identifier","appId")},getCountry=()=>n("country","in").toLowerCase(),parseVersion=e=>{o.info(`Parsing version: ${e}`);const r=e.replace(/[^0-9.]/g,"").split(".").map(Number);return{major:r[0]||0,minor:r[1]||0,patch:r[2]||0,prerelease:e.includes("-")?e.split("-")[1]??"":""}},versionCompare=(e,r)=>{if(o.info(`Comparing current version: ${e} with latest version: ${r}`),!r)return{needsUpdate:!1,updateType:null,notice:"Error: could not get latest version"};try{const t=((e,r)=>{if(o.info(`Comparing versions: ${e} and ${r}`),e===r)return null;const t=parseVersion(e),n=parseVersion(r);return t.major<n.major?"major":t.major>n.major?null:t.minor<n.minor?"minor":t.minor>n.minor?null:t.patch<n.patch?"patch":t.patch>n.patch?null:t.prerelease&&!n.prerelease?"prerelease":!t.prerelease&&n.prerelease?null:t.prerelease<n.prerelease?"prerelease":(t.prerelease,n.prerelease,null)})(e,r);return{needsUpdate:!!t,updateType:t}}catch(t){const n=t instanceof Error?t.message:String(t);o.error(`Error comparing versions: ${n}`);const i=e!==r&&r>e;return{needsUpdate:i,updateType:i?"minor":null,notice:n.replace(/^Invalid Version:/,"Not a valid version:")}}},lookupVersion=async(e,r)=>{o.info(`Looking up version for platform: ${e}, identifier: ${r}`);const t=getCountry(),n="ios"===e?`https://itunes.apple.com/lookup?bundleId=${r}&country=${t}&_=${(new Date).valueOf()}`:`https://play.google.com/store/apps/details?id=${r}&hl=${t}`;return"ios"===e?(async e=>{let r;o.info(`Fetching iOS version from URL: ${e}`);try{r=await fetch(e)}catch{throw o.error("Error connecting to iTunes."),new Error("Error connecting to iTunes.")}const t=await r.json();if(!t||!("results"in t))throw o.error("Unknown error connecting to iTunes."),new Error("Unknown error connecting to iTunes.");if(!t.results.length)throw o.error("App for this bundle ID not found."),new Error("App for this bundle ID not found.");const n=t.results[0];return{version:n.version||null,released:n.currentVersionReleaseDate||n.releaseDate||null,notes:n.releaseNotes||"",url:n.trackViewUrl||n.artistViewUrl||n.sellerUrl||null,country:getCountry(),lastChecked:(new Date).toISOString()}})(n):(async e=>{let r;o.info(`Fetching Android version from URL: ${e}`);try{r=await fetch(e,{headers:{"User-Agent":"Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.96 Mobile Safari/537.36","sec-fetch-site":"same-origin"}})}catch(e){const r=e instanceof Error?e.message:String(e);throw o.error(`Error fetching data from Google Play Store: ${r}`),new Error("Error fetching data from Google Play Store.")}if(!r.ok){if(404===r.status)throw o.error("App with app ID not found in Google Play."),new Error("App with app ID not found in Google Play.");throw o.error(`Error fetching data: ${r.statusText}`),new Error(r.statusText)}const t=await r.text();let n=t.match(/\[\[\[['"]((\d+\.)+\d+)['"]\]\],/);if(n||(n=t.match(/Current Version[^\d]+((\d+\.)+\d+)/i)),n||(n=t.match(/itemprop="softwareVersion"[^>]*>((\d+\.)+\d+)</i)),!n){const e=t.match(/(\d+\.\d+(\.\d+)?)/g);n=e&&e.length>0?[null,e[0]]:null}const i=n?n[1]??null:null;i||o.warn("Could not extract version from Play Store. Using fallback method.");const a=t.match(/<div itemprop="description">(.*?)<\/div>/),s=a?a[1]??"":"";return{version:i,releasedAt:(new Date).toISOString(),notes:s,url:e,lastChecked:(new Date).toISOString()}})(n)},checkForUpdates=async()=>{const e=getSystemName().toLowerCase(),r=getBundleId(),t=(()=>{const e=n("appVersion");if(e)return e;const r=(a??i).getVersion();if(r)return r;throw missing("version","appVersion")})();let s=0,l=null;for(;s<=3;)try{o.info(`Checking if new version is available (attempt ${s+1}/4)`),o.info(`Platform: ${e}, identifier: ${r}, current version: ${t}`);const n=await lookupVersion(e,r),i=versionCompare(t,n.version);return{isNewVersionAvailable:i.needsUpdate,updateType:i.updateType,url:n.url}}catch(e){l=e;const r=e instanceof Error?e.message:String(e);if(o.error(`Error checking version (attempt ${s+1}/4): ${r}`),3===s)throw e;s++,o.info("Retrying version check in 3000ms..."),await new Promise(e=>setTimeout(e,3e3))}throw l instanceof Error?l:new Error("Failed to check for updates after multiple attempts")},getFallbackStoreUrl=()=>{const e=getSystemName().toLowerCase(),r=getBundleId(),t=getCountry();return"ios"===e?`https://apps.apple.com/${t}/app/${r}`:`https://play.google.com/store/apps/details?id=${r}&hl=${t}`};export const VersionCheck={isNewVersionAvailable:async()=>checkForUpdates(),openUpdateUrl:async e=>{const{Linking:r}=require("react-native");try{if(e)o.info(`Opening app store URL: ${e}`),await r.openURL(e);else{const e=getFallbackStoreUrl();o.info(`Opening fallback store URL: ${e}`),await r.openURL(e)}return!0}catch(t){const n=t instanceof Error?t.message:String(t);o.error(`Failed to open store URL: ${n}`);try{if(e){const e=getFallbackStoreUrl();return o.info(`Trying fallback store URL: ${e}`),await r.openURL(e),!0}}catch(e){const r=e instanceof Error?e.message:String(e);o.error(`Failed to open fallback URL: ${r}`)}return!1}},getFallbackStoreUrl:getFallbackStoreUrl};export const useVersionCheck=(e,i=n("environment","Development"))=>{const[a,s]=t({isUpdateRequired:!1,updateUrl:null});return r(()=>{let r=!0;return e&&"Production"===i&&(async()=>{try{const{isNewVersionAvailable:e,url:t}=await VersionCheck.isNewVersionAvailable();e&&r?(o.info("Update required. URL:",t||"Not available yet"),s({isUpdateRequired:!0,updateUrl:t})):r&&o.info("App is up to date")}catch(e){o.error("Error checking app version:",e)}})(),()=>{r=!1}},[e,i]),a};export default VersionCheck;
1
+ "use strict";import*as Application from"expo-application";import{useMemo,useSyncExternalStore}from"react";import{addMinAppVersionListener,getMinAppVersion}from"../api/minAppVersion.js";import{getConfigValue}from"../config/index.js";import{Logger}from"../logger/index.js";import{isBuildBelowMinimum,parseBuildNumber}from"./buildNumber.js";const expoApplicationBackend={getBundleId:()=>Application.applicationId??"",getVersion:()=>Application.nativeApplicationVersion??"",getBuildNumber:()=>Application.nativeBuildVersion??""};let deviceInfo=null;export const setDeviceInfoImplementation=e=>{deviceInfo=e};const missing=(e,r)=>{const t=new Error(`[VersionCheck] the app's ${e} is not available. Call setConfig({ ${r}: ... }) — every Webority app already has this value in appsettings.Compiled.json — or setDeviceInfoImplementation() with a native provider.`);return Logger.error(t.message),t},getSystemName=()=>{const{Platform:e}=require("react-native");return e.OS},getBundleId=()=>{const e=getConfigValue("appId");if(e)return e;const r=(deviceInfo??expoApplicationBackend).getBundleId();if(r)return r;throw missing("store identifier","appId")},getBuildNumber=()=>{const e=parseBuildNumber(getConfigValue("appBuild"));if(null!==e)return e;const r=parseBuildNumber(deviceInfo?.getBuildNumber?.()??expoApplicationBackend.getBuildNumber());if(null!==r)return r;throw missing("build number","appBuild")},getCountry=()=>getConfigValue("country","in").toLowerCase(),subscribeMinimum=e=>addMinAppVersionListener(()=>e()),getFallbackStoreUrl=()=>{const e=getSystemName().toLowerCase(),r=getCountry();if("ios"===e){const e=getConfigValue("appStoreId");return e?`https://apps.apple.com/${r}/app/id${e}`:`https://apps.apple.com/${r}/app/${getBundleId()}`}return`https://play.google.com/store/apps/details?id=${getBundleId()}&hl=${r}`},storeUrlOrNull=()=>{try{return getFallbackStoreUrl()}catch{return null}},checkForUpdates=()=>{const e=isBuildBelowMinimum(getBuildNumber(),getMinAppVersion());return{isNewVersionAvailable:e,updateType:e?"major":null,url:e?storeUrlOrNull():null}};export const VersionCheck={isNewVersionAvailable:async()=>checkForUpdates(),openUpdateUrl:async e=>{const{Linking:r}=require("react-native");try{if(e)Logger.info(`Opening app store URL: ${e}`),await r.openURL(e);else{const e=getFallbackStoreUrl();Logger.info(`Opening fallback store URL: ${e}`),await r.openURL(e)}return!0}catch(t){const i=t instanceof Error?t.message:String(t);Logger.error(`Failed to open store URL: ${i}`);try{if(e){const e=getFallbackStoreUrl();return Logger.info(`Trying fallback store URL: ${e}`),await r.openURL(e),!0}}catch(e){const r=e instanceof Error?e.message:String(e);Logger.error(`Failed to open fallback URL: ${r}`)}return!1}},getFallbackStoreUrl:getFallbackStoreUrl};const NOT_REQUIRED={isUpdateRequired:!1,updateUrl:null};export const useVersionCheck=(e,r=getConfigValue("environment","Development"))=>{const t=useSyncExternalStore(subscribeMinimum,getMinAppVersion,getMinAppVersion);return useMemo(()=>{if(!e||"Production"!==r||null===t)return NOT_REQUIRED;try{if(!isBuildBelowMinimum(getBuildNumber(),t))return NOT_REQUIRED}catch{return NOT_REQUIRED}return{isUpdateRequired:!0,updateUrl:storeUrlOrNull()}},[e,r,t])};export default VersionCheck;export{isBuildBelowMinimum,parseBuildNumber}from"./buildNumber.js";
@@ -0,0 +1,25 @@
1
+ import type { AxiosInstance } from 'axios';
2
+ export declare const CLIENT_BUILD_HEADER = "X-Client-Build";
3
+ export declare const CLIENT_KIND_HEADER = "X-Client-Kind";
4
+ export declare const ACCOUNT_ID_HEADER = "X-Account-Id";
5
+ /** The server picks session rules and the minimum version per kind; the names match its enum. */
6
+ export type ClientKind = 'Browser' | 'MobileApp';
7
+ export declare const CLIENT_KIND: ClientKind;
8
+ /** `<appVersion>+<commit>`, `<appVersion>` when no commit was stamped, `null` before init. */
9
+ export declare const getClientBuild: () => string | null;
10
+ /**
11
+ * Stamps X-Client-Kind and X-Client-Build on every request. A value the app
12
+ * already set (a default header, or a per-request one) wins, so an app that
13
+ * wired them by hand keeps working unchanged.
14
+ */
15
+ export declare const applyClientHeaders: (client: AxiosInstance) => void;
16
+ export type AccountIdProvider = () => string | null | undefined | Promise<string | null | undefined>;
17
+ /**
18
+ * Sets X-Account-Id from the app's provider on every send, overwriting, so a
19
+ * request replayed after a refresh or an account switch carries the current
20
+ * account rather than the one it first went out with. `null` sends none.
21
+ */
22
+ export declare const applyAccountIdHeader: (headers: {
23
+ set: (name: string, value: string) => unknown;
24
+ }, provider: AccountIdProvider | undefined) => Promise<void>;
25
+ //# sourceMappingURL=clientHeaders.d.ts.map
@@ -0,0 +1,24 @@
1
+ import type { AxiosInstance } from 'axios';
2
+ /**
3
+ * The lowest internal build number (the integer `VERSION_INTERNAL`) the API
4
+ * still serves for this client kind. The API sends it on every response,
5
+ * errors included; cross-origin it is readable only when the API lists it in
6
+ * `Access-Control-Expose-Headers`.
7
+ */
8
+ export declare const MIN_APP_VERSION_HEADER = "X-Min-App-Version";
9
+ export type MinAppVersionListener = (minAppVersion: number) => void;
10
+ /** The floor the API last named, or `null` before any response carried one. */
11
+ export declare const getMinAppVersion: () => number | null;
12
+ /**
13
+ * Called with the floor whenever it changes, and at once with the known floor
14
+ * when one has already arrived, so a late subscriber misses nothing. Returns
15
+ * the unsubscribe function. The versionCheck module is the one subscriber an
16
+ * app normally needs.
17
+ */
18
+ export declare const addMinAppVersionListener: (listener: MinAppVersionListener) => (() => void);
19
+ /**
20
+ * Reads the floor off every response the client receives, a rejected one too:
21
+ * a 426 Upgrade Required is exactly the case the floor exists for.
22
+ */
23
+ export declare const readMinAppVersion: (client: AxiosInstance) => void;
24
+ //# sourceMappingURL=minAppVersion.d.ts.map
@@ -0,0 +1,21 @@
1
+ import type { AxiosInstance } from 'axios';
2
+ /**
3
+ * A fresh v4 uuid. `crypto.randomUUID` exists only in a secure context (HTTPS or localhost) and
4
+ * not at all on Hermes, so the id falls back to `getRandomValues`, then to `Math.random`. A
5
+ * correlation id only has to be unique enough to find one request in the logs; it is never a secret.
6
+ */
7
+ export declare const newCorrelationId: () => string;
8
+ /**
9
+ * Applies the configured timeout and the correlation id per request, read on every send so a
10
+ * value set after import still applies (the same reason `applyConfiguredBaseUrl` exists).
11
+ * - `timeoutMs`: used when the request carries no timeout of its own; a request's own axios
12
+ * `timeout` wins. 0, either way, means no timeout (axios's own meaning).
13
+ * - `correlationIdHeader`: a fresh uuid unless the request already carries one. A retry
14
+ * resends the same config, so it keeps the id the first attempt went out with.
15
+ *
16
+ * axios fills a missing `timeout` with its own default of 0, which would make "this request
17
+ * set none" and "this request asked for none" look alike. The client's default is therefore
18
+ * cleared, so an unset timeout reaches the interceptor as undefined.
19
+ */
20
+ export declare const applyRequestDefaults: (client: AxiosInstance) => void;
21
+ //# sourceMappingURL=requestDefaults.d.ts.map
@@ -1,4 +1,4 @@
1
- import type { AxiosError, AxiosInstance } from 'axios';
1
+ import { type AxiosError, type AxiosInstance } from 'axios';
2
2
  export interface RetryPolicy {
3
3
  retries?: number;
4
4
  /** `attempt` is 1-based: 1 is the first retry, not the original request. */
@@ -13,15 +13,29 @@ declare module 'axios' {
13
13
  }
14
14
  }
15
15
  /**
16
- * Status-based ONLY, deliberately: a bare network error (no `error.response`)
17
- * is never retryable. Both clients carry non-idempotent requests, login and OTP
18
- * on the public one and every write on the user one, and a broken connection
19
- * says nothing about whether the request reached the server. Retrying can
20
- * therefore create a second session, or a duplicate record, that the user never
21
- * sees. A generic network-error retry rule is not a substitute: it decides on
22
- * the transport alone and never looks at the HTTP method, so it retries POSTs
23
- * as readily as GETs.
16
+ * A write carrying this header asserts that the API deduplicates it, which is
17
+ * the only thing that makes a POST or PATCH safe to send twice.
24
18
  */
19
+ export declare const IDEMPOTENCY_KEY_HEADER = "Idempotency-Key";
25
20
  export declare const isRetryableStatus: (status: number | undefined) => boolean;
21
+ export declare const isNeverSentError: (error: AxiosError) => boolean;
22
+ /**
23
+ * The wait a refusal named, in milliseconds: the `Retry-After` header (seconds)
24
+ * or a `retryAfterSeconds` body field. `null` when it named none. Cross-origin,
25
+ * the header is readable only when the API lists it in `Access-Control-Expose-Headers`.
26
+ */
27
+ export declare const retryAfterMs: (error: AxiosError) => number | null;
28
+ /**
29
+ * The one retry rule both platforms share:
30
+ * - a request that provably never reached the server is resent, whatever its method;
31
+ * - GET, HEAD, OPTIONS, PUT and DELETE are resent on 429, any 5xx, a timeout or a
32
+ * dropped connection, because a second one leaves the server as one would;
33
+ * - POST and PATCH are resent only on 429 or 503 (the server refused before
34
+ * doing the work) AND only when they carry an `Idempotency-Key`; otherwise never;
35
+ * - a refusal that names a wait longer than 30 seconds is final;
36
+ * - a cancelled request is never resent.
37
+ * A request that knows better passes its own `shouldRetry`.
38
+ */
39
+ export declare const isRetryableFailure: (error: AxiosError) => boolean;
26
40
  export declare const attachRetry: (client: AxiosInstance, policy?: RetryPolicy) => void;
27
41
  //# sourceMappingURL=retry.d.ts.map
@@ -1,3 +1,4 @@
1
+ import { type SessionEndReason } from '../auth/authStore';
1
2
  import type { UserAuthOptions } from '../initUserAuth';
2
3
  /**
3
4
  * The one token-refresh flow in the library, run by userClient's interceptors.
@@ -27,17 +28,28 @@ export declare const injectUserAuth: (options: UserAuthOptions) => void;
27
28
  */
28
29
  export declare const resetRefreshState: () => void;
29
30
  export declare const getErrorMessage: (error: unknown) => string;
31
+ /** The app's account-id provider, once initUserAuth has run with one. */
32
+ export declare const getAccountIdProvider: () => UserAuthOptions["accountId"];
30
33
  /** The refresh endpoint's path, once initUserAuth has run. */
31
34
  export declare const getRefreshPath: () => string | undefined;
32
35
  export declare const dispatchLogout: () => Promise<void>;
36
+ export declare const SIGN_OUT_REASON_HEADER = "X-Sign-Out-Reason";
37
+ /** The server's sign-out reason from response headers (AxiosHeaders or a plain object), or null. */
38
+ export declare const readSignOutReason: (headers: unknown) => string | null;
39
+ /**
40
+ * The session is over: clear it (tokens and session user removed, listeners told why, with
41
+ * the server's reason), then dispatch the app's logout.
42
+ */
43
+ export declare const endSessionAndLogout: (reason: SessionEndReason, signOutReason: string | null) => Promise<void>;
33
44
  export declare const isRefreshInFlight: () => boolean;
34
45
  /** Wait for the in-flight refresh without joining it: resolved or rejected by processQueue. */
35
46
  export declare const queueBehindRefresh: () => Promise<string>;
36
47
  /**
37
48
  * Refresh the access token, joining a refresh already in flight rather than
38
49
  * starting a second one. Every request queued behind it is replayed on success
39
- * and rejected on failure. Logs out only when the failure proves the session
40
- * is over; a network error, timeout or 5xx keeps it and rejects.
50
+ * and rejected on failure. Ends the session (`clearSession('rejected')`, then
51
+ * logout) only when the failure proves it is over; a network error, timeout or
52
+ * 5xx keeps it and rejects.
41
53
  */
42
54
  export declare const refreshAccessToken: () => Promise<string>;
43
55
  //# sourceMappingURL=tokenRefresh.d.ts.map
@@ -1,7 +1,7 @@
1
1
  import { type AxiosInstance } from 'axios';
2
2
  import '../types/axios';
3
3
  export type { RefreshableStore } from './tokenRefresh';
4
- export { injectStore, injectUserAuth, resetRefreshState } from './tokenRefresh';
4
+ export { injectStore, injectUserAuth, resetRefreshState, SIGN_OUT_REASON_HEADER } from './tokenRefresh';
5
5
  /**
6
6
  * Thrown by the request interceptor when the device is offline, before the
7
7
  * request ever reaches the adapter (and therefore the network). Distinct from
@@ -10,10 +10,56 @@
10
10
  */
11
11
  export declare const ACCESS_TOKEN_KEY = "accessToken";
12
12
  export declare const REFRESH_TOKEN_KEY = "refreshToken";
13
+ /**
14
+ * Where the signed-in user's profile snapshot is kept between launches. It is
15
+ * not a secret, and a profile can outgrow what the keychain takes comfortably,
16
+ * so it lives in `Storage`, not the secure store.
17
+ */
18
+ export declare const SESSION_USER_KEY = "sessionUser";
13
19
  export interface StoredToken {
14
20
  token: string;
15
21
  expiry: string;
16
22
  }
23
+ /**
24
+ * Why a session ended, handed to every `addSessionEndedListener` listener.
25
+ * - `signedOut`: the app ended it (the default for `clearSession`).
26
+ * - `rejected`: the refresh endpoint refused the refresh token.
27
+ * - `terminated`: the API reported the session dead (security stamp, admin).
28
+ * - `corrupt`: a stored token could not be read back as a token.
29
+ * - `otherTab`: web only. Another tab signed out of the shared session; never
30
+ * emitted here, declared so both platforms share one type.
31
+ */
32
+ export type SessionEndReason = 'signedOut' | 'rejected' | 'terminated' | 'corrupt' | 'otherTab';
33
+ /** What else is known about a session end, beside its reason. */
34
+ export interface SessionEndDetail {
35
+ /**
36
+ * The server's own name for why it ended the session, from the sign-out-reason response
37
+ * header (`X-Sign-Out-Reason` by default, `initUserAuth({ signOutReasonHeader })`), on a
38
+ * terminated session or a refused refresh. `null` when the server named none, and always
39
+ * `null` for `signedOut` and `corrupt` unless the app passes one.
40
+ */
41
+ signOutReason: string | null;
42
+ }
43
+ export type SessionEndedListener = (reason: SessionEndReason, detail: SessionEndDetail) => void;
44
+ /**
45
+ * The keys an app stored its session under before it adopted this library. `initUserAuth`
46
+ * takes them as `legacyKeys`; the first token read that finds the library's keys empty moves a
47
+ * session found there (see `getToken`). On mobile the token keys are secure-store services and
48
+ * `sessionUser` is a `Storage` key. Each token may be the bare string or the library's own
49
+ * `{ token, expiry }` JSON.
50
+ */
51
+ export interface LegacyTokenKeys {
52
+ accessToken: string;
53
+ refreshToken: string;
54
+ /** The old key of the signed-in user's profile snapshot, if the app kept one. */
55
+ sessionUser?: string;
56
+ }
57
+ /**
58
+ * The expiry given to a migrated refresh token whose expiry the old store never kept and which
59
+ * is not a JWT. The server stays the judge: a refresh token it no longer honours is refused at
60
+ * the first refresh, which ends the session exactly as it would have ended before.
61
+ */
62
+ export declare const LEGACY_REFRESH_TOKEN_LIFETIME_MS: number;
17
63
  /**
18
64
  * The shape this module speaks to its store through. expo-secure-store is
19
65
  * adapted to it below; an app or a test can supply anything else of this shape
@@ -75,5 +121,35 @@ export declare const getToken: (key: string) => Promise<StoredToken | null>;
75
121
  * Remove token by key.
76
122
  */
77
123
  export declare const removeToken: (key: string) => Promise<void>;
124
+ /**
125
+ * Keep the signed-in user's profile snapshot so the app can paint the signed-in
126
+ * shell at launch before the profile fetch returns. Rejects when the write
127
+ * fails, like `storeToken`.
128
+ */
129
+ export declare const storeSessionUser: <T>(user: T) => Promise<void>;
130
+ /**
131
+ * The stored user, or null. An unreadable snapshot is removed and reported,
132
+ * but does not end the session: it is a cache the next profile fetch refills.
133
+ */
134
+ export declare const getSessionUser: <T>() => Promise<T | null>;
135
+ export declare const removeSessionUser: () => Promise<void>;
136
+ /** Set by `initUserAuth({ legacyKeys })`; `null` turns the migration off. */
137
+ export declare const setLegacyTokenKeys: (keys: LegacyTokenKeys | null) => void;
138
+ /**
139
+ * Be told when the session ends, with the reason. Returns the unsubscribe.
140
+ * Fires once per session end, whichever path ended it; the app reacts here
141
+ * (clear its state, route to sign-in, show why) instead of each caller doing it.
142
+ */
143
+ export declare const addSessionEndedListener: (listener: SessionEndedListener) => (() => void);
144
+ /**
145
+ * End the session: remove both tokens and the session user, then tell every
146
+ * `addSessionEndedListener` listener why. Safe to call more than once: the
147
+ * listeners hear only the call that found a session to end, so the refresh
148
+ * flow and the app's own sign-out can both call it without a double event.
149
+ * The library calls it itself with `rejected` when the refresh endpoint refuses
150
+ * the refresh token and `terminated` when the API reports the session dead,
151
+ * with the server's sign-out reason in `detail`.
152
+ */
153
+ export declare const clearSession: (reason?: SessionEndReason, detail?: Partial<SessionEndDetail>) => Promise<void>;
78
154
  export type { KeychainLike, SecureCredentials };
79
155
  //# sourceMappingURL=authStore.d.ts.map
@@ -1,5 +1,5 @@
1
- export type { StoredToken } from './authStore';
2
- export { getToken, removeToken, storeToken } from './authStore';
1
+ export type { LegacyTokenKeys, SessionEndDetail, SessionEndedListener, SessionEndReason, StoredToken } from './authStore';
2
+ export { ACCESS_TOKEN_KEY, addSessionEndedListener, clearSession, getSessionUser, getToken, LEGACY_REFRESH_TOKEN_LIFETIME_MS, REFRESH_TOKEN_KEY, removeSessionUser, removeToken, SESSION_USER_KEY, setLegacyTokenKeys, storeSessionUser, storeToken } from './authStore';
3
3
  export type { JwtClaims } from './jwt';
4
4
  export { decodeJWT, getJWTExpiry, isJWTExpired } from './jwt';
5
5
  //# sourceMappingURL=index.d.ts.map
@@ -2,7 +2,7 @@
2
2
  * Configuration management for Webority packages
3
3
  * Provides a flexible way to inject configuration without hard dependencies
4
4
  */
5
- export type LogLevel = 'info' | 'warn' | 'error';
5
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
6
6
  export type Environment = 'Development' | 'Production' | 'Staging';
7
7
  export interface Config {
8
8
  /** API base URL (required before making API calls) */
@@ -15,7 +15,7 @@ export interface Config {
15
15
  environment: Environment;
16
16
  /**
17
17
  * ISO 3166-1 alpha-2 country code (lowercase) used by the version-check
18
- * module to scope App Store / Play Store lookups. Defaults to `'in'`.
18
+ * module to build the App Store / Play Store link. Defaults to `'in'`.
19
19
  */
20
20
  country: string;
21
21
  /**
@@ -27,12 +27,38 @@ export interface Config {
27
27
  appId: string | null;
28
28
  /** The app's user-visible version string, e.g. `'1.4.2'`. Same rationale as `appId`. */
29
29
  appVersion: string | null;
30
+ /** The short git sha stamped into the build; with `appVersion` it makes the X-Client-Build header. */
31
+ commit: string | null;
32
+ /**
33
+ * The app's internal build number (`VERSION_INTERNAL`: Android versionCode,
34
+ * iOS CFBundleVersion). The version check compares it with the API's
35
+ * `X-Min-App-Version`. Falls back to expo-application when unset.
36
+ */
37
+ appBuild: number | null;
38
+ /**
39
+ * The numeric Apple App Store id (the digits after `id` in the store link).
40
+ * The App Store does not resolve a bundle id, so the iOS update button needs it.
41
+ */
42
+ appStoreId: string | null;
30
43
  /**
31
44
  * Log a copy-pasteable curl command for every API request. Credentials are
32
45
  * redacted either way, but this is still request-level noise, so it stays off
33
46
  * unless an app asks for it while debugging.
34
47
  */
35
48
  logCurl: boolean;
49
+ /**
50
+ * Request timeout for both clients, in milliseconds. A request overrides it with its own
51
+ * axios `timeout`. Default 30000.
52
+ */
53
+ timeoutMs: number;
54
+ /** How many times a failed request is resent under the retry rule. 0 turns retrying off. Default 3. */
55
+ retries: number;
56
+ /**
57
+ * Header that carries a fresh id (a uuid) on every request, for tracing one request across
58
+ * the app, the API and its logs. A retry keeps the id it first went out with. `null`
59
+ * sends none. Default `'X-Correlation-Id'`.
60
+ */
61
+ correlationIdHeader: string | null;
36
62
  }
37
63
  /**
38
64
  * Info lines (each token refresh, each unauthorised URL) reach the device log
@@ -40,6 +66,9 @@ export interface Config {
40
66
  * errors only.
41
67
  */
42
68
  export declare const DEFAULT_LOG_LEVEL: LogLevel;
69
+ export declare const DEFAULT_TIMEOUT_MS = 30000;
70
+ export declare const DEFAULT_RETRIES = 3;
71
+ export declare const DEFAULT_CORRELATION_ID_HEADER = "X-Correlation-Id";
43
72
  /**
44
73
  * Set global configuration for all Webority packages
45
74
  */
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Normalizes any failure a thunk, a service or the RTK Query base query can see into one
3
+ * `ApiError`. Identical source in @webority/web-core and @webority/mobile-core.
4
+ *
5
+ * Bodies read, newest first: the fleet body `{ code, message, traceId, errors }` (D-077),
6
+ * where `errors` is a ModelState dictionary (`{ Field: ["msg"] }`) or a list of
7
+ * `{ field, message, reference }` (a refusal that names its fields),
8
+ * ProblemDetails `{ title, detail, errors }` from APIs not yet moved, legacy `{ error }` and
9
+ * `{ message }`, and a plain-text body.
10
+ *
11
+ * Message precedence is fixed (react.md): first field error, then `message`, `detail`, a
12
+ * legacy `error`, `title`, then `Request failed (<status>)`. `title` is last because it is
13
+ * the ProblemDetails category ("Bad Request"), not the curated sentence.
14
+ *
15
+ * Two things never become `message`: a 5xx body (our failure, nothing the user can act on)
16
+ * and anything that reads like an exception dump. The server's text goes on `detail` for
17
+ * the logger instead.
18
+ */
19
+ export interface ApiError {
20
+ /** Stable snake_case branch key from the server (`not_found`, `validation_failed`). Branch on it, never render it. */
21
+ code: string | null;
22
+ /** The only user-facing string. */
23
+ message: string;
24
+ /** camelCase field path to its first message, ready for react-hook-form `setError`. */
25
+ fieldErrors: Record<string, string>;
26
+ /** HTTP status, or 0 when no response arrived. */
27
+ status: number;
28
+ /** True when no answer arrived (offline, network drop, timeout): a retry is worth offering. */
29
+ offline: boolean;
30
+ /** The server's trace id, for a support reference. */
31
+ traceId: string | null;
32
+ /** What the server or runtime actually said. For the logger, never for the screen. */
33
+ detail: string | null;
34
+ }
35
+ export declare const SERVER_FAILURE_MESSAGE = "Something went wrong on our side. Please try again in a moment.";
36
+ export declare const OFFLINE_MESSAGE = "You appear to be offline. Check your connection and try again.";
37
+ export declare const TIMEOUT_MESSAGE = "That took too long to respond. Check your connection and try again.";
38
+ export declare const UNEXPECTED_MESSAGE = "Something went wrong. Please try again.";
39
+ /** `code` for a request the caller cancelled (axios `CanceledError`). Never toasted. */
40
+ export declare const REQUEST_CANCELED_CODE = "request_canceled";
41
+ /** `code` for a response that failed schema validation at the service boundary. */
42
+ export declare const CONTRACT_ERROR_CODE = "contract_error";
43
+ /** `code` for a request that got no answer in time (axios `ECONNABORTED` / `ETIMEDOUT`). `offline` is true. */
44
+ export declare const TIMEOUT_CODE = "timeout";
45
+ /** `code` for a request that never reached the server (offline, DNS, CORS, a dropped connection). `offline` is true. */
46
+ export declare const NETWORK_CODE = "network";
47
+ /** `Items[0].UnitPrice` -> `items[0].unitPrice`; `$.email` (System.Text.Json path) -> `email`. */
48
+ export declare const toFieldPath: (key: string) => string;
49
+ export declare const isApiError: (value: unknown) => value is ApiError;
50
+ export declare function extractApiError(error: unknown): ApiError;
51
+ /**
52
+ * True when no answer arrived (offline, a dropped connection, a timeout), so the caller cannot
53
+ * tell what the server did. The same as `extractApiError(error).offline`.
54
+ */
55
+ export declare const isNoResponse: (error: unknown) => boolean;
56
+ /**
57
+ * The sentence a screen shows for a failure: the server's own curated message when it sent one,
58
+ * otherwise the screen's `fallback` ("We couldn't save the project."), otherwise the library's
59
+ * generic sentence. A lost answer always reads as the connection message, because "check your
60
+ * connection" is more accurate than any screen's fallback. A 5xx, a cancelled request and a body
61
+ * with no usable sentence count as "the server said nothing usable".
62
+ *
63
+ * `fallback: null` is the "say nothing" mode, for a screen that shows only what the server
64
+ * curated (a live panel that keeps its last result): it returns null when the server said
65
+ * nothing usable, and still the connection message for a lost answer.
66
+ */
67
+ export declare function apiErrorMessage(error: unknown, fallback?: string): string;
68
+ export declare function apiErrorMessage(error: unknown, fallback: string | null): string | null;
69
+ //# sourceMappingURL=extractApiError.d.ts.map
@@ -0,0 +1,3 @@
1
+ export type { ApiError } from './extractApiError';
2
+ export { apiErrorMessage, CONTRACT_ERROR_CODE, extractApiError, isApiError, isNoResponse, NETWORK_CODE, OFFLINE_MESSAGE, REQUEST_CANCELED_CODE, SERVER_FAILURE_MESSAGE, TIMEOUT_CODE, TIMEOUT_MESSAGE, toFieldPath, UNEXPECTED_MESSAGE } from './extractApiError';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -1,24 +1,39 @@
1
- export interface FormatCurrencyOptions {
1
+ /**
2
+ * The currency keys plus any other `Intl.NumberFormatOptions` (`currencyDisplay`, `notation`,
3
+ * `signDisplay`, ...), passed through to Intl.NumberFormat.
4
+ */
5
+ export interface FormatCurrencyOptions extends Omit<Intl.NumberFormatOptions, 'style' | 'currency'> {
2
6
  /** ISO 4217 currency code. Defaults to `'INR'`. */
3
7
  currency?: string;
4
- /** BCP-47 locale tag. Defaults to `'en-IN'` (Indian numbering system). */
8
+ /** BCP-47 locale tag. Defaults to the configured locale, else `'en-IN'` (Indian numbering system). */
5
9
  locale?: string;
6
- /** Minimum fraction digits. Defaults to 0 for whole amounts, 2 otherwise. */
10
+ /** Minimum fraction digits. Defaults to 0 for whole amounts, else the currency's own (2 for INR, 0 for JPY). */
7
11
  minimumFractionDigits?: number;
8
- /** Maximum fraction digits. Defaults to 2. */
12
+ /** Maximum fraction digits. Defaults to the currency's own. */
9
13
  maximumFractionDigits?: number;
10
14
  /** Strip the currency symbol — useful when the symbol is rendered separately. */
11
15
  stripSymbol?: boolean;
16
+ /**
17
+ * What a missing amount prints. Defaults to the configured placeholder once the app has
18
+ * configured a locale or an `empty` (`configureFormatters`), else `''`.
19
+ */
20
+ empty?: string;
12
21
  }
13
22
  /**
14
23
  * Format an amount as currency. Defaults to **INR** with Indian numbering (`en-IN`),
15
- * which matches the standard convention across Webority projects.
24
+ * which matches the standard convention across Webority projects. Called with no
25
+ * options: "₹1,50,000" for 150000, "₹1,234.50" for 1234.5, and `''` for a missing amount.
26
+ * Once the app configures a locale (`configureFormatters({ locale })`), the amount is
27
+ * written in that locale and a missing amount prints the configured placeholder ("-").
28
+ * A numeric string reads as its number. For money the API carries in minor units (paise),
29
+ * use `formatMinor`.
16
30
  *
17
31
  * @example
18
32
  * formatCurrency(150000) // ₹1,50,000
19
33
  * formatCurrency(1234.5) // ₹1,234.50
20
- * formatCurrency(99, { currency: 'USD', locale: 'en-US' }) // $99.00
34
+ * formatCurrency(99, { currency: 'USD', locale: 'en-US' }) // $99
21
35
  * formatCurrency(150000, { stripSymbol: true }) // 1,50,000
36
+ * formatCurrency(1500000, { notation: 'compact' }) // ₹15L
22
37
  */
23
- export declare const formatCurrency: (amount: number | null | undefined, options?: FormatCurrencyOptions) => string;
38
+ export declare const formatCurrency: (amount: number | string | null | undefined, options?: FormatCurrencyOptions) => string;
24
39
  //# sourceMappingURL=currency.d.ts.map
@@ -1,19 +1,19 @@
1
+ import { type DateValue, type FormatDateOptions, type FormatDateTimeOptions, type FormatTimeOptions } from './house';
1
2
  export type DateInput = Date | string | number;
2
3
  export type DateStyle = 'short' | 'medium' | 'long' | 'full';
3
4
  export type TimeStyle = 'short' | 'medium' | 'long';
4
- /**
5
- * Locale-aware date formatter. Defaults to `'medium'` style and `'en-IN'` locale,
6
- * matching the Indian-locale convention used across Webority apps.
7
- */
8
- export declare const formatDate: (value: DateInput, style?: DateStyle, locale?: string) => string;
9
- /**
10
- * Locale-aware time formatter (24-hour fallback when Intl is unavailable).
11
- */
12
- export declare const formatTime: (value: DateInput, style?: TimeStyle, locale?: string) => string;
13
- /**
14
- * Combined date + time formatter.
15
- */
16
- export declare const formatDateTime: (value: DateInput, dateStyle?: DateStyle, timeStyle?: TimeStyle, locale?: string) => string;
5
+ /** "08-Apr-2026" in the configured zone; a bare calendar date stays that day. */
6
+ export declare function formatDate(value: DateValue, options?: FormatDateOptions): string;
7
+ /** Intl `dateStyle` output, `'en-IN'` by default. */
8
+ export declare function formatDate(value: DateInput, style: DateStyle, locale?: string): string;
9
+ /** "8:05 PM" in IST, labelled in any other zone. */
10
+ export declare function formatTime(value: DateValue, options?: FormatTimeOptions): string;
11
+ /** Intl `timeStyle` output (24-hour fallback when Intl is unavailable). */
12
+ export declare function formatTime(value: DateInput, style: TimeStyle, locale?: string): string;
13
+ /** "08-Apr-2026, 8:05 PM" in IST, labelled in any other zone. */
14
+ export declare function formatDateTime(value: DateValue, options?: FormatDateTimeOptions): string;
15
+ /** Intl `dateStyle` + `timeStyle` output. */
16
+ export declare function formatDateTime(value: DateInput, dateStyle: DateStyle, timeStyle?: TimeStyle, locale?: string): string;
17
17
  /**
18
18
  * Returns a humanized relative-time string (e.g. "5 minutes ago", "in 3 days").
19
19
  * Uses `Intl.RelativeTimeFormat` when available; otherwise a sensible English fallback.
@@ -21,9 +21,11 @@ export declare const diffMs: (a: DateInput, b: DateInput) => number;
21
21
  export declare const startOfDay: (value: DateInput) => Date | null;
22
22
  /**
23
23
  * Adds (or, with a negative count, subtracts) whole calendar days in the
24
- * device's local timezone. Returns null for unparseable input.
24
+ * device's local timezone. Returns null for a missing or unparseable date or
25
+ * count, so the result can go straight to `formatDate`. Same name and meaning
26
+ * as `@webority/web-core`'s `addDays`.
25
27
  */
26
- export declare const addDays: (value: DateInput, count: number) => Date | null;
28
+ export declare const addDays: (value: DateInput | null | undefined, count: number) => Date | null;
27
29
  /**
28
30
  * Small dayjs/moment-style token formatter — YYYY/YY, MMMM/MMM/MM/M,
29
31
  * DD/D, dddd/ddd, HH/H (24h), hh/h (12h), mm/m, ss/s, A/a (upper/lower AM/PM).
@@ -0,0 +1,48 @@
1
+ /**
2
+ * App-wide settings for the house formatters. This file is byte-identical in
3
+ * @webority/mobile-core and @webority/web-core, so a screen formats the same
4
+ * instant the same way on a phone and in a browser.
5
+ *
6
+ * Zone policy: an India-market product shows every instant in IST, unlabelled
7
+ * (the default). A product whose users span time zones opts into the viewer's
8
+ * own zone; times then carry a zone label so an instant is never ambiguous.
9
+ *
10
+ * Text policy: with no locale configured, every formatter prints the house
11
+ * text, assembled here so it reads the same on every engine ("08-Apr-2026",
12
+ * "8:05 PM", "1,23,456.79"). Configuring a locale hands the words and
13
+ * separators to Intl for that locale: month names, digit separators, the
14
+ * 12- or 24-hour clock and its AM/PM casing, zone names and relative phrases.
15
+ * The date order stays DD-MMM-YYYY either way.
16
+ */
17
+ /** `'IST'`: every instant in India Standard Time. `'viewer'`: the device or browser zone, labelled. */
18
+ export type ZoneMode = 'IST' | 'viewer';
19
+ /** How the integer part of a number is grouped. */
20
+ export type NumberGrouping = 'indian' | 'international' | 'none';
21
+ export interface FormatterConfig {
22
+ /** Default zone for every date and time formatter. */
23
+ zone: ZoneMode;
24
+ /**
25
+ * BCP-47 locale. Left at its default (en-IN), it only picks the house digit
26
+ * grouping. Configured (`configureFormatters({ locale })`), it switches every
27
+ * formatter to Intl's text for that locale: en-IN gives "08-Sept-2026" and
28
+ * "IST", de-DE gives "1.234,5" and "20:05".
29
+ */
30
+ locale: string;
31
+ /** What every formatter returns for a missing or unreadable value. */
32
+ empty: string;
33
+ }
34
+ /**
35
+ * Set the zone mode, locale or empty placeholder for the whole app. Call it
36
+ * once at startup; a key left out (or undefined) keeps its current value.
37
+ * Passing nothing restores the defaults (IST, en-IN, "-", house text).
38
+ */
39
+ export declare const configureFormatters: (config?: Partial<FormatterConfig>) => void;
40
+ /** The settings in force, as a copy. */
41
+ export declare const getFormatterConfig: () => FormatterConfig;
42
+ /** Whether the app set `key` through `configureFormatters`, rather than leaving the default. */
43
+ export declare const isFormatterConfigured: (key: keyof FormatterConfig) => boolean;
44
+ /** The configured locale when the app set one (Intl text), else null (house text). */
45
+ export declare const configuredLocale: () => string | null;
46
+ /** The grouping a locale implies: Indian for a locale whose region is IN. */
47
+ export declare const groupingForLocale: (locale: string) => NumberGrouping;
48
+ //# sourceMappingURL=formatConfig.d.ts.map