@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.
- package/dist/core/constants/sources.d.ts +68 -2
- package/dist/core/constants/sources.d.ts.map +1 -1
- package/dist/core/constants/sources.js +47 -0
- package/dist/core/constants/sources.js.map +1 -1
- package/dist/core/schemas/output.d.ts +5 -0
- package/dist/core/schemas/output.d.ts.map +1 -1
- package/dist/core/schemas/output.js +15 -0
- package/dist/core/schemas/output.js.map +1 -1
- package/dist/core/services/cache-key.d.ts +15 -0
- package/dist/core/services/cache-key.d.ts.map +1 -1
- package/dist/core/services/cache-key.js +3 -0
- package/dist/core/services/cache-key.js.map +1 -1
- package/dist/core/services/intercom-service.d.ts +85 -0
- package/dist/core/services/intercom-service.d.ts.map +1 -0
- package/dist/core/services/intercom-service.js +312 -0
- package/dist/core/services/intercom-service.js.map +1 -0
- package/dist/core/services/static-article-service.d.ts.map +1 -1
- package/dist/core/services/static-article-service.js +29 -0
- package/dist/core/services/static-article-service.js.map +1 -1
- package/dist/core/services/static-search-service.d.ts +40 -0
- package/dist/core/services/static-search-service.d.ts.map +1 -0
- package/dist/core/services/static-search-service.js +144 -0
- package/dist/core/services/static-search-service.js.map +1 -0
- package/dist/core/tools/get-toc.d.ts.map +1 -1
- package/dist/core/tools/get-toc.js +70 -8
- package/dist/core/tools/get-toc.js.map +1 -1
- package/dist/core/tools/list-products.d.ts.map +1 -1
- package/dist/core/tools/list-products.js +25 -2
- package/dist/core/tools/list-products.js.map +1 -1
- package/dist/core/tools/search.d.ts.map +1 -1
- package/dist/core/tools/search.js +53 -2
- package/dist/core/tools/search.js.map +1 -1
- 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 ? `\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":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAI1C,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAClE,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,gBAAgB,CAAC;AAExB,mEAAmE;AAEnE;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,MAAM,KAAK,GAAG,sDAAsD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChF,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,SAAS,EAAE,CAAC;QAAC,OAAO,IAAI,CAAC;IAAC,CAAC;IAC9C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAA4B,CAAC;IACzD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,uEAAuE;AACvE,SAAS,SAAS,CAAC,IAAY;IAC7B,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACjC,MAAM,KAAK,GAAI,IAAI,EAAE,KAA6C,EAAE,SAAS,CAAC;IAC9E,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAChD,CAAC,CAAC,KAAgC;QAClC,CAAC,CAAC,IAAI,CAAC;AACX,CAAC;AAED;;;;;;GAMG;AACH,SAAS,QAAQ,CAAC,KAAc,EAAE,QAAQ,GAAG,EAAE;IAC7C,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAAC,OAAO,KAAK,CAAC;IAAC,CAAC;IAChD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAAC,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IAAC,CAAC;IACxD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAcD,4EAA4E;AAC5E,SAAS,UAAU,CAAC,IAAY;IAC9B,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,CAAC;IAC7C,CAAC,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE;QAC1B,MAAM,IAAI,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACtC,MAAM,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAC3B,IAAI,IAAI,KAAK,EAAE,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;YAAC,CAAC,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,IAAI,KAAK,KAAK,IAAI,GAAG,CAAC,CAAC;QAAC,CAAC;IAChF,CAAC,CAAC,CAAC;IACH,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACzE,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9E,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACxE,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AACrD,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,SAAS,UAAU,CAAC,KAAsB,EAAE,OAAgB,EAAE,KAAa;IACzE,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAClC,OAAO,KAAK;SACT,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QACnB,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;QACvD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,KAAK,SAAS,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;YAChE,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE;YAC9C,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QAChC,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;YAAC,OAAO,EAAE,CAAC;QAAC,CAAC;QAC/B,MAAM,CAAC,KAAK,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC/C,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,MAAM,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvE,OAAO,GAAG,MAAM,GAAG,MAAM,IAAI,KAAK,KAAK,YAAY,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,YAAY,IAAI,EAAE,CAAC;IAC1F,CAAC,CAAC;SACD,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,iBAAiB,CAAC,KAAoB,EAAE,KAAa;IAC5D,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,SAAS;YACZ,OAAO,MAAM,UAAU,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,MAAM,CAAC;QAClD,KAAK,YAAY;YACf,OAAO,OAAO,UAAU,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,MAAM,CAAC;QACnD,KAAK,WAAW,CAAC,CAAC,CAAC;YACjB,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;YAC1C,OAAO,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,MAAM,CAAC;QAC1C,CAAC;QACD,KAAK,mBAAmB;YACtB,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC;QAC3D,KAAK,qBAAqB;YACxB,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC;QAC5D,KAAK,MAAM;YACT,OAAO,WAAW,KAAK,CAAC,IAAI,IAAI,EAAE,cAAc,CAAC;QACnD,KAAK,gBAAgB;YACnB,OAAO,SAAS,CAAC;QACnB,KAAK,OAAO;YACV,OAAO,KAAK,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QAChE,KAAK,SAAS,CAAC;QACf;YACE,OAAO,IAAI,CAAC;IAChB,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,WAAW,CAAC,KAAoB,EAAE,KAAK,GAAG,CAAC;IACzD,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC/C,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QAAC,OAAO,MAAM,CAAC;IAAC,CAAC;IAEvC,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,SAAS;YACZ,uEAAuE;YACvE,mDAAmD;YACnD,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC;iBACxC,OAAO,EAAE;iBACT,KAAK,CAAC,IAAI,CAAC;iBACX,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC;iBACxB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;QACtB,KAAK,oBAAoB;YACvB,OAAO,KAAK,UAAU,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,SAAS,YAAY,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,CAAC;QAC1F,KAAK,SAAS,CAAC;QACf;YACE,mEAAmE;YACnE,gEAAgE;YAChE,uEAAuE;YACvE,iBAAiB;YACjB,OAAO,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3E,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAuB,EAAE,KAAK,GAAG,CAAC;IAC7D,OAAO,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACjE,CAAC;AAYD;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAC9B,MAAM,OAAO,GAAG,KAAK,EAAE,cAAqD,CAAC;IAC7E,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAAC,OAAO,IAAI,CAAC;IAAC,CAAC;IAE3C,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAoB,CAAC;IACzD,4EAA4E;IAC5E,SAAS;IACT,MAAM,MAAM,GAAG,CAAC,KAAK,EAAE,WAAW,IAAI,EAAE,CAAwC,CAAC;IAEjF,OAAO;QACL,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,UAAU,CAAC;QAC1C,OAAO,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE;QACpC,GAAG,CAAC,OAAO,OAAO,CAAC,WAAW,KAAK,QAAQ,IAAI,OAAO,CAAC,WAAW,KAAK,EAAE;YACvE,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9C,GAAG,CAAC,OAAO,OAAO,CAAC,eAAe,KAAK,QAAQ;YAC7C,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/D,UAAU,EAAE,MAAM;aACf,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC;aAC7C,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,KAAK,EAAE,CAAC;KACjC,CAAC;AACJ,CAAC;AAwBD,SAAS,WAAW,CAAC,GAAW;IAC9B,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;IAC3D,mEAAmE;IACnE,0EAA0E;IAC1E,mEAAmE;IACnE,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;AACnC,CAAC;AAED,8DAA8D;AAC9D,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,GAAkB,EAClB,MAAuB,EACvB,MAAc;IAEd,MAAM,GAAG,GAAG,QAAQ,CAAC,sBAAsB,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IAC5E,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,KAAK,CAAC,GAAG,CAAuB,GAAG,CAAC,CAAC;IAC9D,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QAAC,OAAO,MAAM,CAAC;IAAC,CAAC;IAEvC,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,GAAG,MAAM,CAAC,OAAO,IAAI,MAAM,GAAG,CAAC,CAAC;IAC/D,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAC9B,MAAM,IAAI,GAAG,KAAK,EAAE,IAAqD,CAAC;IAC1E,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,WAAW,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,UAAU,EAAsB,EAAE;QACnF,MAAM,GAAG,GAAG,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QACrC,OAAO;YACL,EAAE,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;YAC3B,IAAI,EAAE,OAAO,UAAU,CAAC,IAAI,KAAK,QAAQ,IAAI,UAAU,CAAC,IAAI,KAAK,EAAE;gBACjE,CAAC,CAAC,UAAU,CAAC,IAAI;gBACjB,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC;YACpB,IAAI,EAAE,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC;YAC/B,WAAW,EAAE,QAAQ,CAAC,UAAU,CAAC,WAAW,CAAC;YAC7C,GAAG;YACH,YAAY,EAAE,OAAO,UAAU,CAAC,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;SACxF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,MAAM,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACpE,OAAO,WAAW,CAAC;AACrB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,GAAkB,EAClB,MAAuB,EACvB,UAA8B;IAE9B,MAAM,GAAG,GAAG,QAAQ,CAAC,yBAAyB,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,UAAU,EAAE,UAAU,CAAC,EAAE,EAAE,CAAC,CAAC;IAClG,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,KAAK,CAAC,GAAG,CAAa,GAAG,CAAC,CAAC;IACpD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QAAC,OAAO,MAAM,CAAC;IAAC,CAAC;IAEvC,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IAC/C,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAC9B,MAAM,GAAG,GAAG,KAAK,EAAE,UAAuC,CAAC;IAE3D,MAAM,SAAS,GAAG,CAAC,SAA2D,EAAc,EAAE,CAC5F,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAChC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,UAAU,CAAC;QAC1C,GAAG,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC;KAC3B,CAAC,CAAC,CAAC;IAEN,MAAM,OAAO,GAAe;QAC1B,wEAAwE;QACxE,sEAAsE;QACtE,uEAAuE;QACvE,GAAG,SAAS,CAAC,GAAG,EAAE,gBAAgB,CAAC;QACnC,GAAG,CAAC,GAAG,EAAE,cAAc,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAY,EAAE;YACnD,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;YACjD,MAAM,KAAK,GAAa;gBACtB,KAAK,EAAE,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,UAAU,CAAC;gBACrC,GAAG,EAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC;aACvB,CAAC;YACF,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAAC,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAC;YAAC,CAAC;YACvD,OAAO,KAAK,CAAC;QACf,CAAC,CAAC;KACH,CAAC;IAEF,MAAM,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAChE,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,GAAkB,EAClB,MAAuB,EACvB,UAA8B,EAC9B,UAA2B,EAAE;IAE7B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,iBAAiB,CAAC,YAAY,CAAC;IAC5D,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,YAAY,CAAC,kBAAkB,CAAC;IAEvE,MAAM,MAAM,GAAG,MAAM,0BAA0B,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;IAEzE,MAAM,KAAK,GAAG,CAAC,OAAmB,EAAU,EAAE,CAAC,OAAO,CAAC,MAAM,CAC3D,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC/F,MAAM,SAAS,GAAG,CAAC,KAAe,EAAE,KAAK,GAAG,CAAC,EAAU,EAAE;QACvD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAClC,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;QAC1F,OAAO,GAAG,MAAM,KAAK,KAAK,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;IAClD,CAAC,CAAC;IAEF,MAAM,IAAI,GAAG,mBAAmB,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,iBAAiB,CAAC,iBAAiB,CAAC,CAAC;IAC3F,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,oBAAoB,CAC3D,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;IACtE,MAAM,cAAc,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAEjD,OAAO;QACL,GAAG,EAAE,KAAK;QACV,UAAU,EAAE;YACV,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC;YACzB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,OAAO,EAAE,IAAI,CAAC,OAAO;SACtB;QACD,SAAS,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE;QAC/C,GAAG,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC5D,CAAC;AACJ,CAAC"}
|
|
@@ -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;
|
|
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":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;
|
|
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;
|
|
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"}
|