{"id":33333993,"url":"https://github.com/miller-28/luminara","last_synced_at":"2025-11-21T14:03:50.002Z","repository":{"id":321245514,"uuid":"1083771378","full_name":"miller-28/luminara","owner":"miller-28","description":"Luminara — inspired by “lumen,” light — is a fast, versatile fetch client that works everywhere: browsers, frameworks, and Node.js. Zero dependencies, rich features, maximum flexibility.","archived":false,"fork":false,"pushed_at":"2025-11-15T10:41:33.000Z","size":859,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-11-15T12:20:58.771Z","etag":null,"topics":["api-client","backoff","browser","deduplication","fetch","framework-agnostic","hedging","http-client","interceptors","javascript","js-library","metrics","nodejs","request","requests","retry","universal","versatile","zero-dependencies"],"latest_commit_sha":null,"homepage":"https://luminara.website","language":"JavaScript","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/miller-28.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-26T17:26:22.000Z","updated_at":"2025-11-15T10:41:36.000Z","dependencies_parsed_at":"2025-10-28T17:38:33.573Z","dependency_job_id":null,"html_url":"https://github.com/miller-28/luminara","commit_stats":null,"previous_names":["miller-28/react-luminara","miller-28/luminara"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/miller-28/luminara","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/miller-28%2Fluminara","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/miller-28%2Fluminara/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/miller-28%2Fluminara/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/miller-28%2Fluminara/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/miller-28","download_url":"https://codeload.github.com/miller-28/luminara/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/miller-28%2Fluminara/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":285104675,"owners_count":27115600,"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-11-18T02:00:05.759Z","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":["api-client","backoff","browser","deduplication","fetch","framework-agnostic","hedging","http-client","interceptors","javascript","js-library","metrics","nodejs","request","requests","retry","universal","versatile","zero-dependencies"],"created_at":"2025-11-21T01:00:29.348Z","updated_at":"2025-11-21T14:03:49.983Z","avatar_url":"https://github.com/miller-28.png","language":"JavaScript","funding_links":[],"categories":["Architecture and Advanced Topics"],"sub_categories":["HTTP"],"readme":"# 🌌 Luminara\n\n[![Website](https://img.shields.io/badge/Website-luminara.website-blue?style=flat-square\u0026logo=web)](https://luminara.website)\n[![GitHub](https://img.shields.io/badge/GitHub-miller--28%2Fluminara-black?style=flat-square\u0026logo=github)](https://github.com/miller-28/luminara)\n[![npm](https://img.shields.io/npm/v/luminara?style=flat-square\u0026logo=npm)](https://www.npmjs.com/package/luminara)\n[![License](https://img.shields.io/badge/License-MIT-green?style=flat-square)](./LICENSE)\n\n**Luminara** is a modern, universal HTTP client built on native fetch, engineered for developers and teams who demand reliability, scalability, and architectural clarity.\nIt provides full lifecycle control over HTTP requests — from orchestration and interception to retries, deduplication, and analytics — all with zero external dependencies.\n\nLightweight by design yet powerful in scope, Luminara enables consistent, predictable network behavior across all environments — browsers (React, Vue, Angular, Svelte, vanilla JS) and Node.js 18+.\nIts domain-driven architecture and type-safe foundation make it ideal for enterprise-grade applications that need transparent debugging, real-time visibility, and extendable control over every request.\n\n## 🔗 Links\n\n- 🎨 **Interactive Sandbox And Documentation Website**: [luminara.website](https://luminara.website/)\n- 📦 **npm Package**: [npmjs.com/package/luminara](https://www.npmjs.com/package/luminara)\n- 🐙 **GitHub Repository**: [github.com/miller-28/luminara](https://github.com/miller-28/luminara)\n- ⚡ **Performance Benchmarks**: [luminara.website/benchmark](https://luminara.website/benchmark/)\n\n## ✨ Features\n\n### Core Architecture\n- ⚡ Built on modern native `fetch` - Zero external dependencies\n- 🌐 **Universal compatibility** - Browsers + Node.js 18+ with native fetch\n- 🏗️ **Framework-agnostic** - Works with React, Vue, Angular, Svelte, vanilla JS, and Node.js\n- 🏗️ **Domain-driven architecture** - Feature-based modular structure\n- 📦 **Dual export support** - ESM/CJS compatibility with auto-detection\n- 🚗 **Extensible driver architecture** - Custom drivers via forking\n- 💎 **Ultra-compact footprint**\n- 🪶 **Zero dependencies** - Truly standalone\n- 🔄 **Same API everywhere** - Identical behavior in all environments\n\n### Request Lifecycle (Orchestration Layer)\n- 🔌 **Enhanced interceptor architecture** - Deterministic order, mutable context, retry-aware\n- 🍪 **Plugin system** - Extensible architecture with official plugins (cookie-jar)\n- 📊 **Comprehensive stats system** - Real-time metrics, analytics, and query interface\n- 📝 **Verbose logging system** - Detailed debugging and request tracing\n\n### Pre-Flight Features (Request Dispatcher - Phase 1)\n- 🔄 **Request deduplication** - Automatic in-flight duplicate request prevention\n- ⏱️ **Request debouncing** - Intelligent request delay with automatic cancellation\n- 🚦 **Advanced rate limiting** - Token bucket algorithm with global, domain, and endpoint scoping\n\n### In-Flight Features (Request Execution - Phase 2)\n- ⏱️ **Configurable timeouts** - Request timeouts and abort controller support\n- 🔄 **Comprehensive retry system** - 6 backoff strategies (exponential, fibonacci, jitter, etc.)\n- 🏎️ **Request hedging** - Race and cancel-and-retry policies for latency optimization\n\n### Post-Flight Features (Response Handlers - Phase 3)\n- 🎯 **Response type handling** - JSON, text, form data, binary support\n- 🛡️ **Robust error handling** - Comprehensive error categorization and handling\n\n### Developer Experience\n- 🎯 **Fully promise-based** with TypeScript support\n\n---\n\n## ✅ Battle-Tested Reliability\n\nLuminara is validated by a **comprehensive test suite** covering all features and edge cases:\n\n- ✅ **241 tests** across **17 test suites** (100% passing)\n- 🎯 **Programmatic validation** - Tests actual behavior, not just API contracts\n- 🧪 **Framework simulation** - React, Vue, Angular usage patterns\n- ⏱️ **Timing accuracy** - Backoff strategies validated to millisecond precision\n- 🛡️ **Error scenarios** - Comprehensive failure case coverage\n- 🔄 **Integration testing** - Feature combinations (retry + timeout + hedging)\n- 📊 **Real package testing** - Tests built distribution, not source files\n\n**Test Categories:**\n- Basic HTTP Operations (8) • Retry Logic (23) • Backoff Strategies (17)\n- **Request Hedging (24)** • Interceptors (12) • Stats System (23)\n- Rate Limiting (7) • Debouncing (16) • Deduplication (17)\n- Error Handling (21) • Timeouts (11) • Response Types (7)\n- Custom Drivers (10) • Edge Cases (15) • Framework Patterns (8)\n- Plugins (7)\n\n📋 **[View Test Documentation](./test-cli/README.md)** • **[Run Tests Locally](./test-cli/)**\n\n---\n\n## 📦 Installation\n\n### NPM/Yarn (All Frameworks)\n```bash\n# npm\nnpm install luminara\n\n# yarn\nyarn add luminara\n\n# pnpm\npnpm add luminara\n```\n\n### CDN (Vanilla JavaScript)\n```html\n\u003c!-- ES Modules via CDN --\u003e\n\u003cscript type=\"module\"\u003e\n  import { createLuminara } from 'https://cdn.skypack.dev/luminara';\n  // Your code here\n\u003c/script\u003e\n```\n\n### Framework-Specific Imports\n\n**React, Vue, Angular, Svelte (Browser)**\n```javascript\nimport { createLuminara } from 'luminara';\n```\n\n**Node.js (ESM)**\n```javascript\nimport { createLuminara } from 'luminara';\n\nconst api = createLuminara({\n  baseURL: 'https://api.example.com',\n  retry: 3,\n  timeout: 5000\n});\n\nconst data = await api.getJson('/users');\nconsole.log(data);\n```\n\n**Node.js (CommonJS)**\n```javascript\nconst { createLuminara } = require('luminara');\n\nconst api = createLuminara({\n  baseURL: 'https://api.example.com'\n});\n\napi.getJson('/users')\n  .then(data =\u003e console.log(data))\n  .catch(err =\u003e console.error(err));\n```\n\n**Vanilla JavaScript (Browser)**\n```javascript\nimport { createLuminara } from 'luminara';\n```\n\n---\n\n## 🚀 Quick Start\n\n### Basic Usage\n\n```js\nimport { createLuminara } from \"luminara\";\n\nconst api = createLuminara();\n\n// GET JSON\nconst response = await api.getJson(\"https://api.example.com/users\");\nconsole.log(response.data);\n\n// POST JSON\nawait api.postJson(\"https://api.example.com/posts\", {\n  title: \"Hello Luminara\",\n  content: \"A beautiful HTTP client\"\n});\n\n// GET Text\nconst textResponse = await api.getText(\"https://example.com\");\n\n// POST Form Data\nawait api.postForm(\"https://api.example.com/upload\", {\n  name: \"John\",\n  email: \"john@example.com\"\n});\n\n// PUT/PATCH with JSON\nawait api.putJson(\"https://api.example.com/users/1\", { name: \"Updated\" });\nawait api.patchJson(\"https://api.example.com/users/1\", { email: \"new@example.com\" });\n\n// GET XML/HTML/Binary\nconst xmlResponse = await api.getXml(\"https://api.example.com/feed.xml\");\nconst htmlResponse = await api.getHtml(\"https://example.com\");\nconst blobResponse = await api.getBlob(\"https://api.example.com/file.pdf\");\nconst bufferResponse = await api.getArrayBuffer(\"https://api.example.com/data.bin\");\n\n// NDJSON (Newline Delimited JSON)\nconst ndjsonResponse = await api.getNDJSON(\"https://api.example.com/stream\");\n\n// Multipart form data\nconst formData = new FormData();\nformData.append('file', fileBlob);\nawait api.postMultipart(\"https://api.example.com/upload\", formData);\n\n// SOAP requests\nawait api.postSoap(\"https://api.example.com/soap\", xmlPayload, {\n  soapVersion: '1.1' // or '1.2'\n});\n```\n\n### Configuration\n\n```js\nconst api = createLuminara({\n  baseURL: \"https://api.example.com\",\n  timeout: 10000,\n  retry: 3,\n  retryDelay: 1000,\n  backoffType: \"exponential\",\n  backoffMaxDelay: 30000,\n  retryStatusCodes: [408, 429, 500, 502, 503, 504],\n  headers: {\n    \"Authorization\": \"Bearer YOUR_TOKEN\"\n  },\n  verbose: true,                // Enable detailed logging\n  statsEnabled: true,           // Enable request statistics (default: true)\n  ignoreResponseError: false,   // Throw on HTTP errors (default: false)\n  responseType: \"auto\",         // Auto-detect response type\n  query: {                      // Default query parameters\n    \"api_version\": \"v1\"\n  }\n});\n```\n\n### Rate Limiting\n\nLuminara includes advanced rate limiting with token bucket algorithm and flexible scoping:\n\n```js\nconst api = createLuminara({\n  baseURL: \"https://api.example.com\",\n  rateLimit: {\n    rps: 10,                   // 10 requests per second\n    burst: 20,                 // Allow burst of 20 requests\n    scope: 'domain'            // Rate limit per domain\n  }\n});\n\n// Different scoping options\nconst globalLimiter = createLuminara({\n  rateLimit: {\n    rps: 100,\n    scope: 'global'            // Single rate limit across all requests\n  }\n});\n\nconst endpointLimiter = createLuminara({\n  rateLimit: {\n    rps: 5,\n    scope: 'endpoint',         // Rate limit per unique endpoint\n    include: ['/api/users/*'], // Only apply to specific patterns\n    exclude: ['/api/health']   // Exclude certain endpoints\n  }\n});\n\n// Get rate limiting stats\nconst rateLimitStats = api.getRateLimitStats();\nconsole.log(rateLimitStats);\n\n// Reset rate limiting stats\napi.resetRateLimitStats();\n```\n\n---\n\n## 📤 Exports \u0026 Advanced Usage\n\nLuminara provides multiple export options for different use cases:\n\n### Simple Factory (Recommended)\n```js\nimport { createLuminara } from \"luminara\";\n\n// Creates client with NativeFetchDriver by default\nconst api = createLuminara({\n  baseURL: \"https://api.example.com\",\n  retry: 3,\n  backoffType: \"exponential\"\n});\n```\n\n### Direct Client \u0026 Driver Access\n```js\nimport { \n  LuminaraClient, \n  NativeFetchDriver\n} from \"luminara\";\n\n// Use native fetch driver (default and only driver)\nconst driver = NativeFetchDriver({\n  timeout: 10000,\n  retry: 5\n});\nconst api = new LuminaraClient(driver);\n```\n\n### Feature Utilities \u0026 Constants\n```js\nimport { \n  backoffStrategies,\n  createBackoffHandler,\n  defaultRetryPolicy,\n  createRetryPolicy,\n  parseRetryAfter,\n  isIdempotentMethod,\n  IDEMPOTENT_METHODS,\n  DEFAULT_RETRY_STATUS_CODES,\n  StatsHub,\n  METRIC_TYPES,\n  GROUP_BY_DIMENSIONS,\n  TIME_WINDOWS\n} from \"luminara\";\n\n// Use backoff strategies directly\nconst exponentialDelay = backoffStrategies.exponential(3, 1000); // 4000ms\n\n// Create custom retry policy\nconst customPolicy = createRetryPolicy({\n  maxRetries: 5,\n  statusCodes: [408, 429, 500, 502, 503],\n  methods: ['GET', 'POST', 'PUT']\n});\n\n// Check if method is idempotent\nif (isIdempotentMethod('GET')) {\n  console.log('Safe to retry GET requests');\n}\n\n// Create standalone stats instance\nconst stats = new StatsHub();\n```\n\n### Build System Support\nLuminara supports both ESM and CommonJS with automatic format detection:\n\n```js\n// ES Modules (modern bundlers, browsers)\nimport { createLuminara } from \"luminara\";\n\n// CommonJS (legacy environments)\nconst { createLuminara } = require(\"luminara\");\n```\n\n**Build Requirements for Development:**\n```bash\n# Required before testing sandbox/examples - generates dist files\nnpm run build        # Production build\nnpm run dev          # Development with watch mode\nnpm run build:watch  # Alternative watch mode command\n```\n\n**Note:** The dist files (`dist/index.mjs` and `dist/index.cjs`) are generated during build and required for the package to work properly. Always run `npm run build` after making changes to `src/` files.\n\n---\n\n## 🔄 Retry \u0026 Backoff Strategies\n\nLuminara includes 6 built-in backoff strategies for intelligent retry handling:\n\n### Linear Backoff\nFixed delay between retries.\n\n```js\nconst api = createLuminara({\n  retry: 5,\n  retryDelay: 1000,\n  backoffType: 'linear'\n});\n```\n\n### Exponential Backoff\nDelays grow exponentially (base × 2^n).\n\n```js\nconst api = createLuminara({\n  retry: 5,\n  retryDelay: 200,\n  backoffType: 'exponential'\n});\n// Delays: 200ms, 400ms, 800ms, 1600ms, 3200ms\n```\n\n### Exponential Capped\nExponential growth with a maximum delay cap.\n\n```js\nconst api = createLuminara({\n  retry: 5,\n  retryDelay: 300,\n  backoffType: 'exponentialCapped',\n  backoffMaxDelay: 3000\n});\n```\n\n### Fibonacci Backoff\nDelays follow the Fibonacci sequence.\n\n```js\nconst api = createLuminara({\n  retry: 8,\n  retryDelay: 200,\n  backoffType: 'fibonacci'\n});\n// Delays: 200ms, 200ms, 400ms, 600ms, 1000ms, 1600ms...\n```\n\n### Jitter Backoff\nRandomized delays to prevent thundering herd.\n\n```js\nconst api = createLuminara({\n  retry: 3,\n  retryDelay: 500,\n  backoffType: 'jitter'\n});\n```\n\n### Exponential Jitter\nCombines exponential growth with randomization.\n\n```js\nconst api = createLuminara({\n  retry: 4,\n  retryDelay: 300,\n  backoffType: 'exponentialJitter',\n  backoffMaxDelay: 5000\n});\n```\n\n### Custom Retry Handler\n\nFor full control, provide a custom retry function:\n\n```js\nconst api = createLuminara({\n  retry: 4,\n  retryDelay: (context) =\u003e {\n    const attempt = context.options.retry || 0;\n    console.log(`Retry attempt ${attempt}`);\n    return 150; // Custom delay in milliseconds\n  }\n});\n```\n\n### Retry on Specific Status Codes\n\n```js\nconst api = createLuminara({\n  retry: 3,\n  retryDelay: 500,\n  retryStatusCodes: [408, 429, 500, 502, 503]\n});\n```\n\n---\n\n## 🏎️ Request Hedging\n\nRequest hedging sends multiple concurrent or sequential requests to reduce latency by racing against slow responses. This is particularly effective for high-latency scenarios where P99 tail latencies impact user experience.\n\n### When to Use Hedging\n\n**Use hedging when:**\n- High P99 latencies are impacting user experience\n- Idempotent read operations (GET, HEAD, OPTIONS)\n- Server-side variability is high (cloud, microservices)\n- Cost of duplicate requests is acceptable\n\n**Don't use hedging when:**\n- Non-idempotent operations (POST, PUT, DELETE)\n- Bandwidth is severely constrained\n- Server capacity is limited\n- Operations have side effects\n\n### Basic Race Policy\n\nRace policy sends multiple concurrent requests and uses the first successful response:\n\n```js\nconst api = createLuminara({\n  hedging: {\n    policy: 'race',\n    hedgeDelay: 1000,     // Wait 1s before sending hedge\n    maxHedges: 2          // Up to 2 hedge requests\n  }\n});\n\n// Timeline:\n// T+0ms:    Primary request sent\n// T+1000ms: Hedge #1 sent (if primary not complete)\n// T+2000ms: Hedge #2 sent (if neither complete)\n// First successful response wins, others cancelled\nawait api.get('/api/data');\n```\n\n### Cancel-and-Retry Policy\n\nCancel-and-retry policy cancels slow requests and retries sequentially:\n\n```js\nconst api = createLuminara({\n  hedging: {\n    policy: 'cancel-and-retry',\n    hedgeDelay: 1500,     // Wait 1.5s before cancelling\n    maxHedges: 2          // Up to 2 hedge attempts\n  }\n});\n\n// Timeline:\n// T+0ms:    Primary request sent\n// T+1500ms: Cancel primary, send hedge #1\n// T+3000ms: Cancel hedge #1, send hedge #2\n// Only one request active at a time\nawait api.get('/api/data');\n```\n\n### Exponential Backoff \u0026 Jitter\n\nIncrease hedge delays exponentially with randomization to prevent thundering herd:\n\n```js\nconst api = createLuminara({\n  hedging: {\n    policy: 'race',\n    hedgeDelay: 500,            // Base delay: 500ms\n    maxHedges: 3,\n    exponentialBackoff: true,   // Enable exponential backoff\n    backoffMultiplier: 2,       // 2x each time\n    jitter: true,               // Add randomness\n    jitterRange: 0.3            // ±30% jitter\n  }\n});\n\n// Hedge timing with backoff:\n// - Primary:  0ms\n// - Hedge 1:  ~500ms  (500 ±30%)\n// - Hedge 2:  ~1000ms (1000 ±30%)\n// - Hedge 3:  ~2000ms (2000 ±30%)\n```\n\n### HTTP Method Whitelist\n\nBy default, only idempotent methods are hedged:\n\n```js\n// Default whitelist: ['GET', 'HEAD', 'OPTIONS']\n\nconst api = createLuminara({\n  hedging: {\n    policy: 'race',\n    hedgeDelay: 1000,\n    maxHedges: 2,\n    includeHttpMethods: ['GET', 'HEAD', 'OPTIONS', 'POST']  // Custom whitelist\n  }\n});\n\nawait api.get('/users');     // ✅ Hedged (in whitelist)\nawait api.post('/data', {}); // ✅ Hedged (added to whitelist)\nawait api.put('/users/1', {}); // ❌ Not hedged (not in whitelist)\n```\n\n### Per-Request Configuration (Bidirectional Override)\n\nOverride hedging settings for specific requests:\n\n```js\n// Scenario 1: Global enabled → disable per-request\nconst client = createLuminara({\n  hedging: { policy: 'race', hedgeDelay: 1000 }\n});\n\nawait client.get('/critical', {\n  hedging: { enabled: false }  // Disable for this request\n});\n\n// Scenario 2: Global disabled → enable per-request\nconst client2 = createLuminara({ /* no hedging */ });\n\nawait client2.get('/slow-endpoint', {\n  hedging: {                     // Enable for this request\n    policy: 'race',\n    hedgeDelay: 500,\n    maxHedges: 1\n  }\n});\n```\n\n### Server Rotation\n\nDistribute hedge requests across multiple servers:\n\n```js\nconst api = createLuminara({\n  hedging: {\n    policy: 'race',\n    hedgeDelay: 1000,\n    maxHedges: 2,\n    servers: [\n      'https://api1.example.com',\n      'https://api2.example.com',\n      'https://api3.example.com'\n    ]\n  }\n});\n\n// Each hedge uses a different server:\n// - Primary: api1.example.com/data\n// - Hedge 1: api2.example.com/data\n// - Hedge 2: api3.example.com/data\n```\n\n### Hedging vs Retry\n\n**Hedging** and **Retry** serve different purposes and can be used together:\n\n| Feature | Hedging | Retry |\n|---------|---------|-------|\n| **Purpose** | Reduce latency | Handle failures |\n| **Trigger** | Slow response | Error response |\n| **Requests** | Concurrent/Sequential proactive | Sequential reactive |\n| **Cost** | Higher (multiple requests) | Lower (on error only) |\n| **Use Case** | P99 optimization | Reliability |\n\n```js\n// Combined: Hedging for latency + Retry for reliability\nconst api = createLuminara({\n  retry: false,  // Disable retry (can use false or 0)\n  hedging: {\n    policy: 'race',\n    hedgeDelay: 1000,\n    maxHedges: 1\n  }\n});\n```\n\n### Configuration Options\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `enabled` | `boolean` | implicit | Explicit enable/disable (implicit if config present) |\n| `policy` | `string` | `'race'` | `'race'` or `'cancel-and-retry'` |\n| `hedgeDelay` | `number` | - | Base delay before hedge (ms) |\n| `maxHedges` | `number` | `1` | Maximum hedge requests |\n| `exponentialBackoff` | `boolean` | `false` | Enable exponential backoff |\n| `backoffMultiplier` | `number` | `2` | Backoff multiplier |\n| `jitter` | `boolean` | `false` | Add randomness to delays |\n| `jitterRange` | `number` | `0.3` | Jitter range (±30%) |\n| `includeHttpMethods` | `string[]` | `['GET', 'HEAD', 'OPTIONS']` | Hedged HTTP methods |\n| `servers` | `string[]` | `[]` | Server rotation URLs |\n\n### Performance Implications\n\n**Bandwidth:** Hedging increases bandwidth usage by sending multiple requests. For a `maxHedges: 2` configuration:\n- Best case: 1 request (primary succeeds quickly)\n- Worst case: 3 requests (primary + 2 hedges)\n- Average: ~1.5-2 requests depending on latency\n\n**Latency Reduction:** Typical P99 improvements:\n- Race policy: 30-60% reduction in tail latencies\n- Cancel-and-retry: 20-40% reduction with lower bandwidth cost\n\n---\n\n## 🚦 Rate Limiting\n\nLuminara's rate limiting system uses a **token bucket algorithm** with flexible scoping to control request flow and prevent API abuse.\n\n### Token Bucket Algorithm\n\nThe rate limiter maintains token buckets that refill at a steady rate:\n\n```js\nconst api = createLuminara({\n  rateLimit: {\n    rps: 10,  // Refill rate: 10 tokens per second\n    burst: 20           // Bucket capacity: 20 tokens max\n  }\n});\n\n// Allows bursts of 20 requests, then sustained 10 req/sec\nawait api.getJson('/api/data');  // Uses 1 token\n```\n\n### Scoping Strategies\n\nControl rate limiting granularity with different scoping options:\n\n#### Global Scoping\nSingle rate limit across all requests:\n```js\nconst api = createLuminara({\n  rateLimit: {\n    rps: 100,\n    scope: 'global'  // One bucket for everything\n  }\n});\n```\n\n#### Domain Scoping\nSeparate rate limits per domain:\n```js\nconst api = createLuminara({\n  rateLimit: {\n    rps: 50,\n    scope: 'domain'  // api.example.com vs api2.example.com\n  }\n});\n```\n\n#### Endpoint Scoping\nIndividual rate limits per unique endpoint:\n```js\nconst api = createLuminara({\n  rateLimit: {\n    rps: 5,\n    scope: 'endpoint'  // /api/users vs /api/posts\n  }\n});\n```\n\n### Pattern Matching\n\nFine-tune rate limiting with include/exclude patterns:\n\n```js\nconst api = createLuminara({\n  rateLimit: {\n    rps: 10,\n    scope: 'endpoint',\n    include: [\n      '/api/users/*',     // Rate limit user endpoints\n      '/api/posts/*'      // Rate limit post endpoints\n    ],\n    exclude: [\n      '/api/health',      // Exclude health checks\n      '/api/status'       // Exclude status checks\n    ]\n  }\n});\n```\n\n### Real-time Statistics\n\nMonitor rate limiting performance:\n\n```js\nconst api = createLuminara({\n  rateLimit: { rps: 10, burst: 20 }\n});\n\n// Get current statistics\nconst stats = api.stats.query()\n  .select(['totalRequests', 'rateLimitedRequests', 'averageWaitTime'])\n  .get();\n\nconsole.log('Rate limit stats:', stats);\n```\n\n### Dynamic Configuration\n\nUpdate rate limits at runtime:\n\n```js\n// Start with conservative limits\nconst api = createLuminara({\n  rateLimit: { rps: 5, burst: 10 }\n});\n\n// Increase limits based on server capacity\napi.updateConfig({\n  rateLimit: { rps: 20, burst: 40 }\n});\n```\n\n### Error Handling\n\nRate limiting integrates seamlessly with Luminara's error system:\n\n```js\ntry {\n  await api.getJson('/api/data');\n} catch (error) {\n  if (error.type === 'RATE_LIMITED') {\n    console.log(`Rate limited. Retry after: ${error.retryAfter}ms`);\n  }\n}\n```\n\n---\n\n## 🍪 Plugins\n\nLuminara supports an extensible plugin system to add custom functionality. Plugins can extend the client with new features while maintaining full compatibility with all Luminara features.\n\n### Cookie Jar Plugin\n\n**Package**: [`luminara-cookie-jar`](https://www.npmjs.com/package/luminara-cookie-jar)\n\nAutomatic `Cookie` / `Set-Cookie` header management for server-side environments using [tough-cookie](https://github.com/salesforce/tough-cookie).\n\nPerfect for Node.js, SSR applications, CLI tools, and test harnesses where cookies aren't automatically managed by the browser.\n\n**Installation:**\n```bash\nnpm install luminara-cookie-jar\n```\n\n**Quick Start:**\n```js\nimport { createLuminara } from 'luminara';\nimport { cookieJarPlugin } from 'luminara-cookie-jar';\n\nconst client = createLuminara({\n  baseURL: 'https://api.example.com',\n  plugins: [cookieJarPlugin()]\n});\n\n// Login request sets cookies automatically\nawait client.post('/login', { username: 'user', password: 'pass' });\n\n// Subsequent requests include cookies automatically\nawait client.get('/profile');  // Cookies sent automatically!\n\n// Access cookie jar directly\nconst cookies = await client.jar.getCookies('https://api.example.com');\nconsole.log('Cookies:', cookies);\n```\n\n**Features:**\n- 🔄 Automatic cookie management (stores `Set-Cookie`, sends `Cookie`)\n- 🌐 Universal compatibility (Node.js, SSR, CLI tools)\n- 🤝 Shared cookie jars across multiple clients\n- 📝 Full TypeScript support\n- 🎯 RFC 6265 compliant\n\n**Documentation:**\n- 📦 [npm Package](https://www.npmjs.com/package/luminara-cookie-jar)\n- 📖 [Full Documentation](https://github.com/miller-28/luminara-cookie-jar#readme)\n- 🔌 [Plugin Development Guide](./docs/plugins/README.md)\n\n---\n\n## 🔌 Enhanced Interceptor System\n\nLuminara's interceptor architecture provides **deterministic execution order** and **guaranteed flow control** with a mutable context object that travels through the entire request lifecycle.\n\n### Execution Flow \u0026 Order Guarantees\n\n```\nRequest → onRequest[] (L→R) → Driver → onResponse[] (R→L) → Success\n                                   ↓\n                               onResponseError[] (R→L) → Error\n```\n\n**Order Guarantees:**\n- **onRequest**: Executes **Left→Right** (registration order)\n- **onResponse**: Executes **Right→Left** (reverse registration order)  \n- **onResponseError**: Executes **Right→Left** (reverse registration order)\n- **On Retry**: Re-runs onRequest interceptors for fresh tokens/headers\n\n### Mutable Context Object\n\nEach interceptor receives a **mutable context** object:\n\n```js\n{\n  req: { /* request object */ },      // Mutable request\n  res: { /* response object */ },     // Mutable response (in onResponse)\n  error: { /* error object */ },      // Error details (in onResponseError)\n  attempt: 1,                         // Current retry attempt number\n  controller: AbortController,        // Request abort controller\n  meta: {}                           // Custom metadata storage\n}\n```\n\n### Request Interceptor\n\nModify requests with guaranteed **Left→Right** execution:\n\n```js\n// First registered = First executed\napi.use({\n  onRequest(context) {\n    console.log(`📤 Attempt ${context.attempt}:`, context.req.method, context.req.url);\n    \n    // Modify request directly\n    context.req.headers = {\n      ...(context.req.headers || {}),\n      'X-Custom-Header': 'Luminara',\n      'X-Attempt': context.attempt.toString()\n    };\n\n    // Store metadata for later interceptors\n    context.meta.startTime = Date.now();\n    \n    // Add fresh auth token (important for retries!)\n    context.req.headers['Authorization'] = `Bearer ${getFreshToken()}`;\n  }\n});\n\n// Second registered = Second executed\napi.use({\n  onRequest(context) {\n    console.log('🔐 Adding security headers...');\n    context.req.headers['X-Request-ID'] = generateRequestId();\n  }\n});\n```\n\n### Response Interceptor\n\nTransform responses with guaranteed **Right→Left** execution:\n\n```js\n// First registered = LAST executed (reverse order)\napi.use({\n  onResponse(context) {\n    console.log('📥 Processing response:', context.res.status);\n    \n    // Transform response data\n    context.res.data = {\n      ...context.res.data,\n      timestamp: new Date().toISOString(),\n      processingTime: Date.now() - context.meta.startTime,\n      attempt: context.attempt\n    };\n  }\n});\n\n// Second registered = FIRST executed (reverse order)\napi.use({\n  onResponse(context) {\n    console.log('✅ Response received, validating...');\n    \n    // Validate response structure\n    if (!context.res.data || typeof context.res.data !== 'object') {\n      throw new Error('Invalid response format');\n    }\n  }\n});\n```\n\n### Error Handler\n\nHandle errors with guaranteed **Right→Left** execution:\n\n```js\napi.use({\n  onResponseError(context) {\n    console.error(`❌ Request failed (attempt ${context.attempt}):`, context.req.url);\n    console.error('Error:', context.error.message);\n    \n    // Log error details\n    context.meta.errorLogged = true;\n    \n    // Modify error before it propagates\n    if (context.error.status === 401) {\n      context.error.message = 'Authentication failed - please refresh your session';\n    }\n  }\n});\n```\n\n### Retry-Aware Authentication\n\nThe enhanced system re-runs **onRequest** interceptors on retry, perfect for refreshing tokens:\n\n```js\napi.use({\n  onRequest(context) {\n    // This runs EVERY attempt, ensuring fresh tokens\n    const token = context.attempt === 1 \n      ? getCachedToken() \n      : await refreshToken(); // Fresh token on retry\n      \n    context.req.headers['Authorization'] = `Bearer ${token}`;\n    \n    console.log(`🔑 Attempt ${context.attempt}: Using ${context.attempt === 1 ? 'cached' : 'fresh'} token`);\n  }\n});\n```\n\n### Complex Multi-Interceptor Example\n\nDemonstrates guaranteed execution order and context sharing:\n\n```js\n// Interceptor 1: Authentication (runs FIRST on request, LAST on response)\napi.use({\n  onRequest(context) {\n    console.log('1️⃣ [Auth] Adding authentication...');\n    context.req.headers['Authorization'] = `Bearer ${getToken()}`;\n    context.meta.authAdded = true;\n  },\n  onResponse(context) {\n    console.log('1️⃣ [Auth] Validating auth response... (LAST)');\n    if (context.res.status === 401) {\n      invalidateToken();\n    }\n  },\n  onResponseError(context) {\n    console.log('1️⃣ [Auth] Handling auth error... (LAST)');\n    if (context.error.status === 401) {\n      context.meta.authFailed = true;\n    }\n  }\n});\n\n// Interceptor 2: Logging (runs SECOND on request, MIDDLE on response)\napi.use({\n  onRequest(context) {\n    console.log('2️⃣ [Log] Request started...');\n    context.meta.startTime = performance.now();\n  },\n  onResponse(context) {\n    console.log('2️⃣ [Log] Request completed (MIDDLE)');\n    const duration = performance.now() - context.meta.startTime;\n    console.log(`Duration: ${duration.toFixed(2)}ms`);\n  },\n  onResponseError(context) {\n    console.log('2️⃣ [Log] Request failed (MIDDLE)');\n    const duration = performance.now() - context.meta.startTime;\n    console.log(`Failed after: ${duration.toFixed(2)}ms`);\n  }\n});\n\n// Interceptor 3: Analytics (runs THIRD on request, FIRST on response)\napi.use({\n  onRequest(context) {\n    console.log('3️⃣ [Analytics] Tracking request... (LAST)');\n    analytics.trackRequestStart(context.req.url);\n  },\n  onResponse(context) {\n    console.log('3️⃣ [Analytics] Tracking success... (FIRST)');\n    analytics.trackRequestSuccess(context.req.url, context.res.status);\n  },\n  onResponseError(context) {\n    console.log('3️⃣ [Analytics] Tracking error... (FIRST)');\n    analytics.trackRequestError(context.req.url, context.error);\n  }\n});\n\n/*\nExecution Order:\nRequest: 1️⃣ Auth → 2️⃣ Log → 3️⃣ Analytics → HTTP Request\nResponse: 3️⃣ Analytics → 2️⃣ Log → 1️⃣ Auth\nError: 3️⃣ Analytics → 2️⃣ Log → 1️⃣ Auth\n*/\n```\n\n### Abort Controller Access\n\nEvery request gets an AbortController accessible via context:\n\n```js\napi.use({\n  onRequest(context) {\n    // Cancel request after 5 seconds\n    setTimeout(() =\u003e {\n      console.log('⏰ Request taking too long, aborting...');\n      context.controller.abort();\n    }, 5000);\n  },\n  onResponseError(context) {\n    if (context.error.name === 'AbortError') {\n      console.log('🚫 Request was aborted');\n    }\n  }\n});\n```\n\n---\n\n## ⏱️ Timeout \u0026 Abort\n\n### Configure Timeout\n\n```js\nconst api = createLuminara({\n  timeout: 5000 // 5 seconds\n});\n\n// Will throw timeout error if request takes longer than 5s\nawait api.get('https://slow-api.example.com/data');\n```\n\n### Manual Abort with AbortController\n\n```js\nconst controller = new AbortController();\n\n// Start request\nconst promise = api.get('https://api.example.com/long-task', {\n  signal: controller.signal\n});\n\n// Abort after 2 seconds\nsetTimeout(() =\u003e controller.abort(), 2000);\n\ntry {\n  await promise;\n} catch (error) {\n  if (error.name === 'AbortError') {\n    console.log('Request was cancelled');\n  }\n}\n```\n\n---\n\n## 🚗 Custom Drivers\n\nReplace the default native fetch driver with your own implementation:\n\n```js\nimport { LuminaraClient } from \"luminara\";\n\nconst customDriver = () =\u003e ({\n  async request(options) {\n    const { url, method = 'GET', headers, body, signal } = options;\n    \n    const response = await fetch(url, {\n      method,\n      headers,\n      body: body ? JSON.stringify(body) : undefined,\n      signal\n    });\n    \n    const contentType = response.headers.get('content-type') || '';\n    const isJson = contentType.includes('application/json');\n    const data = isJson ? await response.json() : await response.text();\n    \n    return {\n      status: response.status,\n      headers: response.headers,\n      data\n    };\n  }\n});\n\nconst api = new LuminaraClient(customDriver());\n```\n\n---\n\n## 📊 Stats System\n\nLuminara includes a **comprehensive statistics system** that tracks request metrics, performance data, and analytics in real-time. Perfect for monitoring application health and request patterns.\n\n### Basic Stats Usage\n\n```js\nconst api = createLuminara({\n  baseURL: \"https://api.example.com\",\n  statsEnabled: true // enabled by default\n});\n\n// Make some requests\nawait api.getJson('/users');\nawait api.postJson('/posts', { title: 'Hello' });\n\n// Get basic counters\nconst counters = api.stats().counters.get();\nconsole.log(counters);\n// { total: 2, success: 2, fail: 0, inflight: 0, retried: 0, aborted: 0 }\n\n// Get performance metrics\nconst timeMetrics = api.stats().time.get();\nconsole.log(timeMetrics);\n// { minMs: 150, avgMs: 275, p50Ms: 200, p95Ms: 350, p99Ms: 350, maxMs: 400 }\n```\n\n### Advanced Query Interface\n\nThe stats system provides a powerful query interface for detailed analytics:\n\n```js\n// Query stats by endpoint\nconst endpointStats = api.stats().query({\n  metrics: ['counters', 'time', 'rate'],\n  groupBy: 'endpoint',\n  window: 'since-reset',\n  limit: 10\n});\n\n// Query stats by domain\nconst domainStats = api.stats().query({\n  metrics: ['counters', 'error'],\n  groupBy: 'domain',\n  where: { method: 'POST' }\n});\n\n// Get rate metrics (requests per second/minute)\nconst rateStats = api.stats().rate.get();\nconsole.log(rateStats);\n// { rps: 2.5, rpm: 150, mode: 'ema-30s' }\n```\n\n### Available Metrics\n\n- **Counters**: `total`, `success`, `fail`, `inflight`, `retried`, `aborted`\n- **Time**: `minMs`, `avgMs`, `p50Ms`, `p95Ms`, `p99Ms`, `maxMs` \n- **Rate**: `rps` (requests/sec), `rpm` (requests/min), `mode`\n- **Retry**: `count`, `giveups`, `avgBackoffMs`, `successAfterAvg`\n- **Error**: `byClass` (timeout, network, 4xx, 5xx), `topCodes`\n\n### Grouping \u0026 Filtering\n\n```js\n// Group by method, filter by domain\nconst methodStats = api.stats().query({\n  metrics: ['counters'],\n  groupBy: 'method',\n  where: { domain: 'api.example.com' },\n  window: 'rolling-60s'\n});\n\n// Group by endpoint with filters\nconst filteredStats = api.stats().query({\n  metrics: ['time', 'error'],\n  groupBy: 'endpoint',\n  where: { \n    method: 'GET',\n    endpointPrefix: '/api/' \n  },\n  limit: 5\n});\n```\n\n### Reset \u0026 Snapshots\n\n```js\n// Reset all stats\napi.stats().reset();\n\n// Reset individual modules\napi.stats().counters.reset();\napi.stats().time.reset();\n\n// Take a snapshot (all metrics, point-in-time)\nconst snapshot = api.stats().snapshot();\nconsole.log(snapshot);\n// { timestamp: \"2025-11-04T...\", window: \"since-start\", groups: [...] }\n```\n\n### Disable/Enable Stats\n\n```js\n// Disable stats for performance-critical apps\nconst api = createLuminara({\n  baseURL: \"https://api.example.com\",\n  statsEnabled: false\n});\n\n// Check if stats are enabled\nconsole.log(api.isStatsEnabled()); // false\n\n// When disabled, stats methods return safe defaults\nconst counters = api.stats().counters.get(); // Returns zero counters\n```\n\n---\n\n## 🎨 Interactive Sandbox\n\nLuminara includes a **beautiful interactive sandbox** where you can explore all features with live examples!\n\n🌐 **[Try the Sandbox](./sandbox/)** • [Sandbox Documentation](./sandbox/README.md) • [Architecture Guide](./sandbox/ARCHITECTURE.md)\n\nThe sandbox features:\n- **86 Interactive Examples** across 16 feature categories\n- **Live Retry Logging** - Watch backoff strategies in action\n- **Individual Test Controls** - Run and stop tests independently\n- **Real-time Feedback** - Color-coded outputs with detailed logs\n- **Clean Architecture** - Demonstrates separation of concerns principles\n\n### Sandbox Categories:\n\n1. 📦 **Basic Usage** - GET/POST JSON, Text, Form data\n2. 🔗 **Base URL \u0026 Query Parameters** - URL configuration  \n3. ⏱️ **Timeout** - Success and failure scenarios\n4. 🔄 **Retry** - Basic retry with status codes\n5. 📈 **Backoff Strategies** - All 6 strategies with live visualization\n6. 🏎️ **Request Hedging** - Race policy, cancel-and-retry, server rotation\n7. 🔌 **Interceptors** - Request/response/error interceptors\n8. 🛡️ **Error Handling** - Comprehensive error scenarios\n9. 🎯 **Response Types** - JSON, text, form, binary data handling\n10. 📊 **Stats System** - Real-time metrics and analytics\n11. 📝 **Verbose Logging** - Detailed debugging and tracing\n12. 🚗 **Custom Drivers** - Replace the HTTP backend\n13. 🚦 **Rate Limiting** - Token bucket algorithm examples\n14. ⏱️ **Debouncer** - Search debouncing, button spam protection, method filtering\n15. 🔁 **Request Deduplicator** - Automatic duplicate prevention, key strategies, TTL\n16. 🍪 **Cookie Jar Plugin** - Server-side cookie management\n\n**Quick Start:**\n```bash\n# Run the sandbox locally\nnpx serve .\n# Open http://localhost:3000/sandbox/\n```\n\n---\n\n## 🌈 Framework Examples\n\n### React\n```jsx\nimport { useEffect, useState } from \"react\";\nimport { createLuminara } from \"luminara\";\n\nconst api = createLuminara({\n  baseURL: \"https://api.example.com\",\n  retry: 3,\n  retryDelay: 1000,\n  backoffType: \"exponential\"\n});\n\n// Add global error handling\napi.use({\n  onError(error) {\n    console.error(\"API Error:\", error.message);\n  }\n});\n\nexport default function UsersList() {\n  const [users, setUsers] = useState([]);\n  const [loading, setLoading] = useState(true);\n\n  useEffect(() =\u003e {\n    api.getJson(\"/users\")\n      .then(res =\u003e {\n        setUsers(res.data);\n        setLoading(false);\n      })\n      .catch(err =\u003e {\n        console.error(err);\n        setLoading(false);\n      });\n  }, []);\n\n  if (loading) return \u003cdiv\u003eLoading...\u003c/div\u003e;\n\n  return (\n    \u003cul\u003e\n      {users.map(user =\u003e (\n        \u003cli key={user.id}\u003e{user.name}\u003c/li\u003e\n      ))}\n    \u003c/ul\u003e\n  );\n}\n}\n```\n\n### Vue 3 (Composition API)\n```vue\n\u003cscript setup\u003e\nimport { ref, onMounted } from 'vue';\nimport { createLuminara } from 'luminara';\n\nconst api = createLuminara({\n  baseURL: 'https://api.example.com',\n  retry: 3,\n  backoffType: 'exponential'\n});\n\nconst users = ref([]);\nconst loading = ref(true);\n\nonMounted(async () =\u003e {\n  try {\n    const response = await api.getJson('/users');\n    users.value = response.data;\n  } catch (error) {\n    console.error('Failed to fetch users:', error);\n  } finally {\n    loading.value = false;\n  }\n});\n\u003c/script\u003e\n\n\u003ctemplate\u003e\n  \u003cdiv\u003e\n    \u003cdiv v-if=\"loading\"\u003eLoading...\u003c/div\u003e\n    \u003cul v-else\u003e\n      \u003cli v-for=\"user in users\" :key=\"user.id\"\u003e\n        {{ user.name }}\n      \u003c/li\u003e\n    \u003c/ul\u003e\n  \u003c/div\u003e\n\u003c/template\u003e\n```\n\n### Angular\n```typescript\nimport { Component, OnInit } from '@angular/core';\nimport { createLuminara } from 'luminara';\n\n@Component({\n  selector: 'app-users',\n  template: `\n    \u003cdiv *ngIf=\"loading\"\u003eLoading...\u003c/div\u003e\n    \u003cul *ngIf=\"!loading\"\u003e\n      \u003cli *ngFor=\"let user of users\"\u003e{{ user.name }}\u003c/li\u003e\n    \u003c/ul\u003e\n  `\n})\nexport class UsersComponent implements OnInit {\n  users: any[] = [];\n  loading = true;\n  \n  private api = createLuminara({\n    baseURL: 'https://api.example.com',\n    retry: 3,\n    backoffType: 'exponential'\n  });\n\n  async ngOnInit() {\n    try {\n      const response = await this.api.getJson('/users');\n      this.users = response.data;\n    } catch (error) {\n      console.error('Failed to fetch users:', error);\n    } finally {\n      this.loading = false;\n    }\n  }\n}\n```\n\n### Pure JavaScript (No Frameworks)\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead\u003e\n  \u003ctitle\u003eLuminara Example\u003c/title\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n  \u003cdiv id=\"app\"\u003e\n    \u003cdiv id=\"loading\"\u003eLoading...\u003c/div\u003e\n    \u003cul id=\"users\" style=\"display: none;\"\u003e\u003c/ul\u003e\n  \u003c/div\u003e\n\n  \u003cscript type=\"module\"\u003e\n    import { createLuminara } from 'https://cdn.skypack.dev/luminara';\n\n    const api = createLuminara({\n      baseURL: 'https://api.example.com',\n      retry: 3,\n      backoffType: 'exponential'\n    });\n\n    async function loadUsers() {\n      try {\n        const response = await api.getJson('/users');\n        \n        const loadingEl = document.getElementById('loading');\n        const usersEl = document.getElementById('users');\n        \n        loadingEl.style.display = 'none';\n        usersEl.style.display = 'block';\n        \n        response.data.forEach(user =\u003e {\n          const li = document.createElement('li');\n          li.textContent = user.name;\n          usersEl.appendChild(li);\n        });\n      } catch (error) {\n        console.error('Failed to fetch users:', error);\n      }\n    }\n\n    loadUsers();\n  \u003c/script\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n---\n\n## 🌐 Framework Compatibility\n\nLuminara is designed to be **completely framework-agnostic** and works seamlessly across all modern JavaScript environments:\n\n| Framework | Compatibility | Example |\n|-----------|---------------|---------|\n| **React** | ✅ Full Support | `useEffect(() =\u003e { api.getJson('/data') }, [])` |\n| **Vue 3** | ✅ Full Support | `onMounted(() =\u003e api.getJson('/data'))` |\n| **Angular** | ✅ Full Support | `ngOnInit() { api.getJson('/data') }` |\n| **Svelte** | ✅ Full Support | `onMount(() =\u003e api.getJson('/data'))` |\n| **Pure JavaScript** | ✅ Full Support | `api.getJson('/data').then(...)` |\n| **Next.js** | ✅ Full Support | Client-side data fetching |\n| **Nuxt.js** | ✅ Full Support | Client-side data fetching |\n| **Vite** | ✅ Full Support | All frameworks via Vite |\n| **Webpack** | ✅ Full Support | All bundled applications |\n\n### Browser Support\n- ✅ Chrome 88+\n- ✅ Firefox 90+\n- ✅ Safari 14+\n- ✅ Edge 88+\n- ✅ Mobile browsers (iOS Safari, Chrome Mobile)\n\n### Node.js Support\n- ✅ Node.js 18.x (LTS - Maintenance)\n- ✅ Node.js 20.x (LTS - Active)\n- ✅ Node.js 22.x (LTS - Current)\n\n### Runtime Requirements\n- **Universal**: Works in browsers and Node.js 18+\n- Modern `fetch` API support (native in all supported environments)\n- ES2020+ JavaScript features\n- ES Modules support\n\n---\n\n### Build System\n- **Dual Exports**: Automatic ESM/CJS format support\n- **Auto-Build**: `npm run build` or `npm run dev` (watch mode)\n- **TypeScript Support**: Generated type definitions\n- **Universal Compatibility**: Works across all JavaScript environments\n\n---\n\n## ⚡ Performance \u0026 Benchmarks\n\nLuminara includes a **comprehensive benchmark suite** validated across Node.js and browsers — from micro-operations to full end-to-end request flows.\n\n### Benchmark Suite Features\n\n- **68 Node.js Benchmarks** - High-precision measurements with memory profiling (Tinybench 2.9.0)\n- **18 Browser Benchmarks** - Automated headless testing across Chromium, Firefox, and WebKit\n- **Interactive Browser UI** - Real-time testing with Chart.js visualizations\n- **Historical Tracking** - Performance regression detection and baseline comparison\n- **Beautiful Reports** - HTML reports with charts, trends, and statistical analysis\n\n### Performance Characteristics (Latest Results)\n\n| Layer | Node.js (Mean) | Browser (Typical) | Verdict |\n|-------|----------------|-------------------|---------|\n| Core API | 0.15–7.5 µs | 5–30 µs | ⚡ Ideal (microsecond precision) |\n| Plugin Orchestration | 26–120 µs | same magnitude | ✅ Excellent (linear scaling) |\n| Driver Layer | 0.09–60 µs | same magnitude | ✅ Excellent (minimal overhead) |\n| Fetch Roundtrip (local mock) | 2–4 ms | 3–25 ms | ⚙️ I/O-bound (network dominates) |\n| Feature Utilities | 2–6 ms | 10–25 ms | ✅ Expected (sub-ms overhead) |\n| Integrated Scenarios | 2.3–27 ms | similar envelope | 🪶 Balanced (near-zero architectural tax) |\n\n### Running Benchmarks\n\n```bash\n# Node.js benchmarks (all categories)\ncd benchmark/node\nnpm run benchmark               # Full suite (68 benchmarks)\n\n# Specific categories\nnpm run benchmark:core          # Core API (createLuminara, use, updateConfig)\nnpm run benchmark:orchestration # Plugin pipeline, context, signals\nnpm run benchmark:driver        # Pre-flight, in-flight, post-flight\nnpm run benchmark:features      # Retry, stats, rate-limit, hedging\nnpm run benchmark:integrated    # End-to-end scenarios\n\n# Browser benchmarks (interactive)\ncd benchmark/browser\nnpm run dev                     # Interactive UI with Chart.js\n\n# Headless cross-browser testing\ncd benchmark/headless\nnpm run benchmark               # Full suite (Chromium, Firefox, WebKit)\nnpm run benchmark:quick         # Quick test (Chromium only)\n\n# Generate reports\ncd benchmark\nnpm run benchmark:report        # HTML report with charts\n```\n\n### Key Findings (Node.js v22.14.0)\n\n✅ **Microsecond-Scale Core** - Client creation ~7.5µs, plugin registration ~0.16µs, config updates ~0.74µs  \n✅ **Linear Plugin Scaling** - 10 plugins add only ~120µs overhead (vs ~30µs for empty pipeline)  \n✅ **Sub-Millisecond Orchestration** - Full pipeline execution stays under 0.2ms even with 10 plugins  \n✅ **Network Parity** - Single request ~2.35ms (bare), ~2.67ms (all features ON) — **0.32ms overhead**  \n✅ **Predictable Concurrency** - 10 concurrent requests: 25ms / 50 concurrent: 130ms (event-loop bound)  \n✅ **Production Validated** - 68 benchmarks, millions of iterations, tight percentiles (P99 ≤ 4× mean)\n\n📊 **[View Detailed Performance Analysis](./docs/performance.md)**\n\n---\n\n## 📚 Documentation\n\n- **[Sandbox Guide](./sandbox/README.md)** - Interactive examples and usage\n- **[Command Line Tests Guide](./test-cli/README.md)** - Cli test usage\n- **[Benchmark Suite Guide](./benchmark/README.md)** - Benchmark usage and configuration\n- **[Performance Benchmarks](./docs/performance.md)** - Detailed performance analysis\n\n---\n\n## 🧠 License\n\nMIT © 2025 [Jonathan Miller](mailto:jonathan@miller28.com) • [LinkedIn](https://www.linkedin.com/in/miller28/)\n\n---\n\n## 🪐 Philosophy\n\n**Luminara** — derived from \"lumen\" (light) — symbolizes clarity and adaptability.\n\nLike light traveling through space, Luminara guides your HTTP requests with grace, reliability, and cosmic precision across all JavaScript environments. Built with mindfulness for developers who craft with intention.\n\n**Framework-Agnostic** • **Simple by Design** • **Separation of Concerns** • **Developer-Friendly** • **Extensible**\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmiller-28%2Fluminara","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmiller-28%2Fluminara","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmiller-28%2Fluminara/lists"}