2022-12-22 08:01:29 -08:00
|
|
|
#!/usr/bin/env node
|
|
|
|
|
2024-01-09 10:17:07 -08:00
|
|
|
const packages = ['nodes-base', '@n8n/nodes-langchain'];
|
|
|
|
const concurrency = 20;
|
|
|
|
let exitCode = 0;
|
|
|
|
|
|
|
|
const debug = require('debug')('n8n');
|
2022-12-22 08:01:29 -08:00
|
|
|
const path = require('path');
|
|
|
|
const https = require('https');
|
2024-01-09 10:17:07 -08:00
|
|
|
const glob = require('glob');
|
2022-12-22 08:01:29 -08:00
|
|
|
const pLimit = require('p-limit');
|
2024-01-09 10:17:07 -08:00
|
|
|
const Lookup = require('cacheable-lookup').default;
|
2022-12-22 08:01:29 -08:00
|
|
|
|
2024-01-09 10:17:07 -08:00
|
|
|
const agent = new https.Agent({ keepAlive: true, keepAliveMsecs: 5000 });
|
|
|
|
new Lookup().install(agent);
|
|
|
|
const limiter = pLimit(concurrency);
|
2022-12-22 08:01:29 -08:00
|
|
|
|
|
|
|
const validateUrl = async (kind, name, documentationUrl) =>
|
|
|
|
new Promise((resolve, reject) => {
|
|
|
|
if (!documentationUrl) resolve([name, null]);
|
|
|
|
const url = new URL(
|
|
|
|
/^https?:\/\//.test(documentationUrl)
|
|
|
|
? documentationUrl
|
|
|
|
: `https://docs.n8n.io/integrations/builtin/${kind}/${documentationUrl.toLowerCase()}/`,
|
|
|
|
);
|
|
|
|
https
|
|
|
|
.request(
|
|
|
|
{
|
|
|
|
hostname: url.hostname,
|
|
|
|
port: 443,
|
|
|
|
path: url.pathname,
|
|
|
|
method: 'HEAD',
|
2024-01-09 10:17:07 -08:00
|
|
|
agent,
|
|
|
|
},
|
|
|
|
(res) => {
|
|
|
|
debug('✓', kind, name);
|
|
|
|
resolve([name, res.statusCode]);
|
2022-12-22 08:01:29 -08:00
|
|
|
},
|
|
|
|
)
|
|
|
|
.on('error', (e) => reject(e))
|
|
|
|
.end();
|
|
|
|
});
|
|
|
|
|
2024-01-09 10:17:07 -08:00
|
|
|
const checkLinks = async (baseDir, kind) => {
|
|
|
|
let types = require(path.join(baseDir, `dist/types/${kind}.json`));
|
2022-12-22 08:01:29 -08:00
|
|
|
if (kind === 'nodes')
|
|
|
|
types = types.filter(({ codex }) => !!codex?.resources?.primaryDocumentation);
|
2024-01-09 10:17:07 -08:00
|
|
|
debug(kind, types.length);
|
|
|
|
|
2022-12-22 08:01:29 -08:00
|
|
|
const statuses = await Promise.all(
|
|
|
|
types.map((type) =>
|
2024-01-09 10:17:07 -08:00
|
|
|
limiter(() => {
|
2022-12-22 08:01:29 -08:00
|
|
|
const documentationUrl =
|
|
|
|
kind === 'credentials'
|
|
|
|
? type.documentationUrl
|
|
|
|
: type.codex?.resources?.primaryDocumentation?.[0]?.url;
|
|
|
|
return validateUrl(kind, type.displayName, documentationUrl);
|
|
|
|
}),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
const missingDocs = [];
|
|
|
|
const invalidUrls = [];
|
|
|
|
for (const [name, statusCode] of statuses) {
|
|
|
|
if (statusCode === null) missingDocs.push(name);
|
|
|
|
if (statusCode !== 200) invalidUrls.push(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (missingDocs.length) console.log('Documentation URL missing for %s', kind, missingDocs);
|
|
|
|
if (invalidUrls.length) console.log('Documentation URL invalid for %s', kind, invalidUrls);
|
2024-01-09 10:17:07 -08:00
|
|
|
if (missingDocs.length || invalidUrls.length) exitCode = 1;
|
2022-12-22 08:01:29 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
(async () => {
|
2024-01-09 10:17:07 -08:00
|
|
|
for (const packageName of packages) {
|
|
|
|
const baseDir = path.resolve(__dirname, '../../packages', packageName);
|
|
|
|
await Promise.all([checkLinks(baseDir, 'credentials'), checkLinks(baseDir, 'nodes')]);
|
|
|
|
if (exitCode !== 0) process.exit(exitCode);
|
|
|
|
}
|
2022-12-22 08:01:29 -08:00
|
|
|
})();
|