@gzl10/ts-helpers 4.2.1
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 +320 -0
- package/README.md +233 -0
- package/USAGE-GUIDE.md +800 -0
- package/dist/browser/async.js +15 -0
- package/dist/browser/async.js.map +1 -0
- package/dist/browser/chunk-4O7ZPIJN.js +383 -0
- package/dist/browser/chunk-4O7ZPIJN.js.map +1 -0
- package/dist/browser/chunk-75XNTC34.js +60 -0
- package/dist/browser/chunk-75XNTC34.js.map +1 -0
- package/dist/browser/chunk-C3D7YZVE.js +299 -0
- package/dist/browser/chunk-C3D7YZVE.js.map +1 -0
- package/dist/browser/chunk-CZL6C2EI.js +452 -0
- package/dist/browser/chunk-CZL6C2EI.js.map +1 -0
- package/dist/browser/chunk-D4FZFIVA.js +240 -0
- package/dist/browser/chunk-D4FZFIVA.js.map +1 -0
- package/dist/browser/chunk-IL7NG7IC.js +72 -0
- package/dist/browser/chunk-IL7NG7IC.js.map +1 -0
- package/dist/browser/chunk-NSBPE2FW.js +17 -0
- package/dist/browser/chunk-NSBPE2FW.js.map +1 -0
- package/dist/browser/chunk-SLQVNPTH.js +27 -0
- package/dist/browser/chunk-SLQVNPTH.js.map +1 -0
- package/dist/browser/chunk-WG7ILCUB.js +195 -0
- package/dist/browser/chunk-WG7ILCUB.js.map +1 -0
- package/dist/browser/chunk-WJA4JDMZ.js +278 -0
- package/dist/browser/chunk-WJA4JDMZ.js.map +1 -0
- package/dist/browser/chunk-ZFVYLUTT.js +65 -0
- package/dist/browser/chunk-ZFVYLUTT.js.map +1 -0
- package/dist/browser/chunk-ZYTSVMTI.js +263 -0
- package/dist/browser/chunk-ZYTSVMTI.js.map +1 -0
- package/dist/browser/dates.js +78 -0
- package/dist/browser/dates.js.map +1 -0
- package/dist/browser/environment-detection.js +21 -0
- package/dist/browser/environment-detection.js.map +1 -0
- package/dist/browser/environment.js +34 -0
- package/dist/browser/environment.js.map +1 -0
- package/dist/browser/errors.js +18 -0
- package/dist/browser/errors.js.map +1 -0
- package/dist/browser/index.js +412 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/math.js +51 -0
- package/dist/browser/math.js.map +1 -0
- package/dist/browser/number.js +10 -0
- package/dist/browser/number.js.map +1 -0
- package/dist/browser/objects.js +31 -0
- package/dist/browser/objects.js.map +1 -0
- package/dist/browser/strings.js +80 -0
- package/dist/browser/strings.js.map +1 -0
- package/dist/browser/validation-core.js +54 -0
- package/dist/browser/validation-core.js.map +1 -0
- package/dist/browser/validation-crypto.js +28 -0
- package/dist/browser/validation-crypto.js.map +1 -0
- package/dist/browser/validators.js +98 -0
- package/dist/browser/validators.js.map +1 -0
- package/dist/cjs/async.js +86 -0
- package/dist/cjs/async.js.map +1 -0
- package/dist/cjs/dates.js +285 -0
- package/dist/cjs/dates.js.map +1 -0
- package/dist/cjs/environment-detection.js +84 -0
- package/dist/cjs/environment-detection.js.map +1 -0
- package/dist/cjs/environment.js +261 -0
- package/dist/cjs/environment.js.map +1 -0
- package/dist/cjs/errors.js +80 -0
- package/dist/cjs/errors.js.map +1 -0
- package/dist/cjs/index.js +2035 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/math.js +388 -0
- package/dist/cjs/math.js.map +1 -0
- package/dist/cjs/number.js +37 -0
- package/dist/cjs/number.js.map +1 -0
- package/dist/cjs/objects.js +249 -0
- package/dist/cjs/objects.js.map +1 -0
- package/dist/cjs/strings.js +253 -0
- package/dist/cjs/strings.js.map +1 -0
- package/dist/cjs/validation.js +450 -0
- package/dist/cjs/validation.js.map +1 -0
- package/dist/esm/async.js +15 -0
- package/dist/esm/async.js.map +1 -0
- package/dist/esm/chunk-4O7ZPIJN.js +383 -0
- package/dist/esm/chunk-4O7ZPIJN.js.map +1 -0
- package/dist/esm/chunk-75XNTC34.js +60 -0
- package/dist/esm/chunk-75XNTC34.js.map +1 -0
- package/dist/esm/chunk-BDOBKBKA.js +72 -0
- package/dist/esm/chunk-BDOBKBKA.js.map +1 -0
- package/dist/esm/chunk-C3D7YZVE.js +299 -0
- package/dist/esm/chunk-C3D7YZVE.js.map +1 -0
- package/dist/esm/chunk-CZL6C2EI.js +452 -0
- package/dist/esm/chunk-CZL6C2EI.js.map +1 -0
- package/dist/esm/chunk-EBLSTOEC.js +263 -0
- package/dist/esm/chunk-EBLSTOEC.js.map +1 -0
- package/dist/esm/chunk-NSBPE2FW.js +17 -0
- package/dist/esm/chunk-NSBPE2FW.js.map +1 -0
- package/dist/esm/chunk-SLQVNPTH.js +27 -0
- package/dist/esm/chunk-SLQVNPTH.js.map +1 -0
- package/dist/esm/chunk-WG7ILCUB.js +195 -0
- package/dist/esm/chunk-WG7ILCUB.js.map +1 -0
- package/dist/esm/chunk-WJA4JDMZ.js +278 -0
- package/dist/esm/chunk-WJA4JDMZ.js.map +1 -0
- package/dist/esm/chunk-ZFVYLUTT.js +65 -0
- package/dist/esm/chunk-ZFVYLUTT.js.map +1 -0
- package/dist/esm/dates.js +78 -0
- package/dist/esm/dates.js.map +1 -0
- package/dist/esm/environment-detection.js +21 -0
- package/dist/esm/environment-detection.js.map +1 -0
- package/dist/esm/environment.js +34 -0
- package/dist/esm/environment.js.map +1 -0
- package/dist/esm/errors.js +18 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/index.js +380 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/math.js +51 -0
- package/dist/esm/math.js.map +1 -0
- package/dist/esm/number.js +10 -0
- package/dist/esm/number.js.map +1 -0
- package/dist/esm/objects.js +31 -0
- package/dist/esm/objects.js.map +1 -0
- package/dist/esm/strings.js +80 -0
- package/dist/esm/strings.js.map +1 -0
- package/dist/esm/validation.js +54 -0
- package/dist/esm/validation.js.map +1 -0
- package/dist/node/async.js +93 -0
- package/dist/node/async.js.map +1 -0
- package/dist/node/csv.js +102 -0
- package/dist/node/csv.js.map +1 -0
- package/dist/node/data.js +880 -0
- package/dist/node/data.js.map +1 -0
- package/dist/node/dates.js +324 -0
- package/dist/node/dates.js.map +1 -0
- package/dist/node/environment.js +278 -0
- package/dist/node/environment.js.map +1 -0
- package/dist/node/errors.js +89 -0
- package/dist/node/errors.js.map +1 -0
- package/dist/node/index.js +3151 -0
- package/dist/node/index.js.map +1 -0
- package/dist/node/json.js +107 -0
- package/dist/node/json.js.map +1 -0
- package/dist/node/math.js +413 -0
- package/dist/node/math.js.map +1 -0
- package/dist/node/number.js +42 -0
- package/dist/node/number.js.map +1 -0
- package/dist/node/objects.js +264 -0
- package/dist/node/objects.js.map +1 -0
- package/dist/node/strings.js +293 -0
- package/dist/node/strings.js.map +1 -0
- package/dist/node/tree.js +89 -0
- package/dist/node/tree.js.map +1 -0
- package/dist/node/validation-core.js +477 -0
- package/dist/node/validation-core.js.map +1 -0
- package/dist/node/validation-crypto.js +179 -0
- package/dist/node/validation-crypto.js.map +1 -0
- package/dist/node/validation.js +677 -0
- package/dist/node/validation.js.map +1 -0
- package/dist/node/validators.js +123 -0
- package/dist/node/validators.js.map +1 -0
- package/dist/node-esm/async.js +15 -0
- package/dist/node-esm/async.js.map +1 -0
- package/dist/node-esm/chunk-3YOF7NPT.js +299 -0
- package/dist/node-esm/chunk-3YOF7NPT.js.map +1 -0
- package/dist/node-esm/chunk-64TBXJQS.js +263 -0
- package/dist/node-esm/chunk-64TBXJQS.js.map +1 -0
- package/dist/node-esm/chunk-75XNTC34.js +60 -0
- package/dist/node-esm/chunk-75XNTC34.js.map +1 -0
- package/dist/node-esm/chunk-C4PKXIPB.js +278 -0
- package/dist/node-esm/chunk-C4PKXIPB.js.map +1 -0
- package/dist/node-esm/chunk-CMDFZME3.js +452 -0
- package/dist/node-esm/chunk-CMDFZME3.js.map +1 -0
- package/dist/node-esm/chunk-DZZPUYMP.js +74 -0
- package/dist/node-esm/chunk-DZZPUYMP.js.map +1 -0
- package/dist/node-esm/chunk-HTSEHRHI.js +195 -0
- package/dist/node-esm/chunk-HTSEHRHI.js.map +1 -0
- package/dist/node-esm/chunk-JCAUVOPH.js +27 -0
- package/dist/node-esm/chunk-JCAUVOPH.js.map +1 -0
- package/dist/node-esm/chunk-KBHE3K2F.js +505 -0
- package/dist/node-esm/chunk-KBHE3K2F.js.map +1 -0
- package/dist/node-esm/chunk-LYTET5NX.js +65 -0
- package/dist/node-esm/chunk-LYTET5NX.js.map +1 -0
- package/dist/node-esm/chunk-PZ5AY32C.js +10 -0
- package/dist/node-esm/chunk-PZ5AY32C.js.map +1 -0
- package/dist/node-esm/chunk-UKGXL2QO.js +383 -0
- package/dist/node-esm/chunk-UKGXL2QO.js.map +1 -0
- package/dist/node-esm/chunk-XAEYT23H.js +164 -0
- package/dist/node-esm/chunk-XAEYT23H.js.map +1 -0
- package/dist/node-esm/csv.js +63 -0
- package/dist/node-esm/csv.js.map +1 -0
- package/dist/node-esm/data.js +32 -0
- package/dist/node-esm/data.js.map +1 -0
- package/dist/node-esm/dates.js +78 -0
- package/dist/node-esm/dates.js.map +1 -0
- package/dist/node-esm/environment.js +34 -0
- package/dist/node-esm/environment.js.map +1 -0
- package/dist/node-esm/errors.js +18 -0
- package/dist/node-esm/errors.js.map +1 -0
- package/dist/node-esm/index.js +426 -0
- package/dist/node-esm/index.js.map +1 -0
- package/dist/node-esm/json.js +68 -0
- package/dist/node-esm/json.js.map +1 -0
- package/dist/node-esm/math.js +51 -0
- package/dist/node-esm/math.js.map +1 -0
- package/dist/node-esm/number.js +10 -0
- package/dist/node-esm/number.js.map +1 -0
- package/dist/node-esm/objects.js +31 -0
- package/dist/node-esm/objects.js.map +1 -0
- package/dist/node-esm/strings.js +80 -0
- package/dist/node-esm/strings.js.map +1 -0
- package/dist/node-esm/tree.js +8 -0
- package/dist/node-esm/tree.js.map +1 -0
- package/dist/node-esm/validation-core.js +54 -0
- package/dist/node-esm/validation-core.js.map +1 -0
- package/dist/node-esm/validation-crypto.js +26 -0
- package/dist/node-esm/validation-crypto.js.map +1 -0
- package/dist/node-esm/validation.js +606 -0
- package/dist/node-esm/validation.js.map +1 -0
- package/dist/node-esm/validators.js +98 -0
- package/dist/node-esm/validators.js.map +1 -0
- package/dist/types/async-C8gvbSG-.d.ts +453 -0
- package/dist/types/async.d.ts +1 -0
- package/dist/types/csv.d.ts +226 -0
- package/dist/types/data.d.ts +1561 -0
- package/dist/types/dates-hTiE0Z11.d.ts +298 -0
- package/dist/types/dates.d.ts +1 -0
- package/dist/types/environment-B8eLS7KT.d.ts +420 -0
- package/dist/types/environment-detection.d.ts +102 -0
- package/dist/types/environment.d.ts +1 -0
- package/dist/types/errors.d.ts +147 -0
- package/dist/types/index.d.ts +211 -0
- package/dist/types/json.d.ts +284 -0
- package/dist/types/math-BQ9Lwdp7.d.ts +2060 -0
- package/dist/types/math.d.ts +1 -0
- package/dist/types/number-CYnQfLWj.d.ts +44 -0
- package/dist/types/number.d.ts +1 -0
- package/dist/types/objects-BohS8GCS.d.ts +1185 -0
- package/dist/types/objects.d.ts +1 -0
- package/dist/types/strings-CiqRPYLL.d.ts +1349 -0
- package/dist/types/strings.d.ts +1 -0
- package/dist/types/tree.d.ts +284 -0
- package/dist/types/validation-core-DfHF8rCG.d.ts +238 -0
- package/dist/types/validation-crypto-browser.d.ts +56 -0
- package/dist/types/validation-crypto-node.d.ts +31 -0
- package/dist/types/validation.d.ts +1 -0
- package/dist/types/validators.d.ts +216 -0
- package/package.json +253 -0
|
@@ -0,0 +1,1349 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* String manipulation utilities
|
|
3
|
+
* Consolidated from primitives/string module
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Sanitizes a string by removing special characters and converting to lowercase with dashes
|
|
7
|
+
*
|
|
8
|
+
* Produces clean, URL-friendly strings by removing special characters and normalizing whitespace.
|
|
9
|
+
* Useful for slugs, IDs, CSS class names, and filename generation.
|
|
10
|
+
*
|
|
11
|
+
* Transformation rules:
|
|
12
|
+
* 1. Remove all non-alphanumeric characters except spaces
|
|
13
|
+
* 2. Remove additional punctuation (`~!@#$%^&*()_|+-=?;:'",.<>{}[]\\/`)
|
|
14
|
+
* 3. Replace spaces with dashes
|
|
15
|
+
* 4. Convert to lowercase
|
|
16
|
+
*
|
|
17
|
+
* @param instr - Input string to sanitize (null returns empty string)
|
|
18
|
+
* @returns Sanitized string in lowercase with dashes replacing spaces
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* // Basic sanitization
|
|
23
|
+
* sanitizeString('Hello World!') // 'hello-world'
|
|
24
|
+
* sanitizeString('user@example.com') // 'userexamplecom'
|
|
25
|
+
* sanitizeString('Price: $19.99') // 'price-1999'
|
|
26
|
+
*
|
|
27
|
+
* // Remove accents and special chars
|
|
28
|
+
* sanitizeString('Café Münchën') // 'caf-mnchen'
|
|
29
|
+
* sanitizeString('北京 2024') // '2024'
|
|
30
|
+
*
|
|
31
|
+
* // Multiple spaces/dashes
|
|
32
|
+
* sanitizeString('hello world') // 'hello---world'
|
|
33
|
+
* sanitizeString('foo-bar_baz') // 'foo-barbaz'
|
|
34
|
+
*
|
|
35
|
+
* // Edge cases
|
|
36
|
+
* sanitizeString(null) // ''
|
|
37
|
+
* sanitizeString('') // ''
|
|
38
|
+
* sanitizeString(' ') // '---'
|
|
39
|
+
* sanitizeString('123') // '123'
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* // Real-world: Generate CSS class names from user input
|
|
45
|
+
* function generateClassName(userInput: string): string {
|
|
46
|
+
* return `custom-${sanitizeString(userInput)}`
|
|
47
|
+
* }
|
|
48
|
+
*
|
|
49
|
+
* generateClassName('My Component!') // 'custom-my-component'
|
|
50
|
+
* generateClassName('Button (Primary)') // 'custom-button-primary'
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```typescript
|
|
55
|
+
* // Real-world: Clean product names for file export
|
|
56
|
+
* const products = [
|
|
57
|
+
* { name: 'T-Shirt (Blue)', sku: 'TS-001' },
|
|
58
|
+
* { name: 'Shoes & Accessories', sku: 'SH-042' }
|
|
59
|
+
* ]
|
|
60
|
+
*
|
|
61
|
+
* products.forEach(p => {
|
|
62
|
+
* const filename = `${sanitizeString(p.name)}-${p.sku}.json`
|
|
63
|
+
* console.log(filename)
|
|
64
|
+
* // 't-shirt-blue-TS-001.json'
|
|
65
|
+
* // 'shoes-accessories-SH-042.json'
|
|
66
|
+
* })
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
* @see {@link toUrlSlug} for more sophisticated URL slug generation with accent removal
|
|
70
|
+
* @see {@link toKebabCase} for case conversion to kebab-case
|
|
71
|
+
*/
|
|
72
|
+
declare const sanitizeString: (instr: string | null) => string;
|
|
73
|
+
/**
|
|
74
|
+
* Removes JSON-like characters and HTML entities from a string
|
|
75
|
+
* @param texto Input string to clean
|
|
76
|
+
* @returns String with JSON characters and HTML entities removed
|
|
77
|
+
*/
|
|
78
|
+
declare const cleanJsonChars: (texto: string | null) => string;
|
|
79
|
+
/**
|
|
80
|
+
* Truncates a string to a specified maximum length with optional suffix
|
|
81
|
+
*
|
|
82
|
+
* Shortens long strings for display in UI components, tables, previews, and tooltips.
|
|
83
|
+
* Preserves readability while fitting space constraints.
|
|
84
|
+
*
|
|
85
|
+
* Behavior:
|
|
86
|
+
* - If string length ≤ maxlength: returns original string unchanged
|
|
87
|
+
* - If string length > maxlength: returns `str.substring(0, maxlength) + suffix`
|
|
88
|
+
* - Suffix is included in addition to maxlength (not counted within limit)
|
|
89
|
+
* - Null/empty strings return empty string
|
|
90
|
+
*
|
|
91
|
+
* @param str - String to truncate (null/empty returns '')
|
|
92
|
+
* @param maxlength - Maximum length before truncation (default: 80 characters)
|
|
93
|
+
* @param suffix - Suffix to append when truncated (default: '...')
|
|
94
|
+
* @returns Truncated string with suffix if needed, original string if shorter
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```typescript
|
|
98
|
+
* // Basic truncation (default 80 chars)
|
|
99
|
+
* truncateString('Short text') // 'Short text' (no truncation)
|
|
100
|
+
*
|
|
101
|
+
* const long = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit'
|
|
102
|
+
* truncateString(long, 20) // 'Lorem ipsum dolor si...' (20 + 3 = 23 chars total)
|
|
103
|
+
*
|
|
104
|
+
* // Custom max length
|
|
105
|
+
* truncateString('Hello World', 5) // 'Hello...'
|
|
106
|
+
* truncateString('Hello World', 11) // 'Hello World' (no truncation)
|
|
107
|
+
*
|
|
108
|
+
* // Custom suffix
|
|
109
|
+
* truncateString('Long text here', 8, '…') // 'Long tex…'
|
|
110
|
+
* truncateString('Long text here', 8, ' [more]') // 'Long tex [more]'
|
|
111
|
+
* truncateString('Long text here', 8, '') // 'Long tex' (no suffix)
|
|
112
|
+
*
|
|
113
|
+
* // Edge cases
|
|
114
|
+
* truncateString('', 10) // ''
|
|
115
|
+
* truncateString(null as any, 10) // '' (graceful handling)
|
|
116
|
+
* truncateString('Hi', 0) // '...' (edge case: 0 maxlength)
|
|
117
|
+
* ```
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```typescript
|
|
121
|
+
* // Real-world: Truncate product descriptions in table
|
|
122
|
+
* const products = [
|
|
123
|
+
* { name: 'Laptop', description: 'High-performance laptop with 16GB RAM and 512GB SSD' },
|
|
124
|
+
* { name: 'Mouse', description: 'Wireless ergonomic mouse' }
|
|
125
|
+
* ]
|
|
126
|
+
*
|
|
127
|
+
* products.forEach(p => {
|
|
128
|
+
* console.log(`${p.name}: ${truncateString(p.description, 30)}`)
|
|
129
|
+
* })
|
|
130
|
+
* // Output:
|
|
131
|
+
* // Laptop: High-performance laptop with 1...
|
|
132
|
+
* // Mouse: Wireless ergonomic mouse
|
|
133
|
+
* ```
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```typescript
|
|
137
|
+
* // Real-world: Truncate user comments for preview
|
|
138
|
+
* function renderCommentPreview(comment: string): string {
|
|
139
|
+
* return `<div class="preview">${truncateString(comment, 100)}</div>`
|
|
140
|
+
* }
|
|
141
|
+
*
|
|
142
|
+
* renderCommentPreview('This is a very long comment that needs to be shortened for display...')
|
|
143
|
+
* // '<div class="preview">This is a very long comment that needs to be shortened for display in the UI without taking too m...</div>'
|
|
144
|
+
* ```
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* ```typescript
|
|
148
|
+
* // Real-world: Email subject line truncation
|
|
149
|
+
* function formatEmailSubject(subject: string): string {
|
|
150
|
+
* // Gmail displays ~60 chars on desktop
|
|
151
|
+
* return truncateString(subject, 60, '…')
|
|
152
|
+
* }
|
|
153
|
+
*
|
|
154
|
+
* formatEmailSubject('Re: Important meeting about Q4 planning and budget review')
|
|
155
|
+
* // 'Re: Important meeting about Q4 planning and budget review' (fits)
|
|
156
|
+
*
|
|
157
|
+
* formatEmailSubject('Re: [URGENT] Critical system outage affecting all production servers and databases')
|
|
158
|
+
* // 'Re: [URGENT] Critical system outage affecting all production…'
|
|
159
|
+
* ```
|
|
160
|
+
*
|
|
161
|
+
* @see {@link isEmpty} for checking empty strings
|
|
162
|
+
*/
|
|
163
|
+
declare const truncateString: (str: string, maxlength?: number, suffix?: string) => string;
|
|
164
|
+
/**
|
|
165
|
+
* Converts Unicode escape sequences to their actual characters
|
|
166
|
+
* @param input String containing Unicode escape sequences
|
|
167
|
+
* @returns String with escape sequences converted to actual characters
|
|
168
|
+
*/
|
|
169
|
+
declare const unescapeUnicode: (input: string) => string;
|
|
170
|
+
/**
|
|
171
|
+
* Ensures a string ends with a specific suffix, adding it if not present
|
|
172
|
+
* @param str Input string to check
|
|
173
|
+
* @param trailing Suffix string to ensure is present at the end
|
|
174
|
+
* @returns String guaranteed to end with the trailing string
|
|
175
|
+
*/
|
|
176
|
+
declare const ensureEndsWith: (str: string, trailing: string) => string;
|
|
177
|
+
/**
|
|
178
|
+
* Removes a specific suffix from the end of a string if present
|
|
179
|
+
* @param str Input string to process
|
|
180
|
+
* @param trailing Suffix string to remove from the end
|
|
181
|
+
* @returns String with trailing suffix removed, original if suffix not found
|
|
182
|
+
*/
|
|
183
|
+
declare const stripFromEnd: (str: string, trailing: string) => string;
|
|
184
|
+
/**
|
|
185
|
+
* Ensures a string starts with a specific prefix, adding it if not present
|
|
186
|
+
* @param str Input string to check
|
|
187
|
+
* @param leading Prefix string to ensure is present at the start
|
|
188
|
+
* @returns String guaranteed to start with the leading string
|
|
189
|
+
*/
|
|
190
|
+
declare const ensureStartsWith: (str: string, leading: string) => string;
|
|
191
|
+
/**
|
|
192
|
+
* Removes a specific prefix from the start of a string if present
|
|
193
|
+
* @param str Input string to process
|
|
194
|
+
* @param leading Prefix string to remove from the start
|
|
195
|
+
* @returns String with leading prefix removed, original if prefix not found
|
|
196
|
+
*/
|
|
197
|
+
declare const stripFromStart: (str: string, leading: string) => string;
|
|
198
|
+
/**
|
|
199
|
+
* Converts all characters in a string to lowercase
|
|
200
|
+
* @param str Input string to convert
|
|
201
|
+
* @returns String with all characters in lowercase
|
|
202
|
+
*/
|
|
203
|
+
declare const toLowerCase: (str: string) => string;
|
|
204
|
+
/**
|
|
205
|
+
* Converts all characters in a string to uppercase
|
|
206
|
+
* @param str Input string to convert
|
|
207
|
+
* @returns String with all characters in uppercase
|
|
208
|
+
*/
|
|
209
|
+
declare const toUpperCase: (str: string) => string;
|
|
210
|
+
/**
|
|
211
|
+
* Capitalizes the first letter of a string and lowercases the rest
|
|
212
|
+
* @param str Input string to capitalize
|
|
213
|
+
* @returns String with first letter capitalized and rest in lowercase
|
|
214
|
+
*/
|
|
215
|
+
declare const capitalizeFirst: (str: string) => string;
|
|
216
|
+
/**
|
|
217
|
+
* Capitalizes the first letter of each word in a string
|
|
218
|
+
* @param str Input string with words separated by spaces
|
|
219
|
+
* @returns String with each word's first letter capitalized
|
|
220
|
+
*/
|
|
221
|
+
declare const capitalizeEachWord: (str: string) => string;
|
|
222
|
+
/**
|
|
223
|
+
* Converts a string to camelCase format
|
|
224
|
+
*
|
|
225
|
+
* Transforms strings from any case convention (kebab-case, snake_case, PascalCase, spaces)
|
|
226
|
+
* to camelCase following JavaScript/TypeScript naming conventions.
|
|
227
|
+
*
|
|
228
|
+
* Algorithm:
|
|
229
|
+
* 1. Detect word boundaries (spaces, hyphens, underscores, case transitions)
|
|
230
|
+
* 2. Split into words and normalize to lowercase
|
|
231
|
+
* 3. First word: keep lowercase
|
|
232
|
+
* 4. Remaining words: capitalize first letter
|
|
233
|
+
* 5. Join without separators
|
|
234
|
+
*
|
|
235
|
+
* Format: `firstWordSecondWordThirdWord`
|
|
236
|
+
*
|
|
237
|
+
* @param str - Input string in any case format (empty returns '')
|
|
238
|
+
* @returns String in camelCase format
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* ```typescript
|
|
242
|
+
* // From kebab-case
|
|
243
|
+
* toCamelCase('hello-world') // 'helloWorld'
|
|
244
|
+
* toCamelCase('user-profile-page') // 'userProfilePage'
|
|
245
|
+
*
|
|
246
|
+
* // From snake_case
|
|
247
|
+
* toCamelCase('hello_world') // 'helloWorld'
|
|
248
|
+
* toCamelCase('user_first_name') // 'userFirstName'
|
|
249
|
+
*
|
|
250
|
+
* // From PascalCase
|
|
251
|
+
* toCamelCase('HelloWorld') // 'helloWorld'
|
|
252
|
+
* toCamelCase('UserProfile') // 'userProfile'
|
|
253
|
+
*
|
|
254
|
+
* // From space-separated
|
|
255
|
+
* toCamelCase('hello world') // 'helloWorld'
|
|
256
|
+
* toCamelCase('First Name') // 'firstName'
|
|
257
|
+
*
|
|
258
|
+
* // Mixed formats
|
|
259
|
+
* toCamelCase('hello-World_test') // 'helloWorldTest'
|
|
260
|
+
* toCamelCase('API_Response-data') // 'apiResponseData'
|
|
261
|
+
*
|
|
262
|
+
* // Edge cases
|
|
263
|
+
* toCamelCase('') // ''
|
|
264
|
+
* toCamelCase('a') // 'a'
|
|
265
|
+
* toCamelCase('UPPERCASE') // 'uppercase'
|
|
266
|
+
* toCamelCase('123-test') // '123Test'
|
|
267
|
+
* ```
|
|
268
|
+
*
|
|
269
|
+
* @example
|
|
270
|
+
* ```typescript
|
|
271
|
+
* // Real-world: Convert database column names to JS properties
|
|
272
|
+
* const dbColumns = ['user_id', 'first_name', 'last_name', 'created_at']
|
|
273
|
+
*
|
|
274
|
+
* const jsProperties = dbColumns.map(toCamelCase)
|
|
275
|
+
* console.log(jsProperties)
|
|
276
|
+
* // ['userId', 'firstName', 'lastName', 'createdAt']
|
|
277
|
+
*
|
|
278
|
+
* // Transform database row to JS object
|
|
279
|
+
* function transformRow(row: Record<string, any>): Record<string, any> {
|
|
280
|
+
* return Object.fromEntries(
|
|
281
|
+
* Object.entries(row).map(([key, value]) => [toCamelCase(key), value])
|
|
282
|
+
* )
|
|
283
|
+
* }
|
|
284
|
+
*
|
|
285
|
+
* transformRow({ user_id: 123, first_name: 'Alice' })
|
|
286
|
+
* // { userId: 123, firstName: 'Alice' }
|
|
287
|
+
* ```
|
|
288
|
+
*
|
|
289
|
+
* @example
|
|
290
|
+
* ```typescript
|
|
291
|
+
* // Real-world: Convert CSS property names to JS style properties
|
|
292
|
+
* const cssProperties = [
|
|
293
|
+
* 'background-color',
|
|
294
|
+
* 'font-size',
|
|
295
|
+
* 'margin-top',
|
|
296
|
+
* 'border-bottom-width'
|
|
297
|
+
* ]
|
|
298
|
+
*
|
|
299
|
+
* cssProperties.forEach(prop => {
|
|
300
|
+
* const jsProp = toCamelCase(prop)
|
|
301
|
+
* console.log(`${prop} → ${jsProp}`)
|
|
302
|
+
* })
|
|
303
|
+
* // background-color → backgroundColor
|
|
304
|
+
* // font-size → fontSize
|
|
305
|
+
* // margin-top → marginTop
|
|
306
|
+
* // border-bottom-width → borderBottomWidth
|
|
307
|
+
* ```
|
|
308
|
+
*
|
|
309
|
+
* @example
|
|
310
|
+
* ```typescript
|
|
311
|
+
* // Real-world: API response transformation
|
|
312
|
+
* interface ApiUser {
|
|
313
|
+
* user_id: number
|
|
314
|
+
* first_name: string
|
|
315
|
+
* last_name: string
|
|
316
|
+
* created_at: string
|
|
317
|
+
* }
|
|
318
|
+
*
|
|
319
|
+
* function transformApiResponse(apiData: ApiUser) {
|
|
320
|
+
* return {
|
|
321
|
+
* userId: apiData.user_id,
|
|
322
|
+
* firstName: apiData.first_name,
|
|
323
|
+
* lastName: apiData.last_name,
|
|
324
|
+
* createdAt: new Date(apiData.created_at)
|
|
325
|
+
* }
|
|
326
|
+
* }
|
|
327
|
+
*
|
|
328
|
+
* // Or generically:
|
|
329
|
+
* function autoTransformKeys<T extends Record<string, any>>(obj: T) {
|
|
330
|
+
* return Object.fromEntries(
|
|
331
|
+
* Object.entries(obj).map(([k, v]) => [toCamelCase(k), v])
|
|
332
|
+
* )
|
|
333
|
+
* }
|
|
334
|
+
* ```
|
|
335
|
+
*
|
|
336
|
+
* @see {@link toPascalCase} for PascalCase conversion (first letter uppercase)
|
|
337
|
+
* @see {@link toSnakeCase} for snake_case conversion
|
|
338
|
+
* @see {@link toKebabCase} for kebab-case conversion
|
|
339
|
+
*/
|
|
340
|
+
declare const toCamelCase: (str: string) => string;
|
|
341
|
+
/**
|
|
342
|
+
* Converts a string to snake_case format
|
|
343
|
+
*
|
|
344
|
+
* Transforms strings from any case convention to snake_case, commonly used in
|
|
345
|
+
* Python, Ruby, database column names, and environment variables.
|
|
346
|
+
*
|
|
347
|
+
* Algorithm:
|
|
348
|
+
* 1. Replace non-word characters with spaces
|
|
349
|
+
* 2. Split on spaces and camelCase boundaries
|
|
350
|
+
* 3. Convert all words to lowercase
|
|
351
|
+
* 4. Join with underscores
|
|
352
|
+
*
|
|
353
|
+
* Format: `first_word_second_word_third_word`
|
|
354
|
+
*
|
|
355
|
+
* @param str - Input string in any case format (empty returns '')
|
|
356
|
+
* @returns String in snake_case format
|
|
357
|
+
*
|
|
358
|
+
* @example
|
|
359
|
+
* ```typescript
|
|
360
|
+
* // From camelCase
|
|
361
|
+
* toSnakeCase('helloWorld') // 'hello_world'
|
|
362
|
+
* toSnakeCase('userFirstName') // 'user_first_name'
|
|
363
|
+
*
|
|
364
|
+
* // From PascalCase
|
|
365
|
+
* toSnakeCase('HelloWorld') // 'hello_world'
|
|
366
|
+
* toSnakeCase('UserProfile') // 'user_profile'
|
|
367
|
+
*
|
|
368
|
+
* // From kebab-case
|
|
369
|
+
* toSnakeCase('hello-world') // 'hello_world'
|
|
370
|
+
* toSnakeCase('user-profile-page') // 'user_profile_page'
|
|
371
|
+
*
|
|
372
|
+
* // From space-separated
|
|
373
|
+
* toSnakeCase('hello world') // 'hello_world'
|
|
374
|
+
* toSnakeCase('First Name') // 'first_name'
|
|
375
|
+
*
|
|
376
|
+
* // Mixed formats
|
|
377
|
+
* toSnakeCase('helloWorld-test') // 'hello_world_test'
|
|
378
|
+
* toSnakeCase('APIResponse') // 'a_p_i_response'
|
|
379
|
+
*
|
|
380
|
+
* // Edge cases
|
|
381
|
+
* toSnakeCase('') // ''
|
|
382
|
+
* toSnakeCase('a') // 'a'
|
|
383
|
+
* toSnakeCase('UPPERCASE') // 'u_p_p_e_r_c_a_s_e'
|
|
384
|
+
* ```
|
|
385
|
+
*
|
|
386
|
+
* @example
|
|
387
|
+
* ```typescript
|
|
388
|
+
* // Real-world: Convert JS properties to database column names
|
|
389
|
+
* const jsObject = {
|
|
390
|
+
* userId: 123,
|
|
391
|
+
* firstName: 'Alice',
|
|
392
|
+
* lastName: 'Smith',
|
|
393
|
+
* createdAt: new Date()
|
|
394
|
+
* }
|
|
395
|
+
*
|
|
396
|
+
* const dbColumns = Object.keys(jsObject).map(toSnakeCase)
|
|
397
|
+
* console.log(dbColumns)
|
|
398
|
+
* // ['user_id', 'first_name', 'last_name', 'created_at']
|
|
399
|
+
*
|
|
400
|
+
* // Generate SQL INSERT statement
|
|
401
|
+
* const columns = Object.keys(jsObject).map(toSnakeCase).join(', ')
|
|
402
|
+
* const sql = `INSERT INTO users (${columns}) VALUES (?)`
|
|
403
|
+
* ```
|
|
404
|
+
*
|
|
405
|
+
* @example
|
|
406
|
+
* ```typescript
|
|
407
|
+
* // Real-world: Environment variable generation
|
|
408
|
+
* const configKeys = ['databaseHost', 'databasePort', 'apiBaseUrl']
|
|
409
|
+
*
|
|
410
|
+
* configKeys.forEach(key => {
|
|
411
|
+
* const envVar = toSnakeCase(key).toUpperCase()
|
|
412
|
+
* console.log(`${envVar}=value`)
|
|
413
|
+
* })
|
|
414
|
+
* // DATABASE_HOST=value
|
|
415
|
+
* // DATABASE_PORT=value
|
|
416
|
+
* // API_BASE_URL=value
|
|
417
|
+
* ```
|
|
418
|
+
*
|
|
419
|
+
* @see {@link toCamelCase} for camelCase conversion
|
|
420
|
+
* @see {@link toKebabCase} for kebab-case conversion
|
|
421
|
+
* @see {@link toPascalCase} for PascalCase conversion
|
|
422
|
+
*/
|
|
423
|
+
declare const toSnakeCase: (str: string) => string;
|
|
424
|
+
/**
|
|
425
|
+
* Converts a string to kebab-case format
|
|
426
|
+
*
|
|
427
|
+
* Transforms strings from any case convention to kebab-case (also called dash-case or hyphen-case).
|
|
428
|
+
* Widely used in URLs, HTML attributes, CSS classes, and file names.
|
|
429
|
+
*
|
|
430
|
+
* Algorithm:
|
|
431
|
+
* 1. Replace underscores and spaces with hyphens
|
|
432
|
+
* 2. Insert hyphens before uppercase letters (handle camelCase)
|
|
433
|
+
* 3. Convert all to lowercase
|
|
434
|
+
* 4. Remove non-alphanumeric characters (except hyphens)
|
|
435
|
+
* 5. Collapse multiple consecutive hyphens
|
|
436
|
+
* 6. Remove leading/trailing hyphens
|
|
437
|
+
*
|
|
438
|
+
* Format: `first-word-second-word-third-word`
|
|
439
|
+
*
|
|
440
|
+
* @param str - Input string in any case format (empty returns '')
|
|
441
|
+
* @returns String in kebab-case format
|
|
442
|
+
*
|
|
443
|
+
* @example
|
|
444
|
+
* ```typescript
|
|
445
|
+
* // From camelCase
|
|
446
|
+
* toKebabCase('helloWorld') // 'hello-world'
|
|
447
|
+
* toKebabCase('userFirstName') // 'user-first-name'
|
|
448
|
+
*
|
|
449
|
+
* // From PascalCase
|
|
450
|
+
* toKebabCase('HelloWorld') // 'hello-world'
|
|
451
|
+
* toKebabCase('UserProfile') // 'user-profile'
|
|
452
|
+
*
|
|
453
|
+
* // From snake_case
|
|
454
|
+
* toKebabCase('hello_world') // 'hello-world'
|
|
455
|
+
* toKebabCase('user_first_name') // 'user-first-name'
|
|
456
|
+
*
|
|
457
|
+
* // From space-separated
|
|
458
|
+
* toKebabCase('hello world') // 'hello-world'
|
|
459
|
+
* toKebabCase('First Name') // 'first-name'
|
|
460
|
+
*
|
|
461
|
+
* // Mixed formats
|
|
462
|
+
* toKebabCase('helloWorld_test') // 'hello-world-test'
|
|
463
|
+
* toKebabCase('API-Response') // 'api-response'
|
|
464
|
+
*
|
|
465
|
+
* // Edge cases
|
|
466
|
+
* toKebabCase('') // ''
|
|
467
|
+
* toKebabCase('a') // 'a'
|
|
468
|
+
* toKebabCase('UPPERCASE') // 'uppercase'
|
|
469
|
+
* toKebabCase('123Test') // '123-test'
|
|
470
|
+
* toKebabCase('--multiple--dashes--') // 'multiple-dashes'
|
|
471
|
+
* ```
|
|
472
|
+
*
|
|
473
|
+
* @example
|
|
474
|
+
* ```typescript
|
|
475
|
+
* // Real-world: Generate URL slugs from page titles
|
|
476
|
+
* const pageTitle = 'My Awesome Blog Post'
|
|
477
|
+
* const urlSlug = toKebabCase(pageTitle)
|
|
478
|
+
* const url = `https://example.com/blog/${urlSlug}`
|
|
479
|
+
* // https://example.com/blog/my-awesome-blog-post
|
|
480
|
+
*
|
|
481
|
+
* // Multiple pages
|
|
482
|
+
* const pages = [
|
|
483
|
+
* { title: 'Getting Started', component: 'GettingStarted' },
|
|
484
|
+
* { title: 'API Reference', component: 'ApiReference' },
|
|
485
|
+
* { title: 'Best Practices', component: 'BestPractices' }
|
|
486
|
+
* ]
|
|
487
|
+
*
|
|
488
|
+
* const routes = pages.map(p => ({
|
|
489
|
+
* path: `/${toKebabCase(p.title)}`,
|
|
490
|
+
* component: p.component
|
|
491
|
+
* }))
|
|
492
|
+
* // [
|
|
493
|
+
* // { path: '/getting-started', component: 'GettingStarted' },
|
|
494
|
+
* // { path: '/api-reference', component: 'ApiReference' },
|
|
495
|
+
* // { path: '/best-practices', component: 'BestPractices' }
|
|
496
|
+
* // ]
|
|
497
|
+
* ```
|
|
498
|
+
*
|
|
499
|
+
* @example
|
|
500
|
+
* ```typescript
|
|
501
|
+
* // Real-world: CSS class name generation
|
|
502
|
+
* function generateClassName(componentName: string, modifier?: string): string {
|
|
503
|
+
* const base = `component-${toKebabCase(componentName)}`
|
|
504
|
+
* return modifier ? `${base}--${toKebabCase(modifier)}` : base
|
|
505
|
+
* }
|
|
506
|
+
*
|
|
507
|
+
* generateClassName('UserProfile') // 'component-user-profile'
|
|
508
|
+
* generateClassName('UserProfile', 'isActive') // 'component-user-profile--is-active'
|
|
509
|
+
* generateClassName('ButtonPrimary', 'largeSize') // 'component-button-primary--large-size'
|
|
510
|
+
* ```
|
|
511
|
+
*
|
|
512
|
+
* @example
|
|
513
|
+
* ```typescript
|
|
514
|
+
* // Real-world: HTML attribute generation
|
|
515
|
+
* function generateDataAttribute(key: string, value: string): string {
|
|
516
|
+
* return `data-${toKebabCase(key)}="${value}"`
|
|
517
|
+
* }
|
|
518
|
+
*
|
|
519
|
+
* generateDataAttribute('userId', '123') // 'data-user-id="123"'
|
|
520
|
+
* generateDataAttribute('testEnvironment', 'staging') // 'data-test-environment="staging"'
|
|
521
|
+
* ```
|
|
522
|
+
*
|
|
523
|
+
* @see {@link toCamelCase} for camelCase conversion
|
|
524
|
+
* @see {@link toSnakeCase} for snake_case conversion
|
|
525
|
+
* @see {@link toPascalCase} for PascalCase conversion
|
|
526
|
+
* @see {@link toUrlSlug} for URL-safe slug generation with accent removal
|
|
527
|
+
*/
|
|
528
|
+
declare const toKebabCase: (str: string) => string;
|
|
529
|
+
/**
|
|
530
|
+
* Converts a string to PascalCase format
|
|
531
|
+
*
|
|
532
|
+
* Transforms strings from any case convention to PascalCase (also called UpperCamelCase).
|
|
533
|
+
* Commonly used for class names, component names, type names, and constructor functions
|
|
534
|
+
* in JavaScript/TypeScript.
|
|
535
|
+
*
|
|
536
|
+
* Algorithm:
|
|
537
|
+
* 1. Split on word boundaries (hyphens, underscores, spaces)
|
|
538
|
+
* 2. Capitalize first letter of each word
|
|
539
|
+
* 3. Join without separators
|
|
540
|
+
* 4. Ensure first character is uppercase
|
|
541
|
+
*
|
|
542
|
+
* Format: `FirstWordSecondWordThirdWord`
|
|
543
|
+
*
|
|
544
|
+
* @param str - Input string in any case format (empty returns '')
|
|
545
|
+
* @returns String in PascalCase format
|
|
546
|
+
*
|
|
547
|
+
* @example
|
|
548
|
+
* ```typescript
|
|
549
|
+
* // From camelCase
|
|
550
|
+
* toPascalCase('helloWorld') // 'HelloWorld'
|
|
551
|
+
* toPascalCase('userFirstName') // 'UserFirstName'
|
|
552
|
+
*
|
|
553
|
+
* // From kebab-case
|
|
554
|
+
* toPascalCase('hello-world') // 'HelloWorld'
|
|
555
|
+
* toPascalCase('user-profile-page') // 'UserProfilePage'
|
|
556
|
+
*
|
|
557
|
+
* // From snake_case
|
|
558
|
+
* toPascalCase('hello_world') // 'HelloWorld'
|
|
559
|
+
* toPascalCase('user_first_name') // 'UserFirstName'
|
|
560
|
+
*
|
|
561
|
+
* // From space-separated
|
|
562
|
+
* toPascalCase('hello world') // 'HelloWorld'
|
|
563
|
+
* toPascalCase('first name') // 'FirstName'
|
|
564
|
+
*
|
|
565
|
+
* // Mixed formats
|
|
566
|
+
* toPascalCase('hello-World_test') // 'HelloWorldTest'
|
|
567
|
+
* toPascalCase('api_response-data') // 'ApiResponseData'
|
|
568
|
+
*
|
|
569
|
+
* // Edge cases
|
|
570
|
+
* toPascalCase('') // ''
|
|
571
|
+
* toPascalCase('a') // 'A'
|
|
572
|
+
* toPascalCase('UPPERCASE') // 'UPPERCASE'
|
|
573
|
+
* ```
|
|
574
|
+
*
|
|
575
|
+
* @example
|
|
576
|
+
* ```typescript
|
|
577
|
+
* // Real-world: Generate React component names
|
|
578
|
+
* const componentNames = ['user-profile', 'navigation-bar', 'footer-links']
|
|
579
|
+
*
|
|
580
|
+
* componentNames.forEach(name => {
|
|
581
|
+
* const pascalName = toPascalCase(name)
|
|
582
|
+
* console.log(`export function ${pascalName}() { ... }`)
|
|
583
|
+
* })
|
|
584
|
+
* // export function UserProfile() { ... }
|
|
585
|
+
* // export function NavigationBar() { ... }
|
|
586
|
+
* // export function FooterLinks() { ... }
|
|
587
|
+
* ```
|
|
588
|
+
*
|
|
589
|
+
* @example
|
|
590
|
+
* ```typescript
|
|
591
|
+
* // Real-world: TypeScript type name generation
|
|
592
|
+
* interface GenerateTypeOptions {
|
|
593
|
+
* name: string
|
|
594
|
+
* fields: Array<{ name: string; type: string }>
|
|
595
|
+
* }
|
|
596
|
+
*
|
|
597
|
+
* function generateTypeDefinition(options: GenerateTypeOptions): string {
|
|
598
|
+
* const typeName = toPascalCase(options.name)
|
|
599
|
+
* const fields = options.fields
|
|
600
|
+
* .map(f => ` ${f.name}: ${f.type}`)
|
|
601
|
+
* .join('\n')
|
|
602
|
+
*
|
|
603
|
+
* return `interface ${typeName} {\n${fields}\n}`
|
|
604
|
+
* }
|
|
605
|
+
*
|
|
606
|
+
* generateTypeDefinition({
|
|
607
|
+
* name: 'user-profile',
|
|
608
|
+
* fields: [
|
|
609
|
+
* { name: 'userId', type: 'number' },
|
|
610
|
+
* { name: 'name', type: 'string' }
|
|
611
|
+
* ]
|
|
612
|
+
* })
|
|
613
|
+
* // interface UserProfile {
|
|
614
|
+
* // userId: number
|
|
615
|
+
* // name: string
|
|
616
|
+
* // }
|
|
617
|
+
* ```
|
|
618
|
+
*
|
|
619
|
+
* @example
|
|
620
|
+
* ```typescript
|
|
621
|
+
* // Real-world: Class name generation for dynamic imports
|
|
622
|
+
* async function loadService(serviceName: string) {
|
|
623
|
+
* const className = toPascalCase(serviceName)
|
|
624
|
+
* const module = await import(`./services/${serviceName}`)
|
|
625
|
+
* return new module[className]()
|
|
626
|
+
* }
|
|
627
|
+
*
|
|
628
|
+
* await loadService('user-service') // new UserService()
|
|
629
|
+
* await loadService('payment-gateway') // new PaymentGateway()
|
|
630
|
+
* ```
|
|
631
|
+
*
|
|
632
|
+
* @see {@link toCamelCase} for camelCase conversion (first letter lowercase)
|
|
633
|
+
* @see {@link toSnakeCase} for snake_case conversion
|
|
634
|
+
* @see {@link toKebabCase} for kebab-case conversion
|
|
635
|
+
*/
|
|
636
|
+
declare const toPascalCase: (str: string) => string;
|
|
637
|
+
/**
|
|
638
|
+
* Checks if a string contains another string with optional case sensitivity
|
|
639
|
+
* @param str String to search within
|
|
640
|
+
* @param searchStr Substring to search for
|
|
641
|
+
* @param caseSensitive Whether to perform case-sensitive search (default: false)
|
|
642
|
+
* @returns True if the string contains the search string, false otherwise
|
|
643
|
+
*/
|
|
644
|
+
declare const contains: (str: string, searchStr: string, caseSensitive?: boolean) => boolean;
|
|
645
|
+
/**
|
|
646
|
+
* Checks if a string starts with another string with optional case sensitivity
|
|
647
|
+
* @param str String to check
|
|
648
|
+
* @param searchStr Prefix to search for
|
|
649
|
+
* @param caseSensitive Whether to perform case-sensitive search (default: false)
|
|
650
|
+
* @returns True if the string starts with the search string, false otherwise
|
|
651
|
+
*/
|
|
652
|
+
declare const startsWith: (str: string, searchStr: string, caseSensitive?: boolean) => boolean;
|
|
653
|
+
/**
|
|
654
|
+
* Checks if a string ends with another string with optional case sensitivity
|
|
655
|
+
* @param str String to check
|
|
656
|
+
* @param searchStr Suffix to search for
|
|
657
|
+
* @param caseSensitive Whether to perform case-sensitive search (default: false)
|
|
658
|
+
* @returns True if the string ends with the search string, false otherwise
|
|
659
|
+
*/
|
|
660
|
+
declare const endsWith: (str: string, searchStr: string, caseSensitive?: boolean) => boolean;
|
|
661
|
+
/**
|
|
662
|
+
* Pads a string at the start (left) with a specific character to reach target length
|
|
663
|
+
* @param str String to pad
|
|
664
|
+
* @param length Target length for the resulting string
|
|
665
|
+
* @param padChar Character to use for padding (default: space)
|
|
666
|
+
* @returns String padded to target length, original if already longer
|
|
667
|
+
*/
|
|
668
|
+
declare const padStart: (str: string, length: number, padChar?: string) => string;
|
|
669
|
+
/**
|
|
670
|
+
* Pads a string at the end (right) with a specific character to reach target length
|
|
671
|
+
* @param str String to pad
|
|
672
|
+
* @param length Target length for the resulting string
|
|
673
|
+
* @param padChar Character to use for padding (default: space)
|
|
674
|
+
* @returns String padded to target length, original if already longer
|
|
675
|
+
*/
|
|
676
|
+
declare const padEnd: (str: string, length: number, padChar?: string) => string;
|
|
677
|
+
/**
|
|
678
|
+
* Removes leading and trailing whitespace from a string
|
|
679
|
+
* @param str String to trim
|
|
680
|
+
* @returns String with leading and trailing whitespace removed
|
|
681
|
+
*/
|
|
682
|
+
declare const trim: (str: string) => string;
|
|
683
|
+
/**
|
|
684
|
+
* Removes leading (start) whitespace from a string
|
|
685
|
+
* @param str String to trim
|
|
686
|
+
* @returns String with leading whitespace removed
|
|
687
|
+
*/
|
|
688
|
+
declare const trimStart: (str: string) => string;
|
|
689
|
+
/**
|
|
690
|
+
* Removes trailing (end) whitespace from a string
|
|
691
|
+
* @param str String to trim
|
|
692
|
+
* @returns String with trailing whitespace removed
|
|
693
|
+
*/
|
|
694
|
+
declare const trimEnd: (str: string) => string;
|
|
695
|
+
/**
|
|
696
|
+
* Reverses the character order in a string
|
|
697
|
+
* @param str String to reverse
|
|
698
|
+
* @returns String with characters in reverse order
|
|
699
|
+
*/
|
|
700
|
+
declare const reverseString: (str: string) => string;
|
|
701
|
+
/**
|
|
702
|
+
* Repeats a string a specified number of times
|
|
703
|
+
* @param str String to repeat
|
|
704
|
+
* @param times Number of times to repeat the string
|
|
705
|
+
* @returns String repeated the specified number of times
|
|
706
|
+
*/
|
|
707
|
+
declare const repeatString: (str: string, times: number) => string;
|
|
708
|
+
/**
|
|
709
|
+
* Replaces all occurrences of a substring with a replacement string
|
|
710
|
+
* @param str String to search in
|
|
711
|
+
* @param searchStr Substring to find and replace
|
|
712
|
+
* @param replaceStr String to replace each occurrence with
|
|
713
|
+
* @returns String with all occurrences replaced
|
|
714
|
+
*/
|
|
715
|
+
declare const replaceAllOccurrences: (str: string, searchStr: string, replaceStr: string) => string;
|
|
716
|
+
/**
|
|
717
|
+
* Counts the number of occurrences of a substring within a string
|
|
718
|
+
* @param str String to search within
|
|
719
|
+
* @param searchStr Substring to count occurrences of
|
|
720
|
+
* @returns Number of times the substring appears in the string
|
|
721
|
+
*/
|
|
722
|
+
declare const countOccurrences: (str: string, searchStr: string) => number;
|
|
723
|
+
/**
|
|
724
|
+
* Checks if a string is empty, null, undefined, or contains only whitespace
|
|
725
|
+
* @param str String to check (can be null or undefined)
|
|
726
|
+
* @returns True if string is empty/null/undefined or only whitespace, false otherwise
|
|
727
|
+
*/
|
|
728
|
+
declare const isEmpty: (str: string | null | undefined) => boolean;
|
|
729
|
+
/**
|
|
730
|
+
* Validates if a string is a properly formatted email address
|
|
731
|
+
* @param str String to validate as email
|
|
732
|
+
* @returns True if string matches email format, false otherwise
|
|
733
|
+
*/
|
|
734
|
+
declare const isEmail: (str: string) => boolean;
|
|
735
|
+
/**
|
|
736
|
+
* Generates a URL-friendly slug from a string
|
|
737
|
+
*
|
|
738
|
+
* Creates clean, SEO-friendly URL slugs by normalizing strings for use in web addresses.
|
|
739
|
+
* Removes special characters, normalizes whitespace, and converts to lowercase with hyphens.
|
|
740
|
+
*
|
|
741
|
+
* Algorithm:
|
|
742
|
+
* 1. Convert to lowercase
|
|
743
|
+
* 2. Trim leading/trailing whitespace
|
|
744
|
+
* 3. Remove non-word characters (except spaces, hyphens, underscores)
|
|
745
|
+
* 4. Replace spaces/underscores/multiple-hyphens with single hyphen
|
|
746
|
+
* 5. Remove leading/trailing hyphens
|
|
747
|
+
*
|
|
748
|
+
* Format: `url-friendly-slug-text`
|
|
749
|
+
*
|
|
750
|
+
* ⚠️ NOTE: Does NOT remove accents. For accent removal, use {@link removeAccents} first.
|
|
751
|
+
*
|
|
752
|
+
* @param str - String to convert to URL slug (special chars removed)
|
|
753
|
+
* @returns URL-friendly slug string in lowercase with hyphens
|
|
754
|
+
*
|
|
755
|
+
* @example
|
|
756
|
+
* ```typescript
|
|
757
|
+
* // Basic slug generation
|
|
758
|
+
* toUrlSlug('Hello World') // 'hello-world'
|
|
759
|
+
* toUrlSlug('My Blog Post Title') // 'my-blog-post-title'
|
|
760
|
+
*
|
|
761
|
+
* // Remove special characters
|
|
762
|
+
* toUrlSlug('User: John Doe!') // 'user-john-doe'
|
|
763
|
+
* toUrlSlug('Price: $19.99') // 'price-1999'
|
|
764
|
+
* toUrlSlug('Hello @ World #2024') // 'hello-world-2024'
|
|
765
|
+
*
|
|
766
|
+
* // Normalize whitespace and separators
|
|
767
|
+
* toUrlSlug('hello world') // 'hello-world'
|
|
768
|
+
* toUrlSlug('hello_world_test') // 'hello-world-test'
|
|
769
|
+
* toUrlSlug('---multiple---dashes---') // 'multiple-dashes'
|
|
770
|
+
*
|
|
771
|
+
* // Preserve numbers and hyphens
|
|
772
|
+
* toUrlSlug('Article 123') // 'article-123'
|
|
773
|
+
* toUrlSlug('ES6-Features') // 'es6-features'
|
|
774
|
+
*
|
|
775
|
+
* // Edge cases
|
|
776
|
+
* toUrlSlug('') // ''
|
|
777
|
+
* toUrlSlug(' ') // ''
|
|
778
|
+
* toUrlSlug('123') // '123'
|
|
779
|
+
* toUrlSlug('a-b-c') // 'a-b-c'
|
|
780
|
+
* ```
|
|
781
|
+
*
|
|
782
|
+
* @example
|
|
783
|
+
* ```typescript
|
|
784
|
+
* // Real-world: Generate blog post URL from title
|
|
785
|
+
* function createBlogPostUrl(title: string, id: number): string {
|
|
786
|
+
* const slug = toUrlSlug(title)
|
|
787
|
+
* return `/blog/${id}/${slug}`
|
|
788
|
+
* }
|
|
789
|
+
*
|
|
790
|
+
* createBlogPostUrl('10 Tips for TypeScript', 42)
|
|
791
|
+
* // '/blog/42/10-tips-for-typescript'
|
|
792
|
+
*
|
|
793
|
+
* createBlogPostUrl('Getting Started with React!', 1)
|
|
794
|
+
* // '/blog/1/getting-started-with-react'
|
|
795
|
+
* ```
|
|
796
|
+
*
|
|
797
|
+
* @example
|
|
798
|
+
* ```typescript
|
|
799
|
+
* // Real-world: Generate product URLs for e-commerce
|
|
800
|
+
* interface Product {
|
|
801
|
+
* id: string
|
|
802
|
+
* name: string
|
|
803
|
+
* category: string
|
|
804
|
+
* }
|
|
805
|
+
*
|
|
806
|
+
* function getProductUrl(product: Product): string {
|
|
807
|
+
* const categorySlug = toUrlSlug(product.category)
|
|
808
|
+
* const nameSlug = toUrlSlug(product.name)
|
|
809
|
+
* return `/products/${categorySlug}/${product.id}/${nameSlug}`
|
|
810
|
+
* }
|
|
811
|
+
*
|
|
812
|
+
* getProductUrl({
|
|
813
|
+
* id: 'SKU-123',
|
|
814
|
+
* name: 'Wireless Mouse (Ergonomic)',
|
|
815
|
+
* category: 'Computer Accessories'
|
|
816
|
+
* })
|
|
817
|
+
* // '/products/computer-accessories/SKU-123/wireless-mouse-ergonomic'
|
|
818
|
+
* ```
|
|
819
|
+
*
|
|
820
|
+
* @example
|
|
821
|
+
* ```typescript
|
|
822
|
+
* // Real-world: SEO-friendly URLs with accent handling
|
|
823
|
+
* function createSeoUrl(title: string): string {
|
|
824
|
+
* // Remove accents BEFORE slug generation for better URL compatibility
|
|
825
|
+
* return toUrlSlug(removeAccents(title))
|
|
826
|
+
* }
|
|
827
|
+
*
|
|
828
|
+
* createSeoUrl('Café Münchën 2024')
|
|
829
|
+
* // 'cafe-munchen-2024' (accents removed)
|
|
830
|
+
*
|
|
831
|
+
* // Without accent removal:
|
|
832
|
+
* toUrlSlug('Café Münchën 2024')
|
|
833
|
+
* // 'caf-mnchen-2024' (accents become invalid chars, get removed)
|
|
834
|
+
* ```
|
|
835
|
+
*
|
|
836
|
+
* @example
|
|
837
|
+
* ```typescript
|
|
838
|
+
* // Real-world: Generate unique slugs for duplicate titles
|
|
839
|
+
* const existingSlugs = new Set(['hello-world', 'hello-world-1'])
|
|
840
|
+
*
|
|
841
|
+
* function generateUniqueSlug(title: string): string {
|
|
842
|
+
* let slug = toUrlSlug(title)
|
|
843
|
+
* let counter = 1
|
|
844
|
+
*
|
|
845
|
+
* while (existingSlugs.has(slug)) {
|
|
846
|
+
* slug = `${toUrlSlug(title)}-${counter}`
|
|
847
|
+
* counter++
|
|
848
|
+
* }
|
|
849
|
+
*
|
|
850
|
+
* existingSlugs.add(slug)
|
|
851
|
+
* return slug
|
|
852
|
+
* }
|
|
853
|
+
*
|
|
854
|
+
* generateUniqueSlug('Hello World') // 'hello-world-2' (avoiding existing)
|
|
855
|
+
* generateUniqueSlug('New Article') // 'new-article'
|
|
856
|
+
* ```
|
|
857
|
+
*
|
|
858
|
+
* @see {@link toKebabCase} for case conversion to kebab-case
|
|
859
|
+
* @see {@link removeAccents} for removing accents before slug generation
|
|
860
|
+
* @see {@link sanitizeString} for basic string sanitization
|
|
861
|
+
*/
|
|
862
|
+
declare const toUrlSlug: (str: string) => string;
|
|
863
|
+
/**
|
|
864
|
+
* Removes accents and diacritical marks from characters in a string
|
|
865
|
+
*
|
|
866
|
+
* Uses Unicode normalization (NFD) to decompose accented characters, then removes
|
|
867
|
+
* combining diacritical marks. Essential for search, sorting, URL slugs, and ASCII compatibility.
|
|
868
|
+
*
|
|
869
|
+
* Algorithm:
|
|
870
|
+
* 1. Normalize string to NFD (Normalization Form Decomposed)
|
|
871
|
+
* 2. Remove Unicode combining diacritical marks (U+0300–U+036F)
|
|
872
|
+
* 3. Return ASCII-compatible base characters
|
|
873
|
+
*
|
|
874
|
+
* Supported diacritics: acute (´), grave (`), circumflex (^), tilde (~), umlaut (¨),
|
|
875
|
+
* cedilla (¸), and many more.
|
|
876
|
+
*
|
|
877
|
+
* @param str - String containing accented characters (e.g., 'café', 'Münchën')
|
|
878
|
+
* @returns String with accents removed to base ASCII characters (e.g., 'cafe', 'Munchen')
|
|
879
|
+
*
|
|
880
|
+
* @example
|
|
881
|
+
* ```typescript
|
|
882
|
+
* // Basic accent removal - Romance languages
|
|
883
|
+
* removeAccents('café') // 'cafe'
|
|
884
|
+
* removeAccents('naïve') // 'naive'
|
|
885
|
+
* removeAccents('résumé') // 'resume'
|
|
886
|
+
* removeAccents('à côté') // 'a cote'
|
|
887
|
+
*
|
|
888
|
+
* // Spanish accents
|
|
889
|
+
* removeAccents('Español') // 'Espanol'
|
|
890
|
+
* removeAccents('niño') // 'nino'
|
|
891
|
+
* removeAccents('José María') // 'Jose Maria'
|
|
892
|
+
*
|
|
893
|
+
* // German umlauts
|
|
894
|
+
* removeAccents('Münchën') // 'Munchen'
|
|
895
|
+
* removeAccents('Köln') // 'Koln'
|
|
896
|
+
* removeAccents('Zürich') // 'Zurich'
|
|
897
|
+
*
|
|
898
|
+
* // Portuguese
|
|
899
|
+
* removeAccents('São Paulo') // 'Sao Paulo'
|
|
900
|
+
* removeAccents('Brasília') // 'Brasilia'
|
|
901
|
+
*
|
|
902
|
+
* // French
|
|
903
|
+
* removeAccents('Côte d'Ivoire') // 'Cote d'Ivoire'
|
|
904
|
+
* removeAccents('Françoise') // 'Francoise'
|
|
905
|
+
*
|
|
906
|
+
* // Mixed
|
|
907
|
+
* removeAccents('Crème brûlée') // 'Creme brulee'
|
|
908
|
+
* removeAccents('Åre, Malmö') // 'Are, Malmo'
|
|
909
|
+
*
|
|
910
|
+
* // Edge cases
|
|
911
|
+
* removeAccents('') // ''
|
|
912
|
+
* removeAccents('ASCII text') // 'ASCII text' (unchanged)
|
|
913
|
+
* ```
|
|
914
|
+
*
|
|
915
|
+
* @example
|
|
916
|
+
* ```typescript
|
|
917
|
+
* // Real-world: Search normalization (case + accent insensitive)
|
|
918
|
+
* function normalizeForSearch(text: string): string {
|
|
919
|
+
* return removeAccents(text.toLowerCase().trim())
|
|
920
|
+
* }
|
|
921
|
+
*
|
|
922
|
+
* const searchQuery = normalizeForSearch('Café') // 'cafe'
|
|
923
|
+
* const productName = normalizeForSearch('CAFÉ PREMIUM') // 'cafe premium'
|
|
924
|
+
*
|
|
925
|
+
* if (productName.includes(searchQuery)) {
|
|
926
|
+
* console.log('Match found!')
|
|
927
|
+
* }
|
|
928
|
+
* ```
|
|
929
|
+
*
|
|
930
|
+
* @example
|
|
931
|
+
* ```typescript
|
|
932
|
+
* // Real-world: Generate SEO-friendly URLs
|
|
933
|
+
* function createSeoUrl(title: string): string {
|
|
934
|
+
* return toUrlSlug(removeAccents(title))
|
|
935
|
+
* }
|
|
936
|
+
*
|
|
937
|
+
* createSeoUrl('Guía de Español')
|
|
938
|
+
* // 'guia-de-espanol'
|
|
939
|
+
*
|
|
940
|
+
* createSeoUrl('Café Münchën 2024')
|
|
941
|
+
* // 'cafe-munchen-2024'
|
|
942
|
+
*
|
|
943
|
+
* createSeoUrl('São Paulo: Best Restaurants')
|
|
944
|
+
* // 'sao-paulo-best-restaurants'
|
|
945
|
+
* ```
|
|
946
|
+
*
|
|
947
|
+
* @example
|
|
948
|
+
* ```typescript
|
|
949
|
+
* // Real-world: Sort names alphabetically (accent-insensitive)
|
|
950
|
+
* const names = ['Álvarez', 'Andersen', 'Ängel', 'Adams']
|
|
951
|
+
*
|
|
952
|
+
* const sorted = names.sort((a, b) =>
|
|
953
|
+
* removeAccents(a).localeCompare(removeAccents(b))
|
|
954
|
+
* )
|
|
955
|
+
* // ['Adams', 'Álvarez', 'Andersen', 'Ängel']
|
|
956
|
+
* // (sorted by: Adams, Alvarez, Andersen, Angel)
|
|
957
|
+
* ```
|
|
958
|
+
*
|
|
959
|
+
* @example
|
|
960
|
+
* ```typescript
|
|
961
|
+
* // Real-world: Filename sanitization
|
|
962
|
+
* function sanitizeFilename(filename: string): string {
|
|
963
|
+
* // Remove accents, then sanitize
|
|
964
|
+
* const withoutAccents = removeAccents(filename)
|
|
965
|
+
* return sanitizeString(withoutAccents)
|
|
966
|
+
* }
|
|
967
|
+
*
|
|
968
|
+
* sanitizeFilename('Presentación_2024.pdf')
|
|
969
|
+
* // 'presentacion-2024pdf'
|
|
970
|
+
*
|
|
971
|
+
* sanitizeFilename('Föräldrar & Barn.docx')
|
|
972
|
+
* // 'foraldrar-barn-docx'
|
|
973
|
+
* ```
|
|
974
|
+
*
|
|
975
|
+
* @example
|
|
976
|
+
* ```typescript
|
|
977
|
+
* // Real-world: Email address generation
|
|
978
|
+
* function generateEmail(firstName: string, lastName: string): string {
|
|
979
|
+
* const first = removeAccents(firstName.toLowerCase())
|
|
980
|
+
* const last = removeAccents(lastName.toLowerCase())
|
|
981
|
+
* return `${first}.${last}@company.com`
|
|
982
|
+
* }
|
|
983
|
+
*
|
|
984
|
+
* generateEmail('José', 'García')
|
|
985
|
+
* // 'jose.garcia@company.com'
|
|
986
|
+
*
|
|
987
|
+
* generateEmail('François', 'Müller')
|
|
988
|
+
* // 'francois.muller@company.com'
|
|
989
|
+
* ```
|
|
990
|
+
*
|
|
991
|
+
* @see {@link toUrlSlug} for URL slug generation (combine with removeAccents for best results)
|
|
992
|
+
* @see {@link sanitizeString} for string sanitization
|
|
993
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize String.normalize() Documentation}
|
|
994
|
+
*/
|
|
995
|
+
declare const removeAccents: (str: string) => string;
|
|
996
|
+
/**
|
|
997
|
+
* Escapes HTML special characters to prevent XSS and display issues
|
|
998
|
+
*
|
|
999
|
+
* Converts dangerous HTML characters to their HTML entity equivalents, preventing
|
|
1000
|
+
* script injection (XSS) and ensuring proper text display in HTML contexts.
|
|
1001
|
+
*
|
|
1002
|
+
* Escaped characters:
|
|
1003
|
+
* - `&` → `&` (must be first to avoid double-escaping)
|
|
1004
|
+
* - `<` → `<` (prevents opening tags)
|
|
1005
|
+
* - `>` → `>` (prevents closing tags)
|
|
1006
|
+
* - `"` → `"` (prevents attribute injection)
|
|
1007
|
+
* - `'` → `'` (prevents attribute injection)
|
|
1008
|
+
*
|
|
1009
|
+
* ⚠️ SECURITY WARNING: This provides basic XSS protection but is NOT a complete solution.
|
|
1010
|
+
* For production HTML sanitization, use DOMPurify or similar dedicated libraries.
|
|
1011
|
+
* This function is safe for:
|
|
1012
|
+
* - Displaying user input as plain text in HTML
|
|
1013
|
+
* - Escaping attribute values in HTML
|
|
1014
|
+
* - Simple content rendering
|
|
1015
|
+
*
|
|
1016
|
+
* NOT sufficient for:
|
|
1017
|
+
* - Rich HTML content (use DOMPurify)
|
|
1018
|
+
* - JavaScript context (use different escaping)
|
|
1019
|
+
* - URL context (use encodeURIComponent)
|
|
1020
|
+
* - CSS context (use CSS-specific escaping)
|
|
1021
|
+
*
|
|
1022
|
+
* @param str - String containing HTML characters to escape
|
|
1023
|
+
* @returns String with HTML characters safely escaped as HTML entities
|
|
1024
|
+
*
|
|
1025
|
+
* @example
|
|
1026
|
+
* ```typescript
|
|
1027
|
+
* // Basic escaping
|
|
1028
|
+
* escapeHtmlChars('<script>alert("XSS")</script>')
|
|
1029
|
+
* // '<script>alert("XSS")</script>'
|
|
1030
|
+
*
|
|
1031
|
+
* escapeHtmlChars('5 < 10 & 10 > 5')
|
|
1032
|
+
* // '5 < 10 & 10 > 5'
|
|
1033
|
+
*
|
|
1034
|
+
* escapeHtmlChars('Say "Hello" & \'Goodbye\'')
|
|
1035
|
+
* // 'Say "Hello" & 'Goodbye''
|
|
1036
|
+
*
|
|
1037
|
+
* // Edge cases
|
|
1038
|
+
* escapeHtmlChars('') // ''
|
|
1039
|
+
* escapeHtmlChars('No special chars') // 'No special chars'
|
|
1040
|
+
* escapeHtmlChars('<') // '&lt;' (escapes already-escaped)
|
|
1041
|
+
* ```
|
|
1042
|
+
*
|
|
1043
|
+
* @example
|
|
1044
|
+
* ```typescript
|
|
1045
|
+
* // Real-world: Safely display user comments in HTML
|
|
1046
|
+
* interface Comment {
|
|
1047
|
+
* author: string
|
|
1048
|
+
* content: string
|
|
1049
|
+
* }
|
|
1050
|
+
*
|
|
1051
|
+
* function renderComment(comment: Comment): string {
|
|
1052
|
+
* const safeAuthor = escapeHtmlChars(comment.author)
|
|
1053
|
+
* const safeContent = escapeHtmlChars(comment.content)
|
|
1054
|
+
*
|
|
1055
|
+
* return `
|
|
1056
|
+
* <div class="comment">
|
|
1057
|
+
* <strong>${safeAuthor}</strong>
|
|
1058
|
+
* <p>${safeContent}</p>
|
|
1059
|
+
* </div>
|
|
1060
|
+
* `
|
|
1061
|
+
* }
|
|
1062
|
+
*
|
|
1063
|
+
* renderComment({
|
|
1064
|
+
* author: '<script>alert("XSS")</script>',
|
|
1065
|
+
* content: 'Great post! <3'
|
|
1066
|
+
* })
|
|
1067
|
+
* // Safe HTML output with escaped tags
|
|
1068
|
+
* ```
|
|
1069
|
+
*
|
|
1070
|
+
* @example
|
|
1071
|
+
* ```typescript
|
|
1072
|
+
* // Real-world: Generate safe HTML attributes
|
|
1073
|
+
* function generateDataAttribute(key: string, value: string): string {
|
|
1074
|
+
* const safeKey = toKebabCase(key)
|
|
1075
|
+
* const safeValue = escapeHtmlChars(value)
|
|
1076
|
+
* return `data-${safeKey}="${safeValue}"`
|
|
1077
|
+
* }
|
|
1078
|
+
*
|
|
1079
|
+
* generateDataAttribute('userInput', '<script>alert(1)</script>')
|
|
1080
|
+
* // 'data-user-input="<script>alert(1)</script>"'
|
|
1081
|
+
*
|
|
1082
|
+
* generateDataAttribute('description', 'Product "Premium" & more')
|
|
1083
|
+
* // 'data-description="Product "Premium" & more"'
|
|
1084
|
+
* ```
|
|
1085
|
+
*
|
|
1086
|
+
* @example
|
|
1087
|
+
* ```typescript
|
|
1088
|
+
* // Real-world: Escape user search query for display
|
|
1089
|
+
* function displaySearchResults(query: string, results: any[]): string {
|
|
1090
|
+
* const safeQuery = escapeHtmlChars(query)
|
|
1091
|
+
*
|
|
1092
|
+
* return `
|
|
1093
|
+
* <div class="search-results">
|
|
1094
|
+
* <h2>Results for: ${safeQuery}</h2>
|
|
1095
|
+
* <p>${results.length} results found</p>
|
|
1096
|
+
* </div>
|
|
1097
|
+
* `
|
|
1098
|
+
* }
|
|
1099
|
+
*
|
|
1100
|
+
* displaySearchResults('<img src=x onerror=alert(1)>', [])
|
|
1101
|
+
* // Safe display: "Results for: <img src=x onerror=alert(1)>"
|
|
1102
|
+
* ```
|
|
1103
|
+
*
|
|
1104
|
+
* @example
|
|
1105
|
+
* ```typescript
|
|
1106
|
+
* // Real-world: CSV to HTML table with safe content
|
|
1107
|
+
* function csvRowToHtmlRow(cells: string[]): string {
|
|
1108
|
+
* const safeCells = cells.map(cell => escapeHtmlChars(cell))
|
|
1109
|
+
* const tdElements = safeCells.map(cell => `<td>${cell}</td>`).join('')
|
|
1110
|
+
* return `<tr>${tdElements}</tr>`
|
|
1111
|
+
* }
|
|
1112
|
+
*
|
|
1113
|
+
* csvRowToHtmlRow(['Alice', '<script>evil</script>', '25'])
|
|
1114
|
+
* // '<tr><td>Alice</td><td><script>evil</script></td><td>25</td></tr>'
|
|
1115
|
+
* ```
|
|
1116
|
+
*
|
|
1117
|
+
* @example
|
|
1118
|
+
* ```typescript
|
|
1119
|
+
* // Edge case: Preventing double-escaping
|
|
1120
|
+
* const userInput = 'Hello & Goodbye'
|
|
1121
|
+
* const escaped = escapeHtmlChars(userInput)
|
|
1122
|
+
* // 'Hello & Goodbye'
|
|
1123
|
+
*
|
|
1124
|
+
* const doubleEscaped = escapeHtmlChars(escaped)
|
|
1125
|
+
* // 'Hello &amp; Goodbye' ⚠️ Over-escaped!
|
|
1126
|
+
*
|
|
1127
|
+
* // Solution: Only escape once, track escaped state
|
|
1128
|
+
* interface SafeString {
|
|
1129
|
+
* value: string
|
|
1130
|
+
* isEscaped: boolean
|
|
1131
|
+
* }
|
|
1132
|
+
*
|
|
1133
|
+
* function safeEscape(str: string | SafeString): SafeString {
|
|
1134
|
+
* if (typeof str === 'object' && str.isEscaped) {
|
|
1135
|
+
* return str
|
|
1136
|
+
* }
|
|
1137
|
+
* const value = typeof str === 'string' ? str : str.value
|
|
1138
|
+
* return { value: escapeHtmlChars(value), isEscaped: true }
|
|
1139
|
+
* }
|
|
1140
|
+
* ```
|
|
1141
|
+
*
|
|
1142
|
+
* @see {@link unescapeHtmlChars} for reversing HTML entity escaping
|
|
1143
|
+
* @see {@link sanitizeString} for basic string sanitization
|
|
1144
|
+
* @see {@link https://owasp.org/www-community/attacks/xss/ OWASP XSS Prevention Cheat Sheet}
|
|
1145
|
+
* @see {@link https://github.com/cure53/DOMPurify DOMPurify for production HTML sanitization}
|
|
1146
|
+
*/
|
|
1147
|
+
declare const escapeHtmlChars: (str: string) => string;
|
|
1148
|
+
/**
|
|
1149
|
+
* Converts HTML entities back to their original characters
|
|
1150
|
+
* @param str String containing HTML entities to unescape
|
|
1151
|
+
* @returns String with HTML entities converted back to original characters
|
|
1152
|
+
*/
|
|
1153
|
+
declare const unescapeHtmlChars: (str: string) => string;
|
|
1154
|
+
/**
|
|
1155
|
+
* Checks if a path matches a wildcard pattern using dot notation
|
|
1156
|
+
*
|
|
1157
|
+
* Supports wildcards (*) to match any segment at that position.
|
|
1158
|
+
* Useful for configuration paths, routing, permissions, and feature flags.
|
|
1159
|
+
*
|
|
1160
|
+
* @param path - Dot-notation path to test (e.g., 'features.auth.enabled')
|
|
1161
|
+
* @param pattern - Pattern with optional wildcards (e.g., 'features.*', 'features.*.enabled')
|
|
1162
|
+
* @returns True if path matches pattern, false otherwise
|
|
1163
|
+
*
|
|
1164
|
+
* @example
|
|
1165
|
+
* ```typescript
|
|
1166
|
+
* // Exact match
|
|
1167
|
+
* matchPathPattern('features.auth', 'features.auth') // true
|
|
1168
|
+
*
|
|
1169
|
+
* // Wildcard at end
|
|
1170
|
+
* matchPathPattern('features.auth', 'features.*') // true
|
|
1171
|
+
* matchPathPattern('features.payments', 'features.*') // true
|
|
1172
|
+
* matchPathPattern('other.value', 'features.*') // false
|
|
1173
|
+
*
|
|
1174
|
+
* // Wildcard in middle
|
|
1175
|
+
* matchPathPattern('features.auth.enabled', 'features.*.enabled') // true
|
|
1176
|
+
* matchPathPattern('features.payments.enabled', 'features.*.enabled') // true
|
|
1177
|
+
* matchPathPattern('features.auth.disabled', 'features.*.enabled') // false
|
|
1178
|
+
*
|
|
1179
|
+
* // Multiple wildcards
|
|
1180
|
+
* matchPathPattern('app.features.auth.oauth', 'app.*.*.oauth') // true
|
|
1181
|
+
* matchPathPattern('app.features.auth.saml', 'app.*.*.oauth') // false
|
|
1182
|
+
*
|
|
1183
|
+
* // No wildcard
|
|
1184
|
+
* matchPathPattern('exact.path.match', 'exact.path.match') // true
|
|
1185
|
+
* matchPathPattern('exact.path.nomatch', 'exact.path.match') // false
|
|
1186
|
+
* ```
|
|
1187
|
+
*
|
|
1188
|
+
* @example
|
|
1189
|
+
* ```typescript
|
|
1190
|
+
* // Real-world: Feature flag matching
|
|
1191
|
+
* const enabledFeatures = [
|
|
1192
|
+
* 'features.auth.oauth',
|
|
1193
|
+
* 'features.auth.saml',
|
|
1194
|
+
* 'features.payments.stripe',
|
|
1195
|
+
* ]
|
|
1196
|
+
*
|
|
1197
|
+
* const hasAuth = enabledFeatures.some(f =>
|
|
1198
|
+
* matchPathPattern(f, 'features.auth.*')
|
|
1199
|
+
* ) // true
|
|
1200
|
+
*
|
|
1201
|
+
* const hasPayments = enabledFeatures.some(f =>
|
|
1202
|
+
* matchPathPattern(f, 'features.payments.*')
|
|
1203
|
+
* ) // true
|
|
1204
|
+
*
|
|
1205
|
+
* // Permission matching
|
|
1206
|
+
* const userPermissions = ['admin.users.read', 'admin.users.write']
|
|
1207
|
+
* const canManageUsers = userPermissions.some(p =>
|
|
1208
|
+
* matchPathPattern(p, 'admin.users.*')
|
|
1209
|
+
* ) // true
|
|
1210
|
+
* ```
|
|
1211
|
+
*/
|
|
1212
|
+
declare function matchPathPattern(path: string, pattern: string): boolean;
|
|
1213
|
+
/**
|
|
1214
|
+
* Converts an environment variable key to dot-notation path
|
|
1215
|
+
*
|
|
1216
|
+
* Transforms uppercase underscore-separated env var names to lowercase
|
|
1217
|
+
* dot-notation paths. Optionally removes a prefix.
|
|
1218
|
+
*
|
|
1219
|
+
* Common convention: ENV_VAR_NAME → env.var.name
|
|
1220
|
+
*
|
|
1221
|
+
* @param envKey - Environment variable key (e.g., 'NX_FEATURES_AUTH', 'APP_DATABASE_HOST')
|
|
1222
|
+
* @param prefix - Optional prefix to remove (e.g., 'NX', 'APP'). Default: 'NX'
|
|
1223
|
+
* @returns Dot-notation path in lowercase
|
|
1224
|
+
*
|
|
1225
|
+
* @example
|
|
1226
|
+
* ```typescript
|
|
1227
|
+
* // Default prefix (NX)
|
|
1228
|
+
* envKeyToPath('NX_FEATURES_AUTH') // 'features.auth'
|
|
1229
|
+
* envKeyToPath('NX_FEATURES_PAYMENTS') // 'features.payments'
|
|
1230
|
+
* envKeyToPath('NX_DATABASE_HOST') // 'database.host'
|
|
1231
|
+
*
|
|
1232
|
+
* // Custom prefix
|
|
1233
|
+
* envKeyToPath('APP_DATABASE_HOST', 'APP') // 'database.host'
|
|
1234
|
+
* envKeyToPath('MY_CONFIG_VALUE', 'MY') // 'config.value'
|
|
1235
|
+
*
|
|
1236
|
+
* // No prefix
|
|
1237
|
+
* envKeyToPath('DATABASE_HOST', '') // 'database.host'
|
|
1238
|
+
* envKeyToPath('FEATURES_AUTH', '') // 'features.auth'
|
|
1239
|
+
* ```
|
|
1240
|
+
*
|
|
1241
|
+
* @example
|
|
1242
|
+
* ```typescript
|
|
1243
|
+
* // Real-world: Parse environment variables to config object
|
|
1244
|
+
* const envVars = {
|
|
1245
|
+
* 'APP_DATABASE_HOST': 'localhost',
|
|
1246
|
+
* 'APP_DATABASE_PORT': '5432',
|
|
1247
|
+
* 'APP_CACHE_TTL': '3600',
|
|
1248
|
+
* }
|
|
1249
|
+
*
|
|
1250
|
+
* const config = {}
|
|
1251
|
+
* Object.entries(envVars).forEach(([key, value]) => {
|
|
1252
|
+
* const path = envKeyToPath(key, 'APP')
|
|
1253
|
+
* // Use with setDeepValue: setDeepValue(config, path, value)
|
|
1254
|
+
* })
|
|
1255
|
+
* // Paths: 'database.host', 'database.port', 'cache.ttl'
|
|
1256
|
+
* ```
|
|
1257
|
+
*/
|
|
1258
|
+
declare function envKeyToPath(envKey: string, prefix?: string): string;
|
|
1259
|
+
/**
|
|
1260
|
+
* Converts a dot-notation path to environment variable key format
|
|
1261
|
+
*
|
|
1262
|
+
* Transforms lowercase dot-notation paths to uppercase underscore-separated
|
|
1263
|
+
* env var names. Optionally adds a prefix.
|
|
1264
|
+
*
|
|
1265
|
+
* Common convention: env.var.name → ENV_VAR_NAME
|
|
1266
|
+
*
|
|
1267
|
+
* @param path - Dot-notation path (e.g., 'features.auth', 'database.host')
|
|
1268
|
+
* @param prefix - Optional prefix to add (e.g., 'NX', 'APP'). Default: 'NX'
|
|
1269
|
+
* @returns Environment variable key in uppercase
|
|
1270
|
+
*
|
|
1271
|
+
* @example
|
|
1272
|
+
* ```typescript
|
|
1273
|
+
* // Default prefix (NX)
|
|
1274
|
+
* pathToEnvKey('features.auth') // 'NX_FEATURES_AUTH'
|
|
1275
|
+
* pathToEnvKey('features.payments') // 'NX_FEATURES_PAYMENTS'
|
|
1276
|
+
* pathToEnvKey('database.host') // 'NX_DATABASE_HOST'
|
|
1277
|
+
*
|
|
1278
|
+
* // Custom prefix
|
|
1279
|
+
* pathToEnvKey('database.host', 'APP') // 'APP_DATABASE_HOST'
|
|
1280
|
+
* pathToEnvKey('config.value', 'MY') // 'MY_CONFIG_VALUE'
|
|
1281
|
+
*
|
|
1282
|
+
* // No prefix
|
|
1283
|
+
* pathToEnvKey('database.host', '') // 'DATABASE_HOST'
|
|
1284
|
+
* pathToEnvKey('features.auth', '') // 'FEATURES_AUTH'
|
|
1285
|
+
* ```
|
|
1286
|
+
*
|
|
1287
|
+
* @example
|
|
1288
|
+
* ```typescript
|
|
1289
|
+
* // Real-world: Generate .env file from config object
|
|
1290
|
+
* const config = {
|
|
1291
|
+
* database: { host: 'localhost', port: 5432 },
|
|
1292
|
+
* cache: { ttl: 3600 }
|
|
1293
|
+
* }
|
|
1294
|
+
*
|
|
1295
|
+
* const envVars = [
|
|
1296
|
+
* `${pathToEnvKey('database.host', 'APP')}=localhost`,
|
|
1297
|
+
* `${pathToEnvKey('database.port', 'APP')}=5432`,
|
|
1298
|
+
* `${pathToEnvKey('cache.ttl', 'APP')}=3600`,
|
|
1299
|
+
* ]
|
|
1300
|
+
* // Output:
|
|
1301
|
+
* // APP_DATABASE_HOST=localhost
|
|
1302
|
+
* // APP_DATABASE_PORT=5432
|
|
1303
|
+
* // APP_CACHE_TTL=3600
|
|
1304
|
+
* ```
|
|
1305
|
+
*/
|
|
1306
|
+
declare function pathToEnvKey(path: string, prefix?: string): string;
|
|
1307
|
+
|
|
1308
|
+
declare const strings_capitalizeEachWord: typeof capitalizeEachWord;
|
|
1309
|
+
declare const strings_capitalizeFirst: typeof capitalizeFirst;
|
|
1310
|
+
declare const strings_cleanJsonChars: typeof cleanJsonChars;
|
|
1311
|
+
declare const strings_contains: typeof contains;
|
|
1312
|
+
declare const strings_countOccurrences: typeof countOccurrences;
|
|
1313
|
+
declare const strings_endsWith: typeof endsWith;
|
|
1314
|
+
declare const strings_ensureEndsWith: typeof ensureEndsWith;
|
|
1315
|
+
declare const strings_ensureStartsWith: typeof ensureStartsWith;
|
|
1316
|
+
declare const strings_envKeyToPath: typeof envKeyToPath;
|
|
1317
|
+
declare const strings_escapeHtmlChars: typeof escapeHtmlChars;
|
|
1318
|
+
declare const strings_isEmail: typeof isEmail;
|
|
1319
|
+
declare const strings_isEmpty: typeof isEmpty;
|
|
1320
|
+
declare const strings_matchPathPattern: typeof matchPathPattern;
|
|
1321
|
+
declare const strings_padEnd: typeof padEnd;
|
|
1322
|
+
declare const strings_padStart: typeof padStart;
|
|
1323
|
+
declare const strings_pathToEnvKey: typeof pathToEnvKey;
|
|
1324
|
+
declare const strings_removeAccents: typeof removeAccents;
|
|
1325
|
+
declare const strings_repeatString: typeof repeatString;
|
|
1326
|
+
declare const strings_replaceAllOccurrences: typeof replaceAllOccurrences;
|
|
1327
|
+
declare const strings_reverseString: typeof reverseString;
|
|
1328
|
+
declare const strings_sanitizeString: typeof sanitizeString;
|
|
1329
|
+
declare const strings_startsWith: typeof startsWith;
|
|
1330
|
+
declare const strings_stripFromEnd: typeof stripFromEnd;
|
|
1331
|
+
declare const strings_stripFromStart: typeof stripFromStart;
|
|
1332
|
+
declare const strings_toCamelCase: typeof toCamelCase;
|
|
1333
|
+
declare const strings_toKebabCase: typeof toKebabCase;
|
|
1334
|
+
declare const strings_toLowerCase: typeof toLowerCase;
|
|
1335
|
+
declare const strings_toPascalCase: typeof toPascalCase;
|
|
1336
|
+
declare const strings_toSnakeCase: typeof toSnakeCase;
|
|
1337
|
+
declare const strings_toUpperCase: typeof toUpperCase;
|
|
1338
|
+
declare const strings_toUrlSlug: typeof toUrlSlug;
|
|
1339
|
+
declare const strings_trim: typeof trim;
|
|
1340
|
+
declare const strings_trimEnd: typeof trimEnd;
|
|
1341
|
+
declare const strings_trimStart: typeof trimStart;
|
|
1342
|
+
declare const strings_truncateString: typeof truncateString;
|
|
1343
|
+
declare const strings_unescapeHtmlChars: typeof unescapeHtmlChars;
|
|
1344
|
+
declare const strings_unescapeUnicode: typeof unescapeUnicode;
|
|
1345
|
+
declare namespace strings {
|
|
1346
|
+
export { strings_capitalizeEachWord as capitalizeEachWord, strings_capitalizeFirst as capitalizeFirst, strings_cleanJsonChars as cleanJsonChars, strings_contains as contains, strings_countOccurrences as countOccurrences, strings_endsWith as endsWith, strings_ensureEndsWith as ensureEndsWith, strings_ensureStartsWith as ensureStartsWith, strings_envKeyToPath as envKeyToPath, strings_escapeHtmlChars as escapeHtmlChars, strings_isEmail as isEmail, strings_isEmpty as isEmpty, strings_matchPathPattern as matchPathPattern, strings_padEnd as padEnd, strings_padStart as padStart, strings_pathToEnvKey as pathToEnvKey, strings_removeAccents as removeAccents, strings_repeatString as repeatString, strings_replaceAllOccurrences as replaceAllOccurrences, strings_reverseString as reverseString, strings_sanitizeString as sanitizeString, strings_startsWith as startsWith, strings_stripFromEnd as stripFromEnd, strings_stripFromStart as stripFromStart, strings_toCamelCase as toCamelCase, strings_toKebabCase as toKebabCase, strings_toLowerCase as toLowerCase, strings_toPascalCase as toPascalCase, strings_toSnakeCase as toSnakeCase, strings_toUpperCase as toUpperCase, strings_toUrlSlug as toUrlSlug, strings_trim as trim, strings_trimEnd as trimEnd, strings_trimStart as trimStart, strings_truncateString as truncateString, strings_unescapeHtmlChars as unescapeHtmlChars, strings_unescapeUnicode as unescapeUnicode };
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
export { repeatString as A, replaceAllOccurrences as B, countOccurrences as C, isEmpty as D, isEmail as E, toUrlSlug as F, removeAccents as G, escapeHtmlChars as H, unescapeHtmlChars as I, matchPathPattern as J, envKeyToPath as K, pathToEnvKey as L, sanitizeString as a, stripFromEnd as b, cleanJsonChars as c, ensureStartsWith as d, ensureEndsWith as e, stripFromStart as f, toLowerCase as g, toUpperCase as h, capitalizeFirst as i, capitalizeEachWord as j, toCamelCase as k, toSnakeCase as l, toKebabCase as m, toPascalCase as n, contains as o, startsWith as p, endsWith as q, padStart as r, strings as s, truncateString as t, unescapeUnicode as u, padEnd as v, trim as w, trimStart as x, trimEnd as y, reverseString as z };
|