@enokdev/springdocs-mcp 1.2.7 → 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.
- package/README.md +108 -40
- package/build/config.d.ts +9 -0
- package/build/config.d.ts.map +1 -0
- package/build/config.js +32 -0
- package/build/config.js.map +1 -0
- package/build/format.d.ts +6 -0
- package/build/format.d.ts.map +1 -0
- package/build/format.js +23 -0
- package/build/format.js.map +1 -0
- package/build/http-server.d.ts +15 -0
- package/build/http-server.d.ts.map +1 -0
- package/build/http-server.js +131 -0
- package/build/http-server.js.map +1 -0
- package/build/index.js +184 -69
- package/build/index.js.map +1 -1
- package/build/prompts.d.ts +23 -0
- package/build/prompts.d.ts.map +1 -0
- package/build/prompts.js +112 -0
- package/build/prompts.js.map +1 -0
- package/build/resources.d.ts +31 -0
- package/build/resources.d.ts.map +1 -0
- package/build/resources.js +66 -0
- package/build/resources.js.map +1 -0
- package/build/services/advanced-features.d.ts +19 -9
- package/build/services/advanced-features.d.ts.map +1 -1
- package/build/services/advanced-features.js +266 -191
- package/build/services/advanced-features.js.map +1 -1
- package/build/services/boot-wiki.d.ts +25 -0
- package/build/services/boot-wiki.d.ts.map +1 -0
- package/build/services/boot-wiki.js +103 -0
- package/build/services/boot-wiki.js.map +1 -0
- package/build/services/cache.d.ts +18 -3
- package/build/services/cache.d.ts.map +1 -1
- package/build/services/cache.js +84 -3
- package/build/services/cache.js.map +1 -1
- package/build/services/dependency-finder.d.ts +46 -0
- package/build/services/dependency-finder.d.ts.map +1 -0
- package/build/services/dependency-finder.js +181 -0
- package/build/services/dependency-finder.js.map +1 -0
- package/build/services/diagnosis.d.ts +46 -0
- package/build/services/diagnosis.d.ts.map +1 -0
- package/build/services/diagnosis.js +481 -0
- package/build/services/diagnosis.js.map +1 -0
- package/build/services/guides-list.d.ts +16 -0
- package/build/services/guides-list.d.ts.map +1 -0
- package/build/services/guides-list.js +45 -0
- package/build/services/guides-list.js.map +1 -0
- package/build/services/http.d.ts +21 -0
- package/build/services/http.d.ts.map +1 -0
- package/build/services/http.js +168 -0
- package/build/services/http.js.map +1 -0
- package/build/services/initializr.d.ts +41 -0
- package/build/services/initializr.d.ts.map +1 -0
- package/build/services/initializr.js +135 -0
- package/build/services/initializr.js.map +1 -0
- package/build/services/markdown.d.ts +56 -0
- package/build/services/markdown.d.ts.map +1 -0
- package/build/services/markdown.js +146 -0
- package/build/services/markdown.js.map +1 -0
- package/build/services/migration-sources.d.ts +13 -0
- package/build/services/migration-sources.d.ts.map +1 -0
- package/build/services/migration-sources.js +48 -0
- package/build/services/migration-sources.js.map +1 -0
- package/build/services/release-notes.d.ts +23 -0
- package/build/services/release-notes.d.ts.map +1 -0
- package/build/services/release-notes.js +94 -0
- package/build/services/release-notes.js.map +1 -0
- package/build/services/search-index.d.ts +33 -0
- package/build/services/search-index.d.ts.map +1 -0
- package/build/services/search-index.js +147 -0
- package/build/services/search-index.js.map +1 -0
- package/build/services/spring-projects-config.d.ts +98 -0
- package/build/services/spring-projects-config.d.ts.map +1 -0
- package/build/services/spring-projects-config.js +397 -0
- package/build/services/spring-projects-config.js.map +1 -0
- package/build/services/springboot-docs-optimized.d.ts +72 -21
- package/build/services/springboot-docs-optimized.d.ts.map +1 -1
- package/build/services/springboot-docs-optimized.js +354 -208
- package/build/services/springboot-docs-optimized.js.map +1 -1
- package/build/services/url.d.ts +15 -0
- package/build/services/url.d.ts.map +1 -0
- package/build/services/url.js +40 -0
- package/build/services/url.js.map +1 -0
- package/build/tools/index.d.ts +393 -2
- package/build/tools/index.d.ts.map +1 -1
- package/build/tools/index.js +188 -15
- package/build/tools/index.js.map +1 -1
- package/build/validation.d.ts +7 -0
- package/build/validation.d.ts.map +1 -0
- package/build/validation.js +50 -0
- package/build/validation.js.map +1 -0
- package/build/version.d.ts +3 -0
- package/build/version.d.ts.map +1 -0
- package/build/version.js +6 -0
- package/build/version.js.map +1 -0
- package/package.json +9 -7
- package/build/debug.d.ts +0 -3
- package/build/debug.d.ts.map +0 -1
- package/build/debug.js +0 -292
- package/build/debug.js.map +0 -1
- package/build/services/advanced-features-old.d.ts +0 -45
- package/build/services/advanced-features-old.d.ts.map +0 -1
- package/build/services/advanced-features-old.js +0 -792
- package/build/services/advanced-features-old.js.map +0 -1
- package/build/services/springboot-docs-optimized-old.d.ts +0 -55
- package/build/services/springboot-docs-optimized-old.d.ts.map +0 -1
- package/build/services/springboot-docs-optimized-old.js +0 -588
- package/build/services/springboot-docs-optimized-old.js.map +0 -1
- package/build/services/springboot-docs.d.ts +0 -65
- package/build/services/springboot-docs.d.ts.map +0 -1
- package/build/services/springboot-docs.js +0 -708
- 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":"AAAA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAI3C,MAAM,eAAe,GAAG,kCAAkC,CAAC;AAC3D,MAAM,aAAa,GAAG,YAAY,CAAC;AACnC,MAAM,oBAAoB,GAAG,uBAAuB,CAAC;AACrD,MAAM,gBAAgB,GAAG,sDAAsD,CAAC;AAChF,MAAM,mBAAmB,GAAG,WAAW,CAAC;AACxC,MAAM,eAAe,GAAG,aAAa,CAAC;AAEtC;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,OAAiB;IACvD,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,EAAE,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACxF,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CACb,mBAAmB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,uDAAuD,CAClG,CAAC;IACJ,CAAC;IACD,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IACxE,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QACzE,MAAM,IAAI,KAAK,CACb,oBAAoB,OAAO,gGAAgG,CAC5H,CAAC;IACJ,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,cAAc,CAAC,UAAkB;IAC/C,OAAO,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;AACxC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CACjC,QAAa,EACb,KAAa,EACb,SAAS,GAAG,EAAE;IAEd,IAAI,IAAmB,CAAC;IACxB,IAAI,SAAS,GAAG,CAAC,CAAC,CAAC;IACnB,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,OAAO,OAAO,EAAE,QAAQ,KAAK,QAAQ,IAAI,OAAO,CAAC,UAAU,KAAK,IAAI;YAAE,SAAS;QACnF,IAAI,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC;QAC3B,IAAI,SAAS,IAAI,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC;YAAE,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;aACzE,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACjD,MAAM,KAAK,GAAG,oBAAoB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7C,IAAI,CAAC,KAAK,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK;YAAE,SAAS;QAC5D,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/B,IAAI,KAAK,GAAG,SAAS,EAAE,CAAC;YACtB,IAAI,GAAG,OAAO,CAAC;YACf,SAAS,GAAG,KAAK,CAAC;QACpB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,iBAAiB,CAAC,IAA+B,EAAE,KAAmB;IACpF,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,IAAI,KAAK,KAAK,KAAK;QAAE,OAAO,IAAI,CAAC;IAEjC,6CAA6C;IAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAClC,kEAAkE;IAClE,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IAC/B,MAAM,SAAS,GAAG,CAAC,KAAa,EAA0B,EAAE,CAC1D,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IAExF,IAAI,KAAK,KAAK,cAAc,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,6BAA6B;QAC5C,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;YAC5C,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;YACjC,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;gBAChB,IAAI,OAAO,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC1C,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;oBAC1B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACpB,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,IAAI,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,KAAK;oBAAE,MAAM;gBACjD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACpB,CAAC;QACH,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;IAClC,CAAC;IAED,MAAM,OAAO,GAAG,KAAK,KAAK,kBAAkB,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,mBAAmB,CAAC;IACtF,OAAO,KAAK;SACT,MAAM,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;SAChE,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC"}
|
|
@@ -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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"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spring Projects Configuration Service
|
|
3
|
+
*
|
|
4
|
+
* Central registry for all Spring project metadata, enabling generic multi-project
|
|
5
|
+
* architecture without hardcoding specific projects in service implementations.
|
|
6
|
+
*
|
|
7
|
+
* This configuration-driven approach allows adding new Spring projects (Security,
|
|
8
|
+
* Data, Cloud, etc.) with zero code changes - only configuration updates.
|
|
9
|
+
*/
|
|
10
|
+
export interface SpringProjectConfig {
|
|
11
|
+
/** Unique project identifier (e.g., "boot", "ai", "security") */
|
|
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;
|
|
17
|
+
/** Human-readable display name (e.g., "Spring Boot", "Spring AI") */
|
|
18
|
+
displayName: string;
|
|
19
|
+
/** Whether documentation URLs include version in path */
|
|
20
|
+
hasVersionedDocs: boolean;
|
|
21
|
+
/** Cache TTL strategy: 'short' (30min) for frequently updated, 'long' (24h) for stable */
|
|
22
|
+
cacheStrategy: 'short' | 'long';
|
|
23
|
+
/** Reference documentation sections available for this project (optional) */
|
|
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;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Spring Projects Registry
|
|
44
|
+
*
|
|
45
|
+
* Single source of truth for all supported Spring projects.
|
|
46
|
+
* To add a new Spring project, simply add a new entry here - no code changes required.
|
|
47
|
+
*/
|
|
48
|
+
export declare const SPRING_PROJECTS: Map<string, SpringProjectConfig>;
|
|
49
|
+
/**
|
|
50
|
+
* Spring Projects Configuration Manager
|
|
51
|
+
*
|
|
52
|
+
* Provides type-safe access to Spring project configurations with validation
|
|
53
|
+
* and helper methods for URL construction.
|
|
54
|
+
*/
|
|
55
|
+
export declare class SpringProjectsConfig {
|
|
56
|
+
private readonly projects;
|
|
57
|
+
constructor(projectsMap?: Map<string, SpringProjectConfig>);
|
|
58
|
+
/**
|
|
59
|
+
* Get configuration for a specific Spring project
|
|
60
|
+
* @throws Error if project not found
|
|
61
|
+
*/
|
|
62
|
+
getProject(projectId: string): SpringProjectConfig;
|
|
63
|
+
/**
|
|
64
|
+
* Get all supported project IDs
|
|
65
|
+
*/
|
|
66
|
+
getAllProjectIds(): string[];
|
|
67
|
+
/**
|
|
68
|
+
* Get all project configurations
|
|
69
|
+
*/
|
|
70
|
+
getAllProjects(): SpringProjectConfig[];
|
|
71
|
+
/**
|
|
72
|
+
* Build documentation reference URL for a project (current documentation version)
|
|
73
|
+
*
|
|
74
|
+
* @param projectId - Project identifier
|
|
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
|
|
79
|
+
*/
|
|
80
|
+
buildReferenceUrl(projectId: string, section: string, subsection?: string, version?: string): string;
|
|
81
|
+
/**
|
|
82
|
+
* Get cache TTL in milliseconds for a project
|
|
83
|
+
*
|
|
84
|
+
* @param projectId - Project identifier
|
|
85
|
+
* @returns TTL in milliseconds
|
|
86
|
+
*/
|
|
87
|
+
getCacheTTL(projectId: string): number;
|
|
88
|
+
/**
|
|
89
|
+
* Validate if a section exists for a project
|
|
90
|
+
* Returns true if validation passes or if project doesn't define sections
|
|
91
|
+
*/
|
|
92
|
+
validateSection(projectId: string, section: string): boolean;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Singleton instance for application-wide use
|
|
96
|
+
*/
|
|
97
|
+
export declare const springProjectsConfig: SpringProjectsConfig;
|
|
98
|
+
//# sourceMappingURL=spring-projects-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|