Jadpo is experimental and not ready for production. These docs follow the compiler's main branch as of 30 Oct 2026. What's built · Install 0.1.0
jadpo
Reference: External services

External services

Where packages live

On this page

packages/ holds only packages you installed. Code your app's authors wrote lives where the rest of their code lives, whatever kind it is:

text
app.jadpodomains/    shipping/libraries/...           the app's helpers for one domainshared/    libraries/...                    the app's helpers for several domains    providers/        acme_couriers/v1/            the app's own integration, in the package formatpackages/                            installed only: in jadpo.lock, read-only    stripe/v2024_06_20/    postcodes/v2/jadpo.lock
  • Installed packages are committed under packages/<name>/<version>/. Agents read them with the tools they already use, reviewers see an upgrade as an ordinary diff (its surface diff included), and CI needs no fetch step. A build never fetches anything.
  • Installed packages are read-only. The compiler refuses one whose files don't match its digest in jadpo.lock. A digest is a fingerprint of the files, so any change to them shows.
  • Your app's own integrations live in shared/providers/<name>/<version>/, in exactly the package format. They can have everything an installed provider can: types, failures, pure functions, decoding, recordings, fakes, provide mappings, actions, subscribers and services. Who may call one is still decided by the connection's domains: list.
  • A use line shows where a name comes from. use packages/postcodes is always installed code, and use shared/providers/acme_couriers is the app's own. The only differences between the two are where the code sits, whether you can edit it and whether the publishing bar applies. So replacing your own integration with an official package later changes jadpo.lock, the folder and the use lines, and not the code that calls it.
  • Publishing moves code out of the app. A package other apps depend on gets its own repository and versions. Every app installs it into packages/, the one it started in included.

Two versions side by side

Two versions of one package can be held at once: a provider during an upgrade, a candidate version while the upgrade plan compiles the app against it, or two library versions while callers move across. Each is its own package in its own folder, both installed or both the app's own. While two are held, every name written through either carries its version, such as Postcodes.v3.UkPostcode. One use line brings in both, because it names the package's folder.

Here the app's own Postcodes is held at v2 and v3 while callers move across:

example
/// Reading UK postcodes.public library uk {    /// Reads a UK postcode as it is written, spaces around it left out.    function standard_form(postcode_text: WrittenPostcode) fails InvalidPostcode -> UkPostcode {        return attempt UkPostcode(text.upper(text.trim(postcode_text))) {            invalid: InvalidPostcode        }    }}
example
use shared/providers/postcodes/// Reads a customer's postcode, as callers did before the upgrade.action read_postcode(    written: Postcodes.v2.WrittenPostcode) fails Postcodes.v2.InvalidPostcode -> Postcodes.v2.UkPostcode {    return attempt Postcodes.v2.uk.standard_form(written)}
example
use shared/providers/postcodes/// Reads a customer's postcode with the next version.action read_typed_postcode(    written: Postcodes.v3.WrittenPostcode) fails Postcodes.v3.InvalidPostcode -> Postcodes.v3.UkPostcode {    return attempt Postcodes.v3.uk.standard_form(written)}

A name without its version while two are held is package.version_needed. Once the old version goes, a name that still carries the version is package.version_unneeded, whose suggested edit drops it. Both are listed under "Package errors".

While two versions of a library package that carries translations are held, the app words each under a key that carries the version, postcodes_v3: { }, as its names do (Jadpo's and packages' text).

Names

  • Provider brand names are reserved. Jadpo ships the list of curated provider names, and an app's own integration cannot take one, so Stripe in code always means the official package.
  • Two packages never share a name in code. Installing a registry package whose name one of the app's own integrations or libraries already uses stops and asks for the app's own to be renamed first (jadpo rename).
  • A package's name can't clash with any other name. Domain, library and standard-library names are unique among themselves, and a package's name joins them. Installing a package refuses one whose name the app already uses for a domain, a library or any declaration.
  • Creating an integration checks the registry and warns when the name is taken.
  • A package is named in UpperCamelCase with its own version label, such as Stripe.v2024_06_20 or Postcodes.v2. Its folder is the name in lower_snake_case with the version below it.

Markdown for agents: /docs/where-packages-live.md.Built from Jadpo main at a707efb.