@webority/mobile-core 0.0.28 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/api/baseUrl.js +1 -34
- package/lib/commonjs/api/curl.js +1 -109
- package/lib/commonjs/api/multipart.js +1 -21
- package/lib/commonjs/api/publicClient.js +1 -79
- package/lib/commonjs/api/responseBodyText.js +1 -0
- package/lib/commonjs/api/retry.js +1 -91
- package/lib/commonjs/api/tokenRefresh.js +1 -205
- package/lib/commonjs/api/userClient.js +1 -265
- package/lib/commonjs/auth/authStore.js +1 -265
- package/lib/commonjs/auth/index.js +1 -44
- package/lib/commonjs/auth/jwt.js +1 -112
- package/lib/commonjs/config/index.js +1 -72
- package/lib/commonjs/deepLink/index.js +1 -286
- package/lib/commonjs/download/adapters/expoFs.js +1 -69
- package/lib/commonjs/download/adapters/shared.js +1 -13
- package/lib/commonjs/download/index.js +1 -250
- package/lib/commonjs/formatters/currency.js +1 -70
- package/lib/commonjs/formatters/date.js +1 -167
- package/lib/commonjs/formatters/dateMath.js +1 -311
- package/lib/commonjs/formatters/index.js +1 -246
- package/lib/commonjs/formatters/initials.js +1 -32
- package/lib/commonjs/formatters/intlCache.js +1 -37
- package/lib/commonjs/formatters/number.js +1 -89
- package/lib/commonjs/formatters/phone.js +1 -369
- package/lib/commonjs/formatters/windowsToIanaTable.js +1 -164
- package/lib/commonjs/geolocation/adapters/expoLocation.js +1 -126
- package/lib/commonjs/geolocation/index.js +1 -355
- package/lib/commonjs/index.js +1 -644
- package/lib/commonjs/initSDK.js +1 -66
- package/lib/commonjs/initUserAuth.js +1 -37
- package/lib/commonjs/logger/crashlytics.js +1 -0
- package/lib/commonjs/logger/index.js +1 -301
- package/lib/commonjs/logger/redact.js +1 -50
- package/lib/commonjs/network/index.js +1 -31
- package/lib/commonjs/network/networkStatus.js +1 -171
- package/lib/commonjs/permissions/calendar.js +1 -14
- package/lib/commonjs/permissions/camera.js +1 -14
- package/lib/commonjs/permissions/contacts.js +1 -10
- package/lib/commonjs/permissions/index.js +1 -133
- package/lib/commonjs/permissions/location.js +1 -14
- package/lib/commonjs/permissions/mediaLibrary.js +1 -10
- package/lib/commonjs/permissions/notifications.js +1 -10
- package/lib/commonjs/permissions/photos.js +1 -14
- package/lib/commonjs/phone/countries.js +1 -265
- package/lib/commonjs/push/index.js +1 -61
- package/lib/commonjs/push/push.js +1 -443
- package/lib/commonjs/push/status.js +1 -0
- package/lib/commonjs/push/task.js +1 -43
- package/lib/commonjs/secureScreen/index.js +1 -0
- package/lib/commonjs/share/index.js +1 -70
- package/lib/commonjs/smsRetriever/index.js +1 -91
- package/lib/commonjs/specs/NativeSmsRetriever.js +1 -15
- package/lib/commonjs/sqlite/adapters/expo.js +1 -53
- package/lib/commonjs/sqlite/index.js +1 -604
- package/lib/commonjs/storage/index.js +1 -214
- package/lib/commonjs/types/axios.js +1 -4
- package/lib/commonjs/types/globals.d.js +1 -6
- package/lib/commonjs/utils/base64.js +1 -24
- package/lib/commonjs/utils/imageCompression.js +1 -389
- package/lib/commonjs/utils/index.js +1 -19
- package/lib/commonjs/validators/index.js +1 -227
- package/lib/commonjs/versionCheck/index.js +1 -419
- package/lib/module/api/baseUrl.js +1 -30
- package/lib/module/api/curl.js +1 -104
- package/lib/module/api/multipart.js +1 -16
- package/lib/module/api/publicClient.js +1 -75
- package/lib/module/api/responseBodyText.js +1 -0
- package/lib/module/api/retry.js +1 -85
- package/lib/module/api/tokenRefresh.js +1 -192
- package/lib/module/api/userClient.js +1 -243
- package/lib/module/auth/authStore.js +1 -257
- package/lib/module/auth/index.js +1 -5
- package/lib/module/auth/jwt.js +1 -106
- package/lib/module/config/index.js +1 -64
- package/lib/module/deepLink/index.js +1 -280
- package/lib/module/download/adapters/expoFs.js +1 -64
- package/lib/module/download/adapters/shared.js +1 -8
- package/lib/module/download/index.js +1 -242
- package/lib/module/formatters/currency.js +1 -65
- package/lib/module/formatters/date.js +1 -159
- package/lib/module/formatters/dateMath.js +1 -295
- package/lib/module/formatters/index.js +1 -9
- package/lib/module/formatters/initials.js +1 -27
- package/lib/module/formatters/intlCache.js +1 -30
- package/lib/module/formatters/number.js +1 -82
- package/lib/module/formatters/phone.js +1 -302
- package/lib/module/formatters/windowsToIanaTable.js +1 -160
- package/lib/module/geolocation/adapters/expoLocation.js +1 -121
- package/lib/module/geolocation/index.js +1 -347
- package/lib/module/index.js +1 -57
- package/lib/module/initSDK.js +1 -61
- package/lib/module/initUserAuth.js +1 -33
- package/lib/module/logger/crashlytics.js +1 -0
- package/lib/module/logger/index.js +1 -295
- package/lib/module/logger/redact.js +1 -44
- package/lib/module/network/index.js +1 -4
- package/lib/module/network/networkStatus.js +1 -161
- package/lib/module/permissions/calendar.js +1 -15
- package/lib/module/permissions/camera.js +1 -15
- package/lib/module/permissions/contacts.js +1 -11
- package/lib/module/permissions/index.js +1 -128
- package/lib/module/permissions/location.js +1 -15
- package/lib/module/permissions/mediaLibrary.js +1 -11
- package/lib/module/permissions/notifications.js +1 -11
- package/lib/module/permissions/photos.js +1 -15
- package/lib/module/phone/countries.js +1 -261
- package/lib/module/push/index.js +1 -4
- package/lib/module/push/push.js +1 -427
- package/lib/module/push/status.js +1 -0
- package/lib/module/push/task.js +1 -38
- package/lib/module/secureScreen/index.js +1 -0
- package/lib/module/share/index.js +1 -64
- package/lib/module/smsRetriever/index.js +1 -86
- package/lib/module/specs/NativeSmsRetriever.js +1 -13
- package/lib/module/sqlite/adapters/expo.js +1 -48
- package/lib/module/sqlite/index.js +1 -594
- package/lib/module/storage/index.js +1 -208
- package/lib/module/types/axios.js +1 -4
- package/lib/module/types/globals.d.js +1 -10
- package/lib/module/utils/base64.js +1 -19
- package/lib/module/utils/imageCompression.js +1 -383
- package/lib/module/utils/index.js +1 -4
- package/lib/module/validators/index.js +1 -205
- package/lib/module/versionCheck/index.js +1 -413
- package/lib/typescript/commonjs/api/responseBodyText.d.ts +6 -0
- package/lib/typescript/commonjs/index.d.ts +5 -0
- package/lib/typescript/commonjs/logger/crashlytics.d.ts +31 -0
- package/lib/typescript/commonjs/push/index.d.ts +3 -1
- package/lib/typescript/commonjs/push/push.d.ts +9 -1
- package/lib/typescript/commonjs/push/status.d.ts +9 -0
- package/lib/typescript/commonjs/secureScreen/index.d.ts +25 -0
- package/lib/typescript/module/api/responseBodyText.d.ts +6 -0
- package/lib/typescript/module/index.d.ts +5 -0
- package/lib/typescript/module/logger/crashlytics.d.ts +31 -0
- package/lib/typescript/module/push/index.d.ts +3 -1
- package/lib/typescript/module/push/push.d.ts +9 -1
- package/lib/typescript/module/push/status.d.ts +9 -0
- package/lib/typescript/module/secureScreen/index.d.ts +25 -0
- package/package.json +23 -4
|
@@ -1,250 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.setFileSystemImplementation = exports.downloadFile = exports.DownloadError = void 0;
|
|
7
|
-
var ExpoFileSystem = _interopRequireWildcard(require("expo-file-system"));
|
|
8
|
-
var _reactNative = require("react-native");
|
|
9
|
-
var _index = require("../logger/index.js");
|
|
10
|
-
var _index2 = require("../permissions/index.js");
|
|
11
|
-
var _expoFs = require("./adapters/expoFs.js");
|
|
12
|
-
var _shared = require("./adapters/shared.js");
|
|
13
|
-
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); }
|
|
14
|
-
const PREFIX = '[@webority/mobile-core]';
|
|
15
|
-
|
|
16
|
-
/** Android stopped requiring a storage permission for a Downloads write at API 29. */
|
|
17
|
-
const SCOPED_STORAGE_API_LEVEL = 29;
|
|
18
|
-
|
|
19
|
-
/** A file can take far longer than the 30 s the fleet's JSON clients allow. */
|
|
20
|
-
const DOWNLOAD_TIMEOUT_MS = 300000;
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* The slice of a filesystem module this module actually uses. expo-file-system
|
|
24
|
-
* is adapted onto it, and a test or the showcase can inject its own with
|
|
25
|
-
* `setFileSystemImplementation`.
|
|
26
|
-
*/
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* The slice of an HTTP client a download needs. The app's axios instance
|
|
30
|
-
* (mobile-core's `userClient` or `publicClient`, or its own) satisfies it as is,
|
|
31
|
-
* so whatever that client adds to a request (bearer token, tenant header, refresh
|
|
32
|
-
* and retry on 401) applies to the download too. Declared structurally, as
|
|
33
|
-
* mobile-support's client is, so no axios type leaks into the declarations.
|
|
34
|
-
*/
|
|
35
|
-
|
|
36
|
-
class DownloadError extends Error {
|
|
37
|
-
constructor(code, message, status) {
|
|
38
|
-
super(`${PREFIX} ${message}`);
|
|
39
|
-
this.name = 'DownloadError';
|
|
40
|
-
this.code = code;
|
|
41
|
-
this.status = status;
|
|
42
|
-
// Babel's class transform can drop the prototype link, which makes
|
|
43
|
-
// `instanceof DownloadError` false in the transpiled build.
|
|
44
|
-
Object.setPrototypeOf(this, DownloadError.prototype);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
exports.DownloadError = DownloadError;
|
|
48
|
-
const describe = error => error instanceof Error ? error.message : String(error);
|
|
49
|
-
|
|
50
|
-
/** Only what a test or the showcase injected; null means use expo-file-system. */
|
|
51
|
-
let backend = null;
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* expo-file-system, adapted once and memoised. The adapter is shape-checked, so
|
|
55
|
-
* a build where the native module did not link returns null and the guided
|
|
56
|
-
* error below still fires rather than a confusing property access.
|
|
57
|
-
*/
|
|
58
|
-
let expoBackend;
|
|
59
|
-
const resolveBackend = () => {
|
|
60
|
-
if (backend) {
|
|
61
|
-
return backend;
|
|
62
|
-
}
|
|
63
|
-
if (expoBackend === undefined) {
|
|
64
|
-
expoBackend = (0, _expoFs.adaptExpoFileSystem)(ExpoFileSystem);
|
|
65
|
-
}
|
|
66
|
-
return expoBackend;
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Replace the filesystem backend. Intended for TESTS and for the showcase,
|
|
71
|
-
* which injects a denying backend to demo the permission error; an app gets
|
|
72
|
-
* expo-file-system without wiring anything. Pass `null` to restore it.
|
|
73
|
-
*/
|
|
74
|
-
const setFileSystemImplementation = impl => {
|
|
75
|
-
backend = impl;
|
|
76
|
-
};
|
|
77
|
-
exports.setFileSystemImplementation = setFileSystemImplementation;
|
|
78
|
-
const requireBackend = () => {
|
|
79
|
-
const fs = resolveBackend();
|
|
80
|
-
if (!fs) {
|
|
81
|
-
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.');
|
|
82
|
-
}
|
|
83
|
-
return fs;
|
|
84
|
-
};
|
|
85
|
-
const stripControlChars = value => Array.from(value).filter(char => (char.codePointAt(0) ?? 0) > 0x1f).join('');
|
|
86
|
-
const lastSegment = raw => {
|
|
87
|
-
const segments = raw.split(/[/\\]/);
|
|
88
|
-
return stripControlChars(segments[segments.length - 1] ?? '').trim();
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Reduce a caller- or server-supplied name to a single path segment: `../../evil.pdf`
|
|
93
|
-
* must land inside the destination directory as `evil.pdf` rather than escaping it.
|
|
94
|
-
*/
|
|
95
|
-
const sanitiseFileName = raw => {
|
|
96
|
-
const base = lastSegment(raw);
|
|
97
|
-
if (base === '' || base === '.' || base === '..') {
|
|
98
|
-
throw new DownloadError('write-failed', `"${raw}" does not contain a usable file name. Pass an explicit fileName.`);
|
|
99
|
-
}
|
|
100
|
-
return base;
|
|
101
|
-
};
|
|
102
|
-
const fileNameFromUrl = url => {
|
|
103
|
-
const withoutQuery = url.split('#')[0]?.split('?')[0] ?? '';
|
|
104
|
-
const last = withoutQuery.split('/').pop() ?? '';
|
|
105
|
-
let decoded = last;
|
|
106
|
-
try {
|
|
107
|
-
decoded = decodeURIComponent(last);
|
|
108
|
-
} catch {
|
|
109
|
-
// A malformed escape sequence is not a reason to fail the download.
|
|
110
|
-
}
|
|
111
|
-
const base = lastSegment(decoded);
|
|
112
|
-
return base === '' || base === '.' || base === '..' ? 'download' : base;
|
|
113
|
-
};
|
|
114
|
-
const androidApiLevel = () => typeof _reactNative.Platform.Version === 'number' ? _reactNative.Platform.Version : Number.parseInt(String(_reactNative.Platform.Version), 10);
|
|
115
|
-
const ensureStoragePermission = async destination => {
|
|
116
|
-
if (_reactNative.Platform.OS !== 'android' || destination !== 'downloads') {
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
if (androidApiLevel() >= SCOPED_STORAGE_API_LEVEL) {
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
const status = await _index2.Permissions.ensure('storage');
|
|
123
|
-
if (status === 'granted' || status === 'limited') {
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
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.');
|
|
127
|
-
};
|
|
128
|
-
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.');
|
|
129
|
-
const requestFailed = (error, options, fileName) => {
|
|
130
|
-
if (options.signal?.aborted) {
|
|
131
|
-
return new DownloadError('aborted', `The download of ${fileName} was aborted.`);
|
|
132
|
-
}
|
|
133
|
-
const e = error ?? {};
|
|
134
|
-
// By name, not instanceof: a second copy of mobile-core would fail instanceof.
|
|
135
|
-
if (e.name === 'RequestOfflineError') {
|
|
136
|
-
return new DownloadError('network', `The device is offline, so ${options.url} was not downloaded.`);
|
|
137
|
-
}
|
|
138
|
-
const status = e.response?.status;
|
|
139
|
-
if (typeof status === 'number') {
|
|
140
|
-
return new DownloadError('http-error', `The server answered ${status} for ${options.url}, so no file was saved.`, status);
|
|
141
|
-
}
|
|
142
|
-
return new DownloadError('network', `The download of ${options.url} failed: ${describe(error)}`);
|
|
143
|
-
};
|
|
144
|
-
const discardPartial = async (fs, path) => {
|
|
145
|
-
try {
|
|
146
|
-
if (await fs.exists(path)) {
|
|
147
|
-
await fs.unlink(path);
|
|
148
|
-
}
|
|
149
|
-
} catch (error) {
|
|
150
|
-
_index.Logger.warn(`${PREFIX} Failed to remove the partial download at ${path}`, error);
|
|
151
|
-
}
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Download a file to the device through the app's own HTTP client, so it is
|
|
156
|
-
* authenticated exactly as the app's other calls are. The whole file is held
|
|
157
|
-
* in memory before it is written, which suits documents and reports rather
|
|
158
|
-
* than large media.
|
|
159
|
-
*
|
|
160
|
-
* A finished download is always verified to be non-empty: a 0-byte file looks
|
|
161
|
-
* like a success to every caller and then wedges whatever consumes it, so it is
|
|
162
|
-
* reported as `empty-file` and nothing is kept.
|
|
163
|
-
*/
|
|
164
|
-
const downloadFile = async options => {
|
|
165
|
-
const fs = requireBackend();
|
|
166
|
-
const destination = options.destination ?? 'documents';
|
|
167
|
-
const fileName = options.fileName ? sanitiseFileName(options.fileName) : fileNameFromUrl(options.url);
|
|
168
|
-
await ensureStoragePermission(destination);
|
|
169
|
-
const directory = fs.dirs[destination];
|
|
170
|
-
const path = `${directory}/${fileName}`;
|
|
171
|
-
if (options.signal?.aborted) {
|
|
172
|
-
throw new DownloadError('aborted', `The download of ${fileName} was aborted before it started.`);
|
|
173
|
-
}
|
|
174
|
-
let reuseExisting = false;
|
|
175
|
-
try {
|
|
176
|
-
await fs.mkdir(directory);
|
|
177
|
-
const exists = await fs.exists(path);
|
|
178
|
-
if (exists && options.overwrite === false) {
|
|
179
|
-
reuseExisting = true;
|
|
180
|
-
} else if (exists) {
|
|
181
|
-
await fs.unlink(path);
|
|
182
|
-
}
|
|
183
|
-
} catch (error) {
|
|
184
|
-
throw new DownloadError('write-failed', `Could not prepare ${path} for writing: ${describe(error)}`);
|
|
185
|
-
}
|
|
186
|
-
if (reuseExisting) {
|
|
187
|
-
const stat = await fs.stat(path);
|
|
188
|
-
return {
|
|
189
|
-
path,
|
|
190
|
-
bytes: (0, _shared.asNumber)(stat?.size),
|
|
191
|
-
mimeType: options.mimeType,
|
|
192
|
-
fromCache: true
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
const {
|
|
196
|
-
onProgress
|
|
197
|
-
} = options;
|
|
198
|
-
let data;
|
|
199
|
-
try {
|
|
200
|
-
({
|
|
201
|
-
data
|
|
202
|
-
} = await options.client.get(options.url, {
|
|
203
|
-
headers: {
|
|
204
|
-
Accept: '*/*',
|
|
205
|
-
...options.headers
|
|
206
|
-
},
|
|
207
|
-
responseType: 'arraybuffer',
|
|
208
|
-
timeout: DOWNLOAD_TIMEOUT_MS,
|
|
209
|
-
signal: options.signal,
|
|
210
|
-
onDownloadProgress: onProgress ? ({
|
|
211
|
-
loaded,
|
|
212
|
-
total = 0
|
|
213
|
-
}) => onProgress(loaded, total, total > 0 ? loaded / total : 0) : undefined
|
|
214
|
-
}));
|
|
215
|
-
} catch (error) {
|
|
216
|
-
throw requestFailed(error, options, fileName);
|
|
217
|
-
}
|
|
218
|
-
if (options.signal?.aborted) {
|
|
219
|
-
throw new DownloadError('aborted', `The download of ${fileName} was aborted.`);
|
|
220
|
-
}
|
|
221
|
-
const received = new Uint8Array(data ?? new ArrayBuffer(0));
|
|
222
|
-
if (received.byteLength === 0) {
|
|
223
|
-
throw emptyFile(options.url);
|
|
224
|
-
}
|
|
225
|
-
try {
|
|
226
|
-
await fs.write(path, received);
|
|
227
|
-
} catch (error) {
|
|
228
|
-
await discardPartial(fs, path);
|
|
229
|
-
throw new DownloadError('write-failed', `Could not write ${path}: ${describe(error)}`);
|
|
230
|
-
}
|
|
231
|
-
let bytes = 0;
|
|
232
|
-
try {
|
|
233
|
-
bytes = (await fs.exists(path)) ? (0, _shared.asNumber)((await fs.stat(path)).size) : 0;
|
|
234
|
-
} catch (error) {
|
|
235
|
-
await discardPartial(fs, path);
|
|
236
|
-
throw new DownloadError('write-failed', `The download of ${fileName} finished but ${path} could not be read back: ${describe(error)}`);
|
|
237
|
-
}
|
|
238
|
-
if (bytes <= 0) {
|
|
239
|
-
await discardPartial(fs, path);
|
|
240
|
-
throw emptyFile(options.url);
|
|
241
|
-
}
|
|
242
|
-
return {
|
|
243
|
-
path,
|
|
244
|
-
bytes,
|
|
245
|
-
mimeType: options.mimeType,
|
|
246
|
-
fromCache: false
|
|
247
|
-
};
|
|
248
|
-
};
|
|
249
|
-
exports.downloadFile = downloadFile;
|
|
250
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.setFileSystemImplementation=exports.downloadFile=exports.DownloadError=void 0;var ExpoFileSystem=_interopRequireWildcard(require("expo-file-system")),_reactNative=require("react-native"),_index=require("../logger/index.js"),_index2=require("../permissions/index.js"),_expoFs=require("./adapters/expoFs.js"),_shared=require("./adapters/shared.js");function _interopRequireWildcard(e,r){if("function"==typeof WeakMap)var t=new WeakMap,o=new WeakMap;return(_interopRequireWildcard=function(e,r){if(!r&&e&&e.__esModule)return e;var a,n,i={__proto__:null,default:e};if(null===e||"object"!=typeof e&&"function"!=typeof e)return i;if(a=r?o:t){if(a.has(e))return a.get(e);a.set(e,i)}for(const r in e)"default"!==r&&{}.hasOwnProperty.call(e,r)&&((n=(a=Object.defineProperty)&&Object.getOwnPropertyDescriptor(e,r))&&(n.get||n.set)?a(i,r,n):i[r]=e[r]);return i})(e,r)}const PREFIX="[@webority/mobile-core]",SCOPED_STORAGE_API_LEVEL=29,DOWNLOAD_TIMEOUT_MS=3e5;class DownloadError extends Error{constructor(e,r,t){super(`${PREFIX} ${r}`),this.name="DownloadError",this.code=e,this.status=t,Object.setPrototypeOf(this,DownloadError.prototype)}}exports.DownloadError=DownloadError;const describe=e=>e instanceof Error?e.message:String(e);let expoBackend,backend=null;const resolveBackend=()=>backend||(void 0===expoBackend&&(expoBackend=(0,_expoFs.adaptExpoFileSystem)(ExpoFileSystem)),expoBackend),setFileSystemImplementation=e=>{backend=e};exports.setFileSystemImplementation=setFileSystemImplementation;const requireBackend=()=>{const e=resolveBackend();if(!e)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.");return e},stripControlChars=e=>Array.from(e).filter(e=>(e.codePointAt(0)??0)>31).join(""),lastSegment=e=>{const r=e.split(/[/\\]/);return stripControlChars(r[r.length-1]??"").trim()},sanitiseFileName=e=>{const r=lastSegment(e);if(""===r||"."===r||".."===r)throw new DownloadError("write-failed",`"${e}" does not contain a usable file name. Pass an explicit fileName.`);return r},fileNameFromUrl=e=>{const r=(e.split("#")[0]?.split("?")[0]??"").split("/").pop()??"";let t=r;try{t=decodeURIComponent(r)}catch{}const o=lastSegment(t);return""===o||"."===o||".."===o?"download":o},androidApiLevel=()=>"number"==typeof _reactNative.Platform.Version?_reactNative.Platform.Version:Number.parseInt(String(_reactNative.Platform.Version),10),ensureStoragePermission=async e=>{if("android"!==_reactNative.Platform.OS||"downloads"!==e)return;if(androidApiLevel()>=29)return;const r=await _index2.Permissions.ensure("storage");if("granted"!==r&&"limited"!==r)throw new DownloadError("permission-denied",`Storage permission is "${r}", 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.`)},emptyFile=e=>new DownloadError("empty-file",`The download of ${e} produced an empty file, so nothing was saved. Check that the URL is correct and that the request carried the credentials the server expects.`),requestFailed=(e,r,t)=>{if(r.signal?.aborted)return new DownloadError("aborted",`The download of ${t} was aborted.`);const o=e??{};if("RequestOfflineError"===o.name)return new DownloadError("network",`The device is offline, so ${r.url} was not downloaded.`);const a=o.response?.status;return"number"==typeof a?new DownloadError("http-error",`The server answered ${a} for ${r.url}, so no file was saved.`,a):new DownloadError("network",`The download of ${r.url} failed: ${describe(e)}`)},discardPartial=async(e,r)=>{try{await e.exists(r)&&await e.unlink(r)}catch(e){_index.Logger.warn(`${PREFIX} Failed to remove the partial download at ${r}`,e)}},downloadFile=async e=>{const r=requireBackend(),t=e.destination??"documents",o=e.fileName?sanitiseFileName(e.fileName):fileNameFromUrl(e.url);await ensureStoragePermission(t);const a=r.dirs[t],n=`${a}/${o}`;if(e.signal?.aborted)throw new DownloadError("aborted",`The download of ${o} was aborted before it started.`);let i=!1;try{await r.mkdir(a);const t=await r.exists(n);t&&!1===e.overwrite?i=!0:t&&await r.unlink(n)}catch(e){throw new DownloadError("write-failed",`Could not prepare ${n} for writing: ${describe(e)}`)}if(i){const t=await r.stat(n);return{path:n,bytes:(0,_shared.asNumber)(t?.size),mimeType:e.mimeType,fromCache:!0}}const{onProgress:s}=e;let d;try{({data:d}=await e.client.get(e.url,{headers:{Accept:"*/*",...e.headers},responseType:"arraybuffer",timeout:3e5,signal:e.signal,onDownloadProgress:s?({loaded:e,total:r=0})=>s(e,r,r>0?e/r:0):void 0}))}catch(r){throw requestFailed(r,e,o)}if(e.signal?.aborted)throw new DownloadError("aborted",`The download of ${o} was aborted.`);const l=new Uint8Array(d??new ArrayBuffer(0));if(0===l.byteLength)throw emptyFile(e.url);try{await r.write(n,l)}catch(e){throw await discardPartial(r,n),new DownloadError("write-failed",`Could not write ${n}: ${describe(e)}`)}let c=0;try{c=await r.exists(n)?(0,_shared.asNumber)((await r.stat(n)).size):0}catch(e){throw await discardPartial(r,n),new DownloadError("write-failed",`The download of ${o} finished but ${n} could not be read back: ${describe(e)}`)}if(c<=0)throw await discardPartial(r,n),emptyFile(e.url);return{path:n,bytes:c,mimeType:e.mimeType,fromCache:!1}};exports.downloadFile=downloadFile;
|
|
@@ -1,70 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.formatCurrency = void 0;
|
|
7
|
-
var _intlCache = require("./intlCache.js");
|
|
8
|
-
const hasIntlNumber = () => typeof Intl !== 'undefined' && typeof Intl.NumberFormat === 'function';
|
|
9
|
-
const indianGroupingFallback = (n, fractionDigits) => {
|
|
10
|
-
// Indian numbering: 1,23,45,678.90 (last 3 digits, then groups of 2).
|
|
11
|
-
const sign = n < 0 ? '-' : '';
|
|
12
|
-
const abs = Math.abs(n);
|
|
13
|
-
const fixed = abs.toFixed(fractionDigits);
|
|
14
|
-
const [intPart, decPart] = fixed.split('.');
|
|
15
|
-
const last3 = intPart.length > 3 ? intPart.slice(-3) : intPart;
|
|
16
|
-
const rest = intPart.length > 3 ? intPart.slice(0, -3) : '';
|
|
17
|
-
const grouped = rest ? rest.replace(/\B(?=(\d{2})+(?!\d))/g, ',') + ',' + last3 : last3;
|
|
18
|
-
return sign + grouped + (decPart ? `.${decPart}` : '');
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Format an amount as currency. Defaults to **INR** with Indian numbering (`en-IN`),
|
|
23
|
-
* which matches the standard convention across Webority projects.
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* formatCurrency(150000) // ₹1,50,000
|
|
27
|
-
* formatCurrency(1234.5) // ₹1,234.50
|
|
28
|
-
* formatCurrency(99, { currency: 'USD', locale: 'en-US' }) // $99.00
|
|
29
|
-
* formatCurrency(150000, { stripSymbol: true }) // 1,50,000
|
|
30
|
-
*/
|
|
31
|
-
const formatCurrency = (amount, options = {}) => {
|
|
32
|
-
if (amount == null || Number.isNaN(amount)) {
|
|
33
|
-
return '';
|
|
34
|
-
}
|
|
35
|
-
const {
|
|
36
|
-
currency = 'INR',
|
|
37
|
-
locale = 'en-IN',
|
|
38
|
-
minimumFractionDigits,
|
|
39
|
-
maximumFractionDigits = 2,
|
|
40
|
-
stripSymbol = false
|
|
41
|
-
} = options;
|
|
42
|
-
const minFrac = minimumFractionDigits ?? (Number.isInteger(amount) ? 0 : 2);
|
|
43
|
-
if (hasIntlNumber()) {
|
|
44
|
-
try {
|
|
45
|
-
const formatted = (0, _intlCache.numberFormat)(locale, {
|
|
46
|
-
style: 'currency',
|
|
47
|
-
currency,
|
|
48
|
-
minimumFractionDigits: minFrac,
|
|
49
|
-
maximumFractionDigits
|
|
50
|
-
}).format(amount);
|
|
51
|
-
if (!stripSymbol) {
|
|
52
|
-
return formatted;
|
|
53
|
-
}
|
|
54
|
-
// Strip currency code/symbol while keeping digits, separators and sign.
|
|
55
|
-
return formatted.replace(/[^\d,.\s\-+]/g, '').trim();
|
|
56
|
-
} catch {
|
|
57
|
-
/* fall through to fallback */
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
const numeric = indianGroupingFallback(amount, minFrac);
|
|
61
|
-
if (stripSymbol) {
|
|
62
|
-
return numeric;
|
|
63
|
-
}
|
|
64
|
-
if (currency === 'INR') {
|
|
65
|
-
return `₹${numeric}`;
|
|
66
|
-
}
|
|
67
|
-
return `${currency} ${numeric}`;
|
|
68
|
-
};
|
|
69
|
-
exports.formatCurrency = formatCurrency;
|
|
70
|
-
//# sourceMappingURL=currency.js.map
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.formatCurrency=void 0;var _intlCache=require("./intlCache.js");const hasIntlNumber=()=>"undefined"!=typeof Intl&&"function"==typeof Intl.NumberFormat,indianGroupingFallback=(r,t)=>{const e=r<0?"-":"",n=Math.abs(r).toFixed(t),[i,c]=n.split("."),a=i.length>3?i.slice(-3):i,o=i.length>3?i.slice(0,-3):"";return e+(o?o.replace(/\B(?=(\d{2})+(?!\d))/g,",")+","+a:a)+(c?`.${c}`:"")},formatCurrency=(r,t={})=>{if(null==r||Number.isNaN(r))return"";const{currency:e="INR",locale:n="en-IN",minimumFractionDigits:i,maximumFractionDigits:c=2,stripSymbol:a=!1}=t,o=i??(Number.isInteger(r)?0:2);if(hasIntlNumber())try{const t=(0,_intlCache.numberFormat)(n,{style:"currency",currency:e,minimumFractionDigits:o,maximumFractionDigits:c}).format(r);return a?t.replace(/[^\d,.\s\-+]/g,"").trim():t}catch{}const u=indianGroupingFallback(r,o);return a?u:"INR"===e?`₹${u}`:`${e} ${u}`};exports.formatCurrency=formatCurrency;
|
|
@@ -1,167 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.formatTime = exports.formatRelativeTime = exports.formatDateTime = exports.formatDate = void 0;
|
|
7
|
-
var _intlCache = require("./intlCache.js");
|
|
8
|
-
const toDate = value => {
|
|
9
|
-
if (value instanceof Date) {
|
|
10
|
-
return Number.isNaN(value.getTime()) ? null : value;
|
|
11
|
-
}
|
|
12
|
-
const d = new Date(value);
|
|
13
|
-
return Number.isNaN(d.getTime()) ? null : d;
|
|
14
|
-
};
|
|
15
|
-
const hasIntl = () => typeof Intl !== 'undefined' && typeof Intl.DateTimeFormat === 'function';
|
|
16
|
-
const PAD = n => n < 10 ? `0${n}` : `${n}`;
|
|
17
|
-
const fallbackDate = (d, style) => {
|
|
18
|
-
const day = PAD(d.getDate());
|
|
19
|
-
const monthShort = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][d.getMonth()];
|
|
20
|
-
const monthLong = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'][d.getMonth()];
|
|
21
|
-
const year = d.getFullYear();
|
|
22
|
-
const weekdayLong = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][d.getDay()];
|
|
23
|
-
switch (style) {
|
|
24
|
-
case 'short':
|
|
25
|
-
return `${day}/${PAD(d.getMonth() + 1)}/${year}`;
|
|
26
|
-
case 'long':
|
|
27
|
-
return `${day} ${monthLong} ${year}`;
|
|
28
|
-
case 'full':
|
|
29
|
-
return `${weekdayLong}, ${day} ${monthLong} ${year}`;
|
|
30
|
-
case 'medium':
|
|
31
|
-
default:
|
|
32
|
-
return `${day} ${monthShort} ${year}`;
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
const fallbackTime = (d, style) => {
|
|
36
|
-
const hh = PAD(d.getHours());
|
|
37
|
-
const mm = PAD(d.getMinutes());
|
|
38
|
-
if (style === 'short') {
|
|
39
|
-
return `${hh}:${mm}`;
|
|
40
|
-
}
|
|
41
|
-
const ss = PAD(d.getSeconds());
|
|
42
|
-
return `${hh}:${mm}:${ss}`;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Locale-aware date formatter. Defaults to `'medium'` style and `'en-IN'` locale,
|
|
47
|
-
* matching the Indian-locale convention used across Webority apps.
|
|
48
|
-
*/
|
|
49
|
-
const formatDate = (value, style = 'medium', locale = 'en-IN') => {
|
|
50
|
-
const d = toDate(value);
|
|
51
|
-
if (!d) {
|
|
52
|
-
return '';
|
|
53
|
-
}
|
|
54
|
-
if (hasIntl()) {
|
|
55
|
-
try {
|
|
56
|
-
return (0, _intlCache.dateTimeFormat)(locale, {
|
|
57
|
-
dateStyle: style
|
|
58
|
-
}).format(d);
|
|
59
|
-
} catch {
|
|
60
|
-
/* fall through to fallback */
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return fallbackDate(d, style);
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Locale-aware time formatter (24-hour fallback when Intl is unavailable).
|
|
68
|
-
*/
|
|
69
|
-
exports.formatDate = formatDate;
|
|
70
|
-
const formatTime = (value, style = 'short', locale = 'en-IN') => {
|
|
71
|
-
const d = toDate(value);
|
|
72
|
-
if (!d) {
|
|
73
|
-
return '';
|
|
74
|
-
}
|
|
75
|
-
if (hasIntl()) {
|
|
76
|
-
try {
|
|
77
|
-
return (0, _intlCache.dateTimeFormat)(locale, {
|
|
78
|
-
timeStyle: style
|
|
79
|
-
}).format(d);
|
|
80
|
-
} catch {
|
|
81
|
-
/* fall through */
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
return fallbackTime(d, style);
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Combined date + time formatter.
|
|
89
|
-
*/
|
|
90
|
-
exports.formatTime = formatTime;
|
|
91
|
-
const formatDateTime = (value, dateStyle = 'medium', timeStyle = 'short', locale = 'en-IN') => {
|
|
92
|
-
const d = toDate(value);
|
|
93
|
-
if (!d) {
|
|
94
|
-
return '';
|
|
95
|
-
}
|
|
96
|
-
if (hasIntl()) {
|
|
97
|
-
try {
|
|
98
|
-
return (0, _intlCache.dateTimeFormat)(locale, {
|
|
99
|
-
dateStyle,
|
|
100
|
-
timeStyle
|
|
101
|
-
}).format(d);
|
|
102
|
-
} catch {
|
|
103
|
-
/* fall through */
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return `${fallbackDate(d, dateStyle)}, ${fallbackTime(d, timeStyle)}`;
|
|
107
|
-
};
|
|
108
|
-
exports.formatDateTime = formatDateTime;
|
|
109
|
-
const SECOND = 1000;
|
|
110
|
-
const MINUTE = 60 * SECOND;
|
|
111
|
-
const HOUR = 60 * MINUTE;
|
|
112
|
-
const DAY = 24 * HOUR;
|
|
113
|
-
const WEEK = 7 * DAY;
|
|
114
|
-
const MONTH = 30 * DAY;
|
|
115
|
-
const YEAR = 365 * DAY;
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Returns a humanized relative-time string (e.g. "5 minutes ago", "in 3 days").
|
|
119
|
-
* Uses `Intl.RelativeTimeFormat` when available; otherwise a sensible English fallback.
|
|
120
|
-
*/
|
|
121
|
-
const formatRelativeTime = (value, base = new Date(), locale = 'en-IN') => {
|
|
122
|
-
const target = toDate(value);
|
|
123
|
-
const now = toDate(base);
|
|
124
|
-
if (!target || !now) {
|
|
125
|
-
return '';
|
|
126
|
-
}
|
|
127
|
-
const diffMs = target.getTime() - now.getTime();
|
|
128
|
-
const abs = Math.abs(diffMs);
|
|
129
|
-
let unit;
|
|
130
|
-
let amount;
|
|
131
|
-
if (abs < MINUTE) {
|
|
132
|
-
unit = 'second';
|
|
133
|
-
amount = Math.round(diffMs / SECOND);
|
|
134
|
-
} else if (abs < HOUR) {
|
|
135
|
-
unit = 'minute';
|
|
136
|
-
amount = Math.round(diffMs / MINUTE);
|
|
137
|
-
} else if (abs < DAY) {
|
|
138
|
-
unit = 'hour';
|
|
139
|
-
amount = Math.round(diffMs / HOUR);
|
|
140
|
-
} else if (abs < WEEK) {
|
|
141
|
-
unit = 'day';
|
|
142
|
-
amount = Math.round(diffMs / DAY);
|
|
143
|
-
} else if (abs < MONTH) {
|
|
144
|
-
unit = 'week';
|
|
145
|
-
amount = Math.round(diffMs / WEEK);
|
|
146
|
-
} else if (abs < YEAR) {
|
|
147
|
-
unit = 'month';
|
|
148
|
-
amount = Math.round(diffMs / MONTH);
|
|
149
|
-
} else {
|
|
150
|
-
unit = 'year';
|
|
151
|
-
amount = Math.round(diffMs / YEAR);
|
|
152
|
-
}
|
|
153
|
-
const Rtf = hasIntl() ? Intl.RelativeTimeFormat : undefined;
|
|
154
|
-
if (typeof Rtf === 'function') {
|
|
155
|
-
try {
|
|
156
|
-
return (0, _intlCache.relativeTimeFormat)(Rtf, locale).format(amount, unit);
|
|
157
|
-
} catch {
|
|
158
|
-
/* fall through */
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
const past = amount < 0;
|
|
162
|
-
const n = Math.abs(amount);
|
|
163
|
-
const u = n === 1 ? unit : `${unit}s`;
|
|
164
|
-
return past ? `${n} ${u} ago` : `in ${n} ${u}`;
|
|
165
|
-
};
|
|
166
|
-
exports.formatRelativeTime = formatRelativeTime;
|
|
167
|
-
//# sourceMappingURL=date.js.map
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.formatTime=exports.formatRelativeTime=exports.formatDateTime=exports.formatDate=void 0;var _intlCache=require("./intlCache.js");const toDate=t=>{if(t instanceof Date)return Number.isNaN(t.getTime())?null:t;const e=new Date(t);return Number.isNaN(e.getTime())?null:e},hasIntl=()=>"undefined"!=typeof Intl&&"function"==typeof Intl.DateTimeFormat,PAD=t=>t<10?`0${t}`:`${t}`,fallbackDate=(t,e)=>{const a=PAD(t.getDate()),r=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"][t.getMonth()],o=["January","February","March","April","May","June","July","August","September","October","November","December"][t.getMonth()],n=t.getFullYear(),i=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"][t.getDay()];switch(e){case"short":return`${a}/${PAD(t.getMonth()+1)}/${n}`;case"long":return`${a} ${o} ${n}`;case"full":return`${i}, ${a} ${o} ${n}`;default:return`${a} ${r} ${n}`}},fallbackTime=(t,e)=>{const a=PAD(t.getHours()),r=PAD(t.getMinutes());if("short"===e)return`${a}:${r}`;return`${a}:${r}:${PAD(t.getSeconds())}`},formatDate=(t,e="medium",a="en-IN")=>{const r=toDate(t);if(!r)return"";if(hasIntl())try{return(0,_intlCache.dateTimeFormat)(a,{dateStyle:e}).format(r)}catch{}return fallbackDate(r,e)};exports.formatDate=formatDate;const formatTime=(t,e="short",a="en-IN")=>{const r=toDate(t);if(!r)return"";if(hasIntl())try{return(0,_intlCache.dateTimeFormat)(a,{timeStyle:e}).format(r)}catch{}return fallbackTime(r,e)};exports.formatTime=formatTime;const formatDateTime=(t,e="medium",a="short",r="en-IN")=>{const o=toDate(t);if(!o)return"";if(hasIntl())try{return(0,_intlCache.dateTimeFormat)(r,{dateStyle:e,timeStyle:a}).format(o)}catch{}return`${fallbackDate(o,e)}, ${fallbackTime(o,a)}`};exports.formatDateTime=formatDateTime;const SECOND=1e3,MINUTE=6e4,HOUR=36e5,DAY=24*HOUR,WEEK=7*DAY,MONTH=30*DAY,YEAR=365*DAY,formatRelativeTime=(t,e=new Date,a="en-IN")=>{const r=toDate(t),o=toDate(e);if(!r||!o)return"";const n=r.getTime()-o.getTime(),i=Math.abs(n);let m,u;i<6e4?(m="second",u=Math.round(n/1e3)):i<HOUR?(m="minute",u=Math.round(n/6e4)):i<DAY?(m="hour",u=Math.round(n/HOUR)):i<WEEK?(m="day",u=Math.round(n/DAY)):i<MONTH?(m="week",u=Math.round(n/WEEK)):i<YEAR?(m="month",u=Math.round(n/MONTH)):(m="year",u=Math.round(n/YEAR));const s=hasIntl()?Intl.RelativeTimeFormat:void 0;if("function"==typeof s)try{return(0,_intlCache.relativeTimeFormat)(s,a).format(u,m)}catch{}const l=u<0,c=Math.abs(u),f=1===c?m:`${m}s`;return l?`${c} ${f} ago`:`in ${c} ${f}`};exports.formatRelativeTime=formatRelativeTime;
|