@cyanheads/ris-austria-mcp-server 0.1.5

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 (154) hide show
  1. package/AGENTS.md +401 -0
  2. package/CLAUDE.md +401 -0
  3. package/Dockerfile +125 -0
  4. package/LICENSE +201 -0
  5. package/README.md +309 -0
  6. package/changelog/0.1.x/0.1.0.md +25 -0
  7. package/changelog/0.1.x/0.1.1.md +24 -0
  8. package/changelog/0.1.x/0.1.2.md +16 -0
  9. package/changelog/0.1.x/0.1.3.md +11 -0
  10. package/changelog/0.1.x/0.1.4.md +11 -0
  11. package/changelog/0.1.x/0.1.5.md +18 -0
  12. package/changelog/template.md +129 -0
  13. package/dist/config/server-config.d.ts +19 -0
  14. package/dist/config/server-config.d.ts.map +1 -0
  15. package/dist/config/server-config.js +35 -0
  16. package/dist/config/server-config.js.map +1 -0
  17. package/dist/index.d.ts +7 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +37 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/mcp-server/resources/definitions/ris-document.resource.d.ts +30 -0
  22. package/dist/mcp-server/resources/definitions/ris-document.resource.d.ts.map +1 -0
  23. package/dist/mcp-server/resources/definitions/ris-document.resource.js +75 -0
  24. package/dist/mcp-server/resources/definitions/ris-document.resource.js.map +1 -0
  25. package/dist/mcp-server/tools/definitions/_shared.d.ts +8 -0
  26. package/dist/mcp-server/tools/definitions/_shared.d.ts.map +1 -0
  27. package/dist/mcp-server/tools/definitions/_shared.js +10 -0
  28. package/dist/mcp-server/tools/definitions/_shared.js.map +1 -0
  29. package/dist/mcp-server/tools/definitions/ris-get-document.tool.d.ts +189 -0
  30. package/dist/mcp-server/tools/definitions/ris-get-document.tool.d.ts.map +1 -0
  31. package/dist/mcp-server/tools/definitions/ris-get-document.tool.js +504 -0
  32. package/dist/mcp-server/tools/definitions/ris-get-document.tool.js.map +1 -0
  33. package/dist/mcp-server/tools/definitions/ris-list-reference.tool.d.ts +42 -0
  34. package/dist/mcp-server/tools/definitions/ris-list-reference.tool.d.ts.map +1 -0
  35. package/dist/mcp-server/tools/definitions/ris-list-reference.tool.js +316 -0
  36. package/dist/mcp-server/tools/definitions/ris-list-reference.tool.js.map +1 -0
  37. package/dist/mcp-server/tools/definitions/ris-lookup-citation.tool.d.ts +80 -0
  38. package/dist/mcp-server/tools/definitions/ris-lookup-citation.tool.d.ts.map +1 -0
  39. package/dist/mcp-server/tools/definitions/ris-lookup-citation.tool.js +529 -0
  40. package/dist/mcp-server/tools/definitions/ris-lookup-citation.tool.js.map +1 -0
  41. package/dist/mcp-server/tools/definitions/ris-search-announcements.tool.d.ts +118 -0
  42. package/dist/mcp-server/tools/definitions/ris-search-announcements.tool.d.ts.map +1 -0
  43. package/dist/mcp-server/tools/definitions/ris-search-announcements.tool.js +389 -0
  44. package/dist/mcp-server/tools/definitions/ris-search-announcements.tool.js.map +1 -0
  45. package/dist/mcp-server/tools/definitions/ris-search-case-law.tool.d.ts +178 -0
  46. package/dist/mcp-server/tools/definitions/ris-search-case-law.tool.d.ts.map +1 -0
  47. package/dist/mcp-server/tools/definitions/ris-search-case-law.tool.js +495 -0
  48. package/dist/mcp-server/tools/definitions/ris-search-case-law.tool.js.map +1 -0
  49. package/dist/mcp-server/tools/definitions/ris-search-drafts.tool.d.ts +80 -0
  50. package/dist/mcp-server/tools/definitions/ris-search-drafts.tool.d.ts.map +1 -0
  51. package/dist/mcp-server/tools/definitions/ris-search-drafts.tool.js +264 -0
  52. package/dist/mcp-server/tools/definitions/ris-search-drafts.tool.js.map +1 -0
  53. package/dist/mcp-server/tools/definitions/ris-search-gazette.tool.d.ts +147 -0
  54. package/dist/mcp-server/tools/definitions/ris-search-gazette.tool.d.ts.map +1 -0
  55. package/dist/mcp-server/tools/definitions/ris-search-gazette.tool.js +509 -0
  56. package/dist/mcp-server/tools/definitions/ris-search-gazette.tool.js.map +1 -0
  57. package/dist/mcp-server/tools/definitions/ris-search-legislation.tool.d.ts +145 -0
  58. package/dist/mcp-server/tools/definitions/ris-search-legislation.tool.d.ts.map +1 -0
  59. package/dist/mcp-server/tools/definitions/ris-search-legislation.tool.js +466 -0
  60. package/dist/mcp-server/tools/definitions/ris-search-legislation.tool.js.map +1 -0
  61. package/dist/mcp-server/tools/definitions/ris-track-changes.tool.d.ts +73 -0
  62. package/dist/mcp-server/tools/definitions/ris-track-changes.tool.d.ts.map +1 -0
  63. package/dist/mcp-server/tools/definitions/ris-track-changes.tool.js +265 -0
  64. package/dist/mcp-server/tools/definitions/ris-track-changes.tool.js.map +1 -0
  65. package/dist/services/ris/normalizer.d.ts +40 -0
  66. package/dist/services/ris/normalizer.d.ts.map +1 -0
  67. package/dist/services/ris/normalizer.js +480 -0
  68. package/dist/services/ris/normalizer.js.map +1 -0
  69. package/dist/services/ris/reference/applications.d.ts +529 -0
  70. package/dist/services/ris/reference/applications.d.ts.map +1 -0
  71. package/dist/services/ris/reference/applications.js +520 -0
  72. package/dist/services/ris/reference/applications.js.map +1 -0
  73. package/dist/services/ris/reference/changed-since-intervals.d.ts +42 -0
  74. package/dist/services/ris/reference/changed-since-intervals.d.ts.map +1 -0
  75. package/dist/services/ris/reference/changed-since-intervals.js +16 -0
  76. package/dist/services/ris/reference/changed-since-intervals.js.map +1 -0
  77. package/dist/services/ris/reference/citation-formats.d.ts +39 -0
  78. package/dist/services/ris/reference/citation-formats.d.ts.map +1 -0
  79. package/dist/services/ris/reference/citation-formats.js +39 -0
  80. package/dist/services/ris/reference/citation-formats.js.map +1 -0
  81. package/dist/services/ris/reference/collections.d.ts +82 -0
  82. package/dist/services/ris/reference/collections.d.ts.map +1 -0
  83. package/dist/services/ris/reference/collections.js +102 -0
  84. package/dist/services/ris/reference/collections.js.map +1 -0
  85. package/dist/services/ris/reference/courts.d.ts +201 -0
  86. package/dist/services/ris/reference/courts.d.ts.map +1 -0
  87. package/dist/services/ris/reference/courts.js +198 -0
  88. package/dist/services/ris/reference/courts.js.map +1 -0
  89. package/dist/services/ris/reference/decision-kinds.d.ts +78 -0
  90. package/dist/services/ris/reference/decision-kinds.d.ts.map +1 -0
  91. package/dist/services/ris/reference/decision-kinds.js +117 -0
  92. package/dist/services/ris/reference/decision-kinds.js.map +1 -0
  93. package/dist/services/ris/reference/decision-types.d.ts +36 -0
  94. package/dist/services/ris/reference/decision-types.d.ts.map +1 -0
  95. package/dist/services/ris/reference/decision-types.js +29 -0
  96. package/dist/services/ris/reference/decision-types.js.map +1 -0
  97. package/dist/services/ris/reference/district-authorities.d.ts +314 -0
  98. package/dist/services/ris/reference/district-authorities.d.ts.map +1 -0
  99. package/dist/services/ris/reference/district-authorities.js +212 -0
  100. package/dist/services/ris/reference/district-authorities.js.map +1 -0
  101. package/dist/services/ris/reference/gazette-parts.d.ts +73 -0
  102. package/dist/services/ris/reference/gazette-parts.d.ts.map +1 -0
  103. package/dist/services/ris/reference/gazette-parts.js +58 -0
  104. package/dist/services/ris/reference/gazette-parts.js.map +1 -0
  105. package/dist/services/ris/reference/index.d.ts +23 -0
  106. package/dist/services/ris/reference/index.d.ts.map +1 -0
  107. package/dist/services/ris/reference/index.js +23 -0
  108. package/dist/services/ris/reference/index.js.map +1 -0
  109. package/dist/services/ris/reference/issuing-bodies.d.ts +642 -0
  110. package/dist/services/ris/reference/issuing-bodies.d.ts.map +1 -0
  111. package/dist/services/ris/reference/issuing-bodies.js +616 -0
  112. package/dist/services/ris/reference/issuing-bodies.js.map +1 -0
  113. package/dist/services/ris/reference/justiz-subject-areas.d.ts +133 -0
  114. package/dist/services/ris/reference/justiz-subject-areas.d.ts.map +1 -0
  115. package/dist/services/ris/reference/justiz-subject-areas.js +67 -0
  116. package/dist/services/ris/reference/justiz-subject-areas.js.map +1 -0
  117. package/dist/services/ris/reference/law-types.d.ts +46 -0
  118. package/dist/services/ris/reference/law-types.d.ts.map +1 -0
  119. package/dist/services/ris/reference/law-types.js +41 -0
  120. package/dist/services/ris/reference/law-types.js.map +1 -0
  121. package/dist/services/ris/reference/ministries.d.ts +360 -0
  122. package/dist/services/ris/reference/ministries.d.ts.map +1 -0
  123. package/dist/services/ris/reference/ministries.js +408 -0
  124. package/dist/services/ris/reference/ministries.js.map +1 -0
  125. package/dist/services/ris/reference/search-syntax.d.ts +66 -0
  126. package/dist/services/ris/reference/search-syntax.d.ts.map +1 -0
  127. package/dist/services/ris/reference/search-syntax.js +65 -0
  128. package/dist/services/ris/reference/search-syntax.js.map +1 -0
  129. package/dist/services/ris/reference/section-types.d.ts +33 -0
  130. package/dist/services/ris/reference/section-types.d.ts.map +1 -0
  131. package/dist/services/ris/reference/section-types.js +29 -0
  132. package/dist/services/ris/reference/section-types.js.map +1 -0
  133. package/dist/services/ris/reference/stages.d.ts +41 -0
  134. package/dist/services/ris/reference/stages.d.ts.map +1 -0
  135. package/dist/services/ris/reference/stages.js +27 -0
  136. package/dist/services/ris/reference/stages.js.map +1 -0
  137. package/dist/services/ris/reference/states.d.ts +90 -0
  138. package/dist/services/ris/reference/states.d.ts.map +1 -0
  139. package/dist/services/ris/reference/states.js +85 -0
  140. package/dist/services/ris/reference/states.js.map +1 -0
  141. package/dist/services/ris/request-builder.d.ts +195 -0
  142. package/dist/services/ris/request-builder.d.ts.map +1 -0
  143. package/dist/services/ris/request-builder.js +673 -0
  144. package/dist/services/ris/request-builder.js.map +1 -0
  145. package/dist/services/ris/ris-service.d.ts +65 -0
  146. package/dist/services/ris/ris-service.d.ts.map +1 -0
  147. package/dist/services/ris/ris-service.js +200 -0
  148. package/dist/services/ris/ris-service.js.map +1 -0
  149. package/dist/services/ris/types.d.ts +651 -0
  150. package/dist/services/ris/types.d.ts.map +1 -0
  151. package/dist/services/ris/types.js +11 -0
  152. package/dist/services/ris/types.js.map +1 -0
  153. package/package.json +108 -0
  154. package/server.json +111 -0
@@ -0,0 +1,673 @@
1
+ /**
2
+ * @fileoverview Request builders for the RIS OGD API — one thin builder per document
3
+ * class over a shared param bag, plus ministry-abbreviation expansion and the History
4
+ * application aliasing.
5
+ *
6
+ * STRICT PARAM ALLOWLIST: RIS silently ignores unknown query params (a typo returns
7
+ * plausible but unfiltered results, never an error), so builders emit only spellings
8
+ * confirmed against the live API (design ledger 2026-07-04/05 + build probes 2026-07-05).
9
+ * A caller-supplied field with no confirmed mapping for the target application throws a
10
+ * local ValidationError — it is never dropped and never forwarded.
11
+ *
12
+ * Flag complexes (`Teil.SucheIn…`, `Typ.SucheIn…`, `Dokumenttyp.SucheIn…`,
13
+ * `Bundesland.SucheIn…`) are only ever emitted as `=true` — probes show a present-but-
14
+ * false flag changes results in undocumented ways, while omission means "no filter".
15
+ * @module services/ris/request-builder
16
+ */
17
+ import { validationError } from '@cyanheads/mcp-ts-core/errors';
18
+ import { RIS_APPLICATIONS, RIS_CHANGED_SINCE_INTERVALS, RIS_COLLECTIONS, RIS_COURTS, RIS_DECISION_KINDS, RIS_MINISTRIES, RIS_STAGES, RIS_STATES, } from './reference/index.js';
19
+ const PAGE_SIZE_TOKENS = {
20
+ 10: 'Ten',
21
+ 20: 'Twenty',
22
+ 50: 'Fifty',
23
+ 100: 'OneHundred',
24
+ };
25
+ const CHANGED_SINCE_TOKENS = new Map(RIS_CHANGED_SINCE_INTERVALS.map((interval) => [interval.code, interval.risValue]));
26
+ const STATE_BY_CODE = new Map(RIS_STATES.map((state) => [state.code, state]));
27
+ const COURT_BY_CODE = new Map(RIS_COURTS.map((court) => [court.code, court]));
28
+ const COLLECTION_BY_CODE = new Map(RIS_COLLECTIONS.map((collection) => [collection.code, collection]));
29
+ const STAGE_BY_CODE = new Map(RIS_STAGES.map((stage) => [stage.code, stage]));
30
+ const APPLICATION_BY_CODE = new Map(RIS_APPLICATIONS.map((app) => [app.code, app]));
31
+ const DECISION_KINDS_BY_COURT = new Map(RIS_DECISION_KINDS.map((entry) => [entry.court, entry]));
32
+ /** States the Vbl backend actually resolves — other values pass schema validation but 500. */
33
+ const VBL_COVERED_STATES = ['tirol'];
34
+ function unsupported(param, application) {
35
+ throw validationError(`Parameter "${param}" has no confirmed RIS mapping for application ${application} — it would be silently ignored upstream, so it is rejected instead.`, { application, param });
36
+ }
37
+ function invalidValue(param, value, valid) {
38
+ throw validationError(`Invalid ${param} value "${value}". Valid values: ${valid.join(', ')}.`, {
39
+ param,
40
+ valid: [...valid],
41
+ value,
42
+ });
43
+ }
44
+ class ParamBag {
45
+ entries = {};
46
+ constructor(application) {
47
+ if (application !== undefined)
48
+ this.entries['Applikation'] = application;
49
+ }
50
+ set(name, value) {
51
+ this.entries[name] = value;
52
+ return this;
53
+ }
54
+ /** Set when the caller provided a non-empty value. */
55
+ setIf(name, value) {
56
+ if (value !== undefined && value !== '')
57
+ this.set(name, value);
58
+ return this;
59
+ }
60
+ paging(params) {
61
+ if (params.pageSize !== undefined)
62
+ this.set('DokumenteProSeite', PAGE_SIZE_TOKENS[params.pageSize]);
63
+ if (params.page !== undefined)
64
+ this.set('Seitennummer', String(Math.max(1, Math.trunc(params.page))));
65
+ return this;
66
+ }
67
+ changedSince(code) {
68
+ if (code === undefined)
69
+ return this;
70
+ const token = CHANGED_SINCE_TOKENS.get(code);
71
+ if (!token)
72
+ invalidValue('changedSince', code, [...CHANGED_SINCE_TOKENS.keys()]);
73
+ return this.set('ImRisSeit', token);
74
+ }
75
+ sort(column, direction) {
76
+ this.set('Sortierung.SortedByColumn', column);
77
+ if (direction !== undefined) {
78
+ this.set('Sortierung.SortDirection', direction === 'ascending' ? 'Ascending' : 'Descending');
79
+ }
80
+ return this;
81
+ }
82
+ toRequest(controller) {
83
+ return { controller, params: this.entries };
84
+ }
85
+ }
86
+ function stateByCode(code) {
87
+ const state = STATE_BY_CODE.get(code);
88
+ if (!state)
89
+ invalidValue('state', code, [...STATE_BY_CODE.keys()]);
90
+ return state;
91
+ }
92
+ /* ------------------------------------------------------------------------------------ */
93
+ /* Ministry expansion */
94
+ /* ------------------------------------------------------------------------------------ */
95
+ /**
96
+ * Resolve a ministry input (abbreviation or full designation) to the value the target
97
+ * issuer-param family needs. Phrase-matched `EinbringendeStelle` gets the bare
98
+ * abbreviation (matches every designation era of that abbreviation — live-confirmed equal
99
+ * to the composite, 536 = 536); the exact-match families get the full designation
100
+ * (Erlaesse `Bundesministerium`) or the `ABBR (Name)` composite (Mrp `Einbringer`).
101
+ * Unknown or ambiguous inputs throw a ValidationError naming the near-misses.
102
+ */
103
+ export function expandMinistry(input, family) {
104
+ const needle = input.trim();
105
+ const lower = needle.toLowerCase();
106
+ const matches = RIS_MINISTRIES.filter((ministry) => ministry.acceptedBy.some((accepted) => accepted === family) &&
107
+ (ministry.abbreviation?.toLowerCase() === lower ||
108
+ ministry.designation.toLowerCase() === lower ||
109
+ ministry.mrpComposite?.toLowerCase() === lower));
110
+ if (matches.length === 0) {
111
+ const nearMisses = RIS_MINISTRIES.filter((ministry) => ministry.abbreviation?.toLowerCase().startsWith(lower) ||
112
+ ministry.designation.toLowerCase().includes(lower))
113
+ .slice(0, 3)
114
+ .map((ministry) => ministry.abbreviation ?? ministry.designation);
115
+ throw validationError(`Unknown ministry "${needle}"${nearMisses.length > 0 ? ` — closest matches: ${nearMisses.join(', ')}` : ''}. Pass an abbreviation or full designation from the RIS ministries table.`, { input: needle, nearMisses });
116
+ }
117
+ if (family === 'einbringende_stelle') {
118
+ const first = matches[0];
119
+ return first.abbreviation ?? first.designation;
120
+ }
121
+ const values = [
122
+ ...new Set(matches.flatMap((ministry) => {
123
+ const value = family === 'mrp_einbringer' ? ministry.mrpComposite : ministry.designation;
124
+ return value === null ? [] : [value];
125
+ })),
126
+ ];
127
+ if (values.length === 0) {
128
+ throw validationError(`Ministry "${needle}" is not accepted by this issuer parameter.`, {
129
+ family,
130
+ input: needle,
131
+ });
132
+ }
133
+ if (values.length > 1) {
134
+ throw validationError(`Ministry "${needle}" is ambiguous here — pass the full designation instead: ${values.join(' | ')}.`, { candidates: values, input: needle });
135
+ }
136
+ return values[0];
137
+ }
138
+ /** Build a legislation search request. */
139
+ export function buildLegislationRequest(params) {
140
+ const app = params.application;
141
+ const consolidated = app === 'BrKons' || app === 'LrKons';
142
+ const bag = new ParamBag(app);
143
+ bag.setIf(app === 'Erv' ? 'SearchTerms' : 'Suchworte', params.query);
144
+ bag.setIf(app === 'Erv' ? 'Title' : 'Titel', params.title);
145
+ if (params.state !== undefined) {
146
+ const state = stateByCode(params.state);
147
+ if (app === 'LrKons')
148
+ bag.set(`Bundesland.${state.flagParam}`, 'true');
149
+ else if (app === 'Gr')
150
+ bag.set('Bundesland', state.flatAscii);
151
+ else
152
+ unsupported('state', app);
153
+ }
154
+ if (params.municipality !== undefined) {
155
+ if (app !== 'Gr')
156
+ unsupported('municipality', app);
157
+ bag.set('Gemeinde', params.municipality);
158
+ }
159
+ if (params.inForceAsOf !== undefined) {
160
+ if (app === 'Erv')
161
+ unsupported('inForceAsOf', app);
162
+ bag.set('FassungVom', params.inForceAsOf);
163
+ }
164
+ for (const [field, name] of [
165
+ ['enteredForceFrom', 'Fassung.VonInkrafttretensdatum'],
166
+ ['enteredForceTo', 'Fassung.BisInkrafttretensdatum'],
167
+ ['leftForceFrom', 'Fassung.VonAusserkrafttretensdatum'],
168
+ ['leftForceTo', 'Fassung.BisAusserkrafttretensdatum'],
169
+ ]) {
170
+ if (params[field] !== undefined) {
171
+ if (!consolidated)
172
+ unsupported(field, app);
173
+ bag.set(name, params[field]);
174
+ }
175
+ }
176
+ if (params.sectionFrom !== undefined ||
177
+ params.sectionTo !== undefined ||
178
+ params.sectionType !== undefined) {
179
+ if (!consolidated)
180
+ unsupported('sectionFrom/sectionTo/sectionType', app);
181
+ bag.setIf('Abschnitt.Von', params.sectionFrom);
182
+ bag.setIf('Abschnitt.Bis', params.sectionTo);
183
+ bag.set('Abschnitt.Typ', params.sectionType ?? 'Paragraph');
184
+ }
185
+ if (params.lawId !== undefined) {
186
+ if (!consolidated)
187
+ unsupported('lawId', app);
188
+ bag.set('Gesetzesnummer', params.lawId);
189
+ }
190
+ if (params.index !== undefined) {
191
+ if (!consolidated)
192
+ unsupported('index', app);
193
+ bag.set('Index', params.index);
194
+ }
195
+ bag.changedSince(params.changedSince);
196
+ if (params.sortBy !== undefined) {
197
+ if (!consolidated)
198
+ unsupported('sortBy', app);
199
+ bag.sort(params.sortBy === 'section' ? 'ArtikelParagraphAnlage' : 'Inkrafttretensdatum', params.sortDirection);
200
+ }
201
+ bag.paging(params);
202
+ return bag.toRequest(app === 'Gr' ? 'Gemeinden' : app === 'LrKons' ? 'Landesrecht' : 'Bundesrecht');
203
+ }
204
+ const COLLECTION_NUMBER_COURTS = new Set(['uvs', 'vfgh', 'vwgh']);
205
+ const ISSUING_BODY_COURTS = new Set(['dok', 'dsk', 'pvak', 'verg']);
206
+ const STATE_COURTS = new Set(['lvwg', 'uvs']);
207
+ const NO_DECISION_TYPE_COURTS = new Set(['gbk', 'normenliste', 'upts']);
208
+ /** Build a case-law search request. */
209
+ export function buildCaseLawRequest(params) {
210
+ const court = COURT_BY_CODE.get(params.court);
211
+ if (!court)
212
+ invalidValue('court', params.court, [...COURT_BY_CODE.keys()]);
213
+ const code = court.code;
214
+ const upts = code === 'upts';
215
+ const bag = new ParamBag(court.application);
216
+ bag.setIf('Suchworte', params.query);
217
+ bag.setIf('Norm', params.norm);
218
+ if (params.caseNumber !== undefined) {
219
+ if (code === 'normenliste')
220
+ unsupported('caseNumber', court.application);
221
+ bag.set(upts ? 'GZ' : 'Geschaeftszahl', params.caseNumber);
222
+ }
223
+ if (params.decisionType !== undefined && params.decisionType !== 'all') {
224
+ if (NO_DECISION_TYPE_COURTS.has(code))
225
+ unsupported('decisionType', court.application);
226
+ bag.set(params.decisionType === 'headnote'
227
+ ? 'Dokumenttyp.SucheInRechtssaetzen'
228
+ : 'Dokumenttyp.SucheInEntscheidungstexten', 'true');
229
+ }
230
+ if (params.decidedFrom !== undefined || params.decidedTo !== undefined) {
231
+ if (code === 'normenliste')
232
+ unsupported('decidedFrom/decidedTo', court.application);
233
+ bag.setIf(upts ? 'Entscheidungsdatum.Von' : 'EntscheidungsdatumVon', params.decidedFrom);
234
+ bag.setIf(upts ? 'Entscheidungsdatum.Bis' : 'EntscheidungsdatumBis', params.decidedTo);
235
+ }
236
+ if (params.decisionKind !== undefined) {
237
+ const kinds = DECISION_KINDS_BY_COURT.get(code);
238
+ if (!kinds)
239
+ unsupported('decisionKind', court.application);
240
+ if (!kinds.values.some((value) => value === params.decisionKind)) {
241
+ invalidValue('decisionKind', params.decisionKind, kinds.values);
242
+ }
243
+ bag.set('Entscheidungsart', params.decisionKind);
244
+ }
245
+ if (params.collectionNumber !== undefined) {
246
+ if (!COLLECTION_NUMBER_COURTS.has(code))
247
+ unsupported('collectionNumber', court.application);
248
+ bag.set('Sammlungsnummer', params.collectionNumber);
249
+ }
250
+ if (params.issuingBody !== undefined) {
251
+ if (!ISSUING_BODY_COURTS.has(code))
252
+ unsupported('issuingBody', court.application);
253
+ bag.set('EntscheidendeBehoerde', params.issuingBody);
254
+ }
255
+ if (params.courtName !== undefined) {
256
+ if (code !== 'justiz')
257
+ unsupported('courtName', court.application);
258
+ bag.set('Gericht', params.courtName);
259
+ }
260
+ if (params.legalArea !== undefined) {
261
+ if (code !== 'justiz')
262
+ unsupported('legalArea', court.application);
263
+ bag.set('Rechtsgebiet', params.legalArea === 'civil' ? 'Zivilrecht' : 'Strafrecht');
264
+ }
265
+ if (params.subjectArea !== undefined) {
266
+ if (code !== 'justiz')
267
+ unsupported('subjectArea', court.application);
268
+ bag.set('Fachgebiet', params.subjectArea);
269
+ }
270
+ if (params.state !== undefined) {
271
+ if (!STATE_COURTS.has(code))
272
+ unsupported('state', court.application);
273
+ bag.set('Bundesland', stateByCode(params.state).flatAscii);
274
+ }
275
+ if (params.party !== undefined) {
276
+ if (!upts)
277
+ unsupported('party', court.application);
278
+ bag.set('Partei', params.party);
279
+ }
280
+ if (params.commission !== undefined) {
281
+ if (code !== 'gbk')
282
+ unsupported('commission', court.application);
283
+ bag.set('Kommission', params.commission === 'federal'
284
+ ? 'BundesGleichbehandlungskommission'
285
+ : 'Gleichbehandlungskommission');
286
+ }
287
+ if (params.senate !== undefined) {
288
+ if (code !== 'gbk')
289
+ unsupported('senate', court.application);
290
+ bag.set('Senat', params.senate);
291
+ }
292
+ if (params.discriminationGround !== undefined) {
293
+ if (code !== 'gbk')
294
+ unsupported('discriminationGround', court.application);
295
+ bag.set('Diskriminierungsgrund', params.discriminationGround);
296
+ }
297
+ if (params.subjectLaw !== undefined) {
298
+ if (code !== 'bks')
299
+ unsupported('subjectLaw', court.application);
300
+ bag.set('Bereich', params.subjectLaw);
301
+ }
302
+ bag.changedSince(params.changedSince);
303
+ if (params.sortBy !== undefined) {
304
+ if (code === 'normenliste')
305
+ unsupported('sortBy', court.application);
306
+ const column = params.sortBy === 'decision_date'
307
+ ? upts
308
+ ? 'Entscheidungsdatum'
309
+ : 'Datum'
310
+ : upts
311
+ ? 'GZ'
312
+ : 'Geschaeftszahl';
313
+ bag.sort(column, params.sortDirection);
314
+ }
315
+ bag.paging(params);
316
+ return bag.toRequest(upts ? 'Sonstige' : 'Judikatur');
317
+ }
318
+ const GAZETTE_NUMBER_PARAMS = {
319
+ BgblAlt: 'Gesetzblattnummer',
320
+ BgblAuth: 'Bgblnummer',
321
+ BgblPdf: 'Bundesgesetzblatt',
322
+ Bvb: 'Kundmachungsnummer',
323
+ GrA: 'Kundmachungsnummer',
324
+ Lgbl: 'Lgblnummer',
325
+ LgblAuth: 'Lgblnummer',
326
+ Vbl: 'Kundmachungsnummer',
327
+ };
328
+ const GAZETTE_DATE_PARAMS = {
329
+ BgblAlt: ['Kundgemacht.Von', 'Kundgemacht.Bis'],
330
+ BgblAuth: ['KundmachungsdatumVon', 'KundmachungsdatumBis'],
331
+ BgblPdf: ['Kundgemacht.Von', 'Kundgemacht.Bis'],
332
+ Bvb: ['Kundmachungsdatum.Von', 'Kundmachungsdatum.Bis'],
333
+ GrA: ['Kundmachungsdatum.Von', 'Kundmachungsdatum.Bis'],
334
+ Lgbl: ['Kundmachung.Von', 'Kundmachung.Bis'],
335
+ LgblAuth: ['Kundmachung.Von', 'Kundmachung.Bis'],
336
+ LgblNO: ['Ausgabedatum.Von', 'Ausgabedatum.Bis'],
337
+ Vbl: ['Kundmachungsdatum.Von', 'Kundmachungsdatum.Bis'],
338
+ };
339
+ const GAZETTE_TYPE_FLAGS = {
340
+ announcements: 'Typ.SucheInKundmachungen',
341
+ laws: 'Typ.SucheInGesetzen',
342
+ other: 'Typ.SucheInSonstiges',
343
+ regulations: 'Typ.SucheInVerordnungen',
344
+ };
345
+ const GAZETTE_TYPE_APPS = new Set([
346
+ 'BgblAuth',
347
+ 'BgblPdf',
348
+ 'Lgbl',
349
+ 'LgblAuth',
350
+ 'LgblNO',
351
+ ]);
352
+ const GAZETTE_CONTROLLERS = {
353
+ BgblAlt: 'Bundesrecht',
354
+ BgblAuth: 'Bundesrecht',
355
+ BgblPdf: 'Bundesrecht',
356
+ Bvb: 'Bezirke',
357
+ GrA: 'Gemeinden',
358
+ Lgbl: 'Landesrecht',
359
+ LgblAuth: 'Landesrecht',
360
+ LgblNO: 'Landesrecht',
361
+ Vbl: 'Landesrecht',
362
+ };
363
+ /** Sort columns confirmed valid per gazette application (probed 2026-07-05). */
364
+ const GAZETTE_SORT_COLUMNS = {
365
+ BgblAlt: { number: 'Fundstelle', published: 'Kundmachungsdatum' },
366
+ BgblAuth: { published: 'Kundmachungsdatum' },
367
+ BgblPdf: { published: 'Kundmachungsdatum' },
368
+ Bvb: { published: 'Kundmachungsdatum' },
369
+ GrA: { published: 'Kundmachungsdatum' },
370
+ Lgbl: { number: 'Fundstelle', published: 'Kundmachungsdatum' },
371
+ LgblAuth: { published: 'Kundmachungsdatum' },
372
+ LgblNO: { published: 'Ausgabedatum' },
373
+ Vbl: { published: 'Kundmachungsdatum' },
374
+ };
375
+ /** Build a gazette search request. */
376
+ export function buildGazetteRequest(params) {
377
+ const app = params.application;
378
+ const bag = new ParamBag(app);
379
+ bag.setIf('Suchworte', params.query);
380
+ bag.setIf('Titel', params.title);
381
+ if (params.number !== undefined) {
382
+ const name = GAZETTE_NUMBER_PARAMS[app];
383
+ if (!name)
384
+ unsupported('number', app);
385
+ bag.set(name, params.number);
386
+ }
387
+ if (params.year !== undefined) {
388
+ if (app !== 'BgblAlt')
389
+ unsupported('year', app);
390
+ bag.set('Jahrgang', params.year);
391
+ }
392
+ if (params.part !== undefined) {
393
+ if (app !== 'BgblAuth' && app !== 'BgblPdf')
394
+ unsupported('part', app);
395
+ if (params.part === 'pre_1997') {
396
+ if (app !== 'BgblPdf')
397
+ unsupported('part: pre_1997', app);
398
+ bag.set('Teil.SucheInAlt', 'true');
399
+ }
400
+ else {
401
+ bag.set(`Teil.SucheInTeil${params.part.slice(-1)}`, 'true');
402
+ }
403
+ }
404
+ if (params.type !== undefined) {
405
+ if (!GAZETTE_TYPE_APPS.has(app))
406
+ unsupported('type', app);
407
+ bag.set(GAZETTE_TYPE_FLAGS[params.type], 'true');
408
+ }
409
+ if (params.publishedFrom !== undefined || params.publishedTo !== undefined) {
410
+ const [from, to] = GAZETTE_DATE_PARAMS[app];
411
+ bag.setIf(from, params.publishedFrom);
412
+ bag.setIf(to, params.publishedTo);
413
+ }
414
+ if (params.issuer !== undefined) {
415
+ if (app === 'BgblAuth')
416
+ bag.set('EinbringendeStelle', expandMinistry(params.issuer, 'einbringende_stelle'));
417
+ else if (app === 'Vbl')
418
+ bag.set('Einbringer', params.issuer);
419
+ else
420
+ unsupported('issuer', app);
421
+ }
422
+ if (params.state !== undefined) {
423
+ const state = stateByCode(params.state);
424
+ if (app === 'LgblAuth')
425
+ bag.set(`Bundesland.${state.flagParam}`, 'true');
426
+ else if (app === 'Lgbl') {
427
+ if (!state.inLgbl) {
428
+ throw validationError(`The historical Lgbl gazette has no ${state.name} — Niederösterreich uses the LgblNO systematic collection and Wien is not carried.`, { application: app, state: params.state });
429
+ }
430
+ bag.set(`Bundesland.${state.flagParam}`, 'true');
431
+ }
432
+ else if (app === 'Vbl') {
433
+ if (!VBL_COVERED_STATES.includes(params.state)) {
434
+ throw validationError(`Verordnungsblätter in RIS currently cover Tirol only — "${params.state}" fails on the RIS backend.`, { application: app, coveredStates: [...VBL_COVERED_STATES], state: params.state });
435
+ }
436
+ bag.set('Bundesland', state.flatAscii);
437
+ }
438
+ else if (app === 'Bvb')
439
+ bag.set('Bundesland', state.flatUmlaut);
440
+ else if (app === 'GrA')
441
+ bag.set('Bundesland', state.flatAscii);
442
+ else
443
+ unsupported('state', app);
444
+ }
445
+ if (params.districtAuthority !== undefined) {
446
+ if (app !== 'Bvb')
447
+ unsupported('districtAuthority', app);
448
+ bag.set('Bezirksverwaltungsbehoerde', params.districtAuthority);
449
+ }
450
+ if (params.municipality !== undefined) {
451
+ if (app !== 'GrA')
452
+ unsupported('municipality', app);
453
+ bag.set('Gemeinde', params.municipality);
454
+ }
455
+ if (params.sortBy !== undefined) {
456
+ const column = GAZETTE_SORT_COLUMNS[app]?.[params.sortBy];
457
+ if (!column)
458
+ unsupported(`sortBy: ${params.sortBy}`, app);
459
+ bag.sort(column, params.sortDirection);
460
+ }
461
+ bag.paging(params);
462
+ return bag.toRequest(GAZETTE_CONTROLLERS[app]);
463
+ }
464
+ /** Build a drafts search request. */
465
+ export function buildDraftsRequest(params) {
466
+ const stage = STAGE_BY_CODE.get(params.stage);
467
+ if (!stage)
468
+ invalidValue('stage', params.stage, [...STAGE_BY_CODE.keys()]);
469
+ const review = stage.code === 'review_drafts';
470
+ const bag = new ParamBag(stage.application);
471
+ bag.setIf('Suchworte', params.query);
472
+ bag.setIf('Titel', params.title);
473
+ if (params.ministry !== undefined)
474
+ bag.set('EinbringendeStelle', expandMinistry(params.ministry, 'einbringende_stelle'));
475
+ if (params.inReviewOn !== undefined) {
476
+ if (!review)
477
+ unsupported('inReviewOn', stage.application);
478
+ bag.set('InBegutachtungAm', params.inReviewOn);
479
+ }
480
+ if (params.decidedFrom !== undefined || params.decidedTo !== undefined) {
481
+ if (review)
482
+ unsupported('decidedFrom/decidedTo', stage.application);
483
+ bag.setIf('BeschlussdatumVon', params.decidedFrom);
484
+ bag.setIf('BeschlussdatumBis', params.decidedTo);
485
+ }
486
+ bag.changedSince(params.changedSince);
487
+ if (params.sortBy !== undefined) {
488
+ const column = params.sortBy === 'title'
489
+ ? 'Kurztitel'
490
+ : params.sortBy === 'ministry'
491
+ ? 'EinbringendeStelle'
492
+ : review
493
+ ? 'EndeBegutachtungsfrist'
494
+ : 'Beschlussdatum';
495
+ bag.sort(column, params.sortDirection);
496
+ }
497
+ bag.paging(params);
498
+ return bag.toRequest('Bundesrecht');
499
+ }
500
+ const ANNOUNCEMENT_NUMBER_PARAMS = {
501
+ Avn: 'Avnnummer',
502
+ Avsv: 'Avsvnummer',
503
+ Spg: 'Spgnummer',
504
+ };
505
+ const ANNOUNCEMENT_DATE_PARAMS = {
506
+ Avn: ['Kundmachung.Von', 'Kundmachung.Bis'],
507
+ Avsv: ['Kundmachung.Von', 'Kundmachung.Bis'],
508
+ KmGer: ['Kundmachungsdatum.Von', 'Kundmachungsdatum.Bis'],
509
+ Mrp: ['Sitzungsdatum.Von', 'Sitzungsdatum.Bis'],
510
+ PruefGewO: ['Kundmachungsdatum.Von', 'Kundmachungsdatum.Bis'],
511
+ Spg: ['Kundmachungsdatum.Von', 'Kundmachungsdatum.Bis'],
512
+ };
513
+ /** Flat vs nested in-force spelling per collection (probed 2026-07-05). */
514
+ const ANNOUNCEMENT_FASSUNG_PARAMS = {
515
+ Avn: 'FassungVom',
516
+ Erlaesse: 'FassungVom',
517
+ KmGer: 'Fassung.FassungVom',
518
+ PruefGewO: 'Fassung.FassungVom',
519
+ Spg: 'Fassung.FassungVom',
520
+ };
521
+ const AVN_TYPE_FLAGS = [
522
+ 'Kundmachungen',
523
+ 'SonstigeVeroeffentlichungen',
524
+ 'VeroeffentlichungenAufGrundVEVO',
525
+ ];
526
+ const NORM_APPS = new Set(['Avn', 'Erlaesse']);
527
+ /** Sort columns confirmed valid per collection (probed 2026-07-05). */
528
+ const ANNOUNCEMENT_SORT_COLUMNS = {
529
+ Avn: { number: 'Avnnummer', published: 'Kundmachungsdatum' },
530
+ Avsv: { number: 'Avsvnummer', published: 'Kundmachungsdatum' },
531
+ Mrp: { published: 'Sitzungsdatum' },
532
+ PruefGewO: { published: 'Kundmachungsdatum' },
533
+ Spg: { number: 'Spgnummer' },
534
+ };
535
+ const PLAN_KIND_TOKENS = {
536
+ all: 'Alle',
537
+ expert_opinion: 'Gutachten',
538
+ regulation: 'Verordnungen',
539
+ };
540
+ /** Build an announcements search request. */
541
+ export function buildAnnouncementsRequest(params) {
542
+ const collection = COLLECTION_BY_CODE.get(params.collection);
543
+ if (!collection)
544
+ invalidValue('collection', params.collection, [...COLLECTION_BY_CODE.keys()]);
545
+ const app = collection.application;
546
+ const bag = new ParamBag(app);
547
+ bag.setIf('Suchworte', params.query);
548
+ if (params.title !== undefined) {
549
+ if (app === 'Mrp')
550
+ unsupported('title', app);
551
+ bag.set('Titel', params.title);
552
+ }
553
+ if (params.number !== undefined) {
554
+ const name = ANNOUNCEMENT_NUMBER_PARAMS[app];
555
+ if (!name)
556
+ unsupported('number', app);
557
+ bag.set(name, params.number);
558
+ }
559
+ if (params.publishedFrom !== undefined || params.publishedTo !== undefined) {
560
+ const pair = ANNOUNCEMENT_DATE_PARAMS[app];
561
+ if (!pair)
562
+ unsupported('publishedFrom/publishedTo', app);
563
+ bag.setIf(pair[0], params.publishedFrom);
564
+ bag.setIf(pair[1], params.publishedTo);
565
+ }
566
+ if (params.inForceAsOf !== undefined) {
567
+ const name = ANNOUNCEMENT_FASSUNG_PARAMS[app];
568
+ if (!name)
569
+ unsupported('inForceAsOf', app);
570
+ bag.set(name, params.inForceAsOf);
571
+ }
572
+ if (params.enteredForceFrom !== undefined || params.enteredForceTo !== undefined) {
573
+ if (app !== 'Erlaesse')
574
+ unsupported('enteredForceFrom/enteredForceTo', app);
575
+ bag.setIf('VonInkrafttretensdatum', params.enteredForceFrom);
576
+ bag.setIf('BisInkrafttretensdatum', params.enteredForceTo);
577
+ }
578
+ if (params.issuer !== undefined) {
579
+ if (app === 'Avsv')
580
+ bag.set('Urheber', params.issuer);
581
+ else if (app === 'Erlaesse')
582
+ bag.set('Bundesministerium', expandMinistry(params.issuer, 'erlaesse_bundesministerium'));
583
+ else if (app === 'Mrp')
584
+ bag.set('Einbringer', expandMinistry(params.issuer, 'mrp_einbringer'));
585
+ else
586
+ unsupported('issuer', app);
587
+ }
588
+ if (params.norm !== undefined) {
589
+ if (!NORM_APPS.has(app))
590
+ unsupported('norm', app);
591
+ bag.set('Norm', params.norm);
592
+ }
593
+ if (params.caseNumber !== undefined) {
594
+ if (!NORM_APPS.has(app))
595
+ unsupported('caseNumber', app);
596
+ bag.set('Geschaeftszahl', params.caseNumber);
597
+ }
598
+ if (params.type !== undefined) {
599
+ if (app === 'Avn') {
600
+ if (!AVN_TYPE_FLAGS.includes(params.type)) {
601
+ invalidValue('type', params.type, AVN_TYPE_FLAGS);
602
+ }
603
+ bag.set(`Typ.SucheIn${params.type}`, 'true');
604
+ }
605
+ else if (app === 'KmGer' || app === 'PruefGewO') {
606
+ bag.set('Typ', params.type);
607
+ }
608
+ else
609
+ unsupported('type', app);
610
+ }
611
+ if (params.department !== undefined) {
612
+ if (app !== 'Erlaesse')
613
+ unsupported('department', app);
614
+ bag.set('Abteilung', params.department);
615
+ }
616
+ if (params.planScope !== undefined ||
617
+ params.planKind !== undefined ||
618
+ params.planState !== undefined) {
619
+ if (app !== 'Spg')
620
+ unsupported('planScope/planKind/planState', app);
621
+ const scope = params.planScope ?? (params.planState !== undefined ? 'regional' : 'federal');
622
+ const kind = PLAN_KIND_TOKENS[params.planKind ?? 'all'];
623
+ if (scope === 'federal') {
624
+ if (params.planState !== undefined) {
625
+ throw validationError('planState applies only to regional (RSG) health-structure plans.', {
626
+ planScope: scope,
627
+ planState: params.planState,
628
+ });
629
+ }
630
+ bag.set('OsgSuchEinschraenkung.SpgStrukturplanType', kind);
631
+ }
632
+ else {
633
+ bag.set('RsgSuchEinschraenkung.SpgStrukturplanType', kind);
634
+ if (params.planState !== undefined) {
635
+ bag.set('RsgSuchEinschraenkung.Land', stateByCode(params.planState).flatAscii);
636
+ }
637
+ }
638
+ }
639
+ if (params.sessionNumber !== undefined) {
640
+ if (app !== 'Mrp')
641
+ unsupported('sessionNumber', app);
642
+ bag.set('Sitzungsnummer', params.sessionNumber);
643
+ }
644
+ if (params.legislature !== undefined) {
645
+ if (app !== 'Mrp')
646
+ unsupported('legislature', app);
647
+ bag.set('Gesetzgebungsperiode', params.legislature);
648
+ }
649
+ bag.changedSince(params.changedSince);
650
+ if (params.sortBy !== undefined) {
651
+ const column = ANNOUNCEMENT_SORT_COLUMNS[app]?.[params.sortBy];
652
+ if (!column)
653
+ unsupported(`sortBy: ${params.sortBy}`, app);
654
+ bag.sort(column, params.sortDirection);
655
+ }
656
+ bag.paging(params);
657
+ return bag.toRequest('Sonstige');
658
+ }
659
+ /** Build a History change-feed request (maps the four aliased application names). */
660
+ export function buildTrackChangesRequest(params) {
661
+ const application = APPLICATION_BY_CODE.get(params.application);
662
+ if (!application)
663
+ invalidValue('application', params.application, [...APPLICATION_BY_CODE.keys()]);
664
+ const bag = new ParamBag();
665
+ bag.set('Anwendung', application.historyName);
666
+ bag.setIf('AenderungenVon', params.changedFrom);
667
+ bag.setIf('AenderungenBis', params.changedTo);
668
+ if (params.includeDeleted === true)
669
+ bag.set('IncludeDeletedDocuments', 'True');
670
+ bag.paging(params);
671
+ return bag.toRequest('History');
672
+ }
673
+ //# sourceMappingURL=request-builder.js.map