@get-technology-inc/jamf-docs-mcp-server 3.0.6 → 3.0.7

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 (183) hide show
  1. package/dist/core/constants/doc-types.d.ts +48 -0
  2. package/dist/core/constants/doc-types.d.ts.map +1 -0
  3. package/dist/core/constants/doc-types.js +58 -0
  4. package/dist/core/constants/doc-types.js.map +1 -0
  5. package/dist/core/constants/index.d.ts +16 -0
  6. package/dist/core/constants/index.d.ts.map +1 -0
  7. package/dist/core/constants/index.js +12 -0
  8. package/dist/core/constants/index.js.map +1 -0
  9. package/dist/core/constants/limits.d.ts +39 -0
  10. package/dist/core/constants/limits.d.ts.map +1 -0
  11. package/dist/core/constants/limits.js +51 -0
  12. package/dist/core/constants/limits.js.map +1 -0
  13. package/dist/core/constants/locales.d.ts +34 -0
  14. package/dist/core/constants/locales.d.ts.map +1 -0
  15. package/dist/core/constants/locales.js +20 -0
  16. package/dist/core/constants/locales.js.map +1 -0
  17. package/dist/core/constants/products.d.ts +127 -0
  18. package/dist/core/constants/products.d.ts.map +1 -0
  19. package/dist/core/constants/products.js +135 -0
  20. package/dist/core/constants/products.js.map +1 -0
  21. package/dist/core/constants/topics.d.ts +168 -0
  22. package/dist/core/constants/topics.d.ts.map +1 -0
  23. package/dist/core/constants/topics.js +186 -0
  24. package/dist/core/constants/topics.js.map +1 -0
  25. package/dist/core/constants.d.ts +7 -404
  26. package/dist/core/constants.d.ts.map +1 -1
  27. package/dist/core/constants.js +16 -427
  28. package/dist/core/constants.js.map +1 -1
  29. package/dist/core/http-client.d.ts +14 -2
  30. package/dist/core/http-client.d.ts.map +1 -1
  31. package/dist/core/http-client.js +148 -17
  32. package/dist/core/http-client.js.map +1 -1
  33. package/dist/core/index.d.ts +12 -12
  34. package/dist/core/index.d.ts.map +1 -1
  35. package/dist/core/index.js +12 -13
  36. package/dist/core/index.js.map +1 -1
  37. package/dist/core/resources/index.js +1 -1
  38. package/dist/core/resources/index.js.map +1 -1
  39. package/dist/core/schemas/index.d.ts +3 -1
  40. package/dist/core/schemas/index.d.ts.map +1 -1
  41. package/dist/core/schemas/index.js +12 -1
  42. package/dist/core/schemas/index.js.map +1 -1
  43. package/dist/core/schemas/output.d.ts +7 -0
  44. package/dist/core/schemas/output.d.ts.map +1 -1
  45. package/dist/core/schemas/output.js +7 -0
  46. package/dist/core/schemas/output.js.map +1 -1
  47. package/dist/core/services/article-service.d.ts +24 -0
  48. package/dist/core/services/article-service.d.ts.map +1 -0
  49. package/dist/core/services/article-service.js +118 -0
  50. package/dist/core/services/article-service.js.map +1 -0
  51. package/dist/core/services/content-parser.d.ts +38 -0
  52. package/dist/core/services/content-parser.d.ts.map +1 -0
  53. package/dist/core/services/content-parser.js +146 -0
  54. package/dist/core/services/content-parser.js.map +1 -0
  55. package/dist/core/services/ft-client.d.ts +52 -0
  56. package/dist/core/services/ft-client.d.ts.map +1 -0
  57. package/dist/core/services/ft-client.js +87 -0
  58. package/dist/core/services/ft-client.js.map +1 -0
  59. package/dist/core/services/glossary.d.ts +9 -5
  60. package/dist/core/services/glossary.d.ts.map +1 -1
  61. package/dist/core/services/glossary.js +174 -107
  62. package/dist/core/services/glossary.js.map +1 -1
  63. package/dist/core/services/interfaces/cache.d.ts +6 -6
  64. package/dist/core/services/interfaces/cache.d.ts.map +1 -1
  65. package/dist/core/services/interfaces/index.d.ts +3 -3
  66. package/dist/core/services/interfaces/index.d.ts.map +1 -1
  67. package/dist/core/services/interfaces/logger.d.ts +17 -10
  68. package/dist/core/services/interfaces/logger.d.ts.map +1 -1
  69. package/dist/core/services/interfaces/metadata.d.ts +0 -10
  70. package/dist/core/services/interfaces/metadata.d.ts.map +1 -1
  71. package/dist/core/services/interfaces/providers.d.ts +23 -10
  72. package/dist/core/services/interfaces/providers.d.ts.map +1 -1
  73. package/dist/core/services/logging.d.ts +38 -18
  74. package/dist/core/services/logging.d.ts.map +1 -1
  75. package/dist/core/services/logging.js +78 -29
  76. package/dist/core/services/logging.js.map +1 -1
  77. package/dist/core/services/maps-registry.d.ts +82 -0
  78. package/dist/core/services/maps-registry.d.ts.map +1 -0
  79. package/dist/core/services/maps-registry.js +232 -0
  80. package/dist/core/services/maps-registry.js.map +1 -0
  81. package/dist/core/services/metadata.d.ts +18 -9
  82. package/dist/core/services/metadata.d.ts.map +1 -1
  83. package/dist/core/services/metadata.js +113 -254
  84. package/dist/core/services/metadata.js.map +1 -1
  85. package/dist/core/services/search-service.d.ts +31 -0
  86. package/dist/core/services/search-service.d.ts.map +1 -0
  87. package/dist/core/services/search-service.js +471 -0
  88. package/dist/core/services/search-service.js.map +1 -0
  89. package/dist/core/services/toc-service.d.ts +30 -0
  90. package/dist/core/services/toc-service.d.ts.map +1 -0
  91. package/dist/core/services/toc-service.js +101 -0
  92. package/dist/core/services/toc-service.js.map +1 -0
  93. package/dist/core/services/tokenizer.d.ts +1 -1
  94. package/dist/core/services/tokenizer.d.ts.map +1 -1
  95. package/dist/core/services/tokenizer.js +3 -3
  96. package/dist/core/services/tokenizer.js.map +1 -1
  97. package/dist/core/services/topic-resolver.d.ts +57 -0
  98. package/dist/core/services/topic-resolver.d.ts.map +1 -0
  99. package/dist/core/services/topic-resolver.js +185 -0
  100. package/dist/core/services/topic-resolver.js.map +1 -0
  101. package/dist/core/tools/batch-get-articles.d.ts +0 -2
  102. package/dist/core/tools/batch-get-articles.d.ts.map +1 -1
  103. package/dist/core/tools/batch-get-articles.js +12 -46
  104. package/dist/core/tools/batch-get-articles.js.map +1 -1
  105. package/dist/core/tools/get-article.d.ts.map +1 -1
  106. package/dist/core/tools/get-article.js +29 -112
  107. package/dist/core/tools/get-article.js.map +1 -1
  108. package/dist/core/tools/get-toc.js +1 -1
  109. package/dist/core/tools/get-toc.js.map +1 -1
  110. package/dist/core/tools/search.js +5 -5
  111. package/dist/core/tools/search.js.map +1 -1
  112. package/dist/core/types/context.d.ts +5 -2
  113. package/dist/core/types/context.d.ts.map +1 -1
  114. package/dist/core/types.d.ts +141 -31
  115. package/dist/core/types.d.ts.map +1 -1
  116. package/dist/core/types.js.map +1 -1
  117. package/dist/core/utils/bundle.d.ts +14 -1
  118. package/dist/core/utils/bundle.d.ts.map +1 -1
  119. package/dist/core/utils/bundle.js +19 -2
  120. package/dist/core/utils/bundle.js.map +1 -1
  121. package/dist/core/utils/format-article.d.ts +45 -0
  122. package/dist/core/utils/format-article.d.ts.map +1 -0
  123. package/dist/core/utils/format-article.js +138 -0
  124. package/dist/core/utils/format-article.js.map +1 -0
  125. package/dist/core/utils/ft-metadata.d.ts +22 -0
  126. package/dist/core/utils/ft-metadata.d.ts.map +1 -0
  127. package/dist/core/utils/ft-metadata.js +30 -0
  128. package/dist/core/utils/ft-metadata.js.map +1 -0
  129. package/dist/core/utils/sanitize.d.ts.map +1 -1
  130. package/dist/core/utils/sanitize.js +0 -2
  131. package/dist/core/utils/sanitize.js.map +1 -1
  132. package/dist/core/utils/url.d.ts.map +1 -1
  133. package/dist/core/utils/url.js +0 -1
  134. package/dist/core/utils/url.js.map +1 -1
  135. package/dist/index.js +10 -6
  136. package/dist/index.js.map +1 -1
  137. package/dist/platforms/node/http-server.d.ts +13 -0
  138. package/dist/platforms/node/http-server.d.ts.map +1 -0
  139. package/dist/platforms/node/http-server.js +231 -0
  140. package/dist/platforms/node/http-server.js.map +1 -0
  141. package/dist/platforms/node/index.d.ts +1 -1
  142. package/dist/platforms/node/index.d.ts.map +1 -1
  143. package/dist/platforms/node/index.js +1 -1
  144. package/dist/platforms/node/index.js.map +1 -1
  145. package/dist/platforms/node/logger.d.ts +13 -2
  146. package/dist/platforms/node/logger.d.ts.map +1 -1
  147. package/dist/platforms/node/logger.js +17 -4
  148. package/dist/platforms/node/logger.js.map +1 -1
  149. package/dist/transport/http-handler.d.ts +25 -0
  150. package/dist/transport/http-handler.d.ts.map +1 -0
  151. package/dist/transport/http-handler.js +193 -0
  152. package/dist/transport/http-handler.js.map +1 -0
  153. package/dist/transport/http-types.d.ts +22 -0
  154. package/dist/transport/http-types.d.ts.map +1 -0
  155. package/dist/transport/http-types.js +65 -0
  156. package/dist/transport/http-types.js.map +1 -0
  157. package/dist/transport/http.d.ts +4 -8
  158. package/dist/transport/http.d.ts.map +1 -1
  159. package/dist/transport/http.js +4 -376
  160. package/dist/transport/http.js.map +1 -1
  161. package/dist/transport/index.js +2 -2
  162. package/dist/transport/index.js.map +1 -1
  163. package/dist/transport/rate-limiter.d.ts +16 -0
  164. package/dist/transport/rate-limiter.d.ts.map +1 -0
  165. package/dist/transport/rate-limiter.js +55 -0
  166. package/dist/transport/rate-limiter.js.map +1 -0
  167. package/package.json +3 -3
  168. package/dist/core/services/interfaces.d.ts +0 -94
  169. package/dist/core/services/interfaces.d.ts.map +0 -1
  170. package/dist/core/services/interfaces.js +0 -8
  171. package/dist/core/services/interfaces.js.map +0 -1
  172. package/dist/core/services/scraper.d.ts +0 -90
  173. package/dist/core/services/scraper.d.ts.map +0 -1
  174. package/dist/core/services/scraper.js +0 -990
  175. package/dist/core/services/scraper.js.map +0 -1
  176. package/dist/core/utils/doc-type.d.ts +0 -12
  177. package/dist/core/utils/doc-type.d.ts.map +0 -1
  178. package/dist/core/utils/doc-type.js +0 -24
  179. package/dist/core/utils/doc-type.js.map +0 -1
  180. package/dist/platforms/node/metadata.d.ts +0 -18
  181. package/dist/platforms/node/metadata.d.ts.map +0 -1
  182. package/dist/platforms/node/metadata.js +0 -35
  183. package/dist/platforms/node/metadata.js.map +0 -1
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Shared article-fetch service
3
+ *
4
+ * Consolidates the duplicated fetch-parse-tokenize pipeline that was in
5
+ * both get-article.ts and batch-get-articles.ts into a single function.
6
+ *
7
+ * Key optimization: metadata and content are fetched in parallel via
8
+ * Promise.all when the cache misses.
9
+ */
10
+ import { TOKEN_CONFIG } from '../constants.js';
11
+ import { buildDisplayUrl } from './topic-resolver.js';
12
+ import { fetchTopicContent, fetchTopicMetadata } from './ft-client.js';
13
+ import { parseArticle } from './content-parser.js';
14
+ import { getMetaValue, bundleStemToDisplayName, FT_META } from '../utils/ft-metadata.js';
15
+ import { extractSections, extractSummary, extractSection, truncateToTokenLimit, createTokenInfo, } from './tokenizer.js';
16
+ export async function fetchArticleFromFt(cache, mapId, contentId, articleUrl, options, cacheTtl) {
17
+ const maxTokens = options.maxTokens ?? TOKEN_CONFIG.DEFAULT_MAX_TOKENS;
18
+ const cacheKey = `ft:article:v2:${mapId}:${contentId}`;
19
+ let cached = await cache.get(cacheKey);
20
+ if (cached === null) {
21
+ // Cache miss — fetch metadata AND content in parallel
22
+ const [topicMeta, html] = await Promise.all([
23
+ fetchTopicMetadata(mapId, contentId),
24
+ fetchTopicContent(mapId, contentId),
25
+ ]);
26
+ const displayUrl = deriveDisplayUrl(topicMeta.readerUrl, articleUrl);
27
+ const { product, version } = extractProductVersion(topicMeta.metadata);
28
+ const rawContent = parseArticle(html, displayUrl, { includeRelated: true });
29
+ cached = { content: rawContent, displayUrl, product, version };
30
+ await cache.set(cacheKey, cached, cacheTtl);
31
+ }
32
+ const { content: rawContent, displayUrl, product, version } = cached;
33
+ // Build base result (shared across all code paths)
34
+ const allSections = extractSections(rawContent.content);
35
+ const base = {
36
+ title: rawContent.title,
37
+ url: displayUrl,
38
+ product,
39
+ version,
40
+ breadcrumb: rawContent.breadcrumb.length > 0 ? rawContent.breadcrumb : undefined,
41
+ relatedArticles: options.includeRelated === true && rawContent.relatedArticles.length > 0
42
+ ? rawContent.relatedArticles : undefined,
43
+ mapId,
44
+ contentId,
45
+ sections: allSections,
46
+ };
47
+ // ── summaryOnly mode ──
48
+ if (options.summaryOnly === true) {
49
+ const summaryResult = extractSummary(rawContent.content, rawContent.title, maxTokens);
50
+ let summaryContent = `## Summary\n\n${summaryResult.summary}\n\n`;
51
+ summaryContent += `## Article Outline (${summaryResult.outline.length} sections)\n\n`;
52
+ for (const section of summaryResult.outline) {
53
+ const indent = ' '.repeat(Math.max(0, section.level - 1));
54
+ summaryContent += `${indent}- ${section.title} (~${section.tokenCount} tokens)\n`;
55
+ }
56
+ summaryContent += `\n*Estimated read time: ${summaryResult.estimatedReadTime} min`
57
+ + ` (${summaryResult.totalTokens.toLocaleString()} tokens)*\n`;
58
+ return { ...base, content: summaryContent, tokenInfo: summaryResult.tokenInfo };
59
+ }
60
+ // ── Section extraction ──
61
+ if (options.section !== undefined && options.section !== '') {
62
+ const sectionResult = extractSection(rawContent.content, options.section, maxTokens);
63
+ if (sectionResult.section !== null) {
64
+ return { ...base, content: sectionResult.content, tokenInfo: sectionResult.tokenInfo };
65
+ }
66
+ const sectionsList = allSections.map(s => `- ${s.title}`).join('\n');
67
+ const notFoundMsg = `*Section "${options.section}" not found.*\n\n**Available sections:**\n${sectionsList}`;
68
+ return { ...base, content: notFoundMsg, tokenInfo: createTokenInfo(notFoundMsg, maxTokens) };
69
+ }
70
+ // ── Full content with truncation ──
71
+ const truncateResult = truncateToTokenLimit(rawContent.content, maxTokens, allSections);
72
+ return { ...base, content: truncateResult.content, tokenInfo: truncateResult.tokenInfo };
73
+ }
74
+ // ─── Resolve + fetch (shared by get-article & batch-get-articles) ──
75
+ /**
76
+ * Full article retrieval pipeline:
77
+ * 1. Resolve mapId + contentId (skip if already provided)
78
+ * 2. Try provider shortcuts (articleProvider)
79
+ * 3. Fall back to FT API via fetchArticleFromFt
80
+ *
81
+ * Both get-article and batch-get-articles delegate here.
82
+ */
83
+ export async function resolveAndFetchArticle(ctx, input, options) {
84
+ const { topicResolver, cache, articleProvider } = ctx;
85
+ const articleUrl = input.url ?? '';
86
+ // Step 1: Resolve mapId + contentId
87
+ let mapId = input.mapId;
88
+ let contentId = input.contentId;
89
+ if (mapId === undefined || contentId === undefined) {
90
+ const resolved = await topicResolver.resolve({ url: articleUrl });
91
+ ({ mapId, contentId } = resolved);
92
+ }
93
+ // Step 2: Try provider shortcuts (ID-based is primary, URL-based is fallback)
94
+ let article = null;
95
+ if (articleProvider !== undefined) {
96
+ article = await articleProvider.getArticleByIds(mapId, contentId, options);
97
+ }
98
+ if (article === null && articleProvider?.getArticle !== undefined && articleUrl !== '') {
99
+ article = await articleProvider.getArticle(articleUrl, options);
100
+ }
101
+ if (article !== null) {
102
+ return { ...article, mapId, contentId };
103
+ }
104
+ // Step 3: Default — fetch from FT API + parse
105
+ return await fetchArticleFromFt(cache, mapId, contentId, articleUrl, options, ctx.config.cacheTtl.article);
106
+ }
107
+ // ─── Helpers ───────────────────────────────────────────────────
108
+ function deriveDisplayUrl(readerUrl, fallbackUrl) {
109
+ const prettyUrl = readerUrl ?? '';
110
+ return prettyUrl !== '' ? buildDisplayUrl(prettyUrl) : fallbackUrl;
111
+ }
112
+ function extractProductVersion(metadata) {
113
+ const stem = getMetaValue(metadata, FT_META.VERSION_BUNDLE_STEM);
114
+ const product = stem !== '' ? bundleStemToDisplayName(stem) : undefined;
115
+ const version = getMetaValue(metadata, FT_META.VERSION);
116
+ return { product, version: version !== '' ? version : 'current' };
117
+ }
118
+ //# sourceMappingURL=article-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"article-service.js","sourceRoot":"","sources":["../../../src/core/services/article-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Content Parser — HTML → Markdown article structure
3
+ *
4
+ * Handles:
5
+ * - HTML cleaning (remove scripts, fix relative URLs)
6
+ * - HTML → Markdown conversion (Turndown)
7
+ * - Article structure extraction (title, breadcrumb, related articles)
8
+ * - Search snippet cleaning
9
+ */
10
+ import * as cheerio from 'cheerio';
11
+ /**
12
+ * Clean HTML content: remove unwanted elements, fix relative URLs.
13
+ */
14
+ export declare function cleanHtml($: cheerio.CheerioAPI): void;
15
+ export interface ParsedArticleContent {
16
+ title: string;
17
+ content: string;
18
+ breadcrumb: string[];
19
+ relatedArticles: {
20
+ title: string;
21
+ url: string;
22
+ }[];
23
+ }
24
+ /**
25
+ * Parse HTML into article structure (title, markdown content, breadcrumb).
26
+ */
27
+ export declare function parseArticle(html: string, displayUrl: string, options?: {
28
+ includeRelated?: boolean;
29
+ }): ParsedArticleContent;
30
+ /**
31
+ * Clean an HTML search snippet: strip tags, clean breadcrumb prefixes.
32
+ */
33
+ export declare function cleanSnippet(snippet: string, title: string, product: string | null): string;
34
+ /**
35
+ * Convert raw HTML to Markdown using the shared Turndown instance.
36
+ */
37
+ export declare function htmlToMarkdown(html: string): string;
38
+ //# sourceMappingURL=content-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-parser.d.ts","sourceRoot":"","sources":["../../../src/core/services/content-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AAiCnC;;GAEG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,CAAC,UAAU,GAAG,IAAI,CAgBrD;AAID,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,eAAe,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACnD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;IAAE,cAAc,CAAC,EAAE,OAAO,CAAA;CAAE,GACrC,oBAAoB,CAgEtB;AAUD;;GAEG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,GAAG,IAAI,GACrB,MAAM,CAwBR;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD"}
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Content Parser — HTML → Markdown article structure
3
+ *
4
+ * Handles:
5
+ * - HTML cleaning (remove scripts, fix relative URLs)
6
+ * - HTML → Markdown conversion (Turndown)
7
+ * - Article structure extraction (title, breadcrumb, related articles)
8
+ * - Search snippet cleaning
9
+ */
10
+ import * as cheerio from 'cheerio';
11
+ import TurndownService from 'turndown';
12
+ import { DOCS_BASE_URL, SELECTORS } from '../constants.js';
13
+ // ─── Turndown instance ──────────────────────────────────────────
14
+ const turndown = new TurndownService({
15
+ headingStyle: 'atx',
16
+ codeBlockStyle: 'fenced',
17
+ bulletListMarker: '-',
18
+ emDelimiter: '*',
19
+ strongDelimiter: '**',
20
+ });
21
+ turndown.addRule('codeBlocks', {
22
+ filter: 'pre',
23
+ replacement: (content, node) => {
24
+ const nodeElement = node;
25
+ const codeElement = nodeElement.querySelector?.('code');
26
+ const language = codeElement?.className?.replace('language-', '') ?? '';
27
+ return `\n\`\`\`${language}\n${content.trim()}\n\`\`\`\n`;
28
+ },
29
+ });
30
+ turndown.addRule('stripScripts', {
31
+ filter: ['script', 'style', 'noscript'],
32
+ replacement: () => '',
33
+ });
34
+ // ─── HTML cleaning ──────────────────────────────────────────────
35
+ /**
36
+ * Clean HTML content: remove unwanted elements, fix relative URLs.
37
+ */
38
+ export function cleanHtml($) {
39
+ $(SELECTORS.REMOVE).remove();
40
+ $('a[href^="/"]').each((_, el) => {
41
+ const href = $(el).attr('href');
42
+ if (href !== undefined && href !== '') {
43
+ $(el).attr('href', `${DOCS_BASE_URL}${href}`);
44
+ }
45
+ });
46
+ $('img[src^="/"]').each((_, el) => {
47
+ const src = $(el).attr('src');
48
+ if (src !== undefined && src !== '') {
49
+ $(el).attr('src', `${DOCS_BASE_URL}${src}`);
50
+ }
51
+ });
52
+ }
53
+ /**
54
+ * Parse HTML into article structure (title, markdown content, breadcrumb).
55
+ */
56
+ export function parseArticle(html, displayUrl, options) {
57
+ const $ = cheerio.load(html);
58
+ cleanHtml($);
59
+ // Extract content — try FT API selectors first (most common path),
60
+ // then fall back to generic page selectors.
61
+ // 1. FT API returns HTML fragments wrapped in <div class="content-locale-...">
62
+ let contentHtml = $('div[class*="content-locale"]').first().html() ?? '';
63
+ // 2. Common FT body wrappers (taskbody, conbody, refbody, etc.)
64
+ if (contentHtml === '') {
65
+ contentHtml = $('[class*="body"]').first().html() ?? '';
66
+ }
67
+ // 3. Standard selectors for full HTML pages (article, .article-content, etc.)
68
+ if (contentHtml === '') {
69
+ contentHtml = $(SELECTORS.CONTENT).html() ?? '';
70
+ }
71
+ // 4. Fallback: inner HTML of <body> (cheerio wraps fragments in <html><body>)
72
+ if (contentHtml === '') {
73
+ contentHtml = $('body').html() ?? '';
74
+ }
75
+ // 5. Last resort: use raw HTML as-is
76
+ if (contentHtml === '') {
77
+ contentHtml = html;
78
+ }
79
+ const extractedTitle = $(SELECTORS.TITLE).first().text().trim();
80
+ const title = extractedTitle !== '' ? extractedTitle : 'Untitled';
81
+ // Convert to Markdown and strip Turndown anchor artifacts from headings
82
+ const content = turndown.turndown(contentHtml)
83
+ .replace(/^(#{1,6}\s+)\[([^\]]*)\]\(#[^)]*\)/gm, '$1$2');
84
+ // Extract breadcrumb
85
+ const breadcrumb = $(SELECTORS.BREADCRUMB)
86
+ .map((_, el) => $(el).text().trim())
87
+ .get()
88
+ .filter(Boolean);
89
+ // Extract related articles
90
+ const relatedArticles = options?.includeRelated === true
91
+ ? $(SELECTORS.RELATED).map((_, el) => {
92
+ const rawHref = $(el).attr('href') ?? '';
93
+ if (rawHref === '' || rawHref.startsWith('#')) {
94
+ return { title: '', url: '' };
95
+ }
96
+ let resolvedUrl;
97
+ try {
98
+ resolvedUrl = new URL(rawHref, displayUrl).toString();
99
+ }
100
+ catch {
101
+ resolvedUrl = rawHref;
102
+ }
103
+ return {
104
+ title: $(el).text().trim(),
105
+ url: resolvedUrl,
106
+ };
107
+ }).get().filter(r => r.title !== '' && r.url !== '')
108
+ : [];
109
+ return { title, content, breadcrumb, relatedArticles };
110
+ }
111
+ // ─── Snippet cleaning ───────────────────────────────────────────
112
+ const MIN_SNIPPET_LENGTH = 50;
113
+ const NAV_PATTERNS = [
114
+ /^Home\s*>/i,
115
+ /^[\w\s]+>\s*[\w\s]+>\s*[\w\s]+/,
116
+ ];
117
+ /**
118
+ * Clean an HTML search snippet: strip tags, clean breadcrumb prefixes.
119
+ */
120
+ export function cleanSnippet(snippet, title, product) {
121
+ // Strip HTML tags — loop until stable to handle nested/malformed fragments
122
+ let cleaned = snippet;
123
+ let prev;
124
+ do {
125
+ prev = cleaned;
126
+ cleaned = cleaned.replace(/<[^>]*>?/g, '').trim();
127
+ } while (cleaned !== prev);
128
+ for (const pattern of NAV_PATTERNS) {
129
+ cleaned = cleaned.replace(pattern, '').trim();
130
+ }
131
+ // As an extra safety step, strip any remaining angle brackets to avoid
132
+ // residual fragments like "<script" from being interpreted as HTML.
133
+ cleaned = cleaned.replace(/[<>]/g, '').trim();
134
+ if (cleaned.length < MIN_SNIPPET_LENGTH) {
135
+ const productSuffix = product !== null && product !== '' ? ` \u2014 ${product}` : '';
136
+ return `${title}${productSuffix}`;
137
+ }
138
+ return cleaned;
139
+ }
140
+ /**
141
+ * Convert raw HTML to Markdown using the shared Turndown instance.
142
+ */
143
+ export function htmlToMarkdown(html) {
144
+ return turndown.turndown(html);
145
+ }
146
+ //# sourceMappingURL=content-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-parser.js","sourceRoot":"","sources":["../../../src/core/services/content-parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Fluid Topics API client
3
+ *
4
+ * Pure HTTP client for the Fluid Topics platform on learn.jamf.com.
5
+ * Stateless — no caching, no business logic, no provider fallback.
6
+ * All functions map 1:1 to FT REST API endpoints.
7
+ */
8
+ import type { FtClusteredSearchResponse, FtSearchRequest, FtMapInfo, FtTocNode, FtTopicInfo } from '../types.js';
9
+ /**
10
+ * POST /api/khub/clustered-search
11
+ *
12
+ * Primary search endpoint. Returns clusters of results grouped by topic,
13
+ * each cluster may contain multiple version entries.
14
+ */
15
+ export declare function search(request: FtSearchRequest): Promise<FtClusteredSearchResponse>;
16
+ /**
17
+ * GET /api/khub/maps
18
+ *
19
+ * Returns all publications (~577 maps across all locales and versions).
20
+ * Each map includes metadata with version_bundle_stem, version,
21
+ * latestVersion, ft:locale, etc.
22
+ */
23
+ export declare function fetchMaps(): Promise<FtMapInfo[]>;
24
+ /**
25
+ * GET /api/khub/maps/{mapId}/toc
26
+ *
27
+ * Returns the full table of contents as a JSON tree.
28
+ * The response is either a single root node or an array of root nodes.
29
+ */
30
+ export declare function fetchMapToc(mapId: string): Promise<FtTocNode[]>;
31
+ /**
32
+ * GET /api/khub/maps/{mapId}/topics
33
+ *
34
+ * Returns a flat list of all topics in a map.
35
+ * Richer than TOC: includes readerUrl, breadcrumb, and full metadata.
36
+ */
37
+ export declare function fetchMapTopics(mapId: string): Promise<FtTopicInfo[]>;
38
+ /**
39
+ * GET /api/khub/maps/{mapId}/topics/{contentId}/content
40
+ *
41
+ * Returns the HTML content of a single topic.
42
+ * Always returns text/html regardless of Accept header.
43
+ */
44
+ export declare function fetchTopicContent(mapId: string, contentId: string): Promise<string>;
45
+ /**
46
+ * GET /api/khub/maps/{mapId}/topics/{contentId}
47
+ *
48
+ * Returns topic metadata including title, contentApiEndpoint,
49
+ * and all metadata key-value pairs.
50
+ */
51
+ export declare function fetchTopicMetadata(mapId: string, contentId: string): Promise<FtTopicInfo>;
52
+ //# sourceMappingURL=ft-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ft-client.d.ts","sourceRoot":"","sources":["../../../src/core/services/ft-client.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EACV,yBAAyB,EACzB,eAAe,EACf,SAAS,EACT,SAAS,EACT,WAAW,EACZ,MAAM,aAAa,CAAC;AAgBrB;;;;;GAKG;AACH,wBAAsB,MAAM,CAC1B,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,yBAAyB,CAAC,CAGpC;AAID;;;;;;GAMG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC,CAGtD;AAID;;;;;GAKG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,SAAS,EAAE,CAAC,CAItB;AAID;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,WAAW,EAAE,CAAC,CAGxB;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CAGjB;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,WAAW,CAAC,CAGtB"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Fluid Topics API client
3
+ *
4
+ * Pure HTTP client for the Fluid Topics platform on learn.jamf.com.
5
+ * Stateless — no caching, no business logic, no provider fallback.
6
+ * All functions map 1:1 to FT REST API endpoints.
7
+ */
8
+ import { httpGetJson, httpGetText, httpPostJson } from '../http-client.js';
9
+ import { FT_API_BASE } from '../constants.js';
10
+ // ─── URL helpers ───────────────────────────────────────────────
11
+ /**
12
+ * Build a /api/khub/maps/{mapId}/... URL with proper encoding.
13
+ * All path segments are encoded via encodeURIComponent.
14
+ */
15
+ function mapsUrl(mapId, ...segments) {
16
+ const base = `${FT_API_BASE}/api/khub/maps/${encodeURIComponent(mapId)}`;
17
+ if (segments.length === 0)
18
+ return base;
19
+ return `${base}/${segments.map(encodeURIComponent).join('/')}`;
20
+ }
21
+ // ─── Search ─────────────────────────────────────────────────────
22
+ /**
23
+ * POST /api/khub/clustered-search
24
+ *
25
+ * Primary search endpoint. Returns clusters of results grouped by topic,
26
+ * each cluster may contain multiple version entries.
27
+ */
28
+ export async function search(request) {
29
+ const url = `${FT_API_BASE}/api/khub/clustered-search`;
30
+ return await httpPostJson(url, request);
31
+ }
32
+ // ─── Maps (Publications) ────────────────────────────────────────
33
+ /**
34
+ * GET /api/khub/maps
35
+ *
36
+ * Returns all publications (~577 maps across all locales and versions).
37
+ * Each map includes metadata with version_bundle_stem, version,
38
+ * latestVersion, ft:locale, etc.
39
+ */
40
+ export async function fetchMaps() {
41
+ const url = `${FT_API_BASE}/api/khub/maps`;
42
+ return await httpGetJson(url);
43
+ }
44
+ // ─── TOC ────────────────────────────────────────────────────────
45
+ /**
46
+ * GET /api/khub/maps/{mapId}/toc
47
+ *
48
+ * Returns the full table of contents as a JSON tree.
49
+ * The response is either a single root node or an array of root nodes.
50
+ */
51
+ export async function fetchMapToc(mapId) {
52
+ const url = mapsUrl(mapId, 'toc');
53
+ const raw = await httpGetJson(url);
54
+ return Array.isArray(raw) ? raw : [raw];
55
+ }
56
+ // ─── Topics ─────────────────────────────────────────────────────
57
+ /**
58
+ * GET /api/khub/maps/{mapId}/topics
59
+ *
60
+ * Returns a flat list of all topics in a map.
61
+ * Richer than TOC: includes readerUrl, breadcrumb, and full metadata.
62
+ */
63
+ export async function fetchMapTopics(mapId) {
64
+ const url = mapsUrl(mapId, 'topics');
65
+ return await httpGetJson(url);
66
+ }
67
+ /**
68
+ * GET /api/khub/maps/{mapId}/topics/{contentId}/content
69
+ *
70
+ * Returns the HTML content of a single topic.
71
+ * Always returns text/html regardless of Accept header.
72
+ */
73
+ export async function fetchTopicContent(mapId, contentId) {
74
+ const url = mapsUrl(mapId, 'topics', contentId, 'content');
75
+ return await httpGetText(url);
76
+ }
77
+ /**
78
+ * GET /api/khub/maps/{mapId}/topics/{contentId}
79
+ *
80
+ * Returns topic metadata including title, contentApiEndpoint,
81
+ * and all metadata key-value pairs.
82
+ */
83
+ export async function fetchTopicMetadata(mapId, contentId) {
84
+ const url = mapsUrl(mapId, 'topics', contentId);
85
+ return await httpGetJson(url);
86
+ }
87
+ //# sourceMappingURL=ft-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ft-client.js","sourceRoot":"","sources":["../../../src/core/services/ft-client.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC3E,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAS9C,kEAAkE;AAElE;;;GAGG;AACH,SAAS,OAAO,CAAC,KAAa,EAAE,GAAG,QAAkB;IACnD,MAAM,IAAI,GAAG,GAAG,WAAW,kBAAkB,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC;IACzE,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACvC,OAAO,GAAG,IAAI,IAAI,QAAQ,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;AACjE,CAAC;AAED,mEAAmE;AAEnE;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,OAAwB;IAExB,MAAM,GAAG,GAAG,GAAG,WAAW,4BAA4B,CAAC;IACvD,OAAO,MAAM,YAAY,CAA4B,GAAG,EAAE,OAAO,CAAC,CAAC;AACrE,CAAC;AAED,mEAAmE;AAEnE;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS;IAC7B,MAAM,GAAG,GAAG,GAAG,WAAW,gBAAgB,CAAC;IAC3C,OAAO,MAAM,WAAW,CAAc,GAAG,CAAC,CAAC;AAC7C,CAAC;AAED,mEAAmE;AAEnE;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,KAAa;IAEb,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAClC,MAAM,GAAG,GAAG,MAAM,WAAW,CAA0B,GAAG,CAAC,CAAC;IAC5D,OAAO,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;AAC1C,CAAC;AAED,mEAAmE;AAEnE;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,KAAa;IAEb,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACrC,OAAO,MAAM,WAAW,CAAgB,GAAG,CAAC,CAAC;AAC/C,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,KAAa,EACb,SAAiB;IAEjB,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;IAC3D,OAAO,MAAM,WAAW,CAAC,GAAG,CAAC,CAAC;AAChC,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAAa,EACb,SAAiB;IAEjB,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;IAChD,OAAO,MAAM,WAAW,CAAc,GAAG,CAAC,CAAC;AAC7C,CAAC"}
@@ -1,8 +1,11 @@
1
1
  /**
2
2
  * Glossary service for parsing and looking up Jamf documentation glossary terms
3
3
  *
4
- * Jamf's glossary uses DITA glossentry format where each term is a separate page:
5
- * <h1 class="glossterm">Term Name</h1>
4
+ * Uses Fluid Topics API on learn.jamf.com:
5
+ * - GET /api/khub/maps/{mapId}/toc — list all glossary terms
6
+ * - GET /api/khub/maps/{mapId}/topics/{contentId}/content — term HTML
7
+ *
8
+ * Jamf's glossary uses DITA glossentry format where each term is a separate topic:
6
9
  * <div class="glossdef"><p>Definition text</p></div>
7
10
  *
8
11
  * Also supports fallback formats:
@@ -33,9 +36,10 @@ export declare function searchGlossaryEntries(entries: GlossaryEntry[], term: st
33
36
  * Look up a glossary term across Jamf documentation.
34
37
  *
35
38
  * Strategy:
36
- * 1. Search via Zoomin API with docType=glossary to find glossary pages
37
- * 2. Fetch and parse each glossary page (with caching)
38
- * 3. Use fuse.js to rank matching terms
39
+ * 1. Fetch glossary TOC from Fluid Topics API (cached)
40
+ * 2. Fuzzy-match term against TOC titles
41
+ * 3. Fetch and parse matching glossary topics
42
+ * 4. Rank results with fuse.js and apply token limit
39
43
  */
40
44
  export declare function lookupGlossaryTerm(ctx: ServerContext, params: {
41
45
  term: string;
@@ -1 +1 @@
1
- {"version":3,"file":"glossary.d.ts","sourceRoot":"","sources":["../../../src/core/services/glossary.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,QAAQ,EACd,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EACV,aAAa,EACb,oBAAoB,EAGrB,MAAM,aAAa,CAAC;AAOrB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAUzD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,aAAa,EAAE,CA0BvG;AAsID;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,aAAa,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,aAAa,EAAE,CAuB7F;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,aAAa,EAClB,MAAM,EAAE;IACN,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAChC,QAAQ,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,GACA,OAAO,CAAC,oBAAoB,CAAC,CAgG/B"}
1
+ {"version":3,"file":"glossary.d.ts","sourceRoot":"","sources":["../../../src/core/services/glossary.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAKH,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,QAAQ,EACd,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EACV,aAAa,EACb,oBAAoB,EAErB,MAAM,aAAa,CAAC;AAIrB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AA4EzD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,MAAM,GACf,aAAa,EAAE,CAyBjB;AAiND;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,aAAa,EAAE,EACxB,IAAI,EAAE,MAAM,GACX,aAAa,EAAE,CAsBjB;AAID;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,aAAa,EAClB,MAAM,EAAE;IACN,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAChC,QAAQ,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,GACA,OAAO,CAAC,oBAAoB,CAAC,CAqI/B"}