Elixir

Thinwrap's three Hex packages are written in Erlang, so an Elixir project depends on them like any other Hex package and calls the Erlang modules directly. There is no Elixir wrapper and there are no structs: config, input and results are plain maps with atom keys and string values. The claims below hold for Elixir only.

Elixir constraints

Runtime dependencies
Zero runtime dependencies
BYO HTTP client
transport behaviour: transport: {MyTransport, opts}, an Elixir module implementing request/2
Provenance
Hex registry signatures + lockfile checksums
Minimum runtime
Elixir 1.17+ on Erlang/OTP 27+
  • The same Hex package as Erlang, with zero dependencies of its own: no Elixir wrapper and no structs. You call the Erlang modules directly (:thinwrap_location.route/2) with plain maps — atom keys, string values — and get maps back.
  • Hex has no build-provenance attestation like npm provenance or PEP 740: the registry signs its index and mix.lock pins each tarball checksum, which proves integrity, not where the package was built.
  • Elixir 1.17 is the first release that runs on OTP 27, which the package requires.

Setup

Add the packages you need to mix.exs. Noextra_applications entry is needed: the packages declareinets and ssl themselves.

defp deps do
  [
    {:thinwrap_location, "~> 1.3"},
    {:thinwrap_notifications, "~> 1.2"},
    {:thinwrap_llm, "~> 1.1"}
  ]
end

One call

Every operation is a function of a config map and an input map. The config's provider picks the connector; switching providers changes the config, not the input or the result. The result is {:ok, map} or {:error, map}, so it pattern-matches like any Elixir API.

# OSRM's public demo server needs no key, so this runs as-is.
config = %{provider: :osrm, base_url: "https://router.project-osrm.org"}

input = %{
  waypoints: [
    %{lat: 52.5200, lng: 13.4050},
    %{lat: 52.3906, lng: 13.0645}
  ]
}

case :thinwrap_location.route(config, input) do
  {:ok, %{total_distance_meters: m, total_duration_seconds: s}} ->
    IO.puts("#{Float.round(m / 1000, 1)} km, #{round(s / 60)} min")

  {:error, %{code: code, message: message}} ->
    IO.puts("#{code}: #{message}")
end
# => 35.1 km, 39 min

Packages

Baseline-coverage discipline

The unified surface includes only features that ≥90% of providers natively support. Sub-baseline fields stay accessible via thepassthrough key (body deep-merged into the request body; your values win), and the vendor's answer rides inraw. Cross-language parity with the other packages is a design goal — not a hard release gate.

An operation failure returns {:error, error}, a map with a :code atom (:auth_failed,:rate_limited, :invalid_request, …) and a:message. Misusing the API — an unknown key, a wrongly typed value, a provider that doesn't serve the operation — raises ArgumentError before any request is sent (catch :error, reason sees the original{:badarg, description}). The wrapper holds no state and performs no automatic retry: the raw Retry-After header and its parsed seconds ride in :cause, and there isno top-level retry key — by design. To bring your own HTTP client, write a module with @behaviour :thinwrap_location_transport and a request/2, and pass it as transport: {MyTransport, opts}.