'use strict'; let Fetcher = require('../_common/fetcher.js'); // https://blog.risingstack.com/update-nodejs-8-end-of-life-no-support/ // 6 mos "current" + 18 mos LTS "active" + 12 mos LTS "maintenance" //let endOfLife = 3 * 366 * 24 * 60 * 60 * 1000; // If there have been no updates in 12 months, it's almost certainly end-of-life const END_OF_LIFE = 366 * 24 * 60 * 60 * 1000; // OSes /** @type {Object.} */ let osMap = { osx: 'macos', // NOTE: filename is 'darwin' linux: 'linux', win: 'windows', // windows sunos: 'sunos', aix: 'aix', }; // CPU architectures /** @type {Object.} */ let archMap = { x64: 'amd64', x86: 'x86', ppc64: 'ppc64', ppc64le: 'ppc64le', arm64: 'arm64', armv7l: 'armv7l', armv6l: 'armv6l', s390x: 's390x', }; // file extensions /** @type {Object.>} */ let pkgMap = { pkg: ['pkg'], //exe: ['exe'], // disable '7z': ['7z'], zip: ['zip'], tar: ['tar.gz', 'tar.xz'], // oddity - no os in download msi: ['msi'], // oddity - no pkg info musl: ['tar.gz', 'tar.xz'], }; /** * @typedef BuildInfo * @prop {String} version * @prop {String} [_version] * @prop {String} arch * @prop {String} channel * @prop {String} date * @prop {String} download * @prop {String} ext * @prop {String} [_filename] * @prop {String} [hash] * @prop {String} libc * @prop {Boolean} lts * @prop {String} os */ async function getDistributables() { let all = { /** @type {Array} */ releases: [], download: '', }; /* [ { "version":"v20.3.1", "date":"2023-06-20", "files":["headers","linux-armv6l","linux-x64-musl","linux-x64-pointer-compression"], "npm":"9.6.7", "v8":"11.3.244.8", "uv":"1.45.0", "zlib":"1.2.13.1-motley", "openssl":"3.0.9+quic", "modules":"115", "lts":false, "security":true }, ] */ { // Alternate: 'https://nodejs.org/dist/index.json', let baseUrl = `https://nodejs.org/download/release`; // Fetch official builds let resp; try { resp = await Fetcher.fetch(`${baseUrl}/index.json`, { headers: { Accept: 'application/json' }, }); } catch (e) { /** @type {Error & { code: string, response: { status: number, body: string } }} */ //@ts-expect-error let err = e; if (err.code === 'E_FETCH_RELEASES') { err.message = `failed to fetch 'node' release data: ${err.response.status} ${err.response.body}`; } throw e; } let data = JSON.parse(resp.body); void transform(baseUrl, data); } { let unofficialBaseUrl = `https://unofficial-builds.nodejs.org/download/release`; // Fetch unofficial builds let resp; try { resp = await Fetcher.fetch(`${unofficialBaseUrl}/index.json`, { headers: { Accept: 'application/json' }, }); } catch (e) { /** @type {Error & { code: string, response: { status: number, body: string } }} */ //@ts-expect-error let err = e; if (err.code === 'E_FETCH_RELEASES') { err.message = `failed to fetch 'node' (unofficial) release data: ${err.response.status} ${err.response.body}`; } throw e; } let data = JSON.parse(resp.body); transform(unofficialBaseUrl, data); } /** * @param {String} baseUrl * @param {Array} builds */ function transform(baseUrl, builds) { for (let build of builds) { let buildDate = new Date(build.date).valueOf(); let age = Date.now() - buildDate; let maintained = age < END_OF_LIFE; if (!maintained) { continue; } let lts = false !== build.lts; // skip 'v' let vparts = build.version.slice(1).split('.'); let major = parseInt(vparts[0], 10); let channel = 'stable'; let isEven = 0 === major % 2; if (!isEven) { channel = 'beta'; } for (let file of build.files) { if ('src' === file || 'headers' === file) { continue; } let fileParts = file.split('-'); let osPart = fileParts[0]; let os = osMap[osPart]; let archPart = fileParts[1]; let arch = archMap[archPart]; let libc = ''; let pkgPart = fileParts[2]; let pkgs = pkgMap[pkgPart]; if (!pkgPart) { pkgs = pkgMap.tar; } if (!pkgs?.length) { continue; } let extra = ''; let muslNative; if (fileParts[2] === 'musl') { extra = '-musl'; muslNative = true; libc = 'musl'; } else if (os === 'linux') { libc = 'gnu'; } if (osPart === 'osx') { osPart = 'darwin'; } for (let pkg of pkgs) { let filename = `node-${build.version}-${osPart}-${archPart}${extra}.${pkg}`; if ('msi' === pkg) { filename = `node-${build.version}-${archPart}${extra}.${pkg}`; } let downloadUrl = `${baseUrl}/${build.version}/${filename}`; let release = { name: filename, version: build.version, lts: lts, channel: channel, date: build.date, os: os, arch: arch, ext: pkg, download: downloadUrl, libc: libc, }; all.releases.push(release); } } } } return all; } module.exports = getDistributables; if (module === require.main) { getDistributables().then(function (all) { all = require('../_webi/normalize.js')(all); console.info(JSON.stringify(all)); //console.info(JSON.stringify(all, null, 2)); }); }