js-powerkit 1.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.
@@ -0,0 +1,319 @@
1
+ /**
2
+ * String Utility Functions
3
+ * @module string
4
+ */
5
+
6
+
7
+ /**
8
+ * Capitalizes the first letter of a string.
9
+ * @param {string} str - The input string.
10
+ * @returns {string} The string with the first letter capitalized.
11
+ * @example
12
+ * capitalize('hello world'); // 'Hello world'
13
+ */
14
+ export const capitalize = (str) => {
15
+ if (typeof str !== 'string') throw new TypeError('Input must be a string');
16
+ return str.charAt(0).toUpperCase() + str.slice(1);
17
+ }
18
+
19
+ /**
20
+ * Converts a string to camelCase.
21
+ * @param {string} str - The input string.
22
+ * @returns {string} The camelCase version of the string.
23
+ * @example
24
+ * toCamelCase('hello world'); // 'helloWorld'
25
+ */
26
+ export const toCamelCase = (str) => {
27
+ if (typeof str !== 'string') throw new TypeError('Input must be a string');
28
+ return str
29
+ .replace(/[-_\s]+(.)?/g, (_, char) => char ? char.toUpperCase() : '')
30
+ .replace(/^./, char => char.toLowerCase());
31
+ }
32
+
33
+ /**
34
+ * Converts a string to kebab-case.
35
+ * @param {string} str - The input string.
36
+ * @returns {string} The kebab-case version of the string.
37
+ * @example
38
+ * toKebabCase('Hello World'); // 'hello-world'
39
+ */
40
+ export const toKebabCase = (str) => {
41
+ if (typeof str !== 'string') throw new TypeError('Input must be a string');
42
+ return str
43
+ .replace(/([a-z])([A-Z])/g, '$1-$2')
44
+ .replace(/[\s_]+/g, '-')
45
+ .toLowerCase();
46
+ }
47
+
48
+ /**
49
+ * Converts a string to snake_case.
50
+ * @param {string} str - The input string.
51
+ * @returns {string} The snake_case version of the string.
52
+ * @example
53
+ * toSnakeCase('Hello World'); // 'hello_world'
54
+ */
55
+ export const toSnakeCase = (str) => {
56
+ if (typeof str !== 'string') throw new TypeError('Input must be a string');
57
+ return str
58
+ .replace(/([a-z])([A-Z])/g, '$1_$2')
59
+ .replace(/[\s-]+/g, '_')
60
+ .toLowerCase();
61
+ }
62
+
63
+ /**
64
+ * Converts a string to PascalCase.
65
+ * @param {string} str - The input string.
66
+ * @returns {string} The PascalCase version of the string.
67
+ * @example
68
+ * toPascalCase('hello world'); // 'HelloWorld'
69
+ */
70
+ export const toPascalCase = (str) => {
71
+ if (typeof str !== 'string') throw new TypeError('Input must be a string');
72
+ return str
73
+ .replace(/[-_\s]+(.)?/g, (_, char) => char ? char.toUpperCase() : '')
74
+ .replace(/^./, char => char.toUpperCase());
75
+ }
76
+
77
+ /**
78
+ * Capitalizes the first letter of each word
79
+ * @param {string} str - The string to convert
80
+ * @returns {string} The title case string
81
+ * @example
82
+ * toTitleCase('hello world'); // 'Hello World'
83
+ */
84
+ export const toTitleCase = (str) => {
85
+ if (!str) return '';
86
+ return str.replace(/\w\S*/g, (txt) =>
87
+ txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()
88
+ );
89
+ };
90
+
91
+ /**
92
+ * Reverses a string.
93
+ * @param {string} str - The input string.
94
+ * @returns {string} The reversed string.
95
+ * @example
96
+ * reverse('hello'); // 'olleh'
97
+ */
98
+ export const reverse = (str) => {
99
+ if (typeof str !== 'string') throw new TypeError('Input must be a string');
100
+ return str.split('').reverse().join('');
101
+ }
102
+
103
+ /**
104
+ * Truncates a string to a specified length and adds an ellipsis.
105
+ * @param {string} str - The input string.
106
+ * @param {number} length - The maximum length.
107
+ * @param {string} [suffix='...'] - The suffix to add.
108
+ * @returns {string} The truncated string.
109
+ * @example
110
+ * truncate('Hello World', 5, '', true); // 'He...'
111
+ * truncate('Hello World', 5, '', false); // 'Hello'
112
+ */
113
+ export const truncate = (str, length, suffix = '...', addSuffix = true) => {
114
+ if (typeof str !== 'string') throw new TypeError('Input must be a string');
115
+ if (typeof length !== 'number') throw new TypeError('Length must be a number');
116
+
117
+ if (str.length <= length) return str;
118
+
119
+ if (addSuffix && suffix) {
120
+ return str.slice(0, length - suffix.length) + suffix;
121
+ }
122
+
123
+ return str.slice(0, length);
124
+ };
125
+
126
+ /**
127
+ * Checks if a string is a palindrome.
128
+ * @param {string} str - The input string.
129
+ * @returns {boolean} True if the string is a palindrome.
130
+ * @example
131
+ * isPalindrome('racecar'); // true
132
+ */
133
+ export const isPalindrome = (str) => {
134
+ if (typeof str !== 'string') throw new TypeError('Input must be a string');
135
+ const cleaned = str.replace(/[^a-zA-Z0-9]/g, '').toLowerCase();
136
+ return cleaned === cleaned.split('').reverse().join('');
137
+ }
138
+
139
+ /**
140
+ * Masks a string (useful for sensitive data)
141
+ * @param {string} str - The string to mask
142
+ * @param {number} visibleChars - Number of visible characters at start/end
143
+ * @param {string} maskChar - Character to use for masking
144
+ * @returns {string} The masked string
145
+ * @example
146
+ * mask('secret', 1); // 's****t'
147
+ */
148
+ export const mask = (str, visibleChars = 4, maskChar = '*') => {
149
+ if (!str || str.length <= visibleChars * 2) return str;
150
+ const start = str.substring(0, visibleChars);
151
+ const end = str.substring(str.length - visibleChars);
152
+ const masked = maskChar.repeat(str.length - visibleChars * 2);
153
+ return start + masked + end;
154
+ };
155
+
156
+ /**
157
+ * Converts a string to a URL slug.
158
+ * @param {string} str - The input string.
159
+ * @returns {string} The slugified string.
160
+ * @example
161
+ * slugify('Hello World!'); // 'hello-world'
162
+ */
163
+ export const slugify = (str) => {
164
+ if (typeof str !== 'string') throw new TypeError('Input must be a string');
165
+ return str
166
+ .toLowerCase()
167
+ .trim()
168
+ .replace(/[^\w\s-]/g, '')
169
+ .replace(/[\s_-]+/g, '-')
170
+ .replace(/^-+|-+$/g, '');
171
+ }
172
+
173
+ /**
174
+ * Counts the number of words in a string.
175
+ * @param {string} str - The input string.
176
+ * @returns {number} The word count.
177
+ * @example
178
+ * countWords('Hello world!'); // 2
179
+ */
180
+ export const countWords = (str) => {
181
+ if (typeof str !== 'string') throw new TypeError('Input must be a string');
182
+ return str.trim().split(/\s+/).filter(word => word.length > 0).length;
183
+ }
184
+
185
+ /**
186
+ * Removes duplicate characters from a string.
187
+ * @param {string} str - The input string.
188
+ * @returns {string} The string with duplicates removed.
189
+ * @example
190
+ * removeDuplicates('hello'); // 'helo'
191
+ */
192
+ export const removeDuplicates = (str) => {
193
+ if (typeof str !== 'string') throw new TypeError('Input must be a string');
194
+ return [...new Set(str)].join('');
195
+ }
196
+
197
+ /**
198
+ * Removes all whitespace from a string
199
+ * @param {string} str - The string to process
200
+ * @returns {string} String without whitespace
201
+ * @example
202
+ * removeWhitespace(' hello world '); // 'helloworld'
203
+ */
204
+ export const removeWhitespace = (str) => {
205
+ if (!str) return '';
206
+ return str.replace(/\s+/g, '');
207
+ };
208
+
209
+ /**
210
+ * Extracts all email addresses from a string
211
+ * @param {string} str - The string to search
212
+ * @returns {Array<string>} Array of email addresses
213
+ * @example
214
+ * extractEmails('Contact us at test@example.com or support@test.org'); // ['test@example.com', 'support@test.org']
215
+ */
216
+ export const extractEmails = (str) => {
217
+ if (!str) return [];
218
+ const emailRegex = /[\w.-]+@[\w.-]+\.\w+/g;
219
+ return str.match(emailRegex) || [];
220
+ };
221
+
222
+ /**
223
+ * Extracts all URLs from a string
224
+ * @param {string} str - The string to search
225
+ * @returns {Array<string>} Array of URLs
226
+ * @example
227
+ * extractUrls('Visit https://example.com or http://test.org'); // ['https://example.com', 'http://test.org']
228
+ */
229
+ export const extractUrls = (str) => {
230
+ if (!str) return [];
231
+ const urlRegex = /(https?:\/\/[^\s]+)/g;
232
+ return str.match(urlRegex) || [];
233
+ };
234
+
235
+ /**
236
+ * Removes HTML tags from a string
237
+ * @param {string} str - The string containing HTML
238
+ * @returns {string} String without HTML tags
239
+ * @example
240
+ * stripHtml('<p>Hello <b>World</b></p>'); // 'Hello World'
241
+ */
242
+ export const stripHtml = (str) => {
243
+ if (!str) return '';
244
+ return str.replace(/<[^>]*>/g, '');
245
+ };
246
+
247
+ /**
248
+ * Escapes HTML special characters
249
+ * @param {string} str - The string to escape
250
+ * @returns {string} The escaped string
251
+ * @example
252
+ * escapeHtml('<div>Test & "quotes"</div>'); // '&lt;div&gt;Test &amp; &quot;quotes&quot;&lt;/div&gt;'
253
+ */
254
+ export const escapeHtml = (str) => {
255
+ if (!str) return '';
256
+ const htmlEscapes = {
257
+ '&': '&amp;',
258
+ '<': '&lt;',
259
+ '>': '&gt;',
260
+ '"': '&quot;',
261
+ "'": '&#39;'
262
+ };
263
+ return str.replace(/[&<>"']/g, char => htmlEscapes[char]);
264
+ };
265
+
266
+ /**
267
+ * Checks if a string contains only numbers
268
+ * @param {string} str - The string to check
269
+ * @returns {boolean} True if numeric
270
+ * @example
271
+ * isNumeric('12345'); // true
272
+ */
273
+ export const isNumeric = (str) => {
274
+ if (!str) return false;
275
+ return /^\d+$/.test(str);
276
+ };
277
+
278
+ /**
279
+ * Checks if a string is a valid email
280
+ * @param {string} str - The string to validate
281
+ * @returns {boolean} True if valid email
282
+ * @example
283
+ * isEmail('test@example.com'); // true
284
+ */
285
+ export const isEmail = (str) => {
286
+ if (!str) return false;
287
+ const emailRegex = /^[\w.-]+@[\w.-]+\.\w+$/;
288
+ return emailRegex.test(str);
289
+ };
290
+
291
+ /**
292
+ * Checks if a string is a valid URL
293
+ * @param {string} str - The string to validate
294
+ * @returns {boolean} True if valid URL
295
+ * @example
296
+ * isUrl('https://example.com'); // true
297
+ */
298
+ export const isUrl = (str) => {
299
+ if (!str) return false;
300
+ try {
301
+ new URL(str);
302
+ return true;
303
+ } catch {
304
+ return false;
305
+ }
306
+ };
307
+
308
+ /**
309
+ * Repeats a string n times
310
+ * @param {string} str - The string to repeat
311
+ * @param {number} times - Number of repetitions
312
+ * @returns {string} The repeated string
313
+ * @example
314
+ * repeatString('ab', 3); // 'ababab'
315
+ */
316
+ export const repeatString = (str, times) => {
317
+ if (!str || times <= 0) return '';
318
+ return str.repeat(times);
319
+ };
@@ -0,0 +1,204 @@
1
+ import {
2
+ chunk,
3
+ flatten,
4
+ unique,
5
+ shuffle,
6
+ randomElement,
7
+ compact,
8
+ sortBy,
9
+ groupBy,
10
+ intersection,
11
+ difference,
12
+ union,
13
+ zip,
14
+ range,
15
+ max,
16
+ min,
17
+ sum,
18
+ average,
19
+ countOccurrences,
20
+ remove,
21
+ take,
22
+ drop,
23
+ includesAll,
24
+ includesAny,
25
+ rotate
26
+ } from '../src/arrayUtils.js';
27
+
28
+ describe('Array Utilities', () => {
29
+ describe('chunk', () => {
30
+ test('splits array into chunks', () => {
31
+ expect(chunk([1, 2, 3, 4, 5], 2)).toEqual([[1, 2], [3, 4], [5]]);
32
+ expect(chunk([1, 2, 3], 3)).toEqual([[1, 2, 3]]);
33
+ });
34
+
35
+ test('throws error for invalid inputs', () => {
36
+ expect(() => chunk('not array', 2)).toThrow(TypeError);
37
+ expect(() => chunk([1, 2], 0)).toThrow(TypeError);
38
+ });
39
+ });
40
+
41
+ describe('flatten', () => {
42
+ test('flattens nested arrays', () => {
43
+ expect(flatten([1, [2, [3, 4]], 5])).toEqual([1, 2, [3, 4], 5]);
44
+ expect(flatten([1, [2, [3, 4]], 5], 2)).toEqual([1, 2, 3, 4, 5]);
45
+ });
46
+ });
47
+
48
+ describe('unique', () => {
49
+ test('removes duplicates', () => {
50
+ expect(unique([1, 2, 2, 3, 4, 4])).toEqual([1, 2, 3, 4]);
51
+ expect(unique(['a', 'b', 'a'])).toEqual(['a', 'b']);
52
+ });
53
+ });
54
+
55
+ describe('shuffle', () => {
56
+ test('shuffles array', () => {
57
+ const arr = [1, 2, 3, 4, 5];
58
+ const shuffled = shuffle(arr);
59
+ expect(shuffled).toHaveLength(5);
60
+ expect(shuffled.sort()).toEqual([1, 2, 3, 4, 5]);
61
+ });
62
+ });
63
+
64
+ describe('randomElement', () => {
65
+ test('returns random element', () => {
66
+ const arr = [1, 2, 3, 4, 5];
67
+ const element = randomElement(arr);
68
+ expect(arr).toContain(element);
69
+ });
70
+
71
+ test('returns undefined for empty array', () => {
72
+ expect(randomElement([])).toBeUndefined();
73
+ });
74
+ });
75
+
76
+ describe('compact', () => {
77
+ test('removes falsy values', () => {
78
+ expect(compact([0, 1, false, 2, '', 3, null, undefined, NaN]))
79
+ .toEqual([1, 2, 3]);
80
+ });
81
+ });
82
+
83
+ describe('sortBy', () => {
84
+ test('sorts by property', () => {
85
+ const arr = [{ a: 2 }, { a: 1 }, { a: 3 }];
86
+ expect(sortBy(arr, 'a')).toEqual([{ a: 1 }, { a: 2 }, { a: 3 }]);
87
+ expect(sortBy(arr, 'a', 'desc')).toEqual([{ a: 3 }, { a: 2 }, { a: 1 }]);
88
+ });
89
+ });
90
+
91
+ describe('groupBy', () => {
92
+ test('groups by property', () => {
93
+ const arr = [{ type: 'a', val: 1 }, { type: 'b', val: 2 }, { type: 'a', val: 3 }];
94
+ expect(groupBy(arr, 'type')).toEqual({
95
+ a: [{ type: 'a', val: 1 }, { type: 'a', val: 3 }],
96
+ b: [{ type: 'b', val: 2 }]
97
+ });
98
+ });
99
+ });
100
+
101
+ describe('intersection', () => {
102
+ test('finds intersection', () => {
103
+ expect(intersection([1, 2, 3], [2, 3, 4])).toEqual([2, 3]);
104
+ });
105
+ });
106
+
107
+ describe('difference', () => {
108
+ test('finds difference', () => {
109
+ expect(difference([1, 2, 3], [2, 3, 4])).toEqual([1]);
110
+ });
111
+ });
112
+
113
+ describe('union', () => {
114
+ test('finds union', () => {
115
+ expect(union([1, 2, 3], [2, 3, 4])).toEqual([1, 2, 3, 4]);
116
+ });
117
+ });
118
+
119
+ describe('zip', () => {
120
+ test('zips arrays', () => {
121
+ expect(zip([1, 2], ['a', 'b'], [true, false])).toEqual([[1, 'a', true], [2, 'b', false]]);
122
+ });
123
+ });
124
+
125
+ describe('range', () => {
126
+ test('creates range', () => {
127
+ expect(range(1, 5)).toEqual([1, 2, 3, 4, 5]);
128
+ expect(range(0, 10, 2)).toEqual([0, 2, 4, 6, 8, 10]);
129
+ });
130
+ });
131
+
132
+ describe('max', () => {
133
+ test('finds maximum value', () => {
134
+ expect(max([1, 5, 3, 9, 2])).toBe(9);
135
+ });
136
+ });
137
+
138
+ describe('min', () => {
139
+ test('finds minimum value', () => {
140
+ expect(min([1, 5, 3, 9, 2])).toBe(1);
141
+ });
142
+ });
143
+
144
+ describe('sum', () => {
145
+ test('calculates sum', () => {
146
+ expect(sum([1, 2, 3, 4, 5])).toBe(15);
147
+ });
148
+ });
149
+
150
+ describe('average', () => {
151
+ test('calculates average', () => {
152
+ expect(average([1, 2, 3, 4, 5])).toBe(3);
153
+ });
154
+ });
155
+
156
+ describe('countOccurrences', () => {
157
+ test('counts occurrences', () => {
158
+ expect(countOccurrences([1, 2, 2, 3, 3, 3]))
159
+ .toEqual({ '1': 1, '2': 2, '3': 3 });
160
+ });
161
+ });
162
+
163
+ describe('remove', () => {
164
+ test('removes value from array', () => {
165
+ expect(remove([1, 2, 3, 2, 4], 2)).toEqual([1, 3, 4]);
166
+ });
167
+ });
168
+
169
+ describe('take', () => {
170
+ test('takes first n elements', () => {
171
+ expect(take([1, 2, 3, 4, 5], 3)).toEqual([1, 2, 3]);
172
+ expect(take([10, 21, 31, 41, 51], 4)).toEqual([10, 21, 31, 41]);
173
+ });
174
+ });
175
+
176
+ describe('drop', () => {
177
+ test('drops first n elements', () => {
178
+ expect(drop([1, 2, 3, 4, 5], 2)).toEqual([3, 4, 5]);
179
+ });
180
+ });
181
+
182
+ describe('includesAll', () => {
183
+ test('checks if includes all values', () => {
184
+ expect(includesAll([1, 2, 3, 4], [2, 3])).toBe(true);
185
+ expect(includesAll([1, 2, 3], [2, 5])).toBe(false);
186
+ });
187
+ });
188
+
189
+ describe('includesAny', () => {
190
+ test('checks if includes any value', () => {
191
+ expect(includesAny([1, 2, 3], [3, 4, 5])).toBe(true);
192
+ expect(includesAny([1, 2, 3], [4, 5, 6])).toBe(false);
193
+ });
194
+ });
195
+
196
+ describe('rotate', () => {
197
+ test('rotates array', () => {
198
+ expect(rotate([1, 2, 3, 4, 5], 2)).toEqual([3, 4, 5, 1, 2]);
199
+ expect(rotate([1, 2, 3, 4, 5], 1)).toEqual([2, 3, 4, 5, 1]);
200
+ expect(rotate([1, 2, 3, 4, 5], -1)).toEqual([5, 1, 2, 3, 4]);
201
+ expect(rotate([1, 2, 3, 4, 5], -2)).toEqual([4, 5, 1, 2, 3]);
202
+ });
203
+ });
204
+ });