@maschinenlesbar.org/pegel-online-cli 0.2.0 → 0.3.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/README.md +29 -8
- package/dist/src/cli/commands/stations.js +29 -4
- package/dist/src/cli/commands/timeseries.js +3 -3
- package/dist/src/cli/io.d.ts +2 -1
- package/dist/src/cli/io.js +7 -2
- package/dist/src/cli/program.js +6 -6
- package/dist/src/cli/run.d.ts +12 -0
- package/dist/src/cli/run.js +29 -1
- package/dist/src/cli/shared.d.ts +8 -0
- package/dist/src/cli/shared.js +16 -0
- package/dist/src/client/client.d.ts +40 -0
- package/dist/src/client/client.js +138 -31
- package/dist/src/client/engine.d.ts +42 -12
- package/dist/src/client/engine.js +385 -47
- package/dist/src/client/errors.d.ts +31 -1
- package/dist/src/client/errors.js +67 -3
- package/dist/src/client/http.d.ts +26 -0
- package/dist/src/client/http.js +16 -1
- package/dist/src/client/index.d.ts +5 -4
- package/dist/src/client/index.js +4 -4
- package/dist/src/client/types.d.ts +54 -7
- package/dist/src/client/validate.d.ts +23 -2
- package/dist/src/client/validate.js +49 -2
- package/package.json +3 -3
|
@@ -9,7 +9,9 @@ export interface RawResponse {
|
|
|
9
9
|
/**
|
|
10
10
|
* Options for {@link RequestEngine} and the client. The numeric options must be
|
|
11
11
|
* integers within their documented range; anything else (negative, fractional,
|
|
12
|
-
* NaN, Infinity, too large) makes the constructor throw a
|
|
12
|
+
* NaN, Infinity, too large) makes the constructor throw a PegelValidationError, as
|
|
13
|
+
* does a `transport` or `sleep` that is not a function and a `headers` value an HTTP
|
|
14
|
+
* header can't carry.
|
|
13
15
|
*/
|
|
14
16
|
export interface EngineOptions {
|
|
15
17
|
/** Base URL of the API. Defaults to https://www.pegelonline.wsv.de */
|
|
@@ -32,15 +34,16 @@ export interface EngineOptions {
|
|
|
32
34
|
*/
|
|
33
35
|
timeoutMs?: number;
|
|
34
36
|
/**
|
|
35
|
-
* Number of automatic retries for transient (429/503) responses
|
|
36
|
-
* (
|
|
37
|
-
*
|
|
38
|
-
*
|
|
37
|
+
* Number of automatic retries for transient (429/503) responses and reset
|
|
38
|
+
* connections (ECONNRESET, EPIPE, ECONNABORTED, undici's UND_ERR_SOCKET, anywhere in
|
|
39
|
+
* the error's `cause` chain; GET and HEAD only), 0..`MAX_RETRIES` (10). Each waits
|
|
40
|
+
* `retryDelayMs * attempt`, or longer when the response's `Retry-After` asks (up to
|
|
41
|
+
* `MAX_RETRY_AFTER_MS`; a longer one is not retried). Timeouts are not retried.
|
|
39
42
|
*/
|
|
40
43
|
maxRetries?: number;
|
|
41
44
|
/**
|
|
42
|
-
* Base backoff between retries in milliseconds (grows linearly);
|
|
43
|
-
* Retry-After. At most `MAX_RETRY_AFTER_MS`.
|
|
45
|
+
* Base backoff between retries in milliseconds (grows linearly); the floor of every
|
|
46
|
+
* wait, Retry-After or not. At most `MAX_RETRY_AFTER_MS`.
|
|
44
47
|
*/
|
|
45
48
|
retryDelayMs?: number;
|
|
46
49
|
/**
|
|
@@ -62,8 +65,10 @@ export declare const MAX_RETRIES = 10;
|
|
|
62
65
|
/**
|
|
63
66
|
* Longest `Retry-After` the engine waits out before retrying a 429/503. When the
|
|
64
67
|
* server asks for longer, the engine does not retry at all and surfaces the error at
|
|
65
|
-
* once
|
|
66
|
-
* out, and a hostile value must
|
|
68
|
+
* once, naming the requested wait (`PegelApiError.retryAfterMs`): retrying early would
|
|
69
|
+
* only land inside the window the server asked us to wait out, and a hostile value must
|
|
70
|
+
* not stall the CLI. A shorter `Retry-After` never makes a wait shorter than the normal
|
|
71
|
+
* backoff.
|
|
67
72
|
*/
|
|
68
73
|
export declare const MAX_RETRY_AFTER_MS = 30000;
|
|
69
74
|
/**
|
|
@@ -77,6 +82,13 @@ export declare const MAX_RETRY_AFTER_MS = 30000;
|
|
|
77
82
|
* read `"1.5"` as a date in 2001 and retry at once.
|
|
78
83
|
*/
|
|
79
84
|
export declare function parseRetryAfter(header: string | string[] | undefined, now?: number): number | undefined;
|
|
85
|
+
/**
|
|
86
|
+
* True for a PegelNetworkError caused by a reset or aborted connection, which the
|
|
87
|
+
* engine retries — whichever transport raised it (a Node error, fetch's TypeError with
|
|
88
|
+
* an undici cause). A refused connection, a DNS failure or a timeout is not transient
|
|
89
|
+
* in that sense and is not retried.
|
|
90
|
+
*/
|
|
91
|
+
export declare function isTransientNetworkError(err: unknown): boolean;
|
|
80
92
|
/**
|
|
81
93
|
* Check a base URL against the library's rules (baseUrlProblem: no whitespace or
|
|
82
94
|
* control characters, an absolute http(s) URL, no query or fragment) and return it
|
|
@@ -87,10 +99,9 @@ export declare function parseRetryAfter(header: string | string[] | undefined, n
|
|
|
87
99
|
*/
|
|
88
100
|
export declare function validateBaseUrl(raw: string): string;
|
|
89
101
|
export declare class RequestEngine {
|
|
90
|
-
private
|
|
102
|
+
#private;
|
|
91
103
|
private readonly transport;
|
|
92
104
|
private readonly userAgent;
|
|
93
|
-
private readonly extraHeaders;
|
|
94
105
|
private readonly timeoutMs;
|
|
95
106
|
private readonly maxRetries;
|
|
96
107
|
private readonly retryDelayMs;
|
|
@@ -101,7 +112,7 @@ export declare class RequestEngine {
|
|
|
101
112
|
/**
|
|
102
113
|
* Build a fully-qualified URL from a path and optional query parameters.
|
|
103
114
|
*
|
|
104
|
-
* Throws a
|
|
115
|
+
* Throws a PegelValidationError for a path with a "." or ".." segment. The resource methods
|
|
105
116
|
* put ids into the path with `encodeURIComponent`, which leaves those two
|
|
106
117
|
* unchanged, and URL parsing then resolves them: `currentMeasurement("BONN", "..")`
|
|
107
118
|
* would request `/stations/currentmeasurement.json` (a station of that name).
|
|
@@ -109,6 +120,25 @@ export declare class RequestEngine {
|
|
|
109
120
|
* encodeURIComponent turns their "%" into "%25".)
|
|
110
121
|
*/
|
|
111
122
|
buildUrl(path: string, query?: QueryParams): string;
|
|
123
|
+
/**
|
|
124
|
+
* `text` without the base URL's credentials: server text (an error body that echoes
|
|
125
|
+
* the request URL) and transport text (fetch's "Failed to fetch <url>") can carry them.
|
|
126
|
+
*/
|
|
127
|
+
private scrub;
|
|
128
|
+
/**
|
|
129
|
+
* A transport failure as the `cause` of the error the engine raises: the original
|
|
130
|
+
* when its text carries no credentials, otherwise a copy with them scrubbed (message,
|
|
131
|
+
* `code` and the cause chain kept), so logging the error with its causes can't reveal
|
|
132
|
+
* the base URL's password.
|
|
133
|
+
*/
|
|
134
|
+
private scrubCause;
|
|
135
|
+
/**
|
|
136
|
+
* Call the transport under the overall deadline (`timeoutMs`): the request gets an
|
|
137
|
+
* AbortSignal that fires at the deadline, and the call rejects then whether the
|
|
138
|
+
* transport stops or not — a custom transport (fetch, a node:http wrapper) that
|
|
139
|
+
* ignores `timeoutMs` can't hang the caller. A synchronous throw becomes a rejection.
|
|
140
|
+
*/
|
|
141
|
+
private callTransport;
|
|
112
142
|
/** Perform a request with Accept negotiation and transient-error retries. */
|
|
113
143
|
request(method: string, path: string, options?: {
|
|
114
144
|
query?: QueryParams;
|