@webority/mobile-core 0.0.25 → 0.0.27
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/LICENSE.txt +9 -0
- package/README.md +13 -0
- package/lib/commonjs/api/publicClient.js +4 -0
- package/lib/commonjs/api/retry.js +17 -1
- package/lib/commonjs/api/userClient.js +11 -3
- package/lib/commonjs/auth/authStore.js +49 -16
- package/lib/commonjs/config/index.js +8 -2
- package/lib/commonjs/formatters/currency.js +2 -1
- package/lib/commonjs/formatters/date.js +5 -6
- package/lib/commonjs/formatters/dateMath.js +2 -1
- package/lib/commonjs/formatters/intlCache.js +37 -0
- package/lib/commonjs/formatters/number.js +4 -3
- package/lib/commonjs/index.js +14 -0
- package/lib/commonjs/initSDK.js +1 -1
- package/lib/commonjs/logger/index.js +16 -6
- package/lib/commonjs/logger/redact.js +50 -0
- package/lib/commonjs/network/networkStatus.js +40 -2
- package/lib/commonjs/types/axios.js +4 -0
- package/lib/commonjs/utils/base64.js +24 -0
- package/lib/module/api/publicClient.js +4 -0
- package/lib/module/api/retry.js +17 -1
- package/lib/module/api/userClient.js +12 -4
- package/lib/module/auth/authStore.js +49 -16
- package/lib/module/config/index.js +7 -1
- package/lib/module/formatters/currency.js +2 -1
- package/lib/module/formatters/date.js +5 -6
- package/lib/module/formatters/dateMath.js +2 -1
- package/lib/module/formatters/intlCache.js +30 -0
- package/lib/module/formatters/number.js +4 -3
- package/lib/module/index.js +3 -0
- package/lib/module/initSDK.js +2 -2
- package/lib/module/logger/index.js +16 -6
- package/lib/module/logger/redact.js +44 -0
- package/lib/module/network/networkStatus.js +37 -0
- package/lib/module/types/axios.js +4 -0
- package/lib/module/utils/base64.js +19 -0
- package/lib/typescript/commonjs/api/publicClient.d.ts +1 -0
- package/lib/typescript/commonjs/api/userClient.d.ts +1 -0
- package/lib/typescript/commonjs/config/index.d.ts +6 -0
- package/lib/typescript/commonjs/formatters/intlCache.d.ts +6 -0
- package/lib/typescript/commonjs/index.d.ts +2 -0
- package/lib/typescript/commonjs/initSDK.d.ts +1 -1
- package/lib/typescript/commonjs/logger/redact.d.ts +4 -0
- package/lib/typescript/commonjs/network/networkStatus.d.ts +6 -0
- package/lib/typescript/commonjs/types/axios.d.ts +8 -0
- package/lib/typescript/commonjs/utils/base64.d.ts +7 -0
- package/lib/typescript/module/api/publicClient.d.ts +1 -0
- package/lib/typescript/module/api/userClient.d.ts +1 -0
- package/lib/typescript/module/config/index.d.ts +6 -0
- package/lib/typescript/module/formatters/intlCache.d.ts +6 -0
- package/lib/typescript/module/index.d.ts +2 -0
- package/lib/typescript/module/initSDK.d.ts +1 -1
- package/lib/typescript/module/logger/redact.d.ts +4 -0
- package/lib/typescript/module/network/networkStatus.d.ts +6 -0
- package/lib/typescript/module/types/axios.d.ts +8 -0
- package/lib/typescript/module/utils/base64.d.ts +7 -0
- package/package.json +2 -2
- package/LICENSE +0 -21
package/LICENSE.txt
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Copyright (c) Webority Technologies. All rights reserved.
|
|
2
|
+
|
|
3
|
+
This software and its NuGet packages are the proprietary and confidential property
|
|
4
|
+
of Webority Technologies. They are published publicly only for the convenience of
|
|
5
|
+
Webority's own projects and internal consumption.
|
|
6
|
+
|
|
7
|
+
No license or right to use, copy, modify, merge, publish, distribute, sublicense,
|
|
8
|
+
or create derivative works is granted to any third party. Unauthorized use,
|
|
9
|
+
reproduction, or distribution is prohibited.
|
package/README.md
CHANGED
|
@@ -47,3 +47,16 @@ keeps push on calls `enablePush` at each start.
|
|
|
47
47
|
|
|
48
48
|
Storage and network status stay on the main barrel but load no package on their own. Wire them at
|
|
49
49
|
startup with `setStorageImplementation` and `setNetworkStatusImplementation` if you want them.
|
|
50
|
+
|
|
51
|
+
## Quiet requests and log redaction
|
|
52
|
+
|
|
53
|
+
Both HTTP clients log a failed response through `Logger.error`. For a call whose failure the app
|
|
54
|
+
handles itself, pass `skipErrorLog: true` in the axios config and the clients stay silent:
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
await userClient.get('/status', { skipErrorLog: true });
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`Logger` masks secrets in every message and argument before any sink sees them: `Bearer` tokens,
|
|
61
|
+
JWT-shaped strings, and the values of keys named `token`, `accessToken`, `refreshToken`,
|
|
62
|
+
`password` and `authorization` (any case) in objects and JSON strings.
|
|
@@ -7,6 +7,7 @@ exports.default = void 0;
|
|
|
7
7
|
var _axios = _interopRequireDefault(require("axios"));
|
|
8
8
|
var _index = require("../config/index.js");
|
|
9
9
|
var _index2 = require("../logger/index.js");
|
|
10
|
+
require("../types/axios.js");
|
|
10
11
|
var _baseUrl = require("./baseUrl.js");
|
|
11
12
|
var _curl = require("./curl.js");
|
|
12
13
|
var _multipart = require("./multipart.js");
|
|
@@ -58,6 +59,9 @@ publicClient.interceptors.response.use(response => {
|
|
|
58
59
|
}, error => {
|
|
59
60
|
if (_axios.default.isAxiosError(error)) {
|
|
60
61
|
const originalRequest = error.config;
|
|
62
|
+
if (originalRequest?.skipErrorLog) {
|
|
63
|
+
return Promise.reject(error);
|
|
64
|
+
}
|
|
61
65
|
if (!error.response) {
|
|
62
66
|
_index2.Logger.error(`Network error or no response from server: ${error.message}, URL: ${originalRequest?.url}`);
|
|
63
67
|
} else {
|
|
@@ -16,10 +16,26 @@ exports.isRetryableStatus = exports.attachRetry = void 0;
|
|
|
16
16
|
*/
|
|
17
17
|
const isRetryableStatus = status => typeof status === 'number' && (status === 429 || status >= 500 && status <= 599);
|
|
18
18
|
exports.isRetryableStatus = isRetryableStatus;
|
|
19
|
+
const IDEMPOTENT_METHODS = new Set(['get', 'head', 'options', 'put', 'delete']);
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The same reasoning reaches status codes on a write. A 500 on a POST or PATCH
|
|
23
|
+
* can follow a partial commit, so replaying it can duplicate the record; only
|
|
24
|
+
* 429 and 503 say the server refused the request before doing the work. A
|
|
25
|
+
* request that knows its write is safe to repeat passes its own `shouldRetry`.
|
|
26
|
+
*/
|
|
27
|
+
const isRetryableFailure = error => {
|
|
28
|
+
const status = error.response?.status;
|
|
29
|
+
if (!isRetryableStatus(status)) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
const method = (error.config?.method ?? 'get').toLowerCase();
|
|
33
|
+
return IDEMPOTENT_METHODS.has(method) || status === 429 || status === 503;
|
|
34
|
+
};
|
|
19
35
|
const DEFAULT_POLICY = {
|
|
20
36
|
retries: 3,
|
|
21
37
|
retryDelayMs: attempt => attempt <= 1 ? 0 : 2 ** attempt * 1000,
|
|
22
|
-
shouldRetry:
|
|
38
|
+
shouldRetry: isRetryableFailure,
|
|
23
39
|
onRetry: () => undefined
|
|
24
40
|
};
|
|
25
41
|
const waitOrAbort = (delay, signal) => new Promise(resolve => {
|
|
@@ -27,6 +27,7 @@ var _authStore = require("../auth/authStore.js");
|
|
|
27
27
|
var _index = require("../config/index.js");
|
|
28
28
|
var _index2 = require("../logger/index.js");
|
|
29
29
|
var _networkStatus = require("../network/networkStatus.js");
|
|
30
|
+
require("../types/axios.js");
|
|
30
31
|
var _baseUrl = require("./baseUrl.js");
|
|
31
32
|
var _curl = require("./curl.js");
|
|
32
33
|
var _multipart = require("./multipart.js");
|
|
@@ -46,6 +47,9 @@ class RequestOfflineError extends Error {
|
|
|
46
47
|
constructor(message = 'Request blocked: device is offline') {
|
|
47
48
|
super(message);
|
|
48
49
|
this.name = 'RequestOfflineError';
|
|
50
|
+
// Babel's class transform can drop the prototype link, which makes
|
|
51
|
+
// `instanceof RequestOfflineError` false in the transpiled build.
|
|
52
|
+
Object.setPrototypeOf(this, RequestOfflineError.prototype);
|
|
49
53
|
}
|
|
50
54
|
}
|
|
51
55
|
|
|
@@ -153,7 +157,7 @@ userClient.interceptors.request.use(async config => {
|
|
|
153
157
|
// Reject before any network call when the device is known to be offline.
|
|
154
158
|
// Unknown connectivity (netinfo not installed, or undetermined) is treated
|
|
155
159
|
// as online — never block a request on an unavailable probe.
|
|
156
|
-
const network = await (0, _networkStatus.
|
|
160
|
+
const network = await (0, _networkStatus.getLastKnownNetworkStatus)();
|
|
157
161
|
if (network.isConnected === false) {
|
|
158
162
|
_index2.Logger.warn(`Request blocked: device is offline. URL: ${config.url}`);
|
|
159
163
|
return Promise.reject(new RequestOfflineError());
|
|
@@ -208,7 +212,9 @@ userClient.interceptors.response.use(response => response, async error => {
|
|
|
208
212
|
const axiosError = error;
|
|
209
213
|
const originalRequest = axiosError.config;
|
|
210
214
|
if (!axiosError.response) {
|
|
211
|
-
|
|
215
|
+
if (!originalRequest?.skipErrorLog) {
|
|
216
|
+
_index2.Logger.error(`Network error or no response from server: ${axiosError.message}, URL: ${originalRequest?.url}`);
|
|
217
|
+
}
|
|
212
218
|
return Promise.reject(error);
|
|
213
219
|
}
|
|
214
220
|
if (axiosError.response.status === 401 && originalRequest && !originalRequest._retry) {
|
|
@@ -250,7 +256,9 @@ userClient.interceptors.response.use(response => response, async error => {
|
|
|
250
256
|
return Promise.reject(err);
|
|
251
257
|
}
|
|
252
258
|
}
|
|
253
|
-
|
|
259
|
+
if (!originalRequest?.skipErrorLog) {
|
|
260
|
+
_index2.Logger.error(`Response error: ${axiosError.response?.data ?? axiosError.message}, URL: ${originalRequest?.url}, Status: ${axiosError.response.status}`);
|
|
261
|
+
}
|
|
254
262
|
return Promise.reject(error);
|
|
255
263
|
});
|
|
256
264
|
var _default = exports.default = userClient;
|
|
@@ -87,6 +87,24 @@ const expoSecureStoreBackend = {
|
|
|
87
87
|
}
|
|
88
88
|
};
|
|
89
89
|
const resolveBackend = () => backend ?? expoSecureStoreBackend;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Every request reads the access token, and a secure-store read is a native
|
|
93
|
+
* keychain/keystore decrypt. Reads are therefore served from memory, written
|
|
94
|
+
* through by `storeToken` and dropped by `removeToken`. `generation` moves on
|
|
95
|
+
* every write, removal and backend swap, so a read that started before one of
|
|
96
|
+
* those cannot cache the value it saw.
|
|
97
|
+
*/
|
|
98
|
+
const memo = new Map();
|
|
99
|
+
let generation = 0;
|
|
100
|
+
const forget = key => {
|
|
101
|
+
generation += 1;
|
|
102
|
+
if (key === undefined) {
|
|
103
|
+
memo.clear();
|
|
104
|
+
} else {
|
|
105
|
+
memo.delete(key);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
90
108
|
const requireBackend = op => {
|
|
91
109
|
const b = resolveBackend();
|
|
92
110
|
if (!b) {
|
|
@@ -104,6 +122,7 @@ const requireBackend = op => {
|
|
|
104
122
|
*/
|
|
105
123
|
const setKeychainImplementation = impl => {
|
|
106
124
|
backend = impl;
|
|
125
|
+
forget();
|
|
107
126
|
};
|
|
108
127
|
|
|
109
128
|
/**
|
|
@@ -114,6 +133,7 @@ const storeToken = async (key, token, expiry) => {
|
|
|
114
133
|
// Never log the token or expiry values: Logger forwards to whatever remote
|
|
115
134
|
// sink the app wired (Crashlytics/Sentry), so a token here leaves the device.
|
|
116
135
|
_index.Logger.info(`Storing token for key: ${key}`);
|
|
136
|
+
forget(key);
|
|
117
137
|
try {
|
|
118
138
|
const data = JSON.stringify({
|
|
119
139
|
token,
|
|
@@ -122,8 +142,14 @@ const storeToken = async (key, token, expiry) => {
|
|
|
122
142
|
await requireBackend('store a token').setGenericPassword('token', data, {
|
|
123
143
|
service: key
|
|
124
144
|
});
|
|
145
|
+
forget(key);
|
|
146
|
+
memo.set(key, {
|
|
147
|
+
token,
|
|
148
|
+
expiry
|
|
149
|
+
});
|
|
125
150
|
_index.Logger.info(`Token stored successfully for key: ${key}`);
|
|
126
151
|
} catch (error) {
|
|
152
|
+
forget(key);
|
|
127
153
|
// Log AND rethrow. Swallowing this told the caller the token was persisted
|
|
128
154
|
// when it was not, so an app would show a signed-in user holding a token
|
|
129
155
|
// that never reached the keychain — the session then dies at the next
|
|
@@ -162,6 +188,10 @@ const parseTokenData = credentials => {
|
|
|
162
188
|
* Retrieve token and check if expired.
|
|
163
189
|
*/
|
|
164
190
|
const getToken = async key => {
|
|
191
|
+
if (memo.has(key)) {
|
|
192
|
+
return memo.get(key) ?? null;
|
|
193
|
+
}
|
|
194
|
+
const seen = generation;
|
|
165
195
|
try {
|
|
166
196
|
const credentials = await requireBackend('read a token').getGenericPassword({
|
|
167
197
|
service: key
|
|
@@ -170,24 +200,24 @@ const getToken = async key => {
|
|
|
170
200
|
// token, and every authenticated request, image and download asks for one.
|
|
171
201
|
// Warning here put three info lines and a warning into the log, and into the
|
|
172
202
|
// remote crash sink, on every one of those calls.
|
|
173
|
-
|
|
174
|
-
|
|
203
|
+
let value = null;
|
|
204
|
+
if (credentials) {
|
|
205
|
+
const parsedData = parseTokenData(credentials);
|
|
206
|
+
if (parsedData) {
|
|
207
|
+
value = {
|
|
208
|
+
token: parsedData.token,
|
|
209
|
+
expiry: parsedData.expiry
|
|
210
|
+
};
|
|
211
|
+
} else {
|
|
212
|
+
// Something IS stored and it is not a token. That is corruption worth
|
|
213
|
+
// hearing about, and it is a different event from having no token.
|
|
214
|
+
_index.Logger.warn(`Stored credentials for key "${key}" are not a valid token; ignoring them.`);
|
|
215
|
+
}
|
|
175
216
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
// Something IS stored and it is not a token. That is corruption worth
|
|
179
|
-
// hearing about, and it is a different event from having no token.
|
|
180
|
-
_index.Logger.warn(`Stored credentials for key "${key}" are not a valid token; ignoring them.`);
|
|
181
|
-
return null;
|
|
217
|
+
if (seen === generation) {
|
|
218
|
+
memo.set(key, value);
|
|
182
219
|
}
|
|
183
|
-
|
|
184
|
-
token,
|
|
185
|
-
expiry
|
|
186
|
-
} = parsedData;
|
|
187
|
-
return {
|
|
188
|
-
token,
|
|
189
|
-
expiry
|
|
190
|
-
};
|
|
220
|
+
return value;
|
|
191
221
|
} catch (error) {
|
|
192
222
|
const message = error instanceof Error ? error.message : String(error);
|
|
193
223
|
// Both spellings matter. Android throws "Key permanently invalidated" when
|
|
@@ -211,6 +241,7 @@ const getToken = async key => {
|
|
|
211
241
|
*/
|
|
212
242
|
exports.getToken = getToken;
|
|
213
243
|
const removeToken = async key => {
|
|
244
|
+
forget(key);
|
|
214
245
|
try {
|
|
215
246
|
await requireBackend('remove a token').resetGenericPassword({
|
|
216
247
|
service: key
|
|
@@ -226,6 +257,8 @@ const removeToken = async key => {
|
|
|
226
257
|
return;
|
|
227
258
|
}
|
|
228
259
|
_index.Logger.error(`Failed to remove token for key: ${key}`, error);
|
|
260
|
+
} finally {
|
|
261
|
+
forget(key);
|
|
229
262
|
}
|
|
230
263
|
};
|
|
231
264
|
exports.removeToken = removeToken;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.getConfig = void 0;
|
|
6
|
+
exports.getConfig = exports.DEFAULT_LOG_LEVEL = void 0;
|
|
7
7
|
exports.getConfigValue = getConfigValue;
|
|
8
8
|
exports.setConfig = exports.resetConfig = void 0;
|
|
9
9
|
/**
|
|
@@ -11,10 +11,16 @@ exports.setConfig = exports.resetConfig = void 0;
|
|
|
11
11
|
* Provides a flexible way to inject configuration without hard dependencies
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Info lines (each token refresh, each unauthorised URL) reach the device log
|
|
16
|
+
* and any remote sink, so a release build that sets nothing logs warnings and
|
|
17
|
+
* errors only.
|
|
18
|
+
*/
|
|
19
|
+
const DEFAULT_LOG_LEVEL = exports.DEFAULT_LOG_LEVEL = __DEV__ ? 'info' : 'warn';
|
|
14
20
|
const DEFAULT_CONFIG = {
|
|
15
21
|
baseUrl: null,
|
|
16
22
|
loggingEnabled: true,
|
|
17
|
-
logLevel:
|
|
23
|
+
logLevel: DEFAULT_LOG_LEVEL,
|
|
18
24
|
environment: 'Development',
|
|
19
25
|
country: 'in',
|
|
20
26
|
appId: null,
|
|
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.formatCurrency = void 0;
|
|
7
|
+
var _intlCache = require("./intlCache.js");
|
|
7
8
|
const hasIntlNumber = () => typeof Intl !== 'undefined' && typeof Intl.NumberFormat === 'function';
|
|
8
9
|
const indianGroupingFallback = (n, fractionDigits) => {
|
|
9
10
|
// Indian numbering: 1,23,45,678.90 (last 3 digits, then groups of 2).
|
|
@@ -41,7 +42,7 @@ const formatCurrency = (amount, options = {}) => {
|
|
|
41
42
|
const minFrac = minimumFractionDigits ?? (Number.isInteger(amount) ? 0 : 2);
|
|
42
43
|
if (hasIntlNumber()) {
|
|
43
44
|
try {
|
|
44
|
-
const formatted =
|
|
45
|
+
const formatted = (0, _intlCache.numberFormat)(locale, {
|
|
45
46
|
style: 'currency',
|
|
46
47
|
currency,
|
|
47
48
|
minimumFractionDigits: minFrac,
|
|
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.formatTime = exports.formatRelativeTime = exports.formatDateTime = exports.formatDate = void 0;
|
|
7
|
+
var _intlCache = require("./intlCache.js");
|
|
7
8
|
const toDate = value => {
|
|
8
9
|
if (value instanceof Date) {
|
|
9
10
|
return Number.isNaN(value.getTime()) ? null : value;
|
|
@@ -52,7 +53,7 @@ const formatDate = (value, style = 'medium', locale = 'en-IN') => {
|
|
|
52
53
|
}
|
|
53
54
|
if (hasIntl()) {
|
|
54
55
|
try {
|
|
55
|
-
return
|
|
56
|
+
return (0, _intlCache.dateTimeFormat)(locale, {
|
|
56
57
|
dateStyle: style
|
|
57
58
|
}).format(d);
|
|
58
59
|
} catch {
|
|
@@ -73,7 +74,7 @@ const formatTime = (value, style = 'short', locale = 'en-IN') => {
|
|
|
73
74
|
}
|
|
74
75
|
if (hasIntl()) {
|
|
75
76
|
try {
|
|
76
|
-
return
|
|
77
|
+
return (0, _intlCache.dateTimeFormat)(locale, {
|
|
77
78
|
timeStyle: style
|
|
78
79
|
}).format(d);
|
|
79
80
|
} catch {
|
|
@@ -94,7 +95,7 @@ const formatDateTime = (value, dateStyle = 'medium', timeStyle = 'short', locale
|
|
|
94
95
|
}
|
|
95
96
|
if (hasIntl()) {
|
|
96
97
|
try {
|
|
97
|
-
return
|
|
98
|
+
return (0, _intlCache.dateTimeFormat)(locale, {
|
|
98
99
|
dateStyle,
|
|
99
100
|
timeStyle
|
|
100
101
|
}).format(d);
|
|
@@ -152,9 +153,7 @@ const formatRelativeTime = (value, base = new Date(), locale = 'en-IN') => {
|
|
|
152
153
|
const Rtf = hasIntl() ? Intl.RelativeTimeFormat : undefined;
|
|
153
154
|
if (typeof Rtf === 'function') {
|
|
154
155
|
try {
|
|
155
|
-
return
|
|
156
|
-
numeric: 'auto'
|
|
157
|
-
}).format(amount, unit);
|
|
156
|
+
return (0, _intlCache.relativeTimeFormat)(Rtf, locale).format(amount, unit);
|
|
158
157
|
} catch {
|
|
159
158
|
/* fall through */
|
|
160
159
|
}
|
|
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.startOfDay = exports.isSameDay = exports.isDateBefore = exports.isDateAfter = exports.formatTimeInZone = exports.formatTimeInWindowsZone = exports.formatTime12h = exports.formatDatePattern = exports.formatDateISO = exports.formatDateDDMMMYYYY = exports.diffMs = exports.addDays = void 0;
|
|
7
|
+
var _intlCache = require("./intlCache.js");
|
|
7
8
|
var _windowsToIanaTable = require("./windowsToIanaTable.js");
|
|
8
9
|
const toDate = value => {
|
|
9
10
|
if (value instanceof Date) {
|
|
@@ -277,7 +278,7 @@ const formatTimeInZone = (value, ianaTimeZone) => {
|
|
|
277
278
|
// answer available, not a genuine timezone conversion.
|
|
278
279
|
return formatTime12h(d);
|
|
279
280
|
}
|
|
280
|
-
const parts =
|
|
281
|
+
const parts = (0, _intlCache.dateTimeFormat)('en-US', {
|
|
281
282
|
timeZone: ianaTimeZone,
|
|
282
283
|
hour: 'numeric',
|
|
283
284
|
minute: '2-digit',
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.relativeTimeFormat = exports.numberFormat = exports.dateTimeFormat = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* Building an Intl formatter costs far more than using one (and on Hermes for
|
|
9
|
+
* Android it goes through ICU over JNI), while list rows and slider frames call
|
|
10
|
+
* the formatters many times with the same arguments. Formatters are therefore
|
|
11
|
+
* built once per locale and options. The limit only guards against unbounded
|
|
12
|
+
* input such as arbitrary time zones; real apps use a handful of keys.
|
|
13
|
+
*/
|
|
14
|
+
const LIMIT = 100;
|
|
15
|
+
const cached = (cache, key, create) => {
|
|
16
|
+
let formatter = cache.get(key);
|
|
17
|
+
if (formatter === undefined) {
|
|
18
|
+
formatter = create();
|
|
19
|
+
if (cache.size >= LIMIT) {
|
|
20
|
+
cache.clear();
|
|
21
|
+
}
|
|
22
|
+
cache.set(key, formatter);
|
|
23
|
+
}
|
|
24
|
+
return formatter;
|
|
25
|
+
};
|
|
26
|
+
const numberFormats = new Map();
|
|
27
|
+
const dateTimeFormats = new Map();
|
|
28
|
+
const relativeTimeFormats = new Map();
|
|
29
|
+
const numberFormat = (locale, options = {}) => cached(numberFormats, `${locale}|${JSON.stringify(options)}`, () => new Intl.NumberFormat(locale, options));
|
|
30
|
+
exports.numberFormat = numberFormat;
|
|
31
|
+
const dateTimeFormat = (locale, options) => cached(dateTimeFormats, `${locale}|${JSON.stringify(options)}`, () => new Intl.DateTimeFormat(locale, options));
|
|
32
|
+
exports.dateTimeFormat = dateTimeFormat;
|
|
33
|
+
const relativeTimeFormat = (RelativeTimeFormat, locale) => cached(relativeTimeFormats, locale, () => new RelativeTimeFormat(locale, {
|
|
34
|
+
numeric: 'auto'
|
|
35
|
+
}));
|
|
36
|
+
exports.relativeTimeFormat = relativeTimeFormat;
|
|
37
|
+
//# sourceMappingURL=intlCache.js.map
|
|
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.formatPercent = exports.formatNumber = exports.formatCompactNumber = void 0;
|
|
7
|
+
var _intlCache = require("./intlCache.js");
|
|
7
8
|
const hasIntlNumber = () => typeof Intl !== 'undefined' && typeof Intl.NumberFormat === 'function';
|
|
8
9
|
|
|
9
10
|
/**
|
|
@@ -20,7 +21,7 @@ const formatNumber = (value, fractionDigits, locale = 'en-IN') => {
|
|
|
20
21
|
opts.minimumFractionDigits = fractionDigits;
|
|
21
22
|
opts.maximumFractionDigits = fractionDigits;
|
|
22
23
|
}
|
|
23
|
-
return
|
|
24
|
+
return (0, _intlCache.numberFormat)(locale, opts).format(value);
|
|
24
25
|
} catch {
|
|
25
26
|
/* fall through */
|
|
26
27
|
}
|
|
@@ -40,7 +41,7 @@ const formatPercent = (value, fractionDigits = 0, asRatio = true, locale = 'en-I
|
|
|
40
41
|
const ratio = asRatio ? value : value / 100;
|
|
41
42
|
if (hasIntlNumber()) {
|
|
42
43
|
try {
|
|
43
|
-
return
|
|
44
|
+
return (0, _intlCache.numberFormat)(locale, {
|
|
44
45
|
style: 'percent',
|
|
45
46
|
minimumFractionDigits: fractionDigits,
|
|
46
47
|
maximumFractionDigits: fractionDigits
|
|
@@ -63,7 +64,7 @@ const formatCompactNumber = (value, locale = 'en-IN') => {
|
|
|
63
64
|
}
|
|
64
65
|
if (hasIntlNumber()) {
|
|
65
66
|
try {
|
|
66
|
-
return
|
|
67
|
+
return (0, _intlCache.numberFormat)(locale, {
|
|
67
68
|
notation: 'compact',
|
|
68
69
|
maximumFractionDigits: 1
|
|
69
70
|
}).format(value);
|
package/lib/commonjs/index.js
CHANGED
|
@@ -117,6 +117,12 @@ Object.defineProperty(exports, "diffMs", {
|
|
|
117
117
|
return _index3.diffMs;
|
|
118
118
|
}
|
|
119
119
|
});
|
|
120
|
+
Object.defineProperty(exports, "encodeBase64", {
|
|
121
|
+
enumerable: true,
|
|
122
|
+
get: function () {
|
|
123
|
+
return _base.encodeBase64;
|
|
124
|
+
}
|
|
125
|
+
});
|
|
120
126
|
Object.defineProperty(exports, "formatCompactNumber", {
|
|
121
127
|
enumerable: true,
|
|
122
128
|
get: function () {
|
|
@@ -501,6 +507,12 @@ Object.defineProperty(exports, "publicClient", {
|
|
|
501
507
|
return _publicClient.default;
|
|
502
508
|
}
|
|
503
509
|
});
|
|
510
|
+
Object.defineProperty(exports, "refreshAccessToken", {
|
|
511
|
+
enumerable: true,
|
|
512
|
+
get: function () {
|
|
513
|
+
return _tokenRefresh.refreshAccessToken;
|
|
514
|
+
}
|
|
515
|
+
});
|
|
504
516
|
Object.defineProperty(exports, "removeToken", {
|
|
505
517
|
enumerable: true,
|
|
506
518
|
get: function () {
|
|
@@ -612,6 +624,7 @@ Object.defineProperty(exports, "validatePhoneNumberLength", {
|
|
|
612
624
|
var _curl = require("./api/curl.js");
|
|
613
625
|
var _publicClient = _interopRequireDefault(require("./api/publicClient.js"));
|
|
614
626
|
var _retry = require("./api/retry.js");
|
|
627
|
+
var _tokenRefresh = require("./api/tokenRefresh.js");
|
|
615
628
|
var _userClient = _interopRequireWildcard(require("./api/userClient.js"));
|
|
616
629
|
var _authStore = require("./auth/authStore.js");
|
|
617
630
|
var _jwt = require("./auth/jwt.js");
|
|
@@ -623,6 +636,7 @@ var _initUserAuth = require("./initUserAuth.js");
|
|
|
623
636
|
var _index4 = require("./logger/index.js");
|
|
624
637
|
var _index5 = require("./network/index.js");
|
|
625
638
|
var _index6 = require("./storage/index.js");
|
|
639
|
+
var _base = require("./utils/base64.js");
|
|
626
640
|
var _index7 = require("./validators/index.js");
|
|
627
641
|
var _index8 = require("./versionCheck/index.js");
|
|
628
642
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
package/lib/commonjs/initSDK.js
CHANGED
|
@@ -39,7 +39,7 @@ const initWebority = config => {
|
|
|
39
39
|
(0, _index.setConfig)({
|
|
40
40
|
baseUrl: config.baseUrl,
|
|
41
41
|
loggingEnabled: config.loggingEnabled ?? true,
|
|
42
|
-
logLevel: config.logLevel ||
|
|
42
|
+
logLevel: config.logLevel || _index.DEFAULT_LOG_LEVEL,
|
|
43
43
|
environment: config.environment || 'Development',
|
|
44
44
|
country: (config.country || 'in').toLowerCase(),
|
|
45
45
|
appId: config.appId ?? null,
|
|
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.setRemoteLogger = exports.setDeviceContext = exports.getDeviceContext = exports.configureLogger = exports.Logger = void 0;
|
|
7
7
|
var _index = require("../config/index.js");
|
|
8
|
+
var _redact = require("./redact.js");
|
|
8
9
|
// ANSI color codes
|
|
9
10
|
const Colors = {
|
|
10
11
|
RESET: '\x1b[0m',
|
|
@@ -209,21 +210,27 @@ const forwardErrorToRemote = (err, context) => {
|
|
|
209
210
|
}
|
|
210
211
|
safe(() => remote?.recordError?.(err, buildErrorContext(context, suppressedCount)));
|
|
211
212
|
};
|
|
212
|
-
const logInfo = (
|
|
213
|
+
const logInfo = (rawMessage, ...rawParams) => {
|
|
214
|
+
const message = (0, _redact.redactString)(rawMessage);
|
|
215
|
+
const optionalParams = (0, _redact.redact)(rawParams);
|
|
213
216
|
if (!isEnabled() || !isLogLevelAllowed(LogLevels.INFO.level)) {
|
|
214
217
|
return;
|
|
215
218
|
}
|
|
216
219
|
console.info(`${LogLevels.INFO.color}[INFO]: ${message}${Colors.RESET}`, ...optionalParams);
|
|
217
220
|
safe(() => remote?.log?.('info', message, optionalParams));
|
|
218
221
|
};
|
|
219
|
-
const logWarning = (
|
|
222
|
+
const logWarning = (rawMessage, ...rawParams) => {
|
|
223
|
+
const message = (0, _redact.redactString)(rawMessage);
|
|
224
|
+
const optionalParams = (0, _redact.redact)(rawParams);
|
|
220
225
|
if (!isEnabled() || !isLogLevelAllowed(LogLevels.WARN.level)) {
|
|
221
226
|
return;
|
|
222
227
|
}
|
|
223
228
|
console.warn(`${LogLevels.WARN.color}[WARNING]: ${message}${Colors.RESET}`, ...optionalParams);
|
|
224
229
|
safe(() => remote?.log?.('warn', message, optionalParams));
|
|
225
230
|
};
|
|
226
|
-
const logError = (
|
|
231
|
+
const logError = (rawMessage, ...rawParams) => {
|
|
232
|
+
const message = (0, _redact.redactString)(rawMessage);
|
|
233
|
+
const optionalParams = (0, _redact.redact)(rawParams);
|
|
227
234
|
if (isEnabled() && isLogLevelAllowed(LogLevels.ERROR.level)) {
|
|
228
235
|
console.error(`${LogLevels.ERROR.color}[ERROR]: ${message}${Colors.RESET}`, ...optionalParams);
|
|
229
236
|
}
|
|
@@ -239,14 +246,17 @@ const logError = (message, ...optionalParams) => {
|
|
|
239
246
|
});
|
|
240
247
|
}
|
|
241
248
|
};
|
|
242
|
-
const breadcrumb = (
|
|
249
|
+
const breadcrumb = (rawMessage, rawData) => {
|
|
250
|
+
const message = (0, _redact.redactString)(rawMessage);
|
|
251
|
+
const data = (0, _redact.redact)(rawData);
|
|
243
252
|
if (isEnabled() && isLogLevelAllowed('info')) {
|
|
244
253
|
console.info(`${Colors.BLUE}[BREADCRUMB]: ${message}${Colors.RESET}`, data ?? '');
|
|
245
254
|
}
|
|
246
255
|
safe(() => remote?.breadcrumb?.(message, data));
|
|
247
256
|
};
|
|
248
|
-
const recordError = (error,
|
|
249
|
-
const err = toErrorObject(error);
|
|
257
|
+
const recordError = (error, rawContext) => {
|
|
258
|
+
const err = (0, _redact.redact)(toErrorObject(error));
|
|
259
|
+
const context = (0, _redact.redact)(rawContext);
|
|
250
260
|
if (isEnabled() && isLogLevelAllowed('error')) {
|
|
251
261
|
console.error(`${Colors.RED_BOLD}[RECORDED]: ${err.message}${Colors.RESET}`, context ?? '');
|
|
252
262
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.redactString = exports.redact = void 0;
|
|
7
|
+
const REDACTED = '[REDACTED]';
|
|
8
|
+
const SECRET_KEYS = /^(token|accesstoken|refreshtoken|password|authorization)$/i;
|
|
9
|
+
const BEARER = /\bBearer\s+[A-Za-z0-9._~+/-]+=*/gi;
|
|
10
|
+
const JWT = /\beyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*/g;
|
|
11
|
+
const KEYED_VALUE = /(["']?(?:token|accessToken|refreshToken|password|authorization)["']?\s*[:=]\s*)(?:"(?:[^"\\]|\\.)*"|'[^']*'|[^\s,;&}]+)/gi;
|
|
12
|
+
const MAX_DEPTH = 6;
|
|
13
|
+
const redactString = input => input.replace(BEARER, `Bearer ${REDACTED}`).replace(JWT, REDACTED).replace(KEYED_VALUE, (_match, prefix) => `${prefix}"${REDACTED}"`);
|
|
14
|
+
exports.redactString = redactString;
|
|
15
|
+
const redactInner = (value, depth, seen) => {
|
|
16
|
+
if (typeof value === 'string') {
|
|
17
|
+
return redactString(value);
|
|
18
|
+
}
|
|
19
|
+
if (value === null || typeof value !== 'object') {
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
if (value instanceof Error) {
|
|
23
|
+
value.message = redactString(value.message);
|
|
24
|
+
if (value.stack) {
|
|
25
|
+
value.stack = redactString(value.stack);
|
|
26
|
+
}
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
if (depth >= MAX_DEPTH || seen.has(value)) {
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
seen.add(value);
|
|
33
|
+
if (Array.isArray(value)) {
|
|
34
|
+
return value.map(item => redactInner(item, depth + 1, seen));
|
|
35
|
+
}
|
|
36
|
+
const proto = Object.getPrototypeOf(value);
|
|
37
|
+
if (proto !== Object.prototype && proto !== null) {
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
const out = {};
|
|
41
|
+
for (const [key, item] of Object.entries(value)) {
|
|
42
|
+
out[key] = SECRET_KEYS.test(key) ? REDACTED : redactInner(item, depth + 1, seen);
|
|
43
|
+
}
|
|
44
|
+
return out;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/** Returns a copy with secrets masked; Errors are masked in place so identity and stack survive. */
|
|
48
|
+
const redact = value => redactInner(value, 0, new WeakSet());
|
|
49
|
+
exports.redact = redact;
|
|
50
|
+
//# sourceMappingURL=redact.js.map
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.useNetworkStatus = exports.setNetworkStatusImplementation = exports.getNetworkStatus = exports.addNetworkStatusListener = void 0;
|
|
6
|
+
exports.useNetworkStatus = exports.setNetworkStatusImplementation = exports.getNetworkStatus = exports.getLastKnownNetworkStatus = exports.addNetworkStatusListener = void 0;
|
|
7
7
|
var ExpoNetwork = _interopRequireWildcard(require("expo-network"));
|
|
8
8
|
var _react = require("react");
|
|
9
9
|
var _index = require("../logger/index.js");
|
|
@@ -23,6 +23,13 @@ const DEFAULT_STATUS = {
|
|
|
23
23
|
* One backend also means one behaviour, rather than each app choosing.
|
|
24
24
|
*/
|
|
25
25
|
let override = null;
|
|
26
|
+
let lastKnown = null;
|
|
27
|
+
let stopFollowing = null;
|
|
28
|
+
const stopFollowingNetwork = () => {
|
|
29
|
+
stopFollowing?.();
|
|
30
|
+
stopFollowing = null;
|
|
31
|
+
lastKnown = null;
|
|
32
|
+
};
|
|
26
33
|
|
|
27
34
|
/**
|
|
28
35
|
* Replace the backend. Intended for TESTS and for the showcase, which uses it
|
|
@@ -31,6 +38,7 @@ let override = null;
|
|
|
31
38
|
*/
|
|
32
39
|
const setNetworkStatusImplementation = impl => {
|
|
33
40
|
override = impl;
|
|
41
|
+
stopFollowingNetwork();
|
|
34
42
|
};
|
|
35
43
|
exports.setNetworkStatusImplementation = setNetworkStatusImplementation;
|
|
36
44
|
const resolveNetInfo = () => override ?? expoNetworkBackend;
|
|
@@ -103,11 +111,41 @@ const addNetworkStatusListener = listener => {
|
|
|
103
111
|
return ni.addEventListener(state => listener(normalize(state)));
|
|
104
112
|
};
|
|
105
113
|
|
|
114
|
+
/**
|
|
115
|
+
* The connectivity the HTTP client checks before every request. One listener,
|
|
116
|
+
* attached on first use, keeps it current, so a request costs no native probe:
|
|
117
|
+
* one probe seeds the value and the listener's events replace it.
|
|
118
|
+
*/
|
|
119
|
+
exports.addNetworkStatusListener = addNetworkStatusListener;
|
|
120
|
+
const getLastKnownNetworkStatus = async () => {
|
|
121
|
+
if (!stopFollowing) {
|
|
122
|
+
try {
|
|
123
|
+
stopFollowing = addNetworkStatusListener(status => {
|
|
124
|
+
lastKnown = status;
|
|
125
|
+
});
|
|
126
|
+
} catch (error) {
|
|
127
|
+
_index.Logger.error('[network] could not follow network changes', error);
|
|
128
|
+
return getNetworkStatus();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (lastKnown) {
|
|
132
|
+
return lastKnown;
|
|
133
|
+
}
|
|
134
|
+
const following = stopFollowing;
|
|
135
|
+
const probed = await getNetworkStatus();
|
|
136
|
+
if (stopFollowing !== following) {
|
|
137
|
+
return probed;
|
|
138
|
+
}
|
|
139
|
+
// An event that arrived during the probe is newer than the probe.
|
|
140
|
+
lastKnown ??= probed;
|
|
141
|
+
return lastKnown;
|
|
142
|
+
};
|
|
143
|
+
|
|
106
144
|
/**
|
|
107
145
|
* React hook that returns the current network status and re-renders on change.
|
|
108
146
|
* Reports an optimistic online default when NetInfo is not installed.
|
|
109
147
|
*/
|
|
110
|
-
exports.
|
|
148
|
+
exports.getLastKnownNetworkStatus = getLastKnownNetworkStatus;
|
|
111
149
|
const useNetworkStatus = () => {
|
|
112
150
|
const [status, setStatus] = (0, _react.useState)(DEFAULT_STATUS);
|
|
113
151
|
(0, _react.useEffect)(() => {
|