@maschinenlesbar.org/pegel-online-cli 0.0.7 → 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/README.md +26 -14
- package/dist/src/cli/commands/stations.d.ts.map +1 -1
- package/dist/src/cli/commands/stations.js +6 -5
- package/dist/src/cli/commands/stations.js.map +1 -1
- package/dist/src/cli/commands/timeseries.d.ts.map +1 -1
- package/dist/src/cli/commands/timeseries.js +10 -11
- package/dist/src/cli/commands/timeseries.js.map +1 -1
- package/dist/src/cli/index.js +2 -0
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/cli/io.d.ts +17 -0
- package/dist/src/cli/io.d.ts.map +1 -1
- package/dist/src/cli/io.js +22 -0
- package/dist/src/cli/io.js.map +1 -1
- package/dist/src/cli/program.d.ts.map +1 -1
- package/dist/src/cli/program.js +4 -3
- package/dist/src/cli/program.js.map +1 -1
- package/dist/src/cli/shared.d.ts +19 -6
- package/dist/src/cli/shared.d.ts.map +1 -1
- package/dist/src/cli/shared.js +66 -15
- package/dist/src/cli/shared.js.map +1 -1
- package/dist/src/client/client.d.ts.map +1 -1
- package/dist/src/client/client.js +51 -15
- package/dist/src/client/client.js.map +1 -1
- package/dist/src/client/engine.d.ts +51 -5
- package/dist/src/client/engine.d.ts.map +1 -1
- package/dist/src/client/engine.js +157 -39
- package/dist/src/client/engine.js.map +1 -1
- package/dist/src/client/errors.d.ts +15 -0
- package/dist/src/client/errors.d.ts.map +1 -1
- package/dist/src/client/errors.js +42 -3
- package/dist/src/client/errors.js.map +1 -1
- package/dist/src/client/http.js +3 -3
- package/dist/src/client/http.js.map +1 -1
- package/dist/src/client/index.d.ts +2 -2
- package/dist/src/client/index.d.ts.map +1 -1
- package/dist/src/client/index.js +2 -2
- package/dist/src/client/index.js.map +1 -1
- package/dist/src/client/types.d.ts +29 -2
- package/dist/src/client/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/client/client.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,mEAAmE;AACnE,EAAE;AACF,8CAA8C;AAC9C,qEAAqE;AACrE,sDAAsD;AACtD,kEAAkE;AAElE,OAAO,EAAE,aAAa,EAAsB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/client/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,6 +6,11 @@ export interface RawResponse {
|
|
|
6
6
|
contentType: string;
|
|
7
7
|
status: number;
|
|
8
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* Options for {@link RequestEngine} and the client. The numeric options must be
|
|
11
|
+
* integers within their documented range; anything else (negative, fractional,
|
|
12
|
+
* NaN, Infinity, too large) makes the constructor throw a PegelError.
|
|
13
|
+
*/
|
|
9
14
|
export interface EngineOptions {
|
|
10
15
|
/** Base URL of the API. Defaults to https://www.pegelonline.wsv.de */
|
|
11
16
|
baseUrl?: string;
|
|
@@ -23,14 +28,26 @@ export interface EngineOptions {
|
|
|
23
28
|
headers?: Record<string, string>;
|
|
24
29
|
/**
|
|
25
30
|
* Time limit per request in milliseconds, covering the whole response body, not
|
|
26
|
-
* only idle gaps (0 disables;
|
|
31
|
+
* only idle gaps (0 disables; at most `MAX_TIMEOUT_MS`, 2^31 - 1 ms).
|
|
27
32
|
*/
|
|
28
33
|
timeoutMs?: number;
|
|
29
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* Number of automatic retries for transient (429/503) responses, 0..`MAX_RETRIES`
|
|
36
|
+
* (10). Each waits the
|
|
37
|
+
* response's `Retry-After` (up to `MAX_RETRY_AFTER_MS`; a longer one is not
|
|
38
|
+
* retried), or else `retryDelayMs * attempt`.
|
|
39
|
+
*/
|
|
30
40
|
maxRetries?: number;
|
|
31
|
-
/**
|
|
41
|
+
/**
|
|
42
|
+
* Base backoff between retries in milliseconds (grows linearly); used without a
|
|
43
|
+
* Retry-After. At most `MAX_RETRY_AFTER_MS`.
|
|
44
|
+
*/
|
|
32
45
|
retryDelayMs?: number;
|
|
33
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* Number of HTTP redirects (301/302/303/307/308) to follow, 0..20. Defaults to 5. Any
|
|
48
|
+
* other 3xx, one with a missing or malformed Location, and one past this limit
|
|
49
|
+
* surface as a PegelApiError naming the target.
|
|
50
|
+
*/
|
|
34
51
|
maxRedirects?: number;
|
|
35
52
|
/**
|
|
36
53
|
* Hard cap on response body size in bytes (defends against memory exhaustion
|
|
@@ -40,6 +57,26 @@ export interface EngineOptions {
|
|
|
40
57
|
/** Injectable sleep, primarily for deterministic tests. */
|
|
41
58
|
sleep?: (ms: number) => Promise<void>;
|
|
42
59
|
}
|
|
60
|
+
/** Most automatic retries a caller may ask for (the CLI's --max-retries shares it). */
|
|
61
|
+
export declare const MAX_RETRIES = 10;
|
|
62
|
+
/**
|
|
63
|
+
* Longest `Retry-After` the engine waits out before retrying a 429/503. When the
|
|
64
|
+
* server asks for longer, the engine does not retry at all and surfaces the error at
|
|
65
|
+
* once: retrying early would only land inside the window the server asked us to wait
|
|
66
|
+
* out, and a hostile value must not stall the CLI.
|
|
67
|
+
*/
|
|
68
|
+
export declare const MAX_RETRY_AFTER_MS = 30000;
|
|
69
|
+
/**
|
|
70
|
+
* Parse a `Retry-After` header into a delay in milliseconds (RFC 9110 §10.2.3):
|
|
71
|
+
* either delay-seconds (`"120"`) or an HTTP-date (`"Wed, 21 Oct 2026 07:28:00 GMT"`,
|
|
72
|
+
* turned into the time left from `now`; a date in the past gives 0).
|
|
73
|
+
*
|
|
74
|
+
* Returns `undefined` when the header is absent or malformed — negative (`"-1"`),
|
|
75
|
+
* fractional (`"1.5"`), padded inside, any other date format — so the caller falls
|
|
76
|
+
* back to its own backoff. The strict patterns matter: `Date.parse` alone would
|
|
77
|
+
* read `"1.5"` as a date in 2001 and retry at once.
|
|
78
|
+
*/
|
|
79
|
+
export declare function parseRetryAfter(header: string | string[] | undefined, now?: number): number | undefined;
|
|
43
80
|
export declare class RequestEngine {
|
|
44
81
|
private readonly baseUrl;
|
|
45
82
|
private readonly transport;
|
|
@@ -52,7 +89,16 @@ export declare class RequestEngine {
|
|
|
52
89
|
private readonly maxResponseBytes;
|
|
53
90
|
private readonly sleep;
|
|
54
91
|
constructor(options?: EngineOptions);
|
|
55
|
-
/**
|
|
92
|
+
/**
|
|
93
|
+
* Build a fully-qualified URL from a path and optional query parameters.
|
|
94
|
+
*
|
|
95
|
+
* Throws a PegelError for a path with a "." or ".." segment. The resource methods
|
|
96
|
+
* put ids into the path with `encodeURIComponent`, which leaves those two
|
|
97
|
+
* unchanged, and URL parsing then resolves them: `currentMeasurement("BONN", "..")`
|
|
98
|
+
* would request `/stations/currentmeasurement.json` (a station of that name).
|
|
99
|
+
* Neither can name a resource. (Percent-encoded forms such as "%2e%2e" are safe:
|
|
100
|
+
* encodeURIComponent turns their "%" into "%25".)
|
|
101
|
+
*/
|
|
56
102
|
buildUrl(path: string, query?: QueryParams): string;
|
|
57
103
|
/** Perform a request with Accept negotiation and transient-error retries. */
|
|
58
104
|
request(method: string, path: string, options?: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../src/client/engine.ts"],"names":[],"mappings":"AAIA,OAAO,
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../src/client/engine.ts"],"names":[],"mappings":"AAIA,OAAO,EAAqC,KAAK,SAAS,EAAE,MAAM,WAAW,CAAC;AAC9E,OAAO,EAAoB,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAGhE,eAAO,MAAM,gBAAgB,mCAAmC,CAAC;AAGjE,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,2DAA2D;IAC3D,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAID,uFAAuF;AACvF,eAAO,MAAM,WAAW,KAAK,CAAC;AA2B9B;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,QAAS,CAAC;AAMzC;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,EACrC,GAAG,GAAE,MAAmB,GACvB,MAAM,GAAG,SAAS,CAOpB;AA4ED,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAyB;IACtD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgC;IAEtD,YAAY,OAAO,GAAE,aAAkB,EA2BtC;IAED;;;;;;;;;OASG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,MAAM,CAUlD;IAED,6EAA6E;IACvE,OAAO,CACX,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;QAAE,KAAK,CAAC,EAAE,WAAW,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAmC,GAChF,OAAO,CAAC,WAAW,CAAC,CAgEtB;IAED,0DAA0D;IACpD,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAQ9D;IAED,OAAO,CAAC,UAAU;CAiCnB"}
|
|
@@ -1,12 +1,65 @@
|
|
|
1
1
|
// The request engine: turns logical (method, path, query) calls into HTTP
|
|
2
2
|
// requests via a Transport, applies retry/backoff for transient statuses
|
|
3
3
|
// (429, 503), and decodes responses.
|
|
4
|
-
import { nodeHttpTransport } from "./http.js";
|
|
4
|
+
import { MAX_TIMEOUT_MS, nodeHttpTransport } from "./http.js";
|
|
5
5
|
import { buildQueryString } from "./query.js";
|
|
6
|
-
import { PegelApiError, PegelError, PegelNetworkError, PegelParseError } from "./errors.js";
|
|
6
|
+
import { PegelApiError, PegelError, PegelNetworkError, PegelParseError, redactUrl } from "./errors.js";
|
|
7
7
|
export const DEFAULT_BASE_URL = "https://www.pegelonline.wsv.de";
|
|
8
8
|
const DEFAULT_USER_AGENT = "pegel-online-cli";
|
|
9
9
|
const DEFAULT_MAX_RESPONSE_BYTES = 100 * 1024 * 1024;
|
|
10
|
+
/** Most automatic retries a caller may ask for (the CLI's --max-retries shares it). */
|
|
11
|
+
export const MAX_RETRIES = 10;
|
|
12
|
+
/** Most redirects a caller may let the engine follow (the Fetch standard's limit). */
|
|
13
|
+
const MAX_REDIRECTS = 20;
|
|
14
|
+
/**
|
|
15
|
+
* Read a numeric engine option: `undefined` gives the default; anything but an
|
|
16
|
+
* integer in [0, max] throws. Without this a negative or NaN `timeoutMs` silently
|
|
17
|
+
* disabled the timeout, and `maxRetries: NaN` silently meant no retries.
|
|
18
|
+
*/
|
|
19
|
+
function intOption(name, value, fallback, max) {
|
|
20
|
+
if (value === undefined)
|
|
21
|
+
return fallback;
|
|
22
|
+
if (!Number.isSafeInteger(value) || value < 0 || value > max) {
|
|
23
|
+
throw new PegelError(`Invalid option ${name}: expected an integer from 0 to ${max}, got ${String(value)}.`);
|
|
24
|
+
}
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The redirect statuses the engine follows. 300 (a choice for the user), 304 (a
|
|
29
|
+
* cache answer to a conditional request this client never sends) and 305/306
|
|
30
|
+
* (deprecated) are not redirects to follow; they surface as a PegelApiError.
|
|
31
|
+
*/
|
|
32
|
+
const FOLLOWED_REDIRECTS = new Set([301, 302, 303, 307, 308]);
|
|
33
|
+
/**
|
|
34
|
+
* Longest `Retry-After` the engine waits out before retrying a 429/503. When the
|
|
35
|
+
* server asks for longer, the engine does not retry at all and surfaces the error at
|
|
36
|
+
* once: retrying early would only land inside the window the server asked us to wait
|
|
37
|
+
* out, and a hostile value must not stall the CLI.
|
|
38
|
+
*/
|
|
39
|
+
export const MAX_RETRY_AFTER_MS = 30_000;
|
|
40
|
+
/** An IMF-fixdate (RFC 9110 §5.6.7), the one HTTP-date form senders must generate. */
|
|
41
|
+
const IMF_FIXDATE = /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun), \d{2} (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{4} \d{2}:\d{2}:\d{2} GMT$/;
|
|
42
|
+
/**
|
|
43
|
+
* Parse a `Retry-After` header into a delay in milliseconds (RFC 9110 §10.2.3):
|
|
44
|
+
* either delay-seconds (`"120"`) or an HTTP-date (`"Wed, 21 Oct 2026 07:28:00 GMT"`,
|
|
45
|
+
* turned into the time left from `now`; a date in the past gives 0).
|
|
46
|
+
*
|
|
47
|
+
* Returns `undefined` when the header is absent or malformed — negative (`"-1"`),
|
|
48
|
+
* fractional (`"1.5"`), padded inside, any other date format — so the caller falls
|
|
49
|
+
* back to its own backoff. The strict patterns matter: `Date.parse` alone would
|
|
50
|
+
* read `"1.5"` as a date in 2001 and retry at once.
|
|
51
|
+
*/
|
|
52
|
+
export function parseRetryAfter(header, now = Date.now()) {
|
|
53
|
+
const value = (Array.isArray(header) ? header[0] : header)?.trim();
|
|
54
|
+
if (value === undefined || value === "")
|
|
55
|
+
return undefined;
|
|
56
|
+
if (/^\d+$/.test(value))
|
|
57
|
+
return Number(value) * 1000;
|
|
58
|
+
if (!IMF_FIXDATE.test(value))
|
|
59
|
+
return undefined;
|
|
60
|
+
const when = Date.parse(value);
|
|
61
|
+
return Number.isNaN(when) ? undefined : Math.max(0, when - now);
|
|
62
|
+
}
|
|
10
63
|
/**
|
|
11
64
|
* Credential-bearing headers that must never be carried across an origin boundary
|
|
12
65
|
* on a redirect. Stored lower-cased and compared case-insensitively so a header
|
|
@@ -51,10 +104,14 @@ function sanitizeServerText(text) {
|
|
|
51
104
|
return out;
|
|
52
105
|
}
|
|
53
106
|
/**
|
|
54
|
-
* Reject a base URL whose scheme is not http(s)
|
|
55
|
-
* gates
|
|
56
|
-
*
|
|
57
|
-
* too (a `file:`/`ftp:` base URL fails
|
|
107
|
+
* Reject a base URL whose scheme is not http(s), or that has a query or fragment.
|
|
108
|
+
* The default transport already gates the scheme per hop, but the engine is
|
|
109
|
+
* exported as a library and may be handed a custom transport that does no such
|
|
110
|
+
* check, so gate the configured base URL here too (a `file:`/`ftp:` base URL fails
|
|
111
|
+
* fast with a typed error). Request paths are appended to the base URL as a string,
|
|
112
|
+
* so a `?` or `#` in it would swallow every path: `http://h/?x=1` requests
|
|
113
|
+
* `/?x=1/webservices/...` and `http://h/#f` requests `/`. Userinfo is allowed (Node
|
|
114
|
+
* sends it as Basic auth) but redacted in every message.
|
|
58
115
|
*/
|
|
59
116
|
function assertHttpScheme(baseUrl) {
|
|
60
117
|
let url;
|
|
@@ -62,10 +119,13 @@ function assertHttpScheme(baseUrl) {
|
|
|
62
119
|
url = new URL(baseUrl);
|
|
63
120
|
}
|
|
64
121
|
catch {
|
|
65
|
-
throw new PegelNetworkError(`Invalid base URL: ${baseUrl}`);
|
|
122
|
+
throw new PegelNetworkError(`Invalid base URL: ${redactUrl(baseUrl)}`);
|
|
66
123
|
}
|
|
67
124
|
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
68
|
-
throw new PegelNetworkError(`Unsupported protocol "${url.protocol}" in base URL: ${baseUrl}`);
|
|
125
|
+
throw new PegelNetworkError(`Unsupported protocol "${url.protocol}" in base URL: ${redactUrl(baseUrl)}`);
|
|
126
|
+
}
|
|
127
|
+
if (/[?#]/.test(baseUrl)) {
|
|
128
|
+
throw new PegelNetworkError(`Base URL must not contain a query or fragment: ${redactUrl(baseUrl)}`);
|
|
69
129
|
}
|
|
70
130
|
}
|
|
71
131
|
const realSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -85,23 +145,40 @@ export class RequestEngine {
|
|
|
85
145
|
assertHttpScheme(this.baseUrl);
|
|
86
146
|
this.transport = options.transport ?? nodeHttpTransport;
|
|
87
147
|
this.userAgent = options.userAgent ?? DEFAULT_USER_AGENT;
|
|
88
|
-
// Reject
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
-
|
|
148
|
+
// Reject what Node's header validation would throw a raw TypeError for (it
|
|
149
|
+
// would surface as an "Unexpected error") with a typed error up front: control
|
|
150
|
+
// characters (CR/LF in particular, which also closes header injection; tab is
|
|
151
|
+
// allowed, as in HTTP) and characters above U+00FF.
|
|
152
|
+
if (/[\x00-\x08\x0a-\x1f\x7f]/.test(this.userAgent)) {
|
|
92
153
|
throw new PegelError("Invalid User-Agent: control characters are not allowed.");
|
|
93
154
|
}
|
|
155
|
+
if (/[^\x00-\xff]/.test(this.userAgent)) {
|
|
156
|
+
throw new PegelError("Invalid User-Agent: characters outside Latin-1 (above U+00FF) are not allowed.");
|
|
157
|
+
}
|
|
94
158
|
this.extraHeaders = options.headers ?? {};
|
|
95
|
-
this.timeoutMs = options.timeoutMs
|
|
96
|
-
this.maxRetries = options.maxRetries
|
|
97
|
-
this.retryDelayMs = options.retryDelayMs
|
|
98
|
-
this.maxRedirects = options.maxRedirects
|
|
99
|
-
this.maxResponseBytes = options.maxResponseBytes
|
|
159
|
+
this.timeoutMs = intOption("timeoutMs", options.timeoutMs, 30_000, MAX_TIMEOUT_MS);
|
|
160
|
+
this.maxRetries = intOption("maxRetries", options.maxRetries, 2, MAX_RETRIES);
|
|
161
|
+
this.retryDelayMs = intOption("retryDelayMs", options.retryDelayMs, 200, MAX_RETRY_AFTER_MS);
|
|
162
|
+
this.maxRedirects = intOption("maxRedirects", options.maxRedirects, 5, MAX_REDIRECTS);
|
|
163
|
+
this.maxResponseBytes = intOption("maxResponseBytes", options.maxResponseBytes, DEFAULT_MAX_RESPONSE_BYTES, Number.MAX_SAFE_INTEGER);
|
|
100
164
|
this.sleep = options.sleep ?? realSleep;
|
|
101
165
|
}
|
|
102
|
-
/**
|
|
166
|
+
/**
|
|
167
|
+
* Build a fully-qualified URL from a path and optional query parameters.
|
|
168
|
+
*
|
|
169
|
+
* Throws a PegelError for a path with a "." or ".." segment. The resource methods
|
|
170
|
+
* put ids into the path with `encodeURIComponent`, which leaves those two
|
|
171
|
+
* unchanged, and URL parsing then resolves them: `currentMeasurement("BONN", "..")`
|
|
172
|
+
* would request `/stations/currentmeasurement.json` (a station of that name).
|
|
173
|
+
* Neither can name a resource. (Percent-encoded forms such as "%2e%2e" are safe:
|
|
174
|
+
* encodeURIComponent turns their "%" into "%25".)
|
|
175
|
+
*/
|
|
103
176
|
buildUrl(path, query) {
|
|
104
177
|
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
178
|
+
const dotSegment = normalizedPath.split("/").find((s) => s === "." || s === "..");
|
|
179
|
+
if (dotSegment !== undefined) {
|
|
180
|
+
throw new PegelError(`Invalid path segment "${dotSegment}" in ${normalizedPath}: "." and ".." cannot be used as an id.`);
|
|
181
|
+
}
|
|
105
182
|
const qs = query ? buildQueryString(query) : "";
|
|
106
183
|
return `${this.baseUrl}${normalizedPath}${qs ? `?${qs}` : ""}`;
|
|
107
184
|
}
|
|
@@ -127,31 +204,41 @@ export class RequestEngine {
|
|
|
127
204
|
const status = response.status;
|
|
128
205
|
const retryable = status === 429 || status === 503;
|
|
129
206
|
if (retryable && attempt < this.maxRetries) {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
207
|
+
// Honour Retry-After; without a usable one, back off linearly. A Retry-After
|
|
208
|
+
// beyond MAX_RETRY_AFTER_MS is not retried: the error below surfaces at once.
|
|
209
|
+
const retryAfter = parseRetryAfter(response.headers["retry-after"]);
|
|
210
|
+
if (retryAfter === undefined || retryAfter <= MAX_RETRY_AFTER_MS) {
|
|
211
|
+
attempt += 1;
|
|
212
|
+
await this.sleep(retryAfter ?? this.retryDelayMs * attempt);
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
133
215
|
}
|
|
134
216
|
// Follow redirects, resolving the Location relative to the current URL.
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
217
|
+
const location = response.headers["location"];
|
|
218
|
+
const next = FOLLOWED_REDIRECTS.has(status) ? resolveLocation(location, url) : undefined;
|
|
219
|
+
if (next !== undefined && redirects >= this.maxRedirects) {
|
|
220
|
+
// A loop (or a long chain): say how far it got rather than a bare 3xx.
|
|
221
|
+
// (With maxRedirects 0 nothing was followed; the plain text says enough.)
|
|
222
|
+
throw this.toApiError(method, url, status, response.body, location, redirects || undefined);
|
|
223
|
+
}
|
|
224
|
+
if (next !== undefined) {
|
|
225
|
+
// Security: never carry credential-bearing headers across an origin
|
|
226
|
+
// boundary. The CLI sends none today, but this guards a future
|
|
227
|
+
// Authorization/Cookie/X-Api-Key header from leaking to an
|
|
228
|
+
// attacker-controlled redirect target. Comparing full origin (scheme +
|
|
229
|
+
// host + port) also strips on a same-host https->http downgrade.
|
|
230
|
+
if (next.origin !== new URL(url).origin) {
|
|
231
|
+
stripSensitiveHeaders(headers);
|
|
150
232
|
}
|
|
233
|
+
url = next.toString();
|
|
234
|
+
redirects += 1;
|
|
235
|
+
continue;
|
|
151
236
|
}
|
|
237
|
+
// Any other 3xx — not a followed status, or no usable Location — falls
|
|
238
|
+
// through and surfaces as a PegelApiError naming the target.
|
|
152
239
|
const contentType = String(response.headers["content-type"] ?? "");
|
|
153
240
|
if (status < 200 || status >= 300) {
|
|
154
|
-
throw this.toApiError(method, url, status, response.body);
|
|
241
|
+
throw this.toApiError(method, url, status, response.body, location);
|
|
155
242
|
}
|
|
156
243
|
return { data: response.body, contentType, status };
|
|
157
244
|
}
|
|
@@ -167,7 +254,7 @@ export class RequestEngine {
|
|
|
167
254
|
throw new PegelParseError(`Failed to parse JSON response from ${path}`, { cause });
|
|
168
255
|
}
|
|
169
256
|
}
|
|
170
|
-
toApiError(method, url, status, body) {
|
|
257
|
+
toApiError(method, url, status, body, locationHeader, redirectsFollowed) {
|
|
171
258
|
const text = body.toString("utf8");
|
|
172
259
|
let detail;
|
|
173
260
|
try {
|
|
@@ -184,7 +271,38 @@ export class RequestEngine {
|
|
|
184
271
|
// endpoint cannot inject terminal escape sequences via the stderr error message.
|
|
185
272
|
if (detail !== undefined)
|
|
186
273
|
detail = sanitizeServerText(detail);
|
|
187
|
-
|
|
274
|
+
// Name the target of a redirect that was not followed.
|
|
275
|
+
const location = status >= 300 && status < 400 && locationHeader ? redirectTarget(url, locationHeader) : undefined;
|
|
276
|
+
return new PegelApiError({
|
|
277
|
+
status,
|
|
278
|
+
url,
|
|
279
|
+
method,
|
|
280
|
+
body: text,
|
|
281
|
+
detail,
|
|
282
|
+
...(location !== undefined ? { location } : {}),
|
|
283
|
+
...(redirectsFollowed !== undefined ? { redirectsFollowed } : {}),
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
/** Resolve a Location header against the current URL; undefined if missing or malformed. */
|
|
288
|
+
function resolveLocation(location, base) {
|
|
289
|
+
if (location === undefined || location === "")
|
|
290
|
+
return undefined;
|
|
291
|
+
try {
|
|
292
|
+
return new URL(location, base);
|
|
293
|
+
}
|
|
294
|
+
catch {
|
|
295
|
+
return undefined;
|
|
188
296
|
}
|
|
189
297
|
}
|
|
298
|
+
/**
|
|
299
|
+
* The absolute, printable form of a `Location` header: resolved against the request
|
|
300
|
+
* URL, userinfo redacted, control characters stripped (it is server text bound for
|
|
301
|
+
* stderr). An unparseable value is shown sanitised as it came.
|
|
302
|
+
*/
|
|
303
|
+
function redirectTarget(requestUrl, location) {
|
|
304
|
+
const resolved = resolveLocation(location, requestUrl);
|
|
305
|
+
const clean = sanitizeServerText(resolved ? redactUrl(resolved.href) : location).trim();
|
|
306
|
+
return clean === "" ? undefined : clean;
|
|
307
|
+
}
|
|
190
308
|
//# sourceMappingURL=engine.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../../src/client/engine.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,yEAAyE;AACzE,qCAAqC;AAErC,OAAO,EAAE,iBAAiB,EAAkB,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../../src/client/engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,12 @@ export declare class PegelError extends Error {
|
|
|
4
4
|
cause?: unknown;
|
|
5
5
|
});
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* Replace the userinfo of a URL (`https://user:secret@host/...`) with `***`, so a
|
|
9
|
+
* credential in a base URL never reaches an error message, a log or CI output.
|
|
10
|
+
* A URL without userinfo, or one that does not parse, is returned unchanged.
|
|
11
|
+
*/
|
|
12
|
+
export declare function redactUrl(url: string): string;
|
|
7
13
|
/**
|
|
8
14
|
* The API responded with a non-2xx status code. `detail` holds a human-readable
|
|
9
15
|
* message extracted from the response body when one is present.
|
|
@@ -14,12 +20,21 @@ export declare class PegelApiError extends PegelError {
|
|
|
14
20
|
readonly url: string;
|
|
15
21
|
readonly method: string;
|
|
16
22
|
readonly body: string;
|
|
23
|
+
/**
|
|
24
|
+
* For a 3xx that was not followed (not a followed status, a malformed Location,
|
|
25
|
+
* or past `maxRedirects`): the redirect target, absolute, sanitised, userinfo
|
|
26
|
+
* redacted. The message names it.
|
|
27
|
+
*/
|
|
28
|
+
readonly location: string | undefined;
|
|
17
29
|
constructor(args: {
|
|
18
30
|
status: number;
|
|
19
31
|
url: string;
|
|
20
32
|
method: string;
|
|
21
33
|
body: string;
|
|
22
34
|
detail?: string;
|
|
35
|
+
location?: string;
|
|
36
|
+
/** Redirects already followed when the limit stopped this one (> 0 only). */
|
|
37
|
+
redirectsFollowed?: number;
|
|
23
38
|
});
|
|
24
39
|
/** True for statuses the API documents as transient and retry-able. */
|
|
25
40
|
get isRetryable(): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/client/errors.ts"],"names":[],"mappings":"AAGA,+DAA+D;AAC/D,qBAAa,UAAW,SAAQ,KAAK;IACnC,YAAY,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,EAGzD;CACF;AAED;;;GAGG;AACH,qBAAa,aAAc,SAAQ,UAAU;IAC3C,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;
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/client/errors.ts"],"names":[],"mappings":"AAGA,+DAA+D;AAC/D,qBAAa,UAAW,SAAQ,KAAK;IACnC,YAAY,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,EAGzD;CACF;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAW7C;AAED;;;GAGG;AACH,qBAAa,aAAc,SAAQ,UAAU;IAC3C,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;IACtB;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAEtC,YAAY,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;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,6EAA6E;QAC7E,iBAAiB,CAAC,EAAE,MAAM,CAAC;KAC5B,EAwBA;IAED,uEAAuE;IACvE,IAAI,WAAW,IAAI,OAAO,CAEzB;CACF;AAED,uEAAuE;AACvE,qBAAa,iBAAkB,SAAQ,UAAU;CAAG;AAEpD,wEAAwE;AACxE,qBAAa,eAAgB,SAAQ,UAAU;CAAG"}
|
|
@@ -7,6 +7,25 @@ export class PegelError extends Error {
|
|
|
7
7
|
this.name = new.target.name;
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* Replace the userinfo of a URL (`https://user:secret@host/...`) with `***`, so a
|
|
12
|
+
* credential in a base URL never reaches an error message, a log or CI output.
|
|
13
|
+
* A URL without userinfo, or one that does not parse, is returned unchanged.
|
|
14
|
+
*/
|
|
15
|
+
export function redactUrl(url) {
|
|
16
|
+
let parsed;
|
|
17
|
+
try {
|
|
18
|
+
parsed = new URL(url);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return url;
|
|
22
|
+
}
|
|
23
|
+
if (parsed.username === "" && parsed.password === "")
|
|
24
|
+
return url;
|
|
25
|
+
parsed.username = "***";
|
|
26
|
+
parsed.password = "";
|
|
27
|
+
return parsed.href;
|
|
28
|
+
}
|
|
10
29
|
/**
|
|
11
30
|
* The API responded with a non-2xx status code. `detail` holds a human-readable
|
|
12
31
|
* message extracted from the response body when one is present.
|
|
@@ -17,14 +36,34 @@ export class PegelApiError extends PegelError {
|
|
|
17
36
|
url;
|
|
18
37
|
method;
|
|
19
38
|
body;
|
|
39
|
+
/**
|
|
40
|
+
* For a 3xx that was not followed (not a followed status, a malformed Location,
|
|
41
|
+
* or past `maxRedirects`): the redirect target, absolute, sanitised, userinfo
|
|
42
|
+
* redacted. The message names it.
|
|
43
|
+
*/
|
|
44
|
+
location;
|
|
20
45
|
constructor(args) {
|
|
21
|
-
const
|
|
22
|
-
|
|
46
|
+
const parts = [];
|
|
47
|
+
if (args.detail)
|
|
48
|
+
parts.push(args.detail);
|
|
49
|
+
if (args.status >= 300 && args.status < 400) {
|
|
50
|
+
const limit = args.redirectsFollowed !== undefined && args.redirectsFollowed > 0
|
|
51
|
+
? ` (stopped after ${args.redirectsFollowed} redirects)`
|
|
52
|
+
: "";
|
|
53
|
+
parts.push(args.location
|
|
54
|
+
? `redirect to ${args.location} not followed${limit}`
|
|
55
|
+
: "redirect not followed (no Location header)");
|
|
56
|
+
}
|
|
57
|
+
const detailPart = parts.length > 0 ? `: ${parts.join("; ")}` : "";
|
|
58
|
+
// The URL is shown without userinfo: a credential in --base-url must not leak.
|
|
59
|
+
const url = redactUrl(args.url);
|
|
60
|
+
super(`HTTP ${args.status} for ${args.method} ${url}${detailPart}`);
|
|
23
61
|
this.status = args.status;
|
|
24
|
-
this.url =
|
|
62
|
+
this.url = url;
|
|
25
63
|
this.method = args.method;
|
|
26
64
|
this.body = args.body;
|
|
27
65
|
this.detail = args.detail;
|
|
66
|
+
this.location = args.location;
|
|
28
67
|
}
|
|
29
68
|
/** True for statuses the API documents as transient and retry-able. */
|
|
30
69
|
get isRetryable() {
|
|
@@ -1 +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,UAAW,SAAQ,KAAK;IACnC,YAAY,OAAe,EAAE,OAA6B;QACxD,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC;IAC9B,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,OAAO,aAAc,SAAQ,UAAU;IAClC,MAAM,CAAS;IACf,MAAM,CAAqB;IAC3B,GAAG,CAAS;IACZ,MAAM,CAAS;IACf,IAAI,CAAS;
|
|
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,UAAW,SAAQ,KAAK;IACnC,YAAY,OAAe,EAAE,OAA6B;QACxD,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC;IAC9B,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,GAAW;IACnC,IAAI,MAAW,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CAAC;IACb,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,KAAK,EAAE,IAAI,MAAM,CAAC,QAAQ,KAAK,EAAE;QAAE,OAAO,GAAG,CAAC;IACjE,MAAM,CAAC,QAAQ,GAAG,KAAK,CAAC;IACxB,MAAM,CAAC,QAAQ,GAAG,EAAE,CAAC;IACrB,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAED;;;GAGG;AACH,MAAM,OAAO,aAAc,SAAQ,UAAU;IAClC,MAAM,CAAS;IACf,MAAM,CAAqB;IAC3B,GAAG,CAAS;IACZ,MAAM,CAAS;IACf,IAAI,CAAS;IACtB;;;;OAIG;IACM,QAAQ,CAAqB;IAEtC,YAAY,IASX;QACC,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,IAAI,IAAI,CAAC,MAAM;YAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACzC,IAAI,IAAI,CAAC,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;YAC5C,MAAM,KAAK,GACT,IAAI,CAAC,iBAAiB,KAAK,SAAS,IAAI,IAAI,CAAC,iBAAiB,GAAG,CAAC;gBAChE,CAAC,CAAC,mBAAmB,IAAI,CAAC,iBAAiB,aAAa;gBACxD,CAAC,CAAC,EAAE,CAAC;YACT,KAAK,CAAC,IAAI,CACR,IAAI,CAAC,QAAQ;gBACX,CAAC,CAAC,eAAe,IAAI,CAAC,QAAQ,gBAAgB,KAAK,EAAE;gBACrD,CAAC,CAAC,4CAA4C,CACjD,CAAC;QACJ,CAAC;QACD,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnE,+EAA+E;QAC/E,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAChC,KAAK,CAAC,QAAQ,IAAI,CAAC,MAAM,QAAQ,IAAI,CAAC,MAAM,IAAI,GAAG,GAAG,UAAU,EAAE,CAAC,CAAC;QACpE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,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;QAC1B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;IAChC,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,iBAAkB,SAAQ,UAAU;CAAG;AAEpD,wEAAwE;AACxE,MAAM,OAAO,eAAgB,SAAQ,UAAU;CAAG"}
|
package/dist/src/client/http.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// `http.createServer` in the test-suite.
|
|
8
8
|
import http from "node:http";
|
|
9
9
|
import https from "node:https";
|
|
10
|
-
import { PegelNetworkError } from "./errors.js";
|
|
10
|
+
import { PegelNetworkError, redactUrl } from "./errors.js";
|
|
11
11
|
/**
|
|
12
12
|
* The longest delay Node's timers support (2^31 - 1 ms, about 24.8 days). A longer one
|
|
13
13
|
* prints a TimeoutOverflowWarning and fires after 1 ms, so timeouts are capped here.
|
|
@@ -24,14 +24,14 @@ export const nodeHttpTransport = (request) => new Promise((resolve, reject) => {
|
|
|
24
24
|
url = new URL(request.url);
|
|
25
25
|
}
|
|
26
26
|
catch {
|
|
27
|
-
reject(new PegelNetworkError(`Invalid URL: ${request.url}`));
|
|
27
|
+
reject(new PegelNetworkError(`Invalid URL: ${redactUrl(request.url)}`));
|
|
28
28
|
return;
|
|
29
29
|
}
|
|
30
30
|
// Only http/https are supported. Reject anything else up front with a clear,
|
|
31
31
|
// typed error instead of letting Node throw an opaque ERR_INVALID_PROTOCOL
|
|
32
32
|
// (and so this never reaches the file:/ftp:/etc. drivers).
|
|
33
33
|
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
34
|
-
reject(new PegelNetworkError(`Unsupported protocol "${url.protocol}" in URL: ${request.url}`));
|
|
34
|
+
reject(new PegelNetworkError(`Unsupported protocol "${url.protocol}" in URL: ${redactUrl(request.url)}`));
|
|
35
35
|
return;
|
|
36
36
|
}
|
|
37
37
|
const isHttps = url.protocol === "https:";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../../src/client/http.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,iDAAiD;AACjD,EAAE;AACF,gFAAgF;AAChF,8EAA8E;AAC9E,8EAA8E;AAC9E,yCAAyC;AAEzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,KAAK,MAAM,YAAY,CAAC;AAC/B,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../../src/client/http.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export { PegelOnlineClient } from "./client.js";
|
|
2
|
-
export { RequestEngine, DEFAULT_BASE_URL } from "./engine.js";
|
|
2
|
+
export { RequestEngine, DEFAULT_BASE_URL, MAX_RETRIES, MAX_RETRY_AFTER_MS, parseRetryAfter, } from "./engine.js";
|
|
3
3
|
export type { EngineOptions, RawResponse } from "./engine.js";
|
|
4
4
|
export { MAX_TIMEOUT_MS, nodeHttpTransport } from "./http.js";
|
|
5
5
|
export type { Transport, HttpRequest, HttpResponse } from "./http.js";
|
|
6
6
|
export { buildQueryString } from "./query.js";
|
|
7
7
|
export type { QueryParams, QueryValue } from "./query.js";
|
|
8
|
-
export { PegelError, PegelApiError, PegelNetworkError, PegelParseError } from "./errors.js";
|
|
8
|
+
export { PegelError, PegelApiError, PegelNetworkError, PegelParseError, redactUrl } from "./errors.js";
|
|
9
9
|
export * from "./types.js";
|
|
10
10
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,WAAW,EACX,kBAAkB,EAClB,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9D,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,UAAU,EAAE,aAAa,EAAE,iBAAiB,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAEvG,cAAc,YAAY,CAAC"}
|
package/dist/src/client/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Public entry point for the API client library.
|
|
2
2
|
export { PegelOnlineClient } from "./client.js";
|
|
3
|
-
export { RequestEngine, DEFAULT_BASE_URL } from "./engine.js";
|
|
3
|
+
export { RequestEngine, DEFAULT_BASE_URL, MAX_RETRIES, MAX_RETRY_AFTER_MS, parseRetryAfter, } from "./engine.js";
|
|
4
4
|
export { MAX_TIMEOUT_MS, nodeHttpTransport } from "./http.js";
|
|
5
5
|
export { buildQueryString } from "./query.js";
|
|
6
|
-
export { PegelError, PegelApiError, PegelNetworkError, PegelParseError } from "./errors.js";
|
|
6
|
+
export { PegelError, PegelApiError, PegelNetworkError, PegelParseError, redactUrl } from "./errors.js";
|
|
7
7
|
export * from "./types.js";
|
|
8
8
|
//# sourceMappingURL=index.js.map
|