README.md

<p align="center">
  <img src="assets/pristine.svg" width="200" height="200" alt="Pristine logo" />
</p>

<h1 align="center">Pristine</h1>

<p align="center">
  <a href="https://hex.pm/packages/pristine"><img src="https://img.shields.io/hexpm/v/pristine.svg" alt="Hex Version" /></a>
  <a href="https://hexdocs.pm/pristine"><img src="https://img.shields.io/badge/hex-docs-blue.svg" alt="Hex Docs" /></a>
  <a href="https://github.com/nshkrdotcom/pristine"><img src="https://img.shields.io/badge/GitHub-repo-black?logo=github" alt="GitHub" /></a>
  <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-green.svg" alt="License" /></a>
</p>

Pristine is the shared runtime and build-time bridge for first-party
OpenAPI-based Elixir SDKs.

The recommended provider-SDK boundary is:

- `Pristine.execute_request/3`
- `Pristine.foundation_context/1`
- `Pristine.SDK.*`

The retained build-time seam is `Pristine.OpenAPI.Bridge.run/3`.

`Pristine.context/1` also remains available when you want full manual
ports-and-adapters control, but provider SDKs should treat `Pristine.Core.*`
and `Pristine.OpenAPI.*` as internal implementation detail rather than as the
blessed SDK contract.

## Runtime Boundary

Use `Pristine.foundation_context/1` for the recommended production runtime:

```elixir
context =
  Pristine.foundation_context(
    base_url: "https://api.example.com",
    transport: Pristine.Adapters.Transport.Finch,
    transport_opts: [finch: MyApp.Finch],
    serializer: Pristine.Adapters.Serializer.JSON,
    auth: [Pristine.Adapters.Auth.Bearer.new(System.fetch_env!("API_TOKEN"))]
  )
```

Execute a normalized request spec through `Pristine.execute_request/3`:

```elixir
request_spec = %{
  id: "widgets.list",
  method: :get,
  path: "/v1/widgets",
  path_params: %{},
  query: %{"limit" => 10},
  headers: %{},
  body: nil,
  form_data: nil,
  auth: nil,
  security: [%{"bearerAuth" => []}],
  request_schema: nil,
  response_schema: nil,
  resource: "widgets",
  retry: "widgets.read",
  rate_limit: "widgets.integration",
  circuit_breaker: "core_api",
  telemetry: "request.widgets"
}

{:ok, response} = Pristine.execute_request(request_spec, context)
```

`Pristine.execute_request/3` also accepts the generated request maps emitted by
`Pristine.SDK.OpenAPI.Client`. In both cases, the same runtime path validation,
serialization, auth, retry, telemetry, rate-limit, and circuit-breaker wiring
still applies.

`Pristine.SDK.*` exposes the stable runtime-facing types used by downstream SDKs:

- `Pristine.SDK.Context`
- `Pristine.SDK.Response`
- `Pristine.SDK.Error`
- `Pristine.SDK.ResultClassification`
- `Pristine.SDK.OpenAPI.*`
- `Pristine.SDK.OAuth2.*`
- `Pristine.SDK.Profiles.Foundation`

## Manual Context Construction

Use `Pristine.context/1` when you want complete control over the raw runtime
ports and adapters:

```elixir
context =
  Pristine.context(
    base_url: "https://api.example.com",
    transport: Pristine.Adapters.Transport.Finch,
    transport_opts: [finch: MyApp.Finch],
    serializer: Pristine.Adapters.Serializer.JSON,
    retry: Pristine.Adapters.Retry.Noop,
    telemetry: Pristine.Adapters.Telemetry.Noop
  )
```

That lower-level constructor is useful for bespoke clients and tests. The
Foundation profile exists so production callers do not have to hand-wire the
same resilience and telemetry stack repeatedly.

## OAuth Provider Construction

SDK-facing OAuth provider construction stays tied to OpenAPI security scheme
metadata, not to manifests.

```elixir
provider =
  Pristine.SDK.OAuth2.Provider.from_security_scheme!(
    "notionOAuth",
    %{
      "type" => "oauth2",
      "flows" => %{
        "authorizationCode" => %{
          "authorizationUrl" => "/v1/oauth/authorize",
          "tokenUrl" => "/v1/oauth/token",
          "scopes" => %{"workspace.read" => "Read workspace data"}
        }
      },
      "x-pristine-flow" => "authorizationCode",
      "x-pristine-token-content-type" => "application/json"
    },
    site: "https://api.notion.com"
  )
```

`Pristine.SDK.OAuth2` uses the in-tree
`Pristine.Adapters.OAuthBackend.Native` backend by default. Browser launch and
loopback callback capture stay optional adapter seams:

- `Pristine.Adapters.OAuthBrowser.SystemCmd`
- `Pristine.Adapters.OAuthCallbackListener.Bandit`

Manual paste-back still works without those adapters, and persisted token
load/save/refresh orchestration lives in `Pristine.OAuth2.SavedToken` on top of
the token-source boundary.

## Build-Time Bridge

`Pristine.OpenAPI.Bridge.run/3` is the retained first-party build-time seam for
SDK generation. It is not the normal consumer runtime entry.

The bridge needs at least a base module and output directory:

```elixir
result =
  Pristine.OpenAPI.Bridge.run(
    :widgets_sdk,
    ["openapi/widgets.json"],
    base_module: WidgetsSDK,
    output_dir: "lib/widgets_sdk/generated",
    source_contexts: %{
      {:get, "/v1/widgets"} => %{
        title: "Widgets",
        url: "https://docs.example.com/widgets"
      }
    }
  )

sources = Pristine.OpenAPI.Bridge.generated_sources(result)
```

The returned `%Pristine.OpenAPI.Result{}` contains:

- `ir`
- `source_contexts`
- `docs_manifest`

That lets first-party SDK generators reuse the same IR, generated files, and
docs manifest without exposing a manifest-shaped runtime API.

## Guides

- [Getting Started](guides/getting-started.md)
- [Foundation Runtime](guides/foundation-runtime.md)
- [Manual Contexts and Adapters](guides/manual-contexts-and-adapters.md)
- [OAuth and Token Sources](guides/oauth-and-token-sources.md)
- [Streaming and SSE](guides/streaming-and-sse.md)
- [Code Generation](guides/code-generation.md)
- [Testing and Verification](guides/testing-and-verification.md)