@redpanda-data/docs-extensions-and-macros 4.13.4 → 4.13.6
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/bin/doc-tools-mcp.js +3 -0
- package/bin/doc-tools.js +3 -0
- package/cli-utils/github-token.js +6 -2
- package/cli-utils/octokit-client.js +36 -0
- package/extensions/generate-rp-connect-info.js +3 -5
- package/package.json +2 -1
- package/tools/cloud-regions/generate-cloud-regions.js +2 -2
- package/tools/fetch-from-github.js +4 -17
- package/tools/get-console-version.js +2 -7
- package/tools/get-redpanda-version.js +2 -7
- package/tools/redpanda-connect/connector-binary-analyzer.js +1 -11
- package/tools/redpanda-connect/helpers/buildConfigYaml.js +8 -3
- package/tools/redpanda-connect/helpers/renderObjectField.js +6 -1
- package/tools/redpanda-connect/rpcn-connector-docs-handler.js +166 -3
package/bin/doc-tools-mcp.js
CHANGED
|
@@ -15,6 +15,9 @@
|
|
|
15
15
|
* - Telemetry: Usage tracking for adoption metrics
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
+
// Load environment variables from .env file if it exists
|
|
19
|
+
require('dotenv').config();
|
|
20
|
+
|
|
18
21
|
const fs = require('fs');
|
|
19
22
|
const path = require('path');
|
|
20
23
|
const { Server } = require('@modelcontextprotocol/sdk/server/index.js');
|
package/bin/doc-tools.js
CHANGED
|
@@ -9,14 +9,18 @@
|
|
|
9
9
|
* Get GitHub token from environment variables
|
|
10
10
|
* Checks multiple common variable names in priority order:
|
|
11
11
|
* 1. REDPANDA_GITHUB_TOKEN - Custom Redpanda token
|
|
12
|
-
* 2.
|
|
13
|
-
* 3.
|
|
12
|
+
* 2. ACTIONS_BOT_TOKEN - GitHub Actions bot token
|
|
13
|
+
* 3. GITHUB_TOKEN - GitHub Actions default
|
|
14
|
+
* 4. VBOT_GITHUB_API_TOKEN - Legacy bot token
|
|
15
|
+
* 5. GH_TOKEN - GitHub CLI default
|
|
14
16
|
*
|
|
15
17
|
* @returns {string|null} GitHub token or null if not found
|
|
16
18
|
*/
|
|
17
19
|
function getGitHubToken() {
|
|
18
20
|
return process.env.REDPANDA_GITHUB_TOKEN ||
|
|
21
|
+
process.env.ACTIONS_BOT_TOKEN ||
|
|
19
22
|
process.env.GITHUB_TOKEN ||
|
|
23
|
+
process.env.VBOT_GITHUB_API_TOKEN ||
|
|
20
24
|
process.env.GH_TOKEN ||
|
|
21
25
|
null;
|
|
22
26
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { Octokit } = require('@octokit/rest')
|
|
4
|
+
const { getGitHubToken } = require('./github-token')
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Shared Octokit client instance for GitHub API access
|
|
8
|
+
* Configured with optional authentication and retry logic
|
|
9
|
+
*
|
|
10
|
+
* This singleton instance is shared across all doc-tools modules to:
|
|
11
|
+
* - Avoid redundant initialization
|
|
12
|
+
* - Share rate limit tracking
|
|
13
|
+
* - Centralize GitHub API configuration
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// Get authentication token from environment
|
|
17
|
+
const token = getGitHubToken()
|
|
18
|
+
|
|
19
|
+
// Configure Octokit options
|
|
20
|
+
const octokitOptions = {
|
|
21
|
+
userAgent: 'redpanda-docs-tools',
|
|
22
|
+
retry: {
|
|
23
|
+
enabled: true,
|
|
24
|
+
retries: 3
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Only add auth if token is available
|
|
29
|
+
if (token) {
|
|
30
|
+
octokitOptions.auth = token
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Create singleton instance
|
|
34
|
+
const octokit = new Octokit(octokitOptions)
|
|
35
|
+
|
|
36
|
+
module.exports = octokit
|
|
@@ -25,11 +25,9 @@ module.exports.register = function ({ config }) {
|
|
|
25
25
|
// Use csvpath (legacy) or csvPath
|
|
26
26
|
const localCsvPath = csvpath || null
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const token = getGitHubToken()
|
|
32
|
-
return token ? new Octokit({ auth: token }) : new Octokit()
|
|
28
|
+
function loadOctokit () {
|
|
29
|
+
// Use shared Octokit client
|
|
30
|
+
return require('../cli-utils/octokit-client')
|
|
33
31
|
}
|
|
34
32
|
|
|
35
33
|
// Use 'on' and return the promise so Antora waits for async completion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redpanda-data/docs-extensions-and-macros",
|
|
3
|
-
"version": "4.13.
|
|
3
|
+
"version": "4.13.6",
|
|
4
4
|
"description": "Antora extensions and macros developed for Redpanda documentation.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"antora",
|
|
@@ -103,6 +103,7 @@
|
|
|
103
103
|
"chalk": "4.1.2",
|
|
104
104
|
"cheerio": "^1.1.2",
|
|
105
105
|
"commander": "^14.0.0",
|
|
106
|
+
"dotenv": "^16.6.1",
|
|
106
107
|
"glob": "^11.0.0",
|
|
107
108
|
"gulp": "^4.0.2",
|
|
108
109
|
"gulp-connect": "^5.7.0",
|
|
@@ -70,8 +70,8 @@ function displayClusterType(ct) {
|
|
|
70
70
|
*/
|
|
71
71
|
async function fetchYaml({ owner, repo, path, ref = 'main', token }) {
|
|
72
72
|
try {
|
|
73
|
-
|
|
74
|
-
const octokit =
|
|
73
|
+
// Use shared Octokit client
|
|
74
|
+
const octokit = require('../../cli-utils/octokit-client');
|
|
75
75
|
|
|
76
76
|
console.log(`[cloud-regions] INFO: Fetching ${owner}/${repo}/${path}@${ref} via GitHub API`);
|
|
77
77
|
|
|
@@ -1,23 +1,10 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
octokitInstance = process.env.VBOT_GITHUB_API_TOKEN
|
|
9
|
-
? new Octokit({
|
|
10
|
-
auth: process.env.VBOT_GITHUB_API_TOKEN,
|
|
11
|
-
})
|
|
12
|
-
: new Octokit();
|
|
13
|
-
|
|
14
|
-
if (!process.env.VBOT_GITHUB_API_TOKEN) {
|
|
15
|
-
console.info(
|
|
16
|
-
'No GitHub token found (VBOT_GITHUB_API_TOKEN).'
|
|
17
|
-
);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
return octokitInstance;
|
|
4
|
+
// Use shared Octokit client
|
|
5
|
+
function loadOctokit() {
|
|
6
|
+
const octokit = require('../cli-utils/octokit-client');
|
|
7
|
+
return octokit;
|
|
21
8
|
}
|
|
22
9
|
|
|
23
10
|
async function saveFile(content, saveDir, filename) {
|
|
@@ -23,13 +23,8 @@ module.exports = async function getConsoleVersion({ beta = false, fromAntora = f
|
|
|
23
23
|
useBeta = getPrereleaseFromAntora();
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
//
|
|
27
|
-
const
|
|
28
|
-
const { Octokit } = await import('@octokit/rest');
|
|
29
|
-
const token = getGitHubToken();
|
|
30
|
-
const octokit = token
|
|
31
|
-
? new Octokit({ auth: token })
|
|
32
|
-
: new Octokit();
|
|
26
|
+
// Use shared Octokit client
|
|
27
|
+
const octokit = require('../cli-utils/octokit-client');
|
|
33
28
|
|
|
34
29
|
// Fetch latest release info
|
|
35
30
|
let data;
|
|
@@ -19,13 +19,8 @@ module.exports = async function getRedpandaVersion({ beta = false, fromAntora =
|
|
|
19
19
|
useBeta = getPrereleaseFromAntora();
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
//
|
|
23
|
-
const
|
|
24
|
-
const { Octokit } = await import('@octokit/rest');
|
|
25
|
-
const token = getGitHubToken();
|
|
26
|
-
const octokit = token
|
|
27
|
-
? new Octokit({ auth: token })
|
|
28
|
-
: new Octokit();
|
|
22
|
+
// Use shared Octokit client
|
|
23
|
+
const octokit = require('../cli-utils/octokit-client');
|
|
29
24
|
|
|
30
25
|
// Fetch version data
|
|
31
26
|
let data;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const octokit = require('../../cli-utils/octokit-client');
|
|
2
2
|
const { execSync, spawnSync } = require('child_process');
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
@@ -13,16 +13,6 @@ const https = require('https');
|
|
|
13
13
|
* - Which connectors are self-hosted only
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
// Initialize Octokit with optional authentication
|
|
17
|
-
const octokit = new Octokit({
|
|
18
|
-
auth: process.env.GITHUB_TOKEN,
|
|
19
|
-
userAgent: 'redpanda-docs-tools',
|
|
20
|
-
retry: {
|
|
21
|
-
enabled: true,
|
|
22
|
-
retries: 3
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
|
|
26
16
|
const REPO_OWNER = 'redpanda-data';
|
|
27
17
|
const REPO_NAME = 'connect';
|
|
28
18
|
|
|
@@ -36,11 +36,16 @@ module.exports = function buildConfigYaml(type, connectorName, children, include
|
|
|
36
36
|
return; // skip deprecated fields
|
|
37
37
|
}
|
|
38
38
|
if (!includeAdvanced && field.is_advanced) {
|
|
39
|
-
return; // skip advanced fields in
|
|
39
|
+
return; // skip advanced fields in "common" mode
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
if
|
|
43
|
-
|
|
42
|
+
// Check if this is an array-of-objects (e.g., client_certs[])
|
|
43
|
+
// These should render as empty arrays, not expanded object structures
|
|
44
|
+
if (field.kind === 'array' && field.type === 'object' && Array.isArray(field.children)) {
|
|
45
|
+
// Render as array leaf (e.g., "client_certs: []")
|
|
46
|
+
lines.push(renderLeafField(field, baseIndent));
|
|
47
|
+
} else if (field.type === 'object' && Array.isArray(field.children)) {
|
|
48
|
+
// Render nested object (plain object, not array)
|
|
44
49
|
const nestedLines = renderObjectField(field, baseIndent);
|
|
45
50
|
lines.push(...nestedLines);
|
|
46
51
|
} else {
|
|
@@ -28,7 +28,12 @@ module.exports = function renderObjectField(field, indentLevel) {
|
|
|
28
28
|
if (child.is_deprecated) {
|
|
29
29
|
return; // skip entirely
|
|
30
30
|
}
|
|
31
|
-
if
|
|
31
|
+
// Check if this is an array-of-objects (e.g., client_certs[])
|
|
32
|
+
// These should render as empty arrays, not expanded object structures
|
|
33
|
+
if (child.kind === 'array' && child.type === 'object' && Array.isArray(child.children)) {
|
|
34
|
+
// Render as array leaf (e.g., "client_certs: []")
|
|
35
|
+
lines.push(renderLeafField(child, childIndent));
|
|
36
|
+
} else if (Array.isArray(child.children) && child.children.length > 0) {
|
|
32
37
|
// Nested object → recurse
|
|
33
38
|
lines.push(...renderObjectField(child, childIndent));
|
|
34
39
|
} else {
|
|
@@ -1072,7 +1072,9 @@ async function handleRpcnConnectorDocs (options) {
|
|
|
1072
1072
|
validConnectors.push({
|
|
1073
1073
|
name: connector.name,
|
|
1074
1074
|
type: type.replace(/s$/, ''),
|
|
1075
|
-
status: connector.status || connector.type || 'stable'
|
|
1075
|
+
status: connector.status || connector.type || 'stable',
|
|
1076
|
+
cloudOnly: connector.cloudOnly === true,
|
|
1077
|
+
requiresCgo: connector.requiresCgo === true
|
|
1076
1078
|
})
|
|
1077
1079
|
}
|
|
1078
1080
|
})
|
|
@@ -1119,14 +1121,175 @@ async function handleRpcnConnectorDocs (options) {
|
|
|
1119
1121
|
cloudOnly: path.resolve(process.cwd(), 'modules/components/partials/components/cloud-only')
|
|
1120
1122
|
}
|
|
1121
1123
|
|
|
1122
|
-
|
|
1124
|
+
// Build a set of cloud-supported connectors (inCloud + cloudOnly, excluding self-hosted-only)
|
|
1125
|
+
const cloudSupportedSet = new Set()
|
|
1126
|
+
if (binaryAnalysis?.comparison) {
|
|
1127
|
+
// inCloud = available in both OSS and Cloud
|
|
1128
|
+
binaryAnalysis.comparison.inCloud?.forEach(c => {
|
|
1129
|
+
cloudSupportedSet.add(`${c.type}:${c.name}`)
|
|
1130
|
+
})
|
|
1131
|
+
// cloudOnly = only available in Cloud (not in OSS)
|
|
1132
|
+
binaryAnalysis.comparison.cloudOnly?.forEach(c => {
|
|
1133
|
+
cloudSupportedSet.add(`${c.type}:${c.name}`)
|
|
1134
|
+
})
|
|
1135
|
+
} else {
|
|
1136
|
+
// Fallback when binary analysis is unavailable:
|
|
1137
|
+
// Check all connectors that have cloudSupported flag or assume all non-deprecated are cloud-supported
|
|
1138
|
+
console.log(' ℹ️ Binary analysis unavailable - checking all non-deprecated connectors for cloud-docs')
|
|
1139
|
+
const types = ['inputs', 'outputs', 'processors', 'caches', 'rate_limits', 'buffers', 'metrics', 'scanners', 'tracers']
|
|
1140
|
+
types.forEach(type => {
|
|
1141
|
+
if (Array.isArray(dataObj[type])) {
|
|
1142
|
+
dataObj[type].forEach(connector => {
|
|
1143
|
+
// Include if cloudSupported is explicitly true, or if it's null/undefined and not deprecated
|
|
1144
|
+
const isCloudSupported = connector.cloudSupported === true ||
|
|
1145
|
+
(connector.cloudSupported == null && connector.status !== 'deprecated')
|
|
1146
|
+
if (isCloudSupported && connector.name) {
|
|
1147
|
+
// Store type as plural to match binary analysis format
|
|
1148
|
+
cloudSupportedSet.add(`${type}:${connector.name}`)
|
|
1149
|
+
}
|
|
1150
|
+
})
|
|
1151
|
+
}
|
|
1152
|
+
})
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
// Check for missing connector documentation in rp-connect-docs
|
|
1156
|
+
const allMissing = validConnectors.filter(({ name, type, cloudOnly }) => {
|
|
1123
1157
|
const relPath = path.join(`${type}s`, `${name}.adoc`)
|
|
1124
|
-
|
|
1158
|
+
|
|
1159
|
+
// For cloud-only connectors, ONLY check the cloud-only directory
|
|
1160
|
+
if (cloudOnly) {
|
|
1161
|
+
return !fs.existsSync(path.join(roots.cloudOnly, relPath))
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
// For regular connectors, check pages and partials (not cloud-only)
|
|
1165
|
+
const existsInAny = [roots.pages, roots.partials].some(root =>
|
|
1125
1166
|
fs.existsSync(path.join(root, relPath))
|
|
1126
1167
|
)
|
|
1127
1168
|
return !existsInAny
|
|
1128
1169
|
})
|
|
1129
1170
|
|
|
1171
|
+
// Check for cloud-supported connectors missing from cloud-docs repo (via GitHub API)
|
|
1172
|
+
const missingFromCloudDocs = []
|
|
1173
|
+
const cloudDocsErrors = []
|
|
1174
|
+
if (cloudSupportedSet.size > 0 && options.checkCloudDocs !== false) {
|
|
1175
|
+
console.log('\n INFO: Checking cloud-docs repository for missing connector pages...')
|
|
1176
|
+
|
|
1177
|
+
// Use shared Octokit instance
|
|
1178
|
+
const octokit = require('../../cli-utils/octokit-client')
|
|
1179
|
+
|
|
1180
|
+
try {
|
|
1181
|
+
// Optimization: Fetch entire directory tree in 1 API call instead of 471 individual calls
|
|
1182
|
+
console.log(' Fetching cloud-docs directory tree (1 API call)...')
|
|
1183
|
+
|
|
1184
|
+
let existingFiles = new Set()
|
|
1185
|
+
|
|
1186
|
+
try {
|
|
1187
|
+
// Get the tree for the components directory
|
|
1188
|
+
const { data: tree } = await octokit.git.getTree({
|
|
1189
|
+
owner: 'redpanda-data',
|
|
1190
|
+
repo: 'cloud-docs',
|
|
1191
|
+
tree_sha: 'main:modules/develop/pages/connect/components',
|
|
1192
|
+
recursive: true
|
|
1193
|
+
})
|
|
1194
|
+
|
|
1195
|
+
// Build a set of existing file paths for O(1) lookup
|
|
1196
|
+
tree.tree.forEach(item => {
|
|
1197
|
+
if (item.type === 'blob' && item.path.endsWith('.adoc')) {
|
|
1198
|
+
existingFiles.add(item.path)
|
|
1199
|
+
}
|
|
1200
|
+
})
|
|
1201
|
+
|
|
1202
|
+
console.log(` Loaded ${existingFiles.size} existing connector pages from cloud-docs`)
|
|
1203
|
+
} catch (treeError) {
|
|
1204
|
+
console.log(` WARNING: Could not fetch tree (${treeError.status}), falling back to individual checks`)
|
|
1205
|
+
// If tree API fails, fall back to individual checks (old behavior)
|
|
1206
|
+
existingFiles = null
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
// Check each cloud-supported connector
|
|
1210
|
+
// Filter to only check actual connector/component types that need individual pages
|
|
1211
|
+
const connectorTypes = ['inputs', 'outputs', 'processors', 'caches', 'buffers', 'scanners', 'metrics', 'tracers']
|
|
1212
|
+
|
|
1213
|
+
for (const connectorKey of cloudSupportedSet) {
|
|
1214
|
+
const [type, name] = connectorKey.split(':')
|
|
1215
|
+
|
|
1216
|
+
// Skip non-connector types (config, bloblang-functions, bloblang-methods, rate-limits)
|
|
1217
|
+
if (!connectorTypes.includes(type)) {
|
|
1218
|
+
continue
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
// Skip deprecated connectors - they don't need cloud-docs pages
|
|
1222
|
+
if (Array.isArray(dataObj[type])) {
|
|
1223
|
+
const connector = dataObj[type].find(c => c.name === name)
|
|
1224
|
+
if (connector && connector.status === 'deprecated') {
|
|
1225
|
+
continue
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
const relativePath = `${type}/${name}.adoc`
|
|
1230
|
+
const fullPath = `modules/develop/pages/connect/components/${relativePath}`
|
|
1231
|
+
|
|
1232
|
+
// Fast path: Check against tree if we have it
|
|
1233
|
+
if (existingFiles !== null) {
|
|
1234
|
+
if (!existingFiles.has(relativePath)) {
|
|
1235
|
+
missingFromCloudDocs.push({ type, name, path: fullPath })
|
|
1236
|
+
}
|
|
1237
|
+
continue
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
// Fallback path: Individual API calls (only if tree fetch failed)
|
|
1241
|
+
try {
|
|
1242
|
+
await octokit.repos.getContent({
|
|
1243
|
+
owner: 'redpanda-data',
|
|
1244
|
+
repo: 'cloud-docs',
|
|
1245
|
+
path: fullPath,
|
|
1246
|
+
ref: 'main'
|
|
1247
|
+
})
|
|
1248
|
+
// File exists, no action needed
|
|
1249
|
+
} catch (error) {
|
|
1250
|
+
if (error.status === 404) {
|
|
1251
|
+
// File doesn't exist in cloud-docs
|
|
1252
|
+
missingFromCloudDocs.push({ type, name, path: fullPath })
|
|
1253
|
+
} else {
|
|
1254
|
+
// Non-404 error - record as error
|
|
1255
|
+
cloudDocsErrors.push({
|
|
1256
|
+
type,
|
|
1257
|
+
name,
|
|
1258
|
+
path: fullPath,
|
|
1259
|
+
status: error.status || 'unknown',
|
|
1260
|
+
message: error.message
|
|
1261
|
+
})
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
// Report results
|
|
1267
|
+
if (cloudDocsErrors.length > 0) {
|
|
1268
|
+
console.log(` WARNING: Encountered ${cloudDocsErrors.length} error(s) while checking cloud-docs (check inconclusive):`)
|
|
1269
|
+
cloudDocsErrors.forEach(({ type, name, status, message }) => {
|
|
1270
|
+
console.log(` - ${type}/${name} - Status ${status}: ${message}`)
|
|
1271
|
+
})
|
|
1272
|
+
console.log(` INFO: Please resolve these errors (e.g., check GITHUB_TOKEN or VBOT_GITHUB_API_TOKEN, API rate limits, network connectivity)`)
|
|
1273
|
+
if (missingFromCloudDocs.length > 0) {
|
|
1274
|
+
console.log(` INFO: Additionally, ${missingFromCloudDocs.length} connector(s) confirmed missing from cloud-docs:`)
|
|
1275
|
+
missingFromCloudDocs.forEach(({ type, name }) => {
|
|
1276
|
+
console.log(` - ${type}/${name}`)
|
|
1277
|
+
})
|
|
1278
|
+
}
|
|
1279
|
+
} else if (missingFromCloudDocs.length > 0) {
|
|
1280
|
+
console.log(` WARNING: Found ${missingFromCloudDocs.length} cloud-supported connector(s) missing from cloud-docs:`)
|
|
1281
|
+
missingFromCloudDocs.forEach(({ type, name }) => {
|
|
1282
|
+
console.log(` - ${type}/${name}`)
|
|
1283
|
+
})
|
|
1284
|
+
console.log(` INFO: These connectors need pages added to https://github.com/redpanda-data/cloud-docs`)
|
|
1285
|
+
} else {
|
|
1286
|
+
console.log(` All cloud-supported connectors have pages in cloud-docs`)
|
|
1287
|
+
}
|
|
1288
|
+
} catch (error) {
|
|
1289
|
+
console.log(` WARNING: Could not check cloud-docs: ${error.message}`)
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1130
1293
|
const missingConnectors = allMissing.filter(c =>
|
|
1131
1294
|
!c.name.includes('sql_driver') &&
|
|
1132
1295
|
c.status !== 'deprecated'
|