@fluixi/utils 1.0.0-alpha.53
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/LICENSE +21 -0
- package/README.md +73 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/index.mjs +1 -0
- package/dist/lib/array/advanced.cjs +1 -0
- package/dist/lib/array/advanced.d.ts +1116 -0
- package/dist/lib/array/advanced.d.ts.map +1 -0
- package/dist/lib/array/advanced.js +2429 -0
- package/dist/lib/array/advanced.mjs +1 -0
- package/dist/lib/array/array.cjs +1 -0
- package/dist/lib/array/array.d.ts +6 -0
- package/dist/lib/array/array.d.ts.map +1 -0
- package/dist/lib/array/array.js +31 -0
- package/dist/lib/array/array.mjs +1 -0
- package/dist/lib/array/index.cjs +1 -0
- package/dist/lib/array/index.d.ts +4 -0
- package/dist/lib/array/index.d.ts.map +1 -0
- package/dist/lib/array/index.js +3 -0
- package/dist/lib/array/index.mjs +1 -0
- package/dist/lib/array/utils.cjs +1 -0
- package/dist/lib/array/utils.d.ts +100 -0
- package/dist/lib/array/utils.d.ts.map +1 -0
- package/dist/lib/array/utils.js +165 -0
- package/dist/lib/array/utils.mjs +1 -0
- package/dist/lib/color/color.cjs +1 -0
- package/dist/lib/color/color.d.ts +366 -0
- package/dist/lib/color/color.d.ts.map +1 -0
- package/dist/lib/color/color.js +722 -0
- package/dist/lib/color/color.mjs +1 -0
- package/dist/lib/color/constants.cjs +1 -0
- package/dist/lib/color/constants.d.ts +361 -0
- package/dist/lib/color/constants.d.ts.map +1 -0
- package/dist/lib/color/constants.js +358 -0
- package/dist/lib/color/constants.mjs +1 -0
- package/dist/lib/color/conversions.cjs +1 -0
- package/dist/lib/color/conversions.d.ts +166 -0
- package/dist/lib/color/conversions.d.ts.map +1 -0
- package/dist/lib/color/conversions.js +766 -0
- package/dist/lib/color/conversions.mjs +1 -0
- package/dist/lib/color/helpers.cjs +1 -0
- package/dist/lib/color/helpers.d.ts +75 -0
- package/dist/lib/color/helpers.d.ts.map +1 -0
- package/dist/lib/color/helpers.js +419 -0
- package/dist/lib/color/helpers.mjs +1 -0
- package/dist/lib/color/index.cjs +1 -0
- package/dist/lib/color/index.d.ts +12 -0
- package/dist/lib/color/index.d.ts.map +1 -0
- package/dist/lib/color/index.js +15 -0
- package/dist/lib/color/index.mjs +1 -0
- package/dist/lib/color/types.cjs +1 -0
- package/dist/lib/color/types.d.ts +179 -0
- package/dist/lib/color/types.d.ts.map +1 -0
- package/dist/lib/color/types.js +1 -0
- package/dist/lib/color/types.mjs +0 -0
- package/dist/lib/color/utils.cjs +1 -0
- package/dist/lib/color/utils.d.ts +199 -0
- package/dist/lib/color/utils.d.ts.map +1 -0
- package/dist/lib/color/utils.js +858 -0
- package/dist/lib/color/utils.mjs +1 -0
- package/dist/lib/compare/comparators.cjs +1 -0
- package/dist/lib/compare/comparators.d.ts +65 -0
- package/dist/lib/compare/comparators.d.ts.map +1 -0
- package/dist/lib/compare/comparators.js +210 -0
- package/dist/lib/compare/comparators.mjs +1 -0
- package/dist/lib/compare/index.cjs +1 -0
- package/dist/lib/compare/index.d.ts +2 -0
- package/dist/lib/compare/index.d.ts.map +1 -0
- package/dist/lib/compare/index.js +1 -0
- package/dist/lib/compare/index.mjs +1 -0
- package/dist/lib/crypto/hash.cjs +1 -0
- package/dist/lib/crypto/hash.d.ts +6 -0
- package/dist/lib/crypto/hash.d.ts.map +1 -0
- package/dist/lib/crypto/hash.js +53 -0
- package/dist/lib/crypto/hash.mjs +1 -0
- package/dist/lib/crypto/index.cjs +1 -0
- package/dist/lib/crypto/index.d.ts +2 -0
- package/dist/lib/crypto/index.d.ts.map +1 -0
- package/dist/lib/crypto/index.js +1 -0
- package/dist/lib/crypto/index.mjs +1 -0
- package/dist/lib/dom/events.cjs +1 -0
- package/dist/lib/dom/events.d.ts +159 -0
- package/dist/lib/dom/events.d.ts.map +1 -0
- package/dist/lib/dom/events.js +50 -0
- package/dist/lib/dom/events.mjs +1 -0
- package/dist/lib/dom/index.cjs +1 -0
- package/dist/lib/dom/index.d.ts +3 -0
- package/dist/lib/dom/index.d.ts.map +1 -0
- package/dist/lib/dom/index.js +2 -0
- package/dist/lib/dom/index.mjs +1 -0
- package/dist/lib/dom/utils.cjs +1 -0
- package/dist/lib/dom/utils.d.ts +6 -0
- package/dist/lib/dom/utils.d.ts.map +1 -0
- package/dist/lib/dom/utils.js +37 -0
- package/dist/lib/dom/utils.mjs +1 -0
- package/dist/lib/functions/functions.cjs +1 -0
- package/dist/lib/functions/functions.d.ts +329 -0
- package/dist/lib/functions/functions.d.ts.map +1 -0
- package/dist/lib/functions/functions.js +585 -0
- package/dist/lib/functions/functions.mjs +1 -0
- package/dist/lib/functions/index.cjs +1 -0
- package/dist/lib/functions/index.d.ts +3 -0
- package/dist/lib/functions/index.d.ts.map +1 -0
- package/dist/lib/functions/index.js +1 -0
- package/dist/lib/functions/index.mjs +1 -0
- package/dist/lib/functions/types.cjs +1 -0
- package/dist/lib/functions/types.d.ts +2 -0
- package/dist/lib/functions/types.d.ts.map +1 -0
- package/dist/lib/functions/types.js +1 -0
- package/dist/lib/functions/types.mjs +0 -0
- package/dist/lib/helpers.cjs +1 -0
- package/dist/lib/helpers.d.ts +13 -0
- package/dist/lib/helpers.d.ts.map +1 -0
- package/dist/lib/helpers.js +66 -0
- package/dist/lib/helpers.mjs +1 -0
- package/dist/lib/index.cjs +1 -0
- package/dist/lib/index.d.ts +6 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +8 -0
- package/dist/lib/index.mjs +1 -0
- package/dist/lib/maths/constants.cjs +1 -0
- package/dist/lib/maths/constants.d.ts +84 -0
- package/dist/lib/maths/constants.d.ts.map +1 -0
- package/dist/lib/maths/constants.js +79 -0
- package/dist/lib/maths/constants.mjs +1 -0
- package/dist/lib/maths/fn/advanced.cjs +1 -0
- package/dist/lib/maths/fn/advanced.d.ts +149 -0
- package/dist/lib/maths/fn/advanced.d.ts.map +1 -0
- package/dist/lib/maths/fn/advanced.js +264 -0
- package/dist/lib/maths/fn/advanced.mjs +1 -0
- package/dist/lib/maths/fn/basic.cjs +1 -0
- package/dist/lib/maths/fn/basic.d.ts +182 -0
- package/dist/lib/maths/fn/basic.d.ts.map +1 -0
- package/dist/lib/maths/fn/basic.js +343 -0
- package/dist/lib/maths/fn/basic.mjs +1 -0
- package/dist/lib/maths/fn/complex.cjs +1 -0
- package/dist/lib/maths/fn/complex.d.ts +153 -0
- package/dist/lib/maths/fn/complex.d.ts.map +1 -0
- package/dist/lib/maths/fn/complex.js +383 -0
- package/dist/lib/maths/fn/complex.mjs +1 -0
- package/dist/lib/maths/fn/index.cjs +1 -0
- package/dist/lib/maths/fn/index.d.ts +10 -0
- package/dist/lib/maths/fn/index.d.ts.map +1 -0
- package/dist/lib/maths/fn/index.js +9 -0
- package/dist/lib/maths/fn/index.mjs +1 -0
- package/dist/lib/maths/fn/trigo.cjs +1 -0
- package/dist/lib/maths/fn/trigo.d.ts +147 -0
- package/dist/lib/maths/fn/trigo.d.ts.map +1 -0
- package/dist/lib/maths/fn/trigo.js +227 -0
- package/dist/lib/maths/fn/trigo.mjs +1 -0
- package/dist/lib/maths/fn/vector.cjs +1 -0
- package/dist/lib/maths/fn/vector.d.ts +512 -0
- package/dist/lib/maths/fn/vector.d.ts.map +1 -0
- package/dist/lib/maths/fn/vector.js +1136 -0
- package/dist/lib/maths/fn/vector.mjs +1 -0
- package/dist/lib/maths/formatter.cjs +1 -0
- package/dist/lib/maths/formatter.d.ts +35 -0
- package/dist/lib/maths/formatter.d.ts.map +1 -0
- package/dist/lib/maths/formatter.js +56 -0
- package/dist/lib/maths/formatter.mjs +1 -0
- package/dist/lib/maths/guards.cjs +1 -0
- package/dist/lib/maths/guards.d.ts +166 -0
- package/dist/lib/maths/guards.d.ts.map +1 -0
- package/dist/lib/maths/guards.js +315 -0
- package/dist/lib/maths/guards.mjs +1 -0
- package/dist/lib/maths/index.cjs +1 -0
- package/dist/lib/maths/index.d.ts +24 -0
- package/dist/lib/maths/index.d.ts.map +1 -0
- package/dist/lib/maths/index.js +29 -0
- package/dist/lib/maths/index.mjs +1 -0
- package/dist/lib/maths/parser.cjs +1 -0
- package/dist/lib/maths/parser.d.ts +20 -0
- package/dist/lib/maths/parser.d.ts.map +1 -0
- package/dist/lib/maths/parser.js +78 -0
- package/dist/lib/maths/parser.mjs +1 -0
- package/dist/lib/maths/random.cjs +1 -0
- package/dist/lib/maths/random.d.ts +27 -0
- package/dist/lib/maths/random.d.ts.map +1 -0
- package/dist/lib/maths/random.js +49 -0
- package/dist/lib/maths/random.mjs +1 -0
- package/dist/lib/maths/types/advanced.cjs +1 -0
- package/dist/lib/maths/types/advanced.d.ts +357 -0
- package/dist/lib/maths/types/advanced.d.ts.map +1 -0
- package/dist/lib/maths/types/advanced.js +5 -0
- package/dist/lib/maths/types/advanced.mjs +0 -0
- package/dist/lib/maths/types/basic.cjs +1 -0
- package/dist/lib/maths/types/basic.d.ts +95 -0
- package/dist/lib/maths/types/basic.d.ts.map +1 -0
- package/dist/lib/maths/types/basic.js +5 -0
- package/dist/lib/maths/types/basic.mjs +0 -0
- package/dist/lib/maths/types/index.cjs +1 -0
- package/dist/lib/maths/types/index.d.ts +4 -0
- package/dist/lib/maths/types/index.d.ts.map +1 -0
- package/dist/lib/maths/types/index.js +3 -0
- package/dist/lib/maths/types/index.mjs +0 -0
- package/dist/lib/maths/types/negative.cjs +1 -0
- package/dist/lib/maths/types/negative.d.ts +51 -0
- package/dist/lib/maths/types/negative.d.ts.map +1 -0
- package/dist/lib/maths/types/negative.js +5 -0
- package/dist/lib/maths/types/negative.mjs +0 -0
- package/dist/lib/maths/types/operations.cjs +1 -0
- package/dist/lib/maths/types/operations.d.ts +188 -0
- package/dist/lib/maths/types/operations.d.ts.map +1 -0
- package/dist/lib/maths/types/operations.js +6 -0
- package/dist/lib/maths/types/operations.mjs +0 -0
- package/dist/lib/object/clone.cjs +1 -0
- package/dist/lib/object/clone.d.ts +46 -0
- package/dist/lib/object/clone.d.ts.map +1 -0
- package/dist/lib/object/clone.js +104 -0
- package/dist/lib/object/clone.mjs +1 -0
- package/dist/lib/object/diff.cjs +1 -0
- package/dist/lib/object/diff.d.ts +203 -0
- package/dist/lib/object/diff.d.ts.map +1 -0
- package/dist/lib/object/diff.js +805 -0
- package/dist/lib/object/diff.mjs +1 -0
- package/dist/lib/object/getter-setter.cjs +1 -0
- package/dist/lib/object/getter-setter.d.ts +361 -0
- package/dist/lib/object/getter-setter.d.ts.map +1 -0
- package/dist/lib/object/getter-setter.js +1771 -0
- package/dist/lib/object/getter-setter.mjs +1 -0
- package/dist/lib/object/index.cjs +6 -0
- package/dist/lib/object/index.d.ts +13 -0
- package/dist/lib/object/index.d.ts.map +1 -0
- package/dist/lib/object/index.js +12 -0
- package/dist/lib/object/index.mjs +6 -0
- package/dist/lib/object/merge.cjs +1 -0
- package/dist/lib/object/merge.d.ts +93 -0
- package/dist/lib/object/merge.d.ts.map +1 -0
- package/dist/lib/object/merge.js +474 -0
- package/dist/lib/object/merge.mjs +1 -0
- package/dist/lib/object/object.cjs +1 -0
- package/dist/lib/object/object.d.ts +128 -0
- package/dist/lib/object/object.d.ts.map +1 -0
- package/dist/lib/object/object.js +432 -0
- package/dist/lib/object/object.mjs +1 -0
- package/dist/lib/object/omit.cjs +1 -0
- package/dist/lib/object/omit.d.ts +351 -0
- package/dist/lib/object/omit.d.ts.map +1 -0
- package/dist/lib/object/omit.js +656 -0
- package/dist/lib/object/omit.mjs +1 -0
- package/dist/lib/object/reconcile.cjs +1 -0
- package/dist/lib/object/reconcile.d.ts +7 -0
- package/dist/lib/object/reconcile.d.ts.map +1 -0
- package/dist/lib/object/reconcile.js +122 -0
- package/dist/lib/object/reconcile.mjs +1 -0
- package/dist/lib/object/selector.cjs +1 -0
- package/dist/lib/object/selector.d.ts +50 -0
- package/dist/lib/object/selector.d.ts.map +1 -0
- package/dist/lib/object/selector.js +371 -0
- package/dist/lib/object/selector.mjs +1 -0
- package/dist/lib/object/sort.cjs +1 -0
- package/dist/lib/object/sort.d.ts +149 -0
- package/dist/lib/object/sort.d.ts.map +1 -0
- package/dist/lib/object/sort.js +298 -0
- package/dist/lib/object/sort.mjs +1 -0
- package/dist/lib/object/transform.cjs +1 -0
- package/dist/lib/object/transform.d.ts +620 -0
- package/dist/lib/object/transform.d.ts.map +1 -0
- package/dist/lib/object/transform.js +1058 -0
- package/dist/lib/object/transform.mjs +1 -0
- package/dist/lib/object/types.cjs +1 -0
- package/dist/lib/object/types.d.ts +250 -0
- package/dist/lib/object/types.d.ts.map +1 -0
- package/dist/lib/object/types.js +1 -0
- package/dist/lib/object/types.mjs +0 -0
- package/dist/lib/object/utils.cjs +6 -0
- package/dist/lib/object/utils.d.ts +165 -0
- package/dist/lib/object/utils.d.ts.map +1 -0
- package/dist/lib/object/utils.js +444 -0
- package/dist/lib/object/utils.mjs +6 -0
- package/dist/lib/primitive/boolean/boolean.cjs +1 -0
- package/dist/lib/primitive/boolean/boolean.d.ts +276 -0
- package/dist/lib/primitive/boolean/boolean.d.ts.map +1 -0
- package/dist/lib/primitive/boolean/boolean.js +615 -0
- package/dist/lib/primitive/boolean/boolean.mjs +1 -0
- package/dist/lib/primitive/boolean/index.cjs +1 -0
- package/dist/lib/primitive/boolean/index.d.ts +3 -0
- package/dist/lib/primitive/boolean/index.d.ts.map +1 -0
- package/dist/lib/primitive/boolean/index.js +2 -0
- package/dist/lib/primitive/boolean/index.mjs +1 -0
- package/dist/lib/primitive/boolean/types.cjs +1 -0
- package/dist/lib/primitive/boolean/types.d.ts +14 -0
- package/dist/lib/primitive/boolean/types.d.ts.map +1 -0
- package/dist/lib/primitive/boolean/types.js +1 -0
- package/dist/lib/primitive/boolean/types.mjs +0 -0
- package/dist/lib/primitive/date/date.cjs +1 -0
- package/dist/lib/primitive/date/date.d.ts +641 -0
- package/dist/lib/primitive/date/date.d.ts.map +1 -0
- package/dist/lib/primitive/date/date.js +1998 -0
- package/dist/lib/primitive/date/date.mjs +1 -0
- package/dist/lib/primitive/date/index.cjs +1 -0
- package/dist/lib/primitive/date/index.d.ts +3 -0
- package/dist/lib/primitive/date/index.d.ts.map +1 -0
- package/dist/lib/primitive/date/index.js +2 -0
- package/dist/lib/primitive/date/index.mjs +1 -0
- package/dist/lib/primitive/date/types.cjs +1 -0
- package/dist/lib/primitive/date/types.d.ts +205 -0
- package/dist/lib/primitive/date/types.d.ts.map +1 -0
- package/dist/lib/primitive/date/types.js +7 -0
- package/dist/lib/primitive/date/types.mjs +0 -0
- package/dist/lib/primitive/index.cjs +5 -0
- package/dist/lib/primitive/index.d.ts +5 -0
- package/dist/lib/primitive/index.d.ts.map +1 -0
- package/dist/lib/primitive/index.js +4 -0
- package/dist/lib/primitive/index.mjs +5 -0
- package/dist/lib/primitive/string/index.cjs +5 -0
- package/dist/lib/primitive/string/index.d.ts +3 -0
- package/dist/lib/primitive/string/index.d.ts.map +1 -0
- package/dist/lib/primitive/string/index.js +2 -0
- package/dist/lib/primitive/string/index.mjs +5 -0
- package/dist/lib/primitive/string/parser-2.cjs +8 -0
- package/dist/lib/primitive/string/parser-2.d.ts +17 -0
- package/dist/lib/primitive/string/parser-2.d.ts.map +1 -0
- package/dist/lib/primitive/string/parser-2.js +656 -0
- package/dist/lib/primitive/string/parser-2.mjs +8 -0
- package/dist/lib/primitive/string/parser.cjs +3 -0
- package/dist/lib/primitive/string/parser.d.ts +9 -0
- package/dist/lib/primitive/string/parser.d.ts.map +1 -0
- package/dist/lib/primitive/string/parser.js +775 -0
- package/dist/lib/primitive/string/parser.mjs +3 -0
- package/dist/lib/primitive/string/string.cjs +5 -0
- package/dist/lib/primitive/string/string.d.ts +535 -0
- package/dist/lib/primitive/string/string.d.ts.map +1 -0
- package/dist/lib/primitive/string/string.js +1301 -0
- package/dist/lib/primitive/string/string.mjs +5 -0
- package/dist/lib/primitive/string/types.cjs +1 -0
- package/dist/lib/primitive/string/types.d.ts +318 -0
- package/dist/lib/primitive/string/types.d.ts.map +1 -0
- package/dist/lib/primitive/string/types.js +5 -0
- package/dist/lib/primitive/string/types.mjs +0 -0
- package/dist/lib/primitive/types.cjs +1 -0
- package/dist/lib/primitive/types.d.ts +27 -0
- package/dist/lib/primitive/types.d.ts.map +1 -0
- package/dist/lib/primitive/types.js +1 -0
- package/dist/lib/primitive/types.mjs +0 -0
- package/dist/lib/url/index.cjs +1 -0
- package/dist/lib/url/index.d.ts +3 -0
- package/dist/lib/url/index.d.ts.map +1 -0
- package/dist/lib/url/index.js +2 -0
- package/dist/lib/url/index.mjs +1 -0
- package/dist/lib/url/parser.cjs +1 -0
- package/dist/lib/url/parser.d.ts +216 -0
- package/dist/lib/url/parser.d.ts.map +1 -0
- package/dist/lib/url/parser.js +488 -0
- package/dist/lib/url/parser.mjs +1 -0
- package/dist/lib/url/types.cjs +1 -0
- package/dist/lib/url/types.d.ts +18 -0
- package/dist/lib/url/types.d.ts.map +1 -0
- package/dist/lib/url/types.js +1 -0
- package/dist/lib/url/types.mjs +0 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -0
- package/package.json +150 -0
|
@@ -0,0 +1,1301 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* String utilities collection for TypeScript
|
|
3
|
+
* Provides optimized methods for common string operations
|
|
4
|
+
*/
|
|
5
|
+
import { parseJsObjectLiteral } from './parser.js';
|
|
6
|
+
/**
|
|
7
|
+
* Trims whitespace from both ends of a string with high performance
|
|
8
|
+
* @param str - The string to trim
|
|
9
|
+
* @returns Trimmed string
|
|
10
|
+
* @example
|
|
11
|
+
* trim(" hello ") // "hello"
|
|
12
|
+
*/
|
|
13
|
+
export const trim = (str) => {
|
|
14
|
+
// Use native trim for best performance
|
|
15
|
+
return str.trim();
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Trims whitespace from the start of a string
|
|
19
|
+
* @param str - The string to trim
|
|
20
|
+
* @returns Left-trimmed string
|
|
21
|
+
* @example
|
|
22
|
+
* trimStart(" hello") // "hello"
|
|
23
|
+
*/
|
|
24
|
+
export const trimStart = (str) => {
|
|
25
|
+
return str.trimStart();
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Trims whitespace from the end of a string
|
|
29
|
+
* @param str - The string to trim
|
|
30
|
+
* @returns Right-trimmed string
|
|
31
|
+
* @example
|
|
32
|
+
* trimEnd("hello ") // "hello"
|
|
33
|
+
*/
|
|
34
|
+
export const trimEnd = (str) => {
|
|
35
|
+
return str.trimEnd();
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Converts string to camelCase
|
|
39
|
+
* @param str - The string to convert
|
|
40
|
+
* @returns camelCase string
|
|
41
|
+
* @example
|
|
42
|
+
* toCamelCase("hello-world") // "helloWorld"
|
|
43
|
+
*/
|
|
44
|
+
export const toCamelCase = (str) => {
|
|
45
|
+
return str
|
|
46
|
+
.replace(/[^a-zA-Z0-9]+(.)/g, (_, chr) => chr.toUpperCase())
|
|
47
|
+
.replace(/^[A-Z]/, (chr) => chr.toLowerCase());
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Converts string to PascalCase
|
|
51
|
+
* @param str - The string to convert
|
|
52
|
+
* @returns PascalCase string
|
|
53
|
+
* @example
|
|
54
|
+
* toPascalCase("hello-world") // "HelloWorld"
|
|
55
|
+
*/
|
|
56
|
+
export const toPascalCase = (str) => {
|
|
57
|
+
return str
|
|
58
|
+
.replace(/[^a-zA-Z0-9]+(.)/g, (_, chr) => chr.toUpperCase())
|
|
59
|
+
.replace(/^[a-z]/, (chr) => chr.toUpperCase());
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Converts string to kebab-case
|
|
63
|
+
* @param str - The string to convert
|
|
64
|
+
* @returns kebab-case string
|
|
65
|
+
* @example
|
|
66
|
+
* toKebabCase("helloWorld") // "hello-world"
|
|
67
|
+
*/
|
|
68
|
+
export const toKebabCase = (str) => {
|
|
69
|
+
return str
|
|
70
|
+
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
71
|
+
.replace(/[_\s]+/g, '-')
|
|
72
|
+
.toLowerCase();
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Converts string to snake_case
|
|
76
|
+
* @param str - The string to convert
|
|
77
|
+
* @returns snake_case string
|
|
78
|
+
* @example
|
|
79
|
+
* toSnakeCase("helloWorld") // "hello_world"
|
|
80
|
+
*/
|
|
81
|
+
export const toSnakeCase = (str) => {
|
|
82
|
+
return str
|
|
83
|
+
.replace(/([a-z])([A-Z])/g, '$1_$2')
|
|
84
|
+
.replace(/[-\s]+/g, '_')
|
|
85
|
+
.toLowerCase();
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Capitalizes the first letter of a string
|
|
89
|
+
* @param str - The string to capitalize
|
|
90
|
+
* @returns Capitalized string
|
|
91
|
+
* @example
|
|
92
|
+
* capitalize("hello") // "Hello"
|
|
93
|
+
*/
|
|
94
|
+
export const capitalize = (str) => {
|
|
95
|
+
if (!str)
|
|
96
|
+
return str;
|
|
97
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Converts string to title case (each word capitalized)
|
|
101
|
+
* @param str - The string to convert
|
|
102
|
+
* @returns Title case string
|
|
103
|
+
* @example
|
|
104
|
+
* toTitleCase("hello world") // "Hello World"
|
|
105
|
+
*/
|
|
106
|
+
export const toTitleCase = (str) => {
|
|
107
|
+
return str.replace(/\b\w+/g, (word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase());
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* Checks if string starts with a substring (case-sensitive)
|
|
111
|
+
* @param str - The string to check
|
|
112
|
+
* @param search - The substring to search for
|
|
113
|
+
* @param position - Position to start searching (default: 0)
|
|
114
|
+
* @returns True if string starts with search string
|
|
115
|
+
* @example
|
|
116
|
+
* startsWith("hello", "he") // true
|
|
117
|
+
*/
|
|
118
|
+
export const startsWith = (str, search, position = 0) => {
|
|
119
|
+
return str.startsWith(search, position);
|
|
120
|
+
};
|
|
121
|
+
/**
|
|
122
|
+
* Checks if string ends with a substring (case-sensitive)
|
|
123
|
+
* @param str - The string to check
|
|
124
|
+
* @param search - The substring to search for
|
|
125
|
+
* @param length - Length to use (default: str.length)
|
|
126
|
+
* @returns True if string ends with search string
|
|
127
|
+
* @example
|
|
128
|
+
* endsWith("hello", "lo") // true
|
|
129
|
+
*/
|
|
130
|
+
export const endsWith = (str, search, length) => {
|
|
131
|
+
return str.endsWith(search, length);
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* Checks if string contains a substring (case-sensitive)
|
|
135
|
+
* @param str - The string to check
|
|
136
|
+
* @param search - The substring to search for
|
|
137
|
+
* @returns True if string contains search string
|
|
138
|
+
* @example
|
|
139
|
+
* includes("hello", "ell") // true
|
|
140
|
+
*/
|
|
141
|
+
export const includes = (str, search) => {
|
|
142
|
+
return str.includes(search);
|
|
143
|
+
};
|
|
144
|
+
/**
|
|
145
|
+
* Checks if string contains a substring (case-insensitive)
|
|
146
|
+
* @param str - The string to check
|
|
147
|
+
* @param search - The substring to search for
|
|
148
|
+
* @returns True if string contains search string (case-insensitive)
|
|
149
|
+
* @example
|
|
150
|
+
* includesIgnoreCase("Hello", "hello") // true
|
|
151
|
+
*/
|
|
152
|
+
export const includesIgnoreCase = (str, search) => {
|
|
153
|
+
return str.toLowerCase().includes(search.toLowerCase());
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* Replaces all occurrences of a substring (case-sensitive)
|
|
157
|
+
* @param str - The string to process
|
|
158
|
+
* @param search - The substring to replace
|
|
159
|
+
* @param replacement - The replacement string
|
|
160
|
+
* @returns String with all occurrences replaced
|
|
161
|
+
* @example
|
|
162
|
+
* replaceAll("hello world", "l", "x") // "hexxo worxd"
|
|
163
|
+
*/
|
|
164
|
+
export const replaceAll = (str, search, replacement) => {
|
|
165
|
+
// Use native replaceAll if available, otherwise use regex with escape
|
|
166
|
+
if (typeof str.replaceAll === 'function') {
|
|
167
|
+
return str.replaceAll(search, replacement);
|
|
168
|
+
}
|
|
169
|
+
return str.replace(new RegExp(escapeRegExp(search), 'g'), replacement);
|
|
170
|
+
};
|
|
171
|
+
/**
|
|
172
|
+
* Replaces all occurrences of a substring (case-insensitive)
|
|
173
|
+
* @param str - The string to process
|
|
174
|
+
* @param search - The substring to replace
|
|
175
|
+
* @param replacement - The replacement string
|
|
176
|
+
* @returns String with all occurrences replaced (case-insensitive)
|
|
177
|
+
* @example
|
|
178
|
+
* replaceAllIgnoreCase("Hello World", "hello", "Hi") // "Hi World"
|
|
179
|
+
*/
|
|
180
|
+
export const replaceAllIgnoreCase = (str, search, replacement) => {
|
|
181
|
+
return str.replace(new RegExp(escapeRegExp(search), 'gi'), replacement);
|
|
182
|
+
};
|
|
183
|
+
/**
|
|
184
|
+
* Escapes regex special characters in a string
|
|
185
|
+
* @param str - The string to escape
|
|
186
|
+
* @returns Escaped string safe for use in regex
|
|
187
|
+
* @example
|
|
188
|
+
* escapeRegExp(".*+?^${}()|[]\\") // "\\.\\*\\+\\?\\^\\$\\{\\}\\(\\)\\|\\[\\]\\\\"
|
|
189
|
+
*/
|
|
190
|
+
export const escapeRegExp = (str) => {
|
|
191
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
192
|
+
};
|
|
193
|
+
/**
|
|
194
|
+
* Escapes HTML special characters
|
|
195
|
+
* @param str - The string to escape
|
|
196
|
+
* @returns HTML-escaped string
|
|
197
|
+
* @example
|
|
198
|
+
* escapeHtml("<div>") // "<div>"
|
|
199
|
+
*/
|
|
200
|
+
export const escapeHtml = (str) => {
|
|
201
|
+
const htmlEntities = {
|
|
202
|
+
'&': '&',
|
|
203
|
+
'<': '<',
|
|
204
|
+
'>': '>',
|
|
205
|
+
'"': '"',
|
|
206
|
+
"'": ''',
|
|
207
|
+
'/': '/',
|
|
208
|
+
};
|
|
209
|
+
return str.replace(/[&<>"'/]/g, (char) => htmlEntities[char] || char);
|
|
210
|
+
};
|
|
211
|
+
/**
|
|
212
|
+
* Unescapes HTML entities
|
|
213
|
+
* @param str - The string to unescape
|
|
214
|
+
* @returns Unescaped string
|
|
215
|
+
* @example
|
|
216
|
+
* unescapeHtml("<div>") // "<div>"
|
|
217
|
+
*/
|
|
218
|
+
export const unescapeHtml = (str) => {
|
|
219
|
+
const htmlEntities = {
|
|
220
|
+
'&': '&',
|
|
221
|
+
'<': '<',
|
|
222
|
+
'>': '>',
|
|
223
|
+
'"': '"',
|
|
224
|
+
''': "'",
|
|
225
|
+
'/': '/',
|
|
226
|
+
};
|
|
227
|
+
return str.replace(/&(?:amp|lt|gt|quot|#39|#x2F);/g, (entity) => htmlEntities[entity] || entity);
|
|
228
|
+
};
|
|
229
|
+
/**
|
|
230
|
+
* Truncates string to specified length with ellipsis
|
|
231
|
+
* @param str - The string to truncate
|
|
232
|
+
* @param length - Maximum length
|
|
233
|
+
* @param ellipsis - Ellipsis string (default: "...")
|
|
234
|
+
* @returns Truncated string
|
|
235
|
+
* @example
|
|
236
|
+
* truncate("Hello world", 8) // "Hello..."
|
|
237
|
+
*/
|
|
238
|
+
export const truncate = (str, length, ellipsis = '...') => {
|
|
239
|
+
if (str.length <= length)
|
|
240
|
+
return str;
|
|
241
|
+
return str.slice(0, length - ellipsis.length) + ellipsis;
|
|
242
|
+
};
|
|
243
|
+
/**
|
|
244
|
+
* Pads string to specified length with character
|
|
245
|
+
* @param str - The string to pad
|
|
246
|
+
* @param length - Target length
|
|
247
|
+
* @param padChar - Character to pad with (default: " ")
|
|
248
|
+
* @param side - Which side to pad: "start", "end", or "both" (default: "start")
|
|
249
|
+
* @returns Padded string
|
|
250
|
+
* @example
|
|
251
|
+
* pad("hello", 10, "*", "both") // "**hello***"
|
|
252
|
+
*/
|
|
253
|
+
export const pad = (str, length, padChar = ' ', side = 'start') => {
|
|
254
|
+
const paddingNeeded = length - str.length;
|
|
255
|
+
if (paddingNeeded <= 0)
|
|
256
|
+
return str;
|
|
257
|
+
const padStr = padChar.repeat(paddingNeeded);
|
|
258
|
+
switch (side) {
|
|
259
|
+
case 'start':
|
|
260
|
+
return padStr + str;
|
|
261
|
+
case 'end':
|
|
262
|
+
return str + padStr;
|
|
263
|
+
case 'both':
|
|
264
|
+
const leftPad = Math.floor(paddingNeeded / 2);
|
|
265
|
+
const rightPad = paddingNeeded - leftPad;
|
|
266
|
+
return padChar.repeat(leftPad) + str + padChar.repeat(rightPad);
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
/**
|
|
270
|
+
* Checks if string is empty or whitespace only
|
|
271
|
+
* @param str - The string to check
|
|
272
|
+
* @returns True if string is empty or whitespace only
|
|
273
|
+
* @example
|
|
274
|
+
* isEmpty(" ") // true
|
|
275
|
+
* isEmpty("hello") // false
|
|
276
|
+
*/
|
|
277
|
+
export const isEmpty = (str) => {
|
|
278
|
+
return str.trim().length === 0;
|
|
279
|
+
};
|
|
280
|
+
/**
|
|
281
|
+
* Checks if string is a valid email address
|
|
282
|
+
* @param email - The email string to validate
|
|
283
|
+
* @returns True if string is a valid email
|
|
284
|
+
* @example
|
|
285
|
+
* isValidEmail("test@example.com") // true
|
|
286
|
+
*/
|
|
287
|
+
export const isValidEmail = (email) => {
|
|
288
|
+
// RFC 5322 compliant regex (simplified but effective)
|
|
289
|
+
const emailRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
|
|
290
|
+
return emailRegex.test(email.trim());
|
|
291
|
+
};
|
|
292
|
+
/**
|
|
293
|
+
* Checks if string is a valid URL
|
|
294
|
+
* @param url - The URL string to validate
|
|
295
|
+
* @returns True if string is a valid URL
|
|
296
|
+
* @example
|
|
297
|
+
* isValidUrl("https://example.com") // true
|
|
298
|
+
*/
|
|
299
|
+
export const isValidUrl = (url) => {
|
|
300
|
+
try {
|
|
301
|
+
new URL(url);
|
|
302
|
+
return true;
|
|
303
|
+
}
|
|
304
|
+
catch {
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
/**
|
|
309
|
+
* Extracts domain from URL
|
|
310
|
+
* @param url - The URL string
|
|
311
|
+
* @returns Domain name or empty string
|
|
312
|
+
* @example
|
|
313
|
+
* extractDomain("https://www.example.com/path") // "example.com"
|
|
314
|
+
*/
|
|
315
|
+
export const extractDomain = (url) => {
|
|
316
|
+
try {
|
|
317
|
+
const urlObj = new URL(url);
|
|
318
|
+
return urlObj.hostname.replace(/^www\./, '');
|
|
319
|
+
}
|
|
320
|
+
catch {
|
|
321
|
+
return '';
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
/**
|
|
325
|
+
* Generates a random string of specified length
|
|
326
|
+
* @param length - Length of random string
|
|
327
|
+
* @param charset - Character set to use (default: alphanumeric)
|
|
328
|
+
* @returns Random string
|
|
329
|
+
* @example
|
|
330
|
+
* randomString(10) // "aB3xYz78pQ"
|
|
331
|
+
*/
|
|
332
|
+
export const randomString = (length = 10, charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') => {
|
|
333
|
+
let result = '';
|
|
334
|
+
const charsetLength = charset.length;
|
|
335
|
+
for (let i = 0; i < length; i++) {
|
|
336
|
+
result += charset.charAt(Math.floor(Math.random() * charsetLength));
|
|
337
|
+
}
|
|
338
|
+
return result;
|
|
339
|
+
};
|
|
340
|
+
/**
|
|
341
|
+
* Counts occurrences of substring in string
|
|
342
|
+
* @param str - The string to search
|
|
343
|
+
* @param search - The substring to count
|
|
344
|
+
* @param caseSensitive - Whether search is case-sensitive (default: true)
|
|
345
|
+
* @returns Number of occurrences
|
|
346
|
+
* @example
|
|
347
|
+
* countOccurrences("hello hello", "lo") // 2
|
|
348
|
+
*/
|
|
349
|
+
export const countOccurrences = (str, search, caseSensitive = true) => {
|
|
350
|
+
if (!search)
|
|
351
|
+
return 0;
|
|
352
|
+
const source = caseSensitive ? str : str.toLowerCase();
|
|
353
|
+
const target = caseSensitive ? search : search.toLowerCase();
|
|
354
|
+
let count = 0;
|
|
355
|
+
let position = 0;
|
|
356
|
+
while ((position = source.indexOf(target, position)) !== -1) {
|
|
357
|
+
count++;
|
|
358
|
+
position += target.length;
|
|
359
|
+
}
|
|
360
|
+
return count;
|
|
361
|
+
};
|
|
362
|
+
/**
|
|
363
|
+
* Removes all whitespace from string
|
|
364
|
+
* @param str - The string to process
|
|
365
|
+
* @returns String without whitespace
|
|
366
|
+
* @example
|
|
367
|
+
* removeWhitespace("hello world") // "helloworld"
|
|
368
|
+
*/
|
|
369
|
+
export const removeWhitespace = (str) => {
|
|
370
|
+
return str.replace(/\s+/g, '');
|
|
371
|
+
};
|
|
372
|
+
/**
|
|
373
|
+
* Reverses a string
|
|
374
|
+
* @param str - The string to reverse
|
|
375
|
+
* @returns Reversed string
|
|
376
|
+
* @example
|
|
377
|
+
* reverse("hello") // "olleh"
|
|
378
|
+
*/
|
|
379
|
+
export const reverse = (str) => {
|
|
380
|
+
return str.split('').reverse().join('');
|
|
381
|
+
};
|
|
382
|
+
/**
|
|
383
|
+
* Checks if string is palindrome (reads same forwards and backwards)
|
|
384
|
+
* @param str - The string to check
|
|
385
|
+
* @param caseSensitive - Whether comparison is case-sensitive (default: false)
|
|
386
|
+
* @returns True if string is palindrome
|
|
387
|
+
* @example
|
|
388
|
+
* isPalindrome("racecar") // true
|
|
389
|
+
*/
|
|
390
|
+
export const isPalindrome = (str, caseSensitive = false) => {
|
|
391
|
+
const normalized = caseSensitive ? str : str.toLowerCase();
|
|
392
|
+
const cleaned = normalized.replace(/[^a-z0-9]/gi, '');
|
|
393
|
+
return cleaned === reverse(cleaned);
|
|
394
|
+
};
|
|
395
|
+
/**
|
|
396
|
+
* Generates slug from string (URL-friendly)
|
|
397
|
+
* @param str - The string to convert
|
|
398
|
+
* @returns URL-friendly slug
|
|
399
|
+
* @example
|
|
400
|
+
* slugify("Hello World!") // "hello-world"
|
|
401
|
+
*/
|
|
402
|
+
export const slugify = (str) => {
|
|
403
|
+
return str
|
|
404
|
+
.toLowerCase()
|
|
405
|
+
.trim()
|
|
406
|
+
.replace(/[^\w\s-]/g, '')
|
|
407
|
+
.replace(/[\s_-]+/g, '-')
|
|
408
|
+
.replace(/^-+|-+$/g, '');
|
|
409
|
+
};
|
|
410
|
+
/**
|
|
411
|
+
* Masks part of a string (e.g., for hiding sensitive data)
|
|
412
|
+
* @param str - The string to mask
|
|
413
|
+
* @param visibleStart - Number of characters to keep at start
|
|
414
|
+
* @param visibleEnd - Number of characters to keep at end
|
|
415
|
+
* @param maskChar - Character to use for masking (default: "*")
|
|
416
|
+
* @returns Masked string
|
|
417
|
+
* @example
|
|
418
|
+
* mask("1234567890", 3, 2) // "123*****90"
|
|
419
|
+
*/
|
|
420
|
+
export const mask = (str, visibleStart = 0, visibleEnd = 0, maskChar = '*') => {
|
|
421
|
+
if (visibleStart + visibleEnd >= str.length) {
|
|
422
|
+
return str;
|
|
423
|
+
}
|
|
424
|
+
const start = str.slice(0, visibleStart);
|
|
425
|
+
const end = visibleEnd > 0 ? str.slice(-visibleEnd) : '';
|
|
426
|
+
const middle = maskChar.repeat(str.length - visibleStart - visibleEnd);
|
|
427
|
+
return start + middle + end;
|
|
428
|
+
};
|
|
429
|
+
/**
|
|
430
|
+
* Generates initials from name
|
|
431
|
+
* @param name - The full name
|
|
432
|
+
* @param maxInitials - Maximum number of initials (default: 2)
|
|
433
|
+
* @returns Initials string
|
|
434
|
+
* @example
|
|
435
|
+
* getInitials("John Doe") // "JD"
|
|
436
|
+
*/
|
|
437
|
+
export const getInitials = (name, maxInitials = 2) => {
|
|
438
|
+
return name
|
|
439
|
+
.split(/\s+/)
|
|
440
|
+
.slice(0, maxInitials)
|
|
441
|
+
.map((word) => word.charAt(0).toUpperCase())
|
|
442
|
+
.join('');
|
|
443
|
+
};
|
|
444
|
+
/**
|
|
445
|
+
* Normalizes line endings to LF (Unix-style)
|
|
446
|
+
* @param str - The string to normalize
|
|
447
|
+
* @returns String with normalized line endings
|
|
448
|
+
* @example
|
|
449
|
+
* normalizeLineEndings("hello\r\nworld") // "hello\nworld"
|
|
450
|
+
*/
|
|
451
|
+
export const normalizeLineEndings = (str) => {
|
|
452
|
+
return str.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
453
|
+
};
|
|
454
|
+
/**
|
|
455
|
+
* Wraps text to specified line length
|
|
456
|
+
* @param text - The text to wrap
|
|
457
|
+
* @param lineLength - Maximum line length
|
|
458
|
+
* @returns Array of wrapped lines
|
|
459
|
+
* @example
|
|
460
|
+
* wordWrap("Hello world, this is a test.", 10)
|
|
461
|
+
* // ["Hello", "world, this", "is a test."]
|
|
462
|
+
*/
|
|
463
|
+
export const wordWrap = (text, lineLength = 80) => {
|
|
464
|
+
const words = text.split(' ');
|
|
465
|
+
const lines = [];
|
|
466
|
+
let currentLine = '';
|
|
467
|
+
for (const word of words) {
|
|
468
|
+
const len = currentLine.length + word.length + 1;
|
|
469
|
+
if (currentLine.length + word.length <= lineLength) {
|
|
470
|
+
currentLine += (currentLine ? ' ' : '') + word;
|
|
471
|
+
}
|
|
472
|
+
else {
|
|
473
|
+
if (currentLine) {
|
|
474
|
+
lines.push(currentLine);
|
|
475
|
+
}
|
|
476
|
+
currentLine = word;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
if (currentLine) {
|
|
480
|
+
lines.push(currentLine);
|
|
481
|
+
}
|
|
482
|
+
return lines;
|
|
483
|
+
};
|
|
484
|
+
/**
|
|
485
|
+
* Type guard for string values
|
|
486
|
+
* @param value - Value to check
|
|
487
|
+
* @returns True if value is a string
|
|
488
|
+
*/
|
|
489
|
+
export const isString = (value) => {
|
|
490
|
+
return typeof value === 'string';
|
|
491
|
+
};
|
|
492
|
+
/**
|
|
493
|
+
* Safely converts any value to string
|
|
494
|
+
* @param value - Value to convert
|
|
495
|
+
* @param fallback - Fallback value if conversion fails (default: "")
|
|
496
|
+
* @returns String representation or fallback
|
|
497
|
+
*/
|
|
498
|
+
export const toString = (value, fallback) => {
|
|
499
|
+
if (value == null) {
|
|
500
|
+
return fallback !== undefined ? fallback : '';
|
|
501
|
+
}
|
|
502
|
+
const v = value;
|
|
503
|
+
if (Array.isArray(v)) {
|
|
504
|
+
if (v.length === 0) {
|
|
505
|
+
return fallback !== undefined ? fallback : '';
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
if (typeof v === 'object' && Object.keys(v).length === 0) {
|
|
510
|
+
return fallback !== undefined ? fallback : '';
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
if (typeof value === 'string') {
|
|
514
|
+
return value;
|
|
515
|
+
}
|
|
516
|
+
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
517
|
+
return String(value);
|
|
518
|
+
}
|
|
519
|
+
try {
|
|
520
|
+
return String(value);
|
|
521
|
+
}
|
|
522
|
+
catch {
|
|
523
|
+
return fallback !== undefined ? fallback : '';
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
/**
|
|
527
|
+
* Escapes regex special characters in a string
|
|
528
|
+
* @param str - The string to escape
|
|
529
|
+
* @param escape - Whether to escape the string (default: true)
|
|
530
|
+
* @returns Escaped string or original if escape is false or string is in exclude list
|
|
531
|
+
* @example
|
|
532
|
+
* escapeString("test[123]") // "test\\[123\\]"
|
|
533
|
+
* escapeString(" ", true) // " " (not escaped due to exclude list)
|
|
534
|
+
*/
|
|
535
|
+
export const escapeString = (str, escape = true) => {
|
|
536
|
+
// Fast path: return early if escaping is disabled
|
|
537
|
+
if (!escape) {
|
|
538
|
+
return str;
|
|
539
|
+
}
|
|
540
|
+
// Use a Set for O(1) lookup instead of O(n) array search
|
|
541
|
+
const exclude = new Set([' ', '\xa0', '\xa0\xa0', '_space_']);
|
|
542
|
+
// Check if string should be excluded from escaping
|
|
543
|
+
if (exclude.has(str)) {
|
|
544
|
+
return str;
|
|
545
|
+
}
|
|
546
|
+
// Optimized regex: escape only regex special characters
|
|
547
|
+
// Note: Only characters that have special meaning in regex need escaping
|
|
548
|
+
const regexSpecialChars = /[.*+?^${}()|[\]\\]/g;
|
|
549
|
+
// Use replace with a function to handle the replacement
|
|
550
|
+
return str.replace(regexSpecialChars, '\\$&');
|
|
551
|
+
};
|
|
552
|
+
/**
|
|
553
|
+
* Replaces a substring by index position
|
|
554
|
+
* @param {string} original - Original string
|
|
555
|
+
* @param {number} startIndex - Start index (inclusive)
|
|
556
|
+
* @param {number} endIndex - End index (exclusive)
|
|
557
|
+
* @param {string} replacement - Replacement string
|
|
558
|
+
* @returns {string} Modified string
|
|
559
|
+
*/
|
|
560
|
+
function replaceStringByIndex(original, startIndex, endIndex, replacement) {
|
|
561
|
+
if (startIndex < 0 || startIndex > original.length) {
|
|
562
|
+
throw new Error('Start index out of bounds');
|
|
563
|
+
}
|
|
564
|
+
if (endIndex < startIndex || endIndex > original.length) {
|
|
565
|
+
throw new Error('End index out of bounds');
|
|
566
|
+
}
|
|
567
|
+
const before = original.substring(0, startIndex);
|
|
568
|
+
const after = original.substring(endIndex);
|
|
569
|
+
return before + replacement + after;
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Replaces a substring at the beginning or end of a string
|
|
573
|
+
* @param originalString - The original string to modify
|
|
574
|
+
* @param valueToReplace - The substring to replace
|
|
575
|
+
* @param position - Position to replace: "start", "end", or "both" (default: "start")
|
|
576
|
+
* @param newValue - The replacement value (default: "")
|
|
577
|
+
* @returns Modified string or original if replacement not applicable
|
|
578
|
+
* @example
|
|
579
|
+
* replaceString("hello world", "hello", "start") // " world"
|
|
580
|
+
* replaceString("hello world", "world", "end") // "hello "
|
|
581
|
+
* replaceString("test.test", ".", "both", "-") // "test-test"
|
|
582
|
+
*/
|
|
583
|
+
export const replaceString = (originalString, valueToReplace, position = 'start', newValue = '') => {
|
|
584
|
+
// Validate inputs
|
|
585
|
+
if (!originalString ||
|
|
586
|
+
!valueToReplace ||
|
|
587
|
+
valueToReplace.length === 0 ||
|
|
588
|
+
originalString.length < valueToReplace.length) {
|
|
589
|
+
return originalString;
|
|
590
|
+
}
|
|
591
|
+
// Handle both positions case
|
|
592
|
+
if (position === 'both') {
|
|
593
|
+
let result = originalString;
|
|
594
|
+
// Replace from start if applicable
|
|
595
|
+
if (result.startsWith(valueToReplace)) {
|
|
596
|
+
result = replaceStringByIndex(result, 0, valueToReplace.length, newValue);
|
|
597
|
+
// result = result.substring(valueToReplace.length);
|
|
598
|
+
}
|
|
599
|
+
// Replace from end if applicable
|
|
600
|
+
if (result.endsWith(valueToReplace)) {
|
|
601
|
+
result = replaceStringByIndex(result, result.length - valueToReplace.length, result.length, newValue);
|
|
602
|
+
// result = result.substring(0, result.length - valueToReplace.length);
|
|
603
|
+
}
|
|
604
|
+
// return result.length < originalString.length
|
|
605
|
+
// ? newValue + result + newValue
|
|
606
|
+
// : result;
|
|
607
|
+
return result;
|
|
608
|
+
}
|
|
609
|
+
// Handle start or end position
|
|
610
|
+
const shouldReplace = (position === 'start' && originalString.startsWith(valueToReplace)) ||
|
|
611
|
+
(position === 'end' && originalString.endsWith(valueToReplace));
|
|
612
|
+
if (!shouldReplace) {
|
|
613
|
+
return originalString;
|
|
614
|
+
}
|
|
615
|
+
if (position === 'start') {
|
|
616
|
+
return newValue + originalString.substring(valueToReplace.length);
|
|
617
|
+
}
|
|
618
|
+
else {
|
|
619
|
+
return (originalString.substring(0, originalString.length - valueToReplace.length) + newValue);
|
|
620
|
+
}
|
|
621
|
+
};
|
|
622
|
+
/**
|
|
623
|
+
* Alternative version with regex for more complex patterns
|
|
624
|
+
* Replaces a pattern at start/end using regex for more control
|
|
625
|
+
* @param originalString - The original string to modify
|
|
626
|
+
* @param pattern - Regex pattern or string to match
|
|
627
|
+
* @param position - Position to replace: "start", "end", or "both"
|
|
628
|
+
* @param newValue - The replacement value
|
|
629
|
+
* @param useRegex - Whether pattern is a regex pattern
|
|
630
|
+
* @returns Modified string
|
|
631
|
+
*/
|
|
632
|
+
export const replaceStringPattern = (originalString, options) => {
|
|
633
|
+
let { pattern, position, newValue, useRegex = false, flags } = { ...options };
|
|
634
|
+
if (!originalString) {
|
|
635
|
+
return originalString;
|
|
636
|
+
}
|
|
637
|
+
if (!newValue) {
|
|
638
|
+
newValue = '';
|
|
639
|
+
}
|
|
640
|
+
let regex;
|
|
641
|
+
const isStart = typeof position === 'undefined' || position === 'start';
|
|
642
|
+
if (useRegex && pattern instanceof RegExp) {
|
|
643
|
+
regex = pattern;
|
|
644
|
+
const regex_string = regex.source;
|
|
645
|
+
if (position) {
|
|
646
|
+
if (position === 'start') {
|
|
647
|
+
pattern = `^${regex_string}`;
|
|
648
|
+
}
|
|
649
|
+
else if (position === 'end') {
|
|
650
|
+
pattern = `${regex_string}$`;
|
|
651
|
+
}
|
|
652
|
+
else {
|
|
653
|
+
pattern = `^${regex_string}|${regex_string}$`;
|
|
654
|
+
}
|
|
655
|
+
pattern = new RegExp(pattern, flags);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
else if (useRegex) {
|
|
659
|
+
if (position) {
|
|
660
|
+
if (position === 'start') {
|
|
661
|
+
pattern = `^${pattern}`;
|
|
662
|
+
}
|
|
663
|
+
else if (position === 'end') {
|
|
664
|
+
pattern = `${pattern}$`;
|
|
665
|
+
}
|
|
666
|
+
else if (position === 'both') {
|
|
667
|
+
pattern = `^${pattern}|${pattern}$`;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
regex = new RegExp(pattern, flags);
|
|
671
|
+
}
|
|
672
|
+
else {
|
|
673
|
+
const escapedPattern = pattern;
|
|
674
|
+
regex = new RegExp(isStart
|
|
675
|
+
? `^${escapedPattern}`
|
|
676
|
+
: position === 'end'
|
|
677
|
+
? `${escapedPattern}$`
|
|
678
|
+
: position === 'both'
|
|
679
|
+
? `^${escapedPattern}|${escapedPattern}$`
|
|
680
|
+
: `${escapedPattern}`, flags || 'g');
|
|
681
|
+
}
|
|
682
|
+
return originalString.replace(regex, newValue);
|
|
683
|
+
};
|
|
684
|
+
/**
|
|
685
|
+
* Type for the parsed object in stringToObjectArray
|
|
686
|
+
*/
|
|
687
|
+
// type ParsedObject = Record<string, any> | string | number | boolean | null;
|
|
688
|
+
// /**
|
|
689
|
+
// * Parses a string or string array into an array of objects or strings
|
|
690
|
+
// * Supports JSON, delimited strings, and various formats
|
|
691
|
+
// * @param input - The input string or array to parse
|
|
692
|
+
// * @param options - Configuration options for parsing
|
|
693
|
+
// * @returns Array of parsed objects or strings
|
|
694
|
+
// * @example
|
|
695
|
+
// * // Returns: [{value: "apple", label: "apple"}, {value: "banana", label: "banana"}]
|
|
696
|
+
// * stringToObjectArray("apple|banana", {valueKey: "value", labelKey: "label"})
|
|
697
|
+
// *
|
|
698
|
+
// * // Returns: ["apple", "banana"]
|
|
699
|
+
// * stringToObjectArray("apple,banana", {delimiters: [","]})
|
|
700
|
+
// *
|
|
701
|
+
// * // Returns: parsed JSON array
|
|
702
|
+
// * stringToObjectArray('[{"id": 1, "name": "test"}]')
|
|
703
|
+
// */
|
|
704
|
+
// export const stringToObjectArray = (
|
|
705
|
+
// input: string | string[] | any[],
|
|
706
|
+
// options: StringToObjectArrayOptions = {}
|
|
707
|
+
// ): ParsedObject[] => {
|
|
708
|
+
// const {
|
|
709
|
+
// delimiters = ['|'],
|
|
710
|
+
// valueKey = 'value',
|
|
711
|
+
// labelKey = 'label',
|
|
712
|
+
// defaultObject = {},
|
|
713
|
+
// trimItems = true,
|
|
714
|
+
// filterEmpty = true,
|
|
715
|
+
// } = options;
|
|
716
|
+
// // Helper function to create object from string
|
|
717
|
+
// const createObject = (item: string): Record<string, any> => {
|
|
718
|
+
// const base = { ...defaultObject };
|
|
719
|
+
// const trimmed = trimItems ? item.trim() : item;
|
|
720
|
+
// // Only add value/label if they don't already exist in defaultObject
|
|
721
|
+
// if (!(valueKey in base)) {
|
|
722
|
+
// base[valueKey] = trimmed;
|
|
723
|
+
// }
|
|
724
|
+
// if (!(labelKey in base) && labelKey !== valueKey) {
|
|
725
|
+
// base[labelKey] = trimmed;
|
|
726
|
+
// }
|
|
727
|
+
// return base;
|
|
728
|
+
// };
|
|
729
|
+
// // Handle array input
|
|
730
|
+
// if (Array.isArray(input)) {
|
|
731
|
+
// return input
|
|
732
|
+
// .map((item): ParsedObject | null => {
|
|
733
|
+
// if (item == null) return null;
|
|
734
|
+
// if (typeof item === 'string') {
|
|
735
|
+
// return valueKey ? createObject(item) : item;
|
|
736
|
+
// }
|
|
737
|
+
// if (typeof item === 'object') {
|
|
738
|
+
// return item;
|
|
739
|
+
// }
|
|
740
|
+
// // Convert other types to string
|
|
741
|
+
// return valueKey ? createObject(String(item)) : String(item);
|
|
742
|
+
// })
|
|
743
|
+
// .filter((item): item is ParsedObject => {
|
|
744
|
+
// if (item == null) return false;
|
|
745
|
+
// if (filterEmpty && typeof item === 'string' && item.trim() === '')
|
|
746
|
+
// return false;
|
|
747
|
+
// if (
|
|
748
|
+
// filterEmpty &&
|
|
749
|
+
// typeof item === 'object' &&
|
|
750
|
+
// valueKey in item &&
|
|
751
|
+
// item[valueKey]?.trim() === ''
|
|
752
|
+
// )
|
|
753
|
+
// return false;
|
|
754
|
+
// return true;
|
|
755
|
+
// });
|
|
756
|
+
// }
|
|
757
|
+
// // Handle string input
|
|
758
|
+
// if (typeof input !== 'string') {
|
|
759
|
+
// return [];
|
|
760
|
+
// }
|
|
761
|
+
// const str = input.trim();
|
|
762
|
+
// // Return empty array for empty string
|
|
763
|
+
// if (str === '') {
|
|
764
|
+
// return [];
|
|
765
|
+
// }
|
|
766
|
+
// // Try to parse as JSON first
|
|
767
|
+
// if (
|
|
768
|
+
// (str.startsWith('[') && str.endsWith(']')) ||
|
|
769
|
+
// (str.startsWith('{') && str.endsWith('}'))
|
|
770
|
+
// ) {
|
|
771
|
+
// try {
|
|
772
|
+
// const parsed = JSON.parse(str);
|
|
773
|
+
// if (Array.isArray(parsed)) {
|
|
774
|
+
// return parsed.map((item): ParsedObject => {
|
|
775
|
+
// if (typeof item === 'string' && valueKey) {
|
|
776
|
+
// return createObject(item);
|
|
777
|
+
// }
|
|
778
|
+
// if (typeof item === 'object' && item !== null) {
|
|
779
|
+
// return { ...defaultObject, ...item };
|
|
780
|
+
// }
|
|
781
|
+
// return valueKey ? createObject(String(item)) : String(item);
|
|
782
|
+
// });
|
|
783
|
+
// }
|
|
784
|
+
// // Single object
|
|
785
|
+
// return [{ ...defaultObject, ...parsed }];
|
|
786
|
+
// } catch {
|
|
787
|
+
// // Not valid JSON, continue with other methods
|
|
788
|
+
// }
|
|
789
|
+
// }
|
|
790
|
+
// // Try to parse as array literal (handles cases like "[a, b, c]")
|
|
791
|
+
// const literalResult = parseArrayLiteral(str);
|
|
792
|
+
// if (Array.isArray(literalResult)) {
|
|
793
|
+
// return literalResult.map((item) =>
|
|
794
|
+
// valueKey ? createObject(String(item)) : String(item)
|
|
795
|
+
// );
|
|
796
|
+
// }
|
|
797
|
+
// // Try each delimiter
|
|
798
|
+
// for (const delimiter of delimiters) {
|
|
799
|
+
// if (str.includes(delimiter)) {
|
|
800
|
+
// const parts = str.split(delimiter);
|
|
801
|
+
// const items = trimItems ? parts.map((part) => part.trim()) : parts;
|
|
802
|
+
// return items
|
|
803
|
+
// .filter((item) => !filterEmpty || item !== '')
|
|
804
|
+
// .map((item) => (valueKey ? createObject(item) : item));
|
|
805
|
+
// }
|
|
806
|
+
// }
|
|
807
|
+
// // Single value
|
|
808
|
+
// return valueKey ? [createObject(str)] : [str];
|
|
809
|
+
// };
|
|
810
|
+
/**
|
|
811
|
+
* Parses a string representation of an array literal
|
|
812
|
+
* Supports formats like: [a, b, c], ["a", "b"], ['a', 'b']
|
|
813
|
+
* @param str - The string to parse
|
|
814
|
+
* @returns Array of strings if successful, original string otherwise
|
|
815
|
+
* @example
|
|
816
|
+
* parseArrayLiteral("[apple, banana, cherry]") // ["apple", "banana", "cherry"]
|
|
817
|
+
* parseArrayLiteral('["apple", "banana"]') // ["apple", "banana"]
|
|
818
|
+
*/
|
|
819
|
+
export const parseArrayLiteral = (str) => {
|
|
820
|
+
// Early return if not an array literal
|
|
821
|
+
if (!str.startsWith('[') || !str.endsWith(']')) {
|
|
822
|
+
return str;
|
|
823
|
+
}
|
|
824
|
+
const innerStr = str.slice(1, -1).trim();
|
|
825
|
+
// Handle empty array
|
|
826
|
+
if (innerStr === '') {
|
|
827
|
+
return [];
|
|
828
|
+
}
|
|
829
|
+
// Try JSON parse first for quoted strings
|
|
830
|
+
try {
|
|
831
|
+
// Wrap in brackets and parse as JSON
|
|
832
|
+
const jsonStr = `[${innerStr}]`;
|
|
833
|
+
const parsed = JSON.parse(jsonStr);
|
|
834
|
+
if (Array.isArray(parsed)) {
|
|
835
|
+
// return parsed.map(String);
|
|
836
|
+
return parsed;
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
catch {
|
|
840
|
+
// Not valid JSON, continue with manual parsing
|
|
841
|
+
}
|
|
842
|
+
// Manual parsing for unquoted strings
|
|
843
|
+
const items = [];
|
|
844
|
+
let current = '';
|
|
845
|
+
let inQuotes = false;
|
|
846
|
+
let quoteChar = '';
|
|
847
|
+
let escaped = false;
|
|
848
|
+
let depth = 0; // Handle nested brackets
|
|
849
|
+
for (let i = 0; i < innerStr.length; i++) {
|
|
850
|
+
const char = innerStr[i];
|
|
851
|
+
// Handle escaping
|
|
852
|
+
if (escaped) {
|
|
853
|
+
current += char;
|
|
854
|
+
escaped = false;
|
|
855
|
+
continue;
|
|
856
|
+
}
|
|
857
|
+
// Check for escape character
|
|
858
|
+
if (char === '\\' && inQuotes) {
|
|
859
|
+
escaped = true;
|
|
860
|
+
continue;
|
|
861
|
+
}
|
|
862
|
+
// Handle quotes
|
|
863
|
+
if ((char === '"' || char === "'") && !inQuotes) {
|
|
864
|
+
inQuotes = true;
|
|
865
|
+
quoteChar = char;
|
|
866
|
+
}
|
|
867
|
+
else if (char === quoteChar && inQuotes) {
|
|
868
|
+
inQuotes = false;
|
|
869
|
+
quoteChar = '';
|
|
870
|
+
}
|
|
871
|
+
// Handle nested brackets
|
|
872
|
+
else if (char === '[' && !inQuotes) {
|
|
873
|
+
depth++;
|
|
874
|
+
current += char;
|
|
875
|
+
}
|
|
876
|
+
else if (char === ']' && !inQuotes) {
|
|
877
|
+
depth--;
|
|
878
|
+
current += char;
|
|
879
|
+
}
|
|
880
|
+
// Handle comma as delimiter (only when not in quotes and not in nested brackets)
|
|
881
|
+
else if (char === ',' && !inQuotes && depth === 0) {
|
|
882
|
+
items.push(current.trim());
|
|
883
|
+
current = '';
|
|
884
|
+
}
|
|
885
|
+
else {
|
|
886
|
+
current += char;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
// Add the last item
|
|
890
|
+
if (current.trim()) {
|
|
891
|
+
items.push(current.trim());
|
|
892
|
+
}
|
|
893
|
+
// Clean up items: remove surrounding quotes
|
|
894
|
+
return items.map((item) => {
|
|
895
|
+
const trimmed = item.trim();
|
|
896
|
+
// Remove surrounding quotes if present
|
|
897
|
+
if ((trimmed.startsWith('"') && trimmed.endsWith('"')) ||
|
|
898
|
+
(trimmed.startsWith("'") && trimmed.endsWith("'"))) {
|
|
899
|
+
return trimmed.slice(1, -1);
|
|
900
|
+
}
|
|
901
|
+
return trimmed;
|
|
902
|
+
});
|
|
903
|
+
};
|
|
904
|
+
/**
|
|
905
|
+
* Enhanced version that handles both array literals and object literals
|
|
906
|
+
* @param str - The string to parse
|
|
907
|
+
* @returns Parsed array, object, or original string
|
|
908
|
+
*/
|
|
909
|
+
export const parseLiteral = (str) => {
|
|
910
|
+
const trimmed = str.trim();
|
|
911
|
+
// Try to parse as array literal
|
|
912
|
+
if (trimmed.startsWith('[') && trimmed.endsWith(']')) {
|
|
913
|
+
const result = parseArrayLiteral(trimmed);
|
|
914
|
+
return Array.isArray(result) ? result : trimmed;
|
|
915
|
+
}
|
|
916
|
+
// Try to parse as object literal
|
|
917
|
+
if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
|
|
918
|
+
try {
|
|
919
|
+
return JSON.parse(trimmed);
|
|
920
|
+
}
|
|
921
|
+
catch {
|
|
922
|
+
// Try to fix common issues
|
|
923
|
+
const p = parseJsObjectLiteral(trimmed);
|
|
924
|
+
if (p !== undefined) {
|
|
925
|
+
return p;
|
|
926
|
+
}
|
|
927
|
+
const fixed = trimmed
|
|
928
|
+
.replace(/([{,]\s*)(\w+)(\s*:)/g, '$1"$2"$3') // Add quotes to unquoted keys
|
|
929
|
+
.replace(/:\s*'([^']*)'/g, ':"$1"'); // Convert single quotes to double quotes
|
|
930
|
+
try {
|
|
931
|
+
return JSON.parse(fixed);
|
|
932
|
+
}
|
|
933
|
+
catch {
|
|
934
|
+
return trimmed;
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
// Try to parse as JSON (might be a number, boolean, or null)
|
|
939
|
+
try {
|
|
940
|
+
return JSON.parse(trimmed);
|
|
941
|
+
}
|
|
942
|
+
catch {
|
|
943
|
+
return trimmed;
|
|
944
|
+
}
|
|
945
|
+
};
|
|
946
|
+
/**
|
|
947
|
+
* Type guard to check if a value is a string array
|
|
948
|
+
* @param value - Value to check
|
|
949
|
+
* @returns True if value is a string array
|
|
950
|
+
*/
|
|
951
|
+
export const isStringArray = (value) => {
|
|
952
|
+
return (Array.isArray(value) && value.every((item) => typeof item === 'string'));
|
|
953
|
+
};
|
|
954
|
+
/**
|
|
955
|
+
* Parses a string or string array into an array of objects or primitive values
|
|
956
|
+
* Supports JSON, delimited strings, and various formats
|
|
957
|
+
* @param input - The input string or array to parse
|
|
958
|
+
* @param options - Configuration options for parsing
|
|
959
|
+
* @returns Array of parsed objects or primitive values
|
|
960
|
+
* @example
|
|
961
|
+
* // Returns primitive array: ["apple", "banana"]
|
|
962
|
+
* stringToObjectArray("apple|banana")
|
|
963
|
+
*
|
|
964
|
+
* // Returns object array: [{value: "apple", label: "apple"}, {value: "banana", label: "banana"}]
|
|
965
|
+
* stringToObjectArray("apple|banana", {valueKey: "value", labelKey: "label"})
|
|
966
|
+
*
|
|
967
|
+
* // Returns object array with defaults: [{id: "apple", name: "apple", type: "fruit"}, ...]
|
|
968
|
+
* stringToObjectArray("apple|banana", {valueKey: "id", labelKey: "name", defaultObject: {type: "fruit"}})
|
|
969
|
+
*
|
|
970
|
+
* // Returns parsed primitives: [1, 2, 3]
|
|
971
|
+
* stringToObjectArray("1|2|3", {parsePrimitives: true})
|
|
972
|
+
*/
|
|
973
|
+
export const stringToObjectArray = (input, options = {}) => {
|
|
974
|
+
const { delimiters = ['|'], valueKey, labelKey, defaultObject, trimItems = true, filterEmpty = true, returnObjects, parsePrimitives = false, preserveNullUndefined = false, } = options;
|
|
975
|
+
// Auto-detect if we should return objects
|
|
976
|
+
// Return objects if: returnObjects is true OR valueKey/labelKey/defaultObject is provided
|
|
977
|
+
const shouldReturnObjects = returnObjects ??
|
|
978
|
+
(valueKey !== undefined ||
|
|
979
|
+
labelKey !== undefined ||
|
|
980
|
+
defaultObject !== undefined);
|
|
981
|
+
// Helper function to create object from string
|
|
982
|
+
const createObject = (item) => {
|
|
983
|
+
const base = defaultObject ? { ...defaultObject } : {};
|
|
984
|
+
const trimmed = trimItems ? item.trim() : item;
|
|
985
|
+
// Only add value if valueKey is provided
|
|
986
|
+
if (valueKey !== undefined) {
|
|
987
|
+
base[valueKey] = parsePrimitives ? parsePrimitiveValue(trimmed) : trimmed;
|
|
988
|
+
}
|
|
989
|
+
if (valueKey !== labelKey ||
|
|
990
|
+
(valueKey === undefined && labelKey === undefined)) {
|
|
991
|
+
// Only add label if labelKey is provided and different from valueKey
|
|
992
|
+
let label;
|
|
993
|
+
if (valueKey === undefined && labelKey === undefined) {
|
|
994
|
+
label = returnObjects ? 'label' : valueKey;
|
|
995
|
+
}
|
|
996
|
+
else {
|
|
997
|
+
label = labelKey;
|
|
998
|
+
}
|
|
999
|
+
if (label !== undefined) {
|
|
1000
|
+
base[label] = trimmed;
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
return base;
|
|
1004
|
+
};
|
|
1005
|
+
// Helper function to parse primitive value
|
|
1006
|
+
const parsePrimitiveValue = (str) => {
|
|
1007
|
+
const trimmed = str.trim();
|
|
1008
|
+
if (trimmed === '') {
|
|
1009
|
+
return preserveNullUndefined ? null : '';
|
|
1010
|
+
}
|
|
1011
|
+
// Parse null
|
|
1012
|
+
if (trimmed.toLowerCase() === 'null') {
|
|
1013
|
+
return preserveNullUndefined ? null : 'null';
|
|
1014
|
+
}
|
|
1015
|
+
// Parse undefined
|
|
1016
|
+
if (trimmed.toLowerCase() === 'undefined') {
|
|
1017
|
+
return preserveNullUndefined ? null : 'undefined';
|
|
1018
|
+
}
|
|
1019
|
+
// Parse boolean
|
|
1020
|
+
if (trimmed.toLowerCase() === 'true')
|
|
1021
|
+
return true;
|
|
1022
|
+
if (trimmed.toLowerCase() === 'false')
|
|
1023
|
+
return false;
|
|
1024
|
+
// Parse number (integers and floats)
|
|
1025
|
+
if (/^-?\d+$/.test(trimmed)) {
|
|
1026
|
+
const num = parseInt(trimmed, 10);
|
|
1027
|
+
return isNaN(num) ? (trimItems ? trimmed : str) : num;
|
|
1028
|
+
}
|
|
1029
|
+
if (/^-?\d+\.\d+$/.test(trimmed)) {
|
|
1030
|
+
const num = parseFloat(trimmed);
|
|
1031
|
+
return isNaN(num) ? (trimItems ? trimmed : str) : num;
|
|
1032
|
+
}
|
|
1033
|
+
if (!trimItems)
|
|
1034
|
+
return str;
|
|
1035
|
+
// Return string as-is
|
|
1036
|
+
return trimmed;
|
|
1037
|
+
};
|
|
1038
|
+
// Helper function to process a single item
|
|
1039
|
+
const processItem = (item) => {
|
|
1040
|
+
// Handle null/undefined
|
|
1041
|
+
if (item == null) {
|
|
1042
|
+
return preserveNullUndefined ? item : null;
|
|
1043
|
+
}
|
|
1044
|
+
// Process string items
|
|
1045
|
+
if (typeof item === 'string') {
|
|
1046
|
+
const processed = trimItems ? item.trim() : item;
|
|
1047
|
+
if (item === 'null')
|
|
1048
|
+
return (preserveNullUndefined ? null : null);
|
|
1049
|
+
if (item === 'undefined')
|
|
1050
|
+
return (preserveNullUndefined ? undefined : 'undefined');
|
|
1051
|
+
if (item === '')
|
|
1052
|
+
return (preserveNullUndefined ? null : '');
|
|
1053
|
+
// Filter empty strings if needed
|
|
1054
|
+
if (filterEmpty && processed === '') {
|
|
1055
|
+
return preserveNullUndefined ? processed : null;
|
|
1056
|
+
}
|
|
1057
|
+
if (shouldReturnObjects) {
|
|
1058
|
+
return createObject(processed);
|
|
1059
|
+
}
|
|
1060
|
+
else {
|
|
1061
|
+
return (parsePrimitives ? parsePrimitiveValue(processed) : processed);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
// Process number/boolean
|
|
1065
|
+
if (typeof item === 'number' || typeof item === 'boolean') {
|
|
1066
|
+
if (shouldReturnObjects) {
|
|
1067
|
+
const base = defaultObject ? { ...defaultObject } : {};
|
|
1068
|
+
if (valueKey !== undefined)
|
|
1069
|
+
base[valueKey] = item;
|
|
1070
|
+
if (labelKey !== undefined && labelKey !== valueKey)
|
|
1071
|
+
base[labelKey] = String(item);
|
|
1072
|
+
return base;
|
|
1073
|
+
}
|
|
1074
|
+
else {
|
|
1075
|
+
return item;
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
// Process existing objects
|
|
1079
|
+
if (typeof item === 'object' && !Array.isArray(item)) {
|
|
1080
|
+
if (shouldReturnObjects) {
|
|
1081
|
+
// Merge with defaultObject if provided
|
|
1082
|
+
return defaultObject
|
|
1083
|
+
? { ...defaultObject, ...item }
|
|
1084
|
+
: item;
|
|
1085
|
+
}
|
|
1086
|
+
else {
|
|
1087
|
+
// Try to extract value from object
|
|
1088
|
+
if (valueKey && item[valueKey] !== undefined) {
|
|
1089
|
+
return item[valueKey];
|
|
1090
|
+
}
|
|
1091
|
+
// Fallback to string representation
|
|
1092
|
+
return item;
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
// Process arrays (convert to string)
|
|
1096
|
+
if (Array.isArray(item)) {
|
|
1097
|
+
const str = JSON.stringify(item);
|
|
1098
|
+
if (shouldReturnObjects) {
|
|
1099
|
+
return createObject(str);
|
|
1100
|
+
}
|
|
1101
|
+
else {
|
|
1102
|
+
return str;
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
// Default: convert to string
|
|
1106
|
+
const str = String(item);
|
|
1107
|
+
if (shouldReturnObjects) {
|
|
1108
|
+
return createObject(str);
|
|
1109
|
+
}
|
|
1110
|
+
else {
|
|
1111
|
+
return str;
|
|
1112
|
+
}
|
|
1113
|
+
};
|
|
1114
|
+
// Handle array input
|
|
1115
|
+
if (Array.isArray(input)) {
|
|
1116
|
+
const result = input.map(processItem);
|
|
1117
|
+
if (preserveNullUndefined) {
|
|
1118
|
+
return result;
|
|
1119
|
+
}
|
|
1120
|
+
return result.filter((item) => item !== null);
|
|
1121
|
+
}
|
|
1122
|
+
// Handle string input
|
|
1123
|
+
if (typeof input !== 'string') {
|
|
1124
|
+
// Try to convert to string
|
|
1125
|
+
if (input == null)
|
|
1126
|
+
return [];
|
|
1127
|
+
return processItem(String(input))
|
|
1128
|
+
? [processItem(String(input))]
|
|
1129
|
+
: [];
|
|
1130
|
+
}
|
|
1131
|
+
const str = input.trim();
|
|
1132
|
+
// Return empty array for empty string
|
|
1133
|
+
if (str === '') {
|
|
1134
|
+
return [];
|
|
1135
|
+
}
|
|
1136
|
+
// Try to parse as JSON first
|
|
1137
|
+
if ((str.startsWith('[') && str.endsWith(']')) ||
|
|
1138
|
+
(str.startsWith('{') && str.endsWith('}'))) {
|
|
1139
|
+
try {
|
|
1140
|
+
const parsed = JSON.parse(str);
|
|
1141
|
+
if (Array.isArray(parsed)) {
|
|
1142
|
+
const result = parsed.map(processItem);
|
|
1143
|
+
if (preserveNullUndefined) {
|
|
1144
|
+
return result;
|
|
1145
|
+
}
|
|
1146
|
+
return result.filter((item) => item !== null);
|
|
1147
|
+
}
|
|
1148
|
+
// Single object or value
|
|
1149
|
+
const processed = processItem(parsed);
|
|
1150
|
+
return processed ? [processed] : [];
|
|
1151
|
+
}
|
|
1152
|
+
catch {
|
|
1153
|
+
// Not valid JSON, continue with other methods
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
// Try to parse as array literal (handles cases like "[a, b, c]")
|
|
1157
|
+
const literalResult = parseArrayLiteral(str);
|
|
1158
|
+
if (Array.isArray(literalResult)) {
|
|
1159
|
+
const result = literalResult.map((item) => {
|
|
1160
|
+
const processed = processItem(item);
|
|
1161
|
+
return processed;
|
|
1162
|
+
});
|
|
1163
|
+
if (preserveNullUndefined) {
|
|
1164
|
+
return result;
|
|
1165
|
+
}
|
|
1166
|
+
return result.filter((item) => item !== null);
|
|
1167
|
+
}
|
|
1168
|
+
// Try each delimiter
|
|
1169
|
+
for (const delimiter of delimiters) {
|
|
1170
|
+
if (str.includes(delimiter)) {
|
|
1171
|
+
const parts = (trimItems ? str : input).split(delimiter);
|
|
1172
|
+
const result = parts.map((part) => processItem(part));
|
|
1173
|
+
if (preserveNullUndefined) {
|
|
1174
|
+
return result;
|
|
1175
|
+
}
|
|
1176
|
+
return result.filter((item) => item !== null && (!filterEmpty || item !== ''));
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
// Single value
|
|
1180
|
+
const processed = processItem(str);
|
|
1181
|
+
return processed ? [processed] : [];
|
|
1182
|
+
};
|
|
1183
|
+
/**
|
|
1184
|
+
* Simplified version that always returns primitive values
|
|
1185
|
+
* @param input - The input string or array to parse
|
|
1186
|
+
* @param options - Configuration options (delimiters, trim, etc.)
|
|
1187
|
+
* @returns Array of primitive values
|
|
1188
|
+
* @example
|
|
1189
|
+
* stringToPrimitiveArray("apple|banana") // ["apple", "banana"]
|
|
1190
|
+
* stringToPrimitiveArray("1,2,3", {delimiters: [","], parsePrimitives: true}) // [1, 2, 3]
|
|
1191
|
+
*/
|
|
1192
|
+
export const stringToPrimitiveArray = (input, options = {}) => {
|
|
1193
|
+
return stringToObjectArray(input, {
|
|
1194
|
+
...options,
|
|
1195
|
+
returnObjects: false,
|
|
1196
|
+
});
|
|
1197
|
+
};
|
|
1198
|
+
/**
|
|
1199
|
+
* Simplified version that always returns objects
|
|
1200
|
+
* @param input - The input string or array to parse
|
|
1201
|
+
* @param options - Configuration options including valueKey, labelKey, etc.
|
|
1202
|
+
* @returns Array of objects
|
|
1203
|
+
* @example
|
|
1204
|
+
* stringToObjectArray("apple|banana", {valueKey: "id"}) // [{id: "apple"}, {id: "banana"}]
|
|
1205
|
+
*/
|
|
1206
|
+
export const stringToObjectArrayOnly = (input, options = {}) => {
|
|
1207
|
+
return stringToObjectArray(input, {
|
|
1208
|
+
...options,
|
|
1209
|
+
returnObjects: true,
|
|
1210
|
+
});
|
|
1211
|
+
};
|
|
1212
|
+
/**
|
|
1213
|
+
* Type guard to check if parsed result is primitive array
|
|
1214
|
+
*/
|
|
1215
|
+
export const isPrimitiveArray = (arr) => {
|
|
1216
|
+
return arr.every((item) => item == null ||
|
|
1217
|
+
typeof item === 'string' ||
|
|
1218
|
+
typeof item === 'number' ||
|
|
1219
|
+
typeof item === 'boolean');
|
|
1220
|
+
};
|
|
1221
|
+
// Helper function used internally
|
|
1222
|
+
export const parsePrimitiveValue = (str) => {
|
|
1223
|
+
const trimmed = str.trim();
|
|
1224
|
+
if (trimmed === '')
|
|
1225
|
+
return null;
|
|
1226
|
+
if (trimmed.toLowerCase() === 'null')
|
|
1227
|
+
return null;
|
|
1228
|
+
if (trimmed.toLowerCase() === 'undefined')
|
|
1229
|
+
return undefined;
|
|
1230
|
+
if (trimmed.toLowerCase() === 'true')
|
|
1231
|
+
return true;
|
|
1232
|
+
if (trimmed.toLowerCase() === 'false')
|
|
1233
|
+
return false;
|
|
1234
|
+
// Try integer
|
|
1235
|
+
if (/^-?\d+$/.test(trimmed)) {
|
|
1236
|
+
const num = parseInt(trimmed, 10);
|
|
1237
|
+
return isNaN(num) ? trimmed : num;
|
|
1238
|
+
}
|
|
1239
|
+
// Try float
|
|
1240
|
+
if (/^-?\d+\.\d+$/.test(trimmed)) {
|
|
1241
|
+
const num = parseFloat(trimmed);
|
|
1242
|
+
return isNaN(num) ? trimmed : num;
|
|
1243
|
+
}
|
|
1244
|
+
// Try scientific notation
|
|
1245
|
+
if (/^-?\d+(?:\.\d+)?[eE][+-]?\d+$/.test(trimmed)) {
|
|
1246
|
+
const num = parseFloat(trimmed);
|
|
1247
|
+
return isNaN(num) ? trimmed : num;
|
|
1248
|
+
}
|
|
1249
|
+
return trimmed;
|
|
1250
|
+
};
|
|
1251
|
+
// Export all utilities
|
|
1252
|
+
export default {
|
|
1253
|
+
trim,
|
|
1254
|
+
trimStart,
|
|
1255
|
+
trimEnd,
|
|
1256
|
+
toCamelCase,
|
|
1257
|
+
toPascalCase,
|
|
1258
|
+
toKebabCase,
|
|
1259
|
+
toSnakeCase,
|
|
1260
|
+
capitalize,
|
|
1261
|
+
toTitleCase,
|
|
1262
|
+
startsWith,
|
|
1263
|
+
endsWith,
|
|
1264
|
+
includes,
|
|
1265
|
+
includesIgnoreCase,
|
|
1266
|
+
replaceStringByIndex,
|
|
1267
|
+
replaceAll,
|
|
1268
|
+
replaceAllIgnoreCase,
|
|
1269
|
+
escapeRegExp,
|
|
1270
|
+
escapeHtml,
|
|
1271
|
+
unescapeHtml,
|
|
1272
|
+
truncate,
|
|
1273
|
+
pad,
|
|
1274
|
+
isEmpty,
|
|
1275
|
+
isValidEmail,
|
|
1276
|
+
isValidUrl,
|
|
1277
|
+
extractDomain,
|
|
1278
|
+
randomString,
|
|
1279
|
+
countOccurrences,
|
|
1280
|
+
removeWhitespace,
|
|
1281
|
+
reverse,
|
|
1282
|
+
isPalindrome,
|
|
1283
|
+
slugify,
|
|
1284
|
+
mask,
|
|
1285
|
+
getInitials,
|
|
1286
|
+
normalizeLineEndings,
|
|
1287
|
+
wordWrap,
|
|
1288
|
+
isString,
|
|
1289
|
+
toString,
|
|
1290
|
+
escapeString,
|
|
1291
|
+
replaceString,
|
|
1292
|
+
replaceStringPattern,
|
|
1293
|
+
stringToObjectArray,
|
|
1294
|
+
parseArrayLiteral,
|
|
1295
|
+
parseLiteral,
|
|
1296
|
+
isStringArray,
|
|
1297
|
+
stringToPrimitiveArray,
|
|
1298
|
+
stringToObjectArrayOnly,
|
|
1299
|
+
isPrimitiveArray,
|
|
1300
|
+
parsePrimitiveValue,
|
|
1301
|
+
};
|