{"id":29583793,"url":"https://github.com/codedynasty-dev/sterad","last_synced_at":"2025-07-19T23:38:57.981Z","repository":{"id":302792344,"uuid":"1013303156","full_name":"CodeDynasty-dev/Sterad","owner":"CodeDynasty-dev","description":"Host your SPAs with SSR experience, no extra work, gain SEO and Fast Content delivery.","archived":false,"fork":false,"pushed_at":"2025-07-17T17:47:03.000Z","size":909,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-07-17T20:42:12.582Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/CodeDynasty-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2025-07-03T17:12:47.000Z","updated_at":"2025-07-17T17:47:07.000Z","dependencies_parsed_at":"2025-07-04T09:26:36.677Z","dependency_job_id":null,"html_url":"https://github.com/CodeDynasty-dev/Sterad","commit_stats":null,"previous_names":["codedynasty-dev/sterad"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/CodeDynasty-dev/Sterad","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CodeDynasty-dev%2FSterad","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CodeDynasty-dev%2FSterad/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CodeDynasty-dev%2FSterad/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CodeDynasty-dev%2FSterad/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/CodeDynasty-dev","download_url":"https://codeload.github.com/CodeDynasty-dev/Sterad/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CodeDynasty-dev%2FSterad/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266042415,"owners_count":23867962,"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","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":[],"created_at":"2025-07-19T23:38:57.366Z","updated_at":"2025-07-19T23:38:57.969Z","avatar_url":"https://github.com/CodeDynasty-dev.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Sterad\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/codedynasty-dev/sterad\"\u003e\n    \u003cimg src=\"https://raw.githubusercontent.com/CodeDynasty-dev/sterad/main/sterad.png\" alt=\"Sterad Logo\" width=\"200\" height=\"200\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003eSEO-Enabled SPA Server\u003c/strong\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  Transform your Single Page Application into an SEO-friendly, fast-loading website without changing your development workflow.\n\u003c/p\u003e\n\n## Features\n\n- **SEO Optimization**: Serve fully-rendered HTML to crawlers\n- **Progressive Caching**: Builds cache organically as users visit pages\n- **Hybrid Caching**: Memory + disk cache with LRU eviction\n- **Zero Framework Lock-in**: Works with React, Vue, Angular, and other SPAs\n- **Minimal Configuration**: Simple TOML-based setup\n- **Lightweight**: \u003c2KB client script with no dependencies\n- **Security Focused**: HTML sanitization and bot detection\n\n## Why Sterad?\n\nSterad solves the fundamental SEO problem of Single Page Applications by serving pre-rendered HTML to search engines while maintaining the full SPA experience for users.\n\n**Key Benefits:**\n\n- **Zero Development Changes**: Deploy your existing SPA without modifications\n- **Progressive Enhancement**: Cache builds organically as users visit pages\n- **Selective Serving**: Crawlers get SEO-optimized HTML, users get interactive SPA\n- **Production Ready**: Enterprise-grade security, performance, and reliability\n\n## How It Works\n\nSterad operates through a simple three-step process:\n\n1. **Initial Request**: When a crawler visits your SPA, Sterad serves the standard SPA shell with an injected capture script\n2. **Content Capture**: The script waits for your SPA to render, then captures and sanitizes the DOM content\n3. **Cache \u0026 Serve**: Subsequent requests for the same route receive the pre-rendered HTML directly from cache\n\n**Architecture:**\n\n- **Memory Cache**: Hot content served instantly from RAM\n- **Disk Cache**: Persistent storage for all cached pages\n- **Smart Routing**: Crawlers get cached HTML, users get interactive SPA\n- **Security Layer**: Multi-stage HTML sanitization prevents XSS attacks\n\n## Client API\n\nOn the client-side (`window.Sterad`) provides programmatic control over caching:\n\n### Methods\n\n#### `triggerCache()`\n\nManually trigger caching of the current page.\n\n```javascript\n// Trigger manual caching\nwindow.Sterad.triggerCache()\n  .then((result) =\u003e {\n    console.log(\"Page cached successfully:\", result);\n  })\n  .catch((error) =\u003e {\n    console.error(\"Caching failed:\", error);\n  });\n```\n\n#### `getCacheInfo()`\n\nGet detailed cache information for the current page.\n\n```javascript\n// Get cache information\nwindow.Sterad.getCacheInfo().then((info) =\u003e {\n  console.log(\"Cache info:\", info);\n  // Returns: { cached: boolean, lastCached: string|null, size?: number, path: string }\n});\n```\n\n#### `isCached()`\n\nCheck if the current page is cached.\n\n```javascript\n// Check cache status\nwindow.Sterad.isCached().then((cached) =\u003e {\n  console.log(\"Page is cached:\", cached);\n});\n```\n\n#### `getLastCached()`\n\nGet the last cached timestamp as a Date object.\n\n```javascript\n// Get last cached time\nwindow.Sterad.getLastCached().then((date) =\u003e {\n  if (date) {\n    console.log(\"Last cached:\", date.toISOString());\n  } else {\n    console.log(\"Page not cached\");\n  }\n});\n```\n\n### Usage Examples\n\n**Cache Status Indicator:**\n\n```javascript\n// Show cache status to users\nasync function showCacheStatus() {\n  const cached = await window.Sterad.isCached();\n  const lastCached = await window.Sterad.getLastCached();\n\n  if (cached \u0026\u0026 lastCached) {\n    console.log(`Page cached ${lastCached.toLocaleString()}`);\n  } else {\n    console.log(\"Page not cached\");\n  }\n}\n```\n\n**Manual Cache Control:**\n\n```javascript\n// Cache important pages immediately\nif (window.location.pathname === \"/important-page\") {\n  window.Sterad.triggerCache()\n    .then(() =\u003e console.log(\"Important page cached\"))\n    .catch((err) =\u003e console.error(\"Cache failed:\", err));\n}\n```\n\n**Cache Analytics:**\n\n```javascript\n// Track cache performance\nwindow.Sterad.getCacheInfo().then((info) =\u003e {\n  if (info.cached) {\n    // Send analytics event\n    analytics.track(\"page_served_from_cache\", {\n      path: info.path,\n      cache_age: Date.now() - new Date(info.lastCached).getTime(),\n    });\n  }\n});\n```\n\n### TypeScript Support\n\nFull TypeScript declarations are included:\n\n```typescript\ninterface SteradAPI {\n  triggerCache(): Promise\u003cany\u003e;\n  getCacheInfo(): Promise\u003cSteradCacheInfo\u003e;\n  isCached(): Promise\u003cboolean\u003e;\n  getLastCached(): Promise\u003cDate | null\u003e;\n}\n\ninterface SteradCacheInfo {\n  cached: boolean;\n  lastCached: string | null;\n  size?: number;\n  path: string;\n}\n\ndeclare global {\n  interface Window {\n    Sterad: SteradAPI;\n  }\n}\n```\n\n## Installation\n\n### Prerequisites\n\n- Bun v1.0.0 or newer\n\n### Setup Process\n\n1. Install Sterad globally:\n\n   ```bash\n   bun add sterad\n   ```\n\n2. Create configuration file (`sterad.toml`):\n\n   ```toml\n   # Required configuration\n   spa_dist = \"./dist\"\n   port = 9081\n   cache_routes = [\"/*\"]\n   memory_cache_limit = 100\n\n   # Optional configuration\n   not_cache_routes = [\"/admin/*\", \"/api/*\"]\n   serve_cached_to = \"crawlers_only\"  # or \"all_clients\"\n   ```\n\n3. Add build script to your package.json:\n\n   ```json\n   \"scripts\": {\n     \"build\": \"vite build\",\n     \"start\": \"sterad\"\n   }\n   ```\n\n4. Start the server:\n   ```bash\n   bun run start\n   ```\n\n## Configuration\n\nSterad uses a TOML configuration file with the following options:\n\n| Key                    | Required | Default                | Description                                                   |\n| ---------------------- | -------- | ---------------------- | ------------------------------------------------------------- |\n| **spa_dist**           | Yes      | -                      | Path to SPA build directory                                   |\n| **port**               | Yes      | -                      | Server port                                                   |\n| **cache_routes**       | Yes      | -                      | Route patterns to cache (supports wildcards)                  |\n| **memory_cache_limit** | Yes      | -                      | Maximum in-memory cache entries                               |\n| **not_cache_routes**   | No       | []                     | Routes to exclude from caching                                |\n| **serve_cached_to**    | No       | \"crawlers_only\"        | Who receives cached content: \"crawlers_only\" or \"all_clients\" |\n| **max_content_length** | No       | 1048576 (1MB)          | Maximum HTML content length in bytes                          |\n| **max_title_length**   | No       | 200                    | Maximum title length in characters                            |\n| **max_tag_ratio**      | No       | 0.7 (70%)              | Maximum ratio of HTML tags to content                         |\n| **allowed_tags**       | No       | [whitelist]            | Array of allowed HTML tags for security                       |\n| **intercept_script**   | No       | -                      | Path to script for HTML transformation before caching         |\n| **cache_dir**          | No       | spa_dist/.sterad_cache | Custom cache directory                                        |\n| **sanitization_level** | No       | \"strict\"               | HTML sanitization level                                       |\n\n### Environment Variables\n\n| Variable         | Required | Default        | Description                                        |\n| ---------------- | -------- | -------------- | -------------------------------------------------- |\n| **JWT_SECRET**   | No       | -              | JWT signing secret for admin routes (min 32 chars) |\n| **JWT_ISSUER**   | No       | \"sterad\"       | JWT token issuer                                   |\n| **JWT_AUDIENCE** | No       | \"sterad-admin\" | JWT token audience                                 |\n\n### Route Pattern Examples\n\n```toml\n# Cache all routes\ncache_routes = [\"/*\"]\n\n# Cache only product pages\ncache_routes = [\"/products/*\", \"/categories/*\"]\n\n# Exclude admin routes\nnot_cache_routes = [\"/admin/*\", \"/dashboard\"]\n```\n\n### Cache Serving Modes\n\nThe `serve_cached_to` option controls who receives cached content:\n\n**Crawlers Only Mode (Default)**:\n\n```toml\nserve_cached_to = \"crawlers_only\"\n```\n\n- Search engines and bots get cached HTML for SEO\n- Regular users get the full SPA experience\n- Best for maintaining SPA interactivity while optimizing SEO\n\n**All Clients Mode**:\n\n```toml\nserve_cached_to = \"all_clients\"\n```\n\n- Both crawlers and regular users get cached HTML\n- Faster initial page loads for all visitors\n- May reduce SPA interactivity on cached pages\n\n**Detected Bot User Agents**:\n\n- Google Bot, Bing Bot, Yahoo Slurp\n- Facebook, Twitter, LinkedIn crawlers\n- SEO tools (Ahrefs, SEMrush, etc.)\n- Generic patterns: bot, crawler, spider, curl, wget\n\n### HTML Intercept Scripts\n\nSterad supports custom HTML transformation scripts that run before content is cached:\n\n```toml\n# Enable HTML transformation\nintercept_script = \"./scripts/transform-html.js\"\n```\n\n**How it works**:\n\n1. After HTML sanitization, Sterad executes your intercept script\n2. The script receives JSON data via stdin with the HTML and context\n3. Your script can transform the HTML and output the result to stdout\n4. The transformed HTML is then cached to disk\n\n**Input Format**:\n\n```json\n{\n  \"html\": \"string\", // Complete HTML to be cached\n  \"context\": {\n    \"path\": \"string\", // URL path being cached\n    \"title\": \"string\", // Page title\n    \"content\": \"string\", // Sanitized main content\n    \"originalHtml\": \"string\", // Original SPA shell HTML\n    \"timestamp\": \"number\" // Unix timestamp\n  }\n}\n```\n\n**Example Use Cases**:\n\n- Add SEO meta tags dynamically\n- Inject structured data (JSON-LD)\n- Add performance optimization hints\n- Minify HTML output\n- Add analytics or tracking codes\n- Transform content for specific routes\n\n**Security Features**:\n\n- Script path validation (must be within project directory)\n- 5-second execution timeout\n- Output size validation\n- Graceful fallback on script failure\n\nSee `scripts/example-intercept.js` for a complete example.\n\n### Admin Authentication\n\nProtected admin routes require JWT authentication:\n\n```bash\n# Set JWT secret (required, min 32 chars)\nexport JWT_SECRET=\"your-super-secure-jwt-secret-key-here\"\n\n# Generate admin token\nbun run scripts/generate-jwt-token.js\n\n# Clear cache\ncurl -X DELETE \"http://localhost:9081/__sterad_capture\" \\\n  -H \"Authorization: Bearer YOUR_JWT_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"path\": \"/page-to-clear\"}'\n```\n\n## Security\n\nSterad implements well researched security measures:\n\n- **Multi-Layer Sanitization**: Client and server-side HTML sanitization\n- **Path Traversal Protection**: Secure file system access controls\n- **ReDoS Mitigation**: Regex timeout protection against denial-of-service attacks\n- **JWT Authentication**: Secure admin route access with configurable tokens\n- **Content Validation**: Comprehensive HTML structure and content validation\n- **Bot Detection**: Advanced user-agent analysis with caching optimization\n\n## Performance\n\nSterad delivers enterprise-grade performance through:\n\n- **LRU Memory Cache**: Instant serving of frequently accessed content\n- **Persistent Disk Cache**: Reliable storage with fast retrieval\n- **Optimized Bot Detection**: Cached user-agent analysis reduces CPU overhead\n- **Smart Static Asset Handling**: Pre-compiled extension matching for faster routing\n- **Intelligent Cache Headers**: Browser-optimized caching strategies\n\n## Deployment\n\n### Docker Deployment\n\n```dockerfile\nFROM oven/bun:1.0\n\nWORKDIR /app\nCOPY . .\nRUN bun install\n\nCMD [\"bun\", \"run\", \"start\"]\n```\n\nBuild and run:\n\n```bash\ndocker build -t sterad-app .\ndocker run -p 9081:9081 sterad-app\n```\n\n## Considerations\n\n- **Progressive Cache Building**: Cache populates as real users visit pages\n- **Dynamic Content**: Best suited for content that doesn't change frequently\n- **Framework Requirements**: Requires standard SPA root elements (`#root`, `#app`, etc.)\n\n## Troubleshooting\n\n### Common Issues\n\n**Cache not updating:**\n\n1. Check hard reload handling\n\n**Content not captured:**\n\n1. Verify root element matches these selectors:\n   ```js\n   const selectors = [\n     '[data-wrapper=\"app\"]',\n     \"#root\",\n     \"#app\",\n     \"#__next\",\n     '[role=\"main\"]',\n   ];\n   ```\n2. Check for CSP conflicts\n\n**Performance options:**\n\n1. Adjust memory cache size:\n   ```toml\n   memory_cache_limit = 200\n   ```\n2. Exclude static assets:\n   ```toml\n   not_cache_routes = [\"/static/*\"]\n   ```\n\n## Contributing\n\nWe welcome contributions! Please follow these steps:\n\n1. Fork the repository\n2. Create a feature branch:\n   ```bash\n   git checkout -b feat/awesome-feature\n   ```\n3. Commit your changes:\n   ```bash\n   git commit -m \"feat: implement awesome feature\"\n   ```\n4. Push to your branch:\n   ```bash\n   git push origin feat/awesome-feature\n   ```\n5. Open a pull request\n\n### Development Setup\n\n```bash\n# Clone repository\ngit clone https://github.com/your/sterad.git\n\n# Install dependencies\nbun install\n\n# Run in development mode\nbun run dev\n\n# Run tests\nbun run test\n\n# Build with tests\nbun bundle.ts\n\n# Skip tests during build\nSKIP_TESTS=true bun bundle.ts\n```\n\n## Testing\n\n**149 comprehensive tests** ensure production-ready security and reliability:\n\n```bash\n# Run all tests (6 test suites)\nnpm test\n\n# Build with integrated testing\nbun bundle.ts\n\n# Skip tests during build\nSKIP_TESTS=true bun bundle.ts\n```\n\n**Security Coverage:**\n\n- ✅ Bot Detection \u0026 User Agent Parsing\n- ✅ JWT Authentication \u0026 Authorization\n- ✅ Path Traversal Protection\n- ✅ ReDoS Mitigation \u0026 Regex Safety\n- ✅ Trust Boundary Validation\n- ✅ Intercept Script Security\n\n**Build Integration:** Tests run automatically before every build, preventing deployment of broken code. CI/CD pipeline includes multi-version testing and security audits.\n\nFor support, contact hello@codedynasty.dev.\n\n## Support\n\nFor information, visit [Codedynasty](https://codedynasty.dev) or email hello@codedynasty.dev.\n\n---\n\n**Codedynasty** © 2022-present, Codedynasty Contributors.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodedynasty-dev%2Fsterad","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcodedynasty-dev%2Fsterad","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodedynasty-dev%2Fsterad/lists"}