typeshi 1.0.1

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.
Files changed (55) hide show
  1. package/dist/config/dataLoader.d.ts +37 -0
  2. package/dist/config/dataLoader.js +171 -0
  3. package/dist/config/env.d.ts +23 -0
  4. package/dist/config/env.js +55 -0
  5. package/dist/config/index.d.ts +6 -0
  6. package/dist/config/index.js +22 -0
  7. package/dist/config/setupLog.d.ts +39 -0
  8. package/dist/config/setupLog.js +144 -0
  9. package/dist/index.d.ts +8 -0
  10. package/dist/index.js +44 -0
  11. package/dist/utils/argumentValidation.d.ts +192 -0
  12. package/dist/utils/argumentValidation.js +807 -0
  13. package/dist/utils/io/dateTime.d.ts +96 -0
  14. package/dist/utils/io/dateTime.js +202 -0
  15. package/dist/utils/io/index.d.ts +8 -0
  16. package/dist/utils/io/index.js +24 -0
  17. package/dist/utils/io/logging.d.ts +34 -0
  18. package/dist/utils/io/logging.js +260 -0
  19. package/dist/utils/io/reading.d.ts +265 -0
  20. package/dist/utils/io/reading.js +1245 -0
  21. package/dist/utils/io/types/Csv.d.ts +31 -0
  22. package/dist/utils/io/types/Csv.js +29 -0
  23. package/dist/utils/io/types/Io.TypeGuards.d.ts +31 -0
  24. package/dist/utils/io/types/Io.TypeGuards.js +75 -0
  25. package/dist/utils/io/types/Io.d.ts +49 -0
  26. package/dist/utils/io/types/Io.js +2 -0
  27. package/dist/utils/io/types/index.d.ts +6 -0
  28. package/dist/utils/io/types/index.js +22 -0
  29. package/dist/utils/io/writing.d.ts +67 -0
  30. package/dist/utils/io/writing.js +333 -0
  31. package/dist/utils/regex/cleaning.d.ts +65 -0
  32. package/dist/utils/regex/cleaning.js +162 -0
  33. package/dist/utils/regex/configureParameters.d.ts +23 -0
  34. package/dist/utils/regex/configureParameters.js +63 -0
  35. package/dist/utils/regex/email.d.ts +6 -0
  36. package/dist/utils/regex/email.js +37 -0
  37. package/dist/utils/regex/entity.d.ts +59 -0
  38. package/dist/utils/regex/entity.js +168 -0
  39. package/dist/utils/regex/index.d.ts +11 -0
  40. package/dist/utils/regex/index.js +27 -0
  41. package/dist/utils/regex/misc.d.ts +37 -0
  42. package/dist/utils/regex/misc.js +75 -0
  43. package/dist/utils/regex/phone.d.ts +83 -0
  44. package/dist/utils/regex/phone.js +132 -0
  45. package/dist/utils/regex/stringOperations.d.ts +45 -0
  46. package/dist/utils/regex/stringOperations.js +201 -0
  47. package/dist/utils/regex/types/StringOptions.d.ts +87 -0
  48. package/dist/utils/regex/types/StringOptions.js +25 -0
  49. package/dist/utils/regex/types/index.d.ts +5 -0
  50. package/dist/utils/regex/types/index.js +21 -0
  51. package/dist/utils/regex/types/typeGuards.d.ts +12 -0
  52. package/dist/utils/regex/types/typeGuards.js +15 -0
  53. package/dist/utils/typeValidation.d.ts +163 -0
  54. package/dist/utils/typeValidation.js +308 -0
  55. package/package.json +56 -0
@@ -0,0 +1,37 @@
1
+ /**
2
+ * @file src/utils/regex/misc.ts
3
+ */
4
+ /**
5
+ * `extractFileNameFromPath`
6
+ * essentially a wrapper for path.basename() for short-hand convenience
7
+ * @param filePath `string` e.g. pass in the node module variable `__filename`
8
+ * @param removeExtension `boolean` `optional, default = true` - flag indicating
9
+ * whether or not to remove the file extension from the fileName
10
+ * @returns **`fileName`** `string`
11
+ */
12
+ export declare function extractFileName(filePath: string, removeExtension?: boolean): string;
13
+ /**
14
+ * = `= /^[^/\\:*?"<>|]+(\.[^/\\:*?"<>|]+)$/`
15
+ */
16
+ export declare const FILE_NAME_WITH_EXTENSION_PATTERN: RegExp;
17
+ /**
18
+ * `re` = `/^\s*(\d{4}-\d{2}-\d{2}|\d{1,2}[\/-]\d{1,2}[\/-]\d{4})\s*$/`
19
+ * 1. matches `YYYY-MM-DD` (ISO) format.
20
+ * 2. matches `MM/DD/YYYY` format. assumes `MM/DD/YYYY` format if the first part is less than or equal to 12 I think.
21
+ */
22
+ export declare const DATE_STRING_PATTERN: RegExp;
23
+ /** e.g. `"Pangyo-ro, Bundag-Gu, Seongnam-si"` */
24
+ export declare const KOREA_ADDRESS_LATIN_TEXT_PATTERN: RegExp;
25
+ /**
26
+ * @consideration `removeClasses` is unnecessary, can assume true if a value for
27
+ * classDelimiter is provided -> have to remove default value for classDelimiter
28
+ * @param value `string` - the string value from which to extract `leaf`
29
+ * @param removeClasses `boolean` - remove string prefixes from result `leaf`
30
+ * - `Default` = `true`
31
+ * - e.g. `'CLASSA:SKU-1'` -> `sku` with classes removed = `'SKU-1'`
32
+ * @param classDelimiter `string` - the character used to delimit the item's classes
33
+ * - `Default` = `':'` (colon character)
34
+ * - e.g. `classDelimiter` of `'CLASSA:SKU-1'` = `':'`
35
+ * @returns **`leaf`**: `string` - the extracted `leaf` or the original value if no extraction performed
36
+ */
37
+ export declare function extractLeaf(value: string, removeClasses?: boolean, classDelimiter?: string): string;
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ /**
3
+ * @file src/utils/regex/misc.ts
4
+ */
5
+ var __importDefault = (this && this.__importDefault) || function (mod) {
6
+ return (mod && mod.__esModule) ? mod : { "default": mod };
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.KOREA_ADDRESS_LATIN_TEXT_PATTERN = exports.DATE_STRING_PATTERN = exports.FILE_NAME_WITH_EXTENSION_PATTERN = void 0;
10
+ exports.extractFileName = extractFileName;
11
+ exports.extractLeaf = extractLeaf;
12
+ const typeValidation_1 = require("../typeValidation");
13
+ const node_path_1 = __importDefault(require("node:path"));
14
+ /**
15
+ * `extractFileNameFromPath`
16
+ * essentially a wrapper for path.basename() for short-hand convenience
17
+ * @param filePath `string` e.g. pass in the node module variable `__filename`
18
+ * @param removeExtension `boolean` `optional, default = true` - flag indicating
19
+ * whether or not to remove the file extension from the fileName
20
+ * @returns **`fileName`** `string`
21
+ */
22
+ function extractFileName(filePath, removeExtension = true) {
23
+ if (!(0, typeValidation_1.isNonEmptyString)(filePath)) {
24
+ return 'undefined';
25
+ }
26
+ let fileName = node_path_1.default.basename(filePath);
27
+ if (removeExtension) {
28
+ fileName = fileName.replace(/(?<=.+)\.[a-z0-9]{1,}$/i, '');
29
+ }
30
+ return fileName;
31
+ }
32
+ /**
33
+ * = `= /^[^/\\:*?"<>|]+(\.[^/\\:*?"<>|]+)$/`
34
+ */
35
+ exports.FILE_NAME_WITH_EXTENSION_PATTERN = /^[^/\\:*?"<>|]+(\.[^/\\:*?"<>|]+)$/;
36
+ /**
37
+ * `re` = `/^\s*(\d{4}-\d{2}-\d{2}|\d{1,2}[\/-]\d{1,2}[\/-]\d{4})\s*$/`
38
+ * 1. matches `YYYY-MM-DD` (ISO) format.
39
+ * 2. matches `MM/DD/YYYY` format. assumes `MM/DD/YYYY` format if the first part is less than or equal to 12 I think.
40
+ */
41
+ exports.DATE_STRING_PATTERN = new RegExp(/^\s*(\d{4}-\d{2}-\d{2}|\d{1,2}[\/-]\d{1,2}[\/-]\d{4})\s*$/);
42
+ /** e.g. `"Pangyo-ro, Bundag-Gu, Seongnam-si"` */
43
+ exports.KOREA_ADDRESS_LATIN_TEXT_PATTERN = new RegExp(/^\s*([a-zA-Z]{2,}-[a-zA-Z]{2,},\s*){1,}[a-zA-Z]{2,}-[a-zA-Z]{2,}\s*$/);
44
+ /**
45
+ * @consideration `removeClasses` is unnecessary, can assume true if a value for
46
+ * classDelimiter is provided -> have to remove default value for classDelimiter
47
+ * @param value `string` - the string value from which to extract `leaf`
48
+ * @param removeClasses `boolean` - remove string prefixes from result `leaf`
49
+ * - `Default` = `true`
50
+ * - e.g. `'CLASSA:SKU-1'` -> `sku` with classes removed = `'SKU-1'`
51
+ * @param classDelimiter `string` - the character used to delimit the item's classes
52
+ * - `Default` = `':'` (colon character)
53
+ * - e.g. `classDelimiter` of `'CLASSA:SKU-1'` = `':'`
54
+ * @returns **`leaf`**: `string` - the extracted `leaf` or the original value if no extraction performed
55
+ */
56
+ function extractLeaf(value, removeClasses = true, classDelimiter = ':') {
57
+ let result = value;
58
+ if (value.includes(' (')) { // remove description enclosed in parentheses
59
+ result = value.split(' (')[0];
60
+ // const match = skuValue.match(skuPattern);
61
+ // if (isNonEmptyArray(match)) {
62
+ // return match[0].trim();
63
+ // }
64
+ }
65
+ if (removeClasses && result.includes(classDelimiter)) {
66
+ let classifierSplit = result.split(classDelimiter);
67
+ result = classifierSplit[classifierSplit.length - 1];
68
+ }
69
+ // if (value.includes(':')) {mlog.debug([`[regex.misc.extractLeaf()]`,
70
+ // ` initial value: '${value}'`,
71
+ // `after removeClasses: '${result}'`,
72
+ // ].join(TAB));
73
+ // STOP_RUNNING(0)}
74
+ return result || value;
75
+ }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * @param phone - `string` - phone number to test
3
+ * @returns **`phone`** - formatted phone number or empty string if unable to format it
4
+ * @description test phone on regex in this order:
5
+ * 1. {@link KOREA_PHONE_REGEX} = `/(?:^|\D)(82)[-).\s]?(\d{1,2})?[-.\s]?(\d{3,4})[-.\s]?(\d{4})(?:\D|$)/`
6
+ * 2. {@link HONG_KONG_PHONE_REGEX} = `/(?:^|\D)(852)[-.\s]?(\d{4})[-.\s]?(\d{4})(?:\D|$)/`
7
+ * 3. {@link CHINA_PHONE_REGEX} = `/(?:^|\D)(86)[-.\s]?(\d{2,3})[-.\s]?(\d{4})[-.\s]?(\d{4})(?:\D|$)`
8
+ * 4. {@link JAPAN_PHONE_REGEX} = `/(?:^|\D)(81)[-.\s]?(\d{1})[-.\s]?(\d{4})[-.\s]?(\d{4})(?:\D|$)/`
9
+ * 5. GENERIC_{@link PHONE_REGEX} = `/(?:^|\D)(\d{1,3}[-.\s]?)?\(?(\d{3})\)?[-.\s]?(\d{3})[-.\s]?(\d{4})[-.\s]?(?:ext|x|ex)?(?:[-:.\s]*)?(\d{1,4})?(?:\D|$)/i`
10
+ * 6. `else` return emtpy string
11
+ * @note Valid formats for NetSuite Phone Number fields are:
12
+ * 1. `999-999-9999`
13
+ * 2. `1-999-999-9999`
14
+ * 3. `(999) 999-9999`
15
+ * 4. `1(999) 999-9999`
16
+ * 5. `999-999-9999 ext 9999`
17
+ */
18
+ export declare function extractPhone(phone: string, phoneRegexList?: {
19
+ re: RegExp;
20
+ groupFormat: string;
21
+ }[]): string[] | RegExpMatchArray | null;
22
+ /**
23
+ * @param phone `string` - the phone number to format
24
+ * @param re {@link RegExp} - the regex to use to extract the phone number
25
+ * @param groupFormat `string` - use to format the phone number
26
+ * - `optional` - if not provided, the phone number is returned as is
27
+ * @returns **`phone`** = `string` - the formatted phone number
28
+ */
29
+ export declare function formatPhone(phone: string, re: RegExp, groupFormat?: string): string;
30
+ /**
31
+ * @description
32
+ * `re` = `/(?:^|\D)(\d{1,3}[-.\s]?)?\(?(\d{3})\)?[-.\s]?(\d{3})[-.\s]?(\d{4})[-.\s]?(?:ext|x|ex)?(?:[-:.\s]*)?(\d{1,4})?(?:\D|$)/i`
33
+ * - There are 5 capturing groups in the regex:
34
+ * - **`$1`** - Country code (optional) - `(\d{1,3})`
35
+ * - **`$2`** - Area code - `(\d{3})`
36
+ * - **`$3`** - First three digits - `(\d{3})`
37
+ * - **`$4`** - Last four digits - `(\d{4})`
38
+ * - **`$5`** - Extension (optional) - `( ?ext ?(\d{3,4}))?`
39
+ * */
40
+ export declare const PHONE_REGEX: RegExp;
41
+ /**
42
+ * @description
43
+ * `re` = `/(?:^|\D)(852)[-.\s]?(\d{4})[-.\s]?(\d{4})(?:\D|$)/`
44
+ * - There are 3 capturing groups in the regex:
45
+ * - **`$1`** - Country code - `(852)`
46
+ * - **`$2`** - First four digits - `(\d{4})`
47
+ * - **`$3`** - Last four digits - `(\d{4})`
48
+ */
49
+ export declare const HONG_KONG_PHONE_REGEX: RegExp;
50
+ /**
51
+ * @description
52
+ * `re` = `/(?:^|\D)(86)[-.\s]?(\d{2,3})[-.\s]?(\d{4})[-.\s]?(\d{4})(?:\D|$)/`
53
+ * - There are 4 capturing groups in the regex:
54
+ * - **`$1`** - Country code - `(86)`
55
+ * - **`$2`** - Area code - `(\d{2,3})`
56
+ * - **`$3`** - First four digits - `(\d{4})`
57
+ * - **`$4`** - Last four digits - `(\d{4})`
58
+ */
59
+ export declare const CHINA_PHONE_REGEX: RegExp;
60
+ /**
61
+ * @description
62
+ * `re` = `/(?:^|\D)(81)[-.\s]?(\d{1})[-.\s]?(\d{4})[-.\s]?(\d{4})(?:\D|$)/`
63
+ * - There are 4 capturing groups in the regex:
64
+ * - **`$1`** - Country code - `(81)`
65
+ * - **`$2`** - Area code - `(\d{1})`
66
+ * - **`$3`** - First four digits - `(\d{4})`
67
+ * - **`$4`** - Last four digits - `(\d{4})`
68
+ */
69
+ export declare const JAPAN_PHONE_REGEX: RegExp;
70
+ /**
71
+ * @description
72
+ * `re` = `/(?:^|\D)(82)[-).\s]?(\d{1,2})?[-.\s]?(\d{3,4})[-.\s]?(\d{4})(?:\D|$)/`
73
+ * - There are 4 capturing groups in the regex:
74
+ * - **`$1`** - Country code - `(82)`
75
+ * - **`$2`** - Area code - `(\d{2})`
76
+ * - **`$3`** - First three digits - `(\d{3})`
77
+ * - **`$4`** - Last four digits - `(\d{4})`
78
+ */
79
+ export declare const KOREA_PHONE_REGEX: RegExp;
80
+ export declare const PHONE_REGEX_LIST: {
81
+ re: RegExp;
82
+ groupFormat: string;
83
+ }[];
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PHONE_REGEX_LIST = exports.KOREA_PHONE_REGEX = exports.JAPAN_PHONE_REGEX = exports.CHINA_PHONE_REGEX = exports.HONG_KONG_PHONE_REGEX = exports.PHONE_REGEX = void 0;
4
+ exports.extractPhone = extractPhone;
5
+ exports.formatPhone = formatPhone;
6
+ const cleaning_1 = require("./cleaning");
7
+ const StringOptions_1 = require("./types/StringOptions");
8
+ /**
9
+ * @param phone - `string` - phone number to test
10
+ * @returns **`phone`** - formatted phone number or empty string if unable to format it
11
+ * @description test phone on regex in this order:
12
+ * 1. {@link KOREA_PHONE_REGEX} = `/(?:^|\D)(82)[-).\s]?(\d{1,2})?[-.\s]?(\d{3,4})[-.\s]?(\d{4})(?:\D|$)/`
13
+ * 2. {@link HONG_KONG_PHONE_REGEX} = `/(?:^|\D)(852)[-.\s]?(\d{4})[-.\s]?(\d{4})(?:\D|$)/`
14
+ * 3. {@link CHINA_PHONE_REGEX} = `/(?:^|\D)(86)[-.\s]?(\d{2,3})[-.\s]?(\d{4})[-.\s]?(\d{4})(?:\D|$)`
15
+ * 4. {@link JAPAN_PHONE_REGEX} = `/(?:^|\D)(81)[-.\s]?(\d{1})[-.\s]?(\d{4})[-.\s]?(\d{4})(?:\D|$)/`
16
+ * 5. GENERIC_{@link PHONE_REGEX} = `/(?:^|\D)(\d{1,3}[-.\s]?)?\(?(\d{3})\)?[-.\s]?(\d{3})[-.\s]?(\d{4})[-.\s]?(?:ext|x|ex)?(?:[-:.\s]*)?(\d{1,4})?(?:\D|$)/i`
17
+ * 6. `else` return emtpy string
18
+ * @note Valid formats for NetSuite Phone Number fields are:
19
+ * 1. `999-999-9999`
20
+ * 2. `1-999-999-9999`
21
+ * 3. `(999) 999-9999`
22
+ * 4. `1(999) 999-9999`
23
+ * 5. `999-999-9999 ext 9999`
24
+ */
25
+ function extractPhone(phone, phoneRegexList = exports.PHONE_REGEX_LIST) {
26
+ if (!phone) {
27
+ return null;
28
+ }
29
+ const originalPhone = String(phone);
30
+ // remove leading and trailing letters. remove commas, semicolons, colons, and slashes
31
+ phone = originalPhone.trim().replace(/^\s*[a-zA-Z]*|[a-zA-Z]\s*$|[,;:/]*/g, '');
32
+ for (let i = 0; i < phoneRegexList.length; i++) {
33
+ const { re, groupFormat } = phoneRegexList[i];
34
+ let matches = phone.match(re);
35
+ if (!matches) {
36
+ continue;
37
+ }
38
+ let formattedPhones = matches.map(p => formatPhone(p, re, groupFormat));
39
+ // DEBUG.push(
40
+ // NL+`extractPhone("${originalPhone}") - "${phone}" matched phoneRegexList[${i}]!`,
41
+ // TAB + ` matches: ${JSON.stringify(matches)}`,
42
+ // TAB + `formattedPhones: ${JSON.stringify(formattedPhones)}`,
43
+ // )
44
+ return formattedPhones;
45
+ }
46
+ if (phone) { // phone is non-empty and did not match any regex
47
+ // DEBUG.push(NL+`extractPhone() - no match found for "${phone}", returning null.`)
48
+ }
49
+ ;
50
+ return null;
51
+ }
52
+ /**
53
+ * @param phone `string` - the phone number to format
54
+ * @param re {@link RegExp} - the regex to use to extract the phone number
55
+ * @param groupFormat `string` - use to format the phone number
56
+ * - `optional` - if not provided, the phone number is returned as is
57
+ * @returns **`phone`** = `string` - the formatted phone number
58
+ */
59
+ function formatPhone(phone, re, groupFormat) {
60
+ if (!phone)
61
+ return '';
62
+ let result = '';
63
+ const match = phone.match(re);
64
+ if (!match) {
65
+ return '';
66
+ }
67
+ result = match[0];
68
+ if (groupFormat) {
69
+ result = result.replace(re, groupFormat);
70
+ }
71
+ return (0, cleaning_1.clean)(result, { char: '-', escape: false })
72
+ .replace(/([a-zA-Z]+\s*$)/, '').trim();
73
+ }
74
+ // https://en.wikipedia.org/wiki/List_of_telephone_country_codes
75
+ /**
76
+ * @description
77
+ * `re` = `/(?:^|\D)(\d{1,3}[-.\s]?)?\(?(\d{3})\)?[-.\s]?(\d{3})[-.\s]?(\d{4})[-.\s]?(?:ext|x|ex)?(?:[-:.\s]*)?(\d{1,4})?(?:\D|$)/i`
78
+ * - There are 5 capturing groups in the regex:
79
+ * - **`$1`** - Country code (optional) - `(\d{1,3})`
80
+ * - **`$2`** - Area code - `(\d{3})`
81
+ * - **`$3`** - First three digits - `(\d{3})`
82
+ * - **`$4`** - Last four digits - `(\d{4})`
83
+ * - **`$5`** - Extension (optional) - `( ?ext ?(\d{3,4}))?`
84
+ * */
85
+ exports.PHONE_REGEX = new RegExp(/(?:^|\D)(\d{1,3}[-.\s]?)?\(?(\d{3})\)?[-.\s]?(\d{3})[-.\s]?(\d{4})[-.\s]?(?:ext|x|ex)?(?:[-:.\s]*)?(\d{1,4})?(?:\D|$)/, StringOptions_1.RegExpFlagsEnum.IGNORE_CASE + StringOptions_1.RegExpFlagsEnum.GLOBAL);
86
+ // /(?:^\D*(\d{1,3})[-.\s]?)?\(?(\d{3})\)?[-.\s]?(\d{3})[-.\s]?(\d{4})[-.\s]?(?:ext|x|ex)?(?:[-:.\s]*)?(\d{1,4})?(?:\D*$)/i;
87
+ /**
88
+ * @description
89
+ * `re` = `/(?:^|\D)(852)[-.\s]?(\d{4})[-.\s]?(\d{4})(?:\D|$)/`
90
+ * - There are 3 capturing groups in the regex:
91
+ * - **`$1`** - Country code - `(852)`
92
+ * - **`$2`** - First four digits - `(\d{4})`
93
+ * - **`$3`** - Last four digits - `(\d{4})`
94
+ */
95
+ exports.HONG_KONG_PHONE_REGEX = new RegExp(/(?:^|\D)(852)[-.\s]?(\d{4})[-.\s]?(\d{4})(?:\D|$)/, StringOptions_1.RegExpFlagsEnum.GLOBAL);
96
+ /**
97
+ * @description
98
+ * `re` = `/(?:^|\D)(86)[-.\s]?(\d{2,3})[-.\s]?(\d{4})[-.\s]?(\d{4})(?:\D|$)/`
99
+ * - There are 4 capturing groups in the regex:
100
+ * - **`$1`** - Country code - `(86)`
101
+ * - **`$2`** - Area code - `(\d{2,3})`
102
+ * - **`$3`** - First four digits - `(\d{4})`
103
+ * - **`$4`** - Last four digits - `(\d{4})`
104
+ */
105
+ exports.CHINA_PHONE_REGEX = new RegExp(/(?:^|\D)(86)[-.\s]?(\d{2,3})[-.\s]?(\d{4})[-.\s]?(\d{4})(?:\D|$)/, StringOptions_1.RegExpFlagsEnum.GLOBAL);
106
+ /**
107
+ * @description
108
+ * `re` = `/(?:^|\D)(81)[-.\s]?(\d{1})[-.\s]?(\d{4})[-.\s]?(\d{4})(?:\D|$)/`
109
+ * - There are 4 capturing groups in the regex:
110
+ * - **`$1`** - Country code - `(81)`
111
+ * - **`$2`** - Area code - `(\d{1})`
112
+ * - **`$3`** - First four digits - `(\d{4})`
113
+ * - **`$4`** - Last four digits - `(\d{4})`
114
+ */
115
+ exports.JAPAN_PHONE_REGEX = new RegExp(/(?:^|\D)(81)[-.\s]?(\d{1})[-.\s]?(\d{4})[-.\s]?(\d{4})(?:\D|$)/, StringOptions_1.RegExpFlagsEnum.GLOBAL);
116
+ /**
117
+ * @description
118
+ * `re` = `/(?:^|\D)(82)[-).\s]?(\d{1,2})?[-.\s]?(\d{3,4})[-.\s]?(\d{4})(?:\D|$)/`
119
+ * - There are 4 capturing groups in the regex:
120
+ * - **`$1`** - Country code - `(82)`
121
+ * - **`$2`** - Area code - `(\d{2})`
122
+ * - **`$3`** - First three digits - `(\d{3})`
123
+ * - **`$4`** - Last four digits - `(\d{4})`
124
+ */
125
+ exports.KOREA_PHONE_REGEX = new RegExp(/(?:^|\D)(82)[-).\s]?(\d{1,2})?[-.\s]?(\d{3,4})[-.\s]?(\d{4})(?:\D|$)/, StringOptions_1.RegExpFlagsEnum.GLOBAL);
126
+ exports.PHONE_REGEX_LIST = [
127
+ { re: exports.CHINA_PHONE_REGEX, groupFormat: '$1-$2-$3-$4' },
128
+ { re: exports.HONG_KONG_PHONE_REGEX, groupFormat: '$1-$2-$3' },
129
+ { re: exports.KOREA_PHONE_REGEX, groupFormat: '$1-$2-$3-$4' },
130
+ { re: exports.JAPAN_PHONE_REGEX, groupFormat: '$1-$2-$3-$4' },
131
+ { re: exports.PHONE_REGEX, groupFormat: '$1-$2-$3-$4 ext $5' },
132
+ ];
@@ -0,0 +1,45 @@
1
+ import { RegExpFlagsEnum } from "./types/StringOptions";
2
+ /**
3
+ * Checks if a string ends with any of the specified suffixes.
4
+ * @param s The `string` to check.
5
+ * @param suffixes An array of possible ending strings.
6
+ * @param flags `Optional` regex flags to use when creating the {@link RegExp} object. see {@link RegExpFlagsEnum}
7
+ * @returns **`true`** if the string ends with any of the suffixes, **`false`** otherwise.
8
+ * @example
9
+ * const myString = "hello world";
10
+ * const possibleEndings = ["world", "universe", "planet"];
11
+ * console.log(endsWithAny(myString, possibleEndings)); // Output: true
12
+ * const anotherString = "goodbye moon";
13
+ * console.log(endsWithAny(anotherString, possibleEndings)); // Output: false
14
+ */
15
+ export declare function stringEndsWithAnyOf(s: string, suffixes: string | string[] | RegExp, ...flags: RegExpFlagsEnum[]): boolean;
16
+ /**
17
+ * @param str The `string` to check.
18
+ * @param prefixes possible starting string(s).
19
+ * @param flags `Optional` regex flags to use when creating the {@link RegExp} object. see {@link RegExpFlagsEnum}
20
+ * @returns **`true`** if the string starts with any of the prefixes, **`false`** otherwise.
21
+ */
22
+ export declare function stringStartsWithAnyOf(str: string, prefixes: string | string[] | RegExp, ...flags: RegExpFlagsEnum[]): boolean;
23
+ /**
24
+ * @param str The `string` to check.
25
+ * @param substrings possible substring(s).
26
+ * @param flags `Optional` regex flags to use when creating the {@link RegExp} object. see {@link RegExpFlagsEnum}
27
+ * @returns **`true`** if the string contains any of the substrings, **`false`** otherwise.
28
+ */
29
+ export declare function stringContainsAnyOf(str: string, substrings: string | string[] | RegExp, ...flags: RegExpFlagsEnum[]): boolean;
30
+ /**
31
+ * @consideration add parameter to ignore case. currently:
32
+ * - converts `s1` & `s2` to lowercase and removes all non-alphanumeric characters from both strings,
33
+ * - sorts the characters in both strings,
34
+ * - then compares the two strings for equivalence.
35
+ * @param s1 `string`
36
+ * @param s2 `string`
37
+ * @param tolerance `number` - a number between 0 and 1, default is `0.90`
38
+ * @returns **`boolean`**
39
+ * - **`true`** `if` the two alphanumeric strings are equivalent,
40
+ * - **`false`** `otherwise`.
41
+ */
42
+ export declare function equivalentAlphanumericStrings(s1: string, s2: string, tolerance?: number): boolean;
43
+ /** for simple regular expressions...
44
+ * so like not ones that have parentheses, pipes, or curly braced numbers */
45
+ export declare function extractSource(regex: RegExp): string;
@@ -0,0 +1,201 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.stringEndsWithAnyOf = stringEndsWithAnyOf;
4
+ exports.stringStartsWithAnyOf = stringStartsWithAnyOf;
5
+ exports.stringContainsAnyOf = stringContainsAnyOf;
6
+ exports.equivalentAlphanumericStrings = equivalentAlphanumericStrings;
7
+ exports.extractSource = extractSource;
8
+ /**
9
+ * @file src/utils/regex/stringOperations.ts
10
+ */
11
+ const config_1 = require("../../config");
12
+ const cleaning_1 = require("./cleaning");
13
+ const fastest_levenshtein_1 = require("fastest-levenshtein");
14
+ const typeValidation_1 = require("../typeValidation");
15
+ /**
16
+ * Checks if a string ends with any of the specified suffixes.
17
+ * @param s The `string` to check.
18
+ * @param suffixes An array of possible ending strings.
19
+ * @param flags `Optional` regex flags to use when creating the {@link RegExp} object. see {@link RegExpFlagsEnum}
20
+ * @returns **`true`** if the string ends with any of the suffixes, **`false`** otherwise.
21
+ * @example
22
+ * const myString = "hello world";
23
+ * const possibleEndings = ["world", "universe", "planet"];
24
+ * console.log(endsWithAny(myString, possibleEndings)); // Output: true
25
+ * const anotherString = "goodbye moon";
26
+ * console.log(endsWithAny(anotherString, possibleEndings)); // Output: false
27
+ */
28
+ function stringEndsWithAnyOf(s, suffixes, ...flags) {
29
+ if (!s || !suffixes) {
30
+ return false;
31
+ }
32
+ let regex = undefined;
33
+ if (typeof suffixes === 'string') {
34
+ suffixes = [suffixes];
35
+ }
36
+ let flagString = ((0, typeValidation_1.isNonEmptyArray)(flags)
37
+ ? flags.join('')
38
+ : suffixes instanceof RegExp && (0, typeValidation_1.isNonEmptyString)(suffixes.flags)
39
+ ? suffixes.flags
40
+ : undefined);
41
+ if ((0, typeValidation_1.isStringArray)(suffixes)) {
42
+ /** Escape special regex characters in suffixes and join them with '|' (OR) */
43
+ const escapedSuffixes = suffixes.map(s => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
44
+ const pattern = `(${escapedSuffixes.join('|')})\\s*$`;
45
+ regex = new RegExp(pattern, flagString);
46
+ }
47
+ else if (suffixes instanceof RegExp) {
48
+ regex = (suffixes.source.endsWith('$')
49
+ ? new RegExp(suffixes, flagString)
50
+ : new RegExp(suffixes.source + '\\s*$', flagString));
51
+ }
52
+ if (!regex) {
53
+ config_1.typeshiLogger.error('endsWithAnyOf() Invalid suffixes type. returning false.', 'Expected string, array of strings, or RegExp but received:', typeof suffixes, suffixes);
54
+ return false; // Invalid suffixes type
55
+ }
56
+ return regex.test(s);
57
+ }
58
+ /**
59
+ * @param str The `string` to check.
60
+ * @param prefixes possible starting string(s).
61
+ * @param flags `Optional` regex flags to use when creating the {@link RegExp} object. see {@link RegExpFlagsEnum}
62
+ * @returns **`true`** if the string starts with any of the prefixes, **`false`** otherwise.
63
+ */
64
+ function stringStartsWithAnyOf(str, prefixes, ...flags) {
65
+ if (!str || !prefixes) {
66
+ return false;
67
+ }
68
+ let regex = undefined;
69
+ if (typeof prefixes === 'string') {
70
+ prefixes = [prefixes];
71
+ }
72
+ let flagString = ((0, typeValidation_1.isNonEmptyArray)(flags)
73
+ ? flags.join('')
74
+ : prefixes instanceof RegExp && prefixes.flags
75
+ ? prefixes.flags
76
+ : undefined);
77
+ if (Array.isArray(prefixes)) {
78
+ /** Escape special regex characters in suffixes and join them with '|' (OR) */
79
+ const escapedPrefixes = prefixes.map(s => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
80
+ const pattern = `^\\s*(${escapedPrefixes.join('|')})`;
81
+ regex = new RegExp(pattern, flagString);
82
+ }
83
+ else if (prefixes instanceof RegExp) {
84
+ regex = (prefixes.source.startsWith('^')
85
+ ? new RegExp(prefixes, flagString)
86
+ : new RegExp('^\\s*' + prefixes.source, flagString));
87
+ }
88
+ if (!regex) {
89
+ config_1.typeshiLogger.warn('startsWithAnyOf() Invalid prefixes type. returning false.', config_1.INDENT_LOG_LINE + 'Expected string, array of strings, or RegExp, but received:', typeof prefixes, config_1.INDENT_LOG_LINE + 'prefixes', prefixes);
90
+ return false; // Invalid prefixes type
91
+ }
92
+ return regex.test(str);
93
+ }
94
+ /**
95
+ * @param str The `string` to check.
96
+ * @param substrings possible substring(s).
97
+ * @param flags `Optional` regex flags to use when creating the {@link RegExp} object. see {@link RegExpFlagsEnum}
98
+ * @returns **`true`** if the string contains any of the substrings, **`false`** otherwise.
99
+ */
100
+ function stringContainsAnyOf(str, substrings, ...flags) {
101
+ if (!str || !substrings) {
102
+ return false;
103
+ }
104
+ let regex = undefined;
105
+ if (typeof substrings === 'string') {
106
+ substrings = [substrings];
107
+ }
108
+ let flagString = ((0, typeValidation_1.isNonEmptyArray)(flags)
109
+ ? flags.join('')
110
+ : substrings instanceof RegExp && substrings.flags
111
+ ? substrings.flags
112
+ : undefined);
113
+ if (Array.isArray(substrings)) {
114
+ /** Escape special regex characters in suffixes and join them with '|' (OR) */
115
+ const escapedSubstrings = substrings.map(s => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
116
+ const pattern = `(${escapedSubstrings.join('|')})`;
117
+ regex = new RegExp(pattern, flagString);
118
+ }
119
+ else if (substrings instanceof RegExp) {
120
+ regex = new RegExp(substrings, flagString);
121
+ }
122
+ if (!regex) {
123
+ config_1.typeshiLogger.warn('containsAnyOf() Invalid substrings type. returning false.', config_1.INDENT_LOG_LINE + `Expected string, array of strings, or RegExp, but received: ${typeof substrings}, ${substrings}`);
124
+ return false; // Invalid substrings type
125
+ }
126
+ return regex.test(str);
127
+ }
128
+ /**
129
+ * @consideration add parameter to ignore case. currently:
130
+ * - converts `s1` & `s2` to lowercase and removes all non-alphanumeric characters from both strings,
131
+ * - sorts the characters in both strings,
132
+ * - then compares the two strings for equivalence.
133
+ * @param s1 `string`
134
+ * @param s2 `string`
135
+ * @param tolerance `number` - a number between 0 and 1, default is `0.90`
136
+ * @returns **`boolean`**
137
+ * - **`true`** `if` the two alphanumeric strings are equivalent,
138
+ * - **`false`** `otherwise`.
139
+ */
140
+ function equivalentAlphanumericStrings(s1, s2, tolerance = 0.90) {
141
+ if (!s1 || !s2)
142
+ return false;
143
+ const cleanOptions = {
144
+ case: { toLower: true },
145
+ replace: [
146
+ { searchValue: /[^A-Za-z0-9]/g, replaceValue: '' }
147
+ ]
148
+ };
149
+ let s1Alphabetical = (0, cleaning_1.clean)(s1, cleanOptions).split('').sort().join('');
150
+ let s2Alphabetical = (0, cleaning_1.clean)(s2, cleanOptions).split('').sort().join('');
151
+ if (s1Alphabetical.length === 0 || s2Alphabetical.length === 0) {
152
+ return false;
153
+ }
154
+ if (s1Alphabetical === s2Alphabetical) { // exact match
155
+ return true;
156
+ }
157
+ const maxLevenshteinDistance = Math.max(Math.floor(s1Alphabetical.length * (1 - tolerance)), Math.floor(s2Alphabetical.length * (1 - tolerance)));
158
+ if ((0, fastest_levenshtein_1.distance)(s1, s2) <= maxLevenshteinDistance
159
+ || (0, fastest_levenshtein_1.distance)(s1Alphabetical, s2Alphabetical) <= maxLevenshteinDistance) {
160
+ return true;
161
+ }
162
+ const s1IncludesTolerableS2 = (s2.length > 0 && s2Alphabetical.length > 0
163
+ && s1Alphabetical.length >= s2Alphabetical.length
164
+ && s2Alphabetical.length / s1Alphabetical.length >= tolerance
165
+ && s1Alphabetical.includes(s2Alphabetical));
166
+ const s2IncludesTolerableS1 = (s1.length > 0 && s1Alphabetical.length > 0
167
+ && s2Alphabetical.length >= s1Alphabetical.length
168
+ && s1Alphabetical.length / s2Alphabetical.length >= tolerance
169
+ && s2Alphabetical.includes(s1Alphabetical));
170
+ if (s1IncludesTolerableS2 || s2IncludesTolerableS1) {
171
+ return true;
172
+ }
173
+ return false;
174
+ }
175
+ /** for simple regular expressions...
176
+ * so like not ones that have parentheses, pipes, or curly braced numbers */
177
+ function extractSource(regex) {
178
+ if (!regex)
179
+ return '';
180
+ const REMOVE_ENDPOINT_CHARS = {
181
+ searchValue: /(^(\^|\\b)|(\$|\\b)$)/g, replaceValue: ''
182
+ };
183
+ const REPLACE_ESCAPED_DOT = {
184
+ searchValue: /\\\./g, replaceValue: '.'
185
+ };
186
+ const REPLACE_ESCAPED_WHITESPACE = {
187
+ searchValue: /\\\s(\*|\+)?/g, replaceValue: ' '
188
+ };
189
+ const REMOVE_UNESCAPED_QUESTION_MARK = {
190
+ searchValue: /(?<!\\)\?/g, replaceValue: ''
191
+ };
192
+ let source = (0, cleaning_1.clean)(regex.source, {
193
+ replace: [
194
+ REMOVE_ENDPOINT_CHARS,
195
+ REPLACE_ESCAPED_DOT,
196
+ REPLACE_ESCAPED_WHITESPACE,
197
+ REMOVE_UNESCAPED_QUESTION_MARK
198
+ ]
199
+ });
200
+ return source.trim();
201
+ }
@@ -0,0 +1,87 @@
1
+ /**
2
+ * @file src/utils/regex/types/StringOptions.ts
3
+ */
4
+ /**
5
+ * @reference {@link https://javascript.info/regexp-introduction}
6
+ * @enum {string} **`RegExpFlagsEnum`**
7
+ * @property {string} IGNORE_CASE - `i` - case insensitive "the search is case-insensitive: no difference between `A` and `a`"
8
+ * @property {string} MULTI_LINE - `m` - multi-line "Multiline mode" see {@link https://javascript.info/regexp-multiline-mode}
9
+ * @property {string} GLOBAL - `g` - global search "With this flag the search looks for all matches, without it – only the first match is returned."
10
+ * @property {string} DOT_MATCHES_NEWLINE - `s` - dot matches newline "By default, a dot doesn’t match the newline character `n`."
11
+ * @property {string} UNICODE - `u` - unicode "Enables full Unicode support. The flag enables correct processing of surrogate pairs." see {@link https://javascript.info/regexp-unicode}
12
+ * @property {string} STICKY - `y` - sticky search "searching at the exact position in the text." see {@link https://javascript.info/regexp-sticky}
13
+ */
14
+ export declare enum RegExpFlagsEnum {
15
+ IGNORE_CASE = "i",
16
+ MULTI_LINE = "m",
17
+ GLOBAL = "g",
18
+ DOT_MATCHES_NEWLINE = "s",
19
+ UNICODE = "u",
20
+ STICKY = "y"
21
+ }
22
+ /**
23
+ * @typedefn **`CleanStringOptions`**
24
+ */
25
+ export type CleanStringOptions = {
26
+ strip?: StringStripOptions;
27
+ case?: StringCaseOptions;
28
+ pad?: StringPadOptions;
29
+ replace?: StringReplaceOptions;
30
+ };
31
+ /**
32
+ * @typedefn **`StringCaseOptions`**
33
+ * @property {boolean} [toUpper] - `true` if the string should be converted to upper case
34
+ * @property {boolean} [toLower] - `true` if the string should be converted to lower case
35
+ * @property {boolean} [toTitle] - `true` if the string should be converted to title case, see {@link toTitleCase}
36
+ */
37
+ export type StringCaseOptions = {
38
+ toUpper?: boolean;
39
+ toLower?: boolean;
40
+ toTitle?: boolean;
41
+ };
42
+ /**
43
+ * @typedefn **`StringPadOptions`**
44
+ * @property {number} padLength - the length of the string after padding
45
+ * @property {string} [padChar] - the character to use for padding, defaults to ' '
46
+ * @property {boolean} [padLeft] - `true` if the padding should be added to the left side of the string
47
+ * @property {boolean} [padRight] - `true` if the padding should be added to the right side of the string
48
+ */
49
+ export type StringPadOptions = {
50
+ padLength: number;
51
+ padChar?: string;
52
+ padLeft?: boolean;
53
+ padRight?: boolean;
54
+ };
55
+ /**
56
+ * @typedefn **`StringStripOptions`**
57
+ * @property {string} char - the character to strip from the string with {@link applyStripOptions}
58
+ * @property {boolean} [escape] - `true` if the character should be escaped
59
+ * @property {function} [stripLeftCondition] - a function that takes a string and returns `true` if the character should be stripped from the left side of the string
60
+ * @property {any[]} [leftArgs] - arguments to pass to the `stripLeftCondition` function
61
+ * - if `stripLeftCondition(s, leftArgs)` is `true` or `stripLeftCondition` is `undefined` (i.e. no conditions need to be met to strip left), the left side of `s` is stripped of `char`
62
+ * @property {function} [stripRightCondition] - a function that takes a string and returns `true` if the character should be stripped from the right side of the string
63
+ * @property {any[]} [rightArgs] - arguments to pass to the `stripRightCondition` function
64
+ * - if `stripRightCondition(s, rightArgs)` is `true` or `stripRightCondition` is `undefined` (i.e. no conditions need to be met to strip right), the right side of `s` is stripped of `char`
65
+ */
66
+ export type StringStripOptions = {
67
+ char: string;
68
+ escape?: boolean;
69
+ stripLeftCondition?: (s: string, ...args: any[]) => boolean;
70
+ leftArgs?: any[];
71
+ stripRightCondition?: (s: string, ...args: any[]) => boolean;
72
+ rightArgs?: any[];
73
+ };
74
+ /**
75
+ * @typedefn **`StringReplaceOptions`**
76
+ * @property {StringReplaceParams[]} replacements - an array of objects containing `searchValue` and `replaceValue` properties
77
+ */
78
+ export type StringReplaceOptions = StringReplaceParams[];
79
+ /**
80
+ * @typedefn **`StringReplaceParams`**
81
+ * @property {string | RegExp} searchValue - the string or regular expression to search for in the string
82
+ * @property {string} replaceValue - the string to replace the `searchValue` with
83
+ */
84
+ export type StringReplaceParams = {
85
+ searchValue: string | RegExp;
86
+ replaceValue: string;
87
+ };