A Package often builds on another. Acme’s support Package apologizes to
a customer with a credit, so it imports the billing Package and doesn’t
call Stripe again. A Package that reads a web page imports the curated
Jina Package. The build has to know where each import comes from. The
Blueprint needs rules for every Package in the chain, including the ones
the program doesn’t import directly, and add-package writes them.
This guide shows you how to make a Package import another, and what the
dependency adds to the Blueprint. The example is @acme/support, which
imports @acme/billing. Substitute your Packages.
Start with the billing Package from Export a function, including its Stripe implementation. The final run needs a Stripe test-mode key and a customer in the same test account.
Import it
Section titled “Import it”@acme/support is the second Package of the project, added with
submilli build new @acme/support packages/support, as shown in
Start a project.
Its one operation credits the customer through the billing Package:
import { applyCredit } from "@acme/billing";
/** * Apologize to a customer with a goodwill credit, and say so in dollars. * @param customerId The customer's id in the billing system. * @returns A sentence saying how much was credited. */export function apologize(customerId: string): string { const credit = applyCredit(customerId, 1500); return `credited $${(credit.amount / 100).toString()}`;}An import submilli.toml doesn’t declare stops the build:
submilli build check -p @acme/supporterror: package `@acme/billing` not found --> packages/support/src/lib.ts:1:29 |1 | import { applyCredit } from "@acme/billing"; | ^^^^^^^^^^^^^^^Declare it
Section titled “Declare it”| The dependency is | Declare it |
|---|---|
| Another Package in the project | In the Package’s dependencies |
| A Package in the local store | There, and in [dependencies] with its version |
| A Package in a GitHub repository | There, and in [dependencies] as { github = "github.com/org/repo", rev = "<commit>" } |
| A Package in a private GitHub repository | The same, and whoever builds needs a GitHub token that can read it |
The billing Package is a sibling, so one line in the support Package’s block declares it:
[[package]]name = "@acme/support"version = "0.1.0"description = "Goodwill credits through Acme's billing package."path = "packages/support"dependencies = ["@acme/billing"]submilli build check -p @acme/supportchecked @acme/billing v0.1.0checked @acme/support v0.1.0-p builds the Package and the siblings it depends on, in order. A
Package from the local store or from GitHub is declared at the top of
submilli.toml as well, and named in the Package’s list the same way:
The following fragment illustrates those other sources. Keep the sibling-only
declaration for this guide. To use the fragment in your own project, install
@submilli/jina v0.1.0 in the local store and replace the CRM repository and
commit with your own.
[dependencies]"@submilli/jina" = "0.1.0""@acme/crm" = { github = "github.com/acme/crm-package", rev = "9c1f2e4a7d3b06e15a8c4f2d7b9e1a3c5f7d9b2e" }
[[package]]name = "@acme/support"version = "0.1.0"description = "Goodwill credits through Acme's billing package."path = "packages/support"dependencies = ["@acme/billing", "@submilli/jina", "@acme/crm"]A GitHub dependency is fetched into the local store by the build, which
records the commits it used in submilli.lock. rev is the full
40-character commit SHA. A branch, tag, or short SHA is refused. A private
one, such as @acme/crm above, is fetched with the GitHub token of whoever
builds or installs. That is your token on your machine
(submilli github authenticate) and the server’s token on a server, as
Install private
Packages explains. The token needs
Contents: Read-only on every private repository in the dependencies,
including the ones your dependencies depend on.
What it adds to the Blueprint
Section titled “What it adds to the Blueprint”What a Package uses of another shows up in what it requires. The build derived this for the support Package:
namespace: acmeprovides: []requires:- capability: acme.com/credits.applyA Blueprint grants that to @acme/support as it would to a program. The
billing Package makes its own calls, as the caller @acme/billing,
so it needs rules too, and so does the secret it reads. add-package
adds rules for the chain. First publish the support Package and its sibling
dependency to the local store. build check installs neither:
submilli build publish-local -p @acme/supportinstalled @acme/billing v0.1.0 -> …/packages/@acme/billinginstalled @acme/support v0.1.0 -> …/packages/@acme/supportThen create the Blueprint and add the Package:
submilli blueprint init supportsubmilli blueprint add-package @acme/support --no-capabilities✓ created blueprint.yaml (name: support)warning: blueprint.yaml: package `@acme/billing` requires secret `BILLING_API_KEY`, but `secrets:` does not declare it✓ added @acme/support to blueprint.yaml added 1 rules to caller `@acme/support` (default allow): allow acme.com/credits.apply✓ added caller rules for @acme/billing, a dependency of @acme/support added 3 rules to caller `@acme/billing` (default allow): allow http.get (filter: host == "api.stripe.com") allow http.post (filter: host == "api.stripe.com") allow secrets.get (filter: name == "BILLING_API_KEY")Each Package in the chain gets its own caller list from what it
requires, but only the Package you named is listed under packages:,
the Packages a program may import. A program can credit a customer only
through apologize, which fixes the amount. Declare the secret the
warning names, and put your Stripe test-mode key in the store if it isn’t
there yet:
submilli blueprint secret add BILLING_API_KEY --store billing_api_keysubmilli secret put billing_api_key✓ declared secret 'BILLING_API_KEY' (store: billing_api_key) in blueprint.yamlValue for 'billing_api_key': [hidden]Stored secret 'billing_api_key'Create apology.ts. Replace the customer id with a customer from the
Stripe test account that owns your key. Each run applies a $15 test credit:
import { apologize } from "@acme/support";
function main(): string { return apologize("cus_VMQR3azuTWVAWs");}submilli run --blueprint blueprint.yaml apology.tscredited $15The program called the support Package, the support Package called the billing Package, and the billing Package called Stripe, each under its own rules. The complete Blueprint:
kind: blueprintname: supportsecrets: BILLING_API_KEY: store: billing_api_keypackages:- '@acme/support'default: denypermissions: '@acme/billing': - capability: http.get filter: host == "api.stripe.com" action: allow - capability: http.post filter: host == "api.stripe.com" action: allow - capability: secrets.get filter: name == "BILLING_API_KEY" action: allow '@acme/support': - capability: acme.com/credits.apply action: allow main: []