const std = @import("std");const io_mod = @import("../shared/io.zig");const process_supervisor = @import("process_supervisor.zig");const session_child_store = @import("../session/session_child_store.zig"); const Allocator = std.mem.Allocator;const legacy_schema_version: i64 = 1;const schema_version: i64 = 2;const max_record_bytes: usize = 256 * 1024;pub const StableBackgroundRecordId = process_supervisor.StableBackgroundRecordId; pub const LogStorage = union(enum) { managed_session: struct { managed_log_name: []u8, }, external: struct { path: []u8, }, pub fn deinit(self: *LogStorage, alloc: Allocator) void { switch (self.*) { .managed_session => |value| alloc.free(value.managed_log_name), .external => |value| alloc.free(value.path), } self.* = undefined; }}; pub fn renderLogStorageJson( alloc: Allocator, storage: LogStorage,) ![]u8 { var out: std.Io.Writer.Allocating = .init(alloc); defer out.deinit(); switch (storage) { .managed_session => |value| { try session_child_store.SessionChildCapability.validateManagedName( value.managed_log_name, ); try out.writer.writeAll( "{\"kind\":\"managed_session\",\"managed_log_name\":", ); try std.json.Stringify.value( value.managed_log_name, .{}, &out.writer, ); }, .external => |value| { if (!std.fs.path.isAbsolute(value.path)) { return error.InvalidBackgroundRecord; } try out.writer.writeAll("{\"kind\":\"external\",\"path\":"); try std.json.Stringify.value(value.path, .{}, &out.writer); }, } try out.writer.writeByte('}'); return out.toOwnedSlice();} pub fn parseLogStorageJson( alloc: Allocator, json_text: []const u8,) !LogStorage { var parsed = std.json.parseFromSlice( std.json.Value, alloc, json_text, .{}, ) catch return error.InvalidBackgroundRecord; defer parsed.deinit(); const object = try requireObject(parsed.value); const kind = try requireString(object, "kind"); if (std.mem.eql(u8, kind, "managed_session")) { const name = try requireString(object, "managed_log_name"); session_child_store.SessionChildCapability.validateManagedName(name) catch return error.InvalidBackgroundRecord; return .{ .managed_session = .{ .managed_log_name = try alloc.dupe(u8, name), } }; } if (std.mem.eql(u8, kind, "external")) { const path = try requireString(object, "path"); if (!std.fs.path.isAbsolute(path)) { return error.InvalidBackgroundRecord; } return .{ .external = .{ .path = try alloc.dupe(u8, path), } }; } return error.InvalidBackgroundRecord;} pub fn classifyLegacyLogStorage( alloc: Allocator, capability: *session_child_store.SessionChildCapability, legacy_path: []const u8, explicit_external_dir: ?[]const u8,) !?LogStorage { if (!std.fs.path.isAbsolute(legacy_path)) return null; const parent = std.fs.path.dirname(legacy_path) orelse return null; const basename = std.fs.path.basename(legacy_path); session_child_store.SessionChildCapability.validateManagedName(basename) catch return null; const managed_route = try capability.displayRoutePath( alloc, .background_logs, ); defer alloc.free(managed_route); if (std.mem.eql(u8, parent, managed_route)) { return .{ .managed_session = .{ .managed_log_name = try alloc.dupe(u8, basename), } }; } const external_dir = explicit_external_dir orelse return null; if (!std.mem.eql(u8, parent, external_dir)) return null; return .{ .external = .{ .path = try alloc.dupe(u8, legacy_path), } };} test "managed and external background log storage remain distinct" { const alloc = std.testing.allocator; var managed = LogStorage{ .managed_session = .{ .managed_log_name = try alloc.dupe(u8, "managed.log"), }, }; defer managed.deinit(alloc); var external = LogStorage{ .external = .{ .path = try alloc.dupe(u8, "/tmp/managed.log"), }, }; defer external.deinit(alloc); switch (managed) { .managed_session => |value| try std.testing.expectEqualStrings( "managed.log", value.managed_log_name, ), .external => return error.ExpectedManagedSessionStorage, } switch (external) { .external => |value| try std.testing.expectEqualStrings( "/tmp/managed.log", value.path, ), .managed_session => return error.ExpectedExternalStorage, }} test "background log storage codec preserves managed and external identity" { const alloc = std.testing.allocator; const cases = [_]LogStorage{ .{ .managed_session = .{ .managed_log_name = @constCast("managed.log"), } }, .{ .external = .{ .path = @constCast("/tmp/external.log"), } }, }; for (cases) |storage| { const encoded = try renderLogStorageJson(alloc, storage); defer alloc.free(encoded); var decoded = try parseLogStorageJson(alloc, encoded); defer decoded.deinit(alloc); switch (storage) { .managed_session => |expected| switch (decoded) { .managed_session => |actual| try std.testing.expectEqualStrings( expected.managed_log_name, actual.managed_log_name, ), .external => return error.ExpectedManagedSessionStorage, }, .external => |expected| switch (decoded) { .external => |actual| try std.testing.expectEqualStrings( expected.path, actual.path, ), .managed_session => return error.ExpectedExternalStorage, }, } }} test "legacy log path maps to managed only for exact display parent" { const alloc = std.testing.allocator; var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); try tmp.dir.createDir( io_mod.getIo(), "session-logs", std.Io.File.Permissions.fromMode(0o700), ); try tmp.dir.createDir( io_mod.getIo(), "external-logs", std.Io.File.Permissions.fromMode(0o700), ); const managed_dir = try io_mod.dirRealpathAlloc( alloc, tmp.dir, "session-logs", ); defer alloc.free(managed_dir); const external_dir = try io_mod.dirRealpathAlloc( alloc, tmp.dir, "external-logs", ); defer alloc.free(external_dir); var capability = try session_child_store.SessionChildCapability.initLegacyRoute( alloc, managed_dir, .background_logs, .read_only, ); defer capability.deinit(); const managed_path = try std.fs.path.join( alloc, &.{ managed_dir, "managed.log" }, ); defer alloc.free(managed_path); var managed = (try classifyLegacyLogStorage( alloc, &capability, managed_path, external_dir, )).?; defer managed.deinit(alloc); switch (managed) { .managed_session => |value| try std.testing.expectEqualStrings( "managed.log", value.managed_log_name, ), .external => return error.ExpectedManagedSessionStorage, } const external_path = try std.fs.path.join( alloc, &.{ external_dir, "external.log" }, ); defer alloc.free(external_path); var external = (try classifyLegacyLogStorage( alloc, &capability, external_path, external_dir, )).?; defer external.deinit(alloc); switch (external) { .external => |value| try std.testing.expectEqualStrings( external_path, value.path, ), .managed_session => return error.ExpectedExternalStorage, } const unauthorized = try std.fs.path.join( alloc, &.{ external_dir, "..", "other.log" }, ); defer alloc.free(unauthorized); try std.testing.expect((try classifyLegacyLogStorage( alloc, &capability, unauthorized, external_dir, )) == null);} test "managed background read only absence does not create route" { const alloc = std.testing.allocator; var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); try tmp.dir.createDir( io_mod.getIo(), "session", std.Io.File.Permissions.fromMode(0o700), ); var session_dir = try tmp.dir.openDir(io_mod.getIo(), "session", .{ .iterate = true, .follow_symlinks = false, }); defer session_dir.close(io_mod.getIo()); const session_path = try io_mod.dirRealpathAlloc( alloc, tmp.dir, "session", ); defer alloc.free(session_path); var capability = try session_child_store.SessionChildCapability.initForTesting( alloc, session_dir, session_path, .read_only, .{}, ); defer capability.deinit(); const store = Store.initManaged(&capability); try std.testing.expectError( error.BackgroundRecordNotFound, store.load(alloc, 1), ); var records = try store.list(alloc); defer records.deinit(alloc); try std.testing.expectEqual(@as(usize, 0), records.items.len); try std.testing.expectError( error.FileNotFound, session_dir.statFile(io_mod.getIo(), "background", .{}), );} pub const TaskState = process_supervisor.TaskState; pub const Record = struct { id: u64, background_record_id: ?StableBackgroundRecordId = null, process_token: ?[]u8 = null, pid: []u8, command: []u8, cwd: []u8, log_path: []u8, log_storage: ?LogStorage = null, expect_url: bool, server_url: ?[]u8 = null, started_at_ms: i64, updated_at_ms: i64, exit_code: ?i32 = null, state: TaskState, diagnostic: ?[]u8 = null, pub fn deinit(self: *Record, alloc: Allocator) void { alloc.free(self.pid); if (self.process_token) |process_token| alloc.free(process_token); alloc.free(self.command); alloc.free(self.cwd); alloc.free(self.log_path); if (self.log_storage) |*storage| storage.deinit(alloc); if (self.server_url) |url| alloc.free(url); if (self.diagnostic) |diagnostic| alloc.free(diagnostic); self.* = undefined; } pub fn fromTaskSnapshot(alloc: Allocator, snapshot: process_supervisor.TaskSnapshot, updated_at_ms: i64) !Record { const pid = try alloc.dupe(u8, snapshot.pid); errdefer alloc.free(pid); const command = try alloc.dupe(u8, snapshot.command); errdefer alloc.free(command); const cwd = try alloc.dupe(u8, snapshot.cwd); errdefer alloc.free(cwd); const log_path = try alloc.dupe(u8, snapshot.log_path); errdefer alloc.free(log_path); var server_url: ?[]u8 = null; errdefer if (server_url) |url| alloc.free(url); if (snapshot.server_url) |url| { server_url = try alloc.dupe(u8, url); } return .{ .id = snapshot.durable_record_id orelse snapshot.id, .background_record_id = snapshot.background_record_id, .process_token = if (snapshot.process_token) |token| try alloc.dupe(u8, token.view()) else null, .pid = pid, .command = command, .cwd = cwd, .log_path = log_path, .log_storage = if (snapshot.managed_log_name) |name| .{ .managed_session = .{ .managed_log_name = try alloc.dupe(u8, name), } } else .{ .external = .{ .path = try alloc.dupe(u8, snapshot.log_path), } }, .expect_url = snapshot.expect_url, .server_url = server_url, .started_at_ms = snapshot.started_at_ms, .updated_at_ms = updated_at_ms, .exit_code = snapshot.exit_code, .state = snapshot.state, }; }}; pub const Store = struct { capability: *session_child_store.SessionChildCapability, owned_capability: ?*session_child_store.SessionChildCapability = null, display_route_path: ?[]u8 = null, pub fn initManaged( capability: *session_child_store.SessionChildCapability, ) Store { return .{ .capability = capability }; } /// Transitional legacy-only constructor retained until adapter conversion. pub fn initWithDir(alloc: Allocator, dir_path: []const u8) !Store { const owned = try alloc.create(session_child_store.SessionChildCapability); errdefer alloc.destroy(owned); owned.* = try session_child_store.SessionChildCapability.initLegacyRoute( alloc, dir_path, .background_records, .writable, ); const display_route_path = try alloc.dupe(u8, dir_path); errdefer alloc.free(display_route_path); return .{ .capability = owned, .owned_capability = owned, .display_route_path = display_route_path, }; } pub fn initReadOnlyWithDir( alloc: Allocator, dir_path: []const u8, ) !Store { const owned = try alloc.create( session_child_store.SessionChildCapability, ); errdefer alloc.destroy(owned); owned.* = try session_child_store.SessionChildCapability.initLegacyRoute( alloc, dir_path, .background_records, .read_only, ); const display_route_path = try alloc.dupe(u8, dir_path); errdefer alloc.free(display_route_path); return .{ .capability = owned, .owned_capability = owned, .display_route_path = display_route_path, }; } pub fn deinit(self: *Store, alloc: Allocator) void { if (self.owned_capability) |owned| { owned.deinit(); alloc.destroy(owned); } if (self.display_route_path) |path| alloc.free(path); self.* = undefined; } pub fn sameBacking(self: Store, other: Store) bool { if (self.display_route_path) |left| { const right = other.display_route_path orelse return false; return std.mem.eql(u8, left, right); } return other.display_route_path == null and self.capability == other.capability; } pub fn nextId(self: Store) !u64 { var max_id: u64 = 0; var entries = try self.capability.iterate( std.heap.c_allocator, .background_records, ); defer entries.deinit(); for (entries.names) |name| { if (!std.mem.endsWith(u8, name, ".json")) continue; const basename = name[0 .. name.len - ".json".len]; const id = std.fmt.parseUnsigned(u64, basename, 10) catch continue; max_id = @max(max_id, id); } return max_id + 1; } pub fn saveTaskSnapshot(self: Store, alloc: Allocator, snapshot: process_supervisor.TaskSnapshot) !void { var record = try Record.fromTaskSnapshot(alloc, snapshot, io_mod.milliTimestamp()); defer record.deinit(alloc); try self.saveRecord(alloc, record); } pub fn saveRecord(self: Store, alloc: Allocator, record: Record) !void { const name = try recordName(alloc, record.id); defer alloc.free(name); try self.validateIndeterminateCurrent(alloc); const text = try renderRecordJson(alloc, record); defer alloc.free(text); var entry = try self.capability.atomicReplace( alloc, .background_records,Lines 1–500 of 1479. Load more
const std = @import("std"); const io_mod = @import("../shared/io.zig"); const process_supervisor = @import("process_supervisor.zig"); const session_child_store = @import("../session/session_child_store.zig"); const Allocator = std.mem.Allocator; const legacy_schema_version: i64 = 1; const schema_version: i64 = 2; const max_record_bytes: usize = 256 * 1024; pub const StableBackgroundRecordId = process_supervisor.StableBackgroundRecordId; pub const LogStorage = union(enum) { managed_session: struct { managed_log_name: []u8, }, external: struct { path: []u8, }, pub fn deinit(self: *LogStorage, alloc: Allocator) void { switch (self.*) { .managed_session => |value| alloc.free(value.managed_log_name), .external => |value| alloc.free(value.path), } self.* = undefined; } }; pub fn renderLogStorageJson( alloc: Allocator, storage: LogStorage, ) ![]u8 { var out: std.Io.Writer.Allocating = .init(alloc); defer out.deinit(); switch (storage) { .managed_session => |value| { try session_child_store.SessionChildCapability.validateManagedName( value.managed_log_name, ); try out.writer.writeAll( "{\"kind\":\"managed_session\",\"managed_log_name\":", ); try std.json.Stringify.value( value.managed_log_name, .{}, &out.writer, ); }, .external => |value| { if (!std.fs.path.isAbsolute(value.path)) { return error.InvalidBackgroundRecord; } try out.writer.writeAll("{\"kind\":\"external\",\"path\":"); try std.json.Stringify.value(value.path, .{}, &out.writer); }, } try out.writer.writeByte('}'); return out.toOwnedSlice(); } pub fn parseLogStorageJson( alloc: Allocator, json_text: []const u8, ) !LogStorage { var parsed = std.json.parseFromSlice( std.json.Value, alloc, json_text, .{}, ) catch return error.InvalidBackgroundRecord; defer parsed.deinit(); const object = try requireObject(parsed.value); const kind = try requireString(object, "kind"); if (std.mem.eql(u8, kind, "managed_session")) { const name = try requireString(object, "managed_log_name"); session_child_store.SessionChildCapability.validateManagedName(name) catch return error.InvalidBackgroundRecord; return .{ .managed_session = .{ .managed_log_name = try alloc.dupe(u8, name), } }; } if (std.mem.eql(u8, kind, "external")) { const path = try requireString(object, "path"); if (!std.fs.path.isAbsolute(path)) { return error.InvalidBackgroundRecord; } return .{ .external = .{ .path = try alloc.dupe(u8, path), } }; } return error.InvalidBackgroundRecord; } pub fn classifyLegacyLogStorage( alloc: Allocator, capability: *session_child_store.SessionChildCapability, legacy_path: []const u8, explicit_external_dir: ?[]const u8, ) !?LogStorage { if (!std.fs.path.isAbsolute(legacy_path)) return null; const parent = std.fs.path.dirname(legacy_path) orelse return null; const basename = std.fs.path.basename(legacy_path); session_child_store.SessionChildCapability.validateManagedName(basename) catch return null; const managed_route = try capability.displayRoutePath( alloc, .background_logs, ); defer alloc.free(managed_route); if (std.mem.eql(u8, parent, managed_route)) { return .{ .managed_session = .{ .managed_log_name = try alloc.dupe(u8, basename), } }; } const external_dir = explicit_external_dir orelse return null; if (!std.mem.eql(u8, parent, external_dir)) return null; return .{ .external = .{ .path = try alloc.dupe(u8, legacy_path), } }; } test "managed and external background log storage remain distinct" { const alloc = std.testing.allocator; var managed = LogStorage{ .managed_session = .{ .managed_log_name = try alloc.dupe(u8, "managed.log"), }, }; defer managed.deinit(alloc); var external = LogStorage{ .external = .{ .path = try alloc.dupe(u8, "/tmp/managed.log"), }, }; defer external.deinit(alloc); switch (managed) { .managed_session => |value| try std.testing.expectEqualStrings( "managed.log", value.managed_log_name, ), .external => return error.ExpectedManagedSessionStorage, } switch (external) { .external => |value| try std.testing.expectEqualStrings( "/tmp/managed.log", value.path, ), .managed_session => return error.ExpectedExternalStorage, } } test "background log storage codec preserves managed and external identity" { const alloc = std.testing.allocator; const cases = [_]LogStorage{ .{ .managed_session = .{ .managed_log_name = @constCast("managed.log"), } }, .{ .external = .{ .path = @constCast("/tmp/external.log"), } }, }; for (cases) |storage| { const encoded = try renderLogStorageJson(alloc, storage); defer alloc.free(encoded); var decoded = try parseLogStorageJson(alloc, encoded); defer decoded.deinit(alloc); switch (storage) { .managed_session => |expected| switch (decoded) { .managed_session => |actual| try std.testing.expectEqualStrings( expected.managed_log_name, actual.managed_log_name, ), .external => return error.ExpectedManagedSessionStorage, }, .external => |expected| switch (decoded) { .external => |actual| try std.testing.expectEqualStrings( expected.path, actual.path, ), .managed_session => return error.ExpectedExternalStorage, }, } } } test "legacy log path maps to managed only for exact display parent" { const alloc = std.testing.allocator; var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); try tmp.dir.createDir( io_mod.getIo(), "session-logs", std.Io.File.Permissions.fromMode(0o700), ); try tmp.dir.createDir( io_mod.getIo(), "external-logs", std.Io.File.Permissions.fromMode(0o700), ); const managed_dir = try io_mod.dirRealpathAlloc( alloc, tmp.dir, "session-logs", ); defer alloc.free(managed_dir); const external_dir = try io_mod.dirRealpathAlloc( alloc, tmp.dir, "external-logs", ); defer alloc.free(external_dir); var capability = try session_child_store.SessionChildCapability.initLegacyRoute( alloc, managed_dir, .background_logs, .read_only, ); defer capability.deinit(); const managed_path = try std.fs.path.join( alloc, &.{ managed_dir, "managed.log" }, ); defer alloc.free(managed_path); var managed = (try classifyLegacyLogStorage( alloc, &capability, managed_path, external_dir, )).?; defer managed.deinit(alloc); switch (managed) { .managed_session => |value| try std.testing.expectEqualStrings( "managed.log", value.managed_log_name, ), .external => return error.ExpectedManagedSessionStorage, } const external_path = try std.fs.path.join( alloc, &.{ external_dir, "external.log" }, ); defer alloc.free(external_path); var external = (try classifyLegacyLogStorage( alloc, &capability, external_path, external_dir, )).?; defer external.deinit(alloc); switch (external) { .external => |value| try std.testing.expectEqualStrings( external_path, value.path, ), .managed_session => return error.ExpectedExternalStorage, } const unauthorized = try std.fs.path.join( alloc, &.{ external_dir, "..", "other.log" }, ); defer alloc.free(unauthorized); try std.testing.expect((try classifyLegacyLogStorage( alloc, &capability, unauthorized, external_dir, )) == null); } test "managed background read only absence does not create route" { const alloc = std.testing.allocator; var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); try tmp.dir.createDir( io_mod.getIo(), "session", std.Io.File.Permissions.fromMode(0o700), ); var session_dir = try tmp.dir.openDir(io_mod.getIo(), "session", .{ .iterate = true, .follow_symlinks = false, }); defer session_dir.close(io_mod.getIo()); const session_path = try io_mod.dirRealpathAlloc( alloc, tmp.dir, "session", ); defer alloc.free(session_path); var capability = try session_child_store.SessionChildCapability.initForTesting( alloc, session_dir, session_path, .read_only, .{}, ); defer capability.deinit(); const store = Store.initManaged(&capability); try std.testing.expectError( error.BackgroundRecordNotFound, store.load(alloc, 1), ); var records = try store.list(alloc); defer records.deinit(alloc); try std.testing.expectEqual(@as(usize, 0), records.items.len); try std.testing.expectError( error.FileNotFound, session_dir.statFile(io_mod.getIo(), "background", .{}), ); } pub const TaskState = process_supervisor.TaskState; pub const Record = struct { id: u64, background_record_id: ?StableBackgroundRecordId = null, process_token: ?[]u8 = null, pid: []u8, command: []u8, cwd: []u8, log_path: []u8, log_storage: ?LogStorage = null, expect_url: bool, server_url: ?[]u8 = null, started_at_ms: i64, updated_at_ms: i64, exit_code: ?i32 = null, state: TaskState, diagnostic: ?[]u8 = null, pub fn deinit(self: *Record, alloc: Allocator) void { alloc.free(self.pid); if (self.process_token) |process_token| alloc.free(process_token); alloc.free(self.command); alloc.free(self.cwd); alloc.free(self.log_path); if (self.log_storage) |*storage| storage.deinit(alloc); if (self.server_url) |url| alloc.free(url); if (self.diagnostic) |diagnostic| alloc.free(diagnostic); self.* = undefined; } pub fn fromTaskSnapshot(alloc: Allocator, snapshot: process_supervisor.TaskSnapshot, updated_at_ms: i64) !Record { const pid = try alloc.dupe(u8, snapshot.pid); errdefer alloc.free(pid); const command = try alloc.dupe(u8, snapshot.command); errdefer alloc.free(command); const cwd = try alloc.dupe(u8, snapshot.cwd); errdefer alloc.free(cwd); const log_path = try alloc.dupe(u8, snapshot.log_path); errdefer alloc.free(log_path); var server_url: ?[]u8 = null; errdefer if (server_url) |url| alloc.free(url); if (snapshot.server_url) |url| { server_url = try alloc.dupe(u8, url); } return .{ .id = snapshot.durable_record_id orelse snapshot.id, .background_record_id = snapshot.background_record_id, .process_token = if (snapshot.process_token) |token| try alloc.dupe(u8, token.view()) else null, .pid = pid, .command = command, .cwd = cwd, .log_path = log_path, .log_storage = if (snapshot.managed_log_name) |name| .{ .managed_session = .{ .managed_log_name = try alloc.dupe(u8, name), } } else .{ .external = .{ .path = try alloc.dupe(u8, snapshot.log_path), } }, .expect_url = snapshot.expect_url, .server_url = server_url, .started_at_ms = snapshot.started_at_ms, .updated_at_ms = updated_at_ms, .exit_code = snapshot.exit_code, .state = snapshot.state, }; } }; pub const Store = struct { capability: *session_child_store.SessionChildCapability, owned_capability: ?*session_child_store.SessionChildCapability = null, display_route_path: ?[]u8 = null, pub fn initManaged( capability: *session_child_store.SessionChildCapability, ) Store { return .{ .capability = capability }; } /// Transitional legacy-only constructor retained until adapter conversion. pub fn initWithDir(alloc: Allocator, dir_path: []const u8) !Store { const owned = try alloc.create(session_child_store.SessionChildCapability); errdefer alloc.destroy(owned); owned.* = try session_child_store.SessionChildCapability.initLegacyRoute( alloc, dir_path, .background_records, .writable, ); const display_route_path = try alloc.dupe(u8, dir_path); errdefer alloc.free(display_route_path); return .{ .capability = owned, .owned_capability = owned, .display_route_path = display_route_path, }; } pub fn initReadOnlyWithDir( alloc: Allocator, dir_path: []const u8, ) !Store { const owned = try alloc.create( session_child_store.SessionChildCapability, ); errdefer alloc.destroy(owned); owned.* = try session_child_store.SessionChildCapability.initLegacyRoute( alloc, dir_path, .background_records, .read_only, ); const display_route_path = try alloc.dupe(u8, dir_path); errdefer alloc.free(display_route_path); return .{ .capability = owned, .owned_capability = owned, .display_route_path = display_route_path, }; } pub fn deinit(self: *Store, alloc: Allocator) void { if (self.owned_capability) |owned| { owned.deinit(); alloc.destroy(owned); } if (self.display_route_path) |path| alloc.free(path); self.* = undefined; } pub fn sameBacking(self: Store, other: Store) bool { if (self.display_route_path) |left| { const right = other.display_route_path orelse return false; return std.mem.eql(u8, left, right); } return other.display_route_path == null and self.capability == other.capability; } pub fn nextId(self: Store) !u64 { var max_id: u64 = 0; var entries = try self.capability.iterate( std.heap.c_allocator, .background_records, ); defer entries.deinit(); for (entries.names) |name| { if (!std.mem.endsWith(u8, name, ".json")) continue; const basename = name[0 .. name.len - ".json".len]; const id = std.fmt.parseUnsigned(u64, basename, 10) catch continue; max_id = @max(max_id, id); } return max_id + 1; } pub fn saveTaskSnapshot(self: Store, alloc: Allocator, snapshot: process_supervisor.TaskSnapshot) !void { var record = try Record.fromTaskSnapshot(alloc, snapshot, io_mod.milliTimestamp()); defer record.deinit(alloc); try self.saveRecord(alloc, record); } pub fn saveRecord(self: Store, alloc: Allocator, record: Record) !void { const name = try recordName(alloc, record.id); defer alloc.free(name); try self.validateIndeterminateCurrent(alloc); const text = try renderRecordJson(alloc, record); defer alloc.free(text); var entry = try self.capability.atomicReplace( alloc, .background_records,