{"id":28383248,"url":"https://github.com/codedynasty-dev/jetflare","last_synced_at":"2026-03-05T23:34:19.487Z","repository":{"id":294869287,"uuid":"988400285","full_name":"CodeDynasty-dev/Jetflare","owner":"CodeDynasty-dev","description":"A type-safe API client for web applications.","archived":false,"fork":false,"pushed_at":"2025-07-07T13:27:46.000Z","size":59,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-26T23:22:44.919Z","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":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/CodeDynasty-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","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-05-22T13:46:25.000Z","updated_at":"2025-10-19T09:45:21.000Z","dependencies_parsed_at":"2025-05-22T15:06:29.844Z","dependency_job_id":"8cdd7432-b5c4-4e26-bf56-117c59ac2c7c","html_url":"https://github.com/CodeDynasty-dev/Jetflare","commit_stats":null,"previous_names":["codedynasty-dev/jetflare","fridaycandour/jetflare"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/CodeDynasty-dev/Jetflare","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CodeDynasty-dev%2FJetflare","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CodeDynasty-dev%2FJetflare/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CodeDynasty-dev%2FJetflare/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CodeDynasty-dev%2FJetflare/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/CodeDynasty-dev","download_url":"https://codeload.github.com/CodeDynasty-dev/Jetflare/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CodeDynasty-dev%2FJetflare/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30155361,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-05T22:39:40.138Z","status":"ssl_error","status_checked_at":"2026-03-05T22:39:24.771Z","response_time":93,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: 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":[],"created_at":"2025-05-30T05:12:31.399Z","updated_at":"2026-03-05T23:34:19.464Z","avatar_url":"https://github.com/CodeDynasty-dev.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Jetflare\n\n[![npm](https://img.shields.io/npm/v/jetflare.svg)](https://www.npmjs.com/package/jetflare)\n[![npm](https://img.shields.io/npm/dm/jetflare)](https://npm-stat.com/charts.html?package=jetflare)\n[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n\nJetflare is a super lightweight, type-safe HTTP client for TypeScript and JavaScript, featuring:\n\n- 🚀 **Type-Safe API** - Full TypeScript support with automatic type inference\n- ⚡ **Real-Time Ready** - Built-in WebSocket and SSE support\n- 🔄 **Smart Caching** - Automatic response caching and invalidation\n- 🛠 **Developer Experience** - IntelliSense for API routes and responses\n\n```typescript\n// Example: Type-safe API client\nconst api = Jetflare(\"https://api.example.com\", {\n  GET_user: {\n    path: \"/users/:id\",\n    method: \"get\",\n    response: { id: \"string\", name: \"string\" },\n  },\n});\n\n// TypeScript knows the response shape\nconst { data: user } = await api.GET_user({ params: { id: \"123\" } });\nconsole.log(user.name); // Type-safe property access\n```\n\n## Table of Contents\n\n- [Installation](#installation)\n- [Quick Start](#quick-start)\n- [Core Concepts](#core-concepts)\n  - [Defining API Routes](#defining-api-routes)\n  - [Making Requests](#making-requests)\n  - [Type Safety](#type-safety)\n- [Guides](#guides)\n  - [REST API](#rest-api)\n  - [Real-time](#real-time)\n  - [Advanced Usage](#advanced-usage)\n- [API Reference](#api-reference)\n- [Contributing](#contributing)\n- [License](#license)\n\n## Installation\n\n```bash\n# Using npm\nnpm install jetflare\n\n# Using yarn\nyarn add jetflare\n\n# Using pnpm\npnpm add jetflare\n```\n\n## Quick Start\n\n1. **Define your API routes**\n\n```typescript\nimport { Jetflare } from \"jetflare\";\n\nconst api = Jetflare(\"https://api.example.com\", {\n  // GET /users\n  GET_users: {\n    path: \"/users\",\n    method: \"get\",\n    response: {\n      users: [\n        {\n          id: \"string\",\n          name: \"string\",\n          email: \"string\",\n        },\n      ],\n    },\n  },\n\n  // POST /users\n  POST_user: {\n    path: \"/users\",\n    method: \"post\",\n    body: {\n      name: \"string\",\n      email: \"string\",\n    },\n    response: {\n      id: \"string\",\n      name: \"string\",\n      email: \"string\",\n    },\n  },\n});\n```\n\n2. **Make type-safe requests**\n\n```typescript\n// GET /users\nconst { data } = await api.GET_users();\n// data is typed as { users: Array\u003c{ id: string, name: string, email: string }\u003e }\n\n// POST /users\nconst newUser = await api.POST_user({\n  body: { name: \"John\", email: \"john@example.com\" },\n});\n// newUser.data is typed as { id: string, name: string, email: string }\n```\n\n## API Reference\n\n### createRoutes\n\n```typescript\nimport { createRoutes } from \"jetflare\";\n\nconst routes = createRoutes({\n  // Define your API routes here\n});\n```\n\n### Jetflare\n\nGET_product_by_id: {\npath: \"/products/:id\",\nmethod: \"get\",\nparams: {\nid: \"string\", // Example: string type for path parameter\n},\ntitle: \"Get product by ID\",\n},\n\n// POST request with a request body, invalidates 'GET_products' cache on success\nCREATE_product: {\npath: \"/products\",\nmethod: \"post\",\nbody: {\nname: \"string\",\nprice: 0,\ndescription: \"string\",\n},\ninvalidates: [\"GET_products\"], // Invalidate cache for 'GET_products'\ntitle: \"Create a new product\",\n},\n\n// PUT request with path parameters and body, invalidates specific product cache\nUPDATE_product: {\npath: \"/products/:id\",\nmethod: \"put\",\nparams: {\nid: \"string\",\n},\nbody: {\nprice: 0,\n},\ninvalidates: [\"GET_products\", \"GET_product_by_id\"], // Invalidate multiple caches\ntitle: \"Update a product\",\n},\n\n// DELETE request with path parameters\nDELETE_product: {\npath: \"/products/:id\",\nmethod: \"delete\",\nparams: {\nid: \"string\",\n},\ninvalidates: [\"GET_products\"], // Invalidate 'GET_products' cache\ntitle: \"Delete a product\",\n},\n\n// Example for file upload (handled via payload.body containing File objects)\nUPLOAD_product_image: {\npath: \"/products/:id/image\",\nmethod: \"post\",\nparams: {\nid: \"string\",\n},\nbody: {\nimageFile: {} as File, // Explicitly define as File or File[]\naltText: \"string\",\n},\ntitle: \"Upload product image\",\n},\n});\n\n````\n\n### WebSocket Routes\n\nDefine a WebSocket route by setting the `method` to `\"websocket\"`.\n\n```typescript\nexport const routes = createRoutes({\n  WS_chat: {\n    path: \"/chat\",\n    method: \"websocket\",\n    title: \"Real-time chat\",\n  },\n});\n````\n\n### Server-Sent Events (SSE) Routes\n\nDefine an SSE route by setting the `method` to `\"sse\"`.\n\n```typescript\nexport const routes = createRoutes({\n  SSE_notifications: {\n    path: \"/notifications\",\n    method: \"sse\",\n    query: {\n      userId: \"string\",\n    },\n    title: \"Event stream for notifications\",\n  },\n});\n```\n\n## Making API Calls\n\nOnce your `jetflare` instance is initialized, you can call your API endpoints directly using the route names defined in your `routes` object.\n\n### GET Requests\n\n```typescript\n// Example: jetflare.GET_users defined with query: { limit: 0, isActive: false }\nconst usersResponse = await jetflare.GET_users({\n  query: {\n    limit: 10,\n    isActive: true,\n  },\n});\nconst usersData = await usersResponse.json();\nconsole.log(\"Users:\", usersData);\n\n// Example: jetflare.GET_product_by_id defined with params: { id: \"string\" }\nconst productResponse = await jetflare.GET_product_by_id({\n  params: { id: \"product_123\" },\n});\nconst productData = await productResponse.json();\nconsole.log(\"Product:\", productData);\n```\n\n### POST/PUT/PATCH Requests (with Body)\n\n```typescript\n// Example: jetflare.CREATE_product defined with body: { name: \"string\", price: 0 }\nconst createResponse = await jetflare.CREATE_product({\n  body: {\n    name: \"New Widget\",\n    price: 29.99,\n    description: \"A fantastic new product.\",\n  },\n});\nconst newProduct = await createResponse.json();\nconsole.log(\"Created product:\", newProduct);\n\n// Example: jetflare.UPDATE_product defined with params: { id: \"string\" }, body: { price: 0 }\nconst updateResponse = await jetflare.UPDATE_product({\n  params: { id: \"product_456\" },\n  body: {\n    price: 35.5,\n  },\n});\nconst updatedProduct = await updateResponse.json();\nconsole.log(\"Updated product:\", updatedProduct);\n```\n\n### DELETE Requests\n\n```typescript\n// Example: jetflare.DELETE_product defined with params: { id: \"string\" }\nconst deleteResponse = await jetflare.DELETE_product({\n  params: { id: \"product_789\" },\n});\nif (deleteResponse.ok) {\n  console.log(\"Product deleted successfully.\");\n}\n```\n\n### Request Configuration\n\nEach request accepts an optional `payload` object to configure behavior for that specific call:\n\n```typescript\ninterface ApiFunctionPayload {\n  body?: any; // Request body for POST, PUT, PATCH\n  query?: Record\u003cstring, any\u003e; // URL query parameters\n  params?: Record\u003cstring, any\u003e; // URL path parameters (e.g., :id)\n  headers?: Record\u003cstring, string\u003e; // Additional request headers\n  cache?: boolean | { ttl?: number; key?: string }; // Caching options\n  timeout?: number; // Request timeout in milliseconds\n  retry?: boolean | { attempts?: number; delay?: number }; // Retry configuration\n  onUploadProgress?: (progress: {\n    loaded: number;\n    total: number;\n    percentage: number;\n  }) =\u003e void; // Upload progress callback\n  onDownloadProgress?: (progress: {\n    loaded: number;\n    total: number;\n    percentage: number;\n  }) =\u003e void; // Download progress callback\n  signal?: AbortSignal; // For manual request cancellation\n}\n```\n\n### Response Handling\n\nJetflare returns an enhanced `JetResponse` object, which wraps the standard `Response` object and adds useful properties:\n\n```typescript\nconst response = await jetflare.GET_users();\n\n// Standard Response properties\nconsole.log(response.ok); // boolean: True if status is 200-299\nconsole.log(response.status); // number: HTTP status code\nconsole.log(response.statusText); // string: HTTP status message\nconsole.log(response.headers); // Headers object\nconsole.log(response.url); // string: Final URL of the response\n\n// Enhanced properties\nconsole.log(response.cached); // boolean: Was this response eligible for caching?\nconsole.log(response.fromCache); // boolean: Did this response come from the cache?\n\n// Response body methods (asynchronous)\nconst json = await response.json(); // Parses response as JSON\nconst text = await response.text(); // Parses response as plain text\nconst blob = await response.blob(); // Parses response as Blob\nconst buffer = await response.arrayBuffer(); // Parses response as ArrayBuffer\nconst formData = await response.formData(); // Parses response as FormData\n```\n\n## File Uploads\n\nJetflare simplifies file uploads by automatically converting `File` or `FileList` objects within your `payload.body` into `FormData`. Progress tracking is available via `onUploadProgress` and `onDownloadProgress`.\n\n```typescript\n// Example: jetflare.UPLOAD_product_image defined with body: { imageFile: {} as File, altText: \"string\" }\n\n// Get a single file from an input element\nconst fileInput = document.getElementById(\"imageFile\") as HTMLInputElement;\nconst selectedFile = fileInput.files?.[0];\n\nif (selectedFile) {\n  try {\n    const uploadResponse = await jetflare.UPLOAD_product_image({\n      params: { id: \"product_123\" },\n      body: {\n        imageFile: selectedFile, // Pass the File object directly\n        altText: \"Front view of the product\",\n      },\n      onUploadProgress: (progress) =\u003e {\n        console.log(`Upload Progress: ${progress.percentage}%`);\n        // Update a UI progress bar here\n      },\n      onDownloadProgress: (progress) =\u003e {\n        console.log(`Download Progress: ${progress.percentage}%`);\n      },\n    });\n\n    const uploadResult = await uploadResponse.json();\n    console.log(\"File upload successful:\", uploadResult);\n  } catch (error) {\n    console.error(\"File upload failed:\", error);\n  }\n}\n\n// Example for multiple files if your route body supports an array of Files\n// Route definition might look like:\n// UPLOAD_documents: { path: \"/documents\", method: \"post\", body: { files: [] as File[], category: \"string\" } }\nconst multipleFilesInput = document.getElementById(\n  \"multipleFiles\"\n) as HTMLInputElement;\nconst filesToUpload = multipleFilesInput.files\n  ? Array.from(multipleFilesInput.files)\n  : [];\n\nif (filesToUpload.length \u003e 0) {\n  try {\n    const multiUploadResponse = await jetflare.UPLOAD_documents({\n      body: {\n        files: filesToUpload, // Pass an array of File objects\n        category: \"manuals\",\n      },\n      onUploadProgress: (progress) =\u003e {\n        console.log(`Multi-file Upload Progress: ${progress.percentage}%`);\n      },\n    });\n    const multiUploadResult = await multiUploadResponse.json();\n    console.log(\"Multiple files upload successful:\", multiUploadResult);\n  } catch (error) {\n    console.error(\"Multiple files upload failed:\", error);\n  }\n}\n```\n\n### Accessing WebSocket Connections\n\nFor WebSocket routes, the generated function returns an object with methods to manage the connection, including direct access to the underlying `WebSocket` instance.\n\n```typescript\n// Example: jetflare.WS_chat defined with method: \"websocket\"\nconst chatService = jetflare.WS_chat();\n\n// Connect to the WebSocket\nchatService.connect();\n\n// Get the raw WebSocket instance for advanced usage\nconst rawSocket = chatService.socket(); // Access the WebSocket object directly\nif (rawSocket) {\n  rawSocket.onopen = () =\u003e console.log(\"WebSocket connected directly!\");\n  rawSocket.onmessage = (event) =\u003e\n    console.log(\"Direct WS message:\", event.data);\n}\n\n// Use Jetflare's convenience methods for event listening\nchatService.on(\"message\", (event) =\u003e {\n  console.log(\"Received message:\", JSON.parse(event.data));\n});\n\nchatService.on(\"open\", () =\u003e {\n  console.log(\"WebSocket connection opened!\");\n  chatService.send({ type: \"greeting\", message: \"Hello from Jetflare!\" });\n});\n\nchatService.on(\"close\", () =\u003e {\n  console.log(\"WebSocket connection closed.\");\n});\n\nchatService.on(\"error\", (error) =\u003e {\n  console.error(\"WebSocket error:\", error);\n});\n\n// Send data\nchatService.send(\"Plain text message\");\nchatService.send({ action: \"ping\", timestamp: Date.now() });\n\n// Close the connection\n// chatService.close();\n```\n\n### Accessing SSE Connections\n\nFor SSE routes, the generated function returns an object with methods to manage the connection, including direct access to the underlying `EventSource` instance.\n\n```typescript\n// Example: jetflare.SSE_notifications defined with method: \"sse\"\nconst notificationService = jetflare.SSE_notifications({\n  query: { userId: \"user_abc\" },\n});\n\n// Connect to the SSE stream\nnotificationService.connect();\n\n// Get the raw EventSource instance for advanced usage\nconst rawEventSource = notificationService.event(); // Access the EventSource object directly\nif (rawEventSource) {\n  rawEventSource.onopen = () =\u003e console.log(\"SSE connection opened directly!\");\n  rawEventSource.onerror = (error) =\u003e console.error(\"Direct SSE error:\", error);\n}\n\n// Use Jetflare's convenience methods for event listening\nnotificationService.on(\"message\", (event) =\u003e {\n  console.log(\"New message:\", event.data);\n});\n\nnotificationService.on(\"user-update\", (event) =\u003e {\n  console.log(\"User updated:\", JSON.parse(event.data));\n});\n\n// Close the connection\n// notificationService.close();\n```\n\n## Advanced Features\n\n### Request Payload Options\n\nAll HTTP requests can accept a `payload` object to customize individual request behavior:\n\n```typescript\nconst response = await jetflare.GET_users({\n  headers: { \"X-API-Key\": \"my-secret-key\" }, // Custom headers for this request\n  cache: { ttl: 60000, key: \"recent_users\" }, // Cache for 1 minute with a specific key\n  timeout: 8000, // Override global timeout to 8 seconds for this request\n  retry: { attempts: 3, delay: 500 }, // Retry up to 3 times with 500ms delay between attempts\n  // onUploadProgress and onDownloadProgress as shown in File Uploads section\n  // signal for manual cancellation (see Request Cancellation)\n});\n```\n\n### Caching System\n\nJetflare includes an intelligent caching system primarily for `GET` requests. It automatically caches responses and can invalidate related cache entries when mutation (POST, PUT, PATCH, DELETE) requests succeed.\n\n#### Cache Configuration\n\n```typescript\n// Define caching directly in route configuration (static cache key)\nexport const routes = createRoutes({\n  GET_users: {\n    path: \"/users\",\n    method: \"get\",\n    cache: { ttl: 300000, key: \"all-users-list\" }, // Cache for 5 minutes\n    title: \"Get all users with static cache\",\n  },\n  GET_user_by_id: {\n    path: \"/users/:id\",\n    method: \"get\",\n    // Dynamic cache key using path parameters for per-user caching\n    cache: { ttl: 60000, key: \"user-:id\" },\n    title: \"Get user by ID with dynamic cache\",\n  },\n});\n\n// Override cache behavior per-request\nconst responseA = await jetflare.GET_users({\n  cache: { ttl: 30000 }, // Cache this specific request for 30 seconds\n});\n\nconst responseB = await jetflare.GET_users({\n  cache: false, // Disable caching for this specific request\n});\n```\n\n#### Cache Invalidation\n\nDefine `invalidates` in your route configuration to automatically clear relevant cache entries upon successful mutation. Supports specific keys and patterns.\n\n```typescript\nexport const routes = createRoutes({\n  // ...\n  POST_user: {\n    path: \"/users\",\n    method: \"post\",\n    invalidates: [\"all-users-list\"], // Invalidate the 'all-users-list' cache\n    title: \"Create new user (invalidates user list cache)\",\n  },\n  PUT_user: {\n    path: \"/users/:id\",\n    method: \"put\",\n    // Invalidate the specific user and the overall list\n    invalidates: [\"user-:id\", \"all-users-list\"],\n    title: \"Update user (invalidates user-specific and list cache)\",\n  },\n});\n```\n\n#### Manual Cache Management\n\nYou can directly interact with the cache manager through `jetflare.cache`.\n\n```typescript\n// Clear all cached entries\njetflare.cache.clear();\n\n// Invalidate specific cache keys or patterns\njetflare.cache.invalidate(\"all-users-list\");\njetflare.cache.invalidate([\"user-123\", \"posts-*\"]); // Supports globs with '*'\n\n// Get a cached entry directly\nconst cachedUsers = jetflare.cache.get(\"all-users-list\");\n\n// Manually set a cache entry\njetflare.cache.set(\"custom-data\", { value: \"example\" }, 300000); // 5 minutes TTL\n```\n\n### Request Retries\n\nConfigure automatic retries for failed HTTP requests using the `retry` option in your payload.\n\n```typescript\nconst response = await jetflare.GET_users({\n  retry: {\n    attempts: 5, // Attempt the request up to 5 times (1 initial + 4 retries)\n    delay: 2000, // Wait 2 seconds between retry attempts\n  },\n  timeout: 5000, // Each attempt has its own 5-second timeout\n});\n```\n\nIf `retry` is set to `true`, it will default to 1 attempt and 1000ms delay.\n\n### Request Cancellation\n\nJetflare supports request cancellation using the standard `AbortController` API.\n\n```typescript\nconst controller = new AbortController();\n\nasync function fetchWithCancellation() {\n  try {\n    const usersPromise = jetflare.GET_users({ signal: controller.signal });\n\n    // Simulate cancelling after some time\n    setTimeout(() =\u003e {\n      console.log(\"Aborting request...\");\n      controller.abort();\n    }, 100);\n\n    const response = await usersPromise;\n    console.log(\"Request completed:\", await response.json());\n  } catch (error) {\n    if (error.name === \"AbortError\") {\n      console.log(\"Request successfully aborted.\");\n    } else {\n      console.error(\"Request failed:\", error);\n    }\n  }\n}\n\nfetchWithCancellation();\n```\n\n### Interceptors\n\nInterceptors allow you to hook into the request and response lifecycle globally to perform actions like logging, authentication, or error handling.\n\n#### Request Interceptors\n\nExecuted before a request is sent. They receive the `payload` configuration and can modify it.\n\n```typescript\n// Add authentication token to all requests\njetflare.interceptors.request.add((config) =\u003e {\n  const token = localStorage.getItem(\"authToken\");\n  if (token) {\n    config.headers = {\n      ...config.headers,\n      Authorization: `Bearer ${token}`,\n    };\n  }\n  return config; // Always return the config object\n});\n\n// Add request logging\njetflare.interceptors.request.add((config) =\u003e {\n  console.log(\n    `Sending request: ${config.route.method.toUpperCase()} ${config.route.path}`\n  );\n  return config;\n});\n```\n\n#### Response Interceptors\n\nExecuted after a response is received (for both success and error status codes). They receive the `JetResponse` object and can modify it.\n\n```typescript\n// Log response status\njetflare.interceptors.response.add((response) =\u003e {\n  console.log(`Received response: ${response.status} ${response.statusText}`);\n  return response; // Always return the response object\n});\n\n// Handle global refresh tokens (if needed)\njetflare.interceptors.response.add(async (response) =\u003e {\n  if (response.status === 401 \u0026\u0026 !response.url.includes(\"/refresh-token\")) {\n    // Logic to refresh token, then potentially retry the original request\n    console.warn(\"Authentication failed, attempting token refresh...\");\n    // ... call a refresh token endpoint ...\n    // Note: Retrying original request within interceptor requires careful handling\n  }\n  return response;\n});\n```\n\n#### Error Interceptors\n\nExecuted when a request fails (e.g., network error, timeout, or uncaught HTTP error). They receive the `Error` object.\n\n```typescript\n// Global error logging and user notification\njetflare.interceptors.error.add((error) =\u003e {\n  console.error(\"Jetflare API Error:\", error.message);\n  // Example: Display a toast notification\n  // showToast(\"An API error occurred: \" + error.message, \"error\");\n  return error; // Always re-throw or return the error\n});\n```\n\n## Configuration\n\n### Global Configuration\n\nYou can set default headers, timeouts, and the base URL globally for all requests made by your Jetflare instance.\n\n```typescript\n// Set default headers for all HTTP requests\njetflare.setDefaultHeaders({\n  \"Content-Type\": \"application/json\",\n  \"X-Client-ID\": \"my-app-v1\",\n});\n\n// Set a global timeout for all requests (e.g., 15 seconds)\njetflare.setDefaultTimeout(15000);\n\n// Update the base URL dynamically\njetflare.setBaseURL(\"https://new-api.example.com/v2\");\n```\n\n### Fluent API\n\nJetflare also provides a fluent API for chaining common configuration methods during initialization or later.\n\n```typescript\nimport { Jetflare, createRoutes } from \"jetflare\";\n\nconst baseRoutes = createRoutes({\n  /* ... your routes ... */\n});\n\nconst jetflare = Jetflare(\"https://api.example.com\", baseRoutes);\n```\n\n## Error Handling\n\nJetflare handles network errors, timeouts, and non-2xx HTTP responses by throwing errors. You should typically wrap your API calls in `try...catch` blocks.\n\n```typescript\ntry {\n  const response = await jetflare.GET_users({ timeout: 2000 }); // Example with timeout\n  if (!response.ok) {\n    // Handle specific HTTP error responses (e.g., 404, 500)\n    const errorData = await response.json().catch(() =\u003e null);\n    throw new Error(\n      `API Error: ${response.status} ${response.statusText} - ${\n        errorData?.message || \"Unknown error\"\n      }`\n    );\n  }\n  const data = await response.json();\n  console.log(\"Data fetched:\", data);\n} catch (error) {\n  // Catch network errors, timeouts (AbortError), and custom thrown errors\n  if (error instanceof Error) {\n    if (error.name === \"AbortError\") {\n      console.error(\n        \"Request aborted (timeout or manual cancellation):\",\n        error.message\n      );\n    } else {\n      console.error(\"Failed to fetch data:\", error.message);\n    }\n  } else {\n    console.error(\"An unexpected error occurred:\", error);\n  }\n}\n```\n\nFor global error handling, refer to the [Error Interceptors](https://www.google.com/search?q=%23error-interceptors) section.\n\n## API Reference\n\n### `Jetflare(origin: string, routes: routesType): API\u003croutes\u003e`\n\nInitializes the Jetflare client.\n\n- `origin`: The base URL for your API (e.g., \"https://www.google.com/url?sa=E\\\u0026source=gmail\\\u0026q=https://api.example.com\").\n- `routes`: An object defining your API endpoints.\n\n### `API\u003croutes\u003e` Instance Properties\n\n- `origin: string`: The current base URL of the API client.\n- `cache: CacheManager`: Instance of the cache manager for manual cache operations.\n- `wsManager: WebSocketManager`: Instance of the WebSocket manager for direct WebSocket control.\n- `interceptors`:\n  - `request: Set\u003c(config: any) =\u003e any\u003e`: Add functions to intercept and modify outgoing request configurations.\n  - `response: Set\u003c(response: JetResponse) =\u003e JetResponse\u003e`: Add functions to intercept and modify incoming responses.\n  - `error: Set\u003c(error: Error) =\u003e Error\u003e`: Add functions to intercept and handle errors.\n\n### `API\u003croutes\u003e` Instance Methods\n\n- `setBaseURL(url: string): void`: Sets a new base URL for the API client.\n- `setDefaultHeaders(headers: Record\u003cstring, string\u003e): void`: Sets/merges default headers for all subsequent requests.\n- `setDefaultTimeout(timeout: number): void`: Sets a global timeout in milliseconds for all requests.\n- `withAuth(token: string): API\u003croutes\u003e`: A fluent method to set a Bearer token in default headers.\n- `withTimeout(timeout: number): API\u003croutes\u003e`: A fluent method to set the default timeout.\n- `withBaseURL(url: string): API\u003croutes\u003e`: A fluent method to set the base URL.\n\n### `CacheManager`\n\n- `get(key: string): any | null`: Retrieves data from the cache by key.\n- `set(key: string, data: any, ttl?: number): void`: Stores data in the cache with an optional time-to-live (TTL) in milliseconds (defaults to 5 minutes).\n- `invalidate(pattern: string | string[]): void`: Invalidates cache entries matching a key or pattern(s). Supports `*` for wildcards.\n- `clear(): void`: Clears all entries from the cache.\n\n### `JetResponse` Object\n\nThe response object returned by HTTP API calls.\n\n```typescript\ninterface JetResponse {\n  ok: boolean; // True if HTTP status is 200-299\n  status: number; // HTTP status code (e.g., 200, 404, 500)\n  statusText: string; // HTTP status message (e.g., \"OK\", \"Not Found\")\n  headers: Headers; // Standard Headers object\n  url: string; // The URL of the response\n  cached: boolean; // True if the response was eligible for caching\n  fromCache: boolean; // True if the response was served from cache\n\n  json\u003cT\u003e(): Promise\u003cT\u003e; // Parses the response body as JSON\n  text(): Promise\u003cstring\u003e; // Parses the response body as plain text\n  blob(): Promise\u003cBlob\u003e; // Parses the response body as a Blob\n  arrayBuffer(): Promise\u003cArrayBuffer\u003e; // Parses the response body as an ArrayBuffer\n  formData(): Promise\u003cFormData\u003e; // Parses the response body as FormData\n}\n```\n\n### `ApiFunctionPayload`\n\nThe configuration object passed to individual HTTP API functions.\n\n```typescript\ninterface ApiFunctionPayload {\n  body?: any; // Request body for POST, PUT, PATCH. Supports objects (JSON) or FormData for files.\n  query?: Record\u003cstring, any\u003e; // URL query parameters (e.g., ?limit=10)\n  params?: Record\u003cstring, any\u003e; // URL path parameters (e.g., /users/:id -\u003e { id: '123' })\n  headers?: Record\u003cstring, string\u003e; // Additional headers for this specific request\n  cache?: boolean | { ttl?: number; key?: string }; // Override caching behavior for this request\n  timeout?: number; // Override global timeout for this request in milliseconds\n  retry?: boolean | { attempts?: number; delay?: number }; // Configure retries for this request\n  onUploadProgress?: (progress: {\n    // Callback for tracking upload progress\n    loaded: number;\n    total: number;\n    percentage: number;\n  }) =\u003e void;\n  onDownloadProgress?: (progress: {\n    // Callback for tracking download progress\n    loaded: number;\n    total: number;\n    percentage: number;\n  }) =\u003e void;\n  signal?: AbortSignal; // An AbortSignal to manually cancel the request\n}\n```\n\n## Examples\n\nFor more comprehensive examples, see the [Examples directory](https://www.google.com/search?q=https://github.com/codedynasty-dev/jetflare/tree/main/examples) in the GitHub repository.\n\n### Complete User Management Example\n\nThis example demonstrates integrating multiple Jetflare features for a user management service.\n\n```typescript\nimport { Jetflare, createRoutes } from \"jetflare\";\n\n// Define user-related API routes\nconst userRoutes = createRoutes({\n  GET_all_users: {\n    path: \"/users\",\n    method: \"get\",\n    cache: { ttl: 300000, key: \"all-users-list\" }, // Cache for 5 mins\n    title: \"Retrieve all users\",\n  },\n  GET_user_by_id: {\n    path: \"/users/:id\",\n    method: \"get\",\n    params: { id: \"string\" },\n    cache: { ttl: 60000, key: \"user-:id\" }, // Cache per user for 1 min\n    title: \"Retrieve user by ID\",\n  },\n  POST_new_user: {\n    path: \"/users\",\n    method: \"post\",\n    body: { name: \"string\", email: \"string\", password: \"string\" },\n    invalidates: [\"all-users-list\"], // Invalidate user list cache\n    title: \"Create a new user\",\n  },\n  PUT_update_user: {\n    path: \"/users/:id\",\n    method: \"put\",\n    params: { id: \"string\" },\n    body: { name: \"string\", email: \"string\" },\n    invalidates: [\"all-users-list\", \"user-:id\"], // Invalidate user list and specific user cache\n    title: \"Update an existing user\",\n  },\n  DELETE_user: {\n    path: \"/users/:id\",\n    method: \"delete\",\n    params: { id: \"string\" },\n    invalidates: [\"all-users-list\", \"user-:id\"], // Invalidate user list and specific user cache\n    title: \"Delete a user\",\n  },\n  UPLOAD_user_avatar: {\n    path: \"/users/:id/avatar\",\n    method: \"post\",\n    params: { id: \"string\" },\n    body: { avatar: {} as File, description: \"string\" }, // File upload with additional data\n    invalidates: [\"user-:id\"], // Invalidate specific user cache\n    title: \"Upload user avatar\",\n  },\n});\n\n// Initialize Jetflare instance with authentication and a global timeout\nconst jetflareApi = Jetflare(\n  \"[https://api.yourapp.com](https://api.yourapp.com)\",\n  userRoutes\n)\n  .withAuth(\"your-static-jwt-token-here\") // Or fetch dynamically and set via interceptor\n  .withTimeout(20000); // 20 seconds global timeout\n\n// Define a service class for user-related operations\nclass UserService {\n  async getAllUsers(page: number = 1, limit: number = 10) {\n    const response = await jetflareApi.GET_all_users({\n      query: { page, limit },\n    });\n    return response.json();\n  }\n\n  async getUserById(id: string) {\n    const response = await jetflareApi.GET_user_by_id({ params: { id } });\n    return response.json();\n  }\n\n  async createUser(data: { name: string; email: string; password?: string }) {\n    const response = await jetflareApi.POST_new_user({ body: data });\n    return response.json();\n  }\n\n  async updateUser(id: string, updates: { name?: string; email?: string }) {\n    const response = await jetflareApi.PUT_update_user({\n      params: { id },\n      body: updates,\n    });\n    return response.json();\n  }\n\n  async deleteUser(id: string) {\n    const response = await jetflareApi.DELETE_user({ params: { id } });\n    if (!response.ok) {\n      throw new Error(`Failed to delete user: ${response.statusText}`);\n    }\n    return response.ok;\n  }\n\n  async uploadUserAvatar(\n    userId: string,\n    avatarFile: File,\n    description: string,\n    onProgress?: (p: {\n      loaded: number;\n      total: number;\n      percentage: number;\n    }) =\u003e void\n  ) {\n    const response = await jetflareApi.UPLOAD_user_avatar({\n      params: { id: userId },\n      body: { avatar: avatarFile, description },\n      onUploadProgress: onProgress, // Pass the progress callback\n    });\n    return response.json();\n  }\n}\n\nexport const userService = new UserService();\n\n// Example Usage (in a React component or utility)\nasync function runUserExamples() {\n  try {\n    // Fetch all users\n    const users = await userService.getAllUsers(1, 5);\n    console.log(\"Fetched users:\", users);\n\n    // Fetch a specific user\n    const singleUser = await userService.getUserById(\"user-abc-123\");\n    console.log(\"Fetched single user:\", singleUser);\n\n    // Create a new user\n    const newUser = await userService.createUser({\n      name: \"Test User\",\n      email: \"test@example.com\",\n      password: \"securepassword\",\n    });\n    console.log(\"Created user:\", newUser);\n\n    // Update a user\n    const updatedUser = await userService.updateUser(newUser.id, {\n      email: \"new.test@example.com\",\n    });\n    console.log(\"Updated user:\", updatedUser);\n\n    // Upload an avatar for a user\n    const dummyFile = new File([\"dummy content\"], \"avatar.png\", {\n      type: \"image/png\",\n    });\n    const uploadResult = await userService.uploadUserAvatar(\n      newUser.id,\n      dummyFile,\n      \"Profile picture\",\n      (p) =\u003e {\n        console.log(`Avatar upload: ${p.percentage}%`);\n      }\n    );\n    console.log(\"Avatar upload result:\", uploadResult);\n\n    // Delete a user\n    const deleted = await userService.deleteUser(newUser.id);\n    console.log(\"User deleted:\", deleted);\n  } catch (error) {\n    console.error(\"User service example failed:\", error);\n  }\n}\n\nrunUserExamples();\n```\n\n### Real-time Chat Example\n\nDemonstrates how to use WebSockets for a real-time chat application.\n\n```typescript\nimport { Jetflare, createRoutes } from \"jetflare\";\n\nconst chatRoutes = createRoutes({\n  WS_chat_room: {\n    path: \"/chat/:roomId\",\n    method: \"websocket\",\n    title: \"Real-time chat room connection\",\n  },\n  POST_chat_message: {\n    path: \"/chat/:roomId/messages\",\n    method: \"post\",\n    params: { roomId: \"string\" },\n    body: { message: \"string\", senderId: \"string\" },\n    title: \"Send chat message (via REST for history)\",\n  },\n});\n\nconst jetflareChat = Jetflare(\"ws://localhost:8080\", chatRoutes); // Use ws:// for WebSocket\n\nclass ChatService {\n  private wsConnection: ReturnType\u003ctypeof jetflareChat.WS_chat_room\u003e | null =\n    null;\n  private currentRoomId: string | null = null;\n\n  joinRoom(roomId: string, onMessageReceived: (message: any) =\u003e void) {\n    if (this.wsConnection \u0026\u0026 this.currentRoomId === roomId) {\n      console.log(`Already connected to room ${roomId}`);\n      return;\n    }\n\n    this.leaveRoom(); // Close any existing connection\n\n    this.currentRoomId = roomId;\n    this.wsConnection = jetflareChat.WS_chat_room({ params: { roomId } });\n\n    // Connect to the WebSocket\n    this.wsConnection.connect();\n\n    // Set up event handlers\n    this.wsConnection.on(\"open\", () =\u003e {\n      console.log(`Connected to room ${roomId}`);\n      // Send a \"join\" message or other initial handshake\n      this.wsConnection?.send(JSON.stringify({ type: \"join\", roomId }));\n    });\n\n    this.wsConnection.on(\"message\", (event: MessageEvent) =\u003e {\n      try {\n        const data = JSON.parse(event.data);\n        onMessageReceived(data); // Callback for UI updates\n      } catch (e) {\n        console.error(\"Failed to parse WS message:\", e);\n      }\n    });\n\n    this.wsConnection.on(\"close\", () =\u003e {\n      console.log(\"Disconnected from chat.\");\n      this.currentRoomId = null;\n    });\n\n    this.wsConnection.on(\"error\", (error: Event) =\u003e {\n      console.error(\"WebSocket error:\", error);\n    });\n  }\n\n  sendMessage(message: string, senderId: string) {\n    if (this.wsConnection \u0026\u0026 this.currentRoomId) {\n      this.wsConnection.send(\n        JSON.stringify({\n          type: \"chat\",\n          roomId: this.currentRoomId,\n          senderId,\n          content: message,\n          timestamp: new Date().toISOString(),\n        })\n      );\n      // Also send via REST for message history persistence (optional)\n      jetflareChat\n        .POST_chat_message({\n          params: { roomId: this.currentRoomId },\n          body: { message, senderId },\n        })\n        .catch((err) =\u003e console.error(\"Failed to post message via REST:\", err));\n    } else {\n      console.warn(\"Not connected to a chat room.\");\n    }\n  }\n\n  leaveRoom() {\n    if (this.wsConnection) {\n      this.wsConnection.close();\n      this.wsConnection = null;\n      console.log(\"Left current chat room.\");\n    }\n  }\n}\n\nconst chatService = new ChatService();\n\n// Example Usage:\nchatService.joinRoom(\"general-chat\", (message) =\u003e {\n  console.log(\"New chat message:\", message);\n  // Update your UI with the new message\n});\n\nsetTimeout(() =\u003e {\n  chatService.sendMessage(\"Hello everyone!\", \"user-456\");\n}, 2000);\n\nsetTimeout(() =\u003e {\n  chatService.leaveRoom();\n}, 10000);\n```\n\n---\n\n## API Reference\n\n### `Jetflare(baseUrl: string, routes: Routes): API`\n\nCreate a new API client instance.\n\n### Route Definition\n\n| Property    | Type     | Description                                |\n| ----------- | -------- | ------------------------------------------ | ----- | -------- | ------- | ----------- | ------ | ----------- |\n| `path`      | `string` | URL path (supports `:param` placeholders)  |\n| `method`    | `'get'   | 'post'                                     | 'put' | 'delete' | 'patch' | 'websocket' | 'sse'` | HTTP method |\n| `response?` | `object` | Expected response shape for type inference |\n| `body?`     | `object` | Request body type definition               |\n| `query?`    | `object` | Query parameters type definition           |\n| `params?`   | `object` | Path parameters type definition            |\n\n## Contributing\n\nContributions are welcome! Please see our [Contributing Guide](CONTRIBUTING.md) for details.\n\n## License\n\nJetflare is [MIT licensed](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodedynasty-dev%2Fjetflare","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcodedynasty-dev%2Fjetflare","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodedynasty-dev%2Fjetflare/lists"}