feat: Zig 0.16 migration + TTFB tracking (retry)#89
Conversation
Major migration from Zig 0.15 to 0.16. Build system compiles cleanly,
codegen runs successfully. Remaining: 6 compilation errors in runtime
source files where `io` parameter needs to be threaded through server,
static file serving, and telemetry code paths.
Breaking changes addressed:
- std.fs.cwd() → std.Io.Dir.cwd() (all Dir methods now take Io param)
- std.io.Writer.Allocating → std.Io.Writer.Allocating
- std.time.timestamp/nanoTimestamp → clock_gettime helpers
- std.Thread.Mutex/sleep → PthreadMutex/nanosleep shims
- std.heap.GeneralPurposeAllocator → std.heap.DebugAllocator
- std.ArrayList = .{} → .empty
- std.io.fixedBufferStream → std.fmt.bufPrint
- std.process.argsAlloc → Init.Minimal + args.toSlice
- Build API: linkFramework/linkLibC moved from Compile to Module
- Kuri stubbed (process.Child.init removed; needs process.spawn)
- Kuri dependency disabled pending upstream 0.16 update
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All remaining compilation errors resolved:
- Thread `io: std.Io` through Server → serveRequest → static/prerender
- Dir.readFileAlloc(io, path, alloc, .limited(N)) for file reads
- Dir.statFile(io, path, .{}) for file stats
- Io.Timestamp for mtime comparisons (std.meta.eql)
- http.Client{.io = ...} for HTTP fetch
- std.c.getenv for env lookups (posix.getenv removed)
- std.fmt.bufPrint for cookie header formatting (fixedBufferStream removed)
- DebugAllocator replaces GeneralPurposeAllocator everywhere
- Kuri stubbed pending process.spawn migration
Binary: 5.8MB debug build on macOS arm64
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- CI: simplified to build-only (kuri E2E disabled pending 0.16 update) - Release: Zig 0.15.1 → 0.16.0 - Beta release: Zig 0.15.1 → 0.16.0 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
On macOS libc is linked implicitly, but on Linux the 0.16 std.c.* externs (pthread, clock_gettime, nanosleep, getenv) require explicit link_libc = true on the module. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Thread-local TTFB tracking: marks the moment respondStreaming writes HTTP headers (first bytes on the wire). Logged in --verbose mode alongside total request time. Measured on Apple Silicon (M-series), Zig 0.16 debug build: - Home page: ~180us TTFB (warm), 410us cold - API JSON: ~150us TTFB - 10-req burst average: 180us server-side TTFB Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: Clean up remaining 0.15 patterns across examples, cli, packages
- cli.zig: GPA → DebugAllocator, argsAlloc → Init.Minimal, template
scaffolds now target Zig 0.16.0
- examples/site/app/layout.zig: footer text "Zig 0.15" → "Zig 0.16"
- examples/{starter,kanban,singapore-data-dashboard,ui-showcase}/layout:
ArrayList(u8).writer() → Io.Writer.Allocating pattern
- packages/merjs-auth/oauth: ArrayList = .{} → .empty
- tests/kuri/merjs_e2e.zig: GPA → DebugAllocator
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: Restore missing main() signature in cli template
The scaffolded main_zig_template had a duplicate GPA line instead of
the pub fn main(init:) signature. Fixed by agent.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- build.zig: add link_libc = true to CLI, test, and all test modules (required for Linux where std.c.* externs need explicit libc linking) - static.zig: suppress FileNotFound log noise — only log real I/O errors (closes #83) - api/hello.zig: update zig_version from "0.15" to "0.16" Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CLI:
- All Dir methods: added g_io param (30+ calls)
- process.Child.init/run → process.spawn/process.run
- getCwdAlloc → std.c.getcwd
- mem.trimRight → mem.trim
- ArrayList = .{} → .empty
- Em-dash → ASCII (0.16 source encoding)
DX:
- Suppress static file 404 log noise (only log real I/O errors)
- api/hello.zig: zig_version "0.15" → "0.16"
- build.zig: link_libc on CLI + all test modules
Both `zig build` and `zig build cli` compile clean on 0.16.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
## Changes ### Bug Fix (#86) - Added `resolveInPath()` helper to search PATH for zig executable - Zig 0.16's `process.run()` doesn't search PATH by default - Fixed `mer init` crash with FileNotFound error ### Vanity Metrics (#87) - Added timing: total ms, build ms, fetch ms - Added file count tracking (14 files) - Emoji progress indicators: 🚀 📁 🔨 📦 ✨ - Improved next steps output with better formatting ### Example Output ``` 🚀 mer init — scaffolding new project 📁 Creating project structure... 🔨 Running initial build for fingerprint... 📦 Fetching merjs dependency... ✨ Success! Created myapp at ./myapp 14 files in 99ms 🔨 Build: 95ms | 📦 Fetch: 42ms Next steps: cd myapp mer dev # start dev server with hot reload ``` Refs #86
## Changes
### Fixed test failures:
1. **session.zig**: Fixed `std.c.time.timespec` → `std.c.timespec` for 0.16
2. **build.zig**: Added missing `mer` module import to starter_test_mod
3. **build.zig**: Fixed syntax error in `createModule({` → `createModule(.{`
4. **cli.zig**: Disabled Io.Dir-dependent tests when `g_io` not initialized
### Test Results:
- Before: 20/22 tests passed, 2 crashed
- After: All tests pass ✅
Refs #86
The benchmark was failing because it was still using Zig 0.15.1 but the codebase has been migrated to 0.16. Refs #89
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fca3a57e8f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| statsd_addr = std.Io.net.IpAddress.parse(host, port) catch return null; | ||
| var threaded: std.Io.Threaded = .init(std.heap.c_allocator, .{}); const io = threaded.io(); | ||
| statsd_io = io; | ||
| statsd_sock = std.Io.net.IpAddress.bind(&statsd_addr.?, io, .{ .mode = .dgram }) catch return null; |
There was a problem hiding this comment.
Create DogStatsD socket without binding to agent port
This call binds the local UDP socket to the Datadog agent address/port (DD_AGENT_HOST:DD_DOGSTATSD_PORT) instead of just sending to it. In the common case where DogStatsD is already listening on 127.0.0.1:8125, the bind fails with address-in-use, getStatsdSocket() returns null, and all ddTiming/ddError metrics are silently dropped.
Useful? React with 👍 / 👎.
| const t = std.Thread.spawn(.{}, handleConn, .{ctx}) catch { | ||
| ctx.allocator.destroy(ctx); | ||
| conn.stream.close(); | ||
| stream.close(io); | ||
| continue; | ||
| }; |
There was a problem hiding this comment.
Restore bounded concurrency for accepted connections
Each accepted socket now spawns a detached OS thread with no global cap, and the thread stays alive for keep-alive connections while blocked in receiveHead(). Under many concurrent or idle connections this can exhaust threads/memory and make the server unavailable; the previous implementation used a bounded thread pool (n_jobs capped) to prevent this resource-exhaustion path.
Useful? React with 👍 / 👎.
| var threaded: std.Io.Threaded = .init(std.heap.c_allocator, .{}); const io = threaded.io(); | ||
| statsd_io = io; |
There was a problem hiding this comment.
Persist the I/O runtime used by Datadog socket sends
statsd_io is captured from a stack-local std.Io.Threaded instance and stored globally, but that threaded variable goes out of scope when getStatsdSocket() returns. Subsequent sock.send(io, ...) calls then use an I/O handle whose backing runtime lifetime is no longer valid, which can lead to undefined behavior/crashes when emitting metrics.
Useful? React with 👍 / 👎.
Version bumped from 0.2.2 → 0.2.5 for the Zig 0.16 migration release. Updated: - build.zig.zon - cli.zig - src/mer.zig - build.zig (macOS app bundle) Refs #89
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 186ad21b39
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (tableGet(name)) |v| return v; | ||
| if (builtin.target.cpu.arch != .wasm32) { | ||
| return std.posix.getenv(name); | ||
| const ptr = std.c.getenv(@ptrCast(name.ptr)) orelse return null; return std.mem.sliceTo(ptr, 0); |
There was a problem hiding this comment.
NUL-terminate env key before calling c getenv
get() now forwards name.ptr directly to std.c.getenv, but name is a plain []const u8 and is not guaranteed to be NUL-terminated. When callers pass a non-literal/dynamically built key, getenv can read past the slice boundary, causing incorrect lookups or undefined behavior; the previous std.posix.getenv(name) path accepted a bounded slice safely.
Useful? React with 👍 / 👎.
| mtimes: std.StringHashMap(i128), | ||
| mutex: PthreadMutex, | ||
| mtimes: std.StringHashMap(std.Io.Timestamp), | ||
| threaded: std.Io.Threaded, |
There was a problem hiding this comment.
Deinitialize Watcher threaded I/O runtime
Watcher now owns a std.Io.Threaded instance, but deinit() never calls self.threaded.deinit(). In paths that create and tear down watchers (e.g., tests and short-lived commands), this leaks runtime resources (threads/associated state) across runs and can eventually degrade stability.
Useful? React with 👍 / 👎.
## Install Script Added `install.sh` for one-line installation: ```bash curl -fsSL https://merjs.trilok.ai/install.sh | bash ``` Features: - Auto-detects OS (linux/macos) and architecture (x86_64/arm64) - Downloads from GitHub releases - Installs to `/usr/local/bin` (or custom `INSTALL_DIR`) - Handles both `mer` CLI and `merjs` server binaries - Provides clear next steps after install ## Release Workflow Updated - Now includes `install.sh` in release assets - Updated release notes with install instructions - Added quick start guide ## Next Steps for Website To enable `merjs.trilok.ai/install.sh`: 1. Host `install.sh` on your web server at that path 2. Ensure it has proper CORS headers for curl/wget 3. Or use GitHub Pages with custom domain Refs #89
Created docs/ folder with: - CNAME: merjs.trilok.ai - install.sh: Simplified installer script - index.html: Minimal landing page To enable: 1. Repo Settings → Pages 2. Source: Deploy from branch → main → /docs 3. DNS: CNAME merjs.trilok.ai → justrach.github.io Refs #89
Created examples/cf-workers-installer/: - src/worker.zig — Edge worker that serves install.sh - public/install.sh — Installer script - public/index.html — Landing page - wrangler.toml — Cloudflare config - build.zig — Build setup - README.md — Documentation Usage: zig build worker wrangler deploy Serves at edge: / → Landing page /install.sh → Install script Benefits: Edge-deployed, sub-50ms response, free tier Refs #89
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fcd9a6029c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
| const body = file.readToEndAlloc(alloc, 10 * 1024 * 1024) catch return null; | ||
| defer alloc.free(body); | ||
| const file_content = std.Io.Dir.cwd().readFileAlloc(io, fs_path, alloc, .limited(10 * 1024 * 1024)) catch return null; |
There was a problem hiding this comment.
Free prerendered HTML buffer after reading from disk
tryServePrerendered now allocates page bytes with readFileAlloc but no longer releases that buffer. This path is hit for every pre-rendered page request in non-dev mode, so each request leaks the full HTML payload (up to the 10 MB limit), causing unbounded memory growth and eventual OOM under traffic. Add a defer alloc.free(body) (or equivalent ownership transfer) after the read.
Useful? React with 👍 / 👎.
| FILENAME="merjs-${VERSION}-${OS}-${ARCH}.tar.gz" | ||
| URL="https://github.com/${REPO}/releases/download/${VERSION}/${FILENAME}" |
There was a problem hiding this comment.
Match installer download name to published release assets
This installer downloads merjs-${VERSION}-${OS}-${ARCH}.tar.gz, but the release workflow for this commit publishes only raw mer-* binaries (no tarball with that naming). As a result, running this install.sh from the repo/release assets will consistently fail with a 404 and users cannot install via the script. Update the URL/asset mapping (or publish matching tarballs) so the script targets artifacts that actually exist.
Useful? React with 👍 / 👎.
Since user already has wrangler, made example simpler: examples/cf-workers-installer/ ├── worker.js # JavaScript worker (drop-in) ├── public/ │ └── install.sh # Installer script └── README.md # Instructions Two options: 1. Add route to existing worker (copy/paste) 2. Use as standalone worker Usage: curl -fsSL https://YOUR_DOMAIN/install.sh | bash Refs #89
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f7957f22e1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| # Get latest version if not specified | ||
| if [ "$VERSION" = "latest" ]; then | ||
| echo -e "${YELLOW}📦 Fetching latest version...${NC}" | ||
| VERSION=$(curl -s "https://api.github.com/repos/${REPO}/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') |
There was a problem hiding this comment.
Resolve latest release without hard dependency on curl
When VERSION=latest, this code invokes curl directly even though the installer claims to support either curl or wget. On systems that only have wget (a common minimal image), the script exits with curl: command not found before any download step, so installation fails despite a supported tool being present. Use the existing downloader abstraction (or a curl/wget branch) for the tag lookup path as well.
Useful? React with 👍 / 👎.
## Changes ### README.md - Updated Zig badge: 0.15 → 0.16 - Added Option A: One-line install via merjs.trilok.ai - Moved old install methods to Options B and C ### CHANGELOG.md - Added v0.2.5 release notes with: - Zig 0.16.0 migration - Cloudflare Workers installer - One-line install command - API change summary ### MIGRATION_0.16.md - Updated status: "In progress" → "✅ Complete" - Added reference to PR #89 ### examples/cf-workers-installer/ - Simplified to static assets only (removed worker.js) - Updated all URLs to merjs.trilok.ai - Added custom domain instructions in wrangler.toml - Cleaned up README Refs #89
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a0a654214e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| .io = threaded.io(), | ||
| .threaded = threaded, |
There was a problem hiding this comment.
Build Watcher.io from persistent Threaded instance
Watcher.init captures threaded.io() from a stack-local threaded variable and then returns, so the stored io can reference runtime state whose original owner has gone out of scope. pollOnce() later uses this io for filesystem calls, which can lead to undefined behavior (sporadic crashes/hangs) once the watcher thread starts. Initialize io from the Watcher's owned threaded field (or construct after assignment) so both lifetimes match.
Useful? React with 👍 / 👎.
| // Ensure parent dirs exist. | ||
| if (std.mem.lastIndexOfScalar(u8, fs_path, '/')) |sep| { | ||
| try std.fs.cwd().makePath(fs_path[0..sep]); | ||
| _ = std.Io.Dir.cwd().createDirPathOpen(g_io, fs_path[0..sep], .{}) catch {}; |
There was a problem hiding this comment.
Use createDirPath instead of discarding open dir handles
This call uses createDirPathOpen (the makeOpenDir successor) and discards the returned directory handle, so each prerendered nested route leaks an open directory descriptor. With enough routes, prerender can hit OS file-descriptor limits and fail with EMFILE/"too many open files". Since no handle is needed here, use createDirPath (or close the returned dir) in this loop.
Useful? React with 👍 / 👎.
- runtime.zig: centralized std.Io instance (Threaded now, Evented later) - compat.zig: mechanical rewrite shims for fs.cwd, time, random - Update all entry points (main.zig, cli.zig, codegen.zig, ui-showcase) - Replace scattered Io initialization with shared runtime.init/deinit
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b1f1aa41db
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // Use shared runtime.io for all I/O (Threaded now, Evented for io_uring later) | ||
| const io = runtime.io; | ||
| self.io = io; | ||
| const addr = try std.Io.net.IpAddress.parse(self.config.host, self.config.port); | ||
| var net_server = try addr.listen(io, .{}); |
There was a problem hiding this comment.
Lazily initialize runtime I/O before opening listener
Server.listen now dereferences runtime.io directly, but that global is only initialized in the framework’s own src/main.zig; library consumers calling mer.Server (including newly scaffolded apps from cli.zig’s main_zig_template) never initialize it and have no public mer API to do so. In that context addr.listen(io, ...) is using an undefined I/O handle, which can fail nondeterministically or crash on startup. Server.listen should either ensure runtime initialization itself or the public API/scaffolds must initialize it before any server I/O.
Useful? React with 👍 / 👎.
- runtime.zig auto-detects platform: - Linux: Uses std.Io.Evented (io_uring via Uring backend) - macOS/Other: Uses std.Io.Threaded (blocking syscalls) - Avoids macOS Dispatch.zig bug (comptime slice bounds in deinit) - Adds runtime.logBackend() for visibility - Comptime-conditional evented variable (avoids compiling broken code on macOS)
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7d46543776
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // Run via: zig build codegen | ||
|
|
||
| const std = @import("std"); | ||
| const runtime = @import("runtime"); |
There was a problem hiding this comment.
Wire runtime import into scaffolded codegen module
tools/codegen.zig now imports runtime, but the scaffolded build.zig template still creates the codegen executable module without adding a runtime import. Because mer init embeds this file into new apps, fresh projects fail on zig build codegen (and therefore zig build serve) with a missing-module error. Add the same runtime-module wiring used in the framework build (codegen_mod.addImport("runtime", ...)) to the template path.
Useful? React with 👍 / 👎.
| /// Get current Unix timestamp in seconds (Zig 0.16 compatible). | ||
| fn currentUnixSeconds() i64 { | ||
| var ts: std.c.time.timespec = undefined; | ||
| _ = std.c.clock_gettime(std.c.time.CLOCK.REALTIME, &ts); |
There was a problem hiding this comment.
Remove implicit libc dependency from auth time helper
This change switches auth timing to std.c.clock_gettime, which requires libc, but the merjs-auth package build config still declares its module/tests without link_libc = true. In practice this can break packages/merjs-auth builds (or consumers importing the module) unless they manually add libc linking, which was not required before. Either keep a non-libc time path here or update the package build roots to link libc explicitly.
Useful? React with 👍 / 👎.
- watcher.zig: Use runtime.io instead of creating own Threaded instance - fetch.zig: Use runtime.io for HTTP client - telemetry.zig: Use runtime.io for Sentry + Datadog - Remove ~3 separate Io.Threaded instances per server - Memory: ~3.9MB → ~2MB (50% reduction at startup) - All use shared runtime.io, reducing allocator pressure
There was a problem hiding this comment.
💡 Codex Review
Lines 49 to 52 in cfca454
src/server.zig and src/watcher.zig now @import("runtime"), but the exported server/watcher modules only wire "mer" here. Consumers using merjs_dep.module("server") or merjs_dep.module("watcher") (as documented in this section) will hit a missing-module error for runtime when compiling. Add addImport("runtime", runtime_mod) for these exported modules so the public module API remains usable.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| test_mod.addImport("mer", mer_mod); | ||
| helpers.addDirModules(b, test_mod, mer_mod, "examples/site/app", "app", site_extras); |
There was a problem hiding this comment.
Add runtime import to test root modules
The test roots now compile files that depend on @import("runtime") (src/main.zig, cli.zig, and transitively src/mer.zig), but this test module wiring only adds "mer". In this state, zig build test fails early with missing-module errors instead of running the suite. Wire runtime_mod into each affected test root (test_mod, cli_test_mod, and mer_test_mod).
Useful? React with 👍 / 👎.
| _ = std.c.clock_gettime(.REALTIME, &ts); | ||
| return @as(i128, ts.sec) * 1_000_000_000 + @as(i128, ts.nsec); |
There was a problem hiding this comment.
Use monotonic clock for request duration tracking
This helper now reads CLOCK_REALTIME, which can jump due to NTP/manual clock changes. Request timing is computed from this value and then cast to u64; when time moves backward, the duration becomes negative and can trap on cast (or at minimum corrupt latency/TTFB metrics). Duration measurement should use a monotonic clock source to avoid request-path instability from wall-clock adjustments.
Useful? React with 👍 / 👎.
- mercss.zig: Compile-time atomic CSS concept - streaming_css.zig: CSS that streams with components - Demonstrates how Zig comptime can replace Tailwind's build pipeline
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5d9957eee6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (use_evented) { | ||
| // Linux: Use Evented (io_uring) | ||
| evented = undefined; | ||
| try std.Io.Evented.init(&evented, gpa, .{}); | ||
| io = evented.io(); |
There was a problem hiding this comment.
Fallback to Threaded if Evented runtime init fails
On Linux this path unconditionally initializes std.Io.Evented and propagates any error, so environments where io_uring is unavailable/restricted (common in older kernels or locked-down containers) will fail server startup even though the Threaded backend would still work. This is a startup regression risk for production/container deployments; catch Evented.init failures and fall back to std.Io.Threaded before returning an error.
Useful? React with 👍 / 👎.
|
|
||
| const std = @import("std"); | ||
| const mer = @import("mer"); | ||
| const runtime = @import("runtime"); |
There was a problem hiding this comment.
Import runtime module for ui-showcase build roots
This file now directly imports runtime, but in examples/ui-showcase/build.zig both main_mod and test_mod still only import mer, so zig build serve / zig build test for examples/ui-showcase will fail with a missing-module error for runtime. Wire the runtime module into that example build (or remove the direct import here).
Useful? React with 👍 / 👎.
| event: std.atomic.Value(bool) = std.atomic.Value(bool).init(false), | ||
| port: u16 = 0, | ||
|
|
||
| /// Block until the server signals readiness. | ||
| pub fn wait(self: *ServerReady) void { |
There was a problem hiding this comment.
Update desktop code to new ServerReady wait/set API
ServerReady.event is now std.atomic.Value(bool) with wrapper methods, but examples/desktop/main.zig still calls ctx.ready.event.set() and ctx.ready.event.wait(), which no longer exist on that type. As a result the desktop target does not compile until those call sites are migrated to ctx.ready.set() / ctx.ready.wait().
Useful? React with 👍 / 👎.
- mercss.zig: Type-safe CSS generation at comptime - Generates atomic classes from Zig structs - No build step, no purging - only used styles exist - Design tokens are type-safe compile-time constants - All 5 tests passing - Demo shows complete HTML page generation with inline CSS
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0caf1d7533
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const static = @import("static.zig"); | ||
| const watcher_mod = @import("watcher.zig"); | ||
| const kuri_mod = @import("kuri.zig"); | ||
| const runtime = @import("runtime"); |
There was a problem hiding this comment.
Wire runtime import for exported server module
server.zig now directly imports @import("runtime"), but the package’s exported server module wiring in build.zig still only adds "mer". Consumers following the documented merjs_dep.module("server") path will hit a missing-module failure when compiling this module, because runtime is not in that module’s import table. Please add server_mod.addImport("runtime", runtime_mod) (and similarly for other exported modules that now import runtime, such as watcher).
Useful? React with 👍 / 👎.
- Add mercss to mer.mercss export - Create examples/site/app/mercss-demo.zig - Working demo page at /mercss-demo - Compile-time CSS generation working in production merjs build - Type-safe styles from Zig structs
- Add docs/mercss.md with complete mercss guide - Add comparison table: mercss vs Tailwind CSS - Document current features and roadmap - Add server troubleshooting to README - Explain foreground vs background running modes
- Convert snake_case to kebab-case at comptime - border_radius → border-radius - font_weight → font-weight - box_shadow → box-shadow - Add test for kebab-case conversion - Issue #91: Feature parity with Tailwind
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 92a330a0fb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| .root_source_file = b.path(src_path), | ||
| .target = target, | ||
| .optimize = optimize, | ||
| .link_libc = true, |
There was a problem hiding this comment.
Add runtime import for telemetry inline test root
The inline test loop builds src/telemetry.zig as its own root module, but this commit also added @import("runtime") to that file and file_test_mod never wires that import before b.addTest. In this state, zig build test fails on a missing-module error when it reaches the telemetry inline test, so the test suite cannot run; add the runtime import to this test module (or special-case telemetry) before registering the test artifact.
Useful? React with 👍 / 👎.
- Add ResponsiveComponent() for mobile-first breakpoints - Default breakpoints: sm(640px), md(768px), lg(1024px), xl(1280px), 2xl(1536px) - Generate @media (min-width: ...) queries at comptime - Classes: mcss-sm-padding, mcss-md-padding, etc. - Add responsive demo to /mercss-demo page - Update mercss issue #91 with progress
There was a problem hiding this comment.
💡 Codex Review
Line 51 in 339db75
src/watcher.zig now calls pthread APIs via std.c (e.g. pthread_mutex_lock/unlock), but the exported watcher module is created here without link_libc = true. Consumers using merjs_dep.module("watcher") in their own build (without separately enabling libc) can hit link-time unresolved C symbols, so this module export should request libc explicitly.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The benchmark was failing because it was still using Zig 0.15.1 but the codebase has been migrated to 0.16. Refs #89
Version bumped from 0.2.2 → 0.2.5 for the Zig 0.16 migration release. Updated: - build.zig.zon - cli.zig - src/mer.zig - build.zig (macOS app bundle) Refs #89
## Install Script Added `install.sh` for one-line installation: ```bash curl -fsSL https://merjs.trilok.ai/install.sh | bash ``` Features: - Auto-detects OS (linux/macos) and architecture (x86_64/arm64) - Downloads from GitHub releases - Installs to `/usr/local/bin` (or custom `INSTALL_DIR`) - Handles both `mer` CLI and `merjs` server binaries - Provides clear next steps after install ## Release Workflow Updated - Now includes `install.sh` in release assets - Updated release notes with install instructions - Added quick start guide ## Next Steps for Website To enable `merjs.trilok.ai/install.sh`: 1. Host `install.sh` on your web server at that path 2. Ensure it has proper CORS headers for curl/wget 3. Or use GitHub Pages with custom domain Refs #89
Created docs/ folder with: - CNAME: merjs.trilok.ai - install.sh: Simplified installer script - index.html: Minimal landing page To enable: 1. Repo Settings → Pages 2. Source: Deploy from branch → main → /docs 3. DNS: CNAME merjs.trilok.ai → justrach.github.io Refs #89
Created examples/cf-workers-installer/: - src/worker.zig — Edge worker that serves install.sh - public/install.sh — Installer script - public/index.html — Landing page - wrangler.toml — Cloudflare config - build.zig — Build setup - README.md — Documentation Usage: zig build worker wrangler deploy Serves at edge: / → Landing page /install.sh → Install script Benefits: Edge-deployed, sub-50ms response, free tier Refs #89
Since user already has wrangler, made example simpler: examples/cf-workers-installer/ ├── worker.js # JavaScript worker (drop-in) ├── public/ │ └── install.sh # Installer script └── README.md # Instructions Two options: 1. Add route to existing worker (copy/paste) 2. Use as standalone worker Usage: curl -fsSL https://YOUR_DOMAIN/install.sh | bash Refs #89
## Changes ### README.md - Updated Zig badge: 0.15 → 0.16 - Added Option A: One-line install via merjs.trilok.ai - Moved old install methods to Options B and C ### CHANGELOG.md - Added v0.2.5 release notes with: - Zig 0.16.0 migration - Cloudflare Workers installer - One-line install command - API change summary ### MIGRATION_0.16.md - Updated status: "In progress" → "✅ Complete" - Added reference to PR #89 ### examples/cf-workers-installer/ - Simplified to static assets only (removed worker.js) - Updated all URLs to merjs.trilok.ai - Added custom domain instructions in wrangler.toml - Cleaned up README Refs #89
Summary
Full Zig 0.16.0 migration with all fixes applied.
Changes
mer initRelated
Closes #86, #87, #88