@jvaarala/vm-buketti 0.1.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.
- package/LICENSE +35 -0
- package/README.md +96 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +152 -0
- package/dist/index.js.map +1 -0
- package/dist/schema.d.ts +98 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +4 -0
- package/dist/schema.js.map +1 -0
- package/package.json +50 -0
- package/src/index.ts +214 -0
- package/src/schema.ts +120 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jesse Väärälä
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
DATA LICENSE NOTICE
|
|
26
|
+
|
|
27
|
+
The data retrieved through this library originates from budjetti.vm.fi and is
|
|
28
|
+
published by the Ministry of Finance of Finland as part of the open government
|
|
29
|
+
data programme. The data is licensed under the Creative Commons Attribution 4.0
|
|
30
|
+
International license (CC BY 4.0):
|
|
31
|
+
|
|
32
|
+
http://creativecommons.org/licenses/by/4.0/
|
|
33
|
+
|
|
34
|
+
Users of this library must comply with the CC BY 4.0 terms when using the data,
|
|
35
|
+
including crediting the Ministry of Finance of Finland as the source.
|
package/README.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# vm-buketti
|
|
2
|
+
|
|
3
|
+
TypeScript client for the Finnish state budget open data API at [budjetti.vm.fi](https://budjetti.vm.fi).
|
|
4
|
+
|
|
5
|
+
Turns the XML-based API into a lazy-loading tree you can navigate by awaiting properties — no manual XML parsing, no upfront fetches.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install @jvaarala/vm-buketti
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { createVmBuketti } from '@jvaarala/vm-buketti';
|
|
17
|
+
|
|
18
|
+
// Node 18+ (direct fetch — no proxy needed):
|
|
19
|
+
const service = createVmBuketti();
|
|
20
|
+
|
|
21
|
+
// Browser (requires a CORS proxy):
|
|
22
|
+
const service = createVmBuketti({ proxyUrl: '/proxy' });
|
|
23
|
+
|
|
24
|
+
// Navigate the budget tree
|
|
25
|
+
const juuri = service.getJuuri();
|
|
26
|
+
const vuodet = await juuri.Vuosi; // all budget years
|
|
27
|
+
const teos = vuodet[0].Teos[0]; // first publication (e.g. tae)
|
|
28
|
+
const kanta = teos.Kanta[0]; // budget version
|
|
29
|
+
const pkList = await kanta.Paaluokka; // expense main classes (lazy fetch)
|
|
30
|
+
const menot = await pkList[0].Menoluku; // chapters within first main class
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Data is fetched on demand. Accessing a property for the first time fires the corresponding XML request; repeated accesses return the cached Promise.
|
|
34
|
+
|
|
35
|
+
## API
|
|
36
|
+
|
|
37
|
+
### `createVmBuketti(options?)`
|
|
38
|
+
|
|
39
|
+
Returns a `VmBuketti` instance.
|
|
40
|
+
|
|
41
|
+
| Option | Type | Description |
|
|
42
|
+
|--------|------|-------------|
|
|
43
|
+
| `proxyUrl` | `string` | Prefix for CORS proxy requests, e.g. `'/proxy'`. Appends `?url=<encoded>`. |
|
|
44
|
+
| `fetchXml` | `(url: string) => Promise<string>` | Custom fetch function. Takes precedence over `proxyUrl`. |
|
|
45
|
+
| `onError` | `(error: Error, url: string) => void` | Called when a sub-link fetch fails. Defaults to silent (partial data returned). |
|
|
46
|
+
|
|
47
|
+
### `service.getJuuri(): Juuri`
|
|
48
|
+
|
|
49
|
+
Returns the root node. Calling this does not trigger any network requests — fetches are deferred until properties are accessed.
|
|
50
|
+
|
|
51
|
+
## Budget hierarchy
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
Juuri
|
|
55
|
+
└── Vuosi[] (year)
|
|
56
|
+
└── Teos[] (publication: tae, ltae1–ltae7)
|
|
57
|
+
└── Kanta[] (version: hallituksenEsitys, eduskunnanKirjelma, …)
|
|
58
|
+
├── Paaluokka[] → Menoluku[] → Menomomentti[] (expenses)
|
|
59
|
+
└── Osasto[] → Tuloluku[] → Tulomomentti[] (revenues)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## TypeScript types
|
|
63
|
+
|
|
64
|
+
All types mirror the XSD schema from budjetti.vm.fi. Lazy nodes (fetched on demand) use `Lazy*` prefixed types with `Promise<T[]>` children.
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import type { LazyVuosi, LazyKanta, Menoluku } from '@jvaarala/vm-buketti';
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Enumerated attributes (e.g. `teostyyppi`, `momenttityyppi`) include a `| (string & {})` fallback so that values the XSD hasn't yet listed — such as `ltae6`/`ltae7` in live data — are accepted without TypeScript errors while known values still benefit from autocomplete.
|
|
71
|
+
|
|
72
|
+
## Regenerating types
|
|
73
|
+
|
|
74
|
+
If the upstream XSD changes, regenerate `src/schema.ts`:
|
|
75
|
+
|
|
76
|
+
```sh
|
|
77
|
+
npm run gen-schema
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The generated file must not be edited by hand.
|
|
81
|
+
|
|
82
|
+
## Requirements
|
|
83
|
+
|
|
84
|
+
- Node 18+ or any modern browser (uses `fetch` and `DOMParser`)
|
|
85
|
+
- No runtime dependencies
|
|
86
|
+
|
|
87
|
+
## Data license
|
|
88
|
+
|
|
89
|
+
The budget data accessed through this library is published by the Finnish Ministry of Finance as part of the open government data programme. Budget proposals have been available in machine-readable XML format since 2014 (government proposals from 2014, Ministry of Finance drafts from 2016). Parliamentary letters are also published in machine-readable form.
|
|
90
|
+
|
|
91
|
+
The data is licensed under the **Creative Commons Attribution 4.0 International** license (CC BY 4.0):
|
|
92
|
+
http://creativecommons.org/licenses/by/4.0/
|
|
93
|
+
|
|
94
|
+
When using data retrieved via this library, you must credit the Ministry of Finance of Finland as the data source.
|
|
95
|
+
|
|
96
|
+
This library itself (the wrapper code) is MIT-licensed.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export type { Laskelmaraha, Muutos, Vertailuluvut, Budjettilaskelma, Tulomomentti, Tuloluku, Osasto, Menomomentti, Menoluku, Paaluokka, Kanta, Teos, Vuosi, LazyOsasto, LazyPaaluokka, LazyKanta, LazyTeos, LazyVuosi, Juuri, } from './schema.js';
|
|
2
|
+
import type { Juuri } from './schema.js';
|
|
3
|
+
export interface VmBukettiOptions {
|
|
4
|
+
/**
|
|
5
|
+
* URL of a CORS proxy endpoint that accepts `?url=<encoded>` query params.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* createVmBuketti({ proxyUrl: '/proxy' });
|
|
9
|
+
*/
|
|
10
|
+
proxyUrl?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Custom function for fetching XML content from a URL.
|
|
13
|
+
* Use this to add custom headers, authentication, etc.
|
|
14
|
+
* Takes precedence over `proxyUrl`.
|
|
15
|
+
*
|
|
16
|
+
* Defaults to `fetch(url)` using the global `fetch`.
|
|
17
|
+
*/
|
|
18
|
+
fetchXml?: (url: string) => Promise<string>;
|
|
19
|
+
/**
|
|
20
|
+
* Called when a sub-link fetch fails during lazy loading.
|
|
21
|
+
* If not provided, failures are silently ignored (partial data is returned).
|
|
22
|
+
*/
|
|
23
|
+
onError?: (error: Error, url: string) => void;
|
|
24
|
+
}
|
|
25
|
+
export interface VmBuketti {
|
|
26
|
+
getJuuri(): Juuri;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Creates a VmBuketti instance for accessing the Finnish state budget API.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* // Node.js (fetch available globally in Node 18+):
|
|
33
|
+
* const service = createVmBuketti();
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* // Browser with CORS proxy:
|
|
37
|
+
* const service = createVmBuketti({
|
|
38
|
+
* fetchXml: url => fetch('/proxy?url=' + encodeURIComponent(url)).then(r => r.text())
|
|
39
|
+
* });
|
|
40
|
+
*/
|
|
41
|
+
export declare function createVmBuketti(options?: VmBukettiOptions): VmBuketti;
|
|
42
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,gBAAgB,EACrD,YAAY,EAAE,QAAQ,EAAE,MAAM,EAC9B,YAAY,EAAE,QAAQ,EAAE,SAAS,EACjC,KAAK,EAAE,IAAI,EAAE,KAAK,EAClB,UAAU,EAAE,aAAa,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EACzD,KAAK,GACN,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,WAAW,gBAAgB;IAC/B;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAE5C;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/C;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,IAAI,KAAK,CAAC;CACnB;AA2ID;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAuBrE"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
const ROOT_URL = 'https://budjetti.vm.fi/opendata/opendata-xml.jsp';
|
|
2
|
+
const XLINK_NS = 'http://www.w3.org/1999/xlink';
|
|
3
|
+
// ── XML helpers ──────────────────────────────────────────────────────────────
|
|
4
|
+
function parseXML(text) {
|
|
5
|
+
const doc = new DOMParser().parseFromString(text, 'application/xml');
|
|
6
|
+
const err = doc.querySelector('parsererror');
|
|
7
|
+
if (err)
|
|
8
|
+
throw new Error('XML parse error: ' + err.textContent?.slice(0, 120));
|
|
9
|
+
return doc;
|
|
10
|
+
}
|
|
11
|
+
function getHref(el) {
|
|
12
|
+
return el.getAttributeNS(XLINK_NS, 'href') || el.getAttribute('xlink:href') || null;
|
|
13
|
+
}
|
|
14
|
+
function toCamel(s) {
|
|
15
|
+
return s.replace(/[-:]([a-z])/g, (_, c) => c.toUpperCase());
|
|
16
|
+
}
|
|
17
|
+
// ── Generic lazy tree builder ────────────────────────────────────────────────
|
|
18
|
+
//
|
|
19
|
+
// The traversal algorithm is the same for every node in the tree:
|
|
20
|
+
// - If the element has xlink:href it must be fetched before its children are
|
|
21
|
+
// readable. A Proxy intercepts child-property accesses and fires the fetch
|
|
22
|
+
// on demand, caching the resulting Promise.
|
|
23
|
+
// - Otherwise, children are already in the DOM and can be wrapped eagerly;
|
|
24
|
+
// but if any child element carries an xlink:href the child array is still
|
|
25
|
+
// wrapped in a Promise so callers use a consistent await pattern.
|
|
26
|
+
//
|
|
27
|
+
// The Juuri root is treated the same way via a document-level Proxy.
|
|
28
|
+
function readAttrs(el) {
|
|
29
|
+
const out = {};
|
|
30
|
+
for (const attr of el.attributes) {
|
|
31
|
+
if (attr.namespaceURI)
|
|
32
|
+
continue; // skip xlink:href, xmlns:* and any other namespace attrs
|
|
33
|
+
const key = toCamel(attr.name);
|
|
34
|
+
out[key] = /^-?\d+(\.\d+)?$/.test(attr.value) ? Number(attr.value) : attr.value;
|
|
35
|
+
}
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
function buildNode(el, rootDoc, resolveEl) {
|
|
39
|
+
const attrs = readAttrs(el);
|
|
40
|
+
if (getHref(el)) {
|
|
41
|
+
// xlink element: children unknown until fetched — use a Proxy
|
|
42
|
+
let resolved = null;
|
|
43
|
+
const ensure = () => {
|
|
44
|
+
if (!resolved)
|
|
45
|
+
resolved = resolveEl(el, rootDoc);
|
|
46
|
+
return resolved;
|
|
47
|
+
};
|
|
48
|
+
const childCache = new Map();
|
|
49
|
+
return new Proxy(attrs, {
|
|
50
|
+
get(target, prop) {
|
|
51
|
+
if (typeof prop !== 'string')
|
|
52
|
+
return undefined;
|
|
53
|
+
if (prop === 'then')
|
|
54
|
+
return undefined;
|
|
55
|
+
if (Object.prototype.hasOwnProperty.call(target, prop))
|
|
56
|
+
return target[prop];
|
|
57
|
+
if (!childCache.has(prop)) {
|
|
58
|
+
childCache.set(prop, ensure().then(() => {
|
|
59
|
+
const children = [...el.children].filter(c => c.tagName === prop);
|
|
60
|
+
return children.map(c => buildNode(c, rootDoc, resolveEl));
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
return childCache.get(prop);
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
// Non-xlink element: children are in the DOM now
|
|
68
|
+
const node = { ...attrs };
|
|
69
|
+
const groups = new Map();
|
|
70
|
+
for (const child of el.children) {
|
|
71
|
+
if (!groups.has(child.tagName))
|
|
72
|
+
groups.set(child.tagName, []);
|
|
73
|
+
groups.get(child.tagName).push(child);
|
|
74
|
+
}
|
|
75
|
+
for (const [tag, children] of groups) {
|
|
76
|
+
node[tag] = children.map(c => buildNode(c, rootDoc, resolveEl));
|
|
77
|
+
}
|
|
78
|
+
return node;
|
|
79
|
+
}
|
|
80
|
+
function buildJuuri(fetchRoot, resolveEl) {
|
|
81
|
+
const childCache = new Map();
|
|
82
|
+
let docP = null;
|
|
83
|
+
const ensure = () => {
|
|
84
|
+
if (!docP)
|
|
85
|
+
docP = fetchRoot();
|
|
86
|
+
return docP;
|
|
87
|
+
};
|
|
88
|
+
return new Proxy({}, {
|
|
89
|
+
get(_, prop) {
|
|
90
|
+
if (typeof prop !== 'string' || prop === 'then')
|
|
91
|
+
return undefined;
|
|
92
|
+
if (!childCache.has(prop))
|
|
93
|
+
childCache.set(prop, ensure().then(doc => [...doc.documentElement.children]
|
|
94
|
+
.filter(c => c.tagName === prop)
|
|
95
|
+
.map(el => buildNode(el, doc, resolveEl))));
|
|
96
|
+
return childCache.get(prop);
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
// ── Fetch infrastructure ─────────────────────────────────────────────────────
|
|
101
|
+
function makeResolveEl(fetchXml, onError) {
|
|
102
|
+
return async function resolveEl(el, rootDoc) {
|
|
103
|
+
const h = getHref(el);
|
|
104
|
+
if (!h)
|
|
105
|
+
return;
|
|
106
|
+
const url = new URL(h, ROOT_URL).href;
|
|
107
|
+
try {
|
|
108
|
+
const text = await fetchXml(url);
|
|
109
|
+
const doc = parseXML(text);
|
|
110
|
+
while (el.lastChild)
|
|
111
|
+
el.removeChild(el.lastChild);
|
|
112
|
+
[...doc.documentElement.children].forEach(c => el.appendChild(rootDoc.importNode(c, true)));
|
|
113
|
+
el.removeAttributeNS(XLINK_NS, 'href');
|
|
114
|
+
el.removeAttribute('xlink:href');
|
|
115
|
+
}
|
|
116
|
+
catch (e) {
|
|
117
|
+
onError?.(e, url);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
// ── Public factory ───────────────────────────────────────────────────────────
|
|
122
|
+
/**
|
|
123
|
+
* Creates a VmBuketti instance for accessing the Finnish state budget API.
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* // Node.js (fetch available globally in Node 18+):
|
|
127
|
+
* const service = createVmBuketti();
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* // Browser with CORS proxy:
|
|
131
|
+
* const service = createVmBuketti({
|
|
132
|
+
* fetchXml: url => fetch('/proxy?url=' + encodeURIComponent(url)).then(r => r.text())
|
|
133
|
+
* });
|
|
134
|
+
*/
|
|
135
|
+
export function createVmBuketti(options) {
|
|
136
|
+
const doFetch = async (url) => {
|
|
137
|
+
const r = await fetch(url);
|
|
138
|
+
if (!r.ok)
|
|
139
|
+
throw new Error(`Server returned ${r.status} for ${url}`);
|
|
140
|
+
return r.text();
|
|
141
|
+
};
|
|
142
|
+
const fetchXml = options?.fetchXml ?? (options?.proxyUrl
|
|
143
|
+
? (url) => doFetch(options.proxyUrl + '?url=' + encodeURIComponent(url))
|
|
144
|
+
: doFetch);
|
|
145
|
+
const resolveEl = makeResolveEl(fetchXml, options?.onError);
|
|
146
|
+
return {
|
|
147
|
+
getJuuri() {
|
|
148
|
+
return buildJuuri(() => fetchXml(ROOT_URL).then(parseXML), resolveEl);
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/schema.d.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
export interface Laskelmaraha {
|
|
2
|
+
tyyppi: 'toteutuma' | 'aiemmin-budjetoitu' | 'aiemmin-budjetoitu-ltae' | 'aiemmin-budjetoitu-ltae1' | 'aiemmin-budjetoitu-ltae2' | 'aiemmin-budjetoitu-ltae3' | 'aiemmin-budjetoitu-ltae4' | 'aiemmin-budjetoitu-ltae5' | 'muutosraha' | 'maararaha' | (string & {});
|
|
3
|
+
vuosi: string | null;
|
|
4
|
+
arvo: number | null;
|
|
5
|
+
}
|
|
6
|
+
export interface Muutos {
|
|
7
|
+
kuvaus: string | null;
|
|
8
|
+
Laskelmaraha: Laskelmaraha[];
|
|
9
|
+
}
|
|
10
|
+
export interface Vertailuluvut {
|
|
11
|
+
Laskelmaraha: Laskelmaraha[];
|
|
12
|
+
}
|
|
13
|
+
export interface Budjettilaskelma {
|
|
14
|
+
Laskelmaraha: Laskelmaraha[];
|
|
15
|
+
Muutos: Muutos[];
|
|
16
|
+
Vertailuluvut: Vertailuluvut[];
|
|
17
|
+
}
|
|
18
|
+
export interface Tulomomentti {
|
|
19
|
+
nimi: string | null;
|
|
20
|
+
numero: string | null;
|
|
21
|
+
momenttityyppi: 'aktiivinen' | 'poistettava' | 'poistettu' | 'siirretty' | 'uusi' | (string & {}) | null;
|
|
22
|
+
infoOsa: string | null;
|
|
23
|
+
Budjettilaskelma: Budjettilaskelma[];
|
|
24
|
+
}
|
|
25
|
+
export interface Tuloluku {
|
|
26
|
+
nimi: string | null;
|
|
27
|
+
numero: string | null;
|
|
28
|
+
lukutyyppi: 'aktiivinen' | 'poistettava' | 'poistettu' | 'uusi' | (string & {}) | null;
|
|
29
|
+
infoOsa: string | null;
|
|
30
|
+
Tulomomentti: Tulomomentti[];
|
|
31
|
+
}
|
|
32
|
+
export interface Osasto {
|
|
33
|
+
numero: string | null;
|
|
34
|
+
nimi: string | null;
|
|
35
|
+
Tuloluku: Tuloluku[];
|
|
36
|
+
}
|
|
37
|
+
export interface Menomomentti {
|
|
38
|
+
maararahalaji: 'arviomaararaha' | 'kiinteamaararaha' | 'siirtomaararaha_2v' | 'siirtomaararaha_3v' | 'siirtomaararaha_5v' | (string & {}) | null;
|
|
39
|
+
nimi: string | null;
|
|
40
|
+
numero: string | null;
|
|
41
|
+
momenttityyppi: 'aktiivinen' | 'poistettava' | 'poistettu' | 'siirretty' | 'uusi' | (string & {}) | null;
|
|
42
|
+
infoOsa: string | null;
|
|
43
|
+
Budjettilaskelma: Budjettilaskelma[];
|
|
44
|
+
}
|
|
45
|
+
export interface Menoluku {
|
|
46
|
+
nimi: string | null;
|
|
47
|
+
numero: string | null;
|
|
48
|
+
lukutyyppi: 'aktiivinen' | 'poistettava' | 'poistettu' | 'uusi' | (string & {}) | null;
|
|
49
|
+
infoOsa: string | null;
|
|
50
|
+
Menomomentti: Menomomentti[];
|
|
51
|
+
}
|
|
52
|
+
export interface Paaluokka {
|
|
53
|
+
numero: string | null;
|
|
54
|
+
nimi: string | null;
|
|
55
|
+
Menoluku: Menoluku[];
|
|
56
|
+
}
|
|
57
|
+
export interface Kanta {
|
|
58
|
+
kanta: 'valtiovarainministerionKanta' | 'hallituksenEsitys' | 'eduskunnanKirjelma' | (string & {}) | null;
|
|
59
|
+
Osasto: Osasto[];
|
|
60
|
+
Paaluokka: Paaluokka[];
|
|
61
|
+
}
|
|
62
|
+
export interface Teos {
|
|
63
|
+
nimi: string | null;
|
|
64
|
+
teostyyppi: 'tae' | 'ltae1' | 'ltae2' | 'ltae3' | 'ltae4' | 'ltae5' | (string & {}) | null;
|
|
65
|
+
Kanta: Kanta[];
|
|
66
|
+
}
|
|
67
|
+
export interface Vuosi {
|
|
68
|
+
vuosi: number;
|
|
69
|
+
Teos: Teos[];
|
|
70
|
+
}
|
|
71
|
+
export interface LazyOsasto {
|
|
72
|
+
numero: string | null;
|
|
73
|
+
nimi: string | null;
|
|
74
|
+
readonly Tuloluku: Promise<Tuloluku[]>;
|
|
75
|
+
}
|
|
76
|
+
export interface LazyPaaluokka {
|
|
77
|
+
numero: string | null;
|
|
78
|
+
nimi: string | null;
|
|
79
|
+
readonly Menoluku: Promise<Menoluku[]>;
|
|
80
|
+
}
|
|
81
|
+
export interface LazyKanta {
|
|
82
|
+
kanta: 'valtiovarainministerionKanta' | 'hallituksenEsitys' | 'eduskunnanKirjelma' | (string & {}) | null;
|
|
83
|
+
readonly Osasto: Promise<LazyOsasto[]>;
|
|
84
|
+
readonly Paaluokka: Promise<LazyPaaluokka[]>;
|
|
85
|
+
}
|
|
86
|
+
export interface LazyTeos {
|
|
87
|
+
nimi: string | null;
|
|
88
|
+
teostyyppi: 'tae' | 'ltae1' | 'ltae2' | 'ltae3' | 'ltae4' | 'ltae5' | (string & {}) | null;
|
|
89
|
+
Kanta: LazyKanta[];
|
|
90
|
+
}
|
|
91
|
+
export interface LazyVuosi {
|
|
92
|
+
vuosi: number;
|
|
93
|
+
Teos: LazyTeos[];
|
|
94
|
+
}
|
|
95
|
+
export interface Juuri {
|
|
96
|
+
readonly Vuosi: Promise<LazyVuosi[]>;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,GAAG,oBAAoB,GAAG,yBAAyB,GAAG,0BAA0B,GAAG,0BAA0B,GAAG,0BAA0B,GAAG,0BAA0B,GAAG,0BAA0B,GAAG,YAAY,GAAG,WAAW,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IACrQ,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,MAAM;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,YAAY,EAAE,YAAY,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,YAAY,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,YAAY,EAAE,CAAC;IAC7B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,aAAa,EAAE,aAAa,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,cAAc,EAAE,YAAY,GAAG,aAAa,GAAG,WAAW,GAAG,WAAW,GAAG,MAAM,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC;IACzG,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,gBAAgB,EAAE,gBAAgB,EAAE,CAAC;CACtC;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,YAAY,GAAG,aAAa,GAAG,WAAW,GAAG,MAAM,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC;IACvF,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,YAAY,EAAE,YAAY,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,MAAM;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,QAAQ,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,aAAa,EAAE,gBAAgB,GAAG,kBAAkB,GAAG,oBAAoB,GAAG,oBAAoB,GAAG,oBAAoB,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC;IACjJ,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,cAAc,EAAE,YAAY,GAAG,aAAa,GAAG,WAAW,GAAG,WAAW,GAAG,MAAM,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC;IACzG,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,gBAAgB,EAAE,gBAAgB,EAAE,CAAC;CACtC;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,YAAY,GAAG,aAAa,GAAG,WAAW,GAAG,MAAM,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC;IACvF,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,YAAY,EAAE,YAAY,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,QAAQ,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,KAAK;IACpB,KAAK,EAAE,8BAA8B,GAAG,mBAAmB,GAAG,oBAAoB,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC;IAC1G,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,SAAS,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE,KAAK,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC;IAC3F,KAAK,EAAE,KAAK,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,KAAK;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,IAAI,EAAE,CAAC;CACd;AAID,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,8BAA8B,GAAG,mBAAmB,GAAG,oBAAoB,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC;IAC1G,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IACvC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;CAC9C;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE,KAAK,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC;IAC3F,KAAK,EAAE,SAAS,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,QAAQ,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;CACtC"}
|
package/dist/schema.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,sCAAsC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jvaarala/vm-buketti",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "TypeScript client for the Finnish state budget open data API (budjetti.vm.fi)",
|
|
5
|
+
"author": "Jesse Väärälä",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/jvaarala/vm-buketti.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/jvaarala/vm-buketti#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/jvaarala/vm-buketti/issues"
|
|
14
|
+
},
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=18"
|
|
17
|
+
},
|
|
18
|
+
"type": "module",
|
|
19
|
+
"main": "./dist/index.js",
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"import": "./dist/index.js",
|
|
24
|
+
"types": "./dist/index.d.ts"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"sideEffects": false,
|
|
28
|
+
"files": ["dist", "src"],
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsc",
|
|
31
|
+
"typecheck": "tsc --noEmit",
|
|
32
|
+
"gen-schema": "node scripts/gen-schema.mjs",
|
|
33
|
+
"prepublishOnly": "npm run build"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"finland",
|
|
37
|
+
"budget",
|
|
38
|
+
"talousarvio",
|
|
39
|
+
"budjetti",
|
|
40
|
+
"vm.fi",
|
|
41
|
+
"opendata",
|
|
42
|
+
"budjetti.vm.fi",
|
|
43
|
+
"api",
|
|
44
|
+
"typescript",
|
|
45
|
+
"client"
|
|
46
|
+
],
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"typescript": "^6.0.2"
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
Laskelmaraha, Muutos, Vertailuluvut, Budjettilaskelma,
|
|
3
|
+
Tulomomentti, Tuloluku, Osasto,
|
|
4
|
+
Menomomentti, Menoluku, Paaluokka,
|
|
5
|
+
Kanta, Teos, Vuosi,
|
|
6
|
+
LazyOsasto, LazyPaaluokka, LazyKanta, LazyTeos, LazyVuosi,
|
|
7
|
+
Juuri,
|
|
8
|
+
} from './schema.js';
|
|
9
|
+
|
|
10
|
+
import type { Juuri } from './schema.js';
|
|
11
|
+
|
|
12
|
+
export interface VmBukettiOptions {
|
|
13
|
+
/**
|
|
14
|
+
* URL of a CORS proxy endpoint that accepts `?url=<encoded>` query params.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* createVmBuketti({ proxyUrl: '/proxy' });
|
|
18
|
+
*/
|
|
19
|
+
proxyUrl?: string;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Custom function for fetching XML content from a URL.
|
|
23
|
+
* Use this to add custom headers, authentication, etc.
|
|
24
|
+
* Takes precedence over `proxyUrl`.
|
|
25
|
+
*
|
|
26
|
+
* Defaults to `fetch(url)` using the global `fetch`.
|
|
27
|
+
*/
|
|
28
|
+
fetchXml?: (url: string) => Promise<string>;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Called when a sub-link fetch fails during lazy loading.
|
|
32
|
+
* If not provided, failures are silently ignored (partial data is returned).
|
|
33
|
+
*/
|
|
34
|
+
onError?: (error: Error, url: string) => void;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface VmBuketti {
|
|
38
|
+
getJuuri(): Juuri;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const ROOT_URL = 'https://budjetti.vm.fi/opendata/opendata-xml.jsp';
|
|
42
|
+
const XLINK_NS = 'http://www.w3.org/1999/xlink';
|
|
43
|
+
|
|
44
|
+
type ResolveEl = (el: Element, rootDoc: Document) => Promise<void>;
|
|
45
|
+
|
|
46
|
+
// ── XML helpers ──────────────────────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
function parseXML(text: string): Document {
|
|
49
|
+
const doc = new DOMParser().parseFromString(text, 'application/xml');
|
|
50
|
+
const err = doc.querySelector('parsererror');
|
|
51
|
+
if (err) throw new Error('XML parse error: ' + err.textContent?.slice(0, 120));
|
|
52
|
+
return doc;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function getHref(el: Element): string | null {
|
|
56
|
+
return el.getAttributeNS(XLINK_NS, 'href') || el.getAttribute('xlink:href') || null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function toCamel(s: string): string {
|
|
60
|
+
return s.replace(/[-:]([a-z])/g, (_, c: string) => c.toUpperCase());
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ── Generic lazy tree builder ────────────────────────────────────────────────
|
|
64
|
+
//
|
|
65
|
+
// The traversal algorithm is the same for every node in the tree:
|
|
66
|
+
// - If the element has xlink:href it must be fetched before its children are
|
|
67
|
+
// readable. A Proxy intercepts child-property accesses and fires the fetch
|
|
68
|
+
// on demand, caching the resulting Promise.
|
|
69
|
+
// - Otherwise, children are already in the DOM and can be wrapped eagerly;
|
|
70
|
+
// but if any child element carries an xlink:href the child array is still
|
|
71
|
+
// wrapped in a Promise so callers use a consistent await pattern.
|
|
72
|
+
//
|
|
73
|
+
// The Juuri root is treated the same way via a document-level Proxy.
|
|
74
|
+
|
|
75
|
+
function readAttrs(el: Element): Record<string, string | number> {
|
|
76
|
+
const out: Record<string, string | number> = {};
|
|
77
|
+
for (const attr of el.attributes) {
|
|
78
|
+
if (attr.namespaceURI) continue; // skip xlink:href, xmlns:* and any other namespace attrs
|
|
79
|
+
const key = toCamel(attr.name);
|
|
80
|
+
out[key] = /^-?\d+(\.\d+)?$/.test(attr.value) ? Number(attr.value) : attr.value;
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function buildNode(el: Element, rootDoc: Document, resolveEl: ResolveEl): unknown {
|
|
86
|
+
const attrs = readAttrs(el);
|
|
87
|
+
|
|
88
|
+
if (getHref(el)) {
|
|
89
|
+
// xlink element: children unknown until fetched — use a Proxy
|
|
90
|
+
let resolved: Promise<void> | null = null;
|
|
91
|
+
const ensure = (): Promise<void> => {
|
|
92
|
+
if (!resolved) resolved = resolveEl(el, rootDoc);
|
|
93
|
+
return resolved;
|
|
94
|
+
};
|
|
95
|
+
const childCache = new Map<string, Promise<unknown[]>>();
|
|
96
|
+
|
|
97
|
+
return new Proxy(attrs as Record<string, unknown>, {
|
|
98
|
+
get(target, prop) {
|
|
99
|
+
if (typeof prop !== 'string') return undefined;
|
|
100
|
+
if (prop === 'then') return undefined;
|
|
101
|
+
if (Object.prototype.hasOwnProperty.call(target, prop)) return target[prop];
|
|
102
|
+
if (!childCache.has(prop)) {
|
|
103
|
+
childCache.set(prop, ensure().then(() => {
|
|
104
|
+
const children = [...el.children].filter(c => c.tagName === prop);
|
|
105
|
+
return children.map(c => buildNode(c, rootDoc, resolveEl));
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
return childCache.get(prop);
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Non-xlink element: children are in the DOM now
|
|
114
|
+
const node: Record<string, unknown> = { ...attrs };
|
|
115
|
+
|
|
116
|
+
const groups = new Map<string, Element[]>();
|
|
117
|
+
for (const child of el.children) {
|
|
118
|
+
if (!groups.has(child.tagName)) groups.set(child.tagName, []);
|
|
119
|
+
groups.get(child.tagName)!.push(child);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
for (const [tag, children] of groups) {
|
|
123
|
+
node[tag] = children.map(c => buildNode(c, rootDoc, resolveEl));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return node;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function buildJuuri(fetchRoot: () => Promise<Document>, resolveEl: ResolveEl): Juuri {
|
|
130
|
+
const childCache = new Map<string, Promise<unknown[]>>();
|
|
131
|
+
let docP: Promise<Document> | null = null;
|
|
132
|
+
const ensure = (): Promise<Document> => {
|
|
133
|
+
if (!docP) docP = fetchRoot();
|
|
134
|
+
return docP;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
return new Proxy({} as unknown as Juuri, {
|
|
138
|
+
get(_, prop) {
|
|
139
|
+
if (typeof prop !== 'string' || prop === 'then') return undefined;
|
|
140
|
+
if (!childCache.has(prop))
|
|
141
|
+
childCache.set(prop, ensure().then(doc =>
|
|
142
|
+
[...doc.documentElement.children]
|
|
143
|
+
.filter(c => c.tagName === prop)
|
|
144
|
+
.map(el => buildNode(el, doc, resolveEl))
|
|
145
|
+
));
|
|
146
|
+
return childCache.get(prop);
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ── Fetch infrastructure ─────────────────────────────────────────────────────
|
|
152
|
+
|
|
153
|
+
function makeResolveEl(
|
|
154
|
+
fetchXml: (url: string) => Promise<string>,
|
|
155
|
+
onError?: (error: Error, url: string) => void,
|
|
156
|
+
): ResolveEl {
|
|
157
|
+
return async function resolveEl(el: Element, rootDoc: Document): Promise<void> {
|
|
158
|
+
const h = getHref(el);
|
|
159
|
+
if (!h) return;
|
|
160
|
+
const url = new URL(h, ROOT_URL).href;
|
|
161
|
+
try {
|
|
162
|
+
const text = await fetchXml(url);
|
|
163
|
+
const doc = parseXML(text);
|
|
164
|
+
while (el.lastChild) el.removeChild(el.lastChild);
|
|
165
|
+
[...doc.documentElement.children].forEach(c =>
|
|
166
|
+
el.appendChild(rootDoc.importNode(c, true))
|
|
167
|
+
);
|
|
168
|
+
el.removeAttributeNS(XLINK_NS, 'href');
|
|
169
|
+
el.removeAttribute('xlink:href');
|
|
170
|
+
} catch (e) {
|
|
171
|
+
onError?.(e as Error, url);
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ── Public factory ───────────────────────────────────────────────────────────
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Creates a VmBuketti instance for accessing the Finnish state budget API.
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* // Node.js (fetch available globally in Node 18+):
|
|
183
|
+
* const service = createVmBuketti();
|
|
184
|
+
*
|
|
185
|
+
* @example
|
|
186
|
+
* // Browser with CORS proxy:
|
|
187
|
+
* const service = createVmBuketti({
|
|
188
|
+
* fetchXml: url => fetch('/proxy?url=' + encodeURIComponent(url)).then(r => r.text())
|
|
189
|
+
* });
|
|
190
|
+
*/
|
|
191
|
+
export function createVmBuketti(options?: VmBukettiOptions): VmBuketti {
|
|
192
|
+
const doFetch = async (url: string) => {
|
|
193
|
+
const r = await fetch(url);
|
|
194
|
+
if (!r.ok) throw new Error(`Server returned ${r.status} for ${url}`);
|
|
195
|
+
return r.text();
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const fetchXml = options?.fetchXml ?? (
|
|
199
|
+
options?.proxyUrl
|
|
200
|
+
? (url: string) => doFetch(options.proxyUrl! + '?url=' + encodeURIComponent(url))
|
|
201
|
+
: doFetch
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
const resolveEl = makeResolveEl(fetchXml, options?.onError);
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
getJuuri(): Juuri {
|
|
208
|
+
return buildJuuri(
|
|
209
|
+
() => fetchXml(ROOT_URL).then(parseXML),
|
|
210
|
+
resolveEl,
|
|
211
|
+
);
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
}
|
package/src/schema.ts
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// Auto-generated from https://budjetti.vm.fi/indox/opendata/kl_buketti.xsd
|
|
2
|
+
// Regenerate with: npm run gen-schema
|
|
3
|
+
|
|
4
|
+
export interface Laskelmaraha {
|
|
5
|
+
tyyppi: 'toteutuma' | 'aiemmin-budjetoitu' | 'aiemmin-budjetoitu-ltae' | 'aiemmin-budjetoitu-ltae1' | 'aiemmin-budjetoitu-ltae2' | 'aiemmin-budjetoitu-ltae3' | 'aiemmin-budjetoitu-ltae4' | 'aiemmin-budjetoitu-ltae5' | 'muutosraha' | 'maararaha' | (string & {});
|
|
6
|
+
vuosi: string | null;
|
|
7
|
+
arvo: number | null;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface Muutos {
|
|
11
|
+
kuvaus: string | null;
|
|
12
|
+
Laskelmaraha: Laskelmaraha[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface Vertailuluvut {
|
|
16
|
+
Laskelmaraha: Laskelmaraha[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface Budjettilaskelma {
|
|
20
|
+
Laskelmaraha: Laskelmaraha[];
|
|
21
|
+
Muutos: Muutos[];
|
|
22
|
+
Vertailuluvut: Vertailuluvut[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface Tulomomentti {
|
|
26
|
+
nimi: string | null;
|
|
27
|
+
numero: string | null;
|
|
28
|
+
momenttityyppi: 'aktiivinen' | 'poistettava' | 'poistettu' | 'siirretty' | 'uusi' | (string & {}) | null;
|
|
29
|
+
infoOsa: string | null;
|
|
30
|
+
Budjettilaskelma: Budjettilaskelma[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface Tuloluku {
|
|
34
|
+
nimi: string | null;
|
|
35
|
+
numero: string | null;
|
|
36
|
+
lukutyyppi: 'aktiivinen' | 'poistettava' | 'poistettu' | 'uusi' | (string & {}) | null;
|
|
37
|
+
infoOsa: string | null;
|
|
38
|
+
Tulomomentti: Tulomomentti[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface Osasto {
|
|
42
|
+
numero: string | null;
|
|
43
|
+
nimi: string | null;
|
|
44
|
+
Tuloluku: Tuloluku[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface Menomomentti {
|
|
48
|
+
maararahalaji: 'arviomaararaha' | 'kiinteamaararaha' | 'siirtomaararaha_2v' | 'siirtomaararaha_3v' | 'siirtomaararaha_5v' | (string & {}) | null;
|
|
49
|
+
nimi: string | null;
|
|
50
|
+
numero: string | null;
|
|
51
|
+
momenttityyppi: 'aktiivinen' | 'poistettava' | 'poistettu' | 'siirretty' | 'uusi' | (string & {}) | null;
|
|
52
|
+
infoOsa: string | null;
|
|
53
|
+
Budjettilaskelma: Budjettilaskelma[];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface Menoluku {
|
|
57
|
+
nimi: string | null;
|
|
58
|
+
numero: string | null;
|
|
59
|
+
lukutyyppi: 'aktiivinen' | 'poistettava' | 'poistettu' | 'uusi' | (string & {}) | null;
|
|
60
|
+
infoOsa: string | null;
|
|
61
|
+
Menomomentti: Menomomentti[];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface Paaluokka {
|
|
65
|
+
numero: string | null;
|
|
66
|
+
nimi: string | null;
|
|
67
|
+
Menoluku: Menoluku[];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface Kanta {
|
|
71
|
+
kanta: 'valtiovarainministerionKanta' | 'hallituksenEsitys' | 'eduskunnanKirjelma' | (string & {}) | null;
|
|
72
|
+
Osasto: Osasto[];
|
|
73
|
+
Paaluokka: Paaluokka[];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface Teos {
|
|
77
|
+
nimi: string | null;
|
|
78
|
+
teostyyppi: 'tae' | 'ltae1' | 'ltae2' | 'ltae3' | 'ltae4' | 'ltae5' | (string & {}) | null;
|
|
79
|
+
Kanta: Kanta[];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface Vuosi {
|
|
83
|
+
vuosi: number;
|
|
84
|
+
Teos: Teos[];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ── Lazy types (for elements with xlink:href and their ancestors) ────────────
|
|
88
|
+
|
|
89
|
+
export interface LazyOsasto {
|
|
90
|
+
numero: string | null;
|
|
91
|
+
nimi: string | null;
|
|
92
|
+
readonly Tuloluku: Promise<Tuloluku[]>;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface LazyPaaluokka {
|
|
96
|
+
numero: string | null;
|
|
97
|
+
nimi: string | null;
|
|
98
|
+
readonly Menoluku: Promise<Menoluku[]>;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface LazyKanta {
|
|
102
|
+
kanta: 'valtiovarainministerionKanta' | 'hallituksenEsitys' | 'eduskunnanKirjelma' | (string & {}) | null;
|
|
103
|
+
readonly Osasto: Promise<LazyOsasto[]>;
|
|
104
|
+
readonly Paaluokka: Promise<LazyPaaluokka[]>;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface LazyTeos {
|
|
108
|
+
nimi: string | null;
|
|
109
|
+
teostyyppi: 'tae' | 'ltae1' | 'ltae2' | 'ltae3' | 'ltae4' | 'ltae5' | (string & {}) | null;
|
|
110
|
+
Kanta: LazyKanta[];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface LazyVuosi {
|
|
114
|
+
vuosi: number;
|
|
115
|
+
Teos: LazyTeos[];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface Juuri {
|
|
119
|
+
readonly Vuosi: Promise<LazyVuosi[]>;
|
|
120
|
+
}
|