@enokdev/springdocs-mcp 1.2.4 → 1.2.5

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.
@@ -3,7 +3,8 @@ import * as cheerio from 'cheerio';
3
3
  import TurndownService from 'turndown';
4
4
  import { CacheService } from './cache.js';
5
5
  /**
6
- * Optimized Spring Boot Documentation Service with caching and enhanced features
6
+ * Clean Spring Boot Documentation Service - uses ONLY real Spring documentation APIs
7
+ * No mock data whatsoever - everything is fetched from actual Spring sources
7
8
  */
8
9
  export class SpringBootDocsServiceOptimized {
9
10
  baseUrl = 'https://docs.spring.io';
@@ -24,7 +25,7 @@ export class SpringBootDocsServiceOptimized {
24
25
  setInterval(() => this.cache.cleanup(), 60 * 60 * 1000);
25
26
  }
26
27
  /**
27
- * Search Spring projects with caching and retry logic
28
+ * Search Spring projects with caching and retry logic - REAL API ONLY
28
29
  */
29
30
  async searchSpringProjects(query, limit = 10) {
30
31
  const cacheKey = `projects:${query}:${limit}`;
@@ -42,7 +43,8 @@ export class SpringBootDocsServiceOptimized {
42
43
  const html = await response.text();
43
44
  const $ = cheerio.load(html);
44
45
  const projects = [];
45
- $('.project-list .project, .project-item, .card').each((index, element) => {
46
+ // Parse actual Spring projects page
47
+ $('.project-list .project, .project-item, .card, .project-card, .project').each((_, element) => {
46
48
  const $project = $(element);
47
49
  const title = $project.find('h2, h3, .title, .project-title').first().text().trim();
48
50
  const description = $project.find('p, .description, .summary').first().text().trim();
@@ -57,29 +59,17 @@ export class SpringBootDocsServiceOptimized {
57
59
  });
58
60
  }
59
61
  });
60
- // If few results, add popular projects
61
- if (projects.length < 3) {
62
- const popularProjects = this.getPopularSpringProjects()
63
- .filter(project => project.title.toLowerCase().includes(query.toLowerCase()) ||
64
- project.description.toLowerCase().includes(query.toLowerCase()) ||
65
- project.keywords.some((k) => k.toLowerCase().includes(query.toLowerCase())));
66
- projects.push(...popularProjects);
67
- }
68
62
  const results = projects.slice(0, limit);
69
63
  this.cache.set(cacheKey, results);
70
64
  return results;
71
65
  }
72
66
  catch (error) {
73
67
  console.error('Error searching Spring projects:', error);
74
- // Fallback to popular projects
75
- return this.getPopularSpringProjects()
76
- .filter(project => project.title.toLowerCase().includes(query.toLowerCase()) ||
77
- project.description.toLowerCase().includes(query.toLowerCase()))
78
- .slice(0, limit);
68
+ return [];
79
69
  }
80
70
  }
81
71
  /**
82
- * Get Spring project details with caching
72
+ * Get Spring project details - REAL API ONLY
83
73
  */
84
74
  async getSpringProject(projectName) {
85
75
  const cacheKey = `project:${projectName}`;
@@ -90,7 +80,7 @@ export class SpringBootDocsServiceOptimized {
90
80
  }
91
81
  console.error(`🔍 Fetching project: ${projectName}`);
92
82
  try {
93
- const url = `${this.springProjectsUrl}/${projectName}`;
83
+ const url = `${this.springProjectsUrl}/${projectName.toLowerCase().replace(/\s+/g, '-')}`;
94
84
  const response = await this.fetchWithRetry(url);
95
85
  if (!response.ok) {
96
86
  throw new Error(`Project not found: ${projectName}`);
@@ -99,28 +89,20 @@ export class SpringBootDocsServiceOptimized {
99
89
  const $ = cheerio.load(html);
100
90
  const content = $('.project-overview, .content, main, .project-details').first();
101
91
  if (content.length === 0) {
102
- throw new Error('Project content not found');
92
+ throw new Error('No content found for project');
103
93
  }
104
- this.cleanHtml($, content);
105
94
  const markdown = this.turndownService.turndown(content.html() || '');
106
- const version = $('.version, .current-version').first().text().trim();
107
- const status = $('.status, .project-status').first().text().trim();
108
- let result = `# Spring Project: ${projectName}\n\n`;
109
- if (version)
110
- result += `**Current Version:** ${version}\n\n`;
111
- if (status)
112
- result += `**Status:** ${status}\n\n`;
113
- result += markdown;
95
+ const result = `# ${projectName}\n\n${markdown}`;
114
96
  this.cache.setLongTerm(cacheKey, result);
115
97
  return result;
116
98
  }
117
99
  catch (error) {
118
- console.error(`Error retrieving project ${projectName}:`, error);
119
- throw new Error(`Unable to retrieve project: ${projectName}`);
100
+ console.error(`Error fetching project ${projectName}:`, error);
101
+ throw error;
120
102
  }
121
103
  }
122
104
  /**
123
- * Get all Spring guides with caching
105
+ * Get all Spring guides - REAL API ONLY
124
106
  */
125
107
  async getAllSpringGuides(category, limit = 20) {
126
108
  const cacheKey = `guides:${category || 'all'}:${limit}`;
@@ -138,39 +120,36 @@ export class SpringBootDocsServiceOptimized {
138
120
  const html = await response.text();
139
121
  const $ = cheerio.load(html);
140
122
  const guides = [];
141
- $('.guide-list .guide, .guide-item, .card').each((index, element) => {
123
+ // Parse actual Spring guides page
124
+ $('.guide-item, .card, .guide-card, .list-item, .guide').each((_, element) => {
142
125
  const $guide = $(element);
143
- const title = $guide.find('h2, h3, .title, .guide-title').first().text().trim();
126
+ const title = $guide.find('h2, h3, .title, .guide-title, a').first().text().trim();
144
127
  const description = $guide.find('p, .description, .summary').first().text().trim();
128
+ const guideCategory = $guide.find('.category, .badge, .label').first().text().trim();
145
129
  const link = $guide.find('a').first().attr('href');
146
- const guideCategory = $guide.find('.category, .tag').first().text().trim();
147
- if (title && (!category || guideCategory.toLowerCase().includes(category.toLowerCase()))) {
148
- guides.push({
149
- type: 'spring-guide',
150
- title: title,
151
- description: description,
152
- category: guideCategory,
153
- url: link?.startsWith('http') ? link : `https://spring.io${link}`,
154
- id: link?.split('/').pop()?.replace('.html', '') || title.toLowerCase().replace(/\s+/g, '-'),
155
- });
130
+ if (title) {
131
+ if (!category || guideCategory.toLowerCase().includes(category.toLowerCase())) {
132
+ guides.push({
133
+ type: 'spring-guide',
134
+ title: title,
135
+ description: description || 'Spring guide',
136
+ category: guideCategory || 'General',
137
+ url: link?.startsWith('http') ? link : `https://spring.io${link}`,
138
+ });
139
+ }
156
140
  }
157
141
  });
158
- // If few results, add popular guides
159
- if (guides.length < 5) {
160
- const popularGuides = this.getPopularSpringGuides();
161
- guides.push(...popularGuides);
162
- }
163
142
  const results = guides.slice(0, limit);
164
143
  this.cache.set(cacheKey, results);
165
144
  return results;
166
145
  }
167
146
  catch (error) {
168
147
  console.error('Error retrieving Spring guides:', error);
169
- return this.getPopularSpringGuides().slice(0, limit);
148
+ return [];
170
149
  }
171
150
  }
172
151
  /**
173
- * Get specific guide with enhanced error handling and caching
152
+ * Get specific guide content - REAL API ONLY
174
153
  */
175
154
  async getGuide(guideId) {
176
155
  const cacheKey = `guide:${guideId}`;
@@ -180,62 +159,55 @@ export class SpringBootDocsServiceOptimized {
180
159
  return cached;
181
160
  }
182
161
  console.error(`🔍 Fetching guide: ${guideId}`);
183
- try {
184
- let content = '';
185
- let sourceUrl = '';
186
- // Multiple sources to try
187
- const sources = [
188
- { url: `https://spring.io/guides/${guideId}/`, name: 'spring.io' },
189
- { url: `https://raw.githubusercontent.com/spring-guides/${guideId}/main/README.adoc`, name: 'GitHub raw' },
190
- { url: `https://github.com/spring-guides/${guideId}/blob/main/README.adoc`, name: 'GitHub blob' }
191
- ];
192
- for (const source of sources) {
193
- try {
194
- console.error(`Trying ${source.name}: ${source.url}`);
195
- const response = await this.fetchWithRetry(source.url);
196
- if (response.ok) {
197
- content = await response.text();
198
- sourceUrl = source.url;
199
- console.error(`✅ Success with ${source.name} (${content.length} chars)`);
200
- break;
201
- }
202
- else {
203
- console.error(`❌ ${source.name} failed: ${response.status}`);
204
- }
162
+ // Try multiple sources for guides
163
+ const sources = [
164
+ { name: 'Spring.io', url: `${this.springGuideUrl}/${guideId}/` },
165
+ { name: 'GitHub', url: `https://github.com/spring-guides/${guideId}` },
166
+ { name: 'Spring.io alt', url: `${this.springGuideUrl}/gs/${guideId}/` }
167
+ ];
168
+ let content = '';
169
+ let sourceUrl = '';
170
+ for (const source of sources) {
171
+ try {
172
+ console.error(`Trying ${source.name}: ${source.url}`);
173
+ const response = await this.fetchWithRetry(source.url);
174
+ if (response.ok) {
175
+ content = await response.text();
176
+ sourceUrl = source.url;
177
+ console.error(`✅ Success with ${source.name} (${content.length} chars)`);
178
+ break;
205
179
  }
206
- catch (error) {
207
- console.error(`❌ ${source.name} error:`, error instanceof Error ? error.message : 'Unknown error');
180
+ else {
181
+ console.error(`❌ ${source.name} failed: ${response.status}`);
208
182
  }
209
183
  }
210
- if (!content) {
211
- return this.generateGuideNotFoundMessage(guideId);
212
- }
213
- let result = '';
214
- // Process different content types
215
- if (content.includes('<html') || content.includes('<!DOCTYPE')) {
216
- // HTML content
217
- result = this.processHtmlGuide(content, guideId, sourceUrl);
218
- }
219
- else if (content.includes('=') || content.includes(':toc:') || content.includes('== ')) {
220
- // AsciiDoc content
221
- result = this.processAsciiDocGuide(content, guideId, sourceUrl);
222
- }
223
- else {
224
- // Plain text
225
- result = `# Spring Guide: ${guideId}\n\n*Source: ${sourceUrl}*\n\n\`\`\`\n${content}\n\`\`\``;
184
+ catch (error) {
185
+ console.error(`❌ ${source.name} error:`, error instanceof Error ? error.message : 'Unknown');
226
186
  }
187
+ }
188
+ if (!content) {
189
+ throw new Error(`Guide not found: ${guideId}`);
190
+ }
191
+ try {
192
+ const result = this.processHtmlGuide(content, guideId, sourceUrl);
227
193
  this.cache.setLongTerm(cacheKey, result);
228
194
  return result;
229
195
  }
230
196
  catch (error) {
231
- console.error(`Error retrieving guide ${guideId}:`, error);
232
- return this.generateGuideErrorMessage(guideId, error);
197
+ console.error('Error processing guide content:', error);
198
+ throw error;
233
199
  }
234
200
  }
235
201
  /**
236
- * Get reference documentation with caching
202
+ * Get Spring reference documentation - alias for backward compatibility
237
203
  */
238
204
  async getReference(section, subsection) {
205
+ return this.getSpringReference(section, subsection);
206
+ }
207
+ /**
208
+ * Get Spring reference documentation - REAL API ONLY
209
+ */
210
+ async getSpringReference(section, subsection) {
239
211
  const cacheKey = `reference:${section}:${subsection || 'main'}`;
240
212
  const cached = this.cache.get(cacheKey);
241
213
  if (cached) {
@@ -244,13 +216,7 @@ export class SpringBootDocsServiceOptimized {
244
216
  }
245
217
  console.error(`🔍 Fetching reference: ${section}`);
246
218
  try {
247
- let url = `${this.baseUrl}/spring-boot/docs/${this.springBootVersion}/reference/html/`;
248
- if (subsection) {
249
- url += `${section}.html#${subsection}`;
250
- }
251
- else {
252
- url += `${section}.html`;
253
- }
219
+ const url = `${this.baseUrl}/spring-boot/docs/${this.springBootVersion}/reference/html/${section}.html`;
254
220
  const response = await this.fetchWithRetry(url);
255
221
  if (!response.ok) {
256
222
  throw new Error(`Reference section not found: ${section}`);
@@ -259,84 +225,148 @@ export class SpringBootDocsServiceOptimized {
259
225
  const $ = cheerio.load(html);
260
226
  const content = $('.content, .section, main').first();
261
227
  if (content.length === 0) {
262
- throw new Error('Section content not found');
228
+ throw new Error('No content found in reference documentation');
263
229
  }
264
- this.cleanHtml($, content);
265
230
  const markdown = this.turndownService.turndown(content.html() || '');
266
231
  const result = `# Spring Boot Reference: ${section}\n\n${markdown}`;
267
232
  this.cache.setLongTerm(cacheKey, result);
268
233
  return result;
269
234
  }
270
235
  catch (error) {
271
- console.error(`Error retrieving reference ${section}:`, error);
272
- throw new Error(`Unable to retrieve reference section: ${section}`);
236
+ console.error(`Error fetching reference ${section}:`, error);
237
+ throw error;
273
238
  }
274
239
  }
275
240
  /**
276
- * Search concepts with caching
241
+ * Search Spring concepts - alias for backward compatibility
277
242
  */
278
243
  async searchConcepts(concept, category) {
244
+ return this.searchSpringConcepts(concept, category);
245
+ }
246
+ /**
247
+ * Search Spring concepts - using real documentation
248
+ */
249
+ async searchSpringConcepts(concept, category) {
279
250
  const cacheKey = `concepts:${concept}:${category || 'all'}`;
280
251
  const cached = this.cache.get(cacheKey);
281
- if (cached) {
282
- console.error(`✅ Cache hit for concepts: ${concept}`);
252
+ if (cached)
283
253
  return cached;
284
- }
285
- console.error(`🔍 Searching concepts: ${concept}`);
286
254
  try {
287
- const concepts = this.getSpringBootConcepts();
288
- const results = {};
289
- for (const [cat, conceptList] of Object.entries(concepts)) {
290
- if (category && cat !== category)
291
- continue;
292
- const matches = conceptList.filter((c) => c.name.toLowerCase().includes(concept.toLowerCase()) ||
293
- c.description.toLowerCase().includes(concept.toLowerCase()) ||
294
- c.keywords.some((k) => k.toLowerCase().includes(concept.toLowerCase())));
295
- if (matches.length > 0) {
296
- results[cat] = matches;
255
+ // Search in Spring Boot reference documentation
256
+ const searchUrl = `${this.baseUrl}/spring-boot/docs/current/reference/html/`;
257
+ const response = await this.fetchWithRetry(searchUrl);
258
+ if (!response.ok) {
259
+ throw new Error('Unable to access Spring Boot documentation');
260
+ }
261
+ const html = await response.text();
262
+ const $ = cheerio.load(html);
263
+ // Look for concept in documentation
264
+ let conceptContent = '';
265
+ $('h1, h2, h3, h4').each((_, element) => {
266
+ const heading = $(element);
267
+ const headingText = heading.text().toLowerCase();
268
+ if (headingText.includes(concept.toLowerCase())) {
269
+ const section = heading.parent();
270
+ conceptContent += this.turndownService.turndown(section.html() || '');
297
271
  }
272
+ });
273
+ if (!conceptContent) {
274
+ conceptContent = `# Spring Concept: ${concept}\n\nConcept not found in documentation. Try searching for more specific terms.`;
298
275
  }
299
- this.cache.setLongTerm(cacheKey, results);
300
- return results;
276
+ else {
277
+ conceptContent = `# Spring Concept: ${concept}\n\n${conceptContent}`;
278
+ }
279
+ this.cache.set(cacheKey, conceptContent);
280
+ return conceptContent;
301
281
  }
302
282
  catch (error) {
303
283
  console.error('Error searching concepts:', error);
304
- throw new Error('Unable to search Spring Boot concepts');
284
+ return `# Spring Concept Search Error\n\nUnable to search for concept "${concept}": ${error instanceof Error ? error.message : 'Unknown error'}`;
305
285
  }
306
286
  }
307
287
  /**
308
- * Enhanced documentation search with parallel processing
288
+ * Search documentation with real API - alias for backward compatibility
309
289
  */
310
290
  async searchDocumentation(query, docType = 'all', limit = 10) {
291
+ return this.searchSpringDocs(query, docType, limit);
292
+ }
293
+ /**
294
+ * Search documentation with real API
295
+ */
296
+ async searchSpringDocs(query, docType = 'all', limit = 10) {
311
297
  const cacheKey = `docs:${query}:${docType}:${limit}`;
312
298
  const cached = this.cache.get(cacheKey);
313
- if (cached) {
314
- console.error(`✅ Cache hit for documentation search: ${query}`);
299
+ if (cached)
315
300
  return cached;
316
- }
317
- console.error(`🔍 Searching documentation: ${query}`);
301
+ const results = [];
318
302
  try {
319
- const searchPromises = [];
320
303
  if (docType === 'all' || docType === 'guides') {
321
- searchPromises.push(this.searchGuides(query, Math.ceil(limit / 3)));
304
+ const guides = await this.getAllSpringGuides(undefined, limit);
305
+ const filteredGuides = guides.filter(guide => guide.title.toLowerCase().includes(query.toLowerCase()) ||
306
+ guide.description.toLowerCase().includes(query.toLowerCase()));
307
+ results.push(...filteredGuides);
322
308
  }
323
- if (docType === 'all' || docType === 'reference') {
324
- searchPromises.push(this.searchReference(query, Math.ceil(limit / 3)));
309
+ if (docType === 'all' || docType === 'projects') {
310
+ const projects = await this.searchSpringProjects(query, limit);
311
+ results.push(...projects);
325
312
  }
326
- if (docType === 'all' || docType === 'api') {
327
- searchPromises.push(this.searchAPI(query, Math.ceil(limit / 3)));
313
+ if (docType === 'all' || docType === 'reference') {
314
+ // Search in reference documentation
315
+ const refResults = await this.searchInReference(query, limit);
316
+ results.push(...refResults);
328
317
  }
329
- const results = await Promise.all(searchPromises);
330
- const flatResults = results.flat().slice(0, limit);
331
- this.cache.set(cacheKey, flatResults);
332
- return flatResults;
318
+ this.cache.set(cacheKey, results);
319
+ return results.slice(0, limit);
333
320
  }
334
321
  catch (error) {
335
322
  console.error('Error searching documentation:', error);
336
- throw new Error('Failed to search Spring Boot documentation');
323
+ return [];
324
+ }
325
+ }
326
+ async searchInReference(query, limit) {
327
+ try {
328
+ const response = await this.fetchWithRetry(`${this.baseUrl}/spring-boot/docs/current/reference/html/`);
329
+ if (!response.ok)
330
+ return [];
331
+ const html = await response.text();
332
+ const $ = cheerio.load(html);
333
+ const results = [];
334
+ $('nav a, .toc a, .nav-link').each((_, element) => {
335
+ const link = $(element);
336
+ const text = link.text().trim();
337
+ const href = link.attr('href');
338
+ if (text && href && text.toLowerCase().includes(query.toLowerCase())) {
339
+ results.push({
340
+ type: 'reference',
341
+ title: `Spring Boot: ${text}`,
342
+ url: href.startsWith('http') ? href : `${this.baseUrl}/spring-boot/docs/current/reference/html/${href}`,
343
+ description: `Reference documentation section`
344
+ });
345
+ }
346
+ });
347
+ return results.slice(0, limit);
348
+ }
349
+ catch (error) {
350
+ console.error('Error searching reference:', error);
351
+ return [];
337
352
  }
338
353
  }
339
- // Private helper methods
354
+ processHtmlGuide(content, guideId, sourceUrl) {
355
+ console.log('Processing HTML content...');
356
+ const $ = cheerio.load(content);
357
+ // Remove navigation and footer elements
358
+ $('nav, footer, .navbar, .sidebar, #js-sidebar').remove();
359
+ // Get main content
360
+ const mainContent = $('.content, .guide-content, main, .markdown-body, .guide-body, article').first();
361
+ if (mainContent.length === 0) {
362
+ console.log('No main content found, using body');
363
+ $('script, style').remove();
364
+ const bodyMarkdown = this.turndownService.turndown($('body').html() || '');
365
+ return `# Spring Guide: ${guideId}\n\n**Source:** ${sourceUrl}\n\n${bodyMarkdown}`;
366
+ }
367
+ const markdown = this.turndownService.turndown(mainContent.html() || '');
368
+ return `# Spring Guide: ${guideId}\n\n**Source:** ${sourceUrl}\n\n${markdown}`;
369
+ }
340
370
  async fetchWithRetry(url, timeout = this.REQUEST_TIMEOUT, retries = this.MAX_RETRIES) {
341
371
  for (let attempt = 1; attempt <= retries; attempt++) {
342
372
  const controller = new AbortController();
@@ -345,7 +375,7 @@ export class SpringBootDocsServiceOptimized {
345
375
  const response = await fetch(url, {
346
376
  signal: controller.signal,
347
377
  headers: {
348
- 'User-Agent': 'Spring-Docs-MCP/1.2.3',
378
+ 'User-Agent': 'Spring-Docs-MCP/1.2.4',
349
379
  'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
350
380
  'Accept-Language': 'en-US,en;q=0.9'
351
381
  }
@@ -363,231 +393,5 @@ export class SpringBootDocsServiceOptimized {
363
393
  }
364
394
  throw new Error('All retry attempts failed');
365
395
  }
366
- processHtmlGuide(content, guideId, sourceUrl) {
367
- console.log('Processing HTML content...');
368
- const $ = cheerio.load(content);
369
- let mainContent = $('.guide-content, .content, main, .container .row .col-md-8, .markdown-body, article').first();
370
- if (mainContent.length === 0) {
371
- const bodyContent = $('.guide-body, .guide-content, .content').first();
372
- if (bodyContent.length > 0) {
373
- mainContent = bodyContent;
374
- }
375
- else {
376
- mainContent = $('body');
377
- }
378
- }
379
- if (mainContent.length > 0) {
380
- this.cleanHtml($, mainContent);
381
- const markdown = this.turndownService.turndown(mainContent.html() || '');
382
- return `# Spring Guide: ${guideId}\n\n*Source: ${sourceUrl}*\n\n${markdown}`;
383
- }
384
- return this.generateGuideNotFoundMessage(guideId);
385
- }
386
- processAsciiDocGuide(content, guideId, sourceUrl) {
387
- console.log('Processing AsciiDoc content...');
388
- let processedContent = content
389
- .replace(/^= (.+)$/gm, '# $1')
390
- .replace(/^== (.+)$/gm, '## $1')
391
- .replace(/^=== (.+)$/gm, '### $1')
392
- .replace(/^==== (.+)$/gm, '#### $1')
393
- .replace(/^\[source,(\w+)\]$/gm, '')
394
- .replace(/^----$/gm, '```')
395
- .replace(/^\+\+\+\+$/gm, '```');
396
- return `# Spring Guide: ${guideId}\n\n*Source: ${sourceUrl}*\n\n${processedContent}`;
397
- }
398
- generateGuideNotFoundMessage(guideId) {
399
- return `# Spring Guide: ${guideId}
400
-
401
- ❌ **Guide not found**
402
-
403
- The guide "${guideId}" could not be retrieved from any source.
404
-
405
- ## 📚 Popular guides available:
406
- - \`gs-rest-service\` - Building a RESTful Web Service
407
- - \`gs-spring-boot\` - Building an Application with Spring Boot
408
- - \`gs-accessing-data-jpa\` - Accessing Data with JPA
409
- - \`gs-securing-web\` - Securing a Web Application
410
- - \`gs-actuator-service\` - Building a RESTful Web Service with Spring Boot Actuator
411
- - \`gs-testing-web\` - Testing the Web Layer
412
-
413
- ## 🔧 Alternative tools:
414
- 1. \`get_all_spring_guides\` - See all available guides
415
- 2. \`search_spring_docs\` - Search documentation
416
-
417
- ## 💡 Identifier format:
418
- The identifier should be in format: \`gs-guide-name\``;
419
- }
420
- generateGuideErrorMessage(guideId, error) {
421
- return `# Spring Guide: ${guideId}
422
-
423
- ❌ **Error retrieving guide**
424
-
425
- The guide "${guideId}" could not be retrieved.
426
-
427
- ## 🔧 Possible solutions:
428
- 1. Check the guide identifier (format: \`gs-guide-name\`)
429
- 2. Use \`get_all_spring_guides\` to see all available guides
430
- 3. Use \`search_spring_docs\` to search documentation
431
-
432
- ## 📚 Popular guides:
433
- - \`gs-rest-service\` - Creating a RESTful Web Service
434
- - \`gs-spring-boot\` - Building an Application with Spring Boot
435
- - \`gs-accessing-data-jpa\` - Accessing Data with JPA
436
- - \`gs-securing-web\` - Securing a Web Application
437
-
438
- **Technical error:** ${error instanceof Error ? error.message : 'Unknown error'}`;
439
- }
440
- cleanHtml($, content) {
441
- content.find('script, style, nav, .sidebar, .toc, .feedback').remove();
442
- content.find('*').each((index, element) => {
443
- $(element).removeAttr('class').removeAttr('style').removeAttr('id');
444
- });
445
- }
446
- // Static data methods (unchanged from original)
447
- getPopularSpringProjects() {
448
- return [
449
- {
450
- type: 'spring-project',
451
- title: 'Spring Boot',
452
- description: 'Create stand-alone, production-grade Spring applications easily',
453
- url: 'https://spring.io/projects/spring-boot',
454
- keywords: ['boot', 'microservices', 'web', 'standalone'],
455
- },
456
- {
457
- type: 'spring-project',
458
- title: 'Spring Framework',
459
- description: 'The most popular Java framework for building enterprise applications',
460
- url: 'https://spring.io/projects/spring-framework',
461
- keywords: ['framework', 'core', 'mvc', 'ioc', 'dependency injection'],
462
- },
463
- {
464
- type: 'spring-project',
465
- title: 'Spring Data',
466
- description: 'Provides a familiar programming model for data access',
467
- url: 'https://spring.io/projects/spring-data',
468
- keywords: ['data', 'jpa', 'mongodb', 'redis', 'repository'],
469
- },
470
- {
471
- type: 'spring-project',
472
- title: 'Spring Security',
473
- description: 'Powerful and highly customizable security framework',
474
- url: 'https://spring.io/projects/spring-security',
475
- keywords: ['security', 'authentication', 'authorization', 'oauth'],
476
- },
477
- {
478
- type: 'spring-project',
479
- title: 'Spring Cloud',
480
- description: 'Tools for rapidly building distributed system patterns',
481
- url: 'https://spring.io/projects/spring-cloud',
482
- keywords: ['cloud', 'microservices', 'distributed', 'netflix'],
483
- },
484
- ];
485
- }
486
- getPopularSpringGuides() {
487
- return [
488
- {
489
- type: 'spring-guide',
490
- title: 'Building a RESTful Web Service',
491
- description: 'Learn how to create a RESTful web service with Spring Boot',
492
- category: 'Web',
493
- url: 'https://spring.io/guides/gs/rest-service/',
494
- id: 'gs-rest-service',
495
- },
496
- {
497
- type: 'spring-guide',
498
- title: 'Building an Application with Spring Boot',
499
- description: 'Learn how to build an application with minimal configuration',
500
- category: 'Getting Started',
501
- url: 'https://spring.io/guides/gs/spring-boot/',
502
- id: 'gs-spring-boot',
503
- },
504
- {
505
- type: 'spring-guide',
506
- title: 'Accessing Data with JPA',
507
- description: 'Learn how to work with JPA data persistence',
508
- category: 'Data',
509
- url: 'https://spring.io/guides/gs/accessing-data-jpa/',
510
- id: 'gs-accessing-data-jpa',
511
- },
512
- ];
513
- }
514
- async searchGuides(query, limit) {
515
- const guides = [
516
- { id: 'gs-rest-service', title: 'Building a RESTful Web Service', description: 'Learn how to create a RESTful web service with Spring Boot' },
517
- { id: 'gs-spring-boot', title: 'Building an Application with Spring Boot', description: 'Learn how to build an application with minimal configuration' },
518
- { id: 'gs-accessing-data-jpa', title: 'Accessing Data with JPA', description: 'Learn how to work with JPA data persistence' },
519
- ];
520
- return guides
521
- .filter(guide => guide.title.toLowerCase().includes(query.toLowerCase()) ||
522
- guide.description.toLowerCase().includes(query.toLowerCase()))
523
- .slice(0, limit)
524
- .map(guide => ({
525
- type: 'guide',
526
- title: guide.title,
527
- url: `${this.springGuideUrl}/${guide.id}/`,
528
- description: guide.description,
529
- id: guide.id,
530
- }));
531
- }
532
- async searchReference(query, limit) {
533
- const sections = [
534
- { name: 'web', title: 'Web Applications', description: 'Building web applications with Spring Boot' },
535
- { name: 'data', title: 'Data Access', description: 'Working with databases and data sources' },
536
- { name: 'security', title: 'Security', description: 'Securing Spring Boot applications' },
537
- ];
538
- return sections
539
- .filter(section => section.title.toLowerCase().includes(query.toLowerCase()) ||
540
- section.description.toLowerCase().includes(query.toLowerCase()))
541
- .slice(0, limit)
542
- .map(section => ({
543
- type: 'reference',
544
- title: section.title,
545
- url: `${this.baseUrl}/spring-boot/docs/${this.springBootVersion}/reference/html/${section.name}.html`,
546
- description: section.description,
547
- section: section.name,
548
- }));
549
- }
550
- async searchAPI(query, limit) {
551
- const apiSections = [
552
- { name: 'Configuration Properties', description: 'Spring Boot configuration properties reference' },
553
- { name: 'Auto-configuration Classes', description: 'List of auto-configuration classes' },
554
- ];
555
- return apiSections
556
- .filter(section => section.name.toLowerCase().includes(query.toLowerCase()) ||
557
- section.description.toLowerCase().includes(query.toLowerCase()))
558
- .slice(0, limit)
559
- .map(section => ({
560
- type: 'api',
561
- title: section.name,
562
- url: `${this.baseUrl}/spring-boot/docs/${this.springBootVersion}/api/`,
563
- description: section.description,
564
- }));
565
- }
566
- getSpringBootConcepts() {
567
- return {
568
- core: [
569
- {
570
- name: 'Auto-configuration',
571
- description: 'Mechanism that automatically configures Spring Boot based on present dependencies',
572
- keywords: ['auto-configuration', '@EnableAutoConfiguration', 'conditional'],
573
- examples: ['@SpringBootApplication', '@Conditional', 'spring.factories'],
574
- },
575
- {
576
- name: 'Profiles',
577
- description: 'Allows separating parts of application configuration for different environments',
578
- keywords: ['profiles', '@Profile', 'application.properties'],
579
- examples: ['application-dev.properties', '@Profile("prod")', 'spring.profiles.active'],
580
- },
581
- ],
582
- web: [
583
- {
584
- name: 'Spring MVC',
585
- description: 'Integrated web framework for building web applications and REST APIs',
586
- keywords: ['mvc', '@Controller', '@RestController', 'web'],
587
- examples: ['@GetMapping', '@PostMapping', '@RequestMapping'],
588
- },
589
- ],
590
- };
591
- }
592
396
  }
593
397
  //# sourceMappingURL=springboot-docs-optimized.js.map