Skip to content

Package Manager

tulpar pkg is the built-in package manager for Tulpar. It manages project dependencies via a small TOML manifest (tulpar.toml) and writes a deterministic lockfile (tulpar.lock) on every install so your tulpar_modules/ tree is reproducible across machines.

The official package registry lives at pkg.tulparlang.dev — point [registry] url there in your manifest to install community packages.

Terminal window
# Inside an empty project directory
tulpar pkg init my-api
# Add a local-path dependency (development)
tulpar pkg add greeter@path:../greeter
# Add a single-file URL dependency
tulpar pkg add lodash@url:http://my-cdn/lodash.tpr
# Add a semver-style registry dependency (requires [registry] url in tulpar.toml)
tulpar pkg add wings@1.2.3
# Vendor everything into ./tulpar_modules
tulpar pkg install

After tulpar pkg install, the consumer can import "greeter" and the runtime resolves it from tulpar_modules/greeter/greeter.tpr.

tulpar.toml is a deliberately tiny TOML subset — string values only, top-level keys plus [registry] and [dependencies] tables.

name = "my-api"
version = "0.1.0"
description = "Tulpar HTTP API example"
author = "Hamza"
license = "MIT"
[registry]
url = "https://api.pkg.tulparlang.dev" # the API base; this is also the default
[dependencies]
wings = "1.2.3" # registry
greeter = "path:../greeter" # local sibling dir
lodash = "url:http://cdn/lodash.tpr" # single-file URL
FormMeaning
path:./local/dirRecursively copy *.tpr from a local directory.
url:http://example.com/x.tprPlain HTTP fetch of a single .tpr file.
1.2.3Exact version from the registry.
^1.2.3 / ~1.2.3Semver range — caret (within major) / tilde (within minor).
>=1.0,<2.0 / * / latestComparator/compound range, or highest published version.

For an exact version Tulpar hits the registry directly. For a range (^, ~, a </>/= comparator, a comma-compound, or */latest) it first fetches the package’s published versions from <registry>/v1/packages/<name>, picks the highest one that satisfies the range, then downloads <registry>/v1/packages/<name>/versions/<version>/source.

A registry version spec requires a registry URL — [registry] url = "..." in the manifest, the --registry flag, or the TULPAR_REGISTRY env var (default https://api.pkg.tulparlang.dev). https:// URLs require Tulpar to be built with OpenSSL (pacman -S mingw-w64-x86_64-openssl on MSYS2).

After every successful pkg install, Tulpar writes tulpar.lock next to your manifest:

# tulpar.lock — auto-generated by `tulpar pkg install`.
# DO NOT EDIT. Commit alongside tulpar.toml so re-installs are reproducible.
[resolved]
wings = "https://api.pkg.tulparlang.dev/v1/packages/wings/versions/1.2.3/source"
greeter = "path:../greeter"
lodash = "url:http://cdn/lodash.tpr"

The lockfile records the fully resolved URL or path plus a SHA-256 of the downloaded bytes for each dependency, so a re-install on another machine fetches the exact same bytes — even if the registry’s latest pointer moves. A re-install that finds the same URL serving a different SHA-256 than the lockfile records is refused rather than silently overwritten.

When the AOT compiler sees import "name", it tries (in order):

  1. The embedded standard library (wings, router, http_client, orm, test, …).
  2. A bundle-local sibling — <importing-file's-dir>/name.tpr — so a multi-file package’s internal imports resolve to its own files.
  3. The literal path ./name.
  4. The literal path with .tpr appended (./name.tpr).
  5. ./tulpar_modules/<name>/<name>.tpr — the vendored entry-point convention.
  6. ./tulpar_modules/<name>.tpr — single-file vendor.

That ordering means the embedded stdlib names (wings, orm, …) always win — you can’t shadow wings with a local wings.tpr — and a vendored package in tulpar_modules/ is the fallback for everything else.

CommandEffect
tulpar pkg init [name]Create a starter tulpar.toml (refuses to overwrite).
tulpar pkg listPrint package metadata + dependencies.
tulpar pkg add <name>[@<ver-spec>]Add or update a manifest dependency line.
tulpar pkg remove <name>Drop a manifest dependency line.
tulpar pkg installVendor every dep into tulpar_modules/ + write tulpar.lock.
tulpar pkg search <query>Search the registry catalog by name/description.
tulpar pkg info <name>Print a package’s registry metadata + published versions.
tulpar pkg publish [--dry-run]Bundle the project’s sources and POST them to the registry.

tulpar pkg publish bundles every .tpr source in your project and uploads it to the registry under your manifest’s name + version (both required). Authentication is a bearer token — pass --token <tok> or set TULPAR_PUBLISH_TOKEN:

Terminal window
export TULPAR_PUBLISH_TOKEN=""
tulpar pkg publish --dry-run # bundle + show what would ship, no upload
tulpar pkg publish # POST to <registry>/v1/publish

The registry target follows the same resolution as installs (--registry flag → [registry] urlTULPAR_REGISTRY → the default https://api.pkg.tulparlang.dev). --dry-run skips the POST so you can inspect the bundle without a token.

  • Multi-file packages via tar/zip extract from registry URLs.
  • Package signing (the lockfile already records a SHA-256 per dependency).