@webority-technologies/mobile-core 0.0.1 → 0.0.2

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 (146) hide show
  1. package/README.md +22 -1
  2. package/lib/commonjs/api/baseUrl.js +34 -0
  3. package/lib/commonjs/api/curl.js +109 -0
  4. package/lib/commonjs/api/publicClient.js +11 -20
  5. package/lib/commonjs/api/retry.js +75 -0
  6. package/lib/commonjs/api/userClient.js +46 -22
  7. package/lib/commonjs/auth/authStore.js +101 -33
  8. package/lib/commonjs/auth/jwt.js +32 -8
  9. package/lib/commonjs/config/index.js +2 -1
  10. package/lib/commonjs/deepLink/index.js +286 -0
  11. package/lib/commonjs/download/adapters/expoFs.js +100 -0
  12. package/lib/commonjs/download/adapters/shared.js +13 -0
  13. package/lib/commonjs/download/index.js +272 -0
  14. package/lib/commonjs/formatters/date.js +1 -1
  15. package/lib/commonjs/formatters/index.js +6 -0
  16. package/lib/commonjs/formatters/phone.js +103 -15
  17. package/lib/commonjs/geolocation/adapters/expoLocation.js +126 -0
  18. package/lib/commonjs/geolocation/index.js +339 -0
  19. package/lib/commonjs/index.js +133 -48
  20. package/lib/commonjs/initSDK.js +23 -2
  21. package/lib/commonjs/initUserAuth.js +2 -1
  22. package/lib/commonjs/logger/index.js +159 -5
  23. package/lib/commonjs/network/index.js +6 -0
  24. package/lib/commonjs/network/networkStatus.js +50 -15
  25. package/lib/commonjs/permissions/index.js +24 -1
  26. package/lib/commonjs/sqlite/adapters/expo.js +53 -0
  27. package/lib/commonjs/sqlite/adapters/op.js +43 -0
  28. package/lib/commonjs/sqlite/adapters/rows.js +33 -0
  29. package/lib/commonjs/sqlite/adapters/storage.js +50 -0
  30. package/lib/commonjs/sqlite/adapters/sync.js +45 -0
  31. package/lib/commonjs/sqlite/index.js +610 -0
  32. package/lib/commonjs/sqlite/nitro.js +17 -0
  33. package/lib/commonjs/sqlite/op.js +18 -0
  34. package/lib/commonjs/sqlite/quick.js +14 -0
  35. package/lib/commonjs/sqlite/storage.js +14 -0
  36. package/lib/commonjs/storage/index.js +40 -19
  37. package/lib/commonjs/types/globals.d.js +6 -0
  38. package/lib/commonjs/utils/imageCompression.js +51 -14
  39. package/lib/commonjs/utils/index.js +6 -0
  40. package/lib/commonjs/versionCheck/index.js +28 -4
  41. package/lib/module/api/baseUrl.js +30 -0
  42. package/lib/module/api/curl.js +104 -0
  43. package/lib/module/api/publicClient.js +11 -20
  44. package/lib/module/api/retry.js +69 -0
  45. package/lib/module/api/userClient.js +45 -21
  46. package/lib/module/auth/authStore.js +99 -32
  47. package/lib/module/auth/jwt.js +32 -8
  48. package/lib/module/config/index.js +2 -1
  49. package/lib/module/deepLink/index.js +280 -0
  50. package/lib/module/download/adapters/expoFs.js +95 -0
  51. package/lib/module/download/adapters/shared.js +8 -0
  52. package/lib/module/download/index.js +264 -0
  53. package/lib/module/formatters/date.js +1 -1
  54. package/lib/module/formatters/index.js +1 -1
  55. package/lib/module/formatters/phone.js +99 -13
  56. package/lib/module/geolocation/adapters/expoLocation.js +121 -0
  57. package/lib/module/geolocation/index.js +332 -0
  58. package/lib/module/index.js +28 -11
  59. package/lib/module/initSDK.js +23 -2
  60. package/lib/module/initUserAuth.js +2 -1
  61. package/lib/module/logger/index.js +156 -4
  62. package/lib/module/network/index.js +1 -1
  63. package/lib/module/network/networkStatus.js +47 -14
  64. package/lib/module/permissions/index.js +22 -0
  65. package/lib/module/sqlite/adapters/expo.js +48 -0
  66. package/lib/module/sqlite/adapters/op.js +38 -0
  67. package/lib/module/sqlite/adapters/rows.js +28 -0
  68. package/lib/module/sqlite/adapters/storage.js +46 -0
  69. package/lib/module/sqlite/adapters/sync.js +41 -0
  70. package/lib/module/sqlite/index.js +600 -0
  71. package/lib/module/sqlite/nitro.js +12 -0
  72. package/lib/module/sqlite/op.js +13 -0
  73. package/lib/module/sqlite/quick.js +9 -0
  74. package/lib/module/sqlite/storage.js +9 -0
  75. package/lib/module/storage/index.js +37 -18
  76. package/lib/module/types/globals.d.js +10 -0
  77. package/lib/module/utils/imageCompression.js +49 -13
  78. package/lib/module/utils/index.js +1 -1
  79. package/lib/module/versionCheck/index.js +27 -4
  80. package/lib/typescript/commonjs/api/baseUrl.d.ts +20 -0
  81. package/lib/typescript/commonjs/api/curl.d.ts +18 -0
  82. package/lib/typescript/commonjs/api/retry.d.ts +27 -0
  83. package/lib/typescript/commonjs/api/userClient.d.ts +11 -0
  84. package/lib/typescript/commonjs/auth/authStore.d.ts +29 -6
  85. package/lib/typescript/commonjs/config/index.d.ts +6 -0
  86. package/lib/typescript/commonjs/deepLink/index.d.ts +54 -0
  87. package/lib/typescript/commonjs/download/adapters/expoFs.d.ts +45 -0
  88. package/lib/typescript/commonjs/download/adapters/shared.d.ts +3 -0
  89. package/lib/typescript/commonjs/download/index.d.ts +76 -0
  90. package/lib/typescript/commonjs/formatters/index.d.ts +2 -1
  91. package/lib/typescript/commonjs/formatters/phone.d.ts +16 -1
  92. package/lib/typescript/commonjs/geolocation/adapters/expoLocation.d.ts +42 -0
  93. package/lib/typescript/commonjs/geolocation/index.d.ts +89 -0
  94. package/lib/typescript/commonjs/index.d.ts +30 -13
  95. package/lib/typescript/commonjs/initSDK.d.ts +32 -0
  96. package/lib/typescript/commonjs/logger/index.d.ts +39 -0
  97. package/lib/typescript/commonjs/network/index.d.ts +2 -2
  98. package/lib/typescript/commonjs/network/networkStatus.d.ts +16 -0
  99. package/lib/typescript/commonjs/permissions/index.d.ts +35 -0
  100. package/lib/typescript/commonjs/sqlite/adapters/expo.d.ts +30 -0
  101. package/lib/typescript/commonjs/sqlite/adapters/op.d.ts +27 -0
  102. package/lib/typescript/commonjs/sqlite/adapters/rows.d.ts +7 -0
  103. package/lib/typescript/commonjs/sqlite/adapters/storage.d.ts +30 -0
  104. package/lib/typescript/commonjs/sqlite/adapters/sync.d.ts +31 -0
  105. package/lib/typescript/commonjs/sqlite/index.d.ts +121 -0
  106. package/lib/typescript/commonjs/sqlite/nitro.d.ts +7 -0
  107. package/lib/typescript/commonjs/sqlite/op.d.ts +8 -0
  108. package/lib/typescript/commonjs/sqlite/quick.d.ts +4 -0
  109. package/lib/typescript/commonjs/sqlite/storage.d.ts +4 -0
  110. package/lib/typescript/commonjs/storage/index.d.ts +24 -2
  111. package/lib/typescript/commonjs/utils/imageCompression.d.ts +17 -0
  112. package/lib/typescript/commonjs/utils/index.d.ts +2 -2
  113. package/lib/typescript/module/api/baseUrl.d.ts +20 -0
  114. package/lib/typescript/module/api/curl.d.ts +18 -0
  115. package/lib/typescript/module/api/retry.d.ts +27 -0
  116. package/lib/typescript/module/api/userClient.d.ts +11 -0
  117. package/lib/typescript/module/auth/authStore.d.ts +29 -6
  118. package/lib/typescript/module/config/index.d.ts +6 -0
  119. package/lib/typescript/module/deepLink/index.d.ts +54 -0
  120. package/lib/typescript/module/download/adapters/expoFs.d.ts +45 -0
  121. package/lib/typescript/module/download/adapters/shared.d.ts +3 -0
  122. package/lib/typescript/module/download/index.d.ts +76 -0
  123. package/lib/typescript/module/formatters/index.d.ts +2 -1
  124. package/lib/typescript/module/formatters/phone.d.ts +16 -1
  125. package/lib/typescript/module/geolocation/adapters/expoLocation.d.ts +42 -0
  126. package/lib/typescript/module/geolocation/index.d.ts +89 -0
  127. package/lib/typescript/module/index.d.ts +30 -13
  128. package/lib/typescript/module/initSDK.d.ts +32 -0
  129. package/lib/typescript/module/logger/index.d.ts +39 -0
  130. package/lib/typescript/module/network/index.d.ts +2 -2
  131. package/lib/typescript/module/network/networkStatus.d.ts +16 -0
  132. package/lib/typescript/module/permissions/index.d.ts +35 -0
  133. package/lib/typescript/module/sqlite/adapters/expo.d.ts +30 -0
  134. package/lib/typescript/module/sqlite/adapters/op.d.ts +27 -0
  135. package/lib/typescript/module/sqlite/adapters/rows.d.ts +7 -0
  136. package/lib/typescript/module/sqlite/adapters/storage.d.ts +30 -0
  137. package/lib/typescript/module/sqlite/adapters/sync.d.ts +31 -0
  138. package/lib/typescript/module/sqlite/index.d.ts +121 -0
  139. package/lib/typescript/module/sqlite/nitro.d.ts +7 -0
  140. package/lib/typescript/module/sqlite/op.d.ts +8 -0
  141. package/lib/typescript/module/sqlite/quick.d.ts +4 -0
  142. package/lib/typescript/module/sqlite/storage.d.ts +4 -0
  143. package/lib/typescript/module/storage/index.d.ts +24 -2
  144. package/lib/typescript/module/utils/imageCompression.d.ts +17 -0
  145. package/lib/typescript/module/utils/index.d.ts +2 -2
  146. package/package.json +102 -14
@@ -0,0 +1,332 @@
1
+ "use strict";
2
+
3
+ import * as ExpoLocation from 'expo-location';
4
+ import { useCallback, useEffect, useRef, useState } from 'react';
5
+ import { Logger } from "../logger/index.js";
6
+ import { Permissions } from "../permissions/index.js";
7
+ import { adaptExpoLocation } from "./adapters/expoLocation.js";
8
+ const PREFIX = '[@webority-technologies/mobile-core]';
9
+ export class GeolocationError extends Error {
10
+ constructor(code, message) {
11
+ super(message);
12
+ this.name = 'GeolocationError';
13
+ this.code = code;
14
+ // Babel downlevels `class extends Error`, which breaks `instanceof` for
15
+ // subclasses unless the prototype is restored explicitly.
16
+ Object.setPrototypeOf(this, GeolocationError.prototype);
17
+ }
18
+ }
19
+
20
+ /** Minimal shape of the native geolocation module we depend on. */
21
+
22
+ const DEFAULT_TIMEOUT_MS = 15000;
23
+ const DEFAULT_RETRIES = 1;
24
+ const DEFAULT_RETRY_DELAY_MS = 800;
25
+ const isGeolocationLike = value => {
26
+ const candidate = value;
27
+ return typeof candidate?.getCurrentPosition === 'function' && typeof candidate?.watchPosition === 'function' && typeof candidate?.clearWatch === 'function';
28
+ };
29
+
30
+ /** Only what a test or the showcase injected; null means use expo-location. */
31
+ let backend = null;
32
+ let expoBackend;
33
+ const resolveBackend = () => {
34
+ if (backend) {
35
+ return backend;
36
+ }
37
+ if (!expoBackend) {
38
+ expoBackend = adaptExpoLocation(ExpoLocation);
39
+ }
40
+ return expoBackend;
41
+ };
42
+
43
+ /**
44
+ * Replace the geolocation provider. Intended for TESTS and for the showcase,
45
+ * which injects a denying provider to demo the permission and services-off
46
+ * errors; an app gets expo-location without wiring anything. Pass `null` to
47
+ * restore it.
48
+ */
49
+ export const setGeolocationImplementation = impl => {
50
+ backend = impl;
51
+ };
52
+ const requireBackend = op => {
53
+ const b = resolveBackend();
54
+ if (!b) {
55
+ throw new GeolocationError('unsupported', `${PREFIX} No geolocation provider is available; cannot ${op}. ` + 'expo-location did not load, which on a real build means the native ' + 'module was not linked.');
56
+ }
57
+ return b;
58
+ };
59
+
60
+ /**
61
+ * Android reports "location services are switched off" through the same code 2
62
+ * that also covers a genuine failure to fix a position, and the two need
63
+ * different handling: one is fixed by the user in system settings, the other is
64
+ * worth retrying. The message is the only signal that separates them.
65
+ */
66
+ const SERVICES_OFF_PATTERN = /(location|gps)[^.]{0,40}(disabled|switched off|turned off|not enabled)|no location provider|settings_not_satisfied|location services (are |is )?off/i;
67
+ const toGeoError = raw => {
68
+ if (raw instanceof GeolocationError) {
69
+ return raw;
70
+ }
71
+ const native = raw ?? {};
72
+ const detail = typeof native.message === 'string' && native.message.length > 0 ? native.message : 'the provider gave no reason';
73
+ switch (native.code) {
74
+ case 1:
75
+ return new GeolocationError('permission-denied', `${PREFIX} Location permission was denied (${detail}). Request the ` + 'location permission before calling, or send the user to settings.');
76
+ case 2:
77
+ return SERVICES_OFF_PATTERN.test(detail) ? new GeolocationError('services-disabled', `${PREFIX} Location services are switched off (${detail}). Ask the user ` + 'to enable location in system settings; retrying will not help.') : new GeolocationError('position-unavailable', `${PREFIX} No position fix (${detail}).`);
78
+ case 3:
79
+ return new GeolocationError('timeout', `${PREFIX} Location request timed out (${detail}).`);
80
+ case 4:
81
+ case 5:
82
+ return new GeolocationError('services-disabled', `${PREFIX} Location services are unavailable on this device (${detail}). ` + 'Ask the user to enable location, or install Google Play services.');
83
+ default:
84
+ return new GeolocationError('position-unavailable', `${PREFIX} Location request failed (${detail}).`);
85
+ }
86
+ };
87
+ const RETRYABLE = ['timeout', 'position-unavailable'];
88
+ const normalizeCoords = raw => {
89
+ const coords = raw?.coords;
90
+ if (!coords || !Number.isFinite(coords.latitude) || !Number.isFinite(coords.longitude)) {
91
+ return null;
92
+ }
93
+ return {
94
+ latitude: coords.latitude,
95
+ longitude: coords.longitude,
96
+ // An absent radius is treated as infinitely bad rather than perfect, so a
97
+ // provider that omits it can never satisfy `minimumAccuracyM` by accident.
98
+ accuracy: Number.isFinite(coords.accuracy) ? coords.accuracy : Number.POSITIVE_INFINITY,
99
+ altitude: Number.isFinite(coords.altitude) ? coords.altitude : null,
100
+ heading: Number.isFinite(coords.heading) ? coords.heading : null,
101
+ speed: Number.isFinite(coords.speed) ? coords.speed : null
102
+ };
103
+ };
104
+ const normalizePosition = raw => {
105
+ const coords = normalizeCoords(raw);
106
+ if (!coords) {
107
+ return null;
108
+ }
109
+ return {
110
+ coords,
111
+ timestamp: Number.isFinite(raw?.timestamp) ? raw?.timestamp : Date.now()
112
+ };
113
+ };
114
+ const toNativeOptions = options => {
115
+ const native = {
116
+ enableHighAccuracy: (options.accuracy ?? 'fine') === 'fine',
117
+ timeout: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
118
+ maximumAge: options.maximumAgeMs ?? 0
119
+ };
120
+ if (options.distanceFilterM !== undefined) {
121
+ native.distanceFilter = options.distanceFilterM;
122
+ }
123
+ return native;
124
+ };
125
+ const sleep = ms => new Promise(resolve => {
126
+ setTimeout(resolve, ms);
127
+ });
128
+ const ensureLocationPermission = async () => {
129
+ const status = await Permissions.ensure('location');
130
+ if (status === 'granted' || status === 'limited') {
131
+ return;
132
+ }
133
+ if (status === 'unavailable') {
134
+ Logger.warn(`${PREFIX} Location permission status is unavailable (react-native-permissions is not ` + 'installed, or this platform has no mapping); attempting the location request anyway.');
135
+ return;
136
+ }
137
+ throw new GeolocationError('permission-denied', `${PREFIX} Location permission is "${status}". Grant it before requesting a position` + (status === 'blocked' ? ', or call Permissions.openSettings() to let the user fix it.' : '.'));
138
+ };
139
+ const positionOnce = (b, options) => {
140
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
141
+ return new Promise((resolve, reject) => {
142
+ let settled = false;
143
+ // The native timeout is passed through as well, but a provider that never
144
+ // calls back at all would otherwise hang the caller forever.
145
+ const timer = setTimeout(() => {
146
+ if (settled) {
147
+ return;
148
+ }
149
+ settled = true;
150
+ reject(new GeolocationError('timeout', `${PREFIX} Location request timed out after ${timeoutMs}ms. Raise timeoutMs, or use ` + "accuracy: 'coarse' where a network fix is good enough."));
151
+ }, timeoutMs);
152
+ const settle = run => {
153
+ if (settled) {
154
+ return;
155
+ }
156
+ settled = true;
157
+ clearTimeout(timer);
158
+ run();
159
+ };
160
+ const onSuccess = raw => settle(() => {
161
+ const position = normalizePosition(raw);
162
+ if (!position) {
163
+ reject(new GeolocationError('position-unavailable', `${PREFIX} The provider returned a fix without usable coordinates.`));
164
+ return;
165
+ }
166
+ const floor = options.minimumAccuracyM;
167
+ if (floor !== undefined && position.coords.accuracy > floor) {
168
+ reject(new GeolocationError('position-unavailable', `${PREFIX} Fix accuracy ${position.coords.accuracy}m is worse than the required ` + `${floor}m.`));
169
+ return;
170
+ }
171
+ resolve(position);
172
+ });
173
+ const onError = error => settle(() => reject(toGeoError(error)));
174
+ try {
175
+ b.getCurrentPosition(onSuccess, onError, toNativeOptions(options));
176
+ } catch (error) {
177
+ settle(() => reject(toGeoError(error)));
178
+ }
179
+ });
180
+ };
181
+ const getCurrentPosition = async (options = {}) => {
182
+ const b = requireBackend('get the current position');
183
+ await ensureLocationPermission();
184
+ const attempts = Math.max(0, Math.trunc(options.retries ?? DEFAULT_RETRIES)) + 1;
185
+ const retryDelayMs = options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS;
186
+ let lastError = null;
187
+ for (let attempt = 0; attempt < attempts; attempt += 1) {
188
+ try {
189
+ return await positionOnce(b, options);
190
+ } catch (error) {
191
+ const geoError = toGeoError(error);
192
+ if (!RETRYABLE.includes(geoError.code)) {
193
+ throw geoError;
194
+ }
195
+ lastError = geoError;
196
+ if (attempt < attempts - 1 && retryDelayMs > 0) {
197
+ await sleep(retryDelayMs);
198
+ }
199
+ }
200
+ }
201
+ throw lastError ?? new GeolocationError('position-unavailable', `${PREFIX} Location request failed.`);
202
+ };
203
+ const watchPosition = (listener, options = {}) => {
204
+ const b = requireBackend('watch the position');
205
+ let cancelled = false;
206
+ let watchId = null;
207
+ const stop = () => {
208
+ if (cancelled) {
209
+ return;
210
+ }
211
+ cancelled = true;
212
+ if (watchId !== null) {
213
+ b.clearWatch(watchId);
214
+ watchId = null;
215
+ }
216
+ };
217
+ const fail = error => {
218
+ const geoError = toGeoError(error);
219
+ Logger.error(geoError.message);
220
+ options.onError?.(geoError);
221
+ };
222
+ void ensureLocationPermission().then(() => {
223
+ // The caller can unsubscribe while the permission gate is still pending;
224
+ // registering afterwards would leak a watch nothing can clear.
225
+ if (cancelled) {
226
+ return;
227
+ }
228
+ watchId = b.watchPosition(raw => {
229
+ if (cancelled) {
230
+ return;
231
+ }
232
+ const position = normalizePosition(raw);
233
+ if (!position) {
234
+ return;
235
+ }
236
+ const floor = options.minimumAccuracyM;
237
+ if (floor !== undefined && position.coords.accuracy > floor) {
238
+ return;
239
+ }
240
+ listener(position);
241
+ }, error => {
242
+ if (!cancelled) {
243
+ fail(error);
244
+ }
245
+ }, toNativeOptions(options));
246
+ }).catch(fail);
247
+ return stop;
248
+ };
249
+ const isAvailable = () => resolveBackend() !== null;
250
+ export const Geolocation = {
251
+ getCurrentPosition,
252
+ watchPosition,
253
+ isAvailable
254
+ };
255
+ export const useCurrentPosition = (options = {}) => {
256
+ const {
257
+ enabled = true,
258
+ accuracy,
259
+ timeoutMs,
260
+ maximumAgeMs,
261
+ retries,
262
+ minimumAccuracyM,
263
+ retryDelayMs
264
+ } = options;
265
+ const [state, setState] = useState({
266
+ position: null,
267
+ error: null,
268
+ loading: enabled
269
+ });
270
+ const mountedRef = useRef(true);
271
+ // Identifies the in-flight request so a slow earlier fix cannot overwrite the
272
+ // result of a later refresh().
273
+ const requestRef = useRef(0);
274
+ useEffect(() => {
275
+ mountedRef.current = true;
276
+ return () => {
277
+ mountedRef.current = false;
278
+ };
279
+ }, []);
280
+
281
+ // Every dependency is a primitive read off `options`, so an inline options
282
+ // object (the normal call shape) does not re-create this on every render.
283
+ const run = useCallback(() => {
284
+ requestRef.current += 1;
285
+ const token = requestRef.current;
286
+ setState(prev => prev.loading ? prev : {
287
+ ...prev,
288
+ loading: true
289
+ });
290
+ const accept = next => {
291
+ if (mountedRef.current && requestRef.current === token) {
292
+ setState(next);
293
+ }
294
+ };
295
+ getCurrentPosition({
296
+ accuracy,
297
+ timeoutMs,
298
+ maximumAgeMs,
299
+ retries,
300
+ minimumAccuracyM,
301
+ retryDelayMs
302
+ }).then(position => accept({
303
+ position,
304
+ error: null,
305
+ loading: false
306
+ }), error => accept({
307
+ position: null,
308
+ error: toGeoError(error),
309
+ loading: false
310
+ }));
311
+ }, [accuracy, timeoutMs, maximumAgeMs, retries, minimumAccuracyM, retryDelayMs]);
312
+ useEffect(() => {
313
+ if (!enabled) {
314
+ // Discard any in-flight request so its result cannot land after opting out.
315
+ requestRef.current += 1;
316
+ setState(prev => prev.loading || prev.position !== null || prev.error !== null ? {
317
+ position: null,
318
+ error: null,
319
+ loading: false
320
+ } : prev);
321
+ return;
322
+ }
323
+ run();
324
+ }, [enabled, run]);
325
+ return {
326
+ position: state.position,
327
+ error: state.error,
328
+ loading: state.loading,
329
+ refresh: run
330
+ };
331
+ };
332
+ //# sourceMappingURL=index.js.map
@@ -1,28 +1,45 @@
1
1
  "use strict";
2
2
 
3
+ /**
4
+ * The main barrel deliberately does NOT re-export the modules that need a native
5
+ * package. Metro resolves requires statically, so anything reachable from here
6
+ * is resolved for every consumer, which would make an optional peer mandatory
7
+ * for anyone importing anything at all. Those live behind their own import
8
+ * paths instead:
9
+ *
10
+ * @webority-technologies/mobile-core/permissions react-native-permissions
11
+ * @webority-technologies/mobile-core/compressor react-native-compressor
12
+ * @webority-technologies/mobile-core/geolocation a geolocation provider
13
+ * @webority-technologies/mobile-core/sqlite a SQLite driver
14
+ * @webority-technologies/mobile-core/download a filesystem module
15
+ *
16
+ * react-native-keychain is the exception and is a REQUIRED peer: the HTTP client
17
+ * reads the bearer token, so it is reachable from the main barrel by design.
18
+ */
3
19
  // API clients
20
+ export { buildCurl, logCurl } from "./api/curl.js";
4
21
  export { default as publicClient } from "./api/publicClient.js";
5
- export { default as userClient, injectAuthActions, injectStore, RequestOfflineError, SESSION_TERMINATION_CODES } from "./api/userClient.js";
22
+ export { attachRetry, isRetryableStatus } from "./api/retry.js";
23
+ export { default as userClient, injectAuthActions, injectStore, RequestOfflineError, resetRefreshState, SESSION_TERMINATION_CODES } from "./api/userClient.js";
6
24
  // Auth / token storage
7
- export { getToken, removeToken, setKeychainImplementation, storeToken } from "./auth/authStore.js";
25
+ export { ACCESS_TOKEN_KEY, getToken, REFRESH_TOKEN_KEY, removeToken, setKeychainImplementation, storeToken } from "./auth/authStore.js";
8
26
  export { decodeJWT, getJWTExpiry, isJWTExpired } from "./auth/jwt.js";
9
27
  // Configuration
10
28
  export { getConfig, getConfigValue, resetConfig, setConfig } from "./config/index.js";
29
+ // Deep links
30
+ export { DeepLink, parseDeepLink, useDeepLink } from "./deepLink/index.js";
11
31
  // Formatters
12
- export { formatCompactNumber, formatCurrency, formatDate, formatDateTime, formatNumber, formatPercent, formatPhone, formatRelativeTime, formatTime, getInitials, normalizePhone } from "./formatters/index.js";
32
+ export { detectPhoneCountry, formatCompactNumber, formatCurrency, formatDate, formatDateTime, formatNumber, formatPercent, formatPhone, formatRelativeTime, formatTime, getInitials, normalizePhone } from "./formatters/index.js";
13
33
  // Initialization
14
34
  export { initWebority } from "./initSDK.js";
15
35
  export { initUserAuth } from "./initUserAuth.js";
16
36
  // Logger
17
- export { Logger, setRemoteLogger } from "./logger/index.js";
37
+ export { configureLogger, getDeviceContext, Logger, setDeviceContext, setRemoteLogger } from "./logger/index.js";
18
38
  // Network status (the NetworkStatusBanner UI lives in @webority-technologies/mobile-ui)
19
- export { addNetworkStatusListener, getNetworkStatus, useNetworkStatus } from "./network/index.js";
20
- // Permissions
21
- export { Permissions } from "./permissions/index.js";
22
- // Storage
23
- export { Storage, setStorageImplementation } from "./storage/index.js";
24
- // Media utils
25
- export { compressImage } from "./utils/index.js";
39
+ export { addNetworkStatusListener, getNetworkStatus, setNetworkStatusImplementation, useNetworkStatus } from "./network/index.js";
40
+ // Storage: the API and the seam. No native package is required; wire one with
41
+ // setStorageImplementation to get persistence.
42
+ export { isStorageAvailable, Storage, setStorageImplementation } from "./storage/index.js";
26
43
  // Validators
27
44
  export { isAadhaar, isAlphanumeric, isBetween, isEmail, isGstin, isHexColor, isIfsc, isIndianMobile, isIndianPincode, isInteger, isNonEmpty, isNumeric, isPan, isPhone, isStrongPassword, isUrl, maxLength, minLength } from "./validators/index.js";
28
45
  // Version check (the ForceUpdateDialog UI lives in @webority-technologies/mobile-ui)
@@ -5,7 +5,11 @@
5
5
  * One function to configure everything needed
6
6
  */
7
7
 
8
+ import { setKeychainImplementation } from "./auth/authStore.js";
8
9
  import { setConfig } from "./config/index.js";
10
+ import { Logger } from "./logger/index.js";
11
+ import { setNetworkStatusImplementation } from "./network/networkStatus.js";
12
+ import { setStorageImplementation } from "./storage/index.js";
9
13
  /**
10
14
  * Initialize Webority packages with your project configuration.
11
15
  *
@@ -33,8 +37,25 @@ export const initWebority = config => {
33
37
  loggingEnabled: config.loggingEnabled ?? true,
34
38
  logLevel: config.logLevel || 'info',
35
39
  environment: config.environment || 'Development',
36
- country: (config.country || 'in').toLowerCase()
40
+ country: (config.country || 'in').toLowerCase(),
41
+ appId: config.appId ?? null,
42
+ appVersion: config.appVersion ?? null,
43
+ logCurl: config.logCurl ?? false
37
44
  });
38
- console.log('✅ Webority packages initialized successfully');
45
+
46
+ // Only what the caller actually passed is wired. Calling a setter with
47
+ // `undefined` would reset a backend an app had already injected by hand,
48
+ // which is why each is guarded rather than passed straight through.
49
+ const backends = config.backends;
50
+ if (backends?.storage) {
51
+ setStorageImplementation(backends.storage);
52
+ }
53
+ if (backends?.keychain) {
54
+ setKeychainImplementation(backends.keychain);
55
+ }
56
+ if (backends?.network) {
57
+ setNetworkStatusImplementation(backends.network);
58
+ }
59
+ Logger.info('[@webority-technologies/mobile-core] initialized');
39
60
  };
40
61
  //# sourceMappingURL=initSDK.js.map
@@ -6,6 +6,7 @@
6
6
  */
7
7
 
8
8
  import { injectAuthActions, injectStore } from "./api/userClient.js";
9
+ import { Logger } from "./logger/index.js";
9
10
 
10
11
  /**
11
12
  * Auth action creators required by the user package.
@@ -29,6 +30,6 @@ export const initUserAuth = (store, authActions) => {
29
30
  }
30
31
  injectStore(store);
31
32
  injectAuthActions(authActions);
32
- console.log(' Webority User package initialized with Redux');
33
+ Logger.info('[@webority-technologies/mobile-core] user auth wired to the store');
33
34
  };
34
35
  //# sourceMappingURL=initUserAuth.js.map
@@ -72,6 +72,137 @@ const toErrorObject = input => {
72
72
  return new Error(String(input));
73
73
  }
74
74
  };
75
+
76
+ /**
77
+ * Device / app context attached once at boot (platform, OS version, app version,
78
+ * build number, device model, etc). Merged into every `recordError` context so
79
+ * every crash carries it without every call site repeating it.
80
+ */
81
+
82
+ let deviceContext = null;
83
+ const normalizeAttributeValue = value => {
84
+ if (value === null || value === undefined) {
85
+ return null;
86
+ }
87
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
88
+ return value;
89
+ }
90
+ return String(value);
91
+ };
92
+
93
+ /**
94
+ * Set (or clear, with `null`) the device/app context merged into every
95
+ * `recordError` call. Each key is also forwarded to `remote.setAttribute`
96
+ * once, at the moment it is set.
97
+ */
98
+ export const setDeviceContext = context => {
99
+ deviceContext = context;
100
+ if (context) {
101
+ for (const [key, value] of Object.entries(context)) {
102
+ safe(() => remote?.setAttribute?.(key, normalizeAttributeValue(value)));
103
+ }
104
+ }
105
+ };
106
+ export const getDeviceContext = () => deviceContext;
107
+
108
+ /** Tuning knobs for screen history and duplicate-error suppression. */
109
+
110
+ const DEFAULT_LOGGER_OPTIONS = {
111
+ dedupeWindowMs: 5000,
112
+ dedupeMaxPerWindow: 1,
113
+ screenHistoryLimit: 20
114
+ };
115
+ let loggerOptions = {
116
+ ...DEFAULT_LOGGER_OPTIONS
117
+ };
118
+
119
+ /** Configure dedupe window/threshold and screen history size. Merges with current options. */
120
+ export const configureLogger = options => {
121
+ loggerOptions = {
122
+ ...loggerOptions,
123
+ ...options
124
+ };
125
+ if (screenHistory.length > loggerOptions.screenHistoryLimit) {
126
+ screenHistory = screenHistory.slice(screenHistory.length - loggerOptions.screenHistoryLimit);
127
+ }
128
+ };
129
+ let currentScreen = null;
130
+ let screenHistory = [];
131
+ const dedupeMap = new Map();
132
+ const MAX_DEDUPE_ENTRIES = 200;
133
+ const dedupeKeyFor = err => {
134
+ const firstFrame = err.stack?.split('\n')[1]?.trim();
135
+ return firstFrame ? `${err.message}::${firstFrame}` : err.message;
136
+ };
137
+ const capDedupeMap = () => {
138
+ while (dedupeMap.size > MAX_DEDUPE_ENTRIES) {
139
+ const oldestKey = dedupeMap.keys().next().value;
140
+ if (oldestKey === undefined) {
141
+ return;
142
+ }
143
+ dedupeMap.delete(oldestKey);
144
+ }
145
+ };
146
+ const shouldForward = key => {
147
+ if (loggerOptions.dedupeWindowMs <= 0) {
148
+ return {
149
+ forward: true
150
+ };
151
+ }
152
+ const now = Date.now();
153
+ const entry = dedupeMap.get(key);
154
+ if (!entry || now - entry.windowStart >= loggerOptions.dedupeWindowMs) {
155
+ const suppressedCount = entry?.suppressedCount ?? 0;
156
+ dedupeMap.set(key, {
157
+ windowStart: now,
158
+ count: 1,
159
+ suppressedCount: 0
160
+ });
161
+ capDedupeMap();
162
+ return suppressedCount > 0 ? {
163
+ forward: true,
164
+ suppressedCount
165
+ } : {
166
+ forward: true
167
+ };
168
+ }
169
+ entry.count += 1;
170
+ if (entry.count > loggerOptions.dedupeMaxPerWindow) {
171
+ entry.suppressedCount += 1;
172
+ return {
173
+ forward: false
174
+ };
175
+ }
176
+ return {
177
+ forward: true
178
+ };
179
+ };
180
+ const buildErrorContext = (context, suppressedCount) => {
181
+ const merged = {
182
+ ...deviceContext
183
+ };
184
+ if (currentScreen !== null) {
185
+ merged.screen = currentScreen;
186
+ }
187
+ if (suppressedCount) {
188
+ merged.suppressedCount = suppressedCount;
189
+ }
190
+ return {
191
+ ...merged,
192
+ ...context
193
+ };
194
+ };
195
+ const forwardErrorToRemote = (err, context) => {
196
+ const key = dedupeKeyFor(err);
197
+ const {
198
+ forward,
199
+ suppressedCount
200
+ } = shouldForward(key);
201
+ if (!forward) {
202
+ return;
203
+ }
204
+ safe(() => remote?.recordError?.(err, buildErrorContext(context, suppressedCount)));
205
+ };
75
206
  const logInfo = (message, ...optionalParams) => {
76
207
  if (!isEnabled() || !isLogLevelAllowed(LogLevels.INFO.level)) {
77
208
  return;
@@ -96,10 +227,10 @@ const logError = (message, ...optionalParams) => {
96
227
  // First optional param is treated as the underlying error if it looks like one.
97
228
  const candidate = optionalParams[0];
98
229
  if (candidate !== undefined) {
99
- safe(() => remote?.recordError?.(toErrorObject(candidate), {
230
+ forwardErrorToRemote(toErrorObject(candidate), {
100
231
  source: 'Logger.error',
101
232
  message
102
- }));
233
+ });
103
234
  }
104
235
  };
105
236
  const breadcrumb = (message, data) => {
@@ -113,7 +244,7 @@ const recordError = (error, context) => {
113
244
  if (isEnabled() && isLogLevelAllowed('error')) {
114
245
  console.error(`${Colors.RED_BOLD}[RECORDED]: ${err.message}${Colors.RESET}`, context ?? '');
115
246
  }
116
- safe(() => remote?.recordError?.(err, context));
247
+ forwardErrorToRemote(err, context);
117
248
  };
118
249
  const setUser = (id, attrs) => {
119
250
  safe(() => remote?.setUser?.(id, attrs));
@@ -121,6 +252,24 @@ const setUser = (id, attrs) => {
121
252
  const setAttribute = (key, value) => {
122
253
  safe(() => remote?.setAttribute?.(key, value));
123
254
  };
255
+
256
+ /**
257
+ * Record the active screen as a breadcrumb and remember it for subsequent
258
+ * `recordError` context. Re-tracking the same screen name is a no-op.
259
+ */
260
+ const trackScreen = (name, params) => {
261
+ if (name === currentScreen) {
262
+ return;
263
+ }
264
+ currentScreen = name;
265
+ screenHistory.push(name);
266
+ if (screenHistory.length > loggerOptions.screenHistoryLimit) {
267
+ screenHistory = screenHistory.slice(screenHistory.length - loggerOptions.screenHistoryLimit);
268
+ }
269
+ breadcrumb(`Screen: ${name}`, params);
270
+ };
271
+ const getCurrentScreen = () => currentScreen;
272
+ const getScreenHistory = () => [...screenHistory];
124
273
  export const Logger = {
125
274
  info: logInfo,
126
275
  warn: logWarning,
@@ -128,6 +277,9 @@ export const Logger = {
128
277
  breadcrumb,
129
278
  recordError,
130
279
  setUser,
131
- setAttribute
280
+ setAttribute,
281
+ trackScreen,
282
+ getCurrentScreen,
283
+ getScreenHistory
132
284
  };
133
285
  //# sourceMappingURL=index.js.map
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
 
3
- export { addNetworkStatusListener, getNetworkStatus, useNetworkStatus } from "./networkStatus.js";
3
+ export { addNetworkStatusListener, getNetworkStatus, setNetworkStatusImplementation, useNetworkStatus } from "./networkStatus.js";
4
4
  //# sourceMappingURL=index.js.map