UNPKG

@extenshi/mcp

Version:

Extenshi MCP server — bring the extension catalog (search, security, market research) and pre-publish scanning into your AI tools

141 lines • 6.56 kB
/** * Page Generator: an extension's landing / homepage as ONE static HTML file. * * No JavaScript in the output — pure HTML/CSS, so it deploys anywhere * (GitHub Pages, Netlify, S3) and trivially passes CSP. Store CTAs render * only for the stores the developer actually filled in. * * Every user-supplied string is HTML-escaped, every link is scheme-clamped * (safeHref: http(s) only; the support link may also be a single mailto: * address) and every image source is clamped to https, a * relative path or a raster `data:image/…;base64` URL (safeImageSrc). An * inline SVG logo is validated (landingLogoSvgProblem) and then embedded * through `<img src="data:image/svg+xml,…">`, where browsers never run * scripts or fetch external resources — so even a validator miss cannot * script the page. * * SINGLE SOURCE, THREE COPIES: * shared-types/landing-page.ts (canonical — edit here; dojo re-exports it) * tools/extenshi-mcp/src/landing-page.ts (copy for the published @extenshi/mcp) * tools/extenshi-cli/src/landing-page.ts (copy for the published @extenshi/cli) * The published packages cannot import workspace shared-types at runtime. * The copies differ from this file ONLY in the import specifiers * (`./json-ld.js` there, the package specifier here — see * module-specifiers.test.ts for why shared-types may not use relative ones). * Schema v2 (sections, theme tokens, locales, SEO) lives in * landing-sections.ts, copied the same way; `renderLandingForm` there is the * entry point that dispatches v1 forms to buildLandingPage below. * `landing-page-sync.test.ts` in both packages fails when a copy drifts. * After editing, run: * sed "s#from 'shared-types\/#from './#" shared-types/landing-page.ts > tools/extenshi-mcp/src/landing-page.ts * sed "s#from 'shared-types\/#from './#" shared-types/landing-page.ts > tools/extenshi-cli/src/landing-page.ts */ import type { LandingV2Fields } from './landing-sections.js'; export type LandingBrowser = 'chrome' | 'firefox' | 'edge'; export interface LandingFeature { title: string; description: string; } export interface LandingScreenshot { /** https URL, or a path relative to the page (e.g. `screenshots/popup.png`). */ url: string; alt: string; } /** * Serializable form persisted as `page-generator` tool state (`{ form }`). * The optional fields were added later; rows saved before them still hydrate. * The v2 fields (sections, tokens, seo, locales — landing-sections.ts) are read * only when `schemaVersion` is 2; buildLandingPage below never looks at them. */ export interface LandingForm extends LandingV2Fields { extensionName: string; tagline: string; description: string; accentColor: string; theme: 'light' | 'dark'; chromeUrl: string; firefoxUrl: string; edgeUrl: string; privacyPolicyUrl: string; supportUrl: string; features: LandingFeature[]; /** Public https URL the page will live at: canonical link, og:url, JSON-LD url. */ homepageUrl?: string; /** Logo image: https URL, relative path, or `data:image/(png|jpeg|gif|webp);base64,…`. */ logoUrl?: string; /** Inline SVG logo source; wins over logoUrl when it passes validation. */ logoSvg?: string; screenshots?: LandingScreenshot[]; } /** Input limits shared by the MCP tool and CLI schemas. */ export declare const LANDING_LIMITS: { readonly extensionName: 120; readonly tagline: 200; readonly description: 2000; readonly url: 2000; readonly features: 12; readonly featureTitle: 120; readonly featureDescription: 500; readonly screenshots: 8; readonly screenshotAlt: 200; readonly logoSvgBytes: 100000; readonly logoUrl: 400000; }; export declare const DEFAULT_LANDING_FORM: LandingForm; export interface LandingPageInput { extensionName: string; tagline: string; description: string; accentColor: string; theme: 'light' | 'dark'; storeUrls: Partial<Record<LandingBrowser, string>>; features: LandingFeature[]; privacyPolicyUrl: string; supportUrl: string; /** * Optional inline SVG logo. Validated with landingLogoSvgProblem() and * embedded as an `<img>` data URI; a rejected SVG falls back to logoUrl, * then to a monogram. */ logoSvg?: string; logoUrl?: string; homepageUrl?: string; screenshots?: LandingScreenshot[]; } /** * Rebuild the editor form from a saved `page-generator` row (or the project * name when nothing is saved). Called during render — not in an effect — * so the live preview iframe mounts with this payload, not the empty default. * An older row that predates `features` (or the optional fields) still yields * a full form. */ export declare function hydrateLandingForm(saved: { form?: Partial<LandingForm>; } | null | undefined, projectName?: string | null): LandingForm; export declare function landingFormToInput(form: LandingForm, logoSvg?: string): LandingPageInput; /** Tolerates non-strings from hand-edited JSON or an old saved row. */ export declare function str(v: unknown): string; export declare function esc(s: string): string; export declare function safeHref(raw: string): string; /** safeHref() for the support link only: http(s), or a validated mailto: address. */ export declare function safeSupportHref(raw: string): string; /** Image src clamp: https, relative path, or raster data URI; '' when unsafe. */ export declare function safeImageSrc(raw: unknown): string; export declare function canonicalUrl(raw: unknown): string; /** Absolute https form of an image URL (for og:image / JSON-LD), or ''. */ export declare function absoluteImageUrl(raw: unknown, base: string): string; /** * Why an inline SVG logo is refused, or null when it is acceptable. A refusal * never throws: buildLandingPage falls back to logoUrl / the monogram, and the * MCP tool and CLI surface the reason instead of silently dropping the logo. */ export declare function landingLogoSvgProblem(svg: string): string | null; export declare const HEX_COLOR: RegExp; /** * Human-readable notes about input the page silently corrected (a link clamped * to "#", an unsafe image dropped, a refused logo). Same rules as the builder, * so agents and the CLI can report exactly what did not make it into the page. */ export declare function landingPageWarnings(input: LandingPageInput): string[]; export declare function buildLandingPage(input: LandingPageInput): string; //# sourceMappingURL=landing-page.d.ts.map