@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.
- package/CONTRIBUTING.md +25 -0
- package/LICENSE +661 -0
- package/LICENSING.md +47 -0
- package/README.md +198 -0
- package/dist/src/cli/commands/catalogue.d.ts +4 -0
- package/dist/src/cli/commands/catalogue.d.ts.map +1 -0
- package/dist/src/cli/commands/catalogue.js +105 -0
- package/dist/src/cli/commands/catalogue.js.map +1 -0
- package/dist/src/cli/index.d.ts +3 -0
- package/dist/src/cli/index.d.ts.map +1 -0
- package/dist/src/cli/index.js +6 -0
- package/dist/src/cli/index.js.map +1 -0
- package/dist/src/cli/io.d.ts +13 -0
- package/dist/src/cli/io.d.ts.map +1 -0
- package/dist/src/cli/io.js +7 -0
- package/dist/src/cli/io.js.map +1 -0
- package/dist/src/cli/program.d.ts +7 -0
- package/dist/src/cli/program.d.ts.map +1 -0
- package/dist/src/cli/program.js +55 -0
- package/dist/src/cli/program.js.map +1 -0
- package/dist/src/cli/run.d.ts +3 -0
- package/dist/src/cli/run.d.ts.map +1 -0
- package/dist/src/cli/run.js +48 -0
- package/dist/src/cli/run.js.map +1 -0
- package/dist/src/cli/shared.d.ts +32 -0
- package/dist/src/cli/shared.d.ts.map +1 -0
- package/dist/src/cli/shared.js +56 -0
- package/dist/src/cli/shared.js.map +1 -0
- package/dist/src/client/client.d.ts +29 -0
- package/dist/src/client/client.d.ts.map +1 -0
- package/dist/src/client/client.js +98 -0
- package/dist/src/client/client.js.map +1 -0
- package/dist/src/client/engine.d.ts +54 -0
- package/dist/src/client/engine.d.ts.map +1 -0
- package/dist/src/client/engine.js +132 -0
- package/dist/src/client/engine.js.map +1 -0
- package/dist/src/client/errors.d.ts +33 -0
- package/dist/src/client/errors.d.ts.map +1 -0
- package/dist/src/client/errors.js +40 -0
- package/dist/src/client/errors.js.map +1 -0
- package/dist/src/client/http.d.ts +26 -0
- package/dist/src/client/http.d.ts.map +1 -0
- package/dist/src/client/http.js +82 -0
- package/dist/src/client/http.js.map +1 -0
- package/dist/src/client/index.d.ts +10 -0
- package/dist/src/client/index.d.ts.map +1 -0
- package/dist/src/client/index.js +8 -0
- package/dist/src/client/index.js.map +1 -0
- package/dist/src/client/query.d.ts +9 -0
- package/dist/src/client/query.d.ts.map +1 -0
- package/dist/src/client/query.js +33 -0
- package/dist/src/client/query.js.map +1 -0
- package/dist/src/client/types.d.ts +49 -0
- package/dist/src/client/types.d.ts.map +1 -0
- package/dist/src/client/types.js +7 -0
- package/dist/src/client/types.js.map +1 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +3 -0
- package/dist/src/index.js.map +1 -0
- package/package.json +66 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Shared helpers used across CLI command groups: option parsers, the global
|
|
2
|
+
// option resolver, and the JSON result renderer.
|
|
3
|
+
import { InvalidArgumentError } from "commander";
|
|
4
|
+
/** commander value-parser: a non-negative integer. */
|
|
5
|
+
export function parseIntArg(value) {
|
|
6
|
+
// Validate the literal shape rather than trusting Number(): that rejects
|
|
7
|
+
// empty/whitespace ("" and " " coerce to 0), hex (0x10) and exponent (1e3)
|
|
8
|
+
// notation, all of which Number() would silently accept.
|
|
9
|
+
if (!/^\d+$/.test(value)) {
|
|
10
|
+
throw new InvalidArgumentError("Expected a non-negative integer.");
|
|
11
|
+
}
|
|
12
|
+
const n = Number(value);
|
|
13
|
+
// Reject magnitudes that cannot be represented exactly (silent precision loss).
|
|
14
|
+
if (!Number.isSafeInteger(n)) {
|
|
15
|
+
throw new InvalidArgumentError("Expected a non-negative integer.");
|
|
16
|
+
}
|
|
17
|
+
return n;
|
|
18
|
+
}
|
|
19
|
+
/** Translate resolved global CLI options into client EngineOptions. */
|
|
20
|
+
export function toEngineOptions(global) {
|
|
21
|
+
const options = {};
|
|
22
|
+
if (global.baseUrl !== undefined)
|
|
23
|
+
options.baseUrl = global.baseUrl;
|
|
24
|
+
if (global.timeout !== undefined)
|
|
25
|
+
options.timeoutMs = global.timeout;
|
|
26
|
+
if (global.userAgent !== undefined)
|
|
27
|
+
options.userAgent = global.userAgent;
|
|
28
|
+
if (global.maxRetries !== undefined)
|
|
29
|
+
options.maxRetries = global.maxRetries;
|
|
30
|
+
if (global.maxResponseBytes !== undefined)
|
|
31
|
+
options.maxResponseBytes = global.maxResponseBytes;
|
|
32
|
+
return options;
|
|
33
|
+
}
|
|
34
|
+
/** Render a JSON value to stdout, pretty by default, compact with --compact. */
|
|
35
|
+
export function renderJson(deps, global, value) {
|
|
36
|
+
const text = global.compact ? JSON.stringify(value) : JSON.stringify(value, null, 2);
|
|
37
|
+
deps.io.out(text);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Wrap an async command action with consistent global-option resolution and
|
|
41
|
+
* client construction. The callback receives a context (client + resolved global
|
|
42
|
+
* options + this command's options) and the command's positional arguments.
|
|
43
|
+
*
|
|
44
|
+
* Commander invokes actions as (arg1, ..., argN, options, command); we slice off
|
|
45
|
+
* the trailing options object and command instance to recover the positionals.
|
|
46
|
+
*/
|
|
47
|
+
export function action(deps, fn) {
|
|
48
|
+
return async (...args) => {
|
|
49
|
+
const command = args[args.length - 1];
|
|
50
|
+
const positionals = args.slice(0, Math.max(0, args.length - 2));
|
|
51
|
+
const global = command.optsWithGlobals();
|
|
52
|
+
const client = deps.createClient(toEngineOptions(global));
|
|
53
|
+
await fn({ client, global, opts: command.opts() }, positionals);
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=shared.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.js","sourceRoot":"","sources":["../../../src/cli/shared.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,iDAAiD;AAGjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAIjD,sDAAsD;AACtD,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,yEAAyE;IACzE,2EAA2E;IAC3E,yDAAyD;IACzD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,oBAAoB,CAAC,kCAAkC,CAAC,CAAC;IACrE,CAAC;IACD,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IACxB,gFAAgF;IAChF,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,oBAAoB,CAAC,kCAAkC,CAAC,CAAC;IACrE,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAWD,uEAAuE;AACvE,MAAM,UAAU,eAAe,CAAC,MAAqB;IACnD,MAAM,OAAO,GAAkB,EAAE,CAAC;IAClC,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS;QAAE,OAAO,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;IACnE,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS;QAAE,OAAO,CAAC,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC;IACrE,IAAI,MAAM,CAAC,SAAS,KAAK,SAAS;QAAE,OAAO,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;IACzE,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS;QAAE,OAAO,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;IAC5E,IAAI,MAAM,CAAC,gBAAgB,KAAK,SAAS;QAAE,OAAO,CAAC,gBAAgB,GAAG,MAAM,CAAC,gBAAgB,CAAC;IAC9F,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,UAAU,CAAC,IAAa,EAAE,MAAqB,EAAE,KAAc;IAC7E,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACrF,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACpB,CAAC;AASD;;;;;;;GAOG;AACH,MAAM,UAAU,MAAM,CACpB,IAAa,EACb,EAAgE;IAEhE,OAAO,KAAK,EAAE,GAAG,IAAe,EAAE,EAAE;QAClC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAY,CAAC;QACjD,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAa,CAAC;QAC5E,MAAM,MAAM,GAAG,OAAO,CAAC,eAAe,EAAmB,CAAC;QAC1D,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC;QAC1D,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,EAAE,EAAE,WAAW,CAAC,CAAC;IAClE,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type EngineOptions } from "./engine.js";
|
|
2
|
+
import type { QueryParams } from "./query.js";
|
|
3
|
+
import type { PackageSearchResult, Package, Organization, Group, Resource, PackageSearchParams, ListParams, JsonValue } from "./types.js";
|
|
4
|
+
export declare class GovDataClient {
|
|
5
|
+
private readonly engine;
|
|
6
|
+
constructor(options?: EngineOptions);
|
|
7
|
+
/**
|
|
8
|
+
* Call any CKAN action by name and return its unwrapped `result`. Throws a
|
|
9
|
+
* GovDataError if the envelope reports `success: false`.
|
|
10
|
+
*/
|
|
11
|
+
action<T = JsonValue>(name: string, params?: QueryParams): Promise<T>;
|
|
12
|
+
/** Full-text / faceted dataset search. */
|
|
13
|
+
packageSearch(params?: PackageSearchParams): Promise<PackageSearchResult>;
|
|
14
|
+
/** A single dataset by id or name. */
|
|
15
|
+
packageShow(id: string): Promise<Package>;
|
|
16
|
+
/** Dataset names (paginated). */
|
|
17
|
+
packageList(params?: ListParams): Promise<string[]>;
|
|
18
|
+
/** Organizations (names, or full objects with `all_fields`). */
|
|
19
|
+
organizationList(params?: ListParams): Promise<JsonValue[]>;
|
|
20
|
+
organizationShow(id: string): Promise<Organization>;
|
|
21
|
+
/** Groups (themes/categories). */
|
|
22
|
+
groupList(params?: ListParams): Promise<JsonValue[]>;
|
|
23
|
+
groupShow(id: string): Promise<Group>;
|
|
24
|
+
/** Tags, optionally filtered by a query substring. */
|
|
25
|
+
tagList(query?: string): Promise<string[]>;
|
|
26
|
+
/** A single resource (distribution) by id. */
|
|
27
|
+
resourceShow(id: string): Promise<Resource>;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/client/client.ts"],"names":[],"mappings":"AAQA,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,KAAK,EAEV,mBAAmB,EACnB,OAAO,EACP,YAAY,EACZ,KAAK,EACL,QAAQ,EACR,mBAAmB,EACnB,UAAU,EACV,SAAS,EACV,MAAM,YAAY,CAAC;AA0BpB,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;gBAE3B,OAAO,GAAE,aAAkB;IAIvC;;;OAGG;IACG,MAAM,CAAC,CAAC,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,GAAE,WAAgB,GAAG,OAAO,CAAC,CAAC,CAAC;IAoB/E,0CAA0C;IAC1C,aAAa,CAAC,MAAM,GAAE,mBAAwB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAc7E,sCAAsC;IACtC,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIzC,iCAAiC;IACjC,WAAW,CAAC,MAAM,GAAE,UAAe,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAOvD,gEAAgE;IAChE,gBAAgB,CAAC,MAAM,GAAE,UAAe,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAI/D,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAInD,kCAAkC;IAClC,SAAS,CAAC,MAAM,GAAE,UAAe,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAIxD,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAIrC,sDAAsD;IACtD,OAAO,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAI1C,8CAA8C;IAC9C,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;CAG5C"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// GovDataClient — a typed client over the open (no-auth) read endpoints of the
|
|
2
|
+
// GovData CKAN Action API (https://ckan.govdata.de/api/3/action), the central
|
|
3
|
+
// German open-data catalogue.
|
|
4
|
+
//
|
|
5
|
+
// client.packageSearch({ q: "Haushalt", rows: 5 })
|
|
6
|
+
// client.packageShow("some-dataset-id")
|
|
7
|
+
// client.action("organization_list") // generic escape hatch
|
|
8
|
+
import { RequestEngine } from "./engine.js";
|
|
9
|
+
import { GovDataError } from "./errors.js";
|
|
10
|
+
const ACTION = "/api/3/action";
|
|
11
|
+
/**
|
|
12
|
+
* CKAN action names are always `[a-z0-9_]+`. Restricting to that allowlist closes
|
|
13
|
+
* the path-traversal hole (`../../..` escaping `/api/3/action/`) and the
|
|
14
|
+
* query/fragment-injection hole (a `?`/`#` in the name corrupting the query) for
|
|
15
|
+
* both the library and the CLI generic-action escape hatch.
|
|
16
|
+
*/
|
|
17
|
+
const ACTION_NAME = /^[a-z0-9_]+$/;
|
|
18
|
+
/**
|
|
19
|
+
* Drop undefined (and empty-string) values so only the parameters the caller
|
|
20
|
+
* actually set are sent. An empty string filter (e.g. `tags --query ""`) is
|
|
21
|
+
* treated as "no filter" rather than forwarded as `query=`.
|
|
22
|
+
*/
|
|
23
|
+
function prune(params) {
|
|
24
|
+
const out = {};
|
|
25
|
+
for (const [k, v] of Object.entries(params)) {
|
|
26
|
+
if (v === undefined || v === "")
|
|
27
|
+
continue;
|
|
28
|
+
out[k] = v;
|
|
29
|
+
}
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
export class GovDataClient {
|
|
33
|
+
engine;
|
|
34
|
+
constructor(options = {}) {
|
|
35
|
+
this.engine = new RequestEngine(options);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Call any CKAN action by name and return its unwrapped `result`. Throws a
|
|
39
|
+
* GovDataError if the envelope reports `success: false`.
|
|
40
|
+
*/
|
|
41
|
+
async action(name, params = {}) {
|
|
42
|
+
const action = String(name).trim();
|
|
43
|
+
if (!ACTION_NAME.test(action)) {
|
|
44
|
+
throw new GovDataError(`Invalid CKAN action name: "${name}"`);
|
|
45
|
+
}
|
|
46
|
+
const env = await this.engine.getJson(`${ACTION}/${encodeURIComponent(action)}`, params);
|
|
47
|
+
if (!env.success) {
|
|
48
|
+
// Surface CKAN's human-readable error.message; fall back to the raw JSON
|
|
49
|
+
// only when no message is present (mirrors the HTTP-error detail path).
|
|
50
|
+
const e = env.error;
|
|
51
|
+
const detail = typeof e?.message === "string" ? e.message : e ? JSON.stringify(e) : "unknown error";
|
|
52
|
+
throw new GovDataError(`CKAN action "${name}" failed: ${detail}`);
|
|
53
|
+
}
|
|
54
|
+
return env.result;
|
|
55
|
+
}
|
|
56
|
+
/** Full-text / faceted dataset search. */
|
|
57
|
+
packageSearch(params = {}) {
|
|
58
|
+
return this.action("package_search", prune({
|
|
59
|
+
q: params.q,
|
|
60
|
+
fq: params.fq,
|
|
61
|
+
rows: params.rows,
|
|
62
|
+
start: params.start,
|
|
63
|
+
sort: params.sort,
|
|
64
|
+
facet_field: params.facet_field,
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
/** A single dataset by id or name. */
|
|
68
|
+
packageShow(id) {
|
|
69
|
+
return this.action("package_show", { id });
|
|
70
|
+
}
|
|
71
|
+
/** Dataset names (paginated). */
|
|
72
|
+
packageList(params = {}) {
|
|
73
|
+
return this.action("package_list", prune({ limit: params.limit, offset: params.offset }));
|
|
74
|
+
}
|
|
75
|
+
/** Organizations (names, or full objects with `all_fields`). */
|
|
76
|
+
organizationList(params = {}) {
|
|
77
|
+
return this.action("organization_list", prune({ all_fields: params.all_fields }));
|
|
78
|
+
}
|
|
79
|
+
organizationShow(id) {
|
|
80
|
+
return this.action("organization_show", { id });
|
|
81
|
+
}
|
|
82
|
+
/** Groups (themes/categories). */
|
|
83
|
+
groupList(params = {}) {
|
|
84
|
+
return this.action("group_list", prune({ all_fields: params.all_fields }));
|
|
85
|
+
}
|
|
86
|
+
groupShow(id) {
|
|
87
|
+
return this.action("group_show", { id });
|
|
88
|
+
}
|
|
89
|
+
/** Tags, optionally filtered by a query substring. */
|
|
90
|
+
tagList(query) {
|
|
91
|
+
return this.action("tag_list", prune({ query }));
|
|
92
|
+
}
|
|
93
|
+
/** A single resource (distribution) by id. */
|
|
94
|
+
resourceShow(id) {
|
|
95
|
+
return this.action("resource_show", { id });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/client/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { type Transport } from "./http.js";
|
|
2
|
+
import { type QueryParams } from "./query.js";
|
|
3
|
+
export declare const DEFAULT_BASE_URL = "https://ckan.govdata.de";
|
|
4
|
+
export interface RawResponse {
|
|
5
|
+
data: Buffer;
|
|
6
|
+
contentType: string;
|
|
7
|
+
status: number;
|
|
8
|
+
}
|
|
9
|
+
export interface EngineOptions {
|
|
10
|
+
/** Base URL of the API. Defaults to https://ckan.govdata.de */
|
|
11
|
+
baseUrl?: string;
|
|
12
|
+
/** Swappable transport. Defaults to the built-in node http/https transport. */
|
|
13
|
+
transport?: Transport;
|
|
14
|
+
/** Value of the User-Agent header. */
|
|
15
|
+
userAgent?: string;
|
|
16
|
+
/** Per-request timeout in milliseconds (0 disables). */
|
|
17
|
+
timeoutMs?: number;
|
|
18
|
+
/** Number of automatic retries for transient (429/503) responses. */
|
|
19
|
+
maxRetries?: number;
|
|
20
|
+
/** Base backoff between retries in milliseconds (grows linearly). */
|
|
21
|
+
retryDelayMs?: number;
|
|
22
|
+
/** Number of HTTP redirects (301/302/303/307/308) to follow. Defaults to 5. */
|
|
23
|
+
maxRedirects?: number;
|
|
24
|
+
/**
|
|
25
|
+
* Hard cap on response body size in bytes (defends against memory exhaustion
|
|
26
|
+
* from a hostile/buggy endpoint). Defaults to 100 MiB; set to 0 for no limit.
|
|
27
|
+
*/
|
|
28
|
+
maxResponseBytes?: number;
|
|
29
|
+
/** Injectable sleep, primarily for deterministic tests. */
|
|
30
|
+
sleep?: (ms: number) => Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
export declare class RequestEngine {
|
|
33
|
+
private readonly baseUrl;
|
|
34
|
+
private readonly transport;
|
|
35
|
+
private readonly userAgent;
|
|
36
|
+
private readonly timeoutMs;
|
|
37
|
+
private readonly maxRetries;
|
|
38
|
+
private readonly retryDelayMs;
|
|
39
|
+
private readonly maxRedirects;
|
|
40
|
+
private readonly maxResponseBytes;
|
|
41
|
+
private readonly sleep;
|
|
42
|
+
constructor(options?: EngineOptions);
|
|
43
|
+
/** Build a fully-qualified URL from a path and optional query parameters. */
|
|
44
|
+
buildUrl(path: string, query?: QueryParams): string;
|
|
45
|
+
/** Perform a request with Accept negotiation and transient-error retries. */
|
|
46
|
+
request(method: string, path: string, options?: {
|
|
47
|
+
query?: QueryParams;
|
|
48
|
+
accept: string;
|
|
49
|
+
}): Promise<RawResponse>;
|
|
50
|
+
/** Perform a GET expecting JSON and parse it into `T`. */
|
|
51
|
+
getJson<T>(path: string, query?: QueryParams): Promise<T>;
|
|
52
|
+
private toApiError;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -0,0 +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,+EAA+E;IAC/E,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;AAOD,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,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgC;gBAE1C,OAAO,GAAE,aAAkB;IAYvC,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;IAuDvB,0DAA0D;IACpD,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC;IAU/D,OAAO,CAAC,UAAU;CAiCnB"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// The request engine: turns logical (method, path, query) calls into HTTP
|
|
2
|
+
// requests via a Transport, applies retry/backoff for transient statuses
|
|
3
|
+
// (429, 503), and decodes responses.
|
|
4
|
+
import { nodeHttpTransport } from "./http.js";
|
|
5
|
+
import { buildQueryString } from "./query.js";
|
|
6
|
+
import { GovDataApiError, GovDataParseError } from "./errors.js";
|
|
7
|
+
export const DEFAULT_BASE_URL = "https://ckan.govdata.de";
|
|
8
|
+
const DEFAULT_USER_AGENT = "govdata-cli";
|
|
9
|
+
const DEFAULT_MAX_RESPONSE_BYTES = 100 * 1024 * 1024;
|
|
10
|
+
const realSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
11
|
+
export class RequestEngine {
|
|
12
|
+
baseUrl;
|
|
13
|
+
transport;
|
|
14
|
+
userAgent;
|
|
15
|
+
timeoutMs;
|
|
16
|
+
maxRetries;
|
|
17
|
+
retryDelayMs;
|
|
18
|
+
maxRedirects;
|
|
19
|
+
maxResponseBytes;
|
|
20
|
+
sleep;
|
|
21
|
+
constructor(options = {}) {
|
|
22
|
+
this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
23
|
+
this.transport = options.transport ?? nodeHttpTransport;
|
|
24
|
+
this.userAgent = options.userAgent ?? DEFAULT_USER_AGENT;
|
|
25
|
+
this.timeoutMs = options.timeoutMs ?? 30_000;
|
|
26
|
+
this.maxRetries = options.maxRetries ?? 2;
|
|
27
|
+
this.retryDelayMs = options.retryDelayMs ?? 200;
|
|
28
|
+
this.maxRedirects = options.maxRedirects ?? 5;
|
|
29
|
+
this.maxResponseBytes = options.maxResponseBytes ?? DEFAULT_MAX_RESPONSE_BYTES;
|
|
30
|
+
this.sleep = options.sleep ?? realSleep;
|
|
31
|
+
}
|
|
32
|
+
/** Build a fully-qualified URL from a path and optional query parameters. */
|
|
33
|
+
buildUrl(path, query) {
|
|
34
|
+
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
35
|
+
const qs = query ? buildQueryString(query) : "";
|
|
36
|
+
return `${this.baseUrl}${normalizedPath}${qs ? `?${qs}` : ""}`;
|
|
37
|
+
}
|
|
38
|
+
/** Perform a request with Accept negotiation and transient-error retries. */
|
|
39
|
+
async request(method, path, options = { accept: "application/json" }) {
|
|
40
|
+
let url = this.buildUrl(path, options.query);
|
|
41
|
+
let headers = {
|
|
42
|
+
Accept: options.accept,
|
|
43
|
+
"User-Agent": this.userAgent,
|
|
44
|
+
};
|
|
45
|
+
let attempt = 0;
|
|
46
|
+
let redirects = 0;
|
|
47
|
+
// attempts = initial try + maxRetries (redirects are counted separately)
|
|
48
|
+
for (;;) {
|
|
49
|
+
const response = await this.transport({
|
|
50
|
+
method,
|
|
51
|
+
url,
|
|
52
|
+
headers,
|
|
53
|
+
timeoutMs: this.timeoutMs,
|
|
54
|
+
...(this.maxResponseBytes > 0 ? { maxResponseBytes: this.maxResponseBytes } : {}),
|
|
55
|
+
});
|
|
56
|
+
const status = response.status;
|
|
57
|
+
const retryable = status === 429 || status === 503;
|
|
58
|
+
if (retryable && attempt < this.maxRetries) {
|
|
59
|
+
attempt += 1;
|
|
60
|
+
await this.sleep(this.retryDelayMs * attempt);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
// Follow redirects, resolving the Location relative to the current URL.
|
|
64
|
+
if (status >= 300 && status < 400 && redirects < this.maxRedirects) {
|
|
65
|
+
const location = response.headers["location"];
|
|
66
|
+
if (typeof location === "string" && location.length > 0) {
|
|
67
|
+
const previous = new URL(url);
|
|
68
|
+
const next = new URL(location, url);
|
|
69
|
+
// Credential-strip guard: if the redirect crosses origin, drop the
|
|
70
|
+
// request headers so any future auth/cookie header is never re-sent to
|
|
71
|
+
// a different host. (Today only Accept/User-Agent are sent, but this
|
|
72
|
+
// future-proofs against header leakage across origins.)
|
|
73
|
+
if (next.origin !== previous.origin) {
|
|
74
|
+
headers = { Accept: options.accept };
|
|
75
|
+
}
|
|
76
|
+
url = next.toString();
|
|
77
|
+
redirects += 1;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const contentType = String(response.headers["content-type"] ?? "");
|
|
82
|
+
if (status < 200 || status >= 300) {
|
|
83
|
+
throw this.toApiError(method, url, status, response.body);
|
|
84
|
+
}
|
|
85
|
+
return { data: response.body, contentType, status };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/** Perform a GET expecting JSON and parse it into `T`. */
|
|
89
|
+
async getJson(path, query) {
|
|
90
|
+
const res = await this.request("GET", path, { query, accept: "application/json" });
|
|
91
|
+
const text = res.data.toString("utf8");
|
|
92
|
+
try {
|
|
93
|
+
return JSON.parse(text);
|
|
94
|
+
}
|
|
95
|
+
catch (cause) {
|
|
96
|
+
throw new GovDataParseError(`Failed to parse JSON response from ${path}`, { cause });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
toApiError(method, url, status, body) {
|
|
100
|
+
const text = body.toString("utf8");
|
|
101
|
+
let detail;
|
|
102
|
+
try {
|
|
103
|
+
const parsed = JSON.parse(text);
|
|
104
|
+
// CKAN nests its human-readable error under `error.message` (with an
|
|
105
|
+
// `error.__type` classifier); plainer APIs use a top-level
|
|
106
|
+
// `detail`/`message`. Prefer the nested CKAN shape, then fall back.
|
|
107
|
+
const ckanError = parsed && typeof parsed.error === "object" && parsed.error !== null
|
|
108
|
+
? parsed.error
|
|
109
|
+
: undefined;
|
|
110
|
+
if (ckanError && typeof ckanError.message === "string") {
|
|
111
|
+
detail =
|
|
112
|
+
typeof ckanError.__type === "string"
|
|
113
|
+
? `${ckanError.__type}: ${ckanError.message}`
|
|
114
|
+
: ckanError.message;
|
|
115
|
+
}
|
|
116
|
+
else if (ckanError) {
|
|
117
|
+
detail = JSON.stringify(ckanError);
|
|
118
|
+
}
|
|
119
|
+
else if (parsed && typeof parsed.detail === "string") {
|
|
120
|
+
detail = parsed.detail;
|
|
121
|
+
}
|
|
122
|
+
else if (parsed && typeof parsed.message === "string") {
|
|
123
|
+
detail = parsed.message;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// Non-JSON error body; leave detail undefined.
|
|
128
|
+
}
|
|
129
|
+
return new GovDataApiError({ status, url, method, body: text, detail });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../../src/client/engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** Base class for every error originating from this client. */
|
|
2
|
+
export declare class GovDataError extends Error {
|
|
3
|
+
constructor(message: string, options?: {
|
|
4
|
+
cause?: unknown;
|
|
5
|
+
});
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* The API responded with a non-2xx status code. `detail` holds a human-readable
|
|
9
|
+
* message extracted from the response body when one is present.
|
|
10
|
+
*/
|
|
11
|
+
export declare class GovDataApiError extends GovDataError {
|
|
12
|
+
readonly status: number;
|
|
13
|
+
readonly detail: string | undefined;
|
|
14
|
+
readonly url: string;
|
|
15
|
+
readonly method: string;
|
|
16
|
+
readonly body: string;
|
|
17
|
+
constructor(args: {
|
|
18
|
+
status: number;
|
|
19
|
+
url: string;
|
|
20
|
+
method: string;
|
|
21
|
+
body: string;
|
|
22
|
+
detail?: string;
|
|
23
|
+
});
|
|
24
|
+
/** True for statuses the API documents as transient and retry-able. */
|
|
25
|
+
get isRetryable(): boolean;
|
|
26
|
+
}
|
|
27
|
+
/** A transport-level failure (DNS, connection reset, timeout, ...). */
|
|
28
|
+
export declare class GovDataNetworkError extends GovDataError {
|
|
29
|
+
}
|
|
30
|
+
/** The response body could not be parsed as the expected JSON shape. */
|
|
31
|
+
export declare class GovDataParseError extends GovDataError {
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/client/errors.ts"],"names":[],"mappings":"AAGA,+DAA+D;AAC/D,qBAAa,YAAa,SAAQ,KAAK;gBACzB,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAI3D;AAED;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,YAAY;IAC/C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,IAAI,EAAE;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB;IAUD,uEAAuE;IACvE,IAAI,WAAW,IAAI,OAAO,CAEzB;CACF;AAED,uEAAuE;AACvE,qBAAa,mBAAoB,SAAQ,YAAY;CAAG;AAExD,wEAAwE;AACxE,qBAAa,iBAAkB,SAAQ,YAAY;CAAG"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Error types raised by the client. Kept free of any I/O so they are trivial to
|
|
2
|
+
// construct in tests and to `instanceof`-check by consumers.
|
|
3
|
+
/** Base class for every error originating from this client. */
|
|
4
|
+
export class GovDataError extends Error {
|
|
5
|
+
constructor(message, options) {
|
|
6
|
+
super(message, options);
|
|
7
|
+
this.name = new.target.name;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The API responded with a non-2xx status code. `detail` holds a human-readable
|
|
12
|
+
* message extracted from the response body when one is present.
|
|
13
|
+
*/
|
|
14
|
+
export class GovDataApiError extends GovDataError {
|
|
15
|
+
status;
|
|
16
|
+
detail;
|
|
17
|
+
url;
|
|
18
|
+
method;
|
|
19
|
+
body;
|
|
20
|
+
constructor(args) {
|
|
21
|
+
const detailPart = args.detail ? `: ${args.detail}` : "";
|
|
22
|
+
super(`HTTP ${args.status} for ${args.method} ${args.url}${detailPart}`);
|
|
23
|
+
this.status = args.status;
|
|
24
|
+
this.url = args.url;
|
|
25
|
+
this.method = args.method;
|
|
26
|
+
this.body = args.body;
|
|
27
|
+
this.detail = args.detail;
|
|
28
|
+
}
|
|
29
|
+
/** True for statuses the API documents as transient and retry-able. */
|
|
30
|
+
get isRetryable() {
|
|
31
|
+
return this.status === 429 || this.status === 503;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/** A transport-level failure (DNS, connection reset, timeout, ...). */
|
|
35
|
+
export class GovDataNetworkError extends GovDataError {
|
|
36
|
+
}
|
|
37
|
+
/** The response body could not be parsed as the expected JSON shape. */
|
|
38
|
+
export class GovDataParseError extends GovDataError {
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/client/errors.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,6DAA6D;AAE7D,+DAA+D;AAC/D,MAAM,OAAO,YAAa,SAAQ,KAAK;IACrC,YAAY,OAAe,EAAE,OAA6B;QACxD,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;IAC9B,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,OAAO,eAAgB,SAAQ,YAAY;IACtC,MAAM,CAAS;IACf,MAAM,CAAqB;IAC3B,GAAG,CAAS;IACZ,MAAM,CAAS;IACf,IAAI,CAAS;IAEtB,YAAY,IAMX;QACC,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACzD,KAAK,CAAC,QAAQ,IAAI,CAAC,MAAM,QAAQ,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,GAAG,GAAG,UAAU,EAAE,CAAC,CAAC;QACzE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;QACpB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IAC5B,CAAC;IAED,uEAAuE;IACvE,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,MAAM,KAAK,GAAG,CAAC;IACpD,CAAC;CACF;AAED,uEAAuE;AACvE,MAAM,OAAO,mBAAoB,SAAQ,YAAY;CAAG;AAExD,wEAAwE;AACxE,MAAM,OAAO,iBAAkB,SAAQ,YAAY;CAAG"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import http from "node:http";
|
|
2
|
+
export interface HttpRequest {
|
|
3
|
+
method: string;
|
|
4
|
+
/** Fully-qualified absolute URL. */
|
|
5
|
+
url: string;
|
|
6
|
+
headers?: Record<string, string>;
|
|
7
|
+
/** Optional request body (already serialised). */
|
|
8
|
+
body?: string | Buffer;
|
|
9
|
+
/** Per-request timeout in milliseconds. */
|
|
10
|
+
timeoutMs?: number;
|
|
11
|
+
/** Hard cap on the response body size in bytes; the request aborts if exceeded. */
|
|
12
|
+
maxResponseBytes?: number;
|
|
13
|
+
}
|
|
14
|
+
export interface HttpResponse {
|
|
15
|
+
status: number;
|
|
16
|
+
headers: http.IncomingHttpHeaders;
|
|
17
|
+
body: Buffer;
|
|
18
|
+
}
|
|
19
|
+
export type Transport = (request: HttpRequest) => Promise<HttpResponse>;
|
|
20
|
+
/**
|
|
21
|
+
* Default transport. Resolves with the raw response (including non-2xx) — status
|
|
22
|
+
* interpretation is the client's job. Rejects only on transport-level failures
|
|
23
|
+
* (connection errors, timeouts, malformed URLs).
|
|
24
|
+
*/
|
|
25
|
+
export declare const nodeHttpTransport: Transport;
|
|
26
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../../src/client/http.ts"],"names":[],"mappings":"AAQA,OAAO,IAAI,MAAM,WAAW,CAAC;AAI7B,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,SAwE5B,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// HTTP transport built on Node's built-in `http`/`https` modules — no axios,
|
|
2
|
+
// no fetch polyfill, no third-party HTTP client.
|
|
3
|
+
//
|
|
4
|
+
// The transport is a plain function so it can be trivially swapped out in tests
|
|
5
|
+
// (inject a `mock.fn()` returning a canned HttpResponse) without touching the
|
|
6
|
+
// network. The default implementation below is exercised against a real local
|
|
7
|
+
// `http.createServer` in the test-suite.
|
|
8
|
+
import http from "node:http";
|
|
9
|
+
import https from "node:https";
|
|
10
|
+
import { GovDataNetworkError } from "./errors.js";
|
|
11
|
+
/**
|
|
12
|
+
* Default transport. Resolves with the raw response (including non-2xx) — status
|
|
13
|
+
* interpretation is the client's job. Rejects only on transport-level failures
|
|
14
|
+
* (connection errors, timeouts, malformed URLs).
|
|
15
|
+
*/
|
|
16
|
+
export const nodeHttpTransport = (request) => new Promise((resolve, reject) => {
|
|
17
|
+
let url;
|
|
18
|
+
try {
|
|
19
|
+
url = new URL(request.url);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
reject(new GovDataNetworkError(`Invalid URL: ${request.url}`));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
// Only http/https are supported. Reject anything else up front with a clear,
|
|
26
|
+
// typed error instead of letting Node throw an opaque ERR_INVALID_PROTOCOL
|
|
27
|
+
// (and so this never reaches the file:/ftp:/etc. drivers).
|
|
28
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
29
|
+
reject(new GovDataNetworkError(`Unsupported protocol "${url.protocol}" in URL: ${request.url}`));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const isHttps = url.protocol === "https:";
|
|
33
|
+
const driver = isHttps ? https : http;
|
|
34
|
+
const maxBytes = request.maxResponseBytes;
|
|
35
|
+
const req = driver.request(url, {
|
|
36
|
+
method: request.method,
|
|
37
|
+
headers: request.headers,
|
|
38
|
+
}, (res) => {
|
|
39
|
+
const chunks = [];
|
|
40
|
+
let received = 0;
|
|
41
|
+
let aborted = false;
|
|
42
|
+
res.on("data", (chunk) => {
|
|
43
|
+
if (aborted)
|
|
44
|
+
return;
|
|
45
|
+
received += chunk.length;
|
|
46
|
+
if (maxBytes !== undefined && received > maxBytes) {
|
|
47
|
+
aborted = true;
|
|
48
|
+
res.destroy();
|
|
49
|
+
reject(new GovDataNetworkError(`Response exceeded maxResponseBytes (${maxBytes})`));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
chunks.push(chunk);
|
|
53
|
+
});
|
|
54
|
+
res.on("end", () => {
|
|
55
|
+
if (aborted)
|
|
56
|
+
return;
|
|
57
|
+
resolve({
|
|
58
|
+
status: res.statusCode ?? 0,
|
|
59
|
+
headers: res.headers,
|
|
60
|
+
body: Buffer.concat(chunks),
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
res.on("error", (err) => {
|
|
64
|
+
if (aborted)
|
|
65
|
+
return; // we already rejected with the size-cap error
|
|
66
|
+
reject(new GovDataNetworkError(`Response stream error: ${err.message}`, { cause: err }));
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
if (request.timeoutMs && request.timeoutMs > 0) {
|
|
70
|
+
req.setTimeout(request.timeoutMs, () => {
|
|
71
|
+
req.destroy(new GovDataNetworkError(`Request timed out after ${request.timeoutMs}ms`));
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
req.on("error", (err) => {
|
|
75
|
+
// A timeout destroy already passes an GovDataNetworkError; don't double-wrap.
|
|
76
|
+
reject(err instanceof GovDataNetworkError ? err : new GovDataNetworkError(err.message, { cause: err }));
|
|
77
|
+
});
|
|
78
|
+
if (request.body !== undefined)
|
|
79
|
+
req.write(request.body);
|
|
80
|
+
req.end();
|
|
81
|
+
});
|
|
82
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../../src/client/http.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { GovDataClient } from "./client.js";
|
|
2
|
+
export { RequestEngine, DEFAULT_BASE_URL } from "./engine.js";
|
|
3
|
+
export type { EngineOptions, RawResponse } from "./engine.js";
|
|
4
|
+
export { nodeHttpTransport } from "./http.js";
|
|
5
|
+
export type { Transport, HttpRequest, HttpResponse } from "./http.js";
|
|
6
|
+
export { buildQueryString } from "./query.js";
|
|
7
|
+
export type { QueryParams, QueryValue } from "./query.js";
|
|
8
|
+
export { GovDataError, GovDataApiError, GovDataNetworkError, GovDataParseError, } from "./errors.js";
|
|
9
|
+
export * from "./types.js";
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC9D,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9C,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EACL,YAAY,EACZ,eAAe,EACf,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,aAAa,CAAC;AAErB,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Public entry point for the API client library.
|
|
2
|
+
export { GovDataClient } from "./client.js";
|
|
3
|
+
export { RequestEngine, DEFAULT_BASE_URL } from "./engine.js";
|
|
4
|
+
export { nodeHttpTransport } from "./http.js";
|
|
5
|
+
export { buildQueryString } from "./query.js";
|
|
6
|
+
export { GovDataError, GovDataApiError, GovDataNetworkError, GovDataParseError, } from "./errors.js";
|
|
7
|
+
export * from "./types.js";
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA,iDAAiD;AAEjD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAE9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EACL,YAAY,EACZ,eAAe,EACf,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,aAAa,CAAC;AAErB,cAAc,YAAY,CAAC"}
|