@maschinenlesbar.org/mudab-cli 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/CONTRIBUTING.md +25 -0
  2. package/LICENSE +661 -0
  3. package/LICENSING.md +47 -0
  4. package/README.md +78 -0
  5. package/dist/src/cli/commands/list.d.ts +4 -0
  6. package/dist/src/cli/commands/list.d.ts.map +1 -0
  7. package/dist/src/cli/commands/list.js +70 -0
  8. package/dist/src/cli/commands/list.js.map +1 -0
  9. package/dist/src/cli/index.d.ts +3 -0
  10. package/dist/src/cli/index.d.ts.map +1 -0
  11. package/dist/src/cli/index.js +11 -0
  12. package/dist/src/cli/index.js.map +1 -0
  13. package/dist/src/cli/io.d.ts +12 -0
  14. package/dist/src/cli/io.d.ts.map +1 -0
  15. package/dist/src/cli/io.js +7 -0
  16. package/dist/src/cli/io.js.map +1 -0
  17. package/dist/src/cli/program.d.ts +7 -0
  18. package/dist/src/cli/program.d.ts.map +1 -0
  19. package/dist/src/cli/program.js +54 -0
  20. package/dist/src/cli/program.js.map +1 -0
  21. package/dist/src/cli/run.d.ts +3 -0
  22. package/dist/src/cli/run.d.ts.map +1 -0
  23. package/dist/src/cli/run.js +87 -0
  24. package/dist/src/cli/run.js.map +1 -0
  25. package/dist/src/cli/shared.d.ts +79 -0
  26. package/dist/src/cli/shared.d.ts.map +1 -0
  27. package/dist/src/cli/shared.js +121 -0
  28. package/dist/src/cli/shared.js.map +1 -0
  29. package/dist/src/client/client.d.ts +37 -0
  30. package/dist/src/client/client.d.ts.map +1 -0
  31. package/dist/src/client/client.js +78 -0
  32. package/dist/src/client/client.js.map +1 -0
  33. package/dist/src/client/engine.d.ts +59 -0
  34. package/dist/src/client/engine.d.ts.map +1 -0
  35. package/dist/src/client/engine.js +125 -0
  36. package/dist/src/client/engine.js.map +1 -0
  37. package/dist/src/client/errors.d.ts +36 -0
  38. package/dist/src/client/errors.d.ts.map +1 -0
  39. package/dist/src/client/errors.js +43 -0
  40. package/dist/src/client/errors.js.map +1 -0
  41. package/dist/src/client/http.d.ts +26 -0
  42. package/dist/src/client/http.d.ts.map +1 -0
  43. package/dist/src/client/http.js +80 -0
  44. package/dist/src/client/http.js.map +1 -0
  45. package/dist/src/client/index.d.ts +9 -0
  46. package/dist/src/client/index.d.ts.map +1 -0
  47. package/dist/src/client/index.js +7 -0
  48. package/dist/src/client/index.js.map +1 -0
  49. package/dist/src/client/types.d.ts +173 -0
  50. package/dist/src/client/types.d.ts.map +1 -0
  51. package/dist/src/client/types.js +8 -0
  52. package/dist/src/client/types.js.map +1 -0
  53. package/dist/src/index.d.ts +2 -0
  54. package/dist/src/index.d.ts.map +1 -0
  55. package/dist/src/index.js +3 -0
  56. package/dist/src/index.js.map +1 -0
  57. package/package.json +70 -0
package/LICENSING.md ADDED
@@ -0,0 +1,47 @@
1
+ # Licensing
2
+
3
+ `mudab-cli` is **dual-licensed**. You may use it under **either** of the
4
+ following, at your choice:
5
+
6
+ ## 1. Open source — AGPL-3.0-or-later (default)
7
+
8
+ Free to use, modify, and distribute under the
9
+ [GNU Affero General Public License v3.0 or later](LICENSE).
10
+
11
+ The key obligation is the AGPL's **§13 network clause**: if you run a modified
12
+ version of this software to provide a service to others over a network, you must
13
+ make the complete corresponding source code of your modified version available
14
+ to the users of that service. The usual copyleft terms also apply when you
15
+ distribute the software or works based on it.
16
+
17
+ This is the right option for open-source projects, internal and personal use,
18
+ evaluation, and anyone able to comply with the AGPL.
19
+
20
+ ## 2. Commercial license
21
+
22
+ If the AGPL's obligations do not fit your situation — for example, you want to:
23
+
24
+ - embed `mudab-cli` in a **closed-source / proprietary product**, or
25
+ - run a **modified version as a proprietary network service (SaaS)** without
26
+ disclosing your source, or
27
+ - keep your modifications private,
28
+
29
+ then a separate **commercial license** is available. It grants use of the same
30
+ code without the AGPL's copyleft and network-disclosure obligations, under
31
+ negotiated terms.
32
+
33
+ **To obtain a commercial license, contact:** sebs@2xs.org
34
+
35
+ ## Why this is possible
36
+
37
+ The copyright holder owns 100% of the source code in this repository and can
38
+ therefore offer it under more than one license simultaneously. This project does
39
+ **not** accept external code contributions (see [CONTRIBUTING.md](CONTRIBUTING.md)),
40
+ which keeps copyright ownership single and unambiguous — a prerequisite for
41
+ offering the commercial option. The only third-party dependency that ships with
42
+ the package, [`commander`](https://github.com/tj/commander.js), is MIT-licensed
43
+ and compatible with both licensing options.
44
+
45
+ ---
46
+
47
+ Copyright © 2026 Sebastian Schürmann. All rights reserved.
package/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # mudab-cli
2
+
3
+ A tiny, dependency-light **TypeScript client + CLI** for the **MUDAB**
4
+ (Meeresumweltdatenbank — the German marine environment database) REST API:
5
+ marine-monitoring data from the coastal Bundesländer and research institutions,
6
+ hosted by the Bundesanstalt für Gewässerkunde (BfG) and surfaced by the
7
+ Umweltbundesamt (UBA). A [bund.dev](https://bund.dev) API.
8
+
9
+ - **No API key.** The API is open — no auth, no account.
10
+ - **Zero runtime HTTP dependencies.** Built on `node:http`/`https`; the CLI's only
11
+ runtime dependency is `commander`.
12
+ - **Library + CLI.** Use the typed `MudabClient` in code, or the `mudab` command.
13
+
14
+ > **We provide the tool, not the data.** The data is © its providers (see
15
+ > [DATA_LICENSE.md](DATA_LICENSE.md)); its terms are **not** stated as open — check
16
+ > before redistributing.
17
+
18
+ ## Install
19
+
20
+ ```bash
21
+ npm install -g @maschinenlesbar.org/mudab-cli # the `mudab` command
22
+ # or as a library:
23
+ npm install @maschinenlesbar.org/mudab-cli
24
+ ```
25
+
26
+ ## CLI
27
+
28
+ Every command lists one dataset. Results are paged with `--from`/`--count`
29
+ (default **100** rows); `--all` fetches the whole table.
30
+
31
+ ```bash
32
+ mudab stations --count 5 # measurement stations
33
+ mudab project-stations # monitoring projects (region, institute)
34
+ mudab parameters --compartment wasser # water parameters
35
+ mudab measurements --from 0 --count 200 # station measurements (page it — huge)
36
+ mudab plc-stations # HELCOM PLC river-load stations
37
+ mudab plc-measurements --all # river pollutant loads
38
+ mudab compartments # compartment code table (offline)
39
+ ```
40
+
41
+ > **The API does no server-side filtering or sorting.** Its OpenAPI spec advertises
42
+ > `filter`/`orderby` on every endpoint, but the live server **ignores** them, so the
43
+ > CLI does not offer filter flags. Fetch a page and filter with `jq`:
44
+ >
45
+ > ```bash
46
+ > mudab parameters --all --compact | jq '[.[] | select(.COMPT_DS=="CW")]'
47
+ > ```
48
+
49
+ Global flags: `--base-url`, `--timeout`, `--user-agent`, `--max-retries`,
50
+ `--max-response-bytes`, `--compact`. See [Usage.md](Usage.md) for the full reference.
51
+
52
+ ## Library
53
+
54
+ ```ts
55
+ import { MudabClient } from "@maschinenlesbar.org/mudab-cli";
56
+
57
+ const mudab = new MudabClient();
58
+ const stations = await mudab.stations({ range: { from: 0, count: 10 } });
59
+ const waterParams = await mudab.parameters({ range: { from: 0, count: 50 } }, "wasser");
60
+ ```
61
+
62
+ Each method returns a typed row array (the client extracts it from the API's
63
+ single-key wrapper object). Only `range` is honoured by the server — see the note in
64
+ `FilterRequest`.
65
+
66
+ ## Documentation
67
+
68
+ - [Usage.md](Usage.md) — full command reference and exit codes
69
+ - [DEVELOPING.md](DEVELOPING.md) — architecture, testing, the live-API quirks
70
+ - [GLOSSARY.md](GLOSSARY.md) — MUDAB domain terms (stations, compartments, PLC)
71
+ - [DATA_LICENSE.md](DATA_LICENSE.md) — upstream data terms (distinct from the code license)
72
+ - [SKILLS.md](SKILLS.md) — the Claude Code skills this repo ships
73
+
74
+ ## Licence
75
+
76
+ Code is dual-licensed **AGPL-3.0-or-later OR commercial** — see
77
+ [LICENSING.md](LICENSING.md). No external code contributions are accepted (see
78
+ [CONTRIBUTING.md](CONTRIBUTING.md)); bug reports and AGPL forks are welcome.
@@ -0,0 +1,4 @@
1
+ import { type Command } from "commander";
2
+ import type { CliDeps } from "../io.js";
3
+ export declare function registerCommands(program: Command, deps: CliDeps): void;
4
+ //# sourceMappingURL=list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/list.ts"],"names":[],"mappings":"AAKA,OAAO,EAAU,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAkDxC,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAmCtE"}
@@ -0,0 +1,70 @@
1
+ // Command group for the MUDAB CLI: one list command per endpoint (stations,
2
+ // parameters, measurements, and the HELCOM PLC set), plus an offline
3
+ // `compartments` code table. Every list command shares the range/filter/orderby
4
+ // options and returns the endpoint's row array as JSON.
5
+ import { Option } from "commander";
6
+ import { action, addListOptions, buildFilterRequest, renderJson } from "../shared.js";
7
+ const LIST_COMMANDS = [
8
+ { name: "stations", desc: "List measurement stations (STATION_SMALL)", run: (c, r) => c.stations(r) },
9
+ {
10
+ name: "project-stations",
11
+ desc: "List project stations (PROJECTSTATION_SMALL)",
12
+ run: (c, r) => c.projectStations(r),
13
+ },
14
+ {
15
+ name: "measurements",
16
+ desc: "List individual station measurements (MV_STATION_MSMNT) — a very large table",
17
+ run: (c, r) => c.measurements(r),
18
+ },
19
+ {
20
+ name: "plc-stations",
21
+ desc: "List HELCOM PLC stations (V_PLC_STATION)",
22
+ run: (c, r) => c.plcStations(r),
23
+ },
24
+ {
25
+ name: "plc-parameters",
26
+ desc: "List parameters measured at PLC stations (V_GEMESSENE_PARA_PLC)",
27
+ run: (c, r) => c.plcParameters(r),
28
+ },
29
+ {
30
+ name: "plc-measurements",
31
+ desc: "List measured values at PLC stations (V_MESSWERTE_PLC)",
32
+ run: (c, r) => c.plcMeasurements(r),
33
+ },
34
+ ];
35
+ const PARAMETER_COMPARTMENTS = ["biologie", "biota", "wasser", "sediment"];
36
+ /**
37
+ * The compartment (COMPT_DS) code table, verified live. NOTE: this is NOT
38
+ * exhaustive — the API also returns codes outside the documented set (e.g. `MM`).
39
+ */
40
+ const COMPARTMENTS = [
41
+ { code: "CW", label: "Wasser (water)", parameterCompartment: "wasser" },
42
+ { code: "CS", label: "Sediment", parameterCompartment: "sediment" },
43
+ { code: "CF", label: "Biota", parameterCompartment: "biota" },
44
+ { code: "BL", label: "Biologie (biology)", parameterCompartment: "biologie" },
45
+ ];
46
+ export function registerCommands(program, deps) {
47
+ for (const sub of LIST_COMMANDS) {
48
+ const cmd = program.command(sub.name).description(sub.desc);
49
+ addListOptions(cmd).action(action(deps, async ({ client, global, opts }) => {
50
+ renderJson(deps, global, await sub.run(client, buildFilterRequest(opts)));
51
+ }));
52
+ }
53
+ // `parameters` also offers the compartment-specific endpoints via --compartment.
54
+ const params = program
55
+ .command("parameters")
56
+ .description("List measured parameters (MV_PARAMETER)")
57
+ .addOption(new Option("--compartment <c>", "restrict to a compartment's parameter endpoint").choices(PARAMETER_COMPARTMENTS));
58
+ addListOptions(params).action(action(deps, async ({ client, global, opts }) => {
59
+ const compartment = opts["compartment"];
60
+ const req = buildFilterRequest(opts);
61
+ renderJson(deps, global, await client.parameters(req, compartment));
62
+ }));
63
+ program
64
+ .command("compartments")
65
+ .description("Print the compartment (COMPT_DS) code table — works offline, no request")
66
+ .action(action(deps, async ({ global }) => {
67
+ renderJson(deps, global, COMPARTMENTS);
68
+ }));
69
+ }
70
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../../../src/cli/commands/list.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ // Bin shim: parse argv, run the CLI, and set the process exit code. All real
3
+ // logic lives in run.ts (testable without spawning a subprocess).
4
+ import { run } from "./run.js";
5
+ run(process.argv.slice(2)).then((code) => {
6
+ process.exitCode = code;
7
+ }, (err) => {
8
+ process.stderr.write(`Unexpected error: ${err instanceof Error ? err.message : String(err)}\n`);
9
+ process.exitCode = 1;
10
+ });
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cli/index.ts"],"names":[],"mappings":";AACA,6EAA6E;AAC7E,kEAAkE;AAElE,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/B,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAC7B,CAAC,IAAI,EAAE,EAAE;IACP,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;AAC1B,CAAC,EACD,CAAC,GAAY,EAAE,EAAE;IACf,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChG,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CACF,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { MudabClient, MudabClientOptions } from "../client/client.js";
2
+ export interface CliIO {
3
+ out(text: string): void;
4
+ err(text: string): void;
5
+ }
6
+ export interface CliDeps {
7
+ io: CliIO;
8
+ /** Build a client from the resolved global options (injectable for tests). */
9
+ createClient(options: MudabClientOptions): MudabClient;
10
+ }
11
+ export declare const defaultIO: CliIO;
12
+ //# sourceMappingURL=io.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"io.d.ts","sourceRoot":"","sources":["../../../src/cli/io.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE3E,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,kBAAkB,GAAG,WAAW,CAAC;CACxD;AAED,eAAO,MAAM,SAAS,EAAE,KAGvB,CAAC"}
@@ -0,0 +1,7 @@
1
+ // I/O seam for the CLI. Everything the CLI writes goes through a CliIO object so
2
+ // tests can capture output instead of hitting the real stdout/stderr.
3
+ export const defaultIO = {
4
+ out: (text) => process.stdout.write(text + "\n"),
5
+ err: (text) => process.stderr.write(text + "\n"),
6
+ };
7
+ //# sourceMappingURL=io.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"io.js","sourceRoot":"","sources":["../../../src/cli/io.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,sEAAsE;AAetE,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"}
@@ -0,0 +1,7 @@
1
+ import { Command } from "commander";
2
+ import type { CliDeps } from "./io.js";
3
+ export declare const VERSION: string;
4
+ /** Default dependencies: real client + real stdout/stderr. */
5
+ export declare const defaultDeps: CliDeps;
6
+ export declare function buildProgram(deps?: CliDeps): Command;
7
+ //# sourceMappingURL=program.d.ts.map
@@ -0,0 +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;AAsBvC,eAAO,MAAM,OAAO,QAAgB,CAAC;AAErC,8DAA8D;AAC9D,eAAO,MAAM,WAAW,EAAE,OAGzB,CAAC;AAEF,wBAAgB,YAAY,CAAC,IAAI,GAAE,OAAqB,GAAG,OAAO,CAkCjE"}
@@ -0,0 +1,54 @@
1
+ // Assemble the full commander program. The program is built around an injectable
2
+ // CliDeps so the entire CLI can be driven in tests with a mocked client and
3
+ // captured output.
4
+ import { readFileSync } from "node:fs";
5
+ import { fileURLToPath } from "node:url";
6
+ import { Command } from "commander";
7
+ import { defaultIO } from "./io.js";
8
+ import { MudabClient } from "../client/client.js";
9
+ import { parseIntArg, parseBoundedInt, parseHeaderValue, parseNonEmpty } from "./shared.js";
10
+ import { registerCommands } from "./commands/list.js";
11
+ /**
12
+ * Single source of truth for the version: read from package.json at runtime
13
+ * rather than duplicating a literal that can silently drift after a release bump.
14
+ * From the compiled location (dist/src/cli/program.js) package.json is three
15
+ * directories up; the same offset holds for the source under src/cli.
16
+ */
17
+ function readVersion() {
18
+ try {
19
+ const pkgUrl = new URL("../../../package.json", import.meta.url);
20
+ const pkg = JSON.parse(readFileSync(fileURLToPath(pkgUrl), "utf8"));
21
+ return pkg.version ?? "0.0.0";
22
+ }
23
+ catch {
24
+ return "0.0.0";
25
+ }
26
+ }
27
+ export const VERSION = readVersion();
28
+ /** Default dependencies: real client + real stdout/stderr. */
29
+ export const defaultDeps = {
30
+ io: defaultIO,
31
+ createClient: (options) => new MudabClient(options),
32
+ };
33
+ export function buildProgram(deps = defaultDeps) {
34
+ const program = new Command();
35
+ program
36
+ .name("mudab")
37
+ .description("CLI for the MUDAB (Meeresumweltdatenbank) API — marine-monitoring data from " +
38
+ "the German coastal Bundesländer and research institutions, hosted by the " +
39
+ "Bundesanstalt für Gewässerkunde (geoportal.bafg.de). No API key needed. " +
40
+ "Each command lists one dataset, paged with --from/--count (default 100 rows; " +
41
+ "--all fetches everything). NOTE: the API ignores server-side filtering/sorting, " +
42
+ "so filter with jq on the --compact output.")
43
+ .version(VERSION)
44
+ .option("--base-url <url>", "API base URL", parseNonEmpty, "https://geoportal.bafg.de/mudab/rest/BaseController/FilterElements")
45
+ .option("--timeout <ms>", "per-request timeout in ms (0 = no timeout)", parseIntArg)
46
+ .option("--user-agent <ua>", "User-Agent header value", parseHeaderValue)
47
+ .option("--max-retries <n>", "retries for transient 429/503 responses (0..10)", parseBoundedInt(0, 10))
48
+ .option("--max-response-bytes <n>", "cap response body size in bytes (0 = unlimited; default 100 MiB)", parseIntArg)
49
+ .option("--compact", "print JSON on a single line instead of pretty-printed")
50
+ .showHelpAfterError();
51
+ registerCommands(program, deps);
52
+ return program;
53
+ }
54
+ //# sourceMappingURL=program.js.map
@@ -0,0 +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,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5F,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD;;;;;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,8DAA8D;AAC9D,MAAM,CAAC,MAAM,WAAW,GAAY;IAClC,EAAE,EAAE,SAAS;IACb,YAAY,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,WAAW,CAAC,OAAO,CAAC;CACpD,CAAC;AAEF,MAAM,UAAU,YAAY,CAAC,OAAgB,WAAW;IACtD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,OAAO,CAAC;SACb,WAAW,CACV,8EAA8E;QAC5E,2EAA2E;QAC3E,0EAA0E;QAC1E,+EAA+E;QAC/E,kFAAkF;QAClF,4CAA4C,CAC/C;SACA,OAAO,CAAC,OAAO,CAAC;SAChB,MAAM,CACL,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,oEAAoE,CACrE;SACA,MAAM,CAAC,gBAAgB,EAAE,4CAA4C,EAAE,WAAW,CAAC;SACnF,MAAM,CAAC,mBAAmB,EAAE,yBAAyB,EAAE,gBAAgB,CAAC;SACxE,MAAM,CAAC,mBAAmB,EAAE,iDAAiD,EAAE,eAAe,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;SACtG,MAAM,CACL,0BAA0B,EAC1B,kEAAkE,EAClE,WAAW,CACZ;SACA,MAAM,CAAC,WAAW,EAAE,uDAAuD,CAAC;SAC5E,kBAAkB,EAAE,CAAC;IAExB,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAEhC,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { CliDeps } from "./io.js";
2
+ export declare function run(argv: string[], deps?: CliDeps): Promise<number>;
3
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +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;AAqCvC,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,GAAE,OAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,CAoDtF"}
@@ -0,0 +1,87 @@
1
+ // Run the CLI and resolve to a process exit code. Kept separate from the bin
2
+ // shim so tests can call run() directly with injected deps and assert on the
3
+ // captured output and exit code without spawning a subprocess.
4
+ import { CommanderError } from "commander";
5
+ import { buildProgram, defaultDeps } from "./program.js";
6
+ import { MudabApiError, MudabError, MudabNetworkError, MudabValidationError, } from "../client/errors.js";
7
+ /**
8
+ * Process exit codes. Distinct codes let scripts tell apart a usage error, a
9
+ * missing resource, a transport failure, and a catch-all.
10
+ */
11
+ const EXIT = {
12
+ /** Usage / parse / client-side validation error. */
13
+ USAGE: 2,
14
+ /** 404 — resource not found. */
15
+ NOT_FOUND: 4,
16
+ /** Network / transport failure (DNS, connection, timeout, size-cap). */
17
+ NETWORK: 6,
18
+ /** Any other error. */
19
+ OTHER: 1,
20
+ };
21
+ /**
22
+ * Apply exitOverride + output redirection to every command in the tree.
23
+ * commander does not propagate these to subcommands, so a parse error on a
24
+ * subcommand would otherwise call process.exit() and bypass our error handling.
25
+ */
26
+ function configureTree(command, deps) {
27
+ command.exitOverride();
28
+ command.configureOutput({
29
+ writeOut: (str) => deps.io.out(str.replace(/\n$/, "")),
30
+ writeErr: (str) => deps.io.err(str.replace(/\n$/, "")),
31
+ });
32
+ for (const child of command.commands)
33
+ configureTree(child, deps);
34
+ }
35
+ export async function run(argv, deps = defaultDeps) {
36
+ const program = buildProgram(deps);
37
+ configureTree(program, deps);
38
+ // A bare invocation (no command) is a help request, not an error: print help
39
+ // to stdout and exit 0, matching `--help`.
40
+ if (argv.length === 0) {
41
+ deps.io.out(program.helpInformation().replace(/\n$/, ""));
42
+ return 0;
43
+ }
44
+ try {
45
+ await program.parseAsync(argv, { from: "user" });
46
+ return 0;
47
+ }
48
+ catch (err) {
49
+ if (err instanceof CommanderError) {
50
+ // Help/version requests exit 0; every genuine usage/parse error maps to a
51
+ // single USAGE code (commander's own exitCode is 1, indistinguishable from
52
+ // the catch-all).
53
+ return err.exitCode === 0 ? 0 : EXIT.USAGE;
54
+ }
55
+ // Client-side validation (e.g. --all combined with --from/--count) — a usage error.
56
+ if (err instanceof MudabValidationError) {
57
+ deps.io.err(`Error: ${err.message}`);
58
+ return EXIT.USAGE;
59
+ }
60
+ if (err instanceof MudabApiError) {
61
+ deps.io.err(`Error: ${err.message}`);
62
+ if (err.status === 404)
63
+ return EXIT.NOT_FOUND;
64
+ // A 3xx means the base URL redirected (e.g. the legacy MUDABAnwendung path).
65
+ // The canonical host answers directly, so this is a base-URL misconfiguration
66
+ // — a usage error, not a generic API failure.
67
+ if (err.status >= 300 && err.status < 400)
68
+ return EXIT.USAGE;
69
+ return EXIT.OTHER;
70
+ }
71
+ if (err instanceof MudabNetworkError) {
72
+ deps.io.err(`Error: ${err.message}`);
73
+ if (/maxResponseBytes/.test(err.message)) {
74
+ deps.io.err("Hint: the response exceeded the size cap. Narrow with --from/--count " +
75
+ "or raise --max-response-bytes <n> (0 = unlimited).");
76
+ }
77
+ return EXIT.NETWORK;
78
+ }
79
+ if (err instanceof MudabError) {
80
+ deps.io.err(`Error: ${err.message}`);
81
+ return EXIT.OTHER;
82
+ }
83
+ deps.io.err(`Unexpected error: ${err instanceof Error ? err.message : String(err)}`);
84
+ return EXIT.OTHER;
85
+ }
86
+ }
87
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/cli/run.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ import type { Command } from "commander";
2
+ import type { CliDeps } from "./io.js";
3
+ import type { MudabClientOptions } from "../client/client.js";
4
+ import type { FilterRequest } from "../client/types.js";
5
+ /** Default `range.count` — MUDAB returns the WHOLE table when no range is sent. */
6
+ export declare const DEFAULT_COUNT = 100;
7
+ /**
8
+ * commander value-parser: a plain base-10 non-negative integer.
9
+ *
10
+ * Uses a strict regex rather than `Number()` coercion, which would otherwise
11
+ * accept empty/whitespace strings (`Number("") === 0`), hex/binary/scientific
12
+ * literals (`0x10`, `0b10`, `1e3`), signs, padding and decimals.
13
+ */
14
+ export declare function parseIntArg(value: string): number;
15
+ /** commander value-parser: a non-empty (after trimming) string. */
16
+ export declare function parseNonEmpty(value: string): string;
17
+ /** Build a commander value-parser for an integer constrained to [min, max]. */
18
+ export declare function parseBoundedInt(min: number, max: number): (value: string) => number;
19
+ /**
20
+ * commander value-parser for a value that ends up in an HTTP header (User-Agent).
21
+ * Rejects control characters — a CR/LF (or other C0/DEL byte) would otherwise
22
+ * reach Node's HTTP layer and throw an opaque `ERR_INVALID_CHAR`, which escapes
23
+ * typed-error handling and surfaces as an ugly "Unexpected error". Tab (0x09) is
24
+ * allowed; checked by char code so the source stays free of control bytes.
25
+ */
26
+ export declare function parseHeaderValue(value: string): string;
27
+ export interface GlobalOptions {
28
+ baseUrl?: string;
29
+ timeout?: number;
30
+ userAgent?: string;
31
+ maxRetries?: number;
32
+ maxResponseBytes?: number;
33
+ compact?: boolean;
34
+ }
35
+ /**
36
+ * Options shared by every list command. Only paging (range) is offered: the API's
37
+ * OpenAPI spec advertises `filter` and `orderby` on every endpoint, but the live
38
+ * server silently ignores both (verified — a filter that should match nothing
39
+ * still returns the full page), so exposing them would be a filter that doesn't
40
+ * filter. Filter/sort client-side instead (e.g. pipe `--compact` output to `jq`).
41
+ */
42
+ export interface ListOptions {
43
+ from?: number;
44
+ count?: number;
45
+ all?: boolean;
46
+ }
47
+ /** Translate resolved global CLI options into client options. */
48
+ export declare function toEngineOptions(global: GlobalOptions): MudabClientOptions;
49
+ /**
50
+ * Add the shared list options (range / filter / orderby) to a command.
51
+ * Kept in one place so every list command exposes an identical surface.
52
+ */
53
+ export declare function addListOptions(cmd: Command): Command;
54
+ /**
55
+ * Build the request body from parsed list options — a `range` (paging) only.
56
+ *
57
+ * `range` is omitted entirely with `--all`; otherwise `count` defaults to
58
+ * {@link DEFAULT_COUNT} so a bare command never dumps a whole table, and `from`
59
+ * defaults to 0 (the server answers a count-only range with an HTTP 500).
60
+ */
61
+ export declare function buildFilterRequest(opts: ListOptions): FilterRequest;
62
+ /** Render a JSON value to stdout, pretty by default, compact with --compact. */
63
+ export declare function renderJson(deps: CliDeps, global: GlobalOptions, value: unknown): void;
64
+ export interface ActionContext {
65
+ client: ReturnType<CliDeps["createClient"]>;
66
+ global: GlobalOptions;
67
+ /** This command's own parsed options. */
68
+ opts: Record<string, unknown>;
69
+ }
70
+ /**
71
+ * Wrap an async command action with consistent global-option resolution and
72
+ * client construction. The callback receives a context (client + resolved global
73
+ * options + this command's options) and the command's positional arguments.
74
+ *
75
+ * Commander invokes actions as (arg1, ..., argN, options, command); we slice off
76
+ * the trailing options object and command instance to recover the positionals.
77
+ */
78
+ export declare function action(deps: CliDeps, fn: (ctx: ActionContext, positionals: string[]) => Promise<void>): (...args: unknown[]) => Promise<void>;
79
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/cli/shared.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,mFAAmF;AACnF,eAAO,MAAM,aAAa,MAAM,CAAC;AAEjC;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CASjD;AAED,mEAAmE;AACnE,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKnD;AAED,+EAA+E;AAC/E,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAOnF;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQtD;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;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,iEAAiE;AACjE,wBAAgB,eAAe,CAAC,MAAM,EAAE,aAAa,GAAG,kBAAkB,CAQzE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAQpD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,WAAW,GAAG,aAAa,CAQnE;AAED,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"}