# Add a dependency

Source: https://submilli.ai/docs/packages/add-a-dependency.md

Authorship: AI-assisted.

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](https://submilli.ai/docs/packages/export-a-function.md), including its Stripe
implementation. The final run needs a Stripe test-mode key and a customer
in the same test account.

## 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](https://submilli.ai/docs/packages/start-a-project.md#add-a-second-package).
Its one operation credits the customer through the billing Package:

```typescript title="packages/support/src/lib.ts"
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:

```sh
submilli build check -p @acme/support
```

```text
error: package `@acme/billing` not found
 --> packages/support/src/lib.ts:1:29
  |
1 | import { applyCredit } from "@acme/billing";
  |                             ^^^^^^^^^^^^^^^
```

## 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:

```toml title="submilli.toml (fragment)"
[[package]]
name = "@acme/support"
version = "0.1.0"
description = "Goodwill credits through Acme's billing package."
path = "packages/support"
dependencies = ["@acme/billing"]
```

```sh
submilli build check -p @acme/support
```

```text
checked @acme/billing v0.1.0
checked @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.

```toml title="submilli.toml (fragment)"
[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](https://submilli.ai/docs/server/install-private-packages.md) 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

What a Package uses of another shows up in what it requires. The build
derived this for the support Package:

```yaml title="packages/support/capabilities.yaml"
namespace: acme
provides: []
requires:
- capability: acme.com/credits.apply
```

A 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:

```sh
submilli build publish-local -p @acme/support
```

```text
installed @acme/billing v0.1.0 -> …/packages/@acme/billing
installed @acme/support v0.1.0 -> …/packages/@acme/support
```

Then create the Blueprint and add the Package:

```sh
submilli blueprint init support
submilli blueprint add-package @acme/support --no-capabilities
```

```text
✓ 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:

```sh
submilli blueprint secret add BILLING_API_KEY --store billing_api_key
submilli secret put billing_api_key
```

```text
✓ declared secret 'BILLING_API_KEY' (store: billing_api_key) in blueprint.yaml
Value 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:

```typescript title="apology.ts"
import { apologize } from "@acme/support";

function main(): string {
    return apologize("cus_VMQR3azuTWVAWs");
}
```

```sh
submilli run --blueprint blueprint.yaml apology.ts
```

```text
credited $15
```

The 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:

```yaml title="blueprint.yaml"
kind: blueprint
name: support
secrets:
  BILLING_API_KEY:
    store: billing_api_key
packages:
- '@acme/support'
default: deny
permissions:
  '@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: []
```
