@augment-vir/common 4.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.
- package/README.md +5 -0
- package/dist/augments/ansi.d.ts +1 -0
- package/dist/augments/ansi.js +27 -0
- package/dist/augments/array.d.ts +20 -0
- package/dist/augments/array.js +44 -0
- package/dist/augments/date.d.ts +26 -0
- package/dist/augments/date.js +81 -0
- package/dist/augments/error.d.ts +8 -0
- package/dist/augments/error.js +43 -0
- package/dist/augments/function.d.ts +1 -0
- package/dist/augments/function.js +7 -0
- package/dist/augments/number.d.ts +27 -0
- package/dist/augments/number.js +83 -0
- package/dist/augments/object.d.ts +62 -0
- package/dist/augments/object.js +269 -0
- package/dist/augments/promise.d.ts +16 -0
- package/dist/augments/promise.js +68 -0
- package/dist/augments/regexp.d.ts +3 -0
- package/dist/augments/regexp.js +20 -0
- package/dist/augments/string.d.ts +34 -0
- package/dist/augments/string.js +192 -0
- package/dist/augments/tuple.d.ts +8 -0
- package/dist/augments/tuple.js +7 -0
- package/dist/augments/type-test.d.ts +3 -0
- package/dist/augments/type-test.js +2 -0
- package/dist/augments/type.d.ts +54 -0
- package/dist/augments/type.js +29 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +29 -0
- package/package.json +41 -0
package/README.md
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# @augment-vir/common
|
|
2
|
+
|
|
3
|
+
`augment-vir` is a collection of small helper functions that I constantly use across all my JavaScript and TypeScript repos. I call these functions `augments`. These are things commonly placed within a "util", or "helpers", etc. directory; they don't really have anything in common with each other except that they have almost no dependencies.
|
|
4
|
+
|
|
5
|
+
This `common` package is for environment-agnostic augments. Everything in here will work in Node.js or the browser with identical results.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const ansiRegex: RegExp;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ansiRegex = void 0;
|
|
4
|
+
// cspell:disable
|
|
5
|
+
/*
|
|
6
|
+
Copied from
|
|
7
|
+
https://github.com/chalk/ansi-regex/blob/1b337add136eb520764634a328e2f6354398eee5/index.js
|
|
8
|
+
because the published ansi-regex package exports this in a manner that Jest can't handle and the
|
|
9
|
+
latest version of the package that doesn't do that has a security vulnerability. The package has
|
|
10
|
+
the following license from
|
|
11
|
+
https://github.com/chalk/ansi-regex/blob/1b337add136eb520764634a328e2f6354398eee5/license
|
|
12
|
+
|
|
13
|
+
MIT License
|
|
14
|
+
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
|
|
15
|
+
|
|
16
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
17
|
+
|
|
18
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
21
|
+
*/
|
|
22
|
+
// cspell:enable
|
|
23
|
+
const patterns = [
|
|
24
|
+
'[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
|
|
25
|
+
'(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))',
|
|
26
|
+
];
|
|
27
|
+
exports.ansiRegex = new RegExp(patterns.join('|'), 'g');
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { AtLeastTuple } from './tuple';
|
|
2
|
+
import { ArrayElement } from './type';
|
|
3
|
+
export declare function filterOutIndexes<T>(array: Readonly<T[]>, indexes: Readonly<number[]>): T[];
|
|
4
|
+
export declare function flatten2dArray<T>(array2d: T[][]): T[];
|
|
5
|
+
export declare type AtLeastOneEntryArray<ArrayGeneric extends ReadonlyArray<any>> = AtLeastTuple<ArrayElement<ArrayGeneric>, 1>;
|
|
6
|
+
export declare function trimArrayStrings(input: string[]): string[];
|
|
7
|
+
/**
|
|
8
|
+
* Acts like calling Array.prototype.forEach in that all elements are executed upon in order, and
|
|
9
|
+
* each execution is blocking. Meaning, the callback won't be called on element 2 until the callback
|
|
10
|
+
* has finished its call on element 1.
|
|
11
|
+
*/
|
|
12
|
+
export declare function awaitedForEach<OriginalGeneric>(input: ReadonlyArray<OriginalGeneric>, callback: (arrayElement: OriginalGeneric, index: number, wholeArray: ReadonlyArray<OriginalGeneric>) => void | PromiseLike<void>): Promise<void>;
|
|
13
|
+
export declare function awaitedBlockingMap<OriginalGeneric, MappedGeneric>(input: ReadonlyArray<OriginalGeneric>, callback: (arrayElement: OriginalGeneric, index: number, wholeArray: ReadonlyArray<OriginalGeneric>) => MappedGeneric | PromiseLike<MappedGeneric>): Promise<Awaited<MappedGeneric>[]>;
|
|
14
|
+
export declare function isInTypedArray<T>(array: T[], input: any): input is T;
|
|
15
|
+
declare type MapCallbackType<ArrayType extends ReadonlyArray<any>, OutputType> = (value: ArrayElement<ArrayType>, index: number, array: ArrayType) => OutputType;
|
|
16
|
+
/** Preserves tuple types. */
|
|
17
|
+
export declare function typedMap<InputArrayGeneric extends ReadonlyArray<any>, OutputType>(arrayToMap: InputArrayGeneric, mapCallback: MapCallbackType<InputArrayGeneric, OutputType>): {
|
|
18
|
+
[Index in keyof InputArrayGeneric]: OutputType;
|
|
19
|
+
};
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.typedMap = exports.isInTypedArray = exports.awaitedBlockingMap = exports.awaitedForEach = exports.trimArrayStrings = exports.flatten2dArray = exports.filterOutIndexes = void 0;
|
|
4
|
+
function filterOutIndexes(array, indexes) {
|
|
5
|
+
return array.filter((_, index) => !indexes.includes(index));
|
|
6
|
+
}
|
|
7
|
+
exports.filterOutIndexes = filterOutIndexes;
|
|
8
|
+
function flatten2dArray(array2d) {
|
|
9
|
+
const flattened = array2d.reduce((accum, row) => accum.concat(row), []);
|
|
10
|
+
return flattened;
|
|
11
|
+
}
|
|
12
|
+
exports.flatten2dArray = flatten2dArray;
|
|
13
|
+
function trimArrayStrings(input) {
|
|
14
|
+
return input.map((line) => line.trim()).filter((line) => line !== '');
|
|
15
|
+
}
|
|
16
|
+
exports.trimArrayStrings = trimArrayStrings;
|
|
17
|
+
/**
|
|
18
|
+
* Acts like calling Array.prototype.forEach in that all elements are executed upon in order, and
|
|
19
|
+
* each execution is blocking. Meaning, the callback won't be called on element 2 until the callback
|
|
20
|
+
* has finished its call on element 1.
|
|
21
|
+
*/
|
|
22
|
+
async function awaitedForEach(input, callback) {
|
|
23
|
+
await awaitedBlockingMap(input, callback);
|
|
24
|
+
}
|
|
25
|
+
exports.awaitedForEach = awaitedForEach;
|
|
26
|
+
async function awaitedBlockingMap(input, callback) {
|
|
27
|
+
const mappedValues = await input.reduce(async (accumPromise, currentElement, index, wholeArray) => {
|
|
28
|
+
const accum = await accumPromise;
|
|
29
|
+
const mappedValue = await callback(currentElement, index, wholeArray);
|
|
30
|
+
accum.push(mappedValue);
|
|
31
|
+
return accum;
|
|
32
|
+
}, Promise.resolve([]));
|
|
33
|
+
return mappedValues;
|
|
34
|
+
}
|
|
35
|
+
exports.awaitedBlockingMap = awaitedBlockingMap;
|
|
36
|
+
function isInTypedArray(array, input) {
|
|
37
|
+
return array.includes(input);
|
|
38
|
+
}
|
|
39
|
+
exports.isInTypedArray = isInTypedArray;
|
|
40
|
+
/** Preserves tuple types. */
|
|
41
|
+
function typedMap(arrayToMap, mapCallback) {
|
|
42
|
+
return arrayToMap.map(mapCallback);
|
|
43
|
+
}
|
|
44
|
+
exports.typedMap = typedMap;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare const englishFullMonthNames: string[];
|
|
2
|
+
export declare const englishShortMonthNames: string[];
|
|
3
|
+
export declare class InvalidDateError extends Error {
|
|
4
|
+
readonly name = "InvalidDateError";
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* @param slashFormatString String that should be of format "MM/DD/YY", "MM/DD/YYYY". When the year
|
|
8
|
+
* portion only contains 2 numbers ("MM/DD/YY") the century must be provided in the form of the
|
|
9
|
+
* yearPrefix input.
|
|
10
|
+
* @param yearPrefix String or number that is used to prefix slash format strings that only contain
|
|
11
|
+
* 2 digits ("MM/DD/YY"). If the year is entirely missing form the given slash format string, the
|
|
12
|
+
* year will default to year 00 of the given century. See test file for examples.
|
|
13
|
+
*/
|
|
14
|
+
export declare function createDateFromSlashFormat(slashFormatString: string, yearPrefix?: number | string): Date;
|
|
15
|
+
/**
|
|
16
|
+
* @param commaFormatString Should be at string of the form "monthName dayNumber, fullYear" Example:
|
|
17
|
+
* "May 19, 2005"
|
|
18
|
+
* @param ignoreInvalidMonth Set to true to ignore invalid months
|
|
19
|
+
*/
|
|
20
|
+
export declare function createDateFromNamedCommaFormat(commaFormatString: string, ignoreInvalidMonth?: boolean): Date;
|
|
21
|
+
/**
|
|
22
|
+
* Converts an iso-formatted string to a UTC date object. The time is nulled out to all zeros.
|
|
23
|
+
*
|
|
24
|
+
* @param isoFormatString Should be a date in the format YYYY-MM-DD.
|
|
25
|
+
*/
|
|
26
|
+
export declare function createDateFromUtcIsoFormat(isoFormatString: string): Date;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createDateFromUtcIsoFormat = exports.createDateFromNamedCommaFormat = exports.createDateFromSlashFormat = exports.InvalidDateError = exports.englishShortMonthNames = exports.englishFullMonthNames = void 0;
|
|
4
|
+
exports.englishFullMonthNames = [
|
|
5
|
+
'january',
|
|
6
|
+
'february',
|
|
7
|
+
'march',
|
|
8
|
+
'april',
|
|
9
|
+
'may',
|
|
10
|
+
'june',
|
|
11
|
+
'july',
|
|
12
|
+
'august',
|
|
13
|
+
'september',
|
|
14
|
+
'october',
|
|
15
|
+
'november',
|
|
16
|
+
'december',
|
|
17
|
+
];
|
|
18
|
+
exports.englishShortMonthNames = exports.englishFullMonthNames.map((longMonthName) => longMonthName.slice(0, 3));
|
|
19
|
+
class InvalidDateError extends Error {
|
|
20
|
+
constructor() {
|
|
21
|
+
super(...arguments);
|
|
22
|
+
this.name = 'InvalidDateError';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.InvalidDateError = InvalidDateError;
|
|
26
|
+
/**
|
|
27
|
+
* @param slashFormatString String that should be of format "MM/DD/YY", "MM/DD/YYYY". When the year
|
|
28
|
+
* portion only contains 2 numbers ("MM/DD/YY") the century must be provided in the form of the
|
|
29
|
+
* yearPrefix input.
|
|
30
|
+
* @param yearPrefix String or number that is used to prefix slash format strings that only contain
|
|
31
|
+
* 2 digits ("MM/DD/YY"). If the year is entirely missing form the given slash format string, the
|
|
32
|
+
* year will default to year 00 of the given century. See test file for examples.
|
|
33
|
+
*/
|
|
34
|
+
function createDateFromSlashFormat(slashFormatString, yearPrefix = '') {
|
|
35
|
+
const [month, day, rawYearEnding = '',] = slashFormatString.split('/');
|
|
36
|
+
if (!month || !day) {
|
|
37
|
+
throw new Error(`Unable to extract month or day from "${slashFormatString}"`);
|
|
38
|
+
}
|
|
39
|
+
const yearEnding = rawYearEnding.length < 4 ? `${yearPrefix}${rawYearEnding.padStart(2, '0')}` : rawYearEnding;
|
|
40
|
+
const returnDate = createDateFromUtcIsoFormat(`${yearEnding.padStart(4, '0')}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`);
|
|
41
|
+
return returnDate;
|
|
42
|
+
}
|
|
43
|
+
exports.createDateFromSlashFormat = createDateFromSlashFormat;
|
|
44
|
+
/**
|
|
45
|
+
* @param commaFormatString Should be at string of the form "monthName dayNumber, fullYear" Example:
|
|
46
|
+
* "May 19, 2005"
|
|
47
|
+
* @param ignoreInvalidMonth Set to true to ignore invalid months
|
|
48
|
+
*/
|
|
49
|
+
function createDateFromNamedCommaFormat(commaFormatString, ignoreInvalidMonth = false) {
|
|
50
|
+
const [monthName, dayNumber, fullYear,] = commaFormatString.replace(',', '').split(' ');
|
|
51
|
+
if (!monthName || !dayNumber || !fullYear) {
|
|
52
|
+
throw new InvalidDateError(`Invalid ${createDateFromNamedCommaFormat.name} input: ${commaFormatString}`);
|
|
53
|
+
}
|
|
54
|
+
const longMonthIndex = exports.englishFullMonthNames.indexOf(monthName.toLowerCase());
|
|
55
|
+
const shortMonthIndex = exports.englishShortMonthNames.indexOf(monthName.toLowerCase());
|
|
56
|
+
let monthIndex = longMonthIndex === -1 ? shortMonthIndex : longMonthIndex;
|
|
57
|
+
if (monthIndex === -1) {
|
|
58
|
+
if (ignoreInvalidMonth) {
|
|
59
|
+
monthIndex = 4;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
throw new InvalidDateError(`Month name ${monthName} was not found.`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const returnDate = createDateFromUtcIsoFormat(`${fullYear.padStart(4, '0')}-${String(monthIndex + 1).padStart(2, '0')}-${dayNumber.padStart(2, '0')}`);
|
|
66
|
+
return returnDate;
|
|
67
|
+
}
|
|
68
|
+
exports.createDateFromNamedCommaFormat = createDateFromNamedCommaFormat;
|
|
69
|
+
/**
|
|
70
|
+
* Converts an iso-formatted string to a UTC date object. The time is nulled out to all zeros.
|
|
71
|
+
*
|
|
72
|
+
* @param isoFormatString Should be a date in the format YYYY-MM-DD.
|
|
73
|
+
*/
|
|
74
|
+
function createDateFromUtcIsoFormat(isoFormatString) {
|
|
75
|
+
const utcDate = new Date(isoFormatString + 'T00:00:00.000Z');
|
|
76
|
+
if (isNaN(Number(utcDate))) {
|
|
77
|
+
throw new InvalidDateError(`Invalid utc date formed from input "${isoFormatString}"`);
|
|
78
|
+
}
|
|
79
|
+
return utcDate;
|
|
80
|
+
}
|
|
81
|
+
exports.createDateFromUtcIsoFormat = createDateFromUtcIsoFormat;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { AtLeastTuple } from './tuple';
|
|
2
|
+
export declare function combineErrors(errors: AtLeastTuple<Error, 1>): Error;
|
|
3
|
+
export declare function combineErrors(errors: never[]): undefined;
|
|
4
|
+
export declare function combineErrors(errors: Error[]): Error | undefined;
|
|
5
|
+
export declare function combineErrors(errors?: undefined): undefined;
|
|
6
|
+
export declare function combineErrorMessages(errors?: (Error | string | undefined)[] | undefined): string;
|
|
7
|
+
export declare function extractErrorMessage(error: unknown): string;
|
|
8
|
+
export declare function ensureError(input: unknown): Error;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ensureError = exports.extractErrorMessage = exports.combineErrorMessages = exports.combineErrors = void 0;
|
|
4
|
+
const function_1 = require("./function");
|
|
5
|
+
function combineErrors(errors) {
|
|
6
|
+
if (!errors || errors.length === 0) {
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
const firstError = errors[0];
|
|
10
|
+
if (errors.length === 1 && firstError) {
|
|
11
|
+
return firstError;
|
|
12
|
+
}
|
|
13
|
+
return new Error(errors.map((error) => extractErrorMessage(error).trim()).join('\n'));
|
|
14
|
+
}
|
|
15
|
+
exports.combineErrors = combineErrors;
|
|
16
|
+
function combineErrorMessages(errors) {
|
|
17
|
+
if (!errors) {
|
|
18
|
+
return '';
|
|
19
|
+
}
|
|
20
|
+
return errors.map(extractErrorMessage).filter(function_1.isTruthy).join('\n');
|
|
21
|
+
}
|
|
22
|
+
exports.combineErrorMessages = combineErrorMessages;
|
|
23
|
+
function extractErrorMessage(error) {
|
|
24
|
+
if (!error) {
|
|
25
|
+
return '';
|
|
26
|
+
}
|
|
27
|
+
if (error instanceof Error) {
|
|
28
|
+
return error.message;
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
return String(error);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.extractErrorMessage = extractErrorMessage;
|
|
35
|
+
function ensureError(input) {
|
|
36
|
+
if (input instanceof Error) {
|
|
37
|
+
return input;
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
return new Error(extractErrorMessage(input));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.ensureError = ensureError;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isTruthy<T>(input: T): input is NonNullable<T>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export declare function clamp(
|
|
2
|
+
/**
|
|
3
|
+
* This uses a destructured object so that consumers cannot get confused as to which input is
|
|
4
|
+
* which (which would be easy to do since they're all of the same type).
|
|
5
|
+
*/
|
|
6
|
+
{ value, min, max, }: {
|
|
7
|
+
value: number;
|
|
8
|
+
min: number;
|
|
9
|
+
max: number;
|
|
10
|
+
}): number;
|
|
11
|
+
/**
|
|
12
|
+
* This truncates a number such that is will never use commas and will at a max have 6 characters
|
|
13
|
+
* including suffix and decimal point.
|
|
14
|
+
*
|
|
15
|
+
* Default suffixes are:
|
|
16
|
+
*
|
|
17
|
+
* '', // no suffix, numbers below 1000
|
|
18
|
+
* 'k', // thousand
|
|
19
|
+
* 'M', // million
|
|
20
|
+
* 'B', // billion
|
|
21
|
+
* 'T', // trillion
|
|
22
|
+
* 'P', // peta-, quadrillion
|
|
23
|
+
* 'E', // exa- quintillion
|
|
24
|
+
* 'Z', // zetta- sextillion
|
|
25
|
+
* 'Y', // yotta- septillion
|
|
26
|
+
*/
|
|
27
|
+
export declare function truncateNumber(originalValue: unknown, suffixes?: readonly string[]): string;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.truncateNumber = exports.clamp = void 0;
|
|
4
|
+
function clamp(
|
|
5
|
+
/**
|
|
6
|
+
* This uses a destructured object so that consumers cannot get confused as to which input is
|
|
7
|
+
* which (which would be easy to do since they're all of the same type).
|
|
8
|
+
*/
|
|
9
|
+
{ value, min, max, }) {
|
|
10
|
+
return Math.max(Math.min(value, max), min);
|
|
11
|
+
}
|
|
12
|
+
exports.clamp = clamp;
|
|
13
|
+
const defaultTruncationSuffixes = [
|
|
14
|
+
'',
|
|
15
|
+
'k',
|
|
16
|
+
'M',
|
|
17
|
+
'B',
|
|
18
|
+
'T',
|
|
19
|
+
'P',
|
|
20
|
+
'E',
|
|
21
|
+
'Z',
|
|
22
|
+
'Y', // yotta- septillion
|
|
23
|
+
];
|
|
24
|
+
function recursiveTruncation(value, recursionDepth = 0, decimalValues = '') {
|
|
25
|
+
var _a, _b;
|
|
26
|
+
if (value.includes('e+')) {
|
|
27
|
+
throw new Error(`Number is too large, it cannot be truncated: ${value}`);
|
|
28
|
+
}
|
|
29
|
+
else if (value.includes('e-')) {
|
|
30
|
+
throw new Error(`Number is too small, it cannot be truncated: ${value}`);
|
|
31
|
+
}
|
|
32
|
+
const split = value.split('.');
|
|
33
|
+
decimalValues = (_a = split[1]) !== null && _a !== void 0 ? _a : decimalValues;
|
|
34
|
+
const amount = (_b = split[0]) !== null && _b !== void 0 ? _b : '';
|
|
35
|
+
if (amount.length > 3) {
|
|
36
|
+
decimalValues = amount.slice(-3);
|
|
37
|
+
return recursiveTruncation(amount.slice(0, -3), recursionDepth + 1, decimalValues);
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
value: amount,
|
|
41
|
+
decimalValues,
|
|
42
|
+
recursionDepth,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const maxDecimals = 4;
|
|
46
|
+
/**
|
|
47
|
+
* This truncates a number such that is will never use commas and will at a max have 6 characters
|
|
48
|
+
* including suffix and decimal point.
|
|
49
|
+
*
|
|
50
|
+
* Default suffixes are:
|
|
51
|
+
*
|
|
52
|
+
* '', // no suffix, numbers below 1000
|
|
53
|
+
* 'k', // thousand
|
|
54
|
+
* 'M', // million
|
|
55
|
+
* 'B', // billion
|
|
56
|
+
* 'T', // trillion
|
|
57
|
+
* 'P', // peta-, quadrillion
|
|
58
|
+
* 'E', // exa- quintillion
|
|
59
|
+
* 'Z', // zetta- sextillion
|
|
60
|
+
* 'Y', // yotta- septillion
|
|
61
|
+
*/
|
|
62
|
+
function truncateNumber(originalValue, suffixes = defaultTruncationSuffixes) {
|
|
63
|
+
try {
|
|
64
|
+
const value = typeof originalValue === 'number' ? originalValue : Number(originalValue);
|
|
65
|
+
if (isNaN(value)) {
|
|
66
|
+
throw new Error(`${originalValue} could not be converted into a number.`);
|
|
67
|
+
}
|
|
68
|
+
const results = recursiveTruncation(String(value));
|
|
69
|
+
const suffix = suffixes[results.recursionDepth];
|
|
70
|
+
if (suffix === undefined) {
|
|
71
|
+
throw new Error(`Number is too large, could not truncate: ${value}`);
|
|
72
|
+
}
|
|
73
|
+
const decimalPlaces = maxDecimals - (results.value.length - 1) - suffix.length;
|
|
74
|
+
const decimalValues = results.decimalValues.replace(/0+$/, '').slice(0, decimalPlaces);
|
|
75
|
+
const withDecimal = decimalValues.length ? `.${decimalValues}` : '';
|
|
76
|
+
return `${results.value}${withDecimal}${suffix}`;
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
console.error(error);
|
|
80
|
+
return String(originalValue);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
exports.truncateNumber = truncateNumber;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { AtLeastOneEntryArray } from './array';
|
|
2
|
+
import { NoInfer, RequiredBy, UnPromise } from './type';
|
|
3
|
+
export declare function getEnumTypedKeys<T extends object>(input: T): (keyof T)[];
|
|
4
|
+
export declare function getEnumTypedValues<T extends object>(input: T): T[keyof T][];
|
|
5
|
+
export declare function isEnumValue<T extends object>(input: unknown, checkEnum: T): input is T[keyof T];
|
|
6
|
+
export declare function isKeyof<ObjectGeneric>(key: PropertyKey, object: ObjectGeneric): key is keyof object;
|
|
7
|
+
export declare function filterToEnumValues<T extends object>(inputs: unknown[], checkEnum: T, caseInsensitive?: boolean): T[keyof T][];
|
|
8
|
+
export declare function getObjectTypedKeys<ObjectGeneric extends unknown>(input: ObjectGeneric): ReadonlyArray<keyof ObjectGeneric>;
|
|
9
|
+
export declare function getObjectTypedValues<ObjectGeneric extends unknown>(input: ObjectGeneric): ObjectGeneric[keyof ObjectGeneric][];
|
|
10
|
+
declare type ExtractValue<KeyGeneric extends PropertyKey, ParentGeneric> = KeyGeneric extends keyof ParentGeneric ? RequiredBy<ParentGeneric, KeyGeneric>[KeyGeneric] : KeyGeneric extends keyof Extract<ParentGeneric, Record<KeyGeneric, any>> ? RequiredBy<Extract<ParentGeneric, Record<KeyGeneric, any>>, KeyGeneric>[KeyGeneric] : never;
|
|
11
|
+
declare type CombinedParentValue<KeyGeneric extends PropertyKey, ParentGeneric> = ExtractValue<KeyGeneric, ParentGeneric> extends never ? unknown : ExtractValue<KeyGeneric, ParentGeneric>;
|
|
12
|
+
declare type CombineTypeWithKey<KeyGeneric extends PropertyKey, ParentGeneric> = ParentGeneric & Record<KeyGeneric, CombinedParentValue<KeyGeneric, ParentGeneric>>;
|
|
13
|
+
export declare function typedHasProperty<KeyGeneric extends PropertyKey, ParentGeneric>(inputObject: ParentGeneric, inputKey: KeyGeneric): inputObject is CombineTypeWithKey<KeyGeneric, ParentGeneric>;
|
|
14
|
+
export declare function typedHasProperties<KeyGeneric extends PropertyKey, ParentGeneric>(inputObject: ParentGeneric, inputKeys: KeyGeneric[]): inputObject is CombineTypeWithKey<KeyGeneric, ParentGeneric>;
|
|
15
|
+
export declare function isObject(input: any): input is NonNullable<object>;
|
|
16
|
+
export declare function getEntriesSortedByKey(input: object): [string, unknown][];
|
|
17
|
+
export declare function areJsonEqual(a: object, b: object): boolean;
|
|
18
|
+
export declare type InnerMappedValues<EntireInputGeneric extends object, MappedValueGeneric> = {
|
|
19
|
+
[MappedProp in keyof EntireInputGeneric]: MappedValueGeneric;
|
|
20
|
+
};
|
|
21
|
+
export declare type MappedValues<EntireInputGeneric extends object, MappedValueGeneric> = MappedValueGeneric extends PromiseLike<unknown> ? Promise<InnerMappedValues<EntireInputGeneric, UnPromise<MappedValueGeneric>>> : InnerMappedValues<EntireInputGeneric, UnPromise<MappedValueGeneric>>;
|
|
22
|
+
/**
|
|
23
|
+
* Creates a new object with the same properties as the input object, but with values set to the
|
|
24
|
+
* result of mapCallback for each property.
|
|
25
|
+
*/
|
|
26
|
+
export declare function mapObjectValues<EntireInputGeneric extends object, MappedValueGeneric>(inputObject: EntireInputGeneric, mapCallback: (inputKey: keyof EntireInputGeneric, keyValue: EntireInputGeneric[typeof inputKey], fullObject: EntireInputGeneric) => MappedValueGeneric): MappedValues<EntireInputGeneric, MappedValueGeneric>;
|
|
27
|
+
export declare function filterObject<ObjectGeneric extends object>(inputObject: ObjectGeneric, callback: (key: keyof ObjectGeneric, value: ObjectValueType<ObjectGeneric>, fullObject: ObjectGeneric) => boolean): Partial<ObjectGeneric>;
|
|
28
|
+
/** The input here must be serializable otherwise JSON parsing errors will be thrown */
|
|
29
|
+
export declare function copyThroughJson<T>(input: T): T;
|
|
30
|
+
export declare type ObjectValueType<T extends object> = T[keyof T];
|
|
31
|
+
/**
|
|
32
|
+
* Checks that the first input, testThisOne, matches the object shape of the second input,
|
|
33
|
+
* compareToThisOne. Does not compare exact values of properties, only types.
|
|
34
|
+
*
|
|
35
|
+
* To allow the test input, the first input, to have additional keys that the compare input, the
|
|
36
|
+
* second input, does not have, pass in a third argument set to true.
|
|
37
|
+
*
|
|
38
|
+
* This function REQUIRES a generic to be assigned to it: it cannot infer it from the inputs.
|
|
39
|
+
*
|
|
40
|
+
* The compare input, the second input, is required to have at least one entry in every array value
|
|
41
|
+
* that exists. If more array values are present, they will be considered other possible types for
|
|
42
|
+
* entries in that array.
|
|
43
|
+
*/
|
|
44
|
+
export declare function matchesObjectShape<MatchThisGeneric extends object>(testThisOne: unknown, compareToThisOne: NoInfer<ObjectWithAtLeastSingleEntryArrays<MatchThisGeneric>>, allowExtraProps?: boolean, shouldLogWhy?: boolean): testThisOne is MatchThisGeneric;
|
|
45
|
+
export declare type ObjectWithAtLeastSingleEntryArrays<BaseObject extends object> = {
|
|
46
|
+
[Prop in keyof BaseObject]: BaseObject[Prop] extends ReadonlyArray<any> ? AtLeastOneEntryArray<BaseObject[Prop]> : BaseObject[Prop] extends object ? ObjectWithAtLeastSingleEntryArrays<BaseObject[Prop]> : BaseObject[Prop];
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Asserts that the first input, testThisOne, matches the object shape of the second input,
|
|
50
|
+
* compareToThisOne. Does not compare exact values of properties, only types.
|
|
51
|
+
*
|
|
52
|
+
* To allow the test input, the first input, to have additional keys that the compare input, the
|
|
53
|
+
* second input, does not have, pass in a third argument set to true.
|
|
54
|
+
*
|
|
55
|
+
* This function REQUIRES a generic to be assigned to it: it cannot infer it from the inputs.
|
|
56
|
+
*
|
|
57
|
+
* The compare input, the second input, is required to have at least one entry in every array value
|
|
58
|
+
* that exists. If more array values are present, they will be considered other possible types for
|
|
59
|
+
* entries in that array.
|
|
60
|
+
*/
|
|
61
|
+
export declare function assertMatchesObjectShape<MatchThisGeneric extends object = never>(testThisOne: unknown, compareToThisOne: NoInfer<ObjectWithAtLeastSingleEntryArrays<MatchThisGeneric>>, allowExtraProps?: boolean): asserts testThisOne is MatchThisGeneric;
|
|
62
|
+
export {};
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.assertMatchesObjectShape = exports.matchesObjectShape = exports.copyThroughJson = exports.filterObject = exports.mapObjectValues = exports.areJsonEqual = exports.getEntriesSortedByKey = exports.isObject = exports.typedHasProperties = exports.typedHasProperty = exports.getObjectTypedValues = exports.getObjectTypedKeys = exports.filterToEnumValues = exports.isKeyof = exports.isEnumValue = exports.getEnumTypedValues = exports.getEnumTypedKeys = void 0;
|
|
4
|
+
const error_1 = require("./error");
|
|
5
|
+
const function_1 = require("./function");
|
|
6
|
+
function getEnumTypedKeys(input) {
|
|
7
|
+
// enum keys are always strings
|
|
8
|
+
return getObjectTypedKeys(input).filter((key) => isNaN(Number(key)));
|
|
9
|
+
}
|
|
10
|
+
exports.getEnumTypedKeys = getEnumTypedKeys;
|
|
11
|
+
function getEnumTypedValues(input) {
|
|
12
|
+
const keys = getEnumTypedKeys(input);
|
|
13
|
+
return keys.map((key) => input[key]);
|
|
14
|
+
}
|
|
15
|
+
exports.getEnumTypedValues = getEnumTypedValues;
|
|
16
|
+
function isEnumValue(input, checkEnum) {
|
|
17
|
+
return getEnumTypedValues(checkEnum).includes(input);
|
|
18
|
+
}
|
|
19
|
+
exports.isEnumValue = isEnumValue;
|
|
20
|
+
function isKeyof(key, object) {
|
|
21
|
+
return typedHasProperty(object, key);
|
|
22
|
+
}
|
|
23
|
+
exports.isKeyof = isKeyof;
|
|
24
|
+
function filterToEnumValues(inputs, checkEnum, caseInsensitive = false) {
|
|
25
|
+
if (caseInsensitive) {
|
|
26
|
+
return inputs.reduce((accum, currentInput) => {
|
|
27
|
+
const matchedEnumValue = getEnumTypedValues(checkEnum).find((actualEnumValue) => {
|
|
28
|
+
return String(actualEnumValue).toUpperCase() === String(currentInput).toUpperCase();
|
|
29
|
+
});
|
|
30
|
+
if (matchedEnumValue) {
|
|
31
|
+
return accum.concat(matchedEnumValue);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
return accum;
|
|
35
|
+
}
|
|
36
|
+
}, []);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
return inputs.filter((input) => isEnumValue(input, checkEnum));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
exports.filterToEnumValues = filterToEnumValues;
|
|
43
|
+
function getObjectTypedKeys(input) {
|
|
44
|
+
let reflectKeys;
|
|
45
|
+
try {
|
|
46
|
+
reflectKeys = Reflect.ownKeys(input);
|
|
47
|
+
}
|
|
48
|
+
catch (error) { }
|
|
49
|
+
return (reflectKeys !== null && reflectKeys !== void 0 ? reflectKeys : [
|
|
50
|
+
...Object.keys(input),
|
|
51
|
+
...Object.getOwnPropertySymbols(input),
|
|
52
|
+
]);
|
|
53
|
+
}
|
|
54
|
+
exports.getObjectTypedKeys = getObjectTypedKeys;
|
|
55
|
+
function getObjectTypedValues(input) {
|
|
56
|
+
return getObjectTypedKeys(input).map((key) => input[key]);
|
|
57
|
+
}
|
|
58
|
+
exports.getObjectTypedValues = getObjectTypedValues;
|
|
59
|
+
const hasPropertyAttempts = [
|
|
60
|
+
(object, key) => {
|
|
61
|
+
return key in object;
|
|
62
|
+
},
|
|
63
|
+
(object, key) => {
|
|
64
|
+
/** This handles cases where the input object can't use `in` directly, like string literals */
|
|
65
|
+
return key in object.constructor.prototype;
|
|
66
|
+
},
|
|
67
|
+
];
|
|
68
|
+
function typedHasProperty(inputObject, inputKey) {
|
|
69
|
+
if (!inputObject) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
return hasPropertyAttempts.some((attemptCallback) => {
|
|
73
|
+
try {
|
|
74
|
+
return attemptCallback(inputObject, inputKey);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
exports.typedHasProperty = typedHasProperty;
|
|
82
|
+
function typedHasProperties(inputObject, inputKeys) {
|
|
83
|
+
return inputObject && inputKeys.every((key) => typedHasProperty(inputObject, key));
|
|
84
|
+
}
|
|
85
|
+
exports.typedHasProperties = typedHasProperties;
|
|
86
|
+
function isObject(input) {
|
|
87
|
+
return !!input && typeof input === 'object';
|
|
88
|
+
}
|
|
89
|
+
exports.isObject = isObject;
|
|
90
|
+
function getEntriesSortedByKey(input) {
|
|
91
|
+
return Object.entries(input).sort((tupleA, tupleB) => tupleA[0].localeCompare(tupleB[0]));
|
|
92
|
+
}
|
|
93
|
+
exports.getEntriesSortedByKey = getEntriesSortedByKey;
|
|
94
|
+
function areJsonEqual(a, b) {
|
|
95
|
+
try {
|
|
96
|
+
const sortedAEntries = getEntriesSortedByKey(a);
|
|
97
|
+
const sortedBEntries = getEntriesSortedByKey(b);
|
|
98
|
+
return JSON.stringify(sortedAEntries) === JSON.stringify(sortedBEntries);
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
console.error(`Failed to compare objects using JSON.stringify`);
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
exports.areJsonEqual = areJsonEqual;
|
|
106
|
+
/**
|
|
107
|
+
* Creates a new object with the same properties as the input object, but with values set to the
|
|
108
|
+
* result of mapCallback for each property.
|
|
109
|
+
*/
|
|
110
|
+
function mapObjectValues(inputObject, mapCallback) {
|
|
111
|
+
let gotAPromise = false;
|
|
112
|
+
const mappedObject = getObjectTypedKeys(inputObject).reduce((accum, currentKey) => {
|
|
113
|
+
const mappedValue = mapCallback(currentKey, inputObject[currentKey], inputObject);
|
|
114
|
+
if (mappedValue instanceof Promise) {
|
|
115
|
+
gotAPromise = true;
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
...accum,
|
|
119
|
+
[currentKey]: mappedValue,
|
|
120
|
+
};
|
|
121
|
+
}, {});
|
|
122
|
+
if (gotAPromise) {
|
|
123
|
+
return new Promise(async (resolve, reject) => {
|
|
124
|
+
try {
|
|
125
|
+
await Promise.all(getObjectTypedKeys(mappedObject).map(async (key) => {
|
|
126
|
+
const value = await mappedObject[key];
|
|
127
|
+
mappedObject[key] = value;
|
|
128
|
+
}));
|
|
129
|
+
resolve(mappedObject);
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
reject(error);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
return mappedObject;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
exports.mapObjectValues = mapObjectValues;
|
|
141
|
+
function filterObject(inputObject, callback) {
|
|
142
|
+
const filteredKeys = getObjectTypedKeys(inputObject).filter((key) => {
|
|
143
|
+
const value = inputObject[key];
|
|
144
|
+
return callback(key, value, inputObject);
|
|
145
|
+
});
|
|
146
|
+
return filteredKeys.reduce((accum, key) => {
|
|
147
|
+
accum[key] = inputObject[key];
|
|
148
|
+
return accum;
|
|
149
|
+
}, {});
|
|
150
|
+
}
|
|
151
|
+
exports.filterObject = filterObject;
|
|
152
|
+
/** The input here must be serializable otherwise JSON parsing errors will be thrown */
|
|
153
|
+
function copyThroughJson(input) {
|
|
154
|
+
try {
|
|
155
|
+
return JSON.parse(JSON.stringify(input));
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
console.error(`Failed to JSON copy for`, input);
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
exports.copyThroughJson = copyThroughJson;
|
|
163
|
+
/**
|
|
164
|
+
* Checks that the first input, testThisOne, matches the object shape of the second input,
|
|
165
|
+
* compareToThisOne. Does not compare exact values of properties, only types.
|
|
166
|
+
*
|
|
167
|
+
* To allow the test input, the first input, to have additional keys that the compare input, the
|
|
168
|
+
* second input, does not have, pass in a third argument set to true.
|
|
169
|
+
*
|
|
170
|
+
* This function REQUIRES a generic to be assigned to it: it cannot infer it from the inputs.
|
|
171
|
+
*
|
|
172
|
+
* The compare input, the second input, is required to have at least one entry in every array value
|
|
173
|
+
* that exists. If more array values are present, they will be considered other possible types for
|
|
174
|
+
* entries in that array.
|
|
175
|
+
*/
|
|
176
|
+
function matchesObjectShape(testThisOne, compareToThisOne, allowExtraProps = false, shouldLogWhy = false) {
|
|
177
|
+
try {
|
|
178
|
+
assertMatchesObjectShape(testThisOne, compareToThisOne, allowExtraProps);
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
if (shouldLogWhy) {
|
|
183
|
+
console.error(error);
|
|
184
|
+
}
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
exports.matchesObjectShape = matchesObjectShape;
|
|
189
|
+
/**
|
|
190
|
+
* Asserts that the first input, testThisOne, matches the object shape of the second input,
|
|
191
|
+
* compareToThisOne. Does not compare exact values of properties, only types.
|
|
192
|
+
*
|
|
193
|
+
* To allow the test input, the first input, to have additional keys that the compare input, the
|
|
194
|
+
* second input, does not have, pass in a third argument set to true.
|
|
195
|
+
*
|
|
196
|
+
* This function REQUIRES a generic to be assigned to it: it cannot infer it from the inputs.
|
|
197
|
+
*
|
|
198
|
+
* The compare input, the second input, is required to have at least one entry in every array value
|
|
199
|
+
* that exists. If more array values are present, they will be considered other possible types for
|
|
200
|
+
* entries in that array.
|
|
201
|
+
*/
|
|
202
|
+
function assertMatchesObjectShape(testThisOne, compareToThisOne, allowExtraProps = false) {
|
|
203
|
+
const testKeys = getObjectTypedKeys(testThisOne);
|
|
204
|
+
const matchKeys = new Set(getObjectTypedKeys(compareToThisOne));
|
|
205
|
+
if (!allowExtraProps) {
|
|
206
|
+
const extraKeys = testKeys.filter((testKey) => !matchKeys.has(testKey));
|
|
207
|
+
if (extraKeys.length) {
|
|
208
|
+
throw new Error(`Test object has extra keys: ${extraKeys.join(', ')}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
matchKeys.forEach((key) => {
|
|
212
|
+
if (!typedHasProperty(testThisOne, key)) {
|
|
213
|
+
throw new Error(`test object does not have key "${String(key)}" from expected shape.`);
|
|
214
|
+
}
|
|
215
|
+
function throwKeyError(reason) {
|
|
216
|
+
throw new Error(`test object value at key "${String(key)}" did not match expected shape: ${reason}`);
|
|
217
|
+
}
|
|
218
|
+
const testValue = testThisOne[key];
|
|
219
|
+
const shouldMatch = compareToThisOne[key];
|
|
220
|
+
compareInnerValue(testValue, shouldMatch, throwKeyError);
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
exports.assertMatchesObjectShape = assertMatchesObjectShape;
|
|
224
|
+
function compareInnerValue(testValue, matchValue, throwKeyError) {
|
|
225
|
+
var _a;
|
|
226
|
+
const testType = typeof testValue;
|
|
227
|
+
const shouldMatchType = typeof matchValue;
|
|
228
|
+
if (testType !== shouldMatchType) {
|
|
229
|
+
throwKeyError(`type "${testType}" did not match expected type "${shouldMatchType}"`);
|
|
230
|
+
}
|
|
231
|
+
try {
|
|
232
|
+
if (typedHasProperty(matchValue, 'constructor')) {
|
|
233
|
+
if (!typedHasProperty(testValue, 'constructor') ||
|
|
234
|
+
testValue.constructor !== matchValue.constructor) {
|
|
235
|
+
throwKeyError(`constructor "${(_a = testValue === null || testValue === void 0 ? void 0 : testValue.constructor) === null || _a === void 0 ? void 0 : _a.name}" did not match expected constructor "${matchValue.constructor}"`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
// ignore errors from trying to find the constructor
|
|
241
|
+
if (error instanceof throwKeyError) {
|
|
242
|
+
throw error;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (Array.isArray(matchValue)) {
|
|
246
|
+
if (!Array.isArray(testValue)) {
|
|
247
|
+
throwKeyError(`expected an array`);
|
|
248
|
+
}
|
|
249
|
+
testValue.forEach((testValueEntry, index) => {
|
|
250
|
+
const errors = matchValue
|
|
251
|
+
.map((matchValue) => {
|
|
252
|
+
try {
|
|
253
|
+
compareInnerValue(testValueEntry, matchValue, throwKeyError);
|
|
254
|
+
return undefined;
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
return new Error(`entry at index "${index}" did not match expected shape: ${(0, error_1.extractErrorMessage)(error)}`);
|
|
258
|
+
}
|
|
259
|
+
})
|
|
260
|
+
.filter(function_1.isTruthy);
|
|
261
|
+
if (errors.length === matchValue.length) {
|
|
262
|
+
throw new Error(`entry at index "${index}" did not match any of the possible types from "${matchValue.join(', ')}"`);
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
else if (isObject(matchValue)) {
|
|
267
|
+
assertMatchesObjectShape(testValue, matchValue);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare function wait(delayMs: number): Promise<void>;
|
|
2
|
+
export declare function isPromiseLike<T>(input: T | unknown): input is T extends PromiseLike<infer ValueType> ? PromiseLike<ValueType> : PromiseLike<unknown>;
|
|
3
|
+
export declare class PromiseTimeoutError extends Error {
|
|
4
|
+
readonly durationMs: number;
|
|
5
|
+
readonly message: string;
|
|
6
|
+
readonly name = "PromiseTimeoutError";
|
|
7
|
+
constructor(durationMs: number, message?: string);
|
|
8
|
+
}
|
|
9
|
+
export declare function wrapPromiseInTimeout<PromiseValueType>(durationMs: number, originalPromise: PromiseLike<PromiseValueType>): Promise<PromiseValueType>;
|
|
10
|
+
/** A promise which can be resolved or rejected by external code. */
|
|
11
|
+
export declare type DeferredPromiseWrapper<T> = {
|
|
12
|
+
promise: Promise<T>;
|
|
13
|
+
resolve: (value: T | PromiseLike<T>) => void;
|
|
14
|
+
reject: (reason?: any) => void;
|
|
15
|
+
};
|
|
16
|
+
export declare function createDeferredPromiseWrapper<T = void>(): DeferredPromiseWrapper<T>;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createDeferredPromiseWrapper = exports.wrapPromiseInTimeout = exports.PromiseTimeoutError = exports.isPromiseLike = exports.wait = void 0;
|
|
4
|
+
const object_1 = require("./object");
|
|
5
|
+
function wait(delayMs) {
|
|
6
|
+
const deferredPromiseWrapper = createDeferredPromiseWrapper();
|
|
7
|
+
if (delayMs === Infinity || delayMs < 0) {
|
|
8
|
+
return deferredPromiseWrapper.promise;
|
|
9
|
+
}
|
|
10
|
+
setTimeout(() => {
|
|
11
|
+
deferredPromiseWrapper.resolve();
|
|
12
|
+
}, delayMs);
|
|
13
|
+
return deferredPromiseWrapper.promise;
|
|
14
|
+
}
|
|
15
|
+
exports.wait = wait;
|
|
16
|
+
function isPromiseLike(input) {
|
|
17
|
+
if ((0, object_1.typedHasProperty)(input, 'then') && typeof input.then === 'function') {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.isPromiseLike = isPromiseLike;
|
|
25
|
+
class PromiseTimeoutError extends Error {
|
|
26
|
+
constructor(durationMs, message = `Promised timed out after ${durationMs} ms.`) {
|
|
27
|
+
super(message);
|
|
28
|
+
this.durationMs = durationMs;
|
|
29
|
+
this.message = message;
|
|
30
|
+
this.name = 'PromiseTimeoutError';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.PromiseTimeoutError = PromiseTimeoutError;
|
|
34
|
+
function wrapPromiseInTimeout(durationMs, originalPromise) {
|
|
35
|
+
return new Promise(async (resolve, reject) => {
|
|
36
|
+
const timeoutId = setTimeout(() => {
|
|
37
|
+
reject(new PromiseTimeoutError(durationMs));
|
|
38
|
+
}, durationMs);
|
|
39
|
+
try {
|
|
40
|
+
const result = await originalPromise;
|
|
41
|
+
resolve(result);
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
reject(error);
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
clearTimeout(timeoutId);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
exports.wrapPromiseInTimeout = wrapPromiseInTimeout;
|
|
52
|
+
function createDeferredPromiseWrapper() {
|
|
53
|
+
let resolve;
|
|
54
|
+
let reject;
|
|
55
|
+
const promise = new Promise((resolveCallback, rejectCallback) => {
|
|
56
|
+
resolve = resolveCallback;
|
|
57
|
+
reject = rejectCallback;
|
|
58
|
+
});
|
|
59
|
+
if (!resolve || !reject) {
|
|
60
|
+
throw new Error(`Reject and resolve callbacks were not set by the promise constructor for ${createDeferredPromiseWrapper.name}.`);
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
promise,
|
|
64
|
+
resolve,
|
|
65
|
+
reject,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
exports.createDeferredPromiseWrapper = createDeferredPromiseWrapper;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.safeMatch = exports.addRegExpFlags = exports.deDupeRegExFlags = void 0;
|
|
4
|
+
function deDupeRegExFlags(flags) {
|
|
5
|
+
const deDuped = new Set(Array.from(flags.toLowerCase()));
|
|
6
|
+
return Array.from(deDuped).join('');
|
|
7
|
+
}
|
|
8
|
+
exports.deDupeRegExFlags = deDupeRegExFlags;
|
|
9
|
+
function addRegExpFlags(originalRegExp, flags) {
|
|
10
|
+
return new RegExp(originalRegExp.source, deDupeRegExFlags([
|
|
11
|
+
originalRegExp.flags,
|
|
12
|
+
flags,
|
|
13
|
+
].join('')));
|
|
14
|
+
}
|
|
15
|
+
exports.addRegExpFlags = addRegExpFlags;
|
|
16
|
+
function safeMatch(input, regExp) {
|
|
17
|
+
const match = input.match(regExp);
|
|
18
|
+
return match !== null && match !== void 0 ? match : [];
|
|
19
|
+
}
|
|
20
|
+
exports.safeMatch = safeMatch;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Join elements into a string with commas separating each value. Add a conjunction before the final
|
|
3
|
+
* item in the list. If the array has a length < 2, the conjunction is not added. If the list is
|
|
4
|
+
* only of length 2, then no commas are added.
|
|
5
|
+
*
|
|
6
|
+
* @param list Array of items to be converted into strings. Works best if these are simply strings
|
|
7
|
+
* to begin with.
|
|
8
|
+
* @param conjunction Defaults to 'and'. The conjunction to be used before the final element.
|
|
9
|
+
*/
|
|
10
|
+
export declare function joinWithFinalConjunction<T>(list: T[], conjunction?: string): string;
|
|
11
|
+
export declare function removeAnsiEscapeCodes(input: string): string;
|
|
12
|
+
export declare const removeColor: typeof removeAnsiEscapeCodes;
|
|
13
|
+
export declare function removeCommasFromNumberString(numberString: string): string;
|
|
14
|
+
/** Collapse all consecutive white space into just one space and trim surrounding whitespace. */
|
|
15
|
+
export declare function collapseWhiteSpace(input: string): string;
|
|
16
|
+
/** Same as String.prototype.split but includes the delimiter to split by in the output array. */
|
|
17
|
+
export declare function splitIncludeSplit(original: string, splitterInput: string | RegExp, caseSensitive: boolean): string[];
|
|
18
|
+
export declare type CasingOptions = {
|
|
19
|
+
capitalizeFirstLetter: boolean;
|
|
20
|
+
};
|
|
21
|
+
export declare function capitalizeFirstLetter(input: string): string;
|
|
22
|
+
export declare function kebabCaseToCamelCase(rawKebabCase: string, casingOptions?: Partial<CasingOptions> | undefined): string;
|
|
23
|
+
export declare function camelCaseToKebabCase(rawCamelCase: string): string;
|
|
24
|
+
export declare function replaceStringAtIndex(originalString: string, start: number, newString: string, length?: number): string;
|
|
25
|
+
/**
|
|
26
|
+
* Escapes characters from the given string so that it can be used within a RegExp without being
|
|
27
|
+
* parsed as RegExp syntax.
|
|
28
|
+
*/
|
|
29
|
+
export declare function escapeStringForRegExp(input: string): string;
|
|
30
|
+
export declare function getAllIndexesOf(searchIn: string, searchForInput: string | RegExp, caseSensitive: boolean, includeLength: true): {
|
|
31
|
+
index: number;
|
|
32
|
+
length: number;
|
|
33
|
+
}[];
|
|
34
|
+
export declare function getAllIndexesOf(searchIn: string, searchForInput: string | RegExp, caseSensitive: boolean, includeLength?: false | undefined): number[];
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getAllIndexesOf = exports.escapeStringForRegExp = exports.replaceStringAtIndex = exports.camelCaseToKebabCase = exports.kebabCaseToCamelCase = exports.capitalizeFirstLetter = exports.splitIncludeSplit = exports.collapseWhiteSpace = exports.removeCommasFromNumberString = exports.removeColor = exports.removeAnsiEscapeCodes = exports.joinWithFinalConjunction = void 0;
|
|
4
|
+
const ansi_1 = require("./ansi");
|
|
5
|
+
const regexp_1 = require("./regexp");
|
|
6
|
+
/**
|
|
7
|
+
* Join elements into a string with commas separating each value. Add a conjunction before the final
|
|
8
|
+
* item in the list. If the array has a length < 2, the conjunction is not added. If the list is
|
|
9
|
+
* only of length 2, then no commas are added.
|
|
10
|
+
*
|
|
11
|
+
* @param list Array of items to be converted into strings. Works best if these are simply strings
|
|
12
|
+
* to begin with.
|
|
13
|
+
* @param conjunction Defaults to 'and'. The conjunction to be used before the final element.
|
|
14
|
+
*/
|
|
15
|
+
function joinWithFinalConjunction(list, conjunction = 'and') {
|
|
16
|
+
if (list.length < 2) {
|
|
17
|
+
/**
|
|
18
|
+
* If there are not multiple things in the list to join, just turn the list into a string
|
|
19
|
+
* for an empty list, this will be '', for a single item list, this will just be the first
|
|
20
|
+
* item as a string.
|
|
21
|
+
*/
|
|
22
|
+
return list.join('');
|
|
23
|
+
}
|
|
24
|
+
/** When there are only two items in the list, we don't want any commas. */
|
|
25
|
+
const commaSep = list.length > 2 ? ', ' : ' ';
|
|
26
|
+
const commaJoined = list.slice(0, -1).join(commaSep);
|
|
27
|
+
const fullyJoined = `${commaJoined}${commaSep}${conjunction} ${list[list.length - 1]}`;
|
|
28
|
+
return fullyJoined;
|
|
29
|
+
}
|
|
30
|
+
exports.joinWithFinalConjunction = joinWithFinalConjunction;
|
|
31
|
+
function removeAnsiEscapeCodes(input) {
|
|
32
|
+
return input.replace(ansi_1.ansiRegex, '');
|
|
33
|
+
}
|
|
34
|
+
exports.removeAnsiEscapeCodes = removeAnsiEscapeCodes;
|
|
35
|
+
exports.removeColor = removeAnsiEscapeCodes;
|
|
36
|
+
function removeCommasFromNumberString(numberString) {
|
|
37
|
+
return numberString.replace(/,/g, '');
|
|
38
|
+
}
|
|
39
|
+
exports.removeCommasFromNumberString = removeCommasFromNumberString;
|
|
40
|
+
/** Collapse all consecutive white space into just one space and trim surrounding whitespace. */
|
|
41
|
+
function collapseWhiteSpace(input) {
|
|
42
|
+
return (input
|
|
43
|
+
// sometimes \n isn't included in \s
|
|
44
|
+
.replace(/\n/g, ' ')
|
|
45
|
+
.trim()
|
|
46
|
+
.replace(/\s{2,}/g, ' '));
|
|
47
|
+
}
|
|
48
|
+
exports.collapseWhiteSpace = collapseWhiteSpace;
|
|
49
|
+
/** Same as String.prototype.split but includes the delimiter to split by in the output array. */
|
|
50
|
+
function splitIncludeSplit(original, splitterInput, caseSensitive) {
|
|
51
|
+
const indexLengths = getAllIndexesOf(original, splitterInput, caseSensitive, true);
|
|
52
|
+
const splitter = makeCaseInsensitiveRegExp(splitterInput, caseSensitive);
|
|
53
|
+
const splits = original.split(splitter);
|
|
54
|
+
const splitterIncluded = splits.reduce((accum, current, index) => {
|
|
55
|
+
// this will be undefined on the last index
|
|
56
|
+
const splitterLength = indexLengths[index];
|
|
57
|
+
const includeCurrent = accum.concat(current);
|
|
58
|
+
if (splitterLength) {
|
|
59
|
+
const splitterMatch = original.slice(splitterLength.index, splitterLength.index + splitterLength.length);
|
|
60
|
+
return includeCurrent.concat(splitterMatch);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
return includeCurrent;
|
|
64
|
+
}
|
|
65
|
+
}, []);
|
|
66
|
+
return splitterIncluded;
|
|
67
|
+
}
|
|
68
|
+
exports.splitIncludeSplit = splitIncludeSplit;
|
|
69
|
+
const defaultCasingOptions = {
|
|
70
|
+
capitalizeFirstLetter: false,
|
|
71
|
+
};
|
|
72
|
+
function capitalizeFirstLetter(input) {
|
|
73
|
+
var _a;
|
|
74
|
+
if (!input.length) {
|
|
75
|
+
return '';
|
|
76
|
+
}
|
|
77
|
+
const firstLetter = (_a = input[0]) !== null && _a !== void 0 ? _a : '';
|
|
78
|
+
return firstLetter.toUpperCase() + input.slice(1);
|
|
79
|
+
}
|
|
80
|
+
exports.capitalizeFirstLetter = capitalizeFirstLetter;
|
|
81
|
+
function maybeCapitalize(input, casingOptions = defaultCasingOptions) {
|
|
82
|
+
return casingOptions.capitalizeFirstLetter ? capitalizeFirstLetter(input) : input;
|
|
83
|
+
}
|
|
84
|
+
function kebabCaseToCamelCase(rawKebabCase, casingOptions = defaultCasingOptions) {
|
|
85
|
+
const kebabCase = rawKebabCase.toLowerCase();
|
|
86
|
+
if (!kebabCase.length) {
|
|
87
|
+
return '';
|
|
88
|
+
}
|
|
89
|
+
const camelCase = kebabCase
|
|
90
|
+
.replace(/^-+/, '')
|
|
91
|
+
.replace(/-{2,}/g, '-')
|
|
92
|
+
.replace(/-(?:.|$)/g, (dashMatch) => {
|
|
93
|
+
const letter = dashMatch[1];
|
|
94
|
+
if (letter) {
|
|
95
|
+
return letter.toUpperCase();
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
return '';
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
return maybeCapitalize(camelCase, casingOptions);
|
|
102
|
+
}
|
|
103
|
+
exports.kebabCaseToCamelCase = kebabCaseToCamelCase;
|
|
104
|
+
function isLowerCase(input) {
|
|
105
|
+
// this excludes letters that are identical between lower and upper case like punctuation
|
|
106
|
+
return input !== input.toUpperCase();
|
|
107
|
+
}
|
|
108
|
+
function camelCaseToKebabCase(rawCamelCase) {
|
|
109
|
+
const kebabCase = rawCamelCase
|
|
110
|
+
.split('')
|
|
111
|
+
.reduce((accum, currentLetter, index, originalString) => {
|
|
112
|
+
var _a, _b;
|
|
113
|
+
const previousLetter = index > 0 ? (_a = originalString[index - 1]) !== null && _a !== void 0 ? _a : '' : '';
|
|
114
|
+
const nextLetter = index < originalString.length ? (_b = originalString[index + 1]) !== null && _b !== void 0 ? _b : '' : '';
|
|
115
|
+
const possibleWordBoundary = isLowerCase(previousLetter) || isLowerCase(nextLetter);
|
|
116
|
+
if (currentLetter === currentLetter.toLowerCase() ||
|
|
117
|
+
index === 0 ||
|
|
118
|
+
!possibleWordBoundary) {
|
|
119
|
+
accum += currentLetter;
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
accum += `-${currentLetter.toLowerCase()}`;
|
|
123
|
+
}
|
|
124
|
+
return accum;
|
|
125
|
+
}, '')
|
|
126
|
+
.toLowerCase();
|
|
127
|
+
return kebabCase;
|
|
128
|
+
}
|
|
129
|
+
exports.camelCaseToKebabCase = camelCaseToKebabCase;
|
|
130
|
+
function replaceStringAtIndex(originalString, start, newString, length = newString.length) {
|
|
131
|
+
const before = originalString.substring(0, start);
|
|
132
|
+
const after = originalString.substring(start + length);
|
|
133
|
+
return `${before}${newString}${after}`;
|
|
134
|
+
}
|
|
135
|
+
exports.replaceStringAtIndex = replaceStringAtIndex;
|
|
136
|
+
/**
|
|
137
|
+
* Escapes characters from the given string so that it can be used within a RegExp without being
|
|
138
|
+
* parsed as RegExp syntax.
|
|
139
|
+
*/
|
|
140
|
+
function escapeStringForRegExp(input) {
|
|
141
|
+
return input.replace(/[\^$\\.*+?()[\]{}|]/g, '\\$&');
|
|
142
|
+
}
|
|
143
|
+
exports.escapeStringForRegExp = escapeStringForRegExp;
|
|
144
|
+
function makeCaseInsensitiveRegExp(searchForInput, caseSensitive) {
|
|
145
|
+
const regExpFlags = `g${!caseSensitive && typeof searchForInput === 'string' ? 'i' : ''}`;
|
|
146
|
+
const searchFor = searchForInput instanceof RegExp
|
|
147
|
+
? new RegExp(searchForInput.source, (0, regexp_1.deDupeRegExFlags)(`${searchForInput.flags}${regExpFlags}`))
|
|
148
|
+
: new RegExp(escapeStringForRegExp(searchForInput), regExpFlags);
|
|
149
|
+
return searchFor;
|
|
150
|
+
}
|
|
151
|
+
function getAllIndexesOf(searchIn, searchForInput,
|
|
152
|
+
/**
|
|
153
|
+
* CaseSensitive only applies when the input is a string. Otherwise, the RegExp's "i" flag is
|
|
154
|
+
* used to determine case sensitivity.
|
|
155
|
+
*/
|
|
156
|
+
caseSensitive, includeLength = false) {
|
|
157
|
+
const searchFor = makeCaseInsensitiveRegExp(searchForInput, caseSensitive);
|
|
158
|
+
const indexes = [];
|
|
159
|
+
const indexesAndLengths = [];
|
|
160
|
+
searchIn.replace(searchFor, (...matchResults) => {
|
|
161
|
+
/**
|
|
162
|
+
* Grabbing the second to last entry in the array (rather than the second) takes capture
|
|
163
|
+
* groups into account.
|
|
164
|
+
*/
|
|
165
|
+
const matchIndex = matchResults[matchResults.length - 2];
|
|
166
|
+
if (typeof matchIndex !== 'number') {
|
|
167
|
+
throw new Error(`Match index "${matchIndex}" is not a number. Searching for "${searchForInput}" in "${searchIn}".`);
|
|
168
|
+
}
|
|
169
|
+
const regExpMatch = matchResults[0];
|
|
170
|
+
if (typeof regExpMatch !== 'string') {
|
|
171
|
+
throw new Error(`regExpMatch should've been a string but was ${typeof regExpMatch}!`);
|
|
172
|
+
}
|
|
173
|
+
indexesAndLengths.push({ index: matchIndex, length: regExpMatch.length });
|
|
174
|
+
indexes.push(matchIndex);
|
|
175
|
+
const originalMatch = matchResults[0];
|
|
176
|
+
if (typeof originalMatch !== 'string') {
|
|
177
|
+
throw new Error(`Original match when searching for "${searchForInput}" in "${searchIn}" at index ${matchIndex} is not a string.`);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Don't actually change any text. What we do here doesn't matter because we're not using
|
|
181
|
+
* the output of the .replace method, we're just producing side effects.
|
|
182
|
+
*/
|
|
183
|
+
return originalMatch;
|
|
184
|
+
});
|
|
185
|
+
if (includeLength) {
|
|
186
|
+
return indexesAndLengths;
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
return indexes;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
exports.getAllIndexesOf = getAllIndexesOf;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare type Tuple<ArrayElementGeneric, LengthGeneric extends number> = LengthGeneric extends LengthGeneric ? number extends LengthGeneric ? ArrayElementGeneric[] : _TupleOf<ArrayElementGeneric, LengthGeneric, []> : never;
|
|
2
|
+
declare type _TupleOf<ArrayElementGeneric, LengthGeneric extends number, FullArrayGeneric extends unknown[]> = FullArrayGeneric['length'] extends LengthGeneric ? FullArrayGeneric : _TupleOf<ArrayElementGeneric, LengthGeneric, [ArrayElementGeneric, ...FullArrayGeneric]>;
|
|
3
|
+
export declare type AtLeastTuple<ArrayElementGeneric, LengthGeneric extends number> = readonly [
|
|
4
|
+
...Tuple<ArrayElementGeneric, LengthGeneric>,
|
|
5
|
+
...ArrayElementGeneric[]
|
|
6
|
+
];
|
|
7
|
+
export declare function isLengthAtLeast<ArrayElementGeneric, LengthGeneric extends number>(array: ReadonlyArray<ArrayElementGeneric>, length: LengthGeneric): array is AtLeastTuple<ArrayElementGeneric, LengthGeneric>;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { Equal } from '@type-challenges/utils';
|
|
2
|
+
export type { Alike, Debug, Equal, ExpectExtends as DoesExtend, ExpectFalse, ExpectTrue, ExpectValidArgs as HasValidArgs, IsAny, IsFalse, IsTrue, MergeInsertions, NotAny, NotEqual, UnionToIntersection, } from '@type-challenges/utils';
|
|
3
|
+
export declare type CouldBeNullish<VALUE> = Equal<VALUE, NonNullable<VALUE>> extends true ? false : true;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/** Makes all properties in an object writable. This is the opposite of Readonly<> */
|
|
2
|
+
export declare type Writeable<T> = {
|
|
3
|
+
-readonly [P in keyof T]: T[P];
|
|
4
|
+
};
|
|
5
|
+
/** Makes all property values in an object also readonly. Can cause issues on primitive. */
|
|
6
|
+
export declare type DeepWriteable<T> = {
|
|
7
|
+
-readonly [P in keyof T]: DeepWriteable<T[P]>;
|
|
8
|
+
};
|
|
9
|
+
/** Replace properties in T with properties in U. */
|
|
10
|
+
export declare type Overwrite<T, U> = Pick<T, Exclude<keyof T, keyof U>> & U;
|
|
11
|
+
/** Extract the element type out of an array type. */
|
|
12
|
+
export declare type ArrayElement<ArrayType extends ReadonlyArray<any>> = ArrayType[number];
|
|
13
|
+
/**
|
|
14
|
+
* Same as the Required<> built-in type helper but this requires that each property be present and
|
|
15
|
+
* be not null.
|
|
16
|
+
*/
|
|
17
|
+
export declare type RequiredAndNotNull<T> = {
|
|
18
|
+
[P in keyof T]-?: NonNullable<T[P]>;
|
|
19
|
+
};
|
|
20
|
+
/** Require only a subset of object properties. */
|
|
21
|
+
export declare type RequiredBy<T, K extends keyof T> = Overwrite<T, Required<Pick<T, K>>>;
|
|
22
|
+
/**
|
|
23
|
+
* Require only a subset of object properties and require that they be not null. This is
|
|
24
|
+
* particularly useful in conjunction with the "exactOptionalPropertyTypes" tsconfig flag.
|
|
25
|
+
*/
|
|
26
|
+
export declare type RequiredAndNotNullBy<T, K extends keyof T> = Omit<T, K> & Required<{
|
|
27
|
+
[PropertyName in K]: NonNullable<T[PropertyName]>;
|
|
28
|
+
}>;
|
|
29
|
+
/** If type T = type U, then type Y. Else type N. */
|
|
30
|
+
export declare type IfEquals<T, U, Y = unknown, N = never> = (<G>() => G extends T ? 1 : 2) extends <G>() => G extends U ? 1 : 2 ? Y : N;
|
|
31
|
+
export declare type UnPromise<T> = T extends PromiseLike<infer PromiseType> ? Awaited<PromiseType> : T;
|
|
32
|
+
/**
|
|
33
|
+
* This function returns another function that simply returns whatever input it's given. However, it
|
|
34
|
+
* also checks that the input matches the original wrapNarrowTypeWithTypeCheck's generic, while
|
|
35
|
+
* maintaining strict "const" like typing.
|
|
36
|
+
*
|
|
37
|
+
* Use like this: wrapNarrowTypeWithTypeCheck<EnforcedTypeHere>()(valueToEnforceTypeOn as const)
|
|
38
|
+
*
|
|
39
|
+
* Sometimes "as const" isn't required, usually it is for any object or array though.
|
|
40
|
+
*/
|
|
41
|
+
export declare function wrapNarrowTypeWithTypeCheck<P>(): <T extends P>(input: T) => Readonly<T>;
|
|
42
|
+
/**
|
|
43
|
+
* This type helper is useful for forcing function generics to be explicitly provided, rather than
|
|
44
|
+
* inferring them from the given inputs. See the test file for examples.
|
|
45
|
+
*/
|
|
46
|
+
export declare type NoInfer<T> = [T][T extends any ? 0 : never];
|
|
47
|
+
/**
|
|
48
|
+
* This is a type helper that ensures the given input matches the given generic type. The generic is
|
|
49
|
+
* setup in such a way that if it is omitted (which is typically allowed in TypeScript, resulting in
|
|
50
|
+
* the generic being inferred from the inputs), there will actually be a type error. This forces
|
|
51
|
+
* each usage of this function to explicitly specify the generic, thus giving us type safety for the
|
|
52
|
+
* input.
|
|
53
|
+
*/
|
|
54
|
+
export declare function ensureType<ExpectedType = never>(input: NoInfer<ExpectedType>): NoInfer<ExpectedType>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ensureType = exports.wrapNarrowTypeWithTypeCheck = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* This function returns another function that simply returns whatever input it's given. However, it
|
|
6
|
+
* also checks that the input matches the original wrapNarrowTypeWithTypeCheck's generic, while
|
|
7
|
+
* maintaining strict "const" like typing.
|
|
8
|
+
*
|
|
9
|
+
* Use like this: wrapNarrowTypeWithTypeCheck<EnforcedTypeHere>()(valueToEnforceTypeOn as const)
|
|
10
|
+
*
|
|
11
|
+
* Sometimes "as const" isn't required, usually it is for any object or array though.
|
|
12
|
+
*/
|
|
13
|
+
function wrapNarrowTypeWithTypeCheck() {
|
|
14
|
+
return (input) => {
|
|
15
|
+
return input;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
exports.wrapNarrowTypeWithTypeCheck = wrapNarrowTypeWithTypeCheck;
|
|
19
|
+
/**
|
|
20
|
+
* This is a type helper that ensures the given input matches the given generic type. The generic is
|
|
21
|
+
* setup in such a way that if it is omitted (which is typically allowed in TypeScript, resulting in
|
|
22
|
+
* the generic being inferred from the inputs), there will actually be a type error. This forces
|
|
23
|
+
* each usage of this function to explicitly specify the generic, thus giving us type safety for the
|
|
24
|
+
* input.
|
|
25
|
+
*/
|
|
26
|
+
function ensureType(input) {
|
|
27
|
+
return input;
|
|
28
|
+
}
|
|
29
|
+
exports.ensureType = ensureType;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export * from './augments/ansi';
|
|
2
|
+
export * from './augments/array';
|
|
3
|
+
export * from './augments/date';
|
|
4
|
+
export * from './augments/error';
|
|
5
|
+
export * from './augments/function';
|
|
6
|
+
export * from './augments/number';
|
|
7
|
+
export * from './augments/object';
|
|
8
|
+
export * from './augments/promise';
|
|
9
|
+
export * from './augments/regexp';
|
|
10
|
+
export * from './augments/string';
|
|
11
|
+
export * from './augments/tuple';
|
|
12
|
+
export * from './augments/type';
|
|
13
|
+
export * from './augments/type-test';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./augments/ansi"), exports);
|
|
18
|
+
__exportStar(require("./augments/array"), exports);
|
|
19
|
+
__exportStar(require("./augments/date"), exports);
|
|
20
|
+
__exportStar(require("./augments/error"), exports);
|
|
21
|
+
__exportStar(require("./augments/function"), exports);
|
|
22
|
+
__exportStar(require("./augments/number"), exports);
|
|
23
|
+
__exportStar(require("./augments/object"), exports);
|
|
24
|
+
__exportStar(require("./augments/promise"), exports);
|
|
25
|
+
__exportStar(require("./augments/regexp"), exports);
|
|
26
|
+
__exportStar(require("./augments/string"), exports);
|
|
27
|
+
__exportStar(require("./augments/tuple"), exports);
|
|
28
|
+
__exportStar(require("./augments/type"), exports);
|
|
29
|
+
__exportStar(require("./augments/type-test"), exports);
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@augment-vir/common",
|
|
3
|
+
"version": "4.0.0",
|
|
4
|
+
"homepage": "https://github.com/electrovir/augment-vir/tree/main/packages/common",
|
|
5
|
+
"bugs": {
|
|
6
|
+
"url": "https://github.com/electrovir/augment-vir/issues"
|
|
7
|
+
},
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/electrovir/augment-vir"
|
|
11
|
+
},
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"author": {
|
|
14
|
+
"name": "electrovir",
|
|
15
|
+
"url": "https://github.com/electrovir"
|
|
16
|
+
},
|
|
17
|
+
"main": "dist/index.js",
|
|
18
|
+
"types": "dist/index.d.ts",
|
|
19
|
+
"scripts": {
|
|
20
|
+
"compile": "rm -rf dist && virmator compile",
|
|
21
|
+
"docs:update": "virmator code-in-markdown",
|
|
22
|
+
"test": "echo \"use common-test to run tests\" && exit 0"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@type-challenges/utils": "^0.1.1"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"prettier": "^2.7.1",
|
|
29
|
+
"prettier-plugin-jsdoc": "^0.4.2",
|
|
30
|
+
"prettier-plugin-multiline-arrays": "^1.1.1",
|
|
31
|
+
"prettier-plugin-organize-imports": "^3.2.0",
|
|
32
|
+
"prettier-plugin-packagejson": "^2.3.0",
|
|
33
|
+
"prettier-plugin-sort-json": "^0.0.3",
|
|
34
|
+
"prettier-plugin-toml": "^0.3.1",
|
|
35
|
+
"ts-node": "^10.9.1",
|
|
36
|
+
"typescript": "^4.8.4"
|
|
37
|
+
},
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
}
|
|
41
|
+
}
|