@holoscript/std 7.0.0 → 7.0.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/README.md +8 -0
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/{chunk-PFX2JM4X.js → chunk-FAMOLKH3.js} +2 -1
- package/dist/{chunk-PFX2JM4X.js.map → chunk-FAMOLKH3.js.map} +1 -1
- package/dist/{chunk-DZHAVOCH.js → chunk-SOKH6FJ7.js} +31 -6
- package/dist/chunk-SOKH6FJ7.js.map +1 -0
- package/dist/collections.cjs +1 -0
- package/dist/collections.cjs.map +1 -1
- package/dist/collections.js +2 -2
- package/dist/fs/fs.d.ts.map +1 -1
- package/dist/fs/index.cjs +221 -8510
- package/dist/fs/index.cjs.map +1 -1
- package/dist/fs/index.js +832 -118
- package/dist/fs/index.js.map +1 -1
- package/dist/fs/sandbox.d.ts +49 -0
- package/dist/fs/sandbox.d.ts.map +1 -0
- package/dist/index.cjs +3494 -12672
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -7
- package/dist/index.js.map +1 -1
- package/dist/math.cjs +18 -3
- package/dist/math.cjs.map +1 -1
- package/dist/math.js +2 -2
- package/dist/string.js +1 -1
- package/dist/time.js +1 -1
- package/dist/traits/EconomicPrimitives.js +1 -1
- package/dist/traits/EconomicTraits.js +1 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +19 -9
- package/dist/__tests__/EconomicPrimitives.test.d.ts +0 -2
- package/dist/__tests__/EconomicPrimitives.test.d.ts.map +0 -1
- package/dist/__tests__/EconomicTraits.test.d.ts +0 -2
- package/dist/__tests__/EconomicTraits.test.d.ts.map +0 -1
- package/dist/__tests__/SimulationLabPrimitives.test.d.ts +0 -2
- package/dist/__tests__/SimulationLabPrimitives.test.d.ts.map +0 -1
- package/dist/__tests__/collections.test.d.ts +0 -2
- package/dist/__tests__/collections.test.d.ts.map +0 -1
- package/dist/__tests__/events.test.d.ts +0 -2
- package/dist/__tests__/events.test.d.ts.map +0 -1
- package/dist/__tests__/index.test.d.ts +0 -2
- package/dist/__tests__/index.test.d.ts.map +0 -1
- package/dist/__tests__/materials.test.d.ts +0 -2
- package/dist/__tests__/materials.test.d.ts.map +0 -1
- package/dist/__tests__/math.test.d.ts +0 -2
- package/dist/__tests__/math.test.d.ts.map +0 -1
- package/dist/__tests__/physics.test.d.ts +0 -2
- package/dist/__tests__/physics.test.d.ts.map +0 -1
- package/dist/__tests__/spatial.test.d.ts +0 -2
- package/dist/__tests__/spatial.test.d.ts.map +0 -1
- package/dist/__tests__/standard-library-fundamentals.test.d.ts +0 -2
- package/dist/__tests__/standard-library-fundamentals.test.d.ts.map +0 -1
- package/dist/__tests__/string-and-time-utilities.test.d.ts +0 -2
- package/dist/__tests__/string-and-time-utilities.test.d.ts.map +0 -1
- package/dist/__tests__/string-decoupled.test.d.ts +0 -2
- package/dist/__tests__/string-decoupled.test.d.ts.map +0 -1
- package/dist/__tests__/string.test.d.ts +0 -2
- package/dist/__tests__/string.test.d.ts.map +0 -1
- package/dist/__tests__/time.test.d.ts +0 -2
- package/dist/__tests__/time.test.d.ts.map +0 -1
- package/dist/chunk-DZHAVOCH.js.map +0 -1
- package/dist/chunk-PR4QN5HX.js +0 -43
- package/dist/chunk-XBA4ARST.js +0 -9313
- package/dist/chunk-XBA4ARST.js.map +0 -1
- package/dist/fs/__tests__/file-watch-system.test.d.ts +0 -2
- package/dist/fs/__tests__/file-watch-system.test.d.ts.map +0 -1
- package/dist/fs/__tests__/path-utilities.test.d.ts +0 -2
- package/dist/fs/__tests__/path-utilities.test.d.ts.map +0 -1
- package/dist/fs/__tests__/path.test.d.ts +0 -2
- package/dist/fs/__tests__/path.test.d.ts.map +0 -1
- package/dist/string.basic.test.d.ts +0 -5
- package/dist/string.basic.test.d.ts.map +0 -1
- package/dist/string.test.d.ts +0 -5
- package/dist/string.test.d.ts.map +0 -1
- package/dist/traits/__tests__/ARTraits.test.d.ts +0 -2
- package/dist/traits/__tests__/ARTraits.test.d.ts.map +0 -1
- package/dist/traits/__tests__/EconomicPrimitives.test.d.ts +0 -2
- package/dist/traits/__tests__/EconomicPrimitives.test.d.ts.map +0 -1
- package/dist/traits/__tests__/EconomicTraits.test.d.ts +0 -2
- package/dist/traits/__tests__/EconomicTraits.test.d.ts.map +0 -1
- package/error-utf8.log +0 -6
- package/error.log +0 -0
- package/src/__tests__/EconomicPrimitives.test.ts +0 -690
- package/src/__tests__/EconomicTraits.test.ts +0 -425
- package/src/__tests__/SimulationLabPrimitives.test.ts +0 -415
- package/src/__tests__/collections.test.ts +0 -182
- package/src/__tests__/events.test.ts +0 -135
- package/src/__tests__/index.test.ts +0 -14
- package/src/__tests__/materials.test.ts +0 -84
- package/src/__tests__/math.test.ts +0 -246
- package/src/__tests__/physics.test.ts +0 -83
- package/src/__tests__/spatial.test.ts +0 -333
- package/src/__tests__/standard-library-fundamentals.test.ts +0 -1318
- package/src/__tests__/string-and-time-utilities.test.ts +0 -1070
- package/src/__tests__/string-decoupled.test.ts +0 -13
- package/src/__tests__/string.test.ts +0 -164
- package/src/__tests__/time.test.ts +0 -110
- package/src/collections.ts +0 -850
- package/src/events.ts +0 -88
- package/src/fs/__tests__/file-watch-system.test.ts +0 -675
- package/src/fs/__tests__/path-utilities.test.ts +0 -375
- package/src/fs/__tests__/path.test.ts +0 -287
- package/src/fs/fs.ts +0 -692
- package/src/fs/index.ts +0 -194
- package/src/fs/path.ts +0 -310
- package/src/fs/watch.ts +0 -413
- package/src/index.ts +0 -476
- package/src/materials.ts +0 -109
- package/src/math.ts +0 -674
- package/src/physics.ts +0 -141
- package/src/spatial.ts +0 -320
- package/src/string.basic.test.ts +0 -14
- package/src/string.test.ts +0 -335
- package/src/string.ts +0 -531
- package/src/time.ts +0 -621
- package/src/traits/ARTraits.ts +0 -103
- package/src/traits/EconomicPrimitives.ts +0 -755
- package/src/traits/EconomicTraits.ts +0 -552
- package/src/traits/IoTTraits.ts +0 -102
- package/src/traits/SimulationLabPrimitives.ts +0 -650
- package/src/traits/SimulationLabTraits.ts +0 -191
- package/src/traits/VRRTraits.ts +0 -326
- package/src/traits/__tests__/ARTraits.test.ts +0 -8
- package/src/traits/__tests__/EconomicPrimitives.test.ts +0 -14
- package/src/traits/__tests__/EconomicTraits.test.ts +0 -16
- package/src/types.ts +0 -418
- package/std_error.log +0 -5
- package/tsconfig.json +0 -18
- package/tsup.config.ts +0 -20
- package/vitest.config.ts +0 -10
- /package/dist/{chunk-PR4QN5HX.js.map → chunk-DGUM43GV.js.map} +0 -0
package/src/string.ts
DELETED
|
@@ -1,531 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @holoscript/std - String Module
|
|
3
|
-
*
|
|
4
|
-
* String manipulation utilities for HoloScript Plus programs.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Check if a string is empty or only whitespace
|
|
9
|
-
*/
|
|
10
|
-
export function isBlank(s: string): boolean {
|
|
11
|
-
return s.trim().length === 0;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Check if a string is not empty and not only whitespace
|
|
16
|
-
*/
|
|
17
|
-
export function isNotBlank(s: string): boolean {
|
|
18
|
-
return s.trim().length > 0;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Capitalize the first letter of a string
|
|
23
|
-
*/
|
|
24
|
-
export function capitalize(s: string): string {
|
|
25
|
-
if (s.length === 0) return s;
|
|
26
|
-
return s[0].toUpperCase() + s.slice(1);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Capitalize the first letter of each word
|
|
31
|
-
*/
|
|
32
|
-
export function titleCase(s: string): string {
|
|
33
|
-
return s.split(/\s+/).map(capitalize).join(' ');
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Convert to camelCase
|
|
38
|
-
*/
|
|
39
|
-
export function camelCase(s: string): string {
|
|
40
|
-
return s
|
|
41
|
-
.replace(/[-_\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ''))
|
|
42
|
-
.replace(/^[A-Z]/, (c) => c.toLowerCase());
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Convert to PascalCase
|
|
47
|
-
*/
|
|
48
|
-
export function pascalCase(s: string): string {
|
|
49
|
-
const camel = camelCase(s);
|
|
50
|
-
return camel.length > 0 ? camel[0].toUpperCase() + camel.slice(1) : camel;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Convert to snake_case
|
|
55
|
-
*/
|
|
56
|
-
export function snakeCase(s: string): string {
|
|
57
|
-
return s
|
|
58
|
-
.replace(/([a-z])([A-Z])/g, '$1_$2')
|
|
59
|
-
.replace(/[-\s]+/g, '_')
|
|
60
|
-
.toLowerCase();
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Convert to kebab-case
|
|
65
|
-
*/
|
|
66
|
-
export function kebabCase(s: string): string {
|
|
67
|
-
return s
|
|
68
|
-
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
69
|
-
.replace(/[_\s]+/g, '-')
|
|
70
|
-
.toLowerCase();
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Convert to SCREAMING_SNAKE_CASE
|
|
75
|
-
*/
|
|
76
|
-
export function constantCase(s: string): string {
|
|
77
|
-
return snakeCase(s).toUpperCase();
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Pad a string on the left to reach target length
|
|
82
|
-
*/
|
|
83
|
-
export function padLeft(s: string, length: number, char = ' '): string {
|
|
84
|
-
return s.padStart(length, char);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Pad a string on the right to reach target length
|
|
89
|
-
*/
|
|
90
|
-
export function padRight(s: string, length: number, char = ' '): string {
|
|
91
|
-
return s.padEnd(length, char);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Pad a string on both sides to center it
|
|
96
|
-
*/
|
|
97
|
-
export function center(s: string, length: number, char = ' '): string {
|
|
98
|
-
if (s.length >= length) return s;
|
|
99
|
-
const totalPadding = length - s.length;
|
|
100
|
-
const leftPadding = Math.floor(totalPadding / 2);
|
|
101
|
-
const rightPadding = totalPadding - leftPadding;
|
|
102
|
-
return char.repeat(leftPadding) + s + char.repeat(rightPadding);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Truncate a string to max length, adding ellipsis if needed
|
|
107
|
-
*/
|
|
108
|
-
export function truncate(s: string, maxLength: number, ellipsis = '...'): string {
|
|
109
|
-
if (s.length <= maxLength) return s;
|
|
110
|
-
return s.slice(0, maxLength - ellipsis.length) + ellipsis;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Truncate a string in the middle
|
|
115
|
-
*/
|
|
116
|
-
export function truncateMiddle(s: string, maxLength: number, ellipsis = '...'): string {
|
|
117
|
-
if (s.length <= maxLength) return s;
|
|
118
|
-
const availableLength = maxLength - ellipsis.length;
|
|
119
|
-
const leftLength = Math.floor(availableLength / 2);
|
|
120
|
-
const rightLength = Math.ceil(availableLength / 2);
|
|
121
|
-
return s.slice(0, leftLength) + ellipsis + s.slice(-rightLength);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Repeat a string n times
|
|
126
|
-
*/
|
|
127
|
-
export function repeat(s: string, count: number): string {
|
|
128
|
-
return s.repeat(count);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Reverse a string
|
|
133
|
-
*/
|
|
134
|
-
export function reverse(s: string): string {
|
|
135
|
-
return [...s].reverse().join('');
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Count occurrences of a substring
|
|
140
|
-
*/
|
|
141
|
-
export function count(s: string, substring: string): number {
|
|
142
|
-
if (substring.length === 0) return 0;
|
|
143
|
-
let count = 0;
|
|
144
|
-
let pos = 0;
|
|
145
|
-
while ((pos = s.indexOf(substring, pos)) !== -1) {
|
|
146
|
-
count++;
|
|
147
|
-
pos += 1;
|
|
148
|
-
}
|
|
149
|
-
return count;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Check if string contains a substring (case-insensitive)
|
|
154
|
-
*/
|
|
155
|
-
export function containsIgnoreCase(s: string, substring: string): boolean {
|
|
156
|
-
return s.toLowerCase().includes(substring.toLowerCase());
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Check if string starts with prefix (case-insensitive)
|
|
161
|
-
*/
|
|
162
|
-
export function startsWithIgnoreCase(s: string, prefix: string): boolean {
|
|
163
|
-
return s.toLowerCase().startsWith(prefix.toLowerCase());
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Check if string ends with suffix (case-insensitive)
|
|
168
|
-
*/
|
|
169
|
-
export function endsWithIgnoreCase(s: string, suffix: string): boolean {
|
|
170
|
-
return s.toLowerCase().endsWith(suffix.toLowerCase());
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Remove all whitespace from a string
|
|
175
|
-
*/
|
|
176
|
-
export function removeWhitespace(s: string): string {
|
|
177
|
-
return s.replace(/\s+/g, '');
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Collapse multiple spaces into single space
|
|
182
|
-
*/
|
|
183
|
-
export function collapseWhitespace(s: string): string {
|
|
184
|
-
return s.replace(/\s+/g, ' ').trim();
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Remove a prefix if present
|
|
189
|
-
*/
|
|
190
|
-
export function removePrefix(s: string, prefix: string): string {
|
|
191
|
-
return s.startsWith(prefix) ? s.slice(prefix.length) : s;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Remove a suffix if present
|
|
196
|
-
*/
|
|
197
|
-
export function removeSuffix(s: string, suffix: string): string {
|
|
198
|
-
return s.endsWith(suffix) ? s.slice(0, -suffix.length) : s;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Wrap a string with prefix and suffix
|
|
203
|
-
*/
|
|
204
|
-
export function wrap(s: string, wrapper: string): string;
|
|
205
|
-
export function wrap(s: string, prefix: string, suffix: string): string;
|
|
206
|
-
export function wrap(s: string, prefixOrWrapper: string, suffix?: string): string {
|
|
207
|
-
if (suffix === undefined) {
|
|
208
|
-
return prefixOrWrapper + s + prefixOrWrapper;
|
|
209
|
-
}
|
|
210
|
-
return prefixOrWrapper + s + suffix;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Unwrap a string by removing matching prefix and suffix
|
|
215
|
-
*/
|
|
216
|
-
export function unwrap(s: string, wrapper: string): string;
|
|
217
|
-
export function unwrap(s: string, prefix: string, suffix: string): string;
|
|
218
|
-
export function unwrap(s: string, prefixOrWrapper: string, suffix?: string): string {
|
|
219
|
-
const prefix = prefixOrWrapper;
|
|
220
|
-
const actualSuffix = suffix ?? prefixOrWrapper;
|
|
221
|
-
if (s.startsWith(prefix) && s.endsWith(actualSuffix)) {
|
|
222
|
-
return s.slice(prefix.length, -actualSuffix.length || undefined);
|
|
223
|
-
}
|
|
224
|
-
return s;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Split a string into lines
|
|
229
|
-
*/
|
|
230
|
-
export function lines(s: string): string[] {
|
|
231
|
-
return s.split(/\r?\n/);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Split a string into words
|
|
236
|
-
*/
|
|
237
|
-
export function words(s: string): string[] {
|
|
238
|
-
return s.split(/\s+/).filter((w) => w.length > 0);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Split a string into characters
|
|
243
|
-
*/
|
|
244
|
-
export function chars(s: string): string[] {
|
|
245
|
-
return [...s];
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Join strings with a separator
|
|
250
|
-
*/
|
|
251
|
-
export function join(strings: string[], separator = ''): string {
|
|
252
|
-
return strings.join(separator);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Format a template string with values
|
|
257
|
-
*/
|
|
258
|
-
export function format(template: string, values: Record<string, unknown>): string {
|
|
259
|
-
return template.replace(/\{(\w+)\}/g, (match, key) => {
|
|
260
|
-
return key in values ? String(values[key]) : match;
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* Format a number as a string with grouping
|
|
266
|
-
*/
|
|
267
|
-
export function formatNumber(n: number, options: Intl.NumberFormatOptions = {}): string {
|
|
268
|
-
return n.toLocaleString('en-US', options);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* Format a number with commas
|
|
273
|
-
*/
|
|
274
|
-
export function numberWithCommas(n: number): string {
|
|
275
|
-
return n.toLocaleString('en-US');
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Format bytes as human-readable string
|
|
280
|
-
*/
|
|
281
|
-
export function formatBytes(bytes: number, decimals = 2): string {
|
|
282
|
-
if (bytes === 0) return '0 B';
|
|
283
|
-
const k = 1024;
|
|
284
|
-
if (bytes < 0) return `${bytes} B`;
|
|
285
|
-
const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
|
|
286
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
287
|
-
return parseFloat((bytes / Math.pow(k, i)).toFixed(decimals)) + ' ' + sizes[i];
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* Format duration in milliseconds as human-readable string
|
|
292
|
-
*/
|
|
293
|
-
export function formatDuration(ms: number): string {
|
|
294
|
-
if (ms < 1000) return `${ms}ms`;
|
|
295
|
-
if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
|
|
296
|
-
if (ms < 3600000) {
|
|
297
|
-
return `${(ms / 60000).toFixed(1)}m`;
|
|
298
|
-
}
|
|
299
|
-
const _hours = Math.floor(ms / 3600000);
|
|
300
|
-
const _mins = Math.floor((ms % 3600000) / 60000);
|
|
301
|
-
return `${(ms / 3600000).toFixed(1)}h`;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* Escape HTML special characters
|
|
306
|
-
*/
|
|
307
|
-
export function escapeHtml(s: string): string {
|
|
308
|
-
const htmlEntities: Record<string, string> = {
|
|
309
|
-
'&': '&',
|
|
310
|
-
'<': '<',
|
|
311
|
-
'>': '>',
|
|
312
|
-
'"': '"',
|
|
313
|
-
"'": ''',
|
|
314
|
-
};
|
|
315
|
-
return s.replace(/[&<>"']/g, (c) => htmlEntities[c]);
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
/**
|
|
319
|
-
* Unescape HTML entities
|
|
320
|
-
*/
|
|
321
|
-
export function unescapeHtml(s: string): string {
|
|
322
|
-
const htmlEntities: Record<string, string> = {
|
|
323
|
-
'&': '&',
|
|
324
|
-
'<': '<',
|
|
325
|
-
'>': '>',
|
|
326
|
-
'"': '"',
|
|
327
|
-
''': "'",
|
|
328
|
-
''': "'",
|
|
329
|
-
'/': '/',
|
|
330
|
-
};
|
|
331
|
-
return s.replace(
|
|
332
|
-
/&(?:amp|lt|gt|quot|#39|#x27|#x2F);/g,
|
|
333
|
-
(entity) => htmlEntities[entity] || entity
|
|
334
|
-
);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* Escape regex special characters
|
|
339
|
-
*/
|
|
340
|
-
export function escapeRegex(s: string): string {
|
|
341
|
-
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
/**
|
|
345
|
-
* Generate a slug from a string
|
|
346
|
-
*/
|
|
347
|
-
export function slugify(s: string): string {
|
|
348
|
-
return s
|
|
349
|
-
.toLowerCase()
|
|
350
|
-
.normalize('NFD')
|
|
351
|
-
.replace(/[\u0300-\u036f]/g, '') // Remove diacritics
|
|
352
|
-
.replace(/[^a-z0-9]+/g, '-')
|
|
353
|
-
.replace(/^-+|-+$/g, '');
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
/**
|
|
357
|
-
* Check if string is a valid identifier
|
|
358
|
-
*/
|
|
359
|
-
export function isValidIdentifier(s: string): boolean {
|
|
360
|
-
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(s);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* Check if string is numeric
|
|
365
|
-
*/
|
|
366
|
-
export function isNumeric(s: string): boolean {
|
|
367
|
-
return !isNaN(parseFloat(s)) && isFinite(Number(s));
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
/**
|
|
371
|
-
* Check if string is alphanumeric
|
|
372
|
-
*/
|
|
373
|
-
export function isAlphanumeric(s: string): boolean {
|
|
374
|
-
return /^[a-zA-Z0-9]+$/.test(s);
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
/**
|
|
378
|
-
* Check if string is alphabetic
|
|
379
|
-
*/
|
|
380
|
-
export function isAlpha(s: string): boolean {
|
|
381
|
-
return /^[a-zA-Z]+$/.test(s);
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
/**
|
|
385
|
-
* Generate a random string
|
|
386
|
-
*/
|
|
387
|
-
export function randomString(
|
|
388
|
-
length: number,
|
|
389
|
-
charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
|
|
390
|
-
): string {
|
|
391
|
-
let result = '';
|
|
392
|
-
for (let i = 0; i < length; i++) {
|
|
393
|
-
result += charset[Math.floor(Math.random() * charset.length)];
|
|
394
|
-
}
|
|
395
|
-
return result;
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
/**
|
|
399
|
-
* Generate a UUID v4
|
|
400
|
-
*/
|
|
401
|
-
export function uuid(): string {
|
|
402
|
-
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
403
|
-
const r = (Math.random() * 16) | 0;
|
|
404
|
-
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
405
|
-
return v.toString(16);
|
|
406
|
-
});
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
/**
|
|
410
|
-
* Indent each line of a string
|
|
411
|
-
*/
|
|
412
|
-
export function indent(s: string, spaces: number, char = ' '): string {
|
|
413
|
-
const prefix = char.repeat(spaces);
|
|
414
|
-
return s
|
|
415
|
-
.split('\n')
|
|
416
|
-
.map((line) => prefix + line)
|
|
417
|
-
.join('\n');
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
/**
|
|
421
|
-
* Dedent a string (remove common leading whitespace)
|
|
422
|
-
*/
|
|
423
|
-
export function dedent(s: string): string {
|
|
424
|
-
const linesList = lines(s);
|
|
425
|
-
const nonEmptyLines = linesList.filter((l) => l.trim().length > 0);
|
|
426
|
-
if (nonEmptyLines.length === 0) return s;
|
|
427
|
-
|
|
428
|
-
const minIndent = Math.min(
|
|
429
|
-
...nonEmptyLines.map((line) => {
|
|
430
|
-
const match = line.match(/^(\s*)/);
|
|
431
|
-
return match ? match[1].length : 0;
|
|
432
|
-
})
|
|
433
|
-
);
|
|
434
|
-
|
|
435
|
-
return linesList.map((line) => line.slice(minIndent)).join('\n');
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
/**
|
|
439
|
-
* Word wrap text to a maximum width
|
|
440
|
-
*/
|
|
441
|
-
export function wordWrap(s: string, maxWidth: number): string {
|
|
442
|
-
const wordsList = words(s);
|
|
443
|
-
const linesList: string[] = [];
|
|
444
|
-
let currentLine = '';
|
|
445
|
-
|
|
446
|
-
for (const word of wordsList) {
|
|
447
|
-
if (currentLine.length === 0) {
|
|
448
|
-
currentLine = word;
|
|
449
|
-
} else if (currentLine.length + 1 + word.length <= maxWidth) {
|
|
450
|
-
currentLine += ' ' + word;
|
|
451
|
-
} else {
|
|
452
|
-
linesList.push(currentLine);
|
|
453
|
-
currentLine = word;
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
if (currentLine.length > 0) {
|
|
458
|
-
linesList.push(currentLine);
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
return linesList.join('\n');
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
/**
|
|
465
|
-
* Levenshtein distance between two strings
|
|
466
|
-
*/
|
|
467
|
-
export function levenshtein(a: string, b: string): number {
|
|
468
|
-
if (a.length === 0) return b.length;
|
|
469
|
-
if (b.length === 0) return a.length;
|
|
470
|
-
|
|
471
|
-
const matrix: number[][] = [];
|
|
472
|
-
|
|
473
|
-
for (let i = 0; i <= b.length; i++) {
|
|
474
|
-
matrix[i] = [i];
|
|
475
|
-
}
|
|
476
|
-
for (let j = 0; j <= a.length; j++) {
|
|
477
|
-
matrix[0][j] = j;
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
for (let i = 1; i <= b.length; i++) {
|
|
481
|
-
for (let j = 1; j <= a.length; j++) {
|
|
482
|
-
if (b[i - 1] === a[j - 1]) {
|
|
483
|
-
matrix[i][j] = matrix[i - 1][j - 1];
|
|
484
|
-
} else {
|
|
485
|
-
matrix[i][j] = Math.min(
|
|
486
|
-
matrix[i - 1][j - 1] + 1, // substitution
|
|
487
|
-
matrix[i][j - 1] + 1, // insertion
|
|
488
|
-
matrix[i - 1][j] + 1 // deletion
|
|
489
|
-
);
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
return matrix[b.length][a.length];
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
function lcsLength(a: string, b: string): number {
|
|
498
|
-
const n = b.length;
|
|
499
|
-
const dp: number[] = new Array(n + 1).fill(0);
|
|
500
|
-
for (let i = 0; i < a.length; i++) {
|
|
501
|
-
let prev = 0;
|
|
502
|
-
for (let j = 0; j < n; j++) {
|
|
503
|
-
const tmp = dp[j + 1];
|
|
504
|
-
dp[j + 1] = a[i] === b[j] ? prev + 1 : Math.max(dp[j + 1], dp[j]);
|
|
505
|
-
prev = tmp;
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
return dp[n];
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
/**
|
|
512
|
-
* Check similarity between two strings (0-1) using Sorensen-Dice over LCS
|
|
513
|
-
*/
|
|
514
|
-
export function similarity(a: string, b: string): number {
|
|
515
|
-
const total = a.length + b.length;
|
|
516
|
-
if (total === 0) return 1;
|
|
517
|
-
return (2 * lcsLength(a, b)) / total;
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
/**
|
|
521
|
-
* Extract HoloScript trait annotations (@name) and JSX component names from code
|
|
522
|
-
*/
|
|
523
|
-
export function extractTraits(code: string): string[] {
|
|
524
|
-
const traits = new Set<string>();
|
|
525
|
-
let m: RegExpExecArray | null;
|
|
526
|
-
const atRe = /@([a-zA-Z_]\w*)/g;
|
|
527
|
-
while ((m = atRe.exec(code)) !== null) traits.add(`@${m[1]}`);
|
|
528
|
-
const tagRe = /<([A-Z][a-zA-Z0-9]*)\b/g;
|
|
529
|
-
while ((m = tagRe.exec(code)) !== null) traits.add(m[1]);
|
|
530
|
-
return [...traits];
|
|
531
|
-
}
|