{"id":17093906,"url":"https://github.com/aashishpanchal/exlite","last_synced_at":"2025-07-24T00:31:40.380Z","repository":{"id":262965018,"uuid":"879949065","full_name":"aashishpanchal/exlite","owner":"aashishpanchal","description":" Exlite is a lightweight utility for Express.js that simplifies common server-side tasks.","archived":false,"fork":false,"pushed_at":"2024-11-18T23:09:24.000Z","size":210,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-11-18T23:18:03.801Z","etag":null,"topics":["expressjs","http-errors","http-status","javascript","nodejs","typescript"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/axpress","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/aashishpanchal.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":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-10-28T20:52:02.000Z","updated_at":"2024-11-18T23:07:20.000Z","dependencies_parsed_at":"2024-11-18T23:13:07.787Z","dependency_job_id":null,"html_url":"https://github.com/aashishpanchal/exlite","commit_stats":null,"previous_names":["aashishpanchal/exlite","aashishpanchal/axlite","aashishpanchal/axpress"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aashishpanchal%2Fexlite","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aashishpanchal%2Fexlite/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aashishpanchal%2Fexlite/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aashishpanchal%2Fexlite/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aashishpanchal","download_url":"https://codeload.github.com/aashishpanchal/exlite/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227381081,"owners_count":17771872,"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":["expressjs","http-errors","http-status","javascript","nodejs","typescript"],"created_at":"2024-10-14T14:09:40.509Z","updated_at":"2024-11-30T17:18:55.218Z","avatar_url":"https://github.com/aashishpanchal.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🚀 Exlite\r\n\r\n[![npm downloads](https://img.shields.io/npm/dm/exlite.svg)](https://www.npmjs.com/package/exlite)\r\n[![npm version](https://img.shields.io/npm/v/exlite.svg)](https://www.npmjs.com/package/exlite)\r\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\r\n\r\n## Overview 🌟\r\n\r\n`exlite` is a lightweight utility library designed specifically for Express.js, helping developers simplify server-side logic and reduce boilerplate code. It provides ready-to-use features like error handling, HTTP status utilities, and standardized API responses, enabling you to write cleaner, more maintainable code effortlessly.\r\n\r\n## Table of Contents 📚\r\n\r\n- [Features](#features-)\r\n- [Installation](#installation-)\r\n- [Motivation](#motivation-)\r\n- [Quick Start](#quick-start-)\r\n- [Error Handler Middleware: `globalErrorHandler`](#error-handler-middleware-globalerrorhandler-)\r\n- [Wrapper: Simplifying Controllers](#wrapper-simplifying-controllers-️)\r\n- [Standardized JSON Responses with `ApiRes`](#standardized-json-responses-with-apires-)\r\n- [HttpError](#httperror-)\r\n- [HttpStatus](#httpstatus-)\r\n- [Conclusion](#conclusion-)\r\n- [Contributing](#contributing-)\r\n- [Author](#author-)\r\n- [License](#license-)\r\n\r\n## Features ✨\r\n\r\n- 🚦 Simplifies route and controller management with pre-built helpers.\r\n- 🛡️ Integrated error handling across all routes and middleware.\r\n- ✨ Easy-to-use wrapper for automatically catching and handling errors.\r\n- 📜 Customizable response formatting for consistent API outputs.\r\n- ⚡ Flexible error handling with custom error classes.\r\n- 🎨 Efficient management of HTTP status codes and responses.\r\n\r\n### Installation 📥\r\n\r\n```bash\r\nnpm install --save exlite\r\n```\r\n\r\n### Motivation 💡\r\n\r\nBuilding APIs often involves repetitive tasks like handling errors, managing HTTP status codes, or structuring JSON responses. exlite was created to eliminate this hassle, allowing developers to focus on writing business logic instead of re-inventing common solutions. Whether you're a beginner or an experienced developer, exlite streamlines your workflow and ensures your Express applications are consistent and reliable.\r\n\r\n### Quick Start ⚡\r\n\r\nHere’s a minimal setup to get you started with `exlite`:\r\n\r\n```typescript\r\nimport express from 'express';\r\nimport {wrapper, globalErrorHandler} from 'exlite';\r\n\r\nconst app = express();\r\n\r\n// Middleware\r\napp.use(express.json());\r\n\r\n// Example route using wrapper\r\nconst getUser = wrapper(async (req, res) =\u003e {\r\n  const user = await getUserById(req.params.id);\r\n  return ApiRes.ok(user); // Send user data in the response\r\n});\r\n\r\n// Routers\r\napp.get('/user/:id', getUser);\r\n\r\n// Error handling middleware\r\napp.use(\r\n  globalErrorHandler({\r\n    isDev: process.env.NODE_ENV === 'development',\r\n    write: error =\u003e console.error(error),\r\n  }),\r\n);\r\n\r\napp.listen(3000, () =\u003e {\r\n  console.log('Server running on port 3000');\r\n});\r\n```\r\n\r\n## Error Handler Middleware: `globalErrorHandler` 🚨\r\n\r\nThe `globalErrorHandler` middleware manages `HttpErrors` and unknown errors, returning appropriate JSON responses.\r\n\r\n**Usage:**\r\n\r\n```typescript\r\nimport {errorHandler} from 'exlite';\r\n\r\n// Basic usage with default options\r\napp.use(\r\n  globalErrorHandler({\r\n    isDev: process.env.NODE_ENV === 'development',\r\n  }),\r\n);\r\n\r\n// Custom usage with logging in production mode\r\napp.use(\r\n  globalErrorHandler({\r\n    isDev: process.env.NODE_ENV === 'development',\r\n    write: error =\u003e console.error(error),\r\n  }),\r\n);\r\n```\r\n\r\n**Signature:**  \r\n`globalErrorHandler({isDev: boolean, write?: (err) =\u003e void}): ErrorRequestHandler`\r\n\r\n**Options:**\r\n\r\n- **`isDev`**: Enables detailed error messages in development mode (default: `true`).\r\n- **`write`**: Optional callback for logging or handling errors.\r\n\r\n## Wrapper: Simplifying Controllers 🛠️\r\n\r\nThe `wrapper` function in `exlite` eliminates repetitive `try-catch` blocks by managing error handling for both async and sync functions. It also integrates seamlessly with `ApiRes` for enhanced response handling. and provide other handler of features.\r\n\r\n#### Simplifying Route Handlers\r\n\r\n```typescript\r\nimport {wrapper, ApiRes} from 'exlite';\r\n\r\n// Route without wrapper (traditional approach with try-catch)\r\napp.get('/user/:id', async (req, res, next) =\u003e {\r\n  try {\r\n    const user = await getUserById(req.params.id);\r\n    res.status(200).json(user);\r\n  } catch (error) {\r\n    next(error); // Pass the error to the error-handling middleware\r\n  }\r\n});\r\n\r\n// Route using wrapper (simplified with exlite)\r\napp.get(\r\n  '/user/:id',\r\n  wrapper(async (req, res) =\u003e {\r\n    const user = await getUserById(req.params.id); // Fetch user from database\r\n    return ApiRes.ok(user, 'User fetched successfully'); // Send success response using ApiRes\r\n  }),\r\n);\r\n```\r\n\r\n#### Advanced Example: Handling Cookies and Headers\r\n\r\n```typescript\r\nconst login = wrapper(async (req, res) =\u003e {\r\n  const {email, password} = req.body;\r\n  const user = await loginUser(email, password);\r\n\r\n  // Manually setting headers\r\n  res.setHeader('X-Custom-Header', 'SomeHeaderValue');\r\n\r\n  // Set multiple cookies for authentication\r\n  res.cookie('access-token', user.accessToken, {\r\n    httpOnly: true,\r\n    secure: true, // Set to true in production with HTTPS\r\n    maxAge: 3600000, // 1 hour\r\n  });\r\n\r\n  res.cookie('refresh-token', user.refreshToken, {\r\n    httpOnly: true,\r\n    secure: true,\r\n    maxAge: 7 * 24 * 3600000, // 1 week\r\n  });\r\n\r\n  // api-response with token and user info\r\n  return ApiRes.ok(user, 'Logged in successfully');\r\n});\r\n```\r\n\r\n#### Minimal Examples\r\n\r\n- **Simple Response:**\r\n  ```typescript\r\n  const getHome = wrapper(() =\u003e 'Hello World!');\r\n  ```\r\n- **Custom JSON Response:**\r\n  ```typescript\r\n  const getHome = wrapper(() =\u003e ({message: 'Hello World!'}));\r\n  ```\r\n- **Without `ApiRes`**\r\n  ```typescript\r\n  const login = wrapper(async (req, res) =\u003e {\r\n    const user = await getUserById(req.params.id);\r\n    // Manually setting headers\r\n    res.setHeader('X-Custom-Header', 'SomeHeaderValue');\r\n    // Setting cookies\r\n    res.cookie('access-token', user.accessToken, {\r\n      httpOnly: true,\r\n      secure: true, // Set to true in production with HTTPS\r\n      maxAge: 3600000, // 1 hour\r\n    });\r\n    // Sending a custom JSON response\r\n    return res.status(200).json({\r\n      status: 'success',\r\n      message: 'User fetched successfully',\r\n      data: user,\r\n    });\r\n  });\r\n  ```\r\n\r\n#### Middleware Example: Role-Based Access Control\r\n\r\n```typescript\r\nimport {Role} from './constants';\r\nimport {wrapper, ForbiddenError} from 'exlite';\r\n\r\n/** Permission middleware */\r\nexport const permission = (...roles: Role[]) =\u003e\r\n  wrapper(async (req, _, next) =\u003e {\r\n    const {user} = req;\r\n\r\n    if (!roles.includes(user?.role))\r\n      throw new ForbiddenError(`Access denied for ${req.originalUrl}`);\r\n\r\n    next();\r\n  });\r\n\r\nexport const onlyAdmin = permission(Role.ADMIN);\r\nexport const adminOrUser = permission(Role.ADMIN, Role.USER);\r\n```\r\n\r\n**Benefits:**\r\n\r\n- Eliminates boilerplate `try-catch` logic.\r\n- Simplifies response handling with `ApiRes`.\r\n- Works seamlessly for both request handlers and middleware.\r\n\r\n## Standardized JSON Responses with `ApiRes` 📊\r\n\r\n`ApiRes` provides a consistent structure for API responses. It includes several static methods that handle common response patterns, such as `ok`, `created` `paginated`.\r\n\r\n**Usage:**\r\n\r\n```typescript\r\nimport {ApiRes} from 'exlite';\r\n\r\n// with paginated\r\nconst list = wrapper(async req =\u003e {\r\n  const {data, meta} = await getUsers(req.query);\r\n  return ApiRes.paginated(data, meta, 'Get users list successfully');\r\n});\r\n\r\n// with created\r\nconst create = wrapper(async req =\u003e {\r\n  const user = await createUser(req.body);\r\n  return ApiRes.created(user, 'User created successfully');\r\n});\r\n\r\n// with ok\r\nconst get = wrapper(async req =\u003e {\r\n  const user = await getUser(req.params);\r\n  return ApiRes.ok(user, 'Get user successfully');\r\n});\r\n\r\n// Routers\r\napp.route('/').get(list).post(create);\r\napp.route('/:id').get(get);\r\n```\r\n\r\n**ApiRes Methods**\r\n\r\n- `ok(result, message)`: Returns a success response (HTTP 200).\r\n- `created(result, message)`: Returns a resource creation response (HTTP 201).\r\n- `paginated(data, meta, message)`: Returns a success response (HTTP 200).\r\n\r\n## HttpError ❌\r\n\r\nThe `HttpError` class standardizes error handling by extending the native `Error` class. It’s used to throw HTTP-related errors, which are then caught by the `httpErrorHandler` middleware.\r\n\r\n**Usage:**\r\n\r\n```typescript\r\nimport {HttpError, HttpStatus} from 'exlite';\r\n\r\n// Example without wrapper\r\napp.get('*', () =\u003e {\r\n  throw new HttpError('Not Found', HttpStatus.NOT_FOUND); // Throw a 404 error\r\n});\r\n\r\n// Example with wrapper\r\napp.post(\r\n  '/example',\r\n  wrapper(req =\u003e {\r\n    if (!req.body.name) throw new BadRequestError('Name is required');\r\n  }),\r\n);\r\n```\r\n\r\n**HttpError(msg, status, details)**\r\n\r\n- `msg` - this parameter accepts an error message, which can be a single string or an array of strings., `required`\r\n- `status` - the status code of the error, mirroring `statusCode` for general compatibility, default is `500`\r\n- `detail` - this is an `optional` plain object that contains additional information about the error.\r\n\r\n```typescript\r\nconst err = new HttpError('Validation error.', 400, {\r\n  username: 'Username is required',\r\n  password: 'Password is required',\r\n});\r\n```\r\n\r\n#### Provide build common http-errors.\r\n\r\n- `BadRequestError`\r\n- `UnAuthorizedError`\r\n- `NotFoundError`\r\n- `ConflictError`\r\n- `ForbiddenError`\r\n- `PaymentRequiredError`\r\n- `NotImplementedError`\r\n- `InternalServerError`\r\n\r\n_Note: If only provides a status code, the `HttpError` class will automatically generate an appropriate error name based on that status code._\r\n\r\n#### **`isHttpError(value)` Static Method**\r\n\r\nThe `HttpError.isHttpError(value)` method is a useful way to determine if a specific value is an instance of the `HttpError` class. It will return `true` if the value is derived from the `HttpError` constructor, allowing you to easily identify HTTP-related errors in your application.\r\n\r\n```typescript\r\n// If it is an HttpError, send a JSON response with the error details\r\nif (HttpError.isHttpError(err))\r\n  return res.status(err.status).json(err.toJson());\r\nelse {\r\n  // If it's not an HttpError, pass it to the next middleware for further handling\r\n  next(err);\r\n}\r\n```\r\n\r\n#### Error Properties\r\n\r\nWhen you create an instance of `HttpError`, it comes with several useful properties that help provide context about the error:\r\n\r\n- **`status`**: The HTTP status code associated with the error (e.g., 404 for Not Found, 500 for Internal Server Error).\r\n- **`message`**: A brief description of the error, which is useful for debugging and logging.\r\n- **`stack`**: The stack trace of the error, available when the application is in development mode. This helps identify where the error occurred in your code.\r\n- **`details`**: An optional property that can hold additional information about the error, such as validation issues or other relevant data.\r\n\r\n#### Custom ErrorHandler Middleware\r\n\r\n```typescript\r\nexport const errorHandler: ErrorRequestHandler = (err, req, res, next): any =\u003e {\r\n  // Handle known HttpError instances\r\n  if (HttpError.isHttpError(err))\r\n    return res.status(err.status).json(err.toJson());\r\n\r\n  // Log unknown errors\r\n  console.error(err);\r\n\r\n  // Create an InternalServerError for unknown errors\r\n  const error = new InternalServerError(\r\n    config.dev ? err.message : 'Something went wrong',\r\n    config.dev ? err.stack : null,\r\n  );\r\n  return res.status(error.status).json(error.toJson());\r\n};\r\n```\r\n\r\n#### `toJson` Static Method\r\n\r\nThe `toJson` method is a static function that allows you to convert an `HttpError` instance into a structured JSON format. This is particularly useful for standardizing error responses sent to clients. When you call `toJson`, it returns an object containing the following properties:\r\n\r\n- **`status`**: The HTTP status code of the error.\r\n- **`message`**: A human-readable message describing the error.\r\n- **`details`** (if applicable): Any additional information that provides context about the error.\r\n\r\nThis method ensures that your API consistently responds to errors in a uniform way, making it easier for clients to understand and handle error responses.\r\n\r\n## HttpStatus ✅\r\n\r\nThe `HttpStatus` provides readable constants for standard HTTP status codes (2xx, 3xx, 4xx, 5xx), improving code clarity and consistency.\r\n\r\n**Usage:**\r\n\r\n```typescript\r\nimport {HttpStatus} from 'exlite';\r\n\r\n// Example: Basic usage in a route\r\napp.get('/status-example', (req, res) =\u003e {\r\n  res.status(HttpStatus.OK).json({message: 'All good!'});\r\n});\r\n\r\n// Example: Custom error handling middleware\r\napp.use((req, res) =\u003e {\r\n  res.status(HttpStatus.NOT_FOUND).json({\r\n    error: 'Resource not found',\r\n  });\r\n});\r\n\r\n// Example: Response with a 201 Created status\r\napp.post('/create', (req, res) =\u003e {\r\n  const resource = createResource(req.body);\r\n  res.status(HttpStatus.CREATED).json({\r\n    message: 'Resource created successfully',\r\n    data: resource,\r\n  });\r\n});\r\n```\r\n\r\n#### `HttpStatus.NAMES` of HTTP Status Code Name\r\n\r\nThe `NAMES` object provides a simple lookup for the descriptive names of HTTP status codes:\r\n\r\n```typescript\r\nconst statusName = HttpStatus.NAMES.$200; // 'OK'\r\n```\r\n\r\n**Benefits:**\r\n\r\n- Improves code readability and maintainability.\r\n- Reduces dependency on remembering or looking up numeric codes.\r\n- Ensures consistent use of status codes throughout your application.\r\n\r\n**Below is a list of commonly used HTTP status codes, their respective constants, and descriptions for easier reference:**\r\n\r\n#### **2xx: Success**\r\n\r\n- **`HttpStatus.OK`**: 200 — Request succeeded.\r\n- **`HttpStatus.CREATED`**: 201 — Resource created.\r\n- **`HttpStatus.ACCEPTED`**: 202 — Request accepted for processing.\r\n- **`HttpStatus.NON_AUTHORITATIVE_INFORMATION`**: 203 — Non-authoritative information.\r\n- **`HttpStatus.NO_CONTENT`**: 204 — No content to send.\r\n- **`HttpStatus.RESET_CONTENT`**: 205 — Content reset.\r\n- **`HttpStatus.PARTIAL_CONTENT`**: 206 — Partial content delivered.\r\n\r\n#### **3xx: Redirection**\r\n\r\n- **`HttpStatus.AMBIGUOUS`**: 300 — Multiple choices available.\r\n- **`HttpStatus.MOVED_PERMANENTLY`**: 301 — Resource moved permanently.\r\n- **`HttpStatus.FOUND`**: 302 — Resource found at another URI.\r\n- **`HttpStatus.SEE_OTHER`**: 303 — See other resource.\r\n- **`HttpStatus.NOT_MODIFIED`**: 304 — Resource not modified.\r\n- **`HttpStatus.TEMPORARY_REDIRECT`**: 307 — Temporary redirect.\r\n- **`HttpStatus.PERMANENT_REDIRECT`**: 308 — Permanent redirect.\r\n\r\n#### **4xx: Client Error**\r\n\r\n- **`HttpStatus.BAD_REQUEST`**: 400 — Bad request.\r\n- **`HttpStatus.UNAUTHORIZED`**: 401 — Authentication required.\r\n- **`HttpStatus.PAYMENT_REQUIRED`**: 402 — Payment required.\r\n- **`HttpStatus.FORBIDDEN`**: 403 — Access forbidden.\r\n- **`HttpStatus.NOT_FOUND`**: 404 — Resource not found.\r\n- **`HttpStatus.METHOD_NOT_ALLOWED`**: 405 — Method not allowed.\r\n- **`HttpStatus.NOT_ACCEPTABLE`**: 406 — Not acceptable content.\r\n- **`HttpStatus.PROXY_AUTHENTICATION_REQUIRED`**: 407 — Proxy authentication required.\r\n- **`HttpStatus.REQUEST_TIMEOUT`**: 408 — Request timed out.\r\n- **`HttpStatus.CONFLICT`**: 409 — Conflict with current state.\r\n- **`HttpStatus.GONE`**: 410 — Resource gone.\r\n- **`HttpStatus.LENGTH_REQUIRED`**: 411 — Length required.\r\n- **`HttpStatus.PRECONDITION_FAILED`**: 412 — Precondition failed.\r\n- **`HttpStatus.PAYLOAD_TOO_LARGE`**: 413 — Payload too large.\r\n- **`HttpStatus.URI_TOO_LONG`**: 414 — URI too long.\r\n- **`HttpStatus.UNSUPPORTED_MEDIA_TYPE`**: 415 — Unsupported media type.\r\n- **`HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE`**: 416 — Requested range not satisfiable.\r\n- **`HttpStatus.EXPECTATION_FAILED`**: 417 — Expectation failed.\r\n- **`HttpStatus.I_AM_A_TEAPOT`**: 418 — I'm a teapot (a joke HTTP status).\r\n- **`HttpStatus.MISDIRECTED_REQUEST`**: 421 — Misdirected request.\r\n- **`HttpStatus.UNPROCESSABLE_ENTITY`**: 422 — Unprocessable entity.\r\n- **`HttpStatus.FAILED_DEPENDENCY`**: 424 — Failed dependency.\r\n- **`HttpStatus.PRECONDITION_REQUIRED`**: 428 — Precondition required.\r\n- **`HttpStatus.TOO_MANY_REQUESTS`**: 429 — Too many requests.\r\n\r\n#### **5xx: Server Error**\r\n\r\n- **`HttpStatus.INTERNAL_SERVER_ERROR`**: 500 — Internal server error.\r\n- **`HttpStatus.NOT_IMPLEMENTED`**: 501 — Not implemented.\r\n- **`HttpStatus.BAD_GATEWAY`**: 502 — Bad gateway.\r\n- **`HttpStatus.SERVICE_UNAVAILABLE`**: 503 — Service unavailable.\r\n- **`HttpStatus.GATEWAY_TIMEOUT`**: 504 — Gateway timeout.\r\n- **`HttpStatus.HTTP_VERSION_NOT_SUPPORTED`**: 505 — HTTP version not supported.\r\n- **`HttpStatus.VARIANT_ALSO_NEGOTIATES`**: 506 — Variant also negotiates.\r\n- **`HttpStatus.INSUFFICIENT_STORAGE`**: 507 — Insufficient storage.\r\n- **`HttpStatus.LOOP_DETECTED`**: 508 — Loop detected.\r\n- **`HttpStatus.BANDWIDTH_LIMIT_EXCEEDED`**: 509 — Bandwidth limit exceeded.\r\n- **`HttpStatus.NOT_EXTENDED`**: 510 — Not extended.\r\n- **`HttpStatus.NETWORK_AUTHENTICATION_REQUIRED`**: 511 — Network authentication required.\r\n\r\n## Conclusion 🏁\r\n\r\n`exlite` is a powerful tool designed to simplify and enhance Express.js applications by providing essential features out of the box. Whether you’re building a simple API or a complex web application, `exlite` helps you maintain clean and manageable code.\r\n\r\n## Contributing 🤝\r\n\r\nContributions are highly appreciated! To contribute:\r\n\r\n1. Fork the repository.\r\n2. Create a new branch for your feature or bug fix.\r\n3. Submit a pull request with a clear description of your changes.\r\n\r\n## Author 👤\r\n\r\n- Created by **Aashish Panchal**.\r\n- GitHub: [@aashishpanchal](https://github.com/aashishpanchal)\r\n\r\n## License 📜\r\n\r\n[MIT © Aashish Panchal ](LICENSE)\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faashishpanchal%2Fexlite","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faashishpanchal%2Fexlite","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faashishpanchal%2Fexlite/lists"}