@cyanheads/eurostat-mcp-server 0.3.0 → 0.5.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/CLAUDE.md +1 -1
- package/Dockerfile +16 -3
- package/README.md +50 -7
- package/dist/config/server-config.d.ts +2 -0
- package/dist/config/server-config.d.ts.map +1 -1
- package/dist/config/server-config.js +14 -0
- package/dist/config/server-config.js.map +1 -1
- package/dist/index.js +28 -2
- package/dist/index.js.map +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-dataframe-describe.tool.d.ts +40 -0
- package/dist/mcp-server/tools/definitions/eurostat-dataframe-describe.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/eurostat-dataframe-describe.tool.js +113 -0
- package/dist/mcp-server/tools/definitions/eurostat-dataframe-describe.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/eurostat-dataframe-query.tool.d.ts +34 -0
- package/dist/mcp-server/tools/definitions/eurostat-dataframe-query.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/eurostat-dataframe-query.tool.js +98 -0
- package/dist/mcp-server/tools/definitions/eurostat-dataframe-query.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/eurostat-download-dataset.tool.d.ts +93 -0
- package/dist/mcp-server/tools/definitions/eurostat-download-dataset.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/eurostat-download-dataset.tool.js +362 -0
- package/dist/mcp-server/tools/definitions/eurostat-download-dataset.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/eurostat-get-dataset-info.tool.js +2 -2
- package/dist/mcp-server/tools/definitions/eurostat-get-dataset-info.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-get-dimension-values.tool.js +2 -2
- package/dist/mcp-server/tools/definitions/eurostat-get-dimension-values.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-query-dataset.tool.d.ts +9 -0
- package/dist/mcp-server/tools/definitions/eurostat-query-dataset.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-query-dataset.tool.js +68 -10
- package/dist/mcp-server/tools/definitions/eurostat-query-dataset.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-search-datasets.tool.js +3 -3
- package/dist/mcp-server/tools/definitions/eurostat-search-datasets.tool.js.map +1 -1
- package/dist/services/canvas-accessor.d.ts +31 -0
- package/dist/services/canvas-accessor.d.ts.map +1 -0
- package/dist/services/canvas-accessor.js +64 -0
- package/dist/services/canvas-accessor.js.map +1 -0
- package/dist/services/eurostat-bulk/eurostat-bulk-service.d.ts +89 -0
- package/dist/services/eurostat-bulk/eurostat-bulk-service.d.ts.map +1 -0
- package/dist/services/eurostat-bulk/eurostat-bulk-service.js +501 -0
- package/dist/services/eurostat-bulk/eurostat-bulk-service.js.map +1 -0
- package/dist/services/eurostat-bulk/types.d.ts +98 -0
- package/dist/services/eurostat-bulk/types.d.ts.map +1 -0
- package/dist/services/eurostat-bulk/types.js +82 -0
- package/dist/services/eurostat-bulk/types.js.map +1 -0
- package/dist/services/eurostat-data/eurostat-data-service.d.ts +63 -8
- package/dist/services/eurostat-data/eurostat-data-service.d.ts.map +1 -1
- package/dist/services/eurostat-data/eurostat-data-service.js +107 -13
- package/dist/services/eurostat-data/eurostat-data-service.js.map +1 -1
- package/dist/services/eurostat-data/types.d.ts +30 -0
- package/dist/services/eurostat-data/types.d.ts.map +1 -1
- package/dist/services/eurostat-data/types.js +4 -0
- package/dist/services/eurostat-data/types.js.map +1 -1
- package/package.json +2 -1
- package/server.json +85 -3
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Eurostat Bulk Service — streaming client for the SDMX 2.1 TSV
|
|
3
|
+
* dissemination endpoint. Builds the positional dimension key, sniffs gzip off
|
|
4
|
+
* the stream, enforces a byte budget mid-transfer, classifies XML SOAP faults
|
|
5
|
+
* and the asynchronous queue envelope, and expands the wide TSV layout into
|
|
6
|
+
* one row per observation.
|
|
7
|
+
* @module services/eurostat-bulk/eurostat-bulk-service
|
|
8
|
+
*/
|
|
9
|
+
import { notFound, serviceUnavailable, validationError, } from '@cyanheads/mcp-ts-core/errors';
|
|
10
|
+
import { fetchWithTimeout } from '@cyanheads/mcp-ts-core/utils';
|
|
11
|
+
import { getServerConfig } from '../../config/server-config.js';
|
|
12
|
+
import { CONF_STATUS_COLUMN, CONF_STATUS_LABEL_COLUMN, CONF_STATUS_LABELS, OBS_FLAG_COLUMN, OBS_FLAG_LABEL_COLUMN, OBS_FLAG_LABELS, OBS_VALUE_COLUMN, TIME_COLUMN, } from './types.js';
|
|
13
|
+
// Context satisfies the runtime contract of RequestContext but lacks the index signature
|
|
14
|
+
// required by fetchWithTimeout.
|
|
15
|
+
const asReqCtx = (ctx) => ctx;
|
|
16
|
+
/**
|
|
17
|
+
* Decoded characters buffered before an XML body is classified.
|
|
18
|
+
*
|
|
19
|
+
* Both non-data shapes are small — the largest SOAP fault observed is under 400
|
|
20
|
+
* bytes and the queue envelope under 350 — so this only has to hold one of them.
|
|
21
|
+
* It deliberately does **not** bound the search for the TSV header's line break:
|
|
22
|
+
* that line carries one field per period and runs to 166 KB on a daily series.
|
|
23
|
+
*/
|
|
24
|
+
const XML_PEEK_CHARS = 8_192;
|
|
25
|
+
/** Separator between the observation flag and the confidentiality flag inside a TSV cell. */
|
|
26
|
+
const CONF_SEPARATOR = '@';
|
|
27
|
+
/** Eurostat's marker for an observation whose value is not available. */
|
|
28
|
+
const MISSING_VALUE = ':';
|
|
29
|
+
/** The header line's first field ends with this literal before the period columns begin. */
|
|
30
|
+
const TIME_HEADER_SUFFIX = '\\TIME_PERIOD';
|
|
31
|
+
/** Minimal XML entity decoding — the five predefined entities SOAP fault strings use. */
|
|
32
|
+
function decodeEntities(text) {
|
|
33
|
+
return text
|
|
34
|
+
.replaceAll('<', '<')
|
|
35
|
+
.replaceAll('>', '>')
|
|
36
|
+
.replaceAll('"', '"')
|
|
37
|
+
.replaceAll(''', "'")
|
|
38
|
+
.replaceAll('&', '&');
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Translate an XML body from the SDMX endpoint into the declared error contract.
|
|
42
|
+
*
|
|
43
|
+
* Two unrelated shapes arrive as XML and neither is data. A `S:Fault` envelope
|
|
44
|
+
* accompanies a 4xx and names the problem in a numeric `faultcode`. The
|
|
45
|
+
* `syncResponse` envelope arrives on **HTTP 200** carrying a queue ticket
|
|
46
|
+
* instead of the dataset — parsed as TSV it yields a header row of XML, so it is
|
|
47
|
+
* detected here rather than downstream.
|
|
48
|
+
*
|
|
49
|
+
* Always throws.
|
|
50
|
+
*/
|
|
51
|
+
export function classifyXmlBody(xml, datasetCode) {
|
|
52
|
+
if (xml.includes('syncResponse') || xml.includes('<status>SUBMITTED</status>')) {
|
|
53
|
+
throw serviceUnavailable(`Eurostat queued this extraction instead of returning data: the SDMX endpoint answered with an asynchronous "SUBMITTED" ticket for "${datasetCode}". The queued result is not retrievable through this server. Narrow the request with dimension filters or a period range so Eurostat serves it synchronously.`, { reason: 'async_queued', datasetCode, retryable: false });
|
|
54
|
+
}
|
|
55
|
+
const code = /<faultcode>\s*(\d+)\s*<\/faultcode>/.exec(xml)?.[1];
|
|
56
|
+
const detail = decodeEntities(/<faultstring>([\s\S]*?)<\/faultstring>/.exec(xml)?.[1]?.trim() ?? xml.slice(0, 300));
|
|
57
|
+
if (code === '100') {
|
|
58
|
+
throw notFound(`Dataset "${datasetCode}" is not available for dissemination. Eurostat fault ${code}: ${detail}`, { reason: 'not_found', datasetCode, faultcode: code });
|
|
59
|
+
}
|
|
60
|
+
if (code === '140') {
|
|
61
|
+
throw validationError(`Eurostat rejected the dimension key: the number of filter positions did not match the dataset's dimensions. Eurostat fault ${code}: ${detail}`, { reason: 'filter_arity', datasetCode, faultcode: code });
|
|
62
|
+
}
|
|
63
|
+
if (code === '150') {
|
|
64
|
+
throw validationError(`Eurostat rejected a filter value. This fault also covers a period range outside the dataset's coverage. Eurostat fault ${code}: ${detail}`, { reason: 'invalid_dimension', datasetCode, faultcode: code });
|
|
65
|
+
}
|
|
66
|
+
throw serviceUnavailable(`Eurostat SDMX endpoint returned a fault for "${datasetCode}"${code ? ` (faultcode ${code})` : ''}: ${detail}`, { reason: 'upstream_fault', datasetCode, ...(code && { faultcode: code }) });
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Split one wide-TSV cell into its value and flags.
|
|
70
|
+
*
|
|
71
|
+
* The grammar, read off live responses, is `<value>` `SPACE` `<flags>`, where
|
|
72
|
+
* flags is `<obs_flag>` optionally followed by `@<conf_status>` and either part
|
|
73
|
+
* may be empty: `429634.3 `, `3853937.0 p`, `: `, `: @C`. A cell that is empty
|
|
74
|
+
* on both sides of the split is not an observation at all and yields
|
|
75
|
+
* `undefined` — distinct from `: `, which is an observation Eurostat reports as
|
|
76
|
+
* unavailable.
|
|
77
|
+
*/
|
|
78
|
+
export function parseCell(cell) {
|
|
79
|
+
const sep = cell.indexOf(' ');
|
|
80
|
+
const rawValue = (sep === -1 ? cell : cell.slice(0, sep)).trim();
|
|
81
|
+
const rawFlags = (sep === -1 ? '' : cell.slice(sep + 1)).trim();
|
|
82
|
+
if (rawValue === '' && rawFlags === '')
|
|
83
|
+
return;
|
|
84
|
+
const atIdx = rawFlags.indexOf(CONF_SEPARATOR);
|
|
85
|
+
const flag = (atIdx === -1 ? rawFlags : rawFlags.slice(0, atIdx)) || null;
|
|
86
|
+
const conf = (atIdx === -1 ? '' : rawFlags.slice(atIdx + 1)) || null;
|
|
87
|
+
if (rawValue === '' || rawValue === MISSING_VALUE)
|
|
88
|
+
return { conf, flag, value: null };
|
|
89
|
+
const num = Number(rawValue);
|
|
90
|
+
return { conf, flag, value: Number.isFinite(num) ? num : null };
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Parse the header line of a wide TSV body.
|
|
94
|
+
*
|
|
95
|
+
* The first tab-separated field is the comma-joined dimension list with
|
|
96
|
+
* `\TIME_PERIOD` appended to the last name; the remaining fields are the period
|
|
97
|
+
* codes, each padded with a trailing space Eurostat does not strip.
|
|
98
|
+
*/
|
|
99
|
+
export function parseHeader(line) {
|
|
100
|
+
const fields = line.split('\t');
|
|
101
|
+
const keyField = (fields[0] ?? '').trim();
|
|
102
|
+
const dimensionField = keyField.endsWith(TIME_HEADER_SUFFIX)
|
|
103
|
+
? keyField.slice(0, -TIME_HEADER_SUFFIX.length)
|
|
104
|
+
: keyField;
|
|
105
|
+
const dimensions = dimensionField
|
|
106
|
+
.split(',')
|
|
107
|
+
.map((d) => d.trim())
|
|
108
|
+
.filter((d) => d !== '');
|
|
109
|
+
const periods = fields.slice(1).map((p) => p.trim());
|
|
110
|
+
if (dimensions.length === 0 || periods.length === 0) {
|
|
111
|
+
throw serviceUnavailable(`Eurostat returned a TSV body whose header names no ${dimensions.length === 0 ? 'dimensions' : 'periods'}. Header: ${line.slice(0, 200)}`, { reason: 'upstream_fault' });
|
|
112
|
+
}
|
|
113
|
+
return { dimensions, periods };
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Build the positional dimension key the SDMX path takes.
|
|
117
|
+
*
|
|
118
|
+
* The key is a dot-separated segment carrying one position per dimension **in
|
|
119
|
+
* dataset order excluding time**; empty means wildcard and `+` means OR. The
|
|
120
|
+
* count must match exactly — a key with one position too few or too many is
|
|
121
|
+
* rejected upstream with faultcode 140 — so positions are emitted for every
|
|
122
|
+
* dimension in `dimensionOrder`, filtered or not, and never only for the
|
|
123
|
+
* dimensions the caller named.
|
|
124
|
+
*
|
|
125
|
+
* Returns `undefined` when no filter applies. An all-wildcard key is accepted
|
|
126
|
+
* by Eurostat, but omitting the segment asks the same question without staking
|
|
127
|
+
* the request on this server's copy of the dimension order being current.
|
|
128
|
+
*/
|
|
129
|
+
export function buildKeyPath(dimensionOrder, filters) {
|
|
130
|
+
const applied = Object.entries(filters).filter(([, values]) => values.length > 0);
|
|
131
|
+
if (applied.length === 0)
|
|
132
|
+
return;
|
|
133
|
+
const unknown = applied.map(([dim]) => dim).filter((dim) => !dimensionOrder.includes(dim));
|
|
134
|
+
if (unknown.length > 0) {
|
|
135
|
+
throw validationError(`Filter names ${unknown.map((d) => `"${d}"`).join(', ')}, which ${unknown.length === 1 ? 'is not a dimension' : 'are not dimensions'} of this dataset. Filterable dimensions, in key order: ${dimensionOrder.join(', ')}. The time dimension is filtered with since_period/until_period instead.`, { reason: 'invalid_dimension', unknownDimensions: unknown, dimensionOrder });
|
|
136
|
+
}
|
|
137
|
+
return dimensionOrder.map((dim) => (filters[dim] ?? []).join('+')).join('.');
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Canvas column schema for {@link BulkRow}, declared rather than sniffed.
|
|
141
|
+
*
|
|
142
|
+
* Inference reads only the leading rows, which types an all-integer measure
|
|
143
|
+
* prefix as `BIGINT` — serialized back out as a string — and types an
|
|
144
|
+
* all-missing prefix from nulls alone. Every column is nullable: a measure is
|
|
145
|
+
* routinely unavailable, and flags are the exception rather than the rule.
|
|
146
|
+
*/
|
|
147
|
+
export function bulkRowSchema(dimensions) {
|
|
148
|
+
return [
|
|
149
|
+
...dimensions.map((dim) => ({ name: dim, type: 'VARCHAR', nullable: true })),
|
|
150
|
+
{ name: TIME_COLUMN, type: 'VARCHAR', nullable: true },
|
|
151
|
+
{ name: OBS_VALUE_COLUMN, type: 'DOUBLE', nullable: true },
|
|
152
|
+
{ name: OBS_FLAG_COLUMN, type: 'VARCHAR', nullable: true },
|
|
153
|
+
{ name: OBS_FLAG_LABEL_COLUMN, type: 'VARCHAR', nullable: true },
|
|
154
|
+
{ name: CONF_STATUS_COLUMN, type: 'VARCHAR', nullable: true },
|
|
155
|
+
{ name: CONF_STATUS_LABEL_COLUMN, type: 'VARCHAR', nullable: true },
|
|
156
|
+
];
|
|
157
|
+
}
|
|
158
|
+
export class EurostatBulkService {
|
|
159
|
+
// config and storage accepted to match the standard service init pattern;
|
|
160
|
+
// this service uses only the Eurostat public API and per-request config.
|
|
161
|
+
// biome-ignore lint/complexity/noUselessConstructor: standard init pattern
|
|
162
|
+
constructor(_config, _storage) { }
|
|
163
|
+
buildUrl(datasetCode, keyPath, sinceP, untilP) {
|
|
164
|
+
const { baseUrl } = getServerConfig();
|
|
165
|
+
const path = keyPath
|
|
166
|
+
? `${encodeURIComponent(datasetCode)}/${encodeURIComponent(keyPath)}`
|
|
167
|
+
: encodeURIComponent(datasetCode);
|
|
168
|
+
const url = new URL(`${baseUrl}/sdmx/2.1/data/${path}`);
|
|
169
|
+
url.searchParams.set('format', 'TSV');
|
|
170
|
+
if (sinceP)
|
|
171
|
+
url.searchParams.set('startPeriod', sinceP);
|
|
172
|
+
if (untilP)
|
|
173
|
+
url.searchParams.set('endPeriod', untilP);
|
|
174
|
+
return url;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Start a bulk download: issue the request, classify the response, parse the
|
|
178
|
+
* header, and hand back a generator over the body.
|
|
179
|
+
*
|
|
180
|
+
* The request is made once with no retry. A bulk body streams for up to
|
|
181
|
+
* `EUROSTAT_BULK_TIMEOUT_MS`, so re-running it on a transient failure costs
|
|
182
|
+
* the caller another full transfer for a request that is already the most
|
|
183
|
+
* expensive one this server makes; the failure is surfaced instead.
|
|
184
|
+
*/
|
|
185
|
+
async startDownload(datasetCode, dimensionOrder, filters, sinceP, untilP, ctx) {
|
|
186
|
+
const { bulkTimeoutMs, bulkMaxBytes } = getServerConfig();
|
|
187
|
+
const keyPath = buildKeyPath(dimensionOrder, filters);
|
|
188
|
+
const url = this.buildUrl(datasetCode, keyPath, sinceP, untilP);
|
|
189
|
+
ctx.log.info('Starting SDMX bulk download', {
|
|
190
|
+
datasetCode,
|
|
191
|
+
keyPath,
|
|
192
|
+
sinceP,
|
|
193
|
+
untilP,
|
|
194
|
+
maxBytes: bulkMaxBytes,
|
|
195
|
+
});
|
|
196
|
+
let response;
|
|
197
|
+
try {
|
|
198
|
+
response = await fetchWithTimeout(url.toString(), bulkTimeoutMs, asReqCtx(ctx), {
|
|
199
|
+
signal: ctx.signal,
|
|
200
|
+
// 400 (bad filter value or arity) and 404 (unknown dataset) are modeled outcomes
|
|
201
|
+
// reclassified below into the declared error contract, not service failures.
|
|
202
|
+
expectedStatuses: [400, 404],
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
catch (err) {
|
|
206
|
+
// fetchWithTimeout throws for any non-2xx before the caller sees the body, so the
|
|
207
|
+
// SOAP fault is only reachable through the captured excerpt on err.data. Faults run
|
|
208
|
+
// well under the framework's 500-byte cap, so the excerpt carries the whole envelope.
|
|
209
|
+
const body = err.data;
|
|
210
|
+
if (typeof body?.body === 'string' && body.body.includes('<')) {
|
|
211
|
+
classifyXmlBody(body.body, datasetCode);
|
|
212
|
+
}
|
|
213
|
+
throw err;
|
|
214
|
+
}
|
|
215
|
+
if (!response.body) {
|
|
216
|
+
throw serviceUnavailable(`Eurostat SDMX endpoint returned an empty body for "${datasetCode}".`, { reason: 'upstream_fault', datasetCode });
|
|
217
|
+
}
|
|
218
|
+
const stats = {
|
|
219
|
+
bytesRead: 0,
|
|
220
|
+
budgetExceeded: false,
|
|
221
|
+
compressed: false,
|
|
222
|
+
rowCount: 0,
|
|
223
|
+
missingCount: 0,
|
|
224
|
+
periodsSeen: [],
|
|
225
|
+
};
|
|
226
|
+
const decoded = await decodeBody(response.body, stats, bulkMaxBytes);
|
|
227
|
+
const { header, remainder, iterator } = await readHeader(decoded, datasetCode);
|
|
228
|
+
return {
|
|
229
|
+
header,
|
|
230
|
+
stats,
|
|
231
|
+
url: url.toString(),
|
|
232
|
+
rows: () => expandRows(iterator, remainder, header, stats),
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Wrap the response body in a byte-budgeted, gzip-aware text stream.
|
|
238
|
+
*
|
|
239
|
+
* Eurostat compresses large bodies **without** a `Content-Encoding` header — the
|
|
240
|
+
* only header-level tell is a `.tsv.gz` filename on `Content-Disposition`, and
|
|
241
|
+
* that is not what is trusted here. The first two bytes are read off the stream
|
|
242
|
+
* and checked for the gzip magic number, so the decision is made on what
|
|
243
|
+
* actually arrived. The switch is not monotonic in dataset size, so it cannot
|
|
244
|
+
* be predicted from the request either.
|
|
245
|
+
*
|
|
246
|
+
* The budget counts bytes *after* decompression. That is the uniform measure
|
|
247
|
+
* across both encodings, and since a gzip body is never larger than what it
|
|
248
|
+
* expands to, bounding the decoded size bounds the transfer as well. Hitting it
|
|
249
|
+
* cancels the underlying reader rather than draining the rest — the body is
|
|
250
|
+
* chunked with no `Content-Length`, so a budget checked after the fact would
|
|
251
|
+
* have already paid for the whole transfer.
|
|
252
|
+
*/
|
|
253
|
+
async function decodeBody(body, stats, maxBytes) {
|
|
254
|
+
const reader = body.getReader();
|
|
255
|
+
// Pull until at least the two magic bytes are in hand; a chunked body can deliver
|
|
256
|
+
// fewer on the first read, and an empty body delivers none.
|
|
257
|
+
const prefix = [];
|
|
258
|
+
let prefixBytes = 0;
|
|
259
|
+
while (prefixBytes < 2) {
|
|
260
|
+
const { done, value } = await reader.read();
|
|
261
|
+
if (done)
|
|
262
|
+
break;
|
|
263
|
+
if (value && value.length > 0) {
|
|
264
|
+
prefix.push(value);
|
|
265
|
+
prefixBytes += value.length;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
const head = concatChunks(prefix, prefixBytes);
|
|
269
|
+
stats.compressed = head.length >= 2 && head[0] === 0x1f && head[1] === 0x8b;
|
|
270
|
+
const replayed = new ReadableStream({
|
|
271
|
+
start(controller) {
|
|
272
|
+
if (head.length > 0)
|
|
273
|
+
controller.enqueue(head);
|
|
274
|
+
},
|
|
275
|
+
async pull(controller) {
|
|
276
|
+
const { done, value } = await reader.read();
|
|
277
|
+
if (done)
|
|
278
|
+
controller.close();
|
|
279
|
+
else
|
|
280
|
+
controller.enqueue(value);
|
|
281
|
+
},
|
|
282
|
+
cancel(reason) {
|
|
283
|
+
return reader.cancel(reason);
|
|
284
|
+
},
|
|
285
|
+
});
|
|
286
|
+
/**
|
|
287
|
+
* `DecompressionStream`'s writable side is typed `BufferSource`, wider than the
|
|
288
|
+
* `Uint8Array` chunks this pipeline carries, so `pipeThrough` will not accept it
|
|
289
|
+
* directly. The cast narrows the transform to the stream types actually in play
|
|
290
|
+
* rather than widening the pipeline to `BufferSource`.
|
|
291
|
+
*/
|
|
292
|
+
const byteStream = stats.compressed
|
|
293
|
+
? replayed.pipeThrough(new DecompressionStream('gzip'))
|
|
294
|
+
: replayed;
|
|
295
|
+
return budgetedText(byteStream, stats, maxBytes);
|
|
296
|
+
}
|
|
297
|
+
function concatChunks(chunks, total) {
|
|
298
|
+
if (chunks.length === 1)
|
|
299
|
+
return chunks[0];
|
|
300
|
+
const out = new Uint8Array(total);
|
|
301
|
+
let offset = 0;
|
|
302
|
+
for (const chunk of chunks) {
|
|
303
|
+
out.set(chunk, offset);
|
|
304
|
+
offset += chunk.length;
|
|
305
|
+
}
|
|
306
|
+
return out;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Decode a byte stream to text, stopping the moment the byte budget is spent.
|
|
310
|
+
*
|
|
311
|
+
* The generator ends cleanly on overspend rather than throwing: the caller has
|
|
312
|
+
* already paid for everything downloaded so far, and discarding it would leave
|
|
313
|
+
* the agent with an error where it could have had a prefix of the dataset plus
|
|
314
|
+
* `budgetExceeded` telling it exactly what happened.
|
|
315
|
+
*
|
|
316
|
+
* Every exit cancels the reader, so nothing further is transferred: overspend,
|
|
317
|
+
* a normal end, and abandonment alike. Abandonment is the case that needs it —
|
|
318
|
+
* a staging failure or an abort part-way through the canvas append runs the
|
|
319
|
+
* `finally`, and releasing the lock alone would leave the response body
|
|
320
|
+
* undrained and its connection held. Cancelling a stream that already ended is
|
|
321
|
+
* a no-op.
|
|
322
|
+
*/
|
|
323
|
+
async function* budgetedText(stream, stats, maxBytes) {
|
|
324
|
+
const reader = stream.getReader();
|
|
325
|
+
const decoder = new TextDecoder('utf-8');
|
|
326
|
+
try {
|
|
327
|
+
while (true) {
|
|
328
|
+
const { done, value } = await reader.read();
|
|
329
|
+
if (done)
|
|
330
|
+
break;
|
|
331
|
+
if (!value || value.length === 0)
|
|
332
|
+
continue;
|
|
333
|
+
stats.bytesRead += value.length;
|
|
334
|
+
if (stats.bytesRead > maxBytes) {
|
|
335
|
+
stats.budgetExceeded = true;
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
const text = decoder.decode(value, { stream: true });
|
|
339
|
+
if (text)
|
|
340
|
+
yield text;
|
|
341
|
+
}
|
|
342
|
+
const tail = decoder.decode();
|
|
343
|
+
if (tail)
|
|
344
|
+
yield tail;
|
|
345
|
+
}
|
|
346
|
+
finally {
|
|
347
|
+
await reader.cancel().catch(() => undefined);
|
|
348
|
+
reader.releaseLock();
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Consume enough of the text stream to tell data from XML, then take the header.
|
|
353
|
+
*
|
|
354
|
+
* Both non-data shapes announce themselves in the first character, so
|
|
355
|
+
* classification happens before a single row is parsed — a `syncResponse`
|
|
356
|
+
* envelope arrives on HTTP 200 and would otherwise be read as a TSV header of
|
|
357
|
+
* XML followed by no rows, which looks like a successful empty download.
|
|
358
|
+
*
|
|
359
|
+
* A body identified as XML is then buffered to the peek window before it is
|
|
360
|
+
* classified, rather than to the first line break. The XML declaration ends in a
|
|
361
|
+
* newline, so stopping there would hand the classifier the declaration alone and
|
|
362
|
+
* lose the `syncResponse` and `faultcode` markers that follow it.
|
|
363
|
+
*
|
|
364
|
+
* A TSV header, by contrast, is buffered to its line break with no length bound.
|
|
365
|
+
* Its length is proportional to the dataset's period count — 166 KB and 13,852
|
|
366
|
+
* fields on a daily series — so capping the search would parse a prefix of it as
|
|
367
|
+
* the whole header, silently drop every period column past the cap, and read the
|
|
368
|
+
* header's own tail as a data line. The byte budget is what bounds this loop.
|
|
369
|
+
*/
|
|
370
|
+
async function readHeader(chunks, datasetCode) {
|
|
371
|
+
let buf = '';
|
|
372
|
+
let exhausted = false;
|
|
373
|
+
const pull = async () => {
|
|
374
|
+
const next = await chunks.next();
|
|
375
|
+
if (next.done)
|
|
376
|
+
exhausted = true;
|
|
377
|
+
else
|
|
378
|
+
buf += next.value;
|
|
379
|
+
};
|
|
380
|
+
while (buf.trim() === '' && !exhausted)
|
|
381
|
+
await pull();
|
|
382
|
+
if (buf.trimStart().startsWith('<')) {
|
|
383
|
+
while (buf.length < XML_PEEK_CHARS && !exhausted)
|
|
384
|
+
await pull();
|
|
385
|
+
classifyXmlBody(buf, datasetCode);
|
|
386
|
+
}
|
|
387
|
+
while (!buf.includes('\n') && !exhausted)
|
|
388
|
+
await pull();
|
|
389
|
+
const newline = buf.indexOf('\n');
|
|
390
|
+
if (newline === -1) {
|
|
391
|
+
if (buf.trim() === '') {
|
|
392
|
+
throw serviceUnavailable(`Eurostat SDMX endpoint returned an empty body for "${datasetCode}".`, { reason: 'upstream_fault', datasetCode });
|
|
393
|
+
}
|
|
394
|
+
// A body that ended without a line break: one header line and no data rows.
|
|
395
|
+
return { header: parseHeader(stripCr(buf)), iterator: chunks, remainder: '' };
|
|
396
|
+
}
|
|
397
|
+
return {
|
|
398
|
+
header: parseHeader(stripCr(buf.slice(0, newline))),
|
|
399
|
+
iterator: chunks,
|
|
400
|
+
remainder: buf.slice(newline + 1),
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
function stripCr(line) {
|
|
404
|
+
return line.endsWith('\r') ? line.slice(0, -1) : line;
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Expand the wide TSV body into one row per populated cell.
|
|
408
|
+
*
|
|
409
|
+
* Each data line carries a comma-joined dimension key and one cell per period
|
|
410
|
+
* column, so a line becomes as many rows as it has populated cells. Cells that
|
|
411
|
+
* are blank on both sides of the value/flag split carry no observation and are
|
|
412
|
+
* skipped; `:` is kept, because Eurostat reporting a value as unavailable is
|
|
413
|
+
* itself data.
|
|
414
|
+
*
|
|
415
|
+
* Only complete lines are parsed. When the byte budget cancels the transfer
|
|
416
|
+
* mid-line the trailing fragment is dropped rather than parsed into a row with
|
|
417
|
+
* silently truncated fields.
|
|
418
|
+
*/
|
|
419
|
+
async function* expandRows(chunks, remainder, header, stats) {
|
|
420
|
+
const seen = new Set();
|
|
421
|
+
let buf = remainder;
|
|
422
|
+
const emit = function* (line) {
|
|
423
|
+
const trimmed = stripCr(line);
|
|
424
|
+
if (trimmed === '')
|
|
425
|
+
return;
|
|
426
|
+
const fields = trimmed.split('\t');
|
|
427
|
+
const keyParts = (fields[0] ?? '').split(',');
|
|
428
|
+
const cellCount = Math.min(fields.length - 1, header.periods.length);
|
|
429
|
+
for (let i = 0; i < cellCount; i++) {
|
|
430
|
+
const parsed = parseCell(fields[i + 1] ?? '');
|
|
431
|
+
if (!parsed)
|
|
432
|
+
continue;
|
|
433
|
+
const period = header.periods[i] ?? '';
|
|
434
|
+
const row = {};
|
|
435
|
+
for (let d = 0; d < header.dimensions.length; d++) {
|
|
436
|
+
row[header.dimensions[d]] = keyParts[d]?.trim() ?? null;
|
|
437
|
+
}
|
|
438
|
+
row[TIME_COLUMN] = period;
|
|
439
|
+
row[OBS_VALUE_COLUMN] = parsed.value;
|
|
440
|
+
row[OBS_FLAG_COLUMN] = parsed.flag;
|
|
441
|
+
row[OBS_FLAG_LABEL_COLUMN] = parsed.flag ? (OBS_FLAG_LABELS[parsed.flag] ?? null) : null;
|
|
442
|
+
row[CONF_STATUS_COLUMN] = parsed.conf;
|
|
443
|
+
row[CONF_STATUS_LABEL_COLUMN] = parsed.conf
|
|
444
|
+
? (CONF_STATUS_LABELS[parsed.conf] ?? null)
|
|
445
|
+
: null;
|
|
446
|
+
stats.rowCount++;
|
|
447
|
+
if (parsed.value === null)
|
|
448
|
+
stats.missingCount++;
|
|
449
|
+
seen.add(period);
|
|
450
|
+
yield row;
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
/**
|
|
454
|
+
* Drain every complete line sitting in the buffer. Called before the read loop
|
|
455
|
+
* as well as inside it: a small body arrives whole while the header is being
|
|
456
|
+
* read, leaving every data line already buffered and no further chunk to
|
|
457
|
+
* trigger a split.
|
|
458
|
+
*/
|
|
459
|
+
const flush = function* () {
|
|
460
|
+
let newline = buf.indexOf('\n');
|
|
461
|
+
while (newline !== -1) {
|
|
462
|
+
yield* emit(buf.slice(0, newline));
|
|
463
|
+
buf = buf.slice(newline + 1);
|
|
464
|
+
newline = buf.indexOf('\n');
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
try {
|
|
468
|
+
yield* flush();
|
|
469
|
+
for await (const chunk of chunks) {
|
|
470
|
+
buf += chunk;
|
|
471
|
+
yield* flush();
|
|
472
|
+
}
|
|
473
|
+
// The final line of a complete body has no trailing newline. A budget-cancelled
|
|
474
|
+
// body's trailing fragment is not a final line — it is a line cut in half.
|
|
475
|
+
if (!stats.budgetExceeded)
|
|
476
|
+
yield* emit(buf);
|
|
477
|
+
stats.periodsSeen = [...seen].sort();
|
|
478
|
+
}
|
|
479
|
+
finally {
|
|
480
|
+
/**
|
|
481
|
+
* Close the text stream on every exit. A consumer that stops early — a canvas
|
|
482
|
+
* append that throws, an abort — abandons this generator, and a small body
|
|
483
|
+
* whose rows were all buffered while the header was read is abandoned before
|
|
484
|
+
* the `for await` above ever starts it. Neither path would otherwise reach the
|
|
485
|
+
* cancel that frees the response body.
|
|
486
|
+
*/
|
|
487
|
+
await chunks.return(undefined);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
// --- Init/accessor pattern ---
|
|
491
|
+
let _service;
|
|
492
|
+
export function initEurostatBulkService(config, storage) {
|
|
493
|
+
_service = new EurostatBulkService(config, storage);
|
|
494
|
+
}
|
|
495
|
+
export function getEurostatBulkService() {
|
|
496
|
+
if (!_service) {
|
|
497
|
+
throw new Error('EurostatBulkService not initialized — call initEurostatBulkService() in setup()');
|
|
498
|
+
}
|
|
499
|
+
return _service;
|
|
500
|
+
}
|
|
501
|
+
//# sourceMappingURL=eurostat-bulk-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eurostat-bulk-service.js","sourceRoot":"","sources":["../../../src/services/eurostat-bulk/eurostat-bulk-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Domain types and the static flag dictionaries for the Eurostat
|
|
3
|
+
* SDMX 2.1 bulk (TSV) service.
|
|
4
|
+
* @module services/eurostat-bulk/types
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* One observation flattened from the wide TSV layout into a dataframe row.
|
|
8
|
+
*
|
|
9
|
+
* The TSV endpoint carries codes only — no labels — so a row has one column per
|
|
10
|
+
* dimension holding the code, `time` for the period, then the measure columns.
|
|
11
|
+
* There is deliberately no `<dim>_label` companion: staging all-null label
|
|
12
|
+
* columns would claim a lookup the bulk endpoint never performed.
|
|
13
|
+
*/
|
|
14
|
+
export type BulkRow = Record<string, string | number | null>;
|
|
15
|
+
/** Period column name. `time` matches the dimension name JSON-stat uses. */
|
|
16
|
+
export declare const TIME_COLUMN = "time";
|
|
17
|
+
/** Measure column names — the non-dimension columns of a {@link BulkRow}. */
|
|
18
|
+
export declare const OBS_VALUE_COLUMN = "obs_value";
|
|
19
|
+
export declare const OBS_FLAG_COLUMN = "obs_flag";
|
|
20
|
+
export declare const OBS_FLAG_LABEL_COLUMN = "obs_flag_label";
|
|
21
|
+
export declare const CONF_STATUS_COLUMN = "conf_status";
|
|
22
|
+
export declare const CONF_STATUS_LABEL_COLUMN = "conf_status_label";
|
|
23
|
+
/**
|
|
24
|
+
* Eurostat's `OBS_FLAG` codelist, verbatim from
|
|
25
|
+
* `sdmx/2.1/codelist/ESTAT/OBS_FLAG` — all 42 codes.
|
|
26
|
+
*
|
|
27
|
+
* Codes are composite: a cell flagged `bdep` is one code meaning "break in time
|
|
28
|
+
* series, definition differs, estimated, provisional", not four codes to be
|
|
29
|
+
* decomposed. Lookups that miss return `undefined` and the row's label column is
|
|
30
|
+
* `null` — Eurostat can ship a code in data that its published codelist omits,
|
|
31
|
+
* and an invented label is worse than an absent one.
|
|
32
|
+
*/
|
|
33
|
+
export declare const OBS_FLAG_LABELS: Readonly<Record<string, string>>;
|
|
34
|
+
/**
|
|
35
|
+
* Eurostat's `CONF_STATUS` codelist, verbatim from
|
|
36
|
+
* `sdmx/2.1/codelist/ESTAT/CONF_STATUS` — all three codes.
|
|
37
|
+
*
|
|
38
|
+
* `C` is what appears in TSV data, behind the `@` of a cell like `: @C`. The
|
|
39
|
+
* same cells surface in JSON-stat folded into the observation status instead,
|
|
40
|
+
* as `|C` labelled `|confidential`, which is why a bulk-staged table and a
|
|
41
|
+
* query-staged one disagree about where a confidentiality marker lives.
|
|
42
|
+
*/
|
|
43
|
+
export declare const CONF_STATUS_LABELS: Readonly<Record<string, string>>;
|
|
44
|
+
/**
|
|
45
|
+
* The header of a wide TSV body, parsed.
|
|
46
|
+
*
|
|
47
|
+
* Read from the response itself rather than from dataset metadata: the first
|
|
48
|
+
* field of the header line names the dimensions in the order the row keys use
|
|
49
|
+
* (`freq,unit,na_item,geo\TIME_PERIOD`), and the remaining tab-separated fields
|
|
50
|
+
* are the period columns. Both arrive with padding Eurostat does not strip.
|
|
51
|
+
*/
|
|
52
|
+
export interface TsvHeader {
|
|
53
|
+
/** Dimension codes in row-key order, `time` excluded. */
|
|
54
|
+
dimensions: string[];
|
|
55
|
+
/** Period codes, in column order. */
|
|
56
|
+
periods: string[];
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Counters filled in as {@link BulkDownload.rows} is drained.
|
|
60
|
+
*
|
|
61
|
+
* Nothing here is knowable before the stream is consumed — the body is chunked
|
|
62
|
+
* with no `Content-Length` — so a consumer reads these only after the generator
|
|
63
|
+
* has been exhausted (or has stopped on the byte budget).
|
|
64
|
+
*/
|
|
65
|
+
export interface BulkStats {
|
|
66
|
+
/**
|
|
67
|
+
* True when the byte budget stopped the transfer before the body ended, so
|
|
68
|
+
* the rows are a prefix of the dataset rather than all of it.
|
|
69
|
+
*/
|
|
70
|
+
budgetExceeded: boolean;
|
|
71
|
+
/** Decoded TSV bytes read. The unit the byte budget is enforced in. */
|
|
72
|
+
bytesRead: number;
|
|
73
|
+
/** Whether the response body arrived gzip-compressed. */
|
|
74
|
+
compressed: boolean;
|
|
75
|
+
/** Emitted observations carrying no numeric value. */
|
|
76
|
+
missingCount: number;
|
|
77
|
+
/** Distinct periods actually emitted, ascending. */
|
|
78
|
+
periodsSeen: string[];
|
|
79
|
+
/** Observations emitted — one per populated cell. */
|
|
80
|
+
rowCount: number;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* A started bulk download: the header is parsed, the body is not yet drained.
|
|
84
|
+
*
|
|
85
|
+
* Returning before the body is consumed is what lets the caller declare a canvas
|
|
86
|
+
* column schema from {@link header} and then hand {@link rows} straight to the
|
|
87
|
+
* canvas appender, so a multi-million-row download never materializes as an
|
|
88
|
+
* array. {@link stats} is mutated as the generator runs and is only meaningful
|
|
89
|
+
* once it has finished.
|
|
90
|
+
*/
|
|
91
|
+
export interface BulkDownload {
|
|
92
|
+
header: TsvHeader;
|
|
93
|
+
rows: () => AsyncGenerator<BulkRow>;
|
|
94
|
+
stats: BulkStats;
|
|
95
|
+
/** The request URL, minus nothing — no credentials are involved. */
|
|
96
|
+
url: string;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/services/eurostat-bulk/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;GAOG;AACH,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,CAAC;AAE7D,4EAA4E;AAC5E,eAAO,MAAM,WAAW,SAAS,CAAC;AAClC,6EAA6E;AAC7E,eAAO,MAAM,gBAAgB,cAAc,CAAC;AAC5C,eAAO,MAAM,eAAe,aAAa,CAAC;AAC1C,eAAO,MAAM,qBAAqB,mBAAmB,CAAC;AACtD,eAAO,MAAM,kBAAkB,gBAAgB,CAAC;AAChD,eAAO,MAAM,wBAAwB,sBAAsB,CAAC;AAE5D;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA2C5D,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAI/D,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,WAAW,SAAS;IACxB,yDAAyD;IACzD,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,qCAAqC;IACrC,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,SAAS;IACxB;;;OAGG;IACH,cAAc,EAAE,OAAO,CAAC;IACxB,uEAAuE;IACvE,SAAS,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,UAAU,EAAE,OAAO,CAAC;IACpB,sDAAsD;IACtD,YAAY,EAAE,MAAM,CAAC;IACrB,oDAAoD;IACpD,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,qDAAqD;IACrD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,SAAS,CAAC;IAClB,IAAI,EAAE,MAAM,cAAc,CAAC,OAAO,CAAC,CAAC;IACpC,KAAK,EAAE,SAAS,CAAC;IACjB,oEAAoE;IACpE,GAAG,EAAE,MAAM,CAAC;CACb"}
|