zynagent-first application framework

Add a build dependency#

This example adds a repository-owned Zig package and makes its module available to application code and tests. The package is declared in the manifest and committed with the application, so it needs no network fetch or undeclared local checkout.

1. Create the package#

Create these files under packages/local_dependency:

// packages/local_dependency/build.zig.zon
.{
    .name = .local_dependency,
    .version = "1.0.0",
    .fingerprint = 0x4d7b0f55d3af6152,
    .minimum_zig_version = "0.16.0",
    .paths = .{ "build.zig", "build.zig.zon", "src" },
}
// packages/local_dependency/build.zig
const std = @import("std");

pub fn build(b: *std.Build) void {
    _ = b.addModule("local_dependency", .{
        .root_source_file = b.path("src/root.zig"),
        .target = b.standardTargetOptions(.{}),
        .optimize = b.standardOptimizeOption(.{}),
    });
}
// packages/local_dependency/src/root.zig
pub const message = "Package loaded";

2. Declare it in the application manifest#

Add this entry inside the existing .dependencies in the application's build.zig.zon, retaining its Zyn dependency and package identity:

// build.zig.zon: dependencies
.local_dependency = .{ .path = "packages/local_dependency" },

Also append "packages" to the existing .paths array so packaging retains the dependency's source. Commit the package files with the manifest.

For an external package instead, use zig fetch --save=NAME URL with a chosen immutable release or commit URL. Commit the .url and content .hash Zig writes; do not invent a hash, track a moving branch, or change unrelated pins. Use that package's actual exported module name in the build configuration.

3. Expose the module through Zyn's build graph#

Replace the generated build.zig delegation with:

// build.zig
const std = @import("std");
const zyn = @import("zyn");

pub fn build(b: *std.Build) void {
    var app = zyn.application(b);
    const dependency = b.dependency("local_dependency", .{
        .target = app.target,
        .optimize = app.optimize,
    });
    app.add_import("local_dependency", dependency.module("local_dependency"));
    _ = app.build();
}

Call app.build() once. app.add_import shares the module with the application, generated test root, and seed module. Keep any application-owned custom build steps. The reserved imports db, zyn-test-app, zyn-test-root, and zyn-test-runtime cannot be replaced.

4. Use and test the import#

// src/actions/dependency_example.zig
const std = @import("std");
const zyn = @import("zyn");
const dependency = @import("local_dependency");

pub fn show(action: *zyn.Action) !zyn.Response {
    return action.json(.{ .message = dependency.message }, .{});
}

test "application receives the dependency import" {
    try std.testing.expectEqualStrings("Package loaded", dependency.message);
}

Add these to the existing route module:

// src/routes.zig: imports
const dependency_example = @import("actions/dependency_example.zig");
// src/routes.zig: table entries
.get("/dependency-example", dependency_example.show, &.{}),

5. Verify#

zyn test --list
zyn check
zyn build
zyn dev --http

The test listing must contain application receives the dependency import. In another terminal:

curl -i http://localhost:7532/dependency-example

Expect 200 and {"message":"Package loaded"}. Use the reported port if it differs. If seed code also uses the module, exercise zyn db seed against a disposable database. See build for adding custom dependencies between Zyn's standard build steps.