{"id":47247620,"url":"https://github.com/coregx/fursy","last_synced_at":"2026-05-14T00:32:26.659Z","repository":{"id":324792648,"uuid":"1097578420","full_name":"coregx/fursy","owner":"coregx","description":"Next-generation HTTP router for Go 1.25+ with type-safe handlers, RFC 9457 errors, and built-in OpenAPI generation. Minimal dependencies, blazing performance.","archived":false,"fork":false,"pushed_at":"2026-03-05T13:39:32.000Z","size":336,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-11T23:16:53.087Z","etag":null,"topics":["generics","go","golang","high-performance","http-router","middleware","openapi","production-ready","radix-tree","rest-api","rfc9457","type-safety","web-framework"],"latest_commit_sha":null,"homepage":"https://pkg.go.dev/github.com/coregx/fursy","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/coregx.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-16T13:01:13.000Z","updated_at":"2026-05-06T17:01:54.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/coregx/fursy","commit_stats":null,"previous_names":["coregx/fursy"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/coregx/fursy","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coregx%2Ffursy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coregx%2Ffursy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coregx%2Ffursy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coregx%2Ffursy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/coregx","download_url":"https://codeload.github.com/coregx/fursy/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coregx%2Ffursy/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33004948,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-13T13:14:54.681Z","status":"ssl_error","status_checked_at":"2026-05-13T13:14:51.610Z","response_time":115,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["generics","go","golang","high-performance","http-router","middleware","openapi","production-ready","radix-tree","rest-api","rfc9457","type-safety","web-framework"],"created_at":"2026-03-14T09:00:29.054Z","updated_at":"2026-05-14T00:32:26.645Z","avatar_url":"https://github.com/coregx.png","language":"Go","funding_links":[],"categories":["Web Frameworks"],"sub_categories":["Routers"],"readme":"# 🔥 FURSY\n\u003e **F**ast **U**niversal **R**outing **Sy**stem\n\nNext-generation HTTP router for Go with blazing performance, type-safe handlers, and minimal dependencies.\n\n[![Go Reference](https://pkg.go.dev/badge/github.com/coregx/fursy.svg)](https://pkg.go.dev/github.com/coregx/fursy)\n[![Go Report Card](https://goreportcard.com/badge/github.com/coregx/fursy)](https://goreportcard.com/report/github.com/coregx/fursy)\n[![Tests](https://github.com/coregx/fursy/actions/workflows/test.yml/badge.svg)](https://github.com/coregx/fursy/actions)\n[![codecov](https://codecov.io/gh/coregx/fursy/branch/main/graph/badge.svg)](https://codecov.io/gh/coregx/fursy)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Release](https://img.shields.io/github/v/release/coregx/fursy)](https://github.com/coregx/fursy/releases)\n\n---\n\n## ⚡ Quick Start\n\n```go\npackage main\n\nimport (\n    \"log\"\n    \"net/http\"\n\n    \"github.com/coregx/fursy\"\n)\n\nfunc main() {\n    router := fursy.New()\n\n    // Optional: Set validator for automatic validation\n    // router.SetValidator(validator.New())\n\n    // Simple text response with convenience method\n    router.GET(\"/\", func(c *fursy.Context) error {\n        return c.Text(\"Welcome to FURSY!\")  // 200 OK\n    })\n\n    // GET with convenience method (200 OK)\n    router.GET(\"/users/:id\", func(c *fursy.Context) error {\n        id := c.Param(\"id\")\n        return c.OK(map[string]string{\n            \"id\":   id,\n            \"name\": \"User \" + id,\n        })\n    })\n\n    // POST with convenience method (201 Created)\n    router.POST(\"/users\", func(c *fursy.Context) error {\n        username := c.Form(\"username\")\n        email := c.Form(\"email\")\n\n        user := map[string]string{\n            \"id\":    \"123\",\n            \"name\":  username,\n            \"email\": email,\n        }\n        return c.Created(user)  // 201 Created - REST best practice!\n    })\n\n    // DELETE with convenience method (204 No Content)\n    router.DELETE(\"/users/:id\", func(c *fursy.Context) error {\n        // Delete user...\n        return c.NoContentSuccess()  // 204 No Content\n    })\n\n    // Query parameters\n    router.GET(\"/search\", func(c *fursy.Context) error {\n        query := c.Query(\"q\")\n        page := c.QueryDefault(\"page\", \"1\")\n        return c.OK(map[string]string{\n            \"query\": query,\n            \"page\":  page,\n        })\n    })\n\n    log.Println(\"Server starting on :8080...\")\n    log.Fatal(http.ListenAndServe(\":8080\", router))\n}\n```\n\n\u003e **Note**: The examples above use simple handlers with `*Context`. For type-safe generic handlers `Box[Req, Res]`, see the [Type-Safe Handlers](#type-safe-handlers-first-in-go) section below!\n\n---\n\n## 🌟 Why FURSY?\n\n### Type-Safe Handlers (First in Go!)\n\n```go\nfunc Handler(box *fursy.Box[Request, Response]) error {\n    // Compile-time type safety\n    // Automatic validation\n    // Zero boilerplate\n}\n```\n\n### Native RFC 9457 Problem Details\n\n```json\n{\n  \"type\": \"https://fursy.coregx.dev/problems/validation-error\",\n  \"title\": \"Validation Failed\",\n  \"status\": 400,\n  \"errors\": [...]\n}\n```\n\n### Built-in OpenAPI 3.1 Generation\n\n```go\nspec := r.OpenAPI(fursy.OpenAPIConfig{\n    Title: \"My API\",\n    Version: \"1.0.0\",\n})\n// Complete OpenAPI 3.1 spec from code!\n```\n\n### Minimal Dependencies\n\n- **Core Routing**: Zero external dependencies (stdlib only)\n- **Middleware**: Minimal deps (JWT: golang-jwt/jwt, RateLimit: x/time)\n- **Plugins**: Optional extensions (OpenTelemetry, validators)\n- Predictable, minimal security surface\n\n### Production-Ready Performance\n\n- **256 ns/op** static routes, **326 ns/op** parametric routes\n- **1 allocation/op** (routing hot path)\n- **~10M req/s** throughput (simple routes)\n- Zero-allocation radix tree routing\n- Efficient context pooling\n\n---\n\n## 📦 Installation\n\n```bash\ngo get github.com/coregx/fursy\n```\n\n**Requirements**: Go 1.25+\n\n---\n\n## 🚀 Features\n\n- ✅ **High Performance Routing** - 256-326 ns/op, 1 alloc/op\n- ✅ **Type-Safe Generic Handlers** - Box[Req, Res] with compile-time safety\n- ✅ **Automatic Validation** - Set once, validate everywhere with 100+ tags\n- ✅ **Content Negotiation** - RFC 9110 compliant, AI agent support\n- ✅ **RFC 9457 Problem Details** - Standardized error responses\n- ✅ **Minimal Dependencies** - Core routing: stdlib only, middleware: minimal deps\n- ✅ **Middleware Pipeline** - Next/Abort pattern, pre-allocated buffers\n- ✅ **Route Groups** - Nested groups with middleware inheritance\n- ✅ **JWT Authentication** - Token validation, claims extraction\n- ✅ **Rate Limiting** - Token bucket algorithm, per-IP/per-user\n- ✅ **Security Headers** - OWASP 2025 compliant (CSP, HSTS, etc.)\n- ✅ **Circuit Breaker** - Failure threshold, auto-recovery\n- ✅ **Graceful Shutdown** - Connection draining, Kubernetes-ready\n- ✅ **Context Pooling** - Memory-efficient, prevents leaks\n- ✅ **Convenience Methods** - REST-friendly shortcuts (OK, Created, NoContentSuccess)\n- ✅ **Real-Time Communications** - SSE + WebSocket via stream library\n- ✅ **Database Integration** - dbcontext pattern with transaction support\n- ✅ **Production Boilerplate** - Complete DDD example with real-time features\n\n---\n\n## 🎛️ Middleware\n\nFURSY includes **8 production-ready middleware** with minimal dependencies. Core middleware have **zero external dependencies** (stdlib only), with only 2 exceptions: JWT (golang-jwt/jwt) and RateLimit (x/time).\n\n### Core Middleware (Zero Dependencies)\n\n#### Logger\n\nStructured logging with `log/slog` for comprehensive request tracking.\n\n```go\nimport (\n    \"log/slog\"\n    \"github.com/coregx/fursy/middleware\"\n)\n\n// Default configuration\nrouter.Use(middleware.Logger())\n\n// With configuration\nlogger := slog.New(slog.NewJSONHandler(os.Stdout, nil))\nrouter.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{\n    Logger: logger,\n    SkipPaths: []string{\"/health\", \"/metrics\"},\n}))\n```\n\n**Features**:\n- ✅ Structured logging with `log/slog` (stdlib)\n- ✅ Request method, path, status, latency, bytes written\n- ✅ Client IP extraction (X-Real-IP, X-Forwarded-For)\n- ✅ Skip paths or custom skip function\n- ✅ JSON or text format support\n- ✅ Zero external dependencies\n\n---\n\n#### Recovery\n\nPanic recovery with stack traces and RFC 9457 Problem Details.\n\n```go\nrouter.Use(middleware.Recovery())\n\n// With stack traces (development)\nrouter.Use(middleware.RecoveryWithConfig(middleware.RecoveryConfig{\n    IncludeStackTrace: true,\n}))\n```\n\n**Features**:\n- ✅ Automatic panic recovery\n- ✅ Stack trace logging\n- ✅ RFC 9457 error responses\n- ✅ Custom error handler\n- ✅ Production-safe (no stack traces by default)\n- ✅ Zero external dependencies\n\n---\n\n#### CORS\n\nCross-Origin Resource Sharing (RFC-compliant, OWASP recommended).\n\n```go\nrouter.Use(middleware.CORS())\n\n// With custom config\nrouter.Use(middleware.CORSWithConfig(middleware.CORSConfig{\n    AllowOrigins: \"https://example.com,https://foo.com\",\n    AllowMethods: \"GET,POST,PUT,DELETE\",\n    AllowHeaders: \"Content-Type,Authorization\",\n    AllowCredentials: true,\n    MaxAge: 12 * time.Hour,\n}))\n```\n\n**Features**:\n- ✅ Wildcard origins (`*`) support\n- ✅ Preflight requests (OPTIONS) handling\n- ✅ Credentials support\n- ✅ Expose headers configuration\n- ✅ MaxAge caching\n- ✅ Zero external dependencies\n\n---\n\n#### BasicAuth\n\nHTTP Basic Authentication with constant-time comparison.\n\n```go\nrouter.Use(middleware.BasicAuth(middleware.BasicAuthConfig{\n    Username: \"admin\",\n    Password: \"secret\",\n}))\n\n// With custom validator\nrouter.Use(middleware.BasicAuth(middleware.BasicAuthConfig{\n    Validator: func(username, password string) bool {\n        return checkDatabase(username, password)\n    },\n}))\n```\n\n**Features**:\n- ✅ Simple username/password validation\n- ✅ Custom validator function\n- ✅ Realm configuration\n- ✅ WWW-Authenticate header\n- ✅ Constant-time comparison (timing attack protection)\n- ✅ Zero external dependencies\n\n---\n\n#### Secure\n\nOWASP 2025 security headers for production hardening.\n\n```go\nrouter.Use(middleware.Secure())\n\n// With custom config\nrouter.Use(middleware.SecureWithConfig(middleware.SecureConfig{\n    ContentSecurityPolicy:   \"default-src 'self'; script-src 'self' 'unsafe-inline'\",\n    HSTSMaxAge:             31536000, // 1 year\n    HSTSExcludeSubdomains:  false,\n    XFrameOptions:          \"DENY\",\n    ContentTypeNosniff:     \"nosniff\",\n    ReferrerPolicy:         \"strict-origin-when-cross-origin\",\n}))\n```\n\n**Features (OWASP 2025)**:\n- ✅ Content-Security-Policy (CSP)\n- ✅ Strict-Transport-Security (HSTS)\n- ✅ X-Frame-Options\n- ✅ X-Content-Type-Options: nosniff\n- ✅ X-XSS-Protection (deprecated, not set by default)\n- ✅ Referrer-Policy\n- ✅ Cross-Origin-Embedder-Policy\n- ✅ Cross-Origin-Opener-Policy\n- ✅ Cross-Origin-Resource-Policy\n- ✅ Permissions-Policy\n\n**Coverage**: 100%\n**Dependencies**: Zero (stdlib only)\n\n---\n\n### Authentication \u0026 Rate Limiting\n\n#### JWT\n\nJWT token validation with algorithm confusion prevention.\n\n```go\n// Simple usage with signing key\nrouter.Use(middleware.JWT([]byte(\"your-secret-key\")))\n\n// With full configuration\nrouter.Use(middleware.JWTWithConfig(middleware.JWTConfig{\n    SigningKey:    []byte(\"your-secret-key\"),\n    SigningMethod: \"HS256\",\n    TokenLookup:  \"header:Authorization\",\n}))\n\n// With issuer and audience validation\nrouter.Use(middleware.JWTWithConfig(middleware.JWTConfig{\n    SigningKey:       []byte(\"secret\"),\n    SigningMethod:    \"HS256\",\n    ValidateIssuer:   \"my-app\",\n    ValidateAudience: \"api\",\n}))\n```\n\n**Features**:\n- ✅ Algorithms: HS256, HS384, HS512, RS256, ES256\n- ✅ Token from Header/Query/Cookie\n- ✅ Issuer/Audience validation\n- ✅ Algorithm confusion prevention (forbids \"none\")\n- ✅ Custom claims support\n- ✅ Expiration time validation\n\n**Dependency**: `github.com/golang-jwt/jwt/v5`\n\n---\n\n#### RateLimit\n\nToken bucket rate limiting with RFC-compliant headers.\n\n```go\n// Simple usage (100 req/s, burst of 200)\nrouter.Use(middleware.RateLimit(100, 200))\n\n// With full configuration and custom key function\nrouter.Use(middleware.RateLimitWithConfig(middleware.RateLimitConfig{\n    Rate:  10,\n    Burst: 20,\n    KeyFunc: func(c *fursy.Context) string {\n        // Rate limit by user ID\n        return c.GetString(\"user_id\")\n    },\n}))\n```\n\n**Features**:\n- ✅ Token bucket algorithm (`golang.org/x/time/rate`)\n- ✅ Per-IP or custom key function\n- ✅ RFC headers (`X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`)\n- ✅ In-memory store with automatic cleanup\n- ✅ Custom error handler\n- ✅ Configurable retry-after header\n\n**Dependency**: `golang.org/x/time/rate`\n\n---\n\n### Resilience\n\n#### CircuitBreaker\n\nZero-dependency circuit breaker for fault tolerance.\n\n```go\n// Simple usage with defaults (5 consecutive failures, 60s timeout)\nrouter.Use(middleware.CircuitBreaker())\n\n// With consecutive failures threshold\nrouter.Use(middleware.CircuitBreakerConsecutive(5, 30*time.Second))\n\n// With ratio-based threshold (3 failures out of 10 requests)\nrouter.Use(middleware.CircuitBreakerRatio(3, 10, 30*time.Second))\n\n// With full configuration\nrouter.Use(middleware.CircuitBreakerWithConfig(middleware.CircuitBreakerConfig{\n    ConsecutiveFailures: 5,\n    Timeout:             30 * time.Second,\n    MaxRequests:         2, // Allow 2 requests in half-open state\n}))\n```\n\n**Features**:\n- ✅ Zero external dependencies (pure Go)\n- ✅ Consecutive failures threshold\n- ✅ Ratio-based threshold\n- ✅ Time-window threshold\n- ✅ Half-open state with max requests\n- ✅ States: Closed → Open → Half-Open → Closed\n- ✅ Custom error handler\n- ✅ Thread-safe (concurrent request handling)\n\n**Coverage**: 95.6%\n**Dependencies**: Zero (stdlib only)\n\n---\n\n### Middleware Comparison\n\n| Middleware | FURSY | Gin | Echo | Fiber |\n|------------|-------|-----|------|-------|\n| **Logger** | ✅ `log/slog` | ✅ Custom | ✅ Custom | ✅ Custom |\n| **Recovery** | ✅ RFC 9457 | ✅ Basic | ✅ Basic | ✅ Basic |\n| **CORS** | ✅ Built-in (zero deps) | 🔧 Plugin | 🔧 Plugin | ✅ Built-in |\n| **BasicAuth** | ✅ Built-in | ✅ Built-in | ✅ Built-in | ✅ Built-in |\n| **JWT** | ✅ Built-in | 🔧 Plugin | 🔧 Plugin | ✅ Built-in |\n| **Rate Limit** | ✅ Built-in (RFC headers) | 🔧 Plugin | 🔧 Plugin | ✅ Built-in |\n| **Security Headers** | ✅ OWASP 2025 | ❌ | 🔧 Plugin | ✅ Basic |\n| **Circuit Breaker** | ✅ Zero deps | ❌ | ❌ | ❌ |\n| **Test Coverage** | **93.8%** | ? | ? | ? |\n| **Dependencies** | **Core: 0, JWT: 1, RateLimit: 1** | Multiple | Multiple | Multiple |\n\n**Legend**:\n- ✅ = Built-in with high quality implementation\n- 🔧 = Plugin/third-party required\n- ❌ = Not available\n\n**FURSY advantage**: Production-ready middleware with minimal dependencies, OWASP 2025 compliance, RFC 9457 error responses, and comprehensive test coverage.\n\n---\n\n### Learn More\n\n- **[Middleware Examples](examples/05-middleware/)** - Complete examples for all 8 middleware\n- **[Middleware Source](middleware/)** - Middleware implementations with tests\n\n---\n\n## 🎯 Convenience Methods (REST Best Practices)\n\nFURSY provides convenient shortcuts for common HTTP response patterns, following REST best practices:\n\n### Context Convenience Methods\n\n```go\n// GET - 200 OK (most common)\nrouter.GET(\"/users\", func(c *fursy.Context) error {\n    users := getAllUsers()\n    return c.OK(users)  // Short for c.JSON(200, users)\n})\n\n// POST - 201 Created (resource creation)\nrouter.POST(\"/users\", func(c *fursy.Context) error {\n    user := createUser(c)\n    return c.Created(user)  // 201, not 200!\n})\n\n// DELETE - 204 No Content (successful deletion)\nrouter.DELETE(\"/users/:id\", func(c *fursy.Context) error {\n    deleteUser(c.Param(\"id\"))\n    return c.NoContentSuccess()  // 204, no body\n})\n\n// Async operations - 202 Accepted\nrouter.POST(\"/jobs\", func(c *fursy.Context) error {\n    jobID := startAsyncJob(c)\n    return c.Accepted(map[string]string{\"jobId\": jobID})\n})\n\n// Simple text - 200 OK\nrouter.GET(\"/ping\", func(c *fursy.Context) error {\n    return c.Text(\"pong\")  // text/plain, 200\n})\n```\n\n**Why use convenience methods?**\n- ✅ **Less boilerplate** - `c.OK(data)` vs `c.JSON(200, data)`\n- ✅ **REST semantics** - `Created()` clearly indicates 201, preventing mistakes\n- ✅ **Self-documenting** - Code intent is clear from method name\n- ✅ **Flexibility** - Original methods still available for custom status codes\n\n**For custom status codes**, use explicit methods:\n```go\n// Partial content - 206\nreturn c.JSON(206, partialData)\n\n// Custom redirect - 307\nreturn c.Redirect(307, \"/new-location\")\n```\n\n### Box Convenience Methods (Type-Safe)\n\n```go\n// GET - 200 OK\nrouter.GET[GetUserRequest, UserResponse](\"/users/:id\", func(b *fursy.Box[GetUserRequest, UserResponse]) error {\n    user := getUser(b.ReqBody.ID)\n    return b.OK(user)  // Type-safe 200 OK\n})\n\n// POST - 201 Created with Location header\nrouter.POST[CreateUserRequest, UserResponse](\"/users\", func(b *fursy.Box[CreateUserRequest, UserResponse]) error {\n    user := createUser(b.ReqBody)\n    return b.Created(\"/users/\"+user.ID, user)  // 201 + Location\n})\n\n// PUT - 200 OK with body\nrouter.PUT[UpdateUserRequest, UserResponse](\"/users/:id\", func(b *fursy.Box[UpdateUserRequest, UserResponse]) error {\n    updated := updateUser(b.ReqBody)\n    return b.UpdatedOK(updated)  // Semantic clarity\n})\n\n// PUT - 204 No Content (no response body)\nrouter.PUT[UpdateUserRequest, Empty](\"/users/:id\", func(b *fursy.Box[UpdateUserRequest, Empty]) error {\n    updateUser(b.ReqBody)\n    return b.UpdatedNoContent()  // 204, no body\n})\n\n// DELETE - 204 No Content\nrouter.DELETE[Empty, Empty](\"/users/:id\", func(b *fursy.Box[Empty, Empty]) error {\n    deleteUser(c.Param(\"id\"))\n    return b.NoContentSuccess()  // 204\n})\n```\n\n### Plugin Integration Methods\n\nFURSY provides seamless integration with plugins through convenient Context methods:\n\n#### Database Access\n\n```go\nimport (\n    \"github.com/coregx/fursy\"\n    \"github.com/coregx/fursy/plugins/database\"\n)\n\n// Setup database\nsqlDB, _ := sql.Open(\"postgres\", dsn)\ndb := database.NewDB(sqlDB)\n\nrouter := fursy.New()\nrouter.Use(database.Middleware(db))\n\n// Access database in handlers\nrouter.GET(\"/users/:id\", func(c *fursy.Context) error {\n    db := c.DB().(*database.DB)  // Type assertion\n\n    var user User\n    err := db.QueryRow(c.Request.Context(),\n        \"SELECT id, name FROM users WHERE id = $1\", c.Param(\"id\")).\n        Scan(\u0026user.ID, \u0026user.Name)\n\n    if err == sql.ErrNoRows {\n        return c.Problem(fursy.NotFound(\"User not found\"))\n    }\n    return c.JSON(200, user)\n})\n```\n\n**Type-safe helper** (recommended):\n\n```go\nrouter.GET(\"/users/:id\", func(c *fursy.Context) error {\n    db, ok := database.GetDB(c)  // Type-safe retrieval\n    if !ok {\n        return c.Problem(fursy.InternalServerError(\"Database not configured\"))\n    }\n\n    // Use db...\n})\n```\n\n#### Server-Sent Events (SSE)\n\n```go\nimport (\n    \"github.com/coregx/fursy/plugins/stream\"\n    \"github.com/coregx/stream/sse\"\n)\n\n// Setup SSE hub\nhub := sse.NewHub[Notification]()\ngo hub.Run()\ndefer hub.Close()\n\nrouter.Use(stream.SSEHub(hub))\n\n// SSE endpoint\nrouter.GET(\"/events\", func(c *fursy.Context) error {\n    hub, _ := stream.GetSSEHub[Notification](c)\n\n    return stream.SSEUpgrade(c, func(conn *sse.Conn) error {\n        hub.Register(conn)\n        defer hub.Unregister(conn)\n\n        \u003c-conn.Done()  // Wait for client disconnect\n        return nil\n    })\n})\n```\n\n#### WebSocket\n\n```go\nimport (\n    \"github.com/coregx/fursy/plugins/stream\"\n    \"github.com/coregx/stream/websocket\"\n)\n\n// Setup WebSocket hub\nhub := websocket.NewHub()\ngo hub.Run()\ndefer hub.Close()\n\nrouter.Use(stream.WebSocketHub(hub))\n\n// WebSocket endpoint\nrouter.GET(\"/ws\", func(c *fursy.Context) error {\n    hub, _ := stream.GetWebSocketHub(c)\n\n    return stream.WebSocketUpgrade(c, func(conn *websocket.Conn) error {\n        hub.Register(conn)\n        defer hub.Unregister(conn)\n\n        for {\n            msgType, data, err := conn.Read()\n            if err != nil {\n                return err\n            }\n            hub.Broadcast(data)  // Echo to all clients\n        }\n    }, nil)\n})\n```\n\n**See also**:\n- **[plugins/database](plugins/database/)** - Database integration with transactions\n- **[plugins/stream](plugins/stream/)** - SSE and WebSocket real-time communication\n- **[examples/07-sse-notifications](examples/07-sse-notifications/)** - SSE example\n- **[examples/08-websocket-chat](examples/08-websocket-chat/)** - WebSocket example\n\n---\n\n## 🎯 Automatic Validation\n\nFURSY provides **type-safe automatic validation** through the validator plugin, giving you compile-time type safety combined with runtime validation - a unique combination in the Go ecosystem.\n\n### Why FURSY Validation is Different\n\nTraditional routers require **manual validation** on every handler:\n\n```go\n// ❌ Manual validation (Gin, Echo, Fiber)\nfunc CreateUser(c *gin.Context) {\n    var req CreateUserRequest\n    if err := c.BindJSON(\u0026req); err != nil {  // No validation!\n        c.JSON(400, gin.H{\"error\": err.Error()})\n        return\n    }\n\n    // Manual validation needed\n    if req.Email == \"\" || !isValidEmail(req.Email) {\n        c.JSON(400, gin.H{\"error\": \"invalid email\"})\n        return\n    }\n    // ... repeat for every field\n}\n```\n\nWith FURSY's **type-safe handlers**, validation is **automatic and guaranteed**:\n\n```go\n// ✅ Automatic validation (FURSY)\nrouter.POST[CreateUserRequest, UserResponse](\"/users\",\n    func(c *fursy.Box[CreateUserRequest, UserResponse]) error {\n        if err := c.Bind(); err != nil {\n            return err  // Automatic RFC 9457 error response\n        }\n\n        // c.ReqBody is ALREADY validated! ✅\n        user := createUser(c.ReqBody)\n        return c.Created(\"/users/\"+user.ID, user)\n    })\n```\n\n**Key advantages:**\n- ✅ **Set once, validate everywhere** - No manual checks per handler\n- ✅ **Compile-time type safety** - Generics ensure request/response types match\n- ✅ **RFC 9457 compliant** - Standard error format with field-level details\n- ✅ **100+ validation tags** - email, URL, UUID, min/max, and more\n\n### Quick Example\n\n```go\npackage main\n\nimport (\n    \"github.com/coregx/fursy\"\n    \"github.com/coregx/fursy/plugins/validator\"\n)\n\ntype CreateUserRequest struct {\n    Name  string `json:\"name\" validate:\"required,min=3,max=50\"`\n    Email string `json:\"email\" validate:\"required,email\"`\n    Age   int    `json:\"age\" validate:\"required,gte=18,lte=120\"`\n}\n\ntype UserResponse struct {\n    ID    string `json:\"id\"`\n    Name  string `json:\"name\"`\n    Email string `json:\"email\"`\n}\n\nfunc main() {\n    router := fursy.New()\n\n    // Set validator once - applies to ALL handlers\n    router.SetValidator(validator.New())\n\n    // Type-safe handler with automatic validation\n    router.POST[CreateUserRequest, UserResponse](\"/users\",\n        func(c *fursy.Box[CreateUserRequest, UserResponse]) error {\n            if err := c.Bind(); err != nil {\n                return err  // Automatic RFC 9457 response\n            }\n\n            // c.ReqBody is validated and type-safe!\n            user := createUser(c.ReqBody)\n            return c.Created(\"/users/\"+user.ID, user)\n        })\n\n    log.Fatal(http.ListenAndServe(\":8080\", router))\n}\n```\n\n### Validation Error Response\n\nWhen validation fails, FURSY returns **RFC 9457 Problem Details** with field-level errors:\n\n**Request:**\n```bash\ncurl -X POST http://localhost:8080/users \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\":\"Jo\",\"email\":\"invalid\",\"age\":15}'\n```\n\n**Response (422 Unprocessable Entity):**\n```json\n{\n  \"type\": \"about:blank\",\n  \"title\": \"Validation Failed\",\n  \"status\": 422,\n  \"detail\": \"3 field(s) failed validation\",\n  \"errors\": {\n    \"name\": \"Name must be at least 3 characters\",\n    \"email\": \"Email must be a valid email address\",\n    \"age\": \"Age must be 18 or greater\"\n  }\n}\n```\n\n### Comparison with Other Routers\n\n| Feature | FURSY | Gin | Echo | Fiber |\n|---------|-------|-----|------|-------|\n| Type Safety | ✅ Compile-time (`Box[Req, Res]`) | ❌ Runtime only | ❌ Runtime only | ❌ Runtime only |\n| Auto Validation | ✅ Set once, validate all | ❌ Manual per handler | ❌ Manual per handler | ❌ Manual per handler |\n| Error Format | ✅ RFC 9457 (standard) | ❌ Custom JSON | ❌ Custom JSON | ❌ Custom JSON |\n| Setup Complexity | ✅ One line (`SetValidator`) | ❌ Validator + binding per route | ❌ Validator + binding per route | ❌ Validator + binding per route |\n| Field-Level Errors | ✅ Automatic | 🔧 Manual mapping | 🔧 Manual mapping | 🔧 Manual mapping |\n\n**Learn more**: See [Validator Plugin Documentation](plugins/validator/README.md) for custom validators, nested structs, and advanced features.\n\n---\n\n## 🌐 Content Negotiation\n\nFURSY provides **RFC 9110 compliant content negotiation**, enabling your API to respond in multiple formats based on the client's `Accept` header. This is essential for building modern APIs that serve both humans (HTML/Markdown) and machines (JSON/XML).\n\n### Why Content Negotiation Matters\n\nModern APIs need to support multiple clients:\n- **Web Browsers** → HTML\n- **API Clients** → JSON\n- **AI Agents** → Markdown (for better understanding)\n- **Legacy Systems** → XML\n\nFURSY handles this automatically using **RFC 9110** standards with quality values (q-parameters).\n\n### Automatic Format Selection\n\nThe simplest approach - FURSY picks the best format automatically:\n\n```go\nrouter.GET(\"/users/:id\", func(c *fursy.Context) error {\n    user := getUser(c.Param(\"id\"))\n\n    // Automatically selects format based on Accept header\n    // Supports: JSON, HTML, XML, Text, Markdown\n    return c.Negotiate(200, user)\n})\n```\n\n**Client requests:**\n```bash\n# JSON (default)\ncurl http://localhost:8080/users/123\n# → Content-Type: application/json\n\n# HTML\ncurl -H \"Accept: text/html\" http://localhost:8080/users/123\n# → Content-Type: text/html\n\n# XML\ncurl -H \"Accept: application/xml\" http://localhost:8080/users/123\n# → Content-Type: application/xml\n```\n\n### Explicit Format Control\n\nFor finer control, check what the client accepts:\n\n```go\nrouter.GET(\"/docs\", func(c *fursy.Context) error {\n    // Check if client accepts markdown\n    if c.Accepts(fursy.MIMETextMarkdown) {\n        docs := generateMarkdownDocs()\n        return c.Markdown(docs)  // AI-friendly format\n    }\n\n    // Fallback to JSON\n    return c.OK(map[string]string{\"message\": \"Use Accept: text/markdown for docs\"})\n})\n```\n\n### Quality Values (q-parameter)\n\nRFC 9110 defines quality values to prioritize formats:\n\n```go\nrouter.GET(\"/api/data\", func(c *fursy.Context) error {\n    data := getData()\n\n    // Client sends: Accept: text/html;q=0.9, application/json;q=1.0\n    // FURSY automatically picks JSON (higher q-value)\n    format := c.AcceptsAny(\n        fursy.MIMEApplicationJSON,  // q=1.0\n        fursy.MIMETextHTML,          // q=0.9\n        fursy.MIMETextMarkdown,      // fallback\n    )\n\n    switch format {\n    case fursy.MIMEApplicationJSON:\n        return c.JSON(200, data)\n    case fursy.MIMETextHTML:\n        return c.HTML(200, renderHTML(data))\n    case fursy.MIMETextMarkdown:\n        return c.Markdown(formatMarkdown(data))\n    default:\n        return c.OK(data)  // Default to JSON\n    }\n})\n```\n\n### Supported Formats\n\n| Format | MIME Type | Constant | Use Case |\n|--------|-----------|----------|----------|\n| JSON | `application/json` | `MIMEApplicationJSON` | API responses (default) |\n| HTML | `text/html` | `MIMETextHTML` | Web browsers |\n| XML | `application/xml` | `MIMEApplicationXML` | Legacy systems |\n| Plain Text | `text/plain` | `MIMETextPlain` | Simple data |\n| Markdown | `text/markdown` | `MIMETextMarkdown` | AI agents, documentation |\n\n### AI Agent Support\n\nFURSY has first-class support for AI agents via Markdown responses:\n\n```go\nrouter.GET(\"/api/schema\", func(c *fursy.Context) error {\n    // AI agents prefer markdown for better understanding\n    if c.Accepts(fursy.MIMETextMarkdown) {\n        schema := `\n# API Schema\n\n## Users Endpoint\n- **GET** /users - List all users\n- **POST** /users - Create new user\n  - Required: name (string), email (string)\n\n## Authentication\nAll endpoints require Bearer token in Authorization header.\n`\n        return c.Markdown(schema)\n    }\n\n    // Regular clients get JSON\n    return c.JSON(200, getOpenAPISchema())\n})\n```\n\n**Why Markdown for AI?**\n- ✅ Better semantic understanding than JSON\n- ✅ Preserves structure (headers, lists, code blocks)\n- ✅ More context for LLMs to understand API behavior\n- ✅ Human-readable for debugging\n\n### Comparison with Other Routers\n\n| Feature | FURSY | Gin | Echo | Fiber |\n|---------|-------|-----|------|-------|\n| RFC 9110 Compliance | ✅ Full | 🔧 Partial | 🔧 Partial | 🔧 Partial |\n| Automatic Negotiation | ✅ `Negotiate()` | ❌ Manual | 🔧 `c.Format()` | ❌ Manual |\n| Quality Values (q) | ✅ Automatic | ❌ No | ❌ No | ❌ No |\n| Accept Helpers | ✅ `Accepts()`, `AcceptsAny()` | ❌ No | ❌ No | ✅ `c.Accepts()` |\n| Markdown Support | ✅ Built-in | ❌ Manual | ❌ Manual | ❌ Manual |\n| AI Agent Ready | ✅ Yes | ❌ No | ❌ No | ❌ No |\n\n**FURSY advantage**: Only router with full RFC 9110 compliance, automatic q-value handling, and built-in AI agent support.\n\n**Learn more**: See [RFC 9110 - HTTP Semantics (Content Negotiation)](https://datatracker.ietf.org/doc/html/rfc9110#section-12) for the complete specification.\n\n---\n\n## 📊 Observability\n\nFURSY provides **production-ready observability** through the OpenTelemetry plugin, giving you complete visibility into your HTTP services with distributed tracing and metrics.\n\n### Why Observability Matters\n\nModern distributed systems require:\n- **Distributed Tracing** → Track requests across microservices\n- **Performance Metrics** → Monitor latency, throughput, errors\n- **Error Tracking** → Automatic error recording and status tracking\n- **Production Debugging** → Understand behavior in real-time\n\nFURSY's OpenTelemetry plugin provides all of this with **zero boilerplate** - just add middleware.\n\n### Distributed Tracing\n\nTrack every request with W3C Trace Context propagation:\n\n```go\nimport (\n    \"context\"\n    \"github.com/coregx/fursy\"\n    \"github.com/coregx/fursy/plugins/opentelemetry\"\n    \"go.opentelemetry.io/otel\"\n    \"go.opentelemetry.io/otel/exporters/jaeger\"\n    sdktrace \"go.opentelemetry.io/otel/sdk/trace\"\n)\n\nfunc main() {\n    // Initialize OpenTelemetry tracer\n    exporter, _ := jaeger.New(jaeger.WithCollectorEndpoint(\n        jaeger.WithEndpoint(\"http://localhost:14268/api/traces\"),\n    ))\n    tp := sdktrace.NewTracerProvider(\n        sdktrace.WithBatcher(exporter),\n    )\n    otel.SetTracerProvider(tp)\n    defer tp.Shutdown(context.Background())\n\n    // Add tracing middleware - that's it!\n    router := fursy.New()\n    router.Use(opentelemetry.Middleware(\"my-service\"))\n\n    router.GET(\"/users/:id\", func(c *fursy.Context) error {\n        // Automatically traced! Span includes:\n        // - HTTP method, path, status\n        // - Request/response headers\n        // - Duration\n        // - Errors (if any)\n        user := getUser(c.Param(\"id\"))\n        return c.OK(user)\n    })\n\n    http.ListenAndServe(\":8080\", router)\n}\n```\n\n**Features**:\n- ✅ **W3C Trace Context** - Automatic propagation across services\n- ✅ **HTTP Semantic Conventions** - Full OpenTelemetry compliance\n- ✅ **Error Recording** - Automatic error and status tracking\n- ✅ **Zero Overhead Filtering** - Skip health checks and metrics endpoints\n\n### Metrics Collection\n\nTrack HTTP performance with Prometheus-compatible metrics:\n\n```go\nimport (\n    \"github.com/coregx/fursy\"\n    \"github.com/coregx/fursy/plugins/opentelemetry\"\n    \"go.opentelemetry.io/otel\"\n    \"go.opentelemetry.io/otel/exporters/prometheus\"\n    sdkmetric \"go.opentelemetry.io/otel/sdk/metric\"\n)\n\nfunc main() {\n    // Initialize Prometheus exporter\n    exporter, _ := prometheus.New()\n    mp := sdkmetric.NewMeterProvider(sdkmetric.WithReader(exporter))\n    otel.SetMeterProvider(mp)\n\n    // Add metrics middleware\n    router := fursy.New()\n    router.Use(opentelemetry.Metrics(\"my-service\"))\n\n    // Metrics automatically collected:\n    // - http.server.request.duration (histogram)\n    // - http.server.request.count (counter)\n    // - http.server.request.size (histogram)\n    // - http.server.response.size (histogram)\n\n    router.GET(\"/users\", func(c *fursy.Context) error {\n        users := getAllUsers()\n        return c.OK(users)\n    })\n\n    // Expose metrics at /metrics\n    router.GET(\"/metrics\", promhttp.Handler())\n\n    http.ListenAndServe(\":8080\", router)\n}\n```\n\n**Available Metrics**:\n\n| Metric | Type | Description | Labels |\n|--------|------|-------------|--------|\n| `http.server.request.duration` | Histogram | Request latency | method, status, server |\n| `http.server.request.count` | Counter | Total requests | method, status |\n| `http.server.request.size` | Histogram | Request body size | method |\n| `http.server.response.size` | Histogram | Response body size | method, status |\n\n**Cardinality Management**: All metrics use low-cardinality labels (method, status, server) to prevent metrics explosion.\n\n### Custom Spans for Business Logic\n\nAdd custom spans to trace specific operations:\n\n```go\nimport \"go.opentelemetry.io/otel\"\n\nrouter.GET(\"/users/:id\", func(c *fursy.Context) error {\n    // HTTP request span is created automatically by middleware\n\n    // Add custom span for database query\n    ctx := c.Request.Context()\n    tracer := otel.Tracer(\"my-service\")\n    ctx, span := tracer.Start(ctx, \"database.get_user\")\n    defer span.End()\n\n    user := db.GetUser(ctx, c.Param(\"id\"))\n\n    // Add custom span for external API call\n    _, apiSpan := tracer.Start(ctx, \"api.enrich_user_data\")\n    enrichedData := api.Enrich(user)\n    apiSpan.End()\n\n    return c.OK(enrichedData)\n})\n```\n\n### Jaeger Integration Example\n\nComplete setup with Jaeger for local development:\n\n```bash\n# Start Jaeger all-in-one (includes UI)\ndocker run -d --name jaeger \\\n  -e COLLECTOR_OTLP_ENABLED=true \\\n  -p 16686:16686 \\\n  -p 14268:14268 \\\n  jaegertracing/all-in-one:latest\n\n# View traces at http://localhost:16686\n```\n\nYour fursy application will automatically send traces to Jaeger. No configuration changes needed!\n\n### Comparison with Other Routers\n\n| Feature | FURSY | Gin | Echo | Fiber |\n|---------|-------|-----|------|-------|\n| OpenTelemetry Built-in | ✅ Plugin | 🔧 Third-party | 🔧 Third-party | 🔧 Third-party |\n| HTTP Semantic Conventions | ✅ Full | 🔧 Partial | 🔧 Partial | 🔧 Partial |\n| Metrics API | ✅ OpenTelemetry | 🔧 Prometheus only | 🔧 Prometheus only | ✅ Built-in |\n| Distributed Tracing | ✅ W3C Trace Context | 🔧 Manual | 🔧 Manual | 🔧 Manual |\n| Cardinality Management | ✅ Automatic | ❌ Manual | ❌ Manual | ✅ Automatic |\n| Zero-config | ✅ One line | ❌ Multiple steps | ❌ Multiple steps | ✅ One line |\n\n**FURSY advantage**: Official OpenTelemetry plugin with full HTTP semantic conventions compliance and zero-config setup.\n\n**Learn more**: See [OpenTelemetry Plugin Documentation](plugins/opentelemetry/README.md) for advanced configuration, custom spans, and production patterns.\n\n---\n\n## 📖 Documentation\n\n**Status**: 🟡 In Development\n\n- [Getting Started](#quick-start) (above)\n- [Validator Plugin](plugins/validator/README.md) - Type-safe validation\n- [OpenTelemetry Plugin](plugins/opentelemetry/README.md) - Distributed tracing and metrics\n- API Reference (coming soon)\n- Examples (coming soon)\n- Migration Guides (coming soon)\n\n---\n\n## 🎯 Comparison\n\n| Feature | FURSY | Gin | Echo | Chi | Fiber |\n|---------|--------|-----|------|-----|-------|\n| Type-Safe Handlers | ✅ | ❌ | ❌ | ❌ | ❌ |\n| Auto Validation | ✅ | 🔧 Manual | 🔧 Manual | 🔧 Manual | 🔧 Manual |\n| Content Negotiation | ✅ RFC 9110 | 🔧 Partial | 🔧 Partial | ❌ | 🔧 Partial |\n| Zero Deps (core) | ✅ | ❌ | ❌ | ✅ | ❌ |\n| OpenAPI Built-in | ✅ | 🔧 Plugin | 🔧 Plugin | 🔧 Plugin | 🔧 Plugin |\n| RFC 9457 Errors | ✅ | ❌ | ❌ | ❌ | ❌ |\n| Performance | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |\n| Go Version | 1.25+ | 1.13+ | 1.17+ | 1.16+ | 1.17+ |\n\n**FURSY is unique**: Only router combining furious performance, type-safe generics, automatic validation, RFC 9110 content negotiation, OpenAPI, and RFC 9457 with minimal dependencies.\n\n---\n\n## 📈 Status\n\n**Current Version**: v0.3.3 (Production Ready)\n\n**Status**: Production Ready - Complete ecosystem with real-time, database, and production examples\n\n**Coverage**: 93.8% test coverage (total), 94.3% core, 97.7% binding, 95.6% middleware\n\n**Performance**: 256 ns/op (static), 326 ns/op (parametric), 1 alloc/op\n\n**Roadmap**:\n\n```\n✅ v0.1.0          ✅ v0.2.0          ✅ v0.3.0             🎯 v1.0.0 LTS\n(Foundation)     (Docs+Examples)  (Real-time+DB)         (TBD - After Full\n                                                          API Stabilization)\n    │                  │                 │                       │\n    ▼                  ▼                 ▼                       ▼\nCore Router        Documentation    Real-Time+DB            Stable API\nMiddleware         11 Examples      Production Ready        Long-Term Support\nProduction         Validation       2 Plugins               (NOT Rushing!)\nFeatures           OpenAPI          DDD Boilerplate\n```\n\n**Current Status**: v0.3.3 Production Ready ✅\n**Ecosystem**: stream v0.1.0 (SSE + WebSocket), 2 production plugins, 10 examples\n**Next**: v0.x.x feature releases as needed (Cache, more plugins, community tools)\n**v1.0.0 LTS**: After 6-12 months of production usage and full API stabilization\n\n---\n\n## 🤝 Contributing\n\nWe welcome contributions! Please see:\n\n- [CONTRIBUTING.md](CONTRIBUTING.md) - Development workflow and guidelines\n- [RELEASE_GUIDE.md](RELEASE_GUIDE.md) - Release process\n- [SECURITY.md](SECURITY.md) - Security policy\n\n**Development Requirements**:\n- Go 1.25+\n- golangci-lint\n- Follow git-flow branching model\n\n**Want to help?**\n- ⭐ Star the repo\n- 📢 Share with others\n- 🐛 Report bugs or request features\n- 💬 Join discussions (coming soon)\n\n---\n\n## 📜 License\n\nMIT License - see [LICENSE](LICENSE) file for details\n\n---\n\n## 🔗 Links\n\n- **GitHub**: [github.com/coregx/fursy](https://github.com/coregx/fursy)\n- **Organization**: [github.com/coregx](https://github.com/coregx)\n- **Sister Project**: [Relica](https://github.com/coregx/relica) - Database query builder\n\n---\n\n## 💡 Inspiration\n\nFURSY stands on the shoulders of giants:\n\n**Technical**:\n- [ozzo-routing](https://github.com/go-ozzo/ozzo-routing) - Middleware pipeline\n- [httprouter](https://github.com/julienschmidt/httprouter) - Radix tree routing\n- [fiber](https://github.com/gofiber/fiber) - Performance inspiration\n- [FastAPI](https://fastapi.tiangolo.com/) (Python) - Type hints + OpenAPI\n\n**Philosophy**:\n- [Relica](https://github.com/coregx/relica) - Zero deps, type safety, quality\n\n### Special Thanks\n\n**Professor Ancha Baranova** - This project would not have been possible without her invaluable help and support. Her assistance was crucial in making all coregx projects a reality.\n\n---\n\n## 📞 Contact\n\n**Questions?** Check back soon for:\n- GitHub Discussions\n- Discord server\n- Documentation site\n\n---\n\n\u003cdiv align=\"center\"\u003e\n\n### 🔥 **FURSY** - Unleash Routing Fursy\n\n**Blazing Fast** • **Minimal Dependencies** • **Type-Safe** • **Furious**\n\n---\n\n*Built with ❤️ by the coregx team*\n\n**Version**: v0.3.3 - Production Ready\n**Ecosystem**: stream v0.1.0 + 2 plugins + 10 examples + DDD boilerplate\n**Next**: v1.0.0 LTS (after full API stabilization)\n\n\u003c/div\u003e\n\n---\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoregx%2Ffursy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcoregx%2Ffursy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoregx%2Ffursy/lists"}