External servicesUsing packages
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:
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,providemappings, actions, subscribers and services. Who may call one is still decided by the connection'sdomains:list. - A
useline shows where a name comes from.use packages/postcodesis always installed code, anduse shared/providers/acme_couriersis 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 changesjadpo.lock, the folder and theuselines, 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:
/// 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 } }}
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)}
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
- Some provider brand names are reserved.
Stripe,TwilioandGitHubare kept for official packages, none of which exists yet. An app's own integration can't take one (package.reserved_name), soStripein code can only ever mean the official package. Name yours something else, such asStripeApi.v1. - 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 (by hand, since
jadpo renameisn't built yet). - 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.
- Registry names are scoped by publisher: a package's identity in the
registry is
@publisher/name, such as@acme/postcodes, and Jadpo's official packages are@jadpo/…. In code a package keeps its own name and version label (Postcodes.v2), so two publishers' packages with one code name can't be installed in the same app.jadpo package addtakes the scoped name, and the lock'sregistrysource records it (The registry protocol). - A package is named in UpperCamelCase with its own version label, such
as
Stripe.v2024_06_20orPostcodes.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 aee2e8f.