@get-technology-inc/jamf-docs-mcp-server 0.0.1 → 1.1.0

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 (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +111 -29
  3. package/dist/constants.d.ts +263 -0
  4. package/dist/constants.d.ts.map +1 -0
  5. package/dist/constants.js +308 -0
  6. package/dist/constants.js.map +1 -0
  7. package/dist/index.d.ts +9 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +40 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/resources/index.d.ts +13 -0
  12. package/dist/resources/index.d.ts.map +1 -0
  13. package/dist/resources/index.js +45 -0
  14. package/dist/resources/index.js.map +1 -0
  15. package/dist/schemas/index.d.ts +61 -0
  16. package/dist/schemas/index.d.ts.map +1 -0
  17. package/dist/schemas/index.js +127 -0
  18. package/dist/schemas/index.js.map +1 -0
  19. package/dist/services/cache.d.ts +47 -0
  20. package/dist/services/cache.d.ts.map +1 -0
  21. package/dist/services/cache.js +165 -0
  22. package/dist/services/cache.js.map +1 -0
  23. package/dist/services/metadata.d.ts +78 -0
  24. package/dist/services/metadata.d.ts.map +1 -0
  25. package/dist/services/metadata.js +349 -0
  26. package/dist/services/metadata.js.map +1 -0
  27. package/dist/services/scraper.d.ts +62 -0
  28. package/dist/services/scraper.d.ts.map +1 -0
  29. package/dist/services/scraper.js +544 -0
  30. package/dist/services/scraper.js.map +1 -0
  31. package/dist/services/search-suggestions.d.ts +27 -0
  32. package/dist/services/search-suggestions.d.ts.map +1 -0
  33. package/dist/services/search-suggestions.js +193 -0
  34. package/dist/services/search-suggestions.js.map +1 -0
  35. package/dist/services/tokenizer.d.ts +93 -0
  36. package/dist/services/tokenizer.d.ts.map +1 -0
  37. package/dist/services/tokenizer.js +330 -0
  38. package/dist/services/tokenizer.js.map +1 -0
  39. package/dist/tools/get-article.d.ts +7 -0
  40. package/dist/tools/get-article.d.ts.map +1 -0
  41. package/dist/tools/get-article.js +244 -0
  42. package/dist/tools/get-article.js.map +1 -0
  43. package/dist/tools/get-toc.d.ts +7 -0
  44. package/dist/tools/get-toc.d.ts.map +1 -0
  45. package/dist/tools/get-toc.js +202 -0
  46. package/dist/tools/get-toc.js.map +1 -0
  47. package/dist/tools/list-products.d.ts +7 -0
  48. package/dist/tools/list-products.d.ts.map +1 -0
  49. package/dist/tools/list-products.js +150 -0
  50. package/dist/tools/list-products.js.map +1 -0
  51. package/dist/tools/search.d.ts +7 -0
  52. package/dist/tools/search.d.ts.map +1 -0
  53. package/dist/tools/search.js +252 -0
  54. package/dist/tools/search.js.map +1 -0
  55. package/dist/types.d.ts +188 -0
  56. package/dist/types.d.ts.map +1 -0
  57. package/dist/types.js +28 -0
  58. package/dist/types.js.map +1 -0
  59. package/package.json +68 -6
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../src/services/metadata.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Web scraping service for Jamf documentation
3
+ *
4
+ * This module handles fetching and parsing HTML from learn.jamf.com
5
+ * (Jamf documentation has moved from docs.jamf.com to learn.jamf.com)
6
+ */
7
+ import { type ProductId } from '../constants.js';
8
+ import { type SearchResult, type ParsedArticle, type TocEntry, type SearchParams, type TokenInfo, type PaginationInfo, type ArticleSection } from '../types.js';
9
+ /**
10
+ * Search response with token and pagination info
11
+ */
12
+ export interface SearchDocumentationResult {
13
+ results: SearchResult[];
14
+ pagination: PaginationInfo;
15
+ tokenInfo: TokenInfo;
16
+ }
17
+ /**
18
+ * Search Jamf documentation using Zoomin Search API
19
+ */
20
+ export declare function searchDocumentation(params: SearchParams): Promise<SearchDocumentationResult>;
21
+ /**
22
+ * Options for fetching articles
23
+ */
24
+ export interface FetchArticleOptions {
25
+ includeRelated?: boolean;
26
+ section?: string;
27
+ summaryOnly?: boolean;
28
+ maxTokens?: number;
29
+ }
30
+ /**
31
+ * Article result with token and section info
32
+ */
33
+ export interface FetchArticleResult extends ParsedArticle {
34
+ tokenInfo: TokenInfo;
35
+ sections: ArticleSection[];
36
+ }
37
+ /**
38
+ * Fetch and parse a documentation article
39
+ * Uses backend URL (learn-be.jamf.com) for pre-rendered content
40
+ */
41
+ export declare function fetchArticle(url: string, options?: FetchArticleOptions): Promise<FetchArticleResult>;
42
+ /**
43
+ * Options for fetching table of contents
44
+ */
45
+ export interface FetchTocOptions {
46
+ page?: number;
47
+ maxTokens?: number;
48
+ }
49
+ /**
50
+ * TOC result with pagination and token info
51
+ */
52
+ export interface FetchTocResult {
53
+ toc: TocEntry[];
54
+ pagination: PaginationInfo;
55
+ tokenInfo: TokenInfo;
56
+ }
57
+ /**
58
+ * Fetch table of contents for a product
59
+ * Uses backend TOC endpoint (learn-be.jamf.com/bundle/{bundleId}/toc)
60
+ */
61
+ export declare function fetchTableOfContents(product: ProductId, version?: string, options?: FetchTocOptions): Promise<FetchTocResult>;
62
+ //# sourceMappingURL=scraper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scraper.d.ts","sourceRoot":"","sources":["../../src/services/scraper.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,EAUL,KAAK,SAAS,EAEf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,QAAQ,EACb,KAAK,YAAY,EAEjB,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,KAAK,cAAc,EACpB,MAAM,aAAa,CAAC;AA+LrB;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,UAAU,EAAE,cAAc,CAAC;IAC3B,SAAS,EAAE,SAAS,CAAC;CACtB;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAiJlG;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAmB,SAAQ,aAAa;IACvD,SAAS,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,cAAc,EAAE,CAAC;CAC5B;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,kBAAkB,CAAC,CAmH7B;AAoFD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,QAAQ,EAAE,CAAC;IAChB,UAAU,EAAE,cAAc,CAAC;IAC3B,SAAS,EAAE,SAAS,CAAC;CACtB;AAkBD;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,SAAS,EAClB,OAAO,SAAY,EACnB,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,cAAc,CAAC,CA+FzB"}
@@ -0,0 +1,544 @@
1
+ /**
2
+ * Web scraping service for Jamf documentation
3
+ *
4
+ * This module handles fetching and parsing HTML from learn.jamf.com
5
+ * (Jamf documentation has moved from docs.jamf.com to learn.jamf.com)
6
+ */
7
+ import axios from 'axios';
8
+ import * as cheerio from 'cheerio';
9
+ import TurndownService from 'turndown';
10
+ import { DOCS_BASE_URL, DOCS_API_URL, JAMF_PRODUCTS, JAMF_TOPICS, REQUEST_CONFIG, SELECTORS, CONTENT_LIMITS, TOKEN_CONFIG, PAGINATION_CONFIG } from '../constants.js';
11
+ import { JamfDocsError, JamfDocsErrorCode } from '../types.js';
12
+ import { cache } from './cache.js';
13
+ import { estimateTokens, createTokenInfo, extractSections, extractSection, extractSummary, truncateToTokenLimit, calculatePagination } from './tokenizer.js';
14
+ import { getBundleIdForVersion } from './metadata.js';
15
+ // Initialize Turndown for HTML to Markdown conversion
16
+ const turndown = new TurndownService({
17
+ headingStyle: 'atx',
18
+ codeBlockStyle: 'fenced',
19
+ bulletListMarker: '-',
20
+ emDelimiter: '*',
21
+ strongDelimiter: '**'
22
+ });
23
+ // Custom Turndown rules
24
+ turndown.addRule('codeBlocks', {
25
+ filter: 'pre',
26
+ replacement: (content, node) => {
27
+ // Handle code element extraction safely
28
+ const nodeElement = node;
29
+ const codeElement = nodeElement.querySelector?.('code');
30
+ const language = codeElement?.className?.replace('language-', '') ?? '';
31
+ return `\n\`\`\`${language}\n${content.trim()}\n\`\`\`\n`;
32
+ }
33
+ });
34
+ // Rate limiter
35
+ let lastRequestTime = 0;
36
+ async function throttle() {
37
+ const now = Date.now();
38
+ const elapsed = now - lastRequestTime;
39
+ if (elapsed < REQUEST_CONFIG.RATE_LIMIT_DELAY) {
40
+ await new Promise(resolve => setTimeout(resolve, REQUEST_CONFIG.RATE_LIMIT_DELAY - elapsed));
41
+ }
42
+ lastRequestTime = Date.now();
43
+ }
44
+ // URL transformation between frontend (learn.jamf.com) and backend (learn-be.jamf.com)
45
+ const transformToBackendUrl = (url) => url.replace('learn.jamf.com', 'learn-be.jamf.com');
46
+ const transformToFrontendUrl = (url) => url.replace('learn-be.jamf.com', 'learn.jamf.com');
47
+ // HTML entity map for stripping HTML
48
+ const HTML_ENTITIES = {
49
+ '&nbsp;': ' ', '&amp;': '&', '&lt;': '<', '&gt;': '>', '&quot;': '"', '&#39;': "'"
50
+ };
51
+ /**
52
+ * Strip HTML tags from a string
53
+ * Uses iterative approach to handle nested/malformed tags
54
+ */
55
+ function stripHtml(html) {
56
+ let text = html;
57
+ // Iteratively remove HTML tags until none remain (handles nested cases like <scr<script>ipt>)
58
+ let prev = '';
59
+ while (prev !== text) {
60
+ prev = text;
61
+ text = text.replace(/<[^>]*>/g, '');
62
+ }
63
+ for (const [entity, char] of Object.entries(HTML_ENTITIES)) {
64
+ text = text.replaceAll(entity, char);
65
+ }
66
+ return text.replace(/\s+/g, ' ').trim();
67
+ }
68
+ /**
69
+ * Handle axios errors and convert to JamfDocsError
70
+ */
71
+ function handleAxiosError(error, url, resourceType) {
72
+ const status = error.response?.status;
73
+ if (status === 404) {
74
+ throw new JamfDocsError(`${resourceType} not found: ${url}`, JamfDocsErrorCode.NOT_FOUND, url, 404);
75
+ }
76
+ if (status === 429) {
77
+ throw new JamfDocsError('Rate limited. Please wait and try again.', JamfDocsErrorCode.RATE_LIMITED, url, 429);
78
+ }
79
+ if (error.code === 'ECONNABORTED') {
80
+ throw new JamfDocsError('Request timed out', JamfDocsErrorCode.TIMEOUT, url);
81
+ }
82
+ throw new JamfDocsError(`Network error: ${error.message}`, JamfDocsErrorCode.NETWORK_ERROR, url);
83
+ }
84
+ /**
85
+ * Fetch data from a URL with error handling
86
+ */
87
+ async function fetchUrl(url, accept, resourceType) {
88
+ await throttle();
89
+ try {
90
+ const response = await axios.get(url, {
91
+ timeout: REQUEST_CONFIG.TIMEOUT,
92
+ headers: {
93
+ 'User-Agent': REQUEST_CONFIG.USER_AGENT,
94
+ 'Accept': accept,
95
+ 'Accept-Language': 'en-US,en;q=0.9'
96
+ }
97
+ });
98
+ return response.data;
99
+ }
100
+ catch (error) {
101
+ if (axios.isAxiosError(error)) {
102
+ handleAxiosError(error, url, resourceType);
103
+ }
104
+ throw error;
105
+ }
106
+ }
107
+ const fetchJson = async (url) => await fetchUrl(url, 'application/json', 'Resource');
108
+ const fetchHtml = async (url) => await fetchUrl(url, 'text/html,application/xhtml+xml', 'Article');
109
+ /**
110
+ * Clean HTML content by removing unwanted elements
111
+ */
112
+ function cleanHtml($) {
113
+ // Remove unwanted elements
114
+ $(SELECTORS.REMOVE).remove();
115
+ // Fix relative URLs
116
+ $('a[href^="/"]').each((_, el) => {
117
+ const href = $(el).attr('href');
118
+ if (href !== undefined && href !== '') {
119
+ $(el).attr('href', `${DOCS_BASE_URL}${href}`);
120
+ }
121
+ });
122
+ $('img[src^="/"]').each((_, el) => {
123
+ const src = $(el).attr('src');
124
+ if (src !== undefined && src !== '') {
125
+ $(el).attr('src', `${DOCS_BASE_URL}${src}`);
126
+ }
127
+ });
128
+ }
129
+ /**
130
+ * Extract product and version from URL
131
+ * Supports both old docs.jamf.com and new learn.jamf.com URL structures
132
+ */
133
+ function extractProductInfo(url) {
134
+ // learn.jamf.com structure: /en-US/bundle/{product}-documentation/page/{page}.html
135
+ const bundleMatch = /\/bundle\/([^/]+)-documentation\//.exec(url);
136
+ if (bundleMatch !== null) {
137
+ const product = Object.values(JAMF_PRODUCTS).find(p => p.bundleId.includes(bundleMatch[1] ?? ''));
138
+ return { product: product?.name, version: 'current' };
139
+ }
140
+ // Legacy docs.jamf.com structure: /{version}/{product}/...
141
+ const pathParts = new URL(url).pathname.split('/').filter(Boolean);
142
+ const versionMatch = pathParts[0]?.match(/^\d+\.\d+(\.\d+)?$/);
143
+ if (versionMatch !== null && versionMatch !== undefined) {
144
+ const product = Object.values(JAMF_PRODUCTS).find(p => p.urlPattern.includes(pathParts[1] ?? ''));
145
+ return { product: product?.name, version: pathParts[0] };
146
+ }
147
+ // Check for unversioned paths
148
+ const matchedProduct = Object.values(JAMF_PRODUCTS).find(p => url.includes(p.bundleId) || url.includes(p.id));
149
+ return { product: matchedProduct?.name, version: matchedProduct !== undefined ? 'current' : undefined };
150
+ }
151
+ /**
152
+ * Check if a search result matches a topic based on keywords
153
+ */
154
+ function matchesTopic(result, topicId) {
155
+ const topic = JAMF_TOPICS[topicId];
156
+ const searchText = `${result.title} ${result.snippet}`.toLowerCase();
157
+ return topic.keywords.some(keyword => searchText.includes(keyword.toLowerCase()));
158
+ }
159
+ /**
160
+ * Search Jamf documentation using Zoomin Search API
161
+ */
162
+ export async function searchDocumentation(params) {
163
+ const page = params.page ?? PAGINATION_CONFIG.DEFAULT_PAGE;
164
+ const pageSize = params.limit ?? CONTENT_LIMITS.DEFAULT_SEARCH_RESULTS;
165
+ const maxTokens = params.maxTokens ?? TOKEN_CONFIG.DEFAULT_MAX_TOKENS;
166
+ const cacheKey = `search:${JSON.stringify({ ...params, page: 1 })}`; // Cache without page for full results
167
+ // Check cache for full results
168
+ let allResults = await cache.get(cacheKey);
169
+ if (allResults === null) {
170
+ // Fetch more results to allow for filtering
171
+ const fetchLimit = CONTENT_LIMITS.MAX_SEARCH_RESULTS;
172
+ // Build API URL with query params
173
+ const apiUrl = new URL(`${DOCS_API_URL}/api/search`);
174
+ apiUrl.searchParams.set('q', params.query);
175
+ apiUrl.searchParams.set('rpp', fetchLimit.toString());
176
+ console.error(`[SEARCH] Query: "${params.query}", Product: ${params.product ?? 'all'}, Topic: ${params.topic ?? 'all'}, URL: ${apiUrl.toString()}`);
177
+ try {
178
+ const response = await fetchJson(apiUrl.toString());
179
+ // Transform Zoomin results to SearchResult format with metadata
180
+ allResults = response.Results
181
+ .filter((wrapper) => wrapper.leading_result !== null && wrapper.leading_result !== undefined)
182
+ .map(wrapper => {
183
+ const result = wrapper.leading_result;
184
+ // Extract product from bundle_id
185
+ const bundleId = result.bundle_id;
186
+ const bundleMatch = /^(jamf-[a-z]+)-documentation/.exec(bundleId);
187
+ const bundleSlug = bundleMatch?.[1] ?? null;
188
+ const productEntry = bundleSlug !== null
189
+ ? Object.entries(JAMF_PRODUCTS).find(([id]) => id === bundleSlug)
190
+ : null;
191
+ const searchResult = {
192
+ title: result.title !== '' ? result.title : 'Untitled',
193
+ url: transformToFrontendUrl(result.url !== '' ? result.url : ''),
194
+ snippet: stripHtml(result.snippet !== '' ? result.snippet : '').slice(0, CONTENT_LIMITS.MAX_SNIPPET_LENGTH),
195
+ product: productEntry !== null && productEntry !== undefined ? productEntry[1].name : (result.publication_title !== '' ? result.publication_title : 'Jamf'),
196
+ version: 'current'
197
+ };
198
+ if (result.score !== undefined) {
199
+ searchResult.relevance = result.score;
200
+ }
201
+ // Find matched topics
202
+ const matchedTopics = Object.keys(JAMF_TOPICS)
203
+ .filter(topicId => matchesTopic(searchResult, topicId));
204
+ return { result: searchResult, bundleSlug, matchedTopics };
205
+ });
206
+ // Cache full results
207
+ await cache.set(cacheKey, allResults);
208
+ }
209
+ catch (error) {
210
+ console.error('[SEARCH] Error:', error);
211
+ if (error instanceof JamfDocsError) {
212
+ throw error;
213
+ }
214
+ // Return empty results on error
215
+ return {
216
+ results: [],
217
+ pagination: {
218
+ page: 1,
219
+ pageSize,
220
+ totalPages: 0,
221
+ totalItems: 0,
222
+ hasNext: false,
223
+ hasPrev: false
224
+ },
225
+ tokenInfo: createTokenInfo('', maxTokens)
226
+ };
227
+ }
228
+ }
229
+ // Apply filters
230
+ let filteredResults = allResults;
231
+ // Product filter
232
+ if (params.product !== undefined) {
233
+ filteredResults = filteredResults.filter(r => r.bundleSlug === params.product);
234
+ }
235
+ // Topic filter
236
+ if (params.topic !== undefined) {
237
+ const topicFilter = params.topic;
238
+ filteredResults = filteredResults.filter(r => r.matchedTopics.includes(topicFilter));
239
+ }
240
+ // Calculate pagination
241
+ const paginationInfo = calculatePagination(filteredResults.length, page, pageSize);
242
+ // Get paginated results
243
+ const paginatedResults = filteredResults
244
+ .slice(paginationInfo.startIndex, paginationInfo.endIndex)
245
+ .map(r => r.result);
246
+ // Calculate token info
247
+ const resultText = paginatedResults.map(r => `${r.title}\n${r.snippet}\n${r.url}`).join('\n\n');
248
+ const tokenCount = estimateTokens(resultText);
249
+ // Check if we need to truncate
250
+ let finalResults = paginatedResults;
251
+ let truncated = false;
252
+ if (tokenCount > maxTokens) {
253
+ // Truncate results to fit token limit
254
+ let runningTokens = 0;
255
+ finalResults = [];
256
+ for (const result of paginatedResults) {
257
+ const resultTokens = estimateTokens(`${result.title}\n${result.snippet}\n${result.url}`);
258
+ if (runningTokens + resultTokens > maxTokens) {
259
+ truncated = true;
260
+ break;
261
+ }
262
+ finalResults.push(result);
263
+ runningTokens += resultTokens;
264
+ }
265
+ }
266
+ return {
267
+ results: finalResults,
268
+ pagination: {
269
+ page: paginationInfo.page,
270
+ pageSize: paginationInfo.pageSize,
271
+ totalPages: paginationInfo.totalPages,
272
+ totalItems: filteredResults.length,
273
+ hasNext: paginationInfo.hasNext || truncated,
274
+ hasPrev: paginationInfo.hasPrev
275
+ },
276
+ tokenInfo: {
277
+ tokenCount: estimateTokens(finalResults.map(r => `${r.title}\n${r.snippet}\n${r.url}`).join('\n\n')),
278
+ truncated,
279
+ maxTokens
280
+ }
281
+ };
282
+ }
283
+ /**
284
+ * Fetch and parse a documentation article
285
+ * Uses backend URL (learn-be.jamf.com) for pre-rendered content
286
+ */
287
+ export async function fetchArticle(url, options = {}) {
288
+ const maxTokens = options.maxTokens ?? TOKEN_CONFIG.DEFAULT_MAX_TOKENS;
289
+ // Store original URL for display, use backend URL for fetching
290
+ const displayUrl = transformToFrontendUrl(url);
291
+ const articleFetchUrl = transformToBackendUrl(url);
292
+ const cacheKey = `article:${displayUrl}`;
293
+ // Check cache for raw article (without section/token processing)
294
+ let rawArticle = await cache.get(cacheKey);
295
+ if (rawArticle === null) {
296
+ // Fetch HTML from backend URL (pre-rendered content, not SPA shell)
297
+ const html = await fetchHtml(articleFetchUrl);
298
+ const $ = cheerio.load(html);
299
+ // Clean content
300
+ cleanHtml($);
301
+ // Extract content
302
+ const contentHtml = $(SELECTORS.CONTENT).html() ?? '';
303
+ const extractedTitle = $(SELECTORS.TITLE).first().text().trim();
304
+ const title = extractedTitle !== '' ? extractedTitle : 'Untitled';
305
+ // Convert to Markdown
306
+ const content = turndown.turndown(contentHtml);
307
+ // Extract breadcrumb
308
+ const breadcrumb = $(SELECTORS.BREADCRUMB)
309
+ .map((_, el) => $(el).text().trim())
310
+ .get()
311
+ .filter(Boolean);
312
+ // Extract related articles
313
+ const relatedArticles = options.includeRelated === true
314
+ ? $(SELECTORS.RELATED).map((_, el) => ({
315
+ title: $(el).text().trim(),
316
+ url: $(el).attr('href') ?? ''
317
+ })).get().filter(r => r.title !== '' && r.url !== '')
318
+ : undefined;
319
+ // Extract product info from URL
320
+ const { product, version } = extractProductInfo(displayUrl);
321
+ rawArticle = {
322
+ title,
323
+ content,
324
+ url: displayUrl,
325
+ product,
326
+ version,
327
+ breadcrumb: breadcrumb.length > 0 ? breadcrumb : undefined,
328
+ relatedArticles: relatedArticles !== undefined && relatedArticles.length > 0 ? relatedArticles : undefined
329
+ };
330
+ // Cache raw article
331
+ await cache.set(cacheKey, rawArticle);
332
+ }
333
+ // At this point rawArticle is guaranteed to be non-null
334
+ const article = rawArticle;
335
+ // Extract all sections
336
+ const allSections = extractSections(article.content);
337
+ // Handle summaryOnly mode
338
+ if (options.summaryOnly === true) {
339
+ const summaryResult = extractSummary(article.content, article.title, maxTokens);
340
+ // Build summary content
341
+ let summaryContent = `## Summary\n\n${summaryResult.summary}\n\n`;
342
+ summaryContent += `## Article Outline (${summaryResult.outline.length} sections)\n\n`;
343
+ for (const section of summaryResult.outline) {
344
+ const indent = ' '.repeat(Math.max(0, section.level - 1));
345
+ summaryContent += `${indent}- ${section.title} (~${section.tokenCount} tokens)\n`;
346
+ }
347
+ summaryContent += `\n*Estimated read time: ${summaryResult.estimatedReadTime} min | Total: ${summaryResult.totalTokens.toLocaleString()} tokens*\n`;
348
+ return {
349
+ ...article,
350
+ content: summaryContent,
351
+ tokenInfo: summaryResult.tokenInfo,
352
+ sections: allSections
353
+ };
354
+ }
355
+ // Handle section extraction if requested
356
+ let processedContent;
357
+ let tokenInfo;
358
+ if (options.section !== undefined && options.section !== '') {
359
+ // Extract specific section
360
+ const sectionResult = extractSection(article.content, options.section, maxTokens);
361
+ processedContent = sectionResult.content;
362
+ tokenInfo = sectionResult.tokenInfo;
363
+ if (sectionResult.section === null) {
364
+ // Section not found, return error info
365
+ processedContent = `*Section "${options.section}" not found.*\n\n**Available sections:**\n${allSections.map(s => `- ${s.title}`).join('\n')}`;
366
+ tokenInfo = createTokenInfo(processedContent, maxTokens);
367
+ }
368
+ }
369
+ else {
370
+ // Apply token limit with smart truncation
371
+ const truncateResult = truncateToTokenLimit(article.content, maxTokens);
372
+ processedContent = truncateResult.content;
373
+ tokenInfo = truncateResult.tokenInfo;
374
+ }
375
+ return {
376
+ ...article,
377
+ content: processedContent,
378
+ tokenInfo,
379
+ sections: allSections
380
+ };
381
+ }
382
+ /**
383
+ * Discover the latest bundle version for a product via search API
384
+ */
385
+ async function discoverLatestBundleId(product) {
386
+ const productInfo = JAMF_PRODUCTS[product];
387
+ const baseBundleId = productInfo.bundleId;
388
+ try {
389
+ // Search for any document from this product to discover the latest version
390
+ const apiUrl = `${DOCS_API_URL}/api/search?q=${encodeURIComponent(productInfo.name)}&rpp=10`;
391
+ const response = await fetchJson(apiUrl);
392
+ // Find a result with a matching bundle prefix
393
+ for (const wrapper of response.Results) {
394
+ if (wrapper.leading_result === null || wrapper.leading_result === undefined) {
395
+ continue;
396
+ }
397
+ const bundleId = wrapper.leading_result.bundle_id;
398
+ if (bundleId.startsWith(baseBundleId)) {
399
+ return bundleId;
400
+ }
401
+ }
402
+ }
403
+ catch (error) {
404
+ console.error(`[TOC] Error discovering bundle version for ${product}:`, error);
405
+ }
406
+ return null;
407
+ }
408
+ /**
409
+ * Parse TOC HTML from Zoomin backend
410
+ */
411
+ function parseTocHtml(html) {
412
+ const $ = cheerio.load(html);
413
+ const toc = [];
414
+ // Parse the nested list structure
415
+ $('ul.list-links > li.toc').each((_, el) => {
416
+ const entry = parseTocEntry($, el);
417
+ if (entry !== null) {
418
+ toc.push(entry);
419
+ }
420
+ });
421
+ return toc;
422
+ }
423
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
424
+ function parseTocEntry($, el) {
425
+ const $el = $(el);
426
+ const $link = $el.children('.inner').children('a').first();
427
+ const title = $link.text().trim();
428
+ let url = $link.attr('href') ?? '';
429
+ if (title === '' || url === '') {
430
+ return null;
431
+ }
432
+ // Transform to frontend URL for display
433
+ url = transformToFrontendUrl(url);
434
+ const entry = { title, url };
435
+ // Check for children
436
+ const $children = $el.children('ul.list-links');
437
+ if ($children.length > 0) {
438
+ const children = [];
439
+ $children.children('li.toc').each((_, childEl) => {
440
+ const childEntry = parseTocEntry($, childEl);
441
+ if (childEntry !== null) {
442
+ children.push(childEntry);
443
+ }
444
+ });
445
+ if (children.length > 0) {
446
+ entry.children = children;
447
+ }
448
+ }
449
+ return entry;
450
+ }
451
+ /**
452
+ * Convert TOC entry to string for token counting
453
+ */
454
+ function tocEntryToString(entry, depth = 0) {
455
+ const indent = ' '.repeat(depth);
456
+ const childrenStr = entry.children?.map(c => tocEntryToString(c, depth + 1)).join('') ?? '';
457
+ return `${indent}- ${entry.title}\n${childrenStr}`;
458
+ }
459
+ /**
460
+ * Count total TOC entries including children
461
+ */
462
+ function countTocEntries(entries) {
463
+ return entries.reduce((count, entry) => count + 1 + (entry.children !== undefined ? countTocEntries(entry.children) : 0), 0);
464
+ }
465
+ /**
466
+ * Fetch table of contents for a product
467
+ * Uses backend TOC endpoint (learn-be.jamf.com/bundle/{bundleId}/toc)
468
+ */
469
+ export async function fetchTableOfContents(product, version = 'current', options = {}) {
470
+ const page = options.page ?? PAGINATION_CONFIG.DEFAULT_PAGE;
471
+ const maxTokens = options.maxTokens ?? TOKEN_CONFIG.DEFAULT_MAX_TOKENS;
472
+ const cacheKey = `toc:${product}:${version}`;
473
+ // Check cache
474
+ let allToc = await cache.get(cacheKey);
475
+ if (allToc === null) {
476
+ // Get bundle ID for the specified version (or latest if current)
477
+ let bundleId = await getBundleIdForVersion(product, version);
478
+ // Fallback to discovery if metadata service fails
479
+ bundleId ??= await discoverLatestBundleId(product);
480
+ if (bundleId === null || bundleId === '') {
481
+ throw new JamfDocsError(`Could not find bundle for ${product} version ${version}`, JamfDocsErrorCode.NOT_FOUND);
482
+ }
483
+ // Fetch TOC from backend
484
+ const tocUrl = `${DOCS_API_URL}/bundle/${bundleId}/toc`;
485
+ console.error(`[TOC] Fetching TOC from: ${tocUrl}`);
486
+ const tocJson = await fetchJson(tocUrl);
487
+ // Parse TOC
488
+ allToc = [];
489
+ for (const [, html] of Object.entries(tocJson)) {
490
+ if (typeof html === 'string' && html.includes('<ul')) {
491
+ const entries = parseTocHtml(html);
492
+ allToc.push(...entries);
493
+ }
494
+ }
495
+ // Cache result
496
+ await cache.set(cacheKey, allToc);
497
+ }
498
+ // Count total entries
499
+ const totalItems = countTocEntries(allToc);
500
+ // For TOC, we paginate at the top-level entries
501
+ const topLevelCount = allToc.length;
502
+ const pageSize = PAGINATION_CONFIG.DEFAULT_PAGE_SIZE;
503
+ const paginationInfo = calculatePagination(topLevelCount, page, pageSize);
504
+ // Get paginated top-level entries (with their children)
505
+ const paginatedToc = allToc.slice(paginationInfo.startIndex, paginationInfo.endIndex);
506
+ // Calculate tokens for paginated TOC
507
+ const tocText = paginatedToc.map(e => tocEntryToString(e)).join('');
508
+ let tokenCount = estimateTokens(tocText);
509
+ let truncated = false;
510
+ let finalToc = paginatedToc;
511
+ // Truncate if over token limit
512
+ if (tokenCount > maxTokens) {
513
+ truncated = true;
514
+ finalToc = [];
515
+ let runningTokens = 0;
516
+ for (const entry of paginatedToc) {
517
+ const entryText = tocEntryToString(entry);
518
+ const entryTokens = estimateTokens(entryText);
519
+ if (runningTokens + entryTokens > maxTokens) {
520
+ break;
521
+ }
522
+ finalToc.push(entry);
523
+ runningTokens += entryTokens;
524
+ }
525
+ tokenCount = runningTokens;
526
+ }
527
+ return {
528
+ toc: finalToc,
529
+ pagination: {
530
+ page: paginationInfo.page,
531
+ pageSize: paginationInfo.pageSize,
532
+ totalPages: paginationInfo.totalPages,
533
+ totalItems,
534
+ hasNext: paginationInfo.hasNext || truncated,
535
+ hasPrev: paginationInfo.hasPrev
536
+ },
537
+ tokenInfo: {
538
+ tokenCount,
539
+ truncated,
540
+ maxTokens
541
+ }
542
+ };
543
+ }
544
+ //# sourceMappingURL=scraper.js.map