@maschinenlesbar.org/nina-warnungen-cli 0.0.2 → 0.0.3

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 CHANGED
@@ -1,216 +1,201 @@
1
1
  # nina-warnungen-cli
2
2
 
3
- A TypeScript **API client** and **command-line interface** for the open
4
- [NINA API](https://nina.api.bund.dev/) (`warnung.bund.de`) — the federal
5
- civil-protection warning system run by the **BBK** (Bundesamt für Bevölkerungsschutz
6
- und Katastrophenhilfe). It aggregates **MoWaS**, **KATWARN**, **BIWAPP**,
7
- **DWD severe-weather**, **flood (LHP)** and **police** alerts.
8
-
9
- - **Zero runtime HTTP dependencies** — built on Node's built-in `http`/`https` (no axios, no fetch polyfill).
10
- - **One small dependency** for the CLI: [`commander`](https://github.com/tj/commander.js).
11
- - **Strongly typed** typed client surface, warning summaries and the source enum.
12
- - **Well tested** unit tests on Node's built-in test runner (`node --test`), every HTTP response mocked.
13
- - **Read-only, no auth** — the NINA API needs no key; this client only reads.
14
-
15
- New to NINA, or terms like *MoWaS*, *Amtlicher Regionalschlüssel (ARS)* or the
16
- CAP *Severity* levels? See **[GLOSSARY.md](GLOSSARY.md)** for the domain concepts
17
- and the project's own vocabulary.
18
-
19
- ## Requirements
20
-
21
- - Node.js **>= 20** (uses the stable built-in test runner, ESM and top-level `await`).
3
+ [![CI](https://github.com/maschinenlesbar-org/nina-warnungen-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/maschinenlesbar-org/nina-warnungen-cli/actions/workflows/ci.yml)
4
+ [![Release](https://github.com/maschinenlesbar-org/nina-warnungen-cli/actions/workflows/release.yml/badge.svg)](https://github.com/maschinenlesbar-org/nina-warnungen-cli/actions/workflows/release.yml)
5
+ [![npm](https://img.shields.io/npm/v/@maschinenlesbar.org/nina-warnungen-cli)](https://www.npmjs.com/package/@maschinenlesbar.org/nina-warnungen-cli)
6
+
7
+ Query Germany's **federal civil-protection warning system** from your terminal.
8
+ `nina` is a command-line tool over the open
9
+ [NINA API](https://nina.api.bund.dev/) (`warnung.bund.de`): fetch current
10
+ warnings by source, look up the full detail or geometry of any alert, check a
11
+ region by its official key, and browse the MoWaS archive as clean JSON you
12
+ can pipe straight into [`jq`](https://jqlang.github.io/jq/).
13
+
14
+ - **Works out of the box** — no account, no API key, no configuration. Install and query.
15
+ - **All sources in one tool** MoWaS, KATWARN, BIWAPP, DWD severe-weather, flood (LHP) and police.
16
+ - **Clean JSON output** pretty-printed by default, `--compact` for one-line/scripting.
17
+ - **GeoJSON support** — download a warning's affected-area geometry directly to a file.
18
+ - **Cheap polling** — a lightweight data-version endpoint tells you whether anything changed without pulling full feeds.
19
+
20
+ > Want to use this as a TypeScript library or understand how it's built?
21
+ > See **[DEVELOPING.md](DEVELOPING.md)**.
22
22
 
23
23
  ## Install
24
24
 
25
25
  ```bash
26
- npm install
27
- npm run build # compiles TypeScript to dist/
26
+ npm i -g @maschinenlesbar.org/nina-warnungen-cli
28
27
  ```
29
28
 
30
- Run the CLI without a global install:
29
+ This installs the **`nina`** command. Requires **Node.js 20+**.
30
+
31
+ Check it works:
31
32
 
32
33
  ```bash
33
- node dist/src/cli/index.js --help
34
- # or, after `npm link` / global install:
35
34
  nina --help
36
35
  ```
37
36
 
38
- ---
39
-
40
- ## CLI usage
37
+ ## Quickstart
41
38
 
42
- Search/list commands print pretty JSON to stdout; `warning geojson` writes the raw
43
- GeoJSON bytes to stdout or to a file via `-o/--output`. The body is buffered in
44
- memory (bounded by `--max-response-bytes`), not streamed. As a sanity check, if the
45
- server returns a body whose `Content-Type` does not contain `json` (e.g. a
46
- `text/html` gateway error page served with a `200` status), the CLI prints a
47
- warning to stderr before writing it. This is a `Content-Type`-only check — the live
48
- API serves GeoJSON as `application/json`, so it does not (and cannot) verify that
49
- the bytes are actually valid GeoJSON, only that the type string looks JSON-ish.
39
+ No setup needed the API is open and read-only. Your first query:
50
40
 
51
- ### Global options
52
-
53
- | Option | Description |
54
- | --- | --- |
55
- | `--base-url <url>` | API base URL (default `https://warnung.bund.de`) |
56
- | `--timeout <ms>` | Per-request timeout (default `30000`; `0` disables the timeout — waits indefinitely) |
57
- | `--user-agent <ua>` | `User-Agent` header value |
58
- | `--max-retries <n>` | Retries for transient `429`/`503` responses (default `2`, capped at `10`) |
59
- | `--max-response-bytes <n>` | Cap response body size in bytes (`0` = unlimited; default 100 MiB) |
60
- | `--compact` | Print JSON on a single line |
61
- | `-o, --output <file>` | For downloads: write bytes to a file instead of stdout |
41
+ ```bash
42
+ nina map-data dwd
43
+ ```
62
44
 
63
- Global options may be given **before or after** the command, e.g.
64
- `nina --compact map-data dwd` and `nina map-data dwd --compact` are equivalent.
45
+ Returns the current severe-weather warnings from the DWD (Deutscher
46
+ Wetterdienst). Pull out just the headlines and severity levels with `jq`:
65
47
 
66
- An identifier that begins with `-` would otherwise be parsed as an option; pass it
67
- after a `--` separator so it is treated as a positional, e.g.
68
- `nina warning get -- -odd.identifier`.
48
+ ```bash
49
+ nina map-data dwd | jq '.[] | {id, severity: .severity, headline: .i18nTitle.de}'
50
+ ```
69
51
 
70
- Numeric options (`--timeout`, `--max-retries`, `--max-response-bytes`) accept only
71
- plain non-negative decimal integers; values like `5.0`, `0x10`, `1e3`, `-1` or a
72
- leading/trailing space are rejected as usage errors.
52
+ Take a warning `id` from those results and fetch its full CAP record:
73
53
 
74
- > **Note on `--base-url` + `--output`:** the base URL is trusted as given, so
75
- > `nina --base-url <any-http(s)-host> -o <file> ...` is effectively a general
76
- > "fetch this URL and write it to a file" tool. Only `http`/`https` are allowed
77
- > (`file:`/`ftp:` are rejected) and redirects are **never followed**, but point it
78
- > only at hosts you trust. Setting `--max-response-bytes 0` disables the in-memory
79
- > response-size guard entirely (including the 100 MiB default).
54
+ ```bash
55
+ nina warning get mow.DE-BY-A-W083-20240101
56
+ ```
80
57
 
81
- ### Commands
58
+ ## Commands
82
59
 
83
60
  ```text
84
- sources list the valid warning sources
85
- map-data <source> current warnings from a source
86
- (mowas | katwarn | biwapp | dwd | lhp | police)
87
- warning get <id> full CAP warning by identifier
88
- warning geojson <id> warning geometry as GeoJSON (-o to save)
89
- dashboard <ars> warnings for a region (Amtlicher Regionalschlüssel / AGS)
90
- archive mapping <id> | get <id> MoWaS archive (history + a past warning)
91
- reference notfalltipps | event-codes | data-version
61
+ sources list the valid warning sources
62
+ map-data <source> current warnings from a source
63
+ (mowas | katwarn | biwapp | dwd | lhp | police)
64
+ warning get <id> full CAP warning by identifier
65
+ warning geojson <id> warning geometry as GeoJSON (-o to save)
66
+ dashboard <ars> warnings for a region (ARS/AGS key)
67
+ archive mapping <id> MoWaS revision history for an identifier
68
+ archive get <id> a specific archived MoWaS warning
69
+ reference notfalltipps emergency-preparedness tips (German)
70
+ reference event-codes CAP event-code catalogue
71
+ reference data-version current data-version hash (for polling)
92
72
  ```
93
73
 
94
- ### Examples
74
+ ### `map-data` sources
95
75
 
96
- ```bash
97
- # Current severe-weather warnings from the DWD
98
- nina map-data dwd
76
+ | Value | Warning type |
77
+ | --- | --- |
78
+ | `mowas` | MoWaS — modular civil-protection alerts |
79
+ | `katwarn` | KATWARN — municipal/regional alerts |
80
+ | `biwapp` | BIWAPP — municipal alerts |
81
+ | `dwd` | DWD — severe-weather (national met service) |
82
+ | `lhp` | LHP — cross-state flood warnings |
83
+ | `police` | Police incident reports |
99
84
 
100
- # A specific warning (identifier from a map-data entry's `id`)
101
- nina warning get mow.DE-BY-A-W083-20240101
85
+ The canonical list is always available at runtime via `nina sources`.
102
86
 
103
- # Its geometry, saved to a file
104
- nina -o warn.geojson warning geojson mow.DE-BY-A-W083-20240101
87
+ ## Common tasks
105
88
 
106
- # All warnings affecting a district by its regional key
107
- nina dashboard 055150000000
89
+ A few recipes to get going see **[Usage.md](Usage.md)** for the full,
90
+ use-case-driven set.
108
91
 
109
- # Poll for changes cheaply
110
- nina --compact reference data-version
111
- ```
92
+ ```bash
93
+ # Current flood warnings
94
+ nina map-data lhp
112
95
 
113
- Exit codes: `0` success, `4` on a `404` from the API, `1` for any other error, non-zero for usage errors.
96
+ # Filter to only Severe or Extreme warnings across a source
97
+ nina map-data mowas | jq '[.[] | select(.severity == "Severe" or .severity == "Extreme")]'
114
98
 
115
- ---
99
+ # Full detail for one warning (id from a map-data entry)
100
+ nina warning get mow.DE-SL-SLS-W038-20260113-000
116
101
 
117
- ## Library usage
102
+ # Save the affected-area geometry as a GeoJSON file
103
+ nina warning geojson mow.DE-SL-SLS-W038-20260113-000 -o warn.geojson
118
104
 
119
- ```ts
120
- import { NinaClient, NinaApiError, type NinaSource } from "@maschinenlesbar.org/nina-warnungen-cli";
105
+ # All warnings currently affecting a district (regional key ARS/AGS)
106
+ nina dashboard 055150000000
121
107
 
122
- const client = new NinaClient(); // defaults to https://warnung.bund.de
108
+ # Cheap polling only fetch full feeds when the version hash changes
109
+ nina --compact reference data-version
110
+ ```
123
111
 
124
- const alerts = await client.mapData("dwd"); // MapWarning[]
125
- const full = await client.warnings.get(alerts[0]!.id);
126
- const region = await client.dashboard("055150000000");
112
+ ## Output & scripting
127
113
 
128
- const geo = await client.warnings.geojson(alerts[0]!.id); // raw bytes
129
- await import("node:fs/promises").then((fs) => fs.writeFile("warn.geojson", geo.data));
114
+ Every command prints **pretty JSON to stdout**. Errors and diagnostics go to
115
+ stderr, so piping stdout into `jq` stays clean.
130
116
 
131
- try {
132
- await client.warnings.get("DOES-NOT-EXIST");
133
- } catch (err) {
134
- if (err instanceof NinaApiError) console.error(err.status, err.detail);
135
- }
136
- ```
117
+ ```bash
118
+ # Count warnings for a region
119
+ nina dashboard 055150000000 | jq 'length'
137
120
 
138
- ### Client options
139
-
140
- ```ts
141
- new NinaClient({
142
- baseUrl: "https://warnung.bund.de",
143
- timeoutMs: 15_000,
144
- maxRetries: 3, // 429 / 503 are retried with linear backoff
145
- maxResponseBytes: 50 << 20, // abort responses larger than 50 MiB (0 = unlimited)
146
- userAgent: "my-app/1.0",
147
- transport: customTransport, // inject your own HTTP transport
148
- });
121
+ # Extract severity + headline from all DWD entries
122
+ nina map-data dwd | jq '.[] | {severity, title: .i18nTitle.de}'
149
123
  ```
150
124
 
151
- ### Resource groups
152
-
153
- `client.mapData(source)`, `client.dashboard(ars)`, `client.warnings` (`.get` / `.geojson`),
154
- `client.archive` (`.mapping` / `.get`), `client.reference` (`.notfalltipps` / `.eventCodes` / `.dataVersion`).
125
+ Use `--compact` for single-line JSON in pipelines and logs:
155
126
 
156
- ---
127
+ ```bash
128
+ nina --compact map-data dwd | jq -c '.[]'
129
+ ```
157
130
 
158
- ## Architecture
131
+ `--compact` (and every global option) works **before or after** the command —
132
+ both `nina --compact map-data dwd` and `nina map-data dwd --compact` do the same
133
+ thing.
159
134
 
160
- ```
161
- src/
162
- client/
163
- enums.ts # NinaSource + severity value sets (runtime + type)
164
- types.ts # response interfaces (typed summaries; full warnings as JsonObject)
165
- query.ts # dependency-free query-string builder
166
- http.ts # the Transport interface + default node:http/https transport
167
- engine.ts # URL building, retry/backoff, JSON/raw decoding, error mapping
168
- errors.ts # NinaError / NinaApiError / NinaNetworkError / NinaParseError
169
- client.ts # NinaClient — resource groups over the engine
170
- cli/
171
- io.ts # injectable I/O seam (stdout/stderr/file)
172
- shared.ts # option parsers, global-option resolver, JSON/raw renderers
173
- commands/ # warnings + archive/reference (misc) command groups
174
- program.ts # assembles the commander program from injectable deps
175
- run.ts # parses argv -> exit code (no process.exit; testable)
176
- index.ts # #! bin shim
177
- ```
135
+ An identifier that begins with `-` would be parsed as an option; pass it after a
136
+ `--` separator: `nina warning get -- -odd.identifier`.
178
137
 
179
- **Design notes**
138
+ **Exit codes** make the CLI easy to use in scripts:
180
139
 
181
- - The HTTP layer is a single `Transport` function (`(req) => Promise<HttpResponse>`). The default
182
- uses `node:http`/`node:https`; tests inject a mock. This keeps the client free of any HTTP framework.
183
- - The CLI is built around injectable `CliDeps` (client factory + I/O), so the whole program can be
184
- driven in-process by tests with a mocked client and captured output — no subprocesses.
185
- - Full CAP warning payloads are deeply nested and standard-specific, so they are returned as
186
- faithful raw `JsonObject`s rather than partially-guessed types.
187
- - The transport issues exactly one request and **does not follow 3xx redirects** — a redirect is
188
- surfaced as a `NinaApiError` like any other non-2xx status. This avoids replaying headers to a
189
- redirect target (no cross-host header leak / SSRF via `Location`).
140
+ | Code | Meaning |
141
+ | --- | --- |
142
+ | `0` | success (also `--help` / `--version`) |
143
+ | `1` | error network failure, parse error, unexpected problem |
144
+ | `4` | warning not found (`404`) |
145
+ | non-zero | bad usage / invalid argument (commander parse error) |
146
+
147
+ ## Troubleshooting
148
+
149
+ - **`command not found: nina`** — the global npm bin directory isn't on your
150
+ `PATH`. Run `npm bin -g` to find it and add it, or run via
151
+ `npx @maschinenlesbar.org/nina-warnungen-cli …`.
152
+ - **Exit `4` / "not found"** — the warning identifier doesn't exist or has
153
+ expired. Re-fetch it from a fresh `map-data` or `dashboard` result; identifiers
154
+ change as warnings are issued and cancelled.
155
+ - **Exit `1` / network error** — connectivity, DNS, or a timeout. Try again, or
156
+ raise the limit with `--timeout 60000`. For flaky networks increase retries:
157
+ `--max-retries 5`.
158
+ - **Empty result from `map-data`** — the source has no active warnings right now.
159
+ Try a different source or come back later.
160
+ - **`warning geojson` prints non-JSON content-type warning** — the API returned
161
+ an unexpected content-type (e.g. a gateway error page). The bytes are still
162
+ written, but check whether the identifier is valid.
163
+
164
+ ## Global options
165
+
166
+ These apply to every command and may be given before *or* after it:
190
167
 
191
- ---
168
+ | Option | Description |
169
+ | --- | --- |
170
+ | `-V, --version` | Print the version number |
171
+ | `-h, --help` | Show help for the program or a command |
172
+ | `--compact` | Print JSON on a single line instead of pretty-printed |
173
+ | `-o, --output <file>` | Write the command's output to a file instead of stdout (JSON commands and downloads alike) |
174
+ | `--base-url <url>` | API base URL (default `https://warnung.bund.de`) |
175
+ | `--timeout <ms>` | Per-request timeout (default `30000`; `0` waits indefinitely) |
176
+ | `--user-agent <ua>` | `User-Agent` header value |
177
+ | `--max-retries <n>` | Retries for transient `429`/`503` responses (default `2`, max `10` — higher is rejected) |
178
+ | `--max-response-bytes <n>` | Cap response body size in bytes (`0` = unlimited; default 100 MiB) |
192
179
 
193
- ## Testing
180
+ Numeric options accept only plain non-negative decimal integers — values like
181
+ `5.0`, `0x10`, `1e3`, or `-1` are rejected as usage errors.
194
182
 
195
- ```bash
196
- npm test # builds, then runs `node --test` over dist/test
197
- ```
183
+ ## Learn more
198
184
 
199
- - **`query.test.ts`**query-string serialisation.
200
- - **`http.test.ts`**the default transport against a real loopback `http.createServer` (GET, unsupported protocol, size cap, timeout, no-redirect-follow).
201
- - **`engine.test.ts`**URL building, JSON/raw decoding, error mapping, 429/503 retry, redirect-as-error, size-cap forwarding — mocked transport.
202
- - **`client.test.ts`**every endpoint's method/URL mapping, including identifier URL-encoding — mocked transport.
203
- - **`cli.test.ts`** — end-to-end command parsing, rendering, file output and exit codes, plus negative paths (network/parse/API errors, write failures, content-type warning) — mocked client.
204
- - **`shared.test.ts`** — the `parseIntArg` value parser (accepts plain decimals, rejects everything else).
185
+ - **[SKILLS.md](SKILLS.md)**Claude Code Agent Skills that drive this CLI for live warning briefings, region watches and GeoJSON maps.
186
+ - **[Usage.md](Usage.md)**full use-case-driven cookbook.
187
+ - **[GLOSSARY.md](GLOSSARY.md)**every command, source, domain term and exit code explained.
188
+ - **[DEVELOPING.md](DEVELOPING.md)**TypeScript library usage, architecture, testing, CI.
205
189
 
206
- ## Continuous integration
190
+ ## Data license
207
191
 
208
- GitHub Actions workflows under `.github/workflows/`:
192
+ This CLI is a **client** — it accesses data it does not own or redistribute. The
193
+ upstream data is © its provider and licensed **separately from this tool's code**.
194
+ See **[DATA_LICENSE.md](DATA_LICENSE.md)**.
209
195
 
210
- - **ci.yml** — type-check, build and test on Node 20/22/24 for every push and PR.
211
- - **release.yml** on a `v*` tag: verify the tag matches `package.json`, test, `npm pack`, and create a GitHub Release with the tarball.
212
- - **publish.yml** manual dispatch: publish to npm via OIDC **Trusted Publishing** (no stored `NPM_TOKEN`) with provenance.
213
- - **docs.yml** — build TypeDoc API docs and deploy to GitHub Pages on each `v*` tag.
196
+ > **BBK** — official federal warnings are amtliche Werke 5 Abs. 2 UrhG): may be
197
+ > redistributed **unaltered** with a source credit. Aggregates third-party sources
198
+ > (DWD, Länder flood portals) that carry their own terms.
214
199
 
215
200
  ## License
216
201
 
@@ -6,7 +6,7 @@ import { fileURLToPath } from "node:url";
6
6
  import { Command } from "commander";
7
7
  import { defaultIO } from "./io.js";
8
8
  import { NinaClient } from "../client/client.js";
9
- import { parseIntArg } from "./shared.js";
9
+ import { parseIntArg, parseMaxRetries } from "./shared.js";
10
10
  import { registerWarningCommands } from "./commands/warnings.js";
11
11
  import { registerMiscCommands } from "./commands/misc.js";
12
12
  /**
@@ -41,10 +41,10 @@ export function buildProgram(deps = defaultDeps) {
41
41
  .option("--base-url <url>", "API base URL", "https://warnung.bund.de")
42
42
  .option("--timeout <ms>", "per-request timeout in milliseconds (0 disables; waits indefinitely)", parseIntArg)
43
43
  .option("--user-agent <ua>", "User-Agent header value")
44
- .option("--max-retries <n>", "retries for transient 429/503 responses (capped at 10)", parseIntArg)
44
+ .option("--max-retries <n>", "retries for transient 429/503 responses (default 2, max 10)", parseMaxRetries)
45
45
  .option("--max-response-bytes <n>", "cap response body size in bytes (0 = unlimited; default 100 MiB)", parseIntArg)
46
46
  .option("--compact", "print JSON on a single line instead of pretty-printed")
47
- .option("-o, --output <file>", "for downloads: write bytes to this file instead of stdout")
47
+ .option("-o, --output <file>", "write the command's output to this file instead of stdout")
48
48
  .showHelpAfterError();
49
49
  registerWarningCommands(program, deps);
50
50
  registerMiscCommands(program, deps);
@@ -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,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE1D;;;;;GAKG;AACH,SAAS,WAAW;IAClB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,uBAAuB,EAAE,MAAM,CAAC,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,UAAU,CAAC,OAAO,CAAC;CACnD,CAAC;AAEF,MAAM,UAAU,YAAY,CAAC,OAAgB,WAAW;IACtD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,MAAM,CAAC;SACZ,WAAW,CACV,iFAAiF;QAC/E,4EAA4E,CAC/E;SACA,OAAO,CAAC,OAAO,CAAC;SAChB,MAAM,CAAC,kBAAkB,EAAE,cAAc,EAAE,yBAAyB,CAAC;SACrE,MAAM,CACL,gBAAgB,EAChB,sEAAsE,EACtE,WAAW,CACZ;SACA,MAAM,CAAC,mBAAmB,EAAE,yBAAyB,CAAC;SACtD,MAAM,CACL,mBAAmB,EACnB,wDAAwD,EACxD,WAAW,CACZ;SACA,MAAM,CACL,0BAA0B,EAC1B,kEAAkE,EAClE,WAAW,CACZ;SACA,MAAM,CAAC,WAAW,EAAE,uDAAuD,CAAC;SAC5E,MAAM,CAAC,qBAAqB,EAAE,2DAA2D,CAAC;SAC1F,kBAAkB,EAAE,CAAC;IAExB,uBAAuB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACvC,oBAAoB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAEpC,OAAO,OAAO,CAAC;AACjB,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,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE1D;;;;;GAKG;AACH,SAAS,WAAW;IAClB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,uBAAuB,EAAE,MAAM,CAAC,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,UAAU,CAAC,OAAO,CAAC;CACnD,CAAC;AAEF,MAAM,UAAU,YAAY,CAAC,OAAgB,WAAW;IACtD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,MAAM,CAAC;SACZ,WAAW,CACV,iFAAiF;QAC/E,4EAA4E,CAC/E;SACA,OAAO,CAAC,OAAO,CAAC;SAChB,MAAM,CAAC,kBAAkB,EAAE,cAAc,EAAE,yBAAyB,CAAC;SACrE,MAAM,CACL,gBAAgB,EAChB,sEAAsE,EACtE,WAAW,CACZ;SACA,MAAM,CAAC,mBAAmB,EAAE,yBAAyB,CAAC;SACtD,MAAM,CACL,mBAAmB,EACnB,6DAA6D,EAC7D,eAAe,CAChB;SACA,MAAM,CACL,0BAA0B,EAC1B,kEAAkE,EAClE,WAAW,CACZ;SACA,MAAM,CAAC,WAAW,EAAE,uDAAuD,CAAC;SAC5E,MAAM,CAAC,qBAAqB,EAAE,2DAA2D,CAAC;SAC1F,kBAAkB,EAAE,CAAC;IAExB,uBAAuB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACvC,oBAAoB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAEpC,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/cli/run.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAiBvC,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,GAAE,OAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,CAiCtF"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/cli/run.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AA2BvC,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,GAAE,OAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,CA+CtF"}
@@ -8,36 +8,55 @@ import { NinaApiError, NinaError } from "../client/errors.js";
8
8
  * Apply exitOverride + output redirection to every command in the tree.
9
9
  * commander does not propagate these to subcommands, so a parse error on a
10
10
  * subcommand would otherwise call process.exit() and bypass our error handling.
11
+ *
12
+ * Commander's own output (help, version, parse-error text) is buffered into
13
+ * `sink` so run() can route it *after* it knows the outcome: a help display goes
14
+ * to stdout (matching `--help`), genuine errors to stderr. Action output is
15
+ * written through deps.io directly and never passes through here.
11
16
  */
12
- function configureTree(command, deps) {
17
+ function configureTree(command, sink) {
13
18
  command.exitOverride();
14
19
  command.configureOutput({
15
- writeOut: (str) => deps.io.out(str.replace(/\n$/, "")),
16
- writeErr: (str) => deps.io.err(str.replace(/\n$/, "")),
20
+ writeOut: (str) => sink.out.push(str.replace(/\n$/, "")),
21
+ writeErr: (str) => sink.err.push(str.replace(/\n$/, "")),
17
22
  });
18
23
  for (const child of command.commands)
19
- configureTree(child, deps);
24
+ configureTree(child, sink);
20
25
  }
21
26
  export async function run(argv, deps = defaultDeps) {
22
27
  const program = buildProgram(deps);
23
- configureTree(program, deps);
28
+ const sink = { out: [], err: [] };
29
+ configureTree(program, sink);
30
+ // Flush commander's buffered output. `helpToStdout` routes the buffered
31
+ // writeErr lines to stdout: commander emits no-command help (a bare invocation
32
+ // or a bare command group) via writeErr, and we want that to match an explicit
33
+ // `--help` (stdout, exit 0) rather than landing on stderr.
34
+ const flush = (helpToStdout) => {
35
+ for (const line of sink.out)
36
+ deps.io.out(line);
37
+ const errSink = helpToStdout ? deps.io.out : deps.io.err;
38
+ for (const line of sink.err)
39
+ errSink(line);
40
+ };
24
41
  try {
25
42
  await program.parseAsync(argv, { from: "user" });
43
+ flush(false);
26
44
  return 0;
27
45
  }
28
46
  catch (err) {
29
47
  if (err instanceof CommanderError) {
30
- // When commander has displayed help whether from an explicit --help
31
- // (code "commander.helpDisplayed", exitCode 0) or from a bare invocation /
32
- // bare command group with no subcommand (code "commander.help", exitCode 1)
33
- // — the visible output is the same successful help text, so exit 0 in both
34
- // cases for a consistent "showed help" result. Genuine parse errors
35
- // (unknown command/option, missing argument) keep their own non-zero code.
36
- if (err.code === "commander.help" || err.code === "commander.helpDisplayed") {
37
- return 0;
38
- }
39
- return err.exitCode;
48
+ // A help/version display is a success: commander shows the requested text —
49
+ // help from an explicit `--help` ("commander.helpDisplayed") or from a bare
50
+ // invocation / bare command group ("commander.help"), or the version from
51
+ // `--version` so we exit 0. Help written for a bare invocation lands on
52
+ // writeErr, so route it to stdout to match `--help`. Genuine parse errors
53
+ // (unknown command/option, missing argument) keep their own non-zero exit
54
+ // code and stay on stderr.
55
+ const isHelp = err.code === "commander.help" || err.code === "commander.helpDisplayed";
56
+ flush(isHelp);
57
+ return isHelp ? 0 : err.exitCode;
40
58
  }
59
+ flush(false);
41
60
  if (err instanceof NinaApiError) {
42
61
  deps.io.err(`Error: ${err.message}`);
43
62
  // Map a few notable statuses to distinct exit codes for scripting.
@@ -1 +1 @@
1
- {"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/cli/run.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,6EAA6E;AAC7E,+DAA+D;AAE/D,OAAO,EAAE,cAAc,EAAgB,MAAM,WAAW,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEzD,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAE9D;;;;GAIG;AACH,SAAS,aAAa,CAAC,OAAgB,EAAE,IAAa;IACpD,OAAO,CAAC,YAAY,EAAE,CAAC;IACvB,OAAO,CAAC,eAAe,CAAC;QACtB,QAAQ,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACtD,QAAQ,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;KACvD,CAAC,CAAC;IACH,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,QAAQ;QAAE,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,GAAG,CAAC,IAAc,EAAE,OAAgB,WAAW;IACnE,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IACnC,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAE7B,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QACjD,OAAO,CAAC,CAAC;IACX,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,cAAc,EAAE,CAAC;YAClC,sEAAsE;YACtE,2EAA2E;YAC3E,4EAA4E;YAC5E,2EAA2E;YAC3E,oEAAoE;YACpE,2EAA2E;YAC3E,IAAI,GAAG,CAAC,IAAI,KAAK,gBAAgB,IAAI,GAAG,CAAC,IAAI,KAAK,yBAAyB,EAAE,CAAC;gBAC5E,OAAO,CAAC,CAAC;YACX,CAAC;YACD,OAAO,GAAG,CAAC,QAAQ,CAAC;QACtB,CAAC;QACD,IAAI,GAAG,YAAY,YAAY,EAAE,CAAC;YAChC,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,UAAU,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;YACrC,mEAAmE;YACnE,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;gBAAE,OAAO,CAAC,CAAC;YACjC,OAAO,CAAC,CAAC;QACX,CAAC;QACD,IAAI,GAAG,YAAY,SAAS,EAAE,CAAC;YAC7B,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,UAAU,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;YACrC,OAAO,CAAC,CAAC;QACX,CAAC;QACD,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,qBAAqB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACrF,OAAO,CAAC,CAAC;IACX,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/cli/run.ts"],"names":[],"mappings":"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"}
@@ -9,6 +9,16 @@ import type { EngineOptions, RawResponse } from "../client/engine.js";
9
9
  * "Infinity"), so a typo never silently becomes a surprising value.
10
10
  */
11
11
  export declare function parseIntArg(value: string): number;
12
+ /**
13
+ * The most retries the engine will ever perform. Mirrors the engine's internal
14
+ * `MAX_RETRIES_CAP` (which still clamps as a defence for direct library users);
15
+ * surfacing it here lets the CLI *reject* an out-of-range value with a clear
16
+ * message rather than silently clamping it, matching the rest of the CLI's
17
+ * validate-and-reject style.
18
+ */
19
+ export declare const MAX_RETRIES_LIMIT = 10;
20
+ /** commander value-parser for `--max-retries`: a non-negative integer, max 10. */
21
+ export declare function parseMaxRetries(value: string): number;
12
22
  /**
13
23
  * Validate a positional argument against an allowed set (commander does not
14
24
  * support .choices() on positional args). Throws a NinaError so run() prints a
@@ -16,10 +26,13 @@ export declare function parseIntArg(value: string): number;
16
26
  */
17
27
  export declare function assertEnum<T extends string>(value: string, allowed: readonly T[], argName: string): T;
18
28
  /**
19
- * Validate a required free-form positional (an identifier or region key). An
20
- * empty or whitespace-only value would build a path like `dashboard/.json` and
21
- * come back as a remote 404 for what is really a local input error; reject it
22
- * up front with a clear message (exit 1) instead.
29
+ * Validate a required free-form positional (an identifier or region key). Both
30
+ * are single path segments, so two inputs can only ever produce a remote 404 (or
31
+ * a confusing parse error) for what is really a local input mistake reject them
32
+ * up front with a clear message (exit 1) instead:
33
+ * - empty/whitespace-only (would build a path like `dashboard/.json`);
34
+ * - one containing a path separator (`/` or `\`), e.g. a `../../etc/passwd`
35
+ * traversal attempt — these are percent-encoded and can never match a real id.
23
36
  */
24
37
  export declare function requireIdentifier(value: string, argName: string): string;
25
38
  export interface GlobalOptions {
@@ -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;AAEvC,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEtE;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CASjD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EACzC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,SAAS,CAAC,EAAE,EACrB,OAAO,EAAE,MAAM,GACd,CAAC,CAKH;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAKxE;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;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,uEAAuE;AACvE,wBAAgB,eAAe,CAAC,MAAM,EAAE,aAAa,GAAG,aAAa,CAQpE;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAUrF;AAeD;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,OAAO,EACb,MAAM,EAAE,aAAa,EACrB,QAAQ,EAAE,WAAW,EACrB,iBAAiB,CAAC,EAAE,MAAM,GACzB,IAAI,CAcN;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"}
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/cli/shared.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEtE;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CASjD;AAED;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAEpC,kFAAkF;AAClF,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMrD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EACzC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,SAAS,CAAC,EAAE,EACrB,OAAO,EAAE,MAAM,GACd,CAAC,CAKH;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAQxE;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;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,uEAAuE;AACvE,wBAAgB,eAAe,CAAC,MAAM,EAAE,aAAa,GAAG,aAAa,CAQpE;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAUrF;AAeD;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,OAAO,EACb,MAAM,EAAE,aAAa,EACrB,QAAQ,EAAE,WAAW,EACrB,iBAAiB,CAAC,EAAE,MAAM,GACzB,IAAI,CAcN;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"}
@@ -20,6 +20,22 @@ export function parseIntArg(value) {
20
20
  }
21
21
  return n;
22
22
  }
23
+ /**
24
+ * The most retries the engine will ever perform. Mirrors the engine's internal
25
+ * `MAX_RETRIES_CAP` (which still clamps as a defence for direct library users);
26
+ * surfacing it here lets the CLI *reject* an out-of-range value with a clear
27
+ * message rather than silently clamping it, matching the rest of the CLI's
28
+ * validate-and-reject style.
29
+ */
30
+ export const MAX_RETRIES_LIMIT = 10;
31
+ /** commander value-parser for `--max-retries`: a non-negative integer, max 10. */
32
+ export function parseMaxRetries(value) {
33
+ const n = parseIntArg(value);
34
+ if (n > MAX_RETRIES_LIMIT) {
35
+ throw new InvalidArgumentError(`Expected a value between 0 and ${MAX_RETRIES_LIMIT}.`);
36
+ }
37
+ return n;
38
+ }
23
39
  /**
24
40
  * Validate a positional argument against an allowed set (commander does not
25
41
  * support .choices() on positional args). Throws a NinaError so run() prints a
@@ -32,15 +48,21 @@ export function assertEnum(value, allowed, argName) {
32
48
  return value;
33
49
  }
34
50
  /**
35
- * Validate a required free-form positional (an identifier or region key). An
36
- * empty or whitespace-only value would build a path like `dashboard/.json` and
37
- * come back as a remote 404 for what is really a local input error; reject it
38
- * up front with a clear message (exit 1) instead.
51
+ * Validate a required free-form positional (an identifier or region key). Both
52
+ * are single path segments, so two inputs can only ever produce a remote 404 (or
53
+ * a confusing parse error) for what is really a local input mistake reject them
54
+ * up front with a clear message (exit 1) instead:
55
+ * - empty/whitespace-only (would build a path like `dashboard/.json`);
56
+ * - one containing a path separator (`/` or `\`), e.g. a `../../etc/passwd`
57
+ * traversal attempt — these are percent-encoded and can never match a real id.
39
58
  */
40
59
  export function requireIdentifier(value, argName) {
41
60
  if (value.trim() === "") {
42
61
  throw new NinaError(`A non-empty ${argName} is required.`);
43
62
  }
63
+ if (/[/\\]/.test(value)) {
64
+ throw new NinaError(`Invalid ${argName} "${value}": must not contain a path separator.`);
65
+ }
44
66
  return value;
45
67
  }
46
68
  /** Translate resolved global CLI options into client EngineOptions. */
@@ -1 +1 @@
1
- {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../../src/cli/shared.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../../src/cli/shared.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../src/client/engine.ts"],"names":[],"mappings":"AAIA,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EAAoB,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAGhE,eAAO,MAAM,gBAAgB,4BAA4B,CAAC;AAG1D,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qEAAqE;IACrE,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;AAeD,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,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgC;gBAE1C,OAAO,GAAE,aAAkB;IAWvC,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,MAAM;IAMnD,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;IAmCvB,0DAA0D;IACpD,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC;IAU/D,+EAA+E;IACzE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IAIrF,OAAO,CAAC,UAAU;CAYnB"}
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../src/client/engine.ts"],"names":[],"mappings":"AAIA,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EAAoB,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAGhE,eAAO,MAAM,gBAAgB,4BAA4B,CAAC;AAG1D,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qEAAqE;IACrE,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;AAqCD,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,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgC;gBAE1C,OAAO,GAAE,aAAkB;IAWvC,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,MAAM;IAyBnD,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;IAqCvB,0DAA0D;IACpD,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC;IAU/D,+EAA+E;IACzE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IAIrF,OAAO,CAAC,UAAU;CAenB"}
@@ -3,7 +3,7 @@
3
3
  // (429, 503), and decodes responses.
4
4
  import { nodeHttpTransport } from "./http.js";
5
5
  import { buildQueryString } from "./query.js";
6
- import { NinaApiError, NinaParseError } from "./errors.js";
6
+ import { NinaApiError, NinaNetworkError, NinaParseError } from "./errors.js";
7
7
  export const DEFAULT_BASE_URL = "https://warnung.bund.de";
8
8
  const DEFAULT_USER_AGENT = "nina-warnungen-cli";
9
9
  const DEFAULT_MAX_RESPONSE_BYTES = 100 * 1024 * 1024;
@@ -14,6 +14,28 @@ const DEFAULT_MAX_RESPONSE_BYTES = 100 * 1024 * 1024;
14
14
  * the process for hours. 10 retries is well beyond any realistic transient blip.
15
15
  */
16
16
  const MAX_RETRIES_CAP = 10;
17
+ /**
18
+ * Strip control characters (all C0/C1 except tab and newline, plus DEL) out of a
19
+ * string that originates in an attacker-controlled response — the error `detail`
20
+ * and the echoed Content-Type. `JSON.parse` decodes an escaped ESC in an error
21
+ * body into a real ESC byte, so without this a hostile/MITM'd endpoint could
22
+ * drive ANSI/OSC escape sequences into the user's terminal when the message is
23
+ * printed to stderr. The success JSON path is already safe (`JSON.stringify`
24
+ * escapes these), so this only needs to cover text that flows into a message.
25
+ *
26
+ * Filtered by code point rather than a regex literal, so no raw control byte ever
27
+ * appears in this source file.
28
+ */
29
+ function sanitizeServerText(text) {
30
+ let out = "";
31
+ for (const ch of text) {
32
+ const n = ch.codePointAt(0) ?? 0;
33
+ if (n <= 8 || (n >= 0x0b && n <= 0x1f) || (n >= 0x7f && n <= 0x9f))
34
+ continue;
35
+ out += ch;
36
+ }
37
+ return out;
38
+ }
17
39
  const realSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
18
40
  export class RequestEngine {
19
41
  baseUrl;
@@ -36,6 +58,24 @@ export class RequestEngine {
36
58
  }
37
59
  /** Build a fully-qualified URL from a path and optional query parameters. */
38
60
  buildUrl(path, query) {
61
+ // Validate the base URL up front so a malformed `baseUrl` (e.g. a stray
62
+ // `--base-url notaurl`) yields a clear message naming the offending value,
63
+ // instead of an opaque "Invalid URL" that carries the full request path and
64
+ // reads as if the path were at fault.
65
+ let parsed;
66
+ try {
67
+ parsed = new URL(this.baseUrl);
68
+ }
69
+ catch {
70
+ throw new NinaNetworkError(`Invalid base URL: ${JSON.stringify(this.baseUrl)}`);
71
+ }
72
+ // Enforce the http(s) scheme here, transport-independently, so the guarantee
73
+ // holds even for a library user who injects a custom Transport that doesn't
74
+ // re-check (the default transport rejects non-http(s) too, but a swapped-in
75
+ // one might not, and could otherwise reach a file:/ftp: driver).
76
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
77
+ throw new NinaNetworkError(`Unsupported base URL scheme "${parsed.protocol}" (only http: and https: are allowed).`);
78
+ }
39
79
  const normalizedPath = path.startsWith("/") ? path : `/${path}`;
40
80
  const qs = query ? buildQueryString(query) : "";
41
81
  return `${this.baseUrl}${normalizedPath}${qs ? `?${qs}` : ""}`;
@@ -64,7 +104,9 @@ export class RequestEngine {
64
104
  await this.sleep(this.retryDelayMs * attempt);
65
105
  continue;
66
106
  }
67
- const contentType = String(response.headers["content-type"] ?? "");
107
+ // The Content-Type is echoed to stderr (raw-download type-mismatch warning),
108
+ // so strip control characters at the source before it leaves the engine.
109
+ const contentType = sanitizeServerText(String(response.headers["content-type"] ?? ""));
68
110
  if (status < 200 || status >= 300) {
69
111
  throw this.toApiError(method, url, status, response.body);
70
112
  }
@@ -99,6 +141,10 @@ export class RequestEngine {
99
141
  catch {
100
142
  // Non-JSON error body; leave detail undefined.
101
143
  }
144
+ // `detail` came from the response body; strip control characters so a hostile
145
+ // endpoint cannot inject terminal escape sequences via the stderr error message.
146
+ if (detail !== undefined)
147
+ detail = sanitizeServerText(detail);
102
148
  return new NinaApiError({ status, url, method, body: text, detail });
103
149
  }
104
150
  }
@@ -1 +1 @@
1
- {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../../src/client/engine.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../../src/client/engine.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../../src/client/http.ts"],"names":[],"mappings":"AAQA,OAAO,IAAI,MAAM,WAAW,CAAC;AAyB7B,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mFAAmF;IACnF,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,IAAI,CAAC,mBAAmB,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;AAExE;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,SA6E5B,CAAC"}
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../../src/client/http.ts"],"names":[],"mappings":"AAQA,OAAO,IAAI,MAAM,WAAW,CAAC;AAyB7B,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mFAAmF;IACnF,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,IAAI,CAAC,mBAAmB,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;AAExE;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,SAuG5B,CAAC"}
@@ -52,6 +52,19 @@ export const nodeHttpTransport = (request) => new Promise((resolve, reject) => {
52
52
  const isHttps = url.protocol === "https:";
53
53
  const driver = isHttps ? https : http;
54
54
  const maxBytes = request.maxResponseBytes;
55
+ const timeoutMs = request.timeoutMs;
56
+ // Wall-clock deadline for the whole request. `req.setTimeout()` alone is an
57
+ // *idle-socket* timeout that resets on every byte, so a slow-drip server that
58
+ // sends one byte just under the idle window (and stays under maxResponseBytes)
59
+ // could keep the request alive indefinitely. A single fixed timer bounds the
60
+ // total time from request start to `end`, independent of the byte cadence.
61
+ let deadline;
62
+ const clearDeadline = () => {
63
+ if (deadline !== undefined) {
64
+ clearTimeout(deadline);
65
+ deadline = undefined;
66
+ }
67
+ };
55
68
  const req = driver.request(url, {
56
69
  method: request.method,
57
70
  headers: request.headers,
@@ -65,6 +78,7 @@ export const nodeHttpTransport = (request) => new Promise((resolve, reject) => {
65
78
  received += chunk.length;
66
79
  if (maxBytes !== undefined && received > maxBytes) {
67
80
  aborted = true;
81
+ clearDeadline();
68
82
  res.destroy();
69
83
  reject(new NinaNetworkError(`Response exceeded maxResponseBytes (${maxBytes})`));
70
84
  return;
@@ -74,6 +88,7 @@ export const nodeHttpTransport = (request) => new Promise((resolve, reject) => {
74
88
  res.on("end", () => {
75
89
  if (aborted)
76
90
  return;
91
+ clearDeadline();
77
92
  resolve({
78
93
  status: res.statusCode ?? 0,
79
94
  headers: res.headers,
@@ -83,15 +98,25 @@ export const nodeHttpTransport = (request) => new Promise((resolve, reject) => {
83
98
  res.on("error", (err) => {
84
99
  if (aborted)
85
100
  return; // we already rejected with the size-cap error
101
+ clearDeadline();
86
102
  reject(new NinaNetworkError(`Response stream error: ${err.message}`, { cause: err }));
87
103
  });
88
104
  });
89
- if (request.timeoutMs && request.timeoutMs > 0) {
90
- req.setTimeout(request.timeoutMs, () => {
91
- req.destroy(new NinaNetworkError(`Request timed out after ${request.timeoutMs}ms`));
105
+ if (timeoutMs && timeoutMs > 0) {
106
+ // Idle-socket timeout (resets on activity)...
107
+ req.setTimeout(timeoutMs, () => {
108
+ req.destroy(new NinaNetworkError(`Request timed out after ${timeoutMs}ms`));
92
109
  });
110
+ // ...plus a hard wall-clock deadline (does not reset) so a slow drip cannot
111
+ // outlast the caller's timeout budget.
112
+ deadline = setTimeout(() => {
113
+ req.destroy(new NinaNetworkError(`Request exceeded the ${timeoutMs}ms deadline`));
114
+ }, timeoutMs);
115
+ // Don't let the deadline timer keep the event loop alive on its own.
116
+ deadline.unref?.();
93
117
  }
94
118
  req.on("error", (err) => {
119
+ clearDeadline();
95
120
  // A timeout destroy already passes an NinaNetworkError; don't double-wrap.
96
121
  if (err instanceof NinaNetworkError) {
97
122
  reject(err);
@@ -1 +1 @@
1
- {"version":3,"file":"http.js","sourceRoot":"","sources":["../../../src/client/http.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../../src/client/http.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maschinenlesbar.org/nina-warnungen-cli",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "TypeScript API client and CLI for the open NINA civil-protection warning API (warnung.bund.de)",
5
5
  "type": "module",
6
6
  "bin": {