@maschinenlesbar.org/luftqualitaet-cli 0.0.1
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/CONTRIBUTING.md +25 -0
- package/LICENSE +661 -0
- package/LICENSING.md +47 -0
- package/README.md +208 -0
- package/dist/src/cli/commands/data.d.ts +4 -0
- package/dist/src/cli/commands/data.d.ts.map +1 -0
- package/dist/src/cli/commands/data.js +152 -0
- package/dist/src/cli/commands/data.js.map +1 -0
- package/dist/src/cli/commands/reference.d.ts +4 -0
- package/dist/src/cli/commands/reference.d.ts.map +1 -0
- package/dist/src/cli/commands/reference.js +33 -0
- package/dist/src/cli/commands/reference.js.map +1 -0
- package/dist/src/cli/index.d.ts +3 -0
- package/dist/src/cli/index.d.ts.map +1 -0
- package/dist/src/cli/index.js +6 -0
- package/dist/src/cli/index.js.map +1 -0
- package/dist/src/cli/io.d.ts +13 -0
- package/dist/src/cli/io.d.ts.map +1 -0
- package/dist/src/cli/io.js +7 -0
- package/dist/src/cli/io.js.map +1 -0
- package/dist/src/cli/program.d.ts +7 -0
- package/dist/src/cli/program.d.ts.map +1 -0
- package/dist/src/cli/program.js +52 -0
- package/dist/src/cli/program.js.map +1 -0
- package/dist/src/cli/run.d.ts +3 -0
- package/dist/src/cli/run.d.ts.map +1 -0
- package/dist/src/cli/run.js +48 -0
- package/dist/src/cli/run.js.map +1 -0
- package/dist/src/cli/shared.d.ts +65 -0
- package/dist/src/cli/shared.d.ts.map +1 -0
- package/dist/src/cli/shared.js +142 -0
- package/dist/src/cli/shared.js.map +1 -0
- package/dist/src/client/client.d.ts +30 -0
- package/dist/src/client/client.d.ts.map +1 -0
- package/dist/src/client/client.js +76 -0
- package/dist/src/client/client.js.map +1 -0
- package/dist/src/client/engine.d.ts +54 -0
- package/dist/src/client/engine.d.ts.map +1 -0
- package/dist/src/client/engine.js +117 -0
- package/dist/src/client/engine.js.map +1 -0
- package/dist/src/client/enums.d.ts +13 -0
- package/dist/src/client/enums.d.ts.map +1 -0
- package/dist/src/client/enums.js +17 -0
- package/dist/src/client/enums.js.map +1 -0
- package/dist/src/client/errors.d.ts +33 -0
- package/dist/src/client/errors.d.ts.map +1 -0
- package/dist/src/client/errors.js +40 -0
- package/dist/src/client/errors.js.map +1 -0
- package/dist/src/client/http.d.ts +26 -0
- package/dist/src/client/http.d.ts.map +1 -0
- package/dist/src/client/http.js +111 -0
- package/dist/src/client/http.js.map +1 -0
- package/dist/src/client/index.d.ts +11 -0
- package/dist/src/client/index.d.ts.map +1 -0
- package/dist/src/client/index.js +9 -0
- package/dist/src/client/index.js.map +1 -0
- package/dist/src/client/query.d.ts +9 -0
- package/dist/src/client/query.d.ts.map +1 -0
- package/dist/src/client/query.js +33 -0
- package/dist/src/client/query.js.map +1 -0
- package/dist/src/client/types.d.ts +57 -0
- package/dist/src/client/types.d.ts.map +1 -0
- package/dist/src/client/types.js +8 -0
- package/dist/src/client/types.js.map +1 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +3 -0
- package/dist/src/index.js.map +1 -0
- package/package.json +66 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** Base class for every error originating from this client. */
|
|
2
|
+
export declare class LuftError extends Error {
|
|
3
|
+
constructor(message: string, options?: {
|
|
4
|
+
cause?: unknown;
|
|
5
|
+
});
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* The API responded with a non-2xx status code. `detail` holds a human-readable
|
|
9
|
+
* message extracted from the response body when one is present.
|
|
10
|
+
*/
|
|
11
|
+
export declare class LuftApiError extends LuftError {
|
|
12
|
+
readonly status: number;
|
|
13
|
+
readonly detail: string | undefined;
|
|
14
|
+
readonly url: string;
|
|
15
|
+
readonly method: string;
|
|
16
|
+
readonly body: string;
|
|
17
|
+
constructor(args: {
|
|
18
|
+
status: number;
|
|
19
|
+
url: string;
|
|
20
|
+
method: string;
|
|
21
|
+
body: string;
|
|
22
|
+
detail?: string;
|
|
23
|
+
});
|
|
24
|
+
/** True for statuses the API documents as transient and retry-able. */
|
|
25
|
+
get isRetryable(): boolean;
|
|
26
|
+
}
|
|
27
|
+
/** A transport-level failure (DNS, connection reset, timeout, ...). */
|
|
28
|
+
export declare class LuftNetworkError extends LuftError {
|
|
29
|
+
}
|
|
30
|
+
/** The response body could not be parsed as the expected JSON shape. */
|
|
31
|
+
export declare class LuftParseError extends LuftError {
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/client/errors.ts"],"names":[],"mappings":"AAGA,+DAA+D;AAC/D,qBAAa,SAAU,SAAQ,KAAK;gBACtB,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAI3D;AAED;;;GAGG;AACH,qBAAa,YAAa,SAAQ,SAAS;IACzC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,IAAI,EAAE;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB;IAUD,uEAAuE;IACvE,IAAI,WAAW,IAAI,OAAO,CAEzB;CACF;AAED,uEAAuE;AACvE,qBAAa,gBAAiB,SAAQ,SAAS;CAAG;AAElD,wEAAwE;AACxE,qBAAa,cAAe,SAAQ,SAAS;CAAG"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Error types raised by the client. Kept free of any I/O so they are trivial to
|
|
2
|
+
// construct in tests and to `instanceof`-check by consumers.
|
|
3
|
+
/** Base class for every error originating from this client. */
|
|
4
|
+
export class LuftError extends Error {
|
|
5
|
+
constructor(message, options) {
|
|
6
|
+
super(message, options);
|
|
7
|
+
this.name = new.target.name;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The API responded with a non-2xx status code. `detail` holds a human-readable
|
|
12
|
+
* message extracted from the response body when one is present.
|
|
13
|
+
*/
|
|
14
|
+
export class LuftApiError extends LuftError {
|
|
15
|
+
status;
|
|
16
|
+
detail;
|
|
17
|
+
url;
|
|
18
|
+
method;
|
|
19
|
+
body;
|
|
20
|
+
constructor(args) {
|
|
21
|
+
const detailPart = args.detail ? `: ${args.detail}` : "";
|
|
22
|
+
super(`HTTP ${args.status} for ${args.method} ${args.url}${detailPart}`);
|
|
23
|
+
this.status = args.status;
|
|
24
|
+
this.url = args.url;
|
|
25
|
+
this.method = args.method;
|
|
26
|
+
this.body = args.body;
|
|
27
|
+
this.detail = args.detail;
|
|
28
|
+
}
|
|
29
|
+
/** True for statuses the API documents as transient and retry-able. */
|
|
30
|
+
get isRetryable() {
|
|
31
|
+
return this.status === 429 || this.status === 503;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/** A transport-level failure (DNS, connection reset, timeout, ...). */
|
|
35
|
+
export class LuftNetworkError extends LuftError {
|
|
36
|
+
}
|
|
37
|
+
/** The response body could not be parsed as the expected JSON shape. */
|
|
38
|
+
export class LuftParseError extends LuftError {
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/client/errors.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,6DAA6D;AAE7D,+DAA+D;AAC/D,MAAM,OAAO,SAAU,SAAQ,KAAK;IAClC,YAAY,OAAe,EAAE,OAA6B;QACxD,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;IAC9B,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,OAAO,YAAa,SAAQ,SAAS;IAChC,MAAM,CAAS;IACf,MAAM,CAAqB;IAC3B,GAAG,CAAS;IACZ,MAAM,CAAS;IACf,IAAI,CAAS;IAEtB,YAAY,IAMX;QACC,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACzD,KAAK,CAAC,QAAQ,IAAI,CAAC,MAAM,QAAQ,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,GAAG,GAAG,UAAU,EAAE,CAAC,CAAC;QACzE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;QACpB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IAC5B,CAAC;IAED,uEAAuE;IACvE,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,MAAM,KAAK,GAAG,CAAC;IACpD,CAAC;CACF;AAED,uEAAuE;AACvE,MAAM,OAAO,gBAAiB,SAAQ,SAAS;CAAG;AAElD,wEAAwE;AACxE,MAAM,OAAO,cAAe,SAAQ,SAAS;CAAG"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import http from "node:http";
|
|
2
|
+
export interface HttpRequest {
|
|
3
|
+
method: string;
|
|
4
|
+
/** Fully-qualified absolute URL. */
|
|
5
|
+
url: string;
|
|
6
|
+
headers?: Record<string, string>;
|
|
7
|
+
/** Optional request body (already serialised). */
|
|
8
|
+
body?: string | Buffer;
|
|
9
|
+
/** Per-request timeout in milliseconds. */
|
|
10
|
+
timeoutMs?: number;
|
|
11
|
+
/** Hard cap on the response body size in bytes; the request aborts if exceeded. */
|
|
12
|
+
maxResponseBytes?: number;
|
|
13
|
+
}
|
|
14
|
+
export interface HttpResponse {
|
|
15
|
+
status: number;
|
|
16
|
+
headers: http.IncomingHttpHeaders;
|
|
17
|
+
body: Buffer;
|
|
18
|
+
}
|
|
19
|
+
export type Transport = (request: HttpRequest) => Promise<HttpResponse>;
|
|
20
|
+
/**
|
|
21
|
+
* Default transport. Resolves with the raw response (including non-2xx) — status
|
|
22
|
+
* interpretation is the client's job. Rejects only on transport-level failures
|
|
23
|
+
* (connection errors, timeouts, malformed URLs).
|
|
24
|
+
*/
|
|
25
|
+
export declare const nodeHttpTransport: Transport;
|
|
26
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../../src/client/http.ts"],"names":[],"mappings":"AAQA,OAAO,IAAI,MAAM,WAAW,CAAC;AAI7B,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mFAAmF;IACnF,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,IAAI,CAAC,mBAAmB,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;AAExE;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,SAuG5B,CAAC"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// HTTP transport built on Node's built-in `http`/`https` modules — no axios,
|
|
2
|
+
// no fetch polyfill, no third-party HTTP client.
|
|
3
|
+
//
|
|
4
|
+
// The transport is a plain function so it can be trivially swapped out in tests
|
|
5
|
+
// (inject a `mock.fn()` returning a canned HttpResponse) without touching the
|
|
6
|
+
// network. The default implementation below is exercised against a real local
|
|
7
|
+
// `http.createServer` in the test-suite.
|
|
8
|
+
import http from "node:http";
|
|
9
|
+
import https from "node:https";
|
|
10
|
+
import { LuftNetworkError } from "./errors.js";
|
|
11
|
+
/**
|
|
12
|
+
* Default transport. Resolves with the raw response (including non-2xx) — status
|
|
13
|
+
* interpretation is the client's job. Rejects only on transport-level failures
|
|
14
|
+
* (connection errors, timeouts, malformed URLs).
|
|
15
|
+
*/
|
|
16
|
+
export const nodeHttpTransport = (request) => new Promise((resolve, reject) => {
|
|
17
|
+
let url;
|
|
18
|
+
try {
|
|
19
|
+
url = new URL(request.url);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
reject(new LuftNetworkError(`Invalid URL: ${request.url}`));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
// Only http/https are supported. Reject anything else up front with a clear,
|
|
26
|
+
// typed error instead of letting Node throw an opaque ERR_INVALID_PROTOCOL
|
|
27
|
+
// (and so this never reaches the file:/ftp:/etc. drivers).
|
|
28
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
29
|
+
reject(new LuftNetworkError(`Unsupported protocol "${url.protocol}" in URL: ${request.url}`));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const isHttps = url.protocol === "https:";
|
|
33
|
+
const driver = isHttps ? https : http;
|
|
34
|
+
const maxBytes = request.maxResponseBytes;
|
|
35
|
+
const req = driver.request(url, {
|
|
36
|
+
method: request.method,
|
|
37
|
+
headers: request.headers,
|
|
38
|
+
}, (res) => {
|
|
39
|
+
const chunks = [];
|
|
40
|
+
let received = 0;
|
|
41
|
+
let aborted = false;
|
|
42
|
+
res.on("data", (chunk) => {
|
|
43
|
+
if (aborted)
|
|
44
|
+
return;
|
|
45
|
+
received += chunk.length;
|
|
46
|
+
if (maxBytes !== undefined && received > maxBytes) {
|
|
47
|
+
aborted = true;
|
|
48
|
+
res.destroy();
|
|
49
|
+
reject(new LuftNetworkError(`Response exceeded maxResponseBytes (${maxBytes})`));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
chunks.push(chunk);
|
|
53
|
+
});
|
|
54
|
+
res.on("end", () => {
|
|
55
|
+
if (aborted)
|
|
56
|
+
return;
|
|
57
|
+
resolve({
|
|
58
|
+
status: res.statusCode ?? 0,
|
|
59
|
+
headers: res.headers,
|
|
60
|
+
body: Buffer.concat(chunks),
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
res.on("error", (err) => {
|
|
64
|
+
if (aborted)
|
|
65
|
+
return; // we already rejected with the size-cap error
|
|
66
|
+
reject(new LuftNetworkError(`Response stream error: ${err.message}`, { cause: err }));
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
// Only treat the timeout as a *timeout* if the socket was actually
|
|
70
|
+
// connected. A pending DNS/connect failure can outrace the timer (an
|
|
71
|
+
// unresolvable host may take longer to fail than the timeout window); in
|
|
72
|
+
// that case we want the real DNS/connect failure to surface, not a
|
|
73
|
+
// misleading "Request timed out" message.
|
|
74
|
+
let connected = false;
|
|
75
|
+
let dnsError;
|
|
76
|
+
req.on("socket", (socket) => {
|
|
77
|
+
// A failed DNS lookup reports here before the socket emits "error";
|
|
78
|
+
// capture it so a racing timeout can report the true cause.
|
|
79
|
+
socket.on("lookup", (err) => {
|
|
80
|
+
if (err)
|
|
81
|
+
dnsError = err;
|
|
82
|
+
});
|
|
83
|
+
socket.on("connect", () => {
|
|
84
|
+
connected = true;
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
if (request.timeoutMs && request.timeoutMs > 0) {
|
|
88
|
+
req.setTimeout(request.timeoutMs, () => {
|
|
89
|
+
if (connected) {
|
|
90
|
+
req.destroy(new LuftNetworkError(`Request timed out after ${request.timeoutMs}ms`));
|
|
91
|
+
}
|
|
92
|
+
else if (dnsError) {
|
|
93
|
+
// DNS resolution already failed; report that, not a timeout.
|
|
94
|
+
req.destroy(new LuftNetworkError(dnsError.message, { cause: dnsError }));
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
// Still in the connect/DNS phase with no socket yet. Attribute the
|
|
98
|
+
// failure to that phase rather than claiming a generic request timeout.
|
|
99
|
+
req.destroy(new LuftNetworkError(`Could not connect to ${url.host} within ${request.timeoutMs}ms (host unresolved or unreachable)`));
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
req.on("error", (err) => {
|
|
104
|
+
// A timeout destroy already passes an LuftNetworkError; don't double-wrap.
|
|
105
|
+
reject(err instanceof LuftNetworkError ? err : new LuftNetworkError(err.message, { cause: err }));
|
|
106
|
+
});
|
|
107
|
+
if (request.body !== undefined)
|
|
108
|
+
req.write(request.body);
|
|
109
|
+
req.end();
|
|
110
|
+
});
|
|
111
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../../src/client/http.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { LuftqualitaetClient } from "./client.js";
|
|
2
|
+
export { RequestEngine, DEFAULT_BASE_URL } from "./engine.js";
|
|
3
|
+
export type { EngineOptions, RawResponse } from "./engine.js";
|
|
4
|
+
export { nodeHttpTransport } from "./http.js";
|
|
5
|
+
export type { Transport, HttpRequest, HttpResponse } from "./http.js";
|
|
6
|
+
export { buildQueryString } from "./query.js";
|
|
7
|
+
export type { QueryParams, QueryValue } from "./query.js";
|
|
8
|
+
export { LuftError, LuftApiError, LuftNetworkError, LuftParseError } from "./errors.js";
|
|
9
|
+
export * from "./enums.js";
|
|
10
|
+
export * from "./types.js";
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC9D,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9C,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAExF,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Public entry point for the API client library.
|
|
2
|
+
export { LuftqualitaetClient } from "./client.js";
|
|
3
|
+
export { RequestEngine, DEFAULT_BASE_URL } from "./engine.js";
|
|
4
|
+
export { nodeHttpTransport } from "./http.js";
|
|
5
|
+
export { buildQueryString } from "./query.js";
|
|
6
|
+
export { LuftError, LuftApiError, LuftNetworkError, LuftParseError } from "./errors.js";
|
|
7
|
+
export * from "./enums.js";
|
|
8
|
+
export * from "./types.js";
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA,iDAAiD;AAEjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAE9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAExF,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type QueryPrimitive = string | number | boolean | Date | null | undefined;
|
|
2
|
+
export type QueryValue = QueryPrimitive | QueryPrimitive[];
|
|
3
|
+
export type QueryParams = Record<string, QueryValue>;
|
|
4
|
+
/**
|
|
5
|
+
* Build a query string (without a leading `?`) from a params object.
|
|
6
|
+
* Returns an empty string when no parameters survive filtering.
|
|
7
|
+
*/
|
|
8
|
+
export declare function buildQueryString(params: QueryParams): string;
|
|
9
|
+
//# sourceMappingURL=query.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../src/client/query.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC;AACjF,MAAM,MAAM,UAAU,GAAG,cAAc,GAAG,cAAc,EAAE,CAAC;AAC3D,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAQrD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAe5D"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Tiny, dependency-free query-string builder.
|
|
2
|
+
// - `undefined` / `null` values are omitted entirely
|
|
3
|
+
// - arrays are serialised as repeated keys (`?id=a&id=b`)
|
|
4
|
+
// - booleans become the strings "true"/"false"
|
|
5
|
+
// - Date values become full ISO-8601 strings
|
|
6
|
+
// - everything else is coerced with String()
|
|
7
|
+
function serializeScalar(value) {
|
|
8
|
+
if (value instanceof Date)
|
|
9
|
+
return value.toISOString();
|
|
10
|
+
if (typeof value === "boolean")
|
|
11
|
+
return value ? "true" : "false";
|
|
12
|
+
return String(value);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Build a query string (without a leading `?`) from a params object.
|
|
16
|
+
* Returns an empty string when no parameters survive filtering.
|
|
17
|
+
*/
|
|
18
|
+
export function buildQueryString(params) {
|
|
19
|
+
const search = new URLSearchParams();
|
|
20
|
+
for (const [key, raw] of Object.entries(params)) {
|
|
21
|
+
if (raw === undefined || raw === null)
|
|
22
|
+
continue;
|
|
23
|
+
const values = Array.isArray(raw) ? raw : [raw];
|
|
24
|
+
for (const value of values) {
|
|
25
|
+
if (value === undefined || value === null)
|
|
26
|
+
continue;
|
|
27
|
+
search.append(key, serializeScalar(value));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// URLSearchParams encodes spaces as "+"; "%20" is more broadly interoperable.
|
|
31
|
+
return search.toString().replace(/\+/g, "%20");
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.js","sourceRoot":"","sources":["../../../src/client/query.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,uDAAuD;AACvD,4DAA4D;AAC5D,iDAAiD;AACjD,+CAA+C;AAC/C,+CAA+C;AAM/C,SAAS,eAAe,CAAC,KAAgD;IACvE,IAAI,KAAK,YAAY,IAAI;QAAE,OAAO,KAAK,CAAC,WAAW,EAAE,CAAC;IACtD,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;IAChE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAmB;IAClD,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;IAErC,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAChD,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,IAAI;YAAE,SAAS;QAEhD,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAChD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;gBAAE,SAAS;YACpD,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,OAAO,MAAM,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AACjD,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { Lang, IndexKind, MetaUse, ThresholdUse } from "./enums.js";
|
|
2
|
+
export type JsonValue = string | number | boolean | null | JsonValue[] | {
|
|
3
|
+
[key: string]: JsonValue;
|
|
4
|
+
};
|
|
5
|
+
export type JsonObject = {
|
|
6
|
+
[key: string]: JsonValue;
|
|
7
|
+
};
|
|
8
|
+
/** A generic UBA response (typically `{ request, indices, data, count, ... }`). */
|
|
9
|
+
export type AirDataResult = JsonObject;
|
|
10
|
+
/** Common options for the reference-list endpoints. */
|
|
11
|
+
export interface ListParams {
|
|
12
|
+
lang?: Lang;
|
|
13
|
+
index?: IndexKind;
|
|
14
|
+
}
|
|
15
|
+
/** The time window + station shared by the data endpoints. */
|
|
16
|
+
export interface WindowParams {
|
|
17
|
+
/** Start date, YYYY-MM-DD. */
|
|
18
|
+
date_from: string;
|
|
19
|
+
/** Start hour, 1..24. */
|
|
20
|
+
time_from: number;
|
|
21
|
+
/** End date, YYYY-MM-DD. */
|
|
22
|
+
date_to: string;
|
|
23
|
+
/** End hour, 1..24. */
|
|
24
|
+
time_to: number;
|
|
25
|
+
/** Station id. */
|
|
26
|
+
station: number;
|
|
27
|
+
}
|
|
28
|
+
/** Parameters for `/measures/json` (a window plus optional component/scope). */
|
|
29
|
+
export interface MeasuresParams extends WindowParams {
|
|
30
|
+
component?: number;
|
|
31
|
+
scope?: number;
|
|
32
|
+
}
|
|
33
|
+
/** Parameters for `/annualbalances/json` and `/transgressions/json`. */
|
|
34
|
+
export interface YearComponentParams {
|
|
35
|
+
component: number;
|
|
36
|
+
/** Four-digit year, >= 2016. */
|
|
37
|
+
year: number;
|
|
38
|
+
lang?: Lang;
|
|
39
|
+
index?: IndexKind;
|
|
40
|
+
}
|
|
41
|
+
/** Parameters for `/thresholds/json`. */
|
|
42
|
+
export interface ThresholdParams {
|
|
43
|
+
use: ThresholdUse;
|
|
44
|
+
lang?: Lang;
|
|
45
|
+
component?: number;
|
|
46
|
+
scope?: number;
|
|
47
|
+
}
|
|
48
|
+
/** Parameters for `/meta/json`. */
|
|
49
|
+
export interface MetaParams {
|
|
50
|
+
use: MetaUse;
|
|
51
|
+
lang?: Lang;
|
|
52
|
+
date_from?: string;
|
|
53
|
+
date_to?: string;
|
|
54
|
+
time_from?: number;
|
|
55
|
+
time_to?: number;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/client/types.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEzE,MAAM,MAAM,SAAS,GACjB,MAAM,GACN,MAAM,GACN,OAAO,GACP,IAAI,GACJ,SAAS,EAAE,GACX;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AACjC,MAAM,MAAM,UAAU,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAEtD,mFAAmF;AACnF,MAAM,MAAM,aAAa,GAAG,UAAU,CAAC;AAEvC,uDAAuD;AACvD,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB;AAED,8DAA8D;AAC9D,MAAM,WAAW,YAAY;IAC3B,8BAA8B;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,4BAA4B;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,uBAAuB;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,kBAAkB;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,gFAAgF;AAChF,MAAM,WAAW,cAAe,SAAQ,YAAY;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wEAAwE;AACxE,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB;AAED,yCAAyC;AACzC,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,YAAY,CAAC;IAClB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,mCAAmC;AACnC,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,OAAO,CAAC;IACb,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Domain types for the Umweltbundesamt Air Data API (umweltbundesamt.de).
|
|
2
|
+
//
|
|
3
|
+
// The API returns "index + data" structures whose row layout is described by an
|
|
4
|
+
// `indices` array and whose payload shape varies by endpoint and parameters, so
|
|
5
|
+
// responses are exposed as faithful raw `JsonObject`s. The parameter objects for
|
|
6
|
+
// the data endpoints are typed precisely.
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/client/types.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,EAAE;AACF,gFAAgF;AAChF,gFAAgF;AAChF,iFAAiF;AACjF,0CAA0C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,cAAc,mBAAmB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,cAAc,mBAAmB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@maschinenlesbar.org/luftqualitaet-cli",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "TypeScript API client and CLI for the open Umweltbundesamt Air Data API (umweltbundesamt.de)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"luftqualitaet": "dist/src/cli/index.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "dist/src/index.js",
|
|
10
|
+
"types": "dist/src/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/src/index.d.ts",
|
|
14
|
+
"import": "./dist/src/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist/src",
|
|
19
|
+
"LICENSING.md",
|
|
20
|
+
"CONTRIBUTING.md"
|
|
21
|
+
],
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=20"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
27
|
+
"build": "tsc -p tsconfig.json",
|
|
28
|
+
"prepack": "npm run build",
|
|
29
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
30
|
+
"pretest": "npm run build",
|
|
31
|
+
"test": "node --test dist/test/*.test.js",
|
|
32
|
+
"docs": "typedoc src/index.ts --out out",
|
|
33
|
+
"start": "node dist/src/cli/index.js"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"umweltbundesamt",
|
|
37
|
+
"uba",
|
|
38
|
+
"luftqualitaet",
|
|
39
|
+
"air-quality",
|
|
40
|
+
"environment",
|
|
41
|
+
"germany",
|
|
42
|
+
"cli",
|
|
43
|
+
"api-client"
|
|
44
|
+
],
|
|
45
|
+
"author": "Sebastian Schürmann <sebs@2xs.org>",
|
|
46
|
+
"license": "AGPL-3.0-or-later",
|
|
47
|
+
"repository": {
|
|
48
|
+
"type": "git",
|
|
49
|
+
"url": "git+https://github.com/maschinenlesbar-org/luftqualitaet-cli.git"
|
|
50
|
+
},
|
|
51
|
+
"bugs": {
|
|
52
|
+
"url": "https://github.com/maschinenlesbar-org/luftqualitaet-cli/issues"
|
|
53
|
+
},
|
|
54
|
+
"homepage": "https://github.com/maschinenlesbar-org/luftqualitaet-cli#readme",
|
|
55
|
+
"publishConfig": {
|
|
56
|
+
"access": "public"
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"commander": "15.0.0"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@types/node": "^22.19.19",
|
|
63
|
+
"typedoc": "0.28.19",
|
|
64
|
+
"typescript": "6.0.3"
|
|
65
|
+
}
|
|
66
|
+
}
|