spck 0.3.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.
Files changed (155) hide show
  1. package/.oxlintrc.json +49 -0
  2. package/LICENSE +21 -0
  3. package/README.md +631 -0
  4. package/bin/cli.js +20 -0
  5. package/bin/validate-cwd.js +41 -0
  6. package/dist/config/__tests__/config.test.d.ts +2 -0
  7. package/dist/config/__tests__/config.test.js +262 -0
  8. package/dist/config/__tests__/credentials.test.d.ts +2 -0
  9. package/dist/config/__tests__/credentials.test.js +360 -0
  10. package/dist/config/config.d.ts +33 -0
  11. package/dist/config/config.js +185 -0
  12. package/dist/config/credentials.d.ts +75 -0
  13. package/dist/config/credentials.js +259 -0
  14. package/dist/config/server-selection.d.ts +40 -0
  15. package/dist/config/server-selection.js +130 -0
  16. package/dist/connection/__tests__/firebase-auth.test.d.ts +2 -0
  17. package/dist/connection/__tests__/firebase-auth.test.js +96 -0
  18. package/dist/connection/__tests__/hmac.test.d.ts +2 -0
  19. package/dist/connection/__tests__/hmac.test.js +372 -0
  20. package/dist/connection/auth.d.ts +13 -0
  21. package/dist/connection/auth.js +91 -0
  22. package/dist/connection/firebase-auth.d.ts +40 -0
  23. package/dist/connection/firebase-auth.js +429 -0
  24. package/dist/connection/hmac.d.ts +24 -0
  25. package/dist/connection/hmac.js +109 -0
  26. package/dist/i18n/index.d.ts +25 -0
  27. package/dist/i18n/index.js +101 -0
  28. package/dist/i18n/locales/en.json +313 -0
  29. package/dist/i18n/locales/es.json +302 -0
  30. package/dist/i18n/locales/fr.json +302 -0
  31. package/dist/i18n/locales/id.json +302 -0
  32. package/dist/i18n/locales/ja.json +302 -0
  33. package/dist/i18n/locales/ko.json +302 -0
  34. package/dist/i18n/locales/locales/en.json +309 -0
  35. package/dist/i18n/locales/locales/es.json +302 -0
  36. package/dist/i18n/locales/locales/fr.json +302 -0
  37. package/dist/i18n/locales/locales/id.json +302 -0
  38. package/dist/i18n/locales/locales/ja.json +302 -0
  39. package/dist/i18n/locales/locales/ko.json +302 -0
  40. package/dist/i18n/locales/locales/pt.json +302 -0
  41. package/dist/i18n/locales/locales/zh-Hans.json +302 -0
  42. package/dist/i18n/locales/pt.json +302 -0
  43. package/dist/i18n/locales/zh-Hans.json +302 -0
  44. package/dist/index.d.ts +25 -0
  45. package/dist/index.js +493 -0
  46. package/dist/proxy/ProxyClient.d.ts +125 -0
  47. package/dist/proxy/ProxyClient.js +781 -0
  48. package/dist/proxy/ProxySocketWrapper.d.ts +43 -0
  49. package/dist/proxy/ProxySocketWrapper.js +98 -0
  50. package/dist/proxy/__tests__/ProxyClient.test.d.ts +2 -0
  51. package/dist/proxy/__tests__/ProxyClient.test.js +445 -0
  52. package/dist/proxy/__tests__/ProxySocketWrapper.test.d.ts +2 -0
  53. package/dist/proxy/__tests__/ProxySocketWrapper.test.js +190 -0
  54. package/dist/proxy/__tests__/handshake-validation.test.d.ts +2 -0
  55. package/dist/proxy/__tests__/handshake-validation.test.js +282 -0
  56. package/dist/proxy/__tests__/token-refresh-race.test.d.ts +14 -0
  57. package/dist/proxy/__tests__/token-refresh-race.test.js +173 -0
  58. package/dist/proxy/chunking.d.ts +53 -0
  59. package/dist/proxy/chunking.js +127 -0
  60. package/dist/proxy/handshake-validation.d.ts +21 -0
  61. package/dist/proxy/handshake-validation.js +49 -0
  62. package/dist/rpc/__tests__/router.test.d.ts +2 -0
  63. package/dist/rpc/__tests__/router.test.js +262 -0
  64. package/dist/rpc/router.d.ts +37 -0
  65. package/dist/rpc/router.js +132 -0
  66. package/dist/services/BrowserProxyService.d.ts +13 -0
  67. package/dist/services/BrowserProxyService.js +139 -0
  68. package/dist/services/FilesystemService.d.ts +99 -0
  69. package/dist/services/FilesystemService.js +742 -0
  70. package/dist/services/GitService.d.ts +243 -0
  71. package/dist/services/GitService.js +1439 -0
  72. package/dist/services/SearchService.d.ts +93 -0
  73. package/dist/services/SearchService.js +670 -0
  74. package/dist/services/TerminalService.d.ts +62 -0
  75. package/dist/services/TerminalService.js +337 -0
  76. package/dist/services/__tests__/BrowserProxyService.test.d.ts +2 -0
  77. package/dist/services/__tests__/BrowserProxyService.test.js +145 -0
  78. package/dist/services/__tests__/FilesystemService.test.d.ts +2 -0
  79. package/dist/services/__tests__/FilesystemService.test.js +609 -0
  80. package/dist/services/__tests__/GitService.test.d.ts +2 -0
  81. package/dist/services/__tests__/GitService.test.js +953 -0
  82. package/dist/services/__tests__/SearchService.test.d.ts +2 -0
  83. package/dist/services/__tests__/SearchService.test.js +384 -0
  84. package/dist/services/__tests__/TerminalService.test.d.ts +2 -0
  85. package/dist/services/__tests__/TerminalService.test.js +513 -0
  86. package/dist/setup/wizard.d.ts +10 -0
  87. package/dist/setup/wizard.js +172 -0
  88. package/dist/types.d.ts +196 -0
  89. package/dist/types.js +44 -0
  90. package/dist/utils/__tests__/gitignore.test.d.ts +2 -0
  91. package/dist/utils/__tests__/gitignore.test.js +127 -0
  92. package/dist/utils/gitignore.d.ts +24 -0
  93. package/dist/utils/gitignore.js +77 -0
  94. package/dist/utils/logger.d.ts +96 -0
  95. package/dist/utils/logger.js +456 -0
  96. package/dist/utils/project-dir.d.ts +51 -0
  97. package/dist/utils/project-dir.js +191 -0
  98. package/dist/utils/ripgrep.d.ts +34 -0
  99. package/dist/utils/ripgrep.js +148 -0
  100. package/dist/utils/tool-detection.d.ts +17 -0
  101. package/dist/utils/tool-detection.js +126 -0
  102. package/dist/watcher/FileWatcher.d.ts +10 -0
  103. package/dist/watcher/FileWatcher.js +42 -0
  104. package/package.json +70 -0
  105. package/src/config/__tests__/config.test.ts +318 -0
  106. package/src/config/__tests__/credentials.test.ts +494 -0
  107. package/src/config/config.ts +206 -0
  108. package/src/config/credentials.ts +302 -0
  109. package/src/config/server-selection.ts +150 -0
  110. package/src/connection/__tests__/firebase-auth.test.ts +121 -0
  111. package/src/connection/__tests__/hmac.test.ts +509 -0
  112. package/src/connection/auth.ts +140 -0
  113. package/src/connection/firebase-auth.ts +504 -0
  114. package/src/connection/hmac.ts +139 -0
  115. package/src/i18n/index.ts +119 -0
  116. package/src/i18n/locales/en.json +313 -0
  117. package/src/i18n/locales/es.json +302 -0
  118. package/src/i18n/locales/fr.json +302 -0
  119. package/src/i18n/locales/id.json +302 -0
  120. package/src/i18n/locales/ja.json +302 -0
  121. package/src/i18n/locales/ko.json +302 -0
  122. package/src/i18n/locales/pt.json +302 -0
  123. package/src/i18n/locales/zh-Hans.json +302 -0
  124. package/src/index.ts +542 -0
  125. package/src/proxy/ProxyClient.ts +968 -0
  126. package/src/proxy/ProxySocketWrapper.ts +113 -0
  127. package/src/proxy/__tests__/ProxyClient.test.ts +575 -0
  128. package/src/proxy/__tests__/ProxySocketWrapper.test.ts +251 -0
  129. package/src/proxy/__tests__/handshake-validation.test.ts +367 -0
  130. package/src/proxy/chunking.ts +162 -0
  131. package/src/proxy/handshake-validation.ts +64 -0
  132. package/src/rpc/__tests__/router.test.ts +400 -0
  133. package/src/rpc/router.ts +183 -0
  134. package/src/services/BrowserProxyService.ts +179 -0
  135. package/src/services/FilesystemService.ts +841 -0
  136. package/src/services/GitService.ts +1639 -0
  137. package/src/services/SearchService.ts +809 -0
  138. package/src/services/TerminalService.ts +413 -0
  139. package/src/services/__tests__/BrowserProxyService.test.ts +155 -0
  140. package/src/services/__tests__/FilesystemService.test.ts +1002 -0
  141. package/src/services/__tests__/GitService.test.ts +1552 -0
  142. package/src/services/__tests__/SearchService.test.ts +484 -0
  143. package/src/services/__tests__/TerminalService.test.ts +702 -0
  144. package/src/setup/wizard.ts +242 -0
  145. package/src/types/fossil-delta.d.ts +4 -0
  146. package/src/types.ts +287 -0
  147. package/src/utils/__tests__/gitignore.test.ts +174 -0
  148. package/src/utils/gitignore.ts +91 -0
  149. package/src/utils/logger.ts +578 -0
  150. package/src/utils/project-dir.ts +218 -0
  151. package/src/utils/ripgrep.ts +180 -0
  152. package/src/utils/tool-detection.ts +141 -0
  153. package/src/watcher/FileWatcher.ts +53 -0
  154. package/tsconfig.json +24 -0
  155. package/vitest.config.ts +19 -0
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=SearchService.test.d.ts.map
@@ -0,0 +1,384 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
+ /**
3
+ * Tests for SearchService - Line Trimming Functionality
4
+ */
5
+ import { SearchService } from '../SearchService.js';
6
+ // Mock ripgrep utilities to prevent spawning child processes during tests
7
+ vi.mock('../../utils/ripgrep', () => ({
8
+ isRipgrepAvailable: vi.fn().mockResolvedValue(false),
9
+ executeRipgrep: vi.fn(),
10
+ executeRipgrepStream: vi.fn(),
11
+ }));
12
+ describe('SearchService - Line Trimming', () => {
13
+ let service;
14
+ beforeEach(() => {
15
+ service = new SearchService(process.cwd(), 10 * 1024 * 1024, 64 * 1024);
16
+ });
17
+ // Helper to access private trimLineToMatch method
18
+ function trimLineToMatch(lineText, matchStart, matchEnd, maxLength) {
19
+ return service.trimLineToMatch(lineText, matchStart, matchEnd, maxLength);
20
+ }
21
+ describe('trimLineToMatch', () => {
22
+ it('should return full line if shorter than maxLength', () => {
23
+ const lineText = 'Short line with test';
24
+ const result = trimLineToMatch(lineText, 16, 20, 100);
25
+ expect(result.line).toBe(lineText);
26
+ expect(result.offset).toBe(0);
27
+ });
28
+ it('should trim long line and center match', () => {
29
+ const lineText = 'This is a very long line with the word test in the middle and more content after';
30
+ const matchStart = lineText.indexOf('test'); // position of "test" = 39
31
+ const matchEnd = matchStart + 4; // 43
32
+ const maxLength = 40;
33
+ const result = trimLineToMatch(lineText, matchStart, matchEnd, maxLength);
34
+ expect(result.line.length).toBeLessThanOrEqual(maxLength);
35
+ expect(result.line).toContain('test');
36
+ // Verify match is in the trimmed line
37
+ const relativeStart = matchStart - result.offset;
38
+ const relativeEnd = matchEnd - result.offset;
39
+ expect(result.line.substring(relativeStart, relativeEnd)).toBe('test');
40
+ });
41
+ it('should handle match at start of line', () => {
42
+ const lineText = 'test is at the start of this very long line with lots of content';
43
+ const matchStart = 0;
44
+ const matchEnd = 4;
45
+ const maxLength = 30;
46
+ const result = trimLineToMatch(lineText, matchStart, matchEnd, maxLength);
47
+ expect(result.line.length).toBeLessThanOrEqual(maxLength);
48
+ expect(result.line).toMatch(/^test/);
49
+ expect(result.offset).toBe(0);
50
+ });
51
+ it('should handle match at end of line', () => {
52
+ const lineText = 'This is a very long line with lots of content and the match is at the end test';
53
+ const matchStart = lineText.indexOf('test'); // position of "test" = 75
54
+ const matchEnd = matchStart + 4; // 79
55
+ const maxLength = 30;
56
+ const result = trimLineToMatch(lineText, matchStart, matchEnd, maxLength);
57
+ expect(result.line.length).toBeLessThanOrEqual(maxLength);
58
+ expect(result.line).toMatch(/test$/);
59
+ // Verify offset is calculated correctly
60
+ const relativeStart = matchStart - result.offset;
61
+ expect(result.line.substring(relativeStart)).toBe('test');
62
+ });
63
+ it('should handle match longer than maxLength', () => {
64
+ const lineText = 'Before verylongmatchthatexceedsmaxlength After';
65
+ const matchStart = 7;
66
+ const matchEnd = 40;
67
+ const maxLength = 20;
68
+ const result = trimLineToMatch(lineText, matchStart, matchEnd, maxLength);
69
+ expect(result.line.length).toBeLessThanOrEqual(maxLength);
70
+ expect(result.offset).toBe(matchStart);
71
+ expect(result.line).toMatch(/^verylongmatch/);
72
+ });
73
+ it('should handle maxLength of 38 (actual UI value)', () => {
74
+ const lineText = 'Some code before the test keyword and some code after it';
75
+ const matchStart = 21; // position of "test"
76
+ const matchEnd = 25;
77
+ const maxLength = 38;
78
+ const result = trimLineToMatch(lineText, matchStart, matchEnd, maxLength);
79
+ expect(result.line.length).toBeLessThanOrEqual(maxLength);
80
+ expect(result.line).toContain('test');
81
+ // Verify match positions are correct
82
+ const relativeStart = matchStart - result.offset;
83
+ const relativeEnd = matchEnd - result.offset;
84
+ expect(result.line.substring(relativeStart, relativeEnd)).toBe('test');
85
+ });
86
+ it('should handle invalid maxLength (0 or negative)', () => {
87
+ const lineText = 'Line with test';
88
+ const result = trimLineToMatch(lineText, 10, 14, 0);
89
+ // Should use default maxLength of 500
90
+ expect(result.line).toBe(lineText);
91
+ expect(result.offset).toBe(0);
92
+ });
93
+ it('should handle invalid match positions', () => {
94
+ const lineText = 'Valid line text';
95
+ // matchStart >= matchEnd
96
+ const result1 = trimLineToMatch(lineText, 10, 10, 50);
97
+ expect(result1.line).toBe(lineText);
98
+ // matchStart < 0
99
+ const result2 = trimLineToMatch(lineText, -1, 5, 50);
100
+ expect(result2.line).toBe(lineText);
101
+ // matchEnd > lineText.length
102
+ const result3 = trimLineToMatch(lineText, 5, 100, 50);
103
+ expect(result3.line).toBe(lineText);
104
+ });
105
+ it('should provide equal context on both sides when possible', () => {
106
+ const lineText = 'aaaaaaaaaa test bbbbbbbbbb'; // 10 chars before and after "test"
107
+ const matchStart = 11;
108
+ const matchEnd = 15;
109
+ const maxLength = 20; // 4 for match + 16 for context = 8 per side
110
+ const result = trimLineToMatch(lineText, matchStart, matchEnd, maxLength);
111
+ expect(result.line.length).toBeLessThanOrEqual(maxLength);
112
+ expect(result.line).toContain('test');
113
+ // Should have roughly equal context (allowing for word boundaries)
114
+ const relativeStart = matchStart - result.offset;
115
+ const beforeMatch = result.line.substring(0, relativeStart);
116
+ const afterMatch = result.line.substring(relativeStart + 4);
117
+ // Allow for some imbalance due to word boundaries and ellipsis
118
+ expect(Math.abs(beforeMatch.length - afterMatch.length)).toBeLessThanOrEqual(8);
119
+ });
120
+ it('should handle unicode characters correctly', () => {
121
+ const lineText = 'Some 中文 before test and 中文 after';
122
+ const matchStart = lineText.indexOf('test'); // position of "test"
123
+ const matchEnd = matchStart + 4;
124
+ const maxLength = 30;
125
+ const result = trimLineToMatch(lineText, matchStart, matchEnd, maxLength);
126
+ expect(result.line).toContain('test');
127
+ expect(result.line).toContain('中文');
128
+ const relativeStart = matchStart - result.offset;
129
+ const relativeEnd = matchEnd - result.offset;
130
+ expect(result.line.substring(relativeStart, relativeEnd)).toBe('test');
131
+ });
132
+ it('should handle very long minified line (realistic scenario)', () => {
133
+ // Simulate a minified JavaScript line
134
+ const minifiedLine = 'function(){var a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10,k=11,l=12,m=13,n=14,o=15,p=16,q=17,r=18,s=19,t=20,u=21,v=22,w=23,x=24,y=25,z=26;return console.log("test")}'.repeat(10);
135
+ const searchTerm = 'test';
136
+ const matchStart = minifiedLine.indexOf(searchTerm);
137
+ const matchEnd = matchStart + searchTerm.length;
138
+ const maxLength = 38;
139
+ const result = trimLineToMatch(minifiedLine, matchStart, matchEnd, maxLength);
140
+ expect(result.line.length).toBeLessThanOrEqual(maxLength);
141
+ expect(result.line).toContain('test');
142
+ expect(result.line.length).toBeGreaterThan(0);
143
+ // Verify the match is correctly positioned
144
+ const relativeStart = matchStart - result.offset;
145
+ const relativeEnd = matchEnd - result.offset;
146
+ expect(result.line.substring(relativeStart, relativeEnd)).toBe('test');
147
+ });
148
+ it('should handle match at exact maxLength boundary', () => {
149
+ const lineText = 'x'.repeat(100);
150
+ const modifiedLine = lineText.substring(0, 50) + 'test' + lineText.substring(54);
151
+ const matchStart = 50;
152
+ const matchEnd = 54;
153
+ const maxLength = 54; // Exactly match + all before
154
+ const result = trimLineToMatch(modifiedLine, matchStart, matchEnd, maxLength);
155
+ expect(result.line).toContain('test');
156
+ expect(result.line.length).toBeLessThanOrEqual(maxLength);
157
+ });
158
+ });
159
+ });
160
+ describe('SearchService - Search Functionality', () => {
161
+ let service;
162
+ let testRoot;
163
+ let mockSocket;
164
+ beforeEach(async () => {
165
+ const fs = require('fs/promises');
166
+ const path = require('path');
167
+ const os = require('os');
168
+ // Create temporary test directory
169
+ testRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'search-test-'));
170
+ service = new SearchService(testRoot, 10 * 1024 * 1024, 64 * 1024);
171
+ // Create mock socket for RPC notifications
172
+ mockSocket = {
173
+ id: 'test-socket',
174
+ data: { uid: 'test-user', deviceId: 'test-device' },
175
+ emit: vi.fn(),
176
+ on: vi.fn(),
177
+ off: vi.fn(),
178
+ };
179
+ // Create test files
180
+ await fs.writeFile(path.join(testRoot, 'file1.txt'), 'Hello world\nThis is a test\nAnother line');
181
+ await fs.writeFile(path.join(testRoot, 'file2.txt'), 'Testing search\nNo matches here');
182
+ await fs.writeFile(path.join(testRoot, 'file3.js'), 'const test = 42;\nconsole.log(test);');
183
+ // Create subdirectory with files
184
+ await fs.mkdir(path.join(testRoot, 'subdir'));
185
+ await fs.writeFile(path.join(testRoot, 'subdir', 'nested.txt'), 'Nested test file\nWith multiple lines');
186
+ });
187
+ afterEach(async () => {
188
+ const fs = require('fs/promises');
189
+ // Clean up test directory
190
+ try {
191
+ await fs.rm(testRoot, { recursive: true, force: true });
192
+ }
193
+ catch { }
194
+ });
195
+ describe('handle method', () => {
196
+ it('should handle findWithStream method', async () => {
197
+ await expect(service.handle('findWithStream', {
198
+ glob: '**/*.txt',
199
+ maxResults: 10,
200
+ maxLength: 100,
201
+ searchTerm: 'test',
202
+ matchCase: false,
203
+ useRegEx: false,
204
+ onlyWholeWords: false,
205
+ }, mockSocket)).resolves.not.toThrow();
206
+ // Should have sent results to socket
207
+ expect(mockSocket.emit).toHaveBeenCalled();
208
+ });
209
+ it('should throw error for unknown method', async () => {
210
+ await expect(service.handle('unknownMethod', {}, mockSocket)).rejects.toMatchObject({
211
+ code: -32601, // METHOD_NOT_FOUND
212
+ message: expect.stringContaining('Method not found'),
213
+ });
214
+ });
215
+ });
216
+ describe('findWithStream (Node.js implementation)', () => {
217
+ it('should find matches in text files', async () => {
218
+ const results = [];
219
+ mockSocket.emit.mockImplementation((event, data) => {
220
+ if (event === 'rpc' && data.method === 'search.results') {
221
+ results.push(...data.params.results);
222
+ }
223
+ });
224
+ await service.handle('findWithStream', {
225
+ glob: '*.txt', // Match .txt files in root
226
+ maxResults: 100,
227
+ maxLength: 100,
228
+ searchTerm: 'test',
229
+ matchCase: false,
230
+ useRegEx: false,
231
+ onlyWholeWords: false,
232
+ }, mockSocket);
233
+ // Should find matches in file1.txt
234
+ expect(results.length).toBeGreaterThan(0);
235
+ expect(results.some((r) => r.path.includes('file1.txt'))).toBe(true);
236
+ });
237
+ it('should respect case sensitivity', async () => {
238
+ const results = [];
239
+ mockSocket.emit.mockImplementation((event, data) => {
240
+ if (event === 'rpc' && data.method === 'search.results') {
241
+ results.push(...data.params.results);
242
+ }
243
+ });
244
+ await service.handle('findWithStream', {
245
+ glob: '**/*.txt',
246
+ maxResults: 100,
247
+ maxLength: 100,
248
+ searchTerm: 'TEST',
249
+ matchCase: true,
250
+ useRegEx: false,
251
+ onlyWholeWords: false,
252
+ }, mockSocket);
253
+ // Should not find matches (all lowercase in files)
254
+ expect(results.length).toBe(0);
255
+ });
256
+ it('should support regex patterns', async () => {
257
+ const results = [];
258
+ mockSocket.emit.mockImplementation((event, data) => {
259
+ if (event === 'rpc' && data.method === 'search.results') {
260
+ results.push(...data.params.results);
261
+ }
262
+ });
263
+ await service.handle('findWithStream', {
264
+ glob: '**/*.txt',
265
+ maxResults: 100,
266
+ maxLength: 100,
267
+ searchTerm: 't[eE]st',
268
+ matchCase: false,
269
+ useRegEx: true,
270
+ onlyWholeWords: false,
271
+ }, mockSocket);
272
+ expect(results.length).toBeGreaterThan(0);
273
+ });
274
+ it('should respect whole word matching', async () => {
275
+ const results = [];
276
+ mockSocket.emit.mockImplementation((event, data) => {
277
+ if (event === 'rpc' && data.method === 'search.results') {
278
+ results.push(...data.params.results);
279
+ }
280
+ });
281
+ await service.handle('findWithStream', {
282
+ glob: '**/*.txt',
283
+ maxResults: 100,
284
+ maxLength: 100,
285
+ searchTerm: 'test',
286
+ matchCase: false,
287
+ useRegEx: false,
288
+ onlyWholeWords: true,
289
+ }, mockSocket);
290
+ // Should find whole word "test" but not "Testing"
291
+ const matchValues = results.map((r) => r.value);
292
+ expect(matchValues).toContain('test');
293
+ expect(matchValues.some((v) => v.toLowerCase() === 'testing')).toBe(false);
294
+ });
295
+ it('should respect maxResults limit', async () => {
296
+ const results = [];
297
+ mockSocket.emit.mockImplementation((event, data) => {
298
+ if (event === 'rpc' && data.method === 'search.results') {
299
+ results.push(...data.params.results);
300
+ }
301
+ });
302
+ await service.handle('findWithStream', {
303
+ glob: '**/*',
304
+ maxResults: 2,
305
+ maxLength: 100,
306
+ searchTerm: 'test',
307
+ matchCase: false,
308
+ useRegEx: false,
309
+ onlyWholeWords: false,
310
+ }, mockSocket);
311
+ expect(results.length).toBeLessThanOrEqual(2);
312
+ });
313
+ it('should filter by glob pattern', async () => {
314
+ const results = [];
315
+ mockSocket.emit.mockImplementation((event, data) => {
316
+ if (event === 'rpc' && data.method === 'search.results') {
317
+ results.push(...data.params.results);
318
+ }
319
+ });
320
+ await service.handle('findWithStream', {
321
+ glob: '**/*.js',
322
+ maxResults: 100,
323
+ maxLength: 100,
324
+ searchTerm: 'test',
325
+ matchCase: false,
326
+ useRegEx: false,
327
+ onlyWholeWords: false,
328
+ }, mockSocket);
329
+ // Should only find matches in .js files
330
+ expect(results.every((r) => r.path.endsWith('.js'))).toBe(true);
331
+ });
332
+ it('should send completion notification', async () => {
333
+ await service.handle('findWithStream', {
334
+ glob: '**/*.txt',
335
+ maxResults: 100,
336
+ maxLength: 100,
337
+ searchTerm: 'test',
338
+ matchCase: false,
339
+ useRegEx: false,
340
+ onlyWholeWords: false,
341
+ }, mockSocket);
342
+ // Find the done notification
343
+ const doneCall = mockSocket.emit.mock.calls.find((call) => call[0] === 'rpc' &&
344
+ call[1].method === 'search.results' &&
345
+ call[1].params.done === true);
346
+ expect(doneCall).toBeDefined();
347
+ expect(doneCall[1].params.total).toBeGreaterThanOrEqual(0);
348
+ });
349
+ it('should include correct match positions', async () => {
350
+ const results = [];
351
+ mockSocket.emit.mockImplementation((event, data) => {
352
+ if (event === 'rpc' && data.method === 'search.results') {
353
+ results.push(...data.params.results);
354
+ }
355
+ });
356
+ await service.handle('findWithStream', {
357
+ glob: 'file1.txt', // Match specific file
358
+ maxResults: 100,
359
+ maxLength: 100,
360
+ searchTerm: 'test',
361
+ matchCase: false,
362
+ useRegEx: false,
363
+ onlyWholeWords: false,
364
+ }, mockSocket);
365
+ expect(results.length).toBeGreaterThan(0);
366
+ const match = results[0];
367
+ // Verify match structure
368
+ expect(match).toHaveProperty('start');
369
+ expect(match).toHaveProperty('end');
370
+ expect(match).toHaveProperty('line');
371
+ expect(match).toHaveProperty('value');
372
+ expect(match).toHaveProperty('match');
373
+ expect(match).toHaveProperty('path');
374
+ // Verify value is extracted correctly from line
375
+ expect(match.line.substring(match.match.start, match.match.end)).toBe(match.value);
376
+ });
377
+ });
378
+ describe('cleanup', () => {
379
+ it('should cleanup without errors', () => {
380
+ expect(() => service.cleanup()).not.toThrow();
381
+ });
382
+ });
383
+ });
384
+ //# sourceMappingURL=SearchService.test.js.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=TerminalService.test.d.ts.map