# The package format

A package is laid out as `shared/` is, so one set of rules covers the app
and every package, and the compiler checks a package as it checks
`shared/`.
A library package:

```text
postcodes/v2/
    package.jadpo                   the index: description, licence, lowest Jadpo version
    types/uk_postcode.jadpo         public type UkPostcode = Text { ... }
    failures/invalid_postcode.jadpo
    libraries/uk.jadpo              public library uk { parse(...)  normalise(...) }
    libraries/uk.test.jadpo         tests beside the file, as in an app
    translations/                   its own messages and failure wording
    GUIDE.md                        optional: how to use it well
    CHANGELOG.md
    surface.jadpo_record            generated
```

A provider package adds `provider.jadpo` and the folders an API needs:

```text
stripe/v2024_06_20/
    package.jadpo                          the same two keys as every package
    provider.jadpo                         how the API is reached and called
    resources/payment_intents.jadpo        imported, corrections merged in
    types/payment_intent.jadpo             imported
    failures/card_declined.jadpo
    events/payment_intent_succeeded.jadpo
    actions/start_subscription.jadpo       written: several of its own calls in order
    subscribers/renewal.jadpo              written: enriches its own webhook
    libraries/tables.jadpo                 written: decoders and helpers
    provides/email.jadpo                   only when it fills a capability
    recipes/track_parcels/                 wiring examples an app copies in
    recordings/payment_intents.create.json real responses that confirm the types
    surface.jadpo_record
```

- **The index lists no files.** A file is named after what it declares, so
  the compiler finds a resource by name and loads only what the app uses.
- **A re-import rewrites only imported files.** Written libraries, actions
  and corrections survive.
- **A package's tests run when it is published**, not in every app's
  `jadpo test`; the digest guarantees the code is the code that passed.
- **No `emails/` folder.** Email templates, partials and layouts are the
  app's own, and an `emails/` folder in a package is a compile error.
- **Translations** are only in library packages, for their own messages
  and failures, laid out as a domain's
  ([Packages' text: groups, calls and the surface](packages-text-in-an-app.md#packages-text-groups-calls-and-the-surface)).
  An app calls them as `Postcodes.translations.<message>(...)` and rewords
  them in its root `translations/`. Provider packages show users no text,
  because their failures are always mapped to the app's own, so they
  carry none.
- **`GUIDE.md`** is optional, and says how to use the package well. Every
  code example in it is compiled and run when the package is published,
  and `jadpo package show <name> --guide` prints it.

## The index: `package.jadpo`

Every package's index has the same form: `package Name.vN { }`, a `///`
description above it, and two keys. `licence:` names its licence, and
`jadpo:` the lowest Jadpo version it needs.

The licence is one of `Licence.mit`, `.apache_2_0`, `.bsd_2_clause`,
`.bsd_3_clause`, `.isc`, `.mpl_2_0`, `.unlicense` and `.proprietary`.
Only a package installed from git may be `.proprietary`; the public
registry refuses it. There is no `requires:`, because packages have no
dependencies, and no list of files.

```jadpo
// packages/postcodes/v2/package.jadpo
/// UK postcodes: parsing, normalising and checking the outward code.
package Postcodes.v2 {
    licence: Licence.mit
    jadpo: "1.0"
}
```

## How the API is reached: `provider.jadpo`

A package that describes an API has one `provider.jadpo`: a
`provider { }` declaration with a fixed set of keys. A package without
one is a library.

One package describes at most one API, because one API is one connection
with one set of hosts and credentials. A vendor with two APIs under
different credentials, such as Shopify's Admin and Storefront APIs, is two
packages.

| Key | Says |
|---|---|
| `protocol:` | How bodies are sent: `Protocol.json_http`, `.form_http`, `.multipart_http`, `.aws_json`. Required |
| `hosts:` | `live:` and `sandbox:`; the same host twice when the sandbox is a test credential; a host may hold one placeholder filled from a setting (`"https://{project_ref}.supabase.co"`) |
| `runs:` | A container Jadpo runs instead of a fixed host: `image:` pinned by digest, `port:` it listens on inside the container, `asks:` the outbound hosts it needs (see "Services") |
| `credential:` | The scheme (`CredentialScheme.bearer`, a named header, basic, AWS signing), which decides the shape of credential the app gives |
| `credential_header:` | For `CredentialScheme.header`, the named header the credential goes in alone (`"x-api-key"`); required for it and only for it |
| `headers:` | The fixed headers every request carries, each `"Name: value"` (`["anthropic-version: 2023-06-01"]`), never one the driver writes itself |
| `oauth:` | The provider's authorize, token and revoke addresses, for a connection whose credential is an `OAuthGrant` (see "Acting for each user") |
| `idempotency:` | `header:` (where the key goes), `window:` (how long the provider keeps a key; required), `max_length:`, the provider's error codes `in_use:` (the key's first request is still running), `reused:` (the key came with a different request) and `new_key_on:` (the first attempt failed and the key cannot be used again), and `replays_failures: true` (default false) for a provider that replays a failed answer under the same key. Left out when the API takes no key |
| `error_code:` | Where the provider's error code is in a failed response |
| `webhooks:` | `check:` (the signature scheme), `event_id:`, `event_type:`, `occurred_at:` where the body says when the event happened, `header:` for `SignatureScheme.hmac_sha256`, and `handshake:` where the provider needs one |
| `polls:` | Operations the runtime calls on a schedule, each with a default interval and the event emitted when the answer changes |
| `erasure:` | The operation that deletes a person at the provider and the key it takes (`by:`) |
| `max_concurrent:` | How many of its calls run at once per app instance |
| `settings:` | Typed values the app gives on the connection (`message_stream:`) |
| `imported:` | The description it was imported from, `file:`, that file's digest, `digest:`, and `reviewed:`, the package's types an agent has reviewed against the provider's documentation |

**Where the API is comes from exactly one place:**

- `hosts:` when it is on the internet;
- `runs:` when Jadpo runs it;
- neither, when the app gives `host:` on the connection: another Jadpo
  app, a self-hosted product, or a service on Workers.

A connection's `host:` beside `runs:` means the service is already
running there, so Jadpo doesn't start it. A package with fixed `hosts:`
refuses a `host:`.

```jadpo
// packages/acme_couriers/v1/provider.jadpo
/// Acme Couriers' booking and tracking API, written from its HTML docs.
provider {
    protocol: Protocol.json_http
    hosts: {
        live: "https://api.acme-couriers.example"
        sandbox: "https://sandbox.acme-couriers.example"
    }
    credential: CredentialScheme.bearer
    idempotency: {
        header: "Idempotency-Key"
        window: 24h
        max_length: 255
        in_use: "idempotency_key_in_use"
        reused: "idempotency_key_reused"
    }
    error_code: "error.code"
}

// packages/acme_couriers/v1/resources/bookings.jadpo
public resource bookings {
    create: {
        method: HttpMethod.post
        path: "/v1/bookings"
        input: CreateBooking
        output: Booking
        changes: true
        idempotent: true
        failures: [SlotTaken]
        source: PackageSource.written
        docs: "https://docs.acme-couriers.example/api#create-booking"
    }
}

// packages/acme_couriers/v1/types/create_booking.jadpo
public type CreateBooking = Object {
    postcode: Text
    slot: Text
}

// packages/acme_couriers/v1/types/booking.jadpo
public type Booking = Object {
    id: Text
    collect_at: Instant {
        wire: Wire.unix_seconds
    }
}

// packages/acme_couriers/v1/failures/slot_taken.jadpo
public type SlotTaken = Failure {
    kind: FailureKind.conflict
    message: "The collection slot is no longer available."
}
```

`idempotent: true` on an operation means it is safe to send again: with
the package's `idempotency:` block, because it takes the key; without
one, because repeating it as it is changes nothing (a `PUT` that
replaces, a `DELETE`). An operation of a connection whose credential is
an OAuth grant also names the scope it needs, `scope: "calendar.events"`.

Inside the package, its names are written bare. An app names them through
the package: `AcmeCouriers.Booking`, `AcmeCouriers.SlotTaken`. That holds
in a `match` arm too (`AcmeCouriers.SlotTaken -> ...`), and for an enum's
values, `AcmeCouriers.Window.morning`.

A failure's code, `slot_taken` from its name, is the provider's error
code. Jadpo reads it from the place in the response that `error_code:`
names.

## What is public

- **`public` marks the package's API**, as it marks what a domain offers
  other domains. A `public resource` and a `public action` can be called
  through the connection. Everything else can be called only by the
  package's own actions, subscribers and polls. As in an app, `public`
  goes on a library, never on a function: every function of a
  `public library` is public, and internal helpers such as decoders live
  in a library without `public`
  ([Project layout and modules](project-layout-and-modules.md)).
  Internal declarations may change freely between versions. An importer
  marks every resource public.
- **Everything reachable from the public surface is public**, and every
  compile checks it. A failure a public library's function `fails` with,
  a provider operation's `failures:` and the types in a public signature
  must themselves be public. Internal failures are mapped or handled
  before they leave a public declaration. A domain's public actions,
  queries and libraries follow the same rule.
- **A package's resources and actions share the connection's namespace**
  (`stripe.customers`, `stripe.start_subscription`), so a clash between
  them is an error in the package.
- **Package events are public or not.** The app may subscribe to a
  `public` event; one without `public` reaches only the package's own
  subscribers, so a raw webhook a package enriches can stay internal while
  its enriched event is public. An importer marks webhook events public;
  subscribing to a package's non-public event is an error.

## No presets, no dependencies

**Built-in drivers are configured in `app.jadpo`, never packaged.** A
driver is native code in Jadpo's runtime that speaks one protocol.
PostgreSQL, Redis, S3, SMTP and OpenID Connect are drivers. A vendor that
speaks one of them is that driver with the vendor's settings: Cloudflare
R2 is `driver: Driver.s3` with its host and `region: "auto"`, Neon is the
PostgreSQL driver with its pooling mode, and Gmail is the SMTP driver
with its host and port. A vendor with storage and an API is a driver
connection plus a package. A driver for one vendor comes in a Jadpo
release only where a flow needs native code.

```jadpo
// app.jadpo
avatars: {
    driver: Driver.s3
    host: config.r2_host
    region: "auto"
    bucket: "avatars"
    credential: {
        key_id: config.r2_key_id
        secret: config.r2_secret
    }
}
```

- **Each driver's page lists the settings for common services**, so an
  agent looks them up rather than guessing: R2, Backblaze B2,
  DigitalOcean Spaces, MinIO and Amazon S3; Neon, Supabase and Amazon
  RDS; Gmail, Microsoft 365 and Amazon SES's SMTP; Google and Microsoft.
  Each is a working connection, checked with the pages' other examples.
  `jadpo check` warns when it recognises a host that is missing one of
  its settings.
- **Sign-in providers are connections.** Google, Microsoft and other
  OpenID Connect providers are `google: { driver: Driver.oidc  issuer:
  client_id:  credential: }` in `connections:`, named by
  `sign_in: { providers: [google, github] }`
  ([Identity](identity.md)). GitHub and Apple aren't plain OpenID
  Connect, so they are the built-in `Driver.github` and `Driver.apple`.

**Packages have no dependencies**: a package can't require another.

- The standard library holds the shared vocabulary, such as money,
  countries, currencies and phone numbers, which is what packages
  elsewhere mostly depend on each other for.
- Anything else is copied. A package that needs another's code includes a
  copy, checked and tested by the publishing bar like the rest of it.
- Two packages' types for the same thing are converted at the call,
  usually in one line.
- Installing a package is one folder and one line in the lock.
