@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.
Files changed (60) hide show
  1. package/dist/helpers/date/index.d.ts +113 -0
  2. package/dist/helpers/date/index.js +171 -0
  3. package/dist/helpers/index.d.ts +3 -1
  4. package/dist/helpers/index.js +2 -1
  5. package/dist/helpers/object/index.d.ts +160 -75
  6. package/dist/helpers/object/index.js +355 -168
  7. package/dist/helpers/string/index.d.ts +227 -74
  8. package/dist/helpers/string/index.js +448 -114
  9. package/dist/types/api/index.d.ts +22 -11
  10. package/dist/types/api/index.js +9 -2
  11. package/dist/types/auth/index.d.ts +56 -18
  12. package/dist/types/auth/index.js +7 -0
  13. package/dist/types/auth/routes.d.ts +76 -47
  14. package/dist/types/auth/routes.js +0 -1
  15. package/dist/types/base/index.d.ts +200 -69
  16. package/dist/types/base/index.js +116 -0
  17. package/dist/types/base/paging.types.d.ts +11 -11
  18. package/dist/types/checklist-template/index.d.ts +8 -8
  19. package/dist/types/checklist-template/routes.d.ts +57 -28
  20. package/dist/types/checklist-template/routes.js +0 -1
  21. package/dist/types/company/index.d.ts +5 -5
  22. package/dist/types/company/routes.d.ts +81 -33
  23. package/dist/types/company/routes.js +0 -1
  24. package/dist/types/damage-report/index.d.ts +9 -9
  25. package/dist/types/damage-report/routes.d.ts +128 -51
  26. package/dist/types/damage-report/routes.js +0 -1
  27. package/dist/types/errors/index.d.ts +15 -16
  28. package/dist/types/errors/index.js +17 -7
  29. package/dist/types/health/index.d.ts +20 -0
  30. package/dist/types/health/index.js +1 -0
  31. package/dist/types/health/routes.d.ts +10 -0
  32. package/dist/types/health/routes.js +1 -0
  33. package/dist/types/image/index.d.ts +34 -0
  34. package/dist/types/image/index.js +11 -0
  35. package/dist/types/image/routes.d.ts +32 -0
  36. package/dist/types/image/routes.js +1 -0
  37. package/dist/types/index.d.ts +3 -0
  38. package/dist/types/index.js +3 -0
  39. package/dist/types/inventory/index.d.ts +13 -87
  40. package/dist/types/inventory/routes.d.ts +73 -36
  41. package/dist/types/inventory/routes.js +0 -1
  42. package/dist/types/job/index.d.ts +7 -0
  43. package/dist/types/job/index.js +1 -0
  44. package/dist/types/property/index.d.ts +88 -29
  45. package/dist/types/property/index.js +23 -23
  46. package/dist/types/property/routes.d.ts +40 -14
  47. package/dist/types/property/routes.js +0 -1
  48. package/dist/types/room/index.d.ts +4 -4
  49. package/dist/types/room/routes.d.ts +27 -8
  50. package/dist/types/room/routes.js +0 -1
  51. package/dist/types/room-checklist/index.d.ts +9 -9
  52. package/dist/types/room-checklist/routes.d.ts +50 -28
  53. package/dist/types/room-checklist/routes.js +0 -1
  54. package/dist/types/user/index.d.ts +12 -12
  55. package/dist/types/user/routes.d.ts +26 -16
  56. package/dist/types/user/routes.js +0 -1
  57. package/dist/types/work-session/index.d.ts +12 -12
  58. package/dist/types/work-session/routes.d.ts +55 -34
  59. package/dist/types/work-session/routes.js +0 -1
  60. package/package.json +14 -5
@@ -1,161 +1,495 @@
1
1
  /**
2
- * Convert a string to "Normal Case":
3
- * - Inserts spaces between camel/pascal case boundaries
4
- * - Capitalizes the first letter of each word, lowercases the rest
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
- * @param {TurndownObject} [str] Input value (falsy returns an empty string)
7
- * @returns {string}
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
- return str
16
- .replace(/([a-z])([A-Z])/g, "$1 $2")
17
- .replace(/([A-Z])([A-Z][a-z])/g, "$1 $2")
18
- .split(/\s+/)
19
- .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
20
- .join(" ");
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
- * Capitalize only the first character; lowercases the rest.
24
- * Trims leading/trailing spaces before processing.
57
+ * Converts a string to camelCase.
25
58
  *
26
- * @param {TurndownObject} [str] Input value (falsy returns an empty string)
27
- * @returns {string}
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 sentenceCase = (str) => {
32
- if (!str)
62
+ export const toCamelCase = (str) => {
63
+ const words = getWords(str);
64
+ if (words.length === 0) {
33
65
  return "";
34
- const s = str.trim();
35
- return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();
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
- * Uppercase the entire string.
75
+ * Converts a string to kebab-case.
39
76
  *
40
- * @param {TurndownObject} [str] Input value (falsy returns an empty string)
41
- * @returns {string}
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 upperCase = (str) => {
46
- return str ? str.toUpperCase() : "";
80
+ export const toKebabCase = (str) => {
81
+ return getWords(str).map((word) => word.toLowerCase()).join("-");
47
82
  };
83
+ export const kebabCase = toKebabCase;
48
84
  /**
49
- * Lowercase the entire string.
85
+ * Converts a string to snake_case.
50
86
  *
51
- * @param {TurndownObject} [str] Input value (falsy returns an empty string)
52
- * @returns {string}
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 lowerCase = (str) => {
57
- return str ? str.toLowerCase() : "";
90
+ export const toSnakeCase = (str) => {
91
+ return getWords(str).map((word) => word.toLowerCase()).join("_");
58
92
  };
93
+ export const snakeCase = toSnakeCase;
59
94
  /**
60
- * Convert to camelCase.
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
- * @param {TurndownObject} [str] Input value (falsy returns an empty string)
65
- * @returns {string}
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 camelCase = (str) => {
71
- if (!str)
72
- return "";
73
- return str
74
- .toLowerCase()
75
- .split(/[\s_-]+/)
76
- .map((word, i) => i === 0
77
- ? word
78
- : word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
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
- * Convert to PascalCase.
83
- * Splits on spaces, underscores, and hyphens; TitleCases all words and joins them.
111
+ * Capitalizes the first character of a string.
84
112
  *
85
- * @param {TurndownObject} [str] Input value (falsy returns an empty string)
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 pascalCase = (str) => {
92
- if (!str)
115
+ export const capitalize = (str) => {
116
+ if (!str) {
93
117
  return "";
94
- return str
95
- .toLowerCase()
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
- * Convert to kebab-case.
102
- * Inserts hyphens between camelCase boundaries, then replaces spaces/underscores with hyphens.
122
+ * Capitalizes the first letter of each word.
103
123
  *
104
- * @param {TurndownObject} [str] Input value (falsy returns an empty string)
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 kebabCase = (str) => {
111
- if (!str)
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
- .replace(/([a-z])([A-Z])/g, "$1-$2")
170
+ .toLowerCase()
171
+ .trim()
172
+ .replace(/[^\w\s-]/g, "")
115
173
  .replace(/[\s_]+/g, "-")
116
- .toLowerCase();
174
+ .replace(/-+/g, "-")
175
+ .replace(/^-+|-+$/g, "");
117
176
  };
118
177
  /**
119
- * Convert to snake_case.
120
- * Inserts underscores between camelCase boundaries, then replaces spaces/hyphens with underscores.
178
+ * Validates if a string is a valid email.
121
179
  *
122
- * @param {TurndownObject} [str] Input value (falsy returns an empty string)
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 snakeCase = (str) => {
129
- if (!str)
130
- return "";
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
- * Convert snake_case to space-delimited words.
187
+ * Validates if a string is a valid URL.
138
188
  *
139
- * @param {TurndownObject} [str] Input value (falsy returns an empty string)
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 snakeCaseToSpaces = (str) => {
145
- if (!str)
146
- return "";
147
- return str.split("_").join(" ");
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
- * Convert kebab-case to space-delimited words.
201
+ * Validates if a string contains only numbers.
151
202
  *
152
- * @param {TurndownObject} [str] Input value (falsy returns an empty string)
153
- * @returns {string}
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 kebabToSpaces = (str) => {
158
- if (!str)
159
- return "";
160
- return str.split("-").join(" ");
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 interface ApiError {
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?: TurndownObject;
17
+ details?: TDetails;
10
18
  }
11
- export interface ApiMeta {
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 ApiResponse<T = TurndownObject> {
25
+ export interface IApiResponse<TData = TurndownObject> {
18
26
  success: boolean;
19
- data?: T | null;
20
- error?: ApiError | null;
21
- meta?: ApiMeta;
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 ErrorCodes: {
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 ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];
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 HttpStatus: {
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 HttpStatusCode = (typeof HttpStatus)[keyof typeof HttpStatus];
70
+ export type THttpStatusCode = (typeof HTTP_STATUS)[keyof typeof HTTP_STATUS];