UNPKG

signalk-server

Version:

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

176 lines 6.57 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.enrichEntry = enrichEntry; const value_1 = require("@sinclair/typebox/value"); const debug_1 = require("../debug"); const cdn_1 = require("./cdn"); const indicators_1 = require("./indicators"); const schemas_1 = require("./schemas"); const debug = (0, debug_1.createDebug)('signalk-server:appstore:enrich'); const MAX_SCREENSHOTS = 6; const DEPRECATED_KEYWORD = 'signalk-deprecated'; const OFFICIAL_PREFIX = '@signalk/'; function isGithubUrl(url) { // Require a real boundary in front of github.com so a URL containing // 'notgithub.com' followed by 'github.com' later in the path can't // accidentally match. ':\\/' covers https:// and git+ssh://; // '@' covers the ssh form (git@github.com:...). return !!url && /(^|:\/\/|@)github\.com[/:]/i.test(url); } function extractGithubUrl(pkg) { const candidates = []; if (pkg.links?.repository) candidates.push(pkg.links.repository); if (typeof pkg.repository === 'string') candidates.push(pkg.repository); else if (pkg.repository?.url) candidates.push(pkg.repository.url); for (const c of candidates) { if (c && isGithubUrl(c)) { return (c .replace(/^git\+/, '') .replace(/^git:\/\//, 'https://') // ssh://git@github.com/... is a valid clone URL but not a // browser-openable link. Rewrite it the same way we rewrite // the scp-style git@github.com:... form below. .replace(/^ssh:\/\/git@github\.com\//, 'https://github.com/') .replace(/\.git$/, '') .replace(/^git@github\.com:/, 'https://github.com/')); } } return undefined; } function extractIssuesUrl(pkg) { if (pkg.links?.bugs) return pkg.links.bugs; if (typeof pkg.bugs === 'string') return pkg.bugs; if (pkg.bugs?.url) return pkg.bugs.url; const gh = extractGithubUrl(pkg); return gh ? `${gh}/issues` : undefined; } function parseSignalKMetadata(pkg) { if (!pkg.signalk || typeof pkg.signalk !== 'object') return undefined; if (value_1.Value.Check(schemas_1.SignalKPackageMetadataSchema, pkg.signalk)) { return pkg.signalk; } debug.enabled && debug('%s: signalk key did not match schema; falling back to loose parse', pkg.name); const loose = pkg.signalk; return { displayName: typeof loose.displayName === 'string' ? loose.displayName : undefined, appIcon: typeof loose.appIcon === 'string' ? loose.appIcon : undefined, screenshots: Array.isArray(loose.screenshots) ? loose.screenshots.filter((s) => typeof s === 'string') : undefined, deprecated: typeof loose.deprecated === 'boolean' ? loose.deprecated : undefined, requires: Array.isArray(loose.requires) ? loose.requires.filter((s) => typeof s === 'string') : undefined, recommends: Array.isArray(loose.recommends) ? loose.recommends.filter((s) => typeof s === 'string') : undefined }; } function defaultResolve(pkg, version, declaredPath, lookup) { const cached = lookup?.(pkg, version, declaredPath); if (cached) return cached; return (0, cdn_1.resolveScreenshotUrl)(pkg, version, declaredPath); } function resolveScreenshotList(pkg, meta, lookup) { const raw = meta?.screenshots; if (!Array.isArray(raw)) return []; const out = []; for (const entry of raw) { if (typeof entry !== 'string' || entry.trim() === '') continue; const resolved = (0, cdn_1.isAbsoluteUrl)(entry) ? entry : defaultResolve(pkg.name, pkg.version, entry, lookup); out.push(resolved); if (out.length >= MAX_SCREENSHOTS) break; } return out; } function resolveAppIcon(pkg, meta, lookup) { const icon = meta?.appIcon; if (!icon || typeof icon !== 'string' || icon.trim() === '') return undefined; return (0, cdn_1.isAbsoluteUrl)(icon) ? icon : defaultResolve(pkg.name, pkg.version, icon, lookup); } function isDeprecated(pkg, meta) { if (meta?.deprecated === true) return true; return (pkg.keywords || []).includes(DEPRECATED_KEYWORD); } function isOfficial(pkg) { return pkg.name.startsWith(OFFICIAL_PREFIX); } function normalizeNameList(value) { if (!Array.isArray(value)) return undefined; const seen = new Set(); const out = []; for (const entry of value) { if (typeof entry !== 'string') continue; const trimmed = entry.trim(); if (trimmed === '' || seen.has(trimmed)) continue; seen.add(trimmed); out.push(trimmed); } return out.length > 0 ? out : undefined; } function enrichEntry(pkg, options = {}) { const meta = parseSignalKMetadata(pkg); const screenshots = resolveScreenshotList(pkg, meta, options.iconUrlLookup); const appIcon = resolveAppIcon(pkg, meta, options.iconUrlLookup); const githubUrl = extractGithubUrl(pkg); const issuesUrl = extractIssuesUrl(pkg); const deprecated = isDeprecated(pkg, meta); const official = isOfficial(pkg); const result = { displayName: meta?.displayName, appIcon, screenshots: screenshots.length > 0 ? screenshots : undefined, official, deprecated, readmeUrl: (0, cdn_1.readmeUrlFor)(pkg.name, pkg.version), changelogUrl: (0, cdn_1.changelogUrlFor)(pkg.name, pkg.version), githubUrl, issuesUrl, requires: normalizeNameList(meta?.requires), recommends: normalizeNameList(meta?.recommends) }; if (options.includeIndicators) { result.indicators = (0, indicators_1.computeIndicators)({ hasRepository: !!githubUrl, githubUrl, hasScreenshots: screenshots.length > 0, hasAppIcon: !!appIcon, description: pkg.description, readme: pkg.readme, keywords: pkg.keywords, ...(options.indicatorInputs || {}) }); } return result; } //# sourceMappingURL=enrich.js.map