ris-mcp-ts 1.4.2 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +6 -4
  2. package/dist/document-cache.d.ts +51 -0
  3. package/dist/document-cache.d.ts.map +1 -0
  4. package/dist/document-cache.js +117 -0
  5. package/dist/document-cache.js.map +1 -0
  6. package/dist/document-loader.d.ts +45 -0
  7. package/dist/document-loader.d.ts.map +1 -0
  8. package/dist/document-loader.js +186 -0
  9. package/dist/document-loader.js.map +1 -0
  10. package/dist/facets.d.ts +30 -0
  11. package/dist/facets.d.ts.map +1 -0
  12. package/dist/facets.js +48 -0
  13. package/dist/facets.js.map +1 -0
  14. package/dist/formatting.d.ts +55 -0
  15. package/dist/formatting.d.ts.map +1 -1
  16. package/dist/formatting.js +170 -37
  17. package/dist/formatting.js.map +1 -1
  18. package/dist/generated/trefferliste-html.d.ts.map +1 -1
  19. package/dist/generated/trefferliste-html.js +1 -1
  20. package/dist/generated/trefferliste-html.js.map +1 -1
  21. package/dist/generated/viewer-html.d.ts +2 -0
  22. package/dist/generated/viewer-html.d.ts.map +1 -0
  23. package/dist/generated/viewer-html.js +3 -0
  24. package/dist/generated/viewer-html.js.map +1 -0
  25. package/dist/helpers.d.ts +8 -1
  26. package/dist/helpers.d.ts.map +1 -1
  27. package/dist/helpers.js.map +1 -1
  28. package/dist/http.d.ts.map +1 -1
  29. package/dist/http.js +8 -1
  30. package/dist/http.js.map +1 -1
  31. package/dist/parser.d.ts.map +1 -1
  32. package/dist/parser.js +3 -23
  33. package/dist/parser.js.map +1 -1
  34. package/dist/tools/dokument-abschnitt.d.ts +12 -0
  35. package/dist/tools/dokument-abschnitt.d.ts.map +1 -0
  36. package/dist/tools/dokument-abschnitt.js +87 -0
  37. package/dist/tools/dokument-abschnitt.js.map +1 -0
  38. package/dist/tools/dokument.d.ts +2 -1
  39. package/dist/tools/dokument.d.ts.map +1 -1
  40. package/dist/tools/dokument.js +79 -152
  41. package/dist/tools/dokument.js.map +1 -1
  42. package/dist/tools/index.d.ts +8 -2
  43. package/dist/tools/index.d.ts.map +1 -1
  44. package/dist/tools/index.js +13 -3
  45. package/dist/tools/index.js.map +1 -1
  46. package/dist/types.d.ts +66 -11
  47. package/dist/types.d.ts.map +1 -1
  48. package/dist/types.js +87 -32
  49. package/dist/types.js.map +1 -1
  50. package/dist/widgets.d.ts +48 -3
  51. package/dist/widgets.d.ts.map +1 -1
  52. package/dist/widgets.js +82 -31
  53. package/dist/widgets.js.map +1 -1
  54. package/package.json +14 -7
package/README.md CHANGED
@@ -162,7 +162,9 @@ npx -y ris-mcp-ts
162
162
 
163
163
  ## Interactive Result List
164
164
 
165
- **One widget, all eleven search tools.** There is a single interactive result list, and every search tool renders into it: federal law, state law, case law, the federal and state law gazettes, government bills, district authority announcements, municipal law, the miscellaneous collections, change history and the ordinance gazettes. Only `ris_dokument` deliberately has no widget — there the full document text *is* the answer. The screenshots below are the same widget in two different hosts, not two different features.
165
+ **One widget, all eleven search tools.** There is a single interactive result list, and every search tool renders into it: federal law, state law, case law, the federal and state law gazettes, government bills, district authority announcements, municipal law, the miscellaneous collections, change history and the ordinance gazettes. The screenshots below are the same widget in two different hosts, not two different features.
166
+
167
+ **`ris_dokument` opens a reader instead.** A single document can run to a quarter of a million characters, far past what one answer may carry, so the viewer loads it section by section as you scroll, offers an outline to jump by where the document has one, and links back into RIS. The full text still reaches the chat either way — the widget is never the only copy.
166
168
 
167
169
  Hosts that support the MCP Apps extension render the hits as an interactive list — page through results without retyping the search, open a document in the browser, or request its full text with one click. The widget follows the host's light or dark theme; the shots below show both.
168
170
 
@@ -199,11 +201,11 @@ In a host without MCP Apps support the same prompts return the full text answer
199
201
 
200
202
  | Host | What you get |
201
203
  |------|--------------|
202
- | claude.ai | Interactive list, pagination, document links |
203
- | ChatGPT | Interactive list |
204
+ | claude.ai | Result list (pagination, document links) + document viewer (outline navigation, lazy loading) |
205
+ | ChatGPT | Result list (pagination) + document viewer (outline navigation, lazy loading) |
204
206
  | Claude Code, and any client without MCP Apps support | The regular text results, unchanged |
205
207
 
206
- The chat answer is always complete. The widget is an addition to it, never a replacement — a host that does not render it loses nothing, and `ris_dokument` (full document text) has no widget by design.
208
+ The chat answer is always complete. The widgets are an addition to it, never a replacement — a host that does not render them loses nothing.
207
209
 
208
210
  ## Tool Reference
209
211
 
@@ -0,0 +1,51 @@
1
+ /**
2
+ * In-memory cache for documents the viewer pages through.
3
+ *
4
+ * The chunk tool addresses a document by character offset, so it has to keep
5
+ * reading the *same* string the first response was cut from — a re-fetch can
6
+ * produce a different metadata header and shift every offset. The cache is what
7
+ * makes that string outlive a single tool call.
8
+ *
9
+ * It is created per `registerAllTools()` call, which means per session on HTTP
10
+ * and per process on stdio, and is bounded by a character budget rather than by
11
+ * entry count alone: with `MAX_SESSIONS = 100` any per-session entry limit is
12
+ * multiplied by a hundred, and RIS documents differ in size by two orders of
13
+ * magnitude.
14
+ */
15
+ import type { OutlineEntry } from './formatting.js';
16
+ /** A document as the viewer consumes it: rendered text plus its jump targets. */
17
+ export interface CachedDocument {
18
+ /** Markdown rendering, exactly as ris_dokument produced it — before truncation. */
19
+ text: string;
20
+ outline: OutlineEntry[];
21
+ /** URL the HTML came from. */
22
+ sourceUrl: string;
23
+ }
24
+ export interface DocumentCache {
25
+ /** Looks up `key` directly, then as an alias. */
26
+ get(key: string): CachedDocument | undefined;
27
+ /** `alias` is the resolved content URL; ignored when equal to `key`. */
28
+ set(key: string, value: CachedDocument, alias?: string): void;
29
+ /** Live entries — for tests and diagnostics. */
30
+ readonly size: number;
31
+ /** Characters currently held — the bound that actually matters. */
32
+ readonly chars: number;
33
+ }
34
+ export interface DocumentCacheOptions {
35
+ maxEntries?: number;
36
+ maxChars?: number;
37
+ ttlMs?: number;
38
+ /** Injectable clock; tests drive TTL expiry without timers. */
39
+ now?: () => number;
40
+ }
41
+ /**
42
+ * Create a bounded, least-recently-used document cache.
43
+ *
44
+ * A document is reachable under two keys: its Dokumentnummer and its resolved
45
+ * content URL. `ris_dokument` and the chunk tool may each be called with either
46
+ * one, and nothing guarantees the viewer holds the same identifier the document
47
+ * was opened with — a single key would miss on exactly that crossing and turn
48
+ * every chunk into a fetch, which looks correct and is merely slow.
49
+ */
50
+ export declare function createDocumentCache(options?: DocumentCacheOptions): DocumentCache;
51
+ //# sourceMappingURL=document-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document-cache.d.ts","sourceRoot":"","sources":["../src/document-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD,iFAAiF;AACjF,MAAM,WAAW,cAAc;IAC7B,mFAAmF;IACnF,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,8BAA8B;IAC9B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,iDAAiD;IACjD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAAC;IAC7C,wEAAwE;IACxE,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9D,gDAAgD;IAChD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,mEAAmE;IACnE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAWD;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,oBAAyB,GAAG,aAAa,CAuGrF"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * In-memory cache for documents the viewer pages through.
3
+ *
4
+ * The chunk tool addresses a document by character offset, so it has to keep
5
+ * reading the *same* string the first response was cut from — a re-fetch can
6
+ * produce a different metadata header and shift every offset. The cache is what
7
+ * makes that string outlive a single tool call.
8
+ *
9
+ * It is created per `registerAllTools()` call, which means per session on HTTP
10
+ * and per process on stdio, and is bounded by a character budget rather than by
11
+ * entry count alone: with `MAX_SESSIONS = 100` any per-session entry limit is
12
+ * multiplied by a hundred, and RIS documents differ in size by two orders of
13
+ * magnitude.
14
+ */
15
+ const DEFAULT_MAX_ENTRIES = 10;
16
+ const DEFAULT_MAX_CHARS = 1_000_000;
17
+ const DEFAULT_TTL_MS = 600_000;
18
+ /**
19
+ * Create a bounded, least-recently-used document cache.
20
+ *
21
+ * A document is reachable under two keys: its Dokumentnummer and its resolved
22
+ * content URL. `ris_dokument` and the chunk tool may each be called with either
23
+ * one, and nothing guarantees the viewer holds the same identifier the document
24
+ * was opened with — a single key would miss on exactly that crossing and turn
25
+ * every chunk into a fetch, which looks correct and is merely slow.
26
+ */
27
+ export function createDocumentCache(options = {}) {
28
+ const maxEntries = options.maxEntries ?? DEFAULT_MAX_ENTRIES;
29
+ const maxChars = options.maxChars ?? DEFAULT_MAX_CHARS;
30
+ const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
31
+ const now = options.now ?? Date.now;
32
+ /** Insertion order doubles as LRU order: oldest first. */
33
+ const entries = new Map();
34
+ /** alias → primary key. Holds no copy of the text, so it costs no budget. */
35
+ const aliases = new Map();
36
+ let chars = 0;
37
+ function drop(key) {
38
+ const entry = entries.get(key);
39
+ if (!entry) {
40
+ return;
41
+ }
42
+ entries.delete(key);
43
+ chars -= entry.value.text.length;
44
+ for (const [alias, primary] of aliases) {
45
+ if (primary === key) {
46
+ aliases.delete(alias);
47
+ }
48
+ }
49
+ }
50
+ return {
51
+ get(key) {
52
+ const primary = entries.has(key) ? key : aliases.get(key);
53
+ if (primary === undefined) {
54
+ return undefined;
55
+ }
56
+ const entry = entries.get(primary);
57
+ if (!entry) {
58
+ // A dangling alias — its entry was evicted. Nothing else refers to it.
59
+ aliases.delete(key);
60
+ return undefined;
61
+ }
62
+ // TTL is checked on read rather than on a timer: a timer would keep the
63
+ // process alive and buy nothing, since an unread entry costs only memory
64
+ // the eviction below reclaims anyway.
65
+ if (now() - entry.storedAt > ttlMs) {
66
+ drop(primary);
67
+ return undefined;
68
+ }
69
+ // Re-insert to move the entry to the young end of the map.
70
+ entries.delete(primary);
71
+ entries.set(primary, entry);
72
+ return entry.value;
73
+ },
74
+ set(key, value, alias) {
75
+ const hasAlias = alias !== undefined && alias !== key;
76
+ // Every previous copy of this document goes first, under either name —
77
+ // before the oversize check below, which stores nothing in its place.
78
+ // Dropping afterwards would leave a document that has grown past the
79
+ // budget being served from its old, shorter text while ris_dokument
80
+ // renders the new one, and the viewer would page against stale offsets.
81
+ // The alias may also have been a primary key of its own (the document was
82
+ // opened by URL and now arrives by Dokumentnummer), which is the same
83
+ // document under a second copy.
84
+ drop(key);
85
+ aliases.delete(key);
86
+ if (hasAlias) {
87
+ drop(alias);
88
+ aliases.delete(alias);
89
+ }
90
+ // Caching a document larger than the whole budget would evict everything
91
+ // else and still overshoot, so it is not cached at all. The chunk path
92
+ // stays correct, it just re-fetches per call.
93
+ if (value.text.length > maxChars) {
94
+ return;
95
+ }
96
+ entries.set(key, { value, storedAt: now() });
97
+ chars += value.text.length;
98
+ if (hasAlias) {
99
+ aliases.set(alias, key);
100
+ }
101
+ while (entries.size > maxEntries || chars > maxChars) {
102
+ const oldest = entries.keys().next().value;
103
+ if (oldest === undefined) {
104
+ break;
105
+ }
106
+ drop(oldest);
107
+ }
108
+ },
109
+ get size() {
110
+ return entries.size;
111
+ },
112
+ get chars() {
113
+ return chars;
114
+ },
115
+ };
116
+ }
117
+ //# sourceMappingURL=document-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document-cache.js","sourceRoot":"","sources":["../src/document-cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Shared load path for a single RIS document.
3
+ *
4
+ * `ris_dokument` and `ris_dokument_abschnitt` both need the full text of a
5
+ * document, and they need the *same* text: the chunk tool hands out character
6
+ * offsets into it, and the two resolution strategies below produce different
7
+ * metadata headers, so a document loaded one way is several thousand characters
8
+ * offset against the same document loaded the other way. Sharing the path is
9
+ * what keeps a chunk pointing at what the reader was shown.
10
+ *
11
+ * Everything here — the validation, both SSRF checks, the direct/search
12
+ * strategy and the German error prose — was lifted out of `tools/dokument.ts`
13
+ * unchanged; the strings are part of that tool's response contract.
14
+ */
15
+ import { type DocumentMetadata } from './formatting.js';
16
+ export interface LoadedDocument {
17
+ /** Markdown (or JSON) rendering, before truncation. */
18
+ text: string;
19
+ /** RIS source HTML — needed for the outline; callers may drop it. */
20
+ html: string;
21
+ /** URL the HTML came from; feeds `source_url` and the document cache key. */
22
+ contentUrl: string;
23
+ metadata: DocumentMetadata;
24
+ }
25
+ export type LoadDocumentResult = {
26
+ success: true;
27
+ document: LoadedDocument;
28
+ } | {
29
+ success: false;
30
+ error: string;
31
+ };
32
+ export interface LoadDocumentArgs {
33
+ dokumentnummer?: string;
34
+ url?: string;
35
+ responseFormat: 'markdown' | 'json';
36
+ }
37
+ /**
38
+ * Resolve, fetch and format a document.
39
+ *
40
+ * Returns a German error string for every condition the user can fix. Throws on
41
+ * cancellation and on unexpected upstream failures — callers keep the abort and
42
+ * `formatErrorResponse` handling they already have.
43
+ */
44
+ export declare function loadDocument(args: LoadDocumentArgs, signal: AbortSignal): Promise<LoadDocumentResult>;
45
+ //# sourceMappingURL=document-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document-loader.d.ts","sourceRoot":"","sources":["../src/document-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAaH,OAAO,EAAkB,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAIxE,MAAM,WAAW,cAAc;IAC7B,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,gBAAgB,CAAC;CAC5B;AAED,MAAM,MAAM,kBAAkB,GAC1B;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,cAAc,CAAA;CAAE,GAC3C;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtC,MAAM,WAAW,gBAAgB;IAC/B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,UAAU,GAAG,MAAM,CAAC;CACrC;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,gBAAgB,EACtB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,kBAAkB,CAAC,CAkL7B"}
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Shared load path for a single RIS document.
3
+ *
4
+ * `ris_dokument` and `ris_dokument_abschnitt` both need the full text of a
5
+ * document, and they need the *same* text: the chunk tool hands out character
6
+ * offsets into it, and the two resolution strategies below produce different
7
+ * metadata headers, so a document loaded one way is several thousand characters
8
+ * offset against the same document loaded the other way. Sharing the path is
9
+ * what keeps a chunk pointing at what the reader was shown.
10
+ *
11
+ * Everything here — the validation, both SSRF checks, the direct/search
12
+ * strategy and the German error prose — was lifted out of `tools/dokument.ts`
13
+ * unchanged; the strings are part of that tool's response contract.
14
+ */
15
+ import { getDocumentByNumber, getDocumentContent, getDocumentRoute, isAllowedUrl, searchBezirke, searchBundesrecht, searchJudikatur, searchLandesrecht, searchSonstige, } from './client.js';
16
+ import { formatDocument } from './formatting.js';
17
+ import { findDocumentByDokumentnummer } from './parser.js';
18
+ /**
19
+ * Resolve, fetch and format a document.
20
+ *
21
+ * Returns a German error string for every condition the user can fix. Throws on
22
+ * cancellation and on unexpected upstream failures — callers keep the abort and
23
+ * `formatErrorResponse` handling they already have.
24
+ */
25
+ export async function loadDocument(args, signal) {
26
+ const { dokumentnummer, url: inputUrl, responseFormat } = args;
27
+ if (!dokumentnummer && !inputUrl) {
28
+ return {
29
+ success: false,
30
+ error: '**Fehler:** Bitte gib entweder eine `dokumentnummer` oder eine `url` an.\n\n' +
31
+ 'Die Dokumentnummer findest du in den Suchergebnissen von `ris_bundesrecht`, ' +
32
+ '`ris_landesrecht` oder `ris_judikatur`.',
33
+ };
34
+ }
35
+ // SSRF protection: validate user-supplied URLs against domain allowlist
36
+ if (inputUrl && !isAllowedUrl(inputUrl)) {
37
+ return {
38
+ success: false,
39
+ error: '**Fehler:** Die angegebene URL ist nicht erlaubt.\n\n' +
40
+ 'Nur HTTPS-URLs zu offiziellen RIS-Domains sind zulaessig ' +
41
+ '(data.bka.gv.at, www.ris.bka.gv.at, ris.bka.gv.at).',
42
+ };
43
+ }
44
+ let contentUrl = inputUrl;
45
+ let htmlContent;
46
+ let metadata;
47
+ if (dokumentnummer && !inputUrl) {
48
+ // Strategy: Try direct URL construction first, fallback to search API
49
+ const directResult = await getDocumentByNumber(dokumentnummer, undefined, signal);
50
+ if (directResult.success) {
51
+ // Direct fetch succeeded - use minimal metadata
52
+ htmlContent = directResult.html;
53
+ contentUrl = directResult.url;
54
+ metadata = {
55
+ dokumentnummer,
56
+ applikation: 'Unbekannt',
57
+ titel: dokumentnummer,
58
+ kurztitel: null,
59
+ citation: {},
60
+ dokument_url: directResult.url,
61
+ };
62
+ }
63
+ else {
64
+ // Direct fetch failed - fallback to search API. Routing (endpoint +
65
+ // Applikation) comes from the shared registry in client.ts so it stays
66
+ // consistent with the direct-URL construction. Unknown prefixes default
67
+ // to a Justiz search.
68
+ const route = getDocumentRoute(dokumentnummer);
69
+ const searchParams = {
70
+ Applikation: route?.applikation ?? 'Justiz',
71
+ Dokumentnummer: dokumentnummer,
72
+ DokumenteProSeite: 'Ten',
73
+ };
74
+ let apiResponse;
75
+ switch (route?.endpoint) {
76
+ case 'Bundesrecht':
77
+ apiResponse = await searchBundesrecht(searchParams, undefined, signal);
78
+ break;
79
+ case 'Landesrecht':
80
+ apiResponse = await searchLandesrecht(searchParams, undefined, signal);
81
+ break;
82
+ case 'Sonstige':
83
+ apiResponse = await searchSonstige(searchParams, undefined, signal);
84
+ break;
85
+ case 'Bezirke':
86
+ apiResponse = await searchBezirke(searchParams, undefined, signal);
87
+ break;
88
+ case 'Judikatur':
89
+ default:
90
+ apiResponse = await searchJudikatur(searchParams, undefined, signal);
91
+ break;
92
+ }
93
+ // Find the document with matching dokumentnummer (don't blindly take first result)
94
+ const findResult = findDocumentByDokumentnummer(apiResponse.documents, dokumentnummer);
95
+ if (!findResult.success) {
96
+ // Both direct fetch and search failed - provide helpful error
97
+ const directError = directResult.error;
98
+ if (findResult.error === 'no_documents') {
99
+ return {
100
+ success: false,
101
+ error: `**Fehler:** Kein Dokument mit der Nummer \`${dokumentnummer}\` gefunden.\n\n` +
102
+ `Direkter Abruf: ${directError}\n` +
103
+ `Suche: Keine Ergebnisse.\n\n` +
104
+ 'Bitte pruefe die Dokumentnummer oder verwende eine Suche, ' +
105
+ 'um das gewuenschte Dokument zu finden.',
106
+ };
107
+ }
108
+ else {
109
+ return {
110
+ success: false,
111
+ error: `**Fehler:** Dokument \`${dokumentnummer}\` nicht gefunden.\n\n` +
112
+ `Direkter Abruf: ${directError}\n` +
113
+ `Suche: ${findResult.totalResults} Ergebnisse, aber keines mit dieser Dokumentnummer.\n\n` +
114
+ `Bitte verwende eine alternative Suche oder die direkte URL.`,
115
+ };
116
+ }
117
+ }
118
+ const doc = findResult.document;
119
+ contentUrl = doc.content_urls.html ?? undefined;
120
+ if (!contentUrl) {
121
+ return {
122
+ success: false,
123
+ error: `**Fehler:** Keine Inhalts-URL fuer Dokument \`${dokumentnummer}\` verfuegbar.\n\n` +
124
+ 'Das Dokument hat moeglicherweise keinen abrufbaren Volltext.',
125
+ };
126
+ }
127
+ // SSRF protection: this URL comes straight from the search API response
128
+ // and would otherwise be fetched unchecked. Validate it against the same
129
+ // domain allowlist used for user-supplied URLs.
130
+ if (!isAllowedUrl(contentUrl)) {
131
+ return {
132
+ success: false,
133
+ error: '**Fehler:** Die Inhalts-URL des Dokuments ist nicht erlaubt.\n\n' +
134
+ 'Nur HTTPS-URLs zu offiziellen RIS-Domains sind zulaessig ' +
135
+ '(data.bka.gv.at, www.ris.bka.gv.at, ris.bka.gv.at).',
136
+ };
137
+ }
138
+ // Build metadata from search result
139
+ metadata = {
140
+ dokumentnummer: doc.dokumentnummer,
141
+ applikation: doc.applikation,
142
+ titel: doc.titel,
143
+ kurztitel: doc.kurztitel,
144
+ citation: {
145
+ kurztitel: doc.citation.kurztitel,
146
+ langtitel: doc.citation.langtitel,
147
+ kundmachungsorgan: doc.citation.kundmachungsorgan,
148
+ paragraph: doc.citation.paragraph,
149
+ eli: doc.citation.eli,
150
+ inkrafttreten: doc.citation.inkrafttreten,
151
+ ausserkrafttreten: doc.citation.ausserkrafttreten,
152
+ },
153
+ dokument_url: doc.dokument_url,
154
+ gesamte_rechtsvorschrift_url: doc.gesamte_rechtsvorschrift_url,
155
+ };
156
+ }
157
+ }
158
+ else {
159
+ // Only URL provided - minimal metadata
160
+ metadata = {
161
+ dokumentnummer: dokumentnummer ?? 'Unbekannt',
162
+ applikation: 'Unbekannt',
163
+ titel: inputUrl ?? '',
164
+ kurztitel: null,
165
+ citation: {},
166
+ dokument_url: inputUrl,
167
+ };
168
+ }
169
+ if (!contentUrl) {
170
+ return { success: false, error: '**Fehler:** Keine gueltige URL zum Abrufen des Dokuments.' };
171
+ }
172
+ // Fetch document content if not already fetched via direct URL
173
+ if (!htmlContent) {
174
+ htmlContent = await getDocumentContent(contentUrl, undefined, signal);
175
+ }
176
+ return {
177
+ success: true,
178
+ document: {
179
+ text: formatDocument(htmlContent, metadata, responseFormat),
180
+ html: htmlContent,
181
+ contentUrl,
182
+ metadata,
183
+ },
184
+ };
185
+ }
186
+ //# sourceMappingURL=document-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document-loader.js","sourceRoot":"","sources":["../src/document-loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Shared vocabulary between the server schemas and the widgets.
3
+ *
4
+ * Deliberately zod-free and dependency-free: the widget bundle imports this
5
+ * file across the tsconfig boundary, and the server builds its `z.enum()`s from
6
+ * it — one home, so the two sides cannot drift (they were found drifting both
7
+ * ways in the #84 final review).
8
+ */
9
+ /**
10
+ * Court/jurisdiction types for case law searches in RIS.
11
+ *
12
+ * This value selects the RIS "Applikation" (data collection), i.e. which
13
+ * court's decisions to search — not an individual court within a collection.
14
+ */
15
+ export declare const JUDIKATUR_GERICHTSBARKEITEN: readonly ["Justiz", "Vfgh", "Vwgh", "Bvwg", "Lvwg", "Dsk", "AsylGH", "Normenliste", "Pvak", "Gbk", "Dok", "Verg", "Uvs", "Ubas", "Umse", "Bks"];
16
+ /**
17
+ * Document type filter for Judikatur searches.
18
+ *
19
+ * The RIS API only searches Rechtssätze (abstract legal principles) by default.
20
+ * "entscheidungstext" additionally/instead searches the full decision texts,
21
+ * and "beide" (the default here) searches both for the most complete results.
22
+ */
23
+ export declare const JUDIKATUR_DOKUMENTTYPEN: readonly ["rechtssatz", "entscheidungstext", "beide"];
24
+ /** Broad legal area filter (Justiz only). */
25
+ export declare const JUDIKATUR_RECHTSGEBIETE: readonly ["Zivilrecht", "Strafrecht"];
26
+ /** Tools whose consolidated documents have dated legal states (`fassung_vom`). */
27
+ export declare const FASSUNG_TOOLS: readonly ["ris_bundesrecht", "ris_landesrecht"];
28
+ /** Applikationen the server silently drops FassungVom for (bundesrecht.ts Erv branch). */
29
+ export declare const FASSUNG_EXCLUDED_APPLIKATIONEN: readonly ["Erv"];
30
+ //# sourceMappingURL=facets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"facets.d.ts","sourceRoot":"","sources":["../src/facets.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,iJAkB9B,CAAC;AAEX;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,uDAAwD,CAAC;AAE7F,6CAA6C;AAC7C,eAAO,MAAM,uBAAuB,uCAAwC,CAAC;AAE7E,kFAAkF;AAClF,eAAO,MAAM,aAAa,iDAAkD,CAAC;AAE7E,0FAA0F;AAC1F,eAAO,MAAM,8BAA8B,kBAAmB,CAAC"}
package/dist/facets.js ADDED
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Shared vocabulary between the server schemas and the widgets.
3
+ *
4
+ * Deliberately zod-free and dependency-free: the widget bundle imports this
5
+ * file across the tsconfig boundary, and the server builds its `z.enum()`s from
6
+ * it — one home, so the two sides cannot drift (they were found drifting both
7
+ * ways in the #84 final review).
8
+ */
9
+ /**
10
+ * Court/jurisdiction types for case law searches in RIS.
11
+ *
12
+ * This value selects the RIS "Applikation" (data collection), i.e. which
13
+ * court's decisions to search — not an individual court within a collection.
14
+ */
15
+ export const JUDIKATUR_GERICHTSBARKEITEN = [
16
+ 'Justiz', // Ordinary courts (OGH, OLG, LG, BG)
17
+ 'Vfgh', // Constitutional Court
18
+ 'Vwgh', // Supreme Administrative Court
19
+ 'Bvwg', // Federal Administrative Court
20
+ 'Lvwg', // State Administrative Courts
21
+ 'Dsk', // Data Protection Authority
22
+ 'AsylGH', // Asylum Court (historical, until 2013)
23
+ 'Normenliste', // Court norm lists (judicial review)
24
+ 'Pvak', // Personnel Representation Supervision Commission
25
+ 'Gbk', // Equal Treatment Commission
26
+ 'Dok', // Disciplinary Commission
27
+ // Historical jurisdictions dissolved on 2014-01-01 (case stock still searchable).
28
+ 'Verg', // Federal Procurement Office (Bundesvergabeamt)
29
+ 'Uvs', // Independent Administrative Senates (Unabhängige Verwaltungssenate)
30
+ 'Ubas', // Independent Federal Asylum Senate (Unabhängiger Bundesasylsenat)
31
+ 'Umse', // Environmental Senate (Umweltsenat)
32
+ 'Bks', // Federal Communications Board (Bundeskommunikationssenat)
33
+ ];
34
+ /**
35
+ * Document type filter for Judikatur searches.
36
+ *
37
+ * The RIS API only searches Rechtssätze (abstract legal principles) by default.
38
+ * "entscheidungstext" additionally/instead searches the full decision texts,
39
+ * and "beide" (the default here) searches both for the most complete results.
40
+ */
41
+ export const JUDIKATUR_DOKUMENTTYPEN = ['rechtssatz', 'entscheidungstext', 'beide'];
42
+ /** Broad legal area filter (Justiz only). */
43
+ export const JUDIKATUR_RECHTSGEBIETE = ['Zivilrecht', 'Strafrecht'];
44
+ /** Tools whose consolidated documents have dated legal states (`fassung_vom`). */
45
+ export const FASSUNG_TOOLS = ['ris_bundesrecht', 'ris_landesrecht'];
46
+ /** Applikationen the server silently drops FassungVom for (bundesrecht.ts Erv branch). */
47
+ export const FASSUNG_EXCLUDED_APPLIKATIONEN = ['Erv'];
48
+ //# sourceMappingURL=facets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"facets.js","sourceRoot":"","sources":["../src/facets.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG;IACzC,QAAQ,EAAE,qCAAqC;IAC/C,MAAM,EAAE,uBAAuB;IAC/B,MAAM,EAAE,+BAA+B;IACvC,MAAM,EAAE,+BAA+B;IACvC,MAAM,EAAE,8BAA8B;IACtC,KAAK,EAAE,4BAA4B;IACnC,QAAQ,EAAE,wCAAwC;IAClD,aAAa,EAAE,qCAAqC;IACpD,MAAM,EAAE,kDAAkD;IAC1D,KAAK,EAAE,6BAA6B;IACpC,KAAK,EAAE,0BAA0B;IACjC,kFAAkF;IAClF,MAAM,EAAE,gDAAgD;IACxD,KAAK,EAAE,qEAAqE;IAC5E,MAAM,EAAE,mEAAmE;IAC3E,MAAM,EAAE,qCAAqC;IAC7C,KAAK,EAAE,2DAA2D;CAC1D,CAAC;AAEX;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,YAAY,EAAE,mBAAmB,EAAE,OAAO,CAAU,CAAC;AAE7F,6CAA6C;AAC7C,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,YAAY,EAAE,YAAY,CAAU,CAAC;AAE7E,kFAAkF;AAClF,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,iBAAiB,EAAE,iBAAiB,CAAU,CAAC;AAE7E,0FAA0F;AAC1F,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,KAAK,CAAU,CAAC"}
@@ -6,6 +6,8 @@
6
6
  * formatting, and document content preparation.
7
7
  */
8
8
  import type { Document, SearchResult } from './types.js';
9
+ /** Hard limit for a single text response, in characters. */
10
+ export declare const CHARACTER_LIMIT = 25000;
9
11
  /**
10
12
  * Convert API date format to readable German format.
11
13
  */
@@ -62,6 +64,59 @@ export interface DocumentMetadata {
62
64
  * Format a full document for LLM context.
63
65
  */
64
66
  export declare function formatDocument(content: string, metadata: DocumentMetadata, format?: 'markdown' | 'json'): string;
67
+ /** One jump target in a document, as offered by the viewer's outline. */
68
+ export interface OutlineEntry {
69
+ /**
70
+ * Heading level 1–6, taken from the RIS source markup. Level 1 is the RIS
71
+ * record layer (field labels plus, in gazettes, the masthead); levels 2 and
72
+ * below are structure inside the document text.
73
+ */
74
+ level: number;
75
+ /** Heading text, whitespace-normalised exactly as the document text is. */
76
+ label: string;
77
+ /** Character offset of the heading's line in the document text. */
78
+ offset: number;
79
+ /**
80
+ * Characters up to the next heading, or to the end of the text for the last
81
+ * entry. The only reliable way to tell a one-line metadata field from a real
82
+ * section: RIS gives both the same element and the same class.
83
+ */
84
+ span: number;
85
+ }
86
+ /**
87
+ * Extract the jump targets of a document.
88
+ *
89
+ * `html` is the RIS source, `text` the markdown rendering it produced. The
90
+ * headings are read from the markup — the rendered text carries no structure
91
+ * beyond the three headings formatDocument() writes itself — and then located in
92
+ * `text` by their own line.
93
+ *
94
+ * A document without headings yields `[]`. That is a valid outline, not an
95
+ * error: the viewer stays navigable through plain offset paging.
96
+ */
97
+ export declare function extractOutline(html: string, text: string): OutlineEntry[];
98
+ /** One slice of a document text, addressed by character offset. */
99
+ export interface ResponseChunk {
100
+ /** The slice starting at the requested offset, at most `limit` characters. */
101
+ text: string;
102
+ /** Length of the complete text this chunk was cut from. */
103
+ total_length: number;
104
+ /** Offset for the following chunk, or null when this chunk ends the text. */
105
+ next_offset: number | null;
106
+ }
107
+ /**
108
+ * Cut one chunk out of `text`, starting at `offset`.
109
+ *
110
+ * Chunk boundaries follow the same paragraph-then-sentence rule as
111
+ * {@link truncateResponse}; unlike that function nothing is appended and nothing
112
+ * is dropped — consecutive chunks concatenate back to `text`.
113
+ *
114
+ * Offsets are character offsets into the complete text, i.e. the unit of
115
+ * `String.length` and `String.slice`. They stay valid as long as the same text
116
+ * does, which is why every chunk carries `total_length`: a caller whose text was
117
+ * re-fetched in between sees the length change and restarts at offset 0.
118
+ */
119
+ export declare function chunkResponse(text: string, offset?: number, limit?: number): ResponseChunk;
65
120
  /**
66
121
  * Truncate response if too long.
67
122
  */
@@ -1 +1 @@
1
- {"version":3,"file":"formatting.d.ts","sourceRoot":"","sources":["../src/formatting.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAqCzD;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAiBrE;AAyBD;;GAEG;AACH,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAyEtD;AAMD;;GAEG;AACH,UAAU,YAAY;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE;QACT,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAClC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC/B,CAAC;CACH;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,QAAQ,GAAG,YAAY,GAAG,MAAM,CAcnE;AA0HD;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/C,MAAM,GAAE,UAAU,GAAG,MAAmB,GACvC,MAAM,CAsBR;AA6FD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,CAAC,EAAE;QACT,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAClC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACpB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACnC,CAAC;IACF,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,4BAA4B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9C;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,gBAAgB,EAC1B,MAAM,GAAE,UAAU,GAAG,MAAmB,GACvC,MAAM,CAaR;AAuFD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,SAAkB,GAAG,MAAM,CAsC9E"}
1
+ {"version":3,"file":"formatting.d.ts","sourceRoot":"","sources":["../src/formatting.ts"],"names":[],"mappings":"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"}