@gitsense/gsc-utils 0.2.24 → 0.2.27
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 +380 -62
- package/dist/gsc-utils.cjs.js +14270 -523
- package/dist/gsc-utils.esm.js +14270 -523
- package/package.json +1 -1
- package/src/AnalyzerUtils/cloner.js +149 -0
- package/src/AnalyzerUtils/constants.js +1 -1
- package/src/AnalyzerUtils/defaultPromptLoader.js +10 -10
- package/src/AnalyzerUtils/discovery.js +48 -39
- package/src/AnalyzerUtils/index.js +13 -7
- package/src/AnalyzerUtils/instructionLoader.js +6 -6
- package/src/AnalyzerUtils/jsonParser.js +35 -0
- package/src/AnalyzerUtils/management.js +6 -6
- package/src/AnalyzerUtils/saver.js +5 -5
- package/src/AnalyzerUtils/schemaLoader.js +194 -26
- package/src/AnalyzerUtils/updater.js +187 -0
- package/src/CodeBlockUtils/blockProcessor.js +14 -32
- package/src/CodeBlockUtils/constants.js +8 -4
- package/src/CodeBlockUtils/headerUtils.js +19 -3
- package/src/CodeBlockUtils/index.js +7 -6
- package/src/CodeBlockUtils/lineageTracer.js +95 -0
- package/src/CompactChatUtils/CompactedMessageUtils.js +224 -0
- package/src/CompactChatUtils/README.md +321 -0
- package/src/CompactChatUtils/ReferenceMessageUtils.js +143 -0
- package/src/CompactChatUtils/index.js +40 -0
- package/src/ContextUtils.js +41 -5
- package/src/DomUtils.js +559 -0
- package/src/GSToolBlockUtils.js +66 -1
- package/src/GitSenseChatUtils.js +108 -16
- package/src/LanguageNameUtils.js +171 -0
- package/src/MarkdownUtils.js +127 -0
- package/src/MessageUtils.js +1 -1
- package/src/MetaRawResultUtils.js +244 -0
- package/src/ObjectUtils.js +54 -0
- package/src/PatchUtils/constants.js +9 -3
- package/src/PatchUtils/patchParser.js +60 -37
- package/src/PatchUtils/patchProcessor.js +8 -5
- package/src/PatchUtils/patchVerifier/detectAndFixOverlappingHunks.js +1 -1
- package/src/SVGUtils.js +1467 -0
- package/src/SharedUtils/stringUtils.js +303 -0
- package/src/CodeBlockUtils/blockProcessor.js.rej +0 -8
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Component: StringUtils
|
|
3
|
+
* Block-UUID: 7069f2b4-f0b3-4dcd-9eae-5cecaddcdd66
|
|
4
|
+
* Version: 1.0.0
|
|
5
|
+
* Description: A collection of utility functions for common string manipulation and processing tasks within GitSense Chat.
|
|
6
|
+
* Language: JavaScript
|
|
7
|
+
* Created-at: 2025-10-30T22:06:47.000Z
|
|
8
|
+
* Authors: Qwen 3 Coder 480B - Cerebras (v1.0.0)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Splits a string into an array of substrings based on a delimiter, up to a specified limit.
|
|
14
|
+
* This mimics behavior similar to Python's `str.split(sep, maxsplit)` or Java's `String.split(regex, limit)`.
|
|
15
|
+
*
|
|
16
|
+
* @param {string} text - The string to split.
|
|
17
|
+
* @param {string|RegExp} delimiter - The delimiter to split the string by.
|
|
18
|
+
* @param {number} [limit=Infinity] - The maximum number of parts to return. The final part will contain the remainder of the string.
|
|
19
|
+
* @returns {string[]} An array of substrings.
|
|
20
|
+
*/
|
|
21
|
+
function splitWithLimit(text, delimiter, limit = Infinity) {
|
|
22
|
+
if (limit === 0) {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
if (limit === 1) {
|
|
26
|
+
return [text];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const parts = [];
|
|
30
|
+
let remainingText = text;
|
|
31
|
+
let count = 0;
|
|
32
|
+
|
|
33
|
+
// Handle RegExp delimiter
|
|
34
|
+
if (delimiter instanceof RegExp) {
|
|
35
|
+
// For RegExp, we need to manually find matches and slice the string
|
|
36
|
+
// This is a simplified approach and might not cover all edge cases of global regex matching
|
|
37
|
+
// especially with capture groups or zero-width matches.
|
|
38
|
+
const flags = delimiter.flags.includes('g') ? delimiter.flags : delimiter.flags + 'g';
|
|
39
|
+
const globalDelimiter = new RegExp(delimiter.source, flags);
|
|
40
|
+
let match;
|
|
41
|
+
let lastIndex = 0;
|
|
42
|
+
let matchCount = 0;
|
|
43
|
+
|
|
44
|
+
// We need (limit - 1) matches to create `limit` parts
|
|
45
|
+
while ((match = globalDelimiter.exec(remainingText)) !== null && matchCount < limit - 1) {
|
|
46
|
+
// Add the part before the match
|
|
47
|
+
parts.push(remainingText.substring(lastIndex, match.index));
|
|
48
|
+
// Add the matched delimiter part itself if it's non-empty (important for zero-width matches)
|
|
49
|
+
// This part is tricky; typically, the delimiter itself is not part of the returned array.
|
|
50
|
+
// The logic below follows the standard split behavior where the delimiter is not included.
|
|
51
|
+
lastIndex = match.index + match[0].length;
|
|
52
|
+
matchCount++;
|
|
53
|
+
// Avoid infinite loop for zero-length matches
|
|
54
|
+
if (match.index === globalDelimiter.lastIndex) {
|
|
55
|
+
globalDelimiter.lastIndex++;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Add the final part
|
|
59
|
+
parts.push(remainingText.substring(lastIndex));
|
|
60
|
+
} else {
|
|
61
|
+
// Handle string delimiter
|
|
62
|
+
const delimLength = delimiter.length;
|
|
63
|
+
if (delimLength === 0) {
|
|
64
|
+
// If delimiter is an empty string, split by characters
|
|
65
|
+
// This is consistent with JS String.split('')
|
|
66
|
+
return text.split('').slice(0, limit);
|
|
67
|
+
}
|
|
68
|
+
let index;
|
|
69
|
+
while (count < limit - 1 && (index = remainingText.indexOf(delimiter)) !== -1) {
|
|
70
|
+
parts.push(remainingText.substring(0, index));
|
|
71
|
+
remainingText = remainingText.substring(index + delimLength);
|
|
72
|
+
count++;
|
|
73
|
+
}
|
|
74
|
+
// Add the final part (the rest of the string)
|
|
75
|
+
parts.push(remainingText);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return parts;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Capitalizes the first character of a string.
|
|
83
|
+
*
|
|
84
|
+
* @param {string} text - The string to capitalize.
|
|
85
|
+
* @returns {string} The string with its first character capitalized.
|
|
86
|
+
*/
|
|
87
|
+
function capitalize(text) {
|
|
88
|
+
if (!text || typeof text !== 'string') {
|
|
89
|
+
return text;
|
|
90
|
+
}
|
|
91
|
+
return text.charAt(0).toUpperCase() + text.slice(1);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Converts a string to title case (capitalizing the first letter of each word).
|
|
96
|
+
* Words are sequences of characters separated by space, tab, or newline.
|
|
97
|
+
*
|
|
98
|
+
* @param {string} text - The string to convert.
|
|
99
|
+
* @returns {string} The string in title case.
|
|
100
|
+
*/
|
|
101
|
+
function titleCase(text) {
|
|
102
|
+
if (!text || typeof text !== 'string') {
|
|
103
|
+
return text;
|
|
104
|
+
}
|
|
105
|
+
// Split by one or more whitespace characters
|
|
106
|
+
return text.split(/\s+/).map(word => capitalize(word)).join(' ');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Converts a string to camelCase.
|
|
111
|
+
*
|
|
112
|
+
* @param {string} text - The string to convert.
|
|
113
|
+
* @returns {string} The string in camelCase.
|
|
114
|
+
*/
|
|
115
|
+
function camelCase(text) {
|
|
116
|
+
if (!text || typeof text !== 'string') {
|
|
117
|
+
return text;
|
|
118
|
+
}
|
|
119
|
+
return text
|
|
120
|
+
.split(/\W+/) // Split on non-word characters
|
|
121
|
+
.map((word, index) => {
|
|
122
|
+
if (index === 0) {
|
|
123
|
+
// First word is lowercase
|
|
124
|
+
return word.toLowerCase();
|
|
125
|
+
}
|
|
126
|
+
// Subsequent words have their first letter capitalized
|
|
127
|
+
return capitalize(word.toLowerCase());
|
|
128
|
+
})
|
|
129
|
+
.join('');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Converts a string to PascalCase.
|
|
134
|
+
*
|
|
135
|
+
* @param {string} text - The string to convert.
|
|
136
|
+
* @returns {string} The string in PascalCase.
|
|
137
|
+
*/
|
|
138
|
+
function pascalCase(text) {
|
|
139
|
+
if (!text || typeof text !== 'string') {
|
|
140
|
+
return text;
|
|
141
|
+
}
|
|
142
|
+
return text
|
|
143
|
+
.split(/\W+/) // Split on non-word characters
|
|
144
|
+
.map(word => capitalize(word.toLowerCase()))
|
|
145
|
+
.join('');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Converts a string to kebab-case.
|
|
150
|
+
*
|
|
151
|
+
* @param {string} text - The string to convert.
|
|
152
|
+
* @returns {string} The string in kebab-case.
|
|
153
|
+
*/
|
|
154
|
+
function kebabCase(text) {
|
|
155
|
+
if (!text || typeof text !== 'string') {
|
|
156
|
+
return text;
|
|
157
|
+
}
|
|
158
|
+
return text
|
|
159
|
+
.split(/\W+/) // Split on non-word characters
|
|
160
|
+
.map(word => word.toLowerCase())
|
|
161
|
+
.filter(word => word.length > 0) // Remove empty strings from split
|
|
162
|
+
.join('-');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Converts a string to snake_case.
|
|
167
|
+
*
|
|
168
|
+
* @param {string} text - The string to convert.
|
|
169
|
+
* @returns {string} The string in snake_case.
|
|
170
|
+
*/
|
|
171
|
+
function snakeCase(text) {
|
|
172
|
+
if (!text || typeof text !== 'string') {
|
|
173
|
+
return text;
|
|
174
|
+
}
|
|
175
|
+
return text
|
|
176
|
+
.split(/\W+/) // Split on non-word characters
|
|
177
|
+
.map(word => word.toLowerCase())
|
|
178
|
+
.filter(word => word.length > 0) // Remove empty strings from split
|
|
179
|
+
.join('_');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Converts a string to CONSTANT_CASE.
|
|
184
|
+
*
|
|
185
|
+
* @param {string} text - The string to convert.
|
|
186
|
+
* @returns {string} The string in CONSTANT_CASE.
|
|
187
|
+
*/
|
|
188
|
+
function constantCase(text) {
|
|
189
|
+
if (!text || typeof text !== 'string') {
|
|
190
|
+
return text;
|
|
191
|
+
}
|
|
192
|
+
return text
|
|
193
|
+
.split(/\W+/) // Split on non-word characters
|
|
194
|
+
.map(word => word.toUpperCase())
|
|
195
|
+
.filter(word => word.length > 0) // Remove empty strings from split
|
|
196
|
+
.join('_');
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Trims leading and trailing whitespace and normalizes internal whitespace
|
|
201
|
+
* (replacing sequences of whitespace characters with a single space).
|
|
202
|
+
*
|
|
203
|
+
* @param {string} text - The string to trim and normalize.
|
|
204
|
+
* @returns {string} The trimmed and normalized string.
|
|
205
|
+
*/
|
|
206
|
+
function trimWhitespace(text) {
|
|
207
|
+
if (!text || typeof text !== 'string') {
|
|
208
|
+
return text;
|
|
209
|
+
}
|
|
210
|
+
return text.trim().replace(/\s+/g, ' ');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Truncates a string to a specified maximum length and appends a suffix if truncated.
|
|
215
|
+
*
|
|
216
|
+
* @param {string} text - The string to truncate.
|
|
217
|
+
* @param {number} maxLength - The maximum length of the string.
|
|
218
|
+
* @param {string} [suffix='...'] - The suffix to append if the string is truncated.
|
|
219
|
+
* @returns {string} The truncated string with suffix, or the original string if not truncated.
|
|
220
|
+
*/
|
|
221
|
+
function truncate(text, maxLength, suffix = '...') {
|
|
222
|
+
if (!text || typeof text !== 'string' || maxLength <= 0) {
|
|
223
|
+
return '';
|
|
224
|
+
}
|
|
225
|
+
if (text.length <= maxLength) {
|
|
226
|
+
return text;
|
|
227
|
+
}
|
|
228
|
+
// Ensure we don't cut the suffix itself if maxLength is very small
|
|
229
|
+
const suffixLength = suffix.length;
|
|
230
|
+
if (maxLength <= suffixLength) {
|
|
231
|
+
// If max length is less than or equal to suffix, return a truncated suffix
|
|
232
|
+
// This is a bit of an edge case, but handles it gracefully.
|
|
233
|
+
return suffix.substring(0, maxLength);
|
|
234
|
+
}
|
|
235
|
+
return text.substring(0, maxLength - suffixLength) + suffix;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Escapes HTML characters in a string to prevent them from being interpreted as HTML.
|
|
240
|
+
*
|
|
241
|
+
* @param {string} text - The string to escape.
|
|
242
|
+
* @returns {string} The escaped string.
|
|
243
|
+
*/
|
|
244
|
+
function escapeHtml(text) {
|
|
245
|
+
if (!text || typeof text !== 'string') {
|
|
246
|
+
return text;
|
|
247
|
+
}
|
|
248
|
+
const htmlEscapes = {
|
|
249
|
+
'&': '&',
|
|
250
|
+
'<': '<',
|
|
251
|
+
'>': '>',
|
|
252
|
+
'"': '"',
|
|
253
|
+
"'": ''',
|
|
254
|
+
};
|
|
255
|
+
return text.replace(/[&<>"']/g, match => htmlEscapes[match]);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Removes HTML tags from a string.
|
|
260
|
+
*
|
|
261
|
+
* @param {string} text - The string to strip tags from.
|
|
262
|
+
* @returns {string} The string without HTML tags.
|
|
263
|
+
*/
|
|
264
|
+
function stripHtml(text) {
|
|
265
|
+
if (!text || typeof text !== 'string') {
|
|
266
|
+
return text;
|
|
267
|
+
}
|
|
268
|
+
return text.replace(/<[^>]*>/g, '');
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Performs a basic check to see if a string looks like a valid email address.
|
|
273
|
+
* This is a simple validation and should not be used for robust security checks.
|
|
274
|
+
*
|
|
275
|
+
* @param {string} email - The email string to validate.
|
|
276
|
+
* @returns {boolean} True if the string looks like a valid email, false otherwise.
|
|
277
|
+
*/
|
|
278
|
+
function isValidEmail(email) {
|
|
279
|
+
if (!email || typeof email !== 'string') {
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
// A basic regex for email validation. Note: Fully validating email addresses
|
|
283
|
+
// is extremely complex. This covers common cases.
|
|
284
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
285
|
+
return emailRegex.test(email);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
module.exports = {
|
|
290
|
+
splitWithLimit,
|
|
291
|
+
capitalize,
|
|
292
|
+
titleCase,
|
|
293
|
+
camelCase,
|
|
294
|
+
kebabCase,
|
|
295
|
+
snakeCase,
|
|
296
|
+
constantCase,
|
|
297
|
+
pascalCase,
|
|
298
|
+
trimWhitespace,
|
|
299
|
+
truncate,
|
|
300
|
+
escapeHtml,
|
|
301
|
+
stripHtml,
|
|
302
|
+
isValidEmail,
|
|
303
|
+
};
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
@@ -262,6 +283,7 @@
|
|
2
|
-
function processCodeBlocks(text, options = { silent: false, validatePatches: false }) {
|
|
3
|
-
if (typeof text !== "string") { // Allow empty strings
|
|
4
|
-
console.warn("Warning: Input must be a string.");
|
|
5
|
-
+ return { blocks: [], warnings: [{ type: 'invalid_input', message: 'Input must be a string.' }], hasIncompleteBlocks: false, lastIncompleteBlock: null };
|
|
6
|
-
}
|
|
7
|
-
if (text.trim() === "") {
|
|
8
|
-
return { blocks: [], warnings: [], hasIncompleteBlocks: false, lastIncompleteBlock: null };
|