Search documentation

Search all pages, headings and text, in the current package or topic or in all documentation.

Receiving packages

The managed source transfer, the one supported way to bring a package into your repository and to update it.

A package arrives in your repository as source: its folder is copied unchanged into packages/<name>, and a provenance record says exactly which bytes came from which source state. There is no registry and no built artefact. Your installation, tests and builds never need a path to the repository the package came from.

What you need#

  • A pnpm workspace whose packages include packages/*.

  • Node.js 26 or later.

  • A .gitattributes entry that keeps the transferred bytes exactly as they are, so a checkout on Windows does not convert line ends:

    Text
    packages/** -text
  • A TypeScript-aware build: packages ship TypeScript source (exports point to src/). Vite compiles a linked workspace package as it is; for Next.js, list the package in transpilePackages.

The two sides#

SideToolWhat it does
the source repositoryrps-transfercomposes a bundle for the packages you request: their folders, what they need, provenance
your repositoryrps-applythe applier: plans and applies a bundle in your repository, and checks your managed state

The applier is two files, rps-apply.mjs and rps-contract.mjs, that you keep in your own repository under tools/rps/. Every bundle carries a reference copy of them under tooling/, so you can take them from the first bundle you receive. Review them before you commit them.

Where the bundles come from today

Every package in the catalogue is maintained in Foundation, which composes its bundles with pnpm rps:transfer bundle --products <product> --owners <owner> --channel <channel> --out <folder>. A product (such as active-ui, active-grid or active-i18n) brings the owners it needs along; an owner (active-format, active-virtual, active-helpers, eslint-config) is requested on its own with --owners. The lint preset never comes along by itself: add --owners eslint-config when you want it in your set. typescript-config is not transferred this way; see the development basis. A bundle always holds your whole managed set: a package left out of it is removed by the update. Ask the maintainer for a bundle; you never need access to the source repository yourself.

Receiving a package, step by step#

From the root of your repository, in this order:

  1. Receive. Plan the bundle, then apply exactly that plan:

    Terminal
    # The ordered decision. Changes nothing; prints the operations and a digest.node tools/rps/rps-apply.mjs plan --bundle <bundle-folder> --development
    # Apply exactly that plan.node tools/rps/rps-apply.mjs apply --bundle <bundle-folder> --expect-plan <digest> --development

    The package now sits in packages/<name>. Do not edit it there.

  2. Declare the package where you use it, for example in your app's package.json:

    JSON
    {  "dependencies": {    "@workspace/active-format": "workspace:*"  }}
  3. Install:

    Terminal
    pnpm install
  4. Verify that the app resolves the managed copy:

    Terminal
    node tools/rps/rps-apply.mjs verify-install --from <app-folder>

    The output must name every package you declared, each as current:

    Text
      current      @workspace/active-format 1.0.0 from apps/web (workspace link: packages/active-format)

    verify-install checks only the managed packages the app declares. If it lists no package, it checked nothing: declare the package (step 2) and install (step 3) first.

  5. Use the package; see its Getting started page, for example active-format.

Channels#

  • release: carries only recorded releases, each with exactly its recorded content.
  • development: a visibly marked trial. The applier refuses it unless you pass --development, and the provenance records it. A development transfer is never a release.

Updating#

An update brings your whole managed set to one new source state: every managed package moves together, and the plan shows which files are kept, updated, added or deleted. You decide when you update. A release in the source repository never changes your repository by itself.

Request the whole set every time. Your repository has one provenance record from one source. A managed package that is missing from the next bundle is deleted by that update. Ask for a bundle of every package in the request of your .rps/provenance.json (plus any you want to add), and read the plan before you apply it: its line Requested packages kept: …; added: …; removed: … must say removed: - unless you mean to remove a package.

Afterwards, repeat steps 3 and 4: install, and verify every app that uses the managed packages.

Local changes and conflicts#

Never edit a managed file. A change in a managed folder blocks the next update:

  • RPS-E4: a managed file differs from its recorded hash;
  • RPS-E5: a folder the bundle would add already exists and is not managed;
  • RPS-E16 and RPS-E17: a managed file is missing or replaced by something else.

The plan names each conflict and changes nothing. Make the improvement in the source repository and receive it with the next transfer. An interrupted application is recovered with rps-apply recover --rollback; rps-apply status shows your managed state, and rps-apply reconcile --bundle <folder> compares your files with a bundle without changing anything.

Your version and the newest version#

Your .rps/provenance.json records the packages, versions, channel and source state you use. The package pages of this site show the version Foundation documents. When the two differ, read the package's changelog between your version and the documented one before you update.

This page as Markdown