@enokdev/springdocs-mcp 1.2.8 → 1.4.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 (112) hide show
  1. package/README.md +83 -40
  2. package/build/config.d.ts +9 -0
  3. package/build/config.d.ts.map +1 -0
  4. package/build/config.js +32 -0
  5. package/build/config.js.map +1 -0
  6. package/build/format.d.ts +6 -0
  7. package/build/format.d.ts.map +1 -0
  8. package/build/format.js +23 -0
  9. package/build/format.js.map +1 -0
  10. package/build/http-server.d.ts +15 -0
  11. package/build/http-server.d.ts.map +1 -0
  12. package/build/http-server.js +131 -0
  13. package/build/http-server.js.map +1 -0
  14. package/build/index.js +182 -68
  15. package/build/index.js.map +1 -1
  16. package/build/prompts.d.ts +23 -0
  17. package/build/prompts.d.ts.map +1 -0
  18. package/build/prompts.js +112 -0
  19. package/build/prompts.js.map +1 -0
  20. package/build/resources.d.ts +31 -0
  21. package/build/resources.d.ts.map +1 -0
  22. package/build/resources.js +66 -0
  23. package/build/resources.js.map +1 -0
  24. package/build/services/advanced-features.d.ts +14 -9
  25. package/build/services/advanced-features.d.ts.map +1 -1
  26. package/build/services/advanced-features.js +205 -195
  27. package/build/services/advanced-features.js.map +1 -1
  28. package/build/services/boot-wiki.d.ts +25 -0
  29. package/build/services/boot-wiki.d.ts.map +1 -0
  30. package/build/services/boot-wiki.js +103 -0
  31. package/build/services/boot-wiki.js.map +1 -0
  32. package/build/services/cache.d.ts +18 -3
  33. package/build/services/cache.d.ts.map +1 -1
  34. package/build/services/cache.js +84 -3
  35. package/build/services/cache.js.map +1 -1
  36. package/build/services/dependency-finder.d.ts +46 -0
  37. package/build/services/dependency-finder.d.ts.map +1 -0
  38. package/build/services/dependency-finder.js +181 -0
  39. package/build/services/dependency-finder.js.map +1 -0
  40. package/build/services/diagnosis.d.ts +46 -0
  41. package/build/services/diagnosis.d.ts.map +1 -0
  42. package/build/services/diagnosis.js +481 -0
  43. package/build/services/diagnosis.js.map +1 -0
  44. package/build/services/guides-list.d.ts +16 -0
  45. package/build/services/guides-list.d.ts.map +1 -0
  46. package/build/services/guides-list.js +45 -0
  47. package/build/services/guides-list.js.map +1 -0
  48. package/build/services/http.d.ts +21 -0
  49. package/build/services/http.d.ts.map +1 -0
  50. package/build/services/http.js +168 -0
  51. package/build/services/http.js.map +1 -0
  52. package/build/services/initializr.d.ts +41 -0
  53. package/build/services/initializr.d.ts.map +1 -0
  54. package/build/services/initializr.js +135 -0
  55. package/build/services/initializr.js.map +1 -0
  56. package/build/services/markdown.d.ts +56 -0
  57. package/build/services/markdown.d.ts.map +1 -0
  58. package/build/services/markdown.js +146 -0
  59. package/build/services/markdown.js.map +1 -0
  60. package/build/services/migration-sources.d.ts +13 -0
  61. package/build/services/migration-sources.d.ts.map +1 -0
  62. package/build/services/migration-sources.js +48 -0
  63. package/build/services/migration-sources.js.map +1 -0
  64. package/build/services/release-notes.d.ts +23 -0
  65. package/build/services/release-notes.d.ts.map +1 -0
  66. package/build/services/release-notes.js +94 -0
  67. package/build/services/release-notes.js.map +1 -0
  68. package/build/services/search-index.d.ts +33 -0
  69. package/build/services/search-index.d.ts.map +1 -0
  70. package/build/services/search-index.js +147 -0
  71. package/build/services/search-index.js.map +1 -0
  72. package/build/services/spring-projects-config.d.ts +26 -33
  73. package/build/services/spring-projects-config.d.ts.map +1 -1
  74. package/build/services/spring-projects-config.js +248 -79
  75. package/build/services/spring-projects-config.js.map +1 -1
  76. package/build/services/springboot-docs-optimized.d.ts +57 -16
  77. package/build/services/springboot-docs-optimized.d.ts.map +1 -1
  78. package/build/services/springboot-docs-optimized.js +315 -197
  79. package/build/services/springboot-docs-optimized.js.map +1 -1
  80. package/build/services/url.d.ts +15 -0
  81. package/build/services/url.d.ts.map +1 -0
  82. package/build/services/url.js +40 -0
  83. package/build/services/url.js.map +1 -0
  84. package/build/tools/index.d.ts +376 -2
  85. package/build/tools/index.d.ts.map +1 -1
  86. package/build/tools/index.js +178 -11
  87. package/build/tools/index.js.map +1 -1
  88. package/build/validation.d.ts +7 -0
  89. package/build/validation.d.ts.map +1 -0
  90. package/build/validation.js +50 -0
  91. package/build/validation.js.map +1 -0
  92. package/build/version.d.ts +3 -0
  93. package/build/version.d.ts.map +1 -0
  94. package/build/version.js +6 -0
  95. package/build/version.js.map +1 -0
  96. package/package.json +6 -6
  97. package/build/debug.d.ts +0 -3
  98. package/build/debug.d.ts.map +0 -1
  99. package/build/debug.js +0 -292
  100. package/build/debug.js.map +0 -1
  101. package/build/services/advanced-features-old.d.ts +0 -45
  102. package/build/services/advanced-features-old.d.ts.map +0 -1
  103. package/build/services/advanced-features-old.js +0 -792
  104. package/build/services/advanced-features-old.js.map +0 -1
  105. package/build/services/springboot-docs-optimized-old.d.ts +0 -55
  106. package/build/services/springboot-docs-optimized-old.d.ts.map +0 -1
  107. package/build/services/springboot-docs-optimized-old.js +0 -588
  108. package/build/services/springboot-docs-optimized-old.js.map +0 -1
  109. package/build/services/springboot-docs.d.ts +0 -65
  110. package/build/services/springboot-docs.d.ts.map +0 -1
  111. package/build/services/springboot-docs.js +0 -708
  112. package/build/services/springboot-docs.js.map +0 -1
@@ -0,0 +1,48 @@
1
+ // Migration guides of projects other than Spring Boot: raw markdown pages of their wiki
2
+ // (raw host of the code forge). Pure helpers, no network access here.
3
+ //
4
+ // Verified against the network: the raw endpoint answers a missing page with a real 404 (unlike
5
+ // the HTML wiki, which redirects to the home page with a 200).
6
+ // Framework: Spring-Framework-<major.minor>-Release-Notes.md (5.3, 6.0, 6.1, 6.2, 7.0 exist)
7
+ // Batch: Spring-Batch-<major.minor>-Migration-Guide.md (5.0, 6.0 exist)
8
+ // Candidates not implemented: Spring Security (docs.spring.io/spring-security/reference/<v>/migration/
9
+ // shows the guide of the version's major, not of a target version) and Spring AI (a single
10
+ // upgrade-notes.html page for all versions).
11
+ export const MIGRATION_PROJECTS = ['spring-boot', 'spring-framework', 'spring-batch'];
12
+ const RAW_SOURCES = {
13
+ 'spring-framework': { repo: 'spring-framework', displayName: 'Spring Framework', document: 'release-notes' },
14
+ 'spring-batch': { repo: 'spring-batch', displayName: 'Spring Batch', document: 'migration-guide' },
15
+ };
16
+ export function isMigrationProject(value) {
17
+ return MIGRATION_PROJECTS.includes(value);
18
+ }
19
+ export function projectDisplayName(project) {
20
+ return project === 'spring-boot' ? 'Spring Boot' : RAW_SOURCES[project].displayName;
21
+ }
22
+ const label = (document) => (document === 'migration-guide' ? 'Migration Guide' : 'Release Notes');
23
+ export function rawWikiTitle(project, version) {
24
+ const source = RAW_SOURCES[project];
25
+ return `${source.displayName} ${version} ${label(source.document)}`;
26
+ }
27
+ export function rawWikiUrl(project, version) {
28
+ const source = RAW_SOURCES[project];
29
+ const page = `${source.displayName.replace(' ', '-')}-${version}-${label(source.document).replace(' ', '-')}`;
30
+ return `https://raw.githubusercontent.com/wiki/spring-projects/${source.repo}/${page}.md`;
31
+ }
32
+ /** `auto` and the project's only document resolve to it; the other one is rejected before any request. */
33
+ export function resolveRawDocument(project, document) {
34
+ const source = RAW_SOURCES[project];
35
+ if (document !== 'auto' && document !== source.document) {
36
+ throw new Error(`${source.displayName} only has "${source.document}" documents (document="${document}" is not available)`);
37
+ }
38
+ return source.document;
39
+ }
40
+ /** A real page is markdown, never an HTML document (home page or error page). */
41
+ export function assertRawMarkdown(body, title, url) {
42
+ const text = body.trim();
43
+ if (!text || /^<(!doctype|html)/i.test(text)) {
44
+ throw new Error(`Wiki page not found: ${title} (${url})`);
45
+ }
46
+ return text;
47
+ }
48
+ //# sourceMappingURL=migration-sources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migration-sources.js","sourceRoot":"","sources":["../../src/services/migration-sources.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,sEAAsE;AACtE,EAAE;AACF,gGAAgG;AAChG,+DAA+D;AAC/D,+FAA+F;AAC/F,gFAAgF;AAChF,uGAAuG;AACvG,2FAA2F;AAC3F,6CAA6C;AAM7C,MAAM,CAAC,MAAM,kBAAkB,GAAgC,CAAC,aAAa,EAAE,kBAAkB,EAAE,cAAc,CAAC,CAAC;AAQnH,MAAM,WAAW,GAA+C;IAC9D,kBAAkB,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,WAAW,EAAE,kBAAkB,EAAE,QAAQ,EAAE,eAAe,EAAE;IAC5G,cAAc,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,WAAW,EAAE,cAAc,EAAE,QAAQ,EAAE,iBAAiB,EAAE;CACnG,CAAC;AAEF,MAAM,UAAU,kBAAkB,CAAC,KAAa;IAC9C,OAAQ,kBAAwC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,OAAyB;IAC1D,OAAO,OAAO,KAAK,aAAa,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC;AACtF,CAAC;AAED,MAAM,KAAK,GAAG,CAAC,QAA8B,EAAE,EAAE,CAAC,CAAC,QAAQ,KAAK,iBAAiB,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC;AAEzH,MAAM,UAAU,YAAY,CAAC,OAA4B,EAAE,OAAe;IACxE,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IACpC,OAAO,GAAG,MAAM,CAAC,WAAW,IAAI,OAAO,IAAI,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;AACtE,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAA4B,EAAE,OAAe;IACtE,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,GAAG,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,OAAO,IAAI,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC;IAC9G,OAAO,0DAA0D,MAAM,CAAC,IAAI,IAAI,IAAI,KAAK,CAAC;AAC5F,CAAC;AAED,0GAA0G;AAC1G,MAAM,UAAU,kBAAkB,CAChC,OAA4B,EAC5B,QAAuC;IAEvC,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IACpC,IAAI,QAAQ,KAAK,MAAM,IAAI,QAAQ,KAAK,MAAM,CAAC,QAAQ,EAAE,CAAC;QACxD,MAAM,IAAI,KAAK,CAAC,GAAG,MAAM,CAAC,WAAW,cAAc,MAAM,CAAC,QAAQ,0BAA0B,QAAQ,qBAAqB,CAAC,CAAC;IAC7H,CAAC;IACD,OAAO,MAAM,CAAC,QAAQ,CAAC;AACzB,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,iBAAiB,CAAC,IAAY,EAAE,KAAa,EAAE,GAAW;IACxE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IACzB,IAAI,CAAC,IAAI,IAAI,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CAAC,wBAAwB,KAAK,KAAK,GAAG,GAAG,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Release notes helpers: pure functions used by the get_release_notes tool.
3
+ */
4
+ export type ReleaseFocus = 'all' | 'breaking-changes' | 'new-features' | 'deprecations';
5
+ /**
6
+ * Normalizes a user-supplied release version: strips a leading "v", returns
7
+ * undefined for absent/empty/"latest" and throws on any invalid format.
8
+ * Accepts a full version ("3.5.0", "4.2.0-M2") or a minor ("3.5").
9
+ */
10
+ export declare function normalizeReleaseVersion(version?: unknown): string | undefined;
11
+ /** True for a normalized "X.Y" minor (as opposed to a full release version). */
12
+ export declare function isMinorVersion(normalized: string): boolean;
13
+ /**
14
+ * Among GitHub releases, the highest stable (X.Y.Z, no pre-release) patch of the given
15
+ * "X.Y" minor, or undefined. Numeric comparison: 3.1 never matches 3.10.
16
+ */
17
+ export declare function latestStableOfMinor<T extends {
18
+ tag_name?: unknown;
19
+ prerelease?: unknown;
20
+ }>(releases: T[], minor: string, tagPrefix?: string): T | undefined;
21
+ /** Filters a GitHub release body according to the requested focus. */
22
+ export declare function filterReleaseBody(body: string | null | undefined, focus: ReleaseFocus): string;
23
+ //# sourceMappingURL=release-notes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"release-notes.d.ts","sourceRoot":"","sources":["../../src/services/release-notes.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,kBAAkB,GAAG,cAAc,GAAG,cAAc,CAAC;AASxF;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAgB7E;AAED,gFAAgF;AAChF,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,EACxF,QAAQ,EAAE,CAAC,EAAE,EACb,KAAK,EAAE,MAAM,EACb,SAAS,SAAK,GACb,CAAC,GAAG,SAAS,CAiBf;AAED,sEAAsE;AACtE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,KAAK,EAAE,YAAY,GAAG,MAAM,CAiC9F"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Release notes helpers: pure functions used by the get_release_notes tool.
3
+ */
4
+ import { scanFences } from './markdown.js';
5
+ const VERSION_PATTERN = /^\d+\.\d+\.\d+(-[A-Za-z0-9.]+)?$/;
6
+ const MINOR_PATTERN = /^\d+\.\d+$/;
7
+ const STABLE_PATCH_PATTERN = /^(\d+)\.(\d+)\.(\d+)$/;
8
+ const BREAKING_PATTERN = /breaking|remov(e|ed|al)|incompatib|no longer|migrat/i;
9
+ const DEPRECATION_PATTERN = /deprecat/i;
10
+ const HEADING_PATTERN = /^(#{1,6})\s/;
11
+ /**
12
+ * Normalizes a user-supplied release version: strips a leading "v", returns
13
+ * undefined for absent/empty/"latest" and throws on any invalid format.
14
+ * Accepts a full version ("3.5.0", "4.2.0-M2") or a minor ("3.5").
15
+ */
16
+ export function normalizeReleaseVersion(version) {
17
+ if (version === undefined || version === null || version === '' || version === 'latest') {
18
+ return undefined;
19
+ }
20
+ if (typeof version !== 'string') {
21
+ throw new Error(`Invalid version ${JSON.stringify(version)}: expected a string like "3.5.0", "3.5" or "4.2.0-M2"`);
22
+ }
23
+ const normalized = version.startsWith('v') ? version.slice(1) : version;
24
+ if (!VERSION_PATTERN.test(normalized) && !MINOR_PATTERN.test(normalized)) {
25
+ throw new Error(`Invalid version "${version}": expected a release version like "3.5.0", "3.5" (latest release of that minor) or "4.2.0-M2"`);
26
+ }
27
+ return normalized;
28
+ }
29
+ /** True for a normalized "X.Y" minor (as opposed to a full release version). */
30
+ export function isMinorVersion(normalized) {
31
+ return MINOR_PATTERN.test(normalized);
32
+ }
33
+ /**
34
+ * Among GitHub releases, the highest stable (X.Y.Z, no pre-release) patch of the given
35
+ * "X.Y" minor, or undefined. Numeric comparison: 3.1 never matches 3.10.
36
+ */
37
+ export function latestStableOfMinor(releases, minor, tagPrefix = '') {
38
+ let best;
39
+ let bestPatch = -1;
40
+ for (const release of releases) {
41
+ if (typeof release?.tag_name !== 'string' || release.prerelease === true)
42
+ continue;
43
+ let tag = release.tag_name;
44
+ if (tagPrefix && tag.startsWith(tagPrefix))
45
+ tag = tag.slice(tagPrefix.length);
46
+ else if (tag.startsWith('v'))
47
+ tag = tag.slice(1);
48
+ const match = STABLE_PATCH_PATTERN.exec(tag);
49
+ if (!match || `${match[1]}.${match[2]}` !== minor)
50
+ continue;
51
+ const patch = Number(match[3]);
52
+ if (patch > bestPatch) {
53
+ best = release;
54
+ bestPatch = patch;
55
+ }
56
+ }
57
+ return best;
58
+ }
59
+ /** Filters a GitHub release body according to the requested focus. */
60
+ export function filterReleaseBody(body, focus) {
61
+ if (!body)
62
+ return '';
63
+ if (focus === 'all')
64
+ return body;
65
+ // GitHub sometimes returns CRLF line endings
66
+ const lines = body.split(/\r?\n/);
67
+ // A line inside a code block (or a fence line) is never a heading
68
+ const scan = scanFences(lines);
69
+ const isHeading = (index) => scan[index].inCode || scan[index].isFence ? null : HEADING_PATTERN.exec(lines[index]);
70
+ if (focus === 'new-features') {
71
+ const result = [];
72
+ let level = 0; // 0 = not inside the section
73
+ for (const [index, line] of lines.entries()) {
74
+ const heading = isHeading(index);
75
+ if (level === 0) {
76
+ if (heading && /new features/i.test(line)) {
77
+ level = heading[1].length;
78
+ result.push(line);
79
+ }
80
+ }
81
+ else {
82
+ if (heading && heading[1].length <= level)
83
+ break;
84
+ result.push(line);
85
+ }
86
+ }
87
+ return result.join('\n').trim();
88
+ }
89
+ const pattern = focus === 'breaking-changes' ? BREAKING_PATTERN : DEPRECATION_PATTERN;
90
+ return lines
91
+ .filter((line, index) => !isHeading(index) && pattern.test(line))
92
+ .join('\n');
93
+ }
94
+ //# sourceMappingURL=release-notes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"release-notes.js","sourceRoot":"","sources":["../../src/services/release-notes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * In-memory BM25 full-text index over documentation pages already read by the server.
3
+ * Pure module: no network, no dependency.
4
+ */
5
+ export interface SearchHit {
6
+ docId: string;
7
+ title: string;
8
+ url: string;
9
+ score: number;
10
+ snippet: string;
11
+ }
12
+ /** Lowercased tokens; dotted/dashed keys (spring.datasource.url) are kept whole and also split. */
13
+ export declare function tokenize(text: string): string[];
14
+ export declare class SearchIndex {
15
+ private readonly maxChars;
16
+ private readonly docs;
17
+ private readonly df;
18
+ private totalLength;
19
+ private totalChars;
20
+ constructor(opts?: {
21
+ maxChars?: number;
22
+ });
23
+ get size(): number;
24
+ has(docId: string): boolean;
25
+ add(docId: string, doc: {
26
+ title: string;
27
+ url: string;
28
+ text: string;
29
+ }): void;
30
+ search(query: string, limit: number, filter?: (docId: string) => boolean): SearchHit[];
31
+ private remove;
32
+ }
33
+ //# sourceMappingURL=search-index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-index.d.ts","sourceRoot":"","sources":["../../src/services/search-index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAyBD,mGAAmG;AACnG,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAY/C;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAiC;IACtD,OAAO,CAAC,QAAQ,CAAC,EAAE,CAA6B;IAChD,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,UAAU,CAAK;gBAEX,IAAI,GAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAO;IAI5C,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAI3B,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IA4B3E,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,GAAG,SAAS,EAAE;IAgCtF,OAAO,CAAC,MAAM;CAYf"}
@@ -0,0 +1,147 @@
1
+ /**
2
+ * In-memory BM25 full-text index over documentation pages already read by the server.
3
+ * Pure module: no network, no dependency.
4
+ */
5
+ const K1 = 1.2;
6
+ const B = 0.75;
7
+ const TITLE_WEIGHT = 3;
8
+ const DEFAULT_MAX_CHARS = 8_000_000;
9
+ const SNIPPET_BEFORE = 80;
10
+ const SNIPPET_AFTER = 120;
11
+ const FALLBACK_SNIPPET = 200;
12
+ const STOPWORDS = new Set([
13
+ "a", "an", "and", "are", "as", "at", "be", "by", "for", "from", "how", "in", "is", "it",
14
+ "of", "on", "or", "that", "the", "this", "to", "with", "what", "when", "which", "you", "your",
15
+ ]);
16
+ const keep = (token) => token.length > 1 && !STOPWORDS.has(token);
17
+ /** Lowercased tokens; dotted/dashed keys (spring.datasource.url) are kept whole and also split. */
18
+ export function tokenize(text) {
19
+ const tokens = [];
20
+ for (const match of text.toLowerCase().matchAll(/[a-z0-9]+(?:[.-][a-z0-9]+)*/g)) {
21
+ const token = match[0];
22
+ if (keep(token))
23
+ tokens.push(token);
24
+ if (/[.-]/.test(token)) {
25
+ for (const part of token.split(/[.-]/)) {
26
+ if (keep(part))
27
+ tokens.push(part);
28
+ }
29
+ }
30
+ }
31
+ return tokens;
32
+ }
33
+ export class SearchIndex {
34
+ maxChars;
35
+ docs = new Map();
36
+ df = new Map();
37
+ totalLength = 0;
38
+ totalChars = 0;
39
+ constructor(opts = {}) {
40
+ this.maxChars = opts.maxChars ?? DEFAULT_MAX_CHARS;
41
+ }
42
+ get size() {
43
+ return this.docs.size;
44
+ }
45
+ has(docId) {
46
+ return this.docs.has(docId);
47
+ }
48
+ add(docId, doc) {
49
+ this.remove(docId);
50
+ const text = doc.text.length > this.maxChars ? doc.text.slice(0, this.maxChars) : doc.text;
51
+ const tf = new Map();
52
+ let length = 0;
53
+ for (const token of tokenize(text)) {
54
+ tf.set(token, (tf.get(token) ?? 0) + 1);
55
+ length += 1;
56
+ }
57
+ for (const token of tokenize(doc.title)) {
58
+ tf.set(token, (tf.get(token) ?? 0) + TITLE_WEIGHT);
59
+ length += TITLE_WEIGHT;
60
+ }
61
+ for (const token of tf.keys()) {
62
+ this.df.set(token, (this.df.get(token) ?? 0) + 1);
63
+ }
64
+ this.docs.set(docId, { title: doc.title, url: doc.url, text, tf, length });
65
+ this.totalLength += length;
66
+ this.totalChars += text.length;
67
+ // Evict the oldest pages until the budget holds (the page just added is always last)
68
+ while (this.totalChars > this.maxChars && this.docs.size > 1) {
69
+ const oldest = this.docs.keys().next().value;
70
+ if (oldest === undefined)
71
+ break;
72
+ this.remove(oldest);
73
+ }
74
+ }
75
+ search(query, limit, filter) {
76
+ const terms = [...new Set(tokenize(query))];
77
+ if (terms.length === 0 || this.docs.size === 0)
78
+ return [];
79
+ const n = this.docs.size;
80
+ const avgLength = this.totalLength / n || 1;
81
+ const idf = new Map();
82
+ for (const term of terms) {
83
+ const df = this.df.get(term) ?? 0;
84
+ idf.set(term, Math.log(1 + (n - df + 0.5) / (df + 0.5)));
85
+ }
86
+ const scored = [];
87
+ for (const [docId, doc] of this.docs) {
88
+ if (filter && !filter(docId))
89
+ continue;
90
+ let score = 0;
91
+ for (const term of terms) {
92
+ const tf = doc.tf.get(term);
93
+ if (!tf)
94
+ continue;
95
+ score += idf.get(term) * (tf * (K1 + 1)) / (tf + K1 * (1 - B + B * doc.length / avgLength));
96
+ }
97
+ if (score > 0)
98
+ scored.push({ docId, score });
99
+ }
100
+ scored.sort((x, y) => y.score - x.score || x.docId.localeCompare(y.docId));
101
+ const byIdf = [...terms].sort((x, y) => idf.get(y) - idf.get(x));
102
+ return scored.slice(0, limit).map(({ docId, score }) => {
103
+ const doc = this.docs.get(docId);
104
+ return { docId, title: doc.title, url: doc.url, score, snippet: makeSnippet(doc.text, byIdf) };
105
+ });
106
+ }
107
+ remove(docId) {
108
+ const doc = this.docs.get(docId);
109
+ if (!doc)
110
+ return;
111
+ for (const token of doc.tf.keys()) {
112
+ const next = (this.df.get(token) ?? 1) - 1;
113
+ if (next <= 0)
114
+ this.df.delete(token);
115
+ else
116
+ this.df.set(token, next);
117
+ }
118
+ this.totalLength -= doc.length;
119
+ this.totalChars -= doc.text.length;
120
+ this.docs.delete(docId);
121
+ }
122
+ }
123
+ /** ~200 characters around the best term, trimmed to whole words. */
124
+ function makeSnippet(text, terms) {
125
+ const lower = text.toLowerCase();
126
+ for (const term of terms) {
127
+ const at = lower.indexOf(term);
128
+ if (at < 0)
129
+ continue;
130
+ let start = Math.max(0, at - SNIPPET_BEFORE);
131
+ let end = Math.min(text.length, at + SNIPPET_AFTER);
132
+ if (start > 0) {
133
+ const space = text.slice(start, at).search(/\s/);
134
+ if (space >= 0)
135
+ start += space + 1;
136
+ }
137
+ if (end < text.length) {
138
+ const space = text.slice(at, end).search(/\s\S*$/);
139
+ if (space >= 0)
140
+ end = at + space;
141
+ }
142
+ const body = text.slice(start, end).replace(/\s+/g, " ").trim();
143
+ return `${start > 0 ? "…" : ""}${body}${end < text.length ? "…" : ""}`;
144
+ }
145
+ return text.slice(0, FALLBACK_SNIPPET).replace(/\s+/g, " ").trim();
146
+ }
147
+ //# sourceMappingURL=search-index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-index.js","sourceRoot":"","sources":["../../src/services/search-index.ts"],"names":[],"mappings":"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"}
@@ -10,22 +10,34 @@
10
10
  export interface SpringProjectConfig {
11
11
  /** Unique project identifier (e.g., "boot", "ai", "security") */
12
12
  id: string;
13
+ /** GitHub repository (owner/name) hosting the releases */
14
+ githubRepo?: string;
15
+ /** Prefix of the release tag, e.g. "v" for v3.5.0 */
16
+ githubTagPrefix?: string;
13
17
  /** Human-readable display name (e.g., "Spring Boot", "Spring AI") */
14
18
  displayName: string;
15
- /** Base URL for project documentation */
16
- baseDocUrl: string;
17
- /** Latest stable version (e.g., "3.5.6", "1.1.2") */
18
- latestVersion: string;
19
- /** Path to API documentation relative to baseDocUrl (optional) */
20
- apiPath?: string;
21
19
  /** Whether documentation URLs include version in path */
22
20
  hasVersionedDocs: boolean;
23
21
  /** Cache TTL strategy: 'short' (30min) for frequently updated, 'long' (24h) for stable */
24
22
  cacheStrategy: 'short' | 'long';
25
- /** Search keywords/scopes associated with this project */
26
- scopes: string[];
27
23
  /** Reference documentation sections available for this project (optional) */
28
24
  referenceSections?: string[];
25
+ /** Base URL of the reference documentation (always the current version, no version in the path) */
26
+ referenceBaseUrl: string;
27
+ /**
28
+ * How a section maps to a page:
29
+ * 'directory' -> <base>/reference/<section>/index.html, 'flat' -> <base>/<section>.html
30
+ */
31
+ referenceLayout: 'directory' | 'flat';
32
+ /**
33
+ * Sections living outside the standard layout, as a path relative to the docs root (directory layout only).
34
+ * Limitation: in the 'directory' layout a subsection only reaches <dir>/<subsection>.html. A subsection that
35
+ * is itself a folder (e.g. oauth2/login/..., authentication/passwords/...) is not reachable, which is why
36
+ * such top-level sections are exposed through referencePaths instead.
37
+ */
38
+ referencePaths?: Record<string, string>;
39
+ /** Prefix of the unversioned reference URL after which "<version>/" is inserted to pin a documentation version */
40
+ versionInsertAfter?: string;
29
41
  }
30
42
  /**
31
43
  * Spring Projects Registry
@@ -48,10 +60,6 @@ export declare class SpringProjectsConfig {
48
60
  * @throws Error if project not found
49
61
  */
50
62
  getProject(projectId: string): SpringProjectConfig;
51
- /**
52
- * Check if a project is supported
53
- */
54
- hasProject(projectId: string): boolean;
55
63
  /**
56
64
  * Get all supported project IDs
57
65
  */
@@ -61,30 +69,15 @@ export declare class SpringProjectsConfig {
61
69
  */
62
70
  getAllProjects(): SpringProjectConfig[];
63
71
  /**
64
- * Build documentation reference URL for a project
72
+ * Build documentation reference URL for a project (current documentation version)
65
73
  *
66
74
  * @param projectId - Project identifier
67
- * @param section - Documentation section (e.g., "web", "chatclient")
68
- * @param version - Optional version override (defaults to latest)
69
- * @returns Complete URL to documentation section
70
- */
71
- buildReferenceUrl(projectId: string, section: string, version?: string): string;
72
- /**
73
- * Build API documentation URL for a project
74
- *
75
- * @param projectId - Project identifier
76
- * @param version - Optional version override (defaults to latest)
77
- * @returns Complete URL to API documentation
78
- */
79
- buildApiUrl(projectId: string, version?: string): string;
80
- /**
81
- * Find projects by scope/keyword
82
- * Useful for routing queries to appropriate projects
83
- *
84
- * @param scope - Search scope keyword (e.g., "ai", "boot", "security")
85
- * @returns Array of matching project configurations
75
+ * @param section - Documentation section (e.g., "web", "chatclient"), already validated as a safe URL segment
76
+ * @param subsection - Optional page inside the section (e.g., "servlet" for boot/web)
77
+ * @param version - Optional documentation version ("3.4" or "3.4.2"); omitted or "current" means the current docs
78
+ * @returns Complete URL to the documentation page
86
79
  */
87
- findProjectsByScope(scope: string): SpringProjectConfig[];
80
+ buildReferenceUrl(projectId: string, section: string, subsection?: string, version?: string): string;
88
81
  /**
89
82
  * Get cache TTL in milliseconds for a project
90
83
  *
@@ -1 +1 @@
1
- {"version":3,"file":"spring-projects-config.d.ts","sourceRoot":"","sources":["../../src/services/spring-projects-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,mBAAmB;IAClC,iEAAiE;IACjE,EAAE,EAAE,MAAM,CAAC;IAEX,qEAAqE;IACrE,WAAW,EAAE,MAAM,CAAC;IAEpB,yCAAyC;IACzC,UAAU,EAAE,MAAM,CAAC;IAEnB,qDAAqD;IACrD,aAAa,EAAE,MAAM,CAAC;IAEtB,kEAAkE;IAClE,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,yDAAyD;IACzD,gBAAgB,EAAE,OAAO,CAAC;IAE1B,0FAA0F;IAC1F,aAAa,EAAE,OAAO,GAAG,MAAM,CAAC;IAEhC,0DAA0D;IAC1D,MAAM,EAAE,MAAM,EAAE,CAAC;IAEjB,6EAA6E;IAC7E,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAsF3D,CAAC;AAEH;;;;;GAKG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmC;gBAEhD,WAAW,GAAE,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAmB;IAI3E;;;OAGG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,mBAAmB;IAWlD;;OAEG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAItC;;OAEG;IACH,gBAAgB,IAAI,MAAM,EAAE;IAI5B;;OAEG;IACH,cAAc,IAAI,mBAAmB,EAAE;IAIvC;;;;;;;OAOG;IACH,iBAAiB,CACf,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,GACf,MAAM;IAWT;;;;;;OAMG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM;IAgBxD;;;;;;OAMG;IACH,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,mBAAmB,EAAE;IAOzD;;;;;OAKG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IActC;;;OAGG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO;CAU7D;AAED;;GAEG;AACH,eAAO,MAAM,oBAAoB,sBAA6B,CAAC"}
1
+ {"version":3,"file":"spring-projects-config.d.ts","sourceRoot":"","sources":["../../src/services/spring-projects-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,MAAM,WAAW,mBAAmB;IAClC,iEAAiE;IACjE,EAAE,EAAE,MAAM,CAAC;IAEX,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,qDAAqD;IACrD,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,qEAAqE;IACrE,WAAW,EAAE,MAAM,CAAC;IAEpB,yDAAyD;IACzD,gBAAgB,EAAE,OAAO,CAAC;IAE1B,0FAA0F;IAC1F,aAAa,EAAE,OAAO,GAAG,MAAM,CAAC;IAEhC,6EAA6E;IAC7E,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAE7B,mGAAmG;IACnG,gBAAgB,EAAE,MAAM,CAAC;IAEzB;;;OAGG;IACH,eAAe,EAAE,WAAW,GAAG,MAAM,CAAC;IAEtC;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAExC,kHAAkH;IAClH,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAqR3D,CAAC;AAEH;;;;;GAKG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmC;gBAEhD,WAAW,GAAE,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAmB;IAI3E;;;OAGG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,mBAAmB;IAWlD;;OAEG;IACH,gBAAgB,IAAI,MAAM,EAAE;IAI5B;;OAEG;IACH,cAAc,IAAI,mBAAmB,EAAE;IAIvC;;;;;;;;OAQG;IACH,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM;IAwBpG;;;;;OAKG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IActC;;;OAGG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO;CAU7D;AAED;;GAEG;AACH,eAAO,MAAM,oBAAoB,sBAA6B,CAAC"}