@augment-vir/common 6.4.0 → 7.0.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.
@@ -1,4 +1,5 @@
1
- export declare function addCommasToNumber(input: number): string;
1
+ export declare const NaNString: string;
2
+ export declare function addCommasToNumber(input: number | string): string;
2
3
  export declare function clamp(
3
4
  /**
4
5
  * This uses a destructured object so that consumers cannot get confused as to which input is
@@ -9,25 +10,6 @@ export declare function clamp(
9
10
  min: number;
10
11
  max: number;
11
12
  }): number;
12
- /**
13
- * This truncates a number such that is will at a max have 6 characters including suffix, decimal
14
- * point, or comma.
15
- *
16
- * Default suffixes are:
17
- *
18
- * '', // no suffix, numbers below 1000
19
- * 'k', // thousand
20
- * 'M', // million
21
- * 'B', // billion
22
- * 'T', // trillion
23
- * 'P', // peta-, quadrillion
24
- * 'E', // exa- quintillion
25
- * 'Z', // zetta- sextillion
26
- * 'Y', // yotta- septillion
27
- */
28
- export declare function truncateNumber(originalValue: unknown, { customSuffixes, suppressErrorLogging, customErrorLogCallback, }?: {
29
- customSuffixes?: ReadonlyArray<string>;
30
- suppressErrorLogging?: boolean;
31
- customErrorLogCallback?: (...args: any) => void;
32
- }): string;
13
+ export declare function convertIntoNumber(input: unknown): number;
14
+ export declare function doesRequireScientificNotation(input: number): boolean;
33
15
  //# sourceMappingURL=common-number.d.ts.map
@@ -1,9 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.truncateNumber = exports.clamp = exports.addCommasToNumber = void 0;
3
+ exports.doesRequireScientificNotation = exports.convertIntoNumber = exports.clamp = exports.addCommasToNumber = exports.NaNString = void 0;
4
4
  const common_string_1 = require("./common-string");
5
5
  const regexp_1 = require("./regexp");
6
+ exports.NaNString = String(NaN);
6
7
  function addCommasToNumber(input) {
8
+ if (typeof input === 'string' && isNaN(Number(input))) {
9
+ return exports.NaNString;
10
+ }
7
11
  const stringValue = String(input);
8
12
  const [digits, decimalValues,] = stringValue.split('.');
9
13
  const decimalString = decimalValues ? `.${decimalValues}` : '';
@@ -23,89 +27,19 @@ function clamp(
23
27
  return Math.max(Math.min(value, max), min);
24
28
  }
25
29
  exports.clamp = clamp;
26
- const defaultTruncationSuffixes = [
27
- '',
28
- 'k',
29
- 'M',
30
- 'B',
31
- 'T',
32
- 'P',
33
- 'E',
34
- 'Z',
35
- 'Y', // yotta- septillion
36
- ];
37
- function recursiveTruncation(value, recursionDepth = 0, decimalValues = '') {
38
- var _a;
39
- if (value.includes('e+')) {
40
- throw new Error(`Number is too large, it cannot be truncated: ${value}`);
30
+ function convertIntoNumber(input) {
31
+ if (typeof input === 'number') {
32
+ return input;
41
33
  }
42
- else if (value.includes('e-')) {
43
- throw new Error(`Number is too small, it cannot be truncated: ${value}`);
34
+ else if (typeof input === 'string') {
35
+ return Number((0, common_string_1.removeCommasFromNumberString)(input));
44
36
  }
45
- const split = (0, common_string_1.typedSplit)(value, '.');
46
- decimalValues = (_a = split[1]) !== null && _a !== void 0 ? _a : decimalValues;
47
- const amount = split[0];
48
- if (amount.length > 3) {
49
- decimalValues = amount.slice(-3);
50
- return recursiveTruncation(amount.slice(0, -3), recursionDepth + 1, decimalValues);
37
+ else {
38
+ return Number(input);
51
39
  }
52
- return {
53
- value: amount,
54
- decimalValues,
55
- recursionDepth,
56
- };
57
40
  }
58
- const maxDecimals = 4;
59
- /**
60
- * This truncates a number such that is will at a max have 6 characters including suffix, decimal
61
- * point, or comma.
62
- *
63
- * Default suffixes are:
64
- *
65
- * '', // no suffix, numbers below 1000
66
- * 'k', // thousand
67
- * 'M', // million
68
- * 'B', // billion
69
- * 'T', // trillion
70
- * 'P', // peta-, quadrillion
71
- * 'E', // exa- quintillion
72
- * 'Z', // zetta- sextillion
73
- * 'Y', // yotta- septillion
74
- */
75
- function truncateNumber(originalValue, { customSuffixes, suppressErrorLogging, customErrorLogCallback, } = {}) {
76
- try {
77
- const value = typeof originalValue === 'number'
78
- ? originalValue
79
- : typeof originalValue === 'string'
80
- ? Number((0, common_string_1.removeCommasFromNumberString)(originalValue))
81
- : Number(originalValue);
82
- if (isNaN(value)) {
83
- throw new Error(`${originalValue} could not be converted into a number.`);
84
- }
85
- const stringValue = String(value);
86
- const results = recursiveTruncation(stringValue);
87
- const suffixes = customSuffixes !== null && customSuffixes !== void 0 ? customSuffixes : defaultTruncationSuffixes;
88
- const suffix = suffixes[results.recursionDepth];
89
- if (suffix === undefined) {
90
- throw new Error(`Number is too large, could not truncate: ${value}`);
91
- }
92
- const decimalPlaces = maxDecimals - (results.value.length - 1) - suffix.length;
93
- const decimalValues = results.decimalValues.replace(/0+$/, '').slice(0, decimalPlaces);
94
- const withDecimal = decimalValues.length ? `.${decimalValues}` : '';
95
- const combined = `${results.value}${withDecimal}${suffix}`;
96
- if (combined.length > stringValue.length + 1) {
97
- return addCommasToNumber(value);
98
- }
99
- else {
100
- return combined;
101
- }
102
- }
103
- catch (error) {
104
- const errorCallback = customErrorLogCallback ? customErrorLogCallback : console.error;
105
- if (!suppressErrorLogging) {
106
- errorCallback(error);
107
- }
108
- return String(originalValue);
109
- }
41
+ exports.convertIntoNumber = convertIntoNumber;
42
+ function doesRequireScientificNotation(input) {
43
+ return String(input).includes('e');
110
44
  }
111
- exports.truncateNumber = truncateNumber;
45
+ exports.doesRequireScientificNotation = doesRequireScientificNotation;
@@ -1,4 +1,6 @@
1
+ import { NoInputsFunction } from './function';
1
2
  import { AtLeastTuple } from './tuple';
3
+ import { UnPromise } from './type';
2
4
  export declare function combineErrors(errors: AtLeastTuple<Error, 1>): Error;
3
5
  export declare function combineErrors(errors: ReadonlyArray<never>): undefined;
4
6
  export declare function combineErrors(errors: ReadonlyArray<Error>): Error | undefined;
@@ -6,4 +8,6 @@ export declare function combineErrors(errors?: undefined): undefined;
6
8
  export declare function combineErrorMessages(errors?: ReadonlyArray<Error | string | undefined> | undefined): string;
7
9
  export declare function extractErrorMessage(error: unknown): string;
8
10
  export declare function ensureError(input: unknown): Error;
11
+ export declare function executeAndReturnError<CallbackGeneric extends NoInputsFunction<PromiseLike<any>>>(callback: CallbackGeneric): Promise<Error | UnPromise<ReturnType<CallbackGeneric>>>;
12
+ export declare function executeAndReturnError<CallbackGeneric extends NoInputsFunction>(callback: CallbackGeneric): Error | ReturnType<CallbackGeneric>;
9
13
  //# sourceMappingURL=error.d.ts.map
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ensureError = exports.extractErrorMessage = exports.combineErrorMessages = exports.combineErrors = void 0;
3
+ exports.executeAndReturnError = exports.ensureError = exports.extractErrorMessage = exports.combineErrorMessages = exports.combineErrors = void 0;
4
4
  const function_1 = require("./function");
5
+ const promise_1 = require("./promise");
5
6
  function combineErrors(errors) {
6
7
  if (!errors || errors.length === 0) {
7
8
  return undefined;
@@ -41,3 +42,29 @@ function ensureError(input) {
41
42
  }
42
43
  }
43
44
  exports.ensureError = ensureError;
45
+ function executeAndReturnError(callback) {
46
+ let caughtError;
47
+ try {
48
+ const result = callback();
49
+ if ((0, promise_1.isPromiseLike)(result)) {
50
+ return new Promise(async (resolve) => {
51
+ try {
52
+ const output = await result;
53
+ return resolve(output);
54
+ }
55
+ catch (error) {
56
+ caughtError = ensureError(error);
57
+ }
58
+ return resolve(caughtError);
59
+ });
60
+ }
61
+ else {
62
+ return result;
63
+ }
64
+ }
65
+ catch (error) {
66
+ caughtError = ensureError(error);
67
+ }
68
+ return caughtError;
69
+ }
70
+ exports.executeAndReturnError = executeAndReturnError;
@@ -1,3 +1,4 @@
1
- export type AnyFunction = (...args: any[]) => any;
1
+ export type AnyFunction<ReturnGeneric = any> = (...args: any[]) => ReturnGeneric;
2
+ export type NoInputsFunction<ReturnGeneric = any> = () => ReturnGeneric;
2
3
  export declare function isTruthy<T>(input: T): input is NonNullable<T>;
3
4
  //# sourceMappingURL=function.d.ts.map
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getValueFromNestedKeys = void 0;
4
- const error_1 = require("./error");
5
4
  const object_1 = require("./object");
6
5
  function getValueFromNestedKeys(inputObject, nestedKeys) {
7
6
  /**
@@ -10,21 +9,15 @@ function getValueFromNestedKeys(inputObject, nestedKeys) {
10
9
  * affect the external API of this function.
11
10
  */
12
11
  const keyToAccess = nestedKeys[0];
13
- try {
14
- if (!(0, object_1.typedHasProperty)(inputObject, keyToAccess)) {
15
- return undefined;
16
- }
17
- const currentValue = inputObject[keyToAccess];
18
- if (nestedKeys.length > 1) {
19
- return getValueFromNestedKeys(currentValue, nestedKeys.slice(1));
20
- }
21
- else {
22
- return currentValue;
23
- }
12
+ if (!(0, object_1.typedHasProperty)(inputObject, keyToAccess)) {
13
+ return undefined;
24
14
  }
25
- catch (error) {
26
- console.error({ inputObject, nestedKeys });
27
- throw new Error(`Failed to traverse into inputObject using key "${String(keyToAccess)}": ${(0, error_1.extractErrorMessage)(error)}`);
15
+ const currentValue = inputObject[keyToAccess];
16
+ if (nestedKeys.length > 1) {
17
+ return getValueFromNestedKeys(currentValue, nestedKeys.slice(1));
18
+ }
19
+ else {
20
+ return currentValue;
28
21
  }
29
22
  }
30
23
  exports.getValueFromNestedKeys = getValueFromNestedKeys;
@@ -5,12 +5,11 @@ const error_1 = require("./error");
5
5
  const object_1 = require("./object");
6
6
  function wait(delayMs) {
7
7
  const deferredPromiseWrapper = createDeferredPromiseWrapper();
8
- if (delayMs === Infinity || delayMs < 0) {
9
- return deferredPromiseWrapper.promise;
8
+ if (delayMs !== Infinity) {
9
+ setTimeout(() => {
10
+ deferredPromiseWrapper.resolve();
11
+ }, delayMs <= 0 ? 0 : delayMs);
10
12
  }
11
- setTimeout(() => {
12
- deferredPromiseWrapper.resolve();
13
- }, delayMs);
14
13
  return deferredPromiseWrapper.promise;
15
14
  }
16
15
  exports.wait = wait;
@@ -57,6 +56,8 @@ function createDeferredPromiseWrapper() {
57
56
  resolve = resolveCallback;
58
57
  reject = rejectCallback;
59
58
  });
59
+ // no way to test this edge case
60
+ // istanbul ignore next
60
61
  if (!resolve || !reject) {
61
62
  throw new Error(`Reject and resolve callbacks were not set by the promise constructor for ${createDeferredPromiseWrapper.name}.`);
62
63
  }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * This truncates a number such that is will at a max have 6 characters including suffix, decimal
3
+ * point, or comma.
4
+ *
5
+ * Default suffixes are:
6
+ *
7
+ * 'k', // thousand
8
+ * 'M', // million
9
+ * 'B', // billion
10
+ * 'T', // trillion
11
+ * 'P', // peta-, quadrillion
12
+ * 'E', // exa- quintillion
13
+ * 'Z', // zetta- sextillion
14
+ * 'Y', // yotta- septillion
15
+ */
16
+ export declare function truncateNumber(originalValue: Readonly<unknown>, { customSuffixes, maxLength, }?: Partial<{
17
+ customSuffixes: ReadonlyArray<string> | undefined;
18
+ maxLength: number | undefined;
19
+ }>): string;
20
+ //# sourceMappingURL=truncate-number.d.ts.map
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.truncateNumber = void 0;
4
+ const common_number_1 = require("./common-number");
5
+ const common_string_1 = require("./common-string");
6
+ const regexp_1 = require("./regexp");
7
+ const defaultTruncationSuffixes = [
8
+ 'k',
9
+ 'M',
10
+ 'B',
11
+ 'T',
12
+ 'P',
13
+ 'E',
14
+ 'Z',
15
+ 'Y', // yotta- septillion
16
+ ];
17
+ function combineBeforeAndAfterDot({ beforeDot, afterDot = '', maxLength, }) {
18
+ if (afterDot.length) {
19
+ const allowedAfterDotLength = maxLength -
20
+ beforeDot.length -
21
+ // 1 for the period
22
+ 1;
23
+ if (allowedAfterDotLength > 0) {
24
+ const slicedAfterDot = afterDot.slice(0, allowedAfterDotLength);
25
+ // if slicedAfterDot is just a bunch of 0
26
+ if (!Number(slicedAfterDot)) {
27
+ return beforeDot;
28
+ }
29
+ return [
30
+ beforeDot,
31
+ slicedAfterDot,
32
+ ].join('.');
33
+ }
34
+ }
35
+ return beforeDot;
36
+ }
37
+ function truncateBigNumber(numberAsString, suffixes, maxLength) {
38
+ const [beforeDot, afterDot,] = (0, common_string_1.typedSplit)(numberAsString, '.');
39
+ const withCommas = (0, common_number_1.addCommasToNumber)(beforeDot);
40
+ const truncationDepth = (0, regexp_1.safeMatch)(withCommas, /,/g).length;
41
+ const suffix = suffixes[truncationDepth - 1];
42
+ const [beforeComma, afterComma,] = (0, common_string_1.typedSplit)(withCommas, ',');
43
+ const trailing = [
44
+ afterComma,
45
+ afterDot,
46
+ ].join('');
47
+ if (beforeComma.length + 1 > maxLength) {
48
+ // will look like 0.9M
49
+ const minimumString = [
50
+ '0.',
51
+ beforeComma[0],
52
+ suffixes[truncationDepth],
53
+ ].join('');
54
+ return minimumString;
55
+ }
56
+ else {
57
+ const combined = combineBeforeAndAfterDot({
58
+ beforeDot: beforeComma,
59
+ afterDot: trailing,
60
+ maxLength: maxLength - 1 /* -1 to account for the suffix*/,
61
+ });
62
+ return [
63
+ combined,
64
+ suffix,
65
+ ].join('');
66
+ }
67
+ }
68
+ const minScientificNotationLength = '1e+'.length;
69
+ function truncateScientificNotation({ input, maxLength, }) {
70
+ const valueString = String(input);
71
+ const [beforeExponent, rawExponent,] = (0, common_string_1.typedSplit)(valueString, 'e');
72
+ const exponent = rawExponent.replace(/^[\-\+]/, '');
73
+ const plusOrMinus = rawExponent[0];
74
+ const eSuffix = [
75
+ 'e',
76
+ plusOrMinus,
77
+ exponent,
78
+ ].join('');
79
+ const [beforeDot, afterDot,] = (0, common_string_1.typedSplit)(beforeExponent, '.');
80
+ const minLength = exponent.length + minScientificNotationLength;
81
+ if (minLength === maxLength) {
82
+ // this will look like "4e+4" or "5e-234"
83
+ return [
84
+ beforeDot,
85
+ eSuffix,
86
+ ].join('');
87
+ }
88
+ else if (minLength > maxLength) {
89
+ // in this case the number is either way too big or way to small for its exponent to fit within the max length so we just jump to 0 or Infinity
90
+ if (plusOrMinus === '-') {
91
+ return '0';
92
+ }
93
+ else {
94
+ return String(Infinity);
95
+ }
96
+ }
97
+ else {
98
+ // in this case we have room to add some decimal values to the number
99
+ const beforeE = combineBeforeAndAfterDot({
100
+ afterDot,
101
+ beforeDot,
102
+ maxLength: maxLength - exponent.length + minScientificNotationLength,
103
+ });
104
+ return [
105
+ beforeE,
106
+ eSuffix,
107
+ ].join('');
108
+ }
109
+ }
110
+ function handleSmallNumbers(numberAsString, maxLength) {
111
+ const [beforeDot, afterDot,] = (0, common_string_1.typedSplit)((0, common_number_1.addCommasToNumber)(numberAsString), '.');
112
+ if (beforeDot.length <= maxLength) {
113
+ return combineBeforeAndAfterDot({
114
+ beforeDot,
115
+ afterDot,
116
+ maxLength,
117
+ });
118
+ }
119
+ // in this case, the number is not small enough to be handled by this function
120
+ return undefined;
121
+ }
122
+ /**
123
+ * This truncates a number such that is will at a max have 6 characters including suffix, decimal
124
+ * point, or comma.
125
+ *
126
+ * Default suffixes are:
127
+ *
128
+ * 'k', // thousand
129
+ * 'M', // million
130
+ * 'B', // billion
131
+ * 'T', // trillion
132
+ * 'P', // peta-, quadrillion
133
+ * 'E', // exa- quintillion
134
+ * 'Z', // zetta- sextillion
135
+ * 'Y', // yotta- septillion
136
+ */
137
+ function truncateNumber(originalValue, { customSuffixes = defaultTruncationSuffixes, maxLength = 6, } = {}) {
138
+ const inputNumber = (0, common_number_1.convertIntoNumber)(originalValue);
139
+ // handle edge cases
140
+ if (isNaN(inputNumber) || inputNumber === Infinity) {
141
+ return String(inputNumber);
142
+ }
143
+ // handle too big or too small edge cases
144
+ if ((0, common_number_1.doesRequireScientificNotation)(inputNumber)) {
145
+ return truncateScientificNotation({ input: inputNumber, maxLength });
146
+ }
147
+ const numberAsString = String(inputNumber);
148
+ const smallResult = handleSmallNumbers(numberAsString, maxLength);
149
+ if (smallResult != undefined) {
150
+ return smallResult;
151
+ }
152
+ return truncateBigNumber(numberAsString, customSuffixes, maxLength);
153
+ }
154
+ exports.truncateNumber = truncateNumber;
@@ -2,8 +2,8 @@ export type Tuple<ArrayElementGeneric, LengthGeneric extends number> = LengthGen
2
2
  type _TupleOf<ArrayElementGeneric, LengthGeneric extends number, FullArrayGeneric extends unknown[]> = FullArrayGeneric['length'] extends LengthGeneric ? FullArrayGeneric : _TupleOf<ArrayElementGeneric, LengthGeneric, [ArrayElementGeneric, ...FullArrayGeneric]>;
3
3
  export type AtLeastTuple<ArrayElementGeneric, LengthGeneric extends number> = readonly [
4
4
  ...Tuple<ArrayElementGeneric, LengthGeneric>,
5
- ...ArrayElementGeneric[]
5
+ ...(ArrayElementGeneric | undefined)[]
6
6
  ];
7
- export declare function isLengthAtLeast<ArrayElementGeneric, LengthGeneric extends number>(array: ReadonlyArray<ArrayElementGeneric>, length: LengthGeneric): array is AtLeastTuple<ArrayElementGeneric, LengthGeneric>;
7
+ export declare function isLengthAtLeast<ArrayElementGeneric, LengthGeneric extends number>(array: ReadonlyArray<ArrayElementGeneric | undefined>, length: LengthGeneric): array is AtLeastTuple<ArrayElementGeneric, LengthGeneric>;
8
8
  export {};
9
9
  //# sourceMappingURL=tuple.d.ts.map
@@ -12,6 +12,7 @@ export * from './augments/object';
12
12
  export * from './augments/pick-deep';
13
13
  export * from './augments/promise';
14
14
  export * from './augments/regexp';
15
+ export * from './augments/truncate-number';
15
16
  export * from './augments/tuple';
16
17
  export * from './augments/type';
17
18
  export * from './augments/type-of';
package/dist/cjs/index.js CHANGED
@@ -28,6 +28,7 @@ __exportStar(require("./augments/object"), exports);
28
28
  __exportStar(require("./augments/pick-deep"), exports);
29
29
  __exportStar(require("./augments/promise"), exports);
30
30
  __exportStar(require("./augments/regexp"), exports);
31
+ __exportStar(require("./augments/truncate-number"), exports);
31
32
  __exportStar(require("./augments/tuple"), exports);
32
33
  __exportStar(require("./augments/type"), exports);
33
34
  __exportStar(require("./augments/type-of"), exports);
@@ -1,4 +1,5 @@
1
- export declare function addCommasToNumber(input: number): string;
1
+ export declare const NaNString: string;
2
+ export declare function addCommasToNumber(input: number | string): string;
2
3
  export declare function clamp(
3
4
  /**
4
5
  * This uses a destructured object so that consumers cannot get confused as to which input is
@@ -9,25 +10,6 @@ export declare function clamp(
9
10
  min: number;
10
11
  max: number;
11
12
  }): number;
12
- /**
13
- * This truncates a number such that is will at a max have 6 characters including suffix, decimal
14
- * point, or comma.
15
- *
16
- * Default suffixes are:
17
- *
18
- * '', // no suffix, numbers below 1000
19
- * 'k', // thousand
20
- * 'M', // million
21
- * 'B', // billion
22
- * 'T', // trillion
23
- * 'P', // peta-, quadrillion
24
- * 'E', // exa- quintillion
25
- * 'Z', // zetta- sextillion
26
- * 'Y', // yotta- septillion
27
- */
28
- export declare function truncateNumber(originalValue: unknown, { customSuffixes, suppressErrorLogging, customErrorLogCallback, }?: {
29
- customSuffixes?: ReadonlyArray<string>;
30
- suppressErrorLogging?: boolean;
31
- customErrorLogCallback?: (...args: any) => void;
32
- }): string;
13
+ export declare function convertIntoNumber(input: unknown): number;
14
+ export declare function doesRequireScientificNotation(input: number): boolean;
33
15
  //# sourceMappingURL=common-number.d.ts.map
@@ -1,6 +1,10 @@
1
- import { removeCommasFromNumberString, typedSplit } from './common-string';
1
+ import { removeCommasFromNumberString } from './common-string';
2
2
  import { safeMatch } from './regexp';
3
+ export const NaNString = String(NaN);
3
4
  export function addCommasToNumber(input) {
5
+ if (typeof input === 'string' && isNaN(Number(input))) {
6
+ return NaNString;
7
+ }
4
8
  const stringValue = String(input);
5
9
  const [digits, decimalValues,] = stringValue.split('.');
6
10
  const decimalString = decimalValues ? `.${decimalValues}` : '';
@@ -18,88 +22,17 @@ export function clamp(
18
22
  { value, min, max, }) {
19
23
  return Math.max(Math.min(value, max), min);
20
24
  }
21
- const defaultTruncationSuffixes = [
22
- '',
23
- 'k',
24
- 'M',
25
- 'B',
26
- 'T',
27
- 'P',
28
- 'E',
29
- 'Z',
30
- 'Y', // yotta- septillion
31
- ];
32
- function recursiveTruncation(value, recursionDepth = 0, decimalValues = '') {
33
- var _a;
34
- if (value.includes('e+')) {
35
- throw new Error(`Number is too large, it cannot be truncated: ${value}`);
25
+ export function convertIntoNumber(input) {
26
+ if (typeof input === 'number') {
27
+ return input;
36
28
  }
37
- else if (value.includes('e-')) {
38
- throw new Error(`Number is too small, it cannot be truncated: ${value}`);
29
+ else if (typeof input === 'string') {
30
+ return Number(removeCommasFromNumberString(input));
39
31
  }
40
- const split = typedSplit(value, '.');
41
- decimalValues = (_a = split[1]) !== null && _a !== void 0 ? _a : decimalValues;
42
- const amount = split[0];
43
- if (amount.length > 3) {
44
- decimalValues = amount.slice(-3);
45
- return recursiveTruncation(amount.slice(0, -3), recursionDepth + 1, decimalValues);
32
+ else {
33
+ return Number(input);
46
34
  }
47
- return {
48
- value: amount,
49
- decimalValues,
50
- recursionDepth,
51
- };
52
35
  }
53
- const maxDecimals = 4;
54
- /**
55
- * This truncates a number such that is will at a max have 6 characters including suffix, decimal
56
- * point, or comma.
57
- *
58
- * Default suffixes are:
59
- *
60
- * '', // no suffix, numbers below 1000
61
- * 'k', // thousand
62
- * 'M', // million
63
- * 'B', // billion
64
- * 'T', // trillion
65
- * 'P', // peta-, quadrillion
66
- * 'E', // exa- quintillion
67
- * 'Z', // zetta- sextillion
68
- * 'Y', // yotta- septillion
69
- */
70
- export function truncateNumber(originalValue, { customSuffixes, suppressErrorLogging, customErrorLogCallback, } = {}) {
71
- try {
72
- const value = typeof originalValue === 'number'
73
- ? originalValue
74
- : typeof originalValue === 'string'
75
- ? Number(removeCommasFromNumberString(originalValue))
76
- : Number(originalValue);
77
- if (isNaN(value)) {
78
- throw new Error(`${originalValue} could not be converted into a number.`);
79
- }
80
- const stringValue = String(value);
81
- const results = recursiveTruncation(stringValue);
82
- const suffixes = customSuffixes !== null && customSuffixes !== void 0 ? customSuffixes : defaultTruncationSuffixes;
83
- const suffix = suffixes[results.recursionDepth];
84
- if (suffix === undefined) {
85
- throw new Error(`Number is too large, could not truncate: ${value}`);
86
- }
87
- const decimalPlaces = maxDecimals - (results.value.length - 1) - suffix.length;
88
- const decimalValues = results.decimalValues.replace(/0+$/, '').slice(0, decimalPlaces);
89
- const withDecimal = decimalValues.length ? `.${decimalValues}` : '';
90
- const combined = `${results.value}${withDecimal}${suffix}`;
91
- if (combined.length > stringValue.length + 1) {
92
- return addCommasToNumber(value);
93
- }
94
- else {
95
- return combined;
96
- }
97
- }
98
- catch (error) {
99
- const errorCallback = customErrorLogCallback ? customErrorLogCallback : console.error;
100
- if (!suppressErrorLogging) {
101
- errorCallback(error);
102
- }
103
- return String(originalValue);
104
- }
36
+ export function doesRequireScientificNotation(input) {
37
+ return String(input).includes('e');
105
38
  }
@@ -1,4 +1,6 @@
1
+ import { NoInputsFunction } from './function';
1
2
  import { AtLeastTuple } from './tuple';
3
+ import { UnPromise } from './type';
2
4
  export declare function combineErrors(errors: AtLeastTuple<Error, 1>): Error;
3
5
  export declare function combineErrors(errors: ReadonlyArray<never>): undefined;
4
6
  export declare function combineErrors(errors: ReadonlyArray<Error>): Error | undefined;
@@ -6,4 +8,6 @@ export declare function combineErrors(errors?: undefined): undefined;
6
8
  export declare function combineErrorMessages(errors?: ReadonlyArray<Error | string | undefined> | undefined): string;
7
9
  export declare function extractErrorMessage(error: unknown): string;
8
10
  export declare function ensureError(input: unknown): Error;
11
+ export declare function executeAndReturnError<CallbackGeneric extends NoInputsFunction<PromiseLike<any>>>(callback: CallbackGeneric): Promise<Error | UnPromise<ReturnType<CallbackGeneric>>>;
12
+ export declare function executeAndReturnError<CallbackGeneric extends NoInputsFunction>(callback: CallbackGeneric): Error | ReturnType<CallbackGeneric>;
9
13
  //# sourceMappingURL=error.d.ts.map
@@ -1,4 +1,5 @@
1
1
  import { isTruthy } from './function';
2
+ import { isPromiseLike } from './promise';
2
3
  export function combineErrors(errors) {
3
4
  if (!errors || errors.length === 0) {
4
5
  return undefined;
@@ -34,3 +35,28 @@ export function ensureError(input) {
34
35
  return new Error(extractErrorMessage(input));
35
36
  }
36
37
  }
38
+ export function executeAndReturnError(callback) {
39
+ let caughtError;
40
+ try {
41
+ const result = callback();
42
+ if (isPromiseLike(result)) {
43
+ return new Promise(async (resolve) => {
44
+ try {
45
+ const output = await result;
46
+ return resolve(output);
47
+ }
48
+ catch (error) {
49
+ caughtError = ensureError(error);
50
+ }
51
+ return resolve(caughtError);
52
+ });
53
+ }
54
+ else {
55
+ return result;
56
+ }
57
+ }
58
+ catch (error) {
59
+ caughtError = ensureError(error);
60
+ }
61
+ return caughtError;
62
+ }
@@ -1,3 +1,4 @@
1
- export type AnyFunction = (...args: any[]) => any;
1
+ export type AnyFunction<ReturnGeneric = any> = (...args: any[]) => ReturnGeneric;
2
+ export type NoInputsFunction<ReturnGeneric = any> = () => ReturnGeneric;
2
3
  export declare function isTruthy<T>(input: T): input is NonNullable<T>;
3
4
  //# sourceMappingURL=function.d.ts.map
@@ -1,4 +1,3 @@
1
- import { extractErrorMessage } from './error';
2
1
  import { typedHasProperty } from './object';
3
2
  export function getValueFromNestedKeys(inputObject, nestedKeys) {
4
3
  /**
@@ -7,20 +6,14 @@ export function getValueFromNestedKeys(inputObject, nestedKeys) {
7
6
  * affect the external API of this function.
8
7
  */
9
8
  const keyToAccess = nestedKeys[0];
10
- try {
11
- if (!typedHasProperty(inputObject, keyToAccess)) {
12
- return undefined;
13
- }
14
- const currentValue = inputObject[keyToAccess];
15
- if (nestedKeys.length > 1) {
16
- return getValueFromNestedKeys(currentValue, nestedKeys.slice(1));
17
- }
18
- else {
19
- return currentValue;
20
- }
9
+ if (!typedHasProperty(inputObject, keyToAccess)) {
10
+ return undefined;
21
11
  }
22
- catch (error) {
23
- console.error({ inputObject, nestedKeys });
24
- throw new Error(`Failed to traverse into inputObject using key "${String(keyToAccess)}": ${extractErrorMessage(error)}`);
12
+ const currentValue = inputObject[keyToAccess];
13
+ if (nestedKeys.length > 1) {
14
+ return getValueFromNestedKeys(currentValue, nestedKeys.slice(1));
15
+ }
16
+ else {
17
+ return currentValue;
25
18
  }
26
19
  }
@@ -2,12 +2,11 @@ import { extractErrorMessage } from './error';
2
2
  import { typedHasProperty } from './object';
3
3
  export function wait(delayMs) {
4
4
  const deferredPromiseWrapper = createDeferredPromiseWrapper();
5
- if (delayMs === Infinity || delayMs < 0) {
6
- return deferredPromiseWrapper.promise;
5
+ if (delayMs !== Infinity) {
6
+ setTimeout(() => {
7
+ deferredPromiseWrapper.resolve();
8
+ }, delayMs <= 0 ? 0 : delayMs);
7
9
  }
8
- setTimeout(() => {
9
- deferredPromiseWrapper.resolve();
10
- }, delayMs);
11
10
  return deferredPromiseWrapper.promise;
12
11
  }
13
12
  export function isPromiseLike(input) {
@@ -50,6 +49,8 @@ export function createDeferredPromiseWrapper() {
50
49
  resolve = resolveCallback;
51
50
  reject = rejectCallback;
52
51
  });
52
+ // no way to test this edge case
53
+ // istanbul ignore next
53
54
  if (!resolve || !reject) {
54
55
  throw new Error(`Reject and resolve callbacks were not set by the promise constructor for ${createDeferredPromiseWrapper.name}.`);
55
56
  }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * This truncates a number such that is will at a max have 6 characters including suffix, decimal
3
+ * point, or comma.
4
+ *
5
+ * Default suffixes are:
6
+ *
7
+ * 'k', // thousand
8
+ * 'M', // million
9
+ * 'B', // billion
10
+ * 'T', // trillion
11
+ * 'P', // peta-, quadrillion
12
+ * 'E', // exa- quintillion
13
+ * 'Z', // zetta- sextillion
14
+ * 'Y', // yotta- septillion
15
+ */
16
+ export declare function truncateNumber(originalValue: Readonly<unknown>, { customSuffixes, maxLength, }?: Partial<{
17
+ customSuffixes: ReadonlyArray<string> | undefined;
18
+ maxLength: number | undefined;
19
+ }>): string;
20
+ //# sourceMappingURL=truncate-number.d.ts.map
@@ -0,0 +1,150 @@
1
+ import { addCommasToNumber, convertIntoNumber, doesRequireScientificNotation } from './common-number';
2
+ import { typedSplit } from './common-string';
3
+ import { safeMatch } from './regexp';
4
+ const defaultTruncationSuffixes = [
5
+ 'k',
6
+ 'M',
7
+ 'B',
8
+ 'T',
9
+ 'P',
10
+ 'E',
11
+ 'Z',
12
+ 'Y', // yotta- septillion
13
+ ];
14
+ function combineBeforeAndAfterDot({ beforeDot, afterDot = '', maxLength, }) {
15
+ if (afterDot.length) {
16
+ const allowedAfterDotLength = maxLength -
17
+ beforeDot.length -
18
+ // 1 for the period
19
+ 1;
20
+ if (allowedAfterDotLength > 0) {
21
+ const slicedAfterDot = afterDot.slice(0, allowedAfterDotLength);
22
+ // if slicedAfterDot is just a bunch of 0
23
+ if (!Number(slicedAfterDot)) {
24
+ return beforeDot;
25
+ }
26
+ return [
27
+ beforeDot,
28
+ slicedAfterDot,
29
+ ].join('.');
30
+ }
31
+ }
32
+ return beforeDot;
33
+ }
34
+ function truncateBigNumber(numberAsString, suffixes, maxLength) {
35
+ const [beforeDot, afterDot,] = typedSplit(numberAsString, '.');
36
+ const withCommas = addCommasToNumber(beforeDot);
37
+ const truncationDepth = safeMatch(withCommas, /,/g).length;
38
+ const suffix = suffixes[truncationDepth - 1];
39
+ const [beforeComma, afterComma,] = typedSplit(withCommas, ',');
40
+ const trailing = [
41
+ afterComma,
42
+ afterDot,
43
+ ].join('');
44
+ if (beforeComma.length + 1 > maxLength) {
45
+ // will look like 0.9M
46
+ const minimumString = [
47
+ '0.',
48
+ beforeComma[0],
49
+ suffixes[truncationDepth],
50
+ ].join('');
51
+ return minimumString;
52
+ }
53
+ else {
54
+ const combined = combineBeforeAndAfterDot({
55
+ beforeDot: beforeComma,
56
+ afterDot: trailing,
57
+ maxLength: maxLength - 1 /* -1 to account for the suffix*/,
58
+ });
59
+ return [
60
+ combined,
61
+ suffix,
62
+ ].join('');
63
+ }
64
+ }
65
+ const minScientificNotationLength = '1e+'.length;
66
+ function truncateScientificNotation({ input, maxLength, }) {
67
+ const valueString = String(input);
68
+ const [beforeExponent, rawExponent,] = typedSplit(valueString, 'e');
69
+ const exponent = rawExponent.replace(/^[\-\+]/, '');
70
+ const plusOrMinus = rawExponent[0];
71
+ const eSuffix = [
72
+ 'e',
73
+ plusOrMinus,
74
+ exponent,
75
+ ].join('');
76
+ const [beforeDot, afterDot,] = typedSplit(beforeExponent, '.');
77
+ const minLength = exponent.length + minScientificNotationLength;
78
+ if (minLength === maxLength) {
79
+ // this will look like "4e+4" or "5e-234"
80
+ return [
81
+ beforeDot,
82
+ eSuffix,
83
+ ].join('');
84
+ }
85
+ else if (minLength > maxLength) {
86
+ // in this case the number is either way too big or way to small for its exponent to fit within the max length so we just jump to 0 or Infinity
87
+ if (plusOrMinus === '-') {
88
+ return '0';
89
+ }
90
+ else {
91
+ return String(Infinity);
92
+ }
93
+ }
94
+ else {
95
+ // in this case we have room to add some decimal values to the number
96
+ const beforeE = combineBeforeAndAfterDot({
97
+ afterDot,
98
+ beforeDot,
99
+ maxLength: maxLength - exponent.length + minScientificNotationLength,
100
+ });
101
+ return [
102
+ beforeE,
103
+ eSuffix,
104
+ ].join('');
105
+ }
106
+ }
107
+ function handleSmallNumbers(numberAsString, maxLength) {
108
+ const [beforeDot, afterDot,] = typedSplit(addCommasToNumber(numberAsString), '.');
109
+ if (beforeDot.length <= maxLength) {
110
+ return combineBeforeAndAfterDot({
111
+ beforeDot,
112
+ afterDot,
113
+ maxLength,
114
+ });
115
+ }
116
+ // in this case, the number is not small enough to be handled by this function
117
+ return undefined;
118
+ }
119
+ /**
120
+ * This truncates a number such that is will at a max have 6 characters including suffix, decimal
121
+ * point, or comma.
122
+ *
123
+ * Default suffixes are:
124
+ *
125
+ * 'k', // thousand
126
+ * 'M', // million
127
+ * 'B', // billion
128
+ * 'T', // trillion
129
+ * 'P', // peta-, quadrillion
130
+ * 'E', // exa- quintillion
131
+ * 'Z', // zetta- sextillion
132
+ * 'Y', // yotta- septillion
133
+ */
134
+ export function truncateNumber(originalValue, { customSuffixes = defaultTruncationSuffixes, maxLength = 6, } = {}) {
135
+ const inputNumber = convertIntoNumber(originalValue);
136
+ // handle edge cases
137
+ if (isNaN(inputNumber) || inputNumber === Infinity) {
138
+ return String(inputNumber);
139
+ }
140
+ // handle too big or too small edge cases
141
+ if (doesRequireScientificNotation(inputNumber)) {
142
+ return truncateScientificNotation({ input: inputNumber, maxLength });
143
+ }
144
+ const numberAsString = String(inputNumber);
145
+ const smallResult = handleSmallNumbers(numberAsString, maxLength);
146
+ if (smallResult != undefined) {
147
+ return smallResult;
148
+ }
149
+ return truncateBigNumber(numberAsString, customSuffixes, maxLength);
150
+ }
@@ -2,8 +2,8 @@ export type Tuple<ArrayElementGeneric, LengthGeneric extends number> = LengthGen
2
2
  type _TupleOf<ArrayElementGeneric, LengthGeneric extends number, FullArrayGeneric extends unknown[]> = FullArrayGeneric['length'] extends LengthGeneric ? FullArrayGeneric : _TupleOf<ArrayElementGeneric, LengthGeneric, [ArrayElementGeneric, ...FullArrayGeneric]>;
3
3
  export type AtLeastTuple<ArrayElementGeneric, LengthGeneric extends number> = readonly [
4
4
  ...Tuple<ArrayElementGeneric, LengthGeneric>,
5
- ...ArrayElementGeneric[]
5
+ ...(ArrayElementGeneric | undefined)[]
6
6
  ];
7
- export declare function isLengthAtLeast<ArrayElementGeneric, LengthGeneric extends number>(array: ReadonlyArray<ArrayElementGeneric>, length: LengthGeneric): array is AtLeastTuple<ArrayElementGeneric, LengthGeneric>;
7
+ export declare function isLengthAtLeast<ArrayElementGeneric, LengthGeneric extends number>(array: ReadonlyArray<ArrayElementGeneric | undefined>, length: LengthGeneric): array is AtLeastTuple<ArrayElementGeneric, LengthGeneric>;
8
8
  export {};
9
9
  //# sourceMappingURL=tuple.d.ts.map
@@ -12,6 +12,7 @@ export * from './augments/object';
12
12
  export * from './augments/pick-deep';
13
13
  export * from './augments/promise';
14
14
  export * from './augments/regexp';
15
+ export * from './augments/truncate-number';
15
16
  export * from './augments/tuple';
16
17
  export * from './augments/type';
17
18
  export * from './augments/type-of';
package/dist/esm/index.js CHANGED
@@ -12,6 +12,7 @@ export * from './augments/object';
12
12
  export * from './augments/pick-deep';
13
13
  export * from './augments/promise';
14
14
  export * from './augments/regexp';
15
+ export * from './augments/truncate-number';
15
16
  export * from './augments/tuple';
16
17
  export * from './augments/type';
17
18
  export * from './augments/type-of';
@@ -1,4 +1,5 @@
1
- export declare function addCommasToNumber(input: number): string;
1
+ export declare const NaNString: string;
2
+ export declare function addCommasToNumber(input: number | string): string;
2
3
  export declare function clamp(
3
4
  /**
4
5
  * This uses a destructured object so that consumers cannot get confused as to which input is
@@ -9,25 +10,6 @@ export declare function clamp(
9
10
  min: number;
10
11
  max: number;
11
12
  }): number;
12
- /**
13
- * This truncates a number such that is will at a max have 6 characters including suffix, decimal
14
- * point, or comma.
15
- *
16
- * Default suffixes are:
17
- *
18
- * '', // no suffix, numbers below 1000
19
- * 'k', // thousand
20
- * 'M', // million
21
- * 'B', // billion
22
- * 'T', // trillion
23
- * 'P', // peta-, quadrillion
24
- * 'E', // exa- quintillion
25
- * 'Z', // zetta- sextillion
26
- * 'Y', // yotta- septillion
27
- */
28
- export declare function truncateNumber(originalValue: unknown, { customSuffixes, suppressErrorLogging, customErrorLogCallback, }?: {
29
- customSuffixes?: ReadonlyArray<string>;
30
- suppressErrorLogging?: boolean;
31
- customErrorLogCallback?: (...args: any) => void;
32
- }): string;
13
+ export declare function convertIntoNumber(input: unknown): number;
14
+ export declare function doesRequireScientificNotation(input: number): boolean;
33
15
  //# sourceMappingURL=common-number.d.ts.map
@@ -1,4 +1,6 @@
1
+ import { NoInputsFunction } from './function';
1
2
  import { AtLeastTuple } from './tuple';
3
+ import { UnPromise } from './type';
2
4
  export declare function combineErrors(errors: AtLeastTuple<Error, 1>): Error;
3
5
  export declare function combineErrors(errors: ReadonlyArray<never>): undefined;
4
6
  export declare function combineErrors(errors: ReadonlyArray<Error>): Error | undefined;
@@ -6,4 +8,6 @@ export declare function combineErrors(errors?: undefined): undefined;
6
8
  export declare function combineErrorMessages(errors?: ReadonlyArray<Error | string | undefined> | undefined): string;
7
9
  export declare function extractErrorMessage(error: unknown): string;
8
10
  export declare function ensureError(input: unknown): Error;
11
+ export declare function executeAndReturnError<CallbackGeneric extends NoInputsFunction<PromiseLike<any>>>(callback: CallbackGeneric): Promise<Error | UnPromise<ReturnType<CallbackGeneric>>>;
12
+ export declare function executeAndReturnError<CallbackGeneric extends NoInputsFunction>(callback: CallbackGeneric): Error | ReturnType<CallbackGeneric>;
9
13
  //# sourceMappingURL=error.d.ts.map
@@ -1,3 +1,4 @@
1
- export type AnyFunction = (...args: any[]) => any;
1
+ export type AnyFunction<ReturnGeneric = any> = (...args: any[]) => ReturnGeneric;
2
+ export type NoInputsFunction<ReturnGeneric = any> = () => ReturnGeneric;
2
3
  export declare function isTruthy<T>(input: T): input is NonNullable<T>;
3
4
  //# sourceMappingURL=function.d.ts.map
@@ -0,0 +1,20 @@
1
+ /**
2
+ * This truncates a number such that is will at a max have 6 characters including suffix, decimal
3
+ * point, or comma.
4
+ *
5
+ * Default suffixes are:
6
+ *
7
+ * 'k', // thousand
8
+ * 'M', // million
9
+ * 'B', // billion
10
+ * 'T', // trillion
11
+ * 'P', // peta-, quadrillion
12
+ * 'E', // exa- quintillion
13
+ * 'Z', // zetta- sextillion
14
+ * 'Y', // yotta- septillion
15
+ */
16
+ export declare function truncateNumber(originalValue: Readonly<unknown>, { customSuffixes, maxLength, }?: Partial<{
17
+ customSuffixes: ReadonlyArray<string> | undefined;
18
+ maxLength: number | undefined;
19
+ }>): string;
20
+ //# sourceMappingURL=truncate-number.d.ts.map
@@ -2,8 +2,8 @@ export type Tuple<ArrayElementGeneric, LengthGeneric extends number> = LengthGen
2
2
  type _TupleOf<ArrayElementGeneric, LengthGeneric extends number, FullArrayGeneric extends unknown[]> = FullArrayGeneric['length'] extends LengthGeneric ? FullArrayGeneric : _TupleOf<ArrayElementGeneric, LengthGeneric, [ArrayElementGeneric, ...FullArrayGeneric]>;
3
3
  export type AtLeastTuple<ArrayElementGeneric, LengthGeneric extends number> = readonly [
4
4
  ...Tuple<ArrayElementGeneric, LengthGeneric>,
5
- ...ArrayElementGeneric[]
5
+ ...(ArrayElementGeneric | undefined)[]
6
6
  ];
7
- export declare function isLengthAtLeast<ArrayElementGeneric, LengthGeneric extends number>(array: ReadonlyArray<ArrayElementGeneric>, length: LengthGeneric): array is AtLeastTuple<ArrayElementGeneric, LengthGeneric>;
7
+ export declare function isLengthAtLeast<ArrayElementGeneric, LengthGeneric extends number>(array: ReadonlyArray<ArrayElementGeneric | undefined>, length: LengthGeneric): array is AtLeastTuple<ArrayElementGeneric, LengthGeneric>;
8
8
  export {};
9
9
  //# sourceMappingURL=tuple.d.ts.map
@@ -12,6 +12,7 @@ export * from './augments/object';
12
12
  export * from './augments/pick-deep';
13
13
  export * from './augments/promise';
14
14
  export * from './augments/regexp';
15
+ export * from './augments/truncate-number';
15
16
  export * from './augments/tuple';
16
17
  export * from './augments/type';
17
18
  export * from './augments/type-of';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@augment-vir/common",
3
- "version": "6.4.0",
3
+ "version": "7.0.0",
4
4
  "homepage": "https://github.com/electrovir/augment-vir/tree/main/packages/common",
5
5
  "bugs": {
6
6
  "url": "https://github.com/electrovir/augment-vir/issues"
@@ -23,7 +23,7 @@
23
23
  "test:coverage": "npm test"
24
24
  },
25
25
  "dependencies": {
26
- "type-fest": "^3.5.0"
26
+ "type-fest": "^3.5.1"
27
27
  },
28
28
  "devDependencies": {
29
29
  "typescript": "^4.9.4"