@get-technology-inc/jamf-docs-mcp-server 5.5.0 → 5.7.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 (42) hide show
  1. package/dist/core/constants/limits.d.ts +21 -0
  2. package/dist/core/constants/limits.d.ts.map +1 -1
  3. package/dist/core/constants/limits.js +0 -1
  4. package/dist/core/constants/limits.js.map +1 -1
  5. package/dist/core/constants/sources.d.ts +122 -0
  6. package/dist/core/constants/sources.d.ts.map +1 -0
  7. package/dist/core/constants/sources.js +93 -0
  8. package/dist/core/constants/sources.js.map +1 -0
  9. package/dist/core/services/article-service.d.ts.map +1 -1
  10. package/dist/core/services/article-service.js +18 -27
  11. package/dist/core/services/article-service.js.map +1 -1
  12. package/dist/core/services/article-view.d.ts +21 -0
  13. package/dist/core/services/article-view.d.ts.map +1 -0
  14. package/dist/core/services/article-view.js +48 -0
  15. package/dist/core/services/article-view.js.map +1 -0
  16. package/dist/core/services/cache-key.d.ts +18 -0
  17. package/dist/core/services/cache-key.d.ts.map +1 -1
  18. package/dist/core/services/cache-key.js +2 -0
  19. package/dist/core/services/cache-key.js.map +1 -1
  20. package/dist/core/services/content-parser.d.ts +24 -1
  21. package/dist/core/services/content-parser.d.ts.map +1 -1
  22. package/dist/core/services/content-parser.js +24 -10
  23. package/dist/core/services/content-parser.js.map +1 -1
  24. package/dist/core/services/sitemap-service.d.ts +60 -0
  25. package/dist/core/services/sitemap-service.d.ts.map +1 -0
  26. package/dist/core/services/sitemap-service.js +201 -0
  27. package/dist/core/services/sitemap-service.js.map +1 -0
  28. package/dist/core/services/static-article-service.d.ts +38 -0
  29. package/dist/core/services/static-article-service.d.ts.map +1 -0
  30. package/dist/core/services/static-article-service.js +121 -0
  31. package/dist/core/services/static-article-service.js.map +1 -0
  32. package/dist/core/tools/get-toc.d.ts.map +1 -1
  33. package/dist/core/tools/get-toc.js +32 -4
  34. package/dist/core/tools/get-toc.js.map +1 -1
  35. package/dist/core/tools/list-products.d.ts.map +1 -1
  36. package/dist/core/tools/list-products.js +21 -10
  37. package/dist/core/tools/list-products.js.map +1 -1
  38. package/dist/core/utils/url.d.ts +9 -1
  39. package/dist/core/utils/url.d.ts.map +1 -1
  40. package/dist/core/utils/url.js +11 -1
  41. package/dist/core/utils/url.js.map +1 -1
  42. package/package.json +1 -1
@@ -32,22 +32,32 @@ turndown.addRule('stripScripts', {
32
32
  filter: ['script', 'style', 'noscript'],
33
33
  replacement: () => '',
34
34
  });
35
- // ─── HTML cleaning ──────────────────────────────────────────────
36
35
  /**
37
36
  * Clean HTML content: remove unwanted elements, fix relative URLs.
37
+ *
38
+ * Both the selector set and the link base are parameters with learn.jamf.com
39
+ * defaults rather than module constants, so a caller reading a different site
40
+ * gets that site's markup rules. Existing single-argument callers — the
41
+ * glossary path among them — are unaffected.
38
42
  */
39
- export function cleanHtml($) {
40
- $(SELECTORS.REMOVE).remove();
43
+ export function cleanHtml($, options) {
44
+ const selectors = options?.selectors ?? SELECTORS;
45
+ // Root-relative links resolve against the site they came from. Hard-coding
46
+ // learn.jamf.com here is what would point every internal link and image of
47
+ // a second source at the wrong host — silently, since both produce a valid
48
+ // absolute URL.
49
+ const linkBase = options?.linkBase ?? DOCS_BASE_URL;
50
+ $(selectors.REMOVE).remove();
41
51
  $('a[href^="/"]').each((_, el) => {
42
52
  const href = $(el).attr('href');
43
53
  if (href !== undefined && href !== '') {
44
- $(el).attr('href', `${DOCS_BASE_URL}${href}`);
54
+ $(el).attr('href', `${linkBase}${href}`);
45
55
  }
46
56
  });
47
57
  $('img[src^="/"]').each((_, el) => {
48
58
  const src = $(el).attr('src');
49
59
  if (src !== undefined && src !== '') {
50
- $(el).attr('src', `${DOCS_BASE_URL}${src}`);
60
+ $(el).attr('src', `${linkBase}${src}`);
51
61
  }
52
62
  });
53
63
  }
@@ -85,7 +95,11 @@ export function linkInternalSpans($, resolve) {
85
95
  */
86
96
  export function parseArticle(html, displayUrl, options) {
87
97
  const $ = cheerio.load(html);
88
- cleanHtml($);
98
+ const selectors = options?.selectors ?? SELECTORS;
99
+ cleanHtml($, {
100
+ selectors,
101
+ ...(options?.linkBase !== undefined ? { linkBase: options.linkBase } : {}),
102
+ });
89
103
  // Before anything reads anchors: internal links are spans until this runs.
90
104
  const resolveInternalLink = options?.resolveInternalLink;
91
105
  if (resolveInternalLink !== undefined) {
@@ -99,7 +113,7 @@ export function parseArticle(html, displayUrl, options) {
99
113
  // Checked before body wrappers because <article> is semantically broader
100
114
  // and should take priority when both exist as siblings.
101
115
  if (contentHtml === '') {
102
- contentHtml = $(SELECTORS.CONTENT).html() ?? '';
116
+ contentHtml = $(selectors.CONTENT).html() ?? '';
103
117
  }
104
118
  // 3. Common FT body wrappers (taskbody, conbody, refbody, etc.)
105
119
  if (contentHtml === '') {
@@ -113,19 +127,19 @@ export function parseArticle(html, displayUrl, options) {
113
127
  if (contentHtml === '') {
114
128
  contentHtml = html;
115
129
  }
116
- const extractedTitle = $(SELECTORS.TITLE).first().text().trim();
130
+ const extractedTitle = $(selectors.TITLE).first().text().trim();
117
131
  const title = extractedTitle !== '' ? extractedTitle : 'Untitled';
118
132
  // Convert to Markdown and strip Turndown anchor artifacts from headings
119
133
  const content = turndown.turndown(contentHtml)
120
134
  .replace(/^(#{1,6}\s+)\[([^\]]*)\]\(#[^)]*\)/gm, '$1$2');
121
135
  // Extract breadcrumb
122
- const breadcrumb = $(SELECTORS.BREADCRUMB)
136
+ const breadcrumb = $(selectors.BREADCRUMB)
123
137
  .map((_, el) => $(el).text().trim())
124
138
  .get()
125
139
  .filter(Boolean);
126
140
  // Extract related articles
127
141
  const relatedArticles = options?.includeRelated === true
128
- ? $(SELECTORS.RELATED).map((_, el) => {
142
+ ? $(selectors.RELATED).map((_, el) => {
129
143
  const rawHref = $(el).attr('href') ?? '';
130
144
  if (rawHref === '' || rawHref.startsWith('#')) {
131
145
  return { title: '', url: '' };
@@ -1 +1 @@
1
- {"version":3,"file":"content-parser.js","sourceRoot":"","sources":["../../../src/core/services/content-parser.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"content-parser.js","sourceRoot":"","sources":["../../../src/core/services/content-parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Table of contents for a static documentation source, built from its sitemap.
3
+ *
4
+ * A Fluid Topics publication ships a real TOC endpoint. A static site does
5
+ * not — but concepts.jamf.com publishes a sitemap whose paths already encode
6
+ * the hierarchy (`{locale}/guides/{category}/{article}`), so the tree can be
7
+ * derived from 990 URLs in one request instead of crawling 99 pages per
8
+ * locale and parsing each one's navigation.
9
+ */
10
+ import type { StaticDocSource, StaticSection } from '../constants/sources.js';
11
+ import type { ServerContext } from '../types/context.js';
12
+ import type { FetchTocOptions, FetchTocResult, TocEntry } from '../types.js';
13
+ /** One `<url>` of a sitemap, reduced to what a TOC needs. */
14
+ export interface SitemapEntry {
15
+ /** Canonical absolute URL. */
16
+ url: string;
17
+ /** Path segments after the origin, e.g. `['en', 'guides', 'ai-governance']`. */
18
+ segments: string[];
19
+ /** `<lastmod>`, when present. */
20
+ lastModified?: string;
21
+ }
22
+ /**
23
+ * Extract every `<loc>` from a sitemap, with its `<lastmod>` when it has one.
24
+ *
25
+ * Deliberately a scan for `<url>` blocks rather than an XML parse: the
26
+ * document is a flat list, cheerio would have to be told to treat it as XML,
27
+ * and a sitemap that fails to parse should still yield the entries it does
28
+ * have.
29
+ */
30
+ export declare function parseSitemap(xml: string): SitemapEntry[];
31
+ /** Fetch and cache a source's sitemap. */
32
+ export declare function loadSitemap(ctx: ServerContext, source: StaticDocSource): Promise<SitemapEntry[]>;
33
+ /**
34
+ * Turn a slug into a heading: `ai-governance` → `AI Governance`.
35
+ *
36
+ * Checked against the fourteen real titles concepts.jamf.com's own guides
37
+ * index renders, which is the only place the site publishes them without a
38
+ * per-page request: eight of nine match exactly. The ninth is
39
+ * `infrastructure-as-code`, which the site titles "Infrastructure As Code"
40
+ * and this produces as "Infrastructure as Code" — standard title case
41
+ * lowercases "as", and matching one page's capitalisation is not worth a
42
+ * special case.
43
+ */
44
+ export declare function titleFromSlug(slug: string): string;
45
+ /**
46
+ * Build a TOC for one section of a static source, in one locale.
47
+ *
48
+ * @param locale the source's own locale code, e.g. `en` — not `en-US`
49
+ */
50
+ export declare function buildStaticToc(ctx: ServerContext, source: StaticDocSource, section: StaticSection, locale: string): Promise<TocEntry[]>;
51
+ /**
52
+ * A `FetchTocResult` for a static source's section.
53
+ *
54
+ * Pagination and token truncation are the same operations the Fluid Topics
55
+ * path performs, applied to entries that came from a sitemap instead of a
56
+ * map: a caller paging through a Concepts TOC must not get a different shape
57
+ * from one paging through Jamf Pro's.
58
+ */
59
+ export declare function fetchStaticToc(ctx: ServerContext, source: StaticDocSource, section: StaticSection, sourceLocale: string, options?: FetchTocOptions): Promise<FetchTocResult>;
60
+ //# sourceMappingURL=sitemap-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sitemap-service.d.ts","sourceRoot":"","sources":["../../../src/core/services/sitemap-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC9E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAQ7E,6DAA6D;AAC7D,MAAM,WAAW,YAAY;IAC3B,8BAA8B;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,gFAAgF;IAChF,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,iCAAiC;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,EAAE,CAmBxD;AAED,0CAA0C;AAC1C,wBAAsB,WAAW,CAC/B,GAAG,EAAE,aAAa,EAClB,MAAM,EAAE,eAAe,GACtB,OAAO,CAAC,YAAY,EAAE,CAAC,CASzB;AA4BD;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAUlD;AAsBD;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,aAAa,EAClB,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,QAAQ,EAAE,CAAC,CAwBrB;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,aAAa,EAClB,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE,aAAa,EACtB,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,cAAc,CAAC,CAgCzB"}
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Table of contents for a static documentation source, built from its sitemap.
3
+ *
4
+ * A Fluid Topics publication ships a real TOC endpoint. A static site does
5
+ * not — but concepts.jamf.com publishes a sitemap whose paths already encode
6
+ * the hierarchy (`{locale}/guides/{category}/{article}`), so the tree can be
7
+ * derived from 990 URLs in one request instead of crawling 99 pages per
8
+ * locale and parsing each one's navigation.
9
+ */
10
+ import { httpGetText } from '../http-client.js';
11
+ import { cacheKey } from './cache-key.js';
12
+ import { canonicalStaticUrl } from './static-article-service.js';
13
+ import { PAGINATION_CONFIG, TOKEN_CONFIG } from '../constants.js';
14
+ import { calculatePagination, truncateListByTokens, buildPaginationNote, } from './tokenizer.js';
15
+ /**
16
+ * Extract every `<loc>` from a sitemap, with its `<lastmod>` when it has one.
17
+ *
18
+ * Deliberately a scan for `<url>` blocks rather than an XML parse: the
19
+ * document is a flat list, cheerio would have to be told to treat it as XML,
20
+ * and a sitemap that fails to parse should still yield the entries it does
21
+ * have.
22
+ */
23
+ export function parseSitemap(xml) {
24
+ const out = [];
25
+ for (const block of xml.match(/<url\b[\s\S]*?<\/url>/g) ?? []) {
26
+ const loc = /<loc>\s*([^<\s]+)\s*<\/loc>/.exec(block)?.[1];
27
+ if (loc === undefined) {
28
+ continue;
29
+ }
30
+ const lastmod = /<lastmod>\s*([^<\s]+)\s*<\/lastmod>/.exec(block)?.[1];
31
+ let segments;
32
+ try {
33
+ segments = new URL(loc).pathname.split('/').filter(Boolean);
34
+ }
35
+ catch {
36
+ continue;
37
+ }
38
+ out.push({
39
+ url: canonicalStaticUrl(loc),
40
+ segments,
41
+ ...(lastmod !== undefined ? { lastModified: lastmod } : {}),
42
+ });
43
+ }
44
+ return out;
45
+ }
46
+ /** Fetch and cache a source's sitemap. */
47
+ export async function loadSitemap(ctx, source) {
48
+ const key = cacheKey('static-sitemap', { source: source.id });
49
+ const cached = await ctx.cache.get(key);
50
+ if (cached !== null) {
51
+ return cached;
52
+ }
53
+ const xml = await httpGetText(`${source.baseUrl}/sitemap.xml`);
54
+ const entries = parseSitemap(xml);
55
+ await ctx.cache.set(key, entries, ctx.config.cacheTtl.products);
56
+ return entries;
57
+ }
58
+ /**
59
+ * Terms whose casing a naive capitalise gets wrong.
60
+ *
61
+ * The sitemap gives slugs, not titles, and fetching 99 pages per locale to
62
+ * read each `og:title` is not worth one heading apiece. Word-by-word
63
+ * capitalisation produces "Ai Governance", "Byod" and "Ios", which read as
64
+ * mistakes; this table is what the site's own index page shows those as.
65
+ * Keyed lowercase.
66
+ */
67
+ const TITLE_CASE_TERMS = {
68
+ ai: 'AI', api: 'API', byod: 'BYOD', ddm: 'DDM', it: 'IT', mdm: 'MDM',
69
+ pki: 'PKI', ldap: 'LDAP', scep: 'SCEP', ztna: 'ZTNA', sso: 'SSO',
70
+ vpn: 'VPN', mfa: 'MFA', dns: 'DNS', ip: 'IP', tls: 'TLS', url: 'URL',
71
+ json: 'JSON', xml: 'XML', sdk: 'SDK', cli: 'CLI', ui: 'UI', ux: 'UX',
72
+ id: 'ID', edr: 'EDR', xdr: 'XDR', siem: 'SIEM', saas: 'SaaS',
73
+ macos: 'macOS', ios: 'iOS', ipados: 'iPadOS', tvos: 'tvOS',
74
+ watchos: 'watchOS', visionos: 'visionOS', jamf: 'Jamf', apple: 'Apple',
75
+ aws: 'AWS', okta: 'Okta', entra: 'Entra', jss: 'JSS',
76
+ };
77
+ /** Words that stay lowercase unless they open the title. */
78
+ const TITLE_MINOR_WORDS = new Set([
79
+ 'a', 'an', 'and', 'as', 'at', 'but', 'by', 'for', 'in', 'of', 'on', 'or',
80
+ 'the', 'to', 'via', 'with',
81
+ ]);
82
+ /**
83
+ * Turn a slug into a heading: `ai-governance` → `AI Governance`.
84
+ *
85
+ * Checked against the fourteen real titles concepts.jamf.com's own guides
86
+ * index renders, which is the only place the site publishes them without a
87
+ * per-page request: eight of nine match exactly. The ninth is
88
+ * `infrastructure-as-code`, which the site titles "Infrastructure As Code"
89
+ * and this produces as "Infrastructure as Code" — standard title case
90
+ * lowercases "as", and matching one page's capitalisation is not worth a
91
+ * special case.
92
+ */
93
+ export function titleFromSlug(slug) {
94
+ const words = slug.split('-').filter(Boolean);
95
+ return words
96
+ .map((word, index) => {
97
+ const known = TITLE_CASE_TERMS[word.toLowerCase()];
98
+ if (known !== undefined) {
99
+ return known;
100
+ }
101
+ if (index > 0 && TITLE_MINOR_WORDS.has(word.toLowerCase())) {
102
+ return word.toLowerCase();
103
+ }
104
+ return word.charAt(0).toUpperCase() + word.slice(1);
105
+ })
106
+ .join(' ');
107
+ }
108
+ function toTocEntries(nodes, titles) {
109
+ return [...nodes]
110
+ .sort((a, b) => a.slug.localeCompare(b.slug))
111
+ .map(node => {
112
+ const children = toTocEntries(node.children.values(), titles);
113
+ const entry = {
114
+ title: titles.get(node.url ?? '') ?? titleFromSlug(node.slug),
115
+ url: node.url ?? '',
116
+ };
117
+ if (children.length > 0) {
118
+ entry.children = children;
119
+ }
120
+ return entry;
121
+ });
122
+ }
123
+ /**
124
+ * Build a TOC for one section of a static source, in one locale.
125
+ *
126
+ * @param locale the source's own locale code, e.g. `en` — not `en-US`
127
+ */
128
+ export async function buildStaticToc(ctx, source, section, locale) {
129
+ const entries = await loadSitemap(ctx, source);
130
+ const root = new Map();
131
+ for (const entry of entries) {
132
+ const [entryLocale, entrySection, ...rest] = entry.segments;
133
+ if (entryLocale !== locale || entrySection !== section.path) {
134
+ continue;
135
+ }
136
+ // The section's own index page is the container, not a child of itself.
137
+ if (rest.length === 0) {
138
+ continue;
139
+ }
140
+ let level = root;
141
+ let node;
142
+ for (const slug of rest) {
143
+ node = level.get(slug);
144
+ if (node === undefined) {
145
+ node = { slug, children: new Map() };
146
+ level.set(slug, node);
147
+ }
148
+ level = node.children;
149
+ }
150
+ if (node !== undefined) {
151
+ node.url = entry.url;
152
+ }
153
+ }
154
+ return toTocEntries(root.values(), new Map());
155
+ }
156
+ /**
157
+ * A `FetchTocResult` for a static source's section.
158
+ *
159
+ * Pagination and token truncation are the same operations the Fluid Topics
160
+ * path performs, applied to entries that came from a sitemap instead of a
161
+ * map: a caller paging through a Concepts TOC must not get a different shape
162
+ * from one paging through Jamf Pro's.
163
+ */
164
+ export async function fetchStaticToc(ctx, source, section, sourceLocale, options = {}) {
165
+ const page = options.page ?? PAGINATION_CONFIG.DEFAULT_PAGE;
166
+ const maxTokens = options.maxTokens ?? TOKEN_CONFIG.DEFAULT_MAX_TOKENS;
167
+ const allToc = await buildStaticToc(ctx, source, section, sourceLocale);
168
+ const totalItems = countTocEntries(allToc);
169
+ const paginationCalc = calculatePagination(allToc.length, page, PAGINATION_CONFIG.DEFAULT_PAGE_SIZE);
170
+ const paginated = allToc.slice(paginationCalc.startIndex, paginationCalc.endIndex);
171
+ const { items, tokenCount, truncated } = truncateListByTokens(paginated, maxTokens, tocEntryToString);
172
+ const paginationNote = buildPaginationNote(paginationCalc);
173
+ return {
174
+ toc: items,
175
+ pagination: {
176
+ page: paginationCalc.page,
177
+ pageSize: paginationCalc.pageSize,
178
+ totalPages: paginationCalc.totalPages,
179
+ totalItems,
180
+ hasNext: paginationCalc.hasNext,
181
+ hasPrev: paginationCalc.hasPrev,
182
+ },
183
+ tokenInfo: { tokenCount, truncated, maxTokens },
184
+ // The locale that answered is the one asked for: unlike Fluid Topics,
185
+ // where a family may exist in en-US only, a static section either
186
+ // publishes the locale or `resolveTocSource` refused before reaching here.
187
+ resolvedLocale: sourceLocale,
188
+ ...(paginationNote !== undefined ? { paginationNote } : {}),
189
+ };
190
+ }
191
+ /** Total entries including nested children. Mirrors toc-service's own count. */
192
+ function countTocEntries(entries) {
193
+ return entries.reduce((count, entry) => count + 1 + (entry.children !== undefined ? countTocEntries(entry.children) : 0), 0);
194
+ }
195
+ /** Serialise one entry for token estimation. Mirrors toc-service's. */
196
+ function tocEntryToString(entry, depth = 0) {
197
+ const indent = ' '.repeat(depth);
198
+ const childrenStr = entry.children?.map(c => tocEntryToString(c, depth + 1)).join('') ?? '';
199
+ return `${indent}- ${entry.title}\n${childrenStr}`;
200
+ }
201
+ //# sourceMappingURL=sitemap-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sitemap-service.js","sourceRoot":"","sources":["../../../src/core/services/sitemap-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Article retrieval for documentation sources outside Fluid Topics.
3
+ *
4
+ * The Fluid Topics path addresses a topic by `mapId` + `contentId` and gets
5
+ * an HTML fragment back. A static site has neither: the address is the URL,
6
+ * and the response is a whole page whose navigation has to be stripped before
7
+ * anything else. Everything downstream of that — sections, summaries,
8
+ * truncation — is shared with the FT path through
9
+ * {@link buildArticleView}, so the two cannot drift on what a caller sees.
10
+ */
11
+ import type { StaticDocSource } from '../constants/sources.js';
12
+ import type { ServerContext } from '../types/context.js';
13
+ import type { FetchArticleOptions, FetchArticleResult } from '../types.js';
14
+ /**
15
+ * Canonicalise a URL for a static site.
16
+ *
17
+ * concepts.jamf.com's sitemap emits paths without a trailing slash while the
18
+ * site itself redirects to the slashed form — all 990 entries, so fetching
19
+ * them as listed is 990 redirects. Adding the slash up front is one line here
20
+ * and saves a round trip per article. Paths that look like a file (`.html`,
21
+ * `.json`) are left alone, as is anything with a query or fragment.
22
+ */
23
+ export declare function canonicalStaticUrl(urlStr: string): string;
24
+ /**
25
+ * The page's own title, preferring Open Graph over `<title>`.
26
+ *
27
+ * `<title>` on these sites carries a site-name suffix ("API Utility | Jamf
28
+ * Concepts"); `og:title` is the same string without it, so it is tried first
29
+ * and the suffix trimmed only as a fallback.
30
+ */
31
+ export declare function extractDocumentTitle(html: string): string | undefined;
32
+ /**
33
+ * Fetch and parse one article from a static documentation source.
34
+ *
35
+ * @param source the registry row for the hostname in `url`
36
+ */
37
+ export declare function fetchStaticArticle(ctx: ServerContext, source: StaticDocSource, url: string, options?: FetchArticleOptions): Promise<FetchArticleResult>;
38
+ //# sourceMappingURL=static-article-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"static-article-service.d.ts","sourceRoot":"","sources":["../../../src/core/services/static-article-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAQH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAU3E;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAWzD;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CASrE;AAYD;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,aAAa,EAClB,MAAM,EAAE,eAAe,EACvB,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,kBAAkB,CAAC,CA2D7B"}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Article retrieval for documentation sources outside Fluid Topics.
3
+ *
4
+ * The Fluid Topics path addresses a topic by `mapId` + `contentId` and gets
5
+ * an HTML fragment back. A static site has neither: the address is the URL,
6
+ * and the response is a whole page whose navigation has to be stripped before
7
+ * anything else. Everything downstream of that — sections, summaries,
8
+ * truncation — is shared with the FT path through
9
+ * {@link buildArticleView}, so the two cannot drift on what a caller sees.
10
+ */
11
+ import { httpGetText } from '../http-client.js';
12
+ import { parseArticle } from './content-parser.js';
13
+ import { buildArticleView } from './article-view.js';
14
+ import { extractSections } from './tokenizer.js';
15
+ import { cacheKey } from './cache-key.js';
16
+ import { TOKEN_CONFIG } from '../constants.js';
17
+ /**
18
+ * Canonicalise a URL for a static site.
19
+ *
20
+ * concepts.jamf.com's sitemap emits paths without a trailing slash while the
21
+ * site itself redirects to the slashed form — all 990 entries, so fetching
22
+ * them as listed is 990 redirects. Adding the slash up front is one line here
23
+ * and saves a round trip per article. Paths that look like a file (`.html`,
24
+ * `.json`) are left alone, as is anything with a query or fragment.
25
+ */
26
+ export function canonicalStaticUrl(urlStr) {
27
+ try {
28
+ const url = new URL(urlStr);
29
+ const last = url.pathname.split('/').pop() ?? '';
30
+ if (!url.pathname.endsWith('/') && !last.includes('.')) {
31
+ url.pathname = `${url.pathname}/`;
32
+ }
33
+ return url.toString();
34
+ }
35
+ catch {
36
+ return urlStr;
37
+ }
38
+ }
39
+ /**
40
+ * The page's own title, preferring Open Graph over `<title>`.
41
+ *
42
+ * `<title>` on these sites carries a site-name suffix ("API Utility | Jamf
43
+ * Concepts"); `og:title` is the same string without it, so it is tried first
44
+ * and the suffix trimmed only as a fallback.
45
+ */
46
+ export function extractDocumentTitle(html) {
47
+ const og = /<meta[^>]+property=["']og:title["'][^>]+content=["']([^"']+)["']/i.exec(html)
48
+ ?? /<meta[^>]+content=["']([^"']+)["'][^>]+property=["']og:title["']/i.exec(html);
49
+ if (og?.[1] !== undefined && og[1].trim() !== '') {
50
+ return decodeEntities(og[1].trim());
51
+ }
52
+ const title = /<title[^>]*>([^<]*)<\/title>/i.exec(html);
53
+ if (title?.[1] === undefined) {
54
+ return undefined;
55
+ }
56
+ const trimmed = title[1].split('|')[0]?.trim() ?? '';
57
+ return trimmed !== '' ? decodeEntities(trimmed) : undefined;
58
+ }
59
+ /** The handful of entities that survive into a title attribute. */
60
+ function decodeEntities(text) {
61
+ return text
62
+ .replace(/&amp;/g, '&')
63
+ .replace(/&lt;/g, '<')
64
+ .replace(/&gt;/g, '>')
65
+ .replace(/&quot;/g, '"')
66
+ .replace(/&#(?:39|x27);/g, "'");
67
+ }
68
+ /**
69
+ * Fetch and parse one article from a static documentation source.
70
+ *
71
+ * @param source the registry row for the hostname in `url`
72
+ */
73
+ export async function fetchStaticArticle(ctx, source, url, options = {}) {
74
+ const maxTokens = options.maxTokens ?? TOKEN_CONFIG.DEFAULT_MAX_TOKENS;
75
+ const displayUrl = canonicalStaticUrl(url);
76
+ const key = cacheKey('static-article', { source: source.id, url: displayUrl });
77
+ let cached = await ctx.cache.get(key);
78
+ if (cached === null) {
79
+ const html = await httpGetText(displayUrl);
80
+ const documentTitle = extractDocumentTitle(html);
81
+ const parsed = parseArticle(html, displayUrl, {
82
+ // The source's own markup rules. Parsing a static page with Fluid
83
+ // Topics' selectors finds no content wrapper and falls through to
84
+ // <body>, which is the whole site chrome.
85
+ selectors: source.selectors,
86
+ // Root-relative links belong to this source, not learn.jamf.com.
87
+ linkBase: source.baseUrl,
88
+ ...(options.includeRelated !== undefined ? { includeRelated: options.includeRelated } : {}),
89
+ });
90
+ // `parseArticle` reads the first surviving <h1>. On a static site the
91
+ // page heading often lives in a hero section outside the content wrapper,
92
+ // and stripping the chrome takes it with it — concepts.jamf.com's tool
93
+ // pages put their only <h1> there, so every one of the 37 came back
94
+ // "Untitled". The document's own title is the reliable answer when the
95
+ // body has none of its own; Fluid Topics never needs this because its
96
+ // titles arrive as metadata.
97
+ const title = parsed.title !== 'Untitled' ? parsed.title : documentTitle ?? parsed.title;
98
+ cached = { title, parsed, displayUrl };
99
+ await ctx.cache.set(key, cached, ctx.config.cacheTtl.article);
100
+ }
101
+ const { title, parsed } = cached;
102
+ // Provenance is part of the content, not metadata: it has to survive
103
+ // section extraction and truncation, both of which slice `content`, and a
104
+ // reader who asked for one section still needs to know what they are
105
+ // reading.
106
+ const content = source.provenance !== undefined
107
+ ? `${parsed.content}\n\n---\n\n*${source.provenance}*\n`
108
+ : parsed.content;
109
+ const allSections = extractSections(content);
110
+ return buildArticleView({
111
+ title,
112
+ url: cached.displayUrl,
113
+ product: source.name,
114
+ breadcrumb: parsed.breadcrumb.length > 0 ? parsed.breadcrumb : undefined,
115
+ relatedArticles: options.includeRelated === true && parsed.relatedArticles.length > 0
116
+ ? parsed.relatedArticles
117
+ : undefined,
118
+ sections: allSections,
119
+ }, content, options, maxTokens, allSections);
120
+ }
121
+ //# sourceMappingURL=static-article-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"static-article-service.js","sourceRoot":"","sources":["../../../src/core/services/static-article-service.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"get-toc.d.ts","sourceRoot":"","sources":["../../../src/core/tools/get-toc.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AA8YzD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,aAAa,GAAG,IAAI,CA0I9E"}
1
+ {"version":3,"file":"get-toc.d.ts","sourceRoot":"","sources":["../../../src/core/tools/get-toc.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAobzD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,aAAa,GAAG,IAAI,CAuJ9E"}
@@ -6,8 +6,10 @@ import { appToolMeta } from '../apps/index.js';
6
6
  import { GetTocInputSchema } from '../schemas/index.js';
7
7
  import { reportProgress } from '../utils/progress.js';
8
8
  import { TocOutputSchema } from '../schemas/output.js';
9
- import { ResponseFormat, OutputMode, JAMF_PRODUCTS, PRODUCT_ID_LIST, TOKEN_CONFIG, PAGINATION_CONFIG } from '../constants.js';
9
+ import { ResponseFormat, OutputMode, JAMF_PRODUCTS, PRODUCT_ID_LIST, TOKEN_CONFIG, PAGINATION_CONFIG, DEFAULT_LOCALE } from '../constants.js';
10
10
  import { fetchTableOfContents } from '../services/toc-service.js';
11
+ import { fetchStaticToc } from '../services/sitemap-service.js';
12
+ import { staticSectionById, } from '../constants/sources.js';
11
13
  import { getAvailableVersions } from '../services/metadata.js';
12
14
  import { sanitizeMarkdownText, sanitizeMarkdownUrl, getSafeErrorMessage } from '../utils/sanitize.js';
13
15
  /**
@@ -291,6 +293,26 @@ async function resolveTocSource(ctx, params, version) {
291
293
  };
292
294
  }
293
295
  const publication = params.publication ?? '';
296
+ // Static sources first: their sections are publications too, and they are
297
+ // not in the Fluid Topics maps registry, so asking it would report them as
298
+ // unknown and suggest something else.
299
+ const staticRow = staticSectionById(publication);
300
+ if (staticRow !== undefined) {
301
+ const locale = params.language ?? DEFAULT_LOCALE;
302
+ const sourceLocale = staticRow.source.locales[locale];
303
+ if (sourceLocale === undefined) {
304
+ return {
305
+ error: `${staticRow.source.name} does not publish in ${locale}. ` +
306
+ `Available: ${Object.keys(staticRow.source.locales).join(', ')}.`,
307
+ };
308
+ }
309
+ return {
310
+ source: publication,
311
+ sourceLabel: staticRow.section.title,
312
+ availableVersions: [],
313
+ staticSection: { ...staticRow, sourceLocale },
314
+ };
315
+ }
294
316
  // Not an enum: there are 97 families and the set is upstream's to change,
295
317
  // so the check is a registry lookup and a miss carries suggestions rather
296
318
  // than a wall of every id.
@@ -358,11 +380,17 @@ export function registerGetTocTool(server, ctx) {
358
380
  }
359
381
  }
360
382
  await reportProgress(extra, { progress: 0, total: 4, message: 'Fetching TOC...' });
361
- const tocResult = await fetchTableOfContents(ctx, source, version, {
383
+ const tocOptions = {
362
384
  ...(params.page !== undefined && { page: params.page }),
363
385
  maxTokens: params.maxTokens ?? TOKEN_CONFIG.DEFAULT_MAX_TOKENS,
364
- locale: params.language
365
- });
386
+ locale: params.language,
387
+ };
388
+ // A static source has no map and no TOC endpoint; its tree comes from
389
+ // the sitemap. Pagination and truncation are shared so both kinds of
390
+ // TOC behave the same once the entries exist.
391
+ const tocResult = resolved.staticSection !== undefined
392
+ ? await fetchStaticToc(ctx, resolved.staticSection.source, resolved.staticSection.section, resolved.staticSection.sourceLocale, tocOptions)
393
+ : await fetchTableOfContents(ctx, source, version, tocOptions);
366
394
  await reportProgress(extra, { progress: 1, total: 4, message: 'Processing entries...' });
367
395
  const { toc, pagination, tokenInfo, paginationNote, mapId, resolvedLocale } = tocResult;
368
396
  // Build response