@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
package/README.md
CHANGED
|
@@ -86,16 +86,20 @@ level); other codes include `Q` (flow/discharge), `WT` (water temperature), and
|
|
|
86
86
|
| `--waters <shortname>` | filter by water shortname (see `waters`) |
|
|
87
87
|
| `--fuzzy-id <id>` | fuzzy match against short/long name |
|
|
88
88
|
| `--include-timeseries` | embed each station's timeseries list |
|
|
89
|
-
| `--include-current` | embed the current measurement |
|
|
90
|
-
| `--include-characteristic` | embed characteristic (gauge-mark) values |
|
|
89
|
+
| `--include-current` | embed the current measurement in each timeseries (implies `--include-timeseries`) |
|
|
90
|
+
| `--include-characteristic` | embed characteristic (gauge-mark) values in each timeseries (implies `--include-timeseries`) |
|
|
91
91
|
|
|
92
92
|
### `stations get` options
|
|
93
93
|
|
|
94
94
|
| Flag | Meaning |
|
|
95
95
|
| --- | --- |
|
|
96
96
|
| `--include-timeseries` | embed the station's timeseries list |
|
|
97
|
-
| `--include-current` | embed the current measurement |
|
|
98
|
-
| `--include-characteristic` | embed characteristic (gauge-mark) values |
|
|
97
|
+
| `--include-current` | embed the current measurement in each timeseries (implies `--include-timeseries`) |
|
|
98
|
+
| `--include-characteristic` | embed characteristic (gauge-mark) values in each timeseries (implies `--include-timeseries`) |
|
|
99
|
+
|
|
100
|
+
The API nests the current measurement and the gauge marks *inside* each
|
|
101
|
+
timeseries and drops them without the timeseries list, so `--include-current` and
|
|
102
|
+
`--include-characteristic` turn on `--include-timeseries` themselves.
|
|
99
103
|
|
|
100
104
|
### `measurements` options
|
|
101
105
|
|
|
@@ -187,15 +191,23 @@ both `pegel --compact waters` and `pegel waters --compact` do the same thing.
|
|
|
187
191
|
`PATH`. Run `npm bin -g` to find it and add it, or run via
|
|
188
192
|
`npx @maschinenlesbar.org/pegel-online-cli …`.
|
|
189
193
|
- **Exit `2` / "invalid argument"** — check the command syntax: a `<station>`
|
|
190
|
-
argument is required, and
|
|
191
|
-
|
|
192
|
-
- **Exit `4` / "not found"** — the station shortname or id doesn't exist
|
|
193
|
-
|
|
194
|
-
|
|
194
|
+
argument is required, and no argument or option value may be blank (or `.` /
|
|
195
|
+
`..` for an id). Run `pegel <command> --help` for the exact signature.
|
|
196
|
+
- **Exit `4` / "not found"** — the station shortname or id doesn't exist, or the
|
|
197
|
+
station doesn't publish the requested series (`Timeseries does not exist.` /
|
|
198
|
+
`Current measurement does not exist.`). Run `pegel stations list --fuzzy-id <name>`
|
|
199
|
+
or `pegel waters` to find the right shortname, and
|
|
200
|
+
`pegel stations get <station> --include-timeseries | jq -r '.timeseries[].shortname'`
|
|
201
|
+
to see which series codes it exposes.
|
|
202
|
+
- **Exit `1` / `HTTP 400` on `measurements`** — `--start` / `--end` are not checked
|
|
203
|
+
locally; the API rejects a value that is not an ISO-8601 instant or period
|
|
204
|
+
(`Given start parameter is neither a valid ISO date time, nor an ISO period.`) and
|
|
205
|
+
a window whose start is not before its end (e.g. a start in the future). Use
|
|
206
|
+
`P7D`, not `7d`.
|
|
207
|
+
- **Empty `[]` from `measurements`** — the window lies outside the data the API
|
|
208
|
+
keeps (about the last month), e.g. a date from earlier in the year.
|
|
195
209
|
- **Exit `1` / network error** — connectivity, DNS, or a timeout. Try again, or
|
|
196
210
|
raise the limit with `--timeout 60000`.
|
|
197
|
-
- **Empty `timeseries` array** — the station doesn't publish the requested series.
|
|
198
|
-
Run `pegel timeseries <station>` to see which codes it actually exposes.
|
|
199
211
|
|
|
200
212
|
## Global options
|
|
201
213
|
|
|
@@ -206,10 +218,10 @@ These apply to every command and may be given before *or* after it:
|
|
|
206
218
|
| `-V, --version` | Print the version number |
|
|
207
219
|
| `-h, --help` | Show help for the program or a command |
|
|
208
220
|
| `--compact` | Print JSON on a single line instead of pretty-printed |
|
|
209
|
-
| `--base-url <url>` | API base URL (default `https://www.pegelonline.wsv.de`) |
|
|
221
|
+
| `--base-url <url>` | API base URL (default `https://www.pegelonline.wsv.de`); http(s) only, a path prefix is fine, no query (`?`), fragment (`#`) or surrounding whitespace; userinfo is sent as Basic auth but shown as `***` in messages |
|
|
210
222
|
| `--timeout <ms>` | Time limit per request in milliseconds, reading the whole response included (default `30000`; at most `2147483647`) |
|
|
211
|
-
| `--user-agent <ua>` | `User-Agent` header value |
|
|
212
|
-
| `--max-retries <n>` | Retries for transient `429`/`503` responses (default `2`) |
|
|
223
|
+
| `--user-agent <ua>` | `User-Agent` header value (not blank; Latin-1, no control characters) |
|
|
224
|
+
| `--max-retries <n>` | Retries for transient `429`/`503` responses, `0`–`10` (default `2`); each waits the server's `Retry-After` (up to 30 s; a longer one is not retried), else 200 ms × attempt |
|
|
213
225
|
| `--max-response-bytes <n>` | Cap response body size in bytes (`0` = unlimited; default 100 MiB) |
|
|
214
226
|
|
|
215
227
|
## Learn more
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stations.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/stations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"stations.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/stations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAmCxC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAkC7E"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Option } from "commander";
|
|
2
|
-
import { action, parseNonEmpty,
|
|
2
|
+
import { STATION_HELP, action, parseNonEmpty, parsePathArg, renderJson } from "../shared.js";
|
|
3
3
|
/** commander accumulator for a repeatable string option. */
|
|
4
4
|
function collect(value, previous = []) {
|
|
5
5
|
return previous.concat([parseNonEmpty(value)]);
|
|
@@ -15,8 +15,8 @@ function includesFrom(opts) {
|
|
|
15
15
|
function addIncludeOptions(cmd) {
|
|
16
16
|
return cmd
|
|
17
17
|
.addOption(new Option("--include-timeseries", "embed each station's timeseries list"))
|
|
18
|
-
.addOption(new Option("--include-current", "embed the current measurement"))
|
|
19
|
-
.addOption(new Option("--include-characteristic", "embed characteristic (gauge-mark) values"));
|
|
18
|
+
.addOption(new Option("--include-current", "embed the current measurement in each timeseries (implies --include-timeseries)"))
|
|
19
|
+
.addOption(new Option("--include-characteristic", "embed characteristic (gauge-mark) values in each timeseries (implies --include-timeseries)"));
|
|
20
20
|
}
|
|
21
21
|
export function registerStationCommands(program, deps) {
|
|
22
22
|
const stations = program.command("stations").description("Measuring stations");
|
|
@@ -36,10 +36,11 @@ export function registerStationCommands(program, deps) {
|
|
|
36
36
|
renderJson(deps, global, await client.stations.list(params));
|
|
37
37
|
}));
|
|
38
38
|
const get = stations
|
|
39
|
-
.command("get
|
|
39
|
+
.command("get")
|
|
40
|
+
.argument("<station>", STATION_HELP, parsePathArg)
|
|
40
41
|
.description("Get one station by uuid/number/shortname/longname");
|
|
41
42
|
addIncludeOptions(get).action(action(deps, async ({ client, global, opts }, [station]) => {
|
|
42
|
-
renderJson(deps, global, await client.stations.get(
|
|
43
|
+
renderJson(deps, global, await client.stations.get(station, includesFrom(opts)));
|
|
43
44
|
}));
|
|
44
45
|
}
|
|
45
46
|
//# sourceMappingURL=stations.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stations.js","sourceRoot":"","sources":["../../../../src/cli/commands/stations.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAEnC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,
|
|
1
|
+
{"version":3,"file":"stations.js","sourceRoot":"","sources":["../../../../src/cli/commands/stations.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAEnC,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG7F,4DAA4D;AAC5D,SAAS,OAAO,CAAC,KAAa,EAAE,QAAQ,GAAa,EAAE;IACrD,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACjD,CAAC;AAED,gEAAgE;AAChE,SAAS,YAAY,CAAC,IAA6B;IACjD,OAAO;QACL,iBAAiB,EAAE,IAAI,CAAC,mBAAmB,CAAwB;QACnE,yBAAyB,EAAE,IAAI,CAAC,gBAAgB,CAAwB;QACxE,2BAA2B,EAAE,IAAI,CAAC,uBAAuB,CAAwB;KAClF,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,GAAY;IACrC,OAAO,GAAG;SACP,SAAS,CAAC,IAAI,MAAM,CAAC,sBAAsB,EAAE,sCAAsC,CAAC,CAAC;SACrF,SAAS,CACR,IAAI,MAAM,CACR,mBAAmB,EACnB,iFAAiF,CAClF,CACF;SACA,SAAS,CACR,IAAI,MAAM,CACR,0BAA0B,EAC1B,4FAA4F,CAC7F,CACF,CAAC;AACN,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,OAAgB,EAAE,IAAa;IACrE,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,WAAW,CAAC,oBAAoB,CAAC,CAAC;IAE/E,MAAM,IAAI,GAAG,QAAQ;SAClB,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,sBAAsB,CAAC;SACnC,MAAM,CAAC,YAAY,EAAE,yDAAyD,EAAE,OAAO,CAAC;SACxF,MAAM,CAAC,sBAAsB,EAAE,0CAA0C,EAAE,aAAa,CAAC;SACzF,MAAM,CAAC,iBAAiB,EAAE,gBAAgB,EAAE,aAAa,CAAC,CAAC;IAC9D,iBAAiB,CAAC,IAAI,CAAC,CAAC,MAAM,CAC5B,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE;QAC9C,MAAM,MAAM,GAAsB;YAChC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAyB;YACxC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAuB;YAC5C,OAAO,EAAE,IAAI,CAAC,SAAS,CAAuB;YAC9C,GAAG,YAAY,CAAC,IAAI,CAAC;SACtB,CAAC;QACF,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAC/D,CAAC,CAAC,CACH,CAAC;IAEF,MAAM,GAAG,GAAG,QAAQ;SACjB,OAAO,CAAC,KAAK,CAAC;SACd,QAAQ,CAAC,WAAW,EAAE,YAAY,EAAE,YAAY,CAAC;SACjD,WAAW,CAAC,mDAAmD,CAAC,CAAC;IACpE,iBAAiB,CAAC,GAAG,CAAC,CAAC,MAAM,CAC3B,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE;QACzD,UAAU,CACR,IAAI,EACJ,MAAM,EACN,MAAM,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAQ,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC,CACxD,CAAC;IACJ,CAAC,CAAC,CACH,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeseries.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/timeseries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"timeseries.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/timeseries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAKxC,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CA2DhF"}
|
|
@@ -1,32 +1,31 @@
|
|
|
1
|
-
import { action, parseNonEmpty,
|
|
2
|
-
const STATION_HELP = "station uuid, number, shortname or longname";
|
|
1
|
+
import { STATION_HELP, action, parseNonEmpty, parsePathArg, renderJson, timeseriesOr } from "../shared.js";
|
|
3
2
|
const TIMESERIES_HELP = "timeseries shortname, e.g. W (water level) or Q (flow)";
|
|
4
3
|
export function registerTimeseriesCommands(program, deps) {
|
|
5
4
|
program
|
|
6
5
|
.command("timeseries")
|
|
7
|
-
.argument("<station>", STATION_HELP)
|
|
8
|
-
.argument("[timeseries]", TIMESERIES_HELP,
|
|
6
|
+
.argument("<station>", STATION_HELP, parsePathArg)
|
|
7
|
+
.argument("[timeseries]", TIMESERIES_HELP, parsePathArg)
|
|
9
8
|
.description("Timeseries metadata (timeseries defaults to 'W' = water level)")
|
|
10
9
|
.action(action(deps, async ({ client, global }, [station, ts]) => {
|
|
11
|
-
renderJson(deps, global, await client.timeseries.get(
|
|
10
|
+
renderJson(deps, global, await client.timeseries.get(station, timeseriesOr(ts)));
|
|
12
11
|
}));
|
|
13
12
|
program
|
|
14
13
|
.command("current")
|
|
15
|
-
.argument("<station>", STATION_HELP)
|
|
16
|
-
.argument("[timeseries]", TIMESERIES_HELP,
|
|
14
|
+
.argument("<station>", STATION_HELP, parsePathArg)
|
|
15
|
+
.argument("[timeseries]", TIMESERIES_HELP, parsePathArg)
|
|
17
16
|
.description("The current measurement (timeseries defaults to 'W')")
|
|
18
17
|
.action(action(deps, async ({ client, global }, [station, ts]) => {
|
|
19
|
-
renderJson(deps, global, await client.timeseries.currentMeasurement(
|
|
18
|
+
renderJson(deps, global, await client.timeseries.currentMeasurement(station, timeseriesOr(ts)));
|
|
20
19
|
}));
|
|
21
20
|
program
|
|
22
21
|
.command("measurements")
|
|
23
|
-
.argument("<station>", STATION_HELP)
|
|
24
|
-
.argument("[timeseries]", TIMESERIES_HELP,
|
|
22
|
+
.argument("<station>", STATION_HELP, parsePathArg)
|
|
23
|
+
.argument("[timeseries]", TIMESERIES_HELP, parsePathArg)
|
|
25
24
|
.description("A window of measurements (timeseries defaults to 'W')")
|
|
26
25
|
.option("--start <iso>", "window start: ISO-8601 instant, or a period like P7D", parseNonEmpty)
|
|
27
26
|
.option("--end <iso>", "window end: ISO-8601 instant", parseNonEmpty)
|
|
28
27
|
.action(action(deps, async ({ client, global, opts }, [station, ts]) => {
|
|
29
|
-
renderJson(deps, global, await client.timeseries.measurements(
|
|
28
|
+
renderJson(deps, global, await client.timeseries.measurements(station, timeseriesOr(ts), {
|
|
30
29
|
start: opts["start"],
|
|
31
30
|
end: opts["end"],
|
|
32
31
|
}));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeseries.js","sourceRoot":"","sources":["../../../../src/cli/commands/timeseries.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,
|
|
1
|
+
{"version":3,"file":"timeseries.js","sourceRoot":"","sources":["../../../../src/cli/commands/timeseries.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE3G,MAAM,eAAe,GAAG,wDAAwD,CAAC;AAEjF,MAAM,UAAU,0BAA0B,CAAC,OAAgB,EAAE,IAAa;IACxE,OAAO;SACJ,OAAO,CAAC,YAAY,CAAC;SACrB,QAAQ,CAAC,WAAW,EAAE,YAAY,EAAE,YAAY,CAAC;SACjD,QAAQ,CAAC,cAAc,EAAE,eAAe,EAAE,YAAY,CAAC;SACvD,WAAW,CAAC,gEAAgE,CAAC;SAC7E,MAAM,CACL,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,EAAE;QACvD,UAAU,CACR,IAAI,EACJ,MAAM,EACN,MAAM,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,OAAQ,EAAE,YAAY,CAAC,EAAE,CAAC,CAAC,CACxD,CAAC;IACJ,CAAC,CAAC,CACH,CAAC;IAEJ,OAAO;SACJ,OAAO,CAAC,SAAS,CAAC;SAClB,QAAQ,CAAC,WAAW,EAAE,YAAY,EAAE,YAAY,CAAC;SACjD,QAAQ,CAAC,cAAc,EAAE,eAAe,EAAE,YAAY,CAAC;SACvD,WAAW,CAAC,sDAAsD,CAAC;SACnE,MAAM,CACL,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,EAAE;QACvD,UAAU,CACR,IAAI,EACJ,MAAM,EACN,MAAM,MAAM,CAAC,UAAU,CAAC,kBAAkB,CAAC,OAAQ,EAAE,YAAY,CAAC,EAAE,CAAC,CAAC,CACvE,CAAC;IACJ,CAAC,CAAC,CACH,CAAC;IAEJ,OAAO;SACJ,OAAO,CAAC,cAAc,CAAC;SACvB,QAAQ,CAAC,WAAW,EAAE,YAAY,EAAE,YAAY,CAAC;SACjD,QAAQ,CAAC,cAAc,EAAE,eAAe,EAAE,YAAY,CAAC;SACvD,WAAW,CAAC,uDAAuD,CAAC;SACpE,MAAM,CAAC,eAAe,EAAE,sDAAsD,EAAE,aAAa,CAAC;SAC9F,MAAM,CAAC,aAAa,EAAE,8BAA8B,EAAE,aAAa,CAAC;SACpE,MAAM,CACL,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,EAAE;QAC7D,UAAU,CACR,IAAI,EACJ,MAAM,EACN,MAAM,MAAM,CAAC,UAAU,CAAC,YAAY,CAAC,OAAQ,EAAE,YAAY,CAAC,EAAE,CAAC,EAAE;YAC/D,KAAK,EAAE,IAAI,CAAC,OAAO,CAAuB;YAC1C,GAAG,EAAE,IAAI,CAAC,KAAK,CAAuB;SACvC,CAAC,CACH,CAAC;IACJ,CAAC,CAAC,CACH,CAAC;IAEJ,OAAO;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,qCAAqC,CAAC;SAClD,MAAM,CACL,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE;QACxC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IAClD,CAAC,CAAC,CACH,CAAC;AACN,CAAC"}
|
package/dist/src/cli/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// Binary entry point. Thin shim around run(); all logic lives in run.ts/program.ts.
|
|
3
|
+
import { handleOutputErrors } from "./io.js";
|
|
3
4
|
import { run } from "./run.js";
|
|
5
|
+
handleOutputErrors();
|
|
4
6
|
const exitCode = await run(process.argv.slice(2));
|
|
5
7
|
process.exitCode = exitCode;
|
|
6
8
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cli/index.ts"],"names":[],"mappings":";AACA,oFAAoF;AAEpF,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/B,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAClD,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cli/index.ts"],"names":[],"mappings":";AACA,oFAAoF;AAEpF,OAAO,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/B,kBAAkB,EAAE,CAAC;AACrB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAClD,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC"}
|
package/dist/src/cli/io.d.ts
CHANGED
|
@@ -9,5 +9,22 @@ export interface CliDeps {
|
|
|
9
9
|
/** Build a client from the resolved global options (injectable for tests). */
|
|
10
10
|
createClient(options: EngineOptions): PegelOnlineClient;
|
|
11
11
|
}
|
|
12
|
+
/** The two process streams, as far as `handleOutputErrors` needs them. */
|
|
13
|
+
export interface OutputStreams {
|
|
14
|
+
stdout: Pick<NodeJS.WriteStream, "on">;
|
|
15
|
+
stderr: Pick<NodeJS.WriteStream, "on">;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Handle write errors on stdout/stderr, which Node otherwise reports as an
|
|
19
|
+
* unhandled 'error' event: a raw stack trace and exit 1.
|
|
20
|
+
*
|
|
21
|
+
* A reader that stops early — `| head`, `| jq` exiting on the first match, a closed
|
|
22
|
+
* pager — closes the pipe while the CLI is still writing, and the next write fails
|
|
23
|
+
* with EPIPE. That is ordinary use, so the process exits 0 at once, quietly. Any
|
|
24
|
+
* other stdout error prints one `Output error: <message>` line to stderr and exits
|
|
25
|
+
* 1; any other stderr error exits 1 silently (there is nowhere left to report it).
|
|
26
|
+
* The bin shim installs this once, before `run()`.
|
|
27
|
+
*/
|
|
28
|
+
export declare function handleOutputErrors(streams?: OutputStreams, exit?: (code: number) => void): void;
|
|
12
29
|
export declare const defaultIO: CliIO;
|
|
13
30
|
//# sourceMappingURL=io.d.ts.map
|
package/dist/src/cli/io.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"io.d.ts","sourceRoot":"","sources":["../../../src/cli/io.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,WAAW,KAAK;IACpB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,KAAK,CAAC;IACV,8EAA8E;IAC9E,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,iBAAiB,CAAC;CACzD;AAED,eAAO,MAAM,SAAS,EAAE,KAGvB,CAAC"}
|
|
1
|
+
{"version":3,"file":"io.d.ts","sourceRoot":"","sources":["../../../src/cli/io.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,WAAW,KAAK;IACpB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,KAAK,CAAC;IACV,8EAA8E;IAC9E,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,iBAAiB,CAAC;CACzD;AAED,0EAA0E;AAC1E,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IACvC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;CACxC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,GAAE,aAAuB,EAChC,IAAI,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAmC,GAC1D,IAAI,CASN;AAED,eAAO,MAAM,SAAS,EAAE,KAGvB,CAAC"}
|
package/dist/src/cli/io.js
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
// I/O seam for the CLI. Everything the CLI writes goes through a CliIO object so
|
|
2
2
|
// tests can capture output instead of hitting the real stdout/stderr.
|
|
3
|
+
/**
|
|
4
|
+
* Handle write errors on stdout/stderr, which Node otherwise reports as an
|
|
5
|
+
* unhandled 'error' event: a raw stack trace and exit 1.
|
|
6
|
+
*
|
|
7
|
+
* A reader that stops early — `| head`, `| jq` exiting on the first match, a closed
|
|
8
|
+
* pager — closes the pipe while the CLI is still writing, and the next write fails
|
|
9
|
+
* with EPIPE. That is ordinary use, so the process exits 0 at once, quietly. Any
|
|
10
|
+
* other stdout error prints one `Output error: <message>` line to stderr and exits
|
|
11
|
+
* 1; any other stderr error exits 1 silently (there is nowhere left to report it).
|
|
12
|
+
* The bin shim installs this once, before `run()`.
|
|
13
|
+
*/
|
|
14
|
+
export function handleOutputErrors(streams = process, exit = (code) => process.exit(code)) {
|
|
15
|
+
streams.stdout.on("error", (err) => {
|
|
16
|
+
if (err.code === "EPIPE")
|
|
17
|
+
return exit(0);
|
|
18
|
+
process.stderr.write(`Output error: ${err.message}\n`);
|
|
19
|
+
exit(1);
|
|
20
|
+
});
|
|
21
|
+
streams.stderr.on("error", (err) => {
|
|
22
|
+
exit(err.code === "EPIPE" ? 0 : 1);
|
|
23
|
+
});
|
|
24
|
+
}
|
|
3
25
|
export const defaultIO = {
|
|
4
26
|
out: (text) => process.stdout.write(text + "\n"),
|
|
5
27
|
err: (text) => process.stderr.write(text + "\n"),
|
package/dist/src/cli/io.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"io.js","sourceRoot":"","sources":["../../../src/cli/io.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,sEAAsE;
|
|
1
|
+
{"version":3,"file":"io.js","sourceRoot":"","sources":["../../../src/cli/io.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,sEAAsE;AAsBtE;;;;;;;;;;GAUG;AACH,MAAM,UAAU,kBAAkB,CAChC,OAAO,GAAkB,OAAO,EAChC,IAAI,GAA2B,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;IAE3D,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAA0B,EAAE,EAAE;QACxD,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO;YAAE,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC;QACzC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,iBAAiB,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC;QACvD,IAAI,CAAC,CAAC,CAAC,CAAC;IACV,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAA0B,EAAE,EAAE;QACxD,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,MAAM,SAAS,GAAU;IAC9B,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC;IAChD,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC;CACjD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"program.d.ts","sourceRoot":"","sources":["../../../src/cli/program.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"program.d.ts","sourceRoot":"","sources":["../../../src/cli/program.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAyBvC,eAAO,MAAM,OAAO,QAAgB,CAAC;AAErC,yEAAyE;AACzE,eAAO,MAAM,WAAW,EAAE,OAGzB,CAAC;AAEF,wBAAgB,YAAY,CAAC,IAAI,GAAE,OAAqB,GAAG,OAAO,CAkCjE"}
|
package/dist/src/cli/program.js
CHANGED
|
@@ -7,7 +7,8 @@ import { Command } from "commander";
|
|
|
7
7
|
import { defaultIO } from "./io.js";
|
|
8
8
|
import { PegelOnlineClient } from "../client/client.js";
|
|
9
9
|
import { MAX_TIMEOUT_MS } from "../client/http.js";
|
|
10
|
-
import {
|
|
10
|
+
import { MAX_RETRIES } from "../client/engine.js";
|
|
11
|
+
import { parseBoundedInt, parseIntArg, parseBaseUrl, parseHeaderValue } from "./shared.js";
|
|
11
12
|
import { registerStationCommands } from "./commands/stations.js";
|
|
12
13
|
import { registerTimeseriesCommands } from "./commands/timeseries.js";
|
|
13
14
|
/**
|
|
@@ -41,8 +42,8 @@ export function buildProgram(deps = defaultDeps) {
|
|
|
41
42
|
.version(VERSION)
|
|
42
43
|
.option("--base-url <url>", "API base URL", parseBaseUrl, "https://www.pegelonline.wsv.de")
|
|
43
44
|
.option("--timeout <ms>", "time limit per request in milliseconds, whole response included", parseBoundedInt(0, MAX_TIMEOUT_MS))
|
|
44
|
-
.option("--user-agent <ua>", "User-Agent header value")
|
|
45
|
-
.option("--max-retries <n>", "retries for transient 429/503 responses",
|
|
45
|
+
.option("--user-agent <ua>", "User-Agent header value", parseHeaderValue)
|
|
46
|
+
.option("--max-retries <n>", "retries for transient 429/503 responses (0..10; each waits the server's Retry-After, up to 30 s)", parseBoundedInt(0, MAX_RETRIES))
|
|
46
47
|
.option("--max-response-bytes <n>", "cap response body size in bytes (0 = unlimited; default 100 MiB)", parseIntArg)
|
|
47
48
|
.option("--compact", "print JSON on a single line instead of pretty-printed")
|
|
48
49
|
.showHelpAfterError();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"program.js","sourceRoot":"","sources":["../../../src/cli/program.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,4EAA4E;AAC5E,mBAAmB;AAEnB,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"program.js","sourceRoot":"","sources":["../../../src/cli/program.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,4EAA4E;AAC5E,mBAAmB;AAEnB,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC3F,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAC;AAEtE;;;;;GAKG;AACH,SAAS,WAAW;IAClB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,uBAAuB,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;QACjE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAyB,CAAC;QAC5F,OAAO,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC;IACjB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,OAAO,GAAG,WAAW,EAAE,CAAC;AAErC,yEAAyE;AACzE,MAAM,CAAC,MAAM,WAAW,GAAY;IAClC,EAAE,EAAE,SAAS;IACb,YAAY,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,iBAAiB,CAAC,OAAO,CAAC;CAC1D,CAAC;AAEF,MAAM,UAAU,YAAY,CAAC,IAAI,GAAY,WAAW;IACtD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,OAAO,CAAC;SACb,WAAW,CACV,oDAAoD;QAClD,0DAA0D,CAC7D;SACA,OAAO,CAAC,OAAO,CAAC;SAChB,MAAM,CAAC,kBAAkB,EAAE,cAAc,EAAE,YAAY,EAAE,gCAAgC,CAAC;SAC1F,MAAM,CACL,gBAAgB,EAChB,iEAAiE,EACjE,eAAe,CAAC,CAAC,EAAE,cAAc,CAAC,CACnC;SACA,MAAM,CAAC,mBAAmB,EAAE,yBAAyB,EAAE,gBAAgB,CAAC;SACxE,MAAM,CACL,mBAAmB,EACnB,kGAAkG,EAClG,eAAe,CAAC,CAAC,EAAE,WAAW,CAAC,CAChC;SACA,MAAM,CACL,0BAA0B,EAC1B,kEAAkE,EAClE,WAAW,CACZ;SACA,MAAM,CAAC,WAAW,EAAE,uDAAuD,CAAC;SAC5E,kBAAkB,EAAE,CAAC;IAExB,uBAAuB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACvC,0BAA0B,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAE1C,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
package/dist/src/cli/shared.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { CliDeps } from "./io.js";
|
|
2
2
|
import type { EngineOptions } from "../client/engine.js";
|
|
3
|
+
/** Help text of every `<station>` positional. */
|
|
4
|
+
export declare const STATION_HELP = "station uuid, number, shortname or longname";
|
|
3
5
|
/** commander value-parser: a non-negative integer. */
|
|
4
6
|
export declare function parseIntArg(value: string): number;
|
|
5
7
|
/** Build a commander value-parser for an integer constrained to [min, max]. */
|
|
@@ -9,6 +11,23 @@ export declare function parseBoundedInt(min: number, max: number): (value: strin
|
|
|
9
11
|
* otherwise be dropped and the command would silently run unfiltered.
|
|
10
12
|
*/
|
|
11
13
|
export declare function parseNonEmpty(value: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* commander value-parser for a value that ends up in an HTTP header (`--user-agent`).
|
|
16
|
+
* Node's HTTP layer throws an opaque "Invalid character in header content" at request
|
|
17
|
+
* time for a CR/LF (or any other C0 control or DEL) and for any character above
|
|
18
|
+
* U+00FF, which surfaced as "Unexpected error". Reject those here as a usage error,
|
|
19
|
+
* along with a blank value. Tab is allowed, as in HTTP. Checked by char code so the
|
|
20
|
+
* source stays free of control bytes.
|
|
21
|
+
*/
|
|
22
|
+
export declare function parseHeaderValue(value: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* commander value-parser for an id that becomes a URL path segment (the
|
|
25
|
+
* `<station>` and `[timeseries]` positionals): not blank (which would build
|
|
26
|
+
* `/stations//W/...`), and not "." / "..", which
|
|
27
|
+
* encodeURIComponent leaves untouched and URL parsing would resolve, sending the
|
|
28
|
+
* request to a different resource. A usage error, before any request.
|
|
29
|
+
*/
|
|
30
|
+
export declare function parsePathArg(value: string): string;
|
|
12
31
|
/**
|
|
13
32
|
* commander value-parser for `--base-url`: reject anything that is not a parseable
|
|
14
33
|
* absolute `http:`/`https:` URL at *parse* time, so a bad scheme (`file:`, `ftp:`)
|
|
@@ -18,12 +37,6 @@ export declare function parseNonEmpty(value: string): string;
|
|
|
18
37
|
* the user-facing rejection earlier and to the correct exit code.
|
|
19
38
|
*/
|
|
20
39
|
export declare function parseBaseUrl(value: string): string;
|
|
21
|
-
/**
|
|
22
|
-
* Validate a required positional argument: reject an empty/blank value rather
|
|
23
|
-
* than forwarding it into the URL path (which would produce a malformed request
|
|
24
|
-
* like `/stations//W/...`). Returns the trimmed value.
|
|
25
|
-
*/
|
|
26
|
-
export declare function requireArg(name: string, value: string | undefined): string;
|
|
27
40
|
/**
|
|
28
41
|
* Default an omitted optional `[timeseries]` positional to "W" (water level),
|
|
29
42
|
* matching the documented default. A blank value never reaches here: the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/cli/shared.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGzD,sDAAsD;AACtD,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAajD;AAED,+EAA+E;AAC/E,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAQnF;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKnD;AAED;;;;;;;GAOG;AACH,wBAAgB,
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/cli/shared.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGzD,iDAAiD;AACjD,eAAO,MAAM,YAAY,gDAAgD,CAAC;AAE1E,sDAAsD;AACtD,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAajD;AAED,+EAA+E;AAC/E,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAQnF;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKnD;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAYtD;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMlD;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAqBlD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,SAAM,GAAG,MAAM,CAE9E;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,uEAAuE;AACvE,wBAAgB,eAAe,CAAC,MAAM,EAAE,aAAa,GAAG,aAAa,CAQpE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAWvD;AAyBD,gFAAgF;AAChF,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAGrF;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC;IAC5C,MAAM,EAAE,aAAa,CAAC;IACtB,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED;;;;;;;GAOG;AACH,wBAAgB,MAAM,CACpB,IAAI,EAAE,OAAO,EACb,EAAE,EAAE,CAAC,GAAG,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,GAC/D,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAQvC"}
|
package/dist/src/cli/shared.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
// option resolver, and the JSON result renderer.
|
|
3
3
|
import { InvalidArgumentError } from "commander";
|
|
4
4
|
import { PegelError } from "../client/errors.js";
|
|
5
|
+
/** Help text of every `<station>` positional. */
|
|
6
|
+
export const STATION_HELP = "station uuid, number, shortname or longname";
|
|
5
7
|
/** commander value-parser: a non-negative integer. */
|
|
6
8
|
export function parseIntArg(value) {
|
|
7
9
|
// Require a plain decimal integer. Reject blank/whitespace ("" and " " coerce
|
|
@@ -37,6 +39,41 @@ export function parseNonEmpty(value) {
|
|
|
37
39
|
}
|
|
38
40
|
return value;
|
|
39
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* commander value-parser for a value that ends up in an HTTP header (`--user-agent`).
|
|
44
|
+
* Node's HTTP layer throws an opaque "Invalid character in header content" at request
|
|
45
|
+
* time for a CR/LF (or any other C0 control or DEL) and for any character above
|
|
46
|
+
* U+00FF, which surfaced as "Unexpected error". Reject those here as a usage error,
|
|
47
|
+
* along with a blank value. Tab is allowed, as in HTTP. Checked by char code so the
|
|
48
|
+
* source stays free of control bytes.
|
|
49
|
+
*/
|
|
50
|
+
export function parseHeaderValue(value) {
|
|
51
|
+
parseNonEmpty(value);
|
|
52
|
+
for (let i = 0; i < value.length; i++) {
|
|
53
|
+
const c = value.charCodeAt(i);
|
|
54
|
+
if ((c < 0x20 && c !== 0x09) || c === 0x7f) {
|
|
55
|
+
throw new InvalidArgumentError("Value contains control characters.");
|
|
56
|
+
}
|
|
57
|
+
if (c > 0xff) {
|
|
58
|
+
throw new InvalidArgumentError("Value contains characters outside Latin-1 (above U+00FF).");
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return value;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* commander value-parser for an id that becomes a URL path segment (the
|
|
65
|
+
* `<station>` and `[timeseries]` positionals): not blank (which would build
|
|
66
|
+
* `/stations//W/...`), and not "." / "..", which
|
|
67
|
+
* encodeURIComponent leaves untouched and URL parsing would resolve, sending the
|
|
68
|
+
* request to a different resource. A usage error, before any request.
|
|
69
|
+
*/
|
|
70
|
+
export function parsePathArg(value) {
|
|
71
|
+
parseNonEmpty(value);
|
|
72
|
+
if (value === "." || value === "..") {
|
|
73
|
+
throw new InvalidArgumentError('"." and ".." cannot be used as an id.');
|
|
74
|
+
}
|
|
75
|
+
return value;
|
|
76
|
+
}
|
|
40
77
|
/**
|
|
41
78
|
* commander value-parser for `--base-url`: reject anything that is not a parseable
|
|
42
79
|
* absolute `http:`/`https:` URL at *parse* time, so a bad scheme (`file:`, `ftp:`)
|
|
@@ -56,21 +93,15 @@ export function parseBaseUrl(value) {
|
|
|
56
93
|
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
57
94
|
throw new InvalidArgumentError("Only http and https URLs are supported.");
|
|
58
95
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
* than forwarding it into the URL path (which would produce a malformed request
|
|
64
|
-
* like `/stations//W/...`). Returns the trimmed value.
|
|
65
|
-
*/
|
|
66
|
-
export function requireArg(name, value) {
|
|
67
|
-
if (value === undefined || value.trim() === "") {
|
|
68
|
-
throw new PegelError(`Missing required <${name}> argument.`);
|
|
96
|
+
// Paths are appended to the base URL as a string, so a query or fragment would
|
|
97
|
+
// swallow every request path ("http://h/#f" requests "/" for every command).
|
|
98
|
+
if (/[?#]/.test(value)) {
|
|
99
|
+
throw new InvalidArgumentError("A base URL cannot have a query (?) or fragment (#).");
|
|
69
100
|
}
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
if (value
|
|
73
|
-
throw new
|
|
101
|
+
// new URL() trims surrounding whitespace silently; the raw value is what the
|
|
102
|
+
// engine uses, so reject it rather than guess.
|
|
103
|
+
if (value !== value.trim()) {
|
|
104
|
+
throw new InvalidArgumentError("A base URL cannot have surrounding whitespace.");
|
|
74
105
|
}
|
|
75
106
|
return value;
|
|
76
107
|
}
|
|
@@ -116,9 +147,29 @@ export function escapeControlChars(json) {
|
|
|
116
147
|
}
|
|
117
148
|
return from === 0 ? json : result + json.slice(from);
|
|
118
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* JSON.stringify, pretty or compact. A deeply nested value (a hostile or broken
|
|
152
|
+
* response) overflows the stack — the pretty form far sooner than the compact one,
|
|
153
|
+
* which is why the message suggests --compact. The RangeError becomes a PegelError so
|
|
154
|
+
* the CLI prints a clear message instead of "Unexpected error: Maximum call stack
|
|
155
|
+
* size exceeded".
|
|
156
|
+
*/
|
|
157
|
+
function stringifyJson(value, compact) {
|
|
158
|
+
try {
|
|
159
|
+
return compact ? JSON.stringify(value) : JSON.stringify(value, null, 2);
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
if (err instanceof RangeError) {
|
|
163
|
+
throw new PegelError(compact
|
|
164
|
+
? "The response is nested too deeply to print."
|
|
165
|
+
: "The response is nested too deeply to pretty-print; try --compact.", { cause: err });
|
|
166
|
+
}
|
|
167
|
+
throw err;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
119
170
|
/** Render a JSON value to stdout, pretty by default, compact with --compact. */
|
|
120
171
|
export function renderJson(deps, global, value) {
|
|
121
|
-
const text = escapeControlChars(
|
|
172
|
+
const text = escapeControlChars(stringifyJson(value, global.compact === true));
|
|
122
173
|
deps.io.out(text);
|
|
123
174
|
}
|
|
124
175
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared.js","sourceRoot":"","sources":["../../../src/cli/shared.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,iDAAiD;AAGjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAGjD,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD,sDAAsD;AACtD,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,8EAA8E;IAC9E,gFAAgF;IAChF,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,oBAAoB,CAAC,kCAAkC,CAAC,CAAC;IACrE,CAAC;IACD,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IACxB,+EAA+E;IAC/E,oEAAoE;IACpE,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,oBAAoB,CAAC,kCAAkC,CAAC,CAAC;IACrE,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,eAAe,CAAC,GAAW,EAAE,GAAW;IACtD,OAAO,CAAC,KAAa,EAAE,EAAE;QACvB,MAAM,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;QAC7B,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC;YACvB,MAAM,IAAI,oBAAoB,CAAC,4BAA4B,GAAG,OAAO,GAAG,GAAG,CAAC,CAAC;QAC/E,CAAC;QACD,OAAO,CAAC,CAAC;IACX,CAAC,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACxB,MAAM,IAAI,oBAAoB,CAAC,6BAA6B,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"shared.js","sourceRoot":"","sources":["../../../src/cli/shared.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/client/client.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/client/client.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAGhE,OAAO,KAAK,EACV,OAAO,EACP,KAAK,EACL,cAAc,EACd,kBAAkB,EAClB,WAAW,EACX,iBAAiB,EACjB,aAAa,EACb,kBAAkB,EACnB,MAAM,YAAY,CAAC;AA8DpB,mFAAmF;AACnF,cAAM,gBAAgB;IACR,OAAO,CAAC,QAAQ,CAAC,CAAC;IAA9B,YAA6B,CAAC,EAAE,aAAa,EAAI;IAEjD,IAAI,CAAC,MAAM,GAAE,iBAAsB,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAQvD;IAEK,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,GAAE,aAAkB,GAAG,OAAO,CAAC,OAAO,CAAC,CAEvE;CACF;AAED,4FAA4F;AAC5F,cAAM,kBAAkB;IACV,OAAO,CAAC,QAAQ,CAAC,CAAC;IAA9B,YAA6B,CAAC,EAAE,aAAa,EAAI;IAEjD,gEAAgE;IAC1D,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,SAAM,EAAE,MAAM,GAAE,aAAkB,GAAG,OAAO,CAAC,cAAc,CAAC,CAKhG;IAEK,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,SAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAIvF;IAEK,YAAY,CAChB,OAAO,EAAE,MAAM,EACf,UAAU,SAAM,EAChB,MAAM,GAAE,kBAAuB,GAC9B,OAAO,CAAC,WAAW,EAAE,CAAC,CAKxB;CACF;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IAEvC,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC;IACpC,QAAQ,CAAC,UAAU,EAAE,kBAAkB,CAAC;IAExC,YAAY,OAAO,GAAE,aAAkB,EAItC;IAED,kEAAkE;IAClE,MAAM,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC,CAEzB;CACF"}
|
|
@@ -6,8 +6,32 @@
|
|
|
6
6
|
// client.timeseries.currentMeasurement("BONN", "W")
|
|
7
7
|
// client.timeseries.measurements("BONN", "W", { start: "P3D" })
|
|
8
8
|
import { RequestEngine } from "./engine.js";
|
|
9
|
+
import { PegelError } from "./errors.js";
|
|
9
10
|
const API = "/webservices/rest-api/v2";
|
|
10
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Station names, waters and ids are matched exactly by the API, which stores them
|
|
13
|
+
* composed (NFC): a decomposed umlaut ("KO" + U+0308 + "LN", as pasted from macOS
|
|
14
|
+
* file names or some PDFs) is a 404 / an empty list. Compose every such input.
|
|
15
|
+
* NFC, not NFKC: an id lookup must not rewrite compatibility characters.
|
|
16
|
+
*/
|
|
17
|
+
function nfc(value) {
|
|
18
|
+
return value.normalize("NFC");
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* One URL path segment from a caller-supplied station or timeseries id. A blank
|
|
22
|
+
* value would build a different path (`stations/.json`, `stations//W.json`), and
|
|
23
|
+
* "." / ".." pass encodeURIComponent unchanged and are resolved by URL parsing
|
|
24
|
+
* (the engine's `buildUrl` refuses them as path segments too), so all are refused.
|
|
25
|
+
*/
|
|
26
|
+
function enc(name, value) {
|
|
27
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
28
|
+
throw new PegelError(`Invalid ${name}: expected a non-empty string, got ${JSON.stringify(value)}.`);
|
|
29
|
+
}
|
|
30
|
+
if (value === "." || value === "..") {
|
|
31
|
+
throw new PegelError(`Invalid ${name} "${value}": "." and ".." cannot be used as an id.`);
|
|
32
|
+
}
|
|
33
|
+
return encodeURIComponent(nfc(value));
|
|
34
|
+
}
|
|
11
35
|
/** Drop undefined values so only the parameters the caller set are sent. */
|
|
12
36
|
function prune(params) {
|
|
13
37
|
const out = {};
|
|
@@ -17,6 +41,20 @@ function prune(params) {
|
|
|
17
41
|
}
|
|
18
42
|
return out;
|
|
19
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* The station include parameters. The API nests the current measurement and the
|
|
46
|
+
* gauge marks *inside* each timeseries, so without `includeTimeseries=true` it
|
|
47
|
+
* silently drops both. Asking for either therefore implies `includeTimeseries`
|
|
48
|
+
* unless the caller set it explicitly.
|
|
49
|
+
*/
|
|
50
|
+
function stationIncludes(p) {
|
|
51
|
+
const nested = p.includeCurrentMeasurement === true || p.includeCharacteristicValues === true;
|
|
52
|
+
return prune({
|
|
53
|
+
includeTimeseries: p.includeTimeseries ?? (nested ? true : undefined),
|
|
54
|
+
includeCurrentMeasurement: p.includeCurrentMeasurement,
|
|
55
|
+
includeCharacteristicValues: p.includeCharacteristicValues,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
20
58
|
function includeQuery(p) {
|
|
21
59
|
return prune({
|
|
22
60
|
includeTimeseries: p.includeTimeseries,
|
|
@@ -32,17 +70,15 @@ class StationsResource {
|
|
|
32
70
|
}
|
|
33
71
|
list(params = {}) {
|
|
34
72
|
const query = prune({
|
|
35
|
-
ids: params.ids && params.ids.length > 0 ? params.ids.join(",") : undefined,
|
|
36
|
-
waters: params.waters,
|
|
37
|
-
fuzzyId: params.fuzzyId,
|
|
38
|
-
|
|
39
|
-
includeCurrentMeasurement: params.includeCurrentMeasurement,
|
|
40
|
-
includeCharacteristicValues: params.includeCharacteristicValues,
|
|
73
|
+
ids: params.ids && params.ids.length > 0 ? params.ids.map(nfc).join(",") : undefined,
|
|
74
|
+
waters: params.waters === undefined ? undefined : nfc(params.waters),
|
|
75
|
+
fuzzyId: params.fuzzyId === undefined ? undefined : nfc(params.fuzzyId),
|
|
76
|
+
...stationIncludes(params),
|
|
41
77
|
});
|
|
42
78
|
return this.e.getJson(`${API}/stations.json`, query);
|
|
43
79
|
}
|
|
44
|
-
get(station, params = {}) {
|
|
45
|
-
return this.e.getJson(`${API}/stations/${enc(station)}.json`,
|
|
80
|
+
async get(station, params = {}) {
|
|
81
|
+
return this.e.getJson(`${API}/stations/${enc("station", station)}.json`, stationIncludes(params));
|
|
46
82
|
}
|
|
47
83
|
}
|
|
48
84
|
/** Timeseries: metadata, the current measurement, a window of measurements, gauge marks. */
|
|
@@ -52,14 +88,14 @@ class TimeseriesResource {
|
|
|
52
88
|
this.e = e;
|
|
53
89
|
}
|
|
54
90
|
/** Timeseries metadata (e.g. "W" = water level, "Q" = flow). */
|
|
55
|
-
get(station, timeseries = "W", params = {}) {
|
|
56
|
-
return this.e.getJson(`${API}/stations/${enc(station)}/${enc(timeseries)}.json`, includeQuery(params));
|
|
91
|
+
async get(station, timeseries = "W", params = {}) {
|
|
92
|
+
return this.e.getJson(`${API}/stations/${enc("station", station)}/${enc("timeseries", timeseries)}.json`, includeQuery(params));
|
|
57
93
|
}
|
|
58
|
-
currentMeasurement(station, timeseries = "W") {
|
|
59
|
-
return this.e.getJson(`${API}/stations/${enc(station)}/${enc(timeseries)}/currentmeasurement.json`);
|
|
94
|
+
async currentMeasurement(station, timeseries = "W") {
|
|
95
|
+
return this.e.getJson(`${API}/stations/${enc("station", station)}/${enc("timeseries", timeseries)}/currentmeasurement.json`);
|
|
60
96
|
}
|
|
61
|
-
measurements(station, timeseries = "W", params = {}) {
|
|
62
|
-
return this.e.getJson(`${API}/stations/${enc(station)}/${enc(timeseries)}/measurements.json`, prune({ start: params.start, end: params.end }));
|
|
97
|
+
async measurements(station, timeseries = "W", params = {}) {
|
|
98
|
+
return this.e.getJson(`${API}/stations/${enc("station", station)}/${enc("timeseries", timeseries)}/measurements.json`, prune({ start: params.start, end: params.end }));
|
|
63
99
|
}
|
|
64
100
|
}
|
|
65
101
|
export class PegelOnlineClient {
|