atproto utils for zig
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

add jetstream smoke test and fix publish-docs for zig 0.16

- add scripts/jetstream_smoke.zig with `zig build smoke` step
- fix publish-docs.zig: posix.getenv → std.c.getenv, std.time.timestamp
→ Io.Timestamp, std.fs.cwd → Io.Dir, XrpcClient.init signature

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

+61 -6
+17
build.zig
··· 60 60 const test_step = b.step("test", "run unit tests"); 61 61 test_step.dependOn(&run_tests.step); 62 62 63 + // jetstream smoke test 64 + const jetstream_smoke = b.addExecutable(.{ 65 + .name = "jetstream-smoke", 66 + .root_module = b.createModule(.{ 67 + .root_source_file = b.path("scripts/jetstream_smoke.zig"), 68 + .target = target, 69 + .optimize = optimize, 70 + .link_libc = true, 71 + .imports = &.{.{ .name = "zat", .module = mod }}, 72 + }), 73 + }); 74 + b.installArtifact(jetstream_smoke); 75 + 76 + const run_smoke = b.addRunArtifact(jetstream_smoke); 77 + const smoke_step = b.step("smoke", "run jetstream smoke test"); 78 + smoke_step.dependOn(&run_smoke.step); 79 + 63 80 // publish-docs script (uses zat to publish docs to ATProto) 64 81 const publish_docs = b.addExecutable(.{ 65 82 .name = "publish-docs",
+38
scripts/jetstream_smoke.zig
··· 1 + const std = @import("std"); 2 + const zat = @import("zat"); 3 + 4 + pub fn main() !void { 5 + var da: std.heap.DebugAllocator(.{}) = .init; 6 + defer _ = da.deinit(); 7 + const allocator = da.allocator(); 8 + 9 + std.debug.print("smoke test starting\n", .{}); 10 + 11 + var handler = Handler{}; 12 + var client = zat.JetstreamClient.init(allocator, .{ 13 + .hosts = &.{"jetstream2.us-east.bsky.network"}, 14 + .wanted_collections = &.{"app.bsky.feed.post"}, 15 + }); 16 + client.subscribe(&handler); 17 + } 18 + 19 + const Handler = struct { 20 + count: u64 = 0, 21 + connects: u64 = 0, 22 + 23 + pub fn onEvent(self: *Handler, event: zat.JetstreamEvent) void { 24 + self.count += 1; 25 + if (self.count % 1000 == 0) { 26 + std.debug.print(" [{d}] time_us={d}\n", .{ self.count, event.timeUs() }); 27 + } 28 + } 29 + 30 + pub fn onConnect(self: *Handler, host: []const u8) void { 31 + self.connects += 1; 32 + std.debug.print("CONNECT #{d} to {s} (total events so far: {d})\n", .{ self.connects, host, self.count }); 33 + } 34 + 35 + pub fn onError(_: *Handler, err: anyerror) void { 36 + std.debug.print("ERROR: {s}\n", .{@errorName(err)}); 37 + } 38 + };
+6 -6
scripts/publish-docs.zig
··· 29 29 30 30 const handle = "zat.dev"; 31 31 32 - const password = std.posix.getenv("ATPROTO_PASSWORD") orelse { 32 + const password = if (std.c.getenv("ATPROTO_PASSWORD")) |p| std.mem.span(p) else { 33 33 std.debug.print("error: ATPROTO_PASSWORD not set\n", .{}); 34 34 return error.MissingEnv; 35 35 }; 36 36 37 - const pds = std.posix.getenv("ATPROTO_PDS") orelse "https://bsky.social"; 37 + const pds = if (std.c.getenv("ATPROTO_PDS")) |p| std.mem.span(p) else "https://bsky.social"; 38 38 39 - var client = zat.XrpcClient.init(allocator, pds); 39 + var client = zat.XrpcClient.init(std.Options.debug_io, allocator, pds); 40 40 defer client.deinit(); 41 41 42 42 const session = try createSession(&client, allocator, handle, password); ··· 69 69 const now = timestamp(); 70 70 71 71 for (docs, 0..) |doc, i| { 72 - const content = std.fs.cwd().readFileAlloc(allocator, doc.file, 1024 * 1024) catch |err| { 72 + const content = std.Io.Dir.readFileAlloc(.cwd(), std.Options.debug_io, doc.file, allocator, .limited(1024 * 1024)) catch |err| { 73 73 std.debug.print("warning: could not read {s}: {}\n", .{ doc.file, err }); 74 74 continue; 75 75 }; ··· 108 108 109 109 // publish devlog entries (clock_id 101, 102, ...) 110 110 for (devlog, 0..) |entry, i| { 111 - const content = std.fs.cwd().readFileAlloc(allocator, entry.file, 1024 * 1024) catch |err| { 111 + const content = std.Io.Dir.readFileAlloc(.cwd(), std.Options.debug_io, entry.file, allocator, .limited(1024 * 1024)) catch |err| { 112 112 std.debug.print("warning: could not read {s}: {}\n", .{ entry.file, err }); 113 113 continue; 114 114 }; ··· 236 236 } 237 237 238 238 fn timestamp() [20]u8 { 239 - const epoch_seconds = std.time.timestamp(); 239 + const epoch_seconds: i64 = @intCast(@divFloor(std.Io.Timestamp.now(std.Options.debug_io, .real).nanoseconds, std.time.ns_per_s)); 240 240 const days: i32 = @intCast(@divFloor(epoch_seconds, std.time.s_per_day)); 241 241 const day_secs: u32 = @intCast(@mod(epoch_seconds, std.time.s_per_day)); 242 242