diff --git a/api/proto/beastdb.pb.go b/api/proto/beastdb.pb.go index 9152404..58b114c 100644 --- a/api/proto/beastdb.pb.go +++ b/api/proto/beastdb.pb.go @@ -544,9 +544,12 @@ func (x *KeyValueResponse) GetValue() []byte { } type StreamWALRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - ReplicaId string `protobuf:"bytes,1,opt,name=replica_id,json=replicaId,proto3" json:"replica_id,omitempty"` - FromLsn uint64 `protobuf:"varint,2,opt,name=from_lsn,json=fromLsn,proto3" json:"from_lsn,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + ReplicaId string `protobuf:"bytes,1,opt,name=replica_id,json=replicaId,proto3" json:"replica_id,omitempty"` + FromLsn uint64 `protobuf:"varint,2,opt,name=from_lsn,json=fromLsn,proto3" json:"from_lsn,omitempty"` + // Highest leader epoch the follower has seen. A leader that sees a higher + // epoch than its own steps down instead of serving. + Epoch uint64 `protobuf:"varint,3,opt,name=epoch,proto3" json:"epoch,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -595,12 +598,22 @@ func (x *StreamWALRequest) GetFromLsn() uint64 { return 0 } +func (x *StreamWALRequest) GetEpoch() uint64 { + if x != nil { + return x.Epoch + } + return 0 +} + type WALRecordMessage struct { - state protoimpl.MessageState `protogen:"open.v1"` - Lsn uint64 `protobuf:"varint,1,opt,name=lsn,proto3" json:"lsn,omitempty"` - OpType uint32 `protobuf:"varint,2,opt,name=op_type,json=opType,proto3" json:"op_type,omitempty"` - Key []byte `protobuf:"bytes,3,opt,name=key,proto3" json:"key,omitempty"` - Value []byte `protobuf:"bytes,4,opt,name=value,proto3" json:"value,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + Lsn uint64 `protobuf:"varint,1,opt,name=lsn,proto3" json:"lsn,omitempty"` + OpType uint32 `protobuf:"varint,2,opt,name=op_type,json=opType,proto3" json:"op_type,omitempty"` + Key []byte `protobuf:"bytes,3,opt,name=key,proto3" json:"key,omitempty"` + Value []byte `protobuf:"bytes,4,opt,name=value,proto3" json:"value,omitempty"` + // Epoch of the leader that produced this record; followers reject lower + // epochs than their own. + Epoch uint64 `protobuf:"varint,5,opt,name=epoch,proto3" json:"epoch,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -663,10 +676,18 @@ func (x *WALRecordMessage) GetValue() []byte { return nil } +func (x *WALRecordMessage) GetEpoch() uint64 { + if x != nil { + return x.Epoch + } + return 0 +} + type ReplicationAck struct { state protoimpl.MessageState `protogen:"open.v1"` ReplicaId string `protobuf:"bytes,1,opt,name=replica_id,json=replicaId,proto3" json:"replica_id,omitempty"` AcknowledgedLsn uint64 `protobuf:"varint,2,opt,name=acknowledged_lsn,json=acknowledgedLsn,proto3" json:"acknowledged_lsn,omitempty"` + Epoch uint64 `protobuf:"varint,3,opt,name=epoch,proto3" json:"epoch,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -715,9 +736,19 @@ func (x *ReplicationAck) GetAcknowledgedLsn() uint64 { return 0 } +func (x *ReplicationAck) GetEpoch() uint64 { + if x != nil { + return x.Epoch + } + return 0 +} + type AckResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + // The leader's epoch and current LSN, so an idle follower can tell whether it is caught up. + Epoch uint64 `protobuf:"varint,2,opt,name=epoch,proto3" json:"epoch,omitempty"` + LeaderLsn uint64 `protobuf:"varint,3,opt,name=leader_lsn,json=leaderLsn,proto3" json:"leader_lsn,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -759,6 +790,127 @@ func (x *AckResponse) GetSuccess() bool { return false } +func (x *AckResponse) GetEpoch() uint64 { + if x != nil { + return x.Epoch + } + return 0 +} + +func (x *AckResponse) GetLeaderLsn() uint64 { + if x != nil { + return x.LeaderLsn + } + return 0 +} + +type FenceRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // New epoch the candidate will lead at; must exceed the leader's epoch. + Epoch uint64 `protobuf:"varint,1,opt,name=epoch,proto3" json:"epoch,omitempty"` + // Replica asking to be promoted. Only one candidate can fence a given epoch. + CandidateId string `protobuf:"bytes,2,opt,name=candidate_id,json=candidateId,proto3" json:"candidate_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FenceRequest) Reset() { + *x = FenceRequest{} + mi := &file_beastdb_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FenceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FenceRequest) ProtoMessage() {} + +func (x *FenceRequest) ProtoReflect() protoreflect.Message { + mi := &file_beastdb_proto_msgTypes[12] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FenceRequest.ProtoReflect.Descriptor instead. +func (*FenceRequest) Descriptor() ([]byte, []int) { + return file_beastdb_proto_rawDescGZIP(), []int{12} +} + +func (x *FenceRequest) GetEpoch() uint64 { + if x != nil { + return x.Epoch + } + return 0 +} + +func (x *FenceRequest) GetCandidateId() string { + if x != nil { + return x.CandidateId + } + return "" +} + +type FenceResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Final LSN of the fenced leader: it accepts no writes beyond this. + FinalLsn uint64 `protobuf:"varint,1,opt,name=final_lsn,json=finalLsn,proto3" json:"final_lsn,omitempty"` + Epoch uint64 `protobuf:"varint,2,opt,name=epoch,proto3" json:"epoch,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FenceResponse) Reset() { + *x = FenceResponse{} + mi := &file_beastdb_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FenceResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FenceResponse) ProtoMessage() {} + +func (x *FenceResponse) ProtoReflect() protoreflect.Message { + mi := &file_beastdb_proto_msgTypes[13] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FenceResponse.ProtoReflect.Descriptor instead. +func (*FenceResponse) Descriptor() ([]byte, []int) { + return file_beastdb_proto_rawDescGZIP(), []int{13} +} + +func (x *FenceResponse) GetFinalLsn() uint64 { + if x != nil { + return x.FinalLsn + } + return 0 +} + +func (x *FenceResponse) GetEpoch() uint64 { + if x != nil { + return x.Epoch + } + return 0 +} + type BatchOperation struct { state protoimpl.MessageState `protogen:"open.v1"` OpType BatchOpType `protobuf:"varint,1,opt,name=op_type,json=opType,proto3,enum=beastdb.v1.BatchOpType" json:"op_type,omitempty"` @@ -770,7 +922,7 @@ type BatchOperation struct { func (x *BatchOperation) Reset() { *x = BatchOperation{} - mi := &file_beastdb_proto_msgTypes[12] + mi := &file_beastdb_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -782,7 +934,7 @@ func (x *BatchOperation) String() string { func (*BatchOperation) ProtoMessage() {} func (x *BatchOperation) ProtoReflect() protoreflect.Message { - mi := &file_beastdb_proto_msgTypes[12] + mi := &file_beastdb_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -795,7 +947,7 @@ func (x *BatchOperation) ProtoReflect() protoreflect.Message { // Deprecated: Use BatchOperation.ProtoReflect.Descriptor instead. func (*BatchOperation) Descriptor() ([]byte, []int) { - return file_beastdb_proto_rawDescGZIP(), []int{12} + return file_beastdb_proto_rawDescGZIP(), []int{14} } func (x *BatchOperation) GetOpType() BatchOpType { @@ -831,7 +983,7 @@ type BatchWriteRequest struct { func (x *BatchWriteRequest) Reset() { *x = BatchWriteRequest{} - mi := &file_beastdb_proto_msgTypes[13] + mi := &file_beastdb_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -843,7 +995,7 @@ func (x *BatchWriteRequest) String() string { func (*BatchWriteRequest) ProtoMessage() {} func (x *BatchWriteRequest) ProtoReflect() protoreflect.Message { - mi := &file_beastdb_proto_msgTypes[13] + mi := &file_beastdb_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -856,7 +1008,7 @@ func (x *BatchWriteRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use BatchWriteRequest.ProtoReflect.Descriptor instead. func (*BatchWriteRequest) Descriptor() ([]byte, []int) { - return file_beastdb_proto_rawDescGZIP(), []int{13} + return file_beastdb_proto_rawDescGZIP(), []int{15} } func (x *BatchWriteRequest) GetOperations() []*BatchOperation { @@ -883,7 +1035,7 @@ type BatchWriteResponse struct { func (x *BatchWriteResponse) Reset() { *x = BatchWriteResponse{} - mi := &file_beastdb_proto_msgTypes[14] + mi := &file_beastdb_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -895,7 +1047,7 @@ func (x *BatchWriteResponse) String() string { func (*BatchWriteResponse) ProtoMessage() {} func (x *BatchWriteResponse) ProtoReflect() protoreflect.Message { - mi := &file_beastdb_proto_msgTypes[14] + mi := &file_beastdb_proto_msgTypes[16] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -908,7 +1060,7 @@ func (x *BatchWriteResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use BatchWriteResponse.ProtoReflect.Descriptor instead. func (*BatchWriteResponse) Descriptor() ([]byte, []int) { - return file_beastdb_proto_rawDescGZIP(), []int{14} + return file_beastdb_proto_rawDescGZIP(), []int{16} } func (x *BatchWriteResponse) GetSuccess() bool { @@ -935,7 +1087,7 @@ type Condition struct { func (x *Condition) Reset() { *x = Condition{} - mi := &file_beastdb_proto_msgTypes[15] + mi := &file_beastdb_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -947,7 +1099,7 @@ func (x *Condition) String() string { func (*Condition) ProtoMessage() {} func (x *Condition) ProtoReflect() protoreflect.Message { - mi := &file_beastdb_proto_msgTypes[15] + mi := &file_beastdb_proto_msgTypes[17] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -960,7 +1112,7 @@ func (x *Condition) ProtoReflect() protoreflect.Message { // Deprecated: Use Condition.ProtoReflect.Descriptor instead. func (*Condition) Descriptor() ([]byte, []int) { - return file_beastdb_proto_rawDescGZIP(), []int{15} + return file_beastdb_proto_rawDescGZIP(), []int{17} } func (x *Condition) GetKind() ConditionKind { @@ -987,7 +1139,7 @@ type KeyCondition struct { func (x *KeyCondition) Reset() { *x = KeyCondition{} - mi := &file_beastdb_proto_msgTypes[16] + mi := &file_beastdb_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -999,7 +1151,7 @@ func (x *KeyCondition) String() string { func (*KeyCondition) ProtoMessage() {} func (x *KeyCondition) ProtoReflect() protoreflect.Message { - mi := &file_beastdb_proto_msgTypes[16] + mi := &file_beastdb_proto_msgTypes[18] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1012,7 +1164,7 @@ func (x *KeyCondition) ProtoReflect() protoreflect.Message { // Deprecated: Use KeyCondition.ProtoReflect.Descriptor instead. func (*KeyCondition) Descriptor() ([]byte, []int) { - return file_beastdb_proto_rawDescGZIP(), []int{16} + return file_beastdb_proto_rawDescGZIP(), []int{18} } func (x *KeyCondition) GetKey() uint64 { @@ -1060,22 +1212,34 @@ const file_beastdb_proto_rawDesc = "" + "\x05limit\x18\x03 \x01(\rR\x05limit\":\n" + "\x10KeyValueResponse\x12\x10\n" + "\x03key\x18\x01 \x01(\x04R\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\fR\x05value\"L\n" + + "\x05value\x18\x02 \x01(\fR\x05value\"b\n" + "\x10StreamWALRequest\x12\x1d\n" + "\n" + "replica_id\x18\x01 \x01(\tR\treplicaId\x12\x19\n" + - "\bfrom_lsn\x18\x02 \x01(\x04R\afromLsn\"e\n" + + "\bfrom_lsn\x18\x02 \x01(\x04R\afromLsn\x12\x14\n" + + "\x05epoch\x18\x03 \x01(\x04R\x05epoch\"{\n" + "\x10WALRecordMessage\x12\x10\n" + "\x03lsn\x18\x01 \x01(\x04R\x03lsn\x12\x17\n" + "\aop_type\x18\x02 \x01(\rR\x06opType\x12\x10\n" + "\x03key\x18\x03 \x01(\fR\x03key\x12\x14\n" + - "\x05value\x18\x04 \x01(\fR\x05value\"Z\n" + + "\x05value\x18\x04 \x01(\fR\x05value\x12\x14\n" + + "\x05epoch\x18\x05 \x01(\x04R\x05epoch\"p\n" + "\x0eReplicationAck\x12\x1d\n" + "\n" + "replica_id\x18\x01 \x01(\tR\treplicaId\x12)\n" + - "\x10acknowledged_lsn\x18\x02 \x01(\x04R\x0facknowledgedLsn\"'\n" + + "\x10acknowledged_lsn\x18\x02 \x01(\x04R\x0facknowledgedLsn\x12\x14\n" + + "\x05epoch\x18\x03 \x01(\x04R\x05epoch\"\\\n" + "\vAckResponse\x12\x18\n" + - "\asuccess\x18\x01 \x01(\bR\asuccess\"j\n" + + "\asuccess\x18\x01 \x01(\bR\asuccess\x12\x14\n" + + "\x05epoch\x18\x02 \x01(\x04R\x05epoch\x12\x1d\n" + + "\n" + + "leader_lsn\x18\x03 \x01(\x04R\tleaderLsn\"G\n" + + "\fFenceRequest\x12\x14\n" + + "\x05epoch\x18\x01 \x01(\x04R\x05epoch\x12!\n" + + "\fcandidate_id\x18\x02 \x01(\tR\vcandidateId\"B\n" + + "\rFenceResponse\x12\x1b\n" + + "\tfinal_lsn\x18\x01 \x01(\x04R\bfinalLsn\x12\x14\n" + + "\x05epoch\x18\x02 \x01(\x04R\x05epoch\"j\n" + "\x0eBatchOperation\x120\n" + "\aop_type\x18\x01 \x01(\x0e2\x17.beastdb.v1.BatchOpTypeR\x06opType\x12\x10\n" + "\x03key\x18\x02 \x01(\x04R\x03key\x12\x14\n" + @@ -1111,10 +1275,11 @@ const file_beastdb_proto_rawDesc = "" + "\x06Delete\x12\x19.beastdb.v1.DeleteRequest\x1a\x1a.beastdb.v1.DeleteResponse\x12?\n" + "\x04Scan\x12\x17.beastdb.v1.ScanRequest\x1a\x1c.beastdb.v1.KeyValueResponse0\x01\x12K\n" + "\n" + - "BatchWrite\x12\x1d.beastdb.v1.BatchWriteRequest\x1a\x1e.beastdb.v1.BatchWriteResponse2\x9b\x01\n" + + "BatchWrite\x12\x1d.beastdb.v1.BatchWriteRequest\x1a\x1e.beastdb.v1.BatchWriteResponse2\xd9\x01\n" + "\x12ReplicationService\x12I\n" + "\tStreamWAL\x12\x1c.beastdb.v1.StreamWALRequest\x1a\x1c.beastdb.v1.WALRecordMessage0\x01\x12:\n" + - "\x03Ack\x12\x1a.beastdb.v1.ReplicationAck\x1a\x17.beastdb.v1.AckResponseB2Z0github.com/ChromaBeast/beastdb/api/proto;beastv1b\x06proto3" + "\x03Ack\x12\x1a.beastdb.v1.ReplicationAck\x1a\x17.beastdb.v1.AckResponse\x12<\n" + + "\x05Fence\x12\x18.beastdb.v1.FenceRequest\x1a\x19.beastdb.v1.FenceResponseB2Z0github.com/ChromaBeast/beastdb/api/proto;beastv1b\x06proto3" var ( file_beastdb_proto_rawDescOnce sync.Once @@ -1129,7 +1294,7 @@ func file_beastdb_proto_rawDescGZIP() []byte { } var file_beastdb_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_beastdb_proto_msgTypes = make([]protoimpl.MessageInfo, 17) +var file_beastdb_proto_msgTypes = make([]protoimpl.MessageInfo, 19) var file_beastdb_proto_goTypes = []any{ (BatchOpType)(0), // 0: beastdb.v1.BatchOpType (ConditionKind)(0), // 1: beastdb.v1.ConditionKind @@ -1145,36 +1310,40 @@ var file_beastdb_proto_goTypes = []any{ (*WALRecordMessage)(nil), // 11: beastdb.v1.WALRecordMessage (*ReplicationAck)(nil), // 12: beastdb.v1.ReplicationAck (*AckResponse)(nil), // 13: beastdb.v1.AckResponse - (*BatchOperation)(nil), // 14: beastdb.v1.BatchOperation - (*BatchWriteRequest)(nil), // 15: beastdb.v1.BatchWriteRequest - (*BatchWriteResponse)(nil), // 16: beastdb.v1.BatchWriteResponse - (*Condition)(nil), // 17: beastdb.v1.Condition - (*KeyCondition)(nil), // 18: beastdb.v1.KeyCondition + (*FenceRequest)(nil), // 14: beastdb.v1.FenceRequest + (*FenceResponse)(nil), // 15: beastdb.v1.FenceResponse + (*BatchOperation)(nil), // 16: beastdb.v1.BatchOperation + (*BatchWriteRequest)(nil), // 17: beastdb.v1.BatchWriteRequest + (*BatchWriteResponse)(nil), // 18: beastdb.v1.BatchWriteResponse + (*Condition)(nil), // 19: beastdb.v1.Condition + (*KeyCondition)(nil), // 20: beastdb.v1.KeyCondition } var file_beastdb_proto_depIdxs = []int32{ - 17, // 0: beastdb.v1.PutRequest.condition:type_name -> beastdb.v1.Condition - 17, // 1: beastdb.v1.DeleteRequest.condition:type_name -> beastdb.v1.Condition + 19, // 0: beastdb.v1.PutRequest.condition:type_name -> beastdb.v1.Condition + 19, // 1: beastdb.v1.DeleteRequest.condition:type_name -> beastdb.v1.Condition 0, // 2: beastdb.v1.BatchOperation.op_type:type_name -> beastdb.v1.BatchOpType - 14, // 3: beastdb.v1.BatchWriteRequest.operations:type_name -> beastdb.v1.BatchOperation - 18, // 4: beastdb.v1.BatchWriteRequest.conditions:type_name -> beastdb.v1.KeyCondition + 16, // 3: beastdb.v1.BatchWriteRequest.operations:type_name -> beastdb.v1.BatchOperation + 20, // 4: beastdb.v1.BatchWriteRequest.conditions:type_name -> beastdb.v1.KeyCondition 1, // 5: beastdb.v1.Condition.kind:type_name -> beastdb.v1.ConditionKind - 17, // 6: beastdb.v1.KeyCondition.condition:type_name -> beastdb.v1.Condition + 19, // 6: beastdb.v1.KeyCondition.condition:type_name -> beastdb.v1.Condition 2, // 7: beastdb.v1.BeastDBService.Get:input_type -> beastdb.v1.GetRequest 4, // 8: beastdb.v1.BeastDBService.Put:input_type -> beastdb.v1.PutRequest 6, // 9: beastdb.v1.BeastDBService.Delete:input_type -> beastdb.v1.DeleteRequest 8, // 10: beastdb.v1.BeastDBService.Scan:input_type -> beastdb.v1.ScanRequest - 15, // 11: beastdb.v1.BeastDBService.BatchWrite:input_type -> beastdb.v1.BatchWriteRequest + 17, // 11: beastdb.v1.BeastDBService.BatchWrite:input_type -> beastdb.v1.BatchWriteRequest 10, // 12: beastdb.v1.ReplicationService.StreamWAL:input_type -> beastdb.v1.StreamWALRequest 12, // 13: beastdb.v1.ReplicationService.Ack:input_type -> beastdb.v1.ReplicationAck - 3, // 14: beastdb.v1.BeastDBService.Get:output_type -> beastdb.v1.GetResponse - 5, // 15: beastdb.v1.BeastDBService.Put:output_type -> beastdb.v1.PutResponse - 7, // 16: beastdb.v1.BeastDBService.Delete:output_type -> beastdb.v1.DeleteResponse - 9, // 17: beastdb.v1.BeastDBService.Scan:output_type -> beastdb.v1.KeyValueResponse - 16, // 18: beastdb.v1.BeastDBService.BatchWrite:output_type -> beastdb.v1.BatchWriteResponse - 11, // 19: beastdb.v1.ReplicationService.StreamWAL:output_type -> beastdb.v1.WALRecordMessage - 13, // 20: beastdb.v1.ReplicationService.Ack:output_type -> beastdb.v1.AckResponse - 14, // [14:21] is the sub-list for method output_type - 7, // [7:14] is the sub-list for method input_type + 14, // 14: beastdb.v1.ReplicationService.Fence:input_type -> beastdb.v1.FenceRequest + 3, // 15: beastdb.v1.BeastDBService.Get:output_type -> beastdb.v1.GetResponse + 5, // 16: beastdb.v1.BeastDBService.Put:output_type -> beastdb.v1.PutResponse + 7, // 17: beastdb.v1.BeastDBService.Delete:output_type -> beastdb.v1.DeleteResponse + 9, // 18: beastdb.v1.BeastDBService.Scan:output_type -> beastdb.v1.KeyValueResponse + 18, // 19: beastdb.v1.BeastDBService.BatchWrite:output_type -> beastdb.v1.BatchWriteResponse + 11, // 20: beastdb.v1.ReplicationService.StreamWAL:output_type -> beastdb.v1.WALRecordMessage + 13, // 21: beastdb.v1.ReplicationService.Ack:output_type -> beastdb.v1.AckResponse + 15, // 22: beastdb.v1.ReplicationService.Fence:output_type -> beastdb.v1.FenceResponse + 15, // [15:23] is the sub-list for method output_type + 7, // [7:15] is the sub-list for method input_type 7, // [7:7] is the sub-list for extension type_name 7, // [7:7] is the sub-list for extension extendee 0, // [0:7] is the sub-list for field type_name @@ -1191,7 +1360,7 @@ func file_beastdb_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_beastdb_proto_rawDesc), len(file_beastdb_proto_rawDesc)), NumEnums: 2, - NumMessages: 17, + NumMessages: 19, NumExtensions: 0, NumServices: 2, }, diff --git a/api/proto/beastdb.proto b/api/proto/beastdb.proto index ea65016..c8c5f65 100644 --- a/api/proto/beastdb.proto +++ b/api/proto/beastdb.proto @@ -17,6 +17,8 @@ service BeastDBService { service ReplicationService { rpc StreamWAL (StreamWALRequest) returns (stream WALRecordMessage); rpc Ack (ReplicationAck) returns (AckResponse); + // Fence freezes a leader's writes at a new epoch and reports its final LSN. + rpc Fence (FenceRequest) returns (FenceResponse); } message GetRequest { @@ -66,6 +68,9 @@ message KeyValueResponse { message StreamWALRequest { string replica_id = 1; uint64 from_lsn = 2; + // Highest leader epoch the follower has seen. A leader that sees a higher + // epoch than its own steps down instead of serving. + uint64 epoch = 3; } message WALRecordMessage { @@ -73,15 +78,35 @@ message WALRecordMessage { uint32 op_type = 2; bytes key = 3; bytes value = 4; + // Epoch of the leader that produced this record; followers reject lower + // epochs than their own. + uint64 epoch = 5; } message ReplicationAck { string replica_id = 1; uint64 acknowledged_lsn = 2; + uint64 epoch = 3; } message AckResponse { bool success = 1; + // The leader's epoch and current LSN, so an idle follower can tell whether it is caught up. + uint64 epoch = 2; + uint64 leader_lsn = 3; +} + +message FenceRequest { + // New epoch the candidate will lead at; must exceed the leader's epoch. + uint64 epoch = 1; + // Replica asking to be promoted. Only one candidate can fence a given epoch. + string candidate_id = 2; +} + +message FenceResponse { + // Final LSN of the fenced leader: it accepts no writes beyond this. + uint64 final_lsn = 1; + uint64 epoch = 2; } enum BatchOpType { diff --git a/api/proto/beastdb_grpc.pb.go b/api/proto/beastdb_grpc.pb.go index 6680805..48d13a9 100644 --- a/api/proto/beastdb_grpc.pb.go +++ b/api/proto/beastdb_grpc.pb.go @@ -283,6 +283,7 @@ var BeastDBService_ServiceDesc = grpc.ServiceDesc{ const ( ReplicationService_StreamWAL_FullMethodName = "/beastdb.v1.ReplicationService/StreamWAL" ReplicationService_Ack_FullMethodName = "/beastdb.v1.ReplicationService/Ack" + ReplicationService_Fence_FullMethodName = "/beastdb.v1.ReplicationService/Fence" ) // ReplicationServiceClient is the client API for ReplicationService service. @@ -293,6 +294,8 @@ const ( type ReplicationServiceClient interface { StreamWAL(ctx context.Context, in *StreamWALRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[WALRecordMessage], error) Ack(ctx context.Context, in *ReplicationAck, opts ...grpc.CallOption) (*AckResponse, error) + // Fence freezes a leader's writes at a new epoch and reports its final LSN. + Fence(ctx context.Context, in *FenceRequest, opts ...grpc.CallOption) (*FenceResponse, error) } type replicationServiceClient struct { @@ -332,6 +335,16 @@ func (c *replicationServiceClient) Ack(ctx context.Context, in *ReplicationAck, return out, nil } +func (c *replicationServiceClient) Fence(ctx context.Context, in *FenceRequest, opts ...grpc.CallOption) (*FenceResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(FenceResponse) + err := c.cc.Invoke(ctx, ReplicationService_Fence_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + // ReplicationServiceServer is the server API for ReplicationService service. // All implementations must embed UnimplementedReplicationServiceServer // for forward compatibility. @@ -340,6 +353,8 @@ func (c *replicationServiceClient) Ack(ctx context.Context, in *ReplicationAck, type ReplicationServiceServer interface { StreamWAL(*StreamWALRequest, grpc.ServerStreamingServer[WALRecordMessage]) error Ack(context.Context, *ReplicationAck) (*AckResponse, error) + // Fence freezes a leader's writes at a new epoch and reports its final LSN. + Fence(context.Context, *FenceRequest) (*FenceResponse, error) mustEmbedUnimplementedReplicationServiceServer() } @@ -356,6 +371,9 @@ func (UnimplementedReplicationServiceServer) StreamWAL(*StreamWALRequest, grpc.S func (UnimplementedReplicationServiceServer) Ack(context.Context, *ReplicationAck) (*AckResponse, error) { return nil, status.Error(codes.Unimplemented, "method Ack not implemented") } +func (UnimplementedReplicationServiceServer) Fence(context.Context, *FenceRequest) (*FenceResponse, error) { + return nil, status.Error(codes.Unimplemented, "method Fence not implemented") +} func (UnimplementedReplicationServiceServer) mustEmbedUnimplementedReplicationServiceServer() {} func (UnimplementedReplicationServiceServer) testEmbeddedByValue() {} @@ -406,6 +424,24 @@ func _ReplicationService_Ack_Handler(srv interface{}, ctx context.Context, dec f return interceptor(ctx, in, info, handler) } +func _ReplicationService_Fence_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(FenceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ReplicationServiceServer).Fence(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ReplicationService_Fence_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ReplicationServiceServer).Fence(ctx, req.(*FenceRequest)) + } + return interceptor(ctx, in, info, handler) +} + // ReplicationService_ServiceDesc is the grpc.ServiceDesc for ReplicationService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -417,6 +453,10 @@ var ReplicationService_ServiceDesc = grpc.ServiceDesc{ MethodName: "Ack", Handler: _ReplicationService_Ack_Handler, }, + { + MethodName: "Fence", + Handler: _ReplicationService_Fence_Handler, + }, }, Streams: []grpc.StreamDesc{ { diff --git a/cmd/beastctl/backup_server_compat_test.go b/cmd/beastctl/backup_server_compat_test.go new file mode 100644 index 0000000..c2f9c53 --- /dev/null +++ b/cmd/beastctl/backup_server_compat_test.go @@ -0,0 +1,53 @@ +package main + +import ( + "fmt" + "path/filepath" + "testing" + + "github.com/ChromaBeast/beastdb/internal/api" + "github.com/ChromaBeast/beastdb/internal/backup" +) + +// A backup made by the running server (internal/backup) must be accepted by the +// offline tools: restore is still a beastctl step. +func TestServerMadeBackupVerifiesAndRestoresWithBeastctl(t *testing.T) { + dir := t.TempDir() + eng, err := api.NewEngine(filepath.Join(dir, "beast.bin"), filepath.Join(dir, "beast.wal"), 32) + if err != nil { + t.Fatal(err) + } + defer eng.Close() + const n = 150 + for i := uint64(1); i <= n; i++ { + if err := eng.Put(i, []byte(fmt.Sprintf("payload-%d", i))); err != nil { + t.Fatal(err) + } + } + + info, err := backup.Create(eng, filepath.Join(dir, "backups"), backup.KindManual, "test") + if err != nil { + t.Fatal(err) + } + bundle := filepath.Join(dir, "backups", info.ID) + + if err := runVerify(bundle); err != nil { + t.Fatalf("beastctl verify rejected a server-made backup: %v", err) + } + res, err := runRestore(bundle, filepath.Join(dir, "restored"), false) + if err != nil { + t.Fatalf("beastctl restore failed on a server-made backup: %v", err) + } + restored, err := api.NewEngine(filepath.Join(dir, "restored", "beast.bin"), filepath.Join(dir, "restored", "beast.wal"), 32) + if err != nil { + t.Fatal(err) + } + defer restored.Close() + for i := uint64(1); i <= n; i++ { + v, ok, err := restored.Get(i) + if err != nil || !ok || string(v) != fmt.Sprintf("payload-%d", i) { + t.Fatalf("record %d after restore: %q %v %v", i, v, ok, err) + } + } + _ = res +} diff --git a/cmd/server/main.go b/cmd/server/main.go index 8608a8c..a85f69f 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -14,8 +14,10 @@ import ( "time" "github.com/ChromaBeast/beastdb/internal/api" + "github.com/ChromaBeast/beastdb/internal/backup" "github.com/ChromaBeast/beastdb/internal/logbuf" "github.com/ChromaBeast/beastdb/internal/replication" + "github.com/ChromaBeast/beastdb/internal/runtimecfg" "github.com/ChromaBeast/beastdb/internal/web" "github.com/ChromaBeast/beastdb/internal/web/handler" "google.golang.org/grpc" @@ -72,15 +74,26 @@ func main() { if err != nil { log.Fatalf("Failed to initialize database engine: %v", err) } - if *role != "leader" { - engine.SetReadOnly(true) - log.Printf("🔒 Node running in follower mode: engine write path set to read-only.") - } ckptCtx, stopCkpt := context.WithCancel(context.Background()) defer stopCkpt() engine.StartAutoCheckpoint(ckptCtx, 10*time.Second, int64(*walCheckpointMB)<<20) + // Settings an admin can change in Studio without a restart; saved values + // in runtime-settings.json override the matching flags. + runtimeCfg, rtErr := runtimecfg.Load(*dataDir, runtimecfg.Values{WALCheckpointMB: *walCheckpointMB, LoginMaxAttempts: 5, LoginWindowSeconds: 30, BackupKeep: 7}) + if rtErr != nil { + log.Printf("Warning: ignoring runtime settings file: %v", rtErr) + } + runtimeCfg.Subscribe(func(v runtimecfg.Values) { engine.SetCheckpointThreshold(int64(v.WALCheckpointMB) << 20) }) + + // Server-managed backups (create, verify, download, optional schedule). + backups := backup.NewManager(engine, filepath.Join(*dataDir, "backups")) + runtimeCfg.Subscribe(func(v runtimecfg.Values) { + backups.Configure(time.Duration(v.BackupIntervalMinutes)*time.Minute, v.BackupKeep) + }) + backups.Start(ckptCtx) + tokenStore := initTokenStore(engine) grpcOpts := api.MessageSizeServerOptions() // Metrics interceptors run first so they also observe auth rejections. @@ -105,12 +118,25 @@ func main() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() + // Every node serves the replication service; it hands out WAL only while the + // node is a leader (or a fenced former leader). The persisted epoch and role + // in /cluster-state.json decide the starting role, so a restart + // cannot undo a promotion or un-fence an old leader. + flagRole := replication.RoleFollower if *role == "leader" { - repServer := replication.NewLeaderServer(engine.WALPath()) - repServer.RegisterService(grpcServer.RawServer()) - engine.SetCommitObserver(repServer) - replicaSource = repServer - } else { + flagRole = replication.RoleLeader + } + repServer := replication.NewLeaderServer(engine.WALPath()) + repServer.RegisterService(grpcServer.RawServer()) + cluster, err := replication.NewCluster(engine, *dataDir, *replicaID, flagRole, repServer) + if err != nil { + log.Fatalf("FATAL: %v", err) + } + replicaSource = repServer + clusterControl = cluster + switch cluster.Role() { + case replication.RoleFollower: + log.Printf("🔒 Node running in follower mode at epoch %d: engine write path set to read-only.", cluster.Epoch()) clientCreds, err := loadClientTLS(*tlsCert, *tlsKey, *tlsCA) if err != nil { log.Fatalf("Failed to initialize follower client TLS: %v", err) @@ -120,19 +146,9 @@ func main() { log.Fatalf("Failed to dial leader node: %v", err) } defer conn.Close() - followerClient := replication.NewFollowerClient(*replicaID, engine, conn) - go func() { - for ctx.Err() == nil { - if err := followerClient.SyncLoop(ctx); err != nil && ctx.Err() == nil { - log.Printf("Follower sync stream dropped: %v. Reconnecting in 1s...", err) - select { - case <-time.After(time.Second): - case <-ctx.Done(): - return - } - } - } - }() + cluster.RunFollower(ctx, replication.NewFollowerClient(*replicaID, engine, conn)) + case replication.RoleFenced: + log.Printf("🔒 Node was fenced at epoch %d: it stays read-only. Wipe its data directory to rejoin it as a follower.", cluster.Epoch()) } var partitions []handler.PartitionEntry @@ -159,7 +175,9 @@ func main() { logs.AddSecret(nonDefault(*adminPassword)) // dev mode generates its password after flag parsing webOpts := web.Options{ - Logs: logs, + Logs: logs, + Runtime: runtimeCfg, + Backups: backups, Settings: handler.RuntimeSettings{ GRPCAddr: grpcBind, WebAddr: *webAddr, DataDir: *dataDir, PoolSize: *poolSize, WALCheckpointMB: *walCheckpointMB, ReplicaID: *replicaID, LeaderAddr: *leaderAddr, @@ -168,6 +186,7 @@ func main() { PartitionConfigPath: cfgPath, DevMode: *devMode, InsecureAuthOverride: *insecureAuth, }, } + webOpts.Cluster = clusterControl stopWeb := startWebServer(*webAddr, *dataDir, *role, *adminPassword, engine, partitions, tokenStore, webOpts, *apiToken, os.Getenv("BEASTDB_METRICS_TOKEN")) defer stopWeb() diff --git a/cmd/server/web.go b/cmd/server/web.go index 757a715..1526a72 100644 --- a/cmd/server/web.go +++ b/cmd/server/web.go @@ -4,6 +4,7 @@ import ( "log" "github.com/ChromaBeast/beastdb/internal/api" + "github.com/ChromaBeast/beastdb/internal/audit" "github.com/ChromaBeast/beastdb/internal/web" "github.com/ChromaBeast/beastdb/internal/web/auth" "github.com/ChromaBeast/beastdb/internal/web/handler" @@ -14,6 +15,9 @@ import ( // It stays nil on followers (they do not see other replicas). var replicaSource handler.ReplicationSource +// clusterControl is set by main; it carries the node's epoch, role and promotion. +var clusterControl handler.ClusterControl + func startWebServer(webAddr, dataDir, role, adminPassword string, engine *api.Engine, partitions []handler.PartitionEntry, tokenStore *auth.TokenStore, extra web.Options, metricsTokens ...string) func() { if webAddr == "" { return func() {} @@ -24,6 +28,12 @@ func startWebServer(webAddr, dataDir, role, adminPassword string, engine *api.En log.Fatalf("Failed to initialize session secret: %v", secretErr) } + auditLog, auditErr := audit.Open(dataDir) + if auditErr != nil { + log.Fatalf("Failed to open audit log: %v", auditErr) + } + extra.Audit = auditLog + webSrv, webErr := web.NewServerWithOptions(webAddr, engine, secret, role, Version, partitions, withBase(withReplication(extra), dataDir, metricsTokens), tokenStore) if webErr != nil { log.Fatalf("Failed to create web server: %v", webErr) @@ -47,6 +57,7 @@ func startWebServer(webAddr, dataDir, role, adminPassword string, engine *api.En go webSrv.Serve() return func() { webSrv.Shutdown() + _ = auditLog.Close() } } diff --git a/docs/adr/0002-replication-membership-and-manual-promotion.md b/docs/adr/0002-replication-membership-and-manual-promotion.md new file mode 100644 index 0000000..5351f7a --- /dev/null +++ b/docs/adr/0002-replication-membership-and-manual-promotion.md @@ -0,0 +1,181 @@ +# ADR 0002: Replication membership and manual promotion + +**Status:** proposed, 13 November 2026. The first slice (sections marked *built*) is +implemented; the rest is design only. Items under "Decisions for the owner" are open. + +## Context + +Replication today is one leader streaming its WAL to followers over gRPC. Roles come from the +`-role` flag. A follower finds its leader from `-leader-addr`; a leader learns of followers only +while they hold a stream open, and forgets them on restart. Nothing records which node is the +leader, nothing stops two nodes from both accepting writes, and there is no way to move +leadership short of editing flags and restarting. + +The Studio design shows *Add replica* and *Promote replica*. Promotion without fencing creates +two writable leaders (split-brain): both acknowledge writes the other never sees, and there is +no way to merge them afterwards. So fencing comes first and the button comes last. + +BeastDB replicates asynchronously: a write is acknowledged to the client once it is fsynced on +the leader, before any follower has it. That decides what promotion can promise (below). + +## Decision + +Manual promotion first. A human decides to move leadership; the system makes that move safe +when the old leader is reachable and loudly unsafe when it is not. Automatic election is out of +scope and is described at the end only to say what it would still need. + +### Membership: static, no protocol + +- A follower knows its leader from `-leader-addr`. A leader knows its replicas from live streams + (unchanged). There is no cluster-wide member list and no discovery. +- The one piece of cluster state a node persists is its **epoch** and **role**, in + `/cluster-state.json` (*built*): `{epoch, role, servedEpoch, fencedFor, promotedAtLSN}` + written by temp file, fsync, rename. The file is the source of truth; `-role` is only the + starting hint for a node with no file. +- After a promotion the operator repoints the other followers (restart with the new + `-leader-addr`) and clients. *Add replica* stays "run this command" in Studio; there is no + join protocol. + +### Epoch (term) + +- An epoch is a number that only increases. The first leader runs at epoch 1. Every promotion + runs at the previous epoch + 1. A node never lowers its epoch (`StateStore.Set` refuses). +- **Carried in replication RPCs (*built*):** `StreamWALRequest.epoch` (the highest epoch the + follower has seen), `WALRecordMessage.epoch` (the epoch the record was written at), + `ReplicationAck.epoch`, `AckResponse.epoch` and `leader_lsn`, and the new `Fence` RPC. +- **Not yet in WAL frames.** The slice stamps the epoch on records as they are streamed but + does not write it into the WAL. The next step is an `OpEpoch` marker record appended when a + node starts leading, so every record's epoch is derivable from the log. That changes the WAL + format (recovery, `beastctl`, a version bump) and is deferred; see decisions. What it buys: + a follower could prove its log is a prefix of the leader's at a given `(LSN, epoch)` and + could detect equal-LSN divergence. Without it, the slice refuses on the signals it does have + (below) and requires a rebuild after any unfenced promotion. + +### Fencing the old leader + +Fencing makes the old leader stop accepting writes *before* the new one starts. + +- **`Fence(epoch, candidate)` RPC (*built*).** The candidate calls it on the old leader. The + leader persists `role=fenced, epoch=, fencedFor=`, sets its engine read-only, + and returns its final LSN. It is idempotent for the same candidate and epoch, and refuses a + different candidate at that epoch, a stale epoch, and any node that is not a leader. The + refusal of a second candidate is what stops two followers being promoted at one epoch while + the leader is up. +- **No write can land after the fence returns.** The read-only check runs inside the + group-commit batch under the engine lock (`runBatch`), not only at the API entry, so a write + already queued is refused too. The final LSN is read after the freeze and is exact. +- **Fencing by contact (*built*).** Any replication RPC carrying an epoch higher than the + leader's makes it step down: persist `fenced`, go read-only, refuse the call. So an old + leader that comes back is fenced by the first follower that already knows the new epoch. +- **Persisted.** A fenced node restarts fenced whatever `-role` says. It refuses to start as a + follower (its data may hold writes the new leader never saw): wipe the data directory to + rejoin it. A promoted node restarted with `-role follower` stays leader. A persisted follower + cannot be started as leader by flag; it must be promoted, which bumps the epoch. +- **The fenced leader keeps serving WAL.** The candidate catches up from it after the fence. +- **Retry after an unfenced promotion (*built*).** If the old leader could not be reached, the + new leader keeps calling `Fence` every 2 s until it answers, so a healed partition or a + restarted old leader is stopped without operator action. + +### What a follower needs before promotion + +- **Old leader reachable (the safe path).** Fence, take the final LSN from the reply, wait until + the follower has applied that LSN (default timeout 15 s), then persist the new epoch, stop + following, and open writes, in that order. No acknowledged write is lost: the leader accepted + nothing past the final LSN and the candidate has everything up to it. *Caught up* is therefore + not a safety requirement on this path; it is a UI precondition (below) and a cheap sanity + check. A fence that succeeds but a catch-up that times out leaves the old leader fenced and + the candidate a follower; the promotion can be retried. +- **Old leader unreachable.** Refused with `leader_unreachable` unless the caller passes + `acceptDataLoss`. If forced, the promoted node has only what it had replicated: anything the + old leader acknowledged after that is lost to the cluster, and the old leader may keep taking + writes until the retry above reaches it. The audit entry says so in words. Studio does not + offer this path; it is API-only. +- **Studio gate (*built*).** The Promote button is enabled only when the follower has heard from + its leader within 3 s (record or heartbeat ACK, sent at least once per second) and its applied + LSN equals the leader's last reported LSN. Otherwise it is disabled and shows the reason. +- **Where it runs.** Promotion is performed by the node being promoted, from its own Studio, as + that node holds the leader connection. The leader's replica table does not carry a Promote + button; it says where to go. + +### How clients find the new leader + +*Built:* a write to a read-only or fenced node fails with `FailedPrecondition`; `/api/replication` +on any node reports role and epoch. Clients are expected to hold a list of node addresses and +retry writes elsewhere on that error. *Not built:* a `GetClusterInfo` RPC returning the leader's +advertised address and epoch, and an advertise-address flag. Neither is needed for manual +promotion, and both are needed before clients can follow a leader without configuration. + +### Failure cases + +| Case | What happens | Loss or risk | +|---|---|---| +| Promote, old leader up, follower lagging | Fence, wait for final LSN, then promote. | None. Writers see read-only errors after the fence. | +| Fence ok, catch-up times out | Old leader stays fenced, nothing promoted. Retry is idempotent. | Writes unavailable until a retry succeeds. | +| Fenced for X, X dies before promoting | Y cannot fence at the same epoch (refused by design). | **No supported recovery yet.** Open decision. | +| Partition, old leader alive on its side | Candidate cannot fence; refused unless `acceptDataLoss`. If forced, old leader keeps accepting writes until the partition heals. | Writes acknowledged by the old leader in that window never reach the new leader: **split-brain for the length of the partition.** Stop the old leader first (power, network, process). | +| Old leader returns, never fenced | Restarts as leader at its old epoch. Fenced by the new leader's retry or by any follower already at the new epoch. | Writes it took before being fenced are discarded with its data; wipe it to rejoin. | +| Old leader returns, was fenced | Stays read-only. Refuses `-role follower` on the stale data. | None; rebuild is manual. | +| Crash during promotion | New epoch is persisted before writes open. A crash between restarts as leader at the new epoch, never as follower of the old leader. | None. | +| Two forced promotions with both leaders unreachable | Nothing prevents it: both reach the same epoch. | **Split-brain.** Needs a quorum to prevent. | +| Other followers ahead of the promoted node | Leader refuses a stream whose `from_lsn` is past its own LSN. | Those followers hold writes the new leader lacks and need a rebuild. | +| Equal LSN, different history | **Not detected** in this slice (needs epochs in the WAL). | Mitigated by wiping any node that was fenced or whose promotion was unfenced. | +| Leader crash with acknowledged, unreplicated writes | Replication is async. Fence cannot run on a dead node. | Lost on forced promotion; this is the cost of async replication. | + +### What automatic election would still need + +None of this is built, and until all of it is, nothing here is high availability. + +1. **Membership and quorum:** a persisted peer list and an odd voter count, so a minority + partition cannot elect a leader. +2. **Leader lease:** a leader that stops writing when it cannot reach a majority within a bound, + so an isolated leader fences *itself* without hearing from anyone. +3. **Vote RPC:** grant at most one vote per epoch; grant only to a candidate whose log is at + least as new, compared by `(last epoch, last LSN)`. Needs the epoch in the WAL. +4. **Log matching and truncation (or snapshot rejoin)** for a returning node whose tail + diverged. +5. **Quorum-acknowledged writes:** otherwise election still loses acknowledged writes, and the + system would claim availability while silently losing data. +6. **Client discovery** (`GetClusterInfo`, advertise addresses) and re-pointing of followers. +7. **Fault-injection tests** (partitions, clock pauses, crash at every step) at the scale of + the failure table above, not only the in-process tests below. + +## Consequences + +- Followers reject a leader whose epoch is lower than theirs (`ErrStaleLeader`) and apply + nothing from it. Leaders step down on a higher epoch. Both persist before acting. +- Every node now serves the replication service on gRPC; a node that is not a leader answers + `FailedPrecondition`. `Fence` joins `StreamWAL` and `Ack` in the replication role's allowed + methods. +- New admin endpoint `POST /api/replication/promote` (body `{"acceptDataLoss": false}`), audited + as `replication.promote` with the actor, epoch, whether the old leader was fenced, and for an + unfenced promotion the words "WITHOUT fencing" and "data loss possible". Refusals are audited + too. `GET /api/replication` gains a `cluster` object (role, epoch, lag, `promotable` and + the reason when not). +- Followers send a heartbeat ACK once a second even when idle, a small constant load per replica. +- Tests (in-process nodes, real gRPC): lossless promotion under concurrent writers, idempotent + and exclusive fencing, step-down on a higher epoch (both RPCs) and across a restart, stale + leader rejected by a follower, partition then forced promotion then heal, old leader restarted + after promotion, crash between persisting the epoch and opening writes, lagging follower with + catch-up timeout then retry, follower ahead of leader, state file torn write and corruption. +- Studio: the Promote action and its reasons; a Studio Replication page on a leader shows + replicas as before. + +## Decisions for the owner + +1. **WAL epoch marker.** Add an `OpEpoch` record (WAL format version bump, recovery and + `beastctl` changes) now, or after promotion has been used in practice? Needed for automatic + election and for detecting equal-LSN divergence. +2. **Stuck fence.** If a leader is fenced for a candidate that then dies, no other node can be + promoted. Options: an operator-only "re-fence at a higher epoch" that names the abandoned + candidate, or a documented wipe-and-reseed of the old leader. Not built. +3. **Rejoining a fenced node.** Keep "wipe and rejoin", or allow a fenced node to rejoin as a + follower when the new leader can prove (via item 1) that its log is a prefix? +4. **Forced promotion in Studio.** Kept API-only so the dangerous path needs a deliberate + `acceptDataLoss`. Say if you want it in the UI behind a typed confirmation. +5. **Sync replication.** Is a `min-sync-replicas` write mode wanted before any automatic + election? Without it, election loses acknowledged writes. +6. **Client discovery.** Build `GetClusterInfo` and an advertise-address flag now, so SDKs can + follow a promotion without configuration? +7. **Leader-driven promotion.** Studio on the leader could promote a replica it sees (a control + message on the WAL stream), at the cost of replicas advertising an address. Today promotion + runs on the replica. diff --git a/docs/studio-figma-gap-plan.md b/docs/studio-figma-gap-plan.md new file mode 100644 index 0000000..09ea8cf --- /dev/null +++ b/docs/studio-figma-gap-plan.md @@ -0,0 +1,97 @@ +# Studio: closing the gap to the Figma design + +Status: plan, nothing here is shipped. Written 2026-11-13 after comparing Figma file `rE4dmZFSRUzMElTXIKtnhG` (frames 1-11) with the running Studio. Complements [studio.md](studio.md) and [upgrade-spec.md](upgrade-spec.md). + +Principle: Studio only shows what the server reports. A Figma control ships only when the engine or API behind it exists. Until then the page keeps its current honest state (read-only, or a "Planned" label). No dead buttons. + +## What the engine exposes today (checked in code) + +| Area | Reality | Where | +|---|---|---| +| Keys | uint64 keys, 64-bit prefix partitions, no collections | `api/proto/beastdb.proto` | +| gRPC | Get, Put, Delete, Scan, BatchWrite; replication StreamWAL, Ack | proto | +| Indexes | One primary B+ tree plus one fixed in-memory text index | `internal/index/` | +| Tokens | Roles and `expires_at` exist on the server; no last-used | `internal/web/auth/token_store.go`, `handler/token_api.go` | +| Logs | Ring buffer of `{seq, time, level, message}`; no source field | `internal/logbuf/logbuf.go` | +| Replication | Leader lists replicas with connected and acked LSN; followers start by flags; manual promotion with epoch fencing (ADR 0002), no automatic election | `internal/replication/` | +| Settings | Read-only startup config | `handler/settings_api.go` | +| Metrics | Prometheus `/metrics`; Studio keeps ~5 s polling history in the browser only | `internal/metrics/`, `useMetricsHistory.ts` | +| Backups | Offline `beastctl backup/verify/restore`; server only runs checkpoints | `cmd/beastctl`, `handler/ops.go` | + +## Phase 0: UI only, no engine work (done 2026-11-13) + +Shipped: Settings sub-menu, API Keys role cards, Logs table header and CSV export. Already existed before this plan: the key expiry field and Expires column, Table Editor Prev/Next, and the Split view with a record detail pane. Not done from the table below: page-top header buttons beyond those that already exist. + +Everything maps to data that already exists. + +| Item | Figma frame | Work | +|---|---|---| +| Settings sub-menu (General / Security / Networking / Node) | 10 | Split the existing read-only cards into sections with a left menu. No Team or Danger zone. | +| API Keys: four role cards on top | 7 | Re-skin `RolesReference` as a 4-up card row. | +| API Keys: expiry field and Expires column | 7 | Send `expires_in_seconds` on create (backend done, spec calls this out); show `expires_at` in the table. | +| Table Editor: record side panel, Prev/Next | 1 | Existing `RecordSheet` content as a persistent right panel; page-based paging on top of the current load-more. | +| Logs: table layout (Time / Level / Event) and client-side Export CSV | 9 | Reuse entries; drop the Source column. | +| Header actions that already work | all | Refresh, Checkpoint now, Download logs placed top-right like Figma. | +| Page titles | all | Keep honest titles ("Logs", not "Logs & Audit"). | + +Done when: each page matches its frame's layout using real data only, and `go build ./... && go vet ./... && go test ./... -race` plus `bun test` pass. + +## Phase 1: small server additions (done 2026-11-13) + +Shipped, with these differences from the table below: +- **Token last-used** is held in memory only (never written, so authenticating does not touch the engine) and resets on restart. The Keys table shows "-" until a key is used after a restart. +- **Log source** is derived from the message text (keywords in `internal/logbuf`), not from tags at each call site, because the server logs through the standard logger. Treat it as a filter aid. `/api/logs` takes `source=`; the Studio filters the time range in the browser. +- **Metrics history** is sampled every 10 s into a 24 h in-memory ring at `/api/metrics/history`; the Metrics page has a Live / 1 h / 24 h picker and Overview shows a request-rate chart. Not persisted. + +| Item | Server work | Studio work | +|---|---|---| +| Token last-used | Update a `last_used` timestamp in the gRPC auth path (throttled, e.g. once a minute per token, so reads do not write). | "Last used" column. | +| Metrics history | Server-side ring of sampled gauges (WAL size, dirty pages, ops/s, replica lag) exposed at `/api/metrics/history?window=`. In-memory only, cleared on restart; say so in the UI. | Time-range picker; the Figma charts become real. Overview throughput chart reuses it. | +| Structured log source | Add `source` (engine, auth, replication, studio, grpc) to `logbuf.Entry`; tag existing call sites. | Source column and filter. | +| Log time-range filter | Query params on `/api/logs`. | Filter dropdown. | + +Risks: write amplification from last-used (mitigate with throttling and no WAL entry); history memory (bounded ring). + +## Phase 2: medium features (done 2026-11-13) + +Shipped, with these notes against the descriptions below: +- **Backups** (`internal/backup`, `/api/backups`): create, list, verify and download (tar) from Studio, plus an optional schedule and retention. Built on the engine's existing `CreateSnapshot`, which holds the engine lock while it copies the data file, so **reads and writes pause for the copy** (time grows with data size). Bundles are the same format `beastctl` writes; a test proves `beastctl verify` and `beastctl restore` accept them. **Restore stays offline**: Studio shows the command, not a button. Backups live on the same host as the data (`/backups`), so they do not protect against losing the machine; off-host copy is still manual (Download). +- **Runtime settings** (`internal/runtimecfg`, `/api/settings/runtime`): the WAL checkpoint threshold, the sign-in rate limit, and the backup schedule. Saved to `runtime-settings.json` in the data directory and override the matching flags on the next start. Everything else (TLS, addresses, pool size) stays startup-only. +- **Audit log** (`internal/audit`, `/api/audit`): append-only, hash-chained `audit.log` in the data directory. Records sign-ins (and failures), key create/revoke, record put/delete, user delete, checkpoints, settings changes and backup actions. Never records record values or secrets. The chain detects edits, removals and reordering; it does not stop someone who can rewrite the whole file, and it is not replicated off the host. The server refuses to start if the existing chain is broken. + +## Phase 2 (original description) + +1. **Server-managed backups.** Move `beastctl` backup, verify and restore logic into an internal package. Add admin endpoints to create a bundle into a configured backup directory, list manifests with size, time and verify status, verify, and stream a download. Scheduling comes last (simple interval, retention count). Restore stays an offline `beastctl` step until restore-to-new-database is designed; the Figma "Restore" button is shown as copy-the-command, not a live action. Needs: concurrent-safe backup while serving (today beastctl assumes a stopped node), so the first design question is a consistent snapshot using checkpoint plus WAL position. +2. **Runtime settings.** Pick the subset that is safe to change live (checkpoint threshold, log level, rate limits), persist to a settings file, apply without restart, audit each change. Everything else stays startup-only. TLS and mTLS toggles are not live-changeable and stay read-only. +3. **Audit log.** Separate append-only log of admin actions (login, token create/revoke, delete user, checkpoint, settings change) with actor and timestamp. Tamper-evident chaining is already on the upgrade-spec "next wave"; start with plain append-only and a hash chain. Studio gets the Logs & Audit tab and the "Audit" level the Figma shows. + +## Phase 3: large, engine-level (quarters, each starts with an ADR) + +| Feature | Blocker | First step | +|---|---|---| +| Collections / namespaces | Keys are uint64; no string keys or namespace concept. Upgrade spec lists string keys plus namespaces as not started. | ADR in `docs/adr/`: key encoding, migration of existing data, partition interaction. | +| Visual query builder + generated beastctl/SDK command | Needs collections and field-level filters; engine only has Get, Scan, text search. | Define filter push-down in Scan after collections land. | +| Secondary indexes (list, create, slow-query suggestion) | Only one fixed text index. Needs index definitions, persistence, build-in-background, and query stats for suggestions. | ADR on index catalog; depends on collections. | +| Replication management (add replica, promote) | **Partly built (2026-11-13), see [ADR 0002](adr/0002-replication-membership-and-manual-promotion.md).** Built: persisted epoch and role, followers reject a stale-epoch leader, a leader steps down on a higher epoch, a `Fence` RPC, admin `POST /api/replication/promote` with an audit entry, and a Studio Promote action that is disabled with a stated reason unless the node is caught up. Promotion is manual and runs on the replica being promoted, not from the leader's table. **Not built:** the epoch in WAL frames, add-replica (still a command), membership or discovery, client leader lookup, any automatic election. This is not high availability. | Owner decisions listed at the end of the ADR (WAL epoch marker, stuck-fence recovery, sync replication, client discovery). | +| Encryption at rest | Not built (data file, WAL, backups, spill file). | ADR on key management and on-disk format versioning. | +| Two-factor for admins | Studio auth is a signed cookie with local users. | Design TOTP storage and recovery; depends on audit log. | +| Restore to new database | Needs snapshot-consistent backups (Phase 2) and a swap-in procedure. | After backups design. | + +## Order and dependencies + +``` +Phase 0 ──► Phase 1 ──► Phase 2.1 backups ─────► restore-to-new-db + │ Phase 2.3 audit log ──► 2FA + └──► Phase 2.2 runtime settings +Phase 3 collections ──► query builder, secondary indexes +Phase 3 replication membership (independent ADR) +Phase 3 encryption at rest (independent ADR) +``` + +## Rules while this is in flight + +- Keep "Planned" labels and read-only states wherever a feature is not shipped; remove them only when the endpoint exists. +- Do not hard-code downstream-application schema in Studio (AGENTS.md); partition labels stay in `partitions.json`. +- No performance or feature-parity claims versus other databases without a benchmark. +- Each phase ends with a Figma-vs-running-Studio screenshot comparison at 1440x900 for the pages it touched. +- Each file stays one responsibility, around 400 lines (split `SettingsView.tsx` while doing the sub-menu). diff --git a/internal/api/engine.go b/internal/api/engine.go index 88585f7..1ab0179 100644 --- a/internal/api/engine.go +++ b/internal/api/engine.go @@ -30,6 +30,9 @@ type Engine struct { lastCkptLSN atomic.Uint64 // LSN at the last successful checkpoint (metrics) lastCkptUnix atomic.Int64 // unix seconds of the last successful checkpoint + + // maxWALBytes is the auto-checkpoint threshold; adjustable at runtime. + maxWALBytes atomic.Int64 } // NewEngine initializes the database engine components with recovery support. @@ -134,14 +137,14 @@ func (e *Engine) ApplyReplicatedRecord(opType byte, key, value []byte) error { return nil } keyUint := binary.LittleEndian.Uint64(key) - return e.commit(func() error { return e.putLocked(keyUint, value) }) + return e.commitReplicated(func() error { return e.putLocked(keyUint, value) }) case wal.OpDelete: if len(key) < 8 { return nil } keyUint := binary.LittleEndian.Uint64(key) - return e.commit(func() error { return e.deleteLocked(keyUint) }) + return e.commitReplicated(func() error { return e.deleteLocked(keyUint) }) case wal.OpBatch: batchOps, err := wal.DecodeBatchPayload(value) diff --git a/internal/api/engine_checkpoint.go b/internal/api/engine_checkpoint.go index ac04326..ff9712a 100644 --- a/internal/api/engine_checkpoint.go +++ b/internal/api/engine_checkpoint.go @@ -77,6 +77,7 @@ func (e *Engine) persistCheckpointLocked(lsn uint64) error { // StartAutoCheckpoint checkpoints whenever the WAL exceeds maxWALBytes, polling every // interval, until ctx is cancelled. It bounds WAL size and crash-recovery time. func (e *Engine) StartAutoCheckpoint(ctx context.Context, interval time.Duration, maxWALBytes int64) { + e.maxWALBytes.Store(maxWALBytes) go func() { t := time.NewTicker(interval) defer t.Stop() @@ -85,7 +86,7 @@ func (e *Engine) StartAutoCheckpoint(ctx context.Context, interval time.Duration case <-ctx.Done(): return case <-t.C: - if e.wal.FileSize() < maxWALBytes { + if e.wal.FileSize() < e.maxWALBytes.Load() { continue } if err := e.Checkpoint(); err != nil { @@ -95,3 +96,11 @@ func (e *Engine) StartAutoCheckpoint(ctx context.Context, interval time.Duration } }() } + +// SetCheckpointThreshold changes the WAL size at which the background +// checkpointer runs. It takes effect on the next tick; no restart is needed. +func (e *Engine) SetCheckpointThreshold(maxWALBytes int64) { + if maxWALBytes > 0 { + e.maxWALBytes.Store(maxWALBytes) + } +} diff --git a/internal/api/engine_commit.go b/internal/api/engine_commit.go index 21788d3..0de9d3b 100644 --- a/internal/api/engine_commit.go +++ b/internal/api/engine_commit.go @@ -21,6 +21,9 @@ type writeReq struct { err error done chan struct{} ok bool // run appended a WAL record (so it depends on the batch fsync) + // replicated marks a write applied from a leader's WAL; only client writes + // are refused on a read-only engine. + replicated bool } // committer is the group-commit queue. Writers enqueue a request; whichever @@ -49,9 +52,15 @@ type committer struct { // record (if it wrote one) is durable. fn runs with e.mu held exclusively and // must record observer events via e.queueNotify rather than calling the // observer directly. -func (e *Engine) commit(fn func() error) error { +func (e *Engine) commit(fn func() error) error { return e.submit(fn, false) } + +// commitReplicated is commit for writes replayed from a leader, which must +// still apply while the engine is read-only. +func (e *Engine) commitReplicated(fn func() error) error { return e.submit(fn, true) } + +func (e *Engine) submit(fn func() error, replicated bool) error { c := &e.cq - req := &writeReq{run: fn, done: make(chan struct{})} + req := &writeReq{run: fn, done: make(chan struct{}), replicated: replicated} c.mu.Lock() if c.closed { @@ -139,6 +148,12 @@ func (e *Engine) runBatch(batch []*writeReq) { e.pending = e.pending[:0] wrote := false for _, r := range batch { + // Checked under e.mu so a client write can never land after + // SetReadOnly(true) returns: this is what makes fencing exact. + if e.readOnly && !r.replicated { + r.err = ErrReadOnlyReplica + continue + } before := e.wal.CurrentLSN() r.err = r.run() // A request that appended a WAL record must wait for the group fsync, diff --git a/internal/api/grpc_auth.go b/internal/api/grpc_auth.go index fc77c09..93916bf 100644 --- a/internal/api/grpc_auth.go +++ b/internal/api/grpc_auth.go @@ -52,6 +52,7 @@ var methodRoles = map[string][]string{ "/beastdb.v1.BeastDBService/BatchWrite": {RoleWriter, RoleAdmin}, "/beastdb.v1.ReplicationService/StreamWAL": {RoleReplication, RoleAdmin}, "/beastdb.v1.ReplicationService/Ack": {RoleReplication, RoleAdmin}, + "/beastdb.v1.ReplicationService/Fence": {RoleReplication, RoleAdmin}, } func roleAllowed(role, method string) bool { diff --git a/internal/audit/audit.go b/internal/audit/audit.go new file mode 100644 index 0000000..95a2e00 --- /dev/null +++ b/internal/audit/audit.go @@ -0,0 +1,271 @@ +// Package audit is an append-only, hash-chained log of administrative actions +// (sign-ins, key changes, deletes, checkpoints, settings changes). +// +// Each record carries the SHA-256 of the previous record, so editing, removing +// or reordering an earlier line breaks every hash after it and Verify reports +// where. The chain detects accidental damage and casual tampering; it is not a +// defence against someone who can rewrite the whole file, and nothing here is +// replicated off the host. Secrets and record values are never written. +package audit + +import ( + "bufio" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "log" + "os" + "path/filepath" + "strings" + "sync" + "time" +) + +// FileName is the audit log's name inside the data directory. +const FileName = "audit.log" + +// Entry is one audit record. Prev and Hash form the chain. +type Entry struct { + Seq uint64 `json:"seq"` + Time time.Time `json:"time"` + Actor string `json:"actor"` + Action string `json:"action"` + Target string `json:"target,omitempty"` + Detail string `json:"detail,omitempty"` + IP string `json:"ip,omitempty"` + Prev string `json:"prev"` + Hash string `json:"hash"` +} + +// Actions recorded by the server. +const ( + ActionLoginOK = "login.ok" + ActionLoginFailed = "login.failed" + ActionLogout = "logout" + ActionTokenCreate = "token.create" + ActionTokenRevoke = "token.revoke" + ActionUserDelete = "user.delete" + ActionRecordPut = "record.put" + ActionRecordDelete = "record.delete" + ActionCheckpoint = "checkpoint" + ActionSettingChange = "settings.update" + ActionBackupCreate = "backup.create" + ActionBackupVerify = "backup.verify" + ActionBackupDownload = "backup.download" + ActionPromote = "replication.promote" +) + +const maxField = 512 + +// Log is the audit log. A nil *Log is valid and discards everything, so callers +// never need to check whether auditing is configured. +type Log struct { + mu sync.Mutex + f *os.File + path string + seq uint64 + last string // hash of the last record +} + +// Open opens (creating if needed) dir/audit.log, verifies the existing chain +// and positions for appending. It fails if the existing chain is broken, so a +// tampered log is noticed at startup instead of being silently extended. +func Open(dir string) (*Log, error) { + path := filepath.Join(dir, FileName) + l := &Log{path: path} + if _, err := os.Stat(path); err == nil { + n, last, verr := verifyFile(path) + if verr != nil { + return nil, fmt.Errorf("audit log %s is damaged: %w", path, verr) + } + l.seq, l.last = n, last + } + f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o600) + if err != nil { + return nil, err + } + l.f = f + return l, nil +} + +// Close releases the file. +func (l *Log) Close() error { + if l == nil || l.f == nil { + return nil + } + l.mu.Lock() + defer l.mu.Unlock() + err := l.f.Close() + l.f = nil + return err +} + +func clip(s string) string { + s = strings.Map(func(r rune) rune { + if r < 0x20 || r == 0x7f { + return ' ' + } + return r + }, s) + if len(s) > maxField { + return s[:maxField] + "…" + } + return s +} + +func hashOf(e Entry) string { + e.Hash = "" + b, _ := json.Marshal(e) + sum := sha256.Sum256(append([]byte(e.Prev+"|"), b...)) + return hex.EncodeToString(sum[:]) +} + +// Record appends one entry and fsyncs it. It is best effort for callers: a +// write failure is logged and returned, but admin actions are not blocked on it. +func (l *Log) Record(actor, action, target, detail, ip string) error { + if l == nil { + return nil + } + l.mu.Lock() + defer l.mu.Unlock() + if l.f == nil { + return errors.New("audit log closed") + } + e := Entry{ + Seq: l.seq + 1, Time: time.Now().UTC(), + Actor: clip(actor), Action: action, Target: clip(target), Detail: clip(detail), IP: clip(ip), + Prev: l.last, + } + e.Hash = hashOf(e) + b, err := json.Marshal(e) + if err != nil { + return err + } + if _, err := l.f.Write(append(b, '\n')); err != nil { + log.Printf("audit: write failed: %v", err) + return err + } + if err := l.f.Sync(); err != nil { + log.Printf("audit: sync failed: %v", err) + return err + } + l.seq, l.last = e.Seq, e.Hash + return nil +} + +// Query selects entries. Zero values mean no constraint. +type Query struct { + // Before returns entries with Seq < Before (for paging back). 0 = newest. + Before uint64 + // Limit caps the result; <=0 means 100, max 1000. + Limit int + Action string + Actor string +} + +// List returns matching entries newest first. +func (l *Log) List(q Query) ([]Entry, error) { + if l == nil { + return []Entry{}, nil + } + limit := q.Limit + if limit <= 0 { + limit = 100 + } + if limit > 1000 { + limit = 1000 + } + l.mu.Lock() + defer l.mu.Unlock() + all, err := readAll(l.path) + if err != nil { + return nil, err + } + out := make([]Entry, 0, limit) + for i := len(all) - 1; i >= 0 && len(out) < limit; i-- { + e := all[i] + if q.Before != 0 && e.Seq >= q.Before { + continue + } + if q.Action != "" && e.Action != q.Action { + continue + } + if q.Actor != "" && e.Actor != q.Actor { + continue + } + out = append(out, e) + } + return out, nil +} + +// Status reports the chain state for display. +type Status struct { + Entries uint64 `json:"entries"` + Verified bool `json:"verified"` + // Error describes the first break in the chain when Verified is false. + Error string `json:"error,omitempty"` +} + +// Verify re-reads the whole file and checks every link. +func (l *Log) Verify() Status { + if l == nil { + return Status{Verified: true} + } + l.mu.Lock() + defer l.mu.Unlock() + n, _, err := verifyFile(l.path) + if err != nil { + return Status{Entries: n, Error: err.Error()} + } + return Status{Entries: n, Verified: true} +} + +func readAll(path string) ([]Entry, error) { + f, err := os.Open(path) + if err != nil { + if os.IsNotExist(err) { + return nil, nil + } + return nil, err + } + defer f.Close() + var out []Entry + sc := bufio.NewScanner(f) + sc.Buffer(make([]byte, 0, 64<<10), 1<<20) + for sc.Scan() { + if len(strings.TrimSpace(sc.Text())) == 0 { + continue + } + var e Entry + if err := json.Unmarshal(sc.Bytes(), &e); err != nil { + return out, fmt.Errorf("line %d: %w", len(out)+1, err) + } + out = append(out, e) + } + return out, sc.Err() +} + +// verifyFile checks sequence numbers, back-links and hashes. It returns the +// count of valid records and the last hash. +func verifyFile(path string) (uint64, string, error) { + entries, err := readAll(path) + if err != nil { + return uint64(len(entries)), "", err + } + prev := "" + for i, e := range entries { + if e.Seq != uint64(i+1) { + return uint64(i), prev, fmt.Errorf("record %d: sequence is %d, expected %d (a line was removed or reordered)", i+1, e.Seq, i+1) + } + if e.Prev != prev { + return uint64(i), prev, fmt.Errorf("record %d: does not link to the previous record", e.Seq) + } + if hashOf(e) != e.Hash { + return uint64(i), prev, fmt.Errorf("record %d: contents do not match their hash (edited)", e.Seq) + } + prev = e.Hash + } + return uint64(len(entries)), prev, nil +} diff --git a/internal/audit/audit_test.go b/internal/audit/audit_test.go new file mode 100644 index 0000000..ff43280 --- /dev/null +++ b/internal/audit/audit_test.go @@ -0,0 +1,115 @@ +package audit + +import ( + "os" + "path/filepath" + "strings" + "sync" + "testing" +) + +func TestChainAppendReopenAndList(t *testing.T) { + dir := t.TempDir() + l, err := Open(dir) + if err != nil { + t.Fatal(err) + } + for _, a := range []string{ActionLoginOK, ActionTokenCreate, ActionCheckpoint} { + if err := l.Record("admin", a, "t", "d", "127.0.0.1"); err != nil { + t.Fatal(err) + } + } + if st := l.Verify(); !st.Verified || st.Entries != 3 { + t.Fatalf("verify: %+v", st) + } + _ = l.Close() + + // Reopening continues the chain instead of restarting it. + l, err = Open(dir) + if err != nil { + t.Fatal(err) + } + defer l.Close() + _ = l.Record("admin", ActionLogout, "", "", "") + got, _ := l.List(Query{}) + if len(got) != 4 || got[0].Seq != 4 || got[0].Action != ActionLogout || got[3].Seq != 1 { + t.Fatalf("list newest first: %+v", got) + } + if got[0].Prev != got[1].Hash { + t.Fatal("new record must link to the previous hash") + } + if f, _ := l.List(Query{Action: ActionCheckpoint}); len(f) != 1 { + t.Fatalf("action filter: %+v", f) + } + if p, _ := l.List(Query{Before: 3, Limit: 10}); len(p) != 2 || p[0].Seq != 2 { + t.Fatalf("paging: %+v", p) + } +} + +func TestTamperingIsDetected(t *testing.T) { + cases := map[string]func(lines []string) []string{ + "edit": func(l []string) []string { l[1] = strings.Replace(l[1], `"admin"`, `"mallory"`, 1); return l }, + "delete": func(l []string) []string { return append(l[:1], l[2:]...) }, + "reorder": func(l []string) []string { l[1], l[2] = l[2], l[1]; return l }, + } + for name, mutate := range cases { + t.Run(name, func(t *testing.T) { + dir := t.TempDir() + l, _ := Open(dir) + for i := 0; i < 4; i++ { + _ = l.Record("admin", ActionCheckpoint, "", "", "") + } + _ = l.Close() + + path := filepath.Join(dir, FileName) + raw, _ := os.ReadFile(path) + lines := mutate(strings.Split(strings.TrimSpace(string(raw)), "\n")) + _ = os.WriteFile(path, []byte(strings.Join(lines, "\n")+"\n"), 0o600) + + if _, err := Open(dir); err == nil { + t.Fatal("Open must refuse a damaged chain") + } + }) + } +} + +func TestNilLogAndSanitising(t *testing.T) { + var l *Log + if err := l.Record("a", "b", "c", "d", "e"); err != nil { + t.Fatal("nil log must discard silently") + } + if st := l.Verify(); !st.Verified { + t.Fatal("nil log verifies trivially") + } + + dir := t.TempDir() + real, _ := Open(dir) + defer real.Close() + _ = real.Record("ad\nmin", "x", strings.Repeat("a", 2000), "line1\r\nline2", "") + e, _ := real.List(Query{}) + if strings.ContainsAny(e[0].Actor+e[0].Detail, "\r\n") || len(e[0].Target) > maxField+4 { + t.Fatalf("fields must be single-line and bounded: %+v", e[0]) + } + if st := real.Verify(); !st.Verified { + t.Fatalf("sanitised entry must still verify: %+v", st) + } +} + +func TestConcurrentRecordsKeepChainValid(t *testing.T) { + l, _ := Open(t.TempDir()) + defer l.Close() + var wg sync.WaitGroup + for g := 0; g < 8; g++ { + wg.Add(1) + go func() { + defer wg.Done() + for i := 0; i < 20; i++ { + _ = l.Record("a", ActionCheckpoint, "", "", "") + } + }() + } + wg.Wait() + if st := l.Verify(); !st.Verified || st.Entries != 160 { + t.Fatalf("%+v", st) + } +} diff --git a/internal/backup/backup.go b/internal/backup/backup.go new file mode 100644 index 0000000..fd3faf6 --- /dev/null +++ b/internal/backup/backup.go @@ -0,0 +1,204 @@ +// Package backup creates, verifies and lists physical backups of a running +// engine. The on-disk format is the one `beastctl backup` writes (data.bin plus +// manifest.json), so `beastctl verify` and `beastctl restore` accept bundles +// made here. Restoring stays an offline `beastctl restore` step. +package backup + +import ( + "crypto/sha256" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "io" + "os" + "path/filepath" + "regexp" + "sort" + "time" + + "github.com/ChromaBeast/beastdb/internal/api" + "github.com/ChromaBeast/beastdb/internal/metrics" + "github.com/ChromaBeast/beastdb/internal/storage" +) + +// Manifest is manifest.json. The first block is shared with beastctl; Kind and +// CreatedBy are optional extras that beastctl ignores. +type Manifest struct { + Version string `json:"version"` + CreatedAt string `json:"created_at"` + DataChecksumSHA256 string `json:"data_checksum_sha256"` + RootPageID uint64 `json:"root_page_id"` + ActiveDataPageID uint64 `json:"active_data_page_id"` + LastCheckpointLSN uint64 `json:"last_checkpoint_lsn"` + FileSize int64 `json:"file_size"` + + Kind string `json:"kind,omitempty"` // "manual" or "scheduled" + CreatedBy string `json:"created_by,omitempty"` +} + +// Info is a listed backup. +type Info struct { + ID string `json:"id"` + Manifest +} + +// Snapshotter is the slice of the engine a backup needs. +type Snapshotter interface { + // CreateSnapshot flushes dirty pages and copies the data file into dir while + // holding the engine lock, so the copy is consistent. Reads and writes wait + // for the copy (time grows with the data file size). + CreateSnapshot(dir string) (string, error) + StorageMetrics() api.StorageMetrics +} + +// Kinds of backup. +const ( + KindManual = "manual" + KindScheduled = "scheduled" +) + +var idPattern = regexp.MustCompile(`^bk-\d{8}-\d{6}(-\d+)?$`) + +// ValidID reports whether id has the shape this package generates. Handlers +// must check it before using an id in a path. +func ValidID(id string) bool { return idPattern.MatchString(id) } + +// ErrNoSpace is returned when the backup directory lacks room for the copy. +var ErrNoSpace = errors.New("not enough free disk space for a backup") + +func newID(root string) string { + base := "bk-" + time.Now().UTC().Format("20060102-150405") + id := base + for n := 1; ; n++ { + if _, err := os.Stat(filepath.Join(root, id)); os.IsNotExist(err) { + return id + } + id = fmt.Sprintf("%s-%d", base, n) + } +} + +// Create writes a new backup bundle under root// and returns it. On any +// failure the partial directory is removed. +func Create(eng Snapshotter, root, kind, createdBy string) (Info, error) { + if err := os.MkdirAll(root, 0o750); err != nil { + return Info{}, err + } + if need := eng.StorageMetrics().DiskSizeBytes; need > 0 { + if free, ok := metrics.DiskFreeBytes(root); ok && free < need+need/10 { + return Info{}, fmt.Errorf("%w: need about %d bytes, %d free", ErrNoSpace, need+need/10, free) + } + } + id := newID(root) + dir := filepath.Join(root, id) + fail := func(err error) (Info, error) { _ = os.RemoveAll(dir); return Info{}, err } + + snap, err := eng.CreateSnapshot(dir) + if err != nil { + return fail(fmt.Errorf("snapshot: %w", err)) + } + data := filepath.Join(dir, "data.bin") + if err := os.Rename(snap, data); err != nil { + return fail(err) + } + sum, size, err := checksum(data) + if err != nil { + return fail(err) + } + m := Manifest{ + Version: "1.0.0", CreatedAt: time.Now().UTC().Format(time.RFC3339), + DataChecksumSHA256: sum, FileSize: size, Kind: kind, CreatedBy: createdBy, + } + if meta, err := readMeta(data); err == nil { + m.RootPageID, m.ActiveDataPageID, m.LastCheckpointLSN = meta.RootPageID, meta.ActiveDataPageID, meta.LastCheckpointLSN + } + b, _ := json.MarshalIndent(m, "", " ") + if err := os.WriteFile(filepath.Join(dir, "manifest.json"), b, 0o640); err != nil { + return fail(err) + } + return Info{ID: id, Manifest: m}, nil +} + +func checksum(path string) (string, int64, error) { + f, err := os.Open(path) + if err != nil { + return "", 0, err + } + defer f.Close() + h := sha256.New() + n, err := io.Copy(h, f) + return hex.EncodeToString(h.Sum(nil)), n, err +} + +func readMeta(path string) (storage.MetaData, error) { + f, err := os.Open(path) + if err != nil { + return storage.MetaData{}, err + } + defer f.Close() + page := make([]byte, storage.PageSize) + if _, err := f.ReadAt(page, 0); err != nil { + return storage.MetaData{}, err + } + return storage.DecodeMeta(page) +} + +// Verify re-hashes data.bin and checks it against manifest.json, the same +// checks `beastctl verify` makes. +func Verify(dir string) (Manifest, error) { + var m Manifest + b, err := os.ReadFile(filepath.Join(dir, "manifest.json")) + if err != nil { + return m, fmt.Errorf("missing or unreadable manifest.json: %w", err) + } + if err := json.Unmarshal(b, &m); err != nil { + return m, fmt.Errorf("invalid manifest JSON: %w", err) + } + sum, size, err := checksum(filepath.Join(dir, "data.bin")) + if err != nil { + return m, fmt.Errorf("missing or unreadable data.bin: %w", err) + } + if sum != m.DataChecksumSHA256 { + return m, fmt.Errorf("checksum mismatch: expected %s, computed %s", m.DataChecksumSHA256, sum) + } + if size != m.FileSize { + return m, fmt.Errorf("file size mismatch: manifest says %d bytes, found %d", m.FileSize, size) + } + meta, err := readMeta(filepath.Join(dir, "data.bin")) + if err != nil { + return m, fmt.Errorf("page 0 metadata validation failed: %w", err) + } + if meta.RootPageID != m.RootPageID { + return m, fmt.Errorf("root page mismatch: manifest=%d, page0=%d", m.RootPageID, meta.RootPageID) + } + return m, nil +} + +// List returns the backups under root, newest first. Directories without a +// readable manifest are skipped. +func List(root string) ([]Info, error) { + ents, err := os.ReadDir(root) + if err != nil { + if os.IsNotExist(err) { + return []Info{}, nil + } + return nil, err + } + out := []Info{} + for _, e := range ents { + if !e.IsDir() || !ValidID(e.Name()) { + continue + } + b, err := os.ReadFile(filepath.Join(root, e.Name(), "manifest.json")) + if err != nil { + continue + } + var m Manifest + if json.Unmarshal(b, &m) != nil { + continue + } + out = append(out, Info{ID: e.Name(), Manifest: m}) + } + sort.Slice(out, func(i, j int) bool { return out[i].ID > out[j].ID }) + return out, nil +} diff --git a/internal/backup/backup_test.go b/internal/backup/backup_test.go new file mode 100644 index 0000000..4de3b48 --- /dev/null +++ b/internal/backup/backup_test.go @@ -0,0 +1,157 @@ +package backup + +import ( + "errors" + "os" + "path/filepath" + "strings" + "sync" + "testing" + "time" + + "github.com/ChromaBeast/beastdb/internal/api" +) + +func newEngine(t *testing.T, records int) *api.Engine { + t.Helper() + dir := t.TempDir() + e, err := api.NewEngine(filepath.Join(dir, "beast.bin"), filepath.Join(dir, "beast.wal"), 32) + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { _ = e.Close() }) + for i := uint64(1); i <= uint64(records); i++ { + if err := e.Put(i, []byte("value-"+strings.Repeat("x", 50))); err != nil { + t.Fatal(err) + } + } + return e +} + +func TestCreateVerifyListAndCorruption(t *testing.T) { + e := newEngine(t, 200) + root := filepath.Join(t.TempDir(), "backups") + + info, err := Create(e, root, KindManual, "root") + if err != nil { + t.Fatal(err) + } + if !ValidID(info.ID) || info.Kind != KindManual || info.CreatedBy != "root" || info.FileSize == 0 || info.RootPageID == 0 { + t.Fatalf("%+v", info) + } + if _, err := Verify(filepath.Join(root, info.ID)); err != nil { + t.Fatalf("fresh backup must verify: %v", err) + } + for _, f := range []string{"data.bin", "manifest.json"} { + if _, err := os.Stat(filepath.Join(root, info.ID, f)); err != nil { + t.Fatalf("bundle is missing %s", f) + } + } + + second, _ := Create(e, root, KindScheduled, "scheduler") + list, _ := List(root) + if len(list) != 2 || list[0].ID != second.ID || list[1].ID != info.ID { + t.Fatalf("newest first: %+v", list) + } + + // Flip a byte: the checksum must catch it. + p := filepath.Join(root, info.ID, "data.bin") + b, _ := os.ReadFile(p) + b[len(b)-1] ^= 0xFF + _ = os.WriteFile(p, b, 0o600) + if _, err := Verify(filepath.Join(root, info.ID)); err == nil || !strings.Contains(err.Error(), "checksum mismatch") { + t.Fatalf("corruption not detected: %v", err) + } +} + +func TestValidIDRejectsPathTricks(t *testing.T) { + for _, bad := range []string{"", "..", "../etc", "bk-1", "bk-20260101-000000/../x", "bk-20260101-000000-", "BK-20260101-000000", "/abs"} { + if ValidID(bad) { + t.Fatalf("%q must not be a valid id", bad) + } + } + for _, ok := range []string{"bk-20260101-000000", "bk-20260101-000000-2"} { + if !ValidID(ok) { + t.Fatalf("%q should be valid", ok) + } + } +} + +// fake blocks in CreateSnapshot so concurrency and retention can be tested without a real engine. +type fake struct { + gate chan struct{} + enter chan struct{} + size uint64 +} + +func (f *fake) CreateSnapshot(dir string) (string, error) { + if f.enter != nil { + f.enter <- struct{}{} + <-f.gate + } + if err := os.MkdirAll(dir, 0o750); err != nil { + return "", err + } + p := filepath.Join(dir, "snap.bin") + return p, os.WriteFile(p, make([]byte, 8192), 0o600) +} +func (f *fake) StorageMetrics() api.StorageMetrics { return api.StorageMetrics{DiskSizeBytes: f.size} } + +func TestManagerSingleFlight(t *testing.T) { + f := &fake{gate: make(chan struct{}), enter: make(chan struct{}, 1)} + m := NewManager(f, filepath.Join(t.TempDir(), "b")) + var wg sync.WaitGroup + wg.Add(1) + go func() { defer wg.Done(); _, _ = m.Create(KindManual, "a") }() + <-f.enter // first backup is now inside CreateSnapshot + if _, err := m.Create(KindManual, "b"); !errors.Is(err, ErrBusy) { + t.Fatalf("second concurrent backup must be refused: %v", err) + } + close(f.gate) + wg.Wait() +} + +func TestPruneKeepsManualAndNewestScheduled(t *testing.T) { + f := &fake{} + root := filepath.Join(t.TempDir(), "b") + m := NewManager(f, root) + m.Configure(0, 2) + + var scheduled []string + for i := 0; i < 4; i++ { + in, err := m.Create(KindScheduled, "s") + if err != nil { + t.Fatal(err) + } + scheduled = append(scheduled, in.ID) + time.Sleep(1100 * time.Millisecond) // ids have one-second resolution + } + manual, _ := m.Create(KindManual, "root") + m.prune() + + got, _ := m.List() + ids := map[string]bool{} + for _, s := range got { + ids[s.ID] = true + } + if len(got) != 3 || !ids[manual.ID] || !ids[scheduled[3]] || !ids[scheduled[2]] || ids[scheduled[0]] { + t.Fatalf("expected manual + 2 newest scheduled, got %+v", got) + } +} + +func TestNoSpaceIsRefusedBeforeCopying(t *testing.T) { + f := &fake{size: 1 << 62} + _, err := Create(f, filepath.Join(t.TempDir(), "b"), KindManual, "x") + if !errors.Is(err, ErrNoSpace) { + t.Fatalf("got %v", err) + } +} + +func TestVerifyUnknownID(t *testing.T) { + m := NewManager(&fake{}, t.TempDir()) + for _, id := range []string{"../x", "bk-20260101-000000"} { + if _, err := m.Verify(id); !errors.Is(err, ErrNotFound) { + t.Fatalf("%q: %v", id, err) + } + } +} diff --git a/internal/backup/manager.go b/internal/backup/manager.go new file mode 100644 index 0000000..7575596 --- /dev/null +++ b/internal/backup/manager.go @@ -0,0 +1,198 @@ +package backup + +import ( + "context" + "errors" + "log" + "os" + "path/filepath" + "sync" + "time" +) + +// ErrBusy is returned when a backup is already running. +var ErrBusy = errors.New("a backup is already running") + +// ErrNotFound is returned for an unknown or malformed backup id. +var ErrNotFound = errors.New("backup not found") + +// Result is the last verification outcome for one backup, kept in memory. +type Result struct { + OK bool `json:"ok"` + At time.Time `json:"at"` + Error string `json:"error,omitempty"` +} + +// Status is a listed backup plus its last known verification. +type Status struct { + Info + Verified *Result `json:"verified"` // nil until verified since this process started +} + +// Manager owns the backup directory, serialises backups and runs the optional +// schedule. +type Manager struct { + root string + eng Snapshotter + + run sync.Mutex // held while a backup is being created + mu sync.Mutex // guards results and schedule fields + results map[string]Result + every time.Duration + keep int + wake chan struct{} +} + +// NewManager returns a Manager storing backups under root. +func NewManager(eng Snapshotter, root string) *Manager { + return &Manager{root: root, eng: eng, results: map[string]Result{}, keep: 7, wake: make(chan struct{}, 1)} +} + +// Dir is the backup directory. +func (m *Manager) Dir() string { return m.root } + +// Create makes a backup now and verifies it straight away. +func (m *Manager) Create(kind, actor string) (Info, error) { + if !m.run.TryLock() { + return Info{}, ErrBusy + } + defer m.run.Unlock() + info, err := Create(m.eng, m.root, kind, actor) + if err != nil { + return Info{}, err + } + m.record(info.ID) + return info, nil +} + +func (m *Manager) record(id string) Result { + r := Result{OK: true, At: time.Now().UTC()} + if _, err := Verify(filepath.Join(m.root, id)); err != nil { + r.OK, r.Error = false, err.Error() + } + m.mu.Lock() + m.results[id] = r + m.mu.Unlock() + return r +} + +// Verify re-checks one backup against its manifest. +func (m *Manager) Verify(id string) (Result, error) { + if !ValidID(id) { + return Result{}, ErrNotFound + } + if _, err := os.Stat(filepath.Join(m.root, id, "manifest.json")); err != nil { + return Result{}, ErrNotFound + } + return m.record(id), nil +} + +// Path returns the directory of a backup after validating its id. +func (m *Manager) Path(id string) (string, error) { + if !ValidID(id) { + return "", ErrNotFound + } + dir := filepath.Join(m.root, id) + if _, err := os.Stat(filepath.Join(dir, "manifest.json")); err != nil { + return "", ErrNotFound + } + return dir, nil +} + +// List returns backups newest first with their last verification result. +func (m *Manager) List() ([]Status, error) { + infos, err := List(m.root) + if err != nil { + return nil, err + } + m.mu.Lock() + defer m.mu.Unlock() + out := make([]Status, len(infos)) + for i, in := range infos { + out[i] = Status{Info: in} + if r, ok := m.results[in.ID]; ok { + r := r + out[i].Verified = &r + } + } + return out, nil +} + +// Schedule returns the current interval (0 = off) and how many scheduled +// backups are kept. +func (m *Manager) Schedule() (time.Duration, int) { + m.mu.Lock() + defer m.mu.Unlock() + return m.every, m.keep +} + +// Configure sets the schedule: a backup every interval (0 disables) and at most +// keep scheduled backups retained (manual ones are never pruned). +func (m *Manager) Configure(interval time.Duration, keep int) { + m.mu.Lock() + m.every = interval + if keep > 0 { + m.keep = keep + } + m.mu.Unlock() + select { + case m.wake <- struct{}{}: + default: + } +} + +// Start runs the schedule loop until ctx ends. +func (m *Manager) Start(ctx context.Context) { + go func() { + for { + every, _ := m.Schedule() + var tick <-chan time.Time + var t *time.Timer + if every > 0 { + t = time.NewTimer(every) + tick = t.C + } + select { + case <-ctx.Done(): + if t != nil { + t.Stop() + } + return + case <-m.wake: // schedule changed: restart the timer + if t != nil { + t.Stop() + } + case <-tick: + if _, err := m.Create(KindScheduled, "scheduler"); err != nil { + log.Printf("scheduled backup failed: %v", err) + } else { + m.prune() + } + } + } + }() +} + +// prune removes the oldest scheduled backups beyond the retention count. +func (m *Manager) prune() { + _, keep := m.Schedule() + infos, err := List(m.root) + if err != nil { + return + } + kept := 0 + for _, in := range infos { // newest first + if in.Kind != KindScheduled { + continue + } + if kept++; kept > keep { + if err := os.RemoveAll(filepath.Join(m.root, in.ID)); err != nil { + log.Printf("backup retention: could not remove %s: %v", in.ID, err) + continue + } + m.mu.Lock() + delete(m.results, in.ID) + m.mu.Unlock() + } + } +} diff --git a/internal/logbuf/logbuf.go b/internal/logbuf/logbuf.go index 616a8cc..9701939 100644 --- a/internal/logbuf/logbuf.go +++ b/internal/logbuf/logbuf.go @@ -20,10 +20,14 @@ const maxMessageBytes = 4096 // Entry is one captured log line. type Entry struct { - Seq uint64 `json:"seq"` - Time time.Time `json:"time"` - Level string `json:"level"` - Message string `json:"message"` + Seq uint64 `json:"seq"` + Time time.Time `json:"time"` + Level string `json:"level"` + // Source is the subsystem the line most likely came from (engine, grpc, + // auth, replication, studio, server). It is derived from the message text, + // not from call-site tags, so treat it as a filter aid. + Source string `json:"source"` + Message string `json:"message"` } // Buffer is a fixed-capacity ring of log entries. Safe for concurrent use. @@ -109,7 +113,7 @@ func (b *Buffer) appendLocked(line string) { line = line[:maxMessageBytes] + "…" } ts, msg := splitTimestamp(line) - e := Entry{Seq: b.nextSeq, Time: ts, Level: detectLevel(msg), Message: msg} + e := Entry{Seq: b.nextSeq, Time: ts, Level: detectLevel(msg), Source: detectSource(msg), Message: msg} b.nextSeq++ if b.n < len(b.ring) { b.ring[(b.start+b.n)%len(b.ring)] = e @@ -150,6 +154,34 @@ func detectLevel(msg string) string { return "info" } +// Known log sources, in the order the Studio lists them. +var Sources = []string{"engine", "grpc", "auth", "replication", "studio", "server"} + +// detectSource classifies a line by keywords this server's own messages use. +// Order matters: the first match wins; anything else is "server". +func detectSource(msg string) string { + m := strings.ToLower(msg) + switch { + case strings.Contains(m, "replica"), strings.Contains(m, "replication"), + strings.Contains(m, "follower"): + return "replication" + case strings.Contains(m, "grpc"): + return "grpc" + case strings.Contains(m, "web console"), strings.Contains(m, "studio"), + strings.Contains(m, "web server"), strings.Contains(m, "admin user"): + return "studio" + case strings.Contains(m, "password"), strings.Contains(m, "token"), + strings.Contains(m, "security"), strings.Contains(m, "login"), + strings.Contains(m, "session secret"), strings.Contains(m, "tls"): + return "auth" + case strings.Contains(m, "wal"), strings.Contains(m, "checkpoint"), + strings.Contains(m, "buffer pool"), strings.Contains(m, "recover"), + strings.Contains(m, "b+ tree"), strings.Contains(m, "page"): + return "engine" + } + return "server" +} + // Query selects entries. type Query struct { // Since returns only entries with Seq > Since. @@ -160,6 +192,8 @@ type Query struct { Level string // Text is a case-insensitive substring filter on the message. Text string + // Source, if set, matches that source exactly (see Sources). + Source string } // Result is a query answer. Latest is the highest seq stored (0 if empty), so @@ -191,6 +225,9 @@ func (b *Buffer) Snapshot(q Query) Result { if q.Level != "" && e.Level != q.Level { continue } + if q.Source != "" && e.Source != q.Source { + continue + } if text != "" && !strings.Contains(strings.ToLower(e.Message), text) { continue } diff --git a/internal/logbuf/logbuf_test.go b/internal/logbuf/logbuf_test.go index 81c4893..6ad2be7 100644 --- a/internal/logbuf/logbuf_test.go +++ b/internal/logbuf/logbuf_test.go @@ -117,3 +117,28 @@ func TestConcurrent(t *testing.T) { t.Fatalf("%d %d", len(r.Entries), r.Latest) } } + +func TestSourceDetectionAndFilter(t *testing.T) { + cases := map[string]string{ + "Starting BeastDB v0.1.0 [Role: leader] on 127.0.0.1:50051...": "server", + "Replica replica-1 caught up": "replication", + "BeastDB gRPC server listening on 127.0.0.1:50051": "grpc", + "BeastDB web console listening on http://127.0.0.1:8080": "studio", + "SECURITY WARNING: Using default password": "auth", + "Checkpoint complete, WAL rotated": "engine", + "something unrelated happened": "server", + } + b := New(20) + for msg := range cases { + fmt.Fprintln(b, msg) + } + for _, e := range b.Snapshot(Query{}).Entries { + if want := cases[e.Message]; e.Source != want { + t.Errorf("%q: source %q, want %q", e.Message, e.Source, want) + } + } + got := b.Snapshot(Query{Source: "grpc"}).Entries + if len(got) != 1 || got[0].Source != "grpc" { + t.Fatalf("source filter: %+v", got) + } +} diff --git a/internal/metrics/history.go b/internal/metrics/history.go new file mode 100644 index 0000000..19b186e --- /dev/null +++ b/internal/metrics/history.go @@ -0,0 +1,88 @@ +package metrics + +import ( + "sync" + "time" +) + +// Point is one sampled set of gauges. Counters are stored as rates already +// converted to per-second values by the sampler. +type Point struct { + Time int64 `json:"t"` // unix seconds + LSN uint64 `json:"lsn"` + WALBytes uint64 `json:"walBytes"` + DirtyPages int `json:"dirtyPages"` + CachedPages int `json:"cachedPages"` + ReqPerSec float64 `json:"reqPerSec"` + // ReplicaLag is the largest lag in LSNs over connected replicas (0 with none). + ReplicaLag uint64 `json:"replicaLag"` +} + +// History is a fixed-capacity in-memory ring of Points, oldest first. It is not +// persisted: it is cleared on restart. Safe for concurrent use. +type History struct { + mu sync.Mutex + ring []Point + next int + n int +} + +// NewHistory returns a ring holding at most capacity points. +func NewHistory(capacity int) *History { + if capacity <= 0 { + capacity = 1 + } + return &History{ring: make([]Point, capacity)} +} + +// Add appends p, evicting the oldest point when full. +func (h *History) Add(p Point) { + h.mu.Lock() + h.ring[h.next] = p + h.next = (h.next + 1) % len(h.ring) + if h.n < len(h.ring) { + h.n++ + } + h.mu.Unlock() +} + +// Window returns points newer than now-window, oldest first, thinned by an even +// stride so the result has at most maxPoints entries. The newest point is kept. +func (h *History) Window(now time.Time, window time.Duration, maxPoints int) []Point { + cutoff := now.Add(-window).Unix() + h.mu.Lock() + all := make([]Point, 0, h.n) + start := (h.next - h.n + len(h.ring)) % len(h.ring) + for i := 0; i < h.n; i++ { + p := h.ring[(start+i)%len(h.ring)] + if p.Time >= cutoff { + all = append(all, p) + } + } + h.mu.Unlock() + + if maxPoints <= 0 || len(all) <= maxPoints { + return all + } + stride := (len(all) + maxPoints - 1) / maxPoints + out := make([]Point, 0, maxPoints) + for i := len(all) - 1; i >= 0 && len(out) < maxPoints; i -= stride { + out = append(out, all[i]) + } + for l, r := 0, len(out)-1; l < r; l, r = l+1, r-1 { + out[l], out[r] = out[r], out[l] + } + return out +} + +// Total returns the number of requests recorded so far across all protocols +// and status codes. Used by samplers to derive a request rate. +func (r *RequestMetrics) Total() uint64 { + r.mu.Lock() + defer r.mu.Unlock() + var n uint64 + for _, v := range r.count { + n += v + } + return n +} diff --git a/internal/metrics/history_test.go b/internal/metrics/history_test.go new file mode 100644 index 0000000..133a34d --- /dev/null +++ b/internal/metrics/history_test.go @@ -0,0 +1,43 @@ +package metrics + +import ( + "testing" + "time" +) + +func TestHistoryRingWindowAndThinning(t *testing.T) { + h := NewHistory(5) + base := time.Unix(1_000_000, 0) + for i := 0; i < 8; i++ { // 3 oldest are evicted + h.Add(Point{Time: base.Add(time.Duration(i) * time.Minute).Unix(), LSN: uint64(i)}) + } + now := base.Add(7 * time.Minute) + + all := h.Window(now, time.Hour, 0) + if len(all) != 5 || all[0].LSN != 3 || all[4].LSN != 7 { + t.Fatalf("ring should hold LSN 3..7 oldest first: %+v", all) + } + + recent := h.Window(now, 2*time.Minute, 0) // minutes 5, 6, 7 + if len(recent) != 3 || recent[0].LSN != 5 { + t.Fatalf("window filter: %+v", recent) + } + + thin := h.Window(now, time.Hour, 2) + if len(thin) > 2 || thin[len(thin)-1].LSN != 7 { + t.Fatalf("thinning must keep the newest point and cap the count: %+v", thin) + } + if thin[0].Time > thin[len(thin)-1].Time { + t.Fatal("thinned points must stay oldest first") + } +} + +func TestRequestTotal(t *testing.T) { + r := NewRequestMetrics() + r.Observe("grpc", "Get", "OK", time.Millisecond) + r.Observe("http", "/x", "200", time.Millisecond) + r.Observe("http", "/x", "500", time.Millisecond) + if got := r.Total(); got != 3 { + t.Fatalf("total %d, want 3", got) + } +} diff --git a/internal/replication/cluster.go b/internal/replication/cluster.go new file mode 100644 index 0000000..01a794e --- /dev/null +++ b/internal/replication/cluster.go @@ -0,0 +1,163 @@ +package replication + +import ( + "context" + "errors" + "fmt" + "log" + "sync" + "time" + + "github.com/ChromaBeast/beastdb/internal/api" +) + +// ErrFenceRefused means this leader will not be fenced by that candidate: its +// epoch is not newer, or it is already fenced for a different candidate. +var ErrFenceRefused = errors.New("replication: fence refused") + +// Cluster ties a node's persisted epoch and role to its engine and its +// replication endpoints. It is the one place that decides whether this node +// may accept writes. +type Cluster struct { + nodeID string + engine *api.Engine + state *StateStore + leader *LeaderServer + + mu sync.Mutex // guards the fields below and serializes role changes + follower *FollowerClient + syncStop context.CancelFunc + syncDone chan struct{} + runCtx context.Context + promoting bool +} + +// NewCluster loads the persisted state, reconciles it with the -role flag +// (see ResolveRole), and puts the engine and leader server into the matching +// mode. A returned error means the node must not start. +func NewCluster(engine *api.Engine, dataDir, nodeID string, flag Role, leader *LeaderServer) (*Cluster, error) { + st, err := LoadStateStore(dataDir) + if err != nil { + return nil, err + } + cur := st.Get() + role, err := ResolveRole(cur, flag) + if err != nil { + return nil, err + } + if cur.Role != role { + next := cur + next.Role = role + if role == RoleLeader { + next.Epoch = max(cur.Epoch, 1) + next.ServedEpoch = next.Epoch + } + if err := st.Set(next); err != nil { + return nil, err + } + } + if role != flag { + log.Printf("replication: persisted state overrides -role %s: starting as %s at epoch %d", flag, role, st.Epoch()) + } + c := &Cluster{nodeID: nodeID, engine: engine, state: st, leader: leader} + leader.cluster = c + engine.SetReadOnly(role != RoleLeader) + leader.SetActive(role != RoleFollower) + if role == RoleLeader { + engine.SetCommitObserver(leader) + } + return c, nil +} + +// Epoch is the highest epoch this node has seen. +func (c *Cluster) Epoch() uint64 { return c.state.Epoch() } + +// ServedEpoch is the epoch this node leads (or last led) at. +func (c *Cluster) ServedEpoch() uint64 { return c.state.Get().ServedEpoch } + +// Role is the node's current persisted role. +func (c *Cluster) Role() Role { return c.state.Get().Role } + +// stepDown fences this leader because a peer reported a higher epoch. It +// persists before it freezes, so a crash in between restarts fenced. +func (c *Cluster) stepDown(epoch uint64, by string) error { + c.mu.Lock() + defer c.mu.Unlock() + cur := c.state.Get() + if cur.Role == RoleFollower || epoch <= cur.Epoch { + return nil + } + next := cur + next.Epoch, next.Role, next.FencedFor = epoch, RoleFenced, by + if err := c.state.Set(next); err != nil { + return err + } + c.engine.SetReadOnly(true) + log.Printf("replication: stepped down: %s reported epoch %d (this node led at %d); writes are now refused", by, epoch, cur.ServedEpoch) + return nil +} + +// fence implements the Fence RPC: freeze at epoch for candidate and return the +// final LSN. No client write can land after this returns. +func (c *Cluster) fence(epoch uint64, candidate string) (uint64, error) { + c.mu.Lock() + defer c.mu.Unlock() + cur := c.state.Get() + switch { + case cur.Role == RoleFollower: + return 0, fmt.Errorf("%w: this node is a follower", ErrFenceRefused) + case cur.Role == RoleFenced && cur.Epoch == epoch && cur.FencedFor == candidate: + // Retry by the same candidate: already frozen, report the same LSN. + return c.engine.CurrentLSN(), nil + case epoch <= cur.Epoch: + who := "" + if cur.Role == RoleFenced { + who = fmt.Sprintf(" (fenced for %q)", cur.FencedFor) + } + return 0, fmt.Errorf("%w: epoch %d is not newer than this node's %d%s", ErrFenceRefused, epoch, cur.Epoch, who) + } + next := cur + next.Epoch, next.Role, next.FencedFor = epoch, RoleFenced, candidate + if err := c.state.Set(next); err != nil { + return 0, err + } + c.engine.SetReadOnly(true) + lsn := c.engine.CurrentLSN() + log.Printf("replication: fenced at epoch %d for %s; final LSN %d", epoch, candidate, lsn) + return lsn, nil +} + +// RunFollower attaches the follower and runs its sync loop (reconnecting on +// error) until ctx ends or the node is promoted. +func (c *Cluster) RunFollower(ctx context.Context, f *FollowerClient) { + f.SetStateStore(c.state) + c.mu.Lock() + c.follower = f + c.runCtx = ctx + loopCtx, cancel := context.WithCancel(ctx) + done := make(chan struct{}) + c.syncStop, c.syncDone = cancel, done + c.mu.Unlock() + go func() { + defer close(done) + var lastErr string + for loopCtx.Err() == nil { + err := f.SyncLoop(loopCtx) + if loopCtx.Err() != nil { + continue + } + wait := 200 * time.Millisecond // clean end of stream + if err != nil { + wait = time.Second + if msg := err.Error(); msg != lastErr { + log.Printf("Follower sync stream dropped: %v. Reconnecting in 1s...", err) + lastErr = msg + } + } + select { + case <-time.After(wait): + case <-loopCtx.Done(): + } + } + }() +} diff --git a/internal/replication/cluster_harness_test.go b/internal/replication/cluster_harness_test.go new file mode 100644 index 0000000..27fcd17 --- /dev/null +++ b/internal/replication/cluster_harness_test.go @@ -0,0 +1,141 @@ +package replication + +import ( + "context" + "net" + "path/filepath" + "sync" + "testing" + "time" + + "github.com/ChromaBeast/beastdb/internal/api" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +// tnode is one in-process BeastDB node: engine, replication server and cluster. +type tnode struct { + t *testing.T + id string + dir string + engine *api.Engine + leader *LeaderServer + cluster *Cluster + srv *grpc.Server + addr string + closeMu sync.Once +} + +// startTNode opens (or reopens) a node in dir and serves gRPC on addr ("" picks a port). +func startTNode(t *testing.T, id, dir string, flag Role, addr string) *tnode { + t.Helper() + n := &tnode{t: t, id: id, dir: dir} + eng, err := api.NewEngine(filepath.Join(dir, "beast.bin"), filepath.Join(dir, "beast.wal"), 50) + if err != nil { + t.Fatalf("%s: engine: %v", id, err) + } + n.engine = eng + n.leader = NewLeaderServer(eng.WALPath()) + n.cluster, err = NewCluster(eng, dir, id, flag, n.leader) + if err != nil { + _ = eng.Close() + t.Fatalf("%s: cluster: %v", id, err) + } + n.serve(addr) + t.Cleanup(n.stop) + return n +} + +func (n *tnode) serve(addr string) { + n.t.Helper() + if addr == "" { + addr = "127.0.0.1:0" + } + var lis net.Listener + var err error + for i := 0; i < 50; i++ { // a just-closed port can take a moment to free up + if lis, err = net.Listen("tcp", addr); err == nil { + break + } + time.Sleep(20 * time.Millisecond) + } + if err != nil { + n.t.Fatalf("%s: listen %s: %v", n.id, addr, err) + } + n.srv = grpc.NewServer() + n.leader.RegisterService(n.srv) + n.addr = lis.Addr().String() + go func() { _ = n.srv.Serve(lis) }() +} + +// partition stops serving gRPC but keeps the engine running, like a network +// partition: clients of the node on the far side can no longer reach it. +func (n *tnode) partition() { n.srv.Stop() } + +// heal serves the same node again at the same address. +func (n *tnode) heal() { n.serve(n.addr) } + +// stop shuts the node down (engine flushed), as a clean process exit would. +func (n *tnode) stop() { + n.closeMu.Do(func() { + n.srv.Stop() + _ = n.engine.Close() + }) +} + +// follow dials leaderAddr and runs this node as its follower until the test ends. +func (n *tnode) follow(leaderAddr string) *FollowerClient { + n.t.Helper() + conn, err := grpc.NewClient(leaderAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + n.t.Fatalf("%s: dial: %v", n.id, err) + } + n.t.Cleanup(func() { _ = conn.Close() }) + f := NewFollowerClient(n.id, n.engine, conn) + ctx, cancel := context.WithCancel(context.Background()) + n.t.Cleanup(cancel) + n.cluster.RunFollower(ctx, f) + return f +} + +// hasKey reports whether key holds want on this node. +func (n *tnode) hasKey(key uint64, want string) bool { + v, ok, err := n.engine.Get(key) + return err == nil && ok && string(v) == want +} + +// caughtUp waits until the follower f has applied the leader's current LSN. +func caughtUp(t *testing.T, f *FollowerClient, leader *tnode) { + t.Helper() + if !waitUntil(t, func() bool { return f.LastAppliedLSN() >= leader.engine.CurrentLSN() }) { + t.Fatalf("follower stuck at LSN %d, leader at %d", f.LastAppliedLSN(), leader.engine.CurrentLSN()) + } +} + +// fastFencing shortens promotion timers for the duration of a test. +func fastFencing(t *testing.T) { + t.Helper() + oldRetry, oldFresh := fenceRetryEvery, leaderFreshness + fenceRetryEvery = 50 * time.Millisecond + t.Cleanup(func() { fenceRetryEvery, leaderFreshness = oldRetry, oldFresh }) +} + +// listen opens a TCP listener on a free local port. +func listen(t *testing.T) (net.Listener, string) { + t.Helper() + lis, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + return lis, lis.Addr().String() +} + +// NewClusterForTest runs only the startup role check against dir, without an engine. +func NewClusterForTest(t *testing.T, dir string, flag Role) (Role, error) { + t.Helper() + st, err := LoadStateStore(dir) + if err != nil { + return "", err + } + return ResolveRole(st.Get(), flag) +} diff --git a/internal/replication/fence.go b/internal/replication/fence.go new file mode 100644 index 0000000..a4cb80f --- /dev/null +++ b/internal/replication/fence.go @@ -0,0 +1,59 @@ +package replication + +import ( + "context" + "errors" + + beastv1 "github.com/ChromaBeast/beastdb/api/proto" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// SetActive marks whether this node serves its WAL. A follower that has never +// led is inactive; a leader, and a fenced former leader (which may still be +// the source a candidate catches up from), are active. +func (s *LeaderServer) SetActive(active bool) { s.inactive.Store(!active) } + +// servedEpoch is the epoch stamped on records and ACK replies. +func (s *LeaderServer) servedEpoch() uint64 { + if s.cluster == nil { + return 0 + } + return s.cluster.ServedEpoch() +} + +// admit gates every replication RPC. It refuses when this node is not serving +// a WAL and, when the peer has seen a higher epoch than ours, steps this node +// down first: a higher epoch proves a newer leader exists. +func (s *LeaderServer) admit(peerEpoch uint64, peerID string) error { + if s.inactive.Load() { + return status.Error(codes.FailedPrecondition, "this node is not a leader") + } + if s.cluster == nil { + return nil + } + if peerEpoch > s.cluster.Epoch() { + if err := s.cluster.stepDown(peerEpoch, peerID); err != nil { + return status.Errorf(codes.Internal, "could not step down: %v", err) + } + return status.Errorf(codes.FailedPrecondition, "stale leader: peer is at epoch %d, this node stepped down", peerEpoch) + } + return nil +} + +// Fence freezes this leader's writes at the candidate's epoch and returns the +// final LSN. It is idempotent for the same candidate and epoch. +func (s *LeaderServer) Fence(ctx context.Context, req *beastv1.FenceRequest) (*beastv1.FenceResponse, error) { + if s.inactive.Load() || s.cluster == nil { + return nil, status.Error(codes.FailedPrecondition, "this node is not a leader") + } + lsn, err := s.cluster.fence(req.Epoch, req.CandidateId) + switch { + case err == nil: + return &beastv1.FenceResponse{FinalLsn: lsn, Epoch: req.Epoch}, nil + case errors.Is(err, ErrFenceRefused): + return nil, status.Error(codes.FailedPrecondition, err.Error()) + default: + return nil, status.Error(codes.Internal, err.Error()) + } +} diff --git a/internal/replication/follower.go b/internal/replication/follower.go index e588f29..012b112 100644 --- a/internal/replication/follower.go +++ b/internal/replication/follower.go @@ -16,11 +16,15 @@ import ( var ( // ErrReplicationGap indicates a non-contiguous LSN was received. ErrReplicationGap = errors.New("replication gap detected: non-contiguous LSN") + // ErrStaleLeader indicates the leader's epoch is lower than one this + // follower has already seen: it was superseded by a promotion. + ErrStaleLeader = errors.New("replication: leader epoch is older than this follower's; leader was superseded") ) const ( ackBatchSize = 100 // send ACK every N records ackBatchInterval = 100 * time.Millisecond // or every 100 ms, whichever first + heartbeatEvery = time.Second // unchanged progress is still acked this often ) // FollowerClient connects to the Leader node and replicates WAL records into local state. @@ -33,6 +37,10 @@ type FollowerClient struct { ackMu sync.Mutex // serializes ACK RPCs so the leader sees them in LSN order lastAckedLSN uint64 // guarded by ackMu + + state *StateStore // nil: epochs are not tracked + leaderLSN uint64 // highest leader LSN seen (records or ack replies); guarded by mu + lastContact time.Time // last record or ack reply from a leader at our epoch; guarded by mu } // NewFollowerClient initializes a replication worker targeting the primary leader. @@ -62,6 +70,7 @@ func (f *FollowerClient) SyncLoop(ctx context.Context) error { stream, err := f.client.StreamWAL(ctx, &beastv1.StreamWALRequest{ ReplicaId: f.replicaID, FromLsn: fromLSN, + Epoch: f.epoch(), }) if err != nil { return err @@ -73,12 +82,17 @@ func (f *FollowerClient) SyncLoop(ctx context.Context) error { msg, err := stream.Recv() if err != nil { if err == io.EOF || ctx.Err() != nil { - f.sendAck(ctx) // flush final ACK on clean shutdown + f.sendAck(ctx, false) // flush final ACK on clean shutdown return nil } return err } + if err := f.checkEpoch(msg.Epoch); err != nil { + return err + } + f.noteLeader(msg.Lsn) + f.mu.Lock() expectedLSN := f.lastAppliedLSN + 1 if msg.Lsn > expectedLSN { @@ -101,7 +115,7 @@ func (f *FollowerClient) SyncLoop(ctx context.Context) error { count++ if count >= ackBatchSize { - f.sendAck(ctx) + f.sendAck(ctx, false) count = 0 } } @@ -112,33 +126,99 @@ func (f *FollowerClient) SyncLoop(ctx context.Context) error { func (f *FollowerClient) ackTicker(ctx context.Context) { t := time.NewTicker(ackBatchInterval) defer t.Stop() + lastBeat := time.Now() for { select { case <-ctx.Done(): return - case <-t.C: - f.sendAck(ctx) + case now := <-t.C: + beat := now.Sub(lastBeat) >= heartbeatEvery + if beat { + lastBeat = now + } + f.sendAck(ctx, beat) } } } // sendAck fires a single ACK for the current lastAppliedLSN, unless that LSN -// is already acknowledged. -func (f *FollowerClient) sendAck(ctx context.Context) { +// is already acknowledged and force is false. A forced ACK is a heartbeat: the +// reply tells an idle follower the leader's LSN, so it can tell it is caught up. +func (f *FollowerClient) sendAck(ctx context.Context, force bool) { f.ackMu.Lock() defer f.ackMu.Unlock() f.mu.RLock() lsn := f.lastAppliedLSN f.mu.RUnlock() - if lsn == f.lastAckedLSN { + if lsn == f.lastAckedLSN && !force { return } - if _, err := f.client.Ack(ctx, &beastv1.ReplicationAck{ + resp, err := f.client.Ack(ctx, &beastv1.ReplicationAck{ ReplicaId: f.replicaID, AcknowledgedLsn: lsn, - }); err == nil { - f.lastAckedLSN = lsn + Epoch: f.epoch(), + }) + if err != nil { + return + } + f.lastAckedLSN = lsn + if f.checkEpoch(resp.Epoch) == nil { + f.noteLeader(resp.LeaderLsn) + } +} + +// SetStateStore makes the follower track and persist leader epochs. +func (f *FollowerClient) SetStateStore(st *StateStore) { f.state = st } + +func (f *FollowerClient) epoch() uint64 { + if f.state == nil { + return 0 + } + return f.state.Epoch() +} + +// checkEpoch rejects a leader older than one already seen and adopts a newer +// one (persisting it before any of that leader's records are applied). +func (f *FollowerClient) checkEpoch(leaderEpoch uint64) error { + if f.state == nil { + return nil } + cur := f.state.Get() + if leaderEpoch < cur.Epoch { + return fmt.Errorf("%w: leader at epoch %d, follower at %d", ErrStaleLeader, leaderEpoch, cur.Epoch) + } + if leaderEpoch > cur.Epoch { + next := cur + next.Epoch = leaderEpoch + next.Role = RoleFollower + return f.state.Set(next) + } + return nil +} + +// noteLeader records contact with a current-epoch leader at the given LSN. +func (f *FollowerClient) noteLeader(lsn uint64) { + f.mu.Lock() + if lsn > f.leaderLSN { + f.leaderLSN = lsn + } + f.lastContact = time.Now() + f.mu.Unlock() +} + +// LeaderLSN returns the highest leader LSN this follower has heard of. +func (f *FollowerClient) LeaderLSN() uint64 { + f.mu.RLock() + defer f.mu.RUnlock() + return f.leaderLSN +} + +// LastContact is when a current-epoch leader last sent a record or answered +// an ACK; the zero time means never. +func (f *FollowerClient) LastContact() time.Time { + f.mu.RLock() + defer f.mu.RUnlock() + return f.lastContact } // LastAppliedLSN returns the latest Log Sequence Number applied by this replica. diff --git a/internal/replication/leader.go b/internal/replication/leader.go index 3912449..d65b583 100644 --- a/internal/replication/leader.go +++ b/internal/replication/leader.go @@ -3,6 +3,7 @@ package replication import ( "context" "sync" + "sync/atomic" beastv1 "github.com/ChromaBeast/beastdb/api/proto" "github.com/ChromaBeast/beastdb/internal/wal" @@ -19,6 +20,12 @@ type LeaderServer struct { acks map[string]uint64 addrs map[string]string // replica id -> remote address of its live stream mu sync.RWMutex + + // cluster is set when the node tracks epochs (see Cluster); nil keeps the + // original unfenced behaviour for standalone use and tests. + cluster *Cluster + // inactive is true on a node that is not (yet) a leader: it serves no WAL. + inactive atomic.Bool } // NewLeaderServer creates a replication server tracking connected replicas and log history. @@ -43,6 +50,7 @@ func (s *LeaderServer) Broadcast(lsn uint64, opType byte, key, value []byte) { OpType: uint32(opType), Key: key, Value: value, + Epoch: s.servedEpoch(), }) } @@ -53,6 +61,13 @@ func (s *LeaderServer) OnCommit(lsn uint64, opType byte, key, value []byte) { // StreamWAL streams historical WAL replay followed by real-time committed WAL frames. func (s *LeaderServer) StreamWAL(req *beastv1.StreamWALRequest, stream grpc.ServerStreamingServer[beastv1.WALRecordMessage]) error { + if err := s.admit(req.Epoch, req.ReplicaId); err != nil { + return err + } + if s.cluster != nil && req.FromLsn > 0 && req.FromLsn-1 > s.cluster.engine.CurrentLSN() { + return status.Errorf(codes.FailedPrecondition, "follower is at LSN %d, ahead of this leader (%d): it holds writes this leader never had; rebuild it from this leader", + req.FromLsn-1, s.cluster.engine.CurrentLSN()) + } // 1. Subscribe FIRST before reading WAL from disk to eliminate the live-handoff race gap. ch, unsubscribe := s.broadcaster.Subscribe(req.ReplicaId, 1024) defer unsubscribe() @@ -74,6 +89,7 @@ func (s *LeaderServer) StreamWAL(req *beastv1.StreamWALRequest, stream grpc.Serv OpType: uint32(rec.Type), Key: rec.Key, Value: rec.Value, + Epoch: s.servedEpoch(), } if err := stream.Send(msg); err != nil { return err @@ -108,11 +124,17 @@ func (s *LeaderServer) StreamWAL(req *beastv1.StreamWALRequest, stream grpc.Serv // Ack records the latest applied LSN acknowledgment from a replica node. func (s *LeaderServer) Ack(ctx context.Context, req *beastv1.ReplicationAck) (*beastv1.AckResponse, error) { + if err := s.admit(req.Epoch, req.ReplicaId); err != nil { + return nil, err + } s.mu.Lock() - defer s.mu.Unlock() - s.acks[req.ReplicaId] = req.AcknowledgedLsn - return &beastv1.AckResponse{Success: true}, nil + s.mu.Unlock() + resp := &beastv1.AckResponse{Success: true, Epoch: s.servedEpoch()} + if s.cluster != nil { + resp.LeaderLsn = s.cluster.engine.CurrentLSN() + } + return resp, nil } // GetReplicaAck returns the highest acknowledged LSN for a given replica. diff --git a/internal/replication/promote.go b/internal/replication/promote.go new file mode 100644 index 0000000..994ee15 --- /dev/null +++ b/internal/replication/promote.go @@ -0,0 +1,252 @@ +package replication + +import ( + "context" + "errors" + "fmt" + "log" + "time" + + beastv1 "github.com/ChromaBeast/beastdb/api/proto" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +var ( + // ErrNotFollower means Promote was called on a node that is not a follower. + ErrNotFollower = errors.New("replication: only a follower can be promoted") + // ErrPromoteInProgress means another Promote call is running. + ErrPromoteInProgress = errors.New("replication: a promotion is already in progress") + // ErrLeaderUnreachable means the old leader could not be fenced because it + // did not answer. Promoting anyway can lose its latest writes and can + // leave two writable leaders until it is fenced, so it needs AcceptDataLoss. + ErrLeaderUnreachable = errors.New("replication: leader unreachable, so it cannot be fenced; promoting now can lose its latest writes") + // ErrCatchUpTimeout means the old leader was fenced but this follower did + // not apply everything up to the leader's final LSN in time. Nothing was + // promoted; retrying the same promotion is safe. + ErrCatchUpTimeout = errors.New("replication: old leader is fenced but this follower has not caught up to its final LSN") +) + +// Tunables, variables so tests can shorten them. +var ( + defaultFenceTimeout = 3 * time.Second + defaultCatchUpTimeout = 15 * time.Second + fenceRetryEvery = 2 * time.Second + // leaderFreshness is how recent leader contact must be for a follower to + // count as caught up. + leaderFreshness = 3 * time.Second +) + +// PromoteOptions tunes Promote. Zero values use safe defaults. +type PromoteOptions struct { + // AcceptDataLoss allows promotion when the old leader cannot be reached to + // fence it. Writes the leader acknowledged but this follower never + // received are lost, and the old leader may keep accepting writes until + // it can be fenced (the new leader keeps trying to fence it). + AcceptDataLoss bool + FenceTimeout time.Duration + CatchUpTimeout time.Duration +} + +// PromoteResult describes a completed promotion. +type PromoteResult struct { + Epoch uint64 `json:"epoch"` + // OldLeaderFenced is true when the old leader confirmed the fence; the + // promotion then lost no acknowledged write. + OldLeaderFenced bool `json:"oldLeaderFenced"` + // FinalLSN is the old leader's last LSN as reported by the fence (0 if unreached). + FinalLSN uint64 `json:"finalLSN"` + // AppliedLSN is this node's LSN when it began leading. + AppliedLSN uint64 `json:"appliedLSN"` + // DataLossPossible is true for an unfenced promotion. + DataLossPossible bool `json:"dataLossPossible"` + // LeaderLSNKnown is the highest old-leader LSN this node had heard of (unfenced only). + LeaderLSNKnown uint64 `json:"leaderLSNKnown,omitempty"` +} + +// Status is a node's replication view, for Studio and the audit trail. +type Status struct { + NodeID string `json:"nodeID"` + Role Role `json:"role"` + Epoch uint64 `json:"epoch"` + FencedFor string `json:"fencedFor,omitempty"` + AppliedLSN uint64 `json:"appliedLSN"` + // LeaderLSN and LeaderContactMS describe a follower's view of its leader; + // LeaderContactMS is -1 when it has never heard from one. + LeaderLSN uint64 `json:"leaderLSN"` + LeaderContactMS int64 `json:"leaderContactMS"` + LagLSN uint64 `json:"lagLSN"` + // Promotable is true only for a follower that is in contact with its + // leader and has applied everything the leader has. When false, + // PromoteBlockedReason says why. + Promotable bool `json:"promotable"` + PromoteBlockedReason string `json:"promoteBlockedReason,omitempty"` +} + +// Status snapshots this node's role, epoch and, on a follower, whether it may be promoted. +func (c *Cluster) Status() Status { + st := c.state.Get() + out := Status{NodeID: c.nodeID, Role: st.Role, Epoch: st.Epoch, FencedFor: st.FencedFor, + AppliedLSN: c.engine.CurrentLSN(), LeaderContactMS: -1} + switch st.Role { + case RoleLeader: + out.PromoteBlockedReason = "This node is the leader." + case RoleFenced: + out.PromoteBlockedReason = fmt.Sprintf("This node was fenced at epoch %d and its data may be behind or diverged; rebuild it as a follower.", st.Epoch) + default: + c.mu.Lock() + f := c.follower + c.mu.Unlock() + if f == nil { + out.PromoteBlockedReason = "No leader connection configured." + return out + } + out.AppliedLSN = f.LastAppliedLSN() + out.LeaderLSN = f.LeaderLSN() + if out.LeaderLSN > out.AppliedLSN { + out.LagLSN = out.LeaderLSN - out.AppliedLSN + } + contact := f.LastContact() + switch { + case contact.IsZero(): + out.PromoteBlockedReason = "Has not heard from the leader yet." + case time.Since(contact) > leaderFreshness: + out.LeaderContactMS = time.Since(contact).Milliseconds() + out.PromoteBlockedReason = fmt.Sprintf("Leader not heard from for %.0f s; its latest writes may not be here.", time.Since(contact).Seconds()) + case out.LagLSN > 0: + out.LeaderContactMS = time.Since(contact).Milliseconds() + out.PromoteBlockedReason = fmt.Sprintf("Behind the leader by %d records (applied %d of %d).", out.LagLSN, out.AppliedLSN, out.LeaderLSN) + default: + out.LeaderContactMS = time.Since(contact).Milliseconds() + out.Promotable = true + } + } + return out +} + +// Promote turns this follower into the leader at epoch+1. +// +// With the old leader reachable it fences the leader first (freezing its +// writes and learning its final LSN), waits until it has applied everything up +// to that LSN, and only then starts leading, so no acknowledged write is lost. +// With the leader unreachable it refuses unless AcceptDataLoss is set; the new +// leader then keeps trying to fence the old one in the background. +func (c *Cluster) Promote(ctx context.Context, opts PromoteOptions) (PromoteResult, error) { + if opts.FenceTimeout <= 0 { + opts.FenceTimeout = defaultFenceTimeout + } + if opts.CatchUpTimeout <= 0 { + opts.CatchUpTimeout = defaultCatchUpTimeout + } + c.mu.Lock() + if c.state.Get().Role != RoleFollower || c.follower == nil { + c.mu.Unlock() + return PromoteResult{}, ErrNotFollower + } + if c.promoting { + c.mu.Unlock() + return PromoteResult{}, ErrPromoteInProgress + } + c.promoting = true + f := c.follower + c.mu.Unlock() + defer func() { c.mu.Lock(); c.promoting = false; c.mu.Unlock() }() + + newEpoch := c.state.Epoch() + 1 + res := PromoteResult{Epoch: newEpoch} + + fctx, cancel := context.WithTimeout(ctx, opts.FenceTimeout) + fr, err := f.client.Fence(fctx, &beastv1.FenceRequest{Epoch: newEpoch, CandidateId: c.nodeID}) + cancel() + switch { + case err == nil: + res.OldLeaderFenced, res.FinalLSN = true, fr.FinalLsn + if err := c.waitApplied(ctx, f, fr.FinalLsn, opts.CatchUpTimeout); err != nil { + return res, err + } + case status.Code(err) == codes.Unavailable || status.Code(err) == codes.DeadlineExceeded: + if !opts.AcceptDataLoss { + return res, ErrLeaderUnreachable + } + res.DataLossPossible, res.LeaderLSNKnown = true, f.LeaderLSN() + default: + // The leader answered and refused (already fenced for someone else, or + // at a higher epoch): never override that, even with AcceptDataLoss. + return res, fmt.Errorf("%w: old leader answered: %s", ErrFenceRefused, status.Convert(err).Message()) + } + + if err := c.becomeLeader(f, newEpoch, &res); err != nil { + return res, err + } + if res.DataLossPossible { + go c.fenceUntilDone(f, newEpoch, fenceRetryEvery) + } + return res, nil +} + +// waitApplied blocks until the follower has applied lsn. +func (c *Cluster) waitApplied(ctx context.Context, f *FollowerClient, lsn uint64, timeout time.Duration) error { + deadline := time.NewTimer(timeout) + defer deadline.Stop() + tick := time.NewTicker(10 * time.Millisecond) + defer tick.Stop() + for f.LastAppliedLSN() < lsn { + select { + case <-ctx.Done(): + return ctx.Err() + case <-deadline.C: + return fmt.Errorf("%w (applied %d of %d)", ErrCatchUpTimeout, f.LastAppliedLSN(), lsn) + case <-tick.C: + } + } + return nil +} + +// becomeLeader persists the new epoch, then stops following, then opens +// writes. Persisting first means a crash at any point restarts as leader at +// the new epoch (ResolveRole), never as a follower of the old leader. +func (c *Cluster) becomeLeader(f *FollowerClient, epoch uint64, res *PromoteResult) error { + c.mu.Lock() + defer c.mu.Unlock() + if err := c.state.Set(State{Epoch: epoch, Role: RoleLeader, ServedEpoch: epoch, PromotedAtLSN: f.LastAppliedLSN()}); err != nil { + return err + } + if c.syncStop != nil { + c.syncStop() + <-c.syncDone + } + res.AppliedLSN = c.engine.CurrentLSN() + c.engine.SetCommitObserver(c.leader) + c.leader.SetActive(true) + c.engine.SetReadOnly(false) + log.Printf("replication: promoted to leader at epoch %d (LSN %d, old leader fenced: %t)", epoch, res.AppliedLSN, res.OldLeaderFenced) + return nil +} + +// fenceUntilDone keeps asking the old leader to freeze until it answers, so a +// partitioned or restarted old leader is stopped as soon as it is reachable. +func (c *Cluster) fenceUntilDone(f *FollowerClient, epoch uint64, every time.Duration) { + c.mu.Lock() + ctx := c.runCtx + c.mu.Unlock() + if ctx == nil { + ctx = context.Background() + } + for ctx.Err() == nil { + fctx, cancel := context.WithTimeout(ctx, every) + _, err := f.client.Fence(fctx, &beastv1.FenceRequest{Epoch: epoch, CandidateId: c.nodeID}) + cancel() + if err == nil { + log.Printf("replication: old leader reached and fenced at epoch %d", epoch) + return + } + if status.Code(err) == codes.FailedPrecondition { + log.Printf("replication: old leader will not be fenced at epoch %d: %v", epoch, err) + return + } + select { + case <-time.After(every): + case <-ctx.Done(): + } + } +} diff --git a/internal/replication/promote_test.go b/internal/replication/promote_test.go new file mode 100644 index 0000000..b0d7bb0 --- /dev/null +++ b/internal/replication/promote_test.go @@ -0,0 +1,507 @@ +package replication + +import ( + "context" + "errors" + "fmt" + "sync" + "sync/atomic" + "testing" + "time" + + beastv1 "github.com/ChromaBeast/beastdb/api/proto" + "github.com/ChromaBeast/beastdb/internal/api" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/status" +) + +func TestPromoteWithReachableLeaderLosesNoAcknowledgedWrite(t *testing.T) { + fastFencing(t) + leader := startTNode(t, "leader", t.TempDir(), RoleLeader, "") + fol := startTNode(t, "r1", t.TempDir(), RoleFollower, "") + f := fol.follow(leader.addr) + + // Writers run straight through the promotion. Every write the leader + // acknowledged must exist on the new leader; writes refused after the + // fence must not. + var acked sync.Map + var stop atomic.Bool + var wg sync.WaitGroup + for w := 0; w < 4; w++ { + wg.Add(1) + go func(w int) { + defer wg.Done() + for i := 0; !stop.Load(); i++ { + key := uint64(w*1_000_000 + i) + if err := leader.engine.Put(key, []byte("v")); err == nil { + acked.Store(key, true) + } else if !errors.Is(err, api.ErrReadOnlyReplica) { + t.Errorf("unexpected write error: %v", err) + return + } + } + }(w) + } + if !waitUntil(t, func() bool { return f.LastAppliedLSN() > 20 }) { + t.Fatal("no replication before promotion") + } + + res, err := fol.cluster.Promote(context.Background(), PromoteOptions{}) + stop.Store(true) + wg.Wait() + if err != nil { + t.Fatalf("Promote: %v", err) + } + if !res.OldLeaderFenced || res.DataLossPossible || res.Epoch != 2 { + t.Fatalf("unexpected result %+v", res) + } + + n := 0 + acked.Range(func(k, _ any) bool { + n++ + if !fol.hasKey(k.(uint64), "v") { + t.Errorf("acknowledged key %d missing on the new leader", k) + } + return true + }) + if n == 0 { + t.Fatal("test wrote nothing") + } + if got := fol.engine.CurrentLSN(); got < res.FinalLSN { + t.Fatalf("new leader at LSN %d, behind the fenced leader's final %d", got, res.FinalLSN) + } + + // Old leader refuses writes; new leader takes them. + if err := leader.engine.Put(1, []byte("late")); !errors.Is(err, api.ErrReadOnlyReplica) { + t.Fatalf("old leader write after fence: err = %v, want ErrReadOnlyReplica", err) + } + if err := fol.engine.Put(7, []byte("new-era")); err != nil { + t.Fatalf("new leader write: %v", err) + } + if got := leader.cluster.Status().Role; got != RoleFenced { + t.Fatalf("old leader role = %s, want fenced", got) + } + if got := fol.cluster.Status().Role; got != RoleLeader { + t.Fatalf("new leader role = %s", got) + } + if fol.cluster.ServedEpoch() != 2 { + t.Fatalf("new leader serves epoch %d, want 2", fol.cluster.ServedEpoch()) + } +} + +func TestFenceIsIdempotentAndExclusive(t *testing.T) { + leader := startTNode(t, "leader", t.TempDir(), RoleLeader, "") + if err := leader.engine.Put(1, []byte("a")); err != nil { + t.Fatal(err) + } + lsn1, err := leader.cluster.fence(2, "r1") + if err != nil { + t.Fatal(err) + } + lsn2, err := leader.cluster.fence(2, "r1") + if err != nil || lsn2 != lsn1 { + t.Fatalf("same candidate retry: (%d, %v), want (%d, nil)", lsn2, err, lsn1) + } + // Two followers must never both be promoted at the same epoch. + if _, err := leader.cluster.fence(2, "r2"); !errors.Is(err, ErrFenceRefused) { + t.Fatalf("second candidate at same epoch: err = %v, want ErrFenceRefused", err) + } + if _, err := leader.cluster.fence(1, "r3"); !errors.Is(err, ErrFenceRefused) { + t.Fatalf("stale epoch: err = %v, want ErrFenceRefused", err) + } +} + +func TestLeaderStepsDownWhenItSeesHigherEpoch(t *testing.T) { + for _, via := range []string{"StreamWAL", "Ack"} { + t.Run(via, func(t *testing.T) { + dir := t.TempDir() + leader := startTNode(t, "leader", dir, RoleLeader, "") + if err := leader.engine.Put(1, []byte("a")); err != nil { + t.Fatal(err) + } + conn, err := grpc.NewClient(leader.addr, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + rc := beastv1.NewReplicationServiceClient(conn) + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + if via == "Ack" { + _, err = rc.Ack(ctx, &beastv1.ReplicationAck{ReplicaId: "peer", AcknowledgedLsn: 1, Epoch: 5}) + } else { + var s grpc.ServerStreamingClient[beastv1.WALRecordMessage] + if s, err = rc.StreamWAL(ctx, &beastv1.StreamWALRequest{ReplicaId: "peer", FromLsn: 1, Epoch: 5}); err == nil { + _, err = s.Recv() + } + } + if status.Code(err) != codes.FailedPrecondition { + t.Fatalf("%s at higher epoch: err = %v, want FailedPrecondition", via, err) + } + if err := leader.engine.Put(2, []byte("b")); !errors.Is(err, api.ErrReadOnlyReplica) { + t.Fatalf("write after step-down: err = %v, want ErrReadOnlyReplica", err) + } + st := leader.cluster.Status() + if st.Role != RoleFenced || st.Epoch != 5 { + t.Fatalf("status after step-down: %+v", st) + } + + // The fence survives a restart: the old leader must not come back writable. + leader.stop() + re := startTNode(t, "leader", dir, RoleLeader, "") + if re.cluster.Role() != RoleFenced { + t.Fatalf("restarted role = %s, want fenced", re.cluster.Role()) + } + if err := re.engine.Put(3, []byte("c")); !errors.Is(err, api.ErrReadOnlyReplica) { + t.Fatalf("write after restart: err = %v, want ErrReadOnlyReplica", err) + } + }) + } +} + +// staleLeader is a replication server that ignores the epoch in requests, as a +// partitioned old leader that has not yet heard of the promotion would. +type staleLeader struct { + beastv1.UnimplementedReplicationServiceServer + epoch uint64 +} + +func (s *staleLeader) StreamWAL(req *beastv1.StreamWALRequest, stream grpc.ServerStreamingServer[beastv1.WALRecordMessage]) error { + if err := stream.Send(&beastv1.WALRecordMessage{Lsn: 1, OpType: 1, Key: []byte{1, 0, 0, 0, 0, 0, 0, 0}, Value: []byte("stale"), Epoch: s.epoch}); err != nil { + return err + } + <-stream.Context().Done() + return nil +} + +func TestFollowerRejectsStaleEpochLeader(t *testing.T) { + dir := t.TempDir() + fol := startTNode(t, "r1", dir, RoleFollower, "") + if err := fol.cluster.state.Set(State{Epoch: 3, Role: RoleFollower}); err != nil { + t.Fatal(err) + } + + srv := grpc.NewServer() + beastv1.RegisterReplicationServiceServer(srv, &staleLeader{epoch: 2}) + lis, addr := listen(t) + go func() { _ = srv.Serve(lis) }() + defer srv.Stop() + + conn, err := grpc.NewClient(addr, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + f := NewFollowerClient("r1", fol.engine, conn) + f.SetStateStore(fol.cluster.state) + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + if err := f.SyncLoop(ctx); !errors.Is(err, ErrStaleLeader) { + t.Fatalf("SyncLoop err = %v, want ErrStaleLeader", err) + } + if fol.hasKey(1, "stale") || f.LastAppliedLSN() != 0 { + t.Fatal("record from a stale-epoch leader was applied") + } + if fol.cluster.Epoch() != 3 { + t.Fatalf("epoch moved to %d", fol.cluster.Epoch()) + } +} + +func TestFollowerAdoptsAndPersistsNewerEpoch(t *testing.T) { + leader := startTNode(t, "leader", t.TempDir(), RoleLeader, "") + if err := leader.cluster.state.Set(State{Epoch: 4, Role: RoleLeader, ServedEpoch: 4}); err != nil { + t.Fatal(err) + } + if err := leader.engine.Put(1, []byte("a")); err != nil { + t.Fatal(err) + } + dir := t.TempDir() + fol := startTNode(t, "r1", dir, RoleFollower, "") + fol.follow(leader.addr) + if !waitUntil(t, func() bool { return fol.hasKey(1, "a") }) { + t.Fatal("not replicated") + } + if fol.cluster.Epoch() != 4 { + t.Fatalf("follower epoch = %d, want 4", fol.cluster.Epoch()) + } + fol.stop() + re, err := LoadStateStore(dir) + if err != nil || re.Epoch() != 4 { + t.Fatalf("persisted epoch = %v (%v), want 4", re.Epoch(), err) + } +} + +func TestPromoteRefusedWhenLeaderUnreachableUnlessDataLossAccepted(t *testing.T) { + fastFencing(t) + leader := startTNode(t, "leader", t.TempDir(), RoleLeader, "") + fol := startTNode(t, "r1", t.TempDir(), RoleFollower, "") + f := fol.follow(leader.addr) + if err := leader.engine.Put(1, []byte("a")); err != nil { + t.Fatal(err) + } + caughtUp(t, f, leader) + + leader.partition() + opts := PromoteOptions{FenceTimeout: 300 * time.Millisecond} + if _, err := fol.cluster.Promote(context.Background(), opts); !errors.Is(err, ErrLeaderUnreachable) { + t.Fatalf("Promote err = %v, want ErrLeaderUnreachable", err) + } + if st := fol.cluster.Status(); st.Role != RoleFollower || st.Epoch != 1 { + t.Fatalf("refused promotion changed state: %+v", st) + } + if err := fol.engine.Put(2, []byte("x")); !errors.Is(err, api.ErrReadOnlyReplica) { + t.Fatalf("follower accepted a write after refused promotion: %v", err) + } + + opts.AcceptDataLoss = true + res, err := fol.cluster.Promote(context.Background(), opts) + if err != nil { + t.Fatalf("forced Promote: %v", err) + } + if res.OldLeaderFenced || !res.DataLossPossible || res.Epoch != 2 { + t.Fatalf("unexpected forced result %+v", res) + } + if err := fol.engine.Put(2, []byte("x")); err != nil { + t.Fatalf("new leader write: %v", err) + } +} + +// The old leader is cut off, a follower is force-promoted, and the old leader +// keeps taking writes (the unavoidable split-brain window). When the partition +// heals the new leader fences it, with no operator action. +func TestPartitionHealFencesOldLeader(t *testing.T) { + fastFencing(t) + leader := startTNode(t, "leader", t.TempDir(), RoleLeader, "") + fol := startTNode(t, "r1", t.TempDir(), RoleFollower, "") + f := fol.follow(leader.addr) + if err := leader.engine.Put(1, []byte("a")); err != nil { + t.Fatal(err) + } + caughtUp(t, f, leader) + + leader.partition() + if _, err := fol.cluster.Promote(context.Background(), PromoteOptions{AcceptDataLoss: true, FenceTimeout: 300 * time.Millisecond}); err != nil { + t.Fatal(err) + } + // Still writable on the far side of the partition: this is the divergence the fence bounds. + if err := leader.engine.Put(2, []byte("diverged")); err != nil { + t.Fatalf("partitioned leader should still accept writes until fenced: %v", err) + } + + leader.heal() + if !waitUntil(t, func() bool { return leader.cluster.Role() == RoleFenced }) { + t.Fatal("old leader was not fenced after the partition healed") + } + if err := leader.engine.Put(3, []byte("after-heal")); !errors.Is(err, api.ErrReadOnlyReplica) { + t.Fatalf("old leader write after heal: err = %v, want ErrReadOnlyReplica", err) + } + if fol.hasKey(2, "diverged") { + t.Fatal("new leader must not contain the old leader's partitioned write") + } +} + +// An old leader that crashed (never fenced) and restarts with -role leader at +// its old epoch is fenced as soon as the promoted node reaches it. +func TestOldLeaderRestartedAfterPromotionIsFenced(t *testing.T) { + fastFencing(t) + ldir := t.TempDir() + leader := startTNode(t, "leader", ldir, RoleLeader, "") + fol := startTNode(t, "r1", t.TempDir(), RoleFollower, "") + f := fol.follow(leader.addr) + if err := leader.engine.Put(1, []byte("a")); err != nil { + t.Fatal(err) + } + caughtUp(t, f, leader) + + addr := leader.addr + leader.stop() // crash + if _, err := fol.cluster.Promote(context.Background(), PromoteOptions{AcceptDataLoss: true, FenceTimeout: 300 * time.Millisecond}); err != nil { + t.Fatal(err) + } + + back := startTNode(t, "leader", ldir, RoleLeader, addr) + if !waitUntil(t, func() bool { return back.cluster.Role() == RoleFenced }) { + t.Fatal("restarted old leader stayed a leader") + } + if err := back.engine.Put(9, []byte("x")); !errors.Is(err, api.ErrReadOnlyReplica) { + t.Fatalf("restarted old leader accepted a write: %v", err) + } + // And it now refuses to be restarted as a follower on its stale data. + back.stop() + if _, err := NewClusterForTest(t, ldir, RoleFollower); err == nil { + t.Fatal("fenced node must refuse to start as a follower without a rebuild") + } +} + +func TestPromotedNodeRestartedAsFollowerStaysLeader(t *testing.T) { + leader := startTNode(t, "leader", t.TempDir(), RoleLeader, "") + dir := t.TempDir() + fol := startTNode(t, "r1", dir, RoleFollower, "") + f := fol.follow(leader.addr) + if err := leader.engine.Put(1, []byte("a")); err != nil { + t.Fatal(err) + } + caughtUp(t, f, leader) + if _, err := fol.cluster.Promote(context.Background(), PromoteOptions{}); err != nil { + t.Fatal(err) + } + fol.stop() + + // The service unit still says -role follower; the persisted epoch wins. + re := startTNode(t, "r1", dir, RoleFollower, "") + if re.cluster.Role() != RoleLeader || re.cluster.Epoch() != 2 { + t.Fatalf("restarted: role %s epoch %d, want leader at 2", re.cluster.Role(), re.cluster.Epoch()) + } + if !re.hasKey(1, "a") { + t.Fatal("data lost across restart") + } + if err := re.engine.Put(2, []byte("b")); err != nil { + t.Fatalf("restarted leader should accept writes: %v", err) + } +} + +// Crash after the new epoch was persisted but before writes were opened: the +// node comes back as leader, never as a follower of the old leader. +func TestCrashBetweenPersistAndOpenRestartsAsLeader(t *testing.T) { + dir := t.TempDir() + fol := startTNode(t, "r1", dir, RoleFollower, "") + if err := fol.cluster.state.Set(State{Epoch: 2, Role: RoleLeader, ServedEpoch: 2}); err != nil { + t.Fatal(err) + } + fol.stop() // "crash": engine still read-only, state already says leader + + re := startTNode(t, "r1", dir, RoleFollower, "") + if re.cluster.Role() != RoleLeader { + t.Fatalf("role = %s, want leader", re.cluster.Role()) + } + if err := re.engine.Put(1, []byte("a")); err != nil { + t.Fatalf("write after recovery: %v", err) + } +} + +// A lagging follower can be promoted when the leader is reachable: the fence +// fixes the leader's final LSN and the follower waits to reach it. +func TestPromoteLaggingFollowerWaitsForFinalLSN(t *testing.T) { + fastFencing(t) + leader := startTNode(t, "leader", t.TempDir(), RoleLeader, "") + fol := startTNode(t, "r1", t.TempDir(), RoleFollower, "") + // Attached but not syncing yet: the follower is behind from the start. + conn, err := grpc.NewClient(leader.addr, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + f := NewFollowerClient("r1", fol.engine, conn) + f.SetStateStore(fol.cluster.state) + // In production a follower adopts the leader's epoch on first contact. + if err := fol.cluster.state.Set(State{Epoch: 1, Role: RoleFollower}); err != nil { + t.Fatal(err) + } + fol.cluster.mu.Lock() + fol.cluster.follower = f + fol.cluster.mu.Unlock() + for i := uint64(1); i <= 50; i++ { + if err := leader.engine.Put(i, []byte(fmt.Sprint(i))); err != nil { + t.Fatal(err) + } + } + if st := fol.cluster.Status(); st.Promotable || st.PromoteBlockedReason == "" { + t.Fatalf("a follower that has not heard from the leader must not look promotable: %+v", st) + } + + // First attempt: leader fenced, but the follower cannot catch up in time. + _, err = fol.cluster.Promote(context.Background(), PromoteOptions{CatchUpTimeout: 150 * time.Millisecond}) + if !errors.Is(err, ErrCatchUpTimeout) { + t.Fatalf("Promote err = %v, want ErrCatchUpTimeout", err) + } + if st := fol.cluster.Status(); st.Role != RoleFollower { + t.Fatalf("a failed catch-up must not promote: %+v", st) + } + if err := leader.engine.Put(99, []byte("z")); !errors.Is(err, api.ErrReadOnlyReplica) { + t.Fatalf("leader stays fenced after a failed promotion attempt: %v", err) + } + + // Retry with sync running: same candidate, so the fence is idempotent. + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + fol.cluster.RunFollower(ctx, f) + res, err := fol.cluster.Promote(context.Background(), PromoteOptions{}) + if err != nil { + t.Fatalf("retry: %v", err) + } + if !res.OldLeaderFenced || res.AppliedLSN < 50 { + t.Fatalf("unexpected result %+v", res) + } + for i := uint64(1); i <= 50; i++ { + if !fol.hasKey(i, fmt.Sprint(i)) { + t.Fatalf("key %d missing after promotion", i) + } + } +} + +func TestStatusPromotableWhenCaughtUp(t *testing.T) { + fastFencing(t) + leader := startTNode(t, "leader", t.TempDir(), RoleLeader, "") + fol := startTNode(t, "r1", t.TempDir(), RoleFollower, "") + f := fol.follow(leader.addr) + for i := uint64(1); i <= 20; i++ { + _ = leader.engine.Put(i, []byte("v")) + } + if !waitUntil(t, func() bool { return fol.cluster.Status().Promotable }) { + t.Fatalf("a caught-up follower with a live leader should be promotable: %+v", fol.cluster.Status()) + } + if st := leader.cluster.Status(); st.Promotable || st.PromoteBlockedReason == "" { + t.Fatalf("a leader is not promotable and must say why: %+v", st) + } + + // Silence from the leader makes the follower non-promotable and says so. + leader.partition() + if !waitUntil(t, func() bool { return !fol.cluster.Status().Promotable }) { + t.Fatal("follower still promotable after leader went silent") + } + if st := fol.cluster.Status(); st.PromoteBlockedReason == "" { + t.Fatalf("no reason given: %+v", st) + } + _ = f +} + +func TestLeaderRefusesFollowerAheadOfIt(t *testing.T) { + leader := startTNode(t, "leader", t.TempDir(), RoleLeader, "") + _ = leader.engine.Put(1, []byte("a")) + conn, err := grpc.NewClient(leader.addr, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + s, err := beastv1.NewReplicationServiceClient(conn).StreamWAL(ctx, &beastv1.StreamWALRequest{ReplicaId: "old", FromLsn: 500}) + if err == nil { + _, err = s.Recv() + } + if status.Code(err) != codes.FailedPrecondition { + t.Fatalf("err = %v, want FailedPrecondition for a follower ahead of the leader", err) + } +} + +func TestInactiveNodeServesNoWAL(t *testing.T) { + fol := startTNode(t, "r1", t.TempDir(), RoleFollower, "") + conn, err := grpc.NewClient(fol.addr, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + rc := beastv1.NewReplicationServiceClient(conn) + if _, err := rc.Fence(ctx, &beastv1.FenceRequest{Epoch: 9, CandidateId: "x"}); status.Code(err) != codes.FailedPrecondition { + t.Fatalf("Fence on a follower: err = %v, want FailedPrecondition", err) + } + if fol.cluster.Epoch() != 0 { + t.Fatal("a follower must not change epoch because it was asked to fence") + } +} diff --git a/internal/replication/state.go b/internal/replication/state.go new file mode 100644 index 0000000..d1632ce --- /dev/null +++ b/internal/replication/state.go @@ -0,0 +1,170 @@ +package replication + +import ( + "encoding/json" + "errors" + "fmt" + "os" + "path/filepath" + "sync" +) + +// Role is a node's persisted place in the replication topology. +type Role string + +const ( + // RoleLeader accepts client writes and serves the WAL to followers. + RoleLeader Role = "leader" + // RoleFollower is read-only and applies a leader's WAL. + RoleFollower Role = "follower" + // RoleFenced is a former leader that saw a higher epoch. It is read-only + // and stays so across restarts until an operator rebuilds it. + RoleFenced Role = "fenced" +) + +// StateFile is the name of the persisted state inside the data directory. +const StateFile = "cluster-state.json" + +// State is the durable replication state of one node. The epoch only ever +// increases; a role change at the same epoch is limited to leader -> fenced. +type State struct { + // Epoch is the highest leader term this node has seen or led. + Epoch uint64 `json:"epoch"` + Role Role `json:"role"` + // ServedEpoch is the epoch this node led at. A fenced leader's Epoch moves + // up to the fencing epoch, but what it already wrote was written at + // ServedEpoch, so that is what it stamps on records it still serves. + ServedEpoch uint64 `json:"servedEpoch,omitempty"` + // FencedFor is the replica that fenced this node (RoleFenced only). A + // different replica asking to fence the same epoch is refused, so two + // followers cannot both be promoted at one epoch. + FencedFor string `json:"fencedFor,omitempty"` + // PromotedAtLSN is the WAL position at which this node became leader by + // promotion (0 if it never was promoted). + PromotedAtLSN uint64 `json:"promotedAtLSN,omitempty"` +} + +// ErrEpochRegression is returned when an update would lower the epoch or make +// a disallowed role change. +var ErrEpochRegression = errors.New("replication: epoch cannot go backwards") + +// StateStore persists State with write-to-temp, fsync, rename, so a crash +// leaves either the old or the new state, never a torn one. +type StateStore struct { + mu sync.Mutex + path string + state State +} + +// LoadStateStore reads /cluster-state.json. A missing file is a fresh +// node (epoch 0, no role). A present but unreadable file is an error: the +// node must not guess its epoch. +func LoadStateStore(dir string) (*StateStore, error) { + st := &StateStore{path: filepath.Join(dir, StateFile)} + data, err := os.ReadFile(st.path) + if errors.Is(err, os.ErrNotExist) { + return st, nil + } + if err != nil { + return nil, err + } + if err := json.Unmarshal(data, &st.state); err != nil { + return nil, fmt.Errorf("replication: %s is corrupt: %w", st.path, err) + } + switch st.state.Role { + case RoleLeader, RoleFollower, RoleFenced: + default: + return nil, fmt.Errorf("replication: %s has unknown role %q", st.path, st.state.Role) + } + return st, nil +} + +// Get returns a copy of the current state. +func (s *StateStore) Get() State { + s.mu.Lock() + defer s.mu.Unlock() + return s.state +} + +// Epoch returns the current epoch. +func (s *StateStore) Epoch() uint64 { return s.Get().Epoch } + +// Set durably replaces the state if it is a legal successor of the current +// one: the epoch may not decrease, and at an equal epoch the role may only +// stay the same or move from leader to fenced. +func (s *StateStore) Set(next State) error { + s.mu.Lock() + defer s.mu.Unlock() + cur := s.state + if next.Epoch < cur.Epoch { + return fmt.Errorf("%w: have %d, asked for %d", ErrEpochRegression, cur.Epoch, next.Epoch) + } + if next.Epoch == cur.Epoch && cur.Role != "" && next.Role != cur.Role && + !(cur.Role == RoleLeader && next.Role == RoleFenced) { + return fmt.Errorf("%w: %s -> %s at epoch %d", ErrEpochRegression, cur.Role, next.Role, cur.Epoch) + } + if err := writeAtomic(s.path, next); err != nil { + return err + } + s.state = next + return nil +} + +func writeAtomic(path string, st State) error { + data, err := json.Marshal(st) + if err != nil { + return err + } + tmp := path + ".tmp" + f, err := os.OpenFile(tmp, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600) + if err != nil { + return err + } + if _, err := f.Write(data); err != nil { + _ = f.Close() + return err + } + if err := f.Sync(); err != nil { + _ = f.Close() + return err + } + if err := f.Close(); err != nil { + return err + } + if err := os.Rename(tmp, path); err != nil { + return err + } + // Persist the rename itself; not supported on every platform, so best effort. + if d, err := os.Open(filepath.Dir(path)); err == nil { + _ = d.Sync() + _ = d.Close() + } + return nil +} + +// ResolveRole decides the role a node starts in from its persisted state and +// the -role flag. The persisted state wins where ignoring it would break +// fencing: +// +// - fenced stays fenced (a fenced node refuses to start as a follower, since +// its data may hold writes the new leader never saw); +// - a promoted leader stays leader when restarted with -role follower; +// - a persisted follower cannot be started as leader by flag: it must be +// promoted, which bumps the epoch. +func ResolveRole(st State, flag Role) (Role, error) { + switch st.Role { + case RoleFenced: + if flag == RoleFollower { + return "", fmt.Errorf("node was fenced at epoch %d: its data may hold writes the current leader never saw; wipe the data directory to rejoin it as a follower", st.Epoch) + } + return RoleFenced, nil + case RoleLeader: + return RoleLeader, nil + case RoleFollower: + if flag == RoleLeader { + return "", fmt.Errorf("node is a follower at epoch %d: promote it (POST /api/replication/promote) instead of starting it with -role leader", st.Epoch) + } + return RoleFollower, nil + } + return flag, nil +} diff --git a/internal/replication/state_test.go b/internal/replication/state_test.go new file mode 100644 index 0000000..88d88a8 --- /dev/null +++ b/internal/replication/state_test.go @@ -0,0 +1,119 @@ +package replication + +import ( + "errors" + "os" + "path/filepath" + "testing" +) + +func TestStateStorePersistsAcrossReopen(t *testing.T) { + dir := t.TempDir() + st, err := LoadStateStore(dir) + if err != nil { + t.Fatal(err) + } + if got := st.Get(); got != (State{}) { + t.Fatalf("fresh node should have zero state, got %+v", got) + } + want := State{Epoch: 3, Role: RoleLeader, ServedEpoch: 3, PromotedAtLSN: 42} + if err := st.Set(want); err != nil { + t.Fatal(err) + } + re, err := LoadStateStore(dir) + if err != nil { + t.Fatal(err) + } + if got := re.Get(); got != want { + t.Fatalf("reopened state = %+v, want %+v", got, want) + } +} + +func TestStateStoreNeverMovesBackwards(t *testing.T) { + st, _ := LoadStateStore(t.TempDir()) + if err := st.Set(State{Epoch: 5, Role: RoleFollower}); err != nil { + t.Fatal(err) + } + if err := st.Set(State{Epoch: 4, Role: RoleFollower}); !errors.Is(err, ErrEpochRegression) { + t.Fatalf("lower epoch: err = %v, want ErrEpochRegression", err) + } + // At one epoch a follower cannot become leader or fenced by update; that + // would be a promotion without an epoch bump. + if err := st.Set(State{Epoch: 5, Role: RoleLeader}); !errors.Is(err, ErrEpochRegression) { + t.Fatalf("same-epoch follower->leader: err = %v, want ErrEpochRegression", err) + } + if got := st.Epoch(); got != 5 { + t.Fatalf("epoch changed by refused updates: %d", got) + } + if err := st.Set(State{Epoch: 6, Role: RoleLeader}); err != nil { + t.Fatalf("higher epoch should be allowed: %v", err) + } + if err := st.Set(State{Epoch: 6, Role: RoleFenced}); err != nil { + t.Fatalf("leader -> fenced at same epoch should be allowed: %v", err) + } + if err := st.Set(State{Epoch: 6, Role: RoleLeader}); !errors.Is(err, ErrEpochRegression) { + t.Fatalf("fenced -> leader at same epoch: err = %v, want ErrEpochRegression", err) + } +} + +// A crash during Set leaves a temp file behind (written, not yet renamed). +// The previous state must still load, and a later Set must still work. +func TestStateStoreSurvivesCrashMidWrite(t *testing.T) { + dir := t.TempDir() + st, _ := LoadStateStore(dir) + if err := st.Set(State{Epoch: 2, Role: RoleFollower}); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(dir, StateFile+".tmp"), []byte(`{"epoch":9,"ro`), 0600); err != nil { + t.Fatal(err) + } + re, err := LoadStateStore(dir) + if err != nil { + t.Fatalf("stray temp file must not break loading: %v", err) + } + if re.Epoch() != 2 { + t.Fatalf("epoch = %d, want 2 (the temp file is not committed)", re.Epoch()) + } + if err := re.Set(State{Epoch: 3, Role: RoleFollower}); err != nil { + t.Fatalf("Set after stray temp file: %v", err) + } +} + +func TestStateStoreRefusesCorruptFile(t *testing.T) { + for name, body := range map[string]string{ + "garbage": "not json", + "unknown role": `{"epoch":2,"role":"king"}`, + } { + dir := t.TempDir() + if err := os.WriteFile(filepath.Join(dir, StateFile), []byte(body), 0600); err != nil { + t.Fatal(err) + } + if _, err := LoadStateStore(dir); err == nil { + t.Fatalf("%s: a node must not guess its epoch from a corrupt state file", name) + } + } +} + +func TestResolveRole(t *testing.T) { + cases := []struct { + name string + st State + flag Role + want Role + wantErr bool + }{ + {"fresh leader", State{}, RoleLeader, RoleLeader, false}, + {"fresh follower", State{}, RoleFollower, RoleFollower, false}, + {"promoted node restarted with follower flag stays leader", State{Epoch: 2, Role: RoleLeader}, RoleFollower, RoleLeader, false}, + {"fenced node restarted as leader stays fenced", State{Epoch: 2, Role: RoleFenced}, RoleLeader, RoleFenced, false}, + {"fenced node cannot restart as follower", State{Epoch: 2, Role: RoleFenced}, RoleFollower, "", true}, + {"follower cannot be started as leader by flag", State{Epoch: 2, Role: RoleFollower}, RoleLeader, "", true}, + {"follower restarts as follower", State{Epoch: 2, Role: RoleFollower}, RoleFollower, RoleFollower, false}, + } + for _, tc := range cases { + got, err := ResolveRole(tc.st, tc.flag) + if (err != nil) != tc.wantErr || got != tc.want { + t.Errorf("%s: got (%q, %v), want (%q, err=%v)", tc.name, got, err, tc.want, tc.wantErr) + } + } +} diff --git a/internal/runtimecfg/runtimecfg.go b/internal/runtimecfg/runtimecfg.go new file mode 100644 index 0000000..47676d8 --- /dev/null +++ b/internal/runtimecfg/runtimecfg.go @@ -0,0 +1,190 @@ +// Package runtimecfg holds the few settings an admin may change while the +// server runs. Everything else stays a startup flag. Changes are validated, +// written atomically to runtime-settings.json in the data directory and applied +// to subscribers at once. On the next start the file overrides the matching +// flags, so a change made in Studio is not undone by a restart. +package runtimecfg + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "sync" +) + +// FileName is the persisted overrides file inside the data directory. +const FileName = "runtime-settings.json" + +// Values are the adjustable settings. +type Values struct { + // WALCheckpointMB is the WAL size in MiB that triggers an automatic checkpoint. + WALCheckpointMB int `json:"wal_checkpoint_mb"` + // LoginMaxAttempts failed sign-ins per client IP within LoginWindowSeconds are allowed. + LoginMaxAttempts int `json:"login_max_attempts"` + LoginWindowSeconds int `json:"login_window_seconds"` + // BackupIntervalMinutes between scheduled backups; 0 turns the schedule off. + BackupIntervalMinutes int `json:"backup_interval_minutes"` + // BackupKeep is how many scheduled backups are retained (manual ones are never pruned). + BackupKeep int `json:"backup_keep"` +} + +// Range is the inclusive bound of one setting. +type Range struct { + Min int `json:"min"` + Max int `json:"max"` +} + +// Ranges lists the accepted bounds, keyed by JSON field name. +var Ranges = map[string]Range{ + "wal_checkpoint_mb": {1, 65536}, + "login_max_attempts": {1, 50}, + "login_window_seconds": {5, 3600}, + // 0 = off; 1-4 are rejected in Validate so a typo cannot flood the disk. + "backup_interval_minutes": {0, 10080}, + "backup_keep": {1, 365}, +} + +func fields(v *Values) map[string]*int { + return map[string]*int{ + "wal_checkpoint_mb": &v.WALCheckpointMB, + "login_max_attempts": &v.LoginMaxAttempts, + "login_window_seconds": &v.LoginWindowSeconds, + "backup_interval_minutes": &v.BackupIntervalMinutes, + "backup_keep": &v.BackupKeep, + } +} + +// Validate checks every value against Ranges. +func Validate(v Values) error { + for name, p := range fields(&v) { + r := Ranges[name] + if *p < r.Min || *p > r.Max { + return fmt.Errorf("%s must be between %d and %d", name, r.Min, r.Max) + } + } + if v.BackupIntervalMinutes > 0 && v.BackupIntervalMinutes < 5 { + return fmt.Errorf("backup_interval_minutes must be 0 (off) or at least 5") + } + return nil +} + +// Store is the live settings holder. Safe for concurrent use. +type Store struct { + mu sync.Mutex + path string + v Values + subs []func(Values) +} + +// Load returns a Store seeded with defaults (the startup flags) and then +// overridden by runtime-settings.json when it exists. An unreadable or invalid +// file is reported and ignored rather than preventing startup. +func Load(dir string, defaults Values) (*Store, error) { + s := &Store{path: filepath.Join(dir, FileName), v: defaults} + b, err := os.ReadFile(s.path) + if err != nil { + if os.IsNotExist(err) { + return s, nil + } + return s, err + } + merged := defaults + if err := json.Unmarshal(b, &merged); err != nil { + return s, fmt.Errorf("%s: %w", FileName, err) + } + if err := Validate(merged); err != nil { + return s, fmt.Errorf("%s: %w", FileName, err) + } + s.v = merged + return s, nil +} + +// Get returns the current values. +func (s *Store) Get() Values { + s.mu.Lock() + defer s.mu.Unlock() + return s.v +} + +// Subscribe registers fn to be called with the new values after every update, +// and once immediately with the current values so it starts in sync. +func (s *Store) Subscribe(fn func(Values)) { + s.mu.Lock() + s.subs = append(s.subs, fn) + v := s.v + s.mu.Unlock() + fn(v) +} + +// Change describes one modified setting for auditing. +type Change struct { + Name string + From, To int +} + +// Update applies a partial change (JSON field name to new value). Unknown names +// and out-of-range values are rejected and nothing changes. On success it +// persists the file, notifies subscribers and returns what changed. +func (s *Store) Update(patch map[string]int) ([]Change, error) { + s.mu.Lock() + next := s.v + nf := fields(&next) + for name := range patch { + if _, ok := nf[name]; !ok { + s.mu.Unlock() + return nil, fmt.Errorf("unknown setting %q", name) + } + } + var changes []Change + of := fields(&s.v) + for name, val := range patch { + if *of[name] != val { + changes = append(changes, Change{Name: name, From: *of[name], To: val}) + } + *nf[name] = val + } + if err := Validate(next); err != nil { + s.mu.Unlock() + return nil, err + } + if len(changes) == 0 { + s.mu.Unlock() + return nil, nil + } + if err := writeAtomic(s.path, next); err != nil { + s.mu.Unlock() + return nil, fmt.Errorf("could not save settings: %w", err) + } + s.v = next + subs := append([]func(Values){}, s.subs...) + s.mu.Unlock() + for _, fn := range subs { + fn(next) + } + return changes, nil +} + +func writeAtomic(path string, v Values) error { + b, err := json.MarshalIndent(v, "", " ") + if err != nil { + return err + } + tmp := path + ".tmp" + f, err := os.OpenFile(tmp, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o600) + if err != nil { + return err + } + if _, err := f.Write(append(b, '\n')); err != nil { + f.Close() + return err + } + if err := f.Sync(); err != nil { + f.Close() + return err + } + if err := f.Close(); err != nil { + return err + } + return os.Rename(tmp, path) +} diff --git a/internal/runtimecfg/runtimecfg_test.go b/internal/runtimecfg/runtimecfg_test.go new file mode 100644 index 0000000..af12c40 --- /dev/null +++ b/internal/runtimecfg/runtimecfg_test.go @@ -0,0 +1,84 @@ +package runtimecfg + +import ( + "os" + "path/filepath" + "strings" + "testing" +) + +var defaults = Values{WALCheckpointMB: 64, LoginMaxAttempts: 5, LoginWindowSeconds: 30, BackupKeep: 7} + +func TestUpdatePersistsAppliesAndOverridesNextStart(t *testing.T) { + dir := t.TempDir() + s, err := Load(dir, defaults) + if err != nil { + t.Fatal(err) + } + var seen []Values + s.Subscribe(func(v Values) { seen = append(seen, v) }) + if len(seen) != 1 || seen[0] != defaults { + t.Fatalf("subscriber must start in sync: %+v", seen) + } + + changes, err := s.Update(map[string]int{"wal_checkpoint_mb": 128}) + if err != nil || len(changes) != 1 || changes[0] != (Change{"wal_checkpoint_mb", 64, 128}) { + t.Fatalf("%+v %v", changes, err) + } + if len(seen) != 2 || seen[1].WALCheckpointMB != 128 || seen[1].LoginMaxAttempts != 5 { + t.Fatalf("subscriber not notified correctly: %+v", seen) + } + if info, _ := os.Stat(filepath.Join(dir, FileName)); info == nil || info.Mode().Perm() != 0o600 { + t.Fatalf("settings file must exist with 0600: %v", info) + } + + // A restart with different flag defaults still sees the saved override. + s2, err := Load(dir, Values{WALCheckpointMB: 32, LoginMaxAttempts: 9, LoginWindowSeconds: 60, BackupKeep: 3}) + if err != nil { + t.Fatal(err) + } + if got := s2.Get(); got.WALCheckpointMB != 128 { + t.Fatalf("override lost across restart: %+v", got) + } +} + +func TestInvalidChangesAreRejectedAndChangeNothing(t *testing.T) { + dir := t.TempDir() + s, _ := Load(dir, defaults) + notified := 0 + s.Subscribe(func(Values) { notified++ }) + notified = 0 + + for name, patch := range map[string]map[string]int{ + "below range": {"login_max_attempts": 0}, + "above range": {"wal_checkpoint_mb": 1 << 30}, + "unknown": {"admin_password": 1}, + "mixed": {"wal_checkpoint_mb": 100, "login_window_seconds": 1}, + "too frequent backups": {"backup_interval_minutes": 2}, + } { + if _, err := s.Update(patch); err == nil { + t.Fatalf("%s: expected an error", name) + } + } + if s.Get() != defaults || notified != 0 { + t.Fatalf("rejected updates must not change or notify: %+v %d", s.Get(), notified) + } + if _, err := os.Stat(filepath.Join(dir, FileName)); !os.IsNotExist(err) { + t.Fatal("rejected updates must not write the file") + } + if ch, err := s.Update(map[string]int{"wal_checkpoint_mb": 64}); err != nil || ch != nil || notified != 0 { + t.Fatal("setting the current value is a no-op") + } +} + +func TestBadFileFallsBackToDefaults(t *testing.T) { + dir := t.TempDir() + _ = os.WriteFile(filepath.Join(dir, FileName), []byte(`{"login_max_attempts": 9999}`), 0o600) + s, err := Load(dir, defaults) + if err == nil || !strings.Contains(err.Error(), "login_max_attempts") { + t.Fatalf("invalid file should be reported: %v", err) + } + if s.Get() != defaults { + t.Fatal("invalid file must not change the running values") + } +} diff --git a/internal/web/auth/token_lastused_test.go b/internal/web/auth/token_lastused_test.go new file mode 100644 index 0000000..2512874 --- /dev/null +++ b/internal/web/auth/token_lastused_test.go @@ -0,0 +1,60 @@ +package auth + +import ( + "testing" + "time" +) + +func TestTokenLastUsedIsMemoryOnly(t *testing.T) { + engine := newMockEngine() + ts := NewTokenStore(engine, nil) + + used, usedTok, _ := ts.Create("used", "viewer", "t") + _, idleTok, _ := ts.Create("idle", "viewer", "t") + + byID := func(id string) APIToken { + for _, tok := range ts.List() { + if tok.ID == id { + return tok + } + } + t.Fatalf("token %s missing from List", id) + return APIToken{} + } + + if byID(usedTok.ID).LastUsedAt != nil { + t.Fatal("a token that never authenticated must have no last-used time") + } + + before := time.Now().Add(-time.Second) + writes := len(engine.data) + if !ts.Validate(used) { + t.Fatal("token should validate") + } + if len(engine.data) != writes { + t.Fatal("authenticating must not write to the engine") + } + + got := byID(usedTok.ID).LastUsedAt + if got == nil || got.Before(before) { + t.Fatalf("last-used not recorded: %v", got) + } + if byID(idleTok.ID).LastUsedAt != nil { + t.Fatal("an unrelated token must stay unused") + } + + // A rejected token must not be recorded. + if ts.Validate("bst_live_not-a-real-token") { + t.Fatal("bogus token validated") + } + + // Revoked tokens drop their tracking. + if err := ts.Delete(usedTok.ID); err != nil { + t.Fatal(err) + } + for _, tok := range ts.List() { + if tok.ID == usedTok.ID { + t.Fatal("revoked token still listed") + } + } +} diff --git a/internal/web/auth/token_store.go b/internal/web/auth/token_store.go index 456a2ce..e6c9977 100644 --- a/internal/web/auth/token_store.go +++ b/internal/web/auth/token_store.go @@ -7,6 +7,7 @@ import ( "encoding/json" "hash/fnv" "sync" + "sync/atomic" "time" ) @@ -23,6 +24,11 @@ type APIToken struct { CreatedBy string `json:"created_by"` // ExpiresAt is optional; nil (or absent in older stored tokens) means no expiry. ExpiresAt *time.Time `json:"expires_at,omitempty"` + // LastUsedAt is filled in by List from in-memory tracking: the last time the + // token authenticated a request since this process started. It is never + // persisted (authenticating must not write to the engine), so it is nil for + // tokens not used since the last restart. + LastUsedAt *time.Time `json:"last_used_at,omitempty"` } // Expired reports whether the token has an expiry that is at or before now. @@ -48,6 +54,7 @@ type TokenStore struct { scanFn func(start, end uint64) ([]TokenRecord, error) mu sync.RWMutex tokenIndex map[string]APIToken // tokenHash -> APIToken + lastUsed sync.Map // tokenHash -> *atomic.Int64 (unix nanoseconds) } // NewTokenStore initializes TokenStore and loads existing tokens. @@ -129,7 +136,13 @@ func (ts *TokenStore) List() []APIToken { defer ts.mu.RUnlock() list := make([]APIToken, 0, len(ts.tokenIndex)) - for _, tok := range ts.tokenIndex { + for h, tok := range ts.tokenIndex { + if v, ok := ts.lastUsed.Load(h); ok { + if ns := v.(*atomic.Int64).Load(); ns > 0 { + t := time.Unix(0, ns).UTC() + tok.LastUsedAt = &t + } + } list = append(list, tok) } return list @@ -143,6 +156,7 @@ func (ts *TokenStore) Delete(id string) error { for h, tok := range ts.tokenIndex { if tok.ID == id { delete(ts.tokenIndex, h) + ts.lastUsed.Delete(h) break } } @@ -163,12 +177,24 @@ func (ts *TokenStore) ValidateRole(raw string) (string, bool) { ts.mu.RLock() tok, exists := ts.tokenIndex[th] ts.mu.RUnlock() - if !exists || tok.Expired(time.Now()) { + now := time.Now() + if !exists || tok.Expired(now) { return "", false } + ts.touch(th, now) return tok.Role, true } +// touch records a successful authentication. Pure memory: one atomic store, and +// an allocation only the first time a token is seen. +func (ts *TokenStore) touch(hash string, now time.Time) { + v, ok := ts.lastUsed.Load(hash) + if !ok { + v, _ = ts.lastUsed.LoadOrStore(hash, new(atomic.Int64)) + } + v.(*atomic.Int64).Store(now.UnixNano()) +} + // Reload repopulates the in-memory token cache from partition 0x05. func (ts *TokenStore) Reload() error { if ts.scanFn == nil { diff --git a/internal/web/handler/api.go b/internal/web/handler/api.go index d8f2fbd..87d7e89 100644 --- a/internal/web/handler/api.go +++ b/internal/web/handler/api.go @@ -7,6 +7,7 @@ import ( "strconv" "github.com/ChromaBeast/beastdb/internal/api" + "github.com/ChromaBeast/beastdb/internal/audit" "github.com/ChromaBeast/beastdb/internal/web/auth" ) @@ -36,6 +37,8 @@ type APIHandler struct { tokens *auth.TokenStore replication ReplicationSource // leader only; nil elsewhere + cluster ClusterControl // epoch/role/promotion; nil disables /api/replication/promote + audit *audit.Log // nil disables auditing } // NewAPIHandler creates an APIHandler with engine, session, and partition registry. @@ -44,6 +47,19 @@ func NewAPIHandler(e EngineReader, s *auth.SessionManager, role, version string, } // SetTokenStore assigns the persistent TokenStore to the APIHandler. +// SetAudit wires the audit log (nil disables auditing). +func (h *APIHandler) SetAudit(l *audit.Log) { h.audit = l } + +// record writes an audit entry attributed to the requesting session. +// Record values and secrets must never be passed in detail. +func (h *APIHandler) record(r *http.Request, action, target, detail string) { + actor := "unknown" + if u, _, err := h.sessions.ValidateSession(r); err == nil { + actor = u + } + _ = h.audit.Record(actor, action, target, detail, ClientIP(r)) +} + func (h *APIHandler) SetTokenStore(ts *auth.TokenStore) { h.tokens = ts } diff --git a/internal/web/handler/audit_api.go b/internal/web/handler/audit_api.go new file mode 100644 index 0000000..8623283 --- /dev/null +++ b/internal/web/handler/audit_api.go @@ -0,0 +1,42 @@ +package handler + +import ( + "net/http" + "strconv" + "strings" + + "github.com/ChromaBeast/beastdb/internal/audit" + "github.com/ChromaBeast/beastdb/internal/web/auth" +) + +// AuditHandler serves GET /api/audit?before=&limit=&action=&actor= (admin only). +// Body: {"entries":[...newest first...],"status":{"entries":N,"verified":bool,"error":""}}. +// Verifying re-reads the whole file, so the status is computed per request; the +// log only grows with admin actions, not with data traffic. +func AuditHandler(sessions *auth.SessionManager, l *audit.Log) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) + return + } + if _, role, err := sessions.ValidateSession(r); err != nil || role != auth.RoleAdmin { + http.Error(w, "Forbidden", http.StatusForbidden) + return + } + qs := r.URL.Query() + q := audit.Query{Action: strings.TrimSpace(qs.Get("action")), Actor: strings.TrimSpace(qs.Get("actor"))} + if v, err := strconv.ParseUint(qs.Get("before"), 10, 64); err == nil { + q.Before = v + } + if v, err := strconv.Atoi(qs.Get("limit")); err == nil { + q.Limit = v + } + entries, err := l.List(q) + if err != nil { + http.Error(w, "Could not read the audit log", http.StatusInternalServerError) + return + } + w.Header().Set("Cache-Control", "no-store") + writeJSON(w, map[string]any{"entries": entries, "status": l.Verify()}) + }) +} diff --git a/internal/web/handler/audit_api_test.go b/internal/web/handler/audit_api_test.go new file mode 100644 index 0000000..d6528d3 --- /dev/null +++ b/internal/web/handler/audit_api_test.go @@ -0,0 +1,108 @@ +package handler + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/ChromaBeast/beastdb/internal/audit" + "github.com/ChromaBeast/beastdb/internal/web/auth" +) + +func adminCookieReq(t *testing.T, sm *auth.SessionManager, method, url, body string) *http.Request { + t.Helper() + rec := httptest.NewRecorder() + sm.IssueSessionCookie(rec, &auth.User{Username: "root", Role: auth.RoleAdmin}) + req := httptest.NewRequest(method, url, strings.NewReader(body)) + for _, c := range rec.Result().Cookies() { + req.AddCookie(c) + } + return req +} + +func TestAuditRecordsAdminActionsWithoutValues(t *testing.T) { + sm := auth.NewSessionManager(make([]byte, 32)) + l, err := audit.Open(t.TempDir()) + if err != nil { + t.Fatal(err) + } + defer l.Close() + h := NewAPIHandler(&precisionEngine{}, sm, "leader", "test", nil) + h.SetAudit(l) + + // A write and a delete by an admin. + w := httptest.NewRecorder() + h.PutKey(w, adminCookieReq(t, sm, http.MethodPost, "/api/key", `{"key":"42","value":"top-secret-payload"}`)) + if w.Code != http.StatusCreated { + t.Fatalf("put: %d", w.Code) + } + w = httptest.NewRecorder() + h.DeleteKey(w, adminCookieReq(t, sm, http.MethodDelete, "/api/key?k=42", "")) + if w.Code != http.StatusNoContent { + t.Fatalf("delete: %d", w.Code) + } + + entries, _ := l.List(audit.Query{}) + if len(entries) != 2 || entries[0].Action != audit.ActionRecordDelete || entries[1].Action != audit.ActionRecordPut { + t.Fatalf("entries: %+v", entries) + } + if entries[1].Actor != "root" || entries[1].Target != "42" { + t.Fatalf("actor/target: %+v", entries[1]) + } + raw, _ := json.Marshal(entries) + if strings.Contains(string(raw), "top-secret-payload") { + t.Fatal("record values must never be written to the audit log") + } + + // A rejected viewer write leaves no entry. + rec := httptest.NewRecorder() + sm.IssueSessionCookie(rec, &auth.User{Username: "v", Role: auth.RoleViewer}) + req := httptest.NewRequest(http.MethodPost, "/api/key", strings.NewReader(`{"key":"7","value":"x"}`)) + req.AddCookie(rec.Result().Cookies()[0]) + h.PutKey(httptest.NewRecorder(), req) + if got, _ := l.List(audit.Query{}); len(got) != 2 { + t.Fatalf("a forbidden request must not be audited as an action: %+v", got) + } +} + +func TestAuditHandlerAuthAndStatus(t *testing.T) { + sm := auth.NewSessionManager(make([]byte, 32)) + l, _ := audit.Open(t.TempDir()) + defer l.Close() + _ = l.Record("root", audit.ActionCheckpoint, "", "LSN 5", "") + _ = l.Record("root", audit.ActionTokenCreate, "tok_1", "ci · role viewer", "") + h := AuditHandler(sm, l) + + for _, req := range []*http.Request{ + httptest.NewRequest(http.MethodGet, "/api/audit", nil), + sessionReq(t, sm, auth.RoleViewer, "/api/audit"), + } { + w := httptest.NewRecorder() + h.ServeHTTP(w, req) + if w.Code != http.StatusForbidden { + t.Fatalf("expected 403, got %d", w.Code) + } + } + + w := httptest.NewRecorder() + h.ServeHTTP(w, sessionReq(t, sm, auth.RoleAdmin, "/api/audit?action=token.create")) + var body struct { + Entries []audit.Entry `json:"entries"` + Status audit.Status `json:"status"` + } + if err := json.Unmarshal(w.Body.Bytes(), &body); err != nil || w.Code != 200 { + t.Fatalf("%d %v", w.Code, err) + } + if len(body.Entries) != 1 || body.Entries[0].Target != "tok_1" || !body.Status.Verified || body.Status.Entries != 2 { + t.Fatalf("%+v", body) + } + + // A nil log (auditing disabled) still answers, with nothing in it. + w = httptest.NewRecorder() + AuditHandler(sm, nil).ServeHTTP(w, sessionReq(t, sm, auth.RoleAdmin, "/api/audit")) + if w.Code != 200 || !strings.Contains(w.Body.String(), `"entries":[]`) { + t.Fatalf("nil log: %d %s", w.Code, w.Body.String()) + } +} diff --git a/internal/web/handler/backups_api.go b/internal/web/handler/backups_api.go new file mode 100644 index 0000000..3c8e398 --- /dev/null +++ b/internal/web/handler/backups_api.go @@ -0,0 +1,130 @@ +package handler + +import ( + "archive/tar" + "errors" + "fmt" + "io" + "net/http" + "os" + "path/filepath" + "strconv" + "time" + + "github.com/ChromaBeast/beastdb/internal/audit" + "github.com/ChromaBeast/beastdb/internal/backup" + "github.com/ChromaBeast/beastdb/internal/web/auth" +) + +// BackupsHandler serves the backup endpoints (admin only): +// +// GET /api/backups list, plus directory and schedule +// POST /api/backups create a manual backup now +// POST /api/backups/verify?id= re-check one backup against its manifest +// GET /api/backups/download?id= stream the bundle as a .tar +// +// Restore is deliberately not an endpoint: it stays an offline `beastctl restore`. +// Creating a backup pauses reads and writes for the time it takes to copy the +// data file, so the response says how long it took. +func BackupsHandler(sessions *auth.SessionManager, mgr *backup.Manager, al *audit.Log) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + actor, role, err := sessions.ValidateSession(r) + if err != nil || role != auth.RoleAdmin { + http.Error(w, "Forbidden", http.StatusForbidden) + return + } + if mgr == nil { + http.Error(w, "Backups unavailable", http.StatusNotFound) + return + } + ip := ClientIP(r) + switch { + case r.URL.Path == "/api/backups" && r.Method == http.MethodGet: + list, err := mgr.List() + if err != nil { + http.Error(w, "Could not read the backup directory", http.StatusInternalServerError) + return + } + every, keep := mgr.Schedule() + w.Header().Set("Cache-Control", "no-store") + writeJSON(w, map[string]any{ + "dir": mgr.Dir(), "intervalMinutes": int(every / time.Minute), "keep": keep, "backups": list, + }) + + case r.URL.Path == "/api/backups" && r.Method == http.MethodPost: + start := time.Now() + info, err := mgr.Create(backup.KindManual, actor) + switch { + case errors.Is(err, backup.ErrBusy): + http.Error(w, err.Error(), http.StatusConflict) + return + case errors.Is(err, backup.ErrNoSpace): + http.Error(w, err.Error(), http.StatusInsufficientStorage) + return + case err != nil: + http.Error(w, "Backup failed: "+err.Error(), http.StatusInternalServerError) + return + } + _ = al.Record(actor, audit.ActionBackupCreate, info.ID, "manual · "+strconv.FormatInt(info.FileSize, 10)+" bytes", ip) + w.WriteHeader(http.StatusCreated) + writeJSON(w, map[string]any{"backup": info, "tookMs": time.Since(start).Milliseconds()}) + + case r.URL.Path == "/api/backups/verify" && r.Method == http.MethodPost: + id := r.URL.Query().Get("id") + res, err := mgr.Verify(id) + if errors.Is(err, backup.ErrNotFound) { + http.Error(w, "Backup not found", http.StatusNotFound) + return + } + detail := "passed" + if !res.OK { + detail = "FAILED: " + res.Error + } + _ = al.Record(actor, audit.ActionBackupVerify, id, detail, ip) + writeJSON(w, res) + + case r.URL.Path == "/api/backups/download" && r.Method == http.MethodGet: + id := r.URL.Query().Get("id") + dir, err := mgr.Path(id) + if err != nil { + http.Error(w, "Backup not found", http.StatusNotFound) + return + } + _ = al.Record(actor, audit.ActionBackupDownload, id, "", ip) + streamBundle(w, dir, id) + + default: + http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) + } + }) +} + +// streamBundle writes data.bin and manifest.json as an uncompressed tar. Headers +// are sent before any data, so a failure mid-stream truncates the archive; the +// client then fails to extract it rather than getting a silently short file. +func streamBundle(w http.ResponseWriter, dir, id string) { + w.Header().Set("Content-Type", "application/x-tar") + w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.tar"`, id)) + tw := tar.NewWriter(w) + for _, name := range []string{"manifest.json", "data.bin"} { + f, err := os.Open(filepath.Join(dir, name)) + if err != nil { + return + } + st, err := f.Stat() + if err != nil { + f.Close() + return + } + if tw.WriteHeader(&tar.Header{Name: id + "/" + name, Mode: 0o640, Size: st.Size(), ModTime: st.ModTime()}) != nil { + f.Close() + return + } + _, err = io.Copy(tw, f) + f.Close() + if err != nil { + return + } + } + _ = tw.Close() +} diff --git a/internal/web/handler/backups_api_test.go b/internal/web/handler/backups_api_test.go new file mode 100644 index 0000000..9ab334a --- /dev/null +++ b/internal/web/handler/backups_api_test.go @@ -0,0 +1,124 @@ +package handler + +import ( + "archive/tar" + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "path/filepath" + "strings" + "testing" + + "github.com/ChromaBeast/beastdb/internal/api" + "github.com/ChromaBeast/beastdb/internal/audit" + "github.com/ChromaBeast/beastdb/internal/backup" + "github.com/ChromaBeast/beastdb/internal/web/auth" +) + +func TestBackupsEndpointsEndToEnd(t *testing.T) { + dir := t.TempDir() + eng, err := api.NewEngine(filepath.Join(dir, "beast.bin"), filepath.Join(dir, "beast.wal"), 32) + if err != nil { + t.Fatal(err) + } + defer eng.Close() + for i := uint64(1); i <= 50; i++ { + _ = eng.Put(i, []byte("v")) + } + sm := auth.NewSessionManager(make([]byte, 32)) + al, _ := audit.Open(dir) + defer al.Close() + mgr := backup.NewManager(eng, filepath.Join(dir, "backups")) + h := BackupsHandler(sm, mgr, al) + + do := func(role auth.Role, method, url string) *httptest.ResponseRecorder { + rec := httptest.NewRecorder() + sm.IssueSessionCookie(rec, &auth.User{Username: "root", Role: role}) + req := httptest.NewRequest(method, url, nil) + req.AddCookie(rec.Result().Cookies()[0]) + w := httptest.NewRecorder() + h.ServeHTTP(w, req) + return w + } + + if w := do(auth.RoleViewer, http.MethodGet, "/api/backups"); w.Code != http.StatusForbidden { + t.Fatalf("viewer: %d", w.Code) + } + anon := httptest.NewRecorder() + h.ServeHTTP(anon, httptest.NewRequest(http.MethodGet, "/api/backups", nil)) + if anon.Code != http.StatusForbidden { + t.Fatalf("anonymous: %d", anon.Code) + } + + w := do(auth.RoleAdmin, http.MethodPost, "/api/backups") + var created struct { + Backup backup.Info `json:"backup"` + } + if w.Code != http.StatusCreated || json.Unmarshal(w.Body.Bytes(), &created) != nil || !backup.ValidID(created.Backup.ID) { + t.Fatalf("create: %d %s", w.Code, w.Body.String()) + } + id := created.Backup.ID + + var list struct { + Backups []backup.Status `json:"backups"` + } + w = do(auth.RoleAdmin, http.MethodGet, "/api/backups") + _ = json.Unmarshal(w.Body.Bytes(), &list) + if len(list.Backups) != 1 || list.Backups[0].ID != id || list.Backups[0].Verified == nil || !list.Backups[0].Verified.OK { + t.Fatalf("a new backup is verified at creation: %+v", list) + } + + if w = do(auth.RoleAdmin, http.MethodPost, "/api/backups/verify?id="+id); w.Code != 200 || !strings.Contains(w.Body.String(), `"ok":true`) { + t.Fatalf("verify: %d %s", w.Code, w.Body.String()) + } + if w = do(auth.RoleAdmin, http.MethodPost, "/api/backups/verify?id=../../etc"); w.Code != http.StatusNotFound { + t.Fatalf("path traversal id must 404, got %d", w.Code) + } + if w = do(auth.RoleAdmin, http.MethodGet, "/api/backups/download?id=bk-20200101-000000"); w.Code != http.StatusNotFound { + t.Fatalf("unknown id: %d", w.Code) + } + + w = do(auth.RoleAdmin, http.MethodGet, "/api/backups/download?id="+id) + if w.Code != 200 || w.Header().Get("Content-Type") != "application/x-tar" { + t.Fatalf("download: %d %v", w.Code, w.Header()) + } + tr := tar.NewReader(w.Body) + names := map[string]int64{} + for { + hd, err := tr.Next() + if err == io.EOF { + break + } + if err != nil { + t.Fatal(err) + } + n, _ := io.Copy(io.Discard, tr) + names[hd.Name] = n + } + if names[id+"/manifest.json"] == 0 || names[id+"/data.bin"] != created.Backup.FileSize { + t.Fatalf("tar contents: %v (want data.bin %d bytes)", names, created.Backup.FileSize) + } + + actions := map[string]bool{} + entries, _ := al.List(audit.Query{}) + for _, e := range entries { + actions[e.Action] = true + } + for _, a := range []string{audit.ActionBackupCreate, audit.ActionBackupVerify, audit.ActionBackupDownload} { + if !actions[a] { + t.Fatalf("missing audit entry %s: %+v", a, entries) + } + } + + var nilMgr = BackupsHandler(sm, nil, al) + rec := httptest.NewRecorder() + sm.IssueSessionCookie(rec, &auth.User{Username: "root", Role: auth.RoleAdmin}) + req := httptest.NewRequest(http.MethodGet, "/api/backups", nil) + req.AddCookie(rec.Result().Cookies()[0]) + nw := httptest.NewRecorder() + nilMgr.ServeHTTP(nw, req) + if nw.Code != http.StatusNotFound { + t.Fatalf("nil manager: %d", nw.Code) + } +} diff --git a/internal/web/handler/key_api.go b/internal/web/handler/key_api.go index 2f0f477..14525b1 100644 --- a/internal/web/handler/key_api.go +++ b/internal/web/handler/key_api.go @@ -7,6 +7,7 @@ import ( "strconv" "github.com/ChromaBeast/beastdb/internal/api" + "github.com/ChromaBeast/beastdb/internal/audit" "github.com/ChromaBeast/beastdb/internal/web/auth" ) @@ -90,6 +91,11 @@ func (h *APIHandler) PutKey(w http.ResponseWriter, r *http.Request) { http.Error(w, "Engine error", http.StatusInternalServerError) return } + detail := "overwrite" + if body.CreateOnly { + detail = "create" + } + h.record(r, audit.ActionRecordPut, strconv.FormatUint(key, 10), detail+" · "+strconv.Itoa(len(body.Value))+" bytes") w.WriteHeader(http.StatusCreated) } @@ -116,6 +122,7 @@ func (h *APIHandler) DeleteKey(w http.ResponseWriter, r *http.Request) { http.Error(w, "Engine error", http.StatusInternalServerError) return } + h.record(r, audit.ActionRecordDelete, strconv.FormatUint(key, 10), "") w.WriteHeader(http.StatusNoContent) } diff --git a/internal/web/handler/login.go b/internal/web/handler/login.go index eabeaaa..b5896e1 100644 --- a/internal/web/handler/login.go +++ b/internal/web/handler/login.go @@ -5,6 +5,7 @@ import ( "io/fs" "net/http" + "github.com/ChromaBeast/beastdb/internal/audit" "github.com/ChromaBeast/beastdb/internal/web/auth" ) @@ -41,6 +42,9 @@ func LogoutHandler(d *Deps) http.HandlerFunc { http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) return } + if user, _, err := d.Sessions.ValidateSession(r); err == nil { + _ = d.Audit.Record(user, audit.ActionLogout, "", "", ClientIP(r)) + } auth.ClearSessionCookie(w) http.Redirect(w, r, "/login", http.StatusSeeOther) } @@ -81,11 +85,13 @@ func handleLoginPost(w http.ResponseWriter, r *http.Request, d *Deps) { if d.RateLimiter != nil { d.RateLimiter.RecordFailure(ip) } + _ = d.Audit.Record(req.Username, audit.ActionLoginFailed, "", "", ip) // Return 401 without revealing whether the user exists http.Error(w, "Invalid credentials", http.StatusUnauthorized) return } + _ = d.Audit.Record(user.Username, audit.ActionLoginOK, "", "role "+string(user.Role), ip) d.Sessions.IssueSessionCookieForRequest(w, r, user) http.Redirect(w, r, "/", http.StatusSeeOther) } diff --git a/internal/web/handler/logs_api.go b/internal/web/handler/logs_api.go index f1f7bf2..276496d 100644 --- a/internal/web/handler/logs_api.go +++ b/internal/web/handler/logs_api.go @@ -9,8 +9,8 @@ import ( "github.com/ChromaBeast/beastdb/internal/web/auth" ) -// LogsHandler serves GET /api/logs?since=&limit=&level=&q= (admin only). -// The response is {"entries":[{seq,time,level,message}],"latest":}. +// LogsHandler serves GET /api/logs?since=&limit=&level=&source=&q= (admin only). +// The response is {"entries":[{seq,time,level,source,message}],"latest":}. func LogsHandler(sessions *auth.SessionManager, buf *logbuf.Buffer) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { @@ -41,6 +41,17 @@ func LogsHandler(sessions *auth.SessionManager, buf *logbuf.Buffer) http.Handler http.Error(w, "invalid level", http.StatusBadRequest) return } + if src := strings.ToLower(qs.Get("source")); src != "" { + valid := false + for _, k := range logbuf.Sources { + valid = valid || k == src + } + if !valid { + http.Error(w, "invalid source", http.StatusBadRequest) + return + } + q.Source = src + } res := buf.Snapshot(q) if res.Entries == nil { res.Entries = []logbuf.Entry{} diff --git a/internal/web/handler/metrics_history.go b/internal/web/handler/metrics_history.go new file mode 100644 index 0000000..8a430d4 --- /dev/null +++ b/internal/web/handler/metrics_history.go @@ -0,0 +1,55 @@ +package handler + +import ( + "net/http" + "strconv" + "time" + + "github.com/ChromaBeast/beastdb/internal/metrics" + "github.com/ChromaBeast/beastdb/internal/web/auth" +) + +const ( + defaultHistoryWindow = time.Hour + maxHistoryWindow = 24 * time.Hour + maxHistoryPoints = 360 +) + +// MetricsHistoryHandler serves GET /api/metrics/history?window= (admin only). +// The history lives in memory only and starts empty after a restart; the +// response says how far back it can actually reach. +// Body: {"intervalSeconds":N,"windowSeconds":N,"points":[metrics.Point...]}. +func MetricsHistoryHandler(sessions *auth.SessionManager, hist *metrics.History, interval time.Duration) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) + return + } + if _, role, err := sessions.ValidateSession(r); err != nil || role != auth.RoleAdmin { + http.Error(w, "Forbidden", http.StatusForbidden) + return + } + window := defaultHistoryWindow + if v := r.URL.Query().Get("window"); v != "" { + secs, err := strconv.Atoi(v) + if err != nil || secs <= 0 { + http.Error(w, "invalid window", http.StatusBadRequest) + return + } + window = time.Duration(secs) * time.Second + if window > maxHistoryWindow { + window = maxHistoryWindow + } + } + points := []metrics.Point{} + if hist != nil { + points = hist.Window(time.Now(), window, maxHistoryPoints) + } + w.Header().Set("Cache-Control", "no-store") + writeJSON(w, map[string]any{ + "intervalSeconds": int(interval / time.Second), + "windowSeconds": int(window / time.Second), + "points": points, + }) + }) +} diff --git a/internal/web/handler/metrics_history_test.go b/internal/web/handler/metrics_history_test.go new file mode 100644 index 0000000..1cb503e --- /dev/null +++ b/internal/web/handler/metrics_history_test.go @@ -0,0 +1,61 @@ +package handler + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/ChromaBeast/beastdb/internal/metrics" + "github.com/ChromaBeast/beastdb/internal/web/auth" +) + +func TestMetricsHistoryHandler(t *testing.T) { + sm := auth.NewSessionManager(make([]byte, 32)) + hist := metrics.NewHistory(10) + now := time.Now() + hist.Add(metrics.Point{Time: now.Add(-2 * time.Hour).Unix(), LSN: 1}) + hist.Add(metrics.Point{Time: now.Add(-time.Minute).Unix(), LSN: 2}) + h := MetricsHistoryHandler(sm, hist, 10*time.Second) + + for _, req := range []*http.Request{ + httptest.NewRequest(http.MethodGet, "/api/metrics/history", nil), + sessionReq(t, sm, auth.RoleViewer, "/api/metrics/history"), + } { + w := httptest.NewRecorder() + h.ServeHTTP(w, req) + if w.Code != http.StatusForbidden { + t.Fatalf("expected 403, got %d", w.Code) + } + } + + type resp struct { + IntervalSeconds int `json:"intervalSeconds"` + WindowSeconds int `json:"windowSeconds"` + Points []metrics.Point `json:"points"` + } + get := func(url string) (int, resp) { + w := httptest.NewRecorder() + h.ServeHTTP(w, sessionReq(t, sm, auth.RoleAdmin, url)) + var r resp + _ = json.Unmarshal(w.Body.Bytes(), &r) + return w.Code, r + } + + code, r := get("/api/metrics/history") // default window: 1 hour + if code != 200 || len(r.Points) != 1 || r.Points[0].LSN != 2 || r.WindowSeconds != 3600 || r.IntervalSeconds != 10 { + t.Fatalf("default window: %d %+v", code, r) + } + if _, r = get("/api/metrics/history?window=86400"); len(r.Points) != 2 { + t.Fatalf("24h window should include both points: %+v", r) + } + if _, r = get("/api/metrics/history?window=999999999"); r.WindowSeconds != 86400 { + t.Fatalf("window must be capped at 24h: %d", r.WindowSeconds) + } + for _, bad := range []string{"abc", "0", "-5"} { + if code, _ := get("/api/metrics/history?window=" + bad); code != http.StatusBadRequest { + t.Fatalf("window=%s: got %d, want 400", bad, code) + } + } +} diff --git a/internal/web/handler/middleware.go b/internal/web/handler/middleware.go index ca4bf62..916ab21 100644 --- a/internal/web/handler/middleware.go +++ b/internal/web/handler/middleware.go @@ -4,6 +4,7 @@ import ( "io/fs" "net/http" + "github.com/ChromaBeast/beastdb/internal/audit" "github.com/ChromaBeast/beastdb/internal/web/auth" ) @@ -13,6 +14,7 @@ type Deps struct { Sessions *auth.SessionManager StaticFS fs.FS RateLimiter *LoginRateLimiter + Audit *audit.Log // nil disables auditing } // AuthMiddleware rejects unauthenticated requests to protected routes. diff --git a/internal/web/handler/ops.go b/internal/web/handler/ops.go index 02481a0..5d35654 100644 --- a/internal/web/handler/ops.go +++ b/internal/web/handler/ops.go @@ -1,8 +1,11 @@ package handler import ( + "context" "net/http" + "strconv" + "github.com/ChromaBeast/beastdb/internal/audit" "github.com/ChromaBeast/beastdb/internal/replication" ) @@ -17,6 +20,24 @@ type ReplicationSource interface { Replicas() []replication.ReplicaInfo } +// ClusterControl is the node's epoch, role and promotion control (replication.Cluster). +type ClusterControl interface { + Status() replication.Status + Promote(ctx context.Context, opts replication.PromoteOptions) (replication.PromoteResult, error) +} + +// currentRole is the live role when epochs are tracked (it changes on +// promotion), else the role the process started with. +func (h *APIHandler) currentRole() string { + if h.cluster != nil { + return string(h.cluster.Status().Role) + } + return h.role +} + +// SetCluster wires epoch-aware role reporting and promotion (nil disables both). +func (h *APIHandler) SetCluster(c ClusterControl) { h.cluster = c } + // ReplicaStatus is one replica row of the /api/replication response. type ReplicaStatus struct { replication.ReplicaInfo @@ -31,6 +52,9 @@ type ReplicationStatus struct { // Tracked is true only on a leader, the node that sees its replicas. Tracked bool `json:"tracked"` Replicas []ReplicaStatus `json:"replicas"` + // Cluster is this node's epoch and promotion state; absent when the node + // does not track epochs. + Cluster *replication.Status `json:"cluster,omitempty"` } // SetReplicationSource wires the leader's replica tracker (nil on followers). @@ -55,6 +79,7 @@ func (h *APIHandler) PostCheckpoint(w http.ResponseWriter, r *http.Request) { return } m := h.engine.StorageMetrics() + h.record(r, audit.ActionCheckpoint, "", "LSN "+strconv.FormatUint(m.CurrentLSN, 10)) writeJSON(w, map[string]any{ "ok": true, "lsn": m.CurrentLSN, @@ -73,7 +98,15 @@ func (h *APIHandler) GetReplication(w http.ResponseWriter, r *http.Request) { } lsn := h.engine.CurrentLSN() resp := ReplicationStatus{Role: h.role, LSN: lsn, Replicas: []ReplicaStatus{}} - if h.replication != nil { + tracked := h.replication != nil + if h.cluster != nil { + st := h.cluster.Status() + resp.Cluster = &st + resp.Role = string(st.Role) + // Only a leader (or a fenced former leader) sees replicas. + tracked = tracked && st.Role != replication.RoleFollower + } + if tracked { resp.Tracked = true for _, rep := range h.replication.Replicas() { var lag uint64 diff --git a/internal/web/handler/promote_api.go b/internal/web/handler/promote_api.go new file mode 100644 index 0000000..567c918 --- /dev/null +++ b/internal/web/handler/promote_api.go @@ -0,0 +1,70 @@ +package handler + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + + "github.com/ChromaBeast/beastdb/internal/audit" + "github.com/ChromaBeast/beastdb/internal/replication" +) + +type promoteRequest struct { + // AcceptDataLoss permits promotion when the old leader cannot be reached + // to fence it. Without it an unreachable leader blocks promotion. + AcceptDataLoss bool `json:"acceptDataLoss"` +} + +// PostPromote handles POST /api/replication/promote (admin-only via middleware). +// It promotes this follower to leader at the next epoch. Every attempt, refused +// or not, is written to the audit log. +func (h *APIHandler) PostPromote(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) + return + } + if h.cluster == nil { + http.Error(w, "promotion is not available on this node", http.StatusNotImplemented) + return + } + var req promoteRequest + if r.ContentLength != 0 { + if err := json.NewDecoder(http.MaxBytesReader(w, r.Body, 1<<10)).Decode(&req); err != nil { + http.Error(w, "invalid JSON body", http.StatusBadRequest) + return + } + } + node := h.cluster.Status().NodeID + res, err := h.cluster.Promote(r.Context(), replication.PromoteOptions{AcceptDataLoss: req.AcceptDataLoss}) + if err != nil { + h.record(r, audit.ActionPromote, node, fmt.Sprintf("refused: %v (acceptDataLoss=%t)", err, req.AcceptDataLoss)) + writePromoteError(w, err) + return + } + if res.OldLeaderFenced { + h.record(r, audit.ActionPromote, node, fmt.Sprintf("promoted to leader at epoch %d; old leader fenced at LSN %d, applied %d", res.Epoch, res.FinalLSN, res.AppliedLSN)) + } else { + h.record(r, audit.ActionPromote, node, fmt.Sprintf("promoted to leader at epoch %d WITHOUT fencing the old leader (acceptDataLoss); data loss possible, old leader LSN known %d, applied %d", res.Epoch, res.LeaderLSNKnown, res.AppliedLSN)) + } + writeJSON(w, map[string]any{"ok": true, "result": res, "cluster": h.cluster.Status()}) +} + +func writePromoteError(w http.ResponseWriter, err error) { + code, status := "failed", http.StatusInternalServerError + switch { + case errors.Is(err, replication.ErrNotFollower): + code, status = "not_follower", http.StatusConflict + case errors.Is(err, replication.ErrPromoteInProgress): + code, status = "in_progress", http.StatusConflict + case errors.Is(err, replication.ErrLeaderUnreachable): + code, status = "leader_unreachable", http.StatusConflict + case errors.Is(err, replication.ErrCatchUpTimeout): + code, status = "catch_up_timeout", http.StatusConflict + case errors.Is(err, replication.ErrFenceRefused): + code, status = "fence_refused", http.StatusConflict + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(status) + _ = json.NewEncoder(w).Encode(map[string]any{"ok": false, "code": code, "error": err.Error()}) +} diff --git a/internal/web/handler/ratelimit.go b/internal/web/handler/ratelimit.go index f6e51df..8fd9cfd 100644 --- a/internal/web/handler/ratelimit.go +++ b/internal/web/handler/ratelimit.go @@ -27,6 +27,19 @@ func NewLoginRateLimiter(maxAttempts int, window time.Duration) *LoginRateLimite return rl } +// Configure changes the limit at runtime. Existing attempts are kept and are +// judged against the new window on the next check. +func (rl *LoginRateLimiter) Configure(maxAttempts int, window time.Duration) { + rl.mu.Lock() + defer rl.mu.Unlock() + if maxAttempts > 0 { + rl.max = maxAttempts + } + if window > 0 { + rl.window = window + } +} + // Allow checks if the given IP is allowed to attempt a login. func (rl *LoginRateLimiter) Allow(ip string) bool { rl.mu.Lock() diff --git a/internal/web/handler/runtime_settings_api.go b/internal/web/handler/runtime_settings_api.go new file mode 100644 index 0000000..e120fa6 --- /dev/null +++ b/internal/web/handler/runtime_settings_api.go @@ -0,0 +1,62 @@ +package handler + +import ( + "encoding/json" + "fmt" + "net/http" + "sort" + "strings" + + "github.com/ChromaBeast/beastdb/internal/audit" + "github.com/ChromaBeast/beastdb/internal/runtimecfg" + "github.com/ChromaBeast/beastdb/internal/web/auth" +) + +// RuntimeSettingsHandler serves /api/settings/runtime (admin only). +// +// GET -> {"values":{...},"ranges":{name:{min,max}}} +// PATCH -> body {"wal_checkpoint_mb":128}; applies and persists the listed +// settings, records one audit entry, returns the same shape as GET. +// +// Only the settings in runtimecfg may change here; anything else is a 400. +func RuntimeSettingsHandler(sessions *auth.SessionManager, store *runtimecfg.Store, al *audit.Log) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + actor, role, err := sessions.ValidateSession(r) + if err != nil || role != auth.RoleAdmin { + http.Error(w, "Forbidden", http.StatusForbidden) + return + } + if store == nil { + http.Error(w, "Runtime settings unavailable", http.StatusNotFound) + return + } + switch r.Method { + case http.MethodGet: + case http.MethodPatch: + r.Body = http.MaxBytesReader(w, r.Body, 4<<10) + var patch map[string]int + if err := json.NewDecoder(r.Body).Decode(&patch); err != nil || len(patch) == 0 { + http.Error(w, "Body must be a JSON object of setting names to whole numbers", http.StatusBadRequest) + return + } + changes, err := store.Update(patch) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + if len(changes) > 0 { + sort.Slice(changes, func(i, j int) bool { return changes[i].Name < changes[j].Name }) + parts := make([]string, len(changes)) + for i, c := range changes { + parts[i] = fmt.Sprintf("%s %d → %d", c.Name, c.From, c.To) + } + _ = al.Record(actor, audit.ActionSettingChange, "runtime", strings.Join(parts, "; "), ClientIP(r)) + } + default: + http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) + return + } + w.Header().Set("Cache-Control", "no-store") + writeJSON(w, map[string]any{"values": store.Get(), "ranges": runtimecfg.Ranges}) + }) +} diff --git a/internal/web/handler/runtime_settings_api_test.go b/internal/web/handler/runtime_settings_api_test.go new file mode 100644 index 0000000..ebe7690 --- /dev/null +++ b/internal/web/handler/runtime_settings_api_test.go @@ -0,0 +1,82 @@ +package handler + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/ChromaBeast/beastdb/internal/audit" + "github.com/ChromaBeast/beastdb/internal/runtimecfg" + "github.com/ChromaBeast/beastdb/internal/web/auth" +) + +func TestRuntimeSettingsHandler(t *testing.T) { + sm := auth.NewSessionManager(make([]byte, 32)) + dir := t.TempDir() + store, _ := runtimecfg.Load(dir, runtimecfg.Values{WALCheckpointMB: 64, LoginMaxAttempts: 5, LoginWindowSeconds: 30, BackupKeep: 7}) + var applied runtimecfg.Values + store.Subscribe(func(v runtimecfg.Values) { applied = v }) + al, _ := audit.Open(dir) + defer al.Close() + h := RuntimeSettingsHandler(sm, store, al) + + do := func(role auth.Role, method, body string) *httptest.ResponseRecorder { + rec := httptest.NewRecorder() + sm.IssueSessionCookie(rec, &auth.User{Username: "root", Role: role}) + req := httptest.NewRequest(method, "/api/settings/runtime", strings.NewReader(body)) + req.AddCookie(rec.Result().Cookies()[0]) + w := httptest.NewRecorder() + h.ServeHTTP(w, req) + return w + } + + if w := do(auth.RoleViewer, http.MethodGet, ""); w.Code != http.StatusForbidden { + t.Fatalf("viewer must be refused: %d", w.Code) + } + if w := httptest.NewRecorder(); true { + h.ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/api/settings/runtime", nil)) + if w.Code != http.StatusForbidden { + t.Fatalf("anonymous must be refused: %d", w.Code) + } + } + + w := do(auth.RoleAdmin, http.MethodPatch, `{"wal_checkpoint_mb":256,"login_max_attempts":3}`) + if w.Code != 200 { + t.Fatalf("patch: %d %s", w.Code, w.Body.String()) + } + var resp struct { + Values runtimecfg.Values `json:"values"` + Ranges map[string]runtimecfg.Range `json:"ranges"` + } + _ = json.Unmarshal(w.Body.Bytes(), &resp) + if resp.Values.WALCheckpointMB != 256 || resp.Values.LoginMaxAttempts != 3 || resp.Ranges["wal_checkpoint_mb"].Max == 0 { + t.Fatalf("%+v", resp) + } + if applied.WALCheckpointMB != 256 || applied.LoginMaxAttempts != 3 { + t.Fatalf("change must reach subscribers: %+v", applied) + } + + entries, _ := al.List(audit.Query{Action: audit.ActionSettingChange}) + if len(entries) != 1 || !strings.Contains(entries[0].Detail, "wal_checkpoint_mb 64 → 256") || !strings.Contains(entries[0].Detail, "login_max_attempts 5 → 3") { + t.Fatalf("audit: %+v", entries) + } + + for name, body := range map[string]string{ + "out of range": `{"login_window_seconds":1}`, + "unknown": `{"admin_password":5}`, + "not a number": `{"wal_checkpoint_mb":"big"}`, + "empty": `{}`, + } { + if w := do(auth.RoleAdmin, http.MethodPatch, body); w.Code != http.StatusBadRequest { + t.Fatalf("%s: got %d, want 400", name, w.Code) + } + } + if got, _ := al.List(audit.Query{Action: audit.ActionSettingChange}); len(got) != 1 { + t.Fatal("rejected changes must not be audited as changes") + } + if store.Get().WALCheckpointMB != 256 { + t.Fatal("rejected changes must not alter the settings") + } +} diff --git a/internal/web/handler/telemetry_api.go b/internal/web/handler/telemetry_api.go index b3b240f..2fd2d40 100644 --- a/internal/web/handler/telemetry_api.go +++ b/internal/web/handler/telemetry_api.go @@ -16,7 +16,7 @@ func (h *APIHandler) GetStats(w http.ResponseWriter, r *http.Request) { resp := map[string]any{ "lsn": h.engine.CurrentLSN(), - "role": h.role, + "role": h.currentRole(), "mode": "B+ Tree (4KB)", "version": h.version, "storage": h.engine.StorageMetrics(), diff --git a/internal/web/handler/token_api.go b/internal/web/handler/token_api.go index 47ddaf6..8f54b0a 100644 --- a/internal/web/handler/token_api.go +++ b/internal/web/handler/token_api.go @@ -7,6 +7,7 @@ import ( "time" "github.com/ChromaBeast/beastdb/internal/api" + "github.com/ChromaBeast/beastdb/internal/audit" "github.com/ChromaBeast/beastdb/internal/web/auth" ) @@ -93,6 +94,12 @@ func (h *APIHandler) CreateToken(w http.ResponseWriter, r *http.Request) { return } + exp := "never expires" + if expiresAt != nil { + exp = "expires " + expiresAt.UTC().Format(time.RFC3339) + } + h.record(r, audit.ActionTokenCreate, meta.ID, name+" · role "+tokRole+" · "+exp) + w.WriteHeader(http.StatusCreated) writeJSON(w, map[string]any{ "token": rawToken, @@ -129,5 +136,6 @@ func (h *APIHandler) DeleteToken(w http.ResponseWriter, r *http.Request) { return } + h.record(r, audit.ActionTokenRevoke, id, "") w.WriteHeader(http.StatusNoContent) } diff --git a/internal/web/handler/user_delete_api.go b/internal/web/handler/user_delete_api.go index 443b446..2e8ce74 100644 --- a/internal/web/handler/user_delete_api.go +++ b/internal/web/handler/user_delete_api.go @@ -4,6 +4,7 @@ import ( "net/http" "strconv" + "github.com/ChromaBeast/beastdb/internal/audit" "github.com/ChromaBeast/beastdb/internal/web/auth" ) @@ -57,6 +58,7 @@ func (h *APIHandler) DeleteUser(w http.ResponseWriter, r *http.Request) { deletedKeys = []uint64{} } + h.record(r, audit.ActionUserDelete, strconv.FormatUint(uint64(targetHash), 10), strconv.Itoa(len(deletedKeys))+" records removed") writeJSON(w, map[string]any{ "deleted": len(deletedKeys), "keys": deletedKeys, diff --git a/internal/web/ops_test.go b/internal/web/ops_test.go index b5f3aca..02f5d2a 100644 --- a/internal/web/ops_test.go +++ b/internal/web/ops_test.go @@ -19,6 +19,15 @@ type fakeReplicas struct{ list []replication.ReplicaInfo } func (f fakeReplicas) Replicas() []replication.ReplicaInfo { return f.list } func opsServer(t *testing.T, src fakeReplicas, withSource bool) (*Server, *api.Engine, []*http.Cookie, []*http.Cookie) { + t.Helper() + opts := Options{} + if withSource { + opts.Replication = src + } + return opsServerWith(t, opts) +} + +func opsServerWith(t *testing.T, opts Options) (*Server, *api.Engine, []*http.Cookie, []*http.Cookie) { t.Helper() dir := t.TempDir() engine, err := api.NewEngine(filepath.Join(dir, "db.bin"), filepath.Join(dir, "db.wal"), 32) @@ -33,10 +42,6 @@ func opsServer(t *testing.T, src fakeReplicas, withSource bool) (*Server, *api.E if err := users.CreateUser("viewer", "viewer-pass", auth.RoleViewer); err != nil { t.Fatal(err) } - opts := Options{} - if withSource { - opts.Replication = src - } srv, err := NewServerWithOptions("127.0.0.1:0", engine, make([]byte, 32), "leader", "test", nil, opts) if err != nil { t.Fatal(err) diff --git a/internal/web/promote_test.go b/internal/web/promote_test.go new file mode 100644 index 0000000..f010262 --- /dev/null +++ b/internal/web/promote_test.go @@ -0,0 +1,174 @@ +package web + +import ( + "context" + "encoding/json" + "errors" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/ChromaBeast/beastdb/internal/audit" + "github.com/ChromaBeast/beastdb/internal/replication" +) + +type fakeCluster struct { + status replication.Status + res replication.PromoteResult + err error + calls []replication.PromoteOptions +} + +func (f *fakeCluster) Status() replication.Status { return f.status } +func (f *fakeCluster) Promote(_ context.Context, o replication.PromoteOptions) (replication.PromoteResult, error) { + f.calls = append(f.calls, o) + return f.res, f.err +} + +func postJSON(srv *Server, path, body string, cookies []*http.Cookie) *httptest.ResponseRecorder { + rec := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodPost, path, strings.NewReader(body)) + req.Header.Set("Content-Type", "application/json") + for _, c := range cookies { + req.AddCookie(c) + } + srv.httpServer.Handler.ServeHTTP(rec, req) + return rec +} + +func promoteServer(t *testing.T, fc *fakeCluster) (*Server, *audit.Log, []*http.Cookie, []*http.Cookie) { + t.Helper() + log, err := audit.Open(t.TempDir()) + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { _ = log.Close() }) + srv, _, admin, viewer := opsServerWith(t, Options{Cluster: fc, Audit: log}) + return srv, log, admin, viewer +} + +func promoteEntries(t *testing.T, log *audit.Log) []audit.Entry { + t.Helper() + es, err := log.List(audit.Query{Action: audit.ActionPromote}) + if err != nil { + t.Fatal(err) + } + return es +} + +func TestPromoteEndpointIsAdminOnly(t *testing.T) { + fc := &fakeCluster{} + srv, log, admin, viewer := promoteServer(t, fc) + if rec := postJSON(srv, "/api/replication/promote", "{}", nil); rec.Code != http.StatusForbidden { + t.Fatalf("anonymous: want 403, got %d", rec.Code) + } + if rec := postJSON(srv, "/api/replication/promote", "{}", viewer); rec.Code != http.StatusForbidden { + t.Fatalf("viewer: want 403, got %d", rec.Code) + } + if rec := do(srv, http.MethodGet, "/api/replication/promote", admin); rec.Code != http.StatusMethodNotAllowed { + t.Fatalf("GET: want 405, got %d", rec.Code) + } + if len(fc.calls) != 0 || len(promoteEntries(t, log)) != 0 { + t.Fatal("a rejected request must not reach Promote or the audit log") + } +} + +func TestPromoteEndpointAuditsSuccess(t *testing.T) { + fc := &fakeCluster{ + status: replication.Status{NodeID: "r1", Role: replication.RoleLeader, Epoch: 2}, + res: replication.PromoteResult{Epoch: 2, OldLeaderFenced: true, FinalLSN: 40, AppliedLSN: 40}, + } + srv, log, admin, _ := promoteServer(t, fc) + rec := postJSON(srv, "/api/replication/promote", "", admin) + if rec.Code != http.StatusOK { + t.Fatalf("%d %s", rec.Code, rec.Body.String()) + } + var body struct { + OK bool `json:"ok"` + Result replication.PromoteResult `json:"result"` + } + if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil || !body.OK || body.Result.Epoch != 2 { + t.Fatalf("body %s (%v)", rec.Body.String(), err) + } + es := promoteEntries(t, log) + if len(es) != 1 || es[0].Actor != "root" || es[0].Target != "r1" || + !strings.Contains(es[0].Detail, "epoch 2") || !strings.Contains(es[0].Detail, "fenced at LSN 40") { + t.Fatalf("audit entries: %+v", es) + } + if fc.calls[0].AcceptDataLoss { + t.Fatal("AcceptDataLoss must default to false") + } +} + +func TestPromoteEndpointAuditsUnfencedPromotionLoudly(t *testing.T) { + fc := &fakeCluster{ + status: replication.Status{NodeID: "r1"}, + res: replication.PromoteResult{Epoch: 2, DataLossPossible: true, LeaderLSNKnown: 90, AppliedLSN: 88}, + } + srv, log, admin, _ := promoteServer(t, fc) + if rec := postJSON(srv, "/api/replication/promote", `{"acceptDataLoss":true}`, admin); rec.Code != http.StatusOK { + t.Fatalf("%d %s", rec.Code, rec.Body.String()) + } + if !fc.calls[0].AcceptDataLoss { + t.Fatal("acceptDataLoss was not passed through") + } + es := promoteEntries(t, log) + if len(es) != 1 || !strings.Contains(es[0].Detail, "WITHOUT fencing") || !strings.Contains(es[0].Detail, "data loss possible") { + t.Fatalf("audit entries: %+v", es) + } +} + +func TestPromoteEndpointReportsAndAuditsRefusal(t *testing.T) { + cases := []struct { + err error + code string + }{ + {replication.ErrLeaderUnreachable, "leader_unreachable"}, + {replication.ErrNotFollower, "not_follower"}, + {replication.ErrCatchUpTimeout, "catch_up_timeout"}, + {replication.ErrFenceRefused, "fence_refused"}, + {errors.New("disk full"), "failed"}, + } + for _, tc := range cases { + fc := &fakeCluster{status: replication.Status{NodeID: "r1"}, err: tc.err} + srv, log, admin, _ := promoteServer(t, fc) + rec := postJSON(srv, "/api/replication/promote", "{}", admin) + wantStatus := http.StatusConflict + if tc.code == "failed" { + wantStatus = http.StatusInternalServerError + } + if rec.Code != wantStatus || !strings.Contains(rec.Body.String(), `"code":"`+tc.code+`"`) { + t.Fatalf("%v: %d %s", tc.err, rec.Code, rec.Body.String()) + } + es := promoteEntries(t, log) + if len(es) != 1 || !strings.HasPrefix(es[0].Detail, "refused:") { + t.Fatalf("%v: refusal not audited: %+v", tc.err, es) + } + } +} + +func TestPromoteEndpointWithoutClusterIsNotImplemented(t *testing.T) { + srv, _, admin, _ := opsServer(t, fakeReplicas{}, false) + if rec := postJSON(srv, "/api/replication/promote", "{}", admin); rec.Code != http.StatusNotImplemented { + t.Fatalf("want 501, got %d", rec.Code) + } +} + +func TestReplicationStatusIncludesClusterAndHidesReplicasOnFollower(t *testing.T) { + fc := &fakeCluster{status: replication.Status{NodeID: "r1", Role: replication.RoleFollower, Epoch: 3, Promotable: false, PromoteBlockedReason: "Behind the leader by 4 records"}} + src := fakeReplicas{list: []replication.ReplicaInfo{{ID: "x"}}} + srv, _, admin, _ := opsServerWith(t, Options{Cluster: fc, Replication: src}) + rec := do(srv, http.MethodGet, "/api/replication", admin) + var body struct { + Role string `json:"role"` + Tracked bool `json:"tracked"` + Cluster *replication.Status `json:"cluster"` + } + if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil { + t.Fatal(err) + } + if body.Role != "follower" || body.Tracked || body.Cluster == nil || body.Cluster.Epoch != 3 || body.Cluster.PromoteBlockedReason == "" { + t.Fatalf("unexpected body: %s", rec.Body.String()) + } +} diff --git a/internal/web/sampler.go b/internal/web/sampler.go new file mode 100644 index 0000000..5b86f27 --- /dev/null +++ b/internal/web/sampler.go @@ -0,0 +1,60 @@ +package web + +import ( + "time" + + "github.com/ChromaBeast/beastdb/internal/metrics" + "github.com/ChromaBeast/beastdb/internal/web/handler" +) + +const ( + // historyInterval is how often the server samples its own gauges. + historyInterval = 10 * time.Second + // historyCapacity keeps 24 hours at historyInterval. + historyCapacity = int(24 * time.Hour / historyInterval) +) + +// startSampler records a metrics.Point every interval until the returned stop +// function is called. The request rate comes from the shared request counter. +func startSampler(engine EngineBackend, repl handler.ReplicationSource, hist *metrics.History, interval time.Duration) (stop func()) { + done := make(chan struct{}) + go func() { + t := time.NewTicker(interval) + defer t.Stop() + last := metrics.Default.Total() + lastAt := time.Now() + for { + select { + case <-done: + return + case now := <-t.C: + m := engine.StorageMetrics() + total := metrics.Default.Total() + rate := 0.0 + if dt := now.Sub(lastAt).Seconds(); dt > 0 && total >= last { + rate = float64(total-last) / dt + } + last, lastAt = total, now + + var lag uint64 + if repl != nil { + for _, rep := range repl.Replicas() { + if rep.Connected && m.CurrentLSN > rep.AckedLSN && m.CurrentLSN-rep.AckedLSN > lag { + lag = m.CurrentLSN - rep.AckedLSN + } + } + } + wal := uint64(0) + if m.WALSizeBytes > 0 { + wal = uint64(m.WALSizeBytes) + } + hist.Add(metrics.Point{ + Time: now.Unix(), LSN: m.CurrentLSN, WALBytes: wal, + DirtyPages: m.DirtyPages, CachedPages: m.CachedPages, + ReqPerSec: rate, ReplicaLag: lag, + }) + } + } + }() + return func() { close(done) } +} diff --git a/internal/web/server.go b/internal/web/server.go index 6c068c2..a90a672 100644 --- a/internal/web/server.go +++ b/internal/web/server.go @@ -7,7 +7,11 @@ import ( "net/http" "time" + "github.com/ChromaBeast/beastdb/internal/audit" + "github.com/ChromaBeast/beastdb/internal/backup" "github.com/ChromaBeast/beastdb/internal/logbuf" + "github.com/ChromaBeast/beastdb/internal/metrics" + "github.com/ChromaBeast/beastdb/internal/runtimecfg" "github.com/ChromaBeast/beastdb/internal/web/auth" "github.com/ChromaBeast/beastdb/internal/web/handler" ) @@ -17,7 +21,8 @@ var staticFiles embed.FS // Server is the embedded HTTP admin server running alongside the gRPC server. type Server struct { - httpServer *http.Server + httpServer *http.Server + stopSampler func() } // EngineBackend is the combined interface required by the web server. @@ -47,6 +52,16 @@ type Options struct { Logs *logbuf.Buffer // Replication supplies the leader's replica view for /api/replication (nil on followers). Replication handler.ReplicationSource + // Cluster backs the epoch/role fields of /api/replication and + // POST /api/replication/promote; nil disables promotion. + Cluster handler.ClusterControl + // Audit records admin actions and backs GET /api/audit; nil disables both. + Audit *audit.Log + // Runtime holds the settings an admin may change live and backs + // /api/settings/runtime; nil makes that endpoint answer 404. + Runtime *runtimecfg.Store + // Backups backs /api/backups; nil makes those endpoints answer 404. + Backups *backup.Manager } // NewServerWithOptions is NewServer plus optional settings. @@ -63,13 +78,21 @@ func NewServerWithOptions(addr string, engine EngineBackend, secret []byte, role apiH.SetTokenStore(tokenStore[0]) } apiH.SetReplicationSource(opts.Replication) + apiH.SetCluster(opts.Cluster) + apiH.SetAudit(opts.Audit) rateLimiter := handler.NewLoginRateLimiter(5, 30*time.Second) + if opts.Runtime != nil { + opts.Runtime.Subscribe(func(v runtimecfg.Values) { + rateLimiter.Configure(v.LoginMaxAttempts, time.Duration(v.LoginWindowSeconds)*time.Second) + }) + } deps := &handler.Deps{ Users: users, Sessions: sessions, StaticFS: staticFS, RateLimiter: rateLimiter, + Audit: opts.Audit, } mux := http.NewServeMux() @@ -147,13 +170,24 @@ func NewServerWithOptions(addr string, engine EngineBackend, secret []byte, role // Operations (checkpoint, replica view) — admin only mux.Handle("/api/checkpoint", handler.AdminMiddleware(sessions, http.HandlerFunc(apiH.PostCheckpoint))) mux.Handle("/api/replication", handler.AdminMiddleware(sessions, http.HandlerFunc(apiH.GetReplication))) + mux.Handle("/api/replication/promote", handler.AdminMiddleware(sessions, http.HandlerFunc(apiH.PostPromote))) + + mux.Handle("/api/settings/runtime", handler.AdminMiddleware(sessions, handler.RuntimeSettingsHandler(sessions, opts.Runtime, opts.Audit))) + backupsH := handler.AdminMiddleware(sessions, handler.BackupsHandler(sessions, opts.Backups, opts.Audit)) + mux.Handle("/api/backups", backupsH) + mux.Handle("/api/backups/", backupsH) + mux.Handle("/api/audit", handler.AdminMiddleware(sessions, handler.AuditHandler(sessions, opts.Audit))) + + // Server-side metric history (memory only), sampled in the background. + history := metrics.NewHistory(historyCapacity) + mux.Handle("/api/metrics/history", handler.AdminMiddleware(sessions, handler.MetricsHistoryHandler(sessions, history, historyInterval))) srv := &http.Server{ Addr: addr, Handler: securityHeaders(handler.HTTPMetrics(mux)), } - return &Server{httpServer: srv}, nil + return &Server{httpServer: srv, stopSampler: startSampler(engine, opts.Replication, history, historyInterval)}, nil } // securityHeaders injects defense-in-depth headers into all web and API responses. @@ -176,5 +210,9 @@ func (s *Server) Serve() { // Shutdown gracefully stops the HTTP server. func (s *Server) Shutdown() { + if s.stopSampler != nil { + s.stopSampler() + s.stopSampler = nil + } _ = s.httpServer.Close() } diff --git a/internal/web/static/404.html b/internal/web/static/404.html index 1f2c56c..b27a97e 100644 --- a/internal/web/static/404.html +++ b/internal/web/static/404.html @@ -1 +1 @@ -404: This page could not be found.BeastDB Studio — Console

404

This page could not be found.

\ No newline at end of file +404: This page could not be found.BeastDB Studio — Console

404

This page could not be found.

\ No newline at end of file diff --git a/internal/web/static/404/index.html b/internal/web/static/404/index.html index 1f2c56c..b27a97e 100644 --- a/internal/web/static/404/index.html +++ b/internal/web/static/404/index.html @@ -1 +1 @@ -404: This page could not be found.BeastDB Studio — Console

404

This page could not be found.

\ No newline at end of file +404: This page could not be found.BeastDB Studio — Console

404

This page could not be found.

\ No newline at end of file diff --git a/internal/web/static/_next/static/chunks/695-b3536a607b599f97.js b/internal/web/static/_next/static/chunks/685-eeaee2683c96969f.js similarity index 97% rename from internal/web/static/_next/static/chunks/695-b3536a607b599f97.js rename to internal/web/static/_next/static/chunks/685-eeaee2683c96969f.js index b5e2daa..62170e2 100644 --- a/internal/web/static/_next/static/chunks/695-b3536a607b599f97.js +++ b/internal/web/static/_next/static/chunks/685-eeaee2683c96969f.js @@ -1 +1 @@ -"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[695],{109:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Search",[["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}],["path",{d:"m21 21-4.3-4.3",key:"1qie3q"}]])},222:(e,t,r)=>{r.d(t,{c:()=>o});var n=r(2115);function o(e){let t=n.useRef(e);return n.useEffect(()=>{t.current=e}),n.useMemo(()=>(...e)=>t.current?.(...e),[])}(0,Object.defineProperty)(o,"name",{value:"useCallbackRef",configurable:!0})},406:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("TriangleAlert",[["path",{d:"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3",key:"wmoenq"}],["path",{d:"M12 9v4",key:"juzpu7"}],["path",{d:"M12 17h.01",key:"p32p05"}]])},443:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("UserX",[["path",{d:"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2",key:"1yyitq"}],["circle",{cx:"9",cy:"7",r:"4",key:"nufk8"}],["line",{x1:"17",x2:"22",y1:"8",y2:"13",key:"3nzzx3"}],["line",{x1:"22",x2:"17",y1:"8",y2:"13",key:"1swrse"}]])},644:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("WifiOff",[["path",{d:"M12 20h.01",key:"zekei9"}],["path",{d:"M8.5 16.429a5 5 0 0 1 7 0",key:"1bycff"}],["path",{d:"M5 12.859a10 10 0 0 1 5.17-2.69",key:"1dl1wf"}],["path",{d:"M19 12.859a10 10 0 0 0-2.007-1.523",key:"4k23kn"}],["path",{d:"M2 8.82a15 15 0 0 1 4.177-2.643",key:"1grhjp"}],["path",{d:"M22 8.82a15 15 0 0 0-11.288-3.764",key:"z3jwby"}],["path",{d:"m2 2 20 20",key:"1ooewy"}]])},805:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("WandSparkles",[["path",{d:"m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.2 1.2 0 0 0 1.72 0L21.64 5.36a1.2 1.2 0 0 0 0-1.72",key:"ul74o6"}],["path",{d:"m14 7 3 3",key:"1r5n42"}],["path",{d:"M5 6v4",key:"ilb8ba"}],["path",{d:"M19 14v4",key:"blhpug"}],["path",{d:"M10 2v2",key:"7u0qdc"}],["path",{d:"M7 8H3",key:"zfb6yr"}],["path",{d:"M21 16h-4",key:"1cnmox"}],["path",{d:"M11 3H9",key:"1obp7u"}]])},984:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("CircleCheck",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]])},1165:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("ArrowDown",[["path",{d:"M12 5v14",key:"s699le"}],["path",{d:"m19 12-7 7-7-7",key:"1idqje"}]])},1190:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("ChevronLeft",[["path",{d:"m15 18-6-6 6-6",key:"1wnfg3"}]])},1270:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Lock",[["rect",{width:"18",height:"11",x:"3",y:"11",rx:"2",ry:"2",key:"1w4ew1"}],["path",{d:"M7 11V7a5 5 0 0 1 10 0v4",key:"fwvmzm"}]])},1286:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("FileSearch",[["path",{d:"M14 2v4a2 2 0 0 0 2 2h4",key:"tnqrlb"}],["path",{d:"M4.268 21a2 2 0 0 0 1.727 1H18a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v3",key:"ms7g94"}],["path",{d:"m9 18-1.5-1.5",key:"1j6qii"}],["circle",{cx:"5",cy:"14",r:"3",key:"ufru5t"}]])},1288:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Copy",[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]])},1851:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Activity",[["path",{d:"M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2",key:"169zse"}]])},2033:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("LoaderCircle",[["path",{d:"M21 12a9 9 0 1 1-6.219-8.56",key:"13zald"}]])},2036:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Pen",[["path",{d:"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z",key:"1a8usu"}]])},2111:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Play",[["polygon",{points:"6 3 20 12 6 21 6 3",key:"1oa8hb"}]])},2251:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Check",[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]])},2265:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Plus",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]])},2467:(e,t,r)=>{r.d(t,{TL:()=>s});var n,o=r(2115),a=r(4446),l=Object.defineProperty,i=(e,t)=>l(e,"name",{value:t,configurable:!0});function s(e){let t=o.forwardRef((t,r)=>{let{children:n,...l}=t,i=null,s=!1,c=[];h(n)&&"function"==typeof g&&(n=g(n._payload)),o.Children.forEach(n,e=>{if(p(e)){s=!0;let t="child"in e.props?e.props.child:e.props.children;h(t)&&"function"==typeof g&&(t=g(t._payload)),i=u(e,t),c.push(i?.props?.children)}else c.push(e)}),i?i=o.cloneElement(i,void 0,c):!s&&1===o.Children.count(n)&&o.isValidElement(n)&&(i=n);let m=i?f(i):void 0,b=(0,a.s)(r,m);if(!i){if(n||0===n)throw Error(s?v(e):y(e));return n}let w=d(l,i.props??{});return i.type!==o.Fragment&&(w.ref=r?b:m),o.cloneElement(i,w)});return t.displayName=`${e}.Slot`,t}i(s,"createSlot");var c=Symbol.for("radix.slottable");i(function(e){let t=i(e=>"child"in e?e.children(e.child):e.children,"Slottable");return t.displayName=`${e}.Slottable`,t.__radixId=c,t},"createSlottable");var u=i((e,t)=>{if("child"in e.props){let t=e.props.child;return o.isValidElement(t)?o.cloneElement(t,void 0,e.props.children(t.props.children)):null}return o.isValidElement(t)?t:null},"getSlottableElementFromSlottable");function d(e,t){let r={...t};for(let n in t){let o=e[n],a=t[n];/^on[A-Z]/.test(n)?o&&a?r[n]=(...e)=>{let t=a(...e);return o(...e),t}:o&&(r[n]=o):"style"===n?r[n]={...o,...a}:"className"===n&&(r[n]=[o,a].filter(Boolean).join(" "))}return{...e,...r}}function f(e){let t=Object.getOwnPropertyDescriptor(e.props,"ref")?.get,r=t&&"isReactWarning"in t&&t.isReactWarning;return r?e.ref:(r=(t=Object.getOwnPropertyDescriptor(e,"ref")?.get)&&"isReactWarning"in t&&t.isReactWarning)?e.props.ref:e.props.ref||e.ref}function p(e){return o.isValidElement(e)&&"function"==typeof e.type&&"__radixId"in e.type&&e.type.__radixId===c}i(d,"mergeProps"),i(f,"getElementRef"),i(p,"isSlottable");var m=Symbol.for("react.lazy");function h(e){return null!=e&&"object"==typeof e&&"$$typeof"in e&&e.$$typeof===m&&"_payload"in e&&b(e._payload)}function b(e){return"object"==typeof e&&null!==e&&"then"in e}i(h,"isLazyComponent"),i(b,"isPromiseLike");var y=i(e=>`${e} failed to slot onto its children. Expected a single React element child or \`Slottable\`.`,"createSlotError"),v=i(e=>`${e} failed to slot onto its \`Slottable\`. Expected \`Slottable\` to receive a single React element child.`,"createSlottableError"),g=(n||(n=r.t(o,2)))[" use ".trim().toString()]},2556:(e,t,r)=>{r.d(t,{mK:()=>l});var n=Object.defineProperty,o=(e,t)=>n(e,"name",{value:t,configurable:!0}),a=!!("undefined"!=typeof window&&window.document&&window.document.createElement);function l(e,t,{checkForDefaultPrevented:r=!0}={}){return o(function(n){if(e?.(n),!1===r||!n||!n.defaultPrevented)return t?.(n)},"handleEvent")}function i(e){if(!a)throw Error("Cannot access document outside of the DOM");return e?.ownerDocument??document}function s(e){return"IFRAME"===e.tagName}o(l,"composeEventHandlers"),o(function(e){if(!a)throw Error("Cannot access window outside of the DOM");return e?.ownerDocument?.defaultView??window},"getOwnerWindow"),o(i,"getOwnerDocument"),o(function e(t,r=!1){let{activeElement:n}=i(t);if(!n?.nodeName)return null;if(s(n)&&n.contentDocument)return e(n.contentDocument.body,r);if(r){let e=n.getAttribute("aria-activedescendant");if(e){let t=i(n).getElementById(e);if(t)return t}}return n},"getActiveElement"),o(s,"isFrame")},2649:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Star",[["path",{d:"M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z",key:"r04s7s"}]])},2784:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Moon",[["path",{d:"M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z",key:"a7tn18"}]])},2821:(e,t,r)=>{r.d(t,{$:()=>n});function n(){for(var e,t,r=0,n="",o=arguments.length;r{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Archive",[["rect",{width:"20",height:"5",x:"2",y:"3",rx:"1",key:"1wp1u1"}],["path",{d:"M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8",key:"1s80jp"}],["path",{d:"M10 12h4",key:"a56b0p"}]])},3063:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Settings",[["path",{d:"M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z",key:"1qme2f"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]])},3101:(e,t,r)=>{r.d(t,{F:()=>l});var n=r(2821);let o=e=>"boolean"==typeof e?`${e}`:0===e?"0":e,a=n.$,l=(e,t)=>r=>{var n;if((null==t?void 0:t.variants)==null)return a(e,null==r?void 0:r.class,null==r?void 0:r.className);let{variants:l,defaultVariants:i}=t,s=Object.keys(l).map(e=>{let t=null==r?void 0:r[e],n=null==i?void 0:i[e];if(null===t)return null;let a=o(t)||o(n);return l[e][a]}),c=r&&Object.entries(r).reduce((e,t)=>{let[r,n]=t;return void 0===n||(e[r]=n),e},{});return a(e,s,null==t||null==(n=t.compoundVariants)?void 0:n.reduce((e,t)=>{let{class:r,className:n,...o}=t;return Object.entries(o).every(e=>{let[t,r]=e;return Array.isArray(r)?r.includes({...i,...c}[t]):({...i,...c})[t]===r})?[...e,r,n]:e},[]),null==r?void 0:r.class,null==r?void 0:r.className)}},3173:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("ArrowRight",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"m12 5 7 7-7 7",key:"xquz4c"}]])},3263:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("ChevronRight",[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]])},3381:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("RefreshCw",[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8",key:"v9h5vc"}],["path",{d:"M21 3v5h-5",key:"1q7to0"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16",key:"3uifl3"}],["path",{d:"M8 16H3v5",key:"1cv678"}]])},3468:(e,t,r)=>{r.d(t,{A:()=>i});var n=r(2115),o=r(5155),a=Object.defineProperty,l=(e,t)=>a(e,"name",{value:t,configurable:!0});function i(e,t=[]){let r=[];function a(t,a){let i=n.createContext(a);i.displayName=t+"Context";let s=r.length;r=[...r,a];let c=l(t=>{let{scope:r,children:a,...l}=t,c=r?.[e]?.[s]||i,u=n.useMemo(()=>l,Object.values(l));return(0,o.jsx)(c.Provider,{value:u,children:a})},"Provider");function u(r,o,l={}){let{optional:c=!1}=l,d=o?.[e]?.[s]||i,f=n.useContext(d);if(f)return f;if(void 0!==a)return a;if(!c)throw Error(`\`${r}\` must be used within \`${t}\``)}return c.displayName=t+"Provider",l(u,"useContext"),[c,u]}l(a,"createContext");let c=l(()=>{let t=r.map(e=>n.createContext(e));return l(function(r){let o=r?.[e]||t;return n.useMemo(()=>({[`__scope${e}`]:{...r,[e]:o}}),[r,o])},"useScope")},"createScope");return c.scopeName=e,[a,s(c,...t)]}function s(...e){let t=e[0];if(1===e.length)return t;let r=l(()=>{let r=e.map(e=>({useScope:e(),scopeName:e.scopeName}));return l(function(e){let o=r.reduce((t,{useScope:r,scopeName:n})=>{let o=r(e)[`__scope${n}`];return{...t,...o}},{});return n.useMemo(()=>({[`__scope${t.scopeName}`]:o}),[o])},"useComposedScopes")},"createScope");return r.scopeName=t.scopeName,r}l(function(e,t){let r=n.createContext(t);r.displayName=e+"Context";let a=l(e=>{let{children:t,...a}=e,l=n.useMemo(()=>a,Object.values(a));return(0,o.jsx)(r.Provider,{value:l,children:t})},"Provider");function i(o,a={}){let{optional:l=!1}=a,s=n.useContext(r);if(s)return s;if(void 0!==t)return t;if(!l)throw Error(`\`${o}\` must be used within \`${e}\``)}return a.displayName=e+"Provider",l(i,"useContext"),[a,i]},"createContext"),l(i,"createContextScope"),l(s,"composeContextScopes")},3489:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Download",[["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["polyline",{points:"7 10 12 15 17 10",key:"2ggqvy"}],["line",{x1:"12",x2:"12",y1:"15",y2:"3",key:"1vk2je"}]])},3527:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Columns2",[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}],["path",{d:"M12 3v18",key:"108xh3"}]])},3630:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Trash2",[["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6",key:"4alrt4"}],["path",{d:"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2",key:"v07s0e"}],["line",{x1:"10",x2:"10",y1:"11",y2:"17",key:"1uufr5"}],["line",{x1:"14",x2:"14",y1:"11",y2:"17",key:"xtxkd"}]])},4129:(e,t,r)=>{r.d(t,{N:()=>o});var n=r(2115),o=globalThis?.document?n.useLayoutEffect:()=>{}},4294:(e,t,r)=>{r.d(t,{A:()=>i});var n=r(2115);let o=function(){for(var e=arguments.length,t=Array(e),r=0;r!!e&&""!==e.trim()&&r.indexOf(e)===t).join(" ").trim()};var a={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};let l=(0,n.forwardRef)((e,t)=>{let{color:r="currentColor",size:l=24,strokeWidth:i=2,absoluteStrokeWidth:s,className:c="",children:u,iconNode:d,...f}=e;return(0,n.createElement)("svg",{ref:t,...a,width:l,height:l,stroke:r,strokeWidth:s?24*Number(i)/Number(l):i,className:o("lucide",c),...f},[...d.map(e=>{let[t,r]=e;return(0,n.createElement)(t,r)}),...Array.isArray(u)?u:[u]])}),i=(e,t)=>{let r=(0,n.forwardRef)((r,a)=>{let{className:i,...s}=r;return(0,n.createElement)(l,{ref:a,iconNode:t,className:o("lucide-".concat(e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase()),i),...s})});return r.displayName="".concat(e),r}},4368:(e,t,r)=>{r.d(t,{bm:()=>ti,UC:()=>tt,VY:()=>tl,hJ:()=>e6,ZL:()=>e9,bL:()=>e1,hE:()=>ta,Hs:()=>eJ});var n,o,a,l=r(2115),i=r(2556),s=r(4446),c=r(3468),u=r(8946),d=r(9478),f=r(7602),p=r(222),m=r(5155),h=Object.defineProperty,b=(e,t)=>h(e,"name",{value:t,configurable:!0}),y="dismissableLayer.update",v=l.createContext({layers:new Set,layersWithOutsidePointerEventsDisabled:new Set,branches:new Set,dismissableSurfaces:new Set}),g=l.forwardRef(b(function(e,t){var r,n;let{disableOutsidePointerEvents:a=!1,deferPointerDownOutside:c=!1,onEscapeKeyDown:u,onPointerDownOutside:d,onFocusOutside:h,onInteractOutside:g,onDismiss:w,...k}=e,C=l.useContext(v),[M,S]=l.useState(null),R=null!=(n=null==M?void 0:M.ownerDocument)?n:null==(r=globalThis)?void 0:r.document,[,O]=l.useState({}),j=(0,s.s)(t,S),D=Array.from(C.layers),[P]=[...C.layersWithOutsidePointerEventsDisabled].slice(-1),z=P?D.indexOf(P):-1,N=M?D.indexOf(M):-1,L=C.layersWithOutsidePointerEventsDisabled.size>0,I=N>=z,T=l.useRef(!1),F=x(e=>{null==d||d(e),null==g||g(e),e.defaultPrevented||null==w||w()},{ownerDocument:R,deferPointerDownOutside:c,isDeferredPointerDownOutsideRef:T,dismissableSurfaces:C.dismissableSurfaces,shouldHandlePointerDownOutside:l.useCallback(e=>{if(!(e instanceof Node))return!1;let t=[...C.branches].some(t=>t.contains(e));return I&&!t},[C.branches,I])}),_=A(e=>{if(c&&T.current)return;let t=e.target;![...C.branches].some(e=>e.contains(t))&&(null==h||h(e),null==g||g(e),e.defaultPrevented||null==w||w())},R),W=!!M&&N===D.length-1,G=(0,p.c)(e=>{"Escape"===e.key&&(null==u||u(e),!e.defaultPrevented&&w&&(e.preventDefault(),w()))});return l.useEffect(()=>{if(W)return R.addEventListener("keydown",G,{capture:!0}),()=>R.removeEventListener("keydown",G,{capture:!0})},[R,W,G]),l.useEffect(()=>{if(M)return a&&(0===C.layersWithOutsidePointerEventsDisabled.size&&(o=R.body.style.pointerEvents,R.body.style.pointerEvents="none"),C.layersWithOutsidePointerEventsDisabled.add(M)),C.layers.add(M),E(),()=>{a&&(C.layersWithOutsidePointerEventsDisabled.delete(M),0===C.layersWithOutsidePointerEventsDisabled.size&&(R.body.style.pointerEvents=o))}},[M,R,a,C]),l.useEffect(()=>()=>{M&&(C.layers.delete(M),C.layersWithOutsidePointerEventsDisabled.delete(M),E())},[M,C]),l.useEffect(()=>{let e=b(()=>O({}),"handleUpdate");return document.addEventListener(y,e),()=>document.removeEventListener(y,e)},[]),(0,m.jsx)(f.sG.div,{...k,ref:j,style:{pointerEvents:L?I?"auto":"none":void 0,...e.style},onFocusCapture:(0,i.mK)(e.onFocusCapture,_.onFocusCapture),onBlurCapture:(0,i.mK)(e.onBlurCapture,_.onBlurCapture),onPointerDownCapture:(0,i.mK)(e.onPointerDownCapture,F.onPointerDownCapture)})},"DismissableLayer"));function w(){let e=l.useContext(v),[t,r]=l.useState(null);return l.useEffect(()=>{if(t)return e.dismissableSurfaces.add(t),()=>{e.dismissableSurfaces.delete(t)}},[t,e.dismissableSurfaces]),r}b(w,"useDismissableLayerSurface");var k=b(()=>!0,"IS_TRUE");function x(e,t){var r;let{ownerDocument:n=null==(r=globalThis)?void 0:r.document,deferPointerDownOutside:o=!1,isDeferredPointerDownOutsideRef:a,dismissableSurfaces:i,shouldHandlePointerDownOutside:s=k}=t,c=(0,p.c)(e),u=l.useRef(!1),d=l.useRef(!1),f=l.useRef(new Map),m=l.useRef(()=>{});return l.useEffect(()=>{function e(){d.current=!1,a.current=!1,f.current.clear()}function t(){return Array.from(f.current.values()).some(Boolean)}function r(e){if(!d.current)return;let t=e.target;t instanceof Node&&[...i].some(e=>e.contains(t))||f.current.set(e.type,!0),"click"===e.type&&window.setTimeout(()=>{d.current&&m.current()},0)}function l(e){d.current&&f.current.set(e.type,!1)}b(e,"resetOutsideInteraction"),b(t,"isOutsideInteractionIntercepted"),b(r,"handleInteractionCapture"),b(l,"handleInteractionBubble");let p=b(r=>{if(r.target&&!u.current){let l=function(){n.removeEventListener("click",m.current);let r=t();e(),r||C("dismissableLayer.pointerDownOutside",c,i,{discrete:!0})};if(b(l,"handleAndDispatchPointerDownOutsideEvent"),!s(r.target)){n.removeEventListener("click",m.current),e(),u.current=!1;return}let i={originalEvent:r};d.current=!0,a.current=o&&0===r.button,f.current.clear(),o&&0===r.button?(n.removeEventListener("click",m.current),m.current=l,n.addEventListener("click",m.current,{once:!0})):l()}else n.removeEventListener("click",m.current),e();u.current=!1},"handlePointerDown"),h=["pointerup","mousedown","mouseup","touchstart","touchend","click"];for(let e of h)n.addEventListener(e,r,!0),n.addEventListener(e,l);let y=window.setTimeout(()=>{n.addEventListener("pointerdown",p)},0);return()=>{for(let e of(window.clearTimeout(y),n.removeEventListener("pointerdown",p),n.removeEventListener("click",m.current),h))n.removeEventListener(e,r,!0),n.removeEventListener(e,l)}},[n,c,o,a,i,s]),{onPointerDownCapture:b(()=>u.current=!0,"onPointerDownCapture")}}function A(e){var t;let r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null==(t=globalThis)?void 0:t.document,n=(0,p.c)(e),o=l.useRef(!1);return l.useEffect(()=>{let e=b(e=>{e.target&&!o.current&&C("dismissableLayer.focusOutside",n,{originalEvent:e},{discrete:!1})},"handleFocus");return r.addEventListener("focusin",e),()=>r.removeEventListener("focusin",e)},[r,n]),{onFocusCapture:b(()=>o.current=!0,"onFocusCapture"),onBlurCapture:b(()=>o.current=!1,"onBlurCapture")}}function E(){let e=new CustomEvent(y);document.dispatchEvent(e)}function C(e,t,r,n){let{discrete:o}=n,a=r.originalEvent.target,l=new CustomEvent(e,{bubbles:!1,cancelable:!0,detail:r});t&&a.addEventListener(e,t,{once:!0}),o?(0,f.hO)(a,l):a.dispatchEvent(l)}b(x,"usePointerDownOutside"),b(A,"useFocusOutside"),b(E,"dispatchUpdate"),b(C,"handleAndDispatchCustomEvent");var M=Object.defineProperty,S=(e,t)=>M(e,"name",{value:t,configurable:!0}),R="focusScope.autoFocusOnMount",O="focusScope.autoFocusOnUnmount",j={bubbles:!1,cancelable:!0},D=l.forwardRef(S(function(e,t){let{loop:r=!1,trapped:n=!1,onMountAutoFocus:o,onUnmountAutoFocus:a,...i}=e,[c,u]=l.useState(null),d=(0,p.c)(o),h=(0,p.c)(a),b=l.useRef(null),y=(0,s.s)(t,u),v=l.useRef({paused:!1,pause(){this.paused=!0},resume(){this.paused=!1}}).current;l.useEffect(()=>{if(n){let e=function(e){if(v.paused||!c)return;let t=e.target;c.contains(t)?b.current=t:F(b.current,{select:!0})},t=function(e){if(v.paused||!c)return;let t=e.relatedTarget;null!==t&&(c.contains(t)||F(b.current,{select:!0}))},r=function(e){if(document.activeElement===document.body)for(let t of e)t.removedNodes.length>0&&F(c)};S(e,"handleFocusIn"),S(t,"handleFocusOut"),S(r,"handleMutations"),document.addEventListener("focusin",e),document.addEventListener("focusout",t);let n=new MutationObserver(r);return c&&n.observe(c,{childList:!0,subtree:!0}),()=>{document.removeEventListener("focusin",e),document.removeEventListener("focusout",t),n.disconnect()}}},[n,c,v.paused]),l.useEffect(()=>{if(c){_.add(v);let e=document.activeElement;if(!c.contains(e)){let t=new CustomEvent(R,j);c.addEventListener(R,d),c.dispatchEvent(t),t.defaultPrevented||(P(K(N(c)),{select:!0}),document.activeElement===e&&F(c))}return()=>{c.removeEventListener(R,d),setTimeout(()=>{let t=new CustomEvent(O,j);c.addEventListener(O,h),c.dispatchEvent(t),t.defaultPrevented||F(null!=e?e:document.body,{select:!0}),c.removeEventListener(O,h),_.remove(v)},0)}}},[c,d,h,v]);let g=l.useCallback(e=>{if(!r&&!n||v.paused)return;let t="Tab"===e.key&&!e.altKey&&!e.ctrlKey&&!e.metaKey,o=document.activeElement;if(t&&o){let t=e.currentTarget,[n,a]=z(t);n&&a?e.shiftKey||o!==a?e.shiftKey&&o===n&&(e.preventDefault(),r&&F(a,{select:!0})):(e.preventDefault(),r&&F(n,{select:!0})):o===t&&e.preventDefault()}},[r,n,v.paused]);return(0,m.jsx)(f.sG.div,{tabIndex:-1,...i,ref:y,onKeyDown:g})},"FocusScope"));function P(e){let{select:t=!1}=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=document.activeElement;for(let n of e)if(F(n,{select:t}),document.activeElement!==r)return}function z(e){let t=N(e);return[L(t,e),L(t.reverse(),e)]}function N(e){let t=[],r=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT,{acceptNode:S(e=>{let t="INPUT"===e.tagName&&"hidden"===e.type;return e.disabled||e.hidden||t?NodeFilter.FILTER_SKIP:e.tabIndex>=0?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP},"acceptNode")});for(;r.nextNode();)t.push(r.currentNode);return t}function L(e,t){let r="function"==typeof t.checkVisibility&&t.checkVisibility({checkVisibilityCSS:!0});for(let n of e)if(!(r?!n.checkVisibility({checkVisibilityCSS:!0}):I(n,{upTo:t})))return n}function I(e,t){let{upTo:r}=t;if("hidden"===getComputedStyle(e).visibility)return!0;for(;e&&(void 0===r||e!==r);){if("none"===getComputedStyle(e).display)return!0;e=e.parentElement}return!1}function T(e){return e instanceof HTMLInputElement&&"select"in e}function F(e){let{select:t=!1}=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(e&&e.focus){let r=document.activeElement;e.focus({preventScroll:!0}),e!==r&&T(e)&&t&&e.select()}}S(P,"focusFirst"),S(z,"getTabbableEdges"),S(N,"getTabbableCandidates"),S(L,"findVisible"),S(I,"isHidden"),S(T,"isSelectableInput"),S(F,"focus");var _=W();function W(){let e=[];return{add(t){let r=e[0];t!==r&&(null==r||r.pause()),(e=G(e,t)).unshift(t)},remove(t){var r;null==(r=(e=G(e,t))[0])||r.resume()}}}function G(e,t){let r=[...e],n=r.indexOf(t);return -1!==n&&r.splice(n,1),r}function K(e){return e.filter(e=>"A"!==e.tagName)}S(W,"createFocusScopesStack"),S(G,"arrayRemove"),S(K,"removeLinks");var V=r(7650),H=r(4129),B=Object.defineProperty,q=l.forwardRef(B(function(e,t){var r,n;let{container:o,...a}=e,[i,s]=l.useState(!1);(0,H.N)(()=>s(!0),[]);let c=o||i&&(null==(n=globalThis)||null==(r=n.document)?void 0:r.body);return c?V.createPortal((0,m.jsx)(f.sG.div,{...a,ref:t}),c):null},"name",{value:"Portal",configurable:!0})),$=r(6842),U=Object.defineProperty,Z=(e,t)=>U(e,"name",{value:t,configurable:!0}),X=0,Y=null;function J(){l.useEffect(()=>{Y||(Y={start:Q(),end:Q()});let{start:e,end:t}=Y;return document.body.firstElementChild!==e&&document.body.insertAdjacentElement("afterbegin",e),document.body.lastElementChild!==t&&document.body.insertAdjacentElement("beforeend",t),X++,()=>{1===X&&(null==Y||Y.start.remove(),null==Y||Y.end.remove(),Y=null),X=Math.max(0,X-1)}},[])}function Q(){let e=document.createElement("span");return e.setAttribute("data-radix-focus-guard",""),e.tabIndex=0,e.style.outline="none",e.style.opacity="0",e.style.position="fixed",e.style.pointerEvents="none",e}Z(function(e){return J(),e.children},"FocusGuards"),Z(J,"useFocusGuards"),Z(Q,"createFocusGuard");var ee=function(){return(ee=Object.assign||function(e){for(var t,r=1,n=arguments.length;rt.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(r[n[o]]=e[n[o]]);return r}Object.create;Object.create;var er=("function"==typeof SuppressedError&&SuppressedError,"right-scroll-bar-position"),en="width-before-scroll-bar";function eo(e,t){return"function"==typeof e?e(t):e&&(e.current=t),e}var ea="undefined"!=typeof window?l.useLayoutEffect:l.useEffect,el=new WeakMap;function ei(e){return e}var es=function(e){void 0===e&&(e={});var t,r,n,o=(void 0===t&&(t=ei),r=[],n=!1,{read:function(){if(n)throw Error("Sidecar: could not `read` from an `assigned` medium. `read` could be used only with `useMedium`.");return r.length?r[r.length-1]:null},useMedium:function(e){var o=t(e,n);return r.push(o),function(){r=r.filter(function(e){return e!==o})}},assignSyncMedium:function(e){for(n=!0;r.length;){var t=r;r=[],t.forEach(e)}r={push:function(t){return e(t)},filter:function(){return r}}},assignMedium:function(e){n=!0;var t=[];if(r.length){var o=r;r=[],o.forEach(e),t=r}var a=function(){var r=t;t=[],r.forEach(e)},l=function(){return Promise.resolve().then(a)};l(),r={push:function(e){t.push(e),l()},filter:function(e){return t=t.filter(e),r}}}});return o.options=ee({async:!0,ssr:!1},e),o}(),ec=function(){},eu=l.forwardRef(function(e,t){var r,n,o,a,i=l.useRef(null),s=l.useState({onScrollCapture:ec,onWheelCapture:ec,onTouchMoveCapture:ec}),c=s[0],u=s[1],d=e.forwardProps,f=e.children,p=e.className,m=e.removeScrollBar,h=e.enabled,b=e.shards,y=e.sideCar,v=e.noRelative,g=e.noIsolation,w=e.inert,k=e.allowPinchZoom,x=e.as,A=e.gapMode,E=et(e,["forwardProps","children","className","removeScrollBar","enabled","shards","sideCar","noRelative","noIsolation","inert","allowPinchZoom","as","gapMode"]),C=(r=[i,t],n=function(e){return r.forEach(function(t){return eo(t,e)})},(o=(0,l.useState)(function(){return{value:null,callback:n,facade:{get current(){return o.value},set current(value){var e=o.value;e!==value&&(o.value=value,o.callback(value,e))}}}})[0]).callback=n,a=o.facade,ea(function(){var e=el.get(a);if(e){var t=new Set(e),n=new Set(r),o=a.current;t.forEach(function(e){n.has(e)||eo(e,null)}),n.forEach(function(e){t.has(e)||eo(e,o)})}el.set(a,r)},[r]),a),M=ee(ee({},E),c);return l.createElement(l.Fragment,null,h&&l.createElement(y,{sideCar:es,removeScrollBar:m,shards:b,noRelative:v,noIsolation:g,inert:w,setCallbacks:u,allowPinchZoom:!!k,lockRef:i,gapMode:A}),d?l.cloneElement(l.Children.only(f),ee(ee({},M),{ref:C})):l.createElement(void 0===x?"div":x,ee({},M,{className:p,ref:C}),f))});eu.defaultProps={enabled:!0,removeScrollBar:!0,inert:!1},eu.classNames={fullWidth:en,zeroRight:er};var ed=function(e){var t=e.sideCar,r=et(e,["sideCar"]);if(!t)throw Error("Sidecar: please provide `sideCar` property to import the right car");var n=t.read();if(!n)throw Error("Sidecar medium not found");return l.createElement(n,ee({},r))};ed.isSideCarExport=!0;var ef=function(){var e=0,t=null;return{add:function(n){if(0==e&&(t=function(){if(!document)return null;var e=document.createElement("style");e.type="text/css";var t=a||r.nc;return t&&e.setAttribute("nonce",t),e}())){var o,l;(o=t).styleSheet?o.styleSheet.cssText=n:o.appendChild(document.createTextNode(n)),l=t,(document.head||document.getElementsByTagName("head")[0]).appendChild(l)}e++},remove:function(){--e||!t||(t.parentNode&&t.parentNode.removeChild(t),t=null)}}},ep=function(){var e=ef();return function(t,r){l.useEffect(function(){return e.add(t),function(){e.remove()}},[t&&r])}},em=function(){var e=ep();return function(t){return e(t.styles,t.dynamic),null}},eh={left:0,top:0,right:0,gap:0},eb=function(e){return parseInt(e||"",10)||0},ey=function(e){var t=window.getComputedStyle(document.body),r=t["padding"===e?"paddingLeft":"marginLeft"],n=t["padding"===e?"paddingTop":"marginTop"],o=t["padding"===e?"paddingRight":"marginRight"];return[eb(r),eb(n),eb(o)]},ev=function(e){if(void 0===e&&(e="margin"),"undefined"==typeof window)return eh;var t=ey(e),r=document.documentElement.clientWidth,n=window.innerWidth;return{left:t[0],top:t[1],right:t[2],gap:Math.max(0,n-r+t[2]-t[0])}},eg=em(),ew="data-scroll-locked",ek=function(e,t,r,n){var o=e.left,a=e.top,l=e.right,i=e.gap;return void 0===r&&(r="margin"),"\n .".concat("with-scroll-bars-hidden"," {\n overflow: hidden ").concat(n,";\n padding-right: ").concat(i,"px ").concat(n,";\n }\n body[").concat(ew,"] {\n overflow: hidden ").concat(n,";\n overscroll-behavior: contain;\n ").concat([t&&"position: relative ".concat(n,";"),"margin"===r&&"\n padding-left: ".concat(o,"px;\n padding-top: ").concat(a,"px;\n padding-right: ").concat(l,"px;\n margin-left:0;\n margin-top:0;\n margin-right: ").concat(i,"px ").concat(n,";\n "),"padding"===r&&"padding-right: ".concat(i,"px ").concat(n,";")].filter(Boolean).join(""),"\n }\n \n .").concat(er," {\n right: ").concat(i,"px ").concat(n,";\n }\n \n .").concat(en," {\n margin-right: ").concat(i,"px ").concat(n,";\n }\n \n .").concat(er," .").concat(er," {\n right: 0 ").concat(n,";\n }\n \n .").concat(en," .").concat(en," {\n margin-right: 0 ").concat(n,";\n }\n \n body[").concat(ew,"] {\n ").concat("--removed-body-scroll-bar-size",": ").concat(i,"px;\n }\n")},ex=function(){var e=parseInt(document.body.getAttribute(ew)||"0",10);return isFinite(e)?e:0},eA=function(){l.useEffect(function(){return document.body.setAttribute(ew,(ex()+1).toString()),function(){var e=ex()-1;e<=0?document.body.removeAttribute(ew):document.body.setAttribute(ew,e.toString())}},[])},eE=function(e){var t=e.noRelative,r=e.noImportant,n=e.gapMode,o=void 0===n?"margin":n;eA();var a=l.useMemo(function(){return ev(o)},[o]);return l.createElement(eg,{styles:ek(a,!t,o,r?"":"!important")})},eC=!1;if("undefined"!=typeof window)try{var eM=Object.defineProperty({},"passive",{get:function(){return eC=!0,!0}});window.addEventListener("test",eM,eM),window.removeEventListener("test",eM,eM)}catch(e){eC=!1}var eS=!!eC&&{passive:!1},eR=function(e,t){if(!(e instanceof Element))return!1;var r=window.getComputedStyle(e);return"hidden"!==r[t]&&(r.overflowY!==r.overflowX||"TEXTAREA"===e.tagName||"visible"!==r[t])},eO=function(e,t){var r=t.ownerDocument,n=t;do{if("undefined"!=typeof ShadowRoot&&n instanceof ShadowRoot&&(n=n.host),ej(e,n)){var o=eD(e,n);if(o[1]>o[2])return!0}n=n.parentNode}while(n&&n!==r.body);return!1},ej=function(e,t){return"v"===e?eR(t,"overflowY"):eR(t,"overflowX")},eD=function(e,t){return"v"===e?[t.scrollTop,t.scrollHeight,t.clientHeight]:[t.scrollLeft,t.scrollWidth,t.clientWidth]},eP=function(e,t,r,n,o){var a,l=(a=window.getComputedStyle(t).direction,"h"===e&&"rtl"===a?-1:1),i=l*n,s=r.target,c=t.contains(s),u=!1,d=i>0,f=0,p=0;do{if(!s)break;var m=eD(e,s),h=m[0],b=m[1]-m[2]-l*h;(h||b)&&ej(e,s)&&(f+=b,p+=h);var y=s.parentNode;s=y&&y.nodeType===Node.DOCUMENT_FRAGMENT_NODE?y.host:y}while(!c&&s!==document.body||c&&(t.contains(s)||t===s));return d&&(o&&1>Math.abs(f)||!o&&i>f)?u=!0:!d&&(o&&1>Math.abs(p)||!o&&-i>p)&&(u=!0),u},ez=function(e){return"changedTouches"in e?[e.changedTouches[0].clientX,e.changedTouches[0].clientY]:[0,0]},eN=function(e){return[e.deltaX,e.deltaY]},eL=function(e){return e&&"current"in e?e.current:e},eI=0,eT=[];let eF=(n=function(e){var t=l.useRef([]),r=l.useRef([0,0]),n=l.useRef(),o=l.useState(eI++)[0],a=l.useState(em)[0],i=l.useRef(e);l.useEffect(function(){i.current=e},[e]),l.useEffect(function(){if(e.inert){document.body.classList.add("block-interactivity-".concat(o));var t=(function(e,t,r){if(r||2==arguments.length)for(var n,o=0,a=t.length;oMath.abs(c)?"h":"v";if("touches"in e&&"h"===d&&"range"===u.type)return!1;var f=window.getSelection(),p=f&&f.anchorNode;if(p&&(p===u||p.contains(u)))return!1;var m=eO(d,u);if(!m)return!0;if(m?o=d:(o="v"===d?"h":"v",m=eO(d,u)),!m)return!1;if(!n.current&&"changedTouches"in e&&(s||c)&&(n.current=o),!o)return!0;var h=n.current||o;return eP(h,t,e,"h"===h?s:c,!0)},[]),c=l.useCallback(function(e){if(eT.length&&eT[eT.length-1]===a){var r="deltaY"in e?eN(e):ez(e),n=t.current.filter(function(t){var n;return t.name===e.type&&(t.target===e.target||e.target===t.shadowParent)&&(n=t.delta,n[0]===r[0]&&n[1]===r[1])})[0];if(n&&n.should){e.cancelable&&e.preventDefault();return}if(!n){var o=(i.current.shards||[]).map(eL).filter(Boolean).filter(function(t){return t.contains(e.target)});(o.length>0?s(e,o[0]):!i.current.noIsolation)&&e.cancelable&&e.preventDefault()}}},[]),u=l.useCallback(function(e,r,n,o){var a={name:e,delta:r,target:n,should:o,shadowParent:function(e){for(var t=null;null!==e;)e instanceof ShadowRoot&&(t=e.host,e=e.host),e=e.parentNode;return t}(n)};t.current.push(a),setTimeout(function(){t.current=t.current.filter(function(e){return e!==a})},1)},[]),d=l.useCallback(function(e){r.current=ez(e),n.current=void 0},[]),f=l.useCallback(function(t){u(t.type,eN(t),t.target,s(t,e.lockRef.current))},[]),p=l.useCallback(function(t){u(t.type,ez(t),t.target,s(t,e.lockRef.current))},[]);l.useEffect(function(){return eT.push(a),e.setCallbacks({onScrollCapture:f,onWheelCapture:f,onTouchMoveCapture:p}),document.addEventListener("wheel",c,eS),document.addEventListener("touchmove",c,eS),document.addEventListener("touchstart",d,eS),function(){eT=eT.filter(function(e){return e!==a}),document.removeEventListener("wheel",c,eS),document.removeEventListener("touchmove",c,eS),document.removeEventListener("touchstart",d,eS)}},[]);var m=e.removeScrollBar,h=e.inert;return l.createElement(l.Fragment,null,h?l.createElement(a,{styles:"\n .block-interactivity-".concat(o," {pointer-events: none;}\n .allow-interactivity-").concat(o," {pointer-events: all;}\n")}):null,m?l.createElement(eE,{noRelative:e.noRelative,gapMode:e.gapMode}):null)},es.useMedium(n),ed);var e_=l.forwardRef(function(e,t){return l.createElement(eu,ee({},e,{ref:t,sideCar:eF}))});e_.classNames=eu.classNames;var eW=new WeakMap,eG=new WeakMap,eK={},eV=0,eH=function(e){return e&&(e.host||eH(e.parentNode))},eB=function(e,t,r,n){var o=(Array.isArray(e)?e:[e]).map(function(e){if(t.contains(e))return e;var r=eH(e);return r&&t.contains(r)?r:(console.error("aria-hidden",e,"in not contained inside",t,". Doing nothing"),null)}).filter(function(e){return!!e});eK[r]||(eK[r]=new WeakMap);var a=eK[r],l=[],i=new Set,s=new Set(o),c=function(e){!e||i.has(e)||(i.add(e),c(e.parentNode))};o.forEach(c);var u=function(e){!e||s.has(e)||Array.prototype.forEach.call(e.children,function(e){if(i.has(e))u(e);else try{var t=e.getAttribute(n),o=null!==t&&"false"!==t,s=(eW.get(e)||0)+1,c=(a.get(e)||0)+1;eW.set(e,s),a.set(e,c),l.push(e),1===s&&o&&eG.set(e,!0),1===c&&e.setAttribute(r,"true"),o||e.setAttribute(n,"true")}catch(t){console.error("aria-hidden: cannot operate on ",e,t)}})};return u(t),i.clear(),eV++,function(){l.forEach(function(e){var t=eW.get(e)-1,o=a.get(e)-1;eW.set(e,t),a.set(e,o),t||(eG.has(e)||e.removeAttribute(n),eG.delete(e)),o||e.removeAttribute(r)}),--eV||(eW=new WeakMap,eW=new WeakMap,eG=new WeakMap,eK={})}},eq=function(e,t,r){void 0===r&&(r="data-aria-hidden");var n=Array.from(Array.isArray(e)?e:[e]),o=t||("undefined"==typeof document?null:(Array.isArray(e)?e[0]:e).ownerDocument.body);return o?(n.push.apply(n,Array.from(o.querySelectorAll("[aria-live], script"))),eB(n,o,r,"aria-hidden")):function(){return null}},e$=r(2467),eU=Object.defineProperty,eZ=(e,t)=>eU(e,"name",{value:t,configurable:!0}),eX="Dialog",[eY,eJ]=(0,c.A)(eX),[eQ,e0]=eY(eX),e1=eZ(e=>{let{__scopeDialog:t,children:r,open:n,defaultOpen:o,onOpenChange:a,modal:i=!0}=e,s=l.useRef(null),c=l.useRef(null),[f,p]=(0,d.i)({prop:n,defaultProp:null!=o&&o,onChange:a,caller:eX}),[h,b]=l.useState(0),[y,v]=l.useState(0);return(0,m.jsx)(eQ,{scope:t,triggerRef:s,contentRef:c,contentId:(0,u.B)(),titleId:(0,u.B)(),descriptionId:(0,u.B)(),titlePresent:h>0,descriptionPresent:y>0,setTitleCount:b,setDescriptionCount:v,open:f,onOpenChange:p,onOpenToggle:l.useCallback(()=>p(e=>!e),[p]),modal:i,children:r})},"Dialog"),e2="DialogPortal",[e4,e5]=eY(e2,{forceMount:void 0}),e9=eZ(e=>{let{__scopeDialog:t,forceMount:r,children:n,container:o}=e,a=e0(e2,t);return(0,m.jsx)(e4,{scope:t,forceMount:r,children:l.Children.map(n,e=>(0,m.jsx)($.C,{present:r||a.open,children:(0,m.jsx)(q,{asChild:!0,container:o,children:e})}))})},"DialogPortal"),e3="DialogOverlay",e6=l.forwardRef(eZ(function(e,t){let r=e5(e3,e.__scopeDialog),{forceMount:n=r.forceMount,...o}=e,a=e0(e3,e.__scopeDialog);return a.modal?(0,m.jsx)($.C,{present:n||a.open,children:(0,m.jsx)(e8,{...o,ref:t})}):null},"DialogOverlay")),e7=(0,e$.TL)("DialogOverlay.RemoveScroll"),e8=l.forwardRef(eZ(function(e,t){let{__scopeDialog:r,...n}=e,o=e0(e3,r),a=w(),l=(0,s.s)(t,a);return(0,m.jsx)(e_,{as:e7,allowPinchZoom:!0,shards:[o.contentRef],children:(0,m.jsx)(f.sG.div,{"data-state":ts(o.open),...n,ref:l,style:{pointerEvents:"auto",...n.style}})})},"DialogOverlayImpl")),te="DialogContent",tt=l.forwardRef(eZ(function(e,t){let r=e5(te,e.__scopeDialog),{forceMount:n=r.forceMount,...o}=e,a=e0(te,e.__scopeDialog);return(0,m.jsx)($.C,{present:n||a.open,children:a.modal?(0,m.jsx)(tr,{...o,ref:t}):(0,m.jsx)(tn,{...o,ref:t})})},"DialogContent")),tr=l.forwardRef(eZ(function(e,t){let r=e0(te,e.__scopeDialog),n=l.useRef(null),o=(0,s.s)(t,r.contentRef,n);return l.useEffect(()=>{let e=n.current;if(e)return eq(e)},[]),(0,m.jsx)(to,{...e,ref:o,trapFocus:r.open,disableOutsidePointerEvents:r.open,onCloseAutoFocus:(0,i.mK)(e.onCloseAutoFocus,e=>{var t;e.preventDefault(),null==(t=r.triggerRef.current)||t.focus()}),onPointerDownOutside:(0,i.mK)(e.onPointerDownOutside,e=>{let t=e.detail.originalEvent,r=0===t.button&&!0===t.ctrlKey;(2===t.button||r)&&e.preventDefault()}),onFocusOutside:(0,i.mK)(e.onFocusOutside,e=>e.preventDefault())})},"DialogContentModal")),tn=l.forwardRef(eZ(function(e,t){let r=e0(te,e.__scopeDialog),n=l.useRef(!1),o=l.useRef(!1);return(0,m.jsx)(to,{...e,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:t=>{var a,l;null==(a=e.onCloseAutoFocus)||a.call(e,t),t.defaultPrevented||(n.current||null==(l=r.triggerRef.current)||l.focus(),t.preventDefault()),n.current=!1,o.current=!1},onInteractOutside:t=>{var a,l;null==(a=e.onInteractOutside)||a.call(e,t),t.defaultPrevented||(n.current=!0,"pointerdown"===t.detail.originalEvent.type&&(o.current=!0));let i=t.target;(null==(l=r.triggerRef.current)?void 0:l.contains(i))&&t.preventDefault(),"focusin"===t.detail.originalEvent.type&&o.current&&t.preventDefault()}})},"DialogContentNonModal")),to=l.forwardRef(eZ(function(e,t){let{__scopeDialog:r,trapFocus:n,onOpenAutoFocus:o,onCloseAutoFocus:a,...l}=e,i=e0(te,r);return J(),(0,m.jsx)(m.Fragment,{children:(0,m.jsx)(D,{asChild:!0,loop:!0,trapped:n,onMountAutoFocus:o,onUnmountAutoFocus:a,children:(0,m.jsx)(g,{role:"dialog",id:i.contentId,"aria-describedby":i.descriptionPresent?i.descriptionId:void 0,"aria-labelledby":i.titlePresent?i.titleId:void 0,"data-state":ts(i.open),...l,ref:t,deferPointerDownOutside:!0,onDismiss:()=>i.onOpenChange(!1)})})})},"DialogContentImpl")),ta=l.forwardRef(eZ(function(e,t){let{__scopeDialog:r,...n}=e,o=e0("DialogTitle",r),{setTitleCount:a}=o;return(0,H.N)(()=>(a(e=>e+1),()=>a(e=>e-1)),[a]),(0,m.jsx)(f.sG.h2,{id:o.titleId,...n,ref:t})},"DialogTitle")),tl=l.forwardRef(eZ(function(e,t){let{__scopeDialog:r,...n}=e,o=e0("DialogDescription",r),{setDescriptionCount:a}=o;return(0,H.N)(()=>(a(e=>e+1),()=>a(e=>e-1)),[a]),(0,m.jsx)(f.sG.p,{id:o.descriptionId,...n,ref:t})},"DialogDescription")),ti=l.forwardRef(eZ(function(e,t){let{__scopeDialog:r,...n}=e,o=e0("DialogClose",r);return(0,m.jsx)(f.sG.button,{type:"button",...n,ref:t,onClick:(0,i.mK)(e.onClick,()=>o.onOpenChange(!1))})},"DialogClose"));function ts(e){return e?"open":"closed"}eZ(ts,"getState")},4381:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Hash",[["line",{x1:"4",x2:"20",y1:"9",y2:"9",key:"4lhtct"}],["line",{x1:"4",x2:"20",y1:"15",y2:"15",key:"vyu0kd"}],["line",{x1:"10",x2:"8",y1:"3",y2:"21",key:"1ggp8o"}],["line",{x1:"16",x2:"14",y1:"3",y2:"21",key:"weycgp"}]])},4446:(e,t,r)=>{r.d(t,{s:()=>s});var n=r(2115),o=Object.defineProperty,a=(e,t)=>o(e,"name",{value:t,configurable:!0});function l(e,t){if("function"==typeof e)return e(t);null!=e&&(e.current=t)}function i(...e){return t=>{let r=!1,n=e.map(e=>{let n=l(e,t);return r||"function"!=typeof n||(r=!0),n});if(r)return()=>{for(let t=0;t{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Gauge",[["path",{d:"m12 14 4-4",key:"9kzdfg"}],["path",{d:"M3.34 19a10 10 0 1 1 17.32 0",key:"19p75a"}]])},5148:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("ScrollText",[["path",{d:"M15 12h-5",key:"r7krc0"}],["path",{d:"M15 8h-5",key:"1khuty"}],["path",{d:"M19 17V5a2 2 0 0 0-2-2H4",key:"zz82l3"}],["path",{d:"M8 21h12a2 2 0 0 0 2-2v-1a1 1 0 0 0-1-1H11a1 1 0 0 0-1 1v1a2 2 0 1 1-4 0V5a2 2 0 1 0-4 0v2a1 1 0 0 0 1 1h3",key:"1ph1d7"}]])},5225:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("FileText",[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z",key:"1rqfz7"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4",key:"tnqrlb"}],["path",{d:"M10 9H8",key:"b1mrlr"}],["path",{d:"M16 13H8",key:"t4e002"}],["path",{d:"M16 17H8",key:"z1uh3a"}]])},5524:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("CodeXml",[["path",{d:"m18 16 4-4-4-4",key:"1inbqp"}],["path",{d:"m6 8-4 4 4 4",key:"15zrgr"}],["path",{d:"m14.5 4-5 16",key:"e7oirm"}]])},5544:(e,t,r)=>{r.d(t,{UC:()=>eS,B8:()=>eC,bL:()=>eE,l9:()=>eM});var n,o,a=r(2115),l=r.t(a,2),i=r(2556),s=r(3468);function c(e,t,r){if(!t.has(e))throw TypeError("attempted to "+r+" private field on non-instance");return t.get(e)}function u(e,t){var r=c(e,t,"get");return r.get?r.get.call(e):r.value}function d(e,t,r){var n=c(e,t,"set");if(n.set)n.set.call(e,r);else{if(!n.writable)throw TypeError("attempted to set read only private field");n.value=r}return r}var f=r(4446),p=r(2467),m=r(5155),h=Object.defineProperty,b=(e,t)=>h(e,"name",{value:t,configurable:!0});function y(e){let t=e+"CollectionProvider",[r,n]=(0,s.A)(t),[o,l]=r(t,{collectionRef:{current:null},itemMap:new Map}),i=b(e=>{let{scope:t,children:r}=e,n=a.useRef(null),l=a.useRef(new Map).current;return(0,m.jsx)(o,{scope:t,itemMap:l,collectionRef:n,children:r})},"CollectionProvider");i.displayName=t;let c=e+"CollectionSlot",u=(0,p.TL)(c),d=a.forwardRef((e,t)=>{let{scope:r,children:n}=e,o=l(c,r),a=(0,f.s)(t,o.collectionRef);return(0,m.jsx)(u,{ref:a,children:n})});d.displayName=c;let h=e+"CollectionItemSlot",y="data-radix-collection-item",v=(0,p.TL)(h),g=a.forwardRef((e,t)=>{let{scope:r,children:n,...o}=e,i=a.useRef(null),s=(0,f.s)(t,i),c=l(h,r);return a.useEffect(()=>(c.itemMap.set(i,{ref:i,...o}),()=>void c.itemMap.delete(i))),(0,m.jsx)(v,{...{[y]:""},ref:s,children:n})});function w(t){let r=l(e+"CollectionConsumer",t);return a.useCallback(()=>{let e=r.collectionRef.current;if(!e)return[];let t=Array.from(e.querySelectorAll("[".concat(y,"]")));return Array.from(r.itemMap.values()).sort((e,r)=>t.indexOf(e.ref.current)-t.indexOf(r.ref.current))},[r.collectionRef,r.itemMap])}return g.displayName=h,b(w,"useCollection"),[{Provider:i,Slot:d,ItemSlot:g},w,n]}b(y,"createCollection");var v=new WeakMap,g=(n=new WeakMap,b(o=class e extends Map{set(e,t){return v.get(this)&&(this.has(e)?u(this,n)[u(this,n).indexOf(e)]=e:u(this,n).push(e)),super.set(e,t),this}insert(e,t,r){let o,a=this.has(t),l=u(this,n).length,i=x(e),s=i>=0?i:l+i,c=s<0||s>=l?-1:s;if(c===this.size||a&&c===this.size-1||-1===c)return this.set(t,r),this;let d=this.size+ +!a;i<0&&s++;let f=[...u(this,n)],p=!1;for(let e=s;e=this.size&&(n=this.size-1),this.at(n)}keyFrom(e,t){let r=this.indexOf(e);if(-1===r)return;let n=r+t;return n<0&&(n=0),n>=this.size&&(n=this.size-1),this.keyAt(n)}find(e,t){let r=0;for(let n of this){if(Reflect.apply(e,t,[n,r,this]))return n;r++}}findIndex(e,t){let r=0;for(let n of this){if(Reflect.apply(e,t,[n,r,this]))return r;r++}return -1}filter(t,r){let n=[],o=0;for(let e of this)Reflect.apply(t,r,[e,o,this])&&n.push(e),o++;return new e(n)}map(t,r){let n=[],o=0;for(let e of this)n.push([e[0],Reflect.apply(t,r,[e,o,this])]),o++;return new e(n)}reduce(){for(var e=arguments.length,t=Array(e),r=0;r=0;e--){let r=this.at(e);a=e===this.size-1&&1===t.length?r:Reflect.apply(n,this,[a,r,e,this])}return a}toSorted(t){return new e([...this.entries()].sort(t))}toReversed(){let t=new e;for(let e=this.size-1;e>=0;e--){let r=this.keyAt(e),n=this.get(r);t.set(r,n)}return t}toSpliced(){for(var t=arguments.length,r=Array(t),n=0;n0&&(o=r-1);for(let e=t;e<=o;e++){let t=this.keyAt(e),r=this.get(t);n.set(t,r)}return n}every(e,t){let r=0;for(let n of this){if(!Reflect.apply(e,t,[n,r,this]))return!1;r++}return!0}some(e,t){let r=0;for(let n of this){if(Reflect.apply(e,t,[n,r,this]))return!0;r++}return!1}constructor(e){super(e),function(e,t,r){if(t.has(e))throw TypeError("Cannot initialize the same private elements twice on an object");t.set(e,r)}(this,n,{writable:!0,value:void 0}),d(this,n,[...super.keys()]),v.set(this,!0)}},"OrderedDict"),o);function w(e,t){if("at"in Array.prototype)return Array.prototype.at.call(e,t);let r=k(e,t);return -1===r?void 0:e[r]}function k(e,t){let r=e.length,n=x(t),o=n>=0?n:r+n;return o<0||o>=r?-1:o}function x(e){return e!=e||0===e?0:Math.trunc(e)}function A(e,t){if(e===t)return!0;if("object"!=typeof e||"object"!=typeof t||null==e||null==t)return!1;let r=Object.keys(e),n=Object.keys(t);if(r.length!==n.length)return!1;for(let n of r)if(!Object.prototype.hasOwnProperty.call(t,n)||e[n]!==t[n])return!1;return!0}function E(e,t){return!!(t.compareDocumentPosition(e)&Node.DOCUMENT_POSITION_PRECEDING)}function C(e,t){return e[1].element&&t[1].element?E(e[1].element,t[1].element)?-1:1:0}function M(e){return new MutationObserver(t=>{for(let r of t)if("childList"===r.type)return void e()})}b(w,"at"),b(k,"toSafeIndex"),b(x,"toSafeInteger"),b(function(e){let t=e+"CollectionProvider",[r,n]=(0,s.A)(t),[o,l]=r(t,{collectionElement:null,collectionRef:{current:null},collectionRefObject:{current:null},itemMap:new g,setItemMap:b(()=>void 0,"setItemMap")}),i=b(e=>{let{state:t,...r}=e;return t?(0,m.jsx)(u,{...r,state:t}):(0,m.jsx)(c,{...r})},"CollectionProvider");i.displayName=t;let c=b(e=>{let t=x();return(0,m.jsx)(u,{...e,state:t})},"CollectionInit");c.displayName=t+"Init";let u=b(e=>{let{scope:t,children:r,state:n}=e,l=a.useRef(null),[i,s]=a.useState(null),c=(0,f.s)(l,s),[u,d]=n;return a.useEffect(()=>{if(!i)return;let e=M(()=>{});return e.observe(i,{childList:!0,subtree:!0}),()=>{e.disconnect()}},[i]),(0,m.jsx)(o,{scope:t,itemMap:u,setItemMap:d,collectionRef:c,collectionRefObject:l,collectionElement:i,children:r})},"CollectionProviderImpl");u.displayName=t+"Impl";let d=e+"CollectionSlot",h=(0,p.TL)(d),y=a.forwardRef((e,t)=>{let{scope:r,children:n}=e,o=l(d,r),a=(0,f.s)(t,o.collectionRef);return(0,m.jsx)(h,{ref:a,children:n})});y.displayName=d;let v=e+"CollectionItemSlot",w=(0,p.TL)(v),k=a.forwardRef((e,t)=>{let{scope:r,children:n,...o}=e,i=a.useRef(null),[s,c]=a.useState(null),u=(0,f.s)(t,i,c),{setItemMap:d}=l(v,r),p=a.useRef(o);A(p.current,o)||(p.current=o);let h=p.current;return a.useEffect(()=>(d(e=>s?e.has(s)?e.set(s,{...h,element:s}).toSorted(C):(e.set(s,{...h,element:s}),e.toSorted(C)):e),()=>{d(e=>s&&e.has(s)?(e.delete(s),new g(e)):e)}),[s,h,d]),(0,m.jsx)(w,{"data-radix-collection-item":"",ref:u,children:n})});function x(){return a.useState(new g)}function E(t){let{itemMap:r}=l(e+"CollectionConsumer",t);return r}return k.displayName=v,b(x,"useInitCollection"),b(E,"useCollection"),[{Provider:i,Slot:y,ItemSlot:k},{createCollectionScope:n,useCollection:E,useInitCollection:x}]},"createCollection"),b(A,"shallowEqual"),b(E,"isElementPreceding"),b(C,"sortByDocumentPosition"),b(M,"getChildListObserver");var S=r(8946),R=r(7602),O=r(222),j=r(9478),D=Object.defineProperty,P=a.createContext(void 0);function z(e){let t=a.useContext(P);return e||t||"ltr"}D(z,"name",{value:"useDirection",configurable:!0});var N=r(4129),L=Object.defineProperty,I=(e,t)=>L(e,"name",{value:t,configurable:!0}),T=!1;function F(){let[e,t]=a.useState(T);return a.useEffect(()=>{T||(T=!0,t(!0))},[]),e}I(F,"useIsHydrated");var _=l[" useSyncExternalStore ".trim().toString()];function W(){return()=>{}}function G(){return _(W,()=>!0,()=>!1)}I(W,"subscribe"),I(G,"useIsHydratedModern");var K="function"==typeof _?G:F,V=Object.defineProperty,H=(e,t)=>V(e,"name",{value:t,configurable:!0}),B="rovingFocusGroup.onEntryFocus",q={bubbles:!1,cancelable:!0},$="RovingFocusGroup",[U,Z,X]=y($),[Y,J]=(0,s.A)($,[X]),[Q,ee]=Y($),et=a.forwardRef(H(function(e,t){return(0,m.jsx)(U.Provider,{scope:e.__scopeRovingFocusGroup,children:(0,m.jsx)(U.Slot,{scope:e.__scopeRovingFocusGroup,children:(0,m.jsx)(er,{...e,ref:t})})})},"RovingFocusGroup")),er=a.forwardRef(H(function(e,t){let{__scopeRovingFocusGroup:r,orientation:n,loop:o=!1,dir:l,currentTabStopId:s,defaultCurrentTabStopId:c,onCurrentTabStopIdChange:u,onEntryFocus:d,preventScrollOnEntryFocus:p=!1,...h}=e,b=a.useRef(null),y=(0,f.s)(t,b),v=z(l),[g,w]=(0,j.i)({prop:s,defaultProp:null!=c?c:null,onChange:u,caller:$}),[k,x]=a.useState(!1),A=(0,O.c)(d),E=Z(r),C=a.useRef(!1),[M,S]=a.useState(0);return a.useEffect(()=>{let e=b.current;if(e)return e.addEventListener(B,A),()=>e.removeEventListener(B,A)},[A]),(0,m.jsx)(Q,{scope:r,orientation:n,dir:v,loop:o,currentTabStopId:g,onItemFocus:a.useCallback(e=>w(e),[w]),onItemShiftTab:a.useCallback(()=>x(!0),[]),onFocusableItemAdd:a.useCallback(()=>S(e=>e+1),[]),onFocusableItemRemove:a.useCallback(()=>S(e=>e-1),[]),children:(0,m.jsx)(R.sG.div,{tabIndex:k||0===M?-1:0,"data-orientation":n,...h,ref:y,style:{outline:"none",...e.style},onMouseDown:(0,i.mK)(e.onMouseDown,()=>{C.current=!0}),onFocus:(0,i.mK)(e.onFocus,e=>{let t=!C.current;if(e.target===e.currentTarget&&t&&!k){let t=new CustomEvent(B,q);if(e.currentTarget.dispatchEvent(t),!t.defaultPrevented){let e=E().filter(e=>e.focusable);ei([e.find(e=>e.active),e.find(e=>e.id===g),...e].filter(Boolean).map(e=>e.ref.current),p)}}C.current=!1}),onBlur:(0,i.mK)(e.onBlur,()=>x(!1))})})},"RovingFocusGroupImpl")),en=a.forwardRef(H(function(e,t){let{__scopeRovingFocusGroup:r,focusable:n=!0,active:o=!1,tabStopId:l,children:s,...c}=e,u=(0,S.B)(),d=l||u,f=ee("RovingFocusGroupItem",r),p=f.currentTabStopId===d,h=Z(r),{onFocusableItemAdd:b,onFocusableItemRemove:y,currentTabStopId:v}=f,g=K();return(0,N.N)(()=>{if(g&&n)return b(),()=>y()},[g,n,b,y]),a.useEffect(()=>{if(!g&&n)return b(),()=>y()},[g,n,b,y]),(0,m.jsx)(U.ItemSlot,{scope:r,id:d,focusable:n,active:o,children:(0,m.jsx)(R.sG.span,{tabIndex:p?0:-1,"data-orientation":f.orientation,...c,ref:t,onMouseDown:(0,i.mK)(e.onMouseDown,e=>{n?f.onItemFocus(d):e.preventDefault()}),onFocus:(0,i.mK)(e.onFocus,()=>f.onItemFocus(d)),onKeyDown:(0,i.mK)(e.onKeyDown,e=>{if("Tab"===e.key&&e.shiftKey)return void f.onItemShiftTab();if(e.target!==e.currentTarget)return;let t=el(e,f.orientation,f.dir);if(void 0!==t){if(e.metaKey||e.ctrlKey||e.altKey||e.shiftKey)return;e.preventDefault();let r=h().filter(e=>e.focusable).map(e=>e.ref.current);if("last"===t)r.reverse();else if("prev"===t||"next"===t){"prev"===t&&r.reverse();let n=r.indexOf(e.currentTarget);r=f.loop?es(r,n+1):r.slice(n+1)}setTimeout(()=>ei(r))}}),children:"function"==typeof s?s({isCurrentTabStop:p,hasTabStop:null!=v}):s})})},"RovingFocusGroupItem")),eo={ArrowLeft:"prev",ArrowUp:"prev",ArrowRight:"next",ArrowDown:"next",PageUp:"first",Home:"first",PageDown:"last",End:"last"};function ea(e,t){return"rtl"!==t?e:"ArrowLeft"===e?"ArrowRight":"ArrowRight"===e?"ArrowLeft":e}function el(e,t,r){let n=ea(e.key,r);if(!("vertical"===t&&["ArrowLeft","ArrowRight"].includes(n))&&!("horizontal"===t&&["ArrowUp","ArrowDown"].includes(n)))return eo[n]}function ei(e){let t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],r=document.activeElement;for(let n of e)if(n===r||(n.focus({preventScroll:t}),document.activeElement!==r))return}function es(e,t){return e.map((r,n)=>e[(t+n)%e.length])}H(ea,"getDirectionAwareKey"),H(el,"getFocusIntent"),H(ei,"focusFirst"),H(es,"wrapArray");var ec=r(6842),eu=Object.defineProperty,ed=(e,t)=>eu(e,"name",{value:t,configurable:!0}),ef="Tabs",[ep,em]=(0,s.A)(ef,[J]),eh=J(),[eb,ey]=ep(ef),ev=a.forwardRef(ed(function(e,t){let{__scopeTabs:r,value:n,onValueChange:o,defaultValue:a,orientation:l="horizontal",dir:i,activationMode:s="automatic",...c}=e,u=z(i),[d,f]=(0,j.i)({prop:n,onChange:o,defaultProp:null!=a?a:"",caller:ef});return(0,m.jsx)(eb,{scope:r,baseId:(0,S.B)(),value:d,onValueChange:f,orientation:l,dir:u,activationMode:s,children:(0,m.jsx)(R.sG.div,{dir:u,"data-orientation":l,...c,ref:t})})},"Tabs")),eg=a.forwardRef(ed(function(e,t){let{__scopeTabs:r,loop:n=!0,...o}=e,a=ey("TabsList",r),l=eh(r);return(0,m.jsx)(et,{asChild:!0,...l,orientation:a.orientation,dir:a.dir,loop:n,children:(0,m.jsx)(R.sG.div,{role:"tablist","aria-orientation":a.orientation,...o,ref:t})})},"TabsList")),ew=a.forwardRef(ed(function(e,t){let{__scopeTabs:r,value:n,disabled:o=!1,...a}=e,l=ey("TabsTrigger",r),s=eh(r),c=ex(l.baseId,n),u=eA(l.baseId,n),d=n===l.value;return(0,m.jsx)(en,{asChild:!0,...s,focusable:!o,active:d,children:(0,m.jsx)(R.sG.button,{type:"button",role:"tab","aria-selected":d,"aria-controls":u,"data-state":d?"active":"inactive","data-disabled":o?"":void 0,disabled:o,id:c,...a,ref:t,onMouseDown:(0,i.mK)(e.onMouseDown,e=>{o||0!==e.button||!1!==e.ctrlKey?e.preventDefault():l.onValueChange(n)}),onKeyDown:(0,i.mK)(e.onKeyDown,e=>{!o&&e.target===e.currentTarget&&[" ","Enter"].includes(e.key)&&l.onValueChange(n)}),onFocus:(0,i.mK)(e.onFocus,()=>{let e="manual"!==l.activationMode;d||o||!e||l.onValueChange(n)})})})},"TabsTrigger")),ek=a.forwardRef(ed(function(e,t){let{__scopeTabs:r,value:n,forceMount:o,children:l,...i}=e,s=ey("TabsContent",r),c=ex(s.baseId,n),u=eA(s.baseId,n),d=n===s.value,f=a.useRef(d);return a.useEffect(()=>{let e=requestAnimationFrame(()=>f.current=!1);return()=>cancelAnimationFrame(e)},[]),(0,m.jsx)(ec.C,{present:o||d,children:r=>{let{present:n}=r;return(0,m.jsx)(R.sG.div,{"data-state":d?"active":"inactive","data-orientation":s.orientation,role:"tabpanel","aria-labelledby":c,hidden:!n,id:u,tabIndex:0,...i,ref:t,style:{...e.style,animationDuration:f.current?"0s":void 0},children:n&&l})}})},"TabsContent"));function ex(e,t){return"".concat(e,"-trigger-").concat(t)}function eA(e,t){return"".concat(e,"-content-").concat(t)}ed(ex,"makeTriggerId"),ed(eA,"makeContentId");var eE=ev,eC=eg,eM=ew,eS=ek},5646:(e,t,r)=>{r.d(t,{UC:()=>S,VY:()=>D,ZD:()=>O,ZL:()=>C,bL:()=>E,hE:()=>j,hJ:()=>M,rc:()=>R});var n=r(2115),o=r(3468),a=r(4446),l=r(4368),i=r(2556),s=r(5155),c=Object.defineProperty,u=(e,t)=>c(e,"name",{value:t,configurable:!0}),[d,f]=(0,o.A)("AlertDialog",[l.Hs]),p=(0,l.Hs)(),m=u(e=>{let{__scopeAlertDialog:t,...r}=e,n=p(t);return(0,s.jsx)(l.bL,{...n,...r,modal:!0})},"AlertDialog"),h=u(e=>{let{__scopeAlertDialog:t,...r}=e,n=p(t);return(0,s.jsx)(l.ZL,{...n,...r})},"AlertDialogPortal"),b=n.forwardRef(u(function(e,t){let{__scopeAlertDialog:r,...n}=e,o=p(r);return(0,s.jsx)(l.hJ,{...o,...n,ref:t})},"AlertDialogOverlay")),[y,v]=d("AlertDialogContent"),g=n.forwardRef(u(function(e,t){let{__scopeAlertDialog:r,children:o,...c}=e,u=p(r),d=n.useRef(null),f=(0,a.s)(t,d),m=n.useRef(null);return(0,s.jsx)(y,{scope:r,cancelRef:m,children:(0,s.jsx)(l.UC,{role:"alertdialog",...u,...c,ref:f,onOpenAutoFocus:(0,i.mK)(c.onOpenAutoFocus,e=>{var t;e.preventDefault(),null==(t=m.current)||t.focus({preventScroll:!0})}),onPointerDownOutside:e=>e.preventDefault(),onInteractOutside:e=>e.preventDefault(),children:o})})},"AlertDialogContent")),w=n.forwardRef(u(function(e,t){let{__scopeAlertDialog:r,...n}=e,o=p(r);return(0,s.jsx)(l.hE,{...o,...n,ref:t})},"AlertDialogTitle")),k=n.forwardRef(u(function(e,t){let{__scopeAlertDialog:r,...n}=e,o=p(r);return(0,s.jsx)(l.VY,{...o,...n,ref:t})},"AlertDialogDescription")),x=n.forwardRef(u(function(e,t){let{__scopeAlertDialog:r,...n}=e,o=p(r);return(0,s.jsx)(l.bm,{...o,...n,ref:t})},"AlertDialogAction")),A=n.forwardRef(u(function(e,t){let{__scopeAlertDialog:r,...n}=e,{cancelRef:o}=v("AlertDialogCancel",r),i=p(r),c=(0,a.s)(t,o);return(0,s.jsx)(l.bm,{...i,...n,ref:c})},"AlertDialogCancel")),E=m,C=h,M=b,S=g,R=x,O=A,j=w,D=k},5889:(e,t,r)=>{r.d(t,{QP:()=>ep});let n=(e=new Map,t=null,r)=>({nextPart:e,validators:t,classGroupId:r}),o=[],a=(e,t,r)=>{if(0==e.length-t)return r.classGroupId;let n=e[t],o=r.nextPart.get(n);if(o){let r=a(e,t+1,o);if(r)return r}let l=r.validators;if(null===l)return;let i=0===t?e.join("-"):e.slice(t).join("-"),s=l.length;for(let e=0;e{let r=n();for(let n in e)i(e[n],r,n,t);return r},i=(e,t,r,n)=>{let o=e.length;for(let a=0;a"string"==typeof e?void c(e,t,r):"function"==typeof e?void u(e,t,r,n):void d(e,t,r,n),c=(e,t,r)=>{(""===e?t:f(t,e)).classGroupId=r},u=(e,t,r,n)=>{if(p(e))return void i(e(n),t,r,n);null===t.validators&&(t.validators=[]),t.validators.push({classGroupId:r,validator:e})},d=(e,t,r,n)=>{let o=Object.entries(e),a=o.length;for(let e=0;e{let r=e,o=t.split("-"),a=o.length;for(let e=0;e"isThemeGetter"in e&&!0===e.isThemeGetter,m=[],h=(e,t,r,n,o)=>({modifiers:e,hasImportantModifier:t,baseClassName:r,maybePostfixModifierPosition:n,isExternal:o}),b=/\s+/,y=e=>{let t;if("string"==typeof e)return e;let r="";for(let n=0;n{let t=t=>t[e]||v;return t.isThemeGetter=!0,t.themeKey=e,t},w=/^\[(?:(\w[\w-]*):)?(.+)\]$/i,k=/^\((?:(\w[\w-]*):)?(.+)\)$/i,x=/^\d+(?:\.\d+)?\/\d+(?:\.\d+)?$/,A=/^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/,E=/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/,C=/^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix|color|light-dark)\(.+\)$/,M=/^(inset_)?-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/,S=/^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/,R=e=>x.test(e),O=e=>!!e&&!Number.isNaN(Number(e)),j=e=>!!e&&Number.isInteger(Number(e)),D=e=>e.endsWith("%")&&O(e.slice(0,-1)),P=e=>A.test(e),z=()=>!0,N=e=>E.test(e)&&!C.test(e),L=()=>!1,I=e=>M.test(e),T=e=>S.test(e),F=e=>!G(e)&&!Z(e),_=e=>e.startsWith("@container")&&("/"===e[10]&&void 0!==e[11]||"s"===e[11]&&void 0!==e[16]&&e.startsWith("-size/",10)||"n"===e[11]&&void 0!==e[18]&&e.startsWith("-normal/",10)),W=e=>en(e,ei,L),G=e=>w.test(e),K=e=>en(e,es,N),V=e=>en(e,ec,O),H=e=>en(e,ed,z),B=e=>en(e,eu,L),q=e=>en(e,ea,L),$=e=>en(e,el,T),U=e=>en(e,ef,I),Z=e=>k.test(e),X=e=>eo(e,es),Y=e=>eo(e,eu),J=e=>eo(e,ea),Q=e=>eo(e,ei),ee=e=>eo(e,el),et=e=>eo(e,ef,!0),er=e=>eo(e,ed,!0),en=(e,t,r)=>{let n=w.exec(e);return!!n&&(n[1]?t(n[1]):r(n[2]))},eo=(e,t,r=!1)=>{let n=k.exec(e);return!!n&&(n[1]?t(n[1]):r)},ea=e=>"position"===e||"percentage"===e,el=e=>"image"===e||"url"===e,ei=e=>"length"===e||"size"===e||"bg-size"===e,es=e=>"length"===e,ec=e=>"number"===e,eu=e=>"family-name"===e,ed=e=>"number"===e||"weight"===e,ef=e=>"shadow"===e;Symbol.toStringTag;let ep=((e,...t)=>{let r,n,i,s,c=e=>{let t=n(e);if(t)return t;let o=((e,t)=>{let{parseClassName:r,getClassGroupId:n,getConflictingClassGroupIds:o,sortModifiers:a,postfixLookupClassGroupIds:l}=t,i=[],s=e.trim().split(b),c="";for(let e=s.length-1;e>=0;e-=1){let t,u=s[e],{isExternal:d,modifiers:f,hasImportantModifier:p,baseClassName:m,maybePostfixModifierPosition:h}=r(u);if(d){c=u+(c.length>0?" "+c:c);continue}let b=!!h;if(b){let e=(t=n(m.substring(0,h)))&&l[t]?n(m):void 0;e&&e!==t&&(t=e,b=!1)}else t=n(m);if(!t){if(!b||!(t=n(m))){c=u+(c.length>0?" "+c:c);continue}b=!1}let y=0===f.length?"":1===f.length?f[0]:a(f).join(":"),v=p?y+"!":y,g=v+t;if(i.indexOf(g)>-1)continue;i.push(g);let w=o(t,b);for(let e=0;e0?" "+c:c)}return c})(e,r);return i(e,o),o};return s=u=>(n=(r=(e=>({cache:(e=>{if(e<1)return{get:()=>void 0,set:()=>{}};let t=0,r=Object.create(null),n=Object.create(null),o=(o,a)=>{r[o]=a,++t>e&&(t=0,n=r,r=Object.create(null))};return{get(e){let t=r[e];return void 0!==t?t:void 0!==(t=n[e])?(o(e,t),t):void 0},set(e,t){e in r?r[e]=t:o(e,t)}}})(e.cacheSize),parseClassName:(e=>{let{prefix:t,experimentalParseClassName:r}=e,n=e=>{let t,r=[],n=0,o=0,a=0,l=e.length;for(let i=0;ia?t-a:void 0)};if(t){let e=t+":",r=n;n=t=>t.startsWith(e)?r(t.slice(e.length)):h(m,!1,t,void 0,!0)}if(r){let e=n;n=t=>r({className:t,parseClassName:e})}return n})(e),sortModifiers:(e=>{let t=new Map;return e.orderSensitiveModifiers.forEach((e,r)=>{t.set(e,1e6+r)}),e=>{let r=[],n=[];for(let o=0;o0&&(n.sort(),r.push(...n),n=[]),r.push(a)):n.push(a)}return n.length>0&&(n.sort(),r.push(...n)),r}})(e),postfixLookupClassGroupIds:(e=>{let t=Object.create(null),r=e.postfixLookupClassGroups;if(r)for(let e=0;e{let t=(e=>{let{theme:t,classGroups:r}=e;return l(r,t)})(e),{conflictingClassGroups:r,conflictingClassGroupModifiers:n}=e;return{getClassGroupId:e=>{if(e.startsWith("[")&&e.endsWith("]"))return(e=>-1===e.slice(1,-1).indexOf(":")?void 0:(()=>{let t=e.slice(1,-1),r=t.indexOf(":"),n=t.slice(0,r);return n?"arbitrary.."+n:void 0})())(e);let r=e.split("-"),n=+(""===r[0]&&r.length>1);return a(r,n,t)},getConflictingClassGroupIds:(e,t)=>{if(t){let t=n[e],a=r[e];return t?a?((e,t)=>{let r=Array(e.length+t.length);for(let t=0;tt(e),e()))).cache.get,i=r.cache.set,s=c,c(u)),(...e)=>s(((...e)=>{let t,r,n=0,o="";for(;n{let e=g("color"),t=g("font"),r=g("text"),n=g("font-weight"),o=g("tracking"),a=g("leading"),l=g("breakpoint"),i=g("container"),s=g("spacing"),c=g("radius"),u=g("shadow"),d=g("inset-shadow"),f=g("text-shadow"),p=g("drop-shadow"),m=g("blur"),h=g("perspective"),b=g("aspect"),y=g("ease"),v=g("animate"),w=()=>["auto","avoid","all","avoid-page","page","left","right","column"],k=()=>["center","top","bottom","left","right","top-left","left-top","top-right","right-top","bottom-right","right-bottom","bottom-left","left-bottom"],x=()=>[...k(),Z,G],A=()=>["auto","hidden","clip","visible","scroll"],E=()=>["auto","contain","none"],C=()=>[Z,G,s],M=()=>[R,"full","auto",...C()],S=()=>[j,"none","subgrid",Z,G],N=()=>["auto",{span:["full",j,Z,G]},j,Z,G],L=()=>[j,"auto",Z,G],I=()=>["auto","min","max","fr",Z,G],T=()=>["start","end","center","between","around","evenly","stretch","baseline","center-safe","end-safe"],en=()=>["start","end","center","stretch","center-safe","end-safe"],eo=()=>["auto",...C()],ea=()=>[R,"auto","full","dvw","dvh","lvw","lvh","svw","svh","min","max","fit",...C()],el=()=>[i,R,"screen","full","dvw","lvw","svw","min","max","fit",...C()],ei=()=>[R,"screen","full","lh","dvh","lvh","svh","min","max","fit",...C()],es=()=>[e,Z,G],ec=()=>[...k(),J,q,{position:[Z,G]}],eu=()=>["no-repeat",{repeat:["","x","y","space","round"]}],ed=()=>["auto","cover","contain",Q,W,{size:[Z,G]}],ef=()=>[D,X,K],ep=()=>["","none","full",c,Z,G],em=()=>["",O,X,K],eh=()=>["solid","dashed","dotted","double"],eb=()=>["normal","multiply","screen","overlay","darken","lighten","color-dodge","color-burn","hard-light","soft-light","difference","exclusion","hue","saturation","color","luminosity"],ey=()=>[O,D,J,q],ev=()=>["","none",m,Z,G],eg=()=>["none",O,Z,G],ew=()=>["none",O,Z,G],ek=()=>[O,Z,G],ex=()=>[R,"full",...C()];return{cacheSize:500,theme:{animate:["spin","ping","pulse","bounce"],aspect:["video"],blur:[P],breakpoint:[P],color:[z],container:[P],"drop-shadow":[P],ease:["in","out","in-out"],font:[F],"font-weight":["thin","extralight","light","normal","medium","semibold","bold","extrabold","black"],"inset-shadow":[P],leading:["none","tight","snug","normal","relaxed","loose"],perspective:["dramatic","near","normal","midrange","distant","none"],radius:[P],shadow:[P],spacing:["px",O],text:[P],"text-shadow":[P],tracking:["tighter","tight","normal","wide","wider","widest"]},classGroups:{aspect:[{aspect:["auto","square",R,G,Z,b]}],container:["container"],"container-type":[{"@container":["","normal","size",Z,G]}],"container-named":[_],columns:[{columns:[O,"auto",G,Z,i]}],"break-after":[{"break-after":w()}],"break-before":[{"break-before":w()}],"break-inside":[{"break-inside":["auto","avoid","avoid-page","avoid-column"]}],"box-decoration":[{"box-decoration":["slice","clone"]}],box:[{box:["border","content"]}],display:["block","inline-block","inline","flex","inline-flex","table","inline-table","table-caption","table-cell","table-column","table-column-group","table-footer-group","table-header-group","table-row-group","table-row","flow-root","grid","inline-grid","contents","list-item","hidden"],sr:["sr-only","not-sr-only"],float:[{float:["right","left","none","start","end"]}],clear:[{clear:["left","right","both","none","start","end"]}],isolation:["isolate","isolation-auto"],"object-fit":[{object:["contain","cover","fill","none","scale-down"]}],"object-position":[{object:x()}],overflow:[{overflow:A()}],"overflow-x":[{"overflow-x":A()}],"overflow-y":[{"overflow-y":A()}],overscroll:[{overscroll:E()}],"overscroll-x":[{"overscroll-x":E()}],"overscroll-y":[{"overscroll-y":E()}],position:["static","fixed","absolute","relative","sticky"],inset:[{inset:M()}],"inset-x":[{"inset-x":M()}],"inset-y":[{"inset-y":M()}],start:[{"inset-s":M(),start:M()}],end:[{"inset-e":M(),end:M()}],"inset-bs":[{"inset-bs":M()}],"inset-be":[{"inset-be":M()}],top:[{top:M()}],right:[{right:M()}],bottom:[{bottom:M()}],left:[{left:M()}],visibility:["visible","invisible","collapse"],z:[{z:[j,"auto",Z,G]}],basis:[{basis:[R,"full","auto",i,...C()]}],"flex-direction":[{flex:["row","row-reverse","col","col-reverse"]}],"flex-wrap":[{flex:["nowrap","wrap","wrap-reverse"]}],flex:[{flex:[O,R,"auto","initial","none",G]}],grow:[{grow:["",O,Z,G]}],shrink:[{shrink:["",O,Z,G]}],order:[{order:[j,"first","last","none",Z,G]}],"grid-cols":[{"grid-cols":S()}],"col-start-end":[{col:N()}],"col-start":[{"col-start":L()}],"col-end":[{"col-end":L()}],"grid-rows":[{"grid-rows":S()}],"row-start-end":[{row:N()}],"row-start":[{"row-start":L()}],"row-end":[{"row-end":L()}],"grid-flow":[{"grid-flow":["row","col","dense","row-dense","col-dense"]}],"auto-cols":[{"auto-cols":I()}],"auto-rows":[{"auto-rows":I()}],gap:[{gap:C()}],"gap-x":[{"gap-x":C()}],"gap-y":[{"gap-y":C()}],"justify-content":[{justify:[...T(),"normal"]}],"justify-items":[{"justify-items":[...en(),"normal"]}],"justify-self":[{"justify-self":["auto",...en()]}],"align-content":[{content:["normal",...T()]}],"align-items":[{items:[...en(),{baseline:["","last"]}]}],"align-self":[{self:["auto",...en(),{baseline:["","last"]}]}],"place-content":[{"place-content":T()}],"place-items":[{"place-items":[...en(),"baseline"]}],"place-self":[{"place-self":["auto",...en()]}],p:[{p:C()}],px:[{px:C()}],py:[{py:C()}],ps:[{ps:C()}],pe:[{pe:C()}],pbs:[{pbs:C()}],pbe:[{pbe:C()}],pt:[{pt:C()}],pr:[{pr:C()}],pb:[{pb:C()}],pl:[{pl:C()}],m:[{m:eo()}],mx:[{mx:eo()}],my:[{my:eo()}],ms:[{ms:eo()}],me:[{me:eo()}],mbs:[{mbs:eo()}],mbe:[{mbe:eo()}],mt:[{mt:eo()}],mr:[{mr:eo()}],mb:[{mb:eo()}],ml:[{ml:eo()}],"space-x":[{"space-x":C()}],"space-x-reverse":["space-x-reverse"],"space-y":[{"space-y":C()}],"space-y-reverse":["space-y-reverse"],size:[{size:ea()}],"inline-size":[{inline:["auto",...el()]}],"min-inline-size":[{"min-inline":["auto",...el()]}],"max-inline-size":[{"max-inline":["none",...el()]}],"block-size":[{block:["auto",...ei()]}],"min-block-size":[{"min-block":["auto",...ei()]}],"max-block-size":[{"max-block":["none",...ei()]}],w:[{w:[i,"screen",...ea()]}],"min-w":[{"min-w":[i,"screen","none",...ea()]}],"max-w":[{"max-w":[i,"screen","none","prose",{screen:[l]},...ea()]}],h:[{h:["screen","lh",...ea()]}],"min-h":[{"min-h":["screen","lh","none",...ea()]}],"max-h":[{"max-h":["screen","lh","none",...ea()]}],"font-size":[{text:["base",r,X,K]}],"font-smoothing":["antialiased","subpixel-antialiased"],"font-style":["italic","not-italic"],"font-weight":[{font:[n,er,H]}],"font-stretch":[{"font-stretch":["ultra-condensed","extra-condensed","condensed","semi-condensed","normal","semi-expanded","expanded","extra-expanded","ultra-expanded",D,G]}],"font-family":[{font:[Y,B,t]}],"font-features":[{"font-features":[G]}],"fvn-normal":["normal-nums"],"fvn-ordinal":["ordinal"],"fvn-slashed-zero":["slashed-zero"],"fvn-figure":["lining-nums","oldstyle-nums"],"fvn-spacing":["proportional-nums","tabular-nums"],"fvn-fraction":["diagonal-fractions","stacked-fractions"],tracking:[{tracking:[o,Z,G]}],"line-clamp":[{"line-clamp":[O,"none",Z,V]}],leading:[{leading:["none",a,...C()]}],"list-image":[{"list-image":["none",Z,G]}],"list-style-position":[{list:["inside","outside"]}],"list-style-type":[{list:["disc","decimal","none",Z,G]}],"text-alignment":[{text:["left","center","right","justify","start","end"]}],"placeholder-color":[{placeholder:es()}],"text-color":[{text:es()}],"text-decoration":["underline","overline","line-through","no-underline"],"text-decoration-style":[{decoration:[...eh(),"wavy"]}],"text-decoration-thickness":[{decoration:[O,"from-font","auto",Z,K]}],"text-decoration-color":[{decoration:es()}],"underline-offset":[{"underline-offset":[O,"auto",Z,G]}],"text-transform":["uppercase","lowercase","capitalize","normal-case"],"text-overflow":["truncate","text-ellipsis","text-clip"],"text-wrap":[{text:["wrap","nowrap","balance","pretty"]}],indent:[{indent:C()}],"tab-size":[{tab:[j,Z,G]}],"vertical-align":[{align:["baseline","top","middle","bottom","text-top","text-bottom","sub","super",Z,G]}],whitespace:[{whitespace:["normal","nowrap","pre","pre-line","pre-wrap","break-spaces"]}],break:[{break:["normal","words","all","keep"]}],wrap:[{wrap:["break-word","anywhere","normal"]}],hyphens:[{hyphens:["none","manual","auto"]}],content:[{content:["none",Z,G]}],"bg-attachment":[{bg:["fixed","local","scroll"]}],"bg-clip":[{"bg-clip":["border","padding","content","text"]}],"bg-origin":[{"bg-origin":["border","padding","content"]}],"bg-position":[{bg:ec()}],"bg-repeat":[{bg:eu()}],"bg-size":[{bg:ed()}],"bg-image":[{bg:["none",{linear:[{to:["t","tr","r","br","b","bl","l","tl"]},j,Z,G],radial:["",Z,G],conic:["",j,Z,G]},ee,$]}],"bg-color":[{bg:es()}],"gradient-from-pos":[{from:ef()}],"gradient-via-pos":[{via:ef()}],"gradient-to-pos":[{to:ef()}],"gradient-from":[{from:es()}],"gradient-via":[{via:es()}],"gradient-to":[{to:es()}],rounded:[{rounded:ep()}],"rounded-s":[{"rounded-s":ep()}],"rounded-e":[{"rounded-e":ep()}],"rounded-t":[{"rounded-t":ep()}],"rounded-r":[{"rounded-r":ep()}],"rounded-b":[{"rounded-b":ep()}],"rounded-l":[{"rounded-l":ep()}],"rounded-ss":[{"rounded-ss":ep()}],"rounded-se":[{"rounded-se":ep()}],"rounded-ee":[{"rounded-ee":ep()}],"rounded-es":[{"rounded-es":ep()}],"rounded-tl":[{"rounded-tl":ep()}],"rounded-tr":[{"rounded-tr":ep()}],"rounded-br":[{"rounded-br":ep()}],"rounded-bl":[{"rounded-bl":ep()}],"border-w":[{border:em()}],"border-w-x":[{"border-x":em()}],"border-w-y":[{"border-y":em()}],"border-w-s":[{"border-s":em()}],"border-w-e":[{"border-e":em()}],"border-w-bs":[{"border-bs":em()}],"border-w-be":[{"border-be":em()}],"border-w-t":[{"border-t":em()}],"border-w-r":[{"border-r":em()}],"border-w-b":[{"border-b":em()}],"border-w-l":[{"border-l":em()}],"divide-x":[{"divide-x":em()}],"divide-x-reverse":["divide-x-reverse"],"divide-y":[{"divide-y":em()}],"divide-y-reverse":["divide-y-reverse"],"border-style":[{border:[...eh(),"hidden","none"]}],"divide-style":[{divide:[...eh(),"hidden","none"]}],"border-color":[{border:es()}],"border-color-x":[{"border-x":es()}],"border-color-y":[{"border-y":es()}],"border-color-s":[{"border-s":es()}],"border-color-e":[{"border-e":es()}],"border-color-bs":[{"border-bs":es()}],"border-color-be":[{"border-be":es()}],"border-color-t":[{"border-t":es()}],"border-color-r":[{"border-r":es()}],"border-color-b":[{"border-b":es()}],"border-color-l":[{"border-l":es()}],"divide-color":[{divide:es()}],"outline-style":[{outline:[...eh(),"none","hidden"]}],"outline-offset":[{"outline-offset":[O,Z,G]}],"outline-w":[{outline:["",O,X,K]}],"outline-color":[{outline:es()}],shadow:[{shadow:["","inner","none",u,et,U]}],"shadow-color":[{shadow:es()}],"inset-shadow":[{"inset-shadow":["none",d,et,U]}],"inset-shadow-color":[{"inset-shadow":es()}],"ring-w":[{ring:em()}],"ring-w-inset":["ring-inset"],"ring-color":[{ring:es()}],"ring-offset-w":[{"ring-offset":[O,K]}],"ring-offset-color":[{"ring-offset":es()}],"inset-ring-w":[{"inset-ring":em()}],"inset-ring-color":[{"inset-ring":es()}],"text-shadow":[{"text-shadow":["none",f,et,U]}],"text-shadow-color":[{"text-shadow":es()}],opacity:[{opacity:[O,Z,G]}],"mix-blend":[{"mix-blend":[...eb(),"plus-darker","plus-lighter"]}],"bg-blend":[{"bg-blend":eb()}],"mask-clip":[{"mask-clip":["border","padding","content","fill","stroke","view"]},"mask-no-clip"],"mask-composite":[{mask:["add","subtract","intersect","exclude"]}],"mask-image-linear-pos":[{"mask-linear":[O]}],"mask-image-linear-from-pos":[{"mask-linear-from":ey()}],"mask-image-linear-to-pos":[{"mask-linear-to":ey()}],"mask-image-linear-from-color":[{"mask-linear-from":es()}],"mask-image-linear-to-color":[{"mask-linear-to":es()}],"mask-image-t-from-pos":[{"mask-t-from":ey()}],"mask-image-t-to-pos":[{"mask-t-to":ey()}],"mask-image-t-from-color":[{"mask-t-from":es()}],"mask-image-t-to-color":[{"mask-t-to":es()}],"mask-image-r-from-pos":[{"mask-r-from":ey()}],"mask-image-r-to-pos":[{"mask-r-to":ey()}],"mask-image-r-from-color":[{"mask-r-from":es()}],"mask-image-r-to-color":[{"mask-r-to":es()}],"mask-image-b-from-pos":[{"mask-b-from":ey()}],"mask-image-b-to-pos":[{"mask-b-to":ey()}],"mask-image-b-from-color":[{"mask-b-from":es()}],"mask-image-b-to-color":[{"mask-b-to":es()}],"mask-image-l-from-pos":[{"mask-l-from":ey()}],"mask-image-l-to-pos":[{"mask-l-to":ey()}],"mask-image-l-from-color":[{"mask-l-from":es()}],"mask-image-l-to-color":[{"mask-l-to":es()}],"mask-image-x-from-pos":[{"mask-x-from":ey()}],"mask-image-x-to-pos":[{"mask-x-to":ey()}],"mask-image-x-from-color":[{"mask-x-from":es()}],"mask-image-x-to-color":[{"mask-x-to":es()}],"mask-image-y-from-pos":[{"mask-y-from":ey()}],"mask-image-y-to-pos":[{"mask-y-to":ey()}],"mask-image-y-from-color":[{"mask-y-from":es()}],"mask-image-y-to-color":[{"mask-y-to":es()}],"mask-image-radial":[{"mask-radial":[Z,G]}],"mask-image-radial-from-pos":[{"mask-radial-from":ey()}],"mask-image-radial-to-pos":[{"mask-radial-to":ey()}],"mask-image-radial-from-color":[{"mask-radial-from":es()}],"mask-image-radial-to-color":[{"mask-radial-to":es()}],"mask-image-radial-shape":[{"mask-radial":["circle","ellipse"]}],"mask-image-radial-size":[{"mask-radial":[{closest:["side","corner"],farthest:["side","corner"]}]}],"mask-image-radial-pos":[{"mask-radial-at":k()}],"mask-image-conic-pos":[{"mask-conic":[O]}],"mask-image-conic-from-pos":[{"mask-conic-from":ey()}],"mask-image-conic-to-pos":[{"mask-conic-to":ey()}],"mask-image-conic-from-color":[{"mask-conic-from":es()}],"mask-image-conic-to-color":[{"mask-conic-to":es()}],"mask-mode":[{mask:["alpha","luminance","match"]}],"mask-origin":[{"mask-origin":["border","padding","content","fill","stroke","view"]}],"mask-position":[{mask:ec()}],"mask-repeat":[{mask:eu()}],"mask-size":[{mask:ed()}],"mask-type":[{"mask-type":["alpha","luminance"]}],"mask-image":[{mask:["none",Z,G]}],filter:[{filter:["","none",Z,G]}],blur:[{blur:ev()}],brightness:[{brightness:[O,Z,G]}],contrast:[{contrast:[O,Z,G]}],"drop-shadow":[{"drop-shadow":["","none",p,et,U]}],"drop-shadow-color":[{"drop-shadow":es()}],grayscale:[{grayscale:["",O,Z,G]}],"hue-rotate":[{"hue-rotate":[O,Z,G]}],invert:[{invert:["",O,Z,G]}],saturate:[{saturate:[O,Z,G]}],sepia:[{sepia:["",O,Z,G]}],"backdrop-filter":[{"backdrop-filter":["","none",Z,G]}],"backdrop-blur":[{"backdrop-blur":ev()}],"backdrop-brightness":[{"backdrop-brightness":[O,Z,G]}],"backdrop-contrast":[{"backdrop-contrast":[O,Z,G]}],"backdrop-grayscale":[{"backdrop-grayscale":["",O,Z,G]}],"backdrop-hue-rotate":[{"backdrop-hue-rotate":[O,Z,G]}],"backdrop-invert":[{"backdrop-invert":["",O,Z,G]}],"backdrop-opacity":[{"backdrop-opacity":[O,Z,G]}],"backdrop-saturate":[{"backdrop-saturate":[O,Z,G]}],"backdrop-sepia":[{"backdrop-sepia":["",O,Z,G]}],"border-collapse":[{border:["collapse","separate"]}],"border-spacing":[{"border-spacing":C()}],"border-spacing-x":[{"border-spacing-x":C()}],"border-spacing-y":[{"border-spacing-y":C()}],"table-layout":[{table:["auto","fixed"]}],caption:[{caption:["top","bottom"]}],transition:[{transition:["","all","colors","opacity","shadow","transform","none",Z,G]}],"transition-behavior":[{transition:["normal","discrete"]}],duration:[{duration:[O,"initial",Z,G]}],ease:[{ease:["linear","initial",y,Z,G]}],delay:[{delay:[O,Z,G]}],animate:[{animate:["none",v,Z,G]}],backface:[{backface:["hidden","visible"]}],perspective:[{perspective:[h,Z,G]}],"perspective-origin":[{"perspective-origin":x()}],rotate:[{rotate:eg()}],"rotate-x":[{"rotate-x":eg()}],"rotate-y":[{"rotate-y":eg()}],"rotate-z":[{"rotate-z":eg()}],scale:[{scale:ew()}],"scale-x":[{"scale-x":ew()}],"scale-y":[{"scale-y":ew()}],"scale-z":[{"scale-z":ew()}],"scale-3d":["scale-3d"],skew:[{skew:ek()}],"skew-x":[{"skew-x":ek()}],"skew-y":[{"skew-y":ek()}],transform:[{transform:[Z,G,"","none","gpu","cpu"]}],"transform-origin":[{origin:x()}],"transform-style":[{transform:["3d","flat"]}],translate:[{translate:ex()}],"translate-x":[{"translate-x":ex()}],"translate-y":[{"translate-y":ex()}],"translate-z":[{"translate-z":ex()}],"translate-none":["translate-none"],zoom:[{zoom:[j,Z,G]}],accent:[{accent:es()}],appearance:[{appearance:["none","auto"]}],"caret-color":[{caret:es()}],"color-scheme":[{scheme:["normal","dark","light","light-dark","only-dark","only-light"]}],cursor:[{cursor:["auto","default","pointer","wait","text","move","help","not-allowed","none","context-menu","progress","cell","crosshair","vertical-text","alias","copy","no-drop","grab","grabbing","all-scroll","col-resize","row-resize","n-resize","e-resize","s-resize","w-resize","ne-resize","nw-resize","se-resize","sw-resize","ew-resize","ns-resize","nesw-resize","nwse-resize","zoom-in","zoom-out",Z,G]}],"field-sizing":[{"field-sizing":["fixed","content"]}],"pointer-events":[{"pointer-events":["auto","none"]}],resize:[{resize:["none","","y","x"]}],"scroll-behavior":[{scroll:["auto","smooth"]}],"scrollbar-thumb-color":[{"scrollbar-thumb":es()}],"scrollbar-track-color":[{"scrollbar-track":es()}],"scrollbar-gutter":[{"scrollbar-gutter":["auto","stable","both"]}],"scrollbar-w":[{scrollbar:["auto","thin","none"]}],"scroll-m":[{"scroll-m":C()}],"scroll-mx":[{"scroll-mx":C()}],"scroll-my":[{"scroll-my":C()}],"scroll-ms":[{"scroll-ms":C()}],"scroll-me":[{"scroll-me":C()}],"scroll-mbs":[{"scroll-mbs":C()}],"scroll-mbe":[{"scroll-mbe":C()}],"scroll-mt":[{"scroll-mt":C()}],"scroll-mr":[{"scroll-mr":C()}],"scroll-mb":[{"scroll-mb":C()}],"scroll-ml":[{"scroll-ml":C()}],"scroll-p":[{"scroll-p":C()}],"scroll-px":[{"scroll-px":C()}],"scroll-py":[{"scroll-py":C()}],"scroll-ps":[{"scroll-ps":C()}],"scroll-pe":[{"scroll-pe":C()}],"scroll-pbs":[{"scroll-pbs":C()}],"scroll-pbe":[{"scroll-pbe":C()}],"scroll-pt":[{"scroll-pt":C()}],"scroll-pr":[{"scroll-pr":C()}],"scroll-pb":[{"scroll-pb":C()}],"scroll-pl":[{"scroll-pl":C()}],"snap-align":[{snap:["start","end","center","align-none"]}],"snap-stop":[{snap:["normal","always"]}],"snap-type":[{snap:["none","x","y","both"]}],"snap-strictness":[{snap:["mandatory","proximity"]}],touch:[{touch:["auto","none","manipulation"]}],"touch-x":[{"touch-pan":["x","left","right"]}],"touch-y":[{"touch-pan":["y","up","down"]}],"touch-pz":["touch-pinch-zoom"],select:[{select:["none","text","all","auto"]}],"will-change":[{"will-change":["auto","scroll","contents","transform",Z,G]}],fill:[{fill:["none",...es()]}],"stroke-w":[{stroke:[O,X,K,V]}],stroke:[{stroke:["none",...es()]}],"forced-color-adjust":[{"forced-color-adjust":["auto","none"]}]},conflictingClassGroups:{"container-named":["container-type"],overflow:["overflow-x","overflow-y"],overscroll:["overscroll-x","overscroll-y"],inset:["inset-x","inset-y","inset-bs","inset-be","start","end","top","right","bottom","left"],"inset-x":["start","end","right","left"],"inset-y":["inset-bs","inset-be","top","bottom"],flex:["basis","grow","shrink"],gap:["gap-x","gap-y"],p:["px","py","ps","pe","pbs","pbe","pt","pr","pb","pl"],px:["ps","pe","pr","pl"],py:["pbs","pbe","pt","pb"],m:["mx","my","ms","me","mbs","mbe","mt","mr","mb","ml"],mx:["ms","me","mr","ml"],my:["mbs","mbe","mt","mb"],size:["w","h"],"font-size":["leading"],"fvn-normal":["fvn-ordinal","fvn-slashed-zero","fvn-figure","fvn-spacing","fvn-fraction"],"fvn-ordinal":["fvn-normal"],"fvn-slashed-zero":["fvn-normal"],"fvn-figure":["fvn-normal"],"fvn-spacing":["fvn-normal"],"fvn-fraction":["fvn-normal"],"line-clamp":["display","overflow"],rounded:["rounded-s","rounded-e","rounded-t","rounded-r","rounded-b","rounded-l","rounded-ss","rounded-se","rounded-ee","rounded-es","rounded-tl","rounded-tr","rounded-br","rounded-bl"],"rounded-s":["rounded-ss","rounded-es"],"rounded-e":["rounded-se","rounded-ee"],"rounded-t":["rounded-tl","rounded-tr"],"rounded-r":["rounded-tr","rounded-br"],"rounded-b":["rounded-br","rounded-bl"],"rounded-l":["rounded-tl","rounded-bl"],"border-spacing":["border-spacing-x","border-spacing-y"],"border-w":["border-w-x","border-w-y","border-w-s","border-w-e","border-w-bs","border-w-be","border-w-t","border-w-r","border-w-b","border-w-l"],"border-w-x":["border-w-s","border-w-e","border-w-r","border-w-l"],"border-w-y":["border-w-bs","border-w-be","border-w-t","border-w-b"],"border-color":["border-color-x","border-color-y","border-color-s","border-color-e","border-color-bs","border-color-be","border-color-t","border-color-r","border-color-b","border-color-l"],"border-color-x":["border-color-s","border-color-e","border-color-r","border-color-l"],"border-color-y":["border-color-bs","border-color-be","border-color-t","border-color-b"],translate:["translate-x","translate-y","translate-none"],"translate-none":["translate","translate-x","translate-y","translate-z"],"scroll-m":["scroll-mx","scroll-my","scroll-ms","scroll-me","scroll-mbs","scroll-mbe","scroll-mt","scroll-mr","scroll-mb","scroll-ml"],"scroll-mx":["scroll-ms","scroll-me","scroll-mr","scroll-ml"],"scroll-my":["scroll-mbs","scroll-mbe","scroll-mt","scroll-mb"],"scroll-p":["scroll-px","scroll-py","scroll-ps","scroll-pe","scroll-pbs","scroll-pbe","scroll-pt","scroll-pr","scroll-pb","scroll-pl"],"scroll-px":["scroll-ps","scroll-pe","scroll-pr","scroll-pl"],"scroll-py":["scroll-pbs","scroll-pbe","scroll-pt","scroll-pb"],touch:["touch-x","touch-y","touch-pz"],"touch-x":["touch"],"touch-y":["touch"],"touch-pz":["touch"]},conflictingClassGroupModifiers:{"font-size":["leading"]},postfixLookupClassGroups:["container-type"],orderSensitiveModifiers:["*","**","after","backdrop","before","details-content","file","first-letter","first-line","marker","placeholder","selection"]}})},5958:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Link2",[["path",{d:"M9 17H7A5 5 0 0 1 7 7h2",key:"8i5ue5"}],["path",{d:"M15 7h2a5 5 0 1 1 0 10h-2",key:"1b9ql8"}],["line",{x1:"8",x2:"16",y1:"12",y2:"12",key:"1jonct"}]])},5989:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Sun",[["circle",{cx:"12",cy:"12",r:"4",key:"4exip2"}],["path",{d:"M12 2v2",key:"tus03m"}],["path",{d:"M12 20v2",key:"1lh1kg"}],["path",{d:"m4.93 4.93 1.41 1.41",key:"149t6j"}],["path",{d:"m17.66 17.66 1.41 1.41",key:"ptbguv"}],["path",{d:"M2 12h2",key:"1t8f8n"}],["path",{d:"M20 12h2",key:"1q8mjw"}],["path",{d:"m6.34 17.66-1.41 1.41",key:"1m8zz5"}],["path",{d:"m19.07 4.93-1.41 1.41",key:"1shlcs"}]])},6204:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("ArrowLeft",[["path",{d:"m12 19-7-7 7-7",key:"1l729n"}],["path",{d:"M19 12H5",key:"x3x0zl"}]])},6615:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("X",[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]])},6842:(e,t,r)=>{r.d(t,{C:()=>s});var n=r(2115),o=r(4129),a=Object.defineProperty,l=(e,t)=>a(e,"name",{value:t,configurable:!0});function i(e,t){return n.useReducer((e,r)=>{let n=t[e][r];return null!=n?n:e},e)}l(i,"useStateMachine");var s=l(e=>{let{present:t,children:r}=e,o=c(t),a="function"==typeof r?r({present:o.isPresent}):n.Children.only(r),l=d(o.ref,p(a));return"function"==typeof r||o.isPresent?n.cloneElement(a,{ref:l}):null},"Presence");function c(e){let[t,r]=n.useState(),a=n.useRef(null),s=n.useRef(e),c=n.useRef("none"),u=n.useRef(void 0),[d,p]=i(e?"mounted":"unmounted",{mounted:{UNMOUNT:"unmounted",ANIMATION_OUT:"unmountSuspended"},unmountSuspended:{MOUNT:"mounted",ANIMATION_END:"unmounted"},unmounted:{MOUNT:"mounted"}});return n.useEffect(()=>{if("mounted"===d){var e;c.current=null!=(e=u.current)?e:f(a.current),u.current=void 0}else c.current="none"},[d]),(0,o.N)(()=>{let t=a.current,r=s.current;if(r!==e){let n=c.current,o=f(t);e?(u.current=o,p("MOUNT")):"none"===o||(null==t?void 0:t.display)==="none"?p("UNMOUNT"):r&&n!==o?p("ANIMATION_OUT"):p("UNMOUNT"),s.current=e}},[e,p]),(0,o.N)(()=>{if(t){var e;let r,n=null!=(e=t.ownerDocument.defaultView)?e:window,o=l(e=>{let o=f(a.current).includes(CSS.escape(e.animationName));if(e.target===t&&o&&(p("ANIMATION_END"),!s.current)){let e=t.style.animationFillMode;t.style.animationFillMode="forwards",r=n.setTimeout(()=>{"forwards"===t.style.animationFillMode&&(t.style.animationFillMode=e)})}},"handleAnimationEnd"),i=l(e=>{e.target===t&&(c.current=f(a.current))},"handleAnimationStart");return t.addEventListener("animationstart",i),t.addEventListener("animationcancel",o),t.addEventListener("animationend",o),()=>{n.clearTimeout(r),t.removeEventListener("animationstart",i),t.removeEventListener("animationcancel",o),t.removeEventListener("animationend",o)}}p("ANIMATION_END")},[t,p]),{isPresent:["mounted","unmountSuspended"].includes(d),ref:n.useCallback(e=>{if(e){let t=getComputedStyle(e);a.current=t,u.current=f(t)}else a.current=null;r(e)},[])}}function u(e,t){if("function"==typeof e)return e(t);null!=e&&(e.current=t)}function d(){for(var e=arguments.length,t=Array(e),r=0;r{let t=o.current,r=!1,n=t.map(t=>{let n=u(t,e);return r||"function"!=typeof n||(r=!0),n});if(r)return()=>{for(let e=0;e{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Folder",[["path",{d:"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z",key:"1kt360"}]])},6916:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Binary",[["rect",{x:"14",y:"14",width:"4",height:"6",rx:"2",key:"p02svl"}],["rect",{x:"6",y:"4",width:"4",height:"6",rx:"2",key:"xm4xkj"}],["path",{d:"M6 20h4",key:"1i6q5t"}],["path",{d:"M14 10h4",key:"ru81e7"}],["path",{d:"M6 14h2v6",key:"16z9wg"}],["path",{d:"M14 4h2v6",key:"1idq9u"}]])},7205:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Network",[["rect",{x:"16",y:"16",width:"6",height:"6",rx:"1",key:"4q2zg0"}],["rect",{x:"2",y:"16",width:"6",height:"6",rx:"1",key:"8cvhb9"}],["rect",{x:"9",y:"2",width:"6",height:"6",rx:"1",key:"1egb70"}],["path",{d:"M5 16v-3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3",key:"1jsf9p"}],["path",{d:"M12 12V8",key:"2874zd"}]])},7602:(e,t,r)=>{r.d(t,{hO:()=>c,sG:()=>s});var n=r(2115),o=r(7650),a=r(2467),l=r(5155),i=Object.defineProperty,s=["a","button","div","form","h2","h3","img","input","label","li","nav","ol","p","select","span","svg","ul"].reduce((e,t)=>{let r=(0,a.TL)(`Primitive.${t}`),o=n.forwardRef((e,n)=>{let{asChild:o,...a}=e;return"undefined"!=typeof window&&(window[Symbol.for("radix-ui")]=!0),(0,l.jsx)(o?r:t,{...a,ref:n})});return o.displayName=`Primitive.${t}`,{...e,[t]:o}},{});function c(e,t){e&&o.flushSync(()=>e.dispatchEvent(t))}i(c,"name",{value:"dispatchDiscreteCustomEvent",configurable:!0})},8288:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Key",[["path",{d:"m15.5 7.5 2.3 2.3a1 1 0 0 0 1.4 0l2.1-2.1a1 1 0 0 0 0-1.4L19 4",key:"g0fldk"}],["path",{d:"m21 2-9.6 9.6",key:"1j0ho8"}],["circle",{cx:"7.5",cy:"15.5",r:"5.5",key:"yqb3hr"}]])},8428:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("LogOut",[["path",{d:"M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4",key:"1uf3rs"}],["polyline",{points:"16 17 21 12 16 7",key:"1gabdz"}],["line",{x1:"21",x2:"9",y1:"12",y2:"12",key:"1uyos4"}]])},8770:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("CircleAlert",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["line",{x1:"12",x2:"12",y1:"8",y2:"12",key:"1pkeuh"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16",key:"4dfq90"}]])},8822:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("LayoutGrid",[["rect",{width:"7",height:"7",x:"3",y:"3",rx:"1",key:"1g98yp"}],["rect",{width:"7",height:"7",x:"14",y:"3",rx:"1",key:"6d4xhi"}],["rect",{width:"7",height:"7",x:"14",y:"14",rx:"1",key:"nxv5o0"}],["rect",{width:"7",height:"7",x:"3",y:"14",rx:"1",key:"1bb6yr"}]])},8844:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Database",[["ellipse",{cx:"12",cy:"5",rx:"9",ry:"3",key:"msslwz"}],["path",{d:"M3 5V19A9 3 0 0 0 21 19V5",key:"1wlel7"}],["path",{d:"M3 12A9 3 0 0 0 21 12",key:"mv7ke4"}]])},8941:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Pause",[["rect",{x:"14",y:"4",width:"4",height:"16",rx:"1",key:"zuxfzm"}],["rect",{x:"6",y:"4",width:"4",height:"16",rx:"1",key:"1okwgv"}]])},8946:(e,t,r)=>{r.d(t,{B:()=>c});var n,o=r(2115),a=r(4129),l=Object.defineProperty,i=(n||(n=r.t(o,2)))[" useId ".trim().toString()]||(()=>void 0),s=0;function c(e){let[t,r]=o.useState(i());return(0,a.N)(()=>{e||r(e=>e??String(s++))},[e]),e||(t?`radix-${t}`:"")}l(c,"name",{value:"useId",configurable:!0})},8971:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Table",[["path",{d:"M12 3v18",key:"108xh3"}],["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}],["path",{d:"M3 9h18",key:"1pudct"}],["path",{d:"M3 15h18",key:"5xshup"}]])},9364:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("ListTree",[["path",{d:"M21 12h-8",key:"1bmf0i"}],["path",{d:"M21 6H8",key:"1pqkrb"}],["path",{d:"M21 18h-8",key:"1tm79t"}],["path",{d:"M3 6v4c0 1.1.9 2 2 2h3",key:"1ywdgy"}],["path",{d:"M3 10v6c0 1.1.9 2 2 2h3",key:"2wc746"}]])},9478:(e,t,r)=>{r.d(t,{i:()=>p});var n=r(2115),o=r.t(n,2),a=r(4129),l=Object.defineProperty,i=o[" useEffectEvent ".trim().toString()],s=o[" useInsertionEffect ".trim().toString()];function c(e){if("function"==typeof i)return i(e);let t=n.useRef(()=>{throw Error("Cannot call an event handler while rendering.")});return"function"==typeof s?s(()=>{t.current=e}):(0,a.N)(()=>{t.current=e}),n.useMemo(()=>(...e)=>t.current?.(...e),[])}l(c,"name",{value:"useEffectEvent",configurable:!0});var u=Object.defineProperty,d=(e,t)=>u(e,"name",{value:t,configurable:!0}),f=o[" useInsertionEffect ".trim().toString()]||a.N;function p({prop:e,defaultProp:t,onChange:r=d(()=>{},"onChange"),caller:o}){let[a,l,i]=m({defaultProp:t,onChange:r}),s=void 0!==e,c=s?e:a;return[c,n.useCallback(t=>{if(s){let r=h(t)?t(e):t;r!==e&&i.current?.(r)}else l(t)},[s,e,l,i])]}function m({defaultProp:e,onChange:t}){let[r,o]=n.useState(e),a=n.useRef(r),l=n.useRef(t);return f(()=>{l.current=t},[t]),n.useEffect(()=>{a.current!==r&&(l.current?.(r),a.current=r)},[r,a]),[r,o,l]}function h(e){return"function"==typeof e}d(p,"useControllableState"),d(m,"useUncontrolledState"),d(h,"isFunction");var b=Symbol("RADIX:SYNC_STATE");d(function(e,t,r,o){let{prop:a,defaultProp:l,onChange:i,caller:s}=t,u=void 0!==a,d=c(i),f=[{...r,state:l}];o&&f.push(o);let[p,m]=n.useReducer((t,r)=>{if(r.type===b)return{...t,state:r.state};let n=e(t,r);return u&&!Object.is(n.state,t.state)&&d(n.state),n},...f),h=p.state,y=n.useRef(h);n.useEffect(()=>{y.current!==h&&(y.current=h,u||d(h))},[h,y,u]);let v=n.useMemo(()=>void 0!==a?{...p,state:a}:p,[p,a]);return n.useEffect(()=>{u&&!Object.is(a,p.state)&&m({type:b,state:a})},[a,p.state,u]),[v,m]},"useControllableStateReducer")},9915:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("History",[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"1357e3"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}],["path",{d:"M12 7v5l4 2",key:"1fdv2h"}]])}}]); \ No newline at end of file +"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[685],{109:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Search",[["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}],["path",{d:"m21 21-4.3-4.3",key:"1qie3q"}]])},222:(e,t,r)=>{r.d(t,{c:()=>o});var n=r(2115);function o(e){let t=n.useRef(e);return n.useEffect(()=>{t.current=e}),n.useMemo(()=>(...e)=>t.current?.(...e),[])}(0,Object.defineProperty)(o,"name",{value:"useCallbackRef",configurable:!0})},406:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("TriangleAlert",[["path",{d:"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3",key:"wmoenq"}],["path",{d:"M12 9v4",key:"juzpu7"}],["path",{d:"M12 17h.01",key:"p32p05"}]])},443:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("UserX",[["path",{d:"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2",key:"1yyitq"}],["circle",{cx:"9",cy:"7",r:"4",key:"nufk8"}],["line",{x1:"17",x2:"22",y1:"8",y2:"13",key:"3nzzx3"}],["line",{x1:"22",x2:"17",y1:"8",y2:"13",key:"1swrse"}]])},644:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("WifiOff",[["path",{d:"M12 20h.01",key:"zekei9"}],["path",{d:"M8.5 16.429a5 5 0 0 1 7 0",key:"1bycff"}],["path",{d:"M5 12.859a10 10 0 0 1 5.17-2.69",key:"1dl1wf"}],["path",{d:"M19 12.859a10 10 0 0 0-2.007-1.523",key:"4k23kn"}],["path",{d:"M2 8.82a15 15 0 0 1 4.177-2.643",key:"1grhjp"}],["path",{d:"M22 8.82a15 15 0 0 0-11.288-3.764",key:"z3jwby"}],["path",{d:"m2 2 20 20",key:"1ooewy"}]])},805:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("WandSparkles",[["path",{d:"m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.2 1.2 0 0 0 1.72 0L21.64 5.36a1.2 1.2 0 0 0 0-1.72",key:"ul74o6"}],["path",{d:"m14 7 3 3",key:"1r5n42"}],["path",{d:"M5 6v4",key:"ilb8ba"}],["path",{d:"M19 14v4",key:"blhpug"}],["path",{d:"M10 2v2",key:"7u0qdc"}],["path",{d:"M7 8H3",key:"zfb6yr"}],["path",{d:"M21 16h-4",key:"1cnmox"}],["path",{d:"M11 3H9",key:"1obp7u"}]])},984:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("CircleCheck",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]])},1165:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("ArrowDown",[["path",{d:"M12 5v14",key:"s699le"}],["path",{d:"m19 12-7 7-7-7",key:"1idqje"}]])},1190:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("ChevronLeft",[["path",{d:"m15 18-6-6 6-6",key:"1wnfg3"}]])},1270:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Lock",[["rect",{width:"18",height:"11",x:"3",y:"11",rx:"2",ry:"2",key:"1w4ew1"}],["path",{d:"M7 11V7a5 5 0 0 1 10 0v4",key:"fwvmzm"}]])},1286:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("FileSearch",[["path",{d:"M14 2v4a2 2 0 0 0 2 2h4",key:"tnqrlb"}],["path",{d:"M4.268 21a2 2 0 0 0 1.727 1H18a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v3",key:"ms7g94"}],["path",{d:"m9 18-1.5-1.5",key:"1j6qii"}],["circle",{cx:"5",cy:"14",r:"3",key:"ufru5t"}]])},1288:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Copy",[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]])},1851:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Activity",[["path",{d:"M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2",key:"169zse"}]])},2033:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("LoaderCircle",[["path",{d:"M21 12a9 9 0 1 1-6.219-8.56",key:"13zald"}]])},2036:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Pen",[["path",{d:"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z",key:"1a8usu"}]])},2111:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Play",[["polygon",{points:"6 3 20 12 6 21 6 3",key:"1oa8hb"}]])},2251:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Check",[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]])},2265:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Plus",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]])},2467:(e,t,r)=>{r.d(t,{TL:()=>s});var n,o=r(2115),a=r(4446),l=Object.defineProperty,i=(e,t)=>l(e,"name",{value:t,configurable:!0});function s(e){let t=o.forwardRef((t,r)=>{let{children:n,...l}=t,i=null,s=!1,c=[];h(n)&&"function"==typeof g&&(n=g(n._payload)),o.Children.forEach(n,e=>{if(p(e)){s=!0;let t="child"in e.props?e.props.child:e.props.children;h(t)&&"function"==typeof g&&(t=g(t._payload)),i=u(e,t),c.push(i?.props?.children)}else c.push(e)}),i?i=o.cloneElement(i,void 0,c):!s&&1===o.Children.count(n)&&o.isValidElement(n)&&(i=n);let m=i?f(i):void 0,b=(0,a.s)(r,m);if(!i){if(n||0===n)throw Error(s?v(e):y(e));return n}let w=d(l,i.props??{});return i.type!==o.Fragment&&(w.ref=r?b:m),o.cloneElement(i,w)});return t.displayName=`${e}.Slot`,t}i(s,"createSlot");var c=Symbol.for("radix.slottable");i(function(e){let t=i(e=>"child"in e?e.children(e.child):e.children,"Slottable");return t.displayName=`${e}.Slottable`,t.__radixId=c,t},"createSlottable");var u=i((e,t)=>{if("child"in e.props){let t=e.props.child;return o.isValidElement(t)?o.cloneElement(t,void 0,e.props.children(t.props.children)):null}return o.isValidElement(t)?t:null},"getSlottableElementFromSlottable");function d(e,t){let r={...t};for(let n in t){let o=e[n],a=t[n];/^on[A-Z]/.test(n)?o&&a?r[n]=(...e)=>{let t=a(...e);return o(...e),t}:o&&(r[n]=o):"style"===n?r[n]={...o,...a}:"className"===n&&(r[n]=[o,a].filter(Boolean).join(" "))}return{...e,...r}}function f(e){let t=Object.getOwnPropertyDescriptor(e.props,"ref")?.get,r=t&&"isReactWarning"in t&&t.isReactWarning;return r?e.ref:(r=(t=Object.getOwnPropertyDescriptor(e,"ref")?.get)&&"isReactWarning"in t&&t.isReactWarning)?e.props.ref:e.props.ref||e.ref}function p(e){return o.isValidElement(e)&&"function"==typeof e.type&&"__radixId"in e.type&&e.type.__radixId===c}i(d,"mergeProps"),i(f,"getElementRef"),i(p,"isSlottable");var m=Symbol.for("react.lazy");function h(e){return null!=e&&"object"==typeof e&&"$$typeof"in e&&e.$$typeof===m&&"_payload"in e&&b(e._payload)}function b(e){return"object"==typeof e&&null!==e&&"then"in e}i(h,"isLazyComponent"),i(b,"isPromiseLike");var y=i(e=>`${e} failed to slot onto its children. Expected a single React element child or \`Slottable\`.`,"createSlotError"),v=i(e=>`${e} failed to slot onto its \`Slottable\`. Expected \`Slottable\` to receive a single React element child.`,"createSlottableError"),g=(n||(n=r.t(o,2)))[" use ".trim().toString()]},2556:(e,t,r)=>{r.d(t,{mK:()=>l});var n=Object.defineProperty,o=(e,t)=>n(e,"name",{value:t,configurable:!0}),a=!!("undefined"!=typeof window&&window.document&&window.document.createElement);function l(e,t,{checkForDefaultPrevented:r=!0}={}){return o(function(n){if(e?.(n),!1===r||!n||!n.defaultPrevented)return t?.(n)},"handleEvent")}function i(e){if(!a)throw Error("Cannot access document outside of the DOM");return e?.ownerDocument??document}function s(e){return"IFRAME"===e.tagName}o(l,"composeEventHandlers"),o(function(e){if(!a)throw Error("Cannot access window outside of the DOM");return e?.ownerDocument?.defaultView??window},"getOwnerWindow"),o(i,"getOwnerDocument"),o(function e(t,r=!1){let{activeElement:n}=i(t);if(!n?.nodeName)return null;if(s(n)&&n.contentDocument)return e(n.contentDocument.body,r);if(r){let e=n.getAttribute("aria-activedescendant");if(e){let t=i(n).getElementById(e);if(t)return t}}return n},"getActiveElement"),o(s,"isFrame")},2649:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Star",[["path",{d:"M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z",key:"r04s7s"}]])},2784:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Moon",[["path",{d:"M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z",key:"a7tn18"}]])},2821:(e,t,r)=>{r.d(t,{$:()=>n});function n(){for(var e,t,r=0,n="",o=arguments.length;r{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Archive",[["rect",{width:"20",height:"5",x:"2",y:"3",rx:"1",key:"1wp1u1"}],["path",{d:"M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8",key:"1s80jp"}],["path",{d:"M10 12h4",key:"a56b0p"}]])},3063:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Settings",[["path",{d:"M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z",key:"1qme2f"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]])},3101:(e,t,r)=>{r.d(t,{F:()=>l});var n=r(2821);let o=e=>"boolean"==typeof e?`${e}`:0===e?"0":e,a=n.$,l=(e,t)=>r=>{var n;if((null==t?void 0:t.variants)==null)return a(e,null==r?void 0:r.class,null==r?void 0:r.className);let{variants:l,defaultVariants:i}=t,s=Object.keys(l).map(e=>{let t=null==r?void 0:r[e],n=null==i?void 0:i[e];if(null===t)return null;let a=o(t)||o(n);return l[e][a]}),c=r&&Object.entries(r).reduce((e,t)=>{let[r,n]=t;return void 0===n||(e[r]=n),e},{});return a(e,s,null==t||null==(n=t.compoundVariants)?void 0:n.reduce((e,t)=>{let{class:r,className:n,...o}=t;return Object.entries(o).every(e=>{let[t,r]=e;return Array.isArray(r)?r.includes({...i,...c}[t]):({...i,...c})[t]===r})?[...e,r,n]:e},[]),null==r?void 0:r.class,null==r?void 0:r.className)}},3173:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("ArrowRight",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"m12 5 7 7-7 7",key:"xquz4c"}]])},3263:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("ChevronRight",[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]])},3381:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("RefreshCw",[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8",key:"v9h5vc"}],["path",{d:"M21 3v5h-5",key:"1q7to0"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16",key:"3uifl3"}],["path",{d:"M8 16H3v5",key:"1cv678"}]])},3468:(e,t,r)=>{r.d(t,{A:()=>i});var n=r(2115),o=r(5155),a=Object.defineProperty,l=(e,t)=>a(e,"name",{value:t,configurable:!0});function i(e,t=[]){let r=[];function a(t,a){let i=n.createContext(a);i.displayName=t+"Context";let s=r.length;r=[...r,a];let c=l(t=>{let{scope:r,children:a,...l}=t,c=r?.[e]?.[s]||i,u=n.useMemo(()=>l,Object.values(l));return(0,o.jsx)(c.Provider,{value:u,children:a})},"Provider");function u(r,o,l={}){let{optional:c=!1}=l,d=o?.[e]?.[s]||i,f=n.useContext(d);if(f)return f;if(void 0!==a)return a;if(!c)throw Error(`\`${r}\` must be used within \`${t}\``)}return c.displayName=t+"Provider",l(u,"useContext"),[c,u]}l(a,"createContext");let c=l(()=>{let t=r.map(e=>n.createContext(e));return l(function(r){let o=r?.[e]||t;return n.useMemo(()=>({[`__scope${e}`]:{...r,[e]:o}}),[r,o])},"useScope")},"createScope");return c.scopeName=e,[a,s(c,...t)]}function s(...e){let t=e[0];if(1===e.length)return t;let r=l(()=>{let r=e.map(e=>({useScope:e(),scopeName:e.scopeName}));return l(function(e){let o=r.reduce((t,{useScope:r,scopeName:n})=>{let o=r(e)[`__scope${n}`];return{...t,...o}},{});return n.useMemo(()=>({[`__scope${t.scopeName}`]:o}),[o])},"useComposedScopes")},"createScope");return r.scopeName=t.scopeName,r}l(function(e,t){let r=n.createContext(t);r.displayName=e+"Context";let a=l(e=>{let{children:t,...a}=e,l=n.useMemo(()=>a,Object.values(a));return(0,o.jsx)(r.Provider,{value:l,children:t})},"Provider");function i(o,a={}){let{optional:l=!1}=a,s=n.useContext(r);if(s)return s;if(void 0!==t)return t;if(!l)throw Error(`\`${o}\` must be used within \`${e}\``)}return a.displayName=e+"Provider",l(i,"useContext"),[a,i]},"createContext"),l(i,"createContextScope"),l(s,"composeContextScopes")},3489:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Download",[["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["polyline",{points:"7 10 12 15 17 10",key:"2ggqvy"}],["line",{x1:"12",x2:"12",y1:"15",y2:"3",key:"1vk2je"}]])},3527:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Columns2",[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}],["path",{d:"M12 3v18",key:"108xh3"}]])},3630:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Trash2",[["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6",key:"4alrt4"}],["path",{d:"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2",key:"v07s0e"}],["line",{x1:"10",x2:"10",y1:"11",y2:"17",key:"1uufr5"}],["line",{x1:"14",x2:"14",y1:"11",y2:"17",key:"xtxkd"}]])},4129:(e,t,r)=>{r.d(t,{N:()=>o});var n=r(2115),o=globalThis?.document?n.useLayoutEffect:()=>{}},4294:(e,t,r)=>{r.d(t,{A:()=>i});var n=r(2115);let o=function(){for(var e=arguments.length,t=Array(e),r=0;r!!e&&""!==e.trim()&&r.indexOf(e)===t).join(" ").trim()};var a={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};let l=(0,n.forwardRef)((e,t)=>{let{color:r="currentColor",size:l=24,strokeWidth:i=2,absoluteStrokeWidth:s,className:c="",children:u,iconNode:d,...f}=e;return(0,n.createElement)("svg",{ref:t,...a,width:l,height:l,stroke:r,strokeWidth:s?24*Number(i)/Number(l):i,className:o("lucide",c),...f},[...d.map(e=>{let[t,r]=e;return(0,n.createElement)(t,r)}),...Array.isArray(u)?u:[u]])}),i=(e,t)=>{let r=(0,n.forwardRef)((r,a)=>{let{className:i,...s}=r;return(0,n.createElement)(l,{ref:a,iconNode:t,className:o("lucide-".concat(e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase()),i),...s})});return r.displayName="".concat(e),r}},4368:(e,t,r)=>{r.d(t,{bm:()=>ti,UC:()=>tt,VY:()=>tl,hJ:()=>e6,ZL:()=>e9,bL:()=>e1,hE:()=>ta,Hs:()=>eJ});var n,o,a,l=r(2115),i=r(2556),s=r(4446),c=r(3468),u=r(8946),d=r(9478),f=r(7602),p=r(222),m=r(5155),h=Object.defineProperty,b=(e,t)=>h(e,"name",{value:t,configurable:!0}),y="dismissableLayer.update",v=l.createContext({layers:new Set,layersWithOutsidePointerEventsDisabled:new Set,branches:new Set,dismissableSurfaces:new Set}),g=l.forwardRef(b(function(e,t){var r,n;let{disableOutsidePointerEvents:a=!1,deferPointerDownOutside:c=!1,onEscapeKeyDown:u,onPointerDownOutside:d,onFocusOutside:h,onInteractOutside:g,onDismiss:w,...k}=e,C=l.useContext(v),[M,S]=l.useState(null),R=null!=(n=null==M?void 0:M.ownerDocument)?n:null==(r=globalThis)?void 0:r.document,[,O]=l.useState({}),j=(0,s.s)(t,S),D=Array.from(C.layers),[P]=[...C.layersWithOutsidePointerEventsDisabled].slice(-1),z=P?D.indexOf(P):-1,N=M?D.indexOf(M):-1,L=C.layersWithOutsidePointerEventsDisabled.size>0,I=N>=z,T=l.useRef(!1),F=x(e=>{null==d||d(e),null==g||g(e),e.defaultPrevented||null==w||w()},{ownerDocument:R,deferPointerDownOutside:c,isDeferredPointerDownOutsideRef:T,dismissableSurfaces:C.dismissableSurfaces,shouldHandlePointerDownOutside:l.useCallback(e=>{if(!(e instanceof Node))return!1;let t=[...C.branches].some(t=>t.contains(e));return I&&!t},[C.branches,I])}),_=A(e=>{if(c&&T.current)return;let t=e.target;![...C.branches].some(e=>e.contains(t))&&(null==h||h(e),null==g||g(e),e.defaultPrevented||null==w||w())},R),W=!!M&&N===D.length-1,G=(0,p.c)(e=>{"Escape"===e.key&&(null==u||u(e),!e.defaultPrevented&&w&&(e.preventDefault(),w()))});return l.useEffect(()=>{if(W)return R.addEventListener("keydown",G,{capture:!0}),()=>R.removeEventListener("keydown",G,{capture:!0})},[R,W,G]),l.useEffect(()=>{if(M)return a&&(0===C.layersWithOutsidePointerEventsDisabled.size&&(o=R.body.style.pointerEvents,R.body.style.pointerEvents="none"),C.layersWithOutsidePointerEventsDisabled.add(M)),C.layers.add(M),E(),()=>{a&&(C.layersWithOutsidePointerEventsDisabled.delete(M),0===C.layersWithOutsidePointerEventsDisabled.size&&(R.body.style.pointerEvents=o))}},[M,R,a,C]),l.useEffect(()=>()=>{M&&(C.layers.delete(M),C.layersWithOutsidePointerEventsDisabled.delete(M),E())},[M,C]),l.useEffect(()=>{let e=b(()=>O({}),"handleUpdate");return document.addEventListener(y,e),()=>document.removeEventListener(y,e)},[]),(0,m.jsx)(f.sG.div,{...k,ref:j,style:{pointerEvents:L?I?"auto":"none":void 0,...e.style},onFocusCapture:(0,i.mK)(e.onFocusCapture,_.onFocusCapture),onBlurCapture:(0,i.mK)(e.onBlurCapture,_.onBlurCapture),onPointerDownCapture:(0,i.mK)(e.onPointerDownCapture,F.onPointerDownCapture)})},"DismissableLayer"));function w(){let e=l.useContext(v),[t,r]=l.useState(null);return l.useEffect(()=>{if(t)return e.dismissableSurfaces.add(t),()=>{e.dismissableSurfaces.delete(t)}},[t,e.dismissableSurfaces]),r}b(w,"useDismissableLayerSurface");var k=b(()=>!0,"IS_TRUE");function x(e,t){var r;let{ownerDocument:n=null==(r=globalThis)?void 0:r.document,deferPointerDownOutside:o=!1,isDeferredPointerDownOutsideRef:a,dismissableSurfaces:i,shouldHandlePointerDownOutside:s=k}=t,c=(0,p.c)(e),u=l.useRef(!1),d=l.useRef(!1),f=l.useRef(new Map),m=l.useRef(()=>{});return l.useEffect(()=>{function e(){d.current=!1,a.current=!1,f.current.clear()}function t(){return Array.from(f.current.values()).some(Boolean)}function r(e){if(!d.current)return;let t=e.target;t instanceof Node&&[...i].some(e=>e.contains(t))||f.current.set(e.type,!0),"click"===e.type&&window.setTimeout(()=>{d.current&&m.current()},0)}function l(e){d.current&&f.current.set(e.type,!1)}b(e,"resetOutsideInteraction"),b(t,"isOutsideInteractionIntercepted"),b(r,"handleInteractionCapture"),b(l,"handleInteractionBubble");let p=b(r=>{if(r.target&&!u.current){let l=function(){n.removeEventListener("click",m.current);let r=t();e(),r||C("dismissableLayer.pointerDownOutside",c,i,{discrete:!0})};if(b(l,"handleAndDispatchPointerDownOutsideEvent"),!s(r.target)){n.removeEventListener("click",m.current),e(),u.current=!1;return}let i={originalEvent:r};d.current=!0,a.current=o&&0===r.button,f.current.clear(),o&&0===r.button?(n.removeEventListener("click",m.current),m.current=l,n.addEventListener("click",m.current,{once:!0})):l()}else n.removeEventListener("click",m.current),e();u.current=!1},"handlePointerDown"),h=["pointerup","mousedown","mouseup","touchstart","touchend","click"];for(let e of h)n.addEventListener(e,r,!0),n.addEventListener(e,l);let y=window.setTimeout(()=>{n.addEventListener("pointerdown",p)},0);return()=>{for(let e of(window.clearTimeout(y),n.removeEventListener("pointerdown",p),n.removeEventListener("click",m.current),h))n.removeEventListener(e,r,!0),n.removeEventListener(e,l)}},[n,c,o,a,i,s]),{onPointerDownCapture:b(()=>u.current=!0,"onPointerDownCapture")}}function A(e){var t;let r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null==(t=globalThis)?void 0:t.document,n=(0,p.c)(e),o=l.useRef(!1);return l.useEffect(()=>{let e=b(e=>{e.target&&!o.current&&C("dismissableLayer.focusOutside",n,{originalEvent:e},{discrete:!1})},"handleFocus");return r.addEventListener("focusin",e),()=>r.removeEventListener("focusin",e)},[r,n]),{onFocusCapture:b(()=>o.current=!0,"onFocusCapture"),onBlurCapture:b(()=>o.current=!1,"onBlurCapture")}}function E(){let e=new CustomEvent(y);document.dispatchEvent(e)}function C(e,t,r,n){let{discrete:o}=n,a=r.originalEvent.target,l=new CustomEvent(e,{bubbles:!1,cancelable:!0,detail:r});t&&a.addEventListener(e,t,{once:!0}),o?(0,f.hO)(a,l):a.dispatchEvent(l)}b(x,"usePointerDownOutside"),b(A,"useFocusOutside"),b(E,"dispatchUpdate"),b(C,"handleAndDispatchCustomEvent");var M=Object.defineProperty,S=(e,t)=>M(e,"name",{value:t,configurable:!0}),R="focusScope.autoFocusOnMount",O="focusScope.autoFocusOnUnmount",j={bubbles:!1,cancelable:!0},D=l.forwardRef(S(function(e,t){let{loop:r=!1,trapped:n=!1,onMountAutoFocus:o,onUnmountAutoFocus:a,...i}=e,[c,u]=l.useState(null),d=(0,p.c)(o),h=(0,p.c)(a),b=l.useRef(null),y=(0,s.s)(t,u),v=l.useRef({paused:!1,pause(){this.paused=!0},resume(){this.paused=!1}}).current;l.useEffect(()=>{if(n){let e=function(e){if(v.paused||!c)return;let t=e.target;c.contains(t)?b.current=t:F(b.current,{select:!0})},t=function(e){if(v.paused||!c)return;let t=e.relatedTarget;null!==t&&(c.contains(t)||F(b.current,{select:!0}))},r=function(e){if(document.activeElement===document.body)for(let t of e)t.removedNodes.length>0&&F(c)};S(e,"handleFocusIn"),S(t,"handleFocusOut"),S(r,"handleMutations"),document.addEventListener("focusin",e),document.addEventListener("focusout",t);let n=new MutationObserver(r);return c&&n.observe(c,{childList:!0,subtree:!0}),()=>{document.removeEventListener("focusin",e),document.removeEventListener("focusout",t),n.disconnect()}}},[n,c,v.paused]),l.useEffect(()=>{if(c){_.add(v);let e=document.activeElement;if(!c.contains(e)){let t=new CustomEvent(R,j);c.addEventListener(R,d),c.dispatchEvent(t),t.defaultPrevented||(P(K(N(c)),{select:!0}),document.activeElement===e&&F(c))}return()=>{c.removeEventListener(R,d),setTimeout(()=>{let t=new CustomEvent(O,j);c.addEventListener(O,h),c.dispatchEvent(t),t.defaultPrevented||F(null!=e?e:document.body,{select:!0}),c.removeEventListener(O,h),_.remove(v)},0)}}},[c,d,h,v]);let g=l.useCallback(e=>{if(!r&&!n||v.paused)return;let t="Tab"===e.key&&!e.altKey&&!e.ctrlKey&&!e.metaKey,o=document.activeElement;if(t&&o){let t=e.currentTarget,[n,a]=z(t);n&&a?e.shiftKey||o!==a?e.shiftKey&&o===n&&(e.preventDefault(),r&&F(a,{select:!0})):(e.preventDefault(),r&&F(n,{select:!0})):o===t&&e.preventDefault()}},[r,n,v.paused]);return(0,m.jsx)(f.sG.div,{tabIndex:-1,...i,ref:y,onKeyDown:g})},"FocusScope"));function P(e){let{select:t=!1}=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=document.activeElement;for(let n of e)if(F(n,{select:t}),document.activeElement!==r)return}function z(e){let t=N(e);return[L(t,e),L(t.reverse(),e)]}function N(e){let t=[],r=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT,{acceptNode:S(e=>{let t="INPUT"===e.tagName&&"hidden"===e.type;return e.disabled||e.hidden||t?NodeFilter.FILTER_SKIP:e.tabIndex>=0?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP},"acceptNode")});for(;r.nextNode();)t.push(r.currentNode);return t}function L(e,t){let r="function"==typeof t.checkVisibility&&t.checkVisibility({checkVisibilityCSS:!0});for(let n of e)if(!(r?!n.checkVisibility({checkVisibilityCSS:!0}):I(n,{upTo:t})))return n}function I(e,t){let{upTo:r}=t;if("hidden"===getComputedStyle(e).visibility)return!0;for(;e&&(void 0===r||e!==r);){if("none"===getComputedStyle(e).display)return!0;e=e.parentElement}return!1}function T(e){return e instanceof HTMLInputElement&&"select"in e}function F(e){let{select:t=!1}=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(e&&e.focus){let r=document.activeElement;e.focus({preventScroll:!0}),e!==r&&T(e)&&t&&e.select()}}S(P,"focusFirst"),S(z,"getTabbableEdges"),S(N,"getTabbableCandidates"),S(L,"findVisible"),S(I,"isHidden"),S(T,"isSelectableInput"),S(F,"focus");var _=W();function W(){let e=[];return{add(t){let r=e[0];t!==r&&(null==r||r.pause()),(e=G(e,t)).unshift(t)},remove(t){var r;null==(r=(e=G(e,t))[0])||r.resume()}}}function G(e,t){let r=[...e],n=r.indexOf(t);return -1!==n&&r.splice(n,1),r}function K(e){return e.filter(e=>"A"!==e.tagName)}S(W,"createFocusScopesStack"),S(G,"arrayRemove"),S(K,"removeLinks");var V=r(7650),H=r(4129),B=Object.defineProperty,q=l.forwardRef(B(function(e,t){var r,n;let{container:o,...a}=e,[i,s]=l.useState(!1);(0,H.N)(()=>s(!0),[]);let c=o||i&&(null==(n=globalThis)||null==(r=n.document)?void 0:r.body);return c?V.createPortal((0,m.jsx)(f.sG.div,{...a,ref:t}),c):null},"name",{value:"Portal",configurable:!0})),$=r(6842),U=Object.defineProperty,Z=(e,t)=>U(e,"name",{value:t,configurable:!0}),X=0,Y=null;function J(){l.useEffect(()=>{Y||(Y={start:Q(),end:Q()});let{start:e,end:t}=Y;return document.body.firstElementChild!==e&&document.body.insertAdjacentElement("afterbegin",e),document.body.lastElementChild!==t&&document.body.insertAdjacentElement("beforeend",t),X++,()=>{1===X&&(null==Y||Y.start.remove(),null==Y||Y.end.remove(),Y=null),X=Math.max(0,X-1)}},[])}function Q(){let e=document.createElement("span");return e.setAttribute("data-radix-focus-guard",""),e.tabIndex=0,e.style.outline="none",e.style.opacity="0",e.style.position="fixed",e.style.pointerEvents="none",e}Z(function(e){return J(),e.children},"FocusGuards"),Z(J,"useFocusGuards"),Z(Q,"createFocusGuard");var ee=function(){return(ee=Object.assign||function(e){for(var t,r=1,n=arguments.length;rt.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,n=Object.getOwnPropertySymbols(e);ot.indexOf(n[o])&&Object.prototype.propertyIsEnumerable.call(e,n[o])&&(r[n[o]]=e[n[o]]);return r}Object.create;Object.create;var er=("function"==typeof SuppressedError&&SuppressedError,"right-scroll-bar-position"),en="width-before-scroll-bar";function eo(e,t){return"function"==typeof e?e(t):e&&(e.current=t),e}var ea="undefined"!=typeof window?l.useLayoutEffect:l.useEffect,el=new WeakMap;function ei(e){return e}var es=function(e){void 0===e&&(e={});var t,r,n,o=(void 0===t&&(t=ei),r=[],n=!1,{read:function(){if(n)throw Error("Sidecar: could not `read` from an `assigned` medium. `read` could be used only with `useMedium`.");return r.length?r[r.length-1]:null},useMedium:function(e){var o=t(e,n);return r.push(o),function(){r=r.filter(function(e){return e!==o})}},assignSyncMedium:function(e){for(n=!0;r.length;){var t=r;r=[],t.forEach(e)}r={push:function(t){return e(t)},filter:function(){return r}}},assignMedium:function(e){n=!0;var t=[];if(r.length){var o=r;r=[],o.forEach(e),t=r}var a=function(){var r=t;t=[],r.forEach(e)},l=function(){return Promise.resolve().then(a)};l(),r={push:function(e){t.push(e),l()},filter:function(e){return t=t.filter(e),r}}}});return o.options=ee({async:!0,ssr:!1},e),o}(),ec=function(){},eu=l.forwardRef(function(e,t){var r,n,o,a,i=l.useRef(null),s=l.useState({onScrollCapture:ec,onWheelCapture:ec,onTouchMoveCapture:ec}),c=s[0],u=s[1],d=e.forwardProps,f=e.children,p=e.className,m=e.removeScrollBar,h=e.enabled,b=e.shards,y=e.sideCar,v=e.noRelative,g=e.noIsolation,w=e.inert,k=e.allowPinchZoom,x=e.as,A=e.gapMode,E=et(e,["forwardProps","children","className","removeScrollBar","enabled","shards","sideCar","noRelative","noIsolation","inert","allowPinchZoom","as","gapMode"]),C=(r=[i,t],n=function(e){return r.forEach(function(t){return eo(t,e)})},(o=(0,l.useState)(function(){return{value:null,callback:n,facade:{get current(){return o.value},set current(value){var e=o.value;e!==value&&(o.value=value,o.callback(value,e))}}}})[0]).callback=n,a=o.facade,ea(function(){var e=el.get(a);if(e){var t=new Set(e),n=new Set(r),o=a.current;t.forEach(function(e){n.has(e)||eo(e,null)}),n.forEach(function(e){t.has(e)||eo(e,o)})}el.set(a,r)},[r]),a),M=ee(ee({},E),c);return l.createElement(l.Fragment,null,h&&l.createElement(y,{sideCar:es,removeScrollBar:m,shards:b,noRelative:v,noIsolation:g,inert:w,setCallbacks:u,allowPinchZoom:!!k,lockRef:i,gapMode:A}),d?l.cloneElement(l.Children.only(f),ee(ee({},M),{ref:C})):l.createElement(void 0===x?"div":x,ee({},M,{className:p,ref:C}),f))});eu.defaultProps={enabled:!0,removeScrollBar:!0,inert:!1},eu.classNames={fullWidth:en,zeroRight:er};var ed=function(e){var t=e.sideCar,r=et(e,["sideCar"]);if(!t)throw Error("Sidecar: please provide `sideCar` property to import the right car");var n=t.read();if(!n)throw Error("Sidecar medium not found");return l.createElement(n,ee({},r))};ed.isSideCarExport=!0;var ef=function(){var e=0,t=null;return{add:function(n){if(0==e&&(t=function(){if(!document)return null;var e=document.createElement("style");e.type="text/css";var t=a||r.nc;return t&&e.setAttribute("nonce",t),e}())){var o,l;(o=t).styleSheet?o.styleSheet.cssText=n:o.appendChild(document.createTextNode(n)),l=t,(document.head||document.getElementsByTagName("head")[0]).appendChild(l)}e++},remove:function(){--e||!t||(t.parentNode&&t.parentNode.removeChild(t),t=null)}}},ep=function(){var e=ef();return function(t,r){l.useEffect(function(){return e.add(t),function(){e.remove()}},[t&&r])}},em=function(){var e=ep();return function(t){return e(t.styles,t.dynamic),null}},eh={left:0,top:0,right:0,gap:0},eb=function(e){return parseInt(e||"",10)||0},ey=function(e){var t=window.getComputedStyle(document.body),r=t["padding"===e?"paddingLeft":"marginLeft"],n=t["padding"===e?"paddingTop":"marginTop"],o=t["padding"===e?"paddingRight":"marginRight"];return[eb(r),eb(n),eb(o)]},ev=function(e){if(void 0===e&&(e="margin"),"undefined"==typeof window)return eh;var t=ey(e),r=document.documentElement.clientWidth,n=window.innerWidth;return{left:t[0],top:t[1],right:t[2],gap:Math.max(0,n-r+t[2]-t[0])}},eg=em(),ew="data-scroll-locked",ek=function(e,t,r,n){var o=e.left,a=e.top,l=e.right,i=e.gap;return void 0===r&&(r="margin"),"\n .".concat("with-scroll-bars-hidden"," {\n overflow: hidden ").concat(n,";\n padding-right: ").concat(i,"px ").concat(n,";\n }\n body[").concat(ew,"] {\n overflow: hidden ").concat(n,";\n overscroll-behavior: contain;\n ").concat([t&&"position: relative ".concat(n,";"),"margin"===r&&"\n padding-left: ".concat(o,"px;\n padding-top: ").concat(a,"px;\n padding-right: ").concat(l,"px;\n margin-left:0;\n margin-top:0;\n margin-right: ").concat(i,"px ").concat(n,";\n "),"padding"===r&&"padding-right: ".concat(i,"px ").concat(n,";")].filter(Boolean).join(""),"\n }\n \n .").concat(er," {\n right: ").concat(i,"px ").concat(n,";\n }\n \n .").concat(en," {\n margin-right: ").concat(i,"px ").concat(n,";\n }\n \n .").concat(er," .").concat(er," {\n right: 0 ").concat(n,";\n }\n \n .").concat(en," .").concat(en," {\n margin-right: 0 ").concat(n,";\n }\n \n body[").concat(ew,"] {\n ").concat("--removed-body-scroll-bar-size",": ").concat(i,"px;\n }\n")},ex=function(){var e=parseInt(document.body.getAttribute(ew)||"0",10);return isFinite(e)?e:0},eA=function(){l.useEffect(function(){return document.body.setAttribute(ew,(ex()+1).toString()),function(){var e=ex()-1;e<=0?document.body.removeAttribute(ew):document.body.setAttribute(ew,e.toString())}},[])},eE=function(e){var t=e.noRelative,r=e.noImportant,n=e.gapMode,o=void 0===n?"margin":n;eA();var a=l.useMemo(function(){return ev(o)},[o]);return l.createElement(eg,{styles:ek(a,!t,o,r?"":"!important")})},eC=!1;if("undefined"!=typeof window)try{var eM=Object.defineProperty({},"passive",{get:function(){return eC=!0,!0}});window.addEventListener("test",eM,eM),window.removeEventListener("test",eM,eM)}catch(e){eC=!1}var eS=!!eC&&{passive:!1},eR=function(e,t){if(!(e instanceof Element))return!1;var r=window.getComputedStyle(e);return"hidden"!==r[t]&&(r.overflowY!==r.overflowX||"TEXTAREA"===e.tagName||"visible"!==r[t])},eO=function(e,t){var r=t.ownerDocument,n=t;do{if("undefined"!=typeof ShadowRoot&&n instanceof ShadowRoot&&(n=n.host),ej(e,n)){var o=eD(e,n);if(o[1]>o[2])return!0}n=n.parentNode}while(n&&n!==r.body);return!1},ej=function(e,t){return"v"===e?eR(t,"overflowY"):eR(t,"overflowX")},eD=function(e,t){return"v"===e?[t.scrollTop,t.scrollHeight,t.clientHeight]:[t.scrollLeft,t.scrollWidth,t.clientWidth]},eP=function(e,t,r,n,o){var a,l=(a=window.getComputedStyle(t).direction,"h"===e&&"rtl"===a?-1:1),i=l*n,s=r.target,c=t.contains(s),u=!1,d=i>0,f=0,p=0;do{if(!s)break;var m=eD(e,s),h=m[0],b=m[1]-m[2]-l*h;(h||b)&&ej(e,s)&&(f+=b,p+=h);var y=s.parentNode;s=y&&y.nodeType===Node.DOCUMENT_FRAGMENT_NODE?y.host:y}while(!c&&s!==document.body||c&&(t.contains(s)||t===s));return d&&(o&&1>Math.abs(f)||!o&&i>f)?u=!0:!d&&(o&&1>Math.abs(p)||!o&&-i>p)&&(u=!0),u},ez=function(e){return"changedTouches"in e?[e.changedTouches[0].clientX,e.changedTouches[0].clientY]:[0,0]},eN=function(e){return[e.deltaX,e.deltaY]},eL=function(e){return e&&"current"in e?e.current:e},eI=0,eT=[];let eF=(n=function(e){var t=l.useRef([]),r=l.useRef([0,0]),n=l.useRef(),o=l.useState(eI++)[0],a=l.useState(em)[0],i=l.useRef(e);l.useEffect(function(){i.current=e},[e]),l.useEffect(function(){if(e.inert){document.body.classList.add("block-interactivity-".concat(o));var t=(function(e,t,r){if(r||2==arguments.length)for(var n,o=0,a=t.length;oMath.abs(c)?"h":"v";if("touches"in e&&"h"===d&&"range"===u.type)return!1;var f=window.getSelection(),p=f&&f.anchorNode;if(p&&(p===u||p.contains(u)))return!1;var m=eO(d,u);if(!m)return!0;if(m?o=d:(o="v"===d?"h":"v",m=eO(d,u)),!m)return!1;if(!n.current&&"changedTouches"in e&&(s||c)&&(n.current=o),!o)return!0;var h=n.current||o;return eP(h,t,e,"h"===h?s:c,!0)},[]),c=l.useCallback(function(e){if(eT.length&&eT[eT.length-1]===a){var r="deltaY"in e?eN(e):ez(e),n=t.current.filter(function(t){var n;return t.name===e.type&&(t.target===e.target||e.target===t.shadowParent)&&(n=t.delta,n[0]===r[0]&&n[1]===r[1])})[0];if(n&&n.should){e.cancelable&&e.preventDefault();return}if(!n){var o=(i.current.shards||[]).map(eL).filter(Boolean).filter(function(t){return t.contains(e.target)});(o.length>0?s(e,o[0]):!i.current.noIsolation)&&e.cancelable&&e.preventDefault()}}},[]),u=l.useCallback(function(e,r,n,o){var a={name:e,delta:r,target:n,should:o,shadowParent:function(e){for(var t=null;null!==e;)e instanceof ShadowRoot&&(t=e.host,e=e.host),e=e.parentNode;return t}(n)};t.current.push(a),setTimeout(function(){t.current=t.current.filter(function(e){return e!==a})},1)},[]),d=l.useCallback(function(e){r.current=ez(e),n.current=void 0},[]),f=l.useCallback(function(t){u(t.type,eN(t),t.target,s(t,e.lockRef.current))},[]),p=l.useCallback(function(t){u(t.type,ez(t),t.target,s(t,e.lockRef.current))},[]);l.useEffect(function(){return eT.push(a),e.setCallbacks({onScrollCapture:f,onWheelCapture:f,onTouchMoveCapture:p}),document.addEventListener("wheel",c,eS),document.addEventListener("touchmove",c,eS),document.addEventListener("touchstart",d,eS),function(){eT=eT.filter(function(e){return e!==a}),document.removeEventListener("wheel",c,eS),document.removeEventListener("touchmove",c,eS),document.removeEventListener("touchstart",d,eS)}},[]);var m=e.removeScrollBar,h=e.inert;return l.createElement(l.Fragment,null,h?l.createElement(a,{styles:"\n .block-interactivity-".concat(o," {pointer-events: none;}\n .allow-interactivity-").concat(o," {pointer-events: all;}\n")}):null,m?l.createElement(eE,{noRelative:e.noRelative,gapMode:e.gapMode}):null)},es.useMedium(n),ed);var e_=l.forwardRef(function(e,t){return l.createElement(eu,ee({},e,{ref:t,sideCar:eF}))});e_.classNames=eu.classNames;var eW=new WeakMap,eG=new WeakMap,eK={},eV=0,eH=function(e){return e&&(e.host||eH(e.parentNode))},eB=function(e,t,r,n){var o=(Array.isArray(e)?e:[e]).map(function(e){if(t.contains(e))return e;var r=eH(e);return r&&t.contains(r)?r:(console.error("aria-hidden",e,"in not contained inside",t,". Doing nothing"),null)}).filter(function(e){return!!e});eK[r]||(eK[r]=new WeakMap);var a=eK[r],l=[],i=new Set,s=new Set(o),c=function(e){!e||i.has(e)||(i.add(e),c(e.parentNode))};o.forEach(c);var u=function(e){!e||s.has(e)||Array.prototype.forEach.call(e.children,function(e){if(i.has(e))u(e);else try{var t=e.getAttribute(n),o=null!==t&&"false"!==t,s=(eW.get(e)||0)+1,c=(a.get(e)||0)+1;eW.set(e,s),a.set(e,c),l.push(e),1===s&&o&&eG.set(e,!0),1===c&&e.setAttribute(r,"true"),o||e.setAttribute(n,"true")}catch(t){console.error("aria-hidden: cannot operate on ",e,t)}})};return u(t),i.clear(),eV++,function(){l.forEach(function(e){var t=eW.get(e)-1,o=a.get(e)-1;eW.set(e,t),a.set(e,o),t||(eG.has(e)||e.removeAttribute(n),eG.delete(e)),o||e.removeAttribute(r)}),--eV||(eW=new WeakMap,eW=new WeakMap,eG=new WeakMap,eK={})}},eq=function(e,t,r){void 0===r&&(r="data-aria-hidden");var n=Array.from(Array.isArray(e)?e:[e]),o=t||("undefined"==typeof document?null:(Array.isArray(e)?e[0]:e).ownerDocument.body);return o?(n.push.apply(n,Array.from(o.querySelectorAll("[aria-live], script"))),eB(n,o,r,"aria-hidden")):function(){return null}},e$=r(2467),eU=Object.defineProperty,eZ=(e,t)=>eU(e,"name",{value:t,configurable:!0}),eX="Dialog",[eY,eJ]=(0,c.A)(eX),[eQ,e0]=eY(eX),e1=eZ(e=>{let{__scopeDialog:t,children:r,open:n,defaultOpen:o,onOpenChange:a,modal:i=!0}=e,s=l.useRef(null),c=l.useRef(null),[f,p]=(0,d.i)({prop:n,defaultProp:null!=o&&o,onChange:a,caller:eX}),[h,b]=l.useState(0),[y,v]=l.useState(0);return(0,m.jsx)(eQ,{scope:t,triggerRef:s,contentRef:c,contentId:(0,u.B)(),titleId:(0,u.B)(),descriptionId:(0,u.B)(),titlePresent:h>0,descriptionPresent:y>0,setTitleCount:b,setDescriptionCount:v,open:f,onOpenChange:p,onOpenToggle:l.useCallback(()=>p(e=>!e),[p]),modal:i,children:r})},"Dialog"),e2="DialogPortal",[e4,e5]=eY(e2,{forceMount:void 0}),e9=eZ(e=>{let{__scopeDialog:t,forceMount:r,children:n,container:o}=e,a=e0(e2,t);return(0,m.jsx)(e4,{scope:t,forceMount:r,children:l.Children.map(n,e=>(0,m.jsx)($.C,{present:r||a.open,children:(0,m.jsx)(q,{asChild:!0,container:o,children:e})}))})},"DialogPortal"),e3="DialogOverlay",e6=l.forwardRef(eZ(function(e,t){let r=e5(e3,e.__scopeDialog),{forceMount:n=r.forceMount,...o}=e,a=e0(e3,e.__scopeDialog);return a.modal?(0,m.jsx)($.C,{present:n||a.open,children:(0,m.jsx)(e8,{...o,ref:t})}):null},"DialogOverlay")),e7=(0,e$.TL)("DialogOverlay.RemoveScroll"),e8=l.forwardRef(eZ(function(e,t){let{__scopeDialog:r,...n}=e,o=e0(e3,r),a=w(),l=(0,s.s)(t,a);return(0,m.jsx)(e_,{as:e7,allowPinchZoom:!0,shards:[o.contentRef],children:(0,m.jsx)(f.sG.div,{"data-state":ts(o.open),...n,ref:l,style:{pointerEvents:"auto",...n.style}})})},"DialogOverlayImpl")),te="DialogContent",tt=l.forwardRef(eZ(function(e,t){let r=e5(te,e.__scopeDialog),{forceMount:n=r.forceMount,...o}=e,a=e0(te,e.__scopeDialog);return(0,m.jsx)($.C,{present:n||a.open,children:a.modal?(0,m.jsx)(tr,{...o,ref:t}):(0,m.jsx)(tn,{...o,ref:t})})},"DialogContent")),tr=l.forwardRef(eZ(function(e,t){let r=e0(te,e.__scopeDialog),n=l.useRef(null),o=(0,s.s)(t,r.contentRef,n);return l.useEffect(()=>{let e=n.current;if(e)return eq(e)},[]),(0,m.jsx)(to,{...e,ref:o,trapFocus:r.open,disableOutsidePointerEvents:r.open,onCloseAutoFocus:(0,i.mK)(e.onCloseAutoFocus,e=>{var t;e.preventDefault(),null==(t=r.triggerRef.current)||t.focus()}),onPointerDownOutside:(0,i.mK)(e.onPointerDownOutside,e=>{let t=e.detail.originalEvent,r=0===t.button&&!0===t.ctrlKey;(2===t.button||r)&&e.preventDefault()}),onFocusOutside:(0,i.mK)(e.onFocusOutside,e=>e.preventDefault())})},"DialogContentModal")),tn=l.forwardRef(eZ(function(e,t){let r=e0(te,e.__scopeDialog),n=l.useRef(!1),o=l.useRef(!1);return(0,m.jsx)(to,{...e,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:t=>{var a,l;null==(a=e.onCloseAutoFocus)||a.call(e,t),t.defaultPrevented||(n.current||null==(l=r.triggerRef.current)||l.focus(),t.preventDefault()),n.current=!1,o.current=!1},onInteractOutside:t=>{var a,l;null==(a=e.onInteractOutside)||a.call(e,t),t.defaultPrevented||(n.current=!0,"pointerdown"===t.detail.originalEvent.type&&(o.current=!0));let i=t.target;(null==(l=r.triggerRef.current)?void 0:l.contains(i))&&t.preventDefault(),"focusin"===t.detail.originalEvent.type&&o.current&&t.preventDefault()}})},"DialogContentNonModal")),to=l.forwardRef(eZ(function(e,t){let{__scopeDialog:r,trapFocus:n,onOpenAutoFocus:o,onCloseAutoFocus:a,...l}=e,i=e0(te,r);return J(),(0,m.jsx)(m.Fragment,{children:(0,m.jsx)(D,{asChild:!0,loop:!0,trapped:n,onMountAutoFocus:o,onUnmountAutoFocus:a,children:(0,m.jsx)(g,{role:"dialog",id:i.contentId,"aria-describedby":i.descriptionPresent?i.descriptionId:void 0,"aria-labelledby":i.titlePresent?i.titleId:void 0,"data-state":ts(i.open),...l,ref:t,deferPointerDownOutside:!0,onDismiss:()=>i.onOpenChange(!1)})})})},"DialogContentImpl")),ta=l.forwardRef(eZ(function(e,t){let{__scopeDialog:r,...n}=e,o=e0("DialogTitle",r),{setTitleCount:a}=o;return(0,H.N)(()=>(a(e=>e+1),()=>a(e=>e-1)),[a]),(0,m.jsx)(f.sG.h2,{id:o.titleId,...n,ref:t})},"DialogTitle")),tl=l.forwardRef(eZ(function(e,t){let{__scopeDialog:r,...n}=e,o=e0("DialogDescription",r),{setDescriptionCount:a}=o;return(0,H.N)(()=>(a(e=>e+1),()=>a(e=>e-1)),[a]),(0,m.jsx)(f.sG.p,{id:o.descriptionId,...n,ref:t})},"DialogDescription")),ti=l.forwardRef(eZ(function(e,t){let{__scopeDialog:r,...n}=e,o=e0("DialogClose",r);return(0,m.jsx)(f.sG.button,{type:"button",...n,ref:t,onClick:(0,i.mK)(e.onClick,()=>o.onOpenChange(!1))})},"DialogClose"));function ts(e){return e?"open":"closed"}eZ(ts,"getState")},4381:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Hash",[["line",{x1:"4",x2:"20",y1:"9",y2:"9",key:"4lhtct"}],["line",{x1:"4",x2:"20",y1:"15",y2:"15",key:"vyu0kd"}],["line",{x1:"10",x2:"8",y1:"3",y2:"21",key:"1ggp8o"}],["line",{x1:"16",x2:"14",y1:"3",y2:"21",key:"weycgp"}]])},4446:(e,t,r)=>{r.d(t,{s:()=>s});var n=r(2115),o=Object.defineProperty,a=(e,t)=>o(e,"name",{value:t,configurable:!0});function l(e,t){if("function"==typeof e)return e(t);null!=e&&(e.current=t)}function i(...e){return t=>{let r=!1,n=e.map(e=>{let n=l(e,t);return r||"function"!=typeof n||(r=!0),n});if(r)return()=>{for(let t=0;t{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Gauge",[["path",{d:"m12 14 4-4",key:"9kzdfg"}],["path",{d:"M3.34 19a10 10 0 1 1 17.32 0",key:"19p75a"}]])},5148:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("ScrollText",[["path",{d:"M15 12h-5",key:"r7krc0"}],["path",{d:"M15 8h-5",key:"1khuty"}],["path",{d:"M19 17V5a2 2 0 0 0-2-2H4",key:"zz82l3"}],["path",{d:"M8 21h12a2 2 0 0 0 2-2v-1a1 1 0 0 0-1-1H11a1 1 0 0 0-1 1v1a2 2 0 1 1-4 0V5a2 2 0 1 0-4 0v2a1 1 0 0 0 1 1h3",key:"1ph1d7"}]])},5225:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("FileText",[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z",key:"1rqfz7"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4",key:"tnqrlb"}],["path",{d:"M10 9H8",key:"b1mrlr"}],["path",{d:"M16 13H8",key:"t4e002"}],["path",{d:"M16 17H8",key:"z1uh3a"}]])},5524:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("CodeXml",[["path",{d:"m18 16 4-4-4-4",key:"1inbqp"}],["path",{d:"m6 8-4 4 4 4",key:"15zrgr"}],["path",{d:"m14.5 4-5 16",key:"e7oirm"}]])},5544:(e,t,r)=>{r.d(t,{UC:()=>eS,B8:()=>eC,bL:()=>eE,l9:()=>eM});var n,o,a=r(2115),l=r.t(a,2),i=r(2556),s=r(3468);function c(e,t,r){if(!t.has(e))throw TypeError("attempted to "+r+" private field on non-instance");return t.get(e)}function u(e,t){var r=c(e,t,"get");return r.get?r.get.call(e):r.value}function d(e,t,r){var n=c(e,t,"set");if(n.set)n.set.call(e,r);else{if(!n.writable)throw TypeError("attempted to set read only private field");n.value=r}return r}var f=r(4446),p=r(2467),m=r(5155),h=Object.defineProperty,b=(e,t)=>h(e,"name",{value:t,configurable:!0});function y(e){let t=e+"CollectionProvider",[r,n]=(0,s.A)(t),[o,l]=r(t,{collectionRef:{current:null},itemMap:new Map}),i=b(e=>{let{scope:t,children:r}=e,n=a.useRef(null),l=a.useRef(new Map).current;return(0,m.jsx)(o,{scope:t,itemMap:l,collectionRef:n,children:r})},"CollectionProvider");i.displayName=t;let c=e+"CollectionSlot",u=(0,p.TL)(c),d=a.forwardRef((e,t)=>{let{scope:r,children:n}=e,o=l(c,r),a=(0,f.s)(t,o.collectionRef);return(0,m.jsx)(u,{ref:a,children:n})});d.displayName=c;let h=e+"CollectionItemSlot",y="data-radix-collection-item",v=(0,p.TL)(h),g=a.forwardRef((e,t)=>{let{scope:r,children:n,...o}=e,i=a.useRef(null),s=(0,f.s)(t,i),c=l(h,r);return a.useEffect(()=>(c.itemMap.set(i,{ref:i,...o}),()=>void c.itemMap.delete(i))),(0,m.jsx)(v,{...{[y]:""},ref:s,children:n})});function w(t){let r=l(e+"CollectionConsumer",t);return a.useCallback(()=>{let e=r.collectionRef.current;if(!e)return[];let t=Array.from(e.querySelectorAll("[".concat(y,"]")));return Array.from(r.itemMap.values()).sort((e,r)=>t.indexOf(e.ref.current)-t.indexOf(r.ref.current))},[r.collectionRef,r.itemMap])}return g.displayName=h,b(w,"useCollection"),[{Provider:i,Slot:d,ItemSlot:g},w,n]}b(y,"createCollection");var v=new WeakMap,g=(n=new WeakMap,b(o=class e extends Map{set(e,t){return v.get(this)&&(this.has(e)?u(this,n)[u(this,n).indexOf(e)]=e:u(this,n).push(e)),super.set(e,t),this}insert(e,t,r){let o,a=this.has(t),l=u(this,n).length,i=x(e),s=i>=0?i:l+i,c=s<0||s>=l?-1:s;if(c===this.size||a&&c===this.size-1||-1===c)return this.set(t,r),this;let d=this.size+ +!a;i<0&&s++;let f=[...u(this,n)],p=!1;for(let e=s;e=this.size&&(n=this.size-1),this.at(n)}keyFrom(e,t){let r=this.indexOf(e);if(-1===r)return;let n=r+t;return n<0&&(n=0),n>=this.size&&(n=this.size-1),this.keyAt(n)}find(e,t){let r=0;for(let n of this){if(Reflect.apply(e,t,[n,r,this]))return n;r++}}findIndex(e,t){let r=0;for(let n of this){if(Reflect.apply(e,t,[n,r,this]))return r;r++}return -1}filter(t,r){let n=[],o=0;for(let e of this)Reflect.apply(t,r,[e,o,this])&&n.push(e),o++;return new e(n)}map(t,r){let n=[],o=0;for(let e of this)n.push([e[0],Reflect.apply(t,r,[e,o,this])]),o++;return new e(n)}reduce(){for(var e=arguments.length,t=Array(e),r=0;r=0;e--){let r=this.at(e);a=e===this.size-1&&1===t.length?r:Reflect.apply(n,this,[a,r,e,this])}return a}toSorted(t){return new e([...this.entries()].sort(t))}toReversed(){let t=new e;for(let e=this.size-1;e>=0;e--){let r=this.keyAt(e),n=this.get(r);t.set(r,n)}return t}toSpliced(){for(var t=arguments.length,r=Array(t),n=0;n0&&(o=r-1);for(let e=t;e<=o;e++){let t=this.keyAt(e),r=this.get(t);n.set(t,r)}return n}every(e,t){let r=0;for(let n of this){if(!Reflect.apply(e,t,[n,r,this]))return!1;r++}return!0}some(e,t){let r=0;for(let n of this){if(Reflect.apply(e,t,[n,r,this]))return!0;r++}return!1}constructor(e){super(e),function(e,t,r){if(t.has(e))throw TypeError("Cannot initialize the same private elements twice on an object");t.set(e,r)}(this,n,{writable:!0,value:void 0}),d(this,n,[...super.keys()]),v.set(this,!0)}},"OrderedDict"),o);function w(e,t){if("at"in Array.prototype)return Array.prototype.at.call(e,t);let r=k(e,t);return -1===r?void 0:e[r]}function k(e,t){let r=e.length,n=x(t),o=n>=0?n:r+n;return o<0||o>=r?-1:o}function x(e){return e!=e||0===e?0:Math.trunc(e)}function A(e,t){if(e===t)return!0;if("object"!=typeof e||"object"!=typeof t||null==e||null==t)return!1;let r=Object.keys(e),n=Object.keys(t);if(r.length!==n.length)return!1;for(let n of r)if(!Object.prototype.hasOwnProperty.call(t,n)||e[n]!==t[n])return!1;return!0}function E(e,t){return!!(t.compareDocumentPosition(e)&Node.DOCUMENT_POSITION_PRECEDING)}function C(e,t){return e[1].element&&t[1].element?E(e[1].element,t[1].element)?-1:1:0}function M(e){return new MutationObserver(t=>{for(let r of t)if("childList"===r.type)return void e()})}b(w,"at"),b(k,"toSafeIndex"),b(x,"toSafeInteger"),b(function(e){let t=e+"CollectionProvider",[r,n]=(0,s.A)(t),[o,l]=r(t,{collectionElement:null,collectionRef:{current:null},collectionRefObject:{current:null},itemMap:new g,setItemMap:b(()=>void 0,"setItemMap")}),i=b(e=>{let{state:t,...r}=e;return t?(0,m.jsx)(u,{...r,state:t}):(0,m.jsx)(c,{...r})},"CollectionProvider");i.displayName=t;let c=b(e=>{let t=x();return(0,m.jsx)(u,{...e,state:t})},"CollectionInit");c.displayName=t+"Init";let u=b(e=>{let{scope:t,children:r,state:n}=e,l=a.useRef(null),[i,s]=a.useState(null),c=(0,f.s)(l,s),[u,d]=n;return a.useEffect(()=>{if(!i)return;let e=M(()=>{});return e.observe(i,{childList:!0,subtree:!0}),()=>{e.disconnect()}},[i]),(0,m.jsx)(o,{scope:t,itemMap:u,setItemMap:d,collectionRef:c,collectionRefObject:l,collectionElement:i,children:r})},"CollectionProviderImpl");u.displayName=t+"Impl";let d=e+"CollectionSlot",h=(0,p.TL)(d),y=a.forwardRef((e,t)=>{let{scope:r,children:n}=e,o=l(d,r),a=(0,f.s)(t,o.collectionRef);return(0,m.jsx)(h,{ref:a,children:n})});y.displayName=d;let v=e+"CollectionItemSlot",w=(0,p.TL)(v),k=a.forwardRef((e,t)=>{let{scope:r,children:n,...o}=e,i=a.useRef(null),[s,c]=a.useState(null),u=(0,f.s)(t,i,c),{setItemMap:d}=l(v,r),p=a.useRef(o);A(p.current,o)||(p.current=o);let h=p.current;return a.useEffect(()=>(d(e=>s?e.has(s)?e.set(s,{...h,element:s}).toSorted(C):(e.set(s,{...h,element:s}),e.toSorted(C)):e),()=>{d(e=>s&&e.has(s)?(e.delete(s),new g(e)):e)}),[s,h,d]),(0,m.jsx)(w,{"data-radix-collection-item":"",ref:u,children:n})});function x(){return a.useState(new g)}function E(t){let{itemMap:r}=l(e+"CollectionConsumer",t);return r}return k.displayName=v,b(x,"useInitCollection"),b(E,"useCollection"),[{Provider:i,Slot:y,ItemSlot:k},{createCollectionScope:n,useCollection:E,useInitCollection:x}]},"createCollection"),b(A,"shallowEqual"),b(E,"isElementPreceding"),b(C,"sortByDocumentPosition"),b(M,"getChildListObserver");var S=r(8946),R=r(7602),O=r(222),j=r(9478),D=Object.defineProperty,P=a.createContext(void 0);function z(e){let t=a.useContext(P);return e||t||"ltr"}D(z,"name",{value:"useDirection",configurable:!0});var N=r(4129),L=Object.defineProperty,I=(e,t)=>L(e,"name",{value:t,configurable:!0}),T=!1;function F(){let[e,t]=a.useState(T);return a.useEffect(()=>{T||(T=!0,t(!0))},[]),e}I(F,"useIsHydrated");var _=l[" useSyncExternalStore ".trim().toString()];function W(){return()=>{}}function G(){return _(W,()=>!0,()=>!1)}I(W,"subscribe"),I(G,"useIsHydratedModern");var K="function"==typeof _?G:F,V=Object.defineProperty,H=(e,t)=>V(e,"name",{value:t,configurable:!0}),B="rovingFocusGroup.onEntryFocus",q={bubbles:!1,cancelable:!0},$="RovingFocusGroup",[U,Z,X]=y($),[Y,J]=(0,s.A)($,[X]),[Q,ee]=Y($),et=a.forwardRef(H(function(e,t){return(0,m.jsx)(U.Provider,{scope:e.__scopeRovingFocusGroup,children:(0,m.jsx)(U.Slot,{scope:e.__scopeRovingFocusGroup,children:(0,m.jsx)(er,{...e,ref:t})})})},"RovingFocusGroup")),er=a.forwardRef(H(function(e,t){let{__scopeRovingFocusGroup:r,orientation:n,loop:o=!1,dir:l,currentTabStopId:s,defaultCurrentTabStopId:c,onCurrentTabStopIdChange:u,onEntryFocus:d,preventScrollOnEntryFocus:p=!1,...h}=e,b=a.useRef(null),y=(0,f.s)(t,b),v=z(l),[g,w]=(0,j.i)({prop:s,defaultProp:null!=c?c:null,onChange:u,caller:$}),[k,x]=a.useState(!1),A=(0,O.c)(d),E=Z(r),C=a.useRef(!1),[M,S]=a.useState(0);return a.useEffect(()=>{let e=b.current;if(e)return e.addEventListener(B,A),()=>e.removeEventListener(B,A)},[A]),(0,m.jsx)(Q,{scope:r,orientation:n,dir:v,loop:o,currentTabStopId:g,onItemFocus:a.useCallback(e=>w(e),[w]),onItemShiftTab:a.useCallback(()=>x(!0),[]),onFocusableItemAdd:a.useCallback(()=>S(e=>e+1),[]),onFocusableItemRemove:a.useCallback(()=>S(e=>e-1),[]),children:(0,m.jsx)(R.sG.div,{tabIndex:k||0===M?-1:0,"data-orientation":n,...h,ref:y,style:{outline:"none",...e.style},onMouseDown:(0,i.mK)(e.onMouseDown,()=>{C.current=!0}),onFocus:(0,i.mK)(e.onFocus,e=>{let t=!C.current;if(e.target===e.currentTarget&&t&&!k){let t=new CustomEvent(B,q);if(e.currentTarget.dispatchEvent(t),!t.defaultPrevented){let e=E().filter(e=>e.focusable);ei([e.find(e=>e.active),e.find(e=>e.id===g),...e].filter(Boolean).map(e=>e.ref.current),p)}}C.current=!1}),onBlur:(0,i.mK)(e.onBlur,()=>x(!1))})})},"RovingFocusGroupImpl")),en=a.forwardRef(H(function(e,t){let{__scopeRovingFocusGroup:r,focusable:n=!0,active:o=!1,tabStopId:l,children:s,...c}=e,u=(0,S.B)(),d=l||u,f=ee("RovingFocusGroupItem",r),p=f.currentTabStopId===d,h=Z(r),{onFocusableItemAdd:b,onFocusableItemRemove:y,currentTabStopId:v}=f,g=K();return(0,N.N)(()=>{if(g&&n)return b(),()=>y()},[g,n,b,y]),a.useEffect(()=>{if(!g&&n)return b(),()=>y()},[g,n,b,y]),(0,m.jsx)(U.ItemSlot,{scope:r,id:d,focusable:n,active:o,children:(0,m.jsx)(R.sG.span,{tabIndex:p?0:-1,"data-orientation":f.orientation,...c,ref:t,onMouseDown:(0,i.mK)(e.onMouseDown,e=>{n?f.onItemFocus(d):e.preventDefault()}),onFocus:(0,i.mK)(e.onFocus,()=>f.onItemFocus(d)),onKeyDown:(0,i.mK)(e.onKeyDown,e=>{if("Tab"===e.key&&e.shiftKey)return void f.onItemShiftTab();if(e.target!==e.currentTarget)return;let t=el(e,f.orientation,f.dir);if(void 0!==t){if(e.metaKey||e.ctrlKey||e.altKey||e.shiftKey)return;e.preventDefault();let r=h().filter(e=>e.focusable).map(e=>e.ref.current);if("last"===t)r.reverse();else if("prev"===t||"next"===t){"prev"===t&&r.reverse();let n=r.indexOf(e.currentTarget);r=f.loop?es(r,n+1):r.slice(n+1)}setTimeout(()=>ei(r))}}),children:"function"==typeof s?s({isCurrentTabStop:p,hasTabStop:null!=v}):s})})},"RovingFocusGroupItem")),eo={ArrowLeft:"prev",ArrowUp:"prev",ArrowRight:"next",ArrowDown:"next",PageUp:"first",Home:"first",PageDown:"last",End:"last"};function ea(e,t){return"rtl"!==t?e:"ArrowLeft"===e?"ArrowRight":"ArrowRight"===e?"ArrowLeft":e}function el(e,t,r){let n=ea(e.key,r);if(!("vertical"===t&&["ArrowLeft","ArrowRight"].includes(n))&&!("horizontal"===t&&["ArrowUp","ArrowDown"].includes(n)))return eo[n]}function ei(e){let t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],r=document.activeElement;for(let n of e)if(n===r||(n.focus({preventScroll:t}),document.activeElement!==r))return}function es(e,t){return e.map((r,n)=>e[(t+n)%e.length])}H(ea,"getDirectionAwareKey"),H(el,"getFocusIntent"),H(ei,"focusFirst"),H(es,"wrapArray");var ec=r(6842),eu=Object.defineProperty,ed=(e,t)=>eu(e,"name",{value:t,configurable:!0}),ef="Tabs",[ep,em]=(0,s.A)(ef,[J]),eh=J(),[eb,ey]=ep(ef),ev=a.forwardRef(ed(function(e,t){let{__scopeTabs:r,value:n,onValueChange:o,defaultValue:a,orientation:l="horizontal",dir:i,activationMode:s="automatic",...c}=e,u=z(i),[d,f]=(0,j.i)({prop:n,onChange:o,defaultProp:null!=a?a:"",caller:ef});return(0,m.jsx)(eb,{scope:r,baseId:(0,S.B)(),value:d,onValueChange:f,orientation:l,dir:u,activationMode:s,children:(0,m.jsx)(R.sG.div,{dir:u,"data-orientation":l,...c,ref:t})})},"Tabs")),eg=a.forwardRef(ed(function(e,t){let{__scopeTabs:r,loop:n=!0,...o}=e,a=ey("TabsList",r),l=eh(r);return(0,m.jsx)(et,{asChild:!0,...l,orientation:a.orientation,dir:a.dir,loop:n,children:(0,m.jsx)(R.sG.div,{role:"tablist","aria-orientation":a.orientation,...o,ref:t})})},"TabsList")),ew=a.forwardRef(ed(function(e,t){let{__scopeTabs:r,value:n,disabled:o=!1,...a}=e,l=ey("TabsTrigger",r),s=eh(r),c=ex(l.baseId,n),u=eA(l.baseId,n),d=n===l.value;return(0,m.jsx)(en,{asChild:!0,...s,focusable:!o,active:d,children:(0,m.jsx)(R.sG.button,{type:"button",role:"tab","aria-selected":d,"aria-controls":u,"data-state":d?"active":"inactive","data-disabled":o?"":void 0,disabled:o,id:c,...a,ref:t,onMouseDown:(0,i.mK)(e.onMouseDown,e=>{o||0!==e.button||!1!==e.ctrlKey?e.preventDefault():l.onValueChange(n)}),onKeyDown:(0,i.mK)(e.onKeyDown,e=>{!o&&e.target===e.currentTarget&&[" ","Enter"].includes(e.key)&&l.onValueChange(n)}),onFocus:(0,i.mK)(e.onFocus,()=>{let e="manual"!==l.activationMode;d||o||!e||l.onValueChange(n)})})})},"TabsTrigger")),ek=a.forwardRef(ed(function(e,t){let{__scopeTabs:r,value:n,forceMount:o,children:l,...i}=e,s=ey("TabsContent",r),c=ex(s.baseId,n),u=eA(s.baseId,n),d=n===s.value,f=a.useRef(d);return a.useEffect(()=>{let e=requestAnimationFrame(()=>f.current=!1);return()=>cancelAnimationFrame(e)},[]),(0,m.jsx)(ec.C,{present:o||d,children:r=>{let{present:n}=r;return(0,m.jsx)(R.sG.div,{"data-state":d?"active":"inactive","data-orientation":s.orientation,role:"tabpanel","aria-labelledby":c,hidden:!n,id:u,tabIndex:0,...i,ref:t,style:{...e.style,animationDuration:f.current?"0s":void 0},children:n&&l})}})},"TabsContent"));function ex(e,t){return"".concat(e,"-trigger-").concat(t)}function eA(e,t){return"".concat(e,"-content-").concat(t)}ed(ex,"makeTriggerId"),ed(eA,"makeContentId");var eE=ev,eC=eg,eM=ew,eS=ek},5646:(e,t,r)=>{r.d(t,{UC:()=>S,VY:()=>D,ZD:()=>O,ZL:()=>C,bL:()=>E,hE:()=>j,hJ:()=>M,rc:()=>R});var n=r(2115),o=r(3468),a=r(4446),l=r(4368),i=r(2556),s=r(5155),c=Object.defineProperty,u=(e,t)=>c(e,"name",{value:t,configurable:!0}),[d,f]=(0,o.A)("AlertDialog",[l.Hs]),p=(0,l.Hs)(),m=u(e=>{let{__scopeAlertDialog:t,...r}=e,n=p(t);return(0,s.jsx)(l.bL,{...n,...r,modal:!0})},"AlertDialog"),h=u(e=>{let{__scopeAlertDialog:t,...r}=e,n=p(t);return(0,s.jsx)(l.ZL,{...n,...r})},"AlertDialogPortal"),b=n.forwardRef(u(function(e,t){let{__scopeAlertDialog:r,...n}=e,o=p(r);return(0,s.jsx)(l.hJ,{...o,...n,ref:t})},"AlertDialogOverlay")),[y,v]=d("AlertDialogContent"),g=n.forwardRef(u(function(e,t){let{__scopeAlertDialog:r,children:o,...c}=e,u=p(r),d=n.useRef(null),f=(0,a.s)(t,d),m=n.useRef(null);return(0,s.jsx)(y,{scope:r,cancelRef:m,children:(0,s.jsx)(l.UC,{role:"alertdialog",...u,...c,ref:f,onOpenAutoFocus:(0,i.mK)(c.onOpenAutoFocus,e=>{var t;e.preventDefault(),null==(t=m.current)||t.focus({preventScroll:!0})}),onPointerDownOutside:e=>e.preventDefault(),onInteractOutside:e=>e.preventDefault(),children:o})})},"AlertDialogContent")),w=n.forwardRef(u(function(e,t){let{__scopeAlertDialog:r,...n}=e,o=p(r);return(0,s.jsx)(l.hE,{...o,...n,ref:t})},"AlertDialogTitle")),k=n.forwardRef(u(function(e,t){let{__scopeAlertDialog:r,...n}=e,o=p(r);return(0,s.jsx)(l.VY,{...o,...n,ref:t})},"AlertDialogDescription")),x=n.forwardRef(u(function(e,t){let{__scopeAlertDialog:r,...n}=e,o=p(r);return(0,s.jsx)(l.bm,{...o,...n,ref:t})},"AlertDialogAction")),A=n.forwardRef(u(function(e,t){let{__scopeAlertDialog:r,...n}=e,{cancelRef:o}=v("AlertDialogCancel",r),i=p(r),c=(0,a.s)(t,o);return(0,s.jsx)(l.bm,{...i,...n,ref:c})},"AlertDialogCancel")),E=m,C=h,M=b,S=g,R=x,O=A,j=w,D=k},5889:(e,t,r)=>{r.d(t,{QP:()=>ep});let n=(e=new Map,t=null,r)=>({nextPart:e,validators:t,classGroupId:r}),o=[],a=(e,t,r)=>{if(0==e.length-t)return r.classGroupId;let n=e[t],o=r.nextPart.get(n);if(o){let r=a(e,t+1,o);if(r)return r}let l=r.validators;if(null===l)return;let i=0===t?e.join("-"):e.slice(t).join("-"),s=l.length;for(let e=0;e{let r=n();for(let n in e)i(e[n],r,n,t);return r},i=(e,t,r,n)=>{let o=e.length;for(let a=0;a"string"==typeof e?void c(e,t,r):"function"==typeof e?void u(e,t,r,n):void d(e,t,r,n),c=(e,t,r)=>{(""===e?t:f(t,e)).classGroupId=r},u=(e,t,r,n)=>{if(p(e))return void i(e(n),t,r,n);null===t.validators&&(t.validators=[]),t.validators.push({classGroupId:r,validator:e})},d=(e,t,r,n)=>{let o=Object.entries(e),a=o.length;for(let e=0;e{let r=e,o=t.split("-"),a=o.length;for(let e=0;e"isThemeGetter"in e&&!0===e.isThemeGetter,m=[],h=(e,t,r,n,o)=>({modifiers:e,hasImportantModifier:t,baseClassName:r,maybePostfixModifierPosition:n,isExternal:o}),b=/\s+/,y=e=>{let t;if("string"==typeof e)return e;let r="";for(let n=0;n{let t=t=>t[e]||v;return t.isThemeGetter=!0,t.themeKey=e,t},w=/^\[(?:(\w[\w-]*):)?(.+)\]$/i,k=/^\((?:(\w[\w-]*):)?(.+)\)$/i,x=/^\d+(?:\.\d+)?\/\d+(?:\.\d+)?$/,A=/^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/,E=/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/,C=/^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix|color|light-dark)\(.+\)$/,M=/^(inset_)?-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/,S=/^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/,R=e=>x.test(e),O=e=>!!e&&!Number.isNaN(Number(e)),j=e=>!!e&&Number.isInteger(Number(e)),D=e=>e.endsWith("%")&&O(e.slice(0,-1)),P=e=>A.test(e),z=()=>!0,N=e=>E.test(e)&&!C.test(e),L=()=>!1,I=e=>M.test(e),T=e=>S.test(e),F=e=>!G(e)&&!Z(e),_=e=>e.startsWith("@container")&&("/"===e[10]&&void 0!==e[11]||"s"===e[11]&&void 0!==e[16]&&e.startsWith("-size/",10)||"n"===e[11]&&void 0!==e[18]&&e.startsWith("-normal/",10)),W=e=>en(e,ei,L),G=e=>w.test(e),K=e=>en(e,es,N),V=e=>en(e,ec,O),H=e=>en(e,ed,z),B=e=>en(e,eu,L),q=e=>en(e,ea,L),$=e=>en(e,el,T),U=e=>en(e,ef,I),Z=e=>k.test(e),X=e=>eo(e,es),Y=e=>eo(e,eu),J=e=>eo(e,ea),Q=e=>eo(e,ei),ee=e=>eo(e,el),et=e=>eo(e,ef,!0),er=e=>eo(e,ed,!0),en=(e,t,r)=>{let n=w.exec(e);return!!n&&(n[1]?t(n[1]):r(n[2]))},eo=(e,t,r=!1)=>{let n=k.exec(e);return!!n&&(n[1]?t(n[1]):r)},ea=e=>"position"===e||"percentage"===e,el=e=>"image"===e||"url"===e,ei=e=>"length"===e||"size"===e||"bg-size"===e,es=e=>"length"===e,ec=e=>"number"===e,eu=e=>"family-name"===e,ed=e=>"number"===e||"weight"===e,ef=e=>"shadow"===e;Symbol.toStringTag;let ep=((e,...t)=>{let r,n,i,s,c=e=>{let t=n(e);if(t)return t;let o=((e,t)=>{let{parseClassName:r,getClassGroupId:n,getConflictingClassGroupIds:o,sortModifiers:a,postfixLookupClassGroupIds:l}=t,i=[],s=e.trim().split(b),c="";for(let e=s.length-1;e>=0;e-=1){let t,u=s[e],{isExternal:d,modifiers:f,hasImportantModifier:p,baseClassName:m,maybePostfixModifierPosition:h}=r(u);if(d){c=u+(c.length>0?" "+c:c);continue}let b=!!h;if(b){let e=(t=n(m.substring(0,h)))&&l[t]?n(m):void 0;e&&e!==t&&(t=e,b=!1)}else t=n(m);if(!t){if(!b||!(t=n(m))){c=u+(c.length>0?" "+c:c);continue}b=!1}let y=0===f.length?"":1===f.length?f[0]:a(f).join(":"),v=p?y+"!":y,g=v+t;if(i.indexOf(g)>-1)continue;i.push(g);let w=o(t,b);for(let e=0;e0?" "+c:c)}return c})(e,r);return i(e,o),o};return s=u=>(n=(r=(e=>({cache:(e=>{if(e<1)return{get:()=>void 0,set:()=>{}};let t=0,r=Object.create(null),n=Object.create(null),o=(o,a)=>{r[o]=a,++t>e&&(t=0,n=r,r=Object.create(null))};return{get(e){let t=r[e];return void 0!==t?t:void 0!==(t=n[e])?(o(e,t),t):void 0},set(e,t){e in r?r[e]=t:o(e,t)}}})(e.cacheSize),parseClassName:(e=>{let{prefix:t,experimentalParseClassName:r}=e,n=e=>{let t,r=[],n=0,o=0,a=0,l=e.length;for(let i=0;ia?t-a:void 0)};if(t){let e=t+":",r=n;n=t=>t.startsWith(e)?r(t.slice(e.length)):h(m,!1,t,void 0,!0)}if(r){let e=n;n=t=>r({className:t,parseClassName:e})}return n})(e),sortModifiers:(e=>{let t=new Map;return e.orderSensitiveModifiers.forEach((e,r)=>{t.set(e,1e6+r)}),e=>{let r=[],n=[];for(let o=0;o0&&(n.sort(),r.push(...n),n=[]),r.push(a)):n.push(a)}return n.length>0&&(n.sort(),r.push(...n)),r}})(e),postfixLookupClassGroupIds:(e=>{let t=Object.create(null),r=e.postfixLookupClassGroups;if(r)for(let e=0;e{let t=(e=>{let{theme:t,classGroups:r}=e;return l(r,t)})(e),{conflictingClassGroups:r,conflictingClassGroupModifiers:n}=e;return{getClassGroupId:e=>{if(e.startsWith("[")&&e.endsWith("]"))return(e=>-1===e.slice(1,-1).indexOf(":")?void 0:(()=>{let t=e.slice(1,-1),r=t.indexOf(":"),n=t.slice(0,r);return n?"arbitrary.."+n:void 0})())(e);let r=e.split("-"),n=+(""===r[0]&&r.length>1);return a(r,n,t)},getConflictingClassGroupIds:(e,t)=>{if(t){let t=n[e],a=r[e];return t?a?((e,t)=>{let r=Array(e.length+t.length);for(let t=0;tt(e),e()))).cache.get,i=r.cache.set,s=c,c(u)),(...e)=>s(((...e)=>{let t,r,n=0,o="";for(;n{let e=g("color"),t=g("font"),r=g("text"),n=g("font-weight"),o=g("tracking"),a=g("leading"),l=g("breakpoint"),i=g("container"),s=g("spacing"),c=g("radius"),u=g("shadow"),d=g("inset-shadow"),f=g("text-shadow"),p=g("drop-shadow"),m=g("blur"),h=g("perspective"),b=g("aspect"),y=g("ease"),v=g("animate"),w=()=>["auto","avoid","all","avoid-page","page","left","right","column"],k=()=>["center","top","bottom","left","right","top-left","left-top","top-right","right-top","bottom-right","right-bottom","bottom-left","left-bottom"],x=()=>[...k(),Z,G],A=()=>["auto","hidden","clip","visible","scroll"],E=()=>["auto","contain","none"],C=()=>[Z,G,s],M=()=>[R,"full","auto",...C()],S=()=>[j,"none","subgrid",Z,G],N=()=>["auto",{span:["full",j,Z,G]},j,Z,G],L=()=>[j,"auto",Z,G],I=()=>["auto","min","max","fr",Z,G],T=()=>["start","end","center","between","around","evenly","stretch","baseline","center-safe","end-safe"],en=()=>["start","end","center","stretch","center-safe","end-safe"],eo=()=>["auto",...C()],ea=()=>[R,"auto","full","dvw","dvh","lvw","lvh","svw","svh","min","max","fit",...C()],el=()=>[i,R,"screen","full","dvw","lvw","svw","min","max","fit",...C()],ei=()=>[R,"screen","full","lh","dvh","lvh","svh","min","max","fit",...C()],es=()=>[e,Z,G],ec=()=>[...k(),J,q,{position:[Z,G]}],eu=()=>["no-repeat",{repeat:["","x","y","space","round"]}],ed=()=>["auto","cover","contain",Q,W,{size:[Z,G]}],ef=()=>[D,X,K],ep=()=>["","none","full",c,Z,G],em=()=>["",O,X,K],eh=()=>["solid","dashed","dotted","double"],eb=()=>["normal","multiply","screen","overlay","darken","lighten","color-dodge","color-burn","hard-light","soft-light","difference","exclusion","hue","saturation","color","luminosity"],ey=()=>[O,D,J,q],ev=()=>["","none",m,Z,G],eg=()=>["none",O,Z,G],ew=()=>["none",O,Z,G],ek=()=>[O,Z,G],ex=()=>[R,"full",...C()];return{cacheSize:500,theme:{animate:["spin","ping","pulse","bounce"],aspect:["video"],blur:[P],breakpoint:[P],color:[z],container:[P],"drop-shadow":[P],ease:["in","out","in-out"],font:[F],"font-weight":["thin","extralight","light","normal","medium","semibold","bold","extrabold","black"],"inset-shadow":[P],leading:["none","tight","snug","normal","relaxed","loose"],perspective:["dramatic","near","normal","midrange","distant","none"],radius:[P],shadow:[P],spacing:["px",O],text:[P],"text-shadow":[P],tracking:["tighter","tight","normal","wide","wider","widest"]},classGroups:{aspect:[{aspect:["auto","square",R,G,Z,b]}],container:["container"],"container-type":[{"@container":["","normal","size",Z,G]}],"container-named":[_],columns:[{columns:[O,"auto",G,Z,i]}],"break-after":[{"break-after":w()}],"break-before":[{"break-before":w()}],"break-inside":[{"break-inside":["auto","avoid","avoid-page","avoid-column"]}],"box-decoration":[{"box-decoration":["slice","clone"]}],box:[{box:["border","content"]}],display:["block","inline-block","inline","flex","inline-flex","table","inline-table","table-caption","table-cell","table-column","table-column-group","table-footer-group","table-header-group","table-row-group","table-row","flow-root","grid","inline-grid","contents","list-item","hidden"],sr:["sr-only","not-sr-only"],float:[{float:["right","left","none","start","end"]}],clear:[{clear:["left","right","both","none","start","end"]}],isolation:["isolate","isolation-auto"],"object-fit":[{object:["contain","cover","fill","none","scale-down"]}],"object-position":[{object:x()}],overflow:[{overflow:A()}],"overflow-x":[{"overflow-x":A()}],"overflow-y":[{"overflow-y":A()}],overscroll:[{overscroll:E()}],"overscroll-x":[{"overscroll-x":E()}],"overscroll-y":[{"overscroll-y":E()}],position:["static","fixed","absolute","relative","sticky"],inset:[{inset:M()}],"inset-x":[{"inset-x":M()}],"inset-y":[{"inset-y":M()}],start:[{"inset-s":M(),start:M()}],end:[{"inset-e":M(),end:M()}],"inset-bs":[{"inset-bs":M()}],"inset-be":[{"inset-be":M()}],top:[{top:M()}],right:[{right:M()}],bottom:[{bottom:M()}],left:[{left:M()}],visibility:["visible","invisible","collapse"],z:[{z:[j,"auto",Z,G]}],basis:[{basis:[R,"full","auto",i,...C()]}],"flex-direction":[{flex:["row","row-reverse","col","col-reverse"]}],"flex-wrap":[{flex:["nowrap","wrap","wrap-reverse"]}],flex:[{flex:[O,R,"auto","initial","none",G]}],grow:[{grow:["",O,Z,G]}],shrink:[{shrink:["",O,Z,G]}],order:[{order:[j,"first","last","none",Z,G]}],"grid-cols":[{"grid-cols":S()}],"col-start-end":[{col:N()}],"col-start":[{"col-start":L()}],"col-end":[{"col-end":L()}],"grid-rows":[{"grid-rows":S()}],"row-start-end":[{row:N()}],"row-start":[{"row-start":L()}],"row-end":[{"row-end":L()}],"grid-flow":[{"grid-flow":["row","col","dense","row-dense","col-dense"]}],"auto-cols":[{"auto-cols":I()}],"auto-rows":[{"auto-rows":I()}],gap:[{gap:C()}],"gap-x":[{"gap-x":C()}],"gap-y":[{"gap-y":C()}],"justify-content":[{justify:[...T(),"normal"]}],"justify-items":[{"justify-items":[...en(),"normal"]}],"justify-self":[{"justify-self":["auto",...en()]}],"align-content":[{content:["normal",...T()]}],"align-items":[{items:[...en(),{baseline:["","last"]}]}],"align-self":[{self:["auto",...en(),{baseline:["","last"]}]}],"place-content":[{"place-content":T()}],"place-items":[{"place-items":[...en(),"baseline"]}],"place-self":[{"place-self":["auto",...en()]}],p:[{p:C()}],px:[{px:C()}],py:[{py:C()}],ps:[{ps:C()}],pe:[{pe:C()}],pbs:[{pbs:C()}],pbe:[{pbe:C()}],pt:[{pt:C()}],pr:[{pr:C()}],pb:[{pb:C()}],pl:[{pl:C()}],m:[{m:eo()}],mx:[{mx:eo()}],my:[{my:eo()}],ms:[{ms:eo()}],me:[{me:eo()}],mbs:[{mbs:eo()}],mbe:[{mbe:eo()}],mt:[{mt:eo()}],mr:[{mr:eo()}],mb:[{mb:eo()}],ml:[{ml:eo()}],"space-x":[{"space-x":C()}],"space-x-reverse":["space-x-reverse"],"space-y":[{"space-y":C()}],"space-y-reverse":["space-y-reverse"],size:[{size:ea()}],"inline-size":[{inline:["auto",...el()]}],"min-inline-size":[{"min-inline":["auto",...el()]}],"max-inline-size":[{"max-inline":["none",...el()]}],"block-size":[{block:["auto",...ei()]}],"min-block-size":[{"min-block":["auto",...ei()]}],"max-block-size":[{"max-block":["none",...ei()]}],w:[{w:[i,"screen",...ea()]}],"min-w":[{"min-w":[i,"screen","none",...ea()]}],"max-w":[{"max-w":[i,"screen","none","prose",{screen:[l]},...ea()]}],h:[{h:["screen","lh",...ea()]}],"min-h":[{"min-h":["screen","lh","none",...ea()]}],"max-h":[{"max-h":["screen","lh","none",...ea()]}],"font-size":[{text:["base",r,X,K]}],"font-smoothing":["antialiased","subpixel-antialiased"],"font-style":["italic","not-italic"],"font-weight":[{font:[n,er,H]}],"font-stretch":[{"font-stretch":["ultra-condensed","extra-condensed","condensed","semi-condensed","normal","semi-expanded","expanded","extra-expanded","ultra-expanded",D,G]}],"font-family":[{font:[Y,B,t]}],"font-features":[{"font-features":[G]}],"fvn-normal":["normal-nums"],"fvn-ordinal":["ordinal"],"fvn-slashed-zero":["slashed-zero"],"fvn-figure":["lining-nums","oldstyle-nums"],"fvn-spacing":["proportional-nums","tabular-nums"],"fvn-fraction":["diagonal-fractions","stacked-fractions"],tracking:[{tracking:[o,Z,G]}],"line-clamp":[{"line-clamp":[O,"none",Z,V]}],leading:[{leading:["none",a,...C()]}],"list-image":[{"list-image":["none",Z,G]}],"list-style-position":[{list:["inside","outside"]}],"list-style-type":[{list:["disc","decimal","none",Z,G]}],"text-alignment":[{text:["left","center","right","justify","start","end"]}],"placeholder-color":[{placeholder:es()}],"text-color":[{text:es()}],"text-decoration":["underline","overline","line-through","no-underline"],"text-decoration-style":[{decoration:[...eh(),"wavy"]}],"text-decoration-thickness":[{decoration:[O,"from-font","auto",Z,K]}],"text-decoration-color":[{decoration:es()}],"underline-offset":[{"underline-offset":[O,"auto",Z,G]}],"text-transform":["uppercase","lowercase","capitalize","normal-case"],"text-overflow":["truncate","text-ellipsis","text-clip"],"text-wrap":[{text:["wrap","nowrap","balance","pretty"]}],indent:[{indent:C()}],"tab-size":[{tab:[j,Z,G]}],"vertical-align":[{align:["baseline","top","middle","bottom","text-top","text-bottom","sub","super",Z,G]}],whitespace:[{whitespace:["normal","nowrap","pre","pre-line","pre-wrap","break-spaces"]}],break:[{break:["normal","words","all","keep"]}],wrap:[{wrap:["break-word","anywhere","normal"]}],hyphens:[{hyphens:["none","manual","auto"]}],content:[{content:["none",Z,G]}],"bg-attachment":[{bg:["fixed","local","scroll"]}],"bg-clip":[{"bg-clip":["border","padding","content","text"]}],"bg-origin":[{"bg-origin":["border","padding","content"]}],"bg-position":[{bg:ec()}],"bg-repeat":[{bg:eu()}],"bg-size":[{bg:ed()}],"bg-image":[{bg:["none",{linear:[{to:["t","tr","r","br","b","bl","l","tl"]},j,Z,G],radial:["",Z,G],conic:["",j,Z,G]},ee,$]}],"bg-color":[{bg:es()}],"gradient-from-pos":[{from:ef()}],"gradient-via-pos":[{via:ef()}],"gradient-to-pos":[{to:ef()}],"gradient-from":[{from:es()}],"gradient-via":[{via:es()}],"gradient-to":[{to:es()}],rounded:[{rounded:ep()}],"rounded-s":[{"rounded-s":ep()}],"rounded-e":[{"rounded-e":ep()}],"rounded-t":[{"rounded-t":ep()}],"rounded-r":[{"rounded-r":ep()}],"rounded-b":[{"rounded-b":ep()}],"rounded-l":[{"rounded-l":ep()}],"rounded-ss":[{"rounded-ss":ep()}],"rounded-se":[{"rounded-se":ep()}],"rounded-ee":[{"rounded-ee":ep()}],"rounded-es":[{"rounded-es":ep()}],"rounded-tl":[{"rounded-tl":ep()}],"rounded-tr":[{"rounded-tr":ep()}],"rounded-br":[{"rounded-br":ep()}],"rounded-bl":[{"rounded-bl":ep()}],"border-w":[{border:em()}],"border-w-x":[{"border-x":em()}],"border-w-y":[{"border-y":em()}],"border-w-s":[{"border-s":em()}],"border-w-e":[{"border-e":em()}],"border-w-bs":[{"border-bs":em()}],"border-w-be":[{"border-be":em()}],"border-w-t":[{"border-t":em()}],"border-w-r":[{"border-r":em()}],"border-w-b":[{"border-b":em()}],"border-w-l":[{"border-l":em()}],"divide-x":[{"divide-x":em()}],"divide-x-reverse":["divide-x-reverse"],"divide-y":[{"divide-y":em()}],"divide-y-reverse":["divide-y-reverse"],"border-style":[{border:[...eh(),"hidden","none"]}],"divide-style":[{divide:[...eh(),"hidden","none"]}],"border-color":[{border:es()}],"border-color-x":[{"border-x":es()}],"border-color-y":[{"border-y":es()}],"border-color-s":[{"border-s":es()}],"border-color-e":[{"border-e":es()}],"border-color-bs":[{"border-bs":es()}],"border-color-be":[{"border-be":es()}],"border-color-t":[{"border-t":es()}],"border-color-r":[{"border-r":es()}],"border-color-b":[{"border-b":es()}],"border-color-l":[{"border-l":es()}],"divide-color":[{divide:es()}],"outline-style":[{outline:[...eh(),"none","hidden"]}],"outline-offset":[{"outline-offset":[O,Z,G]}],"outline-w":[{outline:["",O,X,K]}],"outline-color":[{outline:es()}],shadow:[{shadow:["","inner","none",u,et,U]}],"shadow-color":[{shadow:es()}],"inset-shadow":[{"inset-shadow":["none",d,et,U]}],"inset-shadow-color":[{"inset-shadow":es()}],"ring-w":[{ring:em()}],"ring-w-inset":["ring-inset"],"ring-color":[{ring:es()}],"ring-offset-w":[{"ring-offset":[O,K]}],"ring-offset-color":[{"ring-offset":es()}],"inset-ring-w":[{"inset-ring":em()}],"inset-ring-color":[{"inset-ring":es()}],"text-shadow":[{"text-shadow":["none",f,et,U]}],"text-shadow-color":[{"text-shadow":es()}],opacity:[{opacity:[O,Z,G]}],"mix-blend":[{"mix-blend":[...eb(),"plus-darker","plus-lighter"]}],"bg-blend":[{"bg-blend":eb()}],"mask-clip":[{"mask-clip":["border","padding","content","fill","stroke","view"]},"mask-no-clip"],"mask-composite":[{mask:["add","subtract","intersect","exclude"]}],"mask-image-linear-pos":[{"mask-linear":[O]}],"mask-image-linear-from-pos":[{"mask-linear-from":ey()}],"mask-image-linear-to-pos":[{"mask-linear-to":ey()}],"mask-image-linear-from-color":[{"mask-linear-from":es()}],"mask-image-linear-to-color":[{"mask-linear-to":es()}],"mask-image-t-from-pos":[{"mask-t-from":ey()}],"mask-image-t-to-pos":[{"mask-t-to":ey()}],"mask-image-t-from-color":[{"mask-t-from":es()}],"mask-image-t-to-color":[{"mask-t-to":es()}],"mask-image-r-from-pos":[{"mask-r-from":ey()}],"mask-image-r-to-pos":[{"mask-r-to":ey()}],"mask-image-r-from-color":[{"mask-r-from":es()}],"mask-image-r-to-color":[{"mask-r-to":es()}],"mask-image-b-from-pos":[{"mask-b-from":ey()}],"mask-image-b-to-pos":[{"mask-b-to":ey()}],"mask-image-b-from-color":[{"mask-b-from":es()}],"mask-image-b-to-color":[{"mask-b-to":es()}],"mask-image-l-from-pos":[{"mask-l-from":ey()}],"mask-image-l-to-pos":[{"mask-l-to":ey()}],"mask-image-l-from-color":[{"mask-l-from":es()}],"mask-image-l-to-color":[{"mask-l-to":es()}],"mask-image-x-from-pos":[{"mask-x-from":ey()}],"mask-image-x-to-pos":[{"mask-x-to":ey()}],"mask-image-x-from-color":[{"mask-x-from":es()}],"mask-image-x-to-color":[{"mask-x-to":es()}],"mask-image-y-from-pos":[{"mask-y-from":ey()}],"mask-image-y-to-pos":[{"mask-y-to":ey()}],"mask-image-y-from-color":[{"mask-y-from":es()}],"mask-image-y-to-color":[{"mask-y-to":es()}],"mask-image-radial":[{"mask-radial":[Z,G]}],"mask-image-radial-from-pos":[{"mask-radial-from":ey()}],"mask-image-radial-to-pos":[{"mask-radial-to":ey()}],"mask-image-radial-from-color":[{"mask-radial-from":es()}],"mask-image-radial-to-color":[{"mask-radial-to":es()}],"mask-image-radial-shape":[{"mask-radial":["circle","ellipse"]}],"mask-image-radial-size":[{"mask-radial":[{closest:["side","corner"],farthest:["side","corner"]}]}],"mask-image-radial-pos":[{"mask-radial-at":k()}],"mask-image-conic-pos":[{"mask-conic":[O]}],"mask-image-conic-from-pos":[{"mask-conic-from":ey()}],"mask-image-conic-to-pos":[{"mask-conic-to":ey()}],"mask-image-conic-from-color":[{"mask-conic-from":es()}],"mask-image-conic-to-color":[{"mask-conic-to":es()}],"mask-mode":[{mask:["alpha","luminance","match"]}],"mask-origin":[{"mask-origin":["border","padding","content","fill","stroke","view"]}],"mask-position":[{mask:ec()}],"mask-repeat":[{mask:eu()}],"mask-size":[{mask:ed()}],"mask-type":[{"mask-type":["alpha","luminance"]}],"mask-image":[{mask:["none",Z,G]}],filter:[{filter:["","none",Z,G]}],blur:[{blur:ev()}],brightness:[{brightness:[O,Z,G]}],contrast:[{contrast:[O,Z,G]}],"drop-shadow":[{"drop-shadow":["","none",p,et,U]}],"drop-shadow-color":[{"drop-shadow":es()}],grayscale:[{grayscale:["",O,Z,G]}],"hue-rotate":[{"hue-rotate":[O,Z,G]}],invert:[{invert:["",O,Z,G]}],saturate:[{saturate:[O,Z,G]}],sepia:[{sepia:["",O,Z,G]}],"backdrop-filter":[{"backdrop-filter":["","none",Z,G]}],"backdrop-blur":[{"backdrop-blur":ev()}],"backdrop-brightness":[{"backdrop-brightness":[O,Z,G]}],"backdrop-contrast":[{"backdrop-contrast":[O,Z,G]}],"backdrop-grayscale":[{"backdrop-grayscale":["",O,Z,G]}],"backdrop-hue-rotate":[{"backdrop-hue-rotate":[O,Z,G]}],"backdrop-invert":[{"backdrop-invert":["",O,Z,G]}],"backdrop-opacity":[{"backdrop-opacity":[O,Z,G]}],"backdrop-saturate":[{"backdrop-saturate":[O,Z,G]}],"backdrop-sepia":[{"backdrop-sepia":["",O,Z,G]}],"border-collapse":[{border:["collapse","separate"]}],"border-spacing":[{"border-spacing":C()}],"border-spacing-x":[{"border-spacing-x":C()}],"border-spacing-y":[{"border-spacing-y":C()}],"table-layout":[{table:["auto","fixed"]}],caption:[{caption:["top","bottom"]}],transition:[{transition:["","all","colors","opacity","shadow","transform","none",Z,G]}],"transition-behavior":[{transition:["normal","discrete"]}],duration:[{duration:[O,"initial",Z,G]}],ease:[{ease:["linear","initial",y,Z,G]}],delay:[{delay:[O,Z,G]}],animate:[{animate:["none",v,Z,G]}],backface:[{backface:["hidden","visible"]}],perspective:[{perspective:[h,Z,G]}],"perspective-origin":[{"perspective-origin":x()}],rotate:[{rotate:eg()}],"rotate-x":[{"rotate-x":eg()}],"rotate-y":[{"rotate-y":eg()}],"rotate-z":[{"rotate-z":eg()}],scale:[{scale:ew()}],"scale-x":[{"scale-x":ew()}],"scale-y":[{"scale-y":ew()}],"scale-z":[{"scale-z":ew()}],"scale-3d":["scale-3d"],skew:[{skew:ek()}],"skew-x":[{"skew-x":ek()}],"skew-y":[{"skew-y":ek()}],transform:[{transform:[Z,G,"","none","gpu","cpu"]}],"transform-origin":[{origin:x()}],"transform-style":[{transform:["3d","flat"]}],translate:[{translate:ex()}],"translate-x":[{"translate-x":ex()}],"translate-y":[{"translate-y":ex()}],"translate-z":[{"translate-z":ex()}],"translate-none":["translate-none"],zoom:[{zoom:[j,Z,G]}],accent:[{accent:es()}],appearance:[{appearance:["none","auto"]}],"caret-color":[{caret:es()}],"color-scheme":[{scheme:["normal","dark","light","light-dark","only-dark","only-light"]}],cursor:[{cursor:["auto","default","pointer","wait","text","move","help","not-allowed","none","context-menu","progress","cell","crosshair","vertical-text","alias","copy","no-drop","grab","grabbing","all-scroll","col-resize","row-resize","n-resize","e-resize","s-resize","w-resize","ne-resize","nw-resize","se-resize","sw-resize","ew-resize","ns-resize","nesw-resize","nwse-resize","zoom-in","zoom-out",Z,G]}],"field-sizing":[{"field-sizing":["fixed","content"]}],"pointer-events":[{"pointer-events":["auto","none"]}],resize:[{resize:["none","","y","x"]}],"scroll-behavior":[{scroll:["auto","smooth"]}],"scrollbar-thumb-color":[{"scrollbar-thumb":es()}],"scrollbar-track-color":[{"scrollbar-track":es()}],"scrollbar-gutter":[{"scrollbar-gutter":["auto","stable","both"]}],"scrollbar-w":[{scrollbar:["auto","thin","none"]}],"scroll-m":[{"scroll-m":C()}],"scroll-mx":[{"scroll-mx":C()}],"scroll-my":[{"scroll-my":C()}],"scroll-ms":[{"scroll-ms":C()}],"scroll-me":[{"scroll-me":C()}],"scroll-mbs":[{"scroll-mbs":C()}],"scroll-mbe":[{"scroll-mbe":C()}],"scroll-mt":[{"scroll-mt":C()}],"scroll-mr":[{"scroll-mr":C()}],"scroll-mb":[{"scroll-mb":C()}],"scroll-ml":[{"scroll-ml":C()}],"scroll-p":[{"scroll-p":C()}],"scroll-px":[{"scroll-px":C()}],"scroll-py":[{"scroll-py":C()}],"scroll-ps":[{"scroll-ps":C()}],"scroll-pe":[{"scroll-pe":C()}],"scroll-pbs":[{"scroll-pbs":C()}],"scroll-pbe":[{"scroll-pbe":C()}],"scroll-pt":[{"scroll-pt":C()}],"scroll-pr":[{"scroll-pr":C()}],"scroll-pb":[{"scroll-pb":C()}],"scroll-pl":[{"scroll-pl":C()}],"snap-align":[{snap:["start","end","center","align-none"]}],"snap-stop":[{snap:["normal","always"]}],"snap-type":[{snap:["none","x","y","both"]}],"snap-strictness":[{snap:["mandatory","proximity"]}],touch:[{touch:["auto","none","manipulation"]}],"touch-x":[{"touch-pan":["x","left","right"]}],"touch-y":[{"touch-pan":["y","up","down"]}],"touch-pz":["touch-pinch-zoom"],select:[{select:["none","text","all","auto"]}],"will-change":[{"will-change":["auto","scroll","contents","transform",Z,G]}],fill:[{fill:["none",...es()]}],"stroke-w":[{stroke:[O,X,K,V]}],stroke:[{stroke:["none",...es()]}],"forced-color-adjust":[{"forced-color-adjust":["auto","none"]}]},conflictingClassGroups:{"container-named":["container-type"],overflow:["overflow-x","overflow-y"],overscroll:["overscroll-x","overscroll-y"],inset:["inset-x","inset-y","inset-bs","inset-be","start","end","top","right","bottom","left"],"inset-x":["start","end","right","left"],"inset-y":["inset-bs","inset-be","top","bottom"],flex:["basis","grow","shrink"],gap:["gap-x","gap-y"],p:["px","py","ps","pe","pbs","pbe","pt","pr","pb","pl"],px:["ps","pe","pr","pl"],py:["pbs","pbe","pt","pb"],m:["mx","my","ms","me","mbs","mbe","mt","mr","mb","ml"],mx:["ms","me","mr","ml"],my:["mbs","mbe","mt","mb"],size:["w","h"],"font-size":["leading"],"fvn-normal":["fvn-ordinal","fvn-slashed-zero","fvn-figure","fvn-spacing","fvn-fraction"],"fvn-ordinal":["fvn-normal"],"fvn-slashed-zero":["fvn-normal"],"fvn-figure":["fvn-normal"],"fvn-spacing":["fvn-normal"],"fvn-fraction":["fvn-normal"],"line-clamp":["display","overflow"],rounded:["rounded-s","rounded-e","rounded-t","rounded-r","rounded-b","rounded-l","rounded-ss","rounded-se","rounded-ee","rounded-es","rounded-tl","rounded-tr","rounded-br","rounded-bl"],"rounded-s":["rounded-ss","rounded-es"],"rounded-e":["rounded-se","rounded-ee"],"rounded-t":["rounded-tl","rounded-tr"],"rounded-r":["rounded-tr","rounded-br"],"rounded-b":["rounded-br","rounded-bl"],"rounded-l":["rounded-tl","rounded-bl"],"border-spacing":["border-spacing-x","border-spacing-y"],"border-w":["border-w-x","border-w-y","border-w-s","border-w-e","border-w-bs","border-w-be","border-w-t","border-w-r","border-w-b","border-w-l"],"border-w-x":["border-w-s","border-w-e","border-w-r","border-w-l"],"border-w-y":["border-w-bs","border-w-be","border-w-t","border-w-b"],"border-color":["border-color-x","border-color-y","border-color-s","border-color-e","border-color-bs","border-color-be","border-color-t","border-color-r","border-color-b","border-color-l"],"border-color-x":["border-color-s","border-color-e","border-color-r","border-color-l"],"border-color-y":["border-color-bs","border-color-be","border-color-t","border-color-b"],translate:["translate-x","translate-y","translate-none"],"translate-none":["translate","translate-x","translate-y","translate-z"],"scroll-m":["scroll-mx","scroll-my","scroll-ms","scroll-me","scroll-mbs","scroll-mbe","scroll-mt","scroll-mr","scroll-mb","scroll-ml"],"scroll-mx":["scroll-ms","scroll-me","scroll-mr","scroll-ml"],"scroll-my":["scroll-mbs","scroll-mbe","scroll-mt","scroll-mb"],"scroll-p":["scroll-px","scroll-py","scroll-ps","scroll-pe","scroll-pbs","scroll-pbe","scroll-pt","scroll-pr","scroll-pb","scroll-pl"],"scroll-px":["scroll-ps","scroll-pe","scroll-pr","scroll-pl"],"scroll-py":["scroll-pbs","scroll-pbe","scroll-pt","scroll-pb"],touch:["touch-x","touch-y","touch-pz"],"touch-x":["touch"],"touch-y":["touch"],"touch-pz":["touch"]},conflictingClassGroupModifiers:{"font-size":["leading"]},postfixLookupClassGroups:["container-type"],orderSensitiveModifiers:["*","**","after","backdrop","before","details-content","file","first-letter","first-line","marker","placeholder","selection"]}})},5958:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Link2",[["path",{d:"M9 17H7A5 5 0 0 1 7 7h2",key:"8i5ue5"}],["path",{d:"M15 7h2a5 5 0 1 1 0 10h-2",key:"1b9ql8"}],["line",{x1:"8",x2:"16",y1:"12",y2:"12",key:"1jonct"}]])},5989:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Sun",[["circle",{cx:"12",cy:"12",r:"4",key:"4exip2"}],["path",{d:"M12 2v2",key:"tus03m"}],["path",{d:"M12 20v2",key:"1lh1kg"}],["path",{d:"m4.93 4.93 1.41 1.41",key:"149t6j"}],["path",{d:"m17.66 17.66 1.41 1.41",key:"ptbguv"}],["path",{d:"M2 12h2",key:"1t8f8n"}],["path",{d:"M20 12h2",key:"1q8mjw"}],["path",{d:"m6.34 17.66-1.41 1.41",key:"1m8zz5"}],["path",{d:"m19.07 4.93-1.41 1.41",key:"1shlcs"}]])},6204:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("ArrowLeft",[["path",{d:"m12 19-7-7 7-7",key:"1l729n"}],["path",{d:"M19 12H5",key:"x3x0zl"}]])},6615:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("X",[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]])},6842:(e,t,r)=>{r.d(t,{C:()=>s});var n=r(2115),o=r(4129),a=Object.defineProperty,l=(e,t)=>a(e,"name",{value:t,configurable:!0});function i(e,t){return n.useReducer((e,r)=>{let n=t[e][r];return null!=n?n:e},e)}l(i,"useStateMachine");var s=l(e=>{let{present:t,children:r}=e,o=c(t),a="function"==typeof r?r({present:o.isPresent}):n.Children.only(r),l=d(o.ref,p(a));return"function"==typeof r||o.isPresent?n.cloneElement(a,{ref:l}):null},"Presence");function c(e){let[t,r]=n.useState(),a=n.useRef(null),s=n.useRef(e),c=n.useRef("none"),u=n.useRef(void 0),[d,p]=i(e?"mounted":"unmounted",{mounted:{UNMOUNT:"unmounted",ANIMATION_OUT:"unmountSuspended"},unmountSuspended:{MOUNT:"mounted",ANIMATION_END:"unmounted"},unmounted:{MOUNT:"mounted"}});return n.useEffect(()=>{if("mounted"===d){var e;c.current=null!=(e=u.current)?e:f(a.current),u.current=void 0}else c.current="none"},[d]),(0,o.N)(()=>{let t=a.current,r=s.current;if(r!==e){let n=c.current,o=f(t);e?(u.current=o,p("MOUNT")):"none"===o||(null==t?void 0:t.display)==="none"?p("UNMOUNT"):r&&n!==o?p("ANIMATION_OUT"):p("UNMOUNT"),s.current=e}},[e,p]),(0,o.N)(()=>{if(t){var e;let r,n=null!=(e=t.ownerDocument.defaultView)?e:window,o=l(e=>{let o=f(a.current).includes(CSS.escape(e.animationName));if(e.target===t&&o&&(p("ANIMATION_END"),!s.current)){let e=t.style.animationFillMode;t.style.animationFillMode="forwards",r=n.setTimeout(()=>{"forwards"===t.style.animationFillMode&&(t.style.animationFillMode=e)})}},"handleAnimationEnd"),i=l(e=>{e.target===t&&(c.current=f(a.current))},"handleAnimationStart");return t.addEventListener("animationstart",i),t.addEventListener("animationcancel",o),t.addEventListener("animationend",o),()=>{n.clearTimeout(r),t.removeEventListener("animationstart",i),t.removeEventListener("animationcancel",o),t.removeEventListener("animationend",o)}}p("ANIMATION_END")},[t,p]),{isPresent:["mounted","unmountSuspended"].includes(d),ref:n.useCallback(e=>{if(e){let t=getComputedStyle(e);a.current=t,u.current=f(t)}else a.current=null;r(e)},[])}}function u(e,t){if("function"==typeof e)return e(t);null!=e&&(e.current=t)}function d(){for(var e=arguments.length,t=Array(e),r=0;r{let t=o.current,r=!1,n=t.map(t=>{let n=u(t,e);return r||"function"!=typeof n||(r=!0),n});if(r)return()=>{for(let e=0;e{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Folder",[["path",{d:"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z",key:"1kt360"}]])},6916:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Binary",[["rect",{x:"14",y:"14",width:"4",height:"6",rx:"2",key:"p02svl"}],["rect",{x:"6",y:"4",width:"4",height:"6",rx:"2",key:"xm4xkj"}],["path",{d:"M6 20h4",key:"1i6q5t"}],["path",{d:"M14 10h4",key:"ru81e7"}],["path",{d:"M6 14h2v6",key:"16z9wg"}],["path",{d:"M14 4h2v6",key:"1idq9u"}]])},7205:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Network",[["rect",{x:"16",y:"16",width:"6",height:"6",rx:"1",key:"4q2zg0"}],["rect",{x:"2",y:"16",width:"6",height:"6",rx:"1",key:"8cvhb9"}],["rect",{x:"9",y:"2",width:"6",height:"6",rx:"1",key:"1egb70"}],["path",{d:"M5 16v-3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3",key:"1jsf9p"}],["path",{d:"M12 12V8",key:"2874zd"}]])},7602:(e,t,r)=>{r.d(t,{hO:()=>c,sG:()=>s});var n=r(2115),o=r(7650),a=r(2467),l=r(5155),i=Object.defineProperty,s=["a","button","div","form","h2","h3","img","input","label","li","nav","ol","p","select","span","svg","ul"].reduce((e,t)=>{let r=(0,a.TL)(`Primitive.${t}`),o=n.forwardRef((e,n)=>{let{asChild:o,...a}=e;return"undefined"!=typeof window&&(window[Symbol.for("radix-ui")]=!0),(0,l.jsx)(o?r:t,{...a,ref:n})});return o.displayName=`Primitive.${t}`,{...e,[t]:o}},{});function c(e,t){e&&o.flushSync(()=>e.dispatchEvent(t))}i(c,"name",{value:"dispatchDiscreteCustomEvent",configurable:!0})},8288:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Key",[["path",{d:"m15.5 7.5 2.3 2.3a1 1 0 0 0 1.4 0l2.1-2.1a1 1 0 0 0 0-1.4L19 4",key:"g0fldk"}],["path",{d:"m21 2-9.6 9.6",key:"1j0ho8"}],["circle",{cx:"7.5",cy:"15.5",r:"5.5",key:"yqb3hr"}]])},8428:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("LogOut",[["path",{d:"M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4",key:"1uf3rs"}],["polyline",{points:"16 17 21 12 16 7",key:"1gabdz"}],["line",{x1:"21",x2:"9",y1:"12",y2:"12",key:"1uyos4"}]])},8770:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("CircleAlert",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["line",{x1:"12",x2:"12",y1:"8",y2:"12",key:"1pkeuh"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16",key:"4dfq90"}]])},8822:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("LayoutGrid",[["rect",{width:"7",height:"7",x:"3",y:"3",rx:"1",key:"1g98yp"}],["rect",{width:"7",height:"7",x:"14",y:"3",rx:"1",key:"6d4xhi"}],["rect",{width:"7",height:"7",x:"14",y:"14",rx:"1",key:"nxv5o0"}],["rect",{width:"7",height:"7",x:"3",y:"14",rx:"1",key:"1bb6yr"}]])},8844:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Database",[["ellipse",{cx:"12",cy:"5",rx:"9",ry:"3",key:"msslwz"}],["path",{d:"M3 5V19A9 3 0 0 0 21 19V5",key:"1wlel7"}],["path",{d:"M3 12A9 3 0 0 0 21 12",key:"mv7ke4"}]])},8941:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Pause",[["rect",{x:"14",y:"4",width:"4",height:"16",rx:"1",key:"zuxfzm"}],["rect",{x:"6",y:"4",width:"4",height:"16",rx:"1",key:"1okwgv"}]])},8946:(e,t,r)=>{r.d(t,{B:()=>c});var n,o=r(2115),a=r(4129),l=Object.defineProperty,i=(n||(n=r.t(o,2)))[" useId ".trim().toString()]||(()=>void 0),s=0;function c(e){let[t,r]=o.useState(i());return(0,a.N)(()=>{e||r(e=>e??String(s++))},[e]),e||(t?`radix-${t}`:"")}l(c,"name",{value:"useId",configurable:!0})},8971:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("Table",[["path",{d:"M12 3v18",key:"108xh3"}],["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}],["path",{d:"M3 9h18",key:"1pudct"}],["path",{d:"M3 15h18",key:"5xshup"}]])},9201:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("ShieldCheck",[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]])},9364:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("ListTree",[["path",{d:"M21 12h-8",key:"1bmf0i"}],["path",{d:"M21 6H8",key:"1pqkrb"}],["path",{d:"M21 18h-8",key:"1tm79t"}],["path",{d:"M3 6v4c0 1.1.9 2 2 2h3",key:"1ywdgy"}],["path",{d:"M3 10v6c0 1.1.9 2 2 2h3",key:"2wc746"}]])},9478:(e,t,r)=>{r.d(t,{i:()=>p});var n=r(2115),o=r.t(n,2),a=r(4129),l=Object.defineProperty,i=o[" useEffectEvent ".trim().toString()],s=o[" useInsertionEffect ".trim().toString()];function c(e){if("function"==typeof i)return i(e);let t=n.useRef(()=>{throw Error("Cannot call an event handler while rendering.")});return"function"==typeof s?s(()=>{t.current=e}):(0,a.N)(()=>{t.current=e}),n.useMemo(()=>(...e)=>t.current?.(...e),[])}l(c,"name",{value:"useEffectEvent",configurable:!0});var u=Object.defineProperty,d=(e,t)=>u(e,"name",{value:t,configurable:!0}),f=o[" useInsertionEffect ".trim().toString()]||a.N;function p({prop:e,defaultProp:t,onChange:r=d(()=>{},"onChange"),caller:o}){let[a,l,i]=m({defaultProp:t,onChange:r}),s=void 0!==e,c=s?e:a;return[c,n.useCallback(t=>{if(s){let r=h(t)?t(e):t;r!==e&&i.current?.(r)}else l(t)},[s,e,l,i])]}function m({defaultProp:e,onChange:t}){let[r,o]=n.useState(e),a=n.useRef(r),l=n.useRef(t);return f(()=>{l.current=t},[t]),n.useEffect(()=>{a.current!==r&&(l.current?.(r),a.current=r)},[r,a]),[r,o,l]}function h(e){return"function"==typeof e}d(p,"useControllableState"),d(m,"useUncontrolledState"),d(h,"isFunction");var b=Symbol("RADIX:SYNC_STATE");d(function(e,t,r,o){let{prop:a,defaultProp:l,onChange:i,caller:s}=t,u=void 0!==a,d=c(i),f=[{...r,state:l}];o&&f.push(o);let[p,m]=n.useReducer((t,r)=>{if(r.type===b)return{...t,state:r.state};let n=e(t,r);return u&&!Object.is(n.state,t.state)&&d(n.state),n},...f),h=p.state,y=n.useRef(h);n.useEffect(()=>{y.current!==h&&(y.current=h,u||d(h))},[h,y,u]);let v=n.useMemo(()=>void 0!==a?{...p,state:a}:p,[p,a]);return n.useEffect(()=>{u&&!Object.is(a,p.state)&&m({type:b,state:a})},[a,p.state,u]),[v,m]},"useControllableStateReducer")},9915:(e,t,r)=>{r.d(t,{A:()=>n});let n=(0,r(4294).A)("History",[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"1357e3"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}],["path",{d:"M12 7v5l4 2",key:"1fdv2h"}]])}}]); \ No newline at end of file diff --git a/internal/web/static/_next/static/chunks/app/_not-found/page-2063bb33875c85f7.js b/internal/web/static/_next/static/chunks/app/_not-found/page-2063bb33875c85f7.js new file mode 100644 index 0000000..0dbdc64 --- /dev/null +++ b/internal/web/static/_next/static/chunks/app/_not-found/page-2063bb33875c85f7.js @@ -0,0 +1 @@ +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[492],{1488:(e,t,l)=>{(window.__NEXT_P=window.__NEXT_P||[]).push(["/_not-found/page",function(){return l(9520)}])},4585:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"styles",{enumerable:!0,get:function(){return l}});let l={error:{fontFamily:'system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"',height:"100vh",textAlign:"center",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center"},desc:{display:"inline-block"},h1:{display:"inline-block",margin:"0 20px 0 0",padding:"0 23px 0 0",fontSize:24,fontWeight:500,verticalAlign:"top",lineHeight:"49px"},h2:{fontSize:14,fontWeight:400,lineHeight:"49px",margin:0}};("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&void 0===t.default.__esModule&&(Object.defineProperty(t.default,"__esModule",{value:!0}),Object.assign(t.default,t),e.exports=t.default)},8886:(e,t,l)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"HTTPAccessErrorFallback",{enumerable:!0,get:function(){return o}});let r=l(5155),n=l(4585);function o(e){let{status:t,message:l}=e;return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)("title",{children:t+": "+l}),(0,r.jsx)("div",{style:n.styles.error,children:(0,r.jsxs)("div",{children:[(0,r.jsx)("style",{dangerouslySetInnerHTML:{__html:"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}),(0,r.jsx)("h1",{className:"next-error-h1",style:n.styles.h1,children:t}),(0,r.jsx)("div",{style:n.styles.desc,children:(0,r.jsx)("h2",{style:n.styles.h2,children:l})})]})})]})}("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&void 0===t.default.__esModule&&(Object.defineProperty(t.default,"__esModule",{value:!0}),Object.assign(t.default,t),e.exports=t.default)},9520:(e,t,l)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return o}});let r=l(5155),n=l(8886),o=function(){return(0,r.jsx)("html",{children:(0,r.jsx)("body",{children:(0,r.jsx)(n.HTTPAccessErrorFallback,{status:404,message:"This page could not be found."})})})};("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&void 0===t.default.__esModule&&(Object.defineProperty(t.default,"__esModule",{value:!0}),Object.assign(t.default,t),e.exports=t.default)}},e=>{e.O(0,[441,255,358],()=>e(e.s=1488)),_N_E=e.O()}]); \ No newline at end of file diff --git a/internal/web/static/_next/static/chunks/app/_not-found/page-7d4537dffa3d79ab.js b/internal/web/static/_next/static/chunks/app/_not-found/page-7d4537dffa3d79ab.js deleted file mode 100644 index 58e64ef..0000000 --- a/internal/web/static/_next/static/chunks/app/_not-found/page-7d4537dffa3d79ab.js +++ /dev/null @@ -1 +0,0 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[492],{740:(e,t,l)=>{(window.__NEXT_P=window.__NEXT_P||[]).push(["/_not-found/page",function(){return l(9520)}])},4585:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"styles",{enumerable:!0,get:function(){return l}});let l={error:{fontFamily:'system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"',height:"100vh",textAlign:"center",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center"},desc:{display:"inline-block"},h1:{display:"inline-block",margin:"0 20px 0 0",padding:"0 23px 0 0",fontSize:24,fontWeight:500,verticalAlign:"top",lineHeight:"49px"},h2:{fontSize:14,fontWeight:400,lineHeight:"49px",margin:0}};("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&void 0===t.default.__esModule&&(Object.defineProperty(t.default,"__esModule",{value:!0}),Object.assign(t.default,t),e.exports=t.default)},8886:(e,t,l)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"HTTPAccessErrorFallback",{enumerable:!0,get:function(){return o}});let r=l(5155),n=l(4585);function o(e){let{status:t,message:l}=e;return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)("title",{children:t+": "+l}),(0,r.jsx)("div",{style:n.styles.error,children:(0,r.jsxs)("div",{children:[(0,r.jsx)("style",{dangerouslySetInnerHTML:{__html:"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}),(0,r.jsx)("h1",{className:"next-error-h1",style:n.styles.h1,children:t}),(0,r.jsx)("div",{style:n.styles.desc,children:(0,r.jsx)("h2",{style:n.styles.h2,children:l})})]})})]})}("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&void 0===t.default.__esModule&&(Object.defineProperty(t.default,"__esModule",{value:!0}),Object.assign(t.default,t),e.exports=t.default)},9520:(e,t,l)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return o}});let r=l(5155),n=l(8886),o=function(){return(0,r.jsx)("html",{children:(0,r.jsx)("body",{children:(0,r.jsx)(n.HTTPAccessErrorFallback,{status:404,message:"This page could not be found."})})})};("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&void 0===t.default.__esModule&&(Object.defineProperty(t.default,"__esModule",{value:!0}),Object.assign(t.default,t),e.exports=t.default)}},e=>{e.O(0,[441,255,358],()=>e(e.s=740)),_N_E=e.O()}]); \ No newline at end of file diff --git a/internal/web/static/_next/static/chunks/app/layout-4e4515632bdb7047.js b/internal/web/static/_next/static/chunks/app/layout-4e4515632bdb7047.js new file mode 100644 index 0000000..52cdf1a --- /dev/null +++ b/internal/web/static/_next/static/chunks/app/layout-4e4515632bdb7047.js @@ -0,0 +1 @@ +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[177],{3673:()=>{},5893:(e,s,_)=>{Promise.resolve().then(_.t.bind(_,3673,23))}},e=>{e.O(0,[978,441,255,358],()=>e(e.s=5893)),_N_E=e.O()}]); \ No newline at end of file diff --git a/internal/web/static/_next/static/chunks/app/layout-e9c6581c9773cd8d.js b/internal/web/static/_next/static/chunks/app/layout-e9c6581c9773cd8d.js deleted file mode 100644 index e24e78b..0000000 --- a/internal/web/static/_next/static/chunks/app/layout-e9c6581c9773cd8d.js +++ /dev/null @@ -1 +0,0 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[177],{3673:()=>{},7681:(e,s,_)=>{Promise.resolve().then(_.t.bind(_,3673,23))}},e=>{e.O(0,[978,441,255,358],()=>e(e.s=7681)),_N_E=e.O()}]); \ No newline at end of file diff --git a/internal/web/static/_next/static/chunks/app/page-af03adbf71ed5700.js b/internal/web/static/_next/static/chunks/app/page-af03adbf71ed5700.js deleted file mode 100644 index bfc9a3b..0000000 --- a/internal/web/static/_next/static/chunks/app/page-af03adbf71ed5700.js +++ /dev/null @@ -1 +0,0 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[974],{2564:(e,t,r)=>{Promise.resolve().then(r.bind(r,3644))},3644:(e,t,r)=>{"use strict";r.r(t),r.d(t,{default:()=>r_});var s=r(5155),a=r(2115),n=r(6615),l=r(1851),o=r(8844),i=r(109),d=r(9364),c=r(7205),u=r(3037),x=r(8288),m=r(5040),f=r(5225),h=r(3063),p=r(3381),g=r(5989),b=r(2784),j=r(8428);function v(){return(0,s.jsxs)("div",{className:"shrink-0 flex items-center",role:"img","aria-label":"BeastDB",children:[(0,s.jsx)("img",{src:"/brand/beastdb-horizontal-light.svg",alt:"BeastDB","aria-hidden":"true",className:"h-8 w-auto dark:hidden"}),(0,s.jsx)("img",{src:"/brand/beastdb-horizontal-dark.svg",alt:"BeastDB","aria-hidden":"true",className:"hidden h-8 w-auto dark:block"}),(0,s.jsx)("span",{className:"ml-2.5 rounded border border-border bg-muted/80 px-1.5 py-0.5 text-[10px] font-mono text-muted-foreground",children:"Studio"})]})}function y(e){let{role:t}=e;return"admin"===t?(0,s.jsx)("span",{className:"inline-flex items-center rounded-md border border-primary/40 bg-primary/10 px-2 py-0.5 text-[10px] font-mono font-semibold tracking-wider text-primary",children:"ADMIN"}):"viewer"===t?(0,s.jsx)("span",{className:"inline-flex items-center rounded-md border border-border bg-muted px-2 py-0.5 text-[10px] font-mono font-semibold tracking-wider text-muted-foreground",children:"VIEWER"}):null}function N(){let[e,t]=(0,a.useState)(!0);return(0,a.useEffect)(()=>{t(document.documentElement.classList.contains("dark"));let e=matchMedia("(prefers-color-scheme: dark)"),r=()=>{let r=localStorage.getItem("beastdb-theme"),s=r?"dark"===r:e.matches;document.documentElement.classList.toggle("dark",s),t(s)};return e.addEventListener("change",r),()=>e.removeEventListener("change",r)},[]),{dark:e,toggle:()=>{let e=!document.documentElement.classList.contains("dark");document.documentElement.classList.toggle("dark",e),localStorage.setItem("beastdb-theme",e?"dark":"light"),t(e)}}}var w=r(2821),k=r(5889);function S(){for(var e=arguments.length,t=Array(e),r=0;r{let{id:a,label:n,icon:l}=e;return(0,s.jsxs)("button",{"aria-current":t===a?"page":void 0,onClick:()=>r(a),className:S("flex w-full shrink-0 items-center gap-3 rounded-lg px-3 py-2 text-sm transition-colors",t===a?"bg-primary/10 font-medium text-primary":"text-muted-foreground hover:bg-muted hover:text-foreground"),children:[(0,s.jsx)(l,{size:16}),n]},a)})}),(0,s.jsxs)("div",{className:"hidden items-center gap-2 border-t border-border p-3 md:flex",children:[(0,s.jsx)("div",{className:"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-beast-lime text-[11px] font-bold text-black","aria-hidden":"true",children:((null==a?void 0:a.username)||"??").slice(0,2).toUpperCase()}),(0,s.jsxs)("div",{className:"min-w-0 flex-1",children:[(0,s.jsx)("p",{className:"truncate font-mono text-xs",title:(null==a?void 0:a.username)||"Session unavailable",children:(null==a?void 0:a.username)||"Account"}),(0,s.jsx)(y,{role:null==a?void 0:a.role})]}),(0,s.jsx)(L,{label:"Refresh data",onClick:l,disabled:n,children:(0,s.jsx)(p.A,{size:15,className:n?"animate-spin":""})}),(0,s.jsx)(L,{label:o.dark?"Use light theme":"Use dark theme",onClick:o.toggle,children:o.dark?(0,s.jsx)(g.A,{size:15}):(0,s.jsx)(b.A,{size:15})}),(0,s.jsx)("form",{action:"/logout",method:"POST",children:(0,s.jsx)(L,{label:"Sign out",type:"submit",children:(0,s.jsx)(j.A,{size:15})})})]})]})}function L(e){let{label:t,children:r,...a}=e;return(0,s.jsx)("button",{"aria-label":t,title:t,className:"inline-flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:opacity-50",...a,children:r})}let z=(0,r(3101).F)("inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",{variants:{variant:{default:"bg-beast-lime text-primary-foreground font-semibold hover:bg-beast-lime-hover",secondary:"border border-border bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",outline:"border border-border bg-card shadow-sm hover:bg-muted hover:text-foreground",ghost:"hover:bg-muted hover:text-foreground",destructive:"border border-destructive/30 bg-destructive/15 text-destructive hover:bg-destructive/25"},size:{default:"h-9 px-4 py-2",sm:"h-8 px-3 text-xs",icon:"h-9 w-9"}},defaultVariants:{variant:"default",size:"default"}}),_=a.forwardRef((e,t)=>{let{className:r,variant:a,size:n,...l}=e;return(0,s.jsx)("button",{ref:t,className:S(z({variant:a,size:n}),r),...l})});_.displayName="Button";var M=r(3263);function R(e){let{partitionLabel:t,recordLabel:r,onBack:a}=e;return(0,s.jsxs)("nav",{"aria-label":"Breadcrumb",className:"flex items-center gap-1.5 text-xs text-muted-foreground",children:[(0,s.jsxs)("div",{className:"flex items-center gap-1",children:[(0,s.jsx)(o.A,{size:13,className:"text-primary"}),(0,s.jsx)("span",{className:"font-mono font-medium text-foreground",children:"BeastDB"})]}),(0,s.jsx)(M.A,{size:12,className:"text-muted-foreground/60"}),(0,s.jsx)("button",{type:"button",onClick:a,className:"font-medium text-muted-foreground hover:text-foreground transition",children:t}),r&&(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(M.A,{size:12,className:"text-muted-foreground/60"}),(0,s.jsx)("span",{className:"truncate font-mono font-medium text-foreground max-w-[200px]",children:r})]})]})}var P=r(1286),E=r(4381),T=r(6916),I=r(3173);let D=a.forwardRef((e,t)=>{let{className:r,...a}=e;return(0,s.jsx)("input",{ref:t,className:S("flex h-9 w-full rounded-md border border-input bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50",r),...a})});function B(e){let{mode:t,onModeChange:r,partitions:n,activePartition:l,onSearchText:o,onLookupKey:d,onScanRange:c,busy:u}=e,[x,m]=(0,a.useState)(""),[f,h]=(0,a.useState)(l),[p,g]=(0,a.useState)(""),[b,j]=(0,a.useState)(""),[v,y]=(0,a.useState)("");return(0,a.useEffect)(()=>{h(l)},[l]),(0,s.jsxs)("div",{className:"shrink-0 rounded-xl border border-border bg-card p-3 shadow-sm",children:[(0,s.jsxs)("div",{className:"flex flex-wrap items-center justify-between gap-3 border-b border-border/60 pb-2.5",children:[(0,s.jsxs)("div",{className:"flex items-center gap-1 rounded-lg border border-border bg-muted/60 p-0.5 text-xs",children:[(0,s.jsxs)("button",{type:"button",onClick:()=>r("text"),className:"flex items-center gap-1.5 rounded-md px-3 py-1 font-medium transition ".concat("text"===t?"bg-primary/10 text-primary shadow-sm":"text-muted-foreground hover:text-foreground"),children:[(0,s.jsx)(P.A,{size:13})," Value Text"]}),(0,s.jsxs)("button",{type:"button",onClick:()=>r("exact"),className:"flex items-center gap-1.5 rounded-md px-3 py-1 font-medium transition ".concat("exact"===t?"bg-primary/10 text-primary shadow-sm":"text-muted-foreground hover:text-foreground"),children:[(0,s.jsx)(E.A,{size:13})," Exact Key"]}),(0,s.jsxs)("button",{type:"button",onClick:()=>r("range"),className:"flex items-center gap-1.5 rounded-md px-3 py-1 font-medium transition ".concat("range"===t?"bg-primary/10 text-primary shadow-sm":"text-muted-foreground hover:text-foreground"),children:[(0,s.jsx)(T.A,{size:13})," Key Range"]})]}),(0,s.jsxs)("span",{className:"text-[11px] font-mono text-muted-foreground",children:["text"===t&&"Scans values inside B+ Tree slotted pages","exact"===t&&"O(log N) point lookup in B+ Tree index","range"===t&&"Sequential leaf-node range traversal"]})]}),(0,s.jsxs)("div",{className:"pt-2.5",children:["text"===t&&(0,s.jsxs)("form",{onSubmit:e=>{if(e.preventDefault(),!x.trim()||u)return;let t="all"!==f?Number(f):void 0;o(x.trim(),t)},className:"flex flex-wrap items-center gap-2",children:[(0,s.jsxs)("div",{className:"relative min-w-0 flex-1",children:[(0,s.jsx)(i.A,{size:15,className:"absolute left-3 top-2.5 text-muted-foreground"}),(0,s.jsx)(D,{value:x,onChange:e=>m(e.target.value),placeholder:"Search record content across database (min 2 chars)...",className:"pl-9 text-xs"})]}),(0,s.jsxs)("select",{"aria-label":"Filter search partition",value:f,onChange:e=>h(e.target.value),className:"h-9 rounded-md border border-border bg-card text-foreground px-3 text-xs",children:[(0,s.jsx)("option",{value:"all",children:"Entire Keyspace (All Partitions)"}),n.map(e=>(0,s.jsx)("option",{value:e.prefix,children:e.prefixLabel},e.prefix))]}),(0,s.jsxs)(_,{type:"submit",disabled:x.trim().length<2||u,size:"sm",className:"gap-1.5 text-xs",children:[(0,s.jsx)(i.A,{size:14})," ",u?"Scanning...":"Search Database"]})]}),"exact"===t&&(0,s.jsxs)("form",{onSubmit:e=>{e.preventDefault(),p.trim()&&!u&&d(p.trim())},className:"flex flex-wrap items-center gap-2",children:[(0,s.jsxs)("div",{className:"relative min-w-0 flex-1",children:[(0,s.jsx)(E.A,{size:15,className:"absolute left-3 top-2.5 text-muted-foreground"}),(0,s.jsx)(D,{value:p,onChange:e=>g(e.target.value),placeholder:"Enter 64-bit decimal key (e.g. 72057594037927937)...",className:"pl-9 font-mono text-xs"})]}),(0,s.jsxs)(_,{type:"submit",disabled:!p.trim()||u,size:"sm",className:"gap-1.5 text-xs",children:[(0,s.jsx)(I.A,{size:14})," ",u?"Looking up...":"Find Key"]})]}),"range"===t&&(0,s.jsxs)("form",{onSubmit:e=>{e.preventDefault(),b.trim()&&v.trim()&&!u&&c(b.trim(),v.trim())},className:"flex flex-wrap items-center gap-2",children:[(0,s.jsx)(D,{value:b,onChange:e=>j(e.target.value),placeholder:"Start Key (decimal)...",className:"min-w-0 flex-1 font-mono text-xs"}),(0,s.jsx)("span",{className:"text-xs text-muted-foreground font-mono",children:"to"}),(0,s.jsx)(D,{value:v,onChange:e=>y(e.target.value),placeholder:"End Key (decimal)...",className:"min-w-0 flex-1 font-mono text-xs"}),(0,s.jsxs)(_,{type:"submit",disabled:!b.trim()||!v.trim()||u,size:"sm",className:"gap-1.5 text-xs",children:[(0,s.jsx)(T.A,{size:14})," ",u?"Scanning...":"Scan Range"]})]})]})]})}D.displayName="Input";var U=r(984);function O(e){let{query:t,count:r,scannedCount:a,hasMore:l,prefix:o,prefixLabel:i,loadingMore:d,onContinueScan:c,onClear:u}=e,x=void 0!==o?"Partition 0x".concat(o.toString(16).padStart(2,"0").toUpperCase()).concat(i?" (".concat(i,")"):""):"Entire Keyspace";return(0,s.jsxs)("div",{className:"flex flex-wrap items-center justify-between gap-3 rounded-lg border border-primary/30 bg-primary/5 px-4 py-2.5 text-xs text-foreground",children:[(0,s.jsxs)("div",{className:"flex items-center gap-2",children:[(0,s.jsx)(U.A,{size:16,className:"text-primary shrink-0"}),(0,s.jsxs)("div",{children:[(0,s.jsxs)("span",{className:"font-semibold text-foreground",children:[r," ",1===r?"match":"matches"," found"]}),(0,s.jsxs)("span",{className:"text-muted-foreground",children:[" for “",t,"” \xb7 "]}),(0,s.jsxs)("span",{className:"font-mono text-foreground",children:[a.toLocaleString()," keys inspected in ",x]}),l&&(0,s.jsx)("span",{className:"ml-1 text-amber-600 dark:text-amber-400",children:"(More uninspected keys remain in database)"})]})]}),(0,s.jsxs)("div",{className:"flex items-center gap-2",children:[l&&c&&(0,s.jsxs)(_,{size:"sm",variant:"outline",onClick:c,disabled:d,className:"h-7 text-xs gap-1 border-primary/40 text-primary hover:bg-primary/10",children:[d?(0,s.jsx)(p.A,{size:12,className:"animate-spin"}):(0,s.jsx)(M.A,{size:14}),"Scan Next Chunk"]}),(0,s.jsxs)(_,{size:"sm",variant:"ghost",onClick:u,className:"h-7 text-xs gap-1 text-muted-foreground hover:text-foreground",children:[(0,s.jsx)(n.A,{size:13}),"Clear Results"]})]})]})}var F=r(8971),H=r(8822),K=r(3527),W=r(2265);function q(e){let{records:t,activeStatus:r,onSelectStatus:n}=e,l=(0,a.useMemo)(()=>{let e=new Map;for(let s of t)if(s.status&&"string"==typeof s.status){var r;let t=s.status.trim().toLowerCase();e.set(t,(null!=(r=e.get(t))?r:0)+1)}return[...e.entries()].sort((e,t)=>t[1]-e[1])},[t]);return 0===l.length?null:(0,s.jsxs)("div",{className:"flex items-center gap-1.5 overflow-x-auto pb-1 text-xs",children:[(0,s.jsxs)("button",{type:"button",onClick:()=>n("all"),className:"flex items-center gap-1.5 rounded-full px-3 py-1 font-medium transition ".concat("all"===r?"bg-primary text-primary-foreground shadow-sm":"bg-muted/80 text-muted-foreground hover:bg-muted hover:text-foreground"),children:[(0,s.jsx)("span",{children:"All"}),(0,s.jsx)("span",{className:"font-mono text-[11px] opacity-80",children:t.length})]}),l.map(e=>{let[t,a]=e,l=r===t;return(0,s.jsxs)("button",{type:"button",onClick:()=>n(l?"all":t),className:"flex items-center gap-1.5 rounded-full px-3 py-1 font-medium capitalize transition ".concat(l?"bg-primary text-primary-foreground shadow-sm":"bg-muted/80 text-muted-foreground hover:bg-muted hover:text-foreground"),children:[(0,s.jsx)("span",{children:t}),(0,s.jsx)("span",{className:"font-mono text-[11px] opacity-80",children:a})]},t)})]})}function J(e){let{records:t,activeStatus:r,onSelectStatus:a,viewMode:n,onViewModeChange:l,canWrite:o,onNew:i}=e;return(0,s.jsxs)("div",{className:"flex flex-wrap items-center justify-between gap-3",children:[(0,s.jsx)(q,{records:t,activeStatus:r,onSelectStatus:a}),(0,s.jsxs)("div",{className:"flex items-center gap-2 ml-auto",children:[(0,s.jsxs)("div",{className:"flex items-center rounded-lg border border-border bg-muted/60 p-0.5 text-xs",children:[(0,s.jsxs)("button",{type:"button",onClick:()=>l("table"),title:"Spreadsheet Table View",className:"flex items-center gap-1.5 rounded-md px-2.5 py-1 font-medium transition ".concat("table"===n?"bg-card text-foreground shadow-sm":"text-muted-foreground hover:text-foreground"),children:[(0,s.jsx)(F.A,{size:13}),(0,s.jsx)("span",{className:"hidden sm:inline",children:"Table"})]}),(0,s.jsxs)("button",{type:"button",onClick:()=>l("grid"),title:"Poster / Card Gallery View",className:"flex items-center gap-1.5 rounded-md px-2.5 py-1 font-medium transition ".concat("grid"===n?"bg-card text-foreground shadow-sm":"text-muted-foreground hover:text-foreground"),children:[(0,s.jsx)(H.A,{size:13}),(0,s.jsx)("span",{className:"hidden sm:inline",children:"Gallery"})]}),(0,s.jsxs)("button",{type:"button",onClick:()=>l("split"),title:"Classic 3-Pane Split View",className:"flex items-center gap-1.5 rounded-md px-2.5 py-1 font-medium transition ".concat("split"===n?"bg-card text-foreground shadow-sm":"text-muted-foreground hover:text-foreground"),children:[(0,s.jsx)(K.A,{size:13}),(0,s.jsx)("span",{className:"hidden sm:inline",children:"Split"})]})]}),o&&(0,s.jsxs)(_,{size:"sm",onClick:i,className:"h-7 gap-1 text-xs shadow-sm",children:[(0,s.jsx)(W.A,{size:13})," New Record"]})]})]})}var V=r(6881);let $=["emerald","purple","cyan","blue","indigo","rose","amber","teal","violet","sky","orange"],G=new Set(["user","owner","sender","receiver","actor","target","friend","parent","createdby","updatedby"]),Y=new Set(["data","payload","metadata","details","attributes","fields"]),Z=/^[^\s@]+@[^\s@]+\.[^\s@]+$/,Q=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$|^[0-9a-f]{32,64}$/i;function X(e){return $[e%$.length]}function ee(e){var t;let r=e.replace(/([a-z])([A-Z])/g,"$1 $2").replace(/[_-]/g," ").trim().split(/\s+/).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1).toLowerCase());return 0===r.length?"":[...r.slice(0,-1),(t=r[r.length-1],/^media$/i.test(t)||/s$/i.test(t)?t:/(sh|ch|x|z)$/i.test(t)?"".concat(t,"es"):/[^aeiou]y$/i.test(t)?"".concat(t.slice(0,-1),"ies"):"".concat(t,"s"))].join(" ")}function et(e){let t=new Map,r=0;for(let n of e){var s,a;if("empty"===n.format||!(null==(s=n.raw)?void 0:s.trim()))continue;r++;let e=function(e){if(e.fields)return function(e){var t,r,s,a,n;let l=null!=(a=null!=(s=null!=(r=null!=(t=e.collection)?t:e.entity)?r:e.model)?s:e.table)?a:e._type;if("string"==typeof l&&l.trim())return{label:ee(l),confidence:5};for(let[t,r]of Object.entries(e))if(r&&"object"==typeof r&&!Array.isArray(r)&&!Y.has(t.toLowerCase())&&!G.has(t.toLowerCase())&&("string"==typeof r.title||"string"==typeof r.name||null!=r.id))return{label:ee(t),confidence:4};let o=Object.keys(e);if(null!=e.sku&&null!=e.price)return{label:"Products",confidence:4};if(null!=e.sensorId&&(null!=e.temperature||null!=e.humidity))return{label:"Telemetry",confidence:4};if(null!=e.email&&(null!=e.username||null!=e.passwordHash||null!=e.password_hash))return{label:"Users",confidence:4};if(null!=e.username&&null!=e.role&&null==e.email)return{label:"System Users",confidence:4};if(null!=e.token||null!=e.tokenHash||null!=e.token_hash||null!=e.refreshToken||null!=e.refresh_token)return{label:"Tokens",confidence:4};let i=o.map(e=>{var t;return null==(t=e.match(/^(.+?)(?:Id|_id)$/i))?void 0:t[1]}).filter(e=>!!e).filter(e=>!G.has(e.toLowerCase())&&"item"!==e.toLowerCase());if(1===i.length)return{label:ee(i[0]),confidence:3};let d=null!=(n=e.type)?n:e.kind;if("string"==typeof d&&d.trim())return{label:ee(d),confidence:2}}(e.fields);if("token"===e.format||"string"===e.format&&e.raw.includes("|")&&e.raw.length<250)return{label:"Tokens",confidence:3};if("string"===e.format){let t=e.raw.trim();if(Z.test(t))return{label:"User Index",confidence:3};if(Q.test(t))return{label:"Index Pointers",confidence:2}}}(n);if(!e)continue;let l=null!=(a=t.get(e.label))?a:{score:0,count:0,confidence:0};t.set(e.label,{score:l.score+e.confidence,count:l.count+1,confidence:Math.max(l.confidence,e.confidence)})}let n=[...t].sort((e,t)=>t[1].score-e[1].score)[0];if(n&&(!(n[1].confidence<=2)||n[1].count===r))return n[0]}function er(e,t){var r;return{label:null!=(r=et(e))?r:"Collection ".concat(t.toString(16).padStart(2,"0").toUpperCase()),color:X(t)}}let es={},ea={};function en(e){var t,r;return null!=(r=null!=(t=es[e])?t:ea[e])?r:{label:"Collection ".concat(e.toString(16).padStart(2,"0").toUpperCase()),color:X(e)}}function el(){return Object.entries(es).map(e=>{let[t,r]=e;return{prefix:Number(t),label:r.label,color:r.color,description:r.description}}).sort((e,t)=>e.prefix-t.prefix)}function eo(e){try{let t=BigInt(e),r=Number(t>>56n&255n),s=Number(t>>28n&268435455n),a=Number(268435455n&t),n="0x"+t.toString(16).padStart(16,"0"),l="0x"+r.toString(16).padStart(2,"0").toUpperCase(),o=en(r);return{raw:String(e),hex:n,prefix:r,prefixHex:l,prefixLabel:o.label,color:o.color,userHash:s,itemHash:a}}catch(t){return{raw:String(e),hex:"0x0",prefix:0,prefixHex:"0x00",prefixLabel:"Generic Key",color:"slate",userHash:0,itemHash:0}}}let ei={emerald:"border-emerald-500",teal:"border-teal-500",purple:"border-purple-500",cyan:"border-cyan-500",amber:"border-amber-500",indigo:"border-indigo-500",rose:"border-rose-500",blue:"border-blue-500",violet:"border-violet-500",sky:"border-sky-500",fuchsia:"border-fuchsia-500",orange:"border-orange-500",slate:"border-slate-400"};function ed(e){let{partitions:t,selected:r,total:a,hasMore:n,hasFullCounts:l,onSelect:o}=e;return(0,s.jsxs)("aside",{"aria-label":"Collections",className:"flex min-h-0 flex-col border-r border-border",children:[(0,s.jsxs)("div",{className:"border-b border-border px-5 py-5",children:[(0,s.jsx)("h2",{className:"text-base font-semibold",children:"Collections"}),(0,s.jsxs)("p",{className:"mt-1 text-xs text-muted-foreground",children:[a," records"]})]}),(0,s.jsx)("nav",{"aria-label":"Collection list",className:"min-h-0 flex-1 space-y-1 overflow-y-auto p-2",children:t.map(e=>{var t;let{prefix:a,count:n,prefixLabel:l}=e,i=null!=(t=ei[en(a).color])?t:"border-muted-foreground",d=r===String(a);return(0,s.jsxs)("button",{type:"button",onClick:()=>o(String(a)),"aria-current":d?"page":void 0,className:"flex w-full items-center gap-2.5 rounded-md border-l-4 px-3 py-2.5 text-left text-sm transition ".concat(d?"".concat(i," bg-primary/10 text-foreground font-medium shadow-sm"):"border-transparent text-muted-foreground hover:text-foreground hover:bg-muted"),children:[(0,s.jsx)(V.A,{size:15,className:"shrink-0 ".concat(d?"text-primary":"text-muted-foreground")}),(0,s.jsx)("span",{className:"min-w-0 flex-1 truncate",title:l,children:l}),(0,s.jsx)("span",{className:"text-xs font-mono tabular-nums text-muted-foreground",children:n})]},a)})}),!l&&n&&(0,s.jsx)("p",{className:"border-t border-border px-4 py-2 text-xs text-muted-foreground",children:"More records may be available."})]})}var ec=r(6204);let eu={json_object:"Document",json_array:"List",token:"Delimited text",string:"Text",binary:"Binary data",empty:"Empty record"};function ex(e){return e.replace(/([a-z\d])([A-Z])/g,"$1 $2").replace(/[_-]+/g," ").replace(/\s+/g," ").trim().replace(/^./,e=>e.toUpperCase()).replace(/\bId\b/g,"ID")}function em(e){var t,r;if(e.secondaryLabel)return e.secondaryLabel;if(e.fields){let t=e.attributes.filter(e=>!["name","title","displayName","username","id"].includes(e.key)).slice(0,2).map(e=>"".concat(ex(e.key),": ").concat(e.value)).join(" \xb7 ");if(t)return t}return"json_array"===e.format?"".concat(null!=(r=null==(t=e.arrayItems)?void 0:t.length)?r:0," items"):"empty"===e.format?"No value":eu[e.format]}function ef(e){return(0,s.jsxs)("div",{"aria-label":"Records",className:"flex min-h-0 flex-col border-r border-border",children:[(0,s.jsxs)("div",{className:"flex items-center justify-between gap-2 border-b border-border px-4 py-3",children:[(0,s.jsxs)("div",{className:"flex min-w-0 items-center gap-2",children:[(0,s.jsx)(_,{variant:"ghost",size:"icon",className:"lg:hidden","aria-label":"Back to collections",onClick:e.onBack,children:(0,s.jsx)(ec.A,{size:16})}),(0,s.jsx)("h2",{className:"truncate text-sm font-semibold text-foreground",title:e.title,children:e.title})]}),e.canWrite&&(0,s.jsxs)(_,{size:"sm",onClick:e.onNew,className:"h-7 shrink-0 gap-1 text-xs",children:[(0,s.jsx)(W.A,{size:13})," New"]})]}),(0,s.jsx)("div",{className:"min-h-0 flex-1 overflow-y-auto",children:e.loading&&!e.records.length?(0,s.jsx)("p",{role:"status",className:"p-8 text-center text-sm text-muted-foreground",children:"Loading records…"}):e.records.length?(0,s.jsx)("div",{className:"divide-y divide-border/60",children:e.records.map(t=>{var r;let a=(null==(r=e.selected)?void 0:r.keyStr)===t.keyStr;return(0,s.jsxs)("button",{type:"button",onClick:()=>e.onSelect(t),"aria-current":a?"true":void 0,className:"w-full border-l-2 px-4 py-3 text-left transition ".concat(a?"border-primary bg-primary/10":"border-transparent hover:bg-muted"),children:[(0,s.jsx)("span",{className:"block truncate text-sm font-medium ".concat(a?"text-primary":"text-foreground"),title:t.primaryLabel,children:t.primaryLabel}),(0,s.jsx)("span",{className:"mt-1 block truncate text-xs text-muted-foreground",title:em(t),children:em(t)})]},t.keyStr)})}):(0,s.jsxs)("div",{className:"p-8 text-center",children:[(0,s.jsx)(f.A,{size:22,className:"mx-auto text-muted-foreground"}),(0,s.jsx)("p",{className:"mt-3 text-sm font-medium text-foreground",children:"No records here"}),(0,s.jsx)("p",{className:"mt-1 text-xs text-muted-foreground",children:"Choose another collection or create a record."})]})}),(0,s.jsxs)("div",{className:"flex items-center justify-between gap-2 border-t border-border px-4 py-2.5 text-xs text-muted-foreground",children:[(0,s.jsxs)("span",{children:[e.shownCount," shown",e.hasMore?" \xb7 more available":""]}),e.hasMore&&(0,s.jsx)(_,{variant:"outline",size:"sm",disabled:e.loadingMore,onClick:e.onLoadMore,className:"h-7 text-xs",children:e.loadingMore?"Loading…":"Load more"})]})]})}var eh=r(2649);function ep(e){let t,{keyStr:r,className:a}=e;try{t=BigInt(r).toString(2).padStart(64,"0")}catch(e){return null}return(0,s.jsx)("svg",{width:127,height:7,viewBox:"0 0 ".concat(127," ").concat(7),className:a,role:"img","aria-label":"64-bit key: 8 partition bits, 28 domain bits, 28 item bits",children:Array.from(t).map((e,t)=>{let r="1"===e;return(0,s.jsx)("rect",{x:t%32*4,y:4*Math.floor(t/32),width:3,height:3,rx:.5,className:t<8?r?"fill-primary":"fill-primary/20":r?t<36?"fill-foreground/60":"fill-foreground/35":"fill-border"},t)})})}var eg=r(1190);function eb(e){let{page:t,pageSize:r,totalShown:a,hasMore:n,loadingMore:l,onPageChange:o,onLoadMore:i}=e,d=Math.max(1,Math.ceil(a/r)),c=Math.min(Math.max(1,t),d),u=0===a?0:(c-1)*r+1,x=Math.min(c*r,a);return(0,s.jsxs)("nav",{"aria-label":"Pagination Navigation",className:"flex flex-wrap items-center justify-between gap-3 border-t border-border bg-card/60 px-4 py-2.5 text-xs text-muted-foreground backdrop-blur-sm",children:[(0,s.jsxs)("div",{className:"flex items-center gap-2",children:[(0,s.jsxs)("span",{className:"font-medium text-foreground",children:["Showing ",u,"–",x," of ",a," loaded"]}),(0,s.jsx)("span",{className:"text-[11px] text-muted-foreground/80",children:n?"(more available in partition)":"(all loaded)"})]}),(0,s.jsxs)("div",{className:"flex items-center gap-1.5",children:[n&&i&&c===d&&(0,s.jsx)(_,{variant:"outline",size:"sm",disabled:l,onClick:i,className:"mr-2 h-7 px-2.5 text-xs font-medium","aria-label":"Fetch next page chunk from database engine",children:l?"Loading…":"Fetch more records"}),(0,s.jsx)(_,{variant:"outline",size:"icon",disabled:c<=1,onClick:()=>o(c-1),className:"h-7 w-7","aria-label":"Previous page",children:(0,s.jsx)(eg.A,{size:14})}),(0,s.jsxs)("span",{className:"px-2 text-xs font-mono font-medium text-foreground","aria-current":"page",children:[c," / ",d]}),(0,s.jsx)(_,{variant:"outline",size:"icon",disabled:c>=d,onClick:()=>o(c+1),className:"h-7 w-7","aria-label":"Next page",children:(0,s.jsx)(M.A,{size:14})})]})]})}let ej={completed:"bg-emerald-500/15 text-emerald-600 dark:text-emerald-400 border-emerald-500/30",finished:"bg-emerald-500/15 text-emerald-600 dark:text-emerald-400 border-emerald-500/30",playing:"bg-purple-500/15 text-purple-600 dark:text-purple-400 border-purple-500/30",watching:"bg-cyan-500/15 text-cyan-600 dark:text-cyan-400 border-cyan-500/30",reading:"bg-rose-500/15 text-rose-600 dark:text-rose-400 border-rose-500/30",backlog:"bg-amber-500/15 text-amber-600 dark:text-amber-400 border-amber-500/30",paused:"bg-amber-500/15 text-amber-600 dark:text-amber-400 border-amber-500/30",dropped:"bg-red-500/15 text-red-600 dark:text-red-400 border-red-500/30",active:"bg-blue-500/15 text-blue-600 dark:text-blue-400 border-blue-500/30",admin:"bg-lime-500/15 text-lime-600 dark:text-lime-400 border-lime-500/30"};function ev(e){let{records:t,selectedKey:r,hasMore:n,loadingMore:l,onLoadMore:o,onSelect:i}=e,[d,c]=(0,a.useState)(1),u=(0,a.useMemo)(()=>{let e=(d-1)*25;return t.slice(e,e+25)},[t,d]);return 0===t.length?(0,s.jsxs)("div",{className:"flex flex-1 flex-col items-center justify-center p-12 text-center",children:[(0,s.jsx)(f.A,{size:28,className:"text-muted-foreground/60"}),(0,s.jsx)("p",{className:"mt-3 text-sm font-medium text-foreground",children:"No records match filter"}),(0,s.jsx)("p",{className:"mt-1 text-xs text-muted-foreground",children:"Select another filter or add records."})]}):(0,s.jsxs)("div",{className:"flex min-h-0 flex-1 flex-col overflow-hidden border-t border-border",children:[(0,s.jsx)("div",{className:"min-h-0 flex-1 overflow-auto",children:(0,s.jsxs)("table",{className:"w-full border-collapse text-left text-xs",children:[(0,s.jsx)("thead",{className:"sticky top-0 z-10 border-b border-border bg-muted/90 backdrop-blur-sm",children:(0,s.jsxs)("tr",{children:[(0,s.jsx)("th",{className:"w-12 px-3 py-2.5 font-medium text-muted-foreground",children:"Cover"}),(0,s.jsx)("th",{className:"min-w-[200px] px-3 py-2.5 font-medium text-muted-foreground",children:"Title / Identity"}),(0,s.jsx)("th",{className:"w-28 px-3 py-2.5 font-medium text-muted-foreground",children:"Status"}),(0,s.jsx)("th",{className:"w-20 px-3 py-2.5 font-medium text-muted-foreground",children:"Rating"}),(0,s.jsx)("th",{className:"px-3 py-2.5 font-medium text-muted-foreground",children:"Attributes"}),(0,s.jsx)("th",{className:"w-40 px-3 py-2.5 text-right font-medium text-muted-foreground",children:"Key"})]})}),(0,s.jsx)("tbody",{className:"divide-y divide-border/60",children:u.map(e=>{var t,a;let n=r===e.keyStr,l=null!=(a=ej[String(null!=(t=e.status)?t:"").toLowerCase()])?a:"bg-muted text-muted-foreground border-border";return(0,s.jsxs)("tr",{onClick:()=>i(e),className:"cursor-pointer transition hover:bg-muted/60 ".concat(n?"bg-primary/10 font-medium":""),children:[(0,s.jsx)("td",{className:"px-3 py-2",children:e.coverUrl?(0,s.jsx)("img",{src:e.coverUrl,alt:"",className:"h-9 w-9 shrink-0 rounded object-cover border border-border"}):(0,s.jsx)("div",{className:"flex h-9 w-9 items-center justify-center rounded bg-muted text-muted-foreground",children:(0,s.jsx)(f.A,{size:16})})}),(0,s.jsxs)("td",{className:"px-3 py-2",children:[(0,s.jsx)("p",{className:"truncate font-semibold text-foreground",children:e.primaryLabel}),e.secondaryLabel&&(0,s.jsx)("p",{className:"truncate text-[11px] text-muted-foreground",children:e.secondaryLabel})]}),(0,s.jsx)("td",{className:"px-3 py-2",children:e.status?(0,s.jsx)("span",{className:"inline-flex items-center rounded-full border px-2 py-0.5 text-[10px] font-medium capitalize ".concat(l),children:e.status}):(0,s.jsx)("span",{className:"text-muted-foreground/60",children:"—"})}),(0,s.jsx)("td",{className:"px-3 py-2",children:null!=e.rating?(0,s.jsxs)("span",{className:"inline-flex items-center gap-1 font-mono text-xs font-semibold text-amber-500",children:[(0,s.jsx)(eh.A,{size:11,className:"fill-amber-400"}),String(e.rating)]}):(0,s.jsx)("span",{className:"text-muted-foreground/60",children:"—"})}),(0,s.jsx)("td",{className:"px-3 py-2",children:(0,s.jsx)("div",{className:"flex flex-wrap gap-1",children:e.attributes.slice(0,3).map(e=>(0,s.jsxs)("span",{className:"rounded bg-muted/80 px-1.5 py-0.5 text-[10px] text-muted-foreground font-mono",children:[(0,s.jsxs)("span",{className:"opacity-70",children:[e.key.split(".").pop(),":"]})," ",e.value]},e.key))})}),(0,s.jsxs)("td",{className:"px-3 py-2 text-right font-mono text-[11px] text-muted-foreground",children:[(0,s.jsx)(ep,{keyStr:e.keyStr,className:"ml-auto mb-1"}),function(e){let t=String(e);return t.length>14?t.slice(0,5)+"..."+t.slice(-4):t}(e.keyStr)]})]},e.keyStr)})})]})}),(0,s.jsx)(eb,{page:d,pageSize:25,totalShown:t.length,hasMore:n,loadingMore:l,onPageChange:c,onLoadMore:o})]})}let ey={completed:"bg-emerald-600 text-white",finished:"bg-emerald-600 text-white",playing:"bg-purple-600 text-white",watching:"bg-cyan-600 text-white",reading:"bg-rose-600 text-white",backlog:"bg-amber-600 text-white",active:"bg-blue-600 text-white"};function eN(e){let{records:t,selectedKey:r,hasMore:n,loadingMore:l,onLoadMore:o,onSelect:i}=e,[d,c]=(0,a.useState)(1),u=(0,a.useMemo)(()=>{let e=(d-1)*24;return t.slice(e,e+24)},[t,d]);return 0===t.length?(0,s.jsxs)("div",{className:"flex flex-1 flex-col items-center justify-center p-12 text-center",children:[(0,s.jsx)(f.A,{size:28,className:"text-muted-foreground/60"}),(0,s.jsx)("p",{className:"mt-3 text-sm font-medium text-foreground",children:"No records match filter"}),(0,s.jsx)("p",{className:"mt-1 text-xs text-muted-foreground",children:"Select another filter or add records."})]}):(0,s.jsxs)("div",{className:"flex min-h-0 flex-1 flex-col overflow-hidden border-t border-border",children:[(0,s.jsx)("div",{className:"min-h-0 flex-1 overflow-y-auto p-4",children:(0,s.jsx)("div",{className:"grid grid-cols-2 gap-3 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6",children:u.map(e=>{var t,a;let n=r===e.keyStr,l=null!=(a=ey[String(null!=(t=e.status)?t:"").toLowerCase()])?a:"bg-zinc-800 text-zinc-200";return(0,s.jsxs)("button",{type:"button",onClick:()=>i(e),className:"group flex flex-col overflow-hidden rounded-xl border text-left transition duration-150 hover:shadow-md hover:border-primary/50 ".concat(n?"border-primary ring-2 ring-primary/30 bg-primary/5":"border-border bg-card"),children:[(0,s.jsxs)("div",{className:"relative aspect-[3/4] w-full overflow-hidden bg-muted",children:[e.coverUrl?(0,s.jsx)("img",{src:e.coverUrl,alt:"",className:"h-full w-full object-cover transition duration-300 group-hover:scale-105"}):(0,s.jsxs)("div",{className:"flex h-full w-full flex-col items-center justify-center p-4 text-center text-muted-foreground",children:[(0,s.jsx)(f.A,{size:24,className:"opacity-50"}),(0,s.jsx)("span",{className:"mt-2 text-[10px] font-mono opacity-70 truncate max-w-full",children:e.prefixLabel})]}),e.status&&(0,s.jsx)("span",{className:"absolute top-2 right-2 rounded-full px-2 py-0.5 text-[9px] font-semibold uppercase tracking-wider shadow-sm ".concat(l),children:e.status}),null!=e.rating&&(0,s.jsxs)("span",{className:"absolute bottom-2 left-2 flex items-center gap-1 rounded-md bg-black/75 px-1.5 py-0.5 text-[10px] font-bold text-amber-400 backdrop-blur-sm shadow-sm",children:[(0,s.jsx)(eh.A,{size:10,className:"fill-amber-400"}),String(e.rating)]})]}),(0,s.jsxs)("div",{className:"flex flex-1 flex-col p-2.5",children:[(0,s.jsx)("p",{className:"line-clamp-2 text-xs font-semibold text-foreground group-hover:text-primary transition",children:e.primaryLabel}),e.secondaryLabel&&(0,s.jsx)("p",{className:"mt-0.5 line-clamp-1 text-[10px] text-muted-foreground",children:e.secondaryLabel})]})]},e.keyStr)})})}),(0,s.jsx)(eb,{page:d,pageSize:24,totalShown:t.length,hasMore:n,loadingMore:l,onPageChange:c,onLoadMore:o})]})}var ew=r(2036);function ek(e){let{value:t}=e;if(null===t)return(0,s.jsx)("span",{className:"text-muted-foreground",children:"null"});if(Array.isArray(t))return 0===t.length?(0,s.jsx)("span",{className:"text-muted-foreground",children:"None"}):t.every(e=>null===e||"object"!=typeof e)?(0,s.jsx)("span",{className:"break-words",children:t.map(e=>String(null!=e?e:"null")).join(", ")}):(0,s.jsx)("ul",{className:"space-y-2",children:t.map((e,t)=>(0,s.jsx)("li",{children:(0,s.jsx)(ek,{value:e})},t))});if("object"==typeof t)return(0,s.jsx)("dl",{className:"divide-y divide-border/50 rounded-md border border-border/70 px-3",children:Object.entries(t).map(e=>{let[t,r]=e;return(0,s.jsxs)("div",{className:"grid min-w-0 grid-cols-[minmax(6rem,32%)_minmax(0,1fr)] gap-3 py-2 text-left",children:[(0,s.jsx)("dt",{className:"text-xs text-muted-foreground",children:ex(t)}),(0,s.jsx)("dd",{className:"min-w-0 break-words text-sm",children:(0,s.jsx)(ek,{value:r})})]},t)})});if("boolean"==typeof t)return(0,s.jsx)("span",{children:t?"Yes":"No"});if("string"==typeof t&&/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(t)){let e=new Date(t);if(!Number.isNaN(e.getTime()))return(0,s.jsx)("time",{dateTime:t,title:t,children:e.toLocaleString()})}return"string"==typeof t&&/^https?:\/\//.test(t)?(0,s.jsxs)("div",{className:"space-y-2",children:[/\.(png|jpe?g|webp|gif|svg)(\?|$)/i.test(t)&&(0,s.jsx)("img",{src:t,alt:"Record field preview",className:"max-h-40 rounded-md border object-contain"}),(0,s.jsx)("a",{className:"break-all underline underline-offset-2",href:t,target:"_blank",rel:"noreferrer",children:t})]}):(0,s.jsx)("span",{className:"whitespace-pre-wrap break-words",children:String(t)})}function eS(e){let{fields:t,identifiers:r=!1}=e,a=Object.entries(t).filter(e=>{let[t]=e;return("id"===t||t.endsWith("Id")||/(?:^|[_-])id$/i.test(t))===r});return 0===a.length?null:(0,s.jsx)("dl",{className:"divide-y divide-border/60 rounded-lg border border-border bg-card px-4",children:a.map(e=>{let[t,r]=e,a=null!==r&&"object"==typeof r;return(0,s.jsxs)("div",{className:a?"py-3":"grid grid-cols-[minmax(7rem,30%)_minmax(0,1fr)] gap-3 py-3",children:[(0,s.jsx)("dt",{className:"text-xs text-muted-foreground",children:ex(t)}),(0,s.jsx)("dd",{className:a?"mt-2 min-w-0":"min-w-0 break-words text-sm",children:(0,s.jsx)(ek,{value:r})})]},t)})})}var eC=r(2033),eA=r(3630),eL=r(443);let ez={emerald:"text-emerald-500",teal:"text-teal-500",purple:"text-purple-500",cyan:"text-cyan-500",amber:"text-amber-500",indigo:"text-indigo-500",rose:"text-rose-500",blue:"text-blue-500",violet:"text-violet-500",sky:"text-sky-500",slate:"text-slate-400"};function e_(e){var t;let{collection:r,onSelect:a}=e,n=null!=(t=ez[r.color])?t:"text-muted-foreground";return(0,s.jsxs)("details",{open:!0,className:"group rounded-lg border border-border bg-card/50 text-sm",children:[(0,s.jsxs)("summary",{className:"flex cursor-pointer select-none items-center justify-between px-4 py-2.5 hover:bg-muted/50 rounded-lg",children:[(0,s.jsx)("span",{className:"font-medium ".concat(n),children:r.label}),(0,s.jsx)("span",{className:"ml-2 rounded-full bg-muted px-2 py-0.5 text-xs font-mono text-muted-foreground",children:r.records.length})]}),(0,s.jsx)("ul",{className:"divide-y divide-border/60 border-t border-border",children:r.records.map(e=>(0,s.jsx)("li",{children:(0,s.jsxs)("button",{type:"button",onClick:()=>a(e),className:"flex w-full items-center gap-3 px-4 py-2.5 text-left hover:bg-muted/50 transition",children:[e.coverUrl&&(0,s.jsx)("img",{src:e.coverUrl,alt:"",className:"h-8 w-8 shrink-0 rounded object-cover border border-border"}),(0,s.jsxs)("div",{className:"min-w-0 flex-1",children:[(0,s.jsx)("p",{className:"truncate text-xs font-medium text-foreground",children:e.primaryLabel}),e.secondaryLabel&&(0,s.jsx)("p",{className:"truncate text-[11px] text-muted-foreground",children:e.secondaryLabel})]}),e.status&&(0,s.jsx)("span",{className:"shrink-0 rounded-full bg-muted px-2 py-0.5 text-[10px] text-muted-foreground capitalize",children:e.status})]})},e.keyStr))})]})}function eM(e){if(0===e)return"0 B";let t=Math.floor(Math.log(e)/Math.log(1024));return"".concat(parseFloat((e/Math.pow(1024,t)).toFixed(1))," ").concat(["B","KB","MB","GB"][t])}function eR(e){if(!e)return"N/A";try{let t=new Date(e);if(isNaN(t.getTime()))return e;let r=Math.floor((Date.now()-t.getTime())/1e3);if(r<60)return"Just now";if(r<3600)return"".concat(Math.floor(r/60),"m ago");if(r<86400)return"".concat(Math.floor(r/3600),"h ago");if(r<604800)return"".concat(Math.floor(r/86400),"d ago");return t.toLocaleDateString()}catch(t){return e}}function eP(e){let t=BigInt("0xcbf29ce484222325"),r=BigInt("0x100000001b3"),s=BigInt("0xffffffffffffffff");for(let a=0;a{if(null==e||!r)return;let a=Object.keys(r).map(Number).filter(e=>e!==t&&r[String(e)]>0);if(0===a.length)return void i(!0);l(a.map(e=>{let t=en(e);return{prefix:e,label:t.label,color:t.color,records:[],loading:!0}})),i(!1),l((await Promise.all(a.map(async t=>{let r=en(t),a=BigInt(t)<<56n|BigInt(e)<<28n,n=a.toString(),l=(268435455n|a).toString();try{let e=await s(n,l),a=et(e),o=r.label.startsWith("Collection ")&&a?a:r.label;return{prefix:t,label:o,color:r.color,records:e,loading:!1}}catch(e){return{prefix:t,label:r.label,color:r.color,records:[],loading:!1}}}))).filter(e=>e.records.length>0).sort((e,t)=>e.prefix-t.prefix)),i(!0)},[e,t,r,s]);return(0,a.useEffect)(()=>{d()},[d]),{collections:n,ready:o}}(d.userHash,d.prefix,h,m),w=v.reduce((e,t)=>e+t.records.length,0);return(0,s.jsxs)("div",{className:"flex flex-col gap-4 p-5",children:[(0,s.jsxs)("div",{className:"flex items-center gap-4",children:[(0,s.jsx)("div",{className:"flex h-14 w-14 shrink-0 items-center justify-center rounded-xl bg-beast-lime text-xl font-bold font-mono text-black shadow-[0_0_20px_rgba(168,242,26,0.25)]",children:p}),(0,s.jsxs)("div",{className:"min-w-0 flex-1",children:[(0,s.jsxs)("div",{className:"flex items-center gap-2",children:[(0,s.jsx)("h2",{className:"truncate text-lg font-bold text-foreground tracking-tight",children:(null==(i=d.fields)?void 0:i.username)||d.primaryLabel}),(0,s.jsx)(y,{role:b})]}),g&&(0,s.jsx)("p",{className:"truncate text-xs text-muted-foreground font-mono mt-0.5",children:g}),j&&(0,s.jsxs)("p",{className:"text-[11px] text-muted-foreground mt-0.5",children:["Joined ",eR(j)]})]})]}),(0,s.jsxs)("div",{className:"flex flex-wrap gap-1.5",children:[v.map(e=>(0,s.jsxs)("span",{className:"rounded-full border border-border bg-muted px-2.5 py-1 text-[11px] font-medium text-muted-foreground",children:[e.label," ",(0,s.jsx)("span",{className:"font-mono text-foreground",children:e.records.length})]},e.prefix)),!N&&(0,s.jsxs)("span",{className:"flex items-center gap-1 text-[11px] text-muted-foreground",children:[(0,s.jsx)(eC.A,{size:10,className:"animate-spin"})," Loading…"]}),N&&0===w&&(0,s.jsx)("span",{className:"text-[11px] text-muted-foreground",children:"No related records found."})]}),v.length>0&&(0,s.jsxs)("section",{className:"space-y-2",children:[(0,s.jsx)("h3",{className:"text-xs font-semibold uppercase tracking-wide text-muted-foreground",children:"Library"}),v.map(e=>(0,s.jsx)(e_,{collection:e,onSelect:f},e.prefix))]}),c&&(0,s.jsxs)("div",{className:"flex flex-col gap-2 pt-2 border-t border-border",children:[(0,s.jsxs)(_,{variant:"outline",size:"sm",className:"justify-start gap-2 text-xs",onClick:()=>u(d.keyStr),children:[(0,s.jsx)(eA.A,{size:13,className:"text-muted-foreground"})," Delete Credential Only"]}),null!=d.userHash&&(0,s.jsxs)(_,{variant:"destructive",size:"sm",className:"justify-start gap-2 text-xs bg-destructive/15 border border-destructive/30 text-destructive hover:bg-destructive hover:text-destructive-foreground",onClick:()=>x(d.userHash),children:[(0,s.jsx)(eL.A,{size:13})," Cascade Purge User & All Records"]})]})]})}var eI=r(1288),eD=r(5524),eB=r(5958);function eU(e){let{keyStr:t,prefixLabel:r,userHash:a,itemHash:n,onNotice:l}=e,o="";try{o=BigInt(t).toString(2).padStart(64,"0")}catch(e){return null}let i=o.slice(0,8),d=o.slice(8,36),c=o.slice(36,64),u="0x"+Number(BigInt("0b"+i)).toString(16).padStart(2,"0").toUpperCase(),x=async()=>{try{await navigator.clipboard.writeText(o),null==l||l("64-bit binary copied.")}catch(e){null==l||l("Copy failed.",!0)}};return(0,s.jsxs)("div",{className:"space-y-3 rounded-lg border border-border bg-card p-3.5",children:[(0,s.jsxs)("div",{className:"flex items-center justify-between",children:[(0,s.jsx)("span",{className:"text-[11px] font-semibold font-mono uppercase tracking-wider text-muted-foreground",children:"64-Bit Keyspace Anatomy"}),(0,s.jsx)(_,{variant:"ghost",size:"icon",className:"h-6 w-6 text-muted-foreground hover:text-foreground",onClick:()=>void x(),title:"Copy 64-bit binary",children:(0,s.jsx)(eI.A,{size:12})})]}),(0,s.jsxs)("div",{className:"flex h-3 w-full overflow-hidden rounded bg-muted border border-border",children:[(0,s.jsx)("div",{className:"h-full bg-beast-lime transition-all",style:{width:"12.5%"},title:"Partition Prefix: 8 bits (".concat(i,")")}),(0,s.jsx)("div",{className:"h-full bg-cyan-400 transition-all",style:{width:"43.75%"},title:"Domain/User Hash: 28 bits (".concat(d,")")}),(0,s.jsx)("div",{className:"h-full bg-purple-400 transition-all",style:{width:"43.75%"},title:"Item/Sequence: 28 bits (".concat(c,")")})]}),(0,s.jsxs)("div",{className:"grid grid-cols-3 gap-2 text-[11px]",children:[(0,s.jsxs)("div",{className:"rounded border border-border bg-muted/40 p-2",children:[(0,s.jsxs)("div",{className:"flex items-center gap-1.5",children:[(0,s.jsx)("span",{className:"h-2 w-2 rounded-full bg-primary"}),(0,s.jsx)("span",{className:"text-muted-foreground font-mono text-[10px]",children:"PREFIX (8b)"})]}),(0,s.jsxs)("p",{className:"mt-1 font-mono font-medium text-primary truncate",children:[u," ",r?"\xb7 ".concat(r):""]})]}),(0,s.jsxs)("div",{className:"rounded border border-border bg-muted/40 p-2",children:[(0,s.jsxs)("div",{className:"flex items-center gap-1.5",children:[(0,s.jsx)("span",{className:"h-2 w-2 rounded-full bg-cyan-400"}),(0,s.jsx)("span",{className:"text-muted-foreground font-mono text-[10px]",children:"DOMAIN (28b)"})]}),(0,s.jsx)("p",{className:"mt-1 font-mono font-medium text-cyan-600 dark:text-cyan-400 truncate",children:null!=a?a:"—"})]}),(0,s.jsxs)("div",{className:"rounded border border-border bg-muted/40 p-2",children:[(0,s.jsxs)("div",{className:"flex items-center gap-1.5",children:[(0,s.jsx)("span",{className:"h-2 w-2 rounded-full bg-purple-400"}),(0,s.jsx)("span",{className:"text-muted-foreground font-mono text-[10px]",children:"ITEM / SEQ (28b)"})]}),(0,s.jsx)("p",{className:"mt-1 font-mono font-medium text-purple-600 dark:text-purple-400 truncate",children:null!=n?n:"—"})]})]})]})}function eO(e){let{record:t,formattedRaw:r,canWrite:a,onDelete:n,onNotice:l}=e,o=async(e,t)=>{try{await navigator.clipboard.writeText(e),l("".concat(t," copied."))}catch(e){l("Copy failed.",!0)}},i=async()=>{let e=new URL(window.location.href);e.searchParams.set("key",t.keyStr),e.searchParams.set("partition",String(t.prefix)),await o(e.toString(),"Shareable Link")};return(0,s.jsxs)("details",{className:"group rounded-lg border border-border bg-card/40 text-xs",children:[(0,s.jsxs)("summary",{className:"flex cursor-pointer select-none items-center justify-between px-4 py-2.5 font-mono text-muted-foreground hover:text-foreground hover:bg-muted/40 rounded-lg",children:[(0,s.jsx)("span",{children:"ENGINE & STORAGE INTERNALS"}),(0,s.jsx)("span",{className:"text-[10px] opacity-70",children:"Click to expand"})]}),(0,s.jsxs)("div",{className:"space-y-3.5 border-t border-border p-3.5",children:[t.fields&&(0,s.jsx)(eS,{fields:t.fields,identifiers:!0}),(0,s.jsxs)("p",{className:"text-muted-foreground",children:[eu[t.format]," \xb7 ",eM(t.byteSize)]}),(0,s.jsxs)("div",{className:"font-mono text-muted-foreground space-y-1",children:[(0,s.jsxs)("p",{className:"break-all",children:["Key: ",(0,s.jsx)("span",{className:"text-foreground font-semibold",children:t.keyStr})]}),(0,s.jsxs)("p",{className:"break-all",children:["Hex: ",(0,s.jsx)("span",{className:"text-foreground font-semibold",children:t.keyHex})]})]}),(0,s.jsxs)("div",{className:"flex flex-wrap gap-2",children:[(0,s.jsxs)(_,{variant:"outline",size:"sm",onClick:()=>void o(t.keyStr,"Key"),children:[(0,s.jsx)(eI.A,{size:12})," Key"]}),(0,s.jsxs)(_,{variant:"outline",size:"sm",onClick:()=>void o(r,"Raw"),children:[(0,s.jsx)(eD.A,{size:12})," JSON"]}),(0,s.jsxs)(_,{variant:"outline",size:"sm",onClick:()=>void i(),children:[(0,s.jsx)(eB.A,{size:12})," Link"]}),a&&(0,s.jsxs)(_,{variant:"ghost",size:"sm",className:"text-destructive hover:bg-destructive/10",onClick:()=>n(t.keyStr),children:[(0,s.jsx)(eA.A,{size:12})," Delete"]})]}),(0,s.jsx)(eU,{keyStr:t.keyStr,prefixLabel:t.prefixLabel,userHash:t.userHash,itemHash:t.itemHash,onNotice:l}),(0,s.jsxs)("div",{children:[(0,s.jsx)("p",{className:"mb-1.5 text-muted-foreground font-semibold",children:"Raw Storage Payload"}),(0,s.jsx)("pre",{className:"max-h-56 overflow-auto rounded-lg border border-border bg-muted/40 p-2.5 font-mono text-[11px] leading-relaxed text-foreground whitespace-pre-wrap break-all",children:r})]})]})]})}function eF(e){var t,r;let{record:a,canWrite:n,onDelete:l,onDeleteUser:o,onEdit:i,onNotice:d,onScanRange:c,onSelect:u,partitionCounts:x}=e,m=(()=>{try{return JSON.stringify(JSON.parse(a.raw),null,2)}catch(e){return a.raw}})(),f=async()=>[];return(null==(t=a.fields)?void 0:t.email)||(null==(r=a.fields)?void 0:r.username)?(0,s.jsxs)("div",{className:"min-h-0 flex-1 overflow-y-auto",children:[(0,s.jsx)(eT,{record:a,canWrite:n,onDelete:l,onDeleteUser:null!=o?o:()=>{},onScanRange:null!=c?c:f,onSelect:null!=u?u:()=>{},partitionCounts:x}),(0,s.jsx)("div",{className:"px-5 pb-5",children:(0,s.jsx)(eO,{record:a,formattedRaw:m,canWrite:n,onDelete:l,onNotice:d})})]}):(0,s.jsxs)("div",{className:"flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto px-5 py-5",children:[(0,s.jsxs)("div",{className:"border-b border-border/70 pb-4",children:[(0,s.jsxs)("div",{className:"flex items-center justify-between gap-2",children:[(0,s.jsx)("span",{className:"truncate text-xs font-medium text-muted-foreground",children:a.prefixLabel}),n&&i&&(0,s.jsxs)(_,{variant:"outline",size:"sm",onClick:i,className:"h-7 shrink-0 gap-1 text-xs",children:[(0,s.jsx)(ew.A,{size:13})," Edit"]})]}),(0,s.jsx)("h2",{className:"mt-2 text-lg font-bold tracking-tight text-foreground",children:a.primaryLabel}),a.secondaryLabel&&(0,s.jsx)("p",{className:"text-xs text-muted-foreground mt-0.5",children:a.secondaryLabel})]}),a.coverUrl&&(0,s.jsx)("img",{src:a.coverUrl,alt:"",className:"w-full max-h-56 rounded-xl object-cover border border-border shadow-sm"}),a.fields&&(0,s.jsxs)("section",{className:"space-y-2",children:[(0,s.jsx)("h3",{className:"text-xs font-semibold uppercase tracking-wider text-muted-foreground",children:"Document Fields"}),(0,s.jsx)(eS,{fields:a.fields})]}),a.arrayItems&&(0,s.jsxs)("section",{className:"space-y-2",children:[(0,s.jsxs)("h3",{className:"text-xs font-semibold uppercase tracking-wider text-muted-foreground",children:["Items (",a.arrayItems.length,")"]}),(0,s.jsx)("div",{className:"rounded-lg border border-border bg-card p-3 text-sm",children:(0,s.jsx)(ek,{value:a.arrayItems})})]}),!a.fields&&!a.arrayItems&&(0,s.jsx)("div",{className:"rounded-lg border border-border bg-card p-3 text-sm",children:(0,s.jsx)(ek,{value:a.raw||"Empty value"})}),(0,s.jsx)(eO,{record:a,formattedRaw:m,canWrite:n,onDelete:l,onNotice:d})]})}function eH(e){var t,r;let a="split"===e.viewMode;return(0,s.jsxs)("div",{className:"grid min-h-0 flex-1 overflow-hidden rounded-xl border bg-card shadow-sm ".concat(a?"lg:grid-cols-[220px_minmax(320px,1fr)_minmax(380px,1.4fr)]":e.selected?"lg:grid-cols-[220px_minmax(0,1fr)_420px]":"lg:grid-cols-[220px_minmax(0,1fr)]"),children:[(0,s.jsx)("div",{className:"".concat("partitions"!==e.pane?"hidden lg:flex":"flex"," min-h-0 flex-col"),children:(0,s.jsx)(ed,{partitions:e.partitions,selected:e.partition,total:e.totalCount,hasMore:e.hasMore,hasFullCounts:!!e.partitionCounts,onSelect:e.onChoosePartition})}),(0,s.jsx)("div",{className:"".concat("documents"!==e.pane?"hidden lg:flex":"flex"," min-h-0 flex-col overflow-hidden"),children:a?(0,s.jsx)(ef,{records:e.records,selected:e.selected,loading:e.loading,loadingMore:e.loadingMore,hasMore:e.hasMore,title:e.partitionTitle,shownCount:e.records.length,onLoadMore:e.onLoadMore,onSelect:t=>{e.onSelect(t),e.onSetPane("details")},onBack:()=>e.onSetPane("partitions"),canWrite:e.canWrite,onNew:e.onNew}):"table"===e.viewMode?(0,s.jsx)(ev,{records:e.records,selectedKey:null==(t=e.selected)?void 0:t.keyStr,hasMore:e.hasMore,loadingMore:e.loadingMore,onLoadMore:e.onLoadMore,onSelect:t=>{e.onSelect(t),e.onSetPane("details")}}):(0,s.jsx)(eN,{records:e.records,selectedKey:null==(r=e.selected)?void 0:r.keyStr,hasMore:e.hasMore,loadingMore:e.loadingMore,onLoadMore:e.onLoadMore,onSelect:t=>{e.onSelect(t),e.onSetPane("details")}})}),(a||e.selected)&&(0,s.jsxs)("div",{className:"".concat("details"!==e.pane?"hidden lg:flex":"flex"," min-h-0 flex-col border-l border-border bg-card/60 relative"),children:[!a&&e.selected&&(0,s.jsx)("div",{className:"absolute top-3 right-3 z-10",children:(0,s.jsx)(_,{variant:"ghost",size:"icon",className:"h-7 w-7 rounded-full text-muted-foreground hover:text-foreground",onClick:()=>e.onSelect(null),"aria-label":"Close details",children:(0,s.jsx)(n.A,{size:15})})}),e.selected?(0,s.jsx)(eF,{record:e.selected,canWrite:e.canWrite,onDelete:e.onDelete,onDeleteUser:e.onDeleteUser,onEdit:e.onEdit?()=>e.onEdit(e.selected):void 0,onNotice:e.onNotice,onScanRange:e.onScanRange,onSelect:t=>{e.onSelect(t),e.onSetPane("details")},partitionCounts:e.partitionCounts}):(0,s.jsxs)("div",{className:"flex flex-1 flex-col items-center justify-center p-8 text-center",children:[(0,s.jsx)("div",{className:"rounded-xl border bg-muted p-4",children:(0,s.jsx)(f.A,{size:24,className:"text-muted-foreground"})}),(0,s.jsx)("h2",{className:"mt-4 font-medium text-foreground",children:"Select a record"}),(0,s.jsx)("p",{className:"mt-1 max-w-xs text-sm text-muted-foreground",children:"Choose a record to inspect its fields and storage details."})]})]})]})}async function eK(e,t){let r;try{r=await fetch(e,t)}catch(e){throw Error("Cannot reach BeastDB. Check your connection and retry.")}if(401===r.status)throw Error("Your session expired. Sign in again.");if(403===r.status)throw Error("Your account cannot make this change.");if(404===r.status)throw Error(e.startsWith("/api/key?")?"That record was not found.":"Data endpoint unavailable.");if(!r.ok)throw Error((await r.text()).trim()||"Request failed.");return 204===r.status||201===r.status?void 0:r.json()}let eW=new Set(["passwordHash","password_hash","password","salt","password_salt","token","secret","apiKey","api_key","apiSecret","api_secret","privateKey","private_key","sessionToken","session_secret","api_token","refresh_token"]);function eq(e,t){for(let[r,s]of Object.entries(e))if(t.includes(r.replace(/[_-]/g,"").toLowerCase())&&"string"==typeof s&&s.trim())return s.trim()}function eJ(e){var t,r,s,a,n,l,o;let i,d,c,u,x,m=eo(e.keyText),f=null!=(t=e.value)?t:"",h=new Blob([f]).size,p=f.trim(),g="string",b="",j="",v=[];if(p){if(p.startsWith("{")&&p.endsWith("}"))try{let e=JSON.parse(p);if("object"==typeof e&&null!==e&&!Array.isArray(e)){g="json_object",i=Object.fromEntries(Object.entries(e).filter(e=>{let[t]=e;return!eW.has(t)})),c=function(e){if(e&&"object"==typeof e){for(let t of[e.coverUrl,e.posterUrl,e.avatarUrl,e.cover_url,e.poster_url,e.avatar_url,e.avatar,e.thumbnail,e.image,e.icon,e.photo])if("string"==typeof t&&t.startsWith("http"))return t;for(let t of Object.values(e))if("string"==typeof t&&t.match(/^https?:\/\/.+\.(jpg|jpeg|png|webp|gif|svg)(\?.*)?$/i))return t}}(e);let t=null!=(r=function e(t){let r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,s=eq(t,["title","displayname","name","label"]);if(s||r>=2)return s;for(let s of Object.values(t))if(s&&"object"==typeof s&&!Array.isArray(s)){let t=e(s,r+1);if(t)return t}}(e))?r:eq(e,["username","email"]),d=eq(e,["description","summary","subtitle","notes"]);for(let[r,i]of(b=null!=(s=null!=t?t:d)?s:null!=e.id?"Record ".concat(e.id):"Record ".concat(m.raw.slice(-6)),j=[e.status,t?d:void 0].filter(Boolean).join(" \xb7 "),u=null!=(n=null!=(a=e.status)?a:e.role)?n:e.type,x=null!=(o=null!=(l=e.rating)?l:e.score)?o:e.userRating,Object.entries(e)))if(!eW.has(r)&&!["coverUrl","posterUrl","avatarUrl","description"].includes(r))if("object"!=typeof i||null===i||Array.isArray(i))v.length<5&&v.push({key:r,value:Array.isArray(i)?"[".concat(i.length,"]"):String(i),type:typeof i});else for(let[e,t]of Object.entries(i))v.length<5&&"object"!=typeof t&&v.push({key:"".concat(r,".").concat(e),value:String(t),type:typeof t})}}catch(e){g="string"}else if(p.startsWith("[")&&p.endsWith("]"))try{let e=JSON.parse(p);Array.isArray(e)&&(g="json_array",d=e,b="Array [".concat(e.length," items]"),v.push({key:"length",value:String(e.length),type:"number"}))}catch(e){g="string"}else if(p.includes("|")&&p.length<250){g="token";let e=p.split("|");b=e[0]||"Token Payload",j=e.slice(1).join(" • "),e.forEach((e,t)=>v.push({key:"part_".concat(t+1),value:e,type:"string"}))}}else g="empty",b="Empty record";return"string"===g&&(b=p.length>50?p.slice(0,47)+"...":p,v.push({key:"length",value:"".concat(p.length," chars"),type:"number"})),{keyStr:m.raw,keyHex:m.hex,prefix:m.prefix,prefixLabel:m.prefixLabel,userHash:m.userHash,itemHash:m.itemHash,format:g,byteSize:h,primaryLabel:b||"Key ".concat(m.prefixHex),secondaryLabel:j,coverUrl:c,status:u,rating:x,attributes:v,fields:i,arrayItems:d,raw:f}}function eV(e){var t,r,n;let l=function(e){let[t,r]=(0,a.useState)(0),s=JSON.stringify(null!=e?e:{});return(0,a.useEffect)(()=>{let t=new AbortController,s=Object.entries(null!=e?e:{}).filter(e=>{let[,t]=e;return t>0}).map(e=>{let[t]=e;return Number(t)}).filter(e=>Number.isInteger(e)&&e>=0&&e<=255);async function a(){let e={};for(let a=0;a{let s=BigInt(r)<<56n,a=(BigInt(r)+1n<<56n)-1n;try{let n=await eK("/api/records?start=".concat(s,"&end=").concat(a,"&limit=24"),{signal:t.signal});if(t.signal.aborted)return;e[r]=er(n.records.map(eJ),r)}catch(e){}})),t.signal.aborted)return;ea={...e},r(e=>e+1)}}return ea={},r(e=>e+1),a(),()=>t.abort()},[s]),t}(e.partitionCounts),o=(0,a.useRef)(!1),[i,d]=(0,a.useState)("documents"),[c,u]=(0,a.useState)("text"),[x,m]=(0,a.useState)("table"),[f,h]=(0,a.useState)("all"),[p,g]=(0,a.useState)(!1),[b,j]=(0,a.useState)(!1),[v,y]=(0,a.useState)(null),[N,w]=(0,a.useState)(null),k=(0,a.useMemo)(()=>(function(e,t){var r,s,a;let n=new Map,l=new Map;for(let t of e){let e=l.get(t.prefix);e||(e=[],l.set(t.prefix,e)),e.push(t)}let o=e=>{if(es[e]||ea[e])return en(e);let t=l.get(e);return(null==t?void 0:t.length)?er(t,e):en(e)};for(let e of el()){let s=t&&null!=(r=t[String(e.prefix)])?r:0;n.set(e.prefix,{prefixLabel:e.label,count:s})}for(let[e,r]of Object.entries(null!=t?t:{})){let t=Number(e);if(!n.has(t)){let e=o(t);n.set(t,{prefixLabel:e.label,count:r})}}for(let r of e){let e=n.get(r.prefix);if(e)t||(e.count+=1),e.prefixLabel.startsWith("Collection ")&&(e.prefixLabel=o(r.prefix).label);else{let e=t&&null!=(s=t[String(r.prefix)])?s:1,a=o(r.prefix);n.set(r.prefix,{prefixLabel:a.label,count:e})}}let i=new Map;for(let e of n.values())i.set(e.prefixLabel,(null!=(a=i.get(e.prefixLabel))?a:0)+1);return[...n.entries()].sort((e,t)=>{let[r]=e,[s]=t;return r-s}).map(e=>{var t;let[r,s]=e;return{prefix:r,count:s.count,prefixLabel:(null!=(t=i.get(s.prefixLabel))?t:0)>1?"".concat(s.prefixLabel," \xb7 ").concat(r.toString(16).padStart(2,"0").toUpperCase()):s.prefixLabel}})})(e.records,e.partitionCounts),[e.records,e.partitionCounts,l]),[S,C]=(0,a.useState)("1");(0,a.useEffect)(()=>{var t;if(o.current||!e.partitionCounts||0===k.length)return;o.current=!0;let r=k.some(e=>1===e.prefix)?1:k[0].prefix;C(String(r)),null==(t=e.onLoadPartition)||t.call(e,r)},[k,e.partitionCounts,e.onLoadPartition]);let A=(0,a.useMemo)(()=>e.partitionCounts?Object.values(e.partitionCounts).reduce((e,t)=>e+t,0):e.records.length,[e.partitionCounts,e.records.length]),L=b&&v?v:e.records,z=(0,a.useMemo)(()=>"all"===f?L:L.filter(e=>{var t;return String(null!=(t=e.status)?t:"").trim().toLowerCase()===f}),[L,f]),M=async t=>{g(!0);try{await t()}catch(t){e.onNotice(t.message,!0)}finally{g(!1)}},P=(null==(t=k.find(e=>String(e.prefix)===S))?void 0:t.prefixLabel)||"Collection ".concat(Number(S).toString(16).padStart(2,"0").toUpperCase());return(0,s.jsxs)("section",{"aria-labelledby":"records-title",className:"flex min-h-0 flex-1 flex-col gap-3",children:[(0,s.jsx)("div",{className:"flex flex-wrap items-center justify-between gap-4",children:(0,s.jsxs)("div",{children:[(0,s.jsx)(R,{partitionLabel:P,recordLabel:null==(r=e.selected)?void 0:r.primaryLabel,onBack:()=>d("partitions")}),(0,s.jsx)("h1",{id:"records-title",className:"mt-1 text-[22px] font-bold leading-tight tracking-tight",children:"Table Editor"}),(0,s.jsx)("p",{className:"mt-1 text-sm text-muted-foreground",children:"Browse, search and edit your records."})]})}),(0,s.jsx)(B,{mode:c,onModeChange:u,partitions:k,activePartition:S,onSearchText:(t,r)=>M(async()=>{if(!e.onSearch)return;let s=await e.onSearch(t,{prefix:r});y(s.records),w({...s,query:t}),j(!0),e.onNotice("Found ".concat(s.count," matches across ").concat(s.scannedCount," keys."))}),onLookupKey:t=>M(async()=>{let r=await e.onLookup(t);C(String(r.prefix)),e.onSelect(r),d("details")}),onScanRange:(t,r)=>M(async()=>{if(!e.onScanRange)return;let s=await e.onScanRange(t,r);y(s),j(!0),w(null),e.onNotice("Retrieved ".concat(s.length," records in range."))}),busy:p}),b&&N&&(0,s.jsx)(O,{query:N.query,count:N.count,scannedCount:N.scannedCount,hasMore:N.hasMore,prefix:N.prefix,prefixLabel:null==(n=k.find(e=>e.prefix===N.prefix))?void 0:n.prefixLabel,loadingMore:p,onContinueScan:()=>M(async()=>{if(!e.onSearch||!(null==N?void 0:N.nextKey))return;let t=await e.onSearch(N.query,{prefix:N.prefix,start:N.nextKey});y(e=>[...e||[],...t.records]),w({...t,query:N.query,count:(N.count||0)+t.count,scannedCount:(N.scannedCount||0)+t.scannedCount}),e.onNotice("Scanned +".concat(t.scannedCount," keys. Total: ").concat(N.count+t.count))}),onClear:()=>{j(!1),y(null),w(null)}}),e.error&&(0,s.jsxs)("div",{role:"alert",className:"flex items-center justify-between rounded-lg border border-destructive/30 bg-destructive/5 p-3 text-sm",children:[(0,s.jsx)("span",{children:e.error}),(0,s.jsx)(_,{variant:"outline",size:"sm",onClick:e.onRetry,children:"Retry"})]}),(0,s.jsx)(J,{records:L,activeStatus:f,onSelectStatus:h,viewMode:x,onViewModeChange:m,canWrite:e.canWrite,onNew:e.onNew}),(0,s.jsx)(eH,{viewMode:x,records:z,selected:e.selected,loading:e.loading,loadingMore:e.loadingMore,hasMore:e.hasMore,partitionTitle:b?"Search Results":P,canWrite:e.canWrite,pane:i,partitions:k,partition:S,totalCount:A,partitionCounts:e.partitionCounts,onSelect:e.onSelect,onSetPane:d,onLoadMore:e.onLoadMore,onNew:e.onNew,onChoosePartition:t=>{var r;C(t),h("all"),j(!1),y(null),e.onSelect(null),d("documents"),null==(r=e.onLoadPartition)||r.call(e,Number(t))},onDelete:e.onDelete,onDeleteUser:e.onDeleteUser,onEdit:e.onEdit?()=>e.onEdit(e.selected):void 0,onNotice:e.onNotice,onScanRange:e.onScanRange})]})}let e$=a.forwardRef((e,t)=>{let{className:r,...a}=e;return(0,s.jsx)("div",{ref:t,className:S("rounded-xl border border-border bg-card text-card-foreground",r),...a})});e$.displayName="Card";let eG=a.forwardRef((e,t)=>{let{className:r,...a}=e;return(0,s.jsx)("div",{ref:t,className:S("flex flex-col space-y-1.5 p-5",r),...a})});eG.displayName="CardHeader";let eY=a.forwardRef((e,t)=>{let{className:r,...a}=e;return(0,s.jsx)("h3",{ref:t,className:S("font-semibold leading-none tracking-tight",r),...a})});eY.displayName="CardTitle";let eZ=a.forwardRef((e,t)=>{let{className:r,...a}=e;return(0,s.jsx)("p",{ref:t,className:S("text-xs text-muted-foreground",r),...a})});eZ.displayName="CardDescription";let eQ=a.forwardRef((e,t)=>{let{className:r,...a}=e;return(0,s.jsx)("div",{ref:t,className:S("p-5 pt-0",r),...a})});function eX(e){let{className:t,variant:r="default",...a}=e;return(0,s.jsx)("div",{className:S("inline-flex items-center rounded-md border px-2 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",{"border-transparent bg-primary text-primary-foreground":"default"===r,"border-primary/40 bg-primary/10 text-primary font-mono text-[10px] tracking-wide":"lime"===r,"border-transparent bg-secondary text-secondary-foreground":"secondary"===r,"border-border text-foreground":"outline"===r,"border-destructive/30 bg-destructive/15 text-destructive":"destructive"===r,"border-border bg-muted/80 text-muted-foreground font-mono text-[10px]":"mono"===r},t),...a})}eQ.displayName="CardContent";let e0={pinned:"bg-info",dirty:"bg-primary",clean:"bg-foreground/25",free:"bg-border/60"};function e1(e){var t,r,a,n;let{storage:l}=e,o=null!=(t=null==l?void 0:l.poolSize)?t:0,i=Math.min(null!=(r=null==l?void 0:l.pinnedPages)?r:0,o),d=Math.min(null!=(a=null==l?void 0:l.dirtyPages)?a:0,o-i),c=Math.min(null!=(n=null==l?void 0:l.cachedPages)?n:0,o),u=Math.max(c-i-d,0),x=[...Array(i).fill("pinned"),...Array(d).fill("dirty"),...Array(u).fill("clean")];for(;x.length"free"!==e).length]];return(0,s.jsxs)("div",{children:[(0,s.jsxs)("div",{className:"flex items-baseline justify-between",children:[(0,s.jsx)("h2",{className:"text-sm font-semibold",children:"Buffer pool"}),(0,s.jsxs)("span",{className:"font-mono text-xs text-muted-foreground",children:[c,"/",o||"—"," frames \xb7 4 KiB"]})]}),(0,s.jsx)("div",{className:"mt-4 grid gap-[3px]",style:{gridTemplateColumns:"repeat(auto-fill, minmax(14px, 1fr))"},role:"img","aria-label":"".concat(d," dirty, ").concat(i," pinned, ").concat(u," clean cached, of ").concat(o," frames"),children:x.map((e,t)=>(0,s.jsx)("span",{className:S("aspect-square rounded-[3px]",e0[e])},t))}),(0,s.jsx)("ul",{className:"mt-4 flex flex-wrap gap-x-5 gap-y-1 font-mono text-xs text-muted-foreground",children:m.map(e=>{let[t,r,a]=e;return(0,s.jsxs)("li",{className:"flex items-center gap-1.5",children:[(0,s.jsx)("span",{className:S("h-2.5 w-2.5 rounded-[3px]",e0[t])}),r," ",(0,s.jsx)("span",{className:"text-foreground",children:a})]},t)})})]})}function e2(e){var t,r;let{lsn:n,storage:l}=e,o=(0,a.useRef)(void 0),[i,d]=(0,a.useState)(!1);return(0,a.useEffect)(()=>{if(void 0!==n&&void 0!==o.current&&n>o.current){d(!0);let e=window.setTimeout(()=>d(!1),900);return o.current=n,()=>clearTimeout(e)}o.current=n},[n]),(0,s.jsxs)("div",{className:"flex h-full flex-col",children:[(0,s.jsx)("h2",{className:"text-sm font-semibold",children:"Write-ahead log"}),(0,s.jsx)("p",{className:"mt-4 text-xs text-muted-foreground",children:"Log sequence number"}),(0,s.jsx)("p",{className:S("mt-1 font-mono text-4xl font-bold tracking-tight transition-colors duration-700",i?"text-primary":"text-foreground"),children:null!=(t=null==n?void 0:n.toLocaleString())?t:"—"}),(0,s.jsx)("div",{className:"mt-4 h-1.5 overflow-hidden rounded-full bg-border/60","aria-hidden":"true",children:(0,s.jsx)("div",{className:S("h-full rounded-full bg-primary transition-all duration-700",i?"w-full":"w-1/3")})}),(0,s.jsxs)("dl",{className:"mt-auto grid grid-cols-2 gap-3 pt-5 font-mono text-xs",children:[(0,s.jsxs)("div",{children:[(0,s.jsx)("dt",{className:"text-muted-foreground",children:"WAL size"}),(0,s.jsx)("dd",{className:"mt-0.5 text-sm text-foreground",children:l?eM(l.walSizeBytes):"—"})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)("dt",{className:"text-muted-foreground",children:"Active page"}),(0,s.jsxs)("dd",{className:"mt-0.5 text-sm text-foreground",children:["#",null!=(r=null==l?void 0:l.activeDataPage)?r:"—"]})]})]})]})}let e5={emerald:"#10b981",teal:"#14b8a6",purple:"#a855f7",cyan:"#06b6d4",amber:"#f59e0b",indigo:"#6366f1",rose:"#f43f5e",blue:"#3b82f6",violet:"#8b5cf6",sky:"#0ea5e9",fuchsia:"#d946ef",orange:"#f97316",lime:"#A8F21A",slate:"#71717a"};function e3(e){let{data:t}=e,r=t.reduce((e,t)=>e+t.count,0);if(0===r)return null;let a=0;return(0,s.jsxs)("div",{className:"flex flex-wrap items-center gap-8",children:[(0,s.jsx)("div",{className:"relative shrink-0",children:(0,s.jsxs)("svg",{width:160,height:160,"aria-hidden":"true",children:[t.map((e,t)=>{var n;let l=e.count/r*360,o=a,i=a+l-.5;a+=l;let d=null!=(n=e5[e.color])?n:e5.slate;return(0,s.jsx)("path",{d:function(e,t,r,s,a){let n=e=>e*Math.PI/180,l=80+70*Math.cos(n(s-90)),o=80+70*Math.sin(n(s-90)),i=80+r*Math.cos(n(a-90)),d=80+r*Math.sin(n(a-90));return"M ".concat(e," ").concat(t," L ").concat(l," ").concat(o," A ").concat(r," ").concat(r," 0 ").concat(+(a-s>180)," 1 ").concat(i," ").concat(d," Z")}(80,80,70,o,Math.max(i,o+.1)),fill:d},t)}),(0,s.jsx)("circle",{cx:80,cy:80,r:42,className:"fill-card"}),(0,s.jsx)("text",{x:80,y:76,textAnchor:"middle",className:"fill-foreground font-mono text-base font-bold",fontSize:16,fontWeight:700,children:r.toLocaleString()}),(0,s.jsx)("text",{x:80,y:93,textAnchor:"middle",fontSize:10,className:"fill-muted-foreground font-mono uppercase tracking-wider",children:"records"})]})}),(0,s.jsx)("div",{className:"grid flex-1 grid-cols-1 gap-2 sm:grid-cols-2",children:t.map((e,t)=>{var a;let n=(e.count/r*100).toFixed(1),l=null!=(a=e5[e.color])?a:e5.slate;return(0,s.jsxs)("div",{className:"flex items-center justify-between gap-3 rounded-lg border border-border bg-card px-3 py-2 text-xs",children:[(0,s.jsxs)("div",{className:"flex min-w-0 items-center gap-2",children:[(0,s.jsx)("span",{className:"h-2.5 w-2.5 shrink-0 rounded-full",style:{background:l}}),(0,s.jsx)("span",{className:"truncate text-foreground font-medium",children:e.label})]}),(0,s.jsxs)("div",{className:"flex items-center gap-2",children:[(0,s.jsx)("span",{className:"font-mono text-muted-foreground tabular-nums",children:e.count.toLocaleString()}),(0,s.jsxs)("span",{className:"rounded bg-muted px-1.5 py-0.5 font-mono text-[10px] text-muted-foreground",children:[n,"%"]})]})]},t)})})]})}function e4(e){var t,r,a,n,l;let{stats:o,statsError:i,recordsError:d,user:c,loaded:u,hasMore:x,updatedAt:m,onRetry:f,onAnalyze:h}=e,p=((null==o?void 0:o.partitionCounts)?Object.entries(o.partitionCounts).map(e=>{let[t,r]=e,s=Number(t),a=el().find(e=>e.prefix===s);return{prefix:s,count:r,label:(null==a?void 0:a.label)||"Partition 0x".concat(s.toString(16).padStart(2,"0").toUpperCase()),color:(null==a?void 0:a.color)||"emerald"}}):[]).filter(e=>e.count>0),g=!!o,b=null!=(n=null==o||null==(t=o.storage)?void 0:t.dirtyPages)?n:0,j=null!=(l=null==o||null==(r=o.storage)?void 0:r.poolSize)?l:64;null==o||null==(a=o.storage)||a.cachedPages;let v=b>.8*j;return(0,s.jsxs)("section",{"aria-labelledby":"overview-title",className:"space-y-6",children:[(0,s.jsxs)("div",{className:"flex flex-wrap items-start justify-between gap-4",children:[(0,s.jsxs)("div",{children:[(0,s.jsx)("h1",{id:"overview-title",className:"text-[22px] font-bold leading-tight tracking-tight text-foreground",children:"Overview"}),(0,s.jsx)("p",{className:"mt-1 text-sm text-muted-foreground",children:g?"Engine telemetry and measured vitals.":"Telemetry is unavailable right now."})]}),(0,s.jsxs)("div",{className:"flex items-center gap-2",children:[(0,s.jsxs)("span",{className:"inline-flex h-9 items-center gap-2 rounded-lg border border-border bg-card px-3 text-sm",children:[(0,s.jsx)("span",{className:"h-2 w-2 rounded-full ".concat(g?v?"bg-warning":"bg-primary":"bg-destructive")}),g?v?"High Dirty Load":"Optimal (Verified)":"Offline"]}),(0,s.jsxs)(_,{variant:"outline",size:"sm",onClick:h,className:"h-9 gap-2 text-sm",children:[(0,s.jsx)(T.A,{size:15})," Key Analyzer"]})]})]}),(i||d)&&(0,s.jsxs)("div",{role:"alert",className:"flex items-center justify-between rounded-lg border border-destructive/40 bg-destructive/10 p-3 text-xs text-destructive",children:[(0,s.jsx)("span",{children:i||d}),(0,s.jsx)(_,{variant:"outline",size:"sm",onClick:f,className:"h-7 text-xs",children:"Retry"})]}),(0,s.jsxs)("div",{className:"grid gap-4 lg:grid-cols-3",children:[(0,s.jsx)(e$,{className:"p-6 lg:col-span-2",children:(0,s.jsx)(e1,{storage:null==o?void 0:o.storage})}),(0,s.jsx)(e$,{className:"p-6",children:(0,s.jsx)(e2,{lsn:null==o?void 0:o.lsn,storage:null==o?void 0:o.storage})})]}),(0,s.jsxs)("div",{className:"grid gap-4 sm:grid-cols-3",children:[(0,s.jsx)(e6,{label:"Disk",value:(null==o?void 0:o.storage)?eM(o.storage.diskSizeBytes):"—"}),(0,s.jsx)(e6,{label:"Node role",value:((null==o?void 0:o.role)||"standalone").toUpperCase()}),(0,s.jsx)(e6,{label:"Version",value:"v".concat((null==o?void 0:o.version)||"1.0.0")})]}),p.length>0&&(0,s.jsxs)(e$,{children:[(0,s.jsx)(eG,{children:(0,s.jsxs)("div",{className:"flex items-center justify-between",children:[(0,s.jsx)(eY,{className:"text-sm font-semibold",children:"Keyspace Distribution by Partition"}),(0,s.jsxs)(eX,{variant:"mono",children:[p.length," Partitions Registered"]})]})}),(0,s.jsx)(eQ,{children:(0,s.jsx)(e3,{data:p})})]}),(0,s.jsxs)("div",{className:"grid gap-4 md:grid-cols-2",children:[(0,s.jsxs)(e$,{children:[(0,s.jsx)(eG,{children:(0,s.jsx)(eY,{className:"text-sm font-semibold",children:"Engine Runtime Parameters"})}),(0,s.jsxs)(eQ,{className:"divide-y divide-border text-xs",children:[(0,s.jsxs)("div",{className:"flex justify-between py-2.5",children:[(0,s.jsx)("span",{className:"text-muted-foreground",children:"Storage Hierarchy"}),(0,s.jsx)("span",{className:"font-mono text-foreground",children:"4KB Slotted Pages / Slotted Array"})]}),(0,s.jsxs)("div",{className:"flex justify-between py-2.5",children:[(0,s.jsx)("span",{className:"text-muted-foreground",children:"Durability Model"}),(0,s.jsx)("span",{className:"font-mono text-foreground",children:"ARIES WAL + Append-Only tombstones"})]}),(0,s.jsxs)("div",{className:"flex justify-between py-2.5",children:[(0,s.jsx)("span",{className:"text-muted-foreground",children:"Index Structure"}),(0,s.jsx)("span",{className:"font-mono text-foreground",children:"On-Disk B+ Tree (Fanout ~200)"})]}),(0,s.jsxs)("div",{className:"flex justify-between py-2.5",children:[(0,s.jsx)("span",{className:"text-muted-foreground",children:"Last Telemetry Sync"}),(0,s.jsx)("span",{className:"font-mono text-muted-foreground",children:m?m.toLocaleTimeString():"—"})]})]})]}),(0,s.jsxs)(e$,{children:[(0,s.jsx)(eG,{children:(0,s.jsx)(eY,{className:"text-sm font-semibold",children:"Current Session Privileges"})}),(0,s.jsxs)(eQ,{className:"divide-y divide-border text-xs",children:[(0,s.jsxs)("div",{className:"flex justify-between py-2.5",children:[(0,s.jsx)("span",{className:"text-muted-foreground",children:"Authenticated Subject"}),(0,s.jsx)("span",{className:"font-mono font-medium text-foreground",children:(null==c?void 0:c.username)||"Anonymous"})]}),(0,s.jsxs)("div",{className:"flex justify-between py-2.5",children:[(0,s.jsx)("span",{className:"text-muted-foreground",children:"Authorization Scope"}),(0,s.jsx)(eX,{variant:(null==c?void 0:c.role)==="admin"?"lime":"mono",children:(null==c?void 0:c.role)||"viewer"})]}),(0,s.jsxs)("div",{className:"flex justify-between py-2.5",children:[(0,s.jsx)("span",{className:"text-muted-foreground",children:"Write Operations"}),(0,s.jsx)("span",{className:(null==c?void 0:c.role)==="admin"?"text-primary font-medium":"text-muted-foreground",children:(null==c?void 0:c.role)==="admin"?"Enabled (Full I/O)":"Disabled (Read-Only)"})]}),(0,s.jsxs)("div",{className:"flex justify-between py-2.5",children:[(0,s.jsx)("span",{className:"text-muted-foreground",children:"Session Mode"}),(0,s.jsx)("span",{className:"font-mono text-muted-foreground",children:"HMAC-Signed Cookie"})]})]})]})]})]})}function e6(e){let{label:t,value:r}=e;return(0,s.jsxs)(e$,{className:"flex items-baseline justify-between gap-3 px-5 py-4",children:[(0,s.jsx)("span",{className:"shrink-0 text-xs text-muted-foreground",children:t}),(0,s.jsx)("span",{className:"min-w-0 truncate font-mono text-lg font-semibold text-foreground",title:r,children:r})]})}var e8=r(2111),e9=r(8770),e7=r(9915);function te(e){let{id:t,title:r,subtitle:a,actions:n}=e;return(0,s.jsxs)("div",{className:"flex flex-wrap items-start justify-between gap-4",children:[(0,s.jsxs)("div",{children:[(0,s.jsx)("h1",{id:t,className:"text-[22px] font-bold leading-tight tracking-tight text-foreground",children:r}),a&&(0,s.jsx)("p",{className:"mt-1 text-sm text-muted-foreground",children:a})]}),n&&(0,s.jsx)("div",{className:"flex items-center gap-2",children:n})]})}function tt(e){let{outcome:t,onContinue:r}=e,{records:a,ms:n,scanned:l,indexed:o,hasMore:i,nextKey:d}=t;return(0,s.jsxs)("div",{className:"overflow-hidden rounded-xl border border-border bg-card",children:[(0,s.jsxs)("div",{className:"flex flex-wrap items-center gap-x-4 gap-y-1 border-b border-border px-4 py-2.5 text-xs text-muted-foreground",children:[(0,s.jsxs)("span",{children:[(0,s.jsx)("span",{className:"font-mono text-foreground",children:a.length})," ",1===a.length?"result":"results"]}),(0,s.jsxs)("span",{children:[(0,s.jsx)("span",{className:"font-mono text-foreground",children:n.toFixed(1)})," ms"]}),void 0!==l&&(0,s.jsxs)("span",{children:["scanned ",(0,s.jsx)("span",{className:"font-mono text-foreground",children:l})]}),o&&(0,s.jsx)(eX,{variant:"mono",children:"secondary index"}),i&&d&&r&&(0,s.jsxs)("button",{type:"button",onClick:()=>r(d),className:"ml-auto font-mono text-primary hover:underline",children:["Continue from ",d," →"]})]}),0===a.length?(0,s.jsxs)("p",{className:"px-4 py-10 text-center text-sm text-muted-foreground",children:["No records matched.",i?" More of the key space remains to be scanned.":""]}):(0,s.jsx)("div",{className:"overflow-x-auto",children:(0,s.jsxs)("table",{className:"w-full text-left text-sm",children:[(0,s.jsx)("thead",{children:(0,s.jsxs)("tr",{className:"border-b border-border text-[11px] uppercase tracking-wide text-muted-foreground",children:[(0,s.jsx)("th",{className:"px-4 py-2 font-medium",children:"Key"}),(0,s.jsx)("th",{className:"px-4 py-2 font-medium",children:"Partition"}),(0,s.jsx)("th",{className:"px-4 py-2 text-right font-medium",children:"Size"}),(0,s.jsx)("th",{className:"px-4 py-2 font-medium",children:"Preview"})]})}),(0,s.jsx)("tbody",{children:a.map(e=>{let t=eo(e.keyStr);return(0,s.jsxs)("tr",{className:"border-b border-border/60 last:border-0 hover:bg-muted/40",children:[(0,s.jsxs)("td",{className:"px-4 py-2.5",children:[(0,s.jsx)("div",{className:"font-mono text-xs text-foreground",children:e.keyStr}),(0,s.jsx)(ep,{keyStr:e.keyStr,className:"mt-1"})]}),(0,s.jsxs)("td",{className:"whitespace-nowrap px-4 py-2.5",children:[(0,s.jsx)("span",{className:"text-foreground",children:t.prefixLabel})," ",(0,s.jsx)("span",{className:"font-mono text-xs text-muted-foreground",children:t.prefixHex})]}),(0,s.jsx)("td",{className:"whitespace-nowrap px-4 py-2.5 text-right font-mono text-xs",children:eM(e.byteSize)}),(0,s.jsx)("td",{className:"max-w-md px-4 py-2.5",children:(0,s.jsx)("div",{className:"truncate font-mono text-xs text-muted-foreground",title:e.raw,children:function(e){let t=e.replace(/\s+/g," ").trim();return t.length>120?"".concat(t.slice(0,120),"…"):t||"(empty)"}(e.raw)})})]},e.keyStr)})})]})})]})}let tr={mode:"get",key:"",start:"",end:"",limit:"50",q:"",prefix:"",maxScan:"2500"},ts=(1n<<64n)-1n;function ta(e,t){let r=t.trim();if(!/^(0x[0-9a-f]+|\d+)$/i.test(r))throw Error("".concat(e," must be an unsigned 64-bit integer (decimal or 0x hex)."));let s=BigInt(r);if(s>ts)throw Error("".concat(e," exceeds the 64-bit key space."));return s.toString()}function tn(e,t,r){let s=Number(t.trim());if(!Number.isInteger(s)||s<1)throw Error("".concat(e," must be a positive integer."));return Math.min(s,r)}function tl(e){return"get"===e.mode?"GET ".concat(e.key.trim()):"search"===e.mode?'SEARCH "'.concat(e.q.trim(),'"').concat(e.prefix.trim()?" @".concat(e.prefix.trim()):""):"SCAN ".concat(e.start.trim()||"min"," .. ").concat(e.end.trim()||"max")}async function to(e){let t=performance.now(),r=e=>({...e,ms:performance.now()-t});if("get"===e.mode){var s;if(!e.key.trim())throw Error("Enter a key to look up.");let t=ta("Key",e.key),a=await eK("/api/key?k=".concat(t));return r({records:[eJ({keyText:null!=(s=a.keyText)?s:t,value:a.value})],hasMore:!1})}if("scan"===e.mode){let t=new URLSearchParams({limit:String(tn("Limit",e.limit,500))});e.start.trim()&&t.set("start",ta("Start",e.start)),e.end.trim()&&t.set("end",ta("End",e.end));let s=await eK("/api/records?".concat(t));return r({records:s.records.map(eJ),hasMore:s.hasMore,nextKey:s.hasMore?s.nextKeyText:void 0})}let a=e.q.trim();if(a.length<2)throw Error("Search text must be at least 2 characters.");let n=new URLSearchParams({q:a,limit:"200",maxScan:String(tn("Max scan",e.maxScan,1e4))});if(e.prefix.trim()){let t=Number(ta("Partition",e.prefix));if(t>255)throw Error("Partition prefix must be between 0 and 255.");n.set("prefix",String(t))}e.start.trim()&&n.set("start",ta("Start",e.start));let l=await eK("/api/search?".concat(n));return r({records:l.records.map(eJ),hasMore:l.hasMore,nextKey:l.hasMore?l.nextKey:void 0,scanned:l.scannedCount,indexed:l.indexed})}let ti=[{id:"get",label:"Get key",hint:"Exact lookup in the primary B+ tree by 64-bit key."},{id:"scan",label:"Scan range",hint:"Ordered range scan over the primary index; leave bounds empty for the full key space."},{id:"search",label:"Search text",hint:"Match value text. Uses the secondary index when it can, otherwise scans up to Max scan records."}],td="font-mono text-[13px]",tc="rounded border border-border bg-muted px-1.5 py-0.5 font-mono text-[10px]";function tu(e){let{onNotice:t}=e,[r,n]=(0,a.useState)(tr),[l,o]=(0,a.useState)(null),[i,d]=(0,a.useState)(null),[c,u]=(0,a.useState)(!1),[x,m]=(0,a.useState)([]),f=(0,a.useRef)(0),h=e=>n(t=>({...t,...e})),p=(0,a.useCallback)(async e=>{let r=++f.current;u(!0),d(null);try{let t=await to(e);if(r!==f.current)return;o(t),m(t=>[e,...t.filter(t=>tl(t)!==tl(e))].slice(0,10))}catch(e){if(r!==f.current)return;o(null),d(e.message),t(e.message,!0)}finally{r===f.current&&u(!1)}},[t]),g=ti.find(e=>e.id===r.mode);return(0,s.jsxs)("section",{"aria-labelledby":"query-title",className:"space-y-6",children:[(0,s.jsx)(te,{id:"query-title",title:"Query",subtitle:"Read records through the engine's existing lookup, range and text-search paths."}),(0,s.jsxs)(e$,{className:"p-5",onKeyDown:e=>{(e.metaKey||e.ctrlKey)&&"Enter"===e.key&&(e.preventDefault(),p(r))},children:[(0,s.jsx)("div",{role:"tablist","aria-label":"Query mode",className:"mb-4 inline-flex rounded-lg border border-border bg-muted/40 p-0.5",children:ti.map(e=>(0,s.jsx)("button",{role:"tab",type:"button","aria-selected":r.mode===e.id,onClick:()=>h({mode:e.id}),className:"rounded-md px-3 py-1.5 text-xs font-medium transition-colors ".concat(r.mode===e.id?"bg-card text-foreground shadow-sm":"text-muted-foreground hover:text-foreground"),children:e.label},e.id))}),(0,s.jsx)("p",{className:"mb-3 text-xs text-muted-foreground",children:g.hint}),"get"===r.mode&&(0,s.jsx)(D,{"aria-label":"Key",className:td,placeholder:"Key, e.g. 360287970189639680 or 0x0500000000000001",value:r.key,onChange:e=>h({key:e.target.value})}),"scan"===r.mode&&(0,s.jsxs)("div",{className:"grid gap-3 sm:grid-cols-[1fr_1fr_120px]",children:[(0,s.jsx)(D,{"aria-label":"Start key",className:td,placeholder:"Start key (inclusive)",value:r.start,onChange:e=>h({start:e.target.value})}),(0,s.jsx)(D,{"aria-label":"End key",className:td,placeholder:"End key (inclusive)",value:r.end,onChange:e=>h({end:e.target.value})}),(0,s.jsx)(D,{"aria-label":"Limit",type:"number",min:1,max:500,className:td,placeholder:"Limit",value:r.limit,onChange:e=>h({limit:e.target.value})})]}),"search"===r.mode&&(0,s.jsxs)("div",{className:"grid gap-3 sm:grid-cols-[1fr_140px_120px]",children:[(0,s.jsx)(D,{"aria-label":"Search text",className:td,placeholder:"Text to find in values (min 2 chars)",value:r.q,onChange:e=>h({q:e.target.value})}),(0,s.jsx)(D,{"aria-label":"Partition prefix",className:td,placeholder:"Prefix 0-255 (opt.)",value:r.prefix,onChange:e=>h({prefix:e.target.value})}),(0,s.jsx)(D,{"aria-label":"Max scan",type:"number",min:1,max:1e4,className:td,placeholder:"Max scan",value:r.maxScan,onChange:e=>h({maxScan:e.target.value})})]}),(0,s.jsxs)("div",{className:"mt-4 flex items-center justify-between gap-3",children:[(0,s.jsxs)("span",{className:"text-xs text-muted-foreground",children:[(0,s.jsx)("kbd",{className:tc,children:"Cmd/Ctrl"})," + ",(0,s.jsx)("kbd",{className:tc,children:"Enter"})," to run"]}),(0,s.jsxs)(_,{onClick:()=>void p(r),disabled:c,children:[(0,s.jsx)(e8.A,{className:"mr-1.5 h-3.5 w-3.5","aria-hidden":!0}),c?"Running…":"Run"]})]})]}),i&&(0,s.jsxs)("div",{role:"alert",className:"flex items-start gap-2.5 rounded-xl border border-destructive/30 bg-destructive/10 px-4 py-3 text-sm text-destructive",children:[(0,s.jsx)(e9.A,{className:"mt-0.5 h-4 w-4 shrink-0","aria-hidden":!0}),(0,s.jsx)("span",{children:i})]}),l&&(0,s.jsx)(tt,{outcome:l,onContinue:e=>{let t={...r,start:e};n(t),p(t)}}),!l&&!i&&!c&&(0,s.jsx)("p",{className:"rounded-xl border border-dashed border-border px-4 py-10 text-center text-sm text-muted-foreground",children:"Run a query to see records here."}),x.length>0&&(0,s.jsxs)(e$,{className:"p-4",children:[(0,s.jsxs)("h2",{className:"mb-2 flex items-center gap-1.5 text-xs font-medium uppercase tracking-wide text-muted-foreground",children:[(0,s.jsx)(e7.A,{className:"h-3.5 w-3.5","aria-hidden":!0}),"Recent queries"]}),(0,s.jsx)("ul",{className:"space-y-0.5",children:x.map(e=>(0,s.jsx)("li",{children:(0,s.jsx)("button",{type:"button",onClick:()=>{n(e),p(e)},className:"w-full truncate rounded-md px-2 py-1.5 text-left font-mono text-xs text-muted-foreground hover:bg-muted hover:text-foreground",children:tl(e)})},tl(e)))})]})]})}function tx(e){let{rows:t}=e,r=t.reduce((e,t)=>e+t.count,0);return(0,s.jsxs)(e$,{className:"overflow-hidden",children:[(0,s.jsxs)("div",{className:"border-b border-border px-5 py-3.5",children:[(0,s.jsx)("h2",{className:"text-sm font-semibold text-foreground",children:"Key-space partitions"}),(0,s.jsx)("p",{className:"mt-0.5 text-xs text-muted-foreground",children:"The top 8 bits of each key select the partition; all partitions share one primary B+ tree."})]}),0===t.length?(0,s.jsx)("p",{className:"px-5 py-8 text-center text-sm text-muted-foreground",children:"No partition data yet."}):(0,s.jsxs)("table",{className:"w-full text-left text-sm",children:[(0,s.jsx)("thead",{children:(0,s.jsxs)("tr",{className:"border-b border-border text-[11px] uppercase tracking-wide text-muted-foreground",children:[(0,s.jsx)("th",{className:"px-5 py-2 font-medium",children:"Prefix"}),(0,s.jsx)("th",{className:"px-5 py-2 font-medium",children:"Label"}),(0,s.jsx)("th",{className:"px-5 py-2 text-right font-medium",children:"Records"}),(0,s.jsx)("th",{className:"w-[35%] px-5 py-2 font-medium",children:"Share"})]})}),(0,s.jsx)("tbody",{children:t.map(e=>{let t=r>0?e.count/r*100:0;return(0,s.jsxs)("tr",{className:"border-b border-border/60 last:border-0",children:[(0,s.jsxs)("td",{className:"px-5 py-2.5 font-mono text-xs",children:["0x",e.prefix.toString(16).padStart(2,"0").toUpperCase()]}),(0,s.jsx)("td",{className:"px-5 py-2.5 text-foreground",children:e.label}),(0,s.jsx)("td",{className:"px-5 py-2.5 text-right font-mono text-xs",children:e.count.toLocaleString()}),(0,s.jsx)("td",{className:"px-5 py-2.5",children:(0,s.jsxs)("div",{className:"flex items-center gap-2",children:[(0,s.jsx)("div",{className:"h-1.5 flex-1 overflow-hidden rounded-full bg-muted",children:(0,s.jsx)("div",{className:"h-full rounded-full bg-foreground/50",style:{width:"".concat(t,"%")}})}),(0,s.jsxs)("span",{className:"w-12 text-right font-mono text-xs text-muted-foreground",children:[t.toFixed(1),"%"]})]})})]},e.prefix)})})]})]})}function tm(e){let{label:t,value:r}=e;return(0,s.jsxs)("div",{className:"flex items-baseline justify-between gap-4 border-b border-border/60 py-2 last:border-0",children:[(0,s.jsx)("dt",{className:"text-xs text-muted-foreground",children:t}),(0,s.jsx)("dd",{className:"text-right font-mono text-xs text-foreground",children:r})]})}let tf=e=>void 0===e?"—":e.toLocaleString();function th(e){let{storage:t}=e,r=t?Math.floor(t.diskSizeBytes/4096):void 0;return(0,s.jsxs)(e$,{className:"p-5",children:[(0,s.jsxs)("div",{className:"mb-3 flex items-center justify-between",children:[(0,s.jsx)("h2",{className:"text-sm font-semibold text-foreground",children:"Primary index"}),(0,s.jsx)(eX,{variant:"mono",children:"B+ tree"})]}),(0,s.jsx)("p",{className:"mb-3 text-xs text-muted-foreground",children:"Disk-backed B+ tree over 64-bit keys. Leaves are chained by next-page pointers, which range scans follow."}),(0,s.jsxs)("dl",{children:[(0,s.jsx)(tm,{label:"Key width",value:"64-bit"}),(0,s.jsx)(tm,{label:"Page size",value:"4 KiB"}),(0,s.jsx)(tm,{label:"Leaf entry",value:"18 B (8 B key + 10 B RID)"}),(0,s.jsx)(tm,{label:"Max entries / leaf",value:String(200)}),(0,s.jsx)(tm,{label:"Max keys / internal node",value:"".concat(200," (fanout ").concat(201,")")}),(0,s.jsx)(tm,{label:"Pages in data file (≈ size / 4 KiB)",value:tf(r)}),(0,s.jsx)(tm,{label:"Data file size",value:t?eM(t.diskSizeBytes):"—"})]})]})}function tp(e){let{storage:t}=e,r=t&&t.poolSize>0?Math.min(100,t.cachedPages/t.poolSize*100):0;return(0,s.jsxs)(e$,{className:"p-5",children:[(0,s.jsx)("h2",{className:"mb-3 text-sm font-semibold text-foreground",children:"Buffer pool"}),(0,s.jsx)("div",{className:"mb-3 h-1.5 overflow-hidden rounded-full bg-muted","aria-hidden":!0,children:(0,s.jsx)("div",{className:"h-full rounded-full bg-primary",style:{width:"".concat(r,"%")}})}),(0,s.jsxs)("dl",{children:[(0,s.jsx)(tm,{label:"Pool size (pages)",value:tf(null==t?void 0:t.poolSize)}),(0,s.jsx)(tm,{label:"Cached pages",value:tf(null==t?void 0:t.cachedPages)}),(0,s.jsx)(tm,{label:"Dirty pages",value:tf(null==t?void 0:t.dirtyPages)}),(0,s.jsx)(tm,{label:"Pinned pages",value:tf(null==t?void 0:t.pinnedPages)}),(0,s.jsx)(tm,{label:"Active data page",value:tf(null==t?void 0:t.activeDataPage)})]})]})}function tg(){return(0,s.jsxs)(e$,{className:"p-5",children:[(0,s.jsxs)("div",{className:"mb-3 flex items-center justify-between",children:[(0,s.jsx)("h2",{className:"text-sm font-semibold text-foreground",children:"Secondary text index"}),(0,s.jsx)(eX,{variant:"mono",children:"inverted \xb7 in-memory"})]}),(0,s.jsx)("p",{className:"mb-3 text-xs text-muted-foreground",children:"Maps each token to a sorted list of primary keys. It is rebuilt from stored records when the engine starts and kept current on every write and delete."}),(0,s.jsxs)("dl",{children:[(0,s.jsx)(tm,{label:"Tokenizer",value:"letters + digits, lowercased"}),(0,s.jsx)(tm,{label:"Min token length",value:"2"}),(0,s.jsx)(tm,{label:"Token identity",value:"64-bit FNV-1a hash"}),(0,s.jsx)(tm,{label:"Multi-word query",value:"intersection (AND)"})]}),(0,s.jsx)("p",{className:"mt-3 text-xs text-muted-foreground",children:"Index size and posting counts are not exposed by the API, so they are not shown."})]})}function tb(e){var t,r;let{stats:n,onRefresh:l}=e,[o,i]=(0,a.useState)(null),[d,c]=(0,a.useState)([]),[u,x]=(0,a.useState)(null),[m,f]=(0,a.useState)(!0),h=(0,a.useCallback)(async()=>{var e,t,r;f(!0),x(null);let[s,a]=await Promise.allSettled([eK("/api/stats?counts=true"),eK("/api/partitions")]);"fulfilled"===s.status?i(s.value):x(null!=(t=null==(e=s.reason)?void 0:e.message)?t:"Could not load partition counts."),"fulfilled"===a.status&&c(null!=(r=a.value)?r:[]),f(!1)},[]);(0,a.useEffect)(()=>{h()},[h]);let g=null!=(t=null==o?void 0:o.storage)?t:null==n?void 0:n.storage,b=null!=(r=null==o?void 0:o.partitionCounts)?r:null==n?void 0:n.partitionCounts,j=(0,a.useMemo)(()=>Object.entries(null!=b?b:{}).map(e=>{var t,r;let[s,a]=e,n=Number(s),l=null!=(r=null==(t=d.find(e=>e.prefix===n))?void 0:t.label)?r:en(n).label;return{prefix:n,label:l,count:a}}).filter(e=>e.count>0).sort((e,t)=>t.count-e.count),[b,d]);return(0,s.jsxs)("section",{"aria-labelledby":"indexes-title",className:"space-y-6",children:[(0,s.jsx)(te,{id:"indexes-title",title:"Indexes",subtitle:"How records are located: the primary B+ tree and the secondary text index.",actions:(0,s.jsxs)(_,{variant:"outline",size:"sm",onClick:()=>{l(),h()},disabled:m,children:[(0,s.jsx)(p.A,{className:"mr-1.5 h-3.5 w-3.5 ".concat(m?"animate-spin":""),"aria-hidden":!0}),"Refresh"]})}),u&&(0,s.jsx)("div",{role:"alert",className:"rounded-xl border border-destructive/30 bg-destructive/10 px-4 py-3 text-sm text-destructive",children:u}),(0,s.jsxs)("div",{className:"grid gap-4 lg:grid-cols-3",children:[(0,s.jsx)(th,{storage:g}),(0,s.jsx)(tp,{storage:g}),(0,s.jsx)(tg,{})]}),(0,s.jsx)(tx,{rows:j})]})}var tj=r(2251);function tv(e){let{label:t,command:r,onNotice:n}=e,[l,o]=(0,a.useState)(!1),i=async()=>{try{await navigator.clipboard.writeText(r),o(!0),window.setTimeout(()=>o(!1),1500)}catch(e){n("Could not access the clipboard. Select the command and copy it manually.",!0)}};return(0,s.jsxs)("div",{children:[(0,s.jsx)("p",{className:"mb-1.5 text-xs font-medium text-muted-foreground",children:t}),(0,s.jsxs)("div",{className:"flex items-start justify-between gap-2 rounded-md border border-border bg-background px-3 py-2.5",children:[(0,s.jsx)("code",{className:"min-w-0 flex-1 whitespace-pre-wrap break-all font-mono text-xs text-foreground",children:r}),(0,s.jsx)(_,{variant:"ghost",size:"icon",className:"h-7 w-7 shrink-0",onClick:()=>void i(),"aria-label":"Copy ".concat(t," command"),children:l?(0,s.jsx)(tj.A,{size:14,className:"text-success"}):(0,s.jsx)(eI.A,{size:14})})]})]})}function ty(e){let{label:t,value:r,tone:a}=e;return(0,s.jsxs)("div",{className:"rounded-xl border border-border bg-card p-4",children:[(0,s.jsx)("p",{className:"text-xs text-muted-foreground",children:t}),(0,s.jsx)("p",{className:"mt-1 font-mono text-xl font-semibold ".concat(null!=a?a:"text-foreground"),children:r})]})}function tN(e){var t,r,n,l,o;let{stats:i,user:d,onNotice:c}=e,[u,x]=(0,a.useState)(null),[m,f]=(0,a.useState)(null),h=(null==d?void 0:d.role)==="admin",p=(0,a.useCallback)(async()=>{if(h)try{let e=await fetch("/api/replication");if(!e.ok)throw Error("HTTP ".concat(e.status));x(await e.json()),f(null)}catch(e){f(e instanceof Error?e.message:"request failed")}},[h]);(0,a.useEffect)(()=>{if(p(),!h)return;let e=window.setInterval(()=>void p(),5e3);return()=>window.clearInterval(e)},[p,h]);let g=null!=(r=null!=(t=null==u?void 0:u.role)?t:null==i?void 0:i.role)?r:"standalone",b=null!=(l=null!=(n=null==u?void 0:u.lsn)?n:null==i?void 0:i.lsn)?l:0,j=null!=(o=null==u?void 0:u.replicas)?o:[],v=j.filter(e=>e.connected).length,y="leader"===g;return(0,s.jsxs)("section",{"aria-labelledby":"replication-title",className:"space-y-6",children:[(0,s.jsx)(te,{id:"replication-title",title:"Replication",subtitle:"Leader/follower WAL streaming. Only data the engine tracks is shown.",actions:h&&(0,s.jsx)(_,{variant:"outline",size:"sm",onClick:()=>void p(),children:"Refresh"})}),(0,s.jsxs)("div",{className:"grid gap-4 sm:grid-cols-3",children:[(0,s.jsx)(ty,{label:"This node",value:g,tone:y?"text-primary":void 0}),(0,s.jsx)(ty,{label:"Current LSN",value:b.toLocaleString()}),(0,s.jsx)(ty,{label:"Replicas connected",value:(null==u?void 0:u.tracked)?String(v):"n/a",tone:(null==u?void 0:u.tracked)&&v>0?"text-success":"text-muted-foreground"})]}),(0,s.jsxs)(e$,{children:[(0,s.jsxs)(eG,{className:"flex flex-row items-center justify-between",children:[(0,s.jsx)(eY,{className:"text-sm",children:"Replicas"}),(null==u?void 0:u.tracked)&&(0,s.jsxs)(eX,{variant:"mono",children:[j.length," seen since start"]})]}),(0,s.jsxs)(eQ,{children:[h?m?(0,s.jsxs)("p",{className:"text-sm text-destructive",children:["Could not load replication status: ",m]}):u?u.tracked?0===j.length?(0,s.jsx)("p",{className:"text-sm text-muted-foreground",children:"No replicas connected. A follower appears here once it opens a WAL stream to this leader."}):(0,s.jsx)("div",{className:"overflow-x-auto",children:(0,s.jsxs)("table",{className:"w-full text-left text-xs",children:[(0,s.jsx)("thead",{className:"text-muted-foreground",children:(0,s.jsxs)("tr",{className:"border-b border-border",children:[(0,s.jsx)("th",{className:"py-2 pr-4 font-medium",children:"Replica"}),(0,s.jsx)("th",{className:"py-2 pr-4 font-medium",children:"Address"}),(0,s.jsx)("th",{className:"py-2 pr-4 font-medium",children:"State"}),(0,s.jsx)("th",{className:"py-2 pr-4 text-right font-medium",children:"Acked LSN"}),(0,s.jsx)("th",{className:"py-2 text-right font-medium",children:"Lag (LSN)"})]})}),(0,s.jsx)("tbody",{className:"divide-y divide-border/60",children:j.map(e=>(0,s.jsxs)("tr",{children:[(0,s.jsx)("td",{className:"py-2.5 pr-4 font-mono text-foreground",children:e.id}),(0,s.jsx)("td",{className:"py-2.5 pr-4 font-mono text-muted-foreground",children:e.address||"—"}),(0,s.jsx)("td",{className:"py-2.5 pr-4",children:(0,s.jsx)(eX,{variant:e.connected?"lime":"outline",children:e.connected?"streaming":"disconnected"})}),(0,s.jsx)("td",{className:"py-2.5 pr-4 text-right font-mono",children:e.ackedLSN.toLocaleString()}),(0,s.jsx)("td",{className:"py-2.5 text-right font-mono ".concat(e.lagLSN>0?"text-warning":"text-foreground"),children:e.lagLSN.toLocaleString()})]},e.id))})]})}):(0,s.jsx)("p",{className:"text-sm text-muted-foreground",children:"Replicas are not tracked on this node. Only a leader sees its followers; open Studio on the leader to view them."}):(0,s.jsx)("p",{className:"text-sm text-muted-foreground",children:"Loading…"}):(0,s.jsx)("p",{className:"text-sm text-muted-foreground",children:"Replica details are visible to admin users only."}),(null==u?void 0:u.tracked)&&j.length>0&&(0,s.jsx)("p",{className:"mt-3 text-xs text-muted-foreground",children:"Lag is leader LSN minus the replica's last acknowledgement. Acks are recorded as followers apply records, and a disconnected replica keeps its last ack until the leader restarts."})]})]}),(0,s.jsxs)(e$,{children:[(0,s.jsx)(eG,{children:(0,s.jsx)(eY,{className:"text-sm",children:"Start a follower"})}),(0,s.jsxs)(eQ,{className:"space-y-3",children:[(0,s.jsx)(tv,{label:"Follower node (flags from cmd/server)",command:"go run ./cmd/server -role follower -leader-addr :50051 \\\n -replica-id replica-1 -data-dir ./data-follower -port 50052 -web-addr 127.0.0.1:8081",onNotice:c}),(0,s.jsxs)("p",{className:"text-xs text-muted-foreground",children:["A follower runs read-only and streams the leader's WAL, replaying its archived generation first. Use a unique ",(0,s.jsx)("span",{className:"font-mono",children:"-replica-id"})," and its own data directory. Automatic failover and promotion are not implemented."]})]})]})]})}let tw=["Stop the node (or back up a copy of its data directory). beastctl opens the data files directly and there is no file lock.","Run verify on the bundle before relying on it; restore also verifies first.","Restore into an empty directory. Without --force it refuses to overwrite an existing beast.bin.","Restore removes any old beast.wal at the destination and opens the result once to count records.","Point the server at the restored directory with -data-dir and confirm the record count before cutting over.","Keep at least one copy off the host. Retention and off-host copy are not managed by BeastDB."];function tk(e){let{label:t,value:r,tone:a}=e;return(0,s.jsxs)("div",{className:"flex items-center justify-between py-2.5 text-xs",children:[(0,s.jsx)("span",{className:"text-muted-foreground",children:t}),(0,s.jsx)("span",{className:"font-mono ".concat(null!=a?a:"text-foreground"),children:r})]})}function tS(e){var t,r,n,l,o,i,d,c,u;let{stats:x,user:m,onNotice:f,onRefresh:h}=e,[p,g]=(0,a.useState)(!1),b=null==x?void 0:x.storage,j=null!=(t=null==b?void 0:b.dirtyPages)?t:0,v=(null==m?void 0:m.role)==="admin",y=async()=>{g(!0);try{let e=await fetch("/api/checkpoint",{method:"POST"});if(!e.ok){let t=(await e.text()).trim();throw Error(t||"HTTP ".concat(e.status))}f("Checkpoint complete: dirty pages flushed and the WAL rotated."),h()}catch(e){f("Checkpoint failed: ".concat(e instanceof Error?e.message:"request failed"),!0)}finally{g(!1)}};return(0,s.jsxs)("section",{"aria-labelledby":"backups-title",className:"space-y-6",children:[(0,s.jsx)(te,{id:"backups-title",title:"Backups",subtitle:"Backups are produced offline with beastctl. Studio shows durability state and runs checkpoints."}),(0,s.jsxs)("div",{className:"grid gap-4 lg:grid-cols-2",children:[(0,s.jsxs)(e$,{children:[(0,s.jsxs)(eG,{className:"flex flex-row items-center justify-between",children:[(0,s.jsx)(eY,{className:"text-sm",children:"Durability"}),v&&(0,s.jsx)(_,{size:"sm",onClick:()=>void y(),disabled:p,children:p?"Checkpointing…":"Checkpoint now"})]}),(0,s.jsxs)(eQ,{className:"divide-y divide-border/60",children:[(0,s.jsx)(tk,{label:"Current LSN",value:(null!=(n=null!=(r=null==b?void 0:b.currentLSN)?r:null==x?void 0:x.lsn)?n:0).toLocaleString()}),(0,s.jsx)(tk,{label:"WAL size",value:eM(null!=(l=null==b?void 0:b.walSizeBytes)?l:0)}),(0,s.jsx)(tk,{label:"Dirty pages",value:"".concat(j," of ").concat(null!=(o=null==b?void 0:b.poolSize)?o:0),tone:j>0?"text-warning":void 0}),(0,s.jsx)(tk,{label:"Last checkpoint LSN",value:(null!=(i=null==b?void 0:b.lastCheckpointLSN)?i:0).toLocaleString()}),(0,s.jsx)(tk,{label:"Last checkpoint",value:(u=null==b?void 0:b.lastCheckpointUnix)?new Date(1e3*u).toLocaleString():"none since this process started"}),(0,s.jsx)(tk,{label:"Data file",value:"".concat(eM(null!=(d=null==b?void 0:b.diskSizeBytes)?d:0)," (").concat(null!=(c=null==b?void 0:b.totalPages)?c:0," pages)")})]}),(0,s.jsx)(eQ,{className:"pt-0",children:(0,s.jsxs)("p",{className:"text-xs text-muted-foreground",children:["A checkpoint flushes dirty pages, syncs the data file, then rotates the WAL. The server also checkpoints automatically when the WAL passes ",(0,s.jsx)("span",{className:"font-mono",children:"-wal-checkpoint-mb"}),".",!v&&" Only admin users can trigger one."]})})]}),(0,s.jsxs)(e$,{children:[(0,s.jsx)(eG,{children:(0,s.jsx)(eY,{className:"text-sm",children:"How to back up"})}),(0,s.jsxs)(eQ,{className:"space-y-3",children:[(0,s.jsx)(tv,{label:"1. Create a bundle (data.bin + manifest.json with SHA-256)",command:"beastctl backup --db ./data/beast.bin --wal ./data/beast.wal --out ./backup",onNotice:f}),(0,s.jsx)(tv,{label:"2. Verify the manifest and checksum",command:"beastctl verify --backup ./backup",onNotice:f}),(0,s.jsx)(tv,{label:"3. Restore into a destination directory",command:"beastctl restore --backup ./backup --dest ./restored",onNotice:f}),(0,s.jsxs)("p",{className:"text-xs text-muted-foreground",children:["Add ",(0,s.jsx)("span",{className:"font-mono",children:"--force"})," to restore only when you intend to overwrite an existing database. Defaults shown match the CLI."]})]})]})]}),(0,s.jsxs)("div",{className:"grid gap-4 lg:grid-cols-2",children:[(0,s.jsxs)(e$,{children:[(0,s.jsx)(eG,{children:(0,s.jsx)(eY,{className:"text-sm",children:"Restore safety checklist"})}),(0,s.jsx)(eQ,{children:(0,s.jsx)("ol",{className:"list-decimal space-y-2 pl-5 text-xs text-muted-foreground marker:font-mono",children:tw.map(e=>(0,s.jsx)("li",{children:e},e))})})]}),(0,s.jsxs)(e$,{children:[(0,s.jsx)(eG,{children:(0,s.jsx)(eY,{className:"text-sm",children:"Not available yet"})}),(0,s.jsxs)(eQ,{className:"space-y-3 text-xs",children:[["Scheduled / automatic backups","Point-in-time recovery (PITR)","Retention policies and off-host copy"].map(e=>(0,s.jsxs)("div",{className:"flex items-center justify-between",children:[(0,s.jsx)("span",{className:"text-foreground",children:e}),(0,s.jsx)(eX,{variant:"outline",children:"Planned"})]},e)),(0,s.jsx)("p",{className:"text-muted-foreground",children:"These are on the roadmap (ROADMAP.md) and have no controls here. Until then, run beastctl from your own scheduler and copy bundles off the host yourself."})]})]})]})]})}var tC=r(1270),tA=r(5646);let tL=tA.bL,tz=tA.hE,t_=tA.VY,tM=tA.ZD,tR=tA.rc;function tP(e){let{children:t,className:r,...a}=e;return(0,s.jsxs)(tA.ZL,{children:[(0,s.jsx)(tA.hJ,{className:"fixed inset-0 z-[60] bg-black/55"}),(0,s.jsx)(tA.UC,{className:S("fixed left-1/2 top-1/2 z-[60] w-[calc(100%-2rem)] max-w-md -translate-x-1/2 -translate-y-1/2 rounded-lg border bg-card p-6 text-card-foreground shadow-xl",r),...a,children:t})]})}let tE=["admin","writer","viewer","replication"],tT=[{label:"Never",seconds:0},{label:"24 hours",seconds:86400},{label:"7 days",seconds:604800},{label:"30 days",seconds:2592e3},{label:"90 days",seconds:7776e3},{label:"1 year",seconds:31536e3}];function tI(e,t){let[r,s]=(0,a.useState)([]),[n,l]=(0,a.useState)(!1),[o,i]=(0,a.useState)(!1),[d,c]=(0,a.useState)(null),u=(0,a.useCallback)(async()=>{l(!0);try{let e=await fetch("/api/tokens");if(401===e.status||403===e.status){i(!0),s([]);return}if(!e.ok)throw Error(await e.text()||"HTTP ".concat(e.status));let t=await e.json();i(!1),c(null),s(t.tokens||[])}catch(e){c(e.message)}finally{l(!1)}},[]);(0,a.useEffect)(()=>{e&&u()},[e,u]);let x=(0,a.useCallback)(async e=>{try{let r=await fetch("/api/tokens",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:e.name.trim(),role:e.role,...e.expiresInSeconds>0?{expires_in_seconds:e.expiresInSeconds}:{}})});if(!r.ok)throw Error(await r.text()||"HTTP ".concat(r.status));let s=await r.json();return t("API token generated and saved in partition 0x05."),await u(),s.token}catch(e){return t(e.message,!0),null}},[t,u]),m=(0,a.useCallback)(async e=>{try{let r=await fetch("/api/tokens?id=".concat(encodeURIComponent(e)),{method:"DELETE"});if(!r.ok)throw Error(await r.text()||"HTTP ".concat(r.status));return t("Token revoked."),await u(),!0}catch(e){return t(e.message,!0),!1}},[t,u]);return{tokens:r,loading:n,forbidden:o,error:d,refresh:u,create:x,revoke:m}}let tD="h-9 w-full rounded-md border border-border bg-card px-3 text-sm text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring";function tB(e){let{onCreate:t,onCancel:r}=e,[l,o]=(0,a.useState)(""),[i,d]=(0,a.useState)("viewer"),[c,u]=(0,a.useState)(0),[x,m]=(0,a.useState)(!1),f=async e=>{e.preventDefault(),l.trim()&&!x&&(m(!0),await t({name:l,role:i,expiresInSeconds:c}),m(!1))};return(0,s.jsx)(e$,{className:"p-5",children:(0,s.jsxs)("form",{onSubmit:f,className:"space-y-4",children:[(0,s.jsxs)("div",{className:"flex items-center justify-between",children:[(0,s.jsx)("h2",{className:"text-sm font-semibold",children:"Create key"}),(0,s.jsx)("button",{type:"button",onClick:r,"aria-label":"Close form",className:"text-muted-foreground hover:text-foreground",children:(0,s.jsx)(n.A,{size:14})})]}),(0,s.jsxs)("div",{className:"grid gap-4 sm:grid-cols-3",children:[(0,s.jsxs)("label",{className:"space-y-1.5 text-xs text-muted-foreground sm:col-span-1",children:["Name",(0,s.jsx)(D,{value:l,onChange:e=>o(e.target.value),placeholder:"e.g. analytics-service",maxLength:100,autoFocus:!0,required:!0})]}),(0,s.jsxs)("label",{className:"space-y-1.5 text-xs text-muted-foreground",children:["Role",(0,s.jsx)("select",{value:i,onChange:e=>d(e.target.value),className:tD,children:tE.map(e=>(0,s.jsx)("option",{value:e,children:e},e))})]}),(0,s.jsxs)("label",{className:"space-y-1.5 text-xs text-muted-foreground",children:["Expires",(0,s.jsx)("select",{value:c,onChange:e=>u(Number(e.target.value)),className:tD,children:tT.map(e=>(0,s.jsx)("option",{value:e.seconds,children:e.label},e.seconds))})]})]}),(0,s.jsxs)("div",{className:"flex justify-end gap-2",children:[(0,s.jsx)(_,{type:"button",variant:"outline",size:"sm",onClick:r,children:"Cancel"}),(0,s.jsx)(_,{type:"submit",size:"sm",disabled:!l.trim()||x,children:x?"Creating…":"Create key"})]})]})})}function tU(e){let{secret:t,onDismiss:r}=e,[n,l]=(0,a.useState)(!1),o=async()=>{try{await navigator.clipboard.writeText(t),l(!0),setTimeout(()=>l(!1),2e3)}catch(e){}};return(0,s.jsxs)(e$,{className:"border-primary/40 bg-primary/5 p-5",role:"status",children:[(0,s.jsxs)("div",{className:"flex flex-wrap items-center justify-between gap-2",children:[(0,s.jsx)("h2",{className:"text-sm font-semibold text-foreground",children:"Copy your new key now"}),(0,s.jsx)("span",{className:"text-xs text-muted-foreground",children:"It is shown once and cannot be retrieved later."})]}),(0,s.jsxs)("div",{className:"mt-3 flex items-center gap-2 rounded-md border border-border bg-muted px-3 py-2",children:[(0,s.jsx)("code",{className:"min-w-0 flex-1 select-all break-all font-mono text-xs text-foreground",children:t}),(0,s.jsxs)(_,{size:"sm",variant:"outline",onClick:o,"aria-label":"Copy key",children:[n?(0,s.jsx)(tj.A,{size:14}):(0,s.jsx)(eI.A,{size:14}),n?"Copied":"Copy"]})]}),(0,s.jsx)("div",{className:"mt-3 flex justify-end",children:(0,s.jsx)(_,{size:"sm",variant:"ghost",onClick:r,children:"I have saved it"})})]})}let tO={admin:"border-warning/40 bg-warning/10 text-warning",writer:"border-info/40 bg-info/10 text-info",viewer:"border-border bg-muted text-muted-foreground",replication:"border-border bg-transparent text-muted-foreground"};function tF(e){var t;let{role:r}=e;return(0,s.jsx)("span",{className:S("inline-flex items-center rounded-md border px-2 py-0.5 font-mono text-[10px] font-semibold uppercase tracking-wider",null!=(t=tO[r])?t:tO.viewer),children:r})}let tH=[{role:"viewer",can:"Read data",methods:"BeastDBService: Get, Scan"},{role:"writer",can:"Read and write data",methods:"Get, Scan, Put, Delete, BatchWrite"},{role:"replication",can:"Follow the WAL as a replica",methods:"ReplicationService: StreamWAL, Ack"},{role:"admin",can:"Everything above",methods:"All gRPC methods listed here"}];function tK(){return(0,s.jsxs)(e$,{children:[(0,s.jsxs)(eG,{children:[(0,s.jsx)(eY,{className:"text-sm",children:"Roles"}),(0,s.jsx)(eZ,{children:"What each token role may call over gRPC. Any method not listed is denied."})]}),(0,s.jsxs)(eQ,{children:[(0,s.jsx)("ul",{className:"divide-y divide-border",children:tH.map(e=>(0,s.jsxs)("li",{className:"flex flex-col gap-1 py-3 first:pt-0 last:pb-0 sm:flex-row sm:items-center sm:gap-4",children:[(0,s.jsx)("div",{className:"w-28 shrink-0",children:(0,s.jsx)(tF,{role:e.role})}),(0,s.jsxs)("div",{className:"min-w-0",children:[(0,s.jsx)("p",{className:"text-sm text-foreground",children:e.can}),(0,s.jsx)("p",{className:"font-mono text-[11px] text-muted-foreground",children:e.methods})]})]},e.role))}),(0,s.jsxs)("p",{className:"mt-4 border-t border-border pt-3 text-xs text-muted-foreground",children:["The server's static ",(0,s.jsx)("code",{className:"font-mono",children:"--api-token"})," always acts as admin. Expired tokens are rejected as unauthenticated. Studio sign-in accounts are separate: they are only ",(0,s.jsx)("span",{className:"font-mono",children:"admin"})," or ",(0,s.jsx)("span",{className:"font-mono",children:"viewer"}),", and only admins can list, create or revoke keys."]})]})]})}let tW=e=>e?new Date(e).toLocaleDateString(void 0,{year:"numeric",month:"short",day:"numeric"}):"—";function tq(e){let{token:t}=e;return t.expires_at?!function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:Date.now();return!!e.expires_at&&new Date(e.expires_at).getTime()<=t}(t)?(0,s.jsx)("span",{title:new Date(t.expires_at).toLocaleString(),children:tW(t.expires_at)}):(0,s.jsxs)("span",{className:"text-destructive",children:["Expired ",tW(t.expires_at)]}):(0,s.jsx)("span",{className:"text-muted-foreground",children:"Never"})}function tJ(e){let{tokens:t,onRevoke:r}=e,a=[...t].sort((e,t)=>new Date(t.created_at).getTime()-new Date(e.created_at).getTime());return(0,s.jsx)(e$,{className:"overflow-x-auto",children:(0,s.jsxs)("table",{className:"w-full min-w-[640px] text-sm",children:[(0,s.jsx)("thead",{children:(0,s.jsxs)("tr",{className:"border-b border-border text-left text-xs text-muted-foreground",children:[(0,s.jsx)("th",{scope:"col",className:"px-5 py-3 font-medium",children:"Name"}),(0,s.jsx)("th",{scope:"col",className:"px-3 py-3 font-medium",children:"Role"}),(0,s.jsx)("th",{scope:"col",className:"px-3 py-3 font-medium",children:"Key"}),(0,s.jsx)("th",{scope:"col",className:"px-3 py-3 font-medium",children:"Created"}),(0,s.jsx)("th",{scope:"col",className:"px-3 py-3 font-medium",children:"Expires"}),(0,s.jsx)("th",{scope:"col",className:"px-5 py-3",children:(0,s.jsx)("span",{className:"sr-only",children:"Actions"})})]})}),(0,s.jsx)("tbody",{children:a.map(e=>(0,s.jsxs)("tr",{className:"border-b border-border last:border-0",children:[(0,s.jsxs)("td",{className:"px-5 py-3",children:[(0,s.jsx)("span",{className:"font-medium text-foreground",children:e.name}),e.created_by&&(0,s.jsxs)("span",{className:"block text-[11px] text-muted-foreground",children:["by ",e.created_by]})]}),(0,s.jsx)("td",{className:"px-3 py-3",children:(0,s.jsx)(tF,{role:e.role})}),(0,s.jsx)("td",{className:"px-3 py-3 font-mono text-xs text-muted-foreground",children:e.masked_token}),(0,s.jsx)("td",{className:"px-3 py-3 text-muted-foreground",title:new Date(e.created_at).toLocaleString(),children:eR(e.created_at)}),(0,s.jsx)("td",{className:"px-3 py-3 font-mono text-xs",children:(0,s.jsx)(tq,{token:e})}),(0,s.jsx)("td",{className:"px-5 py-3 text-right",children:(0,s.jsxs)(_,{size:"sm",variant:"ghost",onClick:()=>r(e),"aria-label":"Revoke ".concat(e.name),className:"text-muted-foreground hover:text-destructive",children:[(0,s.jsx)(eA.A,{size:14})," Revoke"]})})]},e.id))})]})})}function tV(e){let{user:t,onNotice:r}=e,n=(null==t?void 0:t.role)==="admin",{tokens:l,loading:o,forbidden:i,error:d,create:c,revoke:u}=tI(n,r),[m,f]=(0,a.useState)(!1),[h,p]=(0,a.useState)(null),[g,b]=(0,a.useState)(null),j=!n||i,v=(0,s.jsx)(te,{id:"keys-title",title:"API Keys & Roles",subtitle:"Bearer tokens for gRPC clients and scrapers. Stored in partition 0x05; only a hash is kept.",actions:j||m?void 0:(0,s.jsxs)(_,{size:"sm",onClick:()=>{f(!0),p(null)},children:[(0,s.jsx)(W.A,{size:14})," Create key"]})});return(0,s.jsxs)("section",{"aria-labelledby":"keys-title",className:"space-y-6",children:[v,j?(0,s.jsxs)(e$,{className:"flex flex-col items-center gap-2 px-6 py-14 text-center",role:"status",children:[(0,s.jsx)(tC.A,{size:22,className:"text-muted-foreground"}),(0,s.jsx)("p",{className:"text-sm font-semibold",children:"Admin only"}),(0,s.jsx)("p",{className:"max-w-md text-xs text-muted-foreground",children:"Viewer accounts cannot list, create or revoke API keys. Ask an administrator, or sign in as one."})]}):(0,s.jsxs)(s.Fragment,{children:[h&&(0,s.jsx)(tU,{secret:h,onDismiss:()=>p(null)}),m&&(0,s.jsx)(tB,{onCancel:()=>f(!1),onCreate:async e=>{let t=await c(e);t&&(p(t),f(!1))}}),d&&(0,s.jsxs)("p",{role:"alert",className:"rounded-lg border border-destructive/30 bg-destructive/10 px-3 py-2 text-xs text-destructive",children:["Could not load keys: ",d]}),l.length>0?(0,s.jsx)(tJ,{tokens:l,onRevoke:b}):(0,s.jsxs)(e$,{className:"flex flex-col items-center gap-2 border-dashed px-6 py-12 text-center",children:[(0,s.jsx)(x.A,{size:22,className:"text-muted-foreground"}),(0,s.jsx)("p",{className:"text-sm font-semibold",children:o?"Loading keys…":"No API keys yet"}),!o&&(0,s.jsx)("p",{className:"max-w-sm text-xs text-muted-foreground",children:"Create a key to let services or scripts connect over gRPC with a limited role."})]})]}),(0,s.jsx)(tK,{}),(0,s.jsx)(tL,{open:!!g,onOpenChange:e=>{e||b(null)},children:(0,s.jsxs)(tP,{children:[(0,s.jsx)(tz,{className:"text-base font-semibold",children:"Revoke this key?"}),(0,s.jsxs)(t_,{className:"mt-2 text-sm text-muted-foreground",children:["Clients using ",(0,s.jsx)("span",{className:"font-mono text-foreground",children:null==g?void 0:g.name})," will be rejected immediately. This cannot be undone."]}),(0,s.jsxs)("div",{className:"mt-5 flex justify-end gap-2",children:[(0,s.jsx)(tM,{asChild:!0,children:(0,s.jsx)(_,{variant:"outline",size:"sm",children:"Cancel"})}),(0,s.jsx)(tR,{asChild:!0,children:(0,s.jsx)(_,{variant:"destructive",size:"sm",onClick:()=>g&&void u(g.id),children:"Revoke key"})})]})]})})]})}var t$=r(644);function tG(e){let{values:t,label:r,className:n,tone:l="text-primary",height:o=56,zeroBased:i=!0}=e,d=(0,a.useId)(),c=t.filter(e=>void 0!==e&&Number.isFinite(e));if(c.length<2)return(0,s.jsx)("div",{role:"img","aria-label":"".concat(r,": collecting samples"),className:S("flex items-center justify-center rounded-md border border-dashed border-border text-[11px] text-muted-foreground",n),style:{height:o},children:"collecting samples"});let u=Math.max(...c),x=i?0:Math.min(...c),m=u-x||1,f=t.length,h=[],p=[];t.forEach((e,t)=>{if(void 0!==e&&Number.isFinite(e))p.push([1===f?0:t/(f-1)*240,o-3-(e-x)/m*(o-6)]);else p.length&&h.push(p),p=[]}),p.length&&h.push(p);let g=(()=>{for(let e=t.length-1;e>=0;e--)if(void 0!==t[e])return e;return -1})(),b=g>=0?t[g]:void 0;return(0,s.jsxs)("svg",{role:"img","aria-label":"".concat(r,": latest ").concat(void 0===b?"n/a":b.toPrecision(3),", range ").concat(x.toPrecision(3)," to ").concat(u.toPrecision(3)),viewBox:"0 0 ".concat(240," ").concat(o),preserveAspectRatio:"none",className:S("w-full",l,n),style:{height:o},children:[(0,s.jsx)("defs",{children:(0,s.jsxs)("linearGradient",{id:d,x1:"0",y1:"0",x2:"0",y2:"1",children:[(0,s.jsx)("stop",{offset:"0%",stopColor:"currentColor",stopOpacity:"0.22"}),(0,s.jsx)("stop",{offset:"100%",stopColor:"currentColor",stopOpacity:"0"})]})}),(0,s.jsx)("line",{x1:"0",x2:240,y1:o-3,y2:o-3,className:"stroke-border",strokeWidth:"1",vectorEffect:"non-scaling-stroke"}),h.map((e,t)=>{let r=e.map((e,t)=>{let[r,s]=e;return"".concat(t?"L":"M").concat(r.toFixed(1)," ").concat(s.toFixed(1))}).join(" "),a="".concat(r," L").concat(e[e.length-1][0].toFixed(1)," ").concat(o-3," L").concat(e[0][0].toFixed(1)," ").concat(o-3," Z");return(0,s.jsxs)("g",{children:[e.length>1&&(0,s.jsx)("path",{d:a,fill:"url(#".concat(d,")")}),(0,s.jsx)("path",{d:r,fill:"none",stroke:"currentColor",strokeWidth:"1.5",strokeLinejoin:"round",vectorEffect:"non-scaling-stroke"})]},t)})]})}function tY(e){let{label:t,value:r,unit:a,hint:n,series:l,tone:o,live:i}=e;return(0,s.jsxs)(e$,{className:"p-4",children:[(0,s.jsx)("p",{className:"text-xs text-muted-foreground",children:t}),(0,s.jsxs)("p",{className:S("mt-1 font-mono text-2xl font-semibold tabular-nums",i?"text-primary":"text-foreground"),children:[r,a&&(0,s.jsx)("span",{className:"ml-1 text-sm font-normal text-muted-foreground",children:a})]}),n&&(0,s.jsx)("p",{className:"mt-0.5 font-mono text-[11px] text-muted-foreground",children:n}),l&&l.some(e=>void 0!==e)&&(0,s.jsx)(tG,{values:l,label:t,tone:o,height:40,className:"mt-3"})]})}function tZ(e){let t=Object.entries(e).map(e=>{let[t,r]=e;return"".concat(t,'="').concat(r,'"')});return t.length?"{".concat(t.join(", "),"}"):""}function tQ(e){let{snapshot:t}=e,[r,n]=(0,a.useState)(!1),l=[...t.values()].sort((e,t)=>e.name.localeCompare(t.name));return(0,s.jsxs)(e$,{children:[(0,s.jsxs)("button",{type:"button",onClick:()=>n(e=>!e),"aria-expanded":r,className:"flex w-full items-center gap-2 px-5 py-4 text-left focus-visible:ring-2 focus-visible:ring-ring",children:[(0,s.jsx)(M.A,{size:14,className:"text-muted-foreground transition-transform ".concat(r?"rotate-90":"")}),(0,s.jsx)("span",{className:"text-sm font-semibold",children:"Raw metrics"}),(0,s.jsxs)("span",{className:"font-mono text-xs text-muted-foreground",children:[l.length," families"]})]}),r&&(0,s.jsx)("div",{className:"max-h-[420px] overflow-auto border-t border-border px-5 py-3",children:l.map(e=>(0,s.jsxs)("div",{className:"mb-3 last:mb-0",children:[(0,s.jsxs)("div",{className:"flex flex-wrap items-baseline gap-2",children:[(0,s.jsx)("code",{className:"font-mono text-xs font-semibold text-foreground",children:e.name}),(0,s.jsx)("span",{className:"font-mono text-[10px] uppercase tracking-wider text-muted-foreground",children:e.type})]}),e.help&&(0,s.jsx)("p",{className:"text-[11px] text-muted-foreground",children:e.help}),(0,s.jsx)("ul",{className:"mt-1 space-y-0.5",children:e.samples.map((t,r)=>{var a;return(0,s.jsxs)("li",{className:"flex justify-between gap-4 font-mono text-[11px] text-muted-foreground",children:[(0,s.jsxs)("span",{className:"truncate",title:t.name+tZ(t.labels),children:[t.name!==e.name?t.name.slice(e.name.length):"",tZ(t.labels)]}),(0,s.jsx)("span",{className:"shrink-0 text-foreground",children:Number.isFinite(a=t.value)?Number.isInteger(a)?String(a):a.toPrecision(6).replace(/\.?0+$/,""):a>0?"+Inf":a<0?"-Inf":"NaN"})]},r)})})]},e.name))})]})}function tX(e){return"+Inf"===e||"Inf"===e?1/0:"-Inf"===e?-1/0:Number(e)}function t0(e,t,r,s){let a=e.get(t);if(!a)return;let n=0,l=!1;for(let e of a.samples)e.name===t&&function(e,t){if(!t)return!0;for(let r of Object.keys(t))if(e.labels[r]!==t[r])return!1;return!0}(e,r)&&(!s||s(e))&&(n+=e.value,l=!0);return l?n:void 0}function t1(e,t){let r=e.buckets.length?e.buckets[e.buckets.length-1].count:0;if(r<=0)return;let s=t*r,a=0,n=0;for(let t of e.buckets){if(t.count>=s){if(!Number.isFinite(t.le))return a;let e=t.count-n;if(e<=0)return t.le;return a+(t.le-a)*(s-n)/e}a=t.le,n=t.count}return a}function t2(e,t,r){if(void 0!==e&&void 0!==t&&!(r<=0)&&!(evoid 0===e?"—":e>=100?e.toFixed(0):e.toFixed(2),t8=e=>void 0===e?"—":"".concat((100*e).toFixed(e>0&&e<.001?3:1),"%"),t9=e=>void 0===e?"—":Math.round(e).toLocaleString();function t7(e,t){for(let r=e.length-1;r>=0;r--)if(void 0!==e[r][t])return e[r][t]}let re=(e,t)=>e.some(e=>void 0!==e[t]);function rt(e){let{icon:t,title:r,body:a}=e;return(0,s.jsxs)(e$,{className:"flex flex-col items-center gap-2 px-6 py-14 text-center",role:"status",children:[(0,s.jsx)("div",{className:"text-muted-foreground",children:t}),(0,s.jsx)("p",{className:"text-sm font-semibold",children:r}),(0,s.jsx)("p",{className:"max-w-md text-xs text-muted-foreground",children:a})]})}function rr(e){let{title:t,value:r,history:a,k:n,tone:l,zeroBased:o}=e;return(0,s.jsxs)(e$,{className:"p-5",children:[(0,s.jsxs)("div",{className:"flex items-baseline justify-between",children:[(0,s.jsx)("h2",{className:"text-sm font-semibold",children:t}),(0,s.jsx)("span",{className:"font-mono text-xs text-muted-foreground",children:r})]}),(0,s.jsx)(tG,{values:a.map(e=>e[n]),label:t,tone:l,height:96,zeroBased:o,className:"mt-3"}),(0,s.jsxs)("p",{className:"mt-2 flex justify-between font-mono text-[10px] text-muted-foreground",children:[(0,s.jsxs)("span",{children:["-",Math.round(5)," min"]}),(0,s.jsx)("span",{children:"now"})]})]})}function rs(e){let t,{user:r}=e,n=(null==r?void 0:r.role)==="admin",{status:o,snapshot:i,history:d,lastUpdated:c,error:u}=function(e){let[t,r]=(0,a.useState)({status:"loading",snapshot:null,history:[],lastUpdated:null,error:null}),s=(0,a.useRef)(void 0);return(0,a.useEffect)(()=>{let t;if(!e)return;let a=!1,n=async()=>{try{let e=await fetch("/metrics",{cache:"no-store"});if(a)return;if(401===e.status||403===e.status)return void r(e=>({...e,status:"forbidden",error:null}));if(!e.ok)throw Error("/metrics returned HTTP ".concat(e.status));let t=function(e){let t=new Map,r=new Map,s=new Map;for(let l of e.split("\n")){let e=l.trim();if(!e)continue;if(e.startsWith("#")){let s=/^#\s+(HELP|TYPE)\s+(\S+)\s*(.*)$/.exec(e);if(!s)continue;"TYPE"===s[1]?t.set(s[2],s[3].trim()):r.set(s[2],s[3]);continue}let o=/^([a-zA-Z_:][a-zA-Z0-9_:]*)(?:\{(.*)\})?\s+(\S+)(?:\s+\S+)?$/.exec(e);if(!o)continue;let i=void 0===o[2]?{}:function(e){let t={},r=0;for(;r=e.length)break;let s=e.indexOf("=",r);if(s<0||'"'!==e[s+1])return null;let a=e.slice(r,s).trim(),n=s+2,l="";for(;n=e.length)return null;t[a]=l,r=n+1}return t}(o[2]);if(!i)continue;let d=tX(o[3]),c=function(e,t){if(t.has(e))return e;for(let r of["_bucket","_sum","_count"])if(e.endsWith(r)){let s=e.slice(0,-r.length),a=t.get(s);if("histogram"===a||"summary"===a)return s}return e}(o[1],t),u=s.get(c);if(!u){var a,n;u={name:c,type:null!=(a=t.get(c))?a:"untyped",help:null!=(n=r.get(c))?n:"",samples:[]},s.set(c,u)}u.samples.push({name:o[1],labels:i,value:d})}return s}(await e.text());if(a)return;if(0===t.size)throw Error("/metrics returned no samples");let n={t:Date.now(),req:t0(t,t5),err:t0(t,t5,void 0,e=>{var t;let r;return(r=null!=(t=e.labels.code)?t:"").startsWith("5")||t3.has(r)}),hist:function(e,t){let r=e.get(t);if(!r||"histogram"!==r.type)return;let s=new Map,a=0,n=0;for(let e of r.samples)if(e.name==="".concat(t,"_bucket")&&void 0!==e.labels.le){var l;let t=tX(e.labels.le);s.set(t,(null!=(l=s.get(t))?l:0)+e.value)}else e.name==="".concat(t,"_sum")?a+=e.value:e.name==="".concat(t,"_count")&&(n+=e.value);if(0!==s.size)return{buckets:[...s.entries()].sort((e,t)=>e[0]-t[0]).map(e=>{let[t,r]=e;return{le:t,count:r}}),sum:a,count:n}}(t,"beastdb_request_duration_seconds"),hits:t0(t,"beastdb_buffer_pool_hits_total"),misses:t0(t,"beastdb_buffer_pool_misses_total")},l=function(e,t,r){let s,a=t?(e.t-t.t)/1e3:0,n=t?t2(e.req,t.req,a):void 0,l=t?t2(e.err,t.err,a):void 0;void 0!==n&&void 0!==l&&n>0?s=l/n:0===n&&(s=0);let o=e.hist&&(null==t?void 0:t.hist)?function(e,t){if(e.count({le:e.le,count:e.count-t.buckets[r].count}));if(!r.some(e=>e.count<0))return{buckets:r,sum:e.sum-t.sum,count:e.count-t.count}}(e.hist,t.hist):void 0;return{t:e.t,reqPerSec:n,errPerSec:l,errorRatio:s,p50:o?t1(o,.5):void 0,p99:o?t1(o,.99):void 0,walBytes:t0(r,"beastdb_wal_size_bytes"),hitRatio:t0(r,"beastdb_buffer_pool_hit_ratio"),dirtyPages:t0(r,"beastdb_buffer_pool_dirty_pages"),inFlight:t0(r,"beastdb_grpc_requests_in_flight")}}(n,s.current,t);s.current=n,r(e=>({status:"ok",snapshot:t,history:[...e.history,l].slice(-60),lastUpdated:n.t,error:null}))}catch(e){if(a)return;r(t=>({...t,status:t.snapshot?t.status:"unavailable",error:e.message}))}a||(t=setTimeout(n,5e3))};return n(),()=>{a=!0,t&&clearTimeout(t)}},[e]),t}(n),x=(0,s.jsx)(te,{id:"metrics-title",title:"Metrics",subtitle:"Live engine telemetry from /metrics, polled every 5 seconds.",actions:"ok"===o&&c?(0,s.jsxs)("span",{className:"inline-flex items-center gap-2 font-mono text-xs text-muted-foreground",children:[(0,s.jsx)("span",{className:"h-1.5 w-1.5 rounded-full bg-primary","aria-hidden":!0}),"live \xb7 ",new Date(c).toLocaleTimeString()]}):void 0});if(n&&"forbidden"!==o)if("unavailable"===o)t=(0,s.jsx)(rt,{icon:(0,s.jsx)(t$.A,{size:22}),title:"Metrics unavailable",body:"Could not read /metrics".concat(u?": ".concat(u):"",". Retrying every 5 seconds.")});else if("loading"!==o&&i){var m;let e=t7(d,"reqPerSec"),r=function(e){let t=t=>t0(e,t),r=[],s=t("beastdb_buffer_pool_size_pages"),a=t("beastdb_buffer_pool_cached_pages");(void 0!==s||void 0!==a)&&r.push({label:"Buffer pool",value:"".concat(t9(a)),unit:void 0!==s?"/ ".concat(t9(s)," pages"):"pages",hint:"pinned ".concat(t9(t("beastdb_buffer_pool_pinned_pages"))," \xb7 dirty ").concat(t9(t("beastdb_buffer_pool_dirty_pages")))});let n=t("beastdb_wal_size_bytes");if(void 0!==n){let e=t("beastdb_checkpoint_age_seconds");r.push({label:"WAL size",value:eM(n),hint:[void 0!==t("beastdb_wal_current_lsn")?"LSN ".concat(t9(t("beastdb_wal_current_lsn"))):"",void 0!==e?"checkpoint ".concat(Math.round(e),"s ago"):""].filter(Boolean).join(" \xb7 ")})}let l=t("beastdb_disk_free_bytes");void 0!==l&&r.push({label:"Disk free",value:eM(l),hint:void 0!==t("beastdb_data_file_size_bytes")?"data file ".concat(eM(t("beastdb_data_file_size_bytes"))):void 0});let o=t("beastdb_uptime_seconds");if(void 0!==o){let e=Math.floor(o/3600);r.push({label:"Uptime",value:e>=24?"".concat(Math.floor(e/24),"d ").concat(e%24,"h"):e>=1?"".concat(e,"h ").concat(Math.floor(o%3600/60),"m"):"".concat(Math.floor(o/60),"m ").concat(Math.floor(o%60),"s")})}for(let t of e.values()){if(!/fsync|group_commit/.test(t.name)||"histogram"===t.type)continue;let s=t0(e,t.name);void 0!==s&&r.push({label:t.name.replace(/^beastdb_/,"").replace(/_/g," "),value:t9(s)})}return r}(i);t=(0,s.jsxs)(s.Fragment,{children:[u&&(0,s.jsxs)("p",{role:"alert",className:"rounded-lg border border-warning/30 bg-warning/10 px-3 py-2 text-xs text-warning",children:["Last poll failed (",u,"); showing the previous sample."]}),(0,s.jsxs)("div",{className:"grid gap-4 sm:grid-cols-2 xl:grid-cols-4",children:[re(d,"reqPerSec")?(0,s.jsx)(tY,{label:"Requests / s",value:t6(e),live:(null!=e?e:0)>0}):(0,s.jsx)(tY,{label:"Requests / s",value:"—",hint:"needs two samples"}),re(d,"errorRatio")&&(0,s.jsx)(tY,{label:"Error rate",value:t8(t7(d,"errorRatio")),hint:"".concat(t6(t7(d,"errPerSec"))," errors/s (5xx, server faults)")}),re(d,"p50")&&(0,s.jsx)(tY,{label:"Latency p50",value:t4(t7(d,"p50")),hint:"last 5s window"}),re(d,"p99")&&(0,s.jsx)(tY,{label:"Latency p99",value:t4(t7(d,"p99")),hint:"bucket-interpolated"}),re(d,"hitRatio")&&(0,s.jsx)(tY,{label:"Buffer hit ratio",value:t8(t7(d,"hitRatio")),hint:"lifetime"}),re(d,"inFlight")&&(0,s.jsx)(tY,{label:"gRPC in flight",value:t9(t7(d,"inFlight"))}),r.map(e=>(0,s.jsx)(tY,{...e},e.label))]}),(0,s.jsxs)("div",{className:"grid gap-4 lg:grid-cols-2",children:[re(d,"reqPerSec")&&(0,s.jsx)(rr,{title:"Requests / s",value:t6(e),history:d,k:"reqPerSec"}),re(d,"errPerSec")&&(0,s.jsx)(rr,{title:"Errors / s",value:t6(t7(d,"errPerSec")),history:d,k:"errPerSec",tone:"text-destructive"}),re(d,"p99")&&(0,s.jsx)(rr,{title:"Latency p99",value:t4(t7(d,"p99")),history:d,k:"p99",tone:"text-info"}),re(d,"walBytes")&&(0,s.jsx)(rr,{title:"WAL size",value:eM(null!=(m=t7(d,"walBytes"))?m:0),history:d,k:"walBytes",tone:"text-warning"}),re(d,"dirtyPages")&&(0,s.jsx)(rr,{title:"Dirty pages",value:t9(t7(d,"dirtyPages")),history:d,k:"dirtyPages",tone:"text-info"})]}),(0,s.jsx)(tQ,{snapshot:i})]})}else t=(0,s.jsx)(rt,{icon:(0,s.jsx)(l.A,{size:22}),title:"Loading metrics",body:"Waiting for the first sample."});else t=(0,s.jsx)(rt,{icon:(0,s.jsx)(tC.A,{size:22}),title:"Admin only",body:"Engine metrics are restricted to administrator sessions. Sign in as an admin to view them."});return(0,s.jsxs)("section",{"aria-labelledby":"metrics-title",className:"space-y-6",children:[x,t]})}var ra=r(5148),rn=r(8941),rl=r(3489),ro=r(1165);let ri=["error","warn","info","debug"],rd={error:"text-destructive",warn:"text-warning",info:"text-muted-foreground",debug:"text-muted-foreground/60"};function rc(e){let{user:t}=e,r=(null==t?void 0:t.role)==="admin",[n,l]=(0,a.useState)([]),[o,i]=(0,a.useState)("loading"),[d,c]=(0,a.useState)(!1),[u,x]=(0,a.useState)(()=>new Set(ri)),[m,f]=(0,a.useState)(""),[h,p]=(0,a.useState)(!0),g=(0,a.useRef)(0),b=(0,a.useRef)(null),j=(0,a.useCallback)(async()=>{try{let e=await fetch("/api/logs?since=".concat(g.current,"&limit=1000"),{cache:"no-store"});if(403===e.status||401===e.status)return i("forbidden");if(!e.ok)return i("error");let t=await e.json();t.latest(function(e,t){let r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:2e3;if(0===t.length)return e;let s=e.length?e[e.length-1].seq:0,a=t.filter(e=>e.seq>s);if(0===a.length)return e;let n=e.concat(a);return n.length>r?n.slice(n.length-r):n})(e,t.entries))),i("ok")}catch(e){i("error")}},[]);(0,a.useEffect)(()=>{if(!r||d||"forbidden"===o)return;j();let e=setInterval(()=>void j(),2e3);return()=>clearInterval(e)},[r,d,"forbidden"===o,j]);let v=(0,a.useMemo)(()=>(function(e,t,r){let s=r.trim().toLowerCase();return e.filter(e=>t.has(e.level)&&(""===s||e.message.toLowerCase().includes(s)))})(n,u,m),[n,u,m]),y=(0,a.useMemo)(()=>{let e={error:0,warn:0,info:0,debug:0};for(let t of n)e[t.level]+=1;return e},[n]);return((0,a.useEffect)(()=>{let e=b.current;e&&h&&(e.scrollTop=e.scrollHeight)},[v,h]),r&&"forbidden"!==o)?(0,s.jsxs)("section",{"aria-labelledby":"logs-title",className:"space-y-4",children:[(0,s.jsx)(te,{id:"logs-title",title:"Logs",subtitle:"Recent server log, kept in memory only (last ".concat(n.length," of at most 2,000 lines; cleared on restart)."),actions:(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(eX,{variant:d?"mono":"lime",children:d?"PAUSED":"LIVE"}),(0,s.jsxs)(_,{variant:"outline",size:"sm",onClick:()=>c(e=>!e),children:[d?(0,s.jsx)(e8.A,{className:"h-3.5 w-3.5","aria-hidden":!0}):(0,s.jsx)(rn.A,{className:"h-3.5 w-3.5","aria-hidden":!0}),d?"Resume":"Pause"]}),(0,s.jsxs)(_,{variant:"outline",size:"sm",onClick:()=>{let e=URL.createObjectURL(new Blob([v.map(e=>"".concat(e.time," ").concat(e.level.toUpperCase().padEnd(5)," ").concat(e.message)).join("\n")+(v.length?"\n":"")],{type:"text/plain"})),t=document.createElement("a");t.href=e,t.download="beastdb-logs-".concat(new Date().toISOString().replace(/[:.]/g,"-"),".txt"),t.click(),URL.revokeObjectURL(e)},disabled:0===v.length,children:[(0,s.jsx)(rl.A,{className:"h-3.5 w-3.5","aria-hidden":!0}),"Download"]})]})}),(0,s.jsxs)("div",{className:"flex flex-wrap items-center gap-2",children:[ri.map(e=>{let t=u.has(e);return(0,s.jsxs)("button",{type:"button","aria-pressed":t,onClick:()=>x(t=>{let r=new Set(t);return r.has(e)?r.delete(e):r.add(e),r}),className:S("rounded-md border px-2.5 py-1 font-mono text-xs uppercase transition-colors",t?"border-border bg-muted text-foreground":"border-border/50 text-muted-foreground/60 hover:text-foreground"),children:[e," ",(0,s.jsx)("span",{className:"text-muted-foreground",children:y[e]})]},e)}),(0,s.jsx)(D,{value:m,onChange:e=>f(e.target.value),placeholder:"Filter messages","aria-label":"Filter log messages",className:"ml-auto h-8 w-full max-w-xs"})]}),(0,s.jsxs)(e$,{className:"relative overflow-hidden",children:[(0,s.jsxs)("div",{ref:b,onScroll:()=>{let e=b.current;e&&p(e.scrollHeight-e.scrollTop-e.clientHeight<24)},role:"log","aria-live":"off",className:"h-[calc(100vh-18rem)] min-h-[20rem] overflow-auto p-2 font-mono text-xs leading-5",children:["error"===o&&(0,s.jsx)("p",{className:"p-3 text-destructive",children:"Cannot read logs right now. Retrying every 2 seconds."}),"loading"===o&&(0,s.jsx)("p",{className:"p-3 text-muted-foreground",children:"Loading logs…"}),"ok"===o&&0===v.length&&(0,s.jsx)("p",{className:"p-3 text-muted-foreground",children:0===n.length?"No log lines yet.":"No lines match the current filters."}),v.map(e=>(0,s.jsxs)("div",{className:"flex gap-3 rounded px-2 hover:bg-muted/50",children:[(0,s.jsx)("span",{className:"shrink-0 text-muted-foreground/70",children:function(e){let t=new Date(e);if(isNaN(t.getTime()))return e;let r=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:2;return String(e).padStart(t,"0")};return"".concat(r(t.getHours()),":").concat(r(t.getMinutes()),":").concat(r(t.getSeconds()))}(e.time)}),(0,s.jsx)("span",{className:S("w-11 shrink-0 uppercase",rd[e.level]),children:e.level}),(0,s.jsx)("span",{className:"min-w-0 whitespace-pre-wrap break-words text-foreground",children:e.message})]},e.seq))]}),!h&&v.length>0&&(0,s.jsxs)(_,{size:"sm",variant:"secondary",onClick:()=>{let e=b.current;e&&(e.scrollTop=e.scrollHeight),p(!0)},className:"absolute bottom-3 right-4 shadow-md",children:[(0,s.jsx)(ro.A,{className:"h-3.5 w-3.5","aria-hidden":!0}),"Jump to latest"]})]})]}):(0,s.jsxs)("section",{"aria-labelledby":"logs-title",className:"space-y-6",children:[(0,s.jsx)(te,{id:"logs-title",title:"Logs",subtitle:"Server log tail."}),(0,s.jsxs)(e$,{className:"p-10 text-center",children:[(0,s.jsx)(ra.A,{className:"mx-auto mb-3 h-6 w-6 text-muted-foreground","aria-hidden":!0}),(0,s.jsx)("p",{className:"font-medium",children:"Logs are limited to administrators"}),(0,s.jsx)("p",{className:"mt-1 text-sm text-muted-foreground",children:"Sign in as an admin to read the server log."})]})]})}function ru(e){let{value:t,label:r}=e,[n,l]=(0,a.useState)(!1),o=async()=>{try{await navigator.clipboard.writeText(t),l(!0),setTimeout(()=>l(!1),1500)}catch(e){}};return(0,s.jsx)(_,{variant:"ghost",size:"icon",className:"h-7 w-7",onClick:o,"aria-label":"Copy ".concat(r),children:n?(0,s.jsx)(tj.A,{className:"h-3.5 w-3.5","aria-hidden":!0}):(0,s.jsx)(eI.A,{className:"h-3.5 w-3.5","aria-hidden":!0})})}function rx(e){let{title:t,description:r,rows:a}=e;return(0,s.jsxs)(e$,{children:[(0,s.jsxs)(eG,{children:[(0,s.jsx)(eY,{className:"text-base",children:t}),(0,s.jsx)(eZ,{children:r})]}),(0,s.jsx)(eQ,{children:(0,s.jsx)("dl",{className:"divide-y divide-border",children:a.map(e=>(0,s.jsxs)("div",{className:"flex items-center justify-between gap-4 py-2",children:[(0,s.jsx)("dt",{className:"text-sm text-muted-foreground",children:e.label}),(0,s.jsxs)("dd",{className:"flex min-w-0 items-center gap-1",children:[e.flag?(0,s.jsx)(eX,{variant:"Yes"===e.value?"outline":"mono",children:e.value}):(0,s.jsx)("span",{className:"truncate font-mono text-sm",title:e.value,children:e.value||"—"}),e.copy&&e.value&&(0,s.jsx)(ru,{value:e.value,label:e.label})]})]},e.label))})})]})}let rm=e=>e?"Yes":"No";function rf(e){let{user:t}=e,{dark:r,toggle:n}=N(),l=(null==t?void 0:t.role)==="admin",[o,i]=(0,a.useState)(null),[d,c]=(0,a.useState)(l?"loading":"forbidden");return(0,a.useEffect)(()=>{if(!l)return void c("forbidden");let e=!1;return(async()=>{try{let t=await fetch("/api/settings",{cache:"no-store"});if(e)return;if(403===t.status||401===t.status)return c("forbidden");if(!t.ok)return c("error");i(await t.json()),c("ok")}catch(t){e||c("error")}})(),()=>{e=!0}},[l]),(0,s.jsxs)("section",{"aria-labelledby":"settings-title",className:"space-y-6",children:[(0,s.jsx)(te,{id:"settings-title",title:"Settings",subtitle:"How this node is configured. Read-only."}),(0,s.jsxs)(e$,{children:[(0,s.jsxs)(eG,{children:[(0,s.jsx)(eY,{className:"text-base",children:"Appearance"}),(0,s.jsx)(eZ,{children:"Stored in this browser only."})]}),(0,s.jsxs)(eQ,{className:"flex items-center justify-between",children:[(0,s.jsxs)("span",{className:"text-sm text-muted-foreground",children:["Theme: ",r?"Dark":"Light"]}),(0,s.jsxs)(_,{variant:"outline",size:"sm",onClick:n,children:[r?(0,s.jsx)(g.A,{className:"h-3.5 w-3.5","aria-hidden":!0}):(0,s.jsx)(b.A,{className:"h-3.5 w-3.5","aria-hidden":!0}),"Switch to ",r?"light":"dark"]})]})]}),"loading"===d&&(0,s.jsx)("p",{className:"text-sm text-muted-foreground",children:"Loading settings…"}),"error"===d&&(0,s.jsx)("p",{className:"text-sm text-destructive",children:"Could not load settings. Reload to retry."}),"forbidden"===d&&(0,s.jsx)(e$,{className:"p-6 text-sm text-muted-foreground",children:"Node settings are visible to administrators only."}),"ok"===d&&o&&(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)("p",{className:"rounded-lg border border-border bg-muted/40 px-4 py-3 text-sm text-muted-foreground",children:o.note}),(0,s.jsx)("div",{className:"grid gap-4 lg:grid-cols-2",children:[{title:"Node",description:"Identity and role of this server.",rows:[{label:"Role",value:o.node.role},{label:"Version",value:o.node.version,copy:!0},{label:"Replica ID",value:o.node.replica_id,copy:!0},...o.node.leader_addr?[{label:"Leader address",value:o.node.leader_addr,copy:!0}]:[],{label:"Dev emulator",value:rm(o.node.dev_mode),flag:!0}]},{title:"Storage",description:"Where data lives and how the engine buffers it.",rows:[{label:"Data directory",value:o.storage.data_dir,copy:!0},{label:"Buffer pool",value:"".concat(o.storage.pool_size_frames," frames (").concat(eM(o.storage.pool_size_frames*o.storage.page_size_bytes),")")},{label:"Page size",value:eM(o.storage.page_size_bytes)},{label:"WAL checkpoint threshold",value:"".concat(o.storage.wal_checkpoint_mb," MiB")}]},{title:"Network",description:"Listening addresses.",rows:[{label:"gRPC",value:o.network.grpc_addr,copy:!0},{label:"Web console",value:o.network.web_addr||"disabled",copy:!!o.network.web_addr}]},{title:"Security",description:"Whether protections are configured. Secret values are never shown.",rows:[{label:"TLS on gRPC",value:rm(o.security.tls_enabled),flag:!0},{label:"Client certificates (mTLS CA)",value:rm(o.security.mutual_tls),flag:!0},{label:"gRPC API token set",value:rm(o.security.api_token_configured),flag:!0},{label:"Metrics token set",value:rm(o.security.metrics_token_configured),flag:!0},{label:"Insecure-auth override",value:rm(o.security.insecure_auth_override),flag:!0},{label:"Partition config",value:o.security.partition_config_path||"none",copy:!!o.security.partition_config_path}]},{title:"Limits",description:"Fixed by the engine; not configurable at runtime.",rows:[{label:"Max value size",value:eM(o.limits.max_value_bytes)},{label:"Max inline record",value:eM(o.limits.max_inline_record)},{label:"Max batch size",value:eM(o.limits.max_batch_bytes)},{label:"Max gRPC message",value:eM(o.limits.max_grpc_message)},{label:"Max web upload body",value:eM(o.limits.max_web_put_body)},{label:"Records page size (max)",value:String(o.limits.max_records_page_size)}]}].map(e=>(0,s.jsx)(rx,{...e},e.title))})]})]})}var rh=r(5544),rp=r(805),rg=r(4368);let rb=rg.bL,rj=rg.hE,rv=rg.VY;function ry(e){let{children:t,className:r,...a}=e;return(0,s.jsxs)(rg.ZL,{children:[(0,s.jsx)(rg.hJ,{className:"fixed inset-0 z-50 bg-black/55"}),(0,s.jsxs)(rg.UC,{className:S("fixed left-1/2 top-1/2 z-50 flex max-h-[90vh] w-[calc(100%-2rem)] max-w-xl -translate-x-1/2 -translate-y-1/2 flex-col overflow-y-auto rounded-lg border bg-card p-6 text-card-foreground shadow-xl",r),...a,children:[t,(0,s.jsx)(rg.bm,{className:"absolute right-4 top-4 rounded-sm text-muted-foreground hover:text-foreground","aria-label":"Close",children:(0,s.jsx)(n.A,{size:18})})]})]})}function rN(e){let{fields:t,setFields:r}=e,a=(e,t)=>r(r=>r.map(r=>r.id===e?{...r,...t}:r));return(0,s.jsxs)("div",{className:"mt-3 space-y-3",children:[(0,s.jsxs)("div",{className:"flex items-center justify-between",children:[(0,s.jsx)("span",{className:"text-sm font-medium",children:"Properties"}),(0,s.jsxs)(_,{type:"button",size:"sm",variant:"outline",onClick:()=>r(e=>[...e,{id:crypto.randomUUID(),name:"",type:"string",value:""}]),children:[(0,s.jsx)(W.A,{size:14}),"Add property"]})]}),(0,s.jsx)("div",{className:"max-h-64 space-y-2 overflow-y-auto",children:t.map(e=>(0,s.jsxs)("div",{className:"grid grid-cols-[1fr_auto] gap-2 rounded-md border p-2 sm:grid-cols-[1fr_100px_1fr_auto]",children:[(0,s.jsx)(D,{"aria-label":"Property name",placeholder:"Name",value:e.name,onChange:t=>a(e.id,{name:t.target.value})}),(0,s.jsxs)("select",{"aria-label":"Property type",className:"h-9 rounded-md border bg-card px-2 text-sm",value:e.type,onChange:t=>a(e.id,{type:t.target.value}),children:[(0,s.jsx)("option",{value:"string",children:"String"}),(0,s.jsx)("option",{value:"number",children:"Number"}),(0,s.jsx)("option",{value:"boolean",children:"Boolean"}),(0,s.jsx)("option",{value:"json",children:"JSON"})]}),"boolean"===e.type?(0,s.jsxs)("select",{"aria-label":"Property value",className:"h-9 rounded-md border bg-card px-2 text-sm",value:e.value||"false",onChange:t=>a(e.id,{value:t.target.value}),children:[(0,s.jsx)("option",{value:"false",children:"false"}),(0,s.jsx)("option",{value:"true",children:"true"})]}):(0,s.jsx)(D,{"aria-label":"Property value",placeholder:"Value",value:e.value,onChange:t=>a(e.id,{value:t.target.value})}),(0,s.jsx)(_,{type:"button",size:"icon",variant:"ghost","aria-label":"Remove property",onClick:()=>r(t=>t.filter(t=>t.id!==e.id)),children:(0,s.jsx)(eA.A,{size:15})})]},e.id))})]})}function rw(e){let{open:t,onClose:r,onSave:n,onLookup:l,onNotice:o}=e,[i,d]=(0,a.useState)("builder"),[c,u]=(0,a.useState)(""),[x,m]=(0,a.useState)(""),[f,h]=(0,a.useState)(1),[p,g]=(0,a.useState)(""),[b,j]=(0,a.useState)(""),[v,y]=(0,a.useState)("raw"),[N,w]=(0,a.useState)(""),[k,S]=(0,a.useState)([{id:"initial",name:"",type:"string",value:""}]),[C,A]=(0,a.useState)(!1),[L,z]=(0,a.useState)(""),M=el(),R=()=>"raw"===i?c:"hash"===i?eP(x.trim()):String(BigInt(f)<<56n|eE(p)<<28n|eE(b)),P=async e=>{let t;e.preventDefault(),z("");let s=R();if(!/^(0|[1-9]\d*)$/.test(s)||BigInt(s)>0xffffffffffffffffn)return void z("Key is not a valid uint64 decimal.");try{t="builder"===v?function(e){let t=Object.create(null);for(let r of e){if(!r.name.trim())throw Error("Each property needs a name.");if(Object.hasOwn(t,r.name))throw Error("Duplicate property: ".concat(r.name));if("number"===r.type){if(!r.value.trim()||!Number.isFinite(Number(r.value)))throw Error("Enter a valid number for ".concat(r.name,"."));t[r.name]=Number(r.value)}else if("boolean"===r.type)t[r.name]="true"===r.value;else if("json"===r.type)try{t[r.name]=JSON.parse(r.value)}catch(e){throw Error("Invalid JSON in ".concat(r.name,"."))}else t[r.name]=r.value}return JSON.stringify(t,null,2)}(k):N}catch(e){z(e.message);return}if(!t.length)return void z("Enter a value to save.");A(!0);try{try{await l(s),z("This key already exists. Choose another to avoid overwriting.");return}catch(e){if("That record was not found."!==e.message)throw e}await n(s,t),o("Record created."),u(""),w(""),m(""),g(""),j(""),r()}catch(e){z(e.message)}finally{A(!1)}},E=(()=>{try{let e=R();if(!e||!/^(0|[1-9]\d*)$/.test(e))return null;return e}catch(e){return null}})();return(0,s.jsx)(rb,{open:t,onOpenChange:e=>{e||C||r()},children:(0,s.jsxs)(ry,{children:[(0,s.jsx)(rj,{className:"pr-8 text-xl font-semibold",children:"New Record"}),(0,s.jsx)(rv,{className:"mt-1 text-sm text-muted-foreground",children:"Add a key–value pair to BeastDB. Keys must be unique uint64 values."}),(0,s.jsxs)("form",{onSubmit:P,className:"mt-5 space-y-5",children:[(0,s.jsxs)("div",{className:"space-y-2",children:[(0,s.jsx)("p",{className:"text-xs font-mono font-semibold uppercase tracking-wider text-muted-foreground",children:"64-bit Key"}),(0,s.jsx)("div",{className:"grid grid-cols-3 rounded-lg border border-border bg-muted/60 p-1 text-xs",children:["builder","hash","raw"].map(e=>(0,s.jsx)("button",{type:"button",onClick:()=>d(e),className:"rounded-md py-1.5 capitalize transition ".concat(i===e?"bg-primary/10 text-primary border border-primary/30 font-medium":"text-muted-foreground hover:text-foreground"),children:"builder"===e?"Partition Builder":"hash"===e?"Hash Seed":"Raw Decimal"},e))}),"builder"===i&&(0,s.jsxs)("div",{className:"space-y-2",children:[(0,s.jsx)("select",{value:f,onChange:e=>h(Number(e.target.value)),className:"w-full rounded-md border border-border bg-card px-3 py-2 text-sm font-mono text-foreground focus:outline-none focus:ring-1 focus:ring-primary",children:M.map(e=>(0,s.jsxs)("option",{value:e.prefix,children:["0x",e.prefix.toString(16).padStart(2,"0").toUpperCase()," \xb7 ",e.label]},e.prefix))}),(0,s.jsxs)("div",{className:"grid grid-cols-2 gap-2",children:[(0,s.jsx)(D,{placeholder:"Domain / user hash seed",value:p,onChange:e=>g(e.target.value),"aria-label":"Domain seed"}),(0,s.jsx)(D,{placeholder:"Item ID / record seed",value:b,onChange:e=>j(e.target.value),"aria-label":"Item seed"})]}),(0,s.jsx)("p",{className:"text-[11px] text-muted-foreground font-mono",children:"key = (prefix << 56) | (FNV28(domain) << 28) | FNV28(item)"})]}),"hash"===i&&(0,s.jsx)("div",{className:"flex gap-2",children:(0,s.jsx)(D,{value:x,onChange:e=>m(e.target.value),placeholder:"Identifier to hash with FNV-1a 64-bit","aria-label":"Hash seed"})}),"raw"===i&&(0,s.jsx)(D,{inputMode:"numeric",value:c,onChange:e=>u(e.target.value),placeholder:"e.g. 72057594037927936"}),E&&(0,s.jsxs)("div",{className:"flex items-center gap-2 rounded-md border border-primary/20 bg-primary/5 px-3 py-2 text-xs font-mono",children:[(0,s.jsx)(rp.A,{size:12,className:"text-primary shrink-0"}),(0,s.jsx)("span",{className:"text-muted-foreground",children:"Key preview:"}),(0,s.jsx)("span",{className:"text-primary font-medium",children:E})]})]}),(0,s.jsxs)(rh.bL,{value:v,onValueChange:y,children:[(0,s.jsx)(rh.B8,{"aria-label":"Value editor",className:"grid grid-cols-2 rounded-lg border border-border bg-muted/60 p-1 text-xs",children:["raw","builder"].map(e=>(0,s.jsx)(rh.l9,{value:e,className:"rounded-md py-1.5 transition data-[state=active]:bg-primary/10 data-[state=active]:text-primary data-[state=active]:border data-[state=active]:border-primary/30 data-[state=active]:font-medium text-muted-foreground hover:text-foreground",children:"raw"===e?"Raw JSON / Payload":"Structured Builder"},e))}),(0,s.jsxs)(rh.UC,{value:"raw",className:"mt-3",children:[(0,s.jsx)("label",{htmlFor:"record-value",className:"text-sm font-medium",children:"Value \xb7 JSON, text, or token"}),(0,s.jsx)("textarea",{id:"record-value",rows:7,value:N,onChange:e=>w(e.target.value),className:"mt-2 w-full rounded-md border border-border bg-card p-3 font-mono text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-primary",placeholder:'{"id": 1, "name": "beast"}'})]}),(0,s.jsx)(rh.UC,{value:"builder",children:(0,s.jsx)(rN,{fields:k,setFields:S})})]}),L&&(0,s.jsx)("p",{role:"alert",className:"text-sm text-destructive",children:L}),(0,s.jsxs)("div",{className:"flex justify-end gap-2 border-t border-border pt-4",children:[(0,s.jsx)(_,{type:"button",variant:"outline",onClick:r,disabled:C,children:"Cancel"}),(0,s.jsx)(_,{type:"submit",disabled:C,children:C?"Writing to WAL…":"Create Record"})]})]})]})})}let rk=[{label:"User (0x01)",prefix:"72057594037927936"},{label:"Token (0x05)",prefix:"360287970189639680"},{label:"Catalog (0x10)",prefix:"1152921504606846976"}];function rS(e){let{open:t,onClose:r}=e,[n,l]=(0,a.useState)(""),[o,i]=(0,a.useState)(""),d=/^(0|[1-9]\d*)$/.test(n)&&0xffffffffffffffffn>=BigInt(n||"0"),c=d&&n?eo(n):null;return(0,s.jsx)(rb,{open:t,onOpenChange:e=>{e||r()},children:(0,s.jsxs)(ry,{className:"max-w-xl",children:[(0,s.jsx)(rj,{className:"text-xl font-bold tracking-tight",children:"Key Analyzer"}),(0,s.jsx)(rv,{className:"mt-1 text-xs text-muted-foreground",children:"Inspect 64-bit BeastDB keys, decode partition anatomy, or hash identifiers via FNV-1a."}),(0,s.jsxs)("div",{className:"mt-4 space-y-4",children:[(0,s.jsxs)("div",{children:[(0,s.jsxs)("div",{className:"flex items-center justify-between",children:[(0,s.jsx)("label",{htmlFor:"analyzer-key",className:"text-xs font-mono text-muted-foreground",children:"DECIMAL KEY"}),(0,s.jsx)("div",{className:"flex gap-1",children:rk.map(e=>(0,s.jsx)("button",{type:"button",onClick:()=>l(e.prefix),className:"text-[10px] font-mono text-muted-foreground hover:text-primary underline",children:e.label},e.prefix))})]}),(0,s.jsx)(D,{id:"analyzer-key",className:"mt-1.5 font-mono text-sm",value:n,onChange:e=>l(e.target.value),placeholder:"e.g. 72057594037927936"})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)("label",{className:"text-xs font-mono text-muted-foreground",children:"HASH STRING IDENTIFIER (FNV-1a)"}),(0,s.jsxs)("div",{className:"mt-1.5 flex gap-2",children:[(0,s.jsx)(D,{"aria-label":"Identifier to hash",value:o,onChange:e=>i(e.target.value),placeholder:"e.g. alice@example.com or user_104",className:"font-mono text-sm"}),(0,s.jsx)(_,{variant:"secondary",disabled:!o.trim(),onClick:()=>l(eP(o.trim())),children:"Hash"})]})]}),n&&!d&&(0,s.jsx)("p",{role:"alert",className:"text-xs text-destructive font-mono",children:"Invalid key: Must be an unsigned 64-bit integer (0 to 18446744073709551615)."}),c&&(0,s.jsxs)("div",{className:"space-y-3 pt-2",children:[(0,s.jsx)(eU,{keyStr:c.raw,prefixLabel:c.prefixLabel,userHash:c.userHash,itemHash:c.itemHash}),(0,s.jsxs)("div",{className:"grid grid-cols-2 gap-2 text-xs",children:[(0,s.jsxs)("div",{className:"rounded border border-border bg-muted/40 p-2.5",children:[(0,s.jsx)("span",{className:"text-[10px] font-mono text-muted-foreground",children:"HEXADECIMAL"}),(0,s.jsx)("p",{className:"mt-1 font-mono text-foreground font-medium",children:c.hex})]}),(0,s.jsxs)("div",{className:"rounded border border-border bg-muted/40 p-2.5",children:[(0,s.jsx)("span",{className:"text-[10px] font-mono text-muted-foreground",children:"PARTITION METADATA"}),(0,s.jsxs)("div",{className:"mt-1 flex items-center gap-1.5",children:[(0,s.jsx)(eX,{variant:"lime",children:c.prefixHex}),(0,s.jsx)("span",{className:"truncate text-foreground font-medium",children:c.prefixLabel})]})]})]})]})]})]})})}var rC=r(406);function rA(e){let{open:t,userHash:r,onConfirm:a,onClose:n}=e;return(0,s.jsx)(tL,{open:t,onOpenChange:e=>{e||n()},children:(0,s.jsxs)(tP,{className:"border-destructive/30 bg-card text-card-foreground",children:[(0,s.jsxs)("div",{className:"flex items-center gap-3",children:[(0,s.jsx)("div",{className:"flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-destructive/15 text-destructive",children:(0,s.jsx)(rC.A,{size:20})}),(0,s.jsxs)("div",{children:[(0,s.jsx)(tz,{className:"text-base font-bold text-foreground",children:"Cascade Delete User Account"}),(0,s.jsxs)("p",{className:"font-mono text-xs text-muted-foreground",children:["Target User Hash: ",r]})]})]}),(0,s.jsx)(t_,{className:"mt-3 text-xs leading-5 text-muted-foreground",children:"This operation purges the primary user credential record (`0x01`) and atomically seeks and deletes all associated records (profiles, tokens, relationships) across the entire keyspace."}),(0,s.jsxs)("div",{className:"mt-5 flex justify-end gap-2 border-t border-border pt-3",children:[(0,s.jsx)(tM,{asChild:!0,children:(0,s.jsx)(_,{variant:"outline",size:"sm",onClick:n,className:"text-xs",children:"Cancel"})}),(0,s.jsx)(tR,{asChild:!0,children:(0,s.jsx)(_,{variant:"destructive",size:"sm",onClick:a,className:"text-xs",children:"Purge User & Records"})})]})]})})}function rL(e){let{record:t,onSave:r,onCancel:n,onNotice:l}=e,o=(()=>{try{return JSON.stringify(JSON.parse(t.raw),null,2)}catch(e){return t.raw}})(),[i,d]=(0,a.useState)(o),[c,u]=(0,a.useState)(!1),x=async()=>{let e=i.trim();if(e.startsWith("{")||e.startsWith("["))try{JSON.parse(e)}catch(e){l("Invalid JSON payload — fix syntax errors before committing.",!0);return}u(!0);try{await r(t.keyStr,i),l("Record updated successfully.")}catch(e){l(e.message,!0)}finally{u(!1)}},m=new TextEncoder().encode(i).length;return(0,s.jsxs)("div",{className:"flex flex-col gap-4 p-5 bg-card text-card-foreground rounded-xl",children:[(0,s.jsxs)("div",{className:"flex items-center justify-between border-b border-border pb-3",children:[(0,s.jsxs)("div",{children:[(0,s.jsxs)("div",{className:"flex items-center gap-2",children:[(0,s.jsx)("h2",{className:"text-base font-bold text-foreground",children:"Edit Record"}),(0,s.jsx)(eX,{variant:"lime",children:t.prefixLabel})]}),(0,s.jsxs)("p",{className:"mt-1 font-mono text-xs text-muted-foreground break-all",children:["Key: ",t.keyStr]})]}),(0,s.jsxs)(_,{variant:"outline",size:"sm",onClick:()=>{try{d(JSON.stringify(JSON.parse(i),null,2)),l("JSON formatted.",!1)}catch(e){l("Cannot format: Value is not valid JSON.",!0)}},className:"h-7 text-xs gap-1.5",title:"Prettify JSON indentation",children:[(0,s.jsx)(eD.A,{size:13})," Prettify"]})]}),(0,s.jsx)("textarea",{className:"min-h-[320px] w-full rounded-lg border border-border bg-muted/40 p-3.5 font-mono text-xs leading-5 text-foreground focus:outline-none focus:ring-2 focus:ring-primary",value:i,onChange:e=>d(e.target.value),"aria-label":"Record payload",spellCheck:!1}),(0,s.jsxs)("div",{className:"flex items-center justify-between text-xs text-muted-foreground font-mono",children:[(0,s.jsxs)("span",{children:[i.length," characters \xb7 ",eM(m)]}),(0,s.jsx)("span",{children:"UTF-8 Slotted Page Payload"})]}),(0,s.jsxs)("div",{className:"flex justify-end gap-2 border-t border-border pt-3",children:[(0,s.jsx)(_,{variant:"outline",onClick:n,disabled:c,className:"text-xs",children:"Cancel"}),(0,s.jsxs)(_,{onClick:()=>void x(),disabled:c,className:"text-xs gap-1.5",children:[(0,s.jsx)(tj.A,{size:14})," ",c?"Writing to WAL…":"Commit Changes"]})]})]})}function rz(e){let{open:t,onClose:r,onNotice:l}=e,{tokens:o,loading:i,create:d,revoke:c}=tI(t,l),[u,m]=(0,a.useState)(!1),[f,h]=(0,a.useState)(""),[p,g]=(0,a.useState)("admin"),[b,j]=(0,a.useState)(null),[v,y]=(0,a.useState)(!1);(0,a.useEffect)(()=>{t&&(j(null),h(""))},[t]);let N=async e=>{if(e.preventDefault(),!f.trim())return;let t=await d({name:f,role:p,expiresInSeconds:0});t&&(j(t),h(""),m(!1))};return(0,s.jsx)(rb,{open:t,onOpenChange:e=>{e||r()},children:(0,s.jsxs)(ry,{className:"max-w-2xl p-6",children:[(0,s.jsxs)("div",{className:"flex flex-row items-center justify-between pb-4 border-b border-border",children:[(0,s.jsxs)("div",{className:"flex items-center gap-3",children:[(0,s.jsx)("div",{className:"p-2 rounded-lg bg-amber-500/10 border border-amber-500/20 text-amber-500",children:(0,s.jsx)(x.A,{size:18})}),(0,s.jsxs)("div",{children:[(0,s.jsx)(rj,{className:"text-base font-semibold text-foreground",children:"API Access Tokens"}),(0,s.jsx)("p",{className:"text-xs text-muted-foreground mt-0.5",children:"Persistent database bearer tokens (Partition 0x05)"})]})]}),!u&&(0,s.jsxs)(_,{size:"sm",onClick:()=>m(!0),className:"text-xs",children:[(0,s.jsx)(W.A,{size:14,className:"mr-1"})," New Token"]})]}),b&&(0,s.jsxs)("div",{className:"my-3 p-4 rounded-lg bg-primary/10 border border-primary/30 space-y-2",children:[(0,s.jsxs)("div",{className:"flex items-center justify-between",children:[(0,s.jsx)("span",{className:"text-xs font-semibold text-primary",children:"Copy Your New Token"}),(0,s.jsx)("span",{className:"text-[11px] text-muted-foreground",children:"You won't be able to see it again!"})]}),(0,s.jsxs)("div",{className:"flex items-center gap-2 bg-muted rounded px-3 py-2 border border-border",children:[(0,s.jsx)("code",{className:"text-xs font-mono text-foreground flex-1 truncate",children:b}),(0,s.jsx)(_,{size:"sm",variant:"ghost",onClick:()=>{b&&(navigator.clipboard.writeText(b),y(!0),setTimeout(()=>y(!1),2e3),l("Token copied to clipboard!"))},className:"h-7 px-2 text-primary hover:bg-primary/20",children:v?(0,s.jsx)(tj.A,{size:14}):(0,s.jsx)(eI.A,{size:14})})]})]}),u&&(0,s.jsxs)("form",{onSubmit:N,className:"my-3 p-4 rounded-lg bg-muted/60 border border-border space-y-3",children:[(0,s.jsxs)("div",{className:"flex items-center justify-between",children:[(0,s.jsx)("span",{className:"text-xs font-semibold text-foreground",children:"Generate API Token"}),(0,s.jsx)("button",{type:"button",onClick:()=>m(!1),className:"text-muted-foreground hover:text-foreground",children:(0,s.jsx)(n.A,{size:14})})]}),(0,s.jsxs)("div",{className:"grid grid-cols-3 gap-3",children:[(0,s.jsx)("input",{type:"text",placeholder:"Token name (e.g. Unfinished Backend)",value:f,onChange:e=>h(e.target.value),className:"col-span-2 rounded bg-card text-foreground px-3 py-1.5 text-xs border border-border focus:outline-none focus:ring-1 focus:ring-primary",autoFocus:!0}),(0,s.jsx)("select",{value:p,onChange:e=>g(e.target.value),className:"rounded bg-card text-foreground px-2 py-1.5 text-xs border border-border focus:outline-none focus:ring-1 focus:ring-primary",children:tE.map(e=>(0,s.jsx)("option",{value:e,children:e.charAt(0).toUpperCase()+e.slice(1)},e))})]}),(0,s.jsxs)("div",{className:"flex justify-end gap-2 pt-1",children:[(0,s.jsx)(_,{size:"sm",variant:"outline",type:"button",onClick:()=>m(!1),className:"text-xs h-7",children:"Cancel"}),(0,s.jsx)(_,{size:"sm",type:"submit",className:"text-xs h-7",children:"Create"})]})]}),(0,s.jsx)("div",{className:"mt-2 space-y-2 max-h-72 overflow-y-auto pr-1",children:i?(0,s.jsx)("p",{className:"py-6 text-center text-xs text-muted-foreground font-mono",children:"Loading active tokens..."}):0===o.length?(0,s.jsxs)("div",{className:"py-8 text-center border border-dashed border-border rounded-lg",children:[(0,s.jsx)(x.A,{size:24,className:"mx-auto text-muted-foreground mb-2"}),(0,s.jsx)("p",{className:"text-xs font-medium text-muted-foreground",children:"No API tokens configured"}),(0,s.jsx)("p",{className:"text-[11px] text-muted-foreground mt-1 max-w-sm mx-auto",children:"Generate a token to allow services like Unfinished or scripts to connect securely via gRPC."})]}):o.map(e=>(0,s.jsxs)("div",{className:"flex items-center justify-between p-3 rounded-lg bg-card border border-border hover:border-primary/40 transition",children:[(0,s.jsxs)("div",{className:"space-y-0.5",children:[(0,s.jsxs)("div",{className:"flex items-center gap-2",children:[(0,s.jsx)("span",{className:"text-xs font-semibold text-foreground",children:e.name}),(0,s.jsx)("span",{className:"text-[10px] font-mono px-1.5 py-0.2 rounded border ".concat("admin"===e.role?"bg-primary/10 text-primary border-primary/30":"bg-muted text-muted-foreground border-border"),children:e.role})]}),(0,s.jsxs)("div",{className:"flex items-center gap-2 text-[11px] font-mono text-muted-foreground",children:[(0,s.jsx)("code",{children:e.masked_token}),(0,s.jsx)("span",{children:"•"}),(0,s.jsx)("span",{children:new Date(e.created_at).toLocaleDateString()})]})]}),(0,s.jsx)(_,{size:"sm",variant:"ghost",onClick:()=>{c(e.id)},title:"Revoke Token",className:"text-muted-foreground hover:text-destructive hover:bg-destructive/10 h-7 w-7 p-0",children:(0,s.jsx)(eA.A,{size:13})})]},e.id))})]})})}function r_(){var e,t,r;let l=function(){let[e,t]=(0,a.useState)(null),[r,s]=(0,a.useState)(null),[n,l]=(0,a.useState)([]),[o,i]=(0,a.useState)(!0),[d,c]=(0,a.useState)(!1),[u,x]=(0,a.useState)(null),[m,f]=(0,a.useState)(null),[h,p]=(0,a.useState)(!1),[g,b]=(0,a.useState)("0"),[j,v]=(0,a.useState)(null),[y,N]=(0,a.useState)(null),w=(0,a.useCallback)(async function(){let e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];try{let r=await eK(e?"/api/stats?counts=true":"/api/stats");t(e=>r.partitionCounts?r:{...r,partitionCounts:null==e?void 0:e.partitionCounts}),f(null)}catch(e){t(null),f(e.message)}},[]),k=(0,a.useCallback)(async e=>{i(!0),x(null),v(e);let t=(BigInt(e)<<56n).toString(),r=((BigInt(e)+1n<<56n)-1n).toString();try{let e=await eK("/api/records?start=".concat(t,"&end=").concat(r,"&limit=100"));l(e.records.map(eJ)),p(e.hasMore),b(e.nextKeyText),N(new Date)}catch(e){x(e.message)}finally{i(!1)}},[]),S=(0,a.useCallback)(async()=>{i(!0),x(null);let[e,t,r]=await Promise.allSettled([eK("/api/records?limit=100"),eK("/api/me"),eK("/api/partitions")]);"fulfilled"===r.status&&function(e){let t={};for(let r of null!=e?e:[])t[r.prefix]={label:r.label,color:r.color,description:r.description};es=t}(r.value),"fulfilled"===e.status?(l(e.value.records.map(eJ)),p(e.value.hasMore),b(e.value.nextKeyText),N(new Date)):x(e.reason.message),"fulfilled"===t.status?s(t.value):(s(null),x(t.reason.message)),await w(!0),i(!1)},[w]);(0,a.useEffect)(()=>{S()},[S]),(0,a.useEffect)(()=>{let e=window.setInterval(()=>{w(!1)},1e4);return()=>window.clearInterval(e)},[w]);let C=async()=>{if(h&&!d){c(!0);try{let e="/api/records?start=".concat(g,"&limit=100");if(null!==j){let t=((BigInt(j)+1n<<56n)-1n).toString();e+="&end=".concat(t)}let t=await eK(e);l(e=>[...e,...t.records.map(eJ)]),p(t.hasMore),b(t.nextKeyText)}catch(e){x(e.message)}finally{c(!1)}}},A=async(e,t,r)=>{await eK("/api/key",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({key:e,value:t,createOnly:r})}),await S()},L=async e=>{await eK("/api/key?k=".concat(encodeURIComponent(e)),{method:"DELETE"}),await S()},z=async e=>{let t=await eK("/api/user?userHash=".concat(e),{method:"DELETE"});return await S(),t};return{stats:e,user:r,records:n,loading:o,loadingMore:d,error:u,statsError:m,hasMore:h,activePartition:j,updatedAt:y,refresh:S,loadMore:C,loadPartition:k,lookup:async e=>eJ(await eK("/api/key?k=".concat(encodeURIComponent(e)))),scanRange:async(e,t)=>(await eK("/api/records?start=".concat(encodeURIComponent(e),"&end=").concat(encodeURIComponent(t),"&limit=100"))).records.map(eJ),save:(e,t)=>A(e,t,!0),update:(e,t)=>A(e,t,!1),remove:L,removeUser:z,search:async(e,t)=>{let r=new URLSearchParams({q:e,limit:"50"});(null==t?void 0:t.prefix)&&r.set("prefix",String(t.prefix)),(null==t?void 0:t.start)&&r.set("start",t.start),(null==t?void 0:t.maxScan)&&r.set("maxScan",String(t.maxScan));let s=await eK("/api/search?".concat(r));return{records:s.records.map(eJ),count:s.count,scannedCount:s.scannedCount,nextKey:s.nextKey,hasMore:s.hasMore,prefix:s.prefix}}}}(),[o,i]=(0,a.useState)("explorer"),[d,c]=(0,a.useState)(null),[u,x]=(0,a.useState)(!1),[m,f]=(0,a.useState)(!1),[h,p]=(0,a.useState)(!1),[g,b]=(0,a.useState)(null),[j,v]=(0,a.useState)(null),[y,N]=(0,a.useState)(null),[w,k]=(0,a.useState)(null),S=function(e){let t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return k({message:e,error:t})};(0,a.useEffect)(()=>{if(!w)return;let e=window.setTimeout(()=>k(null),5e3);return()=>clearTimeout(e)},[w]),(0,a.useEffect)(()=>{let e=new URLSearchParams(window.location.search).get("key");e&&l.lookup(e).then(e=>{e&&c(e)}).catch(()=>{})},[]),(0,a.useEffect)(()=>{let e=new URL(window.location.href);d?e.searchParams.set("key",d.keyStr):e.searchParams.delete("key"),window.history.replaceState({},"",e.toString())},[d]);let C=async()=>{if(g)try{await l.remove(g),c(null),S("Record deleted.")}catch(e){S(e.message,!0)}finally{b(null)}},L=async()=>{if(null!==j)try{let e=await l.removeUser(j);c(null),S("User deleted: ".concat(e.deleted," associated records removed."))}catch(e){S(e.message,!0)}finally{v(null)}},z={stats:l.stats,user:l.user,onNotice:S,onRefresh:()=>void l.refresh()};return(0,s.jsxs)("div",{className:"flex min-h-dvh flex-col bg-background text-foreground md:h-dvh md:flex-row md:overflow-hidden",children:[(0,s.jsx)(A,{view:o,setView:i,user:l.user,busy:l.loading,onRefresh:()=>void l.refresh()}),(0,s.jsxs)("div",{className:"flex min-h-0 min-w-0 flex-1 flex-col md:overflow-y-auto",children:[(null==(e=l.user)?void 0:e.role)==="viewer"&&(0,s.jsx)("div",{className:"border-b border-warning/30 bg-warning/10 px-4 py-2 text-center text-sm text-warning",children:"You have view-only access — contact an admin to make changes."}),(0,s.jsxs)("main",{className:"explorer"===o?"flex min-h-0 w-full flex-1 flex-col px-4 py-4 md:px-8 md:py-6":"w-full max-w-[1400px] px-4 py-6 md:px-8 md:py-8",children:["explorer"===o&&(0,s.jsx)(eV,{records:l.records,selected:d,loading:l.loading,loadingMore:l.loadingMore,hasMore:l.hasMore,error:l.error,canWrite:(null==(t=l.user)?void 0:t.role)==="admin",onRetry:()=>void l.refresh(),onLoadMore:()=>void l.loadMore(),onSelect:c,onDelete:b,onDeleteUser:e=>v(e),onEdit:e=>N(e),onNew:()=>x(!0),onLookup:l.lookup,onSearch:l.search,onScanRange:l.scanRange,partitionCounts:null==(r=l.stats)?void 0:r.partitionCounts,onLoadPartition:l.loadPartition,onNotice:S,onOpenTokens:()=>f(!0)}),"overview"===o&&(0,s.jsx)(e4,{stats:l.stats,statsError:l.statsError,recordsError:l.error,user:l.user,loaded:l.records.length,hasMore:l.hasMore,updatedAt:l.updatedAt,onRetry:()=>void l.refresh(),onAnalyze:()=>p(!0)}),"query"===o&&(0,s.jsx)(tu,{...z}),"indexes"===o&&(0,s.jsx)(tb,{...z}),"replication"===o&&(0,s.jsx)(tN,{...z}),"backups"===o&&(0,s.jsx)(tS,{...z}),"keys"===o&&(0,s.jsx)(tV,{...z}),"metrics"===o&&(0,s.jsx)(rs,{...z}),"logs"===o&&(0,s.jsx)(rc,{...z}),"settings"===o&&(0,s.jsx)(rf,{...z})]})]}),(0,s.jsx)(rw,{open:u,onClose:()=>x(!1),onSave:l.save,onLookup:l.lookup,onNotice:S}),(0,s.jsx)(rz,{open:m,onClose:()=>f(!1),onNotice:S}),(0,s.jsx)(rS,{open:h,onClose:()=>p(!1)}),(0,s.jsx)(rA,{open:null!==j,userHash:j,onConfirm:()=>void L(),onClose:()=>v(null)}),(0,s.jsx)(rb,{open:null!==y,onOpenChange:e=>{e||N(null)},children:(0,s.jsx)(ry,{className:"max-w-2xl p-0",children:y&&(0,s.jsx)(rL,{record:y,onSave:async(e,t)=>{await l.update(e,t),N(null),c(await l.lookup(e))},onCancel:()=>N(null),onNotice:S})})}),(0,s.jsx)(tL,{open:null!==g,onOpenChange:e=>{e||b(null)},children:(0,s.jsxs)(tP,{children:[(0,s.jsx)(tz,{className:"text-lg font-semibold",children:"Delete this record?"}),(0,s.jsxs)(t_,{className:"mt-2 text-sm text-muted-foreground",children:["Key ",(0,s.jsx)("span",{className:"font-mono",children:g})," and its value will be permanently removed."]}),(0,s.jsxs)("div",{className:"mt-6 flex justify-end gap-2",children:[(0,s.jsx)(tM,{asChild:!0,children:(0,s.jsx)(_,{variant:"outline",children:"Cancel"})}),(0,s.jsx)(tR,{asChild:!0,children:(0,s.jsx)(_,{variant:"destructive",onClick:()=>void C(),children:"Delete record"})})]})]})}),w&&(0,s.jsxs)("div",{role:w.error?"alert":"status",className:"fixed bottom-5 right-5 z-[70] flex max-w-sm items-center gap-3 rounded-lg border bg-card px-4 py-3 text-sm shadow-lg ".concat(w.error?"border-destructive text-destructive":""),children:[(0,s.jsx)("span",{children:w.message}),(0,s.jsx)("button",{"aria-label":"Dismiss message",onClick:()=>k(null),children:(0,s.jsx)(n.A,{size:15})})]})]})}}},e=>{e.O(0,[695,441,255,358],()=>e(e.s=2564)),_N_E=e.O()}]); \ No newline at end of file diff --git a/internal/web/static/_next/static/chunks/app/page-d99a717af3e73bf2.js b/internal/web/static/_next/static/chunks/app/page-d99a717af3e73bf2.js new file mode 100644 index 0000000..94e5172 --- /dev/null +++ b/internal/web/static/_next/static/chunks/app/page-d99a717af3e73bf2.js @@ -0,0 +1 @@ +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[974],{3199:(e,t,r)=>{"use strict";r.r(t),r.d(t,{default:()=>rQ});var s=r(5155),a=r(2115),n=r(6615),l=r(1851),o=r(8844),i=r(109),d=r(9364),c=r(7205),u=r(3037),x=r(8288),m=r(5040),h=r(5225),f=r(3063),p=r(3381),g=r(5989),b=r(2784),j=r(8428);function v(){return(0,s.jsxs)("div",{className:"shrink-0 flex items-center",role:"img","aria-label":"BeastDB",children:[(0,s.jsx)("img",{src:"/brand/beastdb-horizontal-light.svg",alt:"BeastDB","aria-hidden":"true",className:"h-8 w-auto dark:hidden"}),(0,s.jsx)("img",{src:"/brand/beastdb-horizontal-dark.svg",alt:"BeastDB","aria-hidden":"true",className:"hidden h-8 w-auto dark:block"}),(0,s.jsx)("span",{className:"ml-2.5 rounded border border-border bg-muted/80 px-1.5 py-0.5 text-[10px] font-mono text-muted-foreground",children:"Studio"})]})}function y(e){let{role:t}=e;return"admin"===t?(0,s.jsx)("span",{className:"inline-flex items-center rounded-md border border-primary/40 bg-primary/10 px-2 py-0.5 text-[10px] font-mono font-semibold tracking-wider text-primary",children:"ADMIN"}):"viewer"===t?(0,s.jsx)("span",{className:"inline-flex items-center rounded-md border border-border bg-muted px-2 py-0.5 text-[10px] font-mono font-semibold tracking-wider text-muted-foreground",children:"VIEWER"}):null}function N(){let[e,t]=(0,a.useState)(!0);return(0,a.useEffect)(()=>{t(document.documentElement.classList.contains("dark"));let e=matchMedia("(prefers-color-scheme: dark)"),r=()=>{let r=localStorage.getItem("beastdb-theme"),s=r?"dark"===r:e.matches;document.documentElement.classList.toggle("dark",s),t(s)};return e.addEventListener("change",r),()=>e.removeEventListener("change",r)},[]),{dark:e,toggle:()=>{let e=!document.documentElement.classList.contains("dark");document.documentElement.classList.toggle("dark",e),localStorage.setItem("beastdb-theme",e?"dark":"light"),t(e)}}}var w=r(2821),k=r(5889);function S(){for(var e=arguments.length,t=Array(e),r=0;r{let{id:a,label:n,icon:l}=e;return(0,s.jsxs)("button",{"aria-current":t===a?"page":void 0,onClick:()=>r(a),className:S("flex w-full shrink-0 items-center gap-2.5 rounded-lg px-2.5 py-2 text-sm transition-colors",t===a?"bg-primary/10 font-medium text-primary dark:bg-[#1e2b0a]":"text-muted-foreground hover:bg-muted hover:text-foreground"),children:[(0,s.jsx)(l,{size:16}),n]},a)})}),(0,s.jsxs)("div",{className:"hidden items-center gap-2 border-t border-border p-3 md:flex",children:[(0,s.jsx)("div",{className:"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-beast-lime text-[11px] font-bold text-black","aria-hidden":"true",children:((null==a?void 0:a.username)||"??").slice(0,2).toUpperCase()}),(0,s.jsxs)("div",{className:"min-w-0 flex-1",children:[(0,s.jsx)("p",{className:"truncate font-mono text-xs",title:(null==a?void 0:a.username)||"Session unavailable",children:(null==a?void 0:a.username)||"Account"}),(0,s.jsx)(y,{role:null==a?void 0:a.role})]}),(0,s.jsx)(A,{label:"Refresh data",onClick:l,disabled:n,children:(0,s.jsx)(p.A,{size:15,className:n?"animate-spin":""})}),(0,s.jsx)(A,{label:o.dark?"Use light theme":"Use dark theme",onClick:o.toggle,children:o.dark?(0,s.jsx)(g.A,{size:15}):(0,s.jsx)(b.A,{size:15})}),(0,s.jsx)("form",{action:"/logout",method:"POST",children:(0,s.jsx)(A,{label:"Sign out",type:"submit",children:(0,s.jsx)(j.A,{size:15})})})]})]})}function A(e){let{label:t,children:r,...a}=e;return(0,s.jsx)("button",{"aria-label":t,title:t,className:"inline-flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:opacity-50",...a,children:r})}let z=(0,r(3101).F)("inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",{variants:{variant:{default:"bg-beast-lime text-primary-foreground font-semibold hover:bg-beast-lime-hover",secondary:"border border-border bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",outline:"border border-border bg-card shadow-sm hover:bg-muted hover:text-foreground",ghost:"hover:bg-muted hover:text-foreground",destructive:"border border-destructive/30 bg-destructive/15 text-destructive hover:bg-destructive/25"},size:{default:"h-9 px-4 py-2",sm:"h-8 px-3 text-xs",icon:"h-9 w-9"}},defaultVariants:{variant:"default",size:"default"}}),_=a.forwardRef((e,t)=>{let{className:r,variant:a,size:n,...l}=e;return(0,s.jsx)("button",{ref:t,className:S(z({variant:a,size:n}),r),...l})});_.displayName="Button";var P=r(3263);function M(e){let{partitionLabel:t,recordLabel:r,onBack:a}=e;return(0,s.jsxs)("nav",{"aria-label":"Breadcrumb",className:"flex items-center gap-1.5 text-xs text-muted-foreground",children:[(0,s.jsxs)("div",{className:"flex items-center gap-1",children:[(0,s.jsx)(o.A,{size:13,className:"text-primary"}),(0,s.jsx)("span",{className:"font-mono font-medium text-foreground",children:"BeastDB"})]}),(0,s.jsx)(P.A,{size:12,className:"text-muted-foreground/60"}),(0,s.jsx)("button",{type:"button",onClick:a,className:"font-medium text-muted-foreground hover:text-foreground transition",children:t}),r&&(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(P.A,{size:12,className:"text-muted-foreground/60"}),(0,s.jsx)("span",{className:"truncate font-mono font-medium text-foreground max-w-[200px]",children:r})]})]})}var R=r(1286),E=r(4381),T=r(6916),I=r(3173);let D=a.forwardRef((e,t)=>{let{className:r,...a}=e;return(0,s.jsx)("input",{ref:t,className:S("flex h-9 w-full rounded-md border border-input bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50",r),...a})});function B(e){let{mode:t,onModeChange:r,partitions:n,activePartition:l,onSearchText:o,onLookupKey:d,onScanRange:c,busy:u}=e,[x,m]=(0,a.useState)(""),[h,f]=(0,a.useState)(l),[p,g]=(0,a.useState)(""),[b,j]=(0,a.useState)(""),[v,y]=(0,a.useState)("");return(0,a.useEffect)(()=>{f(l)},[l]),(0,s.jsxs)("div",{className:"shrink-0 rounded-xl border border-border bg-card p-3 shadow-sm",children:[(0,s.jsxs)("div",{className:"flex flex-wrap items-center justify-between gap-3 border-b border-border/60 pb-2.5",children:[(0,s.jsxs)("div",{className:"flex items-center gap-1 rounded-lg border border-border bg-muted/60 p-0.5 text-xs",children:[(0,s.jsxs)("button",{type:"button",onClick:()=>r("text"),className:"flex items-center gap-1.5 rounded-md px-3 py-1 font-medium transition ".concat("text"===t?"bg-primary/10 text-primary shadow-sm":"text-muted-foreground hover:text-foreground"),children:[(0,s.jsx)(R.A,{size:13})," Value Text"]}),(0,s.jsxs)("button",{type:"button",onClick:()=>r("exact"),className:"flex items-center gap-1.5 rounded-md px-3 py-1 font-medium transition ".concat("exact"===t?"bg-primary/10 text-primary shadow-sm":"text-muted-foreground hover:text-foreground"),children:[(0,s.jsx)(E.A,{size:13})," Exact Key"]}),(0,s.jsxs)("button",{type:"button",onClick:()=>r("range"),className:"flex items-center gap-1.5 rounded-md px-3 py-1 font-medium transition ".concat("range"===t?"bg-primary/10 text-primary shadow-sm":"text-muted-foreground hover:text-foreground"),children:[(0,s.jsx)(T.A,{size:13})," Key Range"]})]}),(0,s.jsxs)("span",{className:"text-[11px] font-mono text-muted-foreground",children:["text"===t&&"Scans values inside B+ Tree slotted pages","exact"===t&&"O(log N) point lookup in B+ Tree index","range"===t&&"Sequential leaf-node range traversal"]})]}),(0,s.jsxs)("div",{className:"pt-2.5",children:["text"===t&&(0,s.jsxs)("form",{onSubmit:e=>{if(e.preventDefault(),!x.trim()||u)return;let t="all"!==h?Number(h):void 0;o(x.trim(),t)},className:"flex flex-wrap items-center gap-2",children:[(0,s.jsxs)("div",{className:"relative min-w-0 flex-1",children:[(0,s.jsx)(i.A,{size:15,className:"absolute left-3 top-2.5 text-muted-foreground"}),(0,s.jsx)(D,{value:x,onChange:e=>m(e.target.value),placeholder:"Search record content across database (min 2 chars)...",className:"pl-9 text-xs"})]}),(0,s.jsxs)("select",{"aria-label":"Filter search partition",value:h,onChange:e=>f(e.target.value),className:"h-9 rounded-md border border-border bg-card text-foreground px-3 text-xs",children:[(0,s.jsx)("option",{value:"all",children:"Entire Keyspace (All Partitions)"}),n.map(e=>(0,s.jsx)("option",{value:e.prefix,children:e.prefixLabel},e.prefix))]}),(0,s.jsxs)(_,{type:"submit",disabled:x.trim().length<2||u,size:"sm",className:"gap-1.5 text-xs",children:[(0,s.jsx)(i.A,{size:14})," ",u?"Scanning...":"Search Database"]})]}),"exact"===t&&(0,s.jsxs)("form",{onSubmit:e=>{e.preventDefault(),p.trim()&&!u&&d(p.trim())},className:"flex flex-wrap items-center gap-2",children:[(0,s.jsxs)("div",{className:"relative min-w-0 flex-1",children:[(0,s.jsx)(E.A,{size:15,className:"absolute left-3 top-2.5 text-muted-foreground"}),(0,s.jsx)(D,{value:p,onChange:e=>g(e.target.value),placeholder:"Enter 64-bit decimal key (e.g. 72057594037927937)...",className:"pl-9 font-mono text-xs"})]}),(0,s.jsxs)(_,{type:"submit",disabled:!p.trim()||u,size:"sm",className:"gap-1.5 text-xs",children:[(0,s.jsx)(I.A,{size:14})," ",u?"Looking up...":"Find Key"]})]}),"range"===t&&(0,s.jsxs)("form",{onSubmit:e=>{e.preventDefault(),b.trim()&&v.trim()&&!u&&c(b.trim(),v.trim())},className:"flex flex-wrap items-center gap-2",children:[(0,s.jsx)(D,{value:b,onChange:e=>j(e.target.value),placeholder:"Start Key (decimal)...",className:"min-w-0 flex-1 font-mono text-xs"}),(0,s.jsx)("span",{className:"text-xs text-muted-foreground font-mono",children:"to"}),(0,s.jsx)(D,{value:v,onChange:e=>y(e.target.value),placeholder:"End Key (decimal)...",className:"min-w-0 flex-1 font-mono text-xs"}),(0,s.jsxs)(_,{type:"submit",disabled:!b.trim()||!v.trim()||u,size:"sm",className:"gap-1.5 text-xs",children:[(0,s.jsx)(T.A,{size:14})," ",u?"Scanning...":"Scan Range"]})]})]})]})}D.displayName="Input";var O=r(984);function U(e){let{query:t,count:r,scannedCount:a,hasMore:l,prefix:o,prefixLabel:i,loadingMore:d,onContinueScan:c,onClear:u}=e,x=void 0!==o?"Partition 0x".concat(o.toString(16).padStart(2,"0").toUpperCase()).concat(i?" (".concat(i,")"):""):"Entire Keyspace";return(0,s.jsxs)("div",{className:"flex flex-wrap items-center justify-between gap-3 rounded-lg border border-primary/30 bg-primary/5 px-4 py-2.5 text-xs text-foreground",children:[(0,s.jsxs)("div",{className:"flex items-center gap-2",children:[(0,s.jsx)(O.A,{size:16,className:"text-primary shrink-0"}),(0,s.jsxs)("div",{children:[(0,s.jsxs)("span",{className:"font-semibold text-foreground",children:[r," ",1===r?"match":"matches"," found"]}),(0,s.jsxs)("span",{className:"text-muted-foreground",children:[" for “",t,"” \xb7 "]}),(0,s.jsxs)("span",{className:"font-mono text-foreground",children:[a.toLocaleString()," keys inspected in ",x]}),l&&(0,s.jsx)("span",{className:"ml-1 text-amber-600 dark:text-amber-400",children:"(More uninspected keys remain in database)"})]})]}),(0,s.jsxs)("div",{className:"flex items-center gap-2",children:[l&&c&&(0,s.jsxs)(_,{size:"sm",variant:"outline",onClick:c,disabled:d,className:"h-7 text-xs gap-1 border-primary/40 text-primary hover:bg-primary/10",children:[d?(0,s.jsx)(p.A,{size:12,className:"animate-spin"}):(0,s.jsx)(P.A,{size:14}),"Scan Next Chunk"]}),(0,s.jsxs)(_,{size:"sm",variant:"ghost",onClick:u,className:"h-7 text-xs gap-1 text-muted-foreground hover:text-foreground",children:[(0,s.jsx)(n.A,{size:13}),"Clear Results"]})]})]})}var F=r(8971),H=r(8822),W=r(3527),K=r(2265);function q(e){let{records:t,activeStatus:r,onSelectStatus:n}=e,l=(0,a.useMemo)(()=>{let e=new Map;for(let s of t)if(s.status&&"string"==typeof s.status){var r;let t=s.status.trim().toLowerCase();e.set(t,(null!=(r=e.get(t))?r:0)+1)}return[...e.entries()].sort((e,t)=>t[1]-e[1])},[t]);return 0===l.length?null:(0,s.jsxs)("div",{className:"flex items-center gap-1.5 overflow-x-auto pb-1 text-xs",children:[(0,s.jsxs)("button",{type:"button",onClick:()=>n("all"),className:"flex items-center gap-1.5 rounded-full px-3 py-1 font-medium transition ".concat("all"===r?"bg-primary text-primary-foreground shadow-sm":"bg-muted/80 text-muted-foreground hover:bg-muted hover:text-foreground"),children:[(0,s.jsx)("span",{children:"All"}),(0,s.jsx)("span",{className:"font-mono text-[11px] opacity-80",children:t.length})]}),l.map(e=>{let[t,a]=e,l=r===t;return(0,s.jsxs)("button",{type:"button",onClick:()=>n(l?"all":t),className:"flex items-center gap-1.5 rounded-full px-3 py-1 font-medium capitalize transition ".concat(l?"bg-primary text-primary-foreground shadow-sm":"bg-muted/80 text-muted-foreground hover:bg-muted hover:text-foreground"),children:[(0,s.jsx)("span",{children:t}),(0,s.jsx)("span",{className:"font-mono text-[11px] opacity-80",children:a})]},t)})]})}function V(e){let{records:t,activeStatus:r,onSelectStatus:a,viewMode:n,onViewModeChange:l,canWrite:o,onNew:i}=e;return(0,s.jsxs)("div",{className:"flex flex-wrap items-center justify-between gap-3",children:[(0,s.jsx)(q,{records:t,activeStatus:r,onSelectStatus:a}),(0,s.jsxs)("div",{className:"flex items-center gap-2 ml-auto",children:[(0,s.jsxs)("div",{className:"flex items-center rounded-lg border border-border bg-muted/60 p-0.5 text-xs",children:[(0,s.jsxs)("button",{type:"button",onClick:()=>l("table"),title:"Spreadsheet Table View",className:"flex items-center gap-1.5 rounded-md px-2.5 py-1 font-medium transition ".concat("table"===n?"bg-card text-foreground shadow-sm":"text-muted-foreground hover:text-foreground"),children:[(0,s.jsx)(F.A,{size:13}),(0,s.jsx)("span",{className:"hidden sm:inline",children:"Table"})]}),(0,s.jsxs)("button",{type:"button",onClick:()=>l("grid"),title:"Poster / Card Gallery View",className:"flex items-center gap-1.5 rounded-md px-2.5 py-1 font-medium transition ".concat("grid"===n?"bg-card text-foreground shadow-sm":"text-muted-foreground hover:text-foreground"),children:[(0,s.jsx)(H.A,{size:13}),(0,s.jsx)("span",{className:"hidden sm:inline",children:"Gallery"})]}),(0,s.jsxs)("button",{type:"button",onClick:()=>l("split"),title:"Classic 3-Pane Split View",className:"flex items-center gap-1.5 rounded-md px-2.5 py-1 font-medium transition ".concat("split"===n?"bg-card text-foreground shadow-sm":"text-muted-foreground hover:text-foreground"),children:[(0,s.jsx)(W.A,{size:13}),(0,s.jsx)("span",{className:"hidden sm:inline",children:"Split"})]})]}),o&&(0,s.jsxs)(_,{size:"sm",onClick:i,className:"h-7 gap-1 text-xs shadow-sm",children:[(0,s.jsx)(K.A,{size:13})," New Record"]})]})]})}var J=r(6881);let $=["emerald","purple","cyan","blue","indigo","rose","amber","teal","violet","sky","orange"],G=new Set(["user","owner","sender","receiver","actor","target","friend","parent","createdby","updatedby"]),Y=new Set(["data","payload","metadata","details","attributes","fields"]),Z=/^[^\s@]+@[^\s@]+\.[^\s@]+$/,Q=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$|^[0-9a-f]{32,64}$/i;function X(e){return $[e%$.length]}function ee(e){var t;let r=e.replace(/([a-z])([A-Z])/g,"$1 $2").replace(/[_-]/g," ").trim().split(/\s+/).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1).toLowerCase());return 0===r.length?"":[...r.slice(0,-1),(t=r[r.length-1],/^media$/i.test(t)||/s$/i.test(t)?t:/(sh|ch|x|z)$/i.test(t)?"".concat(t,"es"):/[^aeiou]y$/i.test(t)?"".concat(t.slice(0,-1),"ies"):"".concat(t,"s"))].join(" ")}function et(e){let t=new Map,r=0;for(let n of e){var s,a;if("empty"===n.format||!(null==(s=n.raw)?void 0:s.trim()))continue;r++;let e=function(e){if(e.fields)return function(e){var t,r,s,a,n;let l=null!=(a=null!=(s=null!=(r=null!=(t=e.collection)?t:e.entity)?r:e.model)?s:e.table)?a:e._type;if("string"==typeof l&&l.trim())return{label:ee(l),confidence:5};for(let[t,r]of Object.entries(e))if(r&&"object"==typeof r&&!Array.isArray(r)&&!Y.has(t.toLowerCase())&&!G.has(t.toLowerCase())&&("string"==typeof r.title||"string"==typeof r.name||null!=r.id))return{label:ee(t),confidence:4};let o=Object.keys(e);if(null!=e.sku&&null!=e.price)return{label:"Products",confidence:4};if(null!=e.sensorId&&(null!=e.temperature||null!=e.humidity))return{label:"Telemetry",confidence:4};if(null!=e.email&&(null!=e.username||null!=e.passwordHash||null!=e.password_hash))return{label:"Users",confidence:4};if(null!=e.username&&null!=e.role&&null==e.email)return{label:"System Users",confidence:4};if(null!=e.token||null!=e.tokenHash||null!=e.token_hash||null!=e.refreshToken||null!=e.refresh_token)return{label:"Tokens",confidence:4};let i=o.map(e=>{var t;return null==(t=e.match(/^(.+?)(?:Id|_id)$/i))?void 0:t[1]}).filter(e=>!!e).filter(e=>!G.has(e.toLowerCase())&&"item"!==e.toLowerCase());if(1===i.length)return{label:ee(i[0]),confidence:3};let d=null!=(n=e.type)?n:e.kind;if("string"==typeof d&&d.trim())return{label:ee(d),confidence:2}}(e.fields);if("token"===e.format||"string"===e.format&&e.raw.includes("|")&&e.raw.length<250)return{label:"Tokens",confidence:3};if("string"===e.format){let t=e.raw.trim();if(Z.test(t))return{label:"User Index",confidence:3};if(Q.test(t))return{label:"Index Pointers",confidence:2}}}(n);if(!e)continue;let l=null!=(a=t.get(e.label))?a:{score:0,count:0,confidence:0};t.set(e.label,{score:l.score+e.confidence,count:l.count+1,confidence:Math.max(l.confidence,e.confidence)})}let n=[...t].sort((e,t)=>t[1].score-e[1].score)[0];if(n&&(!(n[1].confidence<=2)||n[1].count===r))return n[0]}function er(e,t){var r;return{label:null!=(r=et(e))?r:"Collection ".concat(t.toString(16).padStart(2,"0").toUpperCase()),color:X(t)}}let es={},ea={};function en(e){var t,r;return null!=(r=null!=(t=es[e])?t:ea[e])?r:{label:"Collection ".concat(e.toString(16).padStart(2,"0").toUpperCase()),color:X(e)}}function el(){return Object.entries(es).map(e=>{let[t,r]=e;return{prefix:Number(t),label:r.label,color:r.color,description:r.description}}).sort((e,t)=>e.prefix-t.prefix)}function eo(e){try{let t=BigInt(e),r=Number(t>>56n&255n),s=Number(t>>28n&268435455n),a=Number(268435455n&t),n="0x"+t.toString(16).padStart(16,"0"),l="0x"+r.toString(16).padStart(2,"0").toUpperCase(),o=en(r);return{raw:String(e),hex:n,prefix:r,prefixHex:l,prefixLabel:o.label,color:o.color,userHash:s,itemHash:a}}catch(t){return{raw:String(e),hex:"0x0",prefix:0,prefixHex:"0x00",prefixLabel:"Generic Key",color:"slate",userHash:0,itemHash:0}}}let ei={emerald:"border-emerald-500",teal:"border-teal-500",purple:"border-purple-500",cyan:"border-cyan-500",amber:"border-amber-500",indigo:"border-indigo-500",rose:"border-rose-500",blue:"border-blue-500",violet:"border-violet-500",sky:"border-sky-500",fuchsia:"border-fuchsia-500",orange:"border-orange-500",slate:"border-slate-400"};function ed(e){let{partitions:t,selected:r,total:a,hasMore:n,hasFullCounts:l,onSelect:o}=e;return(0,s.jsxs)("aside",{"aria-label":"Collections",className:"flex min-h-0 flex-col border-r border-border",children:[(0,s.jsxs)("div",{className:"border-b border-border px-5 py-5",children:[(0,s.jsx)("h2",{className:"text-base font-semibold",children:"Collections"}),(0,s.jsxs)("p",{className:"mt-1 text-xs text-muted-foreground",children:[a," records"]})]}),(0,s.jsx)("nav",{"aria-label":"Collection list",className:"min-h-0 flex-1 space-y-1 overflow-y-auto p-2",children:t.map(e=>{var t;let{prefix:a,count:n,prefixLabel:l}=e,i=null!=(t=ei[en(a).color])?t:"border-muted-foreground",d=r===String(a);return(0,s.jsxs)("button",{type:"button",onClick:()=>o(String(a)),"aria-current":d?"page":void 0,className:"flex w-full items-center gap-2.5 rounded-md border-l-4 px-3 py-2.5 text-left text-sm transition ".concat(d?"".concat(i," bg-primary/10 text-foreground font-medium shadow-sm"):"border-transparent text-muted-foreground hover:text-foreground hover:bg-muted"),children:[(0,s.jsx)(J.A,{size:15,className:"shrink-0 ".concat(d?"text-primary":"text-muted-foreground")}),(0,s.jsx)("span",{className:"min-w-0 flex-1 truncate",title:l,children:l}),(0,s.jsx)("span",{className:"text-xs font-mono tabular-nums text-muted-foreground",children:n})]},a)})}),!l&&n&&(0,s.jsx)("p",{className:"border-t border-border px-4 py-2 text-xs text-muted-foreground",children:"More records may be available."})]})}var ec=r(6204);let eu={json_object:"Document",json_array:"List",token:"Delimited text",string:"Text",binary:"Binary data",empty:"Empty record"};function ex(e){return e.replace(/([a-z\d])([A-Z])/g,"$1 $2").replace(/[_-]+/g," ").replace(/\s+/g," ").trim().replace(/^./,e=>e.toUpperCase()).replace(/\bId\b/g,"ID")}function em(e){var t,r;if(e.secondaryLabel)return e.secondaryLabel;if(e.fields){let t=e.attributes.filter(e=>!["name","title","displayName","username","id"].includes(e.key)).slice(0,2).map(e=>"".concat(ex(e.key),": ").concat(e.value)).join(" \xb7 ");if(t)return t}return"json_array"===e.format?"".concat(null!=(r=null==(t=e.arrayItems)?void 0:t.length)?r:0," items"):"empty"===e.format?"No value":eu[e.format]}function eh(e){return(0,s.jsxs)("div",{"aria-label":"Records",className:"flex min-h-0 flex-col border-r border-border",children:[(0,s.jsxs)("div",{className:"flex items-center justify-between gap-2 border-b border-border px-4 py-3",children:[(0,s.jsxs)("div",{className:"flex min-w-0 items-center gap-2",children:[(0,s.jsx)(_,{variant:"ghost",size:"icon",className:"lg:hidden","aria-label":"Back to collections",onClick:e.onBack,children:(0,s.jsx)(ec.A,{size:16})}),(0,s.jsx)("h2",{className:"truncate text-sm font-semibold text-foreground",title:e.title,children:e.title})]}),e.canWrite&&(0,s.jsxs)(_,{size:"sm",onClick:e.onNew,className:"h-7 shrink-0 gap-1 text-xs",children:[(0,s.jsx)(K.A,{size:13})," New"]})]}),(0,s.jsx)("div",{className:"min-h-0 flex-1 overflow-y-auto",children:e.loading&&!e.records.length?(0,s.jsx)("p",{role:"status",className:"p-8 text-center text-sm text-muted-foreground",children:"Loading records…"}):e.records.length?(0,s.jsx)("div",{className:"divide-y divide-border/60",children:e.records.map(t=>{var r;let a=(null==(r=e.selected)?void 0:r.keyStr)===t.keyStr;return(0,s.jsxs)("button",{type:"button",onClick:()=>e.onSelect(t),"aria-current":a?"true":void 0,className:"w-full border-l-2 px-4 py-3 text-left transition ".concat(a?"border-primary bg-primary/10":"border-transparent hover:bg-muted"),children:[(0,s.jsx)("span",{className:"block truncate text-sm font-medium ".concat(a?"text-primary":"text-foreground"),title:t.primaryLabel,children:t.primaryLabel}),(0,s.jsx)("span",{className:"mt-1 block truncate text-xs text-muted-foreground",title:em(t),children:em(t)})]},t.keyStr)})}):(0,s.jsxs)("div",{className:"p-8 text-center",children:[(0,s.jsx)(h.A,{size:22,className:"mx-auto text-muted-foreground"}),(0,s.jsx)("p",{className:"mt-3 text-sm font-medium text-foreground",children:"No records here"}),(0,s.jsx)("p",{className:"mt-1 text-xs text-muted-foreground",children:"Choose another collection or create a record."})]})}),(0,s.jsxs)("div",{className:"flex items-center justify-between gap-2 border-t border-border px-4 py-2.5 text-xs text-muted-foreground",children:[(0,s.jsxs)("span",{children:[e.shownCount," shown",e.hasMore?" \xb7 more available":""]}),e.hasMore&&(0,s.jsx)(_,{variant:"outline",size:"sm",disabled:e.loadingMore,onClick:e.onLoadMore,className:"h-7 text-xs",children:e.loadingMore?"Loading…":"Load more"})]})]})}var ef=r(2649);function ep(e){let t,{keyStr:r,className:a}=e;try{t=BigInt(r).toString(2).padStart(64,"0")}catch(e){return null}return(0,s.jsx)("svg",{width:127,height:7,viewBox:"0 0 ".concat(127," ").concat(7),className:a,role:"img","aria-label":"64-bit key: 8 partition bits, 28 domain bits, 28 item bits",children:Array.from(t).map((e,t)=>{let r="1"===e;return(0,s.jsx)("rect",{x:t%32*4,y:4*Math.floor(t/32),width:3,height:3,rx:.5,className:t<8?r?"fill-primary":"fill-primary/20":r?t<36?"fill-foreground/60":"fill-foreground/35":"fill-border"},t)})})}var eg=r(1190);function eb(e){let{page:t,pageSize:r,totalShown:a,hasMore:n,loadingMore:l,onPageChange:o,onLoadMore:i}=e,d=Math.max(1,Math.ceil(a/r)),c=Math.min(Math.max(1,t),d),u=0===a?0:(c-1)*r+1,x=Math.min(c*r,a);return(0,s.jsxs)("nav",{"aria-label":"Pagination Navigation",className:"flex flex-wrap items-center justify-between gap-3 border-t border-border bg-card/60 px-4 py-2.5 text-xs text-muted-foreground backdrop-blur-sm",children:[(0,s.jsxs)("div",{className:"flex items-center gap-2",children:[(0,s.jsxs)("span",{className:"font-medium text-foreground",children:["Showing ",u,"–",x," of ",a," loaded"]}),(0,s.jsx)("span",{className:"text-[11px] text-muted-foreground/80",children:n?"(more available in partition)":"(all loaded)"})]}),(0,s.jsxs)("div",{className:"flex items-center gap-1.5",children:[n&&i&&c===d&&(0,s.jsx)(_,{variant:"outline",size:"sm",disabled:l,onClick:i,className:"mr-2 h-7 px-2.5 text-xs font-medium","aria-label":"Fetch next page chunk from database engine",children:l?"Loading…":"Fetch more records"}),(0,s.jsx)(_,{variant:"outline",size:"icon",disabled:c<=1,onClick:()=>o(c-1),className:"h-7 w-7","aria-label":"Previous page",children:(0,s.jsx)(eg.A,{size:14})}),(0,s.jsxs)("span",{className:"px-2 text-xs font-mono font-medium text-foreground","aria-current":"page",children:[c," / ",d]}),(0,s.jsx)(_,{variant:"outline",size:"icon",disabled:c>=d,onClick:()=>o(c+1),className:"h-7 w-7","aria-label":"Next page",children:(0,s.jsx)(P.A,{size:14})})]})]})}let ej={completed:"bg-emerald-500/15 text-emerald-600 dark:text-emerald-400 border-emerald-500/30",finished:"bg-emerald-500/15 text-emerald-600 dark:text-emerald-400 border-emerald-500/30",playing:"bg-purple-500/15 text-purple-600 dark:text-purple-400 border-purple-500/30",watching:"bg-cyan-500/15 text-cyan-600 dark:text-cyan-400 border-cyan-500/30",reading:"bg-rose-500/15 text-rose-600 dark:text-rose-400 border-rose-500/30",backlog:"bg-amber-500/15 text-amber-600 dark:text-amber-400 border-amber-500/30",paused:"bg-amber-500/15 text-amber-600 dark:text-amber-400 border-amber-500/30",dropped:"bg-red-500/15 text-red-600 dark:text-red-400 border-red-500/30",active:"bg-blue-500/15 text-blue-600 dark:text-blue-400 border-blue-500/30",admin:"bg-lime-500/15 text-lime-600 dark:text-lime-400 border-lime-500/30"};function ev(e){let{records:t,selectedKey:r,hasMore:n,loadingMore:l,onLoadMore:o,onSelect:i}=e,[d,c]=(0,a.useState)(1),u=(0,a.useMemo)(()=>{let e=(d-1)*25;return t.slice(e,e+25)},[t,d]);return 0===t.length?(0,s.jsxs)("div",{className:"flex flex-1 flex-col items-center justify-center p-12 text-center",children:[(0,s.jsx)(h.A,{size:28,className:"text-muted-foreground/60"}),(0,s.jsx)("p",{className:"mt-3 text-sm font-medium text-foreground",children:"No records match filter"}),(0,s.jsx)("p",{className:"mt-1 text-xs text-muted-foreground",children:"Select another filter or add records."})]}):(0,s.jsxs)("div",{className:"flex min-h-0 flex-1 flex-col overflow-hidden border-t border-border",children:[(0,s.jsx)("div",{className:"min-h-0 flex-1 overflow-auto",children:(0,s.jsxs)("table",{className:"w-full border-collapse text-left text-xs",children:[(0,s.jsx)("thead",{className:"sticky top-0 z-10 border-b border-border bg-muted/90 backdrop-blur-sm",children:(0,s.jsxs)("tr",{children:[(0,s.jsx)("th",{className:"w-12 px-3 py-2.5 font-medium text-muted-foreground",children:"Cover"}),(0,s.jsx)("th",{className:"min-w-[200px] px-3 py-2.5 font-medium text-muted-foreground",children:"Title / Identity"}),(0,s.jsx)("th",{className:"w-28 px-3 py-2.5 font-medium text-muted-foreground",children:"Status"}),(0,s.jsx)("th",{className:"w-20 px-3 py-2.5 font-medium text-muted-foreground",children:"Rating"}),(0,s.jsx)("th",{className:"px-3 py-2.5 font-medium text-muted-foreground",children:"Attributes"}),(0,s.jsx)("th",{className:"w-40 px-3 py-2.5 text-right font-medium text-muted-foreground",children:"Key"})]})}),(0,s.jsx)("tbody",{className:"divide-y divide-border/60",children:u.map(e=>{var t,a;let n=r===e.keyStr,l=null!=(a=ej[String(null!=(t=e.status)?t:"").toLowerCase()])?a:"bg-muted text-muted-foreground border-border";return(0,s.jsxs)("tr",{onClick:()=>i(e),className:"cursor-pointer transition hover:bg-muted/60 ".concat(n?"bg-primary/10 font-medium":""),children:[(0,s.jsx)("td",{className:"px-3 py-2",children:e.coverUrl?(0,s.jsx)("img",{src:e.coverUrl,alt:"",className:"h-9 w-9 shrink-0 rounded object-cover border border-border"}):(0,s.jsx)("div",{className:"flex h-9 w-9 items-center justify-center rounded bg-muted text-muted-foreground",children:(0,s.jsx)(h.A,{size:16})})}),(0,s.jsxs)("td",{className:"px-3 py-2",children:[(0,s.jsx)("p",{className:"truncate font-semibold text-foreground",children:e.primaryLabel}),e.secondaryLabel&&(0,s.jsx)("p",{className:"truncate text-[11px] text-muted-foreground",children:e.secondaryLabel})]}),(0,s.jsx)("td",{className:"px-3 py-2",children:e.status?(0,s.jsx)("span",{className:"inline-flex items-center rounded-full border px-2 py-0.5 text-[10px] font-medium capitalize ".concat(l),children:e.status}):(0,s.jsx)("span",{className:"text-muted-foreground/60",children:"—"})}),(0,s.jsx)("td",{className:"px-3 py-2",children:null!=e.rating?(0,s.jsxs)("span",{className:"inline-flex items-center gap-1 font-mono text-xs font-semibold text-amber-500",children:[(0,s.jsx)(ef.A,{size:11,className:"fill-amber-400"}),String(e.rating)]}):(0,s.jsx)("span",{className:"text-muted-foreground/60",children:"—"})}),(0,s.jsx)("td",{className:"px-3 py-2",children:(0,s.jsx)("div",{className:"flex flex-wrap gap-1",children:e.attributes.slice(0,3).map(e=>(0,s.jsxs)("span",{className:"rounded bg-muted/80 px-1.5 py-0.5 text-[10px] text-muted-foreground font-mono",children:[(0,s.jsxs)("span",{className:"opacity-70",children:[e.key.split(".").pop(),":"]})," ",e.value]},e.key))})}),(0,s.jsxs)("td",{className:"px-3 py-2 text-right font-mono text-[11px] text-muted-foreground",children:[(0,s.jsx)(ep,{keyStr:e.keyStr,className:"ml-auto mb-1"}),function(e){let t=String(e);return t.length>14?t.slice(0,5)+"..."+t.slice(-4):t}(e.keyStr)]})]},e.keyStr)})})]})}),(0,s.jsx)(eb,{page:d,pageSize:25,totalShown:t.length,hasMore:n,loadingMore:l,onPageChange:c,onLoadMore:o})]})}let ey={completed:"bg-emerald-600 text-white",finished:"bg-emerald-600 text-white",playing:"bg-purple-600 text-white",watching:"bg-cyan-600 text-white",reading:"bg-rose-600 text-white",backlog:"bg-amber-600 text-white",active:"bg-blue-600 text-white"};function eN(e){let{records:t,selectedKey:r,hasMore:n,loadingMore:l,onLoadMore:o,onSelect:i}=e,[d,c]=(0,a.useState)(1),u=(0,a.useMemo)(()=>{let e=(d-1)*24;return t.slice(e,e+24)},[t,d]);return 0===t.length?(0,s.jsxs)("div",{className:"flex flex-1 flex-col items-center justify-center p-12 text-center",children:[(0,s.jsx)(h.A,{size:28,className:"text-muted-foreground/60"}),(0,s.jsx)("p",{className:"mt-3 text-sm font-medium text-foreground",children:"No records match filter"}),(0,s.jsx)("p",{className:"mt-1 text-xs text-muted-foreground",children:"Select another filter or add records."})]}):(0,s.jsxs)("div",{className:"flex min-h-0 flex-1 flex-col overflow-hidden border-t border-border",children:[(0,s.jsx)("div",{className:"min-h-0 flex-1 overflow-y-auto p-4",children:(0,s.jsx)("div",{className:"grid grid-cols-2 gap-3 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6",children:u.map(e=>{var t,a;let n=r===e.keyStr,l=null!=(a=ey[String(null!=(t=e.status)?t:"").toLowerCase()])?a:"bg-zinc-800 text-zinc-200";return(0,s.jsxs)("button",{type:"button",onClick:()=>i(e),className:"group flex flex-col overflow-hidden rounded-xl border text-left transition duration-150 hover:shadow-md hover:border-primary/50 ".concat(n?"border-primary ring-2 ring-primary/30 bg-primary/5":"border-border bg-card"),children:[(0,s.jsxs)("div",{className:"relative aspect-[3/4] w-full overflow-hidden bg-muted",children:[e.coverUrl?(0,s.jsx)("img",{src:e.coverUrl,alt:"",className:"h-full w-full object-cover transition duration-300 group-hover:scale-105"}):(0,s.jsxs)("div",{className:"flex h-full w-full flex-col items-center justify-center p-4 text-center text-muted-foreground",children:[(0,s.jsx)(h.A,{size:24,className:"opacity-50"}),(0,s.jsx)("span",{className:"mt-2 text-[10px] font-mono opacity-70 truncate max-w-full",children:e.prefixLabel})]}),e.status&&(0,s.jsx)("span",{className:"absolute top-2 right-2 rounded-full px-2 py-0.5 text-[9px] font-semibold uppercase tracking-wider shadow-sm ".concat(l),children:e.status}),null!=e.rating&&(0,s.jsxs)("span",{className:"absolute bottom-2 left-2 flex items-center gap-1 rounded-md bg-black/75 px-1.5 py-0.5 text-[10px] font-bold text-amber-400 backdrop-blur-sm shadow-sm",children:[(0,s.jsx)(ef.A,{size:10,className:"fill-amber-400"}),String(e.rating)]})]}),(0,s.jsxs)("div",{className:"flex flex-1 flex-col p-2.5",children:[(0,s.jsx)("p",{className:"line-clamp-2 text-xs font-semibold text-foreground group-hover:text-primary transition",children:e.primaryLabel}),e.secondaryLabel&&(0,s.jsx)("p",{className:"mt-0.5 line-clamp-1 text-[10px] text-muted-foreground",children:e.secondaryLabel})]})]},e.keyStr)})})}),(0,s.jsx)(eb,{page:d,pageSize:24,totalShown:t.length,hasMore:n,loadingMore:l,onPageChange:c,onLoadMore:o})]})}var ew=r(2036);function ek(e){let{value:t}=e;if(null===t)return(0,s.jsx)("span",{className:"text-muted-foreground",children:"null"});if(Array.isArray(t))return 0===t.length?(0,s.jsx)("span",{className:"text-muted-foreground",children:"None"}):t.every(e=>null===e||"object"!=typeof e)?(0,s.jsx)("span",{className:"break-words",children:t.map(e=>String(null!=e?e:"null")).join(", ")}):(0,s.jsx)("ul",{className:"space-y-2",children:t.map((e,t)=>(0,s.jsx)("li",{children:(0,s.jsx)(ek,{value:e})},t))});if("object"==typeof t)return(0,s.jsx)("dl",{className:"divide-y divide-border/50 rounded-md border border-border/70 px-3",children:Object.entries(t).map(e=>{let[t,r]=e;return(0,s.jsxs)("div",{className:"grid min-w-0 grid-cols-[minmax(6rem,32%)_minmax(0,1fr)] gap-3 py-2 text-left",children:[(0,s.jsx)("dt",{className:"text-xs text-muted-foreground",children:ex(t)}),(0,s.jsx)("dd",{className:"min-w-0 break-words text-sm",children:(0,s.jsx)(ek,{value:r})})]},t)})});if("boolean"==typeof t)return(0,s.jsx)("span",{children:t?"Yes":"No"});if("string"==typeof t&&/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(t)){let e=new Date(t);if(!Number.isNaN(e.getTime()))return(0,s.jsx)("time",{dateTime:t,title:t,children:e.toLocaleString()})}return"string"==typeof t&&/^https?:\/\//.test(t)?(0,s.jsxs)("div",{className:"space-y-2",children:[/\.(png|jpe?g|webp|gif|svg)(\?|$)/i.test(t)&&(0,s.jsx)("img",{src:t,alt:"Record field preview",className:"max-h-40 rounded-md border object-contain"}),(0,s.jsx)("a",{className:"break-all underline underline-offset-2",href:t,target:"_blank",rel:"noreferrer",children:t})]}):(0,s.jsx)("span",{className:"whitespace-pre-wrap break-words",children:String(t)})}function eS(e){let{fields:t,identifiers:r=!1}=e,a=Object.entries(t).filter(e=>{let[t]=e;return("id"===t||t.endsWith("Id")||/(?:^|[_-])id$/i.test(t))===r});return 0===a.length?null:(0,s.jsx)("dl",{className:"divide-y divide-border/60 rounded-lg border border-border bg-card px-4",children:a.map(e=>{let[t,r]=e,a=null!==r&&"object"==typeof r;return(0,s.jsxs)("div",{className:a?"py-3":"grid grid-cols-[minmax(7rem,30%)_minmax(0,1fr)] gap-3 py-3",children:[(0,s.jsx)("dt",{className:"text-xs text-muted-foreground",children:ex(t)}),(0,s.jsx)("dd",{className:a?"mt-2 min-w-0":"min-w-0 break-words text-sm",children:(0,s.jsx)(ek,{value:r})})]},t)})})}var eC=r(2033),eL=r(3630),eA=r(443);let ez={emerald:"text-emerald-500",teal:"text-teal-500",purple:"text-purple-500",cyan:"text-cyan-500",amber:"text-amber-500",indigo:"text-indigo-500",rose:"text-rose-500",blue:"text-blue-500",violet:"text-violet-500",sky:"text-sky-500",slate:"text-slate-400"};function e_(e){var t;let{collection:r,onSelect:a}=e,n=null!=(t=ez[r.color])?t:"text-muted-foreground";return(0,s.jsxs)("details",{open:!0,className:"group rounded-lg border border-border bg-card/50 text-sm",children:[(0,s.jsxs)("summary",{className:"flex cursor-pointer select-none items-center justify-between px-4 py-2.5 hover:bg-muted/50 rounded-lg",children:[(0,s.jsx)("span",{className:"font-medium ".concat(n),children:r.label}),(0,s.jsx)("span",{className:"ml-2 rounded-full bg-muted px-2 py-0.5 text-xs font-mono text-muted-foreground",children:r.records.length})]}),(0,s.jsx)("ul",{className:"divide-y divide-border/60 border-t border-border",children:r.records.map(e=>(0,s.jsx)("li",{children:(0,s.jsxs)("button",{type:"button",onClick:()=>a(e),className:"flex w-full items-center gap-3 px-4 py-2.5 text-left hover:bg-muted/50 transition",children:[e.coverUrl&&(0,s.jsx)("img",{src:e.coverUrl,alt:"",className:"h-8 w-8 shrink-0 rounded object-cover border border-border"}),(0,s.jsxs)("div",{className:"min-w-0 flex-1",children:[(0,s.jsx)("p",{className:"truncate text-xs font-medium text-foreground",children:e.primaryLabel}),e.secondaryLabel&&(0,s.jsx)("p",{className:"truncate text-[11px] text-muted-foreground",children:e.secondaryLabel})]}),e.status&&(0,s.jsx)("span",{className:"shrink-0 rounded-full bg-muted px-2 py-0.5 text-[10px] text-muted-foreground capitalize",children:e.status})]})},e.keyStr))})]})}function eP(e){if(0===e)return"0 B";let t=Math.floor(Math.log(e)/Math.log(1024));return"".concat(parseFloat((e/Math.pow(1024,t)).toFixed(1))," ").concat(["B","KB","MB","GB"][t])}function eM(e){if(!e)return"N/A";try{let t=new Date(e);if(isNaN(t.getTime()))return e;let r=Math.floor((Date.now()-t.getTime())/1e3);if(r<60)return"Just now";if(r<3600)return"".concat(Math.floor(r/60),"m ago");if(r<86400)return"".concat(Math.floor(r/3600),"h ago");if(r<604800)return"".concat(Math.floor(r/86400),"d ago");return t.toLocaleDateString()}catch(t){return e}}function eR(e){let t=BigInt("0xcbf29ce484222325"),r=BigInt("0x100000001b3"),s=BigInt("0xffffffffffffffff");for(let a=0;a{if(null==e||!r)return;let a=Object.keys(r).map(Number).filter(e=>e!==t&&r[String(e)]>0);if(0===a.length)return void i(!0);l(a.map(e=>{let t=en(e);return{prefix:e,label:t.label,color:t.color,records:[],loading:!0}})),i(!1),l((await Promise.all(a.map(async t=>{let r=en(t),a=BigInt(t)<<56n|BigInt(e)<<28n,n=a.toString(),l=(268435455n|a).toString();try{let e=await s(n,l),a=et(e),o=r.label.startsWith("Collection ")&&a?a:r.label;return{prefix:t,label:o,color:r.color,records:e,loading:!1}}catch(e){return{prefix:t,label:r.label,color:r.color,records:[],loading:!1}}}))).filter(e=>e.records.length>0).sort((e,t)=>e.prefix-t.prefix)),i(!0)},[e,t,r,s]);return(0,a.useEffect)(()=>{d()},[d]),{collections:n,ready:o}}(d.userHash,d.prefix,f,m),w=v.reduce((e,t)=>e+t.records.length,0);return(0,s.jsxs)("div",{className:"flex flex-col gap-4 p-5",children:[(0,s.jsxs)("div",{className:"flex items-center gap-4",children:[(0,s.jsx)("div",{className:"flex h-14 w-14 shrink-0 items-center justify-center rounded-xl bg-beast-lime text-xl font-bold font-mono text-black shadow-[0_0_20px_rgba(168,242,26,0.25)]",children:p}),(0,s.jsxs)("div",{className:"min-w-0 flex-1",children:[(0,s.jsxs)("div",{className:"flex items-center gap-2",children:[(0,s.jsx)("h2",{className:"truncate text-lg font-bold text-foreground tracking-tight",children:(null==(i=d.fields)?void 0:i.username)||d.primaryLabel}),(0,s.jsx)(y,{role:b})]}),g&&(0,s.jsx)("p",{className:"truncate text-xs text-muted-foreground font-mono mt-0.5",children:g}),j&&(0,s.jsxs)("p",{className:"text-[11px] text-muted-foreground mt-0.5",children:["Joined ",eM(j)]})]})]}),(0,s.jsxs)("div",{className:"flex flex-wrap gap-1.5",children:[v.map(e=>(0,s.jsxs)("span",{className:"rounded-full border border-border bg-muted px-2.5 py-1 text-[11px] font-medium text-muted-foreground",children:[e.label," ",(0,s.jsx)("span",{className:"font-mono text-foreground",children:e.records.length})]},e.prefix)),!N&&(0,s.jsxs)("span",{className:"flex items-center gap-1 text-[11px] text-muted-foreground",children:[(0,s.jsx)(eC.A,{size:10,className:"animate-spin"})," Loading…"]}),N&&0===w&&(0,s.jsx)("span",{className:"text-[11px] text-muted-foreground",children:"No related records found."})]}),v.length>0&&(0,s.jsxs)("section",{className:"space-y-2",children:[(0,s.jsx)("h3",{className:"text-xs font-semibold uppercase tracking-wide text-muted-foreground",children:"Library"}),v.map(e=>(0,s.jsx)(e_,{collection:e,onSelect:h},e.prefix))]}),c&&(0,s.jsxs)("div",{className:"flex flex-col gap-2 pt-2 border-t border-border",children:[(0,s.jsxs)(_,{variant:"outline",size:"sm",className:"justify-start gap-2 text-xs",onClick:()=>u(d.keyStr),children:[(0,s.jsx)(eL.A,{size:13,className:"text-muted-foreground"})," Delete Credential Only"]}),null!=d.userHash&&(0,s.jsxs)(_,{variant:"destructive",size:"sm",className:"justify-start gap-2 text-xs bg-destructive/15 border border-destructive/30 text-destructive hover:bg-destructive hover:text-destructive-foreground",onClick:()=>x(d.userHash),children:[(0,s.jsx)(eA.A,{size:13})," Cascade Purge User & All Records"]})]})]})}var eI=r(1288),eD=r(5524),eB=r(5958);function eO(e){let{keyStr:t,prefixLabel:r,userHash:a,itemHash:n,onNotice:l}=e,o="";try{o=BigInt(t).toString(2).padStart(64,"0")}catch(e){return null}let i=o.slice(0,8),d=o.slice(8,36),c=o.slice(36,64),u="0x"+Number(BigInt("0b"+i)).toString(16).padStart(2,"0").toUpperCase(),x=async()=>{try{await navigator.clipboard.writeText(o),null==l||l("64-bit binary copied.")}catch(e){null==l||l("Copy failed.",!0)}};return(0,s.jsxs)("div",{className:"space-y-3 rounded-lg border border-border bg-card p-3.5",children:[(0,s.jsxs)("div",{className:"flex items-center justify-between",children:[(0,s.jsx)("span",{className:"text-[11px] font-semibold font-mono uppercase tracking-wider text-muted-foreground",children:"64-Bit Keyspace Anatomy"}),(0,s.jsx)(_,{variant:"ghost",size:"icon",className:"h-6 w-6 text-muted-foreground hover:text-foreground",onClick:()=>void x(),title:"Copy 64-bit binary",children:(0,s.jsx)(eI.A,{size:12})})]}),(0,s.jsxs)("div",{className:"flex h-3 w-full overflow-hidden rounded bg-muted border border-border",children:[(0,s.jsx)("div",{className:"h-full bg-beast-lime transition-all",style:{width:"12.5%"},title:"Partition Prefix: 8 bits (".concat(i,")")}),(0,s.jsx)("div",{className:"h-full bg-cyan-400 transition-all",style:{width:"43.75%"},title:"Domain/User Hash: 28 bits (".concat(d,")")}),(0,s.jsx)("div",{className:"h-full bg-purple-400 transition-all",style:{width:"43.75%"},title:"Item/Sequence: 28 bits (".concat(c,")")})]}),(0,s.jsxs)("div",{className:"grid grid-cols-3 gap-2 text-[11px]",children:[(0,s.jsxs)("div",{className:"rounded border border-border bg-muted/40 p-2",children:[(0,s.jsxs)("div",{className:"flex items-center gap-1.5",children:[(0,s.jsx)("span",{className:"h-2 w-2 rounded-full bg-primary"}),(0,s.jsx)("span",{className:"text-muted-foreground font-mono text-[10px]",children:"PREFIX (8b)"})]}),(0,s.jsxs)("p",{className:"mt-1 font-mono font-medium text-primary truncate",children:[u," ",r?"\xb7 ".concat(r):""]})]}),(0,s.jsxs)("div",{className:"rounded border border-border bg-muted/40 p-2",children:[(0,s.jsxs)("div",{className:"flex items-center gap-1.5",children:[(0,s.jsx)("span",{className:"h-2 w-2 rounded-full bg-cyan-400"}),(0,s.jsx)("span",{className:"text-muted-foreground font-mono text-[10px]",children:"DOMAIN (28b)"})]}),(0,s.jsx)("p",{className:"mt-1 font-mono font-medium text-cyan-600 dark:text-cyan-400 truncate",children:null!=a?a:"—"})]}),(0,s.jsxs)("div",{className:"rounded border border-border bg-muted/40 p-2",children:[(0,s.jsxs)("div",{className:"flex items-center gap-1.5",children:[(0,s.jsx)("span",{className:"h-2 w-2 rounded-full bg-purple-400"}),(0,s.jsx)("span",{className:"text-muted-foreground font-mono text-[10px]",children:"ITEM / SEQ (28b)"})]}),(0,s.jsx)("p",{className:"mt-1 font-mono font-medium text-purple-600 dark:text-purple-400 truncate",children:null!=n?n:"—"})]})]})]})}function eU(e){let{record:t,formattedRaw:r,canWrite:a,onDelete:n,onNotice:l}=e,o=async(e,t)=>{try{await navigator.clipboard.writeText(e),l("".concat(t," copied."))}catch(e){l("Copy failed.",!0)}},i=async()=>{let e=new URL(window.location.href);e.searchParams.set("key",t.keyStr),e.searchParams.set("partition",String(t.prefix)),await o(e.toString(),"Shareable Link")};return(0,s.jsxs)("details",{className:"group rounded-lg border border-border bg-card/40 text-xs",children:[(0,s.jsxs)("summary",{className:"flex cursor-pointer select-none items-center justify-between px-4 py-2.5 font-mono text-muted-foreground hover:text-foreground hover:bg-muted/40 rounded-lg",children:[(0,s.jsx)("span",{children:"ENGINE & STORAGE INTERNALS"}),(0,s.jsx)("span",{className:"text-[10px] opacity-70",children:"Click to expand"})]}),(0,s.jsxs)("div",{className:"space-y-3.5 border-t border-border p-3.5",children:[t.fields&&(0,s.jsx)(eS,{fields:t.fields,identifiers:!0}),(0,s.jsxs)("p",{className:"text-muted-foreground",children:[eu[t.format]," \xb7 ",eP(t.byteSize)]}),(0,s.jsxs)("div",{className:"font-mono text-muted-foreground space-y-1",children:[(0,s.jsxs)("p",{className:"break-all",children:["Key: ",(0,s.jsx)("span",{className:"text-foreground font-semibold",children:t.keyStr})]}),(0,s.jsxs)("p",{className:"break-all",children:["Hex: ",(0,s.jsx)("span",{className:"text-foreground font-semibold",children:t.keyHex})]})]}),(0,s.jsxs)("div",{className:"flex flex-wrap gap-2",children:[(0,s.jsxs)(_,{variant:"outline",size:"sm",onClick:()=>void o(t.keyStr,"Key"),children:[(0,s.jsx)(eI.A,{size:12})," Key"]}),(0,s.jsxs)(_,{variant:"outline",size:"sm",onClick:()=>void o(r,"Raw"),children:[(0,s.jsx)(eD.A,{size:12})," JSON"]}),(0,s.jsxs)(_,{variant:"outline",size:"sm",onClick:()=>void i(),children:[(0,s.jsx)(eB.A,{size:12})," Link"]}),a&&(0,s.jsxs)(_,{variant:"ghost",size:"sm",className:"text-destructive hover:bg-destructive/10",onClick:()=>n(t.keyStr),children:[(0,s.jsx)(eL.A,{size:12})," Delete"]})]}),(0,s.jsx)(eO,{keyStr:t.keyStr,prefixLabel:t.prefixLabel,userHash:t.userHash,itemHash:t.itemHash,onNotice:l}),(0,s.jsxs)("div",{children:[(0,s.jsx)("p",{className:"mb-1.5 text-muted-foreground font-semibold",children:"Raw Storage Payload"}),(0,s.jsx)("pre",{className:"max-h-56 overflow-auto rounded-lg border border-border bg-muted/40 p-2.5 font-mono text-[11px] leading-relaxed text-foreground whitespace-pre-wrap break-all",children:r})]})]})]})}function eF(e){var t,r;let{record:a,canWrite:n,onDelete:l,onDeleteUser:o,onEdit:i,onNotice:d,onScanRange:c,onSelect:u,partitionCounts:x}=e,m=(()=>{try{return JSON.stringify(JSON.parse(a.raw),null,2)}catch(e){return a.raw}})(),h=async()=>[];return(null==(t=a.fields)?void 0:t.email)||(null==(r=a.fields)?void 0:r.username)?(0,s.jsxs)("div",{className:"min-h-0 flex-1 overflow-y-auto",children:[(0,s.jsx)(eT,{record:a,canWrite:n,onDelete:l,onDeleteUser:null!=o?o:()=>{},onScanRange:null!=c?c:h,onSelect:null!=u?u:()=>{},partitionCounts:x}),(0,s.jsx)("div",{className:"px-5 pb-5",children:(0,s.jsx)(eU,{record:a,formattedRaw:m,canWrite:n,onDelete:l,onNotice:d})})]}):(0,s.jsxs)("div",{className:"flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto px-5 py-5",children:[(0,s.jsxs)("div",{className:"border-b border-border/70 pb-4",children:[(0,s.jsxs)("div",{className:"flex items-center justify-between gap-2",children:[(0,s.jsx)("span",{className:"truncate text-xs font-medium text-muted-foreground",children:a.prefixLabel}),n&&i&&(0,s.jsxs)(_,{variant:"outline",size:"sm",onClick:i,className:"h-7 shrink-0 gap-1 text-xs",children:[(0,s.jsx)(ew.A,{size:13})," Edit"]})]}),(0,s.jsx)("h2",{className:"mt-2 text-lg font-bold tracking-tight text-foreground",children:a.primaryLabel}),a.secondaryLabel&&(0,s.jsx)("p",{className:"text-xs text-muted-foreground mt-0.5",children:a.secondaryLabel})]}),a.coverUrl&&(0,s.jsx)("img",{src:a.coverUrl,alt:"",className:"w-full max-h-56 rounded-xl object-cover border border-border shadow-sm"}),a.fields&&(0,s.jsxs)("section",{className:"space-y-2",children:[(0,s.jsx)("h3",{className:"text-xs font-semibold uppercase tracking-wider text-muted-foreground",children:"Document Fields"}),(0,s.jsx)(eS,{fields:a.fields})]}),a.arrayItems&&(0,s.jsxs)("section",{className:"space-y-2",children:[(0,s.jsxs)("h3",{className:"text-xs font-semibold uppercase tracking-wider text-muted-foreground",children:["Items (",a.arrayItems.length,")"]}),(0,s.jsx)("div",{className:"rounded-lg border border-border bg-card p-3 text-sm",children:(0,s.jsx)(ek,{value:a.arrayItems})})]}),!a.fields&&!a.arrayItems&&(0,s.jsx)("div",{className:"rounded-lg border border-border bg-card p-3 text-sm",children:(0,s.jsx)(ek,{value:a.raw||"Empty value"})}),(0,s.jsx)(eU,{record:a,formattedRaw:m,canWrite:n,onDelete:l,onNotice:d})]})}function eH(e){var t,r;let a="split"===e.viewMode;return(0,s.jsxs)("div",{className:"grid min-h-0 flex-1 overflow-hidden rounded-xl border bg-card shadow-sm ".concat(a?"lg:grid-cols-[220px_minmax(320px,1fr)_minmax(380px,1.4fr)]":e.selected?"lg:grid-cols-[220px_minmax(0,1fr)_420px]":"lg:grid-cols-[220px_minmax(0,1fr)]"),children:[(0,s.jsx)("div",{className:"".concat("partitions"!==e.pane?"hidden lg:flex":"flex"," min-h-0 flex-col"),children:(0,s.jsx)(ed,{partitions:e.partitions,selected:e.partition,total:e.totalCount,hasMore:e.hasMore,hasFullCounts:!!e.partitionCounts,onSelect:e.onChoosePartition})}),(0,s.jsx)("div",{className:"".concat("documents"!==e.pane?"hidden lg:flex":"flex"," min-h-0 flex-col overflow-hidden"),children:a?(0,s.jsx)(eh,{records:e.records,selected:e.selected,loading:e.loading,loadingMore:e.loadingMore,hasMore:e.hasMore,title:e.partitionTitle,shownCount:e.records.length,onLoadMore:e.onLoadMore,onSelect:t=>{e.onSelect(t),e.onSetPane("details")},onBack:()=>e.onSetPane("partitions"),canWrite:e.canWrite,onNew:e.onNew}):"table"===e.viewMode?(0,s.jsx)(ev,{records:e.records,selectedKey:null==(t=e.selected)?void 0:t.keyStr,hasMore:e.hasMore,loadingMore:e.loadingMore,onLoadMore:e.onLoadMore,onSelect:t=>{e.onSelect(t),e.onSetPane("details")}}):(0,s.jsx)(eN,{records:e.records,selectedKey:null==(r=e.selected)?void 0:r.keyStr,hasMore:e.hasMore,loadingMore:e.loadingMore,onLoadMore:e.onLoadMore,onSelect:t=>{e.onSelect(t),e.onSetPane("details")}})}),(a||e.selected)&&(0,s.jsxs)("div",{className:"".concat("details"!==e.pane?"hidden lg:flex":"flex"," min-h-0 flex-col border-l border-border bg-card/60 relative"),children:[!a&&e.selected&&(0,s.jsx)("div",{className:"absolute top-3 right-3 z-10",children:(0,s.jsx)(_,{variant:"ghost",size:"icon",className:"h-7 w-7 rounded-full text-muted-foreground hover:text-foreground",onClick:()=>e.onSelect(null),"aria-label":"Close details",children:(0,s.jsx)(n.A,{size:15})})}),e.selected?(0,s.jsx)(eF,{record:e.selected,canWrite:e.canWrite,onDelete:e.onDelete,onDeleteUser:e.onDeleteUser,onEdit:e.onEdit?()=>e.onEdit(e.selected):void 0,onNotice:e.onNotice,onScanRange:e.onScanRange,onSelect:t=>{e.onSelect(t),e.onSetPane("details")},partitionCounts:e.partitionCounts}):(0,s.jsxs)("div",{className:"flex flex-1 flex-col items-center justify-center p-8 text-center",children:[(0,s.jsx)("div",{className:"rounded-xl border bg-muted p-4",children:(0,s.jsx)(h.A,{size:24,className:"text-muted-foreground"})}),(0,s.jsx)("h2",{className:"mt-4 font-medium text-foreground",children:"Select a record"}),(0,s.jsx)("p",{className:"mt-1 max-w-xs text-sm text-muted-foreground",children:"Choose a record to inspect its fields and storage details."})]})]})]})}async function eW(e,t){let r;try{r=await fetch(e,t)}catch(e){throw Error("Cannot reach BeastDB. Check your connection and retry.")}if(401===r.status)throw Error("Your session expired. Sign in again.");if(403===r.status)throw Error("Your account cannot make this change.");if(404===r.status)throw Error(e.startsWith("/api/key?")?"That record was not found.":"Data endpoint unavailable.");if(!r.ok)throw Error((await r.text()).trim()||"Request failed.");return 204===r.status||201===r.status?void 0:r.json()}let eK=new Set(["passwordHash","password_hash","password","salt","password_salt","token","secret","apiKey","api_key","apiSecret","api_secret","privateKey","private_key","sessionToken","session_secret","api_token","refresh_token"]);function eq(e,t){for(let[r,s]of Object.entries(e))if(t.includes(r.replace(/[_-]/g,"").toLowerCase())&&"string"==typeof s&&s.trim())return s.trim()}function eV(e){var t,r,s,a,n,l,o;let i,d,c,u,x,m=eo(e.keyText),h=null!=(t=e.value)?t:"",f=new Blob([h]).size,p=h.trim(),g="string",b="",j="",v=[];if(p){if(p.startsWith("{")&&p.endsWith("}"))try{let e=JSON.parse(p);if("object"==typeof e&&null!==e&&!Array.isArray(e)){g="json_object",i=Object.fromEntries(Object.entries(e).filter(e=>{let[t]=e;return!eK.has(t)})),c=function(e){if(e&&"object"==typeof e){for(let t of[e.coverUrl,e.posterUrl,e.avatarUrl,e.cover_url,e.poster_url,e.avatar_url,e.avatar,e.thumbnail,e.image,e.icon,e.photo])if("string"==typeof t&&t.startsWith("http"))return t;for(let t of Object.values(e))if("string"==typeof t&&t.match(/^https?:\/\/.+\.(jpg|jpeg|png|webp|gif|svg)(\?.*)?$/i))return t}}(e);let t=null!=(r=function e(t){let r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,s=eq(t,["title","displayname","name","label"]);if(s||r>=2)return s;for(let s of Object.values(t))if(s&&"object"==typeof s&&!Array.isArray(s)){let t=e(s,r+1);if(t)return t}}(e))?r:eq(e,["username","email"]),d=eq(e,["description","summary","subtitle","notes"]);for(let[r,i]of(b=null!=(s=null!=t?t:d)?s:null!=e.id?"Record ".concat(e.id):"Record ".concat(m.raw.slice(-6)),j=[e.status,t?d:void 0].filter(Boolean).join(" \xb7 "),u=null!=(n=null!=(a=e.status)?a:e.role)?n:e.type,x=null!=(o=null!=(l=e.rating)?l:e.score)?o:e.userRating,Object.entries(e)))if(!eK.has(r)&&!["coverUrl","posterUrl","avatarUrl","description"].includes(r))if("object"!=typeof i||null===i||Array.isArray(i))v.length<5&&v.push({key:r,value:Array.isArray(i)?"[".concat(i.length,"]"):String(i),type:typeof i});else for(let[e,t]of Object.entries(i))v.length<5&&"object"!=typeof t&&v.push({key:"".concat(r,".").concat(e),value:String(t),type:typeof t})}}catch(e){g="string"}else if(p.startsWith("[")&&p.endsWith("]"))try{let e=JSON.parse(p);Array.isArray(e)&&(g="json_array",d=e,b="Array [".concat(e.length," items]"),v.push({key:"length",value:String(e.length),type:"number"}))}catch(e){g="string"}else if(p.includes("|")&&p.length<250){g="token";let e=p.split("|");b=e[0]||"Token Payload",j=e.slice(1).join(" • "),e.forEach((e,t)=>v.push({key:"part_".concat(t+1),value:e,type:"string"}))}}else g="empty",b="Empty record";return"string"===g&&(b=p.length>50?p.slice(0,47)+"...":p,v.push({key:"length",value:"".concat(p.length," chars"),type:"number"})),{keyStr:m.raw,keyHex:m.hex,prefix:m.prefix,prefixLabel:m.prefixLabel,userHash:m.userHash,itemHash:m.itemHash,format:g,byteSize:f,primaryLabel:b||"Key ".concat(m.prefixHex),secondaryLabel:j,coverUrl:c,status:u,rating:x,attributes:v,fields:i,arrayItems:d,raw:h}}function eJ(e){var t,r,n;let l=function(e){let[t,r]=(0,a.useState)(0),s=JSON.stringify(null!=e?e:{});return(0,a.useEffect)(()=>{let t=new AbortController,s=Object.entries(null!=e?e:{}).filter(e=>{let[,t]=e;return t>0}).map(e=>{let[t]=e;return Number(t)}).filter(e=>Number.isInteger(e)&&e>=0&&e<=255);async function a(){let e={};for(let a=0;a{let s=BigInt(r)<<56n,a=(BigInt(r)+1n<<56n)-1n;try{let n=await eW("/api/records?start=".concat(s,"&end=").concat(a,"&limit=24"),{signal:t.signal});if(t.signal.aborted)return;e[r]=er(n.records.map(eV),r)}catch(e){}})),t.signal.aborted)return;ea={...e},r(e=>e+1)}}return ea={},r(e=>e+1),a(),()=>t.abort()},[s]),t}(e.partitionCounts),o=(0,a.useRef)(!1),[i,d]=(0,a.useState)("documents"),[c,u]=(0,a.useState)("text"),[x,m]=(0,a.useState)("table"),[h,f]=(0,a.useState)("all"),[p,g]=(0,a.useState)(!1),[b,j]=(0,a.useState)(!1),[v,y]=(0,a.useState)(null),[N,w]=(0,a.useState)(null),k=(0,a.useMemo)(()=>(function(e,t){var r,s,a;let n=new Map,l=new Map;for(let t of e){let e=l.get(t.prefix);e||(e=[],l.set(t.prefix,e)),e.push(t)}let o=e=>{if(es[e]||ea[e])return en(e);let t=l.get(e);return(null==t?void 0:t.length)?er(t,e):en(e)};for(let e of el()){let s=t&&null!=(r=t[String(e.prefix)])?r:0;n.set(e.prefix,{prefixLabel:e.label,count:s})}for(let[e,r]of Object.entries(null!=t?t:{})){let t=Number(e);if(!n.has(t)){let e=o(t);n.set(t,{prefixLabel:e.label,count:r})}}for(let r of e){let e=n.get(r.prefix);if(e)t||(e.count+=1),e.prefixLabel.startsWith("Collection ")&&(e.prefixLabel=o(r.prefix).label);else{let e=t&&null!=(s=t[String(r.prefix)])?s:1,a=o(r.prefix);n.set(r.prefix,{prefixLabel:a.label,count:e})}}let i=new Map;for(let e of n.values())i.set(e.prefixLabel,(null!=(a=i.get(e.prefixLabel))?a:0)+1);return[...n.entries()].sort((e,t)=>{let[r]=e,[s]=t;return r-s}).map(e=>{var t;let[r,s]=e;return{prefix:r,count:s.count,prefixLabel:(null!=(t=i.get(s.prefixLabel))?t:0)>1?"".concat(s.prefixLabel," \xb7 ").concat(r.toString(16).padStart(2,"0").toUpperCase()):s.prefixLabel}})})(e.records,e.partitionCounts),[e.records,e.partitionCounts,l]),[S,C]=(0,a.useState)("1");(0,a.useEffect)(()=>{var t;if(o.current||!e.partitionCounts||0===k.length)return;o.current=!0;let r=k.some(e=>1===e.prefix)?1:k[0].prefix;C(String(r)),null==(t=e.onLoadPartition)||t.call(e,r)},[k,e.partitionCounts,e.onLoadPartition]);let L=(0,a.useMemo)(()=>e.partitionCounts?Object.values(e.partitionCounts).reduce((e,t)=>e+t,0):e.records.length,[e.partitionCounts,e.records.length]),A=b&&v?v:e.records,z=(0,a.useMemo)(()=>"all"===h?A:A.filter(e=>{var t;return String(null!=(t=e.status)?t:"").trim().toLowerCase()===h}),[A,h]),P=async t=>{g(!0);try{await t()}catch(t){e.onNotice(t.message,!0)}finally{g(!1)}},R=(null==(t=k.find(e=>String(e.prefix)===S))?void 0:t.prefixLabel)||"Collection ".concat(Number(S).toString(16).padStart(2,"0").toUpperCase());return(0,s.jsxs)("section",{"aria-labelledby":"records-title",className:"flex min-h-0 flex-1 flex-col gap-3",children:[(0,s.jsx)("div",{className:"flex flex-wrap items-center justify-between gap-4",children:(0,s.jsxs)("div",{children:[(0,s.jsx)(M,{partitionLabel:R,recordLabel:null==(r=e.selected)?void 0:r.primaryLabel,onBack:()=>d("partitions")}),(0,s.jsx)("h1",{id:"records-title",className:"mt-1 text-[22px] font-bold leading-tight tracking-tight",children:"Table Editor"}),(0,s.jsx)("p",{className:"mt-1 text-sm text-muted-foreground",children:"Browse, search and edit your records."})]})}),(0,s.jsx)(B,{mode:c,onModeChange:u,partitions:k,activePartition:S,onSearchText:(t,r)=>P(async()=>{if(!e.onSearch)return;let s=await e.onSearch(t,{prefix:r});y(s.records),w({...s,query:t}),j(!0),e.onNotice("Found ".concat(s.count," matches across ").concat(s.scannedCount," keys."))}),onLookupKey:t=>P(async()=>{let r=await e.onLookup(t);C(String(r.prefix)),e.onSelect(r),d("details")}),onScanRange:(t,r)=>P(async()=>{if(!e.onScanRange)return;let s=await e.onScanRange(t,r);y(s),j(!0),w(null),e.onNotice("Retrieved ".concat(s.length," records in range."))}),busy:p}),b&&N&&(0,s.jsx)(U,{query:N.query,count:N.count,scannedCount:N.scannedCount,hasMore:N.hasMore,prefix:N.prefix,prefixLabel:null==(n=k.find(e=>e.prefix===N.prefix))?void 0:n.prefixLabel,loadingMore:p,onContinueScan:()=>P(async()=>{if(!e.onSearch||!(null==N?void 0:N.nextKey))return;let t=await e.onSearch(N.query,{prefix:N.prefix,start:N.nextKey});y(e=>[...e||[],...t.records]),w({...t,query:N.query,count:(N.count||0)+t.count,scannedCount:(N.scannedCount||0)+t.scannedCount}),e.onNotice("Scanned +".concat(t.scannedCount," keys. Total: ").concat(N.count+t.count))}),onClear:()=>{j(!1),y(null),w(null)}}),e.error&&(0,s.jsxs)("div",{role:"alert",className:"flex items-center justify-between rounded-lg border border-destructive/30 bg-destructive/5 p-3 text-sm",children:[(0,s.jsx)("span",{children:e.error}),(0,s.jsx)(_,{variant:"outline",size:"sm",onClick:e.onRetry,children:"Retry"})]}),(0,s.jsx)(V,{records:A,activeStatus:h,onSelectStatus:f,viewMode:x,onViewModeChange:m,canWrite:e.canWrite,onNew:e.onNew}),(0,s.jsx)(eH,{viewMode:x,records:z,selected:e.selected,loading:e.loading,loadingMore:e.loadingMore,hasMore:e.hasMore,partitionTitle:b?"Search Results":R,canWrite:e.canWrite,pane:i,partitions:k,partition:S,totalCount:L,partitionCounts:e.partitionCounts,onSelect:e.onSelect,onSetPane:d,onLoadMore:e.onLoadMore,onNew:e.onNew,onChoosePartition:t=>{var r;C(t),f("all"),j(!1),y(null),e.onSelect(null),d("documents"),null==(r=e.onLoadPartition)||r.call(e,Number(t))},onDelete:e.onDelete,onDeleteUser:e.onDeleteUser,onEdit:e.onEdit?()=>e.onEdit(e.selected):void 0,onNotice:e.onNotice,onScanRange:e.onScanRange})]})}let e$=a.forwardRef((e,t)=>{let{className:r,...a}=e;return(0,s.jsx)("div",{ref:t,className:S("rounded-xl border border-border bg-card text-card-foreground",r),...a})});e$.displayName="Card";let eG=a.forwardRef((e,t)=>{let{className:r,...a}=e;return(0,s.jsx)("div",{ref:t,className:S("flex flex-col space-y-1.5 p-5",r),...a})});eG.displayName="CardHeader";let eY=a.forwardRef((e,t)=>{let{className:r,...a}=e;return(0,s.jsx)("h3",{ref:t,className:S("font-semibold leading-none tracking-tight",r),...a})});eY.displayName="CardTitle";let eZ=a.forwardRef((e,t)=>{let{className:r,...a}=e;return(0,s.jsx)("p",{ref:t,className:S("text-xs text-muted-foreground",r),...a})});eZ.displayName="CardDescription";let eQ=a.forwardRef((e,t)=>{let{className:r,...a}=e;return(0,s.jsx)("div",{ref:t,className:S("p-5 pt-0",r),...a})});function eX(e){let{className:t,variant:r="default",...a}=e;return(0,s.jsx)("div",{className:S("inline-flex items-center rounded-md border px-2 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",{"border-transparent bg-primary text-primary-foreground":"default"===r,"border-primary/40 bg-primary/10 text-primary font-mono text-[10px] tracking-wide":"lime"===r,"border-transparent bg-secondary text-secondary-foreground":"secondary"===r,"border-border text-foreground":"outline"===r,"border-destructive/30 bg-destructive/15 text-destructive":"destructive"===r,"border-border bg-muted/80 text-muted-foreground font-mono text-[10px]":"mono"===r},t),...a})}eQ.displayName="CardContent";let e0={pinned:"bg-info",dirty:"bg-primary",clean:"bg-foreground/25",free:"bg-border/60"};function e1(e){var t,r,a,n;let{storage:l}=e,o=null!=(t=null==l?void 0:l.poolSize)?t:0,i=Math.min(null!=(r=null==l?void 0:l.pinnedPages)?r:0,o),d=Math.min(null!=(a=null==l?void 0:l.dirtyPages)?a:0,o-i),c=Math.min(null!=(n=null==l?void 0:l.cachedPages)?n:0,o),u=Math.max(c-i-d,0),x=[...Array(i).fill("pinned"),...Array(d).fill("dirty"),...Array(u).fill("clean")];for(;x.length"free"!==e).length]];return(0,s.jsxs)("div",{children:[(0,s.jsxs)("div",{className:"flex items-baseline justify-between",children:[(0,s.jsx)("h2",{className:"text-sm font-semibold",children:"Buffer pool"}),(0,s.jsxs)("span",{className:"font-mono text-xs text-muted-foreground",children:[c,"/",o||"—"," frames \xb7 4 KiB"]})]}),(0,s.jsx)("div",{className:"mt-4 grid gap-[3px]",style:{gridTemplateColumns:"repeat(auto-fill, minmax(14px, 1fr))"},role:"img","aria-label":"".concat(d," dirty, ").concat(i," pinned, ").concat(u," clean cached, of ").concat(o," frames"),children:x.map((e,t)=>(0,s.jsx)("span",{className:S("aspect-square rounded-[3px]",e0[e])},t))}),(0,s.jsx)("ul",{className:"mt-4 flex flex-wrap gap-x-5 gap-y-1 font-mono text-xs text-muted-foreground",children:m.map(e=>{let[t,r,a]=e;return(0,s.jsxs)("li",{className:"flex items-center gap-1.5",children:[(0,s.jsx)("span",{className:S("h-2.5 w-2.5 rounded-[3px]",e0[t])}),r," ",(0,s.jsx)("span",{className:"text-foreground",children:a})]},t)})})]})}function e2(e){var t,r;let{lsn:n,storage:l}=e,o=(0,a.useRef)(void 0),[i,d]=(0,a.useState)(!1);return(0,a.useEffect)(()=>{if(void 0!==n&&void 0!==o.current&&n>o.current){d(!0);let e=window.setTimeout(()=>d(!1),900);return o.current=n,()=>clearTimeout(e)}o.current=n},[n]),(0,s.jsxs)("div",{className:"flex h-full flex-col",children:[(0,s.jsx)("h2",{className:"text-sm font-semibold",children:"Write-ahead log"}),(0,s.jsx)("p",{className:"mt-4 text-xs text-muted-foreground",children:"Log sequence number"}),(0,s.jsx)("p",{className:S("mt-1 font-mono text-4xl font-bold tracking-tight transition-colors duration-700",i?"text-primary":"text-foreground"),children:null!=(t=null==n?void 0:n.toLocaleString())?t:"—"}),(0,s.jsx)("div",{className:"mt-4 h-1.5 overflow-hidden rounded-full bg-border/60","aria-hidden":"true",children:(0,s.jsx)("div",{className:S("h-full rounded-full bg-primary transition-all duration-700",i?"w-full":"w-1/3")})}),(0,s.jsxs)("dl",{className:"mt-auto grid grid-cols-2 gap-3 pt-5 font-mono text-xs",children:[(0,s.jsxs)("div",{children:[(0,s.jsx)("dt",{className:"text-muted-foreground",children:"WAL size"}),(0,s.jsx)("dd",{className:"mt-0.5 text-sm text-foreground",children:l?eP(l.walSizeBytes):"—"})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)("dt",{className:"text-muted-foreground",children:"Active page"}),(0,s.jsxs)("dd",{className:"mt-0.5 text-sm text-foreground",children:["#",null!=(r=null==l?void 0:l.activeDataPage)?r:"—"]})]})]})]})}let e5={emerald:"#10b981",teal:"#14b8a6",purple:"#a855f7",cyan:"#06b6d4",amber:"#f59e0b",indigo:"#6366f1",rose:"#f43f5e",blue:"#3b82f6",violet:"#8b5cf6",sky:"#0ea5e9",fuchsia:"#d946ef",orange:"#f97316",lime:"#A8F21A",slate:"#71717a"};function e3(e){let{data:t}=e,r=t.reduce((e,t)=>e+t.count,0);if(0===r)return null;let a=0;return(0,s.jsxs)("div",{className:"flex flex-wrap items-center gap-8",children:[(0,s.jsx)("div",{className:"relative shrink-0",children:(0,s.jsxs)("svg",{width:160,height:160,"aria-hidden":"true",children:[t.map((e,t)=>{var n;let l=e.count/r*360,o=a,i=a+l-.5;a+=l;let d=null!=(n=e5[e.color])?n:e5.slate;return(0,s.jsx)("path",{d:function(e,t,r,s,a){let n=e=>e*Math.PI/180,l=80+70*Math.cos(n(s-90)),o=80+70*Math.sin(n(s-90)),i=80+r*Math.cos(n(a-90)),d=80+r*Math.sin(n(a-90));return"M ".concat(e," ").concat(t," L ").concat(l," ").concat(o," A ").concat(r," ").concat(r," 0 ").concat(+(a-s>180)," 1 ").concat(i," ").concat(d," Z")}(80,80,70,o,Math.max(i,o+.1)),fill:d},t)}),(0,s.jsx)("circle",{cx:80,cy:80,r:42,className:"fill-card"}),(0,s.jsx)("text",{x:80,y:76,textAnchor:"middle",className:"fill-foreground font-mono text-base font-bold",fontSize:16,fontWeight:700,children:r.toLocaleString()}),(0,s.jsx)("text",{x:80,y:93,textAnchor:"middle",fontSize:10,className:"fill-muted-foreground font-mono uppercase tracking-wider",children:"records"})]})}),(0,s.jsx)("div",{className:"grid flex-1 grid-cols-1 gap-2 sm:grid-cols-2",children:t.map((e,t)=>{var a;let n=(e.count/r*100).toFixed(1),l=null!=(a=e5[e.color])?a:e5.slate;return(0,s.jsxs)("div",{className:"flex items-center justify-between gap-3 rounded-lg border border-border bg-card px-3 py-2 text-xs",children:[(0,s.jsxs)("div",{className:"flex min-w-0 items-center gap-2",children:[(0,s.jsx)("span",{className:"h-2.5 w-2.5 shrink-0 rounded-full",style:{background:l}}),(0,s.jsx)("span",{className:"truncate text-foreground font-medium",children:e.label})]}),(0,s.jsxs)("div",{className:"flex items-center gap-2",children:[(0,s.jsx)("span",{className:"font-mono text-muted-foreground tabular-nums",children:e.count.toLocaleString()}),(0,s.jsxs)("span",{className:"rounded bg-muted px-1.5 py-0.5 font-mono text-[10px] text-muted-foreground",children:[n,"%"]})]})]},t)})})]})}let e4={muted:"text-muted-foreground",primary:"text-primary",success:"text-success",warning:"text-warning",info:"text-info"};function e6(e){let{label:t,value:r,hint:a,tone:n="muted"}=e;return(0,s.jsxs)(e$,{className:"px-5 py-4",children:[(0,s.jsx)("p",{className:"text-xs text-muted-foreground",children:t}),(0,s.jsx)("p",{className:"mt-1 truncate text-[28px] font-bold leading-tight tracking-tight text-foreground",title:r,children:r}),a&&(0,s.jsx)("p",{className:S("mt-1 text-xs",e4[n]),children:a})]})}function e8(e){let{cols:t=4,children:r}=e;return(0,s.jsx)("div",{className:S("grid grid-cols-2 gap-4",4===t?"lg:grid-cols-4":"lg:grid-cols-3"),children:r})}function e9(e){let{values:t,label:r,className:n,tone:l="text-primary",height:o=56,zeroBased:i=!0}=e,d=(0,a.useId)(),c=t.filter(e=>void 0!==e&&Number.isFinite(e));if(c.length<2)return(0,s.jsx)("div",{role:"img","aria-label":"".concat(r,": collecting samples"),className:S("flex items-center justify-center rounded-md border border-dashed border-border text-[11px] text-muted-foreground",n),style:{height:o},children:"collecting samples"});let u=Math.max(...c),x=i?0:Math.min(...c),m=u-x||1,h=t.length,f=[],p=[];t.forEach((e,t)=>{if(void 0!==e&&Number.isFinite(e))p.push([1===h?0:t/(h-1)*240,o-3-(e-x)/m*(o-6)]);else p.length&&f.push(p),p=[]}),p.length&&f.push(p);let g=(()=>{for(let e=t.length-1;e>=0;e--)if(void 0!==t[e])return e;return -1})(),b=g>=0?t[g]:void 0;return(0,s.jsxs)("svg",{role:"img","aria-label":"".concat(r,": latest ").concat(void 0===b?"n/a":b.toPrecision(3),", range ").concat(x.toPrecision(3)," to ").concat(u.toPrecision(3)),viewBox:"0 0 ".concat(240," ").concat(o),preserveAspectRatio:"none",className:S("w-full",l,n),style:{height:o},children:[(0,s.jsx)("defs",{children:(0,s.jsxs)("linearGradient",{id:d,x1:"0",y1:"0",x2:"0",y2:"1",children:[(0,s.jsx)("stop",{offset:"0%",stopColor:"currentColor",stopOpacity:"0.22"}),(0,s.jsx)("stop",{offset:"100%",stopColor:"currentColor",stopOpacity:"0"})]})}),(0,s.jsx)("line",{x1:"0",x2:240,y1:o-3,y2:o-3,className:"stroke-border",strokeWidth:"1",vectorEffect:"non-scaling-stroke"}),f.map((e,t)=>{let r=e.map((e,t)=>{let[r,s]=e;return"".concat(t?"L":"M").concat(r.toFixed(1)," ").concat(s.toFixed(1))}).join(" "),a="".concat(r," L").concat(e[e.length-1][0].toFixed(1)," ").concat(o-3," L").concat(e[0][0].toFixed(1)," ").concat(o-3," Z");return(0,s.jsxs)("g",{children:[e.length>1&&(0,s.jsx)("path",{d:a,fill:"url(#".concat(d,")")}),(0,s.jsx)("path",{d:r,fill:"none",stroke:"currentColor",strokeWidth:"1.5",strokeLinejoin:"round",vectorEffect:"non-scaling-stroke"})]},t)})]})}function e7(e,t){let[r,s]=(0,a.useState)(null),[n,l]=(0,a.useState)(null);return(0,a.useEffect)(()=>{if(!t)return;let r=!1,a=async()=>{try{let t=await fetch("/api/metrics/history?window=".concat(e),{cache:"no-store"});if(!t.ok)throw Error("HTTP ".concat(t.status));let a=await t.json();if(r)return;s(a),l(null)}catch(e){r||l(e instanceof Error?e.message:"request failed")}};a();let n=setInterval(()=>void a(),3e4);return()=>{r=!0,clearInterval(n)}},[e,t]),{data:r,error:n}}function te(e){var t,r,a,n,l,o,i,d,c,u;let{stats:x,statsError:m,recordsError:h,user:f,loaded:p,hasMore:g,updatedAt:b,onRetry:j,onAnalyze:v}=e,y=((null==x?void 0:x.partitionCounts)?Object.entries(x.partitionCounts).map(e=>{let[t,r]=e,s=Number(t),a=el().find(e=>e.prefix===s);return{prefix:s,count:r,label:(null==a?void 0:a.label)||"Partition 0x".concat(s.toString(16).padStart(2,"0").toUpperCase()),color:(null==a?void 0:a.color)||"emerald"}}):[]).filter(e=>e.count>0),N=(null==f?void 0:f.role)==="admin",{data:w}=e7(3600,N),k=null!=(l=null==w?void 0:w.points.map(e=>e.reqPerSec))?l:[],S=y.reduce((e,t)=>e+t.count,0),C=!!x,L=null!=(o=null==x||null==(t=x.storage)?void 0:t.dirtyPages)?o:0,A=null!=(i=null==x||null==(r=x.storage)?void 0:r.poolSize)?i:64;null==x||null==(a=x.storage)||a.cachedPages;let z=L>.8*A;return(0,s.jsxs)("section",{"aria-labelledby":"overview-title",className:"space-y-6",children:[(0,s.jsxs)("div",{className:"flex flex-wrap items-start justify-between gap-4",children:[(0,s.jsxs)("div",{children:[(0,s.jsx)("h1",{id:"overview-title",className:"text-[22px] font-bold leading-tight tracking-tight text-foreground",children:"Overview"}),(0,s.jsx)("p",{className:"mt-1 text-sm text-muted-foreground",children:C?"Engine telemetry and measured vitals.":"Telemetry is unavailable right now."})]}),(0,s.jsxs)("div",{className:"flex items-center gap-2",children:[(0,s.jsxs)("span",{className:"inline-flex h-9 items-center gap-2 rounded-lg border border-border bg-card px-3 text-sm",children:[(0,s.jsx)("span",{className:"h-2 w-2 rounded-full ".concat(C?z?"bg-warning":"bg-primary":"bg-destructive")}),C?z?"High Dirty Load":"Optimal (Verified)":"Offline"]}),(0,s.jsxs)(_,{variant:"outline",size:"sm",onClick:v,className:"h-9 gap-2 text-sm",children:[(0,s.jsx)(T.A,{size:15})," Key Analyzer"]})]})]}),(m||h)&&(0,s.jsxs)("div",{role:"alert",className:"flex items-center justify-between rounded-lg border border-destructive/40 bg-destructive/10 p-3 text-xs text-destructive",children:[(0,s.jsx)("span",{children:m||h}),(0,s.jsx)(_,{variant:"outline",size:"sm",onClick:j,className:"h-7 text-xs",children:"Retry"})]}),(0,s.jsxs)(e8,{children:[(0,s.jsx)(e6,{label:"Records",value:S.toLocaleString(),hint:"".concat(y.length," partition").concat(1===y.length?"":"s")}),(0,s.jsx)(e6,{label:"Log sequence number",value:(null!=(d=null==x?void 0:x.lsn)?d:0).toLocaleString(),hint:"WAL head"}),(0,s.jsx)(e6,{label:"Dirty pages",value:"".concat(L),hint:"of ".concat(A," in buffer pool"),tone:z?"warning":"muted"}),(0,s.jsx)(e6,{label:"Storage",value:(null==x?void 0:x.storage)?eP(x.storage.diskSizeBytes):"—",hint:"".concat(null!=(c=null==x||null==(n=x.storage)?void 0:n.totalPages)?c:0," pages")})]}),N&&(0,s.jsxs)(e$,{className:"p-6",children:[(0,s.jsxs)("div",{className:"flex items-baseline justify-between",children:[(0,s.jsx)(eY,{className:"text-sm font-semibold",children:"Requests per second \xb7 last hour"}),(0,s.jsx)("span",{className:"font-mono text-xs text-muted-foreground",children:k.length?"".concat((null!=(u=k[k.length-1])?u:0).toFixed(1)," now"):"—"})]}),(0,s.jsx)(e9,{values:k,label:"Requests per second over the last hour",height:96,className:"mt-3"}),(0,s.jsx)("p",{className:"mt-2 text-[11px] text-muted-foreground",children:"Sampled by the server and kept in memory, so it starts empty after a restart."})]}),(0,s.jsxs)("div",{className:"grid gap-4 lg:grid-cols-3",children:[(0,s.jsx)(e$,{className:"p-6 lg:col-span-2",children:(0,s.jsx)(e1,{storage:null==x?void 0:x.storage})}),(0,s.jsx)(e$,{className:"p-6",children:(0,s.jsx)(e2,{lsn:null==x?void 0:x.lsn,storage:null==x?void 0:x.storage})})]}),(0,s.jsxs)("div",{className:"grid gap-4 sm:grid-cols-2",children:[(0,s.jsx)(tt,{label:"Node role",value:((null==x?void 0:x.role)||"standalone").toUpperCase()}),(0,s.jsx)(tt,{label:"Version",value:"v".concat((null==x?void 0:x.version)||"1.0.0")})]}),y.length>0&&(0,s.jsxs)(e$,{children:[(0,s.jsx)(eG,{children:(0,s.jsxs)("div",{className:"flex items-center justify-between",children:[(0,s.jsx)(eY,{className:"text-sm font-semibold",children:"Keyspace Distribution by Partition"}),(0,s.jsxs)(eX,{variant:"mono",children:[y.length," Partitions Registered"]})]})}),(0,s.jsx)(eQ,{children:(0,s.jsx)(e3,{data:y})})]}),(0,s.jsxs)("div",{className:"grid gap-4 md:grid-cols-2",children:[(0,s.jsxs)(e$,{children:[(0,s.jsx)(eG,{children:(0,s.jsx)(eY,{className:"text-sm font-semibold",children:"Engine Runtime Parameters"})}),(0,s.jsxs)(eQ,{className:"divide-y divide-border text-xs",children:[(0,s.jsxs)("div",{className:"flex justify-between py-2.5",children:[(0,s.jsx)("span",{className:"text-muted-foreground",children:"Storage Hierarchy"}),(0,s.jsx)("span",{className:"font-mono text-foreground",children:"4KB Slotted Pages / Slotted Array"})]}),(0,s.jsxs)("div",{className:"flex justify-between py-2.5",children:[(0,s.jsx)("span",{className:"text-muted-foreground",children:"Durability Model"}),(0,s.jsx)("span",{className:"font-mono text-foreground",children:"ARIES WAL + Append-Only tombstones"})]}),(0,s.jsxs)("div",{className:"flex justify-between py-2.5",children:[(0,s.jsx)("span",{className:"text-muted-foreground",children:"Index Structure"}),(0,s.jsx)("span",{className:"font-mono text-foreground",children:"On-Disk B+ Tree (Fanout ~200)"})]}),(0,s.jsxs)("div",{className:"flex justify-between py-2.5",children:[(0,s.jsx)("span",{className:"text-muted-foreground",children:"Last Telemetry Sync"}),(0,s.jsx)("span",{className:"font-mono text-muted-foreground",children:b?b.toLocaleTimeString():"—"})]})]})]}),(0,s.jsxs)(e$,{children:[(0,s.jsx)(eG,{children:(0,s.jsx)(eY,{className:"text-sm font-semibold",children:"Current Session Privileges"})}),(0,s.jsxs)(eQ,{className:"divide-y divide-border text-xs",children:[(0,s.jsxs)("div",{className:"flex justify-between py-2.5",children:[(0,s.jsx)("span",{className:"text-muted-foreground",children:"Authenticated Subject"}),(0,s.jsx)("span",{className:"font-mono font-medium text-foreground",children:(null==f?void 0:f.username)||"Anonymous"})]}),(0,s.jsxs)("div",{className:"flex justify-between py-2.5",children:[(0,s.jsx)("span",{className:"text-muted-foreground",children:"Authorization Scope"}),(0,s.jsx)(eX,{variant:(null==f?void 0:f.role)==="admin"?"lime":"mono",children:(null==f?void 0:f.role)||"viewer"})]}),(0,s.jsxs)("div",{className:"flex justify-between py-2.5",children:[(0,s.jsx)("span",{className:"text-muted-foreground",children:"Write Operations"}),(0,s.jsx)("span",{className:(null==f?void 0:f.role)==="admin"?"text-primary font-medium":"text-muted-foreground",children:(null==f?void 0:f.role)==="admin"?"Enabled (Full I/O)":"Disabled (Read-Only)"})]}),(0,s.jsxs)("div",{className:"flex justify-between py-2.5",children:[(0,s.jsx)("span",{className:"text-muted-foreground",children:"Session Mode"}),(0,s.jsx)("span",{className:"font-mono text-muted-foreground",children:"HMAC-Signed Cookie"})]})]})]})]})]})}function tt(e){let{label:t,value:r}=e;return(0,s.jsxs)(e$,{className:"flex items-baseline justify-between gap-3 px-5 py-4",children:[(0,s.jsx)("span",{className:"shrink-0 text-xs text-muted-foreground",children:t}),(0,s.jsx)("span",{className:"min-w-0 truncate font-mono text-lg font-semibold text-foreground",title:r,children:r})]})}var tr=r(2111),ts=r(8770),ta=r(9915);function tn(e){let{id:t,title:r,subtitle:a,actions:n}=e;return(0,s.jsxs)("div",{className:"flex flex-wrap items-start justify-between gap-4",children:[(0,s.jsxs)("div",{children:[(0,s.jsx)("h1",{id:t,className:"text-[22px] font-bold leading-tight tracking-tight text-foreground",children:r}),a&&(0,s.jsx)("p",{className:"mt-1 text-sm text-muted-foreground",children:a})]}),n&&(0,s.jsx)("div",{className:"flex items-center gap-2",children:n})]})}function tl(e){let{outcome:t,onContinue:r}=e,{records:a,ms:n,scanned:l,indexed:o,hasMore:i,nextKey:d}=t;return(0,s.jsxs)("div",{className:"overflow-hidden rounded-xl border border-border bg-card",children:[(0,s.jsxs)("div",{className:"flex flex-wrap items-center gap-x-4 gap-y-1 border-b border-border px-4 py-2.5 text-xs text-muted-foreground",children:[(0,s.jsxs)("span",{children:[(0,s.jsx)("span",{className:"font-mono text-foreground",children:a.length})," ",1===a.length?"result":"results"]}),(0,s.jsxs)("span",{children:[(0,s.jsx)("span",{className:"font-mono text-foreground",children:n.toFixed(1)})," ms"]}),void 0!==l&&(0,s.jsxs)("span",{children:["scanned ",(0,s.jsx)("span",{className:"font-mono text-foreground",children:l})]}),o&&(0,s.jsx)(eX,{variant:"mono",children:"secondary index"}),i&&d&&r&&(0,s.jsxs)("button",{type:"button",onClick:()=>r(d),className:"ml-auto font-mono text-primary hover:underline",children:["Continue from ",d," →"]})]}),0===a.length?(0,s.jsxs)("p",{className:"px-4 py-10 text-center text-sm text-muted-foreground",children:["No records matched.",i?" More of the key space remains to be scanned.":""]}):(0,s.jsx)("div",{className:"overflow-x-auto",children:(0,s.jsxs)("table",{className:"w-full text-left text-sm",children:[(0,s.jsx)("thead",{children:(0,s.jsxs)("tr",{className:"border-b border-border text-[11px] uppercase tracking-wide text-muted-foreground",children:[(0,s.jsx)("th",{className:"px-4 py-2 font-medium",children:"Key"}),(0,s.jsx)("th",{className:"px-4 py-2 font-medium",children:"Partition"}),(0,s.jsx)("th",{className:"px-4 py-2 text-right font-medium",children:"Size"}),(0,s.jsx)("th",{className:"px-4 py-2 font-medium",children:"Preview"})]})}),(0,s.jsx)("tbody",{children:a.map(e=>{let t=eo(e.keyStr);return(0,s.jsxs)("tr",{className:"border-b border-border/60 last:border-0 hover:bg-muted/40",children:[(0,s.jsxs)("td",{className:"px-4 py-2.5",children:[(0,s.jsx)("div",{className:"font-mono text-xs text-foreground",children:e.keyStr}),(0,s.jsx)(ep,{keyStr:e.keyStr,className:"mt-1"})]}),(0,s.jsxs)("td",{className:"whitespace-nowrap px-4 py-2.5",children:[(0,s.jsx)("span",{className:"text-foreground",children:t.prefixLabel})," ",(0,s.jsx)("span",{className:"font-mono text-xs text-muted-foreground",children:t.prefixHex})]}),(0,s.jsx)("td",{className:"whitespace-nowrap px-4 py-2.5 text-right font-mono text-xs",children:eP(e.byteSize)}),(0,s.jsx)("td",{className:"max-w-md px-4 py-2.5",children:(0,s.jsx)("div",{className:"truncate font-mono text-xs text-muted-foreground",title:e.raw,children:function(e){let t=e.replace(/\s+/g," ").trim();return t.length>120?"".concat(t.slice(0,120),"…"):t||"(empty)"}(e.raw)})})]},e.keyStr)})})]})})]})}let to={mode:"get",key:"",start:"",end:"",limit:"50",q:"",prefix:"",maxScan:"2500"},ti=(1n<<64n)-1n;function td(e,t){let r=t.trim();if(!/^(0x[0-9a-f]+|\d+)$/i.test(r))throw Error("".concat(e," must be an unsigned 64-bit integer (decimal or 0x hex)."));let s=BigInt(r);if(s>ti)throw Error("".concat(e," exceeds the 64-bit key space."));return s.toString()}function tc(e,t,r){let s=Number(t.trim());if(!Number.isInteger(s)||s<1)throw Error("".concat(e," must be a positive integer."));return Math.min(s,r)}function tu(e){return"get"===e.mode?"GET ".concat(e.key.trim()):"search"===e.mode?'SEARCH "'.concat(e.q.trim(),'"').concat(e.prefix.trim()?" @".concat(e.prefix.trim()):""):"SCAN ".concat(e.start.trim()||"min"," .. ").concat(e.end.trim()||"max")}async function tx(e){let t=performance.now(),r=e=>({...e,ms:performance.now()-t});if("get"===e.mode){var s;if(!e.key.trim())throw Error("Enter a key to look up.");let t=td("Key",e.key),a=await eW("/api/key?k=".concat(t));return r({records:[eV({keyText:null!=(s=a.keyText)?s:t,value:a.value})],hasMore:!1})}if("scan"===e.mode){let t=new URLSearchParams({limit:String(tc("Limit",e.limit,500))});e.start.trim()&&t.set("start",td("Start",e.start)),e.end.trim()&&t.set("end",td("End",e.end));let s=await eW("/api/records?".concat(t));return r({records:s.records.map(eV),hasMore:s.hasMore,nextKey:s.hasMore?s.nextKeyText:void 0})}let a=e.q.trim();if(a.length<2)throw Error("Search text must be at least 2 characters.");let n=new URLSearchParams({q:a,limit:"200",maxScan:String(tc("Max scan",e.maxScan,1e4))});if(e.prefix.trim()){let t=Number(td("Partition",e.prefix));if(t>255)throw Error("Partition prefix must be between 0 and 255.");n.set("prefix",String(t))}e.start.trim()&&n.set("start",td("Start",e.start));let l=await eW("/api/search?".concat(n));return r({records:l.records.map(eV),hasMore:l.hasMore,nextKey:l.hasMore?l.nextKey:void 0,scanned:l.scannedCount,indexed:l.indexed})}let tm=[{id:"get",label:"Get key",hint:"Exact lookup in the primary B+ tree by 64-bit key."},{id:"scan",label:"Scan range",hint:"Ordered range scan over the primary index; leave bounds empty for the full key space."},{id:"search",label:"Search text",hint:"Match value text. Uses the secondary index when it can, otherwise scans up to Max scan records."}],th="font-mono text-[13px]",tf="rounded border border-border bg-muted px-1.5 py-0.5 font-mono text-[10px]";function tp(e){let{onNotice:t}=e,[r,n]=(0,a.useState)(to),[l,o]=(0,a.useState)(null),[i,d]=(0,a.useState)(null),[c,u]=(0,a.useState)(!1),[x,m]=(0,a.useState)([]),h=(0,a.useRef)(0),f=e=>n(t=>({...t,...e})),p=(0,a.useCallback)(async e=>{let r=++h.current;u(!0),d(null);try{let t=await tx(e);if(r!==h.current)return;o(t),m(t=>[e,...t.filter(t=>tu(t)!==tu(e))].slice(0,10))}catch(e){if(r!==h.current)return;o(null),d(e.message),t(e.message,!0)}finally{r===h.current&&u(!1)}},[t]),g=tm.find(e=>e.id===r.mode);return(0,s.jsxs)("section",{"aria-labelledby":"query-title",className:"space-y-6",children:[(0,s.jsx)(tn,{id:"query-title",title:"Query",subtitle:"Read records through the engine's existing lookup, range and text-search paths."}),(0,s.jsxs)(e$,{className:"p-5",onKeyDown:e=>{(e.metaKey||e.ctrlKey)&&"Enter"===e.key&&(e.preventDefault(),p(r))},children:[(0,s.jsx)("div",{role:"tablist","aria-label":"Query mode",className:"mb-4 inline-flex rounded-lg border border-border bg-muted/40 p-0.5",children:tm.map(e=>(0,s.jsx)("button",{role:"tab",type:"button","aria-selected":r.mode===e.id,onClick:()=>f({mode:e.id}),className:"rounded-md px-3 py-1.5 text-xs font-medium transition-colors ".concat(r.mode===e.id?"bg-card text-foreground shadow-sm":"text-muted-foreground hover:text-foreground"),children:e.label},e.id))}),(0,s.jsx)("p",{className:"mb-3 text-xs text-muted-foreground",children:g.hint}),"get"===r.mode&&(0,s.jsx)(D,{"aria-label":"Key",className:th,placeholder:"Key, e.g. 360287970189639680 or 0x0500000000000001",value:r.key,onChange:e=>f({key:e.target.value})}),"scan"===r.mode&&(0,s.jsxs)("div",{className:"grid gap-3 sm:grid-cols-[1fr_1fr_120px]",children:[(0,s.jsx)(D,{"aria-label":"Start key",className:th,placeholder:"Start key (inclusive)",value:r.start,onChange:e=>f({start:e.target.value})}),(0,s.jsx)(D,{"aria-label":"End key",className:th,placeholder:"End key (inclusive)",value:r.end,onChange:e=>f({end:e.target.value})}),(0,s.jsx)(D,{"aria-label":"Limit",type:"number",min:1,max:500,className:th,placeholder:"Limit",value:r.limit,onChange:e=>f({limit:e.target.value})})]}),"search"===r.mode&&(0,s.jsxs)("div",{className:"grid gap-3 sm:grid-cols-[1fr_140px_120px]",children:[(0,s.jsx)(D,{"aria-label":"Search text",className:th,placeholder:"Text to find in values (min 2 chars)",value:r.q,onChange:e=>f({q:e.target.value})}),(0,s.jsx)(D,{"aria-label":"Partition prefix",className:th,placeholder:"Prefix 0-255 (opt.)",value:r.prefix,onChange:e=>f({prefix:e.target.value})}),(0,s.jsx)(D,{"aria-label":"Max scan",type:"number",min:1,max:1e4,className:th,placeholder:"Max scan",value:r.maxScan,onChange:e=>f({maxScan:e.target.value})})]}),(0,s.jsxs)("div",{className:"mt-4 flex items-center justify-between gap-3",children:[(0,s.jsxs)("span",{className:"text-xs text-muted-foreground",children:[(0,s.jsx)("kbd",{className:tf,children:"Cmd/Ctrl"})," + ",(0,s.jsx)("kbd",{className:tf,children:"Enter"})," to run"]}),(0,s.jsxs)(_,{onClick:()=>void p(r),disabled:c,children:[(0,s.jsx)(tr.A,{className:"mr-1.5 h-3.5 w-3.5","aria-hidden":!0}),c?"Running…":"Run"]})]})]}),i&&(0,s.jsxs)("div",{role:"alert",className:"flex items-start gap-2.5 rounded-xl border border-destructive/30 bg-destructive/10 px-4 py-3 text-sm text-destructive",children:[(0,s.jsx)(ts.A,{className:"mt-0.5 h-4 w-4 shrink-0","aria-hidden":!0}),(0,s.jsx)("span",{children:i})]}),l&&(0,s.jsx)(tl,{outcome:l,onContinue:e=>{let t={...r,start:e};n(t),p(t)}}),!l&&!i&&!c&&(0,s.jsx)("p",{className:"rounded-xl border border-dashed border-border px-4 py-10 text-center text-sm text-muted-foreground",children:"Run a query to see records here."}),x.length>0&&(0,s.jsxs)(e$,{className:"p-4",children:[(0,s.jsxs)("h2",{className:"mb-2 flex items-center gap-1.5 text-xs font-medium uppercase tracking-wide text-muted-foreground",children:[(0,s.jsx)(ta.A,{className:"h-3.5 w-3.5","aria-hidden":!0}),"Recent queries"]}),(0,s.jsx)("ul",{className:"space-y-0.5",children:x.map(e=>(0,s.jsx)("li",{children:(0,s.jsx)("button",{type:"button",onClick:()=>{n(e),p(e)},className:"w-full truncate rounded-md px-2 py-1.5 text-left font-mono text-xs text-muted-foreground hover:bg-muted hover:text-foreground",children:tu(e)})},tu(e)))})]})]})}function tg(e){let{rows:t}=e,r=t.reduce((e,t)=>e+t.count,0);return(0,s.jsxs)(e$,{className:"overflow-hidden",children:[(0,s.jsxs)("div",{className:"border-b border-border px-5 py-3.5",children:[(0,s.jsx)("h2",{className:"text-sm font-semibold text-foreground",children:"Key-space partitions"}),(0,s.jsx)("p",{className:"mt-0.5 text-xs text-muted-foreground",children:"The top 8 bits of each key select the partition; all partitions share one primary B+ tree."})]}),0===t.length?(0,s.jsx)("p",{className:"px-5 py-8 text-center text-sm text-muted-foreground",children:"No partition data yet."}):(0,s.jsxs)("table",{className:"w-full text-left text-sm",children:[(0,s.jsx)("thead",{children:(0,s.jsxs)("tr",{className:"border-b border-border text-[11px] uppercase tracking-wide text-muted-foreground",children:[(0,s.jsx)("th",{className:"px-5 py-2 font-medium",children:"Prefix"}),(0,s.jsx)("th",{className:"px-5 py-2 font-medium",children:"Label"}),(0,s.jsx)("th",{className:"px-5 py-2 text-right font-medium",children:"Records"}),(0,s.jsx)("th",{className:"w-[35%] px-5 py-2 font-medium",children:"Share"})]})}),(0,s.jsx)("tbody",{children:t.map(e=>{let t=r>0?e.count/r*100:0;return(0,s.jsxs)("tr",{className:"border-b border-border/60 last:border-0",children:[(0,s.jsxs)("td",{className:"px-5 py-2.5 font-mono text-xs",children:["0x",e.prefix.toString(16).padStart(2,"0").toUpperCase()]}),(0,s.jsx)("td",{className:"px-5 py-2.5 text-foreground",children:e.label}),(0,s.jsx)("td",{className:"px-5 py-2.5 text-right font-mono text-xs",children:e.count.toLocaleString()}),(0,s.jsx)("td",{className:"px-5 py-2.5",children:(0,s.jsxs)("div",{className:"flex items-center gap-2",children:[(0,s.jsx)("div",{className:"h-1.5 flex-1 overflow-hidden rounded-full bg-muted",children:(0,s.jsx)("div",{className:"h-full rounded-full bg-foreground/50",style:{width:"".concat(t,"%")}})}),(0,s.jsxs)("span",{className:"w-12 text-right font-mono text-xs text-muted-foreground",children:[t.toFixed(1),"%"]})]})})]},e.prefix)})})]})]})}function tb(e){let{label:t,value:r}=e;return(0,s.jsxs)("div",{className:"flex items-baseline justify-between gap-4 border-b border-border/60 py-2 last:border-0",children:[(0,s.jsx)("dt",{className:"text-xs text-muted-foreground",children:t}),(0,s.jsx)("dd",{className:"text-right font-mono text-xs text-foreground",children:r})]})}let tj=e=>void 0===e?"—":e.toLocaleString();function tv(e){let{storage:t}=e,r=t?Math.floor(t.diskSizeBytes/4096):void 0;return(0,s.jsxs)(e$,{className:"p-5",children:[(0,s.jsxs)("div",{className:"mb-3 flex items-center justify-between",children:[(0,s.jsx)("h2",{className:"text-sm font-semibold text-foreground",children:"Primary index"}),(0,s.jsx)(eX,{variant:"mono",children:"B+ tree"})]}),(0,s.jsx)("p",{className:"mb-3 text-xs text-muted-foreground",children:"Disk-backed B+ tree over 64-bit keys. Leaves are chained by next-page pointers, which range scans follow."}),(0,s.jsxs)("dl",{children:[(0,s.jsx)(tb,{label:"Key width",value:"64-bit"}),(0,s.jsx)(tb,{label:"Page size",value:"4 KiB"}),(0,s.jsx)(tb,{label:"Leaf entry",value:"18 B (8 B key + 10 B RID)"}),(0,s.jsx)(tb,{label:"Max entries / leaf",value:String(200)}),(0,s.jsx)(tb,{label:"Max keys / internal node",value:"".concat(200," (fanout ").concat(201,")")}),(0,s.jsx)(tb,{label:"Pages in data file (≈ size / 4 KiB)",value:tj(r)}),(0,s.jsx)(tb,{label:"Data file size",value:t?eP(t.diskSizeBytes):"—"})]})]})}function ty(e){let{storage:t}=e,r=t&&t.poolSize>0?Math.min(100,t.cachedPages/t.poolSize*100):0;return(0,s.jsxs)(e$,{className:"p-5",children:[(0,s.jsx)("h2",{className:"mb-3 text-sm font-semibold text-foreground",children:"Buffer pool"}),(0,s.jsx)("div",{className:"mb-3 h-1.5 overflow-hidden rounded-full bg-muted","aria-hidden":!0,children:(0,s.jsx)("div",{className:"h-full rounded-full bg-primary",style:{width:"".concat(r,"%")}})}),(0,s.jsxs)("dl",{children:[(0,s.jsx)(tb,{label:"Pool size (pages)",value:tj(null==t?void 0:t.poolSize)}),(0,s.jsx)(tb,{label:"Cached pages",value:tj(null==t?void 0:t.cachedPages)}),(0,s.jsx)(tb,{label:"Dirty pages",value:tj(null==t?void 0:t.dirtyPages)}),(0,s.jsx)(tb,{label:"Pinned pages",value:tj(null==t?void 0:t.pinnedPages)}),(0,s.jsx)(tb,{label:"Active data page",value:tj(null==t?void 0:t.activeDataPage)})]})]})}function tN(){return(0,s.jsxs)(e$,{className:"p-5",children:[(0,s.jsxs)("div",{className:"mb-3 flex items-center justify-between",children:[(0,s.jsx)("h2",{className:"text-sm font-semibold text-foreground",children:"Secondary text index"}),(0,s.jsx)(eX,{variant:"mono",children:"inverted \xb7 in-memory"})]}),(0,s.jsx)("p",{className:"mb-3 text-xs text-muted-foreground",children:"Maps each token to a sorted list of primary keys. It is rebuilt from stored records when the engine starts and kept current on every write and delete."}),(0,s.jsxs)("dl",{children:[(0,s.jsx)(tb,{label:"Tokenizer",value:"letters + digits, lowercased"}),(0,s.jsx)(tb,{label:"Min token length",value:"2"}),(0,s.jsx)(tb,{label:"Token identity",value:"64-bit FNV-1a hash"}),(0,s.jsx)(tb,{label:"Multi-word query",value:"intersection (AND)"})]}),(0,s.jsx)("p",{className:"mt-3 text-xs text-muted-foreground",children:"Index size and posting counts are not exposed by the API, so they are not shown."})]})}function tw(e){var t,r,n;let{stats:l,onRefresh:o}=e,[i,d]=(0,a.useState)(null),[c,u]=(0,a.useState)([]),[x,m]=(0,a.useState)(null),[h,f]=(0,a.useState)(!0),g=(0,a.useCallback)(async()=>{var e,t,r;f(!0),m(null);let[s,a]=await Promise.allSettled([eW("/api/stats?counts=true"),eW("/api/partitions")]);"fulfilled"===s.status?d(s.value):m(null!=(t=null==(e=s.reason)?void 0:e.message)?t:"Could not load partition counts."),"fulfilled"===a.status&&u(null!=(r=a.value)?r:[]),f(!1)},[]);(0,a.useEffect)(()=>{g()},[g]);let b=null!=(t=null==i?void 0:i.storage)?t:null==l?void 0:l.storage,j=null!=(r=null==i?void 0:i.partitionCounts)?r:null==l?void 0:l.partitionCounts,v=(0,a.useMemo)(()=>Object.entries(null!=j?j:{}).map(e=>{var t,r;let[s,a]=e,n=Number(s),l=null!=(r=null==(t=c.find(e=>e.prefix===n))?void 0:t.label)?r:en(n).label;return{prefix:n,label:l,count:a}}).filter(e=>e.count>0).sort((e,t)=>t.count-e.count),[j,c]);return(0,s.jsxs)("section",{"aria-labelledby":"indexes-title",className:"space-y-6",children:[(0,s.jsx)(tn,{id:"indexes-title",title:"Indexes",subtitle:"How records are located: the primary B+ tree and the secondary text index.",actions:(0,s.jsxs)(_,{variant:"outline",size:"sm",onClick:()=>{o(),g()},disabled:h,children:[(0,s.jsx)(p.A,{className:"mr-1.5 h-3.5 w-3.5 ".concat(h?"animate-spin":""),"aria-hidden":!0}),"Refresh"]})}),x&&(0,s.jsx)("div",{role:"alert",className:"rounded-xl border border-destructive/30 bg-destructive/10 px-4 py-3 text-sm text-destructive",children:x}),(0,s.jsxs)(e8,{cols:3,children:[(0,s.jsx)(e6,{label:"Indexes",value:"2",hint:"primary B+ tree \xb7 text index"}),(0,s.jsx)(e6,{label:"Data file",value:b?eP(b.diskSizeBytes):"—",hint:"".concat(null!=(n=null==b?void 0:b.totalPages)?n:0," pages \xd7 4 KiB")}),(0,s.jsx)(e6,{label:"Partitions in use",value:"".concat(v.length),hint:"".concat(v.reduce((e,t)=>e+t.count,0).toLocaleString()," records")})]}),(0,s.jsxs)("div",{className:"grid gap-4 lg:grid-cols-3",children:[(0,s.jsx)(tv,{storage:b}),(0,s.jsx)(ty,{storage:b}),(0,s.jsx)(tN,{})]}),(0,s.jsx)(tg,{rows:v})]})}var tk=r(2251);function tS(e){let{label:t,command:r,onNotice:n}=e,[l,o]=(0,a.useState)(!1),i=async()=>{try{await navigator.clipboard.writeText(r),o(!0),window.setTimeout(()=>o(!1),1500)}catch(e){n("Could not access the clipboard. Select the command and copy it manually.",!0)}};return(0,s.jsxs)("div",{children:[(0,s.jsx)("p",{className:"mb-1.5 text-xs font-medium text-muted-foreground",children:t}),(0,s.jsxs)("div",{className:"flex items-start justify-between gap-2 rounded-md border border-border bg-background px-3 py-2.5",children:[(0,s.jsx)("code",{className:"min-w-0 flex-1 whitespace-pre-wrap break-all font-mono text-xs text-foreground",children:r}),(0,s.jsx)(_,{variant:"ghost",size:"icon",className:"h-7 w-7 shrink-0",onClick:()=>void i(),"aria-label":"Copy ".concat(t," command"),children:l?(0,s.jsx)(tk.A,{size:14,className:"text-success"}):(0,s.jsx)(eI.A,{size:14})})]})]})}var tC=r(5646);let tL=tC.bL,tA=tC.hE,tz=tC.VY,t_=tC.ZD,tP=tC.rc;function tM(e){let{children:t,className:r,...a}=e;return(0,s.jsxs)(tC.ZL,{children:[(0,s.jsx)(tC.hJ,{className:"fixed inset-0 z-[60] bg-black/55"}),(0,s.jsx)(tC.UC,{className:S("fixed left-1/2 top-1/2 z-[60] w-[calc(100%-2rem)] max-w-md -translate-x-1/2 -translate-y-1/2 rounded-lg border bg-card p-6 text-card-foreground shadow-xl",r),...a,children:t})]})}function tR(e){let{label:t,value:r}=e;return(0,s.jsxs)("div",{className:"flex justify-between gap-4 text-xs",children:[(0,s.jsx)("span",{className:"text-muted-foreground",children:t}),(0,s.jsx)("span",{className:"font-mono text-foreground",children:r})]})}function tE(e){let{cluster:t,isAdmin:r,onNotice:n,onPromoted:l}=e,[o,i]=(0,a.useState)(!1),[d,c]=(0,a.useState)(!1),u=r?t?"leader"===t.role?{enabled:!1,reason:"This node is already the leader."}:"fenced"===t.role?{enabled:!1,reason:"Fenced at epoch ".concat(t.epoch,": rebuild this node as a follower before it can lead.")}:t.promotable?{enabled:!0,reason:""}:{enabled:!1,reason:t.promoteBlockedReason||"Not caught up with the leader."}:{enabled:!1,reason:"This node does not report cluster state."}:{enabled:!1,reason:"Only admin users can promote a node."},x=async()=>{c(!0);try{var e;let t=await fetch("/api/replication/promote",{method:"POST"}),r=await t.json().catch(()=>({}));if(!t.ok||!r.ok)throw Error(function(e,t){switch(e){case"leader_unreachable":return"The leader did not answer, so it could not be fenced. Promoting now could lose its latest writes. If the leader is truly down, use the API with acceptDataLoss.";case"catch_up_timeout":return"The old leader is fenced but this node had not caught up in time. Nothing was promoted; try again.";case"fence_refused":return"The old leader refused to be fenced: ".concat(t);default:return t}}(r.code,null!=(e=r.error)?e:"HTTP ".concat(t.status)));n("Promoted to leader. The old leader was fenced and refuses writes."),l()}catch(e){n("Promote failed: ".concat(e instanceof Error?e.message:"request failed"),!0)}finally{c(!1),i(!1)}},m=t.leaderContactMS<0?"never":"".concat((t.leaderContactMS/1e3).toFixed(1)," s ago");return(0,s.jsxs)(e$,{children:[(0,s.jsxs)(eG,{className:"flex flex-row items-center justify-between",children:[(0,s.jsx)(eY,{className:"text-sm",children:"Promotion"}),(0,s.jsxs)(eX,{variant:"mono",children:["epoch ",t.epoch]})]}),(0,s.jsxs)(eQ,{className:"space-y-3",children:[(0,s.jsxs)("div",{className:"space-y-1.5",children:[(0,s.jsx)(tR,{label:"Node",value:t.nodeID}),(0,s.jsx)(tR,{label:"Role",value:t.role}),"follower"===t.role&&(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(tR,{label:"Applied LSN",value:t.appliedLSN.toLocaleString()}),(0,s.jsx)(tR,{label:"Leader LSN (last heard)",value:t.leaderLSN.toLocaleString()}),(0,s.jsx)(tR,{label:"Lag (records)",value:t.lagLSN.toLocaleString()}),(0,s.jsx)(tR,{label:"Last heard from leader",value:m})]})]}),(0,s.jsxs)("div",{className:"flex flex-wrap items-center gap-3",children:[(0,s.jsx)(_,{size:"sm",variant:"outline",disabled:!u.enabled||d,onClick:()=>i(!0),children:"Promote to leader"}),!u.enabled&&(0,s.jsx)("p",{className:"text-xs text-muted-foreground","data-testid":"promote-reason",children:u.reason})]}),(0,s.jsx)("p",{className:"text-xs text-muted-foreground",children:"Promotion is manual. It first fences the old leader (it stops accepting writes) and waits until this node has every record the leader acknowledged. If the leader cannot be reached, Studio refuses; forcing it is possible only through the API and can lose writes. Failover is not automatic and this is not high availability."})]}),(0,s.jsx)(tL,{open:o,onOpenChange:e=>!d&&i(e),children:(0,s.jsxs)(tM,{children:[(0,s.jsxs)(tA,{className:"text-base font-semibold",children:["Promote ",t.nodeID," to leader?"]}),(0,s.jsxs)(tz,{className:"mt-2 text-sm text-muted-foreground",children:["The current leader will be fenced at epoch ",t.epoch+1," and refuse writes. Clients and other followers must be pointed at this node. The old leader cannot rejoin until its data directory is wiped and it is started as a follower."]}),(0,s.jsxs)("div",{className:"mt-5 flex justify-end gap-2",children:[(0,s.jsx)(t_,{asChild:!0,children:(0,s.jsx)(_,{variant:"outline",size:"sm",disabled:d,children:"Cancel"})}),(0,s.jsx)(tP,{asChild:!0,children:(0,s.jsx)(_,{size:"sm",disabled:d,onClick:e=>{e.preventDefault(),x()},children:d?"Promoting…":"Promote"})})]})]})})]})}function tT(e){let{label:t,value:r,tone:a}=e;return(0,s.jsxs)("div",{className:"rounded-xl border border-border bg-card p-4",children:[(0,s.jsx)("p",{className:"text-xs text-muted-foreground",children:t}),(0,s.jsx)("p",{className:"mt-1 font-mono text-xl font-semibold ".concat(null!=a?a:"text-foreground"),children:r})]})}function tI(e){var t,r,n,l,o;let{stats:i,user:d,onNotice:c}=e,[u,x]=(0,a.useState)(null),[m,h]=(0,a.useState)(null),f=(null==d?void 0:d.role)==="admin",p=(0,a.useCallback)(async()=>{if(f)try{let e=await fetch("/api/replication");if(!e.ok)throw Error("HTTP ".concat(e.status));x(await e.json()),h(null)}catch(e){h(e instanceof Error?e.message:"request failed")}},[f]);(0,a.useEffect)(()=>{if(p(),!f)return;let e=window.setInterval(()=>void p(),5e3);return()=>window.clearInterval(e)},[p,f]);let g=null!=(r=null!=(t=null==u?void 0:u.role)?t:null==i?void 0:i.role)?r:"standalone",b=null!=(l=null!=(n=null==u?void 0:u.lsn)?n:null==i?void 0:i.lsn)?l:0,j=null!=(o=null==u?void 0:u.replicas)?o:[],v=j.filter(e=>e.connected).length,y="leader"===g;return(0,s.jsxs)("section",{"aria-labelledby":"replication-title",className:"space-y-6",children:[(0,s.jsx)(tn,{id:"replication-title",title:"Replication",subtitle:"Leader/follower WAL streaming. Only data the engine tracks is shown.",actions:f&&(0,s.jsx)(_,{variant:"outline",size:"sm",onClick:()=>void p(),children:"Refresh"})}),(0,s.jsxs)("div",{className:"grid gap-4 sm:grid-cols-3",children:[(0,s.jsx)(tT,{label:"This node",value:g,tone:y?"text-primary":void 0}),(0,s.jsx)(tT,{label:"Current LSN",value:b.toLocaleString()}),(0,s.jsx)(tT,{label:"Replicas connected",value:(null==u?void 0:u.tracked)?String(v):"n/a",tone:(null==u?void 0:u.tracked)&&v>0?"text-success":"text-muted-foreground"})]}),f&&(null==u?void 0:u.cluster)&&(0,s.jsx)(tE,{cluster:u.cluster,isAdmin:f,onNotice:c,onPromoted:()=>void p()}),(0,s.jsxs)(e$,{children:[(0,s.jsxs)(eG,{className:"flex flex-row items-center justify-between",children:[(0,s.jsx)(eY,{className:"text-sm",children:"Replicas"}),(null==u?void 0:u.tracked)&&(0,s.jsxs)(eX,{variant:"mono",children:[j.length," seen since start"]})]}),(0,s.jsxs)(eQ,{children:[f?m?(0,s.jsxs)("p",{className:"text-sm text-destructive",children:["Could not load replication status: ",m]}):u?u.tracked?0===j.length?(0,s.jsx)("p",{className:"text-sm text-muted-foreground",children:"No replicas connected. A follower appears here once it opens a WAL stream to this leader."}):(0,s.jsx)("div",{className:"overflow-x-auto",children:(0,s.jsxs)("table",{className:"w-full text-left text-xs",children:[(0,s.jsx)("thead",{className:"text-muted-foreground",children:(0,s.jsxs)("tr",{className:"border-b border-border",children:[(0,s.jsx)("th",{className:"py-2 pr-4 font-medium",children:"Replica"}),(0,s.jsx)("th",{className:"py-2 pr-4 font-medium",children:"Address"}),(0,s.jsx)("th",{className:"py-2 pr-4 font-medium",children:"State"}),(0,s.jsx)("th",{className:"py-2 pr-4 text-right font-medium",children:"Acked LSN"}),(0,s.jsx)("th",{className:"py-2 text-right font-medium",children:"Lag (LSN)"})]})}),(0,s.jsx)("tbody",{className:"divide-y divide-border/60",children:j.map(e=>(0,s.jsxs)("tr",{children:[(0,s.jsx)("td",{className:"py-2.5 pr-4 font-mono text-foreground",children:e.id}),(0,s.jsx)("td",{className:"py-2.5 pr-4 font-mono text-muted-foreground",children:e.address||"—"}),(0,s.jsx)("td",{className:"py-2.5 pr-4",children:(0,s.jsx)(eX,{variant:e.connected?"lime":"outline",children:e.connected?"streaming":"disconnected"})}),(0,s.jsx)("td",{className:"py-2.5 pr-4 text-right font-mono",children:e.ackedLSN.toLocaleString()}),(0,s.jsx)("td",{className:"py-2.5 text-right font-mono ".concat(e.lagLSN>0?"text-warning":"text-foreground"),children:e.lagLSN.toLocaleString()})]},e.id))})]})}):(0,s.jsx)("p",{className:"text-sm text-muted-foreground",children:"Replicas are not tracked on this node. Only a leader sees its followers; open Studio on the leader to view them."}):(0,s.jsx)("p",{className:"text-sm text-muted-foreground",children:"Loading…"}):(0,s.jsx)("p",{className:"text-sm text-muted-foreground",children:"Replica details are visible to admin users only."}),(null==u?void 0:u.tracked)&&j.length>0&&(0,s.jsx)("p",{className:"mt-3 text-xs text-muted-foreground",children:"Lag is leader LSN minus the replica's last acknowledgement. Acks are recorded as followers apply records, and a disconnected replica keeps its last ack until the leader restarts. To promote a replica, open Studio on that replica: promotion runs on the node being promoted."})]})]}),(0,s.jsxs)(e$,{children:[(0,s.jsx)(eG,{children:(0,s.jsx)(eY,{className:"text-sm",children:"Start a follower"})}),(0,s.jsxs)(eQ,{className:"space-y-3",children:[(0,s.jsx)(tS,{label:"Follower node (flags from cmd/server)",command:"go run ./cmd/server -role follower -leader-addr :50051 \\\n -replica-id replica-1 -data-dir ./data-follower -port 50052 -web-addr 127.0.0.1:8081",onNotice:c}),(0,s.jsxs)("p",{className:"text-xs text-muted-foreground",children:["A follower runs read-only and streams the leader's WAL, replaying its archived generation first. Use a unique ",(0,s.jsx)("span",{className:"font-mono",children:"-replica-id"})," and its own data directory. Promotion is manual; automatic failover is not implemented."]})]})]})]})}var tD=r(9201),tB=r(3489);function tO(e){let{v:t}=e;return t?t.ok?(0,s.jsx)(eX,{variant:"lime",title:"Checked ".concat(new Date(t.at).toLocaleString()),children:"Verified"}):(0,s.jsx)(eX,{variant:"mono",title:t.error,children:"FAILED"}):(0,s.jsx)(eX,{variant:"outline",title:"Not verified since the server started",children:"Unverified"})}function tU(e){var t,r;let{canWrite:n,onNotice:l,reloadKey:o}=e,[i,d]=(0,a.useState)(null),[c,u]=(0,a.useState)("loading"),[x,m]=(0,a.useState)(null),[h,f]=(0,a.useState)(null),p=(0,a.useCallback)(async()=>{try{let e=await fetch("/api/backups",{cache:"no-store"});if(404===e.status)return u("unavailable");if(!e.ok)throw Error("HTTP ".concat(e.status));d(await e.json()),u("ok")}catch(e){u("error")}},[]);if((0,a.useEffect)(()=>{n&&p()},[p,n,o]),!n||"unavailable"===c)return null;let g=async e=>{m(e);try{let t=await fetch("/api/backups/verify?id=".concat(encodeURIComponent(e)),{method:"POST"});if(!t.ok)throw Error((await t.text()).trim());let r=await t.json();l(r.ok?"".concat(e," verified: checksum and manifest match."):"".concat(e," FAILED verification: ").concat(r.error),!r.ok),await p()}catch(e){l("Verify failed: ".concat(e instanceof Error?e.message:"request failed"),!0)}finally{m(null)}},b=async e=>{try{var t;let r;await navigator.clipboard.writeText((r=null!=(t=null==i?void 0:i.dir)?t:"./backups","beastctl restore --backup ".concat(r,"/").concat(e," --dest ./restored"))),f(e),window.setTimeout(()=>f(null),1500)}catch(e){l("Could not access the clipboard.",!0)}},j=null!=(t=null==i?void 0:i.backups)?t:[];return(0,s.jsxs)(e$,{children:[(0,s.jsxs)(eG,{children:[(0,s.jsx)(eY,{className:"text-sm",children:"Backups"}),(0,s.jsxs)(eZ,{children:["Stored in ",(0,s.jsx)("span",{className:"font-mono",children:null!=(r=null==i?void 0:i.dir)?r:"…"})," on this host.",i&&i.intervalMinutes>0?" Automatic: every ".concat(i.intervalMinutes," min, keeping the newest ").concat(i.keep," scheduled."):" Automatic backups are off."]})]}),(0,s.jsxs)(eQ,{className:"overflow-x-auto p-0",children:[(0,s.jsxs)("table",{className:"w-full min-w-[720px] text-sm",children:[(0,s.jsx)("thead",{children:(0,s.jsxs)("tr",{className:"border-y border-border text-left text-[11px] uppercase tracking-wide text-muted-foreground",children:[(0,s.jsx)("th",{scope:"col",className:"px-5 py-2.5 font-medium",children:"Backup"}),(0,s.jsx)("th",{scope:"col",className:"px-3 py-2.5 font-medium",children:"Created"}),(0,s.jsx)("th",{scope:"col",className:"px-3 py-2.5 font-medium",children:"Size"}),(0,s.jsx)("th",{scope:"col",className:"px-3 py-2.5 font-medium",children:"Type"}),(0,s.jsx)("th",{scope:"col",className:"px-3 py-2.5 font-medium",children:"Status"}),(0,s.jsx)("th",{scope:"col",className:"px-5 py-2.5",children:(0,s.jsx)("span",{className:"sr-only",children:"Actions"})})]})}),(0,s.jsx)("tbody",{children:j.map(e=>{var t;return(0,s.jsxs)("tr",{className:"border-b border-border last:border-0",children:[(0,s.jsx)("td",{className:"px-5 py-3 font-mono text-xs",children:e.id}),(0,s.jsx)("td",{className:"px-3 py-3 text-xs text-muted-foreground",children:new Date(e.created_at).toLocaleString()}),(0,s.jsx)("td",{className:"px-3 py-3 font-mono text-xs",children:eP(e.file_size)}),(0,s.jsx)("td",{className:"px-3 py-3 text-xs text-muted-foreground",title:e.created_by?"by ".concat(e.created_by):void 0,children:null!=(t=e.kind)?t:"manual"}),(0,s.jsx)("td",{className:"px-3 py-3",children:(0,s.jsx)(tO,{v:e.verified})}),(0,s.jsx)("td",{className:"px-5 py-3",children:(0,s.jsxs)("div",{className:"flex justify-end gap-1",children:[(0,s.jsxs)(_,{size:"sm",variant:"ghost",disabled:x===e.id,onClick:()=>void g(e.id),"aria-label":"Verify ".concat(e.id),children:[(0,s.jsx)(tD.A,{size:14})," ",x===e.id?"Checking…":"Verify"]}),(0,s.jsxs)("a",{href:"/api/backups/download?id=".concat(encodeURIComponent(e.id)),download:!0,"aria-label":"Download ".concat(e.id),className:"inline-flex h-8 items-center justify-center gap-2 rounded-md px-3 text-xs font-medium transition-colors hover:bg-muted hover:text-foreground",children:[(0,s.jsx)(tB.A,{size:14})," Download"]}),(0,s.jsxs)(_,{size:"sm",variant:"ghost",onClick:()=>void b(e.id),"aria-label":"Copy restore command for ".concat(e.id),title:"Restore runs offline with beastctl",children:[h===e.id?(0,s.jsx)(tk.A,{size:14,className:"text-success"}):(0,s.jsx)(eI.A,{size:14})," Restore cmd"]})]})})]},e.id)})})]}),"loading"===c&&(0,s.jsx)("p",{className:"p-5 text-sm text-muted-foreground",children:"Loading backups…"}),"error"===c&&(0,s.jsx)("p",{className:"p-5 text-sm text-destructive",children:"Could not read the backup list."}),"ok"===c&&0===j.length&&(0,s.jsx)("p",{className:"p-5 text-sm text-muted-foreground",children:"No backups yet. Use “Back up now”."})]})]})}function tF(e){let{title:t,description:r,fields:n,onNotice:l}=e,[o,i]=(0,a.useState)(null),[d,c]=(0,a.useState)({}),[u,x]=(0,a.useState)(!1),[m,h]=(0,a.useState)(!1),f=e=>{i(e),c(Object.fromEntries(n.map(t=>[t.name,String(e.values[t.name])])))},p=(0,a.useCallback)(async()=>{try{let e=await fetch("/api/settings/runtime",{cache:"no-store"});if(!e.ok)throw Error("HTTP ".concat(e.status));f(await e.json()),h(!1)}catch(e){h(!0)}},[]);if((0,a.useEffect)(()=>{p()},[p]),m||!o)return null;let g=n.map(e=>({f:e,n:Number(d[e.name]),range:o.ranges[e.name]})),b=g.some(e=>{let{n:t,range:r}=e;return!Number.isInteger(t)||tr.max}),j=g.some(e=>{let{f:t,n:r}=e;return r!==o.values[t.name]}),v=async()=>{x(!0);try{let e=Object.fromEntries(g.filter(e=>{let{f:t,n:r}=e;return r!==o.values[t.name]}).map(e=>{let{f:t,n:r}=e;return[t.name,r]})),t=await fetch("/api/settings/runtime",{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!t.ok)throw Error((await t.text()).trim()||"HTTP ".concat(t.status));f(await t.json()),l("Settings saved. They apply now and survive a restart.")}catch(e){l("Could not save settings: ".concat(e instanceof Error?e.message:"request failed"),!0)}finally{x(!1)}};return(0,s.jsxs)(e$,{children:[(0,s.jsxs)(eG,{children:[(0,s.jsx)(eY,{className:"text-base",children:t}),(0,s.jsx)(eZ,{children:r})]}),(0,s.jsxs)(eQ,{className:"space-y-4",children:[g.map(e=>{var t;let{f:r,n:a,range:n}=e,l=!Number.isInteger(a)||an.max;return(0,s.jsxs)("label",{className:"block space-y-1.5 text-sm",children:[(0,s.jsx)("span",{className:"text-muted-foreground",children:r.label}),(0,s.jsxs)("span",{className:"flex items-center gap-2",children:[(0,s.jsx)(D,{type:"number",inputMode:"numeric",value:null!=(t=d[r.name])?t:"",min:n.min,max:n.max,onChange:e=>c(t=>({...t,[r.name]:e.target.value})),"aria-invalid":l,className:"h-9 w-32 font-mono"}),(0,s.jsxs)("span",{className:"text-xs text-muted-foreground",children:[r.unit," \xb7 ",n.min.toLocaleString(),"–",n.max.toLocaleString()]})]}),l&&(0,s.jsxs)("span",{className:"block text-xs text-destructive",children:["Enter a whole number from ",n.min," to ",n.max,"."]}),r.hint&&!l&&(0,s.jsx)("span",{className:"block text-xs text-muted-foreground",children:r.hint})]},r.name)}),(0,s.jsxs)("div",{className:"flex justify-end gap-2",children:[(0,s.jsx)(_,{variant:"outline",size:"sm",disabled:!j||u,onClick:()=>f(o),children:"Reset"}),(0,s.jsx)(_,{size:"sm",disabled:!j||b||u,onClick:()=>void v(),children:u?"Saving…":"Save changes"})]})]})]})}let tH=["Stop the node (or back up a copy of its data directory). beastctl opens the data files directly and there is no file lock.","Run verify on the bundle before relying on it; restore also verifies first.","Restore into an empty directory. Without --force it refuses to overwrite an existing beast.bin.","Restore removes any old beast.wal at the destination and opens the result once to count records.","Point the server at the restored directory with -data-dir and confirm the record count before cutting over.","Keep at least one copy off the host. Retention and off-host copy are not managed by BeastDB."];function tW(e){let{label:t,value:r,tone:a}=e;return(0,s.jsxs)("div",{className:"flex items-center justify-between py-2.5 text-xs",children:[(0,s.jsx)("span",{className:"text-muted-foreground",children:t}),(0,s.jsx)("span",{className:"font-mono ".concat(null!=a?a:"text-foreground"),children:r})]})}function tK(e){var t,r,n,l,o,i,d,c,u,x,m,h,f,p,g,b;let{stats:j,user:v,onNotice:y,onRefresh:N}=e,[w,k]=(0,a.useState)(!1),[S,C]=(0,a.useState)(!1),[L,A]=(0,a.useState)(0),z=null==j?void 0:j.storage,P=null!=(t=null==z?void 0:z.dirtyPages)?t:0,M=(null==v?void 0:v.role)==="admin",R=async()=>{k(!0);try{let e=await fetch("/api/checkpoint",{method:"POST"});if(!e.ok){let t=(await e.text()).trim();throw Error(t||"HTTP ".concat(e.status))}y("Checkpoint complete: dirty pages flushed and the WAL rotated."),N()}catch(e){y("Checkpoint failed: ".concat(e instanceof Error?e.message:"request failed"),!0)}finally{k(!1)}},E=async()=>{C(!0);try{let e=await fetch("/api/backups",{method:"POST"});if(!e.ok)throw Error((await e.text()).trim()||"HTTP ".concat(e.status));let t=await e.json();y("Backup ".concat(t.backup.id," created in ").concat((t.tookMs/1e3).toFixed(1)," s and verified.")),A(e=>e+1)}catch(e){y("Backup failed: ".concat(e instanceof Error?e.message:"request failed"),!0)}finally{C(!1)}};return(0,s.jsxs)("section",{"aria-labelledby":"backups-title",className:"space-y-6",children:[(0,s.jsx)(tn,{id:"backups-title",title:"Backups",subtitle:"Create, verify and download backups of this node. Restoring runs offline with beastctl.",actions:M?(0,s.jsx)(_,{size:"sm",onClick:()=>void E(),disabled:S,title:"Reads and writes pause while the data file is copied",children:S?"Backing up…":"Back up now"}):void 0}),(0,s.jsxs)(e8,{children:[(0,s.jsx)(e6,{label:"Last checkpoint",value:(null==z?void 0:z.lastCheckpointUnix)?new Date(1e3*z.lastCheckpointUnix).toLocaleTimeString():"None yet",hint:(null==z?void 0:z.lastCheckpointUnix)?"LSN ".concat((null!=(r=z.lastCheckpointLSN)?r:0).toLocaleString()):"since this process started",tone:(null==z?void 0:z.lastCheckpointUnix)?"success":"muted"}),(0,s.jsx)(e6,{label:"WAL size",value:eP(null!=(n=null==z?void 0:z.walSizeBytes)?n:0),hint:"LSN ".concat((null!=(o=null!=(l=null==z?void 0:z.currentLSN)?l:null==j?void 0:j.lsn)?o:0).toLocaleString())}),(0,s.jsx)(e6,{label:"Dirty pages",value:"".concat(P),hint:"of ".concat(null!=(i=null==z?void 0:z.poolSize)?i:0," frames"),tone:P>0?"warning":"muted"}),(0,s.jsx)(e6,{label:"Data file",value:eP(null!=(d=null==z?void 0:z.diskSizeBytes)?d:0),hint:"".concat(null!=(c=null==z?void 0:z.totalPages)?c:0," pages")})]}),(0,s.jsx)(tU,{canWrite:M,onNotice:y,reloadKey:L}),M&&(0,s.jsx)(tF,{title:"Automatic backups",description:"Off by default. Reads and writes pause while each backup copies the data file, so choose a quiet interval. Manual backups are never pruned.",onNotice:(e,t)=>{y(e,t),A(e=>e+1)},fields:[{name:"backup_interval_minutes",label:"Back up every",unit:"minutes (0 = off, otherwise 5 or more)"},{name:"backup_keep",label:"Keep the newest",unit:"scheduled backups"}]}),(0,s.jsxs)("div",{className:"grid gap-4 lg:grid-cols-2",children:[(0,s.jsxs)(e$,{children:[(0,s.jsxs)(eG,{className:"flex flex-row items-center justify-between",children:[(0,s.jsx)(eY,{className:"text-sm",children:"Durability"}),M&&(0,s.jsx)(_,{size:"sm",onClick:()=>void R(),disabled:w,children:w?"Checkpointing…":"Checkpoint now"})]}),(0,s.jsxs)(eQ,{className:"divide-y divide-border/60",children:[(0,s.jsx)(tW,{label:"Current LSN",value:(null!=(x=null!=(u=null==z?void 0:z.currentLSN)?u:null==j?void 0:j.lsn)?x:0).toLocaleString()}),(0,s.jsx)(tW,{label:"WAL size",value:eP(null!=(m=null==z?void 0:z.walSizeBytes)?m:0)}),(0,s.jsx)(tW,{label:"Dirty pages",value:"".concat(P," of ").concat(null!=(h=null==z?void 0:z.poolSize)?h:0),tone:P>0?"text-warning":void 0}),(0,s.jsx)(tW,{label:"Last checkpoint LSN",value:(null!=(f=null==z?void 0:z.lastCheckpointLSN)?f:0).toLocaleString()}),(0,s.jsx)(tW,{label:"Last checkpoint",value:(b=null==z?void 0:z.lastCheckpointUnix)?new Date(1e3*b).toLocaleString():"none since this process started"}),(0,s.jsx)(tW,{label:"Data file",value:"".concat(eP(null!=(p=null==z?void 0:z.diskSizeBytes)?p:0)," (").concat(null!=(g=null==z?void 0:z.totalPages)?g:0," pages)")})]}),(0,s.jsx)(eQ,{className:"pt-0",children:(0,s.jsxs)("p",{className:"text-xs text-muted-foreground",children:["A checkpoint flushes dirty pages, syncs the data file, then rotates the WAL. The server also checkpoints automatically when the WAL passes ",(0,s.jsx)("span",{className:"font-mono",children:"-wal-checkpoint-mb"}),".",!M&&" Only admin users can trigger one."]})})]}),(0,s.jsxs)(e$,{children:[(0,s.jsx)(eG,{children:(0,s.jsx)(eY,{className:"text-sm",children:"How to back up"})}),(0,s.jsxs)(eQ,{className:"space-y-3",children:[(0,s.jsx)(tS,{label:"1. Create a bundle (data.bin + manifest.json with SHA-256)",command:"beastctl backup --db ./data/beast.bin --wal ./data/beast.wal --out ./backup",onNotice:y}),(0,s.jsx)(tS,{label:"2. Verify the manifest and checksum",command:"beastctl verify --backup ./backup",onNotice:y}),(0,s.jsx)(tS,{label:"3. Restore into a destination directory",command:"beastctl restore --backup ./backup --dest ./restored",onNotice:y}),(0,s.jsxs)("p",{className:"text-xs text-muted-foreground",children:["Add ",(0,s.jsx)("span",{className:"font-mono",children:"--force"})," to restore only when you intend to overwrite an existing database. Defaults shown match the CLI."]})]})]})]}),(0,s.jsxs)("div",{className:"grid gap-4 lg:grid-cols-2",children:[(0,s.jsxs)(e$,{children:[(0,s.jsx)(eG,{children:(0,s.jsx)(eY,{className:"text-sm",children:"Restore safety checklist"})}),(0,s.jsx)(eQ,{children:(0,s.jsx)("ol",{className:"list-decimal space-y-2 pl-5 text-xs text-muted-foreground marker:font-mono",children:tH.map(e=>(0,s.jsx)("li",{children:e},e))})})]}),(0,s.jsxs)(e$,{children:[(0,s.jsx)(eG,{children:(0,s.jsx)(eY,{className:"text-sm",children:"Not available yet"})}),(0,s.jsxs)(eQ,{className:"space-y-3 text-xs",children:[["Point-in-time recovery (PITR)","Off-host copy","Restore from Studio"].map(e=>(0,s.jsxs)("div",{className:"flex items-center justify-between",children:[(0,s.jsx)("span",{className:"text-foreground",children:e}),(0,s.jsx)(eX,{variant:"outline",children:"Planned"})]},e)),(0,s.jsx)("p",{className:"text-muted-foreground",children:"These have no controls here. Copy bundles off the host yourself (Download above), and restore with the beastctl command, which verifies the bundle first."})]})]})]})]})}var tq=r(1270);let tV=["admin","writer","viewer","replication"],tJ=[{label:"Never",seconds:0},{label:"24 hours",seconds:86400},{label:"7 days",seconds:604800},{label:"30 days",seconds:2592e3},{label:"90 days",seconds:7776e3},{label:"1 year",seconds:31536e3}];function t$(e,t){let[r,s]=(0,a.useState)([]),[n,l]=(0,a.useState)(!1),[o,i]=(0,a.useState)(!1),[d,c]=(0,a.useState)(null),u=(0,a.useCallback)(async()=>{l(!0);try{let e=await fetch("/api/tokens");if(401===e.status||403===e.status){i(!0),s([]);return}if(!e.ok)throw Error(await e.text()||"HTTP ".concat(e.status));let t=await e.json();i(!1),c(null),s(t.tokens||[])}catch(e){c(e.message)}finally{l(!1)}},[]);(0,a.useEffect)(()=>{e&&u()},[e,u]);let x=(0,a.useCallback)(async e=>{try{let r=await fetch("/api/tokens",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:e.name.trim(),role:e.role,...e.expiresInSeconds>0?{expires_in_seconds:e.expiresInSeconds}:{}})});if(!r.ok)throw Error(await r.text()||"HTTP ".concat(r.status));let s=await r.json();return t("API token generated and saved in partition 0x05."),await u(),s.token}catch(e){return t(e.message,!0),null}},[t,u]),m=(0,a.useCallback)(async e=>{try{let r=await fetch("/api/tokens?id=".concat(encodeURIComponent(e)),{method:"DELETE"});if(!r.ok)throw Error(await r.text()||"HTTP ".concat(r.status));return t("Token revoked."),await u(),!0}catch(e){return t(e.message,!0),!1}},[t,u]);return{tokens:r,loading:n,forbidden:o,error:d,refresh:u,create:x,revoke:m}}let tG="h-9 w-full rounded-md border border-border bg-card px-3 text-sm text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring";function tY(e){let{onCreate:t,onCancel:r}=e,[l,o]=(0,a.useState)(""),[i,d]=(0,a.useState)("viewer"),[c,u]=(0,a.useState)(0),[x,m]=(0,a.useState)(!1),h=async e=>{e.preventDefault(),l.trim()&&!x&&(m(!0),await t({name:l,role:i,expiresInSeconds:c}),m(!1))};return(0,s.jsx)(e$,{className:"p-5",children:(0,s.jsxs)("form",{onSubmit:h,className:"space-y-4",children:[(0,s.jsxs)("div",{className:"flex items-center justify-between",children:[(0,s.jsx)("h2",{className:"text-sm font-semibold",children:"Create key"}),(0,s.jsx)("button",{type:"button",onClick:r,"aria-label":"Close form",className:"text-muted-foreground hover:text-foreground",children:(0,s.jsx)(n.A,{size:14})})]}),(0,s.jsxs)("div",{className:"grid gap-4 sm:grid-cols-3",children:[(0,s.jsxs)("label",{className:"space-y-1.5 text-xs text-muted-foreground sm:col-span-1",children:["Name",(0,s.jsx)(D,{value:l,onChange:e=>o(e.target.value),placeholder:"e.g. analytics-service",maxLength:100,autoFocus:!0,required:!0})]}),(0,s.jsxs)("label",{className:"space-y-1.5 text-xs text-muted-foreground",children:["Role",(0,s.jsx)("select",{value:i,onChange:e=>d(e.target.value),className:tG,children:tV.map(e=>(0,s.jsx)("option",{value:e,children:e},e))})]}),(0,s.jsxs)("label",{className:"space-y-1.5 text-xs text-muted-foreground",children:["Expires",(0,s.jsx)("select",{value:c,onChange:e=>u(Number(e.target.value)),className:tG,children:tJ.map(e=>(0,s.jsx)("option",{value:e.seconds,children:e.label},e.seconds))})]})]}),(0,s.jsxs)("div",{className:"flex justify-end gap-2",children:[(0,s.jsx)(_,{type:"button",variant:"outline",size:"sm",onClick:r,children:"Cancel"}),(0,s.jsx)(_,{type:"submit",size:"sm",disabled:!l.trim()||x,children:x?"Creating…":"Create key"})]})]})})}function tZ(e){let{secret:t,onDismiss:r}=e,[n,l]=(0,a.useState)(!1),o=async()=>{try{await navigator.clipboard.writeText(t),l(!0),setTimeout(()=>l(!1),2e3)}catch(e){}};return(0,s.jsxs)(e$,{className:"border-primary/40 bg-primary/5 p-5",role:"status",children:[(0,s.jsxs)("div",{className:"flex flex-wrap items-center justify-between gap-2",children:[(0,s.jsx)("h2",{className:"text-sm font-semibold text-foreground",children:"Copy your new key now"}),(0,s.jsx)("span",{className:"text-xs text-muted-foreground",children:"It is shown once and cannot be retrieved later."})]}),(0,s.jsxs)("div",{className:"mt-3 flex items-center gap-2 rounded-md border border-border bg-muted px-3 py-2",children:[(0,s.jsx)("code",{className:"min-w-0 flex-1 select-all break-all font-mono text-xs text-foreground",children:t}),(0,s.jsxs)(_,{size:"sm",variant:"outline",onClick:o,"aria-label":"Copy key",children:[n?(0,s.jsx)(tk.A,{size:14}):(0,s.jsx)(eI.A,{size:14}),n?"Copied":"Copy"]})]}),(0,s.jsx)("div",{className:"mt-3 flex justify-end",children:(0,s.jsx)(_,{size:"sm",variant:"ghost",onClick:r,children:"I have saved it"})})]})}let tQ={admin:"border-warning/40 bg-warning/10 text-warning",writer:"border-info/40 bg-info/10 text-info",viewer:"border-border bg-muted text-muted-foreground",replication:"border-border bg-transparent text-muted-foreground"};function tX(e){var t;let{role:r}=e;return(0,s.jsx)("span",{className:S("inline-flex items-center rounded-md border px-2 py-0.5 font-mono text-[10px] font-semibold uppercase tracking-wider",null!=(t=tQ[r])?t:tQ.viewer),children:r})}let t0=[{role:"viewer",can:"Read data",methods:"Get, Scan"},{role:"writer",can:"Read and write data",methods:"Get, Scan, Put, Delete, BatchWrite"},{role:"replication",can:"Follow the WAL as a replica",methods:"StreamWAL, Ack"},{role:"admin",can:"Everything above",methods:"All gRPC methods listed here"}];function t1(){return(0,s.jsx)("ul",{"aria-label":"Roles",className:"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",children:t0.map(e=>(0,s.jsx)("li",{children:(0,s.jsxs)(e$,{className:"h-full px-5 py-4",children:[(0,s.jsx)(tX,{role:e.role}),(0,s.jsx)("p",{className:"mt-3 text-base font-semibold text-foreground",children:e.can}),(0,s.jsx)("p",{className:"mt-1 font-mono text-[11px] text-muted-foreground",children:e.methods})]})},e.role))})}function t2(){return(0,s.jsxs)("p",{className:"rounded-lg border border-border bg-muted/40 px-4 py-3 text-xs text-muted-foreground",children:["Any gRPC method not listed for a role is denied. The server's static ",(0,s.jsx)("code",{className:"font-mono",children:"--api-token"})," always acts as admin. Expired tokens are rejected as unauthenticated. Studio sign-in accounts are separate: they are only ",(0,s.jsx)("span",{className:"font-mono",children:"admin"})," or"," ",(0,s.jsx)("span",{className:"font-mono",children:"viewer"}),", and only admins can list, create or revoke keys."]})}let t5=e=>e?new Date(e).toLocaleDateString(void 0,{year:"numeric",month:"short",day:"numeric"}):"—";function t3(e){let{token:t}=e;return t.expires_at?!function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:Date.now();return!!e.expires_at&&new Date(e.expires_at).getTime()<=t}(t)?(0,s.jsx)("span",{title:new Date(t.expires_at).toLocaleString(),children:t5(t.expires_at)}):(0,s.jsxs)("span",{className:"text-destructive",children:["Expired ",t5(t.expires_at)]}):(0,s.jsx)("span",{className:"text-muted-foreground",children:"Never"})}function t4(e){let{tokens:t,onRevoke:r}=e,a=[...t].sort((e,t)=>new Date(t.created_at).getTime()-new Date(e.created_at).getTime());return(0,s.jsx)(e$,{className:"overflow-x-auto",children:(0,s.jsxs)("table",{className:"w-full min-w-[720px] text-sm",children:[(0,s.jsx)("thead",{children:(0,s.jsxs)("tr",{className:"border-b border-border text-left text-xs text-muted-foreground",children:[(0,s.jsx)("th",{scope:"col",className:"px-5 py-3 font-medium",children:"Name"}),(0,s.jsx)("th",{scope:"col",className:"px-3 py-3 font-medium",children:"Role"}),(0,s.jsx)("th",{scope:"col",className:"px-3 py-3 font-medium",children:"Key"}),(0,s.jsx)("th",{scope:"col",className:"px-3 py-3 font-medium",children:"Created"}),(0,s.jsx)("th",{scope:"col",className:"px-3 py-3 font-medium",children:"Expires"}),(0,s.jsx)("th",{scope:"col",className:"px-3 py-3 font-medium",title:"Since the server last started; not stored on disk",children:"Last used"}),(0,s.jsx)("th",{scope:"col",className:"px-5 py-3",children:(0,s.jsx)("span",{className:"sr-only",children:"Actions"})})]})}),(0,s.jsx)("tbody",{children:a.map(e=>(0,s.jsxs)("tr",{className:"border-b border-border last:border-0",children:[(0,s.jsxs)("td",{className:"px-5 py-3",children:[(0,s.jsx)("span",{className:"font-medium text-foreground",children:e.name}),e.created_by&&(0,s.jsxs)("span",{className:"block text-[11px] text-muted-foreground",children:["by ",e.created_by]})]}),(0,s.jsx)("td",{className:"px-3 py-3",children:(0,s.jsx)(tX,{role:e.role})}),(0,s.jsx)("td",{className:"px-3 py-3 font-mono text-xs text-muted-foreground",children:e.masked_token}),(0,s.jsx)("td",{className:"px-3 py-3 text-muted-foreground",title:new Date(e.created_at).toLocaleString(),children:eM(e.created_at)}),(0,s.jsx)("td",{className:"px-3 py-3 font-mono text-xs",children:(0,s.jsx)(t3,{token:e})}),(0,s.jsx)("td",{className:"px-3 py-3 text-xs text-muted-foreground",title:e.last_used_at?new Date(e.last_used_at).toLocaleString():"Not used since the server started",children:e.last_used_at?eM(e.last_used_at):"—"}),(0,s.jsx)("td",{className:"px-5 py-3 text-right",children:(0,s.jsxs)(_,{size:"sm",variant:"ghost",onClick:()=>r(e),"aria-label":"Revoke ".concat(e.name),className:"text-muted-foreground hover:text-destructive",children:[(0,s.jsx)(eL.A,{size:14})," Revoke"]})})]},e.id))})]})})}function t6(e){let{user:t,onNotice:r}=e,n=(null==t?void 0:t.role)==="admin",{tokens:l,loading:o,forbidden:i,error:d,create:c,revoke:u}=t$(n,r),[m,h]=(0,a.useState)(!1),[f,p]=(0,a.useState)(null),[g,b]=(0,a.useState)(null),j=!n||i,v=(0,s.jsx)(tn,{id:"keys-title",title:"API Keys & Roles",subtitle:"Bearer tokens for gRPC clients and scrapers. Stored in partition 0x05; only a hash is kept.",actions:j||m?void 0:(0,s.jsxs)(_,{size:"sm",onClick:()=>{h(!0),p(null)},children:[(0,s.jsx)(K.A,{size:14})," Create key"]})});return(0,s.jsxs)("section",{"aria-labelledby":"keys-title",className:"space-y-6",children:[v,(0,s.jsx)(t1,{}),j?(0,s.jsxs)(e$,{className:"flex flex-col items-center gap-2 px-6 py-14 text-center",role:"status",children:[(0,s.jsx)(tq.A,{size:22,className:"text-muted-foreground"}),(0,s.jsx)("p",{className:"text-sm font-semibold",children:"Admin only"}),(0,s.jsx)("p",{className:"max-w-md text-xs text-muted-foreground",children:"Viewer accounts cannot list, create or revoke API keys. Ask an administrator, or sign in as one."})]}):(0,s.jsxs)(s.Fragment,{children:[f&&(0,s.jsx)(tZ,{secret:f,onDismiss:()=>p(null)}),m&&(0,s.jsx)(tY,{onCancel:()=>h(!1),onCreate:async e=>{let t=await c(e);t&&(p(t),h(!1))}}),d&&(0,s.jsxs)("p",{role:"alert",className:"rounded-lg border border-destructive/30 bg-destructive/10 px-3 py-2 text-xs text-destructive",children:["Could not load keys: ",d]}),l.length>0?(0,s.jsx)(t4,{tokens:l,onRevoke:b}):(0,s.jsxs)(e$,{className:"flex flex-col items-center gap-2 border-dashed px-6 py-12 text-center",children:[(0,s.jsx)(x.A,{size:22,className:"text-muted-foreground"}),(0,s.jsx)("p",{className:"text-sm font-semibold",children:o?"Loading keys…":"No API keys yet"}),!o&&(0,s.jsx)("p",{className:"max-w-sm text-xs text-muted-foreground",children:"Create a key to let services or scripts connect over gRPC with a limited role."})]})]}),(0,s.jsx)(t2,{}),(0,s.jsx)(tL,{open:!!g,onOpenChange:e=>{e||b(null)},children:(0,s.jsxs)(tM,{children:[(0,s.jsx)(tA,{className:"text-base font-semibold",children:"Revoke this key?"}),(0,s.jsxs)(tz,{className:"mt-2 text-sm text-muted-foreground",children:["Clients using ",(0,s.jsx)("span",{className:"font-mono text-foreground",children:null==g?void 0:g.name})," will be rejected immediately. This cannot be undone."]}),(0,s.jsxs)("div",{className:"mt-5 flex justify-end gap-2",children:[(0,s.jsx)(t_,{asChild:!0,children:(0,s.jsx)(_,{variant:"outline",size:"sm",children:"Cancel"})}),(0,s.jsx)(tP,{asChild:!0,children:(0,s.jsx)(_,{variant:"destructive",size:"sm",onClick:()=>g&&void u(g.id),children:"Revoke key"})})]})]})})]})}var t8=r(644);function t9(e){let{label:t,value:r,unit:a,hint:n,series:l,tone:o,live:i}=e;return(0,s.jsxs)(e$,{className:"p-4",children:[(0,s.jsx)("p",{className:"text-xs text-muted-foreground",children:t}),(0,s.jsxs)("p",{className:S("mt-1 font-mono text-2xl font-semibold tabular-nums",i?"text-primary":"text-foreground"),children:[r,a&&(0,s.jsx)("span",{className:"ml-1 text-sm font-normal text-muted-foreground",children:a})]}),n&&(0,s.jsx)("p",{className:"mt-0.5 font-mono text-[11px] text-muted-foreground",children:n}),l&&l.some(e=>void 0!==e)&&(0,s.jsx)(e9,{values:l,label:t,tone:o,height:40,className:"mt-3"})]})}function t7(e){let t=Object.entries(e).map(e=>{let[t,r]=e;return"".concat(t,'="').concat(r,'"')});return t.length?"{".concat(t.join(", "),"}"):""}function re(e){let{snapshot:t}=e,[r,n]=(0,a.useState)(!1),l=[...t.values()].sort((e,t)=>e.name.localeCompare(t.name));return(0,s.jsxs)(e$,{children:[(0,s.jsxs)("button",{type:"button",onClick:()=>n(e=>!e),"aria-expanded":r,className:"flex w-full items-center gap-2 px-5 py-4 text-left focus-visible:ring-2 focus-visible:ring-ring",children:[(0,s.jsx)(P.A,{size:14,className:"text-muted-foreground transition-transform ".concat(r?"rotate-90":"")}),(0,s.jsx)("span",{className:"text-sm font-semibold",children:"Raw metrics"}),(0,s.jsxs)("span",{className:"font-mono text-xs text-muted-foreground",children:[l.length," families"]})]}),r&&(0,s.jsx)("div",{className:"max-h-[420px] overflow-auto border-t border-border px-5 py-3",children:l.map(e=>(0,s.jsxs)("div",{className:"mb-3 last:mb-0",children:[(0,s.jsxs)("div",{className:"flex flex-wrap items-baseline gap-2",children:[(0,s.jsx)("code",{className:"font-mono text-xs font-semibold text-foreground",children:e.name}),(0,s.jsx)("span",{className:"font-mono text-[10px] uppercase tracking-wider text-muted-foreground",children:e.type})]}),e.help&&(0,s.jsx)("p",{className:"text-[11px] text-muted-foreground",children:e.help}),(0,s.jsx)("ul",{className:"mt-1 space-y-0.5",children:e.samples.map((t,r)=>{var a;return(0,s.jsxs)("li",{className:"flex justify-between gap-4 font-mono text-[11px] text-muted-foreground",children:[(0,s.jsxs)("span",{className:"truncate",title:t.name+t7(t.labels),children:[t.name!==e.name?t.name.slice(e.name.length):"",t7(t.labels)]}),(0,s.jsx)("span",{className:"shrink-0 text-foreground",children:Number.isFinite(a=t.value)?Number.isInteger(a)?String(a):a.toPrecision(6).replace(/\.?0+$/,""):a>0?"+Inf":a<0?"-Inf":"NaN"})]},r)})})]},e.name))})]})}function rt(e){return"+Inf"===e||"Inf"===e?1/0:"-Inf"===e?-1/0:Number(e)}function rr(e,t,r,s){let a=e.get(t);if(!a)return;let n=0,l=!1;for(let e of a.samples)e.name===t&&function(e,t){if(!t)return!0;for(let r of Object.keys(t))if(e.labels[r]!==t[r])return!1;return!0}(e,r)&&(!s||s(e))&&(n+=e.value,l=!0);return l?n:void 0}function rs(e,t){let r=e.buckets.length?e.buckets[e.buckets.length-1].count:0;if(r<=0)return;let s=t*r,a=0,n=0;for(let t of e.buckets){if(t.count>=s){if(!Number.isFinite(t.le))return a;let e=t.count-n;if(e<=0)return t.le;return a+(t.le-a)*(s-n)/e}a=t.le,n=t.count}return a}function ra(e,t,r){if(void 0!==e&&void 0!==t&&!(r<=0)&&!(evoid 0===e?"—":e>=100?e.toFixed(0):e.toFixed(2),rd=e=>void 0===e?"—":"".concat((100*e).toFixed(e>0&&e<.001?3:1),"%"),rc=e=>void 0===e?"—":Math.round(e).toLocaleString();function ru(e,t){for(let r=e.length-1;r>=0;r--)if(void 0!==e[r][t])return e[r][t]}let rx=(e,t)=>e.some(e=>void 0!==e[t]);function rm(e){let{icon:t,title:r,body:a}=e;return(0,s.jsxs)(e$,{className:"flex flex-col items-center gap-2 px-6 py-14 text-center",role:"status",children:[(0,s.jsx)("div",{className:"text-muted-foreground",children:t}),(0,s.jsx)("p",{className:"text-sm font-semibold",children:r}),(0,s.jsx)("p",{className:"max-w-md text-xs text-muted-foreground",children:a})]})}function rh(e){let{title:t,value:r,history:a,k:n,tone:l,zeroBased:o}=e;return(0,s.jsx)(rf,{title:t,value:r,values:a.map(e=>e[n]),tone:l,zeroBased:o,start:"-".concat(Math.round(5)," min")})}function rf(e){let{title:t,value:r,values:a,tone:n,zeroBased:l,start:o}=e;return(0,s.jsxs)(e$,{className:"p-5",children:[(0,s.jsxs)("div",{className:"flex items-baseline justify-between",children:[(0,s.jsx)("h2",{className:"text-sm font-semibold",children:t}),(0,s.jsx)("span",{className:"font-mono text-xs text-muted-foreground",children:r})]}),(0,s.jsx)(e9,{values:a,label:t,tone:n,height:96,zeroBased:l,className:"mt-3"}),(0,s.jsxs)("p",{className:"mt-2 flex justify-between font-mono text-[10px] text-muted-foreground",children:[(0,s.jsx)("span",{children:o}),(0,s.jsx)("span",{children:"now"})]})]})}let rp=[{id:"live",label:"Live (5 min)",seconds:0},{id:"1h",label:"Last hour",seconds:3600},{id:"24h",label:"Last 24 hours",seconds:86400}];function rg(e){var t,r;let{seconds:a}=e,{data:n,error:l}=e7(a,!0),o=null!=(t=null==n?void 0:n.points)?t:[],i=o[o.length-1],d=o.length>1?Math.max(1,Math.round((o[o.length-1].t-o[0].t)/60)):0,c=d>0&&60*d=86400?"-24 h":"-1 h";return(0,s.jsxs)(s.Fragment,{children:[l&&(0,s.jsxs)("p",{role:"alert",className:"rounded-lg border border-warning/30 bg-warning/10 px-3 py-2 text-xs text-warning",children:["Could not load history (",l,")."]}),(0,s.jsxs)("p",{className:"text-xs text-muted-foreground",children:["Sampled by the server every ",null!=(r=null==n?void 0:n.intervalSeconds)?r:10," s and kept in memory only, so it starts empty after a restart."]}),(0,s.jsxs)("div",{className:"grid gap-4 lg:grid-cols-2",children:[(0,s.jsx)(rf,{title:"Requests / s",value:i?ri(i.reqPerSec):"—",values:o.map(e=>e.reqPerSec),start:c}),(0,s.jsx)(rf,{title:"WAL size",value:i?eP(i.walBytes):"—",values:o.map(e=>e.walBytes),tone:"text-warning",start:c}),(0,s.jsx)(rf,{title:"Dirty pages",value:i?rc(i.dirtyPages):"—",values:o.map(e=>e.dirtyPages),tone:"text-info",start:c}),(0,s.jsx)(rf,{title:"Replica lag (max, LSNs)",value:i?rc(i.replicaLag):"—",values:o.map(e=>e.replicaLag),tone:"text-success",start:c})]})]})}function rb(e){var t,r,n;let o,{user:i}=e,d=(null==i?void 0:i.role)==="admin",{status:c,snapshot:u,history:x,lastUpdated:m,error:h}=function(e){let[t,r]=(0,a.useState)({status:"loading",snapshot:null,history:[],lastUpdated:null,error:null}),s=(0,a.useRef)(void 0);return(0,a.useEffect)(()=>{let t;if(!e)return;let a=!1,n=async()=>{try{let e=await fetch("/metrics",{cache:"no-store"});if(a)return;if(401===e.status||403===e.status)return void r(e=>({...e,status:"forbidden",error:null}));if(!e.ok)throw Error("/metrics returned HTTP ".concat(e.status));let t=function(e){let t=new Map,r=new Map,s=new Map;for(let l of e.split("\n")){let e=l.trim();if(!e)continue;if(e.startsWith("#")){let s=/^#\s+(HELP|TYPE)\s+(\S+)\s*(.*)$/.exec(e);if(!s)continue;"TYPE"===s[1]?t.set(s[2],s[3].trim()):r.set(s[2],s[3]);continue}let o=/^([a-zA-Z_:][a-zA-Z0-9_:]*)(?:\{(.*)\})?\s+(\S+)(?:\s+\S+)?$/.exec(e);if(!o)continue;let i=void 0===o[2]?{}:function(e){let t={},r=0;for(;r=e.length)break;let s=e.indexOf("=",r);if(s<0||'"'!==e[s+1])return null;let a=e.slice(r,s).trim(),n=s+2,l="";for(;n=e.length)return null;t[a]=l,r=n+1}return t}(o[2]);if(!i)continue;let d=rt(o[3]),c=function(e,t){if(t.has(e))return e;for(let r of["_bucket","_sum","_count"])if(e.endsWith(r)){let s=e.slice(0,-r.length),a=t.get(s);if("histogram"===a||"summary"===a)return s}return e}(o[1],t),u=s.get(c);if(!u){var a,n;u={name:c,type:null!=(a=t.get(c))?a:"untyped",help:null!=(n=r.get(c))?n:"",samples:[]},s.set(c,u)}u.samples.push({name:o[1],labels:i,value:d})}return s}(await e.text());if(a)return;if(0===t.size)throw Error("/metrics returned no samples");let n={t:Date.now(),req:rr(t,rn),err:rr(t,rn,void 0,e=>{var t;let r;return(r=null!=(t=e.labels.code)?t:"").startsWith("5")||rl.has(r)}),hist:function(e,t){let r=e.get(t);if(!r||"histogram"!==r.type)return;let s=new Map,a=0,n=0;for(let e of r.samples)if(e.name==="".concat(t,"_bucket")&&void 0!==e.labels.le){var l;let t=rt(e.labels.le);s.set(t,(null!=(l=s.get(t))?l:0)+e.value)}else e.name==="".concat(t,"_sum")?a+=e.value:e.name==="".concat(t,"_count")&&(n+=e.value);if(0!==s.size)return{buckets:[...s.entries()].sort((e,t)=>e[0]-t[0]).map(e=>{let[t,r]=e;return{le:t,count:r}}),sum:a,count:n}}(t,"beastdb_request_duration_seconds"),hits:rr(t,"beastdb_buffer_pool_hits_total"),misses:rr(t,"beastdb_buffer_pool_misses_total")},l=function(e,t,r){let s,a=t?(e.t-t.t)/1e3:0,n=t?ra(e.req,t.req,a):void 0,l=t?ra(e.err,t.err,a):void 0;void 0!==n&&void 0!==l&&n>0?s=l/n:0===n&&(s=0);let o=e.hist&&(null==t?void 0:t.hist)?function(e,t){if(e.count({le:e.le,count:e.count-t.buckets[r].count}));if(!r.some(e=>e.count<0))return{buckets:r,sum:e.sum-t.sum,count:e.count-t.count}}(e.hist,t.hist):void 0;return{t:e.t,reqPerSec:n,errPerSec:l,errorRatio:s,p50:o?rs(o,.5):void 0,p99:o?rs(o,.99):void 0,walBytes:rr(r,"beastdb_wal_size_bytes"),hitRatio:rr(r,"beastdb_buffer_pool_hit_ratio"),dirtyPages:rr(r,"beastdb_buffer_pool_dirty_pages"),inFlight:rr(r,"beastdb_grpc_requests_in_flight")}}(n,s.current,t);s.current=n,r(e=>({status:"ok",snapshot:t,history:[...e.history,l].slice(-60),lastUpdated:n.t,error:null}))}catch(e){if(a)return;r(t=>({...t,status:t.snapshot?t.status:"unavailable",error:e.message}))}a||(t=setTimeout(n,5e3))};return n(),()=>{a=!0,t&&clearTimeout(t)}},[e]),t}(d),[f,p]=(0,a.useState)("live"),g=null!=(r=null==(t=rp.find(e=>e.id===f))?void 0:t.seconds)?r:0,b=(0,s.jsx)(tn,{id:"metrics-title",title:"Metrics",subtitle:"Live engine telemetry from /metrics, polled every 5 seconds.",actions:(0,s.jsxs)(s.Fragment,{children:["ok"===c&&m&&(0,s.jsxs)("span",{className:"inline-flex items-center gap-2 font-mono text-xs text-muted-foreground",children:[(0,s.jsx)("span",{className:"h-1.5 w-1.5 rounded-full bg-primary","aria-hidden":!0}),"live \xb7 ",new Date(m).toLocaleTimeString()]}),d&&"forbidden"!==c&&(0,s.jsx)("select",{value:f,onChange:e=>p(e.target.value),"aria-label":"Chart time range",className:"h-9 rounded-md border border-border bg-card px-3 text-sm text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",children:rp.map(e=>(0,s.jsx)("option",{value:e.id,children:e.label},e.id))})]})});if(d&&"forbidden"!==c)if("unavailable"===c)o=(0,s.jsx)(rm,{icon:(0,s.jsx)(t8.A,{size:22}),title:"Metrics unavailable",body:"Could not read /metrics".concat(h?": ".concat(h):"",". Retrying every 5 seconds.")});else if("loading"!==c&&u){let e=ru(x,"reqPerSec"),t=function(e){let t=t=>rr(e,t),r=[],s=t("beastdb_buffer_pool_size_pages"),a=t("beastdb_buffer_pool_cached_pages");(void 0!==s||void 0!==a)&&r.push({label:"Buffer pool",value:"".concat(rc(a)),unit:void 0!==s?"/ ".concat(rc(s)," pages"):"pages",hint:"pinned ".concat(rc(t("beastdb_buffer_pool_pinned_pages"))," \xb7 dirty ").concat(rc(t("beastdb_buffer_pool_dirty_pages")))});let n=t("beastdb_wal_size_bytes");if(void 0!==n){let e=t("beastdb_checkpoint_age_seconds");r.push({label:"WAL size",value:eP(n),hint:[void 0!==t("beastdb_wal_current_lsn")?"LSN ".concat(rc(t("beastdb_wal_current_lsn"))):"",void 0!==e?"checkpoint ".concat(Math.round(e),"s ago"):""].filter(Boolean).join(" \xb7 ")})}let l=t("beastdb_disk_free_bytes");void 0!==l&&r.push({label:"Disk free",value:eP(l),hint:void 0!==t("beastdb_data_file_size_bytes")?"data file ".concat(eP(t("beastdb_data_file_size_bytes"))):void 0});let o=t("beastdb_uptime_seconds");if(void 0!==o){let e=Math.floor(o/3600);r.push({label:"Uptime",value:e>=24?"".concat(Math.floor(e/24),"d ").concat(e%24,"h"):e>=1?"".concat(e,"h ").concat(Math.floor(o%3600/60),"m"):"".concat(Math.floor(o/60),"m ").concat(Math.floor(o%60),"s")})}for(let t of e.values()){if(!/fsync|group_commit/.test(t.name)||"histogram"===t.type)continue;let s=rr(e,t.name);void 0!==s&&r.push({label:t.name.replace(/^beastdb_/,"").replace(/_/g," "),value:rc(s)})}return r}(u);o=(0,s.jsxs)(s.Fragment,{children:[h&&(0,s.jsxs)("p",{role:"alert",className:"rounded-lg border border-warning/30 bg-warning/10 px-3 py-2 text-xs text-warning",children:["Last poll failed (",h,"); showing the previous sample."]}),(0,s.jsxs)("div",{className:"grid gap-4 sm:grid-cols-2 xl:grid-cols-4",children:[rx(x,"reqPerSec")?(0,s.jsx)(t9,{label:"Requests / s",value:ri(e),live:(null!=e?e:0)>0}):(0,s.jsx)(t9,{label:"Requests / s",value:"—",hint:"needs two samples"}),rx(x,"errorRatio")&&(0,s.jsx)(t9,{label:"Error rate",value:rd(ru(x,"errorRatio")),hint:"".concat(ri(ru(x,"errPerSec"))," errors/s (5xx, server faults)")}),rx(x,"p50")&&(0,s.jsx)(t9,{label:"Latency p50",value:ro(ru(x,"p50")),hint:"last 5s window"}),rx(x,"p99")&&(0,s.jsx)(t9,{label:"Latency p99",value:ro(ru(x,"p99")),hint:"bucket-interpolated"}),rx(x,"hitRatio")&&(0,s.jsx)(t9,{label:"Buffer hit ratio",value:rd(ru(x,"hitRatio")),hint:"lifetime"}),rx(x,"inFlight")&&(0,s.jsx)(t9,{label:"gRPC in flight",value:rc(ru(x,"inFlight"))}),t.map(e=>(0,s.jsx)(t9,{...e},e.label))]}),g>0?(0,s.jsx)(rg,{seconds:g}):(0,s.jsxs)("div",{className:"grid gap-4 lg:grid-cols-2",children:[rx(x,"reqPerSec")&&(0,s.jsx)(rh,{title:"Requests / s",value:ri(e),history:x,k:"reqPerSec"}),rx(x,"errPerSec")&&(0,s.jsx)(rh,{title:"Errors / s",value:ri(ru(x,"errPerSec")),history:x,k:"errPerSec",tone:"text-destructive"}),rx(x,"p99")&&(0,s.jsx)(rh,{title:"Latency p99",value:ro(ru(x,"p99")),history:x,k:"p99",tone:"text-info"}),rx(x,"walBytes")&&(0,s.jsx)(rh,{title:"WAL size",value:eP(null!=(n=ru(x,"walBytes"))?n:0),history:x,k:"walBytes",tone:"text-warning"}),rx(x,"dirtyPages")&&(0,s.jsx)(rh,{title:"Dirty pages",value:rc(ru(x,"dirtyPages")),history:x,k:"dirtyPages",tone:"text-info"})]}),(0,s.jsx)(re,{snapshot:u})]})}else o=(0,s.jsx)(rm,{icon:(0,s.jsx)(l.A,{size:22}),title:"Loading metrics",body:"Waiting for the first sample."});else o=(0,s.jsx)(rm,{icon:(0,s.jsx)(tq.A,{size:22}),title:"Admin only",body:"Engine metrics are restricted to administrator sessions. Sign in as an admin to view them."});return(0,s.jsxs)("section",{"aria-labelledby":"metrics-title",className:"space-y-6",children:[b,o]})}var rj=r(5148),rv=r(8941),ry=r(1165);let rN=["login.ok","login.failed","logout","token.create","token.revoke","user.delete","record.put","record.delete","checkpoint","settings.update","backup.create","backup.verify","backup.download"],rw={"login.failed":"text-warning","user.delete":"text-destructive","record.delete":"text-destructive","token.revoke":"text-destructive"};function rk(e){let{tabs:t}=e,[r,n]=(0,a.useState)([]),[l,o]=(0,a.useState)(null),[i,d]=(0,a.useState)(""),[c,u]=(0,a.useState)(!1),[x,m]=(0,a.useState)("loading"),h=(0,a.useCallback)(async e=>{try{let t=new URLSearchParams({limit:String(100)});i&&t.set("action",i),e&&t.set("before",String(e));let r=await fetch("/api/audit?".concat(t),{cache:"no-store"});if(!r.ok)throw Error("HTTP ".concat(r.status));let s=await r.json();n(t=>e?[...t,...s.entries]:s.entries),u(100===s.entries.length),o(s.status),m("ok")}catch(e){m("error")}},[i]);return(0,a.useEffect)(()=>{m("loading"),h()},[h]),(0,s.jsxs)("section",{"aria-labelledby":"logs-title",className:"space-y-4",children:[(0,s.jsx)(tn,{id:"logs-title",title:"Logs & Audit",subtitle:"Sign-ins, key changes, deletes and checkpoints. Append-only and hash-chained; record values and secrets are never written.",actions:l&&(0,s.jsx)(eX,{variant:l.verified?"lime":"mono",title:l.error,children:l.verified?"CHAIN VERIFIED \xb7 ".concat(l.entries):"CHAIN BROKEN"})}),(0,s.jsxs)("div",{className:"flex flex-wrap items-center gap-2",children:[t,(0,s.jsxs)("select",{value:i,onChange:e=>d(e.target.value),"aria-label":"Filter by action",className:"".concat("h-8 rounded-md border border-border bg-card px-2 text-xs text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"," ml-auto"),children:[(0,s.jsx)("option",{value:"",children:"All actions"}),rN.map(e=>(0,s.jsx)("option",{value:e,children:e},e))]})]}),l&&!l.verified&&(0,s.jsxs)("p",{role:"alert",className:"rounded-lg border border-destructive/40 bg-destructive/10 px-4 py-3 text-sm text-destructive",children:["The audit chain does not verify: ",l.error,". The log was edited, truncated or reordered after it was written."]}),(0,s.jsxs)(e$,{className:"overflow-x-auto",children:[(0,s.jsxs)("table",{className:"w-full min-w-[720px] text-sm",children:[(0,s.jsx)("thead",{children:(0,s.jsxs)("tr",{className:"border-b border-border text-left text-[11px] uppercase tracking-wide text-muted-foreground",children:[(0,s.jsx)("th",{scope:"col",className:"px-4 py-3 font-medium",children:"Time"}),(0,s.jsx)("th",{scope:"col",className:"px-3 py-3 font-medium",children:"Actor"}),(0,s.jsx)("th",{scope:"col",className:"px-3 py-3 font-medium",children:"Action"}),(0,s.jsx)("th",{scope:"col",className:"px-3 py-3 font-medium",children:"Target"}),(0,s.jsx)("th",{scope:"col",className:"px-4 py-3 font-medium",children:"Detail"})]})}),(0,s.jsx)("tbody",{children:r.map(e=>{var t;return(0,s.jsxs)("tr",{className:"border-b border-border last:border-0",children:[(0,s.jsx)("td",{className:"whitespace-nowrap px-4 py-2.5 font-mono text-xs text-muted-foreground",title:e.ip?"from ".concat(e.ip):void 0,children:new Date(e.time).toLocaleString()}),(0,s.jsx)("td",{className:"px-3 py-2.5 font-mono text-xs",children:e.actor}),(0,s.jsx)("td",{className:"px-3 py-2.5 font-mono text-xs ".concat(null!=(t=rw[e.action])?t:"text-foreground"),children:e.action}),(0,s.jsx)("td",{className:"px-3 py-2.5 font-mono text-xs text-muted-foreground",children:e.target||"—"}),(0,s.jsx)("td",{className:"px-4 py-2.5 text-xs text-muted-foreground",children:e.detail||"—"})]},e.seq)})})]}),"loading"===x&&(0,s.jsx)("p",{className:"p-4 text-sm text-muted-foreground",children:"Loading audit log…"}),"error"===x&&(0,s.jsx)("p",{className:"p-4 text-sm text-destructive",children:"Could not read the audit log."}),"ok"===x&&0===r.length&&(0,s.jsx)("p",{className:"p-4 text-sm text-muted-foreground",children:"No matching entries yet."})]}),c&&(0,s.jsx)("div",{className:"flex justify-center",children:(0,s.jsx)(_,{variant:"outline",size:"sm",onClick:()=>{var e;h(null==(e=r[r.length-1])?void 0:e.seq)},children:"Load older"})})]})}let rS=["engine","grpc","auth","replication","studio","server"],rC=[{label:"All retained",ms:0},{label:"Last 5 minutes",ms:3e5},{label:"Last hour",ms:36e5}],rL=["error","warn","info","debug"],rA={error:"text-destructive",warn:"text-warning",info:"text-muted-foreground",debug:"text-muted-foreground/60"},rz="h-8 rounded-md border border-border bg-card px-2 text-xs text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring";function r_(e){let{tab:t,setTab:r}=e;return(0,s.jsx)("div",{role:"tablist","aria-label":"Log type",className:"inline-flex rounded-lg border border-border bg-muted/40 p-0.5",children:[["server","Server log"],["audit","Audit"]].map(e=>{let[a,n]=e;return(0,s.jsx)("button",{role:"tab","aria-selected":t===a,onClick:()=>r(a),className:S("rounded-md px-3 py-1 text-xs transition-colors",t===a?"bg-card text-foreground shadow-sm":"text-muted-foreground hover:text-foreground"),children:n},a)})})}function rP(e){let{user:t}=e,r=(null==t?void 0:t.role)==="admin",[n,l]=(0,a.useState)([]),[o,i]=(0,a.useState)("loading"),[d,c]=(0,a.useState)(!1),[u,x]=(0,a.useState)(()=>new Set(rL)),[m,h]=(0,a.useState)(""),[f,p]=(0,a.useState)("server"),[g,b]=(0,a.useState)(""),[j,v]=(0,a.useState)(0),[y,N]=(0,a.useState)(!0),w=(0,a.useRef)(0),k=(0,a.useRef)(null),C=(0,a.useCallback)(async()=>{try{let e=await fetch("/api/logs?since=".concat(w.current,"&limit=1000"),{cache:"no-store"});if(403===e.status||401===e.status)return i("forbidden");if(!e.ok)return i("error");let t=await e.json();t.latest(function(e,t){let r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:2e3;if(0===t.length)return e;let s=e.length?e[e.length-1].seq:0,a=t.filter(e=>e.seq>s);if(0===a.length)return e;let n=e.concat(a);return n.length>r?n.slice(n.length-r):n})(e,t.entries))),i("ok")}catch(e){i("error")}},[]);(0,a.useEffect)(()=>{if(!r||d||"forbidden"===o)return;C();let e=setInterval(()=>void C(),2e3);return()=>clearInterval(e)},[r,d,"forbidden"===o,C]);let L=(0,a.useMemo)(()=>(function(e,t){var r,s;let a=(null!=(r=t.text)?r:"").trim().toLowerCase(),n=t.rangeMs?(null!=(s=t.now)?s:Date.now())-t.rangeMs:0;return e.filter(e=>{if(!t.levels.has(e.level)||t.source&&e.source!==t.source||""!==a&&!e.message.toLowerCase().includes(a))return!1;if(n){let t=new Date(e.time).getTime();if(!isNaN(t)&&t{let e={error:0,warn:0,info:0,debug:0};for(let t of n)e[t.level]+=1;return e},[n]);(0,a.useEffect)(()=>{let e=k.current;e&&y&&(e.scrollTop=e.scrollHeight)},[L,y]);let z=e=>{let t=e?function(e){let t=e=>'"'.concat(e.replace(/"/g,'""'),'"');return["time,level,source,message",...e.map(e=>{var r;return[t(e.time),t(e.level),t(null!=(r=e.source)?r:""),t(e.message)].join(",")})].join("\r\n")+"\r\n"}(L):L.map(e=>"".concat(e.time," ").concat(e.level.toUpperCase().padEnd(5)," ").concat(e.source?e.source.padEnd(11)+" ":"").concat(e.message)).join("\n")+(L.length?"\n":""),r=URL.createObjectURL(new Blob([t],{type:e?"text/csv":"text/plain"})),s=document.createElement("a");s.href=r,s.download="beastdb-logs-".concat(new Date().toISOString().replace(/[:.]/g,"-"),".").concat(e?"csv":"txt"),s.click(),URL.revokeObjectURL(r)};return r&&"forbidden"!==o?"audit"===f?(0,s.jsx)(rk,{tabs:(0,s.jsx)(r_,{tab:f,setTab:p})}):(0,s.jsxs)("section",{"aria-labelledby":"logs-title",className:"space-y-4",children:[(0,s.jsx)(tn,{id:"logs-title",title:"Logs",subtitle:"Recent server log, kept in memory only (last ".concat(n.length," of at most 2,000 lines; cleared on restart)."),actions:(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(eX,{variant:d?"mono":"lime",children:d?"PAUSED":"LIVE"}),(0,s.jsxs)(_,{variant:"outline",size:"sm",onClick:()=>c(e=>!e),children:[d?(0,s.jsx)(tr.A,{className:"h-3.5 w-3.5","aria-hidden":!0}):(0,s.jsx)(rv.A,{className:"h-3.5 w-3.5","aria-hidden":!0}),d?"Resume":"Pause"]}),(0,s.jsxs)(_,{variant:"outline",size:"sm",onClick:()=>z(!1),disabled:0===L.length,children:[(0,s.jsx)(tB.A,{className:"h-3.5 w-3.5","aria-hidden":!0}),"Download"]}),(0,s.jsx)(_,{variant:"outline",size:"sm",onClick:()=>z(!0),disabled:0===L.length,children:"Export CSV"})]})}),(0,s.jsxs)("div",{className:"flex flex-wrap items-center gap-2",children:[(0,s.jsx)(r_,{tab:f,setTab:p}),rL.map(e=>{let t=u.has(e);return(0,s.jsxs)("button",{type:"button","aria-pressed":t,onClick:()=>x(t=>{let r=new Set(t);return r.has(e)?r.delete(e):r.add(e),r}),className:S("rounded-md border px-2.5 py-1 font-mono text-xs uppercase transition-colors",t?"border-border bg-muted text-foreground":"border-border/50 text-muted-foreground/60 hover:text-foreground"),children:[e," ",(0,s.jsx)("span",{className:"text-muted-foreground",children:A[e]})]},e)}),(0,s.jsxs)("select",{value:g,onChange:e=>b(e.target.value),"aria-label":"Filter by source",className:rz,children:[(0,s.jsx)("option",{value:"",children:"All sources"}),rS.map(e=>(0,s.jsx)("option",{value:e,children:e},e))]}),(0,s.jsx)("select",{value:j,onChange:e=>v(Number(e.target.value)),"aria-label":"Filter by time range",className:rz,children:rC.map(e=>(0,s.jsx)("option",{value:e.ms,children:e.label},e.ms))}),(0,s.jsx)(D,{value:m,onChange:e=>h(e.target.value),placeholder:"Filter messages","aria-label":"Filter log messages",className:"h-8 w-full max-w-xs flex-1"})]}),(0,s.jsxs)(e$,{className:"relative overflow-hidden",children:[(0,s.jsxs)("div",{ref:k,onScroll:()=>{let e=k.current;e&&N(e.scrollHeight-e.scrollTop-e.clientHeight<24)},role:"log","aria-live":"off",className:"h-[calc(100vh-18rem)] min-h-[20rem] overflow-auto p-2 font-mono text-xs leading-5",children:["error"===o&&(0,s.jsx)("p",{className:"p-3 text-destructive",children:"Cannot read logs right now. Retrying every 2 seconds."}),"loading"===o&&(0,s.jsx)("p",{className:"p-3 text-muted-foreground",children:"Loading logs…"}),"ok"===o&&0===L.length&&(0,s.jsx)("p",{className:"p-3 text-muted-foreground",children:0===n.length?"No log lines yet.":"No lines match the current filters."}),L.length>0&&(0,s.jsxs)("div",{"aria-hidden":!0,className:"sticky top-0 z-10 flex gap-3 border-b border-border bg-card px-2 py-2 text-[11px] font-medium uppercase tracking-wide text-muted-foreground",children:[(0,s.jsx)("span",{className:"w-[4.5rem] shrink-0",children:"Time"}),(0,s.jsx)("span",{className:"w-14 shrink-0",children:"Level"}),(0,s.jsx)("span",{className:"w-24 shrink-0",children:"Source"}),(0,s.jsx)("span",{children:"Event"})]}),L.map(e=>{var t;return(0,s.jsxs)("div",{className:"flex gap-3 rounded px-2 py-0.5 hover:bg-muted/50",children:[(0,s.jsx)("span",{className:"w-[4.5rem] shrink-0 text-muted-foreground/70",children:function(e){let t=new Date(e);if(isNaN(t.getTime()))return e;let r=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:2;return String(e).padStart(t,"0")};return"".concat(r(t.getHours()),":").concat(r(t.getMinutes()),":").concat(r(t.getSeconds()))}(e.time)}),(0,s.jsx)("span",{className:S("w-14 shrink-0 uppercase",rA[e.level]),children:e.level}),(0,s.jsx)("span",{className:"w-24 shrink-0 text-muted-foreground",children:null!=(t=e.source)?t:""}),(0,s.jsx)("span",{className:"min-w-0 whitespace-pre-wrap break-words text-foreground",children:e.message})]},e.seq)})]}),!y&&L.length>0&&(0,s.jsxs)(_,{size:"sm",variant:"secondary",onClick:()=>{let e=k.current;e&&(e.scrollTop=e.scrollHeight),N(!0)},className:"absolute bottom-3 right-4 shadow-md",children:[(0,s.jsx)(ry.A,{className:"h-3.5 w-3.5","aria-hidden":!0}),"Jump to latest"]})]})]}):(0,s.jsxs)("section",{"aria-labelledby":"logs-title",className:"space-y-6",children:[(0,s.jsx)(tn,{id:"logs-title",title:"Logs",subtitle:"Server log tail."}),(0,s.jsxs)(e$,{className:"p-10 text-center",children:[(0,s.jsx)(rj.A,{className:"mx-auto mb-3 h-6 w-6 text-muted-foreground","aria-hidden":!0}),(0,s.jsx)("p",{className:"font-medium",children:"Logs are limited to administrators"}),(0,s.jsx)("p",{className:"mt-1 text-sm text-muted-foreground",children:"Sign in as an admin to read the server log."})]})]})}function rM(e){let{value:t,label:r}=e,[n,l]=(0,a.useState)(!1),o=async()=>{try{await navigator.clipboard.writeText(t),l(!0),setTimeout(()=>l(!1),1500)}catch(e){}};return(0,s.jsx)(_,{variant:"ghost",size:"icon",className:"h-7 w-7",onClick:o,"aria-label":"Copy ".concat(r),children:n?(0,s.jsx)(tk.A,{className:"h-3.5 w-3.5","aria-hidden":!0}):(0,s.jsx)(eI.A,{className:"h-3.5 w-3.5","aria-hidden":!0})})}function rR(e){let{title:t,description:r,rows:a}=e;return(0,s.jsxs)(e$,{children:[(0,s.jsxs)(eG,{children:[(0,s.jsx)(eY,{className:"text-base",children:t}),(0,s.jsx)(eZ,{children:r})]}),(0,s.jsx)(eQ,{children:(0,s.jsx)("dl",{className:"divide-y divide-border",children:a.map(e=>(0,s.jsxs)("div",{className:"flex items-center justify-between gap-4 py-2",children:[(0,s.jsx)("dt",{className:"text-sm text-muted-foreground",children:e.label}),(0,s.jsxs)("dd",{className:"flex min-w-0 items-center gap-1",children:[e.flag?(0,s.jsx)(eX,{variant:"Yes"===e.value?"outline":"mono",children:e.value}):(0,s.jsx)("span",{className:"truncate font-mono text-sm",title:e.value,children:e.value||"—"}),e.copy&&e.value&&(0,s.jsx)(rM,{value:e.value,label:e.label})]})]},e.label))})})]})}let rE=e=>e?"Yes":"No",rT=[{id:"general",label:"General"},{id:"storage",label:"Storage"},{id:"networking",label:"Networking"},{id:"security",label:"Security"},{id:"limits",label:"Limits"}];function rI(e){let{user:t,onNotice:r}=e,{dark:n,toggle:l}=N(),[o,i]=(0,a.useState)("general"),d=(null==t?void 0:t.role)==="admin",[c,u]=(0,a.useState)(null),[x,m]=(0,a.useState)(d?"loading":"forbidden");return(0,a.useEffect)(()=>{if(!d)return void m("forbidden");let e=!1;return(async()=>{try{let t=await fetch("/api/settings",{cache:"no-store"});if(e)return;if(403===t.status||401===t.status)return m("forbidden");if(!t.ok)return m("error");u(await t.json()),m("ok")}catch(t){e||m("error")}})(),()=>{e=!0}},[d]),(0,s.jsxs)("section",{"aria-labelledby":"settings-title",className:"space-y-6",children:[(0,s.jsx)(tn,{id:"settings-title",title:"Settings",subtitle:"Startup configuration is read-only. A few settings can be changed live and are saved to the data directory."}),(0,s.jsxs)("div",{className:"grid gap-4 md:grid-cols-[200px_minmax(0,1fr)]",children:[(0,s.jsx)("nav",{"aria-label":"Settings sections",className:"flex gap-1 overflow-x-auto md:flex-col md:self-start md:rounded-xl md:border md:border-border md:bg-card md:p-2",children:rT.map(e=>(0,s.jsx)("button",{type:"button","aria-current":o===e.id?"page":void 0,onClick:()=>i(e.id),className:S("shrink-0 rounded-lg px-3 py-2 text-left text-sm transition-colors",o===e.id?"bg-primary/10 font-medium text-primary dark:bg-[#1e2b0a]":"text-muted-foreground hover:bg-muted hover:text-foreground"),children:e.label},e.id))}),(0,s.jsxs)("div",{className:"min-w-0 space-y-4",children:["general"===o&&(0,s.jsxs)(e$,{children:[(0,s.jsxs)(eG,{children:[(0,s.jsx)(eY,{className:"text-base",children:"Appearance"}),(0,s.jsx)(eZ,{children:"Stored in this browser only."})]}),(0,s.jsxs)(eQ,{className:"flex items-center justify-between",children:[(0,s.jsxs)("span",{className:"text-sm text-muted-foreground",children:["Theme: ",n?"Dark":"Light"]}),(0,s.jsxs)(_,{variant:"outline",size:"sm",onClick:l,children:[n?(0,s.jsx)(g.A,{className:"h-3.5 w-3.5","aria-hidden":!0}):(0,s.jsx)(b.A,{className:"h-3.5 w-3.5","aria-hidden":!0}),"Switch to ",n?"light":"dark"]})]})]}),"loading"===x&&(0,s.jsx)("p",{className:"text-sm text-muted-foreground",children:"Loading settings…"}),"error"===x&&(0,s.jsx)("p",{className:"text-sm text-destructive",children:"Could not load settings. Reload to retry."}),"forbidden"===x&&"general"!==o&&(0,s.jsx)(e$,{className:"p-6 text-sm text-muted-foreground",children:"Node settings are visible to administrators only."}),"forbidden"===x&&"general"===o&&(0,s.jsx)(e$,{className:"p-6 text-sm text-muted-foreground",children:"Node identity is visible to administrators only."}),d&&"storage"===o&&(0,s.jsx)(tF,{title:"Checkpointing",description:"Adjustable without a restart. Saved settings override the startup flag.",onNotice:r,fields:[{name:"wal_checkpoint_mb",label:"WAL checkpoint threshold",unit:"MiB",hint:"The server checkpoints and rotates the WAL when it grows past this size."}]}),d&&"security"===o&&(0,s.jsx)(tF,{title:"Sign-in rate limit",description:"Failed Studio sign-ins allowed per client address.",onNotice:r,fields:[{name:"login_max_attempts",label:"Failed attempts allowed",unit:"attempts"},{name:"login_window_seconds",label:"Within a window of",unit:"seconds"}]}),"ok"===x&&c&&(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)("p",{className:"rounded-lg border border-border bg-muted/40 px-4 py-3 text-sm text-muted-foreground",children:c.note}),[{title:"Node",section:"general",description:"Identity and role of this server.",rows:[{label:"Role",value:c.node.role},{label:"Version",value:c.node.version,copy:!0},{label:"Replica ID",value:c.node.replica_id,copy:!0},...c.node.leader_addr?[{label:"Leader address",value:c.node.leader_addr,copy:!0}]:[],{label:"Dev emulator",value:rE(c.node.dev_mode),flag:!0}]},{title:"Storage",section:"storage",description:"Where data lives and how the engine buffers it.",rows:[{label:"Data directory",value:c.storage.data_dir,copy:!0},{label:"Buffer pool",value:"".concat(c.storage.pool_size_frames," frames (").concat(eP(c.storage.pool_size_frames*c.storage.page_size_bytes),")")},{label:"Page size",value:eP(c.storage.page_size_bytes)}]},{title:"Network",section:"networking",description:"Listening addresses.",rows:[{label:"gRPC",value:c.network.grpc_addr,copy:!0},{label:"Web console",value:c.network.web_addr||"disabled",copy:!!c.network.web_addr}]},{title:"Security",section:"security",description:"Whether protections are configured. Secret values are never shown.",rows:[{label:"TLS on gRPC",value:rE(c.security.tls_enabled),flag:!0},{label:"Client certificates (mTLS CA)",value:rE(c.security.mutual_tls),flag:!0},{label:"gRPC API token set",value:rE(c.security.api_token_configured),flag:!0},{label:"Metrics token set",value:rE(c.security.metrics_token_configured),flag:!0},{label:"Insecure-auth override",value:rE(c.security.insecure_auth_override),flag:!0},{label:"Partition config",value:c.security.partition_config_path||"none",copy:!!c.security.partition_config_path}]},{title:"Limits",section:"limits",description:"Fixed by the engine; not configurable at runtime.",rows:[{label:"Max value size",value:eP(c.limits.max_value_bytes)},{label:"Max inline record",value:eP(c.limits.max_inline_record)},{label:"Max batch size",value:eP(c.limits.max_batch_bytes)},{label:"Max gRPC message",value:eP(c.limits.max_grpc_message)},{label:"Max web upload body",value:eP(c.limits.max_web_put_body)},{label:"Records page size (max)",value:String(c.limits.max_records_page_size)}]}].filter(e=>e.section===o).map(e=>{let{section:t,...r}=e;return(0,s.jsx)(rR,{...r},r.title)})]})]})]})]})}var rD=r(5544),rB=r(805),rO=r(4368);let rU=rO.bL,rF=rO.hE,rH=rO.VY;function rW(e){let{children:t,className:r,...a}=e;return(0,s.jsxs)(rO.ZL,{children:[(0,s.jsx)(rO.hJ,{className:"fixed inset-0 z-50 bg-black/55"}),(0,s.jsxs)(rO.UC,{className:S("fixed left-1/2 top-1/2 z-50 flex max-h-[90vh] w-[calc(100%-2rem)] max-w-xl -translate-x-1/2 -translate-y-1/2 flex-col overflow-y-auto rounded-lg border bg-card p-6 text-card-foreground shadow-xl",r),...a,children:[t,(0,s.jsx)(rO.bm,{className:"absolute right-4 top-4 rounded-sm text-muted-foreground hover:text-foreground","aria-label":"Close",children:(0,s.jsx)(n.A,{size:18})})]})]})}function rK(e){let{fields:t,setFields:r}=e,a=(e,t)=>r(r=>r.map(r=>r.id===e?{...r,...t}:r));return(0,s.jsxs)("div",{className:"mt-3 space-y-3",children:[(0,s.jsxs)("div",{className:"flex items-center justify-between",children:[(0,s.jsx)("span",{className:"text-sm font-medium",children:"Properties"}),(0,s.jsxs)(_,{type:"button",size:"sm",variant:"outline",onClick:()=>r(e=>[...e,{id:crypto.randomUUID(),name:"",type:"string",value:""}]),children:[(0,s.jsx)(K.A,{size:14}),"Add property"]})]}),(0,s.jsx)("div",{className:"max-h-64 space-y-2 overflow-y-auto",children:t.map(e=>(0,s.jsxs)("div",{className:"grid grid-cols-[1fr_auto] gap-2 rounded-md border p-2 sm:grid-cols-[1fr_100px_1fr_auto]",children:[(0,s.jsx)(D,{"aria-label":"Property name",placeholder:"Name",value:e.name,onChange:t=>a(e.id,{name:t.target.value})}),(0,s.jsxs)("select",{"aria-label":"Property type",className:"h-9 rounded-md border bg-card px-2 text-sm",value:e.type,onChange:t=>a(e.id,{type:t.target.value}),children:[(0,s.jsx)("option",{value:"string",children:"String"}),(0,s.jsx)("option",{value:"number",children:"Number"}),(0,s.jsx)("option",{value:"boolean",children:"Boolean"}),(0,s.jsx)("option",{value:"json",children:"JSON"})]}),"boolean"===e.type?(0,s.jsxs)("select",{"aria-label":"Property value",className:"h-9 rounded-md border bg-card px-2 text-sm",value:e.value||"false",onChange:t=>a(e.id,{value:t.target.value}),children:[(0,s.jsx)("option",{value:"false",children:"false"}),(0,s.jsx)("option",{value:"true",children:"true"})]}):(0,s.jsx)(D,{"aria-label":"Property value",placeholder:"Value",value:e.value,onChange:t=>a(e.id,{value:t.target.value})}),(0,s.jsx)(_,{type:"button",size:"icon",variant:"ghost","aria-label":"Remove property",onClick:()=>r(t=>t.filter(t=>t.id!==e.id)),children:(0,s.jsx)(eL.A,{size:15})})]},e.id))})]})}function rq(e){let{open:t,onClose:r,onSave:n,onLookup:l,onNotice:o}=e,[i,d]=(0,a.useState)("builder"),[c,u]=(0,a.useState)(""),[x,m]=(0,a.useState)(""),[h,f]=(0,a.useState)(1),[p,g]=(0,a.useState)(""),[b,j]=(0,a.useState)(""),[v,y]=(0,a.useState)("raw"),[N,w]=(0,a.useState)(""),[k,S]=(0,a.useState)([{id:"initial",name:"",type:"string",value:""}]),[C,L]=(0,a.useState)(!1),[A,z]=(0,a.useState)(""),P=el(),M=()=>"raw"===i?c:"hash"===i?eR(x.trim()):String(BigInt(h)<<56n|eE(p)<<28n|eE(b)),R=async e=>{let t;e.preventDefault(),z("");let s=M();if(!/^(0|[1-9]\d*)$/.test(s)||BigInt(s)>0xffffffffffffffffn)return void z("Key is not a valid uint64 decimal.");try{t="builder"===v?function(e){let t=Object.create(null);for(let r of e){if(!r.name.trim())throw Error("Each property needs a name.");if(Object.hasOwn(t,r.name))throw Error("Duplicate property: ".concat(r.name));if("number"===r.type){if(!r.value.trim()||!Number.isFinite(Number(r.value)))throw Error("Enter a valid number for ".concat(r.name,"."));t[r.name]=Number(r.value)}else if("boolean"===r.type)t[r.name]="true"===r.value;else if("json"===r.type)try{t[r.name]=JSON.parse(r.value)}catch(e){throw Error("Invalid JSON in ".concat(r.name,"."))}else t[r.name]=r.value}return JSON.stringify(t,null,2)}(k):N}catch(e){z(e.message);return}if(!t.length)return void z("Enter a value to save.");L(!0);try{try{await l(s),z("This key already exists. Choose another to avoid overwriting.");return}catch(e){if("That record was not found."!==e.message)throw e}await n(s,t),o("Record created."),u(""),w(""),m(""),g(""),j(""),r()}catch(e){z(e.message)}finally{L(!1)}},E=(()=>{try{let e=M();if(!e||!/^(0|[1-9]\d*)$/.test(e))return null;return e}catch(e){return null}})();return(0,s.jsx)(rU,{open:t,onOpenChange:e=>{e||C||r()},children:(0,s.jsxs)(rW,{children:[(0,s.jsx)(rF,{className:"pr-8 text-xl font-semibold",children:"New Record"}),(0,s.jsx)(rH,{className:"mt-1 text-sm text-muted-foreground",children:"Add a key–value pair to BeastDB. Keys must be unique uint64 values."}),(0,s.jsxs)("form",{onSubmit:R,className:"mt-5 space-y-5",children:[(0,s.jsxs)("div",{className:"space-y-2",children:[(0,s.jsx)("p",{className:"text-xs font-mono font-semibold uppercase tracking-wider text-muted-foreground",children:"64-bit Key"}),(0,s.jsx)("div",{className:"grid grid-cols-3 rounded-lg border border-border bg-muted/60 p-1 text-xs",children:["builder","hash","raw"].map(e=>(0,s.jsx)("button",{type:"button",onClick:()=>d(e),className:"rounded-md py-1.5 capitalize transition ".concat(i===e?"bg-primary/10 text-primary border border-primary/30 font-medium":"text-muted-foreground hover:text-foreground"),children:"builder"===e?"Partition Builder":"hash"===e?"Hash Seed":"Raw Decimal"},e))}),"builder"===i&&(0,s.jsxs)("div",{className:"space-y-2",children:[(0,s.jsx)("select",{value:h,onChange:e=>f(Number(e.target.value)),className:"w-full rounded-md border border-border bg-card px-3 py-2 text-sm font-mono text-foreground focus:outline-none focus:ring-1 focus:ring-primary",children:P.map(e=>(0,s.jsxs)("option",{value:e.prefix,children:["0x",e.prefix.toString(16).padStart(2,"0").toUpperCase()," \xb7 ",e.label]},e.prefix))}),(0,s.jsxs)("div",{className:"grid grid-cols-2 gap-2",children:[(0,s.jsx)(D,{placeholder:"Domain / user hash seed",value:p,onChange:e=>g(e.target.value),"aria-label":"Domain seed"}),(0,s.jsx)(D,{placeholder:"Item ID / record seed",value:b,onChange:e=>j(e.target.value),"aria-label":"Item seed"})]}),(0,s.jsx)("p",{className:"text-[11px] text-muted-foreground font-mono",children:"key = (prefix << 56) | (FNV28(domain) << 28) | FNV28(item)"})]}),"hash"===i&&(0,s.jsx)("div",{className:"flex gap-2",children:(0,s.jsx)(D,{value:x,onChange:e=>m(e.target.value),placeholder:"Identifier to hash with FNV-1a 64-bit","aria-label":"Hash seed"})}),"raw"===i&&(0,s.jsx)(D,{inputMode:"numeric",value:c,onChange:e=>u(e.target.value),placeholder:"e.g. 72057594037927936"}),E&&(0,s.jsxs)("div",{className:"flex items-center gap-2 rounded-md border border-primary/20 bg-primary/5 px-3 py-2 text-xs font-mono",children:[(0,s.jsx)(rB.A,{size:12,className:"text-primary shrink-0"}),(0,s.jsx)("span",{className:"text-muted-foreground",children:"Key preview:"}),(0,s.jsx)("span",{className:"text-primary font-medium",children:E})]})]}),(0,s.jsxs)(rD.bL,{value:v,onValueChange:y,children:[(0,s.jsx)(rD.B8,{"aria-label":"Value editor",className:"grid grid-cols-2 rounded-lg border border-border bg-muted/60 p-1 text-xs",children:["raw","builder"].map(e=>(0,s.jsx)(rD.l9,{value:e,className:"rounded-md py-1.5 transition data-[state=active]:bg-primary/10 data-[state=active]:text-primary data-[state=active]:border data-[state=active]:border-primary/30 data-[state=active]:font-medium text-muted-foreground hover:text-foreground",children:"raw"===e?"Raw JSON / Payload":"Structured Builder"},e))}),(0,s.jsxs)(rD.UC,{value:"raw",className:"mt-3",children:[(0,s.jsx)("label",{htmlFor:"record-value",className:"text-sm font-medium",children:"Value \xb7 JSON, text, or token"}),(0,s.jsx)("textarea",{id:"record-value",rows:7,value:N,onChange:e=>w(e.target.value),className:"mt-2 w-full rounded-md border border-border bg-card p-3 font-mono text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-primary",placeholder:'{"id": 1, "name": "beast"}'})]}),(0,s.jsx)(rD.UC,{value:"builder",children:(0,s.jsx)(rK,{fields:k,setFields:S})})]}),A&&(0,s.jsx)("p",{role:"alert",className:"text-sm text-destructive",children:A}),(0,s.jsxs)("div",{className:"flex justify-end gap-2 border-t border-border pt-4",children:[(0,s.jsx)(_,{type:"button",variant:"outline",onClick:r,disabled:C,children:"Cancel"}),(0,s.jsx)(_,{type:"submit",disabled:C,children:C?"Writing to WAL…":"Create Record"})]})]})]})})}let rV=[{label:"User (0x01)",prefix:"72057594037927936"},{label:"Token (0x05)",prefix:"360287970189639680"},{label:"Catalog (0x10)",prefix:"1152921504606846976"}];function rJ(e){let{open:t,onClose:r}=e,[n,l]=(0,a.useState)(""),[o,i]=(0,a.useState)(""),d=/^(0|[1-9]\d*)$/.test(n)&&0xffffffffffffffffn>=BigInt(n||"0"),c=d&&n?eo(n):null;return(0,s.jsx)(rU,{open:t,onOpenChange:e=>{e||r()},children:(0,s.jsxs)(rW,{className:"max-w-xl",children:[(0,s.jsx)(rF,{className:"text-xl font-bold tracking-tight",children:"Key Analyzer"}),(0,s.jsx)(rH,{className:"mt-1 text-xs text-muted-foreground",children:"Inspect 64-bit BeastDB keys, decode partition anatomy, or hash identifiers via FNV-1a."}),(0,s.jsxs)("div",{className:"mt-4 space-y-4",children:[(0,s.jsxs)("div",{children:[(0,s.jsxs)("div",{className:"flex items-center justify-between",children:[(0,s.jsx)("label",{htmlFor:"analyzer-key",className:"text-xs font-mono text-muted-foreground",children:"DECIMAL KEY"}),(0,s.jsx)("div",{className:"flex gap-1",children:rV.map(e=>(0,s.jsx)("button",{type:"button",onClick:()=>l(e.prefix),className:"text-[10px] font-mono text-muted-foreground hover:text-primary underline",children:e.label},e.prefix))})]}),(0,s.jsx)(D,{id:"analyzer-key",className:"mt-1.5 font-mono text-sm",value:n,onChange:e=>l(e.target.value),placeholder:"e.g. 72057594037927936"})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)("label",{className:"text-xs font-mono text-muted-foreground",children:"HASH STRING IDENTIFIER (FNV-1a)"}),(0,s.jsxs)("div",{className:"mt-1.5 flex gap-2",children:[(0,s.jsx)(D,{"aria-label":"Identifier to hash",value:o,onChange:e=>i(e.target.value),placeholder:"e.g. alice@example.com or user_104",className:"font-mono text-sm"}),(0,s.jsx)(_,{variant:"secondary",disabled:!o.trim(),onClick:()=>l(eR(o.trim())),children:"Hash"})]})]}),n&&!d&&(0,s.jsx)("p",{role:"alert",className:"text-xs text-destructive font-mono",children:"Invalid key: Must be an unsigned 64-bit integer (0 to 18446744073709551615)."}),c&&(0,s.jsxs)("div",{className:"space-y-3 pt-2",children:[(0,s.jsx)(eO,{keyStr:c.raw,prefixLabel:c.prefixLabel,userHash:c.userHash,itemHash:c.itemHash}),(0,s.jsxs)("div",{className:"grid grid-cols-2 gap-2 text-xs",children:[(0,s.jsxs)("div",{className:"rounded border border-border bg-muted/40 p-2.5",children:[(0,s.jsx)("span",{className:"text-[10px] font-mono text-muted-foreground",children:"HEXADECIMAL"}),(0,s.jsx)("p",{className:"mt-1 font-mono text-foreground font-medium",children:c.hex})]}),(0,s.jsxs)("div",{className:"rounded border border-border bg-muted/40 p-2.5",children:[(0,s.jsx)("span",{className:"text-[10px] font-mono text-muted-foreground",children:"PARTITION METADATA"}),(0,s.jsxs)("div",{className:"mt-1 flex items-center gap-1.5",children:[(0,s.jsx)(eX,{variant:"lime",children:c.prefixHex}),(0,s.jsx)("span",{className:"truncate text-foreground font-medium",children:c.prefixLabel})]})]})]})]})]})]})})}var r$=r(406);function rG(e){let{open:t,userHash:r,onConfirm:a,onClose:n}=e;return(0,s.jsx)(tL,{open:t,onOpenChange:e=>{e||n()},children:(0,s.jsxs)(tM,{className:"border-destructive/30 bg-card text-card-foreground",children:[(0,s.jsxs)("div",{className:"flex items-center gap-3",children:[(0,s.jsx)("div",{className:"flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-destructive/15 text-destructive",children:(0,s.jsx)(r$.A,{size:20})}),(0,s.jsxs)("div",{children:[(0,s.jsx)(tA,{className:"text-base font-bold text-foreground",children:"Cascade Delete User Account"}),(0,s.jsxs)("p",{className:"font-mono text-xs text-muted-foreground",children:["Target User Hash: ",r]})]})]}),(0,s.jsx)(tz,{className:"mt-3 text-xs leading-5 text-muted-foreground",children:"This operation purges the primary user credential record (`0x01`) and atomically seeks and deletes all associated records (profiles, tokens, relationships) across the entire keyspace."}),(0,s.jsxs)("div",{className:"mt-5 flex justify-end gap-2 border-t border-border pt-3",children:[(0,s.jsx)(t_,{asChild:!0,children:(0,s.jsx)(_,{variant:"outline",size:"sm",onClick:n,className:"text-xs",children:"Cancel"})}),(0,s.jsx)(tP,{asChild:!0,children:(0,s.jsx)(_,{variant:"destructive",size:"sm",onClick:a,className:"text-xs",children:"Purge User & Records"})})]})]})})}function rY(e){let{record:t,onSave:r,onCancel:n,onNotice:l}=e,o=(()=>{try{return JSON.stringify(JSON.parse(t.raw),null,2)}catch(e){return t.raw}})(),[i,d]=(0,a.useState)(o),[c,u]=(0,a.useState)(!1),x=async()=>{let e=i.trim();if(e.startsWith("{")||e.startsWith("["))try{JSON.parse(e)}catch(e){l("Invalid JSON payload — fix syntax errors before committing.",!0);return}u(!0);try{await r(t.keyStr,i),l("Record updated successfully.")}catch(e){l(e.message,!0)}finally{u(!1)}},m=new TextEncoder().encode(i).length;return(0,s.jsxs)("div",{className:"flex flex-col gap-4 p-5 bg-card text-card-foreground rounded-xl",children:[(0,s.jsxs)("div",{className:"flex items-center justify-between border-b border-border pb-3",children:[(0,s.jsxs)("div",{children:[(0,s.jsxs)("div",{className:"flex items-center gap-2",children:[(0,s.jsx)("h2",{className:"text-base font-bold text-foreground",children:"Edit Record"}),(0,s.jsx)(eX,{variant:"lime",children:t.prefixLabel})]}),(0,s.jsxs)("p",{className:"mt-1 font-mono text-xs text-muted-foreground break-all",children:["Key: ",t.keyStr]})]}),(0,s.jsxs)(_,{variant:"outline",size:"sm",onClick:()=>{try{d(JSON.stringify(JSON.parse(i),null,2)),l("JSON formatted.",!1)}catch(e){l("Cannot format: Value is not valid JSON.",!0)}},className:"h-7 text-xs gap-1.5",title:"Prettify JSON indentation",children:[(0,s.jsx)(eD.A,{size:13})," Prettify"]})]}),(0,s.jsx)("textarea",{className:"min-h-[320px] w-full rounded-lg border border-border bg-muted/40 p-3.5 font-mono text-xs leading-5 text-foreground focus:outline-none focus:ring-2 focus:ring-primary",value:i,onChange:e=>d(e.target.value),"aria-label":"Record payload",spellCheck:!1}),(0,s.jsxs)("div",{className:"flex items-center justify-between text-xs text-muted-foreground font-mono",children:[(0,s.jsxs)("span",{children:[i.length," characters \xb7 ",eP(m)]}),(0,s.jsx)("span",{children:"UTF-8 Slotted Page Payload"})]}),(0,s.jsxs)("div",{className:"flex justify-end gap-2 border-t border-border pt-3",children:[(0,s.jsx)(_,{variant:"outline",onClick:n,disabled:c,className:"text-xs",children:"Cancel"}),(0,s.jsxs)(_,{onClick:()=>void x(),disabled:c,className:"text-xs gap-1.5",children:[(0,s.jsx)(tk.A,{size:14})," ",c?"Writing to WAL…":"Commit Changes"]})]})]})}function rZ(e){let{open:t,onClose:r,onNotice:l}=e,{tokens:o,loading:i,create:d,revoke:c}=t$(t,l),[u,m]=(0,a.useState)(!1),[h,f]=(0,a.useState)(""),[p,g]=(0,a.useState)("admin"),[b,j]=(0,a.useState)(null),[v,y]=(0,a.useState)(!1);(0,a.useEffect)(()=>{t&&(j(null),f(""))},[t]);let N=async e=>{if(e.preventDefault(),!h.trim())return;let t=await d({name:h,role:p,expiresInSeconds:0});t&&(j(t),f(""),m(!1))};return(0,s.jsx)(rU,{open:t,onOpenChange:e=>{e||r()},children:(0,s.jsxs)(rW,{className:"max-w-2xl p-6",children:[(0,s.jsxs)("div",{className:"flex flex-row items-center justify-between pb-4 border-b border-border",children:[(0,s.jsxs)("div",{className:"flex items-center gap-3",children:[(0,s.jsx)("div",{className:"p-2 rounded-lg bg-amber-500/10 border border-amber-500/20 text-amber-500",children:(0,s.jsx)(x.A,{size:18})}),(0,s.jsxs)("div",{children:[(0,s.jsx)(rF,{className:"text-base font-semibold text-foreground",children:"API Access Tokens"}),(0,s.jsx)("p",{className:"text-xs text-muted-foreground mt-0.5",children:"Persistent database bearer tokens (Partition 0x05)"})]})]}),!u&&(0,s.jsxs)(_,{size:"sm",onClick:()=>m(!0),className:"text-xs",children:[(0,s.jsx)(K.A,{size:14,className:"mr-1"})," New Token"]})]}),b&&(0,s.jsxs)("div",{className:"my-3 p-4 rounded-lg bg-primary/10 border border-primary/30 space-y-2",children:[(0,s.jsxs)("div",{className:"flex items-center justify-between",children:[(0,s.jsx)("span",{className:"text-xs font-semibold text-primary",children:"Copy Your New Token"}),(0,s.jsx)("span",{className:"text-[11px] text-muted-foreground",children:"You won't be able to see it again!"})]}),(0,s.jsxs)("div",{className:"flex items-center gap-2 bg-muted rounded px-3 py-2 border border-border",children:[(0,s.jsx)("code",{className:"text-xs font-mono text-foreground flex-1 truncate",children:b}),(0,s.jsx)(_,{size:"sm",variant:"ghost",onClick:()=>{b&&(navigator.clipboard.writeText(b),y(!0),setTimeout(()=>y(!1),2e3),l("Token copied to clipboard!"))},className:"h-7 px-2 text-primary hover:bg-primary/20",children:v?(0,s.jsx)(tk.A,{size:14}):(0,s.jsx)(eI.A,{size:14})})]})]}),u&&(0,s.jsxs)("form",{onSubmit:N,className:"my-3 p-4 rounded-lg bg-muted/60 border border-border space-y-3",children:[(0,s.jsxs)("div",{className:"flex items-center justify-between",children:[(0,s.jsx)("span",{className:"text-xs font-semibold text-foreground",children:"Generate API Token"}),(0,s.jsx)("button",{type:"button",onClick:()=>m(!1),className:"text-muted-foreground hover:text-foreground",children:(0,s.jsx)(n.A,{size:14})})]}),(0,s.jsxs)("div",{className:"grid grid-cols-3 gap-3",children:[(0,s.jsx)("input",{type:"text",placeholder:"Token name (e.g. Unfinished Backend)",value:h,onChange:e=>f(e.target.value),className:"col-span-2 rounded bg-card text-foreground px-3 py-1.5 text-xs border border-border focus:outline-none focus:ring-1 focus:ring-primary",autoFocus:!0}),(0,s.jsx)("select",{value:p,onChange:e=>g(e.target.value),className:"rounded bg-card text-foreground px-2 py-1.5 text-xs border border-border focus:outline-none focus:ring-1 focus:ring-primary",children:tV.map(e=>(0,s.jsx)("option",{value:e,children:e.charAt(0).toUpperCase()+e.slice(1)},e))})]}),(0,s.jsxs)("div",{className:"flex justify-end gap-2 pt-1",children:[(0,s.jsx)(_,{size:"sm",variant:"outline",type:"button",onClick:()=>m(!1),className:"text-xs h-7",children:"Cancel"}),(0,s.jsx)(_,{size:"sm",type:"submit",className:"text-xs h-7",children:"Create"})]})]}),(0,s.jsx)("div",{className:"mt-2 space-y-2 max-h-72 overflow-y-auto pr-1",children:i?(0,s.jsx)("p",{className:"py-6 text-center text-xs text-muted-foreground font-mono",children:"Loading active tokens..."}):0===o.length?(0,s.jsxs)("div",{className:"py-8 text-center border border-dashed border-border rounded-lg",children:[(0,s.jsx)(x.A,{size:24,className:"mx-auto text-muted-foreground mb-2"}),(0,s.jsx)("p",{className:"text-xs font-medium text-muted-foreground",children:"No API tokens configured"}),(0,s.jsx)("p",{className:"text-[11px] text-muted-foreground mt-1 max-w-sm mx-auto",children:"Generate a token to allow services like Unfinished or scripts to connect securely via gRPC."})]}):o.map(e=>(0,s.jsxs)("div",{className:"flex items-center justify-between p-3 rounded-lg bg-card border border-border hover:border-primary/40 transition",children:[(0,s.jsxs)("div",{className:"space-y-0.5",children:[(0,s.jsxs)("div",{className:"flex items-center gap-2",children:[(0,s.jsx)("span",{className:"text-xs font-semibold text-foreground",children:e.name}),(0,s.jsx)("span",{className:"text-[10px] font-mono px-1.5 py-0.2 rounded border ".concat("admin"===e.role?"bg-primary/10 text-primary border-primary/30":"bg-muted text-muted-foreground border-border"),children:e.role})]}),(0,s.jsxs)("div",{className:"flex items-center gap-2 text-[11px] font-mono text-muted-foreground",children:[(0,s.jsx)("code",{children:e.masked_token}),(0,s.jsx)("span",{children:"•"}),(0,s.jsx)("span",{children:new Date(e.created_at).toLocaleDateString()})]})]}),(0,s.jsx)(_,{size:"sm",variant:"ghost",onClick:()=>{c(e.id)},title:"Revoke Token",className:"text-muted-foreground hover:text-destructive hover:bg-destructive/10 h-7 w-7 p-0",children:(0,s.jsx)(eL.A,{size:13})})]},e.id))})]})})}function rQ(){var e,t,r;let l=function(){let[e,t]=(0,a.useState)(null),[r,s]=(0,a.useState)(null),[n,l]=(0,a.useState)([]),[o,i]=(0,a.useState)(!0),[d,c]=(0,a.useState)(!1),[u,x]=(0,a.useState)(null),[m,h]=(0,a.useState)(null),[f,p]=(0,a.useState)(!1),[g,b]=(0,a.useState)("0"),[j,v]=(0,a.useState)(null),[y,N]=(0,a.useState)(null),w=(0,a.useCallback)(async function(){let e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];try{let r=await eW(e?"/api/stats?counts=true":"/api/stats");t(e=>r.partitionCounts?r:{...r,partitionCounts:null==e?void 0:e.partitionCounts}),h(null)}catch(e){t(null),h(e.message)}},[]),k=(0,a.useCallback)(async e=>{i(!0),x(null),v(e);let t=(BigInt(e)<<56n).toString(),r=((BigInt(e)+1n<<56n)-1n).toString();try{let e=await eW("/api/records?start=".concat(t,"&end=").concat(r,"&limit=100"));l(e.records.map(eV)),p(e.hasMore),b(e.nextKeyText),N(new Date)}catch(e){x(e.message)}finally{i(!1)}},[]),S=(0,a.useCallback)(async()=>{i(!0),x(null);let[e,t,r]=await Promise.allSettled([eW("/api/records?limit=100"),eW("/api/me"),eW("/api/partitions")]);"fulfilled"===r.status&&function(e){let t={};for(let r of null!=e?e:[])t[r.prefix]={label:r.label,color:r.color,description:r.description};es=t}(r.value),"fulfilled"===e.status?(l(e.value.records.map(eV)),p(e.value.hasMore),b(e.value.nextKeyText),N(new Date)):x(e.reason.message),"fulfilled"===t.status?s(t.value):(s(null),x(t.reason.message)),await w(!0),i(!1)},[w]);(0,a.useEffect)(()=>{S()},[S]),(0,a.useEffect)(()=>{let e=window.setInterval(()=>{w(!1)},1e4);return()=>window.clearInterval(e)},[w]);let C=async()=>{if(f&&!d){c(!0);try{let e="/api/records?start=".concat(g,"&limit=100");if(null!==j){let t=((BigInt(j)+1n<<56n)-1n).toString();e+="&end=".concat(t)}let t=await eW(e);l(e=>[...e,...t.records.map(eV)]),p(t.hasMore),b(t.nextKeyText)}catch(e){x(e.message)}finally{c(!1)}}},L=async(e,t,r)=>{await eW("/api/key",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({key:e,value:t,createOnly:r})}),await S()},A=async e=>{await eW("/api/key?k=".concat(encodeURIComponent(e)),{method:"DELETE"}),await S()},z=async e=>{let t=await eW("/api/user?userHash=".concat(e),{method:"DELETE"});return await S(),t};return{stats:e,user:r,records:n,loading:o,loadingMore:d,error:u,statsError:m,hasMore:f,activePartition:j,updatedAt:y,refresh:S,loadMore:C,loadPartition:k,lookup:async e=>eV(await eW("/api/key?k=".concat(encodeURIComponent(e)))),scanRange:async(e,t)=>(await eW("/api/records?start=".concat(encodeURIComponent(e),"&end=").concat(encodeURIComponent(t),"&limit=100"))).records.map(eV),save:(e,t)=>L(e,t,!0),update:(e,t)=>L(e,t,!1),remove:A,removeUser:z,search:async(e,t)=>{let r=new URLSearchParams({q:e,limit:"50"});(null==t?void 0:t.prefix)&&r.set("prefix",String(t.prefix)),(null==t?void 0:t.start)&&r.set("start",t.start),(null==t?void 0:t.maxScan)&&r.set("maxScan",String(t.maxScan));let s=await eW("/api/search?".concat(r));return{records:s.records.map(eV),count:s.count,scannedCount:s.scannedCount,nextKey:s.nextKey,hasMore:s.hasMore,prefix:s.prefix}}}}(),[o,i]=(0,a.useState)("explorer"),[d,c]=(0,a.useState)(null),[u,x]=(0,a.useState)(!1),[m,h]=(0,a.useState)(!1),[f,p]=(0,a.useState)(!1),[g,b]=(0,a.useState)(null),[j,v]=(0,a.useState)(null),[y,N]=(0,a.useState)(null),[w,k]=(0,a.useState)(null),S=function(e){let t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return k({message:e,error:t})};(0,a.useEffect)(()=>{if(!w)return;let e=window.setTimeout(()=>k(null),5e3);return()=>clearTimeout(e)},[w]),(0,a.useEffect)(()=>{let e=new URLSearchParams(window.location.search).get("key");e&&l.lookup(e).then(e=>{e&&c(e)}).catch(()=>{})},[]),(0,a.useEffect)(()=>{let e=new URL(window.location.href);d?e.searchParams.set("key",d.keyStr):e.searchParams.delete("key"),window.history.replaceState({},"",e.toString())},[d]);let C=async()=>{if(g)try{await l.remove(g),c(null),S("Record deleted.")}catch(e){S(e.message,!0)}finally{b(null)}},A=async()=>{if(null!==j)try{let e=await l.removeUser(j);c(null),S("User deleted: ".concat(e.deleted," associated records removed."))}catch(e){S(e.message,!0)}finally{v(null)}},z={stats:l.stats,user:l.user,onNotice:S,onRefresh:()=>void l.refresh()};return(0,s.jsxs)("div",{className:"flex min-h-dvh flex-col bg-background text-foreground md:h-dvh md:flex-row md:overflow-hidden",children:[(0,s.jsx)(L,{view:o,setView:i,user:l.user,busy:l.loading,onRefresh:()=>void l.refresh()}),(0,s.jsxs)("div",{className:"flex min-h-0 min-w-0 flex-1 flex-col md:overflow-y-auto",children:[(null==(e=l.user)?void 0:e.role)==="viewer"&&(0,s.jsx)("div",{className:"border-b border-warning/30 bg-warning/10 px-4 py-2 text-center text-sm text-warning",children:"You have view-only access — contact an admin to make changes."}),(0,s.jsxs)("main",{className:"explorer"===o?"flex min-h-0 w-full flex-1 flex-col px-4 py-4 md:px-8 md:py-6":"w-full max-w-[1400px] px-4 py-6 md:px-8 md:py-8",children:["explorer"===o&&(0,s.jsx)(eJ,{records:l.records,selected:d,loading:l.loading,loadingMore:l.loadingMore,hasMore:l.hasMore,error:l.error,canWrite:(null==(t=l.user)?void 0:t.role)==="admin",onRetry:()=>void l.refresh(),onLoadMore:()=>void l.loadMore(),onSelect:c,onDelete:b,onDeleteUser:e=>v(e),onEdit:e=>N(e),onNew:()=>x(!0),onLookup:l.lookup,onSearch:l.search,onScanRange:l.scanRange,partitionCounts:null==(r=l.stats)?void 0:r.partitionCounts,onLoadPartition:l.loadPartition,onNotice:S,onOpenTokens:()=>h(!0)}),"overview"===o&&(0,s.jsx)(te,{stats:l.stats,statsError:l.statsError,recordsError:l.error,user:l.user,loaded:l.records.length,hasMore:l.hasMore,updatedAt:l.updatedAt,onRetry:()=>void l.refresh(),onAnalyze:()=>p(!0)}),"query"===o&&(0,s.jsx)(tp,{...z}),"indexes"===o&&(0,s.jsx)(tw,{...z}),"replication"===o&&(0,s.jsx)(tI,{...z}),"backups"===o&&(0,s.jsx)(tK,{...z}),"keys"===o&&(0,s.jsx)(t6,{...z}),"metrics"===o&&(0,s.jsx)(rb,{...z}),"logs"===o&&(0,s.jsx)(rP,{...z}),"settings"===o&&(0,s.jsx)(rI,{...z})]})]}),(0,s.jsx)(rq,{open:u,onClose:()=>x(!1),onSave:l.save,onLookup:l.lookup,onNotice:S}),(0,s.jsx)(rZ,{open:m,onClose:()=>h(!1),onNotice:S}),(0,s.jsx)(rJ,{open:f,onClose:()=>p(!1)}),(0,s.jsx)(rG,{open:null!==j,userHash:j,onConfirm:()=>void A(),onClose:()=>v(null)}),(0,s.jsx)(rU,{open:null!==y,onOpenChange:e=>{e||N(null)},children:(0,s.jsx)(rW,{className:"max-w-2xl p-0",children:y&&(0,s.jsx)(rY,{record:y,onSave:async(e,t)=>{await l.update(e,t),N(null),c(await l.lookup(e))},onCancel:()=>N(null),onNotice:S})})}),(0,s.jsx)(tL,{open:null!==g,onOpenChange:e=>{e||b(null)},children:(0,s.jsxs)(tM,{children:[(0,s.jsx)(tA,{className:"text-lg font-semibold",children:"Delete this record?"}),(0,s.jsxs)(tz,{className:"mt-2 text-sm text-muted-foreground",children:["Key ",(0,s.jsx)("span",{className:"font-mono",children:g})," and its value will be permanently removed."]}),(0,s.jsxs)("div",{className:"mt-6 flex justify-end gap-2",children:[(0,s.jsx)(t_,{asChild:!0,children:(0,s.jsx)(_,{variant:"outline",children:"Cancel"})}),(0,s.jsx)(tP,{asChild:!0,children:(0,s.jsx)(_,{variant:"destructive",onClick:()=>void C(),children:"Delete record"})})]})]})}),w&&(0,s.jsxs)("div",{role:w.error?"alert":"status",className:"fixed bottom-5 right-5 z-[70] flex max-w-sm items-center gap-3 rounded-lg border bg-card px-4 py-3 text-sm shadow-lg ".concat(w.error?"border-destructive text-destructive":""),children:[(0,s.jsx)("span",{children:w.message}),(0,s.jsx)("button",{"aria-label":"Dismiss message",onClick:()=>k(null),children:(0,s.jsx)(n.A,{size:15})})]})]})}},5368:(e,t,r)=>{Promise.resolve().then(r.bind(r,3199))}},e=>{e.O(0,[685,441,255,358],()=>e(e.s=5368)),_N_E=e.O()}]); \ No newline at end of file diff --git a/internal/web/static/_next/static/chunks/main-app-2031bb93e2086048.js b/internal/web/static/_next/static/chunks/main-app-2031bb93e2086048.js deleted file mode 100644 index f44f22b..0000000 --- a/internal/web/static/_next/static/chunks/main-app-2031bb93e2086048.js +++ /dev/null @@ -1 +0,0 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[358],{8612:(e,s,n)=>{Promise.resolve().then(n.t.bind(n,7150,23)),Promise.resolve().then(n.t.bind(n,1959,23)),Promise.resolve().then(n.t.bind(n,7989,23)),Promise.resolve().then(n.t.bind(n,3886,23)),Promise.resolve().then(n.t.bind(n,9766,23)),Promise.resolve().then(n.t.bind(n,5278,23)),Promise.resolve().then(n.t.bind(n,8924,23)),Promise.resolve().then(n.t.bind(n,4431,23)),Promise.resolve().then(n.bind(n,622))},9393:()=>{}},e=>{var s=s=>e(e.s=s);e.O(0,[441,255],()=>(s(1666),s(8612))),_N_E=e.O()}]); \ No newline at end of file diff --git a/internal/web/static/_next/static/chunks/main-app-7d29ad9cfce6bfa2.js b/internal/web/static/_next/static/chunks/main-app-7d29ad9cfce6bfa2.js new file mode 100644 index 0000000..081cd81 --- /dev/null +++ b/internal/web/static/_next/static/chunks/main-app-7d29ad9cfce6bfa2.js @@ -0,0 +1 @@ +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[358],{820:(e,s,n)=>{Promise.resolve().then(n.t.bind(n,7150,23)),Promise.resolve().then(n.t.bind(n,1959,23)),Promise.resolve().then(n.t.bind(n,7989,23)),Promise.resolve().then(n.t.bind(n,3886,23)),Promise.resolve().then(n.t.bind(n,9766,23)),Promise.resolve().then(n.t.bind(n,5278,23)),Promise.resolve().then(n.t.bind(n,8924,23)),Promise.resolve().then(n.t.bind(n,4431,23)),Promise.resolve().then(n.bind(n,622))},9393:()=>{}},e=>{var s=s=>e(e.s=s);e.O(0,[441,255],()=>(s(1666),s(820))),_N_E=e.O()}]); \ No newline at end of file diff --git a/internal/web/static/_next/static/css/27e172611fa9eaf5.css b/internal/web/static/_next/static/css/27e172611fa9eaf5.css deleted file mode 100644 index 7a5be89..0000000 --- a/internal/web/static/_next/static/css/27e172611fa9eaf5.css +++ /dev/null @@ -1,3 +0,0 @@ -*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgb(59 130 246/0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgb(59 130 246/0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/* -! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com -*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.visible{visibility:visible}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.inset-y-0{top:0;bottom:0}.bottom-2{bottom:.5rem}.bottom-3{bottom:.75rem}.bottom-5{bottom:1.25rem}.left-1\/2{left:50%}.left-2{left:.5rem}.left-3{left:.75rem}.right-0{right:0}.right-2{right:.5rem}.right-3{right:.75rem}.right-4{right:1rem}.right-5{right:1.25rem}.top-0{top:0}.top-1\/2{top:50%}.top-2{top:.5rem}.top-2\.5{top:.625rem}.top-3{top:.75rem}.top-4{top:1rem}.z-10{z-index:10}.z-50{z-index:50}.z-\[60\]{z-index:60}.z-\[70\]{z-index:70}.col-span-2{grid-column:span 2/span 2}.m-1{margin:.25rem}.m-6{margin:1.5rem}.mx-auto{margin-left:auto;margin-right:auto}.my-3{margin-top:.75rem;margin-bottom:.75rem}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-2\.5{margin-left:.625rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-1\.5{margin-right:.375rem}.mr-2{margin-right:.5rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.mt-6{margin-top:1.5rem}.mt-auto{margin-top:auto}.line-clamp-1{-webkit-line-clamp:1}.line-clamp-1,.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical}.line-clamp-2{-webkit-line-clamp:2}.block{display:block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.aspect-\[3\/4\]{aspect-ratio:3/4}.aspect-square{aspect-ratio:1/1}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-14{height:3.5rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-\[calc\(100vh-18rem\)\]{height:calc(100vh - 18rem)}.h-full{height:100%}.max-h-40{max-height:10rem}.max-h-56{max-height:14rem}.max-h-64{max-height:16rem}.max-h-72{max-height:18rem}.max-h-\[420px\]{max-height:420px}.max-h-\[90vh\]{max-height:90vh}.min-h-0{min-height:0}.min-h-10{min-height:2.5rem}.min-h-\[20rem\]{min-height:20rem}.min-h-\[320px\]{min-height:320px}.min-h-dvh{min-height:100dvh}.min-h-screen{min-height:100vh}.w-1\.5{width:.375rem}.w-1\/3{width:33.333333%}.w-10{width:2.5rem}.w-11{width:2.75rem}.w-12{width:3rem}.w-14{width:3.5rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-20{width:5rem}.w-28{width:7rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-40{width:10rem}.w-6{width:1.5rem}.w-7{width:1.75rem}.w-8{width:2rem}.w-9{width:2.25rem}.w-\[35\%\]{width:35%}.w-\[calc\(100\%-2rem\)\]{width:calc(100% - 2rem)}.w-auto{width:auto}.w-full{width:100%}.min-w-0{min-width:0}.min-w-\[200px\]{min-width:200px}.min-w-\[640px\]{min-width:640px}.min-w-\[760px\]{min-width:760px}.max-w-2xl{max-width:42rem}.max-w-\[1400px\]{max-width:1400px}.max-w-\[200px\]{max-width:200px}.max-w-\[360px\]{max-width:360px}.max-w-full{max-width:100%}.max-w-md{max-width:28rem}.max-w-sm{max-width:24rem}.max-w-xl{max-width:36rem}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.-translate-x-1\/2{--tw-translate-x:-50%}.-translate-x-1\/2,.-translate-y-1\/2{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y:-50%}.rotate-90{--tw-rotate:90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.select-all{-webkit-user-select:all;-moz-user-select:all;user-select:all}.list-decimal{list-style-type:decimal}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-\[1fr_auto\]{grid-template-columns:1fr auto}.grid-cols-\[minmax\(6rem\2c 32\%\)_minmax\(0\2c 1fr\)\]{grid-template-columns:minmax(6rem,32%) minmax(0,1fr)}.grid-cols-\[minmax\(7rem\2c 30\%\)_minmax\(0\2c 1fr\)\]{grid-template-columns:minmax(7rem,30%) minmax(0,1fr)}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.items-baseline{align-items:baseline}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-2\.5{gap:.625rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-8{gap:2rem}.gap-\[3px\]{gap:3px}.gap-x-4{-moz-column-gap:1rem;column-gap:1rem}.gap-x-5{-moz-column-gap:1.25rem;column-gap:1.25rem}.gap-y-1{row-gap:.25rem}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.125rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem * var(--tw-space-y-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.375rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-3\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.875rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.875rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.25rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.divide-border>:not([hidden])~:not([hidden]){border-color:hsl(var(--border))}.divide-border\/50>:not([hidden])~:not([hidden]){border-color:hsl(var(--border)/.5)}.divide-border\/60>:not([hidden])~:not([hidden]){border-color:hsl(var(--border)/.6)}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.scroll-smooth{scroll-behavior:smooth}.truncate{overflow:hidden;text-overflow:ellipsis}.truncate,.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-\[3px\]{border-radius:3px}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-l-2{border-left-width:2px}.border-l-4{border-left-width:4px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-amber-500{--tw-border-opacity:1;border-color:rgb(245 158 11/var(--tw-border-opacity,1))}.border-amber-500\/20{border-color:rgb(245 158 11/.2)}.border-amber-500\/30{border-color:rgb(245 158 11/.3)}.border-blue-500{--tw-border-opacity:1;border-color:rgb(59 130 246/var(--tw-border-opacity,1))}.border-blue-500\/30{border-color:rgb(59 130 246/.3)}.border-border{border-color:hsl(var(--border))}.border-border\/50{border-color:hsl(var(--border)/.5)}.border-border\/60{border-color:hsl(var(--border)/.6)}.border-border\/70{border-color:hsl(var(--border)/.7)}.border-cyan-500{--tw-border-opacity:1;border-color:rgb(6 182 212/var(--tw-border-opacity,1))}.border-cyan-500\/30{border-color:rgb(6 182 212/.3)}.border-destructive{border-color:hsl(var(--destructive))}.border-destructive\/30{border-color:hsl(var(--destructive)/.3)}.border-destructive\/40{border-color:hsl(var(--destructive)/.4)}.border-emerald-500{--tw-border-opacity:1;border-color:rgb(16 185 129/var(--tw-border-opacity,1))}.border-emerald-500\/30{border-color:rgb(16 185 129/.3)}.border-fuchsia-500{--tw-border-opacity:1;border-color:rgb(217 70 239/var(--tw-border-opacity,1))}.border-indigo-500{--tw-border-opacity:1;border-color:rgb(99 102 241/var(--tw-border-opacity,1))}.border-info\/40{border-color:rgb(96 165 250/.4)}.border-input{border-color:hsl(var(--input))}.border-lime-500\/30{border-color:rgb(132 204 22/.3)}.border-muted-foreground{border-color:hsl(var(--muted-foreground))}.border-orange-500{--tw-border-opacity:1;border-color:rgb(249 115 22/var(--tw-border-opacity,1))}.border-primary{border-color:hsl(var(--primary))}.border-primary\/20{border-color:hsl(var(--primary)/.2)}.border-primary\/30{border-color:hsl(var(--primary)/.3)}.border-primary\/40{border-color:hsl(var(--primary)/.4)}.border-purple-500{--tw-border-opacity:1;border-color:rgb(168 85 247/var(--tw-border-opacity,1))}.border-purple-500\/30{border-color:rgb(168 85 247/.3)}.border-red-500\/30{border-color:rgb(239 68 68/.3)}.border-rose-500{--tw-border-opacity:1;border-color:rgb(244 63 94/var(--tw-border-opacity,1))}.border-rose-500\/30{border-color:rgb(244 63 94/.3)}.border-sky-500{--tw-border-opacity:1;border-color:rgb(14 165 233/var(--tw-border-opacity,1))}.border-slate-400{--tw-border-opacity:1;border-color:rgb(148 163 184/var(--tw-border-opacity,1))}.border-teal-500{--tw-border-opacity:1;border-color:rgb(20 184 166/var(--tw-border-opacity,1))}.border-transparent{border-color:transparent}.border-violet-500{--tw-border-opacity:1;border-color:rgb(139 92 246/var(--tw-border-opacity,1))}.border-warning\/30{border-color:rgb(251 191 36/.3)}.border-warning\/40{border-color:rgb(251 191 36/.4)}.bg-amber-500\/10{background-color:rgb(245 158 11/.1)}.bg-amber-500\/15{background-color:rgb(245 158 11/.15)}.bg-amber-600{--tw-bg-opacity:1;background-color:rgb(217 119 6/var(--tw-bg-opacity,1))}.bg-background{background-color:hsl(var(--background))}.bg-beast-lime{--tw-bg-opacity:1;background-color:rgb(168 242 26/var(--tw-bg-opacity,1))}.bg-black\/55{background-color:rgb(0 0 0/.55)}.bg-black\/75{background-color:rgb(0 0 0/.75)}.bg-blue-500\/15{background-color:rgb(59 130 246/.15)}.bg-blue-600{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.bg-border\/60{background-color:hsl(var(--border)/.6)}.bg-card{background-color:hsl(var(--card))}.bg-card\/40{background-color:hsl(var(--card)/.4)}.bg-card\/50{background-color:hsl(var(--card)/.5)}.bg-card\/60{background-color:hsl(var(--card)/.6)}.bg-cyan-400{--tw-bg-opacity:1;background-color:rgb(34 211 238/var(--tw-bg-opacity,1))}.bg-cyan-500\/15{background-color:rgb(6 182 212/.15)}.bg-cyan-600{--tw-bg-opacity:1;background-color:rgb(8 145 178/var(--tw-bg-opacity,1))}.bg-destructive{background-color:hsl(var(--destructive))}.bg-destructive\/10{background-color:hsl(var(--destructive)/.1)}.bg-destructive\/15{background-color:hsl(var(--destructive)/.15)}.bg-destructive\/5{background-color:hsl(var(--destructive)/.05)}.bg-emerald-500\/15{background-color:rgb(16 185 129/.15)}.bg-emerald-600{--tw-bg-opacity:1;background-color:rgb(5 150 105/var(--tw-bg-opacity,1))}.bg-foreground\/25{background-color:hsl(var(--foreground)/.25)}.bg-foreground\/50{background-color:hsl(var(--foreground)/.5)}.bg-info{--tw-bg-opacity:1;background-color:rgb(96 165 250/var(--tw-bg-opacity,1))}.bg-info\/10{background-color:rgb(96 165 250/.1)}.bg-lime-500\/15{background-color:rgb(132 204 22/.15)}.bg-muted{background-color:hsl(var(--muted))}.bg-muted\/40{background-color:hsl(var(--muted)/.4)}.bg-muted\/60{background-color:hsl(var(--muted)/.6)}.bg-muted\/80{background-color:hsl(var(--muted)/.8)}.bg-muted\/90{background-color:hsl(var(--muted)/.9)}.bg-primary{background-color:hsl(var(--primary))}.bg-primary\/10{background-color:hsl(var(--primary)/.1)}.bg-primary\/5{background-color:hsl(var(--primary)/.05)}.bg-purple-400{--tw-bg-opacity:1;background-color:rgb(192 132 252/var(--tw-bg-opacity,1))}.bg-purple-500\/15{background-color:rgb(168 85 247/.15)}.bg-purple-600{--tw-bg-opacity:1;background-color:rgb(147 51 234/var(--tw-bg-opacity,1))}.bg-red-500\/15{background-color:rgb(239 68 68/.15)}.bg-rose-500\/15{background-color:rgb(244 63 94/.15)}.bg-rose-600{--tw-bg-opacity:1;background-color:rgb(225 29 72/var(--tw-bg-opacity,1))}.bg-secondary{background-color:hsl(var(--secondary))}.bg-sidebar{background-color:hsl(var(--sidebar))}.bg-transparent{background-color:transparent}.bg-warning{--tw-bg-opacity:1;background-color:rgb(251 191 36/var(--tw-bg-opacity,1))}.bg-warning\/10{background-color:rgb(251 191 36/.1)}.bg-zinc-800{--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1))}.fill-amber-400{fill:#fbbf24}.fill-border{fill:hsl(var(--border))}.fill-card{fill:hsl(var(--card))}.fill-foreground{fill:hsl(var(--foreground))}.fill-foreground\/35{fill:hsl(var(--foreground)/.35)}.fill-foreground\/60{fill:hsl(var(--foreground)/.6)}.fill-muted-foreground{fill:hsl(var(--muted-foreground))}.fill-primary{fill:hsl(var(--primary))}.fill-primary\/20{fill:hsl(var(--primary)/.2)}.stroke-border{stroke:hsl(var(--border))}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.p-0{padding:0}.p-0\.5{padding:.125rem}.p-1{padding:.25rem}.p-10{padding:2.5rem}.p-12{padding:3rem}.p-2{padding:.5rem}.p-2\.5{padding:.625rem}.p-3{padding:.75rem}.p-3\.5{padding:.875rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-14{padding-top:3.5rem;padding-bottom:3.5rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-3\.5{padding-top:.875rem;padding-bottom:.875rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-1{padding-bottom:.25rem}.pb-2\.5{padding-bottom:.625rem}.pb-3{padding-bottom:.75rem}.pb-4{padding-bottom:1rem}.pb-5{padding-bottom:1.25rem}.pl-5{padding-left:1.25rem}.pl-9{padding-left:2.25rem}.pr-1{padding-right:.25rem}.pr-4{padding-right:1rem}.pr-8{padding-right:2rem}.pt-0{padding-top:0}.pt-1{padding-top:.25rem}.pt-2{padding-top:.5rem}.pt-2\.5{padding-top:.625rem}.pt-3{padding-top:.75rem}.pt-4{padding-top:1rem}.pt-5{padding-top:1.25rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[13px\]{font-size:13px}.text-\[22px\]{font-size:22px}.text-\[9px\]{font-size:9px}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.capitalize{text-transform:capitalize}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-5{line-height:1.25rem}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.leading-tight{line-height:1.25}.tracking-tight{letter-spacing:-.025em}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.tracking-widest{letter-spacing:.1em}.text-amber-400{--tw-text-opacity:1;color:rgb(251 191 36/var(--tw-text-opacity,1))}.text-amber-500{--tw-text-opacity:1;color:rgb(245 158 11/var(--tw-text-opacity,1))}.text-amber-600{--tw-text-opacity:1;color:rgb(217 119 6/var(--tw-text-opacity,1))}.text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity,1))}.text-blue-500{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity,1))}.text-blue-600{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity,1))}.text-card-foreground{color:hsl(var(--card-foreground))}.text-cyan-500{--tw-text-opacity:1;color:rgb(6 182 212/var(--tw-text-opacity,1))}.text-cyan-600{--tw-text-opacity:1;color:rgb(8 145 178/var(--tw-text-opacity,1))}.text-destructive{color:hsl(var(--destructive))}.text-emerald-500{--tw-text-opacity:1;color:rgb(16 185 129/var(--tw-text-opacity,1))}.text-emerald-600{--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1))}.text-foreground{color:hsl(var(--foreground))}.text-indigo-500{--tw-text-opacity:1;color:rgb(99 102 241/var(--tw-text-opacity,1))}.text-info{--tw-text-opacity:1;color:rgb(96 165 250/var(--tw-text-opacity,1))}.text-lime-600{--tw-text-opacity:1;color:rgb(101 163 13/var(--tw-text-opacity,1))}.text-muted-foreground{color:hsl(var(--muted-foreground))}.text-muted-foreground\/60{color:hsl(var(--muted-foreground)/.6)}.text-muted-foreground\/70{color:hsl(var(--muted-foreground)/.7)}.text-muted-foreground\/80{color:hsl(var(--muted-foreground)/.8)}.text-primary{color:hsl(var(--primary))}.text-primary-foreground{color:hsl(var(--primary-foreground))}.text-purple-500{--tw-text-opacity:1;color:rgb(168 85 247/var(--tw-text-opacity,1))}.text-purple-600{--tw-text-opacity:1;color:rgb(147 51 234/var(--tw-text-opacity,1))}.text-red-600{--tw-text-opacity:1;color:rgb(220 38 38/var(--tw-text-opacity,1))}.text-rose-500{--tw-text-opacity:1;color:rgb(244 63 94/var(--tw-text-opacity,1))}.text-rose-600{--tw-text-opacity:1;color:rgb(225 29 72/var(--tw-text-opacity,1))}.text-secondary-foreground{color:hsl(var(--secondary-foreground))}.text-sky-500{--tw-text-opacity:1;color:rgb(14 165 233/var(--tw-text-opacity,1))}.text-slate-400{--tw-text-opacity:1;color:rgb(148 163 184/var(--tw-text-opacity,1))}.text-success{--tw-text-opacity:1;color:rgb(45 212 191/var(--tw-text-opacity,1))}.text-teal-500{--tw-text-opacity:1;color:rgb(20 184 166/var(--tw-text-opacity,1))}.text-violet-500{--tw-text-opacity:1;color:rgb(139 92 246/var(--tw-text-opacity,1))}.text-warning{--tw-text-opacity:1;color:rgb(251 191 36/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.text-zinc-200{--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity,1))}.underline{text-decoration-line:underline}.underline-offset-2{text-underline-offset:2px}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.opacity-50{opacity:.5}.opacity-70{opacity:.7}.opacity-80{opacity:.8}.shadow-\[0_0_20px_rgba\(168\2c 242\2c 26\2c 0\.25\)\]{--tw-shadow:0 0 20px rgba(168,242,26,0.25);--tw-shadow-colored:0 0 20px var(--tw-shadow-color)}.shadow-\[0_0_20px_rgba\(168\2c 242\2c 26\2c 0\.25\)\],.shadow-lg{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px rgb(0 0 0/0.1),0 4px 6px -4px rgb(0 0 0/0.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-md{--tw-shadow:0 4px 6px -1px rgb(0 0 0/0.1),0 2px 4px -2px rgb(0 0 0/0.1);--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.shadow-md,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 2px 0 rgb(0 0 0/0.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.shadow-xl{--tw-shadow:0 20px 25px -5px rgb(0 0 0/0.1),0 8px 10px -6px rgb(0 0 0/0.1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.outline{outline-style:solid}.ring{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring,.ring-2{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-2{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring-primary\/30{--tw-ring-color:hsl(var(--primary)/0.3)}.\!filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)!important}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-sm{--tw-backdrop-blur:blur(4px);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-150,.transition-transform{transition-duration:.15s}.duration-300{transition-duration:.3s}.duration-700{transition-duration:.7s}:root{color-scheme:light;--background:0 0% 100%;--foreground:240 10% 3.9%;--card:0 0% 100%;--card-foreground:240 10% 3.9%;--popover:0 0% 100%;--popover-foreground:240 10% 3.9%;--primary:84 81% 36%;--primary-foreground:0 0% 98%;--secondary:240 4.8% 95.9%;--secondary-foreground:240 5.9% 10%;--muted:240 4.8% 95.9%;--muted-foreground:240 3.8% 46.1%;--accent:240 4.8% 95.9%;--accent-foreground:240 5.9% 10%;--destructive:0 84.2% 60.2%;--destructive-foreground:0 0% 98%;--border:240 5.9% 90%;--input:240 5.9% 90%;--ring:80 89% 45%;--radius:0.5rem;--sidebar:240 4.8% 97%;--code:240 4.8% 95.9%}.dark{color-scheme:dark;--background:120 14% 6%;--foreground:100 17% 95%;--card:120 11% 9%;--card-foreground:100 17% 95%;--popover:120 11% 9%;--popover-foreground:100 17% 95%;--primary:80 89% 53%;--primary-foreground:90 70% 3%;--secondary:112 13% 12%;--secondary-foreground:100 17% 95%;--muted:112 13% 12%;--muted-foreground:107 8% 60%;--accent:112 13% 12%;--accent-foreground:100 17% 95%;--destructive:0 90% 71%;--destructive-foreground:0 0% 98%;--border:120 12% 18%;--input:120 12% 18%;--ring:80 89% 53%;--sidebar:120 11% 8%;--code:120 14% 5%}*{border-color:hsl(var(--border))}body{background-color:hsl(var(--background));color:hsl(var(--foreground));min-height:100vh;font-feature-settings:"rlig" 1,"calt" 1}button,input,select,textarea{font:inherit}:focus-visible{outline:2px solid hsl(var(--ring));outline-offset:2px}::-moz-selection{background:#A8F21A;color:#000000}::selection{background:#A8F21A;color:#000000}::-webkit-scrollbar{width:7px;height:7px}::-webkit-scrollbar-track{background:hsl(var(--background))}::-webkit-scrollbar-thumb{background:hsl(var(--border));border-radius:4px}::-webkit-scrollbar-thumb:hover{background:hsl(var(--muted-foreground))}.marker\:font-mono ::marker{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.marker\:font-mono::marker{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.selection\:bg-beast-lime ::-moz-selection{--tw-bg-opacity:1;background-color:rgb(168 242 26/var(--tw-bg-opacity,1))}.selection\:bg-beast-lime ::selection{--tw-bg-opacity:1;background-color:rgb(168 242 26/var(--tw-bg-opacity,1))}.selection\:text-black ::-moz-selection{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity,1))}.selection\:text-black ::selection{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity,1))}.selection\:bg-beast-lime::-moz-selection{--tw-bg-opacity:1;background-color:rgb(168 242 26/var(--tw-bg-opacity,1))}.selection\:bg-beast-lime::selection{--tw-bg-opacity:1;background-color:rgb(168 242 26/var(--tw-bg-opacity,1))}.selection\:text-black::-moz-selection{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity,1))}.selection\:text-black::selection{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity,1))}.placeholder\:text-muted-foreground::-moz-placeholder{color:hsl(var(--muted-foreground))}.placeholder\:text-muted-foreground::placeholder{color:hsl(var(--muted-foreground))}.first\:pt-0:first-child{padding-top:0}.last\:mb-0:last-child{margin-bottom:0}.last\:border-0:last-child{border-width:0}.last\:pb-0:last-child{padding-bottom:0}.hover\:border-primary\/40:hover{border-color:hsl(var(--primary)/.4)}.hover\:border-primary\/50:hover{border-color:hsl(var(--primary)/.5)}.hover\:bg-accent\/50:hover{background-color:hsl(var(--accent)/.5)}.hover\:bg-beast-lime-hover:hover{--tw-bg-opacity:1;background-color:rgb(151 222 16/var(--tw-bg-opacity,1))}.hover\:bg-destructive:hover{background-color:hsl(var(--destructive))}.hover\:bg-destructive\/10:hover{background-color:hsl(var(--destructive)/.1)}.hover\:bg-destructive\/25:hover{background-color:hsl(var(--destructive)/.25)}.hover\:bg-muted:hover{background-color:hsl(var(--muted))}.hover\:bg-muted\/40:hover{background-color:hsl(var(--muted)/.4)}.hover\:bg-muted\/50:hover{background-color:hsl(var(--muted)/.5)}.hover\:bg-muted\/60:hover{background-color:hsl(var(--muted)/.6)}.hover\:bg-primary\/10:hover{background-color:hsl(var(--primary)/.1)}.hover\:bg-primary\/20:hover{background-color:hsl(var(--primary)/.2)}.hover\:bg-secondary\/80:hover{background-color:hsl(var(--secondary)/.8)}.hover\:text-destructive:hover{color:hsl(var(--destructive))}.hover\:text-destructive-foreground:hover{color:hsl(var(--destructive-foreground))}.hover\:text-foreground:hover{color:hsl(var(--foreground))}.hover\:text-primary:hover{color:hsl(var(--primary))}.hover\:underline:hover{text-decoration-line:underline}.hover\:shadow-md:hover{--tw-shadow:0 4px 6px -1px rgb(0 0 0/0.1),0 2px 4px -2px rgb(0 0 0/0.1);--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-1:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-1:focus,.focus\:ring-2:focus{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-primary:focus{--tw-ring-color:hsl(var(--primary))}.focus\:ring-ring:focus{--tw-ring-color:hsl(var(--ring))}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px}.focus-visible\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus-visible\:ring-ring:focus-visible{--tw-ring-color:hsl(var(--ring))}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:scale-105{--tw-scale-x:1.05;--tw-scale-y:1.05;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:text-primary{color:hsl(var(--primary))}.data-\[state\=active\]\:border[data-state=active]{border-width:1px}.data-\[state\=active\]\:border-primary\/30[data-state=active]{border-color:hsl(var(--primary)/.3)}.data-\[state\=active\]\:bg-primary\/10[data-state=active]{background-color:hsl(var(--primary)/.1)}.data-\[state\=active\]\:font-medium[data-state=active]{font-weight:500}.data-\[state\=active\]\:text-primary[data-state=active]{color:hsl(var(--primary))}.dark\:block:is(.dark *){display:block}.dark\:hidden:is(.dark *){display:none}.dark\:text-amber-400:is(.dark *){--tw-text-opacity:1;color:rgb(251 191 36/var(--tw-text-opacity,1))}.dark\:text-blue-400:is(.dark *){--tw-text-opacity:1;color:rgb(96 165 250/var(--tw-text-opacity,1))}.dark\:text-cyan-400:is(.dark *){--tw-text-opacity:1;color:rgb(34 211 238/var(--tw-text-opacity,1))}.dark\:text-emerald-400:is(.dark *){--tw-text-opacity:1;color:rgb(52 211 153/var(--tw-text-opacity,1))}.dark\:text-lime-400:is(.dark *){--tw-text-opacity:1;color:rgb(163 230 53/var(--tw-text-opacity,1))}.dark\:text-purple-400:is(.dark *){--tw-text-opacity:1;color:rgb(192 132 252/var(--tw-text-opacity,1))}.dark\:text-red-400:is(.dark *){--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity,1))}.dark\:text-rose-400:is(.dark *){--tw-text-opacity:1;color:rgb(251 113 133/var(--tw-text-opacity,1))}@media (min-width:640px){.sm\:col-span-1{grid-column:span 1/span 1}.sm\:inline{display:inline}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:grid-cols-\[1fr_100px_1fr_auto\]{grid-template-columns:1fr 100px 1fr auto}.sm\:grid-cols-\[1fr_140px_120px\]{grid-template-columns:1fr 140px 120px}.sm\:grid-cols-\[1fr_1fr_120px\]{grid-template-columns:1fr 1fr 120px}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:gap-4{gap:1rem}}@media (min-width:768px){.md\:block{display:block}.md\:flex{display:flex}.md\:hidden{display:none}.md\:h-dvh{height:100dvh}.md\:w-60{width:15rem}.md\:flex-1{flex:1 1 0%}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:flex-col{flex-direction:column}.md\:overflow-hidden{overflow:hidden}.md\:overflow-y-auto{overflow-y:auto}.md\:border-b-0{border-bottom-width:0}.md\:border-r{border-right-width:1px}.md\:px-3{padding-left:.75rem;padding-right:.75rem}.md\:px-5{padding-left:1.25rem;padding-right:1.25rem}.md\:px-8{padding-left:2rem;padding-right:2rem}.md\:py-6{padding-top:1.5rem;padding-bottom:1.5rem}.md\:py-8{padding-top:2rem;padding-bottom:2rem}.md\:pb-2{padding-bottom:.5rem}.md\:pt-4{padding-top:1rem}.md\:pt-5{padding-top:1.25rem}}@media (min-width:1024px){.lg\:col-span-2{grid-column:span 2/span 2}.lg\:flex{display:flex}.lg\:hidden{display:none}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.lg\:grid-cols-\[220px_minmax\(0\2c 1fr\)\]{grid-template-columns:220px minmax(0,1fr)}.lg\:grid-cols-\[220px_minmax\(0\2c 1fr\)_420px\]{grid-template-columns:220px minmax(0,1fr) 420px}.lg\:grid-cols-\[220px_minmax\(320px\2c 1fr\)_minmax\(380px\2c 1\.4fr\)\]{grid-template-columns:220px minmax(320px,1fr) minmax(380px,1.4fr)}}@media (min-width:1280px){.xl\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.xl\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}} \ No newline at end of file diff --git a/internal/web/static/_next/static/css/f98f38bad263934a.css b/internal/web/static/_next/static/css/f98f38bad263934a.css new file mode 100644 index 0000000..b9d8a92 --- /dev/null +++ b/internal/web/static/_next/static/css/f98f38bad263934a.css @@ -0,0 +1,3 @@ +*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgb(59 130 246/0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgb(59 130 246/0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/* +! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com +*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.visible{visibility:visible}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.inset-y-0{top:0;bottom:0}.bottom-2{bottom:.5rem}.bottom-3{bottom:.75rem}.bottom-5{bottom:1.25rem}.left-1\/2{left:50%}.left-2{left:.5rem}.left-3{left:.75rem}.right-0{right:0}.right-2{right:.5rem}.right-3{right:.75rem}.right-4{right:1rem}.right-5{right:1.25rem}.top-0{top:0}.top-1\/2{top:50%}.top-2{top:.5rem}.top-2\.5{top:.625rem}.top-3{top:.75rem}.top-4{top:1rem}.z-10{z-index:10}.z-50{z-index:50}.z-\[60\]{z-index:60}.z-\[70\]{z-index:70}.col-span-2{grid-column:span 2/span 2}.m-1{margin:.25rem}.m-6{margin:1.5rem}.mx-auto{margin-left:auto;margin-right:auto}.my-3{margin-top:.75rem;margin-bottom:.75rem}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-2\.5{margin-left:.625rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-1\.5{margin-right:.375rem}.mr-2{margin-right:.5rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.mt-6{margin-top:1.5rem}.mt-auto{margin-top:auto}.line-clamp-1{-webkit-line-clamp:1}.line-clamp-1,.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical}.line-clamp-2{-webkit-line-clamp:2}.block{display:block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.aspect-\[3\/4\]{aspect-ratio:3/4}.aspect-square{aspect-ratio:1/1}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-14{height:3.5rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-\[calc\(100vh-18rem\)\]{height:calc(100vh - 18rem)}.h-full{height:100%}.max-h-40{max-height:10rem}.max-h-56{max-height:14rem}.max-h-64{max-height:16rem}.max-h-72{max-height:18rem}.max-h-\[420px\]{max-height:420px}.max-h-\[90vh\]{max-height:90vh}.min-h-0{min-height:0}.min-h-10{min-height:2.5rem}.min-h-\[20rem\]{min-height:20rem}.min-h-\[320px\]{min-height:320px}.min-h-dvh{min-height:100dvh}.min-h-screen{min-height:100vh}.w-1\.5{width:.375rem}.w-1\/3{width:33.333333%}.w-10{width:2.5rem}.w-12{width:3rem}.w-14{width:3.5rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-20{width:5rem}.w-24{width:6rem}.w-28{width:7rem}.w-3\.5{width:.875rem}.w-32{width:8rem}.w-4{width:1rem}.w-40{width:10rem}.w-6{width:1.5rem}.w-7{width:1.75rem}.w-8{width:2rem}.w-9{width:2.25rem}.w-\[35\%\]{width:35%}.w-\[4\.5rem\]{width:4.5rem}.w-\[calc\(100\%-2rem\)\]{width:calc(100% - 2rem)}.w-auto{width:auto}.w-full{width:100%}.min-w-0{min-width:0}.min-w-\[200px\]{min-width:200px}.min-w-\[720px\]{min-width:720px}.min-w-\[760px\]{min-width:760px}.max-w-2xl{max-width:42rem}.max-w-\[1400px\]{max-width:1400px}.max-w-\[200px\]{max-width:200px}.max-w-\[360px\]{max-width:360px}.max-w-full{max-width:100%}.max-w-md{max-width:28rem}.max-w-sm{max-width:24rem}.max-w-xl{max-width:36rem}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.-translate-x-1\/2{--tw-translate-x:-50%}.-translate-x-1\/2,.-translate-y-1\/2{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y:-50%}.rotate-90{--tw-rotate:90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.select-all{-webkit-user-select:all;-moz-user-select:all;user-select:all}.list-decimal{list-style-type:decimal}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-\[1fr_auto\]{grid-template-columns:1fr auto}.grid-cols-\[minmax\(6rem\2c 32\%\)_minmax\(0\2c 1fr\)\]{grid-template-columns:minmax(6rem,32%) minmax(0,1fr)}.grid-cols-\[minmax\(7rem\2c 30\%\)_minmax\(0\2c 1fr\)\]{grid-template-columns:minmax(7rem,30%) minmax(0,1fr)}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.items-baseline{align-items:baseline}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-2\.5{gap:.625rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-8{gap:2rem}.gap-\[3px\]{gap:3px}.gap-x-4{-moz-column-gap:1rem;column-gap:1rem}.gap-x-5{-moz-column-gap:1.25rem;column-gap:1.25rem}.gap-y-1{row-gap:.25rem}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.125rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem * var(--tw-space-y-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.375rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-3\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.875rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.875rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.25rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.divide-border>:not([hidden])~:not([hidden]){border-color:hsl(var(--border))}.divide-border\/50>:not([hidden])~:not([hidden]){border-color:hsl(var(--border)/.5)}.divide-border\/60>:not([hidden])~:not([hidden]){border-color:hsl(var(--border)/.6)}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.scroll-smooth{scroll-behavior:smooth}.truncate{overflow:hidden;text-overflow:ellipsis}.truncate,.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-\[3px\]{border-radius:3px}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-y{border-top-width:1px}.border-b,.border-y{border-bottom-width:1px}.border-l{border-left-width:1px}.border-l-2{border-left-width:2px}.border-l-4{border-left-width:4px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-amber-500{--tw-border-opacity:1;border-color:rgb(245 158 11/var(--tw-border-opacity,1))}.border-amber-500\/20{border-color:rgb(245 158 11/.2)}.border-amber-500\/30{border-color:rgb(245 158 11/.3)}.border-blue-500{--tw-border-opacity:1;border-color:rgb(59 130 246/var(--tw-border-opacity,1))}.border-blue-500\/30{border-color:rgb(59 130 246/.3)}.border-border{border-color:hsl(var(--border))}.border-border\/50{border-color:hsl(var(--border)/.5)}.border-border\/60{border-color:hsl(var(--border)/.6)}.border-border\/70{border-color:hsl(var(--border)/.7)}.border-cyan-500{--tw-border-opacity:1;border-color:rgb(6 182 212/var(--tw-border-opacity,1))}.border-cyan-500\/30{border-color:rgb(6 182 212/.3)}.border-destructive{border-color:hsl(var(--destructive))}.border-destructive\/30{border-color:hsl(var(--destructive)/.3)}.border-destructive\/40{border-color:hsl(var(--destructive)/.4)}.border-emerald-500{--tw-border-opacity:1;border-color:rgb(16 185 129/var(--tw-border-opacity,1))}.border-emerald-500\/30{border-color:rgb(16 185 129/.3)}.border-fuchsia-500{--tw-border-opacity:1;border-color:rgb(217 70 239/var(--tw-border-opacity,1))}.border-indigo-500{--tw-border-opacity:1;border-color:rgb(99 102 241/var(--tw-border-opacity,1))}.border-info\/40{border-color:rgb(96 165 250/.4)}.border-input{border-color:hsl(var(--input))}.border-lime-500\/30{border-color:rgb(132 204 22/.3)}.border-muted-foreground{border-color:hsl(var(--muted-foreground))}.border-orange-500{--tw-border-opacity:1;border-color:rgb(249 115 22/var(--tw-border-opacity,1))}.border-primary{border-color:hsl(var(--primary))}.border-primary\/20{border-color:hsl(var(--primary)/.2)}.border-primary\/30{border-color:hsl(var(--primary)/.3)}.border-primary\/40{border-color:hsl(var(--primary)/.4)}.border-purple-500{--tw-border-opacity:1;border-color:rgb(168 85 247/var(--tw-border-opacity,1))}.border-purple-500\/30{border-color:rgb(168 85 247/.3)}.border-red-500\/30{border-color:rgb(239 68 68/.3)}.border-rose-500{--tw-border-opacity:1;border-color:rgb(244 63 94/var(--tw-border-opacity,1))}.border-rose-500\/30{border-color:rgb(244 63 94/.3)}.border-sky-500{--tw-border-opacity:1;border-color:rgb(14 165 233/var(--tw-border-opacity,1))}.border-slate-400{--tw-border-opacity:1;border-color:rgb(148 163 184/var(--tw-border-opacity,1))}.border-teal-500{--tw-border-opacity:1;border-color:rgb(20 184 166/var(--tw-border-opacity,1))}.border-transparent{border-color:transparent}.border-violet-500{--tw-border-opacity:1;border-color:rgb(139 92 246/var(--tw-border-opacity,1))}.border-warning\/30{border-color:rgb(251 191 36/.3)}.border-warning\/40{border-color:rgb(251 191 36/.4)}.bg-amber-500\/10{background-color:rgb(245 158 11/.1)}.bg-amber-500\/15{background-color:rgb(245 158 11/.15)}.bg-amber-600{--tw-bg-opacity:1;background-color:rgb(217 119 6/var(--tw-bg-opacity,1))}.bg-background{background-color:hsl(var(--background))}.bg-beast-lime{--tw-bg-opacity:1;background-color:rgb(168 242 26/var(--tw-bg-opacity,1))}.bg-black\/55{background-color:rgb(0 0 0/.55)}.bg-black\/75{background-color:rgb(0 0 0/.75)}.bg-blue-500\/15{background-color:rgb(59 130 246/.15)}.bg-blue-600{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.bg-border\/60{background-color:hsl(var(--border)/.6)}.bg-card{background-color:hsl(var(--card))}.bg-card\/40{background-color:hsl(var(--card)/.4)}.bg-card\/50{background-color:hsl(var(--card)/.5)}.bg-card\/60{background-color:hsl(var(--card)/.6)}.bg-cyan-400{--tw-bg-opacity:1;background-color:rgb(34 211 238/var(--tw-bg-opacity,1))}.bg-cyan-500\/15{background-color:rgb(6 182 212/.15)}.bg-cyan-600{--tw-bg-opacity:1;background-color:rgb(8 145 178/var(--tw-bg-opacity,1))}.bg-destructive{background-color:hsl(var(--destructive))}.bg-destructive\/10{background-color:hsl(var(--destructive)/.1)}.bg-destructive\/15{background-color:hsl(var(--destructive)/.15)}.bg-destructive\/5{background-color:hsl(var(--destructive)/.05)}.bg-emerald-500\/15{background-color:rgb(16 185 129/.15)}.bg-emerald-600{--tw-bg-opacity:1;background-color:rgb(5 150 105/var(--tw-bg-opacity,1))}.bg-foreground\/25{background-color:hsl(var(--foreground)/.25)}.bg-foreground\/50{background-color:hsl(var(--foreground)/.5)}.bg-info{--tw-bg-opacity:1;background-color:rgb(96 165 250/var(--tw-bg-opacity,1))}.bg-info\/10{background-color:rgb(96 165 250/.1)}.bg-lime-500\/15{background-color:rgb(132 204 22/.15)}.bg-muted{background-color:hsl(var(--muted))}.bg-muted\/40{background-color:hsl(var(--muted)/.4)}.bg-muted\/60{background-color:hsl(var(--muted)/.6)}.bg-muted\/80{background-color:hsl(var(--muted)/.8)}.bg-muted\/90{background-color:hsl(var(--muted)/.9)}.bg-primary{background-color:hsl(var(--primary))}.bg-primary\/10{background-color:hsl(var(--primary)/.1)}.bg-primary\/5{background-color:hsl(var(--primary)/.05)}.bg-purple-400{--tw-bg-opacity:1;background-color:rgb(192 132 252/var(--tw-bg-opacity,1))}.bg-purple-500\/15{background-color:rgb(168 85 247/.15)}.bg-purple-600{--tw-bg-opacity:1;background-color:rgb(147 51 234/var(--tw-bg-opacity,1))}.bg-red-500\/15{background-color:rgb(239 68 68/.15)}.bg-rose-500\/15{background-color:rgb(244 63 94/.15)}.bg-rose-600{--tw-bg-opacity:1;background-color:rgb(225 29 72/var(--tw-bg-opacity,1))}.bg-secondary{background-color:hsl(var(--secondary))}.bg-sidebar{background-color:hsl(var(--sidebar))}.bg-transparent{background-color:transparent}.bg-warning{--tw-bg-opacity:1;background-color:rgb(251 191 36/var(--tw-bg-opacity,1))}.bg-warning\/10{background-color:rgb(251 191 36/.1)}.bg-zinc-800{--tw-bg-opacity:1;background-color:rgb(39 39 42/var(--tw-bg-opacity,1))}.fill-amber-400{fill:#fbbf24}.fill-border{fill:hsl(var(--border))}.fill-card{fill:hsl(var(--card))}.fill-foreground{fill:hsl(var(--foreground))}.fill-foreground\/35{fill:hsl(var(--foreground)/.35)}.fill-foreground\/60{fill:hsl(var(--foreground)/.6)}.fill-muted-foreground{fill:hsl(var(--muted-foreground))}.fill-primary{fill:hsl(var(--primary))}.fill-primary\/20{fill:hsl(var(--primary)/.2)}.stroke-border{stroke:hsl(var(--border))}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.p-0{padding:0}.p-0\.5{padding:.125rem}.p-1{padding:.25rem}.p-10{padding:2.5rem}.p-12{padding:3rem}.p-2{padding:.5rem}.p-2\.5{padding:.625rem}.p-3{padding:.75rem}.p-3\.5{padding:.875rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-14{padding-top:3.5rem;padding-bottom:3.5rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-3\.5{padding-top:.875rem;padding-bottom:.875rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-1{padding-bottom:.25rem}.pb-2\.5{padding-bottom:.625rem}.pb-3{padding-bottom:.75rem}.pb-4{padding-bottom:1rem}.pb-5{padding-bottom:1.25rem}.pl-5{padding-left:1.25rem}.pl-9{padding-left:2.25rem}.pr-1{padding-right:.25rem}.pr-4{padding-right:1rem}.pr-8{padding-right:2rem}.pt-0{padding-top:0}.pt-1{padding-top:.25rem}.pt-2{padding-top:.5rem}.pt-2\.5{padding-top:.625rem}.pt-3{padding-top:.75rem}.pt-4{padding-top:1rem}.pt-5{padding-top:1.25rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[13px\]{font-size:13px}.text-\[22px\]{font-size:22px}.text-\[28px\]{font-size:28px}.text-\[9px\]{font-size:9px}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.capitalize{text-transform:capitalize}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-5{line-height:1.25rem}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.leading-tight{line-height:1.25}.tracking-tight{letter-spacing:-.025em}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.tracking-widest{letter-spacing:.1em}.text-amber-400{--tw-text-opacity:1;color:rgb(251 191 36/var(--tw-text-opacity,1))}.text-amber-500{--tw-text-opacity:1;color:rgb(245 158 11/var(--tw-text-opacity,1))}.text-amber-600{--tw-text-opacity:1;color:rgb(217 119 6/var(--tw-text-opacity,1))}.text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity,1))}.text-blue-500{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity,1))}.text-blue-600{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity,1))}.text-card-foreground{color:hsl(var(--card-foreground))}.text-cyan-500{--tw-text-opacity:1;color:rgb(6 182 212/var(--tw-text-opacity,1))}.text-cyan-600{--tw-text-opacity:1;color:rgb(8 145 178/var(--tw-text-opacity,1))}.text-destructive{color:hsl(var(--destructive))}.text-emerald-500{--tw-text-opacity:1;color:rgb(16 185 129/var(--tw-text-opacity,1))}.text-emerald-600{--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1))}.text-foreground{color:hsl(var(--foreground))}.text-indigo-500{--tw-text-opacity:1;color:rgb(99 102 241/var(--tw-text-opacity,1))}.text-info{--tw-text-opacity:1;color:rgb(96 165 250/var(--tw-text-opacity,1))}.text-lime-600{--tw-text-opacity:1;color:rgb(101 163 13/var(--tw-text-opacity,1))}.text-muted-foreground{color:hsl(var(--muted-foreground))}.text-muted-foreground\/60{color:hsl(var(--muted-foreground)/.6)}.text-muted-foreground\/70{color:hsl(var(--muted-foreground)/.7)}.text-muted-foreground\/80{color:hsl(var(--muted-foreground)/.8)}.text-primary{color:hsl(var(--primary))}.text-primary-foreground{color:hsl(var(--primary-foreground))}.text-purple-500{--tw-text-opacity:1;color:rgb(168 85 247/var(--tw-text-opacity,1))}.text-purple-600{--tw-text-opacity:1;color:rgb(147 51 234/var(--tw-text-opacity,1))}.text-red-600{--tw-text-opacity:1;color:rgb(220 38 38/var(--tw-text-opacity,1))}.text-rose-500{--tw-text-opacity:1;color:rgb(244 63 94/var(--tw-text-opacity,1))}.text-rose-600{--tw-text-opacity:1;color:rgb(225 29 72/var(--tw-text-opacity,1))}.text-secondary-foreground{color:hsl(var(--secondary-foreground))}.text-sky-500{--tw-text-opacity:1;color:rgb(14 165 233/var(--tw-text-opacity,1))}.text-slate-400{--tw-text-opacity:1;color:rgb(148 163 184/var(--tw-text-opacity,1))}.text-success{--tw-text-opacity:1;color:rgb(45 212 191/var(--tw-text-opacity,1))}.text-teal-500{--tw-text-opacity:1;color:rgb(20 184 166/var(--tw-text-opacity,1))}.text-violet-500{--tw-text-opacity:1;color:rgb(139 92 246/var(--tw-text-opacity,1))}.text-warning{--tw-text-opacity:1;color:rgb(251 191 36/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.text-zinc-200{--tw-text-opacity:1;color:rgb(228 228 231/var(--tw-text-opacity,1))}.underline{text-decoration-line:underline}.underline-offset-2{text-underline-offset:2px}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.opacity-50{opacity:.5}.opacity-70{opacity:.7}.opacity-80{opacity:.8}.shadow-\[0_0_20px_rgba\(168\2c 242\2c 26\2c 0\.25\)\]{--tw-shadow:0 0 20px rgba(168,242,26,0.25);--tw-shadow-colored:0 0 20px var(--tw-shadow-color)}.shadow-\[0_0_20px_rgba\(168\2c 242\2c 26\2c 0\.25\)\],.shadow-lg{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px rgb(0 0 0/0.1),0 4px 6px -4px rgb(0 0 0/0.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-md{--tw-shadow:0 4px 6px -1px rgb(0 0 0/0.1),0 2px 4px -2px rgb(0 0 0/0.1);--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.shadow-md,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 2px 0 rgb(0 0 0/0.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.shadow-xl{--tw-shadow:0 20px 25px -5px rgb(0 0 0/0.1),0 8px 10px -6px rgb(0 0 0/0.1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.outline{outline-style:solid}.ring{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring,.ring-2{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-2{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring-primary\/30{--tw-ring-color:hsl(var(--primary)/0.3)}.\!filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)!important}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-sm{--tw-backdrop-blur:blur(4px);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-150,.transition-transform{transition-duration:.15s}.duration-300{transition-duration:.3s}.duration-700{transition-duration:.7s}:root{color-scheme:light;--background:0 0% 100%;--foreground:240 10% 3.9%;--card:0 0% 100%;--card-foreground:240 10% 3.9%;--popover:0 0% 100%;--popover-foreground:240 10% 3.9%;--primary:84 81% 36%;--primary-foreground:0 0% 98%;--secondary:240 4.8% 95.9%;--secondary-foreground:240 5.9% 10%;--muted:240 4.8% 95.9%;--muted-foreground:240 3.8% 46.1%;--accent:240 4.8% 95.9%;--accent-foreground:240 5.9% 10%;--destructive:0 84.2% 60.2%;--destructive-foreground:0 0% 98%;--border:240 5.9% 90%;--input:240 5.9% 90%;--ring:80 89% 45%;--radius:0.5rem;--sidebar:240 4.8% 97%;--code:240 4.8% 95.9%}.dark{color-scheme:dark;--background:120 14% 6%;--foreground:100 17% 95%;--card:120 11% 9%;--card-foreground:100 17% 95%;--popover:120 11% 9%;--popover-foreground:100 17% 95%;--primary:80 89% 53%;--primary-foreground:90 70% 3%;--secondary:112 13% 12%;--secondary-foreground:100 17% 95%;--muted:112 13% 12%;--muted-foreground:107 8% 60%;--accent:112 13% 12%;--accent-foreground:100 17% 95%;--destructive:0 90% 71%;--destructive-foreground:0 0% 98%;--border:120 12% 18%;--input:120 12% 18%;--ring:80 89% 53%;--sidebar:120 11% 9%;--code:120 14% 5%}*{border-color:hsl(var(--border))}body{background-color:hsl(var(--background));color:hsl(var(--foreground));min-height:100vh;font-feature-settings:"rlig" 1,"calt" 1}button,input,select,textarea{font:inherit}:focus-visible{outline:2px solid hsl(var(--ring));outline-offset:2px}::-moz-selection{background:#A8F21A;color:#000000}::selection{background:#A8F21A;color:#000000}::-webkit-scrollbar{width:7px;height:7px}::-webkit-scrollbar-track{background:hsl(var(--background))}::-webkit-scrollbar-thumb{background:hsl(var(--border));border-radius:4px}::-webkit-scrollbar-thumb:hover{background:hsl(var(--muted-foreground))}.marker\:font-mono ::marker{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.marker\:font-mono::marker{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.selection\:bg-beast-lime ::-moz-selection{--tw-bg-opacity:1;background-color:rgb(168 242 26/var(--tw-bg-opacity,1))}.selection\:bg-beast-lime ::selection{--tw-bg-opacity:1;background-color:rgb(168 242 26/var(--tw-bg-opacity,1))}.selection\:text-black ::-moz-selection{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity,1))}.selection\:text-black ::selection{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity,1))}.selection\:bg-beast-lime::-moz-selection{--tw-bg-opacity:1;background-color:rgb(168 242 26/var(--tw-bg-opacity,1))}.selection\:bg-beast-lime::selection{--tw-bg-opacity:1;background-color:rgb(168 242 26/var(--tw-bg-opacity,1))}.selection\:text-black::-moz-selection{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity,1))}.selection\:text-black::selection{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity,1))}.placeholder\:text-muted-foreground::-moz-placeholder{color:hsl(var(--muted-foreground))}.placeholder\:text-muted-foreground::placeholder{color:hsl(var(--muted-foreground))}.last\:mb-0:last-child{margin-bottom:0}.last\:border-0:last-child{border-width:0}.hover\:border-primary\/40:hover{border-color:hsl(var(--primary)/.4)}.hover\:border-primary\/50:hover{border-color:hsl(var(--primary)/.5)}.hover\:bg-accent\/50:hover{background-color:hsl(var(--accent)/.5)}.hover\:bg-beast-lime-hover:hover{--tw-bg-opacity:1;background-color:rgb(151 222 16/var(--tw-bg-opacity,1))}.hover\:bg-destructive:hover{background-color:hsl(var(--destructive))}.hover\:bg-destructive\/10:hover{background-color:hsl(var(--destructive)/.1)}.hover\:bg-destructive\/25:hover{background-color:hsl(var(--destructive)/.25)}.hover\:bg-muted:hover{background-color:hsl(var(--muted))}.hover\:bg-muted\/40:hover{background-color:hsl(var(--muted)/.4)}.hover\:bg-muted\/50:hover{background-color:hsl(var(--muted)/.5)}.hover\:bg-muted\/60:hover{background-color:hsl(var(--muted)/.6)}.hover\:bg-primary\/10:hover{background-color:hsl(var(--primary)/.1)}.hover\:bg-primary\/20:hover{background-color:hsl(var(--primary)/.2)}.hover\:bg-secondary\/80:hover{background-color:hsl(var(--secondary)/.8)}.hover\:text-destructive:hover{color:hsl(var(--destructive))}.hover\:text-destructive-foreground:hover{color:hsl(var(--destructive-foreground))}.hover\:text-foreground:hover{color:hsl(var(--foreground))}.hover\:text-primary:hover{color:hsl(var(--primary))}.hover\:underline:hover{text-decoration-line:underline}.hover\:shadow-md:hover{--tw-shadow:0 4px 6px -1px rgb(0 0 0/0.1),0 2px 4px -2px rgb(0 0 0/0.1);--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-1:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-1:focus,.focus\:ring-2:focus{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-primary:focus{--tw-ring-color:hsl(var(--primary))}.focus\:ring-ring:focus{--tw-ring-color:hsl(var(--ring))}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px}.focus-visible\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus-visible\:ring-ring:focus-visible{--tw-ring-color:hsl(var(--ring))}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:scale-105{--tw-scale-x:1.05;--tw-scale-y:1.05;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:text-primary{color:hsl(var(--primary))}.data-\[state\=active\]\:border[data-state=active]{border-width:1px}.data-\[state\=active\]\:border-primary\/30[data-state=active]{border-color:hsl(var(--primary)/.3)}.data-\[state\=active\]\:bg-primary\/10[data-state=active]{background-color:hsl(var(--primary)/.1)}.data-\[state\=active\]\:font-medium[data-state=active]{font-weight:500}.data-\[state\=active\]\:text-primary[data-state=active]{color:hsl(var(--primary))}.dark\:block:is(.dark *){display:block}.dark\:hidden:is(.dark *){display:none}.dark\:bg-\[\#1e2b0a\]:is(.dark *){--tw-bg-opacity:1;background-color:rgb(30 43 10/var(--tw-bg-opacity,1))}.dark\:text-amber-400:is(.dark *){--tw-text-opacity:1;color:rgb(251 191 36/var(--tw-text-opacity,1))}.dark\:text-blue-400:is(.dark *){--tw-text-opacity:1;color:rgb(96 165 250/var(--tw-text-opacity,1))}.dark\:text-cyan-400:is(.dark *){--tw-text-opacity:1;color:rgb(34 211 238/var(--tw-text-opacity,1))}.dark\:text-emerald-400:is(.dark *){--tw-text-opacity:1;color:rgb(52 211 153/var(--tw-text-opacity,1))}.dark\:text-lime-400:is(.dark *){--tw-text-opacity:1;color:rgb(163 230 53/var(--tw-text-opacity,1))}.dark\:text-purple-400:is(.dark *){--tw-text-opacity:1;color:rgb(192 132 252/var(--tw-text-opacity,1))}.dark\:text-red-400:is(.dark *){--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity,1))}.dark\:text-rose-400:is(.dark *){--tw-text-opacity:1;color:rgb(251 113 133/var(--tw-text-opacity,1))}@media (min-width:640px){.sm\:col-span-1{grid-column:span 1/span 1}.sm\:inline{display:inline}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:grid-cols-\[1fr_100px_1fr_auto\]{grid-template-columns:1fr 100px 1fr auto}.sm\:grid-cols-\[1fr_140px_120px\]{grid-template-columns:1fr 140px 120px}.sm\:grid-cols-\[1fr_1fr_120px\]{grid-template-columns:1fr 1fr 120px}}@media (min-width:768px){.md\:block{display:block}.md\:flex{display:flex}.md\:hidden{display:none}.md\:h-dvh{height:100dvh}.md\:w-\[232px\]{width:232px}.md\:flex-1{flex:1 1 0%}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:grid-cols-\[200px_minmax\(0\2c 1fr\)\]{grid-template-columns:200px minmax(0,1fr)}.md\:flex-row{flex-direction:row}.md\:flex-col{flex-direction:column}.md\:self-start{align-self:flex-start}.md\:overflow-hidden{overflow:hidden}.md\:overflow-y-auto{overflow-y:auto}.md\:rounded-xl{border-radius:.75rem}.md\:border{border-width:1px}.md\:border-b-0{border-bottom-width:0}.md\:border-r{border-right-width:1px}.md\:border-border{border-color:hsl(var(--border))}.md\:bg-card{background-color:hsl(var(--card))}.md\:p-2{padding:.5rem}.md\:px-3{padding-left:.75rem;padding-right:.75rem}.md\:px-5{padding-left:1.25rem;padding-right:1.25rem}.md\:px-8{padding-left:2rem;padding-right:2rem}.md\:py-6{padding-top:1.5rem;padding-bottom:1.5rem}.md\:py-8{padding-top:2rem;padding-bottom:2rem}.md\:pb-2{padding-bottom:.5rem}.md\:pt-4{padding-top:1rem}.md\:pt-5{padding-top:1.25rem}}@media (min-width:1024px){.lg\:col-span-2{grid-column:span 2/span 2}.lg\:flex{display:flex}.lg\:hidden{display:none}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.lg\:grid-cols-\[220px_minmax\(0\2c 1fr\)\]{grid-template-columns:220px minmax(0,1fr)}.lg\:grid-cols-\[220px_minmax\(0\2c 1fr\)_420px\]{grid-template-columns:220px minmax(0,1fr) 420px}.lg\:grid-cols-\[220px_minmax\(320px\2c 1fr\)_minmax\(380px\2c 1\.4fr\)\]{grid-template-columns:220px minmax(320px,1fr) minmax(380px,1.4fr)}}@media (min-width:1280px){.xl\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.xl\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}} \ No newline at end of file diff --git a/internal/web/static/_next/static/BeAt4yy455X2udOGC4MNw/_buildManifest.js b/internal/web/static/_next/static/nEqHlpbv5UYE59pGP9WvK/_buildManifest.js similarity index 100% rename from internal/web/static/_next/static/BeAt4yy455X2udOGC4MNw/_buildManifest.js rename to internal/web/static/_next/static/nEqHlpbv5UYE59pGP9WvK/_buildManifest.js diff --git a/internal/web/static/_next/static/BeAt4yy455X2udOGC4MNw/_ssgManifest.js b/internal/web/static/_next/static/nEqHlpbv5UYE59pGP9WvK/_ssgManifest.js similarity index 100% rename from internal/web/static/_next/static/BeAt4yy455X2udOGC4MNw/_ssgManifest.js rename to internal/web/static/_next/static/nEqHlpbv5UYE59pGP9WvK/_ssgManifest.js diff --git a/internal/web/static/index.html b/internal/web/static/index.html index 809c275..e76a79b 100644 --- a/internal/web/static/index.html +++ b/internal/web/static/index.html @@ -1 +1 @@ -BeastDB Studio — Console

Table Editor

Browse, search and edit your records.

Scans values inside B+ Tree slotted pages

No records match filter

Select another filter or add records.

\ No newline at end of file +BeastDB Studio — Console

Table Editor

Browse, search and edit your records.

Scans values inside B+ Tree slotted pages

No records match filter

Select another filter or add records.

\ No newline at end of file diff --git a/internal/web/static/index.txt b/internal/web/static/index.txt index 0e93836..fa47999 100644 --- a/internal/web/static/index.txt +++ b/internal/web/static/index.txt @@ -2,15 +2,15 @@ 2:I[9766,[],""] 3:I[8924,[],""] 4:I[1959,[],"ClientPageRoot"] -5:I[3644,["695","static/chunks/695-b3536a607b599f97.js","974","static/chunks/app/page-af03adbf71ed5700.js"],"default"] +5:I[3199,["685","static/chunks/685-eeaee2683c96969f.js","974","static/chunks/app/page-d99a717af3e73bf2.js"],"default"] 8:I[4431,[],"OutletBoundary"] a:I[5278,[],"AsyncMetadataOutlet"] c:I[4431,[],"ViewportBoundary"] e:I[4431,[],"MetadataBoundary"] f:"$Sreact.suspense" 11:I[7150,[],""] -:HL["/_next/static/css/27e172611fa9eaf5.css","style"] -0:{"P":null,"b":"BeAt4yy455X2udOGC4MNw","p":"","c":["",""],"i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/27e172611fa9eaf5.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","className":"dark scroll-smooth","suppressHydrationWarning":true,"children":[["$","head",null,{"children":["$","script",null,{"dangerouslySetInnerHTML":{"__html":"(function(){try{var t=localStorage.getItem('beastdb-theme');document.documentElement.classList.toggle('dark',t!=='light')}catch(e){}})()"}}]}],["$","body",null,{"className":"bg-background text-foreground antialiased min-h-screen selection:bg-beast-lime selection:text-black","children":["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]]}]]}],{"children":["__PAGE__",["$","$1","c",{"children":[["$","$L4",null,{"Component":"$5","searchParams":{},"params":{},"promises":["$@6","$@7"]}],null,["$","$L8",null,{"children":["$L9",["$","$La",null,{"promise":"$@b"}]]}]]}],{},null,false]},null,false],["$","$1","h",{"children":[null,[["$","$Lc",null,{"children":"$Ld"}],null],["$","$Le",null,{"children":["$","div",null,{"hidden":true,"children":["$","$f",null,{"fallback":null,"children":"$L10"}]}]}]]}],false]],"m":"$undefined","G":["$11",[]],"s":false,"S":true} +:HL["/_next/static/css/f98f38bad263934a.css","style"] +0:{"P":null,"b":"nEqHlpbv5UYE59pGP9WvK","p":"","c":["",""],"i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/f98f38bad263934a.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","className":"dark scroll-smooth","suppressHydrationWarning":true,"children":[["$","head",null,{"children":["$","script",null,{"dangerouslySetInnerHTML":{"__html":"(function(){try{var t=localStorage.getItem('beastdb-theme');document.documentElement.classList.toggle('dark',t!=='light')}catch(e){}})()"}}]}],["$","body",null,{"className":"bg-background text-foreground antialiased min-h-screen selection:bg-beast-lime selection:text-black","children":["$","$L2",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L3",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]]}]]}],{"children":["__PAGE__",["$","$1","c",{"children":[["$","$L4",null,{"Component":"$5","searchParams":{},"params":{},"promises":["$@6","$@7"]}],null,["$","$L8",null,{"children":["$L9",["$","$La",null,{"promise":"$@b"}]]}]]}],{},null,false]},null,false],["$","$1","h",{"children":[null,[["$","$Lc",null,{"children":"$Ld"}],null],["$","$Le",null,{"children":["$","div",null,{"hidden":true,"children":["$","$f",null,{"fallback":null,"children":"$L10"}]}]}]]}],false]],"m":"$undefined","G":["$11",[]],"s":false,"S":true} 6:{} 7:"$0:f:0:1:2:children:1:props:children:0:props:params" d:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] diff --git a/studio/src/app/globals.css b/studio/src/app/globals.css index 5ede487..d7f57b2 100644 --- a/studio/src/app/globals.css +++ b/studio/src/app/globals.css @@ -49,7 +49,7 @@ --border: 120 12% 18%; --input: 120 12% 18%; --ring: 80 89% 53%; - --sidebar: 120 11% 8%; + --sidebar: 120 11% 9%; --code: 120 14% 5%; } * { diff --git a/studio/src/components/OverviewView.tsx b/studio/src/components/OverviewView.tsx index d5b763d..254c3c3 100644 --- a/studio/src/components/OverviewView.tsx +++ b/studio/src/components/OverviewView.tsx @@ -7,6 +7,9 @@ import { Badge } from "./ui/badge"; import { Button } from "./ui/button"; import { BufferPoolMap, WalHead } from "./EnginePanel"; import { PartitionChart } from "./PartitionChart"; +import { StatCard, StatGrid } from "./StatCard"; +import { Sparkline } from "./metrics/Sparkline"; +import { useServerHistory } from "../hooks/useServerHistory"; import { getRegisteredPartitions } from "../utils/key-decoder"; interface Props { @@ -31,6 +34,10 @@ export function OverviewView({ stats, statsError, recordsError, user, loaded, ha : [] ).filter((d) => d.count > 0); + const isAdmin = user?.role === "admin"; + const { data: history } = useServerHistory(3600, isAdmin); + const rates = history?.points.map((p) => p.reqPerSec) ?? []; + const totalRecords = chartData.reduce((n, d) => n + d.count, 0); const isHealthy = Boolean(stats); const dirtyPages = stats?.storage?.dirtyPages ?? 0; const poolSize = stats?.storage?.poolSize ?? 64; @@ -66,13 +73,32 @@ export function OverviewView({ stats, statsError, recordsError, user, loaded, ha )} + + + + + + + + {isAdmin && ( + +
+ Requests per second · last hour + + {rates.length ? `${(rates[rates.length - 1] ?? 0).toFixed(1)} now` : "—"} + +
+ +

Sampled by the server and kept in memory, so it starts empty after a restart.

+
+ )} +
-
- +
diff --git a/studio/src/components/StatCard.tsx b/studio/src/components/StatCard.tsx new file mode 100644 index 0000000..74144b1 --- /dev/null +++ b/studio/src/components/StatCard.tsx @@ -0,0 +1,42 @@ +import type { ReactNode } from "react"; +import { Card } from "./ui/card"; +import { cn } from "@/lib/utils"; + +const HINT_TONES = { + muted: "text-muted-foreground", + primary: "text-primary", + success: "text-success", + warning: "text-warning", + info: "text-info", +} as const; + +export type StatTone = keyof typeof HINT_TONES; + +interface StatCardProps { + label: string; + value: string; + hint?: ReactNode; + tone?: StatTone; +} + +/** Figma "Stat card": muted caption, large number, optional coloured hint line. */ +export function StatCard({ label, value, hint, tone = "muted" }: StatCardProps) { + return ( + +

{label}

+

+ {value} +

+ {hint &&

{hint}

} +
+ ); +} + +/** Responsive row of stat cards (2 up on small screens, `cols` up on wide ones). */ +export function StatGrid({ cols = 4, children }: { cols?: 3 | 4; children: ReactNode }) { + return ( +
+ {children} +
+ ); +} diff --git a/studio/src/components/StudioSidebar.tsx b/studio/src/components/StudioSidebar.tsx index a77df7a..00eec8b 100644 --- a/studio/src/components/StudioSidebar.tsx +++ b/studio/src/components/StudioSidebar.tsx @@ -48,13 +48,13 @@ const PAGES: { id: StudioView; label: string; icon: LucideIcon }[] = [ ]; const itemBase = - "flex w-full shrink-0 items-center gap-3 rounded-lg px-3 py-2 text-sm transition-colors"; + "flex w-full shrink-0 items-center gap-2.5 rounded-lg px-2.5 py-2 text-sm transition-colors"; export function StudioSidebar({ view, setView, user, busy, onRefresh }: Props) { const theme = useTheme(); return (