@turndown/library 0.1.16 → 0.1.22
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/dist/helpers/date/index.d.ts +113 -0
- package/dist/helpers/date/index.js +171 -0
- package/dist/helpers/index.d.ts +3 -1
- package/dist/helpers/index.js +2 -1
- package/dist/helpers/object/index.d.ts +160 -75
- package/dist/helpers/object/index.js +355 -168
- package/dist/helpers/string/index.d.ts +227 -74
- package/dist/helpers/string/index.js +448 -114
- package/dist/types/api/index.d.ts +22 -11
- package/dist/types/api/index.js +9 -2
- package/dist/types/auth/index.d.ts +56 -18
- package/dist/types/auth/index.js +7 -0
- package/dist/types/auth/routes.d.ts +76 -47
- package/dist/types/auth/routes.js +0 -1
- package/dist/types/base/index.d.ts +200 -69
- package/dist/types/base/index.js +116 -0
- package/dist/types/base/paging.types.d.ts +11 -11
- package/dist/types/checklist-template/index.d.ts +8 -8
- package/dist/types/checklist-template/routes.d.ts +57 -28
- package/dist/types/checklist-template/routes.js +0 -1
- package/dist/types/company/index.d.ts +5 -5
- package/dist/types/company/routes.d.ts +81 -33
- package/dist/types/company/routes.js +0 -1
- package/dist/types/damage-report/index.d.ts +9 -9
- package/dist/types/damage-report/routes.d.ts +128 -51
- package/dist/types/damage-report/routes.js +0 -1
- package/dist/types/errors/index.d.ts +15 -16
- package/dist/types/errors/index.js +17 -7
- package/dist/types/health/index.d.ts +20 -0
- package/dist/types/health/index.js +1 -0
- package/dist/types/health/routes.d.ts +10 -0
- package/dist/types/health/routes.js +1 -0
- package/dist/types/image/index.d.ts +34 -0
- package/dist/types/image/index.js +11 -0
- package/dist/types/image/routes.d.ts +32 -0
- package/dist/types/image/routes.js +1 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.js +3 -0
- package/dist/types/inventory/index.d.ts +13 -87
- package/dist/types/inventory/routes.d.ts +73 -36
- package/dist/types/inventory/routes.js +0 -1
- package/dist/types/job/index.d.ts +7 -0
- package/dist/types/job/index.js +1 -0
- package/dist/types/property/index.d.ts +88 -29
- package/dist/types/property/index.js +23 -23
- package/dist/types/property/routes.d.ts +40 -14
- package/dist/types/property/routes.js +0 -1
- package/dist/types/room/index.d.ts +4 -4
- package/dist/types/room/routes.d.ts +27 -8
- package/dist/types/room/routes.js +0 -1
- package/dist/types/room-checklist/index.d.ts +9 -9
- package/dist/types/room-checklist/routes.d.ts +50 -28
- package/dist/types/room-checklist/routes.js +0 -1
- package/dist/types/user/index.d.ts +12 -12
- package/dist/types/user/routes.d.ts +26 -16
- package/dist/types/user/routes.js +0 -1
- package/dist/types/work-session/index.d.ts +12 -12
- package/dist/types/work-session/routes.d.ts +55 -34
- package/dist/types/work-session/routes.js +0 -1
- package/package.json +14 -5
|
@@ -1,161 +1,495 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
* String utilities for common text manipulation tasks.
|
|
3
|
+
*/
|
|
4
|
+
const wordBoundaryRegex = /([a-z0-9])([A-Z])/g;
|
|
5
|
+
const acronymBoundaryRegex = /([A-Z]+)([A-Z][a-z])/g;
|
|
6
|
+
const getWords = (value) => {
|
|
7
|
+
return value
|
|
8
|
+
.trim()
|
|
9
|
+
.replace(acronymBoundaryRegex, "$1 $2")
|
|
10
|
+
.replace(wordBoundaryRegex, "$1 $2")
|
|
11
|
+
.replace(/[^a-zA-Z0-9]+/g, " ")
|
|
12
|
+
.trim()
|
|
13
|
+
.split(/\s+/)
|
|
14
|
+
.filter(Boolean);
|
|
15
|
+
};
|
|
16
|
+
const capitalizeWord = (value) => {
|
|
17
|
+
if (!value) {
|
|
18
|
+
return "";
|
|
19
|
+
}
|
|
20
|
+
return value.charAt(0).toUpperCase() + value.slice(1).toLowerCase();
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Adds commas to a number for thousands separators.
|
|
5
24
|
*
|
|
6
|
-
* @
|
|
7
|
-
* @
|
|
8
|
-
* @example
|
|
9
|
-
* normalCase("helloWorld") // "Hello World"
|
|
10
|
-
* normalCase("XMLHttpRequest") // "Xml Http Request"
|
|
25
|
+
* @example formatNumber(1000) => '1,000'
|
|
26
|
+
* @example formatNumber('1234567') => '1,234,567'
|
|
27
|
+
* @example formatNumber(1234567.89) => '1,234,567.89'
|
|
11
28
|
*/
|
|
29
|
+
export const formatNumber = (value) => {
|
|
30
|
+
const str = String(value);
|
|
31
|
+
const [integer, decimal] = str.split(".");
|
|
32
|
+
const formattedInteger = integer.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
33
|
+
return decimal !== undefined
|
|
34
|
+
? `${formattedInteger}.${decimal}`
|
|
35
|
+
: formattedInteger;
|
|
36
|
+
};
|
|
12
37
|
export const normalCase = (str) => {
|
|
13
|
-
if (!str)
|
|
38
|
+
if (!str) {
|
|
14
39
|
return "";
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
40
|
+
}
|
|
41
|
+
return getWords(str).map(capitalizeWord).join(" ");
|
|
42
|
+
};
|
|
43
|
+
export const sentenceCase = (str) => {
|
|
44
|
+
if (!str) {
|
|
45
|
+
return "";
|
|
46
|
+
}
|
|
47
|
+
const normalizedValue = str.trim().toLowerCase();
|
|
48
|
+
return normalizedValue.charAt(0).toUpperCase() + normalizedValue.slice(1);
|
|
49
|
+
};
|
|
50
|
+
export const upperCase = (str) => {
|
|
51
|
+
return str?.toUpperCase() ?? "";
|
|
52
|
+
};
|
|
53
|
+
export const lowerCase = (str) => {
|
|
54
|
+
return str?.toLowerCase() ?? "";
|
|
21
55
|
};
|
|
22
56
|
/**
|
|
23
|
-
*
|
|
24
|
-
* Trims leading/trailing spaces before processing.
|
|
57
|
+
* Converts a string to camelCase.
|
|
25
58
|
*
|
|
26
|
-
* @
|
|
27
|
-
* @
|
|
28
|
-
* @example
|
|
29
|
-
* sentenceCase("hELLO WORLD") // "Hello world"
|
|
59
|
+
* @example toCamelCase('hello-world') => 'helloWorld'
|
|
60
|
+
* @example toCamelCase('hello_world') => 'helloWorld'
|
|
30
61
|
*/
|
|
31
|
-
export const
|
|
32
|
-
|
|
62
|
+
export const toCamelCase = (str) => {
|
|
63
|
+
const words = getWords(str);
|
|
64
|
+
if (words.length === 0) {
|
|
33
65
|
return "";
|
|
34
|
-
|
|
35
|
-
|
|
66
|
+
}
|
|
67
|
+
const [firstWord, ...remainingWords] = words;
|
|
68
|
+
return [
|
|
69
|
+
firstWord.toLowerCase(),
|
|
70
|
+
...remainingWords.map(capitalizeWord),
|
|
71
|
+
].join("");
|
|
36
72
|
};
|
|
73
|
+
export const camelCase = toCamelCase;
|
|
37
74
|
/**
|
|
38
|
-
*
|
|
75
|
+
* Converts a string to kebab-case.
|
|
39
76
|
*
|
|
40
|
-
* @
|
|
41
|
-
* @
|
|
42
|
-
* @example
|
|
43
|
-
* upperCase("Hello world") // "HELLO WORLD"
|
|
77
|
+
* @example toKebabCase('helloWorld') => 'hello-world'
|
|
78
|
+
* @example toKebabCase('Hello_World') => 'hello-world'
|
|
44
79
|
*/
|
|
45
|
-
export const
|
|
46
|
-
return str
|
|
80
|
+
export const toKebabCase = (str) => {
|
|
81
|
+
return getWords(str).map((word) => word.toLowerCase()).join("-");
|
|
47
82
|
};
|
|
83
|
+
export const kebabCase = toKebabCase;
|
|
48
84
|
/**
|
|
49
|
-
*
|
|
85
|
+
* Converts a string to snake_case.
|
|
50
86
|
*
|
|
51
|
-
* @
|
|
52
|
-
* @
|
|
53
|
-
* @example
|
|
54
|
-
* lowerCase("Hello WORLD") // "hello world"
|
|
87
|
+
* @example toSnakeCase('helloWorld') => 'hello_world'
|
|
88
|
+
* @example toSnakeCase('hello-world') => 'hello_world'
|
|
55
89
|
*/
|
|
56
|
-
export const
|
|
57
|
-
return str
|
|
90
|
+
export const toSnakeCase = (str) => {
|
|
91
|
+
return getWords(str).map((word) => word.toLowerCase()).join("_");
|
|
58
92
|
};
|
|
93
|
+
export const snakeCase = toSnakeCase;
|
|
59
94
|
/**
|
|
60
|
-
*
|
|
61
|
-
* Splits on spaces, underscores, and hyphens; lowercases the first word,
|
|
62
|
-
* TitleCases the rest, then joins with no separators.
|
|
95
|
+
* Converts a string to PascalCase.
|
|
63
96
|
*
|
|
64
|
-
* @
|
|
65
|
-
* @
|
|
66
|
-
* @example
|
|
67
|
-
* camelCase("Hello world") // "helloWorld"
|
|
68
|
-
* camelCase("hello_world-again") // "helloWorldAgain"
|
|
97
|
+
* @example toPascalCase('hello-world') => 'HelloWorld'
|
|
98
|
+
* @example toPascalCase('hello_world') => 'HelloWorld'
|
|
69
99
|
*/
|
|
70
|
-
export const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
.join("");
|
|
100
|
+
export const toPascalCase = (str) => {
|
|
101
|
+
return getWords(str).map(capitalizeWord).join("");
|
|
102
|
+
};
|
|
103
|
+
export const pascalCase = toPascalCase;
|
|
104
|
+
export const snakeCaseToSpaces = (str) => {
|
|
105
|
+
return str.replace(/_+/g, " ").trim();
|
|
106
|
+
};
|
|
107
|
+
export const kebabToSpaces = (str) => {
|
|
108
|
+
return str.replace(/-+/g, " ").trim();
|
|
80
109
|
};
|
|
81
110
|
/**
|
|
82
|
-
*
|
|
83
|
-
* Splits on spaces, underscores, and hyphens; TitleCases all words and joins them.
|
|
111
|
+
* Capitalizes the first character of a string.
|
|
84
112
|
*
|
|
85
|
-
* @
|
|
86
|
-
* @returns {string}
|
|
87
|
-
* @example
|
|
88
|
-
* pascalCase("hello world") // "HelloWorld"
|
|
89
|
-
* pascalCase("hello_world-again") // "HelloWorldAgain"
|
|
113
|
+
* @example capitalize('hello world') => 'Hello world'
|
|
90
114
|
*/
|
|
91
|
-
export const
|
|
92
|
-
if (!str)
|
|
115
|
+
export const capitalize = (str) => {
|
|
116
|
+
if (!str) {
|
|
93
117
|
return "";
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
.split(/[\s_-]+/)
|
|
97
|
-
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
98
|
-
.join("");
|
|
118
|
+
}
|
|
119
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
99
120
|
};
|
|
100
121
|
/**
|
|
101
|
-
*
|
|
102
|
-
* Inserts hyphens between camelCase boundaries, then replaces spaces/underscores with hyphens.
|
|
122
|
+
* Capitalizes the first letter of each word.
|
|
103
123
|
*
|
|
104
|
-
* @
|
|
105
|
-
* @returns {string}
|
|
106
|
-
* @example
|
|
107
|
-
* kebabCase("HelloWorld Again") // "hello-world-again"
|
|
108
|
-
* kebabCase("hello_world") // "hello-world"
|
|
124
|
+
* @example titleCase('hello world') => 'Hello World'
|
|
109
125
|
*/
|
|
110
|
-
export const
|
|
111
|
-
|
|
126
|
+
export const titleCase = (str) => {
|
|
127
|
+
return str.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* Truncates a string to a specified length and adds ellipsis.
|
|
131
|
+
*
|
|
132
|
+
* @example truncate('hello world', 5) => 'he...'
|
|
133
|
+
*/
|
|
134
|
+
export const truncate = (str, length, suffix = "...") => {
|
|
135
|
+
if (length <= 0) {
|
|
112
136
|
return "";
|
|
137
|
+
}
|
|
138
|
+
if (str.length <= length) {
|
|
139
|
+
return str;
|
|
140
|
+
}
|
|
141
|
+
const maxLength = Math.max(0, length - suffix.length);
|
|
142
|
+
if (maxLength === 0) {
|
|
143
|
+
return suffix.length > length ? "" : suffix;
|
|
144
|
+
}
|
|
145
|
+
return str.slice(0, maxLength) + suffix;
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* Removes all whitespace from a string.
|
|
149
|
+
*
|
|
150
|
+
* @example removeWhitespace('hello world') => 'helloworld'
|
|
151
|
+
*/
|
|
152
|
+
export const removeWhitespace = (str) => {
|
|
153
|
+
return str.replace(/\s+/g, "");
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* Removes all non-alphanumeric characters.
|
|
157
|
+
*
|
|
158
|
+
* @example removeSpecialChars('hello@world#123') => 'helloworld123'
|
|
159
|
+
*/
|
|
160
|
+
export const removeSpecialChars = (str) => {
|
|
161
|
+
return str.replace(/[^a-zA-Z0-9]/g, "");
|
|
162
|
+
};
|
|
163
|
+
/**
|
|
164
|
+
* Generates a URL-friendly slug from a string.
|
|
165
|
+
*
|
|
166
|
+
* @example slug('Hello World 2024!') => 'hello-world-2024'
|
|
167
|
+
*/
|
|
168
|
+
export const slug = (str) => {
|
|
113
169
|
return str
|
|
114
|
-
.
|
|
170
|
+
.toLowerCase()
|
|
171
|
+
.trim()
|
|
172
|
+
.replace(/[^\w\s-]/g, "")
|
|
115
173
|
.replace(/[\s_]+/g, "-")
|
|
116
|
-
.
|
|
174
|
+
.replace(/-+/g, "-")
|
|
175
|
+
.replace(/^-+|-+$/g, "");
|
|
117
176
|
};
|
|
118
177
|
/**
|
|
119
|
-
*
|
|
120
|
-
* Inserts underscores between camelCase boundaries, then replaces spaces/hyphens with underscores.
|
|
178
|
+
* Validates if a string is a valid email.
|
|
121
179
|
*
|
|
122
|
-
* @
|
|
123
|
-
* @returns {string}
|
|
124
|
-
* @example
|
|
125
|
-
* snakeCase("HelloWorld Again") // "hello_world_again"
|
|
126
|
-
* snakeCase("hello-world") // "hello_world"
|
|
180
|
+
* @example isEmail('user@example.com') => true
|
|
127
181
|
*/
|
|
128
|
-
export const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
return str
|
|
132
|
-
.replace(/([a-z])([A-Z])/g, "$1_$2")
|
|
133
|
-
.replace(/[\s-]+/g, "_")
|
|
134
|
-
.toLowerCase();
|
|
182
|
+
export const isEmail = (str) => {
|
|
183
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
184
|
+
return emailRegex.test(str.trim());
|
|
135
185
|
};
|
|
136
186
|
/**
|
|
137
|
-
*
|
|
187
|
+
* Validates if a string is a valid URL.
|
|
138
188
|
*
|
|
139
|
-
* @
|
|
140
|
-
* @returns {string}
|
|
141
|
-
* @example
|
|
142
|
-
* snakeCaseToSpaces("hello_world_again") // "hello world again"
|
|
189
|
+
* @example isUrl('https://example.com') => true
|
|
143
190
|
*/
|
|
144
|
-
export const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
191
|
+
export const isUrl = (str) => {
|
|
192
|
+
try {
|
|
193
|
+
const parsedUrl = new URL(str);
|
|
194
|
+
return parsedUrl.protocol === "http:" || parsedUrl.protocol === "https:";
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
148
199
|
};
|
|
149
200
|
/**
|
|
150
|
-
*
|
|
201
|
+
* Validates if a string contains only numbers.
|
|
151
202
|
*
|
|
152
|
-
* @
|
|
153
|
-
* @
|
|
154
|
-
* @example
|
|
155
|
-
* kebabToSpaces("hello-world-again") // "hello world again"
|
|
203
|
+
* @example isNumeric('12345') => true
|
|
204
|
+
* @example isNumeric('123abc') => false
|
|
156
205
|
*/
|
|
157
|
-
export const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
206
|
+
export const isNumeric = (str) => {
|
|
207
|
+
return /^\d+$/.test(str);
|
|
208
|
+
};
|
|
209
|
+
/**
|
|
210
|
+
* Checks if a string is empty or contains only whitespace.
|
|
211
|
+
*
|
|
212
|
+
* @example isEmpty(' ') => true
|
|
213
|
+
* @example isEmpty('hello') => false
|
|
214
|
+
*/
|
|
215
|
+
export const isEmpty = (str) => {
|
|
216
|
+
return str.trim().length === 0;
|
|
217
|
+
};
|
|
218
|
+
/**
|
|
219
|
+
* Reverses a string.
|
|
220
|
+
*
|
|
221
|
+
* @example reverse('hello') => 'olleh'
|
|
222
|
+
*/
|
|
223
|
+
export const reverse = (str) => {
|
|
224
|
+
return Array.from(str).reverse().join("");
|
|
225
|
+
};
|
|
226
|
+
/**
|
|
227
|
+
* Repeats a string a specified number of times.
|
|
228
|
+
*
|
|
229
|
+
* @example repeat('ab', 3) => 'ababab'
|
|
230
|
+
*/
|
|
231
|
+
export const repeat = (str, times) => {
|
|
232
|
+
return str.repeat(Math.max(0, Math.floor(times)));
|
|
233
|
+
};
|
|
234
|
+
/**
|
|
235
|
+
* Pads a string to a specified length.
|
|
236
|
+
*
|
|
237
|
+
* @example padStart('5', 3, '0') => '005'
|
|
238
|
+
* @example padEnd('5', 3, '0') => '500'
|
|
239
|
+
*/
|
|
240
|
+
export const padStart = (str, length, padChar = " ") => {
|
|
241
|
+
return str.padStart(length, padChar);
|
|
242
|
+
};
|
|
243
|
+
export const padEnd = (str, length, padChar = " ") => {
|
|
244
|
+
return str.padEnd(length, padChar);
|
|
245
|
+
};
|
|
246
|
+
/**
|
|
247
|
+
* Encodes a string to Base64.
|
|
248
|
+
*
|
|
249
|
+
* @example toBase64('hello') => 'aGVsbG8='
|
|
250
|
+
*/
|
|
251
|
+
export const toBase64 = (str) => {
|
|
252
|
+
return Buffer.from(str, "utf-8").toString("base64");
|
|
253
|
+
};
|
|
254
|
+
/**
|
|
255
|
+
* Decodes a Base64 string.
|
|
256
|
+
*
|
|
257
|
+
* @example fromBase64('aGVsbG8=') => 'hello'
|
|
258
|
+
*/
|
|
259
|
+
export const fromBase64 = (str) => {
|
|
260
|
+
return Buffer.from(str, "base64").toString("utf-8");
|
|
261
|
+
};
|
|
262
|
+
/**
|
|
263
|
+
* Counts the number of words in a string.
|
|
264
|
+
*
|
|
265
|
+
* @example wordCount('hello world test') => 3
|
|
266
|
+
*/
|
|
267
|
+
export const wordCount = (str) => {
|
|
268
|
+
const trimmedValue = str.trim();
|
|
269
|
+
if (!trimmedValue) {
|
|
270
|
+
return 0;
|
|
271
|
+
}
|
|
272
|
+
return trimmedValue.split(/\s+/).length;
|
|
273
|
+
};
|
|
274
|
+
/**
|
|
275
|
+
* Counts the number of characters, excluding whitespace.
|
|
276
|
+
*
|
|
277
|
+
* @example charCount('hello world') => 10
|
|
278
|
+
*/
|
|
279
|
+
export const charCount = (str) => {
|
|
280
|
+
return str.replace(/\s/g, "").length;
|
|
281
|
+
};
|
|
282
|
+
/**
|
|
283
|
+
* Repeats a character a specified number of times.
|
|
284
|
+
*
|
|
285
|
+
* @example repeatChar('*', 5) => '*****'
|
|
286
|
+
*/
|
|
287
|
+
export const repeatChar = (char, times) => {
|
|
288
|
+
return char.repeat(Math.max(0, Math.floor(times)));
|
|
289
|
+
};
|
|
290
|
+
/**
|
|
291
|
+
* Extracts numbers from a string.
|
|
292
|
+
*
|
|
293
|
+
* @example extractNumbers('abc123def456') => '123456'
|
|
294
|
+
*/
|
|
295
|
+
export const extractNumbers = (str) => {
|
|
296
|
+
return str.replace(/\D/g, "");
|
|
297
|
+
};
|
|
298
|
+
/**
|
|
299
|
+
* Removes duplicate consecutive characters.
|
|
300
|
+
*
|
|
301
|
+
* @example removeDuplicates('aabbccdd') => 'abcd'
|
|
302
|
+
*/
|
|
303
|
+
export const removeDuplicates = (str) => {
|
|
304
|
+
return str.replace(/(.)\1+/g, "$1");
|
|
305
|
+
};
|
|
306
|
+
/**
|
|
307
|
+
* Checks if a string is a palindrome.
|
|
308
|
+
*
|
|
309
|
+
* @example isPalindrome('racecar') => true
|
|
310
|
+
* @example isPalindrome('hello') => false
|
|
311
|
+
*/
|
|
312
|
+
export const isPalindrome = (str) => {
|
|
313
|
+
const cleaned = str.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
314
|
+
return cleaned === cleaned.split("").reverse().join("");
|
|
315
|
+
};
|
|
316
|
+
/**
|
|
317
|
+
* Finds the longest word in a string.
|
|
318
|
+
*
|
|
319
|
+
* @example longestWord('the quick brown fox') => 'quick'
|
|
320
|
+
*/
|
|
321
|
+
export const longestWord = (str) => {
|
|
322
|
+
const words = str.trim().split(/\s+/).filter(Boolean);
|
|
323
|
+
return words.reduce((longest, word) => (word.length > longest.length ? word : longest), "");
|
|
324
|
+
};
|
|
325
|
+
/**
|
|
326
|
+
* Pluralizes common English words using a simple ruleset.
|
|
327
|
+
*
|
|
328
|
+
* @example pluralize('cat') => 'cats'
|
|
329
|
+
* @example pluralize('box') => 'boxes'
|
|
330
|
+
*/
|
|
331
|
+
export const pluralize = (word) => {
|
|
332
|
+
const exceptions = {
|
|
333
|
+
child: "children",
|
|
334
|
+
person: "people",
|
|
335
|
+
man: "men",
|
|
336
|
+
woman: "women",
|
|
337
|
+
tooth: "teeth",
|
|
338
|
+
foot: "feet",
|
|
339
|
+
mouse: "mice",
|
|
340
|
+
};
|
|
341
|
+
const lowercaseWord = word.toLowerCase();
|
|
342
|
+
const exception = exceptions[lowercaseWord];
|
|
343
|
+
if (exception) {
|
|
344
|
+
return word.charAt(0) === word.charAt(0).toUpperCase()
|
|
345
|
+
? capitalize(exception)
|
|
346
|
+
: exception;
|
|
347
|
+
}
|
|
348
|
+
if (/[^aeiou]y$/i.test(word)) {
|
|
349
|
+
return `${word.slice(0, -1)}ies`;
|
|
350
|
+
}
|
|
351
|
+
if (/(s|x|z|ch|sh)$/i.test(word)) {
|
|
352
|
+
return `${word}es`;
|
|
353
|
+
}
|
|
354
|
+
if (/fe$/i.test(word)) {
|
|
355
|
+
return `${word.slice(0, -2)}ves`;
|
|
356
|
+
}
|
|
357
|
+
if (/f$/i.test(word)) {
|
|
358
|
+
return `${word.slice(0, -1)}ves`;
|
|
359
|
+
}
|
|
360
|
+
return `${word}s`;
|
|
361
|
+
};
|
|
362
|
+
/**
|
|
363
|
+
* Highlights a substring within a string by wrapping it with markers.
|
|
364
|
+
*
|
|
365
|
+
* @example highlight('hello world', 'world', '**') => 'hello **world**'
|
|
366
|
+
*/
|
|
367
|
+
export const highlight = (str, substring, marker = "**") => {
|
|
368
|
+
if (!substring) {
|
|
369
|
+
return str;
|
|
370
|
+
}
|
|
371
|
+
return str.replace(new RegExp(`(${escapeRegex(substring)})`, "gi"), `${marker}$1${marker}`);
|
|
372
|
+
};
|
|
373
|
+
/**
|
|
374
|
+
* Converts a string to a regex-safe string.
|
|
375
|
+
*
|
|
376
|
+
* @example escapeRegex('a.b*c') => 'a\\.b\\*c'
|
|
377
|
+
*/
|
|
378
|
+
export const escapeRegex = (str) => {
|
|
379
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
380
|
+
};
|
|
381
|
+
/**
|
|
382
|
+
* Finds similarity between two strings using Levenshtein distance.
|
|
383
|
+
* Returns a value between 0 and 1, where 1 means identical.
|
|
384
|
+
*
|
|
385
|
+
* @example stringSimilarity('hello', 'hallo') => 0.8
|
|
386
|
+
*/
|
|
387
|
+
export const stringSimilarity = (str1, str2) => {
|
|
388
|
+
const s1 = str1.toLowerCase();
|
|
389
|
+
const s2 = str2.toLowerCase();
|
|
390
|
+
const longer = s1.length > s2.length ? s1 : s2;
|
|
391
|
+
const shorter = s1.length > s2.length ? s2 : s1;
|
|
392
|
+
if (longer.length === 0) {
|
|
393
|
+
return 1;
|
|
394
|
+
}
|
|
395
|
+
const editDistance = getEditDistance(longer, shorter);
|
|
396
|
+
return (longer.length - editDistance) / longer.length;
|
|
397
|
+
};
|
|
398
|
+
/**
|
|
399
|
+
* Helper function to calculate edit distance (Levenshtein distance).
|
|
400
|
+
*/
|
|
401
|
+
const getEditDistance = (s1, s2) => {
|
|
402
|
+
const costs = [];
|
|
403
|
+
for (let i = 0; i <= s1.length; i += 1) {
|
|
404
|
+
let lastValue = i;
|
|
405
|
+
for (let j = 0; j <= s2.length; j += 1) {
|
|
406
|
+
if (i === 0) {
|
|
407
|
+
costs[j] = j;
|
|
408
|
+
}
|
|
409
|
+
else if (j > 0) {
|
|
410
|
+
let newValue = costs[j - 1];
|
|
411
|
+
if (s1.charAt(i - 1) !== s2.charAt(j - 1)) {
|
|
412
|
+
newValue = Math.min(Math.min(newValue, lastValue), costs[j]) + 1;
|
|
413
|
+
}
|
|
414
|
+
costs[j - 1] = lastValue;
|
|
415
|
+
lastValue = newValue;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
if (i > 0) {
|
|
419
|
+
costs[s2.length] = lastValue;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return costs[s2.length];
|
|
423
|
+
};
|
|
424
|
+
/**
|
|
425
|
+
* Strips HTML tags from a string.
|
|
426
|
+
*
|
|
427
|
+
* @example stripHtml('<p>Hello <b>world</b></p>') => 'Hello world'
|
|
428
|
+
*/
|
|
429
|
+
export const stripHtml = (str) => {
|
|
430
|
+
return str.replace(/<[^>]*>/g, "");
|
|
431
|
+
};
|
|
432
|
+
/**
|
|
433
|
+
* Replaces multiple spaces with a single space.
|
|
434
|
+
*
|
|
435
|
+
* @example normalizeSpaces('hello world') => 'hello world'
|
|
436
|
+
*/
|
|
437
|
+
export const normalizeSpaces = (str) => {
|
|
438
|
+
return str.replace(/\s+/g, " ").trim();
|
|
439
|
+
};
|
|
440
|
+
/**
|
|
441
|
+
* Converts a string to a number, returning null if not valid.
|
|
442
|
+
*
|
|
443
|
+
* @example toNumber('123') => 123
|
|
444
|
+
* @example toNumber('abc') => null
|
|
445
|
+
*/
|
|
446
|
+
export const toNumber = (str) => {
|
|
447
|
+
if (str.trim() === "") {
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
const num = Number(str);
|
|
451
|
+
return Number.isNaN(num) ? null : num;
|
|
452
|
+
};
|
|
453
|
+
/**
|
|
454
|
+
* Splits a string by multiple delimiters.
|
|
455
|
+
*
|
|
456
|
+
* @example splitMultiple('a,b;c:d', ',', ';', ':') => ['a', 'b', 'c', 'd']
|
|
457
|
+
*/
|
|
458
|
+
export const splitMultiple = (str, ...delimiters) => {
|
|
459
|
+
if (delimiters.length === 0) {
|
|
460
|
+
return [str].filter((part) => part.length > 0);
|
|
461
|
+
}
|
|
462
|
+
let result = [str];
|
|
463
|
+
delimiters.forEach((delimiter) => {
|
|
464
|
+
if (delimiter !== "") {
|
|
465
|
+
result = result.flatMap((part) => part.split(delimiter));
|
|
466
|
+
}
|
|
467
|
+
});
|
|
468
|
+
return result.filter((part) => part.length > 0);
|
|
469
|
+
};
|
|
470
|
+
/**
|
|
471
|
+
* Checks if a string contains any of the provided substrings.
|
|
472
|
+
*
|
|
473
|
+
* @example containsAny('hello world', 'foo', 'world') => true
|
|
474
|
+
*/
|
|
475
|
+
export const containsAny = (str, ...substrings) => {
|
|
476
|
+
return substrings.some((substring) => str.includes(substring));
|
|
477
|
+
};
|
|
478
|
+
/**
|
|
479
|
+
* Checks if a string contains all of the provided substrings.
|
|
480
|
+
*
|
|
481
|
+
* @example containsAll('hello world', 'hello', 'world') => true
|
|
482
|
+
*/
|
|
483
|
+
export const containsAll = (str, ...substrings) => {
|
|
484
|
+
return substrings.every((substring) => str.includes(substring));
|
|
485
|
+
};
|
|
486
|
+
export const formatAddress = (address) => {
|
|
487
|
+
const parts = [
|
|
488
|
+
address.addressLine1,
|
|
489
|
+
address.addressLine2,
|
|
490
|
+
address.city,
|
|
491
|
+
address.stateCode,
|
|
492
|
+
address.postalCode,
|
|
493
|
+
].filter(Boolean);
|
|
494
|
+
return parts.join(", ");
|
|
161
495
|
};
|
|
@@ -3,28 +3,39 @@
|
|
|
3
3
|
* Standard response structure for all API endpoints
|
|
4
4
|
*/
|
|
5
5
|
import { TurndownObject } from "../base";
|
|
6
|
-
export
|
|
6
|
+
export declare const HTTP_METHOD: {
|
|
7
|
+
readonly Get: "GET";
|
|
8
|
+
readonly Post: "POST";
|
|
9
|
+
readonly Put: "PUT";
|
|
10
|
+
readonly Patch: "PATCH";
|
|
11
|
+
readonly Delete: "DELETE";
|
|
12
|
+
};
|
|
13
|
+
export type THttpMethod = (typeof HTTP_METHOD)[keyof typeof HTTP_METHOD];
|
|
14
|
+
export interface IApiError<TDetails extends object = TurndownObject> {
|
|
7
15
|
message: string;
|
|
8
16
|
code?: string;
|
|
9
|
-
details?:
|
|
17
|
+
details?: TDetails;
|
|
10
18
|
}
|
|
11
|
-
export interface
|
|
19
|
+
export interface IApiMeta {
|
|
12
20
|
timestamp: string;
|
|
13
21
|
version: string;
|
|
14
22
|
environment: string;
|
|
15
23
|
requestId?: string;
|
|
16
24
|
}
|
|
17
|
-
export interface
|
|
25
|
+
export interface IApiResponse<TData = TurndownObject> {
|
|
18
26
|
success: boolean;
|
|
19
|
-
data?:
|
|
20
|
-
error?:
|
|
21
|
-
meta?:
|
|
27
|
+
data?: TData | null;
|
|
28
|
+
error?: IApiError | null;
|
|
29
|
+
meta?: IApiMeta;
|
|
22
30
|
}
|
|
31
|
+
export type TApiResponse<T> = {
|
|
32
|
+
data: T;
|
|
33
|
+
};
|
|
23
34
|
/**
|
|
24
35
|
* Error Codes
|
|
25
36
|
* Standardized error codes used across the platform
|
|
26
37
|
*/
|
|
27
|
-
export declare const
|
|
38
|
+
export declare const ERROR_CODES: {
|
|
28
39
|
readonly BAD_REQUEST: "BAD_REQUEST";
|
|
29
40
|
readonly VALIDATION_ERROR: "VALIDATION_ERROR";
|
|
30
41
|
readonly MISSING_FIELDS: "MISSING_FIELDS";
|
|
@@ -39,12 +50,12 @@ export declare const ErrorCodes: {
|
|
|
39
50
|
readonly INTERNAL_ERROR: "INTERNAL_ERROR";
|
|
40
51
|
readonly DATABASE_ERROR: "DATABASE_ERROR";
|
|
41
52
|
};
|
|
42
|
-
export type
|
|
53
|
+
export type TErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES];
|
|
43
54
|
/**
|
|
44
55
|
* HTTP Status Codes
|
|
45
56
|
* Common status codes used in responses
|
|
46
57
|
*/
|
|
47
|
-
export declare const
|
|
58
|
+
export declare const HTTP_STATUS: {
|
|
48
59
|
readonly OK: 200;
|
|
49
60
|
readonly CREATED: 201;
|
|
50
61
|
readonly NO_CONTENT: 204;
|
|
@@ -56,4 +67,4 @@ export declare const HttpStatus: {
|
|
|
56
67
|
readonly RATE_LIMIT: 429;
|
|
57
68
|
readonly INTERNAL_ERROR: 500;
|
|
58
69
|
};
|
|
59
|
-
export type
|
|
70
|
+
export type THttpStatusCode = (typeof HTTP_STATUS)[keyof typeof HTTP_STATUS];
|