@get-technology-inc/jamf-docs-mcp-server 5.7.0 → 5.9.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 (33) hide show
  1. package/dist/core/constants/sources.d.ts +68 -2
  2. package/dist/core/constants/sources.d.ts.map +1 -1
  3. package/dist/core/constants/sources.js +47 -0
  4. package/dist/core/constants/sources.js.map +1 -1
  5. package/dist/core/schemas/output.d.ts +5 -0
  6. package/dist/core/schemas/output.d.ts.map +1 -1
  7. package/dist/core/schemas/output.js +15 -0
  8. package/dist/core/schemas/output.js.map +1 -1
  9. package/dist/core/services/cache-key.d.ts +15 -0
  10. package/dist/core/services/cache-key.d.ts.map +1 -1
  11. package/dist/core/services/cache-key.js +3 -0
  12. package/dist/core/services/cache-key.js.map +1 -1
  13. package/dist/core/services/intercom-service.d.ts +85 -0
  14. package/dist/core/services/intercom-service.d.ts.map +1 -0
  15. package/dist/core/services/intercom-service.js +312 -0
  16. package/dist/core/services/intercom-service.js.map +1 -0
  17. package/dist/core/services/static-article-service.d.ts.map +1 -1
  18. package/dist/core/services/static-article-service.js +29 -0
  19. package/dist/core/services/static-article-service.js.map +1 -1
  20. package/dist/core/services/static-search-service.d.ts +40 -0
  21. package/dist/core/services/static-search-service.d.ts.map +1 -0
  22. package/dist/core/services/static-search-service.js +144 -0
  23. package/dist/core/services/static-search-service.js.map +1 -0
  24. package/dist/core/tools/get-toc.d.ts.map +1 -1
  25. package/dist/core/tools/get-toc.js +70 -8
  26. package/dist/core/tools/get-toc.js.map +1 -1
  27. package/dist/core/tools/list-products.d.ts.map +1 -1
  28. package/dist/core/tools/list-products.js +25 -2
  29. package/dist/core/tools/list-products.js.map +1 -1
  30. package/dist/core/tools/search.d.ts.map +1 -1
  31. package/dist/core/tools/search.js +53 -2
  32. package/dist/core/tools/search.js.map +1 -1
  33. package/package.json +6 -6
@@ -0,0 +1,312 @@
1
+ /**
2
+ * Reading an Intercom Help Center.
3
+ *
4
+ * support.jamf.com is a Next.js app whose every page embeds its own data as
5
+ * JSON in `<script id="__NEXT_DATA__">`. That is the source of truth: the
6
+ * rendered DOM is a view of it, and parsing the JSON avoids guessing at
7
+ * class names that change with any theme update.
8
+ *
9
+ * The content model is a block list, not HTML, so it is rendered to Markdown
10
+ * here rather than going through `content-parser`.
11
+ */
12
+ import * as cheerio from 'cheerio';
13
+ import { httpGetText } from '../http-client.js';
14
+ import { cacheKey } from './cache-key.js';
15
+ import { PAGINATION_CONFIG, TOKEN_CONFIG } from '../constants.js';
16
+ import { calculatePagination, truncateListByTokens, buildPaginationNote, } from './tokenizer.js';
17
+ // ─── __NEXT_DATA__ ──────────────────────────────────────────────
18
+ /**
19
+ * Pull the embedded page data out of an Intercom Help Center page.
20
+ *
21
+ * The opening tag carries a `nonce` attribute, so a regex anchored on
22
+ * `<script id="__NEXT_DATA__" type="application/json">` misses every page.
23
+ * Matching up to the first `>` is what makes it robust to attribute drift.
24
+ */
25
+ export function parseNextData(html) {
26
+ const match = /<script id="__NEXT_DATA__"[^>]*>([\s\S]*?)<\/script>/.exec(html);
27
+ if (match?.[1] === undefined) {
28
+ return null;
29
+ }
30
+ try {
31
+ return JSON.parse(match[1]);
32
+ }
33
+ catch {
34
+ return null;
35
+ }
36
+ }
37
+ /** `props.pageProps`, or null when the page is not shaped like one. */
38
+ function pageProps(html) {
39
+ const data = parseNextData(html);
40
+ const props = data?.props?.pageProps;
41
+ return typeof props === 'object' && props !== null
42
+ ? props
43
+ : null;
44
+ }
45
+ /**
46
+ * A JSON value read back as a string, or the fallback.
47
+ *
48
+ * `String(value)` on an `unknown` renders an object as "[object Object]" and
49
+ * puts it straight into a title. These payloads come off the wire, so the
50
+ * narrowing has to happen at the boundary rather than being asserted.
51
+ */
52
+ function asString(value, fallback = '') {
53
+ if (typeof value === 'string') {
54
+ return value;
55
+ }
56
+ if (typeof value === 'number') {
57
+ return String(value);
58
+ }
59
+ return fallback;
60
+ }
61
+ /** Inline HTML inside a block's `text`, flattened to Markdown-safe text. */
62
+ function inlineText(html) {
63
+ const $ = cheerio.load(`<div>${html}</div>`);
64
+ $('a[href]').each((_, el) => {
65
+ const href = $(el).attr('href') ?? '';
66
+ const label = $(el).text();
67
+ if (href !== '' && label !== '') {
68
+ $(el).replaceWith(`[${label}](${href})`);
69
+ }
70
+ });
71
+ $('code').each((_, el) => { $(el).replaceWith(`\`${$(el).text()}\``); });
72
+ $('strong, b').each((_, el) => { $(el).replaceWith(`**${$(el).text()}**`); });
73
+ $('em, i').each((_, el) => { $(el).replaceWith(`*${$(el).text()}*`); });
74
+ return $('div').text().replace(/\s+/g, ' ').trim();
75
+ }
76
+ /**
77
+ * Render a nested list.
78
+ *
79
+ * A list item carries its text as a `content` block array — usually a single
80
+ * `paragraph` — not as `text`. The block's own `text` is a pre-rendered
81
+ * string of the whole list ("1. …\n2. …"), which is why reading `item.text`
82
+ * produces empty bullets rather than an obvious error: every item has the
83
+ * field, and it is undefined on all of them.
84
+ *
85
+ * Nested lists arrive as further list blocks inside that same `content`, so
86
+ * they are rendered by recursing through {@link renderBlocks} with the depth
87
+ * carried in the indent.
88
+ */
89
+ function renderList(items, ordered, depth) {
90
+ const indent = ' '.repeat(depth);
91
+ return items
92
+ .map((item, index) => {
93
+ const bullet = ordered ? `${String(index + 1)}.` : '-';
94
+ const body = item.content !== undefined && item.content.length > 0
95
+ ? renderBlocks(item.content, depth + 1).trim()
96
+ : inlineText(item.text ?? '');
97
+ if (body === '') {
98
+ return '';
99
+ }
100
+ const [first = '', ...rest] = body.split('\n');
101
+ const continuation = rest.map(line => `${indent} ${line}`).join('\n');
102
+ return `${indent}${bullet} ${first}\n${continuation === '' ? '' : `${continuation}\n`}`;
103
+ })
104
+ .join('');
105
+ }
106
+ /**
107
+ * The blocks that map to one Markdown construct each.
108
+ *
109
+ * Split from {@link renderBlock} so the two nesting types — `callout` and
110
+ * `collapsibleSection`, which recurse — stay legible next to each other
111
+ * rather than at the bottom of one long switch. Returns null for anything it
112
+ * does not handle.
113
+ */
114
+ function renderSimpleBlock(block, depth) {
115
+ switch (block.type) {
116
+ case 'heading':
117
+ return `## ${inlineText(block.text ?? '')}\n\n`;
118
+ case 'subheading':
119
+ return `### ${inlineText(block.text ?? '')}\n\n`;
120
+ case 'paragraph': {
121
+ const text = inlineText(block.text ?? '');
122
+ return text === '' ? '' : `${text}\n\n`;
123
+ }
124
+ case 'orderedNestedList':
125
+ return `${renderList(block.items ?? [], true, depth)}\n`;
126
+ case 'unorderedNestedList':
127
+ return `${renderList(block.items ?? [], false, depth)}\n`;
128
+ case 'code':
129
+ return `\`\`\`\n${block.text ?? ''}\n\`\`\`\n\n`;
130
+ case 'horizontalRule':
131
+ return '---\n\n';
132
+ case 'image':
133
+ return block.url !== undefined ? `![](${block.url})\n\n` : '';
134
+ case undefined:
135
+ default:
136
+ return null;
137
+ }
138
+ }
139
+ /**
140
+ * Render one Intercom block.
141
+ *
142
+ * Ten types occur across the live corpus, not the four the integration notes
143
+ * listed. `callout` and `collapsibleSection` nest their body under `content`
144
+ * rather than `text`, and `subheading`, `unorderedNestedList`,
145
+ * `collapsibleSection`, `image`, `code` and `horizontalRule` would all be
146
+ * dropped by a renderer that only knew the four — silently, since a missing
147
+ * block leaves no trace in the output.
148
+ */
149
+ export function renderBlock(block, depth = 0) {
150
+ const simple = renderSimpleBlock(block, depth);
151
+ if (simple !== null) {
152
+ return simple;
153
+ }
154
+ switch (block.type) {
155
+ case 'callout':
156
+ // Rendered as a blockquote: a callout is emphasis, and losing it would
157
+ // turn "do not do this" into an ordinary sentence.
158
+ return `${renderBlocks(block.content ?? [])
159
+ .trimEnd()
160
+ .split('\n')
161
+ .map(line => `> ${line}`)
162
+ .join('\n')}\n\n`;
163
+ case 'collapsibleSection':
164
+ return `**${inlineText(block.summary ?? '')}**\n\n${renderBlocks(block.content ?? [])}`;
165
+ case undefined:
166
+ default:
167
+ // Unknown type: keep whatever text it has rather than dropping the
168
+ // block. Intercom adds types over time and silence is the worse
169
+ // failure — a reader cannot tell a missing paragraph from one that was
170
+ // never written.
171
+ return block.text !== undefined ? `${inlineText(block.text)}\n\n` : '';
172
+ }
173
+ }
174
+ export function renderBlocks(blocks, depth = 0) {
175
+ return blocks.map(block => renderBlock(block, depth)).join('');
176
+ }
177
+ /**
178
+ * Parse one Help Center article page.
179
+ *
180
+ * `articleContent.markdown` exists on every article and is `null` on every
181
+ * one measured — the body is in `blocks`. Reading the field that is named
182
+ * for what you want is the trap here.
183
+ */
184
+ export function parseIntercomArticle(html) {
185
+ const props = pageProps(html);
186
+ const article = props?.articleContent;
187
+ if (article === undefined) {
188
+ return null;
189
+ }
190
+ const blocks = (article.blocks ?? []);
191
+ // `breadcrumbs` is a sibling of `articleContent` under pageProps, not a key
192
+ // of it.
193
+ const crumbs = (props?.breadcrumbs ?? []);
194
+ return {
195
+ title: asString(article.title, 'Untitled'),
196
+ content: renderBlocks(blocks).trim(),
197
+ ...(typeof article.description === 'string' && article.description !== ''
198
+ ? { description: article.description } : {}),
199
+ ...(typeof article.lastUpdatedDate === 'string'
200
+ ? { lastUpdated: article.lastUpdatedDate.slice(0, 10) } : {}),
201
+ breadcrumb: crumbs
202
+ .map(crumb => crumb.label ?? crumb.name ?? '')
203
+ .filter(label => label !== ''),
204
+ };
205
+ }
206
+ function slugFromUrl(url) {
207
+ const last = url.replace(/\/$/, '').split('/').pop() ?? '';
208
+ // `/collections/12369024-jamf-pro` → `jamf-pro`. The numeric id is
209
+ // Intercom's and changes if a collection is recreated; the slug is what a
210
+ // reader recognises, so it is what publication ids are built from.
211
+ return last.replace(/^\d+-/, '');
212
+ }
213
+ /** The Help Center's top-level collections for one locale. */
214
+ export async function listIntercomCollections(ctx, source, locale) {
215
+ const key = cacheKey('intercom-collections', { source: source.id, locale });
216
+ const cached = await ctx.cache.get(key);
217
+ if (cached !== null) {
218
+ return cached;
219
+ }
220
+ const html = await httpGetText(`${source.baseUrl}/${locale}/`);
221
+ const props = pageProps(html);
222
+ const home = props?.home;
223
+ const collections = (home?.collections ?? []).map((collection) => {
224
+ const url = asString(collection.url);
225
+ return {
226
+ id: asString(collection.id),
227
+ slug: typeof collection.slug === 'string' && collection.slug !== ''
228
+ ? collection.slug
229
+ : slugFromUrl(url),
230
+ name: asString(collection.name),
231
+ description: asString(collection.description),
232
+ url,
233
+ articleCount: typeof collection.articleCount === 'number' ? collection.articleCount : 0,
234
+ };
235
+ });
236
+ await ctx.cache.set(key, collections, ctx.config.cacheTtl.products);
237
+ return collections;
238
+ }
239
+ /**
240
+ * The article tree of one collection.
241
+ *
242
+ * A collection page's `__NEXT_DATA__` carries the whole subtree —
243
+ * subcollections and every article summary — so one request answers what
244
+ * crawling 356 article pages would.
245
+ */
246
+ export async function fetchIntercomCollectionToc(ctx, source, collection) {
247
+ const key = cacheKey('intercom-collection-toc', { source: source.id, collection: collection.id });
248
+ const cached = await ctx.cache.get(key);
249
+ if (cached !== null) {
250
+ return cached;
251
+ }
252
+ const html = await httpGetText(collection.url);
253
+ const props = pageProps(html);
254
+ const raw = props?.collection;
255
+ const toEntries = (summaries) => (summaries ?? []).map(summary => ({
256
+ title: asString(summary.title, 'Untitled'),
257
+ url: asString(summary.url),
258
+ }));
259
+ const entries = [
260
+ // Articles that sit directly in the collection come first: they are the
261
+ // ones with no subcollection to file them under, and dropping them is
262
+ // the easy mistake — Jamf Pro has 11 of them beside 24 subcollections.
263
+ ...toEntries(raw?.articleSummaries),
264
+ ...(raw?.subcollections ?? []).map((sub) => {
265
+ const children = toEntries(sub.articleSummaries);
266
+ const entry = {
267
+ title: asString(sub.name, 'Untitled'),
268
+ url: asString(sub.url),
269
+ };
270
+ if (children.length > 0) {
271
+ entry.children = children;
272
+ }
273
+ return entry;
274
+ }),
275
+ ];
276
+ await ctx.cache.set(key, entries, ctx.config.cacheTtl.products);
277
+ return entries;
278
+ }
279
+ /**
280
+ * A `FetchTocResult` for one Intercom collection.
281
+ *
282
+ * Pagination and truncation match the other TOC paths, so a caller cannot
283
+ * tell from the response shape which kind of source answered.
284
+ */
285
+ export async function fetchIntercomToc(ctx, source, collection, options = {}) {
286
+ const page = options.page ?? PAGINATION_CONFIG.DEFAULT_PAGE;
287
+ const maxTokens = options.maxTokens ?? TOKEN_CONFIG.DEFAULT_MAX_TOKENS;
288
+ const allToc = await fetchIntercomCollectionToc(ctx, source, collection);
289
+ const count = (entries) => entries.reduce((total, entry) => total + 1 + (entry.children !== undefined ? count(entry.children) : 0), 0);
290
+ const serialise = (entry, depth = 0) => {
291
+ const indent = ' '.repeat(depth);
292
+ const children = entry.children?.map(child => serialise(child, depth + 1)).join('') ?? '';
293
+ return `${indent}- ${entry.title}\n${children}`;
294
+ };
295
+ const calc = calculatePagination(allToc.length, page, PAGINATION_CONFIG.DEFAULT_PAGE_SIZE);
296
+ const { items, tokenCount, truncated } = truncateListByTokens(allToc.slice(calc.startIndex, calc.endIndex), maxTokens, serialise);
297
+ const paginationNote = buildPaginationNote(calc);
298
+ return {
299
+ toc: items,
300
+ pagination: {
301
+ page: calc.page,
302
+ pageSize: calc.pageSize,
303
+ totalPages: calc.totalPages,
304
+ totalItems: count(allToc),
305
+ hasNext: calc.hasNext,
306
+ hasPrev: calc.hasPrev,
307
+ },
308
+ tokenInfo: { tokenCount, truncated, maxTokens },
309
+ ...(paginationNote !== undefined ? { paginationNote } : {}),
310
+ };
311
+ }
312
+ //# sourceMappingURL=intercom-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"intercom-service.js","sourceRoot":"","sources":["../../../src/core/services/intercom-service.ts"],"names":[],"mappings":"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"}
@@ -1 +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"}
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;AAEzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAa3E;;;;;;;;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"}
@@ -14,6 +14,8 @@ import { buildArticleView } from './article-view.js';
14
14
  import { extractSections } from './tokenizer.js';
15
15
  import { cacheKey } from './cache-key.js';
16
16
  import { TOKEN_CONFIG } from '../constants.js';
17
+ import { JamfDocsError, JamfDocsErrorCode } from '../types.js';
18
+ import { parseIntercomArticle } from './intercom-service.js';
17
19
  /**
18
20
  * Canonicalise a URL for a static site.
19
21
  *
@@ -77,6 +79,28 @@ export async function fetchStaticArticle(ctx, source, url, options = {}) {
77
79
  let cached = await ctx.cache.get(key);
78
80
  if (cached === null) {
79
81
  const html = await httpGetText(displayUrl);
82
+ // An Intercom Help Center's body is a block list in `__NEXT_DATA__`, not
83
+ // markup — no selector set can read it, so the parser is chosen per
84
+ // source rather than per page.
85
+ if (source.parser === 'intercom') {
86
+ const article = parseIntercomArticle(html);
87
+ if (article === null) {
88
+ throw new JamfDocsError(`Could not read a ${source.name} article at ${displayUrl}`, JamfDocsErrorCode.PARSE_ERROR);
89
+ }
90
+ cached = {
91
+ title: article.title,
92
+ parsed: {
93
+ title: article.title,
94
+ content: article.content,
95
+ breadcrumb: article.breadcrumb,
96
+ relatedArticles: [],
97
+ },
98
+ displayUrl,
99
+ ...(article.lastUpdated !== undefined ? { lastUpdated: article.lastUpdated } : {}),
100
+ };
101
+ await ctx.cache.set(key, cached, ctx.config.cacheTtl.article);
102
+ return renderStaticArticle(cached, source, options, maxTokens);
103
+ }
80
104
  const documentTitle = extractDocumentTitle(html);
81
105
  const parsed = parseArticle(html, displayUrl, {
82
106
  // The source's own markup rules. Parsing a static page with Fluid
@@ -98,6 +122,10 @@ export async function fetchStaticArticle(ctx, source, url, options = {}) {
98
122
  cached = { title, parsed, displayUrl };
99
123
  await ctx.cache.set(key, cached, ctx.config.cacheTtl.article);
100
124
  }
125
+ return renderStaticArticle(cached, source, options, maxTokens);
126
+ }
127
+ /** Assemble the response from a parsed page, whichever parser produced it. */
128
+ function renderStaticArticle(cached, source, options, maxTokens) {
101
129
  const { title, parsed } = cached;
102
130
  // Provenance is part of the content, not metadata: it has to survive
103
131
  // section extraction and truncation, both of which slice `content`, and a
@@ -111,6 +139,7 @@ export async function fetchStaticArticle(ctx, source, url, options = {}) {
111
139
  title,
112
140
  url: cached.displayUrl,
113
141
  product: source.name,
142
+ ...(cached.lastUpdated !== undefined ? { lastUpdated: cached.lastUpdated } : {}),
114
143
  breadcrumb: parsed.breadcrumb.length > 0 ? parsed.breadcrumb : undefined,
115
144
  relatedArticles: options.includeRelated === true && parsed.relatedArticles.length > 0
116
145
  ? parsed.relatedArticles
@@ -1 +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
+ {"version":3,"file":"static-article-service.js","sourceRoot":"","sources":["../../../src/core/services/static-article-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Searching the non-Fluid-Topics sources.
3
+ *
4
+ * These results are kept in their own block rather than merged into the
5
+ * Fluid Topics ranking. `SearchResult` has no score field and Fluid Topics
6
+ * does not return one — `search.ts` says so where it builds a result — so
7
+ * there is nothing on either side to fuse two orderings on. Inventing a
8
+ * comparable number would produce an interleaving that looks authoritative
9
+ * and is not. A labelled second block says exactly what it is: other places
10
+ * this query matched.
11
+ *
12
+ * The index is titles, recovered from each source's sitemap. That is one
13
+ * request per source for 808 support articles and 98 concepts pages, against
14
+ * ~900 page fetches to read the real headings — and a title is what a
15
+ * ranked pointer needs.
16
+ */
17
+ import { type StaticDocSource } from '../constants/sources.js';
18
+ import type { ServerContext } from '../types/context.js';
19
+ /** One page of a static source, as the index holds it. */
20
+ export interface StaticSearchEntry {
21
+ title: string;
22
+ url: string;
23
+ /** Display name of the source it belongs to. */
24
+ source: string;
25
+ }
26
+ /** A hit, with the source that produced it. */
27
+ export interface StaticSearchHit extends StaticSearchEntry {
28
+ /** Fuse's distance, 0 = exact. Comparable within this block only. */
29
+ score: number;
30
+ }
31
+ /** Build one source's title index for a locale. */
32
+ export declare function loadStaticIndex(ctx: ServerContext, source: StaticDocSource, locale: string): Promise<StaticSearchEntry[]>;
33
+ /**
34
+ * Search every static source that publishes the requested locale.
35
+ *
36
+ * Best-effort per source: one unreachable sitemap costs its own hits, not
37
+ * the block, and never the Fluid Topics results this runs alongside.
38
+ */
39
+ export declare function searchStaticSources(ctx: ServerContext, query: string, locale: string, limit?: number): Promise<StaticSearchHit[]>;
40
+ //# sourceMappingURL=static-search-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"static-search-service.d.ts","sourceRoot":"","sources":["../../../src/core/services/static-search-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAMH,OAAO,EAAsB,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAEnF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,0DAA0D;AAC1D,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,gDAAgD;IAChD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,+CAA+C;AAC/C,MAAM,WAAW,eAAgB,SAAQ,iBAAiB;IACxD,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAC;CACf;AAsCD,mDAAmD;AACnD,wBAAsB,eAAe,CACnC,GAAG,EAAE,aAAa,EAClB,MAAM,EAAE,eAAe,EACvB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAc9B;AAyCD;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,aAAa,EAClB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,KAAK,SAAI,GACR,OAAO,CAAC,eAAe,EAAE,CAAC,CAqB5B"}
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Searching the non-Fluid-Topics sources.
3
+ *
4
+ * These results are kept in their own block rather than merged into the
5
+ * Fluid Topics ranking. `SearchResult` has no score field and Fluid Topics
6
+ * does not return one — `search.ts` says so where it builds a result — so
7
+ * there is nothing on either side to fuse two orderings on. Inventing a
8
+ * comparable number would produce an interleaving that looks authoritative
9
+ * and is not. A labelled second block says exactly what it is: other places
10
+ * this query matched.
11
+ *
12
+ * The index is titles, recovered from each source's sitemap. That is one
13
+ * request per source for 808 support articles and 98 concepts pages, against
14
+ * ~900 page fetches to read the real headings — and a title is what a
15
+ * ranked pointer needs.
16
+ */
17
+ import Fuse from 'fuse.js';
18
+ import { httpGetText } from '../http-client.js';
19
+ import { cacheKey } from './cache-key.js';
20
+ import { titleFromSlug } from './sitemap-service.js';
21
+ import { STATIC_DOC_SOURCES } from '../constants/sources.js';
22
+ /**
23
+ * Path segments that are not articles.
24
+ *
25
+ * `browse` is in concepts.jamf.com's sitemap (ten entries, one per locale)
26
+ * and is a JS-only shell — thirteen characters once tags are stripped. The
27
+ * integration notes claimed these were absent from the sitemap and therefore
28
+ * excluded for free; they are not.
29
+ */
30
+ const NON_ARTICLE_SEGMENTS = new Set(['browse', 'about', 'ecosystem', 'collections']);
31
+ /**
32
+ * Turn a sitemap path into an index entry.
33
+ *
34
+ * Intercom article slugs are prefixed with the numeric id
35
+ * (`10631322-get-started-with-jamf-now`), which is stripped before casing —
36
+ * leaving it in produces "10631322 Get Started With Jamf Now".
37
+ */
38
+ function entryFor(source, url, locale) {
39
+ let segments;
40
+ try {
41
+ segments = new URL(url).pathname.split('/').filter(Boolean);
42
+ }
43
+ catch {
44
+ return null;
45
+ }
46
+ const [entryLocale, section, ...rest] = segments;
47
+ if (entryLocale !== locale) {
48
+ return null;
49
+ }
50
+ if (section === undefined || NON_ARTICLE_SEGMENTS.has(section)) {
51
+ return null;
52
+ }
53
+ // A section index page is a container, not an article.
54
+ if (rest.length === 0) {
55
+ return null;
56
+ }
57
+ const slug = (rest[rest.length - 1] ?? '').replace(/^\d+-/, '');
58
+ if (slug === '') {
59
+ return null;
60
+ }
61
+ return { title: titleFromSlug(slug), url, source: source.name };
62
+ }
63
+ /** Build one source's title index for a locale. */
64
+ export async function loadStaticIndex(ctx, source, locale) {
65
+ const key = cacheKey('static-search-index', { source: source.id, locale });
66
+ const cached = await ctx.cache.get(key);
67
+ if (cached !== null) {
68
+ return cached;
69
+ }
70
+ const xml = await httpGetText(`${source.baseUrl}/sitemap.xml`);
71
+ const entries = [];
72
+ for (const match of xml.matchAll(/<loc>\s*([^<\s]+)\s*<\/loc>/g)) {
73
+ const entry = entryFor(source, match[1] ?? '', locale);
74
+ if (entry !== null) {
75
+ entries.push(entry);
76
+ }
77
+ }
78
+ await ctx.cache.set(key, entries, ctx.config.cacheTtl.products);
79
+ return entries;
80
+ }
81
+ // ─── Fuse index, per server ─────────────────────────────────────
82
+ /**
83
+ * Per-server Fuse indexes, keyed `sourceId:locale`.
84
+ *
85
+ * Same shape as the glossary's: a WeakMap on the CacheProvider so each
86
+ * ServerContext gets its own and it is collected with the context, rather
87
+ * than a module-level map that would outlive a request in a runtime where
88
+ * module scope persists.
89
+ */
90
+ const indexByServer = new WeakMap();
91
+ const FUSE_OPTIONS = {
92
+ keys: [{ name: 'title', weight: 1 }],
93
+ threshold: 0.35,
94
+ includeScore: true,
95
+ ignoreLocation: true,
96
+ minMatchCharLength: 3,
97
+ };
98
+ function fuseFor(ctx, key, entries) {
99
+ let perServer = indexByServer.get(ctx.cache);
100
+ if (perServer === undefined) {
101
+ perServer = new Map();
102
+ indexByServer.set(ctx.cache, perServer);
103
+ }
104
+ const existing = perServer.get(key);
105
+ // Rebuild when the underlying array is a different one — the cache TTL
106
+ // expiring is what replaces it.
107
+ if (existing?.source === entries) {
108
+ return existing.fuse;
109
+ }
110
+ const fuse = new Fuse(entries, FUSE_OPTIONS);
111
+ perServer.set(key, { source: entries, fuse });
112
+ return fuse;
113
+ }
114
+ /**
115
+ * Search every static source that publishes the requested locale.
116
+ *
117
+ * Best-effort per source: one unreachable sitemap costs its own hits, not
118
+ * the block, and never the Fluid Topics results this runs alongside.
119
+ */
120
+ export async function searchStaticSources(ctx, query, locale, limit = 3) {
121
+ const log = ctx.logger.createLogger('static-search');
122
+ const hits = [];
123
+ for (const source of Object.values(STATIC_DOC_SOURCES)) {
124
+ const sourceLocale = source.locales[locale];
125
+ if (sourceLocale === undefined) {
126
+ continue;
127
+ }
128
+ try {
129
+ const entries = await loadStaticIndex(ctx, source, sourceLocale);
130
+ if (entries.length === 0) {
131
+ continue;
132
+ }
133
+ const results = fuseFor(ctx, `${source.id}:${sourceLocale}`, entries).search(query, { limit });
134
+ for (const result of results) {
135
+ hits.push({ ...result.item, score: result.score ?? 1 });
136
+ }
137
+ }
138
+ catch (error) {
139
+ log.warning(`Could not search ${source.name}: ${String(error)}`);
140
+ }
141
+ }
142
+ return hits;
143
+ }
144
+ //# sourceMappingURL=static-search-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"static-search-service.js","sourceRoot":"","sources":["../../../src/core/services/static-search-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;AAobzD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,aAAa,GAAG,IAAI,CAuJ9E"}
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;AA8gBzD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,aAAa,GAAG,IAAI,CA4I9E"}