{"id":15011548,"url":"https://github.com/alrikolson/d2sharp","last_synced_at":"2025-10-13T04:10:27.418Z","repository":{"id":251709666,"uuid":"782751970","full_name":"AlrikOlson/D2Sharp","owner":"AlrikOlson","description":"D2Sharp wraps the D2 diagramming library for .NET, allowing you to render D2 diagrams with C# in your .NET applications.","archived":false,"fork":false,"pushed_at":"2025-10-04T06:26:09.000Z","size":190,"stargazers_count":1,"open_issues_count":12,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-04T06:54:30.264Z","etag":null,"topics":["csharp","d2","d2lang","diagrams","dotnet","nuget"],"latest_commit_sha":null,"homepage":"","language":"C#","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/AlrikOlson.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-04-06T00:16:57.000Z","updated_at":"2025-10-04T06:20:30.000Z","dependencies_parsed_at":"2025-04-12T03:43:52.123Z","dependency_job_id":"65406411-dfde-4deb-847c-b6c47f46b8e5","html_url":"https://github.com/AlrikOlson/D2Sharp","commit_stats":{"total_commits":47,"total_committers":2,"mean_commits":23.5,"dds":0.06382978723404253,"last_synced_commit":"f9e1c63e1665c36e56940b2b787990f075830d8c"},"previous_names":["alrikolson/d2sharp"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/AlrikOlson/D2Sharp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlrikOlson%2FD2Sharp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlrikOlson%2FD2Sharp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlrikOlson%2FD2Sharp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlrikOlson%2FD2Sharp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AlrikOlson","download_url":"https://codeload.github.com/AlrikOlson/D2Sharp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlrikOlson%2FD2Sharp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279013591,"owners_count":26085389,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-13T02:00:06.723Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["csharp","d2","d2lang","diagrams","dotnet","nuget"],"created_at":"2024-09-24T19:41:14.126Z","updated_at":"2025-10-13T04:10:27.410Z","avatar_url":"https://github.com/AlrikOlson.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# D2Sharp\n\nA .NET wrapper for [D2](https://d2lang.com/), the modern diagram scripting language that turns text to diagrams.\n\n[![NuGet](https://img.shields.io/nuget/v/D2Sharp.svg)](https://www.nuget.org/packages/D2Sharp/)\n[![codecov](https://codecov.io/gh/AlrikOlson/D2Sharp/branch/main/graph/badge.svg)](https://codecov.io/gh/AlrikOlson/D2Sharp)\n[![License](https://img.shields.io/github/license/AlrikOlson/D2Sharp)](LICENSE.txt)\n\n## Features\n\n- **Full D2 Support** - Render D2 diagrams\n- **Async/Await** - Task-based async rendering with cancellation and timeout support\n- **Thread-Safe** - Thread-safe with memory leak protection and error handling\n- **Flexible Rendering** - Layout engines (Dagre/ELK), themes, sketch mode\n- **Cross-Platform** - Works on Windows, macOS, and Linux\n- **Type-Safe** - Documented API with XML docs and nullable reference types\n- **Observability** - Built-in telemetry, metrics, and caching (v0.3.0+)\n\n## Installation\n\n```bash\ndotnet add package D2Sharp\n```\n\n## Quick Start\n\n### Basic Rendering\n\n```csharp\nusing D2Sharp;\n\nvar wrapper = new D2Wrapper();\nvar result = wrapper.RenderDiagram(\"A -\u003e B -\u003e C\");\n\nif (result.IsSuccess)\n{\n    Console.WriteLine(result.Svg);\n    // Save to file\n    File.WriteAllText(\"diagram.svg\", result.Svg);\n}\nelse\n{\n    Console.WriteLine($\"Error: {result.Error?.Message}\");\n}\n```\n\n### Async Rendering\n\n```csharp\nusing D2Sharp;\n\nvar wrapper = new D2Wrapper();\n\n// With cancellation token\nvar cts = new CancellationTokenSource();\nvar result = await wrapper.RenderDiagramAsync(\"x -\u003e y\", cancellationToken: cts.Token);\n\n// With timeout (30 seconds)\nvar result = await wrapper.RenderDiagramAsync(\n    \"A -\u003e B\",\n    timeout: TimeSpan.FromSeconds(30)\n);\n```\n\n## Rendering Options\n\nCustomize rendering with `RenderOptions`:\n\n### Themes\n\n300+ themes available:\n\n```csharp\nvar options = new RenderOptions\n{\n    ThemeId = 1  // Cool classics theme\n};\n\nvar result = wrapper.RenderDiagram(\"server -\u003e database\", options);\n```\n\n[View all D2 themes →](https://github.com/terrastruct/d2/tree/master/d2themes)\n\n### Layout Engines\n\n```csharp\nvar options = new RenderOptions\n{\n    Layout = LayoutEngine.Elk  // or LayoutEngine.Dagre (default)\n};\n\nvar result = wrapper.RenderDiagram(@\"\n    A -\u003e B\n    B -\u003e C\n    C -\u003e D\n\", options);\n```\n\n- **Dagre** - Faster, simpler layouts (default)\n- **ELK** - More complex layouts with additional features\n\n### Sketch Mode\n\nCreate hand-drawn style diagrams:\n\n```csharp\nvar options = new RenderOptions\n{\n    Sketch = true\n};\n\nvar result = wrapper.RenderDiagram(\"idea -\u003e prototype -\u003e product\", options);\n```\n\n### Visual Customization\n\n```csharp\nvar options = new RenderOptions\n{\n    Pad = 50,        // Padding around diagram (default: 100)\n    Scale = 0.5,     // Scale factor (0.5 = half size)\n    Center = true    // Center in viewbox\n};\n\nvar result = wrapper.RenderDiagram(\"start -\u003e end\", options);\n```\n\n### Combined Options\n\n```csharp\nvar options = new RenderOptions\n{\n    Layout = LayoutEngine.Elk,\n    ThemeId = 1,\n    Sketch = true,\n    Pad = 75,\n    Center = true\n};\n\nvar result = wrapper.RenderDiagram(@\"\n    server: Web Server {\n        shape: rectangle\n    }\n    db: Database {\n        shape: cylinder\n    }\n    server -\u003e db: queries\n\", options);\n```\n\n## Error Handling\n\nError information includes:\n\n```csharp\nvar result = wrapper.RenderDiagram(\"A -\u003e \");  // Invalid script\n\nif (!result.IsSuccess)\n{\n    var error = result.Error;\n    Console.WriteLine($\"Message: {error.Message}\");\n    Console.WriteLine($\"Line: {error.LineNumber}\");\n    Console.WriteLine($\"Column: {error.Column}\");\n    Console.WriteLine($\"Line Content: {error.LineContent}\");\n\n    // Get highlighted error parts\n    var parts = error.GetHighlightedLineParts();\n    Console.WriteLine($\"Before: {parts.beforeError}\");\n    Console.WriteLine($\"Error: {parts.errorPart}\");\n    Console.WriteLine($\"After: {parts.afterError}\");\n}\n```\n\n## Logging\n\nWorks with `Microsoft.Extensions.Logging`:\n\n```csharp\nusing Microsoft.Extensions.Logging;\n\nvar loggerFactory = LoggerFactory.Create(builder =\u003e\n{\n    builder.AddConsole();\n});\n\nvar logger = loggerFactory.CreateLogger\u003cD2Wrapper\u003e();\nvar wrapper = new D2Wrapper(logger);\n\nvar result = wrapper.RenderDiagram(\"A -\u003e B\");\n```\n\n## Resource Management\n\nImplements `IDisposable`:\n\n```csharp\nusing var wrapper = new D2Wrapper();\nvar result = wrapper.RenderDiagram(\"A -\u003e B\");\n\n// Resources automatically cleaned up when leaving scope\n```\n\n## Advanced Usage\n\n### Dark Theme Support\n\n```csharp\nvar options = new RenderOptions\n{\n    ThemeId = 0,          // Light theme\n    DarkThemeId = 200     // Dark theme (when client is in dark mode)\n};\n```\n\n### Timeout Protection\n\n```csharp\n// Set maximum rendering time\nvar result = await wrapper.RenderDiagramAsync(\n    complexScript,\n    timeout: TimeSpan.FromSeconds(15)\n);\n```\n\nTimeout bounds:\n- Minimum: 100ms\n- Maximum: 10 minutes\n\n### Input Validation\n\nAutomatic validation:\n- Maximum script length: 10MB\n- Timeout ranges\n- Disposed state\n\n```csharp\ntry\n{\n    var result = wrapper.RenderDiagram(veryLongScript);\n}\ncatch (ArgumentException ex)\n{\n    Console.WriteLine($\"Script too long: {ex.Message}\");\n}\n```\n\n## D2 Language Reference\n\nFull D2 language syntax:\n\n```d2\n# Shapes and connections\nserver -\u003e client: HTTPS\n\n# Containers\nnetwork: {\n  router\n  switch\n  router -\u003e switch\n}\n\n# Styling\nserver.style.fill: \"#4CAF50\"\nclient.shape: person\n\n# Direction\ndirection: right\n\n# And much more...\n```\n\n[Learn D2 syntax →](https://d2lang.com/tour/intro)\n\n## Observability \u0026 Diagnostics (v0.3.0+)\n\nBuilt-in caching, distributed tracing, and real-time metrics.\n\n### Quick Start with Observability\n\n```csharp\nusing D2Sharp;\n\n// Configure observability features\nvar options = new D2WrapperOptions\n{\n    EnableCaching = true,              // Enable response caching (default: true)\n    CacheSize = 100,                   // Max cache entries (default: 100)\n    CacheExpiration = TimeSpan.FromHours(1),\n    MaxConcurrentRenders = 10,         // Limit concurrent renders (0 = unlimited)\n    EnableTelemetry = true,            // Enable Activity/spans (default: true)\n    EnableMetrics = true,              // Enable EventCounters (default: true)\n    EnableDiagnosticIds = true         // Generate diagnostic IDs (default: true)\n};\n\nusing var wrapper = new D2Wrapper(options);\nvar result = wrapper.RenderDiagram(\"A -\u003e B -\u003e C\");\n\nConsole.WriteLine($\"Diagnostic ID: {result.DiagnosticId}\");\nConsole.WriteLine($\"From cache: {result.FromCache}\");\n```\n\n### Automatic Caching\n\nCaches successful renders based on script content and options:\n\n```csharp\nvar wrapper = new D2Wrapper(new D2WrapperOptions { EnableCaching = true });\n\n// First render - executes D2 engine\nvar result1 = wrapper.RenderDiagram(\"server -\u003e database\");\nConsole.WriteLine($\"From cache: {result1.FromCache}\"); // False\n\n// Second render - served from cache\nvar result2 = wrapper.RenderDiagram(\"server -\u003e database\");\nConsole.WriteLine($\"From cache: {result2.FromCache}\"); // True\nConsole.WriteLine($\"Same SVG: {result1.Svg == result2.Svg}\"); // True\n```\n\nCache keys are based on:\n- Script content (SHA256 hash)\n- Layout engine\n- Theme IDs\n- Sketch mode\n- Padding, scale, and center options\n\n### Concurrency Control\n\nLimit concurrent render operations to prevent resource exhaustion:\n\n```csharp\nvar options = new D2WrapperOptions\n{\n    MaxConcurrentRenders = 5  // Max 5 concurrent renders\n};\n\nusing var wrapper = new D2Wrapper(options);\n\n// These will be automatically throttled to 5 concurrent executions\nvar tasks = Enumerable.Range(0, 20)\n    .Select(i =\u003e wrapper.RenderDiagramAsync($\"Task {i} -\u003e Result {i}\"))\n    .ToArray();\n\nvar results = await Task.WhenAll(tasks);\n```\n\n### Distributed Tracing\n\nWorks with OpenTelemetry and Application Insights:\n\n```csharp\nusing System.Diagnostics;\nusing OpenTelemetry;\nusing OpenTelemetry.Trace;\n\n// Configure OpenTelemetry\nvar tracerProvider = Sdk.CreateTracerProviderBuilder()\n    .AddSource(\"D2Sharp\")\n    .AddConsoleExporter()\n    .Build();\n\nvar wrapper = new D2Wrapper(new D2WrapperOptions { EnableTelemetry = true });\nvar result = wrapper.RenderDiagram(\"A -\u003e B\");\n\n// Activity tags automatically include:\n// - d2sharp.script.length: Script character count\n// - d2sharp.layout.engine: Layout engine (dagre/elk)\n// - d2sharp.theme.id: Theme ID\n// - d2sharp.sketch.enabled: Sketch mode flag\n// - d2sharp.diagnostic.id: Diagnostic correlation ID\n// - d2sharp.cache.hit: Whether result was from cache\n// - d2sharp.result.status: success/error\n// - d2sharp.error.type: Error type if failed\n```\n\n### Real-Time Metrics\n\nMonitor performance with EventCounters:\n\n```bash\n# View real-time metrics with dotnet-counters\ndotnet-counters monitor -n YourApp --counters D2Sharp\n\n# Available metrics:\n# - renders-total: Total renders per second\n# - renders-active: Currently active render operations\n# - render-duration-ms: Average render duration (ms)\n# - cache-hit-rate: Cache hit percentage\n# - error-rate: Error percentage\n```\n\n### Diagnostic IDs\n\nEvery render gets a unique diagnostic ID for log correlation:\n\n```csharp\nvar wrapper = new D2Wrapper(new D2WrapperOptions { EnableDiagnosticIds = true });\nvar result = wrapper.RenderDiagram(\"A -\u003e B\");\n\n// Use diagnostic ID for log correlation\nConsole.WriteLine($\"Render completed: {result.DiagnosticId}\");\n// Output: Render completed: 7e3f5a9c2b1d4e8f...\n\n// Diagnostic IDs are unique per render, even for cache hits\nvar result2 = wrapper.RenderDiagram(\"A -\u003e B\");\nConsole.WriteLine($\"Cache hit: {result2.DiagnosticId}\"); // Different ID\n```\n\n### Minimal Configuration\n\nFor development or when you don't need observability features:\n\n```csharp\nvar options = new D2WrapperOptions\n{\n    EnableCaching = false,\n    EnableTelemetry = false,\n    EnableMetrics = false,\n    EnableDiagnosticIds = false\n};\n\nusing var wrapper = new D2Wrapper(options);\n// Behaves like the original wrapper with minimal overhead\n```\n\n## Performance\n\n- **Thread-safe**: Safe for concurrent use across multiple threads\n- **Memory efficient**: Automatic cleanup with try-finally patterns, minimal allocations\n- **Async-first**: Non-blocking async API with cancellation support\n- **Optimized**: GeneratedRegex for fast error parsing, zero-allocation patterns\n\n### Test Coverage\n\n- **Line Coverage**: 82.6%\n- **Branch Coverage**: 75.5%\n- **Method Coverage**: 97.4%\n\nCoverage reports are automatically generated on every commit and available on [Codecov](https://codecov.io/gh/AlrikOlson/D2Sharp).\n\n### Performance Benchmarks\n\nPerformance benchmarks are available in the `benchmarks/` directory using BenchmarkDotNet.\n\nRun benchmarks:\n```bash\ncd benchmarks/D2Sharp.Benchmarks\ndotnet run -c Release\n```\n\n**Typical Performance** (Apple Silicon M-series, .NET 8.0):\n- Simple diagrams (A -\u003e B): ~30-50ms\n- Complex diagrams (10-20 nodes): ~100-200ms\n- Very complex diagrams (50+ nodes): ~300-500ms\n\nPerformance varies based on diagram complexity, layout engine (Dagre vs ELK), and hardware.\n\n## Building from Source\n\n### Prerequisites\n\n- .NET 8.0 SDK or newer\n- Go 1.22+ or newer\n- GCC (for compiling the Go wrapper)\n\nCheck your setup:\n\n**Windows:**\n```powershell\n.\\depcheck.ps1\n```\n\n**Unix-based systems:**\n```bash\n./depcheck.sh\n```\n\n### Build\n\n```bash\ndotnet build\n```\n\n## Project Structure\n\n- `src/D2Sharp` - Main library project\n- `src/D2Sharp/d2wrapper` - Go wrapper code\n- `examples/D2Sharp.Web` - Web demo application\n- `tests/D2Sharp.Tests` - Unit and integration tests\n\n## Contributing\n\nContributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.\n\n## License\n\nMIT License - see [LICENSE.txt](LICENSE.txt) for details.\n\n## Acknowledgments\n\n- [D2](https://github.com/terrastruct/d2) - The modern diagram scripting language\n- Built with .NET 8.0\n\n## Links\n\n- [D2 Documentation](https://d2lang.com/)\n- [D2 Themes Gallery](https://github.com/terrastruct/d2/tree/master/d2themes)\n- [D2 Playground](https://play.d2lang.com/)\n- [Report Issues](https://github.com/AlrikOlson/D2Sharp/issues)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falrikolson%2Fd2sharp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falrikolson%2Fd2sharp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falrikolson%2Fd2sharp/lists"}