@s3p-js-deep-purple/utils 0.0.1-security → 1.1.99
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.
Potentially problematic release.
This version of @s3p-js-deep-purple/utils might be problematic. Click here for more details.
- package/.babelrc.json +3 -0
- package/.idea/modules.xml +8 -0
- package/.idea/s3-js-deep-purple-lib-utils.iml +12 -0
- package/README.md +13 -3
- package/bin/ascii-art.txt +36 -0
- package/bin/preinstall.sh +11 -0
- package/bin/pwned.txt +9 -0
- package/dist/declarations/src/array.d.ts +6 -0
- package/dist/declarations/src/casing.d.ts +8 -0
- package/dist/declarations/src/index.d.ts +10 -0
- package/dist/declarations/src/is-empty.d.ts +2 -0
- package/dist/declarations/src/number.d.ts +5 -0
- package/dist/declarations/src/object.d.ts +8 -0
- package/dist/declarations/src/string.d.ts +6 -0
- package/dist/declarations/src/to-hash.d.ts +2 -0
- package/dist/declarations/src/to-path.d.ts +3 -0
- package/dist/declarations/src/types.d.ts +15 -0
- package/dist/declarations/src/url.d.ts +4 -0
- package/dist/s3p-js-deep-purple-utils.cjs.d.ts +1 -0
- package/dist/s3p-js-deep-purple-utils.cjs.dev.js +310 -0
- package/dist/s3p-js-deep-purple-utils.cjs.js +7 -0
- package/dist/s3p-js-deep-purple-utils.cjs.prod.js +310 -0
- package/dist/s3p-js-deep-purple-utils.esm.js +260 -0
- package/jest.config.coverage.json +6 -0
- package/package.json +23 -3
- package/src/array.js +24 -0
- package/src/casing.ts +70 -0
- package/src/index.ts +10 -0
- package/src/is-empty.js +8 -0
- package/src/number.js +40 -0
- package/src/object.ts +45 -0
- package/src/string.js +73 -0
- package/src/to-hash.ts +3 -0
- package/src/to-path.ts +6 -0
- package/src/types.ts +24 -0
- package/src/url.js +22 -0
- package/tests/__snapshots__/casing.spec.js.snap +35 -0
- package/tests/casing.spec.js +29 -0
- package/tests/number.spec.js +67 -0
- package/tests/object.spec.ts +77 -0
- package/tests/string.spec.js +77 -0
- package/tsconfig.json +7 -0
@@ -0,0 +1,310 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
4
|
+
|
5
|
+
var get = require('lodash/get');
|
6
|
+
var isEmpty = require('lodash/isEmpty');
|
7
|
+
var isBoolean = require('lodash/isBoolean');
|
8
|
+
var isNumber = require('lodash/isNumber');
|
9
|
+
var isPlainObject = require('lodash/isPlainObject');
|
10
|
+
var transform = require('lodash/transform');
|
11
|
+
var set = require('lodash/set');
|
12
|
+
var isObjectLike = require('lodash/isObjectLike');
|
13
|
+
var camelCase = require('lodash/camelCase');
|
14
|
+
var snakeCase = require('lodash/snakeCase');
|
15
|
+
var isObject = require('lodash/isObject');
|
16
|
+
var isArray = require('lodash/isArray');
|
17
|
+
var isNil = require('lodash/isNil');
|
18
|
+
var reduce = require('lodash/reduce');
|
19
|
+
var qs = require('qs');
|
20
|
+
|
21
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
|
22
|
+
|
23
|
+
var get__default = /*#__PURE__*/_interopDefault(get);
|
24
|
+
var isEmpty__default = /*#__PURE__*/_interopDefault(isEmpty);
|
25
|
+
var isBoolean__default = /*#__PURE__*/_interopDefault(isBoolean);
|
26
|
+
var isNumber__default = /*#__PURE__*/_interopDefault(isNumber);
|
27
|
+
var isPlainObject__default = /*#__PURE__*/_interopDefault(isPlainObject);
|
28
|
+
var transform__default = /*#__PURE__*/_interopDefault(transform);
|
29
|
+
var set__default = /*#__PURE__*/_interopDefault(set);
|
30
|
+
var isObjectLike__default = /*#__PURE__*/_interopDefault(isObjectLike);
|
31
|
+
var camelCase__default = /*#__PURE__*/_interopDefault(camelCase);
|
32
|
+
var snakeCase__default = /*#__PURE__*/_interopDefault(snakeCase);
|
33
|
+
var isObject__default = /*#__PURE__*/_interopDefault(isObject);
|
34
|
+
var isArray__default = /*#__PURE__*/_interopDefault(isArray);
|
35
|
+
var isNil__default = /*#__PURE__*/_interopDefault(isNil);
|
36
|
+
var reduce__default = /*#__PURE__*/_interopDefault(reduce);
|
37
|
+
var qs__default = /*#__PURE__*/_interopDefault(qs);
|
38
|
+
|
39
|
+
const toPath = path => Array.isArray(path) ? path.filter(Boolean).join('.') : path || '';
|
40
|
+
|
41
|
+
const unique = array => Array.from(new Set(array));
|
42
|
+
const pluck = (array, field) => array.map(obj => get__default["default"](obj || {}, field));
|
43
|
+
const pluckUnique = function () {
|
44
|
+
return unique(pluck(...arguments));
|
45
|
+
};
|
46
|
+
const flatMap = (array, field) => array.reduce((newArray, obj) => {
|
47
|
+
newArray.push(...(obj || {})[field]);
|
48
|
+
return newArray;
|
49
|
+
}, []);
|
50
|
+
const indexByKeyValue = (items, propKey, propValue) => (items || []).reduce((object, item) => {
|
51
|
+
const key = item[propKey];
|
52
|
+
|
53
|
+
if (key) {
|
54
|
+
object[key] = propValue ? item[propValue] : item;
|
55
|
+
}
|
56
|
+
|
57
|
+
return object;
|
58
|
+
}, {});
|
59
|
+
const lowestValue = (array, field) => Math.min(...array.map(object => object[field]));
|
60
|
+
|
61
|
+
const isEmptyValue = value => value === undefined || typeof value === 'number' && Number.isNaN(value) || typeof value === 'string' && value.trim().length === 0 || Array.isArray(value) && value.length === 0 || value === null;
|
62
|
+
const hasValue = value => !isEmptyValue(value);
|
63
|
+
|
64
|
+
/**
|
65
|
+
* Abbreviate a sentence.
|
66
|
+
* John Doe becomes J.D.
|
67
|
+
* @param sentence
|
68
|
+
* @return {string}
|
69
|
+
*/
|
70
|
+
|
71
|
+
const abbreviate = sentence => sentence.trim().split(' ').map(word => word.length ? `${word[0].toUpperCase()}.` : '').join('');
|
72
|
+
/**
|
73
|
+
* Remove spaces from a string
|
74
|
+
* @param value
|
75
|
+
* @returns {string}
|
76
|
+
*/
|
77
|
+
|
78
|
+
const removeSpaces = value => typeof value === 'string' ? value.replace(/\s/g, '') : value;
|
79
|
+
/**
|
80
|
+
* trim spaces on both ends from a string
|
81
|
+
* @param value
|
82
|
+
* @returns {string}
|
83
|
+
*/
|
84
|
+
|
85
|
+
const trimSpaces = value => typeof value === 'string' ? value.trim() : value;
|
86
|
+
/**
|
87
|
+
* A value is 'blank' if it's null, undefined, an empty array [], empty object {}, empty set or empty map.
|
88
|
+
* The difference with lodash's isEmpty, is that it evaluates numbers and booleans as truthy.
|
89
|
+
* @example
|
90
|
+
*
|
91
|
+
* isBlank(null)
|
92
|
+
* // => true
|
93
|
+
*
|
94
|
+
* isBlank({})
|
95
|
+
* // => true
|
96
|
+
*
|
97
|
+
* isBlank([])
|
98
|
+
* // => true
|
99
|
+
*
|
100
|
+
* isBlank(new Map())
|
101
|
+
* // => true
|
102
|
+
*
|
103
|
+
* isBlank(false)
|
104
|
+
* // => false
|
105
|
+
*
|
106
|
+
* isBlank(1);
|
107
|
+
* // => false
|
108
|
+
*
|
109
|
+
* isBlank([1, 2, 3])
|
110
|
+
* // => false
|
111
|
+
*
|
112
|
+
* isBlank({ 'a': 1 })
|
113
|
+
* // => false
|
114
|
+
*
|
115
|
+
* @param value
|
116
|
+
* @returns {boolean}
|
117
|
+
*/
|
118
|
+
|
119
|
+
const isBlank = value => Boolean(isEmpty__default["default"](value) && !isBoolean__default["default"](value) && !isNumber__default["default"](value));
|
120
|
+
const parseJWT = token => {
|
121
|
+
const base64Url = token.split('.')[1];
|
122
|
+
const base64 = base64Url.replace('-', '+').replace('_', '/');
|
123
|
+
return JSON.parse(window.atob(base64));
|
124
|
+
};
|
125
|
+
const convertUnicode = value => value.replace(/\\u(\w\w\w\w)/g, (_, match) => String.fromCharCode(parseInt(match, 16)));
|
126
|
+
|
127
|
+
const toHash = function () {
|
128
|
+
let value = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '/';
|
129
|
+
return value.replace(/[/.]/g, '|');
|
130
|
+
};
|
131
|
+
|
132
|
+
/**
|
133
|
+
* Handle floating point precision. 0.2 + 0.1 = 0.30000000000000004
|
134
|
+
* @param {Number|String} value
|
135
|
+
* @return {Number}
|
136
|
+
*/
|
137
|
+
const toNumeric = value => Number(parseFloat(value).toPrecision(12));
|
138
|
+
/**
|
139
|
+
* Cap a value between a min and max value, so it stays within bounds
|
140
|
+
* @param {Number} value
|
141
|
+
* @param {Number} minValue
|
142
|
+
* @param {Number} maxValue
|
143
|
+
* @return {Number}
|
144
|
+
*/
|
145
|
+
|
146
|
+
const clamp = function () {
|
147
|
+
let value = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
|
148
|
+
let minValue = arguments.length > 1 ? arguments[1] : undefined;
|
149
|
+
let maxValue = arguments.length > 2 ? arguments[2] : undefined;
|
150
|
+
return Math.min(Math.max(toNumeric(value), minValue), maxValue);
|
151
|
+
};
|
152
|
+
/**
|
153
|
+
* Check if the value is a number according to React number input validation
|
154
|
+
* @param value
|
155
|
+
* @return {Boolean}
|
156
|
+
*/
|
157
|
+
|
158
|
+
const isReactNumeric = function () {
|
159
|
+
let value = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
|
160
|
+
return String(value).match(/^-?(\d+|\d+\.\d+|\.\d+)([eE][-+]?\d+)?$/) !== null;
|
161
|
+
};
|
162
|
+
/**
|
163
|
+
* Check if the value can become a number according to React number input validation
|
164
|
+
* @param value
|
165
|
+
* @return {Boolean}
|
166
|
+
*/
|
167
|
+
|
168
|
+
const isReactNumericIncomplete = function () {
|
169
|
+
let value = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
|
170
|
+
return String(value).match(/(?!^-?\d+$)^-?(\d+\.?)([eE][-+]?)?$/) !== null;
|
171
|
+
};
|
172
|
+
/**
|
173
|
+
* Transforms a scientific number (1e7) to a string (0.000001)
|
174
|
+
* @param value
|
175
|
+
* @return {string}
|
176
|
+
*/
|
177
|
+
|
178
|
+
const toFixed = value => Number(value).toFixed(20).replace(/\.?0+$/, '');
|
179
|
+
|
180
|
+
// eslint-disable-next-line no-warning-comments
|
181
|
+
// todo: this does not belong in a generic utils lib, move this to implementation
|
182
|
+
const s3ApiKeyMap = [['_u_i_c_station_code', 'UICStationCode'], ['origin_uic', 'originUIC'], ['destination_uic', 'destinationUIC']];
|
183
|
+
const camelCaseInflectorMap = new Map(s3ApiKeyMap);
|
184
|
+
const snakeCaseInflectorMap = s3ApiKeyMap.reduce((map, inflection) => {
|
185
|
+
return map.set(inflection[1], inflection[0]);
|
186
|
+
}, new Map());
|
187
|
+
|
188
|
+
const convertWithInflectorCreator = (inflector, keyConverter) => key => inflector.has(key) ? inflector.get(key) : keyConverter(key);
|
189
|
+
|
190
|
+
const deepConvertKeys = function (keyConverter) {
|
191
|
+
let whiteListChildren = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
|
192
|
+
|
193
|
+
const convertKeys = (node, parentKey) => {
|
194
|
+
if (Array.isArray(node)) return node.map(convertKeys);
|
195
|
+
|
196
|
+
if (isPlainObject__default["default"](node)) {
|
197
|
+
return transform__default["default"](node, (result, value, key) => {
|
198
|
+
const _key = whiteListChildren.includes(parentKey) ? key : keyConverter(key);
|
199
|
+
|
200
|
+
return set__default["default"](result, _key, isObjectLike__default["default"](value) ? convertKeys(value, _key) : value);
|
201
|
+
});
|
202
|
+
}
|
203
|
+
|
204
|
+
return node;
|
205
|
+
};
|
206
|
+
|
207
|
+
return convertKeys;
|
208
|
+
};
|
209
|
+
const deepConvertValues = valueConvertor => {
|
210
|
+
let originalNode;
|
211
|
+
|
212
|
+
const convertValues = node => {
|
213
|
+
originalNode = originalNode || node;
|
214
|
+
if (Array.isArray(node)) return node.map(convertValues);
|
215
|
+
|
216
|
+
if (isPlainObject__default["default"](node)) {
|
217
|
+
return transform__default["default"](node, (result, value, key) => {
|
218
|
+
const _value = isObjectLike__default["default"](value) ? convertValues(value) : valueConvertor(key, value, originalNode);
|
219
|
+
|
220
|
+
return set__default["default"](result, key, _value);
|
221
|
+
});
|
222
|
+
}
|
223
|
+
|
224
|
+
return node;
|
225
|
+
};
|
226
|
+
|
227
|
+
return convertValues;
|
228
|
+
};
|
229
|
+
const deepCamelCaseKeys = deepConvertKeys(convertWithInflectorCreator(camelCaseInflectorMap, camelCase__default["default"]));
|
230
|
+
const deepSnakeCaseKeys = deepConvertKeys(convertWithInflectorCreator(snakeCaseInflectorMap, snakeCase__default["default"]));
|
231
|
+
|
232
|
+
const omitByPredicateDeep = (obj, predicate) => {
|
233
|
+
return reduce__default["default"](obj, (result, value, key) => {
|
234
|
+
if (isObject__default["default"](value) || isArray__default["default"](value)) {
|
235
|
+
if (isArray__default["default"](result)) {
|
236
|
+
result.push(omitByPredicateDeep(value, predicate));
|
237
|
+
} else {
|
238
|
+
result[key] = omitByPredicateDeep(value, predicate);
|
239
|
+
}
|
240
|
+
} else if (!predicate(value)) {
|
241
|
+
if (isArray__default["default"](result)) {
|
242
|
+
result.push(value);
|
243
|
+
} else {
|
244
|
+
result[key] = value;
|
245
|
+
}
|
246
|
+
}
|
247
|
+
|
248
|
+
return result;
|
249
|
+
}, isArray__default["default"](obj) ? [] : {});
|
250
|
+
};
|
251
|
+
/**
|
252
|
+
* Recursively remove all nil values deeply nested properties of objects and arrays
|
253
|
+
*
|
254
|
+
* note: do not use for cyclical object or Date properties
|
255
|
+
*/
|
256
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
257
|
+
|
258
|
+
const omitNilDeep = obj => omitByPredicateDeep(obj, value => isNil__default["default"](value));
|
259
|
+
|
260
|
+
const stringifyQueryEncode = query => qs__default["default"].stringify(query, {
|
261
|
+
encode: true,
|
262
|
+
skipNulls: true
|
263
|
+
});
|
264
|
+
const parseQueryString = queryString => qs__default["default"].parse(queryString, {
|
265
|
+
ignoreQueryPrefix: true,
|
266
|
+
decoder: (value, decoder) => {
|
267
|
+
const keywords = {
|
268
|
+
true: true,
|
269
|
+
false: false,
|
270
|
+
null: null,
|
271
|
+
undefined
|
272
|
+
};
|
273
|
+
|
274
|
+
if (value in keywords) {
|
275
|
+
return keywords[value];
|
276
|
+
}
|
277
|
+
|
278
|
+
return decoder(value);
|
279
|
+
}
|
280
|
+
});
|
281
|
+
|
282
|
+
exports.abbreviate = abbreviate;
|
283
|
+
exports.clamp = clamp;
|
284
|
+
exports.convertUnicode = convertUnicode;
|
285
|
+
exports.deepCamelCaseKeys = deepCamelCaseKeys;
|
286
|
+
exports.deepConvertKeys = deepConvertKeys;
|
287
|
+
exports.deepConvertValues = deepConvertValues;
|
288
|
+
exports.deepSnakeCaseKeys = deepSnakeCaseKeys;
|
289
|
+
exports.flatMap = flatMap;
|
290
|
+
exports.hasValue = hasValue;
|
291
|
+
exports.indexByKeyValue = indexByKeyValue;
|
292
|
+
exports.isBlank = isBlank;
|
293
|
+
exports.isEmptyValue = isEmptyValue;
|
294
|
+
exports.isReactNumeric = isReactNumeric;
|
295
|
+
exports.isReactNumericIncomplete = isReactNumericIncomplete;
|
296
|
+
exports.lowestValue = lowestValue;
|
297
|
+
exports.omitByPredicateDeep = omitByPredicateDeep;
|
298
|
+
exports.omitNilDeep = omitNilDeep;
|
299
|
+
exports.parseJWT = parseJWT;
|
300
|
+
exports.parseQueryString = parseQueryString;
|
301
|
+
exports.pluck = pluck;
|
302
|
+
exports.pluckUnique = pluckUnique;
|
303
|
+
exports.removeSpaces = removeSpaces;
|
304
|
+
exports.stringifyQueryEncode = stringifyQueryEncode;
|
305
|
+
exports.toFixed = toFixed;
|
306
|
+
exports.toHash = toHash;
|
307
|
+
exports.toNumeric = toNumeric;
|
308
|
+
exports.toPath = toPath;
|
309
|
+
exports.trimSpaces = trimSpaces;
|
310
|
+
exports.unique = unique;
|
@@ -0,0 +1,260 @@
|
|
1
|
+
import get from 'lodash/get';
|
2
|
+
import isEmpty from 'lodash/isEmpty';
|
3
|
+
import isBoolean from 'lodash/isBoolean';
|
4
|
+
import isNumber from 'lodash/isNumber';
|
5
|
+
import isPlainObject from 'lodash/isPlainObject';
|
6
|
+
import transform from 'lodash/transform';
|
7
|
+
import set from 'lodash/set';
|
8
|
+
import isObjectLike from 'lodash/isObjectLike';
|
9
|
+
import camelCase from 'lodash/camelCase';
|
10
|
+
import snakeCase from 'lodash/snakeCase';
|
11
|
+
import isObject from 'lodash/isObject';
|
12
|
+
import isArray from 'lodash/isArray';
|
13
|
+
import isNil from 'lodash/isNil';
|
14
|
+
import reduce from 'lodash/reduce';
|
15
|
+
import qs from 'qs';
|
16
|
+
|
17
|
+
const toPath = path => Array.isArray(path) ? path.filter(Boolean).join('.') : path || '';
|
18
|
+
|
19
|
+
const unique = array => Array.from(new Set(array));
|
20
|
+
const pluck = (array, field) => array.map(obj => get(obj || {}, field));
|
21
|
+
const pluckUnique = function () {
|
22
|
+
return unique(pluck(...arguments));
|
23
|
+
};
|
24
|
+
const flatMap = (array, field) => array.reduce((newArray, obj) => {
|
25
|
+
newArray.push(...(obj || {})[field]);
|
26
|
+
return newArray;
|
27
|
+
}, []);
|
28
|
+
const indexByKeyValue = (items, propKey, propValue) => (items || []).reduce((object, item) => {
|
29
|
+
const key = item[propKey];
|
30
|
+
|
31
|
+
if (key) {
|
32
|
+
object[key] = propValue ? item[propValue] : item;
|
33
|
+
}
|
34
|
+
|
35
|
+
return object;
|
36
|
+
}, {});
|
37
|
+
const lowestValue = (array, field) => Math.min(...array.map(object => object[field]));
|
38
|
+
|
39
|
+
const isEmptyValue = value => value === undefined || typeof value === 'number' && Number.isNaN(value) || typeof value === 'string' && value.trim().length === 0 || Array.isArray(value) && value.length === 0 || value === null;
|
40
|
+
const hasValue = value => !isEmptyValue(value);
|
41
|
+
|
42
|
+
/**
|
43
|
+
* Abbreviate a sentence.
|
44
|
+
* John Doe becomes J.D.
|
45
|
+
* @param sentence
|
46
|
+
* @return {string}
|
47
|
+
*/
|
48
|
+
|
49
|
+
const abbreviate = sentence => sentence.trim().split(' ').map(word => word.length ? `${word[0].toUpperCase()}.` : '').join('');
|
50
|
+
/**
|
51
|
+
* Remove spaces from a string
|
52
|
+
* @param value
|
53
|
+
* @returns {string}
|
54
|
+
*/
|
55
|
+
|
56
|
+
const removeSpaces = value => typeof value === 'string' ? value.replace(/\s/g, '') : value;
|
57
|
+
/**
|
58
|
+
* trim spaces on both ends from a string
|
59
|
+
* @param value
|
60
|
+
* @returns {string}
|
61
|
+
*/
|
62
|
+
|
63
|
+
const trimSpaces = value => typeof value === 'string' ? value.trim() : value;
|
64
|
+
/**
|
65
|
+
* A value is 'blank' if it's null, undefined, an empty array [], empty object {}, empty set or empty map.
|
66
|
+
* The difference with lodash's isEmpty, is that it evaluates numbers and booleans as truthy.
|
67
|
+
* @example
|
68
|
+
*
|
69
|
+
* isBlank(null)
|
70
|
+
* // => true
|
71
|
+
*
|
72
|
+
* isBlank({})
|
73
|
+
* // => true
|
74
|
+
*
|
75
|
+
* isBlank([])
|
76
|
+
* // => true
|
77
|
+
*
|
78
|
+
* isBlank(new Map())
|
79
|
+
* // => true
|
80
|
+
*
|
81
|
+
* isBlank(false)
|
82
|
+
* // => false
|
83
|
+
*
|
84
|
+
* isBlank(1);
|
85
|
+
* // => false
|
86
|
+
*
|
87
|
+
* isBlank([1, 2, 3])
|
88
|
+
* // => false
|
89
|
+
*
|
90
|
+
* isBlank({ 'a': 1 })
|
91
|
+
* // => false
|
92
|
+
*
|
93
|
+
* @param value
|
94
|
+
* @returns {boolean}
|
95
|
+
*/
|
96
|
+
|
97
|
+
const isBlank = value => Boolean(isEmpty(value) && !isBoolean(value) && !isNumber(value));
|
98
|
+
const parseJWT = token => {
|
99
|
+
const base64Url = token.split('.')[1];
|
100
|
+
const base64 = base64Url.replace('-', '+').replace('_', '/');
|
101
|
+
return JSON.parse(window.atob(base64));
|
102
|
+
};
|
103
|
+
const convertUnicode = value => value.replace(/\\u(\w\w\w\w)/g, (_, match) => String.fromCharCode(parseInt(match, 16)));
|
104
|
+
|
105
|
+
const toHash = function () {
|
106
|
+
let value = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '/';
|
107
|
+
return value.replace(/[/.]/g, '|');
|
108
|
+
};
|
109
|
+
|
110
|
+
/**
|
111
|
+
* Handle floating point precision. 0.2 + 0.1 = 0.30000000000000004
|
112
|
+
* @param {Number|String} value
|
113
|
+
* @return {Number}
|
114
|
+
*/
|
115
|
+
const toNumeric = value => Number(parseFloat(value).toPrecision(12));
|
116
|
+
/**
|
117
|
+
* Cap a value between a min and max value, so it stays within bounds
|
118
|
+
* @param {Number} value
|
119
|
+
* @param {Number} minValue
|
120
|
+
* @param {Number} maxValue
|
121
|
+
* @return {Number}
|
122
|
+
*/
|
123
|
+
|
124
|
+
const clamp = function () {
|
125
|
+
let value = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
|
126
|
+
let minValue = arguments.length > 1 ? arguments[1] : undefined;
|
127
|
+
let maxValue = arguments.length > 2 ? arguments[2] : undefined;
|
128
|
+
return Math.min(Math.max(toNumeric(value), minValue), maxValue);
|
129
|
+
};
|
130
|
+
/**
|
131
|
+
* Check if the value is a number according to React number input validation
|
132
|
+
* @param value
|
133
|
+
* @return {Boolean}
|
134
|
+
*/
|
135
|
+
|
136
|
+
const isReactNumeric = function () {
|
137
|
+
let value = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
|
138
|
+
return String(value).match(/^-?(\d+|\d+\.\d+|\.\d+)([eE][-+]?\d+)?$/) !== null;
|
139
|
+
};
|
140
|
+
/**
|
141
|
+
* Check if the value can become a number according to React number input validation
|
142
|
+
* @param value
|
143
|
+
* @return {Boolean}
|
144
|
+
*/
|
145
|
+
|
146
|
+
const isReactNumericIncomplete = function () {
|
147
|
+
let value = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
|
148
|
+
return String(value).match(/(?!^-?\d+$)^-?(\d+\.?)([eE][-+]?)?$/) !== null;
|
149
|
+
};
|
150
|
+
/**
|
151
|
+
* Transforms a scientific number (1e7) to a string (0.000001)
|
152
|
+
* @param value
|
153
|
+
* @return {string}
|
154
|
+
*/
|
155
|
+
|
156
|
+
const toFixed = value => Number(value).toFixed(20).replace(/\.?0+$/, '');
|
157
|
+
|
158
|
+
// eslint-disable-next-line no-warning-comments
|
159
|
+
// todo: this does not belong in a generic utils lib, move this to implementation
|
160
|
+
const s3ApiKeyMap = [['_u_i_c_station_code', 'UICStationCode'], ['origin_uic', 'originUIC'], ['destination_uic', 'destinationUIC']];
|
161
|
+
const camelCaseInflectorMap = new Map(s3ApiKeyMap);
|
162
|
+
const snakeCaseInflectorMap = s3ApiKeyMap.reduce((map, inflection) => {
|
163
|
+
return map.set(inflection[1], inflection[0]);
|
164
|
+
}, new Map());
|
165
|
+
|
166
|
+
const convertWithInflectorCreator = (inflector, keyConverter) => key => inflector.has(key) ? inflector.get(key) : keyConverter(key);
|
167
|
+
|
168
|
+
const deepConvertKeys = function (keyConverter) {
|
169
|
+
let whiteListChildren = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
|
170
|
+
|
171
|
+
const convertKeys = (node, parentKey) => {
|
172
|
+
if (Array.isArray(node)) return node.map(convertKeys);
|
173
|
+
|
174
|
+
if (isPlainObject(node)) {
|
175
|
+
return transform(node, (result, value, key) => {
|
176
|
+
const _key = whiteListChildren.includes(parentKey) ? key : keyConverter(key);
|
177
|
+
|
178
|
+
return set(result, _key, isObjectLike(value) ? convertKeys(value, _key) : value);
|
179
|
+
});
|
180
|
+
}
|
181
|
+
|
182
|
+
return node;
|
183
|
+
};
|
184
|
+
|
185
|
+
return convertKeys;
|
186
|
+
};
|
187
|
+
const deepConvertValues = valueConvertor => {
|
188
|
+
let originalNode;
|
189
|
+
|
190
|
+
const convertValues = node => {
|
191
|
+
originalNode = originalNode || node;
|
192
|
+
if (Array.isArray(node)) return node.map(convertValues);
|
193
|
+
|
194
|
+
if (isPlainObject(node)) {
|
195
|
+
return transform(node, (result, value, key) => {
|
196
|
+
const _value = isObjectLike(value) ? convertValues(value) : valueConvertor(key, value, originalNode);
|
197
|
+
|
198
|
+
return set(result, key, _value);
|
199
|
+
});
|
200
|
+
}
|
201
|
+
|
202
|
+
return node;
|
203
|
+
};
|
204
|
+
|
205
|
+
return convertValues;
|
206
|
+
};
|
207
|
+
const deepCamelCaseKeys = deepConvertKeys(convertWithInflectorCreator(camelCaseInflectorMap, camelCase));
|
208
|
+
const deepSnakeCaseKeys = deepConvertKeys(convertWithInflectorCreator(snakeCaseInflectorMap, snakeCase));
|
209
|
+
|
210
|
+
const omitByPredicateDeep = (obj, predicate) => {
|
211
|
+
return reduce(obj, (result, value, key) => {
|
212
|
+
if (isObject(value) || isArray(value)) {
|
213
|
+
if (isArray(result)) {
|
214
|
+
result.push(omitByPredicateDeep(value, predicate));
|
215
|
+
} else {
|
216
|
+
result[key] = omitByPredicateDeep(value, predicate);
|
217
|
+
}
|
218
|
+
} else if (!predicate(value)) {
|
219
|
+
if (isArray(result)) {
|
220
|
+
result.push(value);
|
221
|
+
} else {
|
222
|
+
result[key] = value;
|
223
|
+
}
|
224
|
+
}
|
225
|
+
|
226
|
+
return result;
|
227
|
+
}, isArray(obj) ? [] : {});
|
228
|
+
};
|
229
|
+
/**
|
230
|
+
* Recursively remove all nil values deeply nested properties of objects and arrays
|
231
|
+
*
|
232
|
+
* note: do not use for cyclical object or Date properties
|
233
|
+
*/
|
234
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
235
|
+
|
236
|
+
const omitNilDeep = obj => omitByPredicateDeep(obj, value => isNil(value));
|
237
|
+
|
238
|
+
const stringifyQueryEncode = query => qs.stringify(query, {
|
239
|
+
encode: true,
|
240
|
+
skipNulls: true
|
241
|
+
});
|
242
|
+
const parseQueryString = queryString => qs.parse(queryString, {
|
243
|
+
ignoreQueryPrefix: true,
|
244
|
+
decoder: (value, decoder) => {
|
245
|
+
const keywords = {
|
246
|
+
true: true,
|
247
|
+
false: false,
|
248
|
+
null: null,
|
249
|
+
undefined
|
250
|
+
};
|
251
|
+
|
252
|
+
if (value in keywords) {
|
253
|
+
return keywords[value];
|
254
|
+
}
|
255
|
+
|
256
|
+
return decoder(value);
|
257
|
+
}
|
258
|
+
});
|
259
|
+
|
260
|
+
export { abbreviate, clamp, convertUnicode, deepCamelCaseKeys, deepConvertKeys, deepConvertValues, deepSnakeCaseKeys, flatMap, hasValue, indexByKeyValue, isBlank, isEmptyValue, isReactNumeric, isReactNumericIncomplete, lowestValue, omitByPredicateDeep, omitNilDeep, parseJWT, parseQueryString, pluck, pluckUnique, removeSpaces, stringifyQueryEncode, toFixed, toHash, toNumeric, toPath, trimSpaces, unique };
|
package/package.json
CHANGED
@@ -1,6 +1,26 @@
|
|
1
1
|
{
|
2
2
|
"name": "@s3p-js-deep-purple/utils",
|
3
|
-
"
|
4
|
-
"
|
5
|
-
"
|
3
|
+
"license": "UNLICENSED",
|
4
|
+
"version": "1.1.99",
|
5
|
+
"description": "Common string, array, date, object, etc utils for S3 Portal",
|
6
|
+
"main": "dist/s3p-js-deep-purple-utils.cjs.js",
|
7
|
+
"module": "dist/s3p-js-deep-purple-utils.esm.js",
|
8
|
+
"types": "dist/s3p-js-deep-purple-utils.cjs.d.ts",
|
9
|
+
"author": "Sqills Products BV",
|
10
|
+
"private": false,
|
11
|
+
"scripts": {
|
12
|
+
"test": "NODE_ENV=test ./node_modules/.bin/jest",
|
13
|
+
"test-coverage": "NODE_ENV=test ./node_modules/.bin/jest --config ./jest.config.coverage.json",
|
14
|
+
"preinstall": "./bin/preinstall.sh"
|
15
|
+
},
|
16
|
+
"devDependencies": {
|
17
|
+
"jest": "^27.5.1"
|
18
|
+
},
|
19
|
+
"dependencies": {
|
20
|
+
"qs": "^6.10.3"
|
21
|
+
},
|
22
|
+
"peerDependencies": {
|
23
|
+
"core-js": "^3.21.1",
|
24
|
+
"lodash": "^4.17.21"
|
25
|
+
}
|
6
26
|
}
|
package/src/array.js
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
import get from 'lodash/get'
|
2
|
+
|
3
|
+
export const unique = array => Array.from(new Set(array))
|
4
|
+
|
5
|
+
export const pluck = (array, field) => array.map(obj => get(obj || {}, field))
|
6
|
+
|
7
|
+
export const pluckUnique = (...args) => unique(pluck(...args))
|
8
|
+
|
9
|
+
export const flatMap = (array, field) =>
|
10
|
+
array.reduce((newArray, obj) => {
|
11
|
+
newArray.push(...(obj || {})[field])
|
12
|
+
return newArray
|
13
|
+
}, [])
|
14
|
+
|
15
|
+
export const indexByKeyValue = (items, propKey, propValue) =>
|
16
|
+
(items || []).reduce((object, item) => {
|
17
|
+
const key = item[propKey]
|
18
|
+
if (key) {
|
19
|
+
object[key] = propValue ? item[propValue] : item
|
20
|
+
}
|
21
|
+
return object
|
22
|
+
}, {})
|
23
|
+
|
24
|
+
export const lowestValue = (array, field) => Math.min(...array.map(object => object[field]))
|