@docsector/docsector-reader 1.0.0 β 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -1
- package/bin/docsector.js +9 -1
- package/package.json +1 -1
- package/src/index.js +13 -0
- package/src/quasar.factory.js +104 -10
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ Transform Markdown content into beautiful, navigable documentation sites β wit
|
|
|
26
26
|
- π€ **LLM Bot Detection** β Automatically serves raw Markdown to known AI crawlers (GPTBot, ClaudeBot, PerplexityBot, GrokBot, and others)
|
|
27
27
|
- πΊοΈ **Sitemap Generation** β Automatic `sitemap.xml` generation at build time with all page URLs (requires `siteUrl` in config)
|
|
28
28
|
- π€ **AI-Friendly robots.txt** β Scaffold includes a `robots.txt` explicitly allowing 23 AI crawlers (GPTBot, ClaudeBot, PerplexityBot, GrokBot, etc.)
|
|
29
|
-
- π **Homepage Link Headers** β Auto-generated `Link` response headers for agent discovery (`service-doc`, `service-desc`, `describedby`) per RFC 8288 / RFC 9727
|
|
29
|
+
- π **Homepage Link Headers** β Auto-generated `Link` response headers for agent discovery (`api-catalog`, `service-doc`, `service-desc`, `describedby`) per RFC 8288 / RFC 9727
|
|
30
30
|
- π **MCP Server** β Auto-generated [MCP](https://modelcontextprotocol.io) server at `/mcp` for AI assistant integration (Claude Desktop, VS Code, etc.)
|
|
31
31
|
- π **llms.txt / llms-full.txt** β Auto-generated [llms.txt](https://llmstxt.org) index and full-content file for LLM discovery (requires `siteUrl` in config)
|
|
32
32
|
|
|
@@ -48,6 +48,7 @@ Transform Markdown content into beautiful, navigable documentation sites β wit
|
|
|
48
48
|
- π **Translation Progress** β Automatic translation percentage based on header coverage
|
|
49
49
|
- π **Markdown Home at Root** β Homepage is rendered from `src/pages/Homepage.{lang}.md` directly at `/`
|
|
50
50
|
- π§ **Quick Links Custom Element** β Use `<d-quick-links>` and `<d-quick-link>` in Markdown to render rich home navigation cards
|
|
51
|
+
- ποΈ **API Catalog Well-Known** β Auto-generates `/.well-known/api-catalog` as Linkset JSON for machine-readable API discovery
|
|
51
52
|
- βοΈ **Single Config File** β Customize branding, links, and languages via `docsector.config.js`
|
|
52
53
|
|
|
53
54
|
---
|
|
@@ -162,6 +163,7 @@ Docsector Reader adds homepage `Link` response headers at build time for agent d
|
|
|
162
163
|
|
|
163
164
|
Default relations emitted on homepage (`/` and `/index.html`):
|
|
164
165
|
|
|
166
|
+
- `rel="api-catalog"` β `</.well-known/api-catalog>`
|
|
165
167
|
- `rel="service-doc"` β `</>`
|
|
166
168
|
- `rel="service-desc"` β `</mcp>` (only when `mcp` is enabled)
|
|
167
169
|
- `rel="describedby"` β `</llms.txt>` (only when `siteUrl` is configured, i.e. `llms.txt` is generated)
|
|
@@ -169,6 +171,7 @@ Default relations emitted on homepage (`/` and `/index.html`):
|
|
|
169
171
|
Generated in:
|
|
170
172
|
|
|
171
173
|
- `dist/spa/_headers`
|
|
174
|
+
- `dist/spa/.well-known/api-catalog` (Linkset JSON)
|
|
172
175
|
|
|
173
176
|
### Optional configuration
|
|
174
177
|
|
|
@@ -178,9 +181,19 @@ export default {
|
|
|
178
181
|
|
|
179
182
|
linkHeaders: {
|
|
180
183
|
enabled: true,
|
|
184
|
+
apiCatalog: '/.well-known/api-catalog',
|
|
181
185
|
serviceDoc: '/',
|
|
182
186
|
serviceDesc: '/mcp',
|
|
183
187
|
describedBy: '/llms.txt'
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
apiCatalog: {
|
|
191
|
+
enabled: true,
|
|
192
|
+
path: '/.well-known/api-catalog',
|
|
193
|
+
items: [
|
|
194
|
+
'/mcp',
|
|
195
|
+
'https://api.example.com/openapi.json'
|
|
196
|
+
]
|
|
184
197
|
}
|
|
185
198
|
}
|
|
186
199
|
```
|
|
@@ -192,6 +205,7 @@ Set any target to `null` or `false` to disable that relation.
|
|
|
192
205
|
```bash
|
|
193
206
|
npx docsector build
|
|
194
207
|
cat dist/spa/_headers
|
|
208
|
+
cat dist/spa/.well-known/api-catalog
|
|
195
209
|
```
|
|
196
210
|
|
|
197
211
|
Or scan discoverability:
|
|
@@ -343,11 +357,18 @@ export default {
|
|
|
343
357
|
|
|
344
358
|
linkHeaders: {
|
|
345
359
|
enabled: true,
|
|
360
|
+
apiCatalog: '/.well-known/api-catalog',
|
|
346
361
|
serviceDoc: '/',
|
|
347
362
|
serviceDesc: '/mcp',
|
|
348
363
|
describedBy: '/llms.txt'
|
|
349
364
|
},
|
|
350
365
|
|
|
366
|
+
apiCatalog: {
|
|
367
|
+
enabled: true,
|
|
368
|
+
path: '/.well-known/api-catalog',
|
|
369
|
+
items: []
|
|
370
|
+
},
|
|
371
|
+
|
|
351
372
|
languages: [
|
|
352
373
|
{ image: '/images/flags/united-states-of-america.png', label: 'English (US)', value: 'en-US' },
|
|
353
374
|
{ 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.
|
|
26
|
+
const VERSION = '1.1.0'
|
|
27
27
|
|
|
28
28
|
const HELP = `
|
|
29
29
|
Docsector Reader v${VERSION}
|
|
@@ -136,11 +136,19 @@ export default {
|
|
|
136
136
|
// @ Homepage Link headers for agent discovery (optional)
|
|
137
137
|
// linkHeaders: {
|
|
138
138
|
// enabled: true,
|
|
139
|
+
// apiCatalog: '/.well-known/api-catalog',
|
|
139
140
|
// serviceDoc: '/',
|
|
140
141
|
// serviceDesc: '/mcp',
|
|
141
142
|
// describedBy: '/llms.txt'
|
|
142
143
|
// },
|
|
143
144
|
|
|
145
|
+
// @ API catalog artifact (RFC 9727) (optional)
|
|
146
|
+
// apiCatalog: {
|
|
147
|
+
// enabled: true,
|
|
148
|
+
// path: '/.well-known/api-catalog',
|
|
149
|
+
// items: ['/mcp']
|
|
150
|
+
// },
|
|
151
|
+
|
|
144
152
|
// @ Languages
|
|
145
153
|
languages: [
|
|
146
154
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@docsector/docsector-reader",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
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
|
@@ -47,8 +47,13 @@
|
|
|
47
47
|
* @param {Object} [config.linkHeaders] - Homepage Link headers for agent discovery
|
|
48
48
|
* @param {boolean} [config.linkHeaders.enabled=true] - Enables homepage Link headers generation
|
|
49
49
|
* @param {string|null|false} [config.linkHeaders.serviceDoc='/'] - Target URI for rel="service-doc"
|
|
50
|
+
* @param {string|null|false} [config.linkHeaders.apiCatalog='/.well-known/api-catalog'] - Target URI for rel="api-catalog"
|
|
50
51
|
* @param {string|null|false} [config.linkHeaders.serviceDesc='/mcp'] - Target URI for rel="service-desc" (only emitted when MCP is enabled)
|
|
51
52
|
* @param {string|null|false} [config.linkHeaders.describedBy='/llms.txt'] - Target URI for rel="describedby" (only emitted when llms.txt is generated)
|
|
53
|
+
* @param {Object} [config.apiCatalog] - API catalog generation settings
|
|
54
|
+
* @param {boolean} [config.apiCatalog.enabled=true] - Enables generation of API catalog artifact
|
|
55
|
+
* @param {string} [config.apiCatalog.path='/.well-known/api-catalog'] - Output URI path for API catalog artifact
|
|
56
|
+
* @param {Array<string|{href: string}>} [config.apiCatalog.items=[]] - Additional API endpoint links to include as item relations
|
|
52
57
|
* @returns {Object} Resolved Docsector configuration
|
|
53
58
|
*/
|
|
54
59
|
export function createDocsector (config = {}) {
|
|
@@ -93,9 +98,17 @@ export function createDocsector (config = {}) {
|
|
|
93
98
|
linkHeaders: {
|
|
94
99
|
enabled: true,
|
|
95
100
|
serviceDoc: '/',
|
|
101
|
+
apiCatalog: '/.well-known/api-catalog',
|
|
96
102
|
serviceDesc: '/mcp',
|
|
97
103
|
describedBy: '/llms.txt',
|
|
98
104
|
...config.linkHeaders
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
apiCatalog: {
|
|
108
|
+
enabled: true,
|
|
109
|
+
path: '/.well-known/api-catalog',
|
|
110
|
+
items: [],
|
|
111
|
+
...config.apiCatalog
|
|
99
112
|
}
|
|
100
113
|
}
|
|
101
114
|
}
|
package/src/quasar.factory.js
CHANGED
|
@@ -679,6 +679,13 @@ function createMarkdownBuildPlugin (projectRoot) {
|
|
|
679
679
|
homepageLinks.push({ rel: 'service-doc', href: serviceDocHref })
|
|
680
680
|
}
|
|
681
681
|
|
|
682
|
+
const apiCatalogHref = linkHeadersConfig.apiCatalog === undefined
|
|
683
|
+
? '/.well-known/api-catalog'
|
|
684
|
+
: linkHeadersConfig.apiCatalog
|
|
685
|
+
if (apiCatalogHref) {
|
|
686
|
+
homepageLinks.push({ rel: 'api-catalog', href: apiCatalogHref })
|
|
687
|
+
}
|
|
688
|
+
|
|
682
689
|
const serviceDescHref = linkHeadersConfig.serviceDesc === undefined
|
|
683
690
|
? '/mcp'
|
|
684
691
|
: linkHeadersConfig.serviceDesc
|
|
@@ -694,19 +701,106 @@ function createMarkdownBuildPlugin (projectRoot) {
|
|
|
694
701
|
}
|
|
695
702
|
|
|
696
703
|
if (homepageLinks.length > 0) {
|
|
697
|
-
const linkLines = homepageLinks.map(({ rel, href }) => ` Link: <${href}>; rel="${rel}"`).join('\n')
|
|
698
|
-
const homepageRule = ['/','/index.html']
|
|
699
|
-
.map(path => `${path}\n${linkLines}`)
|
|
700
|
-
.join('\n\n') + '\n'
|
|
701
|
-
|
|
702
704
|
const currentHeaders = readFileSync(headersPath, 'utf-8')
|
|
703
|
-
const
|
|
704
|
-
|
|
705
|
-
|
|
705
|
+
const missingHomepageLinks = homepageLinks.filter(({ rel }) => !currentHeaders.includes(`rel="${rel}"`))
|
|
706
|
+
|
|
707
|
+
if (missingHomepageLinks.length > 0) {
|
|
708
|
+
const linkLines = missingHomepageLinks
|
|
709
|
+
.map(({ rel, href }) => ` Link: <${href}>; rel="${rel}"`)
|
|
710
|
+
.join('\n')
|
|
711
|
+
const homepageRule = ['/', '/index.html']
|
|
712
|
+
.map(path => `${path}\n${linkLines}`)
|
|
713
|
+
.join('\n\n') + '\n'
|
|
706
714
|
|
|
707
|
-
if (!hasAgentLinks) {
|
|
708
715
|
writeFileSync(headersPath, currentHeaders.trimEnd() + '\n\n' + homepageRule)
|
|
709
|
-
console.log(`\x1b[36m[docsector]\x1b[0m Added homepage Link headers for agent discovery`)
|
|
716
|
+
console.log(`\x1b[36m[docsector]\x1b[0m Added homepage Link headers for agent discovery (${missingHomepageLinks.length} relation(s))`)
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// Generate /.well-known/api-catalog Linkset document (RFC 9727)
|
|
721
|
+
const apiCatalogConfig = config.apiCatalog || {}
|
|
722
|
+
const apiCatalogEnabled = apiCatalogConfig.enabled !== false
|
|
723
|
+
const apiCatalogPath = (apiCatalogConfig.path || apiCatalogHref || '/.well-known/api-catalog')
|
|
724
|
+
|
|
725
|
+
const toUrl = (href) => {
|
|
726
|
+
if (!href) return null
|
|
727
|
+
if (/^https?:\/\//i.test(href)) return href
|
|
728
|
+
const normalizedHref = href.startsWith('/') ? href : `/${href}`
|
|
729
|
+
return siteUrl ? `${siteUrl}${normalizedHref}` : normalizedHref
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
const normalizeLocalPath = (href) => {
|
|
733
|
+
if (!href || /^https?:\/\//i.test(href)) return null
|
|
734
|
+
const path = href.startsWith('/') ? href.slice(1) : href
|
|
735
|
+
return path || null
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
if (apiCatalogEnabled && apiCatalogPath) {
|
|
739
|
+
const catalogDistPath = normalizeLocalPath(apiCatalogPath)
|
|
740
|
+
|
|
741
|
+
if (catalogDistPath) {
|
|
742
|
+
const catalogHref = apiCatalogPath.startsWith('/') ? apiCatalogPath : `/${apiCatalogPath}`
|
|
743
|
+
const catalogEntry = {
|
|
744
|
+
anchor: toUrl(catalogHref)
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
const catalogServiceDoc = toUrl(serviceDocHref)
|
|
748
|
+
if (catalogServiceDoc) {
|
|
749
|
+
catalogEntry['service-doc'] = [{ href: catalogServiceDoc }]
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
const catalogServiceDesc = config.mcp ? toUrl(serviceDescHref) : null
|
|
753
|
+
if (catalogServiceDesc) {
|
|
754
|
+
catalogEntry['service-desc'] = [{ href: catalogServiceDesc }]
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
const catalogDescribedBy = siteUrl ? toUrl(describedByHref) : null
|
|
758
|
+
if (catalogDescribedBy) {
|
|
759
|
+
catalogEntry.describedby = [{ href: catalogDescribedBy }]
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
const customItems = Array.isArray(apiCatalogConfig.items)
|
|
763
|
+
? apiCatalogConfig.items
|
|
764
|
+
: []
|
|
765
|
+
const itemHrefs = new Set()
|
|
766
|
+
|
|
767
|
+
if (catalogServiceDesc) {
|
|
768
|
+
itemHrefs.add(catalogServiceDesc)
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
for (const item of customItems) {
|
|
772
|
+
if (typeof item === 'string') {
|
|
773
|
+
const href = toUrl(item)
|
|
774
|
+
if (href) itemHrefs.add(href)
|
|
775
|
+
continue
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
if (item && typeof item === 'object' && typeof item.href === 'string') {
|
|
779
|
+
const href = toUrl(item.href)
|
|
780
|
+
if (href) itemHrefs.add(href)
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
if (itemHrefs.size > 0) {
|
|
785
|
+
catalogEntry.item = [...itemHrefs].map(href => ({ href }))
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
const catalogDir = resolve(distDir, catalogDistPath, '..')
|
|
789
|
+
mkdirSync(catalogDir, { recursive: true })
|
|
790
|
+
writeFileSync(
|
|
791
|
+
resolve(distDir, catalogDistPath),
|
|
792
|
+
JSON.stringify({ linkset: [catalogEntry] }, null, 2) + '\n'
|
|
793
|
+
)
|
|
794
|
+
console.log(`\x1b[36m[docsector]\x1b[0m Generated ${catalogHref}`)
|
|
795
|
+
|
|
796
|
+
const headersWithLinks = readFileSync(headersPath, 'utf-8')
|
|
797
|
+
if (!headersWithLinks.includes(catalogHref)) {
|
|
798
|
+
const apiCatalogHeaders = `${catalogHref}\n Content-Type: application/linkset+json; profile=\"https://www.rfc-editor.org/info/rfc9727\"\n`
|
|
799
|
+
writeFileSync(headersPath, headersWithLinks.trimEnd() + '\n\n' + apiCatalogHeaders)
|
|
800
|
+
console.log(`\x1b[36m[docsector]\x1b[0m Added _headers rule for ${catalogHref}`)
|
|
801
|
+
}
|
|
802
|
+
} else {
|
|
803
|
+
console.warn(`\x1b[33m[docsector]\x1b[0m Skipped API catalog generation: path must be a local URI path, got \"${apiCatalogPath}\"`)
|
|
710
804
|
}
|
|
711
805
|
}
|
|
712
806
|
}
|