/** * GPR-1178 — reading the getpeppr result contract off the response headers. * * The gateway publishes five additive headers on every public `/v1` response * IT WRITES (GPR-1174), plus a sixth — `Getpeppr-Result-Docs` — only when the * result has a guide to link to. They ride alongside every body shape the API * already returns — `{data,meta}`, bare objects, bare arrays, bodyless `204`s, * PDFs, redirects — which is why the contract lives in headers and not in a * JSON envelope. * * ⚠️ "Plus a sixth" is not a footnote: only a MINORITY of results carry a guide, * so the docs header is absent from most responses. Read its absence as normal — * a client treating it as guaranteed would read the ordinary case as a fault. * * ⛔ NO COUNT HERE, deliberately. An earlier draft of this very sentence said * "73 of 229", which was measured and correct on the day — and is a fact about * a catalogue that grows every tranche, written where nothing can update it. * That is the defect this ticket exists to close, reintroduced in the fix for * it. The public reference page DERIVES its count and cannot go stale; the * always-on five and the conditional sixth are partitioned mechanically in the * console's `lib/api/results/headers.ts`, against the builder that emits them, * with a test that fails if the conditional one stops being a minority. * * ⚠️ "It writes" is load-bearing, and this sentence said "every public /v1 * response" until GPR-1181's relance. Some requests never reach the gateway: * the hosting platform answers them at its own border, with none of the six — * a verb outside the seven Next dispatches gets `405 text/plain` straight from * the edge. That is precisely why every field below is optional and why * `parseApiResultHeaders` returns `undefined` rather than an empty object: a * caller must be able to tell "getpeppr said nothing" from "getpeppr said * nothing useful". Measured inventory: the console's * `lib/api/results/platform-terminations.ts`. * * ## What this module refuses to do * * **Fabricate.** Every field is optional, and an unreadable value yields an * ABSENT field rather than a plausible one. A caller reads absence as "the * gateway did not say"; a fabricated value is indistinguishable from a measured * one and no downstream check can catch it. * * **Truncate.** An over-long value is dropped whole. A truncated sentence reads * exactly like a complete one — the same failure, wearing the shape of success. * * **Close the enums.** `remediation` and `code` are typed open on purpose: a * value this SDK build has never heard of is passed through verbatim. Refusing * it would blank the field and turn "new" into "absent" for a client running an * older SDK against a newer gateway — and that skew is the normal state, not * the exception. */ /** * The six header names, exactly as `packages/console/src/lib/api/results/headers.ts` * emits them. Lookup is case-insensitive (`Headers.get` handles that), so this * spelling is documentation and a test anchor, not a matching requirement. */ export declare const API_RESULT_HEADER_NAMES: { readonly requestId: "Getpeppr-Request-Id"; readonly resultCode: "Getpeppr-Result-Code"; readonly resultMessage: "Getpeppr-Result-Message"; readonly retryable: "Getpeppr-Retryable"; readonly remediation: "Getpeppr-Remediation"; readonly docs: "Getpeppr-Result-Docs"; }; /** * What the caller should DO about this result. * * The catalogue's enum is closed today; this type is deliberately open so a * value added server-side reaches you rather than vanishing. The listed members * are the ones the catalogue defines today; do not assume the set is closed. */ export type ApiResultRemediation = "none" | "fix_request" | "authenticate" | "retry" | "retry_after" | "wait" | "contact_support" | (string & {}); /** * A stable getpeppr result code, spelled `domain.outcome` * (e.g. `"invoices_import.validation_failed"`, `"auth.api_key_invalid"`). * * Typed `string` rather than a generated union: pinning the union in a * published package would make every gateway-side addition a breaking change * for anyone who has not upgraded. */ export type ApiResultCode = string; /** * The canonical result of one HTTP response, as the gateway declared it. * * Every field is optional and independently so. A gateway that has not yet * activated the catalogue emits none of them, and a proxy may strip some — so * never infer one field's meaning from another's presence. */ export interface ApiResult { /** * Server-generated correlation id (`req_` + 32 hex today). Quote it to * support and they can find this exact request. * * `undefined` when the gateway sent no request-id header — pre-activation * deployments, and any hop that strips unknown headers. */ requestId?: string; /** * Stable machine-readable code for this outcome. * * ⚠️ NOT the same field as `PeppolApiError.code`, which reads `body.code` and * carries a route-specific sub-reason. Both can be present and different. * * `undefined` when the header is absent or unreadable. */ code?: ApiResultCode; /** Catalogue sentence for `code`. `undefined` when absent, blank or over-long. */ message?: string; /** * Whether retrying this same request can succeed, as decided by the CODE and * not by the status alone. * * `undefined` when the header is absent or is anything other than `true` / * `false` — an unreadable value must never become a retry permission. */ retryable?: boolean; /** Recommended action. `undefined` when the header is absent or blank. */ remediation?: ApiResultRemediation; /** * Documentation link for this code, normalised through the URL parser. * * `undefined` when absent, relative, carrying any scheme other than * `https:`, carrying credentials in the authority, or malformed enough that * the URL parser would have to repair it — `javascript:` parses perfectly * well, so parsing is not validation. */ docs?: string; } /** * Replace every control character with a space. * * @internal — exported for `client.ts`, which sanitises error bodies with the * same rule. One definition, deliberately: two copies of a security invariant * are two things free to drift apart. */ export declare function stripControls(value: string): string; /** * Accept a value only if it is an absolute http(s) URL, and return the PARSED * form. * * Two reasons, both measured. `new URL` normalises control characters out of * the href — most percent-encoded (`ESC` becomes `%1B`), while TAB, LF and CR * are STRIPPED per the WHATWG parser — so the link cannot smuggle an escape * sequence either way. And it happily accepts `javascript:`, so the protocol * has to be checked separately. * * Returning `parsed.href` rather than the input is the GPR-1174 lesson: * validating one string and emitting another is how a check gets bypassed. * * @internal */ export declare function safeDocsUrl(value: unknown): string | null; /** * Read the getpeppr result contract from a response's headers. * * Returns `undefined` when NOTHING usable is present — an un-activated gateway, * a stripping proxy, or a block whose every value was unreadable. That is a * distinct answer from "a result with all fields absent", and callers rely on * it: `ResponseLogEntry.result` and `PeppolApiError.result` stay `undefined` * rather than carrying an empty shell that looks like a contract. */ export declare function parseApiResultHeaders(headers: Headers): ApiResult | undefined; //# sourceMappingURL=api-result.d.ts.map