/** * Formatting utilities for RIS MCP Server responses. * * This module provides functions to format RIS API responses for optimal * LLM consumption, including proper Austrian legal citations, search results * formatting, and document content preparation. */ import type { Document, SearchResult } from './types.js'; /** Hard limit for a single text response, in characters. */ export declare const CHARACTER_LIMIT = 25000; /** * Convert API date format to readable German format. */ export declare function formatDate(dateStr: string | null | undefined): string; /** * Convert HTML to clean readable text using cheerio. */ export declare function htmlToText(htmlContent: string): string; /** * Reduce a full RIS *website* page to the document it displays. * * `ris_dokument` mostly fetches bare document HTML, but two of its routes serve * a rendered page instead — `GeltendeFassung.wxe` (a whole consolidated law) and * `Dokument.wxe` (a single norm). Those carry the site around the text: skip * links ("Seitenbereiche:"), the header menu, a version navigation, the footer * ("Zum Seitenanfang", the Bundeskanzleramt copyright line). Handed to * {@link htmlToText} whole, the reader gets the navigation first and the law * somewhere below it, and {@link extractOutline} offers the chrome headings as * jump targets (issue #94). * * The site is dropped by *keeping* `.documentContent` rather than by removing * the chrome, because the chrome is not addressable: the version navigation on * `Dokument.wxe` is an attribute-less `
` that no selector can name. The one * exception is `.Warning` — chrome by position, content by meaning, since it * says whether the Fassung below is still in force. It is kept from its place in * the document, not appended, so it stays above the text it qualifies. * * `.onlyScreenreader` survives inside the kept fragments: unlike the `.sr-only` * pairs htmlToText drops as spoken duplicates, it is the only carrier of the * `§ n` section markers of a consolidated law. * * A page without `.documentContent` — every bare document route — is returned * as the identical string, so the far more common path is not re-serialised. */ export declare function scopeRisContent(html: string): { html: string; pageTitle: string | null; }; /** * Document data for citation formatting. */ interface CitationData { applikation?: string; titel?: string; kurztitel?: string | null; dokumentnummer?: string; citation?: { kurztitel?: string | null; langtitel?: string | null; kundmachungsorgan?: string | null; paragraph?: string | null; inkrafttreten?: string | null; }; } /** * Format a proper Austrian legal citation. */ export declare function formatCitation(doc: Document | CitationData): string; /** * Format search results for display. */ export declare function formatSearchResults(results: SearchResult | Record, format?: 'markdown' | 'json'): string; /** * Metadata for document formatting. */ export interface DocumentMetadata { dokumentnummer?: string; applikation?: string; titel?: string; kurztitel?: string | null; citation?: { kurztitel?: string | null; langtitel?: string | null; kundmachungsorgan?: string | null; paragraph?: string | null; eli?: string | null; inkrafttreten?: string | null; ausserkrafttreten?: string | null; }; dokument_url?: string | null; gesamte_rechtsvorschrift_url?: string | null; } /** * Format a full document for LLM context. */ export declare function formatDocument(content: string, metadata: DocumentMetadata, format?: 'markdown' | 'json'): string; /** One jump target in a document, as offered by the viewer's outline. */ export interface OutlineEntry { /** * Heading level 1–6, taken from the RIS source markup. Level 1 is the RIS * record layer (field labels plus, in gazettes, the masthead); levels 2 and * below are structure inside the document text. */ level: number; /** Heading text, whitespace-normalised exactly as the document text is. */ label: string; /** Character offset of the heading's line in the document text. */ offset: number; /** * Characters up to the next heading, or to the end of the text for the last * entry. The only reliable way to tell a one-line metadata field from a real * section: RIS gives both the same element and the same class. */ span: number; } /** * Extract the jump targets of a document. * * `html` is the RIS source, `text` the markdown rendering it produced. The * headings are read from the markup — the rendered text carries no structure * beyond the three headings formatDocument() writes itself — and then located in * `text` by their own line. * * A document without headings yields `[]`. That is a valid outline, not an * error: the viewer stays navigable through plain offset paging. */ export declare function extractOutline(html: string, text: string): OutlineEntry[]; /** One slice of a document text, addressed by character offset. */ export interface ResponseChunk { /** The slice starting at the requested offset, at most `limit` characters. */ text: string; /** Length of the complete text this chunk was cut from. */ total_length: number; /** Offset for the following chunk, or null when this chunk ends the text. */ next_offset: number | null; } /** * Cut one chunk out of `text`, starting at `offset`. * * Chunk boundaries follow the same paragraph-then-sentence rule as * {@link truncateResponse}; unlike that function nothing is appended and nothing * is dropped — consecutive chunks concatenate back to `text`. * * Offsets are character offsets into the complete text, i.e. the unit of * `String.length` and `String.slice`. They stay valid as long as the same text * does, which is why every chunk carries `total_length`: a caller whose text was * re-fetched in between sees the length change and restarts at offset 0. */ export declare function chunkResponse(text: string, offset?: number, limit?: number): ResponseChunk; /** * Truncate response if too long. */ export declare function truncateResponse(text: string, limit?: number): string; export {}; //# sourceMappingURL=formatting.d.ts.map