npm Packages for Koru
Koru Gets a Package Manager
Today we shipped something that makes Koru feel like a real language: npm package support.
You can now:
- Declare npm dependencies directly in your Koru source
- Install them with
koruc app.kz i - Import and use them like any other module
Here’s what it looks like:
~import "$std/package"
~import "$koru/sqlite3"
~std.package:requires.npm { "@korulang/sqlite3": "^0.0.1" }
~koru.sqlite3:open(path: ":memory:")
| db conn |> koru.sqlite3:close(conn)
| closed |> std.io:print.ln("It works!")
| err e |> std.io:print.ln("Error: " ++ e.msg) That’s it. Your dependencies live in your source code, not in a separate manifest file you have to keep in sync.
The First Package: @korulang/sqlite3
We published @korulang/sqlite3 to npm as a proof of concept. It’s a real SQLite binding with phantom type obligations:
~pub event open { path: []const u8 }
| db { conn: *Connection[opened!] }
| err { code: i32, msg: []const u8 } The [opened!] phantom annotation means: “this connection carries an opened obligation that must be discharged.” You can’t forget to close it - the compiler will catch you.
How It Works
1. Project Setup
Run koruc init in an empty directory:
$ koruc init This creates koru.json with path aliases:
{
"name": "my-project",
"version": "0.1.0",
"entry": "app.kz",
"paths": {
"node": "./node_modules",
"koru": "./node_modules/@korulang"
}
} The $koru alias maps to ./node_modules/@korulang, so ~import "$koru/sqlite3" resolves to ./node_modules/@korulang/sqlite3/index.kz.
2. Declare Dependencies
In your Koru source, declare what you need:
~import "$std/package"
~std.package:requires.npm { "@korulang/sqlite3": "^0.0.1" } This is a compile-time declaration. It doesn’t execute at runtime - it tells the compiler what packages your code needs.
3. Install
$ koruc app.kz i
Found 1 npm package declaration(s)
✓ Generated package.json
✓ npm install complete The compiler walks your AST, collects all requires.npm declarations, generates package.json, and runs npm install.
4. Use
Now you can import the package:
~import "$koru/sqlite3"
~koru.sqlite3:open(path: "test.db")
| db d |> ... The imported module’s events become available under the koru.sqlite3 namespace.
Why npm?
We considered building a custom package registry, but npm already exists and works well. By using npm:
- Package authors get familiar tooling (
npm publish) - Users get familiar installation (
npm installunder the hood) - We get versioning, namespacing (
@korulang/*), and distribution for free
Koru packages are just npm packages with .kz files instead of .js files.
What’s Next
This is the foundation. Now we can build:
- More stdlib packages - graphics, networking, parsing
- Third-party ecosystem - anyone can publish
@username/package - Native dependency handling -
~std.build:requiresfor linking C libraries
The @korulang/sqlite3 package already demonstrates native dependencies:
~std.build:requires {
exe.linkSystemLibrary("sqlite3");
exe.linkLibC();
} This tells the Zig build system to link against SQLite. Build requirements and package requirements work together.
Try It
# Get Koru (if you haven't)
# ... installation instructions ...
# Create a project
mkdir my-project && cd my-project
koruc init
# Edit app.kz to use sqlite3
# ... add the code above ...
# Install and run
koruc app.kz i
koruc run app.kz The package ecosystem is live. Go build something.