# 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/`.

Here is the smallest useful package: its index, one type, one library and a
test beside it. An app holds its own package like this in
`shared/providers/postcodes/v2/`. Published, the same files are a repository
of their own, and an app that installs it finds them in
`packages/postcodes/v2/`. `text.upper` and `text.trim` are built in.

`shared/providers/postcodes/v2/package.jadpo`:

```jadpo
/// UK postcodes: tidying them as people write them.
package Postcodes.v2 {
    licence: Licence.mit
    jadpo: "1.0"
}
```

`shared/providers/postcodes/v2/types/postcode.jadpo`:

```jadpo
/// A UK postcode.
public type Postcode = Text {}
```

`shared/providers/postcodes/v2/libraries/uk.jadpo`:

```jadpo
/// Tidying UK postcodes.
public library uk {
    /// A postcode in capitals, with the spaces around it left out.
    function tidy(postcode: Postcode) -> Postcode {
        return Postcode(text.upper(text.trim(postcode)))
    }
}
```

`shared/providers/postcodes/v2/libraries/uk.test.jadpo`:

```jadpo
test "a postcode is tidied" {
    assert uk.tidy(" sw1a 1aa ") == "SW1A 1AA"
}
```

A library package in full:

```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 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](/docs/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 its keys. `licence:` names its licence, `jadpo:`
the lowest Jadpo version it says it needs, and `publisher:` who publishes it in
the registry, as a handle: `publisher: "@acme"` for `@acme/postcodes`. Nothing
compares `jadpo:` with the running Jadpo yet ([How the package format is
read](/docs/how-the-package-format-is-read.md)).

A package only used by path or from git may leave `publisher:` out, since
nothing is published and no one is named. The publishing bar asks for it
([What publishing requires](/docs/developing-and-publishing-a-package.md#what-publishing-requires)).

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
([`publish.licence_proprietary`](/docs/errors/publish.licence_proprietary.md)). 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 {
    publisher: "@acme"
    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:` | Not built yet: 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 ([Services](/docs/running-a-service-in-a-container.md#services)). It isn't the `runs:` on a deployable |
| `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, which isn't built yet;
- 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](/docs/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, SMTP and OpenID Connect are drivers, and Redis and S3 will be. A
vendor that speaks one of them is that driver with the vendor's settings: Neon
is the PostgreSQL driver with its pooling mode, Gmail is the SMTP driver with
its host and port, and Cloudflare R2 will be `driver: Driver.s3` with its host
and `region: "auto"`. 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
    }
}
```

`Driver.s3` isn't built yet, so this is its design and doesn't compile today.

- **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, microsoft] }`. Google's and Microsoft's
  settings are in [Providers](/docs/checking-the-person-sign-in.md#providers). GitHub and Apple
  aren't plain OpenID Connect, so they are the built-in `Driver.github`
  and `Driver.apple`, which aren't built yet.

**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.
