UNPKG

signalk-server

Version:

An implementation of a [Signal K](http://signalk.org) server for boats.

128 lines 4.86 kB
"use strict"; /* * Copyright 2026 Signal K contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 */ Object.defineProperty(exports, "__esModule", { value: true }); exports.buildPluginDetail = buildPluginDetail; exports.readDetailFromCache = readDetailFromCache; const categories_1 = require("../categories"); const debug_1 = require("../debug"); const cdn_1 = require("./cdn"); const github_releases_1 = require("./github-releases"); const indicators_1 = require("./indicators"); const debug = (0, debug_1.createDebug)('signalk-server:appstore:detail'); const DEFAULT_TIMEOUT_MS = 20_000; async function fetchText(url, options = {}) { const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS; try { const res = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) }); if (!res.ok) { debug.enabled && debug('fetchText %s returned %d', url, res.status); // 5xx is a CDN/origin failure, not a definitive 'asset missing' // signal — the asset may still exist once the outage clears. if (res.status >= 500 && res.status < 600) return { kind: 'error' }; return { kind: 'missing' }; } return { kind: 'ok', text: await res.text() }; } catch (err) { debug.enabled && debug('fetchText %s failed: %O', url, err); return { kind: 'error' }; } } function resolveDependencyList(names, resolver) { if (!names || names.length === 0) return []; return names.map((name) => { const lookup = resolver?.(name); return { name, displayName: lookup?.displayName, appIcon: lookup?.appIcon, installed: lookup?.installed ?? false }; }); } async function buildPluginDetail(summary, resolver) { let readmeText = ''; if (summary.npmReadme && summary.npmReadme.trim().length > 0) { readmeText = summary.npmReadme; } else { const fetched = await fetchText((0, cdn_1.readmeUrlFor)(summary.name, summary.version)); if (fetched.kind === 'ok') readmeText = fetched.text; } // Prefer GitHub Releases as the changelog source — that's where most // plugin authors put real release notes. Fall back to a CHANGELOG.md in // the published tarball for packages not hosted on GitHub. Rendered to // Markdown via unified/rehype-remark so the existing Changelog tab // renders release notes directly. let changelog = ''; let changelogFormat = 'synthesized'; const slug = (0, github_releases_1.parseGithubSlug)(summary.githubUrl); if (slug) { const releases = await (0, github_releases_1.fetchReleasesMarkdown)(slug.owner, slug.repo); if (releases && releases.trim()) { changelog = releases; changelogFormat = 'markdown'; } } if (!changelog) { const fetched = await fetchText((0, cdn_1.changelogUrlFor)(summary.name, summary.version)); if (fetched.kind === 'ok' && fetched.text.trim().length > 0) { changelog = fetched.text; changelogFormat = 'markdown'; } } const indicators = (0, indicators_1.computeIndicators)({ hasRepository: !!summary.githubUrl, githubUrl: summary.githubUrl, hasScreenshots: summary.screenshots.length > 0, hasAppIcon: !!summary.appIcon, description: summary.description, readme: readmeText, keywords: summary.keywords, lastReleaseDate: summary.lastReleaseDate }); return { name: summary.name, version: summary.version, displayName: summary.displayName, appIcon: summary.appIcon, screenshots: summary.screenshots, official: summary.official, deprecated: summary.deprecated, description: summary.description, author: summary.author, categories: summary.categories, githubUrl: summary.githubUrl, npmUrl: summary.npmUrl, isPlugin: summary.isPlugin, isWebapp: summary.isWebapp, readme: readmeText, changelog, indicators, requires: resolveDependencyList(summary.requires, resolver), recommends: resolveDependencyList(summary.recommends, resolver), recent: (0, categories_1.isRecent)(summary.lastReleaseDate), readmeFormat: 'markdown', changelogFormat, fetchedAt: Date.now(), fromCache: false }; } function readDetailFromCache(cache, name) { const cached = cache.readPluginDetail(name); return cached?.payload; } //# sourceMappingURL=detail.js.map