@maschinenlesbar.org/govdata-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 (61) hide show
  1. package/CONTRIBUTING.md +25 -0
  2. package/LICENSE +661 -0
  3. package/LICENSING.md +47 -0
  4. package/README.md +198 -0
  5. package/dist/src/cli/commands/catalogue.d.ts +4 -0
  6. package/dist/src/cli/commands/catalogue.d.ts.map +1 -0
  7. package/dist/src/cli/commands/catalogue.js +105 -0
  8. package/dist/src/cli/commands/catalogue.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 +6 -0
  12. package/dist/src/cli/index.js.map +1 -0
  13. package/dist/src/cli/io.d.ts +13 -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 +55 -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 +48 -0
  24. package/dist/src/cli/run.js.map +1 -0
  25. package/dist/src/cli/shared.d.ts +32 -0
  26. package/dist/src/cli/shared.d.ts.map +1 -0
  27. package/dist/src/cli/shared.js +56 -0
  28. package/dist/src/cli/shared.js.map +1 -0
  29. package/dist/src/client/client.d.ts +29 -0
  30. package/dist/src/client/client.d.ts.map +1 -0
  31. package/dist/src/client/client.js +98 -0
  32. package/dist/src/client/client.js.map +1 -0
  33. package/dist/src/client/engine.d.ts +54 -0
  34. package/dist/src/client/engine.d.ts.map +1 -0
  35. package/dist/src/client/engine.js +132 -0
  36. package/dist/src/client/engine.js.map +1 -0
  37. package/dist/src/client/errors.d.ts +33 -0
  38. package/dist/src/client/errors.d.ts.map +1 -0
  39. package/dist/src/client/errors.js +40 -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 +82 -0
  44. package/dist/src/client/http.js.map +1 -0
  45. package/dist/src/client/index.d.ts +10 -0
  46. package/dist/src/client/index.d.ts.map +1 -0
  47. package/dist/src/client/index.js +8 -0
  48. package/dist/src/client/index.js.map +1 -0
  49. package/dist/src/client/query.d.ts +9 -0
  50. package/dist/src/client/query.d.ts.map +1 -0
  51. package/dist/src/client/query.js +33 -0
  52. package/dist/src/client/query.js.map +1 -0
  53. package/dist/src/client/types.d.ts +49 -0
  54. package/dist/src/client/types.d.ts.map +1 -0
  55. package/dist/src/client/types.js +7 -0
  56. package/dist/src/client/types.js.map +1 -0
  57. package/dist/src/index.d.ts +2 -0
  58. package/dist/src/index.d.ts.map +1 -0
  59. package/dist/src/index.js +3 -0
  60. package/dist/src/index.js.map +1 -0
  61. package/package.json +66 -0
package/LICENSING.md ADDED
@@ -0,0 +1,47 @@
1
+ # Licensing
2
+
3
+ `govdata-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 `govdata-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,198 @@
1
+ # govdata-cli
2
+
3
+ A TypeScript **API client** and **command-line interface** for the open
4
+ [GovData](https://www.govdata.de/) **CKAN Action API** (`ckan.govdata.de`) — the
5
+ central German open-data catalogue (Bund/Länder/Kommunen): search datasets,
6
+ inspect packages, organizations, groups, tags and resources.
7
+
8
+ - **Zero runtime HTTP dependencies** — built on Node's built-in `http`/`https` (no axios, no fetch polyfill).
9
+ - **One small dependency** for the CLI: [`commander`](https://github.com/tj/commander.js).
10
+ - **Strongly typed** — typed CKAN envelope, search result and parameter objects, plus a generic `action` escape hatch.
11
+ - **Well tested** — unit tests on Node's built-in test runner (`node --test`), every HTTP response mocked.
12
+ - **Read-only, no auth** — only CKAN read actions are wrapped; no key required.
13
+
14
+ ## Requirements
15
+
16
+ - Node.js **>= 20** (uses the stable built-in test runner, ESM and top-level `await`).
17
+
18
+ ## Install
19
+
20
+ ```bash
21
+ npm install
22
+ npm run build # compiles TypeScript to dist/
23
+ ```
24
+
25
+ Run the CLI without a global install:
26
+
27
+ ```bash
28
+ node dist/src/cli/index.js --help
29
+ # or, after `npm link` / global install:
30
+ govdata --help
31
+ ```
32
+
33
+ ---
34
+
35
+ ## CLI usage
36
+
37
+ CKAN wraps every response in `{ help, success, result }`; this CLI prints the
38
+ unwrapped **`result`** (and exits non-zero if `success` is false). `--compact` for
39
+ a single line.
40
+
41
+ ### Global options
42
+
43
+ | Option | Description |
44
+ | --- | --- |
45
+ | `--base-url <url>` | API base URL (default `https://ckan.govdata.de`) |
46
+ | `--timeout <ms>` | Per-request timeout (default `30000`) |
47
+ | `--user-agent <ua>` | `User-Agent` header value |
48
+ | `--max-retries <n>` | Retries for transient `429`/`503` responses (default `2`) |
49
+ | `--max-response-bytes <n>` | Cap response body size in bytes (`0` = unlimited; default 100 MiB) |
50
+ | `--compact` | Print JSON on a single line |
51
+
52
+ Global options may be given before or after the command, e.g.
53
+ `govdata --compact search Haushalt` or `govdata search Haushalt --compact`.
54
+
55
+ ### Commands
56
+
57
+ ```text
58
+ search [query] [--rows <n>] [--start <n>] [--sort <expr>] [--fq <filter> ...]
59
+ package <id> show one dataset
60
+ packages [--limit] [--offset] list dataset names
61
+ organizations [--all-fields] list organizations (publishers)
62
+ organization <id> show one organization
63
+ groups [--all-fields] list groups (themes)
64
+ group <id> show one group
65
+ tags [--query <substring>] list tags
66
+ resource <id> show one resource (distribution)
67
+ action <name> [--param key=value ...] call any CKAN action (generic)
68
+ ```
69
+
70
+ ### Examples
71
+
72
+ ```bash
73
+ # Search datasets, newest first
74
+ govdata search Haushalt --rows 5 --sort "metadata_modified desc"
75
+
76
+ # Filter by organization and format
77
+ govdata search --fq organization:destatis --fq res_format:CSV
78
+
79
+ # A specific dataset
80
+ govdata package <dataset-id-or-name>
81
+
82
+ # Publishers
83
+ govdata organizations
84
+
85
+ # Any action not wrapped above
86
+ govdata action package_search --param q=Klima --param rows=3
87
+ ```
88
+
89
+ Exit codes: `0` success (and for `--help`/`--version`), `4` on a `404` from the API, `1` for any other error (incl. a CKAN `success:false`) and for commander usage/parse errors.
90
+
91
+ ---
92
+
93
+ ## Library usage
94
+
95
+ ```ts
96
+ import { GovDataClient, GovDataError } from "@maschinenlesbar.org/govdata-cli";
97
+
98
+ const client = new GovDataClient(); // defaults to https://ckan.govdata.de
99
+
100
+ const hits = await client.packageSearch({ q: "Haushalt", rows: 5 });
101
+ const dataset = await client.packageShow(hits.results[0]!.id as string);
102
+ const orgs = await client.organizationList();
103
+
104
+ // Generic escape hatch for any read action:
105
+ const tags = await client.action<string[]>("tag_list", { query: "energie" });
106
+
107
+ try {
108
+ await client.packageShow("does-not-exist");
109
+ } catch (err) {
110
+ if (err instanceof GovDataError) console.error(err.message);
111
+ }
112
+ ```
113
+
114
+ ### Client options
115
+
116
+ ```ts
117
+ new GovDataClient({
118
+ baseUrl: "https://ckan.govdata.de",
119
+ timeoutMs: 15_000,
120
+ maxRetries: 3, // 429 / 503 are retried with linear backoff
121
+ maxResponseBytes: 50 << 20, // abort responses larger than 50 MiB (0 = unlimited)
122
+ userAgent: "my-app/1.0",
123
+ transport: customTransport, // inject your own HTTP transport
124
+ });
125
+ ```
126
+
127
+ ### Methods
128
+
129
+ `packageSearch`, `packageShow`, `packageList`, `organizationList`, `organizationShow`,
130
+ `groupList`, `groupShow`, `tagList`, `resourceShow`, and the generic `action(name, params)`.
131
+
132
+ ---
133
+
134
+ ## Architecture
135
+
136
+ ```
137
+ src/
138
+ client/
139
+ types.ts # CkanEnvelope, PackageSearchResult + parameter objects
140
+ query.ts # dependency-free query-string builder
141
+ http.ts # the Transport interface + default node:http/https transport
142
+ engine.ts # URL building, retry/backoff, redirects, JSON decoding, error mapping
143
+ errors.ts # GovDataError / GovDataApiError / GovDataNetworkError / GovDataParseError
144
+ client.ts # GovDataClient — CKAN actions over the engine (with result-unwrapping)
145
+ cli/
146
+ io.ts # injectable I/O seam (stdout/stderr)
147
+ shared.ts # option parsers, global-option resolver, JSON renderer
148
+ commands/ # search / package / organizations / groups / tags / resource / action
149
+ program.ts # assembles the commander program from injectable deps
150
+ run.ts # parses argv -> exit code (no process.exit; testable)
151
+ index.ts # #! bin shim
152
+ ```
153
+
154
+ **Design notes**
155
+
156
+ - The HTTP layer is a single `Transport` function (`(req) => Promise<HttpResponse>`). The default
157
+ uses `node:http`/`node:https`; tests inject a mock. This keeps the client free of any HTTP framework.
158
+ - The client unwraps CKAN's `{ help, success, result }` envelope and raises `GovDataError`
159
+ when `success` is false, so callers work directly with `result`.
160
+ - A generic `action(name, params)` exposes every read action even where there is no typed convenience method. The action name is validated against `^[a-z0-9_]+$` and URL-encoded, so it cannot inject extra path segments, query string, or fragments into the request URL.
161
+ - Redirects are followed up to `maxRedirects`; if a redirect crosses origin, the request headers are dropped so nothing (e.g. a future auth/cookie header) leaks to another host.
162
+
163
+ ---
164
+
165
+ ## Testing
166
+
167
+ ```bash
168
+ npm test # builds, then runs `node --test` over dist/test
169
+ ```
170
+
171
+ - **`query.test.ts`** — query-string serialisation.
172
+ - **`http.test.ts`** — the default transport against a real loopback `http.createServer`.
173
+ - **`engine.test.ts`** — URL building, JSON decoding, error mapping, 429/503 retry, redirects — mocked transport.
174
+ - **`client.test.ts`** — action URL/param mapping, result unwrapping, success:false handling — mocked transport.
175
+ - **`cli.test.ts`** — end-to-end command parsing, `--param`/`--fq` handling and exit codes — mocked client.
176
+
177
+ ## Continuous integration
178
+
179
+ GitHub Actions workflows under `.github/workflows/`:
180
+
181
+ - **ci.yml** — type-check, build and test on Node 20/22/24 for every push and PR.
182
+ - **release.yml** — on a `v*` tag: verify the tag matches `package.json`, test, `npm pack`, and create a GitHub Release with the tarball.
183
+ - **publish.yml** — manual dispatch: publish to npm via OIDC **Trusted Publishing** (no stored `NPM_TOKEN`) with provenance.
184
+ - **docs.yml** — build TypeDoc API docs and deploy to GitHub Pages on each `v*` tag.
185
+
186
+ ## License
187
+
188
+ **Dual-licensed** — use it under **either**:
189
+
190
+ - **[AGPL-3.0-or-later](LICENSE)** (default, free). Note the AGPL's §13 network
191
+ clause: if you run a modified version as a network service, you must offer that
192
+ modified source to the service's users.
193
+ - **Commercial license** (paid), for closed-source / proprietary or SaaS use
194
+ without the AGPL's obligations.
195
+
196
+ See **[LICENSING.md](LICENSING.md)** for details, and **[CONTRIBUTING.md](CONTRIBUTING.md)**
197
+ for the contribution policy (this project does not accept external code
198
+ contributions). Commercial enquiries: **sebs@2xs.org**.
@@ -0,0 +1,4 @@
1
+ import type { Command } from "commander";
2
+ import type { CliDeps } from "../io.js";
3
+ export declare function registerCatalogueCommands(program: Command, deps: CliDeps): void;
4
+ //# sourceMappingURL=catalogue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalogue.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/catalogue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AA6BxC,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CA8H/E"}
@@ -0,0 +1,105 @@
1
+ import { InvalidArgumentError } from "commander";
2
+ import { action, parseIntArg, renderJson } from "../shared.js";
3
+ /** commander accumulator for a repeatable string option. */
4
+ function collect(value, previous = []) {
5
+ return previous.concat([value]);
6
+ }
7
+ /** commander accumulator for repeatable `key=value` pairs into a record. */
8
+ function collectKeyValue(value, previous = {}) {
9
+ const eq = value.indexOf("=");
10
+ // Throw commander's InvalidArgumentError (not a GovDataError) so the failure
11
+ // is formatted as a usage error with `error:` prefix and help, consistent
12
+ // with other parse-time flag validation (e.g. `--rows abc`).
13
+ if (eq <= 0) {
14
+ throw new InvalidArgumentError(`Invalid --param "${value}". Expected key=value.`);
15
+ }
16
+ const key = value.slice(0, eq);
17
+ // Reject a duplicated key rather than silently overwriting the earlier value.
18
+ if (Object.prototype.hasOwnProperty.call(previous, key)) {
19
+ throw new InvalidArgumentError(`Duplicate --param key "${key}".`);
20
+ }
21
+ return { ...previous, [key]: value.slice(eq + 1) };
22
+ }
23
+ export function registerCatalogueCommands(program, deps) {
24
+ program
25
+ .command("search [query]")
26
+ .description("Search datasets (Solr query syntax)")
27
+ .option("--rows <n>", "max results", parseIntArg)
28
+ .option("--start <n>", "offset for paging", parseIntArg)
29
+ .option("--sort <expr>", 'e.g. "metadata_modified desc"')
30
+ .option("--fq <filter>", "filter query, e.g. organization:destatis (repeatable)", collect)
31
+ .action(action(deps, async ({ client, global, opts }, [query]) => {
32
+ renderJson(deps, global, await client.packageSearch({
33
+ q: query,
34
+ rows: opts["rows"],
35
+ start: opts["start"],
36
+ sort: opts["sort"],
37
+ fq: opts["fq"],
38
+ }));
39
+ }));
40
+ program
41
+ .command("package <id>")
42
+ .description("Show one dataset by id or name")
43
+ .action(action(deps, async ({ client, global }, [id]) => {
44
+ renderJson(deps, global, await client.packageShow(id));
45
+ }));
46
+ program
47
+ .command("packages")
48
+ .description("List dataset names")
49
+ .option("--limit <n>", "max names", parseIntArg)
50
+ .option("--offset <n>", "offset for paging", parseIntArg)
51
+ .action(action(deps, async ({ client, global, opts }) => {
52
+ renderJson(deps, global, await client.packageList({
53
+ limit: opts["limit"],
54
+ offset: opts["offset"],
55
+ }));
56
+ }));
57
+ program
58
+ .command("organizations")
59
+ .description("List organizations (data publishers)")
60
+ .option("--all-fields", "return full objects instead of names")
61
+ .action(action(deps, async ({ client, global, opts }) => {
62
+ renderJson(deps, global, await client.organizationList({ all_fields: opts["allFields"] }));
63
+ }));
64
+ program
65
+ .command("organization <id>")
66
+ .description("Show one organization")
67
+ .action(action(deps, async ({ client, global }, [id]) => {
68
+ renderJson(deps, global, await client.organizationShow(id));
69
+ }));
70
+ program
71
+ .command("groups")
72
+ .description("List groups (themes/categories)")
73
+ .option("--all-fields", "return full objects instead of names")
74
+ .action(action(deps, async ({ client, global, opts }) => {
75
+ renderJson(deps, global, await client.groupList({ all_fields: opts["allFields"] }));
76
+ }));
77
+ program
78
+ .command("group <id>")
79
+ .description("Show one group")
80
+ .action(action(deps, async ({ client, global }, [id]) => {
81
+ renderJson(deps, global, await client.groupShow(id));
82
+ }));
83
+ program
84
+ .command("tags")
85
+ .description("List tags")
86
+ .option("--query <substring>", "filter tags by substring")
87
+ .action(action(deps, async ({ client, global, opts }) => {
88
+ renderJson(deps, global, await client.tagList(opts["query"]));
89
+ }));
90
+ program
91
+ .command("resource <id>")
92
+ .description("Show one resource (distribution) by id")
93
+ .action(action(deps, async ({ client, global }, [id]) => {
94
+ renderJson(deps, global, await client.resourceShow(id));
95
+ }));
96
+ program
97
+ .command("action <name>")
98
+ .description("Call any CKAN action by name (generic escape hatch)")
99
+ .option("--param <key=value>", "query parameter (repeatable)", collectKeyValue)
100
+ .action(action(deps, async ({ client, global, opts }, [name]) => {
101
+ const params = opts["param"] ?? {};
102
+ renderJson(deps, global, await client.action(name, params));
103
+ }));
104
+ }
105
+ //# sourceMappingURL=catalogue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalogue.js","sourceRoot":"","sources":["../../../../src/cli/commands/catalogue.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,6 @@
1
+ #!/usr/bin/env node
2
+ // Binary entry point. Thin shim around run(); all logic lives in run.ts/program.ts.
3
+ import { run } from "./run.js";
4
+ const exitCode = await run(process.argv.slice(2));
5
+ process.exitCode = exitCode;
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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"}
@@ -0,0 +1,13 @@
1
+ import type { GovDataClient } from "../client/client.js";
2
+ import type { EngineOptions } from "../client/engine.js";
3
+ export interface CliIO {
4
+ out(text: string): void;
5
+ err(text: string): void;
6
+ }
7
+ export interface CliDeps {
8
+ io: CliIO;
9
+ /** Build a client from the resolved global options (injectable for tests). */
10
+ createClient(options: EngineOptions): GovDataClient;
11
+ }
12
+ export declare const defaultIO: CliIO;
13
+ //# 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,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,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,aAAa,CAAC;CACrD;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;AAgBtE,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/filesystem. */
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,yEAAyE;AACzE,eAAO,MAAM,WAAW,EAAE,OAGzB,CAAC;AAEF,wBAAgB,YAAY,CAAC,IAAI,GAAE,OAAqB,GAAG,OAAO,CA+BjE"}
@@ -0,0 +1,55 @@
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 { GovDataClient } from "../client/client.js";
9
+ import { parseIntArg } from "./shared.js";
10
+ import { registerCatalogueCommands } from "./commands/catalogue.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/filesystem. */
29
+ export const defaultDeps = {
30
+ io: defaultIO,
31
+ createClient: (options) => new GovDataClient(options),
32
+ };
33
+ export function buildProgram(deps = defaultDeps) {
34
+ const program = new Command();
35
+ program
36
+ .name("govdata")
37
+ .description("CLI for the open GovData CKAN catalogue API (https://ckan.govdata.de/api/3/action)")
38
+ .version(VERSION)
39
+ .option("--base-url <url>", "API base URL", "https://ckan.govdata.de")
40
+ .option("--timeout <ms>", "per-request timeout in milliseconds", parseIntArg)
41
+ .option("--user-agent <ua>", "User-Agent header value")
42
+ .option("--max-retries <n>", "retries for transient 429/503 responses", parseIntArg)
43
+ .option("--max-response-bytes <n>", "cap response body size in bytes (0 = unlimited; default 100 MiB)", parseIntArg)
44
+ .option("--compact", "print JSON on a single line instead of pretty-printed")
45
+ .showHelpAfterError();
46
+ // A bare invocation (no subcommand) should print help to stdout and exit 0,
47
+ // matching `govdata help` / `govdata --help`. Without an explicit root action,
48
+ // commander writes help to stderr and exits 1 for the empty-command case.
49
+ program.action(() => {
50
+ program.help();
51
+ });
52
+ registerCatalogueCommands(program, deps);
53
+ return program;
54
+ }
55
+ //# 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,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AAEpE;;;;;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,aAAa,CAAC,OAAO,CAAC;CACtD,CAAC;AAEF,MAAM,UAAU,YAAY,CAAC,OAAgB,WAAW;IACtD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,SAAS,CAAC;SACf,WAAW,CACV,oFAAoF,CACrF;SACA,OAAO,CAAC,OAAO,CAAC;SAChB,MAAM,CAAC,kBAAkB,EAAE,cAAc,EAAE,yBAAyB,CAAC;SACrE,MAAM,CAAC,gBAAgB,EAAE,qCAAqC,EAAE,WAAW,CAAC;SAC5E,MAAM,CAAC,mBAAmB,EAAE,yBAAyB,CAAC;SACtD,MAAM,CAAC,mBAAmB,EAAE,yCAAyC,EAAE,WAAW,CAAC;SACnF,MAAM,CACL,0BAA0B,EAC1B,kEAAkE,EAClE,WAAW,CACZ;SACA,MAAM,CAAC,WAAW,EAAE,uDAAuD,CAAC;SAC5E,kBAAkB,EAAE,CAAC;IAExB,4EAA4E;IAC5E,+EAA+E;IAC/E,0EAA0E;IAC1E,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE;QAClB,OAAO,CAAC,IAAI,EAAE,CAAC;IACjB,CAAC,CAAC,CAAC;IAEH,yBAAyB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAEzC,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;AAiBvC,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,GAAE,OAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,CAyBtF"}
@@ -0,0 +1,48 @@
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 { GovDataApiError, GovDataError } from "../client/errors.js";
7
+ /**
8
+ * Apply exitOverride + output redirection to every command in the tree.
9
+ * commander does not propagate these to subcommands, so a parse error on a
10
+ * subcommand would otherwise call process.exit() and bypass our error handling.
11
+ */
12
+ function configureTree(command, deps) {
13
+ command.exitOverride();
14
+ command.configureOutput({
15
+ writeOut: (str) => deps.io.out(str.replace(/\n$/, "")),
16
+ writeErr: (str) => deps.io.err(str.replace(/\n$/, "")),
17
+ });
18
+ for (const child of command.commands)
19
+ configureTree(child, deps);
20
+ }
21
+ export async function run(argv, deps = defaultDeps) {
22
+ const program = buildProgram(deps);
23
+ configureTree(program, deps);
24
+ try {
25
+ await program.parseAsync(argv, { from: "user" });
26
+ return 0;
27
+ }
28
+ catch (err) {
29
+ if (err instanceof CommanderError) {
30
+ // Help/version requests exit 0; genuine parse errors carry their own code.
31
+ return err.exitCode;
32
+ }
33
+ if (err instanceof GovDataApiError) {
34
+ deps.io.err(`Error: ${err.message}`);
35
+ // Map a few notable statuses to distinct exit codes for scripting.
36
+ if (err.status === 404)
37
+ return 4;
38
+ return 1;
39
+ }
40
+ if (err instanceof GovDataError) {
41
+ deps.io.err(`Error: ${err.message}`);
42
+ return 1;
43
+ }
44
+ deps.io.err(`Unexpected error: ${err instanceof Error ? err.message : String(err)}`);
45
+ return 1;
46
+ }
47
+ }
48
+ //# sourceMappingURL=run.js.map
@@ -0,0 +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,eAAe,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEpE;;;;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,2EAA2E;YAC3E,OAAO,GAAG,CAAC,QAAQ,CAAC;QACtB,CAAC;QACD,IAAI,GAAG,YAAY,eAAe,EAAE,CAAC;YACnC,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,YAAY,EAAE,CAAC;YAChC,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"}
@@ -0,0 +1,32 @@
1
+ import type { CliDeps } from "./io.js";
2
+ import type { EngineOptions } from "../client/engine.js";
3
+ /** commander value-parser: a non-negative integer. */
4
+ export declare function parseIntArg(value: string): number;
5
+ export interface GlobalOptions {
6
+ baseUrl?: string;
7
+ timeout?: number;
8
+ userAgent?: string;
9
+ maxRetries?: number;
10
+ maxResponseBytes?: number;
11
+ compact?: boolean;
12
+ }
13
+ /** Translate resolved global CLI options into client EngineOptions. */
14
+ export declare function toEngineOptions(global: GlobalOptions): EngineOptions;
15
+ /** Render a JSON value to stdout, pretty by default, compact with --compact. */
16
+ export declare function renderJson(deps: CliDeps, global: GlobalOptions, value: unknown): void;
17
+ export interface ActionContext {
18
+ client: ReturnType<CliDeps["createClient"]>;
19
+ global: GlobalOptions;
20
+ /** This command's own parsed options. */
21
+ opts: Record<string, unknown>;
22
+ }
23
+ /**
24
+ * Wrap an async command action with consistent global-option resolution and
25
+ * client construction. The callback receives a context (client + resolved global
26
+ * options + this command's options) and the command's positional arguments.
27
+ *
28
+ * Commander invokes actions as (arg1, ..., argN, options, command); we slice off
29
+ * the trailing options object and command instance to recover the positionals.
30
+ */
31
+ export declare function action(deps: CliDeps, fn: (ctx: ActionContext, positionals: string[]) => Promise<void>): (...args: unknown[]) => Promise<void>;
32
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +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;AAEzD,sDAAsD;AACtD,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAajD;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,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"}