The Package Manager

Marina's package manager is decentralized: there is no central server and no node_modules. Packages are installed once into a global registry on your machine, and every project uses that copy.

The registry model — like crates.io's storage, not node_modules

A package is a directory (or git repo) with a main.tsm entry file and optional package.json metadata (name, version).
  • Packages live in a global registry at ~/.wave/registry/<name>/<version>/main.tsm
  • Every project on the machine uses the same copy — there is no per-project dependency folder to commit or vendor
  • Because the registry is just a directory on your disk, it works fully offline and can be hosted anywhere: a local folder, a git repository, or a shared filesystem — no central npm-style server required

Accessing the registry

The registry location is configurable via the WAVE_REGISTRY environment variable:

# default location
~/.wave/registry/<name>/<version>/main.tsm

# point wave at a custom registry (e.g. a mounted share or CI cache)
$ export WAVE_REGISTRY=/path/to/your/registry

Initialize a project and inspect what's installed:

$ wave init          # package.json + src/main.tsm
$ wave add net       # std module: informational, nothing to install

Adding packages

wave add accepts a local path or a git URL. It copies the package into the registry and records the dependency in package.json:

# from a local directory
$ wave add ./my-pkg

# from any git host (decentralized — the repo *is* the distribution)
$ wave add https://github.com/u/p.git

# remove it again
$ wave remove p

# verify the manifest + type check the entry point
$ wave check

Example: a project using two packages — one from disk, one from git:

use sample_mod
use sample_mod_another

async function main() {
    let calc = new sample_mod.Calc(10, 20)
    console.log(calc.add())

    let sub = sample_mod.sub.sub(10, 20)
    let x = sample_mod_another.sub_another.access_sub(10, 20)
}

Using packages in code

In code, use my-pkg merges the package's exports behind a namespace: my-pkg.fn(...) calls the package's fn. Module-qualified constructors work too — new sample_mod.Calc(10, 20) — including nested namespaces like sample_mod.sub.sub(10, 20).

Authoring a package

A package is a directory with a main.tsm entry file:

// main.tsm — packages expose functions and classes only
class Calc {
    a: Int32
    b: Int32
}

function sub(a: Int32, b: Int32): Int32 {
    return a - b
}
{
    "name": "sample_mod",
    "version": "0.1.0"
}

Rules & limits

  • Packages expose functions and classes only — no top-level statements, no main
  • No package-on-package dependencies yet (std modules net/http/fs/crypto/sql are allowed)
  • Registry layout is flat: <name>/<version>/ — the same model as Rust's global cargo registry cache