@webority/mobile-core 0.0.23

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 (211) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +39 -0
  3. package/android/build.gradle +65 -0
  4. package/android/src/newarch/java/com/webority/mobilecore/smsretriever/AppSignatureHelper.kt +57 -0
  5. package/android/src/newarch/java/com/webority/mobilecore/smsretriever/SmsRetrieverModule.kt +20 -0
  6. package/android/src/newarch/java/com/webority/mobilecore/smsretriever/SmsRetrieverModuleImpl.kt +146 -0
  7. package/android/src/newarch/java/com/webority/mobilecore/smsretriever/SmsRetrieverPackage.kt +33 -0
  8. package/lib/commonjs/api/baseUrl.js +34 -0
  9. package/lib/commonjs/api/curl.js +109 -0
  10. package/lib/commonjs/api/publicClient.js +73 -0
  11. package/lib/commonjs/api/retry.js +75 -0
  12. package/lib/commonjs/api/tokenRefresh.js +205 -0
  13. package/lib/commonjs/api/userClient.js +255 -0
  14. package/lib/commonjs/auth/authStore.js +232 -0
  15. package/lib/commonjs/auth/index.js +44 -0
  16. package/lib/commonjs/auth/jwt.js +112 -0
  17. package/lib/commonjs/config/index.js +66 -0
  18. package/lib/commonjs/deepLink/index.js +286 -0
  19. package/lib/commonjs/download/adapters/expoFs.js +69 -0
  20. package/lib/commonjs/download/adapters/shared.js +13 -0
  21. package/lib/commonjs/download/index.js +250 -0
  22. package/lib/commonjs/formatters/currency.js +69 -0
  23. package/lib/commonjs/formatters/date.js +168 -0
  24. package/lib/commonjs/formatters/dateMath.js +310 -0
  25. package/lib/commonjs/formatters/index.js +246 -0
  26. package/lib/commonjs/formatters/initials.js +32 -0
  27. package/lib/commonjs/formatters/number.js +88 -0
  28. package/lib/commonjs/formatters/phone.js +369 -0
  29. package/lib/commonjs/formatters/windowsToIanaTable.js +164 -0
  30. package/lib/commonjs/geolocation/adapters/expoLocation.js +126 -0
  31. package/lib/commonjs/geolocation/index.js +355 -0
  32. package/lib/commonjs/index.js +630 -0
  33. package/lib/commonjs/initSDK.js +66 -0
  34. package/lib/commonjs/initUserAuth.js +37 -0
  35. package/lib/commonjs/logger/index.js +291 -0
  36. package/lib/commonjs/network/index.js +31 -0
  37. package/lib/commonjs/network/networkStatus.js +133 -0
  38. package/lib/commonjs/package.json +1 -0
  39. package/lib/commonjs/permissions/calendar.js +14 -0
  40. package/lib/commonjs/permissions/camera.js +14 -0
  41. package/lib/commonjs/permissions/contacts.js +10 -0
  42. package/lib/commonjs/permissions/index.js +133 -0
  43. package/lib/commonjs/permissions/location.js +14 -0
  44. package/lib/commonjs/permissions/mediaLibrary.js +10 -0
  45. package/lib/commonjs/permissions/notifications.js +10 -0
  46. package/lib/commonjs/permissions/photos.js +14 -0
  47. package/lib/commonjs/phone/countries.js +265 -0
  48. package/lib/commonjs/share/index.js +70 -0
  49. package/lib/commonjs/smsRetriever/index.js +91 -0
  50. package/lib/commonjs/specs/NativeSmsRetriever.js +15 -0
  51. package/lib/commonjs/sqlite/adapters/expo.js +53 -0
  52. package/lib/commonjs/sqlite/index.js +604 -0
  53. package/lib/commonjs/storage/index.js +214 -0
  54. package/lib/commonjs/types/globals.d.js +6 -0
  55. package/lib/commonjs/utils/imageCompression.js +389 -0
  56. package/lib/commonjs/utils/index.js +19 -0
  57. package/lib/commonjs/validators/index.js +227 -0
  58. package/lib/commonjs/versionCheck/index.js +419 -0
  59. package/lib/module/api/baseUrl.js +30 -0
  60. package/lib/module/api/curl.js +104 -0
  61. package/lib/module/api/publicClient.js +69 -0
  62. package/lib/module/api/retry.js +69 -0
  63. package/lib/module/api/tokenRefresh.js +192 -0
  64. package/lib/module/api/userClient.js +233 -0
  65. package/lib/module/auth/authStore.js +224 -0
  66. package/lib/module/auth/index.js +5 -0
  67. package/lib/module/auth/jwt.js +106 -0
  68. package/lib/module/config/index.js +58 -0
  69. package/lib/module/deepLink/index.js +280 -0
  70. package/lib/module/download/adapters/expoFs.js +64 -0
  71. package/lib/module/download/adapters/shared.js +8 -0
  72. package/lib/module/download/index.js +242 -0
  73. package/lib/module/formatters/currency.js +64 -0
  74. package/lib/module/formatters/date.js +160 -0
  75. package/lib/module/formatters/dateMath.js +294 -0
  76. package/lib/module/formatters/index.js +9 -0
  77. package/lib/module/formatters/initials.js +27 -0
  78. package/lib/module/formatters/number.js +81 -0
  79. package/lib/module/formatters/phone.js +302 -0
  80. package/lib/module/formatters/windowsToIanaTable.js +160 -0
  81. package/lib/module/geolocation/adapters/expoLocation.js +121 -0
  82. package/lib/module/geolocation/index.js +347 -0
  83. package/lib/module/index.js +54 -0
  84. package/lib/module/initSDK.js +61 -0
  85. package/lib/module/initUserAuth.js +33 -0
  86. package/lib/module/logger/index.js +285 -0
  87. package/lib/module/network/index.js +4 -0
  88. package/lib/module/network/networkStatus.js +124 -0
  89. package/lib/module/package.json +1 -0
  90. package/lib/module/permissions/calendar.js +15 -0
  91. package/lib/module/permissions/camera.js +15 -0
  92. package/lib/module/permissions/contacts.js +11 -0
  93. package/lib/module/permissions/index.js +128 -0
  94. package/lib/module/permissions/location.js +15 -0
  95. package/lib/module/permissions/mediaLibrary.js +11 -0
  96. package/lib/module/permissions/notifications.js +11 -0
  97. package/lib/module/permissions/photos.js +15 -0
  98. package/lib/module/phone/countries.js +261 -0
  99. package/lib/module/share/index.js +64 -0
  100. package/lib/module/smsRetriever/index.js +86 -0
  101. package/lib/module/specs/NativeSmsRetriever.js +13 -0
  102. package/lib/module/sqlite/adapters/expo.js +48 -0
  103. package/lib/module/sqlite/index.js +594 -0
  104. package/lib/module/storage/index.js +208 -0
  105. package/lib/module/types/globals.d.js +10 -0
  106. package/lib/module/utils/imageCompression.js +383 -0
  107. package/lib/module/utils/index.js +4 -0
  108. package/lib/module/validators/index.js +205 -0
  109. package/lib/module/versionCheck/index.js +413 -0
  110. package/lib/typescript/commonjs/api/baseUrl.d.ts +20 -0
  111. package/lib/typescript/commonjs/api/curl.d.ts +18 -0
  112. package/lib/typescript/commonjs/api/publicClient.d.ts +11 -0
  113. package/lib/typescript/commonjs/api/retry.d.ts +27 -0
  114. package/lib/typescript/commonjs/api/tokenRefresh.d.ts +43 -0
  115. package/lib/typescript/commonjs/api/userClient.d.ts +37 -0
  116. package/lib/typescript/commonjs/auth/authStore.d.ts +79 -0
  117. package/lib/typescript/commonjs/auth/index.d.ts +5 -0
  118. package/lib/typescript/commonjs/auth/jwt.d.ts +31 -0
  119. package/lib/typescript/commonjs/config/index.d.ts +55 -0
  120. package/lib/typescript/commonjs/deepLink/index.d.ts +54 -0
  121. package/lib/typescript/commonjs/download/adapters/expoFs.d.ts +38 -0
  122. package/lib/typescript/commonjs/download/adapters/shared.d.ts +3 -0
  123. package/lib/typescript/commonjs/download/index.d.ts +86 -0
  124. package/lib/typescript/commonjs/formatters/currency.d.ts +24 -0
  125. package/lib/typescript/commonjs/formatters/date.d.ts +22 -0
  126. package/lib/typescript/commonjs/formatters/dateMath.d.ts +88 -0
  127. package/lib/typescript/commonjs/formatters/index.d.ts +10 -0
  128. package/lib/typescript/commonjs/formatters/initials.d.ts +11 -0
  129. package/lib/typescript/commonjs/formatters/number.d.ts +15 -0
  130. package/lib/typescript/commonjs/formatters/phone.d.ts +133 -0
  131. package/lib/typescript/commonjs/formatters/windowsToIanaTable.d.ts +18 -0
  132. package/lib/typescript/commonjs/geolocation/adapters/expoLocation.d.ts +42 -0
  133. package/lib/typescript/commonjs/geolocation/index.d.ts +90 -0
  134. package/lib/typescript/commonjs/index.d.ts +54 -0
  135. package/lib/typescript/commonjs/initSDK.d.ts +72 -0
  136. package/lib/typescript/commonjs/initUserAuth.d.ts +41 -0
  137. package/lib/typescript/commonjs/logger/index.d.ts +81 -0
  138. package/lib/typescript/commonjs/network/index.d.ts +3 -0
  139. package/lib/typescript/commonjs/network/networkStatus.d.ts +43 -0
  140. package/lib/typescript/commonjs/package.json +1 -0
  141. package/lib/typescript/commonjs/permissions/calendar.d.ts +2 -0
  142. package/lib/typescript/commonjs/permissions/camera.d.ts +2 -0
  143. package/lib/typescript/commonjs/permissions/contacts.d.ts +2 -0
  144. package/lib/typescript/commonjs/permissions/index.d.ts +48 -0
  145. package/lib/typescript/commonjs/permissions/location.d.ts +2 -0
  146. package/lib/typescript/commonjs/permissions/mediaLibrary.d.ts +2 -0
  147. package/lib/typescript/commonjs/permissions/notifications.d.ts +2 -0
  148. package/lib/typescript/commonjs/permissions/photos.d.ts +2 -0
  149. package/lib/typescript/commonjs/phone/countries.d.ts +13 -0
  150. package/lib/typescript/commonjs/share/index.d.ts +29 -0
  151. package/lib/typescript/commonjs/smsRetriever/index.d.ts +30 -0
  152. package/lib/typescript/commonjs/specs/NativeSmsRetriever.d.ts +15 -0
  153. package/lib/typescript/commonjs/sqlite/adapters/expo.d.ts +30 -0
  154. package/lib/typescript/commonjs/sqlite/index.d.ts +113 -0
  155. package/lib/typescript/commonjs/storage/index.d.ts +64 -0
  156. package/lib/typescript/commonjs/utils/imageCompression.d.ts +152 -0
  157. package/lib/typescript/commonjs/utils/index.d.ts +3 -0
  158. package/lib/typescript/commonjs/validators/index.d.ts +51 -0
  159. package/lib/typescript/commonjs/versionCheck/index.d.ts +80 -0
  160. package/lib/typescript/module/api/baseUrl.d.ts +20 -0
  161. package/lib/typescript/module/api/curl.d.ts +18 -0
  162. package/lib/typescript/module/api/publicClient.d.ts +11 -0
  163. package/lib/typescript/module/api/retry.d.ts +27 -0
  164. package/lib/typescript/module/api/tokenRefresh.d.ts +43 -0
  165. package/lib/typescript/module/api/userClient.d.ts +37 -0
  166. package/lib/typescript/module/auth/authStore.d.ts +79 -0
  167. package/lib/typescript/module/auth/index.d.ts +5 -0
  168. package/lib/typescript/module/auth/jwt.d.ts +31 -0
  169. package/lib/typescript/module/config/index.d.ts +55 -0
  170. package/lib/typescript/module/deepLink/index.d.ts +54 -0
  171. package/lib/typescript/module/download/adapters/expoFs.d.ts +38 -0
  172. package/lib/typescript/module/download/adapters/shared.d.ts +3 -0
  173. package/lib/typescript/module/download/index.d.ts +86 -0
  174. package/lib/typescript/module/formatters/currency.d.ts +24 -0
  175. package/lib/typescript/module/formatters/date.d.ts +22 -0
  176. package/lib/typescript/module/formatters/dateMath.d.ts +88 -0
  177. package/lib/typescript/module/formatters/index.d.ts +10 -0
  178. package/lib/typescript/module/formatters/initials.d.ts +11 -0
  179. package/lib/typescript/module/formatters/number.d.ts +15 -0
  180. package/lib/typescript/module/formatters/phone.d.ts +133 -0
  181. package/lib/typescript/module/formatters/windowsToIanaTable.d.ts +18 -0
  182. package/lib/typescript/module/geolocation/adapters/expoLocation.d.ts +42 -0
  183. package/lib/typescript/module/geolocation/index.d.ts +90 -0
  184. package/lib/typescript/module/index.d.ts +54 -0
  185. package/lib/typescript/module/initSDK.d.ts +72 -0
  186. package/lib/typescript/module/initUserAuth.d.ts +41 -0
  187. package/lib/typescript/module/logger/index.d.ts +81 -0
  188. package/lib/typescript/module/network/index.d.ts +3 -0
  189. package/lib/typescript/module/network/networkStatus.d.ts +43 -0
  190. package/lib/typescript/module/package.json +1 -0
  191. package/lib/typescript/module/permissions/calendar.d.ts +2 -0
  192. package/lib/typescript/module/permissions/camera.d.ts +2 -0
  193. package/lib/typescript/module/permissions/contacts.d.ts +2 -0
  194. package/lib/typescript/module/permissions/index.d.ts +48 -0
  195. package/lib/typescript/module/permissions/location.d.ts +2 -0
  196. package/lib/typescript/module/permissions/mediaLibrary.d.ts +2 -0
  197. package/lib/typescript/module/permissions/notifications.d.ts +2 -0
  198. package/lib/typescript/module/permissions/photos.d.ts +2 -0
  199. package/lib/typescript/module/phone/countries.d.ts +13 -0
  200. package/lib/typescript/module/share/index.d.ts +29 -0
  201. package/lib/typescript/module/smsRetriever/index.d.ts +30 -0
  202. package/lib/typescript/module/specs/NativeSmsRetriever.d.ts +15 -0
  203. package/lib/typescript/module/sqlite/adapters/expo.d.ts +30 -0
  204. package/lib/typescript/module/sqlite/index.d.ts +113 -0
  205. package/lib/typescript/module/storage/index.d.ts +64 -0
  206. package/lib/typescript/module/utils/imageCompression.d.ts +152 -0
  207. package/lib/typescript/module/utils/index.d.ts +3 -0
  208. package/lib/typescript/module/validators/index.d.ts +51 -0
  209. package/lib/typescript/module/versionCheck/index.d.ts +80 -0
  210. package/package.json +298 -0
  211. package/src/specs/NativeSmsRetriever.ts +16 -0
@@ -0,0 +1,224 @@
1
+ "use strict";
2
+
3
+ import * as SecureStore from 'expo-secure-store';
4
+ import { Logger } from "../logger/index.js";
5
+
6
+ /**
7
+ * Token data persisted in the keychain.
8
+ */
9
+ /**
10
+ * The two keys the library itself reads. Everything in the library that needs
11
+ * the bearer token, the HTTP client and the authenticated image cache,
12
+ * resolves it through these rather than repeating the literal, so
13
+ * one of them cannot silently drift and start reading a key nothing writes.
14
+ * Apps are free to store their own tokens under any other key.
15
+ */
16
+ export const ACCESS_TOKEN_KEY = 'accessToken';
17
+ export const REFRESH_TOKEN_KEY = 'refreshToken';
18
+
19
+ /**
20
+ * The shape this module speaks to its store through. expo-secure-store is
21
+ * adapted to it below; an app or a test can supply anything else of this shape
22
+ * through `setKeychainImplementation`.
23
+ */
24
+ /**
25
+ * The GENERIC password API, not the internet-credentials one.
26
+ *
27
+ * `kSecClassInternetPassword` exists for credentials bound to a server, protocol
28
+ * and port, and it integrates with Safari and AutoFill. A bearer token keyed by
29
+ * a name like "accessToken" is none of those things, and the library was only
30
+ * satisfying that API's required `server` argument by passing the key twice,
31
+ * once as the server and once as the service. That is misuse of the class.
32
+ *
33
+ * `kSecClassGenericPassword` is the class for app-local secrets, which is what a
34
+ * token is. It is what expo-secure-store uses underneath, and what the consumer
35
+ * app being migrated had already chosen for itself.
36
+ */
37
+ /**
38
+ * The credential shape the seam reads back, declared here rather than imported
39
+ * from whichever package a consumer's backend comes from. A type-only import of
40
+ * a package this library does not declare survives into the published `.d.ts`,
41
+ * where a consumer that has not installed it either fails to typecheck or,
42
+ * under the RN template's `skipLibCheck: true`, loses all checking on these
43
+ * members with no signal. Any credential object with these three members
44
+ * satisfies it structurally.
45
+ */
46
+
47
+ // Only what a test or the showcase injected; null means use expo-secure-store.
48
+ let backend = null;
49
+
50
+ /**
51
+ * expo-secure-store, adapted to the generic-password shape this module speaks.
52
+ *
53
+ * It is a flat key/value store, so the credential shape is reconstructed around
54
+ * it. The key is sanitised because expo rejects anything outside
55
+ * [A-Za-z0-9._-] at runtime, which would otherwise surface as a throw from deep
56
+ * inside the store rather than as a bad key.
57
+ */
58
+ const keyFor = service => (service ?? 'default').replace(/[^A-Za-z0-9._-]/g, '_');
59
+ const expoSecureStoreBackend = {
60
+ setGenericPassword: async (username, password, options) => {
61
+ await SecureStore.setItemAsync(keyFor(options?.service), JSON.stringify({
62
+ username,
63
+ password
64
+ }));
65
+ return true;
66
+ },
67
+ getGenericPassword: async options => {
68
+ const service = options?.service;
69
+ const raw = await SecureStore.getItemAsync(keyFor(service));
70
+ if (raw === null) {
71
+ return false;
72
+ }
73
+ const parsed = JSON.parse(raw);
74
+ return {
75
+ username: parsed.username,
76
+ password: parsed.password,
77
+ service: service ?? 'default'
78
+ };
79
+ },
80
+ resetGenericPassword: async options => {
81
+ await SecureStore.deleteItemAsync(keyFor(options?.service));
82
+ return true;
83
+ }
84
+ };
85
+ const resolveBackend = () => backend ?? expoSecureStoreBackend;
86
+ const requireBackend = op => {
87
+ const b = resolveBackend();
88
+ if (!b) {
89
+ const err = new Error(`[authStore] no secure-store backend available; cannot ${op}.`);
90
+ Logger.error(err.message);
91
+ throw err;
92
+ }
93
+ return b;
94
+ };
95
+
96
+ /**
97
+ * Replace the secure-store backend. Intended for TESTS and for the showcase,
98
+ * which injects a failing store to demo the write-failure path; it is not how
99
+ * an app configures the library. Pass `null` to restore expo-secure-store.
100
+ */
101
+ export const setKeychainImplementation = impl => {
102
+ backend = impl;
103
+ };
104
+
105
+ /**
106
+ * Store token with expiry.
107
+ */
108
+ export const storeToken = async (key, token, expiry) => {
109
+ // Never log the token or expiry values: Logger forwards to whatever remote
110
+ // sink the app wired (Crashlytics/Sentry), so a token here leaves the device.
111
+ Logger.info(`Storing token for key: ${key}`);
112
+ try {
113
+ const data = JSON.stringify({
114
+ token,
115
+ expiry
116
+ });
117
+ await requireBackend('store a token').setGenericPassword('token', data, {
118
+ service: key
119
+ });
120
+ Logger.info(`Token stored successfully for key: ${key}`);
121
+ } catch (error) {
122
+ // Log AND rethrow. Swallowing this told the caller the token was persisted
123
+ // when it was not, so an app would show a signed-in user holding a token
124
+ // that never reached the keychain — the session then dies at the next
125
+ // cold start with no failure anyone could have seen.
126
+ Logger.error(`Error storing token for key: ${key}`, error);
127
+ throw error;
128
+ }
129
+ };
130
+
131
+ /**
132
+ * Parse token data from credentials.
133
+ */
134
+ const parseTokenData = credentials => {
135
+ if (!credentials) {
136
+ return null;
137
+ }
138
+ const {
139
+ password
140
+ } = credentials;
141
+ try {
142
+ const parsed = JSON.parse(password);
143
+ if (parsed && typeof parsed === 'object' && 'token' in parsed && 'expiry' in parsed && typeof parsed.token === 'string' && typeof parsed.expiry === 'string') {
144
+ return parsed;
145
+ }
146
+ return null;
147
+ } catch {
148
+ // The single report for "stored, but not a token" belongs to getToken, which
149
+ // knows whether anything was stored at all. Logging here too made one event
150
+ // arrive twice, at two different levels.
151
+ return null;
152
+ }
153
+ };
154
+
155
+ /**
156
+ * Retrieve token and check if expired.
157
+ */
158
+ export const getToken = async key => {
159
+ try {
160
+ const credentials = await requireBackend('read a token').getGenericPassword({
161
+ service: key
162
+ });
163
+ // Nothing stored is an ORDINARY state, not a fault: a signed-out user has no
164
+ // token, and every authenticated request, image and download asks for one.
165
+ // Warning here put three info lines and a warning into the log, and into the
166
+ // remote crash sink, on every one of those calls.
167
+ if (!credentials) {
168
+ return null;
169
+ }
170
+ const parsedData = parseTokenData(credentials);
171
+ if (!parsedData) {
172
+ // Something IS stored and it is not a token. That is corruption worth
173
+ // hearing about, and it is a different event from having no token.
174
+ Logger.warn(`Stored credentials for key "${key}" are not a valid token; ignoring them.`);
175
+ return null;
176
+ }
177
+ const {
178
+ token,
179
+ expiry
180
+ } = parsedData;
181
+ return {
182
+ token,
183
+ expiry
184
+ };
185
+ } catch (error) {
186
+ const message = error instanceof Error ? error.message : String(error);
187
+ // Both spellings matter. Android throws "Key permanently invalidated" when
188
+ // the biometric enrolment changes; iOS throws "Secret invalidated" for the
189
+ // equivalent. Either way the stored value can never be decrypted again, so
190
+ // clearing it is the only way out: leaving it makes every later read fail
191
+ // identically, and the user is stuck with no route back to a login. A
192
+ // consumer app handled the iOS spelling and the library did not.
193
+ if (message.includes('Key permanently invalidated') || message.includes('Secret invalidated')) {
194
+ Logger.error(`Stored token for key "${key}" can no longer be decrypted; clearing it.`);
195
+ await removeToken(key);
196
+ return null;
197
+ }
198
+ Logger.error(`Error retrieving token for key: ${key}`, error);
199
+ return null;
200
+ }
201
+ };
202
+
203
+ /**
204
+ * Remove token by key.
205
+ */
206
+ export const removeToken = async key => {
207
+ try {
208
+ await requireBackend('remove a token').resetGenericPassword({
209
+ service: key
210
+ });
211
+ Logger.info(`Token removed successfully for key: ${key}`);
212
+ } catch (error) {
213
+ // Clearing a key that was never written is the ordinary logout path, not a
214
+ // failure: only one of the two tokens may ever have been stored. Logging it
215
+ // as an error puts routine traffic in the channel people watch for real
216
+ // problems, which is how an error channel stops being watched.
217
+ if (error instanceof Error && error.message.includes('Item not found')) {
218
+ Logger.info(`No token found to remove for key: ${key}`);
219
+ return;
220
+ }
221
+ Logger.error(`Failed to remove token for key: ${key}`, error);
222
+ }
223
+ };
224
+ //# sourceMappingURL=authStore.js.map
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+
3
+ export { getToken, removeToken, storeToken } from "./authStore.js";
4
+ export { decodeJWT, getJWTExpiry, isJWTExpired } from "./jwt.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+
3
+ import { Logger } from "../logger/index.js";
4
+
5
+ /**
6
+ * Standard claims plus arbitrary keys.
7
+ * `sub`, `exp`, `iat`, `nbf` follow RFC 7519 conventions.
8
+ */
9
+
10
+ const base64UrlToBase64 = input => {
11
+ const padded = input + '='.repeat((4 - input.length % 4) % 4);
12
+ return padded.replace(/-/g, '+').replace(/_/g, '/');
13
+ };
14
+ const BASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
15
+
16
+ /**
17
+ * Pure-JS base64, used only when the runtime has no `atob`.
18
+ *
19
+ * This used to be `require('buffer')`. That was a latent bundling failure for
20
+ * every consumer: `buffer` is a Node builtin that React Native does not
21
+ * provide, it was never declared as a dependency, and Metro resolves requires
22
+ * STATICALLY — so sitting inside an unreached fallback branch did not save it.
23
+ * The app still failed to bundle, with an error naming this file. It only went
24
+ * unnoticed because the repo's own checks never bundle for a device.
25
+ *
26
+ * Decoding four base64 characters yields three bytes; the padding tells us how
27
+ * many of those three are real.
28
+ */
29
+ const decodeBase64Fallback = input => {
30
+ let out = '';
31
+ for (let i = 0; i < input.length; i += 4) {
32
+ const chunk = [0, 1, 2, 3].map(offset => BASE64_ALPHABET.indexOf(input[i + offset] ?? '='));
33
+ const bits = Math.max(chunk[0], 0) << 18 | Math.max(chunk[1], 0) << 12 | Math.max(chunk[2], 0) << 6 | Math.max(chunk[3], 0);
34
+ out += String.fromCharCode(bits >> 16 & 0xff);
35
+ if (chunk[2] !== -1) {
36
+ out += String.fromCharCode(bits >> 8 & 0xff);
37
+ }
38
+ if (chunk[3] !== -1) {
39
+ out += String.fromCharCode(bits & 0xff);
40
+ }
41
+ }
42
+ return out;
43
+ };
44
+ const decodeBase64 = input =>
45
+ // Every RN runtime the library supports has `atob` (Hermes 0.71+), so the
46
+ // fallback is a safety net rather than a path anyone is expected to take.
47
+ typeof atob === 'function' ? atob(input) : decodeBase64Fallback(input);
48
+ const utf8FromBinary = binary => {
49
+ // Convert percent-encoded UTF-8 sequence so multi-byte chars survive.
50
+ try {
51
+ return decodeURIComponent(binary.split('').map(c => '%' + c.charCodeAt(0).toString(16).padStart(2, '0')).join(''));
52
+ } catch {
53
+ return binary;
54
+ }
55
+ };
56
+
57
+ /**
58
+ * Decode a JWT's payload claims. Does **not** verify the signature.
59
+ *
60
+ * @returns claims object on success, `null` if the token is malformed.
61
+ */
62
+ export const decodeJWT = token => {
63
+ if (!token || typeof token !== 'string') {
64
+ return null;
65
+ }
66
+ const parts = token.split('.');
67
+ if (parts.length !== 3) {
68
+ Logger.warn('[jwt] decodeJWT: token is not a valid JWT (expected 3 segments)');
69
+ return null;
70
+ }
71
+ try {
72
+ const payload = parts[1];
73
+ const json = utf8FromBinary(decodeBase64(base64UrlToBase64(payload)));
74
+ return JSON.parse(json);
75
+ } catch (error) {
76
+ Logger.warn('[jwt] decodeJWT failed to parse payload', error);
77
+ return null;
78
+ }
79
+ };
80
+
81
+ /**
82
+ * Returns true when the token is expired (or unparseable / missing `exp`).
83
+ *
84
+ * @param token JWT string
85
+ * @param leewaySeconds amount of clock-skew tolerance to apply (default 30s)
86
+ */
87
+ export const isJWTExpired = (token, leewaySeconds = 30) => {
88
+ const claims = decodeJWT(token);
89
+ if (!claims || typeof claims.exp !== 'number') {
90
+ return true;
91
+ }
92
+ const nowSeconds = Math.floor(Date.now() / 1000);
93
+ return claims.exp <= nowSeconds + leewaySeconds;
94
+ };
95
+
96
+ /**
97
+ * Returns the token's expiry as a Date, or `null` when no `exp` claim exists.
98
+ */
99
+ export const getJWTExpiry = token => {
100
+ const claims = decodeJWT(token);
101
+ if (!claims || typeof claims.exp !== 'number') {
102
+ return null;
103
+ }
104
+ return new Date(claims.exp * 1000);
105
+ };
106
+ //# sourceMappingURL=jwt.js.map
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Configuration management for Webority packages
5
+ * Provides a flexible way to inject configuration without hard dependencies
6
+ */
7
+
8
+ const DEFAULT_CONFIG = {
9
+ baseUrl: null,
10
+ loggingEnabled: true,
11
+ logLevel: 'info',
12
+ environment: 'Development',
13
+ country: 'in',
14
+ appId: null,
15
+ appVersion: null,
16
+ logCurl: false
17
+ };
18
+ let globalConfig = {
19
+ ...DEFAULT_CONFIG
20
+ };
21
+
22
+ /**
23
+ * Set global configuration for all Webority packages
24
+ */
25
+ export const setConfig = config => {
26
+ globalConfig = {
27
+ ...globalConfig,
28
+ ...config
29
+ };
30
+ };
31
+
32
+ /**
33
+ * Get current configuration
34
+ */
35
+ export const getConfig = () => globalConfig;
36
+
37
+ /**
38
+ * Get a specific config value with optional fallback.
39
+ * If the stored value is `null`, the fallback is returned (when provided).
40
+ */
41
+
42
+ export function getConfigValue(key, fallback) {
43
+ const value = globalConfig[key];
44
+ if (value !== null) {
45
+ return value;
46
+ }
47
+ return fallback;
48
+ }
49
+
50
+ /**
51
+ * Reset configuration to defaults
52
+ */
53
+ export const resetConfig = () => {
54
+ globalConfig = {
55
+ ...DEFAULT_CONFIG
56
+ };
57
+ };
58
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,280 @@
1
+ "use strict";
2
+
3
+ import { useEffect, useRef } from 'react';
4
+ import { Linking } from 'react-native';
5
+ import { Logger } from "../logger/index.js";
6
+ const PREFIX = '[@webority/mobile-core]';
7
+ const decodeSafe = value => {
8
+ try {
9
+ return decodeURIComponent(value.replace(/\+/g, ' '));
10
+ } catch {
11
+ return value;
12
+ }
13
+ };
14
+ const parseQuery = raw => {
15
+ const out = {};
16
+ if (!raw) {
17
+ return out;
18
+ }
19
+ for (const pair of raw.split('&')) {
20
+ if (!pair) {
21
+ continue;
22
+ }
23
+ const eq = pair.indexOf('=');
24
+ const key = decodeSafe(eq >= 0 ? pair.slice(0, eq) : pair);
25
+ if (!key) {
26
+ continue;
27
+ }
28
+ out[key] = eq >= 0 ? decodeSafe(pair.slice(eq + 1)) : '';
29
+ }
30
+ return out;
31
+ };
32
+ const trimSlashes = value => value.replace(/^\/+/, '').replace(/\/+$/, '');
33
+
34
+ /**
35
+ * Parse a deep-link URL. Returns null for anything that is not a usable URL so
36
+ * a hostile or truncated link can never throw on the native event path.
37
+ */
38
+ export const parseDeepLink = url => {
39
+ if (typeof url !== 'string') {
40
+ return null;
41
+ }
42
+ const trimmed = url.trim();
43
+ if (!trimmed || /\s/.test(trimmed)) {
44
+ return null;
45
+ }
46
+ const withoutFragment = trimmed.split('#')[0] ?? '';
47
+ const schemeEnd = withoutFragment.indexOf('://');
48
+ let scheme = '';
49
+ let rest = withoutFragment;
50
+ if (schemeEnd >= 0) {
51
+ const rawScheme = withoutFragment.slice(0, schemeEnd);
52
+ if (!/^[a-zA-Z][a-zA-Z0-9+.-]*$/.test(rawScheme)) {
53
+ return null;
54
+ }
55
+ scheme = rawScheme.toLowerCase();
56
+ rest = withoutFragment.slice(schemeEnd + 3);
57
+ }
58
+ const queryStart = rest.indexOf('?');
59
+ const rawPath = queryStart >= 0 ? rest.slice(0, queryStart) : rest;
60
+ const query = parseQuery(queryStart >= 0 ? rest.slice(queryStart + 1) : '');
61
+ let host = '';
62
+ let pathPart = rawPath;
63
+ if (scheme === 'http' || scheme === 'https') {
64
+ const slash = rawPath.indexOf('/');
65
+ host = slash >= 0 ? rawPath.slice(0, slash) : rawPath;
66
+ pathPart = slash >= 0 ? rawPath.slice(slash + 1) : '';
67
+ }
68
+ const path = trimSlashes(pathPart);
69
+ const segments = path ? path.split('/').filter(Boolean) : [];
70
+ if (scheme && scheme !== 'http' && scheme !== 'https') {
71
+ host = segments[0] ?? '';
72
+ }
73
+ return {
74
+ url: trimmed,
75
+ scheme,
76
+ host,
77
+ path,
78
+ segments,
79
+ query
80
+ };
81
+ };
82
+ const matchPattern = (pattern, segments) => {
83
+ const patternSegments = trimSlashes(pattern).split('/').filter(Boolean);
84
+ if (patternSegments.length !== segments.length) {
85
+ return null;
86
+ }
87
+ const params = {};
88
+ for (let i = 0; i < patternSegments.length; i += 1) {
89
+ const p = patternSegments[i];
90
+ const s = segments[i];
91
+ if (p.startsWith(':')) {
92
+ const name = p.slice(1);
93
+ if (!name) {
94
+ return null;
95
+ }
96
+ params[name] = s;
97
+ } else if (p.toLowerCase() !== s.toLowerCase()) {
98
+ return null;
99
+ }
100
+ }
101
+ return params;
102
+ };
103
+ const matchRegExp = (pattern, path) => {
104
+ // A /g or /y pattern carries lastIndex between calls, so the same link would
105
+ // match on one dispatch and miss on the next.
106
+ if (pattern.global || pattern.sticky) {
107
+ pattern.lastIndex = 0;
108
+ }
109
+ const result = pattern.exec(path);
110
+ if (!result) {
111
+ return null;
112
+ }
113
+ const params = {};
114
+ for (let i = 1; i < result.length; i += 1) {
115
+ const group = result[i];
116
+ if (group !== undefined) {
117
+ params[String(i)] = group;
118
+ }
119
+ }
120
+ for (const [name, value] of Object.entries(result.groups ?? {})) {
121
+ if (value !== undefined) {
122
+ params[name] = value;
123
+ }
124
+ }
125
+ return params;
126
+ };
127
+ const matchRoute = (routes, link) => {
128
+ for (const route of routes) {
129
+ const params = typeof route.match === 'string' ? matchPattern(route.match, link.segments) : matchRegExp(route.match, link.path);
130
+ if (params) {
131
+ return {
132
+ route,
133
+ params
134
+ };
135
+ }
136
+ }
137
+ return null;
138
+ };
139
+ let config = null;
140
+ let subscription = null;
141
+ let started = false;
142
+ let pending = null;
143
+ let readyOverride = null;
144
+ const isReady = () => {
145
+ if (readyOverride !== null) {
146
+ return readyOverride;
147
+ }
148
+ return config?.isReady?.() ?? true;
149
+ };
150
+ const isAuthenticated = () => config?.isAuthenticated?.() ?? true;
151
+ const dispatch = link => {
152
+ const active = config;
153
+ if (!active) {
154
+ return;
155
+ }
156
+ const matched = matchRoute(active.routes, link);
157
+ if (!matched) {
158
+ active.onUnmatched?.(link);
159
+ return;
160
+ }
161
+ if (matched.route.requiresAuth && !isAuthenticated()) {
162
+ pending = link;
163
+ active.onUnauthenticated?.(link);
164
+ return;
165
+ }
166
+ matched.route.handler(link, matched.params);
167
+ };
168
+ const flushPending = () => {
169
+ const link = pending;
170
+ if (!link) {
171
+ return;
172
+ }
173
+ // Cleared BEFORE dispatch: a handler that navigates can re-enter this path,
174
+ // and a link that fires twice is the classic deep-link duplicate-screen bug.
175
+ pending = null;
176
+ dispatch(link);
177
+ };
178
+ const configure = next => {
179
+ if (!next || !Array.isArray(next.routes)) {
180
+ throw new Error(`${PREFIX} DeepLink.configure() needs a config object with a "routes" array. ` + 'Pass { routes: [{ match: "order/:id", handler }] }.');
181
+ }
182
+ config = next;
183
+ };
184
+ const handleUrl = url => {
185
+ if (!config) {
186
+ Logger.error(`${PREFIX} DeepLink received a URL before configure() was called; the link was dropped. ` + 'Call DeepLink.configure({ routes }) (or useDeepLink) during app start.');
187
+ return;
188
+ }
189
+ const parsed = parseDeepLink(url);
190
+ // A malformed URL still reaches the app through onUnmatched, with the raw
191
+ // value in `url`, rather than vanishing.
192
+ if (!parsed) {
193
+ config.onUnmatched?.({
194
+ url: typeof url === 'string' ? url : '',
195
+ scheme: '',
196
+ host: '',
197
+ path: '',
198
+ segments: [],
199
+ query: {}
200
+ });
201
+ return;
202
+ }
203
+ const link = parsed;
204
+ if (!isReady()) {
205
+ pending = link;
206
+ return;
207
+ }
208
+ dispatch(link);
209
+ };
210
+ const start = async () => {
211
+ if (!config) {
212
+ throw new Error(`${PREFIX} DeepLink.start() was called before configure(). ` + 'Call DeepLink.configure({ routes }) first, or use the useDeepLink() hook.');
213
+ }
214
+ if (started) {
215
+ return;
216
+ }
217
+ started = true;
218
+ // Subscribe before reading the cold-start URL so a link arriving during the
219
+ // await is not lost.
220
+ subscription = Linking.addEventListener('url', event => {
221
+ handleUrl(event.url);
222
+ });
223
+ try {
224
+ const initial = await Linking.getInitialURL();
225
+ if (initial) {
226
+ handleUrl(initial);
227
+ }
228
+ } catch (error) {
229
+ Logger.error(`${PREFIX} DeepLink could not read the cold-start URL`, error);
230
+ }
231
+ };
232
+ const stop = () => {
233
+ subscription?.remove?.();
234
+ subscription = null;
235
+ started = false;
236
+ pending = null;
237
+ readyOverride = null;
238
+ };
239
+ const setReady = ready => {
240
+ readyOverride = ready;
241
+ if (ready) {
242
+ flushPending();
243
+ }
244
+ };
245
+ export const DeepLink = {
246
+ configure,
247
+ start,
248
+ stop,
249
+ handleUrl,
250
+ setReady,
251
+ /** Replay the stashed link, once. Call after a successful login. */
252
+ replayPending: flushPending,
253
+ getPending: () => pending
254
+ };
255
+
256
+ /**
257
+ * Configure and start the deep-link service for the lifetime of a component.
258
+ * The stored config delegates to the latest render, so new route closures do
259
+ * not tear down and re-subscribe the native listener.
260
+ */
261
+ export const useDeepLink = deepLinkConfig => {
262
+ const configRef = useRef(deepLinkConfig);
263
+ configRef.current = deepLinkConfig;
264
+ useEffect(() => {
265
+ DeepLink.configure({
266
+ get routes() {
267
+ return configRef.current.routes;
268
+ },
269
+ isReady: () => configRef.current.isReady?.() ?? true,
270
+ isAuthenticated: () => configRef.current.isAuthenticated?.() ?? true,
271
+ onUnauthenticated: link => configRef.current.onUnauthenticated?.(link),
272
+ onUnmatched: link => configRef.current.onUnmatched?.(link)
273
+ });
274
+ void DeepLink.start();
275
+ return () => {
276
+ DeepLink.stop();
277
+ };
278
+ }, []);
279
+ };
280
+ //# sourceMappingURL=index.js.map