@redpanda-data/docs-extensions-and-macros 4.17.3 → 5.0.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/extension-utils/llms-utils.js +2 -5
- package/extensions/algolia-indexer/generate-index.js +99 -54
- package/extensions/collect-bloblang-samples.js +6 -6
- package/extensions/compute-end-of-life.js +8 -4
- package/extensions/conditional-home-redirect.js +90 -0
- package/extensions/convert-to-markdown.js +23 -1
- package/extensions/find-related-docs.js +3 -3
- package/extensions/find-related-labs.js +1 -1
- package/extensions/generate-fields-only-pages.js +7 -7
- package/extensions/generate-rp-connect-categories.js +5 -5
- package/extensions/generate-rp-connect-info.js +6 -6
- package/extensions/modify-connect-tag-playbook.js +1 -1
- package/extensions/unified-navigation.js +669 -0
- package/extensions/unlisted-pages.js +5 -1
- package/extensions/unpublish-pages.js +12 -0
- package/extensions/version-fetcher/fetch-latest-docker-tag.js +7 -2
- package/extensions/version-fetcher/get-latest-connect.js +6 -2
- package/extensions/version-fetcher/get-latest-console-version.js +13 -4
- package/extensions/version-fetcher/get-latest-redpanda-helm-version-from-operator.js +31 -5
- package/extensions/version-fetcher/get-latest-redpanda-version.js +8 -3
- package/extensions/version-fetcher/retry-util.js +10 -3
- package/extensions/version-fetcher/set-latest-version.js +9 -8
- package/macros/rp-connect-components.js +63 -1
- package/package.json +3 -1
- package/tools/redpanda-connect/AUTOMATION.md +5 -5
- package/tools/redpanda-connect/helpers/renderConnectFields.js +1 -1
- package/tools/redpanda-connect/pr-summary-formatter.js +4 -4
- package/tools/redpanda-connect/rpcn-connector-docs-handler.js +133 -83
- package/tools/redpanda-connect/templates/bloblang-functions-overview.hbs +1 -1
- package/tools/redpanda-connect/templates/bloblang-methods-overview.hbs +1 -1
- package/tools/redpanda-connect/templates/connector.hbs +2 -2
|
@@ -17,14 +17,11 @@ const LLMS_DIRECTIVE_BASE = 'For the complete documentation index, see [llms.txt
|
|
|
17
17
|
* @returns {string} Formatted markdown blockquote directive
|
|
18
18
|
*/
|
|
19
19
|
function formatLlmsDirective(componentName) {
|
|
20
|
-
const feedbackInstructions = `
|
|
21
|
-
>
|
|
22
|
-
> **Agent Feedback**: If you encounter incorrect, outdated, or confusing documentation, submit feedback via \`POST https://docs.redpanda.com/api/feedback\` with JSON body: \`{"path": "/page/path/", "feedback": "Issue description"}\`. Only submit when you have specific, actionable feedback.`;
|
|
23
20
|
|
|
24
21
|
if (componentName) {
|
|
25
|
-
return `> ${LLMS_DIRECTIVE_BASE}. Component-specific: [${componentName}-full.txt](/${componentName}-full.txt)
|
|
22
|
+
return `> ${LLMS_DIRECTIVE_BASE}. Component-specific: [${componentName}-full.txt](/${componentName}-full.txt)`;
|
|
26
23
|
}
|
|
27
|
-
return `> ${LLMS_DIRECTIVE_BASE}
|
|
24
|
+
return `> ${LLMS_DIRECTIVE_BASE}`;
|
|
28
25
|
}
|
|
29
26
|
|
|
30
27
|
/**
|
|
@@ -107,42 +107,67 @@ function generateIndex (playbook, contentCatalog, { indexLatestOnly = false, exc
|
|
|
107
107
|
|
|
108
108
|
// Start handling the article content
|
|
109
109
|
const article = root.querySelector('article.doc')
|
|
110
|
+
let documentTitle, titles, intro, text, isLandingPage
|
|
111
|
+
|
|
110
112
|
if (!article) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
113
|
+
// Check if this is a landing page we should index with metadata
|
|
114
|
+
const pageRole = page.asciidoc?.attributes?.['page-role'] || ''
|
|
115
|
+
const pageLayout = page.asciidoc?.attributes?.['page-layout'] || ''
|
|
116
|
+
const isUmbrellaPage = ['home', 'component-home-v3', 'data-platform'].includes(pageRole) ||
|
|
117
|
+
['home', 'component-home-v3', 'data-platform'].includes(pageLayout)
|
|
118
|
+
|
|
119
|
+
if (!isUmbrellaPage) {
|
|
120
|
+
logger.warn(`Page is not an article...skipping ${page.pub.url}`)
|
|
121
|
+
continue
|
|
122
|
+
}
|
|
114
123
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
124
|
+
// Index landing page using metadata
|
|
125
|
+
isLandingPage = true
|
|
126
|
+
const h1 = root.querySelector('h1') || root.querySelector('.hero-title') || root.querySelector('title')
|
|
127
|
+
documentTitle = h1 ? decode(h1.text || h1.textContent || '') : component.title || cname
|
|
128
|
+
titles = []
|
|
129
|
+
|
|
130
|
+
// Get description from meta tag or page attribute
|
|
131
|
+
const metaDesc = root.querySelector('meta[name="description"]')
|
|
132
|
+
intro = metaDesc ? metaDesc.getAttribute('content') :
|
|
133
|
+
page.asciidoc?.attributes?.description || ''
|
|
134
|
+
text = intro
|
|
135
|
+
logger.info(`Indexing landing page: ${page.pub.url}`)
|
|
136
|
+
} else {
|
|
137
|
+
isLandingPage = false
|
|
138
|
+
|
|
139
|
+
// Handle titles
|
|
140
|
+
const h1 = article.querySelector('h1')
|
|
141
|
+
if (!h1) {
|
|
142
|
+
logger.warn(`No H1 in ${page.pub.url}...skipping`)
|
|
143
|
+
continue
|
|
144
|
+
}
|
|
145
|
+
documentTitle = h1.text
|
|
146
|
+
h1.remove()
|
|
147
|
+
|
|
148
|
+
titles = []
|
|
149
|
+
article.querySelectorAll('h2,h3,h4,h5,h6').forEach((title) => {
|
|
150
|
+
const id = title.getAttribute('id')
|
|
151
|
+
if (id) {
|
|
152
|
+
titles.push({
|
|
153
|
+
t: title.text,
|
|
154
|
+
h: id
|
|
155
|
+
})
|
|
156
|
+
}
|
|
157
|
+
title.remove()
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
// Exclude elements within the article that should not be indexed
|
|
161
|
+
for (const excl of excludes) {
|
|
162
|
+
if (!excl) continue
|
|
163
|
+
article.querySelectorAll(excl).forEach((e) => e.remove())
|
|
132
164
|
}
|
|
133
|
-
title.remove()
|
|
134
|
-
})
|
|
135
165
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
article.querySelectorAll(excl).forEach((e) => e.remove())
|
|
166
|
+
// FIXED: Handle potential null intro element
|
|
167
|
+
const introElement = article.querySelector('p')
|
|
168
|
+
intro = introElement ? decode(introElement.rawText) : ''
|
|
140
169
|
}
|
|
141
170
|
|
|
142
|
-
// FIXED: Handle potential null intro element
|
|
143
|
-
const introElement = article.querySelector('p')
|
|
144
|
-
const intro = introElement ? decode(introElement.rawText) : ''
|
|
145
|
-
|
|
146
171
|
// Establish structure in the Algolia index
|
|
147
172
|
if (!(cname in algolia)) algolia[cname] = {}
|
|
148
173
|
if (!(version in algolia[cname])) algolia[cname][version] = []
|
|
@@ -150,10 +175,12 @@ function generateIndex (playbook, contentCatalog, { indexLatestOnly = false, exc
|
|
|
150
175
|
// Check if this is a properties reference page (or has many titles)
|
|
151
176
|
const isPropertiesPage = page.pub.url.includes('/properties/') || titles.length > 30
|
|
152
177
|
|
|
153
|
-
// Handle the article text
|
|
154
|
-
|
|
178
|
+
// Handle the article text (skip for landing pages - already set above)
|
|
179
|
+
if (!isLandingPage) {
|
|
180
|
+
text = ''
|
|
181
|
+
}
|
|
155
182
|
|
|
156
|
-
if (!isPropertiesPage) {
|
|
183
|
+
if (!isLandingPage && !isPropertiesPage && article) {
|
|
157
184
|
// For normal pages, index full text content
|
|
158
185
|
const contentElements = article.querySelectorAll('p, table, li')
|
|
159
186
|
let contentText = ''
|
|
@@ -186,44 +213,62 @@ function generateIndex (playbook, contentCatalog, { indexLatestOnly = false, exc
|
|
|
186
213
|
.replace(/\r/g, ' ')
|
|
187
214
|
.replace(/\s+/g, ' ')
|
|
188
215
|
.trim()
|
|
189
|
-
} else {
|
|
216
|
+
} else if (!isLandingPage && isPropertiesPage) {
|
|
190
217
|
// For long pages, only use intro as text (property names are already in titles array)
|
|
191
218
|
text = intro
|
|
192
219
|
logger.info(`Skipping full text indexing for long page: ${page.pub.url} (${titles.length} properties)`)
|
|
193
220
|
}
|
|
221
|
+
// For landing pages, text is already set above
|
|
194
222
|
|
|
195
223
|
let tag
|
|
196
224
|
const title = (component.title || '').trim()
|
|
197
|
-
|
|
198
|
-
|
|
225
|
+
const titleLower = title.toLowerCase()
|
|
226
|
+
|
|
227
|
+
// Umbrella components that should include multiple product tags
|
|
228
|
+
const UMBRELLA_COMPONENTS = ['home', 'data platform', 'self-managed']
|
|
229
|
+
|
|
230
|
+
if (UMBRELLA_COMPONENTS.includes(titleLower)) {
|
|
231
|
+
// Collect all unique component titles except umbrella/utility components
|
|
199
232
|
const componentsList = typeof contentCatalog.getComponents === 'function'
|
|
200
233
|
? contentCatalog.getComponents()
|
|
201
234
|
: Array.isArray(contentCatalog.components)
|
|
202
235
|
? contentCatalog.components
|
|
203
236
|
: Object.values(contentCatalog.components || contentCatalog._components || {})
|
|
204
237
|
|
|
205
|
-
// Find the latest version for
|
|
206
|
-
let
|
|
207
|
-
const
|
|
208
|
-
if (
|
|
209
|
-
|
|
210
|
-
if (
|
|
211
|
-
|
|
238
|
+
// Find the latest version for Streaming
|
|
239
|
+
let streamingLatestVersion
|
|
240
|
+
const streaming = componentsList.find(c => (c.title || '').trim().toLowerCase() === 'streaming')
|
|
241
|
+
if (streaming?.latest?.version) {
|
|
242
|
+
streamingLatestVersion = streaming.latest.version
|
|
243
|
+
if (streamingLatestVersion && !/^v/.test(streamingLatestVersion)) {
|
|
244
|
+
streamingLatestVersion = 'v' + streamingLatestVersion
|
|
212
245
|
}
|
|
213
246
|
}
|
|
214
247
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
248
|
+
// Filter components based on umbrella type
|
|
249
|
+
let filteredTitles
|
|
250
|
+
if (titleLower === 'home') {
|
|
251
|
+
// Home includes all main products
|
|
252
|
+
filteredTitles = componentsList
|
|
253
|
+
.map(c => (c.title || '').trim())
|
|
254
|
+
.filter(t => t && !['home', 'shared', 'search', 'data platform', 'self-managed'].includes(t.toLowerCase()))
|
|
255
|
+
} else if (titleLower === 'data platform') {
|
|
256
|
+
// Data Platform includes Cloud, Streaming, Connect
|
|
257
|
+
filteredTitles = ['Cloud', 'Streaming', 'Connect']
|
|
258
|
+
} else if (titleLower === 'self-managed') {
|
|
259
|
+
// Self-Managed includes Streaming, Connect
|
|
260
|
+
filteredTitles = ['Streaming', 'Connect']
|
|
221
261
|
}
|
|
222
262
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
263
|
+
if (!filteredTitles || !filteredTitles.length) {
|
|
264
|
+
// Fallback to component title
|
|
265
|
+
tag = title
|
|
266
|
+
} else {
|
|
267
|
+
tag = [...new Set(filteredTitles)]
|
|
268
|
+
// For Streaming, append v<latest-version> to the tag
|
|
269
|
+
if (streamingLatestVersion) {
|
|
270
|
+
tag = tag.map(t => t.toLowerCase() === 'streaming' ? `${t} ${streamingLatestVersion}` : t)
|
|
271
|
+
}
|
|
227
272
|
}
|
|
228
273
|
} else {
|
|
229
274
|
tag = `${title}${version ? ' v' + version : ''}`
|
|
@@ -261,7 +306,7 @@ function generateIndex (playbook, contentCatalog, { indexLatestOnly = false, exc
|
|
|
261
306
|
unixTimestamp: unixTimestamp
|
|
262
307
|
}
|
|
263
308
|
|
|
264
|
-
if (component.name !== '
|
|
309
|
+
if (component.name !== 'labs') {
|
|
265
310
|
indexItem.product = component.title
|
|
266
311
|
indexItem.breadcrumbs = breadcrumbs
|
|
267
312
|
indexItem.type = 'Doc'
|
|
@@ -53,21 +53,21 @@ module.exports.register = function () {
|
|
|
53
53
|
};
|
|
54
54
|
|
|
55
55
|
// Fetch examples from both components
|
|
56
|
-
const examples = contentCatalog.findBy({ component: '
|
|
56
|
+
const examples = contentCatalog.findBy({ component: 'connect', family: 'example' });
|
|
57
57
|
const previewExamples = contentCatalog.findBy({ component: 'preview', family: 'example' });
|
|
58
58
|
|
|
59
|
-
if (!examples.length) logger.warn(`No examples found in the '
|
|
59
|
+
if (!examples.length) logger.warn(`No examples found in the 'connect' component.`);
|
|
60
60
|
|
|
61
61
|
// Get components
|
|
62
|
-
const connect = contentCatalog.getComponents().find((c) => c.name === '
|
|
62
|
+
const connect = contentCatalog.getComponents().find((c) => c.name === 'connect');
|
|
63
63
|
const preview = contentCatalog.getComponents().find((c) => c.name === 'preview');
|
|
64
64
|
|
|
65
65
|
if (connect) {
|
|
66
|
-
const connectSamples = collectExamples(examples, '
|
|
66
|
+
const connectSamples = collectExamples(examples, 'connect');
|
|
67
67
|
connect.latest.asciidoc.attributes['page-bloblang-samples'] = JSON.stringify(connectSamples);
|
|
68
|
-
logger.debug(`Bloblang samples added to '
|
|
68
|
+
logger.debug(`Bloblang samples added to 'connect': ${JSON.stringify(connectSamples, null, 2)}`);
|
|
69
69
|
} else {
|
|
70
|
-
logger.warn(`Component '
|
|
70
|
+
logger.warn(`Component 'connect' not found.`);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
if (preview) {
|
|
@@ -13,10 +13,10 @@ module.exports.register = function ({ config }) {
|
|
|
13
13
|
return;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
eolConfigs.forEach(({ component: componentName, supported_months, warning_weeks, eol_doc, upgrade_doc }) => {
|
|
16
|
+
eolConfigs.forEach(({ component: componentName, supported_months, warning_weeks, eol_doc, upgrade_doc, whats_new_doc }) => {
|
|
17
17
|
if (!eol_doc || !upgrade_doc) {
|
|
18
18
|
logger.error(
|
|
19
|
-
`End-of-life configuration for component "${
|
|
19
|
+
`End-of-life configuration for component "${componentName}" is missing required attributes. ` +
|
|
20
20
|
`Ensure both "eol_doc" and "upgrade_doc" are specified.`
|
|
21
21
|
);
|
|
22
22
|
return;
|
|
@@ -40,14 +40,18 @@ module.exports.register = function ({ config }) {
|
|
|
40
40
|
if (releaseDate) {
|
|
41
41
|
// Pass resolved configuration to calculateEOL
|
|
42
42
|
const eolInfo = calculateEOL(releaseDate, resolvedEOLMonths, resolvedWarningWeeks, logger);
|
|
43
|
-
|
|
43
|
+
const attrs = {
|
|
44
44
|
'page-is-nearing-eol': eolInfo.isNearingEOL.toString(),
|
|
45
45
|
'page-is-past-eol': eolInfo.isPastEOL.toString(),
|
|
46
46
|
'page-eol-date': eolInfo.eolDate,
|
|
47
47
|
'page-eol-doc': eol_doc,
|
|
48
48
|
'page-upgrade-doc': upgrade_doc,
|
|
49
49
|
'page-support-months': resolvedEOLMonths,
|
|
50
|
-
}
|
|
50
|
+
};
|
|
51
|
+
if (whats_new_doc) {
|
|
52
|
+
attrs['page-whats-new-doc'] = whats_new_doc;
|
|
53
|
+
}
|
|
54
|
+
Object.assign(asciidoc.attributes, attrs);
|
|
51
55
|
} else {
|
|
52
56
|
logger.warn(`No release date found for component: ${componentName}. Make sure to set {page-release-date} in the antora.yml of the component version ${version}.`);
|
|
53
57
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Antora extension to redirect home page when agentic-data-plane component doesn't exist
|
|
3
|
+
*
|
|
4
|
+
* When the agentic-data-plane component is not included in the build, this extension
|
|
5
|
+
* programmatically adds an alias to the home page that redirects to the data-platform
|
|
6
|
+
* landing page. This leverages Antora's standard redirect facility (Netlify, etc.).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict'
|
|
10
|
+
|
|
11
|
+
module.exports.register = function () {
|
|
12
|
+
const logger = this.getLogger('conditional-home-redirect')
|
|
13
|
+
let shouldRedirect = false
|
|
14
|
+
|
|
15
|
+
// Check if ADP exists during content aggregation
|
|
16
|
+
this.on('contentAggregated', ({ contentAggregate }) => {
|
|
17
|
+
const adpComponent = contentAggregate.find(c => c.name === 'agentic-data-plane')
|
|
18
|
+
shouldRedirect = !adpComponent
|
|
19
|
+
if (shouldRedirect) {
|
|
20
|
+
logger.info('agentic-data-plane not found, will create redirect after publish')
|
|
21
|
+
} else {
|
|
22
|
+
logger.info('agentic-data-plane found, no redirect needed')
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
// Create redirect files AFTER site is published
|
|
27
|
+
this.on('sitePublished', ({ playbook }) => {
|
|
28
|
+
if (!shouldRedirect) return
|
|
29
|
+
|
|
30
|
+
const fs = require('fs')
|
|
31
|
+
const path = require('path')
|
|
32
|
+
|
|
33
|
+
// Get absolute output directory
|
|
34
|
+
const outputDir = path.resolve(playbook.output.dir || 'build/site')
|
|
35
|
+
logger.debug(`Output directory: ${outputDir}`)
|
|
36
|
+
|
|
37
|
+
// Check redirect facility
|
|
38
|
+
const redirectFacility = playbook.urls.redirectFacility || 'netlify'
|
|
39
|
+
logger.debug(`Redirect facility: ${redirectFacility}`)
|
|
40
|
+
|
|
41
|
+
if (redirectFacility === 'netlify') {
|
|
42
|
+
// Add to _redirects file
|
|
43
|
+
const redirectsPath = path.join(outputDir, '_redirects')
|
|
44
|
+
const redirectRule = '/home/ /data-platform/ 301\n/home/index.html /data-platform/index.html 301\n'
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
let existingRedirects = ''
|
|
48
|
+
if (fs.existsSync(redirectsPath)) {
|
|
49
|
+
existingRedirects = fs.readFileSync(redirectsPath, 'utf8')
|
|
50
|
+
logger.debug(`Found existing _redirects file with ${existingRedirects.split('\\n').length} lines`)
|
|
51
|
+
} else {
|
|
52
|
+
logger.debug('No existing _redirects file, creating new one')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Prepend our redirect to the top (higher priority)
|
|
56
|
+
fs.writeFileSync(redirectsPath, redirectRule + existingRedirects)
|
|
57
|
+
logger.info('Added home -> data-platform redirect to _redirects')
|
|
58
|
+
} catch (err) {
|
|
59
|
+
logger.error(`Failed to write redirects file: ${err.message}`)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Create a static HTML redirect at /home/index.html
|
|
64
|
+
const homeDir = path.join(outputDir, 'home')
|
|
65
|
+
const homeIndexPath = path.join(homeDir, 'index.html')
|
|
66
|
+
|
|
67
|
+
const redirectHtml = `<!DOCTYPE html>
|
|
68
|
+
<meta charset="utf-8">
|
|
69
|
+
<link rel="canonical" href="../data-platform/">
|
|
70
|
+
<script>location="../data-platform/"</script>
|
|
71
|
+
<meta http-equiv="refresh" content="0; url=../data-platform/">
|
|
72
|
+
<meta name="robots" content="noindex">
|
|
73
|
+
<title>Redirect Notice</title>
|
|
74
|
+
<h1>Redirecting to Data Platform</h1>
|
|
75
|
+
<p>The Redpanda documentation home page is being redirected. You will be forwarded to <a href="../data-platform/">Data Platform</a> automatically.</p>`
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
// Ensure directory exists
|
|
79
|
+
if (!fs.existsSync(homeDir)) {
|
|
80
|
+
fs.mkdirSync(homeDir, { recursive: true })
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Overwrite the home page with redirect HTML
|
|
84
|
+
fs.writeFileSync(homeIndexPath, redirectHtml)
|
|
85
|
+
logger.info(`Overwrote home page with redirect at ${homeIndexPath}`)
|
|
86
|
+
} catch (err) {
|
|
87
|
+
logger.error(`Failed to create redirect HTML: ${err.message}`)
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
}
|
|
@@ -51,7 +51,8 @@ function generateFrontmatter(page) {
|
|
|
51
51
|
'title',
|
|
52
52
|
'navtitle',
|
|
53
53
|
'description',
|
|
54
|
-
'categories'
|
|
54
|
+
// Note: 'categories' removed - it's a massive object (all Connect components)
|
|
55
|
+
// that bloats frontmatter. Only include on pages that specifically need it.
|
|
55
56
|
'page-component-name',
|
|
56
57
|
'page-component-title',
|
|
57
58
|
'page-component-version',
|
|
@@ -75,10 +76,25 @@ function generateFrontmatter(page) {
|
|
|
75
76
|
'latest-connect-version',
|
|
76
77
|
]
|
|
77
78
|
|
|
79
|
+
// Blocklist of large data attributes that should never be in frontmatter
|
|
80
|
+
// These are component-level data used by macros but shouldn't pollute markdown
|
|
81
|
+
const blockedAttributes = [
|
|
82
|
+
'categories', // Massive nested object with all Connect components
|
|
83
|
+
'connectCategoriesData', // Raw Connect categories data
|
|
84
|
+
'flatComponentsData', // Flattened Connect component data
|
|
85
|
+
'driverSupportData', // SQL driver support matrix
|
|
86
|
+
'cacheSupportData', // Cache support matrix
|
|
87
|
+
'csvData', // Raw CSV data from generate-rp-connect-info
|
|
88
|
+
'commercialNamesMap', // Commercial names mapping
|
|
89
|
+
]
|
|
90
|
+
|
|
78
91
|
// Add allowed page attributes to frontmatter
|
|
79
92
|
Object.keys(attrs).forEach(key => {
|
|
80
93
|
const value = attrs[key]
|
|
81
94
|
|
|
95
|
+
// Skip blocked attributes (large data objects)
|
|
96
|
+
if (blockedAttributes.includes(key)) return
|
|
97
|
+
|
|
82
98
|
// Allow all learning-objective-* attributes (learning-objective-1, -2, -3, etc.)
|
|
83
99
|
const isLearningObjective = key.startsWith('learning-objective-')
|
|
84
100
|
|
|
@@ -222,6 +238,12 @@ module.exports.register = function () {
|
|
|
222
238
|
'nav-expand',
|
|
223
239
|
'banner-container',
|
|
224
240
|
'markdown-dropdown',
|
|
241
|
+
'version-selector', // Version dropdown (not relevant for LLMs)
|
|
242
|
+
'component-indicator', // Component header bar
|
|
243
|
+
'product-switcher', // Product switcher dropdown
|
|
244
|
+
'breadcrumb', // Breadcrumb navigation
|
|
245
|
+
'chat-panel', // AI chat interface
|
|
246
|
+
'kapa', // Kapa AI widget
|
|
225
247
|
]
|
|
226
248
|
return toRemove.some(
|
|
227
249
|
(x) => classAttr.includes(x) || idAttr.includes(x)
|
|
@@ -12,10 +12,10 @@ module.exports.register = function ({ config }) {
|
|
|
12
12
|
|
|
13
13
|
// Retrieve all documents and labs from the latest versions of each component
|
|
14
14
|
const allDocs = [];
|
|
15
|
-
const allLabs = contentCatalog.findBy({ component: '
|
|
15
|
+
const allLabs = contentCatalog.findBy({ component: 'labs', family: 'page', version: latestVersions['labs'] });
|
|
16
16
|
|
|
17
17
|
Object.keys(latestVersions).forEach(component => {
|
|
18
|
-
if (component === '
|
|
18
|
+
if (component === 'labs') return;
|
|
19
19
|
allDocs.push(...contentCatalog.findBy({ component, family: 'page', version: latestVersions[component] }));
|
|
20
20
|
});
|
|
21
21
|
allLabs.forEach((labPage) => {
|
|
@@ -26,7 +26,7 @@ module.exports.register = function ({ config }) {
|
|
|
26
26
|
if (!pageCategories) return;
|
|
27
27
|
const sourceCategoryList = pageCategories.split(',').map(c => c.trim());
|
|
28
28
|
const sourceDeploymentType = getDeploymentType(sourceAttributes);
|
|
29
|
-
const docs = contentCatalog.findBy({ component: '
|
|
29
|
+
const docs = contentCatalog.findBy({ component: 'streaming', family: 'page', version: latestVersions['streaming'] });
|
|
30
30
|
|
|
31
31
|
allDocs.forEach((docPage) => {
|
|
32
32
|
const related = findRelated(docPage, sourceCategoryList, sourceDeploymentType, logger);
|
|
@@ -12,7 +12,7 @@ module.exports.register = function ({ config }) {
|
|
|
12
12
|
if (!pageCategories) return;
|
|
13
13
|
const sourceCategoryList = pageCategories.split(',').map(c => c.trim());
|
|
14
14
|
const sourceDeploymentType = getDeploymentType(sourceAttributes)
|
|
15
|
-
const labs = contentCatalog.findBy({ component: '
|
|
15
|
+
const labs = contentCatalog.findBy({ component: 'labs', family: 'page' });
|
|
16
16
|
labs.forEach((labPage) => {
|
|
17
17
|
const related = findRelated(labPage, sourceCategoryList, sourceDeploymentType, logger)
|
|
18
18
|
related && relatedLabs.push(related)
|
|
@@ -145,15 +145,15 @@ module.exports.register = function ({ config }) {
|
|
|
145
145
|
const fieldOnlyTemplate = handlebars.compile(`{{{${helperName} children}}}`)
|
|
146
146
|
|
|
147
147
|
this.on('contentClassified', ({ contentCatalog, siteCatalog }) => {
|
|
148
|
-
const component = contentCatalog.getComponent('
|
|
148
|
+
const component = contentCatalog.getComponent('connect')
|
|
149
149
|
if (!component) {
|
|
150
|
-
logger.warn('
|
|
150
|
+
logger.warn('connect component not found. Skipping field-only page generation.')
|
|
151
151
|
return
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
const componentVersion = component.latest
|
|
155
155
|
if (!componentVersion) {
|
|
156
|
-
logger.warn('No latest version found for
|
|
156
|
+
logger.warn('No latest version found for connect component.')
|
|
157
157
|
return
|
|
158
158
|
}
|
|
159
159
|
|
|
@@ -161,7 +161,7 @@ module.exports.register = function ({ config }) {
|
|
|
161
161
|
// Look for any versioned JSON attachment in the components module
|
|
162
162
|
// (i.e. modules/components/attachments/connect-X.Y.Z.json)
|
|
163
163
|
const attachments = contentCatalog.findBy({
|
|
164
|
-
component: '
|
|
164
|
+
component: 'connect',
|
|
165
165
|
version: componentVersion.version,
|
|
166
166
|
module: 'components',
|
|
167
167
|
family: 'attachment'
|
|
@@ -203,7 +203,7 @@ module.exports.register = function ({ config }) {
|
|
|
203
203
|
const attachment = matchingAttachments[0]?.file
|
|
204
204
|
|
|
205
205
|
if (!attachment) {
|
|
206
|
-
logger.warn('No JSON attachment found in the components module of the
|
|
206
|
+
logger.warn('No JSON attachment found in the components module of the connect content catalog. Skipping field-only page generation.')
|
|
207
207
|
return
|
|
208
208
|
}
|
|
209
209
|
|
|
@@ -218,7 +218,7 @@ module.exports.register = function ({ config }) {
|
|
|
218
218
|
let pagesGenerated = 0
|
|
219
219
|
|
|
220
220
|
// Get origin from first existing page in component (for git metadata)
|
|
221
|
-
const existingPages = contentCatalog.getPages((page) => page.src.component === '
|
|
221
|
+
const existingPages = contentCatalog.getPages((page) => page.src.component === 'connect')
|
|
222
222
|
const origin = existingPages.length > 0 ? existingPages[0].src.origin : { type: 'generated' }
|
|
223
223
|
|
|
224
224
|
// Iterate over each type (inputs, outputs, processors, etc.)
|
|
@@ -266,7 +266,7 @@ module.exports.register = function ({ config }) {
|
|
|
266
266
|
contents: contentBuffer,
|
|
267
267
|
stat: stat,
|
|
268
268
|
src: {
|
|
269
|
-
component: '
|
|
269
|
+
component: 'connect',
|
|
270
270
|
version: componentVersion.version,
|
|
271
271
|
module: 'components',
|
|
272
272
|
family: 'page',
|
|
@@ -14,10 +14,10 @@ module.exports.register = function ({ config }) {
|
|
|
14
14
|
const logger = this.getLogger('redpanda-connect-category-aggregation-extension')
|
|
15
15
|
|
|
16
16
|
this.on('contentClassified', ({ contentCatalog }) => {
|
|
17
|
-
const redpandaConnect = contentCatalog.getComponents().find(component => component.name === '
|
|
17
|
+
const redpandaConnect = contentCatalog.getComponents().find(component => component.name === 'connect')
|
|
18
18
|
|
|
19
19
|
if (!redpandaConnect || !redpandaConnect.latest) {
|
|
20
|
-
logger.warn('Could not find the
|
|
20
|
+
logger.warn('Could not find the connect component. Skipping category creation.')
|
|
21
21
|
return
|
|
22
22
|
}
|
|
23
23
|
|
|
@@ -25,12 +25,12 @@ module.exports.register = function ({ config }) {
|
|
|
25
25
|
const csvData = redpandaConnect.latest.asciidoc.attributes.csvData
|
|
26
26
|
|
|
27
27
|
if (!descriptions) {
|
|
28
|
-
logger.error('No categories attribute found in
|
|
28
|
+
logger.error('No categories attribute found in connect component')
|
|
29
29
|
return
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
if (!csvData || !csvData.data) {
|
|
33
|
-
logger.error('No csvData attribute found in
|
|
33
|
+
logger.error('No csvData attribute found in connect component.')
|
|
34
34
|
logger.error('Ensure generate-rp-connect-info extension is listed BEFORE this extension in your playbook.')
|
|
35
35
|
return
|
|
36
36
|
}
|
|
@@ -61,7 +61,7 @@ module.exports.register = function ({ config }) {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
try {
|
|
64
|
-
const files = contentCatalog.findBy({ component: '
|
|
64
|
+
const files = contentCatalog.findBy({ component: 'connect', family: 'page' })
|
|
65
65
|
|
|
66
66
|
for (const file of files) {
|
|
67
67
|
// Prefer using page.asciidoc.attributes when available
|
|
@@ -36,12 +36,12 @@ module.exports.register = function ({ config }) {
|
|
|
36
36
|
})
|
|
37
37
|
|
|
38
38
|
async function processContent (contentCatalog) {
|
|
39
|
-
const redpandaConnect = contentCatalog.getComponents().find(component => component.name === '
|
|
40
|
-
const redpandaCloud = contentCatalog.getComponents().find(component => component.name === '
|
|
39
|
+
const redpandaConnect = contentCatalog.getComponents().find(component => component.name === 'connect')
|
|
40
|
+
const redpandaCloud = contentCatalog.getComponents().find(component => component.name === 'cloud-data-platform')
|
|
41
41
|
const preview = contentCatalog.getComponents().find(component => component.name === 'preview')
|
|
42
42
|
|
|
43
43
|
if (!redpandaConnect) {
|
|
44
|
-
logger.warn('
|
|
44
|
+
logger.warn('connect component not found, skipping CSV enrichment')
|
|
45
45
|
return
|
|
46
46
|
}
|
|
47
47
|
|
|
@@ -141,7 +141,7 @@ module.exports.register = function ({ config }) {
|
|
|
141
141
|
const stem = file.src.stem
|
|
142
142
|
const filePath = file.path
|
|
143
143
|
|
|
144
|
-
if (component === '
|
|
144
|
+
if (component === 'connect') {
|
|
145
145
|
// Store by stem, but only for connector doc paths
|
|
146
146
|
if (isConnectorDocPath(filePath, file)) {
|
|
147
147
|
const type = extractTypeFromPath(filePath)
|
|
@@ -150,7 +150,7 @@ module.exports.register = function ({ config }) {
|
|
|
150
150
|
connectPages.set(key, file)
|
|
151
151
|
}
|
|
152
152
|
}
|
|
153
|
-
} else if (component === '
|
|
153
|
+
} else if (component === 'cloud-data-platform') {
|
|
154
154
|
// Cloud docs have a specific path pattern
|
|
155
155
|
const cloudMatch = filePath.match(/connect\/components\/([^/]+)s\/([^/]+)\.adoc$/)
|
|
156
156
|
if (cloudMatch) {
|
|
@@ -280,7 +280,7 @@ module.exports.register = function ({ config }) {
|
|
|
280
280
|
const { component, relative, module: moduleName } = page.src
|
|
281
281
|
|
|
282
282
|
// Only process Redpanda Connect and Cloud component pages
|
|
283
|
-
if (component !== '
|
|
283
|
+
if (component !== 'connect' && component !== 'cloud-data-platform') continue
|
|
284
284
|
|
|
285
285
|
// Match component documentation pages:
|
|
286
286
|
// 1. Cloud-style paths: connect/components/processors/archive.adoc
|
|
@@ -20,7 +20,7 @@ module.exports.register = function ({ config }) {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
} catch (error) {
|
|
23
|
-
|
|
23
|
+
logger.error('Failed to update playbook with the latest Redpanda Connect tag:', error);
|
|
24
24
|
}
|
|
25
25
|
});
|
|
26
26
|
}
|