@webority/mobile-core 0.0.29 → 0.2.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/README.md +32 -0
- package/lib/commonjs/api/baseUrl.js +1 -34
- package/lib/commonjs/api/clientHeaders.js +1 -0
- package/lib/commonjs/api/curl.js +1 -109
- package/lib/commonjs/api/minAppVersion.js +1 -0
- package/lib/commonjs/api/multipart.js +1 -21
- package/lib/commonjs/api/publicClient.js +1 -79
- package/lib/commonjs/api/requestDefaults.js +1 -0
- 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/errors/extractApiError.js +1 -0
- package/lib/commonjs/errors/index.js +1 -0
- 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/formatConfig.js +1 -0
- package/lib/commonjs/formatters/house.js +1 -0
- 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 -468
- package/lib/commonjs/push/status.js +1 -0
- package/lib/commonjs/push/task.js +1 -43
- package/lib/commonjs/query/apiBaseQuery.js +1 -0
- package/lib/commonjs/query/createAppApi.js +1 -0
- package/lib/commonjs/query/index.js +1 -0
- package/lib/commonjs/query/listeners.js +1 -0
- package/lib/commonjs/query/paging.js +1 -0
- package/lib/commonjs/query/tags.js +1 -0
- package/lib/commonjs/redux/errorToastMiddleware.js +1 -0
- package/lib/commonjs/redux/index.js +1 -0
- package/lib/commonjs/redux/rootReducer.js +1 -0
- package/lib/commonjs/redux/serializableCheck.js +1 -0
- 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/buildNumber.js +1 -0
- package/lib/commonjs/versionCheck/index.js +1 -419
- package/lib/module/api/baseUrl.js +1 -30
- package/lib/module/api/clientHeaders.js +1 -0
- package/lib/module/api/curl.js +1 -104
- package/lib/module/api/minAppVersion.js +1 -0
- package/lib/module/api/multipart.js +1 -16
- package/lib/module/api/publicClient.js +1 -75
- package/lib/module/api/requestDefaults.js +1 -0
- 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/errors/extractApiError.js +1 -0
- package/lib/module/errors/index.js +1 -0
- 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/formatConfig.js +1 -0
- package/lib/module/formatters/house.js +1 -0
- 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 -452
- package/lib/module/push/status.js +1 -0
- package/lib/module/push/task.js +1 -38
- package/lib/module/query/apiBaseQuery.js +1 -0
- package/lib/module/query/createAppApi.js +1 -0
- package/lib/module/query/index.js +1 -0
- package/lib/module/query/listeners.js +1 -0
- package/lib/module/query/paging.js +1 -0
- package/lib/module/query/tags.js +1 -0
- package/lib/module/redux/errorToastMiddleware.js +1 -0
- package/lib/module/redux/index.js +1 -0
- package/lib/module/redux/rootReducer.js +1 -0
- package/lib/module/redux/serializableCheck.js +1 -0
- 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/buildNumber.js +1 -0
- package/lib/module/versionCheck/index.js +1 -413
- package/lib/typescript/commonjs/api/clientHeaders.d.ts +25 -0
- package/lib/typescript/commonjs/api/minAppVersion.d.ts +24 -0
- package/lib/typescript/commonjs/api/requestDefaults.d.ts +21 -0
- package/lib/typescript/commonjs/api/responseBodyText.d.ts +6 -0
- package/lib/typescript/commonjs/api/retry.d.ts +23 -9
- package/lib/typescript/commonjs/api/tokenRefresh.d.ts +14 -2
- package/lib/typescript/commonjs/api/userClient.d.ts +1 -1
- package/lib/typescript/commonjs/auth/authStore.d.ts +76 -0
- package/lib/typescript/commonjs/auth/index.d.ts +2 -2
- package/lib/typescript/commonjs/config/index.d.ts +31 -2
- package/lib/typescript/commonjs/errors/extractApiError.d.ts +69 -0
- package/lib/typescript/commonjs/errors/index.d.ts +3 -0
- package/lib/typescript/commonjs/formatters/currency.d.ts +22 -7
- package/lib/typescript/commonjs/formatters/date.d.ts +13 -13
- package/lib/typescript/commonjs/formatters/dateMath.d.ts +4 -2
- package/lib/typescript/commonjs/formatters/formatConfig.d.ts +48 -0
- package/lib/typescript/commonjs/formatters/house.d.ts +143 -0
- package/lib/typescript/commonjs/formatters/index.d.ts +4 -0
- package/lib/typescript/commonjs/formatters/number.d.ts +15 -5
- package/lib/typescript/commonjs/index.d.ts +26 -8
- package/lib/typescript/commonjs/initSDK.d.ts +18 -6
- package/lib/typescript/commonjs/initUserAuth.d.ts +29 -0
- package/lib/typescript/commonjs/logger/crashlytics.d.ts +31 -0
- package/lib/typescript/commonjs/logger/index.d.ts +17 -0
- package/lib/typescript/commonjs/push/index.d.ts +3 -1
- package/lib/typescript/commonjs/push/push.d.ts +1 -1
- package/lib/typescript/commonjs/push/status.d.ts +9 -0
- package/lib/typescript/commonjs/query/apiBaseQuery.d.ts +55 -0
- package/lib/typescript/commonjs/query/createAppApi.d.ts +21 -0
- package/lib/typescript/commonjs/query/index.d.ts +9 -0
- package/lib/typescript/commonjs/query/listeners.d.ts +11 -0
- package/lib/typescript/commonjs/query/paging.d.ts +42 -0
- package/lib/typescript/commonjs/query/tags.d.ts +31 -0
- package/lib/typescript/commonjs/redux/errorToastMiddleware.d.ts +51 -0
- package/lib/typescript/commonjs/redux/index.d.ts +6 -0
- package/lib/typescript/commonjs/redux/rootReducer.d.ts +19 -0
- package/lib/typescript/commonjs/redux/serializableCheck.d.ts +14 -0
- package/lib/typescript/commonjs/secureScreen/index.d.ts +25 -0
- package/lib/typescript/commonjs/versionCheck/buildNumber.d.ts +14 -0
- package/lib/typescript/commonjs/versionCheck/index.d.ts +28 -17
- package/lib/typescript/module/api/clientHeaders.d.ts +25 -0
- package/lib/typescript/module/api/minAppVersion.d.ts +24 -0
- package/lib/typescript/module/api/requestDefaults.d.ts +21 -0
- package/lib/typescript/module/api/responseBodyText.d.ts +6 -0
- package/lib/typescript/module/api/retry.d.ts +23 -9
- package/lib/typescript/module/api/tokenRefresh.d.ts +14 -2
- package/lib/typescript/module/api/userClient.d.ts +1 -1
- package/lib/typescript/module/auth/authStore.d.ts +76 -0
- package/lib/typescript/module/auth/index.d.ts +2 -2
- package/lib/typescript/module/config/index.d.ts +31 -2
- package/lib/typescript/module/errors/extractApiError.d.ts +69 -0
- package/lib/typescript/module/errors/index.d.ts +3 -0
- package/lib/typescript/module/formatters/currency.d.ts +22 -7
- package/lib/typescript/module/formatters/date.d.ts +13 -13
- package/lib/typescript/module/formatters/dateMath.d.ts +4 -2
- package/lib/typescript/module/formatters/formatConfig.d.ts +48 -0
- package/lib/typescript/module/formatters/house.d.ts +143 -0
- package/lib/typescript/module/formatters/index.d.ts +4 -0
- package/lib/typescript/module/formatters/number.d.ts +15 -5
- package/lib/typescript/module/index.d.ts +26 -8
- package/lib/typescript/module/initSDK.d.ts +18 -6
- package/lib/typescript/module/initUserAuth.d.ts +29 -0
- package/lib/typescript/module/logger/crashlytics.d.ts +31 -0
- package/lib/typescript/module/logger/index.d.ts +17 -0
- package/lib/typescript/module/push/index.d.ts +3 -1
- package/lib/typescript/module/push/push.d.ts +1 -1
- package/lib/typescript/module/push/status.d.ts +9 -0
- package/lib/typescript/module/query/apiBaseQuery.d.ts +55 -0
- package/lib/typescript/module/query/createAppApi.d.ts +21 -0
- package/lib/typescript/module/query/index.d.ts +9 -0
- package/lib/typescript/module/query/listeners.d.ts +11 -0
- package/lib/typescript/module/query/paging.d.ts +42 -0
- package/lib/typescript/module/query/tags.d.ts +31 -0
- package/lib/typescript/module/redux/errorToastMiddleware.d.ts +51 -0
- package/lib/typescript/module/redux/index.d.ts +6 -0
- package/lib/typescript/module/redux/rootReducer.d.ts +19 -0
- package/lib/typescript/module/redux/serializableCheck.d.ts +14 -0
- package/lib/typescript/module/secureScreen/index.d.ts +25 -0
- package/lib/typescript/module/versionCheck/buildNumber.d.ts +14 -0
- package/lib/typescript/module/versionCheck/index.d.ts +28 -17
- package/package.json +52 -5
|
@@ -1,214 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.setStorageImplementation = exports.isStorageAvailable = exports.Storage = void 0;
|
|
7
|
-
var _kvStore = _interopRequireDefault(require("expo-sqlite/kv-store"));
|
|
8
|
-
var _index = require("../logger/index.js");
|
|
9
|
-
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
|
-
/**
|
|
11
|
-
* Minimal AsyncStorage shape we depend on.
|
|
12
|
-
* Lets us avoid a hard import on `@react-native-async-storage/async-storage`.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
/** Only what a test or the showcase injected; null means use expo-sqlite/kv-store. */
|
|
16
|
-
let backend = null;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* expo-sqlite's key/value store, imported directly.
|
|
20
|
-
*
|
|
21
|
-
* It deliberately implements the AsyncStorage v1 surface — `getItem`,
|
|
22
|
-
* `setItem`, `removeItem`, `multiGet`, `multiSet`, `multiRemove`, `clear`,
|
|
23
|
-
* `getAllKeys`, with v1's shapes — which is exactly what this module already
|
|
24
|
-
* speaks, so there is nothing to convert. Verified against expo-sqlite 57.0.2.
|
|
25
|
-
*/
|
|
26
|
-
const resolveBackend = () => backend ?? _kvStore.default;
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Always true now that the backend ships with the library. Kept because callers
|
|
30
|
-
* whose use of storage is an OPTIMISATION branch on it, and removing it would
|
|
31
|
-
* be a breaking change for no gain.
|
|
32
|
-
*/
|
|
33
|
-
const isStorageAvailable = () => resolveBackend() !== null;
|
|
34
|
-
exports.isStorageAvailable = isStorageAvailable;
|
|
35
|
-
const requireBackend = op => {
|
|
36
|
-
const b = resolveBackend();
|
|
37
|
-
if (!b) {
|
|
38
|
-
// Only reachable if a caller injected something falsy; the default backend
|
|
39
|
-
// ships with the library, so there is no "not installed" case any more.
|
|
40
|
-
const err = new Error(`[Storage] no storage backend available; cannot ${op}.`);
|
|
41
|
-
_index.Logger.error(err.message);
|
|
42
|
-
throw err;
|
|
43
|
-
}
|
|
44
|
-
return b;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Replace the storage backend. Intended for TESTS and for the showcase; an app
|
|
49
|
-
* gets expo-sqlite/kv-store without wiring anything. Pass `null` to restore it.
|
|
50
|
-
*/
|
|
51
|
-
const setStorageImplementation = impl => {
|
|
52
|
-
backend = impl;
|
|
53
|
-
};
|
|
54
|
-
exports.setStorageImplementation = setStorageImplementation;
|
|
55
|
-
const safeJsonParse = raw => {
|
|
56
|
-
if (raw == null) {
|
|
57
|
-
return null;
|
|
58
|
-
}
|
|
59
|
-
try {
|
|
60
|
-
return JSON.parse(raw);
|
|
61
|
-
} catch (error) {
|
|
62
|
-
_index.Logger.warn('[Storage] Failed to parse JSON value, returning null', error);
|
|
63
|
-
return null;
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
const getItem = async key => {
|
|
67
|
-
try {
|
|
68
|
-
return await requireBackend('getItem').getItem(key);
|
|
69
|
-
} catch (error) {
|
|
70
|
-
_index.Logger.error(`[Storage] getItem failed for key "${key}"`, error);
|
|
71
|
-
return null;
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
const setItem = async (key, value) => {
|
|
75
|
-
try {
|
|
76
|
-
await requireBackend('setItem').setItem(key, value);
|
|
77
|
-
return true;
|
|
78
|
-
} catch (error) {
|
|
79
|
-
_index.Logger.error(`[Storage] setItem failed for key "${key}"`, error);
|
|
80
|
-
return false;
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
const removeItem = async key => {
|
|
84
|
-
try {
|
|
85
|
-
await requireBackend('removeItem').removeItem(key);
|
|
86
|
-
return true;
|
|
87
|
-
} catch (error) {
|
|
88
|
-
_index.Logger.error(`[Storage] removeItem failed for key "${key}"`, error);
|
|
89
|
-
return false;
|
|
90
|
-
}
|
|
91
|
-
};
|
|
92
|
-
const getJson = async key => {
|
|
93
|
-
const raw = await getItem(key);
|
|
94
|
-
return safeJsonParse(raw);
|
|
95
|
-
};
|
|
96
|
-
const setJson = async (key, value) => {
|
|
97
|
-
try {
|
|
98
|
-
return await setItem(key, JSON.stringify(value));
|
|
99
|
-
} catch (error) {
|
|
100
|
-
_index.Logger.error(`[Storage] setJson failed for key "${key}"`, error);
|
|
101
|
-
return false;
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
const multiGet = async keys => {
|
|
105
|
-
try {
|
|
106
|
-
const b = requireBackend('multiGet');
|
|
107
|
-
// v3 first: its Record is already this function's return shape.
|
|
108
|
-
if (b.getMany) {
|
|
109
|
-
return await b.getMany([...keys]);
|
|
110
|
-
}
|
|
111
|
-
if (!b.multiGet) {
|
|
112
|
-
const out = {};
|
|
113
|
-
await Promise.all(keys.map(async k => {
|
|
114
|
-
out[k] = await b.getItem(k);
|
|
115
|
-
}));
|
|
116
|
-
return out;
|
|
117
|
-
}
|
|
118
|
-
const pairs = await b.multiGet(keys);
|
|
119
|
-
const out = {};
|
|
120
|
-
for (const [k, v] of pairs) {
|
|
121
|
-
out[k] = v;
|
|
122
|
-
}
|
|
123
|
-
return out;
|
|
124
|
-
} catch (error) {
|
|
125
|
-
_index.Logger.error('[Storage] multiGet failed', error);
|
|
126
|
-
return {};
|
|
127
|
-
}
|
|
128
|
-
};
|
|
129
|
-
const multiSet = async entries => {
|
|
130
|
-
try {
|
|
131
|
-
const b = requireBackend('multiSet');
|
|
132
|
-
// v3 first: it takes the Record this function already receives.
|
|
133
|
-
if (b.setMany) {
|
|
134
|
-
await b.setMany(entries);
|
|
135
|
-
return true;
|
|
136
|
-
}
|
|
137
|
-
const pairs = Object.entries(entries);
|
|
138
|
-
if (!b.multiSet) {
|
|
139
|
-
await Promise.all(pairs.map(([k, v]) => b.setItem(k, v)));
|
|
140
|
-
return true;
|
|
141
|
-
}
|
|
142
|
-
await b.multiSet(pairs);
|
|
143
|
-
return true;
|
|
144
|
-
} catch (error) {
|
|
145
|
-
_index.Logger.error('[Storage] multiSet failed', error);
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
148
|
-
};
|
|
149
|
-
const multiRemove = async keys => {
|
|
150
|
-
try {
|
|
151
|
-
const b = requireBackend('multiRemove');
|
|
152
|
-
if (b.removeMany) {
|
|
153
|
-
await b.removeMany([...keys]);
|
|
154
|
-
return true;
|
|
155
|
-
}
|
|
156
|
-
if (!b.multiRemove) {
|
|
157
|
-
await Promise.all(keys.map(k => b.removeItem(k)));
|
|
158
|
-
return true;
|
|
159
|
-
}
|
|
160
|
-
await b.multiRemove(keys);
|
|
161
|
-
return true;
|
|
162
|
-
} catch (error) {
|
|
163
|
-
_index.Logger.error('[Storage] multiRemove failed', error);
|
|
164
|
-
return false;
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
const clear = async () => {
|
|
168
|
-
try {
|
|
169
|
-
const b = requireBackend('clear');
|
|
170
|
-
if (!b.clear) {
|
|
171
|
-
_index.Logger.warn('[Storage] backend does not support clear()');
|
|
172
|
-
return false;
|
|
173
|
-
}
|
|
174
|
-
await b.clear();
|
|
175
|
-
return true;
|
|
176
|
-
} catch (error) {
|
|
177
|
-
_index.Logger.error('[Storage] clear failed', error);
|
|
178
|
-
return false;
|
|
179
|
-
}
|
|
180
|
-
};
|
|
181
|
-
const getAllKeys = async () => {
|
|
182
|
-
try {
|
|
183
|
-
const b = requireBackend('getAllKeys');
|
|
184
|
-
if (!b.getAllKeys) {
|
|
185
|
-
return [];
|
|
186
|
-
}
|
|
187
|
-
const keys = await b.getAllKeys();
|
|
188
|
-
return [...keys];
|
|
189
|
-
} catch (error) {
|
|
190
|
-
_index.Logger.error('[Storage] getAllKeys failed', error);
|
|
191
|
-
return [];
|
|
192
|
-
}
|
|
193
|
-
};
|
|
194
|
-
/**
|
|
195
|
-
* Typed AsyncStorage wrapper with safe JSON helpers.
|
|
196
|
-
* All operations swallow errors (logging them) and return a falsy result
|
|
197
|
-
* so that storage problems never crash the app.
|
|
198
|
-
*
|
|
199
|
-
* Backed by `expo-sqlite/kv-store` by default;
|
|
200
|
-
* override with `setStorageImplementation()` for MMKV or custom adapters.
|
|
201
|
-
*/
|
|
202
|
-
const Storage = exports.Storage = {
|
|
203
|
-
getItem,
|
|
204
|
-
setItem,
|
|
205
|
-
removeItem,
|
|
206
|
-
getJson,
|
|
207
|
-
setJson,
|
|
208
|
-
multiGet,
|
|
209
|
-
multiSet,
|
|
210
|
-
multiRemove,
|
|
211
|
-
clear,
|
|
212
|
-
getAllKeys
|
|
213
|
-
};
|
|
214
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.setStorageImplementation=exports.isStorageAvailable=exports.Storage=void 0;var _kvStore=_interopRequireDefault(require("expo-sqlite/kv-store")),_index=require("../logger/index.js");function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}let backend=null;const resolveBackend=()=>backend??_kvStore.default,isStorageAvailable=()=>null!==resolveBackend();exports.isStorageAvailable=isStorageAvailable;const requireBackend=e=>{const t=resolveBackend();if(!t){const t=new Error(`[Storage] no storage backend available; cannot ${e}.`);throw _index.Logger.error(t.message),t}return t},setStorageImplementation=e=>{backend=e};exports.setStorageImplementation=setStorageImplementation;const safeJsonParse=e=>{if(null==e)return null;try{return JSON.parse(e)}catch(e){return _index.Logger.warn("[Storage] Failed to parse JSON value, returning null",e),null}},getItem=async e=>{try{return await requireBackend("getItem").getItem(e)}catch(t){return _index.Logger.error(`[Storage] getItem failed for key "${e}"`,t),null}},setItem=async(e,t)=>{try{return await requireBackend("setItem").setItem(e,t),!0}catch(t){return _index.Logger.error(`[Storage] setItem failed for key "${e}"`,t),!1}},removeItem=async e=>{try{return await requireBackend("removeItem").removeItem(e),!0}catch(t){return _index.Logger.error(`[Storage] removeItem failed for key "${e}"`,t),!1}},getJson=async e=>{const t=await getItem(e);return safeJsonParse(t)},setJson=async(e,t)=>{try{return await setItem(e,JSON.stringify(t))}catch(t){return _index.Logger.error(`[Storage] setJson failed for key "${e}"`,t),!1}},multiGet=async e=>{try{const t=requireBackend("multiGet");if(t.getMany)return await t.getMany([...e]);if(!t.multiGet){const r={};return await Promise.all(e.map(async e=>{r[e]=await t.getItem(e)})),r}const r=await t.multiGet(e),a={};for(const[e,t]of r)a[e]=t;return a}catch(e){return _index.Logger.error("[Storage] multiGet failed",e),{}}},multiSet=async e=>{try{const t=requireBackend("multiSet");if(t.setMany)return await t.setMany(e),!0;const r=Object.entries(e);return t.multiSet?(await t.multiSet(r),!0):(await Promise.all(r.map(([e,r])=>t.setItem(e,r))),!0)}catch(e){return _index.Logger.error("[Storage] multiSet failed",e),!1}},multiRemove=async e=>{try{const t=requireBackend("multiRemove");return t.removeMany?(await t.removeMany([...e]),!0):t.multiRemove?(await t.multiRemove(e),!0):(await Promise.all(e.map(e=>t.removeItem(e))),!0)}catch(e){return _index.Logger.error("[Storage] multiRemove failed",e),!1}},clear=async()=>{try{const e=requireBackend("clear");return e.clear?(await e.clear(),!0):(_index.Logger.warn("[Storage] backend does not support clear()"),!1)}catch(e){return _index.Logger.error("[Storage] clear failed",e),!1}},getAllKeys=async()=>{try{const e=requireBackend("getAllKeys");if(!e.getAllKeys)return[];return[...await e.getAllKeys()]}catch(e){return _index.Logger.error("[Storage] getAllKeys failed",e),[]}},Storage=exports.Storage={getItem:getItem,setItem:setItem,removeItem:removeItem,getJson:getJson,setJson:setJson,multiGet:multiGet,multiSet:multiSet,multiRemove:multiRemove,clear:clear,getAllKeys:getAllKeys};
|
|
@@ -1,24 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.encodeBase64 = void 0;
|
|
7
|
-
// Small enough for every engine's argument limit in `String.fromCharCode.apply`.
|
|
8
|
-
const CHUNK = 0x2000;
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Base64 of raw bytes, for a `data:` URI. Builds a Latin-1 string a chunk at a
|
|
12
|
-
* time and hands it to the engine's native `btoa` (Hermes ships it), which is
|
|
13
|
-
* many times faster on the JS thread than encoding byte by byte in JS.
|
|
14
|
-
*/
|
|
15
|
-
const encodeBase64 = buffer => {
|
|
16
|
-
const bytes = new Uint8Array(buffer);
|
|
17
|
-
let binary = '';
|
|
18
|
-
for (let i = 0; i < bytes.length; i += CHUNK) {
|
|
19
|
-
binary += String.fromCharCode.apply(null, bytes.subarray(i, i + CHUNK));
|
|
20
|
-
}
|
|
21
|
-
return btoa(binary);
|
|
22
|
-
};
|
|
23
|
-
exports.encodeBase64 = encodeBase64;
|
|
24
|
-
//# sourceMappingURL=base64.js.map
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.encodeBase64=void 0;const CHUNK=8192,encodeBase64=e=>{const o=new Uint8Array(e);let r="";for(let e=0;e<o.length;e+=8192)r+=String.fromCharCode.apply(null,o.subarray(e,e+8192));return btoa(r)};exports.encodeBase64=encodeBase64;
|
|
@@ -1,389 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.setImageCompressorImplementation = exports.qualityForFileSize = exports.compressImage = exports.ImageUnreadableError = void 0;
|
|
7
|
-
var _expoFileSystem = require("expo-file-system");
|
|
8
|
-
var _expoImageManipulator = require("expo-image-manipulator");
|
|
9
|
-
var _index = require("../logger/index.js");
|
|
10
|
-
/**
|
|
11
|
-
* Image compression for uploads: the whole shell an app needs around a
|
|
12
|
-
* picked photo, so an app calls this once and uploads what comes back.
|
|
13
|
-
*
|
|
14
|
-
* What it does, in order:
|
|
15
|
-
*
|
|
16
|
-
* 1. Measures the source. A missing or EMPTY file throws `ImageUnreadableError`
|
|
17
|
-
* so the user is asked to retake: the OS evicts a camera temp file between
|
|
18
|
-
* capture and a delayed save, and uploading the empty result is a certain
|
|
19
|
-
* server rejection that can wedge an outbox.
|
|
20
|
-
* 2. Decodes the photo ONCE and reads its size. The longer side is brought
|
|
21
|
-
* down to `maxSize` (1500 by default, the fleet standard); a photo already
|
|
22
|
-
* within it is never enlarged.
|
|
23
|
-
* 3. Re-encodes at a quality picked from the source's real byte size (bigger
|
|
24
|
-
* files are compressed harder), or at the `quality` given. The re-encode
|
|
25
|
-
* drops EXIF as a side effect; location and time travel as payload fields,
|
|
26
|
-
* never add a second stripping step.
|
|
27
|
-
* 4. With `maxBytes`, keeps re-encoding at a lower quality, then a smaller
|
|
28
|
-
* size, until the output fits, deleting each superseded file.
|
|
29
|
-
* 5. Measures the output the same way as the source and returns everything
|
|
30
|
-
* an upload needs: uri, pixel size, byte size, mime type, extension, file
|
|
31
|
-
* name and a ready `FormData` part.
|
|
32
|
-
*
|
|
33
|
-
* Only an ENCODER failure falls back to the original file, reported through
|
|
34
|
-
* `compressed: false` with the original's own type and size; the reference
|
|
35
|
-
* apps upload the original rather than lose the photo. An unreadable file is
|
|
36
|
-
* never a fallback, it throws.
|
|
37
|
-
*
|
|
38
|
-
* Backed by `expo-image-manipulator` and `expo-file-system`; an app wires
|
|
39
|
-
* nothing. Tests and the showcase inject a backend through
|
|
40
|
-
* `setImageCompressorImplementation`.
|
|
41
|
-
*/
|
|
42
|
-
|
|
43
|
-
/** The source was missing or empty, or the encoder produced an empty file. */
|
|
44
|
-
class ImageUnreadableError extends Error {
|
|
45
|
-
constructor(uri, reason) {
|
|
46
|
-
super(`Image is unreadable (${reason}): ${uri || '<empty uri>'}`);
|
|
47
|
-
this.name = 'ImageUnreadableError';
|
|
48
|
-
this.uri = uri;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/** A decoded image, held in memory until `close`. */
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* What the algorithm needs from a backend. The default is built on
|
|
56
|
-
* expo-image-manipulator and expo-file-system.
|
|
57
|
-
*/
|
|
58
|
-
exports.ImageUnreadableError = ImageUnreadableError;
|
|
59
|
-
const release = object => {
|
|
60
|
-
try {
|
|
61
|
-
object?.release?.();
|
|
62
|
-
} catch {
|
|
63
|
-
// A shared object released twice throws; nothing to do.
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* expo-image-manipulator's contextual API plus expo-file-system's File
|
|
69
|
-
* class. `manipulate` accepts the decoded ImageRef as well as a URI (checked
|
|
70
|
-
* in the Android and iOS module sources for SDK 57), which is what makes the
|
|
71
|
-
* single decode possible: every encode, including each step of the byte
|
|
72
|
-
* ceiling ladder, resizes the in-memory image rather than re-reading and
|
|
73
|
-
* re-decoding the file.
|
|
74
|
-
*/
|
|
75
|
-
const expoBackend = {
|
|
76
|
-
stat: async uri => {
|
|
77
|
-
const file = new _expoFileSystem.File(uri);
|
|
78
|
-
if (!file.exists) {
|
|
79
|
-
return {
|
|
80
|
-
exists: false,
|
|
81
|
-
size: 0
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
// Both native modules hand back null when the size attribute cannot be
|
|
85
|
-
// read (a provider-backed URI). That is UNKNOWN, never empty: reading it
|
|
86
|
-
// as 0 would send a user to retake a perfectly good photo.
|
|
87
|
-
return file.size == null ? null : {
|
|
88
|
-
exists: true,
|
|
89
|
-
size: file.size
|
|
90
|
-
};
|
|
91
|
-
},
|
|
92
|
-
remove: async uri => {
|
|
93
|
-
new _expoFileSystem.File(uri).delete();
|
|
94
|
-
},
|
|
95
|
-
open: async uri => {
|
|
96
|
-
const source = _expoImageManipulator.ImageManipulator.manipulate(uri);
|
|
97
|
-
let image;
|
|
98
|
-
try {
|
|
99
|
-
image = await source.renderAsync();
|
|
100
|
-
} finally {
|
|
101
|
-
release(source);
|
|
102
|
-
}
|
|
103
|
-
return {
|
|
104
|
-
width: image.width,
|
|
105
|
-
height: image.height,
|
|
106
|
-
encode: async ({
|
|
107
|
-
width,
|
|
108
|
-
height,
|
|
109
|
-
quality,
|
|
110
|
-
format
|
|
111
|
-
}) => {
|
|
112
|
-
const context = _expoImageManipulator.ImageManipulator.manipulate(image);
|
|
113
|
-
let rendered = null;
|
|
114
|
-
try {
|
|
115
|
-
if (width !== undefined || height !== undefined) {
|
|
116
|
-
context.resize({
|
|
117
|
-
width: width ?? null,
|
|
118
|
-
height: height ?? null
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
rendered = await context.renderAsync();
|
|
122
|
-
const saved = await rendered.saveAsync({
|
|
123
|
-
compress: quality,
|
|
124
|
-
// expo's SaveFormat enum spells the same three strings.
|
|
125
|
-
format: format
|
|
126
|
-
});
|
|
127
|
-
return {
|
|
128
|
-
uri: saved.uri,
|
|
129
|
-
width: saved.width,
|
|
130
|
-
height: saved.height
|
|
131
|
-
};
|
|
132
|
-
} finally {
|
|
133
|
-
release(rendered);
|
|
134
|
-
release(context);
|
|
135
|
-
}
|
|
136
|
-
},
|
|
137
|
-
close: () => release(image)
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
/** Only what a test or the showcase injected; null means the expo backend. */
|
|
143
|
-
let backend = null;
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Replace the backend. Intended for TESTS and for the showcase; an app gets
|
|
147
|
-
* the expo modules without wiring anything. Pass `null` to restore them.
|
|
148
|
-
*/
|
|
149
|
-
const setImageCompressorImplementation = impl => {
|
|
150
|
-
backend = impl;
|
|
151
|
-
};
|
|
152
|
-
exports.setImageCompressorImplementation = setImageCompressorImplementation;
|
|
153
|
-
const resolveBackend = () => backend ?? expoBackend;
|
|
154
|
-
const EIGHT_MB = 8 * 1024 * 1024;
|
|
155
|
-
const THREE_MB = 3 * 1024 * 1024;
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Bigger source files get compressed harder: a fixed quality either
|
|
159
|
-
* over-compresses small photos or leaves large ones too heavy to upload
|
|
160
|
-
* quickly. Unknown size falls back to the middle rung rather than assuming
|
|
161
|
-
* either extreme. The fleet ladder (`react-native.md`).
|
|
162
|
-
*/
|
|
163
|
-
const qualityForFileSize = fileSize => {
|
|
164
|
-
if (fileSize == null) {
|
|
165
|
-
return 0.7;
|
|
166
|
-
}
|
|
167
|
-
if (fileSize > EIGHT_MB) {
|
|
168
|
-
return 0.6;
|
|
169
|
-
}
|
|
170
|
-
if (fileSize > THREE_MB) {
|
|
171
|
-
return 0.7;
|
|
172
|
-
}
|
|
173
|
-
return 0.8;
|
|
174
|
-
};
|
|
175
|
-
exports.qualityForFileSize = qualityForFileSize;
|
|
176
|
-
const DEFAULT_MAX_SIZE = 1500;
|
|
177
|
-
const DEFAULT_MIN_QUALITY = 0.4;
|
|
178
|
-
/** One step of the byte ceiling ladder. */
|
|
179
|
-
const QUALITY_STEP = 0.1;
|
|
180
|
-
/** How much the longer side shrinks per step once quality is at its floor. */
|
|
181
|
-
const SIZE_STEP = 0.75;
|
|
182
|
-
/** The ladder stops shrinking here; below it a document photo is unreadable. */
|
|
183
|
-
const MIN_SIDE = 320;
|
|
184
|
-
const FORMAT = {
|
|
185
|
-
jpg: {
|
|
186
|
-
encode: 'jpeg',
|
|
187
|
-
mimeType: 'image/jpeg'
|
|
188
|
-
},
|
|
189
|
-
png: {
|
|
190
|
-
encode: 'png',
|
|
191
|
-
mimeType: 'image/png'
|
|
192
|
-
},
|
|
193
|
-
webp: {
|
|
194
|
-
encode: 'webp',
|
|
195
|
-
mimeType: 'image/webp'
|
|
196
|
-
}
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
/** The mime type an untouched original is uploaded with, from its extension. */
|
|
200
|
-
const MIME_BY_EXTENSION = {
|
|
201
|
-
jpg: 'image/jpeg',
|
|
202
|
-
jpeg: 'image/jpeg',
|
|
203
|
-
png: 'image/png',
|
|
204
|
-
webp: 'image/webp',
|
|
205
|
-
gif: 'image/gif',
|
|
206
|
-
heic: 'image/heic',
|
|
207
|
-
heif: 'image/heif',
|
|
208
|
-
bmp: 'image/bmp',
|
|
209
|
-
tif: 'image/tiff',
|
|
210
|
-
tiff: 'image/tiff'
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
/** The last path segment of a URI, without query or fragment. */
|
|
214
|
-
const lastSegment = uri => {
|
|
215
|
-
const path = uri.split(/[?#]/)[0] ?? '';
|
|
216
|
-
return decodeURIComponentSafe(path.slice(path.lastIndexOf('/') + 1));
|
|
217
|
-
};
|
|
218
|
-
const decodeURIComponentSafe = value => {
|
|
219
|
-
try {
|
|
220
|
-
return decodeURIComponent(value);
|
|
221
|
-
} catch {
|
|
222
|
-
return value;
|
|
223
|
-
}
|
|
224
|
-
};
|
|
225
|
-
const extensionOf = fileName => {
|
|
226
|
-
const dot = fileName.lastIndexOf('.');
|
|
227
|
-
return dot > 0 ? fileName.slice(dot + 1).toLowerCase() : '';
|
|
228
|
-
};
|
|
229
|
-
const withoutExtension = fileName => {
|
|
230
|
-
const dot = fileName.lastIndexOf('.');
|
|
231
|
-
return dot > 0 ? fileName.slice(0, dot) : fileName;
|
|
232
|
-
};
|
|
233
|
-
const baseNameFor = (uri, name) => withoutExtension(name?.trim() || lastSegment(uri)) || 'image';
|
|
234
|
-
const round1 = value => Math.round(value * 10) / 10;
|
|
235
|
-
|
|
236
|
-
/** A stat that throws (an unsupported scheme) is UNKNOWN, not missing. */
|
|
237
|
-
const safeStat = async (lib, uri) => {
|
|
238
|
-
try {
|
|
239
|
-
return await lib.stat(uri);
|
|
240
|
-
} catch {
|
|
241
|
-
return null;
|
|
242
|
-
}
|
|
243
|
-
};
|
|
244
|
-
const safeRemove = async (lib, uri) => {
|
|
245
|
-
try {
|
|
246
|
-
await lib.remove(uri);
|
|
247
|
-
} catch {
|
|
248
|
-
// A superseded temp file that will not delete is the cache's problem, not the upload's.
|
|
249
|
-
}
|
|
250
|
-
};
|
|
251
|
-
/**
|
|
252
|
-
* Encode the decoded image with its longer side inside `bound`, then measure
|
|
253
|
-
* the file. Only the longer side is given to the encoder: expo treats a given
|
|
254
|
-
* width AND height as targets and would distort the image.
|
|
255
|
-
*/
|
|
256
|
-
const renderWithin = async (lib, image, bound, quality, format) => {
|
|
257
|
-
const longer = Math.max(image.width, image.height);
|
|
258
|
-
const resize = longer > bound ? image.width >= image.height ? {
|
|
259
|
-
width: bound
|
|
260
|
-
} : {
|
|
261
|
-
height: bound
|
|
262
|
-
} : {};
|
|
263
|
-
const encoded = await image.encode({
|
|
264
|
-
...resize,
|
|
265
|
-
quality,
|
|
266
|
-
format
|
|
267
|
-
});
|
|
268
|
-
const stat = await safeStat(lib, encoded.uri);
|
|
269
|
-
if (stat && (!stat.exists || stat.size === 0)) {
|
|
270
|
-
await safeRemove(lib, encoded.uri);
|
|
271
|
-
throw new ImageUnreadableError(encoded.uri, 'the encoder wrote an empty file');
|
|
272
|
-
}
|
|
273
|
-
return {
|
|
274
|
-
...encoded,
|
|
275
|
-
size: stat?.size ?? null
|
|
276
|
-
};
|
|
277
|
-
};
|
|
278
|
-
const originalResult = (uri, size, baseName, dimensions) => {
|
|
279
|
-
const extension = extensionOf(lastSegment(uri)) || 'jpg';
|
|
280
|
-
const mimeType = MIME_BY_EXTENSION[extension] ?? 'image/jpeg';
|
|
281
|
-
const fileName = `${baseName}.${extension}`;
|
|
282
|
-
return {
|
|
283
|
-
uri,
|
|
284
|
-
width: dimensions?.width ?? null,
|
|
285
|
-
height: dimensions?.height ?? null,
|
|
286
|
-
size,
|
|
287
|
-
mimeType,
|
|
288
|
-
extension,
|
|
289
|
-
fileName,
|
|
290
|
-
compressed: false,
|
|
291
|
-
quality: null,
|
|
292
|
-
part: {
|
|
293
|
-
uri,
|
|
294
|
-
name: fileName,
|
|
295
|
-
type: mimeType
|
|
296
|
-
}
|
|
297
|
-
};
|
|
298
|
-
};
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* Compress an image for upload. See the module comment for the contract.
|
|
302
|
-
*
|
|
303
|
-
* @throws ImageUnreadableError when the source is missing or empty, or the
|
|
304
|
-
* encoder produced an empty file. Any other failure returns the original with
|
|
305
|
-
* `compressed: false`.
|
|
306
|
-
*/
|
|
307
|
-
const compressImage = async (uri, options = {}) => {
|
|
308
|
-
if (!uri) {
|
|
309
|
-
throw new ImageUnreadableError(uri, 'no uri');
|
|
310
|
-
}
|
|
311
|
-
const lib = resolveBackend();
|
|
312
|
-
const {
|
|
313
|
-
maxSize = DEFAULT_MAX_SIZE,
|
|
314
|
-
output = 'jpg',
|
|
315
|
-
maxBytes,
|
|
316
|
-
minQuality = DEFAULT_MIN_QUALITY,
|
|
317
|
-
name
|
|
318
|
-
} = options;
|
|
319
|
-
const format = FORMAT[output] ?? FORMAT.jpg;
|
|
320
|
-
const baseName = baseNameFor(uri, name);
|
|
321
|
-
const source = await safeStat(lib, uri);
|
|
322
|
-
if (source && !source.exists) {
|
|
323
|
-
throw new ImageUnreadableError(uri, 'the file does not exist');
|
|
324
|
-
}
|
|
325
|
-
if (source && source.size === 0) {
|
|
326
|
-
throw new ImageUnreadableError(uri, 'the file is empty');
|
|
327
|
-
}
|
|
328
|
-
const sourceSize = source?.size ?? options.fileSize ?? null;
|
|
329
|
-
let image;
|
|
330
|
-
try {
|
|
331
|
-
image = await lib.open(uri);
|
|
332
|
-
} catch (error) {
|
|
333
|
-
_index.Logger.error('[imageCompression] could not decode the image; uploading the original', error);
|
|
334
|
-
return originalResult(uri, sourceSize, baseName, null);
|
|
335
|
-
}
|
|
336
|
-
try {
|
|
337
|
-
let quality = options.quality ?? qualityForFileSize(sourceSize);
|
|
338
|
-
let bound = maxSize;
|
|
339
|
-
let rendered = await renderWithin(lib, image, bound, quality, format.encode);
|
|
340
|
-
if (maxBytes !== undefined) {
|
|
341
|
-
// Quality first, then size: a smaller file at the same pixel size
|
|
342
|
-
// keeps the document legible longer than a shrunken one.
|
|
343
|
-
while (rendered.size !== null && rendered.size > maxBytes) {
|
|
344
|
-
if (output !== 'png' && round1(quality - QUALITY_STEP) >= minQuality) {
|
|
345
|
-
quality = round1(quality - QUALITY_STEP);
|
|
346
|
-
} else {
|
|
347
|
-
const longer = Math.max(rendered.width, rendered.height);
|
|
348
|
-
if (longer <= MIN_SIDE) {
|
|
349
|
-
break;
|
|
350
|
-
}
|
|
351
|
-
bound = Math.max(MIN_SIDE, Math.floor(longer * SIZE_STEP));
|
|
352
|
-
}
|
|
353
|
-
const next = await renderWithin(lib, image, bound, quality, format.encode);
|
|
354
|
-
await safeRemove(lib, rendered.uri);
|
|
355
|
-
rendered = next;
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
const fileName = `${baseName}.${output}`;
|
|
359
|
-
return {
|
|
360
|
-
uri: rendered.uri,
|
|
361
|
-
width: rendered.width,
|
|
362
|
-
height: rendered.height,
|
|
363
|
-
size: rendered.size,
|
|
364
|
-
mimeType: format.mimeType,
|
|
365
|
-
extension: output,
|
|
366
|
-
fileName,
|
|
367
|
-
compressed: true,
|
|
368
|
-
quality: output === 'png' ? null : quality,
|
|
369
|
-
part: {
|
|
370
|
-
uri: rendered.uri,
|
|
371
|
-
name: fileName,
|
|
372
|
-
type: format.mimeType
|
|
373
|
-
}
|
|
374
|
-
};
|
|
375
|
-
} catch (error) {
|
|
376
|
-
if (error instanceof ImageUnreadableError) {
|
|
377
|
-
throw error;
|
|
378
|
-
}
|
|
379
|
-
_index.Logger.error('[imageCompression] encoding failed; uploading the original', error);
|
|
380
|
-
return originalResult(uri, sourceSize, baseName, {
|
|
381
|
-
width: image.width,
|
|
382
|
-
height: image.height
|
|
383
|
-
});
|
|
384
|
-
} finally {
|
|
385
|
-
image.close();
|
|
386
|
-
}
|
|
387
|
-
};
|
|
388
|
-
exports.compressImage = compressImage;
|
|
389
|
-
//# sourceMappingURL=imageCompression.js.map
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.setImageCompressorImplementation=exports.qualityForFileSize=exports.compressImage=exports.ImageUnreadableError=void 0;var _expoFileSystem=require("expo-file-system"),_expoImageManipulator=require("expo-image-manipulator"),_index=require("../logger/index.js");class ImageUnreadableError extends Error{constructor(e,t){super(`Image is unreadable (${t}): ${e||"<empty uri>"}`),this.name="ImageUnreadableError",this.uri=e}}exports.ImageUnreadableError=ImageUnreadableError;const release=e=>{try{e?.release?.()}catch{}},expoBackend={stat:async e=>{const t=new _expoFileSystem.File(e);return t.exists?null==t.size?null:{exists:!0,size:t.size}:{exists:!1,size:0}},remove:async e=>{new _expoFileSystem.File(e).delete()},open:async e=>{const t=_expoImageManipulator.ImageManipulator.manipulate(e);let i;try{i=await t.renderAsync()}finally{release(t)}return{width:i.width,height:i.height,encode:async({width:e,height:t,quality:a,format:r})=>{const n=_expoImageManipulator.ImageManipulator.manipulate(i);let o=null;try{void 0===e&&void 0===t||n.resize({width:e??null,height:t??null}),o=await n.renderAsync();const i=await o.saveAsync({compress:a,format:r});return{uri:i.uri,width:i.width,height:i.height}}finally{release(o),release(n)}},close:()=>release(i)}}};let backend=null;const setImageCompressorImplementation=e=>{backend=e};exports.setImageCompressorImplementation=setImageCompressorImplementation;const resolveBackend=()=>backend??expoBackend,EIGHT_MB=8388608,THREE_MB=3145728,qualityForFileSize=e=>null==e?.7:e>8388608?.6:e>3145728?.7:.8;exports.qualityForFileSize=qualityForFileSize;const DEFAULT_MAX_SIZE=1500,DEFAULT_MIN_QUALITY=.4,QUALITY_STEP=.1,SIZE_STEP=.75,MIN_SIDE=320,FORMAT={jpg:{encode:"jpeg",mimeType:"image/jpeg"},png:{encode:"png",mimeType:"image/png"},webp:{encode:"webp",mimeType:"image/webp"}},MIME_BY_EXTENSION={jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",webp:"image/webp",gif:"image/gif",heic:"image/heic",heif:"image/heif",bmp:"image/bmp",tif:"image/tiff",tiff:"image/tiff"},lastSegment=e=>{const t=e.split(/[?#]/)[0]??"";return decodeURIComponentSafe(t.slice(t.lastIndexOf("/")+1))},decodeURIComponentSafe=e=>{try{return decodeURIComponent(e)}catch{return e}},extensionOf=e=>{const t=e.lastIndexOf(".");return t>0?e.slice(t+1).toLowerCase():""},withoutExtension=e=>{const t=e.lastIndexOf(".");return t>0?e.slice(0,t):e},baseNameFor=(e,t)=>withoutExtension(t?.trim()||lastSegment(e))||"image",round1=e=>Math.round(10*e)/10,safeStat=async(e,t)=>{try{return await e.stat(t)}catch{return null}},safeRemove=async(e,t)=>{try{await e.remove(t)}catch{}},renderWithin=async(e,t,i,a,r)=>{const n=Math.max(t.width,t.height)>i?t.width>=t.height?{width:i}:{height:i}:{},o=await t.encode({...n,quality:a,format:r}),s=await safeStat(e,o.uri);if(s&&(!s.exists||0===s.size))throw await safeRemove(e,o.uri),new ImageUnreadableError(o.uri,"the encoder wrote an empty file");return{...o,size:s?.size??null}},originalResult=(e,t,i,a)=>{const r=extensionOf(lastSegment(e))||"jpg",n=MIME_BY_EXTENSION[r]??"image/jpeg",o=`${i}.${r}`;return{uri:e,width:a?.width??null,height:a?.height??null,size:t,mimeType:n,extension:r,fileName:o,compressed:!1,quality:null,part:{uri:e,name:o,type:n}}},compressImage=async(e,t={})=>{if(!e)throw new ImageUnreadableError(e,"no uri");const i=resolveBackend(),{maxSize:a=DEFAULT_MAX_SIZE,output:r="jpg",maxBytes:n,minQuality:o=DEFAULT_MIN_QUALITY,name:s}=t,l=FORMAT[r]??FORMAT.jpg,m=baseNameFor(e,s),g=await safeStat(i,e);if(g&&!g.exists)throw new ImageUnreadableError(e,"the file does not exist");if(g&&0===g.size)throw new ImageUnreadableError(e,"the file is empty");const p=g?.size??t.fileSize??null;let u;try{u=await i.open(e)}catch(t){return _index.Logger.error("[imageCompression] could not decode the image; uploading the original",t),originalResult(e,p,m,null)}try{let e=t.quality??qualityForFileSize(p),s=a,g=await renderWithin(i,u,s,e,l.encode);if(void 0!==n)for(;null!==g.size&&g.size>n;){if("png"!==r&&round1(e-.1)>=o)e=round1(e-.1);else{const e=Math.max(g.width,g.height);if(e<=320)break;s=Math.max(320,Math.floor(.75*e))}const t=await renderWithin(i,u,s,e,l.encode);await safeRemove(i,g.uri),g=t}const c=`${m}.${r}`;return{uri:g.uri,width:g.width,height:g.height,size:g.size,mimeType:l.mimeType,extension:r,fileName:c,compressed:!0,quality:"png"===r?null:e,part:{uri:g.uri,name:c,type:l.mimeType}}}catch(t){if(t instanceof ImageUnreadableError)throw t;return _index.Logger.error("[imageCompression] encoding failed; uploading the original",t),originalResult(e,p,m,{width:u.width,height:u.height})}finally{u.close()}};exports.compressImage=compressImage;
|
|
@@ -1,19 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
Object.defineProperty(exports, "compressImage", {
|
|
7
|
-
enumerable: true,
|
|
8
|
-
get: function () {
|
|
9
|
-
return _imageCompression.compressImage;
|
|
10
|
-
}
|
|
11
|
-
});
|
|
12
|
-
Object.defineProperty(exports, "setImageCompressorImplementation", {
|
|
13
|
-
enumerable: true,
|
|
14
|
-
get: function () {
|
|
15
|
-
return _imageCompression.setImageCompressorImplementation;
|
|
16
|
-
}
|
|
17
|
-
});
|
|
18
|
-
var _imageCompression = require("./imageCompression.js");
|
|
19
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),Object.defineProperty(exports,"compressImage",{enumerable:!0,get:function(){return _imageCompression.compressImage}}),Object.defineProperty(exports,"setImageCompressorImplementation",{enumerable:!0,get:function(){return _imageCompression.setImageCompressorImplementation}});var _imageCompression=require("./imageCompression.js");
|