
Security News
Opengrep Adds Apex Support and New Rule Controls in Latest Updates
The latest Opengrep releases add Apex scanning, precision rule tuning, and performance gains for open source static code analysis.
a library to turn any pydantic BaseModel object into a fasthtml/monsterui input form
Generate HTML forms from Pydantic models for your FastHTML applications.
fh-pydantic-form
simplifies creating web forms for FastHTML by automatically generating the necessary HTML input elements based on your Pydantic model definitions. It integrates seamlessly with and leverages MonsterUI components for styling. This makes it ideal for annotation workflows for structured outputs: when the schema updates, so does your annotation app.
You can install fh-pydantic-form
using either pip
or uv
.
Using pip:
pip install fh-pydantic-form
Using uv:
uv add fh-pydantic-form
This will also install necessary dependencies like pydantic
, python-fasthtml
, and monsterui
.
# examples/simple_example.py
import fasthtml.common as fh
import monsterui.all as mui
from pydantic import BaseModel, ValidationError
# 1. Import the form renderer
from fh_pydantic_form import PydanticForm
app, rt = fh.fast_app(
hdrs=[
mui.Theme.blue.headers(),
# Add list_manipulation_js() if using list fields
# from fh_pydantic_form import list_manipulation_js
# list_manipulation_js(),
],
pico=False, # Using MonsterUI, not PicoCSS
live=True, # Enable live reload for development
)
# 2. Define your Pydantic model
class SimpleModel(BaseModel):
"""Model representing a simple form"""
name: str = "Default Name"
age: int
is_active: bool = True
# 3. Create a form renderer instance
# - 'my_form': Unique name for the form (used for prefixes and routes)
# - SimpleModel: The Pydantic model class
form_renderer = PydanticForm("my_form", SimpleModel)
# (Optional) Register list manipulation routes if your model has List fields
# form_renderer.register_routes(app)
# 4. Define routes
@rt("/")
def get():
"""Display the form"""
return fh.Div(
mui.Container(
mui.Card(
mui.CardHeader("Simple Pydantic Form"),
mui.CardBody(
# Use MonsterUI Form component for structure
mui.Form(
# Render the inputs using the renderer
form_renderer.render_inputs(),
# Add standard form buttons
fh.Div(
mui.Button("Submit", type="submit", cls=mui.ButtonT.primary),
form_renderer.refresh_button("đ"),
form_renderer.reset_button("âŠī¸"),
cls="mt-4 flex items-center gap-2",
),
# HTMX attributes for form submission
hx_post="/submit_form",
hx_target="#result", # Target div for response
hx_swap="innerHTML",
# Set a unique ID for the form itself for refresh/reset inclusion
id=f"{form_renderer.name}-form",
)
),
),
# Div to display validation results
fh.Div(id="result"),
),
)
@rt("/submit_form")
async def post_submit_form(req):
"""Handle form submission and validation"""
try:
# 5. Validate the request data against the model
validated_data: SimpleModel = await form_renderer.model_validate_request(req)
# Success: Display the validated data
return mui.Card(
mui.CardHeader(fh.H3("Validation Successful")),
mui.CardBody(
fh.Pre(
validated_data.model_dump_json(indent=2),
)
),
cls="mt-4",
)
except ValidationError as e:
# Validation Error: Display the errors
return mui.Card(
mui.CardHeader(fh.H3("Validation Error", cls="text-red-500")),
mui.CardBody(
fh.Pre(
e.json(indent=2),
)
),
cls="mt-4",
)
if __name__ == "__main__":
fh.serve()
str
, int
, float
, bool
, date
, time
, Optional
, Literal
, nested BaseModel
s, and List
s out-of-the-box.text
, number
, date
, time
, checkbox
, select
).Field(description=...)
from Pydantic to create tooltips (uk-tooltip
via UIkit).required
attribute based on field definitions (considering Optional
and defaults).disabled=True
or disable specific fields with disabled_fields
register_routes
) for adding and deleting list items.list_manipulation_js()
) for client-side reordering (moving items up/down).form_renderer.refresh_button()
, form_renderer.reset_button()
).BaseFieldRenderer
subclasses for specific Pydantic types or complex field logic using FieldRendererRegistry
or by passing custom_renderers
during PydanticForm
initialization.form_renderer.parse
and form_renderer.model_validate_request
) to correctly parse submitted form data (handling prefixes, list indices, nested structures, boolean checkboxes, etc.) back into a dictionary suitable for Pydantic validation.fh-pydantic-form
ships with two spacing presets to fit different UI requirements:
Theme | Purpose | Usage |
---|---|---|
normal (default) | Comfortable margins & borders â great for desktop forms | PydanticForm(..., spacing="normal") |
compact | Ultra-dense UIs, mobile layouts, or forms with many fields | PydanticForm(..., spacing="compact") |
# Example: side-by-side normal vs compact forms
form_normal = PydanticForm("normal_form", MyModel, spacing="normal")
form_compact = PydanticForm("compact_form", MyModel, spacing="compact")
Important: The compact CSS is now scoped with .fhpf-compact
classes and only affects form inputs, not layout containers. This prevents conflicts with your application's layout system.
When your Pydantic models contain List[str]
, List[int]
, or List[BaseModel]
fields, fh-pydantic-form
provides rich list manipulation capabilities:
from fh_pydantic_form import PydanticForm, list_manipulation_js
from typing import List
app, rt = fh.fast_app(
hdrs=[
mui.Theme.blue.headers(),
list_manipulation_js(), # Required for list manipulation
],
pico=False,
live=True,
)
class ListModel(BaseModel):
name: str = ""
tags: List[str] = Field(["tag1", "tag2"])
addresses: List[Address] = Field(default_factory=list)
form_renderer = PydanticForm("list_model", ListModel)
form_renderer.register_routes(app) # Register HTMX endpoints
The list manipulation uses HTMX for seamless updates without page reloads, and includes JavaScript for client-side reordering.
Nested Pydantic models are automatically rendered in collapsible accordion components:
class Address(BaseModel):
street: str = "123 Main St"
city: str = "Anytown"
is_billing: bool = False
class User(BaseModel):
name: str
address: Address # Rendered as collapsible accordion
backup_addresses: List[Address] # List of accordions
Key behaviors:
disabled
and spacing
settings from the parent formuser_address_street
)fh-pydantic-form
provides comprehensive support for choice-based fields through Literal
, Enum
, and IntEnum
types, all automatically rendered as dropdown selects:
from typing import Literal, Optional
class OrderModel(BaseModel):
# Required Literal field - only defined choices available
shipping_method: Literal["STANDARD", "EXPRESS", "OVERNIGHT"] = "STANDARD"
# Optional Literal field - includes "-- None --" option
category: Optional[Literal["ELECTRONICS", "CLOTHING", "BOOKS", "OTHER"]] = None
from enum import Enum, IntEnum
class OrderStatus(Enum):
"""Order status enum with string values."""
PENDING = "pending"
CONFIRMED = "confirmed"
SHIPPED = "shipped"
DELIVERED = "delivered"
CANCELLED = "cancelled"
class Priority(IntEnum):
"""Priority levels using IntEnum for numeric ordering."""
LOW = 1
MEDIUM = 2
HIGH = 3
URGENT = 4
class OrderModel(BaseModel):
# Required Enum field with default
status: OrderStatus = OrderStatus.PENDING
# Optional Enum field without default
payment_method: Optional[PaymentMethod] = None
# Required IntEnum field with default
priority: Priority = Priority.MEDIUM
# Optional IntEnum field without default
urgency_level: Optional[Priority] = Field(
None, description="Override priority for urgent orders"
)
# Enum field without default (required)
fulfillment_status: OrderStatus = Field(
..., description="Current fulfillment status"
)
Field Type | Required | Optional | Notes |
---|---|---|---|
Literal | Shows only defined choices | Includes "-- None --" option | String values displayed as-is |
Enum | Shows enum member values | Includes "-- None --" option | Displays enum.value in dropdown |
IntEnum | Shows integer values | Includes "-- None --" option | Maintains numeric ordering |
Key features:
fh-pydantic-form
intelligently parses initial values from dictionaries, properly converting strings and integers to their corresponding enum types:
# Example initial values from a dictionary
initial_values_dict = {
"shipping_method": "EXPRESS", # Literal value as string
"category": "ELECTRONICS", # Optional Literal value
"status": "shipped", # Enum value (parsed to OrderStatus.SHIPPED)
"payment_method": "paypal", # Optional Enum (parsed to PaymentMethod.PAYPAL)
"priority": 3, # IntEnum as integer (parsed to Priority.HIGH)
"urgency_level": 4, # Optional IntEnum as integer (parsed to Priority.URGENT)
"fulfillment_status": "confirmed" # Required Enum (parsed to OrderStatus.CONFIRMED)
}
# Create form with initial values
form_renderer = PydanticForm("order_form", OrderModel, initial_values=initial_values_dict)
The form automatically handles conversion between different value formats:
Input Type | Target Type | Example | Result |
---|---|---|---|
String | Enum | "shipped" | OrderStatus.SHIPPED |
String | Optional[Enum] | "paypal" | PaymentMethod.PAYPAL |
Integer | IntEnum | 3 | Priority.HIGH |
Integer | Optional[IntEnum] | 4 | Priority.URGENT |
String | Literal | "EXPRESS" | "EXPRESS" (unchanged) |
Benefits:
@rt("/")
def get():
return mui.Form(
form_renderer.render_inputs(), # Pre-populated with parsed enum values
fh.Div(
mui.Button("Submit", type="submit", cls=mui.ButtonT.primary),
form_renderer.refresh_button("đ"),
form_renderer.reset_button("âŠī¸"), # Resets to initial parsed values
cls="mt-4 flex items-center gap-2",
),
hx_post="/submit_order",
hx_target="#result",
id=f"{form_renderer.name}-form",
)
@rt("/submit_order")
async def post_submit_order(req):
try:
# Validates and converts form data back to proper enum types
validated_order: OrderModel = await form_renderer.model_validate_request(req)
# Access enum properties
print(f"Status: {validated_order.status.value} ({validated_order.status.name})")
print(f"Priority: {validated_order.priority.value} ({validated_order.priority.name})")
return success_response(validated_order)
except ValidationError as e:
return error_response(e)
This makes it easy to work with enum-based forms when loading data from databases, APIs, or configuration files.
You can disable the entire form or specific fields:
# Disable all fields
form_renderer = PydanticForm("my_form", FormModel, disabled=True)
# Disable specific fields only
form_renderer = PydanticForm(
"my_form",
FormModel,
disabled_fields=["field1", "field3"]
)
Exclude specific fields from being rendered in the form:
form_renderer = PydanticForm(
"my_form",
FormModel,
exclude_fields=["internal_field", "computed_field"]
)
Important: When fields are excluded from the UI, fh-pydantic-form
automatically injects their default values during form parsing and validation. This ensures:
This automatic default injection means you can safely exclude fields that shouldn't be user-editable while maintaining data integrity.
Forms support dynamic refresh and reset functionality:
mui.Form(
form_renderer.render_inputs(),
fh.Div(
mui.Button("Submit", type="submit", cls=mui.ButtonT.primary),
form_renderer.refresh_button("đ Refresh"), # Update display
form_renderer.reset_button("âŠī¸ Reset"), # Restore initial values
cls="mt-4 flex items-center gap-2",
),
# ... rest of form setup
)
Customize the appearance of field labels with the label_colors
parameter:
form_renderer = PydanticForm(
"my_form",
MyModel,
label_colors={
"name": "text-blue-600", # Tailwind CSS class
"score": "#E12D39", # Hex color value
"status": "text-green-500", # Another Tailwind class
},
)
Supported formats:
"text-blue-600"
, "text-red-500"
, etc."#FF0000"
, "#0066CC"
, etc."red"
, "blue"
, "darkgreen"
, etc.This can be useful for e.g. highlighting the values of different fields in a pdf with different highlighting colors matching the form input label color.
fh-pydantic-form
provides a powerful metrics system for visual highlighting of form fields based on extraction quality scores and confidence assessments. This is particularly useful for evaluating LLM structured output extraction, comparing generated data against ground truth, and building quality assessment interfaces.
from fh_pydantic_form import PydanticForm
# Define metrics for your form fields
metrics_dict = {
"title": {
"metric": 0.95,
"comment": "Excellent title quality - clear and engaging"
},
"rating": {
"metric": 0.3,
"comment": "Low rating needs attention"
},
"status": {
"metric": 0.0,
"comment": "Critical status issue - requires immediate review"
}
}
# Create form with metrics
form_renderer = PydanticForm(
"my_form",
MyModel,
metrics_dict=metrics_dict
)
Each field can have the following metrics properties:
Property | Type | Description |
---|---|---|
metric | float or str | Numeric score (0.0-1.0) or string assessment |
color | str | Custom color (overrides automatic color-coding) |
comment | str | Tooltip text shown on hover |
Numeric metrics are automatically color-coded:
metrics_dict = {
"field1": {"metric": 1.0, "comment": "Perfect!"}, # Bright green
"field2": {"metric": 0.8, "comment": "Very good"}, # Medium green
"field3": {"metric": 0.3, "comment": "Needs work"}, # Dark red
"field4": {"metric": 0.0, "comment": "Critical"}, # Bright red
}
Override automatic colors with custom values:
metrics_dict = {
"status": {
"metric": 0.0,
"color": "purple", # Custom color overrides red
"comment": "Status requires special attention"
},
"priority": {
"metric": 1.0,
"color": "#FF6B35", # Custom hex color
"comment": "High priority with custom highlight"
}
}
Use string values for qualitative assessments:
metrics_dict = {
"validation_status": {
"metric": "NEEDS_REVIEW",
"color": "#F59E0B", # Amber color
"comment": "Requires human review"
},
"data_quality": {
"metric": "EXCELLENT",
"color": "#10B981", # Green color
"comment": "Data quality exceeds standards"
}
}
Support for nested objects and list items:
metrics_dict = {
# Nested object fields
"author.name": {
"metric": 0.95,
"comment": "Author name perfectly formatted"
},
"author.email": {
"metric": 0.9,
"comment": "Email format excellent"
},
# List item metrics
"tags[0]": {
"metric": 1.0,
"comment": "First tag is perfect"
},
"tags[1]": {
"metric": 0.8,
"comment": "Second tag very good"
},
# Complex nested paths
"author.addresses[0].street": {
"metric": 1.0,
"comment": "Street address perfectly formatted"
},
"author.addresses[1].city": {
"metric": 0.1,
"color": "teal",
"comment": "City has verification problems"
}
}
LLM Structured Output Evaluation:
# Evaluate LLM extraction quality against ground truth
extraction_metrics = {
"product.name": {
"metric": 0.9,
"comment": "Name extracted with minor formatting issue: missing space"
},
"product.category": {
"metric": 0.0,
"comment": "Critical error: LLM misclassified Electronics instead of Sports"
},
"key_features": {
"metric": 0.6,
"comment": "LLM missed 2 of 5 key features from source text"
},
"extraction_confidence": {
"metric": 1.0,
"comment": "LLM confidence score accurately reflects actual performance"
}
}
Document Processing Quality:
# Highlight extraction quality from documents
doc_extraction_metrics = {
"invoice_number": {
"metric": 1.0,
"comment": "Invoice number perfectly extracted from PDF"
},
"line_items": {
"metric": 0.75,
"comment": "3/4 line items extracted correctly"
},
"total_amount": {
"metric": 0.0,
"comment": "Amount extraction failed - currency symbol confusion"
}
}
See examples/metrics_example.py
for a comprehensive demonstration of all metrics features.
The ComparisonForm
component provides side-by-side comparison of two related forms, perfect for evaluating LLM structured output against ground truth, annotation correction workflows, and comparing extraction results.
from fh_pydantic_form import PydanticForm, ComparisonForm
# Create two forms to compare
left_form = PydanticForm(
"ground_truth",
ProductModel,
initial_values=annotated_ground_truth,
disabled=False # Editable for annotation correction
)
right_form = PydanticForm(
"llm_output",
ProductModel,
initial_values=llm_extracted_data,
disabled=True, # Read-only LLM output
metrics_dict=extraction_quality_metrics
)
# Create comparison form
comparison_form = ComparisonForm(
name="extraction_evaluation",
left_form=left_form,
right_form=right_form,
left_label="đ Ground Truth (Editable)",
right_label="đ¤ LLM Output (with Quality Scores)"
)
Include the comparison form JavaScript in your app headers:
from fh_pydantic_form import comparison_form_js
app, rt = fh.fast_app(
hdrs=[
mui.Theme.blue.headers(),
comparison_form_js(), # Required for comparison forms
],
pico=False,
live=True,
)
@rt("/")
def get():
return fh.Div(
mui.Container(
mui.Card(
mui.CardHeader(
fh.H1("LLM Extraction Evaluation")
),
mui.CardBody(
# Render the comparison form
comparison_form.form_wrapper(
fh.Div(
comparison_form.render_inputs(),
# Action buttons
fh.Div(
mui.Button(
"Update Ground Truth",
type="submit",
hx_post="/update_ground_truth",
hx_target="#result"
),
comparison_form.left_reset_button("Reset Left"),
comparison_form.left_refresh_button("Refresh Left"),
cls="mt-4 flex gap-2"
),
fh.Div(id="result", cls="mt-4")
)
)
)
)
)
)
@rt("/update_ground_truth")
async def post_update_ground_truth(req):
# Validate left form (ground truth side)
validated = await comparison_form.left_form.model_validate_request(req)
# Process the ground truth update
return success_response(validated)
# Register routes for both forms
comparison_form.register_routes(app)
LLM Output Evaluation:
# Left: Editable ground truth
# Right: Read-only LLM output with extraction quality metrics
truth_form = PydanticForm(..., disabled=False, metrics_dict={})
llm_form = PydanticForm(..., disabled=True, metrics_dict=extraction_metrics)
Document Extraction Comparison:
# Left: Manual annotation
# Right: Automated LLM extraction
manual_form = PydanticForm(..., initial_values=manual_annotation)
auto_form = PydanticForm(..., initial_values=llm_extraction, metrics_dict=quality_scores)
Annotation Correction Workflow:
# Left: Correctable ground truth
# Right: LLM output with confidence scores
ground_truth_form = PydanticForm(..., disabled=False)
llm_output_form = PydanticForm(..., disabled=True, metrics_dict=confidence_scores)
See examples/comparison_example.py
for a complete LLM extraction evaluation interface demonstration.
You can set initial form values of the form by passing a model instance or dictionary:
initial_data = MyModel(name="John", tags=["happy", "joy"])
form_renderer = PydanticForm("my_form", MyModel, initial_values=initial_data)
initial_data_dict = {"name": "John"}
form_renderer = PydanticForm("my_form", MyModel, initial_values=initial_values_dict)
The dictionary does not have to be complete, and we try to handle schema drift gracefully. If you exclude fields from the form, we fill those fields with the initial_values or the default values.
The with_initial_values()
method allows you to create a new form instance with the same configuration but different initial values:
# Create a base form configuration
base_form = PydanticForm(
"product_form",
ProductModel,
disabled_fields=["id"],
label_colors={"name": "text-blue-600", "price": "text-green-600"},
spacing="compact"
)
# Create forms with different initial values using the same configuration
form_for_product_a = base_form.with_initial_values({"name": "Product A", "price": 29.99})
form_for_product_b = base_form.with_initial_values({"name": "Product B", "price": 45.50})
# Or with model instances
existing_product = ProductModel(name="Existing Product", price=19.99)
form_for_existing = base_form.with_initial_values(existing_product)
This is particularly useful for:
fh-pydantic-form
gracefully handles model evolution and schema changes:
Initial values can come from older or newer versions of your model â unknown fields are ignored gracefully and missing fields use defaults.
# Your model evolves over time
class UserModel(BaseModel):
name: str
email: str # Added in v2
phone: Optional[str] # Added in v3
# Old data still works
old_data = {"name": "John"} # Missing newer fields
form = PydanticForm("user", UserModel, initial_values=old_data)
# Newer data works too
new_data = {"name": "Jane", "email": "jane@example.com", "phone": "555-1234", "removed_field": "ignored"}
form = PydanticForm("user", UserModel, initial_values=new_data)
Benefits:
The library is extensible through custom field renderers for specialized input types:
from fh_pydantic_form.field_renderers import BaseFieldRenderer
from fh_pydantic_form import FieldRendererRegistry
class CustomDetail(BaseModel):
value: str = "Default value"
confidence: Literal["HIGH", "MEDIUM", "LOW"] = "MEDIUM"
def __str__(self) -> str:
return f"{self.value} ({self.confidence})"
class CustomDetailFieldRenderer(BaseFieldRenderer):
"""Display value input and dropdown side by side"""
def render_input(self):
value_input = fh.Div(
mui.Input(
value=self.value.get("value", ""),
id=f"{self.field_name}_value",
name=f"{self.field_name}_value",
placeholder=f"Enter {self.original_field_name.replace('_', ' ')} value",
cls="uk-input w-full",
),
cls="flex-grow",
)
confidence_options = [
fh.Option(
opt, value=opt, selected=(opt == self.value.get("confidence", "MEDIUM"))
)
for opt in ["HIGH", "MEDIUM", "LOW"]
]
confidence_select = mui.Select(
*confidence_options,
id=f"{self.field_name}_confidence",
name=f"{self.field_name}_confidence",
cls_wrapper="w-[110px] min-w-[110px] flex-shrink-0",
)
return fh.Div(
value_input,
confidence_select,
cls="flex items-start gap-2 w-full",
)
# Register the custom renderer (multiple ways)
FieldRendererRegistry.register_type_renderer(CustomDetail, CustomDetailFieldRenderer)
# Or pass directly to PydanticForm
form_renderer = PydanticForm(
"my_form",
MyModel,
custom_renderers=[(CustomDetail, CustomDetailFieldRenderer)],
)
register_type_renderer(CustomDetail, CustomDetailFieldRenderer)
register_type_name_renderer("CustomDetail", CustomDetailFieldRenderer)
register_type_renderer_with_predicate(lambda field: isinstance(field.annotation, CustomDetail), CustomDetailFieldRenderer)
Parameter | Type | Default | Description |
---|---|---|---|
form_name | str | Required | Unique identifier for the form (used for HTMX routes and prefixes) |
model_class | Type[BaseModel] | Required | The Pydantic model class to render |
initial_values | Optional[Union[BaseModel, Dict]] | None | Initial form values as model instance or dictionary |
custom_renderers | Optional[List[Tuple[Type, Type[BaseFieldRenderer]]]] | None | List of (type, renderer_class) pairs for custom rendering |
disabled | bool | False | Whether to disable all form inputs |
disabled_fields | Optional[List[str]] | None | List of specific field names to disable |
label_colors | Optional[Dict[str, str]] | None | Mapping of field names to CSS colors or Tailwind classes |
exclude_fields | Optional[List[str]] | None | List of field names to exclude from rendering (auto-injected on submission) |
spacing | SpacingValue | "normal" | Spacing theme: "normal" , "compact" , or SpacingTheme enum |
metrics_dict | Optional[Dict[str, Dict]] | None | Field metrics for highlighting and tooltips |
Parameter | Type | Default | Description |
---|---|---|---|
name | str | Required | Unique identifier for the comparison form |
left_form | PydanticForm | Required | Form to display on the left side |
right_form | PydanticForm | Required | Form to display on the right side |
left_label | str | "Left" | Label for the left form |
right_label | str | "Right" | Label for the right form |
Method | Purpose |
---|---|
render_inputs() | Generate the HTML form inputs (without <form> wrapper) |
with_initial_values(initial_values) | Create a new form instance with same configuration but different initial values |
refresh_button(text=None, **kwargs) | Create a refresh button component |
reset_button(text=None, **kwargs) | Create a reset button component |
register_routes(app) | Register HTMX endpoints for list manipulation |
parse(form_dict) | Parse raw form data into model-compatible dictionary |
model_validate_request(req) | Extract, parse, and validate form data from request |
Method | Purpose |
---|---|
render_inputs() | Generate side-by-side form inputs |
form_wrapper(content) | Wrap content with comparison form structure |
left_reset_button(text=None, **kwargs) | Reset button for left form |
right_reset_button(text=None, **kwargs) | Reset button for right form |
left_refresh_button(text=None, **kwargs) | Refresh button for left form |
right_refresh_button(text=None, **kwargs) | Refresh button for right form |
register_routes(app) | Register HTMX endpoints for both forms |
Function | Purpose |
---|---|
list_manipulation_js() | JavaScript for list reordering and toggle functionality |
comparison_form_js() | JavaScript for comparison form accordion synchronization |
default_dict_for_model(model_class) | Generate default values for all fields in a model |
default_for_annotation(annotation) | Get sensible default for a type annotation |
Contributions are welcome! Please feel free to open an issue or submit a pull request.
FAQs
a library to turn any pydantic BaseModel object into a fasthtml/monsterui input form
We found that fh-pydantic-form demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
The latest Opengrep releases add Apex scanning, precision rule tuning, and performance gains for open source static code analysis.
Security News
npm now supports Trusted Publishing with OIDC, enabling secure package publishing directly from CI/CD workflows without relying on long-lived tokens.
Research
/Security News
A RubyGems malware campaign used 60 malicious packages posing as automation tools to steal credentials from social media and marketing tool users.