Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/hooks/rustapi-guardrails.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"hooks": {
"PreToolUse": [
{
"type": "command",
"windows": "powershell -ExecutionPolicy Bypass -File .github/scripts/copilot_guardrails.ps1",
"linux": "pwsh -File .github/scripts/copilot_guardrails.ps1",
"osx": "pwsh -File .github/scripts/copilot_guardrails.ps1",
"cwd": ".",
"timeout": 10
}
]
}
}
29 changes: 29 additions & 0 deletions .github/release-drafter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,32 @@ version-resolver:
template: |
## What's Changed
$CHANGES

autolabeler:
- label: 'feat'
branch:
- '/^feat\/.+/'
title:
- '/^feat(\(.+\))?:/i'
- label: 'fix'
branch:
- '/^fix\/.+/'
title:
- '/^fix(\(.+\))?:/i'
- label: 'docs'
branch:
- '/^docs?\/.+/'
title:
- '/^docs?(\(.+\))?:/i'
- label: 'chore'
branch:
- '/^chore\/.+/'
- '/^ci\/.+/'
- '/^refactor\/.+/'
title:
- '/^(chore|ci|refactor)(\(.+\))?:/i'
- label: 'perf'
branch:
- '/^perf\/.+/'
title:
- '/^perf(\(.+\))?:/i'
134 changes: 134 additions & 0 deletions .github/scripts/copilot_guardrails.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
[CmdletBinding()]
param(
[string]$RawInput
)

$ErrorActionPreference = 'Stop'

function Get-PropertyValue {
param(
[Parameter(Mandatory = $true)]
[object]$Object,
[Parameter(Mandatory = $true)]
[string[]]$Names
)

foreach ($name in $Names) {
if ($null -ne $Object -and $Object.PSObject.Properties[$name]) {
return $Object.$name
}
}

return $null
}

function Add-Reminder {
param(
[System.Collections.Generic.List[string]]$List,
[string]$Message
)

if (-not [string]::IsNullOrWhiteSpace($Message) -and -not $List.Contains($Message)) {
[void]$List.Add($Message)
}
}

$raw = if ($PSBoundParameters.ContainsKey('RawInput')) {
$RawInput
} else {
[Console]::In.ReadToEnd()
}

if ([string]::IsNullOrWhiteSpace($raw)) {
return
}

try {
$payload = $raw | ConvertFrom-Json
} catch {
return
}

$eventName = Get-PropertyValue -Object $payload -Names @('hookEventName', 'eventName')
if (-not $eventName) {
$hookSpecific = Get-PropertyValue -Object $payload -Names @('hookSpecificInput', 'hookSpecificData')
if ($hookSpecific) {
$eventName = Get-PropertyValue -Object $hookSpecific -Names @('hookEventName', 'eventName')
}
}

if ($eventName -and $eventName -ne 'PreToolUse') {
return
}

$toolName = Get-PropertyValue -Object $payload -Names @('toolName', 'tool', 'tool_name')
if (-not $toolName) {
$toolPayload = Get-PropertyValue -Object $payload -Names @('toolInput', 'input', 'parameters', 'arguments')
if ($toolPayload) {
$toolName = Get-PropertyValue -Object $toolPayload -Names @('toolName', 'tool', 'tool_name')
}
}

$normalized = $raw.ToLowerInvariant()
$reminders = [System.Collections.Generic.List[string]]::new()

if ($normalized -match 'crates[\\/]+rustapi-rs[\\/]|api[\\/]+public[\\/]|contract\.md|cargo\.toml') {
Add-Reminder -List $reminders -Message 'Public API-adjacent files are in play: check CONTRACT.md, public API snapshots, labels (`feature` / `breaking`), and changelog follow-up.'
}

if ($normalized -match 'changelog\.md|releases\.md') {
Add-Reminder -List $reminders -Message 'Release-facing docs are being touched: keep entries user-focused, add migration notes for breaking changes, and call out MSRV changes explicitly.'
}

if ($normalized -match 'docs[\\/]+cookbook|[\\/]examples[\\/]|crates[\\/].+[\\/]examples[\\/]') {
Add-Reminder -List $reminders -Message 'Examples or cookbook content are involved: prefer `use rustapi_rs::prelude::*;`, keep the sample teachable, and verify surrounding docs still match.'
}

if ($normalized -match 'crates[\\/].+\.rs|tests[\\/].+\.rs') {
Add-Reminder -List $reminders -Message 'Rust source is being changed: prefer targeted crate validation first, then widen to workspace checks only if the scope really demands it.'
}

$dangerousPatterns = @(
@{ Pattern = 'git\s+push\b'; Reason = 'Pushing remote changes should stay an explicit human decision.' },
@{ Pattern = 'git\s+reset\s+--hard\b'; Reason = 'Hard resets discard local state and deserve a pause.' },
@{ Pattern = 'git\s+clean\b[^\r\n]*\s-f'; Reason = 'Force-clean commands can remove untracked work.' },
@{ Pattern = 'git\s+checkout\s+--\b'; Reason = 'Checkout-overwrite commands can discard local edits.' },
@{ Pattern = 'git\s+restore\b[^\r\n]*--source\b'; Reason = 'Restore with an explicit source can overwrite working-tree state.' },
@{ Pattern = 'gh\s+pr\s+merge\b'; Reason = 'Merging a PR is a release-significant action and should be deliberate.' },
@{ Pattern = 'remove-item\b[^\r\n]*-recurse\b[^\r\n]*-force\b'; Reason = 'Recursive forced deletion should be confirmed before running.' }
)

$needsConfirmation = $false
$confirmationReason = $null

foreach ($entry in $dangerousPatterns) {
if ($normalized -match $entry.Pattern) {
$needsConfirmation = $true
$confirmationReason = $entry.Reason
break
}
}

if ($needsConfirmation) {
$response = [ordered]@{
continue = $true
systemMessage = if ($reminders.Count -gt 0) { $reminders -join "`n" } else { 'Potentially destructive terminal action detected.' }
hookSpecificOutput = [ordered]@{
hookEventName = 'PreToolUse'
permissionDecision = 'ask'
permissionDecisionReason = $confirmationReason
}
}

Write-Output ($response | ConvertTo-Json -Depth 10 -Compress)
return
}

if ($reminders.Count -gt 0) {
$response = [ordered]@{
continue = $true
systemMessage = $reminders -join "`n"
}

Write-Output ($response | ConvertTo-Json -Depth 10 -Compress)
}
10 changes: 5 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
- name: Install native database client libraries
run: |
sudo apt-get update
sudo apt-get install -y pkg-config libmariadb-dev libmariadb-dev-compat libpq-dev libsqlite3-dev
sudo apt-get install -y pkg-config libmysqlclient-dev libpq-dev libsqlite3-dev

- name: Cache cargo registry
uses: actions/cache@v4
Expand Down Expand Up @@ -78,7 +78,7 @@ jobs:
- name: Install native database client libraries
run: |
sudo apt-get update
sudo apt-get install -y pkg-config libmariadb-dev libmariadb-dev-compat libpq-dev libsqlite3-dev
sudo apt-get install -y pkg-config libmysqlclient-dev libpq-dev libsqlite3-dev

- name: Cache cargo registry
uses: actions/cache@v4
Expand All @@ -94,7 +94,7 @@ jobs:
run: cargo fmt --all -- --check

- name: Run clippy
run: cargo clippy --workspace --all-features -- -D warnings
run: cargo clippy --workspace --all-features

build:
name: Build
Expand All @@ -116,7 +116,7 @@ jobs:
- name: Install native database client libraries
run: |
sudo apt-get update
sudo apt-get install -y pkg-config libmariadb-dev libmariadb-dev-compat libpq-dev libsqlite3-dev
sudo apt-get install -y pkg-config libmysqlclient-dev libpq-dev libsqlite3-dev

- name: Cache cargo registry
uses: actions/cache@v4
Expand Down Expand Up @@ -157,7 +157,7 @@ jobs:
- name: Install native database client libraries
run: |
sudo apt-get update
sudo apt-get install -y pkg-config libmariadb-dev libmariadb-dev-compat libpq-dev libsqlite3-dev
sudo apt-get install -y pkg-config libmysqlclient-dev libpq-dev libsqlite3-dev

- name: Cache cargo registry
uses: actions/cache@v4
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/release-drafter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ jobs:
if: github.event_name == 'push'
steps:
- uses: release-drafter/release-drafter@v7
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
token: ${{ secrets.GITHUB_TOKEN }}

update_pr_label:
permissions:
Expand All @@ -29,6 +29,6 @@ jobs:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: release-drafter/release-drafter@v7
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: release-drafter/release-drafter/autolabeler@v7
with:
token: ${{ secrets.GITHUB_TOKEN }}
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Embedded dashboard snapshots now include request-stage counters, route topology groups, health endpoint summary, and replay admin API discovery metadata.
- Dashboard UI adds route group/method/tag filters plus a replay browser that reuses the existing `ReplayLayer` admin API for list, detail, and diff workflows.
- Replay admin list endpoint now accepts UI-friendly pagination and filters: `offset`, `status_max`, `from`, `to`, `tag`, and `order`.

### Documentation

- Added a cookbook recipe and SVG preview for the embedded dashboard and replay browser workflow, including the inspection-first state rewind model and disabled-feature performance budget.

## [0.1.410] - 2026-03-09

### Added
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions api/public/rustapi-rs.all-features.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ pub use rustapi_rs::Cookies
pub use rustapi_rs::CorsLayer
pub use rustapi_rs::Created
pub use rustapi_rs::CsrfState
pub use rustapi_rs::DashboardConfig
pub use rustapi_rs::DashboardMetrics
pub use rustapi_rs::DashboardSnapshot
pub use rustapi_rs::Environment
pub use rustapi_rs::Extension
pub use rustapi_rs::ExtrasEnvironment
Expand Down Expand Up @@ -222,6 +225,21 @@ pub use rustapi_rs::core::put_route
pub use rustapi_rs::core::route
pub use rustapi_rs::core::serve_dir
pub use rustapi_rs::core::sse_response
pub mod rustapi_rs::dashboard
pub use rustapi_rs::dashboard::DashboardConfig
pub use rustapi_rs::dashboard::DashboardHealthEndpointSnapshot
pub use rustapi_rs::dashboard::DashboardHealthSummary
pub use rustapi_rs::dashboard::DashboardLiveCountersSnapshot
pub use rustapi_rs::dashboard::DashboardMetrics
pub use rustapi_rs::dashboard::DashboardReplayIndexSnapshot
pub use rustapi_rs::dashboard::DashboardSnapshot
pub use rustapi_rs::dashboard::DashboardStageSnapshot
pub use rustapi_rs::dashboard::ExecutionPath
pub use rustapi_rs::dashboard::RequestStage
pub use rustapi_rs::dashboard::RouteGraphSnapshot
pub use rustapi_rs::dashboard::RouteGroupSnapshot
pub use rustapi_rs::dashboard::RouteInventoryItem
pub use rustapi_rs::dashboard::RouteMetricsSnapshot
pub mod rustapi_rs::extras
pub mod rustapi_rs::extras::api_key
pub use rustapi_rs::extras::api_key::api_key
Expand Down
4 changes: 4 additions & 0 deletions crates/rustapi-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ rcgen = { workspace = true, optional = true }
# Replay (feature-gated)
async-trait = { workspace = true, optional = true }

# Dashboard (feature-gated)
dashmap = { version = "6.0", optional = true }

[dev-dependencies]
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
proptest = "1.4"
Expand All @@ -100,6 +103,7 @@ tracing = []
http3 = ["dep:quinn", "dep:h3", "dep:h3-quinn", "dep:rustls", "dep:rustls-pemfile"]
http3-dev = ["http3", "dep:rcgen"]
replay = ["dep:async-trait"]
dashboard = ["dep:dashmap"]



Expand Down
Loading
Loading