@docsector/docsector-reader 1.1.0 β 1.2.1
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/README.md +7 -0
- package/bin/docsector.js +7 -1
- package/package.json +1 -1
- package/src/index.js +9 -0
- package/src/quasar.factory.js +221 -11
package/README.md
CHANGED
|
@@ -22,6 +22,7 @@ Transform Markdown content into beautiful, navigable documentation sites β wit
|
|
|
22
22
|
|
|
23
23
|
- π **Copy Page** β One-click button copies the current page as raw Markdown, ready to paste into LLMs
|
|
24
24
|
- π **View as Markdown** β Open any page as plain text by appending `.md` to the URL, with locale support (`?lang=`)
|
|
25
|
+
- π§ **Markdown Negotiation** β Requests with `Accept: text/markdown` receive markdown responses, while browsers keep HTML by default
|
|
25
26
|
- π€ **Open in ChatGPT / Claude** β One-click links to open the current page directly in ChatGPT or Claude for Q&A
|
|
26
27
|
- π€ **LLM Bot Detection** β Automatically serves raw Markdown to known AI crawlers (GPTBot, ClaudeBot, PerplexityBot, GrokBot, and others)
|
|
27
28
|
- πΊοΈ **Sitemap Generation** β Automatic `sitemap.xml` generation at build time with all page URLs (requires `siteUrl` in config)
|
|
@@ -46,6 +47,7 @@ Transform Markdown content into beautiful, navigable documentation sites β wit
|
|
|
46
47
|
- βοΈ **Edit on GitHub** β Direct links to edit pages on your repository
|
|
47
48
|
- π
**Last Updated Date** β Automatic per-page "last updated" date from git commit history, locale-formatted
|
|
48
49
|
- π **Translation Progress** β Automatic translation percentage based on header coverage
|
|
50
|
+
- π§ **Markdown Negotiation** β Responds with Markdown when clients send `Accept: text/markdown`, while keeping HTML as browser default
|
|
49
51
|
- π **Markdown Home at Root** β Homepage is rendered from `src/pages/Homepage.{lang}.md` directly at `/`
|
|
50
52
|
- π§ **Quick Links Custom Element** β Use `<d-quick-links>` and `<d-quick-link>` in Markdown to render rich home navigation cards
|
|
51
53
|
- ποΈ **API Catalog Well-Known** β Auto-generates `/.well-known/api-catalog` as Linkset JSON for machine-readable API discovery
|
|
@@ -369,6 +371,11 @@ export default {
|
|
|
369
371
|
items: []
|
|
370
372
|
},
|
|
371
373
|
|
|
374
|
+
markdownNegotiation: {
|
|
375
|
+
enabled: true,
|
|
376
|
+
agentFallback: true
|
|
377
|
+
},
|
|
378
|
+
|
|
372
379
|
languages: [
|
|
373
380
|
{ image: '/images/flags/united-states-of-america.png', label: 'English (US)', value: 'en-US' },
|
|
374
381
|
{ image: '/images/flags/brazil.png', label: 'PortuguΓͺs (BR)', value: 'pt-BR' }
|
package/bin/docsector.js
CHANGED
|
@@ -23,7 +23,7 @@ const packageRoot = resolve(__dirname, '..')
|
|
|
23
23
|
const args = process.argv.slice(2)
|
|
24
24
|
const command = args[0]
|
|
25
25
|
|
|
26
|
-
const VERSION = '1.1
|
|
26
|
+
const VERSION = '1.2.1'
|
|
27
27
|
|
|
28
28
|
const HELP = `
|
|
29
29
|
Docsector Reader v${VERSION}
|
|
@@ -149,6 +149,12 @@ export default {
|
|
|
149
149
|
// items: ['/mcp']
|
|
150
150
|
// },
|
|
151
151
|
|
|
152
|
+
// @ Markdown negotiation for agents (optional)
|
|
153
|
+
// markdownNegotiation: {
|
|
154
|
+
// enabled: true,
|
|
155
|
+
// agentFallback: true
|
|
156
|
+
// },
|
|
157
|
+
|
|
152
158
|
// @ Languages
|
|
153
159
|
languages: [
|
|
154
160
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@docsector/docsector-reader",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "A documentation rendering engine built with Vue 3, Quasar v2 and Vite. Transform Markdown into beautiful, navigable documentation sites.",
|
|
5
5
|
"productName": "Docsector Reader",
|
|
6
6
|
"author": "Rodrigo de Araujo Vieira",
|
package/src/index.js
CHANGED
|
@@ -54,6 +54,9 @@
|
|
|
54
54
|
* @param {boolean} [config.apiCatalog.enabled=true] - Enables generation of API catalog artifact
|
|
55
55
|
* @param {string} [config.apiCatalog.path='/.well-known/api-catalog'] - Output URI path for API catalog artifact
|
|
56
56
|
* @param {Array<string|{href: string}>} [config.apiCatalog.items=[]] - Additional API endpoint links to include as item relations
|
|
57
|
+
* @param {Object} [config.markdownNegotiation] - Markdown content negotiation settings for agents
|
|
58
|
+
* @param {boolean} [config.markdownNegotiation.enabled=true] - Enables markdown negotiation by Accept header in production runtime
|
|
59
|
+
* @param {boolean} [config.markdownNegotiation.agentFallback=true] - Enables markdown fallback for known AI bot user agents when Accept is absent
|
|
57
60
|
* @returns {Object} Resolved Docsector configuration
|
|
58
61
|
*/
|
|
59
62
|
export function createDocsector (config = {}) {
|
|
@@ -109,6 +112,12 @@ export function createDocsector (config = {}) {
|
|
|
109
112
|
path: '/.well-known/api-catalog',
|
|
110
113
|
items: [],
|
|
111
114
|
...config.apiCatalog
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
markdownNegotiation: {
|
|
118
|
+
enabled: true,
|
|
119
|
+
agentFallback: true,
|
|
120
|
+
...config.markdownNegotiation
|
|
112
121
|
}
|
|
113
122
|
}
|
|
114
123
|
}
|
package/src/quasar.factory.js
CHANGED
|
@@ -457,6 +457,38 @@ function createMarkdownEndpointPlugin (projectRoot) {
|
|
|
457
457
|
return null
|
|
458
458
|
}
|
|
459
459
|
|
|
460
|
+
function resolveNegotiatedFile (urlPath, lang) {
|
|
461
|
+
const pathname = (urlPath || '').split('?')[0]
|
|
462
|
+
|
|
463
|
+
if (pathname === '/' || pathname === '/index.html') {
|
|
464
|
+
const homepage = resolve(pagesDir, `Homepage.${lang}.md`)
|
|
465
|
+
return existsSync(homepage) ? homepage : null
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (pathname.endsWith('.md')) {
|
|
469
|
+
return resolveMarkdownFile(pathname, lang)
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
let clean = pathname
|
|
473
|
+
if (clean.endsWith('/index.html')) clean = clean.slice(0, -11)
|
|
474
|
+
if (clean.endsWith('/')) clean = clean.slice(0, -1)
|
|
475
|
+
|
|
476
|
+
if (!clean) {
|
|
477
|
+
const homepage = resolve(pagesDir, `Homepage.${lang}.md`)
|
|
478
|
+
return existsSync(homepage) ? homepage : null
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
if (clean.endsWith('/overview') || clean.endsWith('/showcase') || clean.endsWith('/vs')) {
|
|
482
|
+
return resolveMarkdownFile(`${clean}.md`, lang)
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
return resolveMarkdownFile(`${clean}/overview.md`, lang)
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function estimateMarkdownTokens (markdown = '') {
|
|
489
|
+
return Math.max(1, Math.ceil(markdown.length / 4))
|
|
490
|
+
}
|
|
491
|
+
|
|
460
492
|
// LLM bot user-agent patterns
|
|
461
493
|
const LLM_BOT_PATTERN = /GPTBot|ChatGPT-User|OAI-SearchBot|ClaudeBot|Claude-User|Claude-SearchBot|anthropic-ai|Google-Extended|Gemini-Deep-Research|PerplexityBot|Perplexity-User|Bytespider|CCBot|Meta-ExternalAgent|FacebookBot|Amazonbot|Applebot-Extended|cohere-ai|DuckAssistBot|GrokBot|AI2Bot|YouBot|PetalBot/i
|
|
462
494
|
|
|
@@ -466,6 +498,8 @@ function createMarkdownEndpointPlugin (projectRoot) {
|
|
|
466
498
|
configureServer (server) {
|
|
467
499
|
// Read default language from config
|
|
468
500
|
let defaultLang = 'en-US'
|
|
501
|
+
let markdownNegotiationEnabled = true
|
|
502
|
+
let markdownAgentFallback = true
|
|
469
503
|
try {
|
|
470
504
|
const configPath = resolve(projectRoot, 'docsector.config.js')
|
|
471
505
|
if (existsSync(configPath)) {
|
|
@@ -473,34 +507,56 @@ function createMarkdownEndpointPlugin (projectRoot) {
|
|
|
473
507
|
const configContent = readFileSync(configPath, 'utf-8')
|
|
474
508
|
const match = configContent.match(/defaultLanguage\s*:\s*['"]([^'"]+)['"]/)
|
|
475
509
|
if (match) defaultLang = match[1]
|
|
510
|
+
|
|
511
|
+
const enabledMatch = configContent.match(/markdownNegotiation\s*:\s*\{[\s\S]*?enabled\s*:\s*(true|false)/)
|
|
512
|
+
if (enabledMatch) markdownNegotiationEnabled = enabledMatch[1] === 'true'
|
|
513
|
+
|
|
514
|
+
const fallbackMatch = configContent.match(/markdownNegotiation\s*:\s*\{[\s\S]*?agentFallback\s*:\s*(true|false)/)
|
|
515
|
+
if (fallbackMatch) markdownAgentFallback = fallbackMatch[1] === 'true'
|
|
476
516
|
}
|
|
477
517
|
} catch { /* use fallback */ }
|
|
478
518
|
|
|
479
519
|
server.middlewares.use((req, res, next) => {
|
|
480
520
|
const url = new URL(req.url, 'http://localhost')
|
|
521
|
+
const accept = (req.headers.accept || '').toLowerCase()
|
|
522
|
+
const wantsMarkdown = accept.includes('text/markdown')
|
|
523
|
+
const lang = url.searchParams.get('lang') || defaultLang
|
|
481
524
|
|
|
482
525
|
// Explicit .md request
|
|
483
526
|
if (url.pathname.endsWith('.md')) {
|
|
484
|
-
const lang = url.searchParams.get('lang') || defaultLang
|
|
485
527
|
const file = resolveMarkdownFile(url.pathname, lang)
|
|
486
528
|
if (!file) return next()
|
|
487
529
|
|
|
488
530
|
const content = readFileSync(file, 'utf-8')
|
|
489
531
|
res.setHeader('Content-Type', 'text/markdown; charset=utf-8')
|
|
532
|
+
res.setHeader('Vary', 'Accept')
|
|
533
|
+
res.setHeader('x-markdown-tokens', String(estimateMarkdownTokens(content)))
|
|
490
534
|
res.end(content)
|
|
491
535
|
return
|
|
492
536
|
}
|
|
493
537
|
|
|
538
|
+
// Content negotiation for agents requesting markdown explicitly
|
|
539
|
+
if (markdownNegotiationEnabled && wantsMarkdown) {
|
|
540
|
+
const file = resolveNegotiatedFile(url.pathname, lang)
|
|
541
|
+
if (file) {
|
|
542
|
+
const content = readFileSync(file, 'utf-8')
|
|
543
|
+
res.setHeader('Content-Type', 'text/markdown; charset=utf-8')
|
|
544
|
+
res.setHeader('Vary', 'Accept')
|
|
545
|
+
res.setHeader('x-markdown-tokens', String(estimateMarkdownTokens(content)))
|
|
546
|
+
res.end(content)
|
|
547
|
+
return
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
494
551
|
// Auto-serve markdown to LLM bot crawlers
|
|
495
552
|
const ua = req.headers['user-agent'] || ''
|
|
496
|
-
if (LLM_BOT_PATTERN.test(ua)) {
|
|
497
|
-
const
|
|
498
|
-
// Try appending /overview as the default subpage
|
|
499
|
-
const mdPath = url.pathname.replace(/\/$/, '') + '/overview.md'
|
|
500
|
-
const file = resolveMarkdownFile(mdPath, lang)
|
|
553
|
+
if (markdownAgentFallback && LLM_BOT_PATTERN.test(ua)) {
|
|
554
|
+
const file = resolveNegotiatedFile(url.pathname, lang)
|
|
501
555
|
if (file) {
|
|
502
556
|
const content = readFileSync(file, 'utf-8')
|
|
503
557
|
res.setHeader('Content-Type', 'text/markdown; charset=utf-8')
|
|
558
|
+
res.setHeader('Vary', 'Accept')
|
|
559
|
+
res.setHeader('x-markdown-tokens', String(estimateMarkdownTokens(content)))
|
|
504
560
|
res.end(content)
|
|
505
561
|
return
|
|
506
562
|
}
|
|
@@ -563,6 +619,25 @@ function createMarkdownBuildPlugin (projectRoot) {
|
|
|
563
619
|
}
|
|
564
620
|
}
|
|
565
621
|
|
|
622
|
+
// Generate homepage markdown files so root content can be negotiated in production
|
|
623
|
+
const languageValues = config.languages?.map(language => language.value).filter(Boolean) || []
|
|
624
|
+
const allLangs = [...new Set([defaultLang, ...languageValues])]
|
|
625
|
+
let homepageCount = 0
|
|
626
|
+
for (const lang of allLangs) {
|
|
627
|
+
const homepageSrc = resolve(pagesDir, `Homepage.${lang}.md`)
|
|
628
|
+
if (!existsSync(homepageSrc)) continue
|
|
629
|
+
|
|
630
|
+
const homepageContent = readFileSync(homepageSrc, 'utf-8')
|
|
631
|
+
writeFileSync(resolve(distDir, `homepage.${lang}.md`), homepageContent)
|
|
632
|
+
if (lang === defaultLang) {
|
|
633
|
+
writeFileSync(resolve(distDir, 'homepage.md'), homepageContent)
|
|
634
|
+
}
|
|
635
|
+
homepageCount++
|
|
636
|
+
}
|
|
637
|
+
if (homepageCount > 0) {
|
|
638
|
+
console.log(`\x1b[36m[docsector]\x1b[0m Generated ${homepageCount} homepage markdown file(s)`)
|
|
639
|
+
}
|
|
640
|
+
|
|
566
641
|
console.log(`\x1b[36m[docsector]\x1b[0m Generated ${count} static .md files`)
|
|
567
642
|
|
|
568
643
|
// Generate sitemap.xml if siteUrl is configured
|
|
@@ -663,6 +738,103 @@ function createMarkdownBuildPlugin (projectRoot) {
|
|
|
663
738
|
} else {
|
|
664
739
|
writeFileSync(headersPath, headersRule)
|
|
665
740
|
}
|
|
741
|
+
|
|
742
|
+
const markdownNegotiationConfig = config.markdownNegotiation || {}
|
|
743
|
+
const markdownNegotiationEnabled = markdownNegotiationConfig.enabled !== false
|
|
744
|
+
const markdownAgentFallback = markdownNegotiationConfig.agentFallback !== false
|
|
745
|
+
|
|
746
|
+
if (markdownNegotiationEnabled) {
|
|
747
|
+
const functionsDir = resolve(projectRoot, 'functions')
|
|
748
|
+
mkdirSync(functionsDir, { recursive: true })
|
|
749
|
+
|
|
750
|
+
const middlewareCode = `const LLM_BOT_PATTERN = /GPTBot|ChatGPT-User|OAI-SearchBot|ClaudeBot|Claude-User|Claude-SearchBot|anthropic-ai|Google-Extended|Gemini-Deep-Research|PerplexityBot|Perplexity-User|Bytespider|CCBot|Meta-ExternalAgent|FacebookBot|Amazonbot|Applebot-Extended|cohere-ai|DuckAssistBot|GrokBot|AI2Bot|YouBot|PetalBot/i
|
|
751
|
+
|
|
752
|
+
const DEFAULT_LANG = ${JSON.stringify(defaultLang)}
|
|
753
|
+
const AGENT_FALLBACK = ${markdownAgentFallback ? 'true' : 'false'}
|
|
754
|
+
|
|
755
|
+
function wantsMarkdown (request) {
|
|
756
|
+
const accept = (request.headers.get('accept') || '').toLowerCase()
|
|
757
|
+
return accept.includes('text/markdown')
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
function estimateTokens (markdown = '') {
|
|
761
|
+
return Math.max(1, Math.ceil(markdown.length / 4))
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
function shouldBypass (pathname) {
|
|
765
|
+
if (pathname === '/mcp' || pathname.startsWith('/mcp/')) return true
|
|
766
|
+
if (pathname.startsWith('/.well-known/')) return true
|
|
767
|
+
return /\\.(js|css|map|png|jpg|jpeg|gif|webp|svg|ico|woff2?|ttf|eot|xml|json|txt)$/i.test(pathname)
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
function resolveMarkdownPath (pathname, lang) {
|
|
771
|
+
if (!pathname) return null
|
|
772
|
+
if (pathname.endsWith('.md')) return pathname
|
|
773
|
+
if (pathname === '/' || pathname === '/index.html') return '/homepage.' + lang + '.md'
|
|
774
|
+
|
|
775
|
+
let clean = pathname
|
|
776
|
+
if (clean.endsWith('/index.html')) clean = clean.slice(0, -11)
|
|
777
|
+
if (clean.endsWith('/')) clean = clean.slice(0, -1)
|
|
778
|
+
|
|
779
|
+
if (!clean) return '/homepage.' + lang + '.md'
|
|
780
|
+
if (clean.endsWith('/overview') || clean.endsWith('/showcase') || clean.endsWith('/vs')) {
|
|
781
|
+
return clean + '.md'
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
return clean + '/overview.md'
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
export async function onRequest (context) {
|
|
788
|
+
const { request, env, next } = context
|
|
789
|
+
|
|
790
|
+
if (request.method !== 'GET' && request.method !== 'HEAD') {
|
|
791
|
+
return next()
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
const url = new URL(request.url)
|
|
795
|
+
if (shouldBypass(url.pathname)) {
|
|
796
|
+
return next()
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
const ua = request.headers.get('user-agent') || ''
|
|
800
|
+
const acceptMarkdown = wantsMarkdown(request)
|
|
801
|
+
const fallbackMarkdown = AGENT_FALLBACK && LLM_BOT_PATTERN.test(ua)
|
|
802
|
+
if (!acceptMarkdown && !fallbackMarkdown) {
|
|
803
|
+
return next()
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
const lang = url.searchParams.get('lang') || DEFAULT_LANG
|
|
807
|
+
const markdownPath = resolveMarkdownPath(url.pathname, lang)
|
|
808
|
+
if (!markdownPath) {
|
|
809
|
+
return next()
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
const markdownUrl = new URL(url.toString())
|
|
813
|
+
markdownUrl.pathname = markdownPath
|
|
814
|
+
const markdownRequest = new Request(markdownUrl.toString(), request)
|
|
815
|
+
const markdownResponse = await env.ASSETS.fetch(markdownRequest)
|
|
816
|
+
if (!markdownResponse.ok) {
|
|
817
|
+
return next()
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
const markdown = await markdownResponse.text()
|
|
821
|
+
const headers = new Headers(markdownResponse.headers)
|
|
822
|
+
headers.set('Content-Type', 'text/markdown; charset=utf-8')
|
|
823
|
+
const vary = headers.get('Vary')
|
|
824
|
+
headers.set('Vary', vary ? vary + ', Accept' : 'Accept')
|
|
825
|
+
headers.set('x-markdown-tokens', String(estimateTokens(markdown)))
|
|
826
|
+
|
|
827
|
+
if (request.method === 'HEAD') {
|
|
828
|
+
return new Response(null, { status: markdownResponse.status, headers })
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
return new Response(markdown, { status: markdownResponse.status, headers })
|
|
832
|
+
}
|
|
833
|
+
`
|
|
834
|
+
|
|
835
|
+
writeFileSync(resolve(functionsDir, '_middleware.js'), middlewareCode)
|
|
836
|
+
console.log(`\x1b[36m[docsector]\x1b[0m Generated markdown negotiation middleware at functions/_middleware.js`)
|
|
837
|
+
}
|
|
666
838
|
console.log(`\x1b[36m[docsector]\x1b[0m Added _headers rule for .md files`)
|
|
667
839
|
|
|
668
840
|
// Add homepage Link headers for agent discovery (RFC 8288 / RFC 9727)
|
|
@@ -795,12 +967,12 @@ function createMarkdownBuildPlugin (projectRoot) {
|
|
|
795
967
|
|
|
796
968
|
const headersWithLinks = readFileSync(headersPath, 'utf-8')
|
|
797
969
|
if (!headersWithLinks.includes(catalogHref)) {
|
|
798
|
-
const apiCatalogHeaders = `${catalogHref}\n Content-Type: application/linkset+json; profile
|
|
970
|
+
const apiCatalogHeaders = `${catalogHref}\n Content-Type: application/linkset+json; profile="https://www.rfc-editor.org/info/rfc9727"\n`
|
|
799
971
|
writeFileSync(headersPath, headersWithLinks.trimEnd() + '\n\n' + apiCatalogHeaders)
|
|
800
972
|
console.log(`\x1b[36m[docsector]\x1b[0m Added _headers rule for ${catalogHref}`)
|
|
801
973
|
}
|
|
802
974
|
} else {
|
|
803
|
-
console.warn(`\x1b[33m[docsector]\x1b[0m Skipped API catalog generation: path must be a local URI path, got
|
|
975
|
+
console.warn(`\x1b[33m[docsector]\x1b[0m Skipped API catalog generation: path must be a local URI path, got "${apiCatalogPath}"`)
|
|
804
976
|
}
|
|
805
977
|
}
|
|
806
978
|
}
|
|
@@ -874,7 +1046,10 @@ function createMarkdownBuildPlugin (projectRoot) {
|
|
|
874
1046
|
writeFileSync(headersPath, currentHeaders.trimEnd() + '\n\n' + mcpHeaders)
|
|
875
1047
|
}
|
|
876
1048
|
|
|
877
|
-
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
// Generate or merge _routes.json for Cloudflare Pages functions
|
|
1052
|
+
if (config.mcp || markdownNegotiationEnabled) {
|
|
878
1053
|
const routesPath = resolve(distDir, '_routes.json')
|
|
879
1054
|
let routes = { version: 1, include: [], exclude: [] }
|
|
880
1055
|
if (existsSync(routesPath)) {
|
|
@@ -884,11 +1059,46 @@ function createMarkdownBuildPlugin (projectRoot) {
|
|
|
884
1059
|
// empty
|
|
885
1060
|
}
|
|
886
1061
|
}
|
|
887
|
-
|
|
1062
|
+
|
|
1063
|
+
if (markdownNegotiationEnabled && !routes.include.includes('/*')) {
|
|
1064
|
+
routes.include.push('/*')
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
if (config.mcp && !markdownNegotiationEnabled && !routes.include.includes('/mcp')) {
|
|
888
1068
|
routes.include.push('/mcp')
|
|
889
1069
|
}
|
|
1070
|
+
|
|
1071
|
+
// Cloudflare Pages rejects overlapping include rules (e.g. "/mcp" with "/*").
|
|
1072
|
+
// Keep only the catch-all when markdown negotiation is enabled.
|
|
1073
|
+
if (routes.include.includes('/*')) {
|
|
1074
|
+
routes.include = routes.include.filter((route) => route !== '/mcp')
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
const markdownExcludes = [
|
|
1078
|
+
'/assets/*',
|
|
1079
|
+
'/*.js',
|
|
1080
|
+
'/*.css',
|
|
1081
|
+
'/*.png',
|
|
1082
|
+
'/*.jpg',
|
|
1083
|
+
'/*.jpeg',
|
|
1084
|
+
'/*.gif',
|
|
1085
|
+
'/*.webp',
|
|
1086
|
+
'/*.svg',
|
|
1087
|
+
'/*.ico',
|
|
1088
|
+
'/*.woff',
|
|
1089
|
+
'/*.woff2',
|
|
1090
|
+
'/*.ttf',
|
|
1091
|
+
'/*.map'
|
|
1092
|
+
]
|
|
1093
|
+
|
|
1094
|
+
for (const excludePath of markdownExcludes) {
|
|
1095
|
+
if (!routes.exclude.includes(excludePath)) {
|
|
1096
|
+
routes.exclude.push(excludePath)
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
|
|
890
1100
|
writeFileSync(routesPath, JSON.stringify(routes, null, 2))
|
|
891
|
-
console.log(`\x1b[36m[docsector]\x1b[0m
|
|
1101
|
+
console.log(`\x1b[36m[docsector]\x1b[0m Updated _routes.json for functions runtime`)
|
|
892
1102
|
}
|
|
893
1103
|
}
|
|
894
1104
|
}
|