@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,64 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * expo-file-system. Shape verified against 57.0.6, where the legacy function
5
+ * API (`downloadAsync`, `getInfoAsync`) still exists on the main entry point
6
+ * but every one of those functions THROWS at runtime; they now live behind
7
+ * `expo-file-system/legacy`. This adapter therefore uses the class API only.
8
+ *
9
+ * The REQUIRE lives in the entry point beside this file, never here.
10
+ */
11
+
12
+ /**
13
+ * expo reports a directory as a URI with a trailing slash, and the download
14
+ * module joins a directory to a file name with a `/` of its own.
15
+ */
16
+ const asDirectoryPath = uri => uri.replace(/\/+$/, '');
17
+ export const adaptExpoFileSystem = mod => {
18
+ const lib = mod?.default ?? mod;
19
+ if (typeof lib?.File !== 'function' || typeof lib.File.prototype?.write !== 'function') {
20
+ return null;
21
+ }
22
+ const {
23
+ File,
24
+ Directory,
25
+ Paths
26
+ } = lib;
27
+ const documents = asDirectoryPath(Paths.document.uri);
28
+ return {
29
+ dirs: {
30
+ documents,
31
+ cache: asDirectoryPath(Paths.cache.uri),
32
+ // expo exposes no public Downloads directory, so a "downloads" request
33
+ // lands in the app's documents directory instead of the shared folder.
34
+ downloads: documents
35
+ },
36
+ write: (path, bytes) => {
37
+ const file = new File(path);
38
+ file.create({
39
+ intermediates: true,
40
+ overwrite: true
41
+ });
42
+ file.write(bytes);
43
+ return Promise.resolve();
44
+ },
45
+ exists: path => Promise.resolve(new File(path).exists),
46
+ stat: path => Promise.resolve({
47
+ size: new File(path).size
48
+ }),
49
+ unlink: path => {
50
+ new File(path).delete();
51
+ return Promise.resolve();
52
+ },
53
+ // The download module calls this for a directory that usually already
54
+ // exists, so it must be idempotent rather than throw on the second run.
55
+ mkdir: path => {
56
+ new Directory(path).create({
57
+ intermediates: true,
58
+ idempotent: true
59
+ });
60
+ return Promise.resolve();
61
+ }
62
+ };
63
+ };
64
+ //# sourceMappingURL=expoFs.js.map
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+
3
+ /** Coerce a driver-supplied size, which some report as a string. */
4
+ export const asNumber = value => {
5
+ const n = typeof value === 'number' ? value : Number(value);
6
+ return Number.isFinite(n) ? n : 0;
7
+ };
8
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1,242 @@
1
+ "use strict";
2
+
3
+ import * as ExpoFileSystem from 'expo-file-system';
4
+ import { Platform } from 'react-native';
5
+ import { Logger } from "../logger/index.js";
6
+ import { Permissions } from "../permissions/index.js";
7
+ import { adaptExpoFileSystem } from "./adapters/expoFs.js";
8
+ import { asNumber } from "./adapters/shared.js";
9
+ const PREFIX = '[@webority/mobile-core]';
10
+
11
+ /** Android stopped requiring a storage permission for a Downloads write at API 29. */
12
+ const SCOPED_STORAGE_API_LEVEL = 29;
13
+
14
+ /** A file can take far longer than the 30 s the fleet's JSON clients allow. */
15
+ const DOWNLOAD_TIMEOUT_MS = 300000;
16
+
17
+ /**
18
+ * The slice of a filesystem module this module actually uses. expo-file-system
19
+ * is adapted onto it, and a test or the showcase can inject its own with
20
+ * `setFileSystemImplementation`.
21
+ */
22
+
23
+ /**
24
+ * The slice of an HTTP client a download needs. The app's axios instance
25
+ * (mobile-core's `userClient` or `publicClient`, or its own) satisfies it as is,
26
+ * so whatever that client adds to a request (bearer token, tenant header, refresh
27
+ * and retry on 401) applies to the download too. Declared structurally, as
28
+ * mobile-support's client is, so no axios type leaks into the declarations.
29
+ */
30
+
31
+ export class DownloadError extends Error {
32
+ constructor(code, message, status) {
33
+ super(`${PREFIX} ${message}`);
34
+ this.name = 'DownloadError';
35
+ this.code = code;
36
+ this.status = status;
37
+ // Babel's class transform can drop the prototype link, which makes
38
+ // `instanceof DownloadError` false in the transpiled build.
39
+ Object.setPrototypeOf(this, DownloadError.prototype);
40
+ }
41
+ }
42
+ const describe = error => error instanceof Error ? error.message : String(error);
43
+
44
+ /** Only what a test or the showcase injected; null means use expo-file-system. */
45
+ let backend = null;
46
+
47
+ /**
48
+ * expo-file-system, adapted once and memoised. The adapter is shape-checked, so
49
+ * a build where the native module did not link returns null and the guided
50
+ * error below still fires rather than a confusing property access.
51
+ */
52
+ let expoBackend;
53
+ const resolveBackend = () => {
54
+ if (backend) {
55
+ return backend;
56
+ }
57
+ if (expoBackend === undefined) {
58
+ expoBackend = adaptExpoFileSystem(ExpoFileSystem);
59
+ }
60
+ return expoBackend;
61
+ };
62
+
63
+ /**
64
+ * Replace the filesystem backend. Intended for TESTS and for the showcase,
65
+ * which injects a denying backend to demo the permission error; an app gets
66
+ * expo-file-system without wiring anything. Pass `null` to restore it.
67
+ */
68
+ export const setFileSystemImplementation = impl => {
69
+ backend = impl;
70
+ };
71
+ const requireBackend = () => {
72
+ const fs = resolveBackend();
73
+ if (!fs) {
74
+ throw new DownloadError('unsupported', 'No filesystem module is available, so files cannot be downloaded. ' + 'expo-file-system did not load, which on a real build means the native module ' + 'was not linked.');
75
+ }
76
+ return fs;
77
+ };
78
+ const stripControlChars = value => Array.from(value).filter(char => (char.codePointAt(0) ?? 0) > 0x1f).join('');
79
+ const lastSegment = raw => {
80
+ const segments = raw.split(/[/\\]/);
81
+ return stripControlChars(segments[segments.length - 1] ?? '').trim();
82
+ };
83
+
84
+ /**
85
+ * Reduce a caller- or server-supplied name to a single path segment: `../../evil.pdf`
86
+ * must land inside the destination directory as `evil.pdf` rather than escaping it.
87
+ */
88
+ const sanitiseFileName = raw => {
89
+ const base = lastSegment(raw);
90
+ if (base === '' || base === '.' || base === '..') {
91
+ throw new DownloadError('write-failed', `"${raw}" does not contain a usable file name. Pass an explicit fileName.`);
92
+ }
93
+ return base;
94
+ };
95
+ const fileNameFromUrl = url => {
96
+ const withoutQuery = url.split('#')[0]?.split('?')[0] ?? '';
97
+ const last = withoutQuery.split('/').pop() ?? '';
98
+ let decoded = last;
99
+ try {
100
+ decoded = decodeURIComponent(last);
101
+ } catch {
102
+ // A malformed escape sequence is not a reason to fail the download.
103
+ }
104
+ const base = lastSegment(decoded);
105
+ return base === '' || base === '.' || base === '..' ? 'download' : base;
106
+ };
107
+ const androidApiLevel = () => typeof Platform.Version === 'number' ? Platform.Version : Number.parseInt(String(Platform.Version), 10);
108
+ const ensureStoragePermission = async destination => {
109
+ if (Platform.OS !== 'android' || destination !== 'downloads') {
110
+ return;
111
+ }
112
+ if (androidApiLevel() >= SCOPED_STORAGE_API_LEVEL) {
113
+ return;
114
+ }
115
+ const status = await Permissions.ensure('storage');
116
+ if (status === 'granted' || status === 'limited') {
117
+ return;
118
+ }
119
+ throw new DownloadError('permission-denied', `Storage permission is "${status}", so nothing can be written to the Downloads folder ` + 'on this Android version. Ask the user to grant it (Permissions.openSettings()), or ' + 'download to the "documents" destination instead.');
120
+ };
121
+ const emptyFile = url => new DownloadError('empty-file', `The download of ${url} produced an empty file, so nothing was saved. ` + 'Check that the URL is correct and that the request carried the credentials the ' + 'server expects.');
122
+ const requestFailed = (error, options, fileName) => {
123
+ if (options.signal?.aborted) {
124
+ return new DownloadError('aborted', `The download of ${fileName} was aborted.`);
125
+ }
126
+ const e = error ?? {};
127
+ // By name, not instanceof: a second copy of mobile-core would fail instanceof.
128
+ if (e.name === 'RequestOfflineError') {
129
+ return new DownloadError('network', `The device is offline, so ${options.url} was not downloaded.`);
130
+ }
131
+ const status = e.response?.status;
132
+ if (typeof status === 'number') {
133
+ return new DownloadError('http-error', `The server answered ${status} for ${options.url}, so no file was saved.`, status);
134
+ }
135
+ return new DownloadError('network', `The download of ${options.url} failed: ${describe(error)}`);
136
+ };
137
+ const discardPartial = async (fs, path) => {
138
+ try {
139
+ if (await fs.exists(path)) {
140
+ await fs.unlink(path);
141
+ }
142
+ } catch (error) {
143
+ Logger.warn(`${PREFIX} Failed to remove the partial download at ${path}`, error);
144
+ }
145
+ };
146
+
147
+ /**
148
+ * Download a file to the device through the app's own HTTP client, so it is
149
+ * authenticated exactly as the app's other calls are. The whole file is held
150
+ * in memory before it is written, which suits documents and reports rather
151
+ * than large media.
152
+ *
153
+ * A finished download is always verified to be non-empty: a 0-byte file looks
154
+ * like a success to every caller and then wedges whatever consumes it, so it is
155
+ * reported as `empty-file` and nothing is kept.
156
+ */
157
+ export const downloadFile = async options => {
158
+ const fs = requireBackend();
159
+ const destination = options.destination ?? 'documents';
160
+ const fileName = options.fileName ? sanitiseFileName(options.fileName) : fileNameFromUrl(options.url);
161
+ await ensureStoragePermission(destination);
162
+ const directory = fs.dirs[destination];
163
+ const path = `${directory}/${fileName}`;
164
+ if (options.signal?.aborted) {
165
+ throw new DownloadError('aborted', `The download of ${fileName} was aborted before it started.`);
166
+ }
167
+ let reuseExisting = false;
168
+ try {
169
+ await fs.mkdir(directory);
170
+ const exists = await fs.exists(path);
171
+ if (exists && options.overwrite === false) {
172
+ reuseExisting = true;
173
+ } else if (exists) {
174
+ await fs.unlink(path);
175
+ }
176
+ } catch (error) {
177
+ throw new DownloadError('write-failed', `Could not prepare ${path} for writing: ${describe(error)}`);
178
+ }
179
+ if (reuseExisting) {
180
+ const stat = await fs.stat(path);
181
+ return {
182
+ path,
183
+ bytes: asNumber(stat?.size),
184
+ mimeType: options.mimeType,
185
+ fromCache: true
186
+ };
187
+ }
188
+ const {
189
+ onProgress
190
+ } = options;
191
+ let data;
192
+ try {
193
+ ({
194
+ data
195
+ } = await options.client.get(options.url, {
196
+ headers: {
197
+ Accept: '*/*',
198
+ ...options.headers
199
+ },
200
+ responseType: 'arraybuffer',
201
+ timeout: DOWNLOAD_TIMEOUT_MS,
202
+ signal: options.signal,
203
+ onDownloadProgress: onProgress ? ({
204
+ loaded,
205
+ total = 0
206
+ }) => onProgress(loaded, total, total > 0 ? loaded / total : 0) : undefined
207
+ }));
208
+ } catch (error) {
209
+ throw requestFailed(error, options, fileName);
210
+ }
211
+ if (options.signal?.aborted) {
212
+ throw new DownloadError('aborted', `The download of ${fileName} was aborted.`);
213
+ }
214
+ const received = new Uint8Array(data ?? new ArrayBuffer(0));
215
+ if (received.byteLength === 0) {
216
+ throw emptyFile(options.url);
217
+ }
218
+ try {
219
+ await fs.write(path, received);
220
+ } catch (error) {
221
+ await discardPartial(fs, path);
222
+ throw new DownloadError('write-failed', `Could not write ${path}: ${describe(error)}`);
223
+ }
224
+ let bytes = 0;
225
+ try {
226
+ bytes = (await fs.exists(path)) ? asNumber((await fs.stat(path)).size) : 0;
227
+ } catch (error) {
228
+ await discardPartial(fs, path);
229
+ throw new DownloadError('write-failed', `The download of ${fileName} finished but ${path} could not be read back: ${describe(error)}`);
230
+ }
231
+ if (bytes <= 0) {
232
+ await discardPartial(fs, path);
233
+ throw emptyFile(options.url);
234
+ }
235
+ return {
236
+ path,
237
+ bytes,
238
+ mimeType: options.mimeType,
239
+ fromCache: false
240
+ };
241
+ };
242
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+
3
+ const hasIntlNumber = () => typeof Intl !== 'undefined' && typeof Intl.NumberFormat === 'function';
4
+ const indianGroupingFallback = (n, fractionDigits) => {
5
+ // Indian numbering: 1,23,45,678.90 (last 3 digits, then groups of 2).
6
+ const sign = n < 0 ? '-' : '';
7
+ const abs = Math.abs(n);
8
+ const fixed = abs.toFixed(fractionDigits);
9
+ const [intPart, decPart] = fixed.split('.');
10
+ const last3 = intPart.length > 3 ? intPart.slice(-3) : intPart;
11
+ const rest = intPart.length > 3 ? intPart.slice(0, -3) : '';
12
+ const grouped = rest ? rest.replace(/\B(?=(\d{2})+(?!\d))/g, ',') + ',' + last3 : last3;
13
+ return sign + grouped + (decPart ? `.${decPart}` : '');
14
+ };
15
+
16
+ /**
17
+ * Format an amount as currency. Defaults to **INR** with Indian numbering (`en-IN`),
18
+ * which matches the standard convention across Webority projects.
19
+ *
20
+ * @example
21
+ * formatCurrency(150000) // ₹1,50,000
22
+ * formatCurrency(1234.5) // ₹1,234.50
23
+ * formatCurrency(99, { currency: 'USD', locale: 'en-US' }) // $99.00
24
+ * formatCurrency(150000, { stripSymbol: true }) // 1,50,000
25
+ */
26
+ export const formatCurrency = (amount, options = {}) => {
27
+ if (amount == null || Number.isNaN(amount)) {
28
+ return '';
29
+ }
30
+ const {
31
+ currency = 'INR',
32
+ locale = 'en-IN',
33
+ minimumFractionDigits,
34
+ maximumFractionDigits = 2,
35
+ stripSymbol = false
36
+ } = options;
37
+ const minFrac = minimumFractionDigits ?? (Number.isInteger(amount) ? 0 : 2);
38
+ if (hasIntlNumber()) {
39
+ try {
40
+ const formatted = new Intl.NumberFormat(locale, {
41
+ style: 'currency',
42
+ currency,
43
+ minimumFractionDigits: minFrac,
44
+ maximumFractionDigits
45
+ }).format(amount);
46
+ if (!stripSymbol) {
47
+ return formatted;
48
+ }
49
+ // Strip currency code/symbol while keeping digits, separators and sign.
50
+ return formatted.replace(/[^\d,.\s\-+]/g, '').trim();
51
+ } catch {
52
+ /* fall through to fallback */
53
+ }
54
+ }
55
+ const numeric = indianGroupingFallback(amount, minFrac);
56
+ if (stripSymbol) {
57
+ return numeric;
58
+ }
59
+ if (currency === 'INR') {
60
+ return `₹${numeric}`;
61
+ }
62
+ return `${currency} ${numeric}`;
63
+ };
64
+ //# sourceMappingURL=currency.js.map
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+
3
+ const toDate = value => {
4
+ if (value instanceof Date) {
5
+ return Number.isNaN(value.getTime()) ? null : value;
6
+ }
7
+ const d = new Date(value);
8
+ return Number.isNaN(d.getTime()) ? null : d;
9
+ };
10
+ const hasIntl = () => typeof Intl !== 'undefined' && typeof Intl.DateTimeFormat === 'function';
11
+ const PAD = n => n < 10 ? `0${n}` : `${n}`;
12
+ const fallbackDate = (d, style) => {
13
+ const day = PAD(d.getDate());
14
+ const monthShort = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][d.getMonth()];
15
+ const monthLong = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'][d.getMonth()];
16
+ const year = d.getFullYear();
17
+ const weekdayLong = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][d.getDay()];
18
+ switch (style) {
19
+ case 'short':
20
+ return `${day}/${PAD(d.getMonth() + 1)}/${year}`;
21
+ case 'long':
22
+ return `${day} ${monthLong} ${year}`;
23
+ case 'full':
24
+ return `${weekdayLong}, ${day} ${monthLong} ${year}`;
25
+ case 'medium':
26
+ default:
27
+ return `${day} ${monthShort} ${year}`;
28
+ }
29
+ };
30
+ const fallbackTime = (d, style) => {
31
+ const hh = PAD(d.getHours());
32
+ const mm = PAD(d.getMinutes());
33
+ if (style === 'short') {
34
+ return `${hh}:${mm}`;
35
+ }
36
+ const ss = PAD(d.getSeconds());
37
+ return `${hh}:${mm}:${ss}`;
38
+ };
39
+
40
+ /**
41
+ * Locale-aware date formatter. Defaults to `'medium'` style and `'en-IN'` locale,
42
+ * matching the Indian-locale convention used across Webority apps.
43
+ */
44
+ export const formatDate = (value, style = 'medium', locale = 'en-IN') => {
45
+ const d = toDate(value);
46
+ if (!d) {
47
+ return '';
48
+ }
49
+ if (hasIntl()) {
50
+ try {
51
+ return new Intl.DateTimeFormat(locale, {
52
+ dateStyle: style
53
+ }).format(d);
54
+ } catch {
55
+ /* fall through to fallback */
56
+ }
57
+ }
58
+ return fallbackDate(d, style);
59
+ };
60
+
61
+ /**
62
+ * Locale-aware time formatter (24-hour fallback when Intl is unavailable).
63
+ */
64
+ export const formatTime = (value, style = 'short', locale = 'en-IN') => {
65
+ const d = toDate(value);
66
+ if (!d) {
67
+ return '';
68
+ }
69
+ if (hasIntl()) {
70
+ try {
71
+ return new Intl.DateTimeFormat(locale, {
72
+ timeStyle: style
73
+ }).format(d);
74
+ } catch {
75
+ /* fall through */
76
+ }
77
+ }
78
+ return fallbackTime(d, style);
79
+ };
80
+
81
+ /**
82
+ * Combined date + time formatter.
83
+ */
84
+ export const formatDateTime = (value, dateStyle = 'medium', timeStyle = 'short', locale = 'en-IN') => {
85
+ const d = toDate(value);
86
+ if (!d) {
87
+ return '';
88
+ }
89
+ if (hasIntl()) {
90
+ try {
91
+ return new Intl.DateTimeFormat(locale, {
92
+ dateStyle,
93
+ timeStyle
94
+ }).format(d);
95
+ } catch {
96
+ /* fall through */
97
+ }
98
+ }
99
+ return `${fallbackDate(d, dateStyle)}, ${fallbackTime(d, timeStyle)}`;
100
+ };
101
+ const SECOND = 1000;
102
+ const MINUTE = 60 * SECOND;
103
+ const HOUR = 60 * MINUTE;
104
+ const DAY = 24 * HOUR;
105
+ const WEEK = 7 * DAY;
106
+ const MONTH = 30 * DAY;
107
+ const YEAR = 365 * DAY;
108
+
109
+ /**
110
+ * Returns a humanized relative-time string (e.g. "5 minutes ago", "in 3 days").
111
+ * Uses `Intl.RelativeTimeFormat` when available; otherwise a sensible English fallback.
112
+ */
113
+ export const formatRelativeTime = (value, base = new Date(), locale = 'en-IN') => {
114
+ const target = toDate(value);
115
+ const now = toDate(base);
116
+ if (!target || !now) {
117
+ return '';
118
+ }
119
+ const diffMs = target.getTime() - now.getTime();
120
+ const abs = Math.abs(diffMs);
121
+ let unit;
122
+ let amount;
123
+ if (abs < MINUTE) {
124
+ unit = 'second';
125
+ amount = Math.round(diffMs / SECOND);
126
+ } else if (abs < HOUR) {
127
+ unit = 'minute';
128
+ amount = Math.round(diffMs / MINUTE);
129
+ } else if (abs < DAY) {
130
+ unit = 'hour';
131
+ amount = Math.round(diffMs / HOUR);
132
+ } else if (abs < WEEK) {
133
+ unit = 'day';
134
+ amount = Math.round(diffMs / DAY);
135
+ } else if (abs < MONTH) {
136
+ unit = 'week';
137
+ amount = Math.round(diffMs / WEEK);
138
+ } else if (abs < YEAR) {
139
+ unit = 'month';
140
+ amount = Math.round(diffMs / MONTH);
141
+ } else {
142
+ unit = 'year';
143
+ amount = Math.round(diffMs / YEAR);
144
+ }
145
+ const Rtf = hasIntl() ? Intl.RelativeTimeFormat : undefined;
146
+ if (typeof Rtf === 'function') {
147
+ try {
148
+ return new Rtf(locale, {
149
+ numeric: 'auto'
150
+ }).format(amount, unit);
151
+ } catch {
152
+ /* fall through */
153
+ }
154
+ }
155
+ const past = amount < 0;
156
+ const n = Math.abs(amount);
157
+ const u = n === 1 ? unit : `${unit}s`;
158
+ return past ? `${n} ${u} ago` : `in ${n} ${u}`;
159
+ };
160
+ //# sourceMappingURL=date.js.map