Shared transport for the three registries: the one settings resolver (per-call opts first, then context variables, then defaults), a typed HttpError, JSON GETs, and query-string building that drops unset values.

opts keyContext variableDefault
user-agentcompany-registry.sec.user-agentnone: SEC calls fail without it
api-keycompany-registry.companies-house.api-keynone: Companies House calls fail without it
companies-house-base-urlcompany-registry.companies-house.base-urlhttps://api.company-information.service.gov.uk
document-base-urlcompany-registry.companies-house.document-base-urlhttps://document-api.company-information.service.gov.uk
gleif-base-urlcompany-registry.gleif.base-urlhttps://api.gleif.org/api/v1
sourcescompany-registry.sources["gleif", "sec", "companies-house"] minus any without credentials

Per-call credentials. api-key and user-agent in opts are masked in call records and never written to context, so parallel flows and tenants in one run don't see each other's keys. Present but blank or null, either one is an error, never a fallback to context. A per-call companies-house-base-url or document-base-url needs an api-key in the same opts: the context key is never sent to a per-call URL. GLEIF takes no credential, so gleif-base-url needs none.

Context Vars

Optional
  • company-registry.sec.user-agent
  • company-registry.companies-house.api-key
  • company-registry.companies-house.base-url
  • company-registry.companies-house.document-base-url
  • company-registry.gleif.base-url
  • company-registry.sources

Values

COMPANIES_HOUSE_BASE

COMPANIES_HOUSE_BASE: Str "https://api.company-information.service.gov.uk"

CREDENTIAL_KEYS

CREDENTIAL_KEYS: Vec [
  "api-key",
  "user-agent"
]

DOCUMENT_BASE

DOCUMENT_BASE: Str "https://document-api.company-information.service.gov.uk"

GLEIF_BASE

GLEIF_BASE: Str "https://api.gleif.org/api/v1"

KEYED_URL_KEYS

KEYED_URL_KEYS: Vec [
  "companies-house-base-url",
  "document-base-url"
]

PACKAGE_AGENT

PACKAGE_AGENT: Str "hot-dev-company-registry-pkg/0.1 (+https://hot.dev)"

Functions

as-list

fn (v: Any): Vec?

body-detail

fn (body: Any): Str?

columns-to-rows

fn (columns: Map, limit: Int): Vec<Map>

Turn a columnar Map ({a: [1, 2], b: [3, 4]}, as in EDGAR's filings.recent) into a Vec of row Maps ([{a: 1, b: 3}, {a: 2, b: 4}]), at most limit rows. Columns shorter than the first are padded with null. Pure.

get-json

fn (source: Str, url: Str, headers: Map): Any

GET a JSON resource and return the decoded body, or an Err carrying HttpError.

get-json-or-null

fn (source: Str, url: Str, headers: Map): Any

get-json, but a 404 gives null (an absent parent, a missing filing) instead of an Err.

given

fn (v: Any): Bool

True for a non-null value that is not a blank string.

http-error

fn (source: Str, response: ::hot::http/HttpResponse, url: Str): HttpError

Build an HttpError from a non-2xx response. Exposed for testing; every request uses it.

is-not-found

fn (e: Any): Bool

True when e is an HttpError with status 404.

path-segment

fn (value: Any): Str

Percent-encode one path segment.

pick

fn (opts: Map, key: Str, ctx-key: Str, default: Any): Any

query-string

fn (params: Map): Str

Build a ?a=1&b=2 suffix from a Map, dropping null and blank values (pure); "" when nothing remains. Keys may contain brackets (filter[fulltext], page[size]) and are encoded.

request

fn (source: Str, method: Str, url: Str, headers: Map): ::hot::http/HttpResponse

GET (or another method) a registry URL with headers, returning the HttpResponse on 2xx or an Err carrying HttpError. source names the registry in errors. A transport failure passes through as its own Err.

require-api-key

fn (cfg: Map): Str

The Companies House API key from resolved settings, or an Err naming where to set it.

require-user-agent

fn (cfg: Map): Str

The SEC User-Agent from resolved settings, or an Err explaining that SEC requires one with contact details.

resolve

fn (opts: Map): Map

Resolve settings for one call: {user-agent, api-key, companies-house-base-url, document-base-url, gleif-base-url, sources}. Values in opts win over context; a credential in opts is used for that call only and comes back unchanged. An Err when a credential is in opts but blank or null, or when opts has a Companies House URL without api-key.

Example

cfg ::company-registry::api/resolve({api-key: tenant.ch-key})

retry-after

fn (headers: Map): Int?

strip-slash

fn (url: Str): Str

Types

HttpError

HttpError type {
    source: Str,
    status: Int,
    url: Str,
    headers: Map,
    body: Any,
    retry-after: Int?,
    message: Str
}

A non-2xx registry response. source is "gleif", "sec" or "companies-house"; url the requested URL (registries take no credentials in URLs); retry-after the parsed Retry-After seconds on 429s; message a one-line summary that never includes credentials.