indusagi-coding-agent 0.1.23 → 0.1.25
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/CHANGELOG.md +101 -0
- package/README.md +2 -0
- package/dist/cli/args.d.ts +117 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +221 -52
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/config-selector.d.ts +58 -2
- package/dist/cli/config-selector.d.ts.map +1 -1
- package/dist/cli/config-selector.js +130 -12
- package/dist/cli/config-selector.js.map +1 -1
- package/dist/cli/file-processor.d.ts +70 -2
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js +240 -15
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/cli/list-models.d.ts +63 -3
- package/dist/cli/list-models.d.ts.map +1 -1
- package/dist/cli/list-models.js +202 -27
- package/dist/cli/list-models.js.map +1 -1
- package/dist/cli/login-handler.d.ts +82 -8
- package/dist/cli/login-handler.d.ts.map +1 -1
- package/dist/cli/login-handler.js +410 -77
- package/dist/cli/login-handler.js.map +1 -1
- package/dist/cli/session-picker.d.ts +74 -2
- package/dist/cli/session-picker.d.ts.map +1 -1
- package/dist/cli/session-picker.js +236 -12
- package/dist/cli/session-picker.js.map +1 -1
- package/dist/core/agent-session.d.ts +214 -9
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +214 -9
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/bash-executor.d.ts +302 -12
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +302 -12
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/diagnostics.d.ts +191 -0
- package/dist/core/diagnostics.d.ts.map +1 -1
- package/dist/core/diagnostics.js +142 -0
- package/dist/core/diagnostics.js.map +1 -1
- package/dist/core/discover-packages.d.ts +6 -0
- package/dist/core/discover-packages.d.ts.map +1 -0
- package/dist/core/discover-packages.js +62 -0
- package/dist/core/discover-packages.js.map +1 -0
- package/dist/core/event-bus.d.ts +146 -0
- package/dist/core/event-bus.d.ts.map +1 -1
- package/dist/core/event-bus.js +93 -0
- package/dist/core/event-bus.js.map +1 -1
- package/dist/core/export-html/ansi-to-html.d.ts +4 -0
- package/dist/core/export-html/ansi-to-html.d.ts.map +1 -1
- package/dist/core/export-html/ansi-to-html.js +4 -0
- package/dist/core/export-html/ansi-to-html.js.map +1 -1
- package/dist/core/export-html/index.d.ts +128 -0
- package/dist/core/export-html/index.d.ts.map +1 -1
- package/dist/core/export-html/index.js +128 -0
- package/dist/core/export-html/index.js.map +1 -1
- package/dist/core/export-html/tool-renderer.d.ts +4 -0
- package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
- package/dist/core/export-html/tool-renderer.js +4 -0
- package/dist/core/export-html/tool-renderer.js.map +1 -1
- package/dist/core/keybindings.d.ts +142 -0
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +142 -0
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/model-registry.d.ts +98 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +98 -1
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts +99 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +99 -1
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/prompt-templates.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +2 -0
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +127 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +125 -0
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/skills.js.map +1 -1
- package/dist/core/subagents.js.map +1 -1
- package/dist/core/tools/bash.d.ts +391 -11
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +269 -2
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/bg-process.d.ts +49 -0
- package/dist/core/tools/bg-process.d.ts.map +1 -0
- package/dist/core/tools/bg-process.js +69 -0
- package/dist/core/tools/bg-process.js.map +1 -0
- package/dist/core/tools/edit.d.ts +284 -6
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +238 -0
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/find.d.ts +169 -5
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +136 -0
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts +285 -5
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +247 -0
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/index.d.ts +45 -0
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +15 -0
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/ls.d.ts +6 -0
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js +6 -0
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/read.d.ts +308 -7
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +231 -0
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/registry.d.ts +17 -0
- package/dist/core/tools/registry.d.ts.map +1 -0
- package/dist/core/tools/registry.js +108 -0
- package/dist/core/tools/registry.js.map +1 -0
- package/dist/core/tools/webfetch.d.ts +118 -3
- package/dist/core/tools/webfetch.d.ts.map +1 -1
- package/dist/core/tools/webfetch.js +118 -3
- package/dist/core/tools/webfetch.js.map +1 -1
- package/dist/core/tools/websearch.d.ts +130 -3
- package/dist/core/tools/websearch.d.ts.map +1 -1
- package/dist/core/tools/websearch.js +130 -3
- package/dist/core/tools/websearch.js.map +1 -1
- package/dist/core/tools/write.d.ts +251 -5
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +210 -0
- package/dist/core/tools/write.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +12 -1
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts +164 -1
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +164 -1
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts +297 -1
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js +297 -1
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +251 -1
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/user-message.d.ts +186 -1
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js +186 -1
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +1567 -13
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +1567 -13
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts +422 -0
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +422 -0
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/print-mode.d.ts +538 -5
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +538 -5
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +921 -8
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +921 -8
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts +802 -9
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +802 -9
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +356 -3
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js +356 -3
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/modes/shared.d.ts +386 -0
- package/dist/modes/shared.d.ts.map +1 -0
- package/dist/modes/shared.js +543 -0
- package/dist/modes/shared.js.map +1 -0
- package/dist/utils/array.d.ts +389 -0
- package/dist/utils/array.d.ts.map +1 -0
- package/dist/utils/array.js +585 -0
- package/dist/utils/array.js.map +1 -0
- package/dist/utils/color-formatter.d.ts +318 -0
- package/dist/utils/color-formatter.d.ts.map +1 -0
- package/dist/utils/color-formatter.js +442 -0
- package/dist/utils/color-formatter.js.map +1 -0
- package/dist/utils/data-transformer.d.ts +326 -0
- package/dist/utils/data-transformer.d.ts.map +1 -0
- package/dist/utils/data-transformer.js +512 -0
- package/dist/utils/data-transformer.js.map +1 -0
- package/dist/utils/date-formatter.d.ts +281 -0
- package/dist/utils/date-formatter.d.ts.map +1 -0
- package/dist/utils/date-formatter.js +503 -0
- package/dist/utils/date-formatter.js.map +1 -0
- package/dist/utils/error-handler.d.ts +541 -0
- package/dist/utils/error-handler.d.ts.map +1 -0
- package/dist/utils/error-handler.js +726 -0
- package/dist/utils/error-handler.js.map +1 -0
- package/dist/utils/file-operations.d.ts +297 -0
- package/dist/utils/file-operations.d.ts.map +1 -0
- package/dist/utils/file-operations.js +505 -0
- package/dist/utils/file-operations.js.map +1 -0
- package/dist/utils/frontmatter.d.ts +268 -6
- package/dist/utils/frontmatter.d.ts.map +1 -1
- package/dist/utils/frontmatter.js +500 -21
- package/dist/utils/frontmatter.js.map +1 -1
- package/dist/utils/json-formatter.d.ts +259 -0
- package/dist/utils/json-formatter.d.ts.map +1 -0
- package/dist/utils/json-formatter.js +517 -0
- package/dist/utils/json-formatter.js.map +1 -0
- package/dist/utils/logger.d.ts +176 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +346 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/markdown-formatter.d.ts +211 -0
- package/dist/utils/markdown-formatter.d.ts.map +1 -0
- package/dist/utils/markdown-formatter.js +482 -0
- package/dist/utils/markdown-formatter.js.map +1 -0
- package/dist/utils/path-validator.d.ts +603 -0
- package/dist/utils/path-validator.d.ts.map +1 -0
- package/dist/utils/path-validator.js +870 -0
- package/dist/utils/path-validator.js.map +1 -0
- package/dist/utils/string-formatter.d.ts +609 -0
- package/dist/utils/string-formatter.d.ts.map +1 -0
- package/dist/utils/string-formatter.js +806 -0
- package/dist/utils/string-formatter.js.map +1 -0
- package/dist/utils/type-guards.d.ts +629 -0
- package/dist/utils/type-guards.d.ts.map +1 -0
- package/dist/utils/type-guards.js +662 -0
- package/dist/utils/type-guards.js.map +1 -0
- package/docs/COMPLETE-GUIDE.md +300 -0
- package/docs/COMPREHENSIVE-CLI-SUMMARY.md +900 -0
- package/docs/MODES-ARCHITECTURE.md +565 -0
- package/docs/PRINT-MODE-GUIDE.md +456 -0
- package/docs/RPC-GUIDE.md +705 -0
- package/docs/UTILS-IMPLEMENTATION-SUMMARY.md +647 -0
- package/docs/UTILS-MODULE-OVERVIEW.md +1480 -0
- package/docs/UTILS-QA-CHECKLIST.md +1061 -0
- package/docs/UTILS-USAGE-GUIDE.md +1419 -0
- package/package.json +7 -3
|
@@ -0,0 +1,806 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview String Formatter Utilities
|
|
3
|
+
*
|
|
4
|
+
* Comprehensive string manipulation and formatting utilities for text transformation,
|
|
5
|
+
* case conversion, truncation, padding, and other common string operations.
|
|
6
|
+
*
|
|
7
|
+
* This module provides type-safe, well-tested functions for:
|
|
8
|
+
* - Case conversion (camelCase, snake_case, kebab-case, Title Case)
|
|
9
|
+
* - String truncation and padding
|
|
10
|
+
* - Character manipulation (reverse, capitalize, decapitalize)
|
|
11
|
+
* - String searching and matching
|
|
12
|
+
* - String interpolation patterns
|
|
13
|
+
* - Multi-line text handling
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* // Case conversion
|
|
17
|
+
* toTitleCase('hello world') // "Hello World"
|
|
18
|
+
* toCamelCase('hello-world') // "helloWorld"
|
|
19
|
+
* toSnakeCase('helloWorld') // "hello_world"
|
|
20
|
+
* toKebabCase('HelloWorld') // "hello-world"
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* // String manipulation
|
|
24
|
+
* truncate('hello world', 8) // "hello..."
|
|
25
|
+
* capitalize('hello') // "Hello"
|
|
26
|
+
* reverse('hello') // "olleh"
|
|
27
|
+
* repeat('ab', 3) // "ababab"
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* // Padding and spacing
|
|
31
|
+
* padStart('5', 3, '0') // "005"
|
|
32
|
+
* padEnd('5', 3, '0') // "500"
|
|
33
|
+
* repeat(' ', 4) // " "
|
|
34
|
+
*/
|
|
35
|
+
/**
|
|
36
|
+
* Converts a string to Title Case (each word capitalized)
|
|
37
|
+
*
|
|
38
|
+
* Splits on spaces, hyphens, underscores, and camelCase boundaries.
|
|
39
|
+
* Properly handles multiple delimiters and preserves word order.
|
|
40
|
+
*
|
|
41
|
+
* @param str - The input string to convert
|
|
42
|
+
* @returns The title-cased string
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* toTitleCase('hello world') // "Hello World"
|
|
46
|
+
* toTitleCase('hello-world') // "Hello World"
|
|
47
|
+
* toTitleCase('hello_world') // "Hello World"
|
|
48
|
+
* toTitleCase('helloWorld') // "Hello World"
|
|
49
|
+
* toTitleCase('HELLO WORLD') // "Hello World"
|
|
50
|
+
* toTitleCase('hello world') // "Hello World" (collapses spaces)
|
|
51
|
+
* toTitleCase('') // ""
|
|
52
|
+
* toTitleCase('a') // "A"
|
|
53
|
+
*
|
|
54
|
+
* @throws Does not throw; returns empty string for null/undefined
|
|
55
|
+
*/
|
|
56
|
+
export function toTitleCase(str) {
|
|
57
|
+
if (!str)
|
|
58
|
+
return '';
|
|
59
|
+
// Split on common delimiters and camelCase boundaries
|
|
60
|
+
const words = str
|
|
61
|
+
.replace(/([a-z])([A-Z])/g, '$1 $2') // camelCase
|
|
62
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2') // HTMLParser -> HTML Parser
|
|
63
|
+
.replace(/[-_\s]+/g, ' ') // hyphens, underscores, spaces
|
|
64
|
+
.trim()
|
|
65
|
+
.split(/\s+/);
|
|
66
|
+
return words
|
|
67
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
68
|
+
.join(' ');
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Converts a string to camelCase format
|
|
72
|
+
*
|
|
73
|
+
* Useful for JavaScript variable names and object properties.
|
|
74
|
+
* Handles multiple input formats and preserves acronyms intelligently.
|
|
75
|
+
*
|
|
76
|
+
* @param str - The input string to convert
|
|
77
|
+
* @returns The camelCased string
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* toCamelCase('hello world') // "helloWorld"
|
|
81
|
+
* toCamelCase('hello-world') // "helloWorld"
|
|
82
|
+
* toCamelCase('hello_world') // "helloWorld"
|
|
83
|
+
* toCamelCase('HelloWorld') // "helloWorld"
|
|
84
|
+
* toCamelCase('HELLO_WORLD') // "helloWorld"
|
|
85
|
+
* toCamelCase('get-user-id') // "getUserId"
|
|
86
|
+
* toCamelCase('') // ""
|
|
87
|
+
* toCamelCase('x') // "x"
|
|
88
|
+
*
|
|
89
|
+
* @edge-cases
|
|
90
|
+
* - Empty strings return empty string
|
|
91
|
+
* - Single characters preserved as lowercase
|
|
92
|
+
* - Multiple consecutive delimiters treated as single
|
|
93
|
+
*/
|
|
94
|
+
export function toCamelCase(str) {
|
|
95
|
+
if (!str)
|
|
96
|
+
return '';
|
|
97
|
+
const words = str
|
|
98
|
+
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
|
99
|
+
.replace(/[-_\s]+/g, ' ')
|
|
100
|
+
.trim()
|
|
101
|
+
.split(/\s+/);
|
|
102
|
+
if (words.length === 0)
|
|
103
|
+
return '';
|
|
104
|
+
// First word lowercase, rest title case
|
|
105
|
+
return (words[0].toLowerCase() +
|
|
106
|
+
words
|
|
107
|
+
.slice(1)
|
|
108
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
109
|
+
.join(''));
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Converts a string to snake_case format
|
|
113
|
+
*
|
|
114
|
+
* Useful for Python variable names, database columns, and file names.
|
|
115
|
+
* Handles acronyms and consecutive uppercase letters.
|
|
116
|
+
*
|
|
117
|
+
* @param str - The input string to convert
|
|
118
|
+
* @returns The snake_cased string
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* toSnakeCase('HelloWorld') // "hello_world"
|
|
122
|
+
* toSnakeCase('hello world') // "hello_world"
|
|
123
|
+
* toSnakeCase('hello-world') // "hello_world"
|
|
124
|
+
* toSnakeCase('helloWorld') // "hello_world"
|
|
125
|
+
* toSnakeCase('HTMLParser') // "html_parser"
|
|
126
|
+
* toSnakeCase('getHTTPResponseCode') // "get_http_response_code"
|
|
127
|
+
* toSnakeCase('') // ""
|
|
128
|
+
*
|
|
129
|
+
* @edge-cases
|
|
130
|
+
* - Consecutive uppercase letters treated as acronym
|
|
131
|
+
* - Preserves existing underscores
|
|
132
|
+
* - Collapses multiple delimiters
|
|
133
|
+
*/
|
|
134
|
+
export function toSnakeCase(str) {
|
|
135
|
+
if (!str)
|
|
136
|
+
return '';
|
|
137
|
+
return str
|
|
138
|
+
.replace(/([a-z])([A-Z])/g, '$1_$2') // camelCase
|
|
139
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2') // HTMLParser
|
|
140
|
+
.replace(/[-\s]+/g, '_') // hyphens, spaces
|
|
141
|
+
.toLowerCase();
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Converts a string to kebab-case format
|
|
145
|
+
*
|
|
146
|
+
* Useful for URL slugs, CSS class names, and configuration keys.
|
|
147
|
+
* Similar to snake_case but uses hyphens instead of underscores.
|
|
148
|
+
*
|
|
149
|
+
* @param str - The input string to convert
|
|
150
|
+
* @returns The kebab-cased string
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* toKebabCase('HelloWorld') // "hello-world"
|
|
154
|
+
* toKebabCase('hello world') // "hello-world"
|
|
155
|
+
* toKebabCase('hello_world') // "hello-world"
|
|
156
|
+
* toKebabCase('helloWorld') // "hello-world"
|
|
157
|
+
* toKebabCase('get-user-data') // "get-user-data"
|
|
158
|
+
* toKebabCase('GetUserData') // "get-user-data"
|
|
159
|
+
* toKebabCase('') // ""
|
|
160
|
+
*
|
|
161
|
+
* @edge-cases
|
|
162
|
+
* - URL-safe; suitable for web slugs
|
|
163
|
+
* - Collapses multiple delimiters
|
|
164
|
+
*/
|
|
165
|
+
export function toKebabCase(str) {
|
|
166
|
+
if (!str)
|
|
167
|
+
return '';
|
|
168
|
+
return str
|
|
169
|
+
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
170
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')
|
|
171
|
+
.replace(/[_\s]+/g, '-')
|
|
172
|
+
.toLowerCase();
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Truncates a string to a maximum length with optional ellipsis
|
|
176
|
+
*
|
|
177
|
+
* Intelligently handles word boundaries when requested.
|
|
178
|
+
* Preserves readability by truncating at word ends when possible.
|
|
179
|
+
*
|
|
180
|
+
* @param str - The input string to truncate
|
|
181
|
+
* @param maxLength - Maximum length of result (including ellipsis if added)
|
|
182
|
+
* @param ellipsis - String to append if truncated (default: '...')
|
|
183
|
+
* @param wordBoundary - If true, truncate at word boundary (default: false)
|
|
184
|
+
* @returns The truncated string
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* truncate('hello world', 8) // "hello..."
|
|
188
|
+
* truncate('hello world', 11) // "hello world"
|
|
189
|
+
* truncate('hello world', 8, '…') // "hello…"
|
|
190
|
+
* truncate('hello world', 8, '...', true) // "hello..." (word boundary)
|
|
191
|
+
* truncate('supercalifragilisticexpialidocious', 10) // "supercali..."
|
|
192
|
+
* truncate('', 5) // ""
|
|
193
|
+
* truncate('hi', 10) // "hi"
|
|
194
|
+
*
|
|
195
|
+
* @edge-cases
|
|
196
|
+
* - If maxLength < ellipsis.length, returns ellipsis
|
|
197
|
+
* - Word boundary mode may result in shorter strings
|
|
198
|
+
* - Empty strings return empty string
|
|
199
|
+
*/
|
|
200
|
+
export function truncate(str, maxLength, ellipsis = '...', wordBoundary = false) {
|
|
201
|
+
if (!str || maxLength < 1)
|
|
202
|
+
return str;
|
|
203
|
+
if (str.length <= maxLength)
|
|
204
|
+
return str;
|
|
205
|
+
const truncateLength = Math.max(0, maxLength - ellipsis.length);
|
|
206
|
+
if (truncateLength === 0)
|
|
207
|
+
return ellipsis;
|
|
208
|
+
let truncated = str.slice(0, truncateLength);
|
|
209
|
+
if (wordBoundary) {
|
|
210
|
+
const lastSpace = truncated.lastIndexOf(' ');
|
|
211
|
+
if (lastSpace > 0) {
|
|
212
|
+
truncated = truncated.slice(0, lastSpace);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return truncated + ellipsis;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Pads the start of a string with a fill string
|
|
219
|
+
*
|
|
220
|
+
* Complements JavaScript's native padStart with custom handling.
|
|
221
|
+
* Useful for formatting numbers, codes, and aligned output.
|
|
222
|
+
*
|
|
223
|
+
* @param str - The input string to pad
|
|
224
|
+
* @param length - Target length
|
|
225
|
+
* @param fillStr - String to repeat for padding (default: ' ')
|
|
226
|
+
* @returns The padded string
|
|
227
|
+
*
|
|
228
|
+
* @example
|
|
229
|
+
* padStart('5', 3, '0') // "005"
|
|
230
|
+
* padStart('42', 4, '0') // "0042"
|
|
231
|
+
* padStart('hello', 10) // " hello"
|
|
232
|
+
* padStart('hello', 3) // "hello" (no padding if already longer)
|
|
233
|
+
* padStart('', 3, '-') // "---"
|
|
234
|
+
* padStart('a', 5, 'xy') // "xyxya"
|
|
235
|
+
*
|
|
236
|
+
* @edge-cases
|
|
237
|
+
* - If target length is less than string length, returns original
|
|
238
|
+
* - Multiple character fill strings repeat as needed
|
|
239
|
+
*/
|
|
240
|
+
export function padStart(str, length, fillStr = ' ') {
|
|
241
|
+
if (!fillStr || str.length >= length)
|
|
242
|
+
return str;
|
|
243
|
+
const padLength = length - str.length;
|
|
244
|
+
const fill = fillStr.repeat(Math.ceil(padLength / fillStr.length));
|
|
245
|
+
return fill.slice(0, padLength) + str;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Pads the end of a string with a fill string
|
|
249
|
+
*
|
|
250
|
+
* Opposite of padStart. Useful for right-aligned formatting.
|
|
251
|
+
*
|
|
252
|
+
* @param str - The input string to pad
|
|
253
|
+
* @param length - Target length
|
|
254
|
+
* @param fillStr - String to repeat for padding (default: ' ')
|
|
255
|
+
* @returns The padded string
|
|
256
|
+
*
|
|
257
|
+
* @example
|
|
258
|
+
* padEnd('5', 3, '0') // "500"
|
|
259
|
+
* padEnd('hello', 10) // "hello "
|
|
260
|
+
* padEnd('hello', 3) // "hello" (no padding if already longer)
|
|
261
|
+
* padEnd('', 3, '-') // "---"
|
|
262
|
+
* padEnd('a', 5, 'xy') // "axyxy"
|
|
263
|
+
*
|
|
264
|
+
* @edge-cases
|
|
265
|
+
* - If target length is less than string length, returns original
|
|
266
|
+
* - Multiple character fill strings repeat as needed
|
|
267
|
+
*/
|
|
268
|
+
export function padEnd(str, length, fillStr = ' ') {
|
|
269
|
+
if (!fillStr || str.length >= length)
|
|
270
|
+
return str;
|
|
271
|
+
const padLength = length - str.length;
|
|
272
|
+
const fill = fillStr.repeat(Math.ceil(padLength / fillStr.length));
|
|
273
|
+
return str + fill.slice(0, padLength);
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Repeats a string a specified number of times
|
|
277
|
+
*
|
|
278
|
+
* Native alternative with better error handling.
|
|
279
|
+
*
|
|
280
|
+
* @param str - The string to repeat
|
|
281
|
+
* @param count - Number of times to repeat (default: 1)
|
|
282
|
+
* @returns The repeated string
|
|
283
|
+
*
|
|
284
|
+
* @example
|
|
285
|
+
* repeat('ab', 3) // "ababab"
|
|
286
|
+
* repeat('x', 5) // "xxxxx"
|
|
287
|
+
* repeat(' ', 4) // " "
|
|
288
|
+
* repeat('hello', 0) // ""
|
|
289
|
+
* repeat('', 5) // ""
|
|
290
|
+
* repeat('-', 10) // "----------"
|
|
291
|
+
*
|
|
292
|
+
* @edge-cases
|
|
293
|
+
* - Zero or negative count returns empty string
|
|
294
|
+
* - Empty string repeated returns empty string
|
|
295
|
+
*/
|
|
296
|
+
export function repeat(str, count = 1) {
|
|
297
|
+
if (count <= 0 || !str)
|
|
298
|
+
return '';
|
|
299
|
+
return str.repeat(Math.max(0, count));
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Capitalizes the first character of a string
|
|
303
|
+
*
|
|
304
|
+
* Leaves remaining characters unchanged (unlike toTitleCase).
|
|
305
|
+
*
|
|
306
|
+
* @param str - The input string
|
|
307
|
+
* @returns String with first character uppercase
|
|
308
|
+
*
|
|
309
|
+
* @example
|
|
310
|
+
* capitalize('hello') // "Hello"
|
|
311
|
+
* capitalize('HELLO') // "HELLO"
|
|
312
|
+
* capitalize('helloWorld') // "HelloWorld"
|
|
313
|
+
* capitalize('') // ""
|
|
314
|
+
* capitalize('a') // "A"
|
|
315
|
+
* capitalize('already Capitalized') // "Already Capitalized"
|
|
316
|
+
*
|
|
317
|
+
* @edge-cases
|
|
318
|
+
* - Empty string returns empty string
|
|
319
|
+
* - Doesn't change case of remaining characters
|
|
320
|
+
*/
|
|
321
|
+
export function capitalize(str) {
|
|
322
|
+
if (!str)
|
|
323
|
+
return '';
|
|
324
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Decapitalizes the first character of a string
|
|
328
|
+
*
|
|
329
|
+
* Opposite of capitalize. Useful for variable naming.
|
|
330
|
+
*
|
|
331
|
+
* @param str - The input string
|
|
332
|
+
* @returns String with first character lowercase
|
|
333
|
+
*
|
|
334
|
+
* @example
|
|
335
|
+
* decapitalize('Hello') // "hello"
|
|
336
|
+
* decapitalize('HELLO') // "hELLO"
|
|
337
|
+
* decapitalize('hello') // "hello"
|
|
338
|
+
* decapitalize('') // ""
|
|
339
|
+
* decapitalize('A') // "a"
|
|
340
|
+
*
|
|
341
|
+
* @edge-cases
|
|
342
|
+
* - Empty string returns empty string
|
|
343
|
+
* - Doesn't change case of remaining characters
|
|
344
|
+
*/
|
|
345
|
+
export function decapitalize(str) {
|
|
346
|
+
if (!str)
|
|
347
|
+
return '';
|
|
348
|
+
return str.charAt(0).toLowerCase() + str.slice(1);
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Reverses a string character by character
|
|
352
|
+
*
|
|
353
|
+
* Properly handles Unicode and multi-byte characters.
|
|
354
|
+
*
|
|
355
|
+
* @param str - The input string to reverse
|
|
356
|
+
* @returns The reversed string
|
|
357
|
+
*
|
|
358
|
+
* @example
|
|
359
|
+
* reverse('hello') // "olleh"
|
|
360
|
+
* reverse('abc') // "cba"
|
|
361
|
+
* reverse('') // ""
|
|
362
|
+
* reverse('a') // "a"
|
|
363
|
+
* reverse('123') // "321"
|
|
364
|
+
* reverse('hello world') // "dlrow olleh"
|
|
365
|
+
*
|
|
366
|
+
* @edge-cases
|
|
367
|
+
* - Empty string returns empty string
|
|
368
|
+
* - Single character returns same character
|
|
369
|
+
*/
|
|
370
|
+
export function reverse(str) {
|
|
371
|
+
return str.split('').reverse().join('');
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Checks if a string starts with a given prefix
|
|
375
|
+
*
|
|
376
|
+
* Case-sensitive by default. Wrapper with optional case-insensitivity.
|
|
377
|
+
*
|
|
378
|
+
* @param str - The string to search
|
|
379
|
+
* @param prefix - The prefix to check for
|
|
380
|
+
* @param caseSensitive - If false, comparison is case-insensitive (default: true)
|
|
381
|
+
* @returns true if string starts with prefix
|
|
382
|
+
*
|
|
383
|
+
* @example
|
|
384
|
+
* startsWith('hello world', 'hello') // true
|
|
385
|
+
* startsWith('hello world', 'world') // false
|
|
386
|
+
* startsWith('Hello World', 'hello', false) // true
|
|
387
|
+
* startsWith('', '') // true
|
|
388
|
+
* startsWith('a', 'abc') // false
|
|
389
|
+
* startsWith('test.js', '.js') // false
|
|
390
|
+
*
|
|
391
|
+
* @edge-cases
|
|
392
|
+
* - Empty prefix always returns true
|
|
393
|
+
* - Empty string only matches empty prefix
|
|
394
|
+
* - Case-insensitive comparison lowercases both sides
|
|
395
|
+
*/
|
|
396
|
+
export function startsWith(str, prefix, caseSensitive = true) {
|
|
397
|
+
if (caseSensitive) {
|
|
398
|
+
return str.startsWith(prefix);
|
|
399
|
+
}
|
|
400
|
+
return str.toLowerCase().startsWith(prefix.toLowerCase());
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Checks if a string ends with a given suffix
|
|
404
|
+
*
|
|
405
|
+
* Case-sensitive by default. Wrapper with optional case-insensitivity.
|
|
406
|
+
*
|
|
407
|
+
* @param str - The string to search
|
|
408
|
+
* @param suffix - The suffix to check for
|
|
409
|
+
* @param caseSensitive - If false, comparison is case-insensitive (default: true)
|
|
410
|
+
* @returns true if string ends with suffix
|
|
411
|
+
*
|
|
412
|
+
* @example
|
|
413
|
+
* endsWith('hello world', 'world') // true
|
|
414
|
+
* endsWith('hello world', 'hello') // false
|
|
415
|
+
* endsWith('test.js', '.js') // true
|
|
416
|
+
* endsWith('Test.JS', '.js', false) // true
|
|
417
|
+
* endsWith('', '') // true
|
|
418
|
+
* endsWith('a', 'abc') // false
|
|
419
|
+
*
|
|
420
|
+
* @edge-cases
|
|
421
|
+
* - Empty suffix always returns true
|
|
422
|
+
* - Empty string only matches empty suffix
|
|
423
|
+
* - Case-insensitive comparison lowercases both sides
|
|
424
|
+
*/
|
|
425
|
+
export function endsWith(str, suffix, caseSensitive = true) {
|
|
426
|
+
if (caseSensitive) {
|
|
427
|
+
return str.endsWith(suffix);
|
|
428
|
+
}
|
|
429
|
+
return str.toLowerCase().endsWith(suffix.toLowerCase());
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Checks if a string includes a given substring
|
|
433
|
+
*
|
|
434
|
+
* Case-sensitive by default. Wrapper with optional case-insensitivity.
|
|
435
|
+
*
|
|
436
|
+
* @param str - The string to search
|
|
437
|
+
* @param search - The substring to look for
|
|
438
|
+
* @param caseSensitive - If false, comparison is case-insensitive (default: true)
|
|
439
|
+
* @returns true if substring is found
|
|
440
|
+
*
|
|
441
|
+
* @example
|
|
442
|
+
* includes('hello world', 'lo wo') // true
|
|
443
|
+
* includes('hello world', 'xyz') // false
|
|
444
|
+
* includes('Hello World', 'world', false) // true
|
|
445
|
+
* includes('', '') // true
|
|
446
|
+
* includes('test', '') // true
|
|
447
|
+
* includes('JavaScript', 'Script', false) // true
|
|
448
|
+
*
|
|
449
|
+
* @edge-cases
|
|
450
|
+
* - Empty search string always returns true
|
|
451
|
+
* - Empty string only matches empty search
|
|
452
|
+
* - Case-insensitive comparison lowercases both sides
|
|
453
|
+
*/
|
|
454
|
+
export function includes(str, search, caseSensitive = true) {
|
|
455
|
+
if (caseSensitive) {
|
|
456
|
+
return str.includes(search);
|
|
457
|
+
}
|
|
458
|
+
return str.toLowerCase().includes(search.toLowerCase());
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Replaces all occurrences of a string pattern
|
|
462
|
+
*
|
|
463
|
+
* Works with string or regex patterns. Simpler than native replace for all occurrences.
|
|
464
|
+
*
|
|
465
|
+
* @param str - The string to search in
|
|
466
|
+
* @param search - String or regex to search for
|
|
467
|
+
* @param replacement - String or function to replace with
|
|
468
|
+
* @returns The modified string
|
|
469
|
+
*
|
|
470
|
+
* @example
|
|
471
|
+
* replaceAll('hello hello', 'hello', 'hi') // "hi hi"
|
|
472
|
+
* replaceAll('a-b-c', '-', ' ') // "a b c"
|
|
473
|
+
* replaceAll('test test test', 'test', 'result') // "result result result"
|
|
474
|
+
* replaceAll('', 'x', 'y') // ""
|
|
475
|
+
* replaceAll('abc', 'x', 'y') // "abc"
|
|
476
|
+
* replaceAll('aaa', 'a', 'b') // "bbb"
|
|
477
|
+
*
|
|
478
|
+
* @edge-cases
|
|
479
|
+
* - Empty search string doesn't replace
|
|
480
|
+
* - Empty replacement removes the pattern
|
|
481
|
+
* - Can use regex with global flag
|
|
482
|
+
*/
|
|
483
|
+
export function replaceAll(str, search, replacement) {
|
|
484
|
+
if (!str || !search)
|
|
485
|
+
return str;
|
|
486
|
+
if (typeof search === 'string') {
|
|
487
|
+
if (!search)
|
|
488
|
+
return str;
|
|
489
|
+
return str.split(search).join(typeof replacement === 'string' ? replacement : '');
|
|
490
|
+
}
|
|
491
|
+
// For regex, ensure global flag is set
|
|
492
|
+
const flags = search.global ? search.flags : search.flags + 'g';
|
|
493
|
+
const regex = new RegExp(search.source, flags);
|
|
494
|
+
return str.replace(regex, replacement);
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Splits a string by a delimiter with optional trimming
|
|
498
|
+
*
|
|
499
|
+
* Useful for parsing comma-separated values and other delimited data.
|
|
500
|
+
*
|
|
501
|
+
* @param str - The string to split
|
|
502
|
+
* @param delimiter - The delimiter to split on
|
|
503
|
+
* @param trim - If true, trim each part (default: false)
|
|
504
|
+
* @returns Array of string parts
|
|
505
|
+
*
|
|
506
|
+
* @example
|
|
507
|
+
* splitStr('a,b,c', ',') // ["a", "b", "c"]
|
|
508
|
+
* splitStr('a, b, c', ',', true) // ["a", "b", "c"]
|
|
509
|
+
* splitStr('a|b|c', '|') // ["a", "b", "c"]
|
|
510
|
+
* splitStr('', ',') // [""]
|
|
511
|
+
* splitStr('abc', ',') // ["abc"]
|
|
512
|
+
* splitStr('a b c', ' ') // ["a", "b", "c"]
|
|
513
|
+
*
|
|
514
|
+
* @edge-cases
|
|
515
|
+
* - Empty delimiter returns array with original string
|
|
516
|
+
* - Empty string returns array with empty string
|
|
517
|
+
* - Consecutive delimiters create empty elements
|
|
518
|
+
*/
|
|
519
|
+
export function splitStr(str, delimiter, trim = false) {
|
|
520
|
+
if (!delimiter)
|
|
521
|
+
return [str];
|
|
522
|
+
const parts = str.split(delimiter);
|
|
523
|
+
return trim ? parts.map(part => part.trim()) : parts;
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Joins an array of strings with a delimiter
|
|
527
|
+
*
|
|
528
|
+
* Enhanced version of Array.join with filtering options.
|
|
529
|
+
*
|
|
530
|
+
* @param parts - Array of strings to join
|
|
531
|
+
* @param delimiter - The delimiter to use
|
|
532
|
+
* @param filterEmpty - If true, remove empty strings (default: false)
|
|
533
|
+
* @returns The joined string
|
|
534
|
+
*
|
|
535
|
+
* @example
|
|
536
|
+
* joinStr(['a', 'b', 'c'], ',') // "a,b,c"
|
|
537
|
+
* joinStr(['a', '', 'c'], ',') // "a,,c"
|
|
538
|
+
* joinStr(['a', '', 'c'], ',', true) // "a,c"
|
|
539
|
+
* joinStr([], ',') // ""
|
|
540
|
+
* joinStr(['single'], ',') // "single"
|
|
541
|
+
* joinStr(['path', 'to', 'file'], '/') // "path/to/file"
|
|
542
|
+
*
|
|
543
|
+
* @edge-cases
|
|
544
|
+
* - Empty array returns empty string
|
|
545
|
+
* - Single element array returns that element
|
|
546
|
+
* - Filter empty removes all empty strings before joining
|
|
547
|
+
*/
|
|
548
|
+
export function joinStr(parts, delimiter, filterEmpty = false) {
|
|
549
|
+
const filtered = filterEmpty ? parts.filter(p => p.length > 0) : parts;
|
|
550
|
+
return filtered.join(delimiter);
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Counts occurrences of a substring
|
|
554
|
+
*
|
|
555
|
+
* Useful for validation and analysis of string content.
|
|
556
|
+
*
|
|
557
|
+
* @param str - The string to search in
|
|
558
|
+
* @param search - The substring to count
|
|
559
|
+
* @param caseSensitive - If false, comparison is case-insensitive (default: true)
|
|
560
|
+
* @returns Number of occurrences
|
|
561
|
+
*
|
|
562
|
+
* @example
|
|
563
|
+
* countOccurrences('hello hello world', 'hello') // 2
|
|
564
|
+
* countOccurrences('aaa', 'a') // 3
|
|
565
|
+
* countOccurrences('test', 'x') // 0
|
|
566
|
+
* countOccurrences('', 'x') // 0
|
|
567
|
+
* countOccurrences('Hello HELLO hello', 'hello', false) // 3
|
|
568
|
+
* countOccurrences('ababab', 'ab') // 3
|
|
569
|
+
*
|
|
570
|
+
* @edge-cases
|
|
571
|
+
* - Empty search string returns 0
|
|
572
|
+
* - Empty string returns 0
|
|
573
|
+
* - Case-insensitive counts all variations
|
|
574
|
+
*/
|
|
575
|
+
export function countOccurrences(str, search, caseSensitive = true) {
|
|
576
|
+
if (!str || !search)
|
|
577
|
+
return 0;
|
|
578
|
+
const searchStr = caseSensitive ? search : search.toLowerCase();
|
|
579
|
+
const compareStr = caseSensitive ? str : str.toLowerCase();
|
|
580
|
+
const matches = compareStr.match(new RegExp(searchStr.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'));
|
|
581
|
+
return matches ? matches.length : 0;
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* String interpolation helper
|
|
585
|
+
*
|
|
586
|
+
* Replaces {key} patterns with provided values.
|
|
587
|
+
* Useful for templates and dynamic strings.
|
|
588
|
+
*
|
|
589
|
+
* @param template - Template string with {key} placeholders
|
|
590
|
+
* @param values - Object with key-value pairs
|
|
591
|
+
* @returns The interpolated string
|
|
592
|
+
*
|
|
593
|
+
* @example
|
|
594
|
+
* interpolate('Hello {name}!', { name: 'World' }) // "Hello World!"
|
|
595
|
+
* interpolate('{x} + {y} = {z}', { x: '1', y: '2', z: '3' }) // "1 + 2 = 3"
|
|
596
|
+
* interpolate('No placeholders', {}) // "No placeholders"
|
|
597
|
+
* interpolate('Missing {value}', {}) // "Missing {value}"
|
|
598
|
+
* interpolate('', {}) // ""
|
|
599
|
+
*
|
|
600
|
+
* @edge-cases
|
|
601
|
+
* - Missing keys leave placeholder unchanged
|
|
602
|
+
* - Nested objects converted via toString()
|
|
603
|
+
* - Undefined values become "undefined"
|
|
604
|
+
*/
|
|
605
|
+
export function interpolate(template, values) {
|
|
606
|
+
return template.replace(/{(\w+)}/g, (match, key) => {
|
|
607
|
+
return key in values ? String(values[key]) : match;
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Removes leading and trailing whitespace
|
|
612
|
+
*
|
|
613
|
+
* Enhanced trim with custom character removal.
|
|
614
|
+
*
|
|
615
|
+
* @param str - The string to trim
|
|
616
|
+
* @param chars - Characters to remove (default: whitespace)
|
|
617
|
+
* @returns The trimmed string
|
|
618
|
+
*
|
|
619
|
+
* @example
|
|
620
|
+
* trimStr(' hello ') // "hello"
|
|
621
|
+
* trimStr('xxxhelloxxx', 'x') // "hello"
|
|
622
|
+
* trimStr(' hello ') // "hello"
|
|
623
|
+
* trimStr('') // ""
|
|
624
|
+
* trimStr('---test---', '-') // "test"
|
|
625
|
+
*
|
|
626
|
+
* @edge-cases
|
|
627
|
+
* - Empty string returns empty string
|
|
628
|
+
* - Only specified characters are removed from edges
|
|
629
|
+
*/
|
|
630
|
+
export function trimStr(str, chars) {
|
|
631
|
+
if (!str)
|
|
632
|
+
return '';
|
|
633
|
+
if (!chars) {
|
|
634
|
+
return str.trim();
|
|
635
|
+
}
|
|
636
|
+
const charSet = new Set(chars);
|
|
637
|
+
let start = 0;
|
|
638
|
+
let end = str.length;
|
|
639
|
+
while (start < end && charSet.has(str[start]))
|
|
640
|
+
start++;
|
|
641
|
+
while (end > start && charSet.has(str[end - 1]))
|
|
642
|
+
end--;
|
|
643
|
+
return str.slice(start, end);
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* Wraps text to fit within a specified width
|
|
647
|
+
*
|
|
648
|
+
* Intelligently breaks long lines while preserving word integrity.
|
|
649
|
+
* Useful for terminal output and formatted text generation.
|
|
650
|
+
*
|
|
651
|
+
* @param str - The string to wrap
|
|
652
|
+
* @param options - Wrapping options
|
|
653
|
+
* @returns The wrapped string
|
|
654
|
+
*
|
|
655
|
+
* @example
|
|
656
|
+
* wrap('hello world this is a test', { width: 10 })
|
|
657
|
+
* // "hello\nworld this\nis a test"
|
|
658
|
+
*
|
|
659
|
+
* wrap('a very long line here', { width: 10, indent: ' ' })
|
|
660
|
+
* // "a very\n long line\n here"
|
|
661
|
+
*
|
|
662
|
+
* wrap('word1 word2 word3', { width: 10, wordBoundary: true })
|
|
663
|
+
* // "word1\nword2\nword3"
|
|
664
|
+
*
|
|
665
|
+
* @edge-cases
|
|
666
|
+
* - Words longer than width are broken
|
|
667
|
+
* - Existing line breaks are preserved
|
|
668
|
+
*/
|
|
669
|
+
export function wrap(str, options = {}) {
|
|
670
|
+
const { width = 80, lineBreak = '\n', indent = '', wordBoundary = true } = options;
|
|
671
|
+
if (width <= 0)
|
|
672
|
+
return str;
|
|
673
|
+
const lines = str.split('\n');
|
|
674
|
+
return lines
|
|
675
|
+
.map(line => {
|
|
676
|
+
let result = '';
|
|
677
|
+
let currentLine = '';
|
|
678
|
+
const words = line.split(' ');
|
|
679
|
+
for (const word of words) {
|
|
680
|
+
if (currentLine.length + word.length + 1 > width) {
|
|
681
|
+
if (currentLine) {
|
|
682
|
+
result += currentLine + lineBreak;
|
|
683
|
+
currentLine = indent + word;
|
|
684
|
+
}
|
|
685
|
+
else {
|
|
686
|
+
// Word is longer than width
|
|
687
|
+
currentLine = indent + word;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
else {
|
|
691
|
+
currentLine = currentLine ? currentLine + ' ' + word : word;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
if (currentLine) {
|
|
695
|
+
result += currentLine;
|
|
696
|
+
}
|
|
697
|
+
return result;
|
|
698
|
+
})
|
|
699
|
+
.join(lineBreak);
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* Escapes special regex characters in a string
|
|
703
|
+
*
|
|
704
|
+
* Useful for creating regex patterns from user input.
|
|
705
|
+
*
|
|
706
|
+
* @param str - The string to escape
|
|
707
|
+
* @returns The escaped string safe for regex
|
|
708
|
+
*
|
|
709
|
+
* @example
|
|
710
|
+
* escapeRegex('(test)') // "\\(test\\)"
|
|
711
|
+
* escapeRegex('[a-z]') // "\\[a-z\\]"
|
|
712
|
+
* escapeRegex('price: $5.00') // "price: \\$5\\.00"
|
|
713
|
+
* escapeRegex('') // ""
|
|
714
|
+
*
|
|
715
|
+
* @edge-cases
|
|
716
|
+
* - All regex special characters are escaped
|
|
717
|
+
* - Empty string returns empty string
|
|
718
|
+
*/
|
|
719
|
+
export function escapeRegex(str) {
|
|
720
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* Strips HTML tags from a string
|
|
724
|
+
*
|
|
725
|
+
* Simple tag removal; for complex HTML use a proper parser.
|
|
726
|
+
*
|
|
727
|
+
* @param str - The string with HTML tags
|
|
728
|
+
* @returns The text content without tags
|
|
729
|
+
*
|
|
730
|
+
* @example
|
|
731
|
+
* stripHtml('<p>Hello <b>World</b></p>') // "Hello World"
|
|
732
|
+
* stripHtml('No tags here') // "No tags here"
|
|
733
|
+
* stripHtml('<script>alert("xss")</script>') // "alert("xss")"
|
|
734
|
+
* stripHtml('') // ""
|
|
735
|
+
*
|
|
736
|
+
* @edge-cases
|
|
737
|
+
* - Doesn't validate HTML
|
|
738
|
+
* - Removes content inside tags
|
|
739
|
+
* - For security-sensitive use, use htmlspecialchars
|
|
740
|
+
*/
|
|
741
|
+
export function stripHtml(str) {
|
|
742
|
+
if (!str)
|
|
743
|
+
return '';
|
|
744
|
+
return str.replace(/<[^>]*>/g, '');
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* Escapes HTML special characters
|
|
748
|
+
*
|
|
749
|
+
* Makes text safe for insertion into HTML.
|
|
750
|
+
*
|
|
751
|
+
* @param str - The string to escape
|
|
752
|
+
* @returns The escaped string
|
|
753
|
+
*
|
|
754
|
+
* @example
|
|
755
|
+
* escapeHtml('<script>') // "<script>"
|
|
756
|
+
* escapeHtml('Hello & Goodbye') // "Hello & Goodbye"
|
|
757
|
+
* escapeHtml('Price: $5.00') // "Price: $5.00"
|
|
758
|
+
* escapeHtml('') // ""
|
|
759
|
+
* escapeHtml('"quoted"') // ""quoted""
|
|
760
|
+
*
|
|
761
|
+
* @edge-cases
|
|
762
|
+
* - Critical for preventing XSS vulnerabilities
|
|
763
|
+
* - Converts &, <, >, ", '
|
|
764
|
+
*/
|
|
765
|
+
export function escapeHtml(str) {
|
|
766
|
+
if (!str)
|
|
767
|
+
return '';
|
|
768
|
+
const map = {
|
|
769
|
+
'&': '&',
|
|
770
|
+
'<': '<',
|
|
771
|
+
'>': '>',
|
|
772
|
+
'"': '"',
|
|
773
|
+
"'": ''',
|
|
774
|
+
};
|
|
775
|
+
return str.replace(/[&<>"']/g, char => map[char]);
|
|
776
|
+
}
|
|
777
|
+
/**
|
|
778
|
+
* Unescapes HTML special characters
|
|
779
|
+
*
|
|
780
|
+
* Reverses escapeHtml transformation.
|
|
781
|
+
*
|
|
782
|
+
* @param str - The escaped HTML string
|
|
783
|
+
* @returns The unescaped string
|
|
784
|
+
*
|
|
785
|
+
* @example
|
|
786
|
+
* unescapeHtml('<script>') // "<script>"
|
|
787
|
+
* unescapeHtml('Hello & Goodbye') // "Hello & Goodbye"
|
|
788
|
+
* unescapeHtml('"quoted"') // ""quoted""
|
|
789
|
+
*/
|
|
790
|
+
export function unescapeHtml(str) {
|
|
791
|
+
if (!str)
|
|
792
|
+
return '';
|
|
793
|
+
const map = {
|
|
794
|
+
'&': '&',
|
|
795
|
+
'<': '<',
|
|
796
|
+
'>': '>',
|
|
797
|
+
'"': '"',
|
|
798
|
+
''': "'",
|
|
799
|
+
};
|
|
800
|
+
let result = str;
|
|
801
|
+
for (const [entity, char] of Object.entries(map)) {
|
|
802
|
+
result = result.replace(new RegExp(entity, 'g'), char);
|
|
803
|
+
}
|
|
804
|
+
return result;
|
|
805
|
+
}
|
|
806
|
+
//# sourceMappingURL=string-formatter.js.map
|