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 amain.tsmentry file and optionalpackage.jsonmetadata (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/sqlare allowed) - Registry layout is flat:
<name>/<version>/— the same model as Rust's global cargo registry cache