# Packages
A Deft **package** is a directory of modules with a `package.dft`
manifest. Packages are the unit of distribution: install via
`pkg/install`, reference by `owner/name`, import from any module.
## Package Identity
Every package has a canonical **`owner/name`** id (e.g.
`deft/editor`). Owners are globally unique on the registry; `deft`
is reserved for official packages. The id is passed as a single
string to every registry operation:
[pkg/install "deft/editor"] [pkg/info "deft/editor"] [pkg/uninstall "deft/editor"]
## Importing Package Modules
The `owner/name` id is also the import path prefix. Packages under
a `:pkgs` search root (or installed by `pkg/install`) are mounted
automatically as `owner/name`, so modules import without any
configuration:
import "deft/ui/components/button" as button
Pin an exact version with `@version` after the name:
import "deft/ui@0.1.0/components/button" as button
- Unpinned `owner/name/...` resolves through the `:pkgs` roots in
listed order — the first root providing the package wins. For
installed packages the version comes from the `latest` pointer
written by `pkg/install`, else the highest installed semver.
- `owner/name@latest/...` is an explicit alias for unpinned.
- A pin that matches a dev checkout's manifest version resolves to
the dev tree (same root-order rules); otherwise it matches an
installed version directory.
- A missing pin errors with the list of available versions; a bare
package prefix (e.g. `ui/...`) errors with a
`did you mean "owner/name/..."?` hint.
Dev checkouts, installed trees, and the legacy versioned layouts
are all honoured — the difference between "working on a package"
and "using a package" is only which root provides it, not the
import syntax.
## The Manifest (`package.dft`)
The manifest declares the package's identity, version, dependencies,
and entry point:
def manifest %Package{ :owner "alice" :name "math-lib" :version "1.0.0" :description "Personal math helpers" :entry "src/main.dft" :deps @{ %Dep{ :owner "deft" :name "stdext" :version "0.2.0" } | } :capabilities %PackageCapabilities{ :ffi false :tui false | } }
`[pkg/read-manifest "path/to/dir"]` reads and validates a manifest,
returning `%Package{ ... }` or `%Err{ ... }`.
## `pkg/*` Stdlib functions
| Function | Returns | Purpose |
|---|---|---|
| `[pkg/set-registry $url]` | nil | Set registry URL (nil resets to local) |
| `[pkg/get-registry]` | url or nil | Current registry |
| `[pkg/install "owner/name" ?version]` | summary map or `%Err` | Install |
| `[pkg/install-bytes "owner/name" $ver $bytes]` | summary map | Install pre-fetched tarball |
| `[pkg/uninstall "owner/name"]` | bool or `%Err` | Uninstall |
| `[pkg/list]` | list | Packages across all search roots |
| `[pkg/list-paths]` | list of strings | Search roots |
| `[pkg/available]` | list | Packages in the current registry |
| `[pkg/info "owner/name" ?version]` | map | Package detail |
| `[pkg/fetch "owner/name" ?version]` | bytes or Future | Download tarball |
| `[pkg/read-manifest $dir]` | `%Package` or `%Err` | Read manifest |
| `[pkg/mount $export-path $dir]` | nil | Register an import mount |
## Install Locations
Installs always target the first search root (canonical:
`.deft/packages/`), producing the owner-scoped versioned layout:
```
.deft/packages////package.dft
.deft/packages////src/...
```
`pkg/install` also writes a `latest` text pointer beside the
version directories (`.deft/packages///latest`) so
unpinned `owner/name` imports have a defined target.
## Search Paths
At startup every `:pkgs` root is scanned and each package found is
auto-mounted as `owner/name` (plus `owner/name@version` pins and
the `@latest` alias). Roots are scanned in listed order — the
first root providing a package wins, so a dev tree listed before
`.deft/packages/` shadows the installed copies. Explicit prelude
`:mounts` entries always win over the auto-scan.
Use `[pkg/list-paths]` to see the active search roots:
[pkg/list-paths]
# => @{ ".deft/packages" }
All on-disk layouts are honoured:
- Flat (dev): `//package.dft`
- Legacy versioned: `///package.dft`
- Owner-scoped dev: `///package.dft`
- Owner-scoped installed: `////package.dft`
## Registries
Two registry transports:
- **`dir://`** (default) — tarballs live under
`virt-pkg/packages///.tar.gz`. A `latest`
symlink or text file is honoured. Good for local development and
airgapped environments.
- **`http(s)://`** — tarballs fetched from
`/packages///.tar.gz`. Set the URL
with `[pkg/set-registry $url]` or the `--url` CLI flag. The
`latest` file is honoured.
pkg/set-registry "https://packages.example.com" echo [pkg/get-registry] # => https://packages.example.com pkg/set-registry nil # reset to local virt-pkg
The active registry URL is persisted in `prelude.dft`'s `:registry`
field, so subsequent `deft` invocations pick it up automatically.
## Installing a Package
def result [pkg/install "deft/editor"] # => %{ :owner "deft" :name "editor" :version "1.5.0" :path ".deft/packages/deft/editor/1.5.0" } def specific [pkg/install "deft/editor" "1.4.0"]
`pkg/install` is **synchronous** — it blocks until the fetch and
extract complete. Failures return `%Err{ :message ... }`.
### Manual fetch + install
If you've already fetched the tarball bytes yourself (e.g. through
a proxy), use `pkg/install-bytes`:
set bytes [my-fetch "https://.../editor-1.5.0.tar.gz"] pkg/install-bytes "deft/editor" "1.5.0" $bytes
## Mounting a Package Manually
Package auto-discovery covers everything under the `:pkgs` roots.
`[pkg/mount $name $dir]` is the escape hatch for directories
outside them (or for pointing a name at a specific checkout):
pkg/mount "alice/mylib" "/home/alice/dev/mylib" import "alice/mylib/helpers" as helpers
Mount names may be multi-segment (`alice/mylib`) — imports resolve
the longest matching mount prefix first.
## Creating a Package
1. Lay out the directory:
```
my-package/
├── package.dft
└── src/
├── main.dft
└── helpers.dft
```
2. Write the manifest (`package.dft`):
def manifest %Package{ :owner "alice" :name "my-package" :version "0.1.0" :description "An example package." :entry "src/main.dft" :deps @{} | }
3. (Optional) Test it locally by adding the parent directory to
search paths and importing:
pkg/mount "alice/my-package" "/path/to/my-package" import "alice/my-package/src/main" as mp
4. Publish to your registry (tarball the directory; upload to your
HTTP registry or place under `virt-pkg/packages///`).
## Capabilities
A package can declare capabilities it needs (FFI, TUI, etc.) in the
manifest:
:capabilities %PackageCapabilities{ :ffi true :tui false | }
The runtime uses these to surface compatibility warnings when a
package is loaded into a runtime missing a required capability.
## Quick Reference
| Form | Purpose |
|---|---|
| `[pkg/install "owner/name" ?ver]` | Install |
| `[pkg/uninstall "owner/name"]` | Uninstall |
| `[pkg/list]` | List installed |
| `[pkg/available]` | List in registry |
| `[pkg/info "owner/name" ?ver]` | Show detail |
| `[pkg/set-registry $url]` | Switch registry |
| `[pkg/fetch "owner/name" ?ver]` | Download tarball |
| `[pkg/mount $export-path $dir]` | Register a mount |
| `[pkg/read-manifest $dir]` | Read a `package.dft` |