@gitsense/gsc-utils 0.2.25 → 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.
Files changed (35) hide show
  1. package/README.md +380 -62
  2. package/dist/gsc-utils.cjs.js +2203 -331
  3. package/dist/gsc-utils.esm.js +2203 -331
  4. package/package.json +1 -1
  5. package/src/AnalyzerUtils/cloner.js +149 -0
  6. package/src/AnalyzerUtils/constants.js +1 -1
  7. package/src/AnalyzerUtils/defaultPromptLoader.js +10 -10
  8. package/src/AnalyzerUtils/discovery.js +48 -39
  9. package/src/AnalyzerUtils/index.js +13 -7
  10. package/src/AnalyzerUtils/instructionLoader.js +6 -6
  11. package/src/AnalyzerUtils/jsonParser.js +35 -0
  12. package/src/AnalyzerUtils/management.js +6 -6
  13. package/src/AnalyzerUtils/saver.js +5 -5
  14. package/src/AnalyzerUtils/schemaLoader.js +194 -26
  15. package/src/AnalyzerUtils/updater.js +187 -0
  16. package/src/CodeBlockUtils/blockProcessor.js +14 -32
  17. package/src/CodeBlockUtils/index.js +7 -6
  18. package/src/CodeBlockUtils/lineageTracer.js +95 -0
  19. package/src/CompactChatUtils/CompactedMessageUtils.js +224 -0
  20. package/src/CompactChatUtils/README.md +321 -0
  21. package/src/CompactChatUtils/ReferenceMessageUtils.js +143 -0
  22. package/src/CompactChatUtils/index.js +40 -0
  23. package/src/ContextUtils.js +40 -4
  24. package/src/DomUtils.js +86 -12
  25. package/src/GSToolBlockUtils.js +66 -1
  26. package/src/GitSenseChatUtils.js +58 -5
  27. package/src/MarkdownUtils.js +4 -1
  28. package/src/MessageUtils.js +1 -1
  29. package/src/MetaRawResultUtils.js +244 -0
  30. package/src/PatchUtils/constants.js +9 -3
  31. package/src/PatchUtils/patchParser.js +60 -36
  32. package/src/PatchUtils/patchVerifier/detectAndFixOverlappingHunks.js +1 -1
  33. package/src/SVGUtils.js +57 -0
  34. package/src/SharedUtils/stringUtils.js +303 -0
  35. 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
+ '&': '&amp;',
250
+ '<': '&lt;',
251
+ '>': '&gt;',
252
+ '"': '&quot;',
253
+ "'": '&#39;',
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 };